diff --git a/src/verify.rs b/src/verify.rs index 96852e3..6e2b5a6 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -3,7 +3,7 @@ //! This module provides functions to verify that a puzzle's private key //! correctly derives its stored address across multiple blockchains. -use crate::{Chain, PubkeyFormat, Puzzle}; +use crate::{Chain, Passphrase, PubkeyFormat, Puzzle}; use k256::ecdsa::SigningKey; use k256::elliptic_curve::sec1::ToEncodedPoint; use k256::PublicKey; @@ -105,7 +105,16 @@ pub fn verify_puzzle(puzzle: &Puzzle) -> Result { let path = seed.path.ok_or_else(|| { VerifyError::UnverifiableKey("Seed has no derivation path".to_string()) })?; - verify_seed(phrase, path, expected_address, pubkey_format)? + let passphrase = match seed.entropy.as_ref().and_then(|e| e.passphrase) { + Some(Passphrase::Required) => { + return Err(VerifyError::UnverifiableKey( + "Seed requires unknown passphrase".to_string(), + )) + } + Some(Passphrase::Known(p)) => p, + None => "", + }; + verify_seed(phrase, path, expected_address, pubkey_format, passphrase)? } else { return Err(VerifyError::NoPrivateKey); }; @@ -502,6 +511,7 @@ pub fn verify_seed( path: &str, expected_address: &str, pubkey_format: PubkeyFormat, + passphrase: &str, ) -> Result<(String, String), VerifyError> { use bip32::{DerivationPath, XPrv}; use bip39::Mnemonic; @@ -510,7 +520,7 @@ pub fn verify_seed( let mnemonic = Mnemonic::parse_normalized(phrase) .map_err(|e| VerifyError::InvalidKey(format!("Invalid mnemonic: {}", e)))?; - let seed = mnemonic.to_seed(""); + let seed = mnemonic.to_seed(passphrase); let derivation_path = DerivationPath::from_str(path) .map_err(|e| VerifyError::InvalidKey(format!("Invalid derivation path: {}", e)))?; diff --git a/tests/cli.rs b/tests/cli.rs index 3d36594..060d1ba 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -1098,7 +1098,7 @@ mod verify_seed { let path = "m/44'/0'/0'/0/0"; let expected = "1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA"; - let result = verify_seed(phrase, path, expected, PubkeyFormat::Compressed); + let result = verify_seed(phrase, path, expected, PubkeyFormat::Compressed, ""); assert!( result.is_ok(), "Seed verification should succeed: {:?}", @@ -1115,7 +1115,7 @@ mod verify_seed { let path = "m/84'/0'/0'/0/0"; let expected = "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu"; - let result = verify_seed(phrase, path, expected, PubkeyFormat::Compressed); + let result = verify_seed(phrase, path, expected, PubkeyFormat::Compressed, ""); assert!( result.is_ok(), "Seed verification with SegWit path should succeed: {:?}", @@ -1125,13 +1125,46 @@ mod verify_seed { assert_eq!(derived, expected); } + #[test] + fn verify_seed_with_passphrase() { + // BIP39 test vector with passphrase "TREZOR" + // Same mnemonic but passphrase changes the derived seed entirely + let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; + let path = "m/44'/0'/0'/0/0"; + + // Verify passphrase produces a different address than empty passphrase + let no_pass = "1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA"; + let result = verify_seed(phrase, path, no_pass, PubkeyFormat::Compressed, "TREZOR"); + assert!( + result.is_err(), + "TREZOR passphrase should derive a different address than empty passphrase" + ); + } + + #[test] + fn verify_seed_with_passphrase_derives_correct_address() { + // BIP39 "abandon...about" with passphrase "TREZOR" at m/44'/0'/0'/0/0 + let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; + let path = "m/44'/0'/0'/0/0"; + let expected = "1PEha8dk5Me5J1rZWpgqSt5F4BroTBLS5y"; + + let result = verify_seed(phrase, path, expected, PubkeyFormat::Compressed, "TREZOR"); + assert!( + result.is_ok(), + "Should verify correctly with TREZOR passphrase: {:?}", + result + ); + let (derived, _hex) = result.unwrap(); + assert_eq!(derived, expected); + } + #[test] fn verify_seed_invalid_phrase() { let phrase = "invalid mnemonic phrase that is not valid"; let path = "m/44'/0'/0'/0/0"; let expected = "1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA"; - let result = verify_seed(phrase, path, expected, PubkeyFormat::Compressed); + let result = verify_seed(phrase, path, expected, PubkeyFormat::Compressed, ""); assert!(result.is_err(), "Should fail with invalid mnemonic"); } @@ -1141,7 +1174,7 @@ mod verify_seed { let path = "invalid/path/format"; let expected = "1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA"; - let result = verify_seed(phrase, path, expected, PubkeyFormat::Compressed); + let result = verify_seed(phrase, path, expected, PubkeyFormat::Compressed, ""); assert!(result.is_err(), "Should fail with invalid derivation path"); } }