I'm trying to implement passkeys in my app. I successfully get to the dialog in iOS simulator to register with a Passkey and I can also read the result and see all the right things in credentialRegistration.rawClientDataJSON. The one thing that's not working is when decoding the rawAttestationObject (which should be CBOR as I understand), I find all data defined in the spec (aaguid, credentialIdLength, credentialId) except for the credentialPublicKey! The rawAttestationObject basically ends after the credentialId. I see this both when decoding the rawAttestationObject manually as well as when using WebAuthn libraries on the server, which will give me an "Unexpected end of CBOR data" error.
Any ideas why the rawAttestationObject does not contain the public key?
For reference, here is the initialization of the Passkey request:
let publicKeyCredentialProvider = ASAuthorizationPlatformPublicKeyCredentialProvider(relyingPartyIdentifier: options.domain)
let registrationRequest = publicKeyCredentialProvider.createCredentialRegistrationRequest(challenge: challenge, name: name, userID: userID)
let authController = ASAuthorizationController(authorizationRequests: [ registrationRequest ])
authController.performRequests()
And here is how I handle the result:
case let credentialRegistration as ASAuthorizationPlatformPublicKeyCredentialRegistration:
let rawAttestationObject = credentialRegistration.rawAttestationObject!.base64EncodedString()
let credentialID = credentialRegistration.credentialID.base64EncodedString()
let rawClientDataJSON = credentialRegistration.rawClientDataJSON.base64EncodedString()
let response: PasskeysResponse = [
"attestationObject": rawAttestationObject,
"credentialId": credentialID,
"clientDataJson": rawClientDataJSON,
]
Here is an example for a decoded attestation object:
{
"rpIdHash": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViYmW4=",
"flags": {
"userPresent": false,
"userVerified": false,
"backupEligibility": true,
"backupState": true,
"attestedCredentialData": true,
"extensionData": false
},
"signCount": 425116148,
"aaguid": "20318e2d-77fa-f54d-bed7-ba15ccd3fade",
"credentialId": "1B1KJf6uYF0AAAAAAAAAAAAAAAAAAAAAAAAAAAAUQW65BAqkeKqu97vbc0Se5R1F3Y+lAQIDJiABIVggtdSX2ZAHsBxU4ja1xP6hCZGUXgUCb6Ipau3stU8rrz4iWCBwhOBWOgwT4yKRnU1hA11thC8+CvjmrCkfq//648cwHg==",
"credentialPublicKey": ""
}
As you can see, it looks all good except for the "credentialPublicKey": "" part.
You appear to be attempting to decode rawAttestationObject
as an authenticator data
rather than as just an attestation object
. The authenticator data
is a different data structure that (sometimes) contains an attestation object
as one of its fields. All the data you need is in your above response, just decoded as the wrong type :)
For reference, the COSE-encoded public key from the object you shared (when correctly decoded) is:
{
1: 2, # Key type. `2` is EC2, or "elliptic curve key with x and y coordinates"
3: -7, # Key algorithm. `-7` is ES256.
-1: 1, # Elliptic curve. `1` is P-256.
-2: h'B5D497D99007B01C54E236B5C4FEA10991945E05026FA2296AEDECB54F2BAF3E', # x coordinate
-3: h'7084E0563A0C13E322919D4D61035D6D842F3E0AF8E6AC291FABFFFAE3C7301E' # y coordinate
}