Skip to main content

lib_q_hash/
lib.rs

1//! lib-Q HASH - Post-quantum Hash Functions
2//!
3//! This crate provides implementations of post-quantum hash functions.
4
5#![no_std]
6#![forbid(unsafe_code)]
7#![warn(missing_docs, missing_debug_implementations)]
8
9#[cfg(all(feature = "parallelhash", target_arch = "wasm32"))]
10compile_error!(
11    "parallelhash feature is not supported on wasm32; do not enable 'parallelhash' for WASM builds"
12);
13
14extern crate alloc;
15
16use alloc::boxed::Box;
17use alloc::vec::Vec;
18
19// Re-export digest traits for internal use
20pub use digest::{
21    self,
22    CollisionResistance,
23    CustomizedInit,
24    Digest,
25    ExtendableOutput,
26    ExtendableOutputReset,
27    Update,
28};
29// Re-export core types for public use
30pub use lib_q_core::{
31    Algorithm,
32    Hash,
33    HashContext,
34    Result,
35};
36// Re-export external hash implementations (explicit to avoid ambiguity)
37pub use lib_q_k12::{
38    Kt128,
39    Kt128Reader,
40    Kt256,
41    Kt256Reader,
42};
43pub use lib_q_keccak_digest::{
44    Keccak224,
45    Keccak256,
46    Keccak256Full,
47    Keccak384,
48    Keccak512,
49};
50pub use lib_q_sha3::{
51    Sha3_224,
52    Sha3_256,
53    Sha3_384,
54    Sha3_512,
55    Shake128,
56    Shake128Reader,
57    Shake256,
58    Shake256Reader,
59};
60
61// Internal modules
62mod cshake;
63mod hash_types;
64mod internal_block_api;
65mod kmac;
66mod parallelhash;
67#[cfg(feature = "alloc")]
68mod provider;
69mod sha2_hashes;
70mod shake;
71mod tuplehash;
72mod turbo_shake;
73mod utils;
74
75// Re-export internal implementations
76pub use cshake::{
77    CShake128,
78    CShake128Reader,
79    CShake256,
80    CShake256Reader,
81};
82// Re-export SP800-185 implementations
83pub use kmac::{
84    Kmac128,
85    Kmac128Reader,
86    Kmac256,
87    Kmac256Reader,
88};
89pub use parallelhash::{
90    ParallelHash128,
91    ParallelHash128Reader,
92    ParallelHash256,
93    ParallelHash256Reader,
94};
95// Re-export provider
96#[cfg(feature = "alloc")]
97pub use provider::LibQHashProvider;
98pub use sha2_hashes::{
99    Sha224Hash,
100    Sha256Hash,
101    Sha384Hash,
102    Sha512_224Hash,
103    Sha512_256Hash,
104    Sha512Hash,
105};
106pub use shake::{
107    Shake128 as InternalShake128,
108    Shake128Reader as InternalShake128Reader,
109    Shake256 as InternalShake256,
110    Shake256Reader as InternalShake256Reader,
111};
112pub use tuplehash::{
113    TupleHash128,
114    TupleHash128Reader,
115    TupleHash256,
116    TupleHash256Reader,
117};
118pub use turbo_shake::{
119    TurboShake128,
120    TurboShake128Reader,
121    TurboShake256,
122    TurboShake256Reader,
123};
124pub use utils::MAX_SP800185_FIXED_OUTPUT_BYTES;
125
126// Re-export hash types
127pub use crate::hash_types::{
128    CShake128Hash,
129    CShake256Hash,
130    Keccak224Hash,
131    Keccak256Hash,
132    Keccak384Hash,
133    Keccak512Hash,
134    Kmac128Hash,
135    Kmac256Hash,
136    Kt128Hash,
137    Kt256Hash,
138    ParallelHash128Hash,
139    ParallelHash256Hash,
140    Sha3_224Hash,
141    Sha3_256Hash,
142    Sha3_384Hash,
143    Sha3_512Hash,
144    Shake128Hash,
145    Shake256Hash,
146    TupleHash128Hash,
147    TupleHash256Hash,
148    TurboShake128Hash,
149    TurboShake256Hash,
150};
151
152// Constants for SHA-3 implementation
153/// Length of the Keccak state array
154pub const PLEN: usize = 25;
155/// Default number of rounds for Keccak permutation
156pub const DEFAULT_ROUND_COUNT: usize = 24;
157
158// Paddings
159/// Keccak padding value
160pub const KECCAK_PAD: u8 = 0x01;
161/// SHA-3 padding value
162pub const SHA3_PAD: u8 = 0x06;
163/// SHAKE padding value
164pub const SHAKE_PAD: u8 = 0x1F;
165/// cSHAKE padding value
166pub const CSHAKE_PAD: u8 = 0x04;
167
168/// Hash algorithm types that map to lib-q-core Algorithm enum
169#[derive(Debug, Clone, PartialEq, Eq)]
170pub enum HashAlgorithm {
171    /// SHA-3-224
172    Sha3_224,
173    /// SHA-3-256
174    Sha3_256,
175    /// SHA-3-384
176    Sha3_384,
177    /// SHA-3-512
178    Sha3_512,
179    /// SHAKE128
180    Shake128,
181    /// SHAKE256
182    Shake256,
183    /// cSHAKE128
184    Cshake128,
185    /// cSHAKE256
186    Cshake256,
187    /// KT128 (KangarooTwelve with TurboSHAKE128)
188    Kt128,
189    /// KT256 (KangarooTwelve with TurboSHAKE256)
190    Kt256,
191    /// Keccak-224
192    Keccak224,
193    /// Keccak-256
194    Keccak256,
195    /// Keccak-384
196    Keccak384,
197    /// Keccak-512
198    Keccak512,
199    /// TurboShake128
200    TurboShake128,
201    /// TurboShake256
202    TurboShake256,
203    /// KMAC128
204    Kmac128,
205    /// KMAC256
206    Kmac256,
207    /// TupleHash128
208    TupleHash128,
209    /// TupleHash256
210    TupleHash256,
211    /// ParallelHash128
212    ParallelHash128,
213    /// ParallelHash256
214    ParallelHash256,
215    /// SHA-224
216    Sha224,
217    /// SHA-256
218    Sha256,
219    /// SHA-384
220    Sha384,
221    /// SHA-512
222    Sha512,
223    /// SHA-512/224
224    Sha512_224,
225    /// SHA-512/256
226    Sha512_256,
227}
228
229impl HashAlgorithm {
230    /// Get the output size for this algorithm
231    pub fn output_size(&self) -> usize {
232        match self {
233            HashAlgorithm::Sha3_224 => 28,
234            HashAlgorithm::Sha3_256 => 32,
235            HashAlgorithm::Sha3_384 => 48,
236            HashAlgorithm::Sha3_512 => 64,
237            HashAlgorithm::Shake128 => 16,
238            HashAlgorithm::Shake256 => 32,
239            HashAlgorithm::Cshake128 => 16,
240            HashAlgorithm::Cshake256 => 32,
241            HashAlgorithm::Kt128 => 32,
242            HashAlgorithm::Kt256 => 64,
243            HashAlgorithm::Keccak224 => 28,
244            HashAlgorithm::Keccak256 => 32,
245            HashAlgorithm::Keccak384 => 48,
246            HashAlgorithm::Keccak512 => 64,
247            HashAlgorithm::TurboShake128 => 16,
248            HashAlgorithm::TurboShake256 => 32,
249            HashAlgorithm::Kmac128 => 16,
250            HashAlgorithm::Kmac256 => 32,
251            HashAlgorithm::TupleHash128 => 16,
252            HashAlgorithm::TupleHash256 => 32,
253            HashAlgorithm::ParallelHash128 => 16,
254            HashAlgorithm::ParallelHash256 => 32,
255            HashAlgorithm::Sha224 => 28,
256            HashAlgorithm::Sha256 => 32,
257            HashAlgorithm::Sha384 => 48,
258            HashAlgorithm::Sha512 => 64,
259            HashAlgorithm::Sha512_224 => 28,
260            HashAlgorithm::Sha512_256 => 32,
261        }
262    }
263}
264
265/// Get available hash algorithms
266pub fn available_algorithms() -> Vec<&'static str> {
267    alloc::vec![
268        "sha3-224",
269        "sha3-256",
270        "sha3-384",
271        "sha3-512",
272        "shake128",
273        "shake256",
274        "cshake128",
275        "cshake256",
276        "kt128",
277        "kt256",
278        "kangarootwelve",
279        "keccak224",
280        "keccak256",
281        "keccak384",
282        "keccak512",
283        "turboshake128",
284        "turboshake256",
285        "kmac128",
286        "kmac256",
287        "tuplehash128",
288        "tuplehash256",
289        "parallelhash128",
290        "parallelhash256",
291        "sha-224",
292        "sha-256",
293        "sha-384",
294        "sha-512",
295        "sha-512/224",
296        "sha-512/256",
297    ]
298}
299
300/// Map lib-q-core Algorithm to HashAlgorithm
301pub fn algorithm_to_hash_algorithm(algorithm: Algorithm) -> Result<HashAlgorithm> {
302    match algorithm {
303        Algorithm::Sha3_224 => Ok(HashAlgorithm::Sha3_224),
304        Algorithm::Sha3_256 => Ok(HashAlgorithm::Sha3_256),
305        Algorithm::Sha3_384 => Ok(HashAlgorithm::Sha3_384),
306        Algorithm::Sha3_512 => Ok(HashAlgorithm::Sha3_512),
307        Algorithm::Shake128 => Ok(HashAlgorithm::Shake128),
308        Algorithm::Shake256 => Ok(HashAlgorithm::Shake256),
309        Algorithm::CShake128 => Ok(HashAlgorithm::Cshake128),
310        Algorithm::CShake256 => Ok(HashAlgorithm::Cshake256),
311        Algorithm::Kt128 => Ok(HashAlgorithm::Kt128),
312        Algorithm::Kt256 => Ok(HashAlgorithm::Kt256),
313        Algorithm::Keccak224 => Ok(HashAlgorithm::Keccak224),
314        Algorithm::Keccak256 => Ok(HashAlgorithm::Keccak256),
315        Algorithm::Keccak384 => Ok(HashAlgorithm::Keccak384),
316        Algorithm::Keccak512 => Ok(HashAlgorithm::Keccak512),
317        Algorithm::TurboShake128 => Ok(HashAlgorithm::TurboShake128),
318        Algorithm::TurboShake256 => Ok(HashAlgorithm::TurboShake256),
319        Algorithm::Kmac128 => Ok(HashAlgorithm::Kmac128),
320        Algorithm::Kmac256 => Ok(HashAlgorithm::Kmac256),
321        Algorithm::TupleHash128 => Ok(HashAlgorithm::TupleHash128),
322        Algorithm::TupleHash256 => Ok(HashAlgorithm::TupleHash256),
323        Algorithm::ParallelHash128 => Ok(HashAlgorithm::ParallelHash128),
324        Algorithm::ParallelHash256 => Ok(HashAlgorithm::ParallelHash256),
325        Algorithm::Sha224 => Ok(HashAlgorithm::Sha224),
326        Algorithm::Sha256 => Ok(HashAlgorithm::Sha256),
327        Algorithm::Sha384 => Ok(HashAlgorithm::Sha384),
328        Algorithm::Sha512 => Ok(HashAlgorithm::Sha512),
329        Algorithm::Sha512_224 => Ok(HashAlgorithm::Sha512_224),
330        Algorithm::Sha512_256 => Ok(HashAlgorithm::Sha512_256),
331        _ => Err(lib_q_core::Error::InvalidAlgorithm {
332            algorithm: "Algorithm is not a hash algorithm",
333        }),
334    }
335}
336
337/// Create a hash instance by HashAlgorithm enum
338pub fn create_hash(algorithm: HashAlgorithm) -> Result<Box<dyn lib_q_core::Hash>> {
339    match algorithm {
340        HashAlgorithm::Sha3_224 => Ok(Box::new(Sha3_224Hash::new())),
341        HashAlgorithm::Sha3_256 => Ok(Box::new(Sha3_256Hash::new())),
342        HashAlgorithm::Sha3_384 => Ok(Box::new(Sha3_384Hash::new())),
343        HashAlgorithm::Sha3_512 => Ok(Box::new(Sha3_512Hash::new())),
344        HashAlgorithm::Shake128 => Ok(Box::new(Shake128Hash::new())),
345        HashAlgorithm::Shake256 => Ok(Box::new(Shake256Hash::new())),
346        HashAlgorithm::Cshake128 => Ok(Box::new(CShake128Hash::new())),
347        HashAlgorithm::Cshake256 => Ok(Box::new(CShake256Hash::new())),
348        HashAlgorithm::Kmac128 => Ok(Box::new(Kmac128Hash::new())),
349        HashAlgorithm::Kmac256 => Ok(Box::new(Kmac256Hash::new())),
350        HashAlgorithm::TupleHash128 => Ok(Box::new(TupleHash128Hash::new())),
351        HashAlgorithm::TupleHash256 => Ok(Box::new(TupleHash256Hash::new())),
352        HashAlgorithm::ParallelHash128 => Ok(Box::new(ParallelHash128Hash::new())),
353        HashAlgorithm::ParallelHash256 => Ok(Box::new(ParallelHash256Hash::new())),
354        HashAlgorithm::Kt128 => Ok(Box::new(Kt128Hash::new())),
355        HashAlgorithm::Kt256 => Ok(Box::new(Kt256Hash::new())),
356        HashAlgorithm::Keccak224 => Ok(Box::new(Keccak224Hash::new())),
357        HashAlgorithm::Keccak256 => Ok(Box::new(Keccak256Hash::new())),
358        HashAlgorithm::Keccak384 => Ok(Box::new(Keccak384Hash::new())),
359        HashAlgorithm::Keccak512 => Ok(Box::new(Keccak512Hash::new())),
360        HashAlgorithm::TurboShake128 => Ok(Box::new(TurboShake128Hash::new())),
361        HashAlgorithm::TurboShake256 => Ok(Box::new(TurboShake256Hash::new())),
362        HashAlgorithm::Sha224 => Ok(Box::new(Sha224Hash::new())),
363        HashAlgorithm::Sha256 => Ok(Box::new(Sha256Hash::new())),
364        HashAlgorithm::Sha384 => Ok(Box::new(Sha384Hash::new())),
365        HashAlgorithm::Sha512 => Ok(Box::new(Sha512Hash::new())),
366        HashAlgorithm::Sha512_224 => Ok(Box::new(Sha512_224Hash::new())),
367        HashAlgorithm::Sha512_256 => Ok(Box::new(Sha512_256Hash::new())),
368    }
369}
370
371/// Create a [`HashContext`] with this crate's [`LibQHashProvider`] installed.
372///
373/// The provider is registered so [`HashContext::hash`] can dispatch every hash
374/// [`Algorithm`] supported here. The algorithm is chosen on each `hash` call; this
375/// constructor does not bind a single algorithm.
376///
377/// This matches the wiring used by the `libq` umbrella crate's infallible
378/// `create_hash_context`, except that provider initialization is returned as
379/// [`Result`] instead of panicking.
380///
381/// # Errors
382///
383/// Returns [`Err`] when [`LibQHashProvider::new`] fails (for example, if the
384/// security validator cannot be constructed).
385#[cfg(feature = "alloc")]
386pub fn create_hash_context() -> Result<HashContext> {
387    let provider = LibQHashProvider::new()?;
388    Ok(HashContext::with_provider(alloc::boxed::Box::new(provider)))
389}
390
391#[cfg(test)]
392mod tests {
393    use alloc::format;
394    use alloc::string::String;
395    use core::fmt::{
396        self,
397        Display,
398    };
399
400    use digest::block_api::AlgorithmName;
401
402    use super::*;
403
404    struct AlgName<T: AlgorithmName>(core::marker::PhantomData<T>);
405
406    impl<T: AlgorithmName> Display for AlgName<T> {
407        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408            T::write_alg_name(f)
409        }
410    }
411
412    fn alg_name_string<T: AlgorithmName>() -> String {
413        format!("{}", AlgName::<T>(core::marker::PhantomData))
414    }
415
416    #[test]
417    fn test_available_algorithms() {
418        let algorithms = available_algorithms();
419        assert!(!algorithms.is_empty());
420        assert!(algorithms.contains(&"sha3-224"));
421        assert!(algorithms.contains(&"sha3-256"));
422        assert!(algorithms.contains(&"sha3-384"));
423        assert!(algorithms.contains(&"sha3-512"));
424        assert!(algorithms.contains(&"shake128"));
425        assert!(algorithms.contains(&"shake256"));
426        assert!(algorithms.contains(&"cshake256"));
427        assert!(algorithms.contains(&"kt128"));
428        assert!(algorithms.contains(&"kt256"));
429        assert!(algorithms.contains(&"kangarootwelve"));
430        assert!(algorithms.contains(&"keccak224"));
431        assert!(algorithms.contains(&"keccak256"));
432        assert!(algorithms.contains(&"keccak384"));
433        assert!(algorithms.contains(&"keccak512"));
434        assert!(algorithms.contains(&"sha-256"));
435    }
436
437    #[test]
438    fn test_sha256_known_answer() {
439        let h = Sha256Hash::new();
440        let out = h.hash(b"").expect("sha256");
441        assert_eq!(out.len(), 32);
442        assert_eq!(
443            out.as_slice(),
444            hex_literal::hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
445        );
446    }
447
448    #[test]
449    fn test_cshake_implementations() {
450        // Test cSHAKE256
451        let cshake = CShake256Hash::new();
452        let result = cshake.hash(b"Hello, World!").unwrap();
453        assert_eq!(result.len(), 32);
454    }
455
456    #[test]
457    fn test_cshake_customization() {
458        // Test that different customizations produce different outputs
459        let cshake1 = CShake256Hash::new_customized(b"App1");
460        let cshake2 = CShake256Hash::new_customized(b"App2");
461
462        let hash1 = cshake1.hash(b"test").unwrap();
463        let hash2 = cshake2.hash(b"test").unwrap();
464
465        assert_ne!(hash1, hash2);
466    }
467
468    #[test]
469    fn test_invalid_algorithm_name() {
470        // Test that we can't create a hash with an invalid algorithm
471        // This test is now covered by the algorithm_to_hash_algorithm function
472        let result = algorithm_to_hash_algorithm(Algorithm::MlDsa65); // Not a hash algorithm
473        assert!(result.is_err());
474    }
475
476    #[test]
477    fn hash_algorithm_output_size_exhaustive() {
478        use HashAlgorithm::*;
479        let all = [
480            Sha3_224,
481            Sha3_256,
482            Sha3_384,
483            Sha3_512,
484            Shake128,
485            Shake256,
486            Cshake128,
487            Cshake256,
488            Kt128,
489            Kt256,
490            Keccak224,
491            Keccak256,
492            Keccak384,
493            Keccak512,
494            TurboShake128,
495            TurboShake256,
496            Kmac128,
497            Kmac256,
498            TupleHash128,
499            TupleHash256,
500            ParallelHash128,
501            ParallelHash256,
502            Sha224,
503            Sha256,
504            Sha384,
505            Sha512,
506            Sha512_224,
507            Sha512_256,
508        ];
509        for a in all {
510            assert!(a.output_size() > 0);
511        }
512    }
513
514    #[test]
515    fn create_hash_smoke_all_variants() {
516        use HashAlgorithm::*;
517        let variants = [
518            Sha3_224,
519            Sha3_256,
520            Sha3_384,
521            Sha3_512,
522            Shake128,
523            Shake256,
524            Cshake128,
525            Cshake256,
526            Kmac128,
527            Kmac256,
528            TupleHash128,
529            TupleHash256,
530            ParallelHash128,
531            ParallelHash256,
532            Kt128,
533            Kt256,
534            Keccak224,
535            Keccak256,
536            Keccak384,
537            Keccak512,
538            TurboShake128,
539            TurboShake256,
540            Sha224,
541            Sha256,
542            Sha384,
543            Sha512,
544            Sha512_224,
545            Sha512_256,
546        ];
547        for alg in variants {
548            let h = create_hash(alg.clone()).expect("create_hash");
549            let out = h.hash(b"coverage").expect("hash");
550            assert_eq!(out.len(), alg.output_size());
551        }
552    }
553
554    #[test]
555    fn algorithm_to_hash_algorithm_roundtrip_core() {
556        let pairs = [
557            (Algorithm::Sha3_224, HashAlgorithm::Sha3_224),
558            (Algorithm::Sha3_256, HashAlgorithm::Sha3_256),
559            (Algorithm::Shake128, HashAlgorithm::Shake128),
560            (Algorithm::CShake256, HashAlgorithm::Cshake256),
561            (Algorithm::Kmac128, HashAlgorithm::Kmac128),
562            (Algorithm::TupleHash256, HashAlgorithm::TupleHash256),
563            (Algorithm::ParallelHash128, HashAlgorithm::ParallelHash128),
564            (Algorithm::Kt128, HashAlgorithm::Kt128),
565            (Algorithm::Kt256, HashAlgorithm::Kt256),
566            (Algorithm::Keccak256, HashAlgorithm::Keccak256),
567            (Algorithm::TurboShake128, HashAlgorithm::TurboShake128),
568            (Algorithm::Sha256, HashAlgorithm::Sha256),
569            (Algorithm::Sha512_256, HashAlgorithm::Sha512_256),
570        ];
571        for (core, expected) in pairs {
572            assert_eq!(algorithm_to_hash_algorithm(core).unwrap(), expected);
573        }
574    }
575
576    #[cfg(feature = "alloc")]
577    #[test]
578    fn create_hash_context_returns_functional_context() {
579        let mut ctx = create_hash_context().expect("context");
580        let digest = ctx
581            .hash(Algorithm::Sha3_256, b"lib-q-hash context test")
582            .unwrap();
583        assert_eq!(digest.len(), 32);
584    }
585
586    #[test]
587    fn turboshake_hasher_alg_name_and_debug() {
588        let hasher = TurboShake128::<6>::default();
589        assert_eq!(alg_name_string::<TurboShake128<6>>(), "TurboSHAKE128");
590        assert_eq!(format!("{hasher:?}"), "TurboShake128 { ... }");
591    }
592}