I am trying to implement a login with passkeys in a SwiftUI app, based on the sample code provided by Apple (food truck app). On the server side, I am using node.js with @simplewebauthn package. As of now, the web version of the signin/ login process works fine. But when I create a passkey from the swiftUI app and send the registration response back to the server the rpIdHash doesn't match the expected rpIdHash.
At the end of the registration process in swift, in handleAuthorizationResult(_ authorizationResult: ASAuthorizationResult, username: String? = nil), I receive an ASAuthorizationResult, which leads to a .passkeyRegistration case (when registering a new passkey). I then recreate a credential object that has the following structure :
{
id: passkeyRegistration.credentialID,
rawId: passkeyRegistration.credentialID,
type: “public-key“,
authenticatorAttachment: “platform“,
response: {
clientDataJSON: passkeyRegistration.rawClientDataJSON,
attestationObject: passkeyRegistration.rawAttestationObject,
transports: ["internal","hybrid"]
}
}
Which I manage to decode properly on the server-side, but the rpIdHash (203,189,6,160,8,217,220,110,195,214,214,218,140,243,77,214,79,53,154,17,152,74,224,35,148,70,128,25,42,14,122,101) of the attestation object from Apple doesn’t match the expected rpIdHash (203,189,6,160,8,217,220,110,195,214,214,218,140,243,77,214,79,53,154,17,152,74,239,227,148,70,190,25,42,14,122,101). But what is weird is that they only differ from three “keys“ :
- Expected "22":239, "23":227, "26":190
- Recieved "22":224, "23":35, "26":128
So I tried to sample some different rpIdHash to find out where this error might come from but I couldn’t get any hash that would match the one from Apple’s ASAuthorizationResult.
I have no idea how to fix this as both hashing process do not depend on me, so I would really appreciate any help.
PS: For reference, here is the hashing function used by @simplewebauthn:
/**
* Returns hash digest of the given data, using the given algorithm when provided. Defaults to using
* SHA-256.
*/
export function toHash(
data: Uint8Array | string,
algorithm: COSEALG = -7,
): Promise<Uint8Array> {
if (typeof data === 'string') {
data = isoUint8Array.fromUTF8String(data);
}
const digest = isoCrypto.digest(data, algorithm);
return digest;
}
Ok I think I have found where does the problem comes from. In the .passkeyRegistration case, I tried to get the attestationObject as follows:
Which when I then passed into the server directly :
And this then gave me the right expected RPid hash!
So I suppose the issue comes from casting the rawAttestationObject in the following swift struct:
Which I originally did like that:
Hence I then tried to cast it as follows:
But it still didn't work. My guess would be that the issue come from the Data() type, but I don't know what to replace it with to fix this. So far, I have tried the following:
but none of them worked (
bytestStr
anddataStr
were set tonil
andNSString
doesn't conform toEncodable
).So if someone has an idea on how to fix this I would be very gratefull.
UPDATE: Ok I'm actually an idiot, from the beginning in was a Base64 encoded String when it needed to be Base64URL. Didn't know it could make such a difference. At least I got to understand a bit more of what's happening under the hood during the passkey registration process.
So if anyone is wondering or happens to have the same problem, the final 2/3 lines I added are:
or just:
Anyway, thank you @agl for the help and hope this post can help if someone runs into the same issue.