diff --git a/kmp-advcrypto/src/androidMain/kotlin/io/karma/advcrypto/android/keys/AndroidKey.kt b/kmp-advcrypto/src/androidMain/kotlin/io/karma/advcrypto/android/keys/AndroidKey.kt index 99c3d76..9b99fe2 100644 --- a/kmp-advcrypto/src/androidMain/kotlin/io/karma/advcrypto/android/keys/AndroidKey.kt +++ b/kmp-advcrypto/src/androidMain/kotlin/io/karma/advcrypto/android/keys/AndroidKey.kt @@ -16,19 +16,21 @@ package io.karma.advcrypto.android.keys -import io.karma.advcrypto.annotations.InsecureCryptoApi import io.karma.advcrypto.keys.Key import io.karma.advcrypto.keys.enum.KeyFormat import io.karma.advcrypto.keys.enum.KeyType +import io.karma.advcrypto.store.MemoryBuffer typealias RawKey = java.security.Key class AndroidKey(val raw: RawKey, override val purposes: UByte, override val type: KeyType): Key { override val algorithm: String = raw.algorithm - @InsecureCryptoApi - override val encoded: ByteArray? = null override val format: KeyFormat? = null + override fun copyEncodedInto(buffer: MemoryBuffer) { + TODO("Not yet implemented") + } + override fun toString(): String { return "AndroidKey(algorithm='$algorithm', purposes=$purposes, raw=$raw)" } diff --git a/kmp-advcrypto/src/androidMain/kotlin/io/karma/advcrypto/store/MemoryBuffer.android.kt b/kmp-advcrypto/src/androidMain/kotlin/io/karma/advcrypto/store/MemoryBuffer.android.kt new file mode 100644 index 0000000..071ee58 --- /dev/null +++ b/kmp-advcrypto/src/androidMain/kotlin/io/karma/advcrypto/store/MemoryBuffer.android.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Cach30verfl0w + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.karma.advcrypto.store + +/** + * This method creates a memory buffer from the specified byte array. This can be used to + * copy data directly into the byte array + * + * @author Cedric Hammes + * @since 16/06/2024 + */ +actual fun emptyBuffer(): MemoryBuffer { + TODO("Not yet implemented") +} + +/** + * This method creates a secure memory buffer based on the platform. + * + * @author Cedric Hammes + * @since 16/06/2024 + */ +actual fun secureBuffer(): MemoryBuffer { + TODO("Not yet implemented") +} \ No newline at end of file diff --git a/kmp-advcrypto/src/commonMain/kotlin/io/karma/advcrypto/annotations/InsecureCryptoApi.kt b/kmp-advcrypto/src/commonMain/kotlin/io/karma/advcrypto/annotations/InsecureCryptoApi.kt index 450a857..15740df 100644 --- a/kmp-advcrypto/src/commonMain/kotlin/io/karma/advcrypto/annotations/InsecureCryptoApi.kt +++ b/kmp-advcrypto/src/commonMain/kotlin/io/karma/advcrypto/annotations/InsecureCryptoApi.kt @@ -27,5 +27,7 @@ package io.karma.advcrypto.annotations @RequiresOptIn("By using this function of the API, the confidentiality of sensitive data is in risk", RequiresOptIn.Level.ERROR) @MustBeDocumented @Retention(AnnotationRetention.BINARY) -@Target(AnnotationTarget.CLASS, AnnotationTarget.FIELD, AnnotationTarget.PROPERTY) +@Target(AnnotationTarget.CLASS, AnnotationTarget.FIELD, AnnotationTarget.PROPERTY, + AnnotationTarget.FUNCTION +) annotation class InsecureCryptoApi diff --git a/kmp-advcrypto/src/commonMain/kotlin/io/karma/advcrypto/keys/Key.kt b/kmp-advcrypto/src/commonMain/kotlin/io/karma/advcrypto/keys/Key.kt index 3821700..a2c6f8c 100644 --- a/kmp-advcrypto/src/commonMain/kotlin/io/karma/advcrypto/keys/Key.kt +++ b/kmp-advcrypto/src/commonMain/kotlin/io/karma/advcrypto/keys/Key.kt @@ -19,6 +19,8 @@ package io.karma.advcrypto.keys import io.karma.advcrypto.annotations.InsecureCryptoApi import io.karma.advcrypto.keys.enum.KeyFormat import io.karma.advcrypto.keys.enum.KeyType +import io.karma.advcrypto.store.MemoryBuffer +import io.karma.advcrypto.store.emptyBuffer /** * This interface represents every single key which can be generated by this library. These keys are @@ -33,7 +35,16 @@ import io.karma.advcrypto.keys.enum.KeyType interface Key: AutoCloseable { /** - * This value represents the raw encoded key in the format also specified in this key. This is + * This method copies the internal encoded buffer into the specified memory buffer. This can + * be used to copy data securely into a secure memory. + * + * @author Cedric Hammes + * @since 16/06/2024 + */ + fun copyEncodedInto(buffer: MemoryBuffer) + + /** + * This method returns the raw encoded key in the format also specified in this key. This is * used to create another key by a key. Warning: If you use this method, the key can be copied * into insecure memory that can be vulnerable to information leakage. * @@ -41,7 +52,7 @@ interface Key: AutoCloseable { * @since 14/06/2024 */ @InsecureCryptoApi - val encoded: ByteArray? + fun encoded(): ByteArray = emptyBuffer().apply { copyEncodedInto(this) }.toByteArray() /** * This value represents the format of the raw encoded key. This format is used to transform the diff --git a/kmp-advcrypto/src/commonMain/kotlin/io/karma/advcrypto/store/MemoryBuffer.kt b/kmp-advcrypto/src/commonMain/kotlin/io/karma/advcrypto/store/MemoryBuffer.kt new file mode 100644 index 0000000..4fb8ef6 --- /dev/null +++ b/kmp-advcrypto/src/commonMain/kotlin/io/karma/advcrypto/store/MemoryBuffer.kt @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2024 Cach30verfl0w + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.karma.advcrypto.store + +import io.karma.advcrypto.annotations.InsecureCryptoApi + +/** + * This method creates a empty memory buffer based on the platform. + * + * @author Cedric Hammes + * @since 16/06/2024 + */ +expect fun emptyBuffer(): MemoryBuffer + +/** + * This method creates a secure memory buffer based on the platform. + * + * @author Cedric Hammes + * @since 16/06/2024 + */ +expect fun secureBuffer(): MemoryBuffer + +/** + * This is the implementation of a memory buffer. Memory buffers are used to provide cross-platform + * copy and write operations for insecure and secure memory. + * + * @author Cedric Hammes + * @since 16/06/2024 + */ +@OptIn(ExperimentalStdlibApi::class) +interface MemoryBuffer: AutoCloseable { + + /** + * This method copies this memory buffer into another memory buffer. + * + * @author Cedric Hammes + * @since 16/06/2024 + */ + fun copyInto(buffer: MemoryBuffer, size: Int = this.size) + + /** + * This method returns the content of the memory buffer as byte array. If you are using a secure + * buffer, this data can be leaked into insecure memory. + * + * @author Cedric Hammes + * @since 16/06/2024 + */ + @InsecureCryptoApi + fun toByteArray(): ByteArray + + val size: Int + +} diff --git a/kmp-advcrypto/src/jvmMain/kotlin/io/karma/advcrypto/store/MemoryBuffer.jvm.kt b/kmp-advcrypto/src/jvmMain/kotlin/io/karma/advcrypto/store/MemoryBuffer.jvm.kt new file mode 100644 index 0000000..071ee58 --- /dev/null +++ b/kmp-advcrypto/src/jvmMain/kotlin/io/karma/advcrypto/store/MemoryBuffer.jvm.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Cach30verfl0w + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.karma.advcrypto.store + +/** + * This method creates a memory buffer from the specified byte array. This can be used to + * copy data directly into the byte array + * + * @author Cedric Hammes + * @since 16/06/2024 + */ +actual fun emptyBuffer(): MemoryBuffer { + TODO("Not yet implemented") +} + +/** + * This method creates a secure memory buffer based on the platform. + * + * @author Cedric Hammes + * @since 16/06/2024 + */ +actual fun secureBuffer(): MemoryBuffer { + TODO("Not yet implemented") +} \ No newline at end of file diff --git a/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/keys/OpenSSLKey.kt b/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/keys/OpenSSLKey.kt index ac5368d..792f4a5 100644 --- a/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/keys/OpenSSLKey.kt +++ b/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/keys/OpenSSLKey.kt @@ -16,15 +16,16 @@ package io.karma.advcrypto.linux.keys -import io.karma.advcrypto.annotations.InsecureCryptoApi + import io.karma.advcrypto.keys.Key import io.karma.advcrypto.keys.enum.KeyFormat import io.karma.advcrypto.keys.enum.KeyType import io.karma.advcrypto.linux.utils.SecureHeap +import io.karma.advcrypto.store.MemoryBuffer +import io.karma.advcrypto.store.PointerMemoryBuffer import kotlinx.cinterop.CPointer import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.UByteVar -import kotlinx.cinterop.get import kotlinx.cinterop.reinterpret import kotlinx.cinterop.toKString import libssl.ERR_error_string @@ -32,15 +33,18 @@ import libssl.ERR_get_error import libssl.RAND_bytes @OptIn(ExperimentalForeignApi::class) -class OpenSSLKey(private val secureHeap: SecureHeap, - override val purposes: UByte, - override val algorithm: String, - val rawDataPtr: CPointer, - val rawDataSize: ULong, - override val type: KeyType +class OpenSSLKey( + private val secureHeap: SecureHeap, + override val purposes: UByte, + override val algorithm: String, + val rawDataPtr: CPointer, + val rawDataSize: ULong, + override val type: KeyType ): Key { - @InsecureCryptoApi - override val encoded: ByteArray = ByteArray(rawDataSize.toInt()) { rawDataPtr[it].toByte() } + override fun copyEncodedInto(buffer: MemoryBuffer) { + PointerMemoryBuffer(rawDataPtr, rawDataSize.toInt()).copyInto(buffer) + } + override val format: KeyFormat = KeyFormat.DER // TODO: Derive from key override fun close() { diff --git a/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/keys/OpenSSLPKey.kt b/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/keys/OpenSSLPKey.kt index db808b2..9bca0fb 100644 --- a/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/keys/OpenSSLPKey.kt +++ b/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/keys/OpenSSLPKey.kt @@ -16,19 +16,15 @@ package io.karma.advcrypto.linux.keys -import io.karma.advcrypto.annotations.InsecureCryptoApi import io.karma.advcrypto.keys.Key import io.karma.advcrypto.keys.enum.KeyFormat import io.karma.advcrypto.keys.enum.KeyType +import io.karma.advcrypto.store.BIOMemoryBuffer +import io.karma.advcrypto.store.MemoryBuffer +import io.karma.advcrypto.store.emptyBuffer import kotlinx.cinterop.CPointer import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.refTo import kotlinx.cinterop.toKString -import libssl.BIO_ctrl_pending -import libssl.BIO_free -import libssl.BIO_new -import libssl.BIO_read -import libssl.BIO_s_mem import libssl.EVP_PKEY import libssl.EVP_PKEY_ED25519 import libssl.EVP_PKEY_RSA @@ -37,34 +33,25 @@ import libssl.EVP_PKEY_get_base_id import libssl.OBJ_nid2sn import libssl.PEM_write_bio_PUBKEY import libssl.i2d_PUBKEY_bio +import libssl.i2d_PrivateKey_bio @OptIn(ExperimentalForeignApi::class) class OpenSSLPKey(val rawKey: CPointer, override val purposes: UByte, override val type: KeyType, override val format: KeyFormat = KeyFormat.DER): Key { - @InsecureCryptoApi - override val encoded: ByteArray? - get() { - return when(this.format) { - KeyFormat.DER -> { - val bio = BIO_new(BIO_s_mem()) - i2d_PUBKEY_bio(bio, rawKey) - val data = ByteArray(BIO_ctrl_pending(bio).toInt()) - BIO_read(bio, data.refTo(0), data.size) - BIO_free(bio) - data + override fun copyEncodedInto(buffer: MemoryBuffer) { + (emptyBuffer() as BIOMemoryBuffer).apply { + when(format) { + KeyFormat.DER -> when(type) { + KeyType.PUBLIC -> i2d_PUBKEY_bio(this.bio, rawKey) + KeyType.PRIVATE -> i2d_PrivateKey_bio(this.bio, rawKey) + else -> throw UnsupportedOperationException("Unsupported key type") } - KeyFormat.PEM -> { - val bio = BIO_new(BIO_s_mem()) - PEM_write_bio_PUBKEY(bio, rawKey) - val data = ByteArray(BIO_ctrl_pending(bio).toInt()) - BIO_read(bio, data.refTo(0), data.size) - BIO_free(bio) - data - } - else -> null + KeyFormat.PEM -> PEM_write_bio_PUBKEY(this.bio, rawKey) + else -> throw UnsupportedOperationException("Format '${format}' supported") } - } + }.copyInto(buffer) + } override val algorithm: String = when(val baseId = EVP_PKEY_get_base_id(rawKey)) { EVP_PKEY_RSA -> "RSA" EVP_PKEY_ED25519 -> "ED25519" diff --git a/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/providers/DefaultKeyStoreProvider.kt b/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/providers/DefaultKeyStoreProvider.kt index a66936a..d127ae6 100644 --- a/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/providers/DefaultKeyStoreProvider.kt +++ b/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/providers/DefaultKeyStoreProvider.kt @@ -27,7 +27,7 @@ class DefaultKeyStoreProvider: AbstractProvider( "This provider provides access to the keystore interface on Linux devices", "1.0.0-Dev" ) { - private val secHeap = SecureHeap(UShort.MAX_VALUE.toULong() + 1u, 0u) + private val secHeap = SecureHeap() override fun initialize(providers: Providers) { keyStore("Default") { diff --git a/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/providers/OpenSSLCryptoProvider.kt b/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/providers/OpenSSLCryptoProvider.kt index df3ba61..3876974 100644 --- a/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/providers/OpenSSLCryptoProvider.kt +++ b/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/providers/OpenSSLCryptoProvider.kt @@ -166,7 +166,7 @@ class OpenSSLCryptoProvider: AbstractProvider( "This class provides access to the default asymmetric and symmetric algorithms on Linux", "1.0.0-Dev" ) { - private val secureHeap = SecureHeap(UShort.MAX_VALUE.toULong() + 1u, 0u) + private val secureHeap = SecureHeap() @OptIn(ExperimentalForeignApi::class, ExperimentalStdlibApi::class) override fun initialize(providers: Providers) { diff --git a/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/utils/SecureHeap.kt b/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/utils/SecureHeap.kt index dba0e25..cd02233 100644 --- a/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/utils/SecureHeap.kt +++ b/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/linux/utils/SecureHeap.kt @@ -23,7 +23,6 @@ import libssl.CRYPTO_secure_clear_free import libssl.CRYPTO_secure_malloc import libssl.CRYPTO_secure_malloc_done import libssl.CRYPTO_secure_malloc_init -import libssl.CRYPTO_secure_malloc_initialized import libssl.ERR_error_string import libssl.ERR_get_error @@ -40,7 +39,7 @@ import libssl.ERR_get_error * @since 12/06/2024 */ @OptIn(ExperimentalForeignApi::class, ExperimentalStdlibApi::class) -class SecureHeap(size: ULong, minSize: ULong): AutoCloseable { +class SecureHeap: AutoCloseable { /** * This constructor initializes the secure heap if no secure heap was already initialized. If @@ -50,8 +49,9 @@ class SecureHeap(size: ULong, minSize: ULong): AutoCloseable { * @since 12/06/2024 */ init { - if (CRYPTO_secure_malloc_initialized() != 1) { - CRYPTO_secure_malloc_init(size, minSize) + counter += 1 + if (counter == 1) { + CRYPTO_secure_malloc_init(UShort.MAX_VALUE.toULong() + 1u, 0u) } } @@ -65,8 +65,10 @@ class SecureHeap(size: ULong, minSize: ULong): AutoCloseable { * @author Cedric Hammes * @since 12/06/2024 */ - fun allocate(size: ULong): COpaquePointer = CRYPTO_secure_malloc(size, this.toString(), 47) - ?: throw Exception(ERR_error_string(ERR_get_error(), null)?.toKString()) + fun allocate(size: ULong): COpaquePointer { + return CRYPTO_secure_malloc(size, this.toString(), 47) ?: + throw Exception("Failed to allocate secure heap: ${ERR_error_string(ERR_get_error(), null)?.toKString()}") + } /** * This method frees the allocated memory from the secure heap. This also deletes the data @@ -90,10 +92,15 @@ class SecureHeap(size: ULong, minSize: ULong): AutoCloseable { * @since 12/06/2024 */ override fun close() { - if (CRYPTO_secure_malloc_initialized() == 1) { + counter -= 1 + if (counter == 0) { CRYPTO_secure_malloc_done() } } + companion object { + private var counter = 0 + } + } \ No newline at end of file diff --git a/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/store/MemoryBuffer.linuxX64.kt b/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/store/MemoryBuffer.linuxX64.kt new file mode 100644 index 0000000..cb0dec1 --- /dev/null +++ b/kmp-advcrypto/src/linuxX64Main/kotlin/io/karma/advcrypto/store/MemoryBuffer.linuxX64.kt @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2024 Cach30verfl0w + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalForeignApi::class) + +package io.karma.advcrypto.store + +import io.karma.advcrypto.annotations.InsecureCryptoApi +import io.karma.advcrypto.linux.utils.SecureHeap +import kotlinx.cinterop.CPointer +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.addressOf +import kotlinx.cinterop.memScoped +import kotlinx.cinterop.usePinned +import libssl.BIO_CTRL_INFO +import libssl.BIO_METHOD +import libssl.BIO_ctrl +import libssl.BIO_ctrl_pending +import libssl.BIO_free +import libssl.BIO_new +import libssl.BIO_read +import libssl.BIO_s_mem +import libssl.BIO_s_secmem +import libssl.BIO_write +import libssl.OPENSSL_INIT_LOAD_CONFIG +import libssl.OPENSSL_init_crypto +import platform.posix.memcpy + +/** + * This method creates a memory buffer from the specified byte array. This can be used to + * copy data directly into the byte array + * + * @author Cedric Hammes + * @since 16/06/2024 + */ +actual fun emptyBuffer(): MemoryBuffer = BIOMemoryBuffer(checkNotNull(BIO_s_mem())) + +/** + * This method creates a secure memory buffer based on the platform. + * + * @author Cedric Hammes + * @since 16/06/2024 + */ +actual fun secureBuffer(): MemoryBuffer = BIOMemoryBuffer(checkNotNull(BIO_s_secmem())) + +class PointerMemoryBuffer( + val pointer: CPointer<*>, + override val size: Int +): MemoryBuffer { + /** + * This method copies this memory buffer into another memory buffer. + * + * @author Cedric Hammes + * @since 16/06/2024 + */ + override fun copyInto(buffer: MemoryBuffer, size: Int) { + if (buffer is BIOMemoryBuffer) { + BIO_write(buffer.bio, pointer, size) + return + } + + if (buffer is PointerMemoryBuffer) { + memcpy(buffer.pointer, pointer, size.toULong()) + return + } + + throw IllegalArgumentException("Linux-based memory buffers only supports same type") + } + + @InsecureCryptoApi + override fun toByteArray(): ByteArray = ByteArray(this.size).apply { + usePinned { pinnedData -> + memcpy(pinnedData.addressOf(0), pointer, size.toULong()) + } + } + + override fun close() {} +} + +/** + * This is the implementation of a memory buffer. Memory buffers are used to provide cross-platform + * copy and write operations for insecure and secure memory. + * + * @author Cedric Hammes + * @since 16/06/2024 + */ +class BIOMemoryBuffer(bioMethod: CPointer): MemoryBuffer { + val bio = BIO_new(bioMethod) + override val size: Int + get() = BIO_ctrl_pending(bio).toInt() + + /** + * This method copies this memory buffer into another memory buffer. + * + * @author Cedric Hammes + * @since 16/06/2024 + */ + override fun copyInto(buffer: MemoryBuffer, size: Int) { + // Allocate temporary secure buffer and write data from this Basic I/O into the buffer. + // Then write the data into the other Basic I/O and into this Basic I/O. + if (buffer is BIOMemoryBuffer) { + @OptIn(ExperimentalStdlibApi::class) + SecureHeap().use { secureHeap -> + val tempBuffer = secureHeap.allocate(size.toULong()) + val bytesWrite = BIO_read(this.bio, tempBuffer, size) + BIO_write(buffer.bio, tempBuffer, bytesWrite) + BIO_write(this.bio, tempBuffer, bytesWrite) + secureHeap.free(size.toULong(), tempBuffer) + } + return + } + + // This method simple writes the buffer of the BIO object into the pointer buffer + if (buffer is PointerMemoryBuffer) { + BIO_read(this.bio, buffer.pointer, size) + return + } + + throw IllegalArgumentException("Linux-based memory buffers only supports same type") + } + + /** + * This method returns the content of the memory buffer as byte array. If you are using a secure + * buffer, this data can be leaked into insecure memory. + * + * @author Cedric Hammes + * @since 16/06/2024 + */ + @InsecureCryptoApi + override fun toByteArray(): ByteArray = ByteArray(size).apply { + usePinned { pinnedData -> + BIO_read(bio, pinnedData.addressOf(0), size) + } + } + + override fun close() { + BIO_free(bio) + } + +} \ No newline at end of file diff --git a/kmp-advcrypto/src/linuxX64Test/kotlin/io/karma/advcrypto/linux/tests/CipherTests.kt b/kmp-advcrypto/src/linuxX64Test/kotlin/io/karma/advcrypto/linux/tests/CipherTests.kt deleted file mode 100644 index 9ae1d31..0000000 --- a/kmp-advcrypto/src/linuxX64Test/kotlin/io/karma/advcrypto/linux/tests/CipherTests.kt +++ /dev/null @@ -1,107 +0,0 @@ -package io.karma.advcrypto.linux.tests - -import io.karma.advcrypto.linux.utils.SecureHeap -import kotlinx.cinterop.ByteVar -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.IntVar -import kotlinx.cinterop.UByteVar -import kotlinx.cinterop.addressOf -import kotlinx.cinterop.alloc -import kotlinx.cinterop.memScoped -import kotlinx.cinterop.ptr -import kotlinx.cinterop.refTo -import kotlinx.cinterop.reinterpret -import kotlinx.cinterop.toKString -import kotlinx.cinterop.usePinned -import kotlinx.cinterop.value -import libssl.AES_BLOCK_SIZE -import libssl.EVP_CIPHER_CTX_ctrl -import libssl.EVP_CIPHER_CTX_free -import libssl.EVP_CIPHER_CTX_new -import libssl.EVP_CTRL_GCM_GET_TAG -import libssl.EVP_CTRL_GCM_SET_IVLEN -import libssl.EVP_CTRL_GCM_SET_TAG -import libssl.EVP_DecryptFinal_ex -import libssl.EVP_DecryptInit_ex -import libssl.EVP_DecryptUpdate -import libssl.EVP_EncryptFinal_ex -import libssl.EVP_EncryptInit_ex -import libssl.EVP_EncryptUpdate -import libssl.EVP_aes_256_gcm -import libssl.RAND_bytes -import kotlin.test.Test - -class CipherTests { - - // TODO: call EVP_EncryptUpdate and EVP_DecryptUpdate twice (to add additional authentication - // data) - @OptIn(ExperimentalForeignApi::class) - @Test - fun test() { - val secureHeap = SecureHeap(8192u, 0u) - val input = "Test".encodeToByteArray() - - // Generate random key - val key = secureHeap.allocate(64u).reinterpret() - RAND_bytes(key, 64) - - // Generate initialization vector - val iv = secureHeap.allocate(16u).reinterpret() - RAND_bytes(iv, 16) - - // Initialize AES-256-GCM cipher - val encryptContext = EVP_CIPHER_CTX_new().apply { - EVP_EncryptInit_ex(this, EVP_aes_256_gcm(), null, null, null) - EVP_CIPHER_CTX_ctrl(this, EVP_CTRL_GCM_SET_IVLEN, 16, null) - EVP_EncryptInit_ex(this, EVP_aes_256_gcm(), null, key, iv) - } - - val decryptContext = EVP_CIPHER_CTX_new().apply { - EVP_DecryptInit_ex(this, EVP_aes_256_gcm(), null, null, null) - EVP_CIPHER_CTX_ctrl(this, EVP_CTRL_GCM_SET_IVLEN, 16, null) - EVP_DecryptInit_ex(this, EVP_aes_256_gcm(), null, key, iv) - } - - val ciphertext = ByteArray(input.size + AES_BLOCK_SIZE - 1) - memScoped { - // Encrypt data - val ciphertextLength = alloc() - ciphertext.usePinned { ct -> - input.usePinned { ip -> - EVP_EncryptUpdate(encryptContext, ct.addressOf(0).reinterpret(), - ciphertextLength.ptr, ip.addressOf(0).reinterpret(), input.size) - } - - val len = alloc() - EVP_EncryptFinal_ex(encryptContext, ct.addressOf(ciphertextLength.value).reinterpret(), len.ptr) - ciphertextLength.value += len.value - } - - val tag = alloc() - EVP_CIPHER_CTX_ctrl(encryptContext, EVP_CTRL_GCM_GET_TAG, 16, tag.ptr) - - // Decrypt - val output = ByteArray(ciphertext.size) - val outputLength = alloc() - output.usePinned { ot -> - ciphertext.usePinned { ct -> - EVP_DecryptUpdate(decryptContext, ot.addressOf(0).reinterpret(), outputLength.ptr, ct.addressOf(0).reinterpret(), ciphertextLength.value) - } - EVP_CIPHER_CTX_ctrl(decryptContext, EVP_CTRL_GCM_SET_TAG, 16, tag.ptr) - - val len = alloc() - EVP_DecryptFinal_ex(decryptContext, ot.addressOf(outputLength.value).reinterpret(), len.ptr) - outputLength.value += len.value - } - println("EEEEE: " + output.toKString()) - } - - // Free - EVP_CIPHER_CTX_free(encryptContext) - EVP_CIPHER_CTX_free(decryptContext) - secureHeap.free(16u, iv) - secureHeap.free(64u, key) - secureHeap.close() - } - -} \ No newline at end of file diff --git a/kmp-advcrypto/src/linuxX64Test/kotlin/io/karma/advcrypto/linux/tests/KeyReaderHelperTests.kt b/kmp-advcrypto/src/linuxX64Test/kotlin/io/karma/advcrypto/linux/tests/KeyReaderHelperTests.kt index ea8f8c1..44a39a3 100644 --- a/kmp-advcrypto/src/linuxX64Test/kotlin/io/karma/advcrypto/linux/tests/KeyReaderHelperTests.kt +++ b/kmp-advcrypto/src/linuxX64Test/kotlin/io/karma/advcrypto/linux/tests/KeyReaderHelperTests.kt @@ -1,5 +1,6 @@ package io.karma.advcrypto.linux.tests +import io.karma.advcrypto.Providers import io.karma.advcrypto.annotations.InsecureCryptoApi import io.karma.advcrypto.keys.Key import io.karma.advcrypto.keys.enum.KeyFormat @@ -16,13 +17,14 @@ class KeyReaderHelperTests { @Test fun testPEM() { + val providers = Providers() fileSystem.read("./testkeys/rsa-private-key.pem".toPath()) { val key = KeyReaderHelper.tryParse(readByteArray(), Key.PURPOSES_ALL)!! assert(key.algorithm == "RSA") assert(key.format == KeyFormat.PEM) assert(key.type == KeyType.PRIVATE) - val key1 = KeyReaderHelper.tryParse(key.encoded!!, Key.PURPOSES_ALL)!! + val key1 = KeyReaderHelper.tryParse(key.encoded(), Key.PURPOSES_ALL)!! assert(key1.algorithm == "RSA") assert(key1.format == KeyFormat.PEM) assert(key1.type == KeyType.PUBLIC) @@ -35,15 +37,17 @@ class KeyReaderHelperTests { assert(key.format == KeyFormat.PEM) assert(key.type == KeyType.PUBLIC) - val key1 = KeyReaderHelper.tryParse(key.encoded!!, Key.PURPOSES_ALL)!! + val key1 = KeyReaderHelper.tryParse(key.encoded(), Key.PURPOSES_ALL)!! assert(key1.algorithm == "RSA") assert(key1.format == KeyFormat.PEM) assert(key1.type == KeyType.PUBLIC) } + providers.close() } @Test fun testDER() { + val providers = Providers() fileSystem.read("./testkeys/rsa-private-key.der".toPath()) { val key = KeyReaderHelper.tryParse(readByteArray(), Key.PURPOSES_ALL)!! close() @@ -51,10 +55,10 @@ class KeyReaderHelperTests { assert(key.format == KeyFormat.DER) assert(key.type == KeyType.PRIVATE) - val key1 = KeyReaderHelper.tryParse(key.encoded!!, Key.PURPOSES_ALL)!! + val key1 = checkNotNull(KeyReaderHelper.tryParse(key.encoded(), Key.PURPOSES_ALL)) assert(key1.algorithm == "RSA") assert(key1.format == KeyFormat.DER) - assert(key1.type == KeyType.PUBLIC) + assert(key1.type == KeyType.PRIVATE) } fileSystem.read("./testkeys/rsa-public-key.der".toPath()) { @@ -64,11 +68,12 @@ class KeyReaderHelperTests { assert(key.format == KeyFormat.DER) assert(key.type == KeyType.PUBLIC) - val key1 = KeyReaderHelper.tryParse(key.encoded!!, Key.PURPOSES_ALL)!! + val key1 = checkNotNull(KeyReaderHelper.tryParse(key.encoded(), Key.PURPOSES_ALL)) assert(key1.algorithm == "RSA") assert(key1.format == KeyFormat.DER) assert(key1.type == KeyType.PUBLIC) } + providers.close() } } \ No newline at end of file diff --git a/kmp-advcrypto/src/linuxX64Test/kotlin/io/karma/advcrypto/linux/tests/SecureHeapTests.kt b/kmp-advcrypto/src/linuxX64Test/kotlin/io/karma/advcrypto/linux/tests/SecureHeapTests.kt index 715d0fd..f6a54bb 100644 --- a/kmp-advcrypto/src/linuxX64Test/kotlin/io/karma/advcrypto/linux/tests/SecureHeapTests.kt +++ b/kmp-advcrypto/src/linuxX64Test/kotlin/io/karma/advcrypto/linux/tests/SecureHeapTests.kt @@ -14,7 +14,7 @@ class SecureHeapTests { @Test fun test() { - SecureHeap(ULong.SIZE_BYTES.toULong(), 0U).use { heap -> + SecureHeap().use { heap -> val pointer = heap.allocate(8U).reinterpret() pointer.pointed.value = 100U assert(pointer.pointed.value == 100UL)