1#![forbid(unsafe_code)]
12#![deny(missing_docs)]
13#![cfg_attr(not(feature = "std"), no_std)]
14#![allow(clippy::identity_op)]
16
17use core::fmt;
18
19pub 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
38pub 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
59pub const BUFFER_SIZE: usize = 1024;
61
62pub struct ConstSlice {
65 data: [u8; BUFFER_SIZE],
66 head: usize,
67}
68
69impl ConstSlice {
70 pub const fn from_slice(slice: &[u8]) -> Self {
72 let s = Self::new();
73 s.push_slice(slice)
74 }
75
76 pub const fn new() -> Self {
78 Self {
79 data: [0; BUFFER_SIZE],
80 head: 0,
81 }
82 }
83
84 pub const fn push_slice(self, slice: &[u8]) -> Self {
86 self.push_amount(slice, slice.len())
87 }
88
89 pub const fn get(&self, index: usize) -> u8 {
91 self.data[index]
92 }
93
94 pub const fn len(&self) -> usize {
96 self.head
97 }
98
99 pub const fn is_empty(&self) -> bool {
101 self.len() == 0
102 }
103
104 pub const fn as_slice(&self) -> &[u8] {
106 &self.data
107 }
108
109 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
452pub struct Digest {
454 data: [u32; 5],
456}
457
458impl Digest {
459 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}