hmac_sha1_compact/
lib.rs

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