Skip to content

Commit

Permalink
simplify attestation method
Browse files Browse the repository at this point in the history
  • Loading branch information
JesusMcCloud committed Aug 22, 2024
1 parent 86a888c commit 3732aeb
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 44 deletions.
80 changes: 38 additions & 42 deletions warden/src/main/kotlin/AttestationService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import ch.veehait.devicecheck.appattest.assertion.Assertion
import ch.veehait.devicecheck.appattest.attestation.ValidatedAttestation
import com.google.android.attestation.AttestationApplicationId
import com.google.android.attestation.ParsedAttestationRecord
import kotlinx.datetime.Clock
import net.swiftzer.semver.SemVer
import org.slf4j.LoggerFactory
import java.security.PublicKey
Expand Down Expand Up @@ -264,54 +263,22 @@ abstract class AttestationService {
expectedChallenge,
keyToBeAttested.encoded
)) {
is AttestationResult.Android -> {
return if (CryptoPublicKey.fromJcaPublicKey(keyToBeAttested) == CryptoPublicKey.fromJcaPublicKey(
firstTry.attestationCertificate.publicKey
)
)
KeyAttestation(keyToBeAttested, firstTry)
else {
("Android attestation failed: keyToBeAttested (${keyToBeAttested.encoded.encodeBase64()}) does not match " +
"key from attestation certificate: ${firstTry.attestationCertificate.publicKey.encoded.encodeBase64()}").let {
KeyAttestation(
null,
AttestationResult.Error(
explanation = it,
cause = AttException.Content.Android(
it,
AttestationValueException(
it,
cause = null,
reason = AttestationValueException.Reason.APP_UNEXPECTED
)
)
)
)
}
}
}
is AttestationResult.Android -> return processAndroidAttestationResult(keyToBeAttested, firstTry)

is AttestationResult.Error -> {
//try different encodings
val publicKeyEncodings = CryptoPublicKey.fromJcaPublicKey(keyToBeAttested).getOrThrow().let {
listOf(
it.iosEncoded,
(it as CryptoPublicKey.EC).copy(
it.publicPoint,
preferCompressedRepresentation = !it.preferCompressedRepresentation
).iosEncoded,
it.encodeToDer()
)
}

// try all different key encodings
// not the most efficient way, but doing it like this won't involve any guesswork at all
publicKeyEncodings.forEach {
keyToBeAttested.transcodeToAllFormats().forEach {
when (val secondTry =
kotlin.runCatching { verifyAttestation(attestationProof, expectedChallenge, it) }
.getOrElse { return KeyAttestation(null, firstTry) }) {
is AttestationResult.Android -> throw RuntimeException("Logical Error attesting key ${keyToBeAttested.encoded.encodeBase64()} for attestation proof ${attestationProof.joinToString { it.encodeBase64() }} with challenge ${expectedChallenge.encodeBase64()} at ${Clock.System.now()}")
is AttestationResult.Error -> {/*try again*/
}

is AttestationResult.Android ->
throw logicalError(keyToBeAttested, attestationProof, expectedChallenge)

is AttestationResult.Error -> {} //try again, IOS could have encoded it differently

//if this works, perfect!
is AttestationResult.IOS -> return KeyAttestation(keyToBeAttested, secondTry)
}
Expand All @@ -324,6 +291,35 @@ abstract class AttestationService {
}
}

private fun <T : PublicKey> processAndroidAttestationResult(
keyToBeAttested: T,
firstTry: AttestationResult.Android
): KeyAttestation<T> =
if (CryptoPublicKey.fromJcaPublicKey(keyToBeAttested) == CryptoPublicKey.fromJcaPublicKey(
firstTry.attestationCertificate.publicKey
)
) KeyAttestation(keyToBeAttested, firstTry)
else {
("Android attestation failed: keyToBeAttested (${keyToBeAttested.encoded.encodeBase64()}) does not match " +
"key from attestation certificate: ${firstTry.attestationCertificate.publicKey.encoded.encodeBase64()}").let {
KeyAttestation(
null,
AttestationResult.Error(
explanation = it,
cause = AttException.Content.Android(
it,
AttestationValueException(
it,
cause = null,
reason = AttestationValueException.Reason.APP_UNEXPECTED
)
)
)
)
}
}


/** Same as [verifyKeyAttestation], but taking an encoded (either ANSI X9.63 or DER) publix key as a byte array
* @see verifyKeyAttestation
*/
Expand Down
12 changes: 12 additions & 0 deletions warden/src/main/kotlin/Extensions.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package at.asitplus.attestation

import at.asitplus.signum.indispensable.CryptoPublicKey
import at.asitplus.signum.indispensable.fromJcaPublicKey
import at.asitplus.signum.indispensable.getJcaPublicKey
import kotlinx.datetime.Clock
import kotlinx.datetime.toJavaInstant
Expand Down Expand Up @@ -41,6 +42,17 @@ data class AttestationObject(
)
}

internal fun PublicKey.transcodeToAllFormats() = CryptoPublicKey.fromJcaPublicKey(this).getOrThrow().let {
listOf(
it.iosEncoded,
(it as CryptoPublicKey.EC).copy(
it.publicPoint,
preferCompressedRepresentation = !it.preferCompressedRepresentation
).iosEncoded,
it.encodeToDer()
)
}

internal fun String.decodeBase64ToArray() = Base64.decode(this)

internal fun ByteArray.encodeBase64() = Base64.toBase64String(this)
Expand Down
15 changes: 13 additions & 2 deletions warden/src/main/kotlin/Throwables.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import at.asitplus.attestation.AttestationException.Certificate
import at.asitplus.attestation.AttestationException.Content
import at.asitplus.attestation.android.exceptions.AndroidAttestationException
import at.asitplus.attestation.android.exceptions.AttestationValueException
import kotlinx.datetime.Clock
import java.security.PublicKey


/**
Expand Down Expand Up @@ -174,6 +176,15 @@ class IosAttestationException(msg: String? = null, cause: Throwable? = null, val
*/
APP_UNEXPECTED,


}
}
}

internal fun <T : PublicKey> logicalError(
keyToBeAttested: T,
attestationProof: List<ByteArray>,
expectedChallenge: ByteArray
) = RuntimeException("Logical Error attesting key ${
keyToBeAttested.encoded.encodeBase64()
} for attestation proof ${
attestationProof.joinToString { it.encodeBase64() }
} with challenge ${expectedChallenge.encodeBase64()} at ${Clock.System.now()}")

0 comments on commit 3732aeb

Please sign in to comment.