Skip to main content

certran_logs/ctlog/v1/
cert.rs

1use base64::{Engine, prelude::BASE64_STANDARD};
2use std::fmt;
3
4use ouroboros::self_referencing;
5use x509_parser::prelude::*;
6
7use crate::error::{BinaryParsingError, CtLogError};
8
9use super::{
10    model::Entry,
11    util::{read_exact_bytes, read_u8, read_u16_be, read_u24_be, read_u64_be, read_vec},
12};
13
14#[cfg(feature = "debug-fmt")]
15use chrono::TimeZone;
16
17#[cfg(feature = "debug-fmt")]
18use oid_registry::{OidRegistry, format_oid};
19
20#[cfg(feature = "debug-fmt")]
21use super::util::{print_x509_extension, print_x509_ski};
22
23#[self_referencing(pub_extras)]
24#[derive(Debug)]
25pub struct WrapX509Certificate {
26    raw: Vec<u8>,
27    #[borrows(raw)]
28    #[covariant]
29    pub certificate: X509Certificate<'this>,
30}
31
32impl WrapX509Certificate {
33    pub fn try_from_der(v: &[u8]) -> Result<Self, BinaryParsingError> {
34        Ok(WrapX509CertificateBuilder {
35            raw: v.to_vec(),
36            certificate_builder: |raw: &Vec<u8>| X509Certificate::from_der(raw).unwrap().1,
37        }
38        .build())
39    }
40}
41
42#[cfg(feature = "debug-fmt")]
43impl fmt::Display for WrapX509Certificate {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        let certificate = self.borrow_certificate();
46        writeln!(f, "Certificate:")?;
47        writeln!(f, "  Data:")?;
48        writeln!(f, "    Version: {}", certificate.version())?;
49        writeln!(
50            f,
51            "    Serial Number: {} ({})",
52            certificate.serial,
53            certificate.raw_serial_as_string()
54        )?;
55        writeln!(
56            f,
57            "  Signature Algorithm: {}",
58            format_oid(
59                certificate.signature_algorithm.oid(),
60                &OidRegistry::default().with_all_crypto()
61            )
62        )?;
63        writeln!(f, "    Issuer: {}", certificate.issuer())?;
64        writeln!(f, "    Validity:")?;
65        writeln!(f, "      Not Before: {}", certificate.validity().not_before)?;
66        writeln!(f, "      Not After : {}", certificate.validity().not_after)?;
67        writeln!(f, "    Subject: {}", certificate.subject())?;
68        writeln!(f, "    Subject Public Key Info:")?;
69        print_x509_ski(f, certificate.public_key(), 6)?;
70
71        if !certificate.extensions().is_empty() {
72            writeln!(f, "    X509v3 extensions:")?;
73            for extension in certificate.extensions() {
74                print_x509_extension(f, &extension.oid, extension, 6)?;
75            }
76        }
77
78        Ok(())
79    }
80}
81
82#[self_referencing(pub_extras)]
83#[derive(Debug)]
84pub struct WrapTbsCertificate {
85    raw: Vec<u8>,
86    #[borrows(raw)]
87    #[covariant]
88    pub certificate: TbsCertificate<'this>,
89}
90
91impl WrapTbsCertificate {
92    pub fn try_from_der(v: &[u8]) -> Result<Self, BinaryParsingError> {
93        Ok(WrapTbsCertificateBuilder {
94            raw: v.to_vec(),
95            certificate_builder: |raw: &Vec<u8>| TbsCertificate::from_der(raw).unwrap().1,
96        }
97        .build())
98    }
99}
100
101#[cfg(feature = "debug-fmt")]
102impl fmt::Display for WrapTbsCertificate {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        let certificate = self.borrow_certificate();
105
106        writeln!(f, "Certificate:")?;
107        writeln!(f, "  Data:")?;
108        writeln!(f, "    Version: {}", certificate.version())?;
109        writeln!(
110            f,
111            "    Serial Number: {} ({})",
112            certificate.serial,
113            certificate.raw_serial_as_string()
114        )?;
115        writeln!(
116            f,
117            "  Signature Algorithm: {}",
118            format_oid(
119                certificate.signature.oid(),
120                &OidRegistry::default().with_all_crypto()
121            )
122        )?;
123        writeln!(f, "    Issuer: {}", certificate.issuer())?;
124        writeln!(f, "    Validity:")?;
125        writeln!(f, "      Not Before: {}", certificate.validity().not_before)?;
126        writeln!(f, "      Not After : {}", certificate.validity().not_after)?;
127        writeln!(f, "    Subject: {}", certificate.subject())?;
128        writeln!(f, "    Subject Public Key Info:")?;
129        print_x509_ski(f, certificate.public_key(), 6)?;
130
131        if !certificate.extensions().is_empty() {
132            writeln!(f, "    X509v3 extensions:")?;
133            for extension in certificate.extensions() {
134                print_x509_extension(f, &extension.oid, extension, 6)?;
135            }
136        }
137
138        Ok(())
139    }
140}
141
142#[derive(Debug, PartialEq, Eq, Clone)]
143pub enum LogEntryType {
144    X509Entry = 0,
145    PrecertEntry = 1,
146}
147
148impl LogEntryType {
149    pub fn parse(input: &mut &[u8]) -> Result<Self, BinaryParsingError> {
150        let id = read_u16_be(input)?;
151        match id {
152            0 => Ok(LogEntryType::X509Entry),
153            1 => Ok(LogEntryType::PrecertEntry),
154            _ => Err(BinaryParsingError::InvalidSequence(format!(
155                "Invalid LogEntryType id: {}",
156                id
157            ))),
158        }
159    }
160}
161
162#[derive(Debug)]
163#[allow(dead_code)]
164pub struct ASN1Cert {
165    pub length: u32,
166    pub certificate: Box<WrapX509Certificate>,
167}
168
169impl ASN1Cert {
170    pub fn parse(input: &mut &[u8]) -> Result<Self, BinaryParsingError> {
171        let length = read_u24_be(input)?;
172        let cert_data = read_exact_bytes(input, length as usize)?;
173
174        let wrapped_cert = WrapX509Certificate::try_from_der(cert_data)?;
175
176        Ok(ASN1Cert {
177            length,
178            certificate: Box::new(wrapped_cert),
179        })
180    }
181}
182
183#[cfg(feature = "debug-fmt")]
184impl fmt::Display for ASN1Cert {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        self.certificate.fmt(f)
187    }
188}
189
190#[derive(Debug)]
191pub struct ASN1CertChain {
192    pub length: u32,
193    pub certificates: Vec<ASN1Cert>,
194}
195
196impl ASN1CertChain {
197    pub fn parse(input: &mut &[u8]) -> Result<Self, BinaryParsingError> {
198        let total_chain_length = read_u24_be(input)?;
199        let mut certs_data_slice = read_exact_bytes(input, total_chain_length as usize)?;
200        let mut certificates = Vec::new();
201
202        let mut consumed_len = 0;
203        while consumed_len < total_chain_length {
204            let init_len = certs_data_slice.len();
205            if init_len == 0 {
206                if consumed_len < total_chain_length {
207                    return Err(BinaryParsingError::InsufficientData);
208                }
209
210                break;
211            }
212
213            let cert = ASN1Cert::parse(&mut certs_data_slice)?;
214            consumed_len += (init_len - certs_data_slice.len()) as u32;
215            certificates.push(cert);
216        }
217        if consumed_len != total_chain_length {
218            return Err(BinaryParsingError::InvalidSequence(format!(
219                "Invalid ASN1CertChain length: {}",
220                consumed_len
221            )));
222        }
223
224        Ok(ASN1CertChain {
225            length: total_chain_length,
226            certificates,
227        })
228    }
229}
230
231#[derive(Debug)]
232pub struct PrecertChainEntry {
233    pub pre_certificate: ASN1Cert,
234    pub precertificate_chain: ASN1CertChain,
235}
236
237impl PrecertChainEntry {
238    pub fn parse(input: &mut &[u8]) -> Result<Self, BinaryParsingError> {
239        let pre_cert = ASN1Cert::parse(input)?;
240        let pre_cert_chain = ASN1CertChain::parse(input)?;
241        Ok(PrecertChainEntry {
242            pre_certificate: pre_cert,
243            precertificate_chain: pre_cert_chain,
244        })
245    }
246}
247
248#[derive(Debug, PartialEq, Eq, Clone)]
249pub enum Version {
250    V1 = 0,
251}
252
253impl Version {
254    pub fn parse(input: &mut &[u8]) -> Result<Self, BinaryParsingError> {
255        let id = read_u8(input)?;
256        match id {
257            0 => Ok(Version::V1),
258            _ => Err(BinaryParsingError::InvalidSequence(format!(
259                "Unknown Version id: {}",
260                id
261            ))),
262        }
263    }
264}
265
266#[derive(Debug)]
267pub struct IssuerKeyHash([u8; 32]);
268
269impl IssuerKeyHash {
270    pub fn parse(input: &mut &[u8]) -> Result<Self, BinaryParsingError> {
271        let bytes = read_exact_bytes(input, 32)?;
272        let mut arr = [0u8; 32];
273        arr.copy_from_slice(bytes);
274        Ok(IssuerKeyHash(arr))
275    }
276}
277
278impl fmt::LowerHex for IssuerKeyHash {
279    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
280        for byte in &self.0 {
281            write!(f, "{:02x}", byte)?;
282        }
283        Ok(())
284    }
285}
286
287#[derive(Debug)]
288pub struct PreCert {
289    pub issuer_key_hash: IssuerKeyHash,
290    pub length: u32,
291    pub tbs_certificate: Box<WrapTbsCertificate>,
292}
293
294impl PreCert {
295    pub fn parse(input: &mut &[u8]) -> Result<Self, BinaryParsingError> {
296        let iss_key_hash = IssuerKeyHash::parse(input)?;
297        let length = read_u24_be(input)?;
298        let tbs_data = read_exact_bytes(input, length as usize)?;
299
300        let wrapped_tbs_cert = WrapTbsCertificate::try_from_der(tbs_data)?;
301
302        Ok(PreCert {
303            issuer_key_hash: iss_key_hash,
304            length,
305            tbs_certificate: Box::new(wrapped_tbs_cert),
306        })
307    }
308}
309
310#[cfg(feature = "debug-fmt")]
311impl fmt::Display for PreCert {
312    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
313        self.tbs_certificate.fmt(f)
314    }
315}
316
317#[derive(Debug, Clone)]
318pub struct CtExtensions {
319    pub length: u16,
320    pub extensions: Vec<u8>,
321}
322
323impl CtExtensions {
324    pub fn parse(input: &mut &[u8]) -> Result<Self, BinaryParsingError> {
325        let length = read_u16_be(input)?;
326        let ext_data = read_vec(input, length as usize)?;
327        Ok(CtExtensions {
328            length,
329            extensions: ext_data.to_vec(),
330        })
331    }
332}
333
334#[derive(Debug, PartialEq, Eq, Clone)]
335pub enum MerkleLeafType {
336    TimestampedEntry = 0,
337}
338
339impl MerkleLeafType {
340    pub fn parse(input: &mut &[u8]) -> Result<Self, BinaryParsingError> {
341        let id = read_u8(input)?;
342        match id {
343            0 => Ok(MerkleLeafType::TimestampedEntry),
344            _ => Err(BinaryParsingError::InvalidSequence(format!(
345                "Unknown MerkleLeafType id: {}",
346                id
347            ))),
348        }
349    }
350}
351
352#[derive(Debug)]
353pub enum TimestampedEntrySignedInner {
354    X509(ASN1Cert),
355    Precert(PreCert),
356}
357
358impl TimestampedEntrySignedInner {
359    pub fn parse(input: &mut &[u8], entry_type: &LogEntryType) -> Result<Self, BinaryParsingError> {
360        match entry_type {
361            LogEntryType::X509Entry => {
362                ASN1Cert::parse(input).map(TimestampedEntrySignedInner::X509)
363            }
364            LogEntryType::PrecertEntry => {
365                PreCert::parse(input).map(TimestampedEntrySignedInner::Precert)
366            }
367        }
368    }
369}
370
371#[derive(Debug)]
372pub struct TimestampedEntry {
373    pub timestamp: u64,
374    pub entry_type: LogEntryType,
375    pub signed_entry: TimestampedEntrySignedInner,
376    pub extensions: CtExtensions,
377}
378
379impl TimestampedEntry {
380    pub fn parse(input: &mut &[u8]) -> Result<Self, BinaryParsingError> {
381        let timestamp = read_u64_be(input)?;
382        let entry_type = LogEntryType::parse(input)?;
383
384        let signed_entry = TimestampedEntrySignedInner::parse(input, &entry_type)?;
385        let ext = CtExtensions::parse(input)?;
386
387        Ok(TimestampedEntry {
388            timestamp,
389            entry_type,
390            signed_entry,
391            extensions: ext,
392        })
393    }
394}
395
396#[derive(Debug)]
397pub struct MerkleTreeLeaf {
398    pub version: Version,
399    pub leaf_type: MerkleLeafType,
400    pub timestamped_entry: TimestampedEntry,
401}
402
403impl MerkleTreeLeaf {
404    pub fn parse(input: &mut &[u8]) -> Result<Self, BinaryParsingError> {
405        let ver = Version::parse(input)?;
406        let leaf_type = MerkleLeafType::parse(input)?;
407        let timestamped_entry = TimestampedEntry::parse(input)?;
408
409        Ok(MerkleTreeLeaf {
410            version: ver,
411            leaf_type,
412            timestamped_entry,
413        })
414    }
415}
416
417#[derive(Debug)]
418pub enum DecodedEntryInner {
419    X509(ASN1CertChain),
420    Precert(PrecertChainEntry),
421}
422
423/// A structure representing a log entry (parsed from the response of /ct/v1/get-entries).
424#[derive(Debug)]
425pub struct DecodedEntry {
426    pub leaf: MerkleTreeLeaf,
427    pub extra_data: DecodedEntryInner,
428    pub raw_leaf: Vec<u8>,
429}
430
431impl TryFrom<&Entry> for DecodedEntry {
432    type Error = CtLogError;
433
434    fn try_from(entry: &Entry) -> Result<Self, CtLogError> {
435        let decoded_leaf_bytes: Vec<u8> = BASE64_STANDARD.decode(entry.leaf_input.clone())?;
436
437        let mut leaf_in_slice = decoded_leaf_bytes.as_slice();
438        let leaf = MerkleTreeLeaf::parse(&mut leaf_in_slice)?;
439
440        if !leaf_in_slice.is_empty() {
441            return Err(BinaryParsingError::InvalidSequence(format!(
442                "Trailing data after parsing MerkleTreeLeaf: {} bytes left",
443                leaf_in_slice.len()
444            ))
445            .into());
446        }
447
448        let extra_data_decoded = BASE64_STANDARD.decode(&entry.extra_data)?;
449        let mut extra_data_slice = extra_data_decoded.as_slice();
450
451        let extra_data = match leaf.timestamped_entry.entry_type {
452            LogEntryType::X509Entry => {
453                let cert_chain = ASN1CertChain::parse(&mut extra_data_slice)?;
454                DecodedEntryInner::X509(cert_chain)
455            }
456            LogEntryType::PrecertEntry => {
457                let precert_chain = PrecertChainEntry::parse(&mut extra_data_slice)?;
458                DecodedEntryInner::Precert(precert_chain)
459            }
460        };
461
462        if !extra_data_slice.is_empty() {
463            return Err(BinaryParsingError::InvalidSequence(format!(
464                "Trailing data after parsing extra_data: {} bytes left",
465                extra_data_slice.len()
466            ))
467            .into());
468        }
469
470        Ok(Self {
471            leaf,
472            extra_data,
473            raw_leaf: decoded_leaf_bytes,
474        })
475    }
476}
477
478#[cfg(feature = "debug-fmt")]
479impl fmt::Display for DecodedEntry {
480    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
481        write!(
482            f,
483            "Timestamp={} ({}) ",
484            self.leaf.timestamped_entry.timestamp,
485            chrono::Utc
486                .timestamp_millis_opt(self.leaf.timestamped_entry.timestamp as i64)
487                .unwrap()
488        )?;
489
490        match (
491            &self.leaf.timestamped_entry.entry_type,
492            &self.leaf.timestamped_entry.signed_entry,
493        ) {
494            (LogEntryType::X509Entry, TimestampedEntrySignedInner::X509(certificate)) => {
495                writeln!(f, "X.509 certificate:")?;
496                writeln!(f, "{certificate}")?;
497
498                // TODO: print the chain
499            }
500            (LogEntryType::PrecertEntry, TimestampedEntrySignedInner::Precert(certificate)) => {
501                writeln!(
502                    f,
503                    "pre-certificate from issuer with keyhash {:x}:",
504                    certificate.issuer_key_hash
505                )?;
506                writeln!(f, "{certificate}")?;
507
508                // TODO: print the chain
509            }
510            _ => unreachable!(),
511        }
512
513        Ok(())
514    }
515}