3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-12 10:15:52 +00:00

Updated to latest version of BIP-0039.

This commit is contained in:
Ken Sedgwick 2013-12-07 14:16:08 -08:00 committed by Mike Hearn
parent 5cd10a537a
commit e86ce7f268
2 changed files with 191 additions and 181 deletions

View File

@ -17,8 +17,6 @@
package com.google.bitcoin.crypto; package com.google.bitcoin.crypto;
import com.google.bitcoin.core.Sha256Hash; 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 org.spongycastle.util.encoders.Hex;
import java.io.BufferedReader; 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 * A MnemonicCode object may be used to convert between binary seed values and
* lists of words per <a href="https://en.bitcoin.it/wiki/BIP_0039">the BIP 39 * lists of words per <a href="https://en.bitcoin.it/wiki/BIP_0039">the BIP 39
* specification</a> * specification</a>
*
* 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 { public class MnemonicCode {
@ -47,6 +41,8 @@ public class MnemonicCode {
public static String BIP39_ENGLISH_SHA256 = "ad90bf3beb7b0eb7e5acd74727dc0da96e0a280a258354e7293fb7e211ac03db"; public static String BIP39_ENGLISH_SHA256 = "ad90bf3beb7b0eb7e5acd74727dc0da96e0a280a258354e7293fb7e211ac03db";
private static final int HMAC_ROUNDS = 10000;
public MnemonicCode() throws IOException { public MnemonicCode() throws IOException {
this(MnemonicCode.class.getResourceAsStream("mnemonic/wordlist/english.txt"), BIP39_ENGLISH_SHA256); this(MnemonicCode.class.getResourceAsStream("mnemonic/wordlist/english.txt"), BIP39_ENGLISH_SHA256);
} }
@ -84,45 +80,69 @@ public class MnemonicCode {
} }
/** /**
* Encodes a 128, 192 or 256 bit seed into a list of words. * Convert mnemonic word list to seed.
*/ */
public List<String> encode(byte[] seed) throws MnemonicLengthException { public static byte[] toSeed(List<String> words, String passphrase) {
// 2. Make sure its length (L) is 128, 192 or 256 bits. // To create binary seed from mnemonic, we use HMAC-SHA512
int len = seed.length * 8; // function with string "mnemonic" + passphrase (in UTF-8) as
if (len != 128 && len != 192 && len != 256) // key and mnemonic sentence (again in UTF-8) as the
throw new MnemonicLengthException("seed not 128, 192 or 256 bits"); // 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
// 3. Encrypt input data 10000x with Rijndael (ECB mode). byte[] kk = new String("mnemonic" + passphrase).getBytes();
// Set key to SHA256 hash of string ("mnemonic" + user_password). byte[] mm = joinStringList(words).getBytes();
// Set block size to input size (that's why Rijndael is used, not AES).
byte[] indata = stretch(len, seed);
// Convert binary data to array of boolean for processing. for (int ii = 0; ii < HMAC_ROUNDS; ++ii)
boolean[] inarray = new boolean[indata.length * 8]; mm = HDUtils.hmacSha512(kk, mm);
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;
// 4-6 Compute checksum. return mm;
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]; * Convert arbitrary data to mnemonic word list.
for (int ii = 0; ii < inarray.length; ++ii) */
ee[ii] = inarray[ii]; public List<String> toMnemonic(byte[] entropy) throws MnemonicLengthException {
for (int ii = 0; ii < chksum.length; ++ii) if (entropy.length % 4 > 0)
ee[inarray.length + ii] = chksum[ii]; 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);
boolean[] entropyBits = bytesToBits(entropy);
int checksumLengthBits = entropyBits.length / 32;
// 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];
// 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.
// 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<String> words = new ArrayList<String>(); ArrayList<String> words = new ArrayList<String>();
int nwords = ee.length / 11; int nwords = concatBits.length / 11;
for (int ii = 0; ii < nwords; ++ii) { for (int ii = 0; ii < nwords; ++ii) {
int ndx = 0; int ndx = 0;
for (int kk = 0; kk < 11; ++kk) { for (int jj = 0; jj < 11; ++jj) {
ndx <<= 1; ndx <<= 1;
if (ee[(ii * 11) + kk]) if (concatBits[(ii * 11) + jj])
ndx |= 0x1; ndx |= 0x1;
} }
words.add(this.wordList.get(ndx)); words.add(this.wordList.get(ndx));
@ -132,18 +152,17 @@ public class MnemonicCode {
} }
/** /**
* Decodes a list of words into a seed value. * Check to see if a mnemonic word list is valid.
*/ */
public byte[] decode(List<String> words) throws MnemonicLengthException, MnemonicWordException, MnemonicChecksumException { public void check(List<String> words) throws MnemonicLengthException, MnemonicWordException, MnemonicChecksumException {
int nwords = words.size(); 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. // Look up all the words in the list and construct the
if (nwords != 12 && nwords != 18 && nwords != 24) // concatenation of the original entropy and the checksum.
throw new MnemonicLengthException("Mnemonic code not 12, 18 or 24 words"); //
int concatLenBits = words.size() * 11;
// 3. Figure out word indexes in a dictionary and output them as binary stream E. boolean[] concatBits = new boolean[concatLenBits];
int len = nwords * 11;
boolean[] ee = new boolean[len];
int wordindex = 0; int wordindex = 0;
for (String word : words) { for (String word : words) {
// Find the words index in the wordlist. // 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. // Set the next 11 bits to the value of the index.
for (int ii = 0; ii < 11; ++ii) 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; ++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 checksumLengthBits = concatLenBits / 33;
int bblen = (len / 33) * 32; int entropyLengthBits = concatLenBits - checksumLengthBits;
int cclen = len - bblen;
boolean[] bb = new boolean[bblen]; // Extract original entropy as bytes.
for (int ii = 0; ii < bblen; ++ii) byte[] entropy = new byte[entropyLengthBits / 8];
bb[ii] = ee[ii]; for (int ii = 0; ii < entropy.length; ++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)
for (int jj = 0; jj < 8; ++jj) for (int jj = 0; jj < 8; ++jj)
if (bb[(ii * 8) + jj]) if (concatBits[(ii * 8) + jj])
outdata[ii] |= 1 << (7 - jj); entropy[ii] |= 1 << (7 - jj);
// 9. Decrypt this data 10000x with Rijndael (ECB mode), // Take the digest of the entropy.
// use the same parameters as used in step 3 of encryption. byte[] hash = Sha256Hash.create(entropy).getBytes();
byte[] seed = unstretch(bblen, outdata); 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) { private static boolean[] bytesToBits(byte[] data) {
// 3. Encrypt input data 10000x with Rijndael (ECB mode). boolean[] bits = new boolean[data.length * 8];
// Set key to SHA256 hash of string ("mnemonic" + user_password). for (int ii = 0; ii < data.length; ++ii)
// Set block size to input size (that's why Rijndael is used, not AES). for (int jj = 0; jj < 8; ++jj)
byte[] mnemonic = {'m', 'n', 'e', 'm', 'o', 'n', 'i', 'c'}; bits[(ii * 8) + jj] = (data[ii] & (1 << (7 - jj))) != 0;
byte[] key = Sha256Hash.create(mnemonic).getBytes(); return bits;
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 byte[] unstretch(int len, byte[] data) { static private String joinStringList(List<String> list) {
// 9. Decrypt this data 10000x with Rijndael (ECB mode), StringBuilder sb = new StringBuilder();
// use the same parameters as used in step 3 of encryption. boolean first = true;
byte[] mnemonic = {'m', 'n', 'e', 'm', 'o', 'n', 'i', 'c'}; for (String item : list)
byte[] key = Sha256Hash.create(mnemonic).getBytes(); {
byte[] buffer = new byte[data.length]; if (first)
System.arraycopy(data, 0, buffer, 0, data.length); first = false;
RijndaelEngine cipher = new RijndaelEngine(len); else
cipher.init(false, new KeyParameter(key)); sb.append(" ");
for (int ii = 0; ii < 10000; ++ii) sb.append(item);
cipher.processBlock(buffer, 0, buffer, 0); }
return buffer; return sb.toString();
}
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;
} }
} }

View File

@ -29,80 +29,103 @@ import java.util.List;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
public class MnemonicCodeTest { 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[] = { String vectors[] = {
"00000000000000000000000000000000",
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
"cb5e7230ce8229de990674f6aa4288325fd4d8181f761734bd8b5cc944fedc2a4300e64422864b565352de7ffbc5ad0fafdf5344489f3a83e4a4bb5271cafaae",
"00000000000000000000000000000000", "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
"risk tiger venture dinner age assume float denial penalty hello game wing", "legal winner thank year wave sausage worth useful legal winner thank yellow",
"de1277934939d6969519f44b7b3757a905d7f635be41e1e88022c346bc52ad26c0a3e9578e73e9b89066873266f285a5891d27d28cb27fccfe26d92bbd7ee364",
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", "80808080808080808080808080808080",
"truth chase learn pretty right casual acoustic frozen betray main slogan method", "letter advice cage absurd amount doctor acoustic avoid letter advice cage above",
"8863bccef9cfffeacef1e4c6fc97bba8227ab0fc7e8e162be7467282689a13521ea364d7c4bc8cd241b59f53c5147a89c18a47248a96592ab9a2c1f1870b026c",
"80808080808080808080808080808080", "ffffffffffffffffffffffffffffffff",
"olive garment twenty drill people finish hat own usual level milk usage", "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong",
"7a29e57c7a1532af1bddb7e02b892cfccc6a57b74fe9784324ea89fab8a66dc64fde79c31166b159685116f4e93c1795496f20ffdc2d3a69d3439931dabde86e",
"ffffffffffffffffffffffffffffffff", "000000000000000000000000000000000000000000000000",
"laundry faint system client frog vanish plug shell slot cable large embrace", "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",
"c3e382025b6a22a901505cf393faea450eb6c4a5f2a8c8f0596285b2bd84688877a6cc7231420e2bbdd2428e62ed549a78fa215b3adafd8dea075dabfc704d5e",
"000000000000000000000000000000000000000000000000", "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
"giant twelve seat embark ostrich jazz leader lunch budget hover much weapon vendor build truth garden year list", "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will",
"c82666e40eb097bf6eb05fecd7dc2ddfb6bbdc6071900f4b3fd3c3e635db69aa2094f1f450c98e8dc6103aa72df635abdfcc3b6d6ec5261a9208a07a35a3f1c8",
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", "808080808080808080808080808080808080808080808080",
"awful faint gun mean fuel side slogan marine glad donkey velvet oyster movie real type digital dress federal", "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always",
"e90681c67c55504afadca009ce4042819341fa0e90300b6d32b4f2e8e8a6678ff7e7fc1da663ae194dc7a2ef7ec7b50112d1a5efce47bfd00c66eec82f2265b5",
"808080808080808080808080808080808080808080808080", "ffffffffffffffffffffffffffffffffffffffffffffffff",
"bless carpet daughter animal hospital pave faculty escape fortune song sign twin unknown bread mobile normal agent use", "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when",
"2a372547df962742942674170a7cef495ea0b97f4864de16d0f3ee82eb577ca1eca345e601cc2df7c626c5bc51c52c28a3b4294224b685c958c7450bee6769e6",
"ffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000",
"saddle curve flight drama client resemble venture arch will ordinary enrich clutch razor shallow trophy tumble dice outer", "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",
"0000000000000000000000000000000000000000000000000000000000000000", "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
"supreme army trim onion neglect coach squirrel spider device glass cabbage giant web digital floor able social magnet only fork fuel embrace salt fence", "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",
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", "8080808080808080808080808080808080808080808080808080808080808080",
"cloth video uncle switch year captain artist country adjust edit inherit ocean tennis soda baby express hospital forest panel actual profit boy spice elite", "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",
"8080808080808080808080808080808080808080808080808080808080808080", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"fence twin prize extra choose mask twist deny cereal quarter can power term ostrich leg staff nature nut swift sausage amateur aim script wisdom", "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",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "1083fa24dbb0afa4e7b327d23f666567",
"moon fiscal evidence exile rifle series neglect giant exclude banana glance frown kangaroo globe turtle hat fitness casual sudden select idle arctic best unlock", "awesome cabin matrix resist april sponsor paddle gossip split will off soon",
"467406b36a0176e40e013393e5ecef1f5b4019980b502eda9db1db06f7786e088b206f045f2bfcf93bd3b17a598335b078fcc5890115857ff741bd154b54f049",
"449ea2d7249c6e0d8d295424fb8894cf", "d8cbcd1ac2153ecd74048480c2732f637d642b21f0dd40df",
"choice barrel artefact cram increase sell veteran matrix mirror hollow walk pave", "sugar fury effort loud fault grit source mountain liar bean slim shoulder stone better march brick dolphin zero",
"f60180ea5047659cbb17ed6ef79c974de86c0170c7a1962b205329eb8fe9dcdd148615d35c515c4ec8da25f4cf54d5b7cd8cd5bf8dc4059df3a7900ca25f8306",
"75fc3f44a7ff8e2b8af05aa18bded3827a3796df406763dd", "2952f95cefe041616f6f379ab649cf8b702ecf8e4acceaebdda4cc50e2bf1d7b",
"crack outside teach chat praise client manual scorpion predict chalk decrease casino lunch garbage enable ball when bamboo", "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",
"1cce2f8c2c6a7f2d8473ebf1c32ce13b36737835d7a8768f44dcf96d64782c0e", "f5e82717078a6ddc538a03e825f91bed",
"muffin evoke all fiber night guard black quote neck expire dial tenant leisure have dragon neck notable peace captain insane nice uphold shine angry", "vote donkey shift audit plug until evolve document trial cool eight swarm",
"83dad22293225780a914083fc1a69bfe1d910f5b5962b0364820132a42ae1bd567a1fb4d5a19ad3d64539e38a7ee3d6429fac2b74e72b020913131c5eadb7db4",
"3daa82dd08bd144ec9fb9f77c6ece3d2", "16b59b6a426f2f302f73049a32ab8572394278982212357a",
"foil dawn net enroll turtle bird vault trumpet service fun immune unveil", "birth proud surround luggage very object saddle gauge olive next throw tongue neither detail gauge drastic cube strategy",
"38ceb07e0dad221f612631843be6ae44a650aaf789c8ebea9313e07498d7864385227d25c7a8268a5b850367eef31639632e9218acadead20980b864b1cd477e",
"9720239c0039f8446d44334daec325f3c24b3a490315d6d9", "95b6cb48c7bc9c2a54496ae3eea790824b57e52b9637058f084555bc1b809b2f",
"damp all desert dash insane pear debate easily soup enough goddess make friend plug violin pact wealth insect", "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",
"fe58c6644bc3fad95832d4400cea0cce208c8b19bb4734a26995440b7fae7600", "7f93397f750f70a26513de2732ed95ee",
"wet sniff asthma once gap enrich pumpkin define trust rude gesture keen grass fine emerge census immense smooth ritual spirit rescue problem beef choice", "legend oil garlic tube warfare eye nephew knock cheese number grace tackle",
"7f92ad63e4cdf4f15c23740556ad81e7f8cbd67cc672c93894c9c0d4fb171539eed5ab29f366570ed9940b816f45a539c3816f7ac19511794b752c5c1ec0e732",
"99fe82c94edadffe75e1cc64cbd7ada7", "14c29fe840dd1c9f05d392ba13e4e1466b32ed0726a15f89",
"thing real emerge verify domain cloud lens teach travel radio effort glad", "below belt wheel like spike exhibit blanket inch ring palace debate mimic rebel isolate broken stage garbage enhance",
"7bae6e54f8bad645f18f574b310bd3e6fde126dabcaf63a889940380e4798810e48c8151fc56bb2389c07498deacef025f03cbf8fc57ea3ec68f6421b0fcb649",
"4fd6e8d06d55b4700130f8f462f7f9bfc6188da83e3faadb", "cb30610d175ffeab8357d5190d31923997752a7f9815087bfcad5eb0b43f6468",
"diary opinion lobster code orange odor insane permit spirit evolve upset final antique grant friend dutch say enroll", "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",
"7a547fb59606e89ba88188013712946f6cb31c3e0ca606a7ee1ff23f57272c63", "a30b50a5439dcd1774f412ea5ec33403",
"layer owner legal stadium glance oyster element spell episode eager wagon stand pride old defense black print junior fade easy topic ready galaxy debris", "perfect fold citizen mango system merry stable liquid tumble voyage snack alter",
"aae175f26848370c4d5d3d0640597e2bf1b28e95908dd877259b3eac5d71ffe3140739a3ed80180f88159571df84441985620e6b2fb0696e5cba1aa7b8d10b98",
"e5fc62d20e0e5d9b2756e8d4d91cbb80", "70044da2175ad681d0ebbf2da83cf407eb9c8fd91fc0a8c9",
"flat make unit discover rifle armed unit acquire group panel nerve want", "hybrid carbon hammer concert pulp domain dry jewel color draft dial average right elevator good way potato energy",
"a3dffe3a31a2e949d1b04af7495a5b59db17e41d93b985feeaaae89260a9c86c6dcdf7cb32eaba61c2f4f0340f0f17d1ebb67af11657286b2ffd66ec4e05a8b7",
"d29be791a9e4b6a48ff79003dbf31d6afabdc4290a273765", "0e0bab4df9669b97ba3f75a50b2e92423bbe6e91a1b01dbbf3ba200a917c9106",
"absurd valve party disorder basket injury make blanket vintage ancient please random theory cart retire odor borrow belt", "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",
"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",
}; };
private MnemonicCode mc; private MnemonicCode mc;
@ -112,43 +135,42 @@ public class MnemonicCodeTest {
} }
@Test @Test
public void testEncodeVectors() throws Exception { public void testVectors() throws Exception {
for (int ii = 0; ii < vectors.length; ii += 2) { for (int ii = 0; ii < vectors.length; ii += 3) {
List<String> words = mc.encode(Hex.decode(vectors[ii])); String vecData = vectors[ii];
assertEquals(vectors[ii + 1], Joiner.on(' ').join(words)); String vecCode = vectors[ii+1];
} String vecSeed = vectors[ii+2];
}
@Test List<String> code = mc.toMnemonic(Hex.decode(vecData));
public void testDecodeVectors() throws Exception { byte[] seed = MnemonicCode.toSeed(code, "TREZOR");
for (int ii = 0; ii < vectors.length; ii += 2) {
byte[] seed = mc.decode(split(vectors[ii+1])); assertEquals(vecCode, Joiner.on(' ').join(code));
assertEquals(vectors[ii], new String(Hex.encode(seed))); assertEquals(vecSeed, new String(Hex.encode(seed)));
} }
} }
@Test(expected = MnemonicLengthException.class) @Test(expected = MnemonicLengthException.class)
public void testBadSeedLength() throws Exception { public void testBadEntropyLength() throws Exception {
byte[] seed = Hex.decode("7f7f7f7f7f7f7f7f7f7f7f7f7f7f"); byte[] entropy = Hex.decode("7f7f7f7f7f7f7f7f7f7f7f7f7f7f");
mc.encode(seed); mc.toMnemonic(entropy);
} }
@Test(expected = MnemonicLengthException.class) @Test(expected = MnemonicLengthException.class)
public void testBadLength() throws Exception { public void testBadLength() throws Exception {
List<String> words = split("risk tiger venture dinner age assume float denial penalty"); List<String> words = split("risk tiger venture dinner age assume float denial penalty hello");
mc.decode(words); mc.check(words);
} }
@Test(expected = MnemonicWordException.class) @Test(expected = MnemonicWordException.class)
public void testBadWord() throws Exception { public void testBadWord() throws Exception {
List<String> words = split("risk tiger venture dinner xyzzy assume float denial penalty hello game wing"); List<String> words = split("risk tiger venture dinner xyzzy assume float denial penalty hello game wing");
mc.decode(words); mc.check(words);
} }
@Test(expected = MnemonicChecksumException.class) @Test(expected = MnemonicChecksumException.class)
public void testBadChecksum() throws Exception { public void testBadChecksum() throws Exception {
List<String> words = split("risk tiger venture dinner age assume float denial penalty hello game game"); List<String> words = split("bless cloud wheel regular tiny venue bird web grief security dignity zoo");
mc.decode(words); mc.check(words);
} }
static public List<String> split(String words) { static public List<String> split(String words) {