hmac_sha1_compact/
lib.rs

1//! A small, self-contained SHA1 and HMAC-SHA1 implementation
2//! (C) Frank Denis <fdenis [at] fastly [dot] com>
3
4#![no_std]
5#![allow(
6    non_snake_case,
7    clippy::cast_lossless,
8    clippy::eq_op,
9    clippy::identity_op,
10    clippy::many_single_char_names,
11    clippy::needless_range_loop,
12    clippy::unreadable_literal
13)]
14
15#[inline(always)]
16fn load_be(base: &[u8], offset: usize) -> u32 {
17    let addr = &base[offset..];
18    (addr[3] as u32) | (addr[2] as u32) << 8 | (addr[1] as u32) << 16 | (addr[0] as u32) << 24
19}
20
21#[inline(always)]
22fn store_be(base: &mut [u8], offset: usize, x: u32) {
23    let addr = &mut base[offset..];
24    addr[3] = x as u8;
25    addr[2] = (x >> 8) as u8;
26    addr[1] = (x >> 16) as u8;
27    addr[0] = (x >> 24) as u8;
28}
29
30#[derive(Copy, Clone)]
31struct State([u32; 5]);
32
33impl State {
34    fn new() -> Self {
35        State([0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0])
36    }
37
38    fn store(&self, out: &mut [u8]) {
39        for (i, &e) in self.0.iter().enumerate() {
40            store_be(out, i * 4, e);
41        }
42    }
43
44    fn blocks(&mut self, mut input: &[u8]) -> usize {
45        let mut inlen = input.len();
46        let mut w = [0u32; 80];
47        while inlen >= 64 {
48            for i in 0..16 {
49                w[i] = load_be(input, i * 4);
50            }
51            for i in 16..80 {
52                w[i] = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]).rotate_left(1);
53            }
54            let mut t = self.0;
55
56            for (i, wi) in w.iter().enumerate() {
57                let (f, k) = match i {
58                    0..=19 => (t[1] & t[2] | !t[1] & t[3], 0x5a827999),
59                    20..=39 => (t[1] ^ t[2] ^ t[3], 0x6ed9eba1),
60                    40..=59 => (t[1] & t[2] | t[1] & t[3] | t[2] & t[3], 0x8f1bbcdc),
61                    60..=79 => (t[1] ^ t[2] ^ t[3], 0xca62c1d6),
62                    _ => unreachable!(),
63                };
64                let g = t[0]
65                    .rotate_left(5)
66                    .wrapping_add(f.wrapping_add(t[4].wrapping_add(wi.wrapping_add(k))));
67                t[4] = t[3];
68                t[3] = t[2];
69                t[2] = t[1].rotate_left(30);
70                t[1] = t[0];
71                t[0] = g;
72            }
73            for (i, s) in self.0.iter_mut().enumerate() {
74                *s = s.wrapping_add(t[i]);
75            }
76            input = &input[64..];
77            inlen -= 64;
78        }
79        inlen
80    }
81}
82
83#[derive(Copy, Clone)]
84pub struct Hash {
85    state: State,
86    w: [u8; 64],
87    r: usize,
88    len: usize,
89}
90
91impl Hash {
92    pub fn new() -> Hash {
93        Hash {
94            state: State::new(),
95            r: 0,
96            w: [0u8; 64],
97            len: 0,
98        }
99    }
100
101    fn _update<T: AsRef<[u8]>>(&mut self, input: T) {
102        let input = input.as_ref();
103        let mut n = input.len();
104        self.len += n;
105        let av = 64 - self.r;
106        let tc = ::core::cmp::min(n, av);
107        self.w[self.r..self.r + tc].copy_from_slice(&input[0..tc]);
108        self.r += tc;
109        n -= tc;
110        let pos = tc;
111        if self.r == 64 {
112            self.state.blocks(&self.w);
113            self.r = 0;
114        }
115        if self.r == 0 && n > 0 {
116            let rb = self.state.blocks(&input[pos..]);
117            if rb > 0 {
118                self.w[..rb].copy_from_slice(&input[pos + n - rb..]);
119                self.r = rb;
120            }
121        }
122    }
123
124    /// Absorb content
125    pub fn update<T: AsRef<[u8]>>(&mut self, input: T) {
126        self._update(input)
127    }
128
129    /// Compute SHA1(absorbed content)
130    pub fn finalize(mut self) -> [u8; 20] {
131        let mut padded = [0u8; 128];
132        padded[..self.r].copy_from_slice(&self.w[..self.r]);
133        padded[self.r] = 0x80;
134        let r = if self.r < 56 { 64 } else { 128 };
135        let bits = self.len * 8;
136        for i in 0..8 {
137            padded[r - 8 + i] = (bits as u64 >> (56 - i * 8)) as u8;
138        }
139        self.state.blocks(&padded[..r]);
140        let mut out = [0u8; 20];
141        self.state.store(&mut out);
142        out
143    }
144
145    /// Compute SHA1(`input`)
146    pub fn hash(input: &[u8]) -> [u8; 20] {
147        let mut h = Hash::new();
148        h.update(input);
149        h.finalize()
150    }
151}
152
153impl Default for Hash {
154    fn default() -> Self {
155        Self::new()
156    }
157}
158
159pub struct HMAC;
160
161impl HMAC {
162    /// Compute HMAC-SHA1(`input`, `k`)
163    pub fn mac(input: &[u8], k: &[u8]) -> [u8; 20] {
164        let mut hk = [0u8; 20];
165        let k2 = if k.len() > 64 {
166            hk.copy_from_slice(&Hash::hash(k));
167            &hk
168        } else {
169            k
170        };
171        let mut padded = [0x36; 64];
172        for (p, &k) in padded.iter_mut().zip(k2.iter()) {
173            *p ^= k;
174        }
175        let mut ih = Hash::new();
176        ih.update(&padded[..]);
177        ih.update(input);
178
179        for p in padded.iter_mut() {
180            *p ^= 0x6a;
181        }
182        let mut oh = Hash::new();
183        oh.update(&padded[..]);
184        oh.update(ih.finalize());
185        oh.finalize()
186    }
187}
188
189#[cfg(feature = "traits09")]
190mod digest_trait09 {
191    use digest09::consts::{U20, U64};
192    use digest09::{BlockInput, FixedOutputDirty, Output, Reset, Update};
193
194    use super::Hash;
195
196    impl BlockInput for Hash {
197        type BlockSize = U64;
198    }
199
200    impl Update for Hash {
201        fn update(&mut self, input: impl AsRef<[u8]>) {
202            self._update(input)
203        }
204    }
205
206    impl FixedOutputDirty for Hash {
207        type OutputSize = U20;
208
209        fn finalize_into_dirty(&mut self, out: &mut Output<Self>) {
210            let h = self.finalize();
211            out.copy_from_slice(&h);
212        }
213    }
214
215    impl Reset for Hash {
216        fn reset(&mut self) {
217            *self = Self::new()
218        }
219    }
220}
221
222/// Wrapped `Hash` type for the `Digest` trait.
223#[cfg(feature = "traits010")]
224pub type WrappedHash = digest010::core_api::CoreWrapper<Hash>;
225
226#[cfg(feature = "traits010")]
227mod digest_trait010 {
228    use core::fmt;
229
230    use digest010::{
231        block_buffer::Eager,
232        const_oid::{AssociatedOid, ObjectIdentifier},
233        consts::{U20, U64},
234        core_api::{
235            AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, FixedOutputCore,
236            OutputSizeUser, Reset, UpdateCore,
237        },
238        HashMarker,
239    };
240
241    use super::Hash;
242
243    impl AssociatedOid for Hash {
244        const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.14.3.2.26");
245    }
246
247    impl AlgorithmName for Hash {
248        fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
249            f.write_str("Sha1")
250        }
251    }
252
253    impl HashMarker for Hash {}
254
255    impl BufferKindUser for Hash {
256        type BufferKind = Eager;
257    }
258
259    impl BlockSizeUser for Hash {
260        type BlockSize = U64;
261    }
262
263    impl OutputSizeUser for Hash {
264        type OutputSize = U20;
265    }
266
267    impl UpdateCore for Hash {
268        #[inline]
269        fn update_blocks(&mut self, blocks: &[Block<Self>]) {
270            for block in blocks {
271                self._update(block)
272            }
273        }
274    }
275
276    impl FixedOutputCore for Hash {
277        fn finalize_fixed_core(
278            &mut self,
279            buffer: &mut Buffer<Self>,
280            out: &mut digest010::Output<Self>,
281        ) {
282            self._update(buffer.get_data());
283            let h = self.finalize();
284            out.copy_from_slice(&h);
285        }
286    }
287
288    impl Reset for Hash {
289        fn reset(&mut self) {
290            *self = Self::new()
291        }
292    }
293}
294
295#[test]
296fn main() {
297    let h = Hash::hash(b"");
298    assert_eq!(
299        &h[..],
300        &[218, 57, 163, 238, 94, 107, 75, 13, 50, 85, 191, 239, 149, 96, 24, 144, 175, 216, 7, 9]
301    );
302
303    let h = Hash::hash(b"test");
304    assert_eq!(
305        &h[..],
306        &[
307            169, 74, 143, 229, 204, 177, 155, 166, 28, 76, 8, 115, 211, 145, 233, 135, 152, 47,
308            187, 211
309        ]
310    );
311
312    let h = Hash::hash(b"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
313    assert_eq!(
314        &h[..],
315        &[
316            83, 198, 188, 169, 11, 131, 194, 106, 152, 41, 132, 130, 87, 94, 225, 72, 154, 232, 71,
317            164
318        ]
319    );
320
321    let h = HMAC::mac(&[42u8; 69], &[]);
322    assert_eq!(
323        &h[..],
324        &[
325            158, 168, 84, 31, 38, 118, 164, 197, 103, 254, 207, 71, 48, 231, 13, 254, 219, 82, 96,
326            68
327        ]
328    );
329
330    let h = HMAC::mac(&[69u8; 250], &[42u8; 50]);
331    assert_eq!(
332        &h[..],
333        &[227, 229, 82, 212, 89, 36, 86, 81, 168, 126, 125, 32, 181, 28, 108, 40, 2, 213, 102, 90]
334    );
335}
336
337#[cfg(feature = "traits010")]
338#[test]
339fn main_traits() {
340    use digest010::Digest;
341    let mut h = WrappedHash::new();
342    Digest::update(&mut h, b"");
343    assert_eq!(
344        h.finalize().as_slice(),
345        &[218, 57, 163, 238, 94, 107, 75, 13, 50, 85, 191, 239, 149, 96, 24, 144, 175, 216, 7, 9]
346    );
347
348    let mut h = WrappedHash::new();
349    Digest::update(&mut h, b"test");
350    assert_eq!(
351        h.finalize().as_slice(),
352        &[
353            169, 74, 143, 229, 204, 177, 155, 166, 28, 76, 8, 115, 211, 145, 233, 135, 152, 47,
354            187, 211
355        ]
356    );
357}