Changeset View
Changeset View
Standalone View
Standalone View
src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java
Show All 30 Lines | |||||
* <p>You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1</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 | * <p>To build secp256k1 for use with bitcoinj, run | ||||
* `./configure --enable-jni --enable-experimental --enable-module-ecdh` | * `./configure --enable-jni --enable-experimental --enable-module-ecdh` | ||||
* and `make` then copy `.libs/libsecp256k1.so` to your system library path | * 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 | * or point the JVM to the folder containing it with -Djava.library.path | ||||
* </p> | * </p> | ||||
*/ | */ | ||||
public class NativeSecp256k1 { | public class NativeSecp256k1 implements AutoCloseable { | ||||
private static final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); | private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); | ||||
private static final Lock r = rwl.readLock(); | private final Lock r = rwl.readLock(); | ||||
private static final Lock w = rwl.writeLock(); | private final Lock w = rwl.writeLock(); | ||||
private static ThreadLocal<ByteBuffer> nativeByteBuffer = new ThreadLocal<ByteBuffer>(); | private static ThreadLocal<ByteBuffer> nativeByteBuffer = new ThreadLocal<ByteBuffer>(); | ||||
private long context = -1; | |||||
private static final boolean enabled; | |||||
static { | |||||
boolean isEnabled = false; | |||||
deadalnix: This temporary is not useful either. | |||||
try { | |||||
System.loadLibrary("secp256k1_jni"); | |||||
isEnabled = true; | |||||
} catch (UnsatisfiedLinkError e) { | |||||
System.out.println("UnsatisfiedLinkError: " + e.toString()); | |||||
} finally { | |||||
enabled = isEnabled; | |||||
} | |||||
} | |||||
public static boolean isEnabled() { | |||||
return enabled; | |||||
} | |||||
@Override | |||||
public void close() throws Exception { | |||||
deadalnixUnsubmitted Not Done Inline ActionsAutoClosable do not expect close to be idempotent, so you shouldn't check if the state is valid. In addition, it doesn't do what you think it does due to lack of proper synchronization. deadalnix: AutoClosable do not expect close to be idempotent, so you shouldn't check if the state is valid. | |||||
if(isValid()) { | |||||
cleanup(); | |||||
} | |||||
} | |||||
public NativeSecp256k1() throws IllegalStateException { | |||||
checkState(enabled); | |||||
long initContext = secp256k1_init_context(); | |||||
checkState(initContext != 0); | |||||
context = initContext; | |||||
deadalnixUnsubmitted Not Done Inline ActionsNo, you can't resurrect the object even if you catch because this is the constructor. So the temporary is not useful. deadalnix: No, you can't resurrect the object even if you catch because this is the constructor. So the… | |||||
} | |||||
private long getContext() throws IllegalStateException { | |||||
checkState(isValid()); | |||||
return context; | |||||
} | |||||
public boolean isValid() { | |||||
return context != -1; | |||||
} | |||||
/** | /** | ||||
* Verifies the given secp256k1 signature in native code. | * Verifies the given secp256k1 signature in native code. | ||||
* Calling when enabled == false is undefined (probably library not loaded) | * Calling when enabled == false is undefined (probably library not loaded) | ||||
* | * | ||||
* @param data The data which was signed, must be exactly 32 bytes | * @param data The data which was signed, must be exactly 32 bytes | ||||
* @param signature The signature | * @param signature The signature | ||||
* @param pub The public key which did the signing | * @param pub The public key which did the signing | ||||
*/ | */ | ||||
public static boolean verify(byte[] data, byte[] signature, byte[] pub) { | public boolean verify(byte[] data, byte[] signature, byte[] pub) { | ||||
checkArgument(data.length == 32 && signature.length <= 520 && pub.length <= 520); | checkArgument(data.length == 32 && signature.length <= 520 && pub.length <= 520); | ||||
ByteBuffer byteBuff = nativeByteBuffer.get(); | ByteBuffer byteBuff = nativeByteBuffer.get(); | ||||
if (byteBuff == null || byteBuff.capacity() < 520) { | if (byteBuff == null || byteBuff.capacity() < 520) { | ||||
byteBuff = ByteBuffer.allocateDirect(520); | byteBuff = ByteBuffer.allocateDirect(520); | ||||
byteBuff.order(ByteOrder.nativeOrder()); | byteBuff.order(ByteOrder.nativeOrder()); | ||||
nativeByteBuffer.set(byteBuff); | nativeByteBuffer.set(byteBuff); | ||||
} | } | ||||
byteBuff.rewind(); | byteBuff.rewind(); | ||||
byteBuff.put(data); | byteBuff.put(data); | ||||
byteBuff.put(signature); | byteBuff.put(signature); | ||||
byteBuff.put(pub); | byteBuff.put(pub); | ||||
r.lock(); | r.lock(); | ||||
try { | try { | ||||
return secp256k1_ecdsa_verify(byteBuff, Secp256k1Context.getContext(), signature.length, pub.length) == 1; | return secp256k1_ecdsa_verify(byteBuff, getContext(), signature.length, pub.length) == 1; | ||||
} finally { | } finally { | ||||
r.unlock(); | r.unlock(); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* libsecp256k1 Create an ECDSA signature. | * libsecp256k1 Create an ECDSA signature. | ||||
* | * | ||||
* @param data Message hash, 32 bytes | * @param data Message hash, 32 bytes | ||||
* @param key Secret key, 32 bytes | * @param key Secret key, 32 bytes | ||||
* | * | ||||
* Return values | * Return values | ||||
* @param sig byte array of signature | * @param sig byte array of signature | ||||
*/ | */ | ||||
public static byte[] sign(byte[] data, byte[] sec) throws AssertFailException{ | public byte[] sign(byte[] data, byte[] sec) throws AssertFailException{ | ||||
checkArgument(data.length == 32 && sec.length <= 32); | checkArgument(data.length == 32 && sec.length <= 32); | ||||
ByteBuffer byteBuff = nativeByteBuffer.get(); | ByteBuffer byteBuff = nativeByteBuffer.get(); | ||||
if (byteBuff == null || byteBuff.capacity() < 32 + 32) { | if (byteBuff == null || byteBuff.capacity() < 32 + 32) { | ||||
byteBuff = ByteBuffer.allocateDirect(32 + 32); | byteBuff = ByteBuffer.allocateDirect(32 + 32); | ||||
byteBuff.order(ByteOrder.nativeOrder()); | byteBuff.order(ByteOrder.nativeOrder()); | ||||
nativeByteBuffer.set(byteBuff); | nativeByteBuffer.set(byteBuff); | ||||
} | } | ||||
byteBuff.rewind(); | byteBuff.rewind(); | ||||
byteBuff.put(data); | byteBuff.put(data); | ||||
byteBuff.put(sec); | byteBuff.put(sec); | ||||
byte[][] retByteArray; | byte[][] retByteArray; | ||||
r.lock(); | r.lock(); | ||||
try { | try { | ||||
retByteArray = secp256k1_ecdsa_sign(byteBuff, Secp256k1Context.getContext()); | retByteArray = secp256k1_ecdsa_sign(byteBuff, getContext()); | ||||
} finally { | } finally { | ||||
r.unlock(); | r.unlock(); | ||||
} | } | ||||
byte[] sigArr = retByteArray[0]; | byte[] sigArr = retByteArray[0]; | ||||
int sigLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); | int sigLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); | ||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); | int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); | ||||
assertEquals(sigArr.length, sigLen, "Got bad signature length."); | assertEquals(sigArr.length, sigLen, "Got bad signature length."); | ||||
return retVal == 0 ? new byte[0] : sigArr; | return retVal == 0 ? new byte[0] : sigArr; | ||||
} | } | ||||
/** | /** | ||||
* libsecp256k1 Seckey Verify - returns 1 if valid, 0 if invalid | * libsecp256k1 Seckey Verify - returns 1 if valid, 0 if invalid | ||||
* | * | ||||
* @param seckey ECDSA Secret key, 32 bytes | * @param seckey ECDSA Secret key, 32 bytes | ||||
*/ | */ | ||||
public static boolean secKeyVerify(byte[] seckey) { | public boolean secKeyVerify(byte[] seckey) { | ||||
checkArgument(seckey.length == 32); | checkArgument(seckey.length == 32); | ||||
ByteBuffer byteBuff = nativeByteBuffer.get(); | ByteBuffer byteBuff = nativeByteBuffer.get(); | ||||
if (byteBuff == null || byteBuff.capacity() < seckey.length) { | if (byteBuff == null || byteBuff.capacity() < seckey.length) { | ||||
byteBuff = ByteBuffer.allocateDirect(seckey.length); | byteBuff = ByteBuffer.allocateDirect(seckey.length); | ||||
byteBuff.order(ByteOrder.nativeOrder()); | byteBuff.order(ByteOrder.nativeOrder()); | ||||
nativeByteBuffer.set(byteBuff); | nativeByteBuffer.set(byteBuff); | ||||
} | } | ||||
byteBuff.rewind(); | byteBuff.rewind(); | ||||
byteBuff.put(seckey); | byteBuff.put(seckey); | ||||
r.lock(); | r.lock(); | ||||
try { | try { | ||||
return secp256k1_ec_seckey_verify(byteBuff,Secp256k1Context.getContext()) == 1; | return secp256k1_ec_seckey_verify(byteBuff, getContext()) == 1; | ||||
} finally { | } finally { | ||||
r.unlock(); | r.unlock(); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* libsecp256k1 Compute Pubkey - computes public key from secret key | * libsecp256k1 Compute Pubkey - computes public key from secret key | ||||
* | * | ||||
* @param seckey ECDSA Secret key, 32 bytes | * @param seckey ECDSA Secret key, 32 bytes | ||||
* | * | ||||
* Return values | * Return values | ||||
* @param pubkey ECDSA Public key, 33 or 65 bytes | * @param pubkey ECDSA Public key, 33 or 65 bytes | ||||
*/ | */ | ||||
//TODO add a 'compressed' arg | //TODO add a 'compressed' arg | ||||
public static byte[] computePubkey(byte[] seckey) throws AssertFailException{ | public byte[] computePubkey(byte[] seckey) throws AssertFailException{ | ||||
checkArgument(seckey.length == 32); | checkArgument(seckey.length == 32); | ||||
ByteBuffer byteBuff = nativeByteBuffer.get(); | ByteBuffer byteBuff = nativeByteBuffer.get(); | ||||
if (byteBuff == null || byteBuff.capacity() < seckey.length) { | if (byteBuff == null || byteBuff.capacity() < seckey.length) { | ||||
byteBuff = ByteBuffer.allocateDirect(seckey.length); | byteBuff = ByteBuffer.allocateDirect(seckey.length); | ||||
byteBuff.order(ByteOrder.nativeOrder()); | byteBuff.order(ByteOrder.nativeOrder()); | ||||
nativeByteBuffer.set(byteBuff); | nativeByteBuffer.set(byteBuff); | ||||
} | } | ||||
byteBuff.rewind(); | byteBuff.rewind(); | ||||
byteBuff.put(seckey); | byteBuff.put(seckey); | ||||
byte[][] retByteArray; | byte[][] retByteArray; | ||||
r.lock(); | r.lock(); | ||||
try { | try { | ||||
retByteArray = secp256k1_ec_pubkey_create(byteBuff, Secp256k1Context.getContext()); | retByteArray = secp256k1_ec_pubkey_create(byteBuff, getContext()); | ||||
} finally { | } finally { | ||||
r.unlock(); | r.unlock(); | ||||
} | } | ||||
byte[] pubArr = retByteArray[0]; | byte[] pubArr = retByteArray[0]; | ||||
int pubLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); | int pubLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); | ||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); | int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); | ||||
assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); | assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); | ||||
return retVal == 0 ? new byte[0]: pubArr; | return retVal == 0 ? new byte[0]: pubArr; | ||||
} | } | ||||
/** | /** | ||||
* libsecp256k1 Cleanup - This destroys the secp256k1 context object | * libsecp256k1 Cleanup - This destroys the secp256k1 context object | ||||
* This should be called at the end of the program for proper cleanup of the context. | * This should be called at the end of the program for proper cleanup of the context. | ||||
*/ | */ | ||||
public static synchronized void cleanup() { | public synchronized void cleanup() { | ||||
w.lock(); | w.lock(); | ||||
try { | try { | ||||
secp256k1_destroy_context(Secp256k1Context.getContext()); | secp256k1_destroy_context(getContext()); | ||||
} finally { | } finally { | ||||
context = - 1; | |||||
w.unlock(); | w.unlock(); | ||||
} | } | ||||
} | } | ||||
public static long cloneContext() { | public long cloneContext() { | ||||
r.lock(); | r.lock(); | ||||
try { | try { | ||||
return secp256k1_ctx_clone(Secp256k1Context.getContext()); | return secp256k1_ctx_clone(getContext()); | ||||
} finally { r.unlock(); } | } finally { r.unlock(); } | ||||
} | } | ||||
/** | /** | ||||
* libsecp256k1 PrivKey Tweak-Mul - Tweak privkey by multiplying to it | * libsecp256k1 PrivKey Tweak-Mul - Tweak privkey by multiplying to it | ||||
* | * | ||||
* @param tweak some bytes to tweak with | * @param tweak some bytes to tweak with | ||||
* @param seckey 32-byte seckey | * @param seckey 32-byte seckey | ||||
*/ | */ | ||||
public static byte[] privKeyTweakMul(byte[] privkey, byte[] tweak) throws AssertFailException{ | public byte[] privKeyTweakMul(byte[] privkey, byte[] tweak) throws AssertFailException{ | ||||
checkArgument(privkey.length == 32); | checkArgument(privkey.length == 32); | ||||
ByteBuffer byteBuff = nativeByteBuffer.get(); | ByteBuffer byteBuff = nativeByteBuffer.get(); | ||||
if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) { | if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) { | ||||
byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length); | byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length); | ||||
byteBuff.order(ByteOrder.nativeOrder()); | byteBuff.order(ByteOrder.nativeOrder()); | ||||
nativeByteBuffer.set(byteBuff); | nativeByteBuffer.set(byteBuff); | ||||
} | } | ||||
byteBuff.rewind(); | byteBuff.rewind(); | ||||
byteBuff.put(privkey); | byteBuff.put(privkey); | ||||
byteBuff.put(tweak); | byteBuff.put(tweak); | ||||
byte[][] retByteArray; | byte[][] retByteArray; | ||||
r.lock(); | r.lock(); | ||||
try { | try { | ||||
retByteArray = secp256k1_privkey_tweak_mul(byteBuff,Secp256k1Context.getContext()); | retByteArray = secp256k1_privkey_tweak_mul(byteBuff, getContext()); | ||||
} finally { | } finally { | ||||
r.unlock(); | r.unlock(); | ||||
} | } | ||||
byte[] privArr = retByteArray[0]; | byte[] privArr = retByteArray[0]; | ||||
int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; | int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; | ||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); | int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); | ||||
assertEquals(privArr.length, privLen, "Got bad pubkey length."); | assertEquals(privArr.length, privLen, "Got bad pubkey length."); | ||||
assertEquals(retVal, 1, "Failed return value check."); | assertEquals(retVal, 1, "Failed return value check."); | ||||
return privArr; | return privArr; | ||||
} | } | ||||
/** | /** | ||||
* libsecp256k1 PrivKey Tweak-Add - Tweak privkey by adding to it | * libsecp256k1 PrivKey Tweak-Add - Tweak privkey by adding to it | ||||
* | * | ||||
* @param tweak some bytes to tweak with | * @param tweak some bytes to tweak with | ||||
* @param seckey 32-byte seckey | * @param seckey 32-byte seckey | ||||
*/ | */ | ||||
public static byte[] privKeyTweakAdd(byte[] privkey, byte[] tweak) throws AssertFailException{ | public byte[] privKeyTweakAdd(byte[] privkey, byte[] tweak) throws AssertFailException{ | ||||
checkArgument(privkey.length == 32); | checkArgument(privkey.length == 32); | ||||
ByteBuffer byteBuff = nativeByteBuffer.get(); | ByteBuffer byteBuff = nativeByteBuffer.get(); | ||||
if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) { | if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) { | ||||
byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length); | byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length); | ||||
byteBuff.order(ByteOrder.nativeOrder()); | byteBuff.order(ByteOrder.nativeOrder()); | ||||
nativeByteBuffer.set(byteBuff); | nativeByteBuffer.set(byteBuff); | ||||
} | } | ||||
byteBuff.rewind(); | byteBuff.rewind(); | ||||
byteBuff.put(privkey); | byteBuff.put(privkey); | ||||
byteBuff.put(tweak); | byteBuff.put(tweak); | ||||
byte[][] retByteArray; | byte[][] retByteArray; | ||||
r.lock(); | r.lock(); | ||||
try { | try { | ||||
retByteArray = secp256k1_privkey_tweak_add(byteBuff,Secp256k1Context.getContext()); | retByteArray = secp256k1_privkey_tweak_add(byteBuff, getContext()); | ||||
} finally { | } finally { | ||||
r.unlock(); | r.unlock(); | ||||
} | } | ||||
byte[] privArr = retByteArray[0]; | byte[] privArr = retByteArray[0]; | ||||
int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; | int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; | ||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); | int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); | ||||
assertEquals(privArr.length, privLen, "Got bad pubkey length."); | assertEquals(privArr.length, privLen, "Got bad pubkey length."); | ||||
assertEquals(retVal, 1, "Failed return value check."); | assertEquals(retVal, 1, "Failed return value check."); | ||||
return privArr; | return privArr; | ||||
} | } | ||||
/** | /** | ||||
* libsecp256k1 PubKey Tweak-Add - Tweak pubkey by adding to it | * libsecp256k1 PubKey Tweak-Add - Tweak pubkey by adding to it | ||||
* | * | ||||
* @param tweak some bytes to tweak with | * @param tweak some bytes to tweak with | ||||
* @param pubkey 32-byte seckey | * @param pubkey 32-byte seckey | ||||
*/ | */ | ||||
public static byte[] pubKeyTweakAdd(byte[] pubkey, byte[] tweak) throws AssertFailException{ | public byte[] pubKeyTweakAdd(byte[] pubkey, byte[] tweak) throws AssertFailException{ | ||||
checkArgument(pubkey.length == 33 || pubkey.length == 65); | checkArgument(pubkey.length == 33 || pubkey.length == 65); | ||||
ByteBuffer byteBuff = nativeByteBuffer.get(); | ByteBuffer byteBuff = nativeByteBuffer.get(); | ||||
if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) { | if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) { | ||||
byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length); | byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length); | ||||
byteBuff.order(ByteOrder.nativeOrder()); | byteBuff.order(ByteOrder.nativeOrder()); | ||||
nativeByteBuffer.set(byteBuff); | nativeByteBuffer.set(byteBuff); | ||||
} | } | ||||
byteBuff.rewind(); | byteBuff.rewind(); | ||||
byteBuff.put(pubkey); | byteBuff.put(pubkey); | ||||
byteBuff.put(tweak); | byteBuff.put(tweak); | ||||
byte[][] retByteArray; | byte[][] retByteArray; | ||||
r.lock(); | r.lock(); | ||||
try { | try { | ||||
retByteArray = secp256k1_pubkey_tweak_add(byteBuff,Secp256k1Context.getContext(), pubkey.length); | retByteArray = secp256k1_pubkey_tweak_add(byteBuff, getContext(), pubkey.length); | ||||
} finally { | } finally { | ||||
r.unlock(); | r.unlock(); | ||||
} | } | ||||
byte[] pubArr = retByteArray[0]; | byte[] pubArr = retByteArray[0]; | ||||
int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; | int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; | ||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); | int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); | ||||
assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); | assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); | ||||
assertEquals(retVal, 1, "Failed return value check."); | assertEquals(retVal, 1, "Failed return value check."); | ||||
return pubArr; | return pubArr; | ||||
} | } | ||||
/** | /** | ||||
* libsecp256k1 PubKey Tweak-Mul - Tweak pubkey by multiplying to it | * libsecp256k1 PubKey Tweak-Mul - Tweak pubkey by multiplying to it | ||||
* | * | ||||
* @param tweak some bytes to tweak with | * @param tweak some bytes to tweak with | ||||
* @param pubkey 32-byte seckey | * @param pubkey 32-byte seckey | ||||
*/ | */ | ||||
public static byte[] pubKeyTweakMul(byte[] pubkey, byte[] tweak) throws AssertFailException{ | public byte[] pubKeyTweakMul(byte[] pubkey, byte[] tweak) throws AssertFailException{ | ||||
checkArgument(pubkey.length == 33 || pubkey.length == 65); | checkArgument(pubkey.length == 33 || pubkey.length == 65); | ||||
ByteBuffer byteBuff = nativeByteBuffer.get(); | ByteBuffer byteBuff = nativeByteBuffer.get(); | ||||
if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) { | if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) { | ||||
byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length); | byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length); | ||||
byteBuff.order(ByteOrder.nativeOrder()); | byteBuff.order(ByteOrder.nativeOrder()); | ||||
nativeByteBuffer.set(byteBuff); | nativeByteBuffer.set(byteBuff); | ||||
} | } | ||||
byteBuff.rewind(); | byteBuff.rewind(); | ||||
byteBuff.put(pubkey); | byteBuff.put(pubkey); | ||||
byteBuff.put(tweak); | byteBuff.put(tweak); | ||||
byte[][] retByteArray; | byte[][] retByteArray; | ||||
r.lock(); | r.lock(); | ||||
try { | try { | ||||
retByteArray = secp256k1_pubkey_tweak_mul(byteBuff,Secp256k1Context.getContext(), pubkey.length); | retByteArray = secp256k1_pubkey_tweak_mul(byteBuff, getContext(), pubkey.length); | ||||
} finally { | } finally { | ||||
r.unlock(); | r.unlock(); | ||||
} | } | ||||
byte[] pubArr = retByteArray[0]; | byte[] pubArr = retByteArray[0]; | ||||
int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; | int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; | ||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); | int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); | ||||
assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); | assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); | ||||
assertEquals(retVal, 1, "Failed return value check."); | assertEquals(retVal, 1, "Failed return value check."); | ||||
return pubArr; | return pubArr; | ||||
} | } | ||||
/** | /** | ||||
* libsecp256k1 create ECDH secret - constant time ECDH calculation | * libsecp256k1 create ECDH secret - constant time ECDH calculation | ||||
* | * | ||||
* @param seckey byte array of secret key used in exponentiaion | * @param seckey byte array of secret key used in exponentiaion | ||||
* @param pubkey byte array of public key used in exponentiaion | * @param pubkey byte array of public key used in exponentiaion | ||||
*/ | */ | ||||
public static byte[] createECDHSecret(byte[] seckey, byte[] pubkey) throws AssertFailException{ | public byte[] createECDHSecret(byte[] seckey, byte[] pubkey) throws AssertFailException{ | ||||
checkArgument(seckey.length <= 32 && pubkey.length <= 65); | checkArgument(seckey.length <= 32 && pubkey.length <= 65); | ||||
ByteBuffer byteBuff = nativeByteBuffer.get(); | ByteBuffer byteBuff = nativeByteBuffer.get(); | ||||
if (byteBuff == null || byteBuff.capacity() < 32 + pubkey.length) { | if (byteBuff == null || byteBuff.capacity() < 32 + pubkey.length) { | ||||
byteBuff = ByteBuffer.allocateDirect(32 + pubkey.length); | byteBuff = ByteBuffer.allocateDirect(32 + pubkey.length); | ||||
byteBuff.order(ByteOrder.nativeOrder()); | byteBuff.order(ByteOrder.nativeOrder()); | ||||
nativeByteBuffer.set(byteBuff); | nativeByteBuffer.set(byteBuff); | ||||
} | } | ||||
byteBuff.rewind(); | byteBuff.rewind(); | ||||
byteBuff.put(seckey); | byteBuff.put(seckey); | ||||
byteBuff.put(pubkey); | byteBuff.put(pubkey); | ||||
byte[][] retByteArray; | byte[][] retByteArray; | ||||
r.lock(); | r.lock(); | ||||
try { | try { | ||||
retByteArray = secp256k1_ecdh(byteBuff, Secp256k1Context.getContext(), pubkey.length); | retByteArray = secp256k1_ecdh(byteBuff, getContext(), pubkey.length); | ||||
} finally { | } finally { | ||||
r.unlock(); | r.unlock(); | ||||
} | } | ||||
byte[] resArr = retByteArray[0]; | byte[] resArr = retByteArray[0]; | ||||
int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); | int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); | ||||
assertEquals(resArr.length, 32, "Got bad result length."); | assertEquals(resArr.length, 32, "Got bad result length."); | ||||
assertEquals(retVal, 1, "Failed return value check."); | assertEquals(retVal, 1, "Failed return value check."); | ||||
return resArr; | return resArr; | ||||
} | } | ||||
/** | /** | ||||
* libsecp256k1 randomize - updates the context randomization | * libsecp256k1 randomize - updates the context randomization | ||||
* | * | ||||
* @param seed 32-byte random seed | * @param seed 32-byte random seed | ||||
*/ | */ | ||||
public static synchronized boolean randomize(byte[] seed) throws AssertFailException{ | public synchronized boolean randomize(byte[] seed) throws AssertFailException{ | ||||
checkArgument(seed.length == 32 || seed == null); | checkArgument(seed.length == 32 || seed == null); | ||||
ByteBuffer byteBuff = nativeByteBuffer.get(); | ByteBuffer byteBuff = nativeByteBuffer.get(); | ||||
if (byteBuff == null || byteBuff.capacity() < seed.length) { | if (byteBuff == null || byteBuff.capacity() < seed.length) { | ||||
byteBuff = ByteBuffer.allocateDirect(seed.length); | byteBuff = ByteBuffer.allocateDirect(seed.length); | ||||
byteBuff.order(ByteOrder.nativeOrder()); | byteBuff.order(ByteOrder.nativeOrder()); | ||||
nativeByteBuffer.set(byteBuff); | nativeByteBuffer.set(byteBuff); | ||||
} | } | ||||
byteBuff.rewind(); | byteBuff.rewind(); | ||||
byteBuff.put(seed); | byteBuff.put(seed); | ||||
w.lock(); | w.lock(); | ||||
try { | try { | ||||
return secp256k1_context_randomize(byteBuff, Secp256k1Context.getContext()) == 1; | return secp256k1_context_randomize(byteBuff, getContext()) == 1; | ||||
} finally { | } finally { | ||||
w.unlock(); | w.unlock(); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* libsecp256k1 Create a Schnorr signature. | * libsecp256k1 Create a Schnorr signature. | ||||
* | * | ||||
* @param data Message hash, 32 bytes | * @param data Message hash, 32 bytes | ||||
* @param seckey Secret key, 32 bytes | * @param seckey Secret key, 32 bytes | ||||
* @return sig byte array of signature | * @return sig byte array of signature | ||||
*/ | */ | ||||
public static byte[] schnorrSign(byte[] data, byte[] seckey) throws AssertFailException { | public byte[] schnorrSign(byte[] data, byte[] seckey) throws AssertFailException { | ||||
checkArgument(data.length == 32 && seckey.length <= 32); | checkArgument(data.length == 32 && seckey.length <= 32); | ||||
ByteBuffer byteBuff = nativeByteBuffer.get(); | ByteBuffer byteBuff = nativeByteBuffer.get(); | ||||
if (byteBuff == null || byteBuff.capacity() < data.length + seckey.length) { | if (byteBuff == null || byteBuff.capacity() < data.length + seckey.length) { | ||||
byteBuff = ByteBuffer.allocateDirect(32 + 32); | byteBuff = ByteBuffer.allocateDirect(32 + 32); | ||||
byteBuff.order(ByteOrder.nativeOrder()); | byteBuff.order(ByteOrder.nativeOrder()); | ||||
nativeByteBuffer.set(byteBuff); | nativeByteBuffer.set(byteBuff); | ||||
} | } | ||||
byteBuff.rewind(); | byteBuff.rewind(); | ||||
byteBuff.put(data); | byteBuff.put(data); | ||||
byteBuff.put(seckey); | byteBuff.put(seckey); | ||||
byte[][] retByteArray; | byte[][] retByteArray; | ||||
r.lock(); | r.lock(); | ||||
try { | try { | ||||
retByteArray = secp256k1_schnorr_sign(byteBuff, Secp256k1Context.getContext()); | retByteArray = secp256k1_schnorr_sign(byteBuff, getContext()); | ||||
} finally { | } finally { | ||||
r.unlock(); | r.unlock(); | ||||
} | } | ||||
byte[] sigArr = retByteArray[0]; | byte[] sigArr = retByteArray[0]; | ||||
int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); | int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); | ||||
assertEquals(sigArr.length, 64, "Got bad signature length."); | assertEquals(sigArr.length, 64, "Got bad signature length."); | ||||
return retVal == 0 ? new byte[0] : sigArr; | return retVal == 0 ? new byte[0] : sigArr; | ||||
} | } | ||||
/** | /** | ||||
* Verifies the given Schnorr signature in native code. | * Verifies the given Schnorr signature in native code. | ||||
* Calling when enabled == false is undefined (probably library not loaded) | * Calling when enabled == false is undefined (probably library not loaded) | ||||
* | * | ||||
* @param data The data which was signed, must be exactly 32 bytes | * @param data The data which was signed, must be exactly 32 bytes | ||||
* @param signature The signature is exactly 64 bytes | * @param signature The signature is exactly 64 bytes | ||||
* @param pub The public key which did the signing which is the same ECDSA | * @param pub The public key which did the signing which is the same ECDSA | ||||
*/ | */ | ||||
public static boolean schnorrVerify(byte[] data, byte[] signature, byte[] pub) { | public boolean schnorrVerify(byte[] data, byte[] signature, byte[] pub) { | ||||
checkArgument(data.length == 32 && signature.length == 64 && (pub.length == 33 || pub.length == 65)); | checkArgument(data.length == 32 && signature.length == 64 && (pub.length == 33 || pub.length == 65)); | ||||
ByteBuffer byteBuff = nativeByteBuffer.get(); | ByteBuffer byteBuff = nativeByteBuffer.get(); | ||||
if (byteBuff == null || byteBuff.capacity() < 32 + 64 + pub.length) { | if (byteBuff == null || byteBuff.capacity() < 32 + 64 + pub.length) { | ||||
byteBuff = ByteBuffer.allocateDirect(32 + 64 + pub.length); | byteBuff = ByteBuffer.allocateDirect(32 + 64 + pub.length); | ||||
byteBuff.order(ByteOrder.nativeOrder()); | byteBuff.order(ByteOrder.nativeOrder()); | ||||
nativeByteBuffer.set(byteBuff); | nativeByteBuffer.set(byteBuff); | ||||
} | } | ||||
byteBuff.rewind(); | byteBuff.rewind(); | ||||
byteBuff.put(data); | byteBuff.put(data); | ||||
byteBuff.put(signature); | byteBuff.put(signature); | ||||
byteBuff.put(pub); | byteBuff.put(pub); | ||||
r.lock(); | r.lock(); | ||||
try { | try { | ||||
return secp256k1_schnorr_verify(byteBuff, Secp256k1Context.getContext(), pub.length) == 1; | return secp256k1_schnorr_verify(byteBuff, getContext(), pub.length) == 1; | ||||
} finally { | } finally { | ||||
r.unlock(); | r.unlock(); | ||||
} | } | ||||
} | } | ||||
private static native long secp256k1_ctx_clone(long context); | private static native long secp256k1_ctx_clone(long context); | ||||
private static native int secp256k1_context_randomize(ByteBuffer byteBuff, 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_add(ByteBuffer byteBuff, long context); | ||||
private static native byte[][] secp256k1_privkey_tweak_mul(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); | private static native byte[][] secp256k1_pubkey_tweak_add(ByteBuffer byteBuff, long context, int pubLen); | ||||
private static native byte[][] secp256k1_pubkey_tweak_mul(ByteBuffer byteBuff, long context, int pubLen); | private static native byte[][] secp256k1_pubkey_tweak_mul(ByteBuffer byteBuff, long context, int pubLen); | ||||
private static native void secp256k1_destroy_context(long context); | private static native void secp256k1_destroy_context(long context); | ||||
private static native long secp256k1_init_context(); | |||||
private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff, long context, int sigLen, int pubLen); | 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 byte[][] secp256k1_ecdsa_sign(ByteBuffer byteBuff, long context); | ||||
private static native int secp256k1_ec_seckey_verify(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); | private static native byte[][] secp256k1_ec_pubkey_create(ByteBuffer byteBuff, long context); | ||||
Show All 9 Lines |
This temporary is not useful either.