Documentation
¶
Overview ¶
Package tessera is the server-side Go SDK for Tessera, Ciphera's zero-knowledge identity system. It provides three primitives:
- A pooled Unix-domain-socket client for the OPAQUE sidecar (RegisterStart/Finish, LoginStart/Finish). The SDK RELAYS browser-supplied OPAQUE messages to the sidecar; it never computes OPAQUE client math (the single Rust core guarantees cross-environment interop by construction).
- A deterministic, privacy-preserving blind index (Argon2id) used as the account lookup key and OPAQUE credential_identifier, so the server never sees a plaintext email.
- A generic client-encrypted vault (Seal/Open) for arbitrary user-private records, using a versioned DEK/KEK AES-256-GCM envelope.
Persistence and session issuance (JWT) are the caller's responsibility (your identity service), not this SDK's.
Index ¶
- Constants
- Variables
- func BlindIndex(email string) []byte
- func BlindIndexString(email string) string
- func NormalizeEmail(email string) string
- func Open(vaultKey []byte, context string, envelope []byte) ([]byte, error)
- func Seal(vaultKey []byte, context string, plaintext []byte) ([]byte, error)
- type Client
- func (c *Client) Close() error
- func (c *Client) LoginFinish(ctx context.Context, loginID, finalizationB64 string) (sessionKeyB64 string, err error)
- func (c *Client) LoginStart(ctx context.Context, requestB64 string, passwordFileB64 *string, ...) (loginID, responseB64 string, err error)
- func (c *Client) RegisterFinish(ctx context.Context, uploadB64 string) (passwordFileB64 string, err error)
- func (c *Client) RegisterStart(ctx context.Context, requestB64, credentialID string) (responseB64 string, err error)
- type Option
- type SidecarError
Constants ¶
const MaxFrame = 1 << 20
MaxFrame bounds a single wire frame. It MUST match the sidecar's MAX_FRAME (1 MiB).
Variables ¶
var ( // ErrUnsupportedVersion is returned by Open for an envelope whose version byte is not // recognized (forward compatibility — the version is not secret). ErrUnsupportedVersion = errors.New("tessera: unsupported vault envelope version") // ErrMalformedEnvelope is returned for a too-short envelope, a wrong key/context, or any // failed authentication tag — deliberately not distinguished, to avoid a decryption oracle. ErrMalformedEnvelope = errors.New("tessera: malformed or unauthentic vault envelope") // ErrEmptyVaultKey is returned when the supplied vault key is empty. ErrEmptyVaultKey = errors.New("tessera: empty vault key") // ErrEmptyContext is returned when the record context is empty — callers MUST name the record // type (e.g. "address", "totp") so each type gets an independent key. ErrEmptyContext = errors.New("tessera: empty record context") )
Functions ¶
func BlindIndex ¶
BlindIndex derives the deterministic, privacy-preserving account lookup key from an email.
SECURITY: in PRODUCTION this MUST be computed CLIENT-SIDE (in the browser). The server must never receive a plaintext email, so it must never call this on request-path input — doing so would defeat the zero-knowledge property by bringing the email into server memory/logs.
It is ALSO a denial-of-service hazard on the server path: each call allocates 64 MiB held for the full Argon2id computation, so even a single network-reachable handler that calls this can be memory-exhausted by a few tens of concurrent requests. This function must never be reachable from a network-exposed handler. It exists in Go only for (a) cross-language parity vectors and (b) trusted offline tooling such as one-time migration jobs. Memory-hard Argon2id is chosen because the index faces an offline, cross-user-amortized enumeration attack over a low-entropy (email) space; its security rests on that COST, not on the (public, shipped) salt being secret.
func BlindIndexString ¶
BlindIndexString returns the base64url (unpadded) encoding, suitable for use as the OPAQUE credential_identifier and the database lookup key.
func NormalizeEmail ¶
NormalizeEmail canonicalizes an email for stable, implementation-independent lookup. The order is PART OF THE PARITY CONTRACT: TrimSpace (strip surrounding whitespace) THEN ToLower — the TS/WASM SDK must apply the same sequence. (No Unicode NFC / IDNA punycode folding in v1; a documented known limitation and part of the contract.)
func Open ¶
Open reverses Seal. The caller MUST supply the same context used to Seal. It returns ErrUnsupportedVersion for an unknown version, ErrEmptyVaultKey for an empty key, ErrEmptyContext for an empty context, and ErrMalformedEnvelope for a too-short envelope, a wrong key/context, or any failed tag. The empty-key/empty-context guards signal caller error and are independent of envelope content, so they are not a decryption oracle.
func Seal ¶
Seal encrypts plaintext under a fresh random data-encryption key (DEK), wraps the DEK under a per-context KEK derived from vaultKey, and returns the versioned envelope. context names the record type (e.g. "address", "totp") and is REQUIRED — it is bound into both the KEK derivation and the AAD, and is NOT stored in the envelope (the caller must supply the same context to Open). It may also encode a record identity (e.g. "address:user-123"). vaultKey is key-agnostic (any non-empty key material): browser-held vault keys, recovery keys, or legitimately server-held keys.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a pooled client for the OPAQUE sidecar over a Unix domain socket. It is safe for concurrent use. The pool REUSES up to poolSize idle connections; under burst load it may open additional connections, bounded only by the sidecar's TESSERA_MAX_CONNECTIONS cap (default 256). Size the pool to steady-state concurrency and keep peak concurrency below that cap.
func (*Client) Close ¶
Close shuts the pool, closing all idle connections. It is safe to call concurrently with in-flight requests: once Close marks the client closed (under mu), any connection a still-running roundTrip later hands to put is closed rather than parked, so no connection is leaked. The pool channel is deliberately never closed — that would let a concurrent put panic on send.
func (*Client) LoginFinish ¶
func (c *Client) LoginFinish(ctx context.Context, loginID, finalizationB64 string) (sessionKeyB64 string, err error)
LoginFinish finalizes authentication and returns the server's session key (equal to the client's on success).
func (*Client) LoginStart ¶
func (c *Client) LoginStart(ctx context.Context, requestB64 string, passwordFileB64 *string, credentialID string) (loginID, responseB64 string, err error)
LoginStart begins authentication. passwordFileB64 is the stored record, or nil for an unknown account (the sidecar then returns a timing-safe fake response — DO pass nil, never fabricate a record, to keep account existence unobservable).
type Option ¶
type Option func(*config)
Option configures a Client.
func WithDialTimeout ¶
WithDialTimeout sets the per-dial timeout (default 5s).
func WithPoolSize ¶
WithPoolSize sets the max number of pooled connections (default 32).
type SidecarError ¶
SidecarError is returned when the sidecar replies with an Error frame. Code is a stable error class — "unknown_login", "invalid_credentials", "bad_request", or "internal" — suitable for control flow (HTTP status mapping); Message is human-readable, for diagnostics only.
func (*SidecarError) Error ¶
func (e *SidecarError) Error() string