2020-05-05 10:32:49 +02:00
|
|
|
// Copyright (c) 2017 Duo Security, Inc. All rights reserved.
|
|
|
|
// Under BSD 3-Clause "New" or "Revised" License
|
|
|
|
// https://github.com/duo-labs/py_webauthn/
|
|
|
|
|
|
|
|
function b64enc(buf) {
|
|
|
|
return base64js
|
|
|
|
.fromByteArray(buf)
|
|
|
|
.replace(/\+/g, "-")
|
|
|
|
.replace(/\//g, "_")
|
|
|
|
.replace(/=/g, "");
|
|
|
|
}
|
|
|
|
|
|
|
|
function b64RawEnc(buf) {
|
|
|
|
return base64js.fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_");
|
|
|
|
}
|
|
|
|
|
|
|
|
function hexEncode(buf) {
|
|
|
|
return Array.from(buf)
|
|
|
|
.map(function (x) {
|
|
|
|
return ("0" + x.toString(16)).substr(-2);
|
|
|
|
})
|
|
|
|
.join("");
|
|
|
|
}
|
|
|
|
|
|
|
|
const transformCredentialRequestOptions = (
|
|
|
|
credentialRequestOptionsFromServer
|
|
|
|
) => {
|
|
|
|
let { challenge, allowCredentials } = credentialRequestOptionsFromServer;
|
|
|
|
|
|
|
|
challenge = Uint8Array.from(
|
|
|
|
atob(challenge.replace(/\_/g, "/").replace(/\-/g, "+")),
|
|
|
|
(c) => c.charCodeAt(0)
|
|
|
|
);
|
|
|
|
|
|
|
|
allowCredentials = allowCredentials.map((credentialDescriptor) => {
|
|
|
|
let { id } = credentialDescriptor;
|
|
|
|
id = id.replace(/\_/g, "/").replace(/\-/g, "+");
|
|
|
|
id = Uint8Array.from(atob(id), (c) => c.charCodeAt(0));
|
|
|
|
return Object.assign({}, credentialDescriptor, { id });
|
|
|
|
});
|
|
|
|
|
|
|
|
const transformedCredentialRequestOptions = Object.assign(
|
|
|
|
{},
|
|
|
|
credentialRequestOptionsFromServer,
|
|
|
|
{ challenge, allowCredentials }
|
|
|
|
);
|
|
|
|
|
|
|
|
return transformedCredentialRequestOptions;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Transforms items in the credentialCreateOptions generated on the server
|
|
|
|
* into byte arrays expected by the navigator.credentials.create() call
|
|
|
|
* @param {Object} credentialCreateOptionsFromServer
|
|
|
|
*/
|
|
|
|
const transformCredentialCreateOptions = (
|
|
|
|
credentialCreateOptionsFromServer
|
|
|
|
) => {
|
2020-05-18 07:05:37 +02:00
|
|
|
let { challenge, user, excludeCredentials } = credentialCreateOptionsFromServer;
|
2020-05-05 10:32:49 +02:00
|
|
|
user.id = Uint8Array.from(
|
|
|
|
atob(
|
|
|
|
credentialCreateOptionsFromServer.user.id
|
|
|
|
.replace(/\_/g, "/")
|
|
|
|
.replace(/\-/g, "+")
|
|
|
|
),
|
|
|
|
(c) => c.charCodeAt(0)
|
|
|
|
);
|
|
|
|
|
|
|
|
challenge = Uint8Array.from(
|
|
|
|
atob(
|
|
|
|
credentialCreateOptionsFromServer.challenge
|
|
|
|
.replace(/\_/g, "/")
|
|
|
|
.replace(/\-/g, "+")
|
|
|
|
),
|
|
|
|
(c) => c.charCodeAt(0)
|
|
|
|
);
|
|
|
|
|
2020-05-18 07:05:37 +02:00
|
|
|
excludeCredentials = excludeCredentials.map((credentialDescriptor) => {
|
|
|
|
let { id } = credentialDescriptor;
|
|
|
|
id = id.replace(/\_/g, "/").replace(/\-/g, "+");
|
|
|
|
id = Uint8Array.from(atob(id), (c) => c.charCodeAt(0));
|
|
|
|
return Object.assign({}, credentialDescriptor, { id });
|
|
|
|
});
|
|
|
|
|
2020-05-05 10:32:49 +02:00
|
|
|
const transformedCredentialCreateOptions = Object.assign(
|
|
|
|
{},
|
|
|
|
credentialCreateOptionsFromServer,
|
2020-05-18 07:05:37 +02:00
|
|
|
{ challenge, user, excludeCredentials }
|
2020-05-05 10:32:49 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
return transformedCredentialCreateOptions;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Transforms the binary data in the credential into base64 strings
|
|
|
|
* for posting to the server.
|
|
|
|
* @param {PublicKeyCredential} newAssertion
|
|
|
|
*/
|
|
|
|
const transformNewAssertionForServer = (newAssertion) => {
|
|
|
|
const attObj = new Uint8Array(newAssertion.response.attestationObject);
|
|
|
|
const clientDataJSON = new Uint8Array(newAssertion.response.clientDataJSON);
|
|
|
|
const rawId = new Uint8Array(newAssertion.rawId);
|
|
|
|
|
|
|
|
const registrationClientExtensions = newAssertion.getClientExtensionResults();
|
|
|
|
|
|
|
|
return {
|
|
|
|
id: newAssertion.id,
|
|
|
|
rawId: b64enc(rawId),
|
|
|
|
type: newAssertion.type,
|
|
|
|
attObj: b64enc(attObj),
|
|
|
|
clientData: b64enc(clientDataJSON),
|
|
|
|
registrationClientExtensions: JSON.stringify(registrationClientExtensions),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Encodes the binary data in the assertion into strings for posting to the server.
|
|
|
|
* @param {PublicKeyCredential} newAssertion
|
|
|
|
*/
|
|
|
|
const transformAssertionForServer = (newAssertion) => {
|
|
|
|
const authData = new Uint8Array(newAssertion.response.authenticatorData);
|
|
|
|
const clientDataJSON = new Uint8Array(newAssertion.response.clientDataJSON);
|
|
|
|
const rawId = new Uint8Array(newAssertion.rawId);
|
|
|
|
const sig = new Uint8Array(newAssertion.response.signature);
|
|
|
|
const assertionClientExtensions = newAssertion.getClientExtensionResults();
|
|
|
|
|
|
|
|
return {
|
|
|
|
id: newAssertion.id,
|
|
|
|
rawId: b64enc(rawId),
|
|
|
|
type: newAssertion.type,
|
|
|
|
authData: b64RawEnc(authData),
|
|
|
|
clientData: b64RawEnc(clientDataJSON),
|
|
|
|
signature: hexEncode(sig),
|
|
|
|
assertionClientExtensions: JSON.stringify(assertionClientExtensions),
|
|
|
|
};
|
|
|
|
};
|