passkey attestationObject confusion

Hello everybody, I'm trying to implement passkey provider for iOS device. I'm in the register phase of the passkey. Let's say this is my code to register request, what am I doing wrong?:

import SwiftCBOR
class CredentialProviderViewController: ASCredentialProviderViewController {

.
.
.

    func generatePublicKeyCborEncoded() -> Data {
        let privateKey = P256.Signing.PrivateKey()
        let publicKey = privateKey.publicKey.x963Representation
        let decoded: [CBOR: CBOR] = [
            CBOR.init(integerLiteral: 1): CBOR.init(integerLiteral: 2),
            CBOR.init(integerLiteral: 3): CBOR.init(integerLiteral: -7),
            CBOR.init(integerLiteral: -1): CBOR.init(integerLiteral: 1),
            CBOR.init(integerLiteral: -2): CBOR.byteString(publicKey[1..<33].map { $0 }),
            CBOR.init(integerLiteral: -3): CBOR.byteString(publicKey[33..<65].map { $0 })
        ]
        
        return Data(CBOR.encode(decoded))
    }

    @IBAction func onRegister(_ sender: UIButton) {
        NSLog("onRegister called 1")
        guard let request = newRegistrationRequest as? ASPasskeyCredentialRequest else {return}
        let attObj: Data = generatePublicKeyCborEncoded()
        let passkey: ASPasskeyRegistrationCredential = ASPasskeyRegistrationCredential(
            relyingParty: request.credentialIdentity.serviceIdentifier.identifier,
            clientDataHash: request.clientDataHash,
            credentialID: Data([67, 92, 125, 254, 60, 232, 238, 248, 14, 107, 245, 21, 85, 130, 40, 54],
            attestationObject: attObj
        )
        
        extensionContext.completeRegistrationRequest(using: passkey){ endedWell in
            NSLog("onRegister called \(endedWell ? "" : "not") ended well")
        }
    }
}

Replies

so I tried to send all the attestation object using this code:


import SwiftCBOR

class CredentialProviderViewController: ASCredentialProviderViewController {



    ...

    ...

    ...



    func hashRP(_ rp: String) -> [UInt8] {

        let hashed = SHA256.hash(data: Data(rp.data(using: .utf8) ?? Data([])))

        return Array(hashed)

    }

    

    fileprivate func generateCosePublickKey() -> [UInt8] {

        let privateKey = P256.Signing.PrivateKey()

        let publicKey = privateKey.publicKey.x963Representation

	

        let decodedPublicKey: [Int:CBOR] = [

            1: 2,

            3: -7,

            -1: 1,

            -2: CBOR.byteString(publicKey[1..<33].map { $0 }),

            -3: CBOR.byteString(publicKey[33..<65].map { $0 })

        ]

        return CBOR.encode(decodedPublicKey)

    }

    

    func generateAttestedObject(_ rp: String) -> Data {

        var att: [Int:CBOR] = [:]

        

        let hashedRpId: [UInt8] = hashRP(rp)

        let flagsAndSignedCount: [UInt8] = [93, 0, 0, 0, 0]

        let idLength: [UInt8] = [0, 16]

        let cosePublicKey: [UInt8] = generateCosePublickKey()

        let attestedCredentialData = hashedRpId + flagsAndSignedCount + exampleAAGUID + idLength + hashedRpId[0..<16] + cosePublicKey

        

        att[1] = "none"

        att[2] = CBOR.byteString(attestedCredentialData)

        att[3] = CBOR.map([:])

        

        let encoded = CBOR.encode(att)

        

        return Data(encoded)

    }



    @IBAction func onRegister(_ sender: UIButton) {

        guard let request = newRegistrationRequest as? ASPasskeyCredentialRequest else {return}

        let attObj: Data =  Data(generateAttestedObject(request.credentialIdentity.serviceIdentifier.identifier))

        let passkey: ASPasskeyRegistrationCredential = ASPasskeyRegistrationCredential(

            relyingParty: request.credentialIdentity.serviceIdentifier.identifier,

            clientDataHash: request.clientDataHash,

            credentialID: Data(hashRP(request.credentialIdentity.serviceIdentifier.identifier)[0..<16]),

            attestationObject: Data(attObj)

        )

        

        extensionContext.completeRegistrationRequest(using: passkey){ endedWell in

            NSLog("onRegister called \(endedWell ? "" : "not") ended well")

        }

    }

}

but i still get errors on the client side.

can someone please help me?

Your attestation object is not formatted correctly. You seem to be mixing up encoding for WebAuthn and CTAP. These two specs both encode similar information, but in different ways. The WebAuthn spec defines encoding for objects that get passed between the website and the web browser, and is what a passkey provider should use. The CTAP spec defines communication between an operating system and security keys.

I'd recommend looking up an example WebAuthn attestation object or just calling navigator.credentials.create() yourself and inspecting the result, so that you have something to compare with.

  • @garrett-davidson Could you please provide us with an example? It's a bit of a black box when it comes to creating a passkey.

Add a Comment