Hi!
I'm generating assertions using DCAppAttestService.shared.generateAssertion. It's running for almost one and a half years and has the following issue. Approx 6% of our users trying to generate assertions have issues and according to our analytics about half of them have invalid_input error during assertion process). I've managed to reproduce this issue on test device and noticed some weird scenario:
(first app run)
- The AppAttest key generated and attested at Apple side successfully. Key Identifier persisted.
- Attestation object verification on backend and public key extraction is ok
- Unlimited number of assertion can be generated this time
(second app run)
- Key Identifier persisted on previous app run is read and passed to DCAppAttestService.shared.generateAssertion
- Invalid input error received.
- Regeneration of key and attestation works fine.
So looks like there is a kinda state in assertion process - it works well after key generation on first run, but fails with invalid_input on second run.
As invalid_input error cannot say much about the issue, I've swizzled some methods of DCAppAttestService (https://developer.limneos.net/index.php?ios=15.2.1&framework=DeviceCheck.framework&header=DCAppAttestService.h) - _rewrapAsDCError, _loadAppUUID, _saveAppUUID. Swizzling implementation attached (swizzling.swift). As the swizzling logs show - when invalid_input raises, a strange error is printed (Error Domain=com.apple.appattest.error Code=-2 "Invalid appUUID" UserInfo={NSLocalizedDescription=Invalid appUUID}).
What can be the issue? In another app this behaviour isn't reproducible but they share similar dependency with App Attest - wrapping logic I've filed bug report no FB12205670. Thanks.
Logs:
ok case:
key generation:
swizzleLoadAppID
swizzleSaveAppID EDA165DC-0781-4891-A16D-0979FC4FEB84
swizzleRewrap
key attestation: (key_id: "XzTjW3V7944\/ljQ2C8LTpqug0t0gslVyhdWGUCnJXfY=")
swizzleLoadAppID EDA165DC-0781-4891-A16D-0979FC4FEB84
swizzleRewrap
key assertion: (input key_id = "XzTjW3V7944\/ljQ2C8LTpqug0t0gslVyhdWGUCnJXfY=" clientDataHash = "zUwl\/jiunewwd1ofhEOmgNGWM+oD7LmUGe6Te5Iv9pc=")
swizzleLoadAppID EDA165DC-0781-4891-A16D-0979FC4FEB84
swizzleRewrap
issue case (DCError.invalid_input):
key assertion: (input key_id = "XzTjW3V7944\/ljQ2C8LTpqug0t0gslVyhdWGUCnJXfY=" clientDataHash = "F8o5i+8PsZ5cTuyjlZoMe+kcbTG0\/R8Vw6tmjPlzlLc=")
swizzleLoadAppID
swizzleRewrap Error Domain=com.apple.appattest.error Code=-2 "Invalid appUUID" UserInfo={NSLocalizedDescription=Invalid appUUID}
Swizzling logic:
@objc func swizzleRewrap(obj: NSObject) -> NSObject {
let returnValue = swizzleRewrap(obj: obj)
print("swizzleRewrap \(obj)")
return returnValue
}
@objc func swizzleLoadAppID() -> NSObject {
let returnValue = swizzleLoadAppID()
print("swizzleLoadAppID \(returnValue.debugDescription)")
return returnValue
}
@objc func swizzleSaveAppID(app_id: NSObject) {
swizzleSaveAppID(app_id: app_id)
print("swizzleSaveAppID \(app_id)")
}
static func makeSwizzling() {
let sel = NSSelectorFromString("_rewrapAsDCError:")
DCAppAttestService.swizzleInstanceMethod(sel, #selector(DCAppAttestService.swizzleRewrap(obj:)))
let sel1 = NSSelectorFromString("_loadAppUUID")
DCAppAttestService.swizzleInstanceMethod(sel1, #selector(DCAppAttestService.swizzleLoadAppID))
let sel2 = NSSelectorFromString("_saveAppUUID:")
DCAppAttestService.swizzleInstanceMethod(sel2, #selector(DCAppAttestService.swizzleSaveAppID(app_id:)))
}
}
public extension NSObjectProtocol {
static func swizzleInstanceMethod(_ origin: Selector, _ replace: Selector) {
let origin = class_getInstanceMethod(self, origin)
let replace = class_getInstanceMethod(self, replace)
if let origin = origin, let replace = replace {
method_exchangeImplementations(origin, replace)
}
}
}