port-identity-types (src/identity.rs): - TlsIdentity (four variants), Ed25519SecretKey, AcmeDirectory ported verbatim from alknet-core config.rs; OQ-TLS-02 + server-only docs on the variants; 9 in-module tests incl. Debug-no-leak - dep decision: rand_core 0.6 (+getrandom) with ed25519-dalek rand_core feature, NOT rand (lockfile rand 0.10/rand_core 0.10 traits are incompatible with ed25519-dalek 2.2's CryptoRngCore); rand stays out of the tree entirely port-fingerprint (src/fingerprint.rs): - fingerprint_from_cert_der, extract_ed25519_raw_key_from_spki, DerParser ported verbatim; production code sha2 + manual DER (+hex for the normalized formats) - 16 tests: 7 ported + 9 new malformed-DER edges (the extraction had none despite the invariant naming them) - empty-input behavior: matches extraction's actual code (always Some via the SHA-256 fallback); doc records the deviation from the stale None claim port-pem-signing (src/pem.rs, src/signing.rs): - load_cert_chain/load_private_key remapped to TlsError::CertLoad per ADR-002; InvalidData no-key path kept - Ed25519SigningKey rewired to crate::identity::Ed25519SecretKey (the one intentional change); rcgen PEM round-trip test added lib.rs re-export block: fingerprint + pem + signing + identity lines landed; server/client/credentials pending their port tasks Verification: cargo test (36), cargo test --all-features (37), clippy -D warnings (default+all-features), fmt --check, feature checks (noq/tcp/acme) — all green
4.9 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | |||
|---|---|---|---|---|---|---|---|---|---|---|---|
| port-fingerprint | Port fingerprint helpers + DER parser (src/fingerprint.rs) | completed |
|
narrow | low | component | implementation |
|
Description
Port the fingerprint module from alknet-core
(crates/alknet-core/src/fingerprint.rs) into src/fingerprint.rs
per ADR-005: fingerprint_from_cert_der(&[u8]) -> Option<String>
(ed25519:<hex> for RFC 7250 Ed25519 SPKI, SHA256:<hex> for
anything else — the normalized formats from alknet ADR-030 §6),
extract_ed25519_raw_key_from_spki(&[u8]) -> Option<[u8; 32]>, and
the private manual DER parser (DerParser).
Invariants
- Production code stays
sha2+ manual DER — norustls::imports in the module's non-test code (the extracted module's purity; ADR-006). - The Ed25519 OID constant is
[0x2b, 0x65, 0x70](1.3.101.112); the SPKI BIT STRING is 33 bytes (one unused-bits0x00+ the 32-byte key). These are the RFC 7250 wire facts the parser encodes. extract_ed25519_raw_key_from_spkireturnsNonefor non-Ed25519 SPKI / malformed DER / X.509 certs;fingerprint_from_cert_derfalls back to SHA-256-hashing the full DER (returnsNoneonly for empty input).- Port the extracted in-module DER parser tests verbatim (they cover the malformed-input edges: truncated headers, long-form lengths, wrong OIDs, bad BIT STRING lengths).
Work
- Port the module wholesale (it is self-contained).
- Port the extracted tests; assert
ed25519:<hex>andSHA256:<hex>normalization on representative inputs. - Confirm no
rustls::import outside#[cfg(test)].
Verification
- Ported tests green (
cargo test fingerprint) - Round-trip: an Ed25519 SPKI built by
signing.rs'sspki_public_key()yieldsed25519:<hex>matching the source key (integration assert — this pins the normalization across the raw-key paths) - Malformed-DER inputs yield
None/ SHA fallback (ported edge tests) cargo clippy --all-targets -- -D warnings,cargo fmt --check
Acceptance Criteria
- The module compiles without
rustlsin production code - Both normalized fingerprint formats are test-pinned
lib.rsre-exports the two public functions
References
- docs/architecture/decisions/005-config-types-move-into-alktls.md
- docs/architecture/decisions/006-module-layout-and-tests.md
- alknet ADR-030 §6 (fingerprint normalization — the reference)
- Prior art:
/workspace/@alkdev/alknet/crates/alknet-core/src/fingerprint.rs
Notes
- Empty-input discrepancy (resolved against the code): the task
description said
fingerprint_from_cert_der"returnsNoneonly for empty input"; the extraction's doc comment claims the same, but its code has no empty-input check — empty input falls through to the SHA-256 fallback and returnsSome("SHA256:e3b0c442…")(the hash of the empty slice). The port matches the extraction's actual behavior (alwaysSome); the doc comment on the ported function records the deviation so the staleNoneclaim is not propagated. - The extraction's test list (7 tests) contained no malformed-DER edge
tests despite the task invariant naming them — the malformed-DER
suite was written fresh for this port: truncated headers, wrong
outer tag, long-form length edges (0x80 indefinite, overlong,
4 bytes, truncated header), a well-formed long-form length acceptance case, wrong-OID-in-valid-SPKI, bad BIT STRING lengths (32/34 bytes, non-zero unused-bits), and malformed-DER SHA fallback.
- Tests construct the raw key bytes directly (fixed test-key arrays)
and build SPKIs via
rustls::sign::public_key_to_spki— no dependency onidentity.rs(concurrent-port constraint held). - Production code uses
hex::encode(ported verbatim), sohexwas added to[dependencies](it was dev-only; alknet-core does the same). - Ported test
fingerprint_from_ed25519_spki_matches_iroh_formatcompares againstformat!("ed25519:{}", hex::encode(raw_key))per the task (the extraction compared a separately generated key; same shape, key sourced directly instead).
Summary
Ported src/fingerprint.rs wholesale from
/workspace/@alkdev/alknet/crates/alknet-core/src/fingerprint.rs:
fingerprint_from_cert_der, extract_ed25519_raw_key_from_spki,
private DerParser (read_tlv, decode_header, expect_sequence,
expect_oid, expect_bit_string). Production code stays sha2 +
manual DER + hex; the only rustls:: use is the test-only SPKI
builder. 16 tests green (7 ported from the extraction with the
crate::config::Ed25519SecretKey::generate() dependency replaced by
fixed key arrays, 9 new/extended edges). lib.rs re-exports both
public functions (concurrent port lines preserved). Verification:
cargo test (36 pass), cargo test --all-features (37 pass),
cargo clippy --all-targets -- -D warnings, cargo fmt --check,
cargo check --all-features — all clean.