Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ internal object Openssl3CryptographyProvider : CryptographyProvider() {
AES.GCM -> Openssl3AesGcm
ECDSA -> Openssl3Ecdsa
ECDH -> Openssl3Ecdh
EdDSA -> Openssl3EdDSA
XDH -> Openssl3XDH
RSA.PSS -> Openssl3RsaPss
RSA.PKCS1 -> Openssl3RsaPkcs1
RSA.OAEP -> Openssl3RsaOaep
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* Copyright (c) 2025 Oleg Yukhnevich. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.whyoleg.cryptography.providers.openssl3.algorithms

import dev.whyoleg.cryptography.algorithms.*
import dev.whyoleg.cryptography.materials.key.*
import dev.whyoleg.cryptography.operations.*
import dev.whyoleg.cryptography.providers.base.*
import dev.whyoleg.cryptography.providers.openssl3.internal.*
import dev.whyoleg.cryptography.providers.openssl3.internal.cinterop.*
import dev.whyoleg.cryptography.providers.openssl3.materials.*
import kotlinx.cinterop.*
import platform.posix.*
import kotlin.experimental.*
import dev.whyoleg.cryptography.serialization.asn1.modules.*
import dev.whyoleg.cryptography.serialization.asn1.ObjectIdentifier
import dev.whyoleg.cryptography.providers.base.materials.*
import dev.whyoleg.cryptography.providers.openssl3.operations.Openssl3DigestSignatureGenerator
import dev.whyoleg.cryptography.providers.openssl3.operations.Openssl3DigestSignatureVerifier

internal object Openssl3EdDSA : EdDSA {
private fun algorithmName(curve: EdDSA.Curve): String = when (curve) {
EdDSA.Curve.Ed25519 -> "ED25519"
EdDSA.Curve.Ed448 -> "ED448"
}
private fun oid(curve: EdDSA.Curve): ObjectIdentifier = when (curve) {
EdDSA.Curve.Ed25519 -> ObjectIdentifier.Ed25519
EdDSA.Curve.Ed448 -> ObjectIdentifier.Ed448
}

override fun publicKeyDecoder(curve: EdDSA.Curve): KeyDecoder<EdDSA.PublicKey.Format, EdDSA.PublicKey> =
object : Openssl3PublicKeyDecoder<EdDSA.PublicKey.Format, EdDSA.PublicKey>(algorithmName(curve)) {
override fun inputType(format: EdDSA.PublicKey.Format): String = when (format) {
EdDSA.PublicKey.Format.DER -> "DER"
EdDSA.PublicKey.Format.PEM -> "PEM"
EdDSA.PublicKey.Format.JWK,
EdDSA.PublicKey.Format.RAW -> error("should not be called: handled explicitly in decodeFromBlocking")
}

override fun decodeFromByteArrayBlocking(format: EdDSA.PublicKey.Format, bytes: ByteArray): EdDSA.PublicKey = when (format) {
EdDSA.PublicKey.Format.RAW -> super.decodeFromByteArrayBlocking(
EdDSA.PublicKey.Format.DER,
wrapSubjectPublicKeyInfo(UnknownKeyAlgorithmIdentifier(oid(curve)), bytes)
)
else -> super.decodeFromByteArrayBlocking(format, bytes)
}

override fun wrapKey(key: CPointer<EVP_PKEY>): EdDSA.PublicKey = EdDsaPublicKey(key, curve)
}

override fun privateKeyDecoder(curve: EdDSA.Curve): KeyDecoder<EdDSA.PrivateKey.Format, EdDSA.PrivateKey> =
object : Openssl3PrivateKeyDecoder<EdDSA.PrivateKey.Format, EdDSA.PrivateKey>(algorithmName(curve)) {
override fun inputType(format: EdDSA.PrivateKey.Format): String = when (format) {
EdDSA.PrivateKey.Format.DER -> "DER"
EdDSA.PrivateKey.Format.PEM -> "PEM"
EdDSA.PrivateKey.Format.JWK,
EdDSA.PrivateKey.Format.RAW -> error("should not be called: handled explicitly in decodeFromBlocking")
}

override fun decodeFromByteArrayBlocking(format: EdDSA.PrivateKey.Format, bytes: ByteArray): EdDSA.PrivateKey = when (format) {
EdDSA.PrivateKey.Format.RAW -> super.decodeFromByteArrayBlocking(
EdDSA.PrivateKey.Format.DER,
wrapPrivateKeyInfo(0, UnknownKeyAlgorithmIdentifier(oid(curve)), bytes)
)
else -> super.decodeFromByteArrayBlocking(format, bytes)
}

override fun wrapKey(key: CPointer<EVP_PKEY>): EdDSA.PrivateKey = EdDsaPrivateKey(key, curve)
}

override fun keyPairGenerator(curve: EdDSA.Curve): KeyGenerator<EdDSA.KeyPair> =
object : Openssl3KeyPairGenerator<EdDSA.KeyPair>(algorithmName(curve)) {
override fun MemScope.createParams(): CValuesRef<OSSL_PARAM>? = null
override fun wrapKeyPair(keyPair: CPointer<EVP_PKEY>): EdDSA.KeyPair = EdDsaKeyPair(
publicKey = EdDsaPublicKey(keyPair, curve),
privateKey = EdDsaPrivateKey(keyPair, curve)
)
}

private class EdDsaKeyPair(
override val publicKey: EdDSA.PublicKey,
override val privateKey: EdDSA.PrivateKey,
) : EdDSA.KeyPair

private class EdDsaPublicKey(
key: CPointer<EVP_PKEY>,
private val curve: EdDSA.Curve,
) : EdDSA.PublicKey, Openssl3PublicKeyEncodable<EdDSA.PublicKey.Format>(key) {
override fun outputType(format: EdDSA.PublicKey.Format): String = when (format) {
EdDSA.PublicKey.Format.DER -> "DER"
EdDSA.PublicKey.Format.PEM -> "PEM"
EdDSA.PublicKey.Format.JWK,
EdDSA.PublicKey.Format.RAW -> error("should not be called: handled explicitly in encodeToBlocking")
}

override fun encodeToByteArrayBlocking(format: EdDSA.PublicKey.Format): ByteArray = when (format) {
EdDSA.PublicKey.Format.RAW -> unwrapSubjectPublicKeyInfo(
oid(curve),
super.encodeToByteArrayBlocking(EdDSA.PublicKey.Format.DER)
)
else -> super.encodeToByteArrayBlocking(format)
}

override fun signatureVerifier(): SignatureVerifier = EdDsaSignatureVerifier(key)
}

private class EdDsaPrivateKey(
key: CPointer<EVP_PKEY>,
private val curve: EdDSA.Curve,
) : EdDSA.PrivateKey, Openssl3PrivateKeyEncodable<EdDSA.PrivateKey.Format>(key) {
override fun outputType(format: EdDSA.PrivateKey.Format): String = when (format) {
EdDSA.PrivateKey.Format.DER -> "DER"
EdDSA.PrivateKey.Format.PEM -> "PEM"
EdDSA.PrivateKey.Format.JWK,
EdDSA.PrivateKey.Format.RAW -> error("should not be called: handled explicitly in encodeToBlocking")
}

override fun encodeToByteArrayBlocking(format: EdDSA.PrivateKey.Format): ByteArray = when (format) {
EdDSA.PrivateKey.Format.RAW -> unwrapPrivateKeyInfo(
oid(curve),
super.encodeToByteArrayBlocking(EdDSA.PrivateKey.Format.DER)
)
else -> super.encodeToByteArrayBlocking(format)
}

override fun signatureGenerator(): SignatureGenerator = EdDsaSignatureGenerator(key)
}
}


private class EdDsaSignatureGenerator(
private val privateKey: CPointer<EVP_PKEY>,
) : Openssl3DigestSignatureGenerator(privateKey, hashAlgorithm = null) {
override fun MemScope.createParams(): CValuesRef<OSSL_PARAM>? = null
}

private class EdDsaSignatureVerifier(
private val publicKey: CPointer<EVP_PKEY>,
) : Openssl3DigestSignatureVerifier(publicKey, hashAlgorithm = null) {
override fun MemScope.createParams(): CValuesRef<OSSL_PARAM>? = null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright (c) 2025 Oleg Yukhnevich. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.whyoleg.cryptography.providers.openssl3.algorithms

import dev.whyoleg.cryptography.algorithms.*
import dev.whyoleg.cryptography.materials.key.*
import dev.whyoleg.cryptography.operations.*
import dev.whyoleg.cryptography.providers.base.*
import dev.whyoleg.cryptography.providers.openssl3.internal.*
import dev.whyoleg.cryptography.providers.openssl3.internal.cinterop.*
import dev.whyoleg.cryptography.providers.openssl3.materials.*
import kotlinx.cinterop.*
import platform.posix.*
import kotlin.experimental.*
import dev.whyoleg.cryptography.providers.openssl3.operations.*
import dev.whyoleg.cryptography.serialization.asn1.modules.*
import dev.whyoleg.cryptography.serialization.asn1.ObjectIdentifier
import dev.whyoleg.cryptography.providers.base.materials.*

internal object Openssl3XDH : XDH {
private fun algorithmName(curve: XDH.Curve): String = when (curve) {
XDH.Curve.X25519 -> "X25519"
XDH.Curve.X448 -> "X448"
}
private fun oid(curve: XDH.Curve): ObjectIdentifier = when (curve) {
XDH.Curve.X25519 -> ObjectIdentifier.X25519
XDH.Curve.X448 -> ObjectIdentifier.X448
}

override fun publicKeyDecoder(curve: XDH.Curve): KeyDecoder<XDH.PublicKey.Format, XDH.PublicKey> =
object : Openssl3PublicKeyDecoder<XDH.PublicKey.Format, XDH.PublicKey>(algorithmName(curve)) {
override fun inputType(format: XDH.PublicKey.Format): String = when (format) {
XDH.PublicKey.Format.DER -> "DER"
XDH.PublicKey.Format.PEM -> "PEM"
XDH.PublicKey.Format.JWK,
XDH.PublicKey.Format.RAW -> error("should not be called: handled explicitly in decodeFromBlocking")
}

override fun decodeFromByteArrayBlocking(format: XDH.PublicKey.Format, bytes: ByteArray): XDH.PublicKey = when (format) {
XDH.PublicKey.Format.RAW -> super.decodeFromByteArrayBlocking(
XDH.PublicKey.Format.DER,
wrapSubjectPublicKeyInfo(UnknownKeyAlgorithmIdentifier(oid(curve)), bytes)
)
else -> super.decodeFromByteArrayBlocking(format, bytes)
}

override fun wrapKey(key: CPointer<EVP_PKEY>): XDH.PublicKey = XdhPublicKey(key, curve)
}

override fun privateKeyDecoder(curve: XDH.Curve): KeyDecoder<XDH.PrivateKey.Format, XDH.PrivateKey> =
object : Openssl3PrivateKeyDecoder<XDH.PrivateKey.Format, XDH.PrivateKey>(algorithmName(curve)) {
override fun inputType(format: XDH.PrivateKey.Format): String = when (format) {
XDH.PrivateKey.Format.DER -> "DER"
XDH.PrivateKey.Format.PEM -> "PEM"
XDH.PrivateKey.Format.JWK,
XDH.PrivateKey.Format.RAW -> error("should not be called: handled explicitly in decodeFromBlocking")
}

override fun decodeFromByteArrayBlocking(format: XDH.PrivateKey.Format, bytes: ByteArray): XDH.PrivateKey = when (format) {
XDH.PrivateKey.Format.RAW -> super.decodeFromByteArrayBlocking(
XDH.PrivateKey.Format.DER,
wrapPrivateKeyInfo(0, UnknownKeyAlgorithmIdentifier(oid(curve)), bytes)
)
else -> super.decodeFromByteArrayBlocking(format, bytes)
}

override fun wrapKey(key: CPointer<EVP_PKEY>): XDH.PrivateKey = XdhPrivateKey(key, curve)
}

override fun keyPairGenerator(curve: XDH.Curve): KeyGenerator<XDH.KeyPair> =
object : Openssl3KeyPairGenerator<XDH.KeyPair>(algorithmName(curve)) {
override fun MemScope.createParams(): CValuesRef<OSSL_PARAM>? = null
override fun wrapKeyPair(keyPair: CPointer<EVP_PKEY>): XDH.KeyPair = XdhKeyPair(
publicKey = XdhPublicKey(keyPair, curve),
privateKey = XdhPrivateKey(keyPair, curve)
)
}

private class XdhKeyPair(
override val publicKey: XDH.PublicKey,
override val privateKey: XDH.PrivateKey,
) : XDH.KeyPair

private class XdhPublicKey(
key: CPointer<EVP_PKEY>,
private val curve: XDH.Curve,
) : XDH.PublicKey, Openssl3PublicKeyEncodable<XDH.PublicKey.Format>(key), SharedSecretGenerator<XDH.PrivateKey> {
override fun outputType(format: XDH.PublicKey.Format): String = when (format) {
XDH.PublicKey.Format.DER -> "DER"
XDH.PublicKey.Format.PEM -> "PEM"
XDH.PublicKey.Format.JWK,
XDH.PublicKey.Format.RAW -> error("should not be called: handled explicitly in encodeToBlocking")
}

override fun encodeToByteArrayBlocking(format: XDH.PublicKey.Format): ByteArray = when (format) {
XDH.PublicKey.Format.RAW -> unwrapSubjectPublicKeyInfo(oid(curve), super.encodeToByteArrayBlocking(XDH.PublicKey.Format.DER))
else -> super.encodeToByteArrayBlocking(format)
}

override fun sharedSecretGenerator(): SharedSecretGenerator<XDH.PrivateKey> = this
override fun generateSharedSecretToByteArrayBlocking(other: XDH.PrivateKey): ByteArray {
check(other is XdhPrivateKey)
return deriveSharedSecret(publicKey = key, privateKey = other.key)
}
}

private class XdhPrivateKey(
key: CPointer<EVP_PKEY>,
private val curve: XDH.Curve,
) : XDH.PrivateKey, Openssl3PrivateKeyEncodable<XDH.PrivateKey.Format>(key), SharedSecretGenerator<XDH.PublicKey> {
override fun outputType(format: XDH.PrivateKey.Format): String = when (format) {
XDH.PrivateKey.Format.DER -> "DER"
XDH.PrivateKey.Format.PEM -> "PEM"
XDH.PrivateKey.Format.JWK,
XDH.PrivateKey.Format.RAW -> error("should not be called: handled explicitly in encodeToBlocking")
}

override fun encodeToByteArrayBlocking(format: XDH.PrivateKey.Format): ByteArray = when (format) {
XDH.PrivateKey.Format.RAW -> unwrapPrivateKeyInfo(oid(curve), super.encodeToByteArrayBlocking(XDH.PrivateKey.Format.DER))
else -> super.encodeToByteArrayBlocking(format)
}

override fun sharedSecretGenerator(): SharedSecretGenerator<XDH.PublicKey> = this
override fun generateSharedSecretToByteArrayBlocking(other: XDH.PublicKey): ByteArray {
check(other is XdhPublicKey)
return deriveSharedSecret(publicKey = other.key, privateKey = key)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2025 Oleg Yukhnevich. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.whyoleg.cryptography.providers.openssl3.operations

import dev.whyoleg.cryptography.providers.openssl3.internal.*
import dev.whyoleg.cryptography.providers.base.*
import dev.whyoleg.cryptography.providers.openssl3.internal.cinterop.*
import kotlinx.cinterop.*
import platform.posix.*
import kotlin.experimental.*

@OptIn(UnsafeNumber::class)
internal fun deriveSharedSecret(
publicKey: CPointer<EVP_PKEY>,
privateKey: CPointer<EVP_PKEY>,
): ByteArray = memScoped {
val context = checkError(EVP_PKEY_CTX_new_from_pkey(null, privateKey, null))
try {
checkError(EVP_PKEY_derive_init(context))
checkError(EVP_PKEY_derive_set_peer(context, publicKey))
val secretSize = alloc<size_tVar>()
checkError(EVP_PKEY_derive(context, null, secretSize.ptr))
val secret = ByteArray(secretSize.value.toInt())
checkError(EVP_PKEY_derive(context, secret.refToU(0), secretSize.ptr))
secret
} finally {
EVP_PKEY_CTX_free(context)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,57 @@ import dev.whyoleg.cryptography.operations.*
import dev.whyoleg.cryptography.providers.base.*
import dev.whyoleg.cryptography.providers.openssl3.internal.*
import dev.whyoleg.cryptography.providers.openssl3.internal.cinterop.*
import dev.whyoleg.cryptography.providers.base.operations.*
import kotlinx.cinterop.*
import platform.posix.*
import kotlin.experimental.*

internal abstract class Openssl3DigestSignatureGenerator(
private val privateKey: CPointer<EVP_PKEY>,
private val hashAlgorithm: String,
// when null, performs one-shot signing (e.g., EdDSA)
private val hashAlgorithm: String?,
) : SignatureGenerator {
@OptIn(ExperimentalNativeApi::class)
private val cleaner = privateKey.upRef().cleaner()

protected abstract fun MemScope.createParams(): CValuesRef<OSSL_PARAM>?

override fun createSignFunction(): SignFunction {
return Openssl3DigestSignFunction(Resource(checkError(EVP_MD_CTX_new()), ::EVP_MD_CTX_free))
override fun createSignFunction(): SignFunction = when (hashAlgorithm) {
null -> AccumulatingSignFunction { data ->
memScoped {
val ctx = checkError(EVP_MD_CTX_new())
try {
checkError(
EVP_DigestSignInit_ex(
ctx = ctx,
pctx = null,
mdname = null, // one-shot mode
libctx = null,
props = null,
pkey = privateKey,
params = createParams()
)
)
val siglen = alloc<size_tVar>()
data.usePinned { pin ->
checkError(EVP_DigestSign(ctx, null, siglen.ptr, pin.safeAddressOfU(0), data.size.convert()))
val out = ByteArray(siglen.value.convert())
out.usePinned { outPin ->
checkError(EVP_DigestSign(ctx, outPin.safeAddressOfU(0), siglen.ptr, pin.safeAddressOfU(0), data.size.convert()))
}
out.ensureSizeExactly(siglen.value.convert())
out
}
} finally {
EVP_MD_CTX_free(ctx)
}
}
}
else -> StreamingSignFunction(Resource(checkError(EVP_MD_CTX_new()), ::EVP_MD_CTX_free))
}

// inner class to have a reference to class with cleaner
private inner class Openssl3DigestSignFunction(
private inner class StreamingSignFunction(
private val context: Resource<CPointer<EVP_MD_CTX>>,
) : SignFunction, SafeCloseable(SafeCloseAction(context, AutoCloseable::close)) {
init {
Expand Down Expand Up @@ -66,7 +98,7 @@ internal abstract class Openssl3DigestSignatureGenerator(
EVP_DigestSignInit_ex(
ctx = context,
pctx = null,
mdname = hashAlgorithm,
mdname = hashAlgorithm!!,
libctx = null,
props = null,
pkey = privateKey,
Expand All @@ -75,4 +107,6 @@ internal abstract class Openssl3DigestSignatureGenerator(
)
}
}

// One-shot path now handled by AccumulatingSignFunction in createSignFunction
}
Loading