2

I'm trying to make a function in Rust that will return a HMAC-SHA256 digest. I've been working from the description at Wikipedia and RFC 2104.

I've been struggling with returning the correct HMAC. I'm using ring for the SHA256 digests but no matter what I try, I can't seem to get the right result. I suspect it might have something to do with .as_ref().to_vec() conversions. Even if that's true, I don't know how to continue from that. Not everything from RFC 2104 is implemented in the following code, but it highlights my issue.

extern crate ring;
use ring::{digest, test};

pub fn hmac(k: Vec<u8>, mut m: Vec<u8>) -> Vec<u8> {
    // Initialize ipad and opad as byte vectors with SHA256 blocksize
    let ipad = vec![0x5C; 64];
    let opad = vec![0x36; 64];
    // iround and oround are used to seperate the two steps with XORing
    let mut iround = vec![];
    let mut oround = vec![];

    for count in 0..k.len() {
        iround.push(k[count] ^ ipad[count]);
        oround.push(k[count] ^ opad[count]);
    }

    iround.append(&mut m); // m is emptied here
    iround = (digest::digest(&digest::SHA256, &iround).as_ref()).to_vec();
    oround.append(&mut iround); // iround is emptied here
    oround = (digest::digest(&digest::SHA256, &oround).as_ref()).to_vec();
    let hashed_mac = oround.to_vec();

    return hashed_mac;
}

#[test]
fn test_hmac_digest() {
    let k = vec![0x61; 64];
    let m = vec![0x62; 64];
    let actual = hmac(k, m);
    // Expected value taken from: https://www.freeformatter.com/hmac-generator.html#ad-output
    let expected = test::from_hex("f6cbb37b326d36f2f27d294ac3bb46a6aac29c1c9936b985576041bfb338ae70").unwrap();
    assert_eq!(actual, expected);
}

These are the digests:

Actual = [139, 141, 144, 52, 11, 3, 48, 112, 117, 7, 56, 151, 163, 65, 152, 195, 163, 164, 26, 250, 178, 100, 187, 230, 89, 61, 191, 164, 146, 228, 180, 62]

Expected = [246, 203, 179, 123, 50, 109, 54, 242, 242, 125, 41, 74, 195, 187, 70, 166, 170, 194, 156, 28, 153, 54, 185, 133, 87, 96, 65, 191, 179, 56, 174, 112]
Community
  • 1
  • 1
  • *I suspect it might have something to do with `.as_ref().to_vec()` conversions* — why do you suspect that? – Shepmaster Feb 05 '18 at 20:37
  • The `.as_ref()` should return a byte slice `&[u8]` and that there could be a problem with converting that to a `Vec`. I tried with `.extend_from_slice()`, instead of `.append()` (to get rid of the conversion) earlier today, had no luck with that either. –  Feb 05 '18 at 20:43
  • 1
    You just swapped inner and outer padding, it's working otherwise. – emulbreh Feb 05 '18 at 21:46
  • That is next level idiocy. I can't believe I missed that. –  Feb 06 '18 at 10:08

1 Answers1

2

As mentioned in a comment, you have swapped the bytes for the inner and outer padding. Refer back to the Wikipedia page:

o_key_pad = key xor [0x5c * blockSize]   //Outer padded key
i_key_pad = key xor [0x36 * blockSize]   //Inner padded key

Here's what my take on the function would look like. I believe it has less allocation:

extern crate ring;

use ring::{digest, test};

const BLOCK_SIZE: usize = 64;

pub fn hmac(k: &[u8], m: &[u8]) -> Vec<u8> {
    assert_eq!(k.len(), BLOCK_SIZE);

    let mut i_key_pad: Vec<_> = k.iter().map(|&k| k ^ 0x36).collect();
    let mut o_key_pad: Vec<_> = k.iter().map(|&k| k ^ 0x5C).collect();

    i_key_pad.extend_from_slice(m);

    let hash = |v| digest::digest(&digest::SHA256, v);

    let a = hash(&i_key_pad);

    o_key_pad.extend_from_slice(a.as_ref());

    hash(&o_key_pad).as_ref().to_vec()
}

#[test]
fn test_hmac_digest() {
    let k = [0x61; BLOCK_SIZE];
    let m = [0x62; BLOCK_SIZE];
    let actual = hmac(&k, &m);

    // Expected value taken from: https://www.freeformatter.com/hmac-generator.html#ad-output
    let expected = test::from_hex("f6cbb37b326d36f2f27d294ac3bb46a6aac29c1c9936b985576041bfb338ae70").unwrap();
    assert_eq!(actual, expected);
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Very nice! I'm fairly new to Rust and haven't seen `let hash = |v| digest::digest(&digest::SHA256, v);`. Is that a shorthand way of writing a function? –  Feb 06 '18 at 10:40
  • @brycx This is the syntax to define a [closure](https://doc.rust-lang.org/book/second-edition/ch13-01-closures.html). It's similar to a function, except that it can capture variables from the local scope. This particular closure doesn't close over any local variables, so it could have been written as a function, too. – Sven Marnach Feb 06 '18 at 12:00