latest
This commit is contained in:
parent
512b742c46
commit
1f89fce21b
File diff suppressed because it is too large
Load Diff
@ -223,8 +223,13 @@ func (cli *CLIInstance) VaultImport(vaultName string) error {
|
|||||||
return fmt.Errorf("failed to store long-term public key: %w", err)
|
return fmt.Errorf("failed to store long-term public key: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate public key hash
|
// Calculate public key hash from index 0 (same for all vaults with this mnemonic)
|
||||||
publicKeyHash := vault.ComputeDoubleSHA256([]byte(ltPublicKey))
|
// This is used to identify which vaults belong to the same mnemonic family
|
||||||
|
identity0, err := agehd.DeriveIdentity(mnemonic, 0)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to derive identity for index 0: %w", err)
|
||||||
|
}
|
||||||
|
publicKeyHash := vault.ComputeDoubleSHA256([]byte(identity0.Recipient().String()))
|
||||||
|
|
||||||
// Load existing metadata
|
// Load existing metadata
|
||||||
existingMetadata, err := vault.LoadVaultMetadata(cli.fs, vaultDir)
|
existingMetadata, err := vault.LoadVaultMetadata(cli.fs, vaultDir)
|
||||||
|
@ -108,8 +108,18 @@ func TestVaultWithRealFilesystem(t *testing.T) {
|
|||||||
t.Fatalf("Failed to create vault: %v", err)
|
t.Fatalf("Failed to create vault: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derive long-term key from mnemonic
|
// Load vault metadata to get its derivation index
|
||||||
ltIdentity, err := agehd.DeriveIdentity(testMnemonic, 0)
|
vaultDir, err := vlt.GetDirectory()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get vault directory: %v", err)
|
||||||
|
}
|
||||||
|
vaultMetadata, err := vault.LoadVaultMetadata(fs, vaultDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load vault metadata: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive long-term key from mnemonic using the vault's derivation index
|
||||||
|
ltIdentity, err := agehd.DeriveIdentity(testMnemonic, vaultMetadata.DerivationIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to derive long-term key: %v", err)
|
t.Fatalf("Failed to derive long-term key: %v", err)
|
||||||
}
|
}
|
||||||
@ -169,8 +179,18 @@ func TestVaultWithRealFilesystem(t *testing.T) {
|
|||||||
t.Fatalf("Failed to create vault: %v", err)
|
t.Fatalf("Failed to create vault: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derive long-term key from mnemonic for verification
|
// Load vault metadata to get its derivation index
|
||||||
ltIdentity, err := agehd.DeriveIdentity(testMnemonic, 0)
|
vaultDir, err := vlt.GetDirectory()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get vault directory: %v", err)
|
||||||
|
}
|
||||||
|
vaultMetadata, err := vault.LoadVaultMetadata(fs, vaultDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load vault metadata: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive long-term key from mnemonic for verification using the vault's derivation index
|
||||||
|
ltIdentity, err := agehd.DeriveIdentity(testMnemonic, vaultMetadata.DerivationIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to derive long-term key: %v", err)
|
t.Fatalf("Failed to derive long-term key: %v", err)
|
||||||
}
|
}
|
||||||
@ -333,12 +353,33 @@ func TestVaultWithRealFilesystem(t *testing.T) {
|
|||||||
|
|
||||||
// Derive long-term key from mnemonic
|
// Derive long-term key from mnemonic
|
||||||
// Note: Both vaults will have different derivation indexes due to GetNextDerivationIndex
|
// Note: Both vaults will have different derivation indexes due to GetNextDerivationIndex
|
||||||
ltIdentity1, err := agehd.DeriveIdentity(testMnemonic, 0) // vault1 gets index 0
|
|
||||||
|
// Load vault1 metadata to get its derivation index
|
||||||
|
vault1Dir, err := vault1.GetDirectory()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get vault1 directory: %v", err)
|
||||||
|
}
|
||||||
|
vault1Metadata, err := vault.LoadVaultMetadata(fs, vault1Dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load vault1 metadata: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ltIdentity1, err := agehd.DeriveIdentity(testMnemonic, vault1Metadata.DerivationIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to derive long-term key for vault1: %v", err)
|
t.Fatalf("Failed to derive long-term key for vault1: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ltIdentity2, err := agehd.DeriveIdentity(testMnemonic, 1) // vault2 gets index 1
|
// Load vault2 metadata to get its derivation index
|
||||||
|
vault2Dir, err := vault2.GetDirectory()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get vault2 directory: %v", err)
|
||||||
|
}
|
||||||
|
vault2Metadata, err := vault.LoadVaultMetadata(fs, vault2Dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load vault2 metadata: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ltIdentity2, err := agehd.DeriveIdentity(testMnemonic, vault2Metadata.DerivationIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to derive long-term key for vault2: %v", err)
|
t.Fatalf("Failed to derive long-term key for vault2: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -218,7 +218,7 @@ func CreateVault(fs afero.Fs, stateDir string, name string) (*Vault, error) {
|
|||||||
return nil, fmt.Errorf("failed to get next derivation index: %w", err)
|
return nil, fmt.Errorf("failed to get next derivation index: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derive the long-term key
|
// Derive the long-term key using the actual derivation index
|
||||||
ltIdentity, err := agehd.DeriveIdentity(mnemonic, derivationIndex)
|
ltIdentity, err := agehd.DeriveIdentity(mnemonic, derivationIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to derive long-term key: %w", err)
|
return nil, fmt.Errorf("failed to derive long-term key: %w", err)
|
||||||
@ -233,6 +233,7 @@ func CreateVault(fs afero.Fs, stateDir string, name string) (*Vault, error) {
|
|||||||
secret.Debug("Wrote long-term public key", "path", ltPubKeyPath)
|
secret.Debug("Wrote long-term public key", "path", ltPubKeyPath)
|
||||||
|
|
||||||
// Compute public key hash from index 0 (same for all vaults with this mnemonic)
|
// Compute public key hash from index 0 (same for all vaults with this mnemonic)
|
||||||
|
// This is used to identify which vaults belong to the same mnemonic family
|
||||||
identity0, err := agehd.DeriveIdentity(mnemonic, 0)
|
identity0, err := agehd.DeriveIdentity(mnemonic, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to derive identity for index 0: %w", err)
|
return nil, fmt.Errorf("failed to derive identity for index 0: %w", err)
|
||||||
|
@ -5,6 +5,8 @@ import (
|
|||||||
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.eeqj.de/sneak/secret/pkg/agehd"
|
"git.eeqj.de/sneak/secret/pkg/agehd"
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
)
|
)
|
||||||
@ -200,3 +202,216 @@ func TestVaultMetadata(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPublicKeyHashConsistency(t *testing.T) {
|
||||||
|
// Use the same test mnemonic that the integration test uses
|
||||||
|
testMnemonic := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||||
|
|
||||||
|
// Derive identity from index 0 multiple times
|
||||||
|
identity1, err := agehd.DeriveIdentity(testMnemonic, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to derive first identity: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
identity2, err := agehd.DeriveIdentity(testMnemonic, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to derive second identity: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify identities are the same
|
||||||
|
if identity1.Recipient().String() != identity2.Recipient().String() {
|
||||||
|
t.Errorf("Identity derivation is not deterministic")
|
||||||
|
t.Logf("First: %s", identity1.Recipient().String())
|
||||||
|
t.Logf("Second: %s", identity2.Recipient().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute public key hashes
|
||||||
|
hash1 := ComputeDoubleSHA256([]byte(identity1.Recipient().String()))
|
||||||
|
hash2 := ComputeDoubleSHA256([]byte(identity2.Recipient().String()))
|
||||||
|
|
||||||
|
// Verify hashes are the same
|
||||||
|
if hash1 != hash2 {
|
||||||
|
t.Errorf("Public key hash computation is not deterministic")
|
||||||
|
t.Logf("First hash: %s", hash1)
|
||||||
|
t.Logf("Second hash: %s", hash2)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Test mnemonic public key hash (index 0): %s", hash1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSampleHashCalculation(t *testing.T) {
|
||||||
|
// Test with the exact mnemonic from integration test if available
|
||||||
|
// We'll also test with a few different mnemonics to make sure they produce different hashes
|
||||||
|
mnemonics := []string{
|
||||||
|
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||||
|
"legal winner thank year wave sausage worth useful legal winner thank yellow",
|
||||||
|
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong",
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, mnemonic := range mnemonics {
|
||||||
|
identity, err := agehd.DeriveIdentity(mnemonic, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to derive identity for mnemonic %d: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := ComputeDoubleSHA256([]byte(identity.Recipient().String()))
|
||||||
|
t.Logf("Mnemonic %d hash (index 0): %s", i, hash)
|
||||||
|
t.Logf(" Recipient: %s", identity.Recipient().String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWorkflowMismatch(t *testing.T) {
|
||||||
|
testMnemonic := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||||
|
|
||||||
|
// Create a temporary directory for testing
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
fs := afero.NewOsFs()
|
||||||
|
|
||||||
|
// Test Case 1: Create vault WITH mnemonic (like init command)
|
||||||
|
t.Setenv("SB_SECRET_MNEMONIC", testMnemonic)
|
||||||
|
_, err := CreateVault(fs, tempDir, "default")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create vault with mnemonic: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load metadata for vault1
|
||||||
|
vault1Dir := filepath.Join(tempDir, "vaults.d", "default")
|
||||||
|
metadata1, err := LoadVaultMetadata(fs, vault1Dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load vault1 metadata: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Vault1 (with mnemonic) - DerivationIndex: %d, PublicKeyHash: %s",
|
||||||
|
metadata1.DerivationIndex, metadata1.PublicKeyHash)
|
||||||
|
|
||||||
|
// Test Case 2: Create vault WITHOUT mnemonic, then import (like work vault)
|
||||||
|
t.Setenv("SB_SECRET_MNEMONIC", "")
|
||||||
|
_, err = CreateVault(fs, tempDir, "work")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create vault without mnemonic: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vault2Dir := filepath.Join(tempDir, "vaults.d", "work")
|
||||||
|
|
||||||
|
// Simulate the vault import process
|
||||||
|
t.Setenv("SB_SECRET_MNEMONIC", testMnemonic)
|
||||||
|
|
||||||
|
// Get the next available derivation index for this mnemonic
|
||||||
|
derivationIndex, err := GetNextDerivationIndex(fs, tempDir, testMnemonic)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get next derivation index: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Next derivation index for import: %d", derivationIndex)
|
||||||
|
|
||||||
|
// Calculate public key hash from index 0 (same as in VaultImport)
|
||||||
|
identity0, err := agehd.DeriveIdentity(testMnemonic, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to derive identity for index 0: %v", err)
|
||||||
|
}
|
||||||
|
publicKeyHash := ComputeDoubleSHA256([]byte(identity0.Recipient().String()))
|
||||||
|
|
||||||
|
// Load existing metadata and update it (same as in VaultImport)
|
||||||
|
existingMetadata, err := LoadVaultMetadata(fs, vault2Dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load existing metadata: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update metadata with new derivation info
|
||||||
|
existingMetadata.DerivationIndex = derivationIndex
|
||||||
|
existingMetadata.PublicKeyHash = publicKeyHash
|
||||||
|
|
||||||
|
if err := SaveVaultMetadata(fs, vault2Dir, existingMetadata); err != nil {
|
||||||
|
t.Fatalf("Failed to save vault metadata: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load updated metadata for vault2
|
||||||
|
metadata2, err := LoadVaultMetadata(fs, vault2Dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load vault2 metadata: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Vault2 (imported mnemonic) - DerivationIndex: %d, PublicKeyHash: %s",
|
||||||
|
metadata2.DerivationIndex, metadata2.PublicKeyHash)
|
||||||
|
|
||||||
|
// Verify that both vaults have the same public key hash
|
||||||
|
if metadata1.PublicKeyHash != metadata2.PublicKeyHash {
|
||||||
|
t.Errorf("Public key hashes don't match!")
|
||||||
|
t.Logf("Vault1 hash: %s", metadata1.PublicKeyHash)
|
||||||
|
t.Logf("Vault2 hash: %s", metadata2.PublicKeyHash)
|
||||||
|
} else {
|
||||||
|
t.Logf("SUCCESS: Both vaults have the same public key hash: %s", metadata1.PublicKeyHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReverseEngineerHash(t *testing.T) {
|
||||||
|
// This is the hash that the work vault is getting in the failing test
|
||||||
|
wrongHash := "e34a2f500e395d8934a90a99ee9311edcfffd68cb701079575e50cbac7bb9417"
|
||||||
|
correctHash := "992552b00b3879dfae461fab9a084b47784a032771c7a9accaebdde05ec7a7d1"
|
||||||
|
|
||||||
|
// Test mnemonic from integration test
|
||||||
|
testMnemonic := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||||
|
|
||||||
|
// Calculate hash for test mnemonic
|
||||||
|
identity, err := agehd.DeriveIdentity(testMnemonic, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to derive identity: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
calculatedHash := ComputeDoubleSHA256([]byte(identity.Recipient().String()))
|
||||||
|
t.Logf("Test mnemonic hash: %s", calculatedHash)
|
||||||
|
|
||||||
|
if calculatedHash == correctHash {
|
||||||
|
t.Logf("✓ Test mnemonic produces the correct hash")
|
||||||
|
} else {
|
||||||
|
t.Errorf("✗ Test mnemonic does not produce the correct hash")
|
||||||
|
}
|
||||||
|
|
||||||
|
if calculatedHash == wrongHash {
|
||||||
|
t.Logf("✗ Test mnemonic unexpectedly produces the wrong hash")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's try some other possibilities - maybe there's a string normalization issue?
|
||||||
|
variations := []string{
|
||||||
|
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||||
|
" abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about ",
|
||||||
|
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about\n",
|
||||||
|
strings.TrimSpace("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, variation := range variations {
|
||||||
|
identity, err := agehd.DeriveIdentity(variation, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Variation %d failed: %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := ComputeDoubleSHA256([]byte(identity.Recipient().String()))
|
||||||
|
t.Logf("Variation %d hash: %s", i, hash)
|
||||||
|
|
||||||
|
if hash == wrongHash {
|
||||||
|
t.Logf("✗ Found variation that produces wrong hash: '%s'", variation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maybe let's try an empty mnemonic or something else?
|
||||||
|
emptyMnemonics := []string{
|
||||||
|
"",
|
||||||
|
" ",
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, emptyMnemonic := range emptyMnemonics {
|
||||||
|
identity, err := agehd.DeriveIdentity(emptyMnemonic, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Empty mnemonic %d failed (expected): %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := ComputeDoubleSHA256([]byte(identity.Recipient().String()))
|
||||||
|
t.Logf("Empty mnemonic %d hash: %s", i, hash)
|
||||||
|
|
||||||
|
if hash == wrongHash {
|
||||||
|
t.Logf("✗ Empty mnemonic produces wrong hash!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user