sha-1 wip
This commit is contained in:
parent
e8c0d38021
commit
e9ab63efe4
5 changed files with 138 additions and 15 deletions
16
src/hash.rs
16
src/hash.rs
|
@ -78,6 +78,22 @@ pub fn words_to_bytes_le(words: impl AsRef<[u32]>) -> Vec<u8> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn bytes_to_words_be(bytes: impl AsRef<[u8]>) -> Vec<u32> {
|
||||||
|
bytes
|
||||||
|
.as_ref()
|
||||||
|
.array_chunks::<4>()
|
||||||
|
.map(|chunk| u32::from_be_bytes(*chunk))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn words_to_bytes_be(words: impl AsRef<[u32]>) -> Vec<u8> {
|
||||||
|
words
|
||||||
|
.as_ref()
|
||||||
|
.iter()
|
||||||
|
.flat_map(|w| w.to_be_bytes())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -46,21 +46,21 @@ pub fn pad(message: impl AsRef<[u8]>) -> Vec<u8> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute an invidiual step in the md4 algorithm
|
// compute an invidiual step in the md4 algorithm
|
||||||
fn step([mut a, b, c, d]: [u32; 4], words: &[u32], index: usize) -> [u32; 4] {
|
fn step([mut a, b, c, d]: [u32; 4], words: &[u32], i: usize) -> [u32; 4] {
|
||||||
// choose function and constant based on which round is currently active
|
// choose function and constant based on which round is currently active
|
||||||
let (constant, round_function) = match index {
|
let (f, k) = match i {
|
||||||
0..=15 => (C1, F),
|
0..=15 => (F, C1),
|
||||||
16..=31 => (C2, G),
|
16..=31 => (G, C2),
|
||||||
32..=47 => (C3, H),
|
32..=47 => (H, C3),
|
||||||
_ => panic!("This function shouldn't be called using an index outside 0..48"),
|
_ => panic!("This function shouldn't be called using an index outside 0..48"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// main operation
|
// main operation
|
||||||
a = round_function(b, c, d)
|
a = f(b, c, d)
|
||||||
.wrapping_add(a)
|
.wrapping_add(a)
|
||||||
.wrapping_add(words[W[index]])
|
.wrapping_add(words[W[i]])
|
||||||
.wrapping_add(constant)
|
.wrapping_add(k)
|
||||||
.rotate_left(S[index]);
|
.rotate_left(S[i]);
|
||||||
|
|
||||||
[a, b, c, d]
|
[a, b, c, d]
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,12 +70,12 @@ fn step([mut a, b, c, d]: [u32; 4], words: &[u32], index: usize) -> [u32; 4] {
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
pub fn hash(message: impl AsRef<[u8]>) -> Digest<16> {
|
pub fn hash(message: impl AsRef<[u8]>) -> Digest<16> {
|
||||||
|
// the padding function for MD5 is exactly equivalent to the MD4 version, so we reuse it.
|
||||||
let padded = pad(message);
|
let padded = pad(message);
|
||||||
let buffer = padded.array_chunks::<64>().map(bytes_to_words_le).fold(
|
let buffer = padded.array_chunks::<64>().map(bytes_to_words_le).fold(
|
||||||
[A, B, C, D],
|
[A, B, C, D],
|
||||||
|[a, b, c, d], words| {
|
|[a, b, c, d], words| {
|
||||||
println!("{words:08x?}");
|
// initialize state
|
||||||
|
|
||||||
let mut state = [a, b, c, d];
|
let mut state = [a, b, c, d];
|
||||||
|
|
||||||
for i in 0..64 {
|
for i in 0..64 {
|
||||||
|
@ -84,6 +84,7 @@ pub fn hash(message: impl AsRef<[u8]>) -> Digest<16> {
|
||||||
state.rotate_right(1);
|
state.rotate_right(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add the computed state to the buffer
|
||||||
[
|
[
|
||||||
a.wrapping_add(state[0]),
|
a.wrapping_add(state[0]),
|
||||||
b.wrapping_add(state[1]),
|
b.wrapping_add(state[1]),
|
||||||
|
|
111
src/hash/sha1.rs
111
src/hash/sha1.rs
|
@ -1,4 +1,62 @@
|
||||||
use crate::hash::Digest;
|
use crate::hash::{bytes_to_words_be, words_to_bytes_be, Digest};
|
||||||
|
|
||||||
|
// based on RFC3174, US Secure Hash Algorithm 1
|
||||||
|
|
||||||
|
// round functions
|
||||||
|
const F1: fn(u32, u32, u32) -> u32 = |x, y, z| (x & y) | ((!x) & z);
|
||||||
|
const F2: fn(u32, u32, u32) -> u32 = |x, y, z| x ^ y ^ z;
|
||||||
|
const F3: fn(u32, u32, u32) -> u32 = |x, y, z| (x & y) | (x & z) | (y & z);
|
||||||
|
const F4: fn(u32, u32, u32) -> u32 = |x, y, z| x ^ y ^ z;
|
||||||
|
|
||||||
|
// round constants
|
||||||
|
const K1: u32 = 0x5a827999;
|
||||||
|
const K2: u32 = 0x6ed9eba1;
|
||||||
|
const K3: u32 = 0x8f1bbcdc;
|
||||||
|
const K4: u32 = 0xca62c1d6;
|
||||||
|
|
||||||
|
// buffer 2 initial constants
|
||||||
|
const H0: u32 = 0x67452301;
|
||||||
|
const H1: u32 = 0xefcdab89;
|
||||||
|
const H2: u32 = 0x98badcfe;
|
||||||
|
const H3: u32 = 0x10325476;
|
||||||
|
const H4: u32 = 0xc3d2e1f0;
|
||||||
|
|
||||||
|
fn pad(message: impl AsRef<[u8]>) -> Vec<u8> {
|
||||||
|
let mut message = message.as_ref().to_vec();
|
||||||
|
let message_length = message.len().wrapping_mul(8) as u64;
|
||||||
|
|
||||||
|
// push 1 bit (little endian)
|
||||||
|
message.push(0x80);
|
||||||
|
|
||||||
|
// pad with 0 bits until length is congruent with 64 mod 56 bytes
|
||||||
|
while (message.len() % 64) < 56 {
|
||||||
|
message.push(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// append the length of the original message (big endian)
|
||||||
|
message.extend(message_length.to_be_bytes());
|
||||||
|
|
||||||
|
message
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step([mut a, b, c, d, e]: [u32; 5], words: &[u32], i: usize) -> [u32; 5] {
|
||||||
|
let (k, f) = match i {
|
||||||
|
0..=19 => (K1, F1),
|
||||||
|
20..=39 => (K2, F2),
|
||||||
|
40..=59 => (K3, F3),
|
||||||
|
60..=79 => (K4, F4),
|
||||||
|
_ => panic!("step function should not be called with index outside of range 0..80"),
|
||||||
|
};
|
||||||
|
|
||||||
|
a = a
|
||||||
|
.rotate_left(5)
|
||||||
|
.wrapping_add(f(b, c, d))
|
||||||
|
.wrapping_add(e)
|
||||||
|
.wrapping_add(k)
|
||||||
|
.wrapping_add(words[i]);
|
||||||
|
|
||||||
|
[e, a, b.rotate_left(30), c, d]
|
||||||
|
}
|
||||||
|
|
||||||
/// Computes the SHA1 digest of the input bytes.
|
/// Computes the SHA1 digest of the input bytes.
|
||||||
///
|
///
|
||||||
|
@ -15,7 +73,38 @@ use crate::hash::Digest;
|
||||||
/// assert_eq!(digest.to_string(), "a9993e364706816aba3e25717850c26c9cd0d89d")
|
/// assert_eq!(digest.to_string(), "a9993e364706816aba3e25717850c26c9cd0d89d")
|
||||||
/// ```
|
/// ```
|
||||||
pub fn hash(message: impl AsRef<[u8]>) -> Digest<20> {
|
pub fn hash(message: impl AsRef<[u8]>) -> Digest<20> {
|
||||||
todo!()
|
let padded = pad(message);
|
||||||
|
|
||||||
|
let buffer = padded
|
||||||
|
.array_chunks::<64>()
|
||||||
|
.map(|chunk| bytes_to_words_be(*chunk))
|
||||||
|
.fold([H0, H1, H2, H3, H4], |[a, b, c, d, e], words| {
|
||||||
|
// extend 16 words to 80 words
|
||||||
|
|
||||||
|
// initialize state
|
||||||
|
let mut state = [a, b, c, d, e];
|
||||||
|
|
||||||
|
// perform 80 steps
|
||||||
|
for i in 0..80 {
|
||||||
|
state = step(state, &words, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add computed round state to buffer
|
||||||
|
[
|
||||||
|
a.wrapping_add(state[0]),
|
||||||
|
b.wrapping_add(state[1]),
|
||||||
|
c.wrapping_add(state[2]),
|
||||||
|
d.wrapping_add(state[3]),
|
||||||
|
e.wrapping_add(state[4]),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
let digest = *words_to_bytes_be(buffer)
|
||||||
|
.array_chunks::<20>()
|
||||||
|
.next()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Digest(digest)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -23,5 +112,21 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sha1_hash() {}
|
fn sha1_pad() {
|
||||||
|
let expected: [u8; 64] = [
|
||||||
|
0x61, 0x62, 0x63, 0x64, 0x65, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x28,
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
expected.to_vec(),
|
||||||
|
pad([0b01100001, 0b01100010, 0b01100011, 0b01100100, 0b01100101])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sha1_hash() {
|
||||||
|
// panic!();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
#![allow(
|
#![allow(
|
||||||
clippy::unreadable_literal,
|
clippy::unreadable_literal,
|
||||||
clippy::missing_panics_doc,
|
clippy::missing_panics_doc,
|
||||||
clippy::cast_possible_truncation
|
clippy::cast_possible_truncation,
|
||||||
|
clippy::many_single_char_names
|
||||||
)]
|
)]
|
||||||
// docs
|
// docs
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
Loading…
Reference in a new issue