Skip to content

The three security profiles share one wire format and one handshake construction

Context and Problem Statement

N-PAMP defines three security profiles — Standard (0x01), High (0x02), Sovereign (0x03). A natural-but-wrong instinct is to treat them as three different protocols and to write a separate handshake binding / spec per profile. Are the profiles distinct protocols, or one protocol parameterized by algorithm strength? This determines whether the handshake binding spec is written once or per profile.

Decision Drivers

  • The handshake binding spec, the key schedule, and the conformance vectors should not be triplicated if the construction is identical.
  • The profiles must still be distinguishable and individually enforceable on the wire.
  • The decision must be grounded in the draft text, not assumed.

Considered Options

  • One wire format + one construction, parameterized per profile (a profile is a row of algorithm parameters plugged into a single binding).
  • Three independent per-profile bindings / specs.

Decision Outcome

Chosen option: one wire format + one construction, parameterized per profile. The draft (§6, "Profile Negotiation") states verbatim: the profiles "share one wire format and differ only in cryptographic primitives and operational requirements. Each is an escalation of the previous." The handshake binding, transcript construction, and key-schedule structure are identical across profiles; only a parameter row changes. Therefore the binding is specified ONCE with a per-profile parameter table.

Verified per-profile invariants (spec/05_profiles.md, derived from draft-bubblefish-npamp-00.md §6):

Property Standard High Sovereign
Minimum KEM X25519MLKEM768 X25519MLKEM1024 X25519MLKEM1024
Allowed signatures Ed25519 Ed25519, ML-DSA-87 ML-DSA-87
KDF hash SHA-256 SHA-384 SHA-384
Per-frame AEAD diversification Off On On
Downgrade refusal Off Refuses Standard Refuses below Sovereign

The single most consequential parameter is the KDF hash (SHA-256 vs SHA-384): it changes secret and transcript-hash lengths, but not the construction — the binding reads Hash = profile.KDFHash().

Consequences

  • Good, because the binding spec, key schedule, and conformance harness are written once and parameterized — no triplication, no drift between per-profile copies.
  • Good, because the profile is carried in the handshake transcript that the Finished MAC covers, so stripping or downgrading a profile invalidates the MAC and aborts (§6).
  • Bad, because length-dependent code paths (hash output size) must be parameter-driven, not hardcoded to SHA-256, or a profile change silently produces wrong-length secrets.

Confirmation

Grounded in spec/05_profiles.md:7-9,21-28 and impl/go/profiles.go (KDFHash(), MinKEM()). Conformance: the profile.check corpus op verifies the KEM-acceptance invariant per profile (a profile MUST NOT accept a KEM below its minimum). The binding spec (forthcoming) will be authored once with the parameter table above.

Pros and Cons of the Options

One construction, parameterized

  • Good, because matches the draft's own statement and avoids triplication.
  • Neutral, because requires parameter-driven lengths.

Three per-profile bindings

  • Good, because each profile's text is self-contained.
  • Bad, because it contradicts §6, triples the spec/vector/proof surface, and invites drift.

More Information

Draft §6 "Profile Negotiation"; spec/05_profiles.md. Note: the Sovereign profile and its ML-KEM-1024 / ML-DSA-87 codepoints are public draft codepoints. Open-edition products implement the Standard row only; the spec defines all three.