Skip to content

Instantly share code, notes, and snippets.

@davidhq
Last active May 18, 2018 12:03
Show Gist options
  • Save davidhq/71a5b92d03b91ef2c9e794ad6c04b57f to your computer and use it in GitHub Desktop.
Save davidhq/71a5b92d03b91ef2c9e794ad6c04b57f to your computer and use it in GitHub Desktop.
const crypto = require('crypto');
function lpad(str, padString, length) {
while (str.length < length) str = padString + str;
return str;
}
// converts string '110' to integer 6
function binaryToByte(bin) {
return parseInt(bin, 2);
}
// returns string, like: '10011'
function bytesToBinary(bytes) {
return bytes
.map(function(x) {
return lpad(x.toString(2), '0', 8);
})
.join('');
}
function getChunks(password) {
// create pseudo-random 256 bits based on password
const hash = crypto
.createHash('sha256')
.update(password)
.digest();
// get actual bits as string, like: '10011000111...'
const bits = bytesToBinary([].slice.call(hash));
// separate in chunks of 5 bits (= 32 combinations)
return bits.match(/.{1,5}/g);
}
function shuffle(mnemonic, password) {
if (!password || password.trim() == '') {
return mnemonic;
}
const chunks = getChunks(password);
const words = mnemonic.split(' ');
const shuffled = [];
// Fisher–Yates shuffle:
// The algorithm effectively puts all the elements into a hat; it continually determines the next element by randomly drawing an element from the hat until no elements remain.
while (words.length > 0) {
const chunk = chunks.splice(0, 1);
const index = binaryToByte(chunk) % words.length;
shuffled.push(words.splice(index, 1));
}
return shuffled.join(' ');
}
function deshuffle(shuffled, password) {
if (!password || password.trim() == '') {
return shuffled;
}
const wordCount = shuffled.split(' ').length;
const chunks = getChunks(password)
.slice(0, wordCount)
.reverse();
const words = shuffled.split(' ').reverse();
const mnemonic = []; //new Array(words.length);
while (mnemonic.length < wordCount) {
const chunk = chunks.splice(0, 1);
const index = binaryToByte(chunk) % (mnemonic.length + 1);
mnemonic.splice(index, 0, words.splice(0, 1));
}
return mnemonic.join(' ');
}
const mnemonic =
'word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12 word13 word14 word15 word16 word17 word18 word19 word20 word21 word22 word23 word24';
const password = 'PASSWORD123';
const shuffled = shuffle(mnemonic, password);
console.log(`Mnemonic: ${mnemonic}`);
console.log(`Shuffled: ${shuffled}`);
console.log(`Password: ${password}`);
const deshuffled = deshuffle(shuffled, password);
// security info
function log(b, n) {
return Math.log(n) / Math.log(b);
}
function factorial(n) {
return n == 1 ? 1 : n * factorial(n - 1);
}
console.log(`Security: ${Math.floor(log(2, factorial(mnemonic.split(' ').length)))} bits`);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment