Skip to content

fix(kerykeion): accept only the PSK shapes the protocol defines - #436

Merged
forkwright merged 2 commits into
mainfrom
fix/229-psk-index-and-length
Aug 21, 2026
Merged

fix(kerykeion): accept only the PSK shapes the protocol defines#436
forkwright merged 2 commits into
mainfrom
fix/229-psk-index-and-length

Conversation

@forkwright

Copy link
Copy Markdown
Owner

Summary

Two more of #229's clauses: PSK index handling, and ChannelPsk.psk accepting any length.

What the catch-all did

match psk {
    [] => None,
    [n] if *n >= 1 && *n <= 10 => { /* default key, byte 15 = n */ }
    _ => Some(psk.to_vec()),        // ← anything at all becomes a "key"
}

The doc directly above said "16 or 32 bytes → used as-is." The code accepted every length. A single
byte of 0 or 11, a seven-byte blob, a 31-byte near-miss — each became an AES key of its own length.

The effect was a disappearance, not a crash. AES rejected the bad key, the caller continued, and
a misconfigured channel became indistinguishable from one carrying no encryption. Nothing said
otherwise, and the operator saw a quiet channel either way.

Three states, not two

resolve_psk returns Unencrypted / Key / Undefined { len }, so the two cases are distinguishable
at last. The direction matters at the call sites:

  • Receiving logs the undefined case and moves on — a channel that cannot decrypt is not fatal.
  • Sending refuses it. Returning the plaintext there would transmit in the clear on a channel the
    operator believes is encrypted, which is the worse failure of the two.

Undefined carries only the length. The bytes are key material and do not belong in a log line.

The config boundary

ChannelPsk.psk carried the same rule as a comment and accepted any length. It is now checked at
deserialization, so a mistyped key fails in the file where it was written rather than travelling to AES
to be quietly dropped.

The single-byte index shorthand is deliberately not accepted there: that form arrives over the air,
and an operator writing a config means a key. The two contracts differ on purpose, and now each
enforces its own.

Both key lengths are named once in crypto and shared, rather than being literals in two files free to
disagree.

Tests

Seven undefined shapes are rejected with their own length reported — index 0, index 11, index 255, and
lengths 7, 15, 31, 33. Every defined shape still resolves: all ten indices, plus both key lengths. The
config boundary refuses seven wrong lengths and accepts the three documented ones.

Each rejection set has that anti-vacuity partner because a resolver that refused everything would
satisfy all the rejections while silently disabling encryption everywhere.

Refs #229

forkwright added 2 commits August 21, 2026 00:58
resolve_psk matched empty, a single index byte of 1 to 10, and then everything
else fell into `_ => Some(psk.to_vec())`. Its own doc said 16 or 32 bytes; the
code handed AES whatever it was given. A single byte of 0 or 11, a seven-byte
blob, a 31-byte near-miss -- each became a "key" of its own length.

The effect was not a crash but a disappearance. AES rejected the bad key, the
caller skipped that channel, and a misconfigured channel became
indistinguishable from one that carries no encryption. Nothing said otherwise.

resolve_psk now returns three states rather than an Option, so the two are
distinct. Receiving logs the undefined case and moves on; sending refuses it,
because returning the plaintext there would transmit in the clear on a channel
the operator believes is encrypted.

ChannelPsk carried the same rule as a comment and accepted any length. It is now
checked at deserialization, so a mistyped key fails where it was written instead
of travelling to AES. The single-byte index form is deliberately not accepted
there: that shorthand arrives over the air, and an operator writing a config
means a key.

Both accepted key lengths are named once and shared, rather than written as
literals in two files free to disagree.

Refs #229
clippy::useless_vec: a repeat expression with a const length is an array; only
the heterogeneous-length loop above genuinely needs Vec.
@forkwright
forkwright merged commit c04d9a2 into main Aug 21, 2026
11 checks passed
@forkwright
forkwright deleted the fix/229-psk-index-and-length branch August 21, 2026 06:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant