AppAttest Assertion - signature verification failing

Hello,

I'm implementing AppAttest for one of our apps. Though the Attestation procedure works fine, the assertion signed by the private key on the device (via generateAssertion) can't be verified at the server with the public key saved during the attestation.

Environment
AppAttest entitlement: development
iOS 14
Server: Nodejs

The client (app) and server code specific to assertion is below.

Client side code (app)

Code Block
if let clientData = self.prepareClientData(withChallenge: challenge) {
// generate assertion
let clientDataHash = Data(SHA256.hash(data: clientData))
DCAppAttestService.shared.generateAssertion(keyId!, clientDataHash: clientDataHash) { assertion, error in
guard error == nil else {
print ("ERROR: Assertion not available right now")
return
}
// create assertion request
var urlRequest = URLRequest(url: URL(string: self.assertEP)!)
urlRequest.httpMethod = "POST"
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
let clientDataString = clientData.base64EncodedString()
let assertionString = assertion!.base64EncodedString()
let assertRequest: [String: Any] = ["clientData": clientDataString, "assertion": assertionString]
let jsonData: Data
do {
jsonData = try JSONSerialization.data(withJSONObject: assertRequest, options: [])
urlRequest.httpBody = jsonData
} catch {
print (error)
return
}
// send assertion request to server
let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in
guard error == nil else {
// request sending failed, try again later
print (error!)
return
}
self.extractAssertionResponse(withData: data)
}
task.resume()
}
}


Server side Assertion verification code

Code Block
function verifyAssertion(clientDataBuffer, assertionBuffer) {
try {
let assert = cbor.decodeAllSync(assertionBuffer)[0];
let authData = assert.authenticatorData; // buffer
let signature = assert.signature; // buffer
// compute client Data Hash
let clientDataHash = crypto.createHash('sha256').update(clientDataBuffer).digest('base64');
let clientDataHashBuffer = Buffer.from(clientDataHash, 'base64');
// compute composite hash
let compositeBuffer = Buffer.concat([authData, clientDataHashBuffer]);
let nonce = crypto.createHash('sha256').update(compositeBuffer).digest('base64'); // base64 string
let nonceBuffer = Buffer.from(nonce, 'base64');
// load public key
let keyObj = crypto.createPublicKey(k_publicKeyPem);
// verify signature
let verifier = crypto.createVerify('sha256').update(nonceBuffer);
let sign_verify = verifier.verify(keyObj, signature);
console.log("sign_verify: ", sign_verify);
} catch (e) {
console.log(e);
}
}


The verifier.verify() call always results in 'false'.

I've tried 'hex' and 'base64' variations on the signature, but didn't work.

I've verified that the sha256(publicKey) received at the server is equal to the keyId.

Code Block
let keybuf = Buffer.from(k_publicKeyRaw, 'base64');
let keySHA = crypto.createHash('sha256').update(keybuf).digest('base64');
let keyVerified = (keySHA === k_keyId);


Also verified that the clientDataHash generated at the server is the same as clientDataHash generated within the app.

Does crypto Verify not compatible with the generated signatures? If not, what's the suggested way to do it?

I've tried all the encodings supported by crypto Verify.verify but none of those worked.

Code Block
crypto.getHashes();



Please help.
More Information:

I've included the App Store receipt within clientData (simple receipt with no purchases).

Would that make the clientData too big?
Is there a limit to the size of clientData?

I guess it shouldn't matter.

Which crypto library are you using on node side? Note that Apple's signature comes back ASN.1 Encoded as a Sequence of two integers, and the integers are positive big ints meaning they may have a leading 0x0 byte appended to them. In some frameworks (e.g. .Net) they expect a r|s signature format so you have to extract r and s out of the sequence sent by the device, remove leading 0x0 bytes if present and feed that to your crypto framework.

Hey there vag, did you succeed in resolving this issue?

That's not arbitrary: it's X9.62 format, even though that's not called out anywhere.

See:

American National Standards Institute (ANSI), ANSI X9.62-2005: The Elliptic Curve Digital Signature Algorithm (ECDSA), 2005.

AppAttest Assertion - signature verification failing
 
 
Q