diff --git a/src/wallets/load_wallets.rs b/src/wallets/load_wallets.rs index 75def39..90851c9 100644 --- a/src/wallets/load_wallets.rs +++ b/src/wallets/load_wallets.rs @@ -37,10 +37,35 @@ impl Wallet { // Extract the encrypted private key text from the wallet image. if let Some(encrypted_text) = decode_image_and_extract_text(&wallet.private_key) { - // Decrypt the private key with the user-provided wallet key. - if let Some(decrypted_private_key) = decrypts(&encrypted_text, Some(&wallet_key)) { - // Replace the saved encrypted image payload with the decrypted private key in memory. - wallet.private_key = decrypted_private_key; + // Decrypt the private key with the user-provided wallet key. + if let Some(decrypted_private_key) = decrypts(&encrypted_text, Some(&wallet_key)) { + let regenerated_public_key = Self::regenerate_public_key(&decrypted_private_key)?; + let regenerated_public_key_hex = crate::encode(regenerated_public_key.clone()); + if regenerated_public_key_hex != wallet.public_key { + return Err("Wallet public key does not match the decrypted private key." + .to_string()); + } + + let regenerated_short_address_bytes = + Self::public_key_bytes_to_short_address_bytes(®enerated_public_key) + .ok_or_else(|| { + "Wallet public key does not satisfy this network's key rule." + .to_string() + })?; + let regenerated_short_address = + Self::bytes_to_short_address(®enerated_short_address_bytes) + .ok_or_else(|| { + "Wallet public key produced an invalid short address.".to_string() + })?; + if regenerated_short_address != wallet.short_address { + return Err( + "Wallet short address does not match the decrypted private key." + .to_string(), + ); + } + + // Replace the saved encrypted image payload with the decrypted private key in memory. + wallet.private_key = decrypted_private_key; // Attach the encryption key so the full wallet can be reused by callers. let full_wallet = Wallet { diff --git a/src/wallets/structures.rs b/src/wallets/structures.rs index 76304b2..07c159f 100644 --- a/src/wallets/structures.rs +++ b/src/wallets/structures.rs @@ -15,8 +15,7 @@ pub struct Wallet { } impl Wallet { pub const FN_DSA_LOGN: u32 = 9; - pub const REQUIRED_PUBLIC_KEY_PREFIX_BYTE: u8 = 239; - pub const REQUIRED_PUBLIC_KEY_PREFIX_INDEX: usize = 1; + pub const REQUIRED_PUBLIC_KEY_HASH_BYTE: u8 = 239; pub const PUBLIC_KEY_LENGTH: usize = 897; pub const PRIVATE_KEY_LENGTH: usize = 1281; pub const SIGNATURE_LENGTH: usize = 666; diff --git a/src/wallets/verify_address.rs b/src/wallets/verify_address.rs index 1504087..b53cfa6 100644 --- a/src/wallets/verify_address.rs +++ b/src/wallets/verify_address.rs @@ -1,3 +1,5 @@ +use crate::common::skein::skein_256_hash_bytes; +use crate::decode; use crate::wallets::structures::Wallet; impl Wallet { @@ -12,11 +14,16 @@ impl Wallet { return false; } - // The second public-key byte is chain-specific entropy. This restores - // the old key-grinding rule so deterministic seed-derived keys cannot - // be imported or registered unless they happen to match the network rule. - public_key_bytes[Self::REQUIRED_PUBLIC_KEY_PREFIX_INDEX] - == Self::REQUIRED_PUBLIC_KEY_PREFIX_BYTE + // The encoded Falcon key body is bit-packed and some raw byte values + // cannot occur at every position. Apply the chain-specific key rule to + // a public-key hash byte instead so the target remains reachable. + let public_key_hash = match decode(skein_256_hash_bytes(public_key_bytes)) { + Ok(hash) => hash, + Err(_) => return false, + }; + public_key_hash + .first() + .is_some_and(|byte| *byte == Self::REQUIRED_PUBLIC_KEY_HASH_BYTE) } pub fn short_address_validation(short_address: &str) -> bool {