#codec #cbor #encoder #deserialize #serialization #decoder #array

无需std tps_minicbor

适用于no_std嵌入式目标的CBOR编码器和解码器

3个不稳定版本

0.5.1 2023年3月28日
0.4.3 2022年12月19日
0.4.2 2022年12月19日
0.4.1 2022年12月12日

#1515 in 编码

每月下载量

MIT 许可证

225KB
3.5K SLoC

TPS-MINICBOR

一个针对相对受限的嵌入式系统设计的CBOR实现,其中Serde实现可能并不完全适合。

许可证

tps_minicbor是MIT许可。请参阅LICENSE。

特性

  • 专为需要#[no_std]支持的受限嵌入式环境设计。
  • 高级编码和解码API使得序列化和反序列化流程更容易正确编写。
  • 支持大多数CBOR结构(见限制)
    • 所有原始类型(正负整数,tstr,bstr,简单类型,标签,浮点数(包括16位浮点数)。
  • 支持数组和映射的任意嵌套,并自动计算正确的项目数。
  • 支持标准标签的子集(日期/时间和Unix纪元)。请注意,这些需要可分配器可用。
  • 支持与Rust原始类型的转换。
  • 自动为整数和浮点数提供首选序列化。
  • 在反序列化时支持数组和映射的迭代器和索引。
  • 广泛的测试用例,包括RFC8949中所有支持的特性的测试用例。
    • 请注意,浮点数+Infinity,NaN和-Infinity总是序列化为f16格式,因为这是最喜欢的表示。反序列化适用于所有情况。
  • 支持非首选表示的反序列化。

当前限制

  • 不支持规范CBOR
  • 不支持数组和映射的首选序列化
  • 不支持不定长编码
  • 不直接支持大数,十进制分数或大浮点数

测试

您可以通过以下方式运行测试用例

cargotest --features=full

请注意,当前的一个限制是,不能仅通过cargo test单独执行测试,因为特性化不允许这样做。

API的示例

CBOR编码

尽管内存占用小,但CBOR序列化API相当高级,支持数组和映射的任意嵌套。

以下示例是实现IETF正在开发中的实体证明令牌规范草案14中的简单TEE证明

在CBOR诊断格式中,这表示为

{
    / 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 /
}

这以tps_minicbor编码为

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(())
}

唯一需要手动操作的工作是将bstr值转换为合适的引用。

CBOR解码

以下示例展示了解码上述生成的有效载荷的一种方法。

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(())
}

示例

解码

decode示例是低级解码API使用的一个非常短的样本。

要运行示例,从tps_minicbor的顶层目录开始

cargo run --example decode --features=full

预期的输出是

v1 = Ok(1000), v2 = Ok(1000), v3 = Ok(1000), v4 = Err(OutOfRange)
r1 = UInt(1000), e = Some(Eof)
Value: UInt(1000)

trivial_cose

trivial_cose示例是实现RFC9052附录C.2.1中的COSE_Sign1单签名示例的实现。密钥、待签消息和加密配置的其他方面均硬编码到附录中的值。

虽然这个示例被称为trivial_cose,因为它只实现了最简单的COSE示例,但它确实是一个如何编码和解码中等复杂CBOR结构的良好示例。所有输入和输出都与示例精确对应,这归功于签名中使用的确定性ECDSA。

该代码还作为使用Rust加密特性执行ECDSA的简单示例,这是现实中示例很少的情况。

注意:用于ECDSA的p256 crate尚未经过审计。请参阅p256 crate中的警告,并在生产中使用之前自行进行尽职调查。

要运行示例,从tps_minicbor的顶层目录开始

cargo run --example trivial_cose --features=full

预期的输出是

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]

依赖项

~3.5MB
~77K SLoC