X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=tests.js;h=d5a73f6cee83cf1939b08ddfea962184b6952e2d;hb=c554e6ff5c2c543997841c0fb0a506262843e8df;hp=1841af652047bd691f960b0ba8e03b66ab08ff00;hpb=057722b034672d240eea2ff01a7a5a01a4706e00;p=perso%2FImmae%2FProjets%2FCryptomonnaies%2FBIP39.git diff --git a/tests.js b/tests.js index 1841af6..d5a73f6 100644 --- a/tests.js +++ b/tests.js @@ -4,7 +4,12 @@ var page = require('webpage').create(); var url = 'src/index.html'; -var testMaxTime = 5000; +var testMaxTime = 10000; + +page.viewportSize = { + width: 1024, + height: 720 +}; page.onResourceError = function(e) { console.log("Error loading " + e.url); @@ -80,7 +85,7 @@ function waitForEntropyFeedback(fn, maxTime) { maxTime = testMaxTime; } var origFeedback = page.evaluate(function() { - return $(".entropy-error").text(); + return $(".entropy-container").text(); }); var start = new Date().getTime(); var wait = function keepWaiting() { @@ -92,11 +97,7 @@ function waitForEntropyFeedback(fn, maxTime) { return; } var feedback = page.evaluate(function() { - var feedback = $(".entropy-error"); - if (feedback.css("display") == "none") { - return ""; - } - return feedback.text(); + return $(".entropy-container").text(); }); var hasFinished = feedback != origFeedback; if (hasFinished) { @@ -1997,85 +1998,235 @@ page.open(url, function(status) { // Entropy unit tests function() { page.open(url, function(status) { - var error = page.evaluate(function() { + var response = page.evaluate(function() { var e; // binary entropy is detected - e = Entropy.fromString("01010101"); - if (e.base.str != "binary") { - return "Binary entropy not detected correctly"; + try { + e = Entropy.fromString("01010101"); + if (e.base.str != "binary") { + return "Binary entropy not detected correctly"; + } + } + catch (e) { + return e.message; } // base6 entropy is detected - e = Entropy.fromString("012345012345"); - if (e.base.str != "base 6") { - return "base6 entropy not detected correctly"; + try { + e = Entropy.fromString("012345012345"); + if (e.base.str != "base 6") { + return "base6 entropy not detected correctly"; + } + } + catch (e) { + return e.message; } // dice entropy is detected - e = Entropy.fromString("123456123456"); - if (e.base.str != "base 6 (dice)") { - return "dice entropy not detected correctly"; + try { + e = Entropy.fromString("123456123456"); + if (e.base.str != "base 6 (dice)") { + return "dice entropy not detected correctly"; + } + } + catch (e) { + return e.message; } // base10 entropy is detected - e = Entropy.fromString("0123456789"); - if (e.base.str != "base 10") { - return "base10 entropy not detected correctly"; + try { + e = Entropy.fromString("0123456789"); + if (e.base.str != "base 10") { + return "base10 entropy not detected correctly"; + } + } + catch (e) { + return e.message; } // hex entropy is detected - e = Entropy.fromString("0123456789ABCDEF"); - if (e.base.str != "hexadecimal") { - return "hexadecimal entropy not detected correctly"; + try { + e = Entropy.fromString("0123456789ABCDEF"); + if (e.base.str != "hexadecimal") { + return "hexadecimal entropy not detected correctly"; + } + } + catch (e) { + return e.message; + } + // card entropy is detected + try { + e = Entropy.fromString("AC4DTHKS"); + if (e.base.str != "card") { + return "card entropy not detected correctly"; + } + } + catch (e) { + return e.message; } // entropy is case insensitive - e = Entropy.fromString("aBcDeF"); - if (e.cleanStr != "aBcDeF") { - return "Entropy should not be case sensitive"; + try { + e = Entropy.fromString("aBcDeF"); + if (e.cleanStr != "aBcDeF") { + return "Entropy should not be case sensitive"; + } + } + catch (e) { + return e.message; } // dice entropy is converted to base6 - e = Entropy.fromString("123456"); - if (e.cleanStr != "012345") { - return "Dice entropy is not automatically converted to base6"; + try { + e = Entropy.fromString("123456"); + if (e.cleanStr != "123450") { + return "Dice entropy is not automatically converted to base6"; + } + } + catch (e) { + return e.message; } // 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"; + try { + e = Entropy.fromString("12345"); + if (e.base.str != "base 6 (dice)") { + return "dice not used as default over base 6"; + } + } + catch (e) { + return e.message; } // unused characters are ignored - e = Entropy.fromString("fghijkl"); - if (e.cleanStr != "f") { - return "additional characters are not ignored"; + try { + e = Entropy.fromString("fghijkl"); + if (e.cleanStr != "f") { + return "additional characters are not ignored"; + } + } + catch (e) { + return e.message; } // 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"; + try { + 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"; + catch (e) { + return e.message; } // Leading zeros are retained - e = Entropy.fromString("000A"); - if (e.cleanStr != "000A") { - return "Leading zeros are not retained"; + try { + e = Entropy.fromString("000A"); + if (e.cleanStr != "000A") { + return "Leading zeros are not retained"; + } + } + catch (e) { + return e.message; } // 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"; + try { + e = Entropy.fromString("2A"); + if (e.binaryStr != "00101010") { + return "Hex leading zeros are not correct in binary"; + } + } + catch (e) { + return e.message; + } + // Leading zeros for base 6 as binary string + // 20 = 2 events at 2.58 bits per event = 5 bits + // 20 in base 6 = 12 in base 10 = 1100 in base 2 + // so it needs 1 bit of padding to be the right bit length + try { + e = Entropy.fromString("20"); + if (e.binaryStr != "01100") { + return "Base 6 as binary has leading zeros"; + } + } + catch (e) { + return e.message; + } + // Leading zeros for base 10 as binary string + try { + e = Entropy.fromString("17"); + if (e.binaryStr != "010001") { + return "Base 10 as binary has leading zeros"; + } + } + catch (e) { + return e.message; + } + // Leading zeros for card entropy as binary string. + // Card entropy is hashed so 2c does not produce leading zeros. + try { + e = Entropy.fromString("4c"); + if (e.binaryStr != "0001") { + return "Card entropy as binary has leading zeros"; + } + } + catch (e) { + return e.message; } // 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"; + try { + e = Entropy.fromString("aj;se ifj; ask,dfv js;ifj"); + if (e.binaryStr.length >= 30) { + return "Keyboard mashing should produce weak entropy"; + } } - return false; + catch (e) { + return e.message; + } + // Card entropy is used if every pair could be a card + try { + e = Entropy.fromString("4c3c2c"); + if (e.base.str != "card") { + return "Card entropy not used if all pairs are cards"; + } + } + catch (e) { + return e.message; + } + // Card entropy uses base 52 + // [ cards, binary ] + try { + var cards = [ + [ "ac", "0100" ], + [ "acqs", "10111101" ], + [ "acks", "11110000" ], + [ "2cac", "11000010" ], + [ "2c", "1000" ], + [ "3d", "1111" ], + [ "4h", "0011" ], + [ "5s", "1001" ], + [ "6c", "1011" ], + [ "7d", "1101" ], + [ "8h", "1011" ], + [ "9s", "1010" ], + [ "tc", "1101" ], + [ "jd", "1101" ], + [ "qh", "1100" ], + [ "ks", "1111" ], + [ "ks2c", "10000001" ], + [ "KS2C", "10000001" ], + ]; + for (var i=0; i 0) { - console.log("Mnemonic length for " + test.nextStrength + " strength is not " + test.words); - console.log("Mnemonic: " + mnemonic); + // Check mnemonic length + if ("words" in test && test.words == 0) { + if (mnemonic.length > 0) { + console.log("Mnemonic length for " + test.strength + " strength is not " + test.words); + console.log("Entropy: " + test.entropy); + console.log("Mnemonic: " + mnemonic); + fail(); + } + } + else if ("words" in test) { + if (mnemonic.split(" ").length != test.words) { + console.log("Mnemonic length for " + test.strength + " strength is not " + test.words); + console.log("Entropy: " + test.entropy); + console.log("Mnemonic: " + mnemonic); + fail(); + } + } + // check feedback + var feedback = page.evaluate(function() { + return $(".entropy-container").text(); + }); + var feedbackError = getFeedbackError(test, feedback); + if (feedbackError) { + console.log("Entropy feedback for " + test.entropy + " returned error"); + console.log(feedbackError); fail(); } + // Run next test var isLastTest = i == tests.length - 1; if (isLastTest) { next(); @@ -2485,49 +2848,22 @@ page.open(url, function(status) { else { runNextTest(i+1); } - } - else { - waitForGenerate(function() { - // check the strength of the current mnemonic - var mnemonic = page.evaluate(function() { - return $(".phrase").val(); - }); - 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 +// Entropy is truncated from the left function() { page.open(url, function(status) { - var expected = "abandon abandon ability"; + var expected = "avocado zoo zone"; // use entropy page.evaluate(function() { $(".use-entropy").prop("checked", true).trigger("change"); + $(".mnemonic-length").val("raw"); var entropy = "00000000 00000000 00000000 00000000"; - entropy += "11111111 11111111 11111111 1111"; // Missing last byte, only first 8 bytes are used + entropy += "11111111 11111111 11111111 1111"; // Missing last byte $(".entropy").val(entropy).trigger("input"); }); // check the entropy is truncated from the right @@ -2552,6 +2888,7 @@ page.open(url, function(status) { // use entropy page.evaluate(function() { $(".use-entropy").prop("checked", true).trigger("change"); + $(".mnemonic-length").val("raw"); var entropy = ""; // Generate a very long entropy string for (var i=0; i<33; i++) { @@ -2578,16 +2915,15 @@ page.open(url, function(status) { // 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"; + var expected = "train then jungle barely whip fiber purpose puppy eagle cloud clump hospital robot brave balcony utility detect estate old green desk skill multiply virus"; // use entropy page.evaluate(function() { $(".use-entropy").prop("checked", true).trigger("change"); - var entropy = "123450123450123450123450123450123450123450123450123450123450123450123450123450123450123450123450123"; + $(".mnemonic-length").val("raw"); + var entropy = "543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543"; $(".entropy").val(entropy).trigger("input"); }); // check the mnemonic matches the expected value from bip32jp @@ -2645,6 +2981,164 @@ page.open(url, function(status) { }); }, +// Mnemonic length can be selected even for weak entropy +function() { +page.open(url, function(status) { + // use entropy + page.evaluate(function() { + $(".use-entropy").prop("checked", true).trigger("change"); + $(".entropy").val("012345"); + $(".mnemonic-length").val("18").trigger("change"); + }); + // check the mnemonic is the correct length + waitForGenerate(function() { + var phrase = page.evaluate(function() { + return $(".phrase").val(); + }); + var numberOfWords = phrase.split(/\s/g).length; + if (numberOfWords != 18) { + console.log("Weak entropy cannot be overridden to give 18 word mnemonic"); + console.log(phrase); + fail(); + } + next(); + }); +}); +}, + +// Github issue 33 +// https://github.com/iancoleman/bip39/issues/33 +// Final cards should contribute entropy +function() { +page.open(url, function(status) { + // use entropy + page.evaluate(function() { + $(".use-entropy").prop("checked", true).trigger("change"); + $(".mnemonic-length").val("raw"); + $(".entropy").val("7S 9H 9S QH 8C KS AS 7D 7C QD 4S 4D TC 2D 5S JS 3D 8S 8H 4C 3C AC 3S QC 9C JC 7H AD TD JD 6D KH 5C QS 2S 6S 6H JH KD 9D-6C TS TH 4H KC 5H 2H AH 2C 8D 3H 5D").trigger("input"); + }); + // get the mnemonic + waitForGenerate(function() { + var originalPhrase = page.evaluate(function() { + return $(".phrase").val(); + }); + // Set the last 12 cards to be AS + page.evaluate(function() { + $(".addresses").empty(); + $(".entropy").val("7S 9H 9S QH 8C KS AS 7D 7C QD 4S 4D TC 2D 5S JS 3D 8S 8H 4C 3C AC 3S QC 9C JC 7H AD TD JD 6D KH 5C QS 2S 6S 6H JH KD 9D-AS AS AS AS AS AS AS AS AS AS AS AS").trigger("input"); + }); + // get the new mnemonic + waitForGenerate(function() { + var newPhrase = page.evaluate(function() { + return $(".phrase").val(); + }); + // check the phrase has changed + if (newPhrase == originalPhrase) { + console.log("Changing last 12 cards does not change mnemonic"); + console.log("Original:"); + console.log(originalPhrase); + console.log("New:"); + console.log(newPhrase); + fail(); + } + next(); + }); + }); +}); +}, + +// Github issue 35 +// https://github.com/iancoleman/bip39/issues/35 +// QR Code support +function() { +page.open(url, function(status) { + // use entropy + page.evaluate(function() { + $(".generate").click(); + }); + waitForGenerate(function() { + var p = page.evaluate(function() { + // get position of mnemonic element + return $(".phrase").offset(); + }); + p.top = Math.ceil(p.top); + p.left = Math.ceil(p.left); + // check the qr code shows + page.sendEvent("mousemove", p.left+4, p.top+4); + var qrShowing = page.evaluate(function() { + return $(".qr-container").find("canvas").length > 0; + }); + if (!qrShowing) { + console.log("QR Code does not show"); + fail(); + } + // check the qr code hides + page.sendEvent("mousemove", p.left-4, p.top-4); + var qrHidden = page.evaluate(function() { + return $(".qr-container").find("canvas").length == 0; + }); + if (!qrHidden) { + console.log("QR Code does not hide"); + fail(); + } + next(); + }); +}); +}, + +// BIP44 account extendend private key is shown +// github issue 37 - compatibility with electrum +function() { +page.open(url, function(status) { + // set the phrase + var expected = "xprv9yzrnt4zWVJUr1k2VxSPy9ettKz5PpeDMgaVG7UKedhqnw1tDkxP2UyYNhuNSumk2sLE5ctwKZs9vwjsq3e1vo9egCK6CzP87H2cVYXpfwQ"; + page.evaluate(function() { + $(".phrase").val("abandon abandon ability"); + $(".phrase").trigger("input"); + }); + // check the BIP44 account extended private key + waitForGenerate(function() { + var actual = page.evaluate(function() { + return $(".account-xprv").val(); + }); + if (actual != expected) { + console.log("BIP44 account extended private key is incorrect"); + console.log("Expected: " + expected); + console.log("Actual: " + actual); + fail(); + } + next(); + }); +}); +}, + +// BIP44 account extendend public key is shown +// github issue 37 - compatibility with electrum +function() { +page.open(url, function(status) { + // set the phrase + var expected = "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf"; + page.evaluate(function() { + $(".phrase").val("abandon abandon ability"); + $(".phrase").trigger("input"); + }); + // check the BIP44 account extended public key + waitForGenerate(function() { + var actual = page.evaluate(function() { + return $(".account-xpub").val(); + }); + if (actual != expected) { + console.log("BIP44 account extended public key is incorrect"); + console.log("Expected: " + expected); + console.log("Actual: " + actual); + fail(); + } + next(); + }); +}); +}, + + // If you wish to add more tests, do so here... // Here is a blank test template