mirror of
https://github.com/OneKeyHQ/bip39.git
synced 2026-04-05 18:43:47 +00:00
Entropy can be supplied by user
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -65,12 +65,14 @@
|
||||
<div class="col-md-12">
|
||||
<h2>Mnemonic</h2>
|
||||
<form class="form-horizontal" role="form">
|
||||
<div class="col-sm-2"></div>
|
||||
<div class="col-sm-10">
|
||||
<p>You can enter an existing BIP39 mnemonic, or generate a new random one. Typing your own twelve words will probably not work how you expect, since the words require a particular structure (the last word is a checksum)</p>
|
||||
<p>For more info see the <a href="https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki" target="_blank">BIP39 spec</a></p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-2"></div>
|
||||
<div class="col-sm-10">
|
||||
<p>You can enter an existing BIP39 mnemonic, or generate a new random one. Typing your own twelve words will probably not work how you expect, since the words require a particular structure (the last word is a checksum)</p>
|
||||
<p>For more info see the <a href="https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki" target="_blank">BIP39 spec</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group generate-container">
|
||||
<label class="col-sm-2 control-label"></label>
|
||||
<div class="col-sm-10">
|
||||
<div class="form-inline">
|
||||
@@ -92,7 +94,30 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="entropy-container hidden">
|
||||
<label for="entropy" class="col-sm-2 control-label">Entropy</label>
|
||||
<div class="col-sm-10">
|
||||
<input id="entropy" class="entropy form-control" placeholder="Accepts binary, base 6, 6-sided dice, base 10, hexadecimal">
|
||||
<span class="help-block">
|
||||
<div class="text-danger">
|
||||
This is an advanced feature.
|
||||
Your mnemonic may be insecure if this feature is used incorrectly.
|
||||
<a href="#entropy-notes">Read more</a>
|
||||
</div>
|
||||
<div class="text-danger entropy-error"></div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-2"></div>
|
||||
<div class="col-sm-10 checkbox">
|
||||
<label>
|
||||
<input type="checkbox" class="use-entropy">
|
||||
Supply my own source of entropy
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label"></label>
|
||||
<div class="col-sm-10 languages">
|
||||
<a href="#english">English</a>
|
||||
@@ -353,6 +378,24 @@
|
||||
but be careful - it can be easy to make mistakes if you
|
||||
don't know what you're doing
|
||||
</p>
|
||||
<h3 id="entropy-notes">Entropy</h3>
|
||||
<p>
|
||||
Entropy values must be sourced from a
|
||||
<a href="https://en.wikipedia.org/wiki/Random_number_generation" target="_blank">strong source of randomness</a>.
|
||||
This means flipping a fair coin, rolling a fair dice, noise measurements etc. Do <strong>NOT</strong> use
|
||||
phrases from books, lyrics from songs, your birthday or steet address, keyboard mashing, or anything you <i>think</i>
|
||||
is random, because chances are <em>overwhelming</em> that it isn't random enough for the needs of this tool.
|
||||
</p>
|
||||
<p>
|
||||
The random mnemonic generator on this page uses a
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Web/API/RandomSource/getRandomValues" target="_blank">cryptographically secure random number generator</a>,
|
||||
and can generally be trusted more than your own intuition about randomness.
|
||||
If cryptographic randomness isn't available in your browser, this page will show a warning and <i>will not generate
|
||||
random mnemonics</i>.
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://bitcointalk.org/index.php?topic=311000.msg3345309#msg3345309" target="_blank">You are not a good source of entropy.</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -465,6 +508,7 @@
|
||||
<script src="js/wordlist_french.js"></script>
|
||||
<script src="js/wordlist_italian.js"></script>
|
||||
<script src="js/jsbip39.js"></script>
|
||||
<script src="js/entropy.js"></script>
|
||||
<script src="js/index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
1774
src/js/entropy.js
Normal file
1774
src/js/entropy.js
Normal file
File diff suppressed because it is too large
Load Diff
106
src/js/index.js
106
src/js/index.js
@@ -14,14 +14,20 @@
|
||||
var showPubKey = true;
|
||||
var showPrivKey = true;
|
||||
|
||||
var entropyChangeTimeoutEvent = null;
|
||||
var phraseChangeTimeoutEvent = null;
|
||||
var rootKeyChangedTimeoutEvent = null;
|
||||
|
||||
var DOM = {};
|
||||
DOM.network = $(".network");
|
||||
DOM.phraseNetwork = $("#network-phrase");
|
||||
DOM.useEntropy = $(".use-entropy");
|
||||
DOM.entropyContainer = $(".entropy-container");
|
||||
DOM.entropy = $(".entropy");
|
||||
DOM.entropyError = $(".entropy-error");
|
||||
DOM.phrase = $(".phrase");
|
||||
DOM.passphrase = $(".passphrase");
|
||||
DOM.generateContainer = $(".generate-container");
|
||||
DOM.generate = $(".generate");
|
||||
DOM.seed = $(".seed");
|
||||
DOM.rootKey = $(".root-key");
|
||||
@@ -53,6 +59,8 @@
|
||||
function init() {
|
||||
// Events
|
||||
DOM.network.on("change", networkChanged);
|
||||
DOM.useEntropy.on("change", setEntropyVisibility);
|
||||
DOM.entropy.on("input", delayedEntropyChanged);
|
||||
DOM.phrase.on("input", delayedPhraseChanged);
|
||||
DOM.passphrase.on("input", delayedPhraseChanged);
|
||||
DOM.generate.on("click", generateClicked);
|
||||
@@ -89,6 +97,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
function setEntropyVisibility() {
|
||||
if (isUsingOwnEntropy()) {
|
||||
DOM.entropyContainer.removeClass("hidden");
|
||||
DOM.generateContainer.addClass("hidden");
|
||||
DOM.phrase.prop("readonly", true);
|
||||
DOM.entropy.focus();
|
||||
entropyChanged();
|
||||
}
|
||||
else {
|
||||
DOM.entropyContainer.addClass("hidden");
|
||||
DOM.generateContainer.removeClass("hidden");
|
||||
DOM.phrase.prop("readonly", false);
|
||||
}
|
||||
}
|
||||
|
||||
function delayedPhraseChanged() {
|
||||
hideValidationError();
|
||||
showPending();
|
||||
@@ -116,6 +139,20 @@
|
||||
hidePending();
|
||||
}
|
||||
|
||||
function delayedEntropyChanged() {
|
||||
hideValidationError();
|
||||
showPending();
|
||||
if (entropyChangeTimeoutEvent != null) {
|
||||
clearTimeout(entropyChangeTimeoutEvent);
|
||||
}
|
||||
entropyChangeTimeoutEvent = setTimeout(entropyChanged, 400);
|
||||
}
|
||||
|
||||
function entropyChanged() {
|
||||
setMnemonicFromEntropy();
|
||||
phraseChanged();
|
||||
}
|
||||
|
||||
function delayedRootKeyChanged() {
|
||||
// Warn if there is an existing mnemonic or passphrase.
|
||||
if (DOM.phrase.val().length > 0 || DOM.passphrase.val().length > 0) {
|
||||
@@ -168,6 +205,9 @@
|
||||
}
|
||||
|
||||
function generateClicked() {
|
||||
if (isUsingOwnEntropy()) {
|
||||
return;
|
||||
}
|
||||
clearDisplay();
|
||||
showPending();
|
||||
setTimeout(function() {
|
||||
@@ -599,7 +639,12 @@
|
||||
}
|
||||
|
||||
function getLanguageFromUrl() {
|
||||
return window.location.hash.substring(1);
|
||||
for (var language in WORDLISTS) {
|
||||
if (window.location.hash.indexOf(language) > -1) {
|
||||
return language;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function setMnemonicLanguage() {
|
||||
@@ -650,6 +695,65 @@
|
||||
return phrase;
|
||||
}
|
||||
|
||||
function isUsingOwnEntropy() {
|
||||
return DOM.useEntropy.prop("checked");
|
||||
}
|
||||
|
||||
function setMnemonicFromEntropy() {
|
||||
hideEntropyError();
|
||||
// Work out minimum base for entropy
|
||||
var entropyStr = DOM.entropy.val();
|
||||
var entropy = Entropy.fromString(entropyStr);
|
||||
if (entropy.hexStr.length == 0) {
|
||||
return;
|
||||
}
|
||||
// Show entropy details
|
||||
var extraBits = 32 - (entropy.binaryStr.length % 32);
|
||||
var extraChars = Math.ceil(extraBits * Math.log(2) / Math.log(entropy.base.asInt));
|
||||
var strength = "an extremely weak";
|
||||
if (entropy.hexStr.length >= 8) {
|
||||
strength = "a very weak";
|
||||
}
|
||||
if (entropy.hexStr.length >= 12) {
|
||||
strength = "a weak";
|
||||
}
|
||||
if (entropy.hexStr.length >= 24) {
|
||||
strength = "a strong";
|
||||
}
|
||||
if (entropy.hexStr.length >= 32) {
|
||||
strength = "a very strong";
|
||||
}
|
||||
if (entropy.hexStr.length >= 40) {
|
||||
strength = "an extremely strong";
|
||||
}
|
||||
if (entropy.hexStr.length >=48) {
|
||||
strength = "an even stronger"
|
||||
}
|
||||
var msg = "Have " + entropy.binaryStr.length + " bits of entropy, " + extraChars + " more " + entropy.base.str + " chars required to generate " + strength + " mnemonic: " + entropy.cleanStr;
|
||||
showEntropyError(msg);
|
||||
// Discard trailing entropy
|
||||
var hexStr = entropy.hexStr.substring(0, Math.floor(entropy.hexStr.length / 8) * 8);
|
||||
// Convert entropy string to numeric array
|
||||
var entropyArr = [];
|
||||
for (var i=0; i<hexStr.length / 2; i++) {
|
||||
var entropyByte = parseInt(hexStr[i*2].concat(hexStr[i*2+1]), 16);
|
||||
entropyArr.push(entropyByte)
|
||||
}
|
||||
// Convert entropy array to mnemonic
|
||||
var phrase = mnemonic.toMnemonic(entropyArr);
|
||||
// Set the mnemonic in the UI
|
||||
DOM.phrase.val(phrase);
|
||||
}
|
||||
|
||||
function hideEntropyError() {
|
||||
DOM.entropyError.addClass("hidden");
|
||||
}
|
||||
|
||||
function showEntropyError(msg) {
|
||||
DOM.entropyError.text(msg);
|
||||
DOM.entropyError.removeClass("hidden");
|
||||
}
|
||||
|
||||
var networks = [
|
||||
{
|
||||
name: "Bitcoin",
|
||||
|
||||
607
tests.js
607
tests.js
@@ -1960,6 +1960,613 @@ page.open(url, function(status) {
|
||||
});
|
||||
},
|
||||
|
||||
// Entropy unit tests
|
||||
function() {
|
||||
page.open(url, function(status) {
|
||||
var error = page.evaluate(function() {
|
||||
var e;
|
||||
// binary entropy is detected
|
||||
e = Entropy.fromString("01010101");
|
||||
if (e.base.str != "binary") {
|
||||
return "Binary entropy not detected correctly";
|
||||
}
|
||||
// base6 entropy is detected
|
||||
e = Entropy.fromString("012345012345");
|
||||
if (e.base.str != "base 6") {
|
||||
return "base6 entropy not detected correctly";
|
||||
}
|
||||
// dice entropy is detected
|
||||
e = Entropy.fromString("123456123456");
|
||||
if (e.base.str != "base 6 (dice)") {
|
||||
return "dice entropy not detected correctly";
|
||||
}
|
||||
// base10 entropy is detected
|
||||
e = Entropy.fromString("0123456789");
|
||||
if (e.base.str != "base 10") {
|
||||
return "base10 entropy not detected correctly";
|
||||
}
|
||||
// hex entropy is detected
|
||||
e = Entropy.fromString("0123456789ABCDEF");
|
||||
if (e.base.str != "hexadecimal") {
|
||||
return "hexadecimal entropy not detected correctly";
|
||||
}
|
||||
// entropy is case insensitive
|
||||
e = Entropy.fromString("aBcDeF");
|
||||
if (e.cleanStr != "aBcDeF") {
|
||||
return "Entropy should not be case sensitive";
|
||||
}
|
||||
// dice entropy is converted to base6
|
||||
e = Entropy.fromString("123456");
|
||||
if (e.cleanStr != "012345") {
|
||||
return "Dice entropy is not automatically converted to base6";
|
||||
}
|
||||
// dice entropy is preferred to base6 if ambiguous
|
||||
e = Entropy.fromString("12345");
|
||||
if (e.base.str != "base 6 (dice)") {
|
||||
return "dice not used as default over base 6";
|
||||
}
|
||||
// unused characters are ignored
|
||||
e = Entropy.fromString("fghijkl");
|
||||
if (e.cleanStr != "f") {
|
||||
return "additional characters are not ignored";
|
||||
}
|
||||
// the lowest base is used by default
|
||||
// 7 could be decimal or hexadecimal, but should be detected as decimal
|
||||
e = Entropy.fromString("7");
|
||||
if (e.base.str != "base 10") {
|
||||
return "lowest base is not used";
|
||||
}
|
||||
// Hexadecimal representation is returned
|
||||
e = Entropy.fromString("1010");
|
||||
if (e.hexStr != "A") {
|
||||
return "Hexadecimal representation not returned";
|
||||
}
|
||||
// Leading zeros are retained
|
||||
e = Entropy.fromString("000A");
|
||||
if (e.cleanStr != "000A") {
|
||||
return "Leading zeros are not retained";
|
||||
}
|
||||
// Leading zeros are correctly preserved for hex in binary string
|
||||
e = Entropy.fromString("2A");
|
||||
if (e.binaryStr != "00101010") {
|
||||
return "Hex leading zeros are not correct in binary";
|
||||
}
|
||||
// Keyboard mashing results in weak entropy
|
||||
// Despite being a long string, it's less than 30 bits of entropy
|
||||
e = Entropy.fromString("aj;se ifj; ask,dfv js;ifj");
|
||||
if (e.binaryStr.length >= 30) {
|
||||
return "Keyboard mashing should produce weak entropy";
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (error) {
|
||||
console.log("Entropy unit tests");
|
||||
console.log(error);
|
||||
fail();
|
||||
};
|
||||
next();
|
||||
});
|
||||
},
|
||||
|
||||
// Entropy can be entered by the user
|
||||
function() {
|
||||
page.open(url, function(status) {
|
||||
expected = {
|
||||
mnemonic: "abandon abandon ability",
|
||||
address: "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug",
|
||||
}
|
||||
// use entropy
|
||||
page.evaluate(function() {
|
||||
$(".use-entropy").prop("checked", true).trigger("change");
|
||||
$(".entropy").val("00000000 00000000 00000000 00000000").trigger("input");
|
||||
});
|
||||
// check the mnemonic is set and address is correct
|
||||
waitForGenerate(function() {
|
||||
var actual = page.evaluate(function() {
|
||||
return {
|
||||
address: $(".address:first").text(),
|
||||
mnemonic: $(".phrase").val(),
|
||||
}
|
||||
});
|
||||
if (actual.mnemonic != expected.mnemonic) {
|
||||
console.log("Entropy does not generate correct mnemonic");
|
||||
console.log("Expected: " + expected.mnemonic);
|
||||
console.log("Got: " + actual.mnemonic);
|
||||
fail();
|
||||
}
|
||||
if (actual.address != expected.address) {
|
||||
console.log("Entropy does not generate correct address");
|
||||
console.log("Expected: " + expected.address);
|
||||
console.log("Got: " + actual.address);
|
||||
fail();
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// A warning about entropy is shown to the user, with additional information
|
||||
function() {
|
||||
page.open(url, function(status) {
|
||||
// get text content from entropy sections of page
|
||||
var hasWarning = page.evaluate(function() {
|
||||
var entropyText = $(".entropy-container").text();
|
||||
var warning = "mnemonic may be insecure";
|
||||
if (entropyText.indexOf(warning) == -1) {
|
||||
return false;
|
||||
}
|
||||
var readMoreText = $("#entropy-notes").parent().text();
|
||||
var goodSources = "flipping a fair coin, rolling a fair dice, noise measurements etc";
|
||||
if (readMoreText.indexOf(goodSources) == -1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
// check the warnings and information are shown
|
||||
if (!hasWarning) {
|
||||
console.log("Page does not contain warning about using own entropy");
|
||||
fail();
|
||||
}
|
||||
next();
|
||||
});
|
||||
},
|
||||
|
||||
// The types of entropy available are described to the user
|
||||
function() {
|
||||
page.open(url, function(status) {
|
||||
// get placeholder text for entropy field
|
||||
var placeholder = page.evaluate(function() {
|
||||
return $(".entropy").attr("placeholder");
|
||||
});
|
||||
var options = [
|
||||
"binary",
|
||||
"base 6",
|
||||
"dice",
|
||||
"base 10",
|
||||
"hexadecimal",
|
||||
];
|
||||
for (var i=0; i<options.length; i++) {
|
||||
var option = options[i];
|
||||
if (placeholder.indexOf(option) == -1) {
|
||||
console.log("Available entropy type is not shown to user: " + option);
|
||||
fail();
|
||||
}
|
||||
}
|
||||
next();
|
||||
});
|
||||
},
|
||||
|
||||
// The actual entropy used is shown to the user
|
||||
function() {
|
||||
page.open(url, function(status) {
|
||||
// use entropy
|
||||
var badEntropySource = page.evaluate(function() {
|
||||
var entropy = "Not A Very Good Entropy Source At All";
|
||||
$(".use-entropy").prop("checked", true).trigger("change");
|
||||
$(".entropy").val(entropy).trigger("input");
|
||||
});
|
||||
// check the actual entropy being used is shown
|
||||
waitForGenerate(function() {
|
||||
var expectedText = "AedEceAA";
|
||||
var entropyText = page.evaluate(function() {
|
||||
return $(".entropy-container").text();
|
||||
});
|
||||
if (entropyText.indexOf(expectedText) == -1) {
|
||||
console.log("Actual entropy used is not shown");
|
||||
fail();
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Binary entropy can be entered
|
||||
function() {
|
||||
page.open(url, function(status) {
|
||||
// use entropy
|
||||
page.evaluate(function() {
|
||||
$(".use-entropy").prop("checked", true).trigger("change");
|
||||
$(".entropy").val("01").trigger("input");
|
||||
});
|
||||
// check the entropy is shown to be the correct type
|
||||
waitForGenerate(function() {
|
||||
var entropyText = page.evaluate(function() {
|
||||
return $(".entropy-container").text();
|
||||
});
|
||||
if (entropyText.indexOf("binary") == -1) {
|
||||
console.log("Binary entropy is not detected and presented to user");
|
||||
fail();
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Base 6 entropy can be entered
|
||||
function() {
|
||||
page.open(url, function(status) {
|
||||
// use entropy
|
||||
page.evaluate(function() {
|
||||
$(".use-entropy").prop("checked", true).trigger("change");
|
||||
$(".entropy").val("012345").trigger("input");
|
||||
});
|
||||
// check the entropy is shown to be the correct type
|
||||
waitForGenerate(function() {
|
||||
var entropyText = page.evaluate(function() {
|
||||
return $(".entropy-container").text();
|
||||
});
|
||||
if (entropyText.indexOf("base 6") == -1) {
|
||||
console.log("Base 6 entropy is not detected and presented to user");
|
||||
fail();
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Base 6 dice entropy can be entered
|
||||
function() {
|
||||
page.open(url, function(status) {
|
||||
// use entropy
|
||||
page.evaluate(function() {
|
||||
$(".use-entropy").prop("checked", true).trigger("change");
|
||||
$(".entropy").val("123456").trigger("input");
|
||||
});
|
||||
// check the entropy is shown to be the correct type
|
||||
waitForGenerate(function() {
|
||||
var entropyText = page.evaluate(function() {
|
||||
return $(".entropy-container").text();
|
||||
});
|
||||
if (entropyText.indexOf("dice") == -1) {
|
||||
console.log("Dice entropy is not detected and presented to user");
|
||||
fail();
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Base 10 entropy can be entered
|
||||
function() {
|
||||
page.open(url, function(status) {
|
||||
// use entropy
|
||||
page.evaluate(function() {
|
||||
$(".use-entropy").prop("checked", true).trigger("change");
|
||||
$(".entropy").val("789").trigger("input");
|
||||
});
|
||||
// check the entropy is shown to be the correct type
|
||||
waitForGenerate(function() {
|
||||
var entropyText = page.evaluate(function() {
|
||||
return $(".entropy-container").text();
|
||||
});
|
||||
if (entropyText.indexOf("base 10") == -1) {
|
||||
console.log("Base 10 entropy is not detected and presented to user");
|
||||
fail();
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Hexadecimal entropy can be entered
|
||||
function() {
|
||||
page.open(url, function(status) {
|
||||
// use entropy
|
||||
page.evaluate(function() {
|
||||
$(".use-entropy").prop("checked", true).trigger("change");
|
||||
$(".entropy").val("abcdef").trigger("input");
|
||||
});
|
||||
// check the entropy is shown to be the correct type
|
||||
waitForGenerate(function() {
|
||||
var entropyText = page.evaluate(function() {
|
||||
return $(".entropy-container").text();
|
||||
});
|
||||
if (entropyText.indexOf("hexadecimal") == -1) {
|
||||
console.log("Hexadecimal entropy is not detected and presented to user");
|
||||
fail();
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Dice entropy value is shown as the converted base 6 value
|
||||
function() {
|
||||
page.open(url, function(status) {
|
||||
// use entropy
|
||||
page.evaluate(function() {
|
||||
$(".use-entropy").prop("checked", true).trigger("change");
|
||||
$(".entropy").val("123456").trigger("input");
|
||||
});
|
||||
// check the entropy is shown as base 6, not as the original dice value
|
||||
waitForGenerate(function() {
|
||||
var entropyText = page.evaluate(function() {
|
||||
return $(".entropy-container").text();
|
||||
});
|
||||
if (entropyText.indexOf("012345") == -1) {
|
||||
console.log("Dice entropy is not shown to user as base 6 value");
|
||||
fail();
|
||||
}
|
||||
if (entropyText.indexOf("123456") > -1) {
|
||||
console.log("Dice entropy value is shown instead of true base 6 value");
|
||||
fail();
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// The number of bits of entropy accumulated is shown
|
||||
function() {
|
||||
page.open(url, function(status) {
|
||||
var tests = {
|
||||
"0000 0000 0000 0000 0000": "20",
|
||||
"0": "1",
|
||||
"0000": "4",
|
||||
"6": "3",
|
||||
"7": "3",
|
||||
"8": "4",
|
||||
"F": "4",
|
||||
"29": "5",
|
||||
"0A": "8",
|
||||
"1A": "8", // hex is always multiple of 4 bits of entropy
|
||||
"2A": "8",
|
||||
"4A": "8",
|
||||
"8A": "8",
|
||||
"FA": "8",
|
||||
"000A": "16",
|
||||
"2220": "10",
|
||||
"2221": "9", // uses dice, so entropy is actually 1110
|
||||
"2227": "12",
|
||||
"222F": "16",
|
||||
"FFFF": "16",
|
||||
}
|
||||
// Arrange tests in array so last one can be easily detected
|
||||
var entropys = [];
|
||||
var results = [];
|
||||
for (var entropy in tests) {
|
||||
entropys.push(entropy);
|
||||
results.push(tests[entropy]);
|
||||
}
|
||||
// use entropy
|
||||
page.evaluate(function(e) {
|
||||
$(".use-entropy").prop("checked", true).trigger("change");
|
||||
});
|
||||
// Run each test
|
||||
var nextTest = function runNextTest(i) {
|
||||
var entropy = entropys[i];
|
||||
var expected = results[i];
|
||||
// set entropy
|
||||
page.evaluate(function(e) {
|
||||
$(".addresses").empty(); // bit of a hack, but needed for waitForGenerate
|
||||
$(".entropy").val(e).trigger("input");
|
||||
}, entropy);
|
||||
// check the number of bits of entropy is shown
|
||||
waitForGenerate(function() {
|
||||
var entropyText = page.evaluate(function() {
|
||||
return $(".entropy-container").text();
|
||||
});
|
||||
if (entropyText.indexOf("Have " + expected + " bits of entropy") == -1) {
|
||||
console.log("Accumulated entropy is not shown correctly for " + entropy);
|
||||
fail();
|
||||
}
|
||||
var isLastTest = i == results.length - 1;
|
||||
if (isLastTest) {
|
||||
next();
|
||||
}
|
||||
else {
|
||||
runNextTest(i+1);
|
||||
}
|
||||
});
|
||||
}
|
||||
nextTest(0);
|
||||
});
|
||||
},
|
||||
|
||||
// The number of bits of entropy to reach the next mnemonic strength is shown
|
||||
function() {
|
||||
page.open(url, function(status) {
|
||||
// use entropy
|
||||
page.evaluate(function() {
|
||||
$(".use-entropy").prop("checked", true).trigger("change");
|
||||
$(".entropy").val("7654321").trigger("input");
|
||||
});
|
||||
// check the amount of additional entropy required is shown
|
||||
waitForGenerate(function() {
|
||||
var entropyText = page.evaluate(function() {
|
||||
return $(".entropy-container").text();
|
||||
});
|
||||
if (entropyText.indexOf("3 more base 10 chars required") == -1) {
|
||||
console.log("Additional entropy requirement is not shown");
|
||||
fail();
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// The next strength above 0-word mnemonics is considered extremely weak
|
||||
// The next strength above 3-word mnemonics is considered very weak
|
||||
// The next strength above 6-word mnemonics is considered weak
|
||||
// The next strength above 9-word mnemonics is considered strong
|
||||
// The next strength above 12-word mnemonics is considered very strong
|
||||
// The next strength above 15-word mnemonics is considered extremely strong
|
||||
function() {
|
||||
page.open(url, function(status) {
|
||||
var tests = [
|
||||
{
|
||||
entropy: "A",
|
||||
words: 0,
|
||||
nextStrength: "an extremely weak",
|
||||
},
|
||||
{
|
||||
entropy: "AAAAAAAA",
|
||||
words: 3,
|
||||
nextStrength: "a very weak",
|
||||
},
|
||||
{
|
||||
entropy: "AAAAAAAA B",
|
||||
words: 3,
|
||||
nextStrength: "a very weak",
|
||||
},
|
||||
{
|
||||
entropy: "AAAAAAAA BBBBBBBB",
|
||||
words: 6,
|
||||
nextStrength: "a weak",
|
||||
},
|
||||
{
|
||||
entropy: "AAAAAAAA BBBBBBBB CCCCCCCC",
|
||||
words: 9,
|
||||
nextStrength: "a strong",
|
||||
},
|
||||
{
|
||||
entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD",
|
||||
words: 12,
|
||||
nextStrength: "a very strong",
|
||||
},
|
||||
{
|
||||
entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD EEEEEEEE",
|
||||
words: 15,
|
||||
nextStrength: "an extremely strong",
|
||||
}
|
||||
];
|
||||
// use entropy
|
||||
page.evaluate(function() {
|
||||
$(".use-entropy").prop("checked", true).trigger("change");
|
||||
});
|
||||
var nextTest = function runNextTest(i) {
|
||||
test = tests[i];
|
||||
page.evaluate(function(e) {
|
||||
$(".addresses").empty();
|
||||
$(".entropy").val(e).trigger("input");
|
||||
}, test.entropy);
|
||||
waitForGenerate(function() {
|
||||
// check the strength of the current mnemonic
|
||||
var mnemonic = page.evaluate(function() {
|
||||
return $(".phrase").val();
|
||||
});
|
||||
if (test.words == 0) {
|
||||
if (mnemonic.length > 0) {
|
||||
console.log("Mnemonic length for " + test.nextStrength + " strength is not " + test.words);
|
||||
console.log("Mnemonic: " + mnemonic);
|
||||
fail();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (mnemonic.split(" ").length != test.words) {
|
||||
console.log("Mnemonic length for " + test.nextStrength + " strength is not " + test.words);
|
||||
console.log("Mnemonic: " + mnemonic);
|
||||
fail();
|
||||
}
|
||||
}
|
||||
// check the strength of the next mnemonic is shown
|
||||
var entropyText = page.evaluate(function() {
|
||||
return $(".entropy-container").text();
|
||||
});
|
||||
if (entropyText.indexOf("required to generate " + test.nextStrength + " mnemonic") == -1) {
|
||||
console.log("Strength indicator for " + test.nextStrength + " mnemonic is incorrect");
|
||||
fail();
|
||||
}
|
||||
var isLastTest = i == tests.length - 1;
|
||||
if (isLastTest) {
|
||||
next();
|
||||
}
|
||||
else {
|
||||
runNextTest(i+1);
|
||||
}
|
||||
});
|
||||
}
|
||||
nextTest(0);
|
||||
});
|
||||
},
|
||||
|
||||
// Entropy is truncated from the right
|
||||
function() {
|
||||
page.open(url, function(status) {
|
||||
var expected = "abandon abandon ability";
|
||||
// use entropy
|
||||
page.evaluate(function() {
|
||||
$(".use-entropy").prop("checked", true).trigger("change");
|
||||
var entropy = "00000000 00000000 00000000 00000000";
|
||||
entropy += "11111111 11111111 11111111 1111"; // Missing last byte, only first 8 bytes are used
|
||||
$(".entropy").val(entropy).trigger("input");
|
||||
});
|
||||
// check the entropy is truncated from the right
|
||||
waitForGenerate(function() {
|
||||
var actual = page.evaluate(function() {
|
||||
return $(".phrase").val();
|
||||
});
|
||||
if (actual != expected) {
|
||||
console.log("Entropy is not truncated from the right");
|
||||
console.log("Expected: " + expected);
|
||||
console.log("Got: " + actual);
|
||||
fail();
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Very large entropy results in very long mnemonics
|
||||
function() {
|
||||
page.open(url, function(status) {
|
||||
// use entropy
|
||||
page.evaluate(function() {
|
||||
$(".use-entropy").prop("checked", true).trigger("change");
|
||||
var entropy = "";
|
||||
// Generate a very long entropy string
|
||||
for (var i=0; i<33; i++) {
|
||||
entropy += "AAAAAAAA"; // 3 words * 33 iterations = 99 words
|
||||
}
|
||||
$(".entropy").val(entropy).trigger("input");
|
||||
});
|
||||
// check the mnemonic is very long
|
||||
waitForGenerate(function() {
|
||||
var wordCount = page.evaluate(function() {
|
||||
return $(".phrase").val().split(" ").length;
|
||||
});
|
||||
if (wordCount != 99) {
|
||||
console.log("Large entropy does not generate long mnemonic");
|
||||
console.log("Expected 99 words, got " + wordCount);
|
||||
fail();
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Is compatible with bip32jp entropy
|
||||
// https://bip32jp.github.io/english/index.html
|
||||
// NOTES:
|
||||
// Is incompatible with:
|
||||
// base 6 with leading zeros
|
||||
// base 6 wth 12 words / 53 chars
|
||||
// base 20
|
||||
function() {
|
||||
page.open(url, function(status) {
|
||||
var expected = "defy trip fatal jaguar mean rack rifle survey satisfy drift twist champion steel wife state furnace night consider glove olympic oblige donor novel left";
|
||||
// use entropy
|
||||
page.evaluate(function() {
|
||||
$(".use-entropy").prop("checked", true).trigger("change");
|
||||
var entropy = "123450123450123450123450123450123450123450123450123450123450123450123450123450123450123450123450123";
|
||||
$(".entropy").val(entropy).trigger("input");
|
||||
});
|
||||
// check the mnemonic matches the expected value from bip32jp
|
||||
waitForGenerate(function() {
|
||||
var actual = page.evaluate(function() {
|
||||
return $(".phrase").val();
|
||||
});
|
||||
if (actual != expected) {
|
||||
console.log("Mnemonic does not match bip32jp for base 6 entropy");
|
||||
console.log("Expected: " + expected);
|
||||
console.log("Got: " + actual);
|
||||
fail();
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// If you wish to add more tests, do so here...
|
||||
|
||||
// Here is a blank test template
|
||||
|
||||
Reference in New Issue
Block a user