sct/
lib.rs

1//! # SCT.rs: SCT verification library
2//! This library implements verification of Signed Certificate Timestamps.
3//! These are third-party assurances that a particular certificate has
4//! been included in a Certificate Transparency log.
5//!
6//! See RFC6962 for the details of the formats implemented here.
7//!
8//! It is intended to be useful to libraries which perform certificate
9//! validation, OCSP libraries, and TLS libraries.
10
11#![forbid(unsafe_code,
12          unstable_features)]
13#![deny(trivial_casts,
14        trivial_numeric_casts,
15        missing_docs,
16        unused_import_braces,
17        unused_extern_crates,
18        unused_qualifications)]
19
20#![no_std]
21
22extern crate alloc;
23
24use alloc::vec::Vec;
25
26/// Describes a CT log
27///
28/// This structure contains some metadata fields not used by the library.
29/// Rationale: it makes sense to keep this metadata with the other
30/// values for review purposes.
31#[derive(Debug)]
32pub struct Log<'a> {
33    /// The operator's name/description of the log.
34    /// This field is not used by the library.
35    pub description: &'a str,
36
37    /// The certificate submission url.
38    /// This field is not used by the library.
39    pub url: &'a str,
40
41    /// Which entity operates the log.
42    /// This field is not used by the library.
43    pub operated_by: &'a str,
44
45    /// Public key usable for verifying certificates.
46    /// TODO: fixme format of this; should be a SPKI
47    /// so the `id` is verifiable, but currently is a
48    /// raw public key (like, an ECPoint or RSAPublicKey).
49    pub key: &'a [u8],
50
51    /// Key hash, which is SHA256 applied to the SPKI
52    /// encoding.
53    pub id: [u8; 32],
54
55    /// The log's maximum merge delay.
56    /// This field is not used by the library.
57    pub max_merge_delay: usize
58}
59
60/// How sct.rs reports errors.
61#[derive(Debug, PartialEq, Clone, Copy)]
62pub enum Error {
63    /// The SCT was somehow misencoded, truncated or otherwise corrupt.
64    MalformedSCT,
65
66    /// The SCT contained an invalid signature.
67    InvalidSignature,
68
69    /// The SCT was signed in the future.  Clock skew?
70    TimestampInFuture,
71
72    /// The SCT had a version that this library does not handle.
73    UnsupportedSCTVersion,
74
75    /// The SCT was refers to an unknown log.
76    UnknownLog,
77}
78
79impl Error {
80    /// Applies a suggested policy for error handling:
81    ///
82    /// Returns `true` if the error should end processing
83    /// for whatever the SCT is attached to (like, abort a TLS
84    /// handshake).
85    ///
86    /// Returns `false` if this error should be a 'soft failure'
87    /// -- the SCT is unverifiable with this library and set of
88    /// logs.
89    pub fn should_be_fatal(&self) -> bool {
90        match *self {
91            Error::UnknownLog
92                | Error::UnsupportedSCTVersion => false,
93            _ => true
94        }
95    }
96}
97
98fn lookup(logs: &[&Log], id: &[u8]) -> Result<usize, Error> {
99    for (i, l) in logs.iter().enumerate() {
100        if id == &l.id {
101            return Ok(i);
102        }
103    }
104
105    Err(Error::UnknownLog)
106}
107
108fn decode_u64(inp: untrusted::Input) -> u64 {
109    let b = inp.as_slice_less_safe();
110    assert_eq!(b.len(), 8);
111    (b[0] as u64) << 56 |
112        (b[1] as u64) << 48 |
113        (b[2] as u64) << 40 |
114        (b[3] as u64) << 32 |
115        (b[4] as u64) << 24 |
116        (b[5] as u64) << 16 |
117        (b[6] as u64) << 8 |
118        (b[7] as u64)
119}
120
121fn decode_u16(inp: untrusted::Input) -> u16 {
122    let b = inp.as_slice_less_safe();
123    assert_eq!(b.len(), 2);
124    (b[0] as u16) << 8 | (b[1] as u16)
125}
126
127fn write_u64(v: u64, out: &mut Vec<u8>) {
128    out.push((v >> 56) as u8);
129    out.push((v >> 48) as u8);
130    out.push((v >> 40) as u8);
131    out.push((v >> 32) as u8);
132    out.push((v >> 24) as u8);
133    out.push((v >> 16) as u8);
134    out.push((v >> 8) as u8);
135    out.push(v as u8);
136}
137
138fn write_u24(v: u32, out: &mut Vec<u8>) {
139    out.push((v >> 16) as u8);
140    out.push((v >> 8) as u8);
141    out.push(v as u8);
142}
143
144fn write_u16(v: u16, out: &mut Vec<u8>) {
145    out.push((v >> 8) as u8);
146    out.push(v as u8);
147}
148
149struct SCT<'a> {
150    log_id: &'a [u8],
151    timestamp: u64,
152    sig_alg: u16,
153    sig: &'a [u8],
154    exts: &'a [u8],
155}
156
157const ECDSA_SHA256: u16 = 0x0403;
158const ECDSA_SHA384: u16 = 0x0503;
159const RSA_PKCS1_SHA256: u16 = 0x0401;
160const RSA_PKCS1_SHA384: u16 = 0x0501;
161const SCT_V1: u8 = 0u8;
162const SCT_TIMESTAMP: u8 = 0u8;
163const SCT_X509_ENTRY: [u8; 2] = [0, 0];
164
165impl<'a> SCT<'a> {
166    fn verify(&self, key: &[u8], cert: &[u8]) -> Result<(), Error> {
167        let alg: &dyn ring::signature::VerificationAlgorithm = match self.sig_alg {
168            ECDSA_SHA256 => &ring::signature::ECDSA_P256_SHA256_ASN1,
169            ECDSA_SHA384 => &ring::signature::ECDSA_P384_SHA384_ASN1,
170            RSA_PKCS1_SHA256 => &ring::signature::RSA_PKCS1_2048_8192_SHA256,
171            RSA_PKCS1_SHA384 => &ring::signature::RSA_PKCS1_2048_8192_SHA384,
172            _ => return Err(Error::InvalidSignature)
173        };
174
175        let mut data = Vec::new();
176        data.push(SCT_V1);
177        data.push(SCT_TIMESTAMP);
178        write_u64(self.timestamp, &mut data);
179        data.extend_from_slice(&SCT_X509_ENTRY);
180        write_u24(cert.len() as u32, &mut data);
181        data.extend_from_slice(cert);
182        write_u16(self.exts.len() as u16, &mut data);
183        data.extend_from_slice(self.exts);
184
185        let key = ring::signature::UnparsedPublicKey::new(alg, key);
186
187        key.verify(&data, self.sig)
188            .map_err(|_| Error::InvalidSignature)
189    }
190
191    fn parse(enc: &'a [u8]) -> Result<SCT<'a>, Error> {
192        let inp = untrusted::Input::from(enc);
193
194        inp.read_all(
195            Error::MalformedSCT,
196            |rd| {
197                let version = rd.read_byte()
198                    .map_err(|_| Error::MalformedSCT)?;
199                if version != 0 {
200                    return Err(Error::UnsupportedSCTVersion);
201                }
202
203                let id = rd.read_bytes(32)
204                    .map_err(|_| Error::MalformedSCT)?;
205                let timestamp = rd.read_bytes(8)
206                    .map_err(|_| Error::MalformedSCT)
207                    .map(decode_u64)?;
208
209                let ext_len = rd.read_bytes(2)
210                    .map_err(|_| Error::MalformedSCT)
211                    .map(decode_u16)?;
212                let exts = rd.read_bytes(ext_len as usize)
213                    .map_err(|_| Error::MalformedSCT)?;
214
215                let sig_alg = rd.read_bytes(2)
216                    .map_err(|_| Error::MalformedSCT)
217                    .map(decode_u16)?;
218                let sig_len = rd.read_bytes(2)
219                    .map_err(|_| Error::MalformedSCT)
220                    .map(decode_u16)?;
221                let sig = rd.read_bytes(sig_len as usize)
222                    .map_err(|_| Error::MalformedSCT)?;
223
224                let ret = SCT {
225                    log_id: id.as_slice_less_safe(),
226                    timestamp: timestamp,
227                    sig_alg: sig_alg,
228                    sig: sig.as_slice_less_safe(),
229                    exts: exts.as_slice_less_safe(),
230                };
231
232                Ok(ret)
233            })
234    }
235}
236
237/// Verifies that the SCT `sct` (a `SignedCertificateTimestamp` encoding)
238/// is a correctly signed timestamp for `cert` (a DER-encoded X.509 end-entity
239/// certificate) valid `at_time`.  `logs` describe the CT logs trusted by
240/// the caller to sign such an SCT.
241///
242/// On success, this function returns the log used as an index into `logs`.
243/// Otherwise, it returns an `Error`.
244pub fn verify_sct(cert: &[u8],
245                  sct: &[u8],
246                  at_time: u64,
247                  logs: &[&Log]) -> Result<usize, Error> {
248    let sct = SCT::parse(sct)?;
249    let i = lookup(logs, &sct.log_id)?;
250    let log = logs[i];
251    sct.verify(log.key, cert)?;
252
253    if sct.timestamp > at_time {
254        return Err(Error::TimestampInFuture);
255    }
256
257    Ok(i)
258}
259
260#[cfg(test)]
261mod tests_google;
262#[cfg(test)]
263mod tests_generated;
264#[cfg(test)]
265mod tests;