1+ use zeroize:: { Zeroize , ZeroizeOnDrop } ;
2+
13use crate :: errors:: { ErrorKind , Result } ;
24
35/// Supported PEM files for EC and RSA Public and Private Keys
@@ -40,11 +42,12 @@ enum Classification {
4042/// Documentation about these formats is at
4143/// PKCS#1: https://tools.ietf.org/html/rfc8017
4244/// PKCS#8: https://tools.ietf.org/html/rfc5958
43- #[ derive( Debug ) ]
45+ #[ derive( Debug , ZeroizeOnDrop , Zeroize ) ]
4446pub ( crate ) struct PemEncodedKey {
4547 content : Vec < u8 > ,
46- asn1 : Vec < simple_asn1 :: ASN1Block > ,
48+ # [ zeroize ( skip ) ]
4749 pem_type : PemType ,
50+ #[ zeroize( skip) ]
4851 standard : Standard ,
4952}
5053
@@ -53,22 +56,15 @@ impl PemEncodedKey {
5356 pub fn new ( input : & [ u8 ] ) -> Result < PemEncodedKey > {
5457 match pem:: parse ( input) {
5558 Ok ( content) => {
56- let asn1_content = match simple_asn1:: from_der ( content. contents ( ) ) {
57- Ok ( asn1) => asn1,
58- Err ( _) => return Err ( ErrorKind :: InvalidKeyFormat . into ( ) ) ,
59- } ;
60-
6159 match content. tag ( ) {
6260 // This handles a PKCS#1 RSA Private key
6361 "RSA PRIVATE KEY" => Ok ( PemEncodedKey {
6462 content : content. into_contents ( ) ,
65- asn1 : asn1_content,
6663 pem_type : PemType :: RsaPrivate ,
6764 standard : Standard :: Pkcs1 ,
6865 } ) ,
6966 "RSA PUBLIC KEY" => Ok ( PemEncodedKey {
7067 content : content. into_contents ( ) ,
71- asn1 : asn1_content,
7268 pem_type : PemType :: RsaPublic ,
7369 standard : Standard :: Pkcs1 ,
7470 } ) ,
@@ -79,41 +75,22 @@ impl PemEncodedKey {
7975
8076 // This handles PKCS#8 certificates and public & private keys
8177 tag @ "PRIVATE KEY" | tag @ "PUBLIC KEY" | tag @ "CERTIFICATE" => {
82- match classify_pem ( & asn1_content) {
83- Some ( c) => {
84- let is_private = tag == "PRIVATE KEY" ;
85- let pem_type = match c {
86- Classification :: Ec => {
87- if is_private {
88- PemType :: EcPrivate
89- } else {
90- PemType :: EcPublic
91- }
92- }
93- Classification :: Ed => {
94- if is_private {
95- PemType :: EdPrivate
96- } else {
97- PemType :: EdPublic
98- }
99- }
100- Classification :: Rsa => {
101- if is_private {
102- PemType :: RsaPrivate
103- } else {
104- PemType :: RsaPublic
105- }
106- }
107- } ;
108- Ok ( PemEncodedKey {
109- content : content. into_contents ( ) ,
110- asn1 : asn1_content,
111- pem_type,
112- standard : Standard :: Pkcs8 ,
113- } )
114- }
115- None => Err ( ErrorKind :: InvalidKeyFormat . into ( ) ) ,
116- }
78+ let is_private = tag == "PRIVATE KEY" ;
79+ let pem_type = match classify_der ( content. contents ( ) )
80+ . ok_or ( ErrorKind :: InvalidKeyFormat ) ?
81+ {
82+ Classification :: Ec if is_private => PemType :: EcPrivate ,
83+ Classification :: Ec => PemType :: EcPublic ,
84+ Classification :: Ed if is_private => PemType :: EdPrivate ,
85+ Classification :: Ed => PemType :: EdPublic ,
86+ Classification :: Rsa if is_private => PemType :: RsaPrivate ,
87+ Classification :: Rsa => PemType :: RsaPublic ,
88+ } ;
89+ Ok ( PemEncodedKey {
90+ content : content. into_contents ( ) ,
91+ pem_type,
92+ standard : Standard :: Pkcs8 ,
93+ } )
11794 }
11895
11996 // Unknown/unsupported type
@@ -140,7 +117,8 @@ impl PemEncodedKey {
140117 match self . standard {
141118 Standard :: Pkcs1 => Err ( ErrorKind :: InvalidKeyFormat . into ( ) ) ,
142119 Standard :: Pkcs8 => match self . pem_type {
143- PemType :: EcPublic => extract_first_bitstring ( & self . asn1 ) ,
120+ PemType :: EcPublic => extract_first_bitstring_der ( & self . content )
121+ . ok_or_else ( || ErrorKind :: InvalidKeyFormat . into ( ) ) ,
144122 _ => Err ( ErrorKind :: InvalidKeyFormat . into ( ) ) ,
145123 } ,
146124 }
@@ -162,7 +140,8 @@ impl PemEncodedKey {
162140 match self . standard {
163141 Standard :: Pkcs1 => Err ( ErrorKind :: InvalidKeyFormat . into ( ) ) ,
164142 Standard :: Pkcs8 => match self . pem_type {
165- PemType :: EdPublic => extract_first_bitstring ( & self . asn1 ) ,
143+ PemType :: EdPublic => extract_first_bitstring_der ( & self . content )
144+ . ok_or_else ( || ErrorKind :: InvalidKeyFormat . into ( ) ) ,
166145 _ => Err ( ErrorKind :: InvalidKeyFormat . into ( ) ) ,
167146 } ,
168147 }
@@ -173,68 +152,175 @@ impl PemEncodedKey {
173152 match self . standard {
174153 Standard :: Pkcs1 => Ok ( self . content . as_slice ( ) ) ,
175154 Standard :: Pkcs8 => match self . pem_type {
176- PemType :: RsaPrivate => extract_first_bitstring ( & self . asn1 ) ,
177- PemType :: RsaPublic => extract_first_bitstring ( & self . asn1 ) ,
155+ PemType :: RsaPrivate | PemType :: RsaPublic => {
156+ extract_first_bitstring_der ( & self . content )
157+ . ok_or_else ( || ErrorKind :: InvalidKeyFormat . into ( ) )
158+ }
178159 _ => Err ( ErrorKind :: InvalidKeyFormat . into ( ) ) ,
179160 } ,
180161 }
181162 }
182163}
183164
165+ const TAG_BIT_STRING : u8 = 0x03 ;
166+ const TAG_OCTET_STRING : u8 = 0x04 ;
167+ const TAG_OID : u8 = 0x06 ;
168+ const TAG_SEQUENCE : u8 = 0x30 ;
169+
184170// This really just finds and returns the first bitstring or octet string
185171// Which is the x coordinate for EC public keys
186172// And the DER contents of an RSA key
187173// Though PKCS#11 keys shouldn't have anything else.
188174// It will get confusing with certificates.
189- fn extract_first_bitstring ( asn1 : & [ simple_asn1:: ASN1Block ] ) -> Result < & [ u8 ] > {
190- for asn1_entry in asn1. iter ( ) {
191- match asn1_entry {
192- simple_asn1:: ASN1Block :: Sequence ( _, entries) => {
193- if let Ok ( result) = extract_first_bitstring ( entries) {
194- return Ok ( result) ;
175+ fn extract_first_bitstring_der ( bytes : & [ u8 ] ) -> Option < & [ u8 ] > {
176+ let mut stack = vec ! [ bytes] ;
177+
178+ while let Some ( bytes) = stack. pop ( ) {
179+ let Some ( ( tag, value, rest) ) = read_tlv ( bytes) else {
180+ continue ; // Skip invalid TLV
181+ } ;
182+
183+ if !rest. is_empty ( ) {
184+ stack. push ( rest) ;
185+ }
186+
187+ match tag {
188+ TAG_BIT_STRING => {
189+ if value. is_empty ( ) {
190+ return None ; // Missing padding length
191+ } else if value[ 0 ] != 0 {
192+ return None ; // Padding length must be zero for cryptographic keys
195193 }
194+ return Some ( & value[ 1 ..] ) ;
196195 }
197- simple_asn1:: ASN1Block :: BitString ( _, _, value) => {
198- return Ok ( value. as_ref ( ) ) ;
199- }
200- simple_asn1:: ASN1Block :: OctetString ( _, value) => {
201- return Ok ( value. as_ref ( ) ) ;
196+ TAG_OCTET_STRING => return Some ( value) ,
197+ TAG_SEQUENCE => {
198+ stack. push ( value) ;
202199 }
203- _ => ( ) ,
200+ _ => { }
204201 }
205202 }
206203
207- Err ( ErrorKind :: InvalidEcdsaKey . into ( ) )
204+ None
208205}
209206
210207/// Find whether this is EC, RSA, or Ed
211- fn classify_pem ( asn1 : & [ simple_asn1:: ASN1Block ] ) -> Option < Classification > {
212- // These should be constant but the macro requires
213- // #![feature(const_vec_new)]
214- let ec_public_key_oid = simple_asn1:: oid!( 1 , 2 , 840 , 10_045 , 2 , 1 ) ;
215- let rsa_public_key_oid = simple_asn1:: oid!( 1 , 2 , 840 , 113_549 , 1 , 1 , 1 ) ;
216- let ed25519_oid = simple_asn1:: oid!( 1 , 3 , 101 , 112 ) ;
217-
218- for asn1_entry in asn1. iter ( ) {
219- match asn1_entry {
220- simple_asn1:: ASN1Block :: Sequence ( _, entries) => {
221- if let Some ( classification) = classify_pem ( entries) {
222- return Some ( classification) ;
223- }
224- }
225- simple_asn1:: ASN1Block :: ObjectIdentifier ( _, oid) => {
226- if oid == ec_public_key_oid {
227- return Some ( Classification :: Ec ) ;
228- }
229- if oid == rsa_public_key_oid {
230- return Some ( Classification :: Rsa ) ;
231- }
232- if oid == ed25519_oid {
233- return Some ( Classification :: Ed ) ;
234- }
208+ fn classify_der ( bytes : & [ u8 ] ) -> Option < Classification > {
209+ const EC_PUBLIC_KEY_OID : & [ u8 ] = & [ 0x2A , 0x86 , 0x48 , 0xCE , 0x3D , 0x02 , 0x01 ] ; // 1.2.840.10045.2.1
210+ const RSA_PUBLIC_KEY_OID : & [ u8 ] = & [ 0x2A , 0x86 , 0x48 , 0x86 , 0xF7 , 0x0D , 0x01 , 0x01 , 0x01 ] ; // 1.2.840.113549.1.1.1
211+ const ED25519_OID : & [ u8 ] = & [ 0x2B , 0x65 , 0x70 ] ; // 1.3.101.112
212+
213+ let mut stack = vec ! [ bytes] ;
214+
215+ while let Some ( bytes) = stack. pop ( ) {
216+ let Some ( ( tag, value, rest) ) = read_tlv ( bytes) else {
217+ continue ; // Skip invalid TLV
218+ } ;
219+
220+ if !rest. is_empty ( ) {
221+ stack. push ( rest) ;
222+ }
223+
224+ if tag == TAG_OID {
225+ match value {
226+ EC_PUBLIC_KEY_OID => return Some ( Classification :: Ec ) ,
227+ RSA_PUBLIC_KEY_OID => return Some ( Classification :: Rsa ) ,
228+ ED25519_OID => return Some ( Classification :: Ed ) ,
229+ _ => { }
235230 }
236- _ => { }
231+ } else if tag == TAG_SEQUENCE {
232+ stack. push ( value) ;
237233 }
238234 }
235+
239236 None
240237}
238+
239+ /// Returns `Some((tag, value, rest))` or `None` if the TLV is invalid.
240+ fn read_tlv ( mut bytes : & [ u8 ] ) -> Option < ( u8 , & [ u8 ] , & [ u8 ] ) > {
241+ if bytes. len ( ) < 2 {
242+ return None ;
243+ }
244+
245+ let tag = bytes[ 0 ] ;
246+ let len = bytes[ 1 ] ;
247+ bytes = & bytes[ 2 ..] ;
248+
249+ let len = if len < 0x80 {
250+ len as usize
251+ } else {
252+ let len_len = ( len & 0x7f ) as usize ;
253+ if len_len == 0 {
254+ return None ; // Indefinite length
255+ } else if size_of :: < usize > ( ) < len_len {
256+ return None ; // Too long; prevents usize overflow
257+ } else if bytes. len ( ) < len_len {
258+ return None ; // Not enough bytes
259+ }
260+ let ( len_bytes, rest) = bytes. split_at ( len_len) ;
261+ bytes = rest;
262+ len_bytes. iter ( ) . fold ( 0 , |acc, & x| acc * 256 + x as usize )
263+ } ;
264+
265+ if bytes. len ( ) < len {
266+ return None ; // Not enough bytes
267+ }
268+
269+ let ( value, rest) = bytes. split_at ( len) ;
270+ Some ( ( tag, value, rest) )
271+ }
272+
273+ #[ cfg( test) ]
274+ mod tests {
275+ use super :: * ;
276+
277+ #[ test]
278+ fn classify_ec_key ( ) {
279+ let pem = pem:: parse ( include_bytes ! ( "../../tests/ecdsa/public_ecdsa_key.pem" ) ) . unwrap ( ) ;
280+ assert_eq ! ( classify_der( pem. contents( ) ) , Some ( Classification :: Ec ) ) ;
281+ }
282+
283+ #[ test]
284+ fn classify_rsa_key ( ) {
285+ let pem = pem:: parse ( include_bytes ! ( "../../tests/rsa/public_rsa_key_pkcs8.pem" ) ) . unwrap ( ) ;
286+ assert_eq ! ( classify_der( pem. contents( ) ) , Some ( Classification :: Rsa ) ) ;
287+ }
288+
289+ #[ test]
290+ fn classify_ed25519_key ( ) {
291+ let pem = pem:: parse ( include_bytes ! ( "../../tests/eddsa/public_ed25519_key.pem" ) ) . unwrap ( ) ;
292+ assert_eq ! ( classify_der( pem. contents( ) ) , Some ( Classification :: Ed ) ) ;
293+ }
294+
295+ #[ test]
296+ fn ec_public_key_extraction ( ) {
297+ let key =
298+ PemEncodedKey :: new ( include_bytes ! ( "../../tests/ecdsa/public_ecdsa_key.pem" ) ) . unwrap ( ) ;
299+ let bytes = key. as_ec_public_key ( ) . unwrap ( ) ;
300+ assert_eq ! ( bytes[ 0 ] , 0x04 ) ; // uncompressed point
301+ assert_eq ! ( bytes. len( ) , 65 ) ; // 1 + 32 + 32 for P-256
302+ }
303+
304+ #[ test]
305+ fn ed_public_key_extraction ( ) {
306+ let key =
307+ PemEncodedKey :: new ( include_bytes ! ( "../../tests/eddsa/public_ed25519_key.pem" ) ) . unwrap ( ) ;
308+ let bytes = key. as_ed_public_key ( ) . unwrap ( ) ;
309+ assert_eq ! ( bytes. len( ) , 32 ) ;
310+ }
311+
312+ #[ test]
313+ fn rsa_pkcs8_key_extraction ( ) {
314+ let key =
315+ PemEncodedKey :: new ( include_bytes ! ( "../../tests/rsa/public_rsa_key_pkcs8.pem" ) ) . unwrap ( ) ;
316+ let bytes = key. as_rsa_key ( ) . unwrap ( ) ;
317+ assert_eq ! ( bytes[ 0 ] , 0x30 ) ; // SEQUENCE
318+ }
319+ #[ test]
320+ fn rsa_pkcs1_key ( ) {
321+ let key = PemEncodedKey :: new ( include_bytes ! ( "../../tests/rsa/private_rsa_key_pkcs1.pem" ) )
322+ . unwrap ( ) ;
323+ let bytes = key. as_rsa_key ( ) . unwrap ( ) ;
324+ assert_eq ! ( bytes[ 0 ] , 0x30 ) ; // SEQUENCE
325+ }
326+ }
0 commit comments