Alexi Coard
Alexi Coard

Reputation: 7742

WebAuthn retrieve public key and credential id

I have followed this tutorial https://webauthn.guide/#registration

I am working with a yubico nfc key and I nearly managed to enroll the security key. I send a random byte challenge from the server to enroll the key, and other data.

When I register the key I managed to decode the clientDataJson and the authentication response to retrieve a lot of information. However I cannot figure out what to do with the credentialId et authData buffers, I tried decoding them, decrypt them but I always got somme strange data and nothing that looks like a credentialId or a public key.

Here is the code I got so far

 var createCredentialDefaultArgs = {
            publicKey: {
                // Relying Party (a.k.a. - Service):
                rp: {
                    name: 'Dummy'
                },

                // User:
                user: {
                    id: new Uint8Array(16),
                    name: 'John Doe',
                    displayName: 'Mr Doe'
                },

                pubKeyCredParams: [{
                    type: "public-key",
                    alg: -7
                }],

                attestation: "direct",

                timeout: 60000,

                challenge:  new Uint8Array(/* stuff*/).buffer
            }
        };


        $('[data-register-webauthn]')
            .on('click', function () {
                // register / create a new credential
                navigator.credentials.create(createCredentialDefaultArgs)
                    .then((cred) => {
                        console.log("NEW CREDENTIAL", cred);

                        const utf8Decoder = new TextDecoder('utf-8');

                        const decodedClientData = utf8Decoder.decode(cred.response.clientDataJSON);

                        // parse the string as an object
                        const clientDataObj = JSON.parse(decodedClientData);

                        const decodedAttestationObj = CBOR.decode(cred.response.attestationObject);

                        const {authData} = decodedAttestationObj;
                        // get the length of the credential ID
                        const dataView = new DataView(new ArrayBuffer(2));
                        const idLenBytes = authData.slice(53, 55);
                        idLenBytes.forEach(
                            (value, index) => dataView.setUint8(
                                index, value)
                        );
                        const credentialIdLength = dataView.getUint16();

                        // get the credential ID
                        const credentialId = authData.slice(
                            55, credentialIdLength);

                        // get the public key object
                        const publicKeyBytes = authData.slice(
                            55 + credentialIdLength);

                        // the publicKeyBytes are encoded again as CBOR
                        const publicKeyObject = CBOR.decode(
                            publicKeyBytes.buffer);
                        console.log(publicKeyObject)
                    })
                    .catch((err) => {
                        console.log("ERROR", err);
                    });
            })

In the end, I don't know what to do with the public key object and the credentialId. Deconding seems useless.

Any idea ?

Upvotes: 4

Views: 4288

Answers (2)

josemmo
josemmo

Reputation: 7073

I always got some strange data and nothing that looks like a credentialId or a public key.

That's because both are byte sequences.

The credentialId you parsed from authData is a string of random bytes (usually 96 bytes long) generated by the U2F key, so don't expect those to make sense.

The publicKey variable is a bit more tricky as it is CBOR-encoded (not your regular PEM string), which after decoding it in publicKeyObject should give you an output like this:

{
   1: 2,              // Ellipic Curve key type
   3: -7,             // ES256 signature algorithm
  -1: 1,              // P-256 curve
  -2: 0x7885DB484..., // X value
  -3: 0x814F3DD31...  // Y value
}

In the end, I don't know what to do with the public key object and the credentialId.

You need the Credential ID to identify the user who is trying to authenticate against your website and the Public Key to verify its identity.

All the other information extracted from the authData response should be validated but there's no need to keep it, just save the pair credentialId and publicKeyBytes.

This websites explains the authentication process in fair detail: https://webauthn.guide/#authentication

To find out how to validate the signature check this link: https://w3c.github.io/webauthn/#fig-signature

Upvotes: 8

grzuy
grzuy

Reputation: 5021

Credential ID and Credential Public Key values are both non human palatable byte-strings.

Regarding what to do with them. You store them next to the user data in your database or any other long term storage your server is using, so you can then use them as inputs for future Authentications ceremonies.

Upvotes: 1

Related Questions