ctclient_async/
sct.rs

1use std::convert::TryInto;
2
3use openssl::pkey::PKey;
4use openssl::sha::sha256;
5use openssl::x509::X509Ref;
6
7use crate::Error;
8use crate::internal::leaf_hash_constructors;
9use crate::internal::openssl_ffi::{
10    SCTVersion, SignatureAlgorithm, sct_list_from_x509, x509_clone, x509_remove_sct_list,
11    x509_to_tbs,
12};
13use crate::internal::verify_dss_raw;
14
15fn to_unknown_err(openssl_err: openssl::error::ErrorStack) -> Error {
16    Error::Unknown(format!("{}", openssl_err))
17}
18
19/// An unverified *Signed Certificate Timestamp* (SCT).
20#[derive(Debug, Clone)]
21pub struct SignedCertificateTimestamp {
22    pub log_id: [u8; 32],
23    pub timestamp: u64,
24    pub extensions_data: Vec<u8>,
25    pub entry: SctEntry,
26    pub signature_algorithm: SignatureAlgorithm,
27    /// Raw signature encoded in ASN.1
28    pub raw_signature: Vec<u8>,
29}
30
31/// Either a X509 der, or (in case of pre-cert) tbs and issuer key hash.
32///
33/// Used within [`SignedCertificateTimestamp`]
34#[derive(Debug, Clone)]
35pub enum SctEntry {
36    X509(Vec<u8>),
37    PreCert {
38        tbs: Vec<u8>,
39        issuer_key_hash: [u8; 32],
40    },
41}
42
43impl SignedCertificateTimestamp {
44    /// Extract a list of SCTs from the SCT List extension of the given openssl-parsed certificate,
45    /// if the extension is there.
46    ///
47    /// If the certificate does not contain the extension, `Ok(vec![])` is returned.
48    ///
49    /// Will not verify the signature. Call [`self.verify`](Self::verify) with the log's public key to verify.
50    pub fn from_cert_sct_extension(
51        cert: &X509Ref,
52        issuer: &X509Ref,
53    ) -> Result<Vec<SignedCertificateTimestamp>, Error> {
54        let sctlist = sct_list_from_x509(cert)?;
55        if sctlist.is_none() {
56            return Ok(Vec::new());
57        }
58        let sctlist = sctlist.unwrap();
59        let tbs = {
60            let mut cert_clone = x509_clone(cert).map_err(to_unknown_err)?;
61            x509_remove_sct_list(&mut cert_clone).map_err(to_unknown_err)?;
62            x509_to_tbs(&cert_clone).map_err(to_unknown_err)?
63        };
64        let issuer_key_hash = {
65            let k = issuer
66                .public_key()
67                .map_err(|e| {
68                    Error::BadCertificate(format!("Can't parse public key from issuer: {}", e))
69                })?
70                .public_key_to_der()
71                .map_err(to_unknown_err)?;
72            sha256(&k)
73        };
74        let mut scts = Vec::with_capacity(sctlist.len());
75        for raw_sct in sctlist.into_iter() {
76            if raw_sct.version() != Some(SCTVersion::V1) {
77                return Err(Error::BadCertificate("Invalid SCT version.".to_owned()));
78            }
79            scts.push(SignedCertificateTimestamp {
80                log_id: raw_sct.log_id().try_into().map_err(|_| {
81                    Error::BadCertificate("Expected log_id to have len 32".to_owned())
82                })?,
83                timestamp: raw_sct.timestamp(),
84                extensions_data: raw_sct.extensions().to_vec(),
85                entry: SctEntry::PreCert {
86                    tbs: tbs.clone(),
87                    issuer_key_hash,
88                },
89                signature_algorithm: raw_sct
90                    .signature_algorithm()
91                    .ok_or_else(|| Error::BadSct("Unknown signature algorithm.".to_owned()))?,
92                raw_signature: raw_sct.raw_signature().to_vec(),
93            });
94        }
95        Ok(scts)
96    }
97
98    /// Derive the corresponding Merkle leaf hash from this SCTs.
99    ///
100    /// Can be used to check inclusion, for example.
101    pub fn derive_leaf_hash(&self) -> [u8; 32] {
102        match &self.entry {
103            SctEntry::PreCert {
104                tbs,
105                issuer_key_hash,
106            } => leaf_hash_constructors::with_precert(
107                &tbs[..],
108                &issuer_key_hash[..],
109                self.timestamp,
110                &self.extensions_data,
111            ),
112            SctEntry::X509(x509) => {
113                leaf_hash_constructors::with_x509(x509, self.timestamp, &self.extensions_data)
114            }
115        }
116    }
117
118    /// Check the signature in this SCT.
119    ///
120    /// To get the log public key, lookup the log with `self.log_id` by e.g. using [`crate::google_log_list::LogList::find_by_id`].
121    pub fn verify(&self, log_public_key: &PKey<openssl::pkey::Public>) -> Result<(), Error> {
122        // type CertificateTimestamp struct {
123        let mut signed_data: Vec<u8> = Vec::new();
124        // 	SCTVersion    Version       `tls:"maxval:255"`
125        signed_data.push(0u8);
126        // 	SignatureType SignatureType `tls:"maxval:255"`
127        signed_data.push(0u8);
128        // 	Timestamp     uint64
129        signed_data.extend_from_slice(&self.timestamp.to_be_bytes());
130        // 	EntryType     LogEntryType   `tls:"maxval:65535"`
131        signed_data.extend_from_slice(
132            &match &self.entry {
133                SctEntry::X509(_) => 0u16,
134                SctEntry::PreCert {
135                    tbs: _,
136                    issuer_key_hash: _,
137                } => 1u16,
138            }
139            .to_be_bytes(),
140        );
141        match &self.entry {
142            // 	X509Entry     *ASN1Cert      `tls:"selector:EntryType,val:0"`
143            SctEntry::X509(cert) => {
144                let len = cert.len();
145                if len > 1 << 24 {
146                    return Err(Error::BadSct("Certificate too long.".to_owned()));
147                }
148                signed_data.extend_from_slice(&u32::to_be_bytes(len as u32)[1..4]);
149                signed_data.extend_from_slice(cert);
150            }
151            // 	PrecertEntry  *PreCert       `tls:"selector:EntryType,val:1"`
152            SctEntry::PreCert {
153                tbs,
154                issuer_key_hash,
155            } => {
156                signed_data.extend_from_slice(issuer_key_hash);
157                let len = tbs.len();
158                if len > 1 << 24 {
159                    return Err(Error::BadSct("TBS certificate too long.".to_owned()));
160                }
161                signed_data.extend_from_slice(&u32::to_be_bytes(len as u32)[1..4]);
162                signed_data.extend_from_slice(tbs);
163            }
164        }
165        // 	Extensions    CTExtensions   `tls:"minlen:0,maxlen:65535"`
166        let ext_len = self.extensions_data.len();
167        if ext_len > 1 << 16 {
168            return Err(Error::BadSct("extension data too long.".to_owned()));
169        }
170        signed_data.extend_from_slice(&u16::to_be_bytes(ext_len as u16));
171        signed_data.extend_from_slice(&self.extensions_data);
172        // }
173        verify_dss_raw(
174            self.signature_algorithm,
175            log_public_key,
176            &self.raw_signature,
177            &signed_data,
178        )
179    }
180}