identity

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Feb 21, 2026 License: MIT Imports: 19 Imported by: 1

Documentation

Index

Constants

View Source
const (
	// HPKERequestInfo is the info string for HPKE sender/receiver setup.
	// This provides domain separation for the HPKE key schedule.
	HPKERequestInfo = "ehbp request"
	// ResponseKeyLabel is the info string for HKDF-Expand to derive the response key.
	ResponseKeyLabel = "key"
	// ResponseNonceLabel is the info string for HKDF-Expand to derive the response nonce.
	ResponseNonceLabel = "nonce"
	// ExportLabel is the context string for HPKE Export.
	ExportLabel = "ehbp response"
	// ExportLength is the length of the exported secret.
	// Nk (AEAD key size) = 32 bytes for AES-256-GCM.
	ExportLength = 32
	// ResponseNonceLength is the length of the random response nonce.
	// max(Nn, Nk) = max(12, 32) = 32 for AES-256-GCM.
	ResponseNonceLength = 32
	// AES256KeyLength is the length of an AES-256 key
	AES256KeyLength = 32
	// AESGCMNonceLength is the length of an AES-GCM nonce
	AESGCMNonceLength = 12
)

Variables

This section is empty.

Functions

func DecryptResponseWithToken added in v0.2.0

func DecryptResponseWithToken(resp *http.Response, token *SessionRecoveryToken) error

DecryptResponseWithToken decrypts an HTTP response using only a SessionRecoveryToken (no live HPKE context required).

func IsClientError

func IsClientError(err error) bool

IsClientError determines if an error is caused by invalid client input

func IsKeyConfigError added in v0.1.6

func IsKeyConfigError(err error) bool

IsKeyConfigError determines if an error indicates key configuration mismatch.

func NewClientError

func NewClientError(err error) error

NewClientError wraps an error as a client-caused error

func NewKeyConfigError added in v0.1.6

func NewKeyConfigError(err error) error

NewKeyConfigError wraps an error as a key configuration mismatch.

Types

type ClientError

type ClientError struct {
	Err error
}

ClientError represents an error caused by invalid client input

func (ClientError) Error

func (e ClientError) Error() string

func (ClientError) Unwrap

func (e ClientError) Unwrap() error

type DerivedResponseWriter added in v0.1.0

type DerivedResponseWriter struct {
	http.ResponseWriter
	// contains filtered or unexported fields
}

DerivedResponseWriter wraps an http.ResponseWriter for streaming encryption using keys derived from the request's HPKE context.

func (*DerivedResponseWriter) Flush added in v0.1.0

func (w *DerivedResponseWriter) Flush()

func (*DerivedResponseWriter) Write added in v0.1.0

func (w *DerivedResponseWriter) Write(data []byte) (int, error)

Write encrypts data as chunks and writes them to the underlying ResponseWriter.

func (*DerivedResponseWriter) WriteHeader added in v0.1.0

func (w *DerivedResponseWriter) WriteHeader(statusCode int)

WriteHeader captures the status code and delegates to the underlying ResponseWriter

type DerivedStreamingDecryptReader added in v0.1.0

type DerivedStreamingDecryptReader struct {
	// contains filtered or unexported fields
}

DerivedStreamingDecryptReader decrypts response chunks using derived keys.

func (*DerivedStreamingDecryptReader) Close added in v0.1.0

func (*DerivedStreamingDecryptReader) Read added in v0.1.0

func (r *DerivedStreamingDecryptReader) Read(p []byte) (n int, err error)

Read implements io.Reader, decrypting chunks as they are read.

type Identity

type Identity struct {
	// contains filtered or unexported fields
}

func FromFile

func FromFile(filename string) (*Identity, error)

FromFile loads an identity from a file or creates a new one if it doesn't exist

func Import

func Import(identityJSONBytes []byte) (*Identity, error)

Import restores an identity from a JSON representation

func NewIdentity

func NewIdentity() (*Identity, error)

NewIdentity generates a new key pair

func UnmarshalPublicConfig

func UnmarshalPublicConfig(data []byte) (*Identity, error)

UnmarshalPublicConfig unmarshals a keys config into an identity

Per https://github.com/chris-wood/ohttp-go/blob/main/ohttp.go

func (*Identity) AEAD added in v0.2.0

func (i *Identity) AEAD() hpke.AEAD

func (*Identity) ConfigHandler

func (i *Identity) ConfigHandler(w http.ResponseWriter, r *http.Request)

ConfigHandler is an HTTP handler that returns the identity's configuration

func (*Identity) DecryptRequestWithContext added in v0.1.0

func (i *Identity) DecryptRequestWithContext(req *http.Request) (*ResponseContext, error)

DecryptRequestWithContext decrypts the request body and returns the HPKE context needed for response encryption.

The returned ResponseContext contains the HPKE recipient which can be used to export the shared secret for response key derivation.

func (*Identity) EncryptRequestWithContext added in v0.1.0

func (i *Identity) EncryptRequestWithContext(req *http.Request) (*RequestContext, error)

EncryptRequestWithContext encrypts the request body TO this identity's public key and returns the HPKE context needed for response decryption.

For bodyless requests (no body or empty body), returns nil - the request passes through unencrypted and the response will also be unencrypted. See SPEC.md Section 6.4 for the security rationale.

func (*Identity) Export

func (i *Identity) Export() ([]byte, error)

Export returns a JSON representation of the identity

func (*Identity) KDF added in v0.2.0

func (i *Identity) KDF() hpke.KDF

func (*Identity) KEM added in v0.2.0

func (i *Identity) KEM() hpke.KEM

func (*Identity) MarshalConfig

func (i *Identity) MarshalConfig() ([]byte, error)

MarshalConfig returns a binary representation of the identity compatible with RFC9458 application/ohttp-keys

func (*Identity) MarshalPublicKey

func (i *Identity) MarshalPublicKey() []byte

MarshalPublicKey returns a binary representation of the public key

func (*Identity) MarshalPublicKeyHex added in v0.0.2

func (i *Identity) MarshalPublicKeyHex() string

MarshalPublicKeyHex returns a hex string representation of the public key

func (*Identity) Middleware

func (i *Identity) Middleware() func(http.Handler) http.Handler

Middleware wraps an HTTP handler to encrypt/decrypt requests and responses. Requests with Ehbp-Encapsulated-Key header are decrypted and responses are encrypted. Requests without the header are passed through as plaintext.

func (*Identity) PrivateKey

func (i *Identity) PrivateKey() hpke.PrivateKey

PrivateKey returns the private key of an identity

func (*Identity) PublicKey

func (i *Identity) PublicKey() hpke.PublicKey

PublicKey returns the public key of an identity

func (*Identity) SetupDerivedResponseEncryption added in v0.1.0

func (i *Identity) SetupDerivedResponseEncryption(
	w http.ResponseWriter,
	respCtx *ResponseContext,
) (*DerivedResponseWriter, error)

SetupDerivedResponseEncryption creates an encrypted response writer using keys derived from the request's HPKE context.

type IdentityStore

type IdentityStore struct {
	PublicKey []byte
	SecretKey []byte

	// HPKE suite parameters (stored as RFC 9180 identifiers)
	KEM  uint16
	KDF  uint16
	AEAD uint16
}

IdentityStore is a serializable representation of an Identity

type KeyConfigError added in v0.1.6

type KeyConfigError struct {
	Err error
}

KeyConfigError represents a client/server key configuration mismatch. This is used for stale or incompatible HPKE key material that requires rekey.

func (KeyConfigError) Error added in v0.1.6

func (e KeyConfigError) Error() string

func (KeyConfigError) Unwrap added in v0.1.6

func (e KeyConfigError) Unwrap() error

type RequestContext added in v0.1.0

type RequestContext struct {
	Sender     *hpke.Sender // The sender from request encryption (has Export method)
	RequestEnc []byte       // The encapsulated key we sent
}

RequestContext holds the HPKE context needed for response decryption. This is returned by EncryptRequestWithContext and passed to DecryptResponseWithContext.

func (*RequestContext) DecryptResponse added in v0.1.0

func (ctx *RequestContext) DecryptResponse(resp *http.Response) error

DecryptResponse decrypts a response using keys derived from the request's HPKE context.

The response decryption keys are derived using the same process as the server:

  1. Export a secret from the HPKE context using label "ehbp response"
  2. Read the response nonce from the Ehbp-Response-Nonce header
  3. Derive key and IV using HKDF with salt = requestEnc || responseNonce

type ResponseAEAD added in v0.1.0

type ResponseAEAD struct {
	// contains filtered or unexported fields
}

ResponseAEAD provides authenticated encryption with automatic nonce management. It wraps cipher.AEAD and tracks the sequence number internally, computing unique nonces for each operation by XORing the sequence with the nonce base.

This follows the pattern from OHTTP (RFC 9458) where nonces are derived as:

nonce = nonce_base XOR sequence_number (big-endian in last 8 bytes)

The sequence number is automatically incremented after each Seal/Open operation, ensuring nonce uniqueness without requiring caller management.

func (*ResponseAEAD) NonceForSeq added in v0.1.0

func (r *ResponseAEAD) NonceForSeq(seq uint64) []byte

NonceForSeq returns the nonce that would be used for the given sequence number. This does not affect the internal sequence counter. Useful for testing.

func (*ResponseAEAD) NonceSize added in v0.1.0

func (r *ResponseAEAD) NonceSize() int

NonceSize returns the nonce size of the underlying AEAD.

func (*ResponseAEAD) Open added in v0.1.0

func (r *ResponseAEAD) Open(ciphertext, aad []byte) ([]byte, error)

Open decrypts ciphertext with the given additional authenticated data. It automatically computes the nonce from the current sequence number and increments the sequence for the next operation. Returns an error if authentication fails.

func (*ResponseAEAD) OpenWithSeq added in v0.1.0

func (r *ResponseAEAD) OpenWithSeq(seq uint64, ciphertext, aad []byte) ([]byte, error)

OpenWithSeq decrypts ciphertext using a specific sequence number without affecting the internal sequence counter. This is primarily useful for testing. Following the pattern from OHTTP's open_seq() function.

func (*ResponseAEAD) Overhead added in v0.1.0

func (r *ResponseAEAD) Overhead() int

Overhead returns the maximum difference between plaintext and ciphertext lengths.

func (*ResponseAEAD) Seal added in v0.1.0

func (r *ResponseAEAD) Seal(plaintext, aad []byte) []byte

Seal encrypts plaintext with the given additional authenticated data. It automatically computes the nonce from the current sequence number and increments the sequence for the next operation.

func (*ResponseAEAD) Seq added in v0.1.0

func (r *ResponseAEAD) Seq() uint64

Seq returns the current sequence number. Useful for testing.

type ResponseContext added in v0.1.0

type ResponseContext struct {
	RequestEnc []byte // The encapsulated key from the request
	// contains filtered or unexported fields
}

ResponseContext holds the HPKE context information needed for response encryption. This is returned by DecryptRequestWithContext and passed to SetupDerivedResponseEncryption.

type ResponseKeyMaterial added in v0.1.0

type ResponseKeyMaterial struct {
	Key       []byte // 32 bytes for AES-256
	NonceBase []byte // 12 bytes, XORed with sequence number for each chunk
}

ResponseKeyMaterial holds the derived key material for response encryption/decryption

func DeriveResponseKeys added in v0.1.0

func DeriveResponseKeys(exportedSecret, requestEnc, responseNonce []byte) (*ResponseKeyMaterial, error)

DeriveResponseKeys derives the response encryption key and nonce base from: - exportedSecret: The secret exported from the HPKE context (32 bytes) - requestEnc: The encapsulated key from the request (32 bytes for X25519) - responseNonce: The random nonce for this response (32 bytes, matching OHTTP's max(Nn, Nk))

The derivation follows OHTTP (RFC 9458) exactly:

salt = concat(enc, response_nonce)
prk = Extract(salt, secret)
aead_key = Expand(prk, "key", Nk)
aead_nonce = Expand(prk, "nonce", Nn)

func (*ResponseKeyMaterial) NewResponseAEAD added in v0.1.0

func (km *ResponseKeyMaterial) NewResponseAEAD() (*ResponseAEAD, error)

NewResponseAEAD creates an AES-256-GCM AEAD instance with automatic nonce management. The returned ResponseAEAD tracks sequence numbers internally and computes unique nonces for each Seal/Open operation.

type SessionRecoveryToken added in v0.2.0

type SessionRecoveryToken struct {
	ExportedSecret []byte `json:"exportedSecret"`
	RequestEnc     []byte `json:"requestEnc"`
}

SessionRecoveryToken contains the pre-computed bytes needed to decrypt a response without holding a live HPKE context. It can be serialized (e.g. to JSON) and used later or in a different process.

func ExtractSessionRecoveryToken added in v0.2.0

func ExtractSessionRecoveryToken(ctx *RequestContext) (*SessionRecoveryToken, error)

ExtractSessionRecoveryToken exports the HPKE shared secret from a RequestContext and returns a serializable token that can decrypt the corresponding response independently.

func (*SessionRecoveryToken) MarshalJSON added in v0.2.0

func (t *SessionRecoveryToken) MarshalJSON() ([]byte, error)

MarshalJSON encodes both fields as lowercase hex strings for cross-language interoperability (see SPEC.md Section 6.1.1).

func (*SessionRecoveryToken) UnmarshalJSON added in v0.2.0

func (t *SessionRecoveryToken) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes both fields from lowercase hex strings.

type StreamingDecryptReader

type StreamingDecryptReader struct {
	// contains filtered or unexported fields
}

StreamingDecryptReader wraps an io.Reader for streaming decryption

func NewStreamingDecryptReader

func NewStreamingDecryptReader(reader io.Reader, recipient *hpke.Recipient) *StreamingDecryptReader

NewStreamingDecryptReader creates a new streaming decrypt reader

func (*StreamingDecryptReader) Close

func (r *StreamingDecryptReader) Close() error

Close implements io.Closer

func (*StreamingDecryptReader) Read

func (r *StreamingDecryptReader) Read(p []byte) (n int, err error)

Read implements io.Reader, decrypting data as it's read

type StreamingEncryptReader

type StreamingEncryptReader struct {
	// contains filtered or unexported fields
}

StreamingEncryptReader wraps an io.Reader for streaming encryption

func (*StreamingEncryptReader) Close

func (r *StreamingEncryptReader) Close() error

Close implements io.Closer

func (*StreamingEncryptReader) Read

func (r *StreamingEncryptReader) Read(p []byte) (n int, err error)

Read implements io.Reader, encrypting data as it's read

Jump to

Keyboard shortcuts

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