hpke

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Feb 16, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

README

Hybrid Public Key Encryption (HPKE)

This package implements Hybrid Public Key Encryption as specified in RFC 9180. HPKE provides public-key encryption of arbitrary-sized plaintexts with optional sender authentication.

For ECC-based encryption where threshold decryption is not required, this package should be used.

Overview

HPKE combines an asymmetric Key Encapsulation Mechanism (KEM), a Key Derivation Function (KDF), and an Authenticated Encryption with Associated Data (AEAD) algorithm to provide hybrid public-key encryption.

HPKE Modes

HPKE supports four modes with different authentication properties:

Mode Value Sender Authentication Description
Base 0x00 None Encryption to a public key without sender authentication
PSK 0x01 Pre-shared key Both parties share a secret key; recipient verifies sender possessed PSK
Auth 0x02 Asymmetric key Sender's private key authenticates; provides non-repudiation
AuthPSK 0x03 Both Combines PSK and asymmetric authentication for defence in depth
Supported Algorithms

KEM (Key Encapsulation Mechanism):

ID Algorithm Nsecret Nenc Npk Nsk
0x0010 DHKEM(P-256, HKDF-SHA256) 32 65 65 32
0x0020 DHKEM(X25519, HKDF-SHA256) 32 32 32 32

KDF (Key Derivation Function):

ID Algorithm Nh
0x0001 HKDF-SHA256 32
0x0003 HKDF-SHA512 64

AEAD (Authenticated Encryption):

ID Algorithm Nk Nn Nt
0x0001 AES-128-GCM 16 12 16
0x0002 AES-256-GCM 32 12 16
0x0003 ChaCha20Poly1305 32 12 16
0xFFFF Export-only N/A N/A N/A

Usage

This package provides two APIs:

  1. High-Level API (Scheme.Encrypter/Scheme.Decrypter): Simpler interface for common use cases. Good for single-message encryption where you don't need fine-grained control over contexts.

  2. RFC 9180 API (SetupBaseS/SetupBaseR, etc.): Direct implementation of the RFC specification. Provides access to sender/receiver contexts for multi-message encryption, secret export, and full control over the HPKE flow.

Setup

Both APIs require a cipher suite and scheme:

import (
    "crypto/rand"
    "github.com/bronlabs/bron-crypto/pkg/base/curves/p256"
    "github.com/bronlabs/bron-crypto/pkg/encryption/hpke"
)

// Create cipher suite
suite, _ := hpke.NewCipherSuite(
    hpke.DHKEM_P256_HKDF_SHA256,
    hpke.KDF_HKDF_SHA256,
    hpke.AEAD_AES_128_GCM,
)

// Create scheme with P-256 curve
curve := p256.NewCurve()
scheme, _ := hpke.NewScheme(curve, suite)
Key Generation

Generate key pairs using the scheme's key generator:

kg, _ := scheme.Keygen()

// Generate random key pair
sk, pk, _ := kg.Generate(rand.Reader)

// Or derive deterministically from seed (at least 32 bytes)
seed := make([]byte, 32)
rand.Read(seed)
sk, pk, _ := kg.GenerateWithSeed(seed)

High-Level API

The high-level API provides Encrypter and Decrypter types that handle context management internally. Use this when you need simple encrypt/decrypt operations.

Base Mode (No Authentication)
// Create encrypter
encrypter, _ := scheme.Encrypter()

// Encrypt
ciphertext, capsule, _ := encrypter.Encrypt(plaintext, receiverPk, rand.Reader)

// Create decrypter
decrypter, _ := scheme.Decrypter(receiverSk,
    hpke.DecryptingWithCapsule(capsule),
)

// Decrypt
plaintext, _ := decrypter.Decrypt(ciphertext)
Auth Mode (Asymmetric Key Authentication)
// Create encrypter with sender authentication
encrypter, _ := scheme.Encrypter(
    hpke.EncryptingWithAuthentication(senderSk),
)

ciphertext, capsule, _ := encrypter.Encrypt(plaintext, receiverPk, rand.Reader)

// Create decrypter that verifies sender
decrypter, _ := scheme.Decrypter(receiverSk,
    hpke.DecryptingWithCapsule(capsule),
    hpke.DecryptingWithAuthentication(senderPk),
)

plaintext, _ := decrypter.Decrypt(ciphertext)
With Application Info

Bind application-specific context to the encryption:

info := []byte("my-application-context")

encrypter, _ := scheme.Encrypter(
    hpke.EncryptingWithApplicationInfo(info),
)

decrypter, _ := scheme.Decrypter(receiverSk,
    hpke.DecryptingWithCapsule(capsule),
    hpke.DecryptingWithApplicationInfo(info), // Must match sender
)

RFC 9180 API

The RFC API provides direct access to sender and receiver contexts as defined in the specification. Use this when you need:

  • Multi-message encryption with a single context
  • Secret export functionality
  • Fine-grained control over the HPKE flow
Base Mode
// Sender: establish context and encrypt
senderCtx, _ := hpke.SetupBaseS(suite, receiverPk, info, rand.Reader)
ciphertext, _ := senderCtx.Seal(plaintext, aad)
capsule := senderCtx.Capsule // Send capsule + ciphertext to receiver

// Receiver: establish context and decrypt
receiverCtx, _ := hpke.SetupBaseR(suite, receiverSk, capsule, info)
plaintext, _ := receiverCtx.Open(ciphertext, aad)
PSK Mode
psk := make([]byte, 32) // At least 32 bytes of entropy
rand.Read(psk)
pskId := []byte("my-psk-identifier")

// Sender
senderCtx, _ := hpke.SetupPSKS(suite, receiverPk, psk, pskId, info, rand.Reader)
ciphertext, _ := senderCtx.Seal(plaintext, aad)

// Receiver (must have same PSK)
receiverCtx, _ := hpke.SetupPSKR(suite, receiverSk, capsule, psk, pskId, info)
plaintext, _ := receiverCtx.Open(ciphertext, aad)
Auth Mode
// Sender uses their private key for authentication
senderCtx, _ := hpke.SetupAuthS(suite, receiverPk, senderSk, info, rand.Reader)
ciphertext, _ := senderCtx.Seal(plaintext, aad)

// Receiver verifies sender's identity
receiverCtx, _ := hpke.SetupAuthR(suite, receiverSk, capsule, senderPk, info)
plaintext, _ := receiverCtx.Open(ciphertext, aad)
AuthPSK Mode
// Sender: authenticated with both private key and PSK
senderCtx, _ := hpke.SetupAuthPSKS(suite, receiverPk, senderSk, psk, pskId, info, rand.Reader)
ciphertext, _ := senderCtx.Seal(plaintext, aad)

// Receiver: verifies both mechanisms
receiverCtx, _ := hpke.SetupAuthPSKR(suite, receiverSk, capsule, senderPk, psk, pskId, info)
plaintext, _ := receiverCtx.Open(ciphertext, aad)
Multiple Messages with Same Context

A single context can encrypt/decrypt multiple messages. Each message uses a unique nonce derived from an internal sequence number:

senderCtx, _ := hpke.SetupBaseS(suite, receiverPk, info, rand.Reader)
receiverCtx, _ := hpke.SetupBaseR(suite, receiverSk, senderCtx.Capsule, info)

// Messages must be decrypted in the same order they were encrypted
ct1, _ := senderCtx.Seal(msg1, nil)
ct2, _ := senderCtx.Seal(msg2, nil)
ct3, _ := senderCtx.Seal(msg3, nil)

pt1, _ := receiverCtx.Open(ct1, nil) // Must open ct1 first
pt2, _ := receiverCtx.Open(ct2, nil) // Then ct2
pt3, _ := receiverCtx.Open(ct3, nil) // Then ct3
Secret Export

Derive additional secrets from the encryption context:

exporterContext := []byte("application-specific-context")
length := 32

// Both sender and receiver derive the same secret
senderSecret, _ := senderCtx.Export(exporterContext, length)
receiverSecret, _ := receiverCtx.Export(exporterContext, length)
// senderSecret == receiverSecret

Using X25519 Instead of P-256

import "github.com/bronlabs/bron-crypto/pkg/base/curves/curve25519"

curve := curve25519.NewPrimeSubGroup()
suite, _ := hpke.NewCipherSuite(
    hpke.DHKEM_X25519_HKDF_SHA256,
    hpke.KDF_HKDF_SHA256,
    hpke.AEAD_CHACHA_20_POLY_1305,
)
scheme, _ := hpke.NewScheme(curve, suite)

Package Structure

File Contents
hpke.go Scheme type, type aliases, constants
rfc.go RFC 9180 Setup functions (SetupBaseS, SetupBaseR, etc.)
participants.go KeyGenerator, Encrypter, Decrypter and configuration options
kem.go KEM, DEM for direct KEM operations

Security Notes

  • PSK entropy: Pre-shared keys MUST have at least 32 bytes of entropy.
  • Info binding: The info parameter is cryptographically bound to the derived keys; mismatched info causes decryption failure.
  • Sequence ordering: Messages must be decrypted in the same order they were encrypted.
  • Nonce uniqueness: Each context maintains a sequence number to ensure unique nonces; never reuse a context after sequence overflow - if that ever happens.
  • AAD handling: Associated data is authenticated but not encrypted; provide identical AAD during decryption.

Documentation

Overview

Package hpke implements Hybrid Public Key Encryption as specified in RFC 9180. HPKE provides public-key encryption of arbitrary-sized plaintexts with optional sender authentication.

See README.md for details.

Index

Constants

View Source
const (
	// Name is the identifier for the HPKE encryption scheme.
	Name encryption.Name = "HPKE"

	// Base mode (mode_base = 0x00) provides encryption without sender authentication.
	// The recipient cannot verify the sender's identity.
	Base ModeID = internal.Base

	// PSk mode (mode_psk = 0x01) authenticates the sender via a pre-shared key.
	// Both parties must possess the same PSK and PSK ID. The recipient can verify
	// the sender possessed the PSK, but PSK compromise allows impersonation.
	PSk ModeID = internal.PSk

	// Auth mode (mode_auth = 0x02) authenticates the sender via an asymmetric key pair.
	// The sender's private key is used in the encapsulation, allowing the recipient
	// to verify the sender's identity using the sender's public key.
	Auth ModeID = internal.Auth

	// AuthPSk mode (mode_auth_psk = 0x03) combines PSK and asymmetric authentication.
	// The sender is authenticated via both mechanisms, providing defence in depth.
	AuthPSk ModeID = internal.AuthPSk

	// AEAD_RESERVED (0x0000) is reserved and MUST NOT be used.
	AEAD_RESERVED AEADID = internal.AEAD_RESERVED

	// AEAD_AES_128_GCM (0x0001) specifies AES-128-GCM with Nk=16, Nn=12, Nt=16.
	AEAD_AES_128_GCM AEADID = internal.AEAD_AES_128_GCM

	// AEAD_AES_256_GCM (0x0002) specifies AES-256-GCM with Nk=32, Nn=12, Nt=16.
	AEAD_AES_256_GCM AEADID = internal.AEAD_AES_256_GCM

	// AEAD_CHACHA_20_POLY_1305 (0x0003) specifies ChaCha20Poly1305 with Nk=32, Nn=12, Nt=16.
	AEAD_CHACHA_20_POLY_1305 AEADID = internal.AEAD_CHACHA_20_POLY_1305

	// AEAD_EXPORT_ONLY (0xFFFF) indicates that the AEAD is not used for encryption;
	// only the Export interface is available. This is useful for key derivation scenarios.
	AEAD_EXPORT_ONLY AEADID = internal.AEAD_EXPORT_ONLY

	// KDF_HKDF_RESERVED (0x0000) is reserved and MUST NOT be used.
	KDF_HKDF_RESERVED KDFID = internal.KDF_HKDF_RESERVED

	// KDF_HKDF_SHA256 (0x0001) specifies HKDF-SHA256 with Nh=32.
	KDF_HKDF_SHA256 KDFID = internal.KDF_HKDF_SHA256

	// KDF_HKDF_SHA512 (0x0003) specifies HKDF-SHA512 with Nh=64.
	KDF_HKDF_SHA512 KDFID = internal.KDF_HKDF_SHA512

	// DHKEM_RESERVED (0x0000) is reserved and MUST NOT be used.
	DHKEM_RESERVED KEMID = internal.DHKEM_RESERVED

	// DHKEM_P256_HKDF_SHA256 (0x0010) specifies DHKEM(P-256, HKDF-SHA256)
	// with Nsecret=32, Nenc=65, Npk=65, Nsk=32.
	DHKEM_P256_HKDF_SHA256 KEMID = internal.DHKEM_P256_HKDF_SHA256

	// DHKEM_X25519_HKDF_SHA256 (0x0020) specifies DHKEM(X25519, HKDF-SHA256)
	// with Nsecret=32, Nenc=32, Npk=32, Nsk=32.
	DHKEM_X25519_HKDF_SHA256 KEMID = internal.DHKEM_X25519_HKDF_SHA256
)

Variables

View Source
var (
	ErrInvalidArgument = errs.New("invalid argument")
	ErrInvalidLength   = errs.New("invalid length")
	ErrNotSupported    = errs.New("not supported")
)
View Source
var (
	// NewCipherSuite creates a new CipherSuite from the specified KEM, KDF, and AEAD identifiers.
	// Returns an error if any identifier is reserved (0x0000) or invalid.
	//
	// See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1
	NewCipherSuite = internal.NewCipherSuite
)

Functions

func DecryptingWithApplicationInfo

func DecryptingWithApplicationInfo[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](info []byte) encryption.DecrypterOption[*Decrypter[P, B, S], Message, Ciphertext]

DecryptingWithApplicationInfo returns an option that sets the application-specific info parameter. This must match the info used by the sender for successful decryption.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1

func DecryptingWithAuthPSK

func DecryptingWithAuthPSK[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](pk *PublicKey[P, B, S], pskID []byte, psk *encryption.SymmetricKey) encryption.DecrypterOption[*Decrypter[P, B, S], Message, Ciphertext]

DecryptingWithAuthPSK returns an option that enables AuthPSK mode (mode_auth_psk = 0x03) for decryption. Both the sender's public key and PSK must be provided and match those used by the sender.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.4

func DecryptingWithAuthentication

func DecryptingWithAuthentication[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](pk *PublicKey[P, B, S]) encryption.DecrypterOption[*Decrypter[P, B, S], Message, Ciphertext]

DecryptingWithAuthentication returns an option that enables Auth mode (mode_auth = 0x02) for decryption. The sender's public key is used to verify that the sender possessed the corresponding private key during encapsulation.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.3

func DecryptingWithCapsule

func DecryptingWithCapsule[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](capsule *Capsule[P, B, S]) encryption.DecrypterOption[*Decrypter[P, B, S], Message, Ciphertext]

DecryptingWithCapsule returns an option that sets the capsule (enc) received from the sender. The capsule is the serialised ephemeral public key used in the key encapsulation mechanism. This option is required for decryption.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1

func DecryptingWithPreSharedKey

func DecryptingWithPreSharedKey[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](pskID []byte, psk *encryption.SymmetricKey) encryption.DecrypterOption[*Decrypter[P, B, S], Message, Ciphertext]

DecryptingWithPreSharedKey returns an option that enables PSK mode (mode_psk = 0x01) for decryption. The PSK and PSK ID must match those used by the sender.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.2

func EncryptingWhileCachingRecentContextualInfo

func EncryptingWhileCachingRecentContextualInfo[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]]() encryption.EncrypterOption[*Encrypter[P, B, S], *PublicKey[P, B, S], Message, Ciphertext, *Capsule[P, B, S]]

EncryptingWhileCachingRecentContextualInfo returns an option that enables caching of the sender context after encryption. This allows the Export method to be called to derive additional secrets from the encryption context.

Note: When caching is enabled, the encrypter holds a mutex during Seal operations for thread safety.

func EncryptingWithApplicationInfo

func EncryptingWithApplicationInfo[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](info []byte) encryption.EncrypterOption[*Encrypter[P, B, S], *PublicKey[P, B, S], Message, Ciphertext, *Capsule[P, B, S]]

EncryptingWithApplicationInfo returns an option that sets the application-specific info parameter for the HPKE key schedule. The info parameter is bound to the derived keys and must match between sender and receiver for successful decryption.

Per RFC 9180 Section 5.1, info is application-supplied information that should be independently agreed upon by both parties.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1

func EncryptingWithAuthPSK

func EncryptingWithAuthPSK[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](sk *PrivateKey[S], pskID []byte, psk *encryption.SymmetricKey) encryption.EncrypterOption[*Encrypter[P, B, S], *PublicKey[P, B, S], Message, Ciphertext, *Capsule[P, B, S]]

EncryptingWithAuthPSK returns an option that enables AuthPSK mode (mode_auth_psk = 0x03). This combines both asymmetric authentication (via the sender's private key) and PSK authentication, providing defence in depth.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.4

func EncryptingWithAuthentication

func EncryptingWithAuthentication[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](sk *PrivateKey[S]) encryption.EncrypterOption[*Encrypter[P, B, S], *PublicKey[P, B, S], Message, Ciphertext, *Capsule[P, B, S]]

EncryptingWithAuthentication returns an option that enables Auth mode (mode_auth = 0x02). The sender's private key is used in the encapsulation to authenticate the sender's identity. The recipient can verify the sender possessed the corresponding private key.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.3

func EncryptingWithPreSharedKey

func EncryptingWithPreSharedKey[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](pskID []byte, psk *encryption.SymmetricKey) encryption.EncrypterOption[*Encrypter[P, B, S], *PublicKey[P, B, S], Message, Ciphertext, *Capsule[P, B, S]]

EncryptingWithPreSharedKey returns an option that enables PSK mode (mode_psk = 0x01). Both sender and receiver must possess the same pre-shared key (psk) and PSK identifier (pskID). The PSK is incorporated into the key schedule, providing sender authentication.

Per RFC 9180, the psk MUST have at least 32 bytes of entropy, and pskID is a sequence of bytes used to identify the PSK.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.2

func SealPSK

func SealPSK[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](ctx *SenderContext[P, B, S], additionalData, plaintext []byte) (ciphertext []byte, err error)

SealPSK encrypts a plaintext using a PSK-mode sender context. This is a convenience wrapper around ctx.Seal for PSK mode.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.2

func WithSenderPrivateKey

func WithSenderPrivateKey[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](sk *PrivateKey[S]) encryption.KEMOption[*KEM[P, B, S], *PublicKey[P, B, S], *Capsule[P, B, S]]

WithSenderPrivateKey returns an option that enables authenticated encapsulation by providing the sender's private key. When set, the KEM uses AuthEncap instead of Encap, allowing the receiver to verify the sender's identity.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1

func WithSenderPublicKey

func WithSenderPublicKey[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](pk *PublicKey[P, B, S]) encryption.DEMOption[*DEM[P, B, S], *Capsule[P, B, S]]

WithSenderPublicKey returns an option that enables authenticated decapsulation by providing the sender's public key. When set, the DEM uses AuthDecap instead of Decap, verifying that the capsule was created by the sender.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1

Types

type AEADID

type AEADID = internal.AEADID

AEADID is a two-byte value identifying the AEAD algorithm, defined in RFC 9180 Table 3. HPKE supports AES-128-GCM, AES-256-GCM, ChaCha20Poly1305, and an export-only mode.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-7.3

type Capsule

type Capsule[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]] = internal.PublicKey[P, B, S]

Capsule is the encapsulated key transmitted from sender to receiver, represented as an ephemeral public key. In DHKEM, the capsule is the serialised ephemeral public key (enc) that allows the receiver to derive the same shared secret.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1

type CipherSuite

type CipherSuite = internal.CipherSuite

CipherSuite specifies the combination of KEM, KDF, and AEAD algorithms used for HPKE. The suite identifier is computed as concat("HPKE", I2OSP(kem_id, 2), I2OSP(kdf_id, 2), I2OSP(aead_id, 2)).

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1

type Ciphertext

type Ciphertext []byte

Ciphertext is the encrypted message produced by HPKE's AEAD encryption. It contains both the encrypted plaintext and the authentication tag.

func (Ciphertext) Equal

func (c Ciphertext) Equal(other Ciphertext) bool

Equal reports whether both ciphertexts are equal.

type DEM

type DEM[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]] struct {
	// contains filtered or unexported fields
}

DEM provides data encapsulation mechanism (decapsulation) operations for HPKE. It uses the receiver's private key to recover the shared secret from a capsule.

The DEM supports both standard and authenticated decapsulation. Authenticated decapsulation verifies that the shared secret was produced using the sender's private key.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-4

func (*DEM[P, B, S]) Decapsulate

func (d *DEM[P, B, S]) Decapsulate(capsule *Capsule[P, B, S]) (*encryption.SymmetricKey, error)

Decapsulate recovers the shared secret from a capsule using the receiver's private key. Returns a symmetric key derived from the shared secret.

If WithSenderPublicKey was configured, this performs authenticated decapsulation (AuthDecap), otherwise standard decapsulation (Decap).

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1

func (*DEM[P, B, S]) IsAuthenticated

func (d *DEM[P, B, S]) IsAuthenticated() bool

IsAuthenticated returns true if the sender's public key has been configured, indicating that authenticated decapsulation will be used.

type Decrypter

type Decrypter[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]] struct {
	// contains filtered or unexported fields
}

Decrypter performs HPKE decryption operations. The receiver context is established during construction based on the receiver's private key and the capsule from the sender.

The decrypter supports all four HPKE modes, determined by which options were provided during construction. The mode must match what the sender used, or decryption will fail.

func (*Decrypter[P, B, S]) Decrypt

func (e *Decrypter[P, B, S]) Decrypt(ciphertext Ciphertext) (Message, error)

Decrypt decrypts a ciphertext encrypted to this receiver's public key. This is equivalent to Open with nil associated data.

func (*Decrypter[P, B, S]) Export

func (e *Decrypter[P, B, S]) Export(context []byte, length uint) (*encryption.SymmetricKey, error)

Export derives a secret from the decryption context using the HPKE secret export mechanism. The exporter_context parameter and length are inputs to the secret derivation.

When called with the same inputs on both sender and receiver, Export produces the same output, enabling key agreement for additional symmetric keys.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.3

func (*Decrypter[P, B, S]) Mode

func (e *Decrypter[P, B, S]) Mode() ModeID

Mode returns the HPKE mode configured for decryption, determined by which authentication parameters have been set.

func (*Decrypter[P, B, S]) Open

func (e *Decrypter[P, B, S]) Open(ciphertext Ciphertext, aad []byte) (Message, error)

Open decrypts a ciphertext with associated data. The associated data must match exactly what was provided during encryption, or the authentication will fail.

The sequence number is incremented after each successful decryption, so ciphertexts must be opened in the same order they were sealed by the sender.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.2

type Encrypter

type Encrypter[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]] struct {
	// contains filtered or unexported fields
}

Encrypter performs HPKE encryption operations. It establishes a sender context for each encryption and seals messages using the AEAD algorithm specified in the cipher suite.

The encrypter supports all four HPKE modes, determined by which options were provided during construction. For each Encrypt/Seal call, a new ephemeral key pair is generated and a fresh sender context is established.

func (*Encrypter[P, B, S]) Encrypt

func (e *Encrypter[P, B, S]) Encrypt(plaintext Message, receiver *PublicKey[P, B, S], prng io.Reader) (Ciphertext, *Capsule[P, B, S], error)

Encrypt encrypts a plaintext message to the receiver's public key. It returns the ciphertext and the capsule (ephemeral public key) that must be transmitted to the receiver for decryption. This is equivalent to Seal with nil aad.

A fresh sender context is established for each call, generating a new ephemeral key pair and deriving fresh encryption keys.

func (*Encrypter[P, B, S]) Export

func (e *Encrypter[P, B, S]) Export(context []byte, length uint) (*encryption.SymmetricKey, error)

Export derives a secret from the encryption context using the HPKE secret export mechanism. This requires context caching to be enabled via EncryptingWhileCachingRecentContextualInfo.

The exporter_context parameter and length are inputs to the secret derivation. The same inputs will produce the same output on both sender and receiver sides.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.3

func (*Encrypter[P, B, S]) Mode

func (e *Encrypter[P, B, S]) Mode() ModeID

Mode returns the HPKE mode that will be used for encryption, determined by which authentication parameters have been configured.

func (*Encrypter[P, B, S]) Seal

func (e *Encrypter[P, B, S]) Seal(plaintext Message, receiver *PublicKey[P, B, S], aad []byte, prng io.Reader) (Ciphertext, *Capsule[P, B, S], error)

Seal encrypts a plaintext message with associated data to the receiver's public key. The associated data (aad) is authenticated but not encrypted; it must be provided identically during decryption.

Returns the ciphertext (containing encrypted plaintext and authentication tag) and the capsule (ephemeral public key) needed for decryption.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.2

type KDFID

type KDFID = internal.KDFID

KDFID is a two-byte value identifying the key derivation function, defined in RFC 9180 Table 2. HPKE uses HKDF with either SHA-256 or SHA-512 as the underlying hash.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-7.2

type KEM

type KEM[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]] struct {
	// contains filtered or unexported fields
}

KEM provides key encapsulation mechanism operations for HPKE. It wraps the underlying DHKEM (Diffie-Hellman based KEM) and supports both standard and authenticated encapsulation.

The KEM generates an ephemeral key pair and combines it with the receiver's public key to produce a shared secret and a capsule (the ephemeral public key).

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-4

func (*KEM[P, B, S]) Encapsulate

func (k *KEM[P, B, S]) Encapsulate(receiver *PublicKey[P, B, S], prng io.Reader) (*encryption.SymmetricKey, *Capsule[P, B, S], error)

Encapsulate generates a shared secret and encapsulates it for the given receiver. Returns:

  • A symmetric key derived from the shared secret
  • A capsule (ephemeral public key) to send to the receiver

If WithSenderPrivateKey was configured, this performs authenticated encapsulation (AuthEncap), otherwise standard encapsulation (Encap).

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1

func (*KEM[P, B, S]) IsAuthenticated

func (k *KEM[P, B, S]) IsAuthenticated() bool

IsAuthenticated returns true if the sender's private key has been configured, indicating that authenticated encapsulation will be used.

type KEMID

type KEMID = internal.KEMID

KEMID is a two-byte value identifying the key encapsulation mechanism, defined in RFC 9180 Table 2. This implementation supports DHKEM(P-256, HKDF-SHA256) and DHKEM(X25519, HKDF-SHA256).

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1

type KeyGenerator

type KeyGenerator[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]] struct {
	// contains filtered or unexported fields
}

KeyGenerator generates HPKE key pairs for the configured cipher suite. Key generation follows the DeriveKeyPair algorithm defined in RFC 9180 Section 4, which uses the KEM's KDF to derive keys from random input keying material (IKM).

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-4

func (*KeyGenerator[P, B, S]) Generate

func (kg *KeyGenerator[P, B, S]) Generate(prng io.Reader) (sk *PrivateKey[S], pk *PublicKey[P, B, S], err error)

Generate creates a new HPKE key pair using randomness from prng. The private key is derived using the KEM's DeriveKeyPair algorithm with random IKM. The public key is the corresponding curve point pk = sk * G.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-4

func (*KeyGenerator[P, B, S]) GenerateWithSeed

func (kg *KeyGenerator[P, B, S]) GenerateWithSeed(ikm []byte) (sk *PrivateKey[S], pk *PublicKey[P, B, S], err error)

GenerateWithSeed creates a new HPKE key pair deterministically from seed material. The seed (IKM) SHOULD have at least Nsk bytes of entropy for the KEM algorithm. This implements the DeriveKeyPair algorithm from RFC 9180 Section 4.

See: https://www.rfc-editor.org/rfc/rfc9180.html#name-derivekeypair

type KeyGeneratorOption

type KeyGeneratorOption[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]] = encryption.KeyGeneratorOption[*KeyGenerator[P, B, S], *PrivateKey[S], *PublicKey[P, B, S]]

KeyGeneratorOption is a functional option for configuring the HPKE key generator. Currently no options are defined as HPKE key generation is fully determined by the cipher suite's KEM algorithm.

type Message

type Message = []byte

Message is the plaintext input to or output from HPKE encryption/decryption.

type ModeID

type ModeID = internal.ModeID

ModeID is a one-byte value indicating the HPKE mode, defined in RFC 9180 Table 1. The four modes provide different authentication guarantees:

  • Base (0x00): No sender authentication
  • PSK (0x01): Sender authenticated via pre-shared key
  • Auth (0x02): Sender authenticated via asymmetric key
  • AuthPSK (0x03): Sender authenticated via both PSK and asymmetric key

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5

type PrivateKey

type PrivateKey[S algebra.PrimeFieldElement[S]] = internal.PrivateKey[S]

PrivateKey represents a KEM private key, which is a scalar in the underlying elliptic curve's scalar field. The private key is used for decapsulation and, in authenticated modes, to prove the sender's identity.

type PublicKey

type PublicKey[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]] = internal.PublicKey[P, B, S]

PublicKey represents a KEM public key, which is a point on the underlying elliptic curve. The public key is used for encapsulation and to identify the recipient.

type ReceiverContext

type ReceiverContext[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]] = internal.ReceiverContext[P, B, S]

ReceiverContext holds the decryption context established by the receiver after key decapsulation. It provides the Open method for decrypting messages and Export for deriving additional secrets. The context maintains sequence numbers that must match the sender's for successful decryption.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.2

func SetupAuthPSKR

func SetupAuthPSKR[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](suite *CipherSuite, receiverPrivatekey *PrivateKey[S], ephemeralPublicKey, senderPublicKey *PublicKey[P, B, S], psk, pskID, info []byte) (*ReceiverContext[P, B, S], error)

SetupAuthPSKR establishes a decryption context for AuthPSK mode (mode_auth_psk = 0x03). This is the receiver-side counterpart to SetupAuthPSKS.

Both the sender's public key and the PSK are verified during decryption. The decryption will fail if either authentication mechanism fails.

Parameters:

  • suite: The cipher suite (must match sender's)
  • receiverPrivatekey: The recipient's private key (skR)
  • ephemeralPublicKey: The capsule (enc) received from the sender
  • senderPublicKey: The sender's public key (pkS) for authentication
  • psk: The pre-shared key (must match sender's)
  • pskID: Identifier for the PSK (must match sender's)
  • info: Application-supplied information (must match sender's)

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.4

func SetupAuthR

func SetupAuthR[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](suite *CipherSuite, receiverPrivatekey *PrivateKey[S], ephemeralPublicKey, senderPublicKey *PublicKey[P, B, S], info []byte) (*ReceiverContext[P, B, S], error)

SetupAuthR establishes a decryption context for Auth mode (mode_auth = 0x02). This is the receiver-side counterpart to SetupAuthS.

The receiver uses the sender's public key to verify the sender's identity. Decryption will fail if the ciphertext was not created using the corresponding sender private key.

Parameters:

  • suite: The cipher suite (must match sender's)
  • receiverPrivatekey: The recipient's private key (skR)
  • ephemeralPublicKey: The capsule (enc) received from the sender
  • senderPublicKey: The sender's public key (pkS) for authentication
  • info: Application-supplied information (must match sender's)

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.3

func SetupBaseR

func SetupBaseR[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](suite *CipherSuite, receiverPrivatekey *PrivateKey[S], ephemeralPublicKey *PublicKey[P, B, S], info []byte) (*ReceiverContext[P, B, S], error)

SetupBaseR establishes a decryption context for Base mode (mode_base = 0x00). This is the receiver-side counterpart to SetupBaseS.

Parameters:

  • suite: The cipher suite (must match sender's)
  • receiverPrivatekey: The recipient's private key (skR)
  • ephemeralPublicKey: The capsule (enc) received from the sender
  • info: Application-supplied information (must match sender's)

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.1

func SetupPSKR

func SetupPSKR[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](suite *CipherSuite, receiverPrivatekey *PrivateKey[S], ephemeralPublicKey *PublicKey[P, B, S], psk, pskID, info []byte) (*ReceiverContext[P, B, S], error)

SetupPSKR establishes a decryption context for PSK mode (mode_psk = 0x01). This is the receiver-side counterpart to SetupPSKS.

Parameters:

  • suite: The cipher suite (must match sender's)
  • receiverPrivatekey: The recipient's private key (skR)
  • ephemeralPublicKey: The capsule (enc) received from the sender
  • psk: The pre-shared key (must match sender's)
  • pskID: Identifier for the PSK (must match sender's)
  • info: Application-supplied information (must match sender's)

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.2

type Scheme

type Scheme[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]] struct {
	// contains filtered or unexported fields
}

Scheme is the main HPKE scheme type, parameterised by the elliptic curve point type. It combines a KEM, KDF, and AEAD to provide hybrid public-key encryption as specified in RFC 9180. The scheme supports all four HPKE modes: Base, PSK, Auth, and AuthPSK.

func NewScheme

func NewScheme[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](curve curves.Curve[P, B, S], cipherSuite *CipherSuite) (*Scheme[P, B, S], error)

NewScheme creates a new HPKE scheme parameterised by the given elliptic curve and cipher suite. The curve determines the KEM (key encapsulation mechanism), while the cipher suite specifies the KDF (key derivation function) and AEAD (authenticated encryption) algorithms.

The scheme provides factory methods for creating encrypters, decrypters, and direct access to KEM/DEM operations for more advanced use cases.

See: https://www.rfc-editor.org/rfc/rfc9180.html

func (*Scheme[P, B, S]) AEAD

func (s *Scheme[P, B, S]) AEAD(key *encryption.SymmetricKey) (cipher.AEAD, error)

AEAD returns an AEAD cipher instance initialised with the given symmetric key. This provides direct access to the underlying AEAD algorithm (AES-GCM or ChaCha20Poly1305) for use cases requiring manual key management outside the standard HPKE flow.

func (*Scheme[P, B, S]) CipherSuite

func (s *Scheme[P, B, S]) CipherSuite() *CipherSuite

CipherSuite returns the cipher suite used by this scheme.

func (*Scheme[P, B, S]) DEM

func (s *Scheme[P, B, S]) DEM(receiverPrivateKey *PrivateKey[S], opts ...encryption.DEMOption[*DEM[P, B, S], *Capsule[P, B, S]]) (*DEM[P, B, S], error)

DEM returns a data encapsulation mechanism instance for this scheme. The DEM uses the receiver's private key to decapsulate the shared secret from a capsule. Use WithSenderPublicKey option to enable authenticated decapsulation (Auth mode).

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-4

func (*Scheme[P, B, S]) Decrypter

func (s *Scheme[P, B, S]) Decrypter(receiverPrivateKey *PrivateKey[S], opts ...encryption.DecrypterOption[*Decrypter[P, B, S], Message, Ciphertext]) (*Decrypter[P, B, S], error)

Decrypter creates an HPKE decrypter that can open messages encrypted to the receiver's public key. The decrypter requires the receiver's private key and the capsule (ephemeral public key) from the sender. The receiver context is established during construction based on the configured mode.

Required option:

  • DecryptingWithCapsule: The capsule (enc) received from the sender

Mode-specific options:

  • DecryptingWithApplicationInfo: Set application-specific info parameter (must match sender)
  • DecryptingWithAuthentication: Enable Auth mode with sender's public key
  • DecryptingWithPreSharedKey: Enable PSK mode with pre-shared key
  • DecryptingWithAuthPSK: Enable AuthPSK mode with both

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-6

func (*Scheme[P, B, S]) Encrypter

func (s *Scheme[P, B, S]) Encrypter(opts ...encryption.EncrypterOption[*Encrypter[P, B, S], *PublicKey[P, B, S], Message, Ciphertext, *Capsule[P, B, S]]) (*Encrypter[P, B, S], error)

Encrypter creates an HPKE encrypter that can seal messages to a recipient's public key. The encrypter establishes a fresh sender context for each encryption, unless caching is enabled via EncryptingWhileCachingRecentContextualInfo.

Available options configure the HPKE mode:

  • EncryptingWithApplicationInfo: Set application-specific info parameter
  • EncryptingWithAuthentication: Enable Auth mode with sender's private key
  • EncryptingWithPreSharedKey: Enable PSK mode with pre-shared key
  • EncryptingWithAuthPSK: Enable AuthPSK mode with both
  • EncryptingWhileCachingRecentContextualInfo: Cache context for Export

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-6

func (*Scheme[P, B, S]) KEM

func (s *Scheme[P, B, S]) KEM(opts ...encryption.KEMOption[*KEM[P, B, S], *PublicKey[P, B, S], *Capsule[P, B, S]]) (*KEM[P, B, S], error)

KEM returns a key encapsulation mechanism instance for this scheme. In HPKE, the KEM is used to establish a shared secret between sender and receiver. Use WithSenderPrivateKey option to enable authenticated encapsulation (Auth mode).

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-4

func (*Scheme[P, B, S]) Keygen

func (s *Scheme[P, B, S]) Keygen(opts ...KeyGeneratorOption[P, B, S]) (*KeyGenerator[P, B, S], error)

Keygen creates a key generator for this HPKE scheme. The key generator produces key pairs compatible with the scheme's KEM algorithm. Key generation follows RFC 9180 Section 4, using DeriveKeyPair with random IKM.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-4

func (*Scheme[P, B, S]) Name

func (*Scheme[P, B, S]) Name() encryption.Name

Name returns the scheme identifier "HPKE".

type SenderContext

type SenderContext[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]] = internal.SenderContext[P, B, S]

SenderContext holds the encryption context established by the sender after key encapsulation. It provides the Seal method for encrypting messages and Export for deriving additional secrets. The context maintains sequence numbers to ensure unique nonces for each encryption operation.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.2

func SetupAuthPSKS

func SetupAuthPSKS[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](suite *CipherSuite, receiverPublicKey *PublicKey[P, B, S], senderPrivateKey *PrivateKey[S], psk, pskID, info []byte, prng io.Reader) (sender *SenderContext[P, B, S], err error)

SetupAuthPSKS establishes an encryption context for AuthPSK mode (mode_auth_psk = 0x03). This mode combines both PSK and asymmetric authentication, providing defence in depth.

The sender is authenticated via both mechanisms: the PSK is incorporated into the key schedule, and the sender's private key is used in the encapsulation. Both must be valid for decryption to succeed.

Parameters:

  • suite: The cipher suite specifying KEM, KDF, and AEAD algorithms
  • receiverPublicKey: The recipient's public key (pkR)
  • senderPrivateKey: The sender's private key (skS) for authentication
  • psk: The pre-shared key (MUST have at least 32 bytes of entropy)
  • pskID: Identifier for the PSK
  • info: Application-supplied information (optional; default "")
  • prng: Source of randomness for ephemeral key generation

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.4

func SetupAuthS

func SetupAuthS[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](suite *CipherSuite, receiverPublicKey *PublicKey[P, B, S], senderPrivateKey *PrivateKey[S], info []byte, prng io.Reader) (sender *SenderContext[P, B, S], err error)

SetupAuthS establishes an encryption context for Auth mode (mode_auth = 0x02). This mode authenticates the sender via an asymmetric key pair, allowing the recipient to verify the sender possessed the corresponding private key.

Unlike PSK mode, Auth mode provides non-repudiation: only the holder of skS could have created the ciphertext. However, it requires the receiver to know the sender's public key in advance.

Parameters:

  • suite: The cipher suite specifying KEM, KDF, and AEAD algorithms
  • receiverPublicKey: The recipient's public key (pkR)
  • senderPrivateKey: The sender's private key (skS) for authentication
  • info: Application-supplied information (optional; default "")
  • prng: Source of randomness for ephemeral key generation

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.3

func SetupBaseS

func SetupBaseS[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](suite *CipherSuite, receiverPublicKey *PublicKey[P, B, S], info []byte, prng io.Reader) (sender *SenderContext[P, B, S], err error)

SetupBaseS establishes an encryption context for Base mode (mode_base = 0x00). This mode provides encryption to a public key without sender authentication.

Parameters:

  • suite: The cipher suite specifying KEM, KDF, and AEAD algorithms
  • receiverPublicKey: The recipient's public key (pkR)
  • info: Application-supplied information (optional; default "")
  • prng: Source of randomness for ephemeral key generation

Returns a SenderContext containing the capsule (enc) to send to the receiver.

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.1

func SetupPSKS

func SetupPSKS[P curves.Point[P, B, S], B algebra.FiniteFieldElement[B], S algebra.PrimeFieldElement[S]](suite *CipherSuite, receiverPublicKey *PublicKey[P, B, S], psk, pskID, info []byte, prng io.Reader) (sender *SenderContext[P, B, S], err error)

SetupPSKS establishes an encryption context for PSK mode (mode_psk = 0x01). This mode authenticates the sender via a pre-shared key known to both parties.

The PSK provides authentication: the receiver can verify the sender possessed the PSK, but compromise of the PSK allows impersonation.

Parameters:

  • suite: The cipher suite specifying KEM, KDF, and AEAD algorithms
  • receiverPublicKey: The recipient's public key (pkR)
  • psk: The pre-shared key (MUST have at least 32 bytes of entropy)
  • pskID: Identifier for the PSK (used to select among multiple PSKs)
  • info: Application-supplied information (optional; default "")
  • prng: Source of randomness for ephemeral key generation

See: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.2

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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