diff --git a/core/src/main/java/com/google/bitcoin/core/ECKey.java b/core/src/main/java/com/google/bitcoin/core/ECKey.java index ebef9c59..9e275527 100644 --- a/core/src/main/java/com/google/bitcoin/core/ECKey.java +++ b/core/src/main/java/com/google/bitcoin/core/ECKey.java @@ -16,6 +16,7 @@ package com.google.bitcoin.core; +import org.bitcoin.NativeSecp256k1; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Preconditions; @@ -43,6 +44,8 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.Serializable; import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.nio.charset.Charset; import java.security.SecureRandom; import java.security.SignatureException; @@ -395,13 +398,19 @@ public class ECKey implements Serializable { } /** - * Verifies the given ECDSA signature against the message bytes using the public key bytes. + *

xVerifies the given ECDSA signature against the message bytes using the public key bytes.

+ * + *

When using native ECDSA verification, data must be 32 bytes, and no element may be + * larger than 520 bytes.

* * @param data Hash of the data to verify. * @param signature ASN.1 encoded signature. * @param pub The public key bytes to use. */ public static boolean verify(byte[] data, ECDSASignature signature, byte[] pub) { + if (NativeSecp256k1.enabled) + return NativeSecp256k1.verify(data, signature.encodeToDER(), pub); + ECDSASigner signer = new ECDSASigner(); ECPublicKeyParameters params = new ECPublicKeyParameters(ecParams.getCurve().decodePoint(pub), ecParams); signer.init(false, params); @@ -424,6 +433,9 @@ public class ECKey implements Serializable { * @param pub The public key bytes to use. */ public static boolean verify(byte[] data, byte[] signature, byte[] pub) { + if (NativeSecp256k1.enabled) + return NativeSecp256k1.verify(data, signature, pub); + try { ASN1InputStream decoder = new ASN1InputStream(signature); DLSequence seq = (DLSequence) decoder.readObject(); diff --git a/core/src/main/java/org/bitcoin/NativeSecp256k1.java b/core/src/main/java/org/bitcoin/NativeSecp256k1.java new file mode 100644 index 00000000..de25ba43 --- /dev/null +++ b/core/src/main/java/org/bitcoin/NativeSecp256k1.java @@ -0,0 +1,78 @@ +/** + * Copyright 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.bitcoin; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import com.google.common.base.Preconditions; + + +/** + *

This class holds native methods to handle ECDSA verification.

+ * + *

You can find an example library that can be used for this at https://github.com/sipa/secp256k1

+ * + *

To build secp256k1 for use with bitcoinj, run `./configure` and `make libjavasecp256k1.so` then copy + * libjavasecp256k1.so to your system library path or point the JVM to the folder containing it with -Djava.library.path + *

+ */ +public class NativeSecp256k1 { + public static boolean enabled = true; + static { + try { + System.loadLibrary("javasecp256k1"); + } catch (UnsatisfiedLinkError e) { + enabled = false; + } + } + + private static ThreadLocal nativeECDSABuffer = new ThreadLocal(); + /** + * Verifies the given secp256k1 signature in native code. + * Calling when enabled == false is undefined (probably library not loaded) + * + * @param data The data which was signed, must be exactly 32 bytes + * @param signature The signature + * @param pub The public key which did the signing + */ + public static boolean verify(byte[] data, byte[] signature, byte[] pub) { + Preconditions.checkArgument(data.length == 32 && signature.length <= 520 && pub.length <= 520); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null) { + byteBuff = ByteBuffer.allocateDirect(32 + 8 + 520 + 520); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(data); + byteBuff.putInt(signature.length); + byteBuff.putInt(pub.length); + byteBuff.put(signature); + byteBuff.put(pub); + return secp256k1_ecdsa_verify(byteBuff) == 1; + } + + /** + * @param byteBuff signature format is byte[32] data, + * native-endian int signatureLength, native-endian int pubkeyLength, + * byte[signatureLength] signature, byte[pubkeyLength] pub + * @returns 1 for valid signature, anything else for invalid + */ + private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff); +}