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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ this changelog also includes the original WARDEN changelog.

# NEXT
* Add hook to explicitly perform additional verifications before issuing
* Dependency updates:
* Bouncy Castle 1.84
* Ktor 3.5.0
* kotlinx.coroutines 1.11.0
* kotlinx.serialization 1.11.0
* Matrix testing

# 1.0.0-RC10

Expand Down
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ serialization ="1.10.0"

slf4j = "1.7.36"
testballoon = "1.0.0-K2.3.0"
testballoonAddons = "0.9.0"
asp = "20260526"
testballoonAddons = "0.14.0"
asp = "20260609"
sbombastic ="0.0.3"
pitest = "1.19.0-rc.3"
pitest-junit5 = "1.2.2"
Expand Down
3 changes: 2 additions & 1 deletion serverside/makoto/src/main/kotlin/AttestationService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import at.asitplus.catchingUnwrapped
import at.asitplus.signum.indispensable.AndroidKeystoreAttestation
import at.asitplus.signum.indispensable.Attestation
import at.asitplus.signum.indispensable.IosHomebrewAttestation
import at.asitplus.signum.indispensable.encodeToDer
import at.asitplus.signum.indispensable.toJcaPublicKey
import ch.veehait.devicecheck.appattest.assertion.Assertion
import ch.veehait.devicecheck.appattest.assertion.AssertionChallengeValidator
Expand Down Expand Up @@ -377,7 +378,7 @@ object NoopAttestationService : AttestationService() {
)

is AndroidKeystoreAttestation -> KeyAttestation(
attestationProof.certificateChain.first().decodedPublicKey.getOrThrow().toJcaPublicKey().getOrThrow(),
attestationProof.certificateChain.first().publicKey.toJcaPublicKey().getOrThrow(),
AttestationResult.Android.NOOP(attestationProof.certificateChain.map { it.encodeToDer() })
)

Expand Down
13 changes: 4 additions & 9 deletions serverside/makoto/src/main/kotlin/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,8 @@
package at.asitplus.attestation

import at.asitplus.signum.indispensable.*
import at.asitplus.signum.indispensable.asn1.Asn1Decodable
import at.asitplus.signum.indispensable.asn1.Asn1Encodable
import at.asitplus.signum.indispensable.asn1.Asn1Sequence
import at.asitplus.signum.indispensable.asn1.Asn1String
import at.asitplus.signum.indispensable.asn1.encoding.Asn1
import at.asitplus.signum.indispensable.asn1.encoding.decodeToUtf8String
import at.asitplus.signum.indispensable.asn1.encoding.encodeToAsn1OctetStringPrimitive
import at.asitplus.awesn1.*
import at.asitplus.awesn1.encoding.*
import at.asitplus.signum.indispensable.io.ByteArrayBase64Serializer
import at.asitplus.signum.indispensable.io.X509CertificateBase64Serializer
import ch.veehait.devicecheck.appattest.attestation.ValidatedAttestation
Expand Down Expand Up @@ -127,7 +122,7 @@ data class CanonicalIosAttestation(val cert: X509Certificate, val receipt: Recei
fun toValidatedAttestation(): ValidatedAttestation = ValidatedAttestation(cert, receipt, iosVersion)

override fun encodeToTlv() = Asn1.Sequence {
+cert.toKmpCertificate().getOrThrow()
+cert.toKmpCertificate().getOrThrow().encodeToTlv()
+receipt.p7.encodeToAsn1OctetStringPrimitive()
iosVersion?.let { +Asn1String.UTF8(it) }
}
Expand Down Expand Up @@ -168,7 +163,7 @@ data class CanonicalIosAttestation(val cert: X509Certificate, val receipt: Recei

companion object : Asn1Decodable<Asn1Sequence, CanonicalIosAttestation> {
override fun doDecode(src: Asn1Sequence): CanonicalIosAttestation = src.decodeAs {
val cert = at.asitplus.signum.indispensable.pki.X509Certificate.decodeFromTlv(next() as Asn1Sequence)
val cert = at.asitplus.signum.indispensable.pki.Certificate.decodeFromTlv(next() as Asn1Sequence)

val receipt = next().asOctetString().content.let {
Receipt(Receipt.Payload.parse((it.readAsSignedData())), it)
Expand Down
3 changes: 2 additions & 1 deletion serverside/makoto/src/main/kotlin/Makoto.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import at.asitplus.catchingUnwrapped
import at.asitplus.signum.indispensable.AndroidKeystoreAttestation
import at.asitplus.signum.indispensable.Attestation
import at.asitplus.signum.indispensable.IosHomebrewAttestation
import at.asitplus.signum.indispensable.encodeToDer
import at.asitplus.signum.indispensable.toJcaPublicKey
import ch.veehait.devicecheck.appattest.AppleAppAttest
import ch.veehait.devicecheck.appattest.assertion.Assertion
Expand Down Expand Up @@ -544,7 +545,7 @@ class Makoto
).let {
when (it) {
is AttestationResult.Android -> KeyAttestation(
attestationProof.certificateChain.first().decodedPublicKey.getOrThrow().toJcaPublicKey()
attestationProof.certificateChain.first().publicKey.toJcaPublicKey()
.getOrThrow(), it
)

Expand Down
21 changes: 10 additions & 11 deletions serverside/makoto/src/test/kotlin/BuildNumberTest.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import at.asitplus.attestation.BuildNumber
import at.asitplus.testballoon.minus
import at.asitplus.testballoon.withData
import de.infix.testBalloon.framework.core.testSuite
import io.kotest.core.spec.style.FreeSpec
import io.kotest.datatest.withData
import at.asitplus.testballoon.matrix.*
import io.kotest.matchers.comparables.shouldBeLessThan
import java.util.*
import kotlin.random.Random

val BuildNumberTest by testSuite {
val BuildNumberTest by matrixSuite {

"presorted" - {
val buildTrains = List(50) { it }
Expand Down Expand Up @@ -59,11 +55,14 @@ val BuildNumberTest by testSuite {
}
}

withData(
compact("presorted build numbers") {
report = CompactReport.FailuresOnly
} - {
data(
testVectors.dropLast(1).mapIndexed { index, s -> index to s },
compact = true
) {
BuildNumber(it.second) shouldBeLessThan BuildNumber(testVectors[it.first + 1])
) test {
BuildNumber(it.second) shouldBeLessThan BuildNumber(testVectors[it.first + 1])
}
}
}
}
}
6 changes: 2 additions & 4 deletions serverside/makoto/src/test/kotlin/ClockTest.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import at.asitplus.attestation.toKotlinClock
import at.asitplus.testballoon.invoke
import at.asitplus.testballoon.minus
import de.infix.testBalloon.framework.core.testSuite
import at.asitplus.testballoon.matrix.*
import io.kotest.matchers.shouldBe
import java.time.Clock
import java.time.Instant
import kotlin.time.ExperimentalTime
import kotlin.time.toKotlinInstant

@OptIn(ExperimentalTime::class)
val ClockTest by testSuite {
val ClockTest by matrixSuite {

"java to kotlin" {
val javaClock = Clock.fixed(Instant.parse("2023-01-01T12:00:00Z"), Clock.systemUTC().zone)
Expand Down
6 changes: 2 additions & 4 deletions serverside/makoto/src/test/kotlin/ConfigSerializationTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ import at.asitplus.attestation.APPLE_DEFAULT_TRUSTED_ROOTS
import at.asitplus.attestation.IosAttestationConfiguration
import at.asitplus.attestation.IosAttestationConfiguration.OsVersions
import at.asitplus.attestation.android.*
import at.asitplus.testballoon.invoke
import at.asitplus.testballoon.minus
import de.infix.testBalloon.framework.core.testSuite
import at.asitplus.testballoon.matrix.*
import io.kotest.matchers.shouldBe

val serializationTest by testSuite {
val serializationTest by matrixSuite {
val androidAttestationConfiguration = AndroidAttestationConfiguration(
applications = listOf(
/*(1)!*/AndroidAttestationConfiguration.AppData(
Expand Down
5 changes: 2 additions & 3 deletions serverside/makoto/src/test/kotlin/ConfigurationBuilderTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ import at.asitplus.attestation.IosAttestationConfiguration
import at.asitplus.attestation.IosAttestationConfiguration.OsVersions
import at.asitplus.attestation.Makoto
import at.asitplus.attestation.android.AndroidAttestationConfiguration
import at.asitplus.testballoon.invoke
import de.infix.testBalloon.framework.core.testSuite
import at.asitplus.testballoon.matrix.*
import io.kotest.matchers.shouldBe
import kotlin.time.Duration.Companion.seconds

val ConfigurationBuilderTest by testSuite {
val ConfigurationBuilderTest by matrixSuite {

"iOS AppData builder preserves trusted roots" {
val roots = APPLE_DEFAULT_TRUSTED_ROOTS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ package at.asitplus.attestation

import at.asitplus.attestation.android.AndroidAttestationConfiguration
import at.asitplus.attestation.android.TrustedRoot
import at.asitplus.testballoon.invoke
import de.infix.testBalloon.framework.core.testSuite
import at.asitplus.testballoon.matrix.*
import io.kotest.matchers.shouldBe
import java.security.KeyPairGenerator
import kotlin.time.Duration
import kotlin.time.Instant

val DebugStatementSerializationRegressionTests by testSuite {
val DebugStatementSerializationRegressionTests by matrixSuite {

"WardenDebugAttestationStatement.serializeCompact does not crash (TrustedRootSerializer init)" {
val keyPair = KeyPairGenerator.getInstance("RSA").apply { initialize(2048) }.generateKeyPair()
Expand Down
6 changes: 2 additions & 4 deletions serverside/makoto/src/test/kotlin/FeatureDemonstration.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import at.asitplus.attestation.android.AndroidAttestationConfiguration
import at.asitplus.attestation.android.AttestationKeyDescription
import at.asitplus.attestation.android.PatchLevel
import at.asitplus.attestation.data.attestationCertChain
import at.asitplus.testballoon.invoke
import at.asitplus.testballoon.minus
import de.infix.testBalloon.framework.core.testSuite
import at.asitplus.testballoon.matrix.*
import io.kotest.matchers.booleans.shouldBeTrue
import io.kotest.matchers.collections.shouldBeIn
import io.kotest.matchers.shouldBe
Expand All @@ -18,7 +16,7 @@ import kotlin.time.Duration
import kotlin.time.ExperimentalTime
import kotlin.time.Instant

val FeatureDemonstration by testSuite {
val FeatureDemonstration by matrixSuite {


val service = Makoto(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ import at.asitplus.attestation.android.AndroidAttestationConfiguration
import at.asitplus.attestation.android.PatchLevel
import at.asitplus.attestation.android.TrustedRoot
import at.asitplus.attestation.data.AttestationCreator
import at.asitplus.testballoon.invoke
import de.infix.testBalloon.framework.core.testSuite
import at.asitplus.testballoon.matrix.*
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeInstanceOf
import kotlin.random.Random
import kotlin.time.Duration
import kotlin.time.ExperimentalTime

val GeneratedAttestationTests by testSuite {
val GeneratedAttestationTests by matrixSuite {

val challenge = "42".encodeToByteArray()
val packageName = "fa.ke.it.till.you.make.it"
Expand Down
16 changes: 7 additions & 9 deletions serverside/makoto/src/test/kotlin/IosAssertionTest.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import at.asitplus.attestation.*
import at.asitplus.testballoon.invoke
import at.asitplus.testballoon.minus
import at.asitplus.testballoon.withData
import de.infix.testBalloon.framework.core.testSuite
import at.asitplus.awesn1.encoding.decodeFromDer
import at.asitplus.awesn1.encoding.encodeToDer
import at.asitplus.testballoon.matrix.*
import io.kotest.assertions.withClue
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeInstanceOf
import kotlinx.serialization.json.Json
import kotlin.time.Instant

val iosAssertionTests by testSuite {
withData(
val iosAssertionTests by matrixSuite {
data("captured iOS data", listOf(
CapturedIosData(
Instant.parse("2026-02-23T18:05:00+01"),
"9CYHJNG644",
Expand Down Expand Up @@ -101,7 +100,7 @@ val iosAssertionTests by testSuite {
)
)
),
) - {
)) - {
val makoto = Makoto(
iosAttestationConfiguration = IosAttestationConfiguration(
IosAttestationConfiguration.AppData(
Expand Down Expand Up @@ -133,7 +132,7 @@ val iosAssertionTests by testSuite {
CanonicalIosAttestation.decodeFromDer(derEncoded).encodeToDer() shouldBe derEncoded
}
"Assertion" - {
withData(it.assertions) { assertion ->
data("assertions", it.assertions) test { assertion ->
iosResult as AttestationResult.IOS.Verified
val asserted = makoto.ios.verifyAssertion(
iosResult.attestation,
Expand Down Expand Up @@ -215,4 +214,3 @@ data class CapturedIosData(
")"
}
}

5 changes: 2 additions & 3 deletions serverside/makoto/src/test/kotlin/JavaInteropTestRunner.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package at.asitplus.attestation

import JavaInteropTest
import at.asitplus.testballoon.invoke
import de.infix.testBalloon.framework.core.testSuite
import at.asitplus.testballoon.matrix.*

val JavaInteropTestRunner by testSuite {
val JavaInteropTestRunner by matrixSuite {

"testDefaults" { JavaInteropTest.testDefaults() }
"testAttestationCallsJavaFriendliness" { JavaInteropTest.testAttestationCallsJavaFriendliness() }
Expand Down
6 changes: 2 additions & 4 deletions serverside/makoto/src/test/kotlin/KeyConversionTests.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import at.asitplus.attestation.decodeBase64ToArray
import at.asitplus.attestation.parseToPublicKey
import at.asitplus.signum.indispensable.toCryptoPublicKey
import at.asitplus.testballoon.invoke
import at.asitplus.testballoon.minus
import de.infix.testBalloon.framework.core.testSuite
import at.asitplus.testballoon.matrix.*
import io.kotest.matchers.shouldBe

val KeyConversionTests by testSuite {
val KeyConversionTests by matrixSuite {

"Given an X509-encoded key" - {
val x509Key =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package at.asitplus.attestation

import at.asitplus.testballoon.invoke
import de.infix.testBalloon.framework.core.testSuite
import at.asitplus.testballoon.matrix.*
import io.kotest.matchers.types.shouldBeInstanceOf

@OptIn(DisabledAttestation::class)
val NoopAttestationServiceTest by testSuite {
val NoopAttestationServiceTest by matrixSuite {

"TestNOOP" {
NoopAttestationService.verifyAttestation(listOf(), byteArrayOf())
Expand Down
20 changes: 9 additions & 11 deletions serverside/makoto/src/test/kotlin/PatchLevelTest.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import at.asitplus.attestation.android.PatchLevel
import at.asitplus.testballoon.minus
import at.asitplus.testballoon.withData
import de.infix.testBalloon.framework.core.testSuite
import at.asitplus.testballoon.matrix.*
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.shouldBe
import java.time.Month
import java.time.YearMonth

private data class YmTestData(val year: Int, val month: Int, val javaMonth: Month)

val PatchLevelTest by testSuite {
val PatchLevelTest by matrixSuite {

"conversion" - {
withData(
data("year-months", listOf(
YmTestData(2025, 1, Month.JANUARY),
YmTestData(2025, 2, Month.FEBRUARY),
YmTestData(2025, 3, Month.MARCH),
Expand All @@ -25,7 +23,7 @@ val PatchLevelTest by testSuite {
YmTestData(2025, 10, Month.OCTOBER),
YmTestData(2025, 11, Month.NOVEMBER),
YmTestData(2025, 12, Month.DECEMBER),
) { (year, month, javaMonth) ->
)) test { (year, month, javaMonth) ->
val patchLevel = PatchLevel(year, month)
val ym = YearMonth.of(patchLevel.year, patchLevel.month)

Expand All @@ -43,25 +41,25 @@ val PatchLevelTest by testSuite {

"parserCheck" - {
"illegal" - {
withData(
data("invalid patch levels", listOf(
0 to "0",
0 to "13",
0 to "99",
0 to "100",
) { (y, m) ->
)) test { (y, m) ->
val singleInt = "$y$m".toInt()
shouldThrow<IllegalArgumentException> { PatchLevel.fromSingleInt(singleInt) }
}
}

"legal" - {
withData(
data("valid patch levels", listOf(
0 to "1",
-0 to "1", /*same as above*/
-1 to "12",
-999 to "01",
9999999 to "01",
) { (y, m) ->
)) test { (y, m) ->
val singleInt = "$y$m".toInt()
val pl = PatchLevel.fromSingleInt(singleInt)
pl.year shouldBe y
Expand All @@ -71,4 +69,4 @@ val PatchLevelTest by testSuite {


}
}
}
Loading
Loading