3
\$\begingroup\$

I just finished making an implementation of a SHA256 hashing function (https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf) in Rust and I was looking for some feedback.

As far as I can tell it works properly, but I haven't used Rust before so I'm looking for some advice regarding the style. Performance advice would also be nice, it runs a bit slower than my implementation in C (1.8 vs 1.4 seconds for some test file I have). In my testing I ran with cargo run --release inputfile.txt.

The program is in two parts, main.rs and shafuncs.rs

main.rs

use std::io::Read;
use std::fs::File;
use std::num::Wrapping;
use std::env;

mod shafuncs;
use shafuncs::CHUNKBYTES;

macro_rules! wraparray {
    ( $( $x:expr),* ) => {
        [$(Wrapping($x)),*]
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let filename = &args[1];
    let mut f = File::open(filename).expect("Could not open file.");

    let mut buffer: [u8; CHUNKBYTES] = [0; CHUNKBYTES];
    let mut readlen;
    let mut messagelength : u64 = 0;

    let mut digest: [Wrapping<u32>; 8]= wraparray![
            0x6a09e667,
            0xbb67ae85,
            0x3c6ef372,
            0xa54ff53a,
            0x510e527f,
            0x9b05688c,
            0x1f83d9ab,
            0x5be0cd19];


    while {readlen = f.read(&mut buffer).expect("Could not read from file!"); readlen==CHUNKBYTES} {
        messagelength += readlen as u64;
        hashround(&mut digest, buffer)
    }
    messagelength += readlen as u64;
    shafuncs::padmessage(&mut buffer, readlen, messagelength);
    hashround(&mut digest, buffer);

    // Final output
    for i in digest.iter() {
        print!("{:08x}", i);
    }
    println!("  {}", filename);
}



fn hashround(digest: &mut [Wrapping<u32>; 8], bytebuffer: [u8; CHUNKBYTES]) {
    let mut t1: Wrapping<u32>;
    let mut t2: Wrapping<u32>;

    let wordbuffer = shafuncs::bytestowords(bytebuffer);

    // Message Schedule
    let w = shafuncs::message_schedule(wordbuffer);

    let mut a: Wrapping<u32> = digest[0];
    let mut b: Wrapping<u32> = digest[1];
    let mut c: Wrapping<u32> = digest[2];
    let mut d: Wrapping<u32> = digest[3];
    let mut e: Wrapping<u32> = digest[4];
    let mut f: Wrapping<u32> = digest[5];
    let mut g: Wrapping<u32> = digest[6];
    let mut h: Wrapping<u32> = digest[7];



    for t in 0..64 {
        t1 = h + shafuncs::ls1(e) + shafuncs::ch(e,f,g) + K[t] + w[t];
        t2 = shafuncs::ls0(a) + shafuncs::maj(a,b,c);
        h = g;
        g = f;
        f = e;
        e = d+t1;
        d = c;
        c = b;
        b = a;
        a = t1+t2;
    }


    digest[0] += a;
    digest[1] += b;
    digest[2] += c;
    digest[3] += d;
    digest[4] += e;
    digest[5] += f;
    digest[6] += g;
    digest[7] += h;

}



const K: [Wrapping<u32>; 64] = wraparray![

    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
    ];

shafuncs.rs

use std::num::Wrapping;
const WORDSIZE_BITS: usize = 32;
const LENBYTES: usize = 8;
pub const CHUNKBYTES: usize = 64;
const ONEPAD: u8 = 0x80;

fn rotr(x: Wrapping<u32>, n: usize) -> Wrapping<u32> {
    (x >> n) | (x << (WORDSIZE_BITS - n) )
}

pub fn ch(x: Wrapping<u32>, y: Wrapping<u32>, z: Wrapping<u32>) -> Wrapping<u32> {
    (x & y) ^ ((!x) & z)
}

pub fn maj(x: Wrapping<u32>, y: Wrapping<u32>, z: Wrapping<u32>) -> Wrapping<u32> {
    (x & y) ^ (x & z) ^ (y & z)
}

pub fn ls0(x: Wrapping<u32>) -> Wrapping<u32> {
    rotr(x, 2) ^ rotr(x,13) ^ rotr(x,22)
}

pub fn ls1(x: Wrapping<u32>) -> Wrapping<u32> {
    rotr(x,6) ^ rotr(x,11) ^ rotr(x,25)
}

fn s0(x: Wrapping<u32>) -> Wrapping<u32> {
    rotr(x,7) ^ rotr(x,18) ^ (x >> 3)
}

fn s1(x: Wrapping<u32>) -> Wrapping<u32> {
    rotr(x,17) ^ rotr(x,19) ^ (x >> 10)
}

pub fn padmessage(bytebuffer : &mut [u8], readbyte : usize, messagelength: u64) -> () {
    let bitsize: u64 = messagelength * 8;
    let mut pos: usize = readbyte ;
    bytebuffer[pos] = ONEPAD;
    pos+=1;
    for i in pos..(CHUNKBYTES-LENBYTES) {
        bytebuffer[i] = 0;
    }

    let mut v: u8;
    for i in 0..8 {
        v = ((bitsize >> i*8) & 0xff) as u8;
        bytebuffer[63-i] = v;
    }

}

pub fn message_schedule(wordbuffer: [Wrapping<u32>; 16]) -> [Wrapping<u32>; 64] {
    let mut ms: [Wrapping<u32>; 64] = [Wrapping(0); 64];
    ms[..16].clone_from_slice(&wordbuffer);
    for t in 16..64 {
        ms[t] = s1(ms[t-2]) + ms[t-7] + s0(ms[t-15]) + ms[t-16];
    }

    ms
}

pub fn bytestowords(bytebuffer: [u8; CHUNKBYTES]) -> [Wrapping<u32>; 16] {
    let mut wordbuffer: [Wrapping<u32>; 16] = [Wrapping(0);16];
    let mut v: Wrapping<u32>;
    for i in 0..16 {
        v=Wrapping(0);
        v += Wrapping(bytebuffer[4*i] as u32) << (3*8);
        v += Wrapping(bytebuffer[4*i+1] as u32) << (2*8);
        v += Wrapping(bytebuffer[4*i+2] as u32) << (1*8);
        v += Wrapping(bytebuffer[4*i+3] as u32);
        wordbuffer[i] = v;
    }

    wordbuffer

}
\$\endgroup\$
  • \$\begingroup\$ Check out -C target-cpu=native. Also, assuming your benchmark includes I/O, note that stdout is line buffered and must be locked on every call to print. See this helpful article. \$\endgroup\$ – JayDepp Jun 7 at 17:45
  • \$\begingroup\$ Also note that returning the array [Wrapping<u32>; 64] requires copying 4*64 = 256 bytes if not optimized away. You may prefer taking a &mut [Wrapping<u32>; 64] argument if the function is in a hot loop or otherwise determined to be taking a lot of time. \$\endgroup\$ – JayDepp Jun 7 at 17:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.