Documentation
¶
Index ¶
- Constants
- func DecryptResponseWithToken(resp *http.Response, token *SessionRecoveryToken) error
- func IsClientError(err error) bool
- func IsKeyConfigError(err error) bool
- func NewClientError(err error) error
- func NewKeyConfigError(err error) error
- type ClientError
- type DerivedResponseWriter
- type DerivedStreamingDecryptReader
- type Identity
- func (i *Identity) AEAD() hpke.AEAD
- func (i *Identity) ConfigHandler(w http.ResponseWriter, r *http.Request)
- func (i *Identity) DecryptRequestWithContext(req *http.Request) (*ResponseContext, error)
- func (i *Identity) EncryptRequestWithContext(req *http.Request) (*RequestContext, error)
- func (i *Identity) Export() ([]byte, error)
- func (i *Identity) KDF() hpke.KDF
- func (i *Identity) KEM() hpke.KEM
- func (i *Identity) MarshalConfig() ([]byte, error)
- func (i *Identity) MarshalPublicKey() []byte
- func (i *Identity) MarshalPublicKeyHex() string
- func (i *Identity) Middleware() func(http.Handler) http.Handler
- func (i *Identity) PrivateKey() hpke.PrivateKey
- func (i *Identity) PublicKey() hpke.PublicKey
- func (i *Identity) SetupDerivedResponseEncryption(w http.ResponseWriter, respCtx *ResponseContext) (*DerivedResponseWriter, error)
- type IdentityStore
- type KeyConfigError
- type RequestContext
- type ResponseAEAD
- func (r *ResponseAEAD) NonceForSeq(seq uint64) []byte
- func (r *ResponseAEAD) NonceSize() int
- func (r *ResponseAEAD) Open(ciphertext, aad []byte) ([]byte, error)
- func (r *ResponseAEAD) OpenWithSeq(seq uint64, ciphertext, aad []byte) ([]byte, error)
- func (r *ResponseAEAD) Overhead() int
- func (r *ResponseAEAD) Seal(plaintext, aad []byte) []byte
- func (r *ResponseAEAD) Seq() uint64
- type ResponseContext
- type ResponseKeyMaterial
- type SessionRecoveryToken
- type StreamingDecryptReader
- type StreamingEncryptReader
Constants ¶
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 ¶
IsClientError determines if an error is caused by invalid client input
func IsKeyConfigError ¶ added in v0.1.6
IsKeyConfigError determines if an error indicates key configuration mismatch.
func NewClientError ¶
NewClientError wraps an error as a client-caused error
func NewKeyConfigError ¶ added in v0.1.6
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 (r *DerivedStreamingDecryptReader) Close() error
type Identity ¶
type Identity struct {
// contains filtered or unexported fields
}
func UnmarshalPublicConfig ¶
UnmarshalPublicConfig unmarshals a keys config into an identity
Per https://github.com/chris-wood/ohttp-go/blob/main/ohttp.go
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) MarshalConfig ¶
MarshalConfig returns a binary representation of the identity compatible with RFC9458 application/ohttp-keys
func (*Identity) MarshalPublicKey ¶
MarshalPublicKey returns a binary representation of the public key
func (*Identity) MarshalPublicKeyHex ¶ added in v0.0.2
MarshalPublicKeyHex returns a hex string representation of the public key
func (*Identity) Middleware ¶
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) 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:
- Export a secret from the HPKE context using label "ehbp response"
- Read the response nonce from the Ehbp-Response-Nonce header
- 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
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