1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
#[derive(Default, Clone, Copy, PartialEq)]
pub struct Sha1;
impl Sha1 {
// SHA-1 hashing algorithm initial hash values.
// These constants are derived from the fractional parts of the square roots of the first five primes.
const H0: u32 = 0x67452301;
const H1: u32 = 0xEFCDAB89;
const H2: u32 = 0x98BADCFE;
const H3: u32 = 0x10325476;
const H4: u32 = 0xC3D2E1F0;
/// Computes the SHA-1 hash of the input string by taking in either a String of str type.
pub fn hash(key: &[u8]) -> [u8; 20] {
// 1. Initialize variables to the SHA-1's initial hash values.
let (mut h0, mut h1, mut h2, mut h3, mut h4) =
(Self::H0, Self::H1, Self::H2, Self::H3, Self::H4);
// 2. Pad the key
let msg = Self::pad_message(key);
// 3. Process each 512-bit chunk of the padded message.
for chunk in msg.chunks(64) {
// 4. Get the message schedule and copies initial SHA-1 values.
let schedule = Self::build_schedule(chunk);
// 5. initialize the schedule
let (mut a, mut b, mut c, mut d, mut e) = (h0, h1, h2, h3, h4);
// 6. Main loop of the SHA-1 algorithm using predefind values based on primes numbers.
for i in 0..80 {
let (f, k) = match i {
0..=19 => ((b & c) | ((!b) & d), 0x5A827999),
20..=39 => (b ^ c ^ d, 0x6ED9EBA1),
40..=59 => ((b & c) | (b & d) | (c & d), 0x8F1BBCDC),
_ => (b ^ c ^ d, 0xCA62C1D6),
};
// 7. Update the temporary variable and then update the hash values
// in a manner that enforces both diffusion and confusion. Note
// how the "scrambled" data trickles through the variables as we
// loop through.
let temp = a
.rotate_left(5)
.wrapping_add(f)
.wrapping_add(e)
.wrapping_add(k)
.wrapping_add(schedule[i]);
e = d;
d = c;
c = b.rotate_left(30);
b = a;
a = temp;
}
// 8. Add the compressed chunk to the current hash value.
h0 = h0.wrapping_add(a);
h1 = h1.wrapping_add(b);
h2 = h2.wrapping_add(c);
h3 = h3.wrapping_add(d);
h4 = h4.wrapping_add(e);
}
// 9. Produce the final hash value as a 20-byte array.
let mut hash = [0u8; 20];
for (i, h) in [h0, h1, h2, h3, h4].iter().enumerate() {
let (start, end) = (i * 4, (i + 1) * 4);
hash[start..end].copy_from_slice(&h.to_be_bytes());
}
hash
}
/// Pads the input message according to SHA-1 specifications.
fn pad_message(input: &[u8]) -> Vec<u8> {
// 1. turn the input into a vec.
let mut bytes = input.to_vec();
// 2. Save the original message length
let original_bit_length = bytes.len() as u64 * 8;
// 3. Append the byte 0x80 (1) at the end to delineate the padding start
bytes.push(0x80);
// 4. Pad with '0' bytes until the message's length in bits % 512 is 448.
while (bytes.len() * 8) % 512 != 448 {
bytes.push(0);
}
// 5. Append the original message length.
bytes.extend_from_slice(&original_bit_length.to_be_bytes());
bytes
}
/// Builds the message schedule array from a 512-bit chunk.
fn build_schedule(chunk: &[u8]) -> [u32; 80] {
// 1. Initialize an empty schedule
let mut schedule = [0u32; 80];
// 2. Initialize the first 16 words in the array from the chunk.
for (i, block) in chunk.chunks(4).enumerate() {
schedule[i] = u32::from_be_bytes(block.try_into().unwrap());
}
// 3. Extend the schedule array using previously defined values and the XOR (^) operation.
for i in 16..80 {
schedule[i] = schedule[i - 3] ^ schedule[i - 8] ^ schedule[i - 14] ^ schedule[i - 16];
schedule[i] = schedule[i].rotate_left(1);
}
schedule
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn abc() {
let res = Sha1::hash(b"abc");
assert_eq!(
res,
[
0xA9, 0x99, 0x3E, 0x36, // easier to see in fours
0x47, 0x06, 0x81, 0x6A, // second
0xBA, 0x3E, 0x25, 0x71, // third
0x78, 0x50, 0xc2, 0x6c, // fourth
0x9c, 0xd0, 0xd8, 0x9d, // fifth
]
);
}
#[test]
fn empty() {
let res = Sha1::hash(b"");
assert_eq!(
res,
[
0xda, 0x39, 0xa3, 0xee, // first
0x5e, 0x6b, 0x4b, 0x0d, // second
0x32, 0x55, 0xbf, 0xef, // third
0x95, 0x60, 0x18, 0x90, // fourth
0xaf, 0xd8, 0x07, 0x09, // fifth
]
);
}
#[test]
fn ex1() {
let res = Sha1::hash(b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
assert_eq!(
res,
[
0x84, 0x98, 0x3e, 0x44, // first
0x1c, 0x3b, 0xd2, 0x6e, // second
0xba, 0xae, 0x4a, 0xa1, // third
0xf9, 0x51, 0x29, 0xe5, // fourth
0xe5, 0x46, 0x70, 0xf1, // fifth
]
);
}
}