Skip to content
Draft
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
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ cryptography-kotlin provides multiplatform API which consists of multiple compon
* [Secure random][Secure random] with [kotlin.Random][kotlin.Random] like API which can be used independently of other modules
* common API to use different cryptography operations,
like [ciphers][ciphers], [digests][digests], [signatures][signatures], [key derivation][key derivation], [Key agreement][Key agreement]
* multiple algorithms definitions, like [AES][AES], [RSA][RSA], [ECDSA][ECDSA], [ECDH][ECDH], [SHA][SHA256], [HMAC][HMAC]
* multiple algorithms definitions, like [AES][AES], [RSA][RSA], [ECDSA][ECDSA], [ECDH][ECDH], [DH][DH], [SHA][SHA256], [HMAC][HMAC]
and [PBKDF2][PBKDF2]
* multiple cryptography [providers][providers], like [OpenSSL][OpenSSL], [WebCrypto][WebCrypto], [CryptoKit][CryptoKit] and [JDK][JDK]

Expand Down Expand Up @@ -75,6 +75,8 @@ Additionally, it's possible to use [BOM][BOM] or [Gradle version catalog][Gradle

[ECDH]: https://whyoleg.github.io/cryptography-kotlin/api/cryptography-core/dev.whyoleg.cryptography.algorithms/-e-c-d-h/index.html

[DH]: https://whyoleg.github.io/cryptography-kotlin/api/cryptography-core/dev.whyoleg.cryptography.algorithms/-d-h/index.html

[PBKDF2]: https://whyoleg.github.io/cryptography-kotlin/api/cryptography-core/dev.whyoleg.cryptography.algorithms/-p-b-k-d-f2/index.html

[HKDF]: https://whyoleg.github.io/cryptography-kotlin/api/cryptography-core/dev.whyoleg.cryptography.algorithms/-h-k-d-f/index.html
Expand Down
85 changes: 85 additions & 0 deletions cryptography-core/src/commonMain/kotlin/algorithms/DH.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright (c) 2024 Oleg Yukhnevich. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.whyoleg.cryptography.algorithms

import dev.whyoleg.cryptography.*
import dev.whyoleg.cryptography.materials.key.*
import dev.whyoleg.cryptography.operations.*
import kotlin.jvm.*

@SubclassOptInRequired(CryptographyProviderApi::class)
public interface DH : CryptographyAlgorithm {
override val id: CryptographyAlgorithmId<DH> get() = Companion

public companion object : CryptographyAlgorithmId<DH>("DH")

public fun publicKeyDecoder(parameters: Parameters): KeyDecoder<PublicKey.Format, PublicKey>
public fun privateKeyDecoder(parameters: Parameters): KeyDecoder<PrivateKey.Format, PrivateKey>
public fun keyPairGenerator(parameters: Parameters): KeyGenerator<KeyPair>

public fun parametersDecoder(): KeyDecoder<Parameters.Format, Parameters>
public fun parametersGenerator(keySize: Int = 2048): KeyGenerator<Parameters>

@SubclassOptInRequired(CryptographyProviderApi::class)
public interface Parameters : EncodableKey<Parameters.Format> {
public sealed class Format : KeyFormat {
final override fun toString(): String = name

// DER = Distinguished Encoding Rules
public data object DER : Format() {
override val name: String get() = "DER"
}

// PEM = Privacy-Enhanced Mail
public data object PEM : Format() {
override val name: String get() = "PEM"
}
}
}

@SubclassOptInRequired(CryptographyProviderApi::class)
public interface KeyPair : Key {
public val publicKey: PublicKey
public val privateKey: PrivateKey
}

@SubclassOptInRequired(CryptographyProviderApi::class)
public interface PublicKey : EncodableKey<PublicKey.Format> {
public fun sharedSecretGenerator(): SharedSecretGenerator<PrivateKey>

public sealed class Format : KeyFormat {
final override fun toString(): String = name

// DER = Distinguished Encoding Rules (SPKI = SubjectPublicKeyInfo)
public data object DER : Format() {
override val name: String get() = "DER"
}

// PEM = Privacy-Enhanced Mail (SPKI = SubjectPublicKeyInfo)
public data object PEM : Format() {
override val name: String get() = "PEM"
}
}
}

@SubclassOptInRequired(CryptographyProviderApi::class)
public interface PrivateKey : EncodableKey<PrivateKey.Format> {
public fun sharedSecretGenerator(): SharedSecretGenerator<PublicKey>

public sealed class Format : KeyFormat {
final override fun toString(): String = name

// DER = Distinguished Encoding Rules (via PrivateKeyInfo from PKCS8)
public data object DER : Format() {
override val name: String get() = "DER"
}

// PEM = Privacy-Enhanced Mail (via PrivateKeyInfo from PKCS8)
public data object PEM : Format() {
override val name: String get() = "PEM"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ internal class JdkCryptographyProvider(provider: Provider?) : CryptographyProvid
RSA.RAW -> JdkRsaRaw(state)
ECDSA -> JdkEcdsa(state)
ECDH -> JdkEcdh(state)
DH -> JdkDh(state)
PBKDF2 -> JdkPbkdf2(state)
HKDF -> JdkHkdf(state, this)
else -> null
Expand Down
187 changes: 187 additions & 0 deletions cryptography-providers/jdk/src/jvmMain/kotlin/algorithms/JdkDh.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/*
* Copyright (c) 2024 Oleg Yukhnevich. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.whyoleg.cryptography.providers.jdk.algorithms

import dev.whyoleg.cryptography.algorithms.*
import dev.whyoleg.cryptography.materials.key.*
import dev.whyoleg.cryptography.operations.*
import dev.whyoleg.cryptography.providers.base.materials.*
import dev.whyoleg.cryptography.providers.jdk.*
import dev.whyoleg.cryptography.providers.jdk.materials.*
import dev.whyoleg.cryptography.providers.jdk.operations.*
import dev.whyoleg.cryptography.serialization.pem.*
import java.security.spec.*
import javax.crypto.interfaces.*
import javax.crypto.spec.*

internal class JdkDh(private val state: JdkCryptographyState) : DH {
override fun publicKeyDecoder(parameters: DH.Parameters): KeyDecoder<DH.PublicKey.Format, DH.PublicKey> {
return DhPublicKeyDecoder(parameters)
}

override fun privateKeyDecoder(parameters: DH.Parameters): KeyDecoder<DH.PrivateKey.Format, DH.PrivateKey> {
return DhPrivateKeyDecoder(parameters)
}

override fun keyPairGenerator(parameters: DH.Parameters): KeyGenerator<DH.KeyPair> {
return DhKeyPairGenerator(parameters)
}

override fun parametersDecoder(): KeyDecoder<DH.Parameters.Format, DH.Parameters> {
return DhParametersDecoder()
}

override fun parametersGenerator(keySize: Int): KeyGenerator<DH.Parameters> {
return DhParametersGenerator(keySize)
}

private inner class DhParametersGenerator(
private val keySize: Int,
) : JdkKeyPairGenerator<DH.Parameters>(state, "DH") {
override fun JKeyPairGenerator.init() {
initialize(keySize, state.secureRandom)
}

override fun JKeyPair.convert(): DH.Parameters {
val publicKey = public as DHPublicKey
return DhParameters(state, publicKey.params)
}
}

private inner class DhParametersDecoder : KeyDecoder<DH.Parameters.Format, DH.Parameters> {
override suspend fun decodeFromByteArray(format: DH.Parameters.Format, bytes: ByteArray): DH.Parameters {
return decodeFromByteArrayBlocking(format, bytes)
}

override fun decodeFromByteArrayBlocking(format: DH.Parameters.Format, bytes: ByteArray): DH.Parameters = when (format) {
DH.Parameters.Format.DER -> decodeFromDer(bytes)
DH.Parameters.Format.PEM -> decodeFromDer(unwrapPem(PemLabel.DHParams, bytes))
}

private fun decodeFromDer(bytes: ByteArray): DH.Parameters {
val algorithmParameters = state.algorithmParameters("DH")
algorithmParameters.init(bytes)
val parameterSpec = algorithmParameters.getParameterSpec(DHParameterSpec::class.java)
return DhParameters(state, parameterSpec)
}
}

private inner class DhKeyPairGenerator(
private val parameters: DH.Parameters,
) : JdkKeyPairGenerator<DH.KeyPair>(state, "DH") {
override fun JKeyPairGenerator.init() {
val dhParameters = (parameters as DhParameters).parameterSpec
initialize(dhParameters, state.secureRandom)
}

override fun JKeyPair.convert(): DH.KeyPair {
return DhKeyPair(
publicKey = DhPublicKey(state, public as DHPublicKey),
privateKey = DhPrivateKey(state, private as DHPrivateKey)
)
}
}

private inner class DhPublicKeyDecoder(
private val parameters: DH.Parameters,
) : JdkPublicKeyDecoder<DH.PublicKey.Format, DH.PublicKey>(state, "DH") {
override fun JPublicKey.convert(): DH.PublicKey {
check(this is DHPublicKey)
val dhParameters = (parameters as DhParameters).parameterSpec
check(this.params.p == dhParameters.p && this.params.g == dhParameters.g) {
"Key parameters do not match expected parameters"
}
return DhPublicKey(state, this)
}

override fun decodeFromByteArrayBlocking(format: DH.PublicKey.Format, bytes: ByteArray): DH.PublicKey = when (format) {
DH.PublicKey.Format.DER -> decodeFromDer(bytes)
DH.PublicKey.Format.PEM -> decodeFromDer(unwrapPem(PemLabel.PublicKey, bytes))
}
}

private inner class DhPrivateKeyDecoder(
private val parameters: DH.Parameters,
) : JdkPrivateKeyDecoder<DH.PrivateKey.Format, DH.PrivateKey>(state, "DH") {
override fun JPrivateKey.convert(): DH.PrivateKey {
check(this is DHPrivateKey)
val dhParameters = (parameters as DhParameters).parameterSpec
check(this.params.p == dhParameters.p && this.params.g == dhParameters.g) {
"Key parameters do not match expected parameters"
}
return DhPrivateKey(state, this)
}

override fun decodeFromByteArrayBlocking(format: DH.PrivateKey.Format, bytes: ByteArray): DH.PrivateKey = when (format) {
DH.PrivateKey.Format.DER -> decodeFromDer(bytes)
DH.PrivateKey.Format.PEM -> decodeFromDer(unwrapPem(PemLabel.PrivateKey, bytes))
}
}

private class DhParameters(
private val state: JdkCryptographyState,
val parameterSpec: DHParameterSpec,
) : DH.Parameters {
override suspend fun encodeToByteArray(format: DH.Parameters.Format): ByteArray {
return encodeToByteArrayBlocking(format)
}

override fun encodeToByteArrayBlocking(format: DH.Parameters.Format): ByteArray = when (format) {
DH.Parameters.Format.DER -> encodeToDerParameters()
DH.Parameters.Format.PEM -> wrapPem(PemLabel.DHParams, encodeToDerParameters())
}

private fun encodeToDerParameters(): ByteArray {
val algorithmParameters = state.algorithmParameters("DH")
algorithmParameters.init(parameterSpec)
return algorithmParameters.encoded
}
}

private class DhKeyPair(
override val publicKey: DH.PublicKey,
override val privateKey: DH.PrivateKey,
) : DH.KeyPair

private class DhPublicKey(
private val state: JdkCryptographyState,
private val key: DHPublicKey,
) : DH.PublicKey, JdkEncodableKey<DH.PublicKey.Format>(key), SharedSecretGenerator<DH.PrivateKey> {
val dhKey: DHPublicKey get() = key
private val keyAgreement = state.keyAgreement("DH")

override fun sharedSecretGenerator(): SharedSecretGenerator<DH.PrivateKey> = this

override fun generateSharedSecretToByteArrayBlocking(other: DH.PrivateKey): ByteArray {
check(other is DhPrivateKey) { "Only key produced by JDK provider is supported" }
return keyAgreement.doAgreement(state, other.dhKey, this.key)
}

override fun encodeToByteArrayBlocking(format: DH.PublicKey.Format): ByteArray = when (format) {
DH.PublicKey.Format.DER -> encodeToDer()
DH.PublicKey.Format.PEM -> wrapPem(PemLabel.PublicKey, encodeToDer())
}
}

private class DhPrivateKey(
private val state: JdkCryptographyState,
private val key: DHPrivateKey,
) : DH.PrivateKey, JdkEncodableKey<DH.PrivateKey.Format>(key), SharedSecretGenerator<DH.PublicKey> {
val dhKey: DHPrivateKey get() = key
private val keyAgreement = state.keyAgreement("DH")

override fun sharedSecretGenerator(): SharedSecretGenerator<DH.PublicKey> = this

override fun generateSharedSecretToByteArrayBlocking(other: DH.PublicKey): ByteArray {
check(other is DhPublicKey) { "Only key produced by JDK provider is supported" }
return keyAgreement.doAgreement(state, this.key, other.dhKey)
}

override fun encodeToByteArrayBlocking(format: DH.PrivateKey.Format): ByteArray = when (format) {
DH.PrivateKey.Format.DER -> encodeToDer()
DH.PrivateKey.Format.PEM -> wrapPem(PemLabel.PrivateKey, encodeToDer())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ internal object Openssl3CryptographyProvider : CryptographyProvider() {
AES.GCM -> Openssl3AesGcm
ECDSA -> Openssl3Ecdsa
ECDH -> Openssl3Ecdh
DH -> Openssl3Dh
RSA.PSS -> Openssl3RsaPss
RSA.PKCS1 -> Openssl3RsaPkcs1
RSA.OAEP -> Openssl3RsaOaep
Expand Down
Loading