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

Update to latest version of mnemonic code spec from the Trezor team.

This commit is contained in:
Ken Sedgwick 2013-12-17 15:02:05 -08:00 committed by Mike Hearn
parent 8cc1920fa2
commit 29e2af7ec0
7 changed files with 457 additions and 251 deletions

View File

@ -1,35 +0,0 @@
/*
* Copyright 2013 Ken Sedgwick
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.bitcoin.crypto;
/**
* Thrown when a list of MnemonicCode words fails the checksum check.
*/
@SuppressWarnings("serial")
public class MnemonicChecksumException extends Exception {
public MnemonicChecksumException(String msg) {
super(msg);
}
public MnemonicChecksumException(Exception e) {
super(e);
}
public MnemonicChecksumException(String msg, Exception e) {
super(msg, e);
}
}

View File

@ -25,6 +25,7 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -41,7 +42,7 @@ public class MnemonicCode {
public static String BIP39_ENGLISH_SHA256 = "ad90bf3beb7b0eb7e5acd74727dc0da96e0a280a258354e7293fb7e211ac03db"; public static String BIP39_ENGLISH_SHA256 = "ad90bf3beb7b0eb7e5acd74727dc0da96e0a280a258354e7293fb7e211ac03db";
private static final int HMAC_ROUNDS = 10000; private static final int PBKDF2_ROUNDS = 4096;
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,36 +85,73 @@ public class MnemonicCode {
*/ */
public static byte[] toSeed(List<String> words, String passphrase) { public static byte[] toSeed(List<String> words, String passphrase) {
// To create binary seed from mnemonic, we use HMAC-SHA512 // To create binary seed from mnemonic, we use PBKDF2 function
// function with string "mnemonic" + passphrase (in UTF-8) as // with mnemonic sentence (in UTF-8) used as a password and
// key and mnemonic sentence (again in UTF-8) as the // string "mnemonic" + passphrase (again in UTF-8) used as a
// message. We perform 10000 HMAC rounds and use the final // salt. Iteration count is set to 4096 and HMAC-SHA512 is
// result as the binary seed. // used as a pseudo-random function. Desired length of the
// derived key is 512 bits (= 64 bytes).
// //
// Pseudocode: String pass = joinStringList(words);
String salt = new String("mnemonic" + passphrase);
byte[] hash = PBKDF2SHA512.derive(pass, salt, PBKDF2_ROUNDS, 64);
return hash;
}
/**
* Convert mnemonic word list to original entropy value.
*/
public byte[] toEntropy(List<String> words) throws MnemonicException.MnemonicLengthException, MnemonicException.MnemonicWordException, MnemonicException.MnemonicChecksumException {
if (words.size() % 3 > 0)
throw new MnemonicException.MnemonicLengthException("Word list size must be multiple of three words.");
// Look up all the words in the list and construct the
// concatenation of the original entropy and the checksum.
// //
// K = "mnemonic" + passphrase int concatLenBits = words.size() * 11;
// M = mnemonic_sentence boolean[] concatBits = new boolean[concatLenBits];
// for i in 1 ... 10000 do int wordindex = 0;
// M = hmac_sha512(K, M) for (String word : words) {
// done // Find the words index in the wordlist.
// seed = M int ndx = Collections.binarySearch(this.wordList, word);
if (ndx < 0)
throw new MnemonicException.MnemonicWordException("\"" + word + "\" invalid", word);
byte[] kk = new String("mnemonic" + passphrase).getBytes(); // Set the next 11 bits to the value of the index.
byte[] mm = joinStringList(words).getBytes(); for (int ii = 0; ii < 11; ++ii)
concatBits[(wordindex * 11) + ii] = (ndx & (1 << (10 - ii))) != 0;
++wordindex;
}
for (int ii = 0; ii < HMAC_ROUNDS; ++ii) int checksumLengthBits = concatLenBits / 33;
mm = HDUtils.hmacSha512(kk, mm); int entropyLengthBits = concatLenBits - checksumLengthBits;
return mm; // 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 (concatBits[(ii * 8) + jj])
entropy[ii] |= 1 << (7 - jj);
// Take the digest of the entropy.
byte[] hash = Sha256Hash.create(entropy).getBytes();
boolean[] hashBits = bytesToBits(hash);
// Check all the checksum bits.
for (int ii = 0; ii < checksumLengthBits; ++ii)
if (concatBits[entropyLengthBits + ii] != hashBits[ii])
throw new MnemonicException.MnemonicChecksumException("checksum error");
return entropy;
} }
/** /**
* Convert entropy data to mnemonic word list. * Convert entropy data to mnemonic word list.
*/ */
public List<String> toMnemonic(byte[] entropy) throws MnemonicLengthException { public List<String> toMnemonic(byte[] entropy) throws MnemonicException.MnemonicLengthException {
if (entropy.length % 4 > 0) if (entropy.length % 4 > 0)
throw new MnemonicLengthException("entropy length not multiple of 32 bits"); throw new MnemonicException.MnemonicLengthException("entropy length not multiple of 32 bits");
// We take initial entropy of ENT bits and compute its // We take initial entropy of ENT bits and compute its
// checksum by taking first ENT / 32 bits of its SHA256 hash. // checksum by taking first ENT / 32 bits of its SHA256 hash.
@ -154,46 +192,8 @@ public class MnemonicCode {
/** /**
* Check to see if a mnemonic word list is valid. * Check to see if a mnemonic word list is valid.
*/ */
public void check(List<String> words) throws MnemonicLengthException, MnemonicWordException, MnemonicChecksumException { public void check(List<String> words) throws MnemonicException.MnemonicLengthException, MnemonicException.MnemonicWordException, MnemonicException.MnemonicChecksumException {
if (words.size() % 3 > 0) toEntropy(words);
throw new MnemonicLengthException("Word list size must be multiple of three words.");
// 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.
int ndx = Collections.binarySearch(this.wordList, word);
if (ndx < 0)
throw new MnemonicWordException("\"" + word + "\" invalid", word);
// Set the next 11 bits to the value of the index.
for (int ii = 0; ii < 11; ++ii)
concatBits[(wordindex * 11) + ii] = (ndx & (1 << (10 - ii))) != 0;
++wordindex;
}
int checksumLengthBits = concatLenBits / 33;
int entropyLengthBits = concatLenBits - checksumLengthBits;
// 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 (concatBits[(ii * 8) + jj])
entropy[ii] |= 1 << (7 - jj);
// Take the digest of the entropy.
byte[] hash = Sha256Hash.create(entropy).getBytes();
boolean[] hashBits = bytesToBits(hash);
// Check all the checksum bits.
for (int ii = 0; ii < checksumLengthBits; ++ii)
if (concatBits[entropyLengthBits + ii] != hashBits[ii])
throw new MnemonicChecksumException("checksum error");
} }
private static boolean[] bytesToBits(byte[] data) { private static boolean[] bytesToBits(byte[] data) {

View File

@ -0,0 +1,87 @@
/*
* Copyright 2013 Ken Sedgwick
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.bitcoin.crypto;
/**
* Exceptions thrown by the MnemonicCode module.
*/
@SuppressWarnings("serial")
public class MnemonicException extends Exception {
public MnemonicException(String msg) {
super(msg);
}
public MnemonicException(Exception ex) {
super(ex);
}
public MnemonicException(String msg, Exception ex) {
super(msg, ex);
}
/**
* Thrown when an argument to MnemonicCode is the wrong length.
*/
public static class MnemonicLengthException extends MnemonicException {
public MnemonicLengthException(String msg) {
super(msg);
}
public MnemonicLengthException(Exception ex) {
super(ex);
}
public MnemonicLengthException(String msg, Exception ex) {
super(msg, ex);
}
}
/**
* Thrown when a list of MnemonicCode words fails the checksum check.
*/
public static class MnemonicChecksumException extends MnemonicException {
public MnemonicChecksumException(String msg) {
super(msg);
}
public MnemonicChecksumException(Exception ex) {
super(ex);
}
public MnemonicChecksumException(String msg, Exception ex) {
super(msg, ex);
}
}
/**
* Thrown when a word is encountered which is not in the MnemonicCode's word list.
*/
public static class MnemonicWordException extends MnemonicException {
/** Contains the word that was not found in the word list. */
public String badWord;
public MnemonicWordException(String msg, String badWord) {
super(msg);
this.badWord = badWord;
}
public MnemonicWordException(String badWord, Exception ex) {
super(ex);
this.badWord = badWord;
}
public MnemonicWordException(String msg, String badWord, Exception ex) {
super(msg, ex);
this.badWord = badWord;
}
public String getBadWord() {
return badWord;
}
}
}

View File

@ -1,35 +0,0 @@
/*
* Copyright 2013 Ken Sedgwick
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.bitcoin.crypto;
/**
* Thrown when an argument to MnemonicCode is the wrong length.
*/
@SuppressWarnings("serial")
public class MnemonicLengthException extends Exception {
public MnemonicLengthException(String msg) {
super(msg);
}
public MnemonicLengthException(Exception e) {
super(e);
}
public MnemonicLengthException(String msg, Exception e) {
super(msg, e);
}
}

View File

@ -1,45 +0,0 @@
/*
* Copyright 2013 Ken Sedgwick
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.bitcoin.crypto;
/**
* Thrown when a word is encountered which is not in the MnemonicCode's word list.
*/
@SuppressWarnings("serial")
public class MnemonicWordException extends Exception {
/** Contains the word that was not found in the word list. */
public String badWord;
public MnemonicWordException(String msg, String badWord) {
super(msg);
this.badWord = badWord;
}
public MnemonicWordException(String badWord, Exception e) {
super(e);
this.badWord = badWord;
}
public MnemonicWordException(String msg, String badWord, Exception e) {
super(msg, e);
this.badWord = badWord;
}
public String getBadWord() {
return badWord;
}
}

View File

@ -0,0 +1,232 @@
/*
* Copyright (c) 2012 Cole Barnes [cryptofreek{at}gmail{dot}com]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* -----------------------------------------------------------------------------
*
* This is a clean-room implementation of PBKDF2 using RFC 2898 as a reference.
*
* RFC 2898:
* http://tools.ietf.org/html/rfc2898#section-5.2
*
* This code passes all RFC 6070 test vectors:
* http://tools.ietf.org/html/rfc6070
*
* The function "nativeDerive()" is supplied as an example of the native Java
* PBKDF2WithHmacSHA1 implementation. It is used for benchmarking and
* comparison only.
*
* The functions "fromHex()" and "toHex()" came from some message board
* somewhere. No license was included.
*
* http://cryptofreek.org/2012/11/29/pbkdf2-pure-java-implementation/
* Modified to use SHA-512 - Ken Sedgwick ken@bonsai.com
*/
package com.google.bitcoin.crypto;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.spec.KeySpec;
import java.util.Formatter;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class PBKDF2SHA512
{
/* START RFC 2898 IMPLEMENTATION */
public static byte[] derive(String P, String S, int c, int dkLen)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try
{
int hLen = 20;
if (dkLen > ((Math.pow(2, 32)) - 1) * hLen)
{
System.out.println("derived key too long");
}
else
{
int l = (int) Math.ceil((double) dkLen / (double) hLen);
// int r = dkLen - (l-1)*hLen;
for (int i = 1; i <= l; i++)
{
byte[] T = F(P, S, c, i);
baos.write(T);
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
byte[] baDerived = new byte[dkLen];
System.arraycopy(baos.toByteArray(), 0, baDerived, 0, baDerived.length);
return baDerived;
}
private static byte[] F(String P, String S, int c, int i) throws Exception
{
byte[] U_LAST = null;
byte[] U_XOR = null;
SecretKeySpec key = new SecretKeySpec(P.getBytes("UTF-8"), "HmacSHA512");
Mac mac = Mac.getInstance(key.getAlgorithm());
mac.init(key);
for (int j = 0; j < c; j++)
{
if (j == 0)
{
byte[] baS = S.getBytes("UTF-8");
byte[] baI = INT(i);
byte[] baU = new byte[baS.length + baI.length];
System.arraycopy(baS, 0, baU, 0, baS.length);
System.arraycopy(baI, 0, baU, baS.length, baI.length);
U_XOR = mac.doFinal(baU);
U_LAST = U_XOR;
mac.reset();
}
else
{
byte[] baU = mac.doFinal(U_LAST);
mac.reset();
for (int k = 0; k < U_XOR.length; k++)
{
U_XOR[k] = (byte) (U_XOR[k] ^ baU[k]);
}
U_LAST = baU;
}
}
return U_XOR;
}
private static byte[] INT(int i)
{
ByteBuffer bb = ByteBuffer.allocate(4);
bb.order(ByteOrder.BIG_ENDIAN);
bb.putInt(i);
return bb.array();
}
/* END RFC 2898 IMPLEMENTATION */
/* START HELPER FUNCTIONS */
private static String toHex(byte[] ba)
{
String strHex = null;
if (ba != null)
{
StringBuilder sb = new StringBuilder(ba.length * 2);
Formatter formatter = new Formatter(sb);
for (byte b : ba)
{
formatter.format("%02x", b);
}
formatter.close();
strHex = sb.toString().toLowerCase();
}
return strHex;
}
private static byte[] nativeDerive(String strPassword, String strSalt, int nIterations, int nKeyLen)
{
byte[] baDerived = null;
try
{
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec ks = new PBEKeySpec(strPassword.toCharArray(), strSalt.getBytes("UTF-8"), nIterations, nKeyLen * 8);
SecretKey s = f.generateSecret(ks);
baDerived = s.getEncoded();
}
catch (Exception e)
{
e.printStackTrace();
}
return baDerived;
}
/* END HELPER FUNCTIONS */
public static void runTestVector(String P, String S, int c, int dkLen, String strExpectedDk)
{
System.out.println("Input:");
System.out.println(" P = \"" + P + "\"");
System.out.println(" S = \"" + S + "\"");
System.out.println(" c = " + c);
System.out.println(" dkLen = " + dkLen);
System.out.println();
long nStartDk = System.nanoTime();
byte[] DK = derive(P, S, c, dkLen);
long nStopDk = System.nanoTime();
long nStartDkNative = System.nanoTime();
byte[] DK_NATIVE = nativeDerive(P, S, c, dkLen);
long nStopDkNative = System.nanoTime();
System.out.println("Output:");
System.out.println(" DK = " + toHex(DK));
System.out.println(" DK_NATIVE = " + toHex(DK_NATIVE));
System.out.println(" DK_EXPECTED = " + strExpectedDk.replaceAll(" ", ""));
System.out.println();
System.out.println("Duration [my implementation]: " + (nStopDk - nStartDk) + " ns" );
System.out.println("Duration [native implementation]: " + (nStopDkNative - nStartDkNative) + " ns" );
System.out.println("---------------------------------------------------------------");
System.out.println();
}
public static void RFC6070()
{
runTestVector("password", "salt", 1, 20, "0c 60 c8 0f 96 1f 0e 71 f3 a9 b5 24 af 60 12 06 2f e0 37 a6");
runTestVector("password", "salt", 2, 20, "ea 6c 01 4d c7 2d 6f 8c cd 1e d9 2a ce 1d 41 f0 d8 de 89 57");
runTestVector("password", "salt", 4096, 20, "4b 00 79 01 b7 65 48 9a be ad 49 d9 26 f7 21 d0 65 a4 29 c1");
runTestVector("password", "salt", 16777216, 20, "ee fe 3d 61 cd 4d a4 e4 e9 94 5b 3d 6b a2 15 8c 26 34 e9 84");
runTestVector("passwordPASSWORDpassword", "saltSALTsaltSALTsaltSALTsaltSALTsalt", 4096, 25, "3d 2e ec 4f e4 1c 84 9b 80 c8 d8 36 62 c0 e4 4a 8b 29 1a 96 4c f2 f0 70 38");
runTestVector("pass\0word", "sa\0lt", 4096, 16, "56 fa 6a a7 55 48 09 9d cc 37 d7 f0 34 25 e0 c3");
}
public static void main(String[] args)
{
RFC6070();
}
}

View File

@ -33,99 +33,99 @@ public class MnemonicCodeTest {
String vectors[] = { String vectors[] = {
"00000000000000000000000000000000", "00000000000000000000000000000000",
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
"cb5e7230ce8229de990674f6aa4288325fd4d8181f761734bd8b5cc944fedc2a4300e64422864b565352de7ffbc5ad0fafdf5344489f3a83e4a4bb5271cafaae", "3d39670caaa237f5fb2999474413733b59d9dfe12b2cccfe878069a2f605ae467a669619a0a45c7b3378c4c812b80be677c1b8f8f60db9383f1ed265c45eb41c",
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
"legal winner thank year wave sausage worth useful legal winner thank yellow", "legal winner thank year wave sausage worth useful legal winner thank yellow",
"de1277934939d6969519f44b7b3757a905d7f635be41e1e88022c346bc52ad26c0a3e9578e73e9b89066873266f285a5891d27d28cb27fccfe26d92bbd7ee364", "34eb04e0d4d60d00217f1c0150d8b0d6ffc6086e365a8a94fcceae8614e38274e719ebe7a693356426d1c62fdf90c84eaaac3d920743f3e79e0970a295886a08",
"80808080808080808080808080808080", "80808080808080808080808080808080",
"letter advice cage absurd amount doctor acoustic avoid letter advice cage above", "letter advice cage absurd amount doctor acoustic avoid letter advice cage above",
"8863bccef9cfffeacef1e4c6fc97bba8227ab0fc7e8e162be7467282689a13521ea364d7c4bc8cd241b59f53c5147a89c18a47248a96592ab9a2c1f1870b026c", "acd45eb0b06e53d2e0fa6d1b3e8b3c33f200d2be6013fc5f8796c8c1d238552a615a01f325d78a10e633991d92f1236e21c24afe7c679fa1ecbc67fe71a5337d",
"ffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffff",
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong",
"7a29e57c7a1532af1bddb7e02b892cfccc6a57b74fe9784324ea89fab8a66dc64fde79c31166b159685116f4e93c1795496f20ffdc2d3a69d3439931dabde86e", "d20f4e7902c3fd2f7716a533b131042036e6df2e44b14c8d65eb57403d002e4a12fa9be5325b257637ad1209e850188ffb061b3033a315b236ffc70c4ab4ea96",
"000000000000000000000000000000000000000000000000", "000000000000000000000000000000000000000000000000",
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent", "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",
"c3e382025b6a22a901505cf393faea450eb6c4a5f2a8c8f0596285b2bd84688877a6cc7231420e2bbdd2428e62ed549a78fa215b3adafd8dea075dabfc704d5e", "55fb6d9683e16f71025b73efa1a297d522010e37a4fc5b54770b09173e4f7fed85f83b075142965015d17c8f7a365e589ac943ed83cfbf76dcaf301c6c53b9d6",
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
"legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will", "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will",
"c82666e40eb097bf6eb05fecd7dc2ddfb6bbdc6071900f4b3fd3c3e635db69aa2094f1f450c98e8dc6103aa72df635abdfcc3b6d6ec5261a9208a07a35a3f1c8", "a2455beee8e73686d56f92979ed8a69011772f68e8329a437f47e55d79eaeec25afc2ac5ff636ac8578161a09a2ea690747575653f9d91016b09b71227a53791",
"808080808080808080808080808080808080808080808080", "808080808080808080808080808080808080808080808080",
"letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always", "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always",
"e90681c67c55504afadca009ce4042819341fa0e90300b6d32b4f2e8e8a6678ff7e7fc1da663ae194dc7a2ef7ec7b50112d1a5efce47bfd00c66eec82f2265b5", "dd53ab268c524bad304e3decf4298ba1413a785bc866c2118988d1634bddf5a1301718191b247761060bc6dce3c62f833b22c062e51e7508fc04201cd7f8515b",
"ffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffff",
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when", "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when",
"2a372547df962742942674170a7cef495ea0b97f4864de16d0f3ee82eb577ca1eca345e601cc2df7c626c5bc51c52c28a3b4294224b685c958c7450bee6769e6", "f0747118b52dcd33d383e5f3d6dddc94f66accd38b6d48f8a07fed9752fa8457dcb40bba5f40399814dcbd1a3f5cfaead1cf26c72268d1aa71561611421d4aaf",
"0000000000000000000000000000000000000000000000000000000000000000", "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", "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", "681088fbeaf5e4c29de23f55967a942bcb3400dc5b6e507477dfdcb493d921902352e015cd1235279c61ddf26b9536e6cf87fccb3cf2e142db44689f78ccad12",
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", "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", "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", "0881d39ee42046f433bff48ae0520128b79c0b9ff376c805b384340bd73767cdd8d1583606bafe2879be99ff44847e83057dbe521415a6067e8e4d7334e35a6c",
"8080808080808080808080808080808080808080808080808080808080808080", "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", "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", "63362d97a394e67674c5e337b8b8221f242cdf84eac057e08e8d522ac458e0c8b577c487bf82255d41e2ecfaf9326be2b3b31534a990608950b05a5d849e0603",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "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", "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", "3c22432ca0b5c9572a631b1068749bb6b663a78390322ab4b763c3ee21d81a7774f8ecdbc6ec73932b961c7374c7d14156cd61f81e433de3953fad23ea407e58",
"1083fa24dbb0afa4e7b327d23f666567", "b71edfa823c78c5f8d6cade654e298b4",
"awesome cabin matrix resist april sponsor paddle gossip split will off soon", "require want tube elegant juice cool cup noble town poem plate harsh",
"467406b36a0176e40e013393e5ecef1f5b4019980b502eda9db1db06f7786e088b206f045f2bfcf93bd3b17a598335b078fcc5890115857ff741bd154b54f049", "b424ec5ee4eb4127c3ce0722572c456a47084cda617a26fb647f9a02f43fcc3d11a84dadb6e892ec2641d8592149018a39c8bd9c1cadc969188da4b820bf3371",
"d8cbcd1ac2153ecd74048480c2732f637d642b21f0dd40df", "8ffd2ff2b2fa4dd7c61f019698893918dfd72c0ea37d0027",
"sugar fury effort loud fault grit source mountain liar bean slim shoulder stone better march brick dolphin zero", "morning truly witness grass pill typical blur then notable session exact coyote word noodle dentist hurry ability dignity",
"f60180ea5047659cbb17ed6ef79c974de86c0170c7a1962b205329eb8fe9dcdd148615d35c515c4ec8da25f4cf54d5b7cd8cd5bf8dc4059df3a7900ca25f8306", "d20f3879eebf9858443a6ca0f04f568aac06e74fb542475824101855133ed5f54d0805d0fc9bc0e6f03c1f820ee01a462b291caeba7cbf659b670afd00e0db42",
"2952f95cefe041616f6f379ab649cf8b702ecf8e4acceaebdda4cc50e2bf1d7b", "9b655bc7d4e380bad6148df1cf8d8a6b023b781b05c2e823c5b5dd05139d30f6",
"citizen oak fire thank advice radar sad tragic one rather initial black actual guitar decrease flower turtle galaxy hard obvious athlete garbage invest have", "opinion client vehicle prefer day frost flame museum vault ladder glad stock casual rose hire reunion trial bullet hope ring eye soldier sense stereo",
"eff4b6a15bb55fcf4bbfa2b3b9e24e7dc4bed8319ef7703f1786d472c73666922925778eaa5a06f8a26d2c7e7240be746fd69edfaf197e0dae12d7e0b550cfc8", "3c6d3843502c73e29d7e9ddc3e81354d3467121244e0b713cdb8e0e76c357d6db2f738c43bc2550bb85c9d5c900fbd7fbba10c76e7a8b920e0d450ef5a6750eb",
"f5e82717078a6ddc538a03e825f91bed", "bdc823540d4ff8d372d29f4265fe5cf8",
"vote donkey shift audit plug until evolve document trial cool eight swarm", "saddle donate steak box zebra have slender fault draw copper now various",
"83dad22293225780a914083fc1a69bfe1d910f5b5962b0364820132a42ae1bd567a1fb4d5a19ad3d64539e38a7ee3d6429fac2b74e72b020913131c5eadb7db4", "0c960d7d54415e5d3ae41ea805bc961f14d7031ea0b92058c19cecab7b7e51f82a4d987dbe63dfaed30805bd746d93e645fd5dffea8354b90688711d6fa570bd",
"16b59b6a426f2f302f73049a32ab8572394278982212357a", "53de16bcad3ea92c691f5d069de6f1870a9adfe764b1aa33",
"birth proud surround luggage very object saddle gauge olive next throw tongue neither detail gauge drastic cube strategy", "fatigue vague quality foil tunnel normal piece two alley upset round asthma predict husband outside normal pretty oppose",
"38ceb07e0dad221f612631843be6ae44a650aaf789c8ebea9313e07498d7864385227d25c7a8268a5b850367eef31639632e9218acadead20980b864b1cd477e", "fa384e2775768bc4905a5f7a014cdb62bbf77f751a039d5a124191099970dafc02e679232aeab769cb1582038907829baa3995255be01fe042b462539b26f1af",
"95b6cb48c7bc9c2a54496ae3eea790824b57e52b9637058f084555bc1b809b2f", "29932ac2fbba5682ef711bd7429068ac7f90fe6132c9f1a722e8441240cb9a96",
"noble rent split month six benefit eye coil token inside tomorrow afraid rely verb purity shoulder airport joke bacon problem script scare hole trumpet", "civil off radar wash pistol door saddle casino struggle behind boss flight weekend left luggage float vast decorate ring market catch grape heart sport",
"e33e3d32e467877596a18ac60050488a0ec1557fda6bf95bad3d33d964c5e99dcd97d378403cc2723ed1c85c12b42bc59f15458d970d7a9d015f556109c146b0", "777122f7b4e1dd21d385e71b9811d7d8d2adffcb8d0cebb667c93346156d4baec6b23adc519515742a4f6a712cbbef3e03e528f912498a3104206f33259d8823",
"7f93397f750f70a26513de2732ed95ee", "498e5f6f8039399a5e307406f448f846",
"legend oil garlic tube warfare eye nephew knock cheese number grace tackle", "end indicate swim about near snake juice attend alone pelican dignity mimic",
"7f92ad63e4cdf4f15c23740556ad81e7f8cbd67cc672c93894c9c0d4fb171539eed5ab29f366570ed9940b816f45a539c3816f7ac19511794b752c5c1ec0e732", "551e36122e37276b2840ab5c71b2a33888a6efa501a47b8323b313bf03f6dec1fb3d2ef11bfb35ec41580c5c0be3f881907fe2a3154b6e0ba8e05177b8e928a6",
"14c29fe840dd1c9f05d392ba13e4e1466b32ed0726a15f89", "7d205696a272051f60c14f146808a2c83bb0f7ef95037b4e",
"below belt wheel like spike exhibit blanket inch ring palace debate mimic rebel isolate broken stage garbage enhance", "large actor pizza eager cake moral loan clarify behave doctor chunk motor roast know salad parrot kitten item",
"7bae6e54f8bad645f18f574b310bd3e6fde126dabcaf63a889940380e4798810e48c8151fc56bb2389c07498deacef025f03cbf8fc57ea3ec68f6421b0fcb649", "d8bb3b18a5b9844ae1117b680986d93074efc691d9084dc5ef9d3570d9f9bcaffc9c319fb53d6c728564ee0e4494953245607688adb2efbbe77b912835f40e8c",
"cb30610d175ffeab8357d5190d31923997752a7f9815087bfcad5eb0b43f6468", "d10c1c341f19c89a5c8c505cabfa027422ea6cb4cb6c28003898676ef7611cd2",
"sleep loan drive concert zoo fiction ask wide boil hat goose industry jar news wrist actor anchor that clip runway area cabbage museum abuse", "speed genius artist dilemma orient essay impulse meat frequent garlic letter tribe concert curve spring horn chimney achieve champion solution urge rack infant furnace",
"b922030609e7626696b9cf5ca4c06cd99290be30b1052770f6a60c5f26532d178f287a4285d7a2add2845dc89a816b26fdba1c830067d130740f64c0ab5cfbe1", "a2f0b91b238ca1df0c4ac89eaa628701800f70732a1952982f3021e94bf7c3aafa0bb51bbdcc210f1e433d3e740660d1e4053c12edfdc1eb77ceafbe6a32723e",
"a30b50a5439dcd1774f412ea5ec33403", "719ddb2a59b10066c74d26954da2dc2e",
"perfect fold citizen mango system merry stable liquid tumble voyage snack alter", "immense uphold skin recall avoid cricket brush pill next home require friend",
"aae175f26848370c4d5d3d0640597e2bf1b28e95908dd877259b3eac5d71ffe3140739a3ed80180f88159571df84441985620e6b2fb0696e5cba1aa7b8d10b98", "9be5826432f915ace1f77827028a0ab7098dd3776304aa17e03698b09e5e93914e3e3f0d3d38c9b265ec2cc4bba1da8c9d9a97c8a3b1ec05add8e34a1c676490",
"70044da2175ad681d0ebbf2da83cf407eb9c8fd91fc0a8c9", "5746a7704cd3b9dc0a022f2a9025618d813c8ca873b8008c",
"hybrid carbon hammer concert pulp domain dry jewel color draft dial average right elevator good way potato energy", "firm cry swing often describe unlock chimney echo clever license flash brand because edge peace jacket above gentle",
"a3dffe3a31a2e949d1b04af7495a5b59db17e41d93b985feeaaae89260a9c86c6dcdf7cb32eaba61c2f4f0340f0f17d1ebb67af11657286b2ffd66ec4e05a8b7", "06f5b2d7af46c2e7b7b99c87aa52d17c27925ba685ba5e572b4e978da2adee44fe7d5726966cd2ee1ae47b65790baa4b3f7952505b0b45d9c8673a29e6a57ffc",
"0e0bab4df9669b97ba3f75a50b2e92423bbe6e91a1b01dbbf3ba200a917c9106", "8fdda21cc1b39a6fb264ab3c007995cf9ed9efcfebc07652951e769b6bcbfad4",
"asthma front square version have slim trophy upgrade pink floor pig love room dance educate current buffalo test update divorce poverty salad dune scheme", "more unfair mango lock defy daughter sister nice despair adult grace palace unique wave distance job iron net elegant unfold repeat tourist twice number",
"2eb4d85fbd8deaf9b06bf9cdb3e5f36e8da040d110312075eb32e776fc8e505b94be3e63c1525ad41f5e5968a263853001dc7c40ea3af8e8b0cfb7effd5f408c", "c37ae2956e1396b03a722d647dbcf2672cb8db1493c2c136ab9370a62c19c8f45023a635b7c2646a9748ff28c7ef64d93d089c315d390c50cee19cb46a01927f"
}; };
private MnemonicCode mc; private MnemonicCode mc;
@ -143,31 +143,33 @@ public class MnemonicCodeTest {
List<String> code = mc.toMnemonic(Hex.decode(vecData)); List<String> code = mc.toMnemonic(Hex.decode(vecData));
byte[] seed = MnemonicCode.toSeed(code, "TREZOR"); byte[] seed = MnemonicCode.toSeed(code, "TREZOR");
byte[] entropy = mc.toEntropy(split(vecCode));
assertEquals(vecCode, Joiner.on(' ').join(code)); assertEquals(vecCode, Joiner.on(' ').join(code));
assertEquals(vecSeed, new String(Hex.encode(seed))); assertEquals(vecSeed, new String(Hex.encode(seed)));
assertEquals(vecData, new String(Hex.encode(entropy)));
} }
} }
@Test(expected = MnemonicLengthException.class) @Test(expected = MnemonicException.MnemonicLengthException.class)
public void testBadEntropyLength() throws Exception { public void testBadEntropyLength() throws Exception {
byte[] entropy = Hex.decode("7f7f7f7f7f7f7f7f7f7f7f7f7f7f"); byte[] entropy = Hex.decode("7f7f7f7f7f7f7f7f7f7f7f7f7f7f");
mc.toMnemonic(entropy); mc.toMnemonic(entropy);
} }
@Test(expected = MnemonicLengthException.class) @Test(expected = MnemonicException.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 hello"); List<String> words = split("risk tiger venture dinner age assume float denial penalty hello");
mc.check(words); mc.check(words);
} }
@Test(expected = MnemonicWordException.class) @Test(expected = MnemonicException.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.check(words); mc.check(words);
} }
@Test(expected = MnemonicChecksumException.class) @Test(expected = MnemonicException.MnemonicChecksumException.class)
public void testBadChecksum() throws Exception { public void testBadChecksum() throws Exception {
List<String> words = split("bless cloud wheel regular tiny venue bird web grief security dignity zoo"); List<String> words = split("bless cloud wheel regular tiny venue bird web grief security dignity zoo");
mc.check(words); mc.check(words);