From a1e19502ea4415341ae08d54d35b6d57764e4f1f Mon Sep 17 00:00:00 2001 From: KotlinCrypto Date: Sat, 23 Aug 2025 18:13:57 -0400 Subject: [PATCH 1/3] Migrate syscall for SYS_getrandom to C code --- library/crypto-rand/build.gradle.kts | 24 +++++++------ .../random/internal/AndroidNativePlatform.kt | 24 ------------- .../CryptoRandAndroidNativeUnitTest.kt | 34 ------------------- .../random/internal/LinuxAndroidPlatform.kt | 14 ++------ .../random/internal/LinuxPlatform.kt | 22 ------------ .../nativeInterop/cinterop/crypto_rand_sys.c | 23 ++++++++++--- .../nativeInterop/cinterop/crypto_rand_sys.h | 16 ++++++--- .../src/nativeInterop/cinterop/syscall.def | 6 ---- 8 files changed, 46 insertions(+), 117 deletions(-) delete mode 100644 library/crypto-rand/src/androidNativeMain/kotlin/org/kotlincrypto/random/internal/AndroidNativePlatform.kt delete mode 100644 library/crypto-rand/src/androidNativeTest/kotlin/org/kotlincrypto/random/internal/CryptoRandAndroidNativeUnitTest.kt delete mode 100644 library/crypto-rand/src/linuxMain/kotlin/org/kotlincrypto/random/internal/LinuxPlatform.kt delete mode 100644 library/crypto-rand/src/nativeInterop/cinterop/syscall.def diff --git a/library/crypto-rand/build.gradle.kts b/library/crypto-rand/build.gradle.kts index f9a5303..422c023 100644 --- a/library/crypto-rand/build.gradle.kts +++ b/library/crypto-rand/build.gradle.kts @@ -71,16 +71,18 @@ kmpConfiguration { .resolve("cinterop") val interopTaskInfo = targets.filterIsInstance().map { target -> - if (target.konanTarget.family == Family.ANDROID) { - target.compilations["main"].cinterops.create("crypto_rand_sys") { - definitionFile.set(cInteropDir.resolve("$name.def")) - includeDirs(cInteropDir) - } + val hasSysRandom = when (target.konanTarget.family) { + Family.ANDROID, Family.LINUX -> "-D__CRYPTO_RAND_HAS_SYS_RANDOM__=1" + else -> null } - target.compilations["test"].cinterops.create("syscall") { + val taskName = target.compilations["main"].cinterops.create("crypto_rand_sys") { definitionFile.set(cInteropDir.resolve("$name.def")) - }.interopProcessingTaskName to target.konanTarget + includeDirs(cInteropDir) + if (hasSysRandom != null) compilerOpts.add(hasSysRandom) + }.interopProcessingTaskName + + Triple(taskName, target.konanTarget, hasSysRandom) } project.extensions.configure("cklib") { @@ -97,11 +99,13 @@ kmpConfiguration { val kt = KonanTarget.predefinedTargets[target]!! - // Must add dependency on the test cinterop task to ensure - // that Kotlin/Native dependencies get downloaded beforehand - interopTaskInfo.forEach { (interopTaskName, konanTarget) -> + interopTaskInfo.forEach { (interopTaskName, konanTarget, hasSysRandom) -> if (kt != konanTarget) return@forEach + + // Must add dependency on the test cinterop task to ensure + // that Kotlin/Native dependencies get downloaded beforehand this.dependsOn(interopTaskName) + if (hasSysRandom != null) compilerArgs.add(hasSysRandom) } } } diff --git a/library/crypto-rand/src/androidNativeMain/kotlin/org/kotlincrypto/random/internal/AndroidNativePlatform.kt b/library/crypto-rand/src/androidNativeMain/kotlin/org/kotlincrypto/random/internal/AndroidNativePlatform.kt deleted file mode 100644 index 4e8845e..0000000 --- a/library/crypto-rand/src/androidNativeMain/kotlin/org/kotlincrypto/random/internal/AndroidNativePlatform.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2025 KotlinCrypto - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -@file:Suppress("FunctionName", "NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress", "SpellCheckingInspection") - -package org.kotlincrypto.random.internal - -import kotlinx.cinterop.ExperimentalForeignApi - -// https://youtrack.jetbrains.com/issue/KT-75722 -@ExperimentalForeignApi -internal actual inline fun _SYS_getrandom(): Int = __SYS_getrandom() diff --git a/library/crypto-rand/src/androidNativeTest/kotlin/org/kotlincrypto/random/internal/CryptoRandAndroidNativeUnitTest.kt b/library/crypto-rand/src/androidNativeTest/kotlin/org/kotlincrypto/random/internal/CryptoRandAndroidNativeUnitTest.kt deleted file mode 100644 index 72b5571..0000000 --- a/library/crypto-rand/src/androidNativeTest/kotlin/org/kotlincrypto/random/internal/CryptoRandAndroidNativeUnitTest.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2025 KotlinCrypto - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -package org.kotlincrypto.random.internal - -import kotlinx.cinterop.ExperimentalForeignApi -import org.kotlincrypto.random.internal.testing.SYS_getrandom -import kotlin.test.Test -import kotlin.test.assertEquals - -@OptIn(ExperimentalForeignApi::class) -class CryptoRandAndroidNativeUnitTest { - - @Test - fun givenSYSgetrandom_whenCheckedAgainstHeaderDefinition_thenMatches() { - assertEquals( - SYS_getrandom, - _SYS_getrandom(), - "expected[${SYS_getrandom}] vs actual[${_SYS_getrandom()}]", - ) - } -} diff --git a/library/crypto-rand/src/linuxAndroidMain/kotlin/org/kotlincrypto/random/internal/LinuxAndroidPlatform.kt b/library/crypto-rand/src/linuxAndroidMain/kotlin/org/kotlincrypto/random/internal/LinuxAndroidPlatform.kt index ba18938..2428928 100644 --- a/library/crypto-rand/src/linuxAndroidMain/kotlin/org/kotlincrypto/random/internal/LinuxAndroidPlatform.kt +++ b/library/crypto-rand/src/linuxAndroidMain/kotlin/org/kotlincrypto/random/internal/LinuxAndroidPlatform.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ -@file:Suppress("KotlinRedundantDiagnosticSuppress", "NOTHING_TO_INLINE", "SpellCheckingInspection", "UnnecessaryOptInAnnotation") +@file:Suppress("NOTHING_TO_INLINE", "RemoveRedundantCallsOfConversionMethods") package org.kotlincrypto.random.internal @@ -24,20 +24,12 @@ import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind import kotlin.contracts.contract -@Suppress("FunctionName") -internal expect inline fun _SYS_getrandom(): Int - -@OptIn(ExperimentalForeignApi::class, UnsafeNumber::class) -private inline fun getrandom2(buf: CPointer, buflen: size_t, flags: u_int): Int { - return syscall(_SYS_getrandom().convert(), buf, buflen, flags).convert() -} - // getrandom(2) available for Linux Kernel 3.17+ (Android API 26+) @OptIn(ExperimentalForeignApi::class, UnsafeNumber::class) internal val HAS_GET_RANDOM: Boolean by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { val buf = ByteArray(1) val result = buf.usePinned { pinned -> - getrandom2(pinned.addressOf(0), buf.size.convert(), 0x0001u /* GRND_NONBLOCK */) + __SYS_getrandom(__buf = pinned.addressOf(0), __len = buf.size.convert(), __is_nonblock = 1).toInt() } if (result >= 0) return@lazy true @@ -54,7 +46,7 @@ internal val HAS_GET_RANDOM: Boolean by lazy(LazyThreadSafetyMode.SYNCHRONIZED) @OptIn(ExperimentalForeignApi::class, UnsafeNumber::class) internal actual fun ByteArray.cryptoRandFill() { if (HAS_GET_RANDOM) { - cryptoRandFill { ptr, len -> getrandom2(ptr, len.toULong().convert(), 0u) } + cryptoRandFill { ptr, len -> __SYS_getrandom(__buf = ptr, __len = len.convert(), __is_nonblock = 0).toInt() } } else { cryptoRandFillURandom() } diff --git a/library/crypto-rand/src/linuxMain/kotlin/org/kotlincrypto/random/internal/LinuxPlatform.kt b/library/crypto-rand/src/linuxMain/kotlin/org/kotlincrypto/random/internal/LinuxPlatform.kt deleted file mode 100644 index 5edfd42..0000000 --- a/library/crypto-rand/src/linuxMain/kotlin/org/kotlincrypto/random/internal/LinuxPlatform.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2025 KotlinCrypto - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -@file:Suppress("FunctionName", "NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress", "SpellCheckingInspection") - -package org.kotlincrypto.random.internal - -import platform.linux.SYS_getrandom - -internal actual inline fun _SYS_getrandom(): Int = SYS_getrandom diff --git a/library/crypto-rand/src/nativeInterop/cinterop/crypto_rand_sys.c b/library/crypto-rand/src/nativeInterop/cinterop/crypto_rand_sys.c index 9bc3037..52f73e0 100644 --- a/library/crypto-rand/src/nativeInterop/cinterop/crypto_rand_sys.c +++ b/library/crypto-rand/src/nativeInterop/cinterop/crypto_rand_sys.c @@ -16,12 +16,25 @@ #include "crypto_rand_sys.h" -#ifdef __ANDROID__ +#ifdef __CRYPTO_RAND_HAS_SYS_RANDOM__ #include +#include -int -__SYS_getrandom() +#if __has_include() +#include +#endif // __has_include() + +#ifndef GRND_NONBLOCK +#define GRND_NONBLOCK 0x01 +#endif // GRND_NONBLOCK + +ssize_t +__SYS_getrandom(void *__buf, size_t __len, int __is_nonblock) { - return SYS_getrandom; + unsigned int flags = 0; + if (__is_nonblock) { + flags = GRND_NONBLOCK; + } + return syscall(SYS_getrandom, __buf, __len, flags); } -#endif /* !defined(__ANDROID__) */ +#endif // __CRYPTO_RAND_HAS_SYS_RANDOM__ diff --git a/library/crypto-rand/src/nativeInterop/cinterop/crypto_rand_sys.h b/library/crypto-rand/src/nativeInterop/cinterop/crypto_rand_sys.h index 2c9dbbf..047b408 100644 --- a/library/crypto-rand/src/nativeInterop/cinterop/crypto_rand_sys.h +++ b/library/crypto-rand/src/nativeInterop/cinterop/crypto_rand_sys.h @@ -14,12 +14,18 @@ * limitations under the License. **/ -/* https://youtrack.jetbrains.com/issue/KT-75722 */ #ifndef CRYPTO_RAND_SYS_H #define CRYPTO_RAND_SYS_H -#ifdef __ANDROID__ -int __SYS_getrandom(); -#endif /* !defined(__ANDROID__) */ +#ifdef __CRYPTO_RAND_HAS_SYS_RANDOM__ +#include -#endif /* !defined(CRYPTO_RAND_SYS_H) */ +/** + * Performs syscall using SYS_getrandom and provided arguments. + * + * If __is_nonblock > 0, will use flag GRND_NONBLOCK, otherwise will use 0. + * */ +ssize_t __SYS_getrandom(void *__buf, size_t __len, int __is_nonblock); +#endif // __CRYPTO_RAND_HAS_SYS_RANDOM__ + +#endif // CRYPTO_RAND_SYS_H diff --git a/library/crypto-rand/src/nativeInterop/cinterop/syscall.def b/library/crypto-rand/src/nativeInterop/cinterop/syscall.def deleted file mode 100644 index a806b30..0000000 --- a/library/crypto-rand/src/nativeInterop/cinterop/syscall.def +++ /dev/null @@ -1,6 +0,0 @@ -# SYS_getrandom for AndroidNative testing -package = org.kotlincrypto.random.internal.testing ---- -#ifdef __ANDROID__ -#include -#endif From 942edec69f44ae05454193ea19355eb8a053536d Mon Sep 17 00:00:00 2001 From: KotlinCrypto Date: Sat, 23 Aug 2025 18:34:54 -0400 Subject: [PATCH 2/3] Fix android native test error output --- .../random/test/android/AndroidNativeTest.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test-android/src/androidInstrumentedTest/kotlin/org/kotlincrypto/random/test/android/AndroidNativeTest.kt b/test-android/src/androidInstrumentedTest/kotlin/org/kotlincrypto/random/test/android/AndroidNativeTest.kt index d3b79b7..44a8019 100644 --- a/test-android/src/androidInstrumentedTest/kotlin/org/kotlincrypto/random/test/android/AndroidNativeTest.kt +++ b/test-android/src/androidInstrumentedTest/kotlin/org/kotlincrypto/random/test/android/AndroidNativeTest.kt @@ -20,7 +20,7 @@ import androidx.test.core.app.ApplicationProvider import io.matthewnelson.kmp.file.toFile import io.matthewnelson.kmp.process.Process import kotlin.test.Test -import kotlin.test.assertEquals +import kotlin.test.fail class AndroidNativeTest { @@ -35,8 +35,11 @@ class AndroidNativeTest { maxBuffer = Int.MAX_VALUE / 2 } - assertEquals(0, out.processInfo.exitCode, out.stdout) - println(out.stdout) - println(out.stderr) + if (out.processInfo.exitCode == 0) return + + val sb = StringBuilder(out.toString()).appendLine() + sb.appendLine(out.stdout).appendLine() + sb.appendLine(out.stderr).appendLine() + fail(sb.toString()) } } From 7a1ead00af738ecf0fe3dcfd386cb2c4a64d3d93 Mon Sep 17 00:00:00 2001 From: KotlinCrypto Date: Sat, 23 Aug 2025 19:28:28 -0400 Subject: [PATCH 3/3] Clean up definitions --- library/crypto-rand/build.gradle.kts | 20 +++++++++++++------ .../random/internal/LinuxAndroidPlatform.kt | 4 ++-- .../nativeInterop/cinterop/crypto_rand_sys.c | 14 ++++++++----- .../nativeInterop/cinterop/crypto_rand_sys.h | 10 +++++++--- 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/library/crypto-rand/build.gradle.kts b/library/crypto-rand/build.gradle.kts index 422c023..f33e1c6 100644 --- a/library/crypto-rand/build.gradle.kts +++ b/library/crypto-rand/build.gradle.kts @@ -71,18 +71,18 @@ kmpConfiguration { .resolve("cinterop") val interopTaskInfo = targets.filterIsInstance().map { target -> - val hasSysRandom = when (target.konanTarget.family) { - Family.ANDROID, Family.LINUX -> "-D__CRYPTO_RAND_HAS_SYS_RANDOM__=1" + val hasSysGetRandom = when (target.konanTarget.family) { + Family.ANDROID, Family.LINUX -> "-DCRYPTO_RAND_HAS_SYS_GETRANDOM=1" else -> null } val taskName = target.compilations["main"].cinterops.create("crypto_rand_sys") { definitionFile.set(cInteropDir.resolve("$name.def")) includeDirs(cInteropDir) - if (hasSysRandom != null) compilerOpts.add(hasSysRandom) + if (hasSysGetRandom != null) compilerOpts.add(hasSysGetRandom) }.interopProcessingTaskName - Triple(taskName, target.konanTarget, hasSysRandom) + Triple(taskName, target.konanTarget, hasSysGetRandom) } project.extensions.configure("cklib") { @@ -99,13 +99,21 @@ kmpConfiguration { val kt = KonanTarget.predefinedTargets[target]!! - interopTaskInfo.forEach { (interopTaskName, konanTarget, hasSysRandom) -> + interopTaskInfo.forEach { (interopTaskName, konanTarget, hasSysGetRandom) -> if (kt != konanTarget) return@forEach // Must add dependency on the test cinterop task to ensure // that Kotlin/Native dependencies get downloaded beforehand this.dependsOn(interopTaskName) - if (hasSysRandom != null) compilerArgs.add(hasSysRandom) + + if (hasSysGetRandom != null) { + compilerArgs.add(hasSysGetRandom) + + // Linux x86_64 is the only compilation w/o sys/random.h + if (konanTarget != KonanTarget.LINUX_X64) { + compilerArgs.add("-DCRYPTO_RAND_HAS_SYS_RANDOM_H=1") + } + } } } } diff --git a/library/crypto-rand/src/linuxAndroidMain/kotlin/org/kotlincrypto/random/internal/LinuxAndroidPlatform.kt b/library/crypto-rand/src/linuxAndroidMain/kotlin/org/kotlincrypto/random/internal/LinuxAndroidPlatform.kt index 2428928..2ac2a53 100644 --- a/library/crypto-rand/src/linuxAndroidMain/kotlin/org/kotlincrypto/random/internal/LinuxAndroidPlatform.kt +++ b/library/crypto-rand/src/linuxAndroidMain/kotlin/org/kotlincrypto/random/internal/LinuxAndroidPlatform.kt @@ -29,7 +29,7 @@ import kotlin.contracts.contract internal val HAS_GET_RANDOM: Boolean by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { val buf = ByteArray(1) val result = buf.usePinned { pinned -> - __SYS_getrandom(__buf = pinned.addressOf(0), __len = buf.size.convert(), __is_nonblock = 1).toInt() + __getrandom(__buf = pinned.addressOf(0), __len = buf.size.convert(), __is_nonblock = 1).toInt() } if (result >= 0) return@lazy true @@ -46,7 +46,7 @@ internal val HAS_GET_RANDOM: Boolean by lazy(LazyThreadSafetyMode.SYNCHRONIZED) @OptIn(ExperimentalForeignApi::class, UnsafeNumber::class) internal actual fun ByteArray.cryptoRandFill() { if (HAS_GET_RANDOM) { - cryptoRandFill { ptr, len -> __SYS_getrandom(__buf = ptr, __len = len.convert(), __is_nonblock = 0).toInt() } + cryptoRandFill { ptr, len -> __getrandom(__buf = ptr, __len = len.convert(), __is_nonblock = 0).toInt() } } else { cryptoRandFillURandom() } diff --git a/library/crypto-rand/src/nativeInterop/cinterop/crypto_rand_sys.c b/library/crypto-rand/src/nativeInterop/cinterop/crypto_rand_sys.c index 52f73e0..a2b04c4 100644 --- a/library/crypto-rand/src/nativeInterop/cinterop/crypto_rand_sys.c +++ b/library/crypto-rand/src/nativeInterop/cinterop/crypto_rand_sys.c @@ -16,20 +16,24 @@ #include "crypto_rand_sys.h" -#ifdef __CRYPTO_RAND_HAS_SYS_RANDOM__ +#if CRYPTO_RAND_HAS_SYS_GETRANDOM #include #include -#if __has_include() +#ifndef CRYPTO_RAND_HAS_SYS_RANDOM_H +#define CRYPTO_RAND_HAS_SYS_RANDOM_H 0 +#endif // CRYPTO_RAND_HAS_SYS_RANDOM_H + +#if CRYPTO_RAND_HAS_SYS_RANDOM_H #include -#endif // __has_include() +#endif // CRYPTO_RAND_HAS_SYS_RANDOM_H #ifndef GRND_NONBLOCK #define GRND_NONBLOCK 0x01 #endif // GRND_NONBLOCK ssize_t -__SYS_getrandom(void *__buf, size_t __len, int __is_nonblock) +__getrandom(void *__buf, size_t __len, int __is_nonblock) { unsigned int flags = 0; if (__is_nonblock) { @@ -37,4 +41,4 @@ __SYS_getrandom(void *__buf, size_t __len, int __is_nonblock) } return syscall(SYS_getrandom, __buf, __len, flags); } -#endif // __CRYPTO_RAND_HAS_SYS_RANDOM__ +#endif // CRYPTO_RAND_HAS_SYS_GETRANDOM diff --git a/library/crypto-rand/src/nativeInterop/cinterop/crypto_rand_sys.h b/library/crypto-rand/src/nativeInterop/cinterop/crypto_rand_sys.h index 047b408..4ff0861 100644 --- a/library/crypto-rand/src/nativeInterop/cinterop/crypto_rand_sys.h +++ b/library/crypto-rand/src/nativeInterop/cinterop/crypto_rand_sys.h @@ -17,7 +17,11 @@ #ifndef CRYPTO_RAND_SYS_H #define CRYPTO_RAND_SYS_H -#ifdef __CRYPTO_RAND_HAS_SYS_RANDOM__ +#ifndef CRYPTO_RAND_HAS_SYS_GETRANDOM +#define CRYPTO_RAND_HAS_SYS_GETRANDOM 0 +#endif // CRYPTO_RAND_HAS_SYS_GETRANDOM + +#if CRYPTO_RAND_HAS_SYS_GETRANDOM #include /** @@ -25,7 +29,7 @@ * * If __is_nonblock > 0, will use flag GRND_NONBLOCK, otherwise will use 0. * */ -ssize_t __SYS_getrandom(void *__buf, size_t __len, int __is_nonblock); -#endif // __CRYPTO_RAND_HAS_SYS_RANDOM__ +ssize_t __getrandom(void *__buf, size_t __len, int __is_nonblock); +#endif // CRYPTO_RAND_HAS_SYS_GETRANDOM #endif // CRYPTO_RAND_SYS_H