forked from sneak/secret
Compare commits
No commits in common. "fix/issue-3" and "main" have entirely different histories.
fix/issue-
...
main
@ -1,82 +0,0 @@
|
|||||||
package secret
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.eeqj.de/sneak/secret/pkg/agehd"
|
|
||||||
"github.com/awnumar/memguard"
|
|
||||||
"github.com/spf13/afero"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
// realVault is a minimal VaultInterface backed by a real afero filesystem,
|
|
||||||
// using the same directory layout as vault.Vault.
|
|
||||||
type realVault struct {
|
|
||||||
name string
|
|
||||||
stateDir string
|
|
||||||
fs afero.Fs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *realVault) GetDirectory() (string, error) {
|
|
||||||
return filepath.Join(v.stateDir, "vaults.d", v.name), nil
|
|
||||||
}
|
|
||||||
func (v *realVault) GetName() string { return v.name }
|
|
||||||
func (v *realVault) GetFilesystem() afero.Fs { return v.fs }
|
|
||||||
|
|
||||||
// Unused by getLongTermPrivateKey — these satisfy VaultInterface.
|
|
||||||
func (v *realVault) AddSecret(string, *memguard.LockedBuffer, bool) error { panic("not used") }
|
|
||||||
func (v *realVault) GetCurrentUnlocker() (Unlocker, error) { panic("not used") }
|
|
||||||
func (v *realVault) CreatePassphraseUnlocker(*memguard.LockedBuffer) (*PassphraseUnlocker, error) {
|
|
||||||
panic("not used")
|
|
||||||
}
|
|
||||||
|
|
||||||
// createRealVault sets up a complete vault directory structure on an in-memory
|
|
||||||
// filesystem, identical to what vault.CreateVault produces.
|
|
||||||
func createRealVault(t *testing.T, fs afero.Fs, stateDir, name string, derivationIndex uint32) *realVault {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
vaultDir := filepath.Join(stateDir, "vaults.d", name)
|
|
||||||
require.NoError(t, fs.MkdirAll(filepath.Join(vaultDir, "secrets.d"), DirPerms))
|
|
||||||
require.NoError(t, fs.MkdirAll(filepath.Join(vaultDir, "unlockers.d"), DirPerms))
|
|
||||||
|
|
||||||
metadata := VaultMetadata{
|
|
||||||
CreatedAt: time.Now(),
|
|
||||||
DerivationIndex: derivationIndex,
|
|
||||||
}
|
|
||||||
metaBytes, err := json.Marshal(metadata)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NoError(t, afero.WriteFile(fs, filepath.Join(vaultDir, "vault-metadata.json"), metaBytes, FilePerms))
|
|
||||||
|
|
||||||
return &realVault{name: name, stateDir: stateDir, fs: fs}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetLongTermPrivateKeyUsesVaultDerivationIndex(t *testing.T) {
|
|
||||||
const testMnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
|
||||||
|
|
||||||
// Derive expected keys at two different indices to prove they differ.
|
|
||||||
key0, err := agehd.DeriveIdentity(testMnemonic, 0)
|
|
||||||
require.NoError(t, err)
|
|
||||||
key5, err := agehd.DeriveIdentity(testMnemonic, 5)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotEqual(t, key0.String(), key5.String(),
|
|
||||||
"sanity check: different derivation indices must produce different keys")
|
|
||||||
|
|
||||||
// Build a real vault with DerivationIndex=5 on an in-memory filesystem.
|
|
||||||
fs := afero.NewMemMapFs()
|
|
||||||
vault := createRealVault(t, fs, "/state", "test-vault", 5)
|
|
||||||
|
|
||||||
t.Setenv(EnvMnemonic, testMnemonic)
|
|
||||||
|
|
||||||
result, err := getLongTermPrivateKey(fs, vault)
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer result.Destroy()
|
|
||||||
|
|
||||||
assert.Equal(t, key5.String(), string(result.Bytes()),
|
|
||||||
"getLongTermPrivateKey should derive at vault's DerivationIndex (5)")
|
|
||||||
assert.NotEqual(t, key0.String(), string(result.Bytes()),
|
|
||||||
"getLongTermPrivateKey must not use hardcoded index 0")
|
|
||||||
}
|
|
||||||
@ -251,25 +251,8 @@ func getLongTermPrivateKey(fs afero.Fs, vault VaultInterface) (*memguard.LockedB
|
|||||||
// Check if mnemonic is available in environment variable
|
// Check if mnemonic is available in environment variable
|
||||||
envMnemonic := os.Getenv(EnvMnemonic)
|
envMnemonic := os.Getenv(EnvMnemonic)
|
||||||
if envMnemonic != "" {
|
if envMnemonic != "" {
|
||||||
// Read vault metadata to get the correct derivation index
|
// Use mnemonic directly to derive long-term key
|
||||||
vaultDir, err := vault.GetDirectory()
|
ltIdentity, err := agehd.DeriveIdentity(envMnemonic, 0)
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get vault directory: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
metadataPath := filepath.Join(vaultDir, "vault-metadata.json")
|
|
||||||
metadataBytes, err := afero.ReadFile(fs, metadataPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to read vault metadata: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var metadata VaultMetadata
|
|
||||||
if err := json.Unmarshal(metadataBytes, &metadata); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to parse vault metadata: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use mnemonic with the vault's actual derivation index
|
|
||||||
ltIdentity, err := agehd.DeriveIdentity(envMnemonic, metadata.DerivationIndex)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to derive long-term key from mnemonic: %w", err)
|
return nil, fmt.Errorf("failed to derive long-term key from mnemonic: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user