Skip to main content

lib_q_hash/
provider.rs

1//! lib-Q Hash Provider Implementation
2//!
3//! This module provides the LibQHashProvider that implements the HashOperations
4//! trait and routes hash operations to the appropriate algorithm implementations
5//! with proper security validation.
6
7#[cfg(feature = "alloc")]
8extern crate alloc;
9#[cfg(feature = "alloc")]
10use alloc::{
11    format,
12    string::ToString,
13    vec::Vec,
14};
15
16use lib_q_core::api::{
17    Algorithm,
18    CryptoProvider,
19    HashOperations,
20};
21use lib_q_core::error::{
22    Error,
23    Result,
24};
25use lib_q_core::security::SecurityValidator;
26
27use crate::{
28    algorithm_to_hash_algorithm,
29    create_hash,
30};
31
32/// lib-Q hash provider implementation
33///
34/// This provider implements hash operations for lib-Q, including hash computation
35/// with proper security validation and algorithm routing.
36#[cfg(feature = "alloc")]
37#[derive(Clone)]
38pub struct LibQHashProvider {
39    security_validator: SecurityValidator,
40}
41
42#[cfg(feature = "alloc")]
43impl core::fmt::Debug for LibQHashProvider {
44    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
45        f.debug_struct("LibQHashProvider")
46            .field("security_validator", &"<SecurityValidator>")
47            .finish()
48    }
49}
50
51#[cfg(feature = "alloc")]
52impl LibQHashProvider {
53    /// Create a new hash provider
54    ///
55    /// # Returns
56    ///
57    /// A new instance of LibQHashProvider with security validation initialized.
58    ///
59    /// # Errors
60    ///
61    /// Returns an error if the security validator fails to initialize.
62    pub fn new() -> Result<Self> {
63        Ok(Self {
64            security_validator: SecurityValidator::new()?,
65        })
66    }
67
68    /// Get the security validator
69    pub fn security_validator(&self) -> &SecurityValidator {
70        &self.security_validator
71    }
72}
73
74#[cfg(feature = "alloc")]
75impl HashOperations for LibQHashProvider {
76    fn hash(&self, algorithm: Algorithm, data: &[u8]) -> Result<Vec<u8>> {
77        // Validate algorithm category
78        self.security_validator
79            .validate_algorithm_category(algorithm, lib_q_core::api::AlgorithmCategory::Hash)?;
80
81        // Validate input data
82        self.security_validator.validate_hash_input(data)?;
83
84        // Map Algorithm to HashAlgorithm and create hash instance
85        let hash_algorithm = algorithm_to_hash_algorithm(algorithm)?;
86        let hasher = create_hash(hash_algorithm).map_err(|e| Error::InternalError {
87            operation: "hash instance creation".to_string(),
88            details: format!(
89                "Failed to create hash instance for algorithm {:?}: {}",
90                algorithm, e
91            ),
92        })?;
93
94        // Use the hash method from the lib-q-core Hash trait
95        lib_q_core::Hash::hash(&*hasher, data).map_err(|e| Error::InternalError {
96            operation: "hash computation".to_string(),
97            details: format!(
98                "Failed to compute hash for algorithm {:?}: {}",
99                algorithm, e
100            ),
101        })
102    }
103}
104
105#[cfg(feature = "alloc")]
106impl CryptoProvider for LibQHashProvider {
107    fn kem(&self) -> Option<&dyn lib_q_core::api::KemOperations> {
108        None
109    }
110
111    fn signature(&self) -> Option<&dyn lib_q_core::api::SignatureOperations> {
112        None
113    }
114
115    fn hash(&self) -> Option<&dyn HashOperations> {
116        Some(self)
117    }
118
119    fn aead(&self) -> Option<&dyn lib_q_core::api::AeadOperations> {
120        None
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use alloc::vec::Vec;
127
128    use super::*;
129
130    #[test]
131    fn test_provider_creation() {
132        let provider = LibQHashProvider::new();
133        assert!(provider.is_ok(), "Provider should be created successfully");
134    }
135
136    #[test]
137    fn test_provider_security_validator() {
138        let provider = LibQHashProvider::new().unwrap();
139        let _validator = provider.security_validator();
140        // Security validator should be accessible
141    }
142
143    #[test]
144    fn test_provider_unsupported_algorithm() {
145        let provider = LibQHashProvider::new().unwrap();
146        let result = HashOperations::hash(&provider, Algorithm::MlDsa65, b"test data");
147        assert!(
148            result.is_err(),
149            "Should return error for unsupported algorithm"
150        );
151
152        if let Err(Error::InvalidAlgorithm { .. }) = result {
153            // Expected error type
154        } else {
155            panic!("Expected InvalidAlgorithm error");
156        }
157    }
158
159    #[test]
160    fn test_provider_algorithm_routing() {
161        let provider = LibQHashProvider::new().unwrap();
162
163        // Test SHA-3 algorithms
164        let test_data = b"Hello, lib-Q!";
165
166        let result = HashOperations::hash(&provider, Algorithm::Sha3_256, test_data);
167        assert!(result.is_ok(), "SHA3-256 should work");
168        if let Ok(hash) = result {
169            assert_eq!(hash.len(), 32, "SHA3-256 should produce 32-byte hash");
170        }
171
172        let result = HashOperations::hash(&provider, Algorithm::Sha3_512, test_data);
173        assert!(result.is_ok(), "SHA3-512 should work");
174        if let Ok(hash) = result {
175            assert_eq!(hash.len(), 64, "SHA3-512 should produce 64-byte hash");
176        }
177
178        // Test SHAKE algorithms
179        let result = HashOperations::hash(&provider, Algorithm::Shake128, test_data);
180        assert!(result.is_ok(), "SHAKE128 should work");
181        if let Ok(hash) = result {
182            assert_eq!(hash.len(), 16, "SHAKE128 should produce 16-byte hash");
183        }
184
185        let result = HashOperations::hash(&provider, Algorithm::Shake256, test_data);
186        assert!(result.is_ok(), "SHAKE256 should work");
187        if let Ok(hash) = result {
188            assert_eq!(hash.len(), 32, "SHAKE256 should produce 32-byte hash");
189        }
190
191        let result = HashOperations::hash(&provider, Algorithm::Sha256, test_data);
192        assert!(result.is_ok(), "SHA-256 should work");
193        if let Ok(hash) = result {
194            assert_eq!(hash.len(), 32, "SHA-256 should produce 32-byte hash");
195        }
196    }
197
198    /// Hash inputs are not subject to the AEAD default binding cap (formerly 1 MiB for all payloads).
199    #[test]
200    fn hash_accepts_input_above_legacy_one_mib_policy() {
201        let provider = LibQHashProvider::new().unwrap();
202        let mut data = Vec::with_capacity(1024 * 1024 + 1);
203        data.resize(1024 * 1024 + 1, 0x5Au8);
204        let result = HashOperations::hash(&provider, Algorithm::Sha3_256, &data);
205        assert!(
206            result.is_ok(),
207            "expected SHA3-256 over >1 MiB input to succeed, got {:?}",
208            result.as_ref().err()
209        );
210        assert_eq!(result.unwrap().len(), 32);
211    }
212}