Last active
May 18, 2018 12:03
-
-
Save davidhq/71a5b92d03b91ef2c9e794ad6c04b57f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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