From feecfa49b9d672d876b77415ad2a73d86cd52747 Mon Sep 17 00:00:00 2001 From: Infendro Date: Mon, 10 Nov 2025 18:52:19 +0100 Subject: [PATCH] refactor, implement LCG LCG ... Linear Congruential Generator --- build.gradle.kts | 4 ++ gradle/libs.versions.toml | 6 +-- .../kotlin/com/infendro/random/HMAC.kt | 53 ------------------- .../com/infendro/random/csprng/CSPRNG.kt | 16 ++++++ .../kotlin/com/infendro/random/csprng/HMAC.kt | 48 +++++++++++++++++ .../kotlin/com/infendro/random/prng/LCG.kt | 34 ++++++++++++ .../com/infendro/random/{ => prng}/PRNG.kt | 17 ++---- 7 files changed, 109 insertions(+), 69 deletions(-) delete mode 100644 src/commonMain/kotlin/com/infendro/random/HMAC.kt create mode 100644 src/commonMain/kotlin/com/infendro/random/csprng/CSPRNG.kt create mode 100644 src/commonMain/kotlin/com/infendro/random/csprng/HMAC.kt create mode 100644 src/commonMain/kotlin/com/infendro/random/prng/LCG.kt rename src/commonMain/kotlin/com/infendro/random/{ => prng}/PRNG.kt (58%) diff --git a/build.gradle.kts b/build.gradle.kts index bcea473..76c2f21 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -25,6 +25,10 @@ kotlin { implementation(libs.bytearray) } } + + compilerOptions { + freeCompilerArgs.add("-opt-in=kotlin.ExperimentalUnsignedTypes") + } } publishing { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 63399b2..3a0b1a1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,8 @@ [versions] kotlin = "2.1.20" -hash = "1.2.2" -mac = "1.1.2" -bytearray = "1.1.1" +hash = "1.3.2" +mac = "1.2.0" +bytearray = "1.2.2" [plugins] multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } diff --git a/src/commonMain/kotlin/com/infendro/random/HMAC.kt b/src/commonMain/kotlin/com/infendro/random/HMAC.kt deleted file mode 100644 index c7333af..0000000 --- a/src/commonMain/kotlin/com/infendro/random/HMAC.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.infendro.random - -import com.infendro.bytearray.addAll -import com.infendro.hash.HashFunction - -class HMAC internal constructor( - private val function: HashFunction, -) : PRNG() { - private val random = Default - private val hmac = com.infendro.mac.HMAC(function) - private lateinit var K: ByteArray - private lateinit var V: ByteArray - - init { - val bytes = random.nextBytes(function.bytes) - seed(bytes) - } - - 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( - count: Int, - ): ByteArray { - val bytes = buildList { - while (size < count) { - V = hmac.hash(K, V) - addAll(V) - } - } - - K = hmac.hash(K, V + byteArrayOf(0x00)) - V = hmac.hash(K, V) - - return bytes - .take(count) - .toByteArray() - } -} diff --git a/src/commonMain/kotlin/com/infendro/random/csprng/CSPRNG.kt b/src/commonMain/kotlin/com/infendro/random/csprng/CSPRNG.kt new file mode 100644 index 0000000..89118f5 --- /dev/null +++ b/src/commonMain/kotlin/com/infendro/random/csprng/CSPRNG.kt @@ -0,0 +1,16 @@ +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.kt b/src/commonMain/kotlin/com/infendro/random/csprng/HMAC.kt new file mode 100644 index 0000000..66cd4ec --- /dev/null +++ b/src/commonMain/kotlin/com/infendro/random/csprng/HMAC.kt @@ -0,0 +1,48 @@ +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 new file mode 100644 index 0000000..d3bbe82 --- /dev/null +++ b/src/commonMain/kotlin/com/infendro/random/prng/LCG.kt @@ -0,0 +1,34 @@ +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 + +class LCG internal constructor() : PRNG() { + private val a: ULong = 25214903917UL + private val c: ULong = 11UL + private val m: ULong = 1UL shl 48 + + private var x: ULong by Delegates.notNull() + + override fun seed( + seed: UByteArray, + ) { + val s = seed.padStart(8, 0x00U).takeLast(8).toUByteArray() + x = s.toULong() % m + } + + 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() + } +} diff --git a/src/commonMain/kotlin/com/infendro/random/PRNG.kt b/src/commonMain/kotlin/com/infendro/random/prng/PRNG.kt similarity index 58% rename from src/commonMain/kotlin/com/infendro/random/PRNG.kt rename to src/commonMain/kotlin/com/infendro/random/prng/PRNG.kt index 7729ef0..751aa33 100644 --- a/src/commonMain/kotlin/com/infendro/random/PRNG.kt +++ b/src/commonMain/kotlin/com/infendro/random/prng/PRNG.kt @@ -1,21 +1,16 @@ -package com.infendro.random +package com.infendro.random.prng import com.infendro.bytearray.toInt -import com.infendro.hash.HashFunction import kotlin.random.Random abstract class PRNG : Random() { abstract fun seed( - seed: ByteArray, - ) - - abstract fun reseed( - entropy: ByteArray, + seed: UByteArray, ) abstract fun generate( count: Int, - ): ByteArray + ): UByteArray override fun nextBits( bitCount: Int, @@ -27,10 +22,6 @@ abstract class PRNG : Random() { } companion object { - fun HMAC( - function: HashFunction, - ): HMAC { - return com.infendro.random.HMAC(function) - } + fun LCG() = com.infendro.random.prng.LCG() } }