mirror of
https://github.com/OneKeyHQ/bip39.git
synced 2026-04-18 16:32:18 +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">
|
<div class="col-md-12">
|
||||||
<h2>Mnemonic</h2>
|
<h2>Mnemonic</h2>
|
||||||
<form class="form-horizontal" role="form">
|
<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="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>
|
<label class="col-sm-2 control-label"></label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="form-inline">
|
<div class="form-inline">
|
||||||
@@ -92,7 +94,30 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
<label class="col-sm-2 control-label"></label>
|
||||||
<div class="col-sm-10 languages">
|
<div class="col-sm-10 languages">
|
||||||
<a href="#english">English</a>
|
<a href="#english">English</a>
|
||||||
@@ -353,6 +378,24 @@
|
|||||||
but be careful - it can be easy to make mistakes if you
|
but be careful - it can be easy to make mistakes if you
|
||||||
don't know what you're doing
|
don't know what you're doing
|
||||||
</p>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -465,6 +508,7 @@
|
|||||||
<script src="js/wordlist_french.js"></script>
|
<script src="js/wordlist_french.js"></script>
|
||||||
<script src="js/wordlist_italian.js"></script>
|
<script src="js/wordlist_italian.js"></script>
|
||||||
<script src="js/jsbip39.js"></script>
|
<script src="js/jsbip39.js"></script>
|
||||||
|
<script src="js/entropy.js"></script>
|
||||||
<script src="js/index.js"></script>
|
<script src="js/index.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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 showPubKey = true;
|
||||||
var showPrivKey = true;
|
var showPrivKey = true;
|
||||||
|
|
||||||
|
var entropyChangeTimeoutEvent = null;
|
||||||
var phraseChangeTimeoutEvent = null;
|
var phraseChangeTimeoutEvent = null;
|
||||||
var rootKeyChangedTimeoutEvent = null;
|
var rootKeyChangedTimeoutEvent = null;
|
||||||
|
|
||||||
var DOM = {};
|
var DOM = {};
|
||||||
DOM.network = $(".network");
|
DOM.network = $(".network");
|
||||||
DOM.phraseNetwork = $("#network-phrase");
|
DOM.phraseNetwork = $("#network-phrase");
|
||||||
|
DOM.useEntropy = $(".use-entropy");
|
||||||
|
DOM.entropyContainer = $(".entropy-container");
|
||||||
|
DOM.entropy = $(".entropy");
|
||||||
|
DOM.entropyError = $(".entropy-error");
|
||||||
DOM.phrase = $(".phrase");
|
DOM.phrase = $(".phrase");
|
||||||
DOM.passphrase = $(".passphrase");
|
DOM.passphrase = $(".passphrase");
|
||||||
|
DOM.generateContainer = $(".generate-container");
|
||||||
DOM.generate = $(".generate");
|
DOM.generate = $(".generate");
|
||||||
DOM.seed = $(".seed");
|
DOM.seed = $(".seed");
|
||||||
DOM.rootKey = $(".root-key");
|
DOM.rootKey = $(".root-key");
|
||||||
@@ -53,6 +59,8 @@
|
|||||||
function init() {
|
function init() {
|
||||||
// Events
|
// Events
|
||||||
DOM.network.on("change", networkChanged);
|
DOM.network.on("change", networkChanged);
|
||||||
|
DOM.useEntropy.on("change", setEntropyVisibility);
|
||||||
|
DOM.entropy.on("input", delayedEntropyChanged);
|
||||||
DOM.phrase.on("input", delayedPhraseChanged);
|
DOM.phrase.on("input", delayedPhraseChanged);
|
||||||
DOM.passphrase.on("input", delayedPhraseChanged);
|
DOM.passphrase.on("input", delayedPhraseChanged);
|
||||||
DOM.generate.on("click", generateClicked);
|
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() {
|
function delayedPhraseChanged() {
|
||||||
hideValidationError();
|
hideValidationError();
|
||||||
showPending();
|
showPending();
|
||||||
@@ -116,6 +139,20 @@
|
|||||||
hidePending();
|
hidePending();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function delayedEntropyChanged() {
|
||||||
|
hideValidationError();
|
||||||
|
showPending();
|
||||||
|
if (entropyChangeTimeoutEvent != null) {
|
||||||
|
clearTimeout(entropyChangeTimeoutEvent);
|
||||||
|
}
|
||||||
|
entropyChangeTimeoutEvent = setTimeout(entropyChanged, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
function entropyChanged() {
|
||||||
|
setMnemonicFromEntropy();
|
||||||
|
phraseChanged();
|
||||||
|
}
|
||||||
|
|
||||||
function delayedRootKeyChanged() {
|
function delayedRootKeyChanged() {
|
||||||
// Warn if there is an existing mnemonic or passphrase.
|
// Warn if there is an existing mnemonic or passphrase.
|
||||||
if (DOM.phrase.val().length > 0 || DOM.passphrase.val().length > 0) {
|
if (DOM.phrase.val().length > 0 || DOM.passphrase.val().length > 0) {
|
||||||
@@ -168,6 +205,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function generateClicked() {
|
function generateClicked() {
|
||||||
|
if (isUsingOwnEntropy()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
clearDisplay();
|
clearDisplay();
|
||||||
showPending();
|
showPending();
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
@@ -599,7 +639,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getLanguageFromUrl() {
|
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() {
|
function setMnemonicLanguage() {
|
||||||
@@ -650,6 +695,65 @@
|
|||||||
return phrase;
|
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 = [
|
var networks = [
|
||||||
{
|
{
|
||||||
name: "Bitcoin",
|
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...
|
// If you wish to add more tests, do so here...
|
||||||
|
|
||||||
// Here is a blank test template
|
// Here is a blank test template
|
||||||
|
|||||||
Reference in New Issue
Block a user