1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
use std::cmp::Ordering;
use crate::sha1::Sha1;
pub struct HMAC;
impl HMAC {
/// This implements HMAC for SHA1.
/// The function takes in the bytes to hash and a secret key used to hash.
/// The high level overview looks like this:
/// input: &[u8]: the message to hash
/// secret_key: &[u8]: the secret key to use in hashing.
/// 1. The secret key is sized appropriately. (64 bytes in SHA-1)
/// - If the key is too long or too short, it is set to 64 bytes.
/// - If too short, it is padded with zeroes on the right
/// - if too long, it is hashed and then padded with zeroes on the right
/// 2. Two keys are generated
/// - An outer key, which takes the sized key and xors it with 0x5c
/// - And inner key, which is xored with 0x36.
/// 3. The inner key is concatenated with the input and then hashed.
/// 4. And then the hash is calculated of the outer key concatenated by that result.
pub fn mac(input: &[u8], secret_key: &[u8]) -> [u8; 20] {
// 1. If the secret key is too long, it is shortened by hashing it.
// Otherwise, the key can be used as is.
let block_sized_key = Self::block_size_key(secret_key);
// 2. Next, generate two keys.
// The first key, the outer key, is xored with 0x36.
let mut padded = [0x36; 40];
for (p, &k) in padded.iter_mut().zip(block_sized_key.iter()) {
*p ^= k;
}
let mut ih_input = padded.to_vec();
ih_input.extend(input);
let ih = Sha1::hash(&ih_input);
for p in padded.iter_mut() {
*p ^= 0x6a;
}
// 3. The key is hashed with the inner key first then the outer key hashes that.
let mut oh_input = padded.to_vec();
oh_input.extend(&ih);
Sha1::hash(&oh_input)
}
fn block_size_key(secret_key: &[u8]) -> [u8; 64] {
match secret_key.len().cmp(&64) {
Ordering::Less => {
let mut res = [0; 64];
for (i, b) in secret_key.iter().enumerate() {
res[i] = *b;
}
res
}
Ordering::Equal => {
let mut res = [0; 64];
res.copy_from_slice(secret_key);
res
}
Ordering::Greater => {
let mut res = [0; 64];
for (i, b) in Sha1::hash(secret_key).iter().enumerate() {
res[i] = *b;
}
res
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty() {
let h = HMAC::mac(b"", &[]);
assert_eq!(
h,
[
0x2c, 0x4c, 0x5d, 0xb0, // first
0x09, 0x76, 0xff, 0xdb, // second
0x10, 0xdb, 0xd5, 0x32, // third
0xe2, 0x78, 0x35, 0xa9, // fourth
0x84, 0x8e, 0x6c, 0xef, // fifth
]
);
}
}