1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
use crate::{parse_cbor, parsed_cbor_to_tree, CborError, CborResult, CborValue};
use ic_certification::{Certificate, Delegation};

pub trait CertificateToCbor {
    fn from_cbor(cbor: &[u8]) -> CborResult<Certificate>;
}

impl CertificateToCbor for Certificate {
    fn from_cbor(cbor: &[u8]) -> CborResult<Certificate> {
        let parsed_cbor = parse_cbor(cbor).map_err(|e| CborError::MalformedCbor(e.to_string()))?;

        parsed_cbor_to_certificate(parsed_cbor)
    }
}

fn parsed_cbor_to_certificate(parsed_cbor: CborValue) -> CborResult<Certificate> {
    let CborValue::Map(map) = parsed_cbor else {
        return Err(CborError::MalformedCertificate(
            "Expected Map when parsing Certificate Cbor".into()
        ));
    };

    let Some(tree_cbor) = map.get("tree") else {
        return Err(CborError::MalformedCertificate(
            "Expected Tree when parsing Certificate Cbor".into()
        ));
    };

    let tree = parsed_cbor_to_tree(tree_cbor)?;

    let signature = if let Some(CborValue::ByteString(signature)) = map.get("signature") {
        signature.to_owned()
    } else {
        return Err(CborError::MalformedCertificate(
            "Expected Signature when parsing Certificate Cbor".into(),
        ));
    };

    let delegation = if let Some(CborValue::Map(delegation_map)) = map.get("delegation") {
        let Some(CborValue::ByteString(subnet_id)) = delegation_map.get("subnet_id") else {
            return Err(CborError::MalformedCertificate(
                "Expected Delegation Map to contain a Subnet ID when parsing Certificate Cbor".into()
            ));
        };

        let Some(CborValue::ByteString(certificate)) = delegation_map.get("certificate") else {
            return Err(CborError::MalformedCertificate(
                "Expected Delegation Map to contain a Certificate when parsing Certificate Cbor".into()
            ));
        };

        Some(Delegation {
            subnet_id: subnet_id.to_owned(),
            certificate: certificate.to_owned(),
        })
    } else {
        None
    };

    Ok(Certificate {
        tree,
        signature,
        delegation,
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use ic_response_verification_test_utils::{
        cbor_encode, create_certificate, create_certificate_delegation,
    };

    #[test]
    fn deserialize_from_cbor() {
        let certificate = create_certificate(None);

        let cbor = cbor_encode(&certificate);

        let result = Certificate::from_cbor(&cbor).unwrap();

        assert_eq!(result, certificate);
    }

    #[test]
    fn deserialize_from_cbor_with_delegation() {
        let mut certificate = create_certificate(None);
        certificate.delegation = Some(create_certificate_delegation());

        let cbor = cbor_encode(&certificate);

        let result = Certificate::from_cbor(&cbor).unwrap();

        assert_eq!(result, certificate);
    }
}