aws_lc_rs/pqdsa/
key_pair.rs1use crate::aws_lc::{EVP_PKEY_CTX_pqdsa_set_params, EVP_PKEY, EVP_PKEY_PQDSA};
5use crate::encoding::{AsDer, AsRawBytes, Pkcs8V1Der, PqdsaPrivateKeyRaw};
6use crate::error::{KeyRejected, Unspecified};
7use crate::evp_pkey::No_EVP_PKEY_CTX_consumer;
8use crate::pkcs8;
9use crate::pkcs8::{Document, Version};
10use crate::pqdsa::signature::{PqdsaSigningAlgorithm, PublicKey};
11use crate::pqdsa::validate_pqdsa_evp_key;
12use crate::ptr::LcPtr;
13use crate::signature::KeyPair;
14use core::fmt::{Debug, Formatter};
15use std::ffi::c_int;
16
17#[allow(clippy::module_name_repetitions)]
19pub struct PqdsaKeyPair {
20 algorithm: &'static PqdsaSigningAlgorithm,
21 evp_pkey: LcPtr<EVP_PKEY>,
22 pubkey: PublicKey,
23}
24
25#[allow(clippy::missing_fields_in_debug)]
26impl Debug for PqdsaKeyPair {
27 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
28 f.debug_struct("PqdsaKeyPair")
29 .field("algorithm", &self.algorithm)
30 .finish()
31 }
32}
33
34impl KeyPair for PqdsaKeyPair {
35 type PublicKey = PublicKey;
36
37 fn public_key(&self) -> &Self::PublicKey {
38 &self.pubkey
39 }
40}
41
42pub struct PqdsaPrivateKey<'a>(pub(crate) &'a PqdsaKeyPair);
44
45impl AsDer<Pkcs8V1Der<'static>> for PqdsaPrivateKey<'_> {
46 fn as_der(&self) -> Result<Pkcs8V1Der<'static>, Unspecified> {
51 Ok(Pkcs8V1Der::new(
52 self.0
53 .evp_pkey
54 .as_const()
55 .marshal_rfc5208_private_key(pkcs8::Version::V1)?,
56 ))
57 }
58}
59
60impl AsRawBytes<PqdsaPrivateKeyRaw<'static>> for PqdsaPrivateKey<'_> {
61 fn as_raw_bytes(&self) -> Result<PqdsaPrivateKeyRaw<'static>, Unspecified> {
62 Ok(PqdsaPrivateKeyRaw::new(
63 self.0.evp_pkey.as_const().marshal_raw_private_key()?,
64 ))
65 }
66}
67
68impl PqdsaKeyPair {
69 pub fn generate(algorithm: &'static PqdsaSigningAlgorithm) -> Result<Self, Unspecified> {
74 let evp_pkey = evp_key_pqdsa_generate(algorithm.0.id.nid())?;
75 let pubkey = PublicKey::from_private_evp_pkey(&evp_pkey)?;
76 Ok(Self {
77 algorithm,
78 evp_pkey,
79 pubkey,
80 })
81 }
82
83 pub fn from_pkcs8(
88 algorithm: &'static PqdsaSigningAlgorithm,
89 pkcs8: &[u8],
90 ) -> Result<Self, KeyRejected> {
91 let evp_pkey = LcPtr::<EVP_PKEY>::parse_rfc5208_private_key(pkcs8, EVP_PKEY_PQDSA)?;
92 validate_pqdsa_evp_key(&evp_pkey, algorithm.0.id)?;
93 let pubkey = PublicKey::from_private_evp_pkey(&evp_pkey)?;
94 Ok(Self {
95 algorithm,
96 evp_pkey,
97 pubkey,
98 })
99 }
100
101 pub fn from_raw_private_key(
106 algorithm: &'static PqdsaSigningAlgorithm,
107 raw_private_key: &[u8],
108 ) -> Result<Self, KeyRejected> {
109 let evp_pkey = LcPtr::<EVP_PKEY>::parse_raw_private_key(raw_private_key, EVP_PKEY_PQDSA)?;
110 validate_pqdsa_evp_key(&evp_pkey, algorithm.0.id)?;
111 let pubkey = PublicKey::from_private_evp_pkey(&evp_pkey)?;
112 Ok(Self {
113 algorithm,
114 evp_pkey,
115 pubkey,
116 })
117 }
118
119 pub fn to_pkcs8(&self) -> Result<Document, Unspecified> {
124 Ok(Document::new(
125 self.evp_pkey
126 .as_const()
127 .marshal_rfc5208_private_key(Version::V1)?,
128 ))
129 }
130
131 pub fn sign(&self, msg: &[u8], signature: &mut [u8]) -> Result<usize, Unspecified> {
137 let sig_length = self.algorithm.signature_len();
138 if signature.len() < sig_length {
139 return Err(Unspecified);
140 }
141 let sig_bytes = self.evp_pkey.sign(msg, None, No_EVP_PKEY_CTX_consumer)?;
142 signature[0..sig_length].copy_from_slice(&sig_bytes);
143 Ok(sig_length)
144 }
145
146 #[must_use]
148 pub fn algorithm(&self) -> &'static PqdsaSigningAlgorithm {
149 self.algorithm
150 }
151
152 #[must_use]
154 pub fn private_key(&self) -> PqdsaPrivateKey<'_> {
155 PqdsaPrivateKey(self)
156 }
157}
158
159unsafe impl Send for PqdsaKeyPair {}
160
161unsafe impl Sync for PqdsaKeyPair {}
162
163pub(crate) fn evp_key_pqdsa_generate(nid: c_int) -> Result<LcPtr<EVP_PKEY>, Unspecified> {
164 let params_fn = |ctx| {
165 if 1 == unsafe { EVP_PKEY_CTX_pqdsa_set_params(ctx, nid) } {
166 Ok(())
167 } else {
168 Err(())
169 }
170 };
171 LcPtr::<EVP_PKEY>::generate(EVP_PKEY_PQDSA, Some(params_fn))
172}
173
174#[cfg(all(test, feature = "unstable"))]
175mod tests {
176 use super::*;
177
178 use crate::signature::{UnparsedPublicKey, VerificationAlgorithm};
179 use crate::unstable::signature::{ML_DSA_44_SIGNING, ML_DSA_65_SIGNING, ML_DSA_87_SIGNING};
180
181 const TEST_ALGORITHMS: &[&PqdsaSigningAlgorithm] =
182 &[&ML_DSA_44_SIGNING, &ML_DSA_65_SIGNING, &ML_DSA_87_SIGNING];
183
184 #[test]
185 fn test_public_key_serialization() {
186 for &alg in TEST_ALGORITHMS {
187 let keypair = PqdsaKeyPair::generate(alg).unwrap();
189 let message = b"Test message";
190 let different_message = b"Different message";
191 let mut signature = vec![0; alg.signature_len()];
192 assert!(keypair
193 .sign(message, &mut signature[0..(alg.signature_len() - 1)])
194 .is_err());
195 let sig_len = keypair.sign(message, &mut signature).unwrap();
196 assert_eq!(sig_len, alg.signature_len());
197 let invalid_signature = vec![0u8; alg.signature_len()];
198
199 let original_public_key = keypair.public_key();
200
201 let x509_der = original_public_key.as_der().unwrap();
202 let x509_public_key = UnparsedPublicKey::new(alg.0, x509_der.as_ref());
203 assert!(x509_public_key.verify(message, signature.as_ref()).is_ok());
204 assert!(x509_public_key
205 .verify(different_message, signature.as_ref())
206 .is_err());
207 assert!(x509_public_key.verify(message, &invalid_signature).is_err());
208
209 let raw = original_public_key.as_ref();
210 let raw_public_key = UnparsedPublicKey::new(alg.0, raw);
211 assert!(raw_public_key.verify(message, signature.as_ref()).is_ok());
212 assert!(raw_public_key
213 .verify(different_message, signature.as_ref())
214 .is_err());
215 assert!(raw_public_key
216 .verify(different_message, &invalid_signature)
217 .is_err());
218
219 #[cfg(feature = "ring-sig-verify")]
220 #[allow(deprecated)]
221 {
222 assert!(alg
223 .0
224 .verify(
225 raw.into(),
226 message.as_ref().into(),
227 signature.as_slice().into()
228 )
229 .is_ok());
230 }
231 }
232 }
233
234 #[test]
235 fn test_private_key_serialization() {
236 for &alg in TEST_ALGORITHMS {
237 let keypair = PqdsaKeyPair::generate(alg).unwrap();
239 let message = b"Test message";
240 let mut original_signature = vec![0; alg.signature_len()];
241 let sig_len = keypair.sign(message, &mut original_signature).unwrap();
242 assert_eq!(sig_len, alg.signature_len());
243
244 let public_key = keypair.public_key();
245 let unparsed_public_key = UnparsedPublicKey::new(alg.0, public_key.as_ref());
246 unparsed_public_key
247 .verify(message, original_signature.as_ref())
248 .unwrap();
249
250 let pkcs8_1 = keypair.to_pkcs8().unwrap();
251 let pkcs8_2 = keypair.private_key().as_der().unwrap();
252 let raw = keypair.private_key().as_raw_bytes().unwrap();
253
254 assert_eq!(pkcs8_1.as_ref(), pkcs8_2.as_ref());
255
256 let pkcs8_keypair = PqdsaKeyPair::from_pkcs8(alg, pkcs8_1.as_ref()).unwrap();
257 let raw_keypair = PqdsaKeyPair::from_raw_private_key(alg, raw.as_ref()).unwrap();
258
259 assert_eq!(pkcs8_keypair.evp_pkey, raw_keypair.evp_pkey);
260 }
261 }
262
263 #[test]
265 fn test_algorithm_getter() {
266 for &alg in TEST_ALGORITHMS {
267 let keypair = PqdsaKeyPair::generate(alg).unwrap();
268 assert_eq!(keypair.algorithm(), alg);
269 }
270 }
271
272 #[test]
274 fn test_debug() {
275 for &alg in TEST_ALGORITHMS {
276 let keypair = PqdsaKeyPair::generate(alg).unwrap();
277 assert!(
278 format!("{keypair:?}").starts_with("PqdsaKeyPair { algorithm: PqdsaSigningAlgorithm(PqdsaVerificationAlgorithm { id:"),
279 "{keypair:?}"
280 );
281 let pubkey = keypair.public_key();
282 assert!(
283 format!("{pubkey:?}").starts_with("PqdsaPublicKey("),
284 "{pubkey:?}"
285 );
286 }
287 }
288}