mirror of
https://github.com/sparrowwallet/drongo.git
synced 2024-11-02 10:16:44 +00:00
implementation of secp256k1-jdk 0.0.1
This commit is contained in:
parent
0e08478294
commit
14a1a4ec8f
9 changed files with 98 additions and 43 deletions
|
@ -24,9 +24,15 @@ if(os.macOsX) {
|
|||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
flatDir {
|
||||
dirs 'libs'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation files('libs/secp-api-0.0.1.jar')
|
||||
implementation files('libs/secp-ffm-0.0.1.jar')
|
||||
implementation ('org.jspecify:jspecify:1.0.0')
|
||||
implementation ('com.googlecode.json-simple:json-simple:1.1.1') {
|
||||
exclude group: 'junit', module: 'junit'
|
||||
}
|
||||
|
|
BIN
libs/secp-api-0.0.1.jar
Normal file
BIN
libs/secp-api-0.0.1.jar
Normal file
Binary file not shown.
BIN
libs/secp-ffm-0.0.1.jar
Normal file
BIN
libs/secp-ffm-0.0.1.jar
Normal file
Binary file not shown.
|
@ -1,12 +1,13 @@
|
|||
package com.sparrowwallet.drongo.crypto;
|
||||
|
||||
import com.sparrowwallet.drongo.Utils;
|
||||
import com.sparrowwallet.drongo.protocol.SigHash;
|
||||
import com.sparrowwallet.drongo.protocol.SignatureDecodeException;
|
||||
import com.sparrowwallet.drongo.protocol.TransactionSignature;
|
||||
import com.sparrowwallet.drongo.protocol.VerificationException;
|
||||
import org.bitcoin.Secp256k1Context;
|
||||
import org.bitcoinj.secp.api.*;
|
||||
import org.bouncycastle.asn1.*;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.bouncycastle.crypto.signers.ECDSASigner;
|
||||
import org.bouncycastle.util.Properties;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -131,15 +132,16 @@ public class ECDSASignature {
|
|||
* @param pub The public key bytes to use.
|
||||
*/
|
||||
public boolean verify(byte[] data, byte[] pub) {
|
||||
ECDSASigner signer = new ECDSASigner();
|
||||
ECPublicKeyParameters params = new ECPublicKeyParameters(CURVE.getCurve().decodePoint(pub), CURVE);
|
||||
signer.init(false, params);
|
||||
try {
|
||||
return signer.verifySignature(data, r, s);
|
||||
} catch (NullPointerException e) {
|
||||
// Bouncy Castle contains a bug that can cause NPEs given specially crafted signatures. Those signatures
|
||||
// are inherently invalid/attack sigs so we just fail them here rather than crash the thread.
|
||||
log.error("Caught NPE inside bouncy castle", e);
|
||||
if(!Secp256k1Context.isEnabled()) {
|
||||
throw new IllegalStateException("libsecp256k1 is not enabled");
|
||||
}
|
||||
|
||||
try(Secp256k1 secp = Secp256k1.get()) {
|
||||
byte[] sigBytes = Utils.concat(Utils.bigIntegerToBytes(r, 32), Utils.bigIntegerToBytes(s, 32));
|
||||
SignatureData sig = secp.ecdsaSignatureParseCompact(() -> sigBytes).get();
|
||||
return secp.ecdsaVerify(sig, data, secp.ecPubKeyParse(() -> pub).get()).get();
|
||||
} catch(Exception e) {
|
||||
log.error("Error verifying ecdsa", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,8 @@ package com.sparrowwallet.drongo.crypto;
|
|||
|
||||
import com.sparrowwallet.drongo.Utils;
|
||||
import com.sparrowwallet.drongo.protocol.*;
|
||||
import org.bitcoin.NativeSecp256k1;
|
||||
import org.bitcoin.NativeSecp256k1Util;
|
||||
import org.bitcoin.Secp256k1Context;
|
||||
import org.bitcoinj.secp.api.*;
|
||||
import org.bouncycastle.asn1.*;
|
||||
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||
import org.bouncycastle.asn1.x9.X9IntegerConverter;
|
||||
|
@ -280,11 +279,11 @@ public class ECKey {
|
|||
}
|
||||
|
||||
if(Secp256k1Context.isEnabled()) {
|
||||
try {
|
||||
byte[] pubKeyBytes = NativeSecp256k1.computePubkey(Utils.bigIntegerToBytes(privKey, 32), false);
|
||||
LazyECPoint lazyECPoint = new LazyECPoint(CURVE.getCurve(), pubKeyBytes);
|
||||
try(Secp256k1 secp = Secp256k1.get()) {
|
||||
P256k1PubKey pubkey = secp.ecPubKeyCreate(new BigIntegerP256k1PrivKey(privKey));
|
||||
LazyECPoint lazyECPoint = new LazyECPoint(CURVE.getCurve(), pubkey.getCompressed());
|
||||
return lazyECPoint.get();
|
||||
} catch(NativeSecp256k1Util.AssertFailException e) {
|
||||
} catch(Exception e) {
|
||||
log.error("Error computing public key from private", e);
|
||||
}
|
||||
}
|
||||
|
@ -385,16 +384,34 @@ public class ECKey {
|
|||
throw new IllegalArgumentException("Private key cannot be null");
|
||||
}
|
||||
|
||||
ECDSASignature signature;
|
||||
if(!Secp256k1Context.isEnabled()) {
|
||||
throw new IllegalStateException("libsecp256k1 is not enabled");
|
||||
}
|
||||
|
||||
ECDSASignature signature = null;
|
||||
Integer counter = null;
|
||||
do {
|
||||
ECDSASigner signer = new ECDSASigner(new HMacDSANonceKCalculator(new SHA256Digest(), counter));
|
||||
ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(priv, CURVE);
|
||||
signer.init(true, privKey);
|
||||
BigInteger[] components = signer.generateSignature(input.getBytes());
|
||||
signature = new ECDSASignature(components[0], components[1]).toCanonicalised();
|
||||
counter = (counter == null ? 1 : counter+1);
|
||||
} while(!signature.hasLowR());
|
||||
try(Secp256k1 secp = Secp256k1.get()) {
|
||||
do {
|
||||
ECDSASigner signer = new ECDSASigner(new HMacDSANonceKCalculator(new SHA256Digest(), counter));
|
||||
ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(priv, CURVE);
|
||||
signer.init(true, privKey);
|
||||
BigInteger[] components = signer.generateSignature(input.getBytes());
|
||||
signature = new ECDSASignature(components[0], components[1]).toCanonicalised();
|
||||
|
||||
//No way to specify counter as a parameter while grinding for low R
|
||||
SignatureData sig = secp.ecdsaSign(input.getBytes(), new BigIntegerP256k1PrivKey(priv)).get();
|
||||
CompressedSignatureData serialized_signature = secp.ecdsaSignatureSerializeCompact(sig).get();
|
||||
BigInteger r = new BigInteger(1, Arrays.copyOfRange(serialized_signature.bytes(), 0, 32));
|
||||
BigInteger s = new BigInteger(1, Arrays.copyOfRange(serialized_signature.bytes(),32, 64));
|
||||
ECDSASignature signature2 = new ECDSASignature(r, s);
|
||||
if(!signature.equals(signature2)) {
|
||||
System.out.println("Signatures not equal " + signature.hasLowR() + " " + signature2.hasLowR() + " " + counter);
|
||||
}
|
||||
counter = (counter == null ? 1 : counter + 1);
|
||||
} while(!signature.hasLowR());
|
||||
} catch(Exception e) {
|
||||
log.error("Error signing ecdsa", e);
|
||||
}
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
@ -411,10 +428,10 @@ public class ECKey {
|
|||
throw new IllegalStateException("libsecp256k1 is not enabled");
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] sigBytes = NativeSecp256k1.schnorrSign(input.getBytes(), Utils.bigIntegerToBytes(priv, 32), new byte[32]);
|
||||
try(Secp256k1 secp = Secp256k1.get()) {
|
||||
byte[] sigBytes = secp.schnorrSigSign32(input.getBytes(), secp.ecKeyPairCreate(new BigIntegerP256k1PrivKey(priv)));
|
||||
return SchnorrSignature.decode(sigBytes);
|
||||
} catch(NativeSecp256k1Util.AssertFailException e) {
|
||||
} catch(Exception e) {
|
||||
log.error("Error signing schnorr", e);
|
||||
}
|
||||
|
||||
|
@ -883,4 +900,22 @@ public class ECKey {
|
|||
public interface ECDSAHashSigner {
|
||||
ECDSASignature sign(Sha256Hash hash);
|
||||
}
|
||||
|
||||
private static class BigIntegerP256k1PrivKey implements P256k1PrivKey {
|
||||
private final BigInteger privKey;
|
||||
|
||||
public BigIntegerP256k1PrivKey(BigInteger privKey) {
|
||||
this.privKey = privKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getEncoded() {
|
||||
return Utils.bigIntegerToBytes(privKey, 32);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,10 @@ package com.sparrowwallet.drongo.crypto;
|
|||
|
||||
import com.sparrowwallet.drongo.Utils;
|
||||
import com.sparrowwallet.drongo.protocol.TransactionSignature;
|
||||
import org.bitcoin.NativeSecp256k1;
|
||||
import org.bitcoin.NativeSecp256k1Util;
|
||||
import org.bitcoin.Secp256k1Context;
|
||||
import org.bitcoinj.secp.api.P256K1XOnlyPubKey;
|
||||
import org.bitcoinj.secp.api.Result;
|
||||
import org.bitcoinj.secp.api.Secp256k1;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -70,9 +71,9 @@ public class SchnorrSignature {
|
|||
throw new IllegalStateException("libsecp256k1 is not enabled");
|
||||
}
|
||||
|
||||
try {
|
||||
return NativeSecp256k1.schnorrVerify(encode(), data, pub);
|
||||
} catch(NativeSecp256k1Util.AssertFailException e) {
|
||||
try(Secp256k1 secp = Secp256k1.get()) {
|
||||
return secp.schnorrSigVerify(encode(), data, new ByteArrayP256K1XOnlyPubKey(pub)).get();
|
||||
} catch(Exception e) {
|
||||
log.error("Error verifying schnorr signature", e);
|
||||
}
|
||||
|
||||
|
@ -95,4 +96,22 @@ public class SchnorrSignature {
|
|||
public int hashCode() {
|
||||
return Objects.hash(r, s);
|
||||
}
|
||||
|
||||
private static class ByteArrayP256K1XOnlyPubKey implements P256K1XOnlyPubKey {
|
||||
private final byte[] pub;
|
||||
|
||||
public ByteArrayP256K1XOnlyPubKey(byte[] pub) {
|
||||
this.pub = pub;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger getX() {
|
||||
return new BigInteger(1, pub);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getSerialized() {
|
||||
return pub;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ open module com.sparrowwallet.drongo {
|
|||
requires ch.qos.logback.core;
|
||||
requires ch.qos.logback.classic;
|
||||
requires json.simple;
|
||||
requires org.bitcoinj.secp.api;
|
||||
exports com.sparrowwallet.drongo;
|
||||
exports com.sparrowwallet.drongo.psbt;
|
||||
exports com.sparrowwallet.drongo.protocol;
|
||||
|
|
|
@ -9,17 +9,11 @@ import java.io.IOException;
|
|||
public class Secp256k1Context {
|
||||
|
||||
private static final boolean enabled; // true if the library is loaded
|
||||
private static final long context; // ref to pointer to context obj
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(Secp256k1Context.class);
|
||||
|
||||
static { // static initializer
|
||||
enabled = loadLibrary();
|
||||
if(enabled) {
|
||||
context = secp256k1_init_context();
|
||||
} else {
|
||||
context = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isEnabled() {
|
||||
|
@ -29,7 +23,7 @@ public class Secp256k1Context {
|
|||
public static long getContext() {
|
||||
if (!enabled)
|
||||
return -1; // sanity check
|
||||
return context;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private static boolean loadLibrary() {
|
||||
|
@ -55,6 +49,4 @@ public class Secp256k1Context {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static native long secp256k1_init_context();
|
||||
}
|
||||
|
|
Binary file not shown.
Loading…
Reference in a new issue