Files
alktls/tasks/port-identity-types.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

7.1 KiB
Raw Blame History

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
port-identity-types Port identity types — TlsIdentity, Ed25519SecretKey, AcmeDirectory (src/identity.rs) completed
crate-init
narrow low component implementation
identity
types
port

Description

Port the identity types from alknet-core config.rs into src/identity.rs per ADR-005: TlsIdentity (four variants: X509 { cert, key }, RawKey(Ed25519SecretKey), SelfSigned, Acme { domains, cache_dir, directory, contact }), Ed25519SecretKey, and AcmeDirectory (Production / Staging / Custom(String) with url()).

The load-bearing surface (do not change)

  • Ed25519SecretKey: generate(), from_bytes(&[u8; 32]), as_bytes() -> [u8; 32], public() -> ed25519_dalek::VerifyingKey, sign(&self, message) -> ed25519_dalek::Signature. The byte surface is what iroh's iroh_base::SecretKey consumes (ADR-005; verified against iroh 1.1 in Phase 0). Backed by ed25519_dalek::SigningKey.
  • Debug must NOT leak key material (the extracted type formats as Ed25519SecretKey(..) — keep it).
  • AcmeDirectory::Production / Staging URLs are pinned strings (Let's Encrypt production + staging) — assert them in tests.
  • Doc comments carry the OQ-TLS-02 resolution: SelfSigned on the client path presents nothing (documented on the variant).

What moves vs stays

TlsIdentity/Ed25519SecretKey/AcmeDirectory move here wholesale. PeerEntry, AuthPolicy, Identity, fingerprint → peer-id resolution stay OUT (auth layer — ADR-005's carve-out). No serde derives unless the extracted code has them (check; do not add new surface beyond ADR-005's list without noting it).

Work

  1. Port the three types + their inherent methods from crates/alknet-core/src/config.rs.
  2. Port the associated in-module tests (generate/from_bytes round-trip, AcmeDirectory URL assertions).
  3. Add the Debug-no-leak test if not present in the extracted tests.

Verification

  • cargo test -p alktls identity passes (ported tests green)
  • as_bytes/from_bytes round-trip asserted
  • AcmeDirectory URLs asserted (production + staging + custom)
  • Debug output contains no key bytes (test: format then assert hex key absent)
  • cargo clippy --all-targets -- -D warnings, cargo fmt --check

Acceptance Criteria

  • src/identity.rs holds exactly the ADR-005 type set; no auth layer types present
  • The byte surface matches the load-bearing list above verbatim
  • lib.rs re-exports the three types

References

  • docs/architecture/decisions/005-config-types-move-into-alktls.md
  • docs/architecture/decisions/006-module-layout-and-tests.md (module map)
  • Prior art: /workspace/@alkdev/alknet/crates/alknet-core/src/config.rs (lines 3380: Ed25519SecretKey, TlsIdentity, AcmeDirectory)

Notes

Agent fills this during implementation.

Decisions / deviations

  1. rand decision: rand_core = "0.6" + ed25519-dalek's rand_core feature — NOT rand. The alktls lockfile's transitive rand is 0.10.2 (rand_core 0.10.1); ed25519-dalek 2.2.0's SigningKey::generate needs rand_core 0.6.4's CryptoRngCore (its Cargo.toml pins rand_core = "0.6.4", optional). rand 0.10's OsRng only implements rand_core 0.10 traits → incompatible, so rand::rngs::OsRng would not compile against ed25519-dalek 2.2.0. Additionally, generate is gated #[cfg(any(test, feature = "rand_core"))] inside ed25519-dalek — the non-default rand_core feature must be enabled (alknet-core does the same: rand = "0.8" + ed25519-dalek = { features = ["rand_core"] }). Fix: ed25519-dalek = { version = "2", features = ["rand_core"] } + rand_core = { version = "0.6", features = ["getrandom"] } in [dependencies], using rand_core::OsRng. rand_core 0.6.4 + getrandom 0.2.17 were already in the lockfile transitively — no version churn; rand stays out of the dep tree (leaner than the extraction, which depended on rand 0.8).
  2. zeroize::ZeroizeOnDrop not ported (deliberate omission). The extracted Ed25519SecretKey had impl zeroize::ZeroizeOnDrop, but ADR-005's type list doesn't mention it and the task says not to add surface beyond the ADR's list. Note: ed25519-dalek 2.2.0's default features include zeroize, and its SigningKey itself implements ZeroizeOnDrop internally — the wrapped key material is still zeroized on drop through the inner type. Adding the crate-level impl would require a zeroize dep for zero behavioral gain; can be added later if the vault (rewrite's config side) wants the explicit marker impl.
  3. serde derives: none ported. The extracted three types carry no serde derives (checked config.rs lines 3298) — nothing to add, consistent with "no new surface beyond ADR-005's list".
  4. TlsIdentity::Acme's doc comment added (task instruction): server-only, config error (TlsError::AcmeConfig) on the client path — per ADR-001's identity model and the client spec's presentation table.
  5. Sibling-task fix (tree-state deviation, not this port). The working tree contained port-pem-signing's uncommitted work (pem.rs, signing.rs, lib.rs re-exports). Its signing.rs test module was missing use crate::{load_cert_chain, load_private_key}; and pem.rs lacked a trailing newline, which broke cargo test / cargo fmt --check for the whole crate. Two mechanical fixes applied (test-only use line; trailing newline) so whole-crate verification could run; no production code touched.

Summary

Agent fills this on completion. Brief description of what was implemented, files changed, and any follow-up needed.

What landed

  • src/identity.rs: the three ADR-005 types ported verbatim from alknet-core config.rs lines 3298 — Ed25519SecretKey (exact byte surface: generate / from_bytes / as_bytes / public / sign via in-method Signer import; custom Debug with finish_non_exhaustive), AcmeDirectory (pinned production/staging URLs), TlsIdentity (four variants; doc comments carry the OQ-TLS-02 resolution on SelfSigned and the server-only note on Acme).
  • Tests: all five extracted Ed25519SecretKey tests ported (round-trip, sign-verifies, tampered-reject, Debug-no-leak with hex assertion, public-length) + the two extracted TlsIdentity construct tests + a new AcmeDirectory URL-pinning test + an Acme construct test.
  • Cargo.toml: ed25519-dalek gains the rand_core feature; rand_core 0.6 (feature getrandom) added as a dependency.
  • src/lib.rs: pub use identity::{AcmeDirectory, Ed25519SecretKey, TlsIdentity}; added to the incremental re-export block.

Verification

  • cargo test ✓ (20 passed); cargo test -p alktls identity ✓ (9 passed); cargo clippy --all-targets -- -D warnings ✓; cargo fmt --check ✓; cargo check --all-features ✓; cargo test --all-features ✓ (21 passed); cargo clippy --all-targets --all-features -- -D warnings ✓.

Follow-up

  • None for this module. port-client completes the re-export block.