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
14 changes: 5 additions & 9 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ tasks.register<JacocoReport>("jacocoRootReport") {
include("tmp/kotlin-classes/jvmMain/**")
// Exclude exchange package (needs virtual health card for testing)
exclude("**/de/gematik/openhealth/smartcard/*exchange*/**")
exclude("**/de/gematik/openhealth/smartcard/*Exchange*/**")
exclude("**/de/gematik/openhealth/smartcard/*utils*/**")
exclude("**/de/gematik/openhealth/crypto/ByteUnit*")
exclude("**/de/gematik/openhealth/**/*Exception*")
exclude("**/de/gematik/openhealth/crypto/CmacAlgorithm*")
}
},
),
Expand Down Expand Up @@ -121,7 +124,6 @@ tasks.register<JacocoCoverageVerification>("jacocoRootVerification") {
(tasks.named("jacocoRootReport").get() as JacocoReport).classDirectories.files.map {
fileTree(it) {
exclude(
"**/de/gematik/openhealth/smartcard/exchange/**",
"**/*Impl*",
"*.internal.*",
"*.Generated*",
Expand All @@ -139,7 +141,7 @@ tasks.register<JacocoCoverageVerification>("jacocoRootVerification") {
limit {
counter = "LINE"
value = "COVEREDRATIO"
minimum = BigDecimal.valueOf(0.7)
minimum = BigDecimal.valueOf(0.6)
}
excludes =
listOf(
Expand All @@ -149,12 +151,6 @@ tasks.register<JacocoCoverageVerification>("jacocoRootVerification") {
)
}
}
doFirst {
println("\nVerifying coverage for:")
classDirectories.files.forEach { file ->
println("- ${file.absolutePath}")
}
}
}

val localProperties = Properties()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ val Int.bytes: ByteUnit get() = ByteUnit(this)
*/
@ExperimentalCryptoApi
val Int.bits: ByteUnit get() =
if (this % 8 ==
0
) {
if (this % 8 == 0) {
ByteUnit(this / 8)
} else {
error("Value must be multiple of 8")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class CmacTest {
)
}

@Ignore
@Ignore // TODO: check if we should support this
@Test
fun `cmac final can only be called once`() =
runTestWithProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,19 @@ package de.gematik.openhealth.crypto.kem

import de.gematik.openhealth.crypto.runTestWithProvider
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue

class KemTest {
private val secret1 = byteArrayOf(1, 2, 3)
private val secret2 = byteArrayOf(1, 2, 3)
private val secret3 = byteArrayOf(4, 5, 6)
private val wrapped1 = byteArrayOf(7, 8, 9)
private val wrapped2 = byteArrayOf(7, 8, 9)
private val wrapped3 = byteArrayOf(10, 11, 12)

@Test
fun `ml-kem-768 round trip`() =
runTestWithProvider {
Expand Down Expand Up @@ -49,4 +59,85 @@ class KemTest {

assertTrue(aliceDecapsulationResult.isValid(bobEncapsulationResult))
}

@Test
fun `KemEncapsulationResult equals should compare contents`() =
runTestWithProvider {
val result1 = KemEncapsulationResult(secret1, wrapped1)
val result2 = KemEncapsulationResult(secret2, wrapped2)
val result3 = KemEncapsulationResult(secret3, wrapped1)
val result4 = KemEncapsulationResult(secret1, wrapped3)

assertEquals(result1, result2)
assertNotEquals(result1, result3)
assertNotEquals(result1, result4)
}

@Test
fun `KemDecapsulationResult equals should compare contents`() =
runTestWithProvider {
val result1 = KemDecapsulationResult(secret1)
val result2 = KemDecapsulationResult(secret2)
val result3 = KemDecapsulationResult(secret3)

assertEquals(result1, result2)
assertNotEquals(result1, result3)
}

@Test
fun `KemDecapsulationResult isValid should compare shared secrets`() =
runTestWithProvider {
val decap = KemDecapsulationResult(secret1)
val encap1 = KemEncapsulationResult(secret2, wrapped1)
val encap2 = KemEncapsulationResult(secret3, wrapped1)

assertTrue(decap.isValid(encap1))
assertFalse(decap.isValid(encap2))
}

@Test
fun `KemEncapsulationResult should handle reference equality`() =
runTestWithProvider {
val result = KemEncapsulationResult(secret1, wrapped1)
val result2 = KemEncapsulationResult(secret1, wrapped1)
assertEquals(result, result)
assertEquals(result, result2)
}

@Test
fun `KemEncapsulationResult hashCode should be consistent`() =
runTestWithProvider {
val result1 = KemEncapsulationResult(secret1, wrapped1)
val result2 = KemEncapsulationResult(secret1, wrapped1)

assertEquals(result1.hashCode(), result2.hashCode())
}

@Test
fun `KemDecapsulationResult should handle reference equality`() =
runTestWithProvider {
val result = KemDecapsulationResult(secret1)
assertEquals(result, result) // Same instance
}

@Test
fun `KemDecapsulationResult hashCode should be consistent`() =
runTestWithProvider {
val result1 = KemDecapsulationResult(secret1)
val result2 = KemDecapsulationResult(secret1)

assertEquals(result1.hashCode(), result2.hashCode())
}

@Test
fun `KemDecapsulationResult isValid should handle empty secrets`() =
runTestWithProvider {
val decap = KemDecapsulationResult(byteArrayOf())
val encap = KemEncapsulationResult(secret1, wrapped1)
assertFalse(decap.isValid(encap))

val decap2 = KemDecapsulationResult(secret1)
val encap2 = KemEncapsulationResult(byteArrayOf(), wrapped1)
assertFalse(decap2.isValid(encap2))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,38 +112,6 @@ class EcPointTest {
}
}

// @Test
// fun `test point addition with infinity`() =
// runTestWithProvider {
// val x =
// BigInteger.parseString(
// "78028496B5ECAAB3C8B6C12E45DB1E02C9E4D26B4113BC4F015F60C5CCC0D206",
// 16,
// )
// val y =
// BigInteger.parseString(
// "A2AE1762A3831C1D20F03F8D1E3C0C39AFE6F09B4D44BBE80CD100987B05F92B",
// 16,
// )
// val point = EcPoint(curve, x, y)
// val infinity = EcPoint(curve, null, null)
//
// assertFalse(point.isInfinity)
// assertTrue(infinity.isInfinity)
//
// val sum1 = point + infinity
// val sum2 = infinity + point
// val sum3 = infinity + infinity
//
// assertEquals(point.x, sum1.x)
// assertEquals(point.y, sum1.y)
//
// assertEquals(point.x, sum2.x)
// assertEquals(point.y, sum2.y)
//
// assertTrue(sum3.isInfinity)
// }

@Test
fun `test scalar multiplication`() =
runTestWithProvider {
Expand All @@ -163,6 +131,25 @@ class EcPointTest {
assertFalse(result.isInfinity)
}

@Test
fun `test point addition with self`() =
runTestWithProvider {
val x =
BigInteger.parseString(
"78028496B5ECAAB3C8B6C12E45DB1E02C9E4D26B4113BC4F015F60C5CCC0D206",
16,
)
val y =
BigInteger.parseString(
"A2AE1762A3831C1D20F03F8D1E3C0C39AFE6F09B4D44BBE80CD100987B05F92B",
16,
)
val point = EcPoint(curve, x, y)

val doubled = point.plus(point)
assertEquals(doubled, point * BigInteger.TWO)
}

@Test
fun `test conversion to EcPublicKey`() =
runTestWithProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ private class JvmCmac(
*/
override fun final(): ByteArray =
runWithProvider {
cmac._final().toByteArray()
cmac._final().toByteArray().alsoDefer()
}
}

Expand Down
38 changes: 0 additions & 38 deletions crypto/src/jvm/kotlin/de/gematik/openhealth/crypto/LazySuspend.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package de.gematik.openhealth.smartcard.command

import de.gematik.openhealth.smartcard.HealthCardTestScope
import de.gematik.openhealth.smartcard.card.CardKey
import de.gematik.openhealth.smartcard.card.PsoAlgorithm
import de.gematik.openhealth.smartcard.data.getExpectedApdu
import de.gematik.openhealth.smartcard.data.getParameter
import de.gematik.openhealth.smartcard.hexSpaceFormat
Expand Down Expand Up @@ -52,4 +53,27 @@ class ManageSecurityEnvironmentCommandTest {
HealthCardTestScope().test(command).toHexString(hexSpaceFormat),
)
}

@Test
fun shouldCreateValidManageSecurityEnvironmentCommandForSigning() {
val parameters = arrayOf(true, false)

runParametrizedTest(*parameters) {
val dfSpecific = parameter<Boolean>()
val expectedAPDU =
getExpectedApdu("MANAGESECURITYENVIRONMENTCOMMAND_APDU-4", dfSpecific)

val command =
HealthCardCommand.manageSecEnvForSigning(
psoAlgorithm = PsoAlgorithm.SIGN_VERIFY_ECDSA,
key = KEY_PRK_EGK_AUT_CVC_E256,
dfSpecific = dfSpecific,
)

assertEquals(
expectedAPDU,
HealthCardTestScope().test(command).toHexString(hexSpaceFormat),
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -306,8 +306,8 @@ private val EXPECTED_APDU =
mapOf(
"MANAGESECURITYENVIRONMENTCOMMAND_APDU-4" to
mapOf(
"apdu-true" to "0022C1A409800100830189840110",
"apdu-false" to "0022C1A409800100830109840110",
"apdu-true" to "002241B606840189800100",
"apdu-false" to "002241B606840109800100",
),
),
// ManageSecurityEnvironmentCommand(de.gematik.ti.healthcardaccess.commands.ManageSecurityEnvironmentCommand$MseUseCase,de.gematik.ti.healthcardaccess.cardobjects.GemCvCertificate)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package de.gematik.openhealth.smartcard.identifier

import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNotSame

class ApplicationIdentifierTest {
@Test
fun `constructor should accept valid byte array`() {
val minAid = byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05)
val aidMin = ApplicationIdentifier(minAid)
assertContentEquals(minAid, aidMin.aid)

val maxAid = ByteArray(16) { it.toByte() }
val aidMax = ApplicationIdentifier(maxAid)
assertContentEquals(maxAid, aidMax.aid)
}

@Test
fun `constructor should throw for too short byte array`() {
assertFailsWith<IllegalArgumentException> {
ApplicationIdentifier(byteArrayOf(0x01, 0x02, 0x03, 0x04))
}
}

@Test
fun `constructor should throw for too long byte array`() {
assertFailsWith<IllegalArgumentException> {
ApplicationIdentifier(ByteArray(17) { it.toByte() })
}
}

@Test
fun `constructor should accept valid hex string`() {
val aid = ApplicationIdentifier("0102030405")
assertContentEquals(byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05), aid.aid)
}

@Test
fun `constructor should throw for invalid hex string length`() {
assertFailsWith<IllegalArgumentException> {
ApplicationIdentifier("01020304") // Too short
}
assertFailsWith<IllegalArgumentException> {
ApplicationIdentifier("0102030405060708091011121314151617") // Too long
}
}

@Test
fun `getter should return defensive copy`() {
val originalAid = byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05)
val aid = ApplicationIdentifier(originalAid)

// Verify that modifying the original array doesn't affect the AID
originalAid[0] = 0xFF.toByte()
assertContentEquals(byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05), aid.aid)

// Verify that modifying the returned array doesn't affect the AID
val returnedAid = aid.aid
returnedAid[0] = 0xFF.toByte()
assertContentEquals(byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05), aid.aid)

// Verify that each getter call returns a new array
assertNotSame(aid.aid, aid.aid)
}
}
Loading