Difference between revisions of "HTTP asset certification"

From Internet Computer Wiki
Jump to: navigation, search
(Removed content and replaced it with links where the information resides today.)
Tags: Replaced Visual edit
(42 intermediate revisions by 3 users not shown)
Line 1: Line 1:
== Motivation ==
+
This content has moved to the learn hub ([https://learn.internetcomputer.org/hc/en-us/articles/34276431179412-Asset-Certification Asset Certification] and [https://learn.internetcomputer.org/hc/en-us/articles/34211943471892-HTTP-Gateway-Protocol HTTP Gateway Protocol]) and the [https://internetcomputer.org/docs/references/http-gateway-protocol-spec HTTP Gateway Protocol Specification].
A user interacting with the Internet Computer needs to be able to confirm that the responses they receive are actually coming from the Internet Computer and have not been tampered with. Traditionally, on the Internet, this problem is solved using public-key cryptography. The server running the service has a secret key and uses that to sign all its responses. A user can then verify the signature on the response using the server’s public key.
 
 
 
Just like a web server in Web2 maintains a public-key/secret-key pair, the Internet Computer blockchain as a whole maintains a public-key/secret-key pair. Additionally, each individual subnet in the Internet Computer also maintains its own public-key/secret-key pair. When a new subnet is formed, the NNS issues a certificate for the subnet which contains a signature of the subnet's public key with the Internet Computer's public key. When the subnet responds to a user's message, the response contains a certificate chain, which includes a signature on the response by the subnet's public key and the certificate issued by the NNS to the subnet. The user can verify the certificate chain using the Internet Computer's public key similar to verifying a certificate chain in Web2.
 
 
 
Each blockchain node shares only a piece of its subnet secret key. As a result, each node is incapable of signing a message by itself. But if at least 2/3rd of the nodes of a subnet agree on a message, they together can combine their secret key pieces to sign the message. The signed message can be verified easily using the subnet's public key. If the verification succeeds, it means that at least 2/3rd of the blockchain nodes running the canister agreed to deliver that message. The technology used by the Internet Computer to generate and maintain the secret key shares, and sign messages using the secret key shares is called [https://eprint.iacr.org/2021/339.pdf chain-key technology].
 
 
 
The Internet Computer supports two types of messages: Query calls and Update calls. Query calls are similar to HTTP GET requests and do not modify the state of the Internet Computer. The query calls do not go through the [https://wiki.internetcomputer.org/wiki/IC_consensus_layer consensus protocol]. The user can make a query call to any blockchain node in the subnet, and only that (possibly malicious) blockchain node answers the query. As generating a certificate requires consensus from at least 2/3rd of the nodes of the subnet, the Internet Computer doesn't issue a certificate when responding to query calls.
 
 
 
As the query calls have low [https://wiki.internetcomputer.org/wiki/Query_and_update_call_latency latency], the canisters deliver web pages to the client via query calls. However, as the client needs to verify the received content, the Internet Computer introduces the notion of [https://smartcontracts.org/docs/interface-spec/index.html#system-api-certified-data Certified Variables/Certified Data]. In a nutshell, a canister can a-priori choose to create a certificate for a piece of data and store it in the replicated state. Any user can later access the data along with its certificate via query calls. The user can use the IC public key to authenticate the body of the response.
 
 
 
We can use the notion of certified data to certify all the assets (HTML, CSS, Javascript files, images, videos, etc.) of an app a-priori. There are 2 ways of performing the asset certification.
 
* The canister developer can explicitly write code to manage and certify all the assets. The developer can take the help of ic-certified-assets library ([https://github.com/dfinity/sdk/tree/master/src/canisters/frontend/ic-certified-assets github]).
 
* The canister developer can create an "asset canister", by creating a canister with type set to "asset" and specifying the folder containing all the assets. The asset canister is a regular canister, except that the boilerplate code for managing and certifying all the assets is taken care of for us.
 
 
 
When a canister issues a response along with its certificate, a HTTP Gateway can be used to verify the certificate before passing on the response to the client.
 
 
 
== Certified Data ==
 
In every round of the Internet Computer Protocol, a new [https://wiki.internetcomputer.org/wiki/Replicated_state_structure per-round system tree] is generated. This tree is  then Merkelized and the root hash is computed. The nodes in the subnet then engage in a protocol to create a certificate for the root hash of the system tree. 
 
 
 
== Canister protocol ==
 
 
 
A canister must follow the following protocol to certify assets:
 
 
 
<ul>
 
<li>
 
Construct a [https://smartcontracts.org/docs/interface-spec/index.html#_certificate hash tree] that maps paths of HTTP resources to SHA-256 hashes of their bodies.
 
An example of such a tree:
 
<pre>
 
*root*
 
└── http_assets
 
    ├── index.html -> SHA256(body)
 
    ├── ...
 
    └── /css/styles.css -> SHA256(body)
 
</pre>
 
</li>
 
<li>
 
Compute the root hash of the tree and call [https://smartcontracts.org/docs/interface-spec/index.html#system-api-imports <code>ic0.certified_data_set</code>] with the bytes of the hash as the argument.
 
</li>
 
<li>
 
Add a [[#IC-Certificate header]] to each certified HTTP response.
 
</li>
 
</ul>
 
 
 
== Validator protocol ==
 
 
 
The validator follows the following steps to validate the certificate of resource at path <code>PATH</code> served by canister <code>CANISTER_ID</code>:
 
 
 
* Hash the body of the HTTP response, obtaining hash <code>DATA_HASH</code>.
 
* Check that the response contains the <code>IC-Certificate</code> header.
 
* Decode the <code>certificate</code> and the <code>tree</code> from the value of the <code>IC-Certificate</code> header.
 
* Check the validity of the certificate as described in the [https://smartcontracts.org/docs/interface-spec/index.html#certification Interface Specification: Certification]. This step requires knowing the IC root key.
 
* Check that <code>lookup(/http_assets/PATH, tree) = Found(DATA_HASH)</code>
 
* Check that <code>lookup(/canister/CANISTER_ID/certified_data, certificate.tree) = Found(reconstruct(tree))</code>.
 
 
 
== 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]) is a dictionary with members <code>certificate</code> and <code>tree</code>, both of which are Byte Sequences:
 
 
 
<pre>
 
IC-Certificate: certificate=:<base64(c)>:, tree=:<base64(t)>:
 
</pre>
 
 
 
where
 
 
 
* <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].
 
 
 
The <code>certificate</code> must be a valid [https://smartcontracts.org/docs/interface-spec/index.html#_certificate Internet Specification: Certificate] with
 
 
 
<pre>
 
lookup(/canister/<canister_id>/certified_data, certificate.tree)
 
    = Found (reconstruct(tree))
 
</pre>
 
 
 
The <code>tree</code> exposes the relevant nodes in the <code>/http_assets</code> subtree to allow the client to lookup the request path to get the expected body hash.
 
 
 
=== Example ===
 
 
 
For this example, we fetched <code>/index.html</code> resource of the Internet Identity canister (canister id <code>rdmx6-jaaaa-aaaaa-aaadq-cai</code>) 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 <code>478afb8206ca0b566a7f138e623accd169fa822602d2f6d717fb67d1045f4f0d</code>.
 
The response contained the following header:
 
 
 
<pre>
 
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==:
 
</pre>
 
 
 
We can extract the following data from the header value:
 
 
 
<pre>
 
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),
 
    ),
 
}
 
</pre>
 
 
 
== 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.
 
 
 
== Canisters using HTTP asset certification ==
 
 
 
* [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/certified-assets Certified assets canister]
 
 
 
== Validators ==
 
 
 
* [https://github.com/dfinity/ic/tree/master/typescript/service-worker Certifying Service Worker]
 
* [https://github.com/dfinity/icx-proxy/blob/b0de0437fe6806a96d942465e5ee284c23b812e8/src/main.rs#L470 ICX proxy]
 

Revision as of 17:59, 23 June 2025

This content has moved to the learn hub (Asset Certification and HTTP Gateway Protocol) and the HTTP Gateway Protocol Specification.