Skip to main content

Crate bouncycastle_hmac

Crate bouncycastle_hmac 

Source
Expand description

This crate contains an implementation of the Hash-Based Message Authentication Code (HMAC) as specified in RFC2104, taking into account NIST Implementation Guidance in FIPS 140-2 IG A.8 and NIST SP 800-107-r1.

§Usage

The HMAC object (and the MAC trait in general) is designed in three phases:

  • The initialization phase where you specify the underlying hash function and the key material.
  • The update phase where you feed in the content being MAC’d, either in one-shot or in chunks.
  • The finalization phase where you either obtain the MAC value or verify an existing MAC value.

The initialization phase is primarily performed via the MAC::new function which performs checks on the provided key to ensure that it is of the correct type KeyType::MACKey and tagged at the correct security level for the chosen hash function. In cases where you need to use HMAC with an intentially week key (such as an all-zero salt), the alternative constructor MAC::new_allow_weak_key can be used.

The update phase supports streaming of the content via the repeated calls to the MAC::do_update function. One-shot APIs are provided that combine the update and finalization phases into a single function call.

§Examples

HMAC objects can be constructed with any underlying hash function that implements Hash. Type aliases are provided for the common HMAC-HASH algorithms.

The following object instantiations are equivalent:

use hmac::HMAC_SHA256;
use core_interface::traits::MAC;
use core_interface::key_material::{KeyMaterial256, KeyType};

let key = KeyMaterial256::from_bytes_as_type(
            b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
            KeyType::MACKey).unwrap();

let hmac = HMAC_SHA256::new(&key).expect("Should succeed because key is long enough and tagged KeyType::MACKey");

and

use hmac::HMAC;
use sha2::SHA256;
use core_interface::traits::MAC;
use core_interface::key_material::{KeyMaterial256, KeyType};

let key = KeyMaterial256::from_bytes_as_type(
            b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
            KeyType::MACKey).unwrap();


let key = KeyMaterial256::from_bytes_as_type(
            b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
            KeyType::MACKey).unwrap();

let hmac = HMAC::<SHA256>::new(&key).expect("Should succeed because key is long enough and tagged KeyType::MACKey");

§Computing a MAC

MAC functionality is accessed via the MAC trait.

The simplest usage is via the one-shot functions.

use hmac::HMAC_SHA256;
use core_interface::traits::MAC;
use core_interface::key_material::{KeyMaterial256, KeyType};

let key = KeyMaterial256::from_bytes_as_type(
            b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
            KeyType::MACKey).unwrap();
let data: &[u8] = b"Hello, world!";
let hmac = HMAC_SHA256::new(&key).expect("Should succeed because key is long enough and tagged KeyType::MACKey");
let output: Vec<u8> = hmac.mac(data);

More advanced usage will require creating an HMAC object to hold state between successive calls, for example if input is received in chunks and not all available at the same time:

use core_interface::key_material::{KeyMaterial256, KeyType};
use core_interface::traits::MAC;
use hmac::HMAC_SHA256;

let key = KeyMaterial256::from_bytes_as_type(
            b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
            KeyType::MACKey).unwrap();
let mut hmac = HMAC_SHA256::new(&key).expect("Should succeed because key is long enough and tagged KeyType::MACKey");
hmac.do_update(b"Hello,");
hmac.do_update(b" world!");
let output: Vec<u8> = hmac.do_final();

§Verifying a MAC

MAC functionality is accessed via the MAC trait which provides functions for MAC verification. The built-in verification functions use constant-time comparisons and so are strongly recommended rather than re-computing the MAC value and comparing it yourself.

The simplest usage is via the one-shot functions.

use core_interface::key_material::{KeyMaterial256, KeyType};
use core_interface::traits::MAC;

let key = KeyMaterial256::from_bytes_as_type(
            b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
            KeyType::MACKey).unwrap();
let data: &[u8] = b"Hello, world!";

// .verify() returns a bool: true if the MAC is valid, false otherwise.
if hmac::HMAC_SHA256::new(&key).unwrap()
                .verify(data,
                        b"\xa2\xd1\x2e\xcf\xfc\x41\xba\xf1\x23\xd6\x3e\x44\xfc\x27\x88\x90\x47\xcd\x08\xe7\x05\xd7\x0f\xa3\xb8\xaa\x8a\x5c\x18\x7c\x6c\xa9"
                        )
{
    println!("MAC is valid!");
} else {
    println!("MAC is invalid!");
}

Similarly, a streaming version is available, which is identical to the streaming interface for computing a mac value, but calls MAC::do_verify_final instead of MAC::do_final.

use core_interface::key_material::{KeyMaterial256, KeyType};
use core_interface::traits::MAC;
use hmac::HMAC_SHA256;

let key = KeyMaterial256::from_bytes_as_type(
            b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
            KeyType::MACKey).unwrap();
let mut hmac = HMAC_SHA256::new(&key).unwrap();
hmac.do_update(b"Hello,");
hmac.do_update(b" world!");
if hmac.do_verify_final(b"\xa2\xd1\x2e\xcf\xfc\x41\xba\xf1\x23\xd6\x3e\x44\xfc\x27\x88\x90\x47\xcd\x08\xe7\x05\xd7\x0f\xa3\xb8\xaa\x8a\x5c\x18\x7c\x6c\xa9"
                    )
{
    println!("MAC is valid!");
} else {
    println!("MAC is invalid!");
}

§Request for feedback on fallability of this API

We have made an effort to reduce the number of fallibly APIs in the MAC trait; in particular the APIs for the update and most of the finalize phases are infallible – they just work. However, we were not able to design it to completely avoid runtime error conditions, such as providing MAC::do_final_out with a buffer that is too small to hold what NIST considered the smallest valid MAC value for the given underlying hash function.

Also, the key strength and type checking in the initialization phase means that both MAC::new and MAC::new_allow_weak_key have several runtime error conditions that they check for.

All of this leads to application code that requires a lot more .unwrap(), .expect(), or match than we would really like.

We would love feedback on an alternate design for this API than carries less runtime error conditions, without sacrificing the key strength checking that the metadata in the KeyMaterial object allows.

Structs§

HMAC

Constants§

HMAC_SHA3_224_NAME
HMAC_SHA3_256_NAME
HMAC_SHA3_384_NAME
HMAC_SHA3_512_NAME
HMAC_SHA224_NAME
HMAC_SHA256_NAME
HMAC_SHA384_NAME
HMAC_SHA512_NAME
MIN_FIPS_DIGEST_LEN

Type Aliases§

HMAC_SHA3_224
HMAC_SHA3_256
HMAC_SHA3_384
HMAC_SHA3_512
HMAC_SHA224
HMAC_SHA256
HMAC_SHA384
HMAC_SHA512