crypto

package
v0.0.0-...-effd846 Latest Latest
Warning

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

Go to latest
Published: Jun 29, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package crypto implements vec's encryption at rest: the page-level AEAD envelope, the key hierarchy (master key, per-epoch DEK, per-page key, per-write nonce), key rotation, and the verification tag that detects a wrong passphrase before any data page is read. It is spec 23 sections 2 through 5, 14, 17, and 18.

The package has no dependency on the pager or the rest of the engine. The pager reaches it through the Crypto interface, so an unencrypted database carries no crypto code on its hot path: the pager checks Enabled once and takes the no-op branch.

Index

Constants

View Source
const (
	ClassVectorSegment uint8 = 0x01
	ClassHNSWGraph     uint8 = 0x02
	ClassIVFDiskANN    uint8 = 0x03
	ClassMetadata      uint8 = 0x04
	ClassCatalog       uint8 = 0x05
	ClassFreelist      uint8 = 0x06
	ClassOverflow      uint8 = 0x07
)

Page classes tag every encrypted page so a ciphertext from one class cannot be relocated into another (spec 23 section 2.2). The tag goes into the AAD, which makes the ciphertext of a vector segment page cryptographically distinct from a graph page even when the plaintext is identical.

View Source
const DescriptorSize = 77

DescriptorSize is the byte length of the cleartext encryption descriptor (spec 23 section 3.5). The descriptor's own field offsets run to byte 77 (kdf 1, cipher 1, epoch 2, argon2 time 4, memory 4, threads 1, salt 32, db id 16, verification tag 16), so the serialized form is 77 bytes. Its placement inside the 100-byte file header, and any trailing reserved bytes, settle when the header and pager wiring land.

View Source
const EnvelopeOverhead = 28

EnvelopeOverhead is the number of bytes the AEAD envelope adds to a page: a 12-byte nonce and a 16-byte authentication tag (spec 23 section 2.3). A page of size P holds P-28 bytes of plaintext when encryption is on.

Variables

View Source
var (
	ErrInvalidSalt         = errors.New("vec/crypto: salt must be 32 bytes")
	ErrInvalidArgon2Params = errors.New("vec/crypto: argon2id time, memory, and threads must be non-zero")
	ErrInvalidKeyLength    = errors.New("vec/crypto: raw key must be 32 bytes")
	ErrUnknownCipher       = errors.New("vec/crypto: unknown cipher")
	ErrNoDEK               = errors.New("vec/crypto: no DEK supplied")
	ErrWrongPassphrase     = errors.New("vec/crypto: wrong passphrase")
	ErrPageTooShort        = errors.New("vec/crypto: page shorter than the AEAD envelope")
	ErrEpochExhausted      = errors.New("vec/crypto: key epoch exhausted; run RekeyVacuum to reset")
)

Key-setup errors. These surface at open time or at key-rotation time, before any page is processed.

Functions

func DEK

func DEK(masterKey []byte, dbID [16]byte, epoch uint16) ([]byte, error)

DEK derives the database encryption key for an epoch (spec 23 section 3.3). The dbID binds the DEK to one database, so a DEK from database A cannot authenticate pages from database B even under a shared master key.

func MasterKey

func MasterKey(passphrase string, p Argon2Params) ([]byte, error)

MasterKey derives the 32-byte master key from a passphrase with Argon2id (spec 23 section 3.2). A raw 32-byte key supplied by a KMS skips this step entirely; see MasterKeyFromRaw.

func MasterKeyFromRaw

func MasterKeyFromRaw(key []byte) ([]byte, error)

MasterKeyFromRaw accepts a raw 32-byte key from a KMS or keyfile (spec 23 section 3.2). No KDF is applied; the bytes become the master key directly.

func VerificationTag

func VerificationTag(masterKey []byte) ([16]byte, error)

VerificationTag produces the GCM tag over the known constant under the master key and a fixed zero nonce (spec 23 section 3.5). On open, the implementation recomputes this tag from the derived master key and compares it before reading any data page, so a wrong passphrase fails cleanly instead of returning garbage.

func VerifyMasterKey

func VerifyMasterKey(masterKey []byte, stored [16]byte) bool

VerifyMasterKey reports whether masterKey reproduces the stored verification tag, using a constant-time comparison (spec 23 section 14.2).

func Zeroize

func Zeroize(b []byte)

Zeroize overwrites a key slice with zeros (spec 23 section 18.2). Go does not guarantee the garbage collector zeroes reclaimed memory, so key material is wiped explicitly when it is released. This is the portable defense; the off-GC mmap arena in section 18.2 is a further hardening left for the pager wiring.

Types

type Argon2Params

type Argon2Params struct {
	Time    uint32 // passes, default 3
	Memory  uint32 // KiB, default 65536 (64 MiB)
	Threads uint8  // lanes, default 4
	Salt    []byte // 32 random bytes generated at create time
}

Argon2Params holds the Argon2id cost parameters (spec 23 section 3.2). They are stored verbatim in the header descriptor so the file opens on any hardware without prior knowledge of the configuration.

func DefaultArgon2Params

func DefaultArgon2Params(salt []byte) Argon2Params

DefaultArgon2Params returns the OWASP-recommended baseline (t=3, m=64MiB, p=4). The caller supplies the salt.

type Cipher

type Cipher uint8

Cipher selects the AEAD construction recorded in the header descriptor (spec 23 section 3.5). AES-256-GCM is the default and uses hardware AES on all modern x86-64 and arm64 hardware. ChaCha20-Poly1305 is the constant-time software fallback for hardware without AES acceleration.

const (
	CipherAES256GCM        Cipher = 0x01
	CipherChaCha20Poly1305 Cipher = 0x02
)

func (Cipher) String

func (c Cipher) String() string

String renders the cipher id as the token used in the header descriptor and the configuration view.

type Crypto

type Crypto interface {
	// Enabled reports whether encryption is on. The pager checks this on every
	// page read and write; when it is false the pager takes the no-op path.
	Enabled() bool

	// EncryptPage encrypts a plaintext page and returns the ciphertext envelope,
	// which is len(plaintext)+EnvelopeOverhead bytes.
	EncryptPage(plaintext []byte, pageClass uint8, pageNo, lsn uint64) ([]byte, error)

	// DecryptPage authenticates and decrypts an envelope. epoch is the key epoch
	// recorded in the page or WAL frame header.
	DecryptPage(envelope []byte, pageClass uint8, pageNo, lsn uint64, epoch uint16) ([]byte, error)

	// CurrentEpoch returns the epoch the next EncryptPage call will use.
	CurrentEpoch() uint16

	// Close releases in-memory key material.
	Close() error
}

Crypto is the seam the pager calls to encrypt and decrypt pages (spec 23 section 18.1). An implementation must be safe for concurrent use.

type Descriptor

type Descriptor struct {
	KDF             KDF
	Cipher          Cipher
	Epoch           uint16
	Argon2Time      uint32
	Argon2Memory    uint32 // KiB
	Argon2Threads   uint8
	Salt            [32]byte
	DBID            [16]byte
	VerificationTag [16]byte
}

Descriptor is the cleartext key-setup block. It holds everything needed to derive and verify the master key before any data page is read, and nothing that reveals user data (spec 23 section 2.2, 3.5).

func NewDescriptor

func NewDescriptor(passphrase string, c Cipher) (*Descriptor, []byte, error)

NewDescriptor builds a descriptor for a fresh encrypted database (spec 23 section 3.5). It generates a random salt and database id, derives the master key, and records the verification tag. The returned master key is the caller's to derive DEKs from and then zeroize.

func NewDescriptorRaw

func NewDescriptorRaw(rawKey []byte, c Cipher) (*Descriptor, []byte, error)

NewDescriptorRaw builds a descriptor for a database opened with a raw KMS key (spec 23 section 3.2). No Argon2id parameters are recorded; the salt is unused.

func UnmarshalDescriptor

func UnmarshalDescriptor(b []byte) (*Descriptor, error)

UnmarshalDescriptor parses a descriptor read from the file header.

func (*Descriptor) Argon2Params

func (d *Descriptor) Argon2Params() Argon2Params

Argon2Params reconstructs the Argon2id parameters from the descriptor.

func (*Descriptor) Marshal

func (d *Descriptor) Marshal() []byte

Marshal serializes the descriptor to its fixed on-disk form (spec 23 section 3.5). The layout matches the header descriptor offsets exactly.

type EncryptorConfig

type EncryptorConfig struct {
	Cipher  Cipher
	DBID    [16]byte
	DEKs    map[uint16][]byte
	Current uint16
}

EncryptorConfig is the input to NewPageEncryptor. DEKs maps each live epoch to its 32-byte key; Current is the epoch new writes use.

type ErrMissingDEK

type ErrMissingDEK struct {
	Epoch uint16
}

ErrMissingDEK is returned when a page references an epoch whose DEK is no longer in memory (spec 23 section 17.2). Recovery is to reload the DEK for that epoch from the master key.

func (ErrMissingDEK) Error

func (e ErrMissingDEK) Error() string

type ErrPageAuthFailed

type ErrPageAuthFailed struct {
	PageNo    uint64
	PageClass uint8
	Epoch     uint16
	Reason    string
}

ErrPageAuthFailed is returned when a page fails AEAD authentication (spec 23 section 17.1). The cause is a wrong key, a tampered page, or storage corruption. The reader returns this rather than plaintext, and the pager must not cache a page that failed.

func (ErrPageAuthFailed) Error

func (e ErrPageAuthFailed) Error() string

type KDF

type KDF uint8

KDF identifies how the master key is produced (spec 23 section 3.5). A raw key from a KMS skips the KDF; a passphrase runs Argon2id.

const (
	KDFNone     KDF = 0x00 // raw 32-byte key, no KDF
	KDFArgon2id KDF = 0x01
)

type NoCrypto

type NoCrypto struct{}

NoCrypto is the Crypto for an unencrypted database. Every method is a pass through, so the pager's fast path stays free of crypto work.

func (NoCrypto) Close

func (NoCrypto) Close() error

Close does nothing.

func (NoCrypto) CurrentEpoch

func (NoCrypto) CurrentEpoch() uint16

CurrentEpoch returns 0.

func (NoCrypto) DecryptPage

func (NoCrypto) DecryptPage(e []byte, _ uint8, _, _ uint64, _ uint16) ([]byte, error)

DecryptPage returns the envelope unchanged.

func (NoCrypto) Enabled

func (NoCrypto) Enabled() bool

Enabled reports false.

func (NoCrypto) EncryptPage

func (NoCrypto) EncryptPage(p []byte, _ uint8, _, _ uint64) ([]byte, error)

EncryptPage returns the plaintext unchanged.

type PageEncryptor

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

PageEncryptor is the Crypto implementation backed by a per-epoch DEK map (spec 23 section 14.1, 18.3). It is safe for concurrent use: reads and normal writes take a read lock to look up the current epoch and DEK; rotation takes the write lock. Per-page key derivation is stateless and runs without the lock.

func NewPageEncryptor

func NewPageEncryptor(cfg EncryptorConfig) (*PageEncryptor, error)

NewPageEncryptor builds a PageEncryptor from a set of DEKs (spec 23 section 14.2). The caller derives the DEKs from the master key and supplies them here, so this constructor never sees the passphrase.

func OpenWithPassphrase

func OpenWithPassphrase(d *Descriptor, passphrase string) (*PageEncryptor, error)

OpenWithPassphrase derives the master key from the passphrase, verifies it against the descriptor's tag, derives every live DEK, and returns a ready PageEncryptor (spec 23 section 14.2). The master key is zeroized before return.

func OpenWithRawKey

func OpenWithRawKey(d *Descriptor, rawKey []byte) (*PageEncryptor, error)

OpenWithRawKey verifies a raw KMS key against the descriptor and returns a ready PageEncryptor (spec 23 section 3.2).

func (*PageEncryptor) AddDEK

func (e *PageEncryptor) AddDEK(epoch uint16, dek []byte)

AddDEK installs the DEK for an epoch and makes it current (spec 23 section 4.3, the epoch bump). Old DEKs stay in the map so old-epoch pages still read.

func (*PageEncryptor) Close

func (e *PageEncryptor) Close() error

Close zeroizes and drops every DEK (spec 23 section 18.2).

func (*PageEncryptor) CurrentEpoch

func (e *PageEncryptor) CurrentEpoch() uint16

CurrentEpoch returns the epoch the next EncryptPage call uses.

func (*PageEncryptor) DecryptPage

func (e *PageEncryptor) DecryptPage(envelope []byte, pageClass uint8, pageNo, lsn uint64, epoch uint16) ([]byte, error)

DecryptPage authenticates and decrypts an envelope under the recorded epoch (spec 23 section 14.1). A failed tag returns ErrPageAuthFailed and never yields plaintext: the cause is a wrong key, a tampered page, or storage corruption.

func (*PageEncryptor) Enabled

func (e *PageEncryptor) Enabled() bool

Enabled reports true.

func (*PageEncryptor) EncryptPage

func (e *PageEncryptor) EncryptPage(plaintext []byte, pageClass uint8, pageNo, lsn uint64) ([]byte, error)

EncryptPage encrypts a plaintext page under the current epoch and returns the envelope: ciphertext, then the 12-byte nonce, then the 16-byte tag (spec 23 section 2.3, 14.1).

func (*PageEncryptor) ReleaseEpoch

func (e *PageEncryptor) ReleaseEpoch(epoch uint16)

ReleaseEpoch drops the DEK for an old epoch and zeroizes it (spec 23 section 4.3). The caller releases an epoch only once no live page references it, after a RekeyVacuum.

func (*PageEncryptor) ReloadDEK

func (e *PageEncryptor) ReloadDEK(epoch uint16, dek []byte)

ReloadDEK re-installs a DEK for an old epoch without changing the current epoch (spec 23 section 17.2). It recovers from a released DEK that a still-present page needs.

Jump to

Keyboard shortcuts

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