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_set
with 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-Certificate
header. - Decode the
certificate
and thetree
from the value of theIC-Certificate
header. - 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
c
is the a CBOR-encoded certificate (see Interface Specification: Encoding of certificates).t
is a Hash Tree, CBOR-encoded according the CDDL#6.55799(hash-tree)
wherehash-tree
is 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.