]>
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 * Includes code from asmCrypto
26 * https://github.com/tresorit/asmcrypto.js
29 var Mnemonic = function(language
) {
31 var PBKDF2_ROUNDS
= 2048;
38 wordlist
= WORDLISTS
[language
];
39 if (wordlist
.length
!= RADIX
) {
40 err
= 'Wordlist should contain ' + RADIX
+ ' words, but it contains ' + wordlist
.length
+ ' words.';
45 self
.generate = function(strength
) {
46 strength
= strength
|| 128;
47 var r
= strength
% 32;
49 throw 'Strength should be divisible by 32, but it is not (' + r
+ ').';
51 var hasStrongCrypto
= 'crypto' in window
&& window
['crypto'] !== null;
52 if (!hasStrongCrypto
) {
53 throw 'Mnemonic should be generated with strong randomness, but crypto.getRandomValues is unavailable';
55 var buffer
= new Uint8Array(strength
/ 8);
56 var data
= crypto
.getRandomValues(buffer
);
57 return self
.toMnemonic(data
);
60 self
.toMnemonic = function(data
) {
61 if (data
.length
% 4 > 0) {
62 throw 'Data length in bits should be divisible by 32, but it is not (' + data
.length
+ ' bytes = ' + data
.length
*8 + ' bits).'
65 //h = hashlib.sha256(data).hexdigest()
66 var uintArray
= new Uint8Array(data
);
67 var h
= asmCrypto
.SHA256
.bytes(uintArray
);
69 // b is a binary string, eg '00111010101100...'
70 //b = bin(int(binascii.hexlify(data), 16))[2:].zfill(len(data) * 8) + \
71 // bin(int(h, 16))[2:].zfill(256)[:len(data) * 8 / 32]
73 // a = bin(int(binascii.hexlify(data), 16))[2:].zfill(len(data) * 8)
74 // c = bin(int(h, 16))[2:].zfill(256)
75 // d = c[:len(data) * 8 / 32]
76 var a
= byteArrayToBinaryString(data
);
77 var c
= byteArrayToBinaryString(h
);
78 var d
= c
.substring(0, data
.length
* 8 / 32);
83 var blen
= b
.length
/ 11;
84 for (var i
=0; i
<blen
; i
++) {
85 var idx
= parseInt(b
.substring(i
* 11, (i
+ 1) * 11), 2);
86 result
.push(wordlist
[idx
]);
88 return result
.join(' ');
91 self
.check = function(mnemonic
) {
92 var mnemonic
= mnemonic
.split(' ')
93 if (mnemonic
.length
% 3 > 0) {
96 // idx = map(lambda x: bin(self.wordlist.index(x))[2:].zfill(11), mnemonic)
98 for (var i
=0; i
<mnemonic
.length
; i
++) {
99 var word
= mnemonic
[i
];
100 var wordIndex
= wordlist
.indexOf(word
);
101 if (wordIndex
== -1) {
104 var binaryIndex
= zfill(wordIndex
.toString(2), 11);
105 idx
.push(binaryIndex
);
107 var b
= idx
.join('');
109 //d = b[:l / 33 * 32]
111 var d
= b
.substring(0, l
/ 33 * 32);
112 var h
= b
.substring(l
- l
/ 33, l
);
113 //nd = binascii.unhexlify(hex(int(d, 2))[2:].rstrip('L').zfill(l / 33 * 8))
114 //nh = bin(int(hashlib.sha256(nd).hexdigest(), 16))[2:].zfill(256)[:l / 33]
115 var nd
= binaryStringToByteArray(d
);
116 var ndHash
= asmCrypto
.SHA256
.bytes(nd
);
117 var ndBstr
= zfill(byteArrayToBinaryString(ndHash
), 256);
118 var nh
= ndBstr
.substring(0,l
/33);
122 self
.toSeed = function(mnemonic
, passphrase
) {
123 passphrase
= passphrase
|| '';
124 mnemonic
= self
.normalizeString(mnemonic
)
125 passphrase
= self
.normalizeString(passphrase
)
126 passphrase
= "mnemonic" + passphrase
;
127 //return PBKDF2(mnemonic, 'mnemonic' + passphrase, iterations=PBKDF2_ROUNDS, macmodule=hmac, digestmodule=hashlib.sha512).read(64)
128 return asmCrypto
.PBKDF2_HMAC_SHA512
.hex(mnemonic
, passphrase
, PBKDF2_ROUNDS
, 512/8);
131 self
.normalizeString = function(str
) {
132 if (typeof str
.normalize
== "function") {
133 return str
.normalize("NFKD");
136 // TODO decide how to handle this in the future.
137 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize
142 function byteArrayToBinaryString(data
) {
144 for (var i
=0; i
<data
.length
; i
++) {
145 bin
+= zfill(data
[i
].toString(2), 8);
150 function binaryStringToByteArray(str
) {
151 var arrayLen
= str
.length
/ 8;
152 var array
= new Uint8Array(arrayLen
);
153 for (var i
=0; i
<arrayLen
; i
++) {
154 var valueStr
= str
.substring(0,8);
155 var value
= parseInt(valueStr
, 2);
162 // Pad a numeric string on the left with zero digits until the given width
164 // Note this differs to the python implementation because it does not
165 // handle numbers starting with a sign.
166 function zfill(source
, length
) {
167 source
= source
.toString();
168 while (source
.length
< length
) {
169 source
= '0' + source
;