feat: finish p28
This commit is contained in:
8
problems/p28/Cargo.toml
Normal file
8
problems/p28/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "p28"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
sha1 = "0.10"
|
||||
hex = { workspace = true }
|
||||
203
problems/p28/src/main.rs
Normal file
203
problems/p28/src/main.rs
Normal file
@@ -0,0 +1,203 @@
|
||||
// SHA-1 pseudocode
|
||||
// Pseudocode for the SHA-1 algorithm follows:
|
||||
//
|
||||
// Note 1: All variables are unsigned 32-bit quantities and wrap modulo 232 when calculating, except for
|
||||
// ml, the message length, which is a 64-bit quantity, and
|
||||
// hh, the message digest, which is a 160-bit quantity.
|
||||
// Note 2: All constants in this pseudo code are in big endian.
|
||||
// Within each word, the most significant byte is stored in the leftmost byte position
|
||||
//
|
||||
// Initialize variables:
|
||||
//
|
||||
// h0 = 0x67452301
|
||||
// h1 = 0xEFCDAB89
|
||||
// h2 = 0x98BADCFE
|
||||
// h3 = 0x10325476
|
||||
// h4 = 0xC3D2E1F0
|
||||
//
|
||||
// ml = message length in bits (always a multiple of the number of bits in a character).
|
||||
//
|
||||
// Pre-processing:
|
||||
// append the bit '1' to the message e.g. by adding 0x80 if message length is a multiple of 8 bits.
|
||||
// append 0 ≤ k < 512 bits '0', such that the resulting message length in bits
|
||||
// is congruent to −64 ≡ 448 (mod 512)
|
||||
// append ml, the original message length in bits, as a 64-bit big-endian integer.
|
||||
// Thus, the total length is a multiple of 512 bits.
|
||||
//
|
||||
// Process the message in successive 512-bit chunks:
|
||||
// break message into 512-bit chunks
|
||||
// for each chunk
|
||||
// break chunk into sixteen 32-bit big-endian words w[i], 0 ≤ i ≤ 15
|
||||
//
|
||||
// Message schedule: extend the sixteen 32-bit words into eighty 32-bit words:
|
||||
// for i from 16 to 79
|
||||
// Note 3: SHA-0 differs by not having this leftrotate.
|
||||
// w[i] = (w[i-3] xor w[i-8] xor w[i-14] xor w[i-16]) leftrotate 1
|
||||
//
|
||||
// Initialize hash value for this chunk:
|
||||
// a = h0
|
||||
// b = h1
|
||||
// c = h2
|
||||
// d = h3
|
||||
// e = h4
|
||||
//
|
||||
// Main loop:[3][56]
|
||||
// for i from 0 to 79
|
||||
// if 0 ≤ i ≤ 19 then
|
||||
// f = (b and c) or ((not b) and d)
|
||||
// k = 0x5A827999
|
||||
// else if 20 ≤ i ≤ 39
|
||||
// f = b xor c xor d
|
||||
// k = 0x6ED9EBA1
|
||||
// else if 40 ≤ i ≤ 59
|
||||
// f = (b and c) xor (b and d) xor (c and d)
|
||||
// k = 0x8F1BBCDC
|
||||
// else if 60 ≤ i ≤ 79
|
||||
// f = b xor c xor d
|
||||
// k = 0xCA62C1D6
|
||||
//
|
||||
// temp = (a leftrotate 5) + f + e + k + w[i]
|
||||
// e = d
|
||||
// d = c
|
||||
// c = b leftrotate 30
|
||||
// b = a
|
||||
// a = temp
|
||||
//
|
||||
// Add this chunk's hash to result so far:
|
||||
// h0 = h0 + a
|
||||
// h1 = h1 + b
|
||||
// h2 = h2 + c
|
||||
// h3 = h3 + d
|
||||
// h4 = h4 + e
|
||||
//
|
||||
// Produce the final hash value (big-endian) as a 160-bit number:
|
||||
// hh = (h0 leftshift 128) or (h1 leftshift 96) or (h2 leftshift 64) or (h3 leftshift 32) or h4
|
||||
// The number hh is the message digest, which can be written in hexadecimal (base 16).
|
||||
//
|
||||
// The chosen constant values used in the algorithm were assumed to be nothing up my sleeve numbers:
|
||||
//
|
||||
// The four round constants k are 230 times the square roots of 2, 3, 5 and 10. However they were incorrectly rounded to the nearest integer instead of being rounded to the nearest odd integer, with equilibrated proportions of zero and one bits. As well, choosing the square root of 10 (which is not a prime) made it a common factor for the two other chosen square roots of primes 2 and 5, with possibly usable arithmetic properties across successive rounds, reducing the strength of the algorithm against finding collisions on some bits.
|
||||
// The first four starting values for h0 through h3 are the same with the MD5 algorithm, and the fifth (for h4) is similar. However they were not properly verified for being resistant against inversion of the few first rounds to infer possible collisions on some bits, usable by multiblock differential attacks.
|
||||
// Instead of the formulation from the original FIPS PUB 180-1 shown, the following equivalent expressions may be used to compute f in the main loop above:
|
||||
//
|
||||
// Bitwise choice between c and d, controlled by b.
|
||||
// (0 ≤ i ≤ 19): f = d xor (b and (c xor d)) (alternative 1)
|
||||
// (0 ≤ i ≤ 19): f = (b and c) or ((not b) and d) (alternative 2)
|
||||
// (0 ≤ i ≤ 19): f = (b and c) xor ((not b) and d) (alternative 3)
|
||||
// (0 ≤ i ≤ 19): f = vec_sel(d, c, b) (alternative 4)
|
||||
// [premo08]
|
||||
// Bitwise majority function.
|
||||
// (40 ≤ i ≤ 59): f = (b and c) or (d and (b or c)) (alternative 1)
|
||||
// (40 ≤ i ≤ 59): f = (b and c) or (d and (b xor c)) (alternative 2)
|
||||
// (40 ≤ i ≤ 59): f = (b and c) xor (d and (b xor c)) (alternative 3)
|
||||
// (40 ≤ i ≤ 59): f = (b and c) xor (b and d) xor (c and d) (alternative 4)
|
||||
// (40 ≤ i ≤ 59): f = vec_sel(c, b, c xor d) (alternative 5)
|
||||
// It was also shown[57] that for the rounds 32–79 the computation of:
|
||||
//
|
||||
// w[i] = (w[i-3] xor w[i-8] xor w[i-14] xor w[i-16]) leftrotate 1
|
||||
// can be replaced with:
|
||||
//
|
||||
// w[i] = (w[i-6] xor w[i-16] xor w[i-28] xor w[i-32]) leftrotate 2
|
||||
// This transformation keeps all operands 64-bit aligned and, by removing the dependency of w[i] on w[i-3], allows efficient SIMD implementation with a vector length of 4 like x86 SSE instructions.
|
||||
|
||||
use hex;
|
||||
use sha1::{Digest, Sha1};
|
||||
|
||||
fn sha1(input: &[u8]) -> [u8; 20] {
|
||||
let mut h0 = 0x67452301u32;
|
||||
let mut h1 = 0xEFCDAB89u32;
|
||||
let mut h2 = 0x98BADCFEu32;
|
||||
let mut h3 = 0x10325476u32;
|
||||
let mut h4 = 0xC3D2E1F0u32;
|
||||
|
||||
let message_bits_len = input.len() * 8;
|
||||
let mut buffer = input.to_vec();
|
||||
buffer.push(0x80u8);
|
||||
|
||||
while (buffer.len() % 64) != 56 {
|
||||
buffer.push(0x00u8);
|
||||
}
|
||||
|
||||
buffer.extend_from_slice(&(message_bits_len as u64).to_be_bytes());
|
||||
|
||||
for chunk in buffer.chunks(64) {
|
||||
let mut extend_buf = [0u32; 80];
|
||||
for i in 0..16 {
|
||||
let bytes: [u8; 4] = chunk[4 * i..4 * i + 4].try_into().unwrap();
|
||||
extend_buf[i] = u32::from_be_bytes(bytes);
|
||||
}
|
||||
for i in 16..80 {
|
||||
extend_buf[i] =
|
||||
(extend_buf[i - 3] ^ extend_buf[i - 8] ^ extend_buf[i - 14] ^ extend_buf[i - 16])
|
||||
.rotate_left(1);
|
||||
}
|
||||
let mut a = h0;
|
||||
let mut b = h1;
|
||||
let mut c = h2;
|
||||
let mut d = h3;
|
||||
let mut e = h4;
|
||||
for (i, &w_i) in extend_buf.iter().enumerate() {
|
||||
let f = match i {
|
||||
0..20 => (b & c) | (!b & d),
|
||||
20..40 => b ^ c ^ d,
|
||||
40..60 => (b & c) | (b & d) | (c & d),
|
||||
60..80 => b ^ c ^ d,
|
||||
_ => panic!("Invalid round: {}", i),
|
||||
};
|
||||
let k = match i {
|
||||
0..20 => 0x5A827999,
|
||||
20..40 => 0x6ED9EBA1,
|
||||
40..60 => 0x8F1BBCDC,
|
||||
60..80 => 0xCA62C1D6,
|
||||
_ => panic!("Invalid round: {}", i),
|
||||
};
|
||||
let temp = a
|
||||
.rotate_left(5)
|
||||
.wrapping_add(f)
|
||||
.wrapping_add(e)
|
||||
.wrapping_add(w_i)
|
||||
.wrapping_add(k);
|
||||
|
||||
e = d;
|
||||
d = c;
|
||||
c = b.rotate_left(30);
|
||||
b = a;
|
||||
a = temp;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
let mut digest = [0u8; 20];
|
||||
|
||||
digest[0..4].copy_from_slice(&h0.to_be_bytes());
|
||||
digest[4..8].copy_from_slice(&h1.to_be_bytes());
|
||||
digest[8..12].copy_from_slice(&h2.to_be_bytes());
|
||||
digest[12..16].copy_from_slice(&h3.to_be_bytes());
|
||||
digest[16..20].copy_from_slice(&h4.to_be_bytes());
|
||||
|
||||
digest
|
||||
}
|
||||
|
||||
fn sha1_mac(key: &[u8], message: &str) -> String {
|
||||
let mut input = key.to_vec();
|
||||
input.extend_from_slice(message.as_bytes());
|
||||
let hash = sha1(&input);
|
||||
hex::encode(hash)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut hasher = Sha1::new();
|
||||
hasher.update(b"hello world");
|
||||
let result = hasher.finalize();
|
||||
let result: [u8; 20] = result.into();
|
||||
let my_result = sha1(b"hello world");
|
||||
assert_eq!(result, my_result);
|
||||
|
||||
let key = b"helloworld";
|
||||
let message = "HELLOWORLD";
|
||||
println!("{}", sha1_mac(key, message));
|
||||
}
|
||||
Reference in New Issue
Block a user