NAC_Blockchain/nac-wallet-core/src/mnemonic.rs

643 lines
19 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 助记词管理模块
//!
//! 实现BIP39助记词导入、验证、密钥派生和地址生成
use crate::WalletError;
use sha2::{Sha256, Sha512, Digest};
use std::collections::HashMap;
/// BIP39助记词语言
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Language {
/// 英语
English,
/// 简体中文
SimplifiedChinese,
/// 繁体中文
TraditionalChinese,
/// 日语
Japanese,
/// 韩语
Korean,
/// 法语
French,
/// 意大利语
Italian,
/// 西班牙语
Spanish,
}
/// 助记词长度
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MnemonicLength {
/// 12个单词 (128位熵)
Words12 = 12,
/// 15个单词 (160位熵)
Words15 = 15,
/// 18个单词 (192位熵)
Words18 = 18,
/// 21个单词 (224位熵)
Words21 = 21,
/// 24个单词 (256位熵)
Words24 = 24,
}
impl MnemonicLength {
/// 获取熵长度(位)
pub fn entropy_bits(&self) -> usize {
match self {
MnemonicLength::Words12 => 128,
MnemonicLength::Words15 => 160,
MnemonicLength::Words18 => 192,
MnemonicLength::Words21 => 224,
MnemonicLength::Words24 => 256,
}
}
/// 获取熵长度(字节)
pub fn entropy_bytes(&self) -> usize {
self.entropy_bits() / 8
}
}
/// 助记词
#[derive(Debug, Clone)]
pub struct Mnemonic {
/// 助记词单词列表
words: Vec<String>,
/// 语言
language: Language,
/// 熵
entropy: Vec<u8>,
}
impl Mnemonic {
/// 从单词列表创建助记词
pub fn from_words(words: Vec<String>, language: Language) -> Result<Self, WalletError> {
// 验证单词数量
let word_count = words.len();
let valid_counts = [12, 15, 18, 21, 24];
if !valid_counts.contains(&word_count) {
return Err(WalletError::KeyError(
format!("Invalid word count: {}, expected one of {:?}", word_count, valid_counts)
));
}
// 验证每个单词
let wordlist = get_wordlist(language);
for word in &words {
if !wordlist.contains_key(word.as_str()) {
return Err(WalletError::KeyError(
format!("Invalid word: {}", word)
));
}
}
// 将单词转换为索引
let indices: Vec<u16> = words.iter()
.map(|w| *wordlist.get(w.as_str()).unwrap())
.collect();
// 从索引恢复熵和校验和
let entropy_bits = word_count * 11;
let checksum_bits = entropy_bits / 33;
let entropy_length = (entropy_bits - checksum_bits) / 8;
let mut bits = Vec::new();
for index in indices {
for i in (0..11).rev() {
bits.push((index >> i) & 1 == 1);
}
}
// 提取熵
let mut entropy = vec![0u8; entropy_length];
for (i, bit) in bits.iter().take(entropy_length * 8).enumerate() {
if *bit {
entropy[i / 8] |= 1 << (7 - (i % 8));
}
}
// 验证校验和
let mut hasher = Sha256::new();
hasher.update(&entropy);
let hash = hasher.finalize();
let mut checksum_bits_actual = Vec::new();
for i in 0..checksum_bits {
checksum_bits_actual.push((hash[i / 8] >> (7 - (i % 8))) & 1 == 1);
}
let checksum_bits_expected: Vec<bool> = bits.iter()
.skip(entropy_length * 8)
.copied()
.collect();
if checksum_bits_actual != checksum_bits_expected {
return Err(WalletError::KeyError(
"Invalid checksum".to_string()
));
}
Ok(Mnemonic {
words,
language,
entropy,
})
}
/// 从熵创建助记词
pub fn from_entropy(entropy: &[u8], language: Language) -> Result<Self, WalletError> {
// 验证熵长度
let entropy_bits = entropy.len() * 8;
let valid_bits = [128, 160, 192, 224, 256];
if !valid_bits.contains(&entropy_bits) {
return Err(WalletError::KeyError(
format!("Invalid entropy length: {} bits", entropy_bits)
));
}
// 计算校验和
let mut hasher = Sha256::new();
hasher.update(entropy);
let hash = hasher.finalize();
let checksum_bits = entropy_bits / 32;
// 组合熵和校验和
let mut bits = Vec::new();
for byte in entropy {
for i in (0..8).rev() {
bits.push((byte >> i) & 1 == 1);
}
}
for i in 0..checksum_bits {
bits.push((hash[i / 8] >> (7 - (i % 8))) & 1 == 1);
}
// 将11位分组转换为单词索引
let wordlist_array = get_wordlist_array(language);
let mut words = Vec::new();
for chunk in bits.chunks(11) {
let mut index = 0u16;
for (i, bit) in chunk.iter().enumerate() {
if *bit {
index |= 1 << (10 - i);
}
}
words.push(wordlist_array[index as usize].to_string());
}
Ok(Mnemonic {
words,
language,
entropy: entropy.to_vec(),
})
}
/// 获取助记词单词列表
pub fn words(&self) -> &[String] {
&self.words
}
/// 获取助记词字符串
pub fn phrase(&self) -> String {
self.words.join(" ")
}
/// 获取熵
pub fn entropy(&self) -> &[u8] {
&self.entropy
}
/// 获取语言
pub fn language(&self) -> Language {
self.language
}
/// 派生种子BIP39
pub fn to_seed(&self, passphrase: &str) -> Vec<u8> {
let mnemonic = self.phrase();
let salt = format!("mnemonic{}", passphrase);
// PBKDF2-HMAC-SHA512
pbkdf2_hmac_sha512(
mnemonic.as_bytes(),
salt.as_bytes(),
2048,
64
)
}
}
/// PBKDF2-HMAC-SHA512密钥派生
fn pbkdf2_hmac_sha512(password: &[u8], salt: &[u8], iterations: usize, output_len: usize) -> Vec<u8> {
use hmac::{Hmac, Mac};
type HmacSha512 = Hmac<Sha512>;
let mut output = vec![0u8; output_len];
let hlen = 64; // SHA512输出长度
let blocks = (output_len + hlen - 1) / hlen;
for i in 1..=blocks {
let mut mac = HmacSha512::new_from_slice(password).unwrap();
mac.update(salt);
mac.update(&(i as u32).to_be_bytes());
let mut u = mac.finalize().into_bytes().to_vec();
let mut f = u.clone();
for _ in 1..iterations {
let mut mac = HmacSha512::new_from_slice(password).unwrap();
mac.update(&u);
u = mac.finalize().into_bytes().to_vec();
for (f_byte, u_byte) in f.iter_mut().zip(u.iter()) {
*f_byte ^= u_byte;
}
}
let start = (i - 1) * hlen;
let end = std::cmp::min(start + hlen, output_len);
output[start..end].copy_from_slice(&f[..end - start]);
}
output
}
/// 密钥派生路径BIP32/BIP44
#[derive(Debug, Clone)]
pub struct DerivationPath {
/// 路径组件
components: Vec<u32>,
}
impl DerivationPath {
/// 从字符串解析派生路径
/// 格式: m/44'/60'/0'/0/0
pub fn from_str(path: &str) -> Result<Self, WalletError> {
if !path.starts_with("m/") && !path.starts_with("M/") {
return Err(WalletError::KeyError(
"Derivation path must start with m/ or M/".to_string()
));
}
let parts: Vec<&str> = path[2..].split('/').collect();
let mut components = Vec::new();
for part in parts {
if part.is_empty() {
continue;
}
let (index_str, hardened) = if part.ends_with('\'') || part.ends_with('h') {
(&part[..part.len() - 1], true)
} else {
(part, false)
};
let index: u32 = index_str.parse()
.map_err(|_| WalletError::KeyError(
format!("Invalid index in derivation path: {}", part)
))?;
let component = if hardened {
index | 0x80000000
} else {
index
};
components.push(component);
}
Ok(DerivationPath { components })
}
/// 创建标准BIP44路径
/// m/44'/coin_type'/account'/change/address_index
pub fn bip44(coin_type: u32, account: u32, change: u32, address_index: u32) -> Self {
DerivationPath {
components: vec![
44 | 0x80000000, // purpose: 44' (BIP44)
coin_type | 0x80000000, // coin_type: hardened
account | 0x80000000, // account: hardened
change, // change: 0=external, 1=internal
address_index, // address_index
],
}
}
/// 获取路径组件
pub fn components(&self) -> &[u32] {
&self.components
}
/// 转换为字符串
pub fn to_string(&self) -> String {
let mut path = String::from("m");
for component in &self.components {
let hardened = component & 0x80000000 != 0;
let index = component & 0x7fffffff;
path.push('/');
path.push_str(&index.to_string());
if hardened {
path.push('\'');
}
}
path
}
}
/// 扩展密钥
#[derive(Debug, Clone)]
pub struct ExtendedKey {
/// 私钥
private_key: Vec<u8>,
/// 链码
chain_code: Vec<u8>,
/// 深度
depth: u8,
/// 父指纹
parent_fingerprint: [u8; 4],
/// 子索引
child_index: u32,
}
impl ExtendedKey {
/// 从种子创建主密钥
pub fn from_seed(seed: &[u8]) -> Result<Self, WalletError> {
use hmac::{Hmac, Mac};
type HmacSha512 = Hmac<Sha512>;
let mut mac = HmacSha512::new_from_slice(b"Bitcoin seed")
.map_err(|e| WalletError::KeyError(e.to_string()))?;
mac.update(seed);
let result = mac.finalize().into_bytes();
let private_key = result[..32].to_vec();
let chain_code = result[32..].to_vec();
Ok(ExtendedKey {
private_key,
chain_code,
depth: 0,
parent_fingerprint: [0; 4],
child_index: 0,
})
}
/// 派生子密钥
pub fn derive(&self, index: u32) -> Result<Self, WalletError> {
use hmac::{Hmac, Mac};
type HmacSha512 = Hmac<Sha512>;
let hardened = index & 0x80000000 != 0;
let mut mac = HmacSha512::new_from_slice(&self.chain_code)
.map_err(|e| WalletError::KeyError(e.to_string()))?;
if hardened {
// 硬化派生: HMAC-SHA512(Key = cpar, Data = 0x00 || ser256(kpar) || ser32(i))
mac.update(&[0]);
mac.update(&self.private_key);
} else {
// 普通派生: HMAC-SHA512(Key = cpar, Data = serP(point(kpar)) || ser32(i))
// 简化实现:使用私钥
mac.update(&self.private_key);
}
mac.update(&index.to_be_bytes());
let result = mac.finalize().into_bytes();
let child_key = result[..32].to_vec();
let child_chain = result[32..].to_vec();
// 计算父指纹(简化实现)
let mut hasher = Sha256::new();
hasher.update(&self.private_key);
let hash = hasher.finalize();
let mut parent_fingerprint = [0u8; 4];
parent_fingerprint.copy_from_slice(&hash[..4]);
Ok(ExtendedKey {
private_key: child_key,
chain_code: child_chain,
depth: self.depth + 1,
parent_fingerprint,
child_index: index,
})
}
/// 按路径派生
pub fn derive_path(&self, path: &DerivationPath) -> Result<Self, WalletError> {
let mut key = self.clone();
for &component in path.components() {
key = key.derive(component)?;
}
Ok(key)
}
/// 获取私钥
pub fn private_key(&self) -> &[u8] {
&self.private_key
}
/// 获取链码
pub fn chain_code(&self) -> &[u8] {
&self.chain_code
}
}
/// 地址生成器
pub struct AddressGenerator;
impl AddressGenerator {
/// 从扩展密钥生成地址
pub fn from_extended_key(key: &ExtendedKey) -> String {
// 使用私钥生成地址(简化实现)
let mut hasher = Sha256::new();
hasher.update(key.private_key());
let hash = hasher.finalize();
// 转换为十六进制字符串
format!("0x{}", hex::encode(&hash[..20]))
}
/// 从助记词和路径生成地址
pub fn from_mnemonic(mnemonic: &Mnemonic, path: &DerivationPath, passphrase: &str) -> Result<String, WalletError> {
let seed = mnemonic.to_seed(passphrase);
let master_key = ExtendedKey::from_seed(&seed)?;
let derived_key = master_key.derive_path(path)?;
Ok(Self::from_extended_key(&derived_key))
}
/// 批量生成地址
pub fn generate_addresses(
mnemonic: &Mnemonic,
coin_type: u32,
account: u32,
count: usize,
passphrase: &str
) -> Result<Vec<(String, DerivationPath)>, WalletError> {
let mut addresses = Vec::new();
let seed = mnemonic.to_seed(passphrase);
let master_key = ExtendedKey::from_seed(&seed)?;
for i in 0..count {
let path = DerivationPath::bip44(coin_type, account, 0, i as u32);
let derived_key = master_key.derive_path(&path)?;
let address = Self::from_extended_key(&derived_key);
addresses.push((address, path));
}
Ok(addresses)
}
}
/// 获取单词列表(单词 -> 索引)
fn get_wordlist(language: Language) -> HashMap<&'static str, u16> {
let words = get_wordlist_array(language);
words.iter()
.enumerate()
.map(|(i, &word)| (word, i as u16))
.collect()
}
/// 获取单词列表数组(索引 -> 单词)
fn get_wordlist_array(language: Language) -> &'static [&'static str] {
match language {
Language::English => &ENGLISH_WORDLIST,
Language::SimplifiedChinese => &CHINESE_SIMPLIFIED_WORDLIST,
_ => &ENGLISH_WORDLIST, // 其他语言暂时使用英语
}
}
/// 英语单词列表BIP39标准2048个单词
/// 这里只列出前20个作为示例实际应包含全部2048个单词
const ENGLISH_WORDLIST: &[&str] = &[
"abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract",
"absurd", "abuse", "access", "accident", "account", "accuse", "achieve", "acid",
"acoustic", "acquire", "across", "act",
// ... 省略其余2028个单词
// 实际使用时应包含完整的2048个BIP39英语单词
];
/// 简体中文单词列表BIP39标准2048个单词
const CHINESE_SIMPLIFIED_WORDLIST: &[&str] = &[
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
"", "", "", "",
// ... 省略其余2028个单词
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mnemonic_from_entropy() {
let entropy = vec![0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0];
let mnemonic = Mnemonic::from_entropy(&entropy, Language::English).unwrap();
assert_eq!(mnemonic.words().len(), 12);
assert_eq!(mnemonic.entropy(), &entropy);
}
#[test]
fn test_mnemonic_to_seed() {
let entropy = vec![0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0];
let mnemonic = Mnemonic::from_entropy(&entropy, Language::English).unwrap();
let seed = mnemonic.to_seed("");
assert_eq!(seed.len(), 64);
}
#[test]
fn test_derivation_path_parse() {
let path = DerivationPath::from_str("m/44'/60'/0'/0/0").unwrap();
assert_eq!(path.components().len(), 5);
assert_eq!(path.components()[0], 44 | 0x80000000);
assert_eq!(path.components()[1], 60 | 0x80000000);
assert_eq!(path.components()[4], 0);
}
#[test]
fn test_derivation_path_bip44() {
let path = DerivationPath::bip44(60, 0, 0, 0);
assert_eq!(path.components().len(), 5);
assert_eq!(path.to_string(), "m/44'/60'/0'/0/0");
}
#[test]
fn test_extended_key_from_seed() {
let seed = vec![0u8; 64];
let key = ExtendedKey::from_seed(&seed).unwrap();
assert_eq!(key.private_key().len(), 32);
assert_eq!(key.chain_code().len(), 32);
assert_eq!(key.depth, 0);
}
#[test]
fn test_extended_key_derive() {
let seed = vec![0u8; 64];
let master = ExtendedKey::from_seed(&seed).unwrap();
let child = master.derive(0).unwrap();
assert_eq!(child.depth, 1);
assert_eq!(child.child_index, 0);
}
#[test]
fn test_extended_key_derive_path() {
let seed = vec![0u8; 64];
let master = ExtendedKey::from_seed(&seed).unwrap();
let path = DerivationPath::bip44(60, 0, 0, 0);
let derived = master.derive_path(&path).unwrap();
assert_eq!(derived.depth, 5);
}
#[test]
fn test_address_from_extended_key() {
let seed = vec![0u8; 64];
let key = ExtendedKey::from_seed(&seed).unwrap();
let address = AddressGenerator::from_extended_key(&key);
assert!(address.starts_with("0x"));
assert_eq!(address.len(), 42); // 0x + 40 hex chars
}
#[test]
fn test_address_from_mnemonic() {
let entropy = vec![0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0];
let mnemonic = Mnemonic::from_entropy(&entropy, Language::English).unwrap();
let path = DerivationPath::bip44(60, 0, 0, 0);
let address = AddressGenerator::from_mnemonic(&mnemonic, &path, "").unwrap();
assert!(address.starts_with("0x"));
}
#[test]
fn test_generate_addresses() {
let entropy = vec![0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0];
let mnemonic = Mnemonic::from_entropy(&entropy, Language::English).unwrap();
let addresses = AddressGenerator::generate_addresses(&mnemonic, 60, 0, 5, "").unwrap();
assert_eq!(addresses.len(), 5);
for (address, _path) in addresses {
assert!(address.starts_with("0x"));
}
}
#[test]
fn test_invalid_word_count() {
let words = vec!["abandon".to_string(); 11]; // 无效数量
let result = Mnemonic::from_words(words, Language::English);
assert!(result.is_err());
}
#[test]
fn test_invalid_entropy_length() {
let entropy = vec![0u8; 15]; // 无效长度
let result = Mnemonic::from_entropy(&entropy, Language::English);
assert!(result.is_err());
}
}