| Crates.io | c2pa_cbor |
| lib.rs | c2pa_cbor |
| version | 0.77.0 |
| created_at | 2026-01-15 18:41:38.046734+00 |
| updated_at | 2026-01-22 03:14:48.031623+00 |
| description | CBOR encoder/decoder: serde_cbor compatible with support for tagged types. |
| homepage | https://contentauthenticity.org |
| repository | https://github.com/contentauth/c2pa-cbor |
| max_upload_size | |
| id | 2046296 |
| size | 18,155,585 |
A fast, lightweight CBOR (Concise Binary Object Representation) encoder/decoder with comprehensive support for tagged types.
write_tag() and read_tag() methodsserde_transcode support - handles #[serde(flatten)] and other advanced featuresThis library includes built-in protection against malicious CBOR attacks:
These limits are sufficient for legitimate C2PA manifests while preventing denial-of-service attacks. For advanced use cases requiring custom limits, use the builder pattern:
use c2pa_cbor::Decoder;
use std::io::Cursor;
let decoder = Decoder::new(Cursor::new(&data))
.with_max_allocation(1024 * 1024) // 1MB limit
.with_max_depth(64); // Max 64 levels
Add this to your Cargo.toml:
[dependencies]
c2pa_cbor = "0.1"
serde = { version = "1.0", features = ["derive"] }
serde_bytes = "0.11" # For efficient byte array handling
use c2pa_cbor::{to_vec, from_slice};
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct Person {
name: String,
age: u32,
}
let person = Person {
name: "Alice".to_string(),
age: 30,
};
// Encode to CBOR
let encoded = to_vec(&person).unwrap();
// Decode from CBOR
let decoded: Person = from_slice(&encoded).unwrap();
assert_eq!(person, decoded);
use c2pa_cbor::{encode_uri, encode_datetime_string, from_slice};
// Encode a URI with tag 32
let mut buf = Vec::new();
encode_uri(&mut buf, "https://example.com").unwrap();
let decoded: String = from_slice(&buf).unwrap();
assert_eq!(decoded, "https://example.com");
// Encode a datetime string with tag 0
let mut buf = Vec::new();
encode_datetime_string(&mut buf, "2024-01-15T10:30:00Z").unwrap();
let decoded: String = from_slice(&buf).unwrap();
For optimal performance with byte arrays, use serde_bytes:
use c2pa_cbor::{to_vec, from_slice};
use serde_bytes::ByteBuf;
// Efficient byte array encoding
let data = ByteBuf::from(vec![1, 2, 3, 4, 5]);
let encoded = to_vec(&data).unwrap();
// Only 1 byte overhead for small arrays!
assert_eq!(encoded.len(), 6);
let decoded: ByteBuf = from_slice(&encoded).unwrap();
assert_eq!(decoded.into_vec(), vec![1, 2, 3, 4, 5]);
use c2pa_cbor::Encoder;
let mut buf = Vec::new();
let mut encoder = Encoder::new(&mut buf);
// Write a custom tag (e.g., tag 100)
encoder.write_tag(100).unwrap();
encoder.encode(&"custom data").unwrap();
use c2pa_cbor::{encode_uint8_array, encode_uint32be_array};
let mut buf = Vec::new();
// Encode uint8 array with tag 64
encode_uint8_array(&mut buf, &[1, 2, 3, 4, 5]).unwrap();
// Encode uint32 big-endian array with tag 66
let data: [u32; 3] = [0x12345678, 0x9ABCDEF0, 0x11223344];
encode_uint32be_array(&mut buf, &data).unwrap();
This library fully supports serde_transcode for converting between formats:
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
#[derive(Serialize, Deserialize)]
struct Config {
name: String,
#[serde(flatten)] // This works correctly!
extra: HashMap<String, serde_json::Value>,
}
// Convert JSON to CBOR via transcode
let json_str = r#"{"name":"app","version":"1.0","debug":true}"#;
let mut from = serde_json::Deserializer::from_str(json_str);
let mut to = c2pa_cbor::ser::Serializer::new(Vec::new());
serde_transcode::transcode(&mut from, &mut to).unwrap();
let cbor_bytes = to.into_inner();
// The CBOR is always definite-length (required for C2PA signatures)
let config: Config = c2pa_cbor::from_slice(&cbor_bytes).unwrap();
Note: When the collection size is known (the common case), serialization is zero-overhead.
When using #[serde(flatten)] or similar features that require unknown-length serialization,
the library automatically buffers entries to produce definite-length CBOR output.
This implementation is designed for speed with binary byte arrays:
serde_bytesThis library uses a smart dual-path serialization strategy:
Fast Path (99% of cases): When collection sizes are known at serialization time (normal structs, Vec, HashMap, etc.), data is written directly with zero overhead.
Buffering Path (rare cases): When sizes are unknown (e.g., #[serde(flatten)] with serde_transcode), entries are buffered and written as definite-length once the count is known.
This design ensures:
The buffering path adds minimal overhead and only activates when necessary, making the library both fast and fully compatible with the serde ecosystem.
This library is designed as a drop-in replacement for serde_cbor:
// Before (serde_cbor)
use serde_cbor::{to_vec, from_slice};
let encoded = serde_cbor::to_vec(&value)?;
let decoded = serde_cbor::from_slice(&encoded)?;
// After (c2pa_cbor)
use c2pa_cbor::{to_vec, from_slice};
let encoded = c2pa_cbor::to_vec(&value)?;
let decoded = c2pa_cbor::from_slice(&encoded)?;
#[serde(flatten)] - No more "indefinite-length maps require manual encoding" errorsserde_transcode support - Works seamlessly with JSON-to-CBOR conversionto_vec<T: Serialize>(value: &T) -> Result<Vec<u8>> - Encode any serializable valueencode_tagged<W, T>(writer, tag, value) - Encode a tagged valueencode_datetime_string(writer, datetime) - Tag 0encode_epoch_datetime(writer, epoch) - Tag 1encode_uri(writer, uri) - Tag 32encode_base64url(writer, data) - Tag 33encode_base64(writer, data) - Tag 34encode_uint8_array(writer, data) - Tag 64encode_uint16be_array(writer, data) - Tag 65encode_uint32be_array(writer, data) - Tag 66encode_uint64be_array(writer, data) - Tag 67encode_uint16le_array(writer, data) - Tag 69encode_uint32le_array(writer, data) - Tag 70encode_uint64le_array(writer, data) - Tag 71encode_float32be_array(writer, data) - Tag 81encode_float64be_array(writer, data) - Tag 82encode_float32le_array(writer, data) - Tag 85encode_float64le_array(writer, data) - Tag 86from_slice<'de, T: Deserialize<'de>>(slice: &[u8]) -> Result<T> - Decode any deserializable valueuse c2pa_cbor::{Encoder, Decoder};
// Encoding
let mut buf = Vec::new();
let mut encoder = Encoder::new(&mut buf);
encoder.write_tag(42).unwrap();
encoder.encode(&some_value).unwrap();
// Decoding
let mut decoder = Decoder::new(&buf[..]);
let tag = decoder.read_tag().unwrap();
let value: SomeType = decoder.decode().unwrap();
This implementation follows:
This library always produces definite-length CBOR (never indefinite-length), which ensures:
This is achieved through:
Direct encoding when sizes are known (fast path)
Automatic buffering and counting when sizes are unknown (compatibility path)
We welcome contributions to this project. For information on contributing, providing feedback, and about ongoing work, see Contributing. For additional information on testing, see Contributing to the project.
The c2pa crate is distributed under the terms of both the MIT license and the Apache License (Version 2.0).
Some components and dependent crates are licensed under different terms; please check their licenses for details.