Documentation
¶
Index ¶
- Constants
- Variables
- func DeriveDBKey(mk []byte) []byte
- func DeriveDBKeyHex(mk []byte) string
- func DeriveIdentityKey(mk []byte, generation uint32) ed25519.PrivateKey
- func DeriveKEK(secret string, slot *KEKSlot) ([]byte, error)
- func DerivePQKeyFromSeed(seed []byte) (*mldsa65.PublicKey, *mldsa65.PrivateKey)
- func DerivePQSigningKey(mk []byte, generation uint32) (*mldsa65.PublicKey, *mldsa65.PrivateKey)
- func DerivePQSigningSeed(mk []byte, generation uint32) []byte
- func DeriveSessionKey(sharedSecret []byte, initiatorDID, responderDID string) ([]byte, error)
- func EnvelopeFilePath(langoDir string) string
- func GenerateMasterKey() ([]byte, error)
- func GenerateRecoveryMnemonic() (string, error)
- func HasEnvelopeFile(langoDir string) bool
- func HybridKEMScheme() kem.Scheme
- func IsTransient(err error) bool
- func KEMEncapsulate(peerPubKeyBytes []byte) (ct, ss []byte, err error)
- func RetryMigration(ctx context.Context, client *ent.Client, mk []byte, passphrase string, ...) error
- func RetryRekey(db *sql.DB, mk []byte) error
- func SignMLDSA65(sk *mldsa65.PrivateKey, message []byte) ([]byte, error)
- func StoreEnvelopeFile(langoDir string, env *MasterKeyEnvelope) error
- func UnwrapMasterKey(wrapped, nonce, kek []byte) ([]byte, error)
- func ValidateMnemonic(mnemonic string) error
- func VerifyEd25519(publicKey, message, signature []byte) error
- func VerifyMLDSA65(publicKey, message, signature []byte) error
- func VerifySecp256k1Keccak256(publicKey, message, signature []byte) error
- func WrapMasterKey(mk, kek []byte) (wrapped, nonce []byte, err error)
- func ZeroBytes(b []byte)
- type CompositeCryptoProvider
- func (c *CompositeCryptoProvider) Decrypt(ctx context.Context, keyID string, ciphertext []byte) ([]byte, error)
- func (c *CompositeCryptoProvider) Encrypt(ctx context.Context, keyID string, plaintext []byte) ([]byte, error)
- func (c *CompositeCryptoProvider) Sign(ctx context.Context, keyID string, payload []byte) ([]byte, error)
- func (c *CompositeCryptoProvider) UsedLocal() bool
- type ConnectionChecker
- type CryptoProvider
- type DecryptRequest
- type DecryptResponse
- type EncryptRequest
- type EncryptResponse
- type KDFParams
- type KEKSlot
- type KEKSlotType
- type KEMDecapsulator
- type KMSError
- type KMSHealthChecker
- type KMSProviderName
- type KeyInfo
- type KeyRegistry
- func (r *KeyRegistry) DeleteKey(ctx context.Context, name string) error
- func (r *KeyRegistry) GetDefaultKey(ctx context.Context) (*KeyInfo, error)
- func (r *KeyRegistry) GetKey(ctx context.Context, name string) (*KeyInfo, error)
- func (r *KeyRegistry) ListKeys(ctx context.Context) ([]*KeyInfo, error)
- func (r *KeyRegistry) RegisterKey(ctx context.Context, name, remoteKeyID string, keyType KeyType) (*KeyInfo, error)
- func (r *KeyRegistry) UpdateLastUsed(ctx context.Context, name string) error
- type KeyType
- type LocalCryptoProvider
- func (p *LocalCryptoProvider) CalculateChecksum(passphrase string, salt []byte) []byte
- func (p *LocalCryptoProvider) Close()
- func (p *LocalCryptoProvider) Decrypt(ctx context.Context, keyID string, ciphertext []byte) ([]byte, error)
- func (p *LocalCryptoProvider) Encrypt(ctx context.Context, keyID string, plaintext []byte) ([]byte, error)
- func (p *LocalCryptoProvider) Envelope() *MasterKeyEnvelope
- func (p *LocalCryptoProvider) Initialize(passphrase string) error
- func (p *LocalCryptoProvider) InitializeNewEnvelope(passphrase string) (*MasterKeyEnvelope, error)
- func (p *LocalCryptoProvider) InitializeWithEnvelope(mk []byte, envelope *MasterKeyEnvelope) error
- func (p *LocalCryptoProvider) InitializeWithSalt(passphrase string, salt []byte) error
- func (p *LocalCryptoProvider) IsInitialized() bool
- func (p *LocalCryptoProvider) IsLegacy() bool
- func (p *LocalCryptoProvider) MasterKey() []byte
- func (p *LocalCryptoProvider) Salt() []byte
- func (p *LocalCryptoProvider) Sign(ctx context.Context, keyID string, payload []byte) ([]byte, error)
- type MasterKeyEnvelope
- func (e *MasterKeyEnvelope) AddKMSSlot(ctx context.Context, label string, mk []byte, provider CryptoProvider, ...) error
- func (e *MasterKeyEnvelope) AddSlot(slotType KEKSlotType, label string, mk []byte, secret string, params KDFParams) error
- func (e *MasterKeyEnvelope) ChangePassphraseSlot(mk []byte, newPassphrase string) error
- func (e *MasterKeyEnvelope) HasSlotType(slotType KEKSlotType) bool
- func (e *MasterKeyEnvelope) RemoveSlot(id string) error
- func (e *MasterKeyEnvelope) SlotCount() int
- func (e *MasterKeyEnvelope) UnwrapFromKMS(ctx context.Context, provider CryptoProvider, providerName string, ...) ([]byte, string, error)
- func (e *MasterKeyEnvelope) UnwrapFromMnemonic(mnemonic string) ([]byte, string, error)
- func (e *MasterKeyEnvelope) UnwrapFromPassphrase(passphrase string) ([]byte, string, error)
- type RPCProvider
- func (s *RPCProvider) Decrypt(ctx context.Context, keyID string, ciphertext []byte) ([]byte, error)
- func (s *RPCProvider) Encrypt(ctx context.Context, keyID string, plaintext []byte) ([]byte, error)
- func (s *RPCProvider) HandleDecryptResponse(resp DecryptResponse) error
- func (s *RPCProvider) HandleEncryptResponse(resp EncryptResponse) error
- func (s *RPCProvider) HandleSignResponse(resp SignResponse) error
- func (s *RPCProvider) SetSender(sender types.RPCSenderFunc)
- func (s *RPCProvider) Sign(ctx context.Context, keyID string, payload []byte) ([]byte, error)
- type RefStore
- func (r *RefStore) Clear()
- func (r *RefStore) Names() map[string]string
- func (r *RefStore) Resolve(token string) ([]byte, bool)
- func (r *RefStore) ResolveAll(input string) string
- func (r *RefStore) Store(name string, value []byte) string
- func (r *RefStore) StoreDecrypted(id string, value []byte) string
- func (r *RefStore) Values() [][]byte
- type SecretInfo
- type SecretsStore
- func (s *SecretsStore) Delete(ctx context.Context, name string) error
- func (s *SecretsStore) Get(ctx context.Context, name string) ([]byte, error)
- func (s *SecretsStore) List(ctx context.Context) ([]*SecretInfo, error)
- func (s *SecretsStore) Store(ctx context.Context, name string, value []byte) error
- type SecurityConfigStore
- func (s *SecurityConfigStore) EnsureTable() error
- func (s *SecurityConfigStore) IsFirstRun() (bool, error)
- func (s *SecurityConfigStore) LoadChecksum() ([]byte, error)
- func (s *SecurityConfigStore) LoadChecksumNamed(name string) ([]byte, error)
- func (s *SecurityConfigStore) LoadSalt() ([]byte, error)
- func (s *SecurityConfigStore) LoadSaltNamed(name string) ([]byte, error)
- func (s *SecurityConfigStore) StoreChecksum(checksum []byte) error
- func (s *SecurityConfigStore) StoreChecksumNamed(name string, checksum []byte) (int64, error)
- func (s *SecurityConfigStore) StoreSalt(salt []byte) error
- func (s *SecurityConfigStore) StoreSaltNamed(name string, salt []byte) error
- type SignRequest
- type SignResponse
- type SignatureScheme
Constants ¶
const ( KDFAlgPBKDF2SHA256 = "pbkdf2-sha256" KDFAlgNone = "none" WrapAlgAES256GCM = "aes-256-gcm" WrapAlgKMSEnvelope = "kms-envelope" )
KDF and wrap algorithm identifiers.
const ( // KeySize is the size of AES-256 key in bytes. KeySize = 32 // NonceSize is the size of GCM nonce in bytes. NonceSize = 12 // SaltSize is the size of PBKDF2 salt in bytes. SaltSize = 16 // Iterations is the PBKDF2 iteration count. Iterations = 100000 )
const ( AlgorithmSecp256k1Keccak256 = "secp256k1-keccak256" AlgorithmEd25519 = "ed25519" AlgorithmMLDSA65 = "ml-dsa-65" )
Algorithm identifier constants. These are the canonical source of truth for algorithm identifiers across the codebase.
const AlgorithmX25519MLKEM768 = "X25519-MLKEM768"
AlgorithmX25519MLKEM768 is the algorithm identifier for the hybrid X25519 + ML-KEM-768 key encapsulation mechanism (FIPS 203, NIST Level 3).
const EnvelopeVersion = 1
EnvelopeVersion is the current MasterKeyEnvelope format version.
HybridSharedSecretSize is the expected shared secret size from X25519-MLKEM768 (32 bytes ML-KEM-768 + 32 bytes X25519).
const PQSeedSize = mldsa65.SeedSize
PQSeedSize is the seed size for ML-DSA-65 key derivation (32 bytes).
const RecoveryMnemonicBits = 256
RecoveryMnemonicBits is the BIP39 entropy size that yields a 24-word mnemonic.
const SecurityConfigDefault = "default"
SecurityConfigDefault is the canonical row name for the primary salt/checksum pair used by bootstrap. Other callers may store additional rows under different names (e.g. per-context salts in session store tests).
Variables ¶
var ( ErrKeyNotFound = errors.New("key not found") ErrNoEncryptionKeys = errors.New("no encryption keys available") ErrDecryptionFailed = errors.New("decryption failed") // Envelope errors ErrInvalidSlot = errors.New("invalid KEK slot") ErrLastSlot = errors.New("cannot remove last KEK slot") ErrUnwrapFailed = errors.New("master key unwrap failed") ErrEnvelopeCorrupt = errors.New("master key envelope corrupted") ErrNoEnvelopeFile = errors.New("envelope file not found") // KMS errors ErrKMSAccessDenied = errors.New("KMS access denied") ErrKMSKeyDisabled = errors.New("KMS key is disabled") ErrKMSThrottled = errors.New("KMS request throttled") ErrKMSInvalidKey = errors.New("KMS invalid key") ErrPKCS11Module = errors.New("PKCS#11 module error") ErrPKCS11Session = errors.New("PKCS#11 session error") )
var Ed25519Scheme = &SignatureScheme{ ID: AlgorithmEd25519, Verify: VerifyEd25519, SignatureSize: ed25519.SignatureSize, PublicKeySize: ed25519.PublicKeySize, }
Ed25519Scheme describes the Ed25519 signature algorithm. Ed25519 is registered as a framework verification algorithm in Phase 2; it is not wired into production identity flows until Phase 3 (DID v2).
var MLDSA65Scheme = &SignatureScheme{ ID: AlgorithmMLDSA65, Verify: VerifyMLDSA65, SignatureSize: mldsa65.SignatureSize, PublicKeySize: mldsa65.PublicKeySize, }
MLDSA65Scheme describes the ML-DSA-65 (FIPS 204) post-quantum signature algorithm.
var Secp256k1Keccak256Scheme = &SignatureScheme{ ID: AlgorithmSecp256k1Keccak256, Verify: VerifySecp256k1Keccak256, SignatureSize: 65, PublicKeySize: 33, }
Secp256k1Keccak256Scheme describes the secp256k1+keccak256 signature algorithm.
Functions ¶
func DeriveDBKey ¶ added in v0.7.0
DeriveDBKey derives the SQLCipher database encryption key from the Master Key. Uses HKDF-SHA256 with a domain-separated info label. Returns 32 raw bytes.
func DeriveDBKeyHex ¶ added in v0.7.0
DeriveDBKeyHex returns DeriveDBKey(mk) hex-encoded for use with SQLCipher `PRAGMA key = "x'<hex>'"` (raw key mode).
func DeriveIdentityKey ¶ added in v0.7.0
func DeriveIdentityKey(mk []byte, generation uint32) ed25519.PrivateKey
DeriveIdentityKey derives an Ed25519 identity key from the Master Key using HKDF-SHA256 with a domain-separated info label. The generation parameter allows key rotation: generation 0 is the default, higher values produce different keys from the same MK.
Same MK + same generation always produces the same Ed25519 key (deterministic). MK recovery (via mnemonic) recovers the identity key.
func DeriveKEK ¶ added in v0.7.0
DeriveKEK derives a Key Encryption Key from a secret using the slot's KDF parameters. The caller is responsible for providing the correct secret (passphrase or mnemonic).
func DerivePQKeyFromSeed ¶ added in v0.7.0
func DerivePQKeyFromSeed(seed []byte) (*mldsa65.PublicKey, *mldsa65.PrivateKey)
DerivePQKeyFromSeed creates an ML-DSA-65 keypair from a 32-byte seed.
func DerivePQSigningKey ¶ added in v0.7.0
DerivePQSigningKey derives an ML-DSA-65 keypair from the Master Key using HKDF-SHA256. The generation parameter allows key rotation from the same MK.
Domain separation: the info label "lango-pq-signing-mldsa65[:generation]" is independent from the Ed25519 identity key domain "lango-identity-ed25519".
func DerivePQSigningSeed ¶ added in v0.7.0
DerivePQSigningSeed derives the 32-byte ML-DSA-65 seed from the Master Key using HKDF-SHA256. The caller should pass this to DerivePQKeyFromSeed to get the full keypair. Separated for bootstrap: store the seed, derive lazily.
func DeriveSessionKey ¶ added in v0.7.0
DeriveSessionKey derives a 32-byte AES-256 session key from the hybrid KEM shared secret using HKDF-SHA256.
Parameters:
- sharedSecret: 64 bytes from hybrid KEM (ML-KEM-768 SS || X25519 SS)
- initiatorDID: DID of the handshake initiator (from selected signer)
- responderDID: DID of the handshake responder (from selected signer)
The info parameter provides domain separation and binds the key to the specific peer pair. Both sides MUST use the same DID ordering (initiator first, responder second) to derive identical keys.
func EnvelopeFilePath ¶ added in v0.7.0
EnvelopeFilePath returns the absolute path of the envelope file for the given lango dir.
func GenerateMasterKey ¶ added in v0.7.0
GenerateMasterKey returns 32 cryptographically random bytes for use as a Master Key.
func GenerateRecoveryMnemonic ¶ added in v0.7.0
GenerateRecoveryMnemonic returns a fresh 24-word BIP39 mnemonic. The returned string contains all information needed to re-derive a KEK; callers MUST display it to the user and ensure it is recorded before persisting the associated envelope slot.
func HasEnvelopeFile ¶ added in v0.7.0
HasEnvelopeFile reports whether an envelope file exists under langoDir. I/O errors (other than not-exist) are reported as "exists=false" to keep the signature simple; callers that need to distinguish should use LoadEnvelopeFile.
func HybridKEMScheme ¶ added in v0.7.0
HybridKEMScheme returns the X25519-MLKEM768 hybrid KEM scheme from circl.
func IsTransient ¶
IsTransient reports whether err is a transient KMS error eligible for retry.
func KEMEncapsulate ¶ added in v0.7.0
KEMEncapsulate takes a peer's serialized KEM public key and returns (ciphertext, sharedSecret). The shared secret is 64 bytes for X25519-MLKEM768 (32 bytes ML-KEM-768 || 32 bytes X25519).
func RetryMigration ¶ added in v0.7.0
func RetryMigration(ctx context.Context, client *ent.Client, mk []byte, passphrase string, oldSalt []byte) error
RetryMigration re-runs the data re-encryption phase using an already-unwrapped MK. Used by bootstrap when a previous migration crashed after envelope write but before data re-encryption completed. The caller is responsible for clearing PendingMigration and persisting the envelope afterwards.
func RetryRekey ¶ added in v0.7.0
RetryRekey retries `PRAGMA rekey` using the MK-derived raw key. Used when the previous migration crashed between data re-encryption and rekey.
func SignMLDSA65 ¶ added in v0.7.0
func SignMLDSA65(sk *mldsa65.PrivateKey, message []byte) ([]byte, error)
SignMLDSA65 signs a message with an ML-DSA-65 private key. Uses deterministic signing (randomized=false) for reproducibility.
func StoreEnvelopeFile ¶ added in v0.7.0
func StoreEnvelopeFile(langoDir string, env *MasterKeyEnvelope) error
StoreEnvelopeFile writes the envelope to <langoDir>/envelope.json atomically. The file is created with 0600 permissions. Uses write-to-temp-and-rename to avoid leaving a partial file on crash.
func UnwrapMasterKey ¶ added in v0.7.0
UnwrapMasterKey verifies the GCM tag and recovers the Master Key. Wraps failures in ErrUnwrapFailed so callers can match with errors.Is.
func ValidateMnemonic ¶ added in v0.7.0
ValidateMnemonic returns nil if the mnemonic is valid BIP39. It does NOT verify that the mnemonic matches any envelope slot.
func VerifyEd25519 ¶ added in v0.7.0
VerifyEd25519 verifies an Ed25519 signature against a public key and message. Ed25519 handles hashing internally (SHA-512), so the message is passed as-is.
func VerifyMLDSA65 ¶ added in v0.7.0
VerifyMLDSA65 verifies an ML-DSA-65 signature against a public key and message.
func VerifySecp256k1Keccak256 ¶ added in v0.7.0
VerifySecp256k1Keccak256 verifies a secp256k1+keccak256 ECDSA signature. It hashes the message with Keccak256, recovers the public key from the 65-byte signature (R+S+V), and compares with the claimed compressed key.
func WrapMasterKey ¶ added in v0.7.0
WrapMasterKey wraps the Master Key with the given KEK using AES-256-GCM. Returns the ciphertext and nonce separately.
Types ¶
type CompositeCryptoProvider ¶
type CompositeCryptoProvider struct {
// contains filtered or unexported fields
}
CompositeCryptoProvider implements CryptoProvider with fallback logic. It tries the primary provider first (typically companion), then falls back to local.
func NewCompositeCryptoProvider ¶
func NewCompositeCryptoProvider(primary CryptoProvider, fallback CryptoProvider, checker ConnectionChecker) *CompositeCryptoProvider
NewCompositeCryptoProvider creates a new CompositeCryptoProvider.
func (*CompositeCryptoProvider) Decrypt ¶
func (c *CompositeCryptoProvider) Decrypt(ctx context.Context, keyID string, ciphertext []byte) ([]byte, error)
Decrypt implements CryptoProvider.
func (*CompositeCryptoProvider) Encrypt ¶
func (c *CompositeCryptoProvider) Encrypt(ctx context.Context, keyID string, plaintext []byte) ([]byte, error)
Encrypt implements CryptoProvider.
func (*CompositeCryptoProvider) Sign ¶
func (c *CompositeCryptoProvider) Sign(ctx context.Context, keyID string, payload []byte) ([]byte, error)
Sign implements CryptoProvider.
func (*CompositeCryptoProvider) UsedLocal ¶
func (c *CompositeCryptoProvider) UsedLocal() bool
UsedLocal returns true if the last operation used the local fallback.
type ConnectionChecker ¶
type ConnectionChecker interface {
IsConnected() bool
}
ConnectionChecker provides connection status checking.
type CryptoProvider ¶
type CryptoProvider interface {
// Sign generates a signature for the given payload using the specified key ID.
Sign(ctx context.Context, keyID string, payload []byte) ([]byte, error)
// Encrypt encrypts the given plaintext using the specified key ID.
Encrypt(ctx context.Context, keyID string, plaintext []byte) ([]byte, error)
// Decrypt decrypts the given ciphertext using the specified key ID.
Decrypt(ctx context.Context, keyID string, ciphertext []byte) ([]byte, error)
}
CryptoProvider defines the interface for cryptographic operations.
func NewKMSProvider ¶
func NewKMSProvider(providerName KMSProviderName, kmsConfig config.KMSConfig) (CryptoProvider, error)
NewKMSProvider creates a CryptoProvider for the named KMS backend. Supported providers: "aws-kms", "gcp-kms", "azure-kv", "pkcs11". Build tags control which providers are compiled in; uncompiled providers return a descriptive error.
type DecryptRequest ¶
type DecryptRequest struct {
ID string `json:"id"`
KeyID string `json:"keyId"`
Ciphertext []byte `json:"ciphertext"`
}
DecryptRequest represents the payload for decryption.
type DecryptResponse ¶
type DecryptResponse struct {
ID string `json:"id"`
Plaintext []byte `json:"plaintext"`
Error string `json:"error,omitempty"`
}
DecryptResponse represents the payload for decryption response.
type EncryptRequest ¶
type EncryptRequest struct {
ID string `json:"id"`
KeyID string `json:"keyId"`
Plaintext []byte `json:"plaintext"`
}
EncryptRequest represents the payload for encryption.
type EncryptResponse ¶
type EncryptResponse struct {
ID string `json:"id"`
Ciphertext []byte `json:"ciphertext"`
Error string `json:"error,omitempty"`
}
EncryptResponse represents the payload for encryption response.
type KDFParams ¶ added in v0.7.0
type KDFParams struct {
Iterations int `json:"iterations,omitempty"` // PBKDF2
Memory int `json:"memory,omitempty"` // Argon2id (KiB)
Time int `json:"time,omitempty"` // Argon2id
Threads int `json:"threads,omitempty"` // Argon2id
}
KDFParams carries algorithm-specific KDF parameters.
func NewDefaultKDFParams ¶ added in v0.7.0
func NewDefaultKDFParams() KDFParams
NewDefaultKDFParams returns PBKDF2 parameters matching the current security baseline.
type KEKSlot ¶ added in v0.7.0
type KEKSlot struct {
ID string `json:"id"`
Type KEKSlotType `json:"type"`
KDFAlg string `json:"kdf_alg"`
KDFParams KDFParams `json:"kdf_params"`
WrapAlg string `json:"wrap_alg"`
Domain string `json:"domain"`
Salt []byte `json:"salt"`
WrappedMK []byte `json:"wrapped_mk"`
Nonce []byte `json:"nonce"`
CreatedAt time.Time `json:"created_at"`
Label string `json:"label,omitempty"`
// KMS-specific fields (populated only for KEKSlotHardware slots).
KMSProvider string `json:"kms_provider,omitempty"` // "aws-kms", "gcp-kms", "azure-kv", "pkcs11"
KMSKeyID string `json:"kms_key_id,omitempty"` // KMS key identifier
}
KEKSlot is a single envelope slot that can independently unwrap the Master Key.
type KEKSlotType ¶ added in v0.7.0
type KEKSlotType string
KEKSlotType identifies the key-derivation source for a KEK slot.
const ( KEKSlotPassphrase KEKSlotType = "passphrase" KEKSlotMnemonic KEKSlotType = "mnemonic" KEKSlotRecoveryFile KEKSlotType = "recovery_file" // reserved for follow-up KEKSlotHardware KEKSlotType = "hardware" // reserved for follow-up )
type KEMDecapsulator ¶ added in v0.7.0
KEMDecapsulator recovers a shared secret from a KEM ciphertext. Returned by GenerateEphemeralKEM as a closure capturing the private key. The caller MUST NOT persist this function — it holds an ephemeral private key that should be discarded after a single handshake.
func GenerateEphemeralKEM ¶ added in v0.7.0
func GenerateEphemeralKEM() (pubKeyBytes []byte, decap KEMDecapsulator, err error)
GenerateEphemeralKEM generates an ephemeral KEM keypair and returns the serialized public key and a decapsulator closure. The private key is captured inside the closure and never leaves the security package.
The caller MUST NOT persist the decapsulator — it is ephemeral per-handshake. After the handshake completes, the closure (and its captured private key) becomes unreferenced and is garbage collected.
type KMSHealthChecker ¶
type KMSHealthChecker struct {
// contains filtered or unexported fields
}
KMSHealthChecker implements ConnectionChecker for KMS providers. It caches the connection status with a configurable probe interval.
func NewKMSHealthChecker ¶
func NewKMSHealthChecker(provider CryptoProvider, testKeyID string, probeInterval time.Duration) *KMSHealthChecker
NewKMSHealthChecker creates a health checker that probes the KMS provider by attempting a small encrypt/decrypt roundtrip on probeInterval.
func (*KMSHealthChecker) IsConnected ¶
func (h *KMSHealthChecker) IsConnected() bool
IsConnected implements ConnectionChecker. Returns the cached result if fresh, otherwise performs a synchronous probe.
type KMSProviderName ¶
type KMSProviderName string
KMSProviderName identifies a supported KMS backend.
const ( KMSProviderAWS KMSProviderName = "aws-kms" KMSProviderGCP KMSProviderName = "gcp-kms" KMSProviderAzure KMSProviderName = "azure-kv" KMSProviderPKCS11 KMSProviderName = "pkcs11" )
func (KMSProviderName) Valid ¶
func (n KMSProviderName) Valid() bool
Valid reports whether n is a recognised KMS provider name.
type KeyInfo ¶
type KeyInfo struct {
ID uuid.UUID
Name string
RemoteKeyID string
Type KeyType
CreatedAt time.Time
LastUsedAt *time.Time
}
KeyInfo represents key metadata.
type KeyRegistry ¶
type KeyRegistry struct {
// contains filtered or unexported fields
}
KeyRegistry manages encryption/signing keys.
func NewKeyRegistry ¶
func NewKeyRegistry(client *ent.Client) *KeyRegistry
NewKeyRegistry creates a new KeyRegistry.
func (*KeyRegistry) DeleteKey ¶
func (r *KeyRegistry) DeleteKey(ctx context.Context, name string) error
DeleteKey removes a key by name.
func (*KeyRegistry) GetDefaultKey ¶
func (r *KeyRegistry) GetDefaultKey(ctx context.Context) (*KeyInfo, error)
GetDefaultKey retrieves the default encryption key (most recently created).
func (*KeyRegistry) ListKeys ¶
func (r *KeyRegistry) ListKeys(ctx context.Context) ([]*KeyInfo, error)
ListKeys returns all registered keys.
func (*KeyRegistry) RegisterKey ¶
func (r *KeyRegistry) RegisterKey(ctx context.Context, name, remoteKeyID string, keyType KeyType) (*KeyInfo, error)
RegisterKey registers a new key.
func (*KeyRegistry) UpdateLastUsed ¶
func (r *KeyRegistry) UpdateLastUsed(ctx context.Context, name string) error
UpdateLastUsed updates the last used timestamp for a key.
type LocalCryptoProvider ¶
type LocalCryptoProvider struct {
// contains filtered or unexported fields
}
LocalCryptoProvider implements CryptoProvider using local AES-256-GCM encryption.
Two initialization modes are supported:
Envelope mode (preferred): the Master Key is unwrapped from a MasterKeyEnvelope slot and installed as keys["local"]. All data encryption uses the MK directly. This mode supports key rotation (change-passphrase), recovery mnemonics, and DB key derivation via DeriveDBKey.
Legacy mode: the key is derived directly from a passphrase via PBKDF2 and stored in keys["local"]. This mode is kept for backward compatibility and is replaced by envelope mode during migration.
In both modes, Encrypt/Decrypt/Sign use the same keys["local"] lookup, so consumers (SecretsStore, ConfigStore, tools) are unaware of the underlying source of the key.
func NewLocalCryptoProvider ¶
func NewLocalCryptoProvider() *LocalCryptoProvider
NewLocalCryptoProvider creates a new LocalCryptoProvider.
func (*LocalCryptoProvider) CalculateChecksum ¶
func (p *LocalCryptoProvider) CalculateChecksum(passphrase string, salt []byte) []byte
CalculateChecksum computes the checksum for a given passphrase and salt. Uses HMAC-SHA256 with salt as key to avoid length extension attacks. NOTE: Changing this algorithm requires migrating existing stored checksums.
func (*LocalCryptoProvider) Close ¶ added in v0.7.0
func (p *LocalCryptoProvider) Close()
Close zeroes all key material held by the provider. After Close, the provider is unusable and must not be referenced.
func (*LocalCryptoProvider) Decrypt ¶
func (p *LocalCryptoProvider) Decrypt(ctx context.Context, keyID string, ciphertext []byte) ([]byte, error)
Decrypt decrypts data using AES-256-GCM.
func (*LocalCryptoProvider) Encrypt ¶
func (p *LocalCryptoProvider) Encrypt(ctx context.Context, keyID string, plaintext []byte) ([]byte, error)
Encrypt encrypts data using AES-256-GCM.
func (*LocalCryptoProvider) Envelope ¶ added in v0.7.0
func (p *LocalCryptoProvider) Envelope() *MasterKeyEnvelope
Envelope returns the currently installed envelope, or nil in legacy mode. Callers MUST NOT mutate the returned envelope without acquiring exclusive access.
func (*LocalCryptoProvider) Initialize ¶
func (p *LocalCryptoProvider) Initialize(passphrase string) error
Initialize sets up the provider with a passphrase using the legacy direct-key model. The passphrase is used to derive an encryption key via PBKDF2. Marks the provider as legacy — envelope-based bootstrap should use InitializeNewEnvelope or InitializeWithEnvelope instead.
func (*LocalCryptoProvider) InitializeNewEnvelope ¶ added in v0.7.0
func (p *LocalCryptoProvider) InitializeNewEnvelope(passphrase string) (*MasterKeyEnvelope, error)
InitializeNewEnvelope generates a fresh envelope from a passphrase and installs the Master Key as the local key. Returns the envelope so the caller can persist it via StoreEnvelopeFile.
func (*LocalCryptoProvider) InitializeWithEnvelope ¶ added in v0.7.0
func (p *LocalCryptoProvider) InitializeWithEnvelope(mk []byte, envelope *MasterKeyEnvelope) error
InitializeWithEnvelope installs an already-unwrapped Master Key as the local key. The provider takes ownership of the MK bytes and zeroes them in Close(). The envelope reference is kept for diagnostics (SlotCount, recovery status).
func (*LocalCryptoProvider) InitializeWithSalt ¶
func (p *LocalCryptoProvider) InitializeWithSalt(passphrase string, salt []byte) error
InitializeWithSalt sets up the provider with existing salt (legacy direct-key model).
func (*LocalCryptoProvider) IsInitialized ¶
func (p *LocalCryptoProvider) IsInitialized() bool
IsInitialized returns true if the provider has been initialized.
func (*LocalCryptoProvider) IsLegacy ¶ added in v0.7.0
func (p *LocalCryptoProvider) IsLegacy() bool
IsLegacy reports whether the provider was initialized via the legacy direct-passphrase-derived-key path.
func (*LocalCryptoProvider) MasterKey ¶ added in v0.7.0
func (p *LocalCryptoProvider) MasterKey() []byte
MasterKey returns a copy of the unwrapped Master Key, or nil in legacy mode. Callers MUST ZeroBytes the returned slice when done.
func (*LocalCryptoProvider) Salt ¶
func (p *LocalCryptoProvider) Salt() []byte
Salt returns the current salt for persistence.
type MasterKeyEnvelope ¶ added in v0.7.0
type MasterKeyEnvelope struct {
Version int `json:"version"`
Slots []KEKSlot `json:"slots"`
PendingMigration bool `json:"pending_migration,omitempty"`
PendingRekey bool `json:"pending_rekey,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
MasterKeyEnvelope holds the wrapped Master Key across one or more KEK slots. Persisted as JSON at <LangoDir>/envelope.json with 0600 permissions.
func LoadEnvelopeFile ¶ added in v0.7.0
func LoadEnvelopeFile(langoDir string) (*MasterKeyEnvelope, error)
LoadEnvelopeFile reads and parses <langoDir>/envelope.json. Returns (nil, nil) if the file does not exist (fresh install or legacy layout). Returns a wrapped ErrEnvelopeCorrupt on parse failure.
func MigrateToEnvelope ¶ added in v0.7.0
func MigrateToEnvelope( ctx context.Context, db *sql.DB, client *ent.Client, langoDir string, passphrase string, oldSalt, oldChecksum []byte, dbEncrypted bool, ) (*MasterKeyEnvelope, []byte, error)
MigrateToEnvelope performs a one-time legacy → envelope migration.
Flow (SQLCipher):
- Derive legacy key via PBKDF2(passphrase, oldSalt, …) and verify checksum.
- Generate a fresh MK and build an envelope with PendingMigration=true (and PendingRekey=true if the DB is SQLCipher-encrypted). Persist the envelope file BEFORE touching any data so a crash leaves a discoverable marker.
- PRAGMA wal_checkpoint(TRUNCATE) + VACUUM INTO <dbpath>.pre-migration for a WAL-safe backup.
- Re-encrypt every secret and config profile row inside a single ent transaction. Count-verify before/after.
- Clear PendingMigration and persist the envelope.
- PRAGMA rekey to MK-derived raw key (SQLCipher only).
- Clear PendingRekey and persist the envelope.
Returns (envelope, mk, error). The caller takes ownership of the raw MK and must ZeroBytes it when done.
func NewEnvelope ¶ added in v0.7.0
func NewEnvelope(passphrase string) (*MasterKeyEnvelope, []byte, error)
NewEnvelope generates a fresh Master Key, wraps it with a passphrase-derived KEK, and returns a new envelope alongside the raw MK. Callers MUST zero the returned MK with ZeroBytes when done.
func (*MasterKeyEnvelope) AddKMSSlot ¶ added in v0.7.0
func (e *MasterKeyEnvelope) AddKMSSlot( ctx context.Context, label string, mk []byte, provider CryptoProvider, kmsProviderName string, kmsKeyID string, ) error
AddKMSSlot wraps the MK using the CryptoProvider's Encrypt and stores the ciphertext as a KMS KEK slot. The KMS provider handles all wrapping internally (nonces, key material), so no local KDF or GCM nonce is needed.
func (*MasterKeyEnvelope) AddSlot ¶ added in v0.7.0
func (e *MasterKeyEnvelope) AddSlot(slotType KEKSlotType, label string, mk []byte, secret string, params KDFParams) error
AddSlot adds a new KEK slot that wraps the provided MK. The caller must hold the unwrapped MK in memory. The MK is not modified.
func (*MasterKeyEnvelope) ChangePassphraseSlot ¶ added in v0.7.0
func (e *MasterKeyEnvelope) ChangePassphraseSlot(mk []byte, newPassphrase string) error
ChangePassphraseSlot replaces the first passphrase slot (or adds one if missing) with a new KEK derived from newPassphrase. The MK is unchanged and all non-passphrase slots remain intact.
func (*MasterKeyEnvelope) HasSlotType ¶ added in v0.7.0
func (e *MasterKeyEnvelope) HasSlotType(slotType KEKSlotType) bool
HasSlotType reports whether the envelope contains at least one slot of the given type.
func (*MasterKeyEnvelope) RemoveSlot ¶ added in v0.7.0
func (e *MasterKeyEnvelope) RemoveSlot(id string) error
RemoveSlot removes the slot with the given ID. Returns ErrLastSlot if removal would leave zero slots.
func (*MasterKeyEnvelope) SlotCount ¶ added in v0.7.0
func (e *MasterKeyEnvelope) SlotCount() int
SlotCount returns the number of KEK slots.
func (*MasterKeyEnvelope) UnwrapFromKMS ¶ added in v0.7.0
func (e *MasterKeyEnvelope) UnwrapFromKMS( ctx context.Context, provider CryptoProvider, providerName string, keyID string, ) ([]byte, string, error)
UnwrapFromKMS attempts to unwrap the MK using a KMS provider with 2-tier matching.
Tier 1: exact match — slots where KMSProvider == providerName AND KMSKeyID == keyID. Tier 2: provider-only — slots where KMSProvider == providerName (any keyID).
The provider is a bare KMS CryptoProvider. CompositeCryptoProvider with local fallback MUST NOT be used — if KMS fails, the caller should fall through to the next credential path (mnemonic/passphrase), not attempt local decryption.
Returns (mk, slotID, nil) on success. Callers MUST ZeroBytes the returned MK when done.
func (*MasterKeyEnvelope) UnwrapFromMnemonic ¶ added in v0.7.0
func (e *MasterKeyEnvelope) UnwrapFromMnemonic(mnemonic string) ([]byte, string, error)
UnwrapFromMnemonic attempts each mnemonic slot until one unwraps the MK.
func (*MasterKeyEnvelope) UnwrapFromPassphrase ¶ added in v0.7.0
func (e *MasterKeyEnvelope) UnwrapFromPassphrase(passphrase string) ([]byte, string, error)
UnwrapFromPassphrase attempts each passphrase slot in order until one unwraps the MK. Returns (mk, slotID, nil) on success or (nil, "", ErrUnwrapFailed). Callers MUST ZeroBytes the returned MK when done.
type RPCProvider ¶
type RPCProvider struct {
// contains filtered or unexported fields
}
RPCProvider implements CryptoProvider using an asynchronous RPC mechanism.
func (*RPCProvider) HandleDecryptResponse ¶
func (s *RPCProvider) HandleDecryptResponse(resp DecryptResponse) error
HandleDecryptResponse processes an incoming decrypt response.
func (*RPCProvider) HandleEncryptResponse ¶
func (s *RPCProvider) HandleEncryptResponse(resp EncryptResponse) error
HandleEncryptResponse processes an incoming encrypt response.
func (*RPCProvider) HandleSignResponse ¶
func (s *RPCProvider) HandleSignResponse(resp SignResponse) error
HandleSignResponse processes an incoming sign response.
func (*RPCProvider) SetSender ¶
func (s *RPCProvider) SetSender(sender types.RPCSenderFunc)
SetSender configures the function used to send requests.
type RefStore ¶
type RefStore struct {
// contains filtered or unexported fields
}
RefStore manages mapping between opaque reference tokens and secret plaintext values. It prevents AI agents from seeing actual secret values by substituting them with safe reference tokens.
func (*RefStore) Names ¶
Names returns a mapping of plaintext value (as string) to its reference name. This is used by the scanner to mask secrets in output, replacing them with tokens like [SECRET:name].
func (*RefStore) Resolve ¶
Resolve resolves a single reference token to its plaintext value. Returns the plaintext and true if found, or nil and false otherwise.
func (*RefStore) ResolveAll ¶
ResolveAll replaces all {{secret:...}} and {{decrypt:...}} tokens in the input string with their actual plaintext values.
func (*RefStore) Store ¶
Store stores a secret value and returns its reference token in the format {{secret:name}}.
func (*RefStore) StoreDecrypted ¶
StoreDecrypted stores a decrypted value and returns its reference token in the format {{decrypt:id}}.
type SecretInfo ¶
type SecretInfo struct {
ID uuid.UUID
Name string
KeyID uuid.UUID
KeyName string
CreatedAt time.Time
UpdatedAt time.Time
AccessCount int
}
SecretInfo represents secret metadata (without the actual value).
type SecretsStore ¶
type SecretsStore struct {
// contains filtered or unexported fields
}
SecretsStore manages encrypted secrets.
func NewSecretsStore ¶
func NewSecretsStore(client *ent.Client, registry *KeyRegistry, crypto CryptoProvider) *SecretsStore
NewSecretsStore creates a new SecretsStore.
func (*SecretsStore) Delete ¶
func (s *SecretsStore) Delete(ctx context.Context, name string) error
Delete removes a secret by name.
func (*SecretsStore) List ¶
func (s *SecretsStore) List(ctx context.Context) ([]*SecretInfo, error)
List returns metadata for all secrets.
type SecurityConfigStore ¶ added in v0.7.0
type SecurityConfigStore struct {
// contains filtered or unexported fields
}
SecurityConfigStore is the single access point for the raw `security_config` table. It replaces the scattered `ensureSecurityTable`/`storeSalt`/`storeChecksum` helpers that previously lived in both `bootstrap` and `session/ent_store`.
Two calling styles are supported:
- Default row (bootstrap): LoadSalt / StoreSalt / LoadChecksum / StoreChecksum operate on the row named SecurityConfigDefault.
- Arbitrary name (session test suite, future multi-context): the same operations are exposed with a `Named` suffix accepting a row name.
func NewSecurityConfigStore ¶ added in v0.7.0
func NewSecurityConfigStore(db *sql.DB) *SecurityConfigStore
NewSecurityConfigStore wraps the given *sql.DB. EnsureTable must be called before any read/write methods.
func (*SecurityConfigStore) EnsureTable ¶ added in v0.7.0
func (s *SecurityConfigStore) EnsureTable() error
EnsureTable creates the security_config table if it does not exist and adds the checksum column on older installations that lacked it.
func (*SecurityConfigStore) IsFirstRun ¶ added in v0.7.0
func (s *SecurityConfigStore) IsFirstRun() (bool, error)
IsFirstRun reports whether the default row is missing (no salt stored yet).
func (*SecurityConfigStore) LoadChecksum ¶ added in v0.7.0
func (s *SecurityConfigStore) LoadChecksum() ([]byte, error)
LoadChecksum returns the stored HMAC-SHA256 checksum for the default row.
func (*SecurityConfigStore) LoadChecksumNamed ¶ added in v0.7.0
func (s *SecurityConfigStore) LoadChecksumNamed(name string) ([]byte, error)
LoadChecksumNamed returns the stored HMAC-SHA256 checksum for the named row, or nil.
func (*SecurityConfigStore) LoadSalt ¶ added in v0.7.0
func (s *SecurityConfigStore) LoadSalt() ([]byte, error)
LoadSalt returns the stored salt for the default row, or nil if absent.
func (*SecurityConfigStore) LoadSaltNamed ¶ added in v0.7.0
func (s *SecurityConfigStore) LoadSaltNamed(name string) ([]byte, error)
LoadSaltNamed returns the stored salt for the named row, or nil if absent.
func (*SecurityConfigStore) StoreChecksum ¶ added in v0.7.0
func (s *SecurityConfigStore) StoreChecksum(checksum []byte) error
StoreChecksum updates the default row's checksum column. The row must already exist.
func (*SecurityConfigStore) StoreChecksumNamed ¶ added in v0.7.0
func (s *SecurityConfigStore) StoreChecksumNamed(name string, checksum []byte) (int64, error)
StoreChecksumNamed updates the named row's checksum column. The row must already exist (StoreSaltNamed before StoreChecksumNamed). Returns the number of rows affected so callers can detect missing-row cases.
func (*SecurityConfigStore) StoreSalt ¶ added in v0.7.0
func (s *SecurityConfigStore) StoreSalt(salt []byte) error
StoreSalt upserts the default row's salt value.
func (*SecurityConfigStore) StoreSaltNamed ¶ added in v0.7.0
func (s *SecurityConfigStore) StoreSaltNamed(name string, salt []byte) error
StoreSaltNamed upserts the named row's salt value.
type SignRequest ¶
type SignRequest struct {
ID string `json:"id"`
KeyID string `json:"keyId"`
Payload []byte `json:"payload"`
}
SignRequest represents the payload sent to the signer provider.
type SignResponse ¶
type SignResponse struct {
ID string `json:"id"`
Signature []byte `json:"signature"`
Error string `json:"error,omitempty"`
}
SignResponse represents the payload received from the signer provider.
type SignatureScheme ¶ added in v0.7.0
type SignatureScheme struct {
// ID is the algorithm identifier string.
ID string
// Verify checks a signature against a public key and message.
// The implementation handles any algorithm-specific hashing internally.
Verify func(publicKey, message, signature []byte) error
// SignatureSize is the expected byte length of signatures (e.g., 65 for secp256k1, 64 for Ed25519).
SignatureSize int
// PublicKeySize is the expected byte length of public keys (e.g., 33 for compressed secp256k1, 32 for Ed25519).
PublicKeySize int
}
SignatureScheme is a canonical algorithm descriptor with verification function and metadata. This type does NOT act as a registry — actual dispatch is handled by injected verifier maps in each consumer (handshake, provenance). SignatureScheme provides a single source of truth for algorithm metadata (key/signature sizes) and verification logic.
Source Files
¶
- aws_kms_provider_stub.go
- azure_kv_provider_stub.go
- composite_provider.go
- config_store.go
- crypto.go
- derive_identity.go
- derive_pq.go
- derive_session.go
- envelope.go
- envelope_file.go
- errors.go
- gcp_kms_provider_stub.go
- kem.go
- key_registry.go
- kms_checker.go
- kms_factory.go
- kms_retry.go
- local_provider.go
- migrate_envelope.go
- mnemonic.go
- pkcs11_provider_stub.go
- rpc_provider.go
- scheme_ed25519.go
- scheme_mldsa65.go
- scheme_secp256k1.go
- secret_ref.go
- secrets_store.go
- signature_scheme.go