SIGN IN SIGN UP

refactor(crypto): the key decode is the hex crate's, and now it is proven so

Found by sweeping for the pattern that produced the repair-branch defect one
commit ago: a Zig workaround ported as though it were the design. Zig hand-
writes hex decoding because it has nothing to call; `hex` has been a workspace
dependency the whole time, used by `afd_auth` for credential digests and by
`afd_fleet` for minted token bodies, and `afd_crypto` alone hand-rolled the
inverse — eighteen lines and a nibble table, on the path that parses the
master key.

The order here is the point. The two behaviours a swap could silently change
had NO coverage: every existing hex test was the wrong LENGTH, so the digit
check was never exercised, and nothing asserted that uppercase is accepted.
Both were pinned against the hand-rolled decoder FIRST — they pass — and only
then was the implementation replaced. The suite that proves the swap is
therefore a suite that ran green on the old code, which is the only version of
this change worth trusting.

Uppercase acceptance is deliberate and now says so: an operator pasting a key
from a tool that renders uppercase must not get a daemon that refuses to boot.
That is the opposite of the identifier rule elsewhere in this workspace, which
rejects uppercase — because an identifier has a canonical stored form to keep
unique and key material does not.

`decode_to_slice`, not `decode`: it writes straight into the caller's buffer,
so no copy of a key is left on the heap without a destructor. The length check
stays hand-written because `hex` reports a wrong length as one opaque variant
while `KeyHexLength` carries expected and actual — the difference between an
operator seeing "bad key" and seeing that they pasted 63 characters.

afd_crypto suites all green, known-answer tests included.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
K
Kishore Kumar committed
79bbcf646123fa39a2063ff6b3495b83696fbf59
Parent: 7ddb6dc