"AttestationObject invalid CBOR" with integrated iOS Passkeys Attestation function
Hi I'm trying to use the lib with the integrated passkey function from Apple under iOS. Every time I try to complete the registration I get a CBOR error from the lib with following response "AttestationObject invalid CBOR (Declared definite length of CBOR data item exceeds available buffer size.)" When I try this in the browser it works.
Is there a solution for this or does this library not work with the iOS passkeys?
While I haven't tried it myself, I don't see any reason why it wouldn't work as is right now. I'm curious what you mean by When I try this in the browser it works.. What exactly are you doing when it doesn't work? Are you able to provide a sample of the AttestationObject?
Of course here are a example of my credential option that I get from server with the challenge and the Payload I sent to the server with the AttestationObject from the Passkey verification.
CredentialOption:
{"rp":{"id":"localhost","name":"Fido2 Test"},"user":{"name":"Test","id":"VGVzdA","displayName":"Display Test"},"challenge":"aDL84n7ODBul0JIeriAvqa4Q9n0eR06k7pFZjksdDfK7GQtLD3MoU3EADA9TmPzS24cIN72MuMFlLruj4pL9aA","pubKeyCredParams":[{"type":"public-key","alg":-7},{"type":"public-key","alg":-257},{"type":"public-key","alg":-37},{"type":"public-key","alg":-35},{"type":"public-key","alg":-258},{"type":"public-key","alg":-38},{"type":"public-key","alg":-36},{"type":"public-key","alg":-259},{"type":"public-key","alg":-39},{"type":"public-key","alg":-8}],"timeout":30000,"attestation":"none","authenticatorSelection":{"authenticatorAttachment":"cross-platform","requireResidentKey":false,"userVerification":"preferred"},"excludeCredentials":[],"extensions":{"exts":true,"uvm":true},"status":"ok","errorMessage":""}
Payload (like the object in the api):
{ "rawId" : "m0YHSxE\/3j483XnoGgNLE0Z+fUI=", "id" : "m0YHSxE\/3j483XnoGgNLE0Z+fUI=", "extensions" : { "appid" : false, "authnSel" : false }, "response" : { "attestationObject" : "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViY34zVTIo5whmj4gmSk5rN+JXS7PPo2o1ADVHU6UeNq+FdAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJtGB0sRP94+PN156BoDSxNGfn1CpQECAyYgASFYIA3+DhW9g6WqPnV86oKlHt3MRBJiozU7tdRe56zy0NKYIlggogy23\/bQCr6mhr5x46wnBGCuXbhwt7sAhjQNd+WG3A4=", "clientDataJSON" : "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiWVVSTU9EUnVOMDlFUW5Wc01FcEpaWEpwUVhaeFlUUlJPVzR3WlZJd05tczNjRVphYW10elpFUm1TemRIVVhSTVJETk5iMVV6UlVGRVFUbFViVkI2VXpJMFkwbE9OekpOZFUxR2JFeHlkV28wY0V3NVlVRSIsIm9yaWdpbiI6Imh0dHBzOi8vc2ViYXN0aWFucmFuay5kZSJ9" }, "type" : "public-key" }
And that's the error message that I receive:
{"Result":null,"status":"error","errorMessage":"AttestationObject invalid CBOR (Declared definite length of CBOR data item exceeds available buffer size.)"}
That attestation object certainly seems malformed at first glance. See https://cyberchef.org/#recipe=URL_Decode()From_Base64('A-Za-z0-9%2B/%3D',true,false)To_Hex('None',0)CBOR_Decode()&input=bzJObWJYUmtibTl1WldkaGRIUlRkRzEwb0doaGRYUm9SR0YwWVZpWTM0elZUSW81d2htajRnbVNrNXJOK0pYUzdQUG8ybzFBRFZIVTZVZU5xK0ZkQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUZKdEdCMHNSUDk0K1BOMTU2Qm9EU3hOR2ZuMUNwUUVDQXlZZ0FTRllJQTMrRGhXOWc2V3FQblY4Nm9LbEh0M01SQkppb3pVN3RkUmU1Nnp5ME5LWUlsZ2dvZ3kyM1wvYlFDcjZtaHI1eDQ2d25CR0N1WGJod3Q3c0FoalFOZCtXRzNBND0
I know, but why the apple function ASAuthorizationPlatformPublicKeyCredentialRegistration send's me this invalid credential in the app as a rawAttestationObject (this Object contains only bytes) that I convert to a base64 string.
I followed the guidelines from here: https://developer.apple.com/documentation/authenticationservices/public-private_key_authentication/supporting_passkeys
I've posted the question on the Apple developer forum now, let's see if there are some ideas why it's not working and the CBOR object is malformed
https://developer.apple.com/forums/thread/722225
So i decoded the object now by myself, maybe it help to find out why it not work. That's the results:
The decoded attestationObject:
{ fmt: "none", attStmt: nil, authData: [223, 140, 213, 76, 138, 57, 194, 25, 163, 226, 9, 146, 147, 154, 205, 248, 149, 210, 236, 243, 232, 218, 141, 64, 13, 81, 212, 233, 71, 141, 171, 225, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 155, 70, 7, 75, 17, 63, 222, 62, 60, 221, 121, 232, 26, 3, 75, 19, 70, 126, 125, 66, 165, 1, 2, 3, 38, 32, 1, 33, 88, 32, 13, 254, 14, 21, 189, 131, 165, 170, 62, 117, 124, 234, 130, 165, 30, 221, 204, 68, 18, 98, 163, 53, 59, 181, 212, 94, 231, 172, 242, 208, 210, 152, 34, 88, 32, 162, 12, 182, 223, 246, 208, 10, 190, 166, 134, 190, 113, 227, 172, 39, 4, 96, 174, 93, 184, 112, 183, 187, 0, 134, 52, 13, 119, 229, 134, 220, 14] // <-- Byte Array }
decoded authData:
Is that the publickey?
{ "3": -6, "-2": "ogy23/bQCr6mhr5x46wnBGCuXbhwt7sAhjQNd+WG3A4=", // <-- Byte array as Base64 "-0": 1, "1": 2, "-1": "Df4OFb2Dpao+dXzqgqUe3cxEEmKjNTu11F7nrPLQ0pg=" // <-- Byte array as Base64 }
The rest of the authData Object:
{ "rpIdHash": "34zVTIo5whmj4gmSk5rN+JXS7PPo2o1ADVHU6UeNq+E=", // <-- Byte array as Base64 "counterBuffer": 0, "flags": { "UserPresence": true, "UserVerification": true, "AttestationData": true, "ExtensionData": false, "flagsInt": 93 }, "counterBuffer": "AAAAAA==", // <-- Byte array as Base64 "AAGUID"; "00000000-0000-0000-0000-000000000000", "cosePublicKeyBuffer": nil, "coseExtensionsDataBuffer": nil, "restOfDatainBufferAvailable": "pQECAyYgASFYIA3+DhW9g6WqPnV86oKlHt3MRBJiozU7tdRe56zy0NKYIlggogy23/bQCr6mhr5x46wnBGCuXbhwt7sAhjQNd+WG3A4=" //<-- Byte Count (77); Byte array as Base64 }
By padding out the end with zeroes until the decoder is happy I can see we have format none, no attestation statement, then this for the authData:
DF8CD54C8A39C219A3E20992939ACD2574BB3CFA36A3500354753A51E36A15D000000000000000000000000000000000000000000149B46074B113FDE0F375E7A0680D2C4D19F9F50A9404080C988004856080370E15BD83A5AA3E757CEA82A51EDDCC441262A3353BB5D45EE7ACF2D0D298225820A20CB6DFF6D00ABEA686BE71E3AC270460AE5DB870B7BB0086340D7561B70300000000
Further tearing that apart, the first 32 bytes are the rpId hash:
DF8CD54C8A39C219A3E20992939ACD2574BB3CFA36A3500354753A51E36A15D0
That much looks reasonable, then everything goes sideways, because flags is zero and that doesn't make sense because there is more stuff at the end. I can't spot anything in there that resembles a passable credential public key.
mmmmh that's not good 😅 In the Apple Developer forum, no one reports on this either :/
What can I do so that we can find a solution to this problem? I really want it to work from the app with the API ^^
@aseigler I got a response on the apple developer forum, can you please take a look?
He is saying that when we disable the "URL Decode" and "To Hex" steps in the cyberchef test, the attestation object return correct.
result from cyberchef:
{ "fmt": "none", "attStmt": {}, "authData": { "type": "Buffer", "data": [ 223, 140, 213, 76, 138, 57, 194, 25, 163, 226, 9, 146, 147, 154, 205, 248, 149, 210, 236, 243, 232, 218, 141, 64, 13, 81, 212, 233, 71, 141, 171, 225, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 155, 70, 7, 75, 17, 63, 222, 62, 60, 221, 121, 232, 26, 3, 75, 19, 70, 126, 125, 66, 165, 1, 2, 3, 38, 32, 1, 33, 88, 32, 13, 254, 14, 21, 189, 131, 165, 170, 62, 117, 124, 234, 130, 165, 30, 221, 204, 68, 18, 98, 163, 53, 59, 181, 212, 94, 231, 172, 242, 208, 210, 152, 34, 88, 32, 162, 12, 182, 223, 246, 208, 10, 190, 166, 134, 190, 113, 227, 172, 39, 4, 96, 174, 93, 184, 112, 183, 187, 0, 134, 52, 13, 119, 229, 134, 220, 14 ] } }
It look the same that I have decoded above.
Looking at it again now the problem is obvious: all of the various fields that are required by spec to be base64 URL encoded are all standard base64 encoded instead. No clue why I didn't see that before.
@aseigler I will try to figure out a way to discover this and help users figure it out. This is such a source of confusion and so easy to get wrong.
@abergs have you already found something how to get it working in my iOS project?
@androidseb25 Actually @aseigler already found your problem, the data is base64 encoded when it should be base64url encoded.
My comment was rather a way for us to discover this on the server and return a better error message.
thank you for this thread. I ran into the exact same invalid CBOR problem when integrating iOS, and indeed switching from base64 to base64urlencoded resolved the issue!
@abergs @aseigler , somehow after encoding id, attestationObject, and clientDataJSON to base64url, the library fails to decode them properly, returning null. However, when I encode them using regular base64, the library decodes the values correctly. Could there have been any changes in the library that might explain this behavior?
when input encoded base64URL:
{
"rawResponse": {
"id": "PA6k_8VV3vnJe_V9H0X3JBbCOzs",
"type": "public-key",
"rawId": "PA6k_8VV3vnJe_V9H0X3JBbCOzs",
"response": {
"attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViYra9cdqYkFw67A8S9tFPG-3U_xVtGc-73TXCUa3NTV4ldAAAAAPv8MAcVTk7MjAtuAgVX170AFDwOpP_FVd75yXv1fR9F9yQWwjs7pQECAyYgASFYICIe1vMM7DVvLMqo7EHaNapOA-Z2BltMnLh3ZQsBPCzJIlggpaVjARhkBERA8QUMWFAHQZv0AUxHARS2zHHe7XjpVSg",
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoibHoySE12YTlxRVBpeFhZazBKM1dDQSIsIm9yaWdpbiI6Imh0dHBzOi8vaWRlbnRpdHkyLmRldi51a2xvbi5uZXQifQ"
}
}
}
How library sees:
Attestation response: AuthenticatorAttestationRawResponse { Id: null, RawId: null, Type: PublicKey, Response: AttestationResponse { AttestationObject: null, ClientDataJson: null, Transports: null }
when input encodedbase64:
{
"rawResponse": {
"rawId": "++q7tjTVzozrwPxa1xyZNkHTdDE=",
"id": "++q7tjTVzozrwPxa1xyZNkHTdDE=",
"type": "public-key",
"response": {
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiWjBEem1HeERmYzZBTWluRFhGc3pldyIsIm9yaWdpbiI6Imh0dHBzOi8vaWRlbnRpdHkyLmRldi51a2xvbi5uZXQifQ==",
"attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViYra9cdqYkFw67A8S9tFPG+3U/xVtGc+73TXCUa3NTV4ldAAAAAPv8MAcVTk7MjAtuAgVX170AFPvqu7Y01c6M68D8WtccmTZB03QxpQECAyYgASFYILVVkFwEuQS/lzx78T8202Y3F/YE5aaUBcoCS0p/YlibIlggxh3HV60e195uYNyQb8mKgtfPckGVeeI1knmYilYkx/s="
}
}
}
how library sees:
AuthenticatorAttestationRawResponse { Id: \"FBEABBB634D5CE8CEBC0FC5AD71C993641D37431\", RawId: \"FBEABBB634D5CE8CEBC0FC5AD71C993641D37431\", Type: PublicKey, Response: AttestationResponse { AttestationObject: \"A363666D74646E6F6E656761747453746D74A06861757468446174615898ADAF5C76A624170EBB03C4BDB453C6FB753FC55B4673EEF74D70946B735357895D00000000FBFC3007154E4ECC8C0B6E020557D7BD0014FBEABBB634D5CE8CEBC0FC5AD71C993641D37431A5010203262001215820B555905C04B904BF973C7BF13F36D3663717F604E5A69405CA024B4A7F62589B225820C61DC757AD1ED7DE6E60DC906FC98A82D7CF72419579E2359279988A5624C7FB\", ClientDataJson: \"7B2274797065223A22776562617574686E2E637265617465222C226368616C6C656E6765223A225A30447A6D477844666336414D696E445846737A6577222C226F726967696E223A2268747470733A2F2F6964656E74697479322E6465762E756B6C6F6E2E6E6574227D\", Transports: null }, Extensions: null, ClientExtensionResults: null }
@androidseb25 May be you can share your IOS project with me, if it is still workable?