如何生成助记词?
解读
在国内 Rust 岗位面试中,“助记词”通常不是自然语言提示,而是符合 BIP-39 标准的 12/15/18/21/24 个英文单词序列,用于确定性钱包的种子熵备份。面试官想考察的是:
- 你是否理解熵→助记词→种子→私钥的完整派生链路;
- 能否用 Rust 安全、零依赖泄露地实现该流程;
- 是否掌握国密合规与内存安全的边界(国内项目常要求 SM2/SM3 与国密随机源)。
回答时务必强调不引入第三方热钱包代码、不打印日志、清零内存,并给出可审计的 Cargo 特征开关。
知识点
- BIP-39:熵长度与校验位关系(128+4、160+5…256+8)。
- Rust 密码学库选型:tiny-bip39(纯 Rust,no_std 支持) vs bip39(带零化)。
- 随机源:
- std 环境用
rand::rngs::OsRng(封装了 Linuxgetrandom系统调用,国密验收认可); - no_std 环境需对接国密随机数芯片或TrustZone TA。
- std 环境用
- 内存安全:
- 使用
zeroize::Zeroize在 Drop 时清零熵数组; - 禁止
format!打印助记词; - 使用
secrecy::{Secret, ExposeSecret}封装敏感数据。
- 使用
- 国际化:国内硬件钱包常要求简体中文词表,需验证GB 18030 编码无乱码且与英文索引一一对应。
- 单元测试:必须包含已知向量测试(BIP-39 官方测试向量)与国密随机源单测(
gmssl-randcrate 的mock_randfeature)。
答案
use bip39::{Mnemonic, Language, MnemonicType};
use rand::rngs::OsRng;
use zeroize::Zeroize;
/// 生成符合国密验收的 24 词助记词
/// 返回 Mnemonic,调用者负责不落盘、不打印
pub fn generate_mnemonic() -> Mnemonic {
// 256-bit 熵,对应 24 词
let mut entropy = [0u8; 32];
OsRng.fill_bytes(&mut entropy);
let mnemonic = Mnemonic::from_entropy(&entropy, Language::English)
.expect("entropy length valid");
entropy.zeroize(); // 立即清零熵
mnemonic
}
/// 使用示例(仅演示,生产代码禁止 `println!`)
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vector_24() {
let m = generate_mnemonic();
assert_eq!(m.word_count(), 24);
// 验证 checksum 通过
assert!(Mnemonic::from_phrase(m.phrase(), Language::English).is_ok());
}
}
关键细节
- Cargo.toml 仅启用必需 feature:
bip39 = { version = "2", default-features = false, features = ["std", "zeroize"] },避免拉入无关加密原语。 - 若对接国密随机源,替换
OsRng为gmssl_rand::GmRng,并在build.rs中检测/dev/hwrng节点是否存在,缺失时编译失败,满足等保 3 级要求。 - 交付前跑** Miri + Sanitizer** 确保无内存泄漏,审计报告需包含
zeroize汇编级清零证据。
拓展思考
- 如果面试官追问“如何防止侧信道通过功耗分析拿到助记词?”
答:在裸机环境关闭core::hint::black_box优化,使用volatile_write逐字节拷贝,并开启时钟抖动;Rust 侧用inline(never)拆分查表过程,避免词表访问地址与熵位一一对应。 - 若需求改为分层确定性钱包(BIP-44),下一步用
slip10crate 派生m/44'/60'/0'/0/0,国密场景下把hmac-sha512替换为hmac-sm3,需自实现Slip10Sm3trait 并提交密码学评估报告。 - 国内监管要求助记词不得出境,需在 TEE 内生成并加密存储于 SE 芯片。Rust 可借助
optee-teeccrate 调用 TrustZone,助记词短语仅存在于安全世界内存,Rich OS 侧仅拿到加密 blob,满足《区块链密码应用指南》第 7.2 条。