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