Quirks, Bugs, Workarounds, and Hints
Warden Supreme's unified Android and iOS attestation core, Makoto (formerly WARDEN), has been used in production for years and attested millions of clients. Naturally, this caused hiccups and also helped identify their causes. Due to the diversity of its device landscape, Android is most affected. iOS, however, is also not without flaws.
This page lists known quirks and bugs and discusses how to deal with them. It starts with general guidance that applies across platforms.
General Hints
Using attestation to strongly enforce policies and to remotely establish trust in mobile clients is rooted in cryptographic mechanisms and PKI procedures. Hence, timeliness is of the essence; freshness windows and temporal checks are crucial. As a logical consequence, the clocks between a service and the clients being attested need to be in sync.
Given that the service owner is not the device owner, clock drift and even time-zone differences of hours offset are not uncommon. Warden Supreme allows for sending server time zone (and even clock drift information) to clients along with a cryptographic nonce at the start of an attestation procedure. However, cryptographic operations are performed in hardware and are thus not controlled by the application that receives the attestation challenge. A time zone offset or clock drift can result in a certificate chain carrying attestation statements that fail temporal checks (see below).
Clock Drifts and Temporal Validity
Time is relative (literally)!
Three components require a time source (irrespective of Warden Supreme's implementation):
- iOS attestation verification
- Android attestation verification
- The component ensuring freshness, guarding both of the above
Of course, this still leaves two entities with system clocks that are isolated from each other:
- The back-end, verifying attestation proofs (CSRs)
- Mobile clients, issuing those proofs to begin with
This complexity is inherent and nothing can be done to simplify this situation on a conceptual level, but
Warden Supreme provides sane defaults that have proven to work well in practice.
This means that for 99% of all deployments, this complexity is hidden away, but if you need to change the defaults
you will need to get your hands dirty and entertain thoughts about this mess!
Warden Supreme's verifier allows for setting a global verification clock offset through the parameter verificationTimeOffset.
- This defaults to five minutes, because even one millisecond of clock drift can cause otherwise perfectly valid attestations to error out!
- Those five minutes are added to Apple's recommended default lifetime of an iOS attestation, effectively increasing the maximum age of an attestation that is still considered valid by five minutes.
The Two Sources of Attestation Creation Time
(Yes, things get even more complex!)
iOS and Android attestation statements come with two kinds of temporal validity:
- The (leaf) certificates
notBeforeandnotAftervalidity period - An attestation creation time, encoded into the attestation data (this is true for iOS and Android)
These values need to be temporally valid for an attestation to verify in addition to nonce validity!
iOS's attestation implementation is mostly sane, with proper certificate validity and an always present attestation creation time. The diversity of Android implementations, however, leads to a form of anarchy that undermines some requirements for attestation checks. In fact, many Android devices mess up a correctly encoded certificate validity, or the attestation statement validity, or both.
Luckily, this can be worked around if and only if challenges issued by the back-end expire after a couple of minutes and are rooted in a truly random value only used once, that is invalidated once used.
Warden Supreme Default Behaviour
Warden Supreme also ships with a default nonce generation service and a challenge validation component that follows this strategy. Hence, Warden Supreme behaves as follows by default:
- Adding a five-minute verification time offset
- Using the recommended default validity of iOS attestation statements plus that five-minute offset
- Generating truly random nonces that expire after this very same iOS validity
- Completely disabling the validity checks on the leaf certificate and the encoded attestation statement validity period on Android.
The Warden Supreme defaults do not have any adverse impact on security that matters in practice because Warden Supreme checks the validity of challenges before an attestation proof is even parsed. In addition, Warden Supreme communicates nonce validity periods to clients. The validity period encoded into challenges is shifted by the inverse verification time offset, because clients see the drift in the opposite direction. Communicating this information to clients has the inherent benefit that large clock drifts can be caught right away and communicated to the user.
Changing Defaults
It is perfectly possible to tweak this behaviour, if desired, but do keep all the above complexity in mind and do not turn this into a footgun by making changes lightly!
Android
Fundamental Requirements
Only Google Play certified devices (i.e. those bearing the official Android branding) support remote attestation. Huawei devices, for example, or Chinese import devices that do not come with Google Play services out-of-the-box cannot be attested!
Even though the previous section dealt with a crucial Android-specific issue, there's (sadly) more, and Android bugs fall into three categories:
- Encoding flaws affecting the byte representation of attestation information
- OS bugs and vendor quirks affecting the behaviour of devices
- Non-obvious, but deliberate design decisions
Encoding Flaws
Creation Time Issues
Some (especially older) Android devices do not encode an attestation creation time, and always encode zero seconds since
the epoch into the leaf certificate's notBefore and notAfter. This is partially by design, but some devices
continue to do this, even though they should very much not.
ASN.1 Time Bugs
Some vendors encode UTC Time vs. GeneralizedTime incorrectly leading to years of temporal offset. Only the vendor can fix this through updates. However, relying on a tight freshness window based on a cryptographic nonce sourced from true randomness is recommended anyway (see Clock Drifts and Temporal Validity).
Vendor Patch Level Misencoding
Many Android 15 devices (even emulator images) and some Samsung devices do not conform to the ASN.1 schema for attestation data with respect to patch level encoding. This concerns the vendor patch level field, not the OS patch level, and requires monkey-patching Google's upstream parser code to prevent it from glitching out. Warden Supreme already applies the necessary band-aids, but enforcing vendor patch levels is generally discouraged in favour of OS patch levels.
Some devices also truncate vendorPatchLevel and/or bootPatchLevel by omitting the day-of-month.
Instead of the expected yyyyMMdd form (e.g. 20181230), they encode only yyyyMM (e.g. 201812).
This does not conform to the attestation extension schema and needs to be handled leniently (e.g. by treating a missing day as unknown/default and avoiding strict enforcement of these fields).
Broken RSA PKCS#1 AlgorithmIdentifier (Missing ASN.1 NULL)
Some Android devices generate a non-conforming X.509 leaf certificate at attestation time. In other words: when the app requests key attestation, the device creates a leaf certificate that carries the attestation proof and signs it using a key in the TEE — but the resulting certificate is not fully X.509/DER-conforming.
Concretely, for RSA PKCS#1 v1.5 signature algorithms, the X.509 ASN.1 profile requires AlgorithmIdentifier.parameters to be present and encoded as ASN.1 NULL (05 00).
On affected devices, the certificate’s signature algorithm is encoded as an AlgorithmIdentifier that contains only the OID and omits the required NULL parameters.
A common case is sha256WithRSAEncryption with OID 1.2.840.113549.1.1.11, encoded as:
- illegal (missing NULL):
30 0b 06 09 2a 86 48 86 f7 0d 01 01 0b - required by X.509 profile:
30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00
Many parsers accept this in practice, but strict implementations may reject such certificates, or fail when comparing signature algorithm identifiers byte-for-byte. Hence, certificate parsing needs to be relaxed for Android attestation proofs.
Misleading Assumptions about ECDH
Virtually every Android device supports hardware-backed EC crypto and EC Diffie-Hellman key agreement. However, this does not entail that it supports a combination of the two. With respect to EC keys, only ECDSA signatures on NIST curves are guaranteed to be performed in hardware, assuming a cryptographic hardware module. This means that without explicitly specifying KeyProperties.SECURITY_LEVEL_TRUSTED_ENVIRONMENT during key generation, even generating an EC key pair for a curve supported in hardware may lead to a key being actually generated in software, as soon as KeyProperties.PURPOSE_AGREE_KEY is also set.
OS Bugs and Quirks
Bootloader Unlock Destroying Keys
Many devices destroy keys or make attestation impossible after a bootloader unlock. There is nothing to be done about this, and even relocking typically cannot restore cryptographic keys. Hence, this issue manifests itself on the client device. While technically not a violation of the Android certification requirements, it very much is bad practice at the vendor's end. This is especially hard to swallow for device owners, since buying a new device is the only thing that can be done about this. Known affected devices:
- Fairphone 2
- Nothing Phone 3a
- There are likely others
Keystore2 Binder Bug
On rare occasions, attestation fails because the connection between the Keystore and the package manager breaks up. While the bug has been identified and fixed, it will only land in Android 17 (see commit b0be7edbf9e3…).
This bug manifests itself by listing UnknownApplication instead of a proper application package in the attestation statement. Rebooting the device helps.
Deliberate Design Decisions
Revocation
The certificate chains created by Android use neither CRL nor OCSP, but a custom scheme. Hence, back-end services must be able to reach Google's servers hosting the revocation information. Warden Supreme allows for specifying an HTTP proxy URL, to facilitate setups behind a proxy.
Temporally Invalid Leaf Certificates
As mentioned in Clock Drifts and Temporal Validity, many older Android devices do not encode a sensible validity into the leaf certificate carrying attestation information. This was a deliberate choice by Google, that has since been reversed. Some vendors still adhere to this practice, though.
Remote Provisioning
Newer Android devices support remote key provisioning and even require key rollover. Hence, offline devices can exhaust key pools, causing transient attestation failures. Taking devices online fixes this issue.
The issue manifests itself on the client device as r#ERROR_PENDING_INTERNET_CONNECTIVITY 2: Error::Rc(r#OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY)) (public error code: 16 internal Keystore code: 24)
when trying to create an attestation statement.
PKIX Certificate Path Quirks
Especially older Android versions deliberately botched the certificate path leading from the leaf certificate to a Google root certificate. This prevents attestation certificate chains from being used for TLS certificates (which you should not do, anyway). Warden Supreme includes a manual check and Google's custom PKIX certificate path validator introduced with the new upstream attestation library. If you need a certificate chain that works for TLS, issue your own for an attested key (see Integration Guide).
iOS
Online Requirement and Rate Limiting
iOS requires an internet connection on the mobile device to issue attestations, as it needs to talk to an Apple service. This service is subject to rate limiting (see Preparing to Use App Attest). Keep this in mind!
Non-Compliant ASN.1 SET OF
The custom certificate extension carrying some attestation information uses SET OF for some parameters. Apple failed to observe the constraints DER-encoded ASN.1 data must fulfil
and did not sort the members of this set as required.
Warden Supreme relies on a lenient ASN.1 parser that does not get tripped by this. If you are processing iOS attestation using other stacks, this could cause issues, though.