Skip to content

Instantly share code, notes, and snippets.

@obetame
Created March 25, 2019 02:07
Show Gist options
  • Save obetame/e270a836fe5b7be938a0e02a046c1228 to your computer and use it in GitHub Desktop.
Save obetame/e270a836fe5b7be938a0e02a046c1228 to your computer and use it in GitHub Desktop.
WebAuthn helper function in brower
function str2ab(str) {
const buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
const bufView = new Uint16Array(buf);
for (let i=0, strLen=str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
function publicKeyCredentialToJSON(res) {
// res is PublicKeyCredential
return {
id: res.id,
type: res.type,
rawId: ab2str(res.rawId),
response: {
attestationObject: CBOR.decode(res.response.attestationObject),
clientDataJSON: ab2str(res.response.clientDataJSON)
}
};
}
const bufferToString = buff => {
const enc = new TextDecoder(); // always utf-8
return enc.decode(buff);
};
const getEndian = () => {
let arrayBuffer = new ArrayBuffer(2);
let uint8Array = new Uint8Array(arrayBuffer);
let uint16array = new Uint16Array(arrayBuffer);
uint8Array[0] = 0xaa; // set first byte
uint8Array[1] = 0xbb; // set second byte
if (uint16array[0] === 0xbbaa) return 'little';
else return 'big';
};
const readBE16 = buffer => {
if (buffer.length !== 2) throw new Error('Only 2byte buffer allowed!');
if (getEndian() !== 'big') buffer = buffer.reverse();
return new Uint16Array(buffer.buffer)[0];
};
const readBE32 = buffer => {
if (buffer.length !== 4) throw new Error('Only 4byte buffers allowed!');
if (getEndian() !== 'big') buffer = buffer.reverse();
return new Uint32Array(buffer.buffer)[0];
};
const bufToHex = buffer => {
// buffer is an ArrayBuffer
return Array.prototype.map
.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2))
.join('');
};
// https://gist.github.com/herrjemand/dbeb2c2b76362052e5268224660b6fbc
const parseAuthData = buffer => {
let rpIdHash = buffer.slice(0, 32);
buffer = buffer.slice(32);
let flagsBuf = buffer.slice(0, 1);
buffer = buffer.slice(1);
let flagsInt = flagsBuf[0];
let flags = {
up: !!(flagsInt & 0x01),
uv: !!(flagsInt & 0x04),
at: !!(flagsInt & 0x40),
ed: !!(flagsInt & 0x80),
flagsInt
};
let counterBuf = buffer.slice(0, 4);
buffer = buffer.slice(4);
let counter = readBE32(counterBuf);
let aaguid = undefined;
let credID = undefined;
let COSEPublicKey = undefined;
if (flags.at) {
aaguid = buffer.slice(0, 16);
buffer = buffer.slice(16);
let credIDLenBuf = buffer.slice(0, 2);
buffer = buffer.slice(2);
let credIDLen = readBE16(credIDLenBuf);
credID = buffer.slice(0, credIDLen);
buffer = buffer.slice(credIDLen);
COSEPublicKey = buffer;
}
return {
rpIdHash,
flagsBuf,
flags,
counter,
counterBuf,
aaguid,
credID,
COSEPublicKey
};
};
const generateRandomBuffer = length => {
if (!length) length = 32;
const randomBuff = new Uint8Array(length);
window.crypto.getRandomValues(randomBuff);
return randomBuff;
};
const publicKeyCredentialToJSON = pubKeyCred => {
if (pubKeyCred instanceof Array) {
let arr = [];
for (let i of pubKeyCred) arr.push(publicKeyCredentialToJSON(i));
return arr;
}
if (pubKeyCred instanceof ArrayBuffer) {
return base64url.encode(pubKeyCred);
}
if (pubKeyCred instanceof Object) {
let obj = {};
for (let key in pubKeyCred) {
obj[key] = publicKeyCredentialToJSON(pubKeyCred[key]);
}
return obj;
}
return pubKeyCred;
};
const preformatMakeCredReq = makeCredReq => {
makeCredReq.challenge = base64url.decode(makeCredReq.challenge);
makeCredReq.user.id = base64url.decode(makeCredReq.user.id);
return makeCredReq;
};
const preformatGetAssertReq = getAssert => {
getAssert.challenge = base64url.decode(getAssert.challenge);
for (let allowCred of getAssert.allowCredentials) {
allowCred.id = base64url.decode(allowCred.id);
}
return getAssert;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment