keychain

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Jan 27, 2026 License: MIT Imports: 20 Imported by: 0

README

keychain

keychain is a port and extension of Operator's Keychain library for Swift (specifically, KeychainLinux)

Documentation

Overview

Package keychain provides secure local secret storage with minimal memory exposure.

Design principles:

  • Each secret stored separately (not one big blob)
  • Keys loaded into memory only when needed
  • Age encryption for at-rest storage
  • Symmetric keys derived via ECDH + HKDF
  • Protected memory via memguard for sensitive material

Index

Constants

View Source
const DefaultIdentityID = "default"

DefaultIdentityID is the ID used for the default/primary identity.

Variables

This section is empty.

Functions

func DecryptWith

func DecryptWith(key *memguard.LockedBuffer, ciphertext, nonce []byte, cs CipherSuite) ([]byte, error)

DecryptWith decrypts data using a provided symmetric key.

func EncryptWith

func EncryptWith(key *memguard.LockedBuffer, plaintext []byte, cs CipherSuite) (ciphertext, nonce []byte, err error)

EncryptWith encrypts data using a provided symmetric key.

Types

type AddressBook

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

AddressBook manages contacts stored individually for minimal exposure.

func NewAddressBook

func NewAddressBook(store *Store) *AddressBook

NewAddressBook creates an address book backed by the secret store.

func (*AddressBook) AddContact

func (ab *AddressBook) AddContact(contact *Contact) error

AddContact stores a new contact with StatusDefault.

func (*AddressBook) AssociateOTPPad

func (ab *AddressBook) AssociateOTPPad(contactID string, otpID SecretID) error

AssociateOTPPad links a one-time pad to a contact (replaces any existing pad).

func (*AddressBook) AssociateP256Key

func (ab *AddressBook) AssociateP256Key(contactID string, keyID SecretID) error

AssociateP256Key links a P-256 public key to a contact.

func (*AddressBook) AssociateX25519Key

func (ab *AddressBook) AssociateX25519Key(contactID string, keyID SecretID) error

AssociateX25519Key links an X25519 public key to a contact.

func (*AddressBook) CanDecryptWith

func (ab *AddressBook) CanDecryptWith(contactID string, keyType KeyExchangeType) (bool, error)

CanDecryptWith returns true if decryption is possible with the specified key type. Requires the contact's public key and status >= StatusInvited.

func (*AddressBook) CanEncryptWith

func (ab *AddressBook) CanEncryptWith(contactID string, keyType KeyExchangeType) (bool, error)

CanEncryptWith returns true if encryption is possible with the specified key type. Requires the contact's public key and status >= StatusRequested.

func (*AddressBook) DecryptFromContact

func (ab *AddressBook) DecryptFromContact(
	myPrivKeyID SecretID,
	contactID string,
	ciphertext, nonce []byte,
	keyType KeyType,
	cs CipherSuite,
) ([]byte, error)

DecryptFromContact derives a shared key with a contact and decrypts data.

func (*AddressBook) DecryptFromContactWithIdentity

func (ab *AddressBook) DecryptFromContactWithIdentity(
	store *Store,
	identityID string,
	contactID string,
	ciphertext, nonce []byte,
	keyType KeyType,
	cs CipherSuite,
) ([]byte, error)

DecryptFromContactWithIdentity decrypts data from a contact using the identity's key.

func (*AddressBook) DeleteContact

func (ab *AddressBook) DeleteContact(id string) error

DeleteContact removes a contact (does not delete associated keys).

func (*AddressBook) DeleteContactWithKeys

func (ab *AddressBook) DeleteContactWithKeys(id string) error

DeleteContactWithKeys removes a contact and all associated keys.

func (*AddressBook) DisassociateOTPPad

func (ab *AddressBook) DisassociateOTPPad(contactID string) error

DisassociateOTPPad removes the OTP pad association from a contact (does not delete the pad).

func (*AddressBook) EncryptForContact

func (ab *AddressBook) EncryptForContact(
	myPrivKeyID SecretID,
	contactID string,
	plaintext []byte,
	keyType KeyType,
	cs CipherSuite,
) (ciphertext, nonce []byte, err error)

EncryptForContact derives a shared key with a contact and encrypts data. keyType specifies which key type to use (KeyTypeP256 or KeyTypeX25519).

func (*AddressBook) EncryptForContactWithIdentity

func (ab *AddressBook) EncryptForContactWithIdentity(
	store *Store,
	identityID string,
	contactID string,
	plaintext []byte,
	keyType KeyType,
	cs CipherSuite,
) (ciphertext, nonce []byte, err error)

EncryptForContactWithIdentity encrypts data for a contact using the identity's key.

func (*AddressBook) GetContact

func (ab *AddressBook) GetContact(id string) (*Contact, error)

GetContact retrieves a contact (without loading any key material).

func (*AddressBook) GetContactPublicKeys

func (ab *AddressBook) GetContactPublicKeys(id string) (*ContactPublicKeys, error)

GetContactPublicKeys loads the actual public key bytes for a contact. For contacts, we store the public key directly as the secret (not a private key).

func (*AddressBook) GetKeyStatus

func (ab *AddressBook) GetKeyStatus(contactID string, keyType KeyExchangeType) (FriendStatus, error)

GetKeyStatus returns the friend status for a specific key type.

func (*AddressBook) IsKeyVerified

func (ab *AddressBook) IsKeyVerified(contactID string, keyType KeyExchangeType) (bool, error)

IsKeyVerified returns true if the specified key type has been verified.

func (*AddressBook) ListContacts

func (ab *AddressBook) ListContacts() ([]*Contact, error)

ListContacts returns all contacts (without loading any key material).

func (*AddressBook) MarkInvited

func (ab *AddressBook) MarkInvited(contactID string, keyType KeyExchangeType) error

MarkInvited transitions a key's status from Default to Invited. Call this after sending your public key/pad to the contact.

func (*AddressBook) MarkRequested

func (ab *AddressBook) MarkRequested(contactID string, keyType KeyExchangeType) error

MarkRequested transitions a key's status from Default to Requested. Call this after receiving the contact's public key/pad.

func (*AddressBook) MarkVerified

func (ab *AddressBook) MarkVerified(contactID string, keyType KeyExchangeType) error

MarkVerified transitions a key's status to Verified. Call this after out-of-band key verification.

func (*AddressBook) SetKeyStatus

func (ab *AddressBook) SetKeyStatus(contactID string, keyType KeyExchangeType, status FriendStatus) error

SetKeyStatus directly sets the friend status for a key type (use with caution).

func (*AddressBook) UpdateContact

func (ab *AddressBook) UpdateContact(contact *Contact) error

UpdateContact updates an existing contact.

type CipherSuite

type CipherSuite string

CipherSuite identifies the symmetric cipher used for encryption.

const (
	CipherAES256GCM        CipherSuite = "aes-256-gcm"
	CipherChaCha20Poly1305 CipherSuite = "chacha20-poly1305"
)

func (CipherSuite) KeyLength

func (cs CipherSuite) KeyLength() int

KeyLength returns the required key length in bytes for the cipher suite.

type Contact

type Contact struct {
	ID        string `json:"id"`
	Name      string `json:"name"`
	Notes     string `json:"notes,omitempty"`
	CreatedAt int64  `json:"created_at"`
	UpdatedAt int64  `json:"updated_at"`

	// P-256 key reference and exchange status
	P256KeyID  SecretID     `json:"p256_key_id,omitempty"`
	P256Status FriendStatus `json:"p256_status"`

	// X25519 key reference and exchange status
	X25519KeyID  SecretID     `json:"x25519_key_id,omitempty"`
	X25519Status FriendStatus `json:"x25519_status"`

	// OTP pad reference and exchange status (at most one pad per contact)
	OTPPadID  SecretID     `json:"otp_pad_id,omitempty"`
	OTPStatus FriendStatus `json:"otp_status"`
}

Contact represents an entry in the address book. Keys are stored separately in the keychain; contacts only hold references. Each key type has its own exchange status since key exchange is per-key-type.

type ContactPublicKeys

type ContactPublicKeys struct {
	P256PublicKey   []byte
	X25519PublicKey []byte
}

ContactPublicKeys holds the actual public key bytes for a contact. This is returned only when explicitly requested.

type FriendStatus

type FriendStatus int

FriendStatus tracks the key exchange state with a contact.

const (
	// StatusDefault - Contact added, no key exchange yet
	StatusDefault FriendStatus = iota
	// StatusInvited - User sent their public key to this contact
	StatusInvited
	// StatusRequested - Contact sent their public key to user
	StatusRequested
	// StatusApproved - Bidirectional key exchange complete, unverified
	StatusApproved
	// StatusVerified - Keys verified out-of-band (e.g., in person)
	StatusVerified
)

func (FriendStatus) String

func (s FriendStatus) String() string

String returns a human-readable status name.

type Identity

type Identity struct {
	ID          string   `json:"id"`
	Name        string   `json:"name"`
	CreatedAt   int64    `json:"created_at"`
	UpdatedAt   int64    `json:"updated_at"`
	P256KeyID   SecretID `json:"p256_key_id,omitempty"`
	X25519KeyID SecretID `json:"x25519_key_id,omitempty"`
}

Identity represents the local user's identity with their private keys.

type IdentityPublicKeys

type IdentityPublicKeys struct {
	P256PublicKey   []byte
	X25519PublicKey []byte
}

IdentityPublicKeys holds the public keys for an identity.

type KeyExchangeType

type KeyExchangeType int

KeyExchangeType specifies which key type a status operation applies to.

const (
	KeyExchangeP256 KeyExchangeType = iota
	KeyExchangeX25519
	KeyExchangeOTP
)

type KeyType

type KeyType string

KeyType identifies the type of key stored.

const (
	KeyTypeP256         KeyType = "p256"
	KeyTypeX25519       KeyType = "x25519"
	KeyTypeP256Public   KeyType = "p256-public"
	KeyTypeX25519Public KeyType = "x25519-public"
	KeyTypeAES256       KeyType = "aes256"
	KeyTypeChaCha20     KeyType = "chacha20"
	KeyTypeContact      KeyType = "contact"
)
const KeyTypeIdentity KeyType = "identity"

KeyTypeIdentity is the key type for identity metadata.

const KeyTypeOTP KeyType = "otp"

KeyTypeOTP is the key type for one-time pad secrets.

const KeyTypeOTPMeta KeyType = "otp-meta"

KeyTypeOTPMeta is the key type for one-time pad metadata.

type OTPPad

type OTPPad struct {
	ID        SecretID `json:"id"`
	Label     string   `json:"label,omitempty"`
	Length    int      `json:"length"` // Total length of the pad in bytes
	Offset    int      `json:"offset"` // Current offset - bytes before this are consumed
	CreatedAt int64    `json:"created_at"`
}

OTPPad represents a one-time pad for XOR-based encryption. The pad is consumed sequentially and bytes are never reused.

type OTPResult

type OTPResult struct {
	Data        []byte // The XOR'd output
	BytesUsed   int    // Number of pad bytes consumed
	BytesLeft   int    // Remaining bytes in the pad
	StartOffset int    // Offset where this operation started
}

OTPResult contains the result of an OTP encryption/decryption operation.

type SecretID

type SecretID string

SecretID uniquely identifies a secret in the store.

type SecretMetadata

type SecretMetadata struct {
	ID        SecretID `json:"id"`
	KeyType   KeyType  `json:"key_type"`
	Label     string   `json:"label,omitempty"`
	CreatedAt int64    `json:"created_at"`
	PublicKey []byte   `json:"public_key,omitempty"`
}

SecretMetadata is stored inside the encrypted envelope.

type Store

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

Store manages secrets on disk using age encryption.

func OpenStore

func OpenStore(path string, password []byte) (*Store, error)

OpenStore opens or creates a secret store at the given path. The password is used with age's scrypt-based encryption.

func (*Store) AddP256KeyToIdentity

func (s *Store) AddP256KeyToIdentity(id string) ([]byte, error)

AddP256KeyToIdentity generates and adds a P-256 key to an existing identity.

func (*Store) AddX25519KeyToIdentity

func (s *Store) AddX25519KeyToIdentity(id string) ([]byte, error)

AddX25519KeyToIdentity generates and adds an X25519 key to an existing identity.

func (*Store) Close

func (s *Store) Close()

Close securely destroys the password from memory.

func (*Store) CreateDefaultIdentity

func (s *Store) CreateDefaultIdentity(name string) (*Identity, error)

CreateDefaultIdentity creates the default identity with both key types.

func (*Store) CreateIdentity

func (s *Store) CreateIdentity(id string, name string, generateP256, generateX25519 bool) (*Identity, error)

CreateIdentity creates a new identity with the specified key types. If generateP256 is true, a P-256 keypair is generated. If generateX25519 is true, an X25519 keypair is generated.

func (*Store) DecryptWithDerivedKey

func (s *Store) DecryptWithDerivedKey(
	privateKeyID SecretID,
	peerPublicKey []byte,
	info []byte,
	ciphertext, nonce []byte,
	cs CipherSuite,
) ([]byte, error)

DecryptWithDerivedKey derives a shared key via ECDH and decrypts data. This is a convenience function that combines DeriveSharedKey and DecryptWith.

func (*Store) DefaultIdentityExists

func (s *Store) DefaultIdentityExists() bool

DefaultIdentityExists checks if the default identity exists.

func (*Store) DeleteIdentity

func (s *Store) DeleteIdentity(id string) error

DeleteIdentity removes an identity and its associated keys.

func (*Store) DeleteOTPPad

func (s *Store) DeleteOTPPad(id SecretID) error

DeleteOTPPad removes an OTP pad and its metadata.

func (*Store) DeleteSecret

func (s *Store) DeleteSecret(id SecretID) error

DeleteSecret securely removes a secret.

func (*Store) DeriveSharedKey

func (s *Store) DeriveSharedKey(
	privateKeyID SecretID,
	peerPublicKey []byte,
	info []byte,
	keyLen int,
) (*memguard.LockedBuffer, error)

DeriveSharedKey performs ECDH with a peer's public key and derives a symmetric key using HKDF. The private key is loaded only for this operation.

func (*Store) EncryptWithDerivedKey

func (s *Store) EncryptWithDerivedKey(
	privateKeyID SecretID,
	peerPublicKey []byte,
	info []byte,
	plaintext []byte,
	cs CipherSuite,
) (ciphertext, nonce []byte, err error)

EncryptWithDerivedKey derives a shared key via ECDH and encrypts data. This is a convenience function that combines DeriveSharedKey and EncryptWith.

func (*Store) GenerateP256

func (s *Store) GenerateP256(id SecretID, label string) (publicKey []byte, err error)

GenerateP256 creates a new P-256 keypair and stores the private key.

func (*Store) GenerateSymmetricKey

func (s *Store) GenerateSymmetricKey(id SecretID, label string, keyLen int) error

GenerateSymmetricKey creates a random symmetric key of the specified length.

func (*Store) GenerateX25519

func (s *Store) GenerateX25519(id SecretID, label string) (publicKey []byte, err error)

GenerateX25519 creates a new X25519 keypair and stores the private key.

func (*Store) GetDefaultIdentity

func (s *Store) GetDefaultIdentity() (*Identity, error)

GetDefaultIdentity retrieves the default identity.

func (*Store) GetDefaultIdentityPublicKeys

func (s *Store) GetDefaultIdentityPublicKeys() (*IdentityPublicKeys, error)

GetDefaultIdentityPublicKeys retrieves the public keys for the default identity.

func (*Store) GetIdentity

func (s *Store) GetIdentity(id string) (*Identity, error)

GetIdentity retrieves an identity by ID.

func (*Store) GetIdentityPublicKeys

func (s *Store) GetIdentityPublicKeys(id string) (*IdentityPublicKeys, error)

GetIdentityPublicKeys retrieves the public keys for an identity.

func (*Store) GetMetadata

func (s *Store) GetMetadata(id SecretID) (*SecretMetadata, error)

GetMetadata returns metadata for a secret without decrypting the secret itself. Note: This still requires decryption since metadata is inside the age envelope.

func (*Store) GetOTPPadInfo

func (s *Store) GetOTPPadInfo(id SecretID) (*OTPPad, error)

GetOTPPadInfo retrieves metadata about an OTP pad without loading the pad itself.

func (*Store) IdentityExists

func (s *Store) IdentityExists(id string) bool

IdentityExists checks if an identity exists.

func (*Store) ImportP256KeyToIdentity

func (s *Store) ImportP256KeyToIdentity(id string, privateKey []byte) error

ImportP256KeyToIdentity imports an existing P-256 private key to an identity.

func (*Store) ImportX25519KeyToIdentity

func (s *Store) ImportX25519KeyToIdentity(id string, privateKey []byte) error

ImportX25519KeyToIdentity imports an existing X25519 private key to an identity.

func (*Store) ListIdentities

func (s *Store) ListIdentities() ([]*Identity, error)

ListIdentities returns all stored identities.

func (*Store) ListOTPPads

func (s *Store) ListOTPPads() ([]OTPPad, error)

ListOTPPads returns metadata for all stored OTP pads.

func (*Store) ListSecrets

func (s *Store) ListSecrets() ([]SecretMetadata, error)

ListSecrets returns metadata for all stored secrets.

func (*Store) LoadPublicKey

func (s *Store) LoadPublicKey(id SecretID) ([]byte, KeyType, error)

LoadPublicKey loads a public key from the store.

func (*Store) LoadSecret

func (s *Store) LoadSecret(id SecretID) (*memguard.LockedBuffer, *SecretMetadata, error)

LoadSecret decrypts and returns a secret in protected memory. The caller MUST call Destroy() on the returned LockedBuffer when done.

func (*Store) LoadSymmetricKey

func (s *Store) LoadSymmetricKey(id SecretID) (*memguard.LockedBuffer, error)

LoadSymmetricKey loads a symmetric key into protected memory. The caller MUST call Destroy() on the returned LockedBuffer when done.

func (*Store) OTPBytesRemaining

func (s *Store) OTPBytesRemaining(id SecretID) (int, error)

OTPBytesRemaining returns how many unused bytes remain in the pad.

func (*Store) OTPDecrypt

func (s *Store) OTPDecrypt(id SecretID, ciphertext []byte) (*OTPResult, error)

OTPDecrypt XORs data with the next unused bytes from the pad. This is identical to OTPEncrypt (XOR is symmetric) but provided for clarity.

func (*Store) OTPDecryptBits

func (s *Store) OTPDecryptBits(id SecretID, data []byte, numBits int) (*OTPResult, error)

OTPDecryptBits XORs a specific number of bits with the pad.

func (*Store) OTPDecryptWSPR

func (s *Store) OTPDecryptWSPR(id SecretID, data [11]byte) (*OTPResult, [11]byte, error)

OTPDecryptWSPR decrypts a PackedWSPRMessage-format 11-byte array.

func (*Store) OTPEncrypt

func (s *Store) OTPEncrypt(id SecretID, plaintext []byte) (*OTPResult, error)

OTPEncrypt XORs data with the next unused bytes from the pad. The pad offset is advanced to prevent reuse.

func (*Store) OTPEncryptBits

func (s *Store) OTPEncryptBits(id SecretID, data []byte, numBits int) (*OTPResult, error)

OTPEncryptBits XORs a specific number of bits with the pad. This is useful for the 50-bit packed format you mentioned. The bits are taken from the least significant bits of the input bytes.

func (*Store) OTPEncryptWSPR

func (s *Store) OTPEncryptWSPR(id SecretID, data [11]byte) (*OTPResult, [11]byte, error)

OTPEncryptWSPR encrypts a PackedWSPRMessage-format 11-byte array. This XORs the 50 data bits (B0-B5 full bytes + B6 low 2 bits) while preserving B6 high bits and B7-B10 as zero.

Format from codex.go:

  • B0-B5: Full octets (8 bits each = 48 bits)
  • B6: Quartet (2 bits, values 0-3)
  • B7-B10: Always zero

func (*Store) OTPPeek

func (s *Store) OTPPeek(id SecretID, length int) ([]byte, error)

OTPPeek reads pad bytes without consuming them. Use for verification only.

func (*Store) OTPSetOffset

func (s *Store) OTPSetOffset(id SecretID, offset int) error

OTPSetOffset manually sets the pad offset. Use with caution.

func (*Store) OTPSkip

func (s *Store) OTPSkip(id SecretID, numBytes int) error

OTPSkip advances the pad offset without using the bytes for encryption. Use when synchronizing with a remote party that has already consumed bytes.

func (*Store) SecretExists

func (s *Store) SecretExists(id SecretID) bool

SecretExists checks if a secret exists without decrypting it.

func (*Store) StoreOTPPad

func (s *Store) StoreOTPPad(id SecretID, pad []byte, label string) error

StoreOTPPad stores a new one-time pad.

func (*Store) StoreP256PublicKey

func (s *Store) StoreP256PublicKey(id SecretID, publicKey []byte, label string) error

StoreP256PublicKey stores a P-256 public key (for a contact).

func (*Store) StoreSecret

func (s *Store) StoreSecret(id SecretID, keyType KeyType, secret []byte, label string) error

StoreSecret encrypts and persists a secret using age.

func (*Store) StoreX25519PublicKey

func (s *Store) StoreX25519PublicKey(id SecretID, publicKey []byte, label string) error

StoreX25519PublicKey stores an X25519 public key (for a contact).

func (*Store) UpdateIdentity

func (s *Store) UpdateIdentity(identity *Identity) error

UpdateIdentity updates an identity's metadata (name, etc.).

Jump to

Keyboard shortcuts

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