1pub(crate) mod key_pair;
5pub(crate) mod signature;
6
7use crate::aws_lc::{EVP_PKEY, EVP_PKEY_PQDSA, NID_MLDSA44, NID_MLDSA65, NID_MLDSA87};
8use crate::error::{KeyRejected, Unspecified};
9use crate::ptr::LcPtr;
10use core::ffi::c_int;
11
12#[derive(Debug, Eq, PartialEq)]
13#[allow(non_camel_case_types)]
14pub(crate) enum AlgorithmID {
15 ML_DSA_44,
16 ML_DSA_65,
17 ML_DSA_87,
18}
19
20impl AlgorithmID {
21 #[allow(dead_code)]
22 pub(crate) const fn from_nid(nid: c_int) -> Result<Self, Unspecified> {
23 match nid {
24 NID_MLDSA44 => Ok(Self::ML_DSA_44),
25 NID_MLDSA65 => Ok(Self::ML_DSA_65),
26 NID_MLDSA87 => Ok(Self::ML_DSA_87),
27 _ => Err(Unspecified),
28 }
29 }
30
31 pub(crate) const fn nid(&self) -> c_int {
32 match self {
33 Self::ML_DSA_44 => NID_MLDSA44,
34 Self::ML_DSA_65 => NID_MLDSA65,
35 Self::ML_DSA_87 => NID_MLDSA87,
36 }
37 }
38
39 #[allow(dead_code)]
40 pub(crate) const fn priv_key_size_bytes(&self) -> usize {
41 match self {
42 Self::ML_DSA_44 => 2560,
43 Self::ML_DSA_65 => 4032,
44 Self::ML_DSA_87 => 4896,
45 }
46 }
47
48 pub(crate) const fn pub_key_size_bytes(&self) -> usize {
49 match self {
50 Self::ML_DSA_44 => 1312,
51 Self::ML_DSA_65 => 1952,
52 Self::ML_DSA_87 => 2592,
53 }
54 }
55
56 pub(crate) const fn seed_size_bytes(&self) -> usize {
57 match self {
59 Self::ML_DSA_44 | Self::ML_DSA_65 | Self::ML_DSA_87 => 32,
60 }
61 }
62
63 pub(crate) const fn signature_size_bytes(&self) -> usize {
64 match self {
65 Self::ML_DSA_44 => 2420,
66 Self::ML_DSA_65 => 3309,
67 Self::ML_DSA_87 => 4627,
68 }
69 }
70}
71
72pub(crate) fn validate_pqdsa_evp_key(
73 evp_pkey: &LcPtr<EVP_PKEY>,
74 id: &'static AlgorithmID,
75) -> Result<(), KeyRejected> {
76 if evp_pkey.as_const().key_size_bytes() == id.pub_key_size_bytes() {
77 Ok(())
78 } else {
79 Err(KeyRejected::unspecified())
80 }
81}
82
83pub(crate) fn parse_pqdsa_public_key(
84 key_bytes: &[u8],
85 id: &'static AlgorithmID,
86) -> Result<LcPtr<EVP_PKEY>, KeyRejected> {
87 LcPtr::<EVP_PKEY>::parse_rfc5280_public_key(key_bytes, EVP_PKEY_PQDSA)
88 .or(LcPtr::<EVP_PKEY>::parse_raw_public_key(
89 key_bytes,
90 EVP_PKEY_PQDSA,
91 ))
92 .and_then(|key| validate_pqdsa_evp_key(&key, id).map(|()| key))
93}
94
95#[cfg(test)]
96mod tests {
97 use crate::aws_lc::{
98 EVP_PKEY_cmp, EVP_PKEY, EVP_PKEY_PQDSA, NID_MLDSA44, NID_MLDSA65, NID_MLDSA87,
99 };
100
101 use crate::evp_pkey::*;
102
103 use crate::pkcs8::Version;
104 use crate::pqdsa::key_pair::evp_key_pqdsa_generate;
105 use crate::pqdsa::AlgorithmID;
106 use crate::ptr::LcPtr;
107
108 #[test]
109 fn test_keygen() {
110 for nid in [NID_MLDSA44, NID_MLDSA65, NID_MLDSA87] {
111 let key = evp_key_pqdsa_generate(nid).unwrap();
112 println!("key size: {:?}", key.as_const().key_size_bytes());
113 test_serialization_for(&key, &AlgorithmID::from_nid(nid).unwrap());
114 test_signing_for(&key, &AlgorithmID::from_nid(nid).unwrap());
115 }
116 }
117
118 fn test_serialization_for(evp_pkey: &LcPtr<EVP_PKEY>, id: &AlgorithmID) {
119 let public_buffer = evp_pkey.as_const().marshal_rfc5280_public_key().unwrap();
120 println!("public marshall: {public_buffer:?}");
121 let key_public =
122 LcPtr::<EVP_PKEY>::parse_rfc5280_public_key(&public_buffer, EVP_PKEY_PQDSA).unwrap();
123
124 let private_buffer = evp_pkey
125 .as_const()
126 .marshal_rfc5208_private_key(Version::V1)
127 .unwrap();
128 println!("private marshall: {private_buffer:?}");
129 let key_private =
130 LcPtr::<EVP_PKEY>::parse_rfc5208_private_key(&private_buffer, EVP_PKEY_PQDSA).unwrap();
131
132 let raw_public_buffer = key_public.as_const().marshal_raw_public_key().unwrap();
133 assert_eq!(raw_public_buffer.len(), id.pub_key_size_bytes());
134 println!("raw public size: {}", raw_public_buffer.len());
135 let key_public2 =
136 LcPtr::<EVP_PKEY>::parse_raw_public_key(&raw_public_buffer, EVP_PKEY_PQDSA).unwrap();
137
138 assert_eq!(1, unsafe {
139 EVP_PKEY_cmp(key_public.as_const_ptr(), key_public2.as_const_ptr())
140 });
141
142 let raw_private_buffer = key_private.as_const().marshal_raw_private_key().unwrap();
143 assert_eq!(raw_private_buffer.len(), id.priv_key_size_bytes());
144 println!("raw private size: {}", raw_private_buffer.len());
145 let key_private2 =
146 LcPtr::<EVP_PKEY>::parse_raw_private_key(&raw_private_buffer, EVP_PKEY_PQDSA).unwrap();
147 assert_eq!(1, unsafe {
148 EVP_PKEY_cmp(key_private.as_const_ptr(), key_private2.as_const_ptr())
149 });
150 }
151
152 fn test_signing_for(evp_pkey: &LcPtr<EVP_PKEY>, id: &AlgorithmID) {
153 let message = b"hello world";
154 let signature = evp_pkey
155 .sign(message, None, No_EVP_PKEY_CTX_consumer)
156 .unwrap();
157 println!("signature size: {}", signature.len());
158 assert_eq!(signature.len(), evp_pkey.as_const().signature_size_bytes());
159 assert_eq!(signature.len(), id.signature_size_bytes());
160 evp_pkey
161 .verify(message, None, No_EVP_PKEY_CTX_consumer, &signature)
162 .unwrap();
163 println!("verified: {signature:?}");
164 }
165}