bip39


  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: 2013-09-10

 

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 / 32

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 / 32
MS = (ENT + CS) / 11
|  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, 2048, 64, '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, 2)
}

function bytesToBinary (bytes) {
  return bytes.map(function (x) {
    return lpad(x.toString(2), '0', 8)
  }).join('')
}

function deriveChecksumBits (entropyBuffer) {
  var ENT = entropyBuffer.length * 8
  var CS = ENT / 32
  var hash = createHash('sha256').update(entropyBuffer).digest()

  return bytesToBinary([].slice.call(hash)).slice(0, 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, 2048, 64, '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 % 3 !== 0) 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 === -1) throw new Error(INVALID_MNEMONIC)

    return lpad(index.toString(2), '0', 11)
  }).join('')

  // split the binary string into ENT/CS
  var dividerIndex = Math.floor(bits.length / 33) * 32
  var entropyBits = bits.slice(0, dividerIndex)
  var checksumBits = bits.slice(dividerIndex)

  // calculate the checksum and compare
  var entropyBytes = entropyBits.match(/(.{1,8})/g).map(binaryToByte)
  if (entropyBytes.length < 16) throw new Error(INVALID_ENTROPY)
  if (entropyBytes.length > 32) throw new Error(INVALID_ENTROPY)
  if (entropyBytes.length % 4 !== 0) 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 < 16) throw new TypeError(INVALID_ENTROPY)
  if (entropy.length > 32) throw new TypeError(INVALID_ENTROPY)
  if (entropy.length % 4 !== 0) throw new TypeError(INVALID_ENTROPY)

  var entropyBits = bytesToBinary([].slice.call(entropy))
  var checksumBits = deriveChecksumBits(entropy)

  var bits = entropyBits + checksumBits
  var chunks = bits.match(/(.{1,11})/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 || 128
  if (strength % 32 !== 0) throw new TypeError(INVALID_ENTROPY)
  rng = rng || randomBytes

  return entropyToMnemonic(rng(strength / 8), 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

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

 

實例測試:

npm install bip-39 --save
//+ bip39@2.5.0

 

var bip39 = require('bip39')
 
// defaults to BIP39 English word list
// uses HEX strings for entropy
var mnemonic = bip39.entropyToMnemonic('00000000000000000000000000000000')
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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM