Difference between revisions of "HTTP asset certification"
| Line 135: | Line 135: | ||
* [https://github.com/dfinity/internet-identity/blob/7ff3dd51dd98c7b1b43d83950c9f31ea7159103d/src/internet_identity/src/main.rs#L775 Internet Identity canister] | * [https://github.com/dfinity/internet-identity/blob/7ff3dd51dd98c7b1b43d83950c9f31ea7159103d/src/internet_identity/src/main.rs#L775 Internet Identity canister] | ||
* [https://github.com/dfinity/nns-dapp/blob/49126394df77b9583e508277fc736eda51de47be/rs/src/assets.rs#L123 NNS frontend] | * [https://github.com/dfinity/nns-dapp/blob/49126394df77b9583e508277fc736eda51de47be/rs/src/assets.rs#L123 NNS frontend] | ||
| − | * [https:// | + | * [https://github.com/dfinity/certified-assets Certified assets canister] |
== Validators == | == Validators == | ||
Revision as of 11:06, 1 July 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) with is a dictionary with with members certificate and tree, both of which are Byte Sequences:
IC-Certificate: certificate=:<base64(c)>:, tree=:<base64(t)>:
where
cis the a 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.