Foundations(iOS Sourceset): Provide implementation for AESGCM. Duplicate AESGCM tests for iOS. Move dependency to 'cryptography-optimal' make use of all iOS crypto providers.

This commit is contained in:
KotlinGeekDev
2026-02-23 17:57:54 +01:00
parent 460be8cbf3
commit 265fef0a93
8 changed files with 159 additions and 14 deletions
+1 -1
View File
@@ -119,7 +119,7 @@ coil-gif = { group = "io.coil-kt.coil3", name = "coil-gif", version.ref = "coil"
coil-svg = { group = "io.coil-kt.coil3", name = "coil-svg", version.ref = "coil" }
coil-okhttp = { group = "io.coil-kt.coil3", name = "coil-network-okhttp", version.ref = "coil" }
com-ditchoom-buffer-compression = { module = "com.ditchoom:buffer-compression", version.ref = "comDitchoomBufferCompression" }
dev-whyoleg-cryptography-provider-apple = { module = "dev.whyoleg.cryptography:cryptography-provider-apple", version.ref = "devWhyolegCryptography" }
dev-whyoleg-cryptography-provider-apple-optimal = { module = "dev.whyoleg.cryptography:cryptography-provider-optimal", version.ref = "devWhyolegCryptography" }
drfonfon-geohash = { group = "com.github.drfonfon", name = "android-kotlin-geohash", version.ref = "androidKotlinGeohash" }
firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebaseBom" }
firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging" }
+1 -1
View File
@@ -230,7 +230,7 @@ kotlin {
implementation(libs.charlietap.cachemap)
implementation(libs.net.thauvin.erik.urlencoder.lib)
implementation(libs.com.ditchoom.buffer.compression)
implementation(libs.dev.whyoleg.cryptography.provider.apple)
implementation(libs.dev.whyoleg.cryptography.provider.apple.optimal)
}
}
@@ -20,23 +20,47 @@
*/
package com.vitorpamplona.quartz.utils.ciphers
import com.vitorpamplona.quartz.utils.Log
import dev.whyoleg.cryptography.CryptographyProvider
import dev.whyoleg.cryptography.DelicateCryptographyApi
import dev.whyoleg.cryptography.algorithms.AES
actual class AESGCM actual constructor(
actual val keyBytes: ByteArray,
actual val nonce: ByteArray,
) : NostrCipher {
actual override fun name(): String {
TODO("Not yet implemented")
private val provider = CryptographyProvider.Default
private val aesGcm = provider.get(AES.GCM)
private val keyDecoder =
aesGcm
.keyDecoder()
.decodeFromByteArrayBlocking(AES.Key.Format.RAW, keyBytes)
private fun cipher() = keyDecoder.cipher()
actual override fun name(): String = "aes-gcm"
@OptIn(DelicateCryptographyApi::class)
actual override fun encrypt(bytesToEncrypt: ByteArray): ByteArray =
with(cipher()) {
encryptWithIvBlocking(nonce, bytesToEncrypt)
}
actual override fun encrypt(bytesToEncrypt: ByteArray): ByteArray {
TODO("Not yet implemented")
@OptIn(DelicateCryptographyApi::class)
actual override fun decrypt(bytesToDecrypt: ByteArray): ByteArray =
with(cipher()) {
decryptWithIvBlocking(nonce, bytesToDecrypt)
}
actual override fun decrypt(bytesToDecrypt: ByteArray): ByteArray {
TODO("Not yet implemented")
@OptIn(DelicateCryptographyApi::class)
actual override fun decryptOrNull(bytesToDecrypt: ByteArray): ByteArray? =
try {
with(cipher()) {
decryptWithIvBlocking(nonce, bytesToDecrypt)
}
actual override fun decryptOrNull(bytesToDecrypt: ByteArray): ByteArray? {
TODO("Not yet implemented")
} catch (e: Exception) {
Log.w("AESGCM", "Failed to decrypt", e)
null
}
}
@@ -20,10 +20,15 @@
*/
package com.vitorpamplona.quartz
import dev.whyoleg.cryptography.CryptographyProviderApi
import dev.whyoleg.cryptography.providers.base.toByteArray
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.toKString
import kotlinx.io.files.FileNotFoundException
import platform.Foundation.NSData
import platform.Foundation.NSString
import platform.Foundation.NSUTF8StringEncoding
import platform.Foundation.dataWithContentsOfFile
import platform.Foundation.stringWithContentsOfFile
import platform.posix.getenv
@@ -34,4 +39,11 @@ actual class TestResourceLoader {
val filePath = "$resourceDir/$file"
return NSString.stringWithContentsOfFile(filePath, encoding = NSUTF8StringEncoding, error = null) as String
}
@OptIn(ExperimentalForeignApi::class, CryptographyProviderApi::class)
fun loadFileData(file: String): ByteArray {
val resourceDir = getenv("TEST_RESOURCES_ROOT")?.toKString()
val filePath = "$resourceDir/$file"
return NSData.dataWithContentsOfFile(filePath)?.toByteArray() ?: throw FileNotFoundException("Resource $file was not found.")
}
}
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2025 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip17Dm
import com.vitorpamplona.quartz.TestResourceLoader
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
import com.vitorpamplona.quartz.utils.ciphers.AESGCM
import kotlin.test.Test
import kotlin.test.assertEquals
class AESGCMTest {
val decryptionNonce = "01e77c94bd5aba3e3cbb69594e7ba07c"
val decryptionKey = "c128ecffab90ee7810e3df08e7fb2cc39a8d40f24201f48b2b36e23b34ac50ee"
val cipher =
AESGCM(
decryptionKey.hexToByteArray(),
decryptionNonce.encodeToByteArray(),
)
@Test
fun encryptDecrypt() {
val encrypted = cipher.encrypt("Testing".encodeToByteArray())
val decrypted = cipher.decrypt(encrypted)
assertEquals("Testing", decrypted.decodeToString())
}
@Test
fun imageTest() {
val image =
TestResourceLoader().loadFileData("ovxxk2vz.jpg")
val decrypted = cipher.decrypt(image)
assertEquals(44201, decrypted.size)
}
@Test
fun videoTest2() {
val myCipher =
AESGCM(
"373d19850ebc8ed5b0fefcca5cd6f27fde9cb6ac54fd32f6b4fad9d68ebe8ee0".hexToByteArray(),
"95e67b6874784a54299b58b8990499bd".hexToByteArray(),
)
val encrypted =
TestResourceLoader().loadFileData("trouble_video")
val decrypted = myCipher.decrypt(encrypted)
assertEquals(1277122, decrypted.size)
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2025 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip17Dm
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
import kotlinx.collections.immutable.persistentSetOf
import kotlin.test.Test
import kotlin.test.assertEquals
class ChatroomKeyTest {
@Test
fun testEquals() {
val k1 = ChatroomKey(persistentSetOf("Key1", "Key2"))
val k2 = ChatroomKey(persistentSetOf("Key1", "Key2"))
assertEquals(k1, k2)
assertEquals(k1.hashCode(), k2.hashCode())
}
}
Binary file not shown.
Binary file not shown.