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