use crate::{constant_time, digest, error, rand};
#[derive(Clone, Copy, Debug)]
pub struct Signature(digest::Digest);
pub struct SigningKey {
ctx_prototype: SigningContext,
}
derive_debug_via_self!(SigningKey, self.ctx_prototype.inner.algorithm());
impl AsRef<[u8]> for Signature {
#[inline]
fn as_ref(&self) -> &[u8] { self.0.as_ref() }
}
impl SigningKey {
pub fn generate(
digest_alg: &'static digest::Algorithm, rng: &rand::SecureRandom,
) -> Result<SigningKey, error::Unspecified> {
let mut key_bytes = [0u8; digest::MAX_OUTPUT_LEN];
let key_bytes = &mut key_bytes[..recommended_key_len(digest_alg)];
Self::generate_serializable(digest_alg, rng, key_bytes)
}
pub fn generate_serializable(
digest_alg: &'static digest::Algorithm, rng: &rand::SecureRandom, key_bytes: &mut [u8],
) -> Result<SigningKey, error::Unspecified> {
if key_bytes.len() != recommended_key_len(digest_alg) {
return Err(error::Unspecified);
}
rng.fill(key_bytes)?;
Ok(SigningKey::new(digest_alg, key_bytes))
}
pub fn new(digest_alg: &'static digest::Algorithm, key_value: &[u8]) -> SigningKey {
let mut key = SigningKey {
ctx_prototype: SigningContext {
inner: digest::Context::new(digest_alg),
outer: digest::Context::new(digest_alg),
},
};
let key_hash;
let key_value = if key_value.len() <= digest_alg.block_len {
key_value
} else {
key_hash = digest::digest(digest_alg, key_value);
key_hash.as_ref()
};
const IPAD: u8 = 0x36;
const OPAD: u8 = 0x5C;
for b in key_value {
key.ctx_prototype.inner.update(&[IPAD ^ b]);
key.ctx_prototype.outer.update(&[OPAD ^ b]);
}
for _ in key_value.len()..digest_alg.block_len {
key.ctx_prototype.inner.update(&[IPAD]);
key.ctx_prototype.outer.update(&[OPAD]);
}
key
}
pub fn digest_algorithm(&self) -> &'static digest::Algorithm {
self.ctx_prototype.inner.algorithm()
}
}
#[derive(Clone)]
pub struct SigningContext {
inner: digest::Context,
outer: digest::Context,
}
derive_debug_via_self!(SigningContext, self.inner.algorithm());
impl SigningContext {
pub fn with_key(signing_key: &SigningKey) -> SigningContext {
SigningContext {
inner: signing_key.ctx_prototype.inner.clone(),
outer: signing_key.ctx_prototype.outer.clone(),
}
}
pub fn update(&mut self, data: &[u8]) { self.inner.update(data); }
pub fn sign(mut self) -> Signature {
self.outer.update(self.inner.finish().as_ref());
Signature(self.outer.finish())
}
}
pub fn sign(key: &SigningKey, data: &[u8]) -> Signature {
let mut ctx = SigningContext::with_key(key);
ctx.update(data);
ctx.sign()
}
pub struct VerificationKey {
wrapped: SigningKey,
}
impl VerificationKey {
#[inline(always)]
pub fn new(digest_alg: &'static digest::Algorithm, key_value: &[u8]) -> VerificationKey {
VerificationKey {
wrapped: SigningKey::new(digest_alg, key_value),
}
}
#[inline]
pub fn digest_algorithm(&self) -> &'static digest::Algorithm { self.wrapped.digest_algorithm() }
}
#[inline(always)]
pub fn verify(
key: &VerificationKey, data: &[u8], signature: &[u8],
) -> Result<(), error::Unspecified> {
verify_with_own_key(&key.wrapped, data, signature)
}
pub fn verify_with_own_key(
key: &SigningKey, data: &[u8], signature: &[u8],
) -> Result<(), error::Unspecified> {
constant_time::verify_slices_are_equal(sign(key, data).as_ref(), signature)
}
#[inline]
pub fn recommended_key_len(digest_alg: &digest::Algorithm) -> usize { digest_alg.chaining_len }
#[cfg(test)]
mod tests {
use crate::{digest, hmac, rand, test};
#[test]
pub fn hmac_signing_key_coverage() {
let mut rng = rand::SystemRandom::new();
const HELLO_WORLD_GOOD: &[u8] = b"hello, world";
const HELLO_WORLD_BAD: &[u8] = b"hello, worle";
for d in &digest::test_util::ALL_ALGORITHMS {
{
let key = hmac::SigningKey::generate(d, &mut rng).unwrap();
let signature = hmac::sign(&key, HELLO_WORLD_GOOD);
assert!(
hmac::verify_with_own_key(&key, HELLO_WORLD_GOOD, signature.as_ref()).is_ok()
);
assert!(
hmac::verify_with_own_key(&key, HELLO_WORLD_BAD, signature.as_ref()).is_err()
)
}
{
let mut key_bytes = vec![0; d.chaining_len];
let key =
hmac::SigningKey::generate_serializable(d, &mut rng, &mut key_bytes).unwrap();
let signature = hmac::sign(&key, HELLO_WORLD_GOOD);
assert!(
hmac::verify_with_own_key(&key, HELLO_WORLD_GOOD, signature.as_ref()).is_ok()
);
assert!(
hmac::verify_with_own_key(&key, HELLO_WORLD_BAD, signature.as_ref()).is_err()
)
}
if d.chaining_len != d.output_len {
let mut key_bytes = vec![0; d.output_len];
assert!(
hmac::SigningKey::generate_serializable(d, &mut rng, &mut key_bytes).is_err()
);
}
{
let mut key_bytes = vec![0; d.chaining_len - 1];
assert!(
hmac::SigningKey::generate_serializable(d, &mut rng, &mut key_bytes).is_err()
);
}
{
let mut key_bytes = vec![0; d.chaining_len + 1];
assert!(
hmac::SigningKey::generate_serializable(d, &mut rng, &mut key_bytes).is_err()
);
}
}
}
#[test]
pub fn generate_serializable_tests() {
test::from_file(
"src/hmac_generate_serializable_tests.txt",
|section, test_case| {
assert_eq!(section, "");
let digest_alg = test_case.consume_digest_alg("HMAC").unwrap();
let key_value_in = test_case.consume_bytes("Key");
let rng = test::rand::FixedSliceRandom {
bytes: &key_value_in,
};
let mut key_value_out = vec![0; digest_alg.chaining_len];
let _ =
hmac::SigningKey::generate_serializable(digest_alg, &rng, &mut key_value_out)
.unwrap();
assert_eq!(&key_value_in, &key_value_out);
Ok(())
},
)
}
}