/rust/registry/src/index.crates.io-1949cf8c6b5b557f/x509-parser-0.15.1/src/extensions/sct.rs
Line | Count | Source |
1 | | //! Certificate transparency [RFC6962](https://datatracker.ietf.org/doc/html/rfc6962) |
2 | | //! |
3 | | //! Code borrowed from tls-parser crate (file <https://github.com/rusticata/tls-parser/blob/tls-parser-0.11.0/src/certificate_transparency.rs>) |
4 | | |
5 | | use std::convert::TryInto; |
6 | | |
7 | | use asn1_rs::FromDer; |
8 | | use der_parser::error::BerError; |
9 | | use nom::bytes::streaming::take; |
10 | | use nom::combinator::{complete, map_parser}; |
11 | | use nom::multi::{length_data, many1}; |
12 | | use nom::number::streaming::{be_u16, be_u64, be_u8}; |
13 | | use nom::IResult; |
14 | | |
15 | | #[derive(Clone, Debug, PartialEq, Eq)] |
16 | | pub struct SignedCertificateTimestamp<'a> { |
17 | | pub version: CtVersion, |
18 | | pub id: CtLogID<'a>, |
19 | | pub timestamp: u64, |
20 | | pub extensions: CtExtensions<'a>, |
21 | | pub signature: DigitallySigned<'a>, |
22 | | } |
23 | | |
24 | | /// Certificate Transparency Version as defined in |
25 | | /// [RFC6962 Section 3.2](https://datatracker.ietf.org/doc/html/rfc6962#section-3.2) |
26 | | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
27 | | pub struct CtVersion(pub u8); |
28 | | |
29 | | impl CtVersion { |
30 | | pub const V1: CtVersion = CtVersion(0); |
31 | | } |
32 | | |
33 | | /// LogID as defined in |
34 | | /// [RFC6962 Section 3.2](https://datatracker.ietf.org/doc/html/rfc6962#section-3.2) |
35 | | #[derive(Clone, Debug, PartialEq, Eq)] |
36 | | pub struct CtLogID<'a> { |
37 | | pub key_id: &'a [u8; 32], |
38 | | } |
39 | | |
40 | | /// CtExtensions as defined in |
41 | | /// [RFC6962 Section 3.2](https://datatracker.ietf.org/doc/html/rfc6962#section-3.2) |
42 | | #[derive(Clone, Debug, PartialEq, Eq)] |
43 | | pub struct CtExtensions<'a>(pub &'a [u8]); |
44 | | |
45 | | #[derive(Clone, Debug, PartialEq, Eq)] |
46 | | pub struct DigitallySigned<'a> { |
47 | | pub hash_alg_id: u8, |
48 | | pub sign_alg_id: u8, |
49 | | pub data: &'a [u8], |
50 | | } |
51 | | |
52 | | /// Parses a list of Signed Certificate Timestamp entries |
53 | 950 | pub fn parse_ct_signed_certificate_timestamp_list( |
54 | 950 | i: &[u8], |
55 | 950 | ) -> IResult<&[u8], Vec<SignedCertificateTimestamp>, BerError> { |
56 | | // use nom::HexDisplay; |
57 | | // eprintln!("{}", i.to_hex(16)); |
58 | 950 | let (rem, b) = <&[u8]>::from_der(i)?; |
59 | 948 | let (b, sct_len) = be_u16(b)?; |
60 | 947 | let (_, sct_list) = map_parser( |
61 | 947 | take(sct_len as usize), |
62 | 947 | many1(complete(parse_ct_signed_certificate_timestamp)), |
63 | 947 | )(b)?; |
64 | 939 | Ok((rem, sct_list)) |
65 | 950 | } |
66 | | |
67 | | /// Parses as single Signed Certificate Timestamp entry |
68 | 3.70k | pub fn parse_ct_signed_certificate_timestamp( |
69 | 3.70k | i: &[u8], |
70 | 3.70k | ) -> IResult<&[u8], SignedCertificateTimestamp, BerError> { |
71 | 3.70k | map_parser( |
72 | 3.70k | length_data(be_u16), |
73 | 3.70k | parse_ct_signed_certificate_timestamp_content, |
74 | 3.70k | )(i) |
75 | 3.70k | } |
76 | | |
77 | 2.79k | pub(crate) fn parse_ct_signed_certificate_timestamp_content( |
78 | 2.79k | i: &[u8], |
79 | 2.79k | ) -> IResult<&[u8], SignedCertificateTimestamp, BerError> { |
80 | 2.79k | let (i, version) = be_u8(i)?; |
81 | 2.78k | let (i, id) = parse_log_id(i)?; |
82 | 2.78k | let (i, timestamp) = be_u64(i)?; |
83 | 2.78k | let (i, extensions) = parse_ct_extensions(i)?; |
84 | 2.77k | let (i, signature) = parse_digitally_signed(i)?; |
85 | 2.76k | let sct = SignedCertificateTimestamp { |
86 | 2.76k | version: CtVersion(version), |
87 | 2.76k | id, |
88 | 2.76k | timestamp, |
89 | 2.76k | extensions, |
90 | 2.76k | signature, |
91 | 2.76k | }; |
92 | 2.76k | Ok((i, sct)) |
93 | 2.79k | } |
94 | | |
95 | | // Safety: cannot fail, take() returns exactly 32 bytes |
96 | 2.78k | fn parse_log_id(i: &[u8]) -> IResult<&[u8], CtLogID, BerError> { |
97 | 2.78k | let (i, key_id) = take(32usize)(i)?; |
98 | 2.78k | Ok(( |
99 | 2.78k | i, |
100 | 2.78k | CtLogID { |
101 | 2.78k | key_id: key_id |
102 | 2.78k | .try_into() |
103 | 2.78k | .expect("take(32) is in sync with key_id size"), |
104 | 2.78k | }, |
105 | 2.78k | )) |
106 | 2.78k | } |
107 | | |
108 | 2.78k | fn parse_ct_extensions(i: &[u8]) -> IResult<&[u8], CtExtensions, BerError> { |
109 | 2.78k | let (i, ext_len) = be_u16(i)?; |
110 | 2.78k | let (i, ext_data) = take(ext_len as usize)(i)?; |
111 | 2.77k | Ok((i, CtExtensions(ext_data))) |
112 | 2.78k | } |
113 | | |
114 | 2.77k | fn parse_digitally_signed(i: &[u8]) -> IResult<&[u8], DigitallySigned, BerError> { |
115 | 2.77k | let (i, hash_alg_id) = be_u8(i)?; |
116 | 2.77k | let (i, sign_alg_id) = be_u8(i)?; |
117 | 2.77k | let (i, data) = length_data(be_u16)(i)?; |
118 | 2.76k | let signed = DigitallySigned { |
119 | 2.76k | hash_alg_id, |
120 | 2.76k | sign_alg_id, |
121 | 2.76k | data, |
122 | 2.76k | }; |
123 | 2.76k | Ok((i, signed)) |
124 | 2.77k | } |