]>
git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/BIP39.git/blob - src/js/jsbip39.js
2 * Copyright (c) 2013 Pavol Rusnak
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 * this software and associated documentation files (the "Software"), to deal in
6 * the Software without restriction, including without limitation the rights to
7 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 * of the Software, and to permit persons to whom the Software is furnished to do
9 * so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 * Javascript port from python by Ian Coleman
25 * Requires code from sjcl
26 * https://github.com/bitwiseshiftleft/sjcl
29 var Mnemonic = function(language
) {
31 var PBKDF2_ROUNDS
= 2048;
37 var hmacSHA512 = function(key
) {
38 var hasher
= new sjcl
.misc
.hmac(key
, sjcl
.hash
.sha512
);
39 this.encrypt = function() {
40 return hasher
.encrypt
.apply(hasher
, arguments
);
45 wordlist
= WORDLISTS
[language
];
46 if (wordlist
.length
!= RADIX
) {
47 err
= 'Wordlist should contain ' + RADIX
+ ' words, but it contains ' + wordlist
.length
+ ' words.';
52 self
.generate = function(strength
) {
53 strength
= strength
|| 128;
54 var r
= strength
% 32;
56 throw 'Strength should be divisible by 32, but it is not (' + r
+ ').';
58 var hasStrongCrypto
= 'crypto' in window
&& window
['crypto'] !== null;
59 if (!hasStrongCrypto
) {
60 throw 'Mnemonic should be generated with strong randomness, but crypto.getRandomValues is unavailable';
62 var buffer
= new Uint8Array(strength
/ 8);
63 var data
= crypto
.getRandomValues(buffer
);
64 return self
.toMnemonic(data
);
67 self
.toMnemonic = function(byteArray
) {
68 if (byteArray
.length
% 4 > 0) {
69 throw 'Data length in bits should be divisible by 32, but it is not (' + byteArray
.length
+ ' bytes = ' + byteArray
.length
*8 + ' bits).'
72 //h = hashlib.sha256(data).hexdigest()
73 var data
= byteArrayToWordArray(byteArray
);
74 var hash
= sjcl
.hash
.sha256
.hash(data
);
75 var h
= sjcl
.codec
.hex
.fromBits(hash
);
77 // b is a binary string, eg '00111010101100...'
78 //b = bin(int(binascii.hexlify(data), 16))[2:].zfill(len(data) * 8) + \
79 // bin(int(h, 16))[2:].zfill(256)[:len(data) * 8 / 32]
81 // a = bin(int(binascii.hexlify(data), 16))[2:].zfill(len(data) * 8)
82 // c = bin(int(h, 16))[2:].zfill(256)
83 // d = c[:len(data) * 8 / 32]
84 var a
= byteArrayToBinaryString(byteArray
);
85 var c
= zfill(hexStringToBinaryString(h
), 256);
86 var d
= c
.substring(0, byteArray
.length
* 8 / 32);
91 var blen
= b
.length
/ 11;
92 for (var i
=0; i
<blen
; i
++) {
93 var idx
= parseInt(b
.substring(i
* 11, (i
+ 1) * 11), 2);
94 result
.push(wordlist
[idx
]);
96 return self
.joinWords(result
);
99 self
.check = function(mnemonic
) {
100 var b
= mnemonicToBinaryString(mnemonic
);
105 //d = b[:l / 33 * 32]
107 var d
= b
.substring(0, l
/ 33 * 32);
108 var h
= b
.substring(l
- l
/ 33, l
);
109 //nd = binascii.unhexlify(hex(int(d, 2))[2:].rstrip('L').zfill(l / 33 * 8))
110 var nd
= binaryStringToWordArray(d
);
111 //nh = bin(int(hashlib.sha256(nd).hexdigest(), 16))[2:].zfill(256)[:l / 33]
112 var ndHash
= sjcl
.hash
.sha256
.hash(nd
);
113 var ndHex
= sjcl
.codec
.hex
.fromBits(ndHash
);
114 var ndBstr
= zfill(hexStringToBinaryString(ndHex
), 256);
115 var nh
= ndBstr
.substring(0,l
/33);
119 self
.toRawEntropyHex = function(mnemonic
) {
120 var b
= mnemonicToBinaryString(mnemonic
);
123 var d
= b
.substring(0, b
.length
/ 33 * 32);
124 var nd
= binaryStringToWordArray(d
);
127 for (var i
=0; i
<nd
.length
; i
++) {
128 h
+= ('0000000' + nd
[i
].toString(16)).slice(-8);
133 self
.toRawEntropyBin = function(mnemonic
) {
134 var b
= mnemonicToBinaryString(mnemonic
);
135 var d
= b
.substring(0, b
.length
/ 33 * 32);
139 self
.toSeed = function(mnemonic
, passphrase
) {
140 passphrase
= passphrase
|| '';
141 mnemonic
= self
.joinWords(self
.splitWords(mnemonic
)); // removes duplicate blanks
142 var mnemonicNormalized
= self
.normalizeString(mnemonic
);
143 passphrase
= self
.normalizeString(passphrase
)
144 passphrase
= "mnemonic" + passphrase
;
145 var mnemonicBits
= sjcl
.codec
.utf8String
.toBits(mnemonicNormalized
);
146 var passphraseBits
= sjcl
.codec
.utf8String
.toBits(passphrase
);
147 var result
= sjcl
.misc
.pbkdf2(mnemonicBits
, passphraseBits
, PBKDF2_ROUNDS
, 512, hmacSHA512
);
148 var hashHex
= sjcl
.codec
.hex
.fromBits(result
);
152 self
.splitWords = function(mnemonic
) {
153 return mnemonic
.split(/\s/g).filter(function(x
) { return x
.length
; });
156 self
.joinWords = function(words
) {
157 // Set space correctly depending on the language
158 // see https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese
160 if (language
== "japanese") {
161 space
= "\u3000"; // ideographic space
163 return words
.join(space
);
166 self
.normalizeString = function(str
) {
167 return str
.normalize("NFKD");
170 function byteArrayToWordArray(data
) {
172 for (var i
=0; i
<data
.length
/4; i
++) {
174 v
+= data
[i
*4 + 0] << 8 * 3;
175 v
+= data
[i
*4 + 1] << 8 * 2;
176 v
+= data
[i
*4 + 2] << 8 * 1;
177 v
+= data
[i
*4 + 3] << 8 * 0;
183 function byteArrayToBinaryString(data
) {
185 for (var i
=0; i
<data
.length
; i
++) {
186 bin
+= zfill(data
[i
].toString(2), 8);
191 function hexStringToBinaryString(hexString
) {
193 for (var i
=0; i
<hexString
.length
; i
++) {
194 binaryString
+= zfill(parseInt(hexString
[i
], 16).toString(2),4);
199 function binaryStringToWordArray(binary
) {
200 var aLen
= binary
.length
/ 32;
202 for (var i
=0; i
<aLen
; i
++) {
203 var valueStr
= binary
.substring(0,32);
204 var value
= parseInt(valueStr
, 2);
206 binary
= binary
.slice(32);
211 function mnemonicToBinaryString(mnemonic
) {
212 var mnemonic
= self
.splitWords(mnemonic
);
213 if (mnemonic
.length
== 0 || mnemonic
.length
% 3 > 0) {
216 // idx = map(lambda x: bin(self.wordlist.index(x))[2:].zfill(11), mnemonic)
218 for (var i
=0; i
<mnemonic
.length
; i
++) {
219 var word
= mnemonic
[i
];
220 var wordIndex
= wordlist
.indexOf(word
);
221 if (wordIndex
== -1) {
224 var binaryIndex
= zfill(wordIndex
.toString(2), 11);
225 idx
.push(binaryIndex
);
230 // Pad a numeric string on the left with zero digits until the given width
232 // Note this differs to the python implementation because it does not
233 // handle numbers starting with a sign.
234 function zfill(source
, length
) {
235 source
= source
.toString();
236 while (source
.length
< length
) {
237 source
= '0' + source
;