Difference between revisions of "HTTP asset certification"
m |
|||
| Line 40: | Line 40: | ||
== IC-Certificate header == | == IC-Certificate header == | ||
| − | The <code>IC-Certificate</code> header is a Structure Header (as per [https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-header-structure RFC proposal]) | + | The <code>IC-Certificate</code> header is a Structure Header (as per [https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-header-structure RFC proposal]) is a dictionary with members <code>certificate</code> and <code>tree</code>, both of which are Byte Sequences: |
<pre> | <pre> | ||
| Line 48: | Line 48: | ||
where | where | ||
| − | * <code>c</code> is the | + | * <code>c</code> is the CBOR-encoded certificate (see [https://smartcontracts.org/docs/interface-spec/index.html#certification-encoding Interface Specification: Encoding of certificates]). |
* <code>t</code> is a Hash Tree, CBOR-encoded according the CDDL <code>#6.55799(hash-tree)</code> where <code>hash-tree</code> is the corresponding [https://smartcontracts.org/docs/interface-spec/index.html#api-cddl CDDL production in the Interface Specification]. | * <code>t</code> is a Hash Tree, CBOR-encoded according the CDDL <code>#6.55799(hash-tree)</code> where <code>hash-tree</code> is the corresponding [https://smartcontracts.org/docs/interface-spec/index.html#api-cddl CDDL production in the Interface Specification]. | ||
Revision as of 14:09, 2 November 2022
HTTP asset certification is a protocol that allows canisters to certify bodies of responses to GET HTTP requests. The receiver of such responses can use the IC public key to authenticate the body of the response. The protocol relies on Certified data feature of the IC.
Canister protocol
A canister must follow the following protocol to certify assets:
-
Construct a hash tree that maps paths of HTTP resources to SHA-256 hashes of their bodies.
An example of such a tree:
*root* └── http_assets ├── index.html -> SHA256(body) ├── ... └── /css/styles.css -> SHA256(body) -
Compute the root hash of the tree and call
ic0.certified_data_setwith the bytes of the hash as the argument. - Add a #IC-Certificate header to each certified HTTP response.
Validator protocol
The validator follows the following steps to validate the certificate of resource at path PATH served by canister CANISTER_ID:
- Hash the body of the HTTP response, obtaining hash
DATA_HASH. - Check that the response contains the
IC-Certificateheader. - Decode the
certificateand thetreefrom the value of theIC-Certificateheader. - Check the validity of the certificate as described in the Interface Specification: Certification. This step requires knowing the IC root key.
- Check that
lookup(/http_assets/PATH, tree) = Found(DATA_HASH) - Check that
lookup(/canister/CANISTER_ID/certified_data, certificate.tree) = Found(reconstruct(tree)).
IC-Certificate header
The IC-Certificate header is a Structure Header (as per RFC proposal) is a dictionary with members certificate and tree, both of which are Byte Sequences:
IC-Certificate: certificate=:<base64(c)>:, tree=:<base64(t)>:
where
cis the CBOR-encoded certificate (see Interface Specification: Encoding of certificates).tis a Hash Tree, CBOR-encoded according the CDDL#6.55799(hash-tree)wherehash-treeis the corresponding CDDL production in the Interface Specification.
The certificate must be a valid Internet Specification: Certificate with
lookup(/canister/<canister_id>/certified_data, certificate.tree)
= Found (reconstruct(tree))
The tree exposes the relevant nodes in the /http_assets subtree to allow the client to lookup the request path to get the expected body hash.
Example
For this example, we fetched /index.html resource of the Internet Identity canister (canister id rdmx6-jaaaa-aaaaa-aaadq-cai) available at https://rdmx6-jaaaa-aaaaa-aaadq-cai.raw.ic0.app/index.html.
The SHA-256 hash of the resource at the moment of fetching is 478afb8206ca0b566a7f138e623accd169fa822602d2f6d717fb67d1045f4f0d.
The response contained the following header:
IC-Certificate: certificate=:2dn3omR0cmVlgwGDAYMBgwJIY2FuaXN0ZXKDAYMBggRYIIudikoDwH1gRK637olblUhMUX3HlE0Dihj8MTACxGzHgwGCBFggyCRYc8M/ugt8G7C8RPYayn+l4sdBj8gvFotzJELnQ32DAYIEWCA1/+UHZ9SF67w4ssjOi+Jv3ch7WQNzezGmhtuvB+RDpYMCSgAAAAAAAAAHAQGDAYMBgwJOY2VydGlmaWVkX2RhdGGCA1ggWUt10wjWinx0aAWyrNEi/0R7VeuhalDMjGDErzIbZzqCBFgg/VtZRZdYyK/sr3KF2jWeS1rblF+4ajwfDv2ZbCGpaTiCBFggSoI5JS0pCusHP4nh6h780ebr961E0lVnFkFwzF5pZaeCBFggcKidPEGiPoFMPYfEyNGsDRYWmry1iGX0HNUEoKhIATeCBFggR0zdKUZOMcm5EHNl5Tee3XWqbq1gArwUGzZ2FH4rWtmCBFggTkwJcNrh0eJ9FutJcn6th9eCbM2KXnloxed0acxmQNeDAYIEWCA6SNH8IT1JMHEDEE99csK1kw7bqHh7kGMfNDs6popfCoMCRHRpbWWCA0nFnbXrts/65xZpc2lnbmF0dXJlWDCkXN2tcvH5b+xFCzfkuJMqrZDcplfW8vDziJwzx08WOPI4rh2TIGYZ3R6dgQTF0CA=:, tree=:2dn3gwGDAktodHRwX2Fzc2V0c4MBggRYIDgtAGcz5VvevwiEwwZB9zpkt17C9LE6o/O37bEwQUawgwGDAksvaW5kZXguaHRtbIIDWCBHivuCBsoLVmp/E45iOszRafqCJgLS9tcX+2fRBF9PDYIEWCCx2L8SfJwOydBkUxjc8tKXDVUeoiw8qEYI+8b+HRWIWYIEWCAqZ+3yoFSA9s+jbLFbtcVz+wi0HF9x51Kx38qPcBhiDA==:
We can extract the following data from the header value:
ROOT HASH: 0b2d843df534ac8ed2331fe2782deb71d23a08d9b4019a8fa695ec7fde93de36
TREE HASH: 594b75d308d68a7c746805b2acd122ff447b55eba16a50cc8c60c4af321b673a
SIGNATURE: a45cddad72f1f96fec450b37e4b8932aad90dca657d6f2f0f3889c33c74f1638f238ae1d93206619dd1e9d8104c5d020
CERTIFICATE TIME: 2022-02-02T08:23:24.851277509+00:00
CERTIFICATE TREE:
HashTree {
root: Fork(
Fork(
Fork(
Label("canister", Fork(
Fork(
Pruned(8b9d8a4a03c07d6044aeb7ee895b95484c517dc7944d038a18fc313002c46cc7),
Fork(
Pruned(c8245873c33fba0b7c1bb0bc44f61aca7fa5e2c7418fc82f168b732442e7437d),
Fork(
Pruned(35ffe50767d485ebbc38b2c8ce8be26fddc87b5903737b31a686dbaf07e443a5),
Label(0x00000000000000070101, Fork(
Fork(
Label("certified_data", Leaf(0x594b75d308d68a7c746805b2acd122ff447b55eba16a50cc8c60c4af321b673a)),
Pruned(fd5b59459758c8afecaf7285da359e4b5adb945fb86a3c1f0efd996c21a96938),
),
Pruned(4a8239252d290aeb073f89e1ea1efcd1e6ebf7ad44d25567164170cc5e6965a7),
)),
),
),
),
Pruned(70a89d3c41a23e814c3d87c4c8d1ac0d16169abcb58865f41cd504a0a8480137),
)),
Pruned(474cdd29464e31c9b9107365e5379edd75aa6ead6002bc141b3676147e2b5ad9),
),
Pruned(4e4c0970dae1d1e27d16eb49727ead87d7826ccd8a5e7968c5e77469cc6640d7),
),
Fork(
Pruned(3a48d1fc213d49307103104f7d72c2b5930edba8787b90631f343b3aa68a5f0a),
Label("time", Leaf(0xc59db5ebb6cffae716)),
),
),
}
TREE:
HashTree {
root: Fork(
Label("http_assets", Fork(
Pruned(382d006733e55bdebf0884c30641f73a64b75ec2f4b13aa3f3b7edb1304146b0),
Fork(
Label("/index.html", Leaf(0x478afb8206ca0b566a7f138e623accd169fa822602d2f6d717fb67d1045f4f0d)),
Pruned(b1d8bf127c9c0ec9d0645318dcf2d2970d551ea22c3ca84608fbc6fe1d158859),
),
)),
Pruned(2a67edf2a05480f6cfa36cb15bb5c573fb08b41c5f71e752b1dfca8f7018620c),
),
}
Limitations
- The protocol supports only one resource per path. This does not work well with content negotiation protocol.
- The protocol does not support certification of HTTP statuses and headers. Only resource bodies can be certified.