]>
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 result
.join(' ');
99 self
.check = function(mnemonic
) {
100 var mnemonic
= mnemonic
.split(' ')
101 if (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
.normalizeString(mnemonic
).split(' ').filter(function(x
) { return x
.length
; }).join(' ');
134 passphrase
= self
.normalizeString(passphrase
)
135 passphrase
= "mnemonic" + passphrase
;
136 var mnemonicBits
= sjcl
.codec
.utf8String
.toBits(mnemonic
);
137 var passphraseBits
= sjcl
.codec
.utf8String
.toBits(passphrase
);
138 var result
= sjcl
.misc
.pbkdf2(mnemonicBits
, passphraseBits
, PBKDF2_ROUNDS
, 512, hmacSHA512
);
139 var hashHex
= sjcl
.codec
.hex
.fromBits(result
);
143 self
.normalizeString = function(str
) {
144 if (typeof str
.normalize
== "function") {
145 return str
.normalize("NFKD");
148 // TODO decide how to handle this in the future.
149 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize
154 function byteArrayToWordArray(data
) {
156 for (var i
=0; i
<data
.length
/4; i
++) {
158 v
+= data
[i
*4 + 0] << 8 * 3;
159 v
+= data
[i
*4 + 1] << 8 * 2;
160 v
+= data
[i
*4 + 2] << 8 * 1;
161 v
+= data
[i
*4 + 3] << 8 * 0;
167 function byteArrayToBinaryString(data
) {
169 for (var i
=0; i
<data
.length
; i
++) {
170 bin
+= zfill(data
[i
].toString(2), 8);
175 function hexStringToBinaryString(hexString
) {
177 for (var i
=0; i
<hexString
.length
; i
++) {
178 binaryString
+= zfill(parseInt(hexString
[i
], 16).toString(2),4);
183 function binaryStringToWordArray(binary
) {
184 var aLen
= binary
.length
/ 32;
186 for (var i
=0; i
<aLen
; i
++) {
187 var valueStr
= binary
.substring(0,32);
188 var value
= parseInt(valueStr
, 2);
190 binary
= binary
.slice(32);
195 // Pad a numeric string on the left with zero digits until the given width
197 // Note this differs to the python implementation because it does not
198 // handle numbers starting with a sign.
199 function zfill(source
, length
) {
200 source
= source
.toString();
201 while (source
.length
< length
) {
202 source
= '0' + source
;