Skip to content

Commit

Permalink
address PR feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
JesusMcCloud committed Nov 5, 2024
1 parent 9bc77dc commit 7223ec4
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 12 deletions.
2 changes: 2 additions & 0 deletions warden-roboto/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import at.asitplus.gradle.bouncycastle
import at.asitplus.gradle.datetime
import at.asitplus.gradle.ktor
import org.gradle.kotlin.dsl.support.listFilesOrdered
import org.jetbrains.kotlin.gradle.targets.js.testing.karma.processKarmaStackTrace
Expand Down Expand Up @@ -59,6 +60,7 @@ dependencies {
testImplementation("ch.qos.logback:logback-classic:1.2.3")
testImplementation("ch.qos.logback:logback-access:1.2.3")
testImplementation(ktor("client-mock"))
testImplementation(datetime())
}


Expand Down
10 changes: 4 additions & 6 deletions warden-roboto/src/main/kotlin/AndroidAttestationChecker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import java.security.cert.CertificateExpiredException
import java.security.cert.CertificateNotYetValidException
import java.security.cert.X509Certificate
import java.time.Duration
import java.time.Instant
import java.time.YearMonth
import java.util.*
import kotlin.jvm.optionals.getOrNull
Expand Down Expand Up @@ -114,17 +115,14 @@ abstract class AndroidAttestationChecker(

protected abstract val trustAnchors: Collection<PublicKey>

protected open fun ParsedAttestationRecord.verifyAttestationTime(verificationDate: Date = Date()) {
protected open fun ParsedAttestationRecord.verifyAttestationTime(verificationDate: Instant) {
val createdAt =
teeEnforced().creationDateTime().getOrNull() ?: softwareEnforced().creationDateTime().getOrNull()
if (createdAt == null) throw AttestationValueException(
"Attestation statement creation time missing",
reason = AttestationValueException.Reason.TIME
)
val calendar = Calendar.getInstance()
calendar.time = verificationDate
calendar.add(Calendar.SECOND, attestationConfiguration.verificationSecondsOffset)
var checkTime = calendar.toInstant()
var checkTime = verificationDate.plusSeconds(attestationConfiguration.verificationSecondsOffset.toLong())
val difference = Duration.between(createdAt, checkTime)
if (difference.isNegative) throw AttestationValueException(
"Attestation statement creation time too far in the future: $createdAt, check time: $checkTime",
Expand Down Expand Up @@ -287,7 +285,7 @@ abstract class AndroidAttestationChecker(
"verification of attestation challenge failed",
reason = AttestationValueException.Reason.CHALLENGE
)
parsedAttestationRecord.verifyAttestationTime(verificationDate)
parsedAttestationRecord.verifyAttestationTime(verificationDate.toInstant())
parsedAttestationRecord.verifySecurityLevel()
parsedAttestationRecord.verifyBootStateAndSystemImage()
parsedAttestationRecord.verifyRollbackResistance()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import io.ktor.client.plugins.cache.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.serialization.kotlinx.json.*
import java.time.Instant
import java.util.*

class NougatHybridAttestationChecker @JvmOverloads constructor(
Expand Down Expand Up @@ -58,7 +59,7 @@ class NougatHybridAttestationChecker @JvmOverloads constructor(
@Throws(AttestationValueException::class)
override fun ParsedAttestationRecord.verifyRollbackResistance() = teeEnforced().verifyRollbackResistance()

override fun ParsedAttestationRecord.verifyAttestationTime(verificationDate: Date) {
override fun ParsedAttestationRecord.verifyAttestationTime(verificationDate: Instant) {
//impossible
}
}
14 changes: 9 additions & 5 deletions warden-roboto/src/test/kotlin/AttestationTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import io.kotest.matchers.types.shouldBeInstanceOf
import io.ktor.util.*
import org.bouncycastle.util.encoders.Base64
import java.sql.Date
import java.time.Duration
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes


@OptIn(ExperimentalStdlibApi::class)
Expand Down Expand Up @@ -515,7 +516,7 @@ class AttestationTests : FreeSpec() {
recordedAttestation.attestationCertChain,
Date.from(
recordedAttestation.verificationDate.toInstant()
.minus(Duration.ofDays(30000))
.minus(java.time.Duration.ofDays(30000))
),
recordedAttestation.challenge
)
Expand All @@ -528,7 +529,7 @@ class AttestationTests : FreeSpec() {
recordedAttestation.attestationCertChain,
Date.from(
recordedAttestation.verificationDate.toInstant()
.plus(Duration.ofDays(30000))
.plus(java.time.Duration.ofDays(30000))
),
recordedAttestation.challenge
)
Expand Down Expand Up @@ -632,7 +633,8 @@ fun attestationService(
androidPatchLevel: PatchLevel? = PatchLevel(2021, 8),
requireStrongBox: Boolean = false,
unlockedBootloaderAllowed: Boolean = false,
requireRollbackResistance: Boolean = false
requireRollbackResistance: Boolean = false,
attestationStatementValiditiy: Duration = 5.minutes
) = HardwareAttestationChecker(
AndroidAttestationConfiguration(
listOf(
Expand All @@ -646,7 +648,9 @@ fun attestationService(
patchLevel = androidPatchLevel,
requireStrongBox = requireStrongBox,
allowBootloaderUnlock = unlockedBootloaderAllowed,
requireRollbackResistance = requireRollbackResistance
requireRollbackResistance = requireRollbackResistance,
attestationStatementValiditySeconds = attestationStatementValiditiy.inWholeSeconds.toInt()

)
)

Expand Down
131 changes: 131 additions & 0 deletions warden-roboto/src/test/kotlin/TemporalOffsetTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package at.asitplus.attestation.android

import at.asitplus.attestation.android.exceptions.AndroidAttestationException
import at.asitplus.attestation.android.exceptions.AttestationValueException
import at.asitplus.attestation.data.AttestationData
import at.asitplus.attestation.data.attestationCertChain
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FreeSpec
import io.kotest.datatest.withData
import io.kotest.matchers.string.shouldContain
import kotlinx.datetime.toJavaInstant
import kotlinx.datetime.toKotlinInstant
import java.util.*
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds

class TemporalOffsetTest : FreeSpec() {

private val exactStartOfValidity: Map<String, AttestationData> = mapOf(
"KeyMint 200" to AttestationData(
"Pixel 6",
challengeB64 = "9w11c/H1kgfx+2Lqrqscug==",
attestationProofB64 = listOf(
"""
MIICpzCCAk6gAwIBAgIBATAKBggqhkjOPQQDAjA5MQwwCgYDVQQKEwNURUUxKTAnBgNVBAMTIGQ3MWRmYjM1NjNlNWQ5Y2I0NmRkMTJjMWJhMjI2YzM5MB4XDTIzMDQxNDE0MzAyMVoXDTQ4M
DEwMTAwMDAwMFowJTEjMCEGA1UEAxMaaHR0cDovLzE5Mi4xNjguMTc4LjMzOjgwODAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASqzk1wE4o3jS27/n40sW8ZExFxgXopGSxihSaLCUqGHN
sZoAvMTY96sQznDM0p4LoRKu5klGgE+4efkP4d+gyQo4IBWTCCAVUwDgYDVR0PAQH/BAQDAgeAMIIBQQYKKwYBBAHWeQIBEQSCATEwggEtAgIAyAoBAQICAMgKAQEEEPcNdXPx9ZIH8fti6q6
rHLoEADBfv4U9CAIGAYeALKLxv4VFTwRNMEsxJTAjBB5hdC5hc2l0cGx1cy5hdHRlc3RhdGlvbl9jbGllbnQCAQExIgQgNLl2LE1skNSEMZQMV73nMUJYsmQg7+Fqx/cnTw0zCtUwgaehCDEG
AgECAgEDogMCAQOjBAICAQClCDEGAgECAgEEqgMCAQG/g3cCBQC/hT4DAgEAv4VATDBKBCAPbnXIAYO13sB0sAVNQnHpk4nr5LE2sIGd4fFQug/51wEB/woBAAQgNidLYFH3o3y3ufJGD1UzB
8M0ZzGpxDl7RrvUI0SJSwi/hUEFAgMB+9C/hUIFAgMDFj+/hU4GAgQBNLChv4VPBgIEATSwoTAKBggqhkjOPQQDAgNHADBEAiAYJTfwNDCSiw/fob8VIBSNnXfaQaoyLxVmbaP/U5e2AgIgAl
ngbOcR1syv1RP369hnI8cMh4xe1AFnB+H3Y9OVirQ=
""",
"""
MIIBwzCCAWqgAwIBAgIRANcd+zVj5dnLRt0SwboibDkwCgYIKoZIzj0EAwIwKTETMBEGA1UEChMKR29vZ2xlIExMQzESMBAGA1UEAxMJRHJvaWQgQ0EzMB4XDTIzMDMyNjExNDk0OVoXDTIzM
DUwMTExNDk0OVowOTEMMAoGA1UEChMDVEVFMSkwJwYDVQQDEyBkNzFkZmIzNTYzZTVkOWNiNDZkZDEyYzFiYTIyNmMzOTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJS3ylJ9AibrkDPP/W
4PBHmHU/e+yRiSTr4nLkojZzkBDWayhRI6PhrsN8Cetsp2EG2r2dQ60VnPvtvw9ElYYlGjYzBhMB0GA1UdDgQWBBQRvZZGVqzjrxcT1lU/u8OGt6xJSjAfBgNVHSMEGDAWgBTEfQBQs7lkcRy
V+Ok7Vmuti/ra9zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDAKBggqhkjOPQQDAgNHADBEAiAjV7E60YcWRMdplr3lyh/M6nSHuADoGWdO10hP2h/81gIgTRHSnjjwPA3FGlyY
g8DGschrg3a7j8lEzLg2kRmzg9c=
""",
"""
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
DqG8At2JHA==
"""
),
isoDate = "2023-04-14T14:30:22Z", //need to round up millis
pubKeyB64 = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqs5NcBOKN40tu/5+NLFvGRMRcYF6KRksYoUmiwlKhhzbGaALzE2PerEM5wzNKeC6ESruZJRoBPuHn5D+HfoMkA=="
)
)

init {
"Exact Time of Validity" - {
withData(exactStartOfValidity) {
attestationService().verifyAttestation(
it.attestationCertChain,
it.verificationDate,
it.challenge
)
}
}

"Exact Time of Validity + 1D" - {
withData(exactStartOfValidity) {
attestationService(
attestationStatementValiditiy = 1.days + 1.seconds
).verifyAttestation(
it.attestationCertChain,
Date.from((it.verificationDate.toInstant().toKotlinInstant() + 1.days).toJavaInstant()),
it.challenge,
)
}
}

"Exact Time of Validity - 1D" - {
withData(exactStartOfValidity) {
withData(exactStartOfValidity) {
shouldThrow<AttestationValueException> {
attestationService().verifyAttestation(
it.attestationCertChain,
Date.from((it.verificationDate.toInstant().toKotlinInstant() - 1.seconds).toJavaInstant()),
it.challenge,
)
}.message!!.shouldContain("too far in the future")
}


withData(exactStartOfValidity) {
shouldThrow<AttestationValueException> {
attestationService().verifyAttestation(
it.attestationCertChain,
Date.from((it.verificationDate.toInstant().toKotlinInstant() + 10.minutes).toJavaInstant()),
it.challenge,
)
}.message!!.shouldContain("too far in the past")
}

}

}
}
}

0 comments on commit 7223ec4

Please sign in to comment.