fix: resolve exported type stuttering issues (revive)
- Rename VaultMetadata to Metadata in internal/vault package to avoid stuttering - Rename BIP85DRNG to DRNG in pkg/bip85 package to avoid stuttering - Update all references in code and tests
This commit is contained in:
parent
4062242063
commit
bdcddadf90
@ -15,7 +15,8 @@
|
|||||||
"Bash(golangci-lint run:*)",
|
"Bash(golangci-lint run:*)",
|
||||||
"Bash(git add:*)",
|
"Bash(git add:*)",
|
||||||
"Bash(gofumpt:*)",
|
"Bash(gofumpt:*)",
|
||||||
"Bash(git stash:*)"
|
"Bash(git stash:*)",
|
||||||
|
"Bash(git commit:*)"
|
||||||
],
|
],
|
||||||
"deny": []
|
"deny": []
|
||||||
}
|
}
|
||||||
|
@ -1283,6 +1283,7 @@ func test18AgeKeyOperations(t *testing.T, tempDir, secretPath, testMnemonic stri
|
|||||||
fmt.Sprintf("HOME=%s", os.Getenv("HOME")),
|
fmt.Sprintf("HOME=%s", os.Getenv("HOME")),
|
||||||
}
|
}
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
return string(output), err
|
return string(output), err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1349,6 +1350,7 @@ func test19DisasterRecovery(t *testing.T, tempDir, secretPath, testMnemonic stri
|
|||||||
fmt.Sprintf("HOME=%s", os.Getenv("HOME")),
|
fmt.Sprintf("HOME=%s", os.Getenv("HOME")),
|
||||||
}
|
}
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
return string(output), err
|
return string(output), err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1443,6 +1445,7 @@ func test20VersionTimestamps(t *testing.T, tempDir, secretPath, testMnemonic str
|
|||||||
fmt.Sprintf("HOME=%s", os.Getenv("HOME")),
|
fmt.Sprintf("HOME=%s", os.Getenv("HOME")),
|
||||||
}
|
}
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
return string(output), err
|
return string(output), err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2076,6 +2079,7 @@ func readFile(t *testing.T, path string) []byte {
|
|||||||
t.Helper()
|
t.Helper()
|
||||||
data, err := os.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
require.NoError(t, err, "Should be able to read file: %s", path)
|
require.NoError(t, err, "Should be able to read file: %s", path)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2125,6 +2129,7 @@ func copyDir(src, dst string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,7 +244,7 @@ func (cli *Instance) VaultImport(cmd *cobra.Command, vaultName string) error {
|
|||||||
existingMetadata, err := vault.LoadVaultMetadata(cli.fs, vaultDir)
|
existingMetadata, err := vault.LoadVaultMetadata(cli.fs, vaultDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If metadata doesn't exist, create new
|
// If metadata doesn't exist, create new
|
||||||
existingMetadata = &vault.VaultMetadata{
|
existingMetadata = &vault.Metadata{
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,6 +126,7 @@ func (cli *Instance) ListVersions(cmd *cobra.Command, secretName string) error {
|
|||||||
status = "current (error)"
|
status = "current (error)"
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", version, "-", status, "-", "-")
|
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", version, "-", status, "-", "-")
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,5 +193,6 @@ func (cli *Instance) PromoteVersion(cmd *cobra.Command, secretName string, versi
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd.Printf("Promoted version %s to current for secret '%s'\n", version, secretName)
|
cmd.Printf("Promoted version %s to current for secret '%s'\n", version, secretName)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -145,6 +145,7 @@ func (k *KeychainUnlocker) GetID() string {
|
|||||||
// We cannot continue with a fallback ID as that would mask data corruption
|
// We cannot continue with a fallback ID as that would mask data corruption
|
||||||
panic(fmt.Sprintf("Keychain unlocker metadata is corrupt or missing keychain item name: %v", err))
|
panic(fmt.Sprintf("Keychain unlocker metadata is corrupt or missing keychain item name: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%s-keychain", keychainItemName)
|
return fmt.Sprintf("%s-keychain", keychainItemName)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,6 +173,7 @@ func (k *KeychainUnlocker) Remove() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Debug("Successfully removed keychain unlocker", "unlocker_id", k.GetID(), "keychain_item", keychainItemName)
|
Debug("Successfully removed keychain unlocker", "unlocker_id", k.GetID(), "keychain_item", keychainItemName)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,6 +212,7 @@ func generateKeychainUnlockerName(vaultName string) (string, error) {
|
|||||||
|
|
||||||
// Format: secret-<vault>-<hostname>-<date>
|
// Format: secret-<vault>-<hostname>-<date>
|
||||||
enrollmentDate := time.Now().Format("2006-01-02")
|
enrollmentDate := time.Now().Format("2006-01-02")
|
||||||
|
|
||||||
return fmt.Sprintf("secret-%s-%s-%s", vaultName, hostname, enrollmentDate), nil
|
return fmt.Sprintf("secret-%s-%s-%s", vaultName, hostname, enrollmentDate), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,7 +380,9 @@ func CreateKeychainUnlocker(fs afero.Fs, stateDir string) (*KeychainUnlocker, er
|
|||||||
return nil, fmt.Errorf("failed to marshal unlocker metadata: %w", err)
|
return nil, fmt.Errorf("failed to marshal unlocker metadata: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := afero.WriteFile(fs, filepath.Join(unlockerDir, "unlocker-metadata.json"), metadataBytes, FilePerms); err != nil {
|
if err := afero.WriteFile(fs,
|
||||||
|
filepath.Join(unlockerDir, "unlocker-metadata.json"),
|
||||||
|
metadataBytes, FilePerms); err != nil {
|
||||||
return nil, fmt.Errorf("failed to write unlocker metadata: %w", err)
|
return nil, fmt.Errorf("failed to write unlocker metadata: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,6 +399,7 @@ func checkMacOSAvailable() error {
|
|||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
return fmt.Errorf("macOS security command not available: %w (keychain unlockers are only supported on macOS)", err)
|
return fmt.Errorf("macOS security command not available: %w (keychain unlockers are only supported on macOS)", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -415,7 +421,7 @@ func storeInKeychain(itemName string, data []byte) error {
|
|||||||
if err := validateKeychainItemName(itemName); err != nil {
|
if err := validateKeychainItemName(itemName); err != nil {
|
||||||
return fmt.Errorf("invalid keychain item name: %w", err)
|
return fmt.Errorf("invalid keychain item name: %w", err)
|
||||||
}
|
}
|
||||||
cmd := exec.Command("/usr/bin/security", "add-generic-password", //nolint:gosec // Input validated by validateKeychainItemName
|
cmd := exec.Command("/usr/bin/security", "add-generic-password", //nolint:gosec
|
||||||
"-a", itemName,
|
"-a", itemName,
|
||||||
"-s", itemName,
|
"-s", itemName,
|
||||||
"-w", string(data),
|
"-w", string(data),
|
||||||
@ -434,7 +440,7 @@ func retrieveFromKeychain(itemName string) ([]byte, error) {
|
|||||||
return nil, fmt.Errorf("invalid keychain item name: %w", err)
|
return nil, fmt.Errorf("invalid keychain item name: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command("/usr/bin/security", "find-generic-password", //nolint:gosec // Input validated by validateKeychainItemName
|
cmd := exec.Command("/usr/bin/security", "find-generic-password", //nolint:gosec
|
||||||
"-a", itemName,
|
"-a", itemName,
|
||||||
"-s", itemName,
|
"-s", itemName,
|
||||||
"-w") // Return password only
|
"-w") // Return password only
|
||||||
@ -458,7 +464,7 @@ func deleteFromKeychain(itemName string) error {
|
|||||||
return fmt.Errorf("invalid keychain item name: %w", err)
|
return fmt.Errorf("invalid keychain item name: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command("/usr/bin/security", "delete-generic-password", //nolint:gosec // Input validated by validateKeychainItemName
|
cmd := exec.Command("/usr/bin/security", "delete-generic-password", //nolint:gosec
|
||||||
"-a", itemName,
|
"-a", itemName,
|
||||||
"-s", itemName)
|
"-s", itemName)
|
||||||
|
|
||||||
|
@ -6,11 +6,13 @@ import (
|
|||||||
|
|
||||||
// VaultMetadata contains information about a vault
|
// VaultMetadata contains information about a vault
|
||||||
type VaultMetadata struct {
|
type VaultMetadata struct {
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
DerivationIndex uint32 `json:"derivation_index"`
|
DerivationIndex uint32 `json:"derivation_index"`
|
||||||
PublicKeyHash string `json:"public_key_hash,omitempty"` // Double SHA256 hash of the actual long-term public key
|
// Double SHA256 hash of the actual long-term public key
|
||||||
MnemonicFamilyHash string `json:"mnemonic_family_hash,omitempty"` // Double SHA256 hash of index-0 key (for grouping vaults from same mnemonic)
|
PublicKeyHash string `json:"public_key_hash,omitempty"`
|
||||||
|
// Double SHA256 hash of index-0 key (for grouping vaults from same mnemonic)
|
||||||
|
MnemonicFamilyHash string `json:"mnemonic_family_hash,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnlockerMetadata contains information about an unlocker
|
// UnlockerMetadata contains information about an unlocker
|
||||||
|
@ -121,6 +121,7 @@ func (p *PassphraseUnlocker) Remove() error {
|
|||||||
if err := p.fs.RemoveAll(p.Directory); err != nil {
|
if err := p.fs.RemoveAll(p.Directory); err != nil {
|
||||||
return fmt.Errorf("failed to remove passphrase unlocker directory: %w", err)
|
return fmt.Errorf("failed to remove passphrase unlocker directory: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,6 +129,7 @@ func (p *PGPUnlocker) GetID() string {
|
|||||||
// We cannot continue with a fallback ID as that would mask data corruption
|
// We cannot continue with a fallback ID as that would mask data corruption
|
||||||
panic(fmt.Sprintf("PGP unlocker metadata is corrupt or missing GPG key ID: %v", err))
|
panic(fmt.Sprintf("PGP unlocker metadata is corrupt or missing GPG key ID: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%s-pgp", gpgKeyID)
|
return fmt.Sprintf("%s-pgp", gpgKeyID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,6 +140,7 @@ func (p *PGPUnlocker) Remove() error {
|
|||||||
if err := p.fs.RemoveAll(p.Directory); err != nil {
|
if err := p.fs.RemoveAll(p.Directory); err != nil {
|
||||||
return fmt.Errorf("failed to remove PGP unlocker directory: %w", err)
|
return fmt.Errorf("failed to remove PGP unlocker directory: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,6 +179,7 @@ func generatePGPUnlockerName() (string, error) {
|
|||||||
|
|
||||||
// Format: hostname-pgp-YYYY-MM-DD
|
// Format: hostname-pgp-YYYY-MM-DD
|
||||||
enrollmentDate := time.Now().Format("2006-01-02")
|
enrollmentDate := time.Now().Format("2006-01-02")
|
||||||
|
|
||||||
return fmt.Sprintf("%s-pgp-%s", hostname, enrollmentDate), nil
|
return fmt.Sprintf("%s-pgp-%s", hostname, enrollmentDate), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,7 +323,9 @@ func CreatePGPUnlocker(fs afero.Fs, stateDir string, gpgKeyID string) (*PGPUnloc
|
|||||||
return nil, fmt.Errorf("failed to marshal unlocker metadata: %w", err)
|
return nil, fmt.Errorf("failed to marshal unlocker metadata: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := afero.WriteFile(fs, filepath.Join(unlockerDir, "unlocker-metadata.json"), metadataBytes, FilePerms); err != nil {
|
if err := afero.WriteFile(fs,
|
||||||
|
filepath.Join(unlockerDir, "unlocker-metadata.json"),
|
||||||
|
metadataBytes, FilePerms); err != nil {
|
||||||
return nil, fmt.Errorf("failed to write unlocker metadata: %w", err)
|
return nil, fmt.Errorf("failed to write unlocker metadata: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,6 +382,7 @@ func checkGPGAvailable() error {
|
|||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
return fmt.Errorf("GPG not available: %w (make sure 'gpg' command is installed and in PATH)", err)
|
return fmt.Errorf("GPG not available: %w (make sure 'gpg' command is installed and in PATH)", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,6 +78,7 @@ func (s *Secret) Save(value []byte, force bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Debug("Successfully saved secret", "secret_name", s.Name)
|
Debug("Successfully saved secret", "secret_name", s.Name)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,6 +221,7 @@ func (s *Secret) LoadMetadata() error {
|
|||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
UpdatedAt: now,
|
UpdatedAt: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,6 +280,7 @@ func GetCurrentVault(fs afero.Fs, stateDir string) (VaultInterface, error) {
|
|||||||
if getCurrentVaultFunc == nil {
|
if getCurrentVaultFunc == nil {
|
||||||
return nil, fmt.Errorf("GetCurrentVault function not registered")
|
return nil, fmt.Errorf("GetCurrentVault function not registered")
|
||||||
}
|
}
|
||||||
|
|
||||||
return getCurrentVaultFunc(fs, stateDir)
|
return getCurrentVaultFunc(fs, stateDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,6 +262,7 @@ func isValidSecretName(name string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ func NewVersion(vault VaultInterface, secretName string, version string) *Versio
|
|||||||
)
|
)
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
return &Version{
|
return &Version{
|
||||||
SecretName: secretName,
|
SecretName: secretName,
|
||||||
Version: version,
|
Version: version,
|
||||||
@ -219,6 +220,7 @@ func (sv *Version) Save(value []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Debug("Successfully saved secret version", "version", sv.Version, "secret_name", sv.SecretName)
|
Debug("Successfully saved secret version", "version", sv.Version, "secret_name", sv.SecretName)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,6 +279,7 @@ func (sv *Version) LoadMetadata(ltIdentity *age.X25519Identity) error {
|
|||||||
|
|
||||||
sv.Metadata = metadata
|
sv.Metadata = metadata
|
||||||
Debug("Successfully loaded version metadata", "version", sv.Version)
|
Debug("Successfully loaded version metadata", "version", sv.Version)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,6 +347,7 @@ func (sv *Version) GetValue(ltIdentity *age.X25519Identity) ([]byte, error) {
|
|||||||
"version", sv.Version,
|
"version", sv.Version,
|
||||||
"value_length", len(value),
|
"value_length", len(value),
|
||||||
"is_empty", len(value) == 0)
|
"is_empty", len(value) == 0)
|
||||||
|
|
||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,6 +396,7 @@ func GetCurrentVersion(fs afero.Fs, secretDir string) (string, error) {
|
|||||||
if len(parts) >= 2 && parts[0] == "versions" {
|
if len(parts) >= 2 && parts[0] == "versions" {
|
||||||
return parts[1], nil
|
return parts[1], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", fmt.Errorf("invalid current version symlink format: %s", target)
|
return "", fmt.Errorf("invalid current version symlink format: %s", target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,9 @@ func TestVersionIntegrationWorkflow(t *testing.T) {
|
|||||||
stateDir := "/test/state"
|
stateDir := "/test/state"
|
||||||
|
|
||||||
// Set mnemonic for testing
|
// Set mnemonic for testing
|
||||||
t.Setenv(secret.EnvMnemonic, "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about")
|
t.Setenv(secret.EnvMnemonic,
|
||||||
|
"abandon abandon abandon abandon abandon abandon "+
|
||||||
|
"abandon abandon abandon abandon abandon about")
|
||||||
|
|
||||||
// Create vault
|
// Create vault
|
||||||
vault, err := CreateVault(fs, stateDir, "test")
|
vault, err := CreateVault(fs, stateDir, "test")
|
||||||
@ -278,7 +280,7 @@ func TestVersionConcurrency(t *testing.T) {
|
|||||||
done := make(chan bool, 10)
|
done := make(chan bool, 10)
|
||||||
errors := make(chan error, 10)
|
errors := make(chan error, 10)
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
for range 10 {
|
||||||
go func() {
|
go func() {
|
||||||
value, err := vault.GetSecret(secretName)
|
value, err := vault.GetSecret(secretName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -291,7 +293,7 @@ func TestVersionConcurrency(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wait for all goroutines
|
// Wait for all goroutines
|
||||||
for i := 0; i < 10; i++ {
|
for range 10 {
|
||||||
<-done
|
<-done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ func isValidVaultName(name string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
matched, _ := regexp.MatchString(`^[a-z0-9\.\-\_]+$`, name)
|
matched, _ := regexp.MatchString(`^[a-z0-9\.\-\_]+$`, name)
|
||||||
|
|
||||||
return matched
|
return matched
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,6 +86,7 @@ func ResolveVaultSymlink(fs afero.Fs, symlinkPath string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
secret.Debug("resolveVaultSymlink completed successfully", "result", target)
|
secret.Debug("resolveVaultSymlink completed successfully", "result", target)
|
||||||
|
|
||||||
return target, nil
|
return target, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,6 +104,7 @@ func ResolveVaultSymlink(fs afero.Fs, symlinkPath string) (string, error) {
|
|||||||
secret.Debug("Read target path from file", "target", target)
|
secret.Debug("Read target path from file", "target", target)
|
||||||
|
|
||||||
secret.Debug("resolveVaultSymlink completed via fallback", "result", target)
|
secret.Debug("resolveVaultSymlink completed via fallback", "result", target)
|
||||||
|
|
||||||
return target, nil
|
return target, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,7 +253,7 @@ func CreateVault(fs afero.Fs, stateDir string, name string) (*Vault, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save vault metadata
|
// Save vault metadata
|
||||||
metadata := &VaultMetadata{
|
metadata := &Metadata{
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now(),
|
||||||
DerivationIndex: derivationIndex,
|
DerivationIndex: derivationIndex,
|
||||||
PublicKeyHash: publicKeyHash,
|
PublicKeyHash: publicKeyHash,
|
||||||
@ -268,6 +271,7 @@ func CreateVault(fs afero.Fs, stateDir string, name string) (*Vault, error) {
|
|||||||
|
|
||||||
// Create and return the vault
|
// Create and return the vault
|
||||||
secret.Debug("Successfully created vault", "name", name)
|
secret.Debug("Successfully created vault", "name", name)
|
||||||
|
|
||||||
return NewVault(fs, stateDir, name), nil
|
return NewVault(fs, stateDir, name), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,5 +325,6 @@ func SelectVault(fs afero.Fs, stateDir string, name string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
secret.Debug("Successfully selected vault", "vault_name", name)
|
secret.Debug("Successfully selected vault", "vault_name", name)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
|
|
||||||
// Alias the metadata types from secret package for convenience
|
// Alias the metadata types from secret package for convenience
|
||||||
type (
|
type (
|
||||||
VaultMetadata = secret.VaultMetadata
|
Metadata = secret.VaultMetadata
|
||||||
UnlockerMetadata = secret.UnlockerMetadata
|
UnlockerMetadata = secret.UnlockerMetadata
|
||||||
SecretMetadata = secret.Metadata
|
SecretMetadata = secret.Metadata
|
||||||
Configuration = secret.Configuration
|
Configuration = secret.Configuration
|
||||||
@ -24,6 +24,7 @@ type (
|
|||||||
func ComputeDoubleSHA256(data []byte) string {
|
func ComputeDoubleSHA256(data []byte) string {
|
||||||
firstHash := sha256.Sum256(data)
|
firstHash := sha256.Sum256(data)
|
||||||
secondHash := sha256.Sum256(firstHash[:])
|
secondHash := sha256.Sum256(firstHash[:])
|
||||||
|
|
||||||
return hex.EncodeToString(secondHash[:])
|
return hex.EncodeToString(secondHash[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +72,7 @@ func GetNextDerivationIndex(fs afero.Fs, stateDir string, mnemonic string) (uint
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var metadata VaultMetadata
|
var metadata Metadata
|
||||||
if err := json.Unmarshal(metadataBytes, &metadata); err != nil {
|
if err := json.Unmarshal(metadataBytes, &metadata); err != nil {
|
||||||
// Skip vaults with invalid metadata
|
// Skip vaults with invalid metadata
|
||||||
continue
|
continue
|
||||||
@ -93,7 +94,7 @@ func GetNextDerivationIndex(fs afero.Fs, stateDir string, mnemonic string) (uint
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SaveVaultMetadata saves vault metadata to the vault directory
|
// SaveVaultMetadata saves vault metadata to the vault directory
|
||||||
func SaveVaultMetadata(fs afero.Fs, vaultDir string, metadata *VaultMetadata) error {
|
func SaveVaultMetadata(fs afero.Fs, vaultDir string, metadata *Metadata) error {
|
||||||
metadataPath := filepath.Join(vaultDir, "vault-metadata.json")
|
metadataPath := filepath.Join(vaultDir, "vault-metadata.json")
|
||||||
|
|
||||||
metadataBytes, err := json.MarshalIndent(metadata, "", " ")
|
metadataBytes, err := json.MarshalIndent(metadata, "", " ")
|
||||||
@ -109,7 +110,7 @@ func SaveVaultMetadata(fs afero.Fs, vaultDir string, metadata *VaultMetadata) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LoadVaultMetadata loads vault metadata from the vault directory
|
// LoadVaultMetadata loads vault metadata from the vault directory
|
||||||
func LoadVaultMetadata(fs afero.Fs, vaultDir string) (*VaultMetadata, error) {
|
func LoadVaultMetadata(fs afero.Fs, vaultDir string) (*Metadata, error) {
|
||||||
metadataPath := filepath.Join(vaultDir, "vault-metadata.json")
|
metadataPath := filepath.Join(vaultDir, "vault-metadata.json")
|
||||||
|
|
||||||
metadataBytes, err := afero.ReadFile(fs, metadataPath)
|
metadataBytes, err := afero.ReadFile(fs, metadataPath)
|
||||||
@ -117,7 +118,7 @@ func LoadVaultMetadata(fs afero.Fs, vaultDir string) (*VaultMetadata, error) {
|
|||||||
return nil, fmt.Errorf("failed to read vault metadata: %w", err)
|
return nil, fmt.Errorf("failed to read vault metadata: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var metadata VaultMetadata
|
var metadata Metadata
|
||||||
if err := json.Unmarshal(metadataBytes, &metadata); err != nil {
|
if err := json.Unmarshal(metadataBytes, &metadata); err != nil {
|
||||||
return nil, fmt.Errorf("failed to unmarshal vault metadata: %w", err)
|
return nil, fmt.Errorf("failed to unmarshal vault metadata: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ func TestVaultMetadata(t *testing.T) {
|
|||||||
t.Fatalf("Failed to write public key: %v", err)
|
t.Fatalf("Failed to write public key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata1 := &VaultMetadata{
|
metadata1 := &Metadata{
|
||||||
DerivationIndex: 0,
|
DerivationIndex: 0,
|
||||||
PublicKeyHash: pubKeyHash0, // Hash of the actual key (index 0)
|
PublicKeyHash: pubKeyHash0, // Hash of the actual key (index 0)
|
||||||
MnemonicFamilyHash: pubKeyHash0, // Hash of index 0 key (for family identification)
|
MnemonicFamilyHash: pubKeyHash0, // Hash of index 0 key (for family identification)
|
||||||
@ -117,7 +117,7 @@ func TestVaultMetadata(t *testing.T) {
|
|||||||
// Compute the hash for index 5 key
|
// Compute the hash for index 5 key
|
||||||
pubKeyHash5 := ComputeDoubleSHA256([]byte(pubKey5))
|
pubKeyHash5 := ComputeDoubleSHA256([]byte(pubKey5))
|
||||||
|
|
||||||
metadata2 := &VaultMetadata{
|
metadata2 := &Metadata{
|
||||||
DerivationIndex: 5,
|
DerivationIndex: 5,
|
||||||
PublicKeyHash: pubKeyHash5, // Hash of the actual key (index 5)
|
PublicKeyHash: pubKeyHash5, // Hash of the actual key (index 5)
|
||||||
MnemonicFamilyHash: pubKeyHash0, // Same family hash since it's from the same mnemonic
|
MnemonicFamilyHash: pubKeyHash0, // Same family hash since it's from the same mnemonic
|
||||||
@ -143,7 +143,7 @@ func TestVaultMetadata(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create and save metadata
|
// Create and save metadata
|
||||||
metadata := &VaultMetadata{
|
metadata := &Metadata{
|
||||||
DerivationIndex: 3,
|
DerivationIndex: 3,
|
||||||
PublicKeyHash: "test-public-key-hash",
|
PublicKeyHash: "test-public-key-hash",
|
||||||
}
|
}
|
||||||
|
@ -89,6 +89,7 @@ func isValidSecretName(name string) bool {
|
|||||||
|
|
||||||
// Check the basic pattern
|
// Check the basic pattern
|
||||||
matched, _ := regexp.MatchString(`^[a-z0-9\.\-\_\/]+$`, name)
|
matched, _ := regexp.MatchString(`^[a-z0-9\.\-\_\/]+$`, name)
|
||||||
|
|
||||||
return matched
|
return matched
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,7 +222,10 @@ func (v *Vault) AddSecret(name string, value []byte, force bool) error {
|
|||||||
return fmt.Errorf("failed to set current version: %w", err)
|
return fmt.Errorf("failed to set current version: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
secret.Debug("Successfully added secret version to vault", "secret_name", name, "version", versionName, "vault_name", v.Name)
|
secret.Debug("Successfully added secret version to vault",
|
||||||
|
"secret_name", name, "version", versionName,
|
||||||
|
"vault_name", v.Name)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,8 @@ func (v *Vault) GetCurrentUnlocker() (secret.Unlocker, error) {
|
|||||||
// Try to read as symlink first
|
// Try to read as symlink first
|
||||||
unlockerDir, err = linkReader.ReadlinkIfPossible(currentUnlockerPath)
|
unlockerDir, err = linkReader.ReadlinkIfPossible(currentUnlockerPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
secret.Debug("Failed to read symlink, falling back to file contents", "error", err, "symlink_path", currentUnlockerPath)
|
secret.Debug("Failed to read symlink, falling back to file contents",
|
||||||
|
"error", err, "symlink_path", currentUnlockerPath)
|
||||||
// Fallback: read the path from file contents
|
// Fallback: read the path from file contents
|
||||||
unlockerDirBytes, err := afero.ReadFile(v.fs, currentUnlockerPath)
|
unlockerDirBytes, err := afero.ReadFile(v.fs, currentUnlockerPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -363,7 +364,9 @@ func (v *Vault) CreatePassphraseUnlocker(passphrase string) (*secret.PassphraseU
|
|||||||
|
|
||||||
// Write public key
|
// Write public key
|
||||||
pubKeyPath := filepath.Join(unlockerDir, "pub.age")
|
pubKeyPath := filepath.Join(unlockerDir, "pub.age")
|
||||||
if err := afero.WriteFile(v.fs, pubKeyPath, []byte(unlockerIdentity.Recipient().String()), secret.FilePerms); err != nil {
|
if err := afero.WriteFile(v.fs, pubKeyPath,
|
||||||
|
[]byte(unlockerIdentity.Recipient().String()),
|
||||||
|
secret.FilePerms); err != nil {
|
||||||
return nil, fmt.Errorf("failed to write unlocker public key: %w", err)
|
return nil, fmt.Errorf("failed to write unlocker public key: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ func NewVault(fs afero.Fs, stateDir string, name string) *Vault {
|
|||||||
longTermKey: nil,
|
longTermKey: nil,
|
||||||
}
|
}
|
||||||
secret.Debug("Created NewVault instance successfully")
|
secret.Debug("Created NewVault instance successfully")
|
||||||
|
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,6 +93,7 @@ func (v *Vault) GetOrDeriveLongTermKey() (*age.X25519Identity, error) {
|
|||||||
"derived_hash", derivedPubKeyHash,
|
"derived_hash", derivedPubKeyHash,
|
||||||
"stored_hash", metadata.PublicKeyHash,
|
"stored_hash", metadata.PublicKeyHash,
|
||||||
"derivation_index", metadata.DerivationIndex)
|
"derivation_index", metadata.DerivationIndex)
|
||||||
|
|
||||||
return nil, fmt.Errorf("derived public key does not match vault: mnemonic may be incorrect")
|
return nil, fmt.Errorf("derived public key does not match vault: mnemonic may be incorrect")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +180,8 @@ func (v *Vault) GetOrDeriveLongTermKey() (*age.X25519Identity, error) {
|
|||||||
|
|
||||||
// Cache the derived key by unlocking the vault
|
// Cache the derived key by unlocking the vault
|
||||||
v.Unlock(ltIdentity)
|
v.Unlock(ltIdentity)
|
||||||
secret.Debug("Vault is unlocked (lt key in memory) via unlocker", "vault_name", v.Name, "unlocker_type", unlocker.GetType())
|
secret.Debug("Vault is unlocked (lt key in memory) via unlocker",
|
||||||
|
"vault_name", v.Name, "unlocker_type", unlocker.GetType())
|
||||||
|
|
||||||
return ltIdentity, nil
|
return ltIdentity, nil
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,7 @@ func IdentityFromEntropy(ent []byte) (*age.X25519Identity, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("bech32 encode: %w", err)
|
return nil, fmt.Errorf("bech32 encode: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return age.ParseX25519Identity(strings.ToUpper(s))
|
return age.ParseX25519Identity(strings.ToUpper(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,6 +126,7 @@ func DeriveIdentity(mnemonic string, n uint32) (*age.X25519Identity, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return IdentityFromEntropy(ent)
|
return IdentityFromEntropy(ent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,5 +137,6 @@ func DeriveIdentityFromXPRV(xprv string, n uint32) (*age.X25519Identity, error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return IdentityFromEntropy(ent)
|
return IdentityFromEntropy(ent)
|
||||||
}
|
}
|
||||||
|
@ -393,6 +393,7 @@ func TestIdentityFromEntropyEdgeCases(t *testing.T) {
|
|||||||
err,
|
err,
|
||||||
) // In test context, panic is acceptable for setup failures
|
) // In test context, panic is acceptable for setup failures
|
||||||
}
|
}
|
||||||
|
|
||||||
return b
|
return b
|
||||||
}(),
|
}(),
|
||||||
expectError: false,
|
expectError: false,
|
||||||
|
@ -45,29 +45,29 @@ var (
|
|||||||
TestNetPrivateKey = []byte{0x04, 0x35, 0x83, 0x94}
|
TestNetPrivateKey = []byte{0x04, 0x35, 0x83, 0x94}
|
||||||
)
|
)
|
||||||
|
|
||||||
// BIP85DRNG is a deterministic random number generator seeded by BIP85 entropy
|
// DRNG is a deterministic random number generator seeded by BIP85 entropy
|
||||||
type BIP85DRNG struct {
|
type DRNG struct {
|
||||||
shake io.Reader
|
shake io.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBIP85DRNG creates a new DRNG seeded with BIP85 entropy
|
// NewBIP85DRNG creates a new DRNG seeded with BIP85 entropy
|
||||||
func NewBIP85DRNG(entropy []byte) *BIP85DRNG {
|
func NewBIP85DRNG(entropy []byte) *DRNG {
|
||||||
// The entropy must be exactly 64 bytes (512 bits)
|
// The entropy must be exactly 64 bytes (512 bits)
|
||||||
if len(entropy) != 64 {
|
if len(entropy) != 64 {
|
||||||
panic("BIP85DRNG entropy must be 64 bytes")
|
panic("DRNG entropy must be 64 bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize SHAKE256 with the entropy
|
// Initialize SHAKE256 with the entropy
|
||||||
shake := sha3.NewShake256()
|
shake := sha3.NewShake256()
|
||||||
shake.Write(entropy)
|
shake.Write(entropy)
|
||||||
|
|
||||||
return &BIP85DRNG{
|
return &DRNG{
|
||||||
shake: shake,
|
shake: shake,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read implements the io.Reader interface
|
// Read implements the io.Reader interface
|
||||||
func (d *BIP85DRNG) Read(p []byte) (n int, err error) {
|
func (d *DRNG) Read(p []byte) (n int, err error) {
|
||||||
return d.shake.Read(p)
|
return d.shake.Read(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,6 +266,7 @@ func DeriveXPRV(masterKey *hdkeychain.ExtendedKey, index uint32) (*hdkeychain.Ex
|
|||||||
func doubleSHA256(data []byte) []byte {
|
func doubleSHA256(data []byte) []byte {
|
||||||
hash1 := sha256.Sum256(data)
|
hash1 := sha256.Sum256(data)
|
||||||
hash2 := sha256.Sum256(hash1[:])
|
hash2 := sha256.Sum256(hash1[:])
|
||||||
|
|
||||||
return hash2[:]
|
return hash2[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user