wolfcose 0.1.0

Safe Rust API for wolfSSL wolfCOSE.
#![allow(missing_docs)]

use core::ptr::NonNull;
use wolfcose::{
    sign1_into, sign1_to_vec, sign_into, verify1, verify_sign, Algorithm, CoseKey, CoseKeyBuilder,
    Encrypt0Builder, EncryptBuilder, Error, Mac0Builder, MacBuilder, PayloadMode, Recipient,
    Sign1Builder, SignBuilder, Signature,
};

const PAYLOAD: &[u8] = b"secured drone payload";
const AAD: &[u8] = b"drone-protocol/v1:test";
const IV: [u8; 12] = *b"unique-iv-01";

fn aes_key() -> CoseKey {
    CoseKeyBuilder::symmetric([0x11; 16])
        .algorithm(Algorithm::A128GCM)
        .kid(b"aes")
        .build()
        .unwrap()
}

fn mac_key() -> CoseKey {
    CoseKeyBuilder::symmetric([0x22; 32])
        .algorithm(Algorithm::HMAC256)
        .kid(b"mac")
        .build()
        .unwrap()
}

#[test]
fn builders_report_missing_required_inputs() {
    let key = aes_key();
    let mut out = [0; 256];
    assert_eq!(
        Encrypt0Builder::default().encrypt_into(&mut out),
        Err(Error::InvalidArgument)
    );
    assert_eq!(
        Encrypt0Builder::new().encrypt_into(&mut out),
        Err(Error::InvalidArgument)
    );
    assert_eq!(
        Encrypt0Builder::new()
            .key(&key)
            .algorithm(Algorithm::A128GCM)
            .iv(&IV)
            .payload(PayloadMode::Detached(PAYLOAD))
            .encrypt_into(&mut out),
        Err(Error::InvalidArgument)
    );

    let mac_key = mac_key();
    assert_eq!(
        Mac0Builder::default().mac_into(&mut out),
        Err(Error::InvalidArgument)
    );
    assert_eq!(
        Mac0Builder::new().mac_into(&mut out),
        Err(Error::InvalidArgument)
    );
    assert_eq!(
        MacBuilder::default().mac_into(&mut out),
        Err(Error::InvalidArgument)
    );
    assert_eq!(
        MacBuilder::new().mac_into(&mut out),
        Err(Error::InvalidArgument)
    );
    assert_eq!(
        EncryptBuilder::new()
            .recipient(Recipient::new(Algorithm::DIRECT, &key))
            .algorithm(Algorithm::A128GCM)
            .iv(&IV)
            .payload(PayloadMode::Detached(PAYLOAD))
            .encrypt_to_vec(),
        Err(Error::InvalidArgument)
    );
    assert_eq!(
        Mac0Builder::new()
            .key(&mac_key)
            .algorithm(Algorithm::HMAC256)
            .verify(b"not-a-cose-message", None),
        Err(Error::CborType)
    );

    let mut sign1_builder = Sign1Builder::default()
        .key(&key)
        .algorithm(Algorithm::ES256)
        .kid(b"sig")
        .external_aad(AAD)
        .scratch_len(1024)
        .rng(NonNull::dangling())
        .payload(PayloadMode::Attached(PAYLOAD));
    assert!(sign1_builder.sign_into(&mut out).is_err());
    assert!(sign1_builder.sign_to_vec().is_err());

    assert_eq!(
        Sign1Builder::new().sign_into(&mut out),
        Err(Error::InvalidArgument)
    );
    assert_eq!(
        Sign1Builder::new()
            .key(&key)
            .algorithm(Algorithm::ES256)
            .payload(PayloadMode::Attached(PAYLOAD))
            .verify(b"not-a-cose-message", None),
        Err(Error::CborType)
    );
    assert_eq!(
        SignBuilder::default().sign_into(&mut out),
        Err(Error::InvalidArgument)
    );
    assert_eq!(
        SignBuilder::new().sign_into(&mut out),
        Err(Error::InvalidArgument)
    );
    assert_eq!(
        SignBuilder::new().verify(&key, 0, b"not-a-cose-message", None),
        Err(Error::CborType)
    );

    let mut scratch = [0; 1024];
    assert!(sign1_into(
        &key,
        Algorithm::ES256,
        Some(b"sig"),
        PayloadMode::Attached(PAYLOAD),
        AAD,
        &mut scratch,
        &mut out,
        None
    )
    .is_err());
    assert!(sign1_to_vec(
        &key,
        Algorithm::ES256,
        Some(b"sig"),
        PayloadMode::Detached(PAYLOAD),
        AAD,
        &mut scratch,
        None
    )
    .is_err());
    let signer = Signature::new(Algorithm::ES256, &key).with_kid(b"sig");
    let mut sign_builder = SignBuilder::new()
        .signer(Signature::new(Algorithm::ES256, &key).with_kid(b"sig"))
        .external_aad(AAD)
        .rng(NonNull::dangling())
        .payload(PayloadMode::Attached(PAYLOAD));
    assert!(sign_builder.sign_into(&mut out).is_err());
    assert!(sign_builder.sign_to_vec().is_err());
    assert!(sign_into(
        &[signer],
        PayloadMode::Attached(PAYLOAD),
        AAD,
        &mut scratch,
        &mut out,
        None
    )
    .is_err());
    assert_eq!(
        verify1(&key, b"not-a-cose-message", None, AAD, &mut scratch),
        Err(Error::CborType)
    );
    assert_eq!(
        verify_sign(&key, 0, b"not-a-cose-message", None, AAD, &mut scratch),
        Err(Error::CborType)
    );

    let mut detached_encrypt = EncryptBuilder::default()
        .recipient(Recipient::new(Algorithm::DIRECT, &key))
        .algorithm(Algorithm::A128GCM)
        .iv(&IV)
        .external_aad(AAD)
        .rng(NonNull::dangling())
        .payload(PayloadMode::Detached(PAYLOAD));
    match detached_encrypt.encrypt_into(&mut out) {
        Ok(message) => assert!(!message.is_empty()),
        Err(Error::Unsupported) => {}
        Err(error) => panic!("unexpected detached encrypt builder error: {error:?}"),
    }
    assert_eq!(
        detached_encrypt.encrypt_to_vec(),
        Err(Error::InvalidArgument)
    );

    assert_eq!(
        Encrypt0Builder::new()
            .key(&key)
            .algorithm(Algorithm::A128GCM)
            .iv(&IV)
            .payload(PayloadMode::Attached(PAYLOAD))
            .encrypt_detached_into(&mut out, &mut [0; 128]),
        Err(Error::InvalidArgument)
    );

    let mut mac_builder = MacBuilder::default()
        .recipient(Recipient::new(Algorithm::DIRECT, &mac_key))
        .algorithm(Algorithm::HMAC256)
        .external_aad(AAD)
        .payload(PayloadMode::Detached(PAYLOAD));
    let detached_mac = mac_builder.mac_to_vec().unwrap();
    assert_eq!(
        mac_builder
            .verify(
                &Recipient::new(Algorithm::DIRECT, &mac_key),
                0,
                &detached_mac,
                Some(PAYLOAD)
            )
            .unwrap()
            .payload,
        None
    );
}