Skip to main content

dcrypt_algorithms/xof/
mod.rs

1//! Extendable Output Functions (XOF)
2//!
3//! This module contains implementations of Extendable Output Functions (XOFs)
4//! which can produce outputs of arbitrary length.
5
6#[cfg(feature = "alloc")]
7extern crate alloc;
8
9use crate::error::{validate, Error, Result};
10#[cfg(feature = "alloc")]
11use dcrypt_internal::zeroing::ZeroizingBytes;
12
13#[cfg(feature = "alloc")]
14pub mod shake;
15
16#[cfg(feature = "alloc")]
17pub mod blake3;
18
19// Re-exports
20#[cfg(feature = "alloc")]
21pub use shake::{ShakeXof128, ShakeXof256};
22
23#[cfg(feature = "alloc")]
24pub use blake3::Blake3Xof;
25
26/// An Extendable Output Function (XOF) produces output of arbitrary length
27#[cfg(feature = "alloc")]
28pub type Xof = ZeroizingBytes;
29
30/// Trait for extendable output functions
31pub trait ExtendableOutputFunction {
32    /// Creates a new instance of the XOF
33    fn new() -> Self;
34
35    /// Updates the XOF state with new data
36    fn update(&mut self, data: &[u8]) -> Result<()>;
37
38    /// Finalizes the XOF state for output
39    fn finalize(&mut self) -> Result<()>;
40
41    /// Squeezes output bytes into the provided buffer
42    fn squeeze(&mut self, output: &mut [u8]) -> Result<()>;
43
44    /// Squeezes the specified number of output bytes into exact-size storage
45    /// that clears itself on drop.
46    #[cfg(feature = "alloc")]
47    fn squeeze_into_vec(&mut self, len: usize) -> Result<ZeroizingBytes>;
48
49    /// Resets the XOF state
50    fn reset(&mut self) -> Result<()>;
51
52    /// Returns the security level in bits
53    fn security_level() -> usize;
54
55    /// Convenience method to generate output in a single call
56    #[cfg(feature = "alloc")]
57    fn generate(data: &[u8], len: usize) -> Result<ZeroizingBytes>
58    where
59        Self: Sized,
60    {
61        validate::parameter(
62            len > 0,
63            "output_length",
64            "XOF output length must be greater than 0",
65        )?;
66
67        let mut xof = Self::new();
68        xof.update(data)?;
69        xof.squeeze_into_vec(len)
70    }
71}
72
73/// Trait for XOF algorithms with compile-time guarantees
74pub trait XofAlgorithm {
75    /// Security level in bits
76    const SECURITY_LEVEL: usize;
77
78    /// Minimum recommended output size in bytes
79    const MIN_OUTPUT_SIZE: usize;
80
81    /// Maximum output size in bytes (None for unlimited)
82    const MAX_OUTPUT_SIZE: Option<usize>;
83
84    /// Algorithm identifier
85    const ALGORITHM_ID: &'static str;
86
87    /// Algorithm name
88    fn name() -> &'static str {
89        Self::ALGORITHM_ID
90    }
91
92    /// Validate output length
93    fn validate_output_length(len: usize) -> Result<()> {
94        validate::parameter(
95            len >= Self::MIN_OUTPUT_SIZE,
96            "output_length",
97            "Output length below minimum recommended size",
98        )?;
99
100        if let Some(max) = Self::MAX_OUTPUT_SIZE {
101            validate::max_length("XOF output", len, max)?;
102        }
103
104        Ok(())
105    }
106}
107
108/// Type-level constants for SHAKE-128
109pub enum Shake128Algorithm {}
110
111impl XofAlgorithm for Shake128Algorithm {
112    const SECURITY_LEVEL: usize = 128;
113    const MIN_OUTPUT_SIZE: usize = 16; // 128 bits
114    const MAX_OUTPUT_SIZE: Option<usize> = None; // Unlimited
115    const ALGORITHM_ID: &'static str = "SHAKE-128";
116}
117
118/// Type-level constants for SHAKE-256
119pub enum Shake256Algorithm {}
120
121impl XofAlgorithm for Shake256Algorithm {
122    const SECURITY_LEVEL: usize = 256;
123    const MIN_OUTPUT_SIZE: usize = 32; // 256 bits
124    const MAX_OUTPUT_SIZE: Option<usize> = None; // Unlimited
125    const ALGORITHM_ID: &'static str = "SHAKE-256";
126}
127
128/// Type-level constants for BLAKE3
129pub enum Blake3Algorithm {}
130
131impl XofAlgorithm for Blake3Algorithm {
132    const SECURITY_LEVEL: usize = 256;
133    const MIN_OUTPUT_SIZE: usize = 32; // 256 bits
134    const MAX_OUTPUT_SIZE: Option<usize> = None; // Unlimited
135    const ALGORITHM_ID: &'static str = "BLAKE3-XOF";
136}
137
138/// Helper trait for XOFs that need keyed variants
139pub trait KeyedXof: ExtendableOutputFunction {
140    /// Creates a new keyed XOF instance
141    fn with_key(key: &[u8]) -> Result<Self>
142    where
143        Self: Sized;
144
145    /// Generates keyed output in a single call
146    #[cfg(feature = "alloc")]
147    fn keyed_generate(key: &[u8], data: &[u8], len: usize) -> Result<ZeroizingBytes>
148    where
149        Self: Sized,
150    {
151        validate::parameter(
152            len > 0,
153            "output_length",
154            "XOF output length must be greater than 0",
155        )?;
156
157        let mut xof = Self::with_key(key)?;
158        xof.update(data)?;
159        xof.squeeze_into_vec(len)
160    }
161}
162
163/// Helper trait for XOFs that support key derivation mode
164pub trait DeriveKeyXof: ExtendableOutputFunction {
165    /// Creates a new XOF instance for key derivation
166    fn for_derive_key(context: &[u8]) -> Result<Self>
167    where
168        Self: Sized;
169
170    /// Derives key material in a single call
171    #[cfg(feature = "alloc")]
172    fn derive_key(context: &[u8], data: &[u8], len: usize) -> Result<ZeroizingBytes>
173    where
174        Self: Sized,
175    {
176        validate::parameter(
177            len > 0,
178            "output_length",
179            "Key derivation output length must be greater than 0",
180        )?;
181
182        let mut xof = Self::for_derive_key(context)?;
183        xof.update(data)?;
184        xof.squeeze_into_vec(len)
185    }
186}
187
188// Error conversion helpers for XOF-specific errors
189impl Error {
190    /// Create an XOF finalization error
191    pub(crate) fn xof_finalized() -> Self {
192        Error::Processing {
193            operation: "XOF",
194            details: "Cannot update after finalization",
195        }
196    }
197
198    /// Create an XOF squeezing error
199    pub(crate) fn xof_squeezing() -> Self {
200        Error::Processing {
201            operation: "XOF",
202            details: "Cannot update after squeezing has begun",
203        }
204    }
205}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210
211    #[test]
212    fn test_xof_algorithm_validation() {
213        // Test SHAKE-128 validation
214        assert!(Shake128Algorithm::validate_output_length(16).is_ok());
215        assert!(Shake128Algorithm::validate_output_length(15).is_err());
216
217        // Test SHAKE-256 validation
218        assert!(Shake256Algorithm::validate_output_length(32).is_ok());
219        assert!(Shake256Algorithm::validate_output_length(31).is_err());
220
221        // Test BLAKE3 validation
222        assert!(Blake3Algorithm::validate_output_length(32).is_ok());
223        assert!(Blake3Algorithm::validate_output_length(31).is_err());
224    }
225}