Utils: add bytesToBigInteger() helper and use it

This is the antagonist to Utils.bigIntegerToBytes().
This commit is contained in:
Andreas Schildbach 2022-01-27 19:46:24 +01:00
parent 6ad636ccec
commit 435e39a841
13 changed files with 52 additions and 25 deletions

View file

@ -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));
}
/**

View file

@ -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

View file

@ -244,7 +244,7 @@ public class Sha256Hash implements Serializable, Comparable<Sha256Hash> {
* Returns the bytes interpreted as a positive integer.
*/
public BigInteger toBigInteger() {
return new BigInteger(1, bytes);
return Utils.bytesToBigInteger(bytes);
}
/**

View file

@ -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);

View file

@ -76,6 +76,7 @@ public class Utils {
* Otherwise the representation is not minimal.
* For example, if the sign bit is 0000_00<b>0</b>0, then the representation is not minimal due to the rightmost zero.
* </p>
* 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);

View file

@ -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());

View file

@ -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<ChildNumber> 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);
}
}

View file

@ -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();

View file

@ -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()) {

View file

@ -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"));
}
}

View file

@ -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));

View file

@ -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);

View file

@ -1130,7 +1130,7 @@ public class WalletTool implements Callable<Integer> {
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.