From 517b58a3df0dd6fdcc1d8d6e9a03bed2a4e87cb8 Mon Sep 17 00:00:00 2001 From: Infendro Date: Sun, 14 Dec 2025 14:41:30 +0100 Subject: [PATCH 1/7] update README small rewording, link wikipedia article --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f4eb733..6bdbe95 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # prng-kt -`prng-kt` is a Kotlin Multiplatform library that provides common pseudorandom number generators. +`prng-kt` is a Kotlin Multiplatform library that provides common [Pseudorandom Number Generators](https://en.wikipedia.org/wiki/Pseudorandom_number_generator). -Supported PRNGs: +Supported [PRNG](https://en.wikipedia.org/wiki/Pseudorandom_number_generator) algorithms: - LCG -Supported CSPRNGs: +Supported [CSPRNG](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator) algorithms: - HMAC DRBG ## Targets From 7144c21074165b5a95c42a59f1a536dffbbf9dbf Mon Sep 17 00:00:00 2001 From: Infendro Date: Tue, 13 Jan 2026 18:07:23 +0100 Subject: [PATCH 2/7] improve ci --- .gitea/workflows/ci.yaml | 92 ++++++++++++++++++++++++++++++++++++++ .gitea/workflows/main.yaml | 36 --------------- build.gradle.kts | 2 +- 3 files changed, 93 insertions(+), 37 deletions(-) create mode 100644 .gitea/workflows/ci.yaml delete mode 100644 .gitea/workflows/main.yaml diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml new file mode 100644 index 0000000..2f46684 --- /dev/null +++ b/.gitea/workflows/ci.yaml @@ -0,0 +1,92 @@ +on: + push: + branches: [ develop, main ] + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: linux + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Java + uses: actions/setup-java@v4 + with: + java-version: '24' + distribution: 'temurin' + + - name: Cache Gradle + uses: actions/cache@v4 + with: + key: gradle-${{ hashFiles('**/libs.versions.toml') }} + restore-keys: gradle- + path: | + ~/.gradle/caches/ + ~/.gradle/wrapper/ + + - name: Cache Konan + uses: actions/cache@v4 + with: + key: konan-${{ hashFiles('**/libs.versions.toml') }} + restore-keys: konan- + path: | + ~/.konan/ + + - name: Cache Node + uses: actions/cache@v4 + with: + key: node-${{ hashFiles('**/libs.versions.toml') }} + restore-keys: node- + path: | + ~/.gradle/nodejs + ~/.gradle/yarn + + - name: Test + run: ./gradlew allTests + + publish: + if: ${{ github.event_name == 'push' && github.ref_name == 'main' }} + needs: test + runs-on: linux + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Java + uses: actions/setup-java@v4 + with: + java-version: '24' + distribution: 'temurin' + + - name: Cache Gradle + uses: actions/cache@v4 + with: + key: gradle-${{ hashFiles('**/libs.versions.toml') }} + restore-keys: gradle- + path: | + ~/.gradle/caches/ + ~/.gradle/wrapper/ + + - name: Cache Konan + uses: actions/cache@v4 + with: + key: konan-${{ hashFiles('**/libs.versions.toml') }} + restore-keys: konan- + path: | + ~/.konan/ + + - name: Cache Node + uses: actions/cache@v4 + with: + key: node-${{ hashFiles('**/libs.versions.toml') }} + restore-keys: node- + path: | + ~/.gradle/nodejs + ~/.gradle/yarn + + - name: Publish + run: ./gradlew publish + env: + INFENDRO__TOKEN: ${{ secrets.TOKEN }} diff --git a/.gitea/workflows/main.yaml b/.gitea/workflows/main.yaml deleted file mode 100644 index 4885477..0000000 --- a/.gitea/workflows/main.yaml +++ /dev/null @@ -1,36 +0,0 @@ -on: - push: - branches: - - main - -env: - INFENDRO__TOKEN: ${{ secrets.TOKEN }} - -jobs: - publish: - runs-on: linux - steps: - - uses: actions/checkout@v4 - - - uses: actions/cache/restore@v4 - with: - key: gradle - path: | - ~/.gradle/caches/ - ~/.gradle/wrapper/ - ~/.konan/ - - - uses: actions/setup-java@v4 - with: - java-version: '24' - distribution: 'temurin' - - - run: ./gradlew publish - - - uses: actions/cache/save@v4 - with: - key: gradle - path: | - ~/.gradle/caches/ - ~/.gradle/wrapper/ - ~/.konan/ diff --git a/build.gradle.kts b/build.gradle.kts index e66f47a..424333e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,10 +12,10 @@ plugins { kotlin { jvm() + linuxX64() js { nodejs() } - linuxX64() repositories { infendro() From 0f890c923cd47d7bcf073655426011ce5004a9e2 Mon Sep 17 00:00:00 2001 From: Infendro Date: Fri, 30 Jan 2026 13:41:43 +0100 Subject: [PATCH 3/7] implement optimizations --- README.md | 31 ++++-------- build.gradle.kts | 21 +++++---- gradle/libs.versions.toml | 14 +++--- .../kotlin/com/infendro/random/CSPRNG.kt | 5 ++ .../kotlin/com/infendro/random/PRNG.kt | 16 +++++++ .../com/infendro/random/csprng/CSPRNG.kt | 16 ------- .../com/infendro/random/csprng/HMAC-DRBG.kt | 37 +++++++++++++++ .../kotlin/com/infendro/random/csprng/HMAC.kt | 47 ------------------- .../kotlin/com/infendro/random/prng/LCG.kt | 36 +++++--------- .../kotlin/com/infendro/random/prng/PRNG.kt | 44 ----------------- 10 files changed, 99 insertions(+), 168 deletions(-) create mode 100644 src/commonMain/kotlin/com/infendro/random/CSPRNG.kt create mode 100644 src/commonMain/kotlin/com/infendro/random/PRNG.kt delete mode 100644 src/commonMain/kotlin/com/infendro/random/csprng/CSPRNG.kt create mode 100644 src/commonMain/kotlin/com/infendro/random/csprng/HMAC-DRBG.kt delete mode 100644 src/commonMain/kotlin/com/infendro/random/csprng/HMAC.kt delete mode 100644 src/commonMain/kotlin/com/infendro/random/prng/PRNG.kt diff --git a/README.md b/README.md index 6bdbe95..07b96d9 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,7 @@ Supported [PRNG](https://en.wikipedia.org/wiki/Pseudorandom_number_generator) al - LCG Supported [CSPRNG](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator) algorithms: -- HMAC DRBG - -## Targets - -| Target | Status | -|------------------|---------------| -| JVM | Supported | -| JavaScript | Supported | -| Native (Linux) | Supported | -| Native (Windows) | Not Supported | +- HMAC-DRBG ## Installation @@ -27,34 +18,30 @@ repositories { } dependencies { - implementation("com.infendro:prng:1.2.0") + implementation("com.infendro:prng:1.3.0") } ``` ## Usage ```kotlin -import com.infendro.hash.sha2.`SHA-256` -import com.infendro.random.csprng.CSPRNG - fun main() { - // create a PRNG instance + // create PRNG instance val rng = CSPRNG.HMAC(`SHA-256`) - // seed the PRNG to receive predictable results - rng.seed(/* some seed */) + // seed PRNG to receive deterministic numbers + rng.seed(/* seed */) val a = rng.nextInt() val b = rng.nextInt() val c = rng.nextInt() - // reseed the PRNG to add entropy to the following results - rng.reseed(/* some random entropy */) + // reseed PRNG to add additional entropy + rng.reseed(/* entropy */) val d = rng.nextInt() val e = rng.nextInt() val f = rng.nextInt() - // each run: - // a, b, and c will be predictable - // d, e, and f will be random (assuming the entropy is also random) + // a, b, and c will be deterministic + // d, e, and f will be random (assuming entropy is random) } ``` diff --git a/build.gradle.kts b/build.gradle.kts index 424333e..48871bb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,8 +1,11 @@ +@file:OptIn(ExperimentalWasmDsl::class) + import com.infendro.plugin.infendro import com.infendro.plugin.token +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl group = "com.infendro" -version = "1.2.0" +version = "1.3.0" plugins { alias(libs.plugins.infendro) @@ -13,9 +16,13 @@ plugins { kotlin { jvm() linuxX64() - js { - nodejs() - } + linuxArm64() + mingwX64() + macosX64() + macosArm64() + js { nodejs() } + wasmJs { nodejs() } + wasmWasi { nodejs() } repositories { infendro() @@ -26,13 +33,9 @@ kotlin { commonMain.dependencies { implementation(libs.hash) implementation(libs.mac) - implementation(libs.bytearray) + implementation(libs.bytes) } } - - compilerOptions { - freeCompilerArgs.add("-opt-in=kotlin.ExperimentalUnsignedTypes") - } } publishing.repositories { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f087be6..14aa349 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,15 +1,15 @@ [versions] -infendro = "1.0.0" -kotlin = "2.2.21" -hash = "1.3.2" -mac = "1.2.0" -bytearray = "1.2.4" +infendro = "1.1.0" +kotlin = "2.3.0" +mac = "1.3.0" +hash = "1.5.1" +bytes = "1.3.3" [plugins] infendro = { id = "com.infendro.plugin", version.ref = "infendro" } multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } [libraries] -hash = { module = "com.infendro:hash", version.ref = "hash" } mac = { module = "com.infendro:mac", version.ref = "mac" } -bytearray = { module = "com.infendro:bytearray", version.ref = "bytearray" } +hash = { module = "com.infendro:hash", version.ref = "hash" } +bytes = { module = "com.infendro:bytes", version.ref = "bytes" } diff --git a/src/commonMain/kotlin/com/infendro/random/CSPRNG.kt b/src/commonMain/kotlin/com/infendro/random/CSPRNG.kt new file mode 100644 index 0000000..70d68b0 --- /dev/null +++ b/src/commonMain/kotlin/com/infendro/random/CSPRNG.kt @@ -0,0 +1,5 @@ +package com.infendro.random + +abstract class CSPRNG : PRNG() { + abstract fun reseed(entropy: ByteArray) +} diff --git a/src/commonMain/kotlin/com/infendro/random/PRNG.kt b/src/commonMain/kotlin/com/infendro/random/PRNG.kt new file mode 100644 index 0000000..48fe811 --- /dev/null +++ b/src/commonMain/kotlin/com/infendro/random/PRNG.kt @@ -0,0 +1,16 @@ +package com.infendro.random + +import kotlin.random.Random + +abstract class PRNG : Random() { + abstract fun seed(seed: ByteArray) + + protected abstract fun generate(): Int + + override fun nextBits(bitCount: Int): Int { + require(bitCount in 0..32) + + if (bitCount == 0) return 0 + return generate() ushr (32 - bitCount) + } +} diff --git a/src/commonMain/kotlin/com/infendro/random/csprng/CSPRNG.kt b/src/commonMain/kotlin/com/infendro/random/csprng/CSPRNG.kt deleted file mode 100644 index 89118f5..0000000 --- a/src/commonMain/kotlin/com/infendro/random/csprng/CSPRNG.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.infendro.random.csprng - -import com.infendro.hash.HashFunction -import com.infendro.random.prng.PRNG - -abstract class CSPRNG : PRNG() { - abstract fun reseed( - entropy: UByteArray, - ) - - companion object { - fun HMAC( - function: HashFunction, - ) = com.infendro.random.csprng.HMAC(function) - } -} diff --git a/src/commonMain/kotlin/com/infendro/random/csprng/HMAC-DRBG.kt b/src/commonMain/kotlin/com/infendro/random/csprng/HMAC-DRBG.kt new file mode 100644 index 0000000..485a6e1 --- /dev/null +++ b/src/commonMain/kotlin/com/infendro/random/csprng/HMAC-DRBG.kt @@ -0,0 +1,37 @@ +package com.infendro.random.csprng + +import com.infendro.bytes.bytearray.toInt +import com.infendro.hash.HashFunction +import com.infendro.mac.HMAC +import com.infendro.random.CSPRNG +import kotlin.properties.Delegates.notNull + +class `HMAC-DRBG` internal constructor( + private val function: HashFunction, +) : CSPRNG() { + private val hmac = HMAC(function) + + private var k: ByteArray by notNull() + private var v: ByteArray by notNull() + + override fun seed(seed: ByteArray) { + k = ByteArray(function.bytes) { 0x00 } + v = ByteArray(function.bytes) { 0x01 } + reseed(seed) + } + + override fun reseed(entropy: ByteArray) { + k = hmac.hash(k, v + byteArrayOf(0x00) + entropy) + v = hmac.hash(k, v) + k = hmac.hash(k, v + byteArrayOf(0x01) + entropy) + v = hmac.hash(k, v) + } + + override fun generate(): Int { + v = hmac.hash(k, v) + val result = v.toInt() + k = hmac.hash(k, v + byteArrayOf(0x00)) + v = hmac.hash(k, v) + return result + } +} diff --git a/src/commonMain/kotlin/com/infendro/random/csprng/HMAC.kt b/src/commonMain/kotlin/com/infendro/random/csprng/HMAC.kt deleted file mode 100644 index 58b496b..0000000 --- a/src/commonMain/kotlin/com/infendro/random/csprng/HMAC.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.infendro.random.csprng - -import com.infendro.hash.HashFunction -import kotlin.math.min -import kotlin.properties.Delegates.notNull - -class HMAC internal constructor( - private val function: HashFunction, -) : CSPRNG() { - private val hmac = com.infendro.mac.HMAC(function) - - private var k: UByteArray by notNull() - private var v: UByteArray by notNull() - - override fun seed( - seed: UByteArray, - ) { - k = UByteArray(function.bytes) { 0x00U } - v = UByteArray(function.bytes) { 0x01U } - reseed(seed) - } - - override fun reseed( - entropy: UByteArray, - ) { - k = hmac.hash(k, v + ubyteArrayOf(0x00U) + entropy) - v = hmac.hash(k, v) - k = hmac.hash(k, v + ubyteArrayOf(0x01U) + entropy) - v = hmac.hash(k, v) - } - - override fun generate( - count: Int, - ): UByteArray { - val bytes = buildList { - while (size < count) { - v = hmac.hash(k, v) - addAll(v.take(min(count - size, function.bytes))) - } - }.toUByteArray() - - k = hmac.hash(k, v + ubyteArrayOf(0x00U)) - v = hmac.hash(k, v) - - return bytes - } -} diff --git a/src/commonMain/kotlin/com/infendro/random/prng/LCG.kt b/src/commonMain/kotlin/com/infendro/random/prng/LCG.kt index 9e72a73..64e1bef 100644 --- a/src/commonMain/kotlin/com/infendro/random/prng/LCG.kt +++ b/src/commonMain/kotlin/com/infendro/random/prng/LCG.kt @@ -1,33 +1,23 @@ package com.infendro.random.prng -import com.infendro.bytearray.padStart -import com.infendro.bytearray.toUByteArray -import com.infendro.bytearray.toULong -import kotlin.math.min -import kotlin.properties.Delegates +import com.infendro.bytes.bytearray.toLong +import com.infendro.hash.sha2.`SHA-256` +import com.infendro.random.PRNG +import kotlin.properties.Delegates.notNull class LCG internal constructor() : PRNG() { - private val a: ULong = 25214903917UL - private val c: ULong = 11UL - private val m: ULong = 1UL shl 48 + private val a: Long = 25214903917L + private val c: Long = 11L + private val mask: Long = (1L shl 48) - 1 - private var x: ULong by Delegates.notNull() + private var x: Long by notNull() - override fun seed( - seed: UByteArray, - ) { - val s = seed.padStart(8, 0x00U).takeLast(8).toUByteArray() - x = s.toULong() % m + override fun seed(seed: ByteArray) { + x = `SHA-256`.hash(seed).toLong() and mask } - override fun generate( - count: Int, - ): UByteArray { - return buildList { - while (size < count) { - x = (a * x + c) % m - addAll(x.toUByteArray().takeLast(min(count - size, 6))) - } - }.toUByteArray() + override fun generate(): Int { + x = (a * x + c) and mask + return (x ushr 16).toInt() } } diff --git a/src/commonMain/kotlin/com/infendro/random/prng/PRNG.kt b/src/commonMain/kotlin/com/infendro/random/prng/PRNG.kt deleted file mode 100644 index cc720f0..0000000 --- a/src/commonMain/kotlin/com/infendro/random/prng/PRNG.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.infendro.random.prng - -import com.infendro.bytearray.toInt -import kotlin.random.Random - -abstract class PRNG : Random() { - abstract fun seed( - seed: UByteArray, - ) - - abstract fun generate( - count: Int, - ): UByteArray - - override fun nextBits( - bitCount: Int, - ): Int { - require(bitCount in 0..32) - - if (bitCount == 0) return 0 - return generate(4).toInt() ushr (32 - bitCount) - } - - override fun nextBytes( - array: ByteArray, - fromIndex: Int, - toIndex: Int, - ): ByteArray { - require(fromIndex in 0..array.size && toIndex in 0..array.size) - require(fromIndex <= toIndex) - - val size = toIndex - fromIndex - val bytes = generate(size).asByteArray() - for (i in 0.. Date: Fri, 30 Jan 2026 13:43:33 +0100 Subject: [PATCH 4/7] split ci --- .gitea/workflows/ci.yaml | 92 ----------------------------------- .gitea/workflows/publish.yaml | 47 ++++++++++++++++++ .gitea/workflows/test.yaml | 28 +++++++++++ 3 files changed, 75 insertions(+), 92 deletions(-) delete mode 100644 .gitea/workflows/ci.yaml create mode 100644 .gitea/workflows/publish.yaml create mode 100644 .gitea/workflows/test.yaml diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml deleted file mode 100644 index 2f46684..0000000 --- a/.gitea/workflows/ci.yaml +++ /dev/null @@ -1,92 +0,0 @@ -on: - push: - branches: [ develop, main ] - pull_request: - branches: [ main ] - -jobs: - test: - runs-on: linux - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Java - uses: actions/setup-java@v4 - with: - java-version: '24' - distribution: 'temurin' - - - name: Cache Gradle - uses: actions/cache@v4 - with: - key: gradle-${{ hashFiles('**/libs.versions.toml') }} - restore-keys: gradle- - path: | - ~/.gradle/caches/ - ~/.gradle/wrapper/ - - - name: Cache Konan - uses: actions/cache@v4 - with: - key: konan-${{ hashFiles('**/libs.versions.toml') }} - restore-keys: konan- - path: | - ~/.konan/ - - - name: Cache Node - uses: actions/cache@v4 - with: - key: node-${{ hashFiles('**/libs.versions.toml') }} - restore-keys: node- - path: | - ~/.gradle/nodejs - ~/.gradle/yarn - - - name: Test - run: ./gradlew allTests - - publish: - if: ${{ github.event_name == 'push' && github.ref_name == 'main' }} - needs: test - runs-on: linux - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Java - uses: actions/setup-java@v4 - with: - java-version: '24' - distribution: 'temurin' - - - name: Cache Gradle - uses: actions/cache@v4 - with: - key: gradle-${{ hashFiles('**/libs.versions.toml') }} - restore-keys: gradle- - path: | - ~/.gradle/caches/ - ~/.gradle/wrapper/ - - - name: Cache Konan - uses: actions/cache@v4 - with: - key: konan-${{ hashFiles('**/libs.versions.toml') }} - restore-keys: konan- - path: | - ~/.konan/ - - - name: Cache Node - uses: actions/cache@v4 - with: - key: node-${{ hashFiles('**/libs.versions.toml') }} - restore-keys: node- - path: | - ~/.gradle/nodejs - ~/.gradle/yarn - - - name: Publish - run: ./gradlew publish - env: - INFENDRO__TOKEN: ${{ secrets.TOKEN }} diff --git a/.gitea/workflows/publish.yaml b/.gitea/workflows/publish.yaml new file mode 100644 index 0000000..dad1306 --- /dev/null +++ b/.gitea/workflows/publish.yaml @@ -0,0 +1,47 @@ +on: + push: + branches: [ main ] + +jobs: + publish: + runs-on: linux + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Java + uses: actions/setup-java@v4 + with: + java-version: '24' + distribution: 'temurin' + + - name: Cache Gradle + uses: actions/cache@v4 + with: + key: gradle-${{ hashFiles('**/libs.versions.toml') }} + restore-keys: gradle + path: | + ~/.gradle/caches/ + ~/.gradle/wrapper/ + + - name: Cache Konan + uses: actions/cache@v4 + with: + key: konan-${{ hashFiles('**/libs.versions.toml') }} + restore-keys: konan + path: | + ~/.konan/ + + - name: Cache Node + uses: actions/cache@v4 + with: + key: node-${{ hashFiles('**/libs.versions.toml') }} + restore-keys: node + path: | + ~/.gradle/nodejs + ~/.gradle/yarn + + - name: Publish + run: ./gradlew publish + env: + INFENDRO__TOKEN: ${{ secrets.TOKEN }} diff --git a/.gitea/workflows/test.yaml b/.gitea/workflows/test.yaml new file mode 100644 index 0000000..bce9e35 --- /dev/null +++ b/.gitea/workflows/test.yaml @@ -0,0 +1,28 @@ +on: + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: linux + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Java + uses: actions/setup-java@v4 + with: + java-version: '24' + distribution: 'temurin' + + - name: Cache Gradle + uses: actions/cache@v4 + with: + key: gradle-${{ hashFiles('**/libs.versions.toml') }} + restore-keys: gradle + path: | + ~/.gradle/caches/ + ~/.gradle/wrapper/ + + - name: Test + run: ./gradlew jvmTest From 2c6fe0a7d965f1a7d493915ea91fafc6d434024c Mon Sep 17 00:00:00 2001 From: Infendro Date: Fri, 30 Jan 2026 15:03:28 +0100 Subject: [PATCH 5/7] further optimize HMAC-DRBG --- .../com/infendro/random/csprng/HMAC-DRBG.kt | 29 ++++++++++++++----- .../kotlin/com/infendro/random/prng/LCG.kt | 2 +- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/commonMain/kotlin/com/infendro/random/csprng/HMAC-DRBG.kt b/src/commonMain/kotlin/com/infendro/random/csprng/HMAC-DRBG.kt index 485a6e1..02d0bb5 100644 --- a/src/commonMain/kotlin/com/infendro/random/csprng/HMAC-DRBG.kt +++ b/src/commonMain/kotlin/com/infendro/random/csprng/HMAC-DRBG.kt @@ -6,7 +6,7 @@ import com.infendro.mac.HMAC import com.infendro.random.CSPRNG import kotlin.properties.Delegates.notNull -class `HMAC-DRBG` internal constructor( +class `HMAC-DRBG`( private val function: HashFunction, ) : CSPRNG() { private val hmac = HMAC(function) @@ -14,6 +14,8 @@ class `HMAC-DRBG` internal constructor( private var k: ByteArray by notNull() private var v: ByteArray by notNull() + private val buffer = ByteArray(function.bytes + 1) + override fun seed(seed: ByteArray) { k = ByteArray(function.bytes) { 0x00 } v = ByteArray(function.bytes) { 0x01 } @@ -21,17 +23,28 @@ class `HMAC-DRBG` internal constructor( } override fun reseed(entropy: ByteArray) { - k = hmac.hash(k, v + byteArrayOf(0x00) + entropy) - v = hmac.hash(k, v) - k = hmac.hash(k, v + byteArrayOf(0x01) + entropy) - v = hmac.hash(k, v) + val value = ByteArray(v.size + 1 + entropy.size) + + v.copyInto(value) + value[v.size] = 0x00 + entropy.copyInto(value, v.size + 1) + hmac.hashInto(k, value, k) + hmac.hashInto(k, v, v) + + value[v.size] = 0x01 + hmac.hashInto(k, value, k) + hmac.hashInto(k, v, v) } override fun generate(): Int { - v = hmac.hash(k, v) + hmac.hashInto(k, v, v) val result = v.toInt() - k = hmac.hash(k, v + byteArrayOf(0x00)) - v = hmac.hash(k, v) + + v.copyInto(buffer) + buffer[v.size] = 0x00 + hmac.hashInto(k, buffer, k) + hmac.hashInto(k, v, v) + return result } } diff --git a/src/commonMain/kotlin/com/infendro/random/prng/LCG.kt b/src/commonMain/kotlin/com/infendro/random/prng/LCG.kt index 64e1bef..085e622 100644 --- a/src/commonMain/kotlin/com/infendro/random/prng/LCG.kt +++ b/src/commonMain/kotlin/com/infendro/random/prng/LCG.kt @@ -5,7 +5,7 @@ import com.infendro.hash.sha2.`SHA-256` import com.infendro.random.PRNG import kotlin.properties.Delegates.notNull -class LCG internal constructor() : PRNG() { +class LCG : PRNG() { private val a: Long = 25214903917L private val c: Long = 11L private val mask: Long = (1L shl 48) - 1 From 2ae6fda70edb6fbfcd64ad56f77ba4bf8bbf7949 Mon Sep 17 00:00:00 2001 From: Infendro Date: Fri, 30 Jan 2026 15:16:48 +0100 Subject: [PATCH 6/7] implement xoshiro256++ and xoshiro256** --- .../com/infendro/random/prng/Xoshiro256pp.kt | 28 +++++++++++++++++++ .../com/infendro/random/prng/Xoshiro256ss.kt | 28 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 src/commonMain/kotlin/com/infendro/random/prng/Xoshiro256pp.kt create mode 100644 src/commonMain/kotlin/com/infendro/random/prng/Xoshiro256ss.kt diff --git a/src/commonMain/kotlin/com/infendro/random/prng/Xoshiro256pp.kt b/src/commonMain/kotlin/com/infendro/random/prng/Xoshiro256pp.kt new file mode 100644 index 0000000..ff60ce8 --- /dev/null +++ b/src/commonMain/kotlin/com/infendro/random/prng/Xoshiro256pp.kt @@ -0,0 +1,28 @@ +package com.infendro.random.prng + +import com.infendro.bytes.bytearray.toLongArray +import com.infendro.hash.sha2.`SHA-256` +import com.infendro.random.PRNG +import kotlin.properties.Delegates.notNull + +class Xoshiro256pp : PRNG() { + private var s: LongArray by notNull() + + override fun seed(seed: ByteArray) { + s = `SHA-256`.hash(seed).toLongArray() + } + + override fun generate(): Int { + val result = (s[0] + s[3]).rotateLeft(23) + s[0] + + val t = s[1] shl 17 + s[2] = s[2] xor s[0] + s[3] = s[3] xor s[1] + s[1] = s[1] xor s[2] + s[0] = s[0] xor s[3] + s[2] = s[2] xor t + s[3] = s[3].rotateLeft(45) + + return (result ushr 32).toInt() + } +} diff --git a/src/commonMain/kotlin/com/infendro/random/prng/Xoshiro256ss.kt b/src/commonMain/kotlin/com/infendro/random/prng/Xoshiro256ss.kt new file mode 100644 index 0000000..6fbfb1a --- /dev/null +++ b/src/commonMain/kotlin/com/infendro/random/prng/Xoshiro256ss.kt @@ -0,0 +1,28 @@ +package com.infendro.random.prng + +import com.infendro.bytes.bytearray.toLongArray +import com.infendro.hash.sha2.`SHA-256` +import com.infendro.random.PRNG +import kotlin.properties.Delegates.notNull + +class Xoshiro256ss : PRNG() { + private var s: LongArray by notNull() + + override fun seed(seed: ByteArray) { + s = `SHA-256`.hash(seed).toLongArray() + } + + override fun generate(): Int { + val result = (s[1] * 5).rotateLeft(7) * 9 + + val t = s[1] shl 17 + s[2] = s[2] xor s[0] + s[3] = s[3] xor s[1] + s[1] = s[1] xor s[2] + s[0] = s[0] xor s[3] + s[2] = s[2] xor t + s[3] = s[3].rotateLeft(45) + + return (result ushr 32).toInt() + } +} From 2aed836e67613d5594c1b1f944ea4df91ed38ef8 Mon Sep 17 00:00:00 2001 From: Infendro Date: Thu, 19 Feb 2026 12:46:41 +0100 Subject: [PATCH 7/7] Upgrade dependencies --- README.md | 2 ++ build.gradle.kts | 2 +- gradle/libs.versions.toml | 10 +++++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 07b96d9..e693b49 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ Supported [PRNG](https://en.wikipedia.org/wiki/Pseudorandom_number_generator) algorithms: - LCG +- Xoshiro256++ +- Xoshiro256** Supported [CSPRNG](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator) algorithms: - HMAC-DRBG diff --git a/build.gradle.kts b/build.gradle.kts index 48871bb..267fc8f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,9 +31,9 @@ kotlin { sourceSets { commonMain.dependencies { + implementation(libs.bytes) implementation(libs.hash) implementation(libs.mac) - implementation(libs.bytes) } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 14aa349..4215199 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,15 +1,15 @@ [versions] infendro = "1.1.0" kotlin = "2.3.0" -mac = "1.3.0" -hash = "1.5.1" -bytes = "1.3.3" +bytes = "1.4.0" +hash = "1.6.0" +mac = "1.4.0" [plugins] infendro = { id = "com.infendro.plugin", version.ref = "infendro" } multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } [libraries] -mac = { module = "com.infendro:mac", version.ref = "mac" } -hash = { module = "com.infendro:hash", version.ref = "hash" } bytes = { module = "com.infendro:bytes", version.ref = "bytes" } +hash = { module = "com.infendro:hash", version.ref = "hash" } +mac = { module = "com.infendro:mac", version.ref = "mac" }