]>
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 mnemonic
= self
.splitWords(mnemonic
);
101 if (mnemonic
.length
== 0 || mnemonic
.length
% 3 > 0) {
104 // idx = map(lambda x: bin(self.wordlist.index(x))[2:].zfill(11), mnemonic)
106 for (var i
=0; i
<mnemonic
.length
; i
++) {
107 var word
= mnemonic
[i
];
108 var wordIndex
= wordlist
.indexOf(word
);
109 if (wordIndex
== -1) {
112 var binaryIndex
= zfill(wordIndex
.toString(2), 11);
113 idx
.push(binaryIndex
);
115 var b
= idx
.join('');
117 //d = b[:l / 33 * 32]
119 var d
= b
.substring(0, l
/ 33 * 32);
120 var h
= b
.substring(l
- l
/ 33, l
);
121 //nd = binascii.unhexlify(hex(int(d, 2))[2:].rstrip('L').zfill(l / 33 * 8))
122 var nd
= binaryStringToWordArray(d
);
123 //nh = bin(int(hashlib.sha256(nd).hexdigest(), 16))[2:].zfill(256)[:l / 33]
124 var ndHash
= sjcl
.hash
.sha256
.hash(nd
);
125 var ndHex
= sjcl
.codec
.hex
.fromBits(ndHash
);
126 var ndBstr
= zfill(hexStringToBinaryString(ndHex
), 256);
127 var nh
= ndBstr
.substring(0,l
/33);
131 self
.toSeed = function(mnemonic
, passphrase
) {
132 passphrase
= passphrase
|| '';
133 mnemonic
= self
.joinWords(self
.splitWords(mnemonic
)); // removes duplicate blanks
134 var mnemonicNormalized
= self
.normalizeString(mnemonic
);
135 passphrase
= self
.normalizeString(passphrase
)
136 passphrase
= "mnemonic" + passphrase
;
137 var mnemonicBits
= sjcl
.codec
.utf8String
.toBits(mnemonicNormalized
);
138 var passphraseBits
= sjcl
.codec
.utf8String
.toBits(passphrase
);
139 var result
= sjcl
.misc
.pbkdf2(mnemonicBits
, passphraseBits
, PBKDF2_ROUNDS
, 512, hmacSHA512
);
140 var hashHex
= sjcl
.codec
.hex
.fromBits(result
);
144 self
.splitWords = function(mnemonic
) {
145 return mnemonic
.split(/\s/g).filter(function(x
) { return x
.length
; });
148 self
.joinWords = function(words
) {
149 // Set space correctly depending on the language
150 // see https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese
152 if (language
== "japanese") {
153 space
= "\u3000"; // ideographic space
155 return words
.join(space
);
158 self
.normalizeString = function(str
) {
159 if (typeof str
.normalize
== "function") {
160 return str
.normalize("NFKD");
163 // TODO decide how to handle this in the future.
164 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize
169 function byteArrayToWordArray(data
) {
171 for (var i
=0; i
<data
.length
/4; i
++) {
173 v
+= data
[i
*4 + 0] << 8 * 3;
174 v
+= data
[i
*4 + 1] << 8 * 2;
175 v
+= data
[i
*4 + 2] << 8 * 1;
176 v
+= data
[i
*4 + 3] << 8 * 0;
182 function byteArrayToBinaryString(data
) {
184 for (var i
=0; i
<data
.length
; i
++) {
185 bin
+= zfill(data
[i
].toString(2), 8);
190 function hexStringToBinaryString(hexString
) {
192 for (var i
=0; i
<hexString
.length
; i
++) {
193 binaryString
+= zfill(parseInt(hexString
[i
], 16).toString(2),4);
198 function binaryStringToWordArray(binary
) {
199 var aLen
= binary
.length
/ 32;
201 for (var i
=0; i
<aLen
; i
++) {
202 var valueStr
= binary
.substring(0,32);
203 var value
= parseInt(valueStr
, 2);
205 binary
= binary
.slice(32);
210 // Pad a numeric string on the left with zero digits until the given width
212 // Note this differs to the python implementation because it does not
213 // handle numbers starting with a sign.
214 function zfill(source
, length
) {
215 source
= source
.toString();
216 while (source
.length
< length
) {
217 source
= '0' + source
;