BIP: 39 (助记词)
Layer: Applications
Title: Mnemonic code for generating deterministic keys
Author: Marek Palatinus <slush@satoshilabs.com>
Pavol Rusnak <stick@satoshilabs.com>
Aaron Voisine <voisine@gmail.com>
Sean Bowe <ewillbefull@gmail.com>
Comments-Summary: Unanimously Discourage for implementation
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0039
Status: Proposed
Type: Standards Track
Created: --

Abstract

This BIP describes the implementation of a mnemonic code or mnemonic sentence -- a group of easy to remember words -- for the generation of deterministic wallets.

下面就是描述助记码或词(即为了生成hd wallet而生成的一组容易记住的词)的生成

It consists of two parts: generating the mnemonic, and converting it into a binary seed. This seed can be later used to generate deterministic wallets using BIP-0032 or similar methods.

包括两部分:一是生成助记词并将其转成二进制的seed,这个能够作为bip32中的seed来生成hd wallet

Motivation

A mnemonic code or sentence is superior for human interaction compared to the handling of raw binary or hexadecimal representations of a wallet seed. The sentence could be written on paper or spoken over the telephone.

与处理钱包seed的原始二进制或十六进制表示形式相比,助记码或句子更适合于人类交互。这个句子可以写在纸上,也可以通过电话告诉对方。

This guide is meant to be a way to transport computer-generated randomness with a human readable transcription. It's not a way to process user-created sentences (also known as brainwallets) into a wallet seed.

本指南旨在通过人类可读的转换来传输计算机生成的随机数。

Generating the mnemonic

The mnemonic must encode entropy in a multiple of 32 bits. With more entropy security is improved but the sentence length increases. We refer to the initial entropy length as ENT. The allowed size of ENT is 128-256 bits.

助记词必须以32位的倍数选择熵值entropy。随着熵值的增加,句子长度增加,安全性提高。我们将初始熵长度称为ENT。ENT的允许大小是128-256位。

ENT / 

First, an initial entropy of ENT bits is generated. A checksum is generated by taking the first bits of its SHA256 hash. This checksum is appended to the end of the initial entropy. Next, these concatenated bits are split into groups of 11 bits, each encoding a number from 0-2047, serving as an index into a wordlist. Finally, we convert these numbers into words and use the joined words as a mnemonic sentence.

(1)首先,生成ENT比特的初始熵entropy(如下面的例子00000000000000000000000000000000,16进制,熵长度为32*4=128)。

(2)通过对初始熵entropy取SHA256散列来获得CS位 (CS= 熵长度/32=4,取得到的SHA256散列的前CS位)校验和,然后将校验和附加到初始熵的末尾。

(3)接下来,(熵entropy+校验和)被分成以11位为一组(一共MS组),每个组编码对应一个0-2047的数字,该数字作为一个索引到wordlist,对应获得wordlist上相应索引的值。

(4)最后,我们将这些数字转换成单词,最终合在一起作为助记句。

The following table describes the relation between the initial entropy length (ENT), the checksum length (CS) and the length of the generated mnemonic sentence (MS) in words.

下表描述了单词中初始熵长度(ENT)、校验和长度(CS)和生成的助记句长度(MS)之间的关系。

CS = ENT /
MS = (ENT + CS) /
|  ENT  | CS | ENT+CS |  MS  |
+-------+----+--------+------+
| 128 | 4 | 132 | 12 |
| 160 | 5 | 165 | 15 |
| 192 | 6 | 198 | 18 |
| 224 | 7 | 231 | 21 |
| 256 | 8 | 264 | 24 |

Wordlist

An ideal wordlist has the following characteristics:

一个理想的单词表有以下特点:

a) smart selection of words聪明的词汇选择

   - the wordlist is created in such way that it's enough to type the first four
letters to unambiguously identify the word
创建单词列表的方式是这样的:只需输入前四个字母就可以清楚地识别单词

b) similar words avoided避免相似的词

   - word pairs like "build" and "built", "woman" and "women", or "quick" and "quickly"
not only make remembering the sentence difficult, but are also more error
prone and more difficult to guess
-像“build”和“build”、“woman”和“women”、或“quick”和“quick”这样的词对不仅使记忆句子变得困难,而且更容易出错,更难以猜测

c) sorted wordlists分类词库

   - the wordlist is sorted which allows for more efficient lookup of the code words
(i.e. implementations can use binary search instead of linear search)
- this also allows trie (a prefix tree) to be used, e.g. for better compression
- wordlist是有序的,这允许更有效的查找代码字
(例如,实现可以使用二进制搜索代替线性搜索)
-这也允许使用trie(一个前缀树),例如为了更好的压缩

The wordlist can contain native characters, but they must be encoded in UTF-8 using Normalization Form Compatibility Decomposition (NFKD).

wordlist可以包含本机字符,但它们必须使用规范化形式兼容分解(NFKD)以UTF-8编码。

From mnemonic to seed从助记词转成seed

A user may decide to protect their mnemonic with a passphrase. If a passphrase is not present, an empty string "" is used instead.

用户可能决定使用密码来保护他们的助记符。如果没有密码,则使用空字符串“”。

⚠️密码可以作为一个额外的安全因子来保护种子,即使助记词的备份被窃取,也可以保证钱包的安全(也要求密码拥有足够的复杂度和长度),不过另外一方面,如果我们忘记密码,那么将无法恢复我们的数字资产。

To create a binary seed from the mnemonic, we use the PBKDF2 function with a mnemonic sentence (in UTF-8 NFKD) used as the password and the string "mnemonic" + passphrase (again in UTF-8 NFKD) used as the salt. The iteration count is set to 2048 and HMAC-SHA512 is used as the pseudo-random function. The length of the derived key is 512 bits (= 64 bytes).

(1)为了从助记符创建二进制种子,我们使用PBKDF2函数(密钥拉伸(Key stretching)函数),使用助记词(UTF-8 NFKD)作为密码,使用字符串“助记词”+密码(UTF-8 NFKD)作为salt。迭代计数设置为2048(即重复运算2048次),使用hma - sha512作为伪随机函数。派生键的长度是512位(= 64字节,即最后的seed的长度)。

pbkdf2(mnemonicBuffer, saltBuffer, , , 'sha512')

This seed can be later used to generate deterministic wallets using BIP-0032 or similar methods.

这个seed之后将被bip32或相似的方法使用来生成hd wallet

The conversion of the mnemonic sentence to a binary seed is completely independent from generating the sentence. This results in rather simple code; there are no constraints on sentence structure and clients are free to implement their own wordlists or even whole sentence generators, allowing for flexibility in wordlists for typo detection or other purposes.

将助记句转换为二进制种子句与生成句子完全无关。这导致了相当简单的代码;句子结构没有限制,客户机可以自由地实现自己的单词列表,甚至可以实现整个句子生成器,这允许在单词列表中灵活地进行类型检测或其他目的。

Although using a mnemonic not generated by the algorithm described in "Generating the mnemonic" section is possible, this is not advised and software must compute a checksum for the mnemonic sentence using a wordlist and issue a warning if it is invalid.

虽然使用不是由“生成助记符”部分中描述的算法生成的助记符是可能的,但不建议这样做,软件必须使用wordlist计算助记符句子的校验和,并在其无效时发出警告。

The described method also provides plausible deniability, because every passphrase generates a valid seed (and thus a deterministic wallet) but only the correct one will make the desired wallet available.

所描述的方法还提供了可信的可否认性,因为每个密码都生成一个有效的种子(从而产生一个hd wallet),但是只有正确的一个才能使所需的钱包可用。

实现代码:

BIP39标准就是为了解决助记词的需求,通过随机生成12~24个容易记住的单词,单词序列通过PBKDF2与HMAC-SHA512函数创建出随机种子作为BIP32的种子。

bip39/index.js

var Buffer = require('safe-buffer').Buffer
var createHash = require('create-hash')
var pbkdf2 = require('pbkdf2').pbkdf2Sync
var randomBytes = require('randombytes') // use unorm until String.prototype.normalize gets better browser support
var unorm = require('unorm') var CHINESE_SIMPLIFIED_WORDLIST = require('./wordlists/chinese_simplified.json')
var CHINESE_TRADITIONAL_WORDLIST = require('./wordlists/chinese_traditional.json')
var ENGLISH_WORDLIST = require('./wordlists/english.json')
var FRENCH_WORDLIST = require('./wordlists/french.json')
var ITALIAN_WORDLIST = require('./wordlists/italian.json')
var JAPANESE_WORDLIST = require('./wordlists/japanese.json')
var KOREAN_WORDLIST = require('./wordlists/korean.json')
var SPANISH_WORDLIST = require('./wordlists/spanish.json')
var DEFAULT_WORDLIST = ENGLISH_WORDLIST var INVALID_MNEMONIC = 'Invalid mnemonic'
var INVALID_ENTROPY = 'Invalid entropy'
var INVALID_CHECKSUM = 'Invalid mnemonic checksum' function lpad (str, padString, length) {
while (str.length < length) str = padString + str
return str
} function binaryToByte (bin) {
return parseInt(bin, )
} function bytesToBinary (bytes) {
return bytes.map(function (x) {
return lpad(x.toString(), '', )
}).join('')
} function deriveChecksumBits (entropyBuffer) {
var ENT = entropyBuffer.length *
var CS = ENT /
var hash = createHash('sha256').update(entropyBuffer).digest() return bytesToBinary([].slice.call(hash)).slice(, CS)
} function salt (password) {
return 'mnemonic' + (password || '')
} function mnemonicToSeed (mnemonic, password) {
var mnemonicBuffer = Buffer.from(unorm.nfkd(mnemonic), 'utf8')
var saltBuffer = Buffer.from(salt(unorm.nfkd(password)), 'utf8') return pbkdf2(mnemonicBuffer, saltBuffer, , , 'sha512')
} function mnemonicToSeedHex (mnemonic, password) {
return mnemonicToSeed(mnemonic, password).toString('hex')
} function mnemonicToEntropy (mnemonic, wordlist) {
wordlist = wordlist || DEFAULT_WORDLIST var words = unorm.nfkd(mnemonic).split(' ')
if (words.length % !== ) throw new Error(INVALID_MNEMONIC) // convert word indices to 11 bit binary strings
var bits = words.map(function (word) {
var index = wordlist.indexOf(word)
if (index === -) throw new Error(INVALID_MNEMONIC) return lpad(index.toString(), '', )
}).join('') // split the binary string into ENT/CS
var dividerIndex = Math.floor(bits.length / ) *
var entropyBits = bits.slice(, dividerIndex)
var checksumBits = bits.slice(dividerIndex) // calculate the checksum and compare
var entropyBytes = entropyBits.match(/(.{,})/g).map(binaryToByte)
if (entropyBytes.length < ) throw new Error(INVALID_ENTROPY)
if (entropyBytes.length > ) throw new Error(INVALID_ENTROPY)
if (entropyBytes.length % !== ) throw new Error(INVALID_ENTROPY) var entropy = Buffer.from(entropyBytes)
var newChecksum = deriveChecksumBits(entropy)
if (newChecksum !== checksumBits) throw new Error(INVALID_CHECKSUM) return entropy.toString('hex')
} function entropyToMnemonic (entropy, wordlist) {
if (!Buffer.isBuffer(entropy)) entropy = Buffer.from(entropy, 'hex')
wordlist = wordlist || DEFAULT_WORDLIST // 128 <= ENT <= 256
if (entropy.length < ) throw new TypeError(INVALID_ENTROPY)
if (entropy.length > ) throw new TypeError(INVALID_ENTROPY)
if (entropy.length % !== ) throw new TypeError(INVALID_ENTROPY) var entropyBits = bytesToBinary([].slice.call(entropy))
var checksumBits = deriveChecksumBits(entropy) var bits = entropyBits + checksumBits
var chunks = bits.match(/(.{,})/g)
var words = chunks.map(function (binary) {
var index = binaryToByte(binary)
return wordlist[index]
}) return wordlist === JAPANESE_WORDLIST ? words.join('\u3000') : words.join(' ')
} function generateMnemonic (strength, rng, wordlist) {
strength = strength ||
if (strength % !== ) throw new TypeError(INVALID_ENTROPY)
rng = rng || randomBytes return entropyToMnemonic(rng(strength / ), wordlist)
} function validateMnemonic (mnemonic, wordlist) {
try {
mnemonicToEntropy(mnemonic, wordlist)
} catch (e) {
return false
} return true
} module.exports = {
mnemonicToSeed: mnemonicToSeed,
mnemonicToSeedHex: mnemonicToSeedHex,
mnemonicToEntropy: mnemonicToEntropy,
entropyToMnemonic: entropyToMnemonic,
generateMnemonic: generateMnemonic,
validateMnemonic: validateMnemonic,
wordlists: {
EN: ENGLISH_WORDLIST,
JA: JAPANESE_WORDLIST, chinese_simplified: CHINESE_SIMPLIFIED_WORDLIST,
chinese_traditional: CHINESE_TRADITIONAL_WORDLIST,
english: ENGLISH_WORDLIST,
french: FRENCH_WORDLIST,
italian: ITALIAN_WORDLIST,
japanese: JAPANESE_WORDLIST,
korean: KOREAN_WORDLIST,
spanish: SPANISH_WORDLIST
}
}

test vector

"",//entropy
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",//mnemonic
"c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04",//seed
"xprv9s21ZrQH143K3h3fDYiay8mocZ3afhfULfb5GX8kCBdno77K4HiA15Tg23wpbeF1pLfs1c5SPmYHrEpTuuRhxMwvKDwqdKiGJS9XFKzUsAF"//root key

实例测试:

npm install bip- --save
//+ bip39@2.5.0
var bip39 = require('bip39')

// defaults to BIP39 English word list
// uses HEX strings for entropy
var mnemonic = bip39.entropyToMnemonic('')
console.log(mnemonic)
// => abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about // reversible
console.log(bip39.mnemonicToEntropy(mnemonic))
// => '00000000000000000000000000000000' // Generate a random mnemonic (uses crypto.randomBytes under the hood), defaults to 128-bits of entropy
// var mnemonic = bip39.generateMnemonic() console.log(bip39.mnemonicToSeedHex(mnemonic))
// => '5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc19a5ac40b389cd370d086206dec8aa6c43daea6690f20ad3d8d48b2d2ce9e38e4' console.log(bip39.mnemonicToSeed(mnemonic))
// => <Buffer 5e b0 0b bd dc f0 69 08 48 89 a8 ab 91 55 56 81 65 f5 c4 53 cc b8 5e 70 81 1a ae d6 f6 da 5f c1 9a 5a c4 0b 38 9c d3 70 d0 86 20 6d ec 8a a6 c4 3d ae ... 14 more bytes> console.log(bip39.validateMnemonic(mnemonic))
// => true console.log(bip39.validateMnemonic('basket actual'))
// => false

bip39的更多相关文章

  1. 【转】干货 | 【虚拟货币钱包】从 BIP32、BIP39、BIP44 到 Ethereum HD Wallet

    虚拟货币钱包 钱包顾名思义是存放$$$.但在虚拟货币世界有点不一样,我的帐户资讯(像是我有多少钱)是储存在区块链上,实际存在钱包中的是我的帐户对应的 key.有了这把 key 我就可以在虚拟货币世界证 ...

  2. 理解开发HD 钱包涉及的 BIP32、BIP44、BIP39

    如果你还在被HD钱包.BIP32.BIP44.BIP39搞的一头雾水,来看看这边文章吧. 数字钱包概念 钱包用来存钱的,在区块链中,我们的数字资产都会对应到一个账户地址上, 只有拥有账户的钥匙(私钥) ...

  3. Ethereum HD Wallet(虚拟货币钱包)-BIP32、BIP39、BIP44

    1.使用HD钱包的好处(链接:https://www.jianshu.com/p/53405db83c16) 备份更容易 传统钱包的问题是一个钱包可能存有一堆密钥地址,每个地址都有一些比特币.这样备份 ...

  4. 1.16. BIP39协议:使用助记词生成确定性钱包

    以太坊系统学习教程: https://www.netkiller.cn/blockchain/bip39.html 1.16. BIP39协议:使用助记词生成确定性钱包 BIP:39 层:应用层 标题 ...

  5. 【虚拟货币钱包】从 BIP32、BIP39、BIP44 到 Ethereum HD Wallet

    干货 | [虚拟货币钱包]从 BIP32.BIP39.BIP44 到 Ethereum HD Wallet {{uploading-image-882090.png(uploading...)}}

  6. 非对称加密, 助记词, PIN, WIF

    一钱包 1.1非对称加密, 助记词, PIN, WIF, 地址 1.1.1 非对称加密算法 非对称加密算法, 加密与解密使用不同的KEY, 我们分别称为私钥与公钥,其中可以通过私钥生成公钥 在比特币中 ...

  7. [转]HD钱包的助记词与密钥生成原理

    本文转自:https://blog.csdn.net/opassf/article/details/79978047 区块链相关的话题持续发酵之时,应该不少人知道加密货币钱包,钱包是普通用户与加密货币 ...

  8. 以太坊钱包开发系列2 - 账号Keystore文件导入导出

    以太坊去中心化网页钱包开发系列,将从零开始开发出一个可以实际使用的钱包,本系列文章是理论与实战相结合,一共有四篇:创建钱包账号.账号Keystore文件导入导出.展示钱包信息及发起签名交易.发送Tok ...

  9. (转)以太坊 钱包 创建 导入 Keystore

    最近闲来无事 研究了下以太坊钱包 下边分享下 准备工作 : 需要用到的加密:BIP32 BIP39 BIP44 SCRYPT 加密算法 githab地址 https://github.com/Nova ...

随机推荐

  1. 全局唯一订单号生成方法(参考snowflake)

    backgroud Snowflake is a network service for generating unique ID numbers at high scale with some si ...

  2. 【转载】Nginx+Tomcat 动静分离实现负载均衡

    0.前期准备 使用Debian环境.安装Nginx(默认安装),一个web项目,安装tomcat(默认安装)等. 1.一份Nginx.conf配置文件 1 # 定义Nginx运行的用户 和 用户组 如 ...

  3. Android - 自定义控件和属性(attr和TypedArray)

    http://blog.csdn.net/zjh_1110120/article/details/50976027 1.attr format 取值类型 以ShapeView 为例 <decla ...

  4. 【代码笔记】iOS-json文件的使用

    一,工程图. 二,代码. - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the ...

  5. 【读书笔记】iOS-网络-异步请求与运行循环

    异步请求需要运行循环.当数据传递到服务器或是被客户端接收时,运行循环用于实现事件与委托对象之间的通信.异步请求在发出时,会在当前线程的运行循环上操作,这个实现细节是很重要的,因为在GCD块中或者是通过 ...

  6. 微信小程序< 3 > ~ 微信小程序开源项目合集

    简介 移动开发者想学习微信小程序需要学习一点HTML ,CSS和JS才能够比较快速的上手,参考自己学习Android学习过程,阅读源码是一个很好的方式,所以才收集了一些WeApp的开源项目. awes ...

  7. paste 命令

    Linux paste命令用于合并文件的列. paste指令会把每个文件以列对列的方式,一列列地加以合并. 语法: paste [-s][-d <间隔字符>][--help][--vers ...

  8. HBuilder开发iPad程序不能全屏显示的解决方法

    HBuilder开发iPad程序不能全屏显示的解决方法: targets选择HBuilder=>Deployment Info=> devices选择Universal即可

  9. springboot 学习之路 15(集成shiro)

    shiro: Apache Shiro 是 Java 的一个安全框架.功能强大,使用简单的Java安全框架,它为开发人员提供一个直观而全面的认证,授权,加密及会话管理的解决方案.   更多shiro介 ...

  10. matlab练习程序(单层感知器)

    clear all; close all; clc; %生成两组已标记数据 randn(); mu1=[ ]; S1=[ ; ; 0.4]; P1=mvnrnd(mu1,S1,); mu2=[ ]; ...