merlin/
strobe.rs

1//! Minimal implementation of (parts of) Strobe.
2
3use core::ops::{Deref, DerefMut};
4
5use keccak;
6use zeroize::Zeroize;
7
8/// Strobe R value; security level 128 is hardcoded
9const STROBE_R: u8 = 166;
10
11const FLAG_I: u8 = 1;
12const FLAG_A: u8 = 1 << 1;
13const FLAG_C: u8 = 1 << 2;
14const FLAG_T: u8 = 1 << 3;
15const FLAG_M: u8 = 1 << 4;
16const FLAG_K: u8 = 1 << 5;
17
18fn transmute_state(st: &mut AlignedKeccakState) -> &mut [u64; 25] {
19    unsafe { &mut *(st as *mut AlignedKeccakState as *mut [u64; 25]) }
20}
21
22/// This is a wrapper around 200-byte buffer that's always 8-byte aligned
23/// to make pointers to it safely convertible to pointers to [u64; 25]
24/// (since u64 words must be 8-byte aligned)
25#[derive(Clone, Zeroize)]
26#[zeroize(drop)]
27#[repr(align(8))]
28struct AlignedKeccakState([u8; 200]);
29
30/// A Strobe context for the 128-bit security level.
31///
32/// Only `meta-AD`, `AD`, `KEY`, and `PRF` operations are supported.
33#[derive(Clone, Zeroize)]
34pub struct Strobe128 {
35    state: AlignedKeccakState,
36    pos: u8,
37    pos_begin: u8,
38    cur_flags: u8,
39}
40
41impl ::core::fmt::Debug for Strobe128 {
42    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
43        // Ensure that the Strobe state isn't accidentally logged
44        write!(f, "Strobe128: STATE OMITTED")
45    }
46}
47
48impl Strobe128 {
49    pub fn new(protocol_label: &[u8]) -> Strobe128 {
50        let initial_state = {
51            let mut st = AlignedKeccakState([0u8; 200]);
52            st[0..6].copy_from_slice(&[1, STROBE_R + 2, 1, 0, 1, 96]);
53            st[6..18].copy_from_slice(b"STROBEv1.0.2");
54            keccak::f1600(transmute_state(&mut st));
55
56            st
57        };
58
59        let mut strobe = Strobe128 {
60            state: initial_state,
61            pos: 0,
62            pos_begin: 0,
63            cur_flags: 0,
64        };
65
66        strobe.meta_ad(protocol_label, false);
67
68        strobe
69    }
70
71    pub fn meta_ad(&mut self, data: &[u8], more: bool) {
72        self.begin_op(FLAG_M | FLAG_A, more);
73        self.absorb(data);
74    }
75
76    pub fn ad(&mut self, data: &[u8], more: bool) {
77        self.begin_op(FLAG_A, more);
78        self.absorb(data);
79    }
80
81    pub fn prf(&mut self, data: &mut [u8], more: bool) {
82        self.begin_op(FLAG_I | FLAG_A | FLAG_C, more);
83        self.squeeze(data);
84    }
85
86    pub fn key(&mut self, data: &[u8], more: bool) {
87        self.begin_op(FLAG_A | FLAG_C, more);
88        self.overwrite(data);
89    }
90}
91
92impl Strobe128 {
93    fn run_f(&mut self) {
94        self.state[self.pos as usize] ^= self.pos_begin;
95        self.state[(self.pos + 1) as usize] ^= 0x04;
96        self.state[(STROBE_R + 1) as usize] ^= 0x80;
97        keccak::f1600(transmute_state(&mut self.state));
98        self.pos = 0;
99        self.pos_begin = 0;
100    }
101
102    fn absorb(&mut self, data: &[u8]) {
103        for byte in data {
104            self.state[self.pos as usize] ^= byte;
105            self.pos += 1;
106            if self.pos == STROBE_R {
107                self.run_f();
108            }
109        }
110    }
111
112    fn overwrite(&mut self, data: &[u8]) {
113        for byte in data {
114            self.state[self.pos as usize] = *byte;
115            self.pos += 1;
116            if self.pos == STROBE_R {
117                self.run_f();
118            }
119        }
120    }
121
122    fn squeeze(&mut self, data: &mut [u8]) {
123        for byte in data {
124            *byte = self.state[self.pos as usize];
125            self.state[self.pos as usize] = 0;
126            self.pos += 1;
127            if self.pos == STROBE_R {
128                self.run_f();
129            }
130        }
131    }
132
133    fn begin_op(&mut self, flags: u8, more: bool) {
134        // Check if we're continuing an operation
135        if more {
136            assert_eq!(
137                self.cur_flags, flags,
138                "You tried to continue op {:#b} but changed flags to {:#b}",
139                self.cur_flags, flags,
140            );
141            return;
142        }
143
144        // Skip adjusting direction information (we just use AD, PRF)
145        assert_eq!(
146            flags & FLAG_T,
147            0u8,
148            "You used the T flag, which this implementation doesn't support"
149        );
150
151        let old_begin = self.pos_begin;
152        self.pos_begin = self.pos + 1;
153        self.cur_flags = flags;
154
155        self.absorb(&[old_begin, flags]);
156
157        // Force running F if C or K is set
158        let force_f = 0 != (flags & (FLAG_C | FLAG_K));
159
160        if force_f && self.pos != 0 {
161            self.run_f();
162        }
163    }
164}
165
166impl Deref for AlignedKeccakState {
167    type Target = [u8; 200];
168
169    fn deref(&self) -> &Self::Target {
170        &self.0
171    }
172}
173
174impl DerefMut for AlignedKeccakState {
175    fn deref_mut(&mut self) -> &mut Self::Target {
176        &mut self.0
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use strobe_rs::{self, SecParam};
183
184    #[test]
185    fn test_conformance() {
186        let mut s1 = super::Strobe128::new(b"Conformance Test Protocol");
187        let mut s2 = strobe_rs::Strobe::new(b"Conformance Test Protocol", SecParam::B128);
188
189        // meta-AD(b"msg"); AD(msg)
190
191        let msg = [99u8; 1024];
192
193        s1.meta_ad(b"ms", false);
194        s1.meta_ad(b"g", true);
195        s1.ad(&msg, false);
196
197        s2.meta_ad(b"ms", false);
198        s2.meta_ad(b"g", true);
199        s2.ad(&msg, false);
200
201        // meta-AD(b"prf"); PRF()
202
203        let mut prf1 = [0u8; 32];
204        s1.meta_ad(b"prf", false);
205        s1.prf(&mut prf1, false);
206
207        let mut prf2 = [0u8; 32];
208        s2.meta_ad(b"prf", false);
209        s2.prf(&mut prf2, false);
210
211        assert_eq!(prf1, prf2);
212
213        // meta-AD(b"key"); KEY(prf output)
214
215        s1.meta_ad(b"key", false);
216        s1.key(&prf1, false);
217
218        s2.meta_ad(b"key", false);
219        s2.key(&prf2, false);
220
221        // meta-AD(b"prf"); PRF()
222
223        let mut prf1 = [0u8; 32];
224        s1.meta_ad(b"prf", false);
225        s1.prf(&mut prf1, false);
226
227        let mut prf2 = [0u8; 32];
228        s2.meta_ad(b"prf", false);
229        s2.prf(&mut prf2, false);
230
231        assert_eq!(prf1, prf2);
232    }
233}