Skip to content

Commit 9a01c98

Browse files
committed
crypto: support getting CRL num from a PEM CRL
Signed-off-by: Jiaqi Gao <[email protected]>
1 parent 4e19941 commit 9a01c98

File tree

2 files changed

+159
-0
lines changed

2 files changed

+159
-0
lines changed

src/crypto/src/crl.rs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// Copyright (c) 2022 Intel Corporation
2+
//
3+
// SPDX-License-Identifier: BSD-2-Clause-Patent
4+
5+
use crate::x509::{Extension, Time};
6+
use crate::Error;
7+
use alloc::vec::Vec;
8+
use der::asn1::{AnyRef, BitStringRef, ObjectIdentifier};
9+
use der::{Choice, Decode, Encode, ErrorKind, Header, Sequence, Tag, TagMode, TagNumber, Tagged};
10+
use rustls_pemfile::Item;
11+
12+
const CRL_NUMBER_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.5.29.20");
13+
14+
// Define minimal ASN.1 structures to parse the CRL
15+
#[derive(Sequence)]
16+
pub struct Crl<'a> {
17+
tbs_cert_list: TbsCertList<'a>,
18+
signature_algorithm: AnyRef<'a>,
19+
signature_value: BitStringRef<'a>,
20+
}
21+
22+
#[derive(Sequence)]
23+
struct TbsCertList<'a> {
24+
version: Option<AnyRef<'a>>,
25+
signature: AnyRef<'a>,
26+
issuer: AnyRef<'a>,
27+
this_update: Time,
28+
next_update: Option<Time>,
29+
revoked_certificates: Option<Vec<RevokedCertificate<'a>>>,
30+
crl_extensions: Option<Extensions<'a>>,
31+
}
32+
33+
#[derive(Sequence)]
34+
struct RevokedCertificate<'a> {
35+
user_certificate: AnyRef<'a>,
36+
revocation_date: AnyRef<'a>,
37+
crl_entry_extensions: Option<AnyRef<'a>>,
38+
}
39+
40+
#[derive(Clone, Debug, Eq, PartialEq)]
41+
pub struct Extensions<'a>(Vec<Extension<'a>>);
42+
43+
impl<'a> Extensions<'a> {
44+
pub fn get(&self) -> &Vec<Extension<'a>> {
45+
&self.0
46+
}
47+
}
48+
49+
impl Encode for Extensions<'_> {
50+
fn encoded_len(&self) -> der::Result<der::Length> {
51+
let len = self.0.encoded_len()?;
52+
let explicit = Header::new(
53+
Tag::ContextSpecific {
54+
constructed: true,
55+
number: TagNumber::new(0),
56+
},
57+
len,
58+
)?;
59+
explicit.encoded_len() + len
60+
}
61+
62+
fn encode(&self, encoder: &mut impl der::Writer) -> der::Result<()> {
63+
let len = self.0.encoded_len()?;
64+
let explicit = Header::new(
65+
Tag::ContextSpecific {
66+
constructed: true,
67+
number: TagNumber::new(0),
68+
},
69+
len,
70+
)?;
71+
explicit.encode(encoder)?;
72+
self.0.encode(encoder)
73+
}
74+
}
75+
76+
impl<'a> Decode<'a> for Extensions<'a> {
77+
fn decode<R: der::Reader<'a>>(decoder: &mut R) -> der::Result<Self> {
78+
let ext = decoder
79+
.context_specific(TagNumber::new(0), TagMode::Explicit)?
80+
.ok_or(der::Error::new(ErrorKind::Failed, decoder.position()))?;
81+
Ok(Self(ext))
82+
}
83+
}
84+
85+
impl Tagged for Extensions<'_> {
86+
fn tag(&self) -> Tag {
87+
Tag::ContextSpecific {
88+
constructed: true,
89+
number: TagNumber::new(0),
90+
}
91+
}
92+
}
93+
94+
impl<'a> Choice<'a> for Extensions<'a> {
95+
fn can_decode(tag: Tag) -> bool {
96+
tag == Tag::ContextSpecific {
97+
constructed: true,
98+
number: TagNumber::new(0),
99+
}
100+
}
101+
}
102+
103+
/// Parses a CRL and returns the CRL Number extension value
104+
pub fn get_crl_number(crl: &[u8]) -> Result<u32, Error> {
105+
let crl_der = match rustls_pemfile::read_one_from_slice(crl) {
106+
Ok(Some((Item::Crl(data), _))) => data.to_vec(),
107+
Ok(Some(_)) | Ok(None) | Err(_) => return Err(Error::DecodePemCert),
108+
};
109+
110+
let crl = Crl::from_der(&crl_der).map_err(|_| Error::ParseCertificate)?;
111+
112+
if let Some(cs) = crl.tbs_cert_list.crl_extensions {
113+
for ext in cs.get().iter() {
114+
if ext.extn_id == CRL_NUMBER_OID {
115+
let number =
116+
u32::from_der(ext.extn_value.ok_or(Error::CrlNumberNotFound)?.as_bytes())
117+
.map_err(|_| Error::CrlNumberNotFound)?;
118+
return Ok(number);
119+
}
120+
}
121+
}
122+
123+
Err(Error::CrlNumberNotFound)
124+
}
125+
126+
#[cfg(test)]
127+
mod test {
128+
use super::*;
129+
130+
#[test]
131+
fn test_get_crl_number() {
132+
const CRL1: &[u8] = b"-----BEGIN X509 CRL-----
133+
MIIBITCByAIBATAKBggqhkjOPQQDAjBoMRowGAYDVQQDDBFJbnRlbCBTR1ggUm9v
134+
dCBDQTEaMBgGA1UECgwRSW50ZWwgQ29ycG9yYXRpb24xFDASBgNVBAcMC1NhbnRh
135+
IENsYXJhMQswCQYDVQQIDAJDQTELMAkGA1UEBhMCVVMXDTI1MDkxNjExNTMxMloX
136+
DTI2MDkxNjExNTMxMlqgLzAtMAoGA1UdFAQDAgEBMB8GA1UdIwQYMBaAFOnoRFJT
137+
NlxLGJoR/EMYLKXcIIBIMAoGCCqGSM49BAMCA0gAMEUCIQDv5KEBogNCzPgupOPj
138+
FIYJaOubypBPCGqnE0XcYTgFDwIgeSfXk71tIbV5lqp6gWCpN98/xu/8c7y36EV3
139+
pkfootI=
140+
-----END X509 CRL-----";
141+
142+
const CRL2: &[u8] = b"-----BEGIN X509 CRL-----
143+
MIIBKTCB0AIBATAKBggqhkjOPQQDAjBwMSIwIAYDVQQDDBlJbnRlbCBTR1ggUENL
144+
IFBsYXRmb3JtIENBMRowGAYDVQQKDBFJbnRlbCBDb3Jwb3JhdGlvbjEUMBIGA1UE
145+
BwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYDVQQGEwJVUxcNMjUxMTIw
146+
MDY0ODQ5WhcNMjUxMjIwMDY0ODQ5WqAvMC0wCgYDVR0UBAMCAQEwHwYDVR0jBBgw
147+
FoAUWSPTp0qoY1QuOXCt4A8HK1ckKrcwCgYIKoZIzj0EAwIDSAAwRQIgQB8+Xmh7
148+
QJEvrDG15ucaA2b2pByR86M8+3mDd5g5c0sCIQD1WVRItKvP90kBT6EZp03qAOCU
149+
IrrRoE+AsML37e56hg==
150+
-----END X509 CRL-----";
151+
152+
assert_eq!(get_crl_number(CRL1).unwrap(), 1);
153+
assert_eq!(get_crl_number(CRL2).unwrap(), 1);
154+
}
155+
}

src/crypto/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ cfg_if::cfg_if! {
2121
}
2222
}
2323

24+
pub mod crl;
2425
pub mod x509;
2526

2627
pub type Result<T> = core::result::Result<T, Error>;
@@ -80,6 +81,9 @@ pub enum Error {
8081
/// Unsupported signature algorithm
8182
UnsupportedAlgorithm,
8283

84+
/// CRL number extension missing
85+
CrlNumberNotFound,
86+
8387
/// Unexpected error that should not happen
8488
Unexpected,
8589
}

0 commit comments

Comments
 (0)