297 lines
10 KiB
Rust
297 lines
10 KiB
Rust
/// SHA-1 implementation adapted from <https://github.com/vog/sha1/blob/master/sha1.hpp>
|
|
use crate::bytes::Bytes;
|
|
|
|
const STATE_LEN: usize = 5;
|
|
const BLOCK_INTS: usize = 16; // number of 32bit integers per SHA1 block
|
|
const BLOCK_BYTES: usize = BLOCK_INTS * 4; // 64
|
|
type Block = [u32; BLOCK_INTS];
|
|
|
|
#[derive(Debug)]
|
|
pub struct Sha1 {
|
|
h: [u32; STATE_LEN],
|
|
byte_len: u64,
|
|
}
|
|
|
|
impl Default for Sha1 {
|
|
#[inline]
|
|
fn default() -> Self {
|
|
Self {
|
|
h: [
|
|
0x6745_2301,
|
|
0xEFCD_AB89,
|
|
0x98BA_DCFE,
|
|
0x1032_5476,
|
|
0xC3D2_E1F0,
|
|
],
|
|
byte_len: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn bytes_to_block(bytes: &[u8]) -> Block {
|
|
assert_eq!(bytes.len(), BLOCK_BYTES);
|
|
// safety: unwraps work because BLOCK_BYTES length is asserted
|
|
let b: Vec<u32> = bytes
|
|
.chunks(4)
|
|
.map(|c| u32::from_be_bytes(c.try_into().unwrap()))
|
|
.collect();
|
|
b.try_into().unwrap()
|
|
}
|
|
|
|
fn rol(value: u32, bits: usize) -> u32 {
|
|
(value << bits) | (value >> (32 - bits))
|
|
}
|
|
|
|
fn blk(block: &Block, i: usize) -> u32 {
|
|
rol(
|
|
block[(i + 13) & 15] ^ block[(i + 8) & 15] ^ block[(i + 2) & 15] ^ block[i],
|
|
1,
|
|
)
|
|
}
|
|
|
|
fn r0(block: &Block, v: u32, w: &mut u32, x: u32, y: u32, z: &mut u32, i: usize) {
|
|
*z = z
|
|
.wrapping_add((*w & (x ^ y)) ^ y)
|
|
.wrapping_add(block[i])
|
|
.wrapping_add(0x5a82_7999)
|
|
.wrapping_add(rol(v, 5));
|
|
*w = rol(*w, 30);
|
|
}
|
|
|
|
fn r1(block: &mut Block, v: u32, w: &mut u32, x: u32, y: u32, z: &mut u32, i: usize) {
|
|
block[i] = blk(block, i);
|
|
*z = z
|
|
.wrapping_add((*w & (x ^ y)) ^ y)
|
|
.wrapping_add(block[i])
|
|
.wrapping_add(0x5a82_7999)
|
|
.wrapping_add(rol(v, 5));
|
|
*w = rol(*w, 30);
|
|
}
|
|
|
|
fn r2(block: &mut Block, v: u32, w: &mut u32, x: u32, y: u32, z: &mut u32, i: usize) {
|
|
block[i] = blk(block, i);
|
|
*z = z
|
|
.wrapping_add(*w ^ x ^ y)
|
|
.wrapping_add(block[i])
|
|
.wrapping_add(0x6ed9_eba1)
|
|
.wrapping_add(rol(v, 5));
|
|
*w = rol(*w, 30);
|
|
}
|
|
|
|
fn r3(block: &mut Block, v: u32, w: &mut u32, x: u32, y: u32, z: &mut u32, i: usize) {
|
|
block[i] = blk(block, i);
|
|
*z = z
|
|
.wrapping_add(((*w | x) & y) | (*w & x))
|
|
.wrapping_add(block[i])
|
|
.wrapping_add(0x8f1b_bcdc)
|
|
.wrapping_add(rol(v, 5));
|
|
*w = rol(*w, 30);
|
|
}
|
|
|
|
fn r4(block: &mut Block, v: u32, w: &mut u32, x: u32, y: u32, z: &mut u32, i: usize) {
|
|
block[i] = blk(block, i);
|
|
*z = z
|
|
.wrapping_add(*w ^ x ^ y)
|
|
.wrapping_add(block[i])
|
|
.wrapping_add(0xca62_c1d6)
|
|
.wrapping_add(rol(v, 5));
|
|
*w = rol(*w, 30);
|
|
}
|
|
|
|
impl Sha1 {
|
|
#[inline]
|
|
pub fn reset(&mut self) {
|
|
*self = Sha1::default();
|
|
}
|
|
|
|
pub fn fix(&mut self, fixate: [u32; STATE_LEN], byte_len: u64) {
|
|
self.h[..5].copy_from_slice(&fixate[..5]);
|
|
self.byte_len = byte_len;
|
|
}
|
|
|
|
fn transform(&mut self, block: &mut Block) {
|
|
/* Copy digest[] to working vars */
|
|
let mut a = self.h[0];
|
|
let mut b = self.h[1];
|
|
let mut c = self.h[2];
|
|
let mut d = self.h[3];
|
|
let mut e = self.h[4];
|
|
|
|
/* 4 rounds of 20 operations each. Loop unrolled. */
|
|
r0(block, a, &mut b, c, d, &mut e, 0);
|
|
r0(block, e, &mut a, b, c, &mut d, 1);
|
|
r0(block, d, &mut e, a, b, &mut c, 2);
|
|
r0(block, c, &mut d, e, a, &mut b, 3);
|
|
r0(block, b, &mut c, d, e, &mut a, 4);
|
|
r0(block, a, &mut b, c, d, &mut e, 5);
|
|
r0(block, e, &mut a, b, c, &mut d, 6);
|
|
r0(block, d, &mut e, a, b, &mut c, 7);
|
|
r0(block, c, &mut d, e, a, &mut b, 8);
|
|
r0(block, b, &mut c, d, e, &mut a, 9);
|
|
r0(block, a, &mut b, c, d, &mut e, 10);
|
|
r0(block, e, &mut a, b, c, &mut d, 11);
|
|
r0(block, d, &mut e, a, b, &mut c, 12);
|
|
r0(block, c, &mut d, e, a, &mut b, 13);
|
|
r0(block, b, &mut c, d, e, &mut a, 14);
|
|
r0(block, a, &mut b, c, d, &mut e, 15);
|
|
r1(block, e, &mut a, b, c, &mut d, 0);
|
|
r1(block, d, &mut e, a, b, &mut c, 1);
|
|
r1(block, c, &mut d, e, a, &mut b, 2);
|
|
r1(block, b, &mut c, d, e, &mut a, 3);
|
|
r2(block, a, &mut b, c, d, &mut e, 4);
|
|
r2(block, e, &mut a, b, c, &mut d, 5);
|
|
r2(block, d, &mut e, a, b, &mut c, 6);
|
|
r2(block, c, &mut d, e, a, &mut b, 7);
|
|
r2(block, b, &mut c, d, e, &mut a, 8);
|
|
r2(block, a, &mut b, c, d, &mut e, 9);
|
|
r2(block, e, &mut a, b, c, &mut d, 10);
|
|
r2(block, d, &mut e, a, b, &mut c, 11);
|
|
r2(block, c, &mut d, e, a, &mut b, 12);
|
|
r2(block, b, &mut c, d, e, &mut a, 13);
|
|
r2(block, a, &mut b, c, d, &mut e, 14);
|
|
r2(block, e, &mut a, b, c, &mut d, 15);
|
|
r2(block, d, &mut e, a, b, &mut c, 0);
|
|
r2(block, c, &mut d, e, a, &mut b, 1);
|
|
r2(block, b, &mut c, d, e, &mut a, 2);
|
|
r2(block, a, &mut b, c, d, &mut e, 3);
|
|
r2(block, e, &mut a, b, c, &mut d, 4);
|
|
r2(block, d, &mut e, a, b, &mut c, 5);
|
|
r2(block, c, &mut d, e, a, &mut b, 6);
|
|
r2(block, b, &mut c, d, e, &mut a, 7);
|
|
r3(block, a, &mut b, c, d, &mut e, 8);
|
|
r3(block, e, &mut a, b, c, &mut d, 9);
|
|
r3(block, d, &mut e, a, b, &mut c, 10);
|
|
r3(block, c, &mut d, e, a, &mut b, 11);
|
|
r3(block, b, &mut c, d, e, &mut a, 12);
|
|
r3(block, a, &mut b, c, d, &mut e, 13);
|
|
r3(block, e, &mut a, b, c, &mut d, 14);
|
|
r3(block, d, &mut e, a, b, &mut c, 15);
|
|
r3(block, c, &mut d, e, a, &mut b, 0);
|
|
r3(block, b, &mut c, d, e, &mut a, 1);
|
|
r3(block, a, &mut b, c, d, &mut e, 2);
|
|
r3(block, e, &mut a, b, c, &mut d, 3);
|
|
r3(block, d, &mut e, a, b, &mut c, 4);
|
|
r3(block, c, &mut d, e, a, &mut b, 5);
|
|
r3(block, b, &mut c, d, e, &mut a, 6);
|
|
r3(block, a, &mut b, c, d, &mut e, 7);
|
|
r3(block, e, &mut a, b, c, &mut d, 8);
|
|
r3(block, d, &mut e, a, b, &mut c, 9);
|
|
r3(block, c, &mut d, e, a, &mut b, 10);
|
|
r3(block, b, &mut c, d, e, &mut a, 11);
|
|
r4(block, a, &mut b, c, d, &mut e, 12);
|
|
r4(block, e, &mut a, b, c, &mut d, 13);
|
|
r4(block, d, &mut e, a, b, &mut c, 14);
|
|
r4(block, c, &mut d, e, a, &mut b, 15);
|
|
r4(block, b, &mut c, d, e, &mut a, 0);
|
|
r4(block, a, &mut b, c, d, &mut e, 1);
|
|
r4(block, e, &mut a, b, c, &mut d, 2);
|
|
r4(block, d, &mut e, a, b, &mut c, 3);
|
|
r4(block, c, &mut d, e, a, &mut b, 4);
|
|
r4(block, b, &mut c, d, e, &mut a, 5);
|
|
r4(block, a, &mut b, c, d, &mut e, 6);
|
|
r4(block, e, &mut a, b, c, &mut d, 7);
|
|
r4(block, d, &mut e, a, b, &mut c, 8);
|
|
r4(block, c, &mut d, e, a, &mut b, 9);
|
|
r4(block, b, &mut c, d, e, &mut a, 10);
|
|
r4(block, a, &mut b, c, d, &mut e, 11);
|
|
r4(block, e, &mut a, b, c, &mut d, 12);
|
|
r4(block, d, &mut e, a, b, &mut c, 13);
|
|
r4(block, c, &mut d, e, a, &mut b, 14);
|
|
r4(block, b, &mut c, d, e, &mut a, 15);
|
|
|
|
/* Add the working vars back into digest[] */
|
|
self.h[0] = self.h[0].wrapping_add(a);
|
|
self.h[1] = self.h[1].wrapping_add(b);
|
|
self.h[2] = self.h[2].wrapping_add(c);
|
|
self.h[3] = self.h[3].wrapping_add(d);
|
|
self.h[4] = self.h[4].wrapping_add(e);
|
|
|
|
/* Count the number of transformations */
|
|
self.byte_len += BLOCK_BYTES as u64;
|
|
}
|
|
|
|
fn update(&mut self, bytes: &[u8]) {
|
|
assert_eq!(bytes.len() % BLOCK_BYTES, 0);
|
|
let mut block = bytes_to_block(bytes);
|
|
self.transform(&mut block);
|
|
}
|
|
|
|
pub fn get_padding(&self, bytes: &Vec<u8>) -> Vec<u8> {
|
|
// append 0x80 to the message
|
|
let mut padding = vec![0x80];
|
|
|
|
// append 0 ≤ k < 64 bytes so that message.len() ≡ 56 (mod 64)
|
|
while (bytes.len() + padding.len()) % BLOCK_BYTES != (BLOCK_BYTES - 8) {
|
|
padding.push(0x0);
|
|
}
|
|
|
|
// append the original message length as a 64-bit big-endian integer
|
|
let bit_len: u64 = (self.byte_len + bytes.len() as u64) as u64 * 8;
|
|
padding.append(&mut bit_len.to_be_bytes().to_vec());
|
|
padding
|
|
}
|
|
|
|
pub fn hash(&mut self, bytes: &Bytes) -> Bytes {
|
|
let mut final_bytes = vec![];
|
|
for bytes in bytes.0.chunks(BLOCK_BYTES) {
|
|
if bytes.len() == BLOCK_BYTES {
|
|
self.update(bytes);
|
|
} else {
|
|
final_bytes = bytes.to_vec();
|
|
}
|
|
}
|
|
let mut padding = self.get_padding(&final_bytes);
|
|
final_bytes.append(&mut padding);
|
|
for bytes in final_bytes.chunks(BLOCK_BYTES) {
|
|
self.update(bytes);
|
|
}
|
|
Bytes(self.h.iter().flat_map(|i| i.to_be_bytes()).collect())
|
|
}
|
|
}
|
|
|
|
pub fn authenticate(message: &Bytes, key: &Bytes) -> Bytes {
|
|
// Write a function to authenticate a message under a secret key by using a
|
|
// secret-prefix MAC, which is simply:
|
|
// SHA1(key || message)
|
|
let mut c = vec![];
|
|
c.append(&mut key.0.clone());
|
|
c.append(&mut message.0.clone());
|
|
// how to concatenate better: https://stackoverflow.com/a/56490417
|
|
let mut sha1 = Sha1::default();
|
|
sha1.hash(&Bytes(c))
|
|
}
|
|
|
|
pub fn verify(message: &Bytes, key: &Bytes, mac: &Bytes) -> bool {
|
|
authenticate(message, key) == *mac
|
|
}
|
|
|
|
pub fn hmac_sha1(key: &Bytes, message: &Bytes) -> Bytes {
|
|
fn hash(message: Vec<u8>) -> Vec<u8> {
|
|
let mut sha1 = Sha1::default();
|
|
sha1.hash(&Bytes(message)).0
|
|
}
|
|
|
|
fn compute_block_sized_key(key: &Bytes, block_size: usize) -> Bytes {
|
|
let mut key = key.0.clone();
|
|
if key.len() > block_size {
|
|
key = hash(key);
|
|
}
|
|
|
|
while key.len() < block_size {
|
|
key.push(0x0);
|
|
}
|
|
Bytes(key)
|
|
}
|
|
|
|
const BLOCK_SIZE: usize = 64;
|
|
let block_sized_key = compute_block_sized_key(key, BLOCK_SIZE);
|
|
let mut o_key_pad = Bytes::xor(&block_sized_key, &Bytes(vec![0x5c; BLOCK_SIZE])).0;
|
|
let mut i_key_pad = Bytes::xor(&block_sized_key, &Bytes(vec![0x36; BLOCK_SIZE])).0;
|
|
|
|
// hash(o_key_pad ∥ hash(i_key_pad ∥ message))
|
|
i_key_pad.append(&mut message.0.clone());
|
|
let mut inner_hashed = hash(i_key_pad);
|
|
o_key_pad.append(&mut inner_hashed);
|
|
Bytes(hash(o_key_pad))
|
|
}
|