3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-01-31 15:22:16 +00:00

MnemonicCode: Clean up a bit.

This commit is contained in:
Mike Hearn 2013-12-20 17:35:11 +01:00
parent 29e2af7ec0
commit 43460f451a
3 changed files with 109 additions and 272 deletions

View File

@ -17,6 +17,7 @@
package com.google.bitcoin.crypto; package com.google.bitcoin.crypto;
import com.google.bitcoin.core.Sha256Hash; import com.google.bitcoin.core.Sha256Hash;
import com.google.common.base.Joiner;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
import java.io.BufferedReader; import java.io.BufferedReader;
@ -25,9 +26,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.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -92,11 +91,10 @@ public class MnemonicCode {
// used as a pseudo-random function. Desired length of the // used as a pseudo-random function. Desired length of the
// derived key is 512 bits (= 64 bytes). // derived key is 512 bits (= 64 bytes).
// //
String pass = joinStringList(words); String pass = Joiner.on(' ').join(words);
String salt = new String("mnemonic" + passphrase); String salt = "mnemonic" + passphrase;
byte[] hash = PBKDF2SHA512.derive(pass, salt, PBKDF2_ROUNDS, 64); return PBKDF2SHA512.derive(pass, salt, PBKDF2_ROUNDS, 64);
return hash;
} }
/** /**
@ -116,7 +114,7 @@ public class MnemonicCode {
// Find the words index in the wordlist. // Find the words index in the wordlist.
int ndx = Collections.binarySearch(this.wordList, word); int ndx = Collections.binarySearch(this.wordList, word);
if (ndx < 0) if (ndx < 0)
throw new MnemonicException.MnemonicWordException("\"" + word + "\" invalid", word); throw new MnemonicException.MnemonicWordException(word);
// 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)
@ -139,9 +137,9 @@ public class MnemonicCode {
boolean[] hashBits = bytesToBits(hash); boolean[] hashBits = bytesToBits(hash);
// Check all the checksum bits. // Check all the checksum bits.
for (int ii = 0; ii < checksumLengthBits; ++ii) for (int i = 0; i < checksumLengthBits; ++i)
if (concatBits[entropyLengthBits + ii] != hashBits[ii]) if (concatBits[entropyLengthBits + i] != hashBits[i])
throw new MnemonicException.MnemonicChecksumException("checksum error"); throw new MnemonicException.MnemonicChecksumException();
return entropy; return entropy;
} }
@ -164,10 +162,8 @@ public class MnemonicCode {
// We append these bits to the end of the initial entropy. // We append these bits to the end of the initial entropy.
boolean[] concatBits = new boolean[entropyBits.length + checksumLengthBits]; boolean[] concatBits = new boolean[entropyBits.length + checksumLengthBits];
for (int ii = 0; ii < entropyBits.length; ++ii) System.arraycopy(entropyBits, 0, concatBits, 0, entropyBits.length);
concatBits[ii] = entropyBits[ii]; System.arraycopy(hashBits, 0, concatBits, entropyBits.length, checksumLengthBits);
for (int ii = 0; ii < checksumLengthBits; ++ii)
concatBits[entropyBits.length + ii] = hashBits[ii];
// Next we take these concatenated bits and split them into // Next we take these concatenated bits and split them into
// groups of 11 bits. Each group encodes number from 0-2047 // groups of 11 bits. Each group encodes number from 0-2047
@ -176,14 +172,14 @@ public class MnemonicCode {
ArrayList<String> words = new ArrayList<String>(); ArrayList<String> words = new ArrayList<String>();
int nwords = concatBits.length / 11; int nwords = concatBits.length / 11;
for (int ii = 0; ii < nwords; ++ii) { for (int i = 0; i < nwords; ++i) {
int ndx = 0; int index = 0;
for (int jj = 0; jj < 11; ++jj) { for (int j = 0; j < 11; ++j) {
ndx <<= 1; index <<= 1;
if (concatBits[(ii * 11) + jj]) if (concatBits[(i * 11) + j])
ndx |= 0x1; index |= 0x1;
} }
words.add(this.wordList.get(ndx)); words.add(this.wordList.get(index));
} }
return words; return words;
@ -192,29 +188,15 @@ 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 MnemonicException.MnemonicLengthException, MnemonicException.MnemonicWordException, MnemonicException.MnemonicChecksumException { public void check(List<String> words) throws MnemonicException {
toEntropy(words); toEntropy(words);
} }
private static boolean[] bytesToBits(byte[] data) { private static boolean[] bytesToBits(byte[] data) {
boolean[] bits = new boolean[data.length * 8]; boolean[] bits = new boolean[data.length * 8];
for (int ii = 0; ii < data.length; ++ii) for (int i = 0; i < data.length; ++i)
for (int jj = 0; jj < 8; ++jj) for (int j = 0; j < 8; ++j)
bits[(ii * 8) + jj] = (data[ii] & (1 << (7 - jj))) != 0; bits[(i * 8) + j] = (data[i] & (1 << (7 - j))) != 0;
return bits; return bits;
} }
static private String joinStringList(List<String> list) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (String item : list)
{
if (first)
first = false;
else
sb.append(" ");
sb.append(item);
}
return sb.toString();
}
} }

View File

@ -21,15 +21,13 @@ package com.google.bitcoin.crypto;
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class MnemonicException extends Exception { public class MnemonicException extends Exception {
public MnemonicException() {
super();
}
public MnemonicException(String msg) { public MnemonicException(String msg) {
super(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. * Thrown when an argument to MnemonicCode is the wrong length.
@ -38,26 +36,14 @@ public class MnemonicException extends Exception {
public MnemonicLengthException(String msg) { public MnemonicLengthException(String msg) {
super(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. * Thrown when a list of MnemonicCode words fails the checksum check.
*/ */
public static class MnemonicChecksumException extends MnemonicException { public static class MnemonicChecksumException extends MnemonicException {
public MnemonicChecksumException(String msg) { public MnemonicChecksumException() {
super(msg); super();
}
public MnemonicChecksumException(Exception ex) {
super(ex);
}
public MnemonicChecksumException(String msg, Exception ex) {
super(msg, ex);
} }
} }
@ -66,22 +52,11 @@ public class MnemonicException extends Exception {
*/ */
public static class MnemonicWordException extends MnemonicException { public static class MnemonicWordException extends MnemonicException {
/** Contains the word that was not found in the word list. */ /** Contains the word that was not found in the word list. */
public String badWord; public final String badWord;
public MnemonicWordException(String msg, String badWord) { public MnemonicWordException(String badWord) {
super(msg); super();
this.badWord = badWord; 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

@ -19,214 +19,94 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * 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; package com.google.bitcoin.crypto;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.security.spec.KeySpec;
import java.util.Formatter; /**
* <p>This is a clean-room implementation of PBKDF2 using RFC 2898 as a reference.</p>
import javax.crypto.Mac; *
import javax.crypto.SecretKey; * <p>RFC 2898: http://tools.ietf.org/html/rfc2898#section-5.2</p>
import javax.crypto.SecretKeyFactory; *
import javax.crypto.spec.PBEKeySpec; * <p>This code passes all RFC 6070 test vectors: http://tools.ietf.org/html/rfc6070</p>
import javax.crypto.spec.SecretKeySpec; *
* <p>http://cryptofreek.org/2012/11/29/pbkdf2-pure-java-implementation/<br>
public class PBKDF2SHA512 * Modified to use SHA-512 - Ken Sedgwick ken@bonsai.com</p>
{ */
/* START RFC 2898 IMPLEMENTATION */ public class PBKDF2SHA512 {
public static byte[] derive(String P, String S, int c, int dkLen) public static byte[] derive(String P, String S, int c, int dkLen) {
{ ByteArrayOutputStream baos = new ByteArrayOutputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
try int hLen = 20;
{
int hLen = 20; if (dkLen > ((Math.pow(2, 32)) - 1) * hLen) {
throw new IllegalArgumentException("derived key too long");
if (dkLen > ((Math.pow(2, 32)) - 1) * hLen) } else {
{ int l = (int) Math.ceil((double) dkLen / (double) hLen);
System.out.println("derived key too long"); // int r = dkLen - (l-1)*hLen;
}
else for (int i = 1; i <= l; i++) {
{ byte[] T = F(P, S, c, i);
int l = (int) Math.ceil((double) dkLen / (double) hLen); baos.write(T);
// int r = dkLen - (l-1)*hLen; }
}
for (int i = 1; i <= l; i++) } catch (Exception e) {
{ throw new RuntimeException(e);
byte[] T = F(P, S, c, i);
baos.write(T);
} }
}
byte[] baDerived = new byte[dkLen];
System.arraycopy(baos.toByteArray(), 0, baDerived, 0, baDerived.length);
return baDerived;
} }
catch (Exception e)
{ private static byte[] F(String P, String S, int c, int i) throws Exception {
e.printStackTrace(); byte[] U_LAST = null;
} byte[] U_XOR = null;
byte[] baDerived = new byte[dkLen]; SecretKeySpec key = new SecretKeySpec(P.getBytes("UTF-8"), "HmacSHA512");
System.arraycopy(baos.toByteArray(), 0, baDerived, 0, baDerived.length); Mac mac = Mac.getInstance(key.getAlgorithm());
mac.init(key);
return baDerived;
} for (int j = 0; j < c; j++) {
if (j == 0) {
private static byte[] F(String P, String S, int c, int i) throws Exception byte[] baS = S.getBytes("UTF-8");
{ byte[] baI = INT(i);
byte[] U_LAST = null; byte[] baU = new byte[baS.length + baI.length];
byte[] U_XOR = null;
System.arraycopy(baS, 0, baU, 0, baS.length);
SecretKeySpec key = new SecretKeySpec(P.getBytes("UTF-8"), "HmacSHA512"); System.arraycopy(baI, 0, baU, baS.length, baI.length);
Mac mac = Mac.getInstance(key.getAlgorithm());
mac.init(key); U_XOR = mac.doFinal(baU);
U_LAST = U_XOR;
for (int j = 0; j < c; j++) mac.reset();
{ } else {
if (j == 0) byte[] baU = mac.doFinal(U_LAST);
{ mac.reset();
byte[] baS = S.getBytes("UTF-8");
byte[] baI = INT(i); for (int k = 0; k < U_XOR.length; k++) {
byte[] baU = new byte[baS.length + baI.length]; U_XOR[k] = (byte) (U_XOR[k] ^ baU[k]);
}
System.arraycopy(baS, 0, baU, 0, baS.length);
System.arraycopy(baI, 0, baU, baS.length, baI.length); U_LAST = baU;
}
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;
}
} }
return U_XOR; private static byte[] INT(int i) {
} ByteBuffer bb = ByteBuffer.allocate(4);
bb.order(ByteOrder.BIG_ENDIAN);
private static byte[] INT(int i) bb.putInt(i);
{
ByteBuffer bb = ByteBuffer.allocate(4); return bb.array();
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();
}
} }