use corim::cbor;
use corim::cbor::value::Value;
use corim::types::common::*;
use corim::types::corim::*;
use corim::types::measurement::*;
use corim::types::signed::*;
use std::collections::BTreeMap;
#[test]
fn cose_algorithm_all_variants_from_to_i64() {
let cases: Vec<(i64, &str)> = vec![
(-7, "ES256 (deprecated)"),
(-8, "EdDSA (deprecated)"),
(-9, "ESP256"),
(-19, "Ed25519"),
(-35, "ES384 (deprecated)"),
(-36, "ES512 (deprecated)"),
(-37, "PS256"),
(-38, "PS384"),
(-39, "PS512"),
(-51, "ESP384"),
(-52, "ESP512"),
(-53, "Ed448"),
(-999, "Unknown"),
];
for (id, expected_name) in &cases {
let alg = CoseAlgorithm::from_i64(*id);
assert_eq!(alg.to_i64(), *id, "round-trip failed for {}", id);
assert_eq!(alg.name(), *expected_name, "name mismatch for {}", id);
}
}
#[test]
fn cose_algorithm_deprecated_flag() {
assert!(CoseAlgorithm::Es256.is_deprecated());
assert!(CoseAlgorithm::EdDsa.is_deprecated());
assert!(CoseAlgorithm::Es384.is_deprecated());
assert!(CoseAlgorithm::Es512.is_deprecated());
assert!(!CoseAlgorithm::Esp256.is_deprecated());
assert!(!CoseAlgorithm::Ed25519.is_deprecated());
assert!(!CoseAlgorithm::Ps256.is_deprecated());
assert!(!CoseAlgorithm::Ps384.is_deprecated());
assert!(!CoseAlgorithm::Ps512.is_deprecated());
assert!(!CoseAlgorithm::Esp384.is_deprecated());
assert!(!CoseAlgorithm::Esp512.is_deprecated());
assert!(!CoseAlgorithm::Ed448.is_deprecated());
assert!(!CoseAlgorithm::Unknown(42).is_deprecated());
}
#[test]
fn cose_algorithm_display_all_variants() {
for alg in [
CoseAlgorithm::Esp256,
CoseAlgorithm::Ed25519,
CoseAlgorithm::Ps256,
CoseAlgorithm::Ps384,
CoseAlgorithm::Ps512,
CoseAlgorithm::Esp384,
CoseAlgorithm::Esp512,
CoseAlgorithm::Ed448,
CoseAlgorithm::Es256,
CoseAlgorithm::EdDsa,
CoseAlgorithm::Es384,
CoseAlgorithm::Es512,
] {
let s = format!("{}", alg);
assert!(!s.is_empty());
assert!(s.contains('('));
}
let unknown = CoseAlgorithm::Unknown(-999);
assert_eq!(format!("{}", unknown), "Unknown(-999)");
}
#[test]
fn cose_algorithm_from_into_i64() {
let alg: CoseAlgorithm = (-7i64).into();
assert_eq!(alg, CoseAlgorithm::Es256);
let n: i64 = alg.into();
assert_eq!(n, -7);
}
#[test]
fn cose_x509_single_certs() {
let x = CoseX509::Single(vec![0x30, 0x82, 0x01]);
assert_eq!(x.certs().len(), 1);
assert_eq!(x.end_entity(), &[0x30, 0x82, 0x01]);
}
#[test]
fn cose_x509_chain_certs() {
let x = CoseX509::Chain(vec![vec![0x30, 0x01], vec![0x30, 0x02], vec![0x30, 0x03]]);
assert_eq!(x.certs().len(), 3);
assert_eq!(x.end_entity(), &[0x30, 0x01]);
}
fn make_meta() -> CorimMetaMap {
CorimMetaMap {
signer: CorimSignerMap {
signer_name: "ACME Ltd.".into(),
signer_uri: None,
},
signature_validity: None,
}
}
fn make_header_with_meta() -> ProtectedCorimHeaderMap {
ProtectedCorimHeaderMap {
alg: CoseAlgorithm::Es256,
content_type: Some("application/rim+cbor".into()),
payload_hash_alg: None,
payload_preimage_content_type: None,
payload_location: None,
corim_meta: Some(make_meta()),
cwt_claims: None,
kid: None,
x5bag: None,
x5chain: None,
x5t: None,
x5u: None,
extra: BTreeMap::new(),
}
}
#[test]
fn protected_header_with_x509_fields_round_trip() {
let mut header = make_header_with_meta();
header.kid = Some(vec![0x01, 0x02, 0x03]);
header.x5bag = Some(CoseX509::Single(vec![0x30, 0x82]));
header.x5chain = Some(CoseX509::Chain(vec![vec![0x30, 0x01], vec![0x30, 0x02]]));
header.x5t = Some(CoseCertHash {
hash_alg: corim::types::DigestAlg::Int(1),
hash_value: vec![0xAA; 32],
});
header.x5u = Some("https://example.com/cert.pem".into());
let bytes = cbor::encode(&header).unwrap();
let decoded: ProtectedCorimHeaderMap = cbor::decode(&bytes).unwrap();
assert_eq!(decoded.kid, header.kid);
assert!(decoded.x5bag.is_some());
assert!(decoded.x5chain.is_some());
assert!(decoded.x5t.is_some());
assert_eq!(decoded.x5u, header.x5u);
}
#[test]
fn protected_header_hash_envelope_round_trip() {
let header = ProtectedCorimHeaderMap {
alg: CoseAlgorithm::Es256,
content_type: None,
payload_hash_alg: Some(1),
payload_preimage_content_type: Some("application/rim+cbor".into()),
payload_location: Some("https://example.com/payload".into()),
corim_meta: Some(make_meta()),
cwt_claims: None,
kid: None,
x5bag: None,
x5chain: None,
x5t: None,
x5u: None,
extra: BTreeMap::new(),
};
let bytes = cbor::encode(&header).unwrap();
let decoded: ProtectedCorimHeaderMap = cbor::decode(&bytes).unwrap();
assert!(decoded.is_hash_envelope());
assert_eq!(decoded.payload_hash_alg, Some(1));
assert_eq!(
decoded.payload_preimage_content_type.as_deref(),
Some("application/rim+cbor")
);
assert_eq!(
decoded.payload_location.as_deref(),
Some("https://example.com/payload")
);
}
#[test]
fn encode_decode_signed_corim_attached() {
let header = make_header_with_meta();
let protected_bytes = cbor::encode(&header).unwrap();
let signed = CoseSign1Corim {
protected_header_bytes: protected_bytes,
protected: header,
unprotected: vec![],
payload: Some(vec![0xD9, 0x01, 0xF5, 0xA0]),
signature: vec![0xDE; 64],
};
let encoded = encode_signed_corim(&signed).unwrap();
let decoded = decode_signed_corim(&encoded).unwrap();
assert_eq!(decoded.protected.alg, CoseAlgorithm::Es256);
assert!(!decoded.is_detached());
assert_eq!(decoded.signature.len(), 64);
}
#[test]
fn encode_decode_signed_corim_detached() {
let header = make_header_with_meta();
let protected_bytes = cbor::encode(&header).unwrap();
let signed = CoseSign1Corim {
protected_header_bytes: protected_bytes,
protected: header,
unprotected: vec![],
payload: None,
signature: vec![0xAB; 64],
};
let encoded = encode_signed_corim(&signed).unwrap();
let decoded = decode_signed_corim(&encoded).unwrap();
assert!(decoded.is_detached());
}
#[test]
fn decode_signed_corim_not_tag18() {
let val = Value::Tag(
501,
Box::new(Value::Array(vec![
Value::Bytes(vec![0xA0]),
Value::Map(vec![]),
Value::Bytes(vec![]),
Value::Bytes(vec![]),
])),
);
let bytes = cbor::encode(&val).unwrap();
let err = decode_signed_corim(&bytes).unwrap_err();
assert!(format!("{}", err).contains("tag"), "got: {}", err);
}
#[test]
fn decode_signed_corim_not_a_tag() {
let val = Value::Array(vec![Value::Integer(1)]);
let bytes = cbor::encode(&val).unwrap();
let err = decode_signed_corim(&bytes).unwrap_err();
assert!(format!("{}", err).contains("tag 18"), "got: {}", err);
}
#[test]
fn decode_signed_corim_wrong_array_len() {
let val = Value::Tag(
18,
Box::new(Value::Array(vec![Value::Bytes(vec![]), Value::Map(vec![])])),
);
let bytes = cbor::encode(&val).unwrap();
let err = decode_signed_corim(&bytes).unwrap_err();
assert!(format!("{}", err).contains("4-element"), "got: {}", err);
}
#[test]
fn decode_signed_corim_not_array() {
let val = Value::Tag(18, Box::new(Value::Text("not-array".into())));
let bytes = cbor::encode(&val).unwrap();
let err = decode_signed_corim(&bytes).unwrap_err();
assert!(format!("{}", err).contains("array"), "got: {}", err);
}
#[test]
fn decode_signed_corim_protected_not_bstr() {
let val = Value::Tag(
18,
Box::new(Value::Array(vec![
Value::Text("not-bstr".into()),
Value::Map(vec![]),
Value::Null,
Value::Bytes(vec![]),
])),
);
let bytes = cbor::encode(&val).unwrap();
let err = decode_signed_corim(&bytes).unwrap_err();
assert!(format!("{}", err).contains("bstr"), "got: {}", err);
}
#[test]
fn cose_sign1_to_be_signed_attached() {
let header = make_header_with_meta();
let prot_bytes = cbor::encode(&header).unwrap();
let signed = CoseSign1Corim {
protected_header_bytes: prot_bytes,
protected: header,
unprotected: vec![],
payload: Some(vec![0x01, 0x02]),
signature: vec![],
};
let tbs = signed.to_be_signed(&[]).unwrap();
assert!(!tbs.is_empty());
let tbs_d = signed.to_be_signed_detached(&[0x03, 0x04], &[]).unwrap();
assert_ne!(tbs, tbs_d);
}
#[test]
fn cose_sign1_to_be_signed_detached_errors() {
let header = make_header_with_meta();
let prot_bytes = cbor::encode(&header).unwrap();
let signed = CoseSign1Corim {
protected_header_bytes: prot_bytes,
protected: header,
unprotected: vec![],
payload: None,
signature: vec![],
};
assert!(signed.to_be_signed(&[]).is_err());
}
fn make_corim_payload() -> Vec<u8> {
let comid = corim::builder::ComidBuilder::new(TagIdChoice::Text("t".into()))
.add_reference_triple(corim::types::triples::ReferenceTriple::new(
corim::types::environment::EnvironmentMap::for_class("V", "M"),
vec![MeasurementMap {
mkey: None,
mval: MeasurementValuesMap {
svn: Some(SvnChoice::ExactValue(1)),
..Default::default()
},
authorized_by: None,
}],
))
.build()
.unwrap();
corim::builder::CorimBuilder::new(CorimId::Text("test".into()))
.add_comid_tag(comid)
.unwrap()
.build_bytes()
.unwrap()
}
#[test]
fn signed_corim_builder_with_meta() {
let mut builder = SignedCorimBuilder::new(-7i64, make_corim_payload())
.set_corim_meta(make_meta())
.set_content_type("application/rim+cbor")
.add_unprotected(Value::Integer(99), Value::Text("info".into()))
.add_protected(100, Value::Bool(true));
let tbs = builder.to_be_signed(&[]).unwrap();
assert!(!tbs.is_empty());
let tbs2 = builder.to_be_signed(&[]).unwrap();
assert_eq!(tbs, tbs2);
let signed_bytes = builder.build_with_signature(vec![0xAB; 64]).unwrap();
let decoded = decode_signed_corim(&signed_bytes).unwrap();
assert!(!decoded.is_detached());
}
#[test]
fn signed_corim_builder_detached() {
let mut builder = SignedCorimBuilder::new(CoseAlgorithm::Es256, make_corim_payload())
.set_corim_meta(make_meta());
let _tbs = builder.to_be_signed(&[]).unwrap();
let signed_bytes = builder
.build_detached_with_signature(vec![0xCD; 64])
.unwrap();
let decoded = decode_signed_corim(&signed_bytes).unwrap();
assert!(decoded.is_detached());
}
#[test]
fn signed_corim_builder_cwt_claims() {
let mut builder = SignedCorimBuilder::new(CoseAlgorithm::Esp256, make_corim_payload())
.set_cwt_claims(CwtClaims::new("ACME Corp").with_exp(9999999999).with_nbf(0));
let _tbs = builder.to_be_signed(&[]).unwrap();
let signed_bytes = builder.build_with_signature(vec![0; 64]).unwrap();
let decoded = decode_signed_corim(&signed_bytes).unwrap();
let cwt = decoded.protected.cwt_claims.unwrap();
assert_eq!(cwt.iss, "ACME Corp");
assert_eq!(cwt.exp, Some(9999999999));
}
#[test]
fn signed_corim_builder_no_meta_no_cwt_fails() {
let mut builder = SignedCorimBuilder::new(-7i64, vec![0xA0]);
assert!(builder.to_be_signed(&[]).is_err());
}
#[test]
fn cwt_claims_builder_methods() {
let c = CwtClaims::new("iss")
.with_sub("sub")
.with_exp(9999)
.with_nbf(1000);
assert_eq!(c.iss, "iss");
assert_eq!(c.sub, Some("sub".into()));
assert_eq!(c.exp, Some(9999));
assert_eq!(c.nbf, Some(1000));
}
#[test]
fn validate_signed_corim_detached_payload_error() {
let header = make_header_with_meta();
let prot_bytes = cbor::encode(&header).unwrap();
let signed = CoseSign1Corim {
protected_header_bytes: prot_bytes,
protected: header,
unprotected: vec![],
payload: None,
signature: vec![0; 64],
};
assert!(validate_signed_corim_payload(&signed, 0).is_err());
}
#[test]
fn value_ser_unit_variant() {
#[derive(serde::Serialize)]
enum E {
A,
B,
}
assert_eq!(cbor::value::to_value(&E::A).unwrap(), Value::Integer(0));
assert_eq!(cbor::value::to_value(&E::B).unwrap(), Value::Integer(1));
}
#[test]
fn value_ser_newtype_variant() {
#[derive(serde::Serialize)]
enum E {
Wrap(u32),
}
assert_eq!(
cbor::value::to_value(&E::Wrap(42)).unwrap(),
Value::Integer(42)
);
}
#[test]
fn value_ser_tuple_variant() {
#[derive(serde::Serialize)]
enum E {
Pair(u32, String),
}
let v = cbor::value::to_value(&E::Pair(1, "hi".into())).unwrap();
assert!(matches!(v, Value::Array(ref a) if a.len() == 2));
}
#[test]
fn value_ser_struct_variant() {
#[derive(serde::Serialize)]
enum E {
Named { x: u32, y: String },
}
let v = cbor::value::to_value(&E::Named {
x: 1,
y: "hi".into(),
})
.unwrap();
assert!(matches!(v, Value::Map(ref e) if e.len() == 2));
}
#[test]
fn value_ser_newtype_struct() {
#[derive(serde::Serialize)]
struct W(u64);
assert_eq!(cbor::value::to_value(&W(123)).unwrap(), Value::Integer(123));
}
#[test]
fn value_ser_unit_struct() {
#[derive(serde::Serialize)]
struct U;
assert_eq!(cbor::value::to_value(&U).unwrap(), Value::Null);
}
#[test]
fn value_ser_tuple_struct() {
#[derive(serde::Serialize)]
struct P(u32, u32);
let v = cbor::value::to_value(&P(1, 2)).unwrap();
assert!(matches!(v, Value::Array(ref a) if a.len() == 2));
}
#[test]
fn value_ser_named_struct() {
#[derive(serde::Serialize)]
struct S {
name: String,
age: u32,
}
let v = cbor::value::to_value(&S {
name: "A".into(),
age: 1,
})
.unwrap();
assert!(matches!(v, Value::Map(ref e) if e.len() == 2));
}
#[test]
fn value_ser_char() {
assert_eq!(
cbor::value::to_value(&'X').unwrap(),
Value::Text("X".into())
);
}
#[test]
fn int_range_int_round_trip() {
let ir = IntRangeChoice::Int(42);
let bytes = cbor::encode(&ir).unwrap();
let decoded: IntRangeChoice = cbor::decode(&bytes).unwrap();
assert_eq!(decoded, IntRangeChoice::Int(42));
}
#[test]
fn integrity_registers_text_key_round_trip() {
let mut map = BTreeMap::new();
map.insert(
IntegrityRegisterId::Text("pcr-0".into()),
vec![Digest::new(1, vec![0xAA; 32])],
);
let regs = IntegrityRegisters(map);
let bytes = cbor::encode(®s).unwrap();
let decoded: IntegrityRegisters = cbor::decode(&bytes).unwrap();
assert!(decoded
.0
.contains_key(&IntegrityRegisterId::Text("pcr-0".into())));
}
#[test]
fn display_instance_long_bytes() {
let inst = InstanceIdChoice::Bytes(vec![0xFF; 32]);
let s = format!("{}", inst);
assert!(s.contains("..") && s.contains("32 bytes"), "got: {s}");
}
#[test]
fn display_crypto_key_long_thumbprint() {
let key = CryptoKey::KeyThumbprint(Digest::new(7, vec![0xCC; 48]));
let s = format!("{}", key);
assert!(s.contains(".."), "got: {s}");
}
#[test]
fn build_sig_structure1_basic() {
let tbs = build_sig_structure1(&[0xA0], &[], &[0x01, 0x02]).unwrap();
let val: Value = cbor::decode(&tbs).unwrap();
match val {
Value::Array(arr) => {
assert_eq!(arr.len(), 4);
assert_eq!(arr[0], Value::Text("Signature1".into()));
}
_ => panic!("expected array"),
}
}
#[test]
fn build_sig_structure1_with_aad() {
let tbs1 = build_sig_structure1(&[0xA0], &[], &[0x01]).unwrap();
let tbs2 = build_sig_structure1(&[0xA0], &[0xFF], &[0x01]).unwrap();
assert_ne!(tbs1, tbs2);
}
#[test]
fn corim_locator_multiple_href() {
let loc = CorimLocator {
href: CorimLocatorHref::Multiple(vec!["https://a.com".into(), "https://b.com".into()]),
thumbprint: None,
};
let bytes = cbor::encode(&loc).unwrap();
let decoded: CorimLocator = cbor::decode(&bytes).unwrap();
match decoded.href {
CorimLocatorHref::Multiple(uris) => assert_eq!(uris.len(), 2),
_ => panic!("expected multiple"),
}
}