From 3cc652858c75227edae620e7471481264f560198 Mon Sep 17 00:00:00 2001 From: Nicola Atzei Date: Fri, 3 Nov 2017 15:31:00 +0100 Subject: [PATCH] DumpedPrivateKey: Fix toBase58() for the case where the key is compressed. --- .../org/bitcoinj/core/DumpedPrivateKey.java | 23 +++++----- .../bitcoinj/core/DumpedPrivateKeyTest.java | 43 ++++++++++++++++++- 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/bitcoinj/core/DumpedPrivateKey.java b/core/src/main/java/org/bitcoinj/core/DumpedPrivateKey.java index 7c9895c2..51dc52ac 100644 --- a/core/src/main/java/org/bitcoinj/core/DumpedPrivateKey.java +++ b/core/src/main/java/org/bitcoinj/core/DumpedPrivateKey.java @@ -30,7 +30,6 @@ import javax.annotation.Nullable; * the last byte is a discriminator value for the compressed pubkey. */ public class DumpedPrivateKey extends VersionedChecksummedBytes { - private boolean compressed; /** * Construct a private key from its Base58 representation. @@ -50,7 +49,6 @@ public class DumpedPrivateKey extends VersionedChecksummedBytes { // Used by ECKey.getPrivateKeyEncoded() DumpedPrivateKey(NetworkParameters params, byte[] keyBytes, boolean compressed) { super(params.getDumpedPrivateKeyHeader(), encode(keyBytes, compressed)); - this.compressed = compressed; } private static byte[] encode(byte[] keyBytes, boolean compressed) { @@ -72,12 +70,7 @@ public class DumpedPrivateKey extends VersionedChecksummedBytes { super(encoded); if (params != null && version != params.getDumpedPrivateKeyHeader()) throw new WrongNetworkException(version, new int[]{ params.getDumpedPrivateKeyHeader() }); - if (bytes.length == 33 && bytes[32] == 1) { - compressed = true; - bytes = Arrays.copyOf(bytes, 32); // Chop off the additional marker byte. - } else if (bytes.length == 32) { - compressed = false; - } else { + if (bytes.length != 32 && bytes.length != 33) { throw new AddressFormatException("Wrong number of bytes for a private key, not 32 or 33"); } } @@ -86,8 +79,14 @@ public class DumpedPrivateKey extends VersionedChecksummedBytes { * Returns an ECKey created from this encoded private key. */ public ECKey getKey() { - final ECKey key = ECKey.fromPrivate(bytes); - return compressed ? key : key.decompress(); + return ECKey.fromPrivate(Arrays.copyOf(bytes, 32), isPubKeyCompressed()); + } + + /** + * Returns true if the public key corresponding to this private key is compressed. + */ + public boolean isPubKeyCompressed() { + return bytes.length == 33 && bytes[32] == 1; } @Override @@ -95,11 +94,11 @@ public class DumpedPrivateKey extends VersionedChecksummedBytes { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DumpedPrivateKey other = (DumpedPrivateKey) o; - return version == other.version && compressed == other.compressed && Arrays.equals(bytes, other.bytes); + return version == other.version && Arrays.equals(bytes, other.bytes); } @Override public int hashCode() { - return Objects.hashCode(version, compressed, Arrays.hashCode(bytes)); + return Objects.hashCode(version, Arrays.hashCode(bytes)); } } diff --git a/core/src/test/java/org/bitcoinj/core/DumpedPrivateKeyTest.java b/core/src/test/java/org/bitcoinj/core/DumpedPrivateKeyTest.java index 05edfc34..7cdf88bc 100644 --- a/core/src/test/java/org/bitcoinj/core/DumpedPrivateKeyTest.java +++ b/core/src/test/java/org/bitcoinj/core/DumpedPrivateKeyTest.java @@ -17,12 +17,15 @@ package org.bitcoinj.core; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.util.Arrays; import org.junit.Test; import org.bitcoinj.params.MainNetParams; @@ -66,7 +69,43 @@ public class DumpedPrivateKeyTest { @Test public void roundtripBase58() throws Exception { - String base58 = "5HtUCLMFWNueqN9unpgX2DzjMg6SDNZyKRb8s3LJgpFg5ubuMrk"; - assertEquals(base58, DumpedPrivateKey.fromBase58(null, base58).toBase58()); + String base58 = "5HtUCLMFWNueqN9unpgX2DzjMg6SDNZyKRb8s3LJgpFg5ubuMrk"; // 32-bytes key + DumpedPrivateKey dumpedPrivateKey = DumpedPrivateKey.fromBase58(null, base58); + assertFalse(dumpedPrivateKey.isPubKeyCompressed()); + assertEquals(base58, dumpedPrivateKey.toBase58()); + } + + @Test + public void roundtripBase58_compressed() throws Exception { + String base58 = "cSthBXr8YQAexpKeh22LB9PdextVE1UJeahmyns5LzcmMDSy59L4"; // 33-bytes, compressed == true + DumpedPrivateKey dumpedPrivateKey = DumpedPrivateKey.fromBase58(null, base58); + assertTrue(dumpedPrivateKey.isPubKeyCompressed()); + assertEquals(base58, dumpedPrivateKey.toBase58()); + } + + @Test(expected = AddressFormatException.class) + public void roundtripBase58_invalidCompressed() { + String base58 = "5Kg5shEQWrf1TojaHTdc2kLuz5Mfh4uvp3cYu8uJHaHgfTGUbTD"; // 32-bytes key + byte[] bytes = Base58.decodeChecked(base58); + bytes = Arrays.copyOf(bytes, bytes.length + 1); // append a "compress" byte + bytes[bytes.length - 1] = 0; // set it to false + base58 = Base58.encode(bytes); // 33-bytes key, compressed == false + DumpedPrivateKey.fromBase58(null, base58); // fail + } + + @Test + public void roundtripBase58_getKey() throws Exception { + ECKey k = new ECKey().decompress(); + assertFalse(k.isCompressed()); + assertEquals(k.getPrivKey(), + DumpedPrivateKey.fromBase58(null, k.getPrivateKeyAsWiF(MAINNET)).getKey().getPrivKey()); + } + + @Test + public void roundtripBase58_compressed_getKey() throws Exception { + ECKey k = new ECKey(); + assertTrue(k.isCompressed()); + assertEquals(k.getPrivKey(), + DumpedPrivateKey.fromBase58(null, k.getPrivateKeyAsWiF(MAINNET)).getKey().getPrivKey()); } }