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
111 lines
4.9 KiB
Markdown
111 lines
4.9 KiB
Markdown
---
|
|
id: port-fingerprint
|
|
name: Port fingerprint helpers + DER parser (src/fingerprint.rs)
|
|
status: completed
|
|
depends_on: [crate-init]
|
|
scope: narrow
|
|
risk: low
|
|
impact: component
|
|
level: implementation
|
|
tags: [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
|
|
|
|
- [x] Ported tests green (`cargo test fingerprint`)
|
|
- [x] 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)
|
|
- [x] Malformed-DER inputs yield `None` / SHA fallback (ported edge
|
|
tests)
|
|
- [x] `cargo clippy --all-targets -- -D warnings`, `cargo fmt --check`
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [x] The module compiles without `rustls` in production code
|
|
- [x] Both normalized fingerprint formats are test-pinned
|
|
- [x] `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. |