Compare commits
1 Commits
master
...
snapshot/s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61df0e8a9a |
@@ -44,6 +44,11 @@ void JNI_ThrowByName(JNIEnv *penv, const char *name, const char *msg)
|
|||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void secp256k1_noop_illegal_callback_fn(const char* str, void* data) {
|
||||||
|
(void)str;
|
||||||
|
(void)data;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Class: fr_acinq_bitcoin_Secp256k1Bindings
|
* Class: fr_acinq_bitcoin_Secp256k1Bindings
|
||||||
* Method: secp256k1_context_create
|
* Method: secp256k1_context_create
|
||||||
@@ -51,7 +56,9 @@ void JNI_ThrowByName(JNIEnv *penv, const char *name, const char *msg)
|
|||||||
*/
|
*/
|
||||||
JNIEXPORT jlong JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1context_1create(JNIEnv *penv, jclass clazz, jint flags)
|
JNIEXPORT jlong JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1context_1create(JNIEnv *penv, jclass clazz, jint flags)
|
||||||
{
|
{
|
||||||
return (jlong)secp256k1_context_create(flags);
|
jlong ctx = (jlong)secp256k1_context_create(flags);
|
||||||
|
secp256k1_context_set_illegal_callback(ctx, &secp256k1_noop_illegal_callback_fn, NULL);
|
||||||
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -117,7 +117,6 @@ public object NativeSecp256k1 : Secp256k1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun musigPartialSign(secnonce: ByteArray, privkey: ByteArray, keyaggCache: ByteArray, session: ByteArray): ByteArray {
|
override fun musigPartialSign(secnonce: ByteArray, privkey: ByteArray, keyaggCache: ByteArray, session: ByteArray): ByteArray {
|
||||||
require(musigNonceValidate(secnonce, pubkeyCreate(privkey)))
|
|
||||||
return Secp256k1CFunctions.secp256k1_musig_partial_sign(Secp256k1Context.getContext(), secnonce, privkey, keyaggCache, session)
|
return Secp256k1CFunctions.secp256k1_musig_partial_sign(Secp256k1Context.getContext(), secnonce, privkey, keyaggCache, session)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -217,26 +217,6 @@ public interface Secp256k1 {
|
|||||||
*/
|
*/
|
||||||
public fun musigNonceProcess(aggnonce: ByteArray, msg32: ByteArray, keyaggCache: ByteArray): ByteArray
|
public fun musigNonceProcess(aggnonce: ByteArray, msg32: ByteArray, keyaggCache: ByteArray): ByteArray
|
||||||
|
|
||||||
/**
|
|
||||||
* Check that a secret nonce was generated with a public key that matches the private key used for signing.
|
|
||||||
* @param secretnonce secret nonce.
|
|
||||||
* @param pubkey public key for the private key that will be used, with the secret nonce, to generate a partial signature.
|
|
||||||
* @return false if the secret nonce does not match the public key.
|
|
||||||
*/
|
|
||||||
public fun musigNonceValidate(secretnonce: ByteArray, pubkey: ByteArray): Boolean {
|
|
||||||
if (secretnonce.size != MUSIG2_SECRET_NONCE_SIZE) return false
|
|
||||||
if (pubkey.size != 33 && pubkey.size != 65) return false
|
|
||||||
val pk = Secp256k1.pubkeyParse(pubkey)
|
|
||||||
// this is a bit hackish but the secp256k1 library does not export methods to do this cleanly
|
|
||||||
val x = secretnonce.copyOfRange(68, 68 + 32)
|
|
||||||
x.reverse()
|
|
||||||
val y = secretnonce.copyOfRange(68 + 32, 68 + 32 + 32)
|
|
||||||
y.reverse()
|
|
||||||
val pkx = pk.copyOfRange(1, 1 + 32)
|
|
||||||
val pky = pk.copyOfRange(33, 33 + 32)
|
|
||||||
return x.contentEquals(pkx) && y.contentEquals(pky)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a partial signature.
|
* Create a partial signature.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -9,8 +9,11 @@ import secp256k1.*
|
|||||||
public object Secp256k1Native : Secp256k1 {
|
public object Secp256k1Native : Secp256k1 {
|
||||||
|
|
||||||
private val ctx: CPointer<secp256k1_context> by lazy {
|
private val ctx: CPointer<secp256k1_context> by lazy {
|
||||||
secp256k1_context_create((SECP256K1_FLAGS_TYPE_CONTEXT or SECP256K1_FLAGS_BIT_CONTEXT_SIGN or SECP256K1_FLAGS_BIT_CONTEXT_VERIFY).toUInt())
|
val c = secp256k1_context_create((SECP256K1_FLAGS_TYPE_CONTEXT or SECP256K1_FLAGS_BIT_CONTEXT_SIGN or SECP256K1_FLAGS_BIT_CONTEXT_VERIFY).toUInt())
|
||||||
?: error("Could not create secp256k1 context")
|
?: error("Could not create secp256k1 context")
|
||||||
|
val callback = staticCFunction { _: CPointer<ByteVar>?, _: COpaquePointer? -> }
|
||||||
|
secp256k1_context_set_illegal_callback(c, callback, null)
|
||||||
|
c
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Int.requireSuccess(message: String): Int = if (this != 1) throw Secp256k1Exception(message) else this
|
private fun Int.requireSuccess(message: String): Int = if (this != 1) throw Secp256k1Exception(message) else this
|
||||||
@@ -81,7 +84,7 @@ public object Secp256k1Native : Secp256k1 {
|
|||||||
return serialized.readBytes(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
|
return serialized.readBytes(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun DeferScope.toNat(bytes: ByteArray): CPointer<UByteVar> {
|
private fun DeferScope.toNat(bytes: ByteArray): CPointer<UByteVar> {
|
||||||
val ubytes = bytes.asUByteArray()
|
val ubytes = bytes.asUByteArray()
|
||||||
val pinned = ubytes.pin()
|
val pinned = ubytes.pin()
|
||||||
this.defer { pinned.unpin() }
|
this.defer { pinned.unpin() }
|
||||||
@@ -112,7 +115,7 @@ public object Secp256k1Native : Secp256k1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override fun signatureNormalize(sig: ByteArray): Pair<ByteArray, Boolean> {
|
public override fun signatureNormalize(sig: ByteArray): Pair<ByteArray, Boolean> {
|
||||||
require(sig.size >= 64) { "invalid signature ${Hex.encode(sig)}" }
|
require(sig.size >= 64){ "invalid signature ${Hex.encode(sig)}" }
|
||||||
memScoped {
|
memScoped {
|
||||||
val nSig = allocSignature(sig)
|
val nSig = allocSignature(sig)
|
||||||
val isHighS = secp256k1_ecdsa_signature_normalize(ctx, nSig.ptr, nSig.ptr)
|
val isHighS = secp256k1_ecdsa_signature_normalize(ctx, nSig.ptr, nSig.ptr)
|
||||||
@@ -307,16 +310,7 @@ public object Secp256k1Native : Secp256k1 {
|
|||||||
memcpy(n.ptr, toNat(it), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
|
memcpy(n.ptr, toNat(it), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
|
||||||
n
|
n
|
||||||
}
|
}
|
||||||
secp256k1_musig_nonce_gen(
|
secp256k1_musig_nonce_gen(ctx, secnonce.ptr, pubnonce.ptr, toNat(sessionId32), privkey?.let { toNat(it) }, nPubkey.ptr, msg32?.let { toNat(it) },nKeyAggCache?.ptr, extraInput32?.let { toNat(it) }).requireSuccess("secp256k1_musig_nonce_gen() failed")
|
||||||
ctx,
|
|
||||||
secnonce.ptr,
|
|
||||||
pubnonce.ptr,
|
|
||||||
toNat(sessionId32),
|
|
||||||
privkey?.let { toNat(it) },
|
|
||||||
nPubkey.ptr,
|
|
||||||
msg32?.let { toNat(it) },
|
|
||||||
nKeyAggCache?.ptr,
|
|
||||||
extraInput32?.let { toNat(it) }).requireSuccess("secp256k1_musig_nonce_gen() failed")
|
|
||||||
val nPubnonce = allocArray<UByteVar>(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
|
val nPubnonce = allocArray<UByteVar>(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
|
||||||
secp256k1_musig_pubnonce_serialize(ctx, nPubnonce, pubnonce.ptr).requireSuccess("secp256k1_musig_pubnonce_serialize failed")
|
secp256k1_musig_pubnonce_serialize(ctx, nPubnonce, pubnonce.ptr).requireSuccess("secp256k1_musig_pubnonce_serialize failed")
|
||||||
secnonce.ptr.readBytes(Secp256k1.MUSIG2_SECRET_NONCE_SIZE) + nPubnonce.readBytes(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
|
secnonce.ptr.readBytes(Secp256k1.MUSIG2_SECRET_NONCE_SIZE) + nPubnonce.readBytes(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
|
||||||
@@ -348,7 +342,7 @@ public object Secp256k1Native : Secp256k1 {
|
|||||||
n
|
n
|
||||||
}
|
}
|
||||||
secp256k1_musig_pubkey_agg(ctx, combined.ptr, nKeyAggCache?.ptr, nPubkeys.toCValues(), pubkeys.size.convert()).requireSuccess("secp256k1_musig_nonce_agg() failed")
|
secp256k1_musig_pubkey_agg(ctx, combined.ptr, nKeyAggCache?.ptr, nPubkeys.toCValues(), pubkeys.size.convert()).requireSuccess("secp256k1_musig_nonce_agg() failed")
|
||||||
val agg = serializeXonlyPubkey(combined)
|
val agg = serializeXonlyPubkey(combined)
|
||||||
keyaggCache?.let { blob -> nKeyAggCache?.let { memcpy(toNat(blob), it.ptr, Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong()) } }
|
keyaggCache?.let { blob -> nKeyAggCache?.let { memcpy(toNat(blob), it.ptr, Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong()) } }
|
||||||
return agg
|
return agg
|
||||||
}
|
}
|
||||||
@@ -395,14 +389,13 @@ public object Secp256k1Native : Secp256k1 {
|
|||||||
memcpy(toNat(session), nSession.ptr, Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE.toULong())
|
memcpy(toNat(session), nSession.ptr, Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE.toULong())
|
||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun musigPartialSign(secnonce: ByteArray, privkey: ByteArray, keyaggCache: ByteArray, session: ByteArray): ByteArray {
|
override fun musigPartialSign(secnonce: ByteArray, privkey: ByteArray, keyaggCache: ByteArray, session: ByteArray): ByteArray {
|
||||||
require(secnonce.size == Secp256k1.MUSIG2_SECRET_NONCE_SIZE)
|
require(secnonce.size == Secp256k1.MUSIG2_SECRET_NONCE_SIZE)
|
||||||
require(privkey.size == 32)
|
require(privkey.size == 32)
|
||||||
require(keyaggCache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
require(keyaggCache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
||||||
require(session.size == Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE)
|
require(session.size == Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE)
|
||||||
require(musigNonceValidate(secnonce, pubkeyCreate(privkey)))
|
|
||||||
|
|
||||||
memScoped {
|
memScoped {
|
||||||
val nSecnonce = alloc<secp256k1_musig_secnonce>()
|
val nSecnonce = alloc<secp256k1_musig_secnonce>()
|
||||||
|
|||||||
@@ -5,14 +5,6 @@ import kotlin.test.*
|
|||||||
|
|
||||||
class Secp256k1Test {
|
class Secp256k1Test {
|
||||||
|
|
||||||
val random = Random.Default
|
|
||||||
|
|
||||||
fun randomBytes(length: Int): ByteArray {
|
|
||||||
val buffer = ByteArray(length)
|
|
||||||
random.nextBytes(buffer)
|
|
||||||
return buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun verifyValidPrivateKey() {
|
fun verifyValidPrivateKey() {
|
||||||
val priv = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase())
|
val priv = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase())
|
||||||
@@ -462,55 +454,40 @@ class Secp256k1Test {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMusig2SigningSession() {
|
fun testMusig2SigningSession() {
|
||||||
val privkeys = listOf(randomBytes(32), randomBytes(32))
|
val privkeys = listOf(
|
||||||
val sessionId = randomBytes(32)
|
"0101010101010101010101010101010101010101010101010101010101010101",
|
||||||
val msg32 = randomBytes(32)
|
"0202020202020202020202020202020202020202020202020202020202020202",
|
||||||
|
).map { Hex.decode(it) }.toTypedArray()
|
||||||
val pubkeys = privkeys.map { Secp256k1.pubkeyCreate(it) }
|
val pubkeys = privkeys.map { Secp256k1.pubkeyCreate(it) }
|
||||||
|
|
||||||
|
val sessionId = Hex.decode("0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F")
|
||||||
val nonces = pubkeys.map { Secp256k1.musigNonceGen(sessionId, null, it, null, null, null) }
|
val nonces = pubkeys.map { Secp256k1.musigNonceGen(sessionId, null, it, null, null, null) }
|
||||||
val testData = run {
|
|
||||||
val builder = StringBuilder()
|
|
||||||
builder.append("private keys\n")
|
|
||||||
privkeys.forEach { builder.append(Hex.encode(it)).append("\n") }
|
|
||||||
builder.append("sessionId ${Hex.encode(sessionId)}\n")
|
|
||||||
builder.append("msg32 ${Hex.encode(msg32)}\n")
|
|
||||||
builder.append("nonces\n")
|
|
||||||
nonces.forEach { builder.append(Hex.encode(it)).append("\n") }
|
|
||||||
builder.toString()
|
|
||||||
}
|
|
||||||
val secnonces = nonces.map { it.copyOfRange(0, 132) }
|
val secnonces = nonces.map { it.copyOfRange(0, 132) }
|
||||||
val pubnonces = nonces.map { it.copyOfRange(132, 132 + 66) }
|
val pubnonces = nonces.map { it.copyOfRange(132, 132 + 66) }
|
||||||
val aggnonce = Secp256k1.musigNonceAgg(pubnonces.toTypedArray())
|
val aggnonce = Secp256k1.musigNonceAgg(pubnonces.toTypedArray())
|
||||||
|
|
||||||
val keyaggCaches = (0 until 2).map { ByteArray(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE) }
|
val keyaggCaches = (0 until 2).map { ByteArray(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE) }
|
||||||
val aggpubkey = Secp256k1.musigPubkeyAgg(pubkeys.toTypedArray(), keyaggCaches[0])
|
val aggpubkey = Secp256k1.musigPubkeyAgg(pubkeys.toTypedArray(), keyaggCaches[0])
|
||||||
assertContentEquals(aggpubkey, Secp256k1.musigPubkeyAgg(pubkeys.toTypedArray(), keyaggCaches[1]), testData)
|
assertContentEquals(aggpubkey, Secp256k1.musigPubkeyAgg(pubkeys.toTypedArray(), keyaggCaches[1]))
|
||||||
assertContentEquals(keyaggCaches[0], keyaggCaches[1], testData)
|
assertContentEquals(keyaggCaches[0], keyaggCaches[1])
|
||||||
|
|
||||||
|
val msg32 = Hex.decode("0303030303030303030303030303030303030303030303030303030303030303")
|
||||||
val sessions = (0 until 2).map { Secp256k1.musigNonceProcess(aggnonce, msg32, keyaggCaches[it]) }
|
val sessions = (0 until 2).map { Secp256k1.musigNonceProcess(aggnonce, msg32, keyaggCaches[it]) }
|
||||||
val psigs = (0 until 2).map {
|
val psigs = (0 until 2).map {
|
||||||
val psig = Secp256k1.musigPartialSign(secnonces[it], privkeys[it], keyaggCaches[it], sessions[it])
|
val psig = Secp256k1.musigPartialSign(secnonces[it], privkeys[it], keyaggCaches[it], sessions[it])
|
||||||
assertEquals(1, Secp256k1.musigPartialSigVerify(psig, pubnonces[it], pubkeys[it], keyaggCaches[it], sessions[it]), testData)
|
assertEquals(1, Secp256k1.musigPartialSigVerify(psig, pubnonces[it], pubkeys[it], keyaggCaches[it], sessions[it]))
|
||||||
assertEquals(0, Secp256k1.musigPartialSigVerify(Random.nextBytes(32), pubnonces[it], pubkeys[it], keyaggCaches[it], sessions[it]), testData)
|
assertEquals(0, Secp256k1.musigPartialSigVerify(Random.nextBytes(32), pubnonces[it], pubkeys[it], keyaggCaches[it], sessions[it]))
|
||||||
psig
|
psig
|
||||||
}
|
}
|
||||||
|
|
||||||
// signing fails if the secret nonce does not match the private key's public key
|
|
||||||
assertFails(testData) {
|
|
||||||
Secp256k1.musigPartialSign(secnonces[1], privkeys[0], keyaggCaches[0], sessions[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
assertFails(testData) {
|
|
||||||
Secp256k1.musigPartialSign(secnonces[0], privkeys[1], keyaggCaches[1], sessions[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
val sig = Secp256k1.musigPartialSigAgg(sessions[0], psigs.toTypedArray())
|
val sig = Secp256k1.musigPartialSigAgg(sessions[0], psigs.toTypedArray())
|
||||||
assertContentEquals(sig, Secp256k1.musigPartialSigAgg(sessions[1], psigs.toTypedArray()), testData)
|
assertContentEquals(sig, Secp256k1.musigPartialSigAgg(sessions[1], psigs.toTypedArray()))
|
||||||
assertTrue(Secp256k1.verifySchnorr(sig, msg32, aggpubkey), testData)
|
assertTrue(Secp256k1.verifySchnorr(sig, msg32, aggpubkey))
|
||||||
|
|
||||||
val invalidSig1 = Secp256k1.musigPartialSigAgg(sessions[0], arrayOf(psigs[0], psigs[0]))
|
val invalidSig1 = Secp256k1.musigPartialSigAgg(sessions[0], arrayOf(psigs[0], psigs[0]))
|
||||||
assertFalse(Secp256k1.verifySchnorr(invalidSig1, msg32, aggpubkey), testData)
|
assertFalse(Secp256k1.verifySchnorr(invalidSig1, msg32, aggpubkey))
|
||||||
val invalidSig2 = Secp256k1.musigPartialSigAgg(sessions[0], arrayOf(Random.nextBytes(32), Random.nextBytes(32)))
|
val invalidSig2 = Secp256k1.musigPartialSigAgg(sessions[0], arrayOf(Random.nextBytes(32), Random.nextBytes(32)))
|
||||||
assertFalse(Secp256k1.verifySchnorr(invalidSig2, msg32, aggpubkey), testData)
|
assertFalse(Secp256k1.verifySchnorr(invalidSig2, msg32, aggpubkey))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -543,17 +520,41 @@ class Secp256k1Test {
|
|||||||
-1
|
-1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
assertFails {
|
||||||
|
val privkeys = listOf(
|
||||||
|
"0101010101010101010101010101010101010101010101010101010101010101",
|
||||||
|
"0202020202020202020202020202020202020202020202020202020202020202",
|
||||||
|
).map { Hex.decode(it) }.toTypedArray()
|
||||||
|
val pubkeys = privkeys.map { Secp256k1.pubkeyCreate(it) }
|
||||||
|
|
||||||
@Test
|
val sessionId = Hex.decode("0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F")
|
||||||
fun fuzzMusig2SigningSession() {
|
val nonces = pubkeys.map { Secp256k1.musigNonceGen(sessionId, null, it, null, null, null) }
|
||||||
repeat(1000) {
|
val secnonces = nonces.map { it.copyOfRange(0, 132) }
|
||||||
testMusig2SigningSession()
|
val pubnonces = nonces.map { it.copyOfRange(132, 132 + 66) }
|
||||||
|
val aggnonce = Secp256k1.musigNonceAgg(pubnonces.toTypedArray())
|
||||||
|
|
||||||
|
val keyaggCaches = (0 until 2).map { ByteArray(Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE) }
|
||||||
|
val aggpubkey = Secp256k1.musigPubkeyAgg(pubkeys.toTypedArray(), keyaggCaches[0])
|
||||||
|
assertContentEquals(aggpubkey, Secp256k1.musigPubkeyAgg(pubkeys.toTypedArray(), keyaggCaches[1]))
|
||||||
|
assertContentEquals(keyaggCaches[0], keyaggCaches[1])
|
||||||
|
val msg32 = Hex.decode("0303030303030303030303030303030303030303030303030303030303030303")
|
||||||
|
val sessions = (0 until 2).map { Secp256k1.musigNonceProcess(aggnonce, msg32, keyaggCaches[it]) }
|
||||||
|
|
||||||
|
// we sign with the wrong secret nonce. it should fail (i.e. trigger an exception) but not crash the JVM
|
||||||
|
Secp256k1.musigPartialSign(secnonces[1], privkeys[0], keyaggCaches[0], sessions[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun fuzzEcdsaSignVerify() {
|
fun fuzzEcdsaSignVerify() {
|
||||||
|
val random = Random.Default
|
||||||
|
|
||||||
|
fun randomBytes(length: Int): ByteArray {
|
||||||
|
val buffer = ByteArray(length)
|
||||||
|
random.nextBytes(buffer)
|
||||||
|
return buffer
|
||||||
|
}
|
||||||
|
|
||||||
repeat(200) {
|
repeat(200) {
|
||||||
val priv = randomBytes(32)
|
val priv = randomBytes(32)
|
||||||
assertTrue(Secp256k1.secKeyVerify(priv))
|
assertTrue(Secp256k1.secKeyVerify(priv))
|
||||||
|
|||||||
Reference in New Issue
Block a user