1#[cfg(feature = "alloc")]
38extern crate alloc;
39#[cfg(all(
40 feature = "alloc",
41 not(feature = "std"),
42 any(
43 not(feature = "ml-dsa"),
44 not(feature = "fn-dsa"),
45 not(feature = "slh-dsa"),
46 ),
47))]
48use alloc::string::ToString;
49#[cfg(feature = "alloc")]
50use alloc::vec::Vec;
51
52#[cfg(feature = "alloc")]
53use lib_q_core::api::{
54 Algorithm,
55 CryptoProvider,
56 SignatureOperations,
57};
58#[cfg(feature = "alloc")]
59use lib_q_core::error::{
60 Error,
61 Result,
62};
63#[cfg(feature = "alloc")]
64use lib_q_core::security::SecurityValidator;
65#[cfg(all(feature = "alloc", any(feature = "ml-dsa", feature = "fn-dsa")))]
66use lib_q_core::traits::Signature;
67#[cfg(feature = "alloc")]
68use lib_q_core::traits::{
69 SigKeypair,
70 SigPublicKey,
71 SigSecretKey,
72};
73
74#[cfg(feature = "fn-dsa")]
75use crate::fn_dsa::{
76 FnDsa,
77 FnDsa512,
78 FnDsa1024,
79};
80#[cfg(feature = "ml-dsa")]
82use crate::ml_dsa::MlDsa;
83#[cfg(feature = "slh-dsa")]
84use crate::slh_dsa::SlhDsa;
85
86#[cfg(feature = "alloc")]
91#[derive(Clone)]
92pub struct LibQSignatureProvider {
93 security_validator: SecurityValidator,
94}
95
96#[cfg(feature = "alloc")]
97impl LibQSignatureProvider {
98 pub fn new() -> Result<Self> {
108 Ok(Self {
109 security_validator: SecurityValidator::new()?,
110 })
111 }
112
113 pub fn security_validator(&self) -> &SecurityValidator {
115 &self.security_validator
116 }
117}
118
119#[cfg(feature = "alloc")]
120impl SignatureOperations for LibQSignatureProvider {
121 fn generate_keypair(
122 &self,
123 algorithm: Algorithm,
124 randomness: Option<&[u8]>,
125 ) -> Result<SigKeypair> {
126 self.security_validator.validate_algorithm_category(
128 algorithm,
129 lib_q_core::api::AlgorithmCategory::Signature,
130 )?;
131
132 if let Some(rng) = randomness {
134 self.security_validator.validate_randomness(rng)?;
135 }
136
137 match algorithm {
139 #[cfg(feature = "ml-dsa")]
141 Algorithm::MlDsa44 => {
142 let ml_dsa = MlDsa::ml_dsa_44();
143 if let Some(rng) = randomness {
144 let rng_array: [u8; 32] =
146 rng.try_into().map_err(|_| Error::InvalidKeySize {
147 expected: 32,
148 actual: rng.len(),
149 })?;
150 ml_dsa.generate_keypair_with_randomness(rng_array)
151 } else {
152 ml_dsa.generate_keypair()
153 }
154 }
155 #[cfg(feature = "ml-dsa")]
156 Algorithm::MlDsa65 => {
157 let ml_dsa = MlDsa::ml_dsa_65();
158 if let Some(rng) = randomness {
159 let rng_array: [u8; 32] =
160 rng.try_into().map_err(|_| Error::InvalidKeySize {
161 expected: 32,
162 actual: rng.len(),
163 })?;
164 ml_dsa.generate_keypair_with_randomness(rng_array)
165 } else {
166 ml_dsa.generate_keypair()
167 }
168 }
169 #[cfg(feature = "ml-dsa")]
170 Algorithm::MlDsa87 => {
171 let ml_dsa = MlDsa::ml_dsa_87();
172 if let Some(rng) = randomness {
173 let rng_array: [u8; 32] =
174 rng.try_into().map_err(|_| Error::InvalidKeySize {
175 expected: 32,
176 actual: rng.len(),
177 })?;
178 ml_dsa.generate_keypair_with_randomness(rng_array)
179 } else {
180 ml_dsa.generate_keypair()
181 }
182 }
183
184 #[cfg(feature = "fn-dsa")]
186 Algorithm::FnDsa => {
187 let fn_dsa = FnDsa::level1();
188 fn_dsa.generate_keypair()
189 }
190 #[cfg(feature = "fn-dsa")]
191 Algorithm::FnDsa512 => {
192 let fn_dsa = FnDsa512::new();
193 fn_dsa.generate_keypair()
194 }
195 #[cfg(feature = "fn-dsa")]
196 Algorithm::FnDsa1024 => {
197 let fn_dsa = FnDsa1024::new();
198 fn_dsa.generate_keypair()
199 }
200
201 #[cfg(feature = "slh-dsa")]
203 Algorithm::SlhDsaSha256128fRobust => {
204 let slh_dsa = SlhDsa::new();
205 slh_dsa.generate_keypair_for_algorithm(algorithm, randomness)
206 }
207 #[cfg(feature = "slh-dsa")]
208 Algorithm::SlhDsaSha256192fRobust => {
209 let slh_dsa = SlhDsa::new();
210 slh_dsa.generate_keypair_for_algorithm(algorithm, randomness)
211 }
212 #[cfg(feature = "slh-dsa")]
213 Algorithm::SlhDsaSha256256fRobust => {
214 let slh_dsa = SlhDsa::new();
215 slh_dsa.generate_keypair_for_algorithm(algorithm, randomness)
216 }
217 #[cfg(feature = "slh-dsa")]
218 Algorithm::SlhDsaShake256128fRobust => {
219 let slh_dsa = SlhDsa::new();
220 slh_dsa.generate_keypair_for_algorithm(algorithm, randomness)
221 }
222 #[cfg(feature = "slh-dsa")]
223 Algorithm::SlhDsaShake256192fRobust => {
224 let slh_dsa = SlhDsa::new();
225 slh_dsa.generate_keypair_for_algorithm(algorithm, randomness)
226 }
227 #[cfg(feature = "slh-dsa")]
228 Algorithm::SlhDsaShake256256fRobust => {
229 let slh_dsa = SlhDsa::new();
230 slh_dsa.generate_keypair_for_algorithm(algorithm, randomness)
231 }
232
233 #[cfg(not(feature = "ml-dsa"))]
235 Algorithm::MlDsa44 | Algorithm::MlDsa65 | Algorithm::MlDsa87 => {
236 Err(Error::NotImplemented {
237 feature: "ML-DSA implementations require 'ml-dsa' feature flag".to_string(),
238 })
239 }
240 #[cfg(not(feature = "fn-dsa"))]
241 Algorithm::FnDsa | Algorithm::FnDsa512 | Algorithm::FnDsa1024 => {
242 Err(Error::NotImplemented {
243 feature: "FN-DSA implementations require 'fn-dsa' feature flag".to_string(),
244 })
245 }
246 #[cfg(not(feature = "slh-dsa"))]
247 Algorithm::SlhDsaSha256128fRobust |
248 Algorithm::SlhDsaSha256192fRobust |
249 Algorithm::SlhDsaSha256256fRobust |
250 Algorithm::SlhDsaShake256128fRobust |
251 Algorithm::SlhDsaShake256192fRobust |
252 Algorithm::SlhDsaShake256256fRobust => Err(Error::NotImplemented {
253 feature: "SLH-DSA implementations require 'slh-dsa' feature flag".to_string(),
254 }),
255
256 _ => Err(Error::InvalidAlgorithm {
257 algorithm: "Algorithm not supported for signature operations",
258 }),
259 }
260 }
261
262 fn sign(
263 &self,
264 algorithm: Algorithm,
265 secret_key: &SigSecretKey,
266 message: &[u8],
267 randomness: Option<&[u8]>,
268 ) -> Result<Vec<u8>> {
269 self.security_validator.validate_algorithm_category(
271 algorithm,
272 lib_q_core::api::AlgorithmCategory::Signature,
273 )?;
274
275 self.security_validator
277 .validate_secret_key(algorithm, secret_key.as_bytes())?;
278
279 self.security_validator
281 .validate_signature_message(message)?;
282
283 if let Some(rng) = randomness {
285 self.security_validator.validate_randomness(rng)?;
286 }
287
288 match algorithm {
290 #[cfg(feature = "ml-dsa")]
292 Algorithm::MlDsa44 => {
293 let ml_dsa = MlDsa::ml_dsa_44();
294 if let Some(rng) = randomness {
295 let rng_array: [u8; 32] =
296 rng.try_into().map_err(|_| Error::InvalidKeySize {
297 expected: 32,
298 actual: rng.len(),
299 })?;
300 ml_dsa.sign_with_randomness(secret_key, message, rng_array)
301 } else {
302 ml_dsa.sign(secret_key, message)
303 }
304 }
305 #[cfg(feature = "ml-dsa")]
306 Algorithm::MlDsa65 => {
307 let ml_dsa = MlDsa::ml_dsa_65();
308 if let Some(rng) = randomness {
309 let rng_array: [u8; 32] =
310 rng.try_into().map_err(|_| Error::InvalidKeySize {
311 expected: 32,
312 actual: rng.len(),
313 })?;
314 ml_dsa.sign_with_randomness(secret_key, message, rng_array)
315 } else {
316 ml_dsa.sign(secret_key, message)
317 }
318 }
319 #[cfg(feature = "ml-dsa")]
320 Algorithm::MlDsa87 => {
321 let ml_dsa = MlDsa::ml_dsa_87();
322 if let Some(rng) = randomness {
323 let rng_array: [u8; 32] =
324 rng.try_into().map_err(|_| Error::InvalidKeySize {
325 expected: 32,
326 actual: rng.len(),
327 })?;
328 ml_dsa.sign_with_randomness(secret_key, message, rng_array)
329 } else {
330 ml_dsa.sign(secret_key, message)
331 }
332 }
333
334 #[cfg(feature = "fn-dsa")]
336 Algorithm::FnDsa => {
337 let fn_dsa = FnDsa::level1();
338 fn_dsa.sign(secret_key, message)
339 }
340 #[cfg(feature = "fn-dsa")]
341 Algorithm::FnDsa512 => {
342 let fn_dsa = FnDsa512::new();
343 fn_dsa.sign(secret_key, message)
344 }
345 #[cfg(feature = "fn-dsa")]
346 Algorithm::FnDsa1024 => {
347 let fn_dsa = FnDsa1024::new();
348 fn_dsa.sign(secret_key, message)
349 }
350
351 #[cfg(feature = "slh-dsa")]
353 Algorithm::SlhDsaSha256128fRobust |
354 Algorithm::SlhDsaSha256192fRobust |
355 Algorithm::SlhDsaSha256256fRobust |
356 Algorithm::SlhDsaShake256128fRobust |
357 Algorithm::SlhDsaShake256192fRobust |
358 Algorithm::SlhDsaShake256256fRobust => {
359 let slh_dsa = SlhDsa::new();
360 slh_dsa.sign_for_algorithm(algorithm, secret_key, message, randomness)
361 }
362
363 #[cfg(not(feature = "ml-dsa"))]
365 Algorithm::MlDsa44 | Algorithm::MlDsa65 | Algorithm::MlDsa87 => {
366 Err(Error::NotImplemented {
367 feature: "ML-DSA implementations require 'ml-dsa' feature flag".to_string(),
368 })
369 }
370 #[cfg(not(feature = "fn-dsa"))]
371 Algorithm::FnDsa | Algorithm::FnDsa512 | Algorithm::FnDsa1024 => {
372 Err(Error::NotImplemented {
373 feature: "FN-DSA implementations require 'fn-dsa' feature flag".to_string(),
374 })
375 }
376 #[cfg(not(feature = "slh-dsa"))]
377 Algorithm::SlhDsaSha256128fRobust |
378 Algorithm::SlhDsaSha256192fRobust |
379 Algorithm::SlhDsaSha256256fRobust |
380 Algorithm::SlhDsaShake256128fRobust |
381 Algorithm::SlhDsaShake256192fRobust |
382 Algorithm::SlhDsaShake256256fRobust => Err(Error::NotImplemented {
383 feature: "SLH-DSA implementations require 'slh-dsa' feature flag".to_string(),
384 }),
385
386 _ => Err(Error::InvalidAlgorithm {
387 algorithm: "Algorithm not supported for signature operations",
388 }),
389 }
390 }
391
392 fn verify(
393 &self,
394 algorithm: Algorithm,
395 public_key: &SigPublicKey,
396 message: &[u8],
397 signature: &[u8],
398 ) -> Result<bool> {
399 self.security_validator.validate_algorithm_category(
401 algorithm,
402 lib_q_core::api::AlgorithmCategory::Signature,
403 )?;
404
405 self.security_validator
407 .validate_public_key(algorithm, public_key.as_bytes())?;
408
409 self.security_validator
411 .validate_signature_message(message)?;
412
413 self.security_validator
415 .validate_signature(algorithm, signature)?;
416
417 match algorithm {
419 #[cfg(feature = "ml-dsa")]
421 Algorithm::MlDsa44 => {
422 let ml_dsa = MlDsa::ml_dsa_44();
423 ml_dsa.verify(public_key, message, signature)
424 }
425 #[cfg(feature = "ml-dsa")]
426 Algorithm::MlDsa65 => {
427 let ml_dsa = MlDsa::ml_dsa_65();
428 ml_dsa.verify(public_key, message, signature)
429 }
430 #[cfg(feature = "ml-dsa")]
431 Algorithm::MlDsa87 => {
432 let ml_dsa = MlDsa::ml_dsa_87();
433 ml_dsa.verify(public_key, message, signature)
434 }
435
436 #[cfg(feature = "fn-dsa")]
438 Algorithm::FnDsa => {
439 let fn_dsa = FnDsa::level1();
440 fn_dsa.verify(public_key, message, signature)
441 }
442 #[cfg(feature = "fn-dsa")]
443 Algorithm::FnDsa512 => {
444 let fn_dsa = FnDsa512::new();
445 fn_dsa.verify(public_key, message, signature)
446 }
447 #[cfg(feature = "fn-dsa")]
448 Algorithm::FnDsa1024 => {
449 let fn_dsa = FnDsa1024::new();
450 fn_dsa.verify(public_key, message, signature)
451 }
452
453 #[cfg(feature = "slh-dsa")]
455 Algorithm::SlhDsaSha256128fRobust |
456 Algorithm::SlhDsaSha256192fRobust |
457 Algorithm::SlhDsaSha256256fRobust |
458 Algorithm::SlhDsaShake256128fRobust |
459 Algorithm::SlhDsaShake256192fRobust |
460 Algorithm::SlhDsaShake256256fRobust => {
461 let slh_dsa = SlhDsa::new();
462 slh_dsa.verify_for_algorithm(algorithm, public_key, message, signature)
463 }
464
465 #[cfg(not(feature = "ml-dsa"))]
467 Algorithm::MlDsa44 | Algorithm::MlDsa65 | Algorithm::MlDsa87 => {
468 Err(Error::NotImplemented {
469 feature: "ML-DSA implementations require 'ml-dsa' feature flag".to_string(),
470 })
471 }
472 #[cfg(not(feature = "fn-dsa"))]
473 Algorithm::FnDsa | Algorithm::FnDsa512 | Algorithm::FnDsa1024 => {
474 Err(Error::NotImplemented {
475 feature: "FN-DSA implementations require 'fn-dsa' feature flag".to_string(),
476 })
477 }
478 #[cfg(not(feature = "slh-dsa"))]
479 Algorithm::SlhDsaSha256128fRobust |
480 Algorithm::SlhDsaSha256192fRobust |
481 Algorithm::SlhDsaSha256256fRobust |
482 Algorithm::SlhDsaShake256128fRobust |
483 Algorithm::SlhDsaShake256192fRobust |
484 Algorithm::SlhDsaShake256256fRobust => Err(Error::NotImplemented {
485 feature: "SLH-DSA implementations require 'slh-dsa' feature flag".to_string(),
486 }),
487
488 _ => Err(Error::InvalidAlgorithm {
489 algorithm: "Algorithm not supported for signature operations",
490 }),
491 }
492 }
493}
494
495#[cfg(feature = "alloc")]
496impl CryptoProvider for LibQSignatureProvider {
497 fn kem(&self) -> Option<&dyn lib_q_core::api::KemOperations> {
498 None
499 }
500
501 fn signature(&self) -> Option<&dyn SignatureOperations> {
502 Some(self)
503 }
504
505 fn hash(&self) -> Option<&dyn lib_q_core::api::HashOperations> {
506 None
507 }
508
509 fn aead(&self) -> Option<&dyn lib_q_core::api::AeadOperations> {
510 None
511 }
512}
513
514#[cfg(test)]
515mod tests {
516 use alloc::vec::Vec;
517
518 use super::*;
519
520 #[test]
521 fn test_provider_creation() {
522 let provider = LibQSignatureProvider::new();
523 assert!(provider.is_ok(), "Provider should be created successfully");
524 }
525
526 #[test]
527 fn test_provider_security_validator() {
528 let provider = LibQSignatureProvider::new().unwrap();
529 let _validator = provider.security_validator();
530 }
533
534 #[test]
535 fn test_provider_unsupported_algorithm() {
536 let provider = LibQSignatureProvider::new().unwrap();
537 let result = provider.generate_keypair(Algorithm::Sha3_256, None);
538 assert!(
539 result.is_err(),
540 "Should return error for unsupported algorithm"
541 );
542
543 if let Err(Error::InvalidAlgorithm { .. }) = result {
544 } else {
546 panic!("Expected InvalidAlgorithm error");
547 }
548 }
549
550 #[test]
551 fn test_provider_feature_flag_handling() {
552 let _provider = LibQSignatureProvider::new().unwrap();
553
554 #[cfg(not(feature = "ml-dsa"))]
556 {
557 let result = _provider.generate_keypair(Algorithm::MlDsa65, None);
558 assert!(
559 result.is_err(),
560 "Should return error when feature flag is not enabled"
561 );
562
563 if let Err(Error::NotImplemented { feature }) = result {
564 assert!(
565 feature.contains("ML-DSA implementations require 'ml-dsa' feature flag"),
566 "Error should mention feature flag requirement"
567 );
568 } else {
569 panic!("Expected NotImplemented error");
570 }
571 }
572
573 #[cfg(not(feature = "fn-dsa"))]
575 {
576 let result = _provider.generate_keypair(Algorithm::FnDsa512, None);
577 assert!(
578 result.is_err(),
579 "Should return error when feature flag is not enabled"
580 );
581
582 if let Err(Error::NotImplemented { feature }) = result {
583 assert!(
584 feature.contains("FN-DSA implementations require 'fn-dsa' feature flag"),
585 "Error should mention feature flag requirement"
586 );
587 } else {
588 panic!("Expected NotImplemented error");
589 }
590 }
591
592 #[cfg(not(feature = "slh-dsa"))]
594 {
595 let result = _provider.generate_keypair(Algorithm::SlhDsaSha256128fRobust, None);
596 assert!(
597 result.is_err(),
598 "Should return error when feature flag is not enabled"
599 );
600
601 if let Err(Error::NotImplemented { feature }) = result {
602 assert!(
603 feature.contains("SLH-DSA implementations require 'slh-dsa' feature flag"),
604 "Error should mention feature flag requirement"
605 );
606 } else {
607 panic!("Expected NotImplemented error");
608 }
609 }
610 }
611
612 #[test]
613 fn test_provider_algorithm_routing() {
614 let provider = LibQSignatureProvider::new().unwrap();
615
616 #[cfg(feature = "ml-dsa")]
618 {
619 let result = provider.generate_keypair(Algorithm::MlDsa65, None);
620 match result {
622 Ok(_) => {
623 }
625 Err(Error::NotImplemented { .. }) => {
626 }
628 Err(Error::RandomGenerationFailed { .. }) => {
629 }
631 Err(e) => {
632 panic!("Unexpected error type: {:?}", e);
633 }
634 }
635 }
636
637 #[cfg(feature = "fn-dsa")]
638 {
639 let result = provider.generate_keypair(Algorithm::FnDsa512, None);
640 match result {
642 Ok(_) => {
643 }
645 Err(Error::NotImplemented { .. }) => {
646 }
648 Err(Error::RandomGenerationFailed { .. }) => {
649 }
651 Err(e) => {
652 panic!("Unexpected error type: {:?}", e);
653 }
654 }
655 }
656
657 #[cfg(feature = "slh-dsa")]
658 {
659 let result = provider.generate_keypair(Algorithm::SlhDsaSha256128fRobust, None);
660 match result {
662 Ok(_) => {
663 }
665 Err(Error::NotImplemented { .. }) => {
666 }
668 Err(Error::RandomGenerationFailed { .. }) => {
669 }
671 Err(e) => {
672 panic!("Unexpected error type: {:?}", e);
673 }
674 }
675 }
676 }
677
678 #[test]
679 fn test_provider_sign_rejects_non_signature_algorithm() {
680 let provider = LibQSignatureProvider::new().unwrap();
681 let secret_key = SigSecretKey::new(Vec::new());
682 let result = provider.sign(Algorithm::Sha3_256, &secret_key, b"message", None);
683 assert!(
684 matches!(result, Err(Error::InvalidAlgorithm { .. })),
685 "sign should reject non-signature algorithms before key validation"
686 );
687 }
688
689 #[test]
690 fn test_provider_verify_rejects_non_signature_algorithm() {
691 let provider = LibQSignatureProvider::new().unwrap();
692 let public_key = SigPublicKey::new(Vec::new());
693 let result = provider.verify(Algorithm::Sha3_256, &public_key, b"message", b"sig");
694 assert!(
695 matches!(result, Err(Error::InvalidAlgorithm { .. })),
696 "verify should reject non-signature algorithms before key/signature validation"
697 );
698 }
699
700 #[test]
701 fn test_crypto_provider_exposes_signature_only() {
702 let provider = LibQSignatureProvider::new().unwrap();
703 assert!(provider.signature().is_some());
704 assert!(provider.kem().is_none());
705 assert!(provider.hash().is_none());
706 assert!(provider.aead().is_none());
707 }
708
709 #[cfg(feature = "ml-dsa")]
710 #[test]
711 fn test_provider_ml_dsa44_with_explicit_randomness_round_trip() {
712 use lib_q_core::Utils;
713
714 let provider = LibQSignatureProvider::new().unwrap();
715 let message = b"provider ml-dsa44 explicit randomness";
716 let key_randomness = Utils::random_bytes(32).expect("test randomness generation failed");
717 let signing_randomness =
718 Utils::random_bytes(32).expect("test randomness generation failed");
719
720 let keypair = provider
721 .generate_keypair(Algorithm::MlDsa44, Some(&key_randomness))
722 .expect("key generation with explicit randomness should succeed");
723
724 let signature = provider
725 .sign(
726 Algorithm::MlDsa44,
727 keypair.secret_key(),
728 message,
729 Some(&signing_randomness),
730 )
731 .expect("signing with explicit randomness should succeed");
732
733 let is_valid = provider
734 .verify(
735 Algorithm::MlDsa44,
736 keypair.public_key(),
737 message,
738 &signature,
739 )
740 .expect("verification should succeed");
741 assert!(
742 is_valid,
743 "provider should verify its own ML-DSA-44 signatures"
744 );
745 }
746}