Skip to content

Commit

Permalink
reword readme for new attestation format
Browse files Browse the repository at this point in the history
  • Loading branch information
JesusMcCloud committed Oct 3, 2024
1 parent ed352d8 commit 93395bb
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 22 deletions.
22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,17 +102,27 @@ authentic, uncompromised app on a non-jailbroken device was used to generate thi
#### Supreme Attestation Format (Supported Since Version 2.2.0)
Following Apple's attestation format makes it clear that no data, but only hashes are ever encoded and signed.
Hence, it allows for a lot of flexibility when it comes to the data to be hashed.
The new supreme attestation format exploits this and does not only pass the hash over a challenge to the AppAttest
service, but instead constructs a structured (JSON) client data object, inspired by WebAuthn and passes tha hash of this
data to `DCAppAttest`.
As such, clients need to create a Secure-Enclave-protected key pair before calling `DCAppattest` and construct the structured
The new _Supreme_ attestation format exploits this and does not only pass the hash over a challenge to the AppAttest
service, but instead constructs a structured (JSON) client data object, inspired by WebAuthn and passes tha hash of this data to DCAppAttest. This means that:

1. A `ClientData` object is created based on the challenge and the public key to attest.
2. The ClientData is serialized to JSON, and the `ByteArray`-representation of this JSON string are hashed using SHA-256:
* `val clientDataJSON = Json.encodeToString(clientData).encodeToByteArray()`
* `val clientDataHash = Digest.SHA256.digest(clientDataJSON).toNSData()`
3. This hash is then passed to DCAppAttest.
If your mobile clients are using the Supreme KMP crypto provider, this is procedure already implemented, and you don't have to worry about it.
4. The `IosHomebrewAttestation` provided by Signum's _Indispensable_ module lets you access both the raw bytes of this client data
as well as the original `ClientData` object, so you can easily verify both the hash of this data and its contents.

For this whole routine to work, clients need to create a Secure-Enclave-protected key pair before calling `DCAppattest` and construct the structured
client data, containing the public part of this key pair and the server challenge.
The client data format is defined in the _Signum's
[Indispensable](https://a-sit-plus.github.io/signum/dokka/indispensable/at.asitplus.signum.indispensable/-ios-homebrew-attestation/-client-data/index.html)_
module.
module, as is the [IosHomebrewAttestation](https://a-sit-plus.github.io/signum/dokka/indispensable/at.asitplus.signum.indispensable/-ios-homebrew-attestation/index.html) containing it.

This library abstracts away all the nitty-gritty details of this verification process and provides a unified API
which works with both Android and iOS.
which works with both Android and iOS. (The [AndroidKeyStoreAttestation](https://a-sit-plus.github.io/signum/dokka/indispensable/at.asitplus.signum.indispensable/-android-keystore-attestation/index.html) contains simply the certificate chain attached to an attested key.)
The test resources contain examples of [Android](https://github.com/a-sit-plus/warden/tree/main/warden/src/test/resources/aksattest.json) and [iOS](https://github.com/a-sit-plus/warden/tree/main/warden/src/test/resources/ios-appattest.json) attestation proofs.

## Usage
Written in Kotlin, plays nicely with Java (cf. `@JvmOverloads`), published at maven central.
Expand Down
25 changes: 9 additions & 16 deletions warden/src/test/kotlin/WardenTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1082,16 +1082,17 @@ class WardenTest : FreeSpec() {
val android = "CAC4307080875C418BEB668E825649DC" to
Json.decodeFromStream<Attestation>(this::class.java.classLoader.getResourceAsStream("aksattest.json"))

val androidSigDigests = listOf(
"941A4513A3027563D3A6EA48EEE85BA45EB9F69CEEA19EF0EBB17F100BFC8878".hexToByteArray(
HexFormat.UpperCase
)
)
withData(ios, android) {
Warden(
AndroidAttestationConfiguration.Builder(
AndroidAttestationConfiguration.AppData(
"at.asitplus.cryptotest.androidApp",
listOf(
"941A4513A3027563D3A6EA48EEE85BA45EB9F69CEEA19EF0EBB17F100BFC8878".hexToByteArray(
HexFormat.UpperCase
)
)
androidSigDigests
)
).build(),
IOSAttestationConfiguration(
Expand Down Expand Up @@ -1120,11 +1121,7 @@ class WardenTest : FreeSpec() {
AndroidAttestationConfiguration.Builder(
AndroidAttestationConfiguration.AppData(
"borked",
listOf(
"941A4513A3027563D3A6EA48EEE85BA45EB9F69CEEA19EF0EBB17F100BFC8878".hexToByteArray(
HexFormat.UpperCase
)
)
androidSigDigests
)
).build(),
IOSAttestationConfiguration(
Expand All @@ -1147,7 +1144,7 @@ class WardenTest : FreeSpec() {
AndroidAttestationConfiguration.Builder(
AndroidAttestationConfiguration.AppData(
"at.asitplus.cryptotest.androidApp",
listOf(
listOf( //wrong digest
"491A4513A3027563D3A6EA48EEE85BA45EB9F69CEEA19EF0EBB17F100BFC8878".hexToByteArray(
HexFormat.UpperCase
)
Expand All @@ -1174,11 +1171,7 @@ class WardenTest : FreeSpec() {
AndroidAttestationConfiguration.Builder(
AndroidAttestationConfiguration.AppData(
"at.asitplus.cryptotest.androidApp",
listOf(
"941A4513A3027563D3A6EA48EEE85BA45EB9F69CEEA19EF0EBB17F100BFC8878".hexToByteArray(
HexFormat.UpperCase
)
)
androidSigDigests
)
).build(),
IOSAttestationConfiguration(
Expand Down

0 comments on commit 93395bb

Please sign in to comment.