quantacore/
hash.rs

1//! Hash and MAC operations.
2//!
3//! This module provides hardware-accelerated hashing, HMAC, and HKDF.
4
5use crate::device::Device;
6use crate::error::{check_error, Result};
7use crate::ffi;
8use crate::types::HashAlgorithm;
9
10/// Hash context for incremental hashing.
11///
12/// Use this for hashing data that arrives in chunks.
13pub struct HashContext {
14    handle: ffi::quac_hash_ctx_t,
15    algorithm: HashAlgorithm,
16}
17
18impl HashContext {
19    /// Create a new hash context.
20    fn new(handle: ffi::quac_hash_ctx_t, algorithm: HashAlgorithm) -> Self {
21        Self { handle, algorithm }
22    }
23
24    /// Update the hash with additional data.
25    pub fn update(&mut self, data: &[u8]) -> Result<()> {
26        let result = unsafe { ffi::quac_hash_update(self.handle, data.as_ptr(), data.len()) };
27        check_error(result)
28    }
29
30    /// Finalize and get the digest.
31    ///
32    /// The context is consumed and cannot be used after this.
33    pub fn finalize(self) -> Result<Vec<u8>> {
34        let size = self.algorithm.digest_size().unwrap_or(64);
35        let mut digest = vec![0u8; size];
36        let mut digest_len = size;
37
38        let result = unsafe {
39            ffi::quac_hash_final(self.handle, digest.as_mut_ptr(), &mut digest_len)
40        };
41
42        // Don't run drop since finalize already cleans up
43        std::mem::forget(self);
44
45        check_error(result)?;
46        digest.truncate(digest_len);
47
48        Ok(digest)
49    }
50
51    /// Get the algorithm.
52    pub fn algorithm(&self) -> HashAlgorithm {
53        self.algorithm
54    }
55}
56
57impl Drop for HashContext {
58    fn drop(&mut self) {
59        unsafe {
60            ffi::quac_hash_free(self.handle);
61        }
62    }
63}
64
65// Safety: Hash context is thread-safe
66unsafe impl Send for HashContext {}
67
68impl std::fmt::Debug for HashContext {
69    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70        f.debug_struct("HashContext")
71            .field("algorithm", &self.algorithm)
72            .finish()
73    }
74}
75
76/// Hash subsystem.
77///
78/// Provides hardware-accelerated hash functions including
79/// SHA-2, SHA-3, SHAKE, HMAC, and HKDF.
80///
81/// # Example
82///
83/// ```no_run
84/// use quantacore::{initialize, open_first_device, HashAlgorithm};
85///
86/// initialize().unwrap();
87/// let device = open_first_device().unwrap();
88/// let hash = device.hash();
89///
90/// // One-shot hashing
91/// let digest = hash.sha256(b"Hello, World!").unwrap();
92/// println!("SHA-256: {}", hex::encode(&digest));
93///
94/// // Incremental hashing
95/// let mut ctx = hash.create_context(HashAlgorithm::Sha3_256).unwrap();
96/// ctx.update(b"Hello, ").unwrap();
97/// ctx.update(b"World!").unwrap();
98/// let digest = ctx.finalize().unwrap();
99/// ```
100#[derive(Clone)]
101pub struct Hash {
102    device: Device,
103}
104
105impl Hash {
106    /// Create a new hash subsystem handle.
107    pub(crate) fn new(device: Device) -> Self {
108        Self { device }
109    }
110
111    /// Compute a hash digest.
112    ///
113    /// # Arguments
114    ///
115    /// * `algorithm` - The hash algorithm to use
116    /// * `data` - The data to hash
117    ///
118    /// # Returns
119    ///
120    /// The hash digest.
121    pub fn hash(&self, algorithm: HashAlgorithm, data: &[u8]) -> Result<Vec<u8>> {
122        let size = algorithm.digest_size().unwrap_or(64);
123        let mut digest = vec![0u8; size];
124        let mut digest_len = size;
125
126        let result = unsafe {
127            ffi::quac_hash(
128                self.device.handle(),
129                algorithm.to_raw(),
130                data.as_ptr(),
131                data.len(),
132                digest.as_mut_ptr(),
133                &mut digest_len,
134            )
135        };
136
137        check_error(result)?;
138        digest.truncate(digest_len);
139
140        Ok(digest)
141    }
142
143    /// Compute SHA-256 digest.
144    pub fn sha256(&self, data: &[u8]) -> Result<Vec<u8>> {
145        self.hash(HashAlgorithm::Sha256, data)
146    }
147
148    /// Compute SHA-384 digest.
149    pub fn sha384(&self, data: &[u8]) -> Result<Vec<u8>> {
150        self.hash(HashAlgorithm::Sha384, data)
151    }
152
153    /// Compute SHA-512 digest.
154    pub fn sha512(&self, data: &[u8]) -> Result<Vec<u8>> {
155        self.hash(HashAlgorithm::Sha512, data)
156    }
157
158    /// Compute SHA3-256 digest.
159    pub fn sha3_256(&self, data: &[u8]) -> Result<Vec<u8>> {
160        self.hash(HashAlgorithm::Sha3_256, data)
161    }
162
163    /// Compute SHA3-384 digest.
164    pub fn sha3_384(&self, data: &[u8]) -> Result<Vec<u8>> {
165        self.hash(HashAlgorithm::Sha3_384, data)
166    }
167
168    /// Compute SHA3-512 digest.
169    pub fn sha3_512(&self, data: &[u8]) -> Result<Vec<u8>> {
170        self.hash(HashAlgorithm::Sha3_512, data)
171    }
172
173    /// Compute SHAKE128 output.
174    ///
175    /// # Arguments
176    ///
177    /// * `data` - The input data
178    /// * `output_len` - The desired output length in bytes
179    pub fn shake128(&self, data: &[u8], output_len: usize) -> Result<Vec<u8>> {
180        self.shake(HashAlgorithm::Shake128, data, output_len)
181    }
182
183    /// Compute SHAKE256 output.
184    ///
185    /// # Arguments
186    ///
187    /// * `data` - The input data
188    /// * `output_len` - The desired output length in bytes
189    pub fn shake256(&self, data: &[u8], output_len: usize) -> Result<Vec<u8>> {
190        self.shake(HashAlgorithm::Shake256, data, output_len)
191    }
192
193    /// Compute SHAKE output.
194    fn shake(&self, algorithm: HashAlgorithm, data: &[u8], output_len: usize) -> Result<Vec<u8>> {
195        let mut output = vec![0u8; output_len];
196        let mut len = output_len;
197
198        let result = unsafe {
199            ffi::quac_hash(
200                self.device.handle(),
201                algorithm.to_raw(),
202                data.as_ptr(),
203                data.len(),
204                output.as_mut_ptr(),
205                &mut len,
206            )
207        };
208
209        check_error(result)?;
210        output.truncate(len);
211
212        Ok(output)
213    }
214
215    /// Create a hash context for incremental hashing.
216    ///
217    /// # Example
218    ///
219    /// ```no_run
220    /// # use quantacore::{initialize, open_first_device, HashAlgorithm};
221    /// # initialize().unwrap();
222    /// # let device = open_first_device().unwrap();
223    /// # let hash = device.hash();
224    /// let mut ctx = hash.create_context(HashAlgorithm::Sha256).unwrap();
225    /// ctx.update(b"part 1").unwrap();
226    /// ctx.update(b"part 2").unwrap();
227    /// let digest = ctx.finalize().unwrap();
228    /// ```
229    pub fn create_context(&self, algorithm: HashAlgorithm) -> Result<HashContext> {
230        let mut handle = std::ptr::null_mut();
231        let result = unsafe {
232            ffi::quac_hash_init(self.device.handle(), algorithm.to_raw(), &mut handle)
233        };
234        check_error(result)?;
235        Ok(HashContext::new(handle, algorithm))
236    }
237
238    /// Compute HMAC.
239    ///
240    /// # Arguments
241    ///
242    /// * `algorithm` - The hash algorithm to use
243    /// * `key` - The HMAC key
244    /// * `data` - The data to authenticate
245    ///
246    /// # Returns
247    ///
248    /// The MAC tag.
249    pub fn hmac(&self, algorithm: HashAlgorithm, key: &[u8], data: &[u8]) -> Result<Vec<u8>> {
250        let size = algorithm.digest_size().unwrap_or(64);
251        let mut mac = vec![0u8; size];
252        let mut mac_len = size;
253
254        let result = unsafe {
255            ffi::quac_hmac(
256                self.device.handle(),
257                algorithm.to_raw(),
258                key.as_ptr(),
259                key.len(),
260                data.as_ptr(),
261                data.len(),
262                mac.as_mut_ptr(),
263                &mut mac_len,
264            )
265        };
266
267        check_error(result)?;
268        mac.truncate(mac_len);
269
270        Ok(mac)
271    }
272
273    /// Compute HMAC-SHA256.
274    pub fn hmac_sha256(&self, key: &[u8], data: &[u8]) -> Result<Vec<u8>> {
275        self.hmac(HashAlgorithm::Sha256, key, data)
276    }
277
278    /// Compute HMAC-SHA384.
279    pub fn hmac_sha384(&self, key: &[u8], data: &[u8]) -> Result<Vec<u8>> {
280        self.hmac(HashAlgorithm::Sha384, key, data)
281    }
282
283    /// Compute HMAC-SHA512.
284    pub fn hmac_sha512(&self, key: &[u8], data: &[u8]) -> Result<Vec<u8>> {
285        self.hmac(HashAlgorithm::Sha512, key, data)
286    }
287
288    /// Derive a key using HKDF.
289    ///
290    /// # Arguments
291    ///
292    /// * `algorithm` - The hash algorithm to use
293    /// * `ikm` - Input keying material
294    /// * `salt` - Optional salt (can be empty)
295    /// * `info` - Optional context info (can be empty)
296    /// * `output_len` - Desired output length
297    ///
298    /// # Returns
299    ///
300    /// The derived key material.
301    pub fn hkdf(
302        &self,
303        algorithm: HashAlgorithm,
304        ikm: &[u8],
305        salt: &[u8],
306        info: &[u8],
307        output_len: usize,
308    ) -> Result<Vec<u8>> {
309        let mut okm = vec![0u8; output_len];
310
311        let result = unsafe {
312            ffi::quac_hkdf(
313                self.device.handle(),
314                algorithm.to_raw(),
315                ikm.as_ptr(),
316                ikm.len(),
317                salt.as_ptr(),
318                salt.len(),
319                info.as_ptr(),
320                info.len(),
321                okm.as_mut_ptr(),
322                output_len,
323            )
324        };
325
326        check_error(result)?;
327        Ok(okm)
328    }
329
330    /// Derive a key using HKDF-SHA256.
331    pub fn hkdf_sha256(
332        &self,
333        ikm: &[u8],
334        salt: &[u8],
335        info: &[u8],
336        output_len: usize,
337    ) -> Result<Vec<u8>> {
338        self.hkdf(HashAlgorithm::Sha256, ikm, salt, info, output_len)
339    }
340}
341
342impl std::fmt::Debug for Hash {
343    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
344        f.debug_struct("Hash").finish()
345    }
346}