You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-desktop/libloki/mnemonic.js

163 lines
5.5 KiB
JavaScript

const crc32 = require('buffer-crc32');
const sc_reduce32_module = require('../components/sc_reduce32/sc_reduce32');
module.exports = {
mn_encode,
mn_decode,
sc_reduce32,
};
class MnemonicError extends Error {}
function hexToUint8Array(e) {
if (e.length % 2 != 0)
throw "Hex string has invalid length!";
for (var t = new Uint8Array(e.length / 2), r = 0; r < e.length / 2; ++r)
t[r] = parseInt(e.slice(2 * r, 2 * r + 2), 16);
return t
}
function Uint8ArrayToHex(e) {
for (var t = [], r = 0; r < e.length; ++r)
t.push(("0" + e[r].toString(16)).slice(-2));
return t.join("")
}
function sc_reduce32(e) {
var t = hexToUint8Array(e);
if (32 !== t.length)
throw "Invalid input length";
var r = sc_reduce32_module._malloc(32);
sc_reduce32_module.HEAPU8.set(t, r),
sc_reduce32_module.ccall("sc_reduce32", "void", ["number"], [r]);
var o = sc_reduce32_module.HEAPU8.subarray(r, r + 32);
return sc_reduce32_module._free(r),Uint8ArrayToHex(o);
}
/*
mnemonic.js : Converts between 4-byte aligned strings and a human-readable
sequence of words. Uses 1626 common words taken from wikipedia article:
http://en.wiktionary.org/wiki/Wiktionary:Frequency_lists/Contemporary_poetry
Originally written in python special for Electrum (lightweight Bitcoin client).
This version has been reimplemented in javascript and placed in public domain.
*/
var mn_default_wordset = 'english';
function mn_get_checksum_index(words, prefix_len) {
var trimmed_words = "";
for (var i = 0; i < words.length; i++) {
trimmed_words += words[i].slice(0, prefix_len);
}
var checksum = crc32.unsigned(trimmed_words);
var index = checksum % words.length;
return index;
}
function mn_encode(str, wordset_name) {
'use strict';
wordset_name = wordset_name || mn_default_wordset;
var wordset = mn_words[wordset_name];
var out = [];
var n = wordset.words.length;
for (var j = 0; j < str.length; j += 8) {
str = str.slice(0, j) + mn_swap_endian_4byte(str.slice(j, j + 8)) + str.slice(j + 8);
}
for (var i = 0; i < str.length; i += 8) {
var x = parseInt(str.substr(i, 8), 16);
var w1 = (x % n);
var w2 = (Math.floor(x / n) + w1) % n;
var w3 = (Math.floor(Math.floor(x / n) / n) + w2) % n;
out = out.concat([wordset.words[w1], wordset.words[w2], wordset.words[w3]]);
}
if (wordset.prefix_len > 0) {
out.push(out[mn_get_checksum_index(out, wordset.prefix_len)]);
}
return out.join(' ');
}
function mn_swap_endian_4byte(str) {
'use strict';
if (str.length !== 8) throw new MnemonicError('Invalid input length: ' + str.length);
return str.slice(6, 8) + str.slice(4, 6) + str.slice(2, 4) + str.slice(0, 2);
}
function mn_decode(str, wordset_name) {
'use strict';
wordset_name = wordset_name || mn_default_wordset;
var wordset = mn_words[wordset_name];
var out = '';
var n = wordset.words.length;
var wlist = str.split(' ');
var checksum_word = '';
if (wlist.length < 12) throw new MnemonicError("You've entered too few words, please try again");
if ((wordset.prefix_len === 0 && (wlist.length % 3 !== 0)) ||
(wordset.prefix_len > 0 && (wlist.length % 3 === 2))) throw new MnemonicError("You've entered too few words, please try again");
if (wordset.prefix_len > 0 && (wlist.length % 3 === 0)) throw new MnemonicError("You seem to be missing the last word in your private key, please try again");
if (wordset.prefix_len > 0) {
// Pop checksum from mnemonic
checksum_word = wlist.pop();
}
// Decode mnemonic
for (var i = 0; i < wlist.length; i += 3) {
var w1, w2, w3;
if (wordset.prefix_len === 0) {
w1 = wordset.words.indexOf(wlist[i]);
w2 = wordset.words.indexOf(wlist[i + 1]);
w3 = wordset.words.indexOf(wlist[i + 2]);
} else {
w1 = wordset.trunc_words.indexOf(wlist[i].slice(0, wordset.prefix_len));
w2 = wordset.trunc_words.indexOf(wlist[i + 1].slice(0, wordset.prefix_len));
w3 = wordset.trunc_words.indexOf(wlist[i + 2].slice(0, wordset.prefix_len));
}
if (w1 === -1 || w2 === -1 || w3 === -1) {
throw MnemonicError("invalid word in mnemonic");
}
var x = w1 + n * (((n - w1) + w2) % n) + n * n * (((n - w2) + w3) % n);
if (x % n != w1) throw new MnemonicError('Something went wrong when decoding your private key, please try again');
out += mn_swap_endian_4byte(('0000000' + x.toString(16)).slice(-8));
}
// Verify checksum
if (wordset.prefix_len > 0) {
var index = mn_get_checksum_index(wlist, wordset.prefix_len);
var expected_checksum_word = wlist[index];
if (expected_checksum_word.slice(0, wordset.prefix_len) !== checksum_word.slice(0, wordset.prefix_len)) {
throw new MnemonicError("Your private key could not be verified, please try again");
}
}
return out;
}
var mn_words = {
'electrum': {
prefix_len: 0,
words: require('../mnemonic_languages/electrum'),
},
'english': {
prefix_len: 3,
words: require('../mnemonic_languages/english'),
},
'spanish': {
prefix_len: 4,
words: require('../mnemonic_languages/spanish'),
},
'portuguese': {
prefix_len: 4,
words: require('../mnemonic_languages/portuguese'),
},
'japanese': {
prefix_len: 3,
words: require('../mnemonic_languages/japanese'),
}
};
for (var i in mn_words) {
if (mn_words.hasOwnProperty(i)) {
if (mn_words[i].prefix_len === 0) {
continue;
}
mn_words[i].trunc_words = [];
for (var j = 0; j < mn_words[i].words.length; ++j) {
mn_words[i].trunc_words.push(mn_words[i].words[j].slice(0, mn_words[i].prefix_len));
}
}
}