Explicit Signature & PubKey formats + bug fixes

This commit is contained in:
Salomon BRYS 2020-06-29 17:10:58 +02:00
parent b67cafed38
commit 4bdc836556
8 changed files with 217 additions and 187 deletions

View File

@ -4,7 +4,7 @@ plugins {
`maven-publish`
}
group = "fr.acinq.secp256k1"
version = "0.1.0-1.4-M2"
version = "0.2.0-1.4-M2"
repositories {
jcenter()

View File

@ -76,7 +76,7 @@ SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1ve
}
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jboolean compact, jlong ctx_l)
{
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
@ -91,10 +91,14 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e
int ret = secp256k1_ecdsa_sign(ctx, &sig, data, secKey, NULL, NULL);
unsigned char outputSer[72];
size_t outputLen = 72;
size_t outputLen = compact ? 64 : 72;
if( ret ) {
int ret2 = secp256k1_ecdsa_signature_serialize_der(ctx,outputSer, &outputLen, &sig ); (void)ret2;
if (compact) {
int ret2 = secp256k1_ecdsa_signature_serialize_compact(ctx, outputSer, &sig ); (void)ret2;
} else {
int ret2 = secp256k1_ecdsa_signature_serialize_der(ctx, outputSer, &outputLen, &sig ); (void)ret2;
}
}
intsarray[0] = outputLen;
@ -117,30 +121,44 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e
return retArray;
}
JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign_1compact
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1normalize
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jint siglen, jboolean compact, jlong ctx_l)
{
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
unsigned char* secKey = (unsigned char*) (data + 32);
unsigned char* sigdata = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
jobjectArray retArray;
jbyteArray sigArray, intsByteArray;
unsigned char intsarray[2];
unsigned char intsarray[3];
secp256k1_ecdsa_signature sig[72];
secp256k1_ecdsa_signature sig;
int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL);
int ret = 0;
if (siglen == 64) {
ret = secp256k1_ecdsa_signature_parse_compact(ctx, &sig, sigdata);
} else {
ret = secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigdata, siglen);
}
unsigned char outputSer[64];
size_t outputLen = 64;
int ret2 = 0;
if (ret) {
ret2 = secp256k1_ecdsa_signature_normalize(ctx, &sig, &sig);
}
unsigned char outputSer[72];
size_t outputLen = compact ? 64 : 72;
if( ret ) {
int ret2 = secp256k1_ecdsa_signature_serialize_compact(ctx,outputSer, sig ); (void)ret2;
if (compact) {
int ret3 = secp256k1_ecdsa_signature_serialize_compact(ctx, outputSer, &sig ); (void)ret3;
} else {
int ret3 = secp256k1_ecdsa_signature_serialize_der(ctx, outputSer, &outputLen, &sig ); (void)ret3;
}
}
intsarray[0] = outputLen;
intsarray[1] = ret;
intsarray[2] = ret2;
retArray = (*env)->NewObjectArray(env, 2,
(*env)->FindClass(env, "[B"),
@ -150,15 +168,15 @@ JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa
(*env)->SetByteArrayRegion(env, sigArray, 0, outputLen, (jbyte*)outputSer);
(*env)->SetObjectArrayElement(env, retArray, 0, sigArray);
intsByteArray = (*env)->NewByteArray(env, 2);
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
intsByteArray = (*env)->NewByteArray(env, 3);
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 3, (jbyte*)intsarray);
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
(void)classObject;
return retArray;
}
}
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
@ -172,7 +190,7 @@ SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1secke
}
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jboolean compressed, jlong ctx_l)
{
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
const unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
@ -186,10 +204,10 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e
int ret = secp256k1_ec_pubkey_create(ctx, &pubkey, secKey);
unsigned char outputSer[65];
size_t outputLen = 65;
size_t outputLen = compressed ? 33 : 65;
if( ret ) {
int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey, compressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED );(void)ret2;
}
intsarray[0] = outputLen;
@ -213,7 +231,7 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e
}
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1parse
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint inputlen)
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint inputlen, jboolean compressed)
{
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
const unsigned char* pubkeydata = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
@ -227,10 +245,10 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e
int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeydata, inputlen);
unsigned char outputSer[65];
size_t outputLen = 65;
size_t outputLen = compressed ? 33 : 65;
if( ret ) {
int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey, SECP256K1_EC_UNCOMPRESSED );(void)ret2;
int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey, compressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED );(void)ret2;
}
intsarray[0] = outputLen;
@ -412,7 +430,7 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1p
jbyteArray pubArray, intsByteArray;
unsigned char intsarray[2];
unsigned char outputSer[65];
size_t outputLen = 65;
size_t outputLen = publen;
secp256k1_pubkey pubkey;
int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen);
@ -422,7 +440,7 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1p
}
if( ret ) {
int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey, publen == 33 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);(void)ret2;
}
intsarray[0] = outputLen;
@ -456,7 +474,7 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1p
jbyteArray pubArray, intsByteArray;
unsigned char intsarray[2];
unsigned char outputSer[65];
size_t outputLen = 65;
size_t outputLen = publen;
secp256k1_pubkey pubkey;
int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen);
@ -466,7 +484,7 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1p
}
if( ret ) {
int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey, publen == 33 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);(void)ret2;
}
intsarray[0] = outputLen;
@ -501,7 +519,7 @@ JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1p
secp256k1_pubkey pubkey1, pubkey2;
const secp256k1_pubkey *pubkeys[2];
unsigned char outputSer[65];
size_t outputLen = 65;
size_t outputLen = publen1;
int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey1, pubdata1, publen1);
if (ret) {
@ -514,7 +532,7 @@ JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1p
ret = secp256k1_ec_pubkey_combine(ctx, &result, pubkeys, 2);
}
if (ret) {
ret = secp256k1_ec_pubkey_serialize(ctx, outputSer, &outputLen, &result, SECP256K1_EC_UNCOMPRESSED );
ret = secp256k1_ec_pubkey_serialize(ctx, outputSer, &outputLen, &result, publen1 == 33 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED );
}
intsarray[0] = outputLen;
intsarray[1] = ret;
@ -588,7 +606,7 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e
* Signature: (Ljava/nio/ByteBuffer;JI)[[B
*/
JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1recover
(JNIEnv *env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint recid)
(JNIEnv *env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint recid, jboolean compressed)
{
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
const unsigned char* sigdata = (*env)->GetDirectBufferAddress(env, byteBufferObject);
@ -596,7 +614,7 @@ JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa
secp256k1_ecdsa_recoverable_signature sig;
secp256k1_pubkey pub;
unsigned char outputSer[65];
size_t outputLen = 65;
size_t outputLen = compressed ? 33 : 65;
jobjectArray retArray;
jbyteArray pubArray, intsByteArray;
unsigned char intsarray[1];
@ -605,7 +623,7 @@ JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa
if (ret) {
ret = secp256k1_ecdsa_recover(ctx, &pub, &sig, msgdata);
if (ret) {
ret = secp256k1_ec_pubkey_serialize(ctx, outputSer, &outputLen, &pub, SECP256K1_EC_UNCOMPRESSED );
ret = secp256k1_ec_pubkey_serialize(ctx, outputSer, &outputLen, &pub, compressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED );
}
}

View File

@ -90,18 +90,18 @@ JNIEXPORT jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
/*
* Class: org_bitcoin_NativeSecp256k1
* Method: secp256k1_ecdsa_sign
* Signature: (Ljava/nio/ByteBuffer;J)[[B
* Signature: (Ljava/nio/ByteBuffer;ZJ)[[B
*/
JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign
(JNIEnv *, jclass, jobject, jlong);
(JNIEnv *, jclass, jobject, jboolean, jlong);
/*
* Class: org_bitcoin_NativeSecp256k1
* Method: secp256k1_ecdsa_sign_compact
* Signature: (Ljava/nio/ByteBuffer;J)[[B
* Method: secp256k1_ecdsa_normalize
* Signature: (Ljava/nio/ByteBuffer;I114:1ZJ)[[B
*/
JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign_1compact
(JNIEnv *, jclass, jobject, jlong);
JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1normalize
(JNIEnv *, jclass, jobject, jint, jboolean, jlong);
/*
* Class: org_bitcoin_NativeSecp256k1
@ -114,18 +114,18 @@ JNIEXPORT jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1v
/*
* Class: org_bitcoin_NativeSecp256k1
* Method: secp256k1_ec_pubkey_create
* Signature: (Ljava/nio/ByteBuffer;J)[[B
* Signature: (Ljava/nio/ByteBuffer;ZJ)[[B
*/
JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create
(JNIEnv *, jclass, jobject, jlong);
(JNIEnv *, jclass, jobject, jboolean, jlong);
/*
* Class: org_bitcoin_NativeSecp256k1
* Method: secp256k1_ec_pubkey_parse
* Signature: (Ljava/nio/ByteBuffer;JI)[[B
* Signature: (Ljava/nio/ByteBuffer;JIZ)[[B
*/
JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1parse
(JNIEnv *, jclass, jobject, jlong, jint);
(JNIEnv *, jclass, jobject, jlong, jint, jboolean);
/*
* Class: org_bitcoin_NativeSecp256k1
@ -146,10 +146,10 @@ JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh
/*
* Class: org_bitcoin_NativeSecp256k1
* Method: secp256k1_ecdsa_recover
* Signature: (Ljava/nio/ByteBuffer;JI)[[B
* Signature: (Ljava/nio/ByteBuffer;JIZ)[[B
*/
JNIEXPORT jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1recover
(JNIEnv *, jclass, jobject, jlong, jint);
(JNIEnv *, jclass, jobject, jlong, jint, jboolean);
#ifdef __cplusplus
}

View File

@ -19,19 +19,23 @@ package fr.acinq.secp256k1
import kotlin.jvm.JvmStatic
import kotlin.jvm.Synchronized
public enum class SigFormat(internal val size: Int) { COMPACT(64), DER(72) }
public enum class PubKeyFormat(internal val size: Int) { COMPRESSED(33), UNCOMPRESSED(65) }
public expect object Secp256k1 {
public fun verify(data: ByteArray, signature: ByteArray, pub: ByteArray): Boolean
public fun sign(data: ByteArray, sec: ByteArray): ByteArray
public fun sign(data: ByteArray, sec: ByteArray, format: SigFormat): ByteArray
public fun signCompact(data: ByteArray, sec: ByteArray): ByteArray
public fun signatureNormalize(sig: ByteArray, format: SigFormat): Pair<ByteArray, Boolean>
public fun secKeyVerify(seckey: ByteArray): Boolean
public fun computePubkey(seckey: ByteArray): ByteArray
public fun computePubkey(seckey: ByteArray, format: PubKeyFormat): ByteArray
public fun parsePubkey(pubkey: ByteArray): ByteArray
public fun parsePubkey(pubkey: ByteArray, format: PubKeyFormat): ByteArray
public fun cleanup()
@ -51,7 +55,7 @@ public expect object Secp256k1 {
public fun createECDHSecret(seckey: ByteArray, pubkey: ByteArray): ByteArray
public fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray
public fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int, format: PubKeyFormat): ByteArray
public fun randomize(seed: ByteArray): Boolean
}

View File

@ -2,6 +2,7 @@ package fr.acinq.secp256k1
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
@ -20,10 +21,10 @@ class Secp256k1Test {
val sig: ByteArray = Hex.decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase())
val pub: ByteArray = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase())
result = Secp256k1.verify(data, sig, pub)
assertEquals(result, true, "testVerifyPos")
assertTrue(result, "testVerifyPos")
val sigCompact: ByteArray = Hex.decode("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase())
result = Secp256k1.verify(data, sigCompact, pub)
assertEquals(result, true, "testVerifyPos")
assertTrue(result, "testVerifyPos")
}
/**
@ -37,7 +38,7 @@ class Secp256k1Test {
val pub: ByteArray = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase())
result = Secp256k1.verify(data, sig, pub)
//System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
assertEquals(result, false, "testVerifyNeg")
assertFalse(result, "testVerifyNeg")
}
/**
@ -49,7 +50,7 @@ class Secp256k1Test {
val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase())
result = Secp256k1.secKeyVerify(sec)
//System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
assertEquals(result, true, "testSecKeyVerifyPos")
assertTrue(result, "testSecKeyVerifyPos")
}
/**
@ -61,7 +62,7 @@ class Secp256k1Test {
val sec: ByteArray = Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase())
result = Secp256k1.secKeyVerify(sec)
//System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
assertEquals(result, false, "testSecKeyVerifyNeg")
assertFalse(result, "testSecKeyVerifyNeg")
}
/**
@ -70,11 +71,11 @@ class Secp256k1Test {
@Test
fun testPubKeyCreatePos() {
val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase())
val resultArr: ByteArray = Secp256k1.computePubkey(sec)
val resultArr: ByteArray = Secp256k1.computePubkey(sec, PubKeyFormat.UNCOMPRESSED)
val pubkeyString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(
pubkeyString,
"04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6",
pubkeyString,
"testPubKeyCreatePos"
)
}
@ -85,26 +86,26 @@ class Secp256k1Test {
@Test
fun testPubKeyCreateNeg() {
val sec: ByteArray = Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase())
val resultArr: ByteArray = Secp256k1.computePubkey(sec)
val resultArr: ByteArray = Secp256k1.computePubkey(sec, PubKeyFormat.UNCOMPRESSED)
val pubkeyString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(pubkeyString, "", "testPubKeyCreateNeg")
assertEquals("", pubkeyString, "testPubKeyCreateNeg")
}
@Test
fun testPubKeyNegatePos() {
val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase())
val pubkey: ByteArray = Secp256k1.computePubkey(sec)
val pubkey: ByteArray = Secp256k1.computePubkey(sec, PubKeyFormat.UNCOMPRESSED)
val pubkeyString: String = Hex.encode(pubkey).toUpperCase()
assertEquals(
pubkeyString,
"04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6",
pubkeyString,
"testPubKeyCreatePos"
)
val pubkey1: ByteArray = Secp256k1.pubKeyNegate(pubkey)
val pubkeyString1: String = Hex.encode(pubkey1).toUpperCase()
assertEquals(
pubkeyString1,
"04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2DDEFC12B6B8E73968536514302E69ED1DDB24B999EFEE79C12D03AB17E79E1989",
pubkeyString1,
"testPubKeyNegatePos"
)
}
@ -115,11 +116,11 @@ class Secp256k1Test {
@Test
fun testPubKeyParse() {
val pub: ByteArray = Hex.decode("02C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D".toLowerCase())
val resultArr: ByteArray = Secp256k1.parsePubkey(pub)
val resultArr: ByteArray = Secp256k1.parsePubkey(pub, PubKeyFormat.UNCOMPRESSED)
val pubkeyString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(
pubkeyString,
"04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6",
pubkeyString,
"testPubKeyAdd"
)
}
@ -131,8 +132,8 @@ class Secp256k1Test {
val pub3: ByteArray = Secp256k1.pubKeyAdd(pub1, pub2)
val pubkeyString: String = Hex.encode(pub3).toUpperCase()
assertEquals(
pubkeyString,
"04531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE3379E92C265E71E481BA82A84675A47AC705A200FCD524E92D93B0E7386F26A5458",
pubkeyString,
"testPubKeyAdd"
)
}
@ -144,15 +145,28 @@ class Secp256k1Test {
fun testSignPos() {
val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()) //sha256hash of "testing"
val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase())
val resultArr: ByteArray = Secp256k1.sign(data, sec)
val resultArr: ByteArray = Secp256k1.sign(data, sec, SigFormat.DER)
val sigString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(
sigString,
"30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9",
sigString,
"testSignPos"
)
}
@Test
fun testSignatureNormalize() {
val data: ByteArray = Hex.decode("30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9".toLowerCase())
val (resultArr, isHighS) = Secp256k1.signatureNormalize(data, SigFormat.COMPACT)
val sigString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(
"182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A21C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9",
sigString,
"testSignPos"
)
assertFalse(isHighS, "isHighS")
}
/**
* This tests sign() for a invalid secretkey
*/
@ -160,20 +174,20 @@ class Secp256k1Test {
fun testSignNeg() {
val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()) //sha256hash of "testing"
val sec: ByteArray = Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase())
val resultArr: ByteArray = Secp256k1.sign(data, sec)
val resultArr: ByteArray = Secp256k1.sign(data, sec, SigFormat.DER)
val sigString: String = Hex.encode(resultArr)
assertEquals(sigString, "", "testSignNeg")
assertEquals("", sigString, "testSignNeg")
}
@Test
fun testSignCompactPos() {
val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()) //sha256hash of "testing"
val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase())
val resultArr: ByteArray = Secp256k1.signCompact(data, sec)
val resultArr: ByteArray = Secp256k1.sign(data, sec, SigFormat.COMPACT)
val sigString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(
sigString,
"182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A21C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9",
sigString,
"testSignCompactPos"
)
//assertEquals( sigString, "30 44 02 20 182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A2 02 20 1C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9" , "testSignPos");
@ -184,8 +198,8 @@ class Secp256k1Test {
val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase())
val sec1: ByteArray = Secp256k1.privKeyNegate(sec)
assertEquals(
Hex.encode(sec1).toUpperCase(),
"981A9A7DD677A622518DA068D66D5F824E5F22F084B8A0E2F195B5662F300C11",
Hex.encode(sec1).toUpperCase(),
"testPrivKeyNegate"
)
val sec2: ByteArray = Secp256k1.privKeyNegate(sec1)
@ -201,7 +215,11 @@ class Secp256k1Test {
val data: ByteArray = Hex.decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()) //sha256hash of "tweak"
val resultArr: ByteArray = Secp256k1.privKeyTweakAdd(sec, data)
val sigString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(sigString, "A168571E189E6F9A7E2D657A4B53AE99B909F7E712D1C23CED28093CD57C88F3", "testPrivKeyAdd_1")
assertEquals(
"A168571E189E6F9A7E2D657A4B53AE99B909F7E712D1C23CED28093CD57C88F3",
sigString,
"testPrivKeyAdd_1"
)
}
/**
@ -213,7 +231,11 @@ class Secp256k1Test {
val data: ByteArray = Hex.decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()) //sha256hash of "tweak"
val resultArr: ByteArray = Secp256k1.privKeyTweakMul(sec, data)
val sigString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(sigString, "97F8184235F101550F3C71C927507651BD3F1CDB4A5A33B8986ACF0DEE20FFFC", "testPrivKeyMul_1")
assertEquals(
"97F8184235F101550F3C71C927507651BD3F1CDB4A5A33B8986ACF0DEE20FFFC",
sigString,
"testPrivKeyMul_1"
)
}
/**
@ -226,8 +248,8 @@ class Secp256k1Test {
val resultArr: ByteArray = Secp256k1.pubKeyTweakAdd(pub, data)
val sigString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(
sigString,
"0411C6790F4B663CCE607BAAE08C43557EDC1A4D11D88DFCB3D841D0C6A941AF525A268E2A863C148555C48FB5FBA368E88718A46E205FABC3DBA2CCFFAB0796EF",
sigString,
"testPrivKeyAdd_2"
)
}
@ -242,8 +264,8 @@ class Secp256k1Test {
val resultArr: ByteArray = Secp256k1.pubKeyTweakMul(pub, data)
val sigString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(
sigString,
"04E0FE6FE55EBCA626B98A807F6CAF654139E14E5E3698F01A9A658E21DC1D2791EC060D4F412A794D5370F672BC94B722640B5F76914151CFCA6E712CA48CC589",
sigString,
"testPrivKeyMul_2"
)
}
@ -255,7 +277,7 @@ class Secp256k1Test {
fun testRandomize() {
val seed: ByteArray = Hex.decode("A441B15FE9A3CF56661190A0B93B9DEC7D04127288CC87250967CF3B52894D11".toLowerCase()) //sha256hash of "random"
val result: Boolean = Secp256k1.randomize(seed)
assertEquals(result, true, "testRandomize")
assertTrue(result, "testRandomize")
}
@Test
@ -265,8 +287,8 @@ class Secp256k1Test {
val resultArr: ByteArray = Secp256k1.createECDHSecret(sec, pub)
val ecdhString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(
ecdhString,
"2A2A67007A926E6594AF3EB564FC74005B37A9C8AEF2033C4552051B5C87F043",
ecdhString,
"testCreateECDHSecret"
)
}
@ -275,10 +297,10 @@ class Secp256k1Test {
fun testEcdsaRecover() {
val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()) //sha256hash of "testing"
val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase())
val pub: ByteArray = Secp256k1.computePubkey(sec)
val sig: ByteArray = Secp256k1.signCompact(data, sec)
val pub0: ByteArray = Secp256k1.ecdsaRecover(sig, data, 0)
val pub1: ByteArray = Secp256k1.ecdsaRecover(sig, data, 1)
val pub: ByteArray = Secp256k1.computePubkey(sec, PubKeyFormat.UNCOMPRESSED)
val sig: ByteArray = Secp256k1.sign(data, sec, SigFormat.COMPACT)
val pub0: ByteArray = Secp256k1.ecdsaRecover(sig, data, 0, PubKeyFormat.UNCOMPRESSED)
val pub1: ByteArray = Secp256k1.ecdsaRecover(sig, data, 1, PubKeyFormat.UNCOMPRESSED)
assertTrue(pub.contentEquals(pub0) || pub.contentEquals(pub1), "testEcdsaRecover")
}
}

View File

@ -30,15 +30,15 @@ public actual object Secp256k1 {
public actual fun verify(data: ByteArray, signature: ByteArray, pub: ByteArray): Boolean = NativeSecp256k1.verify(data, signature, pub)
public actual fun sign(data: ByteArray, sec: ByteArray): ByteArray = NativeSecp256k1.sign(data, sec)
public actual fun sign(data: ByteArray, sec: ByteArray, format: SigFormat): ByteArray = NativeSecp256k1.sign(data, sec, format == SigFormat.COMPACT)
public actual fun signCompact(data: ByteArray, sec: ByteArray): ByteArray = NativeSecp256k1.signCompact(data, sec)
public actual fun signatureNormalize(sig: ByteArray, format: SigFormat): Pair<ByteArray, Boolean> = NativeSecp256k1.signatureNormalize(sig, format == SigFormat.COMPACT)
public actual fun secKeyVerify(seckey: ByteArray): Boolean = NativeSecp256k1.secKeyVerify(seckey)
public actual fun computePubkey(seckey: ByteArray): ByteArray = NativeSecp256k1.computePubkey(seckey)
public actual fun computePubkey(seckey: ByteArray, format: PubKeyFormat): ByteArray = NativeSecp256k1.computePubkey(seckey, format == PubKeyFormat.COMPRESSED)
public actual fun parsePubkey(pubkey: ByteArray): ByteArray = NativeSecp256k1.parsePubkey(pubkey)
public actual fun parsePubkey(pubkey: ByteArray, format: PubKeyFormat): ByteArray = NativeSecp256k1.parsePubkey(pubkey, format == PubKeyFormat.COMPRESSED)
public actual fun cleanup(): Unit = NativeSecp256k1.cleanup()
@ -58,7 +58,7 @@ public actual object Secp256k1 {
public actual fun createECDHSecret(seckey: ByteArray, pubkey: ByteArray): ByteArray = NativeSecp256k1.createECDHSecret(seckey, pubkey)
public actual fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray = NativeSecp256k1.ecdsaRecover(sig, message, recid)
public actual fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int, format: PubKeyFormat): ByteArray = NativeSecp256k1.ecdsaRecover(sig, message, recid, format == PubKeyFormat.COMPRESSED)
public actual fun randomize(seed: ByteArray): Boolean = NativeSecp256k1.randomize(seed)

View File

@ -92,14 +92,15 @@ public object NativeSecp256k1 {
/**
* libsecp256k1 Create an ECDSA signature.
*
* @param data Message hash, 32 bytes
* @param sec Secret key, 32 bytes
* @param data Message hash, 32 bytes
* @param sec Secret key, 32 bytes
* @param compact True for compact signature, false for DER
* @return a signature, or an empty array is signing failed
* @throws AssertFailException in case of failure
*/
@JvmStatic
@Throws(AssertFailException::class)
public fun sign(data: ByteArray, sec: ByteArray): ByteArray {
public fun sign(data: ByteArray, sec: ByteArray, compact: Boolean): ByteArray {
require(data.size == 32 && sec.size <= 32)
val byteBuff = pack(data, sec)
val retByteArray: Array<ByteArray>
@ -107,6 +108,7 @@ public object NativeSecp256k1 {
retByteArray = try {
secp256k1_ecdsa_sign(
byteBuff,
compact,
Secp256k1Context.getContext()
)
} finally {
@ -123,27 +125,18 @@ public object NativeSecp256k1 {
return if (retVal == 0) ByteArray(0) else sigArr
}
/**
* libsecp256k1 Create an ECDSA signature.
*
* @param data Message hash, 32 bytes
* @param sec Secret key, 32 bytes
*
*
* Return values
* @return a signature, or an empty array is signing failed
* @throws AssertFailException in case of failure
*/
@JvmStatic
@Throws(AssertFailException::class)
public fun signCompact(data: ByteArray, sec: ByteArray): ByteArray {
require(data.size == 32 && sec.size <= 32)
val byteBuff = pack(data, sec)
public fun signatureNormalize(sig: ByteArray, compact: Boolean): Pair<ByteArray, Boolean> {
require(sig.size == 64 || sig.size in 70..73)
val byteBuff = pack(sig)
val retByteArray: Array<ByteArray>
r.lock()
retByteArray = try {
secp256k1_ecdsa_sign_compact(
secp256k1_ecdsa_normalize(
byteBuff,
sig.size,
compact,
Secp256k1Context.getContext()
)
} finally {
@ -152,12 +145,13 @@ public object NativeSecp256k1 {
val sigArr = retByteArray[0]
val sigLen = BigInteger(byteArrayOf(retByteArray[1][0])).toInt()
val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt()
val retBool = BigInteger(byteArrayOf(retByteArray[1][2])).toInt()
NativeSecp256k1Util.assertEquals(
sigArr.size,
sigLen,
"Got bad signature length."
)
return if (retVal == 0) ByteArray(0) else sigArr
return (if (retVal == 0) ByteArray(0) else sigArr) to (retBool == 1)
}
/**
@ -191,7 +185,7 @@ public object NativeSecp256k1 {
//TODO add a 'compressed' arg
@JvmStatic
@Throws(AssertFailException::class)
public fun computePubkey(seckey: ByteArray): ByteArray {
public fun computePubkey(seckey: ByteArray, compressed: Boolean): ByteArray {
require(seckey.size == 32)
val byteBuff = pack(seckey)
val retByteArray: Array<ByteArray>
@ -199,6 +193,7 @@ public object NativeSecp256k1 {
retByteArray = try {
secp256k1_ec_pubkey_create(
byteBuff,
compressed,
Secp256k1Context.getContext()
)
} finally {
@ -218,7 +213,7 @@ public object NativeSecp256k1 {
*/
@JvmStatic
@Throws(AssertFailException::class)
public fun parsePubkey(pubkey: ByteArray): ByteArray {
public fun parsePubkey(pubkey: ByteArray, compressed: Boolean): ByteArray {
require(pubkey.size == 33 || pubkey.size == 65)
val byteBuff = pack(pubkey)
val retByteArray: Array<ByteArray>
@ -227,7 +222,8 @@ public object NativeSecp256k1 {
secp256k1_ec_pubkey_parse(
byteBuff,
Secp256k1Context.getContext(),
pubkey.size
pubkey.size,
compressed
)
} finally {
r.unlock()
@ -235,7 +231,7 @@ public object NativeSecp256k1 {
val pubArr = retByteArray[0]
BigInteger(byteArrayOf(retByteArray[1][0])).toInt()
val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt()
NativeSecp256k1Util.assertEquals(pubArr.size, 65, "Got bad pubkey length.")
NativeSecp256k1Util.assertEquals(pubArr.size, if (compressed) 33 else 65, "Got bad pubkey length.")
return if (retVal == 0) ByteArray(0) else pubArr
}
@ -456,7 +452,7 @@ public object NativeSecp256k1 {
val pubArr = retByteArray[0]
val pubLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF
val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt()
NativeSecp256k1Util.assertEquals(65, pubLen, "Got bad pubkey length.")
NativeSecp256k1Util.assertEquals(pubkey1.size, pubLen, "Got bad pubkey length.")
NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.")
return pubArr
}
@ -494,7 +490,7 @@ public object NativeSecp256k1 {
@JvmStatic
@Throws(AssertFailException::class)
public fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray {
public fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int, compressed: Boolean): ByteArray {
require(sig.size == 64)
require(message.size == 32)
val byteBuff = pack(sig, message)
@ -504,14 +500,15 @@ public object NativeSecp256k1 {
secp256k1_ecdsa_recover(
byteBuff,
Secp256k1Context.getContext(),
recid
recid,
compressed
)
} finally {
r.unlock()
}
val resArr = retByteArray[0]
val retVal = BigInteger(byteArrayOf(retByteArray[1][0])).toInt()
NativeSecp256k1Util.assertEquals(resArr.size, 65, "Got bad result length.")
NativeSecp256k1Util.assertEquals(resArr.size, if (compressed) 33 else 65, "Got bad result length.")
NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.")
return resArr
}
@ -549,23 +546,12 @@ public object NativeSecp256k1 {
@JvmStatic private external fun secp256k1_pubkey_tweak_mul(byteBuff: ByteBuffer, context: Long, pubLen: Int): Array<ByteArray>
@JvmStatic private external fun secp256k1_destroy_context(context: Long)
@JvmStatic private external fun secp256k1_ecdsa_verify(byteBuff: ByteBuffer, context: Long, sigLen: Int, pubLen: Int): Int
@JvmStatic private external fun secp256k1_ecdsa_sign(byteBuff: ByteBuffer, context: Long): Array<ByteArray>
@JvmStatic private external fun secp256k1_ecdsa_sign_compact(byteBuff: ByteBuffer, context: Long): Array<ByteArray>
@JvmStatic private external fun secp256k1_ecdsa_sign(byteBuff: ByteBuffer, compact: Boolean, context: Long): Array<ByteArray>
@JvmStatic private external fun secp256k1_ecdsa_normalize(byteBuff: ByteBuffer, sigLen: Int, compact: Boolean, context: Long): Array<ByteArray>
@JvmStatic private external fun secp256k1_ec_seckey_verify(byteBuff: ByteBuffer, context: Long): Int
@JvmStatic private external fun secp256k1_ec_pubkey_create(byteBuff: ByteBuffer, context: Long): Array<ByteArray>
@JvmStatic private external fun secp256k1_ec_pubkey_parse(
byteBuff: ByteBuffer,
context: Long,
inputLen: Int
): Array<ByteArray>
@JvmStatic private external fun secp256k1_ec_pubkey_add(
byteBuff: ByteBuffer,
context: Long,
lent1: Int,
len2: Int
): Array<ByteArray>
@JvmStatic private external fun secp256k1_ec_pubkey_create(byteBuff: ByteBuffer, compressed: Boolean, context: Long): Array<ByteArray>
@JvmStatic private external fun secp256k1_ec_pubkey_parse(byteBuff: ByteBuffer, context: Long, inputLen: Int, compressed: Boolean): Array<ByteArray>
@JvmStatic private external fun secp256k1_ec_pubkey_add(byteBuff: ByteBuffer, context: Long, lent1: Int, len2: Int): Array<ByteArray>
@JvmStatic private external fun secp256k1_ecdh(byteBuff: ByteBuffer, context: Long, inputLen: Int): Array<ByteArray>
@JvmStatic private external fun secp256k1_ecdsa_recover(byteBuff: ByteBuffer, context: Long, recid: Int): Array<ByteArray>
@JvmStatic private external fun secp256k1_ecdsa_recover(byteBuff: ByteBuffer, context: Long, recid: Int, compressed: Boolean): Array<ByteArray>
}

View File

@ -7,10 +7,6 @@ import secp256k1.*
@OptIn(ExperimentalUnsignedTypes::class)
public actual object Secp256k1 {
private const val SIG_FORMAT_UNKNOWN = 0
private const val SIG_FORMAT_COMPACT = 1
private const val SIG_FORMAT_DER = 2
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())
?: error("Could not create segp256k1 context")
@ -19,22 +15,34 @@ public actual object Secp256k1 {
private fun Int.requireSuccess() = require(this == 1) { "secp256k1 native function call failed" }
private fun MemScope.allocSignature(input: ByteArray): secp256k1_ecdsa_signature {
val sigFormat = when (input.size) {
64 -> SIG_FORMAT_COMPACT
70, 71, 72, 73 -> SIG_FORMAT_DER
else -> SIG_FORMAT_UNKNOWN
}
val sig = alloc<secp256k1_ecdsa_signature>()
val nativeBytes = toNat(input)
val result = when (sigFormat) {
SIG_FORMAT_COMPACT -> secp256k1_ecdsa_signature_parse_compact(ctx, sig.ptr, nativeBytes)
SIG_FORMAT_DER -> secp256k1_ecdsa_signature_parse_der(ctx, sig.ptr, nativeBytes, input.size.toULong())
else -> 0
val result = when (input.size) {
64 -> secp256k1_ecdsa_signature_parse_compact(ctx, sig.ptr, nativeBytes)
in 70..73 -> secp256k1_ecdsa_signature_parse_der(ctx, sig.ptr, nativeBytes, input.size.convert())
else -> error("Unknown signature format")
}
require(result == 1) { "cannot parse signature (size = ${input.size}, format = $sigFormat sig = ${Hex.encode(input)}" }
require(result == 1) { "cannot parse signature (size = ${input.size} sig = ${Hex.encode(input)}" }
return sig
}
private fun MemScope.serializeSignature(signature: secp256k1_ecdsa_signature, format: SigFormat): ByteArray {
val natOutput = allocArray<UByteVar>(format.size)
when (format) {
SigFormat.DER -> {
val outputLen = alloc<size_tVar>()
outputLen.value = 72.convert()
secp256k1_ecdsa_signature_serialize_der(ctx, natOutput, outputLen.ptr, signature.ptr).requireSuccess()
return natOutput.readBytes(outputLen.value.toInt())
}
SigFormat.COMPACT -> {
secp256k1_ecdsa_signature_serialize_compact(ctx, natOutput, signature.ptr).requireSuccess()
return natOutput.readBytes(64)
}
}
}
private fun MemScope.allocPublicKey(pubkey: ByteArray): secp256k1_pubkey {
val natPub = toNat(pubkey)
val pub = alloc<secp256k1_pubkey>()
@ -61,68 +69,59 @@ public actual object Secp256k1 {
require(data.size == 32)
require(pub.size == 33 || pub.size == 65)
memScoped {
val pubkey = allocPublicKey(pub)
val natData = toNat(data)
val parsedSig = allocSignature(signature)
return secp256k1_ecdsa_verify(ctx, parsedSig.ptr, natData, pubkey.ptr) == 1
val nPubkey = allocPublicKey(pub)
val nData = toNat(data)
val nSig = allocSignature(signature)
return secp256k1_ecdsa_verify(ctx, nSig.ptr, nData, nPubkey.ptr) == 1
}
}
public actual fun sign(data: ByteArray, sec: ByteArray): ByteArray {
public actual fun sign(data: ByteArray, sec: ByteArray, format: SigFormat): ByteArray {
require(sec.size == 32)
require(data.size == 32)
memScoped {
val natSec = toNat(sec)
val natData = toNat(data)
val natSig = alloc<secp256k1_ecdsa_signature>()
val result = secp256k1_ecdsa_sign(ctx, natSig.ptr, natData, natSec, null, null)
val nSec = toNat(sec)
val nData = toNat(data)
val nSig = alloc<secp256k1_ecdsa_signature>()
val result = secp256k1_ecdsa_sign(ctx, nSig.ptr, nData, nSec, null, null)
if (result == 0) return ByteArray(0)
val natOutput = allocArray<UByteVar>(72)
val outputLen = alloc<size_tVar>()
outputLen.value = 72.convert()
secp256k1_ecdsa_signature_serialize_der(ctx, natOutput, outputLen.ptr, natSig.ptr).requireSuccess()
return natOutput.readBytes(outputLen.value.toInt())
return serializeSignature(nSig, format)
}
}
public actual fun signCompact(data: ByteArray, sec: ByteArray): ByteArray {
require(data.size == 32)
require(sec.size <= 32)
public actual fun signatureNormalize(sig: ByteArray, format: SigFormat): Pair<ByteArray, Boolean> {
require(sig.size == 64 || sig.size in 70..73)
memScoped {
val natSec = toNat(sec)
val natData = toNat(data)
val natSig = alloc<secp256k1_ecdsa_signature>()
secp256k1_ecdsa_sign(ctx, natSig.ptr, natData, natSec, null, null).requireSuccess()
val natCompact = allocArray<UByteVar>(64)
secp256k1_ecdsa_signature_serialize_compact(ctx, natCompact, natSig.ptr).requireSuccess()
return natCompact.readBytes(64)
val nSig = allocSignature(sig)
val isHighS = secp256k1_ecdsa_signature_normalize(ctx, nSig.ptr, nSig.ptr)
return Pair(serializeSignature(nSig, format), isHighS == 1)
}
}
public actual fun secKeyVerify(seckey: ByteArray): Boolean {
require(seckey.size == 32)
memScoped {
val natSec = toNat(seckey)
return secp256k1_ec_seckey_verify(ctx, natSec) == 1
val nSec = toNat(seckey)
return secp256k1_ec_seckey_verify(ctx, nSec) == 1
}
}
public actual fun computePubkey(seckey: ByteArray): ByteArray {
public actual fun computePubkey(seckey: ByteArray, format: PubKeyFormat): ByteArray {
require(seckey.size == 32)
memScoped {
val natSec = toNat(seckey)
val pubkey = alloc<secp256k1_pubkey>()
val result = secp256k1_ec_pubkey_create(ctx, pubkey.ptr, natSec)
val nSec = toNat(seckey)
val nPubkey = alloc<secp256k1_pubkey>()
val result = secp256k1_ec_pubkey_create(ctx, nPubkey.ptr, nSec)
if (result == 0) return ByteArray(0)
return serializePubkey(pubkey, 65)
return serializePubkey(nPubkey, format.size)
}
}
public actual fun parsePubkey(pubkey: ByteArray): ByteArray {
public actual fun parsePubkey(pubkey: ByteArray, format: PubKeyFormat): ByteArray {
require(pubkey.size == 33 || pubkey.size == 65)
memScoped {
val nPubkey = allocPublicKey(pubkey)
return serializePubkey(nPubkey, 65)
return serializePubkey(nPubkey, format.size)
}
}
@ -177,7 +176,7 @@ public actual object Secp256k1 {
val nPubkey = allocPublicKey(pubkey)
val nTweak = toNat(tweak)
secp256k1_ec_pubkey_tweak_add(ctx, nPubkey.ptr, nTweak).requireSuccess()
return serializePubkey(nPubkey, 65)
return serializePubkey(nPubkey, pubkey.size)
}
}
@ -187,18 +186,19 @@ public actual object Secp256k1 {
val nPubkey = allocPublicKey(pubkey)
val nTweak = toNat(tweak)
secp256k1_ec_pubkey_tweak_mul(ctx, nPubkey.ptr, nTweak).requireSuccess()
return serializePubkey(nPubkey, 65)
return serializePubkey(nPubkey, pubkey.size)
}
}
public actual fun pubKeyAdd(pubkey1: ByteArray, pubkey2: ByteArray): ByteArray {
require(pubkey1.size == 33 || pubkey2.size == 65)
require(pubkey1.size == 33 || pubkey1.size == 65)
require(pubkey2.size == 33 || pubkey2.size == 65)
memScoped {
val nPubkey1 = allocPublicKey(pubkey1)
val nPubkey2 = allocPublicKey(pubkey2)
val combined = nativeHeap.alloc<secp256k1_pubkey>()
val combined = alloc<secp256k1_pubkey>()
secp256k1_ec_pubkey_combine(ctx, combined.ptr, cValuesOf(nPubkey1.ptr, nPubkey2.ptr), 2.convert()).requireSuccess()
return serializePubkey(combined, 65)
return serializePubkey(combined, pubkey1.size)
}
}
@ -214,17 +214,17 @@ public actual object Secp256k1 {
}
}
public actual fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray {
public actual fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int, format: PubKeyFormat): ByteArray {
require(sig.size == 64)
require(message.size == 32)
memScoped {
val nSig = toNat(sig)
val rSig = nativeHeap.alloc<secp256k1_ecdsa_recoverable_signature>()
val rSig = alloc<secp256k1_ecdsa_recoverable_signature>()
secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, rSig.ptr, nSig, recid).requireSuccess()
val nMessage = toNat(message)
val pubkey = nativeHeap.alloc<secp256k1_pubkey>()
val pubkey = alloc<secp256k1_pubkey>()
secp256k1_ecdsa_recover(ctx, pubkey.ptr, rSig.ptr, nMessage).requireSuccess()
return serializePubkey(pubkey, 65)
return serializePubkey(pubkey, format.size)
}
}