seekable_stream_cipher/
ascon.rs

1use core::cmp;
2
3/// An ASCON-based seekable stream cipher.
4#[derive(Clone, Copy)]
5pub struct StreamCipher {
6    /// The ASCON state
7    st: [u64; 5],
8}
9
10impl StreamCipher {
11    /// The key length in bytes
12    pub const KEY_LENGTH: usize = 32;
13
14    /// The ASCON constants
15    const RKS: [u64; 12] = [
16        0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b,
17    ];
18
19    /// Create a new state with the given key and context.
20    ///
21    /// The key must be 32 bytes long, and must be randomly generated, for example using
22    /// `rand::thread_rng().gen::<[u8; 32]>()` or `getrandom::getrandom()`.
23    ///
24    /// The context is optional can be of any length. It is used to improve multi-user security.
25    pub fn new(key: &[u8; Self::KEY_LENGTH], context: impl AsRef<[u8]>) -> Self {
26        let context = context.as_ref();
27        let st = [0x010080cc00000000, 0, 0, 0, 0];
28
29        let mut state = StreamCipher { st };
30        state.st[1] ^= u64::from_le_bytes(key[0..8].try_into().unwrap());
31        state.st[2] ^= u64::from_le_bytes(key[8..16].try_into().unwrap());
32        state.st[3] ^= u64::from_le_bytes(key[16..24].try_into().unwrap());
33        state.st[4] ^= u64::from_le_bytes(key[24..32].try_into().unwrap());
34        state.permute();
35
36        let mut context = context;
37
38        while context.len() > 32 {
39            let context_part_len = 32;
40            state.st[0] ^= u64::from_le_bytes(context[0..8].try_into().unwrap());
41            state.st[1] ^= u64::from_le_bytes(context[8..16].try_into().unwrap());
42            state.st[2] ^= u64::from_le_bytes(context[16..24].try_into().unwrap());
43            state.st[3] ^= u64::from_le_bytes(context[24..32].try_into().unwrap());
44            context = &context[context_part_len..];
45            state.permute();
46        }
47
48        let context_len = context.len();
49        let mut buf = [0u8; 32];
50        buf[..context_len].copy_from_slice(context);
51        state.st[0] ^= u64::from_le_bytes(buf[0..8].try_into().unwrap());
52        state.st[1] ^= u64::from_le_bytes(buf[8..16].try_into().unwrap());
53        state.st[2] ^= u64::from_le_bytes(buf[16..24].try_into().unwrap());
54        state.st[3] ^= u64::from_le_bytes(buf[24..32].try_into().unwrap());
55        state.st[4] ^= 0x01;
56        state.permute();
57
58        state.st[0] ^= u64::from_le_bytes(key[0..8].try_into().unwrap());
59        state.st[1] ^= u64::from_le_bytes(key[8..16].try_into().unwrap());
60        state.st[2] ^= u64::from_le_bytes(key[16..24].try_into().unwrap());
61        state.st[3] ^= u64::from_le_bytes(key[24..32].try_into().unwrap());
62
63        state
64    }
65
66    /// Squeeze a 16-byte block, and store it in the given buffer.
67    #[inline(always)]
68    fn store_rate(mut self, out: &mut [u8], block_offset: u64) {
69        let mask: [u64; 2] = self.st[0..2].try_into().unwrap();
70        self.st[4] ^= block_offset;
71        self.permute();
72        out[..8].copy_from_slice(&(self.st[3] ^ mask[0]).to_le_bytes());
73        out[8..].copy_from_slice(&(self.st[4] ^ mask[1]).to_le_bytes());
74    }
75
76    /// Squeeze a 16-byte block, and add it to the given buffer.
77    #[inline(always)]
78    fn apply_rate(mut self, out: &mut [u8], block_offset: u64) {
79        let mask: [u64; 2] = self.st[0..2].try_into().unwrap();
80        self.st[4] ^= block_offset;
81        self.permute();
82        let out0 = u64::from_le_bytes(out[..8].try_into().unwrap());
83        let out1 = u64::from_le_bytes(out[8..][..8].try_into().unwrap());
84        out[..8].copy_from_slice(&(self.st[3] ^ out0 ^ mask[0]).to_le_bytes());
85        out[8..].copy_from_slice(&(self.st[4] ^ out1 ^ mask[1]).to_le_bytes());
86    }
87
88    /// Squeeze and return a 16-byte block.
89    #[inline(always)]
90    fn squeeze_rate(self, block_offset: u64) -> [u8; 16] {
91        let mut out = [0u8; 16];
92        self.store_rate(&mut out, block_offset);
93        out
94    }
95
96    /// Fill the given buffer with the keystream starting at the given offset.
97    ///
98    /// The offset is in bytes.
99    ///
100    /// The key stream is deterministic: the same key, context and offset will always produce the same output.
101    pub fn fill(&self, mut out: &mut [u8], start_offset: u64) -> Result<(), &'static str> {
102        if start_offset.checked_add(out.len() as u64).is_none() {
103            return Err("offset would overflow");
104        }
105        let mut block_offset = start_offset / 16;
106        let offset_in_first_block = (start_offset % 16) as usize;
107        let bytes_to_copy = cmp::min(16 - offset_in_first_block, out.len());
108        if bytes_to_copy > 0 {
109            let rate = self.squeeze_rate(block_offset);
110            out[..bytes_to_copy].copy_from_slice(&rate[offset_in_first_block..][..bytes_to_copy]);
111            out = &mut out[bytes_to_copy..];
112        }
113        while out.len() >= 16 {
114            block_offset += 1;
115            self.store_rate(&mut out[..16], block_offset);
116            out = &mut out[16..];
117        }
118        if !out.is_empty() {
119            block_offset += 1;
120            let rate = self.squeeze_rate(block_offset);
121            out.copy_from_slice(&rate[..out.len()]);
122        }
123        Ok(())
124    }
125
126    /// Encrypt or decrypt the given buffer in place, given the offset.
127    ///
128    /// The buffer is modified in place.
129    /// The offset is in bytes.
130    ///
131    /// The key stream is deterministic: the same key, context and offset will always produce the same output.
132    /// This function is equivalent to calling `fill` and then XORing the output with the input.
133    ///
134    /// # Caveats
135    ///
136    /// * There is no integrity.
137    /// * An adversary can flip arbitrary bits in the ciphertext and the corresponding bits in the plaintext will be flipped when decrypted.
138    pub fn apply_keystream(
139        &self,
140        mut out: &mut [u8],
141        start_offset: u64,
142    ) -> Result<(), &'static str> {
143        if start_offset.checked_add(out.len() as u64).is_none() {
144            return Err("offset would overflow");
145        }
146        let mut block_offset = start_offset / 16;
147        let offset_in_first_block = (start_offset % 16) as usize;
148        let bytes_to_copy = cmp::min(16 - offset_in_first_block, out.len());
149        if bytes_to_copy > 0 {
150            let rate = self.squeeze_rate(block_offset);
151            for i in 0..bytes_to_copy {
152                out[i] ^= rate[offset_in_first_block + i];
153            }
154            out = &mut out[bytes_to_copy..];
155        }
156        while out.len() >= 16 {
157            block_offset += 1;
158            self.apply_rate(&mut out[..16], block_offset);
159            out = &mut out[16..];
160        }
161        if !out.is_empty() {
162            block_offset += 1;
163            let rate = self.squeeze_rate(block_offset);
164            for i in 0..out.len() {
165                out[i] ^= rate[i];
166            }
167        }
168        Ok(())
169    }
170
171    #[inline(always)]
172    fn round(&mut self, rk: u64) {
173        let x = &mut self.st;
174        x[2] ^= rk;
175
176        x[0] ^= x[4];
177        x[4] ^= x[3];
178        x[2] ^= x[1];
179        let mut t = [
180            x[0] ^ (!x[1] & x[2]),
181            x[1] ^ (!x[2] & x[3]),
182            x[2] ^ (!x[3] & x[4]),
183            x[3] ^ (!x[4] & x[0]),
184            x[4] ^ (!x[0] & x[1]),
185        ];
186        t[1] ^= t[0];
187        t[3] ^= t[2];
188        t[0] ^= t[4];
189
190        x[2] = t[2] ^ t[2].rotate_right(6 - 1);
191        x[3] = t[3] ^ t[3].rotate_right(17 - 10);
192        x[4] = t[4] ^ t[4].rotate_right(41 - 7);
193        x[0] = t[0] ^ t[0].rotate_right(28 - 19);
194        x[1] = t[1] ^ t[1].rotate_right(61 - 39);
195        x[2] = t[2] ^ x[2].rotate_right(1);
196        x[3] = t[3] ^ x[3].rotate_right(10);
197        x[4] = t[4] ^ x[4].rotate_right(7);
198        x[0] = t[0] ^ x[0].rotate_right(19);
199        x[1] = t[1] ^ x[1].rotate_right(39);
200        x[2] = !x[2];
201    }
202
203    fn permute(&mut self) {
204        for &rk in &Self::RKS {
205            self.round(rk);
206        }
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213
214    #[test]
215    fn test_ascon() {
216        let mut key = [0u8; StreamCipher::KEY_LENGTH];
217        getrandom::getrandom(&mut key).unwrap();
218
219        let st = StreamCipher::new(&key, b"test");
220
221        let mut out = [0u8; 10000];
222        st.apply_keystream(&mut out, 10).unwrap();
223
224        let mut out2 = [0u8; 10000];
225        st.fill(&mut out2, 10).unwrap();
226
227        assert_eq!(out, out2);
228
229        st.fill(&mut out2, 11).unwrap();
230        assert_eq!(out[1..], out2[0..out2.len() - 1]);
231    }
232
233    #[test]
234    fn test_large_context() {
235        let mut key = [0u8; StreamCipher::KEY_LENGTH];
236        getrandom::getrandom(&mut key).unwrap();
237        let context = [0u8; 10000];
238        let _ = StreamCipher::new(&key, context);
239    }
240}