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
- Variables
- func DEK(masterKey []byte, dbID [16]byte, epoch uint16) ([]byte, error)
- func MasterKey(passphrase string, p Argon2Params) ([]byte, error)
- func MasterKeyFromRaw(key []byte) ([]byte, error)
- func VerificationTag(masterKey []byte) ([16]byte, error)
- func VerifyMasterKey(masterKey []byte, stored [16]byte) bool
- func Zeroize(b []byte)
- type Argon2Params
- type Cipher
- type Crypto
- type Descriptor
- type EncryptorConfig
- type ErrMissingDEK
- type ErrPageAuthFailed
- type KDF
- type NoCrypto
- type PageEncryptor
- func (e *PageEncryptor) AddDEK(epoch uint16, dek []byte)
- func (e *PageEncryptor) Close() error
- func (e *PageEncryptor) CurrentEpoch() uint16
- func (e *PageEncryptor) DecryptPage(envelope []byte, pageClass uint8, pageNo, lsn uint64, epoch uint16) ([]byte, error)
- func (e *PageEncryptor) Enabled() bool
- func (e *PageEncryptor) EncryptPage(plaintext []byte, pageClass uint8, pageNo, lsn uint64) ([]byte, error)
- func (e *PageEncryptor) ReleaseEpoch(epoch uint16)
- func (e *PageEncryptor) ReloadDEK(epoch uint16, dek []byte)
Constants ¶
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.
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.
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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.
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 ¶
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 ¶
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.
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) DecryptPage ¶
DecryptPage returns the envelope 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) 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.