mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-24 15:02:17 +01:00
Merge pull request #60 from Christewart/libsecp256k1
Libsecp256k1 integration
This commit is contained in:
commit
f6bc61c7cd
13 changed files with 839 additions and 44 deletions
53
.gitignore
vendored
53
.gitignore
vendored
|
@ -16,4 +16,55 @@ project/plugins/project/
|
|||
.scala_dependencies
|
||||
.worksheet
|
||||
.idea/*
|
||||
*.iml
|
||||
*.iml
|
||||
|
||||
# secp256k1 ignore files
|
||||
bench_inv
|
||||
bench_ecdh
|
||||
bench_sign
|
||||
bench_verify
|
||||
bench_schnorr_verify
|
||||
bench_recover
|
||||
bench_internal
|
||||
tests
|
||||
exhaustive_tests
|
||||
gen_context
|
||||
*.exe
|
||||
*.so
|
||||
*.a
|
||||
!.gitignore
|
||||
|
||||
Makefile
|
||||
configure
|
||||
.libs/
|
||||
Makefile.in
|
||||
aclocal.m4
|
||||
autom4te.cache/
|
||||
config.log
|
||||
config.status
|
||||
*.tar.gz
|
||||
*.la
|
||||
libtool
|
||||
.deps/
|
||||
.dirstamp
|
||||
*.lo
|
||||
*.o
|
||||
*~
|
||||
src/libsecp256k1-config.h
|
||||
src/libsecp256k1-config.h.in
|
||||
src/ecmult_static_context.h
|
||||
build-aux/config.guess
|
||||
build-aux/config.sub
|
||||
build-aux/depcomp
|
||||
build-aux/install-sh
|
||||
build-aux/ltmain.sh
|
||||
build-aux/m4/libtool.m4
|
||||
build-aux/m4/lt~obsolete.m4
|
||||
build-aux/m4/ltoptions.m4
|
||||
build-aux/m4/ltsugar.m4
|
||||
build-aux/m4/ltversion.m4
|
||||
build-aux/missing
|
||||
build-aux/compile
|
||||
build-aux/test-driver
|
||||
src/stamp-h1
|
||||
libsecp256k1.pc
|
||||
|
|
457
src/main/java/org/bitcoin/NativeSecp256k1.java
Normal file
457
src/main/java/org/bitcoin/NativeSecp256k1.java
Normal file
|
@ -0,0 +1,457 @@
|
|||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
* Copyright 2014-2016 the libsecp256k1 contributors
|
||||
*
|
||||
* 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 java.math.BigInteger;
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import static org.bitcoin.NativeSecp256k1Util.*;
|
||||
|
||||
/**
|
||||
* <p>This class holds native methods to handle ECDSA verification.</p>
|
||||
*
|
||||
* <p>You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1</p>
|
||||
*
|
||||
* <p>To build secp256k1 for use with bitcoinj, run
|
||||
* `./configure --enable-jni --enable-experimental --enable-module-ecdh`
|
||||
* and `make` then copy `.libs/libsecp256k1.so` to your system library path
|
||||
* or point the JVM to the folder containing it with -Djava.library.path
|
||||
* </p>
|
||||
*/
|
||||
public class NativeSecp256k1 {
|
||||
|
||||
private static final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
|
||||
private static final Lock r = rwl.readLock();
|
||||
private static final Lock w = rwl.writeLock();
|
||||
private static ThreadLocal<ByteBuffer> nativeECDSABuffer = new ThreadLocal<ByteBuffer>();
|
||||
/**
|
||||
* 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) throws AssertFailException{
|
||||
checkInvariant(data.length == 32 && signature.length <= 520 && pub.length <= 520);
|
||||
checkInvariant(Secp256k1Context.isEnabled());
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < 520) {
|
||||
byteBuff = ByteBuffer.allocateDirect(520);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(data);
|
||||
byteBuff.put(signature);
|
||||
byteBuff.put(pub);
|
||||
|
||||
byte[][] retByteArray;
|
||||
|
||||
r.lock();
|
||||
try {
|
||||
return secp256k1_ecdsa_verify(byteBuff, Secp256k1Context.getContext(),
|
||||
signature.length, pub.length) == 1;
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 Create an ECDSA signature.
|
||||
*
|
||||
* @param data Message hash, 32 bytes
|
||||
* @param key Secret key, 32 bytes
|
||||
*
|
||||
* Return values
|
||||
* @param sig byte array of signature
|
||||
*/
|
||||
public static byte[] sign(byte[] data, byte[] sec) throws AssertFailException{
|
||||
checkInvariant(data != null);
|
||||
checkInvariant(data.length == 32);
|
||||
checkInvariant(sec.length <= 32);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < 32 + 32) {
|
||||
byteBuff = ByteBuffer.allocateDirect(32 + 32);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(data);
|
||||
byteBuff.put(sec);
|
||||
|
||||
byte[][] retByteArray;
|
||||
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_ecdsa_sign(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 - returns 1 if valid, 0 if invalid
|
||||
*
|
||||
* @param seckey ECDSA Secret key, 32 bytes
|
||||
*/
|
||||
public static boolean secKeyVerify(byte[] seckey) {
|
||||
checkInvariant(seckey.length == 32);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < seckey.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(seckey.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(seckey);
|
||||
|
||||
r.lock();
|
||||
try {
|
||||
return secp256k1_ec_seckey_verify(byteBuff,Secp256k1Context.getContext()) == 1;
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* libsecp256k1 Compute Pubkey - computes public key from secret key
|
||||
*
|
||||
* @param seckey ECDSA Secret key, 32 bytes
|
||||
*
|
||||
* Return values
|
||||
* @param pubkey ECDSA Public key, 33 or 65 bytes
|
||||
*/
|
||||
//TODO add a 'compressed' arg
|
||||
public static byte[] computePubkey(byte[] seckey, boolean fCompressed) throws AssertFailException{
|
||||
checkInvariant(seckey.length == 32);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < seckey.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(seckey.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(seckey);
|
||||
|
||||
byte[][] retByteArray;
|
||||
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_ec_pubkey_create(byteBuff, Secp256k1Context.getContext(), fCompressed);
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] pubArr = retByteArray[0];
|
||||
int pubLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
|
||||
|
||||
assertEquals(pubArr.length, pubLen, "Got bad pubkey length.");
|
||||
|
||||
return retVal == 0 ? new byte[0]: pubArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 Cleanup - This destroys the secp256k1 context object
|
||||
* This should be called at the end of the program for proper cleanup of the context.
|
||||
*/
|
||||
public static synchronized void cleanup() {
|
||||
w.lock();
|
||||
try {
|
||||
secp256k1_destroy_context(Secp256k1Context.getContext());
|
||||
} finally {
|
||||
w.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public static long cloneContext() {
|
||||
r.lock();
|
||||
try {
|
||||
return secp256k1_ctx_clone(Secp256k1Context.getContext());
|
||||
} finally { r.unlock(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 PrivKey Tweak-Mul - Tweak privkey by multiplying to it
|
||||
*
|
||||
* @param tweak some bytes to tweak with
|
||||
* @param seckey 32-byte seckey
|
||||
*/
|
||||
public static byte[] privKeyTweakMul(byte[] privkey, byte[] tweak) throws AssertFailException{
|
||||
checkInvariant(privkey.length == 32);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(privkey);
|
||||
byteBuff.put(tweak);
|
||||
|
||||
byte[][] retByteArray;
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_privkey_tweak_mul(byteBuff,Secp256k1Context.getContext());
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] privArr = retByteArray[0];
|
||||
|
||||
int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
|
||||
|
||||
assertEquals(privArr.length, privLen, "Got bad pubkey length.");
|
||||
|
||||
assertEquals(retVal, 1, "Failed return value check.");
|
||||
|
||||
return privArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 PrivKey Tweak-Add - Tweak privkey by adding to it
|
||||
*
|
||||
* @param tweak some bytes to tweak with
|
||||
* @param seckey 32-byte seckey
|
||||
*/
|
||||
public static byte[] privKeyTweakAdd(byte[] privkey, byte[] tweak) throws AssertFailException{
|
||||
checkInvariant(privkey.length == 32);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(privkey);
|
||||
byteBuff.put(tweak);
|
||||
|
||||
byte[][] retByteArray;
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_privkey_tweak_add(byteBuff,Secp256k1Context.getContext());
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] privArr = retByteArray[0];
|
||||
|
||||
int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
|
||||
|
||||
assertEquals(privArr.length, privLen, "Got bad pubkey length.");
|
||||
|
||||
assertEquals(retVal, 1, "Failed return value check.");
|
||||
|
||||
return privArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 PubKey Tweak-Add - Tweak pubkey by adding to it
|
||||
*
|
||||
* @param tweak some bytes to tweak with
|
||||
* @param pubkey 32-byte seckey
|
||||
*/
|
||||
public static byte[] pubKeyTweakAdd(byte[] pubkey, byte[] tweak, boolean fCompressed) throws AssertFailException{
|
||||
checkInvariant(pubkey.length == 33 || pubkey.length == 65);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(pubkey);
|
||||
byteBuff.put(tweak);
|
||||
|
||||
byte[][] retByteArray;
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_pubkey_tweak_add(byteBuff,Secp256k1Context.getContext(), pubkey.length, fCompressed);
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] pubArr = retByteArray[0];
|
||||
|
||||
int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
|
||||
|
||||
assertEquals(pubArr.length, pubLen, "Got bad pubkey length.");
|
||||
|
||||
assertEquals(retVal, 1, "Failed return value check.");
|
||||
|
||||
return pubArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 PubKey Tweak-Mul - Tweak pubkey by multiplying to it
|
||||
*
|
||||
* @param tweak some bytes to tweak with
|
||||
* @param pubkey 32-byte seckey
|
||||
*/
|
||||
public static byte[] pubKeyTweakMul(byte[] pubkey, byte[] tweak, boolean fCompressed) throws AssertFailException{
|
||||
checkInvariant(pubkey.length == 33 || pubkey.length == 65);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(pubkey);
|
||||
byteBuff.put(tweak);
|
||||
|
||||
byte[][] retByteArray;
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_pubkey_tweak_mul(byteBuff,Secp256k1Context.getContext(), pubkey.length, fCompressed);
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] pubArr = retByteArray[0];
|
||||
|
||||
int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
|
||||
|
||||
assertEquals(pubArr.length, pubLen, "Got bad pubkey length.");
|
||||
|
||||
assertEquals(retVal, 1, "Failed return value check.");
|
||||
|
||||
return pubArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 create ECDH secret - constant time ECDH calculation
|
||||
*
|
||||
* @param seckey byte array of secret key used in exponentiaion
|
||||
* @param pubkey byte array of public key used in exponentiaion
|
||||
*/
|
||||
public static byte[] createECDHSecret(byte[] seckey, byte[] pubkey) throws AssertFailException{
|
||||
checkInvariant(seckey.length <= 32 && pubkey.length <= 65);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < 32 + pubkey.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(32 + pubkey.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(seckey);
|
||||
byteBuff.put(pubkey);
|
||||
|
||||
byte[][] retByteArray;
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_ecdh(byteBuff, Secp256k1Context.getContext(), pubkey.length);
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] resArr = retByteArray[0];
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
|
||||
|
||||
assertEquals(resArr.length, 32, "Got bad result length.");
|
||||
assertEquals(retVal, 1, "Failed return value check.");
|
||||
|
||||
return resArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 randomize - updates the context randomization
|
||||
*
|
||||
* @param seed 32-byte random seed
|
||||
*/
|
||||
public static synchronized boolean randomize(byte[] seed) throws AssertFailException{
|
||||
checkInvariant(seed.length == 32 || seed == null);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < seed.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(seed.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(seed);
|
||||
|
||||
w.lock();
|
||||
try {
|
||||
return secp256k1_context_randomize(byteBuff, Secp256k1Context.getContext()) == 1;
|
||||
} finally {
|
||||
w.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private static native long secp256k1_ctx_clone(long context);
|
||||
|
||||
private static native int secp256k1_context_randomize(ByteBuffer byteBuff, long context);
|
||||
|
||||
private static native byte[][] secp256k1_privkey_tweak_add(ByteBuffer byteBuff, long context);
|
||||
|
||||
private static native byte[][] secp256k1_privkey_tweak_mul(ByteBuffer byteBuff, long context);
|
||||
|
||||
private static native byte[][] secp256k1_pubkey_tweak_add(ByteBuffer byteBuff, long context, int pubLen, boolean fCompressed);
|
||||
|
||||
private static native byte[][] secp256k1_pubkey_tweak_mul(ByteBuffer byteBuff, long context, int pubLen, boolean fCompressed);
|
||||
|
||||
private static native void secp256k1_destroy_context(long context);
|
||||
|
||||
private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff, long context, int sigLen, int pubLen);
|
||||
|
||||
private static native byte[][] secp256k1_ecdsa_sign(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 fCompressed);
|
||||
|
||||
private static native byte[][] secp256k1_ec_pubkey_parse(ByteBuffer byteBuff, long context, int inputLen);
|
||||
|
||||
private static native byte[][] secp256k1_ecdh(ByteBuffer byteBuff, long context, int inputLen);
|
||||
|
||||
|
||||
private static void checkInvariant(boolean result) throws IllegalArgumentException {
|
||||
if (!result) throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
45
src/main/java/org/bitcoin/NativeSecp256k1Util.java
Normal file
45
src/main/java/org/bitcoin/NativeSecp256k1Util.java
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2014-2016 the libsecp256k1 contributors
|
||||
*
|
||||
* 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;
|
||||
|
||||
public class NativeSecp256k1Util {
|
||||
|
||||
public static void assertEquals( int val, int val2, String message ) throws AssertFailException{
|
||||
if( val != val2 )
|
||||
throw new AssertFailException("FAIL: " + message);
|
||||
}
|
||||
|
||||
public static void assertEquals( boolean val, boolean val2, String message ) throws AssertFailException{
|
||||
if( val != val2 )
|
||||
throw new AssertFailException("FAIL: " + message);
|
||||
else
|
||||
System.out.println("PASS: " + message);
|
||||
}
|
||||
|
||||
public static void assertEquals( String val, String val2, String message ) throws AssertFailException{
|
||||
if( !val.equals(val2) )
|
||||
throw new AssertFailException("FAIL: " + message);
|
||||
else
|
||||
System.out.println("PASS: " + message);
|
||||
}
|
||||
|
||||
public static class AssertFailException extends Exception {
|
||||
public AssertFailException(String message) {
|
||||
super( message );
|
||||
}
|
||||
}
|
||||
}
|
51
src/main/java/org/bitcoin/Secp256k1Context.java
Normal file
51
src/main/java/org/bitcoin/Secp256k1Context.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2014-2016 the libsecp256k1 contributors
|
||||
*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* This class holds the context reference used in native methods
|
||||
* to handle ECDSA operations.
|
||||
*/
|
||||
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
|
||||
|
||||
static { //static initializer
|
||||
boolean isEnabled = true;
|
||||
long contextRef = -1;
|
||||
try {
|
||||
System.loadLibrary("secp256k1");
|
||||
contextRef = secp256k1_init_context();
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
System.out.println("UnsatisfiedLinkError: " + e.toString());
|
||||
isEnabled = false;
|
||||
}
|
||||
enabled = isEnabled;
|
||||
context = contextRef;
|
||||
}
|
||||
|
||||
public static boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public static long getContext() {
|
||||
if(!enabled) return -1; //sanity check
|
||||
return context;
|
||||
}
|
||||
|
||||
private static native long secp256k1_init_context();
|
||||
}
|
|
@ -17,8 +17,7 @@ sealed trait ECDigitalSignature extends BitcoinSLogger {
|
|||
/**
|
||||
* Checks if this signature is encoded to DER correctly
|
||||
* https://crypto.stackexchange.com/questions/1795/how-can-i-convert-a-der-ecdsa-signature-to-asn-1
|
||||
*
|
||||
* @return boolean representing if the signature is a valid
|
||||
* @return boolean representing if the signature is a valid
|
||||
*/
|
||||
def isDEREncoded : Boolean = DERSignatureUtil.isDEREncoded(this)
|
||||
|
||||
|
@ -27,32 +26,26 @@ sealed trait ECDigitalSignature extends BitcoinSLogger {
|
|||
* [[https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki]]
|
||||
* */
|
||||
def isStrictEncoded: Boolean = DERSignatureUtil.isValidSignatureEncoding(this)
|
||||
|
||||
/**
|
||||
* Decodes the digital signature into it's r and s points
|
||||
* throws an exception if the given sequence of bytes is not a DER encoded signature
|
||||
*
|
||||
* @return the (r,s) values for the elliptic curve digital signature
|
||||
* @return the (r,s) values for the elliptic curve digital signature
|
||||
*/
|
||||
def decodeSignature : (BigInt,BigInt) = DERSignatureUtil.decodeSignature(this)
|
||||
|
||||
|
||||
/**
|
||||
* Represents the r value found in a elliptic curve digital signature
|
||||
*/
|
||||
/** Represents the r value found in a elliptic curve digital signature */
|
||||
def r = decodeSignature._1
|
||||
|
||||
|
||||
/**
|
||||
* Represents the s value found in a elliptic curve digital signature
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
/** Represents the s value found in a elliptic curve digital signature */
|
||||
def s = decodeSignature._2
|
||||
|
||||
}
|
||||
|
||||
case object EmptyDigitalSignature extends ECDigitalSignature {
|
||||
def bytes = Seq()
|
||||
def bytes = Nil
|
||||
override def r = java.math.BigInteger.valueOf(0)
|
||||
override def s = r
|
||||
}
|
||||
|
@ -91,12 +84,4 @@ object ECDigitalSignature extends Factory[ECDigitalSignature] {
|
|||
r.toByteArray.toSeq ++ Seq(0x2.toByte, s.toByteArray.size.toByte) ++ s.toByteArray.toSeq
|
||||
fromBytes(bytes)
|
||||
}
|
||||
|
||||
|
||||
/** Checks if the given digital signature uses a low s value,
|
||||
* if it does not it converts it to a low s value and returns it */
|
||||
private def lowS(signature: ECDigitalSignature): ECDigitalSignature = {
|
||||
if (signature.s.bigInteger.compareTo(CryptoParams.halfCurveOrder) <= 0) signature
|
||||
else ECDigitalSignature(signature.r,CryptoParams.curve.getN().subtract(signature.s.bigInteger))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,3 +9,4 @@ trait ECKey {
|
|||
}
|
||||
|
||||
case class ECKeyImpl(privateKey : Option[ECPrivateKey], publicKey : ECPublicKey) extends ECKey
|
||||
|
||||
|
|
|
@ -6,16 +6,10 @@ import org.bitcoins.core.util.{Factory, BitcoinSUtil}
|
|||
* Created by chris on 5/24/16.
|
||||
*/
|
||||
sealed trait HashDigest {
|
||||
/**
|
||||
* The message digest represented in bytes
|
||||
* @return
|
||||
*/
|
||||
/** The message digest represented in bytes */
|
||||
def bytes : Seq[Byte]
|
||||
|
||||
/**
|
||||
* The message digest represented in hexadecimal
|
||||
* @return
|
||||
*/
|
||||
/** The message digest represented in hexadecimal */
|
||||
def hex : String = BitcoinSUtil.encodeHex(bytes)
|
||||
}
|
||||
|
||||
|
@ -39,6 +33,7 @@ sealed trait Sha256Digest extends HashDigest
|
|||
|
||||
object Sha256Digest extends Factory[Sha256Digest] {
|
||||
private case class Sha256DigestImpl(bytes: Seq[Byte]) extends Sha256Digest {
|
||||
require(bytes.length == 32, "Sha256Digest must be 32 bytes in size, got: " + bytes.length)
|
||||
override def toString = "Sha256DigestImpl(" + hex + ")"
|
||||
}
|
||||
override def fromBytes(bytes:Seq[Byte]) : Sha256Digest = Sha256DigestImpl(bytes)
|
||||
|
@ -51,6 +46,7 @@ sealed trait DoubleSha256Digest extends HashDigest
|
|||
|
||||
object DoubleSha256Digest extends Factory[DoubleSha256Digest] {
|
||||
private case class DoubleSha256DigestImpl(bytes: Seq[Byte]) extends DoubleSha256Digest {
|
||||
require(bytes.length == 32, "DoubleSha256Digest must always be 32 bytes, got: " + bytes.length)
|
||||
override def toString = "DoubleSha256DigestImpl(" + hex + ")"
|
||||
}
|
||||
override def fromBytes(bytes : Seq[Byte]) : DoubleSha256Digest = DoubleSha256DigestImpl(bytes)
|
||||
|
@ -64,19 +60,21 @@ sealed trait RipeMd160Digest extends HashDigest
|
|||
|
||||
object RipeMd160Digest extends Factory[RipeMd160Digest] {
|
||||
private case class RipeMd160DigestImpl(bytes : Seq[Byte]) extends RipeMd160Digest {
|
||||
require(bytes.length == 20, "RIPEMD160Digest must always be 20 bytes, got: " + bytes.length)
|
||||
override def toString = "RipeMd160DigestImpl(" + hex + ")"
|
||||
}
|
||||
override def fromBytes(bytes : Seq[Byte]) : RipeMd160Digest = RipeMd160DigestImpl(bytes)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the result of RIPEMD160(SHA256())
|
||||
*/
|
||||
sealed trait Sha256Hash160Digest extends HashDigest
|
||||
|
||||
object Sha256Hash160Digest extends Factory[Sha256Hash160Digest] {
|
||||
case class Sha256Hash160DigestImpl(bytes : Seq[Byte]) extends Sha256Hash160Digest {
|
||||
private case class Sha256Hash160DigestImpl(bytes : Seq[Byte]) extends Sha256Hash160Digest {
|
||||
require(bytes.length == 20, "Sha256Hash160Digest must always be 20 bytes, got: " + bytes.length)
|
||||
override def toString = "Sha256Hash160DigestImpl(" + hex + ")"
|
||||
}
|
||||
override def fromBytes(bytes : Seq[Byte]) = Sha256Hash160DigestImpl(bytes)
|
||||
override def fromBytes(bytes : Seq[Byte]): Sha256Hash160Digest = Sha256Hash160DigestImpl(bytes)
|
||||
}
|
|
@ -62,7 +62,8 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
|
|||
}
|
||||
|
||||
logger.debug("Hash for signature: " + BitcoinSUtil.encodeHex(hashForSignature.bytes))
|
||||
val isValid = pubKey.verify(hashForSignature,signature)
|
||||
val sigWithoutHashType = stripHashType(signature)
|
||||
val isValid = pubKey.verify(hashForSignature,sigWithoutHashType)
|
||||
if (isValid) SignatureValidationSuccess
|
||||
else nullFailCheck(Seq(signature),SignatureValidationErrorIncorrectSignatures, flags)
|
||||
}
|
||||
|
@ -123,6 +124,7 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
|
|||
|
||||
|
||||
}
|
||||
|
||||
/** If the NULLFAIL flag is set as defined in BIP146, it checks to make sure all failed signatures were an empty byte vector
|
||||
* [[https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#NULLFAIL]]
|
||||
* */
|
||||
|
@ -134,6 +136,11 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
|
|||
SignatureValidationErrorNullFail
|
||||
} else result
|
||||
}
|
||||
|
||||
/** Removes the hash type from the [[org.bitcoins.core.crypto.ECDigitalSignature]] */
|
||||
private def stripHashType(sig: ECDigitalSignature): ECDigitalSignature = {
|
||||
ECDigitalSignature(sig.bytes.slice(0,sig.bytes.length-1))
|
||||
}
|
||||
}
|
||||
|
||||
object TransactionSignatureChecker extends TransactionSignatureChecker
|
||||
|
|
|
@ -23,6 +23,7 @@ trait TransactionSignatureCreator extends BitcoinSLogger {
|
|||
val sig = ECDigitalSignature(signature.bytes ++ Seq(hashType.byte))
|
||||
logger.debug("TxSigCreator sig: " + sig)
|
||||
require(sig.isStrictEncoded, "We did not create a signature that is strictly encoded, got: " + sig)
|
||||
require(DERSignatureUtil.isLowS(sig), "Sig does not have a low s value")
|
||||
sig
|
||||
}
|
||||
}
|
||||
|
|
193
src/test/java/org/bitcoin/NativeSecp256k1Test.java
Normal file
193
src/test/java/org/bitcoin/NativeSecp256k1Test.java
Normal file
|
@ -0,0 +1,193 @@
|
|||
package org.bitcoin;
|
||||
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.bitcoin.NativeSecp256k1Util.AssertFailException;
|
||||
import static org.bitcoin.NativeSecp256k1Util.assertEquals;
|
||||
|
||||
/**
|
||||
* This class holds test cases defined for testing this library.
|
||||
*/
|
||||
public class NativeSecp256k1Test {
|
||||
|
||||
//TODO improve comments/add more tests
|
||||
/**
|
||||
* This tests verify() for a valid signature
|
||||
*/
|
||||
@Test
|
||||
public void testVerifyPos() throws AssertFailException {
|
||||
boolean result = false;
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
|
||||
byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase());
|
||||
byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
|
||||
|
||||
result = NativeSecp256k1.verify( data, sig, pub);
|
||||
assertEquals( result, true , "testVerifyPos");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests verify() for a non-valid signature
|
||||
*/
|
||||
@Test
|
||||
public void testVerifyNeg() throws AssertFailException{
|
||||
boolean result = false;
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A91".toLowerCase()); //sha256hash of "testing"
|
||||
byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase());
|
||||
byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
|
||||
|
||||
result = NativeSecp256k1.verify( data, sig, pub);
|
||||
//System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
|
||||
assertEquals( result, false , "testVerifyNeg");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests secret key verify() for a valid secretkey
|
||||
*/
|
||||
@Test
|
||||
public void testSecKeyVerifyPos() throws AssertFailException{
|
||||
boolean result = false;
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
|
||||
|
||||
result = NativeSecp256k1.secKeyVerify( sec );
|
||||
//System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
|
||||
assertEquals( result, true , "testSecKeyVerifyPos");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests secret key verify() for a invalid secretkey
|
||||
*/
|
||||
@Test
|
||||
public void testSecKeyVerifyNeg() throws AssertFailException{
|
||||
boolean result = false;
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());
|
||||
|
||||
result = NativeSecp256k1.secKeyVerify( sec );
|
||||
//System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
|
||||
assertEquals( result, false , "testSecKeyVerifyNeg");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests public key create() for a valid secretkey
|
||||
*/
|
||||
@Test
|
||||
public void testPubKeyCreatePos() throws AssertFailException{
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.computePubkey( sec, true);
|
||||
String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( pubkeyString , "02C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D" , "testPubKeyCreatePos");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests public key create() for a invalid secretkey
|
||||
*/
|
||||
@Test
|
||||
public void testPubKeyCreateNeg() throws AssertFailException{
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.computePubkey( sec, true);
|
||||
String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( pubkeyString, "" , "testPubKeyCreateNeg");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests sign() for a valid secretkey
|
||||
*/
|
||||
@Test
|
||||
public void testSignPos() throws AssertFailException{
|
||||
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.sign(data, sec);
|
||||
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( sigString, "30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9" , "testSignPos");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests sign() for a invalid secretkey
|
||||
*/
|
||||
@Test
|
||||
public void testSignNeg() throws AssertFailException{
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.sign(data, sec);
|
||||
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( sigString, "" , "testSignNeg");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests private key tweak-add
|
||||
*/
|
||||
@Test
|
||||
public void testPrivKeyTweakAdd_1() throws AssertFailException {
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.privKeyTweakAdd( sec , data );
|
||||
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( sigString , "A168571E189E6F9A7E2D657A4B53AE99B909F7E712D1C23CED28093CD57C88F3" , "testPrivKeyAdd_1");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests private key tweak-mul
|
||||
*/
|
||||
@Test
|
||||
public void testPrivKeyTweakMul_1() throws AssertFailException {
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.privKeyTweakMul( sec , data );
|
||||
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( sigString , "97F8184235F101550F3C71C927507651BD3F1CDB4A5A33B8986ACF0DEE20FFFC" , "testPrivKeyMul_1");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests private key tweak-add uncompressed
|
||||
*/
|
||||
@Test
|
||||
public void testPrivKeyTweakAdd_2() throws AssertFailException {
|
||||
byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.pubKeyTweakAdd( pub , data, false );
|
||||
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( sigString , "0411C6790F4B663CCE607BAAE08C43557EDC1A4D11D88DFCB3D841D0C6A941AF525A268E2A863C148555C48FB5FBA368E88718A46E205FABC3DBA2CCFFAB0796EF" , "testPrivKeyAdd_2");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests private key tweak-mul uncompressed
|
||||
*/
|
||||
@Test
|
||||
public void testPrivKeyTweakMul_2() throws AssertFailException {
|
||||
byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.pubKeyTweakMul( pub , data, false );
|
||||
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( sigString , "04E0FE6FE55EBCA626B98A807F6CAF654139E14E5E3698F01A9A658E21DC1D2791EC060D4F412A794D5370F672BC94B722640B5F76914151CFCA6E712CA48CC589", "testPrivKeyMul_2");
|
||||
}
|
||||
/**
|
||||
* This tests seed randomization
|
||||
*/
|
||||
@Test
|
||||
public void testRandomize() throws AssertFailException {
|
||||
byte[] seed = BaseEncoding.base16().lowerCase().decode("A441B15FE9A3CF56661190A0B93B9DEC7D04127288CC87250967CF3B52894D11".toLowerCase()); //sha256hash of "random"
|
||||
boolean result = NativeSecp256k1.randomize(seed);
|
||||
assertEquals( result, true, "testRandomize");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateECDHSecret() throws AssertFailException{
|
||||
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
|
||||
byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.createECDHSecret(sec, pub);
|
||||
String ecdhString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( ecdhString, "2A2A67007A926E6594AF3EB564FC74005B37A9C8AEF2033C4552051B5C87F043" , "testCreateECDHSecret");
|
||||
}
|
||||
|
||||
}
|
|
@ -16,7 +16,8 @@ class ECPublicKeyTest extends FlatSpec with MustMatchers {
|
|||
val privateKeyHex = "180cb41c7c600be951b5d3d0a7334acc7506173875834f7a6c4c786a28fcbb19"
|
||||
val key: ECPrivateKey = ECPrivateKey(privateKeyHex)
|
||||
|
||||
val signature: ECDigitalSignature = key.sign(Sha256Hash.ZERO_HASH.getBytes.toSeq)
|
||||
val hash = DoubleSha256Digest(Sha256Hash.ZERO_HASH.getBytes.toSeq)
|
||||
val signature: ECDigitalSignature = key.sign(hash)
|
||||
|
||||
val isValid : Boolean = key.publicKey.verify(Sha256Hash.ZERO_HASH.getBytes.toSeq,signature)
|
||||
isValid must be (true)
|
||||
|
@ -27,10 +28,11 @@ class ECPublicKeyTest extends FlatSpec with MustMatchers {
|
|||
it must "fail to verify a piece of data if the wrong public key is given" in {
|
||||
val privateKeyHex = "180cb41c7c600be951b5d3d0a7334acc7506173875834f7a6c4c786a28fcbb19"
|
||||
val key: ECPrivateKey = ECPrivateKey(privateKeyHex)
|
||||
val signature: ECDigitalSignature = key.sign(Sha256Hash.ZERO_HASH.getBytes.toSeq)
|
||||
val hash = DoubleSha256Digest(Sha256Hash.ZERO_HASH.getBytes.toSeq)
|
||||
val signature: ECDigitalSignature = key.sign(hash)
|
||||
|
||||
val wrongPublicKey = ECPublicKey.freshPublicKey
|
||||
val isValid : Boolean = wrongPublicKey.verify(Sha256Hash.ZERO_HASH.getBytes.toSeq,signature)
|
||||
val isValid : Boolean = wrongPublicKey.verify(hash,signature)
|
||||
isValid must be (false)
|
||||
}
|
||||
|
||||
|
@ -46,9 +48,11 @@ class ECPublicKeyTest extends FlatSpec with MustMatchers {
|
|||
|
||||
it must "verify a piece of data was signed with a bitcoins private key inside of bitcoinj" in {
|
||||
val bitcoinsPrivKey = ECPrivateKey.freshPrivateKey
|
||||
val bitcoinsSignature = bitcoinsPrivKey.sign(Sha256Hash.ZERO_HASH.getBytes)
|
||||
val hash = DoubleSha256Digest(Sha256Hash.ZERO_HASH.getBytes)
|
||||
val bitcoinsSignature = bitcoinsPrivKey.sign(hash)
|
||||
val bitcoinjPublicKey = org.bitcoinj.core.ECKey.fromPublicOnly(bitcoinsPrivKey.publicKey.bytes.toArray)
|
||||
bitcoinjPublicKey.verify(Sha256Hash.ZERO_HASH.getBytes, bitcoinsSignature.bytes.toArray) must be (true)
|
||||
bitcoinjPublicKey.verify(Sha256Hash.ZERO_HASH.getBytes,
|
||||
bitcoinsSignature.bytes.toArray) must be (true)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ class TransactionSignatureCreatorSpec extends Properties("TransactionSignatureCr
|
|||
val result = ScriptInterpreter.run(program)
|
||||
result == ScriptOk
|
||||
}
|
||||
|
||||
property("generate valid signatures for a multisignature transaction") =
|
||||
Prop.forAllNoShrink(TransactionGenerators.signedMultiSigTransaction) {
|
||||
case (txSignatureComponent: TransactionSignatureComponent, _) =>
|
||||
|
|
|
@ -55,17 +55,20 @@ class TransactionSignatureCreatorTest extends FlatSpec with MustMatchers with Bi
|
|||
val scriptPubKey = P2PKScriptPubKey(publicKey)
|
||||
val (creditingTx,outputIndex) = TransactionTestUtil.buildCreditingTransaction(scriptPubKey)
|
||||
val scriptSig = P2PKScriptSignature(EmptyDigitalSignature)
|
||||
val (spendingTx,inputIndex) = TransactionTestUtil.buildSpendingTransaction(creditingTx,scriptSig,outputIndex)
|
||||
val (spendingTx,inputIndex) = TransactionTestUtil.buildSpendingTransaction(creditingTx,
|
||||
scriptSig,outputIndex)
|
||||
val txSignatureComponent = TransactionSignatureComponent(spendingTx,inputIndex,scriptPubKey,
|
||||
Policy.standardScriptVerifyFlags)
|
||||
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent,privateKey,HashType.sigHashAll)
|
||||
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent,
|
||||
privateKey, HashType.sigHashAll)
|
||||
|
||||
//add the signature to the scriptSig instead of having an empty scriptSig
|
||||
val signedScriptSig = P2PKScriptSignature(txSignature)
|
||||
val (signedTx,_) = TransactionTestUtil.buildSpendingTransaction(creditingTx,signedScriptSig,outputIndex)
|
||||
val (signedTx,_) = TransactionTestUtil.buildSpendingTransaction(creditingTx,signedScriptSig,
|
||||
outputIndex)
|
||||
|
||||
//run it through the interpreter
|
||||
val program = ScriptProgram(signedTx,scriptPubKey,inputIndex, Policy.standardScriptVerifyFlags)
|
||||
val program = ScriptProgram(signedTx, scriptPubKey, inputIndex, Policy.standardScriptVerifyFlags)
|
||||
|
||||
val result = ScriptInterpreter.run(program)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue