use futures::io::{AsyncRead, AsyncReadExt};
use crate::sha256::get_final_chunks_sha;
pub fn encode(bytes: &[u8]) -> [u8; 20] {
let mut h0: u32 = 0x67452301;
let mut h1: u32 = 0xEFCDAB89;
let mut h2: u32 = 0x98BADCFE;
let mut h3: u32 = 0x10325476;
let mut h4: u32 = 0xC3D2E1F0;
let bytes = get_normalized_message_bytes(bytes);
for chunk_num in 0..(bytes.len() >> 6) {
let chunk_start_index = chunk_num << 6;
let chunk: &[u8] = &bytes[chunk_start_index..chunk_start_index + 64];
let result = process_chunk(chunk, h0, h1, h2, h3, h4);
h0 = (h0 as u64 + result.0 as u64) as u32;
h1 = (h1 as u64 + result.1 as u64) as u32;
h2 = (h2 as u64 + result.2 as u64) as u32;
h3 = (h3 as u64 + result.3 as u64) as u32;
h4 = (h4 as u64 + result.4 as u64) as u32;
}
let mut hh: [u8; 20] = [0; 20];
hh[..4].copy_from_slice(&h0.to_be_bytes());
hh[4..8].copy_from_slice(&h1.to_be_bytes());
hh[8..12].copy_from_slice(&h2.to_be_bytes());
hh[12..16].copy_from_slice(&h3.to_be_bytes());
hh[16..].copy_from_slice(&h4.to_be_bytes());
hh
}
pub async fn encode_async<R>(mut reader: R) -> Result<[u8; 20], std::io::Error>
where
R: AsyncRead + Unpin,
{
let mut h0: u32 = 0x67452301;
let mut h1: u32 = 0xEFCDAB89;
let mut h2: u32 = 0x98BADCFE;
let mut h3: u32 = 0x10325476;
let mut h4: u32 = 0xC3D2E1F0;
let mut buffer = [0u8; 64];
let mut buffer_len = 0;
let mut total_length = 0;
loop {
let bytes_read = reader.read(&mut buffer[buffer_len..]).await?;
if bytes_read == 0 {
break;
}
buffer_len += bytes_read;
total_length += bytes_read;
if buffer_len == 64 {
let result = process_chunk(&buffer, h0, h1, h2, h3, h4);
h0 = h0.wrapping_add(result.0);
h1 = h1.wrapping_add(result.1);
h2 = h2.wrapping_add(result.2);
h3 = h3.wrapping_add(result.3);
h4 = h4.wrapping_add(result.4);
buffer_len = 0;
}
}
let final_chunk2 =
get_final_chunks_sha(&mut buffer, total_length, buffer_len);
let result = process_chunk(&buffer, h0, h1, h2, h3, h4);
h0 = h0.wrapping_add(result.0);
h1 = h1.wrapping_add(result.1);
h2 = h2.wrapping_add(result.2);
h3 = h3.wrapping_add(result.3);
h4 = h4.wrapping_add(result.4);
if let Some(chunk2) = final_chunk2 {
let result = process_chunk(&chunk2, h0, h1, h2, h3, h4);
h0 = h0.wrapping_add(result.0);
h1 = h1.wrapping_add(result.1);
h2 = h2.wrapping_add(result.2);
h3 = h3.wrapping_add(result.3);
h4 = h4.wrapping_add(result.4);
}
let mut hh: [u8; 20] = [0; 20];
hh[0..4].copy_from_slice(&h0.to_be_bytes());
hh[4..8].copy_from_slice(&h1.to_be_bytes());
hh[8..12].copy_from_slice(&h2.to_be_bytes());
hh[12..16].copy_from_slice(&h3.to_be_bytes());
hh[16..20].copy_from_slice(&h4.to_be_bytes());
Ok(hh)
}
fn get_normalized_message_bytes(bytes: &[u8]) -> Vec<u8> {
let message_length: u64 = bytes.len() as u64;
let message_bit_length: u64 = message_length << 3;
let mut bytes: Vec<u8> = bytes.to_vec();
bytes.push(0x80);
for _ in 0..64 - (message_length + 9) % 64 {
bytes.push(0);
}
let message_length_as_bytes: [u8; 8] = message_bit_length.to_be_bytes();
bytes.extend_from_slice(&message_length_as_bytes);
bytes
}
fn process_chunk(
chunk: &[u8],
a: u32,
b: u32,
c: u32,
d: u32,
e: u32,
) -> (u32, u32, u32, u32, u32) {
let mut chunk_u32 = [0u32; 16];
for i in 0..16 {
let start = i * 4;
chunk_u32[i] = u32::from_be_bytes([
chunk[start],
chunk[start + 1],
chunk[start + 2],
chunk[start + 3]
]);
}
let mut w: [u32; 80] = [0; 80];
w[0..16].copy_from_slice(&chunk_u32);
for num in 16..80 {
w[num] = (w[num - 3] ^ w[num - 8] ^ w[num - 14] ^ w[num - 16]).rotate_left(1);
}
let mut a = a;
let mut b = b;
let mut c = c;
let mut d = d;
let mut e = e;
for num in 0..80 {
let (f, k) = match num {
0..=19 => {
let f = d ^ (b & (c ^ d));
(f, 0x5A827999)
},
20..=39 => {
let f = b ^ c ^ d;
(f, 0x6ED9EBA1)
},
40..=59 => {
let f = (b & c) ^ (d & (b ^ c));
(f, 0x8F1BBCDC)
},
_ => {
let f = b ^ c ^ d;
(f, 0xCA62C1D6)
}
};
let temp = a.rotate_left(5).wrapping_add(f).wrapping_add(e).wrapping_add(k).wrapping_add(w[num]);
e = d;
d = c;
c = b.rotate_left(30);
b = a;
a = temp;
}
(a, b, c, d, e)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_normalized_message_bytes() {
let bytes = get_normalized_message_bytes("message".as_bytes());
let message = b"message";
assert_eq!(bytes.len(), 64);
assert_eq!(bytes[0], message[0]);
assert_eq!(bytes[6], message[6]);
assert_eq!(bytes[7], 0x80);
assert_eq!(bytes[8], 0x0);
assert_eq!(bytes[63], 56);
}
#[tokio::test]
async fn test_encode_quick_brown_fox_dog() {
let sha1 = encode_async("The quick brown fox jumps over the lazy dog".as_bytes()).await.unwrap();
assert_eq!(
sha1,
[
0x2f, 0xd4, 0xe1, 0xc6, 0x7a, 0x2d, 0x28, 0xfc, 0xed, 0x84, 0x9e, 0xe1, 0xbb, 0x76,
0xe7, 0x39, 0x1b, 0x93, 0xeb, 0x12
]
);
}
#[tokio::test]
async fn test_encode_quick_brown_fox_cog() {
let sha1 = encode_async("The quick brown fox jumps over the lazy cog".as_bytes()).await.unwrap();
assert_eq!(
sha1,
[
0xde, 0x9f, 0x2c, 0x7f, 0xd2, 0x5e, 0x1b, 0x3a, 0xfa, 0xd3, 0xe8, 0x5a, 0x0b, 0xd1,
0x7d, 0x9b, 0x10, 0x0d, 0xb4, 0xb3
]
);
}
#[tokio::test]
async fn test_encode_empty_string() {
let sha1 = encode_async("".as_bytes()).await.unwrap();
assert_eq!(
sha1,
[
0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60,
0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09
]
);
}
#[tokio::test]
async fn test_encode_message_digest() {
let sha1 = encode_async("message digest".as_bytes()).await.unwrap();
assert_eq!(
sha1,
[
0xc1, 0x22, 0x52, 0xce, 0xda, 0x8b, 0xe8, 0x99, 0x4d, 0x5f, 0xa0, 0x29, 0x0a, 0x47,
0x23, 0x1c, 0x1d, 0x16, 0xaa, 0xe3
]
);
}
#[tokio::test]
async fn test_encode_abcdefghijklmnopqrstuvwxyz() {
let sha1 = encode_async("abcdefghijklmnopqrstuvwxyz".as_bytes()).await.unwrap();
assert_eq!(
sha1,
[
0x32, 0xd1, 0x0c, 0x7b, 0x8c, 0xf9, 0x65, 0x70, 0xca, 0x04, 0xce, 0x37, 0xf2, 0xa1,
0x9d, 0x84, 0x24, 0x0d, 0x3a, 0x89
]
);
}
#[tokio::test]
async fn test_encode_alphanumeric_string() {
let sha1 = encode_async("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".as_bytes()).await.unwrap();
assert_eq!(
sha1,
[
0x76, 0x1c, 0x45, 0x7b, 0xf7, 0x3b, 0x14, 0xd2, 0x7e, 0x9e, 0x92, 0x65, 0xc4, 0x6f,
0x4b, 0x4d, 0xda, 0x11, 0xf9, 0x40
]
);
}
#[tokio::test]
async fn test_encode2() {
let bytes = [
0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x37, 0x66, 0x37, 0xc8, 0x92, 0x3d,
0xc5, 0x95, 0xe4, 0x1a, 0x27, 0x97, 0x2e, 0x99, 0x51, 0x21, 0x1d, 0x40, 0xce, 0x98,
];
let sha1 = encode_async(bytes.as_slice()).await.unwrap();
assert_eq!(
sha1,
[
0xb6, 0x17, 0x31, 0x86, 0x55, 0x05, 0x72, 0x64, 0xe2, 0x8b, 0xc0, 0xb6, 0xfb, 0x37,
0x8c, 0x8e, 0xf1, 0x46, 0xbe, 0x00
]
);
}
}