Documentation
¶
Overview ¶
Package secretbox encrypts data at rest with a password.
It pairs a key-derivation function (Argon2id by default) with an authenticated cipher (AES-256-GCM by default) behind two layers:
- One-shot functions Seal and Unseal produce a self-describing blob that carries its own salt and KDF parameters — decryptable years later with only the password.
- A Vault manages the long-lived "secure mode" pattern: a metadata file with a salt and an encrypted sentinel, an in-memory session key, and transparent file encryption, plus password change and key rotation.
Both layers default to the same primitives matcha uses, and both accept custom KDF/Cipher implementations via options.
Index ¶
- Variables
- func Seal(plaintext []byte, password string) ([]byte, error)
- func SealWith(plaintext []byte, password string, kdf KDF, c Cipher) ([]byte, error)
- func Unseal(blob []byte, password string) ([]byte, error)
- type AESGCM
- type Argon2id
- type Argon2idParams
- type ChaCha20Poly1305
- type Cipher
- type KDF
- type Meta
- type Option
- type Vault
- func (v *Vault) ChangePassword(newPassword string) error
- func (v *Vault) Decrypt(ciphertext []byte) ([]byte, error)
- func (v *Vault) Encrypt(plaintext []byte) ([]byte, error)
- func (v *Vault) Init(password string) error
- func (v *Vault) Initialized() bool
- func (v *Vault) Key() []byte
- func (v *Vault) Lock()
- func (v *Vault) Locked() bool
- func (v *Vault) ReadFile(path string) ([]byte, error)
- func (v *Vault) Rekey(newPassword string, files []string) error
- func (v *Vault) Unlock(password string) error
- func (v *Vault) WriteFile(path string, data []byte, perm os.FileMode) error
Constants ¶
This section is empty.
Variables ¶
var ( // ErrDecrypt is returned for any decryption failure: wrong key, truncated // input, or authentication-tag mismatch. It deliberately does not say // which, to avoid leaking information to an attacker. ErrDecrypt = errors.New("secretbox: decryption failed") // ErrWrongPassword is returned by Vault.Unlock when the password fails the // sentinel check. ErrWrongPassword = errors.New("secretbox: incorrect password") // ErrLocked is returned by Vault operations that need the session key while // the vault is locked. ErrLocked = errors.New("secretbox: vault is locked") // ErrNotInitialized is returned when a vault's metadata file is absent. ErrNotInitialized = errors.New("secretbox: vault not initialized") // ErrUnsupported is returned when metadata names a KDF or cipher this build // does not know how to construct. ErrUnsupported = errors.New("secretbox: unsupported algorithm") )
Sentinel errors. Compare with errors.Is.
var DefaultArgon2id = Argon2idParams{Time: 3, Memory: 64 * 1024, Threads: 4}
DefaultArgon2id is a sensible interactive-login baseline: 3 passes over 64 MiB across 4 lanes. It matches the parameters matcha ships with.
Functions ¶
func Seal ¶
Seal encrypts plaintext with password using the default primitives (Argon2id + AES-256-GCM) and returns a self-describing blob.
The blob embeds a fresh random salt and the KDF parameters, so Unseal needs only the password. Use SealWith to choose different primitives.
Types ¶
type AESGCM ¶
type AESGCM struct{}
AESGCM is the default cipher: AES-256 in Galois/Counter Mode. The key must be exactly 32 bytes. This matches matcha's on-disk format.
type Argon2id ¶
type Argon2id struct {
// contains filtered or unexported fields
}
Argon2id is the default KDF. It implements KDF using the Argon2id variant, which resists both GPU and side-channel attacks.
func NewArgon2id ¶
func NewArgon2id(p Argon2idParams) Argon2id
NewArgon2id returns an Argon2id KDF with the given parameters. Passing the zero value falls back to DefaultArgon2id.
type Argon2idParams ¶
type Argon2idParams struct {
Time uint32 // number of passes over memory
Memory uint32 // memory in KiB
Threads uint8 // parallelism
}
Argon2idParams configures the Argon2id key-derivation function.
The zero value is not usable; start from DefaultArgon2id and adjust. Higher Time/Memory raise the cost of a brute-force attack at the expense of latency on the legitimate path.
type ChaCha20Poly1305 ¶
type ChaCha20Poly1305 struct{}
ChaCha20Poly1305 is an alternative cipher using the XChaCha20-Poly1305 AEAD, which has a 24-byte nonce (safe to generate randomly without a counter). The key must be exactly 32 bytes. Prefer this on platforms without AES hardware acceleration.
func (ChaCha20Poly1305) Decrypt ¶
func (ChaCha20Poly1305) Decrypt(ciphertext, key []byte) ([]byte, error)
Decrypt implements Cipher.
type Cipher ¶
type Cipher interface {
// Encrypt seals plaintext under key, returning nonce-prefixed ciphertext.
Encrypt(plaintext, key []byte) ([]byte, error)
// Decrypt opens ciphertext produced by Encrypt. It returns ErrDecrypt on
// any authentication or format failure.
Decrypt(ciphertext, key []byte) ([]byte, error)
// ID is the stable identifier persisted in vault metadata.
ID() string
// KeySize is the required key length in bytes.
KeySize() int
}
Cipher is an authenticated encryption scheme keyed by a KDF-derived key.
Encrypt must generate a fresh random nonce per call and prepend it to the returned ciphertext; Decrypt reverses that framing. Implementations are stateless and safe for concurrent use.
type KDF ¶
type KDF interface {
// DeriveKey stretches password+salt into a key of keyLen bytes.
DeriveKey(password string, salt []byte, keyLen uint32) []byte
// ID is the stable identifier persisted in vault metadata (e.g. "argon2id").
ID() string
// Params returns the tunable parameters so they can be stored alongside the
// salt and reproduced on unlock.
Params() map[string]uint32
}
KDF derives a symmetric key from a low-entropy password and a random salt.
Implementations must be deterministic: the same password, salt, and parameters always produce the same key. The returned key length must equal the cipher's KeySize.
type Meta ¶
type Meta struct {
Version int `json:"version"`
KDF string `json:"kdf"`
Cipher string `json:"cipher"`
Salt string `json:"salt"` // base64
Sentinel string `json:"sentinel"` // base64 ciphertext of sentinelPlaintext
Params map[string]uint32 `json:"params"` // KDF parameters
}
Meta is the JSON document written to the vault's metadata file. It holds everything needed to re-derive the key from a password — except the password itself. It is safe to store in plaintext.
type Option ¶
type Option func(*Vault)
Option configures a Vault.
func WithCipher ¶
WithCipher sets the cipher used for new vaults. On Unlock the cipher is reconstructed from metadata. Defaults to AES-256-GCM.
type Vault ¶
type Vault struct {
// contains filtered or unexported fields
}
Vault implements the "secure mode" pattern: a metadata file with a salt and an encrypted sentinel, an in-memory session key derived on Unlock, and transparent file encryption while unlocked.
A Vault is safe for concurrent use once unlocked. Lock zeroes the key.
func NewVault ¶
NewVault returns a vault backed by the metadata file at metaPath. It does not touch the filesystem; call Init to create a new vault or Unlock to open an existing one.
func (*Vault) ChangePassword ¶
ChangePassword re-keys the vault to newPassword. The vault must be unlocked (newer code can Unlock with the old password first). It generates a fresh salt, derives a new key, re-encrypts the sentinel, and rewrites metadata.
Existing files encrypted with the old key are NOT touched — pass them to Rekey instead, which migrates files and rotates the password atomically from the caller's perspective.
func (*Vault) Init ¶
Init creates a new vault: it generates a random salt, derives the key from password, encrypts the sentinel, writes the metadata file, and leaves the vault unlocked with the session key set.
It fails if the vault is already initialized.
func (*Vault) Initialized ¶
Initialized reports whether the metadata file exists, i.e. whether secure mode has been enabled for this vault.
func (*Vault) Key ¶ added in v0.1.0
Key returns a copy of the current session key, or nil if the vault is locked.
The returned slice is an independent copy — mutating it does not affect the key stored inside the vault, and the vault's Lock method will not zero the copy. Callers that hold the key beyond the immediate call should zero it explicitly (e.g. with a deferred loop) once it is no longer needed, to shorten the window during which key material sits in memory.
The primary use case is bridging to an API that stores or passes around a raw key (as opposed to driving all encryption through the vault itself). For normal encrypt/decrypt operations, prefer Encrypt, Decrypt, ReadFile, and WriteFile, which never expose the key.
func (*Vault) Lock ¶
func (v *Vault) Lock()
Lock zeroes and discards the session key. After Lock, Encrypt/Decrypt/ ReadFile/WriteFile return ErrLocked until the next Unlock.
func (*Vault) ReadFile ¶
ReadFile reads path and decrypts it with the session key. Returns ErrLocked if locked.
func (*Vault) Rekey ¶
Rekey migrates files to a new password: it decrypts each path with the current key, rotates the vault to newPassword, then re-encrypts each path with the new key. The vault must be unlocked.
Files that do not exist are skipped. If a file fails to decrypt with the current key it is left untouched and an error is returned before any metadata change, so a wrong assumption cannot corrupt data.
func (*Vault) Unlock ¶
Unlock derives the key from password, verifies it against the stored sentinel, and on success stores the session key. It reconstructs the KDF and cipher from metadata, so a vault created with non-default primitives unlocks correctly regardless of the options passed to NewVault.
It returns ErrWrongPassword on mismatch and ErrNotInitialized if no metadata file exists.