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/.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 diff --git a/README.md b/README.md index 23c1d1f..f392dd7 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,10 @@ # mac-kt -`mac-kt` is a Kotlin Multiplatform library that provides common MAC implementations. +`mac-kt` is a Kotlin Multiplatform library that provides common [Message Authentication Codes](https://en.wikipedia.org/wiki/Message_authentication_code). -Supported MACs: +Supported algorithms: - HMAC -## Targets - -| Target | Status | -|------------------|---------------| -| JVM | Supported | -| JavaScript | Supported | -| Native (Linux) | Supported | -| Native (Windows) | Not Supported | - ## Installation Add the following to your `build.gradle.kts`. @@ -24,25 +15,20 @@ repositories { } dependencies { - implementation("com.infendro:mac:1.2.0") + implementation("com.infendro:mac:1.3.0") } ``` ## Usage ```kotlin -import com.infendro.hash.sha1.SHA1 -import com.infendro.mac.HMAC - fun main() { // create an HMAC instance using SHA1 val hmac = HMAC(SHA1) // generate MAC - val key = "secret" - .encodeToByteArray().asUByteArray() - val value = "Hello World!" - .encodeToByteArray().asUByteArray() + val key = "secret".encodeToByteArray() + val value = "Hello World!".encodeToByteArray() val mac = hmac.hash(key, value) } ``` diff --git a/build.gradle.kts b/build.gradle.kts index 89bb4ce..168c970 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) @@ -12,10 +15,14 @@ plugins { kotlin { jvm() - js { - nodejs() - } linuxX64() + linuxArm64() + mingwX64() + macosX64() + macosArm64() + js { nodejs() } + wasmJs { nodejs() } + wasmWasi { nodejs() } repositories { infendro() @@ -25,7 +32,7 @@ kotlin { sourceSets { commonMain.dependencies { implementation(libs.hash) - implementation(libs.bytearray) + implementation(libs.bytes) } commonTest.dependencies { implementation(libs.test) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4b5468d..627e5eb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,8 @@ [versions] -infendro = "1.0.0" -kotlin = "2.2.21" -hash = "1.3.2" -bytearray = "1.2.4" +infendro = "1.1.0" +kotlin = "2.3.0" +hash = "1.5.1" +bytes = "1.3.3" [plugins] infendro = { id = "com.infendro.plugin", version.ref = "infendro" } @@ -10,5 +10,5 @@ multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotl [libraries] hash = { module = "com.infendro:hash", version.ref = "hash" } -bytearray = { module = "com.infendro:bytearray", version.ref = "bytearray" } +bytes = { module = "com.infendro:bytes", version.ref = "bytes" } test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } diff --git a/src/commonMain/kotlin/com/infendro/mac/HMAC.kt b/src/commonMain/kotlin/com/infendro/mac/HMAC.kt index 6d28138..922eef1 100644 --- a/src/commonMain/kotlin/com/infendro/mac/HMAC.kt +++ b/src/commonMain/kotlin/com/infendro/mac/HMAC.kt @@ -1,6 +1,6 @@ package com.infendro.mac -import com.infendro.bytearray.padEnd +import com.infendro.bytes.bytearray.xorInto import com.infendro.hash.HashFunction class HMAC( @@ -9,25 +9,24 @@ class HMAC( override val bytes: Int get() = function.bytes - override fun hash( - key: UByteArray, - value: UByteArray, - ): UByteArray { + override fun hashInto(key: ByteArray, value: ByteArray, destination: ByteArray, destinationOffset: Int) { val paddedKey = pad(key) - - val innerKey = paddedKey.map { it xor 0x36U }.toUByteArray() - val outerKey = paddedKey.map { it xor 0x5CU }.toUByteArray() - - return function.hash(outerKey + function.hash(innerKey + value)) - } - - private fun pad( - key: UByteArray, - ): UByteArray { - return when { - key.size > function.block -> function.hash(key) - key.size < function.block -> key.padEnd(function.block, 0x00U) - else -> key + val inner = ByteArray(function.block + value.size).also { + paddedKey.xorInto(0x36, it) + value.copyInto(it, function.block) } + val outer = ByteArray(function.block + function.bytes).also { + paddedKey.xorInto(0x5C, it) + function.hashInto(inner, it, function.block) + } + function.hashInto(outer, destination, destinationOffset) } + + private fun pad(key: ByteArray): ByteArray = + ByteArray(function.block).also { + when { + key.size > function.block -> function.hashInto(key, it) + else -> key.copyInto(it) + } + } } diff --git a/src/commonMain/kotlin/com/infendro/mac/MAC.kt b/src/commonMain/kotlin/com/infendro/mac/MAC.kt index 686a46b..0069681 100644 --- a/src/commonMain/kotlin/com/infendro/mac/MAC.kt +++ b/src/commonMain/kotlin/com/infendro/mac/MAC.kt @@ -5,8 +5,20 @@ interface MAC { val bits: Int get() = bytes * 8 - fun hash( - key: UByteArray, - value: UByteArray, - ): UByteArray + fun hashInto(key: ByteArray, value: ByteArray, destination: ByteArray, destinationOffset: Int = 0) + + fun hashInto(key: ByteArray, value: ByteArray, destination: UByteArray, destinationOffset: Int = 0) = + hashInto(key, value, destination.asByteArray(), destinationOffset) + + fun hashInto(key: UByteArray, value: UByteArray, destination: ByteArray, destinationOffset: Int = 0) = + hashInto(key.asByteArray(), value.asByteArray(), destination, destinationOffset) + + fun hashInto(key: UByteArray, value: UByteArray, destination: UByteArray, destinationOffset: Int = 0) = + hashInto(key.asByteArray(), value.asByteArray(), destination.asByteArray(), destinationOffset) + + fun hash(key: ByteArray, value: ByteArray): ByteArray = + ByteArray(bytes).also { hashInto(key, value, it) } + + fun hash(key: UByteArray, value: UByteArray): UByteArray = + UByteArray(bytes).also { hashInto(key, value, it) } } diff --git a/src/commonTest/kotlin/com/infendro/mac/HMAC.kt b/src/commonTest/kotlin/com/infendro/mac/HMAC.kt index e47ab4b..2964ec3 100644 --- a/src/commonTest/kotlin/com/infendro/mac/HMAC.kt +++ b/src/commonTest/kotlin/com/infendro/mac/HMAC.kt @@ -1,6 +1,5 @@ package com.infendro.mac -import com.infendro.bytearray.encodeToUByteArray import com.infendro.hash.sha2.`SHA-256` import kotlin.test.Test import kotlin.test.assertContentEquals @@ -16,22 +15,21 @@ class `HMAC Test` { } @Test - fun `hash() - example`() { - assertContentEquals( - ubyteArrayOf( - 0xf7U, 0xbcU, 0x83U, 0xf4U, - 0x30U, 0x53U, 0x84U, 0x24U, - 0xb1U, 0x32U, 0x98U, 0xe6U, - 0xaaU, 0x6fU, 0xb1U, 0x43U, - 0xefU, 0x4dU, 0x59U, 0xa1U, - 0x49U, 0x46U, 0x17U, 0x59U, - 0x97U, 0x47U, 0x9dU, 0xbcU, - 0x2dU, 0x1aU, 0x3cU, 0xd8U, - ), - hmac.hash( - "key".encodeToUByteArray(), - "The quick brown fox jumps over the lazy dog".encodeToUByteArray() - ) + fun example() { + val expected = ubyteArrayOf( + 0xf7U, 0xbcU, 0x83U, 0xf4U, + 0x30U, 0x53U, 0x84U, 0x24U, + 0xb1U, 0x32U, 0x98U, 0xe6U, + 0xaaU, 0x6fU, 0xb1U, 0x43U, + 0xefU, 0x4dU, 0x59U, 0xa1U, + 0x49U, 0x46U, 0x17U, 0x59U, + 0x97U, 0x47U, 0x9dU, 0xbcU, + 0x2dU, 0x1aU, 0x3cU, 0xd8U, + ).asByteArray() + val actual = hmac.hash( + "key".encodeToByteArray(), + "The quick brown fox jumps over the lazy dog".encodeToByteArray(), ) + assertContentEquals(expected, actual) } }