Skip to content

Illustration for #80 #82

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions .github/workflows/hkdf.yml
Original file line number Diff line number Diff line change
@@ -36,11 +36,6 @@ jobs:
targets: ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}

minimal-versions:
uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master
with:
working-directory: ${{ github.workflow }}

test:
runs-on: ubuntu-latest
strategy:
@@ -55,6 +50,6 @@ jobs:
with:
toolchain: ${{ matrix.rust }}
- run: cargo check --all-features
- run: cargo test --no-default-features
- run: cargo test
- run: cargo test --all-features
- run: cargo test --no-default-features -- --test-threads=1
- run: cargo test -- --test-threads=1
- run: cargo test --all-features -- --test-threads=1
6 changes: 1 addition & 5 deletions hkdf/src/lib.rs
Original file line number Diff line number Diff line change
@@ -100,6 +100,7 @@
extern crate std;

pub use hmac;
pub use sealed::HmacImpl;

use core::fmt;
use core::marker::PhantomData;
@@ -282,8 +283,3 @@ where
f.write_str("> { ... }")
}
}

/// Sealed trait implemented for [`Hmac`] and [`SimpleHmac`].
pub trait HmacImpl<H: OutputSizeUser>: sealed::Sealed<H> {}

impl<H: OutputSizeUser, T: sealed::Sealed<H>> HmacImpl<H> for T {}
8 changes: 5 additions & 3 deletions hkdf/src/sealed.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(missing_docs)]

use hmac::digest::{
block_buffer::Eager,
core_api::{
@@ -9,7 +11,7 @@ use hmac::digest::{
};
use hmac::{Hmac, HmacCore, SimpleHmac};

pub trait Sealed<H: OutputSizeUser> {
pub trait HmacImpl<H: OutputSizeUser> {
type Core: Clone;

fn new_from_slice(key: &[u8]) -> Self;
@@ -23,7 +25,7 @@ pub trait Sealed<H: OutputSizeUser> {
fn finalize(self) -> Output<H>;
}

impl<H> Sealed<H> for Hmac<H>
impl<H> HmacImpl<H> for Hmac<H>
where
H: CoreProxy + OutputSizeUser,
H::Core: HashMarker
@@ -65,7 +67,7 @@ where
}
}

impl<H: Digest + BlockSizeUser + Clone> Sealed<H> for SimpleHmac<H> {
impl<H: Digest + BlockSizeUser + Clone> HmacImpl<H> for SimpleHmac<H> {
type Core = Self;

#[inline(always)]
95 changes: 84 additions & 11 deletions hkdf/tests/tests.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,77 @@
use core::iter;

use hex_literal::hex;
use hkdf::HmacImpl;
use hkdf::{Hkdf, HkdfExtract, SimpleHkdf, SimpleHkdfExtract};
use hmac::digest::KeyInit;
use hmac::Hmac;
use sha1::Sha1;
use sha2::digest::{Digest, FixedOutput, Output};
use sha2::{Sha256, Sha384, Sha512};
use std::convert::TryInto;
use std::sync::atomic::{AtomicBool, Ordering};

// Those functions are provided by hardware. We implement them for testing purposes only.
fn hw_hash(input: &[u8], output: &mut [u8; 32]) {
output.copy_from_slice(&Sha256::digest(input));
}
fn hw_init(key: &[u8]) -> Result<(), ()> {
let ctxt = unsafe { &mut HW_CTXT };
*ctxt = Some(<Hmac<_> as KeyInit>::new_from_slice(key).map_err(|_| ())?);
Ok(())
}
fn hw_update(data: &[u8]) -> Result<(), ()> {
let ctxt = unsafe { &mut HW_CTXT }.as_mut().ok_or(())?;
ctxt.update(data);
Ok(())
}
fn hw_finalize(output: &mut [u8; 32]) -> Result<(), ()> {
let ctxt = unsafe { &mut HW_CTXT }.take().ok_or(())?;
ctxt.finalize_into(output.into());
Ok(())
}
// The hmac context is stored in hardware (unique and global). We need to run the tests with a
// single thread because of that: `cargo test -- --test-threads=1`.
static mut HW_CTXT: Option<Hmac<Sha256>> = None;

// How the user would implement HmacImpl on top of hardware to be used in HKDF.
struct HmacContext; // should not be publicly constructible
static HMAC_INITIALIZED: AtomicBool = AtomicBool::new(false);
impl HmacImpl<Sha256> for HmacContext {
type Core = [u8; 64];

fn new_from_slice(key: &[u8]) -> Self {
Self::from_core(&Self::new_core(key))
}

fn new_core(key: &[u8]) -> Self::Core {
let mut core = [0; 64];
if key.len() <= 64 {
core[..key.len()].copy_from_slice(key);
} else {
hw_hash(key, (&mut core[..32]).try_into().unwrap());
}
core
}

fn from_core(core: &Self::Core) -> Self {
assert!(!HMAC_INITIALIZED.swap(true, Ordering::SeqCst));
hw_init(core).unwrap(); // RustCrypto API does not support errors.
HmacContext
}

fn update(&mut self, data: &[u8]) {
assert!(HMAC_INITIALIZED.load(Ordering::SeqCst));
hw_update(data).unwrap(); // RustCrypto API does not support errors.
}

fn finalize(self) -> Output<Sha256> {
assert!(HMAC_INITIALIZED.swap(false, Ordering::SeqCst));
let mut output = [0; 32];
hw_finalize(&mut output).unwrap(); // RustCrypto API does not support errors.
output.into()
}
}

struct Test<'a> {
ikm: &'a [u8],
@@ -91,15 +159,15 @@ fn test_rfc5869_sha256() {
} else {
Some(&salt[..])
};
let (prk2, hkdf) = Hkdf::<Sha256>::extract(salt, ikm);
let (prk2, hkdf) = Hkdf::<Sha256, HmacContext>::extract(salt, ikm);
let mut okm2 = vec![0u8; okm.len()];
assert!(hkdf.expand(&info[..], &mut okm2).is_ok());

assert_eq!(prk2[..], prk[..]);
assert_eq!(okm2[..], okm[..]);

okm2.iter_mut().for_each(|b| *b = 0);
let hkdf = Hkdf::<Sha256>::from_prk(prk).unwrap();
let hkdf = Hkdf::<Sha256, HmacContext>::from_prk(prk).unwrap();
assert!(hkdf.expand(&info[..], &mut okm2).is_ok());
assert_eq!(okm2[..], okm[..]);
}
@@ -203,7 +271,7 @@ const MAX_SHA256_LENGTH: usize = 255 * (256 / 8); // =8160

#[test]
fn test_lengths() {
let hkdf = Hkdf::<Sha256>::new(None, &[]);
let hkdf = Hkdf::<Sha256, HmacContext>::new(None, &[]);
let mut longest = vec![0u8; MAX_SHA256_LENGTH];
assert!(hkdf.expand(&[], &mut longest).is_ok());
// Runtime is O(length), so exhaustively testing all legal lengths
@@ -222,21 +290,21 @@ fn test_lengths() {

#[test]
fn test_max_length() {
let hkdf = Hkdf::<Sha256>::new(Some(&[]), &[]);
let hkdf = Hkdf::<Sha256, HmacContext>::new(Some(&[]), &[]);
let mut okm = vec![0u8; MAX_SHA256_LENGTH];
assert!(hkdf.expand(&[], &mut okm).is_ok());
}

#[test]
fn test_max_length_exceeded() {
let hkdf = Hkdf::<Sha256>::new(Some(&[]), &[]);
let hkdf = Hkdf::<Sha256, HmacContext>::new(Some(&[]), &[]);
let mut okm = vec![0u8; MAX_SHA256_LENGTH + 1];
assert!(hkdf.expand(&[], &mut okm).is_err());
}

#[test]
fn test_unsupported_length() {
let hkdf = Hkdf::<Sha256>::new(Some(&[]), &[]);
let hkdf = Hkdf::<Sha256, HmacContext>::new(Some(&[]), &[]);
let mut okm = vec![0u8; 90000];
assert!(hkdf.expand(&[], &mut okm).is_err());
}
@@ -247,7 +315,7 @@ fn test_prk_too_short() {

let output_len = Sha256::output_size();
let prk = vec![0; output_len - 1];
assert!(Hkdf::<Sha256>::from_prk(&prk).is_err());
assert!(Hkdf::<Sha256, HmacContext>::from_prk(&prk).is_err());
}

#[test]
@@ -284,7 +352,7 @@ fn test_expand_multi_info() {
&b"1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d"[..],
];

let (_, hkdf_ctx) = Hkdf::<Sha256>::extract(None, b"some ikm here");
let (_, hkdf_ctx) = Hkdf::<Sha256, HmacContext>::extract(None, b"some ikm here");

// Compute HKDF-Expand on the concatenation of all the info components
let mut oneshot_res = [0u8; 16];
@@ -328,7 +396,8 @@ fn test_extract_streaming() {
let salt = b"mysalt";

// Compute HKDF-Extract on the concatenation of all the IKM components
let (oneshot_res, _) = Hkdf::<Sha256>::extract(Some(&salt[..]), &ikm_components.concat());
let (oneshot_res, _) =
Hkdf::<Sha256, HmacContext>::extract(Some(&salt[..]), &ikm_components.concat());

// Now iteratively join the components of ikm_components until it's all 1 component. The value
// of HKDF-Extract should be the same throughout
@@ -340,7 +409,7 @@ fn test_extract_streaming() {

// Make a new extraction context and build the new input to be the IKM head followed by the
// remaining components
let mut extract_ctx = HkdfExtract::<Sha256>::new(Some(&salt[..]));
let mut extract_ctx = HkdfExtract::<Sha256, HmacContext>::new(Some(&salt[..]));
let input = iter::once(ikm_head.as_slice())
.chain(ikm_components.iter().cloned().skip(num_concatted + 1));

@@ -422,7 +491,11 @@ macro_rules! new_test {
}

new_test!(wycheproof_sha1, "wycheproof-sha1", Hkdf::<Sha1>);
new_test!(wycheproof_sha256, "wycheproof-sha256", Hkdf::<Sha256>);
new_test!(
wycheproof_sha256,
"wycheproof-sha256",
Hkdf::<Sha256, HmacContext>
);
new_test!(wycheproof_sha384, "wycheproof-sha384", Hkdf::<Sha384>);
new_test!(wycheproof_sha512, "wycheproof-sha512", Hkdf::<Sha512>);