1#[cfg(feature = "alloc")]
40extern crate alloc;
41#[cfg(all(
42 feature = "alloc",
43 not(feature = "std"),
44 any(
45 not(feature = "ml-dsa"),
46 not(feature = "fn-dsa"),
47 not(feature = "slh-dsa"),
48 ),
49))]
50use alloc::string::ToString;
51#[cfg(feature = "alloc")]
52use alloc::vec::Vec;
53
54#[cfg(feature = "alloc")]
55use lib_q_core::api::{
56 Algorithm,
57 CryptoProvider,
58 SignatureOperations,
59};
60#[cfg(feature = "alloc")]
61use lib_q_core::error::{
62 Error,
63 Result,
64};
65#[cfg(feature = "alloc")]
66use lib_q_core::security::SecurityValidator;
67#[cfg(all(feature = "alloc", any(feature = "ml-dsa", feature = "fn-dsa")))]
68use lib_q_core::traits::Signature;
69#[cfg(feature = "alloc")]
70use lib_q_core::traits::{
71 SigKeypair,
72 SigPublicKey,
73 SigSecretKey,
74};
75
76#[cfg(feature = "fn-dsa")]
77use crate::fn_dsa::{
78 FnDsa,
79 FnDsa512,
80 FnDsa1024,
81};
82#[cfg(feature = "ml-dsa")]
84use crate::ml_dsa::MlDsa;
85#[cfg(feature = "slh-dsa")]
86use crate::slh_dsa::SlhDsa;
87
88#[cfg(feature = "alloc")]
93#[derive(Clone)]
94pub struct LibQSignatureProvider {
95 security_validator: SecurityValidator,
96}
97
98#[cfg(feature = "alloc")]
99impl LibQSignatureProvider {
100 pub fn new() -> Result<Self> {
110 Ok(Self {
111 security_validator: SecurityValidator::new()?,
112 })
113 }
114
115 pub fn security_validator(&self) -> &SecurityValidator {
117 &self.security_validator
118 }
119}
120
121#[cfg(feature = "alloc")]
122impl SignatureOperations for LibQSignatureProvider {
123 fn generate_keypair(
124 &self,
125 algorithm: Algorithm,
126 randomness: Option<&[u8]>,
127 ) -> Result<SigKeypair> {
128 self.security_validator.validate_algorithm_category(
130 algorithm,
131 lib_q_core::api::AlgorithmCategory::Signature,
132 )?;
133
134 if let Some(rng) = randomness {
136 self.security_validator.validate_randomness(rng)?;
137 }
138
139 match algorithm {
141 #[cfg(feature = "ml-dsa")]
143 Algorithm::MlDsa44 => {
144 let ml_dsa = MlDsa::ml_dsa_44();
145 if let Some(rng) = randomness {
146 let rng_array: [u8; 32] =
148 rng.try_into().map_err(|_| Error::InvalidKeySize {
149 expected: 32,
150 actual: rng.len(),
151 })?;
152 ml_dsa.generate_keypair_with_randomness(rng_array)
153 } else {
154 ml_dsa.generate_keypair()
155 }
156 }
157 #[cfg(feature = "ml-dsa")]
158 Algorithm::MlDsa65 => {
159 let ml_dsa = MlDsa::ml_dsa_65();
160 if let Some(rng) = randomness {
161 let rng_array: [u8; 32] =
162 rng.try_into().map_err(|_| Error::InvalidKeySize {
163 expected: 32,
164 actual: rng.len(),
165 })?;
166 ml_dsa.generate_keypair_with_randomness(rng_array)
167 } else {
168 ml_dsa.generate_keypair()
169 }
170 }
171 #[cfg(feature = "ml-dsa")]
172 Algorithm::MlDsa87 => {
173 let ml_dsa = MlDsa::ml_dsa_87();
174 if let Some(rng) = randomness {
175 let rng_array: [u8; 32] =
176 rng.try_into().map_err(|_| Error::InvalidKeySize {
177 expected: 32,
178 actual: rng.len(),
179 })?;
180 ml_dsa.generate_keypair_with_randomness(rng_array)
181 } else {
182 ml_dsa.generate_keypair()
183 }
184 }
185
186 #[cfg(feature = "fn-dsa")]
188 Algorithm::FnDsa => {
189 let fn_dsa = FnDsa::level1();
190 fn_dsa.generate_keypair()
191 }
192 #[cfg(feature = "fn-dsa")]
193 Algorithm::FnDsa512 => {
194 let fn_dsa = FnDsa512::new();
195 fn_dsa.generate_keypair()
196 }
197 #[cfg(feature = "fn-dsa")]
198 Algorithm::FnDsa1024 => {
199 let fn_dsa = FnDsa1024::new();
200 fn_dsa.generate_keypair()
201 }
202
203 #[cfg(feature = "slh-dsa")]
205 Algorithm::SlhDsaSha256128fRobust => {
206 let slh_dsa = SlhDsa::new();
207 slh_dsa.generate_keypair_for_algorithm(algorithm, randomness)
208 }
209 #[cfg(feature = "slh-dsa")]
210 Algorithm::SlhDsaSha256192fRobust => {
211 let slh_dsa = SlhDsa::new();
212 slh_dsa.generate_keypair_for_algorithm(algorithm, randomness)
213 }
214 #[cfg(feature = "slh-dsa")]
215 Algorithm::SlhDsaSha256256fRobust => {
216 let slh_dsa = SlhDsa::new();
217 slh_dsa.generate_keypair_for_algorithm(algorithm, randomness)
218 }
219 #[cfg(feature = "slh-dsa")]
220 Algorithm::SlhDsaShake256128fRobust => {
221 let slh_dsa = SlhDsa::new();
222 slh_dsa.generate_keypair_for_algorithm(algorithm, randomness)
223 }
224 #[cfg(feature = "slh-dsa")]
225 Algorithm::SlhDsaShake256192fRobust => {
226 let slh_dsa = SlhDsa::new();
227 slh_dsa.generate_keypair_for_algorithm(algorithm, randomness)
228 }
229 #[cfg(feature = "slh-dsa")]
230 Algorithm::SlhDsaShake256256fRobust => {
231 let slh_dsa = SlhDsa::new();
232 slh_dsa.generate_keypair_for_algorithm(algorithm, randomness)
233 }
234
235 #[cfg(not(feature = "ml-dsa"))]
237 Algorithm::MlDsa44 | Algorithm::MlDsa65 | Algorithm::MlDsa87 => {
238 Err(Error::NotImplemented {
239 feature: "ML-DSA implementations require 'ml-dsa' feature flag".to_string(),
240 })
241 }
242 #[cfg(not(feature = "fn-dsa"))]
243 Algorithm::FnDsa | Algorithm::FnDsa512 | Algorithm::FnDsa1024 => {
244 Err(Error::NotImplemented {
245 feature: "FN-DSA implementations require 'fn-dsa' feature flag".to_string(),
246 })
247 }
248 #[cfg(not(feature = "slh-dsa"))]
249 Algorithm::SlhDsaSha256128fRobust |
250 Algorithm::SlhDsaSha256192fRobust |
251 Algorithm::SlhDsaSha256256fRobust |
252 Algorithm::SlhDsaShake256128fRobust |
253 Algorithm::SlhDsaShake256192fRobust |
254 Algorithm::SlhDsaShake256256fRobust => Err(Error::NotImplemented {
255 feature: "SLH-DSA implementations require 'slh-dsa' feature flag".to_string(),
256 }),
257
258 _ => Err(Error::InvalidAlgorithm {
259 algorithm: "Algorithm not supported for signature operations",
260 }),
261 }
262 }
263
264 fn sign(
265 &self,
266 algorithm: Algorithm,
267 secret_key: &SigSecretKey,
268 message: &[u8],
269 randomness: Option<&[u8]>,
270 ) -> Result<Vec<u8>> {
271 self.security_validator.validate_algorithm_category(
273 algorithm,
274 lib_q_core::api::AlgorithmCategory::Signature,
275 )?;
276
277 self.security_validator
279 .validate_secret_key(algorithm, secret_key.as_bytes())?;
280
281 self.security_validator
283 .validate_signature_message(message)?;
284
285 if let Some(rng) = randomness {
287 self.security_validator.validate_randomness(rng)?;
288 }
289
290 match algorithm {
292 #[cfg(feature = "ml-dsa")]
294 Algorithm::MlDsa44 => {
295 let ml_dsa = MlDsa::ml_dsa_44();
296 if let Some(rng) = randomness {
297 let rng_array: [u8; 32] =
298 rng.try_into().map_err(|_| Error::InvalidKeySize {
299 expected: 32,
300 actual: rng.len(),
301 })?;
302 ml_dsa.sign_with_randomness(secret_key, message, rng_array)
303 } else {
304 ml_dsa.sign(secret_key, message)
305 }
306 }
307 #[cfg(feature = "ml-dsa")]
308 Algorithm::MlDsa65 => {
309 let ml_dsa = MlDsa::ml_dsa_65();
310 if let Some(rng) = randomness {
311 let rng_array: [u8; 32] =
312 rng.try_into().map_err(|_| Error::InvalidKeySize {
313 expected: 32,
314 actual: rng.len(),
315 })?;
316 ml_dsa.sign_with_randomness(secret_key, message, rng_array)
317 } else {
318 ml_dsa.sign(secret_key, message)
319 }
320 }
321 #[cfg(feature = "ml-dsa")]
322 Algorithm::MlDsa87 => {
323 let ml_dsa = MlDsa::ml_dsa_87();
324 if let Some(rng) = randomness {
325 let rng_array: [u8; 32] =
326 rng.try_into().map_err(|_| Error::InvalidKeySize {
327 expected: 32,
328 actual: rng.len(),
329 })?;
330 ml_dsa.sign_with_randomness(secret_key, message, rng_array)
331 } else {
332 ml_dsa.sign(secret_key, message)
333 }
334 }
335
336 #[cfg(feature = "fn-dsa")]
338 Algorithm::FnDsa => {
339 let fn_dsa = FnDsa::level1();
340 fn_dsa.sign(secret_key, message)
341 }
342 #[cfg(feature = "fn-dsa")]
343 Algorithm::FnDsa512 => {
344 let fn_dsa = FnDsa512::new();
345 fn_dsa.sign(secret_key, message)
346 }
347 #[cfg(feature = "fn-dsa")]
348 Algorithm::FnDsa1024 => {
349 let fn_dsa = FnDsa1024::new();
350 fn_dsa.sign(secret_key, message)
351 }
352
353 #[cfg(feature = "slh-dsa")]
355 Algorithm::SlhDsaSha256128fRobust |
356 Algorithm::SlhDsaSha256192fRobust |
357 Algorithm::SlhDsaSha256256fRobust |
358 Algorithm::SlhDsaShake256128fRobust |
359 Algorithm::SlhDsaShake256192fRobust |
360 Algorithm::SlhDsaShake256256fRobust => {
361 let slh_dsa = SlhDsa::new();
362 slh_dsa.sign_for_algorithm(algorithm, secret_key, message, randomness)
363 }
364
365 #[cfg(not(feature = "ml-dsa"))]
367 Algorithm::MlDsa44 | Algorithm::MlDsa65 | Algorithm::MlDsa87 => {
368 Err(Error::NotImplemented {
369 feature: "ML-DSA implementations require 'ml-dsa' feature flag".to_string(),
370 })
371 }
372 #[cfg(not(feature = "fn-dsa"))]
373 Algorithm::FnDsa | Algorithm::FnDsa512 | Algorithm::FnDsa1024 => {
374 Err(Error::NotImplemented {
375 feature: "FN-DSA implementations require 'fn-dsa' feature flag".to_string(),
376 })
377 }
378 #[cfg(not(feature = "slh-dsa"))]
379 Algorithm::SlhDsaSha256128fRobust |
380 Algorithm::SlhDsaSha256192fRobust |
381 Algorithm::SlhDsaSha256256fRobust |
382 Algorithm::SlhDsaShake256128fRobust |
383 Algorithm::SlhDsaShake256192fRobust |
384 Algorithm::SlhDsaShake256256fRobust => Err(Error::NotImplemented {
385 feature: "SLH-DSA implementations require 'slh-dsa' feature flag".to_string(),
386 }),
387
388 _ => Err(Error::InvalidAlgorithm {
389 algorithm: "Algorithm not supported for signature operations",
390 }),
391 }
392 }
393
394 fn verify(
395 &self,
396 algorithm: Algorithm,
397 public_key: &SigPublicKey,
398 message: &[u8],
399 signature: &[u8],
400 ) -> Result<bool> {
401 self.security_validator.validate_algorithm_category(
403 algorithm,
404 lib_q_core::api::AlgorithmCategory::Signature,
405 )?;
406
407 self.security_validator
409 .validate_public_key(algorithm, public_key.as_bytes())?;
410
411 self.security_validator
413 .validate_signature_message(message)?;
414
415 self.security_validator
417 .validate_signature(algorithm, signature)?;
418
419 match algorithm {
421 #[cfg(feature = "ml-dsa")]
423 Algorithm::MlDsa44 => {
424 let ml_dsa = MlDsa::ml_dsa_44();
425 ml_dsa.verify(public_key, message, signature)
426 }
427 #[cfg(feature = "ml-dsa")]
428 Algorithm::MlDsa65 => {
429 let ml_dsa = MlDsa::ml_dsa_65();
430 ml_dsa.verify(public_key, message, signature)
431 }
432 #[cfg(feature = "ml-dsa")]
433 Algorithm::MlDsa87 => {
434 let ml_dsa = MlDsa::ml_dsa_87();
435 ml_dsa.verify(public_key, message, signature)
436 }
437
438 #[cfg(feature = "fn-dsa")]
440 Algorithm::FnDsa => {
441 let fn_dsa = FnDsa::level1();
442 fn_dsa.verify(public_key, message, signature)
443 }
444 #[cfg(feature = "fn-dsa")]
445 Algorithm::FnDsa512 => {
446 let fn_dsa = FnDsa512::new();
447 fn_dsa.verify(public_key, message, signature)
448 }
449 #[cfg(feature = "fn-dsa")]
450 Algorithm::FnDsa1024 => {
451 let fn_dsa = FnDsa1024::new();
452 fn_dsa.verify(public_key, message, signature)
453 }
454
455 #[cfg(feature = "slh-dsa")]
457 Algorithm::SlhDsaSha256128fRobust |
458 Algorithm::SlhDsaSha256192fRobust |
459 Algorithm::SlhDsaSha256256fRobust |
460 Algorithm::SlhDsaShake256128fRobust |
461 Algorithm::SlhDsaShake256192fRobust |
462 Algorithm::SlhDsaShake256256fRobust => {
463 let slh_dsa = SlhDsa::new();
464 slh_dsa.verify_for_algorithm(algorithm, public_key, message, signature)
465 }
466
467 #[cfg(not(feature = "ml-dsa"))]
469 Algorithm::MlDsa44 | Algorithm::MlDsa65 | Algorithm::MlDsa87 => {
470 Err(Error::NotImplemented {
471 feature: "ML-DSA implementations require 'ml-dsa' feature flag".to_string(),
472 })
473 }
474 #[cfg(not(feature = "fn-dsa"))]
475 Algorithm::FnDsa | Algorithm::FnDsa512 | Algorithm::FnDsa1024 => {
476 Err(Error::NotImplemented {
477 feature: "FN-DSA implementations require 'fn-dsa' feature flag".to_string(),
478 })
479 }
480 #[cfg(not(feature = "slh-dsa"))]
481 Algorithm::SlhDsaSha256128fRobust |
482 Algorithm::SlhDsaSha256192fRobust |
483 Algorithm::SlhDsaSha256256fRobust |
484 Algorithm::SlhDsaShake256128fRobust |
485 Algorithm::SlhDsaShake256192fRobust |
486 Algorithm::SlhDsaShake256256fRobust => Err(Error::NotImplemented {
487 feature: "SLH-DSA implementations require 'slh-dsa' feature flag".to_string(),
488 }),
489
490 _ => Err(Error::InvalidAlgorithm {
491 algorithm: "Algorithm not supported for signature operations",
492 }),
493 }
494 }
495
496 fn sign_with_context(
500 &self,
501 algorithm: Algorithm,
502 secret_key: &SigSecretKey,
503 message: &[u8],
504 context: &[u8],
505 randomness: Option<&[u8]>,
506 ) -> Result<Vec<u8>> {
507 if context.is_empty() {
508 return self.sign(algorithm, secret_key, message, randomness);
509 }
510
511 self.security_validator.validate_algorithm_category(
513 algorithm,
514 lib_q_core::api::AlgorithmCategory::Signature,
515 )?;
516
517 self.security_validator
519 .validate_secret_key(algorithm, secret_key.as_bytes())?;
520
521 self.security_validator
523 .validate_signature_message(message)?;
524
525 if let Some(rng) = randomness {
527 self.security_validator.validate_randomness(rng)?;
528 }
529
530 #[cfg(feature = "ml-dsa")]
531 {
532 let ml_dsa = match algorithm {
533 Algorithm::MlDsa44 => Some(MlDsa::ml_dsa_44()),
534 Algorithm::MlDsa65 => Some(MlDsa::ml_dsa_65()),
535 Algorithm::MlDsa87 => Some(MlDsa::ml_dsa_87()),
536 _ => None,
537 };
538 if let Some(ml_dsa) = ml_dsa {
539 return if let Some(rng) = randomness {
540 let rng_array: [u8; 32] =
541 rng.try_into().map_err(|_| Error::InvalidKeySize {
542 expected: 32,
543 actual: rng.len(),
544 })?;
545 ml_dsa.sign_with_randomness_and_context(secret_key, message, context, rng_array)
546 } else {
547 ml_dsa.sign_with_context(secret_key, message, context)
548 };
549 }
550 }
551
552 Err(Error::NotImplemented {
553 feature: "signing contexts are only supported for ML-DSA".to_string(),
554 })
555 }
556
557 fn verify_with_context(
561 &self,
562 algorithm: Algorithm,
563 public_key: &SigPublicKey,
564 message: &[u8],
565 context: &[u8],
566 signature: &[u8],
567 ) -> Result<bool> {
568 if context.is_empty() {
569 return self.verify(algorithm, public_key, message, signature);
570 }
571
572 self.security_validator.validate_algorithm_category(
574 algorithm,
575 lib_q_core::api::AlgorithmCategory::Signature,
576 )?;
577
578 self.security_validator
580 .validate_public_key(algorithm, public_key.as_bytes())?;
581
582 self.security_validator
584 .validate_signature_message(message)?;
585
586 self.security_validator
588 .validate_signature(algorithm, signature)?;
589
590 #[cfg(feature = "ml-dsa")]
591 {
592 let ml_dsa = match algorithm {
593 Algorithm::MlDsa44 => Some(MlDsa::ml_dsa_44()),
594 Algorithm::MlDsa65 => Some(MlDsa::ml_dsa_65()),
595 Algorithm::MlDsa87 => Some(MlDsa::ml_dsa_87()),
596 _ => None,
597 };
598 if let Some(ml_dsa) = ml_dsa {
599 return ml_dsa.verify_with_context(public_key, message, context, signature);
600 }
601 }
602
603 Err(Error::NotImplemented {
604 feature: "signing contexts are only supported for ML-DSA".to_string(),
605 })
606 }
607}
608
609#[cfg(feature = "alloc")]
610impl CryptoProvider for LibQSignatureProvider {
611 fn kem(&self) -> Option<&dyn lib_q_core::api::KemOperations> {
612 None
613 }
614
615 fn signature(&self) -> Option<&dyn SignatureOperations> {
616 Some(self)
617 }
618
619 fn hash(&self) -> Option<&dyn lib_q_core::api::HashOperations> {
620 None
621 }
622
623 fn aead(&self) -> Option<&dyn lib_q_core::api::AeadOperations> {
624 None
625 }
626}
627
628#[cfg(test)]
629mod tests {
630 use alloc::vec::Vec;
631
632 use super::*;
633
634 #[test]
635 fn test_provider_creation() {
636 let provider = LibQSignatureProvider::new();
637 assert!(provider.is_ok(), "Provider should be created successfully");
638 }
639
640 #[test]
641 fn test_provider_security_validator() {
642 let provider = LibQSignatureProvider::new().unwrap();
643 let _validator = provider.security_validator();
644 }
647
648 #[test]
649 fn test_provider_unsupported_algorithm() {
650 let provider = LibQSignatureProvider::new().unwrap();
651 let result = provider.generate_keypair(Algorithm::Sha3_256, None);
652 assert!(
653 result.is_err(),
654 "Should return error for unsupported algorithm"
655 );
656
657 if let Err(Error::InvalidAlgorithm { .. }) = result {
658 } else {
660 panic!("Expected InvalidAlgorithm error");
661 }
662 }
663
664 #[test]
665 fn test_provider_feature_flag_handling() {
666 let _provider = LibQSignatureProvider::new().unwrap();
667
668 #[cfg(not(feature = "ml-dsa"))]
670 {
671 let result = _provider.generate_keypair(Algorithm::MlDsa65, None);
672 assert!(
673 result.is_err(),
674 "Should return error when feature flag is not enabled"
675 );
676
677 if let Err(Error::NotImplemented { feature }) = result {
678 assert!(
679 feature.contains("ML-DSA implementations require 'ml-dsa' feature flag"),
680 "Error should mention feature flag requirement"
681 );
682 } else {
683 panic!("Expected NotImplemented error");
684 }
685 }
686
687 #[cfg(not(feature = "fn-dsa"))]
689 {
690 let result = _provider.generate_keypair(Algorithm::FnDsa512, None);
691 assert!(
692 result.is_err(),
693 "Should return error when feature flag is not enabled"
694 );
695
696 if let Err(Error::NotImplemented { feature }) = result {
697 assert!(
698 feature.contains("FN-DSA implementations require 'fn-dsa' feature flag"),
699 "Error should mention feature flag requirement"
700 );
701 } else {
702 panic!("Expected NotImplemented error");
703 }
704 }
705
706 #[cfg(not(feature = "slh-dsa"))]
708 {
709 let result = _provider.generate_keypair(Algorithm::SlhDsaSha256128fRobust, None);
710 assert!(
711 result.is_err(),
712 "Should return error when feature flag is not enabled"
713 );
714
715 if let Err(Error::NotImplemented { feature }) = result {
716 assert!(
717 feature.contains("SLH-DSA implementations require 'slh-dsa' feature flag"),
718 "Error should mention feature flag requirement"
719 );
720 } else {
721 panic!("Expected NotImplemented error");
722 }
723 }
724 }
725
726 #[test]
727 fn test_provider_algorithm_routing() {
728 let provider = LibQSignatureProvider::new().unwrap();
729
730 #[cfg(feature = "ml-dsa")]
732 {
733 let result = provider.generate_keypair(Algorithm::MlDsa65, None);
734 match result {
736 Ok(_) => {
737 }
739 Err(Error::NotImplemented { .. }) => {
740 }
742 Err(Error::RandomGenerationFailed { .. }) => {
743 }
745 Err(e) => {
746 panic!("Unexpected error type: {:?}", e);
747 }
748 }
749 }
750
751 #[cfg(feature = "fn-dsa")]
752 {
753 let result = provider.generate_keypair(Algorithm::FnDsa512, None);
754 match result {
756 Ok(_) => {
757 }
759 Err(Error::NotImplemented { .. }) => {
760 }
762 Err(Error::RandomGenerationFailed { .. }) => {
763 }
765 Err(e) => {
766 panic!("Unexpected error type: {:?}", e);
767 }
768 }
769 }
770
771 #[cfg(feature = "slh-dsa")]
772 {
773 let result = provider.generate_keypair(Algorithm::SlhDsaSha256128fRobust, None);
774 match result {
776 Ok(_) => {
777 }
779 Err(Error::NotImplemented { .. }) => {
780 }
782 Err(Error::RandomGenerationFailed { .. }) => {
783 }
785 Err(e) => {
786 panic!("Unexpected error type: {:?}", e);
787 }
788 }
789 }
790 }
791
792 #[test]
793 fn test_provider_sign_rejects_non_signature_algorithm() {
794 let provider = LibQSignatureProvider::new().unwrap();
795 let secret_key = SigSecretKey::new(Vec::new());
796 let result = provider.sign(Algorithm::Sha3_256, &secret_key, b"message", None);
797 assert!(
798 matches!(result, Err(Error::InvalidAlgorithm { .. })),
799 "sign should reject non-signature algorithms before key validation"
800 );
801 }
802
803 #[test]
804 fn test_provider_verify_rejects_non_signature_algorithm() {
805 let provider = LibQSignatureProvider::new().unwrap();
806 let public_key = SigPublicKey::new(Vec::new());
807 let result = provider.verify(Algorithm::Sha3_256, &public_key, b"message", b"sig");
808 assert!(
809 matches!(result, Err(Error::InvalidAlgorithm { .. })),
810 "verify should reject non-signature algorithms before key/signature validation"
811 );
812 }
813
814 #[test]
815 fn test_crypto_provider_exposes_signature_only() {
816 let provider = LibQSignatureProvider::new().unwrap();
817 assert!(provider.signature().is_some());
818 assert!(provider.kem().is_none());
819 assert!(provider.hash().is_none());
820 assert!(provider.aead().is_none());
821 }
822
823 #[cfg(feature = "ml-dsa")]
824 #[test]
825 fn test_provider_ml_dsa44_with_explicit_randomness_round_trip() {
826 use lib_q_core::Utils;
827
828 let provider = LibQSignatureProvider::new().unwrap();
829 let message = b"provider ml-dsa44 explicit randomness";
830 let key_randomness = Utils::random_bytes(32).expect("test randomness generation failed");
831 let signing_randomness =
832 Utils::random_bytes(32).expect("test randomness generation failed");
833
834 let keypair = provider
835 .generate_keypair(Algorithm::MlDsa44, Some(&key_randomness))
836 .expect("key generation with explicit randomness should succeed");
837
838 let signature = provider
839 .sign(
840 Algorithm::MlDsa44,
841 keypair.secret_key(),
842 message,
843 Some(&signing_randomness),
844 )
845 .expect("signing with explicit randomness should succeed");
846
847 let is_valid = provider
848 .verify(
849 Algorithm::MlDsa44,
850 keypair.public_key(),
851 message,
852 &signature,
853 )
854 .expect("verification should succeed");
855 assert!(
856 is_valid,
857 "provider should verify its own ML-DSA-44 signatures"
858 );
859 }
860
861 #[cfg(feature = "ml-dsa")]
869 #[test]
870 fn ml_dsa_round_trips_through_the_provider_at_every_parameter_set() {
871 let provider = LibQSignatureProvider::new().expect("provider construction should succeed");
872 let message = b"ml-dsa provider round trip";
873
874 for algorithm in [Algorithm::MlDsa44, Algorithm::MlDsa65, Algorithm::MlDsa87] {
875 let keypair = provider
876 .generate_keypair(algorithm, None)
877 .unwrap_or_else(|e| panic!("{algorithm:?} keygen failed: {e:?}"));
878
879 let signature = provider
880 .sign(algorithm, keypair.secret_key(), message, None)
881 .unwrap_or_else(|e| {
882 panic!("{algorithm:?}: provider rejected a key it just generated: {e:?}")
883 });
884
885 let is_valid = provider
886 .verify(algorithm, keypair.public_key(), message, &signature)
887 .unwrap_or_else(|e| panic!("{algorithm:?} verify errored: {e:?}"));
888 assert!(
889 is_valid,
890 "{algorithm:?}: provider must verify its own signature"
891 );
892 }
893 }
894
895 #[test]
905 #[cfg(feature = "fn-dsa")]
906 fn fn_dsa_round_trips_through_the_provider_at_every_parameter_set() {
907 let provider = LibQSignatureProvider::new().expect("provider construction should succeed");
908 let message = b"fn-dsa provider round trip";
909
910 for algorithm in [Algorithm::FnDsa512, Algorithm::FnDsa1024] {
911 let keypair = match provider.generate_keypair(algorithm, None) {
912 Ok(kp) => kp,
913 Err(Error::NotImplemented { .. }) | Err(Error::RandomGenerationFailed { .. }) => {
916 continue;
917 }
918 Err(e) => panic!("{algorithm:?} keygen failed: {e:?}"),
919 };
920
921 let signature = provider
922 .sign(algorithm, keypair.secret_key(), message, None)
923 .unwrap_or_else(|e| {
924 panic!("{algorithm:?}: provider rejected a key it just generated: {e:?}")
925 });
926
927 let is_valid = provider
928 .verify(algorithm, keypair.public_key(), message, &signature)
929 .unwrap_or_else(|e| panic!("{algorithm:?} verify errored: {e:?}"));
930 assert!(
931 is_valid,
932 "{algorithm:?}: provider must verify its own signature"
933 );
934 }
935 }
936}