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 AsDer<Pkcs8V1Der<'static>> for PqdsaPrivateKey<'_> {
48 fn as_der(&self) -> Result<Pkcs8V1Der<'static>, Unspecified> {
53 Ok(Pkcs8V1Der::new(
54 self.0
55 .evp_pkey
56 .as_const()
57 .marshal_rfc5208_private_key(pkcs8::Version::V1)?,
58 ))
59 }
60}
61
62impl AsRawBytes<PqdsaPrivateKeyRaw<'static>> for PqdsaPrivateKey<'_> {
63 fn as_raw_bytes(&self) -> Result<PqdsaPrivateKeyRaw<'static>, Unspecified> {
64 Ok(PqdsaPrivateKeyRaw::new(
65 self.0.evp_pkey.as_const().marshal_raw_private_key()?,
66 ))
67 }
68}
69
70impl PqdsaKeyPair {
71 pub fn generate(algorithm: &'static PqdsaSigningAlgorithm) -> Result<Self, Unspecified> {
76 let evp_pkey = evp_key_pqdsa_generate(algorithm.0.id.nid())?;
77 let pubkey = PublicKey::from_private_evp_pkey(&evp_pkey)?;
78 Ok(Self {
79 algorithm,
80 evp_pkey,
81 pubkey,
82 })
83 }
84
85 pub fn from_pkcs8(
90 algorithm: &'static PqdsaSigningAlgorithm,
91 pkcs8: &[u8],
92 ) -> Result<Self, KeyRejected> {
93 let evp_pkey = LcPtr::<EVP_PKEY>::parse_rfc5208_private_key(pkcs8, EVP_PKEY_PQDSA)?;
94 validate_pqdsa_evp_key(&evp_pkey, algorithm.0.id)?;
95 let pubkey = PublicKey::from_private_evp_pkey(&evp_pkey)?;
96 Ok(Self {
97 algorithm,
98 evp_pkey,
99 pubkey,
100 })
101 }
102
103 pub fn from_raw_private_key(
108 algorithm: &'static PqdsaSigningAlgorithm,
109 raw_private_key: &[u8],
110 ) -> Result<Self, KeyRejected> {
111 let evp_pkey = LcPtr::<EVP_PKEY>::parse_raw_private_key(raw_private_key, EVP_PKEY_PQDSA)?;
112 validate_pqdsa_evp_key(&evp_pkey, algorithm.0.id)?;
113 let pubkey = PublicKey::from_private_evp_pkey(&evp_pkey)?;
114 Ok(Self {
115 algorithm,
116 evp_pkey,
117 pubkey,
118 })
119 }
120
121 pub fn from_seed(
152 algorithm: &'static PqdsaSigningAlgorithm,
153 seed: &[u8],
154 ) -> Result<Self, KeyRejected> {
155 let expected_seed_len = algorithm.0.id.seed_size_bytes();
156 match seed.len().cmp(&expected_seed_len) {
157 core::cmp::Ordering::Less => return Err(KeyRejected::too_small()),
158 core::cmp::Ordering::Greater => return Err(KeyRejected::too_large()),
159 core::cmp::Ordering::Equal => {}
160 }
161 let nid = algorithm.0.id.nid();
162 let evp_pkey = LcPtr::new(unsafe {
163 EVP_PKEY_pqdsa_new_raw_private_key(nid, seed.as_ptr(), seed.len())
164 })
165 .map_err(|()| KeyRejected::unspecified())?;
166 validate_pqdsa_evp_key(&evp_pkey, algorithm.0.id)?;
167 let pubkey =
168 PublicKey::from_private_evp_pkey(&evp_pkey).map_err(|_| KeyRejected::unspecified())?;
169 Ok(Self {
170 algorithm,
171 evp_pkey,
172 pubkey,
173 })
174 }
175
176 pub fn to_pkcs8(&self) -> Result<Document, Unspecified> {
181 Ok(Document::new(
182 self.evp_pkey
183 .as_const()
184 .marshal_rfc5208_private_key(Version::V1)?,
185 ))
186 }
187
188 pub fn sign(&self, msg: &[u8], signature: &mut [u8]) -> Result<usize, Unspecified> {
194 let sig_length = self.algorithm.signature_len();
195 if signature.len() < sig_length {
196 return Err(Unspecified);
197 }
198 let sig_bytes = self.evp_pkey.sign(msg, None, No_EVP_PKEY_CTX_consumer)?;
199 signature[0..sig_length].copy_from_slice(&sig_bytes);
200 Ok(sig_length)
201 }
202
203 #[must_use]
205 pub fn algorithm(&self) -> &'static PqdsaSigningAlgorithm {
206 self.algorithm
207 }
208
209 #[must_use]
211 pub fn private_key(&self) -> PqdsaPrivateKey<'_> {
212 PqdsaPrivateKey(self)
213 }
214}
215
216unsafe impl Send for PqdsaKeyPair {}
217
218unsafe impl Sync for PqdsaKeyPair {}
219
220pub(crate) fn evp_key_pqdsa_generate(nid: c_int) -> Result<LcPtr<EVP_PKEY>, Unspecified> {
221 let params_fn = |ctx| {
222 if 1 == unsafe { EVP_PKEY_CTX_pqdsa_set_params(ctx, nid) } {
223 Ok(())
224 } else {
225 Err(())
226 }
227 };
228 LcPtr::<EVP_PKEY>::generate(EVP_PKEY_PQDSA, Some(params_fn))
229}
230
231#[cfg(all(test, feature = "unstable"))]
232mod tests {
233 use super::*;
234
235 use crate::signature::UnparsedPublicKey;
236 use crate::unstable::signature::{ML_DSA_44_SIGNING, ML_DSA_65_SIGNING, ML_DSA_87_SIGNING};
237
238 const TEST_ALGORITHMS: &[&PqdsaSigningAlgorithm] =
239 &[&ML_DSA_44_SIGNING, &ML_DSA_65_SIGNING, &ML_DSA_87_SIGNING];
240
241 #[test]
242 fn test_public_key_serialization() {
243 for &alg in TEST_ALGORITHMS {
244 let keypair = PqdsaKeyPair::generate(alg).unwrap();
246 let message = b"Test message";
247 let different_message = b"Different message";
248 let mut signature = vec![0; alg.signature_len()];
249 assert!(keypair
250 .sign(message, &mut signature[0..(alg.signature_len() - 1)])
251 .is_err());
252 let sig_len = keypair.sign(message, &mut signature).unwrap();
253 assert_eq!(sig_len, alg.signature_len());
254 let invalid_signature = vec![0u8; alg.signature_len()];
255
256 let original_public_key = keypair.public_key();
257
258 let x509_der = original_public_key.as_der().unwrap();
259 let x509_public_key = UnparsedPublicKey::new(alg.0, x509_der.as_ref());
260 assert!(x509_public_key.verify(message, signature.as_ref()).is_ok());
261 assert!(x509_public_key
262 .verify(different_message, signature.as_ref())
263 .is_err());
264 assert!(x509_public_key.verify(message, &invalid_signature).is_err());
265
266 let raw = original_public_key.as_ref();
267 let raw_public_key = UnparsedPublicKey::new(alg.0, raw);
268 assert!(raw_public_key.verify(message, signature.as_ref()).is_ok());
269 assert!(raw_public_key
270 .verify(different_message, signature.as_ref())
271 .is_err());
272 assert!(raw_public_key
273 .verify(different_message, &invalid_signature)
274 .is_err());
275
276 #[cfg(feature = "ring-sig-verify")]
277 #[allow(deprecated)]
278 {
279 use crate::signature::VerificationAlgorithm;
280 assert!(alg
281 .0
282 .verify(
283 raw.into(),
284 message.as_ref().into(),
285 signature.as_slice().into()
286 )
287 .is_ok());
288 }
289 }
290 }
291
292 #[test]
293 fn test_private_key_serialization() {
294 for &alg in TEST_ALGORITHMS {
295 let keypair = PqdsaKeyPair::generate(alg).unwrap();
297 let message = b"Test message";
298 let mut original_signature = vec![0; alg.signature_len()];
299 let sig_len = keypair.sign(message, &mut original_signature).unwrap();
300 assert_eq!(sig_len, alg.signature_len());
301
302 let public_key = keypair.public_key();
303 let unparsed_public_key = UnparsedPublicKey::new(alg.0, public_key.as_ref());
304 unparsed_public_key
305 .verify(message, original_signature.as_ref())
306 .unwrap();
307
308 let pkcs8_1 = keypair.to_pkcs8().unwrap();
309 let pkcs8_2 = keypair.private_key().as_der().unwrap();
310 let raw = keypair.private_key().as_raw_bytes().unwrap();
311
312 assert_eq!(pkcs8_1.as_ref(), pkcs8_2.as_ref());
313
314 let pkcs8_keypair = PqdsaKeyPair::from_pkcs8(alg, pkcs8_1.as_ref()).unwrap();
315 let raw_keypair = PqdsaKeyPair::from_raw_private_key(alg, raw.as_ref()).unwrap();
316
317 assert_eq!(pkcs8_keypair.evp_pkey, raw_keypair.evp_pkey);
318 }
319 }
320
321 #[test]
322 fn test_from_seed() {
323 for &alg in TEST_ALGORITHMS {
324 let seed = [1u8; 32];
325 let kp = PqdsaKeyPair::from_seed(alg, &seed).unwrap();
326 assert_eq!(kp.algorithm(), alg);
327 let msg = b"seed test";
329 let mut sig = vec![0; alg.signature_len()];
330 let sig_len = kp.sign(msg, &mut sig).unwrap();
331 assert_eq!(sig_len, alg.signature_len());
332 }
333 }
334
335 #[test]
336 fn test_from_seed_deterministic() {
337 for &alg in TEST_ALGORITHMS {
338 let seed = [42u8; 32];
339 let kp1 = PqdsaKeyPair::from_seed(alg, &seed).unwrap();
340 let kp2 = PqdsaKeyPair::from_seed(alg, &seed).unwrap();
341 assert_eq!(kp1.public_key().as_ref(), kp2.public_key().as_ref());
342 }
343 }
344
345 #[test]
346 fn test_from_seed_wrong_size() {
347 use crate::error::KeyRejected;
348 for &alg in TEST_ALGORITHMS {
349 assert_eq!(
350 PqdsaKeyPair::from_seed(alg, &[0u8; 31]).err(),
351 Some(KeyRejected::too_small())
352 );
353 assert_eq!(
354 PqdsaKeyPair::from_seed(alg, &[0u8; 33]).err(),
355 Some(KeyRejected::too_large())
356 );
357 assert_eq!(
358 PqdsaKeyPair::from_seed(alg, &[]).err(),
359 Some(KeyRejected::too_small())
360 );
361 }
362 }
363
364 #[test]
365 fn test_from_seed_different_seeds_different_keys() {
366 for &alg in TEST_ALGORITHMS {
367 let kp1 = PqdsaKeyPair::from_seed(alg, &[1u8; 32]).unwrap();
368 let kp2 = PqdsaKeyPair::from_seed(alg, &[2u8; 32]).unwrap();
369 assert_ne!(kp1.public_key().as_ref(), kp2.public_key().as_ref());
370 }
371 }
372
373 #[test]
374 fn test_from_seed_raw_private_key_roundtrip() {
375 use crate::encoding::AsRawBytes;
376 for &alg in TEST_ALGORITHMS {
377 let seed = [55u8; 32];
378 let kp = PqdsaKeyPair::from_seed(alg, &seed).unwrap();
379 let raw_bytes = kp.private_key().as_raw_bytes().unwrap();
380 let kp2 = PqdsaKeyPair::from_raw_private_key(alg, raw_bytes.as_ref()).unwrap();
381 assert_eq!(kp.public_key().as_ref(), kp2.public_key().as_ref());
382 }
383 }
384
385 #[test]
386 fn test_from_seed_pkcs8_roundtrip() {
387 for &alg in TEST_ALGORITHMS {
388 let seed = [77u8; 32];
389 let kp = PqdsaKeyPair::from_seed(alg, &seed).unwrap();
390 let pkcs8 = kp.to_pkcs8().unwrap();
391 let kp2 = PqdsaKeyPair::from_pkcs8(alg, pkcs8.as_ref()).unwrap();
392 assert_eq!(kp.public_key().as_ref(), kp2.public_key().as_ref());
393 }
394 }
395
396 #[test]
397 fn test_from_seed_same_seed_different_algorithms() {
398 let seed = [42u8; 32];
400 let kp_44 = PqdsaKeyPair::from_seed(&ML_DSA_44_SIGNING, &seed).unwrap();
401 let kp_65 = PqdsaKeyPair::from_seed(&ML_DSA_65_SIGNING, &seed).unwrap();
402 let kp_87 = PqdsaKeyPair::from_seed(&ML_DSA_87_SIGNING, &seed).unwrap();
403 assert_ne!(
405 kp_44.public_key().as_ref().len(),
406 kp_65.public_key().as_ref().len()
407 );
408 assert_ne!(
409 kp_65.public_key().as_ref().len(),
410 kp_87.public_key().as_ref().len()
411 );
412 }
413
414 #[test]
416 fn test_algorithm_getter() {
417 for &alg in TEST_ALGORITHMS {
418 let keypair = PqdsaKeyPair::generate(alg).unwrap();
419 assert_eq!(keypair.algorithm(), alg);
420 }
421 }
422
423 #[test]
425 fn test_debug() {
426 for &alg in TEST_ALGORITHMS {
427 let keypair = PqdsaKeyPair::generate(alg).unwrap();
428 assert!(
429 format!("{keypair:?}").starts_with("PqdsaKeyPair { algorithm: PqdsaSigningAlgorithm(PqdsaVerificationAlgorithm { id:"),
430 "{keypair:?}"
431 );
432 let pubkey = keypair.public_key();
433 assert!(
434 format!("{pubkey:?}").starts_with("PqdsaPublicKey("),
435 "{pubkey:?}"
436 );
437 }
438 }
439}