hpke

package
v0.24.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 4, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package hpke implements the subset of RFC 9180 (Hybrid Public Key Encryption) that device envelopes need: mode_auth with DHKEM(X25519, HKDF-SHA256), HKDF-SHA256 and ChaCha20Poly1305, single-shot at sequence 0.

It is deliberately not a general HPKE library. There is one ciphersuite, one mode, no PSK, no exporter surface, and no multi-message context: a caller seals exactly one message per encapsulation. Anything outside that shape belongs in a real HPKE library, not here.

Why mode_auth

mode_auth (0x02) is the mode where the sender contributes BOTH a fresh ephemeral key and its long-term static key. DHKEM's AuthEncap derives the shared secret from DH(skE, pkR) || DH(skS, pkR), so only a holder of the sender's long-term private key can produce a shared secret that the recipient's AuthDecap(enc, skR, pkS) reproduces. Sender authentication is therefore implicit and deniable — no signature, and no signing key, which matters because X25519 identity keys cannot sign.

Forward secrecy is one-sided

The ephemeral is the SENDER's. The recipient contributes a static key to both DHs, so an attacker who later steals the recipient's long-term private key can still recompute the shared secret for captured ciphertexts. Closing that half needs a recipient ephemeral — an interactive handshake or published one-time prekeys — which a one-shot stateless envelope does not admit.

All byte strings are RFC 9180's own serializations; the implementation follows §4.1 (LabeledExtract/LabeledExpand), §4.1 and §5.1 (key schedule) and §7.1.2 (DHKEM(X25519)) literally, and is checked against the CFRG test vectors in testdata.

Index

Constants

View Source
const (

	// KeySize is the length of an X25519 public key, private scalar, and
	// therefore of an encapsulated key (enc).
	KeySize = 32
)

Suite parameter lengths, in bytes (RFC 9180 §7.1, §7.2, §7.3).

Variables

View Source
var ErrKeySize = errors.New("hpke: wrong key length")

ErrKeySize reports a key or encapsulation of the wrong length. It is structural, not cryptographic: it says the caller passed the wrong number of bytes, which is a bug in the caller and reveals nothing about any secret.

View Source
var ErrOpen = errors.New("hpke: open failed")

ErrOpen reports that OpenAuth failed somewhere after its inputs were checked for length: decapsulation, the key schedule, or the AEAD. It deliberately carries no detail about which — see OpenAuth on why.

Functions

func OpenAuth

func OpenAuth(recipientPriv, senderPub, enc, info, aad, ciphertext []byte) ([]byte, error)

OpenAuth decapsulates enc with recipientPriv in mode_auth, authenticating it as coming from senderPub, and opens ciphertext at sequence 0.

The error contract, exactly

There are two error classes and the split is drawn at input validation:

  • LENGTH is checked first, before any key material is touched, and a wrong length is reported as ErrKeySize. That is a structural bug in the caller and says nothing about a secret.
  • EVERYTHING after that — a point crypto/ecdh rejects, a low-order or all-zero encapsulation, a shared secret that does not match, a key schedule that will not derive, a forged tag — is exactly ErrOpen and nothing more. No wrapping, no cause, no distinguishable message.

The second half is the point: an attacker who can feed this function chosen bytes learns one bit, "it did not open", from every one of those failures. Reporting which step failed would turn the package into a decryption oracle — low-order encapsulations in particular are attacker-supplied, and a distinct error for them would confirm the recipient key reached ECDH at all.

func SealAuth

func SealAuth(rand io.Reader, recipientPub, senderPriv, info []byte, aadFor func(enc []byte) []byte, plaintext []byte) (enc, ciphertext []byte, err error)

SealAuth encapsulates to recipientPub in mode_auth as senderPriv and seals plaintext under the resulting key schedule at sequence 0. It returns the encapsulated ephemeral public key (enc) and the AEAD ciphertext.

rand supplies the 32-byte ephemeral private scalar; it must be crypto/rand.Reader outside tests. The ephemeral private key never leaves this function.

aadFor builds the AEAD associated data and is a function, not a []byte, because a caller that binds enc into its AAD — as device envelopes do — cannot know enc until encapsulation has happened. Whatever it returns is passed to the AEAD verbatim; a caller with fixed associated data can ignore the argument. It is called exactly once, after encapsulation.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL