Expand description

This crate implements a compact CBOR-encoded voucher defined by Constrained BRSKI.

Examples

In this section, we first introduce the Voucher abstraction offered by this crate, along with its API methods used when dealing with the BRSKI voucher attributes. We then present some practical examples on how to perfrom CBOR encoding/decoding of BRSKI vouchers with the underlying COSE signing and validation operations also considered.

1. Using the Voucher struct

The Voucher struct abstracts both “Voucher Request” and “Voucher” artifacts of Constrained BRSKI. Once a Voucher is instatiated, we can manage its attributes using the dedicated API methods (get, set, remove, etc.). These methods operate on the Attr enum (occasionally through its discriminant constants ATTR_*) that represents the BRSKI voucher attributes.

In this example, we demonstrate how to use the Voucher struct with a “voucher request” instance created by Voucher::new_vrq().

Notes

All of the Voucher struct’s methods shown below can also be called by a “voucher” instance created by Voucher::new_vch().

use minerva_voucher::{Voucher, attr::*};

// Create an empty voucher request.
let mut vrq = Voucher::new_vrq();

// Add some attributes.
vrq.set(Attr::Assertion(Assertion::Proximity))
    .set(Attr::CreatedOn(1599086034))
    .set(Attr::SerialNumber(b"00-D0-E5-F2-00-02".to_vec()));

// Count attributes.
assert_eq!(vrq.len(), 3);

// Check for specific ones.
assert_eq!(vrq.get(ATTR_CREATED_ON), Some(&Attr::CreatedOn(1599086034)));
assert_eq!(vrq.get(ATTR_EXPIRES_ON), None);

// Remove a specific one.
assert_eq!(vrq.remove(ATTR_CREATED_ON), true);

// Count attributes again.
assert_eq!(vrq.len(), 2);

// Iterate over everything.
for attr in vrq.iter() {
    println!("attr: {:?}", attr);
}

Using the vrq/vch declarative macros, a Voucher with a known list of attributes can be conveniently created as:

use minerva_voucher::{Voucher, attr::*, vrq, vch};

let v = vrq![
    Attr::Assertion(Assertion::Proximity),
    Attr::SerialNumber(b"00-11-22-33-44-55".to_vec())];

assert!(v.is_vrq());
assert_eq!(v.len(), 2);

let v = vch![
    Attr::Assertion(Assertion::Logged),
    Attr::SerialNumber(b"00-11-22-33-44-55".to_vec())];

assert!(v.is_vch());
assert_eq!(v.len(), 2);

2. Encoding a Voucher into CBOR

To encode a Voucher into a compact CBOR-encoded voucher, use Voucher::serialize.

In this example, we instantiate a new voucher request, populate it with some attributes, COSE-sign it, and finally encode it into a CBOR byte string.

use minerva_voucher::{Voucher, attr::*, SignatureAlgorithm, Sign};

static KEY_PEM_F2_00_02: &[u8] = core::include_bytes!(
    concat!(env!("CARGO_MANIFEST_DIR"), "/data/00-D0-E5-F2-00-02/key.pem"));

// This is required when the `Sign` trait is backed by mbedtls.
minerva_voucher::init_psa_crypto();

// Create a voucher request with five attributes and COSE-sign it.
let mut vrq = Voucher::new_vrq();
assert!(vrq
    .set(Attr::Assertion(Assertion::Proximity))
    .set(Attr::CreatedOn(1599086034))
    .set(Attr::Nonce(vec![48, 130, 1, 216, 48, 130, 1, 94, 160, 3, 2, 1, 2, 2, 1, 1, 48, 10, 6, 8, 42, 134, 72, 206, 61, 4, 3, 2, 48, 115, 49, 18, 48, 16, 6, 10, 9, 146, 38, 137, 147, 242, 44, 100, 1, 25, 22, 2, 99, 97, 49, 25, 48, 23, 6, 10, 9, 146, 38, 137, 147, 242, 44, 100, 1, 25, 22, 9, 115, 97, 110, 100, 101, 108, 109, 97, 110, 49, 66, 48, 64, 6, 3, 85, 4, 3, 12, 57, 35, 60, 83, 121, 115, 116, 101, 109, 86, 97, 114, 105, 97, 98, 108, 101, 58, 48, 120, 48, 48, 48, 48, 53, 53, 98, 56, 50, 53, 48, 99, 48, 100, 98, 56, 62, 32, 85, 110, 115, 116, 114, 117, 110, 103, 32, 70, 111, 117, 110, 116, 97, 105, 110, 32, 67, 65, 48, 30, 23, 13, 50, 48, 48, 56, 50, 57, 48, 52, 48, 48, 49, 54, 90, 23, 13, 50, 50, 48, 56, 50, 57, 48, 52, 48, 48, 49, 54, 90, 48, 70, 49, 18, 48, 16, 6, 10, 9, 146, 38, 137, 147, 242, 44, 100, 1, 25, 22, 2, 99, 97, 49, 25, 48, 23, 6, 10, 9, 146, 38, 137, 147, 242, 44, 100, 1, 25, 22, 9, 115, 97, 110, 100, 101, 108, 109, 97, 110, 49, 21, 48, 19, 6, 3, 85, 4, 3, 12, 12, 85, 110, 115, 116, 114, 117, 110, 103, 32, 74, 82, 67, 48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 150, 101, 80, 114, 52, 186, 159, 229, 221, 230, 95, 246, 240, 129, 111, 233, 72, 158, 129, 12, 18, 7, 59, 70, 143, 151, 100, 43, 99, 0, 141, 2, 15, 87, 201, 124, 148, 127, 132, 140, 178, 14, 97, 214, 201, 136, 141, 21, 180, 66, 31, 215, 242, 106, 183, 228, 206, 5, 248, 167, 76, 211, 139, 58, 163, 16, 48, 14, 48, 12, 6, 3, 85, 29, 19, 1, 1, 255, 4, 2, 48, 0, 48, 10, 6, 8, 42, 134, 72, 206, 61, 4, 3, 2, 3, 104, 0, 48, 101, 2, 49, 0, 135, 158, 205, 227, 138, 5, 18, 46, 182, 247, 44, 178, 27, 195, 210, 92, 190, 230, 87, 55, 112, 86, 156, 236, 35, 12, 164, 140, 57, 241, 64, 77, 114, 212, 215, 85, 5, 155, 128, 130, 2, 14, 212, 29, 79, 17, 159, 231, 2, 48, 60, 20, 216, 138, 10, 252, 64, 71, 207, 31, 135, 184, 115, 193, 106, 40, 191, 184, 60, 15, 136, 67, 77, 157, 243, 247, 168, 110, 45, 198, 189, 136, 149, 68, 47, 32, 55, 237, 204, 228, 133, 91, 17, 218, 154, 25, 228, 232]))
    .set(Attr::ProximityRegistrarCert(vec![102, 114, 118, 85, 105, 90, 104, 89, 56, 80, 110, 86, 108, 82, 75, 67, 73, 83, 51, 113, 77, 81]))
    .set(Attr::SerialNumber(b"00-D0-E5-F2-00-02".to_vec()))
    .sign(KEY_PEM_F2_00_02, SignatureAlgorithm::ES256)
    .is_ok());

// Encode the voucher request.
let cbor = vrq.serialize().unwrap();

assert_eq!(cbor.len(), 630);

3. Decoding a CBOR-encoded voucher into a Voucher

To decode a COSE-signed CBOR-encoded voucher, use the TryFrom<&u8> trait implemented for the Voucher struct.

In this example, we decode a “voucher” sample in the 00-D0-E5-F2-00-02 constrained voucher directory into a Voucher instance, COSE-validate it, and iterate through each attribute in the voucher.

use minerva_voucher::{Voucher, attr::*, Validate};
use core::convert::TryFrom;

static VCH_F2_00_02: &[u8] = core::include_bytes!(
    concat!(env!("CARGO_MANIFEST_DIR"), "/data/00-D0-E5-F2-00-02/voucher_00-D0-E5-F2-00-02.vch"));
static MASA_CRT_F2_00_02: &[u8] = core::include_bytes!(
    concat!(env!("CARGO_MANIFEST_DIR"), "/data/00-D0-E5-F2-00-02/masa.crt"));

// This is required when the `Validate` trait is backed by mbedtls.
minerva_voucher::init_psa_crypto();

// Decode the voucher.
let vch = Voucher::try_from(VCH_F2_00_02).unwrap();

// COSE-validate the voucher.
assert!(vch.validate(Some(MASA_CRT_F2_00_02)).is_ok());

// This voucher has five attributes.
assert_eq!(vch.len(), 5);

for attr in vch.iter() {
    println!("attr: {:?}", attr);

    // Check data belonging to the attribute.
    match attr {
        Attr::Assertion(x) => assert_eq!(x, &Assertion::Logged),
        Attr::CreatedOn(x) => assert_eq!(x, &1599525239),
        Attr::Nonce(x) => assert_eq!(x, &[88, 83, 121, 70, 52, 76, 76, 73, 105, 113, 85, 50, 45, 79, 71, 107, 54, 108, 70, 67, 65, 103]),
        Attr::PinnedDomainCert(x) => assert_eq!(x[0..4], [77, 73, 73, 66]),
        Attr::SerialNumber(x) => assert_eq!(x, b"00-D0-E5-F2-00-02"),
        _ => panic!(),
    }
}
Notes

Instead of TryFrom, we could use TryInto (via use core::convert::TryInto;) to decode the same voucher as

let vch: Voucher = VCH_F2_00_02.try_into().unwrap();

In this case, the type annotation : Voucher is needed.

Modules

Enums and constants for voucher attributes.

Macros

Creates a “Voucher” instance with a known list of attributes.

Creates a “Voucher Request” instance with a known list of attributes.

Structs

A structure implementing both “Voucher Request” and “Voucher” artifacts of Constrained BRSKI.

Enums

An enum identifying supported signature algorithms. Currently ES256 (ECDSA with P256 and SHA256), ES384 (ECDSA with P384 and SHA384) ES512 (ECDSA with P521 and SHA512), and PS256 (RSASSA-PSS with SHA256) are supported. Note that with PS256, the salt length is defined to be 32 bytes.

Errors that can be returned from Voucher functions.

Traits

Used to COSE-sign a Voucher.

Used to COSE-validate a Voucher.

Functions

Initializes the PSA cryptography API context. Call this function when using the Sign/Validate trait backed by mbedtls.