x509-cert 0.3.0 declares RFC 5914's TrustAnchorInfo v1 as 0, so it encodes v1 as INTEGER 0 and reads a written version 0 as v1
I maintainer di solito rispondono entro 1 giorno
Valutazione
- Difficoltà
- 3/5
- Tempo stimato
- 1-2 giorni
- Idoneità per principianti
- 61/100
Direzione di ricerca
Start with x509-cert/src/anchor.rs, especially the Version declaration and the TrustAnchorInfo.version default; the issue identifies both as the source of the mismatch. Read the RFC 5914 version rule and reproduce the reported encode and decode cases with the supplied harness/ and repro.sh. Done means v1 follows the RFC’s integer value and the reproduction checks pass.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Hi,
Details, the source lines, and a self-contained reproduction are below.
Versions pinned
| Thing | Pin |
|---|---|
| x509-cert | 0.3.0, the newest release on crates.io (read 2026-10-21), over der 0.8; the .crate checksums are in harness/Cargo.lock |
| x509-cert, as we use it | 0.2.5, the same lines (src/anchor.rs:17-21, :[email protected]); cited, not run |
| x509-cert master | 13a0390d (2026-10-13), the same declaration (x509-cert/src/anchor.rs:19-22, :36, :43@13a0390d); read 2026-10-21, not run |
| the input | one anchor (an Ed25519 key, a one-octet keyId) with its version left out, and written as 0, 1 and 2, in harness/src/main.rs |
| toolchain | rustc 1.98.0 (the bundled rust-toolchain.toml) |
The rule
RFC 5914 section 2:
version TrustAnchorInfoVersion DEFAULT v1,TrustAnchorInfoVersion ::= INTEGER { v1(1) }- §2.1: "version identifies the version of TrustAnchorInfo. ... However,
the default value, v1, cannot be changed."
So v1 is the integer 1. A DER encoding of a v1 anchor leaves version out,
because it is the default.
What x509-cert 0.3.0 does
anchor::Versionis anEnumeratedINTEGER with one value,V1 = 0
(src/anchor.rs:[email protected]).- The same file's doc comment quotes the RFC's
TrustAnchorInfoVersion ::= INTEGER { v1(1) }
(src/anchor.rs:[email protected]). TrustAnchorInfo.versiontakes that type with
#[asn1(default = "Default::default")]
(src/anchor.rs:[email protected]). An anchor without the field
decodes asV1and is written back without it, which is right. The value
V1stands for is not.
What the run shows
repro.sh builds harness/ against x509-cert 0.3.0 at its locked checksum
and compares each OBS line with the claim table in its EXPECT blocks:
| Input or call | x509-cert 0.3.0 | RFC 5914 |
|---|---|---|
Version::V1, encoded |
02 01 00 (INTEGER 0) |
02 01 01 (INTEGER 1) |
an anchor that writes version 0 |
accepted as V1, and written back without the field |
0 is not v1 |
an anchor that writes version 1 |
refused: "malformed ASN.1 DER value for INTEGER" | v1, written out where DER leaves it out |
an anchor that writes version 2 |
refused, the same way | a version this RFC does not define |
an anchor without version |
V1, written back the same |
v1 |
The controls are the last two rows. The claim is the first two rows: v1 is
encoded as 0, and a written 0 is read as v1 and then lost on re-encoding.
Not claimed
- That an anchor with
version 1written out must be accepted. Under
DER it must not be, since DER leaves a default out. The run records only
that x509-cert refuses it for being outside its INTEGER values, not for
being a written-out default. - Any effect on certificates.
TrustAnchorInfois the only type
involved. x509-cert'scertificate::Versionis not part of this entry. - Anything about other x509-cert releases than 0.2.5 and 0.3.0. 0.2.5
and master at13a0390dhave the same declaration and doc comment, cited
above but not run.
The path
flowchart TD
spec["RFC 5914 section 2<br/>TrustAnchorInfoVersion ::= INTEGER { v1(1) }<br/>version ... DEFAULT v1<br/>rfc5914...txt:149, 158, 162-165"]
decl["anchor::Version { V1 = 0 }<br/>anchor.rs:[email protected]<br/>its doc comment quotes v1(1), anchor.rs:36"]
spec -. "v1 is 1" .-> decl
decl --> enc["Version::V1 encoded: 02 01 00"]
w0["anchor writing version 0"] --> dec["TrustAnchorInfo::from_der<br/>anchor.rs:43-44, default = V1"]
dec --> acc["accepted as V1<br/>written back without the field: the 0 is lost"]
w1["anchor writing version 1"] --> dec
dec --> ref["refused: malformed INTEGER<br/>(right under DER, for another reason)"]
absent["anchor without version"] --> dec
dec --> ok["V1, written back the same"]
classDef bad fill:#fde,stroke:#b35
classDef good fill:#def,stroke:#37b
class enc,acc bad
class ok good
Reproduction script (self-contained)
Save it as repro.sh and run bash repro.sh. It writes the files it needs into a new temporary directory and runs the check there; it exits 0 when every observation matches this report. The script is yours to use under MIT OR Apache-2.0.
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Protoworks Group (Pty) Ltd
# SPDX-License-Identifier: MIT OR Apache-2.0
#
# Self-contained reproduction (UP-X509CERT-001). It writes the files below into a new
# temporary directory and runs repro.sh there; nothing outside that
# directory is written. Run it with: bash this-file.sh
set -euo pipefail
dir=$(mktemp -d "${TMPDIR:-/tmp}/repro.XXXXXX")
echo "files written to $dir"
cat >"$dir/rust-toolchain.toml" <<'__REPRO_FILE_1__'
[toolchain]
channel = "1.98.0"
__REPRO_FILE_1__
mkdir -p "$dir/harness"
cat >"$dir/harness/Cargo.lock" <<'__REPRO_FILE_2__'
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "base64ct"
version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
[[package]]
name = "const-oid"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
[[package]]
name = "der"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a878c850e9e421b20262e9b41f9c860e4785fa07541c266b62ff9d1ef998a80a"
dependencies = [
"const-oid",
"der_derive",
"flagset",
"zeroize",
]
[[package]]
name = "der_derive"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59600e2c2d636fde9b65e99cc6445ac770c63d3628195ff39932b8d6d7409903"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "flagset"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe"
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "spki"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef958a98b9d5da290cfc78946e9f3e61e1e62a18db0d92cac0b83cc161491a9"
dependencies = [
"base64ct",
"der",
]
[[package]]
name = "syn"
version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
[[package]]
name = "up-x509cert-001"
version = "0.0.0"
dependencies = [
"der",
"x509-cert",
]
[[package]]
name = "x509-cert"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "105ef4642d9cb137ef83d623d0e4bf08b8adf69e9918ca904a174adb6d3d038b"
dependencies = [
"const-oid",
"der",
"spki",
]
[[package]]
name = "zeroize"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879"
__REPRO_FILE_2__
mkdir -p "$dir/harness"
cat >"$dir/harness/Cargo.toml" <<'__REPRO_FILE_3__'
# SPDX-FileCopyrightText: Protoworks Group (Pty) Ltd
# SPDX-License-Identifier: MIT OR Apache-2.0
#
# UP-X509CERT-001's harness: x509-cert 0.3.0 (the newest release) decoding
# one RFC 5914 TrustAnchorInfo with its version left out and written as 0, 1
# and 2, and encoding anchor::Version::V1, through its public API only.
[package]
name = "up-x509cert-001"
version = "0.0.0"
edition = "2021"
publish = false
[dependencies]
x509-cert = { version = "=0.3.0", default-features = false }
der = { version = "0.8", features = ["alloc"] }
[workspace]
__REPRO_FILE_3__
mkdir -p "$dir/harness/src"
cat >"$dir/harness/src/main.rs" <<'__REPRO_FILE_4__'
// SPDX-FileCopyrightText: Protoworks Group (Pty) Ltd
// SPDX-License-Identifier: MIT OR Apache-2.0
//
//! UP-X509CERT-001: RFC 5914 has `TrustAnchorInfoVersion ::= INTEGER { v1(1) }`
//! and `version TrustAnchorInfoVersion DEFAULT v1`. One anchor (an Ed25519
//! key, a one-octet keyId) is written here with no version and with a version
//! of 0, 1 and 2, and decoded by x509-cert; `Version::V1` is encoded on its
//! own. Prints `OBS key=value`. Nothing is fetched.
use der::{Decode, Encode};
use x509_cert::anchor::{TrustAnchorInfo, Version};
use x509_cert::certificate::Rfc5280;
fn hex(octets: &[u8]) -> String {
octets.iter().map(|o| format!("{o:02x}")).collect()
}
/// The anchor's DER, with `version` written as `INTEGER version` when given.
fn anchor(version: Option<u8>) -> Vec<u8> {
let mut fields = Vec::new();
if let Some(v) = version {
fields.extend_from_slice(&[0x02, 0x01, v]);
}
// SubjectPublicKeyInfo: id-Ed25519 (RFC 8410) and a 32-octet key.
fields.extend_from_slice(&[0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00]);
fields.extend_from_slice(&[0x11; 32]);
// keyId: OCTET STRING of one octet.
fields.extend_from_slice(&[0x04, 0x01, 0xaa]);
let mut der = vec![0x30, u8::try_from(fields.len()).expect("short form")];
der.extend_from_slice(&fields);
der
}
fn main() {
for (name, version) in [("absent", None), ("written-0", Some(0)), ("written-1", Some(1)), ("written-2", Some(2))] {
match TrustAnchorInfo::<Rfc5280>::from_der(&anchor(version)) {
Ok(info) => {
println!("OBS decode.{name}=accepted as {:?}", info.version);
let again = info.to_der().expect("DER");
println!("OBS decode.{name}.reencoded-without-version={}", again == anchor(None));
}
Err(error) => println!("OBS decode.{name}=refused: {error}"),
}
}
println!("OBS encode.Version::V1={}", hex(&Version::V1.to_der().expect("DER")));
}
__REPRO_FILE_4__
cat >"$dir/repro.sh" <<'__REPRO_FILE_5__'
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Protoworks Group (Pty) Ltd
# SPDX-License-Identifier: MIT OR Apache-2.0
#
set -euo pipefail
# repro.sh — UP-X509CERT-001: x509-cert 0.3.0 (the newest release) declares
# the RFC 5914 TrustAnchorInfoVersion v1 as 0, where the RFC's module has
# INTEGER { v1(1) }. It encodes Version::V1 as INTEGER 0, and it accepts an
# anchor that writes version 0, which is not v1, as v1 and drops the field
# when it writes the anchor again.
#
# ./repro.sh
#
# What it does: builds harness/ (locked by harness/Cargo.lock) with the
# toolchain rust-toolchain.toml pins, runs it, and compares each
# `OBS key=value` with EXPECT, the claim table of the report.
#
# Network: cargo's fetch of the crates harness/Cargo.lock pins. Nothing else.
#
# Exit status: 0 when every check holds (the defect is shown); otherwise the
# number of checks that disagreed; 2 if the harness could not be built or run.
#
# Dependencies: bash, awk, cargo and rustup (1.98.0).
here=$(cd "$(dirname "$0")" && pwd)
cd "$here"
fail=0
note() { echo "repro: $*"; }
check() { # check <label> <want> <got>
if [[ "$2" == "$3" ]]; then
printf ' ok %-60s %s\n' "$1" "$3"
else
printf ' FAIL %-60s got %s, want %s\n' "$1" "$3" "$2"
fail=$((fail + 1))
fi
}
if ! command -v cargo >/dev/null 2>&1; then
note "cargo is not installed; cannot run"
exit 2
fi
target=$(mktemp -d "${TMPDIR:-/tmp}/up-ev.XXXXXX")
trap 'rm -rf "$target"' EXIT
# The toolchain is the one the repository's rust-toolchain.toml pins.
rustc_version=$(cd "$here/harness" && rustc --version)
note "toolchain: $rustc_version"
check "rustc is the pinned 1.98.0" "rustc 1.98.0" "$(cut -d' ' -f1-2 <<<"$rustc_version")"
run_harness() { # run_harness -> the harness's stdout
(cd "$here/harness" && CARGO_TARGET_DIR="$target" cargo run --locked --quiet --release)
}
compare() { # compare <label> <harness output> ; reads key=value lines on stdin
local label="$1" out="$2" key want got
while IFS='=' read -r key want; do
[[ -z "$key" || "$key" == \#* ]] && continue
got=$(awk -v k="OBS ${key}=" 'index($0, k) == 1 { print substr($0, length(k) + 1); exit }' <<<"$out")
[[ -n "$got" ]] || got="(not printed)"
check "$label: $key" "$want" "$got"
done
}
finish() { # finish <id>
echo
if [[ "$fail" -eq 0 ]]; then
note "all checks held: $1 is shown as the report states it"
exit 0
fi
note "$fail checks disagreed with the claim"
exit "$fail"
}
note "building and running harness/ (x509-cert 0.3.0)"
out=$(run_harness) || { note "the harness did not build or run"; exit 2; }
printf '%s\n' "$out" | sed 's/^/ /'
# The controls: an anchor that leaves the version out is v1 and is written
# back as it came, and versions RFC 5914 does not define are refused.
compare control "$out" <<'EXPECT'
decode.absent=accepted as V1
decode.absent.reencoded-without-version=true
decode.written-2=refused: malformed ASN.1 DER value for INTEGER
EXPECT
# The claim: v1 is encoded as INTEGER 0 (RFC 5914: v1(1), so 020101), and an
# anchor that writes version 0 is accepted as v1 and written back without
# the field, so the 0 it carried is lost.
compare claim "$out" <<'EXPECT'
encode.Version::V1=020100
decode.written-0=accepted as V1
decode.written-0.reencoded-without-version=true
EXPECT
finish UP-X509CERT-001
__REPRO_FILE_5__
chmod +x "$dir/repro.sh"
cd "$dir" && ./repro.sh
Recorded run
date: 2026-10-21T20:32:36Z
arch: Darwin arm64
tools: bash 5.3.20(1)-release; git version 2.56.0; docker client 29.9.0; shellcheck 0.11.0
repro: toolchain: rustc 1.98.0 (88d9e12ae 2026-08-18)
ok rustc is the pinned 1.98.0 rustc 1.98.0
repro: building and running harness/ (x509-cert 0.3.0)
OBS decode.absent=accepted as V1
OBS decode.absent.reencoded-without-version=true
OBS decode.written-0=accepted as V1
OBS decode.written-0.reencoded-without-version=true
OBS decode.written-1=refused: malformed ASN.1 DER value for INTEGER
OBS decode.written-2=refused: malformed ASN.1 DER value for INTEGER
OBS encode.Version::V1=020100
ok control: decode.absent accepted as V1
ok control: decode.absent.reencoded-without-version true
ok control: decode.written-2 refused: malformed ASN.1 DER value for INTEGER
ok claim: encode.Version::V1 020100
ok claim: decode.written-0 accepted as V1
ok claim: decode.written-0.reencoded-without-version true
repro: all checks held: UP-X509CERT-001 is shown as the report states it
exit: 0
- Lingua principale
- Rust
- Stelle
- 344
- Fork
- 196
- Merge medio
- 1g 17h
- PR unite (30g)
- 24
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di RustCrypto/formats
-
pem-rfc7468: a lone CR before -----BEGIN is refused as Error::Preamble, so a CR-terminated multi-certificate PEM fails load_pem_chain (RFC 7468 eol = CRLF / CR / LF)Forse già presa @choksi2212 l’ha presa 6 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 77/100
RustCrypto/formats#2486 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
IssuingDistributionPoint has the wrong AssociatedOid (id-pe-subjectInfoAccess)Forse già presa @eyusd l’ha presa 84 giorni fa. Aperta
Difficoltà 1/5 1-3 ore Idoneità per principianti 86/100
RustCrypto/formats#2389 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
RustCrypto/formats#2366 ·
I maintainer di solito rispondono entro 1 giorno
-
x509-cert/const-oid: a 100-character OID (RFC 5280 Appendix B) needs 40 octets, so a certificate carrying one as a non-critical extension id does not decodeForse di nuovo libera Una pull request per questa issue è stata chiusa senza essere unita. Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 45/100
RustCrypto/formats#2485 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
mcf: `PasswordHash::push_base64` with empty input produces an invalid hashForse già presa @choksi2212 l’ha presa 4 giorni fa. Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 45/100
RustCrypto/formats#2471 ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di RustCrypto/formats
Issue simili
-
good first issue help wanted r-docs
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
servo/rust-smallvec#824 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
superradcompany/microsandbox#1974 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 83/100
I maintainer di solito rispondono entro 1 giorno
-
priority middle
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 62/100
KATO-Hiro/AtCoderClans#12940 ·
I maintainer di solito rispondono entro 1 giorno