Skip to content

Commit 5863768

Browse files
committed
Check in emitted CMS Rust module (fully verified)
1 parent bf07cfd commit 5863768

7 files changed

Lines changed: 16865 additions & 16 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,24 @@ jobs:
8383
include:
8484
- name: vest_lib-core
8585
cargo_args: -p vest_lib --no-default-features
86+
verus_args: --expand-errors
8687
- name: vest_lib-alloc
8788
cargo_args: -p vest_lib --no-default-features --features alloc
89+
verus_args: --expand-errors
8890
- name: vest_lib-std
8991
cargo_args: -p vest_lib
92+
verus_args: --expand-errors
9093
- name: vest-tests
9194
cargo_args: -p vest_tests
95+
verus_args: --expand-errors
96+
# --rlimit matches vest_asn1_tests/Makefile: the width stress fixture
97+
# needs more SMT budget than the Verus default. Keep the two in step.
9298
- name: vest-asn1-tests
9399
cargo_args: -p vest_asn1_tests
100+
verus_args: --expand-errors --rlimit 100
94101
- name: vest-dev
95102
cargo_args: -p vest_dev
103+
verus_args: --expand-errors
96104
name: Verify ${{ matrix.name }}
97105
steps:
98106
- uses: actions/checkout@v4
@@ -114,7 +122,7 @@ jobs:
114122
restore-keys: |
115123
linux-verus-${{ matrix.name }}-
116124
linux-verus-
117-
- run: cargo verus verify ${{ matrix.cargo_args }} -- --expand-errors
125+
- run: cargo verus verify ${{ matrix.cargo_args }} -- ${{ matrix.verus_args }}
118126

119127
verify-macos:
120128
if: github.event_name == 'schedule'
@@ -132,5 +140,5 @@ jobs:
132140
run: echo "${{ github.workspace }}/.verus" >> "$GITHUB_PATH"
133141
- run: cargo verus verify -p vest_lib -- --expand-errors
134142
- run: cargo verus verify -p vest_tests -- --expand-errors
135-
- run: cargo verus verify -p vest_asn1_tests -- --expand-errors
143+
- run: cargo verus verify -p vest_asn1_tests -- --expand-errors --rlimit 100
136144
- run: cargo verus verify -p vest_dev -- --expand-errors

‎README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ proofs.
2323
- [`vest_lib/`](vest_lib/) — verified parser and serializer combinators;
2424
- [`vest_asn1/`](vest_asn1/) — an ASN.1 frontend targeting the same backend;
2525
- [`vest_tests/`](vest_tests/) — DSL fixtures, including TLS and Bitcoin;
26-
- [`vest_asn1_tests/`](vest_asn1_tests/) — generated DER, BER, and mixed-rule fixtures; and
26+
- [`vest_asn1_tests/`](vest_asn1_tests/) — generated DER, BER, and mixed-rule fixtures, including the curated RFC 5652 CMS module; and
2727
- [`vest_dev/`](vest_dev/) — handwritten formats and development examples.
2828

2929
`vest_lib` includes primitive integer and byte formats, dependent and recursive
@@ -99,7 +99,7 @@ cargo check -p vest_lib --no-default-features --all-targets
9999
cargo check -p vest_lib --no-default-features --features alloc --all-targets
100100
cargo verus verify -p vest_lib -- --expand-errors
101101
cargo verus verify -p vest_tests -- --expand-errors
102-
cargo verus verify -p vest_asn1_tests -- --expand-errors
102+
cargo verus verify -p vest_asn1_tests -- --expand-errors --rlimit 100
103103
```
104104

105105
Regenerate checked-in fixtures with `make -C vest_tests vest` and

‎vest_asn1/README.md‎

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,25 @@ silent approximation.
127127

128128
## Tests
129129

130-
The checked-in DER, BER, and mixed-rule fixtures under `test/` cover nominal
131-
formats, inline helpers, slice serialization, tagging, OPTIONAL, DEFAULT,
132-
CHOICE, SEQUENCE/SET OF, heterogeneous DER SET, ENUMERATED, OID and REAL round
133-
trips, BER constructed values, SIZE refinements, and generated proof
134-
interfaces.
135-
136-
Run `make test` or `make verify` in `test/` to test or verify the generated
137-
fixtures. Codegen freshness is checked by `cargo test`; set `UPDATE_GOLDEN=1`
138-
when intentionally regenerating the checked-in Rust files.
130+
The checked-in DER, BER, and mixed-rule fixtures in
131+
[`vest_asn1_tests/`](../vest_asn1_tests/) cover nominal formats, inline helpers,
132+
slice serialization, tagging, OPTIONAL, DEFAULT, CHOICE, SEQUENCE/SET OF,
133+
heterogeneous DER SET, ENUMERATED, OID and REAL round trips, BER constructed
134+
values, SIZE refinements, and generated proof interfaces.
135+
136+
Two further fixtures are scalability probes rather than feature coverage:
137+
`schema_depth.asn1` chains 18 nested definitions, and `schema_width.asn1` is a
138+
16-field SEQUENCE cycling OPTIONAL, DEFAULT, and required components. The width
139+
fixture needs a raised SMT ceiling; see the `VERUS_ARGS` comment in
140+
[`vest_asn1_tests/Makefile`](../vest_asn1_tests/Makefile).
141+
142+
`generated_cms.rs` compiles the curated RFC 5652 CMS module in
143+
[`rfcs/`](rfcs/) — a BER envelope with a DER island, and the largest schema in
144+
the corpus. [`rfcs/README.md`](rfcs/README.md) derives the encoding-rule split
145+
from RFC 5652, RFC 5280, and RFC 5755, and records the resulting strictness
146+
deviations.
147+
148+
Run `make -C ../vest_asn1_tests test` or `make -C ../vest_asn1_tests verify` to
149+
test or verify the generated fixtures, and `make -C ../vest_asn1_tests generate`
150+
to regenerate them. Codegen freshness is checked by `cargo test`; set
151+
`UPDATE_GOLDEN=1` when intentionally regenerating the checked-in Rust files.

‎vest_asn1/rfcs/README.md‎

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Curated RFC schemas
2+
3+
This directory holds ASN.1 modules transcribed from published RFCs, curated so
4+
that `vest_asn1` can compile them. They are inputs to the verification corpus,
5+
not part of the `vest_asn1` library.
6+
7+
| Schema | Source | Generated module |
8+
| --- | --- | --- |
9+
| [`CMS-RFC5652-Curated.asn1`](CMS-RFC5652-Curated.asn1) | RFC 5652 §12, with structured dependencies from RFC 5280 App. A and RFC 5755 §4 | [`vest_asn1_tests/src/generated_cms.rs`](../../vest_asn1_tests/src/generated_cms.rs) |
10+
11+
The header comment of each schema records its curation rules — what was
12+
expanded, what was replaced by ordinary wire types, and what was deliberately
13+
left out.
14+
15+
## Generating `generated_cms.rs`
16+
17+
The rule is recorded once, in the `generate` target of
18+
[`vest_asn1_tests/Makefile`](../../vest_asn1_tests/Makefile). Regenerate with:
19+
20+
```sh
21+
make -C vest_asn1_tests generate
22+
```
23+
24+
which runs, for this schema:
25+
26+
```sh
27+
cargo run -p vest_asn1 -- --rules ber \
28+
--der-definition SignedAttributes \
29+
--der-definition AuthAttributes \
30+
--der-definition Certificate \
31+
--der-definition CertificateList \
32+
--der-definition AttributeCertificate \
33+
--der-definition AttributeCertificateV1 \
34+
vest_asn1/rfcs/CMS-RFC5652-Curated.asn1 -o vest_asn1_tests/src/generated_cms.rs
35+
```
36+
37+
CI regenerates and requires `git diff --exit-code` to be empty, so the committed
38+
module and this command cannot drift apart.
39+
40+
## Why BER is the default
41+
42+
RFC 5652 §1 states the design intent for every CMS content type:
43+
44+
> As a general design philosophy, each content type permits single pass
45+
> processing using indefinite-length Basic Encoding Rules (BER) encoding.
46+
47+
and §5.2 confirms that the carried content itself is unconstrained:
48+
49+
> The eContent need not be DER encoded.
50+
51+
So the CMS envelope — `ContentInfo`, `SignedData`, `EnvelopedData`,
52+
`DigestedData`, `EncryptedData`, `AuthenticatedData`, the `RecipientInfo`
53+
family, `SignerInfo`, and the unsigned/unauthenticated/unprotected attribute
54+
sets — is generated under BER.
55+
56+
## Why the overrides are DER
57+
58+
Each override is a structure whose octets are an input to a signature or digest.
59+
A verifier that does not re-encode — and a Vest-generated parser must not
60+
re-encode, since re-encoding is precisely the step that reintroduces
61+
malleability — has to receive those octets already in DER.
62+
63+
| Definition | Authority | Text |
64+
| --- | --- | --- |
65+
| `SignedAttributes` | RFC 5652 §5.3 | "SignedAttributes MUST be DER encoded, even if the rest of the structure is BER encoded." |
66+
| `AuthAttributes` | RFC 5652 §9.1 | "The AuthAttributes structure MUST be DER encoded, even if the rest of the structure is BER encoded." |
67+
| `Certificate` | RFC 5280 §4.1.1.3 | "The signatureValue field contains a digital signature computed upon the ASN.1 DER encoded tbsCertificate." |
68+
| | RFC 5755 §7.3 | "the digest MUST be calculated over the DER encoding of the entire PKC, including the signature value." |
69+
| `CertificateList` | RFC 5280 §5.1.1.3 | "The signatureValue field contains a digital signature computed upon the ASN.1 DER encoded tbsCertList." |
70+
| `AttributeCertificate` | RFC 5755 §7.3 | Same signed-object shape; RFC 5755 §4 profiles ACs on top of RFC 5280. |
71+
| `AttributeCertificateV1` | RFC 5652 §12.2 | Same signed-object shape. Declared obsolete by §10.2.2, but retained for backward compatibility. |
72+
73+
RFC 5652 §1 also says that "signed attributes and authenticated attributes are
74+
the only data types used in the CMS that require DER encoding". That is a
75+
statement about the types CMS itself defines. `Certificate`, `CertificateList`,
76+
and `AttributeCertificate` are imported from RFC 5280 and RFC 5755, and are
77+
governed by those documents.
78+
79+
`vest_asn1` propagates a rule to every transitive child of an overridden
80+
definition without changing its parents, so the six roots above put the whole
81+
X.509 subtree under DER — `TBSCertificate`, `AlgorithmIdentifier`, `Name`,
82+
`RDNSequence`, `Extensions`, `Validity`, `SubjectPublicKeyInfo`, `GeneralName`,
83+
the X.400 `ORAddress` subtree, and `Attribute`/`AttributeValue`. The result is a
84+
62/62 split between DER and BER nominal formats.
85+
86+
`PersonalName` lands in that closure via
87+
`AttributeCertificate → GeneralNames → GeneralName → ORAddress →
88+
BuiltInStandardAttributes`. It has to: it is a heterogeneous `SET`, and BER
89+
permits a `SET` to carry its components in any order, so `vest_asn1` emits
90+
heterogeneous `SET`s only under DER, where X.690 clause 11 fixes them in
91+
ascending tag order and a single fixed-order combinator is sound.
92+
93+
## What is deliberately *not* overridden
94+
95+
`ExtendedCertificate` is a signed object too, but its closure reaches
96+
`UnauthAttributes`, which CMS leaves under BER. Forcing it to DER would make
97+
unauthenticated attributes stricter than RFC 5652 allows. RFC 5652 §10.2.2 also
98+
declares it obsolete:
99+
100+
> The PKCS #6 extended certificate is obsolete. The PKCS #6 certificate is
101+
> included for backward compatibility, and PKCS #6 certificates SHOULD NOT be
102+
> used.
103+
104+
so it stays BER.
105+
106+
## Known strictness deviations
107+
108+
`vest_asn1` gives each definition exactly one rule, so a definition shared
109+
between a DER and a BER context resolves to DER. Two shared definitions in this
110+
schema are therefore stricter than RFC 5652 alone requires:
111+
112+
- **`Attribute` / `AttributeValue`** are DER because `SignedAttributes` and
113+
`AuthAttributes` reach them. `UnsignedAttributes`, `UnauthAttributes`, and
114+
`UnprotectedAttributes` remain BER `SET OF`s, but their *elements* must now be
115+
DER-encoded. RFC 5652 permits BER there.
116+
- **`AlgorithmIdentifier`** is DER because `TBSCertificate` reaches it. The
117+
aliases `DigestAlgorithmIdentifier`, `SignatureAlgorithmIdentifier`,
118+
`KeyEncryptionAlgorithmIdentifier`, `ContentEncryptionAlgorithmIdentifier`,
119+
`MessageAuthenticationCodeAlgorithm`, and `KeyDerivationAlgorithmIdentifier`
120+
stay BER as parents, but the algorithm identifier they wrap — including the
121+
one in a BER `SignerInfo` — must be DER-encoded. RFC 5652 permits BER there.
122+
123+
Both narrow the set of accepted encodings; neither accepts anything the RFCs
124+
reject. Splitting the shared definitions in the curated schema would remove them
125+
at the cost of introducing type names that do not appear in the source RFCs.
126+
127+
## Scope
128+
129+
This is a wire schema. It does not express CMS version-selection rules,
130+
algorithm policy, attribute uniqueness, `ANY DEFINED BY` dispatch, or any
131+
cryptographic validation. See [`../scalability.md`](../scalability.md) for how
132+
this module drove the nominal-format and start-domain design.

‎vest_asn1_tests/Makefile‎

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,46 @@ generate:
1212
schema_depth.asn1 -o src/generated_stress_depth.rs
1313
cargo run --manifest-path ../Cargo.toml -p vest_asn1 -- --rules ber \
1414
schema_width.asn1 -o src/generated_stress_width.rs
15+
# CMS is a BER envelope around a DER island. BER is the default because RFC 5652
16+
# section 1 designs every content type for "single pass processing using
17+
# indefinite-length Basic Encoding Rules". Each DER override below is a
18+
# structure whose octets are a signature or digest input, so a parser that must
19+
# not re-encode has to receive it already in DER:
20+
#
21+
# SignedAttributes RFC 5652 5.3 "MUST be DER encoded, even if the rest
22+
# AuthAttributes RFC 5652 9.1 of the structure is BER encoded"
23+
# Certificate RFC 5280 4.1.1.3 signature is over the DER tbsCertificate;
24+
# RFC 5755 7.3 "the DER encoding of the entire PKC"
25+
# CertificateList RFC 5280 5.1.1.3 signature is over the DER tbsCertList
26+
# AttributeCertificate RFC 5755 7.3 same signed-object shape
27+
# AttributeCertificateV1 RFC 5652 12.2 same signed-object shape
28+
#
29+
# The rule propagates to every transitive child, which is what puts the whole
30+
# X.509 subtree (AlgorithmIdentifier, Name, Extensions, GeneralName, ORAddress,
31+
# PersonalName, ...) and Attribute/AttributeValue under DER. ExtendedCertificate
32+
# is deliberately NOT overridden: its closure reaches UnauthAttributes, which CMS
33+
# leaves BER, and RFC 5652 section 10.2.2 declares PKCS #6 extended certificates
34+
# obsolete. See ../vest_asn1/rfcs/README.md for the full derivation.
35+
cargo run --manifest-path ../Cargo.toml -p vest_asn1 -- --rules ber \
36+
--der-definition SignedAttributes \
37+
--der-definition AuthAttributes \
38+
--der-definition Certificate \
39+
--der-definition CertificateList \
40+
--der-definition AttributeCertificate \
41+
--der-definition AttributeCertificateV1 \
42+
../vest_asn1/rfcs/CMS-RFC5652-Curated.asn1 -o src/generated_cms.rs
1543

1644
test: generate
1745
cargo test
1846

47+
# schema_width.asn1 is a deliberate scalability probe: a 16-field BER SEQUENCE
48+
# cycling OPTIONAL, DEFAULT, and required components. Its nominal exec proof
49+
# needs more SMT budget than the Verus default, so this crate raises the ceiling.
50+
# Everything else here verifies well inside the default limit. --rlimit is a
51+
# resource ceiling, not a soundness setting. Keep this value in step with the
52+
# `vest-asn1-tests` entry in .github/workflows/ci.yml.
53+
VERUS_ARGS := --expand-errors --rlimit 100
54+
1955
verify: generate
2056
touch src/lib.rs
21-
cargo verus verify -- --expand-errors
57+
cargo verus verify -- $(VERUS_ARGS)

0 commit comments

Comments
 (0)