Skip to main content

lib_q_sig/
provider.rs

1//! lib-Q Signature Provider Implementation
2//!
3//! This module provides the `LibQSignatureProvider` that implements the `SignatureOperations`
4//! trait and routes signature operations to the appropriate algorithm implementations
5//! with comprehensive security validation.
6//!
7//! ## Architecture
8//!
9//! The `LibQSignatureProvider` serves as the central routing hub for all signature operations:
10//! - **Algorithm Routing**: Routes operations to the correct algorithm implementation
11//! - **Security Validation**: Validates all inputs using `SecurityValidator`
12//! - **Feature Flag Handling**: Gracefully handles missing feature flags
13//! - **Provider Integration**: Implements `CryptoProvider` for lib-q-core integration
14//!
15//! ## Security Features
16//!
17//! - **Input Validation**: All inputs are validated before processing
18//! - **Algorithm Category Validation**: Ensures only signature algorithms are processed
19//! - **Key Size Validation**: Validates key sizes against algorithm requirements
20//! - **Randomness Validation**: Validates randomness quality and size
21//! - **Message Validation**: Validates message content and size
22//! - **Signature Validation**: Validates signature format and size
23//!
24//! ## Supported Operations
25//!
26//! - **Key Generation**: Generates keypairs for all supported algorithms
27//! - **Signing**: Creates signatures with proper randomness handling
28//! - **Verification**: Verifies signatures with comprehensive validation
29//!
30//! ## Algorithm Support
31//!
32//! The provider supports all NIST-approved signature algorithms:
33//! - ML-DSA (CRYSTALS-ML-DSA): Levels 1, 3, 4
34//! - FN-DSA (FIPS 206): Levels 1, 5
35//! - SLH-DSA (SPHINCS+): Levels 1, 3, 5
36
37#[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// Import algorithm implementations
81#[cfg(feature = "ml-dsa")]
82use crate::ml_dsa::MlDsa;
83#[cfg(feature = "slh-dsa")]
84use crate::slh_dsa::SlhDsa;
85
86/// lib-Q signature provider implementation
87///
88/// This provider implements signature operations for lib-Q, including key generation,
89/// signing, and verification with proper security validation and algorithm routing.
90#[cfg(feature = "alloc")]
91#[derive(Clone)]
92pub struct LibQSignatureProvider {
93    security_validator: SecurityValidator,
94}
95
96#[cfg(feature = "alloc")]
97impl LibQSignatureProvider {
98    /// Create a new signature provider
99    ///
100    /// # Returns
101    ///
102    /// A new instance of LibQSignatureProvider with security validation initialized.
103    ///
104    /// # Errors
105    ///
106    /// Returns an error if the security validator fails to initialize.
107    pub fn new() -> Result<Self> {
108        Ok(Self {
109            security_validator: SecurityValidator::new()?,
110        })
111    }
112
113    /// Get the security validator
114    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        // Validate algorithm category
127        self.security_validator.validate_algorithm_category(
128            algorithm,
129            lib_q_core::api::AlgorithmCategory::Signature,
130        )?;
131
132        // Validate randomness if provided
133        if let Some(rng) = randomness {
134            self.security_validator.validate_randomness(rng)?;
135        }
136
137        // Route to specific algorithm implementation
138        match algorithm {
139            // ML-DSA algorithms
140            #[cfg(feature = "ml-dsa")]
141            Algorithm::MlDsa44 => {
142                let ml_dsa = MlDsa::ml_dsa_44();
143                if let Some(rng) = randomness {
144                    // Use provided randomness
145                    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            // FN-DSA algorithms
185            #[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            // SLH-DSA algorithms
202            #[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            // Handle missing feature flags
234            #[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        // Validate algorithm category
270        self.security_validator.validate_algorithm_category(
271            algorithm,
272            lib_q_core::api::AlgorithmCategory::Signature,
273        )?;
274
275        // Validate secret key
276        self.security_validator
277            .validate_secret_key(algorithm, secret_key.as_bytes())?;
278
279        // Validate message
280        self.security_validator
281            .validate_signature_message(message)?;
282
283        // Validate randomness if provided
284        if let Some(rng) = randomness {
285            self.security_validator.validate_randomness(rng)?;
286        }
287
288        // Route to specific algorithm implementation
289        match algorithm {
290            // ML-DSA algorithms
291            #[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            // FN-DSA algorithms
335            #[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            // SLH-DSA algorithms
352            #[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            // Handle missing feature flags
364            #[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        // Validate algorithm category
400        self.security_validator.validate_algorithm_category(
401            algorithm,
402            lib_q_core::api::AlgorithmCategory::Signature,
403        )?;
404
405        // Validate public key
406        self.security_validator
407            .validate_public_key(algorithm, public_key.as_bytes())?;
408
409        // Validate message
410        self.security_validator
411            .validate_signature_message(message)?;
412
413        // Validate signature
414        self.security_validator
415            .validate_signature(algorithm, signature)?;
416
417        // Route to specific algorithm implementation
418        match algorithm {
419            // ML-DSA algorithms
420            #[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            // FN-DSA algorithms
437            #[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            // SLH-DSA algorithms
454            #[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            // Handle missing feature flags
466            #[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        // Security validator should be accessible
531        // Security validator is accessible
532    }
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            // Expected error type
545        } 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        // Test ML-DSA without feature flag
555        #[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        // Test FN-DSA without feature flag
574        #[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        // Test SLH-DSA without feature flag
593        #[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        // Test that algorithms are properly routed
617        #[cfg(feature = "ml-dsa")]
618        {
619            let result = provider.generate_keypair(Algorithm::MlDsa65, None);
620            // Should either succeed or return NotImplemented (depending on std feature)
621            match result {
622                Ok(_) => {
623                    // Success case - this is expected with std feature
624                }
625                Err(Error::NotImplemented { .. }) => {
626                    // Expected when std feature is not available
627                }
628                Err(Error::RandomGenerationFailed { .. }) => {
629                    // Expected when std feature is not available for randomness generation
630                }
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            // Should either succeed or return NotImplemented (depending on std feature)
641            match result {
642                Ok(_) => {
643                    // Success case - this is expected with std feature
644                }
645                Err(Error::NotImplemented { .. }) => {
646                    // Expected when std feature is not available
647                }
648                Err(Error::RandomGenerationFailed { .. }) => {
649                    // Expected when std feature is not available for randomness generation
650                }
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            // Should either succeed or return NotImplemented (depending on std feature)
661            match result {
662                Ok(_) => {
663                    // Success case - this is expected with std feature
664                }
665                Err(Error::NotImplemented { .. }) => {
666                    // Expected when std feature is not available
667                }
668                Err(Error::RandomGenerationFailed { .. }) => {
669                    // Expected when std feature is not available for randomness generation
670                }
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}