5 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
14 changed files with 106 additions and 58 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

@@ -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.11.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

@@ -517,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++) {
@@ -600,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