crypto

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2026 License: AGPL-3.0 Imports: 15 Imported by: 0

Documentation

Overview

Package crypto holds envelope encryption for mailbox credentials and password hashing.

Index

Constants

View Source
const MasterKeyID = "master"

MasterKeyID is the KeyID of MasterKey.

View Source
const MinPasswordLen = 12

MinPasswordLen is the shortest admin password, for first-run setup, a change in the browser and the reset command.

Variables

View Source
var ErrDecrypt = errors.New("decrypt failed")

ErrDecrypt means a ciphertext was tampered with, moved between rows, or sealed under another key.

View Source
var ErrKeyUnavailable = errors.New("key service unreachable")

ErrKeyUnavailable means the key provider could not be asked to wrap or unwrap a key (a key service that does not answer, or refuses for a reason other than the key): nothing sealed under a key it holds opens until it answers again, and nothing new is sealed. It is never a wrong key, which is ErrDecrypt.

View Source
var ErrNoMasterKey = errors.New("no master key")

ErrNoMasterKey means no key was given, in the environment or a named file, and the data directory has no master.key yet. LoadMasterKey never makes one: whether a new key is safe depends on what the database holds, so the caller decides and calls GenerateMasterKey.

Functions

func Envelope added in v0.3.0

func Envelope(dekEnc []byte) int

Envelope reports how a secret was sealed, by its wrapped per-secret key: 1, 2 or 3, and 0 for none (an empty, deleted secret) or one it does not know.

func GenerateMasterKey added in v0.1.2

func GenerateMasterKey(dataDir string) ([]byte, error)

GenerateMasterKey makes a new random key and stores it, readable only by the user, as <dataDir>/master.key. It never replaces a file that is already there.

func HashPassword

func HashPassword(password string) (string, error)

HashPassword returns an argon2id hash in the standard encoded form.

func KEKID added in v0.3.0

func KEKID(dekEnc []byte) string

KEKID is the KEK an envelope-3 copy was sealed under (its KeyProvider's KeyID), "" for any other envelope.

func LoadMasterKey

func LoadMasterKey(value, keyFile, dataDir string) ([]byte, error)

LoadMasterKey returns the key-encryption key: from the base64 value, else from keyFile, else from <dataDir>/master.key. It returns ErrNoMasterKey when that file does not exist.

func MasterKeyFile added in v0.1.2

func MasterKeyFile(dataDir string) string

MasterKeyFile is where the key lives when neither the environment nor a named file gives it.

func MasterKeySource added in v0.1.2

func MasterKeySource(value, keyFile, dataDir string) string

MasterKeySource names where LoadMasterKey reads the key from, for a message to a person.

func NewDataKey added in v0.3.0

func NewDataKey() ([]byte, error)

NewDataKey makes a random data key for a tenant.

func OpenKEK added in v0.3.0

func OpenKEK(ctx context.Context, p KeyProvider, legacy LegacyOpener, b Binding, secretEnc, dekEnc []byte) ([]byte, error)

OpenKEK reverses SealKEK; with legacy, it also opens envelope 1 bound to b.LegacyRow.

func SealKEK added in v0.3.0

func SealKEK(ctx context.Context, p KeyProvider, b Binding, plaintext []byte) (secretEnc, dekEnc []byte, err error)

SealKEK seals plaintext under a fresh per-secret key that the provider wraps itself (envelope 3), bound to b: for a copy that must stay readable after its tenant's key is gone. The per-secret key is wrapped as tenant b.TenantID's.

func VerifyPassword

func VerifyPassword(encoded, password string) bool

VerifyPassword reports whether password matches an encoded hash, in constant time.

Types

type Binding added in v0.3.0

type Binding struct {
	TenantID  int64
	Kind, Ref string
	LegacyRow int64
}

Binding is what a secret is sealed to: its tenant, its kind ("account", "destination", "setting", "removed") and its row id or name. LegacyRow is the number envelope 1 bound it to, for opening one sealed before tenant keys.

type KeyProvider added in v0.3.0

type KeyProvider interface {
	// KeyID names the KEK new keys are wrapped under; it is stored with each wrapped key,
	// so a provider holding several (a rotation) knows which one opens it.
	KeyID() string
	Wrap(ctx context.Context, tenantID int64, key []byte) ([]byte, error)
	// Unwrap returns ErrDecrypt when keyID or the tenant is not the one wrapped. Any other
	// error means the provider could not answer (ErrKeyUnavailable).
	Unwrap(ctx context.Context, tenantID int64, keyID string, wrapped []byte) ([]byte, error)
}

KeyProvider holds a key-encryption key: it wraps tenants' data keys and unwraps them. tenantID is bound into each wrap, so a wrapped key does not open as another tenant's. ext.KeyProvider is the same contract as a module implements it.

func WithMasterKey added in v0.3.0

func WithMasterKey(p KeyProvider, master MasterKey) KeyProvider

WithMasterKey is p, the key provider of a module (ext.Module.Keys), with the master key still unwrapping what was wrapped under it before p held the keys: the tenant keys whose kek_id is MasterKeyID, removed mailboxes' envelope-3 copies under it, and envelope 1. Everything else goes to p alone, so a key p wrapped never falls back to the master key. master may be nil (an install that never had one, or has retired it): what was wrapped under the master key is then ErrDecrypt, without asking p.

type LegacyOpener added in v0.3.0

type LegacyOpener interface {
	OpenLegacy(rowID int64, secretEnc, dekEnc []byte) ([]byte, error)
}

LegacyOpener opens secrets sealed before tenant keys (envelope 1), with the master key they were sealed under.

type MasterKey added in v0.3.0

type MasterKey []byte

MasterKey is the KeyProvider of the master key (LoadMasterKey), the KEK of self-host and of the hosted build until a KMS holds it. It also opens envelope 1.

func (MasterKey) KeyID added in v0.3.0

func (m MasterKey) KeyID() string

KeyID implements KeyProvider.

func (MasterKey) OpenLegacy added in v0.3.0

func (m MasterKey) OpenLegacy(rowID int64, secretEnc, dekEnc []byte) ([]byte, error)

OpenLegacy implements LegacyOpener.

func (MasterKey) Unwrap added in v0.3.0

func (m MasterKey) Unwrap(_ context.Context, tenantID int64, keyID string, wrapped []byte) ([]byte, error)

Unwrap implements KeyProvider.

func (MasterKey) Wrap added in v0.3.0

func (m MasterKey) Wrap(_ context.Context, tenantID int64, key []byte) ([]byte, error)

Wrap implements KeyProvider.

type TenantKey added in v0.3.0

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

TenantKey is one tenant's unwrapped data key, with what opens the tenant's secrets sealed before it existed. It never leaves the process.

func NewTenantKey added in v0.3.0

func NewTenantKey(tenantID int64, key []byte, legacy LegacyOpener) TenantKey

NewTenantKey is tenant's data key, unwrapped; legacy may be nil.

func (TenantKey) MAC added in v0.3.0

func (k TenantKey) MAC(label string, data []byte) []byte

MAC is an HMAC of data under a key derived from the tenant's key for label: a fingerprint that ties a stored value to a secret without storing the secret.

func (TenantKey) Open added in v0.3.0

func (k TenantKey) Open(kind, ref string, legacyRow int64, secretEnc, dekEnc []byte) ([]byte, error)

Open opens a secret of the tenant sealed by Seal under kind and ref, or one sealed before tenant keys (envelope 1) to legacyRow. It returns ErrDecrypt for a wrong key, tenant, kind, row or ciphertext.

func (TenantKey) Seal added in v0.3.0

func (k TenantKey) Seal(kind, ref string, plaintext []byte) (secretEnc, dekEnc []byte, err error)

Seal seals plaintext under a fresh per-secret key wrapped by the tenant's key (envelope 2), bound to kind and ref within the tenant.

func (TenantKey) Tenant added in v0.3.0

func (k TenantKey) Tenant() int64

Tenant is the tenant the key is of.

Jump to

Keyboard shortcuts

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