From becdf3bc0b7a72a656885e774643bf0aa4143e8a Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Thu, 16 May 2019 15:05:44 -0700 Subject: [PATCH] more WebAuthn cleanup. --- Webauthn.js | 84 +++++++++++++------------- public/images/hardware-key-U2F-24.png | Bin 988 -> 0 bytes views/default-min.handlebars | 2 +- views/default.handlebars | 2 +- views/login-min.handlebars | 2 +- views/login-mobile-min.handlebars | 2 +- views/login-mobile.handlebars | 19 ------ views/login.handlebars | 19 ------ 8 files changed, 46 insertions(+), 84 deletions(-) delete mode 100644 public/images/hardware-key-U2F-24.png diff --git a/Webauthn.js b/Webauthn.js index 72f48b03..5c62541e 100644 --- a/Webauthn.js +++ b/Webauthn.js @@ -3,12 +3,12 @@ * @version v0.0.1 */ -// +// This code is based on a portion of the webauthn module at: https://www.npmjs.com/package/webauthn 'use strict' -const crypto = require('crypto') -const cbor = require('cbor') +const crypto = require('crypto'); +const cbor = require('cbor'); //const iso_3166_1 = require('iso-3166-1') //const Certificate = null; //require('@fidm/x509') @@ -23,7 +23,7 @@ module.exports.CreateWebAuthnModule = function () { pubKeyCredParams: [{ type: 'public-key', alg: -7 }], timeout: 60000, attestation: 'none' - } + }; } obj.verifyAuthenticatorAttestationResponse = function (webauthnResponse) { @@ -38,7 +38,7 @@ module.exports.CreateWebAuthnModule = function () { if (ctapMakeCredResp.fmt === 'none') { if (!(authrDataStruct.flags & 0x01)) { throw new Error('User was NOT presented during authentication!'); } // U2F_USER_PRESENTED - const publicKey = COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey) + const publicKey = COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey); response.verified = true; if (response.verified) { @@ -47,7 +47,7 @@ module.exports.CreateWebAuthnModule = function () { publicKey: ASN1toPEM(publicKey), counter: authrDataStruct.counter, keyId: authrDataStruct.credID.toString('base64') - } + }; } } /* @@ -194,14 +194,14 @@ module.exports.CreateWebAuthnModule = function () { throw new Error(`Unsupported attestation format: ${ctapMakeCredResp.fmt}`); } - return response + return response; } obj.verifyAuthenticatorAssertionResponse = function (webauthnResponse, authr) { const response = { 'verified': false } if (['fido-u2f'].includes(authr.fmt)) { - const authrDataStruct = parseGetAssertAuthData(webauthnResponse.authenticatorData) - if (!(authrDataStruct.flags & 0x01)) { throw new Error('User was not presented durring authentication!') } // U2F_USER_PRESENTED + const authrDataStruct = parseGetAssertAuthData(webauthnResponse.authenticatorData); + if (!(authrDataStruct.flags & 0x01)) { throw new Error('User was not presented durring authentication!'); } // U2F_USER_PRESENTED response.counter = authrDataStruct.counter; response.verified = verifySignature(webauthnResponse.signature, Buffer.concat([authrDataStruct.rpIdHash, authrDataStruct.flagsBuf, authrDataStruct.counterBuf, hash(webauthnResponse.clientDataJSON)]), authr.publicKey); } @@ -212,51 +212,51 @@ module.exports.CreateWebAuthnModule = function () { function verifySignature(signature, data, publicKey) { return crypto.createVerify('SHA256').update(data).verify(publicKey, signature); } function parseGetAssertAuthData(buffer) { - const rpIdHash = buffer.slice(0, 32) - buffer = buffer.slice(32) - const flagsBuf = buffer.slice(0, 1) - buffer = buffer.slice(1) - const flags = flagsBuf[0] - const counterBuf = buffer.slice(0, 4) - buffer = buffer.slice(4) - const counter = counterBuf.readUInt32BE(0) - return { rpIdHash, flagsBuf, flags, counter, counterBuf } + const rpIdHash = buffer.slice(0, 32); + buffer = buffer.slice(32); + const flagsBuf = buffer.slice(0, 1); + buffer = buffer.slice(1); + const flags = flagsBuf[0]; + const counterBuf = buffer.slice(0, 4); + buffer = buffer.slice(4); + const counter = counterBuf.readUInt32BE(0); + return { rpIdHash, flagsBuf, flags, counter, counterBuf }; } function parseMakeCredAuthData(buffer) { - const rpIdHash = buffer.slice(0, 32) - buffer = buffer.slice(32) - const flagsBuf = buffer.slice(0, 1) - buffer = buffer.slice(1) - const flags = flagsBuf[0] - const counterBuf = buffer.slice(0, 4) - buffer = buffer.slice(4) - const counter = counterBuf.readUInt32BE(0) - const aaguid = buffer.slice(0, 16) - buffer = buffer.slice(16) - const credIDLenBuf = buffer.slice(0, 2) - buffer = buffer.slice(2) - const credIDLen = credIDLenBuf.readUInt16BE(0) - const credID = buffer.slice(0, credIDLen) - buffer = buffer.slice(credIDLen) - const COSEPublicKey = buffer - return { rpIdHash, flagsBuf, flags, counter, counterBuf, aaguid, credID, COSEPublicKey } + const rpIdHash = buffer.slice(0, 32); + buffer = buffer.slice(32); + const flagsBuf = buffer.slice(0, 1); + buffer = buffer.slice(1); + const flags = flagsBuf[0]; + const counterBuf = buffer.slice(0, 4); + buffer = buffer.slice(4); + const counter = counterBuf.readUInt32BE(0); + const aaguid = buffer.slice(0, 16); + buffer = buffer.slice(16); + const credIDLenBuf = buffer.slice(0, 2); + buffer = buffer.slice(2); + const credIDLen = credIDLenBuf.readUInt16BE(0); + const credID = buffer.slice(0, credIDLen); + buffer = buffer.slice(credIDLen); + const COSEPublicKey = buffer; + return { rpIdHash, flagsBuf, flags, counter, counterBuf, aaguid, credID, COSEPublicKey }; } function COSEECDHAtoPKCS(COSEPublicKey) { const coseStruct = cbor.decodeAllSync(COSEPublicKey)[0]; - return Buffer.concat([Buffer.from([0x04]), coseStruct.get(-2), coseStruct.get(-3)]) + return Buffer.concat([Buffer.from([0x04]), coseStruct.get(-2), coseStruct.get(-3)]); } function ASN1toPEM(pkBuffer) { if (!Buffer.isBuffer(pkBuffer)) { throw new Error("ASN1toPEM: pkBuffer must be Buffer."); } - let type - if (pkBuffer.length == 65 && pkBuffer[0] == 0x04) { pkBuffer = Buffer.concat([ new Buffer.from("3059301306072a8648ce3d020106082a8648ce3d030107034200", "hex"), pkBuffer ]); type = 'PUBLIC KEY' } else { type = 'CERTIFICATE' } - const b64cert = pkBuffer.toString('base64') - let PEMKey = '' + let type; + if (pkBuffer.length == 65 && pkBuffer[0] == 0x04) { pkBuffer = Buffer.concat([new Buffer.from("3059301306072a8648ce3d020106082a8648ce3d030107034200", "hex"), pkBuffer]); type = 'PUBLIC KEY'; } else { type = 'CERTIFICATE'; } + const b64cert = pkBuffer.toString('base64'); + let PEMKey = ''; for (let i = 0; i < Math.ceil(b64cert.length / 64); i++) { const start = 64 * i; PEMKey += b64cert.substr(start, 64) + '\n'; } - PEMKey = `-----BEGIN ${type}-----\n` + PEMKey + `-----END ${type}-----\n` - return PEMKey + PEMKey = `-----BEGIN ${type}-----\n` + PEMKey + `-----END ${type}-----\n`; + return PEMKey; } return obj; diff --git a/public/images/hardware-key-U2F-24.png b/public/images/hardware-key-U2F-24.png deleted file mode 100644 index 7d11dd81af69624c018c5222d00d04c7d097dc6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 988 zcmV<210(#2P)N2bZe?^J zG%heMIczh2P5=M{T1iAfR5(wq)ca49XB5Ek3Kg;H#zg1D7K(QiX@LgTrWDFh8E$Bl zOmtI>G8;!gaj0A3Q0H{X!eHRMVP>RI%_Nd2!o(Hu$| zJ`~?Y$U9RLrZ6>e3JY9sv$Y{l<}VgZPmE;O*-wN5b~cIFRKOPvE`qf|OfrVEv$24$ znhV+8Tts$3x@^pHIoQ*@iJ2CoI!LlEHuH7Mr~Y*fF-g;?yiiKj#ZoFSm9kIR;VF_A z?AaEP@8bK*KHF;wW!;%$Wq<2#<|NOM`JVbMLJ4I}+d0&>m$}I^2NuLxW2kAb!ria7 zeIGxxmZ2S~#V|br_b*j=Iu78uwx6}1t&okYHs^5S+CfaVx#}Qk;RjBBynYz7Emo~* zNU)n|yy@Xgr-#PQ#FZob)K!ng9xv}C zITECz9WGjKpAtR3x?GU#u+a9$Ioj@?Wl-+7uOd!l+#8u!{*D&61E z{^teZBG%L-S?~DklF&*=&t;(%TUxT(Bi^&qMwjAM-0}*2H@MyVE4M^`tiGBp zrJLDX`6caluF&208{Lm@(*2~9lytisw7zhyunt#0LzI4CfiokOM^Ah3KD|%xvmSay z^gdIbh35~XzUPmGhddTh@#JrXm-P8gb {{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

\ No newline at end of file + {{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

\ No newline at end of file diff --git a/views/default.handlebars b/views/default.handlebars index a94eee17..7f9dd040 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -1553,7 +1553,7 @@ x += "
"; if (message.keys && message.keys.length > 0) { for (var i in message.keys) { - var key = message.keys[i], type = ((key.type == 1)?'U2F':(key.type == 2)?'OTP':'WebAuthn'); + var key = message.keys[i], type = (key.type == 2)?'OTP':'WebAuthn'; x += start + '' + key.name + "" + end; } } else { diff --git a/views/login-min.handlebars b/views/login-min.handlebars index 1fbcd7a3..f000ea29 100644 --- a/views/login-min.handlebars +++ b/views/login-min.handlebars @@ -1 +1 @@ - {{{title}}} - Login
{{{title}}}
{{{title2}}}

Welcome


\ No newline at end of file + {{{title}}} - Login
{{{title}}}
{{{title2}}}

Welcome


\ No newline at end of file diff --git a/views/login-mobile-min.handlebars b/views/login-mobile-min.handlebars index 7b68add9..f98691f5 100644 --- a/views/login-mobile-min.handlebars +++ b/views/login-mobile-min.handlebars @@ -1 +1 @@ - MeshCentral - Login
{{{title}}}
{{{title2}}}
\ No newline at end of file + MeshCentral - Login
{{{title}}}
{{{title2}}}
\ No newline at end of file diff --git a/views/login-mobile.handlebars b/views/login-mobile.handlebars index 6db853ad..d4071235 100644 --- a/views/login-mobile.handlebars +++ b/views/login-mobile.handlebars @@ -313,15 +313,6 @@ }, function (error) { console.log('credentials-get error', error); } ); - } else if ((hardwareKeyChallenge != null) && u2fSupported()) { - // Old U2F hardware keys - window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) { - if ((currentpanel == 4) && authResponse.signatureData) { - Q('hwtokenInput').value = JSON.stringify(authResponse); - QE('tokenOkButton', true); - Q('tokenOkButton').click(); - } - }, hardwareKeyChallenge.timeoutSeconds); } } @@ -353,15 +344,6 @@ }, function (error) { console.log('credentials-get error', error); } ); - } else if ((hardwareKeyChallenge != null) && u2fSupported()) { - // Old U2F hardware keys - window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) { - if ((currentpanel == 5) && authResponse.signatureData) { - Q('resetHwtokenInput').value = JSON.stringify(authResponse); - QE('resetTokenOkButton', true); - Q('resetTokenOkButton').click(); - } - }, hardwareKeyChallenge.timeoutSeconds); } } } @@ -613,7 +595,6 @@ function haltEvent(e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; } function haltReturn(e) { if (e.keyCode == 13) { haltEvent(e); } } function validateEmail(v) { var emailReg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return emailReg.test(v); } // New version - function u2fSupported() { return (window.u2f && ((navigator.userAgent.indexOf('Chrome/') > 0) || (navigator.userAgent.indexOf('Firefox/') > 0) || (navigator.userAgent.indexOf('Opera/') > 0) || (navigator.userAgent.indexOf('Safari/') > 0))); } diff --git a/views/login.handlebars b/views/login.handlebars index b88702cf..55d75b3e 100644 --- a/views/login.handlebars +++ b/views/login.handlebars @@ -321,15 +321,6 @@ }, function (error) { console.log('credentials-get error', error); } ); - } else if ((hardwareKeyChallenge != null) && u2fSupported()) { - // Old U2F hardware keys - window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) { - if ((currentpanel == 4) && authResponse.signatureData) { - Q('hwtokenInput').value = JSON.stringify(authResponse); - QE('tokenOkButton', true); - Q('tokenOkButton').click(); - } - }, hardwareKeyChallenge.timeoutSeconds); } } @@ -361,15 +352,6 @@ }, function (error) { console.log('credentials-get error', error); } ); - } else if ((hardwareKeyChallenge != null) && u2fSupported()) { - // Old U2F hardware keys - window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) { - if ((currentpanel == 5) && authResponse.signatureData) { - Q('resetHwtokenInput').value = JSON.stringify(authResponse); - QE('resetTokenOkButton', true); - Q('resetTokenOkButton').click(); - } - }, hardwareKeyChallenge.timeoutSeconds); } } @@ -685,7 +667,6 @@ function validateEmail(v) { var emailReg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return emailReg.test(v); } // New version function putstore(name, val) { try { if (typeof (localStorage) === 'undefined') return; localStorage.setItem(name, val); } catch (e) { } } function getstore(name, val) { try { if (typeof (localStorage) === 'undefined') return val; var v = localStorage.getItem(name); if ((v == null) || (v == null)) return val; return v; } catch (e) { return val; } } - function u2fSupported() { return (window.u2f && ((navigator.userAgent.indexOf('Chrome/') > 0) || (navigator.userAgent.indexOf('Firefox/') > 0) || (navigator.userAgent.indexOf('Opera/') > 0) || (navigator.userAgent.indexOf('Safari/') > 0))); }