wolfcose 0.1.0

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

use wolfcose::{
    from_slice, to_vec, Algorithm, ByteStr, CborSerialize, CborSerializer, CborValue,
    CoseEncrypt0Message, CoseEncryptMessage, CoseMac0Message, CoseMacMessage, CoseMessageKind,
    CoseSign1Message, CoseSignMessage, Error, HeaderLabel, HeaderMap, HeaderValue, ProtectedHeader,
};

#[test]
fn parses_all_cose_message_envelope_shapes() {
    let sign1 = envelope(CoseMessageKind::Sign1, false, 0, true);
    let sign1 = CoseSign1Message::parse(&sign1).unwrap();
    assert!(sign1.payload_attached());
    assert_eq!(sign1.raw().len(), sign1.raw().len());

    let sign = envelope(CoseMessageKind::Sign, true, 2, true);
    let sign = CoseSignMessage::parse(&sign).unwrap();
    assert!(!sign.payload_attached());
    assert_eq!(sign.signer_count(), 2);
    assert_eq!(sign.protected().algorithm(), Some(Algorithm::HMAC256));

    let encrypt0 = envelope(CoseMessageKind::Encrypt0, true, 0, true);
    assert_eq!(
        CoseEncrypt0Message::parse(&encrypt0),
        Err(Error::CborMalformed)
    );

    let encrypt = envelope(CoseMessageKind::Encrypt, false, 2, true);
    let encrypt = CoseEncryptMessage::parse(&encrypt).unwrap();
    assert!(encrypt.ciphertext_attached());
    assert_eq!(encrypt.recipient_count(), 2);

    let mac0 = envelope(CoseMessageKind::Mac0, false, 0, false);
    let mac0 = CoseMac0Message::parse(&mac0).unwrap();
    assert!(mac0.payload_attached());

    let mac = envelope(CoseMessageKind::Mac, true, 2, false);
    let mac = CoseMacMessage::parse(&mac).unwrap();
    assert!(!mac.payload_attached());
    assert_eq!(mac.recipient_count(), 2);

    let bad_tag = envelope(CoseMessageKind::Sign1, false, 0, true);
    assert_eq!(CoseMac0Message::parse(&bad_tag), Err(Error::CoseBadTag));

    let mut trailing = envelope(CoseMessageKind::Sign1, false, 0, true);
    trailing.push(0);
    assert_eq!(
        CoseSign1Message::parse(&trailing),
        Err(Error::CborMalformed)
    );
}

#[test]
fn typed_headers_preserve_known_and_unknown_values() {
    assert_eq!(HeaderLabel::from_i64(1), HeaderLabel::Algorithm);
    assert_eq!(HeaderLabel::from_i64(2), HeaderLabel::Critical);
    assert_eq!(HeaderLabel::from_i64(3), HeaderLabel::ContentType);
    assert_eq!(HeaderLabel::from_i64(4), HeaderLabel::Kid);
    assert_eq!(HeaderLabel::from_i64(5), HeaderLabel::Iv);
    assert_eq!(HeaderLabel::from_i64(6), HeaderLabel::PartialIv);
    assert_eq!(HeaderLabel::from_i64(-1), HeaderLabel::EphemeralKey);
    assert_eq!(HeaderLabel::Text("x".to_owned()).as_i64(), None);

    let mut headers = HeaderMap::new();
    assert!(headers.is_empty());
    headers.insert(
        HeaderLabel::Algorithm,
        HeaderValue::Int(Algorithm::HMAC256.id() as i64),
    );
    headers.insert(HeaderLabel::Kid, HeaderValue::Bytes(b"kid".to_vec()));
    headers.insert(
        HeaderLabel::Critical,
        HeaderValue::Array(vec![CborValue::Unsigned(1)]),
    );
    headers.insert(
        HeaderLabel::Text("raw".to_owned()),
        HeaderValue::Raw(CborValue::Null),
    );
    headers.insert(
        HeaderLabel::ContentType,
        HeaderValue::Text("text/plain".to_owned()),
    );
    headers.insert(HeaderLabel::Iv, HeaderValue::Bytes(vec![1, 2, 3]));
    headers.insert(HeaderLabel::PartialIv, HeaderValue::Bytes(vec![4, 5]));
    headers.insert(
        HeaderLabel::EphemeralKey,
        HeaderValue::Raw(CborValue::Map(vec![])),
    );
    headers.insert(HeaderLabel::Int(99), HeaderValue::Int(7));
    headers.insert(HeaderLabel::Kid, HeaderValue::Bytes(b"replaced".to_vec()));
    assert_eq!(headers.len(), 9);
    assert_eq!(headers.kid(), Some(&b"replaced"[..]));
    assert_eq!(headers.algorithm(), Some(Algorithm::HMAC256));
    assert_eq!(headers.iter().count(), 9);

    let decoded: HeaderMap = from_slice(&to_vec(&headers).unwrap()).unwrap();
    assert_eq!(decoded.algorithm(), Some(Algorithm::HMAC256));
    assert_eq!(decoded.kid(), Some(&b"replaced"[..]));

    assert_eq!(ProtectedHeader::from_bstr(&[]).unwrap().0.len(), 0);
    let err = from_slice::<HeaderLabel>(&to_vec(&()).unwrap()).unwrap_err();
    assert_eq!(err, Error::CborType);
}

fn envelope(
    kind: CoseMessageKind,
    detached_body: bool,
    trailing_count: usize,
    include_tag: bool,
) -> Vec<u8> {
    let mut protected = HeaderMap::new();
    protected.insert(
        HeaderLabel::Algorithm,
        HeaderValue::Algorithm(Algorithm::HMAC256),
    );
    let protected = to_vec(&protected).unwrap();

    let mut unprotected = HeaderMap::new();
    unprotected.insert(HeaderLabel::Kid, HeaderValue::Bytes(b"kid".to_vec()));

    let mut out = [0; 512];
    let mut serializer = CborSerializer::new(&mut out);
    if include_tag {
        serializer.tag(tag_for(kind)).unwrap();
    }
    serializer.array(array_len_for(kind)).unwrap();
    ByteStr(&protected).serialize(&mut serializer).unwrap();
    unprotected.serialize(&mut serializer).unwrap();
    if detached_body {
        Option::<u8>::None.serialize(&mut serializer).unwrap();
    } else {
        ByteStr(b"body").serialize(&mut serializer).unwrap();
    }
    match kind {
        CoseMessageKind::Sign1 => ByteStr(b"signature").serialize(&mut serializer).unwrap(),
        CoseMessageKind::Encrypt0 => {}
        CoseMessageKind::Mac0 => ByteStr(b"tag").serialize(&mut serializer).unwrap(),
        CoseMessageKind::Sign | CoseMessageKind::Encrypt => {
            serializer.array(trailing_count).unwrap();
            for _ in 0..trailing_count {
                serializer.array(0).unwrap();
            }
        }
        CoseMessageKind::Mac => {
            ByteStr(b"tag").serialize(&mut serializer).unwrap();
            serializer.array(trailing_count).unwrap();
            for _ in 0..trailing_count {
                serializer.array(0).unwrap();
            }
        }
    }
    serializer.as_written().to_vec()
}

fn tag_for(kind: CoseMessageKind) -> u64 {
    match kind {
        CoseMessageKind::Sign1 => wolfcose::raw::WOLFCOSE_TAG_SIGN1 as u64,
        CoseMessageKind::Sign => wolfcose::raw::WOLFCOSE_TAG_SIGN as u64,
        CoseMessageKind::Encrypt0 => wolfcose::raw::WOLFCOSE_TAG_ENCRYPT0 as u64,
        CoseMessageKind::Encrypt => wolfcose::raw::WOLFCOSE_TAG_ENCRYPT as u64,
        CoseMessageKind::Mac0 => wolfcose::raw::WOLFCOSE_TAG_MAC0 as u64,
        CoseMessageKind::Mac => wolfcose::raw::WOLFCOSE_TAG_MAC as u64,
    }
}

fn array_len_for(kind: CoseMessageKind) -> usize {
    match kind {
        CoseMessageKind::Encrypt0 => 3,
        CoseMessageKind::Mac => 5,
        _ => 4,
    }
}