1use crate::aws_lc::{
5 EVP_PKEY_CTX_pqdsa_set_params, EVP_PKEY_pqdsa_new_raw_private_key, EVP_PKEY, EVP_PKEY_PQDSA,
6};
7use crate::encoding::{AsDer, AsRawBytes, Pkcs8V1Der, PqdsaPrivateKeyRaw};
8use crate::error::{KeyRejected, Unspecified};
9use crate::evp_pkey::No_EVP_PKEY_CTX_consumer;
10use crate::pkcs8;
11use crate::pkcs8::{Document, Version};
12use crate::pqdsa::signature::{PqdsaSigningAlgorithm, PublicKey};
13use crate::pqdsa::validate_pqdsa_evp_key;
14use crate::ptr::LcPtr;
15use crate::signature::KeyPair;
16use core::fmt::{Debug, Formatter};
17use std::ffi::c_int;
18
19#[allow(clippy::module_name_repetitions)]
21pub struct PqdsaKeyPair {
22 algorithm: &'static PqdsaSigningAlgorithm,
23 evp_pkey: LcPtr<EVP_PKEY>,
24 pubkey: PublicKey,
25}
26
27#[allow(clippy::missing_fields_in_debug)]
28impl Debug for PqdsaKeyPair {
29 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
30 f.debug_struct("PqdsaKeyPair")
31 .field("algorithm", &self.algorithm)
32 .finish()
33 }
34}
35
36impl KeyPair for PqdsaKeyPair {
37 type PublicKey = PublicKey;
38
39 fn public_key(&self) -> &Self::PublicKey {
40 &self.pubkey
41 }
42}
43
44pub struct PqdsaPrivateKey<'a>(pub(crate) &'a PqdsaKeyPair);
46
47impl Debug for PqdsaPrivateKey<'_> {
48 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
49 f.write_str(&format!("PqdsaPrivateKey({:?})", self.0.algorithm.0.id))
50 }
51}
52
53impl AsDer<Pkcs8V1Der<'static>> for PqdsaPrivateKey<'_> {
54 fn as_der(&self) -> Result<Pkcs8V1Der<'static>, Unspecified> {
61 Ok(Pkcs8V1Der::new(
62 self.0
63 .evp_pkey
64 .as_const()
65 .marshal_rfc5208_private_key(pkcs8::Version::V1)?,
66 ))
67 }
68}
69
70impl AsRawBytes<PqdsaPrivateKeyRaw<'static>> for PqdsaPrivateKey<'_> {
71 fn as_raw_bytes(&self) -> Result<PqdsaPrivateKeyRaw<'static>, Unspecified> {
75 Ok(PqdsaPrivateKeyRaw::new(
76 self.0.evp_pkey.as_const().marshal_raw_private_key()?,
77 ))
78 }
79}
80
81impl PqdsaKeyPair {
82 pub fn generate(algorithm: &'static PqdsaSigningAlgorithm) -> Result<Self, Unspecified> {
90 let evp_pkey = evp_key_pqdsa_generate(algorithm.0.id.nid())?;
91 let pubkey = PublicKey::from_private_evp_pkey(&evp_pkey)?;
92 Ok(Self {
93 algorithm,
94 evp_pkey,
95 pubkey,
96 })
97 }
98
99 pub fn from_pkcs8(
107 algorithm: &'static PqdsaSigningAlgorithm,
108 pkcs8: &[u8],
109 ) -> Result<Self, KeyRejected> {
110 let evp_pkey = LcPtr::<EVP_PKEY>::parse_rfc5208_private_key(pkcs8, EVP_PKEY_PQDSA)?;
111 validate_pqdsa_evp_key(&evp_pkey, algorithm.0.id)?;
112 let pubkey = PublicKey::from_private_evp_pkey(&evp_pkey)?;
113 Ok(Self {
114 algorithm,
115 evp_pkey,
116 pubkey,
117 })
118 }
119
120 pub fn from_raw_private_key(
127 algorithm: &'static PqdsaSigningAlgorithm,
128 raw_private_key: &[u8],
129 ) -> Result<Self, KeyRejected> {
130 let evp_pkey = LcPtr::<EVP_PKEY>::parse_raw_private_key(raw_private_key, EVP_PKEY_PQDSA)?;
131 validate_pqdsa_evp_key(&evp_pkey, algorithm.0.id)?;
132 let pubkey = PublicKey::from_private_evp_pkey(&evp_pkey)?;
133 Ok(Self {
134 algorithm,
135 evp_pkey,
136 pubkey,
137 })
138 }
139
140 pub fn from_seed(
176 algorithm: &'static PqdsaSigningAlgorithm,
177 seed: &[u8],
178 ) -> Result<Self, KeyRejected> {
179 let expected_seed_len = algorithm.0.id.seed_size_bytes();
180 match seed.len().cmp(&expected_seed_len) {
181 core::cmp::Ordering::Less => return Err(KeyRejected::too_small()),
182 core::cmp::Ordering::Greater => return Err(KeyRejected::too_large()),
183 core::cmp::Ordering::Equal => {}
184 }
185 let nid = algorithm.0.id.nid();
186 let evp_pkey = LcPtr::new(unsafe {
187 EVP_PKEY_pqdsa_new_raw_private_key(nid, seed.as_ptr(), seed.len())
188 })
189 .map_err(|()| KeyRejected::unspecified())?;
190 validate_pqdsa_evp_key(&evp_pkey, algorithm.0.id)?;
191 let pubkey =
192 PublicKey::from_private_evp_pkey(&evp_pkey).map_err(|_| KeyRejected::unspecified())?;
193 Ok(Self {
194 algorithm,
195 evp_pkey,
196 pubkey,
197 })
198 }
199
200 pub fn to_pkcs8v1(&self) -> Result<Document, Unspecified> {
210 Ok(Document::new(
211 self.evp_pkey
212 .as_const()
213 .marshal_rfc5208_private_key(Version::V1)?,
214 ))
215 }
216
217 #[cfg(feature = "unstable")]
225 #[deprecated(note = "use `PqdsaKeyPair::to_pkcs8v1`")]
226 pub fn to_pkcs8(&self) -> Result<Document, Unspecified> {
227 self.to_pkcs8v1()
228 }
229
230 pub fn sign(&self, msg: &[u8], signature: &mut [u8]) -> Result<usize, Unspecified> {
240 let sig_length = self.algorithm.signature_len();
241 if signature.len() < sig_length {
242 return Err(Unspecified);
243 }
244 let sig_bytes = self.evp_pkey.sign(msg, None, No_EVP_PKEY_CTX_consumer)?;
245 signature[0..sig_length].copy_from_slice(&sig_bytes);
246 Ok(sig_length)
247 }
248
249 #[must_use]
251 pub fn algorithm(&self) -> &'static PqdsaSigningAlgorithm {
252 self.algorithm
253 }
254
255 #[must_use]
257 pub fn private_key(&self) -> PqdsaPrivateKey<'_> {
258 PqdsaPrivateKey(self)
259 }
260}
261
262unsafe impl Send for PqdsaKeyPair {}
263
264unsafe impl Sync for PqdsaKeyPair {}
265
266pub(crate) fn evp_key_pqdsa_generate(nid: c_int) -> Result<LcPtr<EVP_PKEY>, Unspecified> {
267 let params_fn = |ctx| {
268 if 1 == unsafe { EVP_PKEY_CTX_pqdsa_set_params(ctx, nid) } {
269 Ok(())
270 } else {
271 Err(())
272 }
273 };
274 LcPtr::<EVP_PKEY>::generate(EVP_PKEY_PQDSA, Some(params_fn))
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280
281 use crate::signature::{
282 UnparsedPublicKey, ML_DSA_44_SIGNING, ML_DSA_65_SIGNING, ML_DSA_87_SIGNING,
283 };
284
285 const TEST_ALGORITHMS: &[&PqdsaSigningAlgorithm] =
286 &[&ML_DSA_44_SIGNING, &ML_DSA_65_SIGNING, &ML_DSA_87_SIGNING];
287
288 #[test]
289 fn test_public_key_serialization() {
290 for &alg in TEST_ALGORITHMS {
291 let keypair = PqdsaKeyPair::generate(alg).unwrap();
293 let message = b"Test message";
294 let different_message = b"Different message";
295 let mut signature = vec![0; alg.signature_len()];
296 assert!(keypair
297 .sign(message, &mut signature[0..(alg.signature_len() - 1)])
298 .is_err());
299 let sig_len = keypair.sign(message, &mut signature).unwrap();
300 assert_eq!(sig_len, alg.signature_len());
301 let invalid_signature = vec![0u8; alg.signature_len()];
302
303 let original_public_key = keypair.public_key();
304
305 let x509_der = original_public_key.as_der().unwrap();
306 let x509_public_key = UnparsedPublicKey::new(alg.0, x509_der.as_ref());
307 assert!(x509_public_key.verify(message, signature.as_ref()).is_ok());
308 assert!(x509_public_key
309 .verify(different_message, signature.as_ref())
310 .is_err());
311 assert!(x509_public_key.verify(message, &invalid_signature).is_err());
312
313 let raw = original_public_key.as_ref();
314 let raw_public_key = UnparsedPublicKey::new(alg.0, raw);
315 assert!(raw_public_key.verify(message, signature.as_ref()).is_ok());
316 assert!(raw_public_key
317 .verify(different_message, signature.as_ref())
318 .is_err());
319 assert!(raw_public_key
320 .verify(different_message, &invalid_signature)
321 .is_err());
322
323 #[cfg(feature = "ring-sig-verify")]
324 #[allow(deprecated)]
325 {
326 use crate::signature::VerificationAlgorithm;
327 assert!(alg
328 .0
329 .verify(
330 raw.into(),
331 message.as_ref().into(),
332 signature.as_slice().into()
333 )
334 .is_ok());
335 }
336 }
337 }
338
339 #[test]
340 fn test_private_key_serialization() {
341 for &alg in TEST_ALGORITHMS {
342 let keypair = PqdsaKeyPair::generate(alg).unwrap();
344 let message = b"Test message";
345 let mut original_signature = vec![0; alg.signature_len()];
346 let sig_len = keypair.sign(message, &mut original_signature).unwrap();
347 assert_eq!(sig_len, alg.signature_len());
348
349 let public_key = keypair.public_key();
350 let unparsed_public_key = UnparsedPublicKey::new(alg.0, public_key.as_ref());
351 unparsed_public_key
352 .verify(message, original_signature.as_ref())
353 .unwrap();
354
355 let pkcs8_1 = keypair.to_pkcs8v1().unwrap();
356 let pkcs8_2 = keypair.private_key().as_der().unwrap();
357 let raw = keypair.private_key().as_raw_bytes().unwrap();
358
359 assert_eq!(pkcs8_1.as_ref(), pkcs8_2.as_ref());
360
361 let pkcs8_keypair = PqdsaKeyPair::from_pkcs8(alg, pkcs8_1.as_ref()).unwrap();
362 let raw_keypair = PqdsaKeyPair::from_raw_private_key(alg, raw.as_ref()).unwrap();
363
364 assert_eq!(pkcs8_keypair.evp_pkey, raw_keypair.evp_pkey);
365 }
366 }
367
368 #[test]
369 fn test_from_seed() {
370 for &alg in TEST_ALGORITHMS {
371 let seed = [1u8; 32];
372 let kp = PqdsaKeyPair::from_seed(alg, &seed).unwrap();
373 assert_eq!(kp.algorithm(), alg);
374 let msg = b"seed test";
376 let mut sig = vec![0u8; alg.signature_len()];
377 let sig_len = kp.sign(msg, &mut sig).unwrap();
378 assert_eq!(sig_len, alg.signature_len());
379 let public_key = UnparsedPublicKey::new(alg.0, kp.public_key().as_ref());
380 public_key.verify(msg, &sig).unwrap();
381 }
382 }
383
384 #[test]
385 fn test_from_seed_deterministic() {
386 for &alg in TEST_ALGORITHMS {
387 let seed = [42u8; 32];
388 let kp1 = PqdsaKeyPair::from_seed(alg, &seed).unwrap();
389 let kp2 = PqdsaKeyPair::from_seed(alg, &seed).unwrap();
390 assert_eq!(kp1.public_key().as_ref(), kp2.public_key().as_ref());
391 }
392 }
393
394 #[test]
395 fn test_from_seed_wrong_size() {
396 use crate::error::KeyRejected;
397 for &alg in TEST_ALGORITHMS {
398 assert_eq!(
399 PqdsaKeyPair::from_seed(alg, &[0u8; 31]).err(),
400 Some(KeyRejected::too_small())
401 );
402 assert_eq!(
403 PqdsaKeyPair::from_seed(alg, &[0u8; 33]).err(),
404 Some(KeyRejected::too_large())
405 );
406 assert_eq!(
407 PqdsaKeyPair::from_seed(alg, &[]).err(),
408 Some(KeyRejected::too_small())
409 );
410 }
411 }
412
413 #[test]
414 fn test_from_seed_different_seeds_different_keys() {
415 for &alg in TEST_ALGORITHMS {
416 let kp1 = PqdsaKeyPair::from_seed(alg, &[1u8; 32]).unwrap();
417 let kp2 = PqdsaKeyPair::from_seed(alg, &[2u8; 32]).unwrap();
418 assert_ne!(kp1.public_key().as_ref(), kp2.public_key().as_ref());
419 }
420 }
421
422 #[test]
423 fn test_from_seed_raw_private_key_roundtrip() {
424 use crate::encoding::AsRawBytes;
425 for &alg in TEST_ALGORITHMS {
426 let seed = [55u8; 32];
427 let kp = PqdsaKeyPair::from_seed(alg, &seed).unwrap();
428 let raw_bytes = kp.private_key().as_raw_bytes().unwrap();
429 let kp2 = PqdsaKeyPair::from_raw_private_key(alg, raw_bytes.as_ref()).unwrap();
430 assert_eq!(kp.public_key().as_ref(), kp2.public_key().as_ref());
431 }
432 }
433
434 #[test]
435 fn test_from_seed_pkcs8_roundtrip() {
436 for &alg in TEST_ALGORITHMS {
437 let seed = [77u8; 32];
438 let kp = PqdsaKeyPair::from_seed(alg, &seed).unwrap();
439 let pkcs8 = kp.to_pkcs8v1().unwrap();
440 let kp2 = PqdsaKeyPair::from_pkcs8(alg, pkcs8.as_ref()).unwrap();
441 assert_eq!(kp.public_key().as_ref(), kp2.public_key().as_ref());
442 }
443 }
444
445 #[test]
446 fn test_from_seed_same_seed_different_algorithms() {
447 let seed = [42u8; 32];
449 let kp_44 = PqdsaKeyPair::from_seed(&ML_DSA_44_SIGNING, &seed).unwrap();
450 let kp_65 = PqdsaKeyPair::from_seed(&ML_DSA_65_SIGNING, &seed).unwrap();
451 let kp_87 = PqdsaKeyPair::from_seed(&ML_DSA_87_SIGNING, &seed).unwrap();
452 assert_ne!(
454 kp_44.public_key().as_ref().len(),
455 kp_65.public_key().as_ref().len()
456 );
457 assert_ne!(
458 kp_65.public_key().as_ref().len(),
459 kp_87.public_key().as_ref().len()
460 );
461 }
462
463 #[test]
467 fn test_from_raw_private_key_rejects_seed() {
468 for &alg in TEST_ALGORITHMS {
469 assert!(PqdsaKeyPair::from_raw_private_key(alg, &[7u8; 32]).is_err());
472 }
473 }
474
475 #[test]
479 fn test_from_seed_pkcs8_is_seed_form() {
480 for &alg in TEST_ALGORITHMS {
481 let kp = PqdsaKeyPair::from_seed(alg, &[3u8; 32]).unwrap();
482 let pkcs8 = kp.to_pkcs8v1().unwrap();
483 let raw_expanded = kp.private_key().as_raw_bytes().unwrap();
484 assert!(
487 pkcs8.as_ref().len() < raw_expanded.as_ref().len(),
488 "seed-form pkcs8 ({}) should be smaller than expanded key ({})",
489 pkcs8.as_ref().len(),
490 raw_expanded.as_ref().len(),
491 );
492 assert!(
493 pkcs8.as_ref().len() < 128,
494 "seed-form pkcs8 should be compact, got {}",
495 pkcs8.as_ref().len(),
496 );
497 }
498 }
499
500 #[test]
506 fn test_expanded_key_pkcs8_unavailable() {
507 for &alg in TEST_ALGORITHMS {
508 let seed_kp = PqdsaKeyPair::from_seed(alg, &[4u8; 32]).unwrap();
510 let raw = seed_kp.private_key().as_raw_bytes().unwrap();
511 let expanded_kp = PqdsaKeyPair::from_raw_private_key(alg, raw.as_ref()).unwrap();
512
513 assert!(expanded_kp.private_key().as_raw_bytes().is_ok());
516 assert!(
518 expanded_kp.to_pkcs8v1().is_err(),
519 "expanded-only key unexpectedly serialized to PKCS#8",
520 );
521 }
522 }
523
524 #[test]
528 fn test_from_seed_pkcs8_roundtrip_preserves_seed_form() {
529 for &alg in TEST_ALGORITHMS {
530 let kp = PqdsaKeyPair::from_seed(alg, &[8u8; 32]).unwrap();
531 let pkcs8 = kp.to_pkcs8v1().unwrap();
532 let reparsed = PqdsaKeyPair::from_pkcs8(alg, pkcs8.as_ref()).unwrap();
533 let pkcs8_again = reparsed.to_pkcs8v1().unwrap();
534 assert_eq!(pkcs8.as_ref(), pkcs8_again.as_ref());
536 assert_eq!(kp.public_key().as_ref(), reparsed.public_key().as_ref());
538 }
539 }
540
541 #[test]
545 fn test_from_seed_as_der_matches_to_pkcs8v1() {
546 for &alg in TEST_ALGORITHMS {
547 let kp = PqdsaKeyPair::from_seed(alg, &[11u8; 32]).unwrap();
548 let pkcs8 = kp.to_pkcs8v1().unwrap();
549 let der = kp.private_key().as_der().unwrap();
550 assert_eq!(pkcs8.as_ref(), der.as_ref());
551 }
552 }
553
554 #[test]
556 fn test_algorithm_getter() {
557 for &alg in TEST_ALGORITHMS {
558 let keypair = PqdsaKeyPair::generate(alg).unwrap();
559 assert_eq!(keypair.algorithm(), alg);
560 }
561 }
562
563 #[test]
564 fn test_algorithm_lengths() {
565 for (alg, signature_len, public_key_len) in [
566 (&ML_DSA_44_SIGNING, 2420, 1312),
567 (&ML_DSA_65_SIGNING, 3309, 1952),
568 (&ML_DSA_87_SIGNING, 4627, 2592),
569 ] {
570 assert_eq!(alg.signature_len(), signature_len);
571 assert_eq!(alg.public_key_len(), public_key_len);
572 assert_eq!(alg.seed_len(), 32);
573 }
574 }
575
576 #[cfg(feature = "unstable")]
579 #[allow(deprecated)]
580 #[test]
581 fn test_to_pkcs8_deprecated_alias() {
582 for &alg in TEST_ALGORITHMS {
583 let kp = PqdsaKeyPair::from_seed(alg, &[9u8; 32]).unwrap();
584 assert_eq!(
585 kp.to_pkcs8().unwrap().as_ref(),
586 kp.to_pkcs8v1().unwrap().as_ref()
587 );
588 }
589 }
590
591 #[test]
592 fn test_debug() {
593 for &alg in TEST_ALGORITHMS {
594 let keypair = PqdsaKeyPair::generate(alg).unwrap();
595 assert!(
596 format!("{keypair:?}").starts_with("PqdsaKeyPair { algorithm: PqdsaSigningAlgorithm(PqdsaVerificationAlgorithm { id:"),
597 "{keypair:?}"
598 );
599 let pubkey = keypair.public_key();
600 assert!(
601 format!("{pubkey:?}").starts_with("PqdsaPublicKey("),
602 "{pubkey:?}"
603 );
604 let privkey = keypair.private_key();
605 assert!(
606 format!("{privkey:?}").starts_with("PqdsaPrivateKey("),
607 "{privkey:?}"
608 );
609 }
610 }
611}