use super::sha1::Sha1;
use super::sha2::HashAlg;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Md {
Sha1,
Sha256,
Sha384,
Sha512,
}
#[derive(Clone)]
pub enum MdHasher {
Sha1(Sha1),
Sha2(super::sha2::Hasher),
}
impl MdHasher {
pub fn new(md: Md) -> MdHasher {
match md.sha2() {
None => MdHasher::Sha1(Sha1::new()),
Some(h) => MdHasher::Sha2(super::sha2::Hasher::new(h)),
}
}
pub fn update(&mut self, data: &[u8]) {
match self {
MdHasher::Sha1(h) => h.update(data),
MdHasher::Sha2(h) => h.update(data),
}
}
pub fn finish(self) -> Vec<u8> {
match self {
MdHasher::Sha1(h) => h.finish().to_vec(),
MdHasher::Sha2(h) => h.finish().to_vec(),
}
}
}
impl From<HashAlg> for Md {
fn from(h: HashAlg) -> Md {
match h {
HashAlg::Sha256 => Md::Sha256,
HashAlg::Sha384 => Md::Sha384,
HashAlg::Sha512 => Md::Sha512,
}
}
}
impl Md {
pub fn sha2(self) -> Option<HashAlg> {
match self {
Md::Sha1 => None,
Md::Sha256 => Some(HashAlg::Sha256),
Md::Sha384 => Some(HashAlg::Sha384),
Md::Sha512 => Some(HashAlg::Sha512),
}
}
pub fn output_len(self) -> usize {
match self {
Md::Sha1 => 20,
Md::Sha256 => 32,
Md::Sha384 => 48,
Md::Sha512 => 64,
}
}
pub fn block_len(self) -> usize {
match self {
Md::Sha1 | Md::Sha256 => 64,
_ => 128,
}
}
pub fn from_oid(oid: &str) -> Option<Md> {
Some(match oid {
"1.3.14.3.2.26" | "1.2.840.113549.2.7" => Md::Sha1,
"2.16.840.1.101.3.4.2.1" | "1.2.840.113549.2.9" => Md::Sha256,
"2.16.840.1.101.3.4.2.2" | "1.2.840.113549.2.10" => Md::Sha384,
"2.16.840.1.101.3.4.2.3" | "1.2.840.113549.2.11" => Md::Sha512,
_ => return None,
})
}
pub fn oid(self) -> &'static str {
match self {
Md::Sha1 => "1.3.14.3.2.26",
Md::Sha256 => "2.16.840.1.101.3.4.2.1",
Md::Sha384 => "2.16.840.1.101.3.4.2.2",
Md::Sha512 => "2.16.840.1.101.3.4.2.3",
}
}
pub fn hmac_oid(self) -> &'static str {
match self {
Md::Sha1 => "1.2.840.113549.2.7",
Md::Sha256 => "1.2.840.113549.2.9",
Md::Sha384 => "1.2.840.113549.2.10",
Md::Sha512 => "1.2.840.113549.2.11",
}
}
pub fn hash(self, data: &[u8]) -> Vec<u8> {
let mut h = MdHasher::new(self);
h.update(data);
h.finish()
}
pub fn hmac(self, key: &[u8], parts: &[&[u8]]) -> Vec<u8> {
if let Some(h) = self.sha2() {
return h.hmac(key, parts).to_vec();
}
let bl = self.block_len();
let mut k = [0u8; 128];
if key.len() > bl {
let d = self.hash(key);
k[..d.len()].copy_from_slice(&d);
} else {
k[..key.len()].copy_from_slice(key);
}
let mut pad = [0u8; 128];
for (p, b) in pad[..bl].iter_mut().zip(&k[..bl]) {
*p = b ^ 0x36;
}
let mut inner = MdHasher::new(self);
inner.update(&pad[..bl]);
for p in parts {
inner.update(p);
}
let ih = inner.finish();
for (p, b) in pad[..bl].iter_mut().zip(&k[..bl]) {
*p = b ^ 0x5c;
}
let mut outer = MdHasher::new(self);
outer.update(&pad[..bl]);
outer.update(&ih);
crate::util::zeroize(&mut k);
crate::util::zeroize(&mut pad);
outer.finish()
}
pub fn pbkdf2(self, password: &[u8], salt: &[u8], iterations: u32, len: usize) -> Vec<u8> {
let mut out = Vec::with_capacity(len);
let mut block = 1u32;
while out.len() < len {
let mut u = self.hmac(password, &[salt, &block.to_be_bytes()]);
let mut t = u.clone();
for _ in 1..iterations {
u = self.hmac(password, &[&u]);
for (a, b) in t.iter_mut().zip(&u) {
*a ^= b;
}
}
let take = (len - out.len()).min(t.len());
out.extend_from_slice(&t[..take]);
block += 1;
}
out
}
pub fn pkcs12_kdf(self, password: &[u8], salt: &[u8], id: u8, iterations: u32, len: usize) -> Vec<u8> {
let v = self.block_len();
let u = self.output_len();
let fill = |src: &[u8]| -> Vec<u8> {
if src.is_empty() {
return Vec::new();
}
let n = v * src.len().div_ceil(v);
(0..n).map(|i| src[i % src.len()]).collect()
};
let mut i_buf = fill(salt);
i_buf.extend(fill(password));
let mut out = Vec::with_capacity(len);
loop {
let mut h = MdHasher::new(self);
h.update(&vec![id; v]);
h.update(&i_buf);
let mut a = h.finish();
for _ in 1..iterations {
a = self.hash(&a);
}
let take = (len - out.len()).min(u);
out.extend_from_slice(&a[..take]);
if out.len() >= len {
return out;
}
let b: Vec<u8> = (0..v).map(|i| a[i % u]).collect();
for chunk in i_buf.chunks_mut(v) {
let mut carry = 1u16;
for k in (0..v).rev() {
let s = chunk[k] as u16 + b[k] as u16 + carry;
chunk[k] = s as u8;
carry = s >> 8;
}
}
}
}
}
pub const PKCS12_KEY: u8 = 1;
pub const PKCS12_IV: u8 = 2;
pub const PKCS12_MAC: u8 = 3;
pub fn bmp_password(password: &str) -> Vec<u8> {
let mut out = Vec::new();
for u in password.encode_utf16() {
out.extend_from_slice(&u.to_be_bytes());
}
out.extend_from_slice(&[0, 0]);
out
}
#[cfg(test)]
mod tests {
use super::*;
use crate::util::{hex, unhex};
#[test]
fn hmac_sha1_rfc2202() {
assert_eq!(hex(&Md::Sha1.hmac(&[0x0b; 20], &[b"Hi There"])), "b617318655057264e28bc0b6fb378c8ef146be00");
assert_eq!(hex(&Md::Sha1.hmac(b"Jefe", &[b"what do ya want ", b"for nothing?"])), "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79");
assert_eq!(
hex(&Md::Sha1.hmac(&[0xaa; 80], &[b"Test Using Larger Than Block-Size Key - Hash Key First"])),
"aa4ae5e15272d00e95705637ce8a3b55ed402112"
);
assert_eq!(hex(&Md::Sha256.hmac(b"Jefe", &[b"what do ya want for nothing?"])), "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843");
}
#[test]
fn pbkdf2_rfc6070_and_sha256() {
assert_eq!(hex(&Md::Sha1.pbkdf2(b"password", b"salt", 1, 20)), "0c60c80f961f0e71f3a9b524af6012062fe037a6");
assert_eq!(hex(&Md::Sha1.pbkdf2(b"password", b"salt", 2, 20)), "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957");
assert_eq!(hex(&Md::Sha1.pbkdf2(b"password", b"salt", 4096, 20)), "4b007901b765489abead49d926f721d065a429c1");
assert_eq!(
hex(&Md::Sha1.pbkdf2(b"passwordPASSWORDpassword", b"saltSALTsaltSALTsaltSALTsaltSALTsalt", 4096, 25)),
"3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"
);
assert_eq!(hex(&Md::Sha256.pbkdf2(b"password", b"salt", 2, 32)), "ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43");
}
#[test]
fn pkcs12_kdf_vectors() {
let pw = bmp_password("smeg");
let salt = unhex("0A58CF64530D823F");
assert_eq!(hex(&Md::Sha1.pkcs12_kdf(&pw, &salt, PKCS12_KEY, 1, 24)), "8aaae6297b6cb04642ab5b077851284eb7128f1a2a7fbca3");
assert_eq!(hex(&Md::Sha1.pkcs12_kdf(&pw, &salt, PKCS12_IV, 1, 8)), "79993dfe048d3b76");
let pw = bmp_password("queeg");
let salt = unhex("1682C0FC5B3F7EC5");
assert_eq!(hex(&Md::Sha1.pkcs12_kdf(&pw, &salt, PKCS12_KEY, 1000, 24)), "483dd6e919d7de2e8e648ba8f862f3fbfbdc2bcb2c02957f");
assert_eq!(hex(&Md::Sha1.pkcs12_kdf(&pw, &salt, PKCS12_IV, 1000, 8)), "9d461d1b00355c50");
assert_eq!(hex(&bmp_password("ab")), "0061006200 00".replace(' ', ""));
}
}