diff --git a/src/main/resources/META-INF/resources/js/webauthn-debug.js b/src/main/resources/META-INF/resources/js/webauthn-debug.js index 2630a52..ca87691 100644 --- a/src/main/resources/META-INF/resources/js/webauthn-debug.js +++ b/src/main/resources/META-INF/resources/js/webauthn-debug.js @@ -221,6 +221,7 @@ }; WebAuthn.base64ToBuffer = base64ToBuffer; + WebAuthn.bufferToBase64 = bufferToBase64; return WebAuthn; })); \ No newline at end of file diff --git a/src/main/resources/templates/OAuthResource/loginPasswordless.html b/src/main/resources/templates/OAuthResource/loginPasswordless.html index 2e0f902..d9fd3b5 100644 --- a/src/main/resources/templates/OAuthResource/loginPasswordless.html +++ b/src/main/resources/templates/OAuthResource/loginPasswordless.html @@ -239,6 +239,8 @@ This is a base64 encoded binary representation of an attestation statement. The attestation statement is produced by the authenticator to prove to the relying party (e.g., a website) that a new public key credential has been created in the authenticator. It typically contains details about the authenticator, a freshly generated public key for the user, some metadata, and a signature from the authenticator. It is CBOR encoded.

+

The authData is binary encoded and contain the actual public key, but also more flags and info about the authentication:

+
  • clientDataJSON

    @@ -345,6 +347,8 @@

    This contains information about the authentication event. It typically includes the hash of the clientDataJSON, a sign count (to protect against clone attacks), and other data relevant to the authentication process.

    +

    It is binary encoded and this time does not contain the public key:

    +
  • signature

    @@ -418,6 +422,63 @@ } } + function readHexString(view, start, len){ + const hash = new Uint8Array(len); + + for (let i = 0; i < len; i++) { + hash[i] = view.getUint8(i+start); + } + + return Array.from(hash).map(byte => { + return ('0' + byte.toString(16)).slice(-2); // Ensure two-digit hex representation + }).join(''); + } + + function parseCredentialPubKey(array) { + console.log(array) + let view = new DataView(array.buffer, array.byteOffset, array.byteLength); + let credentialIdLength = view.getUint16(16, false); + return { + "aaguid": readHexString(view, 0, 16), + "credentialIdLength": credentialIdLength, + "credentialId": WebAuthn.bufferToBase64(array.slice(18, credentialIdLength).buffer), + "credentialPublicKey": WebAuthn.bufferToBase64(array.slice(18+credentialIdLength).buffer) + } + } + + function parseAuthenticatorData(array) { + try { + console.log(array) + let view = new DataView(array.buffer, array.byteOffset, array.byteLength); + let flags = view.getInt8(32); + const hashHex = readHexString(view, 0, 32); + + const result = { + "rpIdHash": hashHex, + "flags": { + "userPresent": !!(flags & 1), + "userVerified": !!(flags & (1 << 2)), + "attestedCredentialData": !!(flags & (1 << 6)), + "extensionDataIncluded": !!(flags & (1 << 7)) + }, + "signCount": view.getUint32(33, false), + } + + try { + if (result.flags.attestedCredentialData) { + result['credentialPubKey'] = parseCredentialPubKey(array.slice(37)); + } + } catch (e) { + console.log(e) + } + + return result; + } catch (e) { + console.warn(e) + return "Unable to parse: " + e; + } + } + function tracer(stage, params) { console.log(stage, params) @@ -498,7 +559,10 @@ $("#navigator-attestation").showInViewport(); $("#navigator-attestation-body").html(JSON.stringify(response, null, 2)); $("#navigator-attestation-clientDataJSON").html(JSON.stringify(JSON.parse(tryDecodeBase64(response.response.clientDataJSON)), null, 2)); - $("#navigator-attestationObject").html(JSON.stringify(CBOR.decode(WebAuthn.base64ToBuffer(response.response.attestationObject)), null, 2)); + let attestationObject = CBOR.decode(WebAuthn.base64ToBuffer(response.response.attestationObject)); + let authData = parseAuthenticatorData(attestationObject.authData); + $("#navigator-attestationObject").html(JSON.stringify(attestationObject, null, 2)); + $("#navigator-authData").html(JSON.stringify(authData, null, 2)); return continueButton("#navigator-attestation", response); } @@ -512,6 +576,9 @@ $("#navigator-assertion").showInViewport(); $("#navigator-assertion-body").html(JSON.stringify(response, null, 2)); $("#navigator-assertion-clientDataJSON").html(JSON.stringify(JSON.parse(tryDecodeBase64(response.response.clientDataJSON)), null, 2)); + let authenticatorData = WebAuthn.base64ToBuffer(response.response.authenticatorData); + let authData = parseAuthenticatorData(new Uint8Array(authenticatorData)); + $("#navigator-authenticatorData").html(JSON.stringify(authData, null, 2)); return continueButton("#navigator-assertion", response); }