diff --git a/Cargo.toml b/Cargo.toml index 409bbd06..a638f877 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "orion" -version = "0.4.3" +version = "0.5.0" authors = ["brycx "] description = "Easy and usable rust crypto" keywords = [ "cryptography", "hmac", "hkdf", "pbkdf2", "cshake" ] -categories = [ "cryptography" ] +categories = [ "cryptography", "no-std" ] readme = "README.md" repository = "https://github.com/brycx/orion" documentation = "https://docs.rs/orion" @@ -14,15 +14,16 @@ exclude = [ ".travis.yml", "benches/*", "fuzz/*", + "src/tests/*" ] [dependencies] rand = "0.5.5" sha2 = "0.7.1" tiny-keccak = "1.4.2" -clear_on_drop = "0.2.3" byte-tools = "0.2.0" -constant_time_eq = "0.1.3" +subtle = "0.7.0" +seckey = "0.9.1" [dev-dependencies] hex = "0.3.2" diff --git a/README.md b/README.md index f102ce69..44c66532 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,10 @@ professional. Look in the Alternatives section if this means orion is not for yo Currently contains: -* HMAC with SHA256, SHA384, SHA512 and SHA512/256. -* HKDF with the above HMAC options. -* PBKDF2 with the above HMAC options. -* cSHAKE128 and cSHAKE256. +* HMAC-SHA512 +* HKDF-HMAC-SHA512. +* PBKDF2-HMAC-SHA512. +* cSHAKE256. ***Note on cSHAKE***: The cSHAKE implementation currently relies on the `tiny-keccak` crate. Currently this crate @@ -31,35 +31,13 @@ will produce **incorrect results on big-endian based systems**. See [issue here] ### Usage ```rust extern crate orion; -use orion::{default, core::util}; +use orion::default; -// HMAC-SHA512/256 -let key = util::gen_rand_key(64).unwrap(); -let msg = "Some message".as_bytes(); +let password = "Password to be hashed".as_bytes(); -let expected_hmac = default::hmac(&key, msg).unwrap(); -assert!(default::hmac_verify(&expected_hmac, &key, &msg).unwrap()); +let password_hash = default::pbkdf2(password).unwrap(); -// HKDF-HMAC-SHA512/256 -let salt = util::gen_rand_key(64).unwrap(); -let data = "Some data".as_bytes(); -let info = "Some info".as_bytes(); - -let dk = default::hkdf(&salt, data, info, 64).unwrap(); -assert!(default::hkdf_verify(&dk, &salt, data, info, 64).unwrap()); - -// PBKDF2-HMAC-SHA512/256 -let password = "Secret password".as_bytes(); - -let dk = default::pbkdf2(password).unwrap(); -assert!(default::pbkdf2_verify(&dk, password).unwrap()); - -// cSHAKE256 -let data = "Not so random data".as_bytes(); -let custom = "Custom".as_bytes(); - -let hash = default::cshake(data, custom).unwrap(); -assert!(default::cshake_verify(hash, data, custom).unwrap()); +assert!(default::pbkdf2_verify(&password_hash, password).unwrap()); ``` diff --git a/benches/bench.rs b/benches/bench.rs index f816c527..4cb19cc1 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -2,68 +2,48 @@ extern crate orion; extern crate test; -use orion::core::options::KeccakVariantOption; -use orion::core::options::ShaVariantOption; -use orion::hazardous::cshake::CShake; -use orion::hazardous::hkdf::Hkdf; -use orion::hazardous::hmac::Hmac; -use orion::hazardous::pbkdf2::Pbkdf2; +use orion::hazardous::cshake; +use orion::hazardous::hkdf; +use orion::hazardous::hmac; +use orion::hazardous::pbkdf2; use test::Bencher; #[bench] fn bench_hmac(b: &mut Bencher) { b.iter(|| { - let hmac = Hmac { - secret_key: vec![0x01; 32], - data: vec![0x01; 32], - sha2: ShaVariantOption::SHA256, - }; - - hmac.finalize(); + let mut mac = hmac::init(&vec![0x01; 64]); + mac.update(&vec![0x01; 64]); + mac.finalize(); }); } #[bench] fn bench_hkdf(b: &mut Bencher) { b.iter(|| { - let hkdf = Hkdf { - salt: vec![0x01; 32], - ikm: vec![0x01; 32], - info: vec![0x01; 32], - length: 32, - hmac: ShaVariantOption::SHA256, - }; - - hkdf.derive_key().unwrap(); + let mut okm_out = [0u8; 64]; + hkdf::derive_key( + &vec![0x01; 64], + &vec![0x01; 64], + &vec![0x01; 64], + &mut okm_out, + ).unwrap(); }); } #[bench] fn bench_pbkdf2(b: &mut Bencher) { b.iter(|| { - let pbkdf = Pbkdf2 { - salt: vec![0x01; 32], - password: vec![0x01; 32], - iterations: 10000, - dklen: 32, - hmac: ShaVariantOption::SHA256, - }; - - pbkdf.derive_key().unwrap(); + let mut dk_out = [0u8; 64]; + pbkdf2::derive_key(&vec![0x01; 64], &vec![0x01; 64], 10000, &mut dk_out).unwrap(); }); } #[bench] fn bench_cshake(b: &mut Bencher) { b.iter(|| { - let cshake = CShake { - input: vec![0x01; 32], - name: vec![0x00; 0], - custom: vec![0x01; 32], - length: 64, - keccak: KeccakVariantOption::KECCAK256, - }; - - cshake.finalize().unwrap(); + let mut hash_out = [0u8; 64]; + let mut cshake = cshake::init(&vec![0x01; 64], None).unwrap(); + cshake.update(&vec![0x01; 64]); + cshake.finalize(&mut hash_out).unwrap(); }); } diff --git a/fuzz/fuzz_targets/cshake.rs b/fuzz/fuzz_targets/cshake.rs index d35d2a59..65aa4ef8 100644 --- a/fuzz/fuzz_targets/cshake.rs +++ b/fuzz/fuzz_targets/cshake.rs @@ -4,17 +4,10 @@ extern crate libfuzzer_sys; extern crate orion; extern crate rand; -use orion::core::options::KeccakVariantOption; -use orion::hazardous::cshake::CShake; +use orion::hazardous::cshake; use rand::prelude::*; -fn fuzz_cshake( - input: &[u8], - name: &[u8], - custom: &[u8], - len_max: usize, - keccak: KeccakVariantOption, -) { +fn fuzz_cshake(input: &[u8], name: &[u8], custom: &[u8], len_max: usize) { let mut rng = rand::thread_rng(); let len_rand = rng.gen_range(1, len_max + 1); @@ -22,63 +15,15 @@ fn fuzz_cshake( let mut mod_custom = custom.to_vec(); mod_custom.push(0u8); - let cshake = CShake { - input: input.to_vec(), - name: name.to_vec(), - custom: mod_custom, - length: len_rand, - keccak, - }; - - let hash = cshake.finalize().unwrap(); - - assert_eq!(cshake.verify(&hash).unwrap(), true); + let mut hash_out = vec![0u8; len_rand]; + let mut cshake = cshake::init(&mod_custom, Some(name)).unwrap(); + cshake.update(input); + cshake.finalize(&mut hash_out).unwrap(); } fuzz_target!(|data: &[u8]| { - fuzz_cshake(data, data, data, 65536, KeccakVariantOption::KECCAK256); - fuzz_cshake( - data, - &Vec::new(), - data, - 65536, - KeccakVariantOption::KECCAK256, - ); - fuzz_cshake( - data, - data, - &Vec::new(), - 65536, - KeccakVariantOption::KECCAK256, - ); - fuzz_cshake( - &Vec::new(), - data, - data, - 65536, - KeccakVariantOption::KECCAK256, - ); - - fuzz_cshake(data, data, data, 65536, KeccakVariantOption::KECCAK512); - fuzz_cshake( - data, - &Vec::new(), - data, - 65536, - KeccakVariantOption::KECCAK512, - ); - fuzz_cshake( - data, - data, - &Vec::new(), - 65536, - KeccakVariantOption::KECCAK512, - ); - fuzz_cshake( - &Vec::new(), - data, - data, - 65536, - KeccakVariantOption::KECCAK512, - ); + fuzz_cshake(data, data, data, 65536); + fuzz_cshake(data, &Vec::new(), data, 65536); + fuzz_cshake(data, data, &Vec::new(), 65536); + fuzz_cshake(&Vec::new(), data, data, 65536); }); diff --git a/fuzz/fuzz_targets/default.rs b/fuzz/fuzz_targets/default.rs index 32aa76fd..35bb5ca1 100644 --- a/fuzz/fuzz_targets/default.rs +++ b/fuzz/fuzz_targets/default.rs @@ -4,12 +4,13 @@ extern crate libfuzzer_sys; extern crate orion; extern crate rand; -use orion::core::util; use orion::default; +use orion::utilities::util; use rand::Rng; fn fuzz_default(data: &[u8]) -> () { - let rand_salt = util::gen_rand_key(64).unwrap(); + let mut rand_salt = [0u8; 64]; + util::gen_rand_key(&mut rand_salt).unwrap(); let mut rng = rand::thread_rng(); // cSHAKE `custom` can't be empty @@ -17,14 +18,11 @@ fn fuzz_default(data: &[u8]) -> () { mod_custom.push(0u8); if rng.gen() { - let len_hkdf: usize = rng.gen_range(1, 8161); - default::hkdf_verify( - &default::hkdf(&rand_salt, data, data, len_hkdf).unwrap(), + &default::hkdf(&rand_salt, data, data).unwrap(), &rand_salt, &data, data, - len_hkdf, ).unwrap(); default::hmac_verify(&default::hmac(&rand_salt, data).unwrap(), &rand_salt, data).unwrap(); @@ -34,7 +32,11 @@ fn fuzz_default(data: &[u8]) -> () { default::pbkdf2_verify(&default::pbkdf2(&password).unwrap(), &password).unwrap(); - default::cshake_verify(&default::cshake(&data, &mod_custom).unwrap(), &data, &mod_custom).unwrap(); + default::cshake_verify( + &default::cshake(&data, &mod_custom).unwrap(), + &data, + &mod_custom, + ).unwrap(); default::cshake_verify( &default::cshake("".as_bytes(), &mod_custom).unwrap(), "".as_bytes(), diff --git a/fuzz/fuzz_targets/hkdf.rs b/fuzz/fuzz_targets/hkdf.rs index b8d00fda..2919135e 100644 --- a/fuzz/fuzz_targets/hkdf.rs +++ b/fuzz/fuzz_targets/hkdf.rs @@ -4,35 +4,27 @@ extern crate libfuzzer_sys; extern crate orion; extern crate rand; -use orion::core::options::ShaVariantOption; -use orion::hazardous::hkdf::Hkdf; +use orion::hazardous::hkdf; use rand::prelude::*; -fn fuzz_hkdf(salt: &[u8], ikm: &[u8], info: &[u8], len_max: usize, hmac: ShaVariantOption) { +fn fuzz_hkdf(salt: &[u8], ikm: &[u8], info: &[u8], len_max: usize) { let mut rng = rand::thread_rng(); let okm_len_rand = rng.gen_range(1, len_max + 1); - let dk = Hkdf { - salt: salt.to_vec(), - ikm: ikm.to_vec(), - info: info.to_vec(), - length: okm_len_rand, - hmac, - }; + let prk = hkdf::extract(ikm, salt); + let mut dk_out = vec![0u8; okm_len_rand]; + hkdf::expand(&prk, info, &mut dk_out).unwrap(); - let prk = dk.extract(ikm, salt); - let dk_fin = dk.expand(&prk).unwrap(); - - assert_eq!(dk_fin, dk.derive_key().unwrap()); - assert_eq!(dk.verify(&dk_fin).unwrap(), true); + let exp_okm = dk_out.clone(); + assert!(hkdf::verify(&exp_okm, salt, ikm, info, &mut dk_out).unwrap()); } fuzz_target!(|data: &[u8]| { - fuzz_hkdf(data, data, data, 8160, ShaVariantOption::SHA256); + fuzz_hkdf(data, data, data, 8160); - fuzz_hkdf(data, data, data, 12240, ShaVariantOption::SHA384); + fuzz_hkdf(data, data, data, 12240); - fuzz_hkdf(data, data, data, 16320, ShaVariantOption::SHA512); + fuzz_hkdf(data, data, data, 16320); - fuzz_hkdf(data, data, data, 8160, ShaVariantOption::SHA512Trunc256); + fuzz_hkdf(data, data, data, 8160); }); diff --git a/fuzz/fuzz_targets/hmac.rs b/fuzz/fuzz_targets/hmac.rs index 8e764d8d..a9ed972a 100644 --- a/fuzz/fuzz_targets/hmac.rs +++ b/fuzz/fuzz_targets/hmac.rs @@ -2,31 +2,16 @@ #[macro_use] extern crate libfuzzer_sys; extern crate orion; -use orion::core::options::ShaVariantOption; -use orion::hazardous::hmac::*; +use orion::hazardous::hmac; -fn fuzz_hmac(secret_key: &[u8], data: &[u8], sha2: ShaVariantOption) { - let mac = Hmac { - secret_key: secret_key.to_vec(), - data: data.to_vec(), - sha2, - }; +fn fuzz_hmac(secret_key: &[u8], data: &[u8]) { + let mut mac = hmac::init(secret_key); + mac.update(data); - let (ipad, opad) = mac.pad_key(secret_key); let mac_def = mac.finalize(); - let mac_pbkdf2 = pbkdf2_hmac(&ipad, &opad, &mac.data, mac.sha2); - - assert_eq!(mac_def, mac_pbkdf2); - assert_eq!(mac.verify(&mac_def).unwrap(), true); - assert_eq!(mac.verify(&mac_pbkdf2).unwrap(), true); + assert_eq!(hmac::verify(&mac_def, secret_key, data).unwrap(), true); } fuzz_target!(|data: &[u8]| { - fuzz_hmac(data, data, ShaVariantOption::SHA256); - - fuzz_hmac(data, data, ShaVariantOption::SHA384); - - fuzz_hmac(data, data, ShaVariantOption::SHA512); - - fuzz_hmac(data, data, ShaVariantOption::SHA512Trunc256); + fuzz_hmac(data, data); }); diff --git a/fuzz/fuzz_targets/pbkdf2.rs b/fuzz/fuzz_targets/pbkdf2.rs index 42cca79e..d07e34f8 100644 --- a/fuzz/fuzz_targets/pbkdf2.rs +++ b/fuzz/fuzz_targets/pbkdf2.rs @@ -4,35 +4,26 @@ extern crate libfuzzer_sys; extern crate orion; extern crate rand; -use orion::core::options::ShaVariantOption; -use orion::hazardous::pbkdf2::Pbkdf2; +use orion::hazardous::pbkdf2; use rand::prelude::*; -fn fuzz_pbkdf2(password: &[u8], salt: &[u8], hmac: ShaVariantOption) { +fn fuzz_pbkdf2(password: &[u8], salt: &[u8]) { let mut rng = rand::thread_rng(); if rng.gen() { let iter: usize = rng.gen_range(1, 10001); let len: usize = rng.gen_range(1, 1025); - let dk = Pbkdf2 { - password: password.to_vec(), - salt: salt.to_vec(), - iterations: iter, - dklen: len, - hmac, - }; + let mut dk_out = vec![0u8; len]; - assert_eq!(dk.verify(&dk.derive_key().unwrap()).unwrap(), true); + pbkdf2::derive_key(password, salt, iter, &mut dk_out).unwrap(); + + let exp_dk = dk_out.clone(); + + assert!(pbkdf2::verify(&exp_dk, password, salt, iter, &mut dk_out).unwrap()); } } fuzz_target!(|data: &[u8]| { - fuzz_pbkdf2(data, data, ShaVariantOption::SHA256); - - fuzz_pbkdf2(data, data, ShaVariantOption::SHA384); - - fuzz_pbkdf2(data, data, ShaVariantOption::SHA512); - - fuzz_pbkdf2(data, data, ShaVariantOption::SHA512Trunc256); + fuzz_pbkdf2(data, data); }); diff --git a/fuzz/fuzz_targets/ring_compare.rs b/fuzz/fuzz_targets/ring_compare.rs index a59089a1..97b67cf4 100644 --- a/fuzz/fuzz_targets/ring_compare.rs +++ b/fuzz/fuzz_targets/ring_compare.rs @@ -5,54 +5,45 @@ extern crate orion; extern crate rand; extern crate ring; -use orion::core::options::ShaVariantOption; use orion::hazardous::hkdf; use orion::hazardous::hmac; use orion::hazardous::pbkdf2; use rand::prelude::*; use ring::digest; -use ring::hkdf::*; +use ring::hkdf::extract_and_expand as ring_hkdf; use ring::hmac as ring_hmac; use ring::pbkdf2 as ring_pbkdf2; -fn return_digest(sha2: ShaVariantOption) -> &'static digest::Algorithm { - match sha2 { - ShaVariantOption::SHA256 => &digest::SHA256, - ShaVariantOption::SHA384 => &digest::SHA384, - ShaVariantOption::SHA512 => &digest::SHA512, - ShaVariantOption::SHA512Trunc256 => &digest::SHA512_256, - } +fn return_digest() -> &'static digest::Algorithm { + &digest::SHA512 } -fn ro_hmac(buf1: &[u8], buf2: &[u8], sha2: ShaVariantOption) { - let key = buf1.to_vec(); - let message = buf2.to_vec(); - - let s_key = ring_hmac::SigningKey::new(return_digest(sha2), key.as_ref()); - let ring_signature = ring_hmac::sign(&s_key, message.as_ref()); +fn ro_hmac(buf1: &[u8], buf2: &[u8]) { + let key = buf1; + let message = buf2; - let orion_hmac = hmac::Hmac { - secret_key: key.to_vec(), - data: message.to_vec(), - sha2, - }; + let s_key = ring_hmac::SigningKey::new(return_digest(), key); + let ring_signature = ring_hmac::sign(&s_key, message); + let mut orion_hmac = hmac::init(key); + orion_hmac.update(message); let orion_signature = orion_hmac.finalize(); - let v_key = ring_hmac::VerificationKey::new(return_digest(sha2), key.as_ref()); + + let v_key = ring_hmac::VerificationKey::new(return_digest(), key); let mut ring_res = false; let mut ring_res_switch = false; - if ring_hmac::verify(&v_key, message.as_ref(), orion_signature.as_ref()).is_ok() { + if ring_hmac::verify(&v_key, message, orion_signature.as_ref()).is_ok() { ring_res = true; } - if ring_hmac::verify(&v_key, message.as_ref(), ring_signature.as_ref()).is_ok() { + if ring_hmac::verify(&v_key, message, ring_signature.as_ref()).is_ok() { ring_res_switch = true; } - let orion_res_switch = orion_hmac.verify(orion_signature.as_ref()).unwrap(); - let orion_res = orion_hmac.verify(ring_signature.as_ref()).unwrap(); + let orion_res_switch = hmac::verify(orion_signature.as_ref(), key, message).unwrap(); + let orion_res = hmac::verify(ring_signature.as_ref(), key, message).unwrap(); assert!(orion_res); assert!(orion_res_switch); @@ -60,103 +51,47 @@ fn ro_hmac(buf1: &[u8], buf2: &[u8], sha2: ShaVariantOption) { assert!(ring_res_switch); } -fn ro_hkdf(buf1: &[u8], buf2: &[u8], buf3: &[u8], hmac: ShaVariantOption) { - let salt = buf1.to_vec(); - let ikm = buf2.to_vec(); - let info = buf3.to_vec(); +fn ro_hkdf(buf1: &[u8], buf2: &[u8], buf3: &[u8]) { + let salt = buf1; + let ikm = buf2; + let info = buf3; let mut rng = thread_rng(); - - let okm_len: usize = match hmac { - ShaVariantOption::SHA256 => rng.gen_range(1, 8161), - ShaVariantOption::SHA384 => rng.gen_range(1, 12241), - ShaVariantOption::SHA512 => rng.gen_range(1, 16321), - ShaVariantOption::SHA512Trunc256 => rng.gen_range(1, 8161), - }; + let okm_len: usize = rng.gen_range(1, 8161); let mut out_okm = vec![0u8; okm_len]; + let mut out_okm_orion = vec![0u8; okm_len]; + + hkdf::derive_key(salt, ikm, info, &mut out_okm_orion).unwrap(); - let orion_hkdf = hkdf::Hkdf { - salt: salt.to_vec(), - ikm: ikm.to_vec(), - info: info.to_vec(), - length: okm_len, - hmac, - }; - - let orion_prk = orion_hkdf.extract(&orion_hkdf.salt, &orion_hkdf.ikm); - let orion_okm = orion_hkdf.expand(&orion_prk).unwrap(); - let orion_derived = orion_hkdf.derive_key().unwrap(); - - let s_key = ring_hmac::SigningKey::new(return_digest(hmac), salt.as_ref()); - let ring_prk = extract(&s_key, ikm.as_ref()); - expand(&ring_prk, &info, &mut out_okm); - assert_eq!(orion_okm, out_okm); - - extract_and_expand(&s_key, ikm.as_ref(), &info, &mut out_okm); - assert_eq!(orion_derived, out_okm); + let s_key = ring_hmac::SigningKey::new(return_digest(), salt); + ring_hkdf(&s_key, ikm, info, &mut out_okm); + assert_eq!(out_okm_orion, out_okm); } -fn ro_pbkdf2(buf1: &[u8], buf2: &[u8], hmac: ShaVariantOption) { - let salt = buf1.to_vec(); - let password = buf2.to_vec(); +fn ro_pbkdf2(buf1: &[u8], buf2: &[u8]) { + let salt = buf1; + let password = buf2; let mut rng = rand::thread_rng(); - let iter: usize = rng.gen_range(1, 10001); - let len: usize = rng.gen_range(1, 128); + let len: usize = rng.gen_range(1, 129); let mut dk_out = vec![0u8; len]; + let mut dk_out_orion = vec![0u8; len]; + + pbkdf2::derive_key(&password, &salt, iter, &mut dk_out_orion).unwrap(); + ring_pbkdf2::derive(return_digest(), iter as u32, &salt, &password, &mut dk_out); - let dk = pbkdf2::Pbkdf2 { - password: password.to_vec(), - salt: salt.to_vec(), - iterations: iter, - dklen: len, - hmac, - }; - - ring_pbkdf2::derive( - return_digest(hmac), - iter as u32, - &salt, - &password, - &mut dk_out, - ); - - let orion_dk = dk.derive_key().unwrap(); - - assert_eq!(dk_out, orion_dk); - - assert!( - ring_pbkdf2::verify(return_digest(hmac), iter as u32, &salt, &password, &dk_out).is_ok() - ); - assert!( - ring_pbkdf2::verify( - return_digest(hmac), - iter as u32, - &salt, - &password, - &orion_dk - ).is_ok() - ); - assert!(dk.verify(&dk_out).is_ok()); - assert!(dk.verify(&orion_dk).is_ok()); + assert_eq!(&dk_out, &dk_out_orion); + assert!(ring_pbkdf2::verify(return_digest(), iter as u32, &salt, &password, &dk_out_orion).is_ok()); + assert!(pbkdf2::verify(&dk_out, password, salt, iter, &mut dk_out_orion).unwrap()); } fuzz_target!(|data: &[u8]| { - let variants = [ - ShaVariantOption::SHA256, - ShaVariantOption::SHA384, - ShaVariantOption::SHA512, - ShaVariantOption::SHA512Trunc256, - ]; + ro_hmac(data, data); - for selec in variants.iter() { - ro_hmac(data, data, *selec); + ro_hkdf(data, data, data); - ro_hkdf(data, data, data, *selec); - - ro_pbkdf2(data, data, *selec); - } + ro_pbkdf2(data, data); }); diff --git a/fuzz/fuzz_targets/sp800_185_compare.rs b/fuzz/fuzz_targets/sp800_185_compare.rs index 7b73f483..8baee0c7 100644 --- a/fuzz/fuzz_targets/sp800_185_compare.rs +++ b/fuzz/fuzz_targets/sp800_185_compare.rs @@ -5,18 +5,11 @@ extern crate orion; extern crate rand; extern crate sp800_185; -use orion::core::options::KeccakVariantOption; -use orion::hazardous::cshake::CShake; +use orion::hazardous::cshake; use rand::prelude::*; use sp800_185::CShake as sp_cshake; -fn fuzz_cshake( - input: &[u8], - name: &[u8], - custom: &[u8], - len_max: usize, - keccak: KeccakVariantOption, -) { +fn fuzz_cshake(input: &[u8], name: &[u8], custom: &[u8], len_max: usize) { let mut rng = rand::thread_rng(); let len_rand = rng.gen_range(1, len_max + 1); @@ -24,75 +17,24 @@ fn fuzz_cshake( let mut mod_custom = custom.to_vec(); mod_custom.push(0u8); - let cshake = CShake { - input: input.to_vec(), - name: name.to_vec(), - custom: mod_custom.to_vec(), - length: len_rand, - keccak, - }; + let mut cshake = cshake::init(&mod_custom, Some(name)).unwrap(); + cshake.update(input); - let hash = cshake.finalize().unwrap(); - - let mut sp_cshake_hash = match &keccak { - KeccakVariantOption::KECCAK256 => sp_cshake::new_cshake128(name, &mod_custom), - KeccakVariantOption::KECCAK512 => sp_cshake::new_cshake256(name, &mod_custom), - }; + let mut hash_out = vec![0u8; len_rand]; + cshake.finalize(&mut hash_out).unwrap(); + let mut sp_cshake_hash = sp_cshake::new_cshake256(name, &mod_custom); sp_cshake_hash.update(input); + let mut sp_cshake_fin = vec![0u8; len_rand]; sp_cshake_hash.finalize(&mut sp_cshake_fin); - assert_eq!(hash.len(), sp_cshake_fin.len()); - assert_eq!(&hash, &sp_cshake_fin); - assert_eq!(cshake.verify(&hash).unwrap(), true); - assert_eq!(cshake.verify(&sp_cshake_fin).unwrap(), true); + assert_eq!(&hash_out, &sp_cshake_fin); } fuzz_target!(|data: &[u8]| { - fuzz_cshake(data, data, data, 65536, KeccakVariantOption::KECCAK256); - fuzz_cshake( - data, - &Vec::new(), - data, - 65536, - KeccakVariantOption::KECCAK256, - ); - fuzz_cshake( - data, - data, - &Vec::new(), - 65536, - KeccakVariantOption::KECCAK256, - ); - fuzz_cshake( - &Vec::new(), - data, - data, - 65536, - KeccakVariantOption::KECCAK256, - ); - - fuzz_cshake(data, data, data, 65536, KeccakVariantOption::KECCAK512); - fuzz_cshake( - data, - &Vec::new(), - data, - 65536, - KeccakVariantOption::KECCAK512, - ); - fuzz_cshake( - data, - data, - &Vec::new(), - 65536, - KeccakVariantOption::KECCAK512, - ); - fuzz_cshake( - &Vec::new(), - data, - data, - 65536, - KeccakVariantOption::KECCAK512, - ); + fuzz_cshake(data, data, data, 65536); + fuzz_cshake(data, &Vec::new(), data, 65536); + fuzz_cshake(data, data, &Vec::new(), 65536); + fuzz_cshake(&Vec::new(), data, data, 65536); }); diff --git a/src/core/options.rs b/src/core/options.rs deleted file mode 100644 index 6e65a759..00000000 --- a/src/core/options.rs +++ /dev/null @@ -1,144 +0,0 @@ -// MIT License - -// Copyright (c) 2018 brycx - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -use sha2; -use sha2::Digest; - -#[derive(Clone, Copy)] -/// SHA2 options and hashing. -pub enum ShaVariantOption { - SHA256, - SHA384, - SHA512, - SHA512Trunc256, -} - -impl ShaVariantOption { - /// Return the output size in bytes, matching SHA2 variant. - pub fn output_size(self) -> usize { - match self { - ShaVariantOption::SHA256 => 32, - ShaVariantOption::SHA384 => 48, - ShaVariantOption::SHA512 => 64, - ShaVariantOption::SHA512Trunc256 => 32, - } - } - - /// Return blocksize in bytes, matching SHA2 variant. - pub fn blocksize(self) -> usize { - match self { - ShaVariantOption::SHA256 => 64, - ShaVariantOption::SHA384 => 128, - ShaVariantOption::SHA512 => 128, - ShaVariantOption::SHA512Trunc256 => 128, - } - } - - /// Return a SHA2 digest of a given byte slice. - pub fn hash(self, data: &[u8]) -> Vec { - match self { - ShaVariantOption::SHA256 => { - let mut hash = sha2::Sha256::default(); - hash.input(data); - hash.result().to_vec() - } - ShaVariantOption::SHA384 => { - let mut hash = sha2::Sha384::default(); - hash.input(data); - hash.result().to_vec() - } - ShaVariantOption::SHA512 => { - let mut hash = sha2::Sha512::default(); - hash.input(data); - hash.result().to_vec() - } - ShaVariantOption::SHA512Trunc256 => { - let mut hash = sha2::Sha512Trunc256::default(); - hash.input(data); - hash.result().to_vec() - } - } - } -} - -#[derive(Clone, Copy)] -/// Keccak options. -pub enum KeccakVariantOption { - KECCAK256, - KECCAK512, -} - -#[cfg(test)] -mod test { - use core::options::ShaVariantOption; - extern crate hex; - use self::hex::decode; - - // These test cases are some picks from - // the [NIST SHAVS](https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/secure-hashing#shavs) - #[test] - fn shavs_256() { - let msg = decode("889468b1").unwrap(); - let expected_md = decode( - "855b2244b875ed9ae089fb10d84c85257f30c65ea1325c2f\ - 76727a582ba4c801", - ).unwrap(); - let actual_md = ShaVariantOption::SHA256.hash(&msg); - - assert_eq!(expected_md, actual_md); - } - - #[test] - fn shavs_384() { - let msg = decode("15247149").unwrap(); - let expected_md = decode( - "f1f2164a41471741d30ef3408be496e3f7903b2c005b57e9\ - d707cee8ab50777d4ddfc9348ad2aba7cca92fca3b7108e6", - ).unwrap(); - let actual_md = ShaVariantOption::SHA384.hash(&msg); - - assert_eq!(expected_md, actual_md); - } - - #[test] - fn shavs_512() { - let msg = decode("012c461b").unwrap(); - let expected_md = decode( - "4a49e900d69c87a95d1a3fefabe9dc767fd0d70d866f85ef05453\ - 7bb8f0a4224313590fee49fd65b76f4ea414ed457f0a12a52455570\ - 717cbb051ca2af23ca20", - ).unwrap(); - let actual_md = ShaVariantOption::SHA512.hash(&msg); - - assert_eq!(expected_md, actual_md); - } - - #[test] - fn shavs_512_trunc_256() { - let msg = decode("63d8cfd72768c44920d7b015460489ad578c063be19053889cb809").unwrap(); - let expected_md = - decode("876e59c8a64faf9d665f7cde5d42fbb331ba818ddcd284491ac51ed50e1613be").unwrap(); - let actual_md = ShaVariantOption::SHA512Trunc256.hash(&msg); - - assert_eq!(expected_md, actual_md); - } -} diff --git a/src/default.rs b/src/default.rs index 185b548d..cbcb0ca6 100644 --- a/src/default.rs +++ b/src/default.rs @@ -20,15 +20,13 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use core::options::KeccakVariantOption; -use core::options::ShaVariantOption; -use core::{errors::*, util}; -use hazardous::cshake::CShake; -use hazardous::hkdf::Hkdf; -use hazardous::hmac::Hmac; -use hazardous::pbkdf2::Pbkdf2; - -/// HMAC-SHA512/256. +use hazardous::cshake; +use hazardous::hkdf; +use hazardous::hmac; +use hazardous::pbkdf2; +use utilities::{errors::*, util}; + +/// HMAC-SHA512. /// # Parameters: /// - `secret_key`: The authentication key /// - `data`: Data to be authenticated @@ -41,41 +39,43 @@ use hazardous::pbkdf2::Pbkdf2; /// /// # Security: /// The secret key should always be generated using a CSPRNG. The `gen_rand_key` function -/// in `util` can be used for this. The recommended length for a secret key is the SHA functions digest -/// size in bytes. +/// in `util` can be used for this. /// /// # Example: /// ``` /// use orion::default; -/// use orion::core::util; +/// use orion::utilities::util; /// -/// let key = util::gen_rand_key(64).unwrap(); +/// let mut key = [0u8; 64]; +/// util::gen_rand_key(&mut key).unwrap(); /// let msg = "Some message.".as_bytes(); /// /// let hmac = default::hmac(&key, msg).unwrap(); /// ``` -pub fn hmac(secret_key: &[u8], data: &[u8]) -> Result, UnknownCryptoError> { +pub fn hmac(secret_key: &[u8], data: &[u8]) -> Result<[u8; 64], UnknownCryptoError> { if secret_key.len() < 64 { return Err(UnknownCryptoError); } - let mac = Hmac { - secret_key: secret_key.to_vec(), - data: data.to_vec(), - sha2: ShaVariantOption::SHA512Trunc256, - }; + let mut mac = hmac::init(secret_key); + mac.update(data); Ok(mac.finalize()) } -/// Verify an HMAC-SHA512/256 against a key and data in constant time, with Double-HMAC Verification. +/// Verify a HMAC-SHA512 MAC in constant time, with Double-HMAC Verification. +/// +/// # About: +/// This uses `default::hmac()` to generate the MAC. +/// /// # Example: /// /// ``` /// use orion::default; -/// use orion::core::util; +/// use orion::utilities::util; /// -/// let key = util::gen_rand_key(64).unwrap(); +/// let mut key = [0u8; 64]; +/// util::gen_rand_key(&mut key).unwrap(); /// let msg = "Some message.".as_bytes(); /// /// let expected_hmac = default::hmac(&key, msg).unwrap(); @@ -86,21 +86,19 @@ pub fn hmac_verify( secret_key: &[u8], data: &[u8], ) -> Result { - let mac = Hmac { - secret_key: secret_key.to_vec(), - data: data.to_vec(), - sha2: ShaVariantOption::SHA512Trunc256, - }; - - mac.verify(&expected_hmac) + hmac::verify(&expected_hmac, secret_key, data) } -/// HKDF-HMAC-SHA512/256. +/// HKDF-HMAC-SHA512. +/// +/// # About: +/// The output length is set to 32, which makes the derived key suitable for use with AES256. +/// /// # Parameters: -/// - `salt`: Optional salt value +/// - `salt`: Salt value /// - `input`: Input keying material /// - `info`: Optional context and application specific information (can be a zero-length string) -/// - `len`: Length of output keying material +/// /// /// See [RFC](https://tools.ietf.org/html/rfc5869#section-2.2) for more information. /// @@ -111,85 +109,76 @@ pub fn hmac_verify( /// # Security: /// Salts should always be generated using a CSPRNG. The `gen_rand_key` function /// in `util` can be used for this. The recommended length for a salt is 16 bytes as a minimum. -/// HKDF is not suitable for password storage. Even though a salt value is optional, it is strongly -/// recommended to use one. +/// HKDF is not suitable for password storage. /// /// # Example: /// ``` /// use orion::default; -/// use orion::core::util; +/// use orion::utilities::util; /// -/// let salt = util::gen_rand_key(32).unwrap(); +/// let mut salt = [0u8; 32]; +/// util::gen_rand_key(&mut salt).unwrap(); /// let data = "Some data.".as_bytes(); /// let info = "Some info.".as_bytes(); /// -/// let hkdf = default::hkdf(&salt, data, info, 32).unwrap(); +/// let hkdf = default::hkdf(&salt, data, info).unwrap(); /// ``` -pub fn hkdf( - salt: &[u8], - input: &[u8], - info: &[u8], - len: usize, -) -> Result, UnknownCryptoError> { +pub fn hkdf(salt: &[u8], input: &[u8], info: &[u8]) -> Result<[u8; 32], UnknownCryptoError> { if salt.len() < 16 { return Err(UnknownCryptoError); } - let hkdf = Hkdf { - salt: salt.to_vec(), - ikm: input.to_vec(), - info: info.to_vec(), - length: len, - hmac: ShaVariantOption::SHA512Trunc256, - }; + let mut okm = [0u8; 32]; + + hkdf::derive_key(salt, input, info, &mut okm).unwrap(); - hkdf.derive_key() + Ok(okm) } -/// Verify an HKDF-HMAC-SHA512/256 derived key in constant time. Both derived keys must -/// be of equal length. +/// Verify an HKDF-HMAC-SHA512 derived key in constant time. +/// +/// # About: +/// The expected key must be of length 32. This uses `default::hkdf()`. +/// /// # Example: /// /// ``` /// use orion::default; -/// use orion::core::util; +/// use orion::utilities::util; /// -/// let salt = util::gen_rand_key(32).unwrap(); +/// let mut salt = [0u8; 32]; +/// util::gen_rand_key(&mut salt).unwrap(); /// let data = "Some data.".as_bytes(); /// let info = "Some info.".as_bytes(); /// -/// let hkdf = default::hkdf(&salt, data, info, 32).unwrap(); -/// assert_eq!(default::hkdf_verify(&hkdf, &salt, data, info, 32).unwrap(), true); +/// let hkdf = default::hkdf(&salt, data, info).unwrap(); +/// assert_eq!(default::hkdf_verify(&hkdf, &salt, data, info).unwrap(), true); /// ``` pub fn hkdf_verify( expected_dk: &[u8], salt: &[u8], input: &[u8], info: &[u8], - len: usize, ) -> Result { - let hkdf = Hkdf { - salt: salt.to_vec(), - ikm: input.to_vec(), - info: info.to_vec(), - length: len, - hmac: ShaVariantOption::SHA512Trunc256, - }; - - hkdf.verify(&expected_dk) + if expected_dk.len() != 32 { + return Err(ValidationCryptoError); + } + + let mut okm = [0u8; 32]; + + hkdf::verify(&expected_dk, salt, input, info, &mut okm) } -/// PBKDF2-HMAC-SHA512/256. Suitable for password storage. +/// PBKDF2-HMAC-SHA512. Suitable for password storage. /// # About: /// This is meant to be used for password storage. /// - A salt of 32 bytes is automatically generated. /// - The derived key length is set to 32. /// - 512.000 iterations are used. -/// - The salt is prepended to the password before being passed to the PBKDF2 function. -/// - A byte vector of 64 bytes is returned. +/// - An array of 64 bytes is returned. /// -/// The first 32 bytes of this vector is the salt used to derive the key and the last 32 bytes -/// is the actual derived key. When using this function with `default::pbkdf2_verify` +/// The first 32 bytes of this array is the salt used to derive the key and the last 32 bytes +/// is the actual derived key. When using this function with `default::pbkdf2_verify()` /// then the seperation of salt and password are automatically handeled. /// /// # Exceptions: @@ -205,46 +194,30 @@ pub fn hkdf_verify( /// /// let derived_password = default::pbkdf2(password); /// ``` -pub fn pbkdf2(password: &[u8]) -> Result, UnknownCryptoError> { +pub fn pbkdf2(password: &[u8]) -> Result<[u8; 64], UnknownCryptoError> { if password.len() < 14 { return Err(UnknownCryptoError); } - let salt: Vec = util::gen_rand_key(32).unwrap(); - // Prepend salt to password before deriving key - let mut pass_extented: Vec = Vec::new(); - pass_extented.extend_from_slice(&salt); - pass_extented.extend_from_slice(password); - // Prepend salt to derived key - let mut dk = Vec::new(); - dk.extend_from_slice(&salt); - - let pbkdf2_dk = Pbkdf2 { - password: pass_extented, - salt, - iterations: 512_000, - dklen: 32, - hmac: ShaVariantOption::SHA512Trunc256, - }; - - // Output format: First 32 bytes are the salt, last 32 bytes are the derived key - dk.extend_from_slice(&pbkdf2_dk.derive_key().unwrap()); - - if dk.len() != 64 { - return Err(UnknownCryptoError); - } + let mut dk = [0u8; 64]; + let mut salt = [0u8; 32]; + util::gen_rand_key(&mut salt).unwrap(); + + pbkdf2::derive_key(password, &salt, 512_000, &mut dk[32..]).unwrap(); + + dk[..32].copy_from_slice(&salt); Ok(dk) } -/// Verify PBKDF2-HMAC-SHA512/256 derived key in constant time. +/// Verify PBKDF2-HMAC-SHA512 derived key in constant time. /// # About: -/// This function is meant to be used with the `default::pbkdf2` function in orion's default API. It can be +/// This function is meant to be used with the `default::pbkdf2()` function in orion's default API. It can be /// used without it, but then the `expected_dk` passed to the function must be constructed just as in -/// `default::pbkdf2`. See documention on `default::pbkdf2` for details on this. +/// `default::pbkdf2()`. See documention on `default::pbkdf2()` for details on this. /// # Exceptions: /// An exception will be thrown if: -/// - The expected derived key length is not 64 bytes. +/// - The length of `expected_dk` is not 64 bytes. /// # Example: /// /// ``` @@ -260,30 +233,10 @@ pub fn pbkdf2_verify(expected_dk: &[u8], password: &[u8]) -> Result = expected_dk[..32].to_vec(); - let mut pass_extented: Vec = Vec::new(); - pass_extented.extend_from_slice(&salt); - pass_extented.extend_from_slice(password); - - // Prepend salt to derived key - let mut dk = Vec::new(); - dk.extend_from_slice(&salt); + let mut dk = [0u8; 32]; + let salt = &expected_dk[..32]; - let pbkdf2_dk = Pbkdf2 { - password: pass_extented, - salt, - iterations: 512_000, - dklen: 32, - hmac: ShaVariantOption::SHA512Trunc256, - }; - - dk.extend_from_slice(&pbkdf2_dk.derive_key().unwrap()); - - if util::compare_ct(&dk, expected_dk).is_err() { - Err(ValidationCryptoError) - } else { - Ok(true) - } + pbkdf2::verify(&expected_dk[32..], password, salt, 512_000, &mut dk) } /// cSHAKE256. @@ -317,19 +270,25 @@ pub fn pbkdf2_verify(expected_dk: &[u8], password: &[u8]) -> Result Result, UnknownCryptoError> { - let cshake = CShake { - input: input.to_vec(), - name: Vec::new(), - custom: custom.to_vec(), - length: 64, - keccak: KeccakVariantOption::KECCAK512, - }; - - cshake.finalize() +pub fn cshake(input: &[u8], custom: &[u8]) -> Result<[u8; 64], UnknownCryptoError> { + if custom.is_empty() { + return Err(UnknownCryptoError); + } + + let mut hash = [0u8; 64]; + + let mut cshake = cshake::init(custom, None).unwrap(); + cshake.update(input); + cshake.finalize(&mut hash).unwrap(); + + Ok(hash) } -/// Verify a cSHAKE256 hash in constant time. The expected hash must be of length 64. +/// Verify a cSHAKE256 hash in constant time. +/// +/// # About: +/// The expected hash must be of length 64. This uses `default::cshake()`. +/// /// # Example: /// /// ``` @@ -346,15 +305,15 @@ pub fn cshake_verify( input: &[u8], custom: &[u8], ) -> Result { - let cshake = CShake { - input: input.to_vec(), - name: Vec::new(), - custom: custom.to_vec(), - length: 64, - keccak: KeccakVariantOption::KECCAK512, - }; - - cshake.verify(&expected) + if expected.len() != 64 { + return Err(ValidationCryptoError); + } + + if util::compare_ct(expected, &cshake(input, custom).unwrap()).is_err() { + Err(ValidationCryptoError) + } else { + Ok(true) + } } #[cfg(test)] @@ -362,18 +321,18 @@ mod test { extern crate hex; use self::hex::decode; - use core::util; use default; + use utilities::util; #[test] fn hmac_secret_key_too_short() { - assert!(default::hmac(&vec![0x61; 10], &vec![0x61; 10]).is_err()); + assert!(default::hmac(&[0x61; 10], &[0x61; 10]).is_err()); } #[test] fn hmac_secret_key_allowed_len() { - default::hmac(&vec![0x61; 64], &vec![0x61; 10]).unwrap(); - default::hmac(&vec![0x61; 78], &vec![0x61; 10]).unwrap(); + default::hmac(&[0x61; 64], &[0x61; 10]).unwrap(); + default::hmac(&[0x61; 78], &[0x61; 10]).unwrap(); } #[test] @@ -406,73 +365,100 @@ mod test { #[test] fn hkdf_verify() { - let salt = util::gen_rand_key(64).unwrap(); + let mut salt = [0u8; 64]; + util::gen_rand_key(&mut salt).unwrap(); let data = "Some data.".as_bytes(); let info = "Some info.".as_bytes(); - let hkdf_dk = default::hkdf(&salt, data, info, 64).unwrap(); + let hkdf_dk = default::hkdf(&salt, data, info).unwrap(); assert_eq!( - default::hkdf_verify(&hkdf_dk, &salt, data, info, 64).unwrap(), + default::hkdf_verify(&hkdf_dk, &salt, data, info).unwrap(), true ); } #[test] fn hkdf_verify_err() { - let salt = util::gen_rand_key(64).unwrap(); + let mut salt = [0u8; 64]; + util::gen_rand_key(&mut salt).unwrap(); let data = "Some data.".as_bytes(); let info = "Some info.".as_bytes(); - let mut hkdf_dk = default::hkdf(&salt, data, info, 64).unwrap(); - hkdf_dk.extend_from_slice(&[0u8; 4]); + let mut hkdf_dk = default::hkdf(&salt, data, info).unwrap(); + hkdf_dk[..4].copy_from_slice(&[0u8; 4]); - assert!(default::hkdf_verify(&hkdf_dk, &salt, data, info, 64).is_err()); + assert!(default::hkdf_verify(&hkdf_dk, &salt, data, info).is_err()); } + #[test] + fn hkdf_verify_exptected_too_long() { + let mut salt = [0u8; 64]; + util::gen_rand_key(&mut salt).unwrap(); + let data = "Some data.".as_bytes(); + let info = "Some info.".as_bytes(); + + assert!(default::hkdf_verify(&salt, &salt, data, info).is_err()); + } + + #[test] + fn hkdf_verify_exptected_too_short() { + let mut salt = [0u8; 16]; + util::gen_rand_key(&mut salt).unwrap(); + let data = "Some data.".as_bytes(); + let info = "Some info.".as_bytes(); + + assert!(default::hkdf_verify(&salt, &salt, data, info).is_err()); + } + + #[test] fn hkdf_salt_too_short() { - assert!(default::hkdf(&vec![0x61; 10], &vec![0x61; 10], &vec![0x61; 10], 20).is_err()); + assert!(default::hkdf(&[0x61; 10], &[0x61; 10], &[0x61; 10]).is_err()); } #[test] fn hkdf_salt_allowed_len() { - default::hkdf(&vec![0x61; 67], &vec![0x61; 10], &vec![0x61; 10], 20).unwrap(); - default::hkdf(&vec![0x61; 89], &vec![0x61; 10], &vec![0x61; 10], 20).unwrap(); + default::hkdf(&[0x61; 67], &[0x61; 10], &[0x61; 10]).unwrap(); + default::hkdf(&[0x61; 89], &[0x61; 10], &[0x61; 10]).unwrap(); } #[test] fn pbkdf2_verify() { - let password = util::gen_rand_key(64).unwrap(); + let mut password = [0u8; 64]; + util::gen_rand_key(&mut password).unwrap(); - let pbkdf2_dk = default::pbkdf2(&password).unwrap(); + let pbkdf2_dk: [u8; 64] = default::pbkdf2(&password).unwrap(); assert_eq!(default::pbkdf2_verify(&pbkdf2_dk, &password).unwrap(), true); } #[test] fn pbkdf2_verify_err() { - let password = util::gen_rand_key(64).unwrap(); + let mut password = [0u8; 64]; + util::gen_rand_key(&mut password).unwrap(); let mut pbkdf2_dk = default::pbkdf2(&password).unwrap(); - pbkdf2_dk.extend_from_slice(&[0u8; 4]); + pbkdf2_dk[..10].copy_from_slice(&[0x61; 10]); assert!(default::pbkdf2_verify(&pbkdf2_dk, &password).is_err()); } #[test] fn pbkdf2_verify_expected_dk_too_long() { - let password = util::gen_rand_key(32).unwrap(); + let mut password = [0u8; 32]; + util::gen_rand_key(&mut password).unwrap(); - let mut pbkdf2_dk = default::pbkdf2(&password).unwrap(); - pbkdf2_dk.extend_from_slice(&[0u8; 1]); + let mut pbkdf2_dk = [0u8; 65]; + pbkdf2_dk[..64].copy_from_slice(&default::pbkdf2(&password).unwrap()); assert!(default::pbkdf2_verify(&pbkdf2_dk, &password).is_err()); } #[test] fn pbkdf2_verify_expected_dk_too_short() { - let password = util::gen_rand_key(64).unwrap(); + let mut password = [0u8; 64]; + util::gen_rand_key(&mut password).unwrap(); let pbkdf2_dk = default::pbkdf2(&password).unwrap(); @@ -481,14 +467,17 @@ mod test { #[test] fn pbkdf2_password_too_short() { - let password = util::gen_rand_key(13).unwrap(); + let mut password = [0u8; 13]; + util::gen_rand_key(&mut password).unwrap(); assert!(default::pbkdf2(&password).is_err()); } #[test] fn cshake_ok() { - let data = util::gen_rand_key(64).unwrap(); + let mut data = [0u8; 64]; + util::gen_rand_key(&mut data).unwrap(); + let custom = "Some custom string".as_bytes(); assert!(default::cshake(&data, custom).is_ok()); @@ -496,7 +485,9 @@ mod test { #[test] fn cshake_empty_custom_err() { - let data = util::gen_rand_key(64).unwrap(); + let mut data = [0u8; 64]; + util::gen_rand_key(&mut data).unwrap(); + let custom = "".as_bytes(); assert!(default::cshake(&data, custom).is_err()); @@ -504,7 +495,9 @@ mod test { #[test] fn cshake_verify() { - let data = util::gen_rand_key(64).unwrap(); + let mut data = [0u8; 64]; + util::gen_rand_key(&mut data).unwrap(); + let custom = "Some custom string".as_bytes(); let cshake = default::cshake(&data, custom).unwrap(); @@ -517,7 +510,9 @@ mod test { #[test] fn cshake_verify_err() { - let data = util::gen_rand_key(64).unwrap(); + let mut data = [0u8; 64]; + util::gen_rand_key(&mut data).unwrap(); + let custom = "Some custom string".as_bytes(); let cshake = default::cshake(&data, custom).unwrap(); @@ -527,7 +522,9 @@ mod test { #[test] fn cshake_verify_err_len() { - let data = util::gen_rand_key(64).unwrap(); + let mut data = [0u8; 64]; + util::gen_rand_key(&mut data).unwrap(); + let custom = "Some custom string".as_bytes(); let cshake = default::cshake(&data, custom).unwrap(); diff --git a/src/hazardous/constants.rs b/src/hazardous/constants.rs new file mode 100644 index 00000000..e4aa4d98 --- /dev/null +++ b/src/hazardous/constants.rs @@ -0,0 +1,30 @@ +// MIT License + +// Copyright (c) 2018 brycx + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/// The blocksize for the hash function SHA512. +pub const BLOCKSIZE: usize = 128; +/// The output size for the hash function SHA512. +pub const HLEN: usize = 64; +/// Type for an array of length `BLOCKSIZE`. +pub type BlocksizeArray = [u8; BLOCKSIZE]; +/// Type for an array of length `HLEN`. +pub type HLenArray = [u8; HLEN]; diff --git a/src/hazardous/cshake.rs b/src/hazardous/cshake.rs index 5374fbf6..373b352e 100644 --- a/src/hazardous/cshake.rs +++ b/src/hazardous/cshake.rs @@ -20,50 +20,93 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +//! ### Notes: +//! The cSHAKE256 implementation currently relies on the `tiny-keccak` crate. Currently this crate +//! will produce ***incorrect results on big-endian based systems***. See [issue here](https://github.com/debris/tiny-keccak/issues/15). +//! +//! # Security: +//! cSHAKE256 has a security strength of 256 bits. The recommended output length for cSHAKE256 is 64. +//! +//! # Example: +//! ``` +//! use orion::hazardous::cshake; +//! +//! let input = b"\x00\x01\x02\x03"; +//! let custom = b"Email signature"; +//! let mut out = [0u8; 64]; +//! +//! let mut hash = cshake::init(custom, None).unwrap(); +//! hash.update(input); +//! +//! hash.finalize(&mut out).unwrap(); +//! ``` use byte_tools::write_u64_be; -use clear_on_drop::clear::Clear; -use core::errors::*; -use core::options::KeccakVariantOption; -use core::util; +use core::mem; use tiny_keccak::Keccak; +use utilities::errors::*; -/// cSHAKE as specified in the [NIST SP 800-185](https://csrc.nist.gov/publications/detail/sp/800-185/final). +/// cSHAKE256 as specified in the [NIST SP 800-185](https://csrc.nist.gov/publications/detail/sp/800-185/final). +/// # Parameters: +/// - `input`: The main input string +/// - `dst_out`: Destination buffer for the digest. The length of the digest is implied by the length of `dst_out` /// -/// Fields `input` and `custom` are zeroed out on drop. +/// # Exceptions: +/// An exception will be thrown if: +/// - The length of `dst_out` is zero +/// - The length of `dst_out` is greater than 65536 pub struct CShake { - pub input: Vec, - pub name: Vec, - pub custom: Vec, - pub length: usize, - pub keccak: KeccakVariantOption, + hasher: Keccak, } -impl Drop for CShake { - fn drop(&mut self) { - Clear::clear(&mut self.input); - Clear::clear(&mut self.custom) +impl CShake { + /// Initial setup with encoding of `custom` and `name`. + fn setup(&mut self, custom: &[u8], name: &[u8]) { + // Only append the left encoded rate, not the rate itself as with `name` and `custom` + let (encoded, offset) = left_encode(136_u64); + self.hasher.update(&encoded[(offset - 1)..]); + + // The below two calls are equivalent to encode_string() from the spec + let (encoded, offset) = left_encode(name.len() as u64 * 8); + self.hasher.update(&encoded[(offset - 1)..]); + self.hasher.update(&name); + + let (encoded, offset) = left_encode(custom.len() as u64 * 8); + self.hasher.update(&encoded[(offset - 1)..]); + self.hasher.update(custom); + + // Pad with zeroes before calling pad() in finalize() + self.hasher.fill_block(); + } + /// Set `input`. Can be called repeatedly. + pub fn update(&mut self, input: &[u8]) { + self.hasher.update(input); + } + + /// Return a cSHAKE hash. + pub fn finalize(&mut self, dst_out: &mut [u8]) -> Result<(), UnknownCryptoError> { + if dst_out.is_empty() || dst_out.len() > 65536 { + return Err(UnknownCryptoError); + } + + let mut hasher_new = Keccak::new(136, 0x04); + mem::swap(&mut self.hasher, &mut hasher_new); + + hasher_new.finalize(dst_out); + + Ok(()) } } -/// cSHAKE as specified in the [NIST SP 800-185](https://csrc.nist.gov/publications/detail/sp/800-185/final). -/// +/// Initialize a `CShake struct`. /// # Parameters: -/// - `input`: The main input string -/// - `length`: Output length in bytes -/// - `name`: Function-name string +/// - `name`: Optional function-name string. If `None` it is set to a zero-length string. /// - `custom`: Customization string -/// - `keccak`: Keccak variant to be used -/// /// /// "The customization string is intended to avoid a collision between these two cSHAKE values—it /// will be very difficult for an attacker to somehow force one computation (the email signature) /// to yield the same result as the other computation (the key fingerprint) if different values /// of S are used." See [NIST SP 800-185](https://csrc.nist.gov/publications/detail/sp/800-185/final) for more information. /// -/// ### Notes: -/// The cSHAKE implementation currently relies on the `tiny-keccak` crate. Currently this crate -/// will produce ***incorrect results on big-endian based systems***. See [issue here](https://github.com/debris/tiny-keccak/issues/15). -/// /// `name` is a special parameter that in most cases should be just set to a zero string: /// "This is intended for use by NIST in defining SHA-3-derived functions, and should only be set /// to values defined by NIST". See [NIST SP 800-185](https://csrc.nist.gov/publications/detail/sp/800-185/final) for more information. @@ -71,112 +114,37 @@ impl Drop for CShake { /// # Exceptions: /// An exception will be thrown if: /// - Both `name` and `custom` are empty -/// - The specified length is zero -/// - The specified length is greater than 65536 /// - If the length of either `name` or `custom` is greater than 65536 /// /// The reason that `name` and `custom` cannot both be empty is because that would be equivalent to /// a SHAKE call. -/// -/// # Security: -/// cSHAKE128 has a security strength of 128 bits, whereas cSHAKE256 has a security strength of -/// 256 bits. The recommended output length for cSHAKE128 is 32 and 64 for cSHAKE256. -/// -/// # Example: -/// ``` -/// use orion::hazardous::cshake::CShake; -/// use orion::core::util::gen_rand_key; -/// use orion::core::options::KeccakVariantOption; -/// -/// let key = gen_rand_key(32).unwrap(); -/// -/// let cshake = CShake { -/// input: key, -/// name: "".as_bytes().to_vec(), -/// custom: "Email signature".as_bytes().to_vec(), -/// length: 32, -/// keccak: KeccakVariantOption::KECCAK256, -/// }; -/// -/// let result = cshake.finalize().unwrap(); -/// assert_eq!(cshake.verify(&result).unwrap(), true); -/// ``` - -impl CShake { - /// Return the rate in bytes of the respective Keccak sponge function. - fn rate(&self) -> u64 { - match &self.keccak { - KeccakVariantOption::KECCAK256 => 168_u64, - KeccakVariantOption::KECCAK512 => 136_u64, - } +pub fn init(custom: &[u8], name: Option<&[u8]>) -> Result { + // 136 is the rate of Keccak512 + let mut hash = CShake { + hasher: Keccak::new(136, 0x04), + }; + + // "When N and S are both empty strings, cSHAKE(X, L, N, S) is equivalent to SHAKE as + // defined in FIPS 202" + let name_val = match name { + Some(ref n_val) => *n_val, + None => &[0u8; 0], + }; + if (name_val.is_empty()) && (custom.is_empty()) { + return Err(UnknownCryptoError); } - - /// Initialize a Keccak hasher. - fn keccak_init(&self) -> Keccak { - match &self.keccak { - KeccakVariantOption::KECCAK256 => Keccak::new(self.rate() as usize, 0x04), - KeccakVariantOption::KECCAK512 => Keccak::new(self.rate() as usize, 0x04), - } + if name_val.len() > 65536 || custom.len() > 65536 { + return Err(UnknownCryptoError); } - /// Return a Keccak hash. - fn keccak_finalize(&self, mut state: Keccak) -> Vec { - let mut hash = vec![0u8; self.length]; - state.update(&self.input); - // finalize() will call pad(), then keccakf() and finally squeeze() - state.finalize(&mut hash); - hash - } + hash.setup(custom, name_val); - /// Return a cSHAKE hash. - pub fn finalize(&self) -> Result, UnknownCryptoError> { - // "When N and S are both empty strings, cSHAKE(X, L, N, S) is equivalent to SHAKE as - // defined in FIPS 202" - if (self.name.is_empty()) && (self.custom.is_empty()) { - return Err(UnknownCryptoError); - } - if self.length == 0 || self.length > 65536 { - return Err(UnknownCryptoError); - } - if self.name.len() > 65536 || self.custom.len() > 65536 { - return Err(UnknownCryptoError); - } - - let mut cshake_pad: Keccak = self.keccak_init(); - - // Only append the left encoded rate, not the rate itself as with `name` and `custom` - cshake_pad.update(&left_encode(self.rate())); - - // The below two calls are equivalent to encode_string() from the spec - cshake_pad.update(&left_encode(self.name.len() as u64 * 8)); - cshake_pad.update(&self.name); - - cshake_pad.update(&left_encode(self.custom.len() as u64 * 8)); - cshake_pad.update(&self.custom); - - // Pad with zeroes before calling pad() in finalize() - cshake_pad.fill_block(); - - Ok(self.keccak_finalize(cshake_pad)) - } - - /// Verify a cSHAKE hash by comparing one from the current struct fields to the input hash - /// passed to the function. Comparison is done in constant time. Both hashes must be - /// of equal length. - pub fn verify(&self, input: &[u8]) -> Result { - let own_hash = self.finalize().unwrap(); - - if util::compare_ct(&own_hash, input).is_err() { - Err(ValidationCryptoError) - } else { - Ok(true) - } - } + Ok(hash) } /// The left_encode function as specified in the NIST SP 800-185. -fn left_encode(x: u64) -> Vec { - let mut input = vec![0u8; 9]; +fn left_encode(x: u64) -> ([u8; 9], usize) { + let mut input = [0u8; 9]; let mut offset: usize = 0; if x == 0 { @@ -193,7 +161,7 @@ fn left_encode(x: u64) -> Vec { input[offset - 1] = (9 - offset) as u8; - input[(offset - 1)..].to_vec() + (input, offset) } #[cfg(test)] @@ -203,173 +171,151 @@ mod test { #[test] fn test_left_encode() { - let test_1 = left_encode(32); - let test_2 = left_encode(255); - let test_3 = left_encode(0); - let test_4 = left_encode(64); - let test_5 = left_encode(u64::max_value()); - - assert_eq!(&test_1, &[1, 32]); - assert_eq!(&test_2, &[1, 255]); - assert_eq!(&test_3, &[1, 0]); - assert_eq!(&test_4, &[1, 64]); - assert_eq!(&test_5, &[8, 255, 255, 255, 255, 255, 255, 255, 255]); + let (test_1, offset_1) = left_encode(32); + let (test_2, offset_2) = left_encode(255); + let (test_3, offset_3) = left_encode(0); + let (test_4, offset_4) = left_encode(64); + let (test_5, offset_5) = left_encode(u64::max_value()); + + assert_eq!(&test_1[(offset_1 - 1)..], &[1, 32]); + assert_eq!(&test_2[(offset_2 - 1)..], &[1, 255]); + assert_eq!(&test_3[(offset_3 - 1)..], &[1, 0]); + assert_eq!(&test_4[(offset_4 - 1)..], &[1, 64]); + assert_eq!( + &test_5[(offset_5 - 1)..], + &[8, 255, 255, 255, 255, 255, 255, 255, 255] + ); } #[test] - fn err_on_empty_n_c() { - let cshake = CShake { - input: b"\x00\x01\x02\x03".to_vec(), - length: 32, - name: b"".to_vec(), - custom: b"".to_vec(), - keccak: KeccakVariantOption::KECCAK256, - }; - - assert!(cshake.finalize().is_err()); + fn err_on_empty_name_custom() { + let custom = b""; + let name = b""; + + assert!(init(custom, Some(name)).is_err()); } #[test] fn empty_custom_ok() { - let cshake = CShake { - input: b"\x00\x01\x02\x03".to_vec(), - length: 32, - name: b"Email signature".to_vec(), - custom: b"".to_vec(), - keccak: KeccakVariantOption::KECCAK256, - }; - - assert!(cshake.finalize().is_ok()); + let custom = b""; + let name = b"Email signature"; + + assert!(init(custom, Some(name)).is_ok()); } #[test] fn empty_input_ok() { - let cshake = CShake { - input: b"".to_vec(), - length: 32, - name: b"Email signature".to_vec(), - custom: b"".to_vec(), - keccak: KeccakVariantOption::KECCAK256, - }; - - assert!(cshake.finalize().is_ok()); + let custom = b"Custom String"; + let name = b"Email signature"; + + assert!(init(custom, Some(name)).is_ok()); } #[test] fn err_on_zero_length() { - let cshake = CShake { - input: b"\x00\x01\x02\x03".to_vec(), - length: 0, - name: b"Email signature".to_vec(), - custom: b"".to_vec(), - keccak: KeccakVariantOption::KECCAK256, - }; - - assert!(cshake.finalize().is_err()); + let input = b"\x00\x01\x02\x03"; + let custom = b""; + let name = b"Email signature"; + let mut out = [0u8; 0]; + + let mut hash = init(custom, Some(name)).unwrap(); + hash.update(input); + + assert!(hash.finalize(&mut out).is_err()); } #[test] fn err_on_above_max_length() { - let cshake = CShake { - input: b"\x00\x01\x02\x03".to_vec(), - length: 65537, - name: b"Email signature".to_vec(), - custom: b"".to_vec(), - keccak: KeccakVariantOption::KECCAK256, - }; - - assert!(cshake.finalize().is_err()); + let input = b"\x00\x01\x02\x03"; + let custom = b""; + let name = b"Email signature"; + let mut out = [0u8; 65537]; + + let mut hash = init(custom, Some(name)).unwrap(); + hash.update(input); + + assert!(hash.finalize(&mut out).is_err()); } #[test] fn err_on_name_max_length() { - let cshake = CShake { - input: b"\x00\x01\x02\x03".to_vec(), - length: 32, - name: vec![0u8; 65537], - custom: b"Email signature".to_vec(), - keccak: KeccakVariantOption::KECCAK256, - }; - - assert!(cshake.finalize().is_err()); + let custom = b""; + let name = [0u8; 65537]; + + assert!(init(custom, Some(&name)).is_err()); } #[test] fn err_on_n_c_max_length() { - let cshake = CShake { - input: b"\x00\x01\x02\x03".to_vec(), - length: 32, - name: vec![0u8; 65537], - custom: vec![0u8; 65537], - keccak: KeccakVariantOption::KECCAK256, - }; - - assert!(cshake.finalize().is_err()); + let custom = [0u8; 65537]; + let name = [0u8; 65537]; + + assert!(init(&custom, Some(&name)).is_err()); } #[test] fn err_on_custom_max_length() { - let cshake = CShake { - input: b"\x00\x01\x02\x03".to_vec(), - length: 32, - name: b"Email signature".to_vec(), - custom: vec![0u8; 65537], - keccak: KeccakVariantOption::KECCAK256, - }; - - assert!(cshake.finalize().is_err()); + let custom = [0u8; 65537]; + let name = [0u8; 0]; + + assert!(init(&custom, Some(&name)).is_err()); + assert!(init(&custom, None).is_err()); } #[test] fn non_8_div_len() { - let cshake = CShake { - input: b"\x00\x01\x02\x03".to_vec(), - length: 17, - name: b"".to_vec(), - custom: b"Email Signature".to_vec(), - keccak: KeccakVariantOption::KECCAK256, - }; - - let expected = b"\xC1\xC3\x69\x25\xB6\x40\x9A\x04\xF1\xB5\x04\xFC\xBC\xA9\xD8\x2B\x40\x17\ - \x27\x7C\xB5\xED\x2B\x20\x65\xFC\x1D\x38\x14\xD5\xAA\xF5" - .to_vec(); - - assert_eq!(expected[..17].len(), cshake.finalize().unwrap().len()); - assert_eq!(cshake.finalize().unwrap(), &expected[..17]); + let input = b"\x00\x01\x02\x03"; + let custom = b"Email Signature"; + let mut out = [0u8; 17]; + + let mut cshake = init(custom, None).unwrap(); + cshake.update(input); + cshake.finalize(&mut out).unwrap(); + + let expected = b"\xD0\x08\x82\x8E\x2B\x80\xAC\x9D\x22\x18\xFF\xEE\x1D\x07\x0C\x48\xB8\ + \xE4\xC8\x7B\xFF\x32\xC9\x69\x9D\x5B\x68\x96\xEE\xE0\xED\xD1\x64\x02\ + \x0E\x2B\xE0\x56\x08\x58\xD9\xC0\x0C\x03\x7E\x34\xA9\x69\x37\xC5\x61\ + \xA7\x4C\x41\x2B\xB4\xC7\x46\x46\x95\x27\x28\x1C\x8C"; + + assert_eq!(expected[..17].len(), out.len()); + assert_eq!(out, &expected[..17]); } #[test] - fn verify_ok() { - let cshake = CShake { - input: b"\x00\x01\x02\x03".to_vec(), - name: b"".to_vec(), - custom: b"Email Signature".to_vec(), - length: 32, - keccak: KeccakVariantOption::KECCAK256, - }; - - let expected = b"\xC1\xC3\x69\x25\xB6\x40\x9A\x04\xF1\xB5\x04\xFC\xBC\xA9\xD8\x2B\x40\x17\ - \x27\x7C\xB5\xED\x2B\x20\x65\xFC\x1D\x38\x14\xD5\xAA\xF5" - .to_vec(); - - assert_eq!(cshake.verify(&expected).unwrap(), true); + fn result_ok() { + let input = b"\x00\x01\x02\x03"; + let custom = b"Email Signature"; + let mut out = [0u8; 64]; + + let mut cshake = init(custom, None).unwrap(); + cshake.update(input); + cshake.finalize(&mut out).unwrap(); + + let expected = b"\xD0\x08\x82\x8E\x2B\x80\xAC\x9D\x22\x18\xFF\xEE\x1D\x07\x0C\x48\xB8\ + \xE4\xC8\x7B\xFF\x32\xC9\x69\x9D\x5B\x68\x96\xEE\xE0\xED\xD1\x64\x02\ + \x0E\x2B\xE0\x56\x08\x58\xD9\xC0\x0C\x03\x7E\x34\xA9\x69\x37\xC5\x61\ + \xA7\x4C\x41\x2B\xB4\xC7\x46\x46\x95\x27\x28\x1C\x8C"; + + assert_eq!(out.as_ref(), expected.as_ref()); } #[test] fn verify_err() { // `name` and `custom` values have been switched here compared to the previous one - let cshake = CShake { - input: b"\x00\x01\x02\x03".to_vec(), - length: 32, - name: b"Email signature".to_vec(), - custom: b"".to_vec(), - keccak: KeccakVariantOption::KECCAK256, - }; - - let expected = b"\xC1\xC3\x69\x25\xB6\x40\x9A\x04\xF1\xB5\x04\xFC\xBC\xA9\xD8\x2B\x40\x17\ - \x27\x7C\xB5\xED\x2B\x20\x65\xFC\x1D\x38\x14\xD5\xAA\xF5" - .to_vec(); - - assert!(cshake.verify(&expected).is_err()); + let input = b"\x00\x01\x02\x03"; + let custom = b""; + let name = b"Email Signature"; + let mut out = [0u8; 64]; + + let mut cshake = init(custom, Some(name)).unwrap(); + cshake.update(input); + cshake.finalize(&mut out).unwrap(); + + let expected = b"\xD0\x08\x82\x8E\x2B\x80\xAC\x9D\x22\x18\xFF\xEE\x1D\x07\x0C\x48\xB8\ + \xE4\xC8\x7B\xFF\x32\xC9\x69\x9D\x5B\x68\x96\xEE\xE0\xED\xD1\x64\x02\ + \x0E\x2B\xE0\x56\x08\x58\xD9\xC0\x0C\x03\x7E\x34\xA9\x69\x37\xC5\x61\ + \xA7\x4C\x41\x2B\xB4\xC7\x46\x46\x95\x27\x28\x1C\x8C"; + + assert_ne!(out.as_ref(), expected.as_ref()); } } diff --git a/src/hazardous/hkdf.rs b/src/hazardous/hkdf.rs index 0bc2b2a5..f67a38eb 100644 --- a/src/hazardous/hkdf.rs +++ b/src/hazardous/hkdf.rs @@ -20,173 +20,120 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use clear_on_drop::clear::Clear; -use core::options::ShaVariantOption; -use core::{errors::*, util}; -use hazardous::hmac::Hmac; - -/// HKDF (HMAC-based Extract-and-Expand Key Derivation Function) as specified in the -/// [RFC 5869](https://tools.ietf.org/html/rfc5869). -/// -/// Fields `salt`, `ikm` and `info` are zeroed out on drop. -pub struct Hkdf { - pub salt: Vec, - pub ikm: Vec, - pub info: Vec, - pub length: usize, - pub hmac: ShaVariantOption, +//! # Parameters: +//! - `salt`: Optional salt value +//! - `ikm`: Input keying material +//! - `info`: Optional context and application specific information (can be a zero-length string) +//! - `okm_out`: Destination buffer for the derived key. The length of the derived key is implied by the length of `okm_out` +//! +//! See [RFC](https://tools.ietf.org/html/rfc5869#section-2.2) for more information. +//! +//! # Exceptions: +//! An exception will be thrown if: +//! - The length of `okm_out` is less than 1 +//! - The length of `okm_out` is greater than 255 * hash_output_size_in_bytes +//! +//! # Security: +//! Salts should always be generated using a CSPRNG. The `gen_rand_key` function +//! in `util` can be used for this. The recommended length for a salt is 16 bytes as a minimum. +//! HKDF is not suitable for password storage. Even though a salt value is optional, it is strongly +//! recommended to use one. +//! +//! # Example: +//! ### Generating derived key: +//! ``` +//! use orion::utilities::util; +//! use orion::hazardous::hkdf; +//! +//! let mut salt = [0u8; 16]; +//! util::gen_rand_key(&mut salt).unwrap(); +//! let mut okm_out = [0u8; 32]; +//! +//! hkdf::derive_key(&salt, "IKM".as_bytes(), "Info".as_bytes(), &mut okm_out).unwrap(); +//! ``` +//! ### Verifying derived key: +//! ``` +//! use orion::utilities::util; +//! use orion::hazardous::hkdf; +//! +//! let mut salt = [0u8; 16]; +//! util::gen_rand_key(&mut salt).unwrap(); +//! let mut okm_out = [0u8; 32]; +//! +//! hkdf::derive_key(&salt, "IKM".as_bytes(), "Info".as_bytes(), &mut okm_out).unwrap(); +//! let exp_okm = okm_out; +//! assert!(hkdf::verify(&exp_okm, &salt, "IKM".as_bytes(), "Info".as_bytes(), &mut okm_out).unwrap()); +//! ``` + +use hazardous::constants::HLEN; +use hazardous::hmac; +use utilities::{errors::*, util}; + +#[inline(always)] +/// The HKDF extract step. +pub fn extract(salt: &[u8], ikm: &[u8]) -> [u8; 64] { + let mut prk = hmac::init(salt); + prk.update(ikm); + + prk.finalize() } -impl Drop for Hkdf { - fn drop(&mut self) { - Clear::clear(&mut self.salt); - Clear::clear(&mut self.ikm); - Clear::clear(&mut self.info) +#[inline(always)] +/// The HKDF expand step. +pub fn expand(prk: &[u8], info: &[u8], okm_out: &mut [u8]) -> Result<(), UnknownCryptoError> { + if okm_out.len() > 16320 { + return Err(UnknownCryptoError); } -} - -/// HKDF (HMAC-based Extract-and-Expand Key Derivation Function) as specified in the -/// [RFC 5869](https://tools.ietf.org/html/rfc5869). -/// # Parameters: -/// - `salt`: Optional salt value -/// - `ikm`: Input keying material -/// - `info`: Optional context and application specific information (can be a zero-length string) -/// - `length`: Length of output keying material -/// - `hmac`: HMAC function -/// -/// See [RFC](https://tools.ietf.org/html/rfc5869#section-2.2) for more information. -/// -/// # Exceptions: -/// An exception will be thrown if: -/// - The specified length is less than 1 -/// - The specified length is greater than 255 * hash_output_size_in_bytes -/// -/// # Security: -/// Salts should always be generated using a CSPRNG. The `gen_rand_key` function -/// in `util` can be used for this. The recommended length for a salt is 16 bytes as a minimum. -/// HKDF is not suitable for password storage. Even though a salt value is optional, it is strongly -/// recommended to use one. -/// -/// # Example: -/// ### Generating derived key: -/// ``` -/// use orion::hazardous::hkdf::Hkdf; -/// use orion::core::util::gen_rand_key; -/// use orion::core::options::ShaVariantOption; -/// -/// let key = gen_rand_key(32).unwrap(); -/// let salt = gen_rand_key(32).unwrap(); -/// let info = gen_rand_key(32).unwrap(); -/// -/// let dk = Hkdf { -/// salt: salt, -/// ikm: key, -/// info: info, -/// length: 50, -/// hmac: ShaVariantOption::SHA256, -/// }; -/// -/// let dk_final = dk.derive_key().unwrap(); -/// ``` -/// ### Verifying derived key: -/// ``` -/// use orion::hazardous::hkdf::Hkdf; -/// use orion::core::util::gen_rand_key; -/// use orion::core::options::ShaVariantOption; -/// -/// let key = gen_rand_key(32).unwrap(); -/// let salt = gen_rand_key(32).unwrap(); -/// let info = gen_rand_key(32).unwrap(); -/// -/// let dk = Hkdf { -/// salt: salt, -/// ikm: key, -/// info: info, -/// length: 50, -/// hmac: ShaVariantOption::SHA256, -/// }; -/// -/// let dk_final = dk.derive_key().unwrap(); -/// -/// assert_eq!(dk.verify(&dk_final).unwrap(), true); -/// ``` - -impl Hkdf { - /// Return the maximum okm length (255 * hLen). - fn max_okmlen(&self) -> usize { - match self.hmac.output_size() { - 32 => 8160, - 48 => 12240, - 64 => 16320, - _ => panic!(UnknownCryptoError), - } - } - - /// The HKDF Extract step. - pub fn extract(&self, salt: &[u8], ikm: &[u8]) -> Vec { - let prk = Hmac { - secret_key: salt.to_vec(), - data: ikm.to_vec(), - sha2: self.hmac, - }; - - prk.finalize() + if okm_out.is_empty() { + return Err(UnknownCryptoError); } - /// The HKDF Expand step. - pub fn expand(&self, prk: &[u8]) -> Result, UnknownCryptoError> { - if self.length > self.max_okmlen() { - return Err(UnknownCryptoError); - } - if self.length < 1 { - return Err(UnknownCryptoError); - } + let mut hmac = hmac::init(prk); - let n_iter: usize = 1 + ((self.length - 1) / self.hmac.output_size()); + for (idx, hlen_block) in okm_out.chunks_mut(HLEN).enumerate() { + hmac.update(info); + hmac.update(&[idx as u8 + 1_u8]); - // con_step will hold the intermediate state of "T_n | info | 0x0n" as described in the RFC - let mut con_step: Vec = Vec::new(); - let mut okm: Vec = Vec::new(); + let block_len = hlen_block.len(); + hmac.finalize_with_dst(&mut hlen_block[..block_len]); - for index in 1..n_iter + 1 { - con_step.extend_from_slice(&self.info); - con_step.push(index as u8); - // Given that n_iter is rounded correctly, then the `index as u8` - // should not be able to overflow. If the maximum okmlen is selected, - // along with the highest output size, then n_iter will equal exactly `u8::max_value()` - - // Calling extract as it yields the same result as an HMAC call - con_step = self.extract(prk, &con_step); - okm.extend_from_slice(&con_step); + // Check if it's the last iteration, if yes don't process anything + // only works if `okm_out.len()` is not a multiple of HLEN + if block_len < HLEN { + break; + } else { + hmac.reset(); + hmac.update(&hlen_block); } - - okm.truncate(self.length); - - Ok(okm) } - /// Combine Extract and Expand to return a derived key. - pub fn derive_key(&self) -> Result, UnknownCryptoError> { - let mut prk = self.extract(&self.salt, &self.ikm); - - let dk = self.expand(&prk); - - Clear::clear(&mut prk); - - dk - } + Ok(()) +} - /// Verify a derived key by comparing one from the current struct fields to the derived key - /// passed to the function. Comparison is done in constant time. Both derived keys must be - /// of equal length. - pub fn verify(&self, expected_dk: &[u8]) -> Result { - let own_dk = self.derive_key().unwrap(); +/// Combine `extract` and `expand` to return a derived key. +pub fn derive_key( + salt: &[u8], + ikm: &[u8], + info: &[u8], + okm_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + expand(&extract(salt, ikm), info, okm_out) +} - if util::compare_ct(&own_dk, expected_dk).is_err() { - Err(ValidationCryptoError) - } else { - Ok(true) - } +/// Verify a derived key in constant time. +pub fn verify( + expected_dk: &[u8], + salt: &[u8], + ikm: &[u8], + info: &[u8], + okm_out: &mut [u8], +) -> Result { + expand(&extract(salt, ikm), info, okm_out).unwrap(); + + if util::compare_ct(&okm_out, expected_dk).is_err() { + Err(ValidationCryptoError) + } else { + Ok(true) } } @@ -194,147 +141,84 @@ impl Hkdf { mod test { extern crate hex; use self::hex::decode; - use core::options::ShaVariantOption; - use hazardous::hkdf::Hkdf; - - #[test] - fn hkdf_maximum_length_256() { - let hkdf = Hkdf { - salt: decode("").unwrap(), - ikm: decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), - info: decode("").unwrap(), - // Max allowed length here is 8160 - length: 9000, - hmac: ShaVariantOption::SHA256, - }; - - let hkdf_extract = hkdf.extract(&hkdf.ikm, &hkdf.salt); - - assert!(hkdf.expand(&hkdf_extract).is_err()); - assert!(hkdf.derive_key().is_err()); - } - - #[test] - fn hkdf_maximum_length_384() { - let hkdf = Hkdf { - salt: decode("").unwrap(), - ikm: decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), - info: decode("").unwrap(), - // Max allowed length here is 12240 - length: 13000, - hmac: ShaVariantOption::SHA384, - }; - - let hkdf_extract = hkdf.extract(&hkdf.ikm, &hkdf.salt); - - assert!(hkdf.expand(&hkdf_extract).is_err()); - assert!(hkdf.derive_key().is_err()); - } + use hazardous::hkdf::*; #[test] fn hkdf_maximum_length_512() { - let hkdf = Hkdf { - salt: decode("").unwrap(), - ikm: decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), - info: decode("").unwrap(), - // Max allowed length here is 16320 - length: 17000, - hmac: ShaVariantOption::SHA512, - }; - - let hkdf_extract = hkdf.extract(&hkdf.ikm, &hkdf.salt); - - assert!(hkdf.expand(&hkdf_extract).is_err()); - assert!(hkdf.derive_key().is_err()); + // Max allowed length here is 16320 + let mut okm_out = [0u8; 17000]; + let prk = extract("".as_bytes(), "".as_bytes()); + + assert!(expand(&prk, "".as_bytes(), &mut okm_out).is_err()); } #[test] fn hkdf_zero_length() { - let hkdf = Hkdf { - salt: decode("").unwrap(), - ikm: decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), - info: decode("").unwrap(), - length: 0, - hmac: ShaVariantOption::SHA512, - }; - - let hkdf_extract = hkdf.extract(&hkdf.ikm, &hkdf.salt); - - assert!(hkdf.expand(&hkdf_extract).is_err()); - assert!(hkdf.derive_key().is_err()); + let mut okm_out = [0u8; 0]; + let prk = extract("".as_bytes(), "".as_bytes()); + + assert!(expand(&prk, "".as_bytes(), &mut okm_out).is_err()); } #[test] fn hkdf_verify_true() { - let hkdf = Hkdf { - salt: decode("").unwrap(), - ikm: decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), - info: decode("").unwrap(), - length: 42, - hmac: ShaVariantOption::SHA256, - }; + let ikm = decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); + let salt = decode("000102030405060708090a0b0c").unwrap(); + let info = decode("f0f1f2f3f4f5f6f7f8f9").unwrap(); + let mut okm_out = [0u8; 42]; let expected_okm = decode( - "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d\ - 9d201395faa4b61a96c8", + "832390086cda71fb47625bb5ceb168e4c8e26a1a16ed34d9fc7fe92c1481579338da362cb8d9f925d7cb", ).unwrap(); - assert_eq!(hkdf.verify(&expected_okm).unwrap(), true); + assert_eq!( + verify(&expected_okm, &salt, &ikm, &info, &mut okm_out).unwrap(), + true + ); } #[test] fn hkdf_verify_wrong_salt() { - // Salt value differs between this and the previous test case - let hkdf = Hkdf { - salt: "salt".as_bytes().to_vec(), - ikm: decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), - info: decode("").unwrap(), - length: 42, - hmac: ShaVariantOption::SHA256, - }; + let salt = "salt".as_bytes(); + let ikm = decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); + let info = "".as_bytes(); + let mut okm_out = [0u8; 42]; let expected_okm = decode( "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d\ 9d201395faa4b61a96c8", ).unwrap(); - assert!(hkdf.verify(&expected_okm).is_err()); + assert!(verify(&expected_okm, salt, &ikm, info, &mut okm_out).is_err()); } #[test] fn hkdf_verify_wrong_ikm() { - let hkdf = Hkdf { - salt: decode("").unwrap(), - ikm: decode("0b").unwrap(), - info: decode("").unwrap(), - length: 42, - hmac: ShaVariantOption::SHA256, - }; + let salt = "".as_bytes(); + let ikm = decode("0b").unwrap(); + let info = "".as_bytes(); + let mut okm_out = [0u8; 42]; let expected_okm = decode( "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d\ 9d201395faa4b61a96c8", ).unwrap(); - assert!(hkdf.verify(&expected_okm).is_err()); + assert!(verify(&expected_okm, salt, &ikm, info, &mut okm_out).is_err()); } #[test] - fn verify_diff_length_panic() { - // Different length than expected okm - let hkdf = Hkdf { - salt: "salt".as_bytes().to_vec(), - ikm: decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), - info: decode("").unwrap(), - length: 75, - hmac: ShaVariantOption::SHA256, - }; + fn verify_diff_length() { + let salt = "".as_bytes(); + let ikm = decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); + let info = "".as_bytes(); + let mut okm_out = [0u8; 72]; let expected_okm = decode( "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d\ 9d201395faa4b61a96c8", ).unwrap(); - assert!(hkdf.verify(&expected_okm).is_err()); + assert!(verify(&expected_okm, salt, &ikm, info, &mut okm_out).is_err()); } } diff --git a/src/hazardous/hmac.rs b/src/hazardous/hmac.rs index 2893cbcf..5ef36be8 100644 --- a/src/hazardous/hmac.rs +++ b/src/hazardous/hmac.rs @@ -20,209 +20,201 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use clear_on_drop::clear::Clear; -use core::options::ShaVariantOption; -use core::{errors::*, util}; - -/// HMAC (Hash-based Message Authentication Code) as specified in the +//! # Parameters: +//! - `secret_key`: The authentication key +//! - `data`: Data to be authenticated +//! +//! See [RFC](https://tools.ietf.org/html/rfc2104#section-2) for more information. +//! +//! # Security: +//! The secret key should always be generated using a CSPRNG. The `gen_rand_key` function +//! in `util` can be used for this. The recommended length for a secret key is the SHA functions digest +//! size in bytes. +//! # Example: +//! ### Generating HMAC: +//! ``` +//! use orion::hazardous::hmac; +//! use orion::utilities::util; +//! +//! let mut key = [0u8; 64]; +//! util::gen_rand_key(&mut key); +//! let msg = "Some message."; +//! +//! let mut mac = hmac::init(&key); +//! mac.update(msg.as_bytes()); +//! mac.finalize(); +//! ``` +//! ### Verifying HMAC: +//! ``` +//! use orion::hazardous::hmac; +//! use orion::utilities::util; +//! +//! let mut key = [0u8; 64]; +//! util::gen_rand_key(&mut key); +//! let msg = "Some message."; +//! +//! let mut mac = hmac::init(&key); +//! mac.update(msg.as_bytes()); +//! +//! assert!(hmac::verify(&mac.finalize(), &key, msg.as_bytes()).unwrap()); +//! ``` + +use core::mem; +use hazardous::constants::{BlocksizeArray, HLenArray, BLOCKSIZE, HLEN}; +use seckey::zero; +use sha2::{Digest, Sha512}; +use utilities::{errors::*, util}; + +/// HMAC-SHA512 (Hash-based Message Authentication Code) as specified in the /// [RFC 2104](https://tools.ietf.org/html/rfc2104). -/// -/// Fields `secret_key` and `data` are zeroed out on drop. pub struct Hmac { - pub secret_key: Vec, - pub data: Vec, - pub sha2: ShaVariantOption, + ipad: BlocksizeArray, + opad: BlocksizeArray, + hasher: Sha512, } impl Drop for Hmac { fn drop(&mut self) { - Clear::clear(&mut self.secret_key); - Clear::clear(&mut self.data) + zero(&mut self.ipad); + zero(&mut self.opad) } } -/// HMAC (Hash-based Message Authentication Code) as specified in the -/// [RFC 2104](https://tools.ietf.org/html/rfc2104). -/// -/// # Parameters: -/// - `secret_key`: The authentication key -/// - `data`: Data to be authenticated -/// - `sha2`: Cryptographic hash function -/// -/// See [RFC](https://tools.ietf.org/html/rfc2104#section-2) for more information. -/// -/// # Security: -/// The secret key should always be generated using a CSPRNG. The `gen_rand_key` function -/// in `util` can be used for this. The recommended length for a secret key is the SHA functions digest -/// size in bytes. -/// # Example: -/// ### Generating HMAC: -/// ``` -/// use orion::hazardous::hmac::Hmac; -/// use orion::core::util::gen_rand_key; -/// use orion::core::options::ShaVariantOption; -/// -/// let key = gen_rand_key(32).unwrap(); -/// let message = gen_rand_key(32).unwrap(); -/// -/// let hmac = Hmac { -/// secret_key: key, -/// data: message, -/// sha2: ShaVariantOption::SHA256 -/// }; -/// -/// hmac.finalize(); -/// ``` -/// ### Verifying HMAC: -/// ``` -/// use orion::hazardous::hmac::Hmac; -/// use orion::core::options::ShaVariantOption; -/// -/// let key = "Some key."; -/// let msg = "Some message."; -/// -/// let hmac = Hmac { -/// secret_key: key.as_bytes().to_vec(), -/// data: msg.as_bytes().to_vec(), -/// sha2: ShaVariantOption::SHA256 -/// }; -/// let received_hmac = Hmac { -/// secret_key: key.as_bytes().to_vec(), -/// data: msg.as_bytes().to_vec(), -/// sha2: ShaVariantOption::SHA256 -/// }; -/// assert_eq!(hmac.verify(&received_hmac.finalize()).unwrap(), true); -/// ``` - impl Hmac { - /// Pad the key and return inner and outer padding. - pub fn pad_key(&self, secret_key: &[u8]) -> (Vec, Vec) { - - let mut inner_pad = vec![0x36; self.sha2.blocksize()]; - let mut outer_pad = vec![0x5C; self.sha2.blocksize()]; - - if secret_key.len() > self.sha2.blocksize() { - let key = self.sha2.hash(secret_key); - - for index in 0..self.sha2.output_size() { - inner_pad[index] ^= key[index]; - outer_pad[index] ^= key[index]; + #[inline(always)] + /// Pad `key` with `ipad` and `opad`. + fn pad_key_io(&mut self, key: &[u8]) { + if key.len() > BLOCKSIZE { + self.ipad[..HLEN].copy_from_slice(&Sha512::digest(&key)); + + for (idx, itm) in self.ipad.iter_mut().take(64).enumerate() { + *itm ^= 0x36; + self.opad[idx] = *itm ^ 0x6A; // XOR with result of (0x5C ^ 0x36) to inverse } } else { - for index in 0..secret_key.len() { - inner_pad[index] ^= secret_key[index]; - outer_pad[index] ^= secret_key[index]; + for (idx, itm) in key.iter().enumerate() { + self.ipad[idx] ^= itm; + self.opad[idx] ^= itm; } } - (inner_pad, outer_pad) + self.hasher.input(&self.ipad); } - /// Returns an HMAC for a given key and data. - pub fn finalize(&self) -> Vec { - let (mut ipad, mut opad) = self.pad_key(&self.secret_key); + /// Reset to `init()` state. + pub fn reset(&mut self) { + self.hasher.input(&self.ipad); + } - ipad.extend_from_slice(&self.data); - opad.extend_from_slice(&self.sha2.hash(&ipad)); + /// This can be called multiple times. + pub fn update(&mut self, message: &[u8]) { + self.hasher.input(message); + } - let mac = self.sha2.hash(&opad); + #[inline(always)] + /// Return MAC. + pub fn finalize(&mut self) -> [u8; 64] { + let mut hash_ires = Sha512::default(); + mem::swap(&mut self.hasher, &mut hash_ires); - Clear::clear(&mut ipad); - Clear::clear(&mut opad); + let mut hash_ores = Sha512::default(); + hash_ores.input(&self.opad); + hash_ores.input(&hash_ires.result()); + let mut mac: HLenArray = [0u8; HLEN]; + mac.copy_from_slice(&hash_ores.result()); mac } - /// Check HMAC validity by computing one from the current struct fields and comparing this - /// to the passed HMAC. Comparison is done in constant time and with Double-HMAC Verification. - pub fn verify(&self, expected_hmac: &[u8]) -> Result { - let own_hmac = self.finalize(); + #[inline(always)] + /// Retrieve MAC and copy to `dst`. + pub fn finalize_with_dst(&mut self, dst: &mut [u8]) { + let mut hash_ires = Sha512::default(); + mem::swap(&mut self.hasher, &mut hash_ires); - let rand_key = util::gen_rand_key(self.sha2.blocksize()).unwrap(); + let mut hash_ores = Sha512::default(); + let dst_len = dst.len(); - let nd_round_own = Hmac { - secret_key: rand_key.clone(), - data: own_hmac, - sha2: self.sha2, - }; + hash_ores.input(&self.opad); + hash_ores.input(&hash_ires.result()); - let nd_round_received = Hmac { - secret_key: rand_key, - data: expected_hmac.to_vec(), - sha2: self.sha2, - }; + dst.copy_from_slice(&hash_ores.result()[..dst_len]); + } +} - if util::compare_ct(&nd_round_own.finalize(), &nd_round_received.finalize()).is_err() { - Err(ValidationCryptoError) - } else { - Ok(true) - } +/// Verify a HMAC-SHA512 MAC in constant time, with Double-HMAC Verification. +pub fn verify( + expected: &[u8], + secret_key: &[u8], + message: &[u8], +) -> Result { + let mut mac = init(secret_key); + mac.update(message); + + let mut rand_key: HLenArray = [0u8; HLEN]; + util::gen_rand_key(&mut rand_key).unwrap(); + + let mut nd_round_mac = init(secret_key); + let mut nd_round_expected = init(secret_key); + + nd_round_mac.update(&mac.finalize()); + nd_round_expected.update(expected); + + if util::compare_ct(&nd_round_mac.finalize(), &nd_round_expected.finalize()).is_err() { + Err(ValidationCryptoError) + } else { + Ok(true) } } -/// HMAC used for PBKDF2. -pub fn pbkdf2_hmac( - ipad: &[u8], - opad: &[u8], - data: &[u8], - hmac: ShaVariantOption, -) -> Vec { - - let mut mac = Vec::new(); - mac.extend_from_slice(opad); - mac.extend_from_slice(ipad); - mac.extend_from_slice(data); - let tmp = hmac.hash(&mac.split_off(opad.len())); - mac.extend_from_slice(&tmp); - mac = hmac.hash(&mac); +#[inline(always)] +/// Initialize `Hmac` struct with a given key. +pub fn init(secret_key: &[u8]) -> Hmac { + let mut mac = Hmac { + ipad: [0x36; BLOCKSIZE], + opad: [0x5C; BLOCKSIZE], + hasher: Sha512::default(), + }; + mac.pad_key_io(secret_key); mac } #[test] fn finalize_and_veriy_true() { - let own_hmac = Hmac { - secret_key: "Jefe".as_bytes().to_vec(), - data: "what do ya want for nothing?".as_bytes().to_vec(), - sha2: ShaVariantOption::SHA256, - }; - let recieved_hmac = Hmac { - secret_key: "Jefe".as_bytes().to_vec(), - data: "what do ya want for nothing?".as_bytes().to_vec(), - sha2: ShaVariantOption::SHA256, - }; + let secret_key = "Jefe".as_bytes(); + let data = "what do ya want for nothing?".as_bytes(); - assert_eq!(own_hmac.verify(&recieved_hmac.finalize()).unwrap(), true); + let mut mac = init(secret_key); + mac.update(data); + + assert_eq!(verify(&mac.finalize(), secret_key, data).unwrap(), true); } #[test] -fn veriy_false_wrong_secret_key() { - let own_hmac = Hmac { - secret_key: "Jefe".as_bytes().to_vec(), - data: "what do ya want for nothing?".as_bytes().to_vec(), - sha2: ShaVariantOption::SHA256, - }; - let false_hmac = Hmac { - secret_key: "Jefe".as_bytes().to_vec(), - data: "what do ya want for something?".as_bytes().to_vec(), - sha2: ShaVariantOption::SHA256, - }; - - assert!(own_hmac.verify(&false_hmac.finalize()).is_err()); +fn veriy_false_wrong_data() { + let secret_key = "Jefe".as_bytes(); + let data = "what do ya want for nothing?".as_bytes(); + + let mut mac = init(secret_key); + mac.update(data); + + assert!( + verify( + &mac.finalize(), + secret_key, + "what do ya want for something?".as_bytes() + ).is_err() + ); } #[test] -fn veriy_false_wrong_data() { - let own_hmac = Hmac { - secret_key: "Jefe".as_bytes().to_vec(), - data: "what do ya want for nothing?".as_bytes().to_vec(), - sha2: ShaVariantOption::SHA256, - }; - let false_hmac = Hmac { - secret_key: "Jose".as_bytes().to_vec(), - data: "what do ya want for nothing?".as_bytes().to_vec(), - sha2: ShaVariantOption::SHA256, - }; +fn veriy_false_wrong_secret_key() { + let secret_key = "Jefe".as_bytes(); + let data = "what do ya want for nothing?".as_bytes(); + + let mut mac = init(secret_key); + mac.update(data); - assert!(own_hmac.verify(&false_hmac.finalize()).is_err()); + assert!(verify(&mac.finalize(), "Jose".as_bytes(), data).is_err()); } diff --git a/src/hazardous/mod.rs b/src/hazardous/mod.rs index 937fcea1..40a4cba2 100644 --- a/src/hazardous/mod.rs +++ b/src/hazardous/mod.rs @@ -20,14 +20,17 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -/// HMAC (Hash-based Message Authentication Code) as specified in the [RFC 2104](https://tools.ietf.org/html/rfc2104). +/// HMAC-SHA512 (Hash-based Message Authentication Code) as specified in the [RFC 2104](https://tools.ietf.org/html/rfc2104). pub mod hmac; -/// HKDF (HMAC-based Extract-and-Expand Key Derivation Function) as specified in the [RFC 5869](https://tools.ietf.org/html/rfc5869). +/// HKDF-HMAC-SHA512 (HMAC-based Extract-and-Expand Key Derivation Function) as specified in the [RFC 5869](https://tools.ietf.org/html/rfc5869). pub mod hkdf; -/// PBKDF2 (Password-Based Key Derivation Function 2) as specified in the [RFC 8018](https://tools.ietf.org/html/rfc8018). +/// PBKDF2-HMAC-SHA512 (Password-Based Key Derivation Function 2) as specified in the [RFC 8018](https://tools.ietf.org/html/rfc8018). pub mod pbkdf2; -/// cSHAKE as specified in the [NIST SP 800-185](https://csrc.nist.gov/publications/detail/sp/800-185/final). +/// cSHAKE256 as specified in the [NIST SP 800-185](https://csrc.nist.gov/publications/detail/sp/800-185/final). pub mod cshake; + +/// Constant values and types. +pub mod constants; diff --git a/src/hazardous/pbkdf2.rs b/src/hazardous/pbkdf2.rs index a2bedc4e..24c4eeeb 100644 --- a/src/hazardous/pbkdf2.rs +++ b/src/hazardous/pbkdf2.rs @@ -20,190 +20,131 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use byte_tools::write_u32_be; -use clear_on_drop::clear::Clear; -use core::options::ShaVariantOption; -use core::{errors::*, util}; -use hazardous::hmac::*; +//! # Parameters: +//! - `password`: Password +//! - `salt`: Salt value +//! - `iterations`: Iteration count +//! - `dk_out`: Destination buffer for the derived key. The length of the derived key is implied by the length of `dk_out` +//! +//! See [RFC](https://tools.ietf.org/html/rfc8018#section-5.2) for more information. +//! +//! # Exceptions: +//! An exception will be thrown if: +//! - The length of `dk_out` is less than 1 +//! - The length of `dk_out` is greater than (2^32 - 1) * hLen +//! - The specified iteration count is less than 1 +//! +//! # Security: +//! Salts should always be generated using a CSPRNG. The `gen_rand_key` function +//! in `util` can be used for this. The recommended length for a salt is 16 bytes as a minimum. +//! The iteration count should be set as high as feasible. +//! # Example: +//! ### Generating derived key: +//! ``` +//! use orion::hazardous::pbkdf2; +//! use orion::utilities::util; +//! +//! let mut salt = [0u8; 16]; +//! util::gen_rand_key(&mut salt); +//! let mut dk_out = [0u8; 64]; +//! +//! pbkdf2::derive_key("Secret password".as_bytes(), &salt, 10000, &mut dk_out).unwrap(); +//! ``` +//! ### Verifying derived key: +//! ``` +//! use orion::hazardous::pbkdf2; +//! use orion::utilities::util; +//! +//! let mut salt = [0u8; 16]; +//! util::gen_rand_key(&mut salt); +//! let mut dk_out = [0u8; 64]; +//! +//! pbkdf2::derive_key("Secret password".as_bytes(), &salt, 10000, &mut dk_out).unwrap(); +//! let exp_dk = dk_out; +//! assert!(pbkdf2::verify(&exp_dk, "Secret password".as_bytes(), &salt, 10000, &mut dk_out).unwrap()); +//! ``` -/// PBKDF2 (Password-Based Key Derivation Function 2) as specified in the -/// [RFC 8018](https://tools.ietf.org/html/rfc8018). -/// -/// Fields `password` and `salt` are zeroed out on drop. -pub struct Pbkdf2 { - pub password: Vec, - pub salt: Vec, - pub iterations: usize, - pub dklen: usize, - pub hmac: ShaVariantOption, -} - -impl Drop for Pbkdf2 { - fn drop(&mut self) { - Clear::clear(&mut self.password); - Clear::clear(&mut self.salt) +use byte_tools::write_u32_be; +use hazardous::constants::{HLenArray, HLEN}; +use hazardous::hmac; +use utilities::{errors::*, util}; + +#[inline(always)] +fn function_f( + salt: &[u8], + iterations: usize, + index: usize, + dk_block: &mut [u8], + hmac: &mut hmac::Hmac, +) { + let block_len = dk_block.len(); + + let mut u_step: HLenArray = [0u8; 64]; + // First 4 bytes used for index BE conversion + write_u32_be(&mut u_step[..4], index as u32); + hmac.update(salt); + hmac.update(&u_step[..4]); + + hmac.finalize_with_dst(&mut u_step); + dk_block.copy_from_slice(&u_step[..block_len]); + + if iterations > 1 { + for _ in 1..iterations { + hmac.reset(); + hmac.update(&u_step); + hmac.finalize_with_dst(&mut u_step); + + for (idx, val) in u_step[..block_len].iter().enumerate() { + dk_block[idx] ^= val; + } + } } } -/// PBKDF2 (Password-Based Key Derivation Function 2) as specified in the +#[inline(always)] +/// PBKDF2-SHA512 (Password-Based Key Derivation Function 2) as specified in the /// [RFC 8018](https://tools.ietf.org/html/rfc8018). -/// -/// # Parameters: -/// - `password`: Password -/// - `salt`: Salt value -/// - `iterations`: Iteration count -/// - `dklen`: Length of the derived key -/// - `hmac`: Pseudorandom function -/// -/// See [RFC](https://tools.ietf.org/html/rfc8018#section-5.2) for more information. -/// -/// # Exceptions: -/// An exception will be thrown if: -/// - The specified dklen is less than 1 -/// - The specified dklen is greater than (2^32 - 1) * hLen -/// - The specified iteration count is less than 1 -/// -/// # Security: -/// Salts should always be generated using a CSPRNG. The `gen_rand_key` function -/// in `util` can be used for this. The recommended length for a salt is 16 bytes as a minimum. -/// The iteration count should be set as high as feasible. -/// # Example: -/// ### Generating derived key: -/// ``` -/// use orion::hazardous::pbkdf2::Pbkdf2; -/// use orion::core::util::gen_rand_key; -/// use orion::core::options::ShaVariantOption; -/// -/// let password = gen_rand_key(32).unwrap(); -/// let salt = gen_rand_key(32).unwrap(); -/// -/// let dk = Pbkdf2 { -/// password: password, -/// salt: salt, -/// iterations: 10000, -/// dklen: 64, -/// hmac: ShaVariantOption::SHA256 -/// }; -/// -/// dk.derive_key().unwrap(); -/// ``` -/// ### Verifying derived key: -/// ``` -/// use orion::hazardous::pbkdf2::Pbkdf2; -/// use orion::core::util::gen_rand_key; -/// use orion::core::options::ShaVariantOption; -/// -/// let password = gen_rand_key(32).unwrap(); -/// let salt = gen_rand_key(32).unwrap(); -/// -/// let dk = Pbkdf2 { -/// password: password, -/// salt: salt, -/// iterations: 10000, -/// dklen: 64, -/// hmac: ShaVariantOption::SHA256 -/// }; -/// -/// let derived_key = dk.derive_key().unwrap(); -/// assert_eq!(dk.verify(&derived_key).unwrap(), true); -/// ``` - -impl Pbkdf2 { - /// Return the maximum derived key dklen ((2^32 - 1) * hLen). - fn max_dklen(&self) -> usize { - match self.hmac.output_size() { - 32 => 137_438_953_440, - 48 => 206_158_430_160, - 64 => 274_877_906_880, - _ => panic!(UnknownCryptoError), - } +pub fn derive_key( + password: &[u8], + salt: &[u8], + iterations: usize, + dk_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if iterations < 1 { + return Err(UnknownCryptoError); } - - /// Returns a PRK using HMAC as the PRF. The parameters `ipad` and `opad` are constructed - /// in the `derive_key`. They are used to speed up HMAC calls. - fn prf(&self, ipad: &[u8], opad: &[u8], data: &[u8]) -> Vec { - pbkdf2_hmac(ipad, opad, data, self.hmac) + if dk_out.len() > 274_877_906_880 { + return Err(UnknownCryptoError); } - - /// Function F as described in the RFC. - fn function_f(&self, index: u32, ipad: &[u8], opad: &[u8], salt_ext: &mut [u8]) -> Vec { - - let pos = salt_ext.len() - 4; - write_u32_be(&mut salt_ext[pos..], index); - - // First iteration - let mut f_result: Vec = self.prf(ipad, opad, &salt_ext); - - // Remaining iterations - if self.iterations > 1 { - - let mut u_step = Vec::new(); - u_step.extend_from_slice(&f_result); - - for _ in 1..self.iterations { - u_step = self.prf(ipad, opad, &u_step); - - for index in 0..f_result.len() { - f_result[index] ^= u_step[index]; - } - } - } - - f_result + if dk_out.is_empty() { + return Err(UnknownCryptoError); } - /// Main PBKDF2 function. Returns a derived key. - pub fn derive_key(&self) -> Result, UnknownCryptoError> { - if self.iterations < 1 { - return Err(UnknownCryptoError); - } - if self.dklen > self.max_dklen() { - return Err(UnknownCryptoError); - } - if self.dklen < 1 { - return Err(UnknownCryptoError); - } - - let hlen_blocks: usize = 1 + ((self.dklen - 1) / self.hmac.output_size()); - - let pad_const = Hmac { - secret_key: Vec::new(), - data: Vec::new(), - sha2: self.hmac, - }; - let (mut ipad, mut opad) = pad_const.pad_key(&self.password); - let mut salt_ext = self.salt.clone(); - // We need 4 bytes of space for the index value - salt_ext.extend_from_slice(&[0u8; 4]); - let mut derived_key: Vec = Vec::new(); - - for index in 1..hlen_blocks + 1 { - derived_key.extend_from_slice(&self.function_f(index as u32, &ipad, &opad, &mut salt_ext)); - // Given that hlen_blocks is rounded correctly, then the `index as u32` - // should not be able to overflow. If the maximum dklen is selected, - // along with the highest output size, then hlen_blocks will equal - // exactly `u32::max_value()` - } - - Clear::clear(&mut ipad); - Clear::clear(&mut opad); + let mut hmac = hmac::init(password); - derived_key.truncate(self.dklen); - - Ok(derived_key) + for (idx, dk_block) in dk_out.chunks_mut(HLEN).enumerate() { + function_f(salt, iterations, idx + 1, dk_block, &mut hmac); + // Reset the HMAC state to clear the data from this iteration + hmac.reset(); } - /// Verify a derived key by comparing one from the current struct fields with the derived key - /// passed to the function. Comparison is done in constant time. Both derived keys must be - /// of equal length. - pub fn verify(&self, expected_dk: &[u8]) -> Result { - let own_dk = self.derive_key().unwrap(); + Ok(()) +} - if util::compare_ct(&own_dk, expected_dk).is_err() { - Err(ValidationCryptoError) - } else { - Ok(true) - } +/// Verify PBKDF2-HMAC-SHA512 derived key in constant time. +pub fn verify( + expected_dk: &[u8], + password: &[u8], + salt: &[u8], + iterations: usize, + dk_out: &mut [u8], +) -> Result { + derive_key(password, salt, iterations, dk_out).unwrap(); + + if util::compare_ct(&dk_out, expected_dk).is_err() { + Err(ValidationCryptoError) + } else { + Ok(true) } } @@ -212,154 +153,87 @@ mod test { extern crate hex; use self::hex::decode; - use core::options::ShaVariantOption; - use hazardous::pbkdf2::Pbkdf2; - - #[test] - fn dklen_too_high_sha256() { - let too_long = ((2_u64.pow(32) - 1) * 32 as u64) as usize + 1; - - let dk = Pbkdf2 { - password: "password".as_bytes().to_vec(), - salt: "salt".as_bytes().to_vec(), - iterations: 1, - dklen: too_long, - hmac: ShaVariantOption::SHA256, - }; - - assert!(dk.derive_key().is_err()); - } - - #[test] - fn dklen_too_high_sha384() { - let too_long = ((2_u64.pow(32) - 1) * 48 as u64) as usize + 1; - - let dk = Pbkdf2 { - password: "password".as_bytes().to_vec(), - salt: "salt".as_bytes().to_vec(), - iterations: 1, - dklen: too_long, - hmac: ShaVariantOption::SHA384, - }; - - assert!(dk.derive_key().is_err()); - } - - #[test] - fn dklen_too_high_sha512() { - let too_long = ((2_u64.pow(32) - 1) * 64 as u64) as usize + 1; - - let dk = Pbkdf2 { - password: "password".as_bytes().to_vec(), - salt: "salt".as_bytes().to_vec(), - iterations: 1, - dklen: too_long, - hmac: ShaVariantOption::SHA512, - }; - - assert!(dk.derive_key().is_err()); - } + use hazardous::pbkdf2::*; #[test] fn zero_iterations_err() { - let dk = Pbkdf2 { - password: "password".as_bytes().to_vec(), - salt: "salt".as_bytes().to_vec(), - iterations: 0, - dklen: 15, - hmac: ShaVariantOption::SHA256, - }; - - assert!(dk.derive_key().is_err()); + let password = "password".as_bytes(); + let salt = "salt".as_bytes(); + let iterations: usize = 0; + let mut okm_out = [0u8; 15]; + + assert!(derive_key(password, salt, iterations, &mut okm_out).is_err()); } #[test] fn zero_dklen_err() { - let dk = Pbkdf2 { - password: "password".as_bytes().to_vec(), - salt: "salt".as_bytes().to_vec(), - iterations: 2, - dklen: 0, - hmac: ShaVariantOption::SHA256, - }; - - assert!(dk.derive_key().is_err()); + let password = "password".as_bytes(); + let salt = "salt".as_bytes(); + let iterations: usize = 1; + let mut okm_out = [0u8; 0]; + + assert!(derive_key(password, salt, iterations, &mut okm_out).is_err()); } #[test] fn verify_true() { - let dk = Pbkdf2 { - password: "pass\0word".as_bytes().to_vec(), - salt: "sa\0lt".as_bytes().to_vec(), - iterations: 4096, - dklen: 16, - hmac: ShaVariantOption::SHA512, - }; + let password = "pass\0word".as_bytes(); + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 4096; + let mut okm_out = [0u8; 16]; let expected_dk = decode("9d9e9c4cd21fe4be24d5b8244c759665").unwrap(); - assert_eq!(dk.verify(&expected_dk).unwrap(), true); + assert_eq!( + verify(&expected_dk, password, salt, iterations, &mut okm_out).unwrap(), + true + ); } #[test] fn verify_false_wrong_salt() { - // Salt value differs between this and the previous test case - let dk = Pbkdf2 { - password: "pass\0word".as_bytes().to_vec(), - salt: "".as_bytes().to_vec(), - iterations: 4096, - dklen: 16, - hmac: ShaVariantOption::SHA512, - }; + let password = "pass\0word".as_bytes(); + let salt = "".as_bytes(); + let iterations: usize = 4096; + let mut okm_out = [0u8; 16]; let expected_dk = decode("9d9e9c4cd21fe4be24d5b8244c759665").unwrap(); - assert!(dk.verify(&expected_dk).is_err()); + assert!(verify(&expected_dk, password, salt, iterations, &mut okm_out).is_err()); } - #[test] fn verify_false_wrong_password() { - let dk = Pbkdf2 { - password: "none".as_bytes().to_vec(), - salt: "sa\0lt".as_bytes().to_vec(), - iterations: 4096, - dklen: 16, - hmac: ShaVariantOption::SHA512, - }; + let password = "".as_bytes(); + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 4096; + let mut okm_out = [0u8; 16]; let expected_dk = decode("9d9e9c4cd21fe4be24d5b8244c759665").unwrap(); - assert!(dk.verify(&expected_dk).is_err()); + assert!(verify(&expected_dk, password, salt, iterations, &mut okm_out).is_err()); } #[test] fn verify_diff_dklen_error() { - // Different dklen than expected dk - let dk = Pbkdf2 { - password: "pass\0word".as_bytes().to_vec(), - salt: "sa\0lt".as_bytes().to_vec(), - iterations: 4096, - dklen: 32, - hmac: ShaVariantOption::SHA512, - }; + let password = "pass\0word".as_bytes(); + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 4096; + let mut okm_out = [0u8; 32]; let expected_dk = decode("9d9e9c4cd21fe4be24d5b8244c759665").unwrap(); - assert!(dk.verify(&expected_dk).is_err()); + assert!(verify(&expected_dk, password, salt, iterations, &mut okm_out).is_err()); } #[test] fn verify_diff_iter_error() { - let dk = Pbkdf2 { - password: "pass\0word".as_bytes().to_vec(), - salt: "sa\0lt".as_bytes().to_vec(), - iterations: 800, - dklen: 16, - hmac: ShaVariantOption::SHA512, - }; + let password = "pass\0word".as_bytes(); + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 512; + let mut okm_out = [0u8; 16]; let expected_dk = decode("9d9e9c4cd21fe4be24d5b8244c759665").unwrap(); - assert!(dk.verify(&expected_dk).is_err()); + assert!(verify(&expected_dk, password, salt, iterations, &mut okm_out).is_err()); } } diff --git a/src/lib.rs b/src/lib.rs index b0b9d47f..7bf4adda 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,17 +20,18 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#![forbid(dead_code, warnings, unsafe_code, unused_imports)] +#![no_std] +#![forbid(warnings, unsafe_code)] extern crate byte_tools; -extern crate clear_on_drop; -extern crate constant_time_eq; extern crate rand; +extern crate seckey; extern crate sha2; +extern crate subtle; extern crate tiny_keccak; /// Core functionality such as generating a salt/key/IV/nonce. -pub mod core; +pub mod utilities; /// High-level API with safer defaults. Includes HMAC, HKDF, PBKDF2 and cSHAKE. pub mod default; diff --git a/src/tests/custom_hkdf.rs b/src/tests/custom_hkdf.rs new file mode 100644 index 00000000..c7391b64 --- /dev/null +++ b/src/tests/custom_hkdf.rs @@ -0,0 +1,188 @@ +// MIT License + +// Copyright (c) 2018 brycx + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// Testing against custom test vectors. +// These test vectors have been generated with the cryptography.io Python package. +// More information here: https://github.com/brycx/Test-Vector-Generation/ + +#[cfg(test)] +mod custom_hkdf { + + extern crate hex; + use self::hex::decode; + use hazardous::hkdf::*; + + fn hkdf_test_runner( + excp_okm: &[u8], + salt: &[u8], + ikm: &[u8], + info: &[u8], + okm_out: &mut [u8], + ) -> bool { + let actual_prk = extract(&salt, &ikm); + + expand(&actual_prk, &info, okm_out).unwrap(); + + (okm_out == excp_okm) + } + + #[test] + fn test_case_1() { + let ikm = "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b".as_bytes(); + let salt = "000102030405060708090a0b0c".as_bytes(); + let info = decode("").unwrap(); + let mut okm = [0u8; 32]; + + let expected_okm = + decode("55f4e219c32e613b33c637e4795379e886c8babb53cfbb9b8fa201fc7fdf604a").unwrap(); + + assert!(hkdf_test_runner( + &expected_okm, + &salt, + &ikm, + &info, + &mut okm + )); + } + + #[test] + fn test_case_2() { + let ikm = + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2\ + 02122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243444546474849\ + 4a4b4c4d4e4f" + .as_bytes(); + let salt = "salt".as_bytes(); + let info = "random InF\0".as_bytes(); + let mut okm = [0u8; 128]; + + let expected_okm = decode( + "56b03ea00ca8ef5bf639027ae10af7c0392f619a63b40cf61f305505f4e2c039df6e1baae41\ + f0f8545bba840afe297d8da514b03892233076ad0a38a5215d21c37757bb753c60cc03b2b71b\ + b4cad3fced96a9038b4b4369c9863879dd2e17d0c84d889e07f57a4502e2137b042a0e913974f\ + adbee53ff94a5bdae6aaae333766", + ).unwrap(); + + assert!(hkdf_test_runner( + &expected_okm, + &salt, + &ikm, + &info, + &mut okm + )); + } + + #[test] + fn test_case_3() { + let ikm = "password".as_bytes(); + let salt = + "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7\ + f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a\ + 8a9aaabacadaeaf" + .as_bytes(); + let info = decode("").unwrap(); + let mut okm = [0u8; 256]; + + let expected_okm = decode( + "b59f1a1f19a28aa1401bb5c3b8a6fded08d39ee15ae4101687585116fca3bd63ab64b9ba83f9\ + 675cfe97e7012a9b7fbce263c2b9c952ab07891d7cf4110846424f74a74c15a37d2124ee5962e\ + 94f5a00af64cdfdf59afc58af18dc43652860b4502770a8fd6997ed130546624ed26b75c8c7799\ + 54e8ba286a324e9cdad8babaf609ef12c84f3d7351a391bce365839a20ebf59e23dc22854d73c\ + 8386536c36261f67410309178097e40d46fe62bfe24d7e71c1a1bf854e62f8300d24c72137067b\ + d63e401606c5ece5fa31fba62bd3fc05d1906672c5b12605bd12ab8b2c3e216437d32973890d5\ + 1f9d713cc11ddbd40ef32fda2d581c4ac3f9ba8bdc823bdbe", + ).unwrap(); + + assert!(hkdf_test_runner( + &expected_okm, + &salt, + &ikm, + &info, + &mut okm + )); + } + + #[test] + fn test_case_4() { + let ikm = "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b".as_bytes(); + let salt = "s$#$#%$#SBGHWE@#W#lt".as_bytes(); + let info = "random InF\0".as_bytes(); + let mut okm = [0u8; 256]; + + let expected_okm = decode( + "2033ff164f1c52fff45984131a8e791da83f41b681d33f254cbd9fdc2e376f485ef07a5132a\ + 461a43efa454439b75dc270177a9871b4ceccc1c4d354d89a8e22ca4362839e55576088f3a3d\ + b677b3c7c2841f1f49bd646519020a0ac868390d52d3c96497c125bcf5a7083ad10422ec0b8f\ + 9a0c3f9d8eaa049173a67282d8393fdd8f24d78bb458b02eba60137c445f456c6e1922b8db02\ + 706efee900f5aa4b49cf407f8ac85297a09c29e2a16ef3a02da90ce8268a150cfa1e5325926f\ + 0bdefae6dabea86af6401d67c3c9a62e21db7ade26641628301d321f6d000034eae6e570321b\ + bb8eb2a36bd954b7c39dcb950b1bf55e1b3deffc06855f77513950d03", + ).unwrap(); + assert!(hkdf_test_runner( + &expected_okm, + &salt, + &ikm, + &info, + &mut okm + )); + } + + #[test] + fn test_case_5() { + let ikm = "passwordPASSWORDpassword".as_bytes(); + let salt = "salt".as_bytes(); + let info = decode("").unwrap(); + let mut okm = [0u8; 32]; + + let expected_okm = + decode("1ef9dccc02d5786f0d7133da824afe212547f2d8c97e9299345db86814dcb9b8").unwrap(); + + assert!(hkdf_test_runner( + &expected_okm, + &salt, + &ikm, + &info, + &mut okm + )); + } + + #[test] + fn test_case_6() { + let ikm = "pass\0word".as_bytes(); + let salt = "saltSALTSALTSALTSALTSALTSALTSALTSALTSALTSALTSALTSALT".as_bytes(); + let info = "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbccc\ + dcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f\ + 3f4f5f6f7f8f9fafbfcfdfeff" + .as_bytes(); + let mut okm = [0u8; 16]; + + let expected_okm = decode("ae4107effdae85d156832ec28e6e84e6").unwrap(); + + assert!(hkdf_test_runner( + &expected_okm, + &salt, + &ikm, + &info, + &mut okm + )); + } +} diff --git a/src/tests/custom_pbkdf2.rs b/src/tests/custom_pbkdf2.rs index a451b1a0..68891d9c 100644 --- a/src/tests/custom_pbkdf2.rs +++ b/src/tests/custom_pbkdf2.rs @@ -22,238 +22,103 @@ // Testing against custom test vectors. // These test vectors have been generated with the cryptography.io Python package. -// More information here: https://github.com/brycx/PBKDF2-HMAC-SHA2-Test-Vectors +// More information here: https://github.com/brycx/Test-Vector-Generation/ #[cfg(test)] mod custom_test_vectors { extern crate hex; use self::hex::decode; - use core::options::ShaVariantOption; - use hazardous::pbkdf2::Pbkdf2; - - #[test] - fn sha256_test_case_1() { - let actual_dk = Pbkdf2 { - password: "password".as_bytes().to_vec(), - salt: "salt".as_bytes().to_vec(), - iterations: 1, - dklen: 20, - hmac: ShaVariantOption::SHA256, - }; - - let expected_dk = decode("120fb6cffcf8b32c43e7225256c4f837a86548c9").unwrap(); - - assert_eq!(expected_dk, actual_dk.derive_key().unwrap()); - } - - #[test] - fn sha256_test_case_2() { - let actual_dk = Pbkdf2 { - password: "password".as_bytes().to_vec(), - salt: "salt".as_bytes().to_vec(), - iterations: 2, - dklen: 20, - hmac: ShaVariantOption::SHA256, - }; - - let expected_dk = decode("ae4d0c95af6b46d32d0adff928f06dd02a303f8e").unwrap(); - - assert_eq!(expected_dk, actual_dk.derive_key().unwrap()); - } - - #[test] - fn sha256_test_case_3() { - let actual_dk = Pbkdf2 { - password: "password".as_bytes().to_vec(), - salt: "salt".as_bytes().to_vec(), - iterations: 4096, - dklen: 20, - hmac: ShaVariantOption::SHA256, - }; - - let expected_dk = decode("c5e478d59288c841aa530db6845c4c8d962893a0").unwrap(); - - assert_eq!(expected_dk, actual_dk.derive_key().unwrap()); - } - - #[test] - fn sha256_test_case_4() { - let actual_dk = Pbkdf2 { - password: "passwordPASSWORDpassword".as_bytes().to_vec(), - salt: "saltSALTsaltSALTsaltSALTsaltSALTsalt".as_bytes().to_vec(), - iterations: 4096, - dklen: 25, - hmac: ShaVariantOption::SHA256, - }; - - let expected_dk = decode("348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c").unwrap(); - - assert_eq!(expected_dk, actual_dk.derive_key().unwrap()); - } - - #[test] - fn sha256_test_case_5() { - let actual_dk = Pbkdf2 { - password: "pass\0word".as_bytes().to_vec(), - salt: "sa\0lt".as_bytes().to_vec(), - iterations: 4096, - dklen: 16, - hmac: ShaVariantOption::SHA256, - }; - - let expected_dk = decode("89b69d0516f829893c696226650a8687").unwrap(); - - assert_eq!(expected_dk, actual_dk.derive_key().unwrap()); - } - - #[test] - fn sha384_test_case_1() { - let actual_dk = Pbkdf2 { - password: "password".as_bytes().to_vec(), - salt: "salt".as_bytes().to_vec(), - iterations: 1, - dklen: 20, - hmac: ShaVariantOption::SHA384, - }; - - let expected_dk = decode("c0e14f06e49e32d73f9f52ddf1d0c5c719160923").unwrap(); - - assert_eq!(expected_dk, actual_dk.derive_key().unwrap()); - } - - #[test] - fn sha384_test_case_2() { - let actual_dk = Pbkdf2 { - password: "password".as_bytes().to_vec(), - salt: "salt".as_bytes().to_vec(), - iterations: 2, - dklen: 20, - hmac: ShaVariantOption::SHA384, - }; - - let expected_dk = decode("54f775c6d790f21930459162fc535dbf04a93918").unwrap(); - - assert_eq!(expected_dk, actual_dk.derive_key().unwrap()); - } - - #[test] - fn sha384_test_case_3() { - let actual_dk = Pbkdf2 { - password: "password".as_bytes().to_vec(), - salt: "salt".as_bytes().to_vec(), - iterations: 4096, - dklen: 20, - hmac: ShaVariantOption::SHA384, - }; - - let expected_dk = decode("559726be38db125bc85ed7895f6e3cf574c7a01c").unwrap(); - - assert_eq!(expected_dk, actual_dk.derive_key().unwrap()); - } - - #[test] - fn sha384_test_case_4() { - let actual_dk = Pbkdf2 { - password: "passwordPASSWORDpassword".as_bytes().to_vec(), - salt: "saltSALTsaltSALTsaltSALTsaltSALTsalt".as_bytes().to_vec(), - iterations: 4096, - dklen: 25, - hmac: ShaVariantOption::SHA384, - }; - - let expected_dk = decode("819143ad66df9a552559b9e131c52ae6c5c1b0eed18f4d283b").unwrap(); - - assert_eq!(expected_dk, actual_dk.derive_key().unwrap()); - } - - #[test] - fn sha384_test_case_5() { - let actual_dk = Pbkdf2 { - password: "pass\0word".as_bytes().to_vec(), - salt: "sa\0lt".as_bytes().to_vec(), - iterations: 4096, - dklen: 16, - hmac: ShaVariantOption::SHA384, - }; - - let expected_dk = decode("a3f00ac8657e095f8e0823d232fc60b3").unwrap(); - - assert_eq!(expected_dk, actual_dk.derive_key().unwrap()); - } + use hazardous::pbkdf2::*; #[test] fn sha512_test_case_1() { - let actual_dk = Pbkdf2 { - password: "password".as_bytes().to_vec(), - salt: "salt".as_bytes().to_vec(), - iterations: 1, - dklen: 20, - hmac: ShaVariantOption::SHA512, - }; + let password = "password".as_bytes(); + let salt = "salt".as_bytes(); + let iter = 1; + let mut dk_out = [0u8; 20]; let expected_dk = decode("867f70cf1ade02cff3752599a3a53dc4af34c7a6").unwrap(); - assert_eq!(expected_dk, actual_dk.derive_key().unwrap()); + // verify() also runs derive_key() + assert!(verify(&expected_dk, password, salt, iter, &mut dk_out).unwrap()); } #[test] fn sha512_test_case_2() { - let actual_dk = Pbkdf2 { - password: "password".as_bytes().to_vec(), - salt: "salt".as_bytes().to_vec(), - iterations: 2, - dklen: 20, - hmac: ShaVariantOption::SHA512, - }; + let password = "password".as_bytes(); + let salt = "salt".as_bytes(); + let iter = 2; + let mut dk_out = [0u8; 20]; let expected_dk = decode("e1d9c16aa681708a45f5c7c4e215ceb66e011a2e").unwrap(); - assert_eq!(expected_dk, actual_dk.derive_key().unwrap()); + // verify() also runs derive_key() + assert!(verify(&expected_dk, password, salt, iter, &mut dk_out).unwrap()); } #[test] fn sha512_test_case_3() { - let actual_dk = Pbkdf2 { - password: "password".as_bytes().to_vec(), - salt: "salt".as_bytes().to_vec(), - iterations: 4096, - dklen: 20, - hmac: ShaVariantOption::SHA512, - }; + let password = "password".as_bytes(); + let salt = "salt".as_bytes(); + let iter = 4096; + let mut dk_out = [0u8; 20]; let expected_dk = decode("d197b1b33db0143e018b12f3d1d1479e6cdebdcc").unwrap(); - assert_eq!(expected_dk, actual_dk.derive_key().unwrap()); + // verify() also runs derive_key() + assert!(verify(&expected_dk, password, salt, iter, &mut dk_out).unwrap()); } #[test] fn sha512_test_case_4() { - let actual_dk = Pbkdf2 { - password: "passwordPASSWORDpassword".as_bytes().to_vec(), - salt: "saltSALTsaltSALTsaltSALTsaltSALTsalt".as_bytes().to_vec(), - iterations: 4096, - dklen: 25, - hmac: ShaVariantOption::SHA512, - }; + let password = "passwordPASSWORDpassword".as_bytes(); + let salt = "saltSALTsaltSALTsaltSALTsaltSALTsalt".as_bytes(); + let iter = 4096; + let mut dk_out = [0u8; 25]; let expected_dk = decode("8c0511f4c6e597c6ac6315d8f0362e225f3c501495ba23b868").unwrap(); - assert_eq!(expected_dk, actual_dk.derive_key().unwrap()); + // verify() also runs derive_key() + assert!(verify(&expected_dk, password, salt, iter, &mut dk_out).unwrap()); } #[test] fn sha512_test_case_5() { - let actual_dk = Pbkdf2 { - password: "pass\0word".as_bytes().to_vec(), - salt: "sa\0lt".as_bytes().to_vec(), - iterations: 4096, - dklen: 16, - hmac: ShaVariantOption::SHA512, - }; + let password = "pass\0word".as_bytes(); + let salt = "sa\0lt".as_bytes(); + let iter = 4096; + let mut dk_out = [0u8; 16]; let expected_dk = decode("9d9e9c4cd21fe4be24d5b8244c759665").unwrap(); - assert_eq!(expected_dk, actual_dk.derive_key().unwrap()); + // verify() also runs derive_key() + assert!(verify(&expected_dk, password, salt, iter, &mut dk_out).unwrap()); + } + + #[test] + fn sha512_test_case_6() { + let password = "passwd".as_bytes(); + let salt = "salt".as_bytes(); + let iter = 1; + let mut dk_out = [0u8; 128]; + + let expected_dk = decode("c74319d99499fc3e9013acff597c23c5baf0a0bec5634c46b8352b793e324723d55caa76b2b25c43402dcfdc06cdcf66f95b7d0429420b39520006749c51a04ef3eb99e576617395a178ba33214793e48045132928a9e9bf2661769fdc668f31798597aaf6da70dd996a81019726084d70f152baed8aafe2227c07636c6ddece").unwrap(); + + // verify() also runs derive_key() + assert!(verify(&expected_dk, password, salt, iter, &mut dk_out).unwrap()); + } + + #[test] + fn sha512_test_case_7() { + let password = "Password".as_bytes(); + let salt = "NaCl".as_bytes(); + let iter = 80000; + let mut dk_out = [0u8; 128]; + + let expected_dk = decode("e6337d6fbeb645c794d4a9b5b75b7b30dac9ac50376a91df1f4460f6060d5addb2c1fd1f84409abacc67de7eb4056e6bb06c2d82c3ef4ccd1bded0f675ed97c65c33d39f81248454327aa6d03fd049fc5cbb2b5e6dac08e8ace996cdc960b1bd4530b7e754773d75f67a733fdb99baf6470e42ffcb753c15c352d4800fb6f9d6").unwrap(); + + // verify() also runs derive_key() + assert!(verify(&expected_dk, password, salt, iter, &mut dk_out).unwrap()); } } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index eded7384..fb1da495 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -20,23 +20,20 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -/// Test HMAC against NIST test vectors. -pub mod nist_hmac; - /// Test HMAC aginast RFC test vectors. pub mod rfc_hmac; +/// Test against NIST CAVP test vectors; +pub mod nist_hmac; + /// Test HKDF aginast RFC test vectors. -pub mod rfc_hkdf; +pub mod other_hkdf; -/// Test PBKDF2 aginast RFC test vectors. -pub mod rfc_pbkdf2; +/// Test HKDF aginast custom test vectors. +pub mod custom_hkdf; -/// Test HMAC aginast custom test vectors. +/// Test PBKDF2 aginast custom test vectors. pub mod custom_pbkdf2; /// Test cSHAKE against official test vectors from the KeccakCodePackage. pub mod official_cshake; - -/// Test HMAC against IETF Draft test vectors. -pub mod other_hmac; diff --git a/src/tests/nist_hmac.rs b/src/tests/nist_hmac.rs index 1d3cca6a..d8d31457 100644 --- a/src/tests/nist_hmac.rs +++ b/src/tests/nist_hmac.rs @@ -16,33 +16,28 @@ // Testing against NIST CAVP HMACVS test vectors extern crate ring; use self::ring::{error, test}; -use core::options::ShaVariantOption; -use hazardous::hmac::Hmac; +use hazardous::hmac; fn hmac_test_runner( - option: ShaVariantOption, key: &[u8], input: &[u8], output: &[u8], is_ok: bool, ) -> Result<(), error::Unspecified> { - let hmac = Hmac { - secret_key: key.to_vec(), - data: input.to_vec(), - sha2: option, - }; + let mut mac = hmac::init(key); + mac.update(input); - let digest = hmac.finalize(); + let digest = mac.finalize(); - assert_eq!(is_ok, digest == output); + assert_eq!(is_ok, digest.as_ref() == output.as_ref()); // To conform with the Result construction of compare functions match is_ok { true => { - assert_eq!(is_ok, hmac.verify(output).unwrap()); + assert_eq!(is_ok, hmac::verify(output, key, input).unwrap()); } false => { - assert!(hmac.verify(output).is_err()); + assert!(hmac::verify(output, key, input).is_err()); } } @@ -58,18 +53,21 @@ fn hmac_tests() { let mut input = test_case.consume_bytes("Input"); let output = test_case.consume_bytes("Output"); - let alg = match digest_alg.as_ref() { - "SHA256" => ShaVariantOption::SHA256, - "SHA384" => ShaVariantOption::SHA384, - "SHA512" => ShaVariantOption::SHA512, + let run: bool = match digest_alg.as_ref() { + "SHA256" => false, // Not supported anymore + "SHA384" => false, // Not supported anymore + "SHA512" => true, _ => panic!("option not found"), }; + if run { + hmac_test_runner(&key_value[..], &input[..], &output[..], true)?; - hmac_test_runner(alg, &key_value[..], &input[..], &output[..], true)?; + // Tamper with the input and check that verification fails + input[0] ^= 1; - // Tamper with the input and check that verification fails - input[0] ^= 1; - - hmac_test_runner(alg, &key_value[..], &input[..], &output[..], false) + hmac_test_runner(&key_value[..], &input[..], &output[..], false) + } else { + Ok(()) + } }); } diff --git a/src/tests/official_cshake.rs b/src/tests/official_cshake.rs index caeec64a..613e1700 100644 --- a/src/tests/official_cshake.rs +++ b/src/tests/official_cshake.rs @@ -25,65 +25,17 @@ #[cfg(test)] mod kcp_test_vectors { - use core::options::*; - use hazardous::cshake::CShake; - - #[test] - fn cshake128_test_case_1() { - let cshake = CShake { - input: b"\x00\x01\x02\x03".to_vec(), - name: b"".to_vec(), - custom: b"Email Signature".to_vec(), - length: 32, - keccak: KeccakVariantOption::KECCAK256, - }; - - let expected = b"\xC1\xC3\x69\x25\xB6\x40\x9A\x04\xF1\xB5\x04\xFC\xBC\xA9\xD8\x2B\x40\x17\ - \x27\x7C\xB5\xED\x2B\x20\x65\xFC\x1D\x38\x14\xD5\xAA\xF5" - .to_vec(); - - assert_eq!(expected.len(), cshake.finalize().unwrap().len()); - assert_eq!(cshake.finalize().unwrap(), expected); - } - - #[test] - fn cshake_128_test_case_2() { - let cshake = CShake { - input: b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\ - \x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\ - \x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\ - \x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\ - \x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\ - \x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\ - \x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\ - \x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\ - \x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\ - \xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\ - \xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7" - .to_vec(), - name: b"".to_vec(), - custom: b"Email Signature".to_vec(), - length: 32, - keccak: KeccakVariantOption::KECCAK256, - }; - - let expected = b"\xC5\x22\x1D\x50\xE4\xF8\x22\xD9\x6A\x2E\x88\x81\xA9\x61\x42\x0F\x29\x4B\ - \x7B\x24\xFE\x3D\x20\x94\xBA\xED\x2C\x65\x24\xCC\x16\x6B" - .to_vec(); - - assert_eq!(expected.len(), cshake.finalize().unwrap().len()); - assert_eq!(cshake.finalize().unwrap(), expected); - } + use hazardous::cshake; #[test] fn cshake_256_test_case_1() { - let cshake = CShake { - input: b"\x00\x01\x02\x03".to_vec(), - name: b"".to_vec(), - custom: b"Email Signature".to_vec(), - length: 64, - keccak: KeccakVariantOption::KECCAK512, - }; + let input = b"\x00\x01\x02\x03"; + let custom = b"Email Signature"; + let mut out = [0u8; 64]; + + let mut cshake = cshake::init(custom, None).unwrap(); + cshake.update(input); + cshake.finalize(&mut out).unwrap(); let expected = b"\xD0\x08\x82\x8E\x2B\x80\xAC\x9D\x22\x18\xFF\xEE\x1D\x07\x0C\x48\xB8\ \xE4\xC8\x7B\xFF\x32\xC9\x69\x9D\x5B\x68\x96\xEE\xE0\xED\xD1\x64\x02\ @@ -91,14 +43,13 @@ mod kcp_test_vectors { \xA7\x4C\x41\x2B\xB4\xC7\x46\x46\x95\x27\x28\x1C\x8C" .to_vec(); - assert_eq!(expected.len(), cshake.finalize().unwrap().len()); - assert_eq!(cshake.finalize().unwrap(), expected); + assert_eq!(expected.len(), out.len()); + assert_eq!(out[..], expected[..]); } #[test] fn cshake_256_test_case_2() { - let cshake = CShake { - input: b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\ + let input = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\ \x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\ \x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\ \x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\ @@ -108,21 +59,21 @@ mod kcp_test_vectors { \x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\ \x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\ \xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\ - \xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7" - .to_vec(), - name: b"".to_vec(), - custom: b"Email Signature".to_vec(), - length: 64, - keccak: KeccakVariantOption::KECCAK512, - }; + \xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7"; + let custom = b"Email Signature"; + let mut out = [0u8; 64]; + + let mut cshake = cshake::init(custom, None).unwrap(); + cshake.update(input); + cshake.finalize(&mut out).unwrap(); let expected = b"\x07\xDC\x27\xB1\x1E\x51\xFB\xAC\x75\xBC\x7B\x3C\x1D\x98\x3E\x8B\x4B\x85\ - \xFB\x1D\xEF\xAF\x21\x89\x12\xAC\x86\x43\x02\x73\x09\x17\x27\xF4\x2B\x17\ - \xED\x1D\xF6\x3E\x8E\xC1\x18\xF0\x4B\x23\x63\x3C\x1D\xFB\x15\x74\xC8\xFB\ - \x55\xCB\x45\xDA\x8E\x25\xAF\xB0\x92\xBB" - .to_vec(); + \xFB\x1D\xEF\xAF\x21\x89\x12\xAC\x86\x43\x02\x73\x09\x17\x27\xF4\x2B\x17\ + \xED\x1D\xF6\x3E\x8E\xC1\x18\xF0\x4B\x23\x63\x3C\x1D\xFB\x15\x74\xC8\xFB\ + \x55\xCB\x45\xDA\x8E\x25\xAF\xB0\x92\xBB" + .to_vec(); - assert_eq!(expected.len(), cshake.finalize().unwrap().len()); - assert_eq!(cshake.finalize().unwrap(), expected); + assert_eq!(expected.len(), out.len()); + assert_eq!(out[..], expected[..]); } } diff --git a/src/tests/other_hkdf.rs b/src/tests/other_hkdf.rs new file mode 100644 index 00000000..91f1d70d --- /dev/null +++ b/src/tests/other_hkdf.rs @@ -0,0 +1,170 @@ +// MIT License + +// Copyright (c) 2018 brycx + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// Testing against test vectors from https://www.kullo.net/blog/hkdf-sha-512-test-vectors/ + +#[cfg(test)] +mod other_hkdf { + + extern crate hex; + use self::hex::decode; + use hazardous::hkdf::*; + + fn hkdf_test_runner( + excp_prk: &[u8], + excp_okm: &[u8], + salt: &[u8], + ikm: &[u8], + info: &[u8], + okm_out: &mut [u8], + ) -> bool { + let actual_prk = extract(&salt, &ikm); + + expand(&actual_prk, &info, okm_out).unwrap(); + + (actual_prk.as_ref() == excp_prk) == (okm_out == excp_okm) + } + + #[test] + fn test_case_1() { + let ikm = decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); + let salt = decode("000102030405060708090a0b0c").unwrap(); + let info = decode("f0f1f2f3f4f5f6f7f8f9").unwrap(); + let mut okm = [0u8; 42]; + + let expected_prk = decode( + "665799823737ded04a88e47e54a5890bb2c3d247c7a4254a8e61350723590a26c36238127d8661b88cf80ef802d57e2f7cebcf1e00e083848be19929c61b4237", + ).unwrap(); + + let expected_okm = decode( + "832390086cda71fb47625bb5ceb168e4c8e26a1a16ed34d9fc7fe92c1481579338da362cb8d9f925d7cb", + ).unwrap(); + + assert!(hkdf_test_runner( + &expected_prk, + &expected_okm, + &salt, + &ikm, + &info, + &mut okm + )); + } + + #[test] + fn test_case_2() { + let ikm = decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f").unwrap(); + let salt = decode("606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf").unwrap(); + let info = decode("b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff").unwrap(); + let mut okm = [0u8; 82]; + + let expected_prk = decode( + "35672542907d4e142c00e84499e74e1de08be86535f924e022804ad775dde27ec86cd1e5b7d178c74489bdbeb30712beb82d4f97416c5a94ea81ebdf3e629e4a", + ).unwrap(); + + let expected_okm = decode( + "ce6c97192805b346e6161e821ed165673b84f400a2b514b2fe23d84cd189ddf1b695b48cbd1c8388441137b3ce28f16aa64ba33ba466b24df6cfcb021ecff235f6a2056ce3af1de44d572097a8505d9e7a93", + ).unwrap(); + + assert!(hkdf_test_runner( + &expected_prk, + &expected_okm, + &salt, + &ikm, + &info, + &mut okm + )); + } + + #[test] + fn test_case_3() { + let ikm = decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); + let salt = decode("").unwrap(); + let info = decode("").unwrap(); + let mut okm = [0u8; 42]; + + let expected_prk = decode( + "fd200c4987ac491313bd4a2a13287121247239e11c9ef82802044b66ef357e5b194498d0682611382348572a7b1611de54764094286320578a863f36562b0df6", + ).unwrap(); + + let expected_okm = decode( + "f5fa02b18298a72a8c23898a8703472c6eb179dc204c03425c970e3b164bf90fff22d04836d0e2343bac", + ).unwrap(); + + assert!(hkdf_test_runner( + &expected_prk, + &expected_okm, + &salt, + &ikm, + &info, + &mut okm + )); + } + + #[test] + fn test_case_4() { + let ikm = decode("0b0b0b0b0b0b0b0b0b0b0b").unwrap(); + let salt = decode("000102030405060708090a0b0c").unwrap(); + let info = decode("f0f1f2f3f4f5f6f7f8f9").unwrap(); + let mut okm = [0u8; 42]; + + let expected_prk = decode( + "67409c9cac28b52ee9fad91c2fda999f7ca22e3434f0ae772863836568ad6a7f10cf113bfddd560129a594a8f52385c2d661d785d29ce93a11400c920683181d", + ).unwrap(); + + let expected_okm = decode( + "7413e8997e020610fbf6823f2ce14bff01875db1ca55f68cfcf3954dc8aff53559bd5e3028b080f7c068", + ).unwrap(); + assert!(hkdf_test_runner( + &expected_prk, + &expected_okm, + &salt, + &ikm, + &info, + &mut okm + )); + } + + #[test] + fn test_case_5() { + let ikm = decode("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c").unwrap(); + let salt = decode("").unwrap(); + let info = decode("").unwrap(); + let mut okm = [0u8; 42]; + + let expected_prk = decode( + "5346b376bf3aa9f84f8f6ed5b1c4f489172e244dac303d12f68ecc766ea600aa88495e7fb605803122fa136924a840b1f0719d2d5f68e29b242299d758ed680c", + ).unwrap(); + + let expected_okm = decode( + "1407d46013d98bc6decefcfee55f0f90b0c7f63d68eb1a80eaf07e953cfc0a3a5240a155d6e4daa965bb", + ).unwrap(); + + assert!(hkdf_test_runner( + &expected_prk, + &expected_okm, + &salt, + &ikm, + &info, + &mut okm + )); + } +} diff --git a/src/tests/other_hmac.rs b/src/tests/other_hmac.rs deleted file mode 100644 index 97f0b98f..00000000 --- a/src/tests/other_hmac.rs +++ /dev/null @@ -1,290 +0,0 @@ -// MIT License - -// Copyright (c) 2018 brycx - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// Testing against [IETF Draft](https://tools.ietf.org/html/draft-ietf-ipsec-ciph-sha-256-01) test vectors -#[cfg(test)] -mod ietf_draft { - - extern crate hex; - use self::hex::decode; - use core::options::ShaVariantOption; - use hazardous::hmac::*; - - fn hmac_test_runner( - secret_key: &[u8], - data: &[u8], - sha2: ShaVariantOption, - expected: &[u8], - trunc: Option, - should_be: bool, - ) -> bool { - let mac = Hmac { - secret_key: secret_key.to_vec(), - data: data.to_vec(), - sha2, - }; - - let (ipad, opad) = mac.pad_key(&mac.secret_key); - - let mut def_hmac = mac.finalize(); - let mut pbkdf2_hmac = pbkdf2_hmac(&ipad, &opad, &mac.data, mac.sha2); - - match trunc { - Some(ref length) => { - def_hmac.truncate(*length); - pbkdf2_hmac.truncate(*length); - } - None => (), - }; - - // If the MACs are modified, then they should not be equal to the expected - assert_ne!(&def_hmac[..def_hmac.len() - 1], expected); - assert_ne!(&pbkdf2_hmac[..pbkdf2_hmac.len() - 1], expected); - - should_be == ((pbkdf2_hmac == expected) == (def_hmac == expected)) - } - - #[test] - fn test_case_1() { - let secret_key = decode( - "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1\ - c1d1e1f20", - ).unwrap(); - let data = "abc".as_bytes().to_vec(); - - let expected_hmac_256 = - decode("a21b1f5d4cf4f73a4dd939750f7a066a7f98cc131cb16a6692759021cfab8181").unwrap(); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA256, - &expected_hmac_256, - None, - true - )); - } - - #[test] - fn test_case_2() { - let secret_key = decode( - "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1\ - c1d1e1f20", - ).unwrap(); - let data = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" - .as_bytes() - .to_vec(); - - let expected_hmac_256 = - decode("104fdc1257328f08184ba73131c53caee698e36119421149ea8c712456697d30").unwrap(); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA256, - &expected_hmac_256, - None, - true - )); - } - - #[test] - fn test_case_3() { - let secret_key = decode( - "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1\ - c1d1e1f20", - ).unwrap(); - let data = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopqabcdbcdecdef\ - defgefghfghighijhijkijkljklmklmnlmnomnopnopq" - .as_bytes() - .to_vec(); - - let expected_hmac_256 = - decode("470305fc7e40fe34d3eeb3e773d95aab73acf0fd060447a5eb4595bf33a9d1a3").unwrap(); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA256, - &expected_hmac_256, - None, - true - )); - } - - #[test] - fn test_case_4() { - let secret_key = decode( - "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b\ - 0b0b0b0b0b0b", - ).unwrap(); - let data = "Hi There".as_bytes().to_vec(); - - let expected_hmac_256 = - decode("198a607eb44bfbc69903a0f1cf2bbdc5ba0aa3f3d9ae3c1c7a3b1696a0b68cf7").unwrap(); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA256, - &expected_hmac_256, - None, - true - )); - } - - #[test] - fn test_case_5() { - let secret_key = "Jefe".as_bytes().to_vec(); - let data = "what do ya want for nothing?".as_bytes().to_vec(); - - let expected_hmac_256 = - decode("5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843").unwrap(); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA256, - &expected_hmac_256, - None, - true - )); - } - - #[test] - fn test_case_6() { - let secret_key = decode( - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaa", - ).unwrap(); - let data = "ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\ - ddddddddddddddddddddddddddddddddd" - .as_bytes() - .to_vec(); - - let expected_hmac_256 = - decode("cdcb1220d1ecccea91e53aba3092f962e549fe6ce9ed7fdc43191fbde45c30b0").unwrap(); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA256, - &expected_hmac_256, - None, - true - )); - } - - #[test] - fn test_case_7() { - let secret_key = decode( - "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20\ - 2122232425", - ).unwrap(); - let data = "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd\ - cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" - .as_bytes() - .to_vec(); - - let expected_hmac_256 = - decode("d4633c17f6fb8d744c66dee0f8f074556ec4af55ef07998541468eb49bd2e917").unwrap(); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA256, - &expected_hmac_256, - None, - true - )); - } - - #[test] - fn test_case_8() { - let secret_key = decode( - "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0\ - c0c0c0c0c0c0c", - ).unwrap(); - let data = "Test With Truncation".as_bytes().to_vec(); - - let expected_hmac_256 = - decode("7546af01841fc09b1ab9c3749a5f1c17d4f589668a587b2700a9c97c1193cf42").unwrap(); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA256, - &expected_hmac_256, - None, - true - )); - } - - #[test] - fn test_case_9() { - let secret_key = decode( - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - ).unwrap(); - let data = "Test Using Larger Than Block-Size Key - Hash Key First" - .as_bytes() - .to_vec(); - - let expected_hmac_256 = - decode("6953025ed96f0c09f80a96f78e6538dbe2e7b820e3dd970e7ddd39091b32352f").unwrap(); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA256, - &expected_hmac_256, - None, - true - )); - } - - #[test] - fn test_case_10() { - let secret_key = decode( - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - ).unwrap(); - let data = "Test Using Larger Than Block-Size Key and Larger Than One \ - Block-Size Data" - .as_bytes() - .to_vec(); - - let expected_hmac_256 = - decode("6355ac22e890d0a3c8481a5ca4825bc884d3e7a1ff98a2fc2ac7d8e064c3b2e6").unwrap(); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA256, - &expected_hmac_256, - None, - true - )); - } -} diff --git a/src/tests/rfc_hkdf.rs b/src/tests/rfc_hkdf.rs deleted file mode 100644 index 1d9d7881..00000000 --- a/src/tests/rfc_hkdf.rs +++ /dev/null @@ -1,120 +0,0 @@ -// MIT License - -// Copyright (c) 2018 brycx - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// Testing against RFC 5869 test vectors. - -#[cfg(test)] -mod rfc5869 { - - extern crate hex; - use self::hex::decode; - use core::options::ShaVariantOption; - use hazardous::hkdf::Hkdf; - - #[test] - fn test_case_1() { - let hkdf = Hkdf { - salt: decode("000102030405060708090a0b0c").unwrap(), - ikm: decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), - info: decode("f0f1f2f3f4f5f6f7f8f9").unwrap(), - length: 42, - hmac: ShaVariantOption::SHA256, - }; - - let expected_prk = - decode("077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5").unwrap(); - - let expected_okm = decode( - "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf\ - 34007208d5b887185865", - ).unwrap(); - - let actual_prk = hkdf.extract(&hkdf.salt, &hkdf.ikm); - - assert_eq!(actual_prk, expected_prk); - assert_eq!(hkdf.expand(&actual_prk).unwrap(), expected_okm); - assert_eq!(hkdf.derive_key().unwrap(), expected_okm); - } - - #[test] - fn test_case_2() { - let hkdf = Hkdf { - salt: decode( - "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f\ - 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f\ - a0a1a2a3a4a5a6a7a8a9aaabacadaeaf", - ).unwrap(), - ikm: decode( - "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\ - 202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f\ - 404142434445464748494a4b4c4d4e4f", - ).unwrap(), - info: decode( - "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecf\ - d0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef\ - f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", - ).unwrap(), - length: 82, - hmac: ShaVariantOption::SHA256, - }; - - let expected_prk = - decode("06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc244").unwrap(); - - let expected_okm = decode( - "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c\ - 59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71\ - cc30c58179ec3e87c14c01d5c1f3434f1d87", - ).unwrap(); - - let actual_prk = hkdf.extract(&hkdf.salt, &hkdf.ikm); - - assert_eq!(actual_prk, expected_prk); - assert_eq!(hkdf.expand(&actual_prk).unwrap(), expected_okm); - assert_eq!(hkdf.derive_key().unwrap(), expected_okm); - } - - #[test] - fn test_case_3() { - let hkdf = Hkdf { - salt: decode("").unwrap(), - ikm: decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), - info: decode("").unwrap(), - length: 42, - hmac: ShaVariantOption::SHA256, - }; - - let expected_prk = - decode("19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04").unwrap(); - - let expected_okm = decode( - "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d\ - 9d201395faa4b61a96c8", - ).unwrap(); - - let actual_prk = hkdf.extract(&hkdf.salt, &hkdf.ikm); - - assert_eq!(actual_prk, expected_prk); - assert_eq!(hkdf.expand(&actual_prk).unwrap(), expected_okm); - assert_eq!(hkdf.derive_key().unwrap(), expected_okm); - } -} diff --git a/src/tests/rfc_hmac.rs b/src/tests/rfc_hmac.rs index 2045cb22..3ab32d79 100644 --- a/src/tests/rfc_hmac.rs +++ b/src/tests/rfc_hmac.rs @@ -26,54 +26,34 @@ mod rfc4231 { extern crate hex; use self::hex::decode; - use core::options::ShaVariantOption; - use hazardous::hmac::*; + use hazardous::hmac; fn hmac_test_runner( secret_key: &[u8], data: &[u8], - sha2: ShaVariantOption, expected: &[u8], trunc: Option, should_be: bool, ) -> bool { - let mac = Hmac { - secret_key: secret_key.to_vec(), - data: data.to_vec(), - sha2, - }; - - let (ipad, opad) = mac.pad_key(&mac.secret_key); + let mut mac = hmac::init(secret_key); + mac.update(data); - let mut def_hmac = mac.finalize(); - let mut pbkdf2_hmac = pbkdf2_hmac(&ipad, &opad, &mac.data, mac.sha2); - - match trunc { - Some(ref length) => { - def_hmac.truncate(*length); - pbkdf2_hmac.truncate(*length); - } - None => (), + let res = mac.finalize(); + let len = match trunc { + Some(ref length) => *length, + None => 64, }; // If the MACs are modified, then they should not be equal to the expected - assert_ne!(&def_hmac[..def_hmac.len() - 1], expected); - assert_ne!(&pbkdf2_hmac[..pbkdf2_hmac.len() - 1], expected); + assert_ne!(&res[..len - 1], expected); - should_be == ((pbkdf2_hmac == expected) == (def_hmac == expected)) + should_be == (res[..len] == expected[..len]) } #[test] fn test_case_1() { let secret_key = decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); let data = "Hi There".as_bytes().to_vec(); - - let expected_hmac_256 = - decode("b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7").unwrap(); - let expected_hmac_384 = decode( - "afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59c\ - faea9ea9076ede7f4af152e8b2fa9cb6", - ).unwrap(); let expected_hmac_512 = decode( "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde\ daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854", @@ -82,25 +62,6 @@ mod rfc4231 { assert!(hmac_test_runner( &secret_key, &data, - ShaVariantOption::SHA256, - &expected_hmac_256, - None, - true - )); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA384, - &expected_hmac_384, - None, - true - )); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA512, &expected_hmac_512, None, true @@ -112,12 +73,6 @@ mod rfc4231 { let secret_key = "Jefe".as_bytes().to_vec(); let data = "what do ya want for nothing?".as_bytes().to_vec(); - let expected_hmac_256 = - decode("5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843").unwrap(); - let expected_hmac_384 = decode( - "af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e\ - 8e2240ca5e69e2c78b3239ecfab21649", - ).unwrap(); let expected_hmac_512 = decode( "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554\ 9758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737", @@ -126,25 +81,6 @@ mod rfc4231 { assert!(hmac_test_runner( &secret_key, &data, - ShaVariantOption::SHA256, - &expected_hmac_256, - None, - true - )); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA384, - &expected_hmac_384, - None, - true - )); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA512, &expected_hmac_512, None, true @@ -159,12 +95,6 @@ mod rfc4231 { dddddddddddddddddddddddddddddddddddd", ).unwrap(); - let expected_hmac_256 = - decode("773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe").unwrap(); - let expected_hmac_384 = decode( - "88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e55966144b\ - 2a5ab39dc13814b94e3ab6e101a34f27", - ).unwrap(); let expected_hmac_512 = decode( "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39\ bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb", @@ -173,25 +103,6 @@ mod rfc4231 { assert!(hmac_test_runner( &secret_key, &data, - ShaVariantOption::SHA256, - &expected_hmac_256, - None, - true - )); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA384, - &expected_hmac_384, - None, - true - )); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA512, &expected_hmac_512, None, true @@ -206,12 +117,6 @@ mod rfc4231 { cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", ).unwrap(); - let expected_hmac_256 = - decode("82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b").unwrap(); - let expected_hmac_384 = decode( - "3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b4e\ - 6801dd23c4a7d679ccf8a386c674cffb", - ).unwrap(); let expected_hmac_512 = decode( "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3db\ a91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd", @@ -220,25 +125,6 @@ mod rfc4231 { assert!(hmac_test_runner( &secret_key, &data, - ShaVariantOption::SHA256, - &expected_hmac_256, - None, - true - )); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA384, - &expected_hmac_384, - None, - true - )); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA512, &expected_hmac_512, None, true @@ -250,32 +136,11 @@ mod rfc4231 { let secret_key = decode("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c").unwrap(); let data = decode("546573742057697468205472756e636174696f6e").unwrap(); - let expected_hmac_256 = decode("a3b6167473100ee06e0c796c2955552b").unwrap(); - let expected_hmac_384 = decode("3abf34c3503b2a23a46efc619baef897").unwrap(); let expected_hmac_512 = decode("415fad6271580a531d4179bc891d87a6").unwrap(); assert!(hmac_test_runner( &secret_key, &data, - ShaVariantOption::SHA256, - &expected_hmac_256, - Some(16), - true - )); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA384, - &expected_hmac_384, - Some(16), - true - )); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA512, &expected_hmac_512, Some(16), true @@ -296,12 +161,6 @@ mod rfc4231 { 65204b6579202d2048617368204b6579204669727374", ).unwrap(); - let expected_hmac_256 = - decode("60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54").unwrap(); - let expected_hmac_384 = decode( - "4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4c6\ - 0c2ef6ab4030fe8296248df163f44952", - ).unwrap(); let expected_hmac_512 = decode( "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f352\ 6b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598", @@ -310,25 +169,6 @@ mod rfc4231 { assert!(hmac_test_runner( &secret_key, &data, - ShaVariantOption::SHA256, - &expected_hmac_256, - None, - true - )); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA384, - &expected_hmac_384, - None, - true - )); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA512, &expected_hmac_512, None, true @@ -352,12 +192,6 @@ mod rfc4231 { 642062792074686520484d414320616c676f726974686d2e", ).unwrap(); - let expected_hmac_256 = - decode("9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2").unwrap(); - let expected_hmac_384 = decode( - "6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99c5\ - a678cc31e799176d3860e6110c46523e", - ).unwrap(); let expected_hmac_512 = decode( "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944\ b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58", @@ -366,25 +200,6 @@ mod rfc4231 { assert!(hmac_test_runner( &secret_key, &data, - ShaVariantOption::SHA256, - &expected_hmac_256, - None, - true - )); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA384, - &expected_hmac_384, - None, - true - )); - - assert!(hmac_test_runner( - &secret_key, - &data, - ShaVariantOption::SHA512, &expected_hmac_512, None, true diff --git a/src/tests/rfc_pbkdf2.rs b/src/tests/rfc_pbkdf2.rs deleted file mode 100644 index 69345d95..00000000 --- a/src/tests/rfc_pbkdf2.rs +++ /dev/null @@ -1,67 +0,0 @@ -// MIT License - -// Copyright (c) 2018 brycx - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// Testing against RFC 7914 test vectors -#[cfg(test)] -mod rfc7914 { - - extern crate hex; - use self::hex::decode; - use core::options::ShaVariantOption; - use hazardous::pbkdf2::Pbkdf2; - - #[test] - fn rfc7914_test_case_1() { - let dk = Pbkdf2 { - password: "passwd".as_bytes().to_vec(), - salt: "salt".as_bytes().to_vec(), - iterations: 1, - dklen: 64, - hmac: ShaVariantOption::SHA256, - }; - - let expected_dk = decode( - "55ac046e56e3089fec1691c22544b605f94185216dde0465e68b9d57c20dacbc\ - 49ca9cccf179b645991664b39d77ef317c71b845b1e30bd509112041d3a19783", - ).unwrap(); - - assert_eq!(expected_dk, dk.derive_key().unwrap()); - } - - #[test] - fn rfc7914_test_case_2() { - let dk = Pbkdf2 { - password: "Password".as_bytes().to_vec(), - salt: "NaCl".as_bytes().to_vec(), - iterations: 80000, - dklen: 64, - hmac: ShaVariantOption::SHA256, - }; - - let expected_dk = decode( - "4ddcd8f60b98be21830cee5ef22701f9641a4418d04c0414aeff08876b34ab56\ - a1d425a1225833549adb841b51c9b3176a272bdebba1d078478f62b397f33c8d", - ).unwrap(); - - assert_eq!(expected_dk, dk.derive_key().unwrap()); - } -} diff --git a/src/core/errors.rs b/src/utilities/errors.rs similarity index 82% rename from src/core/errors.rs rename to src/utilities/errors.rs index 65b96556..0ce20b3e 100644 --- a/src/core/errors.rs +++ b/src/utilities/errors.rs @@ -20,9 +20,9 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +use core::fmt; use rand; -use std::error::Error; -use std::fmt; + /// Opaque error. #[derive(Debug, PartialEq)] pub struct UnknownCryptoError; @@ -33,16 +33,6 @@ impl fmt::Display for UnknownCryptoError { } } -impl Error for UnknownCryptoError { - fn description(&self) -> &str { - "UnknownCryptoError" - } - - fn cause(&self) -> Option<&Error> { - None - } -} - // Required for rand's generators impl From for UnknownCryptoError { fn from(_: rand::Error) -> Self { @@ -59,13 +49,3 @@ impl fmt::Display for ValidationCryptoError { write!(f, "ValidationCryptoError") } } - -impl Error for ValidationCryptoError { - fn description(&self) -> &str { - "ValidationCryptoError" - } - - fn cause(&self) -> Option<&Error> { - None - } -} diff --git a/src/core/mod.rs b/src/utilities/mod.rs similarity index 95% rename from src/core/mod.rs rename to src/utilities/mod.rs index c14dffae..fd9c59cc 100644 --- a/src/core/mod.rs +++ b/src/utilities/mod.rs @@ -23,8 +23,5 @@ /// Errors for orion's cryptographic operations. pub mod errors; -/// SHA2/Keccak options and hashing. -pub mod options; - /// Utility functions such as constant time comparison. pub mod util; diff --git a/src/core/util.rs b/src/utilities/util.rs similarity index 76% rename from src/core/util.rs rename to src/utilities/util.rs index ae79a965..7c3c2e22 100644 --- a/src/core/util.rs +++ b/src/utilities/util.rs @@ -20,33 +20,32 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use constant_time_eq::constant_time_eq; -use core::errors; use rand::{rngs::OsRng, RngCore}; +use subtle::ConstantTimeEq; +use utilities::errors; #[inline(never)] -/// Return a random byte vector of a given length. This uses rand's -/// [OsRng](https://docs.rs/rand/0.5.1/rand/rngs/struct.OsRng.html). Length must be >= 1. -pub fn gen_rand_key(len: usize) -> Result, errors::UnknownCryptoError> { - if len < 1 { +/// Fill `dst` with random bytes. This uses rand's +/// [OsRng](https://docs.rs/rand/0.5.1/rand/rngs/struct.OsRng.html). Length of `dst` must be >= 1. +pub fn gen_rand_key(dst: &mut [u8]) -> Result<(), errors::UnknownCryptoError> { + if dst.is_empty() { return Err(errors::UnknownCryptoError); } - let mut rand_vec = vec![0x00; len]; let mut generator = OsRng::new()?; - generator.try_fill_bytes(&mut rand_vec)?; + generator.try_fill_bytes(dst)?; - Ok(rand_vec) + Ok(()) } /// Compare two equal length slices in constant time, using the -/// [constant_time_eq](https://crates.io/crates/constant_time_eq) crate. +/// [subtle](https://crates.io/crates/subtle) crate. pub fn compare_ct(a: &[u8], b: &[u8]) -> Result { if a.len() != b.len() { return Err(errors::UnknownCryptoError); } - if constant_time_eq(a, b) { + if a.ct_eq(b).unwrap_u8() == 1 { Ok(true) } else { Err(errors::UnknownCryptoError) @@ -55,21 +54,24 @@ pub fn compare_ct(a: &[u8], b: &[u8]) -> Result