Skip to content

Commit

Permalink
Fix usage of uncompressed private keys
Browse files Browse the repository at this point in the history
  • Loading branch information
natzei committed Jan 30, 2021
1 parent 9a79f8d commit 3b409d8
Show file tree
Hide file tree
Showing 32 changed files with 185 additions and 142 deletions.
11 changes: 0 additions & 11 deletions xyz.balzaclang.balzac.cli/.classpath
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,13 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="maven.pomderived" value="true"/>
Expand Down
2 changes: 1 addition & 1 deletion xyz.balzaclang.balzac.examples/src/main/java/bug.balzac
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const kC = key:cVhDA3Yxkeacnci8WUokAfQT6Nv4tGpmy1GzSYtJdYqDDwZipPPB
const kCpub = kB.toPubkey

// tx redeemable with Alice's private key
transaction A_funds {
transaction A_funds {
input = _
output = 1 BTC: fun(x). versig(kApub; x)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
import xyz.balzaclang.lib.model.PrivateKey;
import xyz.balzaclang.lib.utils.BitcoinUtils;

public class ECKeyStore {
public class PrivateKeysStore {

public static String getUniqueID(PrivateKey key) {
return BitcoinUtils.encode(Utils.sha256hash160(key.getBytes()));
Expand All @@ -55,40 +55,41 @@ public static String getUniqueID(PrivateKey key) {
private char[] password;
private KeyStore ks;
private Map<String, NetworkType> netwotkTypeMap = new HashMap<>();
private Map<String, Boolean> compressPubkeyMap = new HashMap<>();

/**
* Create a new ECKeyStore with an <b>empty password</b>. Use
* {@link #changePassword(char[])} to set a new password.
*
*
* @return an instance of ECKeyStore
* @throws KeyStoreException if an error occur creating the {@link KeyStore}
*/
public ECKeyStore() throws KeyStoreException {
public PrivateKeysStore() throws KeyStoreException {
this(new char[0]);
}

/**
* Create a new ECKeyStore with the specified password. The same password is
* used to store the keystore via {@link #store(File)} and for entries. Use
* {@link #changePassword(char[])} to set a new password.
*
*
* @param password a password for the store and its entries.
* @return an instance of ECKeyStore.
* @throws KeyStoreException if an error occur creating the {@link KeyStore}
*/
public ECKeyStore(char[] password) throws KeyStoreException {
public PrivateKeysStore(char[] password) throws KeyStoreException {
this(null, password);
}

/**
* Load the keystore from the given input stream, or create a new one if null.
* The password is used to decrypt the keystore <b>and each entry</b>.
*
*
* @param input the input stream from which load the keystore.
* @param password a password for the store and its entries.
* @throws KeyStoreException
*/
public ECKeyStore(InputStream input, char[] password) throws KeyStoreException {
public PrivateKeysStore(InputStream input, char[] password) throws KeyStoreException {
this.password = Arrays.copyOf(password, password.length);
this.ks = KeyStore.getInstance("pkcs12");
try {
Expand All @@ -104,6 +105,7 @@ public String addKey(PrivateKey key) throws KeyStoreException {
SecretKeyEntry kEntry = new SecretKeyEntry(secretKey);
ks.setEntry(keyID, kEntry, new PasswordProtection(password));
netwotkTypeMap.put(keyID, key.getNetworkType());
compressPubkeyMap.put(keyID, key.compressPublicKey());
return keyID;
}

Expand All @@ -112,7 +114,9 @@ public PrivateKey getKey(String keyID) throws KeyStoreException {
Key entryKey;
try {
entryKey = ks.getKey(keyID, password);
return PrivateKey.from(entryKey.getEncoded(), netwotkTypeMap.get(keyID));
boolean compressPublicKey = compressPubkeyMap.get(keyID);
NetworkType networkType = netwotkTypeMap.get(keyID);
return PrivateKey.from(entryKey.getEncoded(), compressPublicKey, networkType);
} catch (UnrecoverableKeyException | NoSuchAlgorithmException e) {
throw new KeyStoreException("Cannot fetch key " + keyID + ": " + e.getMessage(), e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public interface PrivateKey {

public byte[] getBytes();

public boolean compressPublicKey();

public String getWif();

public String getBytesAsString();
Expand All @@ -33,21 +35,21 @@ public interface PrivateKey {

public NetworkType getNetworkType();

public PrivateKey withNetwork(NetworkType networkType);

public static PrivateKey fromBase58(String wif) {
DumpedPrivateKey key = DumpedPrivateKey.fromBase58(null, wif);
return from(key.getKey().getPrivKeyBytes(), NetworkType.from(key.getParameters()));
byte[] keyBytes = key.getKey().getPrivKeyBytes();
boolean compressPubkey = key.isPubKeyCompressed();
return from(keyBytes, compressPubkey, NetworkType.from(key.getParameters()));
}

public static PrivateKey from(byte[] keyBytes, NetworkType params) {
return new PrivateKeyImpl(keyBytes, params);
public static PrivateKey from(byte[] keyBytes, boolean compressPubkey, NetworkType params) {
return new PrivateKeyImpl(keyBytes, compressPubkey, params);
}

public static PrivateKey fresh(NetworkType params) {
return from(new ECKey().getPrivKeyBytes(), params);
ECKey key = new ECKey();
return from(key.getPrivKeyBytes(), key.isCompressed(), params);
}

public static PrivateKey copy(PrivateKey key, NetworkType params) {
return from(key.getBytes(), params);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ class PrivateKeyImpl implements PrivateKey {

private final NetworkType params;
private final byte[] privkey;
private final boolean compressPubkey;
private final PublicKey pubkey;
private final Address address;

PrivateKeyImpl(byte[] privkey, NetworkType params) {
PrivateKeyImpl(byte[] privkey, boolean compressPubkey, NetworkType params) {
this.params = params;
this.privkey = Arrays.copyOf(privkey, privkey.length);
this.pubkey = PublicKey.fromBytes(ECKey.fromPrivate(privkey).getPubKey());
this.compressPubkey = compressPubkey;
this.pubkey = PublicKey.fromBytes(ECKey.fromPrivate(privkey, compressPubkey).getPubKey());
this.address = Address.fromPubkey(pubkey.getBytes(), params);
}

Expand All @@ -41,9 +43,15 @@ public byte[] getBytes() {
return Arrays.copyOf(privkey, privkey.length);
}


@Override
public boolean compressPublicKey() {
return compressPubkey;
}

@Override
public String getWif() {
return ECKey.fromPrivate(privkey).getPrivateKeyAsWiF(params.toNetworkParameters());
return ECKey.fromPrivate(privkey, compressPubkey).getPrivateKeyAsWiF(params.toNetworkParameters());
}

@Override
Expand All @@ -66,6 +74,11 @@ public NetworkType getNetworkType() {
return params;
}

@Override
public PrivateKey withNetwork(NetworkType networkType) {
return new PrivateKeyImpl(privkey, compressPubkey, networkType);
}

@Override
public int hashCode() {
final int prime = 31;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import org.bitcoinj.core.VerificationException;
import org.bitcoinj.crypto.TransactionSignature;

import xyz.balzaclang.lib.ECKeyStore;
import xyz.balzaclang.lib.PrivateKeysStore;
import xyz.balzaclang.lib.model.transaction.ITransactionBuilder;
import xyz.balzaclang.lib.model.transaction.Input;
import xyz.balzaclang.lib.model.transaction.Output;
Expand Down Expand Up @@ -96,7 +96,7 @@ public boolean equals(Object obj) {
public static Signature computeSignature(
PrivateKey key,
ITransactionBuilder txBuilder,
ECKeyStore keyStore,
PrivateKeysStore keyStore,
int inputIndex,
SignatureModifier modifier) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
* <p>
* Examples:
* </p>
*
*
* <pre>
* // OK
* new AbstractScriptBuilder(){}.number(42).number(5).data(new byte[]{})
Expand Down Expand Up @@ -128,22 +128,22 @@ public Script build() {
* </tr>
* <tr>
* <td>
*
*
* <pre>
* ... 42 (TOALTSTACK FROMALTSTACK)* 21 ...
* </pre>
*
*
* </td>
* <td>
*
*
* <pre>
* ... 42 21 ...
* </pre>
*
*
* </td>
* </tr>
* </table>
*
*
* @return this builder
*/
@SuppressWarnings("unchecked")
Expand All @@ -157,7 +157,7 @@ public T optimize() {
/**
* Optimize the given script, returning a copy. A copy is returned even if no
* optimization can be performed.
*
*
* @param script the script to optimize.
* @return an optimized script.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
import org.bitcoinj.script.ScriptChunk;
import org.bitcoinj.script.ScriptOpCodes;

import xyz.balzaclang.lib.ECKeyStore;
import xyz.balzaclang.lib.PrivateKeysStore;
import xyz.balzaclang.lib.model.Hash;
import xyz.balzaclang.lib.model.PrivateKey;
import xyz.balzaclang.lib.model.Signature;
Expand Down Expand Up @@ -221,15 +221,15 @@ private Script substituteAllBinding() {
/**
* Replace all the signatures placeholder with the actual signatures. Each
* placeholder is already associated with the key and the modifiers.
*
*
* @param tx the transaction to be signed
* @param inputIndex the index of the input that will contain this script
* @param outScript the redeemed output script
* @return a <b>copy</b> of this builder
* @throws KeyStoreException if an error occurs retrieving private keys
*/
@SuppressWarnings("unchecked")
public T setAllSignatures(ECKeyStore keystore, Transaction tx, int inputIndex, byte[] outScript, boolean isP2PKH)
public T setAllSignatures(PrivateKeysStore keystore, Transaction tx, int inputIndex, byte[] outScript, boolean isP2PKH)
throws KeyStoreException {

List<ScriptChunk> newChunks = new ArrayList<>();
Expand Down Expand Up @@ -284,7 +284,7 @@ public T setAllSignatures(ECKeyStore keystore, Transaction tx, int inputIndex, b

/**
* Append the given script to this builder.
*
*
* @param append the script to append
* @return this builder
*/
Expand All @@ -296,7 +296,7 @@ public T append(Script append) {
/**
* Append the given script builder to this one. If it contains some free
* variables or signatures placeholder, they are merged ensuring consistency.
*
*
* @param <U> the concrete type of the given builder
* @param append the script builder to append
* @return this builder
Expand Down Expand Up @@ -352,7 +352,7 @@ else if (isSignature(ch)) {

/**
* Extract a string representation of this builder.
*
*
* @return a string representing this builder
*/
public String serialize() {
Expand All @@ -365,7 +365,7 @@ public String serialize() {

/**
* Parse the given string to initialize this builder.
*
*
* @param str a string representation of this builder
* @return this builder
*/
Expand Down
Loading

0 comments on commit 3b409d8

Please sign in to comment.