cddl-verifier
Validate CBOR (RFC 8949) data against CDDL (RFC 8610) schemas, render it as annotated EDN (Extended Diagnostic Notation), and convert between CBOR and JSON. It is built for attestation formats such as CoRIM and CoSWID.
Scope. cddl-verifier parses the whole CDDL grammar (RFC 8610 as updated by RFC 9682) and enforces every construct and control operator in the conformance matrix, which lists each one with its tests. A corpus of real schemas and CBOR from RFCs and IETF drafts is checked against it, and the few places where it differs on purpose are listed there. See also Limitations and docs/CDDL_SUPPORT.md. The public API is stable from 1.0: see docs/COMPATIBILITY.md.
| Surface | Name |
|---|---|
| PyPI distribution | cddl-verifier |
| Python import package | cddl_verifier |
| Command-line tool | cddl-verify |
Installation
pip install cddl-verifier
Requires Python 3.9 or newer. There are no runtime dependencies: CBOR encoding
and decoding are bundled. Extras for contributors: pip install "cddl-verifier[test]"
(pytest, Hypothesis, pycddl) and [dev] (the same plus build, twine).
Quick start
New here? docs/GETTING_STARTED.md walks from installation to reading a failed validation, with a triage flow for each kind of failure. For copy-pasteable workflows (validation, debugging, JSON conversion, CI), see the task-oriented recipes.
Command line
cddl-verify schema.cddl data.cbor --type corim-map # validate, print annotated EDN
cddl-verify schema.cddl data.cbor --type corim-map --output data.edn # write EDN to a file
cddl-verify schema.cddl data.cbor --show-types # list the parsed CDDL types
cddl-verify schema.cddl data.cbor -M modules/ --module rfc9393=coswid.cddl # load imported CDDL modules
The exit status is 1 when the CBOR cannot be decoded or, with --type, when
validation fails. Without --type the first rule is used and validation errors
are only warnings (exit status 0).
python -m cddl_verifier takes the same arguments. See docs/CLI.md.
Python
from pathlib import Path
from cddl_verifier import validate
result = validate(Path("schema.cddl"), Path("data.cbor").read_bytes(),
root_type="corim-map")
if result.valid:
print("valid")
else:
for diagnostic in result.diagnostics:
print(diagnostic.code, diagnostic.path_text, diagnostic.message)
schemais CDDL text (str) or a path (pathlib.Path).datais CBOR bytes or an already-decoded Python value.root_typedefaults to the first rule in the schema.- CBOR that cannot be decoded gives an invalid result with a
"decode"diagnostic and its byte offset; it does not raise. - Each diagnostic also has the data
path, the byteoffsetand bytes of the item, therule, and what wasexpectedand found.result.to_json()andrender_text(result.diagnostics, verbose=True)render them. See docs/API.md.
To check many items against one schema, or to render EDN, use Validator:
from cddl_verifier import Validator
validator = Validator(Path("schema.cddl"))
result = validator.validate(cbor_bytes, root_type="corim-map")
print(validator.to_edn(cbor_bytes, root_type="corim-map"))
CBOR and JSON helpers:
from cddl_verifier.cbor import encode, decode
from cddl_verifier.json_codec import cbor_to_json
raw = encode({0: "Alice", 1: b"\x01"}, canonical=True)
assert decode(raw) == {0: "Alice", 1: b"\x01"}
print(cbor_to_json(raw, typed=True, pretty=True))
Decoding is strict: the input must be exactly one complete CBOR item. Empty,
truncated or trailing-byte input raises CBORDecodeError with the byte offset
of the problem. decode_sequence reads a CBOR sequence (RFC 8742) as the list of
its items. See docs/API.md for the full API.
What is supported
- CDDL maps, arrays, type aliases, groups, type choices (
a / band/=), sockets (//=), optional fields, occurrence indicators, IANA-registered parameters (&(name: n)), CBOR tags (#6.n(...)), numeric ranges (0..255,0.0...1.0), and the controls.size,.cbor,.cborseq,.regexp,.bits,.ge,.gt,.le,.lt,.eq,.ne,.and,.withinand the RFC 9165 / RFC 9741 controls (.plus,.cat,.det,.abnf, the text conversions,.printf,.json,.join). Details: docs/CDDL_SUPPORT.md. - Real CoRIM and CoSWID schemas: docs/CORIM_SUPPORT.md.
- CBOR encoding and strict decoding with byte offsets in errors; duplicate map keys, invalid UTF-8 and excessive nesting are rejected.
- Annotated EDN output with field names from the schema. A member is named by its
key or its
&(name: 0)label; withcomment_names=True(--comment-names) a trailing comment (0: tstr ; name) names a member that has no&(name: 0)label (a label always wins). - CBOR ↔ JSON conversion.
typed=Truekeeps byte strings, tags and non-string map keys (such as the integer keys in CoRIM maps), so typed JSON converts back to the same CBOR data. Without it, map keys become JSON strings, and keys that would collide (0and"0") raise an error.
Limitations
- CDDL: the whole RFC 8610 grammar (as updated by RFC 9682) is parsed, and
schemas that are not valid CDDL raise
SchemaErrorwith a line and column. So do schemas that parse but do not make sense, such as an undefined name, a misused generic or an unknown control operator (#70). Validation covers a practical subset of what the grammar can express; the RFC 9165 and RFC 9741 controls are enforced (.plus,.cat,.det,.abnf,.abnfb, the base64, base32, base16 and base45 conversions,.base10,.json,.printfand.join), with the limits in CDDL_SUPPORT.md. The conformance matrix has the full list. - Canonical encoding: implements the RFC 8949 §4.2.1 core deterministic
profile (see CANONICAL_AND_JSON.md).
The optional §4.2.2 reduction of integral floats to integers is not applied,
and floats are written in their shortest exact width in both modes (RFC 8949 §4.1);
float_width(value, 64)fixes the width of one value. - CBOR: indefinite-length items,
undefinedand the other simple values decode; the encoder writes definite-length items.decoderejects a CBOR sequence;decode_sequencereads one. - Value types: tags decode to
CBORTag(tag, value); a(tag, value)tuple is not a tag (the encoder raisesTypeErrorfor one, since 0.24.0). Map keys that are booleans, floats, arrays, maps or tags decode toCBORKey, so{1: a, true: b}keeps both entries and re-encodes to the same bytes.
Documentation
| Document | Contents |
|---|---|
| docs/GETTING_STARTED.md | Install, write a schema, validate, and triage each kind of failure, with example commands and output |
| docs/RECIPES.md | End-to-end CLI, Python, debugging, JSON/CBOR, CoRIM, and CI recipes |
| docs/CLI.md | cddl-verify and JSON converter options, input rules, EDN output formats |
| docs/API.md | Python API: validate, Validator, CBOR and JSON helpers |
| docs/CDDL_SUPPORT.md | Supported CDDL syntax, types, and validation behaviour |
| docs/CONFORMANCE.md | Conformance matrix: every construct, control and CBOR item by layer, with status and tests |
| docs/CORPUS.md | Conformance corpus: real schemas and CBOR from RFC 8610, COSE, CoRIM, CoSWID, EDHOC and SUIT with the verdict each source gives, and what this library gets wrong |
| docs/divergences.json | Where this library and the independent cddl crate (pycddl) disagree on the conformance corpus, and who is right (Differential testing) |
| docs/CORIM_SUPPORT.md | CoRIM / CoSWID schema support |
| docs/CBOR_BUILDER_QUICK_REF.md, docs/ITERATIVE_CONSTRUCTION.md | CBOR builder API in depth |
| docs/CBOR_DIAGNOSTIC_DUMP.md | Diagnostic dump format |
| docs/CANONICAL_AND_JSON.md | Canonical encoding and JSON round-trips |
| docs/EDN_FORMATTING_IMPROVEMENTS.md, docs/IANA_ANNOTATIONS_STATUS.md | What the annotated EDN looks like, and registered parameters |
| docs/ | Tag notation and other notes |
| docs/COMPATIBILITY.md | What the 1.x compatibility promise covers, and what it does not; deprecation and Python version policy |
| docs/BENCHMARKS.md | Benchmarks and profiling: how to run them and what they found |
| docs/CDDL_AST_DESIGN.md | Contributor design: CDDL AST, parser and how it was introduced |
| TESTING.md | Running and writing tests, CI setup |
| CONTRIBUTING.md | Pull request checklist, versioning and releases |
| Releases | Release notes |
Development
git clone https://github.com/sahebbiswas/cddl_verifier
cd cddl_verifier
pip install -e ".[test]"
pytest
The package source is in src/cddl_verifier/. Modules whose names start with
an underscore are internal. See TESTING.md.
Versioning and releases
Every pull request merged to main bumps the version in
src/cddl_verifier/_version.py,
following Semantic Versioning 2.0.0: a patch bump for
fixes and docs, a minor bump for new features, and a major bump only for an
incompatible change to the public API (cddl_verifier, cddl_verifier.cbor,
cddl_verifier.json_codec and the cddl-verify CLI). What is covered, and what
is not (message wording, EDN layout, internal modules), is in
docs/COMPATIBILITY.md.
Releases are cut from main when it
is stable, so not every version is published. Each release's notes are on the
GitHub releases page;
there is no changelog file. See
CONTRIBUTING.md
for the rules and the release steps.
Metadata
Release files for cddl-verifier 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cddl_verifier-1.0.0.tar.gz | 540.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cddl_verifier-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 740.7 kB
Release files / cddl_verifier-1.0.0.tar.gz
| Download URL | cddl_verifier-1.0.0.tar.gz |
|---|---|
| Size | 540.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
82cd0568e4a35e4b735f8cc08f865318d89be28fa794b76e1cd8d19738b60819
|
|
BLAKE2b-256 checksum How to use checksums |
7ad5d59d68dfd1956bbd8f795c1702cd6df599bc2626e3b97786e725bfe2eb61
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency logRelease files / cddl_verifier-1.0.0-py3-none-any.whl
| Download URL | cddl_verifier-1.0.0-py3-none-any.whl |
|---|---|
| Size | 200.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f88969e6f1189524cfc7e6b764acabf2d0321bf8f9c1581b950c276dfd287cc7
|
|
BLAKE2b-256 checksum How to use checksums |
8a5f0af8f92246634654877b66eb7dfe4b2ebe3c51ccc8424147fc2d5f9a4815
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency log