助记词

廖雪峰
资深软件开发工程师,业余马拉松选手。

重要提示

此区块链教程为技术教程,教程的所有内容均不构成任何投资比特币或其他数字货币的意见和建议,也不赞成个人炒作任何数字货币!

从HD钱包的创建方式可知,要创建一个HD钱包,我们必须首先有一个确定的512bit(64字节)的随机数种子。

如果用电脑生成一个64字节的随机数作为种子当然是可以的,但是恐怕谁也记不住。

如果自己想一个句子,例如bitcoin is awesome,然后计算SHA-512获得这个64字节的种子,虽然是可行的,但是其安全性取决于自己想的句子到底有多随机。像bitcoin is awesome本质上就是3个英文单词构成的随机数,长度太短,所以安全性非常差。

为了解决初始化种子的易用性问题,BIP-39规范提出了一种通过助记词来推算种子的算法:

以英文单词为例,首先,挑选2048个常用的英文单词,构造一个数组:

const words = ['abandon', 'ability', 'able', ..., 'zoo'];

然后,生成128~256位随机数,注意随机数的总位数必须是32的倍数。例如,生成的256位随机数以16进制表示为:

179e5af5ef66e5da5049cd3de0258c5339a722094e0fdbbbe0e96f148ae80924

在随机数末尾加上校验码,校验码取SHA-256的前若干位,并使得总位数凑成11的倍数。上述随机数校验码的二进制表示为00010000

将随机数+校验码按每11 bit一组,得到范围是0~2047的24个整数,把这24个整数作为索引,就得到了最多24个助记词,例如:

bleak version runway tell hour unfold donkey defy digital abuse glide please omit much cement sea sweet tenant demise taste emerge inject cause link

由于在生成助记词的过程中引入了校验码,所以,助记词如果弄错了,软件可以提示用户输入的助记词可能不对。

生成助记词的过程是计算机随机产生的,用户只要记住这些助记词,就可以根据助记词推算出HD钱包的种子。

警告

千万不要自己挑选助记词,原因一是随机性太差,二是缺少校验。

生成助记词可以使用bip39这个JavaScript库:

const bip39 = require('bip39');

let words = bip39.generateMnemonic(256);
console.log(words);

console.log('is valid mnemonic? ' + bip39.validateMnemonic(words));

运行上述代码,每次都会得到随机生成的不同的助记词。

如果想用中文作助记词也是可以的,给generateMnemonic()传入一个中文助记词数组即可:

const bip39 = require('bip39');

// 第二个参数rng可以为null:
let words = bip39.generateMnemonic(256, null, bip39.wordlists.chinese_simplified);
console.log(words);

注意:同样索引的中文和英文生成的HD种子是不同的。各种语言的助记词定义在bip-0039-wordlists.md

根据助记词推算种子

根据助记词推算种子的算法是PBKDF2,使用的哈希函数是Hmac-SHA512,其中,输入是助记词的UTF-8编码,并设置Key为mnemonic+用户口令,循环2048次,得到最终的64字节种子。上述助记词加上口令bitcoin得到的HD种子是:

b59a8078d4ac5c05b0c92b775b96a466cd136664bfe14c1d49aff3ccc94d52dfb1d59ee628426192eff5535d6058cb64317ef2992c8b124d0f72af81c9ebfaaa

该种子即为HD钱包的种子。

助记词口令

要特别注意:用户除了需要记住助记词外,还可以额外设置一个口令。HD种子的生成依赖于助记词和口令,丢失助记词或者丢失口令(如果设置了口令的话)都将导致HD钱包丢失!

用JavaScript代码实现为:

const bip39 = require('bip39');

let words = bip39.generateMnemonic(256);
console.log(words);

let seedBuffer = bip39.mnemonicToSeed(words);
let seedAsHex = seedBuffer.toString('hex');
// or use bip39.mnemonicToSeedHex(words)
console.log(seedAsHex);

根据助记词和口令生成HD种子的方法是在mnemonicToSeed()函数中传入password:

const bip39 = require('bip39');

let words = bip39.generateMnemonic(256);
console.log(words);

let password = 'bitcoin';

let seedAsHex = bip39.mnemonicToSeedHex(words, password);
console.log(seedAsHex);

从助记词算法可知,只要确定了助记词和口令,生成的HD种子就是确定的。

如果两个人的助记词相同,那么他们的HD种子也是相同的。这也意味着如果把助记词抄在纸上,一旦泄漏,HD种子就泄漏了。

如果在助记词的基础上设置了口令,那么只知道助记词,不知道口令,也是无法推算出HD种子的。

把助记词抄在纸上,口令记在脑子里,这样,泄漏了助记词也不会导致HD种子被泄漏,但要牢牢记住口令。

最后,我们使用助记词+口令的方式来生成一个HD钱包的HD种子并计算出根扩展私钥:

const
    bitcoin = require('bitcoinjs-lib'),
    bip39 = require('bip39');

let
    words = 'bleak version runway tell hour unfold donkey defy digital abuse glide please omit much cement sea sweet tenant demise taste emerge inject cause link',
    password = 'bitcoin';

// 计算seed:
let seedHex = bip39.mnemonicToSeedHex(words, password);
console.log('seed: ' + seedHex); // b59a8078...c9ebfaaa

// 生成root:
let root = bitcoin.HDNode.fromSeedHex(seedHex);
console.log('xprv: ' + root.toBase58()); // xprv9s21ZrQH...uLgyr9kF
console.log('xpub: ' + root.neutered().toBase58()); // xpub661MyMwA...oy32fcRG

// 生成派生key:
let child0 = root.derivePath("m/44'/0'/0'/0/0");
console.log("prv m/44'/0'/0'/0/0: " + child0.keyPair.toWIF()); // KzuPk3PXKdnd6QwLqUCK38PrXoqJfJmACzxTaa6TFKzPJR7H7AFg
console.log("pub m/44'/0'/0'/0/0: " + child0.getAddress()); // 1PwKkrF366RdTuYsS8KWEbGxfP4bikegcS

可以通过https://iancoleman.io/bip39/在线测试BIP-39并生成HD钱包。请注意,该网站仅供测试使用。生成正式使用的HD钱包必须在可信任的离线环境下操作。

小结

BIP-39规范通过使用助记词+口令来生成HD钱包的种子,用户只需记忆助记词和口令即可随时恢复HD钱包。

丢失助记词或者丢失口令均会导致HD钱包丢失。



Comments

Loading comments...