crypto

package
v0.5.0 Latest Latest
Warning

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

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

Documentation

Overview

Package crypto wraps age. Cipher is satisfied by PassphraseCipher (MVP) and, post-MVP, a recipients-based cipher.

Index

Constants

View Source
const (
	SlotPassphrase = "passphrase"
	SlotRecipient  = "recipient"
)

Slot type tags.

Variables

View Source
var ErrNotRecipient = errors.New("blob was not encrypted under the current master key")

ErrNotRecipient reports that a ciphertext is valid age but was not encrypted to the key that tried to open it — the key is wrong, not the data. Callers (rotation's fallback read, the stale-cache retry) branch on it with errors.Is.

View Source
var ErrWrongPassphrase = errors.New("wrong passphrase")

ErrWrongPassphrase is returned when a passphrase does not match the ciphertext (or, for Header.Unlock, any key slot).

Functions

func EncryptToMasters added in v0.2.0

func EncryptToMasters(plaintext []byte, masters ...*MasterKey) ([]byte, error)

EncryptToMasters seals plaintext to every master's recipient, so any of their identities can decrypt it. Rotation uses this to keep a blob readable under both the old and new master at once during the transition.

func NewHeader

func NewHeader(passphrase, slotName string) (*Header, *MasterKey, error)

NewHeader generates a master key and a header with one passphrase slot: the primary slot, owned by whoever initialized the storage.

Types

type Cipher

type Cipher interface {
	Encrypt(plaintext []byte) ([]byte, error)
	Decrypt(ciphertext []byte) ([]byte, error)
}
type Header struct {
	Version   int    `json:"version"`
	Recipient string `json:"recipient"` // master public key (decorative)
	Revision  int    `json:"revision"`  // monotonic; bumped on every write (anti-rollback)
	Master    []byte `json:"master"`    // master identity, age-encrypted to every slot's public key
	Slots     []Slot `json:"slots"`
	Auth      []byte `json:"auth,omitempty"` // HMAC over the header keyed from the master (see auth.go)
}

Header is the parsed header object.

func ParseHeader

func ParseHeader(data []byte) (*Header, error)

func (*Header) AddPassphraseSlot added in v0.2.0

func (h *Header) AddPassphraseSlot(passphrase, name string, mk *MasterKey) error

AddPassphraseSlot creates a slot keypair, wraps its private key under the passphrase, and re-encrypts the master to include the new slot.

func (*Header) AddRecipientSlot added in v0.2.0

func (h *Header) AddRecipientSlot(recipient *age.X25519Recipient, name string, mk *MasterKey) error

AddRecipientSlot adds a teammate by their age public key, which is the slot key; they hold the private key and unlock with their own age identity.

func (*Header) Marshal

func (h *Header) Marshal() ([]byte, error)

func (*Header) PrimarySlot added in v0.2.0

func (h *Header) PrimarySlot() int

PrimarySlot returns the index of the primary slot, or -1 if none is marked.

func (*Header) RemoveSlot added in v0.2.0

func (h *Header) RemoveSlot(i int, mk *MasterKey) error

RemoveSlot deletes slot i and re-encrypts the master to the survivors, so the removed slot can no longer decrypt the master. It refuses to remove the last slot (which would brick the header). NOTE: this does not re-key blobs, so a holder who retained the master is not revoked; true revocation re-keys via SetMaster (rotate-master). mk is the current master.

func (*Header) RotateSlot added in v0.2.0

func (h *Header) RotateSlot(i int, newPassphrase string, slotKey *age.X25519Identity) error

RotateSlot re-wraps a passphrase slot's private key under a new passphrase. The master, the slot keypair, and Master are untouched, so other slots and every blob are unaffected. slotKey is the slot's private key, obtained from Unlock.

func (*Header) Seal added in v0.2.0

func (h *Header) Seal(mk *MasterKey) error

Seal sets the header's authentication tag over its current contents.

func (*Header) SetMaster added in v0.2.0

func (h *Header) SetMaster(mk *MasterKey) error

SetMaster installs a new master key, re-encrypting it to every slot's public key. Used by rotate-master after the blobs have been re-keyed.

func (*Header) SetPrimary added in v0.2.0

func (h *Header) SetPrimary(i int) error

SetPrimary makes slot i the sole primary slot.

func (*Header) Unlock

func (h *Header) Unlock(passphrase string) (*MasterKey, int, *age.X25519Identity, error)

Unlock opens the master via a passphrase. It finds the passphrase slot whose wrapped private key the passphrase decrypts, then decrypts the master with that slot key. Returns the master, the matched slot index, and the slot private key (needed to rotate that passphrase). ErrWrongPassphrase if none opens.

func (*Header) UnlockIdentity added in v0.2.0

func (h *Header) UnlockIdentity(id *age.X25519Identity) (*MasterKey, int, error)

UnlockIdentity opens the master via a teammate's age identity (their recipient slot). Returns the master and the matched slot index (or -1 if the identity decrypts the master but matches no slot's public key). ErrWrongPassphrase if the identity is not a recipient of the master.

func (*Header) Verify added in v0.2.0

func (h *Header) Verify(mk *MasterKey) error

Verify recomputes the tag and constant-time compares it to the stored one.

type MasterKey

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

MasterKey is the unwrapped master identity. It satisfies Cipher: Encrypt seals to the master recipient, Decrypt opens with the identity.

func GenerateMasterKey added in v0.2.0

func GenerateMasterKey() (*MasterKey, error)

GenerateMasterKey mints a fresh master key with no header (used by rotation).

func ParseMasterKey

func ParseMasterKey(s string) (*MasterKey, error)

ParseMasterKey parses the identity string form (used by the session cache, which stores the unwrapped master key, not the passphrase).

func (*MasterKey) Decrypt

func (m *MasterKey) Decrypt(ciphertext []byte) ([]byte, error)

func (*MasterKey) Encrypt

func (m *MasterKey) Encrypt(plaintext []byte) ([]byte, error)

func (*MasterKey) PublicKey added in v0.2.0

func (m *MasterKey) PublicKey() string

PublicKey returns the master's public key (its rollback-pin fingerprint).

func (*MasterKey) String

func (m *MasterKey) String() string

type PassphraseCipher

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

PassphraseCipher encrypts to an age scrypt recipient (symmetric, passphrase-derived). In the header model it wraps key-slot contents, not data blobs.

func NewPassphraseCipher

func NewPassphraseCipher(passphrase string) *PassphraseCipher

func (*PassphraseCipher) Decrypt

func (c *PassphraseCipher) Decrypt(ciphertext []byte) ([]byte, error)

func (*PassphraseCipher) Encrypt

func (c *PassphraseCipher) Encrypt(plaintext []byte) ([]byte, error)

type Slot

type Slot struct {
	Name      string `json:"name,omitempty"`
	Primary   bool   `json:"primary,omitempty"`
	Type      string `json:"type"`              // "passphrase" | "recipient"
	PublicKey string `json:"public_key"`        // recipient of Master
	Wrapped   []byte `json:"wrapped,omitempty"` // passphrase slots: slot private key, scrypt-encrypted
}

Slot is one credential that can unlock the master. Name identifies its owner (user@host). Primary marks the slot whose owner may rotate/remove other slots; advisory until header signing exists, but tooling refuses to remove or demote it. PublicKey is the slot's age public key (a recipient of Master); for a recipient slot it is the teammate's public key.

Jump to

Keyboard shortcuts

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