Concise Reference Integrity Manifest (CoRIM) — Rust implementation of draft-ietf-rats-corim-10.
This crate provides CBOR-native Rust types for the CoRIM / CoMID CDDL schema, a builder API, validation/appraisal logic, and signed CoRIM (COSE_Sign1) support for Remote Attestation (RATS) Endorsements and Reference Values.
-
Full CDDL coverage — types for
corim-map,concise-mid-tag(CoMID),concise-tl-tag(CoTL), all 9 triple types (reference, endorsed, identity, attest-key, domain dependency/membership, CoSWID, conditional endorsement, conditional endorsement series),measurement-values-mapwith all fields (digests, SVN, flags, raw-value, MAC/IP addresses, integrity registers, int-range, crypto keys, etc.). -
Signed CoRIM (
#6.18) — decode, validate, and construct COSE_Sign1-corim structures per §4.2. Supports both attached and detached payload modes. No cryptographic dependencies — the caller signs/verifies externally using the emittedSig_structure1TBS blob. Protected header extraction includescorim-meta,CWT-Claims, and hash-envelope fields. -
Zero-dependency CBOR — built-in CBOR encoder/decoder with deterministic encoding per RFC 8949 §4.2.1. No external CBOR library required. The
CborCodectrait allows plugging in alternative backends in the future. -
no_stdsupport — thecorimlibrary crate compiles with#![no_std]+alloc. Thestdfeature (default) addsSystemTime-based validation. Thejsonfeature requiresstd. -
Integer-keyed CBOR maps — derive macros (
CborSerialize/CborDeserialize) emit deterministic CBOR with integer keys per RFC 8949 §4.2.1. -
Builder API — fluent
ComidBuilder,CotlBuilder,CorimBuilder, andSignedCorimBuilderfor constructing tagged CoRIM payloads.ComidBuilderhas an opt-in environment catalog (declare_env/EnvRef/add_*_for(…)) for sharing oneEnvironmentMapacross multiple triples, plus astrict_linkslint that flags conditional/endorsed triples whose condition env is not anchored by any reference triple in the same CoMID. -
Validation & Appraisal — reference value matching (Phase 3) and conditional endorsement series application (Phase 4) per §9 of the spec.
-
Profile framework —
corim::profiledefines aProfiletrait, aProfileRegistry, and aMatchContext(epoch-aware) so downstream crates can plug in CoRIM profiles that introduce extra measurement-values-map fields or non-core CBOR tags. The first-party Intel profile (draft-cds-rats-intel-corim-profile) ships under theprofile-intelCargo feature with anIntelProfile, the#6.60010expression decoder, andtdate-aware match semantics. -
CoSWID — structured
ConciseSwidTag,SwidEntity,SwidLinktypes per RFC 9393 with co-constraint validation (patch/supplemental, tag-creator role, patches link). -
Optional JSON —
jsonfeature gate addsValue ↔ serde_json::Valueconversion with integer-to-string key remapping and type-choice JSON format. -
TCG / NVIDIA decode interop — accepts the legacy
#6.500/#6.502outer wrappers, barecorim-mappayloads, and TCG-style#6.506(map)CoMID nesting seen in real-world signed CoRIMs (notably NVIDIA NIC firmware). Decode-only; encoders always emit draft-10 wire format. Seecorim::compatfor the full list.
Rust 1.85.
use corim::builder::{ComidBuilder, CorimBuilder};
use corim::types::common::{TagIdChoice, MeasuredElement};
use corim::types::corim::CorimId;
use corim::types::environment::{ClassMap, EnvironmentMap};
use corim::types::measurement::{Digest, MeasurementMap, MeasurementValuesMap};
use corim::types::triples::ReferenceTriple;
let env = EnvironmentMap {
class: Some(ClassMap {
class_id: None,
vendor: Some("ACME".into()),
model: Some("Widget".into()),
layer: None,
index: None,
}),
instance: None,
group: None,
};
let meas = MeasurementMap {
mkey: Some(MeasuredElement::Text("firmware".into())),
mval: MeasurementValuesMap {
digests: Some(vec![Digest::new(7, vec![0xAA; 48])]),
..MeasurementValuesMap::default()
},
authorized_by: None,
};
// Build a CoMID with reference values
let comid = ComidBuilder::new(TagIdChoice::Text("my-comid-tag".into()))
.add_reference_triple(ReferenceTriple::new(env, vec![meas]))
.build()
.unwrap();
// Wrap in a CoRIM and encode to tag-501-wrapped CBOR
let bytes = CorimBuilder::new(CorimId::Text("my-corim".into()))
.add_comid_tag(comid).unwrap()
.build_bytes().unwrap();
// Decode and validate
let (_corim, _comids) = corim::validate::decode_and_validate(&bytes).unwrap();This crate implements CoRIM per draft-ietf-rats-corim-10.
| Feature | Status |
|---|---|
CoMID (§5) — #6.506 |
✅ Fully modeled — types, builder, validation, appraisal |
CoTL (§6) — #6.508 |
✅ Fully modeled — ConciseTlTag, CotlBuilder, validity checks |
CoSWID (RFC 9393) — #6.505 |
✅ Structured — ConciseSwidTag, SwidEntity, SwidLink; payload/evidence opaque |
Signed CoRIM (§4.2) — #6.18 |
✅ Decode, validate, construct (attached + detached); no crypto dependency |
| CDDL extension sockets | ❌ Not modeled; unknown keys silently skipped for forward compatibility |
| CoTS (concise-ta-stores) | ❌ Separate draft, not modeled |
no_std + alloc |
✅ Library crate compiles without std; std feature is default-on |
The crate supports creating and parsing signed CoRIM documents (#6.18 /
COSE_Sign1-corim) without any cryptographic dependencies. The caller
performs signature operations externally.
use corim::types::signed::{SignedCorimBuilder, CwtClaims};
// 1. Build unsigned CoRIM payload bytes (tag-501-wrapped)
let corim_bytes: Vec<u8> = /* CorimBuilder::build_bytes() */ vec![];
// 2. Create a signed CoRIM builder
let mut builder = SignedCorimBuilder::new(-7, corim_bytes) // ES256
.set_cwt_claims(CwtClaims::new("ACME Corp"));
// 3. Get the Sig_structure1 TBS blob
let tbs = builder.to_be_signed(&[]).unwrap();
// 4. Sign with your crypto library (ring, openssl, etc.)
let signature = vec![0u8; 64]; // placeholder
// 5. Produce the final signed CoRIM
let signed_bytes = builder.build_with_signature(signature).unwrap();For detached payloads, use build_detached_with_signature() and
to_be_signed_detached() on the decoded envelope. See the
types::signed module documentation for
the full API.
| Crate | Description |
|---|---|
corim |
Main library — types, builder, validation, signed CoRIM, CBOR engine |
corim-macros |
Proc-macro derives for integer-keyed CBOR map serde |
corim-cli |
CLI tool for validating and inspecting CoRIM documents |
This crate includes a built-in minimal CBOR encoder/decoder. No external CBOR library is needed.
What's supported — the CBOR subset used by CoRIM:
- All CBOR major types (unsigned/negative int, byte/text strings, arrays, maps, tags)
- Deterministic encoding per RFC 8949 §4.2.1 (canonical map key sorting)
- Semantic tags (essential for CoRIM type-choice dispatching)
- Half/single/double precision float decoding
Limitations (none affect CoRIM functionality):
- No indefinite-length encoding (rejected on decode; CoRIM uses definite only)
- Float encoding always uses float64 (CoRIM rarely uses floats)
- No CBOR simple values beyond false/true/null (not used in CoRIM)
- Nesting depth limited by call stack (~100+ levels; CoRIM is typically 5–10)
The corim-cli binary validates, inspects, and generates both unsigned
(tag 501) and signed (tag 18) CoRIM documents. It is organized into
subcommands:
# Validate an unsigned CoRIM
corim-cli validate --skip-expiry myfile.corim
# Validate a signed CoRIM (auto-detected)
corim-cli validate --skip-expiry signed.corim
# JSON output
corim-cli validate -f json myfile.corim
# Non-aborting structural diagnose pass — prints issues without rejecting
corim-cli validate --diagnose myfile.corim
# Generate an unsigned CoRIM from a JSON template
corim-cli generate template.json -o out.cbor
# Convert an unsigned CoRIM back to a JSON template (inverse of generate)
corim-cli convert myfile.corim -o template.json
# Extract the unsigned CoRIM payload from a signed CoRIM
corim-cli extract signed.cose -o unsigned.cbor
# Sign (bring-your-own-signer): prepare a staging envelope + to-be-signed bytes,
# sign the TBS externally (HSM / openssl / ...), then inject the signature
corim-cli sign prepare unsigned.cbor --alg ES256 --signer-name "ACME Ltd." \
--x5chain leaf.pem --out-staging staging.cose --out-tbs tbs.bin
corim-cli sign finalize staging.cose --signature sig.bin -o signed.coseThe tool performs no cryptography, so signing is a two-step, bring-your-own-signer flow:
sign preparewraps an unsigned CoRIM in a COSE_Sign1 protected header (algorithm,CWT-Claimssigner identity, and anx5chaincertificate chain from DER or PEM files, leaf-first) and writes a staging envelope (placeholder signature) plus the rawSig_structure1to-be-signed bytes.- Sign the TBS bytes with your own key/HSM per the chosen COSE algorithm.
sign finalizeinjects that signature into the staging envelope, producing the final signed CoRIM.
extract reverses signing: it pulls the embedded unsigned CoRIM payload
back out of a signed CoRIM (attached payloads only). validate on a
signed CoRIM checks structure only and prints a note that the signature
is not cryptographically verified.
generate builds an unsigned CoRIM from a hand-authored JSON
template. A template has a corim-id, an optional profile, optional
CoRIM-level fields (rim-validity, entities, dependent-rims), and
one or more tag arrays (comids, coswids, cotls). Each tag is
deserialized into its decoded type (CoMID gets the full triples tree),
then encoded and wrapped by the builder.
Map keys may be written as prose names ("tag-identity",
"triples", "vendor", "svn", …); the CLI rewrites them to the CBOR
integer keys the core json layer expects using a context-aware state
machine (it knows, e.g., that "version" is key 1 in tag-identity but
key 0 in measurement-values-map). Raw integer-string keys ("1",
"4", …) are still accepted, and the rewrite is idempotent — so
prose, integer, and mixed templates all produce identical output.
Triple records may be written as labeled objects using the CDDL
field names (e.g. a conditional-endorsement-series triple as
{ "common-condition": { "environment": …, "claims-list": … }, "series": [ { "condition": …, "addition": … } ] }) or as the legacy positional
arrays; both are accepted, and convert emits the labeled form.
corim-id and profile accept either a plain string (text id / URI) or
a type-choice object for the other variants — corim-id as
{ "type": "uuid", "value": "…" }, profile as
{ "type": "oid", "value": "<base64>" }. rim-validity is
{ "not-before"?: <epoch>, "not-after": <epoch> } (epoch seconds).
Any object may carry a $comment (or //) key as an authoring note;
these are stripped before encoding and never reach the CBOR output. The
template stays valid standard JSON, so any JSON tool accepts it.
Comments can annotate objects (the root, a CoMID, a triple record, an
environment, …) but not bare array elements or scalars.
Profile-defined measurement-values-map extension keys can be written
by alias (e.g. "tcbstatus": "UpToDate" instead of "-700": ...) when
the template's profile field names a profile the CLI was compiled with.
See corim-cli/templates/azure_ndpa.json
for a worked example (equivalent to the build_corim_ovl3_tdisp_ndpa
example). Signed CoRIM generation is out of scope — sign the output
separately via SignedCorimBuilder.
Byte-string fields are authored as base64 text (matching the output
of corim-cli validate -f json). Bare bstr positions — digest values,
ueid, uuid, mac-addr, ip-addr, and integrity-registers digests
— are decoded to CBOR bytes automatically, as is the bstr inside
byte-bearing type-choice tags the core layer leaves as text
(oid, cose-key, key-thumbprint, cert-thumbprint,
cert-path-thumbprint, pkix-asn1der-cert, masked-raw-value). All
three ovl3_tdisp reference examples (NDPA, SOCMANA, SFUA) reproduce
byte-identically from templates. Remaining gaps: signed CoRIMs, and
type-choice variants the core json layer does not round-trip.
convert is the inverse of generate. It decodes a tag-501 unsigned
CoRIM and emits a prose-keyed JSON template (to -o FILE, or stdout)
that feeds straight back into generate, reproducing the original bytes:
corim-cli convert myfile.corim -o template.json
corim-cli generate template.json -o roundtrip.corim # byte-identicalThis differs from validate -f json, which prints a validation
summary (validity, counts, triple types), not the CoRIM contents. Use
convert when you want the full structure as editable JSON, and validate --edn for CBOR Extended Diagnostic Notation. All three ovl3_tdisp
reference examples round-trip convert → generate byte-identically.
Signed CoRIMs are out of scope — convert the payload instead.
Two worked examples show how to build fixtures directly with the builder API (they write the CoRIM to stdout):
# minimal unsigned CoRIM fixture
cargo run -p corim-cli --example gen_sample > sample.corim
# minimal signed CoRIM fixture (COSE_Sign1 with a placeholder signature)
cargo run -p corim-cli --example gen_signed_sample > signed.corimcorim-cli is a local development tool and is not published to crates.io.
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit Contributor License Agreements.
When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.
See CONTRIBUTING.md for detailed guidelines.
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft's Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.