From e86ce7f268e8d2ba6aa53ddd4fe17684624370ef Mon Sep 17 00:00:00 2001 From: Ken Sedgwick Date: Sat, 7 Dec 2013 14:16:08 -0800 Subject: [PATCH] Updated to latest version of BIP-0039. --- .../google/bitcoin/crypto/MnemonicCode.java | 212 +++++++++--------- .../bitcoin/crypto/MnemonicCodeTest.java | 160 +++++++------ 2 files changed, 191 insertions(+), 181 deletions(-) diff --git a/core/src/main/java/com/google/bitcoin/crypto/MnemonicCode.java b/core/src/main/java/com/google/bitcoin/crypto/MnemonicCode.java index 5045d976..37bb41a2 100644 --- a/core/src/main/java/com/google/bitcoin/crypto/MnemonicCode.java +++ b/core/src/main/java/com/google/bitcoin/crypto/MnemonicCode.java @@ -17,8 +17,6 @@ package com.google.bitcoin.crypto; import com.google.bitcoin.core.Sha256Hash; -import org.spongycastle.crypto.engines.RijndaelEngine; -import org.spongycastle.crypto.params.KeyParameter; import org.spongycastle.util.encoders.Hex; import java.io.BufferedReader; @@ -36,10 +34,6 @@ import java.util.List; * A MnemonicCode object may be used to convert between binary seed values and * lists of words per the BIP 39 * specification - * - * NOTE - as of 15 Oct 2013 the spec at - * https://en.bitcoin.it/wiki/BIP_0039 is out-of-date. The correct - * spec can be found at https://github.com/trezor/python-mnemonic */ public class MnemonicCode { @@ -47,6 +41,8 @@ public class MnemonicCode { public static String BIP39_ENGLISH_SHA256 = "ad90bf3beb7b0eb7e5acd74727dc0da96e0a280a258354e7293fb7e211ac03db"; + private static final int HMAC_ROUNDS = 10000; + public MnemonicCode() throws IOException { this(MnemonicCode.class.getResourceAsStream("mnemonic/wordlist/english.txt"), BIP39_ENGLISH_SHA256); } @@ -84,66 +80,89 @@ public class MnemonicCode { } /** - * Encodes a 128, 192 or 256 bit seed into a list of words. + * Convert mnemonic word list to seed. */ - public List encode(byte[] seed) throws MnemonicLengthException { + public static byte[] toSeed(List words, String passphrase) { + + // To create binary seed from mnemonic, we use HMAC-SHA512 + // function with string "mnemonic" + passphrase (in UTF-8) as + // key and mnemonic sentence (again in UTF-8) as the + // message. We perform 10000 HMAC rounds and use the final + // result as the binary seed. + // + // Pseudocode: + // + // K = "mnemonic" + passphrase + // M = mnemonic_sentence + // for i in 1 ... 10000 do + // M = hmac_sha512(K, M) + // done + // seed = M + + byte[] kk = new String("mnemonic" + passphrase).getBytes(); + byte[] mm = joinStringList(words).getBytes(); + + for (int ii = 0; ii < HMAC_ROUNDS; ++ii) + mm = HDUtils.hmacSha512(kk, mm); + + return mm; + } + + /** + * Convert arbitrary data to mnemonic word list. + */ + public List toMnemonic(byte[] entropy) throws MnemonicLengthException { + if (entropy.length % 4 > 0) + throw new MnemonicLengthException("entropy length not multiple of 32 bits"); + + // We take initial entropy of ENT bits and compute its + // checksum by taking first ENT / 32 bits of its SHA256 hash. + + byte[] hash = Sha256Hash.create(entropy).getBytes(); + boolean[] hashBits = bytesToBits(hash); - // 2. Make sure its length (L) is 128, 192 or 256 bits. - int len = seed.length * 8; - if (len != 128 && len != 192 && len != 256) - throw new MnemonicLengthException("seed not 128, 192 or 256 bits"); + boolean[] entropyBits = bytesToBits(entropy); + int checksumLengthBits = entropyBits.length / 32; - // 3. Encrypt input data 10000x with Rijndael (ECB mode). - // Set key to SHA256 hash of string ("mnemonic" + user_password). - // Set block size to input size (that's why Rijndael is used, not AES). - byte[] indata = stretch(len, seed); + // We append these bits to the end of the initial entropy. + boolean[] concatBits = new boolean[entropyBits.length + checksumLengthBits]; + for (int ii = 0; ii < entropyBits.length; ++ii) + concatBits[ii] = entropyBits[ii]; + for (int ii = 0; ii < checksumLengthBits; ++ii) + concatBits[entropyBits.length + ii] = hashBits[ii]; - // Convert binary data to array of boolean for processing. - boolean[] inarray = new boolean[indata.length * 8]; - for (int ii = 0; ii < indata.length; ++ii) - for (int kk = 0; kk < 8; ++kk) - inarray[(ii * 8) + kk] = (indata[ii] & (1 << (7 - kk))) != 0; + // Next we take these concatenated bits and split them into + // groups of 11 bits. Each group encodes number from 0-2047 + // which is a position in a wordlist. We convert numbers into + // words and use joined words as mnemonic sentence. - // 4-6 Compute checksum. - boolean[] chksum = checksum(inarray); - - // 7. Concatenate I and C into encoded data (E). Length of E is divisable by 33 bits. - boolean[] ee = new boolean[inarray.length + chksum.length]; - for (int ii = 0; ii < inarray.length; ++ii) - ee[ii] = inarray[ii]; - for (int ii = 0; ii < chksum.length; ++ii) - ee[inarray.length + ii] = chksum[ii]; - - // 8. Keep taking 11 bits from E until there are none left. - // 9. Treat them as integer W, add word with index W to the output. ArrayList words = new ArrayList(); - int nwords = ee.length / 11; + int nwords = concatBits.length / 11; for (int ii = 0; ii < nwords; ++ii) { int ndx = 0; - for (int kk = 0; kk < 11; ++kk) { + for (int jj = 0; jj < 11; ++jj) { ndx <<= 1; - if (ee[(ii * 11) + kk]) + if (concatBits[(ii * 11) + jj]) ndx |= 0x1; } words.add(this.wordList.get(ndx)); } - return words; + return words; } /** - * Decodes a list of words into a seed value. + * Check to see if a mnemonic word list is valid. */ - public byte[] decode(List words) throws MnemonicLengthException, MnemonicWordException, MnemonicChecksumException { - int nwords = words.size(); + public void check(List words) throws MnemonicLengthException, MnemonicWordException, MnemonicChecksumException { + if (words.size() % 3 > 0) + throw new MnemonicLengthException("Word list size must be multiple of three words."); - // 2. Make sure the number of words is 12, 18 or 24. - if (nwords != 12 && nwords != 18 && nwords != 24) - throw new MnemonicLengthException("Mnemonic code not 12, 18 or 24 words"); - - // 3. Figure out word indexes in a dictionary and output them as binary stream E. - int len = nwords * 11; - boolean[] ee = new boolean[len]; + // Look up all the words in the list and construct the + // concatenation of the original entropy and the checksum. + // + int concatLenBits = words.size() * 11; + boolean[] concatBits = new boolean[concatLenBits]; int wordindex = 0; for (String word : words) { // Find the words index in the wordlist. @@ -153,80 +172,49 @@ public class MnemonicCode { // Set the next 11 bits to the value of the index. for (int ii = 0; ii < 11; ++ii) - ee[(wordindex * 11) + ii] = (ndx & (1 << (10 - ii))) != 0; + concatBits[(wordindex * 11) + ii] = (ndx & (1 << (10 - ii))) != 0; ++wordindex; - } + } - // 5. Split E into two parts: B and C, where B are first L/33*32 bits, C are last L/33 bits. - int bblen = (len / 33) * 32; - int cclen = len - bblen; + int checksumLengthBits = concatLenBits / 33; + int entropyLengthBits = concatLenBits - checksumLengthBits; - boolean[] bb = new boolean[bblen]; - for (int ii = 0; ii < bblen; ++ii) - bb[ii] = ee[ii]; - - boolean[] cc = new boolean[cclen]; - for (int ii = 0; ii < cclen; ++ii) - cc[ii] = ee[bblen + ii]; - - // 6. Make sure C is the checksum of B (using the step 5 from the above paragraph). - boolean[] chksum = checksum(bb); - if (!Arrays.equals(chksum, cc)) - throw new MnemonicChecksumException("checksum error"); - - // 8. Treat B as binary data. - byte[] outdata = new byte[bblen / 8]; - for (int ii = 0; ii < outdata.length; ++ii) + // Extract original entropy as bytes. + byte[] entropy = new byte[entropyLengthBits / 8]; + for (int ii = 0; ii < entropy.length; ++ii) for (int jj = 0; jj < 8; ++jj) - if (bb[(ii * 8) + jj]) - outdata[ii] |= 1 << (7 - jj); + if (concatBits[(ii * 8) + jj]) + entropy[ii] |= 1 << (7 - jj); - // 9. Decrypt this data 10000x with Rijndael (ECB mode), - // use the same parameters as used in step 3 of encryption. - byte[] seed = unstretch(bblen, outdata); + // Take the digest of the entropy. + byte[] hash = Sha256Hash.create(entropy).getBytes(); + boolean[] hashBits = bytesToBits(hash); - return seed; + // Check all the checksum bits. + for (int ii = 0; ii < checksumLengthBits; ++ii) + if (concatBits[entropyLengthBits + ii] != hashBits[ii]) + throw new MnemonicChecksumException("checksum error"); } - private byte[] stretch(int len, byte[] data) { - // 3. Encrypt input data 10000x with Rijndael (ECB mode). - // Set key to SHA256 hash of string ("mnemonic" + user_password). - // Set block size to input size (that's why Rijndael is used, not AES). - byte[] mnemonic = {'m', 'n', 'e', 'm', 'o', 'n', 'i', 'c'}; - byte[] key = Sha256Hash.create(mnemonic).getBytes(); - byte[] buffer = new byte[data.length]; - System.arraycopy(data, 0, buffer, 0, data.length); - RijndaelEngine cipher = new RijndaelEngine(len); - cipher.init(true, new KeyParameter(key)); - for (int ii = 0; ii < 10000; ++ii) - cipher.processBlock(buffer, 0, buffer, 0); - return buffer; + private static boolean[] bytesToBits(byte[] data) { + boolean[] bits = new boolean[data.length * 8]; + for (int ii = 0; ii < data.length; ++ii) + for (int jj = 0; jj < 8; ++jj) + bits[(ii * 8) + jj] = (data[ii] & (1 << (7 - jj))) != 0; + return bits; } - private byte[] unstretch(int len, byte[] data) { - // 9. Decrypt this data 10000x with Rijndael (ECB mode), - // use the same parameters as used in step 3 of encryption. - byte[] mnemonic = {'m', 'n', 'e', 'm', 'o', 'n', 'i', 'c'}; - byte[] key = Sha256Hash.create(mnemonic).getBytes(); - byte[] buffer = new byte[data.length]; - System.arraycopy(data, 0, buffer, 0, data.length); - RijndaelEngine cipher = new RijndaelEngine(len); - cipher.init(false, new KeyParameter(key)); - for (int ii = 0; ii < 10000; ++ii) - cipher.processBlock(buffer, 0, buffer, 0); - return buffer; - } - - private boolean[] checksum(boolean[] bits) { - // 4. Compute the length of the checkum (LC). LC = L/32 - int lc = bits.length / 32; - - // 5. Split I into chunks of LC bits (I1, I2, I3, ...). - // 6. XOR them altogether and produce the checksum C. C = I1 xor I2 xor I3 ... xor In. - boolean[] cc = new boolean[lc]; - for (int ii = 0; ii < 32; ++ii) - for (int jj = 0; jj < lc; ++jj) - cc[jj] ^= bits[(ii * lc) + jj]; - return cc; + static private String joinStringList(List list) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (String item : list) + { + if (first) + first = false; + else + sb.append(" "); + sb.append(item); + } + return sb.toString(); } } diff --git a/core/src/test/java/com/google/bitcoin/crypto/MnemonicCodeTest.java b/core/src/test/java/com/google/bitcoin/crypto/MnemonicCodeTest.java index 747bbf99..abbf6825 100644 --- a/core/src/test/java/com/google/bitcoin/crypto/MnemonicCodeTest.java +++ b/core/src/test/java/com/google/bitcoin/crypto/MnemonicCodeTest.java @@ -29,80 +29,103 @@ import java.util.List; import static org.junit.Assert.assertEquals; public class MnemonicCodeTest { - // These vectors are from https://raw.github.com/trezor/python-mnemonic/master/vectors.json + // These vectors are from https://github.com/trezor/python-mnemonic/blob/master/vectors.json String vectors[] = { - - "00000000000000000000000000000000", - "risk tiger venture dinner age assume float denial penalty hello game wing", + "00000000000000000000000000000000", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", + "cb5e7230ce8229de990674f6aa4288325fd4d8181f761734bd8b5cc944fedc2a4300e64422864b565352de7ffbc5ad0fafdf5344489f3a83e4a4bb5271cafaae", - "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", - "truth chase learn pretty right casual acoustic frozen betray main slogan method", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "legal winner thank year wave sausage worth useful legal winner thank yellow", + "de1277934939d6969519f44b7b3757a905d7f635be41e1e88022c346bc52ad26c0a3e9578e73e9b89066873266f285a5891d27d28cb27fccfe26d92bbd7ee364", - "80808080808080808080808080808080", - "olive garment twenty drill people finish hat own usual level milk usage", + "80808080808080808080808080808080", + "letter advice cage absurd amount doctor acoustic avoid letter advice cage above", + "8863bccef9cfffeacef1e4c6fc97bba8227ab0fc7e8e162be7467282689a13521ea364d7c4bc8cd241b59f53c5147a89c18a47248a96592ab9a2c1f1870b026c", - "ffffffffffffffffffffffffffffffff", - "laundry faint system client frog vanish plug shell slot cable large embrace", + "ffffffffffffffffffffffffffffffff", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", + "7a29e57c7a1532af1bddb7e02b892cfccc6a57b74fe9784324ea89fab8a66dc64fde79c31166b159685116f4e93c1795496f20ffdc2d3a69d3439931dabde86e", - "000000000000000000000000000000000000000000000000", - "giant twelve seat embark ostrich jazz leader lunch budget hover much weapon vendor build truth garden year list", + "000000000000000000000000000000000000000000000000", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent", + "c3e382025b6a22a901505cf393faea450eb6c4a5f2a8c8f0596285b2bd84688877a6cc7231420e2bbdd2428e62ed549a78fa215b3adafd8dea075dabfc704d5e", - "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", - "awful faint gun mean fuel side slogan marine glad donkey velvet oyster movie real type digital dress federal", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will", + "c82666e40eb097bf6eb05fecd7dc2ddfb6bbdc6071900f4b3fd3c3e635db69aa2094f1f450c98e8dc6103aa72df635abdfcc3b6d6ec5261a9208a07a35a3f1c8", - "808080808080808080808080808080808080808080808080", - "bless carpet daughter animal hospital pave faculty escape fortune song sign twin unknown bread mobile normal agent use", + "808080808080808080808080808080808080808080808080", + "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always", + "e90681c67c55504afadca009ce4042819341fa0e90300b6d32b4f2e8e8a6678ff7e7fc1da663ae194dc7a2ef7ec7b50112d1a5efce47bfd00c66eec82f2265b5", - "ffffffffffffffffffffffffffffffffffffffffffffffff", - "saddle curve flight drama client resemble venture arch will ordinary enrich clutch razor shallow trophy tumble dice outer", + "ffffffffffffffffffffffffffffffffffffffffffffffff", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when", + "2a372547df962742942674170a7cef495ea0b97f4864de16d0f3ee82eb577ca1eca345e601cc2df7c626c5bc51c52c28a3b4294224b685c958c7450bee6769e6", - "0000000000000000000000000000000000000000000000000000000000000000", - "supreme army trim onion neglect coach squirrel spider device glass cabbage giant web digital floor able social magnet only fork fuel embrace salt fence", + "0000000000000000000000000000000000000000000000000000000000000000", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art", + "58cb9c5555d67ecc7b32305a78d1a2fcf0c9b22f1af761cfafc65eb1d3909f63ee2cab84996a7478cfd3e864cda5efb0caf580d56cf49739c6b3638d94e758c1", - "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", - "cloth video uncle switch year captain artist country adjust edit inherit ocean tennis soda baby express hospital forest panel actual profit boy spice elite", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title", + "0093cb3ed6d1302d3cf498017f8cb1c7dc2fdbd62ec57fc49e4b2a4dd47a23e44e0b309517d5a3e7b0f4f0ef0ed132818cf120a098a92e572ad086f1a90ccb7f", - "8080808080808080808080808080808080808080808080808080808080808080", - "fence twin prize extra choose mask twist deny cereal quarter can power term ostrich leg staff nature nut swift sausage amateur aim script wisdom", + "8080808080808080808080808080808080808080808080808080808080808080", + "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless", + "8a21e46b9d264328c63e707e3d38ed4eb21508deda309fa2ef57cc8eca8b351ca3018758844ba9fb5851bab15d026a61cabace53a9a39bc91dc2c51407542cf5", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "moon fiscal evidence exile rifle series neglect giant exclude banana glance frown kangaroo globe turtle hat fitness casual sudden select idle arctic best unlock", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote", + "5c8a1a284ab2844daf02cab322df3996574c9d53cbd36159c493441990f0d2a6bc9bc1502e3a067943d8ec67324663cbfb9667b57fed220e3f28335e26a90f93", - "449ea2d7249c6e0d8d295424fb8894cf", - "choice barrel artefact cram increase sell veteran matrix mirror hollow walk pave", + "1083fa24dbb0afa4e7b327d23f666567", + "awesome cabin matrix resist april sponsor paddle gossip split will off soon", + "467406b36a0176e40e013393e5ecef1f5b4019980b502eda9db1db06f7786e088b206f045f2bfcf93bd3b17a598335b078fcc5890115857ff741bd154b54f049", - "75fc3f44a7ff8e2b8af05aa18bded3827a3796df406763dd", - "crack outside teach chat praise client manual scorpion predict chalk decrease casino lunch garbage enable ball when bamboo", + "d8cbcd1ac2153ecd74048480c2732f637d642b21f0dd40df", + "sugar fury effort loud fault grit source mountain liar bean slim shoulder stone better march brick dolphin zero", + "f60180ea5047659cbb17ed6ef79c974de86c0170c7a1962b205329eb8fe9dcdd148615d35c515c4ec8da25f4cf54d5b7cd8cd5bf8dc4059df3a7900ca25f8306", - "1cce2f8c2c6a7f2d8473ebf1c32ce13b36737835d7a8768f44dcf96d64782c0e", - "muffin evoke all fiber night guard black quote neck expire dial tenant leisure have dragon neck notable peace captain insane nice uphold shine angry", + "2952f95cefe041616f6f379ab649cf8b702ecf8e4acceaebdda4cc50e2bf1d7b", + "citizen oak fire thank advice radar sad tragic one rather initial black actual guitar decrease flower turtle galaxy hard obvious athlete garbage invest have", + "eff4b6a15bb55fcf4bbfa2b3b9e24e7dc4bed8319ef7703f1786d472c73666922925778eaa5a06f8a26d2c7e7240be746fd69edfaf197e0dae12d7e0b550cfc8", - "3daa82dd08bd144ec9fb9f77c6ece3d2", - "foil dawn net enroll turtle bird vault trumpet service fun immune unveil", + "f5e82717078a6ddc538a03e825f91bed", + "vote donkey shift audit plug until evolve document trial cool eight swarm", + "83dad22293225780a914083fc1a69bfe1d910f5b5962b0364820132a42ae1bd567a1fb4d5a19ad3d64539e38a7ee3d6429fac2b74e72b020913131c5eadb7db4", - "9720239c0039f8446d44334daec325f3c24b3a490315d6d9", - "damp all desert dash insane pear debate easily soup enough goddess make friend plug violin pact wealth insect", + "16b59b6a426f2f302f73049a32ab8572394278982212357a", + "birth proud surround luggage very object saddle gauge olive next throw tongue neither detail gauge drastic cube strategy", + "38ceb07e0dad221f612631843be6ae44a650aaf789c8ebea9313e07498d7864385227d25c7a8268a5b850367eef31639632e9218acadead20980b864b1cd477e", - "fe58c6644bc3fad95832d4400cea0cce208c8b19bb4734a26995440b7fae7600", - "wet sniff asthma once gap enrich pumpkin define trust rude gesture keen grass fine emerge census immense smooth ritual spirit rescue problem beef choice", + "95b6cb48c7bc9c2a54496ae3eea790824b57e52b9637058f084555bc1b809b2f", + "noble rent split month six benefit eye coil token inside tomorrow afraid rely verb purity shoulder airport joke bacon problem script scare hole trumpet", + "e33e3d32e467877596a18ac60050488a0ec1557fda6bf95bad3d33d964c5e99dcd97d378403cc2723ed1c85c12b42bc59f15458d970d7a9d015f556109c146b0", - "99fe82c94edadffe75e1cc64cbd7ada7", - "thing real emerge verify domain cloud lens teach travel radio effort glad", + "7f93397f750f70a26513de2732ed95ee", + "legend oil garlic tube warfare eye nephew knock cheese number grace tackle", + "7f92ad63e4cdf4f15c23740556ad81e7f8cbd67cc672c93894c9c0d4fb171539eed5ab29f366570ed9940b816f45a539c3816f7ac19511794b752c5c1ec0e732", - "4fd6e8d06d55b4700130f8f462f7f9bfc6188da83e3faadb", - "diary opinion lobster code orange odor insane permit spirit evolve upset final antique grant friend dutch say enroll", + "14c29fe840dd1c9f05d392ba13e4e1466b32ed0726a15f89", + "below belt wheel like spike exhibit blanket inch ring palace debate mimic rebel isolate broken stage garbage enhance", + "7bae6e54f8bad645f18f574b310bd3e6fde126dabcaf63a889940380e4798810e48c8151fc56bb2389c07498deacef025f03cbf8fc57ea3ec68f6421b0fcb649", - "7a547fb59606e89ba88188013712946f6cb31c3e0ca606a7ee1ff23f57272c63", - "layer owner legal stadium glance oyster element spell episode eager wagon stand pride old defense black print junior fade easy topic ready galaxy debris", + "cb30610d175ffeab8357d5190d31923997752a7f9815087bfcad5eb0b43f6468", + "sleep loan drive concert zoo fiction ask wide boil hat goose industry jar news wrist actor anchor that clip runway area cabbage museum abuse", + "b922030609e7626696b9cf5ca4c06cd99290be30b1052770f6a60c5f26532d178f287a4285d7a2add2845dc89a816b26fdba1c830067d130740f64c0ab5cfbe1", - "e5fc62d20e0e5d9b2756e8d4d91cbb80", - "flat make unit discover rifle armed unit acquire group panel nerve want", + "a30b50a5439dcd1774f412ea5ec33403", + "perfect fold citizen mango system merry stable liquid tumble voyage snack alter", + "aae175f26848370c4d5d3d0640597e2bf1b28e95908dd877259b3eac5d71ffe3140739a3ed80180f88159571df84441985620e6b2fb0696e5cba1aa7b8d10b98", - "d29be791a9e4b6a48ff79003dbf31d6afabdc4290a273765", - "absurd valve party disorder basket injury make blanket vintage ancient please random theory cart retire odor borrow belt", + "70044da2175ad681d0ebbf2da83cf407eb9c8fd91fc0a8c9", + "hybrid carbon hammer concert pulp domain dry jewel color draft dial average right elevator good way potato energy", + "a3dffe3a31a2e949d1b04af7495a5b59db17e41d93b985feeaaae89260a9c86c6dcdf7cb32eaba61c2f4f0340f0f17d1ebb67af11657286b2ffd66ec4e05a8b7", - "c87c135433c16f1ecbf9919dc53dd9f30f85824dc7264d4e1bd644826c902be2", - "upper will wisdom term once bean blur inquiry used bamboo frequent hamster amazing cake attack any author mimic leopard day token joy install company", + "0e0bab4df9669b97ba3f75a50b2e92423bbe6e91a1b01dbbf3ba200a917c9106", + "asthma front square version have slim trophy upgrade pink floor pig love room dance educate current buffalo test update divorce poverty salad dune scheme", + "2eb4d85fbd8deaf9b06bf9cdb3e5f36e8da040d110312075eb32e776fc8e505b94be3e63c1525ad41f5e5968a263853001dc7c40ea3af8e8b0cfb7effd5f408c", }; private MnemonicCode mc; @@ -112,43 +135,42 @@ public class MnemonicCodeTest { } @Test - public void testEncodeVectors() throws Exception { - for (int ii = 0; ii < vectors.length; ii += 2) { - List words = mc.encode(Hex.decode(vectors[ii])); - assertEquals(vectors[ii + 1], Joiner.on(' ').join(words)); - } - } + public void testVectors() throws Exception { + for (int ii = 0; ii < vectors.length; ii += 3) { + String vecData = vectors[ii]; + String vecCode = vectors[ii+1]; + String vecSeed = vectors[ii+2]; - @Test - public void testDecodeVectors() throws Exception { - for (int ii = 0; ii < vectors.length; ii += 2) { - byte[] seed = mc.decode(split(vectors[ii+1])); - assertEquals(vectors[ii], new String(Hex.encode(seed))); + List code = mc.toMnemonic(Hex.decode(vecData)); + byte[] seed = MnemonicCode.toSeed(code, "TREZOR"); + + assertEquals(vecCode, Joiner.on(' ').join(code)); + assertEquals(vecSeed, new String(Hex.encode(seed))); } } @Test(expected = MnemonicLengthException.class) - public void testBadSeedLength() throws Exception { - byte[] seed = Hex.decode("7f7f7f7f7f7f7f7f7f7f7f7f7f7f"); - mc.encode(seed); + public void testBadEntropyLength() throws Exception { + byte[] entropy = Hex.decode("7f7f7f7f7f7f7f7f7f7f7f7f7f7f"); + mc.toMnemonic(entropy); } @Test(expected = MnemonicLengthException.class) public void testBadLength() throws Exception { - List words = split("risk tiger venture dinner age assume float denial penalty"); - mc.decode(words); + List words = split("risk tiger venture dinner age assume float denial penalty hello"); + mc.check(words); } @Test(expected = MnemonicWordException.class) public void testBadWord() throws Exception { List words = split("risk tiger venture dinner xyzzy assume float denial penalty hello game wing"); - mc.decode(words); + mc.check(words); } @Test(expected = MnemonicChecksumException.class) public void testBadChecksum() throws Exception { - List words = split("risk tiger venture dinner age assume float denial penalty hello game game"); - mc.decode(words); + List words = split("bless cloud wheel regular tiny venue bird web grief security dignity zoo"); + mc.check(words); } static public List split(String words) {