diff --git a/internal/secret/derivation_index_test.go b/internal/secret/derivation_index_test.go new file mode 100644 index 0000000..2d04edc --- /dev/null +++ b/internal/secret/derivation_index_test.go @@ -0,0 +1,68 @@ +package secret + +import ( + "encoding/json" + "testing" + "time" + + "github.com/awnumar/memguard" + "github.com/spf13/afero" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "git.eeqj.de/sneak/secret/pkg/agehd" +) + +// mockVault implements VaultInterface for testing getLongTermPrivateKey +type mockVaultForDerivation struct { + dir string + fs afero.Fs +} + +func (m *mockVaultForDerivation) GetDirectory() (string, error) { return m.dir, nil } +func (m *mockVaultForDerivation) GetName() string { return "test-vault" } +func (m *mockVaultForDerivation) GetFilesystem() afero.Fs { return m.fs } + +// Stubs for unused interface methods +func (m *mockVaultForDerivation) AddSecret(name string, value *memguard.LockedBuffer, force bool) error { + return nil +} +func (m *mockVaultForDerivation) GetCurrentUnlocker() (Unlocker, error) { return nil, nil } +func (m *mockVaultForDerivation) CreatePassphraseUnlocker(passphrase *memguard.LockedBuffer) (*PassphraseUnlocker, error) { + return nil, nil +} + +func TestGetLongTermPrivateKeyUsesVaultDerivationIndex(t *testing.T) { + testMnemonic := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" + + // Derive keys at index 0 and index 5 — they must 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(), "different derivation indices must produce different keys") + + // Set up in-memory filesystem with vault metadata at index 5 + fs := afero.NewMemMapFs() + vaultDir := "/test-vault" + require.NoError(t, fs.MkdirAll(vaultDir, 0o700)) + + metadata := VaultMetadata{ + CreatedAt: time.Now(), + DerivationIndex: 5, + } + metaBytes, err := json.Marshal(metadata) + require.NoError(t, err) + require.NoError(t, afero.WriteFile(fs, vaultDir+"/vault-metadata.json", metaBytes, 0o600)) + + // Set the mnemonic env var + t.Setenv(EnvMnemonic, testMnemonic) + + vault := &mockVaultForDerivation{dir: vaultDir, fs: fs} + result, err := getLongTermPrivateKey(fs, vault) + require.NoError(t, err) + defer result.Destroy() + + // The derived key should match index 5, NOT index 0 + assert.Equal(t, key5.String(), string(result.Bytes()), "getLongTermPrivateKey should use vault's derivation index (5), not hardcoded 0") + assert.NotEqual(t, key0.String(), string(result.Bytes()), "getLongTermPrivateKey must not use hardcoded index 0") +}