Skip to content
This repository has been archived by the owner on Aug 12, 2024. It is now read-only.

Commit

Permalink
Add support for RSA key generation on Linux x86_64
Browse files Browse the repository at this point in the history
  • Loading branch information
Cach30verfl0w committed Jun 13, 2024
1 parent 30ba34c commit 4efc502
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class KeyGeneratorDelegate<C: Any>(
private set
private var keyPairGenerator: ((context: KeyGenContext<C>) -> KeyPair)? = null
private var keyGenerator: ((context: KeyGenContext<C>) -> Key)? = null
private var close: ((context: KeyGenContext<C>) -> Unit)? = null

/**
* This method creates an instance of a public-private keypair generator if a key pair generator
Expand Down Expand Up @@ -75,6 +76,12 @@ class KeyGeneratorDelegate<C: Any>(
override fun generateKeyPair(): KeyPair {
return keyPairGenerator!!.invoke(context!!)
}

override fun close() {
if (close != null) {
close()
}
}
}
}

Expand Down Expand Up @@ -104,7 +111,12 @@ class KeyGeneratorDelegate<C: Any>(
override fun generateKey(): Key {
return keyGenerator!!.invoke(context!!)
}
override fun close() {}

override fun close() {
if (close != null) {
close()
}
}
}
}

Expand Down Expand Up @@ -179,4 +191,15 @@ class KeyGeneratorDelegate<C: Any>(
}
this.keyGenerator = closure
}

/**
* This method sets a delegate to the close function of the key/keypair generato.
*
* @author Cedric Hammes
* @since 11/06/2024
*/
@Suppress("MemberVisibilityCanBePrivate")
fun close(closure: (context: KeyGenContext<C>) -> Unit) {
this.close = closure
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import io.karma.advcrypto.algorithm.delegates.KeyGeneratorDelegate
* @author Cedric Hammes
* @since 11/06/2024
*/
interface KeyPairGenerator {
@OptIn(ExperimentalStdlibApi::class)
interface KeyPairGenerator: AutoCloseable {

/**
* This method initializes the keypair generator with the specified specification. This
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package io.karma.advcrypto.linux.keys

import io.karma.advcrypto.keys.Key
import io.karma.advcrypto.linux.utils.SecureHeap
import kotlinx.cinterop.CPointer
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.UByteVar
import kotlinx.cinterop.reinterpret
Expand All @@ -27,22 +28,27 @@ import libssl.ERR_get_error
import libssl.RAND_bytes

@OptIn(ExperimentalForeignApi::class)
class OpenSSLKey(private val secureHeap: SecureHeap, val keySize: Int,
override val purposes: UByte, override val algorithm: String): Key {
private val rawDataPtr = secureHeap.allocate((keySize / 8).toULong()).reinterpret<UByteVar>()
class OpenSSLKey(private val secureHeap: SecureHeap,
override val purposes: UByte,
override val algorithm: String,
private val rawDataPtr: CPointer<UByteVar>,
private val rawDataSize: ULong
): Key {

override fun close() {
secureHeap.free((keySize / 8).toULong(), rawDataPtr)
secureHeap.free(rawDataSize, rawDataPtr)
}

companion object {
fun generateRandom(secureHeap: SecureHeap, keySize: Int, purposes: UByte,
algorithm: String): OpenSSLKey =
OpenSSLKey(secureHeap, keySize, purposes, algorithm).apply {
if (RAND_bytes(rawDataPtr, 1) != 1) {
throw Exception(ERR_func_error_string(ERR_get_error())?.toKString())
}
fun generateRandom(secureHeap: SecureHeap, keySize: Int, purposes: UByte, algorithm: String): OpenSSLKey {
val dataSize = (keySize / 8).toULong()
val rawDataPtr = secureHeap.allocate((keySize / 8).toULong()).reinterpret<UByteVar>()
if (RAND_bytes(rawDataPtr, 1) != 1) {
throw Exception(ERR_func_error_string(ERR_get_error())?.toKString())
}

return OpenSSLKey(secureHeap, purposes, algorithm, rawDataPtr, dataSize)
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,27 @@ import io.karma.advcrypto.AbstractProvider
import io.karma.advcrypto.Providers
import io.karma.advcrypto.algorithm.delegates.KeyGenContext
import io.karma.advcrypto.keys.Key
import io.karma.advcrypto.keys.KeyPair
import io.karma.advcrypto.linux.keys.OpenSSLKey
import io.karma.advcrypto.linux.utils.SecureHeap
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.UByteVar
import kotlinx.cinterop.reinterpret
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.BN_free
import libssl.BN_new
import libssl.BN_set_word
import libssl.PEM_write_bio_RSAPrivateKey
import libssl.PEM_write_bio_RSAPublicKey
import libssl.RSA_F4
import libssl.RSA_free
import libssl.RSA_generate_key_ex
import libssl.RSA_new

class OpenSSLCryptoProvider: AbstractProvider(
"Default",
Expand All @@ -30,6 +49,7 @@ class OpenSSLCryptoProvider: AbstractProvider(
) {
private val secureHeap = SecureHeap(UShort.MAX_VALUE.toULong() + 1u, 0u)

@OptIn(ExperimentalForeignApi::class)
override fun initialize(providers: Providers) {
algorithm(providers, "AES") {
keyGenerator<Unit>(Key.PURPOSES_SYMMETRIC, arrayOf(128, 196, 256), 256) {
Expand All @@ -44,6 +64,85 @@ class OpenSSLCryptoProvider: AbstractProvider(
}
}
}

algorithm(providers, "RSA") {
keyGenerator(Key.PURPOSES_ALL, arrayOf(1024, 2048, 4096), 4096) {
initializer {
val bne = BN_new()
if (BN_set_word(bne, RSA_F4.toULong()) != 1) {
BN_free(bne)
throw RuntimeException("Initialization of RSA key generator failed")
}
bne!!
}

generateKeyPair { context ->
val keySize = context.generatorSpec.keySize?: defaultKeySize

// Generate keys
val rsa = RSA_new()
if (RSA_generate_key_ex(rsa, keySize, context.internalContext, null) != 1) {
RSA_free(rsa)
throw RuntimeException("RSA key generation failed")
}

// Write public and private key into memory
val privKeyBio = BIO_new(BIO_s_secmem())
if (PEM_write_bio_RSAPrivateKey(privKeyBio, rsa, null, null, 0, null, null) != 1) {
BIO_free(privKeyBio)
RSA_free(rsa)
throw RuntimeException("Writing private key into memory failed")
}

val pubKeyBio = BIO_new(BIO_s_secmem())
if (PEM_write_bio_RSAPublicKey(pubKeyBio, rsa) != 1) {
BIO_free(privKeyBio)
BIO_free(pubKeyBio)
RSA_free(rsa)
throw RuntimeException("Writing public key into memory failed")
}

// Read public key into other secure memory
val pubKeyBufferSize = BIO_ctrl_pending(pubKeyBio)
val pubKeyBuffer = secureHeap.allocate(pubKeyBufferSize)
val pubLen = BIO_read(pubKeyBio, pubKeyBuffer, pubKeyBufferSize.toInt())
if (pubLen < 0) {
secureHeap.free(pubKeyBufferSize, pubKeyBuffer)
BIO_free(privKeyBio)
BIO_free(pubKeyBio)
RSA_free(rsa)
}

// Read public key into other secure memory
val privKeyBufferSize = BIO_ctrl_pending(privKeyBio)
val privKeyBuffer = secureHeap.allocate(privKeyBufferSize)
val privLen = BIO_read(privKeyBio, privKeyBuffer, privKeyBufferSize.toInt())
if (privLen < 0) {
secureHeap.free(pubKeyBufferSize, pubKeyBuffer)
secureHeap.free(privKeyBufferSize, privKeyBuffer)
BIO_free(privKeyBio)
BIO_free(pubKeyBio)
RSA_free(rsa)
}

// Free BIO data
BIO_free(privKeyBio)
BIO_free(pubKeyBio)

// Return key pair
KeyPair(
OpenSSLKey(secureHeap, 0u, "RSA", pubKeyBuffer.reinterpret(),
pubKeyBufferSize),
OpenSSLKey(secureHeap, 0u, "RSA", privKeyBuffer.reinterpret(),
privKeyBufferSize)
)
}

close { context ->
BN_free(context.internalContext)
}
}
}
}

override fun close() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import io.karma.advcrypto.Providers
import io.karma.advcrypto.algorithm.specs.KeyGeneratorSpec
import io.karma.advcrypto.keys.Key
import io.karma.advcrypto.wrapper.KeyGenerator
import io.karma.advcrypto.wrapper.KeyPairGenerator
import kotlin.test.Test

class KeyGeneratorTests {
Expand All @@ -16,4 +17,12 @@ class KeyGeneratorTests {
providers.close()
}

@Test
fun testRSA() {
val providers = Providers()
val spec = KeyGeneratorSpec.Builder(Key.PURPOSES_ALL).setKeySize(4096).build()
KeyPairGenerator.getInstance(providers, "RSA").initialize(spec).generateKeyPair().close()
providers.close()
}

}
2 changes: 1 addition & 1 deletion kmp-advcrypto/src/nativeInterop/cinterop/libssl.def
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
headers = openssl/crypto.h openssl/err.h openssl/aes.h openssl/rand.h openssl/evp.h
headers = openssl/crypto.h openssl/err.h openssl/aes.h openssl/rsa.h openssl/rand.h openssl/evp.h openssl/pem.h openssl/bio.h
linkerOpts = -L/usr/lib/x86_64-linux-gnu -L/usr/lib -ldl -lpthread -lc -lm -lssl -lcrypto
compilerOpts = -I/usr/include -I/usr/include/x86_64-linux-gnu

0 comments on commit 4efc502

Please sign in to comment.