Help us shape the future of Go! Take the 2026 Go Developer Survey

crypto

package
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Sep 28, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package crypto implements the SyncMyEnv key hierarchy on vetted libraries only (age, x/crypto) — no custom primitives.

master password ──argon2id──▶ KEK₁ ─┐
                                   ├─ XChaCha20-Poly1305 ─▶ vault identity
recovery key ─────hkdf─────▶ KEK₂ ─┘      (age hybrid: ML-KEM-768 + X25519)
                                                  │
                                      vault recipient (public key)
                                                  │
                     the daemon seals every revision to the recipient

Sealing needs only the public recipient, so the daemon can run unattended without holding any secret. Opening (history, restore, share) needs the identity, which is only ever unwrapped in memory.

The hybrid key is post-quantum: ciphertext synced today stays safe even if someone stores it and waits for a quantum computer.

Index

Constants

View Source
const MaxPlaintext = 1 << 20

MaxPlaintext bounds what we'll seal/open. Env files are tiny; anything bigger is almost certainly a mistake (or an attack on memory).

View Source
const MinPasswordLen = 12

MinPasswordLen for the vault master password.

Variables

View Source
var (
	ErrWrongPassword = errors.New("wrong master password")
	ErrWrongRecovery = errors.New("recovery key doesn't match this vault")
	ErrCorrupt       = errors.New("key file is corrupt or was tampered with")
	ErrWeakPassword  = fmt.Errorf("master password must be at least %d characters", MinPasswordLen)
)
View Source
var DefaultArgon = ArgonParams{Memory: 64 * 1024, Time: 3, Threads: 4}

DefaultArgon: 64 MiB, 3 passes, 4 lanes (~0.2–0.5s on a laptop).

View Source
var ErrBadRecoveryKey = errors.New("invalid recovery key — check for typos")

ErrBadRecoveryKey means the key is malformed or mistyped.

View Source
var ErrTooLarge = errors.New("crypto: data too large")

ErrTooLarge is returned for plaintexts or ciphertexts above MaxPlaintext.

Functions

func Fingerprint

func Fingerprint(recipient string) string

Fingerprint is a short, human-comparable ID for a (very long) PQ recipient, e.g. "5c1e:9a07:33d2:fb18". Compare it across machines to be sure you're talking about the same vault.

func Seal

func Seal(recipient string, plaintext []byte) ([]byte, error)

Seal encrypts plaintext to a vault recipient. It needs no secret.

Types

type ArgonParams

type ArgonParams struct {
	Memory  uint32 `json:"m"` // KiB
	Time    uint32 `json:"t"`
	Threads uint8  `json:"p"`
}

ArgonParams are stored per key file, so they can be raised later without breaking existing vaults.

type KeyFile

type KeyFile struct {
	Version   int       `json:"version"`
	Recipient string    `json:"recipient"`
	CreatedAt time.Time `json:"created_at"`
	Password  *Wrapped  `json:"password"`
	Recovery  *Wrapped  `json:"recovery"`
}

KeyFile is what's stored at ~/.syncmyenv/keys.json and (later) synced to remotes for new-device recovery. It contains no plaintext secret.

func NewKeyFile

func NewKeyFile(k *VaultKey, password string, rk RecoveryKey) (*KeyFile, error)

NewKeyFile wraps k under both the master password and the recovery key.

func (*KeyFile) SetPassword

func (f *KeyFile) SetPassword(k *VaultKey, password string) error

SetPassword (re)wraps k under a new master password. The recovery key and all sealed data are unaffected.

func (*KeyFile) UnlockWithPassword

func (f *KeyFile) UnlockWithPassword(password string) (*VaultKey, error)

UnlockWithPassword unwraps the vault key with the master password.

func (*KeyFile) UnlockWithRecovery

func (f *KeyFile) UnlockWithRecovery(rk RecoveryKey) (*VaultKey, error)

UnlockWithRecovery unwraps the vault key with the recovery key.

type RecoveryKey

type RecoveryKey [30]byte

A recovery key is 240 random bits + a 16-bit checksum, written in Crockford base32 (no I, L, O, U) as 13 groups of 4:

SME1-7QK2-M4XW-…-R9TD

The checksum catches typos before we burn an unlock attempt.

func NewRecoveryKey

func NewRecoveryKey() (RecoveryKey, error)

NewRecoveryKey returns a fresh random recovery key.

func ParseRecoveryKey

func ParseRecoveryKey(s string) (RecoveryKey, error)

ParseRecoveryKey accepts any case, spaces or dashes, and Crockford look-alikes (I/L→1, O→0).

func (RecoveryKey) String

func (k RecoveryKey) String() string

type VaultKey

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

VaultKey is the vault's secret identity. Keep it in memory only.

func NewVaultKey

func NewVaultKey() (*VaultKey, error)

NewVaultKey generates a fresh post-quantum hybrid vault key.

func (*VaultKey) CertifyDevice

func (k *VaultKey) CertifyDevice(devicePub []byte) []byte

CertifyDevice vouches for a device signing key. Only holders of the vault key can produce (or check) a certificate — the server can't, even though it knows the public recipient. This is what stops a malicious server from forging log entries: sealing to the recipient is public, certifying isn't.

func (*VaultKey) MAC

func (k *VaultKey) MAC(data []byte) []byte

MAC is a keyed hash for change detection and dedup. Never store a bare hash of a secret: short .env values would be brute-forceable offline.

func (*VaultKey) Open

func (k *VaultKey) Open(ciphertext []byte) ([]byte, error)

Open decrypts a sealed blob.

func (*VaultKey) Recipient

func (k *VaultKey) Recipient() string

Recipient is the public key revisions are sealed to (age1pq1…).

func (*VaultKey) VerifyDeviceCert

func (k *VaultKey) VerifyDeviceCert(devicePub, cert []byte) bool

VerifyDeviceCert checks a certificate made by CertifyDevice.

type Wrapped

type Wrapped struct {
	KDF        string       `json:"kdf"` // "argon2id" | "hkdf-sha256"
	Argon      *ArgonParams `json:"argon,omitempty"`
	Salt       []byte       `json:"salt"`
	Nonce      []byte       `json:"nonce"`
	Ciphertext []byte       `json:"ciphertext"`
}

Wrapped is the vault identity encrypted under one unlock method.

Jump to

Keyboard shortcuts

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