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
Maintainer thường phản hồi trong vòng 1 ngày
Đánh giá
- Độ khó
- 3/5
- Thời gian dự kiến
- 1-2 ngày
- Mức phù hợp với người mới
- 61/100
Hướng nghiên cứu
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.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
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
- Ngôn ngữ chính
- Rust
- Star
- 344
- Fork
- 196
- Merge trung bình
- 1 ngày 10 giờ
- Pull request đã merge (30 ngày)
- 19
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của 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)Có thể đã có người làm @choksi2212 đã nhận 10 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 77/100
RustCrypto/formats#2486 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
IssuingDistributionPoint has the wrong AssociatedOid (id-pe-subjectInfoAccess)Có thể đã có người làm @eyusd đã nhận 88 ngày trước. Đang mở
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 86/100
RustCrypto/formats#2389 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
RustCrypto/formats#2366 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
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 decodeCó thể làm lại được Pull request cho issue này đã bị đóng mà không được merge. Đang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 45/100
RustCrypto/formats#2485 · 3 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
mcf: `PasswordHash::push_base64` with empty input produces an invalid hashCó thể đã có người làm @choksi2212 đã nhận 8 ngày trước. Đang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 45/100
RustCrypto/formats#2471 ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của RustCrypto/formats
Issue tương tự
-
priority: low ☕️ type: bug 🐛
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
Maintainer thường phản hồi trong vòng 1 ngày
-
area:docs documentation good first issue priority:medium
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 78/100
rtk-ai/rtk#4958 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
feature
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
calagopus/panel#285 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
agent-self
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
yologdev/yoyo-evolve#1117 · 1 bình luận ·
-
[Bug]: streamed Responses API flex_unavailable error maps to 500 InternalServerError instead of 429Đang mởllm translation
Độ khó 2/5 Dưới một giờ Mức phù hợp với người mới 85/100
BerriAI/litellm#49736 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày