const_sha1/
lib.rs

1//! A const evaluated sha1 function.
2//!
3//! # Use
4//!
5//! ```
6//! const fn signature() -> const_sha1::Digest {
7//!     const_sha1::sha1(stringify!(MyType).as_bytes())
8//! }
9//! ```
10
11#![forbid(unsafe_code)]
12#![deny(missing_docs)]
13#![cfg_attr(not(feature = "std"), no_std)]
14// Allow identity ops for readability
15#![allow(clippy::identity_op)]
16
17use core::fmt;
18
19/// A const evaluated sha1 function.
20///
21/// # Use
22///
23/// ```
24/// const fn signature() -> const_sha1::Digest {
25///     const_sha1::sha1(stringify!(MyType).as_bytes())
26/// }
27/// ```
28pub const fn sha1(data: &[u8]) -> Digest {
29    let state: [u32; 5] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0];
30    let blocks = Blocks {
31        len: 0,
32        data: [0; 64],
33    };
34    let (blocks, len, state) = process_blocks(blocks, data, data.len(), state);
35    digest(state, len, blocks)
36}
37
38/// A const evaluated sha1 function. The function differs from `sha1`
39/// in that it uses a `ConstSlice` which allows for resizing a slice
40/// at const evaluation time which is not permitted with built-in slices.
41///
42/// # Use
43///
44/// ```
45/// const fn signature() -> const_sha1::Digest {
46///     const_sha1::sha1_from_const_slice(&const_sha1::ConstSlice::from_slice(stringify!(MyType).as_bytes()))
47/// }
48/// ```
49pub const fn sha1_from_const_slice(data: &ConstSlice) -> Digest {
50    let state: [u32; 5] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0];
51    let blocks = Blocks {
52        len: 0,
53        data: [0; 64],
54    };
55    let (blocks, len, state) = process_blocks(blocks, data.as_slice(), data.len(), state);
56    digest(state, len, blocks)
57}
58
59/// The size of the ConstSlice.
60pub const BUFFER_SIZE: usize = 1024;
61
62/// A buffer of a constant size suitable for use in const contexts
63/// as a temporary replacement for slices.
64pub struct ConstSlice {
65    data: [u8; BUFFER_SIZE],
66    head: usize,
67}
68
69impl ConstSlice {
70    /// Convert a slice into a `ConstSlice`.
71    pub const fn from_slice(slice: &[u8]) -> Self {
72        let s = Self::new();
73        s.push_slice(slice)
74    }
75
76    /// Create an empty `ConstSlice`.
77    pub const fn new() -> Self {
78        Self {
79            data: [0; BUFFER_SIZE],
80            head: 0,
81        }
82    }
83
84    /// Push a slice of bytes on to the buffer.
85    pub const fn push_slice(self, slice: &[u8]) -> Self {
86        self.push_amount(slice, slice.len())
87    }
88
89    /// Get a byte at a given index.
90    pub const fn get(&self, index: usize) -> u8 {
91        self.data[index]
92    }
93
94    /// Get the length of the buffer that has been written to.
95    pub const fn len(&self) -> usize {
96        self.head
97    }
98
99    /// Whether the buffer is empty or not.
100    pub const fn is_empty(&self) -> bool {
101        self.len() == 0
102    }
103
104    /// Get the buffer as a slice.
105    pub const fn as_slice(&self) -> &[u8] {
106        &self.data
107    }
108
109    /// Push another `ConstSlice` on to the current buffer.
110    pub const fn push_other(self, other: Self) -> Self {
111        self.push_amount(other.as_slice(), other.len())
112    }
113
114    const fn push_amount(mut self, slice: &[u8], amount: usize) -> Self {
115        let mut i = 0;
116        while i < amount {
117            self.data[self.head + i] = slice[i];
118            i += 1;
119        }
120        self.head += i;
121        self
122    }
123}
124
125impl fmt::Debug for ConstSlice {
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        write!(f, "{:x?}", &self.data[0..self.head])
128    }
129}
130
131struct Blocks {
132    len: u32,
133    data: [u8; 64],
134}
135
136const fn process_blocks(
137    mut blocks: Blocks,
138    data: &[u8],
139    data_len: usize,
140    mut state: [u32; 5],
141) -> (Blocks, usize, [u32; 5]) {
142    const fn as_block(input: &[u8], offset: usize) -> [u32; 16] {
143        let mut result = [0u32; 16];
144
145        let mut i = 0;
146        while i != 16 {
147            let off = offset + (i * 4);
148            result[i] = 0
149                | ((input[off + 3] as u32) << 0)
150                | ((input[off + 2] as u32) << 8)
151                | ((input[off + 1] as u32) << 16)
152                | ((input[off + 0] as u32) << 24);
153            i += 1;
154        }
155        result
156    }
157
158    const fn clone_from_slice_64(
159        mut data: [u8; 64],
160        slice: &[u8],
161        offset: usize,
162        num_elems: usize,
163    ) -> [u8; 64] {
164        let mut i = 0;
165        while i < num_elems {
166            data[i] = slice[offset + i];
167            i += 1;
168        }
169        data
170    }
171
172    let mut len = 0;
173    while len < data_len {
174        let left = data_len - len;
175        if left >= 64 {
176            let chunk_block = as_block(data, len);
177            state = process_state(state, chunk_block);
178            len += 64;
179        } else {
180            blocks.data = clone_from_slice_64(blocks.data, data, len, left);
181            blocks.len = left as u32;
182            break;
183        }
184    }
185    (blocks, len, state)
186}
187
188const fn process_state(mut state: [u32; 5], block: [u32; 16]) -> [u32; 5] {
189    let a = state[0];
190    let b = state[1];
191    let c = state[2];
192    let d = state[3];
193    let e = state[4];
194    let (block, b, e) = r0(block, a, b, c, d, e, 0);
195    let (block, a, d) = r0(block, e, a, b, c, d, 1);
196    let (block, e, c) = r0(block, d, e, a, b, c, 2);
197    let (block, d, b) = r0(block, c, d, e, a, b, 3);
198    let (block, c, a) = r0(block, b, c, d, e, a, 4);
199    let (block, b, e) = r0(block, a, b, c, d, e, 5);
200    let (block, a, d) = r0(block, e, a, b, c, d, 6);
201    let (block, e, c) = r0(block, d, e, a, b, c, 7);
202    let (block, d, b) = r0(block, c, d, e, a, b, 8);
203    let (block, c, a) = r0(block, b, c, d, e, a, 9);
204    let (block, b, e) = r0(block, a, b, c, d, e, 10);
205    let (block, a, d) = r0(block, e, a, b, c, d, 11);
206    let (block, e, c) = r0(block, d, e, a, b, c, 12);
207    let (block, d, b) = r0(block, c, d, e, a, b, 13);
208    let (block, c, a) = r0(block, b, c, d, e, a, 14);
209    let (block, b, e) = r0(block, a, b, c, d, e, 15);
210    let (block, a, d) = r1(block, e, a, b, c, d, 0);
211    let (block, e, c) = r1(block, d, e, a, b, c, 1);
212    let (block, d, b) = r1(block, c, d, e, a, b, 2);
213    let (block, c, a) = r1(block, b, c, d, e, a, 3);
214    let (block, b, e) = r2(block, a, b, c, d, e, 4);
215    let (block, a, d) = r2(block, e, a, b, c, d, 5);
216    let (block, e, c) = r2(block, d, e, a, b, c, 6);
217    let (block, d, b) = r2(block, c, d, e, a, b, 7);
218    let (block, c, a) = r2(block, b, c, d, e, a, 8);
219    let (block, b, e) = r2(block, a, b, c, d, e, 9);
220    let (block, a, d) = r2(block, e, a, b, c, d, 10);
221    let (block, e, c) = r2(block, d, e, a, b, c, 11);
222    let (block, d, b) = r2(block, c, d, e, a, b, 12);
223    let (block, c, a) = r2(block, b, c, d, e, a, 13);
224    let (block, b, e) = r2(block, a, b, c, d, e, 14);
225    let (block, a, d) = r2(block, e, a, b, c, d, 15);
226    let (block, e, c) = r2(block, d, e, a, b, c, 0);
227    let (block, d, b) = r2(block, c, d, e, a, b, 1);
228    let (block, c, a) = r2(block, b, c, d, e, a, 2);
229    let (block, b, e) = r2(block, a, b, c, d, e, 3);
230    let (block, a, d) = r2(block, e, a, b, c, d, 4);
231    let (block, e, c) = r2(block, d, e, a, b, c, 5);
232    let (block, d, b) = r2(block, c, d, e, a, b, 6);
233    let (block, c, a) = r2(block, b, c, d, e, a, 7);
234    let (block, b, e) = r3(block, a, b, c, d, e, 8);
235    let (block, a, d) = r3(block, e, a, b, c, d, 9);
236    let (block, e, c) = r3(block, d, e, a, b, c, 10);
237    let (block, d, b) = r3(block, c, d, e, a, b, 11);
238    let (block, c, a) = r3(block, b, c, d, e, a, 12);
239    let (block, b, e) = r3(block, a, b, c, d, e, 13);
240    let (block, a, d) = r3(block, e, a, b, c, d, 14);
241    let (block, e, c) = r3(block, d, e, a, b, c, 15);
242    let (block, d, b) = r3(block, c, d, e, a, b, 0);
243    let (block, c, a) = r3(block, b, c, d, e, a, 1);
244    let (block, b, e) = r3(block, a, b, c, d, e, 2);
245    let (block, a, d) = r3(block, e, a, b, c, d, 3);
246    let (block, e, c) = r3(block, d, e, a, b, c, 4);
247    let (block, d, b) = r3(block, c, d, e, a, b, 5);
248    let (block, c, a) = r3(block, b, c, d, e, a, 6);
249    let (block, b, e) = r3(block, a, b, c, d, e, 7);
250    let (block, a, d) = r3(block, e, a, b, c, d, 8);
251    let (block, e, c) = r3(block, d, e, a, b, c, 9);
252    let (block, d, b) = r3(block, c, d, e, a, b, 10);
253    let (block, c, a) = r3(block, b, c, d, e, a, 11);
254    let (block, b, e) = r4(block, a, b, c, d, e, 12);
255    let (block, a, d) = r4(block, e, a, b, c, d, 13);
256    let (block, e, c) = r4(block, d, e, a, b, c, 14);
257    let (block, d, b) = r4(block, c, d, e, a, b, 15);
258    let (block, c, a) = r4(block, b, c, d, e, a, 0);
259    let (block, b, e) = r4(block, a, b, c, d, e, 1);
260    let (block, a, d) = r4(block, e, a, b, c, d, 2);
261    let (block, e, c) = r4(block, d, e, a, b, c, 3);
262    let (block, d, b) = r4(block, c, d, e, a, b, 4);
263    let (block, c, a) = r4(block, b, c, d, e, a, 5);
264    let (block, b, e) = r4(block, a, b, c, d, e, 6);
265    let (block, a, d) = r4(block, e, a, b, c, d, 7);
266    let (block, e, c) = r4(block, d, e, a, b, c, 8);
267    let (block, d, b) = r4(block, c, d, e, a, b, 9);
268    let (block, c, a) = r4(block, b, c, d, e, a, 10);
269    let (block, b, e) = r4(block, a, b, c, d, e, 11);
270    let (block, a, d) = r4(block, e, a, b, c, d, 12);
271    let (block, e, c) = r4(block, d, e, a, b, c, 13);
272    let (block, d, b) = r4(block, c, d, e, a, b, 14);
273    let (_, c, a) = r4(block, b, c, d, e, a, 15);
274
275    state[0] = state[0].wrapping_add(a);
276    state[1] = state[1].wrapping_add(b);
277    state[2] = state[2].wrapping_add(c);
278    state[3] = state[3].wrapping_add(d);
279    state[4] = state[4].wrapping_add(e);
280    state
281}
282
283const fn digest(mut state: [u32; 5], len: usize, blocks: Blocks) -> Digest {
284    const fn clone_from_slice_128(
285        mut data: [u8; 128],
286        slice: &[u8],
287        offset: usize,
288        num_elems: usize,
289    ) -> [u8; 128] {
290        let mut i = 0;
291        while i < num_elems {
292            data[i] = slice[offset + i];
293            i += 1;
294        }
295        data
296    }
297
298    const fn clone_slice_128(mut data: [u8; 128], slice: &[u8], offset: usize) -> [u8; 128] {
299        let mut i = 0;
300        while i < slice.len() {
301            data[offset + i] = slice[i];
302            i += 1;
303        }
304        data
305    }
306
307    const fn as_block(input: &[u8], offset: usize) -> [u32; 16] {
308        let mut result = [0u32; 16];
309
310        let mut i = 0;
311        while i != 16 {
312            let off = offset + (i * 4);
313            result[i] = (input[off + 3] as u32)
314                | ((input[off + 2] as u32) << 8)
315                | ((input[off + 1] as u32) << 16)
316                | ((input[off] as u32) << 24);
317            i += 1;
318        }
319        result
320    }
321
322    let bits = ((len as u64) + (blocks.len as u64)) * 8;
323    let extra = [
324        (bits >> 56) as u8,
325        (bits >> 48) as u8,
326        (bits >> 40) as u8,
327        (bits >> 32) as u8,
328        (bits >> 24) as u8,
329        (bits >> 16) as u8,
330        (bits >> 8) as u8,
331        (bits >> 0) as u8,
332    ];
333    let mut last = [0; 128];
334    let blocklen = blocks.len as usize;
335    last = clone_from_slice_128(last, &blocks.data, 0, blocklen);
336    last[blocklen] = 0x80;
337
338    if blocklen < 56 {
339        last = clone_slice_128(last, &extra, 56);
340        state = process_state(state, as_block(&last, 0));
341    } else {
342        last = clone_slice_128(last, &extra, 120);
343        state = process_state(state, as_block(&last, 0));
344        state = process_state(state, as_block(&last, 64));
345    }
346    Digest { data: state }
347}
348
349const fn rol(value: u32, bits: usize) -> u32 {
350    (value << bits) | (value >> (32 - bits))
351}
352
353const fn blk(block: &[u32], i: usize) -> u32 {
354    let value = block[(i + 13) & 15] ^ block[(i + 8) & 15] ^ block[(i + 2) & 15] ^ block[i];
355    rol(value, 1)
356}
357
358const fn r0(
359    block: [u32; 16],
360    v: u32,
361    mut w: u32,
362    x: u32,
363    y: u32,
364    mut z: u32,
365    i: usize,
366) -> ([u32; 16], u32, u32) {
367    let n = ((w & (x ^ y)) ^ y)
368        .wrapping_add(block[i])
369        .wrapping_add(0x5a82_7999)
370        .wrapping_add(rol(v, 5));
371    z = z.wrapping_add(n);
372    w = rol(w, 30);
373    (block, w, z)
374}
375
376const fn r1(
377    mut block: [u32; 16],
378    v: u32,
379    mut w: u32,
380    x: u32,
381    y: u32,
382    mut z: u32,
383    i: usize,
384) -> ([u32; 16], u32, u32) {
385    block[i] = blk(&block, i);
386    let n = ((w & (x ^ y)) ^ y)
387        .wrapping_add(block[i])
388        .wrapping_add(0x5a82_7999)
389        .wrapping_add(rol(v, 5));
390    z = z.wrapping_add(n);
391    w = rol(w, 30);
392    (block, w, z)
393}
394
395const fn r2(
396    mut block: [u32; 16],
397    v: u32,
398    mut w: u32,
399    x: u32,
400    y: u32,
401    mut z: u32,
402    i: usize,
403) -> ([u32; 16], u32, u32) {
404    block[i] = blk(&block, i);
405    let n = (w ^ x ^ y)
406        .wrapping_add(block[i])
407        .wrapping_add(0x6ed_9eba1)
408        .wrapping_add(rol(v, 5));
409    z = z.wrapping_add(n);
410    w = rol(w, 30);
411    (block, w, z)
412}
413
414const fn r3(
415    mut block: [u32; 16],
416    v: u32,
417    mut w: u32,
418    x: u32,
419    y: u32,
420    mut z: u32,
421    i: usize,
422) -> ([u32; 16], u32, u32) {
423    block[i] = blk(&block, i);
424    let n = (((w | x) & y) | (w & x))
425        .wrapping_add(block[i])
426        .wrapping_add(0x8f1b_bcdc)
427        .wrapping_add(rol(v, 5));
428    z = z.wrapping_add(n);
429    w = rol(w, 30);
430    (block, w, z)
431}
432
433const fn r4(
434    mut block: [u32; 16],
435    v: u32,
436    mut w: u32,
437    x: u32,
438    y: u32,
439    mut z: u32,
440    i: usize,
441) -> ([u32; 16], u32, u32) {
442    block[i] = blk(&block, i);
443    let n = (w ^ x ^ y)
444        .wrapping_add(block[i])
445        .wrapping_add(0xca62_c1d6)
446        .wrapping_add(rol(v, 5));
447    z = z.wrapping_add(n);
448    w = rol(w, 30);
449    (block, w, z)
450}
451
452/// A sha1 digest
453pub struct Digest {
454    /// The sha1 digest's data
455    data: [u32; 5],
456}
457
458impl Digest {
459    /// Returns the 160 bit (20 byte) digest as a byte array.
460    pub const fn as_bytes(&self) -> [u8; 20] {
461        [
462            (self.data[0] >> 24) as u8,
463            (self.data[0] >> 16) as u8,
464            (self.data[0] >> 8) as u8,
465            (self.data[0] >> 0) as u8,
466            (self.data[1] >> 24) as u8,
467            (self.data[1] >> 16) as u8,
468            (self.data[1] >> 8) as u8,
469            (self.data[1] >> 0) as u8,
470            (self.data[2] >> 24) as u8,
471            (self.data[2] >> 16) as u8,
472            (self.data[2] >> 8) as u8,
473            (self.data[2] >> 0) as u8,
474            (self.data[3] >> 24) as u8,
475            (self.data[3] >> 16) as u8,
476            (self.data[3] >> 8) as u8,
477            (self.data[3] >> 0) as u8,
478            (self.data[4] >> 24) as u8,
479            (self.data[4] >> 16) as u8,
480            (self.data[4] >> 8) as u8,
481            (self.data[4] >> 0) as u8,
482        ]
483    }
484}
485
486impl fmt::Display for Digest {
487    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
488        for i in self.data.iter() {
489            write!(f, "{:08x}", i)?;
490        }
491        Ok(())
492    }
493}
494
495#[cfg(test)]
496mod tests {
497    use super::*;
498    #[cfg(not(feature = "std"))]
499    extern crate alloc;
500    #[cfg(not(feature = "std"))]
501    use alloc::string::ToString;
502
503    #[test]
504    fn it_works() {
505        let tests = [
506            (
507                "The quick brown fox jumps over the lazy dog",
508                "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12",
509            ),
510            (
511                "The quick brown fox jumps over the lazy cog",
512                "de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3",
513            ),
514            (
515                "",
516                "da39a3ee5e6b4b0d3255bfef95601890afd80709"),
517            (
518                "testing\n",
519                "9801739daae44ec5293d4e1f53d3f4d2d426d91c"),
520            (
521                "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
522                "025ecbd5d70f8fb3c5457cd96bab13fda305dc59"
523            ),
524            (
525                "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
526                "4300320394f7ee239bcdce7d3b8bcee173a0cd5c"
527            ),
528            (
529                "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
530                "cef734ba81a024479e09eb5a75b6ddae62e6abf1"
531            ),
532            (
533                "pinterface({1f6db258-e803-48a1-9546-eb7353398884};pinterface({faa585ea-6214-4217-afda-7f46de5869b3};{96369f54-8eb6-48f0-abce-c1b211e627c3}))", 
534                "b1b3deeb1552c97f3f36152f7baeec0f6ac159bc"
535            ),
536            (
537                "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla aliquet varius molestie. Morbi eu est id massa fringilla gravida. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Duis pharetra facilisis ipsum et faucibus. Donec ut magna posuere, pretium nunc nec, egestas sem. Vivamus mattis ex neque, eu vehicula ex pharetra vitae. Aliquam ac cursus nunc. Duis ac nibh non velit ultrices luctus eu eu orci. Aenean suscipit sem a risus convallis ultrices. Nullam accumsan, turpis at pharetra porttitor, augue nisi pulvinar neque, id mattis ex eros ac diam. Praesent ultrices, ex sed elementum viverra, nunc ligula efficitur tellus, vel placerat dui dui in odio. Vivamus gravida pulvinar nisl, sit amet laoreet augue tristique at. Nunc in velit nisi. Praesent suscipit, mi quis dictum aliquet, lacus nulla ornare arcu, sit amet hendrerit urna ex a erat. Donec tempor lorem libero, sed aliquam libero tristique vitae.\nAenean nisl ipsum, pharetra id sollicitudin vitae, rhoncus eu est. Integer at sem sem. Duis venenatis dapibus ornare. Donec fermentum scelerisque lectus, sit amet tempus turpis sodales ut. Maecenas ultrices libero quis pulvinar auctor. Maecenas et enim vel eros pretium iaculis. Aenean luctus convallis lectus ut convallis. Maecenas eu orci quis lacus tincidunt tristique eget id odio. Quisque sit amet dictum nunc, molestie dapibus massa. Integer ultricies enim massa, et semper ligula imperdiet ut. Proin malesuada dapibus magna a bibendum. Phasellus quis vehicula lorem. Quisque sit amet tempor erat, eu mollis odio. Proin consequat interdum cursus. Vivamus ornare enim et tincidunt interdum. Nam suscipit magna a ex tempor tempor eget a nisl.\nProin aliquet ligula mollis bibendum malesuada. Fusce ac eros nunc. Quisque vel commodo ligula, ac efficitur nisl. Phasellus in ipsum et tortor elementum laoreet nec rutrum libero. Cras ut justo eleifend, vulputate sapien vel, tempus libero. Integer a nisi a mauris varius scelerisque vitae at felis. Phasellus sit amet iaculis libero porttitor.",
538                "30648ad988839ad25365fe1674417eca19c7da01"
539            )
540        ];
541
542        for &(s, expected) in tests.iter() {
543            let hash = sha1(s.as_bytes()).to_string();
544
545            assert_eq!(hash, expected);
546        }
547
548        for &(s, expected) in tests.iter().filter(|(s, _)| s.len() <= BUFFER_SIZE) {
549            let hash = sha1_from_const_slice(&ConstSlice::from_slice(s.as_bytes())).to_string();
550
551            assert_eq!(hash, expected);
552        }
553    }
554}