X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=src%2Fjs%2Fjsbip39.js;h=3230e3bb46f275356bf3db523d85f66f36d65f3e;hb=956e44efd75941d7b6613a947de663923d5be7b3;hp=d0ac3ae168a1bfc67f0c542aebba71d00ae4f67b;hpb=ebd8d4e8b526477edf6190ba1fb9d56a6dbcf2ae;p=perso%2FImmae%2FProjets%2FCryptomonnaies%2FBIP39.git diff --git a/src/js/jsbip39.js b/src/js/jsbip39.js index d0ac3ae..3230e3b 100644 --- a/src/js/jsbip39.js +++ b/src/js/jsbip39.js @@ -22,8 +22,8 @@ /* * Javascript port from python by Ian Coleman * - * Includes code from asmCrypto - * https://github.com/tresorit/asmcrypto.js + * Requires code from sjcl + * https://github.com/bitwiseshiftleft/sjcl */ var Mnemonic = function(language) { @@ -34,6 +34,13 @@ var Mnemonic = function(language) { var self = this; var wordlist = []; + var hmacSHA512 = function(key) { + var hasher = new sjcl.misc.hmac(key, sjcl.hash.sha512); + this.encrypt = function() { + return hasher.encrypt.apply(hasher, arguments); + }; + }; + function init() { wordlist = WORDLISTS[language]; if (wordlist.length != RADIX) { @@ -57,14 +64,15 @@ var Mnemonic = function(language) { return self.toMnemonic(data); } - self.toMnemonic = function(data) { - if (data.length % 4 > 0) { - throw 'Data length in bits should be divisible by 32, but it is not (' + data.length + ' bytes = ' + data.length*8 + ' bits).' + self.toMnemonic = function(byteArray) { + if (byteArray.length % 4 > 0) { + throw 'Data length in bits should be divisible by 32, but it is not (' + byteArray.length + ' bytes = ' + byteArray.length*8 + ' bits).' } //h = hashlib.sha256(data).hexdigest() - var uintArray = new Uint8Array(data); - var h = asmCrypto.SHA256.bytes(uintArray); + var data = byteArrayToWordArray(byteArray); + var hash = sjcl.hash.sha256.hash(data); + var h = sjcl.codec.hex.fromBits(hash); // b is a binary string, eg '00111010101100...' //b = bin(int(binascii.hexlify(data), 16))[2:].zfill(len(data) * 8) + \ @@ -73,9 +81,9 @@ var Mnemonic = function(language) { // a = bin(int(binascii.hexlify(data), 16))[2:].zfill(len(data) * 8) // c = bin(int(h, 16))[2:].zfill(256) // d = c[:len(data) * 8 / 32] - var a = byteArrayToBinaryString(data); - var c = byteArrayToBinaryString(h); - var d = c.substring(0, data.length * 8 / 32); + var a = byteArrayToBinaryString(byteArray); + var c = zfill(hexStringToBinaryString(h), 256); + var d = c.substring(0, byteArray.length * 8 / 32); // b = line1 + line2 var b = a + d; @@ -85,12 +93,12 @@ var Mnemonic = function(language) { var idx = parseInt(b.substring(i * 11, (i + 1) * 11), 2); result.push(wordlist[idx]); } - return result.join(' '); + return self.joinWords(result); } self.check = function(mnemonic) { - var mnemonic = mnemonic.split(' ') - if (mnemonic.length % 3 > 0) { + var mnemonic = self.splitWords(mnemonic); + if (mnemonic.length == 0 || mnemonic.length % 3 > 0) { return false } // idx = map(lambda x: bin(self.wordlist.index(x))[2:].zfill(11), mnemonic) @@ -111,33 +119,57 @@ var Mnemonic = function(language) { var d = b.substring(0, l / 33 * 32); var h = b.substring(l - l / 33, l); //nd = binascii.unhexlify(hex(int(d, 2))[2:].rstrip('L').zfill(l / 33 * 8)) + var nd = binaryStringToWordArray(d); //nh = bin(int(hashlib.sha256(nd).hexdigest(), 16))[2:].zfill(256)[:l / 33] - var nd = binaryStringToByteArray(d); - var ndHash = asmCrypto.SHA256.bytes(nd); - var ndBstr = zfill(byteArrayToBinaryString(ndHash), 256); + var ndHash = sjcl.hash.sha256.hash(nd); + var ndHex = sjcl.codec.hex.fromBits(ndHash); + var ndBstr = zfill(hexStringToBinaryString(ndHex), 256); var nh = ndBstr.substring(0,l/33); return h == nh; } self.toSeed = function(mnemonic, passphrase) { passphrase = passphrase || ''; - mnemonic = normalizeString(mnemonic) - passphrase = normalizeString(passphrase) + mnemonic = self.joinWords(self.splitWords(mnemonic)); // removes duplicate blanks + var mnemonicNormalized = self.normalizeString(mnemonic); + passphrase = self.normalizeString(passphrase) passphrase = "mnemonic" + passphrase; - //return PBKDF2(mnemonic, 'mnemonic' + passphrase, iterations=PBKDF2_ROUNDS, macmodule=hmac, digestmodule=hashlib.sha512).read(64) - return asmCrypto.PBKDF2_HMAC_SHA512.hex(mnemonic, passphrase, PBKDF2_ROUNDS, 512/8); + var mnemonicBits = sjcl.codec.utf8String.toBits(mnemonicNormalized); + var passphraseBits = sjcl.codec.utf8String.toBits(passphrase); + var result = sjcl.misc.pbkdf2(mnemonicBits, passphraseBits, PBKDF2_ROUNDS, 512, hmacSHA512); + var hashHex = sjcl.codec.hex.fromBits(result); + return hashHex; + } + + self.splitWords = function(mnemonic) { + return mnemonic.split(/\s/g).filter(function(x) { return x.length; }); } - function normalizeString(str) { - if (typeof str.normalize == "function") { - return str.normalize("NFKD"); + self.joinWords = function(words) { + // Set space correctly depending on the language + // see https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese + var space = " "; + if (language == "japanese") { + space = "\u3000"; // ideographic space } - else { - // TODO find a library to do this - // Not supported on firefox mobile - console.warn("NFKD Normalization is unavailable"); - return str; + return words.join(space); + } + + self.normalizeString = function(str) { + return str.normalize("NFKD"); + } + + function byteArrayToWordArray(data) { + var a = []; + for (var i=0; i