1#![cfg_attr(not(feature = "std"), no_std)]
79#![deny(unsafe_code)]
80#![deny(unused_qualifications)]
81
82#[cfg(feature = "alloc")]
83extern crate alloc;
84
85pub use lib_q_core::{
87 Algorithm,
88 AlgorithmCategory,
89 Error,
90 Kem,
91 KemContext,
92 KemKeypair,
93 KemOperations,
94 KemPublicKey,
95 KemSecretKey,
96 Result,
97};
98
99pub mod provider;
101
102#[cfg(feature = "ml-kem")]
104pub mod ml_kem;
105
106#[cfg(feature = "hqc")]
107pub mod hqc;
108
109#[cfg(feature = "alloc")]
111pub use provider::LibQKemProvider;
112
113#[cfg(feature = "std")]
115pub fn available_algorithms() -> Vec<&'static str> {
116 let mut algorithms = Vec::new();
117
118 #[cfg(feature = "ml-kem")]
119 {
120 algorithms.extend(["ML-KEM-512", "ML-KEM-768", "ML-KEM-1024"]);
121 }
122
123 #[cfg(feature = "cb-kem")]
124 {
125 algorithms.extend([
126 "CB-KEM-348864",
127 "CB-KEM-460896",
128 "CB-KEM-6688128",
129 "CB-KEM-6960119",
130 "CB-KEM-8192128",
131 ]);
132 }
133
134 #[cfg(feature = "hqc")]
135 {
136 algorithms.extend(["HQC-128", "HQC-192", "HQC-256"]);
137 }
138
139 algorithms
140}
141
142#[cfg(not(feature = "std"))]
144pub fn available_algorithms() -> &'static [&'static str] {
145 &[
146 #[cfg(feature = "ml-kem")]
147 "ML-KEM-512",
148 #[cfg(feature = "ml-kem")]
149 "ML-KEM-768",
150 #[cfg(feature = "ml-kem")]
151 "ML-KEM-1024",
152 #[cfg(feature = "cb-kem")]
153 "CB-KEM-348864",
154 #[cfg(feature = "cb-kem")]
155 "CB-KEM-460896",
156 #[cfg(feature = "cb-kem")]
157 "CB-KEM-6688128",
158 #[cfg(feature = "cb-kem")]
159 "CB-KEM-6960119",
160 #[cfg(feature = "cb-kem")]
161 "CB-KEM-8192128",
162 #[cfg(feature = "hqc")]
163 "HQC-128",
164 #[cfg(feature = "hqc")]
165 "HQC-192",
166 #[cfg(feature = "hqc")]
167 "HQC-256",
168 ]
169}
170
171#[cfg(feature = "std")]
173pub fn create_kem(algorithm: &str) -> Result<Box<dyn Kem>> {
174 match algorithm {
175 #[cfg(feature = "ml-kem")]
176 "ml-kem-512" | "ML-KEM-512" => Ok(Box::new(ml_kem::MlKem512Impl::default())),
177 #[cfg(feature = "ml-kem")]
178 "ml-kem-768" | "ML-KEM-768" => Ok(Box::new(ml_kem::MlKem768Impl::default())),
179 #[cfg(feature = "ml-kem")]
180 "ml-kem-1024" | "ML-KEM-1024" => Ok(Box::new(ml_kem::MlKem1024Impl::default())),
181
182 #[cfg(feature = "hqc")]
183 "HQC-128" | "hqc-128" => Ok(Box::new(hqc::Hqc128Impl)),
184 #[cfg(feature = "hqc")]
185 "HQC-192" | "hqc-192" => Ok(Box::new(hqc::Hqc192Impl)),
186 #[cfg(feature = "hqc")]
187 "HQC-256" | "hqc-256" => Ok(Box::new(hqc::Hqc256Impl)),
188
189 _ => Err(Error::InvalidAlgorithm {
190 algorithm: "Unknown algorithm",
191 }),
192 }
193}
194
195pub fn create_kem_context(algorithm: Algorithm) -> Result<KemContext> {
197 if algorithm.category() != AlgorithmCategory::Kem {
199 return Err(Error::InvalidAlgorithm {
200 algorithm: "Algorithm is not a KEM algorithm",
201 });
202 }
203
204 Ok(KemContext::new())
205}
206
207#[cfg(feature = "wasm")]
209pub mod wasm {
210 use alloc::string::ToString;
211 use alloc::vec::Vec;
212
213 use lib_q_core::{
214 Algorithm,
215 KemKeypair,
216 KemPublicKey,
217 KemSecretKey,
218 };
219 #[allow(unused_imports)]
220 use wasm_bindgen::{
221 JsError,
222 prelude::*,
223 };
224
225 use super::*;
226
227 #[wasm_bindgen]
229 pub fn generate_keypair(algorithm: Algorithm) -> core::result::Result<KemKeypair, JsError> {
230 let provider = LibQKemProvider::new().map_err(|e| JsError::new(&e.to_string()))?;
231 provider
232 .generate_keypair(algorithm, None)
233 .map_err(|e| JsError::new(&e.to_string()))
234 }
235
236 #[wasm_bindgen]
238 pub fn encapsulate(
239 algorithm: Algorithm,
240 public_key: &KemPublicKey,
241 ) -> core::result::Result<EncapsulationResult, JsError> {
242 let provider = LibQKemProvider::new().map_err(|e| JsError::new(&e.to_string()))?;
243 let (ciphertext, shared_secret) = provider
244 .encapsulate(algorithm, public_key, None)
245 .map_err(|e| JsError::new(&e.to_string()))?;
246 Ok(EncapsulationResult::new(ciphertext, shared_secret))
247 }
248
249 #[wasm_bindgen]
251 pub fn decapsulate(
252 algorithm: Algorithm,
253 secret_key: &KemSecretKey,
254 ciphertext: &[u8],
255 ) -> core::result::Result<Vec<u8>, JsError> {
256 let provider = LibQKemProvider::new().map_err(|e| JsError::new(&e.to_string()))?;
257 provider
258 .decapsulate(algorithm, secret_key, ciphertext)
259 .map_err(|e| JsError::new(&e.to_string()))
260 }
261
262 #[wasm_bindgen]
264 pub struct EncapsulationResult {
265 ciphertext: Vec<u8>,
266 shared_secret: Vec<u8>,
267 }
268
269 #[wasm_bindgen]
270 impl EncapsulationResult {
271 #[wasm_bindgen(constructor)]
272 pub fn new(ciphertext: Vec<u8>, shared_secret: Vec<u8>) -> Self {
273 Self {
274 ciphertext,
275 shared_secret,
276 }
277 }
278
279 #[wasm_bindgen(getter)]
280 pub fn ciphertext(&self) -> Vec<u8> {
281 self.ciphertext.clone()
282 }
283
284 #[wasm_bindgen(getter)]
285 pub fn shared_secret(&self) -> Vec<u8> {
286 self.shared_secret.clone()
287 }
288 }
289}
290
291#[cfg(all(test, feature = "alloc"))]
292mod tests {
293 use super::*;
294
295 #[test]
296 fn test_available_algorithms() {
297 let algorithms = available_algorithms();
298
299 #[cfg(feature = "ml-kem")]
301 {
302 assert!(
303 algorithms.contains(&"ML-KEM-512"),
304 "ML-KEM 512 should be available when ml-kem feature is enabled"
305 );
306 assert!(
307 algorithms.contains(&"ML-KEM-768"),
308 "ML-KEM 768 should be available when ml-kem feature is enabled"
309 );
310 assert!(
311 algorithms.contains(&"ML-KEM-1024"),
312 "ML-KEM 1024 should be available when ml-kem feature is enabled"
313 );
314 }
315
316 #[cfg(feature = "cb-kem")]
317 {
318 assert!(
319 algorithms.contains(&"CB-KEM-348864"),
320 "CB-KEM-348864 should be available when cb-kem feature is enabled"
321 );
322 assert!(
323 algorithms.contains(&"CB-KEM-460896"),
324 "CB-KEM-460896 should be available when cb-kem feature is enabled"
325 );
326 assert!(
327 algorithms.contains(&"CB-KEM-6688128"),
328 "CB-KEM-6688128 should be available when cb-kem feature is enabled"
329 );
330 assert!(
331 algorithms.contains(&"CB-KEM-6960119"),
332 "CB-KEM-6960119 should be available when cb-kem feature is enabled"
333 );
334 assert!(
335 algorithms.contains(&"CB-KEM-8192128"),
336 "CB-KEM-8192128 should be available when cb-kem feature is enabled"
337 );
338 }
339
340 #[cfg(feature = "hqc")]
341 {
342 assert!(
343 algorithms.contains(&"HQC-128"),
344 "HQC-128 should be available when hqc feature is enabled"
345 );
346 assert!(
347 algorithms.contains(&"HQC-192"),
348 "HQC-192 should be available when hqc feature is enabled"
349 );
350 assert!(
351 algorithms.contains(&"HQC-256"),
352 "HQC-256 should be available when hqc feature is enabled"
353 );
354 }
355
356 #[cfg(any(feature = "ml-kem", feature = "cb-kem", feature = "hqc"))]
358 assert!(
359 !algorithms.is_empty(),
360 "Should have at least one algorithm when features are enabled"
361 );
362
363 #[cfg(not(any(feature = "ml-kem", feature = "cb-kem", feature = "hqc")))]
365 assert!(
366 algorithms.is_empty(),
367 "Should have no algorithms when no features are enabled"
368 );
369
370 let expected_count = {
372 let count = 0;
373 #[cfg(feature = "ml-kem")]
374 let count = count + 3; #[cfg(feature = "cb-kem")]
376 let count = count + 5; #[cfg(feature = "hqc")]
378 let count = count + 3; count
380 };
381
382 assert_eq!(
383 algorithms.len(),
384 expected_count,
385 "Algorithm count should match expected count based on enabled features"
386 );
387 }
388
389 #[test]
390 fn test_create_kem_context() {
391 let result = create_kem_context(Algorithm::MlKem512);
393 assert!(
394 result.is_ok(),
395 "Context creation should succeed for valid KEM algorithm"
396 );
397
398 let mut ctx = result.unwrap();
401
402 let keypair_result = ctx.generate_keypair(Algorithm::MlKem512, None);
404 assert!(
405 keypair_result.is_err(),
406 "Keypair generation should fail without provider"
407 );
408 if let Err(err) = keypair_result {
409 assert!(matches!(err, Error::ProviderNotConfigured { .. }));
410 }
411 }
412
413 #[test]
414 fn test_create_kem_context_invalid_algorithm() {
415 let result = create_kem_context(Algorithm::MlDsa65);
416 assert!(result.is_err());
417 }
418
419 #[test]
420 fn test_provider_creation() {
421 let provider = LibQKemProvider::new();
422 assert!(provider.is_ok(), "Provider should be created successfully");
423 }
424
425 #[test]
426 fn test_provider_algorithm_support() {
427 let provider = LibQKemProvider::new().unwrap();
428
429 #[cfg(feature = "ml-kem")]
431 {
432 let result = provider.generate_keypair(Algorithm::MlKem512, None);
433 match result {
435 Ok(_) => {
436 }
438 Err(Error::NotImplemented { .. }) => {
439 }
441 Err(Error::RandomGenerationFailed { .. }) => {
442 }
444 Err(e) => {
445 panic!("Unexpected error type: {:?}", e);
446 }
447 }
448 }
449
450 let result = provider.generate_keypair(Algorithm::Sha3_256, None);
452 assert!(result.is_err());
453 if let Err(Error::InvalidAlgorithm { .. }) = result {
454 } else {
456 panic!("Expected InvalidAlgorithm error for non-KEM algorithm");
457 }
458 }
459
460 #[test]
461 fn test_algorithm_naming_consistency() {
462 let algorithms = available_algorithms();
463
464 for algorithm in algorithms {
466 assert!(
467 algorithm.starts_with("ML-KEM-") ||
468 algorithm.starts_with("CB-KEM-") ||
469 algorithm.starts_with("HQC-"),
470 "Algorithm name '{}' should follow NIST naming conventions",
471 algorithm
472 );
473 }
474 }
475
476 #[cfg(feature = "std")]
477 #[test]
478 fn test_legacy_compatibility() {
479 #[cfg(feature = "ml-kem")]
480 {
481 let result = create_kem("ml-kem-512");
483 assert!(result.is_ok(), "Legacy 'ml-kem-512' name should work");
484
485 let result = create_kem("ML-KEM-512");
486 assert!(result.is_ok(), "NIST 'ML-KEM-512' name should work");
487 }
488 }
489}