Created
March 25, 2019 02:07
-
-
Save obetame/e270a836fe5b7be938a0e02a046c1228 to your computer and use it in GitHub Desktop.
WebAuthn helper function in brower
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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