Skip to main content

corim/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! CoRIM (Concise Reference Integrity Manifest) — Rust library.
5//!
6//! This crate provides Rust types for the CoRIM/CoMID CDDL schema
7//! ([draft-ietf-rats-corim-10](https://www.ietf.org/archive/id/draft-ietf-rats-corim-10.html)),
8//! CBOR encoding/decoding (via a swappable backend), a builder API for
9//! constructing CoRIM and CoMID structures, and validation per the spec.
10//!
11//! # Quick example
12//!
13//! ```rust
14//! use corim::builder::{ComidBuilder, CorimBuilder};
15//! use corim::types::common::{TagIdChoice, MeasuredElement};
16//! use corim::types::corim::CorimId;
17//! use corim::types::environment::{ClassMap, EnvironmentMap};
18//! use corim::types::measurement::{Digest, MeasurementMap, MeasurementValuesMap};
19//! use corim::types::triples::ReferenceTriple;
20//!
21//! let env = EnvironmentMap {
22//!     class: Some(ClassMap {
23//!         class_id: None, vendor: Some("ACME".into()),
24//!         model: Some("Widget".into()), layer: None, index: None,
25//!     }),
26//!     instance: None, group: None,
27//! };
28//!
29//! let meas = MeasurementMap {
30//!     mkey: Some(MeasuredElement::Text("firmware".into())),
31//!     mval: MeasurementValuesMap {
32//!         digests: Some(vec![Digest::new(7, vec![0xAA; 48])]),
33//!         ..MeasurementValuesMap::default()
34//!     },
35//!     authorized_by: None,
36//! };
37//!
38//! let comid = ComidBuilder::new(TagIdChoice::Text("my-tag".into()))
39//!     .add_reference_triple(ReferenceTriple::new(env, vec![meas]))
40//!     .build().unwrap();
41//!
42//! let bytes = CorimBuilder::new(CorimId::Text("my-corim".into()))
43//!     .add_comid_tag(comid).unwrap()
44//!     .build_bytes().unwrap();
45//!
46//! let (_corim, _comids) = corim::validate::decode_and_validate(&bytes).unwrap();
47//! ```
48//!
49//! # CBOR backend
50//!
51//! This crate includes an in-house minimal CBOR encoder/decoder that
52//! guarantees RFC 8949 §4.2.1 deterministic encoding with zero external
53//! CBOR dependencies. The [`cbor::CborCodec`] trait is designed so that
54//! alternative backends (e.g., ciborium) can be added behind feature gates
55//! in the future without changing any public APIs.
56//!
57//! ## CBOR implementation limitations
58//!
59//! The built-in CBOR codec covers the subset needed by CoRIM. Known
60//! limitations (none of which affect CoRIM functionality):
61//!
62//! - **No indefinite-length encoding** — rejected on decode. CoRIM/CoMID
63//!   CDDL uses definite-length only.
64//! - **Float encoding is always float64** — half and single precision floats
65//!   are decoded correctly, but encoding always uses 8-byte float64. CoRIM
66//!   data rarely uses floats (only `cwt-claims` exp/nbf, which are `int`).
67//! - **No CBOR simple values** beyond `false`, `true`, `null` — other simple
68//!   values (0–19, 32–255) are rejected. Not used in CoRIM.
69//! - **No CBOR sequences** — only single top-level items. CoRIM always has
70//!   a single tagged wrapper.
71//! - **Maximum nesting depth** is limited by the call stack (~100+ levels).
72//!   CoRIM documents are typically 5–10 levels deep.
73//!
74//! # Compliance notes
75//!
76//! This crate implements CoRIM per draft-ietf-rats-corim-10.
77//!
78//! ## Tag coverage
79//!
80//! The RFC defines three tag types inside a CoRIM `tags` array:
81//!
82//! | Tag | CBOR | Status |
83//! |-----|------|--------|
84//! | **CoMID** (§5) | `#6.506` | ✅ Fully modeled — types, builder, validation, appraisal |
85//! | **CoTL** (§6) | `#6.508` | ✅ Fully modeled — `ConciseTlTag`, `CotlBuilder`, validity checks |
86//! | **CoSWID** (RFC 9393) | `#6.505` | ✅ Structured — `ConciseSwidTag`, `SwidEntity`, `SwidLink`; payload/evidence opaque |
87//!
88//! ## Signed CoRIM (`#6.18`)
89//!
90//! The crate supports **decoding, structural validation, and construction**
91//! of signed CoRIM documents (`COSE_Sign1-corim`) per §4.2. Cryptographic
92//! signature verification is intentionally **not** performed — the caller
93//! is responsible for verifying signatures using their preferred crypto
94//! library. The crate provides:
95//!
96//! - [`types::signed::decode_signed_corim`]: Parse `#6.18` COSE_Sign1 structures
97//! - [`types::signed::validate_signed_corim_payload`]: Validate attached payloads
98//! - [`types::signed::validate_signed_corim_payload_detached`]: Validate detached payloads
99//! - [`types::signed::SignedCorimBuilder`]: Construct signed CoRIM with external signing
100//!   (attached via `build_with_signature` or detached via `build_detached_with_signature`)
101//! - [`types::signed::CoseSign1Corim::to_be_signed`]: Emit TBS for attached payloads
102//! - [`types::signed::CoseSign1Corim::to_be_signed_detached`]: Emit TBS for detached payloads
103//!
104//! Additionally:
105//! - **CoTS** (`draft-ietf-rats-concise-ta-stores`) is a separate draft, not modeled.
106//! - **CDDL extension sockets** (`$$corim-map-extension`, etc.) are not
107//!   modeled; unknown CBOR map keys are silently skipped for forward
108//!   compatibility.
109//! - **`raw-value-mask-DEPRECATED`** (key 5) is accepted on decode but not
110//!   exposed as a struct field.
111//!
112//! ## `no_std` support
113//!
114//! The `corim` crate supports `#![no_std]` with the `alloc` crate.
115//! Disable the default `std` feature:
116//!
117//! ```toml
118//! corim = { version = "0.1", default-features = false }
119//! ```
120//!
121//! The `std` feature (on by default) adds [`validate::decode_and_validate`]
122//! and [`validate::decode_and_validate_full`] which use `SystemTime::now()`.
123//! The `_at` variants that take an explicit timestamp work in `no_std`.
124//! The `json` feature requires `std`.
125
126#![cfg_attr(docsrs, feature(doc_cfg))]
127#![cfg_attr(not(feature = "std"), no_std)]
128
129// In no_std mode, alloc provides String, Vec, Box, format!, vec!, etc.
130// The #[macro_use] brings format! and vec! macros into scope crate-wide.
131#[cfg_attr(not(feature = "std"), macro_use)]
132extern crate alloc;
133
134// Re-export std when available so modules can `use std::time::SystemTime` etc.
135// In no_std mode, modules must use core:: and alloc:: directly.
136#[cfg(feature = "std")]
137extern crate std;
138
139/// Internal prelude for no_std compatibility.
140///
141/// All modules should `use crate::nostd_prelude::*;` to get String, Vec, Box
142/// and other alloc types regardless of std/no_std mode.
143#[allow(unused_imports)]
144pub(crate) mod nostd_prelude {
145    pub use alloc::borrow::ToOwned;
146    pub use alloc::boxed::Box;
147    pub use alloc::collections::BTreeMap;
148    pub use alloc::string::{String, ToString};
149    pub use alloc::vec;
150    pub use alloc::vec::Vec;
151}
152
153pub mod cbor;
154pub mod error;
155pub mod types;
156
157pub mod builder;
158/// Decode-only interop helpers (legacy tag stripping, etc.). See module docs.
159pub mod compat;
160/// **Unstable** — debugging-only structural inspector. The shapes of the
161/// types in this module may change between minor versions without a
162/// deprecation cycle. Production code should use [`validate::decode_and_validate`].
163pub mod diagnose;
164/// Profile-extension registry and trait. See module docs for how
165/// profile-aware crates plug into validation and diagnosis.
166pub mod profile;
167pub mod validate;
168
169#[cfg(feature = "json")]
170#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
171pub mod json;
172
173pub use error::{BuilderError, DecodeError, EncodeError, ValidationError};
174
175/// Trait for types that can self-validate per the CoRIM specification.
176///
177/// Each type validates its own constraints (non-empty maps,
178/// size limits, structural invariants) without requiring an external validator.
179///
180/// # Example
181///
182/// ```rust
183/// use corim::Validate;
184/// use corim::types::environment::{ClassMap, EnvironmentMap};
185///
186/// let env = EnvironmentMap::for_class("ACME", "Widget");
187/// assert!(env.valid().is_ok());
188///
189/// let empty_env = EnvironmentMap { class: None, instance: None, group: None };
190/// assert!(empty_env.valid().is_err());
191/// ```
192pub trait Validate {
193    /// Validate that this value satisfies its specification constraints.
194    ///
195    /// Returns `Ok(())` if valid, or a human-readable error message.
196    fn valid(&self) -> Result<(), alloc::string::String>;
197}