refactor: Implement proper separation between unlock keys and secret decryption - Remove DecryptSecret methods from all unlock key implementations - Secrets now handle their own decryption via Secret.GetValue(unlockKey) - Unlock keys are only responsible for vault access (getting long-term key) - Add decryptWithLongTermKey helper for per-secret key architecture - Fix vault import to work in non-interactive mode without unlock keys - Maintain clean architecture: unlock keys → vault access → secret decryption - All tests passing with new architecture

This commit is contained in:
Jeffrey Paul 2025-05-29 10:06:30 -07:00
parent 4b59d6fb82
commit 345709a306
6 changed files with 146 additions and 363 deletions

View File

@ -1480,6 +1480,11 @@ func (cli *CLIInstance) importMnemonic(vaultName, mnemonic string) error {
vault := NewVault(cli.fs, vaultName, cli.stateDir) vault := NewVault(cli.fs, vaultName, cli.stateDir)
vault.Unlock(ltIdentity) vault.Unlock(ltIdentity)
fmt.Printf("Successfully imported mnemonic into vault '%s'\n", vaultName)
fmt.Printf("Long-term public key: %s\n", ltPubKey)
// Try to create unlock key only if running interactively
if term.IsTerminal(int(syscall.Stderr)) {
// Get or create passphrase for unlock key // Get or create passphrase for unlock key
var passphraseStr string var passphraseStr string
if envPassphrase := os.Getenv(EnvUnlockPassphrase); envPassphrase != "" { if envPassphrase := os.Getenv(EnvUnlockPassphrase); envPassphrase != "" {
@ -1488,19 +1493,25 @@ func (cli *CLIInstance) importMnemonic(vaultName, mnemonic string) error {
// Use secure passphrase input with confirmation // Use secure passphrase input with confirmation
passphraseStr, err = readSecurePassphrase("Enter passphrase for unlock key: ") passphraseStr, err = readSecurePassphrase("Enter passphrase for unlock key: ")
if err != nil { if err != nil {
return fmt.Errorf("failed to read passphrase: %w", err) fmt.Printf("Warning: Failed to create unlock key: %v\n", err)
fmt.Printf("You can create unlock keys later with 'secret keys add passphrase'\n")
return nil
} }
} }
// Create passphrase-protected unlock key (vault is now unlocked) // Create passphrase-protected unlock key (vault is now unlocked)
passphraseKey, err := vault.CreatePassphraseKey(passphraseStr) passphraseKey, err := vault.CreatePassphraseKey(passphraseStr)
if err != nil { if err != nil {
return fmt.Errorf("failed to create unlock key: %w", err) fmt.Printf("Warning: Failed to create unlock key: %v\n", err)
fmt.Printf("You can create unlock keys later with 'secret keys add passphrase'\n")
return nil
} }
fmt.Printf("Successfully imported mnemonic into vault '%s'\n", vaultName)
fmt.Printf("Long-term public key: %s\n", ltPubKey)
fmt.Printf("Unlock key ID: %s\n", passphraseKey.GetMetadata().ID) fmt.Printf("Unlock key ID: %s\n", passphraseKey.GetMetadata().ID)
} else {
fmt.Printf("Running in non-interactive mode - unlock key not created\n")
fmt.Printf("You can create unlock keys later with 'secret keys add passphrase'\n")
}
return nil return nil
} }

View File

@ -176,104 +176,6 @@ func (k *KeychainUnlockKey) Remove() error {
return nil return nil
} }
// DecryptSecret decrypts a secret using this keychain unlock key's long-term key management
func (k *KeychainUnlockKey) DecryptSecret(secret *Secret) ([]byte, error) {
DebugWith("Decrypting secret with keychain unlock key",
slog.String("secret_name", secret.Name),
slog.String("key_id", k.GetID()),
slog.String("key_type", k.GetType()),
)
// Let the secret read its own encrypted data
encryptedData, err := secret.GetEncryptedData()
if err != nil {
Debug("Failed to get encrypted secret data for keychain decryption", "error", err, "secret_name", secret.Name)
return nil, fmt.Errorf("failed to get encrypted secret data: %w", err)
}
DebugWith("Retrieved encrypted secret data for keychain decryption",
slog.String("secret_name", secret.Name),
slog.String("key_id", k.GetID()),
slog.Int("encrypted_length", len(encryptedData)),
)
// Get or derive the long-term private key
var ltPrivKeyData []byte
// Check if mnemonic is available in environment variable
if envMnemonic := os.Getenv(EnvMnemonic); envMnemonic != "" {
// Use mnemonic directly to derive long-term key
ltIdentity, err := agehd.DeriveIdentity(envMnemonic, 0)
if err != nil {
return nil, fmt.Errorf("failed to derive long-term key from mnemonic: %w", err)
}
ltPrivKeyData = []byte(ltIdentity.String())
} else {
// Get keychain item name and retrieve data
keychainItemName, err := k.GetKeychainItemName()
if err != nil {
return nil, fmt.Errorf("failed to get keychain item name: %w", err)
}
keychainDataBytes, err := retrieveFromKeychain(keychainItemName)
if err != nil {
return nil, fmt.Errorf("failed to retrieve data from keychain: %w", err)
}
var keychainData KeychainData
if err := json.Unmarshal(keychainDataBytes, &keychainData); err != nil {
return nil, fmt.Errorf("failed to parse keychain data: %w", err)
}
// Decrypt the long-term private key using the encrypted data from keychain
encryptedLtPrivKey, err := hex.DecodeString(keychainData.EncryptedLongtermKey)
if err != nil {
return nil, fmt.Errorf("failed to decode encrypted long-term key: %w", err)
}
// Get our unlock key identity to decrypt the long-term key
unlockIdentity, err := k.GetIdentity()
if err != nil {
return nil, fmt.Errorf("failed to get unlock identity: %w", err)
}
// Decrypt long-term private key using our unlock key
ltPrivKeyData, err = decryptWithIdentity(encryptedLtPrivKey, unlockIdentity)
if err != nil {
return nil, fmt.Errorf("failed to decrypt long-term private key: %w", err)
}
}
// Parse long-term private key
Debug("Parsing long-term private key", "key_id", k.GetID())
ltIdentity, err := age.ParseX25519Identity(string(ltPrivKeyData))
if err != nil {
Debug("Failed to parse long-term private key", "error", err, "key_id", k.GetID())
return nil, fmt.Errorf("failed to parse long-term private key: %w", err)
}
DebugWith("Successfully parsed long-term identity",
slog.String("key_id", k.GetID()),
slog.String("public_key", ltIdentity.Recipient().String()),
)
// Decrypt secret data using long-term key
Debug("Decrypting secret data with long-term key", "secret_name", secret.Name, "key_id", k.GetID())
decryptedData, err := decryptWithIdentity(encryptedData, ltIdentity)
if err != nil {
Debug("Failed to decrypt secret with long-term key", "error", err, "secret_name", secret.Name, "key_id", k.GetID())
return nil, fmt.Errorf("failed to decrypt secret: %w", err)
}
DebugWith("Successfully decrypted secret with keychain unlock key",
slog.String("secret_name", secret.Name),
slog.String("key_id", k.GetID()),
slog.Int("decrypted_length", len(decryptedData)),
)
return decryptedData, nil
}
// NewKeychainUnlockKey creates a new KeychainUnlockKey instance // NewKeychainUnlockKey creates a new KeychainUnlockKey instance
func NewKeychainUnlockKey(fs afero.Fs, directory string, metadata UnlockKeyMetadata) *KeychainUnlockKey { func NewKeychainUnlockKey(fs afero.Fs, directory string, metadata UnlockKeyMetadata) *KeychainUnlockKey {
return &KeychainUnlockKey{ return &KeychainUnlockKey{

View File

@ -140,90 +140,3 @@ func CreatePassphraseKey(fs afero.Fs, stateDir string, passphrase string) (*Pass
return currentVault.CreatePassphraseKey(passphrase) return currentVault.CreatePassphraseKey(passphrase)
} }
// DecryptSecret decrypts a secret using this passphrase unlock key's long-term key management
func (p *PassphraseUnlockKey) DecryptSecret(secret *Secret) ([]byte, error) {
DebugWith("Decrypting secret with passphrase unlock key",
slog.String("secret_name", secret.Name),
slog.String("key_id", p.GetID()),
slog.String("key_type", p.GetType()),
)
// Get our unlock key encrypted data
encryptedData, err := secret.GetEncryptedData()
if err != nil {
Debug("Failed to get encrypted secret data for passphrase decryption", "error", err, "secret_name", secret.Name)
return nil, fmt.Errorf("failed to get encrypted secret data: %w", err)
}
DebugWith("Retrieved encrypted secret data for passphrase decryption",
slog.String("secret_name", secret.Name),
slog.String("key_id", p.GetID()),
slog.Int("encrypted_length", len(encryptedData)),
)
// Get our age identity
Debug("Getting passphrase unlock key identity for secret decryption", "key_id", p.GetID())
unlockIdentity, err := p.GetIdentity()
if err != nil {
Debug("Failed to get passphrase unlock identity", "error", err, "key_id", p.GetID())
return nil, fmt.Errorf("failed to get unlock identity: %w", err)
}
// Read encrypted long-term private key
encryptedLtPrivKeyPath := filepath.Join(p.Directory, "longterm.age")
Debug("Reading encrypted long-term private key", "path", encryptedLtPrivKeyPath)
encryptedLtPrivKey, err := afero.ReadFile(p.fs, encryptedLtPrivKeyPath)
if err != nil {
Debug("Failed to read encrypted long-term private key", "error", err, "path", encryptedLtPrivKeyPath)
return nil, fmt.Errorf("failed to read encrypted long-term private key: %w", err)
}
DebugWith("Read encrypted long-term private key",
slog.String("key_id", p.GetID()),
slog.Int("encrypted_length", len(encryptedLtPrivKey)),
)
// Decrypt long-term private key using our unlock key
Debug("Decrypting long-term private key with passphrase unlock key", "key_id", p.GetID())
ltPrivKeyData, err := decryptWithIdentity(encryptedLtPrivKey, unlockIdentity)
if err != nil {
Debug("Failed to decrypt long-term private key", "error", err, "key_id", p.GetID())
return nil, fmt.Errorf("failed to decrypt long-term private key: %w", err)
}
DebugWith("Successfully decrypted long-term private key",
slog.String("key_id", p.GetID()),
slog.Int("decrypted_length", len(ltPrivKeyData)),
)
// Parse long-term private key
Debug("Parsing long-term private key", "key_id", p.GetID())
ltIdentity, err := age.ParseX25519Identity(string(ltPrivKeyData))
if err != nil {
Debug("Failed to parse long-term private key", "error", err, "key_id", p.GetID())
return nil, fmt.Errorf("failed to parse long-term private key: %w", err)
}
DebugWith("Successfully parsed long-term identity",
slog.String("key_id", p.GetID()),
slog.String("public_key", ltIdentity.Recipient().String()),
)
// Decrypt secret data using long-term key
Debug("Decrypting secret data with long-term key", "secret_name", secret.Name, "key_id", p.GetID())
decryptedData, err := decryptWithIdentity(encryptedData, ltIdentity)
if err != nil {
Debug("Failed to decrypt secret with long-term key", "error", err, "secret_name", secret.Name, "key_id", p.GetID())
return nil, fmt.Errorf("failed to decrypt secret: %w", err)
}
DebugWith("Successfully decrypted secret with passphrase unlock key",
slog.String("secret_name", secret.Name),
slog.String("key_id", p.GetID()),
slog.Int("decrypted_length", len(decryptedData)),
)
return decryptedData, nil
}

View File

@ -124,124 +124,6 @@ func (p *PGPUnlockKey) Remove() error {
return nil return nil
} }
// DecryptSecret decrypts a secret using this PGP unlock key's long-term key management
func (p *PGPUnlockKey) DecryptSecret(secret *Secret) ([]byte, error) {
DebugWith("Decrypting secret with PGP unlock key",
slog.String("secret_name", secret.Name),
slog.String("key_id", p.GetID()),
slog.String("key_type", p.GetType()),
)
// Let the secret read its own encrypted data
encryptedData, err := secret.GetEncryptedData()
if err != nil {
Debug("Failed to get encrypted secret data for PGP decryption", "error", err, "secret_name", secret.Name)
return nil, fmt.Errorf("failed to get encrypted secret data: %w", err)
}
DebugWith("Retrieved encrypted secret data for PGP decryption",
slog.String("secret_name", secret.Name),
slog.String("key_id", p.GetID()),
slog.Int("encrypted_length", len(encryptedData)),
)
// Get our age identity
Debug("Getting PGP unlock key identity for secret decryption", "key_id", p.GetID())
_, err = p.GetIdentity()
if err != nil {
Debug("Failed to get PGP unlock identity", "error", err, "key_id", p.GetID())
return nil, fmt.Errorf("failed to get unlock identity: %w", err)
}
// Get or derive the long-term private key
var ltPrivKeyData []byte
// Check if mnemonic is available in environment variable
if envMnemonic := os.Getenv(EnvMnemonic); envMnemonic != "" {
// Use mnemonic directly to derive long-term key
ltIdentity, err := agehd.DeriveIdentity(envMnemonic, 0)
if err != nil {
return nil, fmt.Errorf("failed to derive long-term key from mnemonic: %w", err)
}
ltPrivKeyData = []byte(ltIdentity.String())
} else {
// Get the vault to access current unlock key
stateDir := filepath.Dir(filepath.Dir(filepath.Dir(p.Directory)))
vault, err := GetCurrentVault(p.fs, stateDir)
if err != nil {
return nil, fmt.Errorf("failed to get vault: %w", err)
}
// Get current unlock key
currentUnlockKey, err := vault.GetCurrentUnlockKey()
if err != nil {
return nil, fmt.Errorf("failed to get current unlock key: %w", err)
}
// Get the current unlock key identity
currentUnlockIdentity, err := currentUnlockKey.GetIdentity()
if err != nil {
return nil, fmt.Errorf("failed to get current unlock key identity: %w", err)
}
// Get encrypted long-term key from current unlock key, handling different types
var encryptedLtPrivKey []byte
switch currentUnlockKey := currentUnlockKey.(type) {
case *PassphraseUnlockKey:
// Read the encrypted long-term private key from passphrase unlock key
encryptedLtPrivKey, err = afero.ReadFile(p.fs, filepath.Join(currentUnlockKey.GetDirectory(), "longterm.age"))
if err != nil {
return nil, fmt.Errorf("failed to read encrypted long-term key from current passphrase unlock key: %w", err)
}
case *PGPUnlockKey:
// Read the encrypted long-term private key from PGP unlock key
encryptedLtPrivKey, err = afero.ReadFile(p.fs, filepath.Join(currentUnlockKey.GetDirectory(), "longterm.age"))
if err != nil {
return nil, fmt.Errorf("failed to read encrypted long-term key from current PGP unlock key: %w", err)
}
default:
return nil, fmt.Errorf("unsupported current unlock key type for PGP unlock key creation")
}
// Decrypt long-term private key using current unlock key
ltPrivKeyData, err = decryptWithIdentity(encryptedLtPrivKey, currentUnlockIdentity)
if err != nil {
return nil, fmt.Errorf("failed to decrypt long-term private key: %w", err)
}
}
// Parse long-term private key
Debug("Parsing long-term private key", "key_id", p.GetID())
ltIdentity, err := age.ParseX25519Identity(string(ltPrivKeyData))
if err != nil {
Debug("Failed to parse long-term private key", "error", err, "key_id", p.GetID())
return nil, fmt.Errorf("failed to parse long-term private key: %w", err)
}
DebugWith("Successfully parsed long-term identity",
slog.String("key_id", p.GetID()),
slog.String("public_key", ltIdentity.Recipient().String()),
)
// Decrypt secret data using long-term key
Debug("Decrypting secret data with long-term key", "secret_name", secret.Name, "key_id", p.GetID())
decryptedData, err := decryptWithIdentity(encryptedData, ltIdentity)
if err != nil {
Debug("Failed to decrypt secret with long-term key", "error", err, "secret_name", secret.Name, "key_id", p.GetID())
return nil, fmt.Errorf("failed to decrypt secret: %w", err)
}
DebugWith("Successfully decrypted secret with PGP unlock key",
slog.String("secret_name", secret.Name),
slog.String("key_id", p.GetID()),
slog.Int("decrypted_length", len(decryptedData)),
)
return decryptedData, nil
}
// NewPGPUnlockKey creates a new PGPUnlockKey instance // NewPGPUnlockKey creates a new PGPUnlockKey instance
func NewPGPUnlockKey(fs afero.Fs, directory string, metadata UnlockKeyMetadata) *PGPUnlockKey { func NewPGPUnlockKey(fs afero.Fs, directory string, metadata UnlockKeyMetadata) *PGPUnlockKey {
return &PGPUnlockKey{ return &PGPUnlockKey{

View File

@ -9,6 +9,7 @@ import (
"strings" "strings"
"time" "time"
"filippo.io/age"
"git.eeqj.de/sneak/secret/pkg/agehd" "git.eeqj.de/sneak/secret/pkg/agehd"
"github.com/spf13/afero" "github.com/spf13/afero"
) )
@ -92,7 +93,7 @@ func (s *Secret) GetValue(unlockKey UnlockKey) ([]byte, error) {
// Check if we have SB_SECRET_MNEMONIC environment variable for direct decryption // Check if we have SB_SECRET_MNEMONIC environment variable for direct decryption
if envMnemonic := os.Getenv(EnvMnemonic); envMnemonic != "" { if envMnemonic := os.Getenv(EnvMnemonic); envMnemonic != "" {
Debug("Using mnemonic from environment for secret decryption", "secret_name", s.Name) Debug("Using mnemonic from environment for direct long-term key derivation", "secret_name", s.Name)
// Use mnemonic directly to derive long-term key // Use mnemonic directly to derive long-term key
ltIdentity, err := agehd.DeriveIdentity(envMnemonic, 0) ltIdentity, err := agehd.DeriveIdentity(envMnemonic, 0)
@ -103,62 +104,139 @@ func (s *Secret) GetValue(unlockKey UnlockKey) ([]byte, error) {
Debug("Successfully derived long-term key from mnemonic", "secret_name", s.Name) Debug("Successfully derived long-term key from mnemonic", "secret_name", s.Name)
// Read our own encrypted data // Use the long-term key to decrypt the secret using per-secret architecture
encryptedData, err := s.GetEncryptedData() return s.decryptWithLongTermKey(ltIdentity)
if err != nil {
Debug("Failed to get encrypted data for mnemonic decryption", "error", err, "secret_name", s.Name)
return nil, err
} }
DebugWith("Retrieved encrypted data for mnemonic decryption", Debug("Using unlock key for vault access", "secret_name", s.Name)
slog.String("secret_name", s.Name),
slog.Int("encrypted_length", len(encryptedData)),
)
// Decrypt secret data // Use the provided unlock key to get the vault's long-term private key
Debug("Decrypting secret with long-term key from mnemonic", "secret_name", s.Name)
decryptedData, err := decryptWithIdentity(encryptedData, ltIdentity)
if err != nil {
Debug("Failed to decrypt secret with mnemonic", "error", err, "secret_name", s.Name)
return nil, fmt.Errorf("failed to decrypt secret: %w", err)
}
DebugWith("Successfully decrypted secret with mnemonic",
slog.String("secret_name", s.Name),
slog.Int("decrypted_length", len(decryptedData)),
)
return decryptedData, nil
}
Debug("Using unlock key for secret decryption", "secret_name", s.Name)
// Use the provided unlock key to decrypt the secret
if unlockKey == nil { if unlockKey == nil {
Debug("No unlock key provided for secret decryption", "secret_name", s.Name) Debug("No unlock key provided for secret decryption", "secret_name", s.Name)
return nil, fmt.Errorf("unlock key required to decrypt secret") return nil, fmt.Errorf("unlock key required to decrypt secret")
} }
DebugWith("Delegating secret decryption to unlock key", DebugWith("Getting vault's long-term key using unlock key",
slog.String("secret_name", s.Name), slog.String("secret_name", s.Name),
slog.String("unlock_key_type", unlockKey.GetType()), slog.String("unlock_key_type", unlockKey.GetType()),
slog.String("unlock_key_id", unlockKey.GetID()), slog.String("unlock_key_id", unlockKey.GetID()),
) )
// Delegate decryption to the unlock key implementation // Step 1: Use the unlock key to get the vault's long-term private key
decryptedData, err := unlockKey.DecryptSecret(s) unlockIdentity, err := unlockKey.GetIdentity()
if err != nil { if err != nil {
Debug("Unlock key failed to decrypt secret", "error", err, "secret_name", s.Name, "unlock_key_type", unlockKey.GetType()) Debug("Failed to get unlock key identity", "error", err, "secret_name", s.Name, "unlock_key_type", unlockKey.GetType())
return nil, err return nil, fmt.Errorf("failed to get unlock key identity: %w", err)
} }
DebugWith("Successfully decrypted secret via unlock key", // Read the encrypted long-term private key from the unlock key directory
encryptedLtPrivKeyPath := filepath.Join(unlockKey.GetDirectory(), "longterm.age")
Debug("Reading encrypted long-term private key", "path", encryptedLtPrivKeyPath)
encryptedLtPrivKey, err := afero.ReadFile(s.vault.fs, encryptedLtPrivKeyPath)
if err != nil {
Debug("Failed to read encrypted long-term private key", "error", err, "path", encryptedLtPrivKeyPath)
return nil, fmt.Errorf("failed to read encrypted long-term private key: %w", err)
}
// Decrypt the vault's long-term private key using the unlock key
Debug("Decrypting vault's long-term private key with unlock key", "secret_name", s.Name)
ltPrivKeyData, err := decryptWithIdentity(encryptedLtPrivKey, unlockIdentity)
if err != nil {
Debug("Failed to decrypt long-term private key", "error", err, "secret_name", s.Name)
return nil, fmt.Errorf("failed to decrypt long-term private key: %w", err)
}
// Parse the long-term private key
Debug("Parsing long-term private key", "secret_name", s.Name)
ltIdentity, err := age.ParseX25519Identity(string(ltPrivKeyData))
if err != nil {
Debug("Failed to parse long-term private key", "error", err, "secret_name", s.Name)
return nil, fmt.Errorf("failed to parse long-term private key: %w", err)
}
DebugWith("Successfully obtained vault's long-term key",
slog.String("secret_name", s.Name), slog.String("secret_name", s.Name),
slog.String("unlock_key_type", unlockKey.GetType()), slog.String("public_key", ltIdentity.Recipient().String()),
slog.Int("decrypted_length", len(decryptedData)),
) )
return decryptedData, nil // Use the long-term key to decrypt the secret using per-secret architecture
return s.decryptWithLongTermKey(ltIdentity)
}
// decryptWithLongTermKey decrypts the secret using the vault's long-term private key
// This implements the per-secret key architecture: longterm -> secret private key -> secret value
func (s *Secret) decryptWithLongTermKey(ltIdentity *age.X25519Identity) ([]byte, error) {
DebugWith("Decrypting secret with long-term key using per-secret architecture",
slog.String("secret_name", s.Name),
slog.String("vault_name", s.vault.Name),
)
// Step 1: Read the secret's encrypted private key from priv.age
encryptedSecretPrivKeyPath := filepath.Join(s.Directory, "priv.age")
Debug("Reading encrypted secret private key", "path", encryptedSecretPrivKeyPath)
encryptedSecretPrivKey, err := afero.ReadFile(s.vault.fs, encryptedSecretPrivKeyPath)
if err != nil {
Debug("Failed to read encrypted secret private key", "error", err, "path", encryptedSecretPrivKeyPath)
return nil, fmt.Errorf("failed to read encrypted secret private key: %w", err)
}
DebugWith("Read encrypted secret private key",
slog.String("secret_name", s.Name),
slog.Int("encrypted_length", len(encryptedSecretPrivKey)),
)
// Step 2: Decrypt the secret's private key using the vault's long-term private key
Debug("Decrypting secret's private key with vault's long-term key", "secret_name", s.Name)
secretPrivKeyData, err := decryptWithIdentity(encryptedSecretPrivKey, ltIdentity)
if err != nil {
Debug("Failed to decrypt secret's private key", "error", err, "secret_name", s.Name)
return nil, fmt.Errorf("failed to decrypt secret's private key: %w", err)
}
// Parse the secret's private key
Debug("Parsing secret's private key", "secret_name", s.Name)
secretIdentity, err := age.ParseX25519Identity(string(secretPrivKeyData))
if err != nil {
Debug("Failed to parse secret's private key", "error", err, "secret_name", s.Name)
return nil, fmt.Errorf("failed to parse secret's private key: %w", err)
}
DebugWith("Successfully decrypted and parsed secret's identity",
slog.String("secret_name", s.Name),
slog.String("secret_public_key", secretIdentity.Recipient().String()),
)
// Step 3: Read the secret's encrypted value from value.age
encryptedValuePath := filepath.Join(s.Directory, "value.age")
Debug("Reading encrypted secret value", "path", encryptedValuePath)
encryptedValue, err := afero.ReadFile(s.vault.fs, encryptedValuePath)
if err != nil {
Debug("Failed to read encrypted secret value", "error", err, "path", encryptedValuePath)
return nil, fmt.Errorf("failed to read encrypted secret value: %w", err)
}
DebugWith("Read encrypted secret value",
slog.String("secret_name", s.Name),
slog.Int("encrypted_length", len(encryptedValue)),
)
// Step 4: Decrypt the secret's value using the secret's private key
Debug("Decrypting secret value with secret's private key", "secret_name", s.Name)
decryptedValue, err := decryptWithIdentity(encryptedValue, secretIdentity)
if err != nil {
Debug("Failed to decrypt secret value", "error", err, "secret_name", s.Name)
return nil, fmt.Errorf("failed to decrypt secret value: %w", err)
}
DebugWith("Successfully decrypted secret value using per-secret key architecture",
slog.String("secret_name", s.Name),
slog.Int("decrypted_length", len(decryptedValue)),
)
return decryptedValue, nil
} }
// LoadMetadata loads the secret metadata from disk // LoadMetadata loads the secret metadata from disk

View File

@ -13,7 +13,4 @@ type UnlockKey interface {
GetID() string GetID() string
ID() string // Generate ID from the key's public key ID() string // Generate ID from the key's public key
Remove() error // Remove the unlock key and any associated resources Remove() error // Remove the unlock key and any associated resources
// DecryptSecret decrypts a secret using this unlock key's long-term key management
DecryptSecret(secret *Secret) ([]byte, error)
} }