1mod encoding;
73mod error;
74mod types;
75
76pub use error::{Error, Result};
78pub use types::{
79 Algorithm, AlgorithmFamily, AlgorithmInfo, AlgorithmSizes, AlgorithmType, HashFunction, MlDsa,
80 MlKem, SecurityLevel, SlhDsa, SlhDsaMode,
81};
82
83pub use encoding::{decode_oid, encode_oid, encode_oid_to};
85
86pub mod oid {
91 pub const ML_KEM_512: &str = "2.16.840.1.101.3.4.4.1";
93 pub const ML_KEM_768: &str = "2.16.840.1.101.3.4.4.2";
94 pub const ML_KEM_1024: &str = "2.16.840.1.101.3.4.4.3";
95
96 pub const ML_KEM_512_BYTES: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x04, 0x01];
98 pub const ML_KEM_768_BYTES: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x04, 0x02];
99 pub const ML_KEM_1024_BYTES: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x04, 0x03];
100
101 pub const ML_DSA_44: &str = "2.16.840.1.101.3.4.3.17";
103 pub const ML_DSA_65: &str = "2.16.840.1.101.3.4.3.18";
104 pub const ML_DSA_87: &str = "2.16.840.1.101.3.4.3.19";
105
106 pub const ML_DSA_44_BYTES: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x11];
108 pub const ML_DSA_65_BYTES: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x12];
109 pub const ML_DSA_87_BYTES: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x13];
110
111 pub const SLH_DSA_SHA2_128S: &str = "2.16.840.1.101.3.4.3.20";
113 pub const SLH_DSA_SHA2_128F: &str = "2.16.840.1.101.3.4.3.21";
114 pub const SLH_DSA_SHA2_192S: &str = "2.16.840.1.101.3.4.3.22";
115 pub const SLH_DSA_SHA2_192F: &str = "2.16.840.1.101.3.4.3.23";
116 pub const SLH_DSA_SHA2_256S: &str = "2.16.840.1.101.3.4.3.24";
117 pub const SLH_DSA_SHA2_256F: &str = "2.16.840.1.101.3.4.3.25";
118
119 pub const SLH_DSA_SHA2_128S_BYTES: &[u8] =
121 &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x14];
122 pub const SLH_DSA_SHA2_128F_BYTES: &[u8] =
123 &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x15];
124 pub const SLH_DSA_SHA2_192S_BYTES: &[u8] =
125 &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x16];
126 pub const SLH_DSA_SHA2_192F_BYTES: &[u8] =
127 &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x17];
128 pub const SLH_DSA_SHA2_256S_BYTES: &[u8] =
129 &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x18];
130 pub const SLH_DSA_SHA2_256F_BYTES: &[u8] =
131 &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x19];
132
133 pub const SLH_DSA_SHAKE_128S: &str = "2.16.840.1.101.3.4.3.26";
135 pub const SLH_DSA_SHAKE_128F: &str = "2.16.840.1.101.3.4.3.27";
136 pub const SLH_DSA_SHAKE_192S: &str = "2.16.840.1.101.3.4.3.28";
137 pub const SLH_DSA_SHAKE_192F: &str = "2.16.840.1.101.3.4.3.29";
138 pub const SLH_DSA_SHAKE_256S: &str = "2.16.840.1.101.3.4.3.30";
139 pub const SLH_DSA_SHAKE_256F: &str = "2.16.840.1.101.3.4.3.31";
140
141 pub const SLH_DSA_SHAKE_128S_BYTES: &[u8] =
143 &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x1a];
144 pub const SLH_DSA_SHAKE_128F_BYTES: &[u8] =
145 &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x1b];
146 pub const SLH_DSA_SHAKE_192S_BYTES: &[u8] =
147 &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x1c];
148 pub const SLH_DSA_SHAKE_192F_BYTES: &[u8] =
149 &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x1d];
150 pub const SLH_DSA_SHAKE_256S_BYTES: &[u8] =
151 &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x1e];
152 pub const SLH_DSA_SHAKE_256F_BYTES: &[u8] =
153 &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x1f];
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159 use std::str::FromStr;
160
161 #[test]
162 fn test_ml_kem_ergonomics() {
163 let alg: MlKem = "ML-KEM-512".parse().unwrap();
165 assert_eq!(alg, MlKem::Kem512);
166
167 let alg: MlKem = "ML-KEM-768".try_into().unwrap();
169 assert_eq!(alg, MlKem::Kem768);
170
171 assert_eq!(MlKem::Kem512.oid(), oid::ML_KEM_512);
173 assert_eq!(MlKem::Kem512.public_key_size(), 800);
174 assert_eq!(MlKem::Kem512.ciphertext_size(), 768);
175
176 assert_eq!(MlKem::Kem512.to_string(), "ML-KEM-512");
178 assert_eq!(MlKem::Kem512.as_ref(), "ML-KEM-512");
179 }
180
181 #[test]
182 fn test_ml_dsa_ergonomics() {
183 let alg: MlDsa = "ML-DSA-65".parse().unwrap();
184 assert_eq!(alg.oid(), oid::ML_DSA_65);
185 assert_eq!(alg.jose(), "ML-DSA-65");
186 assert_eq!(alg.cose(), -49);
187 assert_eq!(alg.signature_size(), 3309);
188 }
189
190 #[test]
191 fn test_ml_dsa_jose_cose_roundtrip() {
192 for dsa in MlDsa::ALL {
193 let jose = dsa.jose();
195 let recovered = MlDsa::from_jose(jose).unwrap();
196 assert_eq!(*dsa, recovered);
197
198 let cose = dsa.cose();
200 let recovered = MlDsa::from_cose(cose).unwrap();
201 assert_eq!(*dsa, recovered);
202 }
203 }
204
205 #[test]
206 fn test_slh_dsa_properties() {
207 let alg: SlhDsa = "SLH-DSA-SHA2-128s".parse().unwrap();
208 assert_eq!(alg.hash_function(), HashFunction::Sha2);
209 assert_eq!(alg.mode(), SlhDsaMode::Small);
210 assert_eq!(alg.security_level(), SecurityLevel::Level1);
211
212 let alg: SlhDsa = "SLH-DSA-SHAKE-256f".parse().unwrap();
213 assert_eq!(alg.hash_function(), HashFunction::Shake);
214 assert_eq!(alg.mode(), SlhDsaMode::Fast);
215 assert_eq!(alg.security_level(), SecurityLevel::Level5);
216 }
217
218 #[test]
219 fn test_algorithm_unified() {
220 let alg: Algorithm = "ML-KEM-512".parse().unwrap();
222 assert_eq!(alg.family(), AlgorithmFamily::MlKem);
223 assert_eq!(alg.algorithm_type(), AlgorithmType::Kem);
224
225 let alg: Algorithm = "ML-DSA-44".parse().unwrap();
226 assert_eq!(alg.family(), AlgorithmFamily::MlDsa);
227 assert_eq!(alg.algorithm_type(), AlgorithmType::Sign);
228
229 let alg: Algorithm = MlKem::Kem768.into();
231 assert_eq!(alg.oid(), oid::ML_KEM_768);
232
233 assert!(alg.as_ml_kem().is_some());
235 assert!(alg.as_ml_dsa().is_none());
236 }
237
238 #[test]
239 fn test_algorithm_iteration() {
240 assert_eq!(Algorithm::all().count(), 18);
241 assert_eq!(Algorithm::kems().count(), 3);
242 assert_eq!(Algorithm::signatures().count(), 15);
243 }
244
245 #[test]
246 fn test_from_oid() {
247 let alg = MlKem::from_oid("2.16.840.1.101.3.4.4.1").unwrap();
248 assert_eq!(alg, MlKem::Kem512);
249
250 let alg = Algorithm::from_oid("2.16.840.1.101.3.4.3.17").unwrap();
251 assert_eq!(alg, Algorithm::MlDsa(MlDsa::Dsa44));
252 }
253
254 #[test]
255 fn test_oid_bytes_roundtrip() {
256 for alg in Algorithm::all() {
257 let oid = alg.oid();
258 let bytes = encode_oid(oid).unwrap();
259 let decoded = decode_oid(&bytes).unwrap();
260 assert_eq!(oid, decoded);
261 }
262 }
263
264 #[test]
265 fn test_algorithm_info() {
266 let info = MlKem::Kem512.info();
267 assert_eq!(info.name, "ML-KEM-512");
268 assert_eq!(info.oid, oid::ML_KEM_512);
269 assert_eq!(info.algorithm_type, AlgorithmType::Kem);
270 assert_eq!(info.family, AlgorithmFamily::MlKem);
271 assert_eq!(info.public_key_size, 800);
272 assert_eq!(
273 info.sizes,
274 AlgorithmSizes::Kem {
275 ciphertext_size: 768,
276 shared_secret_size: 32
277 }
278 );
279 }
280
281 #[test]
282 fn test_error_handling() {
283 assert!(MlKem::from_str("invalid").is_err());
284 assert!(MlDsa::from_jose("invalid").is_err());
285 assert!(MlDsa::from_cose(-100).is_none());
286 assert!(Algorithm::from_oid("1.2.3.4").is_err());
287 }
288
289 #[test]
290 fn test_oid_bytes_constants() {
291 assert_eq!(encode_oid(oid::ML_KEM_512).unwrap(), oid::ML_KEM_512_BYTES);
295 assert_eq!(encode_oid(oid::ML_KEM_768).unwrap(), oid::ML_KEM_768_BYTES);
296 assert_eq!(
297 encode_oid(oid::ML_KEM_1024).unwrap(),
298 oid::ML_KEM_1024_BYTES
299 );
300
301 assert_eq!(encode_oid(oid::ML_DSA_44).unwrap(), oid::ML_DSA_44_BYTES);
303 assert_eq!(encode_oid(oid::ML_DSA_65).unwrap(), oid::ML_DSA_65_BYTES);
304 assert_eq!(encode_oid(oid::ML_DSA_87).unwrap(), oid::ML_DSA_87_BYTES);
305
306 assert_eq!(
308 encode_oid(oid::SLH_DSA_SHA2_128S).unwrap(),
309 oid::SLH_DSA_SHA2_128S_BYTES
310 );
311 assert_eq!(
312 encode_oid(oid::SLH_DSA_SHA2_128F).unwrap(),
313 oid::SLH_DSA_SHA2_128F_BYTES
314 );
315 assert_eq!(
316 encode_oid(oid::SLH_DSA_SHA2_192S).unwrap(),
317 oid::SLH_DSA_SHA2_192S_BYTES
318 );
319 assert_eq!(
320 encode_oid(oid::SLH_DSA_SHA2_192F).unwrap(),
321 oid::SLH_DSA_SHA2_192F_BYTES
322 );
323 assert_eq!(
324 encode_oid(oid::SLH_DSA_SHA2_256S).unwrap(),
325 oid::SLH_DSA_SHA2_256S_BYTES
326 );
327 assert_eq!(
328 encode_oid(oid::SLH_DSA_SHA2_256F).unwrap(),
329 oid::SLH_DSA_SHA2_256F_BYTES
330 );
331
332 assert_eq!(
334 encode_oid(oid::SLH_DSA_SHAKE_128S).unwrap(),
335 oid::SLH_DSA_SHAKE_128S_BYTES
336 );
337 assert_eq!(
338 encode_oid(oid::SLH_DSA_SHAKE_128F).unwrap(),
339 oid::SLH_DSA_SHAKE_128F_BYTES
340 );
341 assert_eq!(
342 encode_oid(oid::SLH_DSA_SHAKE_192S).unwrap(),
343 oid::SLH_DSA_SHAKE_192S_BYTES
344 );
345 assert_eq!(
346 encode_oid(oid::SLH_DSA_SHAKE_192F).unwrap(),
347 oid::SLH_DSA_SHAKE_192F_BYTES
348 );
349 assert_eq!(
350 encode_oid(oid::SLH_DSA_SHAKE_256S).unwrap(),
351 oid::SLH_DSA_SHAKE_256S_BYTES
352 );
353 assert_eq!(
354 encode_oid(oid::SLH_DSA_SHAKE_256F).unwrap(),
355 oid::SLH_DSA_SHAKE_256F_BYTES
356 );
357 }
358}