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