crypto-async-rs 0.1.3

High-performance pure Rust cryptographic library with async streaming support
Documentation
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);
    //break message into 512 - bit chunks (64 bytes = 2^6)
    for chunk_num in 0..(bytes.len() >> 6) {
        //break chunk into sixteen 32 - bit big - endian words
        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());
        //println!("message {:?}", b"message");
        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();
        //2fd4e1c67a2d28fced849ee1bb76e7391b93eb12
        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();
        //de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3
        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();
        //da39a3ee5e6b4b0d3255bfef95601890afd80709
        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
            ]
        );
    }

}