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 = "traits011")]
227digest011::buffer_fixed!(
228    /// Wrapped `Hash` type for the `Digest` trait (digest 0.11).
229    pub struct WrappedHash011(Hash);
230    oid: "1.3.14.3.2.26";
231    impl: Debug Clone Default Reset HashMarker BlockSizeUser OutputSizeUser Update FixedOutput FixedOutputReset;
232);
233
234#[cfg(feature = "traits010")]
235mod digest_trait010 {
236    use core::fmt;
237
238    use digest010::{
239        block_buffer::Eager,
240        const_oid::{AssociatedOid, ObjectIdentifier},
241        consts::{U20, U64},
242        core_api::{
243            AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, FixedOutputCore,
244            OutputSizeUser, Reset, UpdateCore,
245        },
246        HashMarker,
247    };
248
249    use super::Hash;
250
251    impl AssociatedOid for Hash {
252        const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.14.3.2.26");
253    }
254
255    impl AlgorithmName for Hash {
256        fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
257            f.write_str("Sha1")
258        }
259    }
260
261    impl HashMarker for Hash {}
262
263    impl BufferKindUser for Hash {
264        type BufferKind = Eager;
265    }
266
267    impl BlockSizeUser for Hash {
268        type BlockSize = U64;
269    }
270
271    impl OutputSizeUser for Hash {
272        type OutputSize = U20;
273    }
274
275    impl UpdateCore for Hash {
276        #[inline]
277        fn update_blocks(&mut self, blocks: &[Block<Self>]) {
278            for block in blocks {
279                self._update(block)
280            }
281        }
282    }
283
284    impl FixedOutputCore for Hash {
285        fn finalize_fixed_core(
286            &mut self,
287            buffer: &mut Buffer<Self>,
288            out: &mut digest010::Output<Self>,
289        ) {
290            self._update(buffer.get_data());
291            let h = self.finalize();
292            out.copy_from_slice(&h);
293        }
294    }
295
296    impl Reset for Hash {
297        fn reset(&mut self) {
298            *self = Self::new()
299        }
300    }
301}
302
303#[cfg(feature = "traits011")]
304mod digest_trait011 {
305    use core::fmt;
306
307    use digest011::{
308        block_buffer::Eager,
309        const_oid::{AssociatedOid, ObjectIdentifier},
310        consts::{U20, U64},
311        core_api::{
312            AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, FixedOutputCore,
313            OutputSizeUser, Reset, UpdateCore,
314        },
315        HashMarker,
316    };
317
318    use super::Hash;
319
320    impl AssociatedOid for Hash {
321        const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.14.3.2.26");
322    }
323
324    impl AlgorithmName for Hash {
325        fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
326            f.write_str("Sha1")
327        }
328    }
329
330    impl HashMarker for Hash {}
331
332    impl BufferKindUser for Hash {
333        type BufferKind = Eager;
334    }
335
336    impl BlockSizeUser for Hash {
337        type BlockSize = U64;
338    }
339
340    impl OutputSizeUser for Hash {
341        type OutputSize = U20;
342    }
343
344    impl UpdateCore for Hash {
345        #[inline]
346        fn update_blocks(&mut self, blocks: &[Block<Self>]) {
347            for block in blocks {
348                self._update(block)
349            }
350        }
351    }
352
353    impl FixedOutputCore for Hash {
354        fn finalize_fixed_core(
355            &mut self,
356            buffer: &mut Buffer<Self>,
357            out: &mut digest011::Output<Self>,
358        ) {
359            self._update(buffer.get_data());
360            let h = self.finalize();
361            out.copy_from_slice(&h);
362        }
363    }
364
365    impl Reset for Hash {
366        fn reset(&mut self) {
367            *self = Self::new()
368        }
369    }
370}
371
372#[test]
373fn main() {
374    let h = Hash::hash(b"");
375    assert_eq!(
376        &h[..],
377        &[218, 57, 163, 238, 94, 107, 75, 13, 50, 85, 191, 239, 149, 96, 24, 144, 175, 216, 7, 9]
378    );
379
380    let h = Hash::hash(b"test");
381    assert_eq!(
382        &h[..],
383        &[
384            169, 74, 143, 229, 204, 177, 155, 166, 28, 76, 8, 115, 211, 145, 233, 135, 152, 47,
385            187, 211
386        ]
387    );
388
389    let h = Hash::hash(b"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
390    assert_eq!(
391        &h[..],
392        &[
393            83, 198, 188, 169, 11, 131, 194, 106, 152, 41, 132, 130, 87, 94, 225, 72, 154, 232, 71,
394            164
395        ]
396    );
397
398    let h = HMAC::mac(&[42u8; 69], &[]);
399    assert_eq!(
400        &h[..],
401        &[
402            158, 168, 84, 31, 38, 118, 164, 197, 103, 254, 207, 71, 48, 231, 13, 254, 219, 82, 96,
403            68
404        ]
405    );
406
407    let h = HMAC::mac(&[69u8; 250], &[42u8; 50]);
408    assert_eq!(
409        &h[..],
410        &[227, 229, 82, 212, 89, 36, 86, 81, 168, 126, 125, 32, 181, 28, 108, 40, 2, 213, 102, 90]
411    );
412}
413
414#[cfg(feature = "traits010")]
415#[test]
416fn main_traits() {
417    use digest010::Digest;
418    let mut h = WrappedHash::new();
419    Digest::update(&mut h, b"");
420    assert_eq!(
421        h.finalize().as_slice(),
422        &[218, 57, 163, 238, 94, 107, 75, 13, 50, 85, 191, 239, 149, 96, 24, 144, 175, 216, 7, 9]
423    );
424
425    let mut h = WrappedHash::new();
426    Digest::update(&mut h, b"test");
427    assert_eq!(
428        h.finalize().as_slice(),
429        &[
430            169, 74, 143, 229, 204, 177, 155, 166, 28, 76, 8, 115, 211, 145, 233, 135, 152, 47,
431            187, 211
432        ]
433    );
434}
435
436#[cfg(feature = "traits011")]
437#[test]
438fn main_traits011() {
439    use digest011::Digest;
440    let mut h = WrappedHash011::new();
441    Digest::update(&mut h, b"");
442    assert_eq!(
443        h.finalize().as_slice(),
444        &[218, 57, 163, 238, 94, 107, 75, 13, 50, 85, 191, 239, 149, 96, 24, 144, 175, 216, 7, 9]
445    );
446
447    let mut h = WrappedHash011::new();
448    Digest::update(&mut h, b"test");
449    assert_eq!(
450        h.finalize().as_slice(),
451        &[
452            169, 74, 143, 229, 204, 177, 155, 166, 28, 76, 8, 115, 211, 145, 233, 135, 152, 47,
453            187, 211
454        ]
455    );
456}