diff --git a/core/src/main/java/org/bitcoinj/core/ECKey.java b/core/src/main/java/org/bitcoinj/core/ECKey.java index d3ffef93..9d732451 100644 --- a/core/src/main/java/org/bitcoinj/core/ECKey.java +++ b/core/src/main/java/org/bitcoinj/core/ECKey.java @@ -39,6 +39,7 @@ import org.spongycastle.crypto.signers.ECDSASigner; import org.spongycastle.crypto.signers.HMacDSAKCalculator; import org.spongycastle.math.ec.ECAlgorithms; import org.spongycastle.math.ec.ECPoint; +import org.spongycastle.math.ec.FixedPointCombMultiplier; import org.spongycastle.math.ec.FixedPointUtil; import org.spongycastle.math.ec.custom.sec.SecP256K1Curve; import org.spongycastle.util.encoders.Base64; @@ -243,7 +244,7 @@ public class ECKey implements EncryptableItem, Serializable { * compressed or not. */ public static ECKey fromPrivate(BigInteger privKey, boolean compressed) { - ECPoint point = CURVE.getG().multiply(privKey); + ECPoint point = publicPointFromPrivate(privKey); return new ECKey(privKey, compressed ? compressPoint(point) : decompressPoint(point)); } @@ -361,7 +362,7 @@ public class ECKey implements EncryptableItem, Serializable { this.priv = privKey; if (pubKey == null) { // Derive public from private. - ECPoint point = CURVE.getG().multiply(privKey); + ECPoint point = publicPointFromPrivate(privKey); if (compressed) point = compressPoint(point); this.pub = new LazyECPoint(point); @@ -439,10 +440,25 @@ public class ECKey implements EncryptableItem, Serializable { * new BigInteger(1, bytes); */ public static byte[] publicKeyFromPrivate(BigInteger privKey, boolean compressed) { - ECPoint point = CURVE.getG().multiply(privKey); + ECPoint point = publicPointFromPrivate(privKey); return point.getEncoded(compressed); } + /** + * Returns public key point from the given private key. To convert a byte array into a BigInteger, use + * new BigInteger(1, bytes); + */ + public static ECPoint publicPointFromPrivate(BigInteger privKey) { + /* + * TODO: FixedPointCombMultiplier currently doesn't support scalars longer than the group order, + * but that could change in future versions. + */ + if (privKey.bitLength() > CURVE.getN().bitLength()) { + privKey = privKey.mod(CURVE.getN()); + } + return new FixedPointCombMultiplier().multiply(CURVE.getG(), privKey); + } + /** Gets the hash160 form of the public key (as seen in addresses). */ public byte[] getPubKeyHash() { if (pubKeyHash == null) diff --git a/core/src/main/java/org/bitcoinj/crypto/DeterministicKey.java b/core/src/main/java/org/bitcoinj/crypto/DeterministicKey.java index d7c4c5b1..e03dd078 100644 --- a/core/src/main/java/org/bitcoinj/crypto/DeterministicKey.java +++ b/core/src/main/java/org/bitcoinj/crypto/DeterministicKey.java @@ -88,7 +88,7 @@ public class DeterministicKey extends ECKey { byte[] chainCode, BigInteger priv, @Nullable DeterministicKey parent) { - super(priv, compressPoint(ECKey.CURVE.getG().multiply(priv))); + super(priv, compressPoint(ECKey.publicPointFromPrivate(priv))); checkArgument(chainCode.length == 32); this.parent = parent; this.childNumberPath = checkNotNull(childNumberPath); @@ -156,7 +156,7 @@ public class DeterministicKey extends ECKey { @Nullable DeterministicKey parent, int depth, int parentFingerprint) { - super(priv, compressPoint(ECKey.CURVE.getG().multiply(priv))); + super(priv, compressPoint(ECKey.publicPointFromPrivate(priv))); checkArgument(chainCode.length == 32); this.parent = parent; this.childNumberPath = checkNotNull(childNumberPath); diff --git a/core/src/main/java/org/bitcoinj/crypto/HDKeyDerivation.java b/core/src/main/java/org/bitcoinj/crypto/HDKeyDerivation.java index ab0c4f66..c816fda4 100644 --- a/core/src/main/java/org/bitcoinj/crypto/HDKeyDerivation.java +++ b/core/src/main/java/org/bitcoinj/crypto/HDKeyDerivation.java @@ -194,21 +194,20 @@ public final class HDKeyDerivation { BigInteger ilInt = new BigInteger(1, il); assertLessThanN(ilInt, "Illegal derived key: I_L >= n"); - final ECPoint G = ECKey.CURVE.getG(); final BigInteger N = ECKey.CURVE.getN(); ECPoint Ki; switch (mode) { case NORMAL: - Ki = G.multiply(ilInt).add(parent.getPubKeyPoint()); + Ki = ECKey.publicPointFromPrivate(ilInt).add(parent.getPubKeyPoint()); break; case WITH_INVERSION: // This trick comes from Gregory Maxwell. Check the homomorphic properties of our curve hold. The // below calculations should be redundant and give the same result as NORMAL but if the precalculated // tables have taken a bit flip will yield a different answer. This mode is used when vending a key // to perform a last-ditch sanity check trying to catch bad RAM. - Ki = G.multiply(ilInt.add(RAND_INT)); + Ki = ECKey.publicPointFromPrivate(ilInt.add(RAND_INT).mod(N)); BigInteger additiveInverse = RAND_INT.negate().mod(N); - Ki = Ki.add(G.multiply(additiveInverse)); + Ki = Ki.add(ECKey.publicPointFromPrivate(additiveInverse)); Ki = Ki.add(parent.getPubKeyPoint()); break; default: throw new AssertionError();