cryptopals/src/sha1.rs

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))
}