Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

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 decode

未关闭
#2,485 3 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

  • #2497 来自 @choksi2212 —— 已关闭,未合并

评估

难度
4/5
预计耗时
3-5 天
新手友好度
45/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
rust
领域
backend, security

调研方向

Start with const-oid’s DEFAULT_MAX_SIZE and parsing in src/lib.rs and src/encoder.rs, then trace DER OID decoding in der/src/asn1/oid.rs and Extension::extn_id in x509-cert’s src/ext.rs. Reproduce the 40-octet case and compare it with the 39-octet control. Done when certificates with the Appendix B sized non-critical extension OID decode successfully and the existing controls still pass.

由索引模型根据 Issue 内容生成。

描述

Hi,

RFC 5280 Appendix B requires support for OIDs whose dotted-decimal form is up to 100 characters, with up to 20 arcs, each below 2^28. ObjectIdentifier's default capacity is 39 octets. A 100-character OID that stays within those limits can need 40, and then it doesn't fit. When that OID is the id of a non-critical extension, Certificate::from_der refuses the whole certificate, although RFC 5280 §4.2 says a non-critical extension a relying party doesn't recognise MAY be ignored.

The OID used is 1.3 followed by nine arcs of 268435455 (2^28 − 1, the largest Appendix B mandates) and then 123456: 12 arcs, 100 characters, 40 contents octets (1 + 9 × 4 + 3). The control swaps the last arc for 16383 (99 characters, 39 octets) and decodes fine. The three test certificates differ only in that extension id, and both controls re-encode to the exact octets they were decoded from, so the DER is canonical and the 40th octet is what fails.

What the reproduction shows

const-oid 0.10.2 + x509-cert 0.3.0 const-oid 0.9.6 + x509-cert 0.2.5
OID, 100 chars / 40 octets new and from_bytes: Err(Length) from_bytes: Err(NotEnoughArcs)
OID, 99 chars / 39 octets (control) Ok Ok
certificate, extension id 1.3.6.1.4.1.99999.1 (control) Ok, re-encodes identically Ok, re-encodes identically
certificate, extension id = the 99-char OID (control) Ok, re-encodes identically Ok, re-encodes identically
certificate, extension id = the 100-char OID Err: incorrect length for OBJECT IDENTIFIER at DER byte 214 Err: incorrect length for OBJECT IDENTIFIER at DER byte 212

Where it comes from

  • const-oid 0.10.2: DEFAULT_MAX_SIZE is 39 (src/lib.rs:48, src/lib.rs:66). The string parser refuses an arc that would end past it (src/encoder.rs:96-102), and from_bytes does the same after checking the arcs (src/lib.rs:121-132 into src/lib.rs:234-254, Error::Length at :240-241).
  • der 0.8.2 decodes an OBJECT IDENTIFIER into a buffer of that capacity and fails when the contents are longer (src/asn1/oid.rs:12-28).
  • x509-cert 0.3.0 and 0.2.5 type Extension::extn_id as the default ObjectIdentifier (src/ext.rs:29-36), and the extensions are a field of the TBS certificate (src/certificate.rs:163-164 in 0.3.0), so one extension id that doesn't fit fails the decode, critical or not.
  • On 0.9.6, from_bytes reports an input that is too long as Error::NotEnoughArcs (src/lib.rs:143-147), which names the wrong problem.
flowchart TD
  cert["Certificate DER, extension id = 1.3 + 9 x 268435455 + 123456<br/>100 characters, 40 octets (RFC 5280 App. B: must be supported)"]
  cert --> a["x509-cert 0.3.0 Certificate::from_der"]
  cert --> b["x509-cert 0.2.5 Certificate::from_der"]
  a --> ext3["Extension.extn_id: ObjectIdentifier<br/>ext.rs:29-36"]
  b --> ext2["Extension.extn_id: ObjectIdentifier<br/>ext.rs:29-36"]
  ext3 --> d8["der 0.8.2 decode_value: buf [u8; 39]<br/>oid.rs:12-28 -> length error"]
  ext2 --> d7["der 0.7.10 decode_value: buf [u8; 39]<br/>oid.rs:12-23 -> length error"]
  d8 --> cap10["const-oid 0.10.2 DEFAULT_MAX_SIZE = 39<br/>lib.rs:48, 66"]
  d7 --> cap9["const-oid 0.9.6 MAX_SIZE = 39<br/>lib.rs:92"]
  cap10 --> fail["Err: the whole certificate is unreadable<br/>(RFC 5280 4.2: a non-critical extension MAY be ignored)"]
  cap9 --> fail
  ctl["control: the same certificate with 39 octets<br/>Ok, re-encodes identically"]
  classDef bad fill:#fde,stroke:#b35
  classDef good fill:#def,stroke:#37b
  class fail bad
  class ctl good

This is related to #1984 but isn't the same thing: that asked for arcs beyond 2^32, which Appendix B doesn't require. This OID is inside Appendix B's limits and is one octet over the default capacity.

What would help, if you agree it's worth fixing: a default capacity that holds every OID Appendix B mandates, or an x509-cert decode path that accepts a larger MAX_SIZE, which const-oid's const parameter already makes possible. I'm happy to send a PR for whichever you prefer.

Reproduction script (self-contained; needs bash, rustup and network access for cargo to fetch the pinned crates)

Save it as repro.sh and run bash repro.sh. It writes a small Cargo project (locked by its own Cargo.lock) into a new temporary directory, builds it with Rust 1.98.0, and compares each observation with the table above. It exits 0 when everything matches. The script is yours to use under Apache-2.0 OR MIT.

#!/usr/bin/env bash
# SPDX-FileCopyrightText: Protoworks Group (Pty) Ltd
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
# Self-contained reproduction (UP-CONSTOID-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.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"

[[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.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
dependencies = [
 "const-oid 0.9.6",
 "der_derive 0.7.3",
 "flagset",
 "zeroize",
]

[[package]]
name = "der"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a878c850e9e421b20262e9b41f9c860e4785fa07541c266b62ff9d1ef998a80a"
dependencies = [
 "const-oid 0.10.2",
 "der_derive 0.8.0",
 "flagset",
 "zeroize",
]

[[package]]
name = "der_derive"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[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.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
dependencies = [
 "base64ct",
 "der 0.7.10",
]

[[package]]
name = "spki"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef958a98b9d5da290cfc78946e9f3e61e1e62a18db0d92cac0b83cc161491a9"
dependencies = [
 "base64ct",
 "der 0.8.2",
]

[[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-constoid-001"
version = "0.0.0"
dependencies = [
 "const-oid 0.10.2",
 "const-oid 0.9.6",
 "x509-cert 0.2.5",
 "x509-cert 0.3.0",
]

[[package]]
name = "x509-cert"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94"
dependencies = [
 "const-oid 0.9.6",
 "der 0.7.10",
 "spki 0.7.3",
]

[[package]]
name = "x509-cert"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "105ef4642d9cb137ef83d623d0e4bf08b8adf69e9918ca904a174adb6d3d038b"
dependencies = [
 "const-oid 0.10.2",
 "der 0.8.2",
 "spki 0.8.1",
]

[[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: Apache-2.0 OR MIT
#

# UP-CONSTOID-001's harness: const-oid 0.10.2 under x509-cert 0.3.0 (the
# newest of each), and const-oid 0.9.6 under x509-cert 0.2.5 (the 0.2
# line).
[package]
name = "up-constoid-001"
version = "0.0.0"
edition = "2021"
publish = false

[dependencies]
const-oid = "=0.10.2"
x509-cert = { version = "=0.3.0", default-features = false }
const-oid-09 = { package = "const-oid", version = "=0.9.6" }
x509-cert-02 = { package = "x509-cert", version = "=0.2.5", default-features = false }

[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: Apache-2.0 OR MIT
//

//! UP-CONSTOID-001: an OID whose dotted-decimal form is 100 characters, as
//! the id of a non-critical extension, through const-oid 0.10.2 / x509-cert
//! 0.3.0 and const-oid 0.9.6 / x509-cert 0.2.5. RFC 5280 Appendix B:
//! "Implementations MUST also support OIDs where the length of the dotted
//! decimal ... string representation can be up to 100 bytes (inclusive)."
//! The certificate is assembled here octet by octet, so nothing of either
//! crate builds it; the control decodes and re-encodes to the same octets.
//! Prints `OBS key=value`.

use x509_cert::der::{Decode as _, Encode as _};
use x509_cert_02::der::{Decode as _, Encode as _};

/// The dotted form: `1.3`, nine `.268435455` (2^28 - 1, the largest arc RFC
/// 5280 Appendix B mandates) and a last arc.
fn dotted(last: u32) -> String {
    let mut s = String::from("1.3");
    for _ in 0..9 {
        s.push_str(".268435455");
    }
    s.push_str(&format!(".{last}"));
    s
}

/// X.690 §8.19 contents octets, written here rather than by either crate.
fn oid_contents(dotted: &str) -> Vec<u8> {
    let arcs: Vec<u64> = dotted.split('.').map(|a| a.parse().expect("digits")).collect();
    let mut out = vec![(arcs[0] * 40 + arcs[1]) as u8];
    for &arc in &arcs[2..] {
        let mut groups = vec![(arc & 0x7f) as u8];
        let mut rest = arc >> 7;
        while rest > 0 {
            groups.push(0x80 | (rest & 0x7f) as u8);
            rest >>= 7;
        }
        groups.reverse();
        out.extend(groups);
    }
    out
}

fn tlv(tag: u8, value: &[u8]) -> Vec<u8> {
    let mut out = vec![tag];
    let n = value.len();
    if n < 0x80 {
        out.push(n as u8);
    } else if n <= 0xff {
        out.extend([0x81, n as u8]);
    } else {
        out.extend([0x82, (n >> 8) as u8, n as u8]);
    }
    out.extend_from_slice(value);
    out
}

fn seq(parts: &[Vec<u8>]) -> Vec<u8> {
    tlv(0x30, &parts.concat())
}

fn oid(dotted: &str) -> Vec<u8> {
    tlv(0x06, &oid_contents(dotted))
}

/// A v3 certificate with one non-critical extension whose id is `ext_id`.
fn certificate(ext_id: &str) -> Vec<u8> {
    let ecdsa_sha256 = seq(&[oid("1.2.840.10045.4.3.2")]);
    let name = seq(&[tlv(0x31, &seq(&[oid("2.5.4.3"), tlv(0x0c, b"up-constoid-001")]))]);
    let validity = seq(&[tlv(0x17, b"250101000000Z"), tlv(0x17, b"350101000000Z")]);
    let mut point = vec![0x00, 0x04];
    point.extend([0x11; 64]);
    let spki = seq(&[
        seq(&[oid("1.2.840.10045.2.1"), oid("1.2.840.10045.3.1.7")]),
        tlv(0x03, &point),
    ]);
    let extension = seq(&[oid(ext_id), tlv(0x04, &[0x05, 0x00])]);
    let tbs = seq(&[
        tlv(0xa0, &tlv(0x02, &[0x02])),
        tlv(0x02, &[0x01]),
        ecdsa_sha256.clone(),
        name.clone(),
        validity,
        name,
        spki,
        tlv(0xa3, &seq(&[extension])),
    ]);
    seq(&[tbs, ecdsa_sha256, tlv(0x03, &[0x00, 0x01, 0x02, 0x03])])
}

fn obs(key: &str, value: impl std::fmt::Display) {
    println!("OBS {key}={value}");
}

fn verdict<T, E: std::fmt::Debug>(r: &Result<T, E>) -> String {
    match r {
        Ok(_) => "Ok".to_owned(),
        Err(e) => format!("Err({e:?})"),
    }
}

/// Decode with both x509-cert lines; on Ok, say whether re-encoding gives
/// back the same octets (it proves the assembled DER is the canonical form).
fn decode_both(label: &str, der: &[u8]) {
    match x509_cert::Certificate::from_der(der) {
        Ok(cert) => {
            obs(&format!("{label}.x509-cert-0.3.0"), "Ok");
            obs(
                &format!("{label}.x509-cert-0.3.0.reencodes-identically"),
                cert.to_der().map(|d| d == der).unwrap_or(false),
            );
        }
        Err(e) => {
            obs(&format!("{label}.x509-cert-0.3.0"), "Err");
            obs(&format!("info.{label}.x509-cert-0.3.0.error"), e);
        }
    }
    match x509_cert_02::Certificate::from_der(der) {
        Ok(cert) => {
            obs(&format!("{label}.x509-cert-0.2.5"), "Ok");
            obs(
                &format!("{label}.x509-cert-0.2.5.reencodes-identically"),
                cert.to_der().map(|d| d == der).unwrap_or(false),
            );
        }
        Err(e) => {
            obs(&format!("{label}.x509-cert-0.2.5"), "Err");
            obs(&format!("info.{label}.x509-cert-0.2.5.error"), e);
        }
    }
}

fn main() {
    // 100 characters, 40 BER octets: the claim.
    let long = dotted(123456);
    // 99 characters, 39 BER octets: the largest the crates hold.
    let fits = dotted(16383);

    obs("oid100.chars", long.len());
    obs("oid100.ber-octets", oid_contents(&long).len());
    obs("oid99.chars", fits.len());
    obs("oid99.ber-octets", oid_contents(&fits).len());

    obs("const-oid-0.10.2.new(oid100)", verdict(&const_oid::ObjectIdentifier::new(&long)));
    obs(
        "const-oid-0.10.2.from_bytes(oid100)",
        verdict(&const_oid::ObjectIdentifier::from_bytes(&oid_contents(&long))),
    );
    obs("const-oid-0.10.2.new(oid99)", verdict(&const_oid::ObjectIdentifier::new(&fits)));
    obs(
        "const-oid-0.9.6.from_bytes(oid100)",
        verdict(&const_oid_09::ObjectIdentifier::from_bytes(&oid_contents(&long))),
    );
    obs(
        "const-oid-0.9.6.from_bytes(oid99)",
        verdict(&const_oid_09::ObjectIdentifier::from_bytes(&oid_contents(&fits))),
    );
    obs("info.const-oid-0.9.6.new(oid100)", verdict(&const_oid_09::ObjectIdentifier::new(&long)));
    obs("info.const-oid-0.9.6.new(oid99)", verdict(&const_oid_09::ObjectIdentifier::new(&fits)));

    decode_both("control", &certificate("1.3.6.1.4.1.99999.1"));
    decode_both("cert-oid99", &certificate(&fits));
    decode_both("cert-oid100", &certificate(&long));
}
__REPRO_FILE_4__
cat >"$dir/repro.sh" <<'__REPRO_FILE_5__'
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Protoworks Group (Pty) Ltd
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
set -euo pipefail

# repro.sh — UP-CONSTOID-001: an OID whose dotted-decimal form is 100
# characters (RFC 5280 Appendix B: "MUST also support OIDs where the length
# of the dotted decimal ... string representation can be up to 100 bytes")
# does not fit const-oid's ObjectIdentifier, in 0.10.2 (the newest, under
# x509-cert 0.3.0) or 0.9.6 (under x509-cert 0.2.5); as the id of a
# non-critical extension it makes the whole certificate undecodable. A
# defensive check over a certificate the harness assembles octet by octet.
#
#   ./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. Lines named
# info.* are printed for the reader and not checked.
#
# 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/ (const-oid 0.10.2 + x509-cert 0.3.0, const-oid 0.9.6 + x509-cert 0.2.5)"
out=$(run_harness) || { note "the harness did not build or run"; exit 2; }
printf '%s\n' "$out" | sed 's/^/    /'

compare oid "$out" <<'EXPECT'
oid100.chars=100
oid100.ber-octets=40
oid99.chars=99
oid99.ber-octets=39
EXPECT

compare const-oid "$out" <<'EXPECT'
const-oid-0.10.2.new(oid100)=Err(Length)
const-oid-0.10.2.from_bytes(oid100)=Err(Length)
const-oid-0.10.2.new(oid99)=Ok
const-oid-0.9.6.from_bytes(oid100)=Err(NotEnoughArcs)
const-oid-0.9.6.from_bytes(oid99)=Ok
EXPECT

# The controls: the assembled certificate is canonical DER, and the
# extension id is the only thing that changes between the three.
compare control "$out" <<'EXPECT'
control.x509-cert-0.3.0=Ok
control.x509-cert-0.3.0.reencodes-identically=true
control.x509-cert-0.2.5=Ok
control.x509-cert-0.2.5.reencodes-identically=true
cert-oid99.x509-cert-0.3.0=Ok
cert-oid99.x509-cert-0.3.0.reencodes-identically=true
cert-oid99.x509-cert-0.2.5=Ok
cert-oid99.x509-cert-0.2.5.reencodes-identically=true
EXPECT

compare claim "$out" <<'EXPECT'
cert-oid100.x509-cert-0.3.0=Err
cert-oid100.x509-cert-0.2.5=Err
EXPECT

finish UP-CONSTOID-001
__REPRO_FILE_5__
chmod +x "$dir/repro.sh"
cd "$dir" && ./repro.sh
Recorded run (macOS arm64, rustc 1.98.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/ (const-oid 0.10.2 + x509-cert 0.3.0, const-oid 0.9.6 + x509-cert 0.2.5)
  ok   oid: oid100.chars                                            100
  ok   oid: oid100.ber-octets                                       40
  ok   oid: oid99.chars                                             99
  ok   oid: oid99.ber-octets                                        39
  ok   const-oid: const-oid-0.10.2.new(oid100)                      Err(Length)
  ok   const-oid: const-oid-0.10.2.from_bytes(oid100)               Err(Length)
  ok   const-oid: const-oid-0.10.2.new(oid99)                       Ok
  ok   const-oid: const-oid-0.9.6.from_bytes(oid100)                Err(NotEnoughArcs)
  ok   const-oid: const-oid-0.9.6.from_bytes(oid99)                 Ok
  ok   control: control.x509-cert-0.3.0                             Ok
  ok   control: control.x509-cert-0.3.0.reencodes-identically       true
  ok   control: control.x509-cert-0.2.5                             Ok
  ok   control: control.x509-cert-0.2.5.reencodes-identically       true
  ok   control: cert-oid99.x509-cert-0.3.0                          Ok
  ok   control: cert-oid99.x509-cert-0.3.0.reencodes-identically    true
  ok   control: cert-oid99.x509-cert-0.2.5                          Ok
  ok   control: cert-oid99.x509-cert-0.2.5.reencodes-identically    true
  ok   claim: cert-oid100.x509-cert-0.3.0                           Err
  ok   claim: cert-oid100.x509-cert-0.2.5                           Err

repro: all checks held: UP-CONSTOID-001 is shown as the report states it
主要语言
Rust
星标
344
派生
196
平均合并
1 天 17 小时
30 天内合并 PR
24

环境准备

这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

RustCrypto/formats 的其他 Issue

查看 RustCrypto/formats 的全部 Issue

相似的 Issue

更多 Rust Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。