Skip to content

Commit

Permalink
Merge pull request #7 from laopisai/packed-format
Browse files Browse the repository at this point in the history
Packed format
  • Loading branch information
yackermann committed Jan 2, 2019
2 parents 31efebe + a32c36c commit 1760b0b
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 64 deletions.
2 changes: 2 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const cookieParser = require('cookie-parser');
const urllib = require('url');
const path = require('path');
const crypto = require('crypto');
const x509 = require('@fidm/x509');
const iso_3166_1 = require('iso-3166-1');

const config = require('./config.json');
const defaultroutes = require('./routes/default');
Expand Down
27 changes: 26 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
"description": "",
"main": "app.js",
"dependencies": {
"@fidm/x509": "^1.2.0",
"base64url": "^2.0.0",
"body-parser": "^1.15.2",
"cbor": "^3.0.3",
"cookie-parser": "^1.4.3",
"cookie-session": "^2.0.0-beta.3",
"express": "^4.15.4"
"express": "^4.15.4",
"iso-3166-1": "^1.1.0"
},
"repository": {
"type": "git",
Expand Down
6 changes: 0 additions & 6 deletions static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ <h3>Register page</h3>
<label for="username">username</label>
<input type="text" name="username">

<label for="password">password</label>
<input type="password" name="password">

<input class="button-primary" type="submit" value="Register">
</fieldset>
</form>
Expand All @@ -60,9 +57,6 @@ <h3>Login page</h3>
<label for="username">username</label>
<input type="text" name="username">

<label for="password">password</label>
<input type="password" name="password">

<input class="button-primary" type="submit" value="Login">
</fieldset>
</form>
Expand Down
111 changes: 55 additions & 56 deletions static/js/password.auth.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,65 @@
'use strict';
/* Handle for register form submission */
$('#register').submit(function(event) {
event.preventDefault();
// /* Handle for register form submission */
// $('#register').submit(function(event) {
// event.preventDefault();

let username = this.username.value;
let password = this.password.value;
let name = this.name.value;
// let username = this.username.value;
// let password = this.password.value;
// let name = this.name.value;

if(!username || !password || !name) {
alert('Name, username or password is missing!')
return
}
// if(!username || !password || !name) {
// alert('Name, username or password is missing!')
// return
// }

let formBody = {username, password, name};
// let formBody = {username, password, name};

fetch('/password/register', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formBody)
})
.then((response) => response.json())
.then((response) => {
if(response.status === 'ok') {
loadMainContainer()
} else {
alert(`Server responed with error. The message is: ${response.message}`);
}
})
})
// fetch('/password/register', {
// method: 'POST',
// credentials: 'include',
// headers: {
// 'Content-Type': 'application/json'
// },
// body: JSON.stringify(formBody)
// })
// .then((response) => response.json())
// .then((response) => {
// if(response.status === 'ok') {
// loadMainContainer()
// } else {
// alert(`Server responed with error. The message is: ${response.message}`);
// }
// })
// })

/* Handle for login form submission */
$('#login').submit(function(event) {
event.preventDefault();
// /* Handle for login form submission */
// $('#login').submit(function(event) {
// event.preventDefault();

let username = this.username.value;
let password = this.password.value;
// let username = this.username.value;
// let password = this.password.value;

if(!username || !password) {
alert('Username or password is missing!')
return
}
// if(!username || !password) {
// alert('Username or password is missing!')
// return
// }

let formBody = {username, password};
fetch('/password/login', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formBody)
})
.then((response) => response.json())
.then((response) => {
if(response.status === 'ok') {
loadMainContainer()
} else {
alert(`Server responed with error. The message is: ${response.message}`);
}
})
})
// let formBody = {username, password};
// fetch('/password/login', {
// method: 'POST',
// credentials: 'include',
// headers: {
// 'Content-Type': 'application/json'
// },
// body: JSON.stringify(formBody)
// })
// .then((response) => response.json())
// .then((response) => {
// if(response.status === 'ok') {
// loadMainContainer()
// } else {
// alert(`Server responed with error. The message is: ${response.message}`);
// }
// })
// })


53 changes: 53 additions & 0 deletions utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const crypto = require('crypto');
const base64url = require('base64url');
const cbor = require('cbor');
const { Certificate } = require('@fidm/x509');
const iso_3166_1 = require('iso-3166-1');

/**
* U2F Presence constant
Expand Down Expand Up @@ -222,6 +224,57 @@ let verifyAuthenticatorAttestationResponse = (webAuthnResponse) => {
credID: base64url.encode(authrDataStruct.credID)
}
}
} else if(ctapMakeCredResp.fmt === 'packed' && ctapMakeCredResp.attStmt.hasOwnProperty('x5c')) {
let authrDataStruct = parseMakeCredAuthData(ctapMakeCredResp.authData);

if(!(authrDataStruct.flags & U2F_USER_PRESENTED))
throw new Error('User was NOT presented durring authentication!');

let clientDataHash = hash(base64url.toBuffer(webAuthnResponse.response.clientDataJSON))
let publicKey = COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey)
let signatureBase = Buffer.concat([ctapMakeCredResp.authData, clientDataHash]);

let PEMCertificate = ASN1toPEM(ctapMakeCredResp.attStmt.x5c[0]);
let signature = ctapMakeCredResp.attStmt.sig;

let pem = Certificate.fromPEM(PEMCertificate);

// Getting requirements from https://www.w3.org/TR/webauthn/#packed-attestation
let aaguid_ext = pem.getExtension('1.3.6.1.4.1.45724.1.1.4')

response.verified = // Verify that sig is a valid signature over the concatenation of authenticatorData
// and clientDataHash using the attestation public key in attestnCert with the algorithm specified in alg.
verifySignature(signature, signatureBase, PEMCertificate) &&
// version must be 3 (which is indicated by an ASN.1 INTEGER with value 2)
pem.version == 3 &&
// ISO 3166 valid country
typeof iso_3166_1.whereAlpha2(pem.subject.countryName) !== 'undefined' &&
// Legal name of the Authenticator vendor (UTF8String)
pem.subject.organizationName &&
// Literal string “Authenticator Attestation” (UTF8String)
pem.subject.organizationalUnitName === 'Authenticator Attestation' &&
// A UTF8String of the vendor’s choosing
pem.subject.commonName &&
// The Basic Constraints extension MUST have the CA component set to false
!pem.extensions.isCA &&
// If attestnCert contains an extension with OID 1.3.6.1.4.1.45724.1.1.4 (id-fido-gen-ce-aaguid)
// verify that the value of this extension matches the aaguid in authenticatorData.
// The extension MUST NOT be marked as critical.
(aaguid_ext != null ?
(authrDataStruct.hasOwnProperty('aaguid') ?
!aaguid_ext.critical && aaguid_ext.value.slice(2).equals(authrDataStruct.aaguid) : false)
: true);

if(response.verified) {
response.authrInfo = {
fmt: 'fido-u2f',
publicKey: base64url.encode(publicKey),
counter: authrDataStruct.counter,
credID: base64url.encode(authrDataStruct.credID)
}
}
} else {
throw new Error('Unsupported attestation format! ' + ctapMakeCredResp.fmt);
}

return response
Expand Down

0 comments on commit 1760b0b

Please sign in to comment.