Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* `RSAPadding`
* `SymmetricEncryptionAlgorithm` and its nested implementations
* Fix project setup on non-macOS build hosts
* COSE: Add identifiers `-9`, `-51`, `-53` for fully-specified algorithms for ECDSA, see [RFC 9864](https://www.rfc-editor.org/rfc/rfc9864.html#name-elliptic-curve-digital-sign)
* Fix Android project setup
* Limit Keystore operations with `limitedParallelism` from `kotlinx.coroutines`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,13 @@ package at.asitplus.signum.indispensable.cosef

import at.asitplus.KmmResult
import at.asitplus.catching
import at.asitplus.signum.Enumerable
import at.asitplus.signum.Enumeration
import at.asitplus.signum.UnsupportedCryptoException
import at.asitplus.signum.indispensable.DataIntegrityAlgorithm
import at.asitplus.signum.indispensable.Digest
import at.asitplus.signum.indispensable.HMAC
import at.asitplus.signum.indispensable.MessageAuthenticationCode
import at.asitplus.signum.indispensable.RSAPadding
import at.asitplus.signum.indispensable.SignatureAlgorithm
import at.asitplus.signum.indispensable.SpecializedDataIntegrityAlgorithm
import at.asitplus.signum.indispensable.SpecializedMessageAuthenticationCode
import at.asitplus.signum.indispensable.SpecializedSignatureAlgorithm
import at.asitplus.signum.indispensable.*
import at.asitplus.signum.indispensable.misc.bit
import at.asitplus.signum.indispensable.symmetric.SpecializedSymmetricEncryptionAlgorithm
import at.asitplus.signum.indispensable.symmetric.SymmetricEncryptionAlgorithm
import at.asitplus.signum.Enumerable
import at.asitplus.signum.Enumeration
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
Expand Down Expand Up @@ -56,7 +48,10 @@ sealed interface CoseAlgorithm : Enumerable {
}

@Serializable(with = CoseAlgorithmSerializer::class)
sealed class SymmetricEncryption(override val coseValue: Int, override val algorithm: SymmetricEncryptionAlgorithm<*, *, *>) :
sealed class SymmetricEncryption(
override val coseValue: Int,
override val algorithm: SymmetricEncryptionAlgorithm<*, *, *>
) :
CoseAlgorithm.Symmetric, SpecializedSymmetricEncryptionAlgorithm {


Expand All @@ -74,7 +69,14 @@ sealed interface CoseAlgorithm : Enumerable {
data object ChaCha20Poly1305 : SymmetricEncryption(24, SymmetricEncryptionAlgorithm.ChaCha20Poly1305)

companion object : Enumeration<SymmetricEncryption> {
override val entries: Collection<SymmetricEncryption> by lazy { setOf(A128GCM, A192GCM, A256GCM, ChaCha20Poly1305) }
override val entries: Collection<SymmetricEncryption> by lazy {
setOf(
A128GCM,
A192GCM,
A256GCM,
ChaCha20Poly1305
)
}
}
}

Expand All @@ -88,12 +90,24 @@ sealed interface CoseAlgorithm : Enumerable {
@Serializable(with = CoseAlgorithmSerializer::class)
data object ES256 : Signature(-7, SignatureAlgorithm.ECDSAwithSHA256)

@Serializable(with = CoseAlgorithmSerializer::class)
data object ESP256 :
Signature(-9, SignatureAlgorithm.ECDSA(Digest.SHA256, requiredCurve = ECCurve.SECP_256_R_1))

@Serializable(with = CoseAlgorithmSerializer::class)
data object ES384 : Signature(-35, SignatureAlgorithm.ECDSAwithSHA384)

@Serializable(with = CoseAlgorithmSerializer::class)
data object ESP384 :
Signature(-51, SignatureAlgorithm.ECDSA(Digest.SHA384, requiredCurve = ECCurve.SECP_384_R_1))

@Serializable(with = CoseAlgorithmSerializer::class)
data object ES512 : Signature(-36, SignatureAlgorithm.ECDSAwithSHA512)

@Serializable(with = CoseAlgorithmSerializer::class)
data object ESP512 :
Signature(-52, SignatureAlgorithm.ECDSA(Digest.SHA512, requiredCurve = ECCurve.SECP_521_R_1))

// RSASSA-PSS with SHA-size
@Serializable(with = CoseAlgorithmSerializer::class)
data object PS256 : Signature(-37, SignatureAlgorithm.RSAwithSHA256andPSSPadding)
Expand Down Expand Up @@ -121,8 +135,11 @@ sealed interface CoseAlgorithm : Enumerable {
companion object : Enumeration<Signature> {
override val entries: Collection<Signature> by lazy {
setOf(
ESP256,
ES256,
ESP384,
ES384,
ESP512,
ES512,
PS256,
PS384,
Expand Down Expand Up @@ -201,9 +218,24 @@ object CoseAlgorithmSerializer : KSerializer<CoseAlgorithm> {
fun SignatureAlgorithm.toCoseAlgorithm(): KmmResult<CoseAlgorithm.Signature> = catching {
when (this) {
is SignatureAlgorithm.ECDSA -> when (this.digest) {
Digest.SHA256 -> CoseAlgorithm.Signature.ES256
Digest.SHA384 -> CoseAlgorithm.Signature.ES384
Digest.SHA512 -> CoseAlgorithm.Signature.ES512
Digest.SHA256 -> when (this.requiredCurve) {
ECCurve.SECP_256_R_1 -> CoseAlgorithm.Signature.ESP256
null -> CoseAlgorithm.Signature.ES256
else -> throw UnsupportedCryptoException("ECDSA with ${this.digest} and $requiredCurve is unsupported by COSE")
}

Digest.SHA384 -> when (this.requiredCurve) {
ECCurve.SECP_384_R_1 -> CoseAlgorithm.Signature.ESP384
null -> CoseAlgorithm.Signature.ES384
else -> throw UnsupportedCryptoException("ECDSA with ${this.digest} and $requiredCurve is unsupported by COSE")
}

Digest.SHA512 -> when (this.requiredCurve) {
ECCurve.SECP_521_R_1 -> CoseAlgorithm.Signature.ESP512
null -> CoseAlgorithm.Signature.ES512
else -> throw UnsupportedCryptoException("ECDSA with ${this.digest} and $requiredCurve is unsupported by COSE")
}

else -> throw UnsupportedCryptoException("ECDSA with ${this.digest} is unsupported by COSE")
}

Expand All @@ -226,7 +258,7 @@ fun SignatureAlgorithm.toCoseAlgorithm(): KmmResult<CoseAlgorithm.Signature> = c
}

fun DataIntegrityAlgorithm.toCoseAlgorithm(): KmmResult<CoseAlgorithm.DataIntegrity> =
when (this) {
when (this) {
is SignatureAlgorithm -> toCoseAlgorithm()
is MessageAuthenticationCode -> toCoseAlgorithm()
}
Expand Down Expand Up @@ -259,9 +291,11 @@ fun SymmetricEncryptionAlgorithm<*, *, *>.toCoseAlgorithm(): KmmResult<CoseAlgor
/** Tries to find a matching COSE algorithm. Note that COSE imposes curve restrictions on ECDSA based on the digest. */
fun SpecializedSignatureAlgorithm.toCoseAlgorithm(): KmmResult<CoseAlgorithm.Signature> =
this.algorithm.toCoseAlgorithm()

/** Tries to find a matching COSE algorithm. Note that COSE imposes curve restrictions on ECDSA based on the digest. */
fun SpecializedDataIntegrityAlgorithm.toCoseAlgorithm(): KmmResult<CoseAlgorithm.DataIntegrity> =
this.algorithm.toCoseAlgorithm()

/** Tries to find a matching COSE algorithm. Note that COSE imposes curve restrictions on ECDSA based on the digest. */
fun SpecializedMessageAuthenticationCode.toCoseAlgorithm(): KmmResult<CoseAlgorithm.MAC> =
this.algorithm.toCoseAlgorithm()
18 changes: 12 additions & 6 deletions indispensable-cosef/src/jvmTest/kotlin/ConversionTests.kt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import at.asitplus.KmmResult
import at.asitplus.signum.indispensable.SignatureAlgorithm
import at.asitplus.signum.indispensable.cosef.CoseAlgorithm
import at.asitplus.signum.indispensable.cosef.toCoseAlgorithm
import at.asitplus.signum.indispensable.cosef.toCoseKey
Expand All @@ -9,12 +10,10 @@ import at.asitplus.testballoon.minus
import at.asitplus.testballoon.withData
import de.infix.testBalloon.framework.core.testSuite
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import java.security.KeyPairGenerator
import java.security.interfaces.ECPublicKey
import kotlin.random.Random
import de.infix.testBalloon.framework.core.TestConfig
import kotlin.time.Duration.Companion.minutes
import de.infix.testBalloon.framework.core.testScope

//somehow including kmmresult-test makes this fail
infix fun <T> KmmResult<T>.shouldSucceedWith(b: T): T =
Expand All @@ -34,10 +33,17 @@ val ConversionTests by testSuite {
}
}
}
"COSE -> X509 -> COSE is stable" - {
withData(CoseAlgorithm.Signature.entries) {
"COSE -> X509 -> COSE" - {
withData(CoseAlgorithm.Signature.entries) - {
it.toX509SignatureAlgorithm().getOrNull()?.let { x509 ->
x509.toCoseAlgorithm() shouldSucceedWith it
if (it.algorithm is SignatureAlgorithm.ECDSA && (it.algorithm as SignatureAlgorithm.ECDSA).requiredCurve != null) {
"Curve information is lost" {
val algorithm = x509.toCoseAlgorithm().getOrThrow()
algorithm shouldNotBe it
algorithm.toString().takeLast(3) shouldBe it.toString().takeLast(3)
algorithm.toString().take(2) shouldBe it.toString().take(2)
}
} else "is stable" { x509.toCoseAlgorithm() shouldSucceedWith it }
}
}
}
Expand Down
Loading