Skip to content

Commit

Permalink
made tests pass again
Browse files Browse the repository at this point in the history
  • Loading branch information
dbasch committed Jan 18, 2014
1 parent 7cadf5f commit 69d1e10
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 20 deletions.
6 changes: 2 additions & 4 deletions README.md
Expand Up @@ -4,9 +4,7 @@ A Java implementation of the BIP-0038 Draft: [Passphrase-protected private key](

## Usage

`BIP38.encryptEC(password, lot, sequence)` generates an encrypted key starting with 6P.
If you don't care about lot/sequence (if you don't know what they are, you don't care),
just use -1 for the lot. That will cause those parameters to be ignored.
`BIP38.generateEncryptedKey(password)` generates an encrypted key starting with "6P".

`BIP38.encryptNoEC(password, encodedKey, isCompressed)` encrypts a known key.

Expand All @@ -16,7 +14,7 @@ If you don't care about lot/sequence (if you don't know what they are, you don't

Key generation:

`encrypt("hello", -1, -1)`
`generateEncryptedKey("hello")`

might produce

Expand Down
36 changes: 23 additions & 13 deletions src/main/java/com/fruitcat/bitcoin/BIP38.java
Expand Up @@ -34,10 +34,7 @@
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.SecureRandom;
import java.security.Security;
import java.security.*;
import java.util.Arrays;

public class BIP38 {
Expand All @@ -51,16 +48,27 @@ public class BIP38 {
/**
* Generates an encrypted key with EC multiplication.
* Only uncompressed format for now.
* if lot is less than 0, lot and sequence are ignored.
*
* @param password
* @return
* @throws UnsupportedEncodingException
* @throws GeneralSecurityException
* @throws AddressFormatException
*/
public static String encryptEC(String password, int lot, int sequence) throws UnsupportedEncodingException, GeneralSecurityException, AddressFormatException {
public static String generateEncryptedKey(String password) throws UnsupportedEncodingException, GeneralSecurityException, AddressFormatException {

byte[] intermediate = Arrays.copyOfRange(Base58.decode(intermediatePassphrase(password, -1, -1)), 0, 53);
return encryptedKeyFromIntermediate(intermediate, -1);
}

byte[] intermediate = Base58.decode(intermediatePassphrase(password, lot, sequence));
/**
* if lot is less than 0, lot and sequence are ignored.
* @param intermediate
* @param lot
* @return
* @throws GeneralSecurityException
*/
public static String encryptedKeyFromIntermediate(byte[] intermediate, int lot) throws GeneralSecurityException {

byte flagByte = (lot > 0) ? (byte) 4 : (byte) 0; //uncompressed
byte[] ownerEntropy = new byte[8];
Expand Down Expand Up @@ -89,7 +97,7 @@ public static String encryptEC(String password, int lot, int sequence) throws Un
byte[] m2 = new byte[16];
for (int i = 0; i < 16; i++) {
m1[i] = (byte) (seedB[i] ^ derivedHalf1[i]);
m2[i] = (byte) (m2[i] ^ derivedHalf1[16 + i]);

}

Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding", "BC");
Expand All @@ -98,6 +106,9 @@ public static String encryptEC(String password, int lot, int sequence) throws Un
byte[] encryptedPart1 = cipher.doFinal(m1);
System.arraycopy(encryptedPart1, 8, m2, 0, 8);
System.arraycopy(seedB, 16, m2, 8, 8);
for (int i = 0 ; i < 16; i ++) {
m2[i] = (byte) (m2[i] ^ derivedHalf1[16 + i]);
}

byte[] encryptedPart2 = cipher.doFinal(m2);
byte[] encryptedPrivateKey = new byte[39];
Expand Down Expand Up @@ -236,14 +247,14 @@ public static String decryptEC(String password, byte[] encryptedKey) throws Unsu
byte[] m2 = cipher.doFinal(encryptedPart2);

byte[] encryptedPart1 = new byte[16];
System.arraycopy(encryptedKey, 15, encryptedPart1, 0, 8);

byte[] seedB = new byte[24];

for (int i = 0; i < 16; i++) {
m2[i] = (byte) (m2[i] ^ derivedHalf1[16 + i]);
}

System.arraycopy(m2, 0, encryptedPart1, 8, 8);
System.arraycopy(encryptedKey, 15, encryptedPart1, 0, 8);

byte[] m1 = cipher.doFinal(encryptedPart1);

Expand Down Expand Up @@ -276,7 +287,6 @@ public static String encryptNoEC(String encodedPrivateKey, String password, bool
DumpedPrivateKey dk = new DumpedPrivateKey(MainNetParams.get(), encodedPrivateKey);

ECKey key = dk.getKey();
System.out.println(key.getPrivateKeyEncoded(MainNetParams.get()));
byte[] keyBytes = key.getPrivKeyBytes();
String address = key.toAddress(MainNetParams.get()).toString();
byte[] tmp = address.getBytes("ASCII");
Expand Down Expand Up @@ -345,8 +355,8 @@ public static String decryptNoEC(String password, byte[] encryptedKey) throws Un

// generate a key, decrypt it, print the decrypted key and the address.
public static void main(String args[]) throws Exception {
String encryptedKey = encryptEC("hello", 1, 1);
System.out.println(encryptedKey);
String encryptedKey = generateEncryptedKey("hello");
System.out.println("key is:" + encryptedKey);
String key = decrypt("hello", encryptedKey);
DumpedPrivateKey dk = new DumpedPrivateKey(MainNetParams.get(), key);
ECKey k = dk.getKey();
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/fruitcat/bitcoin/Utils.java
Expand Up @@ -61,7 +61,8 @@ public static byte[] sha256ripe160(byte[] data) throws NoSuchAlgorithmException
}

//for debugging
protected static void pb(byte [] x) {
protected static void pb(String name, byte [] x) {
System.out.print(name + ": ");
for (byte b : x) {
int l = b >= 0 ? b : 256 + b;
System.out.print(l + " ");
Expand Down
14 changes: 12 additions & 2 deletions src/test/java/com/fruitcat/bitcoin/BIP38Test.java
@@ -1,7 +1,10 @@
package com.fruitcat.bitcoin;

import com.google.bitcoin.core.Base58;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;

import java.util.Arrays;
import java.util.Random;

/**
Expand All @@ -14,7 +17,7 @@ public class BIP38Test {

//EC multiply, no compression, no lot/sequence numbers
@Test
public void decryptECNoCompressionNoLotTest() throws Exception {
public void decryptECNoCompressionNoLot() throws Exception {
String encryptedKey = "6PfQu77ygVyJLZjfvMLyhLMQbYnu5uguoJJ4kMCLqWwPEdfpwANVS76gTX";
String key = "5K4caxezwjGCGfnoPTZ8tMcJBLB7Jvyjv4xxeacadhq8nLisLR2";
String decryptedKey = BIP38.decrypt(testPass, encryptedKey);
Expand All @@ -36,9 +39,16 @@ public void randomRoundTripNoEC() throws Exception {
byte[] r = new byte[16];
(new Random()).nextBytes(r);
String randomPass = new String(r, "ASCII");
System.out.println(randomPass);
String key = "5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR";
String encryptedKey = BIP38.encryptNoEC(key, randomPass, false);
assertEquals(key, (BIP38.decrypt(randomPass, encryptedKey)));
}

//generate an encrypted key and make sure it looks ok.
@Test
public void generateEncryptedKey() throws Exception {
String k = BIP38.generateEncryptedKey(testPass);
String dk = BIP38.decrypt(testPass, k);
assertEquals(dk.charAt(0), '5');
}
}

0 comments on commit 69d1e10

Please sign in to comment.