Crates.io | tps_minicbor |
lib.rs | tps_minicbor |
version | 0.5.1 |
source | src |
created_at | 2022-12-12 18:48:18.744318 |
updated_at | 2023-03-28 17:21:49.047616 |
description | A CBOR encoder and decoder suitable for `no_std` embedded targets. |
homepage | https://globalplatform.org |
repository | https://github.com/GlobalPlatform/TPS-API-Reference-Implementations |
max_upload_size | |
id | 735153 |
size | 343,769 |
An implementation of CBOR in Rust which is aimed at relatively constrained embedded systems where the Serde implementation is not necessarily well suited.
tps_minicbor
is MIT licensed. See LICENSE.
#[no_std]
support.You can run the test cases as follows:
cargo test --features=full
Note that a current limitation is that tests cannot be executed by cargo test alone as the featurization does not allow this.
Despite the small memory footprint, the CBOR serialization API is quite high-level, supporting arbitrary nesting of arrays and maps.
The example below is an implementation of Simple TEE Attestation from draft 14 of the Entity Attestation Token specification under development at the IETF.
In CBOR diagnostic format, this is displayed as:
{
/ nonce / 10: h'948f8860d13a463e',
/ UEID / 256: h'0198f50a4ff6c05861c8860d13a638ea',
/ OEMID / 258: 64242, / Private Enterprise Number /
/ security-level / 261: 3, / hardware level security /
/ secure-boot / 262: true,
/ debug-status / 263: 3, / disabled-permanently /
/ HW version / 260: [ "3.1", 1 ] / Type is multipartnumeric /
}
This is encoded in tps_minicbor as:
fn encode_tee_eat() -> Result<(), CBORError> {
// Encode-decode round trip test
println!("<========================== encode_tee_eat =========================>");
let mut bytes = [0u8; 1024];
let nonce: &[u8] = &[0x94, 0x8f, 0x88, 0x60, 0xd1, 0x3a, 0x46, 0x3e];
let ueid: &[u8] = &[
0x01, 0x98, 0xf5, 0x0a, 0x4f, 0xf6, 0xc0, 0x58, 0x61, 0xc8, 0x86, 0x0d, 0x13,
0xa6, 0x38, 0xea,
];
let mut encoded_cbor = CBORBuilder::new(&mut bytes);
encoded_cbor.insert(&map(|buff| {
buff.insert_key_value(&10, &nonce)?
.insert_key_value(&256, &ueid)?
.insert_key_value(&258, &64242)?
.insert_key_value(&261, &3)?
.insert_key_value(&262, &true)?
.insert_key_value(&263, &3)?
.insert_key_value(&260, &array(|buf| buf.insert(&"3.1")?.insert(&1)))
}))?;
// do_something_with(encoded_cbor.encoded()?);
Ok(())
}
The only work to do 'by hand' is turning the bstr
values into suitable references.
The example below shows one way to decode the payload generated above.
fn decode_tee_eat() -> Result<(), CBORError> {
let mut input: &[u8] = &[
167, 10, 72, 148, 143, 136, 96, 209, 58, 70, 62, 25, 1, 0, 80, 1, 152, 245,
10, 79, 246, 192, 88, 97, 200, 134, 13, 19, 166, 56, 234, 25, 1, 2, 25, 250,
242, 25, 1, 5, 3, 25, 1, 6, 245, 25, 1, 7, 3, 25, 1, 4, 130, 99, 51, 46,
49, 1,
];
let mut nonce = None;
let mut ueid = None;
let mut oemid = None;
let mut sec_level = None;
let mut sec_boot = None;
let mut debug_state = None;
let mut hw_ver_int = None;
let mut decoder = CBORDecoder::from_slice(&mut input);
decoder.decode_with(is_map(), |cbor| {
if let CBOR::Map(map) = cbor {
nonce = map.get_int(10);
ueid = map.get_int(256);
oemid = map.get_int(258);
sec_level = map.get_int(261);
sec_boot = map.get_int(262);
debug_state = map.get_int(263);
if let Some(CBOR::Array(ab)) = map.get_int(260) {
hw_ver_int = match ab.index(1) {
None => None,
Some(CBOR::UInt(vi)) => Some(vi.clone()),
_ => None
};
}
}
Ok(())
})?;
Ok(())
}
The decode
example is a very short sample of the use of the low-level decode API.
To run the example, from the top directory of the tps_minicbor
crate:
cargo run --example decode --features=full
The expected output is:
v1 = Ok(1000), v2 = Ok(1000), v3 = Ok(1000), v4 = Err(OutOfRange)
r1 = UInt(1000), e = Some(Eof)
Value: UInt(1000)
The trivial_cose
example is an implementation of the COSE_Sign1
single signer example in RFC9052
Appendix C.2.1. Keys, the message to be signed and other aspects of the cryptographic configuration
are hard-coded to the values in the Appendix.
While the example is called trivial_cose
as it implements only the very simplest COSE example, it does
stand as a good example of how to encode and decode moderately complex CBOR structures. All of the inputs
and outputs are bit-exact against the example, thanks to the use of deterministic ECDSA in the signature.
The code also serves as a simplistic example of how to do ECDSA using the Rust crypto traits - something for which there is a dearth of realistic examples.
Note: The p256 crate used for ECDSA has not been audited. Please see the warning on the p256 crate, and perform your own due diligence before use in production.
To run the example, from the top directory of the tps_minicbor
crate:
cargo run --example trivial_cose --features=full
The expected output is:
To be signed 846a5369676e61747572653143a101264054546869732069732074686520636f6e74656e742e
Signature 8eb33e4ca31d1c465ab05aac34cc6b23d58fef5c083106c4d25a91aef0b0117e2af9a291aa32e14ab834dc56ed2a223444547e01f11d3b0916e5a4c345cacb36
Output d28443a10126a10242313154546869732069732074686520636f6e74656e742e58408eb33e4ca31d1c465ab05aac34cc6b23d58fef5c083106c4d25a91aef0b0117e2af9a291aa32e14ab834dc56ed2a223444547e01f11d3b0916e5a4c345cacb36
18( [
h'a10126' ,
{
2 : h'3131' ,
}
,
h'546869732069732074686520636f6e74656e742e' ,
h'8eb33e4ca31d1c465ab05aac34cc6b23d58fef5c083106c4d25a91aef0b0117e2af9a291aa32e14ab834dc56ed2a223444547e01f11d3b0916e5a4c345cacb36' ,
],
)
To be verified 846a5369676e61747572653143a101264054546869732069732074686520636f6e74656e742e
Verification succeeded: message content [84, 104, 105, 115, 32, 105, 115, 32, 116, 104, 101, 32, 99, 111, 110, 116, 101, 110, 116, 46]