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)
}
}
}