junobuild_storage/certification/
cert.rs

1use crate::certification::constants::{IC_CERTIFICATE_EXPRESSION_HEADER, IC_CERTIFICATE_HEADER};
2use crate::certification::tree_utils::response_headers_expression;
3use crate::certification::types::certified::CertifiedAssetHashes;
4use crate::http::types::HeaderField;
5use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
6use ic_cdk::api::data_certificate;
7use ic_certification::{fork, HashTree};
8use junobuild_shared::types::core::Blob;
9use serde::Serialize;
10use serde_cbor::ser::Serializer;
11
12pub fn build_asset_certificate_header(
13    asset_hashes: &CertifiedAssetHashes,
14    url: String,
15    certificate_version: &Option<u16>,
16    rewrite_source: &Option<String>,
17    sigs_tree: HashTree,
18) -> Result<HeaderField, &'static str> {
19    let certificate = data_certificate();
20
21    match certificate {
22        None => Err("No certificate found."),
23        Some(certificate) => match certificate_version {
24            None | Some(1) => {
25                build_asset_certificate_header_v1_impl(&certificate, asset_hashes, &url, sigs_tree)
26            }
27            Some(2) => build_asset_certificate_header_v2_impl(
28                &certificate,
29                asset_hashes,
30                &url,
31                rewrite_source,
32                sigs_tree,
33            ),
34            _ => Err("Unsupported certificate version to certify headers."),
35        },
36    }
37}
38
39pub fn build_certified_expression(
40    asset_headers: &[HeaderField],
41    certificate_version: &Option<u16>,
42) -> Result<Option<HeaderField>, &'static str> {
43    match certificate_version {
44        None | Some(1) => Ok(None),
45        Some(2) => Ok(Some(HeaderField(
46            IC_CERTIFICATE_EXPRESSION_HEADER.to_string(),
47            response_headers_expression(asset_headers),
48        ))),
49        _ => Err("Unsupported certificate version to certify expression."),
50    }
51}
52
53fn build_asset_certificate_header_v1_impl(
54    certificate: &Blob,
55    asset_hashes: &CertifiedAssetHashes,
56    url: &str,
57    sigs_tree: HashTree,
58) -> Result<HeaderField, &'static str> {
59    let witness_asset_tree = asset_hashes.witness_v1(url);
60
61    let tree = fork(witness_asset_tree, sigs_tree);
62
63    let mut serializer = Serializer::new(vec![]);
64    serializer.self_describe().unwrap();
65    let result = tree.serialize(&mut serializer);
66
67    match result {
68        Err(_err) => Err("Failed to serialize a hash tree."),
69        Ok(_serialize) => Ok(HeaderField(
70            IC_CERTIFICATE_HEADER.to_string(),
71            format!(
72                "certificate=:{}:, tree=:{}:",
73                BASE64.encode(certificate),
74                BASE64.encode(serializer.into_inner())
75            ),
76        )),
77    }
78}
79
80fn build_asset_certificate_header_v2_impl(
81    certificate: &Blob,
82    asset_hashes: &CertifiedAssetHashes,
83    url: &str,
84    rewrite_source: &Option<String>,
85    sigs_tree: HashTree,
86) -> Result<HeaderField, &'static str> {
87    assert!(url.starts_with('/'));
88
89    let witness_asset_tree = match rewrite_source {
90        None => asset_hashes.witness_v2(url),
91        Some(_) => asset_hashes.witness_rewrite_v2(url),
92    };
93
94    let tree = fork(witness_asset_tree, sigs_tree);
95
96    let mut serializer = Serializer::new(vec![]);
97    serializer.self_describe().unwrap();
98    let result = tree.serialize(&mut serializer);
99
100    match result {
101        Err(_err) => Err("Failed to serialize a hash tree."),
102        Ok(_serialize) => {
103            let mut expr_path_serializer = Serializer::new(vec![]);
104            expr_path_serializer.self_describe().unwrap();
105
106            let path = asset_hashes.expr_path_v2(url, rewrite_source);
107            let result_path = path.serialize(&mut expr_path_serializer);
108
109            match result_path {
110                Err(_err) => Err("Failed to serialize path."),
111                Ok(_serialize) => Ok(HeaderField(
112                    IC_CERTIFICATE_HEADER.to_string(),
113                    format!(
114                        "certificate=:{}:, tree=:{}:, expr_path=:{}:, version=2",
115                        BASE64.encode(certificate),
116                        BASE64.encode(serializer.into_inner()),
117                        BASE64.encode(expr_path_serializer.into_inner())
118                    ),
119                )),
120            }
121        }
122    }
123}