- From: Craig Francis via GitHub <sysbot+gh@w3.org>
- Date: Wed, 15 Jan 2020 23:06:13 +0000
- To: public-webauthn@w3.org
craigfrancis has just created a new issue for https://github.com/w3c/webauthn: == Use of CBOR, and Uint8Array/ArrayBuffer == Forgive me for a simplistic question, but why does WebAuthn use CBOR encoding? This is an API for users to do a single authentication, so there is pretty much no performance benefit of using a binary format, it just makes the API harder for developers to understand, make mistakes, and requires every website to pull in a 3rd party library to parse it (not good for security). Couldn't this API simply return a JavaScript object, with the values ready to be sent back to the server? And because the binary data needs to be sent be sent to/from the server, the use of Uint8Array/ArrayBuffer is a pain - can't it use base64 encoding? It's supported in every language, can be easily sent in a POST request, and is used in other fields (e.g. the [Client Data `challenge` is base64 encoded](https://w3c.github.io/webauthn/#dictionary-client-data), but the [Credential Creation `challenge` is not](https://w3c.github.io/webauthn/#dom-publickeycredentialcreationoptions-challenge)). --- I started with this ugly mix of PHP and (unsafe-inline) JavaScript to provide the challenge: <script nonce="<?= htmlentities($script_nonce) ?>" integrity="haha, nope"> challenge_uInt8 = new Uint8Array([<?= htmlentities(implode(',', unpack('C*', $challenge))) ?>]) </script> It's much easier/safer to pass a base64 encoded value to JavaScript with a `data-` attribute: <input type="button" ... data-challenge="<?= htmlentities(base64_encode($challenge)) ?>" /> Unfortunately I then need to convert this base64 value to something WebAuthn understands: function base64_to_uint8array(base64) { var binary = window.atob(base64), array = new Uint8Array(new ArrayBuffer(binary.length)); for (var k = (binary.length - 1); k >= 0; k--) { array[k] = binary.charCodeAt(k); } return array; } var challenge_base64 = this.getAttribute('data-challenge'), challenge_uInt8 = base64_to_uint8array(challenge_base64); And it's worse getting the credentials.create() response into something usable: var decoder = new TextDecoder('utf-8'), clientData = JSON.parse(decoder.decode(result.response.clientDataJSON)); var attestationData = CBOR.decode(result.response.attestationObject); // A whole CBOR parsing library needed for this. var dataView = new DataView(new ArrayBuffer(2)); var idLenBytes = attestationData.authData.slice(53, 55); // Great, another binary format to parse. idLenBytes.forEach(function(value, index) { dataView.setUint8(index, value) }); var credentialIdLength = dataView.getUint16(); var credentialId = attestationData.authData.slice(55, credentialIdLength); var publicKeyBytes = attestationData.authData.slice(55 + credentialIdLength); var publicKeyObject1 = CBOR.decode(publicKeyBytes.buffer); // And CBOR is back again. var publicKeyObject2 = { 'id': result.id, 'type': result.type, 'client_type': client_data.type, 'client_origin': client_data.origin, 'client_challenge': client_data.challenge, 'key_type': publicKeyObject1[1], // 2 = Elliptic Curve; using more magic numbers for keys and values, does this save a few bytes somewhere? 'key_algorithm': publicKeyObject1[3], // -7 = ECDSA with SHA256 'key_curve_type': publicKeyObject1[-1], // 1 = P-256 'key_curve_x': btoa(String.fromCharCode.apply(null, publicKeyObject1[-2])), 'key_curve_y': btoa(String.fromCharCode.apply(null, publicKeyObject1[-3])) }; return JSON.stringify(publicKeyObject2); // Finally, something that can be sent to the server. --- As an aside, would the [non-JavaScript approach](https://github.com/w3c/webauthn/issues/1255) be able to skip most of this extra processing? Please view or discuss this issue at https://github.com/w3c/webauthn/issues/1362 using your GitHub account
Received on Wednesday, 15 January 2020 23:06:15 UTC