>()
15 | val instance = block(error)
16 | error.value?.let(onError)
17 | instance
18 | }
19 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/util/NSError.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.util
2 |
3 | import platform.Foundation.NSError
4 | import kotlin.native.internal.ObjCErrorException
5 |
6 | internal fun NSError.asThrowable(): Throwable {
7 | val message = buildString {
8 | append(domain ?: "NSError")
9 | append(" ")
10 | append("code [$code]")
11 | userInfo.forEach {
12 | appendLine("{${it.key} = ${it.value}}")
13 | }
14 | }
15 | return Throwable(
16 | message = message
17 | )
18 | }
19 |
20 | val Throwable.isNSError: Boolean
21 | get() = this is ObjCErrorException
22 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/util/NSData.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.util
2 |
3 | import kotlinx.cinterop.convert
4 | import kotlinx.cinterop.usePinned
5 | import platform.Foundation.NSData
6 | import platform.Foundation.dataWithBytes
7 | import platform.posix.memcpy
8 |
9 | internal inline fun ByteArray.toNSData(): NSData = usePinned {
10 | NSData.dataWithBytes(
11 | bytes = it.startAddressOf,
12 | length = size.convert()
13 | )
14 | }
15 |
16 | internal inline fun NSData.toByteArray(): ByteArray = ByteArray(length.toInt()).apply {
17 | usePinned {
18 | memcpy(it.startAddressOf, bytes, length)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | PODS_ROOT = ./Framework
2 | STUB_ROOT = ./TinkStub
3 |
4 | .PHONY: clean
5 | clean:
6 | @rm -rf $(PODS_ROOT)/Tink.xcframework
7 | @rm -rf "$(STUB_ROOT)/objc/bazel-*"
8 |
9 | .PHONY: bootstrap-submodule
10 | bootstrap-submodule:
11 | @git submodule init
12 | @git submodule update
13 |
14 | .PHONY: build-bazel
15 | build-bazel: bootstrap-submodule
16 | @cd $(STUB_ROOT)/objc && bazelisk --output_base=./../../build/bazel build --disk_cache=./../../build/bazel --apple_platform_type=ios --xcode_version="$(cat ./../.xcode-version)" //:Tink && cd ../..
17 |
18 | .PHONY: archive
19 | archive: build-bazel
20 | @unzip -o $(STUB_ROOT)/objc/bazel-bin/Tink.xcframework.zip -d $(PODS_ROOT)
21 |
--------------------------------------------------------------------------------
/tink/src/androidMain/kotlin/io/github/ryunen344/tink/signature/AndroidPublicKeySign.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.signature
2 |
3 | import io.github.ryunen344.tink.exception.GeneralSecurityException
4 |
5 | internal typealias NativePublicKeySign = com.google.crypto.tink.PublicKeySign
6 |
7 | class AndroidPublicKeySign(private val native: NativePublicKeySign) : PublicKeySign, NativePublicKeySign by native {
8 | constructor(handle: com.google.crypto.tink.KeysetHandle) :
9 | this(handle.getPrimitive(NativePublicKeySign::class.java))
10 |
11 | @Throws(GeneralSecurityException::class)
12 | override fun sign(data: ByteArray): ByteArray =
13 | native.sign(data)
14 | }
15 |
--------------------------------------------------------------------------------
/.github/release.yml:
--------------------------------------------------------------------------------
1 | # https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes#example-configuration
2 | changelog:
3 | exclude:
4 | labels:
5 | - ignore-for-release
6 | authors:
7 | - octocat
8 | categories:
9 | - title: ⚠️ Breaking Changes
10 | labels:
11 | - breaking-change
12 | - title: 🎉 Exciting New Features
13 | labels:
14 | - enhancement
15 | - title: 🐛 Bug fixes
16 | labels:
17 | - bug
18 | - title: 🌲 Dependency updates
19 | labels:
20 | - dependencies
21 | - dependency
22 | - title: Other Changes
23 | labels:
24 | - "*"
25 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/mac/MacConfig.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.mac
2 |
3 | import com.google.crypto.tink.TINKConfig
4 | import com.google.crypto.tink.TINKMacConfig
5 | import io.github.ryunen344.tink.exception.GeneralSecurityException
6 | import io.github.ryunen344.tink.util.asThrowable
7 | import io.github.ryunen344.tink.util.memScopedInstance
8 | import kotlinx.cinterop.ptr
9 |
10 | @Throws(GeneralSecurityException::class)
11 | actual fun MacConfig.Companion.register(): Unit =
12 | memScopedInstance(
13 | block = { TINKConfig.registerConfig(TINKMacConfig(it.ptr), it.ptr) },
14 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) }
15 | )
16 |
--------------------------------------------------------------------------------
/tink/src/androidMain/kotlin/io/github/ryunen344/tink/hybrid/AndroidHybridEncrypt.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.hybrid
2 |
3 | import io.github.ryunen344.tink.exception.GeneralSecurityException
4 |
5 | internal typealias NativeHybridEncrypt = com.google.crypto.tink.HybridEncrypt
6 |
7 | class AndroidHybridEncrypt(private val native: NativeHybridEncrypt) : HybridEncrypt, NativeHybridEncrypt by native {
8 | constructor(handle: com.google.crypto.tink.KeysetHandle) :
9 | this(handle.getPrimitive(NativeHybridEncrypt::class.java))
10 |
11 | @Throws(GeneralSecurityException::class)
12 | override fun encrypt(plaintext: ByteArray, contextInfo: ByteArray): ByteArray =
13 | native.encrypt(plaintext, contextInfo)
14 | }
15 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/aead/AeadConfig.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.aead
2 |
3 | import com.google.crypto.tink.TINKAeadConfig
4 | import com.google.crypto.tink.TINKConfig
5 | import io.github.ryunen344.tink.exception.GeneralSecurityException
6 | import io.github.ryunen344.tink.util.asThrowable
7 | import io.github.ryunen344.tink.util.memScopedInstance
8 | import kotlinx.cinterop.ptr
9 |
10 | @Throws(GeneralSecurityException::class)
11 | actual fun AeadConfig.Companion.register(): Unit =
12 | memScopedInstance(
13 | block = { TINKConfig.registerConfig(config = TINKAeadConfig(it.ptr), error = it.ptr) },
14 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) }
15 | )
16 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/config/TinkConfig.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.config
2 |
3 | import com.google.crypto.tink.TINKAllConfig
4 | import com.google.crypto.tink.TINKConfig
5 | import io.github.ryunen344.tink.exception.GeneralSecurityException
6 | import io.github.ryunen344.tink.util.asThrowable
7 | import io.github.ryunen344.tink.util.memScopedInstance
8 | import kotlinx.cinterop.ptr
9 |
10 | @Throws(GeneralSecurityException::class)
11 | actual fun TinkConfig.Companion.register(): Unit =
12 | memScopedInstance(
13 | block = { TINKConfig.registerConfig(config = TINKAllConfig(it.ptr), error = it.ptr) },
14 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) }
15 | )
16 |
--------------------------------------------------------------------------------
/tink/src/androidMain/kotlin/io/github/ryunen344/tink/hybrid/AndroidHybridDecrypt.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.hybrid
2 |
3 | import io.github.ryunen344.tink.exception.GeneralSecurityException
4 |
5 | internal typealias NativeHybridDecrypt = com.google.crypto.tink.HybridDecrypt
6 |
7 | class AndroidHybridDecrypt(private val native: NativeHybridDecrypt) : HybridDecrypt, NativeHybridDecrypt by native {
8 | constructor(handle: com.google.crypto.tink.KeysetHandle) :
9 | this(handle.getPrimitive(NativeHybridDecrypt::class.java))
10 |
11 | @Throws(GeneralSecurityException::class)
12 | override fun decrypt(ciphertext: ByteArray, contextInfo: ByteArray): ByteArray =
13 | native.decrypt(ciphertext, contextInfo)
14 | }
15 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/hybrid/HybridConfig.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.hybrid
2 |
3 | import com.google.crypto.tink.TINKConfig
4 | import com.google.crypto.tink.TINKHybridConfig
5 | import io.github.ryunen344.tink.exception.GeneralSecurityException
6 | import io.github.ryunen344.tink.util.asThrowable
7 | import io.github.ryunen344.tink.util.memScopedInstance
8 | import kotlinx.cinterop.ptr
9 |
10 | @Throws(GeneralSecurityException::class)
11 | actual fun HybridConfig.Companion.register(): Unit =
12 | memScopedInstance(
13 | block = { TINKConfig.registerConfig(config = TINKHybridConfig(it.ptr), error = it.ptr) },
14 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) }
15 | )
16 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/BinaryKeysetReader.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink
2 |
3 | import com.google.crypto.tink.TINKBinaryKeysetReader
4 | import io.github.ryunen344.tink.exception.IOException
5 | import io.github.ryunen344.tink.util.asThrowable
6 | import io.github.ryunen344.tink.util.memScopedInstance
7 | import io.github.ryunen344.tink.util.toNSData
8 | import kotlinx.cinterop.ptr
9 |
10 | actual class BinaryKeysetReader actual constructor(bytes: ByteArray) :
11 | KeysetReader(
12 | native = memScopedInstance(
13 | block = { TINKBinaryKeysetReader(serializedKeyset = bytes.toNSData(), error = it.ptr) },
14 | onError = { throw IOException(cause = it.asThrowable()) }
15 | )
16 | )
17 |
--------------------------------------------------------------------------------
/tink/src/androidMain/kotlin/io/github/ryunen344/tink/signature/AndroidPublicKeyVerify.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.signature
2 |
3 | import io.github.ryunen344.tink.exception.GeneralSecurityException
4 |
5 | internal typealias NativePublicKeyVerify = com.google.crypto.tink.PublicKeyVerify
6 |
7 | class AndroidPublicKeyVerify(
8 | private val native: NativePublicKeyVerify,
9 | ) : PublicKeyVerify, NativePublicKeyVerify by native {
10 |
11 | constructor(handle: com.google.crypto.tink.KeysetHandle) :
12 | this(handle.getPrimitive(NativePublicKeyVerify::class.java))
13 |
14 | @Throws(GeneralSecurityException::class)
15 | override fun verify(signature: ByteArray, data: ByteArray) =
16 | native.verify(signature, data)
17 | }
18 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/signature/SignatureConfig.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.signature
2 |
3 | import com.google.crypto.tink.TINKConfig
4 | import com.google.crypto.tink.TINKSignatureConfig
5 | import io.github.ryunen344.tink.exception.GeneralSecurityException
6 | import io.github.ryunen344.tink.util.asThrowable
7 | import io.github.ryunen344.tink.util.memScopedInstance
8 | import kotlinx.cinterop.ptr
9 |
10 | @Throws(GeneralSecurityException::class)
11 | actual fun SignatureConfig.Companion.register(): Unit =
12 | memScopedInstance(
13 | block = { TINKConfig.registerConfig(config = TINKSignatureConfig(it.ptr), error = it.ptr) },
14 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) }
15 | )
16 |
--------------------------------------------------------------------------------
/tink/src/androidMain/kotlin/io/github/ryunen344/tink/mac/AndroidMac.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.mac
2 |
3 | import io.github.ryunen344.tink.exception.GeneralSecurityException
4 |
5 | internal typealias NativeMac = com.google.crypto.tink.Mac
6 |
7 | class AndroidMac(private val native: NativeMac) : Mac, NativeMac by native {
8 | constructor(handle: com.google.crypto.tink.KeysetHandle) :
9 | this(handle.getPrimitive(NativeMac::class.java))
10 |
11 | @Throws(GeneralSecurityException::class)
12 | override fun computeMac(data: ByteArray): ByteArray =
13 | native.computeMac(data)
14 |
15 | @Throws(GeneralSecurityException::class)
16 | override fun verifyMac(mac: ByteArray, data: ByteArray) =
17 | native.verifyMac(mac, data)
18 | }
19 |
--------------------------------------------------------------------------------
/tink/src/commonTest/kotlin/io/github/ryunen344/tink/KeyTemplateTest.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink
2 |
3 | import io.github.ryunen344.tink.daead.DeterministicAeadConfig
4 | import io.github.ryunen344.tink.daead.register
5 | import io.github.ryunen344.tink.hybrid.HybridConfig
6 | import io.github.ryunen344.tink.hybrid.register
7 | import io.github.ryunen344.tink.signature.SignatureConfig
8 | import io.github.ryunen344.tink.signature.register
9 | import kotlin.test.Test
10 |
11 | class KeyTemplateTest {
12 | @Test
13 | fun test_KeyTemplateSet() {
14 | HybridConfig.register()
15 | DeterministicAeadConfig.register()
16 | SignatureConfig.register()
17 | KeyTemplateSet.values().map(KeyTemplateSet::template).forEach(::println)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/daead/DeterministicAeadConfig.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.daead
2 |
3 | import com.google.crypto.tink.TINKConfig
4 | import com.google.crypto.tink.TINKDeterministicAeadConfig
5 | import io.github.ryunen344.tink.exception.GeneralSecurityException
6 | import io.github.ryunen344.tink.util.asThrowable
7 | import io.github.ryunen344.tink.util.memScopedInstance
8 | import kotlinx.cinterop.ptr
9 |
10 | @Throws(GeneralSecurityException::class)
11 | actual fun DeterministicAeadConfig.Companion.register(): Unit =
12 | memScopedInstance(
13 | block = { TINKConfig.registerConfig(config = TINKDeterministicAeadConfig(it.ptr), error = it.ptr) },
14 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) }
15 | )
16 |
--------------------------------------------------------------------------------
/tink/src/commonMain/kotlin/io/github/ryunen344/tink/KeysetHandle.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink
2 |
3 | import io.github.ryunen344.tink.exception.GeneralSecurityException
4 | import io.github.ryunen344.tink.exception.IOException
5 | import kotlin.reflect.KClass
6 |
7 | expect class KeysetHandle
8 |
9 | @Throws(GeneralSecurityException::class, IOException::class)
10 | expect fun KeysetHandle.writeNoSecret(writer: KeysetWriter)
11 |
12 | @Throws(GeneralSecurityException::class, IOException::class)
13 | expect fun KeysetHandle.writeCleartext(writer: KeysetWriter)
14 |
15 | @Throws(GeneralSecurityException::class)
16 | expect fun KeysetHandle.getPrimitive(kClass: KClass
): P
17 |
18 | @Throws(GeneralSecurityException::class)
19 | expect fun KeysetHandle.publicKeysetHandle(): KeysetHandle
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # general
2 | .DS_Store
3 | .AppleDouble
4 | .LSOverride
5 |
6 | # intellij
7 | *.iml
8 | /.idea/.gitignore
9 | /.idea/.name
10 | /.idea/caches
11 | /.idea/libraries
12 | /.idea/modules.xml
13 | /.idea/workspace.xml
14 | /.idea/navEditor.xml
15 | /.idea/assetWizardSettings.xml
16 | /.idea/compiler.xml
17 | /.idea/git_toolbox_prj.xml
18 | /.idea/gradle.xml
19 | /.idea/kotlinc.xml
20 | /.idea/misc.xml
21 | /.idea/vcs.xml
22 | /.idea/shelf/
23 | /.idea/jarRepositories.xml
24 | /.idea/markdown.xml
25 | /.idea/deploymentTargetDropDown.xml
26 | /.idea/androidTestResultsUserPreferences.xml
27 |
28 | # gradle
29 | .gradle
30 | /local.properties
31 |
32 | # xcode
33 | xcuserdata/
34 | DerivedData/
35 |
36 | # bundler
37 | .bundle/vendor/
38 |
39 | # Tink
40 | Framework/Pods
41 | Framework/Tink.xcframework
42 |
43 | # build
44 | build/
45 | .build/
46 | releases/
47 |
--------------------------------------------------------------------------------
/tink/src/androidMain/kotlin/io/github/ryunen344/tink/aead/AndroidAead.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.aead
2 |
3 | import io.github.ryunen344.tink.exception.GeneralSecurityException
4 |
5 | internal typealias NativeAead = com.google.crypto.tink.Aead
6 |
7 | class AndroidAead(private val native: NativeAead) : Aead, NativeAead by native {
8 | constructor(handle: com.google.crypto.tink.KeysetHandle) :
9 | this(handle.getPrimitive(NativeAead::class.java))
10 |
11 | @Throws(GeneralSecurityException::class)
12 | override fun encrypt(plaintext: ByteArray, associatedData: ByteArray): ByteArray =
13 | native.encrypt(plaintext, associatedData)
14 |
15 | @Throws(GeneralSecurityException::class)
16 | override fun decrypt(ciphertext: ByteArray, associatedData: ByteArray): ByteArray =
17 | native.decrypt(ciphertext, associatedData)
18 | }
19 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | #Gradle
2 | org.gradle.jvmargs=-Xmx4096M -XX:+UseParallelGC -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx4096M"
3 | org.gradle.parallel=true
4 | org.gradle.caching=true
5 |
6 | #Kotlin
7 | kotlin.code.style=official
8 |
9 | #Android
10 | android.useAndroidX=true
11 | android.enableJetifier=false
12 | android.nonTransitiveRClass=true
13 | android.defaults.buildfeatures.aidl=false
14 | android.defaults.buildfeatures.buildconfig=true
15 | android.defaults.buildfeatures.databinding"=false
16 | android.defaults.buildfeatures.renderscript=false
17 | android.defaults.buildfeatures.resvalues=false
18 | android.defaults.buildfeatures.shaders=false
19 | android.defaults.buildfeatures.viewbinding=false
20 | android.library.defaults.buildfeatures.androidresources=false
21 |
22 | #MPP
23 | kotlin.mpp.enableCInteropCommonization=true
24 | kotlin.native.cacheKind=none
25 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/JsonKeysetReader.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink
2 |
3 | import com.google.crypto.tink.TINKJSONKeysetReader
4 | import io.github.ryunen344.tink.exception.JsonException
5 | import io.github.ryunen344.tink.util.asThrowable
6 | import io.github.ryunen344.tink.util.memScopedInstance
7 | import io.github.ryunen344.tink.util.toNSData
8 | import kotlinx.cinterop.ptr
9 |
10 | actual class JsonKeysetReader
11 | @Throws(JsonException::class)
12 | actual constructor(bytes: ByteArray) : KeysetReader(
13 | native = memScopedInstance(
14 | block = { TINKJSONKeysetReader(serializedKeyset = bytes.toNSData(), error = it.ptr) },
15 | onError = { throw JsonException(cause = it.asThrowable()) }
16 | )
17 | ) {
18 | @Throws(JsonException::class)
19 | actual constructor(json: String) : this(json.encodeToByteArray())
20 | }
21 |
--------------------------------------------------------------------------------
/tink/src/commonMain/kotlin/io/github/ryunen344/tink/KeysetHandleGenerator.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink
2 |
3 | import io.github.ryunen344.tink.aead.Aead
4 | import io.github.ryunen344.tink.exception.GeneralSecurityException
5 |
6 | class KeysetHandleGenerator {
7 | companion object
8 | }
9 |
10 | @Throws(GeneralSecurityException::class)
11 | expect fun KeysetHandleGenerator.Companion.generateNew(keyTemplate: KeyTemplate): KeysetHandle
12 |
13 | @Throws(GeneralSecurityException::class)
14 | expect fun KeysetHandleGenerator.Companion.read(reader: KeysetReader, aead: Aead): KeysetHandle
15 |
16 | @Throws(GeneralSecurityException::class)
17 | expect fun KeysetHandleGenerator.Companion.readClearText(reader: KeysetReader): KeysetHandle
18 |
19 | @Throws(GeneralSecurityException::class)
20 | expect fun KeysetHandleGenerator.Companion.readNoSecret(keyset: ByteArray): KeysetHandle
21 |
--------------------------------------------------------------------------------
/iosApp/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("org.openbakery.xcode-plugin")
3 | }
4 |
5 | xcodebuild {
6 | version = File(rootDir, ".xcode-version").readText().trim()
7 | scheme = "iosApp"
8 | target = "iosAppTests"
9 | setProjectFile("iosApp.xcodeproj")
10 | destination(
11 | closureOf {
12 | platform = "iOS Simulator"
13 | name = "iPhone 14 Pro"
14 | os = "16.4"
15 | }
16 | )
17 |
18 | // xcode-pluginがSYMROOTをいじるためCONFIGURATION_BUILD_DIRをSYMROOTと揃えることで帳尻を合わせる
19 | additionalParameters = listOf(
20 | "CONFIGURATION_BUILD_DIR=\$SYMROOT",
21 | )
22 | }
23 |
24 | tasks.withType().configureEach {
25 | dependsOn(":tink:assembleTinKMMDebugXCFramework")
26 | }
27 |
28 | tasks.withType().configureEach {
29 | dependsOn(":tink:assembleTinKMMDebugXCFramework")
30 | }
31 |
--------------------------------------------------------------------------------
/tink/src/androidMain/kotlin/io/github/ryunen344/tink/daead/AndroidDeterministicAead.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.daead
2 |
3 | import io.github.ryunen344.tink.exception.GeneralSecurityException
4 |
5 | internal typealias NativeDeterministicAead = com.google.crypto.tink.DeterministicAead
6 |
7 | class AndroidDeterministicAead(
8 | private val native: NativeDeterministicAead,
9 | ) : DeterministicAead, NativeDeterministicAead by native {
10 |
11 | constructor(handle: com.google.crypto.tink.KeysetHandle) :
12 | this(handle.getPrimitive(NativeDeterministicAead::class.java))
13 |
14 | @Throws(GeneralSecurityException::class)
15 | override fun encryptDeterministically(plaintext: ByteArray, associatedData: ByteArray): ByteArray =
16 | native.encryptDeterministically(plaintext, associatedData)
17 |
18 | @Throws(GeneralSecurityException::class)
19 | override fun decryptDeterministically(ciphertext: ByteArray, associatedData: ByteArray): ByteArray =
20 | native.decryptDeterministically(ciphertext, associatedData)
21 | }
22 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/TinkPrimitive.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink
2 |
3 | import io.github.ryunen344.tink.aead.Aead
4 | import io.github.ryunen344.tink.daead.DeterministicAead
5 | import io.github.ryunen344.tink.hybrid.HybridDecrypt
6 | import io.github.ryunen344.tink.hybrid.HybridEncrypt
7 | import io.github.ryunen344.tink.mac.Mac
8 | import io.github.ryunen344.tink.signature.PublicKeySign
9 | import io.github.ryunen344.tink.signature.PublicKeyVerify
10 | import kotlin.reflect.KClass
11 |
12 | val aead: KClass
13 | get() = Aead::class
14 |
15 | val deterministicAead: KClass
16 | get() = DeterministicAead::class
17 |
18 | val hybridDecrypt: KClass
19 | get() = HybridDecrypt::class
20 |
21 | val hybridEncrypt: KClass
22 | get() = HybridEncrypt::class
23 |
24 | val mac: KClass
25 | get() = Mac::class
26 |
27 | val publicKeySign: KClass
28 | get() = PublicKeySign::class
29 |
30 | val publicKeyVerify: KClass
31 | get() = PublicKeyVerify::class
32 |
--------------------------------------------------------------------------------
/tink/src/androidMain/kotlin/io/github/ryunen344/tink/KeysetHandleGenerator.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink
2 |
3 | import com.google.crypto.tink.CleartextKeysetHandle
4 | import io.github.ryunen344.tink.aead.Aead
5 | import io.github.ryunen344.tink.aead.NativeAead
6 | import io.github.ryunen344.tink.exception.GeneralSecurityException
7 |
8 | @Throws(GeneralSecurityException::class)
9 | actual fun KeysetHandleGenerator.Companion.generateNew(keyTemplate: KeyTemplate): KeysetHandle =
10 | KeysetHandle.generateNew(keyTemplate)
11 |
12 | @Throws(GeneralSecurityException::class)
13 | actual fun KeysetHandleGenerator.Companion.read(
14 | reader: KeysetReader,
15 | aead: Aead,
16 | ): KeysetHandle = KeysetHandle.read(reader, aead as NativeAead)
17 |
18 | @Throws(GeneralSecurityException::class)
19 | actual fun KeysetHandleGenerator.Companion.readClearText(reader: KeysetReader): KeysetHandle =
20 | CleartextKeysetHandle.read(reader)
21 |
22 | @Throws(GeneralSecurityException::class)
23 | actual fun KeysetHandleGenerator.Companion.readNoSecret(keyset: ByteArray): KeysetHandle =
24 | KeysetHandle.readNoSecret(keyset)
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 RyuNen344
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/scripts/utilities:
--------------------------------------------------------------------------------
1 | readonly _ESCAPE_="$(printf '\033')"
2 | readonly _WARNING_="$(printf '\U26A0')"
3 | readonly _CHECK_MARK_="$(printf '\U2705')"
4 | readonly _TADA_="$(printf '\U1F389')"
5 | readonly _REPO_ROOT_ABS_PATH_="$(git rev-parse --show-toplevel)"
6 |
7 | checking_command() {
8 | local -r cmd="$1"
9 |
10 | sleep 1
11 |
12 | if type "$cmd" >/dev/null 2>&1; then
13 | info "$_CHECK_MARK_ ${_ESCAPE_}[32m$cmd${_ESCAPE_}[m is found"
14 | return 0
15 | elif type "$cmd.bat" >/dev/null 2>&1; then
16 | info "$_CHECK_MARK_ ${_ESCAPE_}[32m$cmd${_ESCAPE_}[m is found"
17 | return 0
18 | else
19 | warn "$_WARNING_ ${_ESCAPE_}[33m$cmd${_ESCAPE_}[m is not found"
20 | return 1
21 | fi
22 | }
23 |
24 | fatal() {
25 | err "$@"
26 | exit 1
27 | }
28 |
29 | err() {
30 | echo "${_ESCAPE_}[31m[ERROR]${_ESCAPE_}[m $@" 1>&2
31 | }
32 |
33 | warn() {
34 | echo "${_ESCAPE_}[33m[WARN]${_ESCAPE_}[m $@" 1>&2
35 | }
36 |
37 | info() {
38 | echo "${_ESCAPE_}[32m[INFO]${_ESCAPE_}[m $@" 1>&2
39 | }
40 |
41 | run() {
42 | echo "Executing... $@" 1>&2
43 | "$@"
44 | }
45 |
46 | go_to_repo_root() {
47 | cd "$_REPO_ROOT_ABS_PATH_"
48 | }
49 |
--------------------------------------------------------------------------------
/iosApp/iosAppTests/iosAppTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | final class iosAppTests: XCTestCase {
4 |
5 | override func setUpWithError() throws {
6 | // Put setup code here. This method is called before the invocation of each test method in the class.
7 | }
8 |
9 | override func tearDownWithError() throws {
10 | // Put teardown code here. This method is called after the invocation of each test method in the class.
11 | }
12 |
13 | func testExample() throws {
14 | // This is an example of a functional test case.
15 | // Use XCTAssert and related functions to verify your tests produce the correct results.
16 | // Any test you write for XCTest can be annotated as throws and async.
17 | // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
18 | // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
19 | }
20 |
21 | func testPerformanceExample() throws {
22 | // This is an example of a performance test case.
23 | measure {
24 | // Put the code you want to measure the time of here.
25 | }
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/.github/actions/sdkman/action.yml:
--------------------------------------------------------------------------------
1 | name: "install sdkman"
2 | description: "setup sdkman and install via .sdkmanrc"
3 | runs:
4 | using: "composite"
5 | steps:
6 | - uses: actions/cache/restore@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4
7 | id: cache-sdkman
8 | with:
9 | path: |
10 | ~/.sdkman
11 | key: v1-sdkman-${{ runner.os }}-${{ hashFiles('.sdkmanrc') }}
12 |
13 | - if: ${{ steps.cache-sdkman.outputs.cache-hit != 'true' }}
14 | name: install sdkman
15 | shell: bash
16 | run: |
17 | export SDKMAN_DIR="$HOME/.sdkman" && curl -s "https://get.sdkman.io" | bash
18 |
19 | - name: setup sdkman
20 | shell: bash
21 | run: |
22 | source "$HOME/.sdkman/bin/sdkman-init.sh"
23 | sdkman_auto_answer=true
24 | sdkman_selfupdate_enable=false
25 | sdk env install
26 | echo "JAVA_HOME=$HOME/.sdkman/candidates/java/current" >> $GITHUB_ENV
27 |
28 | - if: ${{ steps.cache-sdkman.outputs.cache-hit != 'true' }}
29 | uses: actions/cache/save@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4
30 | with:
31 | path: |
32 | ~/.sdkman
33 | key: v1-sdkman-${{ runner.os }}-${{ hashFiles('.sdkmanrc') }}
34 |
--------------------------------------------------------------------------------
/renovate.json5:
--------------------------------------------------------------------------------
1 | {
2 | $schema: "https://docs.renovatebot.com/renovate-schema.json",
3 | extends: [
4 | "config:base",
5 | // https://github.com/whitesource/merge-confidence
6 | "github>whitesource/merge-confidence:beta"
7 | ],
8 | "labels": [
9 | "dependencies"
10 | ],
11 | // https://docs.renovatebot.com/configuration-options/#automergestrategy
12 | "automergeStrategy": "squash",
13 | packageRules: [
14 | {
15 | "groupName": "android gradle plugin",
16 | "matchPackageNames": [
17 | "com.android.application",
18 | "com.android.library",
19 | "com.android.settings",
20 | "com.android.tools.build:gradle"
21 | ],
22 | },
23 | {
24 | "groupName": "detekt",
25 | "matchPackagePatterns": [
26 | "^io\\.gitlab\\.arturbosch.detekt"
27 | ],
28 | },
29 | {
30 | groupName: "gh-actions",
31 | matchManagers: [
32 | "github-actions"
33 | ],
34 | matchDatasources: [
35 | "github-tags"
36 | ],
37 | matchUpdateTypes: [
38 | "minor",
39 | "patch"
40 | ],
41 | automerge: true,
42 | platformAutomerge: true,
43 | pinDigests: true,
44 | },
45 | ],
46 | }
47 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
2 | pluginManagement {
3 | resolutionStrategy {
4 | eachPlugin {
5 | if (requested.id.id == "org.openbakery.xcode-plugin") {
6 | useModule("org.openbakery:xcode-plugin:0.23.1.30")
7 | }
8 | }
9 | }
10 | repositories {
11 | google()
12 | mavenCentral()
13 | maven(url = "https://openbakery.org/repository/") {
14 | content {
15 | includeGroup("org.openbakery")
16 | }
17 | }
18 | gradlePluginPortal()
19 | }
20 | }
21 |
22 | dependencyResolutionManagement {
23 | // https://youtrack.jetbrains.com/issue/KT-51379
24 | repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
25 | repositories {
26 | google()
27 | mavenCentral()
28 | }
29 | }
30 |
31 | plugins {
32 | id("com.gradle.enterprise").version("3.18.1")
33 | }
34 |
35 | gradleEnterprise {
36 | if (System.getenv("CI") != null) {
37 | buildScan {
38 | termsOfServiceUrl = "https://gradle.com/terms-of-service"
39 | termsOfServiceAgree = "yes"
40 | }
41 | }
42 | }
43 |
44 | rootProject.name = "Tink-KMM"
45 | include(":iosApp")
46 | include(":tink")
47 |
--------------------------------------------------------------------------------
/tink/src/androidUnitTest/kotlin/io/github/ryunen344/tink/KeysetReaderTest.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink
2 |
3 | import io.github.ryunen344.tink.aead.AeadConfig
4 | import io.github.ryunen344.tink.aead.register
5 | import io.github.ryunen344.tink.signature.SignatureConfig
6 | import io.github.ryunen344.tink.signature.register
7 | import kotlin.test.Test
8 |
9 | class KeysetReaderTest {
10 | @Test
11 | fun test_read() {
12 | runCatching {
13 | AeadConfig.register()
14 | val handle = KeysetHandleGenerator.generateNew(KeyTemplateSet.AES256_GCM.template())
15 | val writer = JsonKeysetWriter()
16 | handle.writeCleartext(writer)
17 | println(writer.write().decodeToString())
18 | }.onFailure {
19 | it.printStackTrace()
20 | }
21 | }
22 |
23 | @Test
24 | fun test_signature() {
25 | SignatureConfig.register()
26 |
27 | val privateHandle = KeysetHandleGenerator.generateNew(KeyTemplateSet.ECDSA_P256.template())
28 | val publicHandle = privateHandle.publicKeysetHandle()
29 |
30 | JsonKeysetWriter().run {
31 | privateHandle.writeCleartext(this)
32 | println(write().decodeToString())
33 | }
34 |
35 | JsonKeysetWriter().run {
36 | publicHandle.writeNoSecret(this)
37 | println(write().decodeToString())
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/scripts/doctor:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 |
4 | . "$(git rev-parse --show-toplevel)/scripts/utilities"
5 |
6 | go_to_repo_root
7 |
8 | declare -a missing_required_components=()
9 | info 'check command...'
10 |
11 | if ! checking_command 'java'; then
12 | missing_required_components+=('java')
13 | warn "Please install JDK and export it to PATH"
14 | fi
15 |
16 | # until release Tink 1.7.0 at CocoaPods
17 | if ! checking_command 'bazel'; then
18 | missing_required_components+=('bazel')
19 | warn "Please install bazel and export it to PATH"
20 | fi
21 |
22 | if ! checking_command 'ruby'; then
23 | missing_required_components+=('ruby')
24 | warn "Please install ruby"
25 | fi
26 |
27 | if ! checking_command 'bundler'; then
28 | missing_required_components+=('bundler')
29 | warn "Please gem install bundler"
30 | fi
31 |
32 | if ! checking_command 'go'; then
33 | missing_required_components+=('go')
34 | warn "Please install go"
35 | fi
36 |
37 | if ! checking_command 'reviewdog'; then
38 | missing_required_components+=('reviewdog')
39 | warn "Please go install github.com/reviewdog/reviewdog/cmd/reviewdog@latest"
40 | fi
41 |
42 | sleep 1
43 |
44 | if ((0 < ${#missing_required_components[@]})); then
45 | err '---------'
46 | err "${missing_required_components[*]} are required. Please install them to complete setup."
47 | fatal '---------'
48 | fi
49 |
50 | info "Found all required commands. ${_TADA_}"
51 |
--------------------------------------------------------------------------------
/Framework/Makefile:
--------------------------------------------------------------------------------
1 | PROJECT_ROOT = $(cd $(dirname $0); cd ..; pwd)
2 | PODS_ROOT = ./Pods
3 | PODS_PROJECT = $(PODS_ROOT)/Pods.xcodeproj
4 | SYMROOT = $(PODS_ROOT)/Build
5 | IPHONEOS_DEPLOYMENT_TARGET = 9.0
6 |
7 | .PHONY: clean
8 | clean:
9 | @rm -rf Pods
10 | @rm -rf Tink.xcframework
11 |
12 | .PHONY: bootstrap-cocoapods
13 | bootstrap-cocoapods: clean
14 | @bundle install
15 | @bundle exec pod install
16 |
17 | .PHONY: build-cocoapods
18 | build-cocoapods: bootstrap-cocoapods
19 | @xcodebuild -project "$(PODS_PROJECT)" \
20 | -sdk iphoneos \
21 | -configuration Release \
22 | -alltargets \
23 | ONLY_ACTIVE_ARCH=NO \
24 | ENABLE_TESTABILITY=NO \
25 | SYMROOT="$(SYMROOT)" \
26 | CLANG_ENABLE_MODULE_DEBUGGING=NO \
27 | BITCODE_GENERATION_MODE=bitcode \
28 | IPHONEOS_DEPLOYMENT_TARGET="$(IPHONEOS_DEPLOYMENT_TARGET)" | bundle exec xcpretty -c
29 | @xcodebuild -project "$(PODS_PROJECT)" \
30 | -sdk iphonesimulator \
31 | -configuration Release \
32 | -alltargets \
33 | ONLY_ACTIVE_ARCH=NO \
34 | ENABLE_TESTABILITY=NO \
35 | SYMROOT="$(SYMROOT)" \
36 | CLANG_ENABLE_MODULE_DEBUGGING=NO \
37 | BITCODE_GENERATION_MODE=bitcode \
38 | IPHONEOS_DEPLOYMENT_TARGET="$(IPHONEOS_DEPLOYMENT_TARGET)" | bundle exec xcpretty -c
39 |
40 | .PHONY: archive
41 | archive: build-cocoapods
42 | @xcodebuild -create-xcframework \
43 | -framework Pods/Pods/Build/Release-iphonesimulator/Tink/Tink.framework \
44 | -framework Pods/Pods/Build/Release-iphoneos/Tink/Tink.framework \
45 | -output Tink.xcframework | bundle exec xcpretty -c
46 |
47 | .PHONY: check
48 | check:
49 | xcrun lipo -info Pods/Tink/Frameworks/Tink.framework/Tink
50 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/signature/DarwinPublicKeyVerify.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.signature
2 |
3 | import com.google.crypto.tink.TINKKeysetHandle
4 | import com.google.crypto.tink.TINKPublicKeyVerifyFactory
5 | import com.google.crypto.tink.TINKPublicKeyVerifyProtocol
6 | import io.github.ryunen344.tink.exception.GeneralSecurityException
7 | import io.github.ryunen344.tink.util.asThrowable
8 | import io.github.ryunen344.tink.util.memScopedInstance
9 | import io.github.ryunen344.tink.util.toNSData
10 | import kotlinx.cinterop.ptr
11 | import kotlinx.cinterop.value
12 |
13 | class DarwinPublicKeyVerify(private val native: TINKPublicKeyVerifyProtocol) : PublicKeyVerify {
14 |
15 | @Throws(GeneralSecurityException::class)
16 | constructor(handle: TINKKeysetHandle) : this(
17 | memScopedInstance(
18 | block = {
19 | TINKPublicKeyVerifyFactory.primitiveWithKeysetHandle(handle, it.ptr) ?: throw GeneralSecurityException(
20 | cause = it.value?.asThrowable()
21 | )
22 | },
23 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) },
24 | )
25 | )
26 |
27 | @Throws(GeneralSecurityException::class)
28 | override fun verify(signature: ByteArray, data: ByteArray) = memScopedInstance(
29 | block = {
30 | val verified = native.verifySignature(signature.toNSData(), data.toNSData(), it.ptr)
31 | if (!verified) throw GeneralSecurityException("Invalid signature")
32 | },
33 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) },
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/signature/DarwinPublicKeySign.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.signature
2 |
3 | import com.google.crypto.tink.TINKKeysetHandle
4 | import com.google.crypto.tink.TINKPublicKeySignFactory
5 | import com.google.crypto.tink.TINKPublicKeySignProtocol
6 | import io.github.ryunen344.tink.exception.GeneralSecurityException
7 | import io.github.ryunen344.tink.util.asThrowable
8 | import io.github.ryunen344.tink.util.memScopedInstance
9 | import io.github.ryunen344.tink.util.toByteArray
10 | import io.github.ryunen344.tink.util.toNSData
11 | import kotlinx.cinterop.ptr
12 | import kotlinx.cinterop.value
13 |
14 | class DarwinPublicKeySign(private val native: TINKPublicKeySignProtocol) : PublicKeySign {
15 |
16 | @Throws(GeneralSecurityException::class)
17 | constructor(handle: TINKKeysetHandle) : this(
18 | memScopedInstance(
19 | block = {
20 | TINKPublicKeySignFactory.primitiveWithKeysetHandle(handle, it.ptr) ?: throw GeneralSecurityException(
21 | cause = it.value?.asThrowable()
22 | )
23 | },
24 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) },
25 | )
26 | )
27 |
28 | @Throws(GeneralSecurityException::class)
29 | override fun sign(data: ByteArray): ByteArray = memScopedInstance(
30 | block = {
31 | native.signatureForData(data.toNSData(), it.ptr)?.toByteArray()
32 | ?: throw GeneralSecurityException(cause = it.value?.asThrowable())
33 | },
34 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) },
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/hybrid/DarwinHybridEncrypt.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.hybrid
2 |
3 | import com.google.crypto.tink.TINKHybridEncryptFactory
4 | import com.google.crypto.tink.TINKHybridEncryptProtocol
5 | import com.google.crypto.tink.TINKKeysetHandle
6 | import io.github.ryunen344.tink.exception.GeneralSecurityException
7 | import io.github.ryunen344.tink.util.asThrowable
8 | import io.github.ryunen344.tink.util.memScopedInstance
9 | import io.github.ryunen344.tink.util.toByteArray
10 | import io.github.ryunen344.tink.util.toNSData
11 | import kotlinx.cinterop.ptr
12 | import kotlinx.cinterop.value
13 |
14 | class DarwinHybridEncrypt(private val native: TINKHybridEncryptProtocol) : HybridEncrypt {
15 |
16 | @Throws(GeneralSecurityException::class)
17 | constructor(handle: TINKKeysetHandle) : this(
18 | memScopedInstance(
19 | block = {
20 | TINKHybridEncryptFactory.primitiveWithKeysetHandle(handle, it.ptr)
21 | ?: throw GeneralSecurityException(cause = it.value?.asThrowable())
22 | },
23 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) },
24 | )
25 | )
26 |
27 | @Throws(GeneralSecurityException::class)
28 | override fun encrypt(plaintext: ByteArray, contextInfo: ByteArray): ByteArray = memScopedInstance(
29 | block = {
30 | native.encrypt(plaintext.toNSData(), contextInfo.toNSData(), it.ptr)?.toByteArray()
31 | ?: throw GeneralSecurityException(cause = it.value?.asThrowable())
32 | },
33 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) },
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/hybrid/DarwinHybridDecrypt.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.hybrid
2 |
3 | import com.google.crypto.tink.TINKHybridDecryptFactory
4 | import com.google.crypto.tink.TINKHybridDecryptProtocol
5 | import com.google.crypto.tink.TINKKeysetHandle
6 | import io.github.ryunen344.tink.exception.GeneralSecurityException
7 | import io.github.ryunen344.tink.util.asThrowable
8 | import io.github.ryunen344.tink.util.memScopedInstance
9 | import io.github.ryunen344.tink.util.toByteArray
10 | import io.github.ryunen344.tink.util.toNSData
11 | import kotlinx.cinterop.ptr
12 | import kotlinx.cinterop.value
13 |
14 | class DarwinHybridDecrypt(private val native: TINKHybridDecryptProtocol) : HybridDecrypt {
15 |
16 | @Throws(GeneralSecurityException::class)
17 | constructor(handle: TINKKeysetHandle) : this(
18 | memScopedInstance(
19 | block = {
20 | TINKHybridDecryptFactory.primitiveWithKeysetHandle(handle, it.ptr) ?: throw GeneralSecurityException(
21 | cause = it.value?.asThrowable()
22 | )
23 | },
24 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) },
25 | )
26 | )
27 |
28 | @Throws(GeneralSecurityException::class)
29 | override fun decrypt(ciphertext: ByteArray, contextInfo: ByteArray): ByteArray = memScopedInstance(
30 | block = {
31 | native.decrypt(ciphertext.toNSData(), contextInfo.toNSData(), it.ptr)?.toByteArray()
32 | ?: throw GeneralSecurityException(cause = it.value?.asThrowable())
33 | },
34 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) },
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 |
28 | UILaunchScreen
29 |
30 | UIRequiredDeviceCapabilities
31 |
32 | armv7
33 |
34 | UISupportedInterfaceOrientations
35 |
36 | UIInterfaceOrientationPortrait
37 | UIInterfaceOrientationLandscapeLeft
38 | UIInterfaceOrientationLandscapeRight
39 |
40 | UISupportedInterfaceOrientations~ipad
41 |
42 | UIInterfaceOrientationPortrait
43 | UIInterfaceOrientationPortraitUpsideDown
44 | UIInterfaceOrientationLandscapeLeft
45 | UIInterfaceOrientationLandscapeRight
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/.github/workflows/codeql.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL"
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | types:
9 | - opened
10 | - synchronize
11 | - reopened
12 | schedule:
13 | - cron: "0 1 * * 0"
14 |
15 | concurrency:
16 | group: ${{ github.workflow }}-${{ github.ref }}
17 | cancel-in-progress: true
18 |
19 | permissions:
20 | actions: read
21 | contents: read
22 | security-events: write
23 |
24 | jobs:
25 | analyze:
26 | runs-on: macos-14
27 | steps:
28 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
29 | - uses: ./.github/actions/sdkman
30 | - uses: ./.github/actions/konan
31 | - uses: ./.github/actions/xcode-select
32 | - uses: github/codeql-action/init@662472033e021d55d94146f66f6058822b0b39fd # v3
33 | with:
34 | config-file: ./.github/codeql/codeql-config.yml
35 | - uses: ruby/setup-ruby@217c988b8c2bf2bacb2d5c78a7e7b18f8c34daed # v1.200.0
36 | with:
37 | bundler-cache: true
38 | - uses: bazelbuild/setup-bazelisk@b39c379c82683a5f25d34f0d062761f62693e0b2 # v3
39 | - run: make bootstrap-submodule
40 | - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4
41 | with:
42 | path: |
43 | build/bazel
44 | Framework/Tink.xcframework
45 | key: v2-bazel-${{ runner.os }}-${{ hashFiles('.git/modules/TinkStub/HEAD') }}
46 | - uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3
47 | id: framework
48 | with:
49 | files: "Framework/Tink.xcframework"
50 | - uses: gradle/gradle-build-action@ac2d340dc04d9e1113182899e983b5400c17cda1 # v3
51 | with:
52 | gradle-home-cache-cleanup: true
53 | arguments: assemble --scan
54 | - uses: github/codeql-action/analyze@662472033e021d55d94146f66f6058822b0b39fd # v3
55 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/mac/DarwinMac.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.mac
2 |
3 | import com.google.crypto.tink.TINKKeysetHandle
4 | import com.google.crypto.tink.TINKMacFactory
5 | import com.google.crypto.tink.TINKMacProtocol
6 | import io.github.ryunen344.tink.exception.GeneralSecurityException
7 | import io.github.ryunen344.tink.util.asThrowable
8 | import io.github.ryunen344.tink.util.memScopedInstance
9 | import io.github.ryunen344.tink.util.toByteArray
10 | import io.github.ryunen344.tink.util.toNSData
11 | import kotlinx.cinterop.ptr
12 | import kotlinx.cinterop.value
13 |
14 | class DarwinMac(private val native: TINKMacProtocol) : Mac {
15 |
16 | @Throws(GeneralSecurityException::class)
17 | constructor(handle: TINKKeysetHandle) : this(
18 | memScopedInstance(
19 | block = {
20 | TINKMacFactory.primitiveWithKeysetHandle(handle, it.ptr)
21 | ?: throw GeneralSecurityException(cause = it.value?.asThrowable())
22 | },
23 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) }
24 | )
25 | )
26 |
27 | @Throws(GeneralSecurityException::class)
28 | override fun computeMac(data: ByteArray): ByteArray = memScopedInstance(
29 | block = {
30 | native.computeMacForData(data.toNSData(), it.ptr)?.toByteArray()
31 | ?: throw GeneralSecurityException(cause = it.value?.asThrowable())
32 | },
33 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) }
34 | )
35 |
36 | @Throws(GeneralSecurityException::class)
37 | override fun verifyMac(mac: ByteArray, data: ByteArray): Unit = memScopedInstance(
38 | block = {
39 | val verified = native.verifyMac(mac.toNSData(), data.toNSData(), it.ptr)
40 | if (!verified) throw GeneralSecurityException("invalid MAC")
41 | },
42 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) }
43 | )
44 | }
45 |
--------------------------------------------------------------------------------
/tink/src/commonMain/kotlin/io/github/ryunen344/tink/KeyTemplateSet.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink
2 |
3 | import io.github.ryunen344.tink.exception.GeneralSecurityException
4 |
5 | enum class KeyTemplateSet(val value: String) {
6 | // AEAD
7 | AES128_GCM("AES128_GCM"),
8 | AES128_GCM_RAW("AES128_GCM_RAW"),
9 | AES256_GCM("AES256_GCM"),
10 | AES256_GCM_RAW("AES256_GCM_RAW"),
11 | AES128_CTR_HMAC_SHA256("AES128_CTR_HMAC_SHA256"),
12 | AES256_CTR_HMAC_SHA256("AES256_CTR_HMAC_SHA256"),
13 | AES128_EAX("AES128_EAX"),
14 | AES256_EAX("AES256_EAX"),
15 | XCHACHA20_POLY1305("XCHACHA20_POLY1305"),
16 |
17 | // Deterministic AEAD
18 | AES256_SIV("AES256_SIV"),
19 |
20 | // MAC
21 | HMAC_SHA256_128BITTAG("HMAC_SHA256_128BITTAG"),
22 | HMAC_SHA256_256BITTAG("HMAC_SHA256_256BITTAG"),
23 | HMAC_SHA512_256BITTAG("HMAC_SHA512_256BITTAG"),
24 | HMAC_SHA512_512BITTAG("HMAC_SHA512_512BITTAG"),
25 | AES_CMAC("AES_CMAC"),
26 |
27 | // Digital Signatures
28 | ECDSA_P256("ECDSA_P256"),
29 | ECDSA_P384("ECDSA_P384"),
30 | ECDSA_P384_SHA384("ECDSA_P384_SHA384"),
31 | ECDSA_P384_SHA512("ECDSA_P384_SHA512"),
32 | ECDSA_P521("ECDSA_P521"),
33 | ECDSA_P256_IEEE_P1363("ECDSA_P256_IEEE_P1363"),
34 | ECDSA_P384_IEEE_P1363("ECDSA_P384_IEEE_P1363"),
35 | ECDSA_P521_IEEE_P1363("ECDSA_P521_IEEE_P1363"),
36 | ED25519("ED25519"),
37 | RSA_SSA_PKCS1_3072_SHA256_F4("RSA_SSA_PKCS1_3072_SHA256_F4"),
38 | RSA_SSA_PKCS1_4096_SHA512_F4("RSA_SSA_PKCS1_4096_SHA512_F4"),
39 | RSA_SSA_PSS_3072_SHA256_SHA256_32_F4("RSA_SSA_PSS_3072_SHA256_SHA256_32_F4"),
40 | RSA_SSA_PSS_4096_SHA512_SHA512_64_F4("RSA_SSA_PSS_4096_SHA512_SHA512_64_F4"),
41 |
42 | // Hybrid Encryption
43 | ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM("ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM"),
44 | ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256("ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256"),
45 | ;
46 | }
47 |
48 | @Throws(GeneralSecurityException::class)
49 | expect fun KeyTemplateSet.template(): KeyTemplate
50 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/aead/DarwinAead.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.aead
2 |
3 | import com.google.crypto.tink.TINKAeadFactory
4 | import com.google.crypto.tink.TINKAeadProtocol
5 | import com.google.crypto.tink.TINKKeysetHandle
6 | import io.github.ryunen344.tink.exception.GeneralSecurityException
7 | import io.github.ryunen344.tink.util.asThrowable
8 | import io.github.ryunen344.tink.util.memScopedInstance
9 | import io.github.ryunen344.tink.util.toByteArray
10 | import io.github.ryunen344.tink.util.toNSData
11 | import kotlinx.cinterop.ptr
12 | import kotlinx.cinterop.value
13 |
14 | class DarwinAead(val native: TINKAeadProtocol) : Aead {
15 |
16 | @Throws(GeneralSecurityException::class)
17 | constructor(handle: TINKKeysetHandle) : this(
18 | memScopedInstance(
19 | block = {
20 | TINKAeadFactory.primitiveWithKeysetHandle(handle, it.ptr)
21 | ?: throw GeneralSecurityException(cause = it.value?.asThrowable())
22 | },
23 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) },
24 | )
25 | )
26 |
27 | @Throws(GeneralSecurityException::class)
28 | override fun encrypt(plaintext: ByteArray, associatedData: ByteArray): ByteArray = memScopedInstance(
29 | block = {
30 | native.encrypt(plaintext.toNSData(), associatedData.toNSData(), it.ptr)?.toByteArray()
31 | ?: throw GeneralSecurityException(cause = it.value?.asThrowable())
32 | },
33 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) },
34 | )
35 |
36 | @Throws(GeneralSecurityException::class)
37 | override fun decrypt(ciphertext: ByteArray, associatedData: ByteArray): ByteArray = memScopedInstance(
38 | block = {
39 | native.decrypt(ciphertext.toNSData(), associatedData.toNSData(), it.ptr)?.toByteArray()
40 | ?: throw GeneralSecurityException(cause = it.value?.asThrowable())
41 | },
42 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) },
43 | )
44 | }
45 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/daead/DarwinDeterministicAead.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.daead
2 |
3 | import com.google.crypto.tink.TINKDeterministicAeadFactory
4 | import com.google.crypto.tink.TINKDeterministicAeadProtocol
5 | import com.google.crypto.tink.TINKKeysetHandle
6 | import io.github.ryunen344.tink.exception.GeneralSecurityException
7 | import io.github.ryunen344.tink.util.asThrowable
8 | import io.github.ryunen344.tink.util.memScopedInstance
9 | import io.github.ryunen344.tink.util.toByteArray
10 | import io.github.ryunen344.tink.util.toNSData
11 | import kotlinx.cinterop.ptr
12 | import kotlinx.cinterop.value
13 |
14 | class DarwinDeterministicAead(private val native: TINKDeterministicAeadProtocol) : DeterministicAead {
15 |
16 | @Throws(GeneralSecurityException::class)
17 | constructor(handle: TINKKeysetHandle) : this(
18 | memScopedInstance(
19 | block = {
20 | TINKDeterministicAeadFactory.primitiveWithKeysetHandle(handle, it.ptr)
21 | ?: throw GeneralSecurityException(cause = it.value?.asThrowable())
22 | },
23 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) },
24 | )
25 | )
26 |
27 | @Throws(GeneralSecurityException::class)
28 | override fun encryptDeterministically(plaintext: ByteArray, associatedData: ByteArray): ByteArray =
29 | memScopedInstance(
30 | block = {
31 | native.encryptDeterministically(plaintext.toNSData(), associatedData.toNSData(), it.ptr)?.toByteArray()
32 | ?: throw GeneralSecurityException(cause = it.value?.asThrowable())
33 | },
34 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) },
35 | )
36 |
37 | @Throws(GeneralSecurityException::class)
38 | override fun decryptDeterministically(ciphertext: ByteArray, associatedData: ByteArray): ByteArray =
39 | memScopedInstance(
40 | block = {
41 | native.decryptDeterministically(ciphertext.toNSData(), associatedData.toNSData(), it.ptr)?.toByteArray()
42 | ?: throw GeneralSecurityException(cause = it.value?.asThrowable())
43 | },
44 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) },
45 | )
46 | }
47 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: lint
2 |
3 | on:
4 | pull_request:
5 | types:
6 | - opened
7 | - synchronize
8 | - reopened
9 |
10 | concurrency:
11 | group: ${{ github.workflow }}-${{ github.ref }}
12 | cancel-in-progress: true
13 |
14 | jobs:
15 | lint-docs:
16 | runs-on: ubuntu-latest
17 | steps:
18 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
19 | - uses: reviewdog/action-actionlint@7eeec1dd160c2301eb28e1568721837d084558ad # v1
20 | with:
21 | github_token: ${{ secrets.GITHUB_TOKEN }}
22 | reporter: github-pr-review
23 | - uses: reviewdog/action-yamllint@e09f07780388032a624e9eb44a23fd1bbb4052cc # v1
24 | with:
25 | github_token: ${{ secrets.GITHUB_TOKEN }}
26 | reporter: github-pr-review
27 | - uses: reviewdog/action-markdownlint@28fb4224271253fedd5079b61de820d6228041fd # v0
28 | with:
29 | github_token: ${{ secrets.GITHUB_TOKEN }}
30 | reporter: github-pr-review
31 |
32 | lint-android:
33 | runs-on: ubuntu-latest
34 | steps:
35 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
36 | - uses: reviewdog/action-setup@3f401fe1d58fe77e10d665ab713057375e39b887 # v1
37 | - uses: ./.github/actions/sdkman
38 | - uses: ./.github/actions/konan
39 | - uses: gradle/gradle-build-action@ac2d340dc04d9e1113182899e983b5400c17cda1 # v3
40 | with:
41 | gradle-home-cache-cleanup: true
42 | arguments: lint --scan
43 | - uses: gradle/gradle-build-action@ac2d340dc04d9e1113182899e983b5400c17cda1 # v3
44 | with:
45 | gradle-home-cache-cleanup: true
46 | arguments: detekt --scan
47 | - uses: yutailang0119/action-android-lint@bd0b5a7d2cc453d16080b90e2a975d4af4aa9588 # v4
48 | with:
49 | report-path: "**/build/reports/lint-results*.xml"
50 | - run: < ./build/reports/detekt/detekt.xml reviewdog -f=checkstyle -name="detekt" -reporter="github-pr-review"
51 | env:
52 | REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
53 |
54 | lint-ios:
55 | runs-on: ubuntu-latest
56 | steps:
57 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
58 | - uses: reviewdog/action-setup@3f401fe1d58fe77e10d665ab713057375e39b887 # v1
59 | - uses: ./.github/actions/sdkman
60 | - uses: ./.github/actions/konan
61 |
--------------------------------------------------------------------------------
/tink/src/androidMain/kotlin/io/github/ryunen344/tink/KeysetHandle.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink
2 |
3 | import com.google.crypto.tink.CleartextKeysetHandle
4 | import io.github.ryunen344.tink.aead.Aead
5 | import io.github.ryunen344.tink.aead.AndroidAead
6 | import io.github.ryunen344.tink.daead.AndroidDeterministicAead
7 | import io.github.ryunen344.tink.daead.DeterministicAead
8 | import io.github.ryunen344.tink.exception.GeneralSecurityException
9 | import io.github.ryunen344.tink.exception.IOException
10 | import io.github.ryunen344.tink.hybrid.AndroidHybridDecrypt
11 | import io.github.ryunen344.tink.hybrid.AndroidHybridEncrypt
12 | import io.github.ryunen344.tink.hybrid.HybridDecrypt
13 | import io.github.ryunen344.tink.hybrid.HybridEncrypt
14 | import io.github.ryunen344.tink.mac.AndroidMac
15 | import io.github.ryunen344.tink.mac.Mac
16 | import io.github.ryunen344.tink.signature.AndroidPublicKeySign
17 | import io.github.ryunen344.tink.signature.AndroidPublicKeyVerify
18 | import io.github.ryunen344.tink.signature.PublicKeySign
19 | import io.github.ryunen344.tink.signature.PublicKeyVerify
20 | import kotlin.reflect.KClass
21 |
22 | actual typealias KeysetHandle = com.google.crypto.tink.KeysetHandle
23 |
24 | @Suppress("EXTENSION_SHADOWED_BY_MEMBER")
25 | @Throws(GeneralSecurityException::class, IOException::class)
26 | actual fun KeysetHandle.writeNoSecret(writer: KeysetWriter) = writeNoSecret(writer)
27 |
28 | @Throws(GeneralSecurityException::class, IOException::class)
29 | actual fun KeysetHandle.writeCleartext(writer: KeysetWriter) = CleartextKeysetHandle.write(this, writer)
30 |
31 | @Suppress("UNCHECKED_CAST")
32 | @Throws(GeneralSecurityException::class)
33 | actual fun KeysetHandle.getPrimitive(kClass: KClass
): P {
34 | val primitive = when (kClass) {
35 | Aead::class -> AndroidAead(this)
36 | DeterministicAead::class -> AndroidDeterministicAead(this)
37 | Mac::class -> AndroidMac(this)
38 | PublicKeySign::class -> AndroidPublicKeySign(this)
39 | PublicKeyVerify::class -> AndroidPublicKeyVerify(this)
40 | HybridEncrypt::class -> AndroidHybridEncrypt(this)
41 | HybridDecrypt::class -> AndroidHybridDecrypt(this)
42 | else -> throw GeneralSecurityException("not supported $kClass")
43 | }
44 |
45 | return primitive as P
46 | }
47 |
48 | @Throws(GeneralSecurityException::class)
49 | actual fun KeysetHandle.publicKeysetHandle(): KeysetHandle = publicKeysetHandle
50 |
--------------------------------------------------------------------------------
/.github/workflows/release-please.yml:
--------------------------------------------------------------------------------
1 | name: release-please
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | permissions:
9 | contents: write
10 | pull-requests: write
11 |
12 | jobs:
13 | release-please:
14 | runs-on: macos-14
15 | steps:
16 | - uses: google-github-actions/release-please-action@a37ac6e4f6449ce8b3f7607e4d97d0146028dc0b # v4
17 | id: release
18 | with:
19 | default-branch: main
20 | release-type: simple
21 | package-name: release-please-action
22 | include-v-in-tag: false
23 | bump-minor-pre-major: true
24 | bump-patch-for-minor-pre-major: true
25 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
26 | if: ${{ steps.release.outputs.release_created }}
27 | - uses: ./.github/actions/sdkman
28 | if: ${{ steps.release.outputs.release_created }}
29 | - uses: ./.github/actions/konan
30 | if: ${{ steps.release.outputs.release_created }}
31 | - uses: ./.github/actions/xcode-select
32 | if: ${{ steps.release.outputs.release_created }}
33 | - uses: ruby/setup-ruby@217c988b8c2bf2bacb2d5c78a7e7b18f8c34daed # v1.200.0
34 | if: ${{ steps.release.outputs.release_created }}
35 | with:
36 | bundler-cache: true
37 | - uses: bazelbuild/setup-bazelisk@b39c379c82683a5f25d34f0d062761f62693e0b2 # v3
38 | if: ${{ steps.release.outputs.release_created }}
39 | - run: make bootstrap-submodule
40 | if: ${{ steps.release.outputs.release_created }}
41 | - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4
42 | if: ${{ steps.release.outputs.release_created }}
43 | with:
44 | path: |
45 | build/bazel
46 | Framework/Tink.xcframework
47 | key: v2-bazel-${{ runner.os }}-${{ hashFiles('.git/modules/TinkStub/HEAD') }}
48 | - run: make archive
49 | if: ${{ steps.release.outputs.release_created }}
50 | - uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e # v3.0.0
51 | if: ${{ steps.release.outputs.release_created }}
52 | env:
53 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
54 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
55 | PGP_KEY_ID: ${{ secrets.PGP_KEY_ID }}
56 | PGP_SIGNING_KEY: ${{ secrets.PGP_SIGNING_KEY }}
57 | PGP_SIGNING_PASSWORD: ${{ secrets.PGP_SIGNING_PASSWORD }}
58 | with:
59 | max_attempts: 3
60 | retry_on: error
61 | timeout_minutes: 40
62 | command: ./gradlew publishAllPublicationsToSonatypeRepository closeAndReleaseSonatypeStagingRepository --stacktrace
63 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/KeysetHandleGenerator.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink
2 |
3 | import com.google.crypto.tink.TINKKeysetHandle
4 | import com.google.crypto.tink.create
5 | import io.github.ryunen344.tink.aead.Aead
6 | import io.github.ryunen344.tink.aead.DarwinAead
7 | import io.github.ryunen344.tink.exception.GeneralSecurityException
8 | import io.github.ryunen344.tink.util.asThrowable
9 | import io.github.ryunen344.tink.util.memScopedInstance
10 | import io.github.ryunen344.tink.util.toNSData
11 | import kotlinx.cinterop.ptr
12 | import kotlinx.cinterop.value
13 |
14 | @Throws(GeneralSecurityException::class)
15 | actual fun KeysetHandleGenerator.Companion.generateNew(keyTemplate: KeyTemplate): KeysetHandle = memScopedInstance(
16 | block = {
17 | runCatching {
18 | KeysetHandle(keyTemplate = keyTemplate, error = it.ptr)
19 | }.getOrElse {
20 | throw GeneralSecurityException(
21 | message = "No manager for type ${keyTemplate.description} has been registered.",
22 | cause = it
23 | )
24 | }
25 | },
26 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) }
27 | )
28 |
29 | @Throws(GeneralSecurityException::class)
30 | actual fun KeysetHandleGenerator.Companion.read(
31 | reader: KeysetReader,
32 | aead: Aead,
33 | ): KeysetHandle = memScopedInstance(
34 | block = {
35 | runCatching {
36 | KeysetHandle(keysetReader = reader.native, andKey = (aead as DarwinAead).native, error = it.ptr)
37 | }.getOrElse {
38 | throw GeneralSecurityException(
39 | message = "No manager has been registered.",
40 | cause = it
41 | )
42 | }
43 | },
44 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) }
45 | )
46 |
47 | @Throws(GeneralSecurityException::class)
48 | actual fun KeysetHandleGenerator.Companion.readClearText(reader: KeysetReader): KeysetHandle = memScopedInstance(
49 | block = {
50 | TINKKeysetHandle.create(cleartextKeysetHandleWithKeysetReader = reader.native, error = it.ptr)
51 | ?: throw GeneralSecurityException(
52 | message = "No manager has been registered.",
53 | cause = it.value?.asThrowable()
54 | )
55 | },
56 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) }
57 | )
58 |
59 | @Throws(GeneralSecurityException::class)
60 | actual fun KeysetHandleGenerator.Companion.readNoSecret(keyset: ByteArray): KeysetHandle = memScopedInstance(
61 | block = {
62 | runCatching {
63 | KeysetHandle(noSecretKeyset = keyset.toNSData(), error = it.ptr)
64 | }.getOrElse {
65 | throw GeneralSecurityException(
66 | message = "No manager has been registered.",
67 | cause = it
68 | )
69 | }
70 | },
71 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) }
72 | )
73 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | CFPropertyList (3.0.7)
5 | base64
6 | nkf
7 | rexml
8 | activesupport (7.2.2)
9 | base64
10 | benchmark (>= 0.3)
11 | bigdecimal
12 | concurrent-ruby (~> 1.0, >= 1.3.1)
13 | connection_pool (>= 2.2.5)
14 | drb
15 | i18n (>= 1.6, < 2)
16 | logger (>= 1.4.2)
17 | minitest (>= 5.1)
18 | securerandom (>= 0.3)
19 | tzinfo (~> 2.0, >= 2.0.5)
20 | addressable (2.8.7)
21 | public_suffix (>= 2.0.2, < 7.0)
22 | algoliasearch (1.27.5)
23 | httpclient (~> 2.8, >= 2.8.3)
24 | json (>= 1.5.1)
25 | atomos (0.1.3)
26 | base64 (0.2.0)
27 | benchmark (0.3.0)
28 | bigdecimal (3.1.8)
29 | claide (1.1.0)
30 | cocoapods (1.16.2)
31 | addressable (~> 2.8)
32 | claide (>= 1.0.2, < 2.0)
33 | cocoapods-core (= 1.16.2)
34 | cocoapods-deintegrate (>= 1.0.3, < 2.0)
35 | cocoapods-downloader (>= 2.1, < 3.0)
36 | cocoapods-plugins (>= 1.0.0, < 2.0)
37 | cocoapods-search (>= 1.0.0, < 2.0)
38 | cocoapods-trunk (>= 1.6.0, < 2.0)
39 | cocoapods-try (>= 1.1.0, < 2.0)
40 | colored2 (~> 3.1)
41 | escape (~> 0.0.4)
42 | fourflusher (>= 2.3.0, < 3.0)
43 | gh_inspector (~> 1.0)
44 | molinillo (~> 0.8.0)
45 | nap (~> 1.0)
46 | ruby-macho (>= 2.3.0, < 3.0)
47 | xcodeproj (>= 1.27.0, < 2.0)
48 | cocoapods-core (1.16.2)
49 | activesupport (>= 5.0, < 8)
50 | addressable (~> 2.8)
51 | algoliasearch (~> 1.0)
52 | concurrent-ruby (~> 1.1)
53 | fuzzy_match (~> 2.0.4)
54 | nap (~> 1.0)
55 | netrc (~> 0.11)
56 | public_suffix (~> 4.0)
57 | typhoeus (~> 1.0)
58 | cocoapods-deintegrate (1.0.5)
59 | cocoapods-downloader (2.1)
60 | cocoapods-plugins (1.0.0)
61 | nap
62 | cocoapods-search (1.0.1)
63 | cocoapods-trunk (1.6.0)
64 | nap (>= 0.8, < 2.0)
65 | netrc (~> 0.11)
66 | cocoapods-try (1.2.0)
67 | colored2 (3.1.2)
68 | concurrent-ruby (1.3.4)
69 | connection_pool (2.4.1)
70 | drb (2.2.1)
71 | escape (0.0.4)
72 | ethon (0.16.0)
73 | ffi (>= 1.15.0)
74 | ffi (1.17.0)
75 | fourflusher (2.3.1)
76 | fuzzy_match (2.0.4)
77 | gh_inspector (1.1.3)
78 | httpclient (2.8.3)
79 | i18n (1.14.6)
80 | concurrent-ruby (~> 1.0)
81 | json (2.7.5)
82 | logger (1.6.1)
83 | minitest (5.25.1)
84 | molinillo (0.8.0)
85 | nanaimo (0.4.0)
86 | nap (1.1.0)
87 | netrc (0.11.0)
88 | nkf (0.2.0)
89 | public_suffix (4.0.7)
90 | rexml (3.3.9)
91 | rouge (3.28.0)
92 | ruby-macho (2.5.1)
93 | securerandom (0.3.1)
94 | typhoeus (1.4.1)
95 | ethon (>= 0.9.0)
96 | tzinfo (2.0.6)
97 | concurrent-ruby (~> 1.0)
98 | xcodeproj (1.27.0)
99 | CFPropertyList (>= 2.3.3, < 4.0)
100 | atomos (~> 0.1.3)
101 | claide (>= 1.0.2, < 2.0)
102 | colored2 (~> 3.1)
103 | nanaimo (~> 0.4.0)
104 | rexml (>= 3.3.6, < 4.0)
105 | xcpretty (0.4.0)
106 | rouge (~> 3.28.0)
107 |
108 | PLATFORMS
109 | ruby
110 |
111 | DEPENDENCIES
112 | cocoapods (= 1.16.2)
113 | xcpretty (= 0.4.0)
114 |
115 | BUNDLED WITH
116 | 2.4.19
117 |
--------------------------------------------------------------------------------
/iosApp/iosApp.xcodeproj/xcshareddata/xcschemes/iosApp.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/KeysetHandle.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink
2 |
3 | import com.google.crypto.tink.serializedKeyset
4 | import io.github.ryunen344.tink.aead.Aead
5 | import io.github.ryunen344.tink.aead.DarwinAead
6 | import io.github.ryunen344.tink.daead.DarwinDeterministicAead
7 | import io.github.ryunen344.tink.daead.DeterministicAead
8 | import io.github.ryunen344.tink.exception.GeneralSecurityException
9 | import io.github.ryunen344.tink.exception.IOException
10 | import io.github.ryunen344.tink.hybrid.DarwinHybridDecrypt
11 | import io.github.ryunen344.tink.hybrid.DarwinHybridEncrypt
12 | import io.github.ryunen344.tink.hybrid.HybridDecrypt
13 | import io.github.ryunen344.tink.hybrid.HybridEncrypt
14 | import io.github.ryunen344.tink.mac.DarwinMac
15 | import io.github.ryunen344.tink.mac.Mac
16 | import io.github.ryunen344.tink.signature.DarwinPublicKeySign
17 | import io.github.ryunen344.tink.signature.DarwinPublicKeyVerify
18 | import io.github.ryunen344.tink.signature.PublicKeySign
19 | import io.github.ryunen344.tink.signature.PublicKeyVerify
20 | import io.github.ryunen344.tink.util.asThrowable
21 | import io.github.ryunen344.tink.util.memScopedInstance
22 | import kotlinx.cinterop.ptr
23 | import kotlinx.cinterop.value
24 | import platform.Foundation.NSData
25 | import platform.Foundation.NSString
26 | import platform.Foundation.NSUTF8StringEncoding
27 | import platform.Foundation.create
28 | import platform.Foundation.dataUsingEncoding
29 | import kotlin.reflect.KClass
30 |
31 | actual typealias KeysetHandle = com.google.crypto.tink.TINKKeysetHandle
32 |
33 | @Throws(GeneralSecurityException::class, IOException::class)
34 | actual fun KeysetHandle.writeNoSecret(writer: KeysetWriter) = memScopedInstance(
35 | block = { writer.value = serializedKeysetNoSecret(it.ptr) },
36 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) }
37 | )
38 |
39 | @Throws(GeneralSecurityException::class, IOException::class)
40 | actual fun KeysetHandle.writeCleartext(writer: KeysetWriter) {
41 | writer.value = serializedKeyset()
42 | }
43 |
44 | @Suppress("CAST_NEVER_SUCCEEDS")
45 | inline fun String.toNSString() = this as NSString
46 |
47 | inline fun String.toNSData() = toNSString().toNSData()
48 |
49 | @Suppress("CAST_NEVER_SUCCEEDS")
50 | inline fun NSString.toKString() = this as String
51 |
52 | inline fun NSString.toNSData() = dataUsingEncoding(NSUTF8StringEncoding)
53 |
54 | inline fun NSData.toNSString() = NSString.create(data = this, encoding = NSUTF8StringEncoding)
55 |
56 | @Suppress("UNCHECKED_CAST")
57 | @Throws(GeneralSecurityException::class)
58 | actual fun
KeysetHandle.getPrimitive(kClass: KClass
): P {
59 | val primitive = when (kClass) {
60 | Aead::class -> DarwinAead(this)
61 | DeterministicAead::class -> DarwinDeterministicAead(this)
62 | Mac::class -> DarwinMac(this)
63 | PublicKeySign::class -> DarwinPublicKeySign(this)
64 | PublicKeyVerify::class -> DarwinPublicKeyVerify(this)
65 | HybridEncrypt::class -> DarwinHybridEncrypt(this)
66 | HybridDecrypt::class -> DarwinHybridDecrypt(this)
67 | else -> throw GeneralSecurityException("not supported $kClass")
68 | }
69 | return primitive as P
70 | }
71 |
72 | @Throws(GeneralSecurityException::class)
73 | actual fun KeysetHandle.publicKeysetHandle(): KeysetHandle = memScopedInstance(
74 | block = {
75 | KeysetHandle.publicKeysetHandleWithHandle(this@publicKeysetHandle, it.ptr)
76 | ?: throw GeneralSecurityException(cause = it.value?.asThrowable())
77 | },
78 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) }
79 | )
80 |
--------------------------------------------------------------------------------
/iosApp/iosApp/ContentView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import TinKMM
3 | import Tink
4 |
5 | struct ContentView: View {
6 |
7 | private var handle: TINKKeysetHandle
8 |
9 | private let aead: Aead
10 |
11 | @State private var input: String = ""
12 | @State private var encrypted: Data? = nil
13 | @State private var decrypted: String = ""
14 |
15 | var body: some View {
16 | VStack {
17 | Group {
18 | Text("input")
19 | TextField("target", text: $input)
20 | .textFieldStyle(RoundedBorderTextFieldStyle())
21 | }
22 |
23 | Group {
24 | Text("encrypted")
25 | Text(encrypted?.string ?? "nil")
26 | }
27 |
28 | Group {
29 | Text("decrypted")
30 | Text(decrypted)
31 | }
32 |
33 | Group {
34 | Button("encrypt") {
35 | encrypted = try! aead.encrypt(plaintext: input.array, associatedData: "associatedData".array).data
36 | }
37 | Button("decrypt") {
38 | guard let data = encrypted else { return }
39 | var result: KotlinByteArray? = nil
40 | do {
41 | result = try aead.decrypt(ciphertext: data.array, associatedData: "associatedData".array)
42 | } catch let error {
43 | print(error)
44 | }
45 | decrypted = result?.string ?? ""
46 | }
47 | }
48 | }
49 | }
50 |
51 | init() {
52 | try! AeadConfig.companion.register()
53 | let template = try! KeyTemplateSet.aes256Gcm.template()
54 | handle = try! KeysetHandleGenerator.companion.generateNew(keyTemplate: template)
55 | aead = try! KeysetHandleKt.getPrimitive(handle, kClass: TinkPrimitiveKt.aead) as! Aead
56 | }
57 | }
58 |
59 | struct ContentView_Previews: PreviewProvider {
60 | static var previews: some View {
61 | ContentView()
62 | }
63 | }
64 |
65 | extension String {
66 | var array: KotlinByteArray {
67 | let data = Data(self.utf8)
68 | let result = KotlinByteArray(size: Int32(data.count))
69 | for (idx, byte) in data.enumerated() {
70 | result.set(index: Int32(idx), value: Int8(bitPattern: byte))
71 | }
72 | return result
73 | }
74 | }
75 |
76 | extension Data {
77 | var string: String {
78 | return String(decoding: self, as: UTF8.self)
79 | }
80 |
81 | var array: KotlinByteArray {
82 | let result = KotlinByteArray(size: Int32(self.count))
83 | for (idx, byte) in self.enumerated() {
84 | result.set(index: Int32(idx), value: Int8(bitPattern: byte))
85 | }
86 | return result
87 | }
88 | }
89 |
90 | extension KotlinByteArray {
91 | var data: Data {
92 | var data = Data(count: Int(size))
93 | for idx in 0 ..< size {
94 | let byte: Int8 = self.get(index: idx)
95 | data[Int(idx)] = UInt8(bitPattern: byte)
96 | }
97 | return data
98 | }
99 |
100 | var string: String {
101 | let size = self.size
102 | var data = Data(count: Int(size))
103 | for idx in 0 ..< size {
104 | let byte: Int8 = self.get(index: idx)
105 | data[Int(idx)] = UInt8(bitPattern: byte)
106 | }
107 | return String(decoding: data, as: UTF8.self)
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/tink/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.kotlin.gradle.plugin.mpp.BitcodeEmbeddingMode
2 | import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType
3 | import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework
4 | import org.jetbrains.kotlin.konan.target.KonanTarget
5 |
6 | plugins {
7 | kotlin("multiplatform")
8 | id("com.android.library")
9 | id("jacoco")
10 | }
11 |
12 | kotlin {
13 | androidTarget {
14 | publishLibraryVariants("release")
15 | }
16 |
17 | val frameworkName = "TinKMM"
18 | val xcf = XCFramework(frameworkName)
19 |
20 | listOf(
21 | iosX64(),
22 | iosSimulatorArm64(),
23 | iosArm64(),
24 | ).forEach {
25 | it.compilations.named("main") {
26 | cinterops.create("Tink") {
27 | compilerOpts(tinkCompilerOption(it.konanTarget))
28 | }
29 | }
30 | it.binaries {
31 | getTest(NativeBuildType.DEBUG).linkerOpts(tinkLinkerOption(it.konanTarget))
32 | framework {
33 | baseName = frameworkName
34 | embedBitcode(BitcodeEmbeddingMode.DISABLE)
35 | binaryOption("bundleId", "io.github.ryunen344.tink")
36 | binaryOption("bundleVersion", version.toString())
37 | binaryOption("bundleShortVersionString", version.toString())
38 | linkerOpts(tinkLinkerOption(it.konanTarget))
39 | xcf.add(this)
40 | }
41 | }
42 | }
43 |
44 | sourceSets {
45 | getByName("commonTest").dependencies {
46 | implementation(kotlin("test"))
47 | }
48 | getByName("androidMain").dependencies {
49 | implementation("com.google.crypto.tink:tink-android:1.7.0")
50 | }
51 | create("iosMain") {
52 | dependsOn(getByName("commonMain"))
53 | maybeCreate("iosX64Main").dependsOn(this)
54 | maybeCreate("iosSimulatorArm64Main").dependsOn(this)
55 | maybeCreate("iosArm64Main").dependsOn(this)
56 | }
57 | create("iosTest") {
58 | dependsOn(getByName("commonTest"))
59 | maybeCreate("iosX64Test").dependsOn(this)
60 | maybeCreate("iosSimulatorArm64Test").dependsOn(this)
61 | maybeCreate("iosArm64Test").dependsOn(this)
62 | }
63 | }
64 |
65 | compilerOptions {
66 | freeCompilerArgs.set(
67 | freeCompilerArgs.get() + listOf(
68 | // for Kotlin/Native interop
69 | "-opt-in=kotlinx.cinterop.BetaInteropApi",
70 | "-opt-in=kotlinx.cinterop.ExperimentalForeignApi",
71 | "-Xexpect-actual-classes",
72 | )
73 | )
74 | }
75 | }
76 |
77 | android {
78 | namespace = "io.github.ryunen344.tink"
79 | compileSdk = 33
80 | defaultConfig {
81 | minSdk = 24
82 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
83 | }
84 | compileOptions {
85 | sourceCompatibility = JavaVersion.VERSION_17
86 | targetCompatibility = JavaVersion.VERSION_17
87 | }
88 | testOptions {
89 | unitTests.isIncludeAndroidResources = true
90 | unitTests.all { test ->
91 | test.testLogging.showStandardStreams = true
92 | jacoco.jacocoVersion = "0.8.9"
93 | test.extensions.configure {
94 | isIncludeNoLocationClasses = true
95 | excludes = listOf("jdk.internal.*")
96 | }
97 | }
98 | }
99 | buildTypes.getByName("debug") {
100 | enableUnitTestCoverage = true
101 | enableAndroidTestCoverage = true
102 | }
103 | lint {
104 | abortOnError = false
105 | }
106 | with(sourceSets["main"]) {
107 | java.srcDirs("src/androidMain/kotlin", "src/commonMain/kotlin")
108 | res.srcDirs("src/androidMain/res")
109 | }
110 | with(sourceSets["test"]) {
111 | java.srcDirs("src/androidUnitTest/kotlin", "src/commonTest/kotlin")
112 | res.srcDirs("src/androidUnitTest/res")
113 | }
114 | }
115 |
116 | fun Project.xcfPath(target: KonanTarget): String {
117 | return "$rootDir/Framework/Tink.xcframework/" +
118 | if (target is KonanTarget.IOS_ARM64) "ios-arm64" else "ios-arm64_x86_64-simulator"
119 | }
120 |
121 | fun Project.tinkCompilerOption(target: KonanTarget): List {
122 | return listOf(
123 | "-framework",
124 | "Tink",
125 | "-F${xcfPath(target)}",
126 | )
127 | }
128 |
129 | fun Project.tinkLinkerOption(target: KonanTarget): List {
130 | return tinkCompilerOption(target) + listOf(
131 | "-rpath",
132 | xcfPath(target),
133 | "-ObjC",
134 | )
135 | }
136 |
--------------------------------------------------------------------------------
/tink/src/commonTest/kotlin/io/github/ryunen344/tink/KeysetWriterTest.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink
2 |
3 | import io.github.ryunen344.tink.daead.DeterministicAeadConfig
4 | import io.github.ryunen344.tink.daead.register
5 | import io.github.ryunen344.tink.exception.GeneralSecurityException
6 | import io.github.ryunen344.tink.signature.SignatureConfig
7 | import io.github.ryunen344.tink.signature.register
8 | import kotlin.test.Test
9 | import kotlin.test.assertEquals
10 | import kotlin.test.assertFailsWith
11 |
12 | class KeysetWriterTest {
13 | @Test
14 | fun test_write() {
15 | DeterministicAeadConfig.register()
16 | val handle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_DAEAD_KEYSET))
17 | val writer = BinaryKeysetWriter()
18 | handle.writeCleartext(writer)
19 | assertEquals(
20 | DAEAD_KEYSET_CONTENT,
21 | writer.write().contentToString()
22 | )
23 | }
24 |
25 | @Test
26 | fun test_write_public_handle() {
27 | SignatureConfig.register()
28 | val handle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_PUBLIC_KEYSET))
29 | val writer = BinaryKeysetWriter()
30 | handle.writeNoSecret(writer)
31 | assertEquals(
32 | PUBLIC_KEYSET_CONTENT,
33 | writer.write().contentToString()
34 | )
35 | }
36 |
37 | @Test
38 | fun test_write_private_handle_then_throw_error() {
39 | DeterministicAeadConfig.register()
40 | val handle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_DAEAD_KEYSET))
41 | val writer = BinaryKeysetWriter()
42 |
43 | assertFailsWith {
44 | handle.writeNoSecret(writer)
45 | }
46 | }
47 |
48 | private companion object {
49 | val JSON_DAEAD_KEYSET = """
50 | {
51 | "primaryKeyId": 961932622,
52 | "key": [
53 | {
54 | "keyData": {
55 | "typeUrl": "type.googleapis.com/google.crypto.tink.AesSivKey",
56 | "keyMaterialType": "SYMMETRIC",
57 | "value": "EkCJ9r5iwc5uxq5ugFyrHXh5dijTa7qalWUgZ8Gf08RxNd545FjtLMYL7ObcaFtCSkvV2+7u6F2DN+kqUjAfkf2W"
58 | },
59 | "outputPrefixType": "TINK",
60 | "keyId": 961932622,
61 | "status": "ENABLED"
62 | }
63 | ]
64 | }
65 | """.trimIndent()
66 |
67 | @Suppress("MaxLineLength")
68 | const val DAEAD_KEYSET_CONTENT =
69 | "[8, -50, -38, -41, -54, 3, 18, -124, 1, 10, 120, 10, 48, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 99, 114, 121, 112, 116, 111, 46, 116, 105, 110, 107, 46, 65, 101, 115, 83, 105, 118, 75, 101, 121, 18, 66, 18, 64, -119, -10, -66, 98, -63, -50, 110, -58, -82, 110, -128, 92, -85, 29, 120, 121, 118, 40, -45, 107, -70, -102, -107, 101, 32, 103, -63, -97, -45, -60, 113, 53, -34, 120, -28, 88, -19, 44, -58, 11, -20, -26, -36, 104, 91, 66, 74, 75, -43, -37, -18, -18, -24, 93, -125, 55, -23, 42, 82, 48, 31, -111, -3, -106, 24, 1, 16, 1, 24, -50, -38, -41, -54, 3, 32, 1]"
70 |
71 | val JSON_PUBLIC_KEYSET = """
72 | {
73 | "primaryKeyId": 775870498,
74 | "key": [
75 | {
76 | "keyData": {
77 | "typeUrl": "type.googleapis.com/google.crypto.tink.EcdsaPublicKey",
78 | "value": "IiApA+NmYivxRfhMuvTKZAwqETmn+WagBP/reucEjEvXkRog1AJ5GBzf+n27xnj9KcoGllF9NIFfQrDEP99FNH+Cne4SBhgCEAIIAw==",
79 | "keyMaterialType": "ASYMMETRIC_PUBLIC"
80 | },
81 | "status": "ENABLED",
82 | "keyId": 775870498,
83 | "outputPrefixType": "TINK"
84 | }
85 | ]
86 | }
87 | """.trimIndent()
88 |
89 | @Suppress("MaxLineLength")
90 | const val PUBLIC_KEYSET_CONTENT =
91 | "[8, -94, -80, -5, -15, 2, 18, -108, 1, 10, -121, 1, 10, 53, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 99, 114, 121, 112, 116, 111, 46, 116, 105, 110, 107, 46, 69, 99, 100, 115, 97, 80, 117, 98, 108, 105, 99, 75, 101, 121, 18, 76, 34, 32, 41, 3, -29, 102, 98, 43, -15, 69, -8, 76, -70, -12, -54, 100, 12, 42, 17, 57, -89, -7, 102, -96, 4, -1, -21, 122, -25, 4, -116, 75, -41, -111, 26, 32, -44, 2, 121, 24, 28, -33, -6, 125, -69, -58, 120, -3, 41, -54, 6, -106, 81, 125, 52, -127, 95, 66, -80, -60, 63, -33, 69, 52, 127, -126, -99, -18, 18, 6, 24, 2, 16, 2, 8, 3, 24, 3, 16, 1, 24, -94, -80, -5, -15, 2, 32, 1]"
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: test
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | types:
9 | - opened
10 | - synchronize
11 | - reopened
12 |
13 | concurrency:
14 | group: ${{ github.workflow }}-${{ github.ref }}
15 | cancel-in-progress: true
16 |
17 | jobs:
18 | unit-test-android:
19 | runs-on: ubuntu-latest
20 | steps:
21 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
22 | - uses: ./.github/actions/sdkman
23 | - uses: ./.github/actions/konan
24 | - uses: gradle/gradle-build-action@ac2d340dc04d9e1113182899e983b5400c17cda1 # v3
25 | with:
26 | gradle-home-cache-cleanup: true
27 | arguments: testDebugUnitTest --scan
28 | - uses: gradle/gradle-build-action@ac2d340dc04d9e1113182899e983b5400c17cda1 # v3
29 | with:
30 | gradle-home-cache-cleanup: true
31 | arguments: jacocoMergedReport
32 | - uses: EnricoMi/publish-unit-test-result-action@170bf24d20d201b842d7a52403b73ed297e6645b # v2
33 | if: ${{ !cancelled() && (success() || failure()) }}
34 | with:
35 | check_name: Test Results android
36 | files: |
37 | **/test-results/**/*.xml
38 | - uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4
39 | with:
40 | token: ${{ secrets.CODECOV_TOKEN }}
41 |
42 | unit-test-ios:
43 | runs-on: macos-14
44 | steps:
45 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
46 | - uses: ./.github/actions/sdkman
47 | - uses: ./.github/actions/konan
48 | - uses: ./.github/actions/xcode-select
49 | - uses: ruby/setup-ruby@217c988b8c2bf2bacb2d5c78a7e7b18f8c34daed # v1.200.0
50 | with:
51 | bundler-cache: true
52 | - uses: bazelbuild/setup-bazelisk@b39c379c82683a5f25d34f0d062761f62693e0b2 # v3
53 | - run: make bootstrap-submodule
54 | - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4
55 | with:
56 | path: |
57 | build/bazel
58 | Framework/Tink.xcframework
59 | key: v2-bazel-${{ runner.os }}-${{ hashFiles('.git/modules/TinkStub/HEAD') }}
60 | - uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3
61 | id: framework
62 | with:
63 | files: "Framework/Tink.xcframework"
64 | - run: make archive
65 | if: steps.framework.outputs.files_exists != 'true'
66 | - uses: gradle/gradle-build-action@ac2d340dc04d9e1113182899e983b5400c17cda1 # v3
67 | with:
68 | gradle-home-cache-cleanup: true
69 | arguments: iosX64Test --scan
70 | - uses: EnricoMi/publish-unit-test-result-action/composite@170bf24d20d201b842d7a52403b73ed297e6645b # v2
71 | if: ${{ !cancelled() && (success() || failure()) }}
72 | with:
73 | check_name: Unit Test Results iosX64
74 | files: |
75 | **/test-results/**/*.xml
76 |
77 | instrumented-test-ios:
78 | runs-on: macos-14
79 | steps:
80 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
81 | - uses: ./.github/actions/sdkman
82 | - uses: ./.github/actions/konan
83 | - uses: ./.github/actions/xcode-select
84 | - uses: ruby/setup-ruby@217c988b8c2bf2bacb2d5c78a7e7b18f8c34daed # v1.200.0
85 | with:
86 | bundler-cache: true
87 | - uses: bazelbuild/setup-bazelisk@b39c379c82683a5f25d34f0d062761f62693e0b2 # v3
88 | - run: make bootstrap-submodule
89 | - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4
90 | with:
91 | path: |
92 | build/bazel
93 | Framework/Tink.xcframework
94 | key: v2-bazel-${{ runner.os }}-${{ hashFiles('.git/modules/TinkStub/HEAD') }}
95 | - uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3
96 | id: framework
97 | with:
98 | files: "Framework/Tink.xcframework"
99 | - run: make archive
100 | if: steps.framework.outputs.files_exists != 'true'
101 | - uses: gradle/gradle-build-action@ac2d340dc04d9e1113182899e983b5400c17cda1 # v3
102 | with:
103 | gradle-home-cache-cleanup: true
104 | arguments: :iosApp:simulatorsList
105 | - uses: gradle/gradle-build-action@ac2d340dc04d9e1113182899e983b5400c17cda1 # v3
106 | with:
107 | gradle-home-cache-cleanup: true
108 | arguments: :iosApp:xcodetest --scan --info
109 | - uses: EnricoMi/publish-unit-test-result-action/composite@170bf24d20d201b842d7a52403b73ed297e6645b # v2
110 | if: ${{ !cancelled() && (success() || failure()) }}
111 | with:
112 | check_name: Instrumented Test Results iosX64
113 | files: |
114 | **/test-results.xml
115 |
--------------------------------------------------------------------------------
/tink/src/iosMain/kotlin/io/github/ryunen344/tink/KeyTemplateSet.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink
2 |
3 | import com.google.crypto.tink.TINKAeadKeyTemplate
4 | import com.google.crypto.tink.TINKAes128CtrHmacSha256
5 | import com.google.crypto.tink.TINKAes128Eax
6 | import com.google.crypto.tink.TINKAes128Gcm
7 | import com.google.crypto.tink.TINKAes128GcmNoPrefix
8 | import com.google.crypto.tink.TINKAes256CtrHmacSha256
9 | import com.google.crypto.tink.TINKAes256Eax
10 | import com.google.crypto.tink.TINKAes256Gcm
11 | import com.google.crypto.tink.TINKAes256GcmNoPrefix
12 | import com.google.crypto.tink.TINKAes256Siv
13 | import com.google.crypto.tink.TINKAesCmac
14 | import com.google.crypto.tink.TINKDeterministicAeadKeyTemplate
15 | import com.google.crypto.tink.TINKEcdsaP256
16 | import com.google.crypto.tink.TINKEcdsaP256Ieee
17 | import com.google.crypto.tink.TINKEcdsaP384
18 | import com.google.crypto.tink.TINKEcdsaP384Ieee
19 | import com.google.crypto.tink.TINKEcdsaP384Sha384
20 | import com.google.crypto.tink.TINKEcdsaP384Sha512
21 | import com.google.crypto.tink.TINKEcdsaP521
22 | import com.google.crypto.tink.TINKEcdsaP521Ieee
23 | import com.google.crypto.tink.TINKEciesP256HkdfHmacSha256Aes128CtrHmacSha256
24 | import com.google.crypto.tink.TINKEciesP256HkdfHmacSha256Aes128Gcm
25 | import com.google.crypto.tink.TINKEd25519
26 | import com.google.crypto.tink.TINKHmacSha256
27 | import com.google.crypto.tink.TINKHmacSha256HalfSizeTag
28 | import com.google.crypto.tink.TINKHmacSha512
29 | import com.google.crypto.tink.TINKHmacSha512HalfSizeTag
30 | import com.google.crypto.tink.TINKHybridKeyTemplate
31 | import com.google.crypto.tink.TINKMacKeyTemplate
32 | import com.google.crypto.tink.TINKRsaSsaPkcs13072Sha256F4
33 | import com.google.crypto.tink.TINKRsaSsaPkcs14096Sha512F4
34 | import com.google.crypto.tink.TINKRsaSsaPss3072Sha256Sha256F4
35 | import com.google.crypto.tink.TINKRsaSsaPss4096Sha512Sha512F4
36 | import com.google.crypto.tink.TINKSignatureKeyTemplate
37 | import com.google.crypto.tink.TINKXChaCha20Poly1305
38 | import io.github.ryunen344.tink.exception.GeneralSecurityException
39 | import io.github.ryunen344.tink.util.asThrowable
40 | import io.github.ryunen344.tink.util.memScopedInstance
41 | import kotlinx.cinterop.ptr
42 |
43 | @Suppress("CyclomaticComplexMethod")
44 | @Throws(GeneralSecurityException::class)
45 | actual fun KeyTemplateSet.template(): KeyTemplate = memScopedInstance(
46 | block = {
47 | when (this@template) {
48 | // AEAD
49 | KeyTemplateSet.AES128_GCM -> TINKAeadKeyTemplate(TINKAes128Gcm, it.ptr)
50 | KeyTemplateSet.AES128_GCM_RAW -> TINKAeadKeyTemplate(TINKAes128GcmNoPrefix, it.ptr)
51 | KeyTemplateSet.AES256_GCM -> TINKAeadKeyTemplate(TINKAes256Gcm, it.ptr)
52 | KeyTemplateSet.AES256_GCM_RAW -> TINKAeadKeyTemplate(TINKAes256GcmNoPrefix, it.ptr)
53 | KeyTemplateSet.AES128_CTR_HMAC_SHA256 -> TINKAeadKeyTemplate(TINKAes128CtrHmacSha256, it.ptr)
54 | KeyTemplateSet.AES256_CTR_HMAC_SHA256 -> TINKAeadKeyTemplate(TINKAes256CtrHmacSha256, it.ptr)
55 | KeyTemplateSet.AES128_EAX -> TINKAeadKeyTemplate(TINKAes128Eax, it.ptr)
56 | KeyTemplateSet.AES256_EAX -> TINKAeadKeyTemplate(TINKAes256Eax, it.ptr)
57 | KeyTemplateSet.XCHACHA20_POLY1305 -> TINKAeadKeyTemplate(TINKXChaCha20Poly1305, it.ptr)
58 |
59 | // Deterministic AEAD
60 | KeyTemplateSet.AES256_SIV -> TINKDeterministicAeadKeyTemplate(TINKAes256Siv, it.ptr)
61 |
62 | // MAC
63 | KeyTemplateSet.HMAC_SHA256_128BITTAG -> TINKMacKeyTemplate(TINKHmacSha256HalfSizeTag, it.ptr)
64 | KeyTemplateSet.HMAC_SHA256_256BITTAG -> TINKMacKeyTemplate(TINKHmacSha256, it.ptr)
65 | KeyTemplateSet.HMAC_SHA512_256BITTAG -> TINKMacKeyTemplate(TINKHmacSha512HalfSizeTag, it.ptr)
66 | KeyTemplateSet.HMAC_SHA512_512BITTAG -> TINKMacKeyTemplate(TINKHmacSha512, it.ptr)
67 | KeyTemplateSet.AES_CMAC -> TINKMacKeyTemplate(TINKAesCmac, it.ptr)
68 |
69 | // Digital Signatures
70 | KeyTemplateSet.ECDSA_P256 -> TINKSignatureKeyTemplate(TINKEcdsaP256, it.ptr)
71 | KeyTemplateSet.ECDSA_P384 -> TINKSignatureKeyTemplate(TINKEcdsaP384, it.ptr)
72 | KeyTemplateSet.ECDSA_P384_SHA384 -> TINKSignatureKeyTemplate(TINKEcdsaP384Sha384, it.ptr)
73 | KeyTemplateSet.ECDSA_P384_SHA512 -> TINKSignatureKeyTemplate(TINKEcdsaP384Sha512, it.ptr)
74 | KeyTemplateSet.ECDSA_P521 -> TINKSignatureKeyTemplate(TINKEcdsaP521, it.ptr)
75 | KeyTemplateSet.ECDSA_P256_IEEE_P1363 -> TINKSignatureKeyTemplate(TINKEcdsaP256Ieee, it.ptr)
76 | KeyTemplateSet.ECDSA_P384_IEEE_P1363 -> TINKSignatureKeyTemplate(TINKEcdsaP384Ieee, it.ptr)
77 | KeyTemplateSet.ECDSA_P521_IEEE_P1363 -> TINKSignatureKeyTemplate(TINKEcdsaP521Ieee, it.ptr)
78 | KeyTemplateSet.ED25519 -> TINKSignatureKeyTemplate(TINKEd25519, it.ptr)
79 | KeyTemplateSet.RSA_SSA_PKCS1_3072_SHA256_F4 -> TINKSignatureKeyTemplate(TINKRsaSsaPkcs13072Sha256F4, it.ptr)
80 | KeyTemplateSet.RSA_SSA_PKCS1_4096_SHA512_F4 -> TINKSignatureKeyTemplate(TINKRsaSsaPkcs14096Sha512F4, it.ptr)
81 |
82 | KeyTemplateSet.RSA_SSA_PSS_3072_SHA256_SHA256_32_F4 -> TINKSignatureKeyTemplate(
83 | TINKRsaSsaPss3072Sha256Sha256F4,
84 | it.ptr
85 | )
86 |
87 | KeyTemplateSet.RSA_SSA_PSS_4096_SHA512_SHA512_64_F4 -> TINKSignatureKeyTemplate(
88 | TINKRsaSsaPss4096Sha512Sha512F4,
89 | it.ptr
90 | )
91 |
92 | // Hybrid Encryption
93 | KeyTemplateSet.ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM -> TINKHybridKeyTemplate(
94 | TINKEciesP256HkdfHmacSha256Aes128Gcm,
95 | it.ptr
96 | )
97 |
98 | KeyTemplateSet.ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256 -> TINKHybridKeyTemplate(
99 | TINKEciesP256HkdfHmacSha256Aes128CtrHmacSha256,
100 | it.ptr
101 | )
102 | }
103 | },
104 | onError = { throw GeneralSecurityException(cause = it.asThrowable()) }
105 | )
106 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [0.0.7](https://github.com/RyuNen344/tink-kmm/compare/0.0.6...0.0.7) (2023-11-30)
4 |
5 |
6 | ### Bug Fixes
7 |
8 | * **deps:** update detekt to v1.23.2 ([#166](https://github.com/RyuNen344/tink-kmm/issues/166)) ([f58757b](https://github.com/RyuNen344/tink-kmm/commit/f58757bc4c797832dcb85afb9a26295515fc5a9e))
9 | * **deps:** update detekt to v1.23.3 ([#168](https://github.com/RyuNen344/tink-kmm/issues/168)) ([77ee74c](https://github.com/RyuNen344/tink-kmm/commit/77ee74c6816cf5b0d2a63514600fdf02ee1ee0ed))
10 | * **deps:** update detekt to v1.23.4 ([#177](https://github.com/RyuNen344/tink-kmm/issues/177)) ([d090399](https://github.com/RyuNen344/tink-kmm/commit/d090399c610df06a54810ee8e64ec8a3d4ae717b))
11 |
12 | ## [0.0.6](https://github.com/RyuNen344/tink-kmm/compare/0.0.5...0.0.6) (2023-09-17)
13 |
14 |
15 | ### Bug Fixes
16 |
17 | * **deps:** update detekt to v1.23.1 ([#114](https://github.com/RyuNen344/tink-kmm/issues/114)) ([6d7c1fc](https://github.com/RyuNen344/tink-kmm/commit/6d7c1fc3e704901951975dda2ec4f6d6d1399e72))
18 |
19 | ## [0.0.5](https://github.com/RyuNen344/tink-kmm/compare/0.0.4...0.0.5) (2023-07-23)
20 |
21 |
22 | ### Features
23 |
24 | * **deps:** update kotlin monorepo to v1.9.0 ([#76](https://github.com/RyuNen344/tink-kmm/issues/76)) ([08b94c0](https://github.com/RyuNen344/tink-kmm/commit/08b94c06cf1ef4a91ddafbfd1e8f08282f0f8940))
25 |
26 | ## [0.0.4](https://github.com/RyuNen344/tink-kmm/compare/0.0.3...0.0.4) (2023-07-15)
27 |
28 |
29 | ### Features
30 |
31 | * bump kotlin version to 1.8.22 ([#99](https://github.com/RyuNen344/tink-kmm/issues/99)) ([fa2280f](https://github.com/RyuNen344/tink-kmm/commit/fa2280f11f4f75d1c02237ed45aa9bcd111118d0))
32 |
33 |
34 | ### Bug Fixes
35 |
36 | * **deps:** update detekt to v1.23.0 ([#70](https://github.com/RyuNen344/tink-kmm/issues/70)) ([746b8b7](https://github.com/RyuNen344/tink-kmm/commit/746b8b7535e074a751e93d07469195f25ba959f9))
37 |
38 | ## [0.0.3](https://github.com/RyuNen344/tink-kmm/compare/0.0.2...0.0.3) (2023-05-07)
39 |
40 |
41 | ### Features
42 |
43 | * **ci:** automate maven release ([b1e1d1d](https://github.com/RyuNen344/tink-kmm/commit/b1e1d1d972e5b899e6b10bc8c5658a243d38d189))
44 |
45 |
46 | ### Deps
47 |
48 | * **deps:** update android gradle plugin to v8 (major) ([#64](https://github.com/RyuNen344/tink-kmm/pull/64)) ([64bae51](https://github.com/RyuNen344/tink-kmm/commit/64bae514929d44939fcaa9705fea3f5c5c732297))
49 | * **deps:** update dependency gradle to v8 ([#62](https://github.com/RyuNen344/tink-kmm/pull/62)) ([01fe6c6](https://github.com/RyuNen344/tink-kmm/commit/01fe6c6695671e85ece8b513d94191fed559adc1))
50 |
51 |
52 | ### Bug Fixes
53 |
54 | * **deps:** update detekt to v1.23.0-rc3 ([#59](https://github.com/RyuNen344/tink-kmm/issues/59)) ([5de6564](https://github.com/RyuNen344/tink-kmm/commit/5de6564ceb26cba3c41315ce912b0f8746e42bd1))
55 |
56 | ## [0.0.2](https://github.com/RyuNen344/tink-kmm/compare/0.0.1...0.0.2) (2023-05-06)
57 |
58 |
59 | ### Miscellaneous Chores
60 |
61 | * release 0.0.2 ([2c0d0aa](https://github.com/RyuNen344/tink-kmm/commit/2c0d0aa7d481e5746612035d2b5cd8218090d387))
62 | * update kotlin to 1.8.21
63 |
64 | ## [0.0.1](https://github.com/RyuNen344/tink-kmm/compare/0.0.1-rc03...0.0.1) (2023-05-04)
65 |
66 |
67 | ### Miscellaneous Chores
68 |
69 | * release 0.0.1 ([8df0fe4](https://github.com/RyuNen344/tink-kmm/commit/8df0fe4df1cb7a866f85614639d8e233bf3ac6ee))
70 |
71 | ## [0.0.1-rc03](https://github.com/RyuNen344/tink-kmm/compare/0.0.1-rc02...0.0.1-rc03) (2023-05-04)
72 |
73 |
74 | ### Bug Fixes
75 |
76 | * invalid pom file ([e8c611b](https://github.com/RyuNen344/tink-kmm/commit/e8c611b175961306791c6be01fe8a0a23f437194))
77 |
78 |
79 | ### Miscellaneous Chores
80 |
81 | * release 0.0.1-rc03 ([89acd48](https://github.com/RyuNen344/tink-kmm/commit/89acd48b3cbf876095a921929d44884d53531d56))
82 |
83 | ## [0.0.1-rc02](https://github.com/RyuNen344/tink-kmm/compare/0.0.1-rc01...0.0.1-rc02) (2023-05-04)
84 |
85 |
86 | ### Miscellaneous Chores
87 |
88 | * release 0.0.1-rc02 ([0f48dbf](https://github.com/RyuNen344/tink-kmm/commit/0f48dbff7298330da50e633af3385b5e4bc62af0))
89 |
90 | ## 0.0.1-rc01 (2023-05-04)
91 |
92 |
93 | ### Features
94 |
95 | * **aead config:** impl AEADConfig ailas ([f816121](https://github.com/RyuNen344/tink-kmm/commit/f8161213d81aa51c357a3853a37cefaa4608f967))
96 | * **configs:** computed property to function, to append throws annotation ([b0b150c](https://github.com/RyuNen344/tink-kmm/commit/b0b150cba3a67ffacb21c076df9b07abef9ac906))
97 | * **configs:** fix imports ([6cf435a](https://github.com/RyuNen344/tink-kmm/commit/6cf435a1a257aa82b32b17d35e322fabb5447e25))
98 | * **configs:** fix initialization error ([4898211](https://github.com/RyuNen344/tink-kmm/commit/4898211274a663a8f06ae6d514d237260e138669))
99 | * **configs:** impl HybridConfig ailas ([c040f94](https://github.com/RyuNen344/tink-kmm/commit/c040f942f32e7fd8cd6266dd724cd2dd9ebf96c7))
100 | * **configs:** impl KeyTemplateSet, and configs ([fb6c84d](https://github.com/RyuNen344/tink-kmm/commit/fb6c84dcf610f4f6eeac8e882fd41ad35ac7454f))
101 | * **configs:** move files ([49201b0](https://github.com/RyuNen344/tink-kmm/commit/49201b0e755c16336f533d47307af4b9a8ba4a3f))
102 | * **generator:** impl KeysetHandleGenerator and Reader ([fbffd8c](https://github.com/RyuNen344/tink-kmm/commit/fbffd8c17b5d37ff3ec31eba85267927f21f42a5))
103 | * **KClass:** fix interface ([84c8052](https://github.com/RyuNen344/tink-kmm/commit/84c8052b8ef06a9ebfc92b75753b5572a2190d5f))
104 | * **KClass:** handle when constructor throws NullPointException ([5543917](https://github.com/RyuNen344/tink-kmm/commit/5543917de24b5f795c26ce15b2cfd357ba81b7a7))
105 | * **KClass:** support KClass interface ([722466c](https://github.com/RyuNen344/tink-kmm/commit/722466cfb58699a1bbedd09a889fc9642ec57d03))
106 | * **keyset reader:** add clear text keyset reader if ([b1a3902](https://github.com/RyuNen344/tink-kmm/commit/b1a3902c57f069759b36ba303fbd5122c592a80e))
107 | * **keyset reader:** impl BinaryKeysetReader ([2650f69](https://github.com/RyuNen344/tink-kmm/commit/2650f69d7bc7f88196875da020c1a184b7d05efe))
108 | * **keyset reader:** impl ClearTextKeysetReader ([46d3edc](https://github.com/RyuNen344/tink-kmm/commit/46d3edc4e3a32dba0da4d2d46eef2f95adda25aa))
109 | * **keyset reader:** impl JsonKeysetReader ([09e3ab2](https://github.com/RyuNen344/tink-kmm/commit/09e3ab281a030dc7943baf98e676ab36bd865727))
110 | * **primitives:** impl Basic Primitive Encryption ([ca3f745](https://github.com/RyuNen344/tink-kmm/commit/ca3f745e405fbb47f9bc8fb3f817be37a24893f8))
111 | * **writer:** remove unavailable interfaces ([e93a7b3](https://github.com/RyuNen344/tink-kmm/commit/e93a7b384a0bddc4523a72a280bd0c28a6c3d108))
112 |
113 |
114 | ### Bug Fixes
115 |
116 | * action ([b1901c9](https://github.com/RyuNen344/tink-kmm/commit/b1901c9a37da707b19ca91bd5fcd8db6f2e55108))
117 | * action id ([d9a417b](https://github.com/RyuNen344/tink-kmm/commit/d9a417bba6ddbc721b05fbe85148f4996ab07993))
118 | * actions ([2fecbd3](https://github.com/RyuNen344/tink-kmm/commit/2fecbd339a92b980ffbb7664eb81039c58b9be4f))
119 | * actions ([97ef06f](https://github.com/RyuNen344/tink-kmm/commit/97ef06f0f9c4fa366a678ec04d5b5e1d654a7e0e))
120 | * bazel command ([2bff396](https://github.com/RyuNen344/tink-kmm/commit/2bff3967b0de450bfe87b0185783c8981ae96db3))
121 | * codecov token ([2517fe4](https://github.com/RyuNen344/tink-kmm/commit/2517fe48abef921e43c4ecd9b1aaa3e4441ab6c2))
122 | * command option order ([e070b76](https://github.com/RyuNen344/tink-kmm/commit/e070b763a59912f51dcba4968860412dde0e0dc9))
123 | * command option order ([45bd4c8](https://github.com/RyuNen344/tink-kmm/commit/45bd4c8e34915ff7c00b3050859ff66ade633b55))
124 | * coverage config ([9d2c019](https://github.com/RyuNen344/tink-kmm/commit/9d2c0196390ee9b536ab7963122863ada344c236))
125 | * java home ([baf7954](https://github.com/RyuNen344/tink-kmm/commit/baf7954262293f42e7e8a39221605a12f7c6ac66))
126 | * path ([fa3b2ca](https://github.com/RyuNen344/tink-kmm/commit/fa3b2ca8477ae7b7247ee0bf4d2595c373ab12d4))
127 | * path ([8a08bc6](https://github.com/RyuNen344/tink-kmm/commit/8a08bc6bf9d4dd3e16eb2ddb21e87f5d6ef60e4c))
128 | * sdkman cache ([fe76d78](https://github.com/RyuNen344/tink-kmm/commit/fe76d78c89083ede0c006e2f582c3be7d0c14174))
129 | * shell ([e6a50d8](https://github.com/RyuNen344/tink-kmm/commit/e6a50d8b5bb14f0f7cd9f99094c31ba6e00e2d02))
130 | * shellcheck ([a52ddb5](https://github.com/RyuNen344/tink-kmm/commit/a52ddb513e265b84bb02795eaa8d611b7c138e40))
131 | * space ([aa02d10](https://github.com/RyuNen344/tink-kmm/commit/aa02d10c82ae7f32a5dadae75aa7e88d98b2807a))
132 | * submodule init timing ([47807db](https://github.com/RyuNen344/tink-kmm/commit/47807db35d83c23b931030160625dcf99bcfed96))
133 |
134 |
135 | ### Miscellaneous Chores
136 |
137 | * release 0.0.1-rc01 ([951bcb4](https://github.com/RyuNen344/tink-kmm/commit/951bcb40635ef35090ecd387d97bc3bf21f5b9b5))
138 | * update config ([95f81ee](https://github.com/RyuNen344/tink-kmm/commit/95f81ee3e8a5d0c5cf48b0889f36124c02aac56b))
139 | * update config ([075b18b](https://github.com/RyuNen344/tink-kmm/commit/075b18b264c055ae06466c19bd49a43326d3ccaf))
140 |
--------------------------------------------------------------------------------
/tink/src/commonTest/kotlin/io/github/ryunen344/tink/daead/DeterministicAeadTest.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.daead
2 |
3 | import io.github.ryunen344.tink.JsonKeysetReader
4 | import io.github.ryunen344.tink.KeyTemplateSet
5 | import io.github.ryunen344.tink.KeysetHandleGenerator
6 | import io.github.ryunen344.tink.exception.GeneralSecurityException
7 | import io.github.ryunen344.tink.generateNew
8 | import io.github.ryunen344.tink.getPrimitive
9 | import io.github.ryunen344.tink.readClearText
10 | import io.github.ryunen344.tink.template
11 | import kotlin.test.BeforeTest
12 | import kotlin.test.Test
13 | import kotlin.test.assertContentEquals
14 | import kotlin.test.assertFailsWith
15 |
16 | class DeterministicAeadTest {
17 |
18 | @BeforeTest
19 | fun setup() {
20 | DeterministicAeadConfig.register()
21 | }
22 |
23 | @Test
24 | fun test_encrypt_then_success() {
25 | val handle = KeysetHandleGenerator.generateNew(KeyTemplateSet.AES256_SIV.template())
26 | val daead = handle.getPrimitive(DeterministicAead::class)
27 | val plaintext = "plaintext".encodeToByteArray()
28 | val associatedData = "associatedData".encodeToByteArray()
29 | val ciphertext = daead.encryptDeterministically(plaintext, associatedData)
30 | val decrypted = daead.decryptDeterministically(ciphertext, associatedData)
31 |
32 | assertContentEquals(plaintext, decrypted)
33 | assertContentEquals(ciphertext, daead.encryptDeterministically(plaintext, associatedData))
34 | }
35 |
36 | @Test
37 | fun test_encrypt_given_empty_then_success() {
38 | val handle = KeysetHandleGenerator.generateNew(KeyTemplateSet.AES256_SIV.template())
39 | val daead = handle.getPrimitive(DeterministicAead::class)
40 | val plaintext = "plaintext".encodeToByteArray()
41 | val associatedData = "associatedData".encodeToByteArray()
42 | val empty = "".encodeToByteArray()
43 |
44 | assertContentEquals(
45 | empty,
46 | daead.decryptDeterministically(daead.encryptDeterministically(empty, associatedData), associatedData)
47 | )
48 | assertContentEquals(
49 | plaintext,
50 | daead.decryptDeterministically(daead.encryptDeterministically(plaintext, empty), empty)
51 | )
52 | }
53 |
54 | @Test
55 | fun test_decrypt_given_invalid_associate_data_then_throws_error() {
56 | val handle = KeysetHandleGenerator.generateNew(KeyTemplateSet.AES256_SIV.template())
57 | val daead = handle.getPrimitive(DeterministicAead::class)
58 | val plaintext = "plaintext".encodeToByteArray()
59 | val associatedData = "associatedData".encodeToByteArray()
60 | val ciphertext = daead.encryptDeterministically(plaintext, associatedData)
61 | val invalid = "invalid".encodeToByteArray()
62 |
63 | assertFailsWith {
64 | daead.decryptDeterministically(ciphertext, invalid)
65 | }
66 | }
67 |
68 | @Test
69 | fun test_decrypt_given_invalid_data_then_throws_error() {
70 | val handle = KeysetHandleGenerator.generateNew(KeyTemplateSet.AES256_SIV.template())
71 | val daead = handle.getPrimitive(DeterministicAead::class)
72 | val associatedData = "associatedData".encodeToByteArray()
73 | val invalid = "invalid".encodeToByteArray()
74 |
75 | assertFailsWith {
76 | daead.decryptDeterministically(invalid, associatedData)
77 | }
78 | }
79 |
80 | @Test
81 | fun test_decrypt_given_empty_data_then_throws_error() {
82 | val handle = KeysetHandleGenerator.generateNew(KeyTemplateSet.AES256_SIV.template())
83 | val daead = handle.getPrimitive(DeterministicAead::class)
84 | val associatedData = "associatedData".encodeToByteArray()
85 | val empty = "".encodeToByteArray()
86 |
87 | assertFailsWith {
88 | daead.decryptDeterministically(empty, associatedData)
89 | }
90 | }
91 |
92 | @Test
93 | fun test_decrypt_given_other_daead_then_throws_error() {
94 | val handle = KeysetHandleGenerator.generateNew(KeyTemplateSet.AES256_SIV.template())
95 | val daead = handle.getPrimitive(DeterministicAead::class)
96 | val plaintext = "plaintext".encodeToByteArray()
97 | val associatedData = "associatedData".encodeToByteArray()
98 | val ciphertext = daead.encryptDeterministically(plaintext, associatedData)
99 |
100 | val otherHandle = KeysetHandleGenerator.generateNew(KeyTemplateSet.AES256_SIV.template())
101 | val otherDaead = otherHandle.getPrimitive(DeterministicAead::class)
102 |
103 | assertFailsWith {
104 | otherDaead.decryptDeterministically(ciphertext, associatedData)
105 | }
106 | }
107 |
108 | @Test
109 | fun test_encrypt_given_json_keyset_then_success() {
110 | val handle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_DAEAD_KEYSET))
111 | val daead = handle.getPrimitive(DeterministicAead::class)
112 | val plaintext: ByteArray = "plaintext".encodeToByteArray()
113 | val associatedData: ByteArray = "associatedData".encodeToByteArray()
114 | val ciphertext = daead.encryptDeterministically(plaintext, associatedData)
115 | val decrypted = daead.decryptDeterministically(ciphertext, associatedData)
116 | assertContentEquals(plaintext, decrypted)
117 | }
118 |
119 | @Test
120 | fun test_encrypt_given_multiple_keyset_then_success() {
121 | val plaintext: ByteArray = "plaintext".encodeToByteArray()
122 | val associatedData: ByteArray = "associatedData".encodeToByteArray()
123 |
124 | val primaryHandle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_DAEAD_KEYSET_WITH_MULTIPLE_KEYS))
125 | val primaryDaead = primaryHandle.getPrimitive(DeterministicAead::class)
126 | assertContentEquals(
127 | plaintext,
128 | primaryDaead.decryptDeterministically(
129 | primaryDaead.encryptDeterministically(plaintext, associatedData),
130 | associatedData
131 | )
132 | )
133 |
134 | // Also test that daead can decrypt ciphertexts encrypted with a non-primary key. We use
135 | // JSON_DAEAD_KEYSET to encrypt with the first key.
136 | val secondaryHandle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_DAEAD_KEYSET))
137 | val secondaryDaead = secondaryHandle.getPrimitive(DeterministicAead::class)
138 | assertContentEquals(
139 | plaintext,
140 | secondaryDaead.decryptDeterministically(
141 | secondaryDaead.encryptDeterministically(plaintext, associatedData),
142 | associatedData
143 | )
144 | )
145 | }
146 |
147 | private companion object {
148 | val JSON_DAEAD_KEYSET = """
149 | {
150 | "primaryKeyId": 961932622,
151 | "key": [
152 | {
153 | "keyData": {
154 | "typeUrl": "type.googleapis.com/google.crypto.tink.AesSivKey",
155 | "keyMaterialType": "SYMMETRIC",
156 | "value": "EkCJ9r5iwc5uxq5ugFyrHXh5dijTa7qalWUgZ8Gf08RxNd545FjtLMYL7ObcaFtCSkvV2+7u6F2DN+kqUjAfkf2W"
157 | },
158 | "outputPrefixType": "TINK",
159 | "keyId": 961932622,
160 | "status": "ENABLED"
161 | }
162 | ]
163 | }
164 | """.trimIndent()
165 |
166 | val JSON_DAEAD_KEYSET_WITH_MULTIPLE_KEYS = """
167 | {
168 | "primaryKeyId": 385749617,
169 | "key": [
170 | {
171 | "keyData": {
172 | "typeUrl": "type.googleapis.com/google.crypto.tink.AesSivKey",
173 | "keyMaterialType": "SYMMETRIC",
174 | "value": "EkCJ9r5iwc5uxq5ugFyrHXh5dijTa7qalWUgZ8Gf08RxNd545FjtLMYL7ObcaFtCSkvV2+7u6F2DN+kqUjAfkf2W"
175 | },
176 | "outputPrefixType": "TINK",
177 | "keyId": 961932622,
178 | "status": "ENABLED"
179 | },
180 | {
181 | "keyData": {
182 | "typeUrl": "type.googleapis.com/google.crypto.tink.AesSivKey",
183 | "value": "EkCGjyLCW8IOilSjFtkBOvpQoOA8ZsCAsFnCawU9ySiii3KefQkY4pGZcdlwJypOZem1/L+wPthYeCo4xmdq68hl",
184 | "keyMaterialType": "SYMMETRIC"
185 | },
186 | "status": "ENABLED",
187 | "keyId": 385749617,
188 | "outputPrefixType": "RAW"
189 | },
190 | {
191 | "keyData": {
192 | "typeUrl": "type.googleapis.com/google.crypto.tink.AesSivKey",
193 | "value": "EkCCo6EJBokVl3uTcZMA5iCtQArJliOlBBBfjmZ+IHdLGCatgWJ/tsUi2cmpw0o3yXyJaJbyT06kUCEP+GvFIjCQ",
194 | "keyMaterialType": "SYMMETRIC"
195 | },
196 | "status": "ENABLED",
197 | "keyId": 919668303,
198 | "outputPrefixType": "LEGACY"
199 | }
200 | ]
201 | }
202 | """.trimIndent()
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 | #
20 |
21 | ##############################################################################
22 | #
23 | # Gradle start up script for POSIX generated by Gradle.
24 | #
25 | # Important for running:
26 | #
27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
28 | # noncompliant, but you have some other compliant shell such as ksh or
29 | # bash, then to run this script, type that shell name before the whole
30 | # command line, like:
31 | #
32 | # ksh Gradle
33 | #
34 | # Busybox and similar reduced shells will NOT work, because this script
35 | # requires all of these POSIX shell features:
36 | # * functions;
37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
39 | # * compound commands having a testable exit status, especially «case»;
40 | # * various built-in commands including «command», «set», and «ulimit».
41 | #
42 | # Important for patching:
43 | #
44 | # (2) This script targets any POSIX shell, so it avoids extensions provided
45 | # by Bash, Ksh, etc; in particular arrays are avoided.
46 | #
47 | # The "traditional" practice of packing multiple parameters into a
48 | # space-separated string is a well documented source of bugs and security
49 | # problems, so this is (mostly) avoided, by progressively accumulating
50 | # options in "$@", and eventually passing that to Java.
51 | #
52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
54 | # see the in-line comments for details.
55 | #
56 | # There are tweaks for specific operating systems such as AIX, CygWin,
57 | # Darwin, MinGW, and NonStop.
58 | #
59 | # (3) This script is generated from the Groovy template
60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
61 | # within the Gradle project.
62 | #
63 | # You can find Gradle at https://github.com/gradle/gradle/.
64 | #
65 | ##############################################################################
66 |
67 | # Attempt to set APP_HOME
68 |
69 | # Resolve links: $0 may be a link
70 | app_path=$0
71 |
72 | # Need this for daisy-chained symlinks.
73 | while
74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
75 | [ -h "$app_path" ]
76 | do
77 | ls=$( ls -ld "$app_path" )
78 | link=${ls#*' -> '}
79 | case $link in #(
80 | /*) app_path=$link ;; #(
81 | *) app_path=$APP_HOME$link ;;
82 | esac
83 | done
84 |
85 | # This is normally unused
86 | # shellcheck disable=SC2034
87 | APP_BASE_NAME=${0##*/}
88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
90 | ' "$PWD" ) || exit
91 |
92 | # Use the maximum available, or set MAX_FD != -1 to use that value.
93 | MAX_FD=maximum
94 |
95 | warn () {
96 | echo "$*"
97 | } >&2
98 |
99 | die () {
100 | echo
101 | echo "$*"
102 | echo
103 | exit 1
104 | } >&2
105 |
106 | # OS specific support (must be 'true' or 'false').
107 | cygwin=false
108 | msys=false
109 | darwin=false
110 | nonstop=false
111 | case "$( uname )" in #(
112 | CYGWIN* ) cygwin=true ;; #(
113 | Darwin* ) darwin=true ;; #(
114 | MSYS* | MINGW* ) msys=true ;; #(
115 | NONSTOP* ) nonstop=true ;;
116 | esac
117 |
118 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
119 |
120 |
121 | # Determine the Java command to use to start the JVM.
122 | if [ -n "$JAVA_HOME" ] ; then
123 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
124 | # IBM's JDK on AIX uses strange locations for the executables
125 | JAVACMD=$JAVA_HOME/jre/sh/java
126 | else
127 | JAVACMD=$JAVA_HOME/bin/java
128 | fi
129 | if [ ! -x "$JAVACMD" ] ; then
130 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
131 |
132 | Please set the JAVA_HOME variable in your environment to match the
133 | location of your Java installation."
134 | fi
135 | else
136 | JAVACMD=java
137 | if ! command -v java >/dev/null 2>&1
138 | then
139 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
140 |
141 | Please set the JAVA_HOME variable in your environment to match the
142 | location of your Java installation."
143 | fi
144 | fi
145 |
146 | # Increase the maximum file descriptors if we can.
147 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
148 | case $MAX_FD in #(
149 | max*)
150 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
151 | # shellcheck disable=SC2039,SC3045
152 | MAX_FD=$( ulimit -H -n ) ||
153 | warn "Could not query maximum file descriptor limit"
154 | esac
155 | case $MAX_FD in #(
156 | '' | soft) :;; #(
157 | *)
158 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
159 | # shellcheck disable=SC2039,SC3045
160 | ulimit -n "$MAX_FD" ||
161 | warn "Could not set maximum file descriptor limit to $MAX_FD"
162 | esac
163 | fi
164 |
165 | # Collect all arguments for the java command, stacking in reverse order:
166 | # * args from the command line
167 | # * the main class name
168 | # * -classpath
169 | # * -D...appname settings
170 | # * --module-path (only if needed)
171 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
172 |
173 | # For Cygwin or MSYS, switch paths to Windows format before running java
174 | if "$cygwin" || "$msys" ; then
175 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
176 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
177 |
178 | JAVACMD=$( cygpath --unix "$JAVACMD" )
179 |
180 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
181 | for arg do
182 | if
183 | case $arg in #(
184 | -*) false ;; # don't mess with options #(
185 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
186 | [ -e "$t" ] ;; #(
187 | *) false ;;
188 | esac
189 | then
190 | arg=$( cygpath --path --ignore --mixed "$arg" )
191 | fi
192 | # Roll the args list around exactly as many times as the number of
193 | # args, so each arg winds up back in the position where it started, but
194 | # possibly modified.
195 | #
196 | # NB: a `for` loop captures its iteration list before it begins, so
197 | # changing the positional parameters here affects neither the number of
198 | # iterations, nor the values presented in `arg`.
199 | shift # remove old arg
200 | set -- "$@" "$arg" # push replacement arg
201 | done
202 | fi
203 |
204 |
205 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
206 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
207 |
208 | # Collect all arguments for the java command:
209 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
210 | # and any embedded shellness will be escaped.
211 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
212 | # treated as '${Hostname}' itself on the command line.
213 |
214 | set -- \
215 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
216 | -classpath "$CLASSPATH" \
217 | org.gradle.wrapper.GradleWrapperMain \
218 | "$@"
219 |
220 | # Stop when "xargs" is not available.
221 | if ! command -v xargs >/dev/null 2>&1
222 | then
223 | die "xargs is not available"
224 | fi
225 |
226 | # Use "xargs" to parse quoted args.
227 | #
228 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
229 | #
230 | # In Bash we could simply go:
231 | #
232 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
233 | # set -- "${ARGS[@]}" "$@"
234 | #
235 | # but POSIX shell has neither arrays nor command substitution, so instead we
236 | # post-process each arg (as a line of input to sed) to backslash-escape any
237 | # character that might be a shell metacharacter, then use eval to reverse
238 | # that process (while maintaining the separation between arguments), and wrap
239 | # the whole thing up as a single "set" statement.
240 | #
241 | # This will of course break if any of these variables contains a newline or
242 | # an unmatched quote.
243 | #
244 |
245 | eval "set -- $(
246 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
247 | xargs -n1 |
248 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
249 | tr '\n' ' '
250 | )" '"$@"'
251 |
252 | exec "$JAVACMD" "$@"
253 |
--------------------------------------------------------------------------------
/tink/src/commonTest/kotlin/io/github/ryunen344/tink/mac/MacTest.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.mac
2 |
3 | import io.github.ryunen344.tink.JsonKeysetReader
4 | import io.github.ryunen344.tink.KeyTemplateSet
5 | import io.github.ryunen344.tink.KeysetHandleGenerator
6 | import io.github.ryunen344.tink.daead.DeterministicAeadConfig
7 | import io.github.ryunen344.tink.daead.register
8 | import io.github.ryunen344.tink.exception.GeneralSecurityException
9 | import io.github.ryunen344.tink.generateNew
10 | import io.github.ryunen344.tink.getPrimitive
11 | import io.github.ryunen344.tink.readClearText
12 | import io.github.ryunen344.tink.template
13 | import kotlin.test.BeforeTest
14 | import kotlin.test.Test
15 | import kotlin.test.assertFailsWith
16 |
17 | class MacTest {
18 |
19 | private fun verify(
20 | set: KeyTemplateSet,
21 | data: ByteArray = "data".encodeToByteArray(),
22 | ) {
23 | val handle = KeysetHandleGenerator.generateNew(set.template())
24 | val mac = handle.getPrimitive(Mac::class)
25 | val tag = mac.computeMac(data)
26 | mac.verifyMac(tag, data)
27 | }
28 |
29 | private fun invalid(
30 | set: KeyTemplateSet,
31 | ) {
32 | val handle = KeysetHandleGenerator.generateNew(set.template())
33 | val mac = handle.getPrimitive(Mac::class)
34 | val data = "data".encodeToByteArray()
35 | val tag = mac.computeMac(data)
36 |
37 | val invalid = "invalid".encodeToByteArray()
38 | val empty = "".encodeToByteArray()
39 |
40 | assertFailsWith { mac.verifyMac(invalid, data) }
41 | assertFailsWith { mac.verifyMac(tag, invalid) }
42 | assertFailsWith { mac.verifyMac(empty, data) }
43 | assertFailsWith { mac.verifyMac(tag, empty) }
44 | }
45 |
46 | private fun other(
47 | set: KeyTemplateSet,
48 | ) {
49 | val handle = KeysetHandleGenerator.generateNew(set.template())
50 | val mac = handle.getPrimitive(Mac::class)
51 | val data = "data".encodeToByteArray()
52 | val tag = mac.computeMac(data)
53 |
54 | val otherHandle = KeysetHandleGenerator.generateNew(set.template())
55 | val otherMac = otherHandle.getPrimitive(Mac::class)
56 |
57 | assertFailsWith { otherMac.verifyMac(tag, data) }
58 | }
59 |
60 | @BeforeTest
61 | fun setup() {
62 | MacConfig.register()
63 | }
64 |
65 | @Test
66 | fun test_verify_HMAC_SHA256_128BITTAG_then_success() = verify(KeyTemplateSet.HMAC_SHA256_128BITTAG)
67 |
68 | @Test
69 | fun test_verify_HMAC_SHA256_128BITTAG_given_empty_data_then_success() =
70 | verify(KeyTemplateSet.HMAC_SHA256_128BITTAG, "".encodeToByteArray())
71 |
72 | @Test
73 | fun test_verify_HMAC_SHA256_128BITTAG_given_invalid_data_then_throw_error() =
74 | invalid(KeyTemplateSet.HMAC_SHA256_128BITTAG)
75 |
76 | @Test
77 | fun test_verify_HMAC_SHA256_128BITTAG_when_verify_other_keyset_then_throw_error() =
78 | other(KeyTemplateSet.HMAC_SHA256_128BITTAG)
79 |
80 | @Test
81 | fun test_verify_HMAC_SHA256_256BITTAG_then_success() = verify(KeyTemplateSet.HMAC_SHA256_256BITTAG)
82 |
83 | @Test
84 | fun test_verify_HMAC_SHA256_256BITTAG_then_given_empty_data_then_success() =
85 | verify(KeyTemplateSet.HMAC_SHA256_256BITTAG, "".encodeToByteArray())
86 |
87 | @Test
88 | fun test_verify_HMAC_SHA256_256BITTAG_then_given_invalid_data_then_throw_error() =
89 | invalid(KeyTemplateSet.HMAC_SHA256_256BITTAG)
90 |
91 | @Test
92 | fun test_verify_HMAC_SHA256_256BITTAG_then_when_verify_other_keyset_then_throw_error() =
93 | other(KeyTemplateSet.HMAC_SHA256_256BITTAG)
94 |
95 | @Test
96 | fun test_verify_HMAC_SHA512_256BITTAG_then_success() = verify(KeyTemplateSet.HMAC_SHA512_256BITTAG)
97 |
98 | @Test
99 | fun test_verify_HMAC_SHA512_256BITTAG_then_given_empty_data_then_success() =
100 | verify(KeyTemplateSet.HMAC_SHA512_256BITTAG, "".encodeToByteArray())
101 |
102 | @Test
103 | fun test_verify_HMAC_SHA512_256BITTAG_then_given_invalid_data_then_throw_error() =
104 | invalid(KeyTemplateSet.HMAC_SHA512_256BITTAG)
105 |
106 | @Test
107 | fun test_verify_HMAC_SHA512_256BITTAG_when_verify_other_keyset_then_throw_error() =
108 | other(KeyTemplateSet.HMAC_SHA512_256BITTAG)
109 |
110 | @Test
111 | fun test_verify_HMAC_SHA512_512BITTAG_then_success() = verify(KeyTemplateSet.HMAC_SHA512_512BITTAG)
112 |
113 | @Test
114 | fun test_verify_HMAC_SHA512_512BITTAG_then_given_empty_data_then_success() =
115 | verify(KeyTemplateSet.HMAC_SHA512_512BITTAG, "".encodeToByteArray())
116 |
117 | @Test
118 | fun test_verify_HMAC_SHA512_512BITTAG_then_given_invalid_data_then_throw_error() =
119 | invalid(KeyTemplateSet.HMAC_SHA512_512BITTAG)
120 |
121 | @Test
122 | fun test_verify_HMAC_SHA512_512BITTAG_when_verify_other_keyset_then_throw_error() =
123 | other(KeyTemplateSet.HMAC_SHA512_512BITTAG)
124 |
125 | @Test
126 | fun test_verify_AES_CMAC_then_success() = verify(KeyTemplateSet.AES_CMAC)
127 |
128 | @Test
129 | fun test_verify_AES_CMAC_then_given_empty_data_then_success() =
130 | verify(KeyTemplateSet.AES_CMAC, "".encodeToByteArray())
131 |
132 | @Test
133 | fun test_verify_AES_CMAC_then_given_invalid_data_then_throw_error() = invalid(KeyTemplateSet.AES_CMAC)
134 |
135 | @Test
136 | fun test_verify_AES_CMAC_when_verify_other_keyset_then_throw_error() = other(KeyTemplateSet.AES_CMAC)
137 |
138 | @Test
139 | fun test_verify_given_json_keyset_then_success() {
140 | val handle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_MAC_KEYSET))
141 | val mac = handle.getPrimitive(Mac::class)
142 | val data = "data".encodeToByteArray()
143 | val tag = mac.computeMac(data)
144 | mac.verifyMac(tag, data)
145 | }
146 |
147 | @Test
148 | fun test_verify_given_multiple_keyset_then_success() {
149 | val primaryHandle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_MAC_KEYSET_WITH_MULTIPLE_KEYS))
150 | val primaryMac = primaryHandle.getPrimitive(Mac::class)
151 | val data = "data".encodeToByteArray()
152 | val tag = primaryMac.computeMac(data)
153 | primaryMac.verifyMac(tag, data)
154 |
155 | // Also test that mac can verify tags computed with a non-primary key. We use
156 | // JSON_MAC_KEYSET to compute a tag with the first key.
157 | val secondaryHandle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_MAC_KEYSET))
158 | val secondaryMac = secondaryHandle.getPrimitive(Mac::class)
159 | val secondaryTag = secondaryMac.computeMac(data)
160 | primaryMac.verifyMac(secondaryTag, data)
161 | }
162 |
163 | @Test
164 | fun test_getPrimitive_given_NonMacKeyset_then_throws_error() {
165 | DeterministicAeadConfig.register()
166 | val handle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_DAEAD_KEYSET))
167 | assertFailsWith {
168 | handle.getPrimitive(Mac::class)
169 | }
170 | }
171 |
172 | private companion object {
173 | val JSON_MAC_KEYSET = """
174 | {
175 | "primaryKeyId": 207420876,
176 | "key": [
177 | {
178 | "keyData": {
179 | "typeUrl": "type.googleapis.com/google.crypto.tink.HmacKey",
180 | "value": "GiAPii+kxtLpvCARQpftFLt4R+O6ARsyhTR7SkCCGt0bHRIEEBAIAw==",
181 | "keyMaterialType": "SYMMETRIC"
182 | },
183 | "status": "ENABLED",
184 | "keyId": 207420876,
185 | "outputPrefixType": "TINK"
186 | }
187 | ]
188 | }
189 | """.trimIndent()
190 |
191 | val JSON_MAC_KEYSET_WITH_MULTIPLE_KEYS = """
192 | {
193 | "primaryKeyId": 2054715504,
194 | "key": [
195 | {
196 | "keyData": {
197 | "typeUrl": "type.googleapis.com/google.crypto.tink.HmacKey",
198 | "value": "GiAPii+kxtLpvCARQpftFLt4R+O6ARsyhTR7SkCCGt0bHRIEEBAIAw==",
199 | "keyMaterialType": "SYMMETRIC"
200 | },
201 | "status": "ENABLED",
202 | "keyId": 207420876,
203 | "outputPrefixType": "TINK"
204 | },
205 | {
206 | "keyData": {
207 | "typeUrl": "type.googleapis.com/google.crypto.tink.AesCmacKey",
208 | "value": "GgIIEBIgLaZ/6QXYeqZB8F4zHTRJU5k6TF5xvlSX9ZVLVA09UY0=",
209 | "keyMaterialType": "SYMMETRIC"
210 | },
211 | "status": "ENABLED",
212 | "keyId": 2054715504,
213 | "outputPrefixType": "RAW"
214 | },
215 | {
216 | "keyData": {
217 | "typeUrl": "type.googleapis.com/google.crypto.tink.HmacKey",
218 | "value": "GkCCIGYpFz3mj8wnTH3Ca81F1sQ7JEMxoE8B2nKiND7LrKfbaUx+/qqDXUPVjkzC9XdbjsaEqc9yI+RKyITef+eUEgQQQAgE",
219 | "keyMaterialType": "SYMMETRIC"
220 | },
221 | "status": "ENABLED",
222 | "keyId": 1540103625,
223 | "outputPrefixType": "LEGACY"
224 | },
225 | {
226 | "keyData": {
227 | "typeUrl": "type.googleapis.com/google.crypto.tink.HmacKey",
228 | "value": "GkA8u6JKtInsySJDZO4j6TLoIvLuGAeAZHDZoTlST0aZZ8gZZViHogzWTqti2Vlp3ccy+OdN6lhMxSiphcPaR5OiEgQQIAgE",
229 | "keyMaterialType": "SYMMETRIC"
230 | },
231 | "status": "ENABLED",
232 | "keyId": 570162478,
233 | "outputPrefixType": "CRUNCHY"
234 | }
235 | ]
236 | }
237 | """.trimIndent()
238 |
239 | val JSON_DAEAD_KEYSET = """
240 | {
241 | "primaryKeyId": 961932622,
242 | "key": [
243 | {
244 | "keyData": {
245 | "typeUrl": "type.googleapis.com/google.crypto.tink.AesSivKey",
246 | "keyMaterialType": "SYMMETRIC",
247 | "value": "EkCJ9r5iwc5uxq5ugFyrHXh5dijTa7qalWUgZ8Gf08RxNd545FjtLMYL7ObcaFtCSkvV2+7u6F2DN+kqUjAfkf2W"
248 | },
249 | "outputPrefixType": "TINK",
250 | "keyId": 961932622,
251 | "status": "ENABLED"
252 | }
253 | ]
254 | }
255 | """.trimIndent()
256 | }
257 | }
258 |
--------------------------------------------------------------------------------
/tink/src/commonTest/kotlin/io/github/ryunen344/tink/aead/AeadTest.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.aead
2 |
3 | import io.github.ryunen344.tink.JsonKeysetReader
4 | import io.github.ryunen344.tink.KeyTemplate
5 | import io.github.ryunen344.tink.KeyTemplateSet
6 | import io.github.ryunen344.tink.KeysetHandleGenerator
7 | import io.github.ryunen344.tink.daead.DeterministicAead
8 | import io.github.ryunen344.tink.daead.DeterministicAeadConfig
9 | import io.github.ryunen344.tink.daead.register
10 | import io.github.ryunen344.tink.exception.GeneralSecurityException
11 | import io.github.ryunen344.tink.generateNew
12 | import io.github.ryunen344.tink.getPrimitive
13 | import io.github.ryunen344.tink.readClearText
14 | import io.github.ryunen344.tink.template
15 | import kotlin.test.BeforeTest
16 | import kotlin.test.Test
17 | import kotlin.test.assertContentEquals
18 | import kotlin.test.assertFailsWith
19 |
20 | class AeadTest {
21 |
22 | @BeforeTest
23 | fun setup() {
24 | AeadConfig.register()
25 | }
26 |
27 | private fun encrypt(
28 | template: KeyTemplate,
29 | plaintext: ByteArray = "plaintext".encodeToByteArray(),
30 | associatedData: ByteArray = "associatedData".encodeToByteArray(),
31 | ) {
32 | val handle = KeysetHandleGenerator.generateNew(template)
33 | val aead = handle.getPrimitive(Aead::class)
34 |
35 | val ciphertext = aead.encrypt(plaintext, associatedData)
36 | val decrypted = aead.decrypt(ciphertext, associatedData)
37 |
38 | assertContentEquals(plaintext, decrypted)
39 | }
40 |
41 | private fun decryptInvalidAssociateData(
42 | template: KeyTemplate,
43 | ) {
44 | val handle = KeysetHandleGenerator.generateNew(template)
45 | val aead = handle.getPrimitive(Aead::class)
46 | val plaintext = "plaintext".encodeToByteArray()
47 | val invalid = "invalid".encodeToByteArray()
48 |
49 | assertFailsWith {
50 | aead.decrypt(plaintext, invalid)
51 | }
52 | }
53 |
54 | private fun decryptInvalidData(
55 | template: KeyTemplate,
56 | ) {
57 | val handle = KeysetHandleGenerator.generateNew(template)
58 | val aead = handle.getPrimitive(Aead::class)
59 | val associatedData = "associatedData".encodeToByteArray()
60 | val invalid = "invalid".encodeToByteArray()
61 |
62 | assertFailsWith {
63 | aead.decrypt(invalid, associatedData)
64 | }
65 | }
66 |
67 | @Test
68 | fun test_encrypt_AES128_GCM_then_success() = encrypt(KeyTemplateSet.AES128_GCM.template())
69 |
70 | @Test
71 | fun test_encrypt_AES128_GCM_given_empty_then_success() =
72 | encrypt(KeyTemplateSet.AES128_GCM.template(), "".encodeToByteArray())
73 |
74 | @Test
75 | fun test_encrypt_AES128_GCM_given_invalid_associate_data_then_throw_error() =
76 | decryptInvalidAssociateData(KeyTemplateSet.AES128_GCM.template())
77 |
78 | @Test
79 | fun test_encrypt_AES128_GCM_given_invalid_data_then_throw_error() =
80 | decryptInvalidData(KeyTemplateSet.AES128_GCM.template())
81 |
82 | @Test
83 | fun test_encrypt_AES128_GCM_RAW_then_success() = encrypt(KeyTemplateSet.AES128_GCM_RAW.template())
84 |
85 | @Test
86 | fun test_encrypt_AES128_GCM_RAW_given_empty_then_success() =
87 | encrypt(KeyTemplateSet.AES128_GCM_RAW.template(), "".encodeToByteArray())
88 |
89 | @Test
90 | fun test_encrypt_AES128_GCM_RAW_given_invalid_associate_data_then_throw_error() =
91 | decryptInvalidAssociateData(KeyTemplateSet.AES128_GCM_RAW.template())
92 |
93 | @Test
94 | fun test_encrypt_AES128_GCM_RAW_given_invalid_data_then_throw_error() =
95 | decryptInvalidData(KeyTemplateSet.AES128_GCM_RAW.template())
96 |
97 | @Test
98 | fun test_encrypt_AES256_GCM_then_success() = encrypt(KeyTemplateSet.AES256_GCM.template())
99 |
100 | @Test
101 | fun test_encrypt_AES256_GCM_given_empty_then_success() =
102 | encrypt(KeyTemplateSet.AES256_GCM.template(), "".encodeToByteArray())
103 |
104 | @Test
105 | fun test_encrypt_AES256_GCM_given_invalid_associate_data_then_throw_error() =
106 | decryptInvalidAssociateData(KeyTemplateSet.AES256_GCM.template())
107 |
108 | @Test
109 | fun test_encrypt_AES256_GCM_given_invalid_data_then_throw_error() =
110 | decryptInvalidData(KeyTemplateSet.AES256_GCM.template())
111 |
112 | @Test
113 | fun test_encrypt_AES256_GCM_RAW_then_success() = encrypt(KeyTemplateSet.AES256_GCM_RAW.template())
114 |
115 | @Test
116 | fun test_encrypt_AES256_GCM_RAW_given_empty_then_success() =
117 | encrypt(KeyTemplateSet.AES256_GCM_RAW.template(), "".encodeToByteArray())
118 |
119 | @Test
120 | fun test_encrypt_AES256_GCM_RAW_given_invalid_associate_data_then_throw_error() =
121 | decryptInvalidAssociateData(KeyTemplateSet.AES256_GCM_RAW.template())
122 |
123 | @Test
124 | fun test_encrypt_AES256_GCM_RAW_given_invalid_data_then_throw_error() =
125 | decryptInvalidData(KeyTemplateSet.AES256_GCM_RAW.template())
126 |
127 | @Test
128 | fun test_encrypt_AES128_CTR_HMAC_SHA256_then_success() = encrypt(KeyTemplateSet.AES128_CTR_HMAC_SHA256.template())
129 |
130 | @Test
131 | fun test_encrypt_AES128_CTR_HMAC_SHA256_given_empty_then_success() =
132 | encrypt(KeyTemplateSet.AES128_CTR_HMAC_SHA256.template(), "".encodeToByteArray())
133 |
134 | @Test
135 | fun test_encrypt_AES128_CTR_HMAC_SHA256_given_invalid_associate_data_then_throw_error() =
136 | decryptInvalidAssociateData(KeyTemplateSet.AES128_CTR_HMAC_SHA256.template())
137 |
138 | @Test
139 | fun test_encrypt_AES128_CTR_HMAC_SHA256_given_invalid_data_then_throw_error() =
140 | decryptInvalidData(KeyTemplateSet.AES128_CTR_HMAC_SHA256.template())
141 |
142 | @Test
143 | fun test_encrypt_AES256_CTR_HMAC_SHA256_then_success() = encrypt(KeyTemplateSet.AES256_CTR_HMAC_SHA256.template())
144 |
145 | @Test
146 | fun test_encrypt_AES256_CTR_HMAC_SHA256_given_empty_then_success() =
147 | encrypt(KeyTemplateSet.AES256_CTR_HMAC_SHA256.template(), "".encodeToByteArray())
148 |
149 | @Test
150 | fun test_encrypt_AES256_CTR_HMAC_SHA256_given_invalid_associate_data_then_throw_error() =
151 | decryptInvalidAssociateData(KeyTemplateSet.AES256_CTR_HMAC_SHA256.template())
152 |
153 | @Test
154 | fun test_encrypt_AES256_CTR_HMAC_SHA256_given_invalid_data_then_throw_error() =
155 | decryptInvalidData(KeyTemplateSet.AES256_CTR_HMAC_SHA256.template())
156 |
157 | @Test
158 | fun test_encrypt_AES128_EAX_then_success() = encrypt(KeyTemplateSet.AES128_EAX.template())
159 |
160 | @Test
161 | fun test_encrypt_AES128_EAX_then_given_empty_success() =
162 | encrypt(KeyTemplateSet.AES128_EAX.template(), "".encodeToByteArray())
163 |
164 | @Test
165 | fun test_encrypt_AES128_EAX_given_invalid_associate_data_then_throw_error() =
166 | decryptInvalidAssociateData(KeyTemplateSet.AES128_EAX.template())
167 |
168 | @Test
169 | fun test_encrypt_AES128_EAX_given_invalid_data_then_throw_error() =
170 | decryptInvalidData(KeyTemplateSet.AES128_EAX.template())
171 |
172 | @Test
173 | fun test_encrypt_AES256_EAX_then_success() = encrypt(KeyTemplateSet.AES256_EAX.template())
174 |
175 | @Test
176 | fun test_encrypt_AES256_EAX_then_given_empty_success() =
177 | encrypt(KeyTemplateSet.AES256_EAX.template(), "".encodeToByteArray())
178 |
179 | @Test
180 | fun test_encrypt_AES256_EAX_given_invalid_associate_data_then_throw_error() =
181 | decryptInvalidAssociateData(KeyTemplateSet.AES256_EAX.template())
182 |
183 | @Test
184 | fun test_encrypt_AES256_EAX_given_invalid_data_then_throw_error() =
185 | decryptInvalidData(KeyTemplateSet.AES256_EAX.template())
186 |
187 | @Test
188 | fun test_encrypt_XCHACHA20_POLY1305_then_success() = encrypt(KeyTemplateSet.XCHACHA20_POLY1305.template())
189 |
190 | @Test
191 | fun test_encrypt_XCHACHA20_POLY1305_given_empty_then_success() =
192 | encrypt(KeyTemplateSet.XCHACHA20_POLY1305.template(), "".encodeToByteArray())
193 |
194 | @Test
195 | fun test_encrypt_XCHACHA20_POLY1305_given_invalid_associate_data_then_throw_error() =
196 | decryptInvalidAssociateData(KeyTemplateSet.XCHACHA20_POLY1305.template())
197 |
198 | @Test
199 | fun test_encrypt_XCHACHA20_POLY1305_given_invalid_data_then_throw_error() =
200 | decryptInvalidData(KeyTemplateSet.XCHACHA20_POLY1305.template())
201 |
202 | @Test
203 | fun test_encrypt_given_json_keyset_then_success() {
204 | val handle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_AEAD_KEYSET))
205 | val aead = handle.getPrimitive(Aead::class)
206 | val plaintext: ByteArray = "plaintext".encodeToByteArray()
207 | val associatedData: ByteArray = "associatedData".encodeToByteArray()
208 | val ciphertext = aead.encrypt(plaintext, associatedData)
209 | val decrypted = aead.decrypt(ciphertext, associatedData)
210 | assertContentEquals(plaintext, decrypted)
211 | }
212 |
213 | @Test
214 | fun test_encrypt_given_multiple_keyset_then_success() {
215 | val plaintext: ByteArray = "plaintext".encodeToByteArray()
216 | val associatedData: ByteArray = "associatedData".encodeToByteArray()
217 |
218 | val primaryHandle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_AEAD_KEYSET_WITH_MULTIPLE_KEYS))
219 | val primaryAead = primaryHandle.getPrimitive(Aead::class)
220 | assertContentEquals(
221 | plaintext,
222 | primaryAead.decrypt(
223 | primaryAead.encrypt(plaintext, associatedData),
224 | associatedData
225 | )
226 | )
227 |
228 | // Also test that aead can decrypt ciphertexts encrypted with a non-primary key. We use
229 | // JSON_AEAD_KEYSET to encrypt with the first key.
230 | val subHandle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_AEAD_KEYSET))
231 | val subAead = subHandle.getPrimitive(Aead::class)
232 | assertContentEquals(
233 | plaintext,
234 | subAead.decrypt(
235 | subAead.encrypt(plaintext, associatedData),
236 | associatedData
237 | )
238 | )
239 | }
240 |
241 | @Test
242 | fun test_getPrimitive_given_NonAeadKeyset_then_throws_error() {
243 | DeterministicAeadConfig.register()
244 | val handle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_DAEAD_KEYSET))
245 | handle.getPrimitive(DeterministicAead::class)
246 |
247 | assertFailsWith {
248 | handle.getPrimitive(Aead::class)
249 | }
250 | }
251 |
252 | private companion object {
253 | val JSON_AEAD_KEYSET = """
254 | {
255 | "primaryKeyId": 42818733,
256 | "key": [
257 | {
258 | "keyData": {
259 | "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey",
260 | "keyMaterialType": "SYMMETRIC",
261 | "value": "GhCC74uJ+2f4qlpaHwR4ylNQ"
262 | },
263 | "outputPrefixType": "TINK",
264 | "keyId": 42818733,
265 | "status": "ENABLED"
266 | }
267 | ]
268 | }
269 | """.trimIndent()
270 |
271 | val JSON_AEAD_KEYSET_WITH_MULTIPLE_KEYS = """
272 | {
273 | "primaryKeyId": 365202604,
274 | "key": [
275 | {
276 | "keyData": {
277 | "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey",
278 | "keyMaterialType": "SYMMETRIC",
279 | "value": "GhCC74uJ+2f4qlpaHwR4ylNQ"
280 | },
281 | "outputPrefixType": "TINK",
282 | "keyId": 42818733,
283 | "status": "ENABLED"
284 | },
285 | {
286 | "keyData": {
287 | "typeUrl": "type.googleapis.com/google.crypto.tink.AesEaxKey",
288 | "keyMaterialType": "SYMMETRIC",
289 | "value": "EgIIEBogU4nieBfIeJHBrhC+TjezFgxkkuhQHbyWkUMH+7atLxI="
290 | },
291 | "outputPrefixType": "RAW",
292 | "keyId": 365202604,
293 | "status": "ENABLED"
294 | },
295 | {
296 | "keyData": {
297 | "typeUrl": "type.googleapis.com/google.crypto.tink.AesCtrHmacAeadKey",
298 | "keyMaterialType": "SYMMETRIC",
299 | "value": "GigaIMttlipP/JvQOpIB0NYhDPoLgWBiIxmtaWbSPa2TeQOmEgQQEAgDEhYaEPcCMmPLgRGhmMmSC4AJ1CESAggQ"
300 | },
301 | "outputPrefixType": "LEGACY",
302 | "keyId": 277095770,
303 | "status": "ENABLED"
304 | }
305 | ]
306 | }
307 | """.trimIndent()
308 |
309 | val JSON_DAEAD_KEYSET = """
310 | {
311 | "primaryKeyId": 961932622,
312 | "key": [
313 | {
314 | "keyData": {
315 | "typeUrl": "type.googleapis.com/google.crypto.tink.AesSivKey",
316 | "keyMaterialType": "SYMMETRIC",
317 | "value": "EkCJ9r5iwc5uxq5ugFyrHXh5dijTa7qalWUgZ8Gf08RxNd545FjtLMYL7ObcaFtCSkvV2+7u6F2DN+kqUjAfkf2W"
318 | },
319 | "outputPrefixType": "TINK",
320 | "keyId": 961932622,
321 | "status": "ENABLED"
322 | }
323 | ]
324 | }
325 | """.trimIndent()
326 | }
327 | }
328 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tink-KMM
2 |
3 | [](https://maven-badges.herokuapp.com/maven-central/io.github.ryunen344.tink/tink)
4 | [](https://opensource.org/licenses/MIT)
5 | [](https://github.com/RyuNen344/tink-kmm/actions/workflows/test.yml)
6 | [](https://codecov.io/gh/RyuNen344/tink-kmm)
7 | 
8 | 
9 | 
10 |
11 | This is a repositoy of [Google/Tink](https://github.com/google/tink) Mapper for KMM(Kotlin Multiplatform Mobile)
12 | This wrapper library allows you to use Tink Primitive Encryption in your Kotlin Multiplatform Mobile project.
13 |
14 | ## Why this library?
15 |
16 | KMM currently does not have a Crypto Library that can be used as a de facto standard.
17 | Of course, we can implement encryption using the expect/actual modifier.
18 | But, CCCrypto has a limitation of supported algorithm.(e.g, it can not be use AES-GCM)
19 |
20 | ## Tink Primitives
21 |
22 | | **Primitive** | **Interfaces** |
23 | | ---------------------------------------------------- | ------------------------------ |
24 | | Authenticated Encryption with Associated Data (AEAD) | AEAD |
25 | | _Streaming_ AEAD | StreamingAEAD |
26 | | _Deterministic_ AEAD | DeterministicAEAD |
27 | | Message Authentication Code (MAC) | MAC |
28 | | Pseudo Random Function Family (PRF) | Prf, PrfSet |
29 | | Hybrid encryption | HybridEncrypt, HybridDecrypt |
30 | | Digital signatures | PublicKeySign, PublicKeyVerify |
31 |
32 | ### Supported primitives and their implementations
33 |
34 | #### Primitives supported by language
35 |
36 | | **Primitive** | **Java** | **Objective-C** | **Tink-KMM** |
37 | | ------------------ | :------: | :-------------: | :----------: |
38 | | AEAD | yes | yes | yes |
39 | | Streaming AEAD | yes | **no** | **no** |
40 | | Deterministic AEAD | yes | yes | yes |
41 | | MAC | yes | yes | yes |
42 | | PRF | yes | **no** | **no** |
43 | | Digital signatures | yes | yes | yes |
44 | | Hybrid encryption | yes | yes | yes |
45 |
46 | #### Primitive implementations supported by language
47 |
48 | | **Primitive** | **Implementation** | **Java** | **Objective-C** | **Tink-KMM** |
49 | | ------------------ | ------------------------------------- | :------: | :-------------: | :----------: |
50 | | AEAD | AES-GCM | yes | yes | yes |
51 | | | AES-GCM-SIV | yes | **no** | **no** |
52 | | | AES-CTR-HMAC | yes | yes | yes |
53 | | | AES-EAX | yes | yes | yes |
54 | | | KMS Envelope | yes | **no** | **no** |
55 | | | CHACHA20-POLY1305 | yes | **no** | **no** |
56 | | | XCHACHA20-POLY1305 | yes | yes | yes |
57 | | Streaming AEAD | AES-GCM-HKDF-STREAMING | yes | **no** | **no** |
58 | | | AES-CTR-HMAC-STREAMING | yes | **no** | **no** |
59 | | Deterministic AEAD | AES-SIV | yes | yes | yes |
60 | | MAC | HMAC-SHA2 | yes | yes | yes |
61 | | | AES-CMAC | yes | yes | yes |
62 | | PRF | HKDF-SHA2 | yes | **no** | **no** |
63 | | | HMAC-SHA2 | yes | **no** | **no** |
64 | | | AES-CMAC | yes | **no** | **no** |
65 | | Digital Signatures | ECDSA over NIST curves | yes | yes | yes |
66 | | | Ed25519 | yes | yes | yes |
67 | | | RSA-SSA-PKCS1 | yes | yes | yes |
68 | | | RSA-SSA-PSS | yes | yes | yes |
69 | | Hybrid Encryption | HPKE | yes | **no** | **no** |
70 | | | ECIES with AEAD and HKDF | yes | yes | yes |
71 | | | ECIES with DeterministicAEAD and HKDF | yes | **no** | **no** |
72 |
73 | ## Compatibility
74 |
75 | | **Version** | **Kotlin** | **Tink-android** | **Tink-ObjC** |
76 | |---------------|:----------:| :--------------: | :-----------: |
77 | | 0.0.1 | 1.8.20 | 1.7.0 | 1.7.0 |
78 | | 0.0.2 ~ 0.0.3 | 1.8.21 | 1.7.0 | 1.7.0 |
79 | | 0.0.4 | 1.8.22 | 1.7.0 | 1.7.0 |
80 | | 0.0.5 | 1.9.0 | 1.7.0 | 1.7.0 |
81 | | 0.0.6 | 1.9.10 | 1.7.0 | 1.7.0 |
82 | | 0.0.7 | 1.9.21 | 1.7.0 | 1.7.0 |
83 |
84 | > **Warning**
85 | > Tink-ObjC 1.7.0 has not been released to CocoaPods yet.
86 | > so, you need to build Tink-ObjC 1.7.0 by yourself.
87 | > you can use my fork [RyuNen344/tink](https://github.com/RyuNen344/tink), and [Makefile](Makefile) can be used as a reference about how to build Tink-ObjC 1.7.0.
88 |
89 | ## Installation
90 |
91 | add the following to your `settings.gradle` and `build.gradle` file:
92 |
93 | ```kotlin:settings.gradle
94 | repositories {
95 | mavenCentral()
96 | }
97 | ```
98 |
99 | ```kotlin:build.gradle
100 | kotlin {
101 | // if you want to use without CocoaPods, you need to link with your Tink.framework like below.
102 | iosX64 {
103 | binaries {
104 | framework {
105 | linkerOpts(
106 | listOf(
107 | "-framework",
108 | "Tink",
109 | "-Fpath/to/Tink.framework",
110 | "-rpath",
111 | "path/to/Tink.framework",
112 | "-ObjC",
113 | )
114 | )
115 | }
116 | }
117 | }
118 |
119 | commonMain {
120 | dependencies {
121 | implementation "io.github.ryunen344.tink:tink:$tink_kmm_version"
122 | }
123 | }
124 | }
125 | ```
126 |
127 | ## Usage
128 |
129 | you can use Tink-KMM like java Tink.
130 |
131 | ### Initialization
132 |
133 | ```kotlin
134 | import io.github.ryunen344.tink.aead.AeadConfig
135 | import io.github.ryunen344.tink.aead.register
136 | import io.github.ryunen344.tink.daead.DeterministicAeadConfig
137 | import io.github.ryunen344.tink.daead.register
138 | import io.github.ryunen344.tink.hybrid.HybridConfig
139 | import io.github.ryunen344.tink.hybrid.register
140 | import io.github.ryunen344.tink.mac.MacConfig
141 | import io.github.ryunen344.tink.mac.register
142 | import io.github.ryunen344.tink.signature.SignatureConfig
143 | import io.github.ryunen344.tink.signature.register
144 |
145 | AeadConfig.register()
146 | DeterministicAeadConfig.register()
147 | HybridConfig.register()
148 | MacConfig.register()
149 | SignatureConfig.register()
150 | ```
151 |
152 | ### Generate new keys and keysets
153 |
154 | ```kotlin
155 | import io.github.ryunen344.tink.KeyTemplateSet
156 | import io.github.ryunen344.tink.KeysetHandleGenerator
157 | import io.github.ryunen344.tink.generateNew
158 |
159 | KeysetHandleGenerator.generateNew(KeyTemplateSet.AES256_GCM.template())
160 | ```
161 |
162 | > **Note**
163 | > Defined available key templates in [KeyTemplateSet](tink/src/commonMain/kotlin/io/github/ryunen344/tink/KeyTemplateSet.kt)
164 |
165 | ### Serialize and Deserialize
166 |
167 | #### Serialize with KeysetWriter
168 |
169 | ##### Cleartext
170 |
171 | ```kotlin:SerializeCleartext.kt
172 | val writer = BinaryKeysetWriter()
173 |
174 | // 1. write keyset to writer
175 | handle.writeCleartext(writer)
176 |
177 | // 2. get ByteArray from writer and save serialized somewhere
178 | val serialized = writer.write()
179 | ```
180 |
181 | ##### NoSecret
182 |
183 | ```kotlin:SerializeNoSecret.kt
184 | val writer = BinaryKeysetWriter()
185 |
186 | // 1. write keyset to writer
187 | handle.writeNoSecret(writer)
188 |
189 | // 2. get ByteArray from writer(if keyset has secret key, throw exception)
190 | val serialized = writer.write()
191 | ```
192 |
193 | #### Deserialize with KeysetReader
194 |
195 | ##### Binary
196 |
197 | ```kotlin:Deserialize.kt
198 | // read with AEAD
199 | val masterKey: AEAD
200 | KeysetHandleGenerator.read(serialized, masterKey)
201 |
202 | // read ByteArray from somewhere
203 | KeysetHandleGenerator.readClearText(BinaryKeysetReader(serialized))
204 |
205 | // read json string from somewhere
206 | KeysetHandleGenerator.readClearText(JsonKeysetReader("json string"))
207 |
208 | // read ByteArray with secret key from somewhere
209 | KeysetHandleGenerator.readNoSecret(serialized)
210 | ```
211 |
212 | ### Obtaining and using primitives
213 |
214 | #### AEAD
215 |
216 | ```kotlin:Aead.kt
217 | import io.github.ryunen344.tink.aead.Aead
218 | import io.github.ryunen344.tink.KeyTemplateSet
219 | import io.github.ryunen344.tink.KeysetHandleGenerator
220 | import io.github.ryunen344.tink.generateNew
221 | import io.github.ryunen344.tink.getPrimitive
222 |
223 | // 1. Generate the key material.
224 | val handle = KeysetHandleGenerator.generateNew(KeyTemplateSet.AES256_GCM.template())
225 |
226 | // 2. Get the primitive.
227 | val aead = handle.getPrimitive(Aead::class)
228 |
229 | // 3. Use the primitive to encrypt a plaintext,
230 | val ciphertext = aead.encrypt(plaintext, associatedData)
231 |
232 | // ... or to decrypt a ciphertext.
233 | val decrypted = aead.decrypt(ciphertext, associatedData)
234 | ```
235 |
236 | #### DeterministicAEAD
237 |
238 | ```kotlin:DeterministicAead.kt
239 | import io.github.ryunen344.tink.daead.DeterministicAead
240 | import io.github.ryunen344.tink.KeyTemplateSet
241 | import io.github.ryunen344.tink.KeysetHandleGenerator
242 | import io.github.ryunen344.tink.generateNew
243 | import io.github.ryunen344.tink.getPrimitive
244 |
245 | // 1. Generate the key material.
246 | val handle = KeysetHandleGenerator.generateNew(KeyTemplateSet.AES256_GCM.template())
247 |
248 | // 2. Get the primitive.
249 | val daead = handle.getPrimitive(DeterministicAead::class)
250 |
251 | // 3. Use the primitive to encrypt a plaintext,
252 | val ciphertext = daead.encrypt(plaintext, associatedData)
253 |
254 | // ... or to decrypt a ciphertext.
255 | val decrypted = daead.decrypt(ciphertext, associatedData)
256 | ```
257 |
258 | #### HybridAEAD
259 |
260 | ```kotlin:HybridAead.kt
261 | import io.github.ryunen344.tink.hybrid.HybridEncrypt
262 | import io.github.ryunen344.tink.hybrid.HybridDecrypt
263 | import io.github.ryunen344.tink.KeyTemplateSet
264 | import io.github.ryunen344.tink.KeysetHandleGenerator
265 | import io.github.ryunen344.tink.generateNew
266 | import io.github.ryunen344.tink.getPrimitive
267 | import io.github.ryunen344.tink.publicKeysetHandle
268 |
269 | // 1. Generate the key material.
270 | val privateKeysetHandle = KeysetHandleGenerator.generateNew(KeyTemplateSet.ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM.template())
271 | val publicKeysetHandle = privateKeysetHandle.publicKeysetHandle()
272 |
273 | // 2. Get the primitives.
274 | val hybridEncrypt = publicKeysetHandle.getPrimitive(HybridEncrypt::class)
275 | val hybridDecrypt = privateKeysetHandle.getPrimitive(HybridDecrypt::class)
276 |
277 | // 3. Use the primitives to encrypt and decrypt.
278 | val ciphertext = hybridEncrypt.encrypt(plaintext, contextInfo)
279 | val decrypted = hybridDecrypt.decrypt(ciphertext, contextInfo)
280 | ```
281 |
282 | #### MAC
283 |
284 | ```kotlin:Mac.kt
285 | import io.github.ryunen344.tink.mac.Mac
286 | import io.github.ryunen344.tink.KeyTemplateSet
287 | import io.github.ryunen344.tink.KeysetHandleGenerator
288 | import io.github.ryunen344.tink.generateNew
289 | import io.github.ryunen344.tink.getPrimitive
290 |
291 | // 1. Generate the key material.
292 | val handle = KeysetHandleGenerator.generateNew(KeyTemplateSet.HMAC_SHA256_128BITTAG.template())
293 |
294 | // 2. Get the primitive.
295 | val mac = handle.getPrimitive(Mac::class)
296 |
297 | // 3. Use the primitive to compute a tag.
298 | val tag = mac.computeMac(plaintext)
299 |
300 | // ... or to verify a tag.
301 | mac.verifyMac(tag, plaintext)
302 | ```
303 |
304 | #### Signature
305 |
306 | ```kotlin:Signature.kt
307 | import io.github.ryunen344.tink.signature.PublicKeySign
308 | import io.github.ryunen344.tink.signature.PublicKeyVerify
309 | import io.github.ryunen344.tink.KeyTemplateSet
310 | import io.github.ryunen344.tink.KeysetHandleGenerator
311 | import io.github.ryunen344.tink.generateNew
312 | import io.github.ryunen344.tink.getPrimitive
313 | import io.github.ryunen344.tink.publicKeysetHandle
314 |
315 | // 1. Generate the key material.
316 | val privateHandle = KeysetHandleGenerator.generateNew(KeyTemplateSet.ECDSA_P256.template())
317 | val publicHandle = privateHandle.publicKeysetHandle()
318 |
319 | // 2. Get the primitive.
320 | val signer = privateHandle.getPrimitive(PublicKeySign::class)
321 | val verifier = publicHandle.getPrimitive(PublicKeyVerify::class)
322 |
323 | // 3. Use the primitive to sign a message.
324 | val signature = signer.sign(message)
325 |
326 | // ... or to verify a signature.
327 | verifier.verify(signature, message)
328 | ```
329 |
330 | #### Swift(Optional)
331 |
332 | This library also supports be used in Swift directory.
333 |
334 | ```swift
335 | try! AeadConfig.companion.register()
336 | let template = try! KeyTemplateSet.aes256Gcm.template()
337 | let handle = try! KeysetHandleGenerator.companion.generateNew(keyTemplate: template)
338 | let aead = try! KeysetHandleKt.getPrimitive(handle, kClass: TinkPrimitiveKt.aead) as! Aead
339 | let ciphertext = try! aead.encrypt(plaintext, with: associatedData)
340 | let decrypted = try! aead.decrypt(ciphertext, with: associatedData)
341 | ```
342 |
--------------------------------------------------------------------------------
/tink/src/commonTest/kotlin/io/github/ryunen344/tink/hybrid/HybridTest.kt:
--------------------------------------------------------------------------------
1 | package io.github.ryunen344.tink.hybrid
2 |
3 | import io.github.ryunen344.tink.JsonKeysetReader
4 | import io.github.ryunen344.tink.KeyTemplateSet
5 | import io.github.ryunen344.tink.KeysetHandleGenerator
6 | import io.github.ryunen344.tink.daead.DeterministicAead
7 | import io.github.ryunen344.tink.daead.DeterministicAeadConfig
8 | import io.github.ryunen344.tink.daead.register
9 | import io.github.ryunen344.tink.exception.GeneralSecurityException
10 | import io.github.ryunen344.tink.generateNew
11 | import io.github.ryunen344.tink.getPrimitive
12 | import io.github.ryunen344.tink.publicKeysetHandle
13 | import io.github.ryunen344.tink.readClearText
14 | import io.github.ryunen344.tink.template
15 | import kotlin.test.BeforeTest
16 | import kotlin.test.Test
17 | import kotlin.test.assertContentEquals
18 | import kotlin.test.assertFailsWith
19 |
20 | class HybridTest {
21 |
22 | private fun verify(
23 | set: KeyTemplateSet,
24 | plaintext: ByteArray = "plaintext".encodeToByteArray(),
25 | contextInfo: ByteArray = "contextInfo".encodeToByteArray(),
26 | ) {
27 | val privateHandle = KeysetHandleGenerator.generateNew(set.template())
28 | val publicHandle = privateHandle.publicKeysetHandle()
29 | val encryptor = publicHandle.getPrimitive(HybridEncrypt::class)
30 | val decryptor = privateHandle.getPrimitive(HybridDecrypt::class)
31 |
32 | val cipherText = encryptor.encrypt(plaintext, contextInfo)
33 | assertContentEquals(
34 | plaintext,
35 | decryptor.decrypt(cipherText, contextInfo)
36 | )
37 | }
38 |
39 | private fun invalidData(
40 | set: KeyTemplateSet,
41 | ) {
42 | val privateHandle = KeysetHandleGenerator.generateNew(set.template())
43 | val decryptor = privateHandle.getPrimitive(HybridDecrypt::class)
44 |
45 | val contextInfo = "contextInfo".encodeToByteArray()
46 | val invalid = "invalid".encodeToByteArray()
47 |
48 | assertFailsWith {
49 | decryptor.decrypt(invalid, contextInfo)
50 | }
51 | }
52 |
53 | private fun invalidContextInfo(
54 | set: KeyTemplateSet,
55 | ) {
56 | val privateHandle = KeysetHandleGenerator.generateNew(set.template())
57 | val publicHandle = privateHandle.publicKeysetHandle()
58 | val encryptor = publicHandle.getPrimitive(HybridEncrypt::class)
59 | val decryptor = privateHandle.getPrimitive(HybridDecrypt::class)
60 |
61 | val plaintext = "plaintext".encodeToByteArray()
62 | val contextInfo = "contextInfo".encodeToByteArray()
63 | val invalid = "invalid".encodeToByteArray()
64 | val cipherText = encryptor.encrypt(plaintext, contextInfo)
65 |
66 | assertFailsWith {
67 | decryptor.decrypt(cipherText, invalid)
68 | }
69 | }
70 |
71 | private fun match(
72 | set: KeyTemplateSet,
73 | ) {
74 | val privateHandle = KeysetHandleGenerator.generateNew(set.template())
75 | val publicHandle = privateHandle.publicKeysetHandle()
76 | val encryptor = publicHandle.getPrimitive(HybridEncrypt::class)
77 |
78 | val plaintext = "plaintext".encodeToByteArray()
79 | val contextInfo = "contextInfo".encodeToByteArray()
80 | val cipherText = encryptor.encrypt(plaintext, contextInfo)
81 |
82 | val otherHandle = KeysetHandleGenerator.generateNew(set.template())
83 | val otherDecrypter = otherHandle.getPrimitive(HybridDecrypt::class)
84 |
85 | assertFailsWith {
86 | otherDecrypter.decrypt(cipherText, contextInfo)
87 | }
88 | }
89 |
90 | @BeforeTest
91 | fun setup() {
92 | HybridConfig.register()
93 | }
94 |
95 | @Test
96 | fun test_encrypt_ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM_then_success() =
97 | verify(KeyTemplateSet.ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM)
98 |
99 | @Test
100 | fun test_encrypt_ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM_given_empty_data_then_success() =
101 | verify(KeyTemplateSet.ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM, plaintext = "".encodeToByteArray())
102 |
103 | @Test
104 | fun test_encrypt_ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM_given_empty_context_info_then_success() =
105 | verify(KeyTemplateSet.ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM, contextInfo = "".encodeToByteArray())
106 |
107 | @Test
108 | fun test_encrypt_ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM_given_invalid_data_then_throw_error() =
109 | invalidData(KeyTemplateSet.ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM)
110 |
111 | @Test
112 | fun test_encrypt_ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM_given_invalid_context_then_throw_error() =
113 | invalidContextInfo(KeyTemplateSet.ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM)
114 |
115 | @Test
116 | fun test_encrypt_ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM_when_not_match_keyset_then_throw_error() =
117 | match(KeyTemplateSet.ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM)
118 |
119 | @Test
120 | fun test_encrypt_ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256_then_success() =
121 | verify(KeyTemplateSet.ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256)
122 |
123 | @Test
124 | fun test_encrypt_ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256_given_empty_data_then_success() =
125 | verify(KeyTemplateSet.ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256, plaintext = "".encodeToByteArray())
126 |
127 | @Test
128 | fun test_encrypt_ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256_given_empty_context_info_then_success() =
129 | verify(KeyTemplateSet.ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256, contextInfo = "".encodeToByteArray())
130 |
131 | @Test
132 | fun test_encrypt_ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256_given_invalid_data_then_throw_error() =
133 | invalidData(KeyTemplateSet.ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256)
134 |
135 | @Test
136 | fun test_encrypt_ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256_given_invalid_context_then_throw_error() =
137 | invalidContextInfo(KeyTemplateSet.ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256)
138 |
139 | @Test
140 | fun test_encrypt_ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256_when_not_match_keyset_then_throw_error() =
141 | match(KeyTemplateSet.ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256)
142 |
143 | @Test
144 | fun test_encrypt_given_json_keyset_then_success() {
145 | val privateHandle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_PRIVATE_KEYSET))
146 | val publicHandle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_PUBLIC_KEYSET))
147 |
148 | val encryptor = publicHandle.getPrimitive(HybridEncrypt::class)
149 | val decryptor = privateHandle.getPrimitive(HybridDecrypt::class)
150 |
151 | val plaintext = "plaintext".encodeToByteArray()
152 | val contextInfo = "contextInfo".encodeToByteArray()
153 | val ciphertext = encryptor.encrypt(plaintext, contextInfo)
154 | val decrypted = decryptor.decrypt(ciphertext, contextInfo)
155 | assertContentEquals(plaintext, decrypted)
156 | }
157 |
158 | @Test
159 | fun test_encrypt_given_multiple_json_keyset_then_success() {
160 | val privateHandle =
161 | KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_PRIVATE_KEYSET_WITH_MULTIPLE_KEYS))
162 | val publicHandle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_PUBLIC_KEYSET_WITH_MULTIPLE_KEYS))
163 |
164 | val encryptor = publicHandle.getPrimitive(HybridEncrypt::class)
165 | val decryptor = privateHandle.getPrimitive(HybridDecrypt::class)
166 |
167 | val plaintext = "plaintext".encodeToByteArray()
168 | val contextInfo = "contextInfo".encodeToByteArray()
169 | val ciphertext = encryptor.encrypt(plaintext, contextInfo)
170 | val decrypted = decryptor.decrypt(ciphertext, contextInfo)
171 | assertContentEquals(plaintext, decrypted)
172 |
173 | val otherPublicHandle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_PUBLIC_KEYSET))
174 | val otherEncryptor = otherPublicHandle.getPrimitive(HybridEncrypt::class)
175 | val otherCiphertext = otherEncryptor.encrypt(plaintext, contextInfo)
176 | assertContentEquals(
177 | plaintext,
178 | decryptor.decrypt(otherCiphertext, contextInfo)
179 | )
180 | }
181 |
182 | @Test
183 | fun test_getPrimitive_given_NonSignatureKeyset_then_throws_error() {
184 | DeterministicAeadConfig.register()
185 | val handle = KeysetHandleGenerator.readClearText(JsonKeysetReader(JSON_DAEAD_KEYSET))
186 | handle.getPrimitive(DeterministicAead::class)
187 |
188 | assertFailsWith {
189 | handle.getPrimitive(HybridEncrypt::class)
190 | }
191 | assertFailsWith {
192 | handle.getPrimitive(HybridDecrypt::class)
193 | }
194 | }
195 |
196 | private companion object {
197 | val JSON_PRIVATE_KEYSET = """
198 | {
199 | "primaryKeyId": 647048814,
200 | "key": [
201 | {
202 | "keyData": {
203 | "typeUrl": "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey",
204 | "value": "EosBEkQKBAgCEAMSOhI4CjB0eXBlLmdvb2dsZWFwaXMuY29tL2dvb2dsZS5jcnlwdG8udGluay5BZXNHY21LZXkSAhAQGAEYARogI4P/E3HzF6GSRNM4XlRwKBjGw81REj8ovlBno2uNvc8iIQC7Zjep7K4nPGJljgg6GCOrovBJcJRGWMsg8XLDTh0CdxogOIDYp690Aa0r2+xWsdhEZzRS5MVg8y0BdwQwMuYR63s=",
205 | "keyMaterialType": "ASYMMETRIC_PRIVATE"
206 | },
207 | "status": "ENABLED",
208 | "keyId": 647048814,
209 | "outputPrefixType": "TINK"
210 | }
211 | ]
212 | }
213 | """.trimIndent()
214 |
215 | val JSON_PUBLIC_KEYSET = """
216 | {
217 | "primaryKeyId": 647048814,
218 | "key": [
219 | {
220 | "keyData": {
221 | "typeUrl": "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey",
222 | "value": "EkQKBAgCEAMSOhI4CjB0eXBlLmdvb2dsZWFwaXMuY29tL2dvb2dsZS5jcnlwdG8udGluay5BZXNHY21LZXkSAhAQGAEYARogI4P/E3HzF6GSRNM4XlRwKBjGw81REj8ovlBno2uNvc8iIQC7Zjep7K4nPGJljgg6GCOrovBJcJRGWMsg8XLDTh0Cdw==",
223 | "keyMaterialType": "ASYMMETRIC_PUBLIC"
224 | },
225 | "status": "ENABLED",
226 | "keyId": 647048814,
227 | "outputPrefixType": "TINK"
228 | }
229 | ]
230 | }
231 | """.trimIndent()
232 |
233 | val JSON_PRIVATE_KEYSET_WITH_MULTIPLE_KEYS = """
234 | {
235 | "primaryKeyId": 1013057693,
236 | "key": [
237 | {
238 | "keyData": {
239 | "typeUrl": "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey",
240 | "value": "EosBEkQKBAgCEAMSOhI4CjB0eXBlLmdvb2dsZWFwaXMuY29tL2dvb2dsZS5jcnlwdG8udGluay5BZXNHY21LZXkSAhAQGAEYARogI4P/E3HzF6GSRNM4XlRwKBjGw81REj8ovlBno2uNvc8iIQC7Zjep7K4nPGJljgg6GCOrovBJcJRGWMsg8XLDTh0CdxogOIDYp690Aa0r2+xWsdhEZzRS5MVg8y0BdwQwMuYR63s=",
241 | "keyMaterialType": "ASYMMETRIC_PRIVATE"
242 | },
243 | "status": "ENABLED",
244 | "keyId": 647048814,
245 | "outputPrefixType": "TINK"
246 | },
247 | {
248 | "keyData": {
249 | "typeUrl": "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey",
250 | "value": "EooBEkQKBAgCEAMSOhI4CjB0eXBlLmdvb2dsZWFwaXMuY29tL2dvb2dsZS5jcnlwdG8udGluay5BZXNHY21LZXkSAhAQGAEYARogYDMh2pw+/IEZ5OTcWsnl3k8QunjsB1spu2Ex71L82WEiIECb/Un5ANDbIFdOpf+fxK0DJiTno1XVKuJym1WCqZTzGiBC8yu+DPjOz2Ut+oNkH73hxUcpgWmuJ+NPEqu5GbkLoQ==",
251 | "keyMaterialType": "ASYMMETRIC_PRIVATE"
252 | },
253 | "status": "ENABLED",
254 | "keyId": 418995680,
255 | "outputPrefixType": "TINK"
256 | },
257 | {
258 | "keyData": {
259 | "typeUrl": "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey",
260 | "value": "EowBEkQKBAgCEAMSOhI4CjB0eXBlLmdvb2dsZWFwaXMuY29tL2dvb2dsZS5jcnlwdG8udGluay5BZXNHY21LZXkSAhAQGAEYARohAMJtGycPgL1lcApnYiP3MbURUE5tkkOxdeiOUTxsclmLIiEA4aNiPurRhAnYMdpLS52MbOR+DWjxnvzOgRUPUTnYOeYaIH8YV8/5mBhN2GVpnHIWEYUKEpqcM6t+ZhGC5UJ1ZbYU",
261 | "keyMaterialType": "ASYMMETRIC_PRIVATE"
262 | },
263 | "status": "ENABLED",
264 | "keyId": 1013057693,
265 | "outputPrefixType": "TINK"
266 | }
267 | ]
268 | }
269 | """.trimIndent()
270 |
271 | // Keyset with the public keys of the keys from JSON_PRIVATE_KEYSET_WITH_MULTIPLE_KEYS.
272 | val JSON_PUBLIC_KEYSET_WITH_MULTIPLE_KEYS = """
273 | {
274 | "primaryKeyId": 1013057693,
275 | "key": [
276 | {
277 | "keyData": {
278 | "typeUrl": "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey",
279 | "value": "EkQKBAgCEAMSOhI4CjB0eXBlLmdvb2dsZWFwaXMuY29tL2dvb2dsZS5jcnlwdG8udGluay5BZXNHY21LZXkSAhAQGAEYARogI4P/E3HzF6GSRNM4XlRwKBjGw81REj8ovlBno2uNvc8iIQC7Zjep7K4nPGJljgg6GCOrovBJcJRGWMsg8XLDTh0Cdw==",
280 | "keyMaterialType": "ASYMMETRIC_PUBLIC"
281 | },
282 | "status": "ENABLED",
283 | "keyId": 647048814,
284 | "outputPrefixType": "TINK"
285 | },
286 | {
287 | "keyData": {
288 | "typeUrl": "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey",
289 | "value": "EkQKBAgCEAMSOhI4CjB0eXBlLmdvb2dsZWFwaXMuY29tL2dvb2dsZS5jcnlwdG8udGluay5BZXNHY21LZXkSAhAQGAEYARogYDMh2pw+/IEZ5OTcWsnl3k8QunjsB1spu2Ex71L82WEiIECb/Un5ANDbIFdOpf+fxK0DJiTno1XVKuJym1WCqZTz",
290 | "keyMaterialType": "ASYMMETRIC_PUBLIC"
291 | },
292 | "status": "ENABLED",
293 | "keyId": 418995680,
294 | "outputPrefixType": "TINK"
295 | },
296 | {
297 | "keyData": {
298 | "typeUrl": "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey",
299 | "value": "EkQKBAgCEAMSOhI4CjB0eXBlLmdvb2dsZWFwaXMuY29tL2dvb2dsZS5jcnlwdG8udGluay5BZXNHY21LZXkSAhAQGAEYARohAMJtGycPgL1lcApnYiP3MbURUE5tkkOxdeiOUTxsclmLIiEA4aNiPurRhAnYMdpLS52MbOR+DWjxnvzOgRUPUTnYOeY=",
300 | "keyMaterialType": "ASYMMETRIC_PUBLIC"
301 | },
302 | "status": "ENABLED",
303 | "keyId": 1013057693,
304 | "outputPrefixType": "TINK"
305 | }
306 | ]
307 | }
308 | """.trimIndent()
309 |
310 | val JSON_DAEAD_KEYSET = """
311 | {
312 | "primaryKeyId": 961932622,
313 | "key": [
314 | {
315 | "keyData": {
316 | "typeUrl": "type.googleapis.com/google.crypto.tink.AesSivKey",
317 | "keyMaterialType": "SYMMETRIC",
318 | "value": "EkCJ9r5iwc5uxq5ugFyrHXh5dijTa7qalWUgZ8Gf08RxNd545FjtLMYL7ObcaFtCSkvV2+7u6F2DN+kqUjAfkf2W"
319 | },
320 | "outputPrefixType": "TINK",
321 | "keyId": 961932622,
322 | "status": "ENABLED"
323 | }
324 | ]
325 | }
326 | """.trimIndent()
327 | }
328 | }
329 |
--------------------------------------------------------------------------------