Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -105,7 +105,16 @@ pub fn verify_puzzle(puzzle: &Puzzle) -> Result<VerifyResult, VerifyError> {
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);
};
Expand Down Expand Up @@ -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;
Expand All @@ -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)))?;
Expand Down
41 changes: 37 additions & 4 deletions tests/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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: {:?}",
Expand All @@ -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: {:?}",
Expand All @@ -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"
);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

#[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");
}

Expand All @@ -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");
}
}
Loading