diff --git a/core/src/main/java/com/google/bitcoin/crypto/MnemonicChecksumException.java b/core/src/main/java/com/google/bitcoin/crypto/MnemonicChecksumException.java deleted file mode 100644 index 43e69dfb..00000000 --- a/core/src/main/java/com/google/bitcoin/crypto/MnemonicChecksumException.java +++ /dev/null @@ -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); - } -} 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 eacd93e5..98ed9d9a 100644 --- a/core/src/main/java/com/google/bitcoin/crypto/MnemonicCode.java +++ b/core/src/main/java/com/google/bitcoin/crypto/MnemonicCode.java @@ -25,6 +25,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -41,7 +42,7 @@ public class MnemonicCode { 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 { this(MnemonicCode.class.getResourceAsStream("mnemonic/wordlist/english.txt"), BIP39_ENGLISH_SHA256); @@ -84,36 +85,73 @@ public class MnemonicCode { */ 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. + // To create binary seed from mnemonic, we use PBKDF2 function + // with mnemonic sentence (in UTF-8) used as a password and + // string "mnemonic" + passphrase (again in UTF-8) used as a + // salt. Iteration count is set to 4096 and HMAC-SHA512 is + // 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 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 - // M = mnemonic_sentence - // for i in 1 ... 10000 do - // M = hmac_sha512(K, M) - // done - // seed = M + 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 MnemonicException.MnemonicWordException("\"" + word + "\" invalid", word); - byte[] kk = new String("mnemonic" + passphrase).getBytes(); - byte[] mm = joinStringList(words).getBytes(); + // 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; + } - for (int ii = 0; ii < HMAC_ROUNDS; ++ii) - mm = HDUtils.hmacSha512(kk, mm); + int checksumLengthBits = concatLenBits / 33; + 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. */ - public List toMnemonic(byte[] entropy) throws MnemonicLengthException { + public List toMnemonic(byte[] entropy) throws MnemonicException.MnemonicLengthException { 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 // 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. */ - 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."); - - // 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"); + public void check(List words) throws MnemonicException.MnemonicLengthException, MnemonicException.MnemonicWordException, MnemonicException.MnemonicChecksumException { + toEntropy(words); } private static boolean[] bytesToBits(byte[] data) { diff --git a/core/src/main/java/com/google/bitcoin/crypto/MnemonicException.java b/core/src/main/java/com/google/bitcoin/crypto/MnemonicException.java new file mode 100644 index 00000000..6245696a --- /dev/null +++ b/core/src/main/java/com/google/bitcoin/crypto/MnemonicException.java @@ -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; + } + } +} diff --git a/core/src/main/java/com/google/bitcoin/crypto/MnemonicLengthException.java b/core/src/main/java/com/google/bitcoin/crypto/MnemonicLengthException.java deleted file mode 100644 index b863f63d..00000000 --- a/core/src/main/java/com/google/bitcoin/crypto/MnemonicLengthException.java +++ /dev/null @@ -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); - } -} diff --git a/core/src/main/java/com/google/bitcoin/crypto/MnemonicWordException.java b/core/src/main/java/com/google/bitcoin/crypto/MnemonicWordException.java deleted file mode 100644 index 5bcc8418..00000000 --- a/core/src/main/java/com/google/bitcoin/crypto/MnemonicWordException.java +++ /dev/null @@ -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; - } -} diff --git a/core/src/main/java/com/google/bitcoin/crypto/PBKDF2SHA512.java b/core/src/main/java/com/google/bitcoin/crypto/PBKDF2SHA512.java new file mode 100644 index 00000000..02d5dc58 --- /dev/null +++ b/core/src/main/java/com/google/bitcoin/crypto/PBKDF2SHA512.java @@ -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(); + } +} 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 abbf6825..3780b8d2 100644 --- a/core/src/test/java/com/google/bitcoin/crypto/MnemonicCodeTest.java +++ b/core/src/test/java/com/google/bitcoin/crypto/MnemonicCodeTest.java @@ -31,101 +31,101 @@ import static org.junit.Assert.assertEquals; public class MnemonicCodeTest { // These vectors are from https://github.com/trezor/python-mnemonic/blob/master/vectors.json String vectors[] = { - "00000000000000000000000000000000", - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - "cb5e7230ce8229de990674f6aa4288325fd4d8181f761734bd8b5cc944fedc2a4300e64422864b565352de7ffbc5ad0fafdf5344489f3a83e4a4bb5271cafaae", + "00000000000000000000000000000000", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", + "3d39670caaa237f5fb2999474413733b59d9dfe12b2cccfe878069a2f605ae467a669619a0a45c7b3378c4c812b80be677c1b8f8f60db9383f1ed265c45eb41c", - "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", - "legal winner thank year wave sausage worth useful legal winner thank yellow", - "de1277934939d6969519f44b7b3757a905d7f635be41e1e88022c346bc52ad26c0a3e9578e73e9b89066873266f285a5891d27d28cb27fccfe26d92bbd7ee364", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "legal winner thank year wave sausage worth useful legal winner thank yellow", + "34eb04e0d4d60d00217f1c0150d8b0d6ffc6086e365a8a94fcceae8614e38274e719ebe7a693356426d1c62fdf90c84eaaac3d920743f3e79e0970a295886a08", - "80808080808080808080808080808080", - "letter advice cage absurd amount doctor acoustic avoid letter advice cage above", - "8863bccef9cfffeacef1e4c6fc97bba8227ab0fc7e8e162be7467282689a13521ea364d7c4bc8cd241b59f53c5147a89c18a47248a96592ab9a2c1f1870b026c", + "80808080808080808080808080808080", + "letter advice cage absurd amount doctor acoustic avoid letter advice cage above", + "acd45eb0b06e53d2e0fa6d1b3e8b3c33f200d2be6013fc5f8796c8c1d238552a615a01f325d78a10e633991d92f1236e21c24afe7c679fa1ecbc67fe71a5337d", - "ffffffffffffffffffffffffffffffff", - "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", - "7a29e57c7a1532af1bddb7e02b892cfccc6a57b74fe9784324ea89fab8a66dc64fde79c31166b159685116f4e93c1795496f20ffdc2d3a69d3439931dabde86e", + "ffffffffffffffffffffffffffffffff", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", + "d20f4e7902c3fd2f7716a533b131042036e6df2e44b14c8d65eb57403d002e4a12fa9be5325b257637ad1209e850188ffb061b3033a315b236ffc70c4ab4ea96", - "000000000000000000000000000000000000000000000000", - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent", - "c3e382025b6a22a901505cf393faea450eb6c4a5f2a8c8f0596285b2bd84688877a6cc7231420e2bbdd2428e62ed549a78fa215b3adafd8dea075dabfc704d5e", + "000000000000000000000000000000000000000000000000", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent", + "55fb6d9683e16f71025b73efa1a297d522010e37a4fc5b54770b09173e4f7fed85f83b075142965015d17c8f7a365e589ac943ed83cfbf76dcaf301c6c53b9d6", - "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", - "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will", - "c82666e40eb097bf6eb05fecd7dc2ddfb6bbdc6071900f4b3fd3c3e635db69aa2094f1f450c98e8dc6103aa72df635abdfcc3b6d6ec5261a9208a07a35a3f1c8", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will", + "a2455beee8e73686d56f92979ed8a69011772f68e8329a437f47e55d79eaeec25afc2ac5ff636ac8578161a09a2ea690747575653f9d91016b09b71227a53791", - "808080808080808080808080808080808080808080808080", - "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always", - "e90681c67c55504afadca009ce4042819341fa0e90300b6d32b4f2e8e8a6678ff7e7fc1da663ae194dc7a2ef7ec7b50112d1a5efce47bfd00c66eec82f2265b5", + "808080808080808080808080808080808080808080808080", + "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always", + "dd53ab268c524bad304e3decf4298ba1413a785bc866c2118988d1634bddf5a1301718191b247761060bc6dce3c62f833b22c062e51e7508fc04201cd7f8515b", - "ffffffffffffffffffffffffffffffffffffffffffffffff", - "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when", - "2a372547df962742942674170a7cef495ea0b97f4864de16d0f3ee82eb577ca1eca345e601cc2df7c626c5bc51c52c28a3b4294224b685c958c7450bee6769e6", + "ffffffffffffffffffffffffffffffffffffffffffffffff", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when", + "f0747118b52dcd33d383e5f3d6dddc94f66accd38b6d48f8a07fed9752fa8457dcb40bba5f40399814dcbd1a3f5cfaead1cf26c72268d1aa71561611421d4aaf", - "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", + "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", + "681088fbeaf5e4c29de23f55967a942bcb3400dc5b6e507477dfdcb493d921902352e015cd1235279c61ddf26b9536e6cf87fccb3cf2e142db44689f78ccad12", - "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", + "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", + "0881d39ee42046f433bff48ae0520128b79c0b9ff376c805b384340bd73767cdd8d1583606bafe2879be99ff44847e83057dbe521415a6067e8e4d7334e35a6c", - "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", + "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", + "63362d97a394e67674c5e337b8b8221f242cdf84eac057e08e8d522ac458e0c8b577c487bf82255d41e2ecfaf9326be2b3b31534a990608950b05a5d849e0603", - "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", + "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", + "3c22432ca0b5c9572a631b1068749bb6b663a78390322ab4b763c3ee21d81a7774f8ecdbc6ec73932b961c7374c7d14156cd61f81e433de3953fad23ea407e58", - "1083fa24dbb0afa4e7b327d23f666567", - "awesome cabin matrix resist april sponsor paddle gossip split will off soon", - "467406b36a0176e40e013393e5ecef1f5b4019980b502eda9db1db06f7786e088b206f045f2bfcf93bd3b17a598335b078fcc5890115857ff741bd154b54f049", + "b71edfa823c78c5f8d6cade654e298b4", + "require want tube elegant juice cool cup noble town poem plate harsh", + "b424ec5ee4eb4127c3ce0722572c456a47084cda617a26fb647f9a02f43fcc3d11a84dadb6e892ec2641d8592149018a39c8bd9c1cadc969188da4b820bf3371", - "d8cbcd1ac2153ecd74048480c2732f637d642b21f0dd40df", - "sugar fury effort loud fault grit source mountain liar bean slim shoulder stone better march brick dolphin zero", - "f60180ea5047659cbb17ed6ef79c974de86c0170c7a1962b205329eb8fe9dcdd148615d35c515c4ec8da25f4cf54d5b7cd8cd5bf8dc4059df3a7900ca25f8306", + "8ffd2ff2b2fa4dd7c61f019698893918dfd72c0ea37d0027", + "morning truly witness grass pill typical blur then notable session exact coyote word noodle dentist hurry ability dignity", + "d20f3879eebf9858443a6ca0f04f568aac06e74fb542475824101855133ed5f54d0805d0fc9bc0e6f03c1f820ee01a462b291caeba7cbf659b670afd00e0db42", - "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", + "9b655bc7d4e380bad6148df1cf8d8a6b023b781b05c2e823c5b5dd05139d30f6", + "opinion client vehicle prefer day frost flame museum vault ladder glad stock casual rose hire reunion trial bullet hope ring eye soldier sense stereo", + "3c6d3843502c73e29d7e9ddc3e81354d3467121244e0b713cdb8e0e76c357d6db2f738c43bc2550bb85c9d5c900fbd7fbba10c76e7a8b920e0d450ef5a6750eb", - "f5e82717078a6ddc538a03e825f91bed", - "vote donkey shift audit plug until evolve document trial cool eight swarm", - "83dad22293225780a914083fc1a69bfe1d910f5b5962b0364820132a42ae1bd567a1fb4d5a19ad3d64539e38a7ee3d6429fac2b74e72b020913131c5eadb7db4", + "bdc823540d4ff8d372d29f4265fe5cf8", + "saddle donate steak box zebra have slender fault draw copper now various", + "0c960d7d54415e5d3ae41ea805bc961f14d7031ea0b92058c19cecab7b7e51f82a4d987dbe63dfaed30805bd746d93e645fd5dffea8354b90688711d6fa570bd", - "16b59b6a426f2f302f73049a32ab8572394278982212357a", - "birth proud surround luggage very object saddle gauge olive next throw tongue neither detail gauge drastic cube strategy", - "38ceb07e0dad221f612631843be6ae44a650aaf789c8ebea9313e07498d7864385227d25c7a8268a5b850367eef31639632e9218acadead20980b864b1cd477e", + "53de16bcad3ea92c691f5d069de6f1870a9adfe764b1aa33", + "fatigue vague quality foil tunnel normal piece two alley upset round asthma predict husband outside normal pretty oppose", + "fa384e2775768bc4905a5f7a014cdb62bbf77f751a039d5a124191099970dafc02e679232aeab769cb1582038907829baa3995255be01fe042b462539b26f1af", - "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", + "29932ac2fbba5682ef711bd7429068ac7f90fe6132c9f1a722e8441240cb9a96", + "civil off radar wash pistol door saddle casino struggle behind boss flight weekend left luggage float vast decorate ring market catch grape heart sport", + "777122f7b4e1dd21d385e71b9811d7d8d2adffcb8d0cebb667c93346156d4baec6b23adc519515742a4f6a712cbbef3e03e528f912498a3104206f33259d8823", - "7f93397f750f70a26513de2732ed95ee", - "legend oil garlic tube warfare eye nephew knock cheese number grace tackle", - "7f92ad63e4cdf4f15c23740556ad81e7f8cbd67cc672c93894c9c0d4fb171539eed5ab29f366570ed9940b816f45a539c3816f7ac19511794b752c5c1ec0e732", + "498e5f6f8039399a5e307406f448f846", + "end indicate swim about near snake juice attend alone pelican dignity mimic", + "551e36122e37276b2840ab5c71b2a33888a6efa501a47b8323b313bf03f6dec1fb3d2ef11bfb35ec41580c5c0be3f881907fe2a3154b6e0ba8e05177b8e928a6", - "14c29fe840dd1c9f05d392ba13e4e1466b32ed0726a15f89", - "below belt wheel like spike exhibit blanket inch ring palace debate mimic rebel isolate broken stage garbage enhance", - "7bae6e54f8bad645f18f574b310bd3e6fde126dabcaf63a889940380e4798810e48c8151fc56bb2389c07498deacef025f03cbf8fc57ea3ec68f6421b0fcb649", + "7d205696a272051f60c14f146808a2c83bb0f7ef95037b4e", + "large actor pizza eager cake moral loan clarify behave doctor chunk motor roast know salad parrot kitten item", + "d8bb3b18a5b9844ae1117b680986d93074efc691d9084dc5ef9d3570d9f9bcaffc9c319fb53d6c728564ee0e4494953245607688adb2efbbe77b912835f40e8c", - "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", + "d10c1c341f19c89a5c8c505cabfa027422ea6cb4cb6c28003898676ef7611cd2", + "speed genius artist dilemma orient essay impulse meat frequent garlic letter tribe concert curve spring horn chimney achieve champion solution urge rack infant furnace", + "a2f0b91b238ca1df0c4ac89eaa628701800f70732a1952982f3021e94bf7c3aafa0bb51bbdcc210f1e433d3e740660d1e4053c12edfdc1eb77ceafbe6a32723e", - "a30b50a5439dcd1774f412ea5ec33403", - "perfect fold citizen mango system merry stable liquid tumble voyage snack alter", - "aae175f26848370c4d5d3d0640597e2bf1b28e95908dd877259b3eac5d71ffe3140739a3ed80180f88159571df84441985620e6b2fb0696e5cba1aa7b8d10b98", + "719ddb2a59b10066c74d26954da2dc2e", + "immense uphold skin recall avoid cricket brush pill next home require friend", + "9be5826432f915ace1f77827028a0ab7098dd3776304aa17e03698b09e5e93914e3e3f0d3d38c9b265ec2cc4bba1da8c9d9a97c8a3b1ec05add8e34a1c676490", - "70044da2175ad681d0ebbf2da83cf407eb9c8fd91fc0a8c9", - "hybrid carbon hammer concert pulp domain dry jewel color draft dial average right elevator good way potato energy", - "a3dffe3a31a2e949d1b04af7495a5b59db17e41d93b985feeaaae89260a9c86c6dcdf7cb32eaba61c2f4f0340f0f17d1ebb67af11657286b2ffd66ec4e05a8b7", + "5746a7704cd3b9dc0a022f2a9025618d813c8ca873b8008c", + "firm cry swing often describe unlock chimney echo clever license flash brand because edge peace jacket above gentle", + "06f5b2d7af46c2e7b7b99c87aa52d17c27925ba685ba5e572b4e978da2adee44fe7d5726966cd2ee1ae47b65790baa4b3f7952505b0b45d9c8673a29e6a57ffc", - "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", + "8fdda21cc1b39a6fb264ab3c007995cf9ed9efcfebc07652951e769b6bcbfad4", + "more unfair mango lock defy daughter sister nice despair adult grace palace unique wave distance job iron net elegant unfold repeat tourist twice number", + "c37ae2956e1396b03a722d647dbcf2672cb8db1493c2c136ab9370a62c19c8f45023a635b7c2646a9748ff28c7ef64d93d089c315d390c50cee19cb46a01927f" }; private MnemonicCode mc; @@ -143,31 +143,33 @@ public class MnemonicCodeTest { List code = mc.toMnemonic(Hex.decode(vecData)); byte[] seed = MnemonicCode.toSeed(code, "TREZOR"); + byte[] entropy = mc.toEntropy(split(vecCode)); assertEquals(vecCode, Joiner.on(' ').join(code)); 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 { byte[] entropy = Hex.decode("7f7f7f7f7f7f7f7f7f7f7f7f7f7f"); mc.toMnemonic(entropy); } - @Test(expected = MnemonicLengthException.class) + @Test(expected = MnemonicException.MnemonicLengthException.class) public void testBadLength() throws Exception { List words = split("risk tiger venture dinner age assume float denial penalty hello"); mc.check(words); } - @Test(expected = MnemonicWordException.class) + @Test(expected = MnemonicException.MnemonicWordException.class) public void testBadWord() throws Exception { List words = split("risk tiger venture dinner xyzzy assume float denial penalty hello game wing"); mc.check(words); } - @Test(expected = MnemonicChecksumException.class) + @Test(expected = MnemonicException.MnemonicChecksumException.class) public void testBadChecksum() throws Exception { List words = split("bless cloud wheel regular tiny venue bird web grief security dignity zoo"); mc.check(words);