mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-19 09:52:09 +01:00
Add low R signing (#1342)
* Added signing with entropy to the JNI and implemented low R signing * Added check for determinism of low R signing * Cleaned up test * Implemented signing with entropy in Bouncy Castle * Added docs for signing with entropy * Fixed things after rebase * De-parallelized signLowR and added scaladoc warnings to signWithEntropy
This commit is contained in:
parent
51d35e24e9
commit
666d53d94a
@ -249,6 +249,12 @@ sealed abstract class ExtPrivateKey
|
||||
key.signFunction
|
||||
}
|
||||
|
||||
override def signWithEntropyFunction: (
|
||||
ByteVector,
|
||||
ByteVector) => Future[ECDigitalSignature] = {
|
||||
key.signWithEntropyFunction
|
||||
}
|
||||
|
||||
/** Signs the given bytes with the given [[BIP32Path path]] */
|
||||
override def deriveAndSignFuture: (
|
||||
ByteVector,
|
||||
|
@ -138,7 +138,9 @@ object TxUtil {
|
||||
case (inputInfo, index) =>
|
||||
val mockSigners = inputInfo.pubKeys.take(inputInfo.requiredSigs).map {
|
||||
pubKey =>
|
||||
Sign(_ => Future.successful(DummyECDigitalSignature), pubKey)
|
||||
Sign(_ => Future.successful(DummyECDigitalSignature),
|
||||
(_, _) => Future.successful(DummyECDigitalSignature),
|
||||
pubKey)
|
||||
}
|
||||
|
||||
val mockSpendingInfo =
|
||||
|
@ -90,6 +90,19 @@ class BouncyCastleSecp256k1Test extends BitcoinSUnitTest {
|
||||
}
|
||||
}
|
||||
|
||||
it must "compute signatures with entropy the same" in {
|
||||
forAll(CryptoGenerators.privateKey,
|
||||
NumberGenerator.bytevector(32),
|
||||
NumberGenerator.bytevector(32)) {
|
||||
case (privKey, bytes, entropy) =>
|
||||
assert(
|
||||
privKey.signWithEntropy(bytes,
|
||||
entropy,
|
||||
context = BouncyCastle) == privKey
|
||||
.signWithEntropy(bytes, entropy, context = LibSecp256k1))
|
||||
}
|
||||
}
|
||||
|
||||
it must "verify signatures the same" in {
|
||||
forAll(CryptoGenerators.privateKey,
|
||||
NumberGenerator.bytevector(32),
|
||||
|
@ -1,35 +1,56 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import org.bitcoins.testkit.core.gen.CryptoGenerators
|
||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||
import scodec.bits.ByteVector
|
||||
import org.bitcoins.testkit.util.BitcoinSAsyncTest
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
class SignTest extends BitcoinSAsyncTest {
|
||||
|
||||
class SignTest extends BitcoinSUnitTest {
|
||||
implicit val ec = ExecutionContext.global
|
||||
implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
|
||||
generatorDrivenConfigNewCode
|
||||
|
||||
//ECPrivateKey implements the sign interface
|
||||
//so just use it for testing purposes
|
||||
val signTestImpl = new Sign {
|
||||
private val key = ECPrivateKey.freshPrivateKey
|
||||
|
||||
override def signFunction: ByteVector => Future[ECDigitalSignature] = {
|
||||
key.signFunction
|
||||
}
|
||||
|
||||
override def publicKey: ECPublicKey = key.publicKey
|
||||
}
|
||||
val privKey: Sign = ECPrivateKey.freshPrivateKey
|
||||
val pubKey: ECPublicKey = privKey.publicKey
|
||||
|
||||
behavior of "Sign"
|
||||
|
||||
it must "sign arbitrary pieces of data correctly" in {
|
||||
forAll(CryptoGenerators.sha256Digest) {
|
||||
case hash: Sha256Digest =>
|
||||
val pubKey = signTestImpl.publicKey
|
||||
val sigF = signTestImpl.signFunction(hash.bytes)
|
||||
forAllAsync(CryptoGenerators.sha256Digest) { hash =>
|
||||
val sigF = privKey.signFunction(hash.bytes)
|
||||
|
||||
sigF.map(sig => assert(pubKey.verify(hash.hex, sig)))
|
||||
sigF.map { sig =>
|
||||
assert(pubKey.verify(hash.bytes, sig))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it must "sign arbitrary pieces of data with arbitrary entropy correctly" in {
|
||||
forAllAsync(CryptoGenerators.sha256Digest, CryptoGenerators.sha256Digest) {
|
||||
case (hash, entropy) =>
|
||||
val sigF = privKey.signWithEntropyFunction(hash.bytes, entropy.bytes)
|
||||
|
||||
sigF.map { sig =>
|
||||
assert(pubKey.verify(hash.bytes, sig))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it must "sign arbitrary data correctly with low R values" in {
|
||||
forAllAsync(CryptoGenerators.sha256Digest) { hash =>
|
||||
val bytes = hash.bytes
|
||||
|
||||
for {
|
||||
sig1 <- privKey.signLowRFuture(bytes)
|
||||
sig2 <- privKey.signLowRFuture(bytes) // Check for determinism
|
||||
} yield {
|
||||
assert(pubKey.verify(bytes, sig1))
|
||||
assert(
|
||||
sig1.bytes.length <= 70
|
||||
) // This assertion fails if Low R is not used
|
||||
assert(sig1.bytes == sig2.bytes)
|
||||
assert(sig1 == sig2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,39 @@ object BouncyCastleUtil {
|
||||
signatureLowS
|
||||
}
|
||||
|
||||
/** Create an ECDSA signature adding specified entropy.
|
||||
*
|
||||
* This can be used to include your own entropy to nonce generation
|
||||
* in addition to the message and private key, while still doing so deterministically.
|
||||
*
|
||||
* In particular, this is used when generating low R signatures.
|
||||
* @see [[https://github.com/bitcoin/bitcoin/pull/13666/]]
|
||||
*/
|
||||
def signWithEntropy(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
entropy: ByteVector): ECDigitalSignature = {
|
||||
val signer: ECDSASigner = new ECDSASigner(
|
||||
new HMacDSAKCalculatorWithEntropy(new SHA256Digest(), entropy))
|
||||
val privKey: ECPrivateKeyParameters =
|
||||
new ECPrivateKeyParameters(getBigInteger(privateKey.bytes),
|
||||
CryptoParams.curve)
|
||||
signer.init(true, privKey)
|
||||
val components: Array[BigInteger] =
|
||||
signer.generateSignature(dataToSign.toArray)
|
||||
val (r, s) = (components(0), components(1))
|
||||
val signature = ECDigitalSignature(r, s)
|
||||
//make sure the signature follows BIP62's low-s value
|
||||
//https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#Low_S_values_in_signatures
|
||||
//bitcoinj implementation
|
||||
//https://github.com/bitcoinj/bitcoinj/blob/1e66b9a8e38d9ad425507bf5f34d64c5d3d23bb8/core/src/main/java/org/bitcoinj/core/ECKey.java#L551
|
||||
val signatureLowS = DERSignatureUtil.lowS(signature)
|
||||
require(
|
||||
signatureLowS.isDEREncoded,
|
||||
"We must create DER encoded signatures when signing a piece of data, got: " + signatureLowS)
|
||||
signatureLowS
|
||||
}
|
||||
|
||||
def verifyDigitalSignature(
|
||||
data: ByteVector,
|
||||
publicKey: ECPublicKey,
|
||||
|
@ -71,6 +71,47 @@ sealed abstract class ECPrivateKey
|
||||
ec: ExecutionContext): Future[ECDigitalSignature] =
|
||||
Future(sign(hash))
|
||||
|
||||
override def signWithEntropy(
|
||||
bytes: ByteVector,
|
||||
entropy: ByteVector): ECDigitalSignature = {
|
||||
signWithEntropy(bytes, entropy, CryptoContext.default)
|
||||
}
|
||||
|
||||
def signWithEntropy(
|
||||
bytes: ByteVector,
|
||||
entropy: ByteVector,
|
||||
context: CryptoContext): ECDigitalSignature = {
|
||||
context match {
|
||||
case CryptoContext.LibSecp256k1 => signWithEntropyWithSecp(bytes, entropy)
|
||||
case CryptoContext.BouncyCastle =>
|
||||
signWithEntropyWithBouncyCastle(bytes, entropy)
|
||||
}
|
||||
}
|
||||
|
||||
def signWithEntropyWithSecp(
|
||||
bytes: ByteVector,
|
||||
entropy: ByteVector): ECDigitalSignature = {
|
||||
val sigBytes = NativeSecp256k1.signWithEntropy(bytes.toArray,
|
||||
this.bytes.toArray,
|
||||
entropy.toArray)
|
||||
|
||||
ECDigitalSignature(ByteVector(sigBytes))
|
||||
}
|
||||
|
||||
def signWithEntropyWithBouncyCastle(
|
||||
bytes: ByteVector,
|
||||
entropy: ByteVector): ECDigitalSignature = {
|
||||
BouncyCastleUtil.signWithEntropy(bytes, this, entropy)
|
||||
}
|
||||
|
||||
override def signWithEntropyFunction: (
|
||||
ByteVector,
|
||||
ByteVector) => Future[ECDigitalSignature] = {
|
||||
case (bytes, entropy) =>
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
Future(signWithEntropy(bytes, entropy))
|
||||
}
|
||||
|
||||
def schnorrSign(dataToSign: ByteVector): SchnorrDigitalSignature = {
|
||||
val auxRand = ECPrivateKey.freshPrivateKey.bytes
|
||||
schnorrSign(dataToSign, auxRand)
|
||||
|
@ -0,0 +1,138 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import java.math.BigInteger
|
||||
import java.security.SecureRandom
|
||||
|
||||
import org.bouncycastle.crypto.Digest
|
||||
import org.bouncycastle.crypto.macs.HMac
|
||||
import org.bouncycastle.crypto.params.KeyParameter
|
||||
import org.bouncycastle.crypto.signers.DSAKCalculator
|
||||
import org.bouncycastle.util.{Arrays, BigIntegers}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/** Entirely copied from [[org.bouncycastle.crypto.signers.HMacDSAKCalculator HMacDSAKCalculator]]
|
||||
* with an added entropy parameter as well as two lines added adding the entropy to the hash.
|
||||
*
|
||||
* For a reference in secp256k1, see nonce_function_rfc6979 in secp256k1.c
|
||||
* For a description of the altered part, see RFC 6979 section 3.2d
|
||||
* here [[https://tools.ietf.org/html/rfc6979#section-3.2]]
|
||||
*
|
||||
* The added lines are marked below with comments.
|
||||
*/
|
||||
class HMacDSAKCalculatorWithEntropy(digest: Digest, entropy: ByteVector)
|
||||
extends DSAKCalculator {
|
||||
require(entropy.length == 32, "Entropy must be 32 bytes")
|
||||
|
||||
private val ZERO = BigInteger.valueOf(0)
|
||||
|
||||
private val hMac = new HMac(digest)
|
||||
private val K = new Array[Byte](hMac.getMacSize)
|
||||
private val V = new Array[Byte](hMac.getMacSize)
|
||||
|
||||
private var n = CryptoParams.curve.getN
|
||||
|
||||
override def isDeterministic = true
|
||||
|
||||
override def init(n: BigInteger, random: SecureRandom): Unit = {
|
||||
throw new IllegalStateException("Operation not supported")
|
||||
}
|
||||
|
||||
override def init(
|
||||
n: BigInteger,
|
||||
d: BigInteger,
|
||||
message: Array[Byte]): Unit = {
|
||||
this.n = n
|
||||
|
||||
Arrays.fill(V, 0x01.toByte)
|
||||
Arrays.fill(K, 0.toByte)
|
||||
|
||||
val x = new Array[Byte]((n.bitLength + 7) / 8)
|
||||
val dVal = BigIntegers.asUnsignedByteArray(d)
|
||||
|
||||
System.arraycopy(dVal, 0, x, x.length - dVal.length, dVal.length)
|
||||
|
||||
val m = new Array[Byte]((n.bitLength + 7) / 8)
|
||||
|
||||
var mInt = bitsToInt(message)
|
||||
|
||||
if (mInt.compareTo(n) >= 0)
|
||||
mInt = mInt.subtract(n)
|
||||
|
||||
val mVal = BigIntegers.asUnsignedByteArray(mInt)
|
||||
|
||||
System.arraycopy(mVal, 0, m, m.length - mVal.length, mVal.length)
|
||||
|
||||
hMac.init(new KeyParameter(K))
|
||||
|
||||
hMac.update(V, 0, V.length)
|
||||
hMac.update(0x00.toByte)
|
||||
hMac.update(x, 0, x.length)
|
||||
hMac.update(m, 0, m.length)
|
||||
hMac.update(entropy.toArray, 0, 32) // Added entropy
|
||||
|
||||
hMac.doFinal(K, 0)
|
||||
|
||||
hMac.init(new KeyParameter(K))
|
||||
|
||||
hMac.update(V, 0, V.length)
|
||||
|
||||
hMac.doFinal(V, 0)
|
||||
|
||||
hMac.update(V, 0, V.length)
|
||||
hMac.update(0x01.toByte)
|
||||
hMac.update(x, 0, x.length)
|
||||
hMac.update(m, 0, m.length)
|
||||
hMac.update(entropy.toArray, 0, 32) // Added entropy
|
||||
|
||||
hMac.doFinal(K, 0)
|
||||
|
||||
hMac.init(new KeyParameter(K))
|
||||
|
||||
hMac.update(V, 0, V.length)
|
||||
|
||||
hMac.doFinal(V, 0)
|
||||
|
||||
()
|
||||
}
|
||||
|
||||
override def nextK(): BigInteger = {
|
||||
val t = new Array[Byte]((n.bitLength + 7) / 8)
|
||||
|
||||
while (true) {
|
||||
var tOff = 0
|
||||
while (tOff < t.length) {
|
||||
hMac.update(V, 0, V.length)
|
||||
|
||||
hMac.doFinal(V, 0)
|
||||
|
||||
val len = Math.min(t.length - tOff, V.length)
|
||||
System.arraycopy(V, 0, t, tOff, len)
|
||||
tOff += len
|
||||
}
|
||||
|
||||
val k = bitsToInt(t)
|
||||
|
||||
if (k.compareTo(ZERO) > 0 && k.compareTo(n) < 0)
|
||||
return k
|
||||
|
||||
hMac.update(V, 0, V.length)
|
||||
hMac.update(0x00.toByte)
|
||||
|
||||
hMac.doFinal(K, 0)
|
||||
|
||||
hMac.init(new KeyParameter(K))
|
||||
|
||||
hMac.update(V, 0, V.length)
|
||||
|
||||
hMac.doFinal(V, 0)
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Return was supposed to happen above.")
|
||||
}
|
||||
|
||||
private def bitsToInt(t: Array[Byte]) = {
|
||||
var v = new BigInteger(1, t)
|
||||
if (t.length * 8 > n.bitLength) v = v.shiftRight(t.length * 8 - n.bitLength)
|
||||
v
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ package org.bitcoins.crypto
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.concurrent.duration.DurationInt
|
||||
import scala.concurrent.{Await, Future}
|
||||
import scala.concurrent.{Await, ExecutionContext, Future}
|
||||
|
||||
/**
|
||||
* This is meant to be an abstraction for a [[org.bitcoins.crypto.ECPrivateKey]], sometimes we will not
|
||||
@ -24,10 +24,69 @@ trait Sign {
|
||||
def signFuture(bytes: ByteVector): Future[ECDigitalSignature] =
|
||||
signFunction(bytes)
|
||||
|
||||
/** Note that using this function to generate digital signatures with specific
|
||||
* properties (by trying a bunch of entropy values) can reduce privacy as it will
|
||||
* fingerprint your wallet. Additionally it could lead to a loss of entropy in
|
||||
* the resulting nonce should the property you are interested in cause a constraint
|
||||
* on the input space.
|
||||
*
|
||||
* In short, ALL USES OF THIS FUNCTION THAT SIGN THE SAME DATA WITH DIFFERENT ENTROPY
|
||||
* HAVE THE POTENTIAL TO CAUSE REDUCTIONS IN SECURITY AND PRIVACY, BEWARE!
|
||||
*/
|
||||
def signWithEntropyFunction: (
|
||||
ByteVector,
|
||||
ByteVector) => Future[ECDigitalSignature]
|
||||
|
||||
/** Note that using this function to generate digital signatures with specific
|
||||
* properties (by trying a bunch of entropy values) can reduce privacy as it will
|
||||
* fingerprint your wallet. Additionally it could lead to a loss of entropy in
|
||||
* the resulting nonce should the property you are interested in cause a constraint
|
||||
* on the input space.
|
||||
*
|
||||
* In short, ALL USES OF THIS FUNCTION THAT SIGN THE SAME DATA WITH DIFFERENT ENTROPY
|
||||
* HAVE THE POTENTIAL TO CAUSE REDUCTIONS IN SECURITY AND PRIVACY, BEWARE!
|
||||
*/
|
||||
def signWithEntropyFuture(
|
||||
bytes: ByteVector,
|
||||
entropy: ByteVector): Future[ECDigitalSignature] =
|
||||
signWithEntropyFunction(bytes, entropy)
|
||||
|
||||
private def signLowRFuture(bytes: ByteVector, startAt: Long)(implicit
|
||||
ec: ExecutionContext): Future[ECDigitalSignature] = {
|
||||
val startBytes = ByteVector.fromLong(startAt).padLeft(32)
|
||||
|
||||
val sigF: Future[ECDigitalSignature] =
|
||||
signWithEntropyFunction(bytes, startBytes)
|
||||
|
||||
sigF.flatMap { sig =>
|
||||
if (sig.bytes.length <= 70) {
|
||||
Future.successful(sig)
|
||||
} else {
|
||||
signLowRFuture(bytes, startAt + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def signLowRFuture(bytes: ByteVector)(implicit
|
||||
ec: ExecutionContext): Future[ECDigitalSignature] = {
|
||||
signLowRFuture(bytes, startAt = 0)
|
||||
}
|
||||
|
||||
def sign(bytes: ByteVector): ECDigitalSignature = {
|
||||
Await.result(signFuture(bytes), 30.seconds)
|
||||
}
|
||||
|
||||
def signLowR(bytes: ByteVector)(implicit
|
||||
ec: ExecutionContext): ECDigitalSignature = {
|
||||
Await.result(signLowRFuture(bytes), 30.seconds)
|
||||
}
|
||||
|
||||
def signWithEntropy(
|
||||
bytes: ByteVector,
|
||||
entropy: ByteVector): ECDigitalSignature = {
|
||||
Await.result(signWithEntropyFuture(bytes, entropy), 30.seconds)
|
||||
}
|
||||
|
||||
def publicKey: ECPublicKey
|
||||
}
|
||||
|
||||
@ -35,17 +94,25 @@ object Sign {
|
||||
|
||||
private case class SignImpl(
|
||||
signFunction: ByteVector => Future[ECDigitalSignature],
|
||||
signWithEntropyFunction: (
|
||||
ByteVector,
|
||||
ByteVector) => Future[ECDigitalSignature],
|
||||
publicKey: ECPublicKey)
|
||||
extends Sign
|
||||
|
||||
def apply(
|
||||
signFunction: ByteVector => Future[ECDigitalSignature],
|
||||
signWithEntropyFunction: (
|
||||
ByteVector,
|
||||
ByteVector) => Future[ECDigitalSignature],
|
||||
pubKey: ECPublicKey): Sign = {
|
||||
SignImpl(signFunction, pubKey)
|
||||
SignImpl(signFunction, signWithEntropyFunction, pubKey)
|
||||
}
|
||||
|
||||
def constant(sig: ECDigitalSignature, pubKey: ECPublicKey): Sign = {
|
||||
SignImpl(_ => Future.successful(sig), pubKey)
|
||||
SignImpl(_ => Future.successful(sig),
|
||||
(_, _) => Future.successful(sig),
|
||||
pubKey)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,9 +124,6 @@ object Sign {
|
||||
* a specific private key on another server
|
||||
*/
|
||||
def dummySign(publicKey: ECPublicKey): Sign = {
|
||||
SignImpl({ _: ByteVector =>
|
||||
Future.successful(EmptyDigitalSignature)
|
||||
},
|
||||
publicKey)
|
||||
constant(EmptyDigitalSignature, publicKey)
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit e0239255f1f386cf76279c008253b70cd17fe466
|
||||
Subproject commit c9bab11ef05ef0cdc0cb0f0e02d3c8e052799767
|
Binary file not shown.
@ -113,6 +113,52 @@ public class NativeSecp256k1 {
|
||||
return retVal == 0 ? new byte[0] : sigArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 Create an ECDSA signature adding specified entropy.
|
||||
*
|
||||
* This can be used to include your own entropy to nonce generation
|
||||
* in addition to the message and private key, while still doing so deterministically.
|
||||
*
|
||||
* In particular, this is used when generating low R signatures.
|
||||
* See https://github.com/bitcoin/bitcoin/pull/13666/
|
||||
*
|
||||
* @param data Message hash, 32 bytes
|
||||
* @param seckey ECDSA Secret key, 32 bytes
|
||||
* @param entropy 32 bytes of entropy
|
||||
* @return sig byte array of signature
|
||||
*/
|
||||
public static byte[] signWithEntropy(byte[] data, byte[] seckey, byte[] entropy) throws AssertFailException{
|
||||
checkArgument(data.length == 32 && seckey.length == 32 && entropy.length == 32);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < 32 + 32 + 32) {
|
||||
byteBuff = ByteBuffer.allocateDirect(32 + 32 + 32);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(data);
|
||||
byteBuff.put(seckey);
|
||||
byteBuff.put(entropy);
|
||||
|
||||
byte[][] retByteArray;
|
||||
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_ecdsa_sign_with_entropy(byteBuff, Secp256k1Context.getContext());
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] sigArr = retByteArray[0];
|
||||
int sigLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
|
||||
|
||||
assertEquals(sigArr.length, sigLen, "Got bad signature length.");
|
||||
|
||||
return retVal == 0 ? new byte[0] : sigArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 Seckey Verify - Verifies an ECDSA secret key
|
||||
*
|
||||
@ -526,6 +572,8 @@ public class NativeSecp256k1 {
|
||||
|
||||
private static native byte[][] secp256k1_ecdsa_sign(ByteBuffer byteBuff, long context);
|
||||
|
||||
private static native byte[][] secp256k1_ecdsa_sign_with_entropy(ByteBuffer byteBuff, long context);
|
||||
|
||||
private static native int secp256k1_ec_seckey_verify(ByteBuffer byteBuff, long context);
|
||||
|
||||
private static native byte[][] secp256k1_ec_pubkey_create(ByteBuffer byteBuff, long context, boolean compressed);
|
||||
|
@ -86,8 +86,8 @@ public class NativeSecp256k1Test {
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests sign() for a valid secretkey
|
||||
*/
|
||||
* This tests sign() for a valid secretkey
|
||||
*/
|
||||
@Test
|
||||
public void testSignPos() throws AssertFailException{
|
||||
|
||||
@ -112,6 +112,35 @@ public class NativeSecp256k1Test {
|
||||
assertEquals( sigString, "" , "testSignNeg");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests signWithEntropy() for a valid secretkey
|
||||
*/
|
||||
@Test
|
||||
public void testSignWithEntropyPos() throws AssertFailException{
|
||||
|
||||
byte[] data = toByteArray("53702647283D86B3D6410ADEF184EC608372CC3DD8B9202795D731EB1EA54275");
|
||||
byte[] sec = toByteArray("B4F62DE42D38D5D24B66FF01761C3FD0A6E7C8B719E0DC54D168FA013BFAF97F");
|
||||
byte[] entropy = toByteArray("EDF312C904B610B11442320FFB94C4F976831051A481A17176CE2B81EB3A8B6F");
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.signWithEntropy(data, sec, entropy);
|
||||
String sigString = toHex(resultArr);
|
||||
assertEquals( sigString, "30450221009D9714BE0CE9A3FD08497125C6D01362FDE2FF118FC817FDB14EE4C38CADFB7A022033B082E161F7D75ABC25642ED71226049DC59EC14AB19DF2A8EFEA47A6C75FAC" , "testSignWithEntropyPos");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests signWithEntropy() for a invalid secretkey
|
||||
*/
|
||||
@Test
|
||||
public void testSignWithEntropyNeg() throws AssertFailException{
|
||||
byte[] data = toByteArray("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90"); //sha256hash of "testing"
|
||||
byte[] sec = toByteArray("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
|
||||
byte[] entropy = toByteArray("EDF312C904B610B11442320FFB94C4F976831051A481A17176CE2B81EB3A8B6F");
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.signWithEntropy(data, sec, entropy);
|
||||
String sigString = toHex(resultArr);
|
||||
assertEquals( sigString, "" , "testSignWithEntropyNeg");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests private key tweak-add
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user