ct_sct/
sct.rs

1//! Signed Certificate Timestamps (SCTs)
2use std::{fmt::Display, time::Duration};
3
4use const_oid::{AssociatedOid, ObjectIdentifier};
5use der::asn1::{GeneralizedTime, OctetString, UtcTime};
6use x509_cert::{ext::AsExtension, impl_newtype, time::Time};
7
8// Remove this constant when the upstream PR is merged:
9// https://github.com/RustCrypto/formats/pull/1094
10/// OID for signed certificate timestamps extension
11pub const CT_PRECERT_SCTS: ObjectIdentifier =
12    ObjectIdentifier::new_unwrap("1.3.6.1.4.1.11129.2.4.2");
13
14/// Signed certificate timestamps list
15pub struct SctList(pub OctetString);
16
17impl AssociatedOid for SctList {
18    const OID: ObjectIdentifier = CT_PRECERT_SCTS;
19}
20
21impl_newtype!(SctList, OctetString);
22
23impl AsExtension for SctList {
24    fn critical(
25        &self,
26        _subject: &x509_cert::name::Name,
27        _extensions: &[x509_cert::ext::Extension],
28    ) -> bool {
29        false
30    }
31}
32
33/// Error returned when decoding an SCT list
34#[derive(Debug)]
35pub enum Error {
36    /// Returned if the SCT list's length from the prefix doesn't match the byte slice length
37    DecodeTlsSctListError,
38    /// Returned if the SCT's length from the prefix is greater than the byte slice length
39    DecodeTlsSctError,
40    /// Returned if the decoded [Version] is invalid
41    DecodeVersionError,
42    /// Returned if not enough bytes are left in the byte slice to decode an integer
43    DecodeIntError,
44    /// Returned if less than 32 bytes are left in the byte slice to decode a log id
45    DecodeLogIdError,
46    /// Returned if timestamp can't be decoded into a [der::asn1::UtcTime] or a [der::asn1::GeneralizedTime]
47    DecodeTimestampError,
48    /// Returned if the extenions' length from the prefix is greater than the byte slice length
49    DecodeExtensionsError,
50    /// Returned if the digitally signed struct's length from the prefix is greater than the byte slice length
51    DecodeDigitallySignedError,
52    /// Returned if the decoded [HashAlgo] is invalid
53    DecodeHashAlgoError,
54    /// Returned if the decoded [SignatureAlgo] is invalid
55    DecodeSignAlgoError,
56}
57
58/// Decodes a u8 from the byte slice and returns the rest of the bytes
59fn decode_u8_be(bytes: &[u8]) -> Result<(u8, &[u8]), Error> {
60    if bytes.is_empty() {
61        return Err(Error::DecodeIntError);
62    }
63    let result = u8::from_be_bytes(bytes[..1].try_into().unwrap());
64
65    Ok((result, &bytes[1..]))
66}
67
68/// Decodes a u16 as a big-endian integer from the byte slice and returns the rest of the bytes
69fn decode_u16_be(bytes: &[u8]) -> Result<(u16, &[u8]), Error> {
70    if bytes.len() < 2 {
71        return Err(Error::DecodeIntError);
72    }
73    let result = u16::from_be_bytes(bytes[..2].try_into().unwrap());
74
75    Ok((result, &bytes[2..]))
76}
77
78/// Decodes a u64 as a big-endian integer from the byte slice and returns the rest of the bytes
79fn decode_u64_be(bytes: &[u8]) -> Result<(u64, &[u8]), Error> {
80    if bytes.len() < 8 {
81        return Err(Error::DecodeIntError);
82    }
83    let result = u64::from_be_bytes(bytes[..8].try_into().unwrap());
84
85    Ok((result, &bytes[8..]))
86}
87
88/// A TLS SCT list embedded in an x509 certificate
89pub struct TlsSctList {
90    /// A vector of TLS Signed Certificate Timestamps.
91    pub scts: Vec<TlsSct>,
92}
93
94impl TlsSctList {
95    /// Decodes a [TlsSctList] from a [SctList]
96    pub fn from_sct_list(sct_list: &SctList) -> Result<Self, Error> {
97        let bytes = sct_list.0.as_bytes();
98        TlsSctList::decode(bytes)
99    }
100
101    /// Decodes a [TlsSctList] from a TLS encoded byte slice. See RFC 5246 to know about TLS encoding.
102    fn decode(bytes: &[u8]) -> Result<Self, Error>
103    where
104        Self: Sized,
105    {
106        let (len, bytes) = decode_u16_be(bytes)?;
107        let len = len as usize;
108        if len != bytes.len() {
109            return Err(Error::DecodeTlsSctListError);
110        }
111        let mut scts = Vec::new();
112        let mut bytes = bytes;
113        while !bytes.is_empty() {
114            let (sct, rest) = TlsSct::decode(bytes)?;
115            bytes = rest;
116            scts.push(sct);
117        }
118        Ok(Self { scts })
119    }
120}
121
122/// A TLS Signed Certificate Timestamp.
123pub struct TlsSct {
124    /// Version of the SCT
125    pub version: Version,
126    /// Log id of the SCT
127    pub log_id: [u8; 32],
128    /// Timestamp of the SCT
129    pub timestamp: Time,
130    /// Extensions of the SCT
131    pub extensions: Vec<u8>,
132    /// Signature of the SCT
133    pub sign: DigitallySigned,
134}
135
136impl TlsSct {
137    /// Decodes a [TlsSct] from a byte slice
138    fn decode(bytes: &[u8]) -> Result<(Self, &[u8]), Error>
139    where
140        Self: Sized,
141    {
142        let (len, bytes) = decode_u16_be(bytes)?;
143        let len = len as usize;
144        if len > bytes.len() {
145            return Err(Error::DecodeTlsSctError);
146        }
147        let (version, bytes) = Version::decode(bytes)?;
148        let (log_id, bytes) = Self::decode_log_id(bytes)?;
149        let (timestamp, bytes) = Self::decode_timestamp(bytes)?;
150        let (extensions, bytes) = Self::decode_extensions(bytes)?;
151        let (sign, bytes) = DigitallySigned::decode(bytes)?;
152
153        Ok((
154            Self {
155                version,
156                log_id,
157                timestamp,
158                extensions,
159                sign,
160            },
161            bytes,
162        ))
163    }
164
165    /// Decodes a log id from a byte slice
166    fn decode_log_id(bytes: &[u8]) -> Result<([u8; 32], &[u8]), Error> {
167        if bytes.len() < 32 {
168            return Err(Error::DecodeLogIdError);
169        }
170        let result = bytes[..32].try_into().unwrap();
171
172        Ok((result, &bytes[32..]))
173    }
174
175    /// Decodes a timestamp from a byte slice
176    fn decode_timestamp(bytes: &[u8]) -> Result<(Time, &[u8]), Error> {
177        let (timestamp, bytes) = decode_u64_be(bytes)?;
178        let timestamp = Duration::from_millis(timestamp);
179        let generalized_time = GeneralizedTime::from_unix_duration(timestamp);
180        if let Ok(generalized_time) = generalized_time {
181            Ok((generalized_time.into(), bytes))
182        } else {
183            let utc_time = UtcTime::from_unix_duration(timestamp);
184            if let Ok(utc_time) = utc_time {
185                Ok((utc_time.into(), bytes))
186            } else {
187                Err(Error::DecodeTimestampError)
188            }
189        }
190    }
191
192    /// Decodes extensions from a byte slice
193    fn decode_extensions(bytes: &[u8]) -> Result<(Vec<u8>, &[u8]), Error> {
194        let (len, rest) = decode_u16_be(bytes)?;
195        let len = len as usize;
196        if len > bytes.len() {
197            return Err(Error::DecodeExtensionsError);
198        }
199        let mut vec = Vec::with_capacity(len);
200        vec.extend_from_slice(&rest[..len]);
201        Ok((vec, &rest[len..]))
202    }
203}
204
205/// Version of a Signed Certificate Timestamp
206#[derive(Debug)]
207pub enum Version {
208    /// Version 1
209    V1 = 0,
210}
211
212impl Version {
213    /// Decodes a [Version] from a byte slice
214    fn decode(bytes: &[u8]) -> Result<(Self, &[u8]), Error> {
215        let (version, bytes) = decode_u8_be(bytes)?;
216        Ok((version.try_into()?, bytes))
217    }
218}
219
220impl TryFrom<u8> for Version {
221    type Error = Error;
222
223    fn try_from(value: u8) -> Result<Self, Self::Error> {
224        match value {
225            0 => Ok(Version::V1),
226            _ => Err(Error::DecodeVersionError),
227        }
228    }
229}
230
231impl Display for Version {
232    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
233        match self {
234            Version::V1 => write!(f, "v1"),
235        }
236    }
237}
238
239/// Digital signature of a Signed Certificate Timestamp
240#[derive(Debug)]
241pub struct DigitallySigned {
242    /// [SignAndHashAlgo] of the struct
243    pub sign_and_hash_algo: SignAndHashAlgo,
244    /// Signature of the struct
245    pub sign: Vec<u8>,
246}
247
248impl DigitallySigned {
249    /// Decodes a [DigitallySigned] from a byte slice
250    fn decode(bytes: &[u8]) -> Result<(Self, &[u8]), Error> {
251        let (sign_and_hash_algo, bytes) = SignAndHashAlgo::decode(bytes)?;
252
253        let (len, rest) = decode_u16_be(bytes)?;
254        let len = len as usize;
255        if len > bytes.len() {
256            return Err(Error::DecodeDigitallySignedError);
257        }
258        let mut sign = Vec::with_capacity(len);
259        sign.extend_from_slice(&rest[..len]);
260        Ok((
261            Self {
262                sign_and_hash_algo,
263                sign,
264            },
265            &rest[len..],
266        ))
267    }
268}
269
270/// A pair of signature algorithm and a hash algorithm
271#[derive(Debug)]
272pub struct SignAndHashAlgo {
273    /// Signature algorithm
274    pub sign: SignatureAlgo,
275    /// Hash algorithm
276    pub hash: HashAlgo,
277}
278
279impl SignAndHashAlgo {
280    /// Decodes a [SignAndHashAlgo] from a byte slice
281    fn decode(bytes: &[u8]) -> Result<(Self, &[u8]), Error> {
282        let (hash, bytes) = HashAlgo::decode(bytes)?;
283        let (sign, bytes) = SignatureAlgo::decode(bytes)?;
284        Ok((Self { sign, hash }, bytes))
285    }
286}
287
288impl Display for SignAndHashAlgo {
289    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290        write!(f, "{}-with-{}", self.sign, self.hash)
291    }
292}
293
294/// Signature algorithms, as defined in RFC5246 and RFC8422
295#[derive(Debug)]
296pub enum SignatureAlgo {
297    /// Anonymous signature algo
298    Anonymous = 0,
299    /// RSA signature algo
300    Rsa = 1,
301    /// DSA signature algo
302    Dsa = 2,
303    /// ECDSA signature algo
304    Ecdsa = 3,
305    /// ED25519 signature algo
306    Ed25519 = 7,
307    /// ED448 signature algo
308    Ed448 = 8,
309}
310
311impl SignatureAlgo {
312    /// Decodes a [SignatureAlgo] from a byte slice
313    fn decode(bytes: &[u8]) -> Result<(Self, &[u8]), Error> {
314        let (algo, bytes) = decode_u8_be(bytes)?;
315        Ok((algo.try_into()?, bytes))
316    }
317}
318
319impl TryFrom<u8> for SignatureAlgo {
320    type Error = Error;
321
322    fn try_from(value: u8) -> Result<Self, Self::Error> {
323        match value {
324            0 => Ok(SignatureAlgo::Anonymous),
325            1 => Ok(SignatureAlgo::Rsa),
326            2 => Ok(SignatureAlgo::Dsa),
327            3 => Ok(SignatureAlgo::Ecdsa),
328            7 => Ok(SignatureAlgo::Ed25519),
329            8 => Ok(SignatureAlgo::Ed448),
330            _ => Err(Error::DecodeSignAlgoError),
331        }
332    }
333}
334
335impl Display for SignatureAlgo {
336    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
337        match self {
338            SignatureAlgo::Anonymous => write!(f, "anonymous"),
339            SignatureAlgo::Rsa => write!(f, "rsa"),
340            SignatureAlgo::Dsa => write!(f, "dsa"),
341            SignatureAlgo::Ecdsa => write!(f, "ecdsa"),
342            SignatureAlgo::Ed25519 => write!(f, "ed25519"),
343            SignatureAlgo::Ed448 => write!(f, "ed448"),
344        }
345    }
346}
347
348/// Hash algorithms, as defined in RFC5246 and RFC8422
349#[derive(Debug)]
350pub enum HashAlgo {
351    /// No algorithm
352    None = 0,
353    /// MD5 algorithm
354    Md5 = 1,
355    /// SHA1 algorithm
356    Sha1 = 2,
357    /// SHA224 algorithm
358    Sha224 = 3,
359    /// SHA256 algorithm
360    Sha256 = 4,
361    /// SHA384 algorithm
362    Sha384 = 5,
363    /// SHA512 algorithm
364    Sha512 = 6,
365    /// Intrinsic algorithm
366    Intrinsic = 8,
367}
368
369impl HashAlgo {
370    /// Decodes a [HashAlgo] from a byte slice
371    fn decode(bytes: &[u8]) -> Result<(Self, &[u8]), Error> {
372        let (algo, bytes) = decode_u8_be(bytes)?;
373        Ok((algo.try_into()?, bytes))
374    }
375}
376
377impl TryFrom<u8> for HashAlgo {
378    type Error = Error;
379
380    fn try_from(value: u8) -> Result<Self, Self::Error> {
381        match value {
382            0 => Ok(HashAlgo::None),
383            1 => Ok(HashAlgo::Md5),
384            2 => Ok(HashAlgo::Sha1),
385            3 => Ok(HashAlgo::Sha224),
386            4 => Ok(HashAlgo::Sha256),
387            5 => Ok(HashAlgo::Sha384),
388            6 => Ok(HashAlgo::Sha512),
389            8 => Ok(HashAlgo::Intrinsic),
390            _ => Err(Error::DecodeHashAlgoError),
391        }
392    }
393}
394
395impl Display for HashAlgo {
396    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
397        match self {
398            HashAlgo::None => write!(f, "NONE"),
399            HashAlgo::Md5 => write!(f, "MD5"),
400            HashAlgo::Sha1 => write!(f, "SHA1"),
401            HashAlgo::Sha224 => write!(f, "SHA224"),
402            HashAlgo::Sha256 => write!(f, "SHA256"),
403            HashAlgo::Sha384 => write!(f, "SHA384"),
404            HashAlgo::Sha512 => write!(f, "SHA512"),
405            HashAlgo::Intrinsic => write!(f, "INTRINSIC"),
406        }
407    }
408}