Files
alktls/tasks/port-fingerprint.md
T
glm-5.3-flash 4bdc12e84f generation 2: port identity types, fingerprint, pem + signing
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
2026-09-10 13:48:32 +00:00

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
crate-init
narrow low component implementation
fingerprint
port

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 — no rustls:: 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-bits 0x00 + the 32-byte key). These are the RFC 7250 wire facts the parser encodes.
  • extract_ed25519_raw_key_from_spki returns None for non-Ed25519 SPKI / malformed DER / X.509 certs; fingerprint_from_cert_der falls back to SHA-256-hashing the full DER (returns None only 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

  1. Port the module wholesale (it is self-contained).
  2. Port the extracted tests; assert ed25519:<hex> and SHA256:<hex> normalization on representative inputs.
  3. Confirm no rustls:: import outside #[cfg(test)].

Verification

  • Ported tests green (cargo test fingerprint)
  • Round-trip: an Ed25519 SPKI built by signing.rs's spki_public_key() yields ed25519:<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 rustls in production code
  • Both normalized fingerprint formats are test-pinned
  • lib.rs re-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 "returns None only 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 returns Some("SHA256:e3b0c442…") (the hash of the empty slice). The port matches the extraction's actual behavior (always Some); the doc comment on the ported function records the deviation so the stale None claim 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 on identity.rs (concurrent-port constraint held).
  • Production code uses hex::encode (ported verbatim), so hex was added to [dependencies] (it was dev-only; alknet-core does the same).
  • Ported test fingerprint_from_ed25519_spki_matches_iroh_format compares against format!("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.