10 Commits

Author SHA1 Message Date
Fabrice Drouin
f5e1655ff0 Set version to 0.13.0 (#98) 2024-01-23 16:46:55 +01:00
Fabrice Drouin
8ba5d4652e Use kotlin 1.9 (#92)
Use kotlin 1.9
2024-01-23 15:44:06 +01:00
Fabrice Drouin
e94e41b896 Update secp256k1 to version 0.4.1 (#96)
Use secp256k1 0.4.1
2024-01-02 11:16:36 +01:00
Fabrice Drouin
1a4c8b37cb Release 0.12.0 (#95) 2023-12-13 16:40:32 +01:00
Fabrice Drouin
f242b4ffe8 Check arguments passed to secp256k1 methods (#94)
* Check arguments passed to secp256k1 methods

Illegal arguments will trigger an internal callback that prints to stderr and calls abort.
We already check arguments in our JNI and kotlin native code but had missed 2 checks (recid in ecdsaRecover, empty arrays in pubkeyCombine).

* Implement the same "tweak" checks in the native code and JNI code

The native code was missing checks on the "tweak" size (which must be 32 bytes)
2023-12-13 13:42:14 +01:00
Fabrice Drouin
161da89ee1 Set version to 0.11.0 (#86) 2023-09-28 09:50:24 +02:00
Fabrice Drouin
3706a546a2 Use secp256k1 0.4.0 (#85) 2023-09-18 14:05:36 +02:00
Fabrice Drouin
ffcaaf1b64 Set version to 0.10.1 (#84) 2023-06-28 13:03:19 +02:00
Fabrice Drouin
6ef94df247 Use secp256k1 0.3.2 (#83) 2023-06-28 10:43:05 +02:00
Fabrice Drouin
5169073a92 Update README.md 2023-05-15 11:15:30 +02:00
15 changed files with 113 additions and 62 deletions

View File

@@ -58,15 +58,11 @@ jobs:
base-devel
autotools
mingw-w64-x86_64-gcc
- name: Set up JDK 8
uses: actions/setup-java@v1
with:
java-version: 8
- name: Setup Android
if: matrix.os != 'windows-latest'
shell: bash
run: |
$ANDROID_HOME/tools/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
- name: Setup Android
if: matrix.os == 'windows-latest'
shell: msys2 {0}
@@ -86,7 +82,7 @@ jobs:
- name: Check Linux
if: matrix.os == 'ubuntu-latest'
shell: bash
run: ./gradlew linuxTest
run: ./gradlew linuxX64Test
- name: Check iOS
if: matrix.os == 'macOS-latest'
shell: bash
@@ -103,7 +99,7 @@ jobs:
- name: Publish Linux
if: matrix.os == 'ubuntu-latest'
shell: bash
run: ./gradlew publishLinuxPublicationToMavenLocal :jni:jvm:linux:publishJvmPublicationToMavenLocal
run: ./gradlew publishLinuxX64PublicationToMavenLocal :jni:jvm:linux:publishJvmPublicationToMavenLocal
- name: Publish Windows
if: matrix.os == 'windows-latest'
shell: msys2 {0}

View File

@@ -67,15 +67,11 @@ jobs:
base-devel
autotools
mingw-w64-x86_64-gcc
- name: Set up JDK 8
uses: actions/setup-java@v1
with:
java-version: 8
- name: Setup Android
if: matrix.os != 'windows-latest'
shell: bash
run: |
$ANDROID_HOME/tools/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
- name: Setup Android
if: matrix.os == 'windows-latest'
shell: msys2 {0}
@@ -95,7 +91,7 @@ jobs:
- name: Check Linux
if: matrix.os == 'ubuntu-latest'
shell: bash
run: ./gradlew linuxTest
run: ./gradlew linuxX64Test
- name: Check iOS
if: matrix.os == 'macOS-latest'
shell: bash
@@ -112,7 +108,7 @@ jobs:
- name: Publish Linux
if: matrix.os == 'ubuntu-latest'
shell: bash
run: ./gradlew publishLinuxPublicationToMavenLocal :jni:jvm:linux:publishJvmPublicationToMavenLocal -PsnapshotNumber=${{ github.run_number }} -PgitRef=${{ github.ref }}
run: ./gradlew publishLinuxX64PublicationToMavenLocal :jni:jvm:linux:publishJvmPublicationToMavenLocal -PsnapshotNumber=${{ github.run_number }} -PgitRef=${{ github.ref }}
- name: Publish Windows
if: matrix.os == 'windows-latest'
shell: msys2 {0}

View File

@@ -73,15 +73,11 @@ jobs:
base-devel
autotools
mingw-w64-x86_64-gcc
- name: Set up JDK 8
uses: actions/setup-java@v1
with:
java-version: 8
- name: Setup Android
if: matrix.os != 'windows-latest'
shell: bash
run: |
$ANDROID_HOME/tools/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
- name: Setup Android
if: matrix.os == 'windows-latest'
shell: msys2 {0}
@@ -101,7 +97,7 @@ jobs:
- name: Check Linux
if: matrix.os == 'ubuntu-latest'
shell: bash
run: ./gradlew linuxTest
run: ./gradlew linuxX64Test
- name: Check iOS
if: matrix.os == 'macOS-latest'
shell: bash

View File

@@ -1,4 +1,4 @@
[![Kotlin](https://img.shields.io/badge/Kotlin-1.6.21-blue.svg?style=flat&logo=kotlin)](http://kotlinlang.org)
[![Kotlin](https://img.shields.io/badge/Kotlin-1.8.21-blue.svg?style=flat&logo=kotlin)](http://kotlinlang.org)
[![Maven Central](https://img.shields.io/maven-central/v/fr.acinq.secp256k1/secp256k1-kmp)](https://search.maven.org/search?q=g:fr.acinq.secp256k1%20a:secp256k1-kmp*)
![Github Actions](https://github.com/ACINQ/secp256k1-kmp/actions/workflows/test.yml/badge.svg)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/ACINQ/secp256k1-kmp/blob/master/LICENSE)
@@ -58,9 +58,9 @@ Native targets include libsecp256k1, called through KMP's c-interop, simply add
The JVM library uses JNI bindings for libsecp256k1, which is much faster than BouncyCastle. It will extract and load native bindings for your operating system in a temporary directory.
JNI libraries are included for:
- Linux 64 bits
- Windows 64 bits
- Macos 64 bits
- Linux 64 bits (x86_64 and arm64)
- Windows 64 bits (x86_64)
- Macos 64 bits (x86_64 and arm64)
Along this library, you **must** specify which JNI native library to use in your dependency manager:

View File

@@ -3,8 +3,8 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.dokka.Platform
plugins {
kotlin("multiplatform") version "1.8.21"
id("org.jetbrains.dokka") version "1.8.10"
kotlin("multiplatform") version "1.9.22"
id("org.jetbrains.dokka") version "1.9.10"
`maven-publish`
}
@@ -16,13 +16,13 @@ buildscript {
dependencies {
classpath("com.android.tools.build:gradle:7.3.1")
classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.8.10")
classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.9.10")
}
}
allprojects {
group = "fr.acinq.secp256k1"
version = "0.10.0"
version = "0.13.0"
repositories {
google()
@@ -52,20 +52,22 @@ kotlin {
}
}
val nativeMain by sourceSets.creating { dependsOn(commonMain) }
val nativeMain by sourceSets.creating
linuxX64("linux") {
linuxX64 {
secp256k1CInterop("host")
compilations["main"].defaultSourceSet.dependsOn(nativeMain)
// https://youtrack.jetbrains.com/issue/KT-39396
compilations["main"].kotlinOptions.freeCompilerArgs += listOf("-include-binary", "$rootDir/native/build/linux/libsecp256k1.a")
}
ios {
iosX64 {
secp256k1CInterop("ios")
}
iosArm64 {
secp256k1CInterop("ios")
}
iosSimulatorArm64 {
secp256k1CInterop("ios")
compilations["main"].defaultSourceSet.dependsOn(nativeMain)
// https://youtrack.jetbrains.com/issue/KT-39396
compilations["main"].kotlinOptions.freeCompilerArgs += listOf("-include-binary", "$rootDir/native/build/ios/libsecp256k1.a")
}
sourceSets.all {
@@ -80,9 +82,9 @@ allprojects {
val currentOs = OperatingSystem.current()
val targets = when {
currentOs.isLinux -> listOf()
currentOs.isMacOsX -> listOf("linux")
currentOs.isWindows -> listOf("linux")
else -> listOf("linux")
currentOs.isMacOsX -> listOf("linuxX64")
currentOs.isWindows -> listOf("linuxX64")
else -> listOf("linuxX64")
}.mapNotNull { kotlin.targets.findByName(it) as? KotlinNativeTarget }
configure(targets) {

View File

@@ -6,11 +6,10 @@ import fr.acinq.secp256k1.NativeSecp256k1
import java.util.*
public object NativeSecp256k1AndroidLoader {
@JvmStatic
@Synchronized
@Throws(Exception::class)
fun load(): Secp256k1 {
public fun load(): Secp256k1 {
try {
System.loadLibrary("secp256k1-jni")
return NativeSecp256k1
@@ -27,5 +26,4 @@ public object NativeSecp256k1AndroidLoader {
}
}
}

View File

@@ -1,6 +1,9 @@
#include <string.h>
#include <stdlib.h>
#ifdef WIN32
#define SECP256K1_STATIC // needed on windows when linking to a static version of secp256k1
#endif
#include "include/secp256k1.h"
#include "include/secp256k1_ecdh.h"
#include "include/secp256k1_recovery.h"
@@ -514,6 +517,7 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
if (jpubkeys == NULL) return NULL;
count = (*penv)->GetArrayLength(penv, jpubkeys);
CHECKRESULT(count < 1, "pubkey array cannot be empty")
pubkeys = calloc(count, sizeof(secp256k1_pubkey*));
for(i = 0; i < count; i++) {
@@ -597,6 +601,7 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
if (jctx == 0) return NULL;
if (jsig == NULL) return NULL;
if (jmsg == NULL) return NULL;
CHECKRESULT(recid < 0 || recid > 3, "invalid recovery id");
sigSize = (*penv)->GetArrayLength(penv, jsig);
int sigFormat = GetSignatureFormat(sigSize);

View File

@@ -9,7 +9,10 @@ cd secp256k1
sh xconfigure.sh --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no
mkdir -p ../build/ios
cp -v _build/universal/* ../build/ios/
cp -v _build/universal/ios/* ../build/ios/
mkdir -p ../build/iosSimulatorArm64
cp -v _build/universal/iosSimulatorArm64/* ../build/iosSimulatorArm64/
rm -rf _build
make clean

View File

@@ -69,9 +69,22 @@ HOST_FLAGS="${ARCH_FLAGS} -mios-simulator-version-min=${MIN_IOS_VERSION} -isysro
CHOST="x86_64-apple-darwin"
Build "$@"
## Build for iphone M1/M2/Mx simulators
SDK="iphonesimulator"
PLATFORM="arm64-sim"
PLATFORM_SIM_ARM=${PLATFORM}
ARCH_FLAGS="-arch arm64"
HOST_FLAGS="${ARCH_FLAGS} -mios-simulator-version-min=${MIN_IOS_VERSION} -isysroot $(xcrun --sdk ${SDK} --show-sdk-path)"
CHOST="arm-apple-darwin"
Build "$@"
# Create universal binary
cd "${PLATFORMS}/${PLATFORM_ARM}/lib"
LIB_NAME=`find . -iname *.a`
cd -
mkdir -p "${UNIVERSAL}" &> /dev/null
lipo -create -output "${UNIVERSAL}/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_ARM}/lib/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_ISIM}/lib/${LIB_NAME}"
mkdir -p "${UNIVERSAL}/ios" &> /dev/null
mkdir -p "${UNIVERSAL}/iosSimulatorArm64" &> /dev/null
lipo -create -output "${UNIVERSAL}/ios/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_ARM}/lib/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_ISIM}/lib/${LIB_NAME}"
# create a specific library for arm64 simulator: it cannot be included in the lib above which already contains an arm64 lib
lipo -create -output "${UNIVERSAL}/iosSimulatorArm64/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_SIM_ARM}/lib/${LIB_NAME}"

View File

@@ -21,9 +21,9 @@ mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/conte
-Djavadoc=$ARTIFACT_ID_BASE-$VERSION-javadoc.jar
popd
pushd .
for i in iosarm64 iosx64 jni-android jni-common jni-jvm-darwin jni-jvm-extract jni-jvm-linux jni-jvm-mingw jni-jvm jvm linux; do
for i in iosarm64 iossimulatorarm64 iosx64 jni-android jni-common jni-jvm-darwin jni-jvm-extract jni-jvm-linux jni-jvm-mingw jni-jvm jvm linuxx64; do
cd fr/acinq/secp256k1/secp256k1-kmp-$i/$VERSION
if [ $i == iosarm64 ] || [ $i == iosx64 ]; then
if [ $i == iosarm64 ] || [ $i == iossimulatorarm64 ] || [ $i == iosx64 ]; then
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \
@@ -32,7 +32,7 @@ for i in iosarm64 iosx64 jni-android jni-common jni-jvm-darwin jni-jvm-extract j
-Dclassifiers=metadata,,cinterop-libsecp256k1 \
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
elif [ $i == linux ]; then
elif [ $i == linuxx64 ]; then
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \

View File

@@ -3,8 +3,12 @@ package = secp256k1
headers = secp256k1.h secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h
headerFilter = secp256k1/** secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h secp256k1.h
libraryPaths.linux = c/secp256k1/build/linux/
staticLibraries.linux = libsecp256k1.a
libraryPaths.linux = c/secp256k1/build/linux/ native/build/linux/ native/build/darwin/
linkerOpts.linux = -L/usr/lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/local/lib
libraryPaths.ios = c/secp256k1/build/ios/ /usr/local/lib
staticLibraries.ios = libsecp256k1.a
libraryPaths.ios_x64 = c/secp256k1/build/ios/ /usr/local/lib native/build/ios/
libraryPaths.ios_arm64 = c/secp256k1/build/ios/ /usr/local/lib native/build/ios/
libraryPaths.ios_simulator_arm64 = c/secp256k1/build/ios/ /usr/local/lib native/build/iosSimulatorArm64/
linkerOpts.ios = -framework Security -framework Foundation

View File

@@ -4,7 +4,7 @@ import kotlinx.cinterop.*
import platform.posix.size_tVar
import secp256k1.*
@OptIn(ExperimentalUnsignedTypes::class)
@OptIn(ExperimentalUnsignedTypes::class, ExperimentalForeignApi::class)
public object Secp256k1Native : Secp256k1 {
private val ctx: CPointer<secp256k1_context> by lazy {
@@ -125,6 +125,7 @@ public object Secp256k1Native : Secp256k1 {
public override fun privKeyTweakAdd(privkey: ByteArray, tweak: ByteArray): ByteArray {
require(privkey.size == 32)
require(tweak.size == 32)
memScoped {
val added = privkey.copyOf()
val natAdd = toNat(added)
@@ -136,6 +137,7 @@ public object Secp256k1Native : Secp256k1 {
public override fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray {
require(privkey.size == 32)
require(tweak.size == 32)
memScoped {
val multiplied = privkey.copyOf()
val natMul = toNat(multiplied)
@@ -156,6 +158,7 @@ public object Secp256k1Native : Secp256k1 {
public override fun pubKeyTweakAdd(pubkey: ByteArray, tweak: ByteArray): ByteArray {
require(pubkey.size == 33 || pubkey.size == 65)
require(tweak.size == 32)
memScoped {
val nPubkey = allocPublicKey(pubkey)
val nTweak = toNat(tweak)
@@ -166,6 +169,7 @@ public object Secp256k1Native : Secp256k1 {
public override fun pubKeyTweakMul(pubkey: ByteArray, tweak: ByteArray): ByteArray {
require(pubkey.size == 33 || pubkey.size == 65)
require(tweak.size == 32)
memScoped {
val nPubkey = allocPublicKey(pubkey)
val nTweak = toNat(tweak)
@@ -175,6 +179,7 @@ public object Secp256k1Native : Secp256k1 {
}
public override fun pubKeyCombine(pubkeys: Array<ByteArray>): ByteArray {
require(pubkeys.isNotEmpty())
pubkeys.forEach { require(it.size == 33 || it.size == 65) }
memScoped {
val nPubkeys = pubkeys.map { allocPublicKey(it).ptr }
@@ -199,6 +204,7 @@ public object Secp256k1Native : Secp256k1 {
public override fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray {
require(sig.size == 64)
require(message.size == 32)
require(recid in 0..3)
memScoped {
val nSig = toNat(sig)
val rSig = alloc<secp256k1_ecdsa_recoverable_signature>()
@@ -232,7 +238,7 @@ public object Secp256k1Native : Secp256k1 {
secp256k1_xonly_pubkey_parse(ctx, pubkey.ptr, nPub).requireSuccess("secp256k1_xonly_pubkey_parse() failed")
val nData = toNat(data)
val nSig = toNat(signature)
return secp256k1_schnorrsig_verify(ctx, nSig, nData, 32, pubkey.ptr) == 1
return secp256k1_schnorrsig_verify(ctx, nSig, nData, 32u, pubkey.ptr) == 1
}
}

View File

@@ -1,4 +1,3 @@
plugins {
kotlin("multiplatform")
if (System.getProperty("includeAndroid")?.toBoolean() == true) {
@@ -36,14 +35,14 @@ kotlin {
}
if (includeAndroid) {
android {
androidTarget {
compilations.all {
kotlinOptions.jvmTarget = "1.8"
}
sourceSets["androidMain"].dependencies {
implementation(project(":jni:android"))
}
sourceSets["androidTest"].dependencies {
sourceSets["androidUnitTest"].dependencies {
implementation(kotlin("test-junit"))
implementation("androidx.test.ext:junit:1.1.2")
implementation("androidx.test.espresso:espresso-core:3.3.0")
@@ -51,17 +50,18 @@ kotlin {
}
}
linuxX64("linux")
ios()
linuxX64()
iosX64()
iosArm64()
iosSimulatorArm64()
}
val includeAndroid = System.getProperty("includeAndroid")?.toBoolean() ?: true
if (includeAndroid) {
extensions.configure<com.android.build.gradle.LibraryExtension>("android") {
defaultConfig {
compileSdkVersion(30)
minSdkVersion(21)
compileSdk = 30
minSdk = 21
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

View File

@@ -352,6 +352,38 @@ class Secp256k1Test {
}
}
@Test
fun testInvalidArguments() {
assertFails {
Secp256k1.pubkeyCreate(ByteArray(32))
}
assertFails {
Secp256k1.pubkeyCreate(Hex.decode("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
}
assertFails {
Secp256k1.pubkeyParse(ByteArray(33))
}
assertFails {
Secp256k1.pubkeyParse(Hex.decode("03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
}
assertFails {
Secp256k1.pubKeyCombine(arrayOf())
}
assertFails {
Secp256k1.pubKeyCombine(arrayOf(ByteArray(0)))
}
assertFails {
Secp256k1.signSchnorr(ByteArray(0), Hex.decode("0101010101010101010101010101010101010101010101010101010101010101"), null)
}
assertFails {
Secp256k1.ecdsaRecover(
Hex.decode("01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101"),
Hex.decode("0202020202020202020202020202020202020202020202020202020202020202"),
-1
)
}
}
@Test
fun fuzzEcdsaSignVerify() {
val random = Random.Default