Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

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

Aperta
#2,487 2 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

@choksi2212 ci sta già lavorando.

Dal 24/10/2026.

  • #2489 di @choksi2212 — aperta

Valutazione

Difficoltà
3/5
Tempo stimato
1-2 giorni
Idoneità per principianti
61/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
rust
Ambito
backend

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::Version is an Enumerated INTEGER 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.version takes that type with
    #[asn1(default = "Default::default")]
    (src/anchor.rs:[email protected]). An anchor without the field
    decodes as V1 and is written back without it, which is right. The value
    V1 stands 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 1 written 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. TrustAnchorInfo is the only type
    involved. x509-cert's certificate::Version is not part of this entry.
  • Anything about other x509-cert releases than 0.2.5 and 0.3.0. 0.2.5
    and master at 13a0390d have 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

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di RustCrypto/formats

Tutte le issue di RustCrypto/formats

Issue simili

Altre issue su Rust

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.