1use super::der::{DerError, Reader};
2use crate::{
3 curve25519::ed25519,
4 p256::{PUBLIC_KEY_UNCOMPRESSED_SIZE, SECRET_KEY_SIZE, SecretKey},
5 p384,
6};
7
8const PKCS8_DER_LEN: usize = 138;
9
10const EC_PUBLIC_KEY_OID: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01];
11const SECP256R1_OID: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07];
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum Pkcs8Error {
15 InvalidLength,
16 InvalidSequence,
17 InvalidVersion,
18 InvalidAlgorithmIdentifier,
19 InvalidOctetString,
20 InvalidEcSecretKey,
21 InvalidEcVersion,
22 InvalidSecretKeyOctet,
23 InvalidPublicKeyExplicit,
24 InvalidPublicKeyBitString,
25 InvalidPublicKeyPrefix,
26 InvalidPublicKeyMismatch,
27 KeyDerivationFailed,
28 InvalidDer(DerError),
29 UnsupportedAlgorithm,
30 UnsupportedCurve,
31 InvalidEd25519PrivateKey,
32 InvalidMlDsaPrivateKey,
33 MlDsaExpandedKeyNotSupported,
34}
35
36impl From<DerError> for Pkcs8Error {
37 fn from(err: DerError) -> Self {
38 Pkcs8Error::InvalidDer(err)
39 }
40}
41
42#[cfg(feature = "alloc")]
43impl core::fmt::Display for Pkcs8Error {
44 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
45 match self {
46 Pkcs8Error::InvalidLength => write!(f, "invalid DER length"),
47 Pkcs8Error::InvalidSequence => write!(f, "invalid outer SEQUENCE"),
48 Pkcs8Error::InvalidVersion => write!(f, "invalid version"),
49 Pkcs8Error::InvalidAlgorithmIdentifier => write!(f, "invalid AlgorithmIdentifier"),
50 Pkcs8Error::InvalidOctetString => write!(f, "invalid OCTET STRING wrapping"),
51 Pkcs8Error::InvalidEcSecretKey => write!(f, "invalid ECSecretKey SEQUENCE"),
52 Pkcs8Error::InvalidEcVersion => write!(f, "invalid EC version"),
53 Pkcs8Error::InvalidSecretKeyOctet => write!(f, "invalid private key OCTET STRING"),
54 Pkcs8Error::InvalidPublicKeyExplicit => write!(f, "invalid public key [1] EXPLICIT"),
55 Pkcs8Error::InvalidPublicKeyBitString => write!(f, "invalid public key BIT STRING"),
56 Pkcs8Error::InvalidPublicKeyPrefix => write!(f, "invalid public key prefix"),
57 Pkcs8Error::InvalidPublicKeyMismatch => write!(f, "embedded public key does not match private key"),
58 Pkcs8Error::KeyDerivationFailed => write!(f, "key derivation failed"),
59 Pkcs8Error::InvalidDer(err) => write!(f, "invalid DER: {err}"),
60 Pkcs8Error::UnsupportedAlgorithm => write!(f, "unsupported private key algorithm"),
61 Pkcs8Error::UnsupportedCurve => write!(f, "unsupported or missing named curve"),
62 Pkcs8Error::InvalidEd25519PrivateKey => write!(f, "invalid Ed25519 private key"),
63 Pkcs8Error::InvalidMlDsaPrivateKey => write!(f, "invalid ML-DSA private key"),
64 Pkcs8Error::MlDsaExpandedKeyNotSupported => write!(
65 f,
66 "ML-DSA private keys stored in expanded form cannot be loaded; \
67 provide the key in seed form (RFC 9881)"
68 ),
69 }
70 }
71}
72
73fn validate_fixed_prefix(der: &[u8]) -> Result<(), Pkcs8Error> {
74 if der.len() != PKCS8_DER_LEN {
75 return Err(Pkcs8Error::InvalidLength);
76 }
77
78 if der[0] != 0x30 || der[1] != 0x81 || der[2] != 0x87 {
80 return Err(Pkcs8Error::InvalidSequence);
81 }
82 if der[3] != 0x02 || der[4] != 0x01 || der[5] != 0x00 {
84 return Err(Pkcs8Error::InvalidVersion);
85 }
86 if der[6] != 0x30 || der[7] != 0x13 {
88 return Err(Pkcs8Error::InvalidAlgorithmIdentifier);
89 }
90 if der[8] != 0x06 || der[9] != 0x07 || der[10..17] != *EC_PUBLIC_KEY_OID {
92 return Err(Pkcs8Error::InvalidAlgorithmIdentifier);
93 }
94 if der[17] != 0x06 || der[18] != 0x08 || der[19..27] != *SECP256R1_OID {
96 return Err(Pkcs8Error::InvalidAlgorithmIdentifier);
97 }
98 if der[27] != 0x04 || der[28] != 0x6d {
100 return Err(Pkcs8Error::InvalidOctetString);
101 }
102 if der[29] != 0x30 || der[30] != 0x6b {
104 return Err(Pkcs8Error::InvalidEcSecretKey);
105 }
106 if der[31] != 0x02 || der[32] != 0x01 || der[33] != 0x01 {
108 return Err(Pkcs8Error::InvalidEcVersion);
109 }
110 if der[34] != 0x04 || der[35] != 0x20 {
112 return Err(Pkcs8Error::InvalidSecretKeyOctet);
113 }
114 if der[68] != 0xa1 || der[69] != 0x44 {
116 return Err(Pkcs8Error::InvalidPublicKeyExplicit);
117 }
118 if der[70] != 0x03 || der[71] != 0x42 || der[72] != 0x00 {
120 return Err(Pkcs8Error::InvalidPublicKeyBitString);
121 }
122 if der[73] != 0x04 {
124 return Err(Pkcs8Error::InvalidPublicKeyPrefix);
125 }
126
127 Ok(())
128}
129
130static TEMPLATE: [u8; PKCS8_DER_LEN] = [
131 0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07,
137 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
142 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
143 0xa1, 0x44, 0x03, 0x42, 0x00,
146 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
148 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
149 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
150 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
151];
152
153const PRIVATE_KEY_OFFSET: usize = 36;
154const PUBLIC_KEY_OFFSET: usize = 73;
155
156pub fn encode_p256_pkcs8_der(key: &SecretKey) -> Result<[u8; PKCS8_DER_LEN], Pkcs8Error> {
162 let public_key = key.public_key();
163 let pub_bytes = public_key.to_bytes();
164 let priv_bytes = key.to_bytes();
165
166 let mut der = TEMPLATE;
167 der[PUBLIC_KEY_OFFSET..PUBLIC_KEY_OFFSET + PUBLIC_KEY_UNCOMPRESSED_SIZE].copy_from_slice(&pub_bytes);
168 der[PRIVATE_KEY_OFFSET..PRIVATE_KEY_OFFSET + SECRET_KEY_SIZE].copy_from_slice(&priv_bytes);
169
170 Ok(der)
171}
172
173pub fn decode_p256_pkcs8_der(der: &[u8]) -> Result<SecretKey, Pkcs8Error> {
184 validate_fixed_prefix(der)?;
185
186 let mut private_key = [0u8; SECRET_KEY_SIZE];
187 private_key.copy_from_slice(&der[PRIVATE_KEY_OFFSET..PRIVATE_KEY_OFFSET + SECRET_KEY_SIZE]);
188
189 let mut public_key = [0u8; PUBLIC_KEY_UNCOMPRESSED_SIZE];
190 public_key.copy_from_slice(&der[PUBLIC_KEY_OFFSET..PUBLIC_KEY_OFFSET + PUBLIC_KEY_UNCOMPRESSED_SIZE]);
191
192 let key = SecretKey::from_bytes(&private_key).map_err(|_| Pkcs8Error::KeyDerivationFailed)?;
193
194 if key.public_key().to_bytes() != public_key {
195 return Err(Pkcs8Error::InvalidPublicKeyMismatch);
196 }
197
198 Ok(key)
199}
200
201pub const OID_EC_PUBLIC_KEY: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01];
207pub const OID_SECP256R1: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07];
209pub const OID_SECP384R1: &[u8] = &[0x2b, 0x81, 0x04, 0x00, 0x22];
211pub const OID_ED25519: &[u8] = &[0x2b, 0x65, 0x70];
213pub const OID_RSA_ENCRYPTION: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01];
215
216pub enum EcPrivateKey {
218 P256(crate::p256::SecretKey),
220 P384(crate::p384::SecretKey),
222}
223
224#[derive(Clone, Copy, PartialEq, Eq)]
225enum Curve {
226 P256,
227 P384,
228}
229
230fn curve_from_oid(oid: &[u8]) -> Result<Curve, Pkcs8Error> {
231 if oid == OID_SECP256R1 {
232 Ok(Curve::P256)
233 } else if oid == OID_SECP384R1 {
234 Ok(Curve::P384)
235 } else {
236 Err(Pkcs8Error::UnsupportedCurve)
237 }
238}
239
240fn build_ec_key(curve: Curve, scalar: &[u8]) -> Result<EcPrivateKey, Pkcs8Error> {
241 match curve {
242 Curve::P256 => {
243 let scalar: [u8; crate::p256::SECRET_KEY_SIZE] =
244 scalar.try_into().map_err(|_| Pkcs8Error::InvalidEcSecretKey)?;
245 let key = crate::p256::SecretKey::from_bytes(&scalar).map_err(|_| Pkcs8Error::KeyDerivationFailed)?;
246 Ok(EcPrivateKey::P256(key))
247 }
248 Curve::P384 => {
249 let scalar: [u8; crate::p384::SECRET_KEY_SIZE] =
250 scalar.try_into().map_err(|_| Pkcs8Error::InvalidEcSecretKey)?;
251 let key = crate::p384::SecretKey::from_bytes(&scalar).map_err(|_| Pkcs8Error::KeyDerivationFailed)?;
252 Ok(EcPrivateKey::P384(key))
253 }
254 }
255}
256
257fn parse_sec1(der: &[u8]) -> Result<(Option<Curve>, &[u8]), Pkcs8Error> {
260 let mut outer = Reader::new(der);
261 let mut seq = outer.read_sequence()?;
262 outer.finish()?;
263
264 let version = seq.read_integer()?;
265 if version != [0x01] {
266 return Err(Pkcs8Error::InvalidEcVersion);
267 }
268 let scalar = seq.read_octet_string()?;
269
270 let mut curve = None;
271 while !seq.is_empty() {
272 match seq.peek_tag()? {
273 0xa0 => {
274 let mut params = seq.read_explicit(0xa0)?;
275 let oid = params.read_oid()?;
276 params.finish()?;
277 let parsed = curve_from_oid(oid)?;
278 if let Some(previous) = curve {
279 if previous != parsed {
280 return Err(Pkcs8Error::InvalidAlgorithmIdentifier);
281 }
282 }
283 curve = Some(parsed);
284 }
285 0xa1 => {
286 let mut public = seq.read_explicit(0xa1)?;
287 let _ = public.read_bit_string()?;
288 public.finish()?;
289 }
290 _ => return Err(Pkcs8Error::InvalidEcSecretKey),
291 }
292 }
293
294 Ok((curve, scalar))
295}
296
297pub fn decode_ec_sec1_der(der: &[u8]) -> Result<EcPrivateKey, Pkcs8Error> {
305 let (curve, scalar) = parse_sec1(der)?;
306 let curve = curve.ok_or(Pkcs8Error::UnsupportedCurve)?;
307 build_ec_key(curve, scalar)
308}
309
310pub fn decode_ec_pkcs8_der(der: &[u8]) -> Result<EcPrivateKey, Pkcs8Error> {
319 let mut outer = Reader::new(der);
320 let mut seq = outer.read_sequence()?;
321 outer.finish()?;
322
323 let version = seq.read_integer()?;
324 if version != [0x00] {
325 return Err(Pkcs8Error::InvalidVersion);
326 }
327
328 let mut algorithm = seq.read_sequence()?;
329 let algorithm_oid = algorithm.read_oid()?;
330 if algorithm_oid != OID_EC_PUBLIC_KEY {
331 return Err(Pkcs8Error::UnsupportedAlgorithm);
332 }
333 let curve_oid = algorithm.read_oid()?;
334 algorithm.finish()?;
335 let outer_curve = curve_from_oid(curve_oid)?;
336
337 let private_key = seq.read_octet_string()?;
338
339 while !seq.is_empty() {
341 match seq.peek_tag()? {
342 0xa0 => {
343 let _attributes = seq.read_explicit(0xa0)?;
344 }
345 _ => return Err(Pkcs8Error::InvalidDer(DerError::UnexpectedTag)),
346 }
347 }
348
349 let (inner_curve, scalar) = parse_sec1(private_key)?;
350 if let Some(inner_curve) = inner_curve {
351 if inner_curve != outer_curve {
352 return Err(Pkcs8Error::InvalidAlgorithmIdentifier);
353 }
354 }
355 build_ec_key(outer_curve, scalar)
356}
357
358pub fn decode_ed25519_pkcs8_der(der: &[u8]) -> Result<ed25519::SecretKey, Pkcs8Error> {
368 let mut outer = Reader::new(der);
369 let mut seq = outer.read_sequence()?;
370 outer.finish()?;
371
372 let version = seq.read_integer()?;
373 if version != [0x00] && version != [0x01] {
374 return Err(Pkcs8Error::InvalidVersion);
375 }
376
377 let mut algorithm = seq.read_sequence()?;
378 let algorithm_oid = algorithm.read_oid()?;
379 if algorithm_oid != OID_ED25519 {
380 return Err(Pkcs8Error::UnsupportedAlgorithm);
381 }
382 algorithm.finish()?;
384
385 let private_key = seq.read_octet_string()?;
386
387 while !seq.is_empty() {
388 match seq.peek_tag()? {
389 0xa0 => {
390 let _attributes = seq.read_explicit(0xa0)?;
391 }
392 0xa1 => {
393 let mut public = seq.read_explicit(0xa1)?;
394 let _ = public.read_bit_string()?;
395 public.finish()?;
396 }
397 _ => return Err(Pkcs8Error::InvalidDer(DerError::UnexpectedTag)),
398 }
399 }
400
401 let seed = if private_key.len() == ed25519::SECRET_KEY_SIZE {
402 private_key
403 } else {
404 let mut nested = Reader::new(private_key);
405 let seed = nested.read_octet_string()?;
406 nested.finish()?;
407 seed
408 };
409
410 let seed: [u8; ed25519::SECRET_KEY_SIZE] = seed.try_into().map_err(|_| Pkcs8Error::InvalidEd25519PrivateKey)?;
411 Ok(ed25519::SecretKey::from_bytes(&seed))
412}
413
414pub fn is_rsa_pkcs8_der(der: &[u8]) -> bool {
422 fn inner(der: &[u8]) -> Result<bool, Pkcs8Error> {
423 let mut outer = Reader::new(der);
424 let mut seq = outer.read_sequence()?;
425 outer.finish()?;
426
427 let _version = seq.read_integer()?;
428 let mut algorithm = seq.read_sequence()?;
429 let algorithm_oid = algorithm.read_oid()?;
432
433 Ok(algorithm_oid == OID_RSA_ENCRYPTION)
434 }
435
436 inner(der).unwrap_or(false)
437}
438
439pub const OID_ML_DSA_44: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x11];
445pub const OID_ML_DSA_65: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x12];
447pub const OID_ML_DSA_87: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x13];
449
450pub enum MlDsaPrivateKey {
455 Dsa44(crate::mldsa::MlDsa44SecretKey),
457 Dsa65(crate::mldsa::MlDsa65SecretKey),
459 Dsa87(crate::mldsa::MlDsa87SecretKey),
461}
462
463fn parse_mldsa_private_key_seed(der: &[u8]) -> Result<[u8; 32], Pkcs8Error> {
470 let mut reader = Reader::new(der);
471 match reader.peek_tag()? {
472 0x80 => {
474 let seed = reader.read_expected(0x80)?;
475 reader.finish()?;
476 seed.try_into().map_err(|_| Pkcs8Error::InvalidMlDsaPrivateKey)
477 }
478 0xa0 => {
480 let mut explicit = reader.read_explicit(0xa0)?;
481 reader.finish()?;
482 let seed = explicit.read_octet_string()?;
483 explicit.finish()?;
484 seed.try_into().map_err(|_| Pkcs8Error::InvalidMlDsaPrivateKey)
485 }
486 0x04 => Err(Pkcs8Error::MlDsaExpandedKeyNotSupported),
488 0x30 => {
490 let mut both = reader.read_sequence()?;
491 reader.finish()?;
492 let seed = both.read_octet_string()?;
493 let _expanded = both.read_octet_string()?;
494 both.finish()?;
495 seed.try_into().map_err(|_| Pkcs8Error::InvalidMlDsaPrivateKey)
496 }
497 _ => Err(Pkcs8Error::InvalidMlDsaPrivateKey),
498 }
499}
500
501pub fn decode_mldsa_pkcs8_der(der: &[u8]) -> Result<MlDsaPrivateKey, Pkcs8Error> {
514 let mut outer = Reader::new(der);
515 let mut seq = outer.read_sequence()?;
516 outer.finish()?;
517
518 let version = seq.read_integer()?;
519 if version != [0x00] && version != [0x01] {
520 return Err(Pkcs8Error::InvalidVersion);
521 }
522
523 let mut algorithm = seq.read_sequence()?;
524 let oid = algorithm.read_oid()?;
525 algorithm.finish()?;
527
528 let private_key = seq.read_octet_string()?;
529
530 let mut public_key = None;
531 while !seq.is_empty() {
532 match seq.peek_tag()? {
533 0xa0 => {
534 let _attributes = seq.read_explicit(0xa0)?;
535 }
536 0xa1 => {
537 let mut public = seq.read_explicit(0xa1)?;
538 public_key = Some(public.read_bit_string()?);
539 public.finish()?;
540 }
541 _ => return Err(Pkcs8Error::InvalidDer(DerError::UnexpectedTag)),
542 }
543 }
544
545 let seed = parse_mldsa_private_key_seed(private_key)?;
546
547 match oid {
548 OID_ML_DSA_44 => {
549 let key = crate::mldsa::MlDsa44SecretKey::new(&seed);
550 check_mldsa_public_key(public_key, &key.public_key().to_bytes())?;
551 Ok(MlDsaPrivateKey::Dsa44(key))
552 }
553 OID_ML_DSA_65 => {
554 let key = crate::mldsa::MlDsa65SecretKey::new(&seed);
555 check_mldsa_public_key(public_key, &key.public_key().to_bytes())?;
556 Ok(MlDsaPrivateKey::Dsa65(key))
557 }
558 OID_ML_DSA_87 => {
559 let key = crate::mldsa::MlDsa87SecretKey::new(&seed);
560 check_mldsa_public_key(public_key, &key.public_key().to_bytes())?;
561 Ok(MlDsaPrivateKey::Dsa87(key))
562 }
563 _ => Err(Pkcs8Error::UnsupportedAlgorithm),
564 }
565}
566
567fn check_mldsa_public_key(public_key: Option<&[u8]>, derived: &[u8]) -> Result<(), Pkcs8Error> {
568 match public_key {
569 Some(public_key) if public_key != derived => Err(Pkcs8Error::InvalidPublicKeyMismatch),
570 _ => Ok(()),
571 }
572}
573
574pub fn encode_mldsa44_pkcs8_der(key: &crate::mldsa::MlDsa44SecretKey) -> [u8; ML_DSA_SEED_PKCS8_LEN] {
576 encode_mldsa_seed_pkcs8(OID_ML_DSA_44, key.seed())
577}
578
579pub fn encode_mldsa65_pkcs8_der(key: &crate::mldsa::MlDsa65SecretKey) -> [u8; ML_DSA_SEED_PKCS8_LEN] {
581 encode_mldsa_seed_pkcs8(OID_ML_DSA_65, key.seed())
582}
583
584pub fn encode_mldsa87_pkcs8_der(key: &crate::mldsa::MlDsa87SecretKey) -> [u8; ML_DSA_SEED_PKCS8_LEN] {
586 encode_mldsa_seed_pkcs8(OID_ML_DSA_87, key.seed())
587}
588
589const ML_DSA_SEED_PKCS8_LEN: usize = 54;
590
591fn encode_mldsa_seed_pkcs8(oid: &[u8], seed: &[u8; 32]) -> [u8; ML_DSA_SEED_PKCS8_LEN] {
592 debug_assert_eq!(oid.len(), 9);
593 let mut der = [0u8; ML_DSA_SEED_PKCS8_LEN];
594 der[0] = 0x30;
595 der[1] = 0x34;
596 der[2] = 0x02;
597 der[3] = 0x01;
598 der[4] = 0x00;
599 der[5] = 0x30;
600 der[6] = 0x0b;
601 der[7] = 0x06;
602 der[8] = 0x09;
603 der[9..18].copy_from_slice(oid);
604 der[18] = 0x04;
605 der[19] = 0x22;
606 der[20] = 0x80;
607 der[21] = 0x20;
608 der[22..54].copy_from_slice(seed);
609 der
610}
611
612const PKCS8_P384_DER_LEN: usize = 185;
613const PKCS8_ED25519_DER_LEN: usize = 48;
614
615static P384_TEMPLATE: [u8; PKCS8_P384_DER_LEN] = [
616 0x30, 0x81, 0xb6, 0x02, 0x01, 0x00, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x04, 0x81, 0x9e, 0x30, 0x81, 0x9b, 0x02, 0x01, 0x01, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
626 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
627 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
628 0xa1, 0x64, 0x03, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
632 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
633 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
634 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
635 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
636 0x00, 0x00,
637];
638
639static ED25519_TEMPLATE: [u8; PKCS8_ED25519_DER_LEN] = [
640 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
647 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
648];
649
650const P384_PRIVATE_KEY_OFFSET: usize = 35;
651const P384_PUBLIC_KEY_OFFSET: usize = 88;
652const ED25519_SEED_OFFSET: usize = 16;
653
654pub fn encode_p384_pkcs8_der(key: &p384::SecretKey) -> [u8; PKCS8_P384_DER_LEN] {
660 let mut der = P384_TEMPLATE;
661 der[P384_PRIVATE_KEY_OFFSET..P384_PRIVATE_KEY_OFFSET + p384::SECRET_KEY_SIZE].copy_from_slice(&key.to_bytes());
662 der[P384_PUBLIC_KEY_OFFSET..P384_PUBLIC_KEY_OFFSET + p384::PUBLIC_KEY_UNCOMPRESSED_SIZE]
663 .copy_from_slice(&key.public_key().to_bytes());
664 der
665}
666
667pub fn encode_ed25519_pkcs8_der(key: &ed25519::SecretKey) -> [u8; PKCS8_ED25519_DER_LEN] {
670 let mut der = ED25519_TEMPLATE;
671 der[ED25519_SEED_OFFSET..ED25519_SEED_OFFSET + ed25519::SECRET_KEY_SIZE].copy_from_slice(&key.to_bytes());
672 der
673}
674
675#[cfg(test)]
676mod tests {
677 use super::*;
678
679 const TEST_PRIVATE_KEY: [u8; 32] = [
683 0x25, 0x55, 0x82, 0xfd, 0x0c, 0xce, 0x4c, 0x24, 0xb9, 0xbe, 0xdb, 0x09, 0xa7, 0x62, 0x06, 0xf9, 0x40, 0xdc,
684 0xf1, 0xc7, 0x43, 0x7d, 0xea, 0x0a, 0xb7, 0x14, 0x99, 0xc5, 0xac, 0xe7, 0x33, 0xe9,
685 ];
686
687 const TEST_PUBLIC_KEY: [u8; 65] = [
688 0x04, 0x1e, 0x9c, 0x23, 0xe8, 0x1a, 0x03, 0xc5, 0x4d, 0xd3, 0xc9, 0xed, 0x52, 0xcb, 0x2a, 0xde, 0x7a, 0x47,
689 0x13, 0xe5, 0x61, 0x3d, 0x70, 0x35, 0x79, 0xc7, 0x73, 0x9e, 0x0f, 0x13, 0x20, 0x60, 0xdc, 0xbc, 0x4a, 0x68,
690 0x7d, 0x3e, 0xb0, 0x99, 0x17, 0xd2, 0x62, 0xfc, 0x2f, 0x23, 0xc4, 0x76, 0xc7, 0xcb, 0xfc, 0xec, 0xf8, 0x4f,
691 0x11, 0xe4, 0x58, 0xb2, 0x46, 0xad, 0x75, 0x6d, 0x36, 0x17, 0xc7,
692 ];
693
694 fn decode_hex(hex_str: &str) -> Vec<u8> {
695 (0..hex_str.len())
696 .step_by(2)
697 .map(|i| u8::from_str_radix(&hex_str[i..i + 2], 16).unwrap())
698 .collect()
699 }
700
701 const TEST_DER_HEX: &str = "308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b0201010420255582fd0cce4c24b9bedb09a76206f940dcf1c7437dea0ab71499c5ace733e9a144034200041e9c23e81a03c54dd3c9ed52cb2ade7a4713e5613d703579c7739e0f132060dcbc4a687d3eb09917d262fc2f23c476c7cbfcecf84f11e458b246ad756d3617c7";
702
703 #[test]
704 fn encode_round_trip() {
705 let key = SecretKey::from_bytes(&TEST_PRIVATE_KEY).unwrap();
706 let der = encode_p256_pkcs8_der(&key).unwrap();
707 let decoded = decode_p256_pkcs8_der(&der).unwrap();
708 assert_eq!(decoded.to_bytes(), TEST_PRIVATE_KEY);
709 assert_eq!(decoded.public_key().to_bytes(), TEST_PUBLIC_KEY);
710 }
711
712 #[test]
713 fn decode_known_vector() {
714 let der_bytes = decode_hex(TEST_DER_HEX);
715 let key = decode_p256_pkcs8_der(&der_bytes).unwrap();
716 assert_eq!(key.to_bytes(), TEST_PRIVATE_KEY);
717 assert_eq!(key.public_key().to_bytes(), TEST_PUBLIC_KEY);
718 }
719
720 #[test]
721 fn encode_matches_known_der() {
722 let key = SecretKey::from_bytes(&TEST_PRIVATE_KEY).unwrap();
723 let der = encode_p256_pkcs8_der(&key).unwrap();
724 let expected = decode_hex(TEST_DER_HEX);
725 assert_eq!(der.as_slice(), expected.as_slice());
726 }
727
728 #[test]
729 fn detects_rsa_pkcs8() {
730 let rsa = decode_hex("3016020100300d06092a864886f70d010101050004023000");
732 assert!(is_rsa_pkcs8_der(&rsa));
733
734 assert!(!is_rsa_pkcs8_der(&decode_hex(TEST_DER_HEX)));
736
737 assert!(!is_rsa_pkcs8_der(&[0x30, 0x00]));
739 }
740
741 #[test]
742 fn decode_too_short() {
743 assert!(matches!(decode_p256_pkcs8_der(&[0u8; 100]), Err(Pkcs8Error::InvalidLength)));
744 }
745
746 #[test]
747 fn decode_too_long() {
748 assert!(matches!(decode_p256_pkcs8_der(&[0u8; 200]), Err(Pkcs8Error::InvalidLength)));
749 }
750
751 #[test]
752 fn decode_bad_sequence() {
753 let mut der = decode_hex(TEST_DER_HEX);
754 der[0] = 0x31;
755 assert!(matches!(decode_p256_pkcs8_der(&der), Err(Pkcs8Error::InvalidSequence)));
756 }
757
758 #[test]
759 fn decode_bad_version() {
760 let mut der = decode_hex(TEST_DER_HEX);
761 der[5] = 0x01;
762 assert!(matches!(decode_p256_pkcs8_der(&der), Err(Pkcs8Error::InvalidVersion)));
763 }
764
765 #[test]
766 fn decode_bad_algo_identifier() {
767 let mut der = decode_hex(TEST_DER_HEX);
768 der[10] = 0x00;
769 assert!(matches!(
770 decode_p256_pkcs8_der(&der),
771 Err(Pkcs8Error::InvalidAlgorithmIdentifier)
772 ));
773 }
774
775 #[test]
776 fn decode_bad_ec_version() {
777 let mut der = decode_hex(TEST_DER_HEX);
778 der[33] = 0x02;
779 assert!(matches!(decode_p256_pkcs8_der(&der), Err(Pkcs8Error::InvalidEcVersion)));
780 }
781
782 #[test]
783 fn decode_missing_public_key() {
784 let mut der = decode_hex(TEST_DER_HEX);
785 der[68] = 0x00;
786 assert!(matches!(decode_p256_pkcs8_der(&der), Err(Pkcs8Error::InvalidPublicKeyExplicit)));
787 }
788
789 #[test]
790 fn decode_bad_public_key_prefix() {
791 let mut der = decode_hex(TEST_DER_HEX);
792 der[73] = 0x02;
793 assert!(matches!(decode_p256_pkcs8_der(&der), Err(Pkcs8Error::InvalidPublicKeyPrefix)));
794 }
795
796 #[test]
797 fn decode_mismatched_public_key() {
798 let mut der = decode_hex(TEST_DER_HEX);
799 der[80] ^= 0xff;
800 assert!(matches!(decode_p256_pkcs8_der(&der), Err(Pkcs8Error::InvalidPublicKeyMismatch)));
801 }
802
803 #[test]
804 fn encode_round_trip_random_keys() {
805 for _ in 0..10 {
806 let priv_key: [u8; 32] = rand::random();
807 let key = match SecretKey::from_bytes(&priv_key) {
808 Ok(k) => k,
809 Err(_) => continue,
810 };
811 let der = encode_p256_pkcs8_der(&key).unwrap();
812 let decoded = decode_p256_pkcs8_der(&der).unwrap();
813 assert_eq!(decoded.to_bytes(), key.to_bytes());
814 assert_eq!(decoded.public_key().to_bytes().len(), 65);
815 assert_eq!(decoded.public_key().to_bytes()[0], 0x04);
816 }
817 }
818
819 #[test]
820 fn encode_rejects_invalid_key() {
821 let invalid = [0u8; 32];
822 assert!(SecretKey::from_bytes(&invalid).is_err());
823 }
824
825 const P384_PRIVATE_KEY_HEX: &str =
828 "78eff649cf18e8211af8581aa79c9f3271decb2d416a18b96c4efe305ccba836f34b68cb2138798bf3d07ee6ffb306dc";
829 const P384_PUBLIC_KEY_HEX: &str = "049fd48843177dcffdded82c7946bc174bbc14e672bb3f8dfad3b27814b686eb366a2ccdd8e898b397355ae71a09bad802c92f5a4d11a844e28dfa763b5450ff0ae67fa555698cb02121d0457acf44d9ee5d09307fdf1b58f0089d6b43e32c9500";
830 const P384_PKCS8_DER_HEX: &str = "3081b6020100301006072a8648ce3d020106052b8104002204819e30819b020101043078eff649cf18e8211af8581aa79c9f3271decb2d416a18b96c4efe305ccba836f34b68cb2138798bf3d07ee6ffb306dca164036200049fd48843177dcffdded82c7946bc174bbc14e672bb3f8dfad3b27814b686eb366a2ccdd8e898b397355ae71a09bad802c92f5a4d11a844e28dfa763b5450ff0ae67fa555698cb02121d0457acf44d9ee5d09307fdf1b58f0089d6b43e32c9500";
831 const P384_SEC1_DER_HEX: &str = "3081a4020101043078eff649cf18e8211af8581aa79c9f3271decb2d416a18b96c4efe305ccba836f34b68cb2138798bf3d07ee6ffb306dca00706052b81040022a164036200049fd48843177dcffdded82c7946bc174bbc14e672bb3f8dfad3b27814b686eb366a2ccdd8e898b397355ae71a09bad802c92f5a4d11a844e28dfa763b5450ff0ae67fa555698cb02121d0457acf44d9ee5d09307fdf1b58f0089d6b43e32c9500";
832
833 const ED25519_SEED_HEX: &str = "cef4aba318713bb028bb60247b458aea67ce12f9fa394afbc185b298ffc98974";
835 const ED25519_DER_HEX: &str =
836 "302e020100300506032b657004220420cef4aba318713bb028bb60247b458aea67ce12f9fa394afbc185b298ffc98974";
837
838 #[test]
839 fn decode_ec_pkcs8_p256_known_vector() {
840 let der = decode_hex(TEST_DER_HEX);
841 match decode_ec_pkcs8_der(&der).unwrap() {
842 EcPrivateKey::P256(key) => {
843 assert_eq!(key.to_bytes(), TEST_PRIVATE_KEY);
844 assert_eq!(key.public_key().to_bytes(), TEST_PUBLIC_KEY);
845 }
846 EcPrivateKey::P384(_) => panic!("expected P-256"),
847 }
848 }
849
850 #[test]
851 fn decode_ec_pkcs8_p384_known_vector() {
852 let der = decode_hex(P384_PKCS8_DER_HEX);
853 match decode_ec_pkcs8_der(&der).unwrap() {
854 EcPrivateKey::P384(key) => {
855 assert_eq!(key.to_bytes().as_slice(), decode_hex(P384_PRIVATE_KEY_HEX));
856 assert_eq!(key.public_key().to_bytes().as_slice(), decode_hex(P384_PUBLIC_KEY_HEX));
857 }
858 EcPrivateKey::P256(_) => panic!("expected P-384"),
859 }
860 }
861
862 #[test]
863 fn decode_ec_sec1_p384_known_vector() {
864 let der = decode_hex(P384_SEC1_DER_HEX);
865 match decode_ec_sec1_der(&der).unwrap() {
866 EcPrivateKey::P384(key) => {
867 assert_eq!(key.to_bytes().as_slice(), decode_hex(P384_PRIVATE_KEY_HEX));
868 assert_eq!(key.public_key().to_bytes().as_slice(), decode_hex(P384_PUBLIC_KEY_HEX));
869 }
870 EcPrivateKey::P256(_) => panic!("expected P-384"),
871 }
872 }
873
874 #[test]
875 fn encode_p384_matches_known_der() {
876 let scalar: [u8; 48] = decode_hex(P384_PRIVATE_KEY_HEX).try_into().unwrap();
877 let key = p384::SecretKey::from_bytes(&scalar).unwrap();
878 let der = encode_p384_pkcs8_der(&key);
879 assert_eq!(der.as_slice(), decode_hex(P384_PKCS8_DER_HEX).as_slice());
880 }
881
882 #[test]
883 fn encode_p384_round_trip() {
884 let scalar: [u8; 48] = decode_hex(P384_PRIVATE_KEY_HEX).try_into().unwrap();
885 let key = p384::SecretKey::from_bytes(&scalar).unwrap();
886 let der = encode_p384_pkcs8_der(&key);
887 match decode_ec_pkcs8_der(&der).unwrap() {
888 EcPrivateKey::P384(decoded) => assert_eq!(decoded.to_bytes(), scalar),
889 EcPrivateKey::P256(_) => panic!("expected P-384"),
890 }
891 }
892
893 #[test]
894 fn decode_ed25519_known_vector() {
895 let der = decode_hex(ED25519_DER_HEX);
896 let key = decode_ed25519_pkcs8_der(&der).unwrap();
897 assert_eq!(key.to_bytes().as_slice(), decode_hex(ED25519_SEED_HEX));
898 }
899
900 #[test]
901 fn encode_ed25519_matches_known_der() {
902 let seed: [u8; 32] = decode_hex(ED25519_SEED_HEX).try_into().unwrap();
903 let key = crate::curve25519::ed25519::SecretKey::from_bytes(&seed);
904 let der = encode_ed25519_pkcs8_der(&key);
905 assert_eq!(der.as_slice(), decode_hex(ED25519_DER_HEX).as_slice());
906 }
907
908 #[test]
909 fn decode_ec_pkcs8_rejects_unsupported_curve() {
910 let mut der = decode_hex(P384_PKCS8_DER_HEX);
912 der[21] = 0x01;
913 assert!(matches!(decode_ec_pkcs8_der(&der), Err(Pkcs8Error::UnsupportedCurve)));
914 }
915
916 #[test]
917 fn decode_ec_pkcs8_rejects_trailing_data() {
918 let mut der = decode_hex(P384_PKCS8_DER_HEX);
919 der.push(0x00);
920 assert!(matches!(
921 decode_ec_pkcs8_der(&der),
922 Err(Pkcs8Error::InvalidDer(DerError::TrailingData))
923 ));
924 }
925
926 #[test]
927 fn decode_ed25519_rejects_wrong_algorithm() {
928 let mut der = decode_hex(ED25519_DER_HEX);
929 der[9] = 0x2c;
930 assert!(matches!(decode_ed25519_pkcs8_der(&der), Err(Pkcs8Error::UnsupportedAlgorithm)));
931 }
932
933 const ML_DSA_SEED: [u8; 32] = [
936 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
937 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
938 ];
939 const ML_DSA_44_SEED_PKCS8_HEX: &str =
940 "3034020100300b060960864801650304031104228020000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
941
942 fn der_length(out: &mut Vec<u8>, len: usize) {
943 if len < 0x80 {
944 out.push(len as u8);
945 } else if len < 0x100 {
946 out.push(0x81);
947 out.push(len as u8);
948 } else {
949 out.push(0x82);
950 out.extend_from_slice(&(len as u16).to_be_bytes());
951 }
952 }
953
954 fn der_tlv(tag: u8, content: &[u8]) -> Vec<u8> {
955 let mut out = vec![tag];
956 der_length(&mut out, content.len());
957 out.extend_from_slice(content);
958 out
959 }
960
961 fn mldsa_pkcs8(oid: &[u8], choice: &[u8], public_key: Option<&[u8]>) -> Vec<u8> {
964 let algorithm = der_tlv(0x30, &der_tlv(0x06, oid));
965
966 let mut pki = der_tlv(0x02, &[0x00]);
967 pki.extend(algorithm);
968 pki.extend(der_tlv(0x04, choice));
969 if let Some(public_key) = public_key {
970 let mut bit_string = vec![0x00];
971 bit_string.extend_from_slice(public_key);
972 pki.extend(der_tlv(0xa1, &der_tlv(0x03, &bit_string)));
973 }
974 der_tlv(0x30, &pki)
975 }
976
977 fn mldsa_both_choice(seed: &[u8; 32], expanded_len: usize) -> Vec<u8> {
978 let mut content = der_tlv(0x04, seed);
979 content.extend(der_tlv(0x04, &vec![0xab; expanded_len]));
980 der_tlv(0x30, &content)
981 }
982
983 #[test]
984 fn encode_mldsa44_matches_rfc9881_example() {
985 let key = crate::mldsa::MlDsa44SecretKey::new(&ML_DSA_SEED);
986 assert_eq!(
987 encode_mldsa44_pkcs8_der(&key).as_slice(),
988 decode_hex(ML_DSA_44_SEED_PKCS8_HEX).as_slice()
989 );
990 }
991
992 #[test]
993 fn decode_mldsa44_seed_example() {
994 let der = decode_hex(ML_DSA_44_SEED_PKCS8_HEX);
995 match decode_mldsa_pkcs8_der(&der).unwrap() {
996 MlDsaPrivateKey::Dsa44(key) => assert_eq!(key.seed(), &ML_DSA_SEED),
997 _ => panic!("expected ML-DSA-44"),
998 }
999 }
1000
1001 #[test]
1002 fn mldsa_seed_round_trip_all_variants() {
1003 let der = encode_mldsa44_pkcs8_der(&crate::mldsa::MlDsa44SecretKey::new(&ML_DSA_SEED));
1004 match decode_mldsa_pkcs8_der(&der).unwrap() {
1005 MlDsaPrivateKey::Dsa44(key) => assert_eq!(key.seed(), &ML_DSA_SEED),
1006 _ => panic!("expected ML-DSA-44"),
1007 }
1008
1009 let der = encode_mldsa65_pkcs8_der(&crate::mldsa::MlDsa65SecretKey::new(&ML_DSA_SEED));
1010 match decode_mldsa_pkcs8_der(&der).unwrap() {
1011 MlDsaPrivateKey::Dsa65(key) => assert_eq!(key.seed(), &ML_DSA_SEED),
1012 _ => panic!("expected ML-DSA-65"),
1013 }
1014
1015 let der = encode_mldsa87_pkcs8_der(&crate::mldsa::MlDsa87SecretKey::new(&ML_DSA_SEED));
1016 match decode_mldsa_pkcs8_der(&der).unwrap() {
1017 MlDsaPrivateKey::Dsa87(key) => assert_eq!(key.seed(), &ML_DSA_SEED),
1018 _ => panic!("expected ML-DSA-87"),
1019 }
1020 }
1021
1022 #[test]
1023 fn decode_mldsa_both_form() {
1024 let choice = mldsa_both_choice(&ML_DSA_SEED, 2560);
1026 let der = mldsa_pkcs8(OID_ML_DSA_44, &choice, None);
1027 match decode_mldsa_pkcs8_der(&der).unwrap() {
1028 MlDsaPrivateKey::Dsa44(key) => assert_eq!(key.seed(), &ML_DSA_SEED),
1029 _ => panic!("expected ML-DSA-44"),
1030 }
1031 }
1032
1033 #[test]
1034 fn decode_mldsa_rejects_expanded_only() {
1035 let choice = der_tlv(0x04, &vec![0xcd; 4032]);
1036 let der = mldsa_pkcs8(OID_ML_DSA_65, &choice, None);
1037 assert!(matches!(
1038 decode_mldsa_pkcs8_der(&der),
1039 Err(Pkcs8Error::MlDsaExpandedKeyNotSupported)
1040 ));
1041 }
1042
1043 #[test]
1044 fn decode_mldsa_checks_public_key() {
1045 let mut seed_only = vec![0x80];
1047 seed_only.push(0x20);
1048 seed_only.extend_from_slice(&ML_DSA_SEED);
1049 let key = crate::mldsa::MlDsa44SecretKey::new(&ML_DSA_SEED);
1050 let der = mldsa_pkcs8(OID_ML_DSA_44, &seed_only, Some(&key.public_key().to_bytes()));
1051 assert!(decode_mldsa_pkcs8_der(&der).is_ok());
1052
1053 let mut wrong = key.public_key().to_bytes();
1055 wrong[0] ^= 0xff;
1056 let der = mldsa_pkcs8(OID_ML_DSA_44, &seed_only, Some(&wrong));
1057 assert!(matches!(
1058 decode_mldsa_pkcs8_der(&der),
1059 Err(Pkcs8Error::InvalidPublicKeyMismatch)
1060 ));
1061 }
1062
1063 #[test]
1064 fn decode_mldsa_rejects_wrong_algorithm() {
1065 let mut seed_only = vec![0x80, 0x20];
1066 seed_only.extend_from_slice(&ML_DSA_SEED);
1067 let der = mldsa_pkcs8(OID_EC_PUBLIC_KEY, &seed_only, None);
1068 assert!(matches!(decode_mldsa_pkcs8_der(&der), Err(Pkcs8Error::UnsupportedAlgorithm)));
1069 }
1070
1071 #[test]
1072 fn decode_mldsa_rejects_trailing_data() {
1073 let mut der = decode_hex(ML_DSA_44_SEED_PKCS8_HEX);
1074 der.push(0x00);
1075 assert!(matches!(
1076 decode_mldsa_pkcs8_der(&der),
1077 Err(Pkcs8Error::InvalidDer(DerError::TrailingData))
1078 ));
1079 }
1080}