1#![forbid(unsafe_code, unstable_features)]
12#![deny(
13 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::Vec};
25
26#[derive(Debug)]
32pub struct Log<'a> {
33 pub description: &'a str,
36
37 pub url: &'a str,
40
41 pub operated_by: &'a str,
44
45 pub key: &'a [u8],
50
51 pub id: [u8; 32],
54
55 pub max_merge_delay: usize,
58}
59
60#[derive(Debug, PartialEq, Clone, Copy)]
62pub enum Error {
63 MalformedSct,
65
66 InvalidSignature,
68
69 TimestampInFuture,
71
72 UnsupportedSctVersion,
74
75 UnknownLog,
77}
78
79impl Error {
80 pub fn should_be_fatal(&self) -> bool {
90 !matches!(self, Error::UnknownLog | Error::UnsupportedSctVersion)
91 }
92}
93
94fn lookup(logs: &[&Log], id: &[u8]) -> Result<usize, Error> {
95 for (i, l) in logs.iter().enumerate() {
96 if id == l.id {
97 return Ok(i);
98 }
99 }
100
101 Err(Error::UnknownLog)
102}
103
104fn decode_u64(inp: untrusted::Input) -> u64 {
105 let b = inp.as_slice_less_safe();
106 assert_eq!(b.len(), 8);
107 (b[0] as u64) << 56
108 | (b[1] as u64) << 48
109 | (b[2] as u64) << 40
110 | (b[3] as u64) << 32
111 | (b[4] as u64) << 24
112 | (b[5] as u64) << 16
113 | (b[6] as u64) << 8
114 | (b[7] as u64)
115}
116
117fn decode_u16(inp: untrusted::Input) -> u16 {
118 let b = inp.as_slice_less_safe();
119 assert_eq!(b.len(), 2);
120 (b[0] as u16) << 8 | (b[1] as u16)
121}
122
123fn write_u64(v: u64, out: &mut Vec<u8>) {
124 out.push((v >> 56) as u8);
125 out.push((v >> 48) as u8);
126 out.push((v >> 40) as u8);
127 out.push((v >> 32) as u8);
128 out.push((v >> 24) as u8);
129 out.push((v >> 16) as u8);
130 out.push((v >> 8) as u8);
131 out.push(v as u8);
132}
133
134fn write_u24(v: u32, out: &mut Vec<u8>) {
135 out.push((v >> 16) as u8);
136 out.push((v >> 8) as u8);
137 out.push(v as u8);
138}
139
140fn write_u16(v: u16, out: &mut Vec<u8>) {
141 out.push((v >> 8) as u8);
142 out.push(v as u8);
143}
144
145struct Sct<'a> {
146 log_id: &'a [u8],
147 timestamp: u64,
148 sig_alg: u16,
149 sig: &'a [u8],
150 exts: &'a [u8],
151}
152
153const ECDSA_SHA256: u16 = 0x0403;
154const ECDSA_SHA384: u16 = 0x0503;
155const RSA_PKCS1_SHA256: u16 = 0x0401;
156const RSA_PKCS1_SHA384: u16 = 0x0501;
157const SCT_V1: u8 = 0u8;
158const SCT_TIMESTAMP: u8 = 0u8;
159const SCT_X509_ENTRY: [u8; 2] = [0, 0];
160
161impl<'a> Sct<'a> {
162 fn verify(&self, key: &[u8], cert: &[u8]) -> Result<(), Error> {
163 let alg: &dyn ring::signature::VerificationAlgorithm = match self.sig_alg {
164 ECDSA_SHA256 => &ring::signature::ECDSA_P256_SHA256_ASN1,
165 ECDSA_SHA384 => &ring::signature::ECDSA_P384_SHA384_ASN1,
166 RSA_PKCS1_SHA256 => &ring::signature::RSA_PKCS1_2048_8192_SHA256,
167 RSA_PKCS1_SHA384 => &ring::signature::RSA_PKCS1_2048_8192_SHA384,
168 _ => return Err(Error::InvalidSignature),
169 };
170
171 let mut data = vec![SCT_V1, SCT_TIMESTAMP];
172 write_u64(self.timestamp, &mut data);
173 data.extend_from_slice(&SCT_X509_ENTRY);
174 write_u24(cert.len() as u32, &mut data);
175 data.extend_from_slice(cert);
176 write_u16(self.exts.len() as u16, &mut data);
177 data.extend_from_slice(self.exts);
178
179 let key = ring::signature::UnparsedPublicKey::new(alg, key);
180
181 key.verify(&data, self.sig)
182 .map_err(|_| Error::InvalidSignature)
183 }
184
185 fn parse(enc: &'a [u8]) -> Result<Sct<'a>, Error> {
186 let inp = untrusted::Input::from(enc);
187
188 inp.read_all(Error::MalformedSct, |rd| {
189 let version = rd.read_byte().map_err(|_| Error::MalformedSct)?;
190 if version != 0 {
191 return Err(Error::UnsupportedSctVersion);
192 }
193
194 let id = rd.read_bytes(32).map_err(|_| Error::MalformedSct)?;
195 let timestamp = rd
196 .read_bytes(8)
197 .map_err(|_| Error::MalformedSct)
198 .map(decode_u64)?;
199
200 let ext_len = rd
201 .read_bytes(2)
202 .map_err(|_| Error::MalformedSct)
203 .map(decode_u16)?;
204 let exts = rd
205 .read_bytes(ext_len as usize)
206 .map_err(|_| Error::MalformedSct)?;
207
208 let sig_alg = rd
209 .read_bytes(2)
210 .map_err(|_| Error::MalformedSct)
211 .map(decode_u16)?;
212 let sig_len = rd
213 .read_bytes(2)
214 .map_err(|_| Error::MalformedSct)
215 .map(decode_u16)?;
216 let sig = rd
217 .read_bytes(sig_len as usize)
218 .map_err(|_| Error::MalformedSct)?;
219
220 let ret = Sct {
221 log_id: id.as_slice_less_safe(),
222 timestamp,
223 sig_alg,
224 sig: sig.as_slice_less_safe(),
225 exts: exts.as_slice_less_safe(),
226 };
227
228 Ok(ret)
229 })
230 }
231}
232
233pub fn verify_sct(cert: &[u8], sct: &[u8], at_time: u64, logs: &[&Log]) -> Result<usize, Error> {
241 let sct = Sct::parse(sct)?;
242 let i = lookup(logs, sct.log_id)?;
243 let log = logs[i];
244 sct.verify(log.key, cert)?;
245
246 if sct.timestamp > at_time {
247 return Err(Error::TimestampInFuture);
248 }
249
250 Ok(i)
251}
252
253#[cfg(test)]
254mod tests;
255#[cfg(test)]
256mod tests_generated;
257#[cfg(test)]
258mod tests_google;