dusk_safe/
lib.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7#![no_std]
8#![doc = include_str!("../README.md")]
9#![deny(missing_docs)]
10#![deny(rustdoc::broken_intra_doc_links)]
11
12extern crate alloc;
13use alloc::vec::Vec;
14
15mod error;
16mod sponge;
17
18pub use error::Error;
19pub use sponge::{Safe, Sponge};
20
21#[cfg(feature = "encryption")]
22mod encryption;
23#[cfg(feature = "encryption")]
24pub use encryption::{decrypt, encrypt, Encryption};
25
26/// Enum to encode the calls to [`Sponge::absorb`] and [`Sponge::squeeze`] that
27/// make the IO-pattern.
28///
29/// An implementation must forbid any further usage of the sponge and any of
30/// its internal data if this pattern is not followed. In particular, the output
31/// from any previous calls to [`Sponge::squeeze`] must not be used.
32#[derive(Debug, Clone, Copy, PartialEq)]
33pub enum Call {
34    /// Absorb the specified amount of elements into the state.
35    Absorb(usize),
36    /// Squeeze the specified amount of elements from the state.
37    Squeeze(usize),
38}
39
40impl Call {
41    /// Returns the length of the call.
42    pub fn call_len(&self) -> &usize {
43        match self {
44            Call::Absorb(len) => len,
45            Call::Squeeze(len) => len,
46        }
47    }
48}
49
50/// Encode the input for the tag for the sponge instance, using the
51/// domain-separator and IO-pattern.
52///
53/// This function returns an error if the IO-pattern is not sensible.
54///
55/// # Parameters
56///
57/// - `iopattern`: A slice of `Call` enum representing the IO-pattern.
58/// - `domain_sep`: The domain separator to be used for encoding.
59///
60/// # Returns
61///
62/// A `Result` containing a vector of `u8` on success, or an `Error` if the
63/// IO-pattern is not valid.
64fn tag_input(
65    iopattern: impl AsRef<[Call]>,
66    domain_sep: u64,
67) -> Result<Vec<u8>, Error> {
68    // make sure the IO-pattern is valid: start with absorb, end with squeeze
69    // and none of the calls have a len == 0
70    validate_io_pattern(iopattern.as_ref())?;
71
72    // ABSORB_MASK = 0b10000000_00000000_00000000_00000000
73    const ABSORB_MASK: u32 = 0x8000_0000;
74
75    // we know that the first call needs to be to absorb so we can initialize
76    // the vec
77    let mut input_u32 = Vec::new();
78    input_u32.push(ABSORB_MASK);
79
80    // Aggregate and encode calls to absorb and squeeze
81    iopattern.as_ref().iter().for_each(|call| {
82        // get a mutable ref to the previously encoded call
83        // Note: This is safe since we initialized the vector with one element
84        let l = input_u32.len();
85        let prev = &mut input_u32[l - 1];
86        match call {
87            // if both this and the previous calls are to absorb, aggregate them
88            Call::Absorb(len) if *prev & ABSORB_MASK != 0 => {
89                *prev += *len as u32
90            }
91            // else add an encoded call to absorb
92            Call::Absorb(len) => input_u32.push(ABSORB_MASK + *len as u32),
93            // if both this and the previous calls are to squeeze, aggregate
94            // them
95            Call::Squeeze(len) if *prev & ABSORB_MASK == 0 => {
96                *prev += *len as u32
97            }
98            // else add an encoded call to squeeze
99            Call::Squeeze(len) => input_u32.push(*len as u32),
100        }
101    });
102
103    // Convert hash input to an array of u8, using big endian conversion
104    let mut input: Vec<u8> = input_u32
105        .iter()
106        .flat_map(|u32_int| u32_int.to_be_bytes().into_iter())
107        .collect();
108
109    // Add the domain separator to the hash input
110    input.extend(domain_sep.to_be_bytes());
111
112    Ok(input)
113}
114
115/// Check that the IO-pattern is sensible. This means that:
116/// - It doesn't start with a call to squeeze
117/// - It doesn't end with a call to absorb
118/// - Every call to absorb or squeeze has a length between 0 < len < 2^31
119///
120/// # Parameters
121///
122/// - `iopattern`: A slice of `Call` enum representing the IO-pattern.
123///
124/// # Returns
125///
126/// A `Result` indicating success if the IO-pattern is valid, otherwise an
127/// `Error`.
128fn validate_io_pattern(iopattern: impl AsRef<[Call]>) -> Result<(), Error> {
129    // make sure the IO-pattern starts with a call to absorb and ends with a
130    // call to squeeze
131    match (iopattern.as_ref().first(), iopattern.as_ref().last()) {
132        (Some(Call::Absorb(_)), Some(Call::Squeeze(_))) => {}
133        _ => return Err(Error::InvalidIOPattern),
134    }
135
136    // check that every call to absorb or squeeze has a length between:
137    // 0 < len < 2^31
138    const MAX_LEN: usize = u32::MAX as usize >> 1;
139    if iopattern.as_ref().iter().any(|call| {
140        let call_len = *call.call_len();
141        call_len == 0 || call_len > MAX_LEN
142    }) {
143        Err(Error::InvalidIOPattern)
144    } else {
145        Ok(())
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    extern crate std;
152    use std::vec;
153
154    use super::*;
155
156    #[test]
157    fn test_validate_io_pattern() {
158        // test valid
159        let iopattern = vec![Call::Absorb(42), Call::Squeeze(3)];
160        assert!(validate_io_pattern(&iopattern).is_ok());
161
162        let iopattern = vec![
163            Call::Absorb(42),
164            Call::Absorb(5),
165            Call::Squeeze(4),
166            Call::Squeeze(3),
167        ];
168        assert!(validate_io_pattern(&iopattern).is_ok());
169
170        let iopattern = vec![
171            Call::Absorb(42),
172            Call::Absorb(5),
173            Call::Squeeze(4),
174            Call::Absorb(5),
175            Call::Squeeze(3),
176            Call::Squeeze(3),
177        ];
178        assert!(validate_io_pattern(&iopattern).is_ok());
179
180        let iopattern = vec![
181            Call::Absorb(42),
182            Call::Squeeze(4),
183            Call::Absorb(5),
184            Call::Squeeze(4),
185            Call::Absorb(5),
186            Call::Squeeze(3),
187            Call::Absorb(5),
188            Call::Squeeze(3),
189        ];
190        assert!(validate_io_pattern(&iopattern).is_ok());
191
192        // test invalid
193        let iopattern = vec![];
194        assert!(validate_io_pattern(&iopattern).is_err());
195
196        let iopattern = vec![Call::Absorb(2)];
197        assert!(validate_io_pattern(&iopattern).is_err());
198
199        let iopattern = vec![Call::Squeeze(2)];
200        assert!(validate_io_pattern(&iopattern).is_err());
201
202        let iopattern = vec![Call::Absorb(0), Call::Squeeze(2)];
203        assert!(validate_io_pattern(&iopattern).is_err());
204
205        let iopattern = vec![Call::Absorb(42), Call::Squeeze(0)];
206        assert!(validate_io_pattern(&iopattern).is_err());
207
208        let iopattern =
209            vec![Call::Squeeze(42), Call::Absorb(3), Call::Squeeze(4)];
210        assert!(validate_io_pattern(&iopattern).is_err());
211
212        let iopattern = vec![
213            Call::Absorb(42),
214            Call::Absorb(3),
215            Call::Squeeze(4),
216            Call::Absorb(3),
217        ];
218        assert!(validate_io_pattern(&iopattern).is_err());
219
220        let iopattern = vec![
221            Call::Absorb(42),
222            Call::Absorb(3),
223            Call::Squeeze(0),
224            Call::Absorb(3),
225            Call::Squeeze(4),
226        ];
227        assert!(validate_io_pattern(&iopattern).is_err());
228
229        let iopattern =
230            vec![Call::Absorb(3), Call::Absorb(1 << 31), Call::Squeeze(1)];
231        assert!(validate_io_pattern(&iopattern).is_err());
232    }
233
234    #[test]
235    fn test_tag_input() -> Result<(), Error> {
236        let domain_sep = 42;
237
238        // check unequal patterns fail
239        let pattern1 = vec![Call::Absorb(2), Call::Squeeze(10)];
240        let pattern2 = vec![Call::Absorb(2), Call::Squeeze(1)];
241        assert_ne!(
242            tag_input(&pattern1, domain_sep)?,
243            tag_input(&pattern2, domain_sep)?
244        );
245
246        // check patterns whose aggregate are equal
247        let pattern1 = vec![Call::Absorb(2), Call::Squeeze(1)];
248        let pattern2 = vec![Call::Absorb(2), Call::Squeeze(1)];
249        assert_eq!(
250            tag_input(&pattern1, domain_sep)?,
251            tag_input(&pattern2, domain_sep)?
252        );
253
254        let pattern1 = vec![Call::Absorb(1), Call::Absorb(1), Call::Squeeze(1)];
255        let pattern2 = vec![Call::Absorb(2), Call::Squeeze(1)];
256        assert_eq!(
257            tag_input(&pattern1, domain_sep)?,
258            tag_input(&pattern2, domain_sep)?
259        );
260
261        let pattern1 = vec![Call::Absorb(2), Call::Squeeze(10)];
262        let pattern2 = vec![
263            Call::Absorb(2),
264            Call::Squeeze(1),
265            Call::Squeeze(1),
266            Call::Squeeze(8),
267        ];
268        assert_eq!(
269            tag_input(&pattern1, domain_sep)?,
270            tag_input(&pattern2, domain_sep)?
271        );
272
273        let pattern1 = vec![
274            Call::Absorb(2),
275            Call::Absorb(2),
276            Call::Squeeze(1),
277            Call::Squeeze(1),
278            Call::Squeeze(1),
279            Call::Absorb(2),
280            Call::Absorb(2),
281            Call::Squeeze(1),
282            Call::Squeeze(8),
283        ];
284        let pattern2 = vec![
285            Call::Absorb(3),
286            Call::Absorb(1),
287            Call::Squeeze(2),
288            Call::Squeeze(1),
289            Call::Absorb(1),
290            Call::Absorb(3),
291            Call::Squeeze(5),
292            Call::Squeeze(4),
293        ];
294        assert_eq!(
295            tag_input(&pattern1, domain_sep)?,
296            tag_input(&pattern2, domain_sep)?
297        );
298
299        Ok(())
300    }
301}