1#[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#[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 pub fn new() -> Result<Self> {
63 Ok(Self {
64 security_validator: SecurityValidator::new()?,
65 })
66 }
67
68 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 self.security_validator
79 .validate_algorithm_category(algorithm, lib_q_core::api::AlgorithmCategory::Hash)?;
80
81 self.security_validator.validate_hash_input(data)?;
83
84 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 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 }
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 } 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 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 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 #[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}