Skip to content

Commit aa66e1c

Browse files
committed
rev sct support
1 parent d6a06b1 commit aa66e1c

File tree

5 files changed

+78
-42
lines changed

5 files changed

+78
-42
lines changed

src/end_entity.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,9 @@ impl EndEntityCert<'_> {
176176
/// iterator.
177177
pub fn sct_log_timestamps<'a>(
178178
&'a self,
179-
) -> Result<
180-
impl Iterator<Item = Result<(sct::LogId, sct::Timestamp), sct::Error>> + 'a,
181-
sct::Error,
182-
> {
183-
Ok(sct::SctParser::new(self.scts)?.map(|sct| sct.map(|sct| (sct.log_id, sct.timestamp))))
179+
) -> Result<impl Iterator<Item = Result<sct::LogIdAndTimestamp, sct::Error>> + 'a, sct::Error>
180+
{
181+
Ok(sct::SctParser::new(self.scts)?.map(|sct| sct.map(|sct| sct.log_id_and_timestamp())))
184182
}
185183
}
186184

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ mod error;
6666
#[cfg(feature = "ring")]
6767
mod ring_algs;
6868
mod rpk_entity;
69-
#[allow(missing_docs, dead_code)]
69+
/// Processing of certificate transparency SCTs.
7070
pub mod sct;
7171
mod signed_data;
7272
mod subject_name;

src/sct.rs

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,5 @@
11
use core::marker::PhantomData;
22

3-
/// Reads a `SignedCertificateTimestampList` encoding, yielding each `SignedCertificateTimestamp`.
4-
pub(crate) fn iter_scts<'a>(
5-
bytes: untrusted::Input<'a>,
6-
) -> Result<impl Iterator<Item = Result<SignedCertificateTimestamp<'a>, Error>> + 'a, Error> {
7-
let items_body = bytes.read_all(Error::MalformedSct, |rd| read_field(rd, non_zero_u16_len))?;
8-
9-
let mut reader = untrusted::Reader::new(items_body);
10-
11-
Ok(core::iter::from_fn(move || {
12-
let item = read_field(&mut reader, non_zero_u16_len).ok()?;
13-
Some(SignedCertificateTimestamp::try_from(
14-
item.as_slice_less_safe(),
15-
))
16-
}))
17-
}
18-
193
pub(crate) struct SctParser<'a> {
204
reader: untrusted::Reader<'a>,
215
}
@@ -53,11 +37,20 @@ impl<'a> Iterator for SctParser<'a> {
5337
/// [RFC6962]: https://www.rfc-editor.org/rfc/rfc6962.html#section-3.2
5438
#[derive(Debug)]
5539
pub(crate) struct SignedCertificateTimestamp<'a> {
56-
pub(crate) log_id: LogId,
57-
pub(crate) timestamp: Timestamp,
40+
log_id: LogId,
41+
timestamp: Timestamp,
5842
_future_lifetime_for_signature: PhantomData<&'a ()>,
5943
}
6044

45+
impl SignedCertificateTimestamp<'_> {
46+
pub(crate) fn log_id_and_timestamp(&self) -> LogIdAndTimestamp {
47+
LogIdAndTimestamp {
48+
log_id: self.log_id.0,
49+
timestamp: self.timestamp.0,
50+
}
51+
}
52+
}
53+
6154
impl<'a> TryFrom<&'a [u8]> for SignedCertificateTimestamp<'a> {
6255
type Error = Error;
6356

@@ -84,11 +77,20 @@ impl<'a> TryFrom<&'a [u8]> for SignedCertificateTimestamp<'a> {
8477
}
8578
}
8679

80+
/// The certificate transparency log ID and associated inclusion timestamp.
81+
#[derive(Debug, PartialEq)]
82+
pub struct LogIdAndTimestamp {
83+
/// Log ID
84+
pub log_id: [u8; 32],
85+
/// Inclusion timestamp
86+
pub timestamp: u64,
87+
}
88+
8789
#[derive(Debug)]
88-
pub struct LogId(pub [u8; 32]);
90+
struct LogId([u8; 32]);
8991

9092
#[derive(Debug)]
91-
pub struct Timestamp(pub u64);
93+
struct Timestamp(u64);
9294

9395
/// Read a length-prefixed field from `rd`.
9496
///
@@ -126,7 +128,9 @@ fn non_zero_u16_len(bytes: [u8; 2]) -> Result<usize, Error> {
126128
Ok(len)
127129
}
128130

131+
/// Possible errors from SCT parsing and processing
129132
#[derive(Clone, Debug, PartialEq, Eq)]
133+
#[non_exhaustive]
130134
pub enum Error {
131135
/// The SCT was somehow misencoded, truncated or otherwise corrupt.
132136
MalformedSct,

src/verify_cert.rs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ use crate::crl::RevocationOptions;
2626
use crate::der::{self, FromDer};
2727
use crate::end_entity::EndEntityCert;
2828
use crate::error::Error;
29-
use crate::sct::{self, LogId, SctParser, Timestamp};
3029
use crate::{public_values_eq, subject_name};
3130

3231
// Use `'a` for lifetimes that we don't care about, `'p` for lifetimes that become a part of
@@ -198,21 +197,6 @@ impl<'p> VerifiedPath<'p> {
198197
}
199198
}
200199

201-
/// Returns the SCT logs that contributed to the SCTs included in the certificate.
202-
///
203-
/// Note this method does not verify the SCTs themselves, but does require that
204-
/// the certificate chain was previously verified by the caller. This is demonstrated
205-
/// by the `verified_path` parameter.
206-
///
207-
/// If the certificate does not contain an SCT extension, this method returns
208-
/// `Ok(Vec::new())`.
209-
pub fn sct_log_timestamps(
210-
&self,
211-
) -> Result<impl Iterator<Item = Result<(LogId, Timestamp), sct::Error>> + 'p, sct::Error> {
212-
Ok(SctParser::new(self.end_entity.scts)?
213-
.map(|sct| sct.map(|sct| (sct.log_id, sct.timestamp))))
214-
}
215-
216200
/// Yields a (double-ended) iterator over the intermediate certificates in this path.
217201
pub fn intermediate_certificates(&'p self) -> IntermediateIterator<'p> {
218202
IntermediateIterator {

tests/integration.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use core::slice;
1919
use core::time::Duration;
2020

2121
use pki_types::{CertificateDer, UnixTime};
22+
use webpki::sct::LogIdAndTimestamp;
2223
use webpki::{ExtendedKeyUsage, anchor_from_trusted_cert};
2324

2425
/* Checks we can verify netflix's cert chain. This is notable
@@ -463,3 +464,52 @@ fn cert_time_validity() {
463464
})
464465
);
465466
}
467+
468+
#[test]
469+
fn with_scts() {
470+
let ee: &[u8] = include_bytes!("cloudflare_dns/ee.der");
471+
let ee = CertificateDer::from(ee);
472+
let cert = webpki::EndEntityCert::try_from(&ee).unwrap();
473+
474+
let expect_scts = vec![
475+
LogIdAndTimestamp {
476+
log_id: [
477+
41, 121, 190, 240, 158, 57, 57, 33, 240, 86, 115, 159, 99, 165, 119, 229, 190, 87,
478+
125, 156, 96, 10, 248, 249, 77, 93, 38, 92, 37, 93, 199, 132,
479+
],
480+
timestamp: 1635197764079,
481+
},
482+
LogIdAndTimestamp {
483+
log_id: [
484+
81, 163, 176, 245, 253, 1, 121, 156, 86, 109, 184, 55, 120, 143, 12, 164, 122, 204,
485+
27, 39, 203, 247, 158, 136, 66, 154, 13, 254, 212, 139, 5, 229,
486+
],
487+
timestamp: 1635197764090,
488+
},
489+
LogIdAndTimestamp {
490+
log_id: [
491+
65, 200, 202, 177, 223, 34, 70, 74, 16, 198, 161, 58, 9, 66, 135, 94, 78, 49, 139,
492+
27, 3, 235, 235, 75, 199, 104, 240, 144, 98, 150, 6, 246,
493+
],
494+
timestamp: 1635197764024,
495+
},
496+
];
497+
assert_eq!(
498+
Ok(expect_scts),
499+
cert.sct_log_timestamps()
500+
.unwrap()
501+
.collect::<Result<Vec<_>, _>>()
502+
);
503+
}
504+
505+
#[test]
506+
fn no_scts() {
507+
let der = CertificateDer::from(&include_bytes!("misc/uri_san_ee.der")[..]);
508+
let cert = webpki::EndEntityCert::try_from(&der).unwrap();
509+
assert_eq!(
510+
Ok(vec![]),
511+
cert.sct_log_timestamps()
512+
.unwrap()
513+
.collect::<Result<Vec<_>, _>>()
514+
);
515+
}

0 commit comments

Comments
 (0)