diff --git a/core/src/main/java/org/bitcoinj/core/Base58.java b/core/src/main/java/org/bitcoinj/core/Base58.java index 6ec2c47ac..9718aa3f7 100644 --- a/core/src/main/java/org/bitcoinj/core/Base58.java +++ b/core/src/main/java/org/bitcoinj/core/Base58.java @@ -156,7 +156,7 @@ public class Base58 { } public static BigInteger decodeToBigInteger(String input) throws AddressFormatException { - return new BigInteger(1, decode(input)); + return Utils.bytesToBigInteger(decode(input)); } /** diff --git a/core/src/main/java/org/bitcoinj/core/ECKey.java b/core/src/main/java/org/bitcoinj/core/ECKey.java index 4d089abec..75250cf15 100644 --- a/core/src/main/java/org/bitcoinj/core/ECKey.java +++ b/core/src/main/java/org/bitcoinj/core/ECKey.java @@ -212,7 +212,7 @@ public class ECKey implements EncryptableItem { * public key is compressed. */ public static ECKey fromPrivate(byte[] privKeyBytes) { - return fromPrivate(new BigInteger(1, privKeyBytes)); + return fromPrivate(Utils.bytesToBigInteger(privKeyBytes)); } /** @@ -220,7 +220,7 @@ public class ECKey implements EncryptableItem { * @param compressed Determines whether the resulting ECKey will use a compressed encoding for the public key. */ public static ECKey fromPrivate(byte[] privKeyBytes, boolean compressed) { - return fromPrivate(new BigInteger(1, privKeyBytes), compressed); + return fromPrivate(Utils.bytesToBigInteger(privKeyBytes), compressed); } /** @@ -241,7 +241,7 @@ public class ECKey implements EncryptableItem { public static ECKey fromPrivateAndPrecalculatedPublic(byte[] priv, byte[] pub) { checkNotNull(priv); checkNotNull(pub); - return new ECKey(new BigInteger(1, priv), new LazyECPoint(CURVE.getCurve(), pub)); + return new ECKey(Utils.bytesToBigInteger(priv), new LazyECPoint(CURVE.getCurve(), pub)); } /** @@ -339,7 +339,7 @@ public class ECKey implements EncryptableItem { /** * Returns public key bytes from the given private key. To convert a byte array into a BigInteger, - * use {@code new BigInteger(1, bytes);} + * use {@link Utils#bytesToBigInteger(byte[])} */ public static byte[] publicKeyFromPrivate(BigInteger privKey, boolean compressed) { ECPoint point = publicPointFromPrivate(privKey); @@ -348,7 +348,7 @@ public class ECKey implements EncryptableItem { /** * Returns public key point from the given private key. To convert a byte array into a BigInteger, - * use {@code new BigInteger(1, bytes);} + * use {@link Utils#bytesToBigInteger(byte[])} */ public static ECPoint publicPointFromPrivate(BigInteger privKey) { /* @@ -721,7 +721,7 @@ public class ECKey implements EncryptableItem { "Input is of wrong version"); byte[] privbits = ((ASN1OctetString) seq.getObjectAt(1)).getOctets(); - BigInteger privkey = new BigInteger(1, privbits); + BigInteger privkey = Utils.bytesToBigInteger(privbits); ASN1TaggedObject pubkey = (ASN1TaggedObject) seq.getObjectAt(3); checkArgument(pubkey.getTagNo() == 1, "Input has 'publicKey' with bad tag number"); @@ -799,8 +799,8 @@ public class ECKey implements EncryptableItem { // 0x1D = second key with even y, 0x1E = second key with odd y if (header < 27 || header > 34) throw new SignatureException("Header byte out of range: " + header); - BigInteger r = new BigInteger(1, Arrays.copyOfRange(signatureEncoded, 1, 33)); - BigInteger s = new BigInteger(1, Arrays.copyOfRange(signatureEncoded, 33, 65)); + BigInteger r = Utils.bytesToBigInteger(Arrays.copyOfRange(signatureEncoded, 1, 33)); + BigInteger s = Utils.bytesToBigInteger(Arrays.copyOfRange(signatureEncoded, 33, 65)); ECDSASignature sig = new ECDSASignature(r, s); byte[] messageBytes = formatMessageForSigning(message); // Note that the C++ code doesn't actually seem to specify any character encoding. Presumably it's whatever diff --git a/core/src/main/java/org/bitcoinj/core/Sha256Hash.java b/core/src/main/java/org/bitcoinj/core/Sha256Hash.java index f2c40c844..61a03e291 100644 --- a/core/src/main/java/org/bitcoinj/core/Sha256Hash.java +++ b/core/src/main/java/org/bitcoinj/core/Sha256Hash.java @@ -244,7 +244,7 @@ public class Sha256Hash implements Serializable, Comparable { * Returns the bytes interpreted as a positive integer. */ public BigInteger toBigInteger() { - return new BigInteger(1, bytes); + return Utils.bytesToBigInteger(bytes); } /** diff --git a/core/src/main/java/org/bitcoinj/core/StoredBlock.java b/core/src/main/java/org/bitcoinj/core/StoredBlock.java index 5ce660d73..b9588ee35 100644 --- a/core/src/main/java/org/bitcoinj/core/StoredBlock.java +++ b/core/src/main/java/org/bitcoinj/core/StoredBlock.java @@ -142,7 +142,7 @@ public class StoredBlock { public static StoredBlock deserializeCompact(NetworkParameters params, ByteBuffer buffer) throws ProtocolException { byte[] chainWorkBytes = new byte[StoredBlock.CHAIN_WORK_BYTES]; buffer.get(chainWorkBytes); - BigInteger chainWork = new BigInteger(1, chainWorkBytes); + BigInteger chainWork = Utils.bytesToBigInteger(chainWorkBytes); int height = buffer.getInt(); // +4 bytes byte[] header = new byte[Block.HEADER_SIZE + 1]; // Extra byte for the 00 transactions length. buffer.get(header, 0, Block.HEADER_SIZE); diff --git a/core/src/main/java/org/bitcoinj/core/Utils.java b/core/src/main/java/org/bitcoinj/core/Utils.java index 2f2297c81..bdc0717b5 100644 --- a/core/src/main/java/org/bitcoinj/core/Utils.java +++ b/core/src/main/java/org/bitcoinj/core/Utils.java @@ -76,6 +76,7 @@ public class Utils { * Otherwise the representation is not minimal. * For example, if the sign bit is 0000_0000, then the representation is not minimal due to the rightmost zero. *

+ * This is the antagonist to {@link #bytesToBigInteger(byte[])}. * @param b the integer to format into a byte array * @param numBytes the desired size of the resulting byte array * @return numBytes byte long array. @@ -94,6 +95,17 @@ public class Utils { return dest; } + /** + * Converts an array of bytes into a positive BigInteger. This is the antagonist to + * {@link #bigIntegerToBytes(BigInteger, int)}. + * + * @param bytes to convert into a BigInteger + * @return the converted BigInteger + */ + public static BigInteger bytesToBigInteger(byte[] bytes) { + return new BigInteger(1, bytes); + } + /** Write 2 bytes to the byte array (starting at the offset) as unsigned 16-bit integer in little endian format. */ public static void uint16ToByteArrayLE(int val, byte[] out, int offset) { out[offset] = (byte) (0xFF & val); diff --git a/core/src/main/java/org/bitcoinj/crypto/BIP38PrivateKey.java b/core/src/main/java/org/bitcoinj/crypto/BIP38PrivateKey.java index 45fcee64f..ba8f98c57 100644 --- a/core/src/main/java/org/bitcoinj/crypto/BIP38PrivateKey.java +++ b/core/src/main/java/org/bitcoinj/crypto/BIP38PrivateKey.java @@ -155,7 +155,7 @@ public class BIP38PrivateKey extends PrefixedChecksummedBytes { checkState(hashBytes.length == 40); passFactorBytes = Sha256Hash.hashTwice(hashBytes); } - BigInteger passFactor = new BigInteger(1, passFactorBytes); + BigInteger passFactor = Utils.bytesToBigInteger(passFactorBytes); ECKey k = ECKey.fromPrivate(passFactor, true); byte[] salt = Bytes.concat(addressHash, ownerEntropy); @@ -181,7 +181,7 @@ public class BIP38PrivateKey extends PrefixedChecksummedBytes { byte[] seed = Bytes.concat(decrypted1, Arrays.copyOfRange(decrypted2, 8, 16)); checkState(seed.length == 24); - BigInteger seedFactor = new BigInteger(1, Sha256Hash.hashTwice(seed)); + BigInteger seedFactor = Utils.bytesToBigInteger(Sha256Hash.hashTwice(seed)); checkState(passFactor.signum() >= 0); checkState(seedFactor.signum() >= 0); BigInteger priv = passFactor.multiply(seedFactor).mod(ECKey.CURVE.getN()); diff --git a/core/src/main/java/org/bitcoinj/crypto/DeterministicKey.java b/core/src/main/java/org/bitcoinj/crypto/DeterministicKey.java index f1fe3a87f..409bb2786 100644 --- a/core/src/main/java/org/bitcoinj/crypto/DeterministicKey.java +++ b/core/src/main/java/org/bitcoinj/crypto/DeterministicKey.java @@ -394,7 +394,7 @@ public class DeterministicKey extends ECKey { if (decryptedKey.length != 32) throw new KeyCrypterException.InvalidCipherText( "Decrypted key must be 32 bytes long, but is " + decryptedKey.length); - return new BigInteger(1, decryptedKey); + return Utils.bytesToBigInteger(decryptedKey); } // Otherwise we don't have it, but maybe we can figure it out from our parents. Walk up the tree looking for // the first key that has some encrypted private key data. @@ -431,7 +431,7 @@ public class DeterministicKey extends ECKey { private BigInteger derivePrivateKeyDownwards(DeterministicKey cursor, byte[] parentalPrivateKeyBytes) { DeterministicKey downCursor = new DeterministicKey(cursor.childNumberPath, cursor.chainCode, - cursor.pub, new BigInteger(1, parentalPrivateKeyBytes), cursor.parent); + cursor.pub, Utils.bytesToBigInteger(parentalPrivateKeyBytes), cursor.parent); // Now we have to rederive the keys along the path back to ourselves. That path can be found by just truncating // our path with the length of the parents path. List path = childNumberPath.subList(cursor.getPath().size(), childNumberPath.size()); @@ -576,7 +576,7 @@ public class DeterministicKey extends ECKey { if (pub) { return new DeterministicKey(path, chainCode, new LazyECPoint(ECKey.CURVE.getCurve(), data), parent, depth, parentFingerprint); } else { - return new DeterministicKey(path, chainCode, new BigInteger(1, data), parent, depth, parentFingerprint); + return new DeterministicKey(path, chainCode, Utils.bytesToBigInteger(data), parent, depth, parentFingerprint); } } diff --git a/core/src/main/java/org/bitcoinj/crypto/HDKeyDerivation.java b/core/src/main/java/org/bitcoinj/crypto/HDKeyDerivation.java index b656c3b6a..357340301 100644 --- a/core/src/main/java/org/bitcoinj/crypto/HDKeyDerivation.java +++ b/core/src/main/java/org/bitcoinj/crypto/HDKeyDerivation.java @@ -81,7 +81,7 @@ public final class HDKeyDerivation { * @throws HDDerivationException if privKeyBytes is invalid (not between 0 and n inclusive). */ public static DeterministicKey createMasterPrivKeyFromBytes(byte[] privKeyBytes, byte[] chainCode) throws HDDerivationException { - BigInteger priv = new BigInteger(1, privKeyBytes); + BigInteger priv = Utils.bytesToBigInteger(privKeyBytes); assertNonZero(priv, "Generated master key is invalid."); assertLessThanN(priv, "Generated master key is invalid."); return new DeterministicKey(HDPath.m(), chainCode, priv, null); @@ -137,7 +137,7 @@ public final class HDKeyDerivation { throws HDDerivationException { RawKeyBytes rawKey = deriveChildKeyBytesFromPrivate(parent, childNumber); return new DeterministicKey(parent.getPath().extend(childNumber), rawKey.chainCode, - new BigInteger(1, rawKey.keyBytes), parent); + Utils.bytesToBigInteger(rawKey.keyBytes), parent); } public static RawKeyBytes deriveChildKeyBytesFromPrivate(DeterministicKey parent, @@ -156,7 +156,7 @@ public final class HDKeyDerivation { checkState(i.length == 64, i.length); byte[] il = Arrays.copyOfRange(i, 0, 32); byte[] chainCode = Arrays.copyOfRange(i, 32, 64); - BigInteger ilInt = new BigInteger(1, il); + BigInteger ilInt = Utils.bytesToBigInteger(il); assertLessThanN(ilInt, "Illegal derived key: I_L >= n"); final BigInteger priv = parent.getPrivKey(); BigInteger ki = priv.add(ilInt).mod(ECKey.CURVE.getN()); @@ -187,7 +187,7 @@ public final class HDKeyDerivation { checkState(i.length == 64, i.length); byte[] il = Arrays.copyOfRange(i, 0, 32); byte[] chainCode = Arrays.copyOfRange(i, 32, 64); - BigInteger ilInt = new BigInteger(1, il); + BigInteger ilInt = Utils.bytesToBigInteger(il); assertLessThanN(ilInt, "Illegal derived key: I_L >= n"); final BigInteger N = ECKey.CURVE.getN(); diff --git a/core/src/main/java/org/bitcoinj/wallet/DeterministicKeyChain.java b/core/src/main/java/org/bitcoinj/wallet/DeterministicKeyChain.java index d20912435..c5bea7524 100644 --- a/core/src/main/java/org/bitcoinj/wallet/DeterministicKeyChain.java +++ b/core/src/main/java/org/bitcoinj/wallet/DeterministicKeyChain.java @@ -889,7 +889,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain { boolean isMarried = !isFollowingKey && !chains.isEmpty() && chains.get(chains.size() - 1).isFollowing(); // If this has a private key but no seed, then all we know is the spending key H if (seed == null && key.hasSecretBytes()) { - DeterministicKey accountKey = new DeterministicKey(immutablePath, chainCode, pubkey, new BigInteger(1, key.getSecretBytes().toByteArray()), null); + DeterministicKey accountKey = new DeterministicKey(immutablePath, chainCode, pubkey, Utils.bytesToBigInteger(key.getSecretBytes().toByteArray()), null); accountKey.setCreationTimeSeconds(key.getCreationTimestamp() / 1000); chain = factory.makeSpendingKeyChain(accountKey, isMarried, outputScriptType); isSpendingKey = true; @@ -918,7 +918,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain { DeterministicKey detkey; if (key.hasSecretBytes()) { // Not encrypted: private key is available. - final BigInteger priv = new BigInteger(1, key.getSecretBytes().toByteArray()); + final BigInteger priv = Utils.bytesToBigInteger(key.getSecretBytes().toByteArray()); detkey = new DeterministicKey(immutablePath, chainCode, pubkey, priv, parent); } else { if (key.hasEncryptedData()) { diff --git a/core/src/test/java/org/bitcoinj/core/Base58DecodeToBigIntegerTest.java b/core/src/test/java/org/bitcoinj/core/Base58DecodeToBigIntegerTest.java index 4d7a4b80a..0efb8faaa 100644 --- a/core/src/test/java/org/bitcoinj/core/Base58DecodeToBigIntegerTest.java +++ b/core/src/test/java/org/bitcoinj/core/Base58DecodeToBigIntegerTest.java @@ -28,6 +28,6 @@ public class Base58DecodeToBigIntegerTest { @Test public void testDecodeToBigInteger() { byte[] input = Base58.decode("129"); - assertEquals(new BigInteger(1, input), Base58.decodeToBigInteger("129")); + assertEquals(Utils.bytesToBigInteger(input), Base58.decodeToBigInteger("129")); } } diff --git a/core/src/test/java/org/bitcoinj/core/ECKeyTest.java b/core/src/test/java/org/bitcoinj/core/ECKeyTest.java index bf4546a8e..455157cfe 100644 --- a/core/src/test/java/org/bitcoinj/core/ECKeyTest.java +++ b/core/src/test/java/org/bitcoinj/core/ECKeyTest.java @@ -97,7 +97,7 @@ public class ECKeyTest { public void testSignatures() throws Exception { // Test that we can construct an ECKey from a private key (deriving the public from the private), then signing // a message with it. - BigInteger privkey = new BigInteger(1, HEX.decode("180cb41c7c600be951b5d3d0a7334acc7506173875834f7a6c4c786a28fcbb19")); + BigInteger privkey = Utils.bytesToBigInteger(HEX.decode("180cb41c7c600be951b5d3d0a7334acc7506173875834f7a6c4c786a28fcbb19")); ECKey key = ECKey.fromPrivate(privkey); byte[] output = key.sign(Sha256Hash.ZERO_HASH).encodeToDER(); assertTrue(key.verify(Sha256Hash.ZERO_HASH.getBytes(), output)); diff --git a/core/src/test/java/org/bitcoinj/core/UtilsTest.java b/core/src/test/java/org/bitcoinj/core/UtilsTest.java index 47f53ff14..b41ff2920 100644 --- a/core/src/test/java/org/bitcoinj/core/UtilsTest.java +++ b/core/src/test/java/org/bitcoinj/core/UtilsTest.java @@ -20,6 +20,7 @@ package org.bitcoinj.core; import java.math.BigInteger; import java.util.Date; +import java.util.Random; import org.junit.Test; @@ -54,6 +55,20 @@ public class UtilsTest { assertEquals("2014-11-16T10:54:33Z", Utils.dateTimeFormat(new Date(1416135273781L))); } + @Test + public void bigIntegerToBytes_roundTrip() { + int ITERATIONS = 100; + int LENGTH = 32; + Random rnd = new Random(); + byte[] bytes = new byte[LENGTH]; + + for (int i = 0; i < ITERATIONS; i++) { + rnd.nextBytes(bytes); + BigInteger bi = Utils.bytesToBigInteger(bytes); + assertArrayEquals(Utils.HEX.encode(bytes), bytes, Utils.bigIntegerToBytes(bi, LENGTH)); + } + } + @Test(expected = IllegalArgumentException.class) public void bigIntegerToBytes_convertNegativeNumber() { BigInteger b = BigInteger.valueOf(-1); diff --git a/wallettool/src/main/java/org/bitcoinj/wallettool/WalletTool.java b/wallettool/src/main/java/org/bitcoinj/wallettool/WalletTool.java index 5bdb86221..7a5d93b17 100644 --- a/wallettool/src/main/java/org/bitcoinj/wallettool/WalletTool.java +++ b/wallettool/src/main/java/org/bitcoinj/wallettool/WalletTool.java @@ -1130,7 +1130,7 @@ public class WalletTool implements Callable { System.err.println("Could not understand --privkey as either WIF, hex or base58: " + privKeyStr); return; } - key = ECKey.fromPrivate(new BigInteger(1, decode)); + key = ECKey.fromPrivate(Utils.bytesToBigInteger(decode)); } if (pubKeyStr != null) { // Give the user a hint.