summaryrefslogtreecommitdiffhomepage
path: root/packages/server/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/server/src')
-rw-r--r--packages/server/src/assertion/parseAssertionAuthData.ts28
-rw-r--r--packages/server/src/assertion/verifyAssertionResponse.test.ts47
-rw-r--r--packages/server/src/assertion/verifyAssertionResponse.ts25
-rw-r--r--packages/server/src/attestation/verifications/verifyAndroidSafetyNet.ts28
-rw-r--r--packages/server/src/attestation/verifications/verifyFIDOU2F.ts9
-rw-r--r--packages/server/src/attestation/verifications/verifyNone.ts13
-rw-r--r--packages/server/src/attestation/verifications/verifyPacked.ts9
-rw-r--r--packages/server/src/helpers/parseAuthenticatorData.ts (renamed from packages/server/src/attestation/parseAttestationAuthData.ts)4
8 files changed, 77 insertions, 86 deletions
diff --git a/packages/server/src/assertion/parseAssertionAuthData.ts b/packages/server/src/assertion/parseAssertionAuthData.ts
deleted file mode 100644
index bdd636a..0000000
--- a/packages/server/src/assertion/parseAssertionAuthData.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { ParsedAssertionAuthData } from "@webauthntine/typescript-types";
-
-/**
- * Make sense of the authData buffer contained in an Assertion
- */
-export default function parseAssertionAuthData(authData: Buffer): ParsedAssertionAuthData {
- let intBuffer = authData;
-
- const rpIdHash = intBuffer.slice(0, 32);
- intBuffer = intBuffer.slice(32);
-
- const flagsBuf = intBuffer.slice(0, 1);
- intBuffer = intBuffer.slice(1);
-
- const flags = flagsBuf[0];
- const counterBuf = intBuffer.slice(0, 4);
- intBuffer = intBuffer.slice(4);
-
- const counter = counterBuf.readUInt32BE(0);
-
- return {
- rpIdHash,
- flagsBuf,
- flags,
- counter,
- counterBuf,
- };
-}
diff --git a/packages/server/src/assertion/verifyAssertionResponse.test.ts b/packages/server/src/assertion/verifyAssertionResponse.test.ts
index 9e5b083..99e87d2 100644
--- a/packages/server/src/assertion/verifyAssertionResponse.test.ts
+++ b/packages/server/src/assertion/verifyAssertionResponse.test.ts
@@ -1,14 +1,14 @@
import verifyAssertionResponse from './verifyAssertionResponse';
import * as decodeClientDataJSON from '../helpers/decodeClientDataJSON';
-import * as parseAssertionAuthData from './parseAssertionAuthData';
+import * as parseAuthenticatorData from '../helpers/parseAuthenticatorData';
let mockDecodeClientData: jest.SpyInstance;
let mockParseAuthData: jest.SpyInstance;
beforeEach(() => {
mockDecodeClientData = jest.spyOn(decodeClientDataJSON, 'default');
- mockParseAuthData = jest.spyOn(parseAssertionAuthData, 'default');
+ mockParseAuthData = jest.spyOn(parseAuthenticatorData, 'default');
});
afterEach(() => {
@@ -19,13 +19,24 @@ afterEach(() => {
test('should verify an assertion response', () => {
const verification = verifyAssertionResponse(
assertionResponse,
- 'https://dev.dontneeda.pw',
+ assertionOrigin,
authenticator,
);
expect(verification.verified).toEqual(true);
});
+test('should return authenticator info after verification', () => {
+ const verification = verifyAssertionResponse(
+ assertionResponse,
+ assertionOrigin,
+ authenticator,
+ );
+
+ expect(verification.authenticatorInfo.counter).toEqual(144);
+ expect(verification.authenticatorInfo.base64CredentialID).toEqual(authenticator.base64CredentialID);
+});
+
test('should throw when response origin is not expected value', () => {
expect(() => {
verifyAssertionResponse(
@@ -68,18 +79,18 @@ test('should throw error if user was not present', () => {
test('should throw error if previous counter value is not less than in response', () => {
// This'll match the `counter` value in `assertionResponse`, simulating a potential replay attack
- const badCounter = 135;
+ const badCounter = 144;
const badDevice = {
...authenticator,
counter: badCounter,
};
expect(() => {
- verifyAssertionResponse(
+ console.log(verifyAssertionResponse(
assertionResponse,
assertionOrigin,
badDevice,
- );
+ ));
}).toThrow();
});
@@ -93,19 +104,21 @@ test('should throw error if previous counter value is not less than in response'
* }
*/
const assertionResponse = {
- base64AuthenticatorData: 'PdxHEOnAiLIp26idVjIguzn3Ipr_RlsKZWsa-5qK-KABAAAAhw',
- base64ClientDataJSON: 'eyJjaGFsbGVuZ2UiOiJXRzVRU21RM1oyOTROR2gyTVROUk56WnViVmhMTlZZMWMwOHRP' +
- 'V3BLVG5JIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoi' +
- 'aHR0cHM6Ly9kZXYuZG9udG5lZWRhLnB3IiwidHlwZSI6IndlYmF1dGhuLmdldCJ9',
- base64Signature: 'MEQCIHZYFY3LsKzI0T9XRwEACl7YsYZysZ2HUw3q9f7tlq3wAiBNbyBbQMNM56P6Z00tBEZ6v' +
- 'II4f9Al-p4pZw7OBpSaog',
+ base64CredentialID: 'KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Px' +
+ 'g6jo_o0hYiew',
+ base64AuthenticatorData: 'PdxHEOnAiLIp26idVjIguzn3Ipr_RlsKZWsa-5qK-KABAAAAkA==',
+ base64ClientDataJSON: 'eyJjaGFsbGVuZ2UiOiJkRzkwWVd4c2VWVnVhWEYxWlZaaGJIVmxSWFpsY25sVWFXMWwiLCJj' +
+ 'bGllbnRFeHRlbnNpb25zIjp7fSwiaGFzaEFsZ29yaXRobSI6IlNIQS0yNTYiLCJvcmlnaW4iOiJodHRwczovL2Rldi5k' +
+ 'b250bmVlZGEucHciLCJ0eXBlIjoid2ViYXV0aG4uZ2V0In0=',
+ base64Signature: 'MEUCIQDYXBOpCWSWq2Ll4558GJKD2RoWg958lvJSB_GdeokxogIgWuEVQ7ee6AswQY0OsuQ6y8Ks6' +
+ 'jhd45bDx92wjXKs900='
};
const assertionOrigin = 'https://dev.dontneeda.pw';
const authenticator = {
- base64PublicKey: 'BBMQEnZRfg4ASys9kfGUj99Xlsa028wqYJZw8xuGahPQJWN3K9D9DajLxzKlY7uf_ulA5D6gh' +
- 'UJ9hrouDX84S_I',
- base64CredentialID: 'wJZRtQbYjKlpiRnzet7yyVizdsj_oUhi11kFbKyO0hc5gIg-4xeaTC9YC9y9sfow6gO3jE' +
- 'MoONBKNX4SmSclmQ',
- counter: 134,
+ base64PublicKey: 'BIheFp-u6GvFT2LNGovf3ZrT0iFVBsA_76rRysxRG9A18WGeA6hPmnab0HAViUYVRkwTNcN77QBf_' +
+ 'RR0dv3lIvQ',
+ base64CredentialID: 'KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Px' +
+ 'g6jo_o0hYiew',
+ counter: 0,
};
diff --git a/packages/server/src/assertion/verifyAssertionResponse.ts b/packages/server/src/assertion/verifyAssertionResponse.ts
index fb668f4..a3b631b 100644
--- a/packages/server/src/assertion/verifyAssertionResponse.ts
+++ b/packages/server/src/assertion/verifyAssertionResponse.ts
@@ -1,17 +1,16 @@
import base64url from 'base64url';
import {
AuthenticatorAssertionResponseJSON,
- U2F_USER_PRESENTED,
AuthenticatorDevice,
VerifiedAssertion,
} from "@webauthntine/typescript-types";
import decodeClientDataJSON from "@helpers/decodeClientDataJSON";
-import parseAssertionAuthData from './parseAssertionAuthData';
import toHash from '@helpers/toHash';
import convertASN1toPEM from '@helpers/convertASN1toPEM';
import verifySignature from '@helpers/verifySignature';
+import parseAuthenticatorData from '@helpers/parseAuthenticatorData';
/**
* Verify that the user has legitimately completed the login process
@@ -40,19 +39,13 @@ export default function verifyAssertionResponse(
}
const authDataBuffer = base64url.toBuffer(base64AuthenticatorData);
- const authData = parseAssertionAuthData(authDataBuffer);
+ const authDataStruct = parseAuthenticatorData(authDataBuffer);
+ const { credentialID, flags, counter } = authDataStruct;
- if (!(authData.flags & U2F_USER_PRESENTED)) {
+ if (!(flags.up)) {
throw new Error('User was NOT present during assertion!');
}
- const {
- rpIdHash,
- flagsBuf,
- counterBuf,
- counter,
- } = authData;
-
if (counter <= authenticator.counter) {
// Error out when the counter in the DB is greater than or equal to the counter in the
// dataStruct. It's related to how the authenticator maintains the number of times its been
@@ -63,6 +56,12 @@ export default function verifyAssertionResponse(
);
}
+ const {
+ rpIdHash,
+ flagsBuf,
+ counterBuf,
+ } = authDataStruct;
+
const clientDataHash = toHash(base64url.toBuffer(base64ClientDataJSON));
const signatureBase = Buffer.concat([
rpIdHash,
@@ -76,6 +75,10 @@ export default function verifyAssertionResponse(
const toReturn = {
verified: verifySignature(signature, signatureBase, publicKey),
+ authenticatorInfo: {
+ counter,
+ base64CredentialID: response.base64CredentialID,
+ },
};
return toReturn;
diff --git a/packages/server/src/attestation/verifications/verifyAndroidSafetyNet.ts b/packages/server/src/attestation/verifications/verifyAndroidSafetyNet.ts
index 6f5365a..5705065 100644
--- a/packages/server/src/attestation/verifications/verifyAndroidSafetyNet.ts
+++ b/packages/server/src/attestation/verifications/verifyAndroidSafetyNet.ts
@@ -11,8 +11,7 @@ import toHash from "@helpers/toHash";
import verifySignature from '@helpers/verifySignature';
import convertCOSEtoPKCS from '@helpers/convertCOSEtoPKCS';
import getCertificateInfo from '@helpers/getCertificateInfo';
-
-import parseAttestationAuthData from '../parseAttestationAuthData';
+import parseAuthenticatorData from '@helpers/parseAuthenticatorData';
/**
@@ -23,6 +22,20 @@ export default function verifyAttestationAndroidSafetyNet(
base64ClientDataJSON: string,
): VerifiedAttestation {
const { attStmt, authData, fmt } = attestationObject;
+ const authDataStruct = parseAuthenticatorData(authData);
+ const { counter, credentialID, COSEPublicKey, flags } = authDataStruct;
+
+ if (!flags.up) {
+ throw new Error('User was not present for attestation (None)');
+ }
+
+ if (!COSEPublicKey) {
+ throw new Error('No public key was provided by authenticator (SafetyNet)');
+ }
+
+ if (!credentialID) {
+ throw new Error('No credential ID was provided by authenticator (SafetyNet)');
+ }
if (!attStmt.response) {
throw new Error('No response was included in attStmt by authenticator (SafetyNet)');
@@ -107,19 +120,8 @@ export default function verifyAttestationAndroidSafetyNet(
if (toReturn.verified) {
- const authDataStruct = parseAttestationAuthData(authData);
- const { counter, credentialID, COSEPublicKey, flags } = authDataStruct;
-
toReturn.userVerified = flags.uv;
- if (!COSEPublicKey) {
- throw new Error('No public key was provided by authenticator (SafetyNet)');
- }
-
- if (!credentialID) {
- throw new Error('No credential ID was provided by authenticator (SafetyNet)');
- }
-
const publicKey = convertCOSEtoPKCS(COSEPublicKey);
toReturn.authenticatorInfo = {
diff --git a/packages/server/src/attestation/verifications/verifyFIDOU2F.ts b/packages/server/src/attestation/verifications/verifyFIDOU2F.ts
index 75e664f..6768abc 100644
--- a/packages/server/src/attestation/verifications/verifyFIDOU2F.ts
+++ b/packages/server/src/attestation/verifications/verifyFIDOU2F.ts
@@ -1,12 +1,11 @@
import base64url from 'base64url';
-import { AttestationObject, VerifiedAttestation, U2F_USER_PRESENTED } from '@webauthntine/typescript-types';
+import { AttestationObject, VerifiedAttestation } from '@webauthntine/typescript-types';
import toHash from '@helpers/toHash';
import convertCOSEtoPKCS from '@helpers/convertCOSEtoPKCS';
import convertASN1toPEM from '@helpers/convertASN1toPEM';
import verifySignature from '@helpers/verifySignature';
-
-import parseAttestationAuthData from '../parseAttestationAuthData';
+import parseAuthenticatorData from '@helpers/parseAuthenticatorData';
/**
@@ -18,7 +17,7 @@ export default function verifyAttestationFIDOU2F(
): VerifiedAttestation {
const { fmt, authData, attStmt } = attestationObject;
- const authDataStruct = parseAttestationAuthData(authData);
+ const authDataStruct = parseAuthenticatorData(authData);
const {
flags,
COSEPublicKey,
@@ -27,7 +26,7 @@ export default function verifyAttestationFIDOU2F(
counter,
} = authDataStruct;
- if (!(flags.flagsInt & U2F_USER_PRESENTED)) {
+ if (!(flags.up)) {
throw new Error('User was NOT present during authentication (FIDOU2F)');
}
diff --git a/packages/server/src/attestation/verifications/verifyNone.ts b/packages/server/src/attestation/verifications/verifyNone.ts
index 4f967d1..470a10a 100644
--- a/packages/server/src/attestation/verifications/verifyNone.ts
+++ b/packages/server/src/attestation/verifications/verifyNone.ts
@@ -2,8 +2,8 @@ import base64url from 'base64url';
import { AttestationObject, VerifiedAttestation } from "@webauthntine/typescript-types";
import convertCOSEtoPKCS from "@helpers/convertCOSEtoPKCS";
+import parseAuthenticatorData from '@helpers/parseAuthenticatorData';
-import parseAttestationAuthData from '../parseAttestationAuthData';
/**
@@ -15,7 +15,7 @@ export default function verifyAttestationNone(
attestationObject: AttestationObject,
): VerifiedAttestation {
const { fmt, authData } = attestationObject;
- const authDataStruct = parseAttestationAuthData(authData);
+ const authDataStruct = parseAuthenticatorData(authData);
const {
credentialID,
@@ -24,6 +24,10 @@ export default function verifyAttestationNone(
flags,
} = authDataStruct;
+ if (!flags.up) {
+ throw new Error('User was not present for attestation (None)');
+ }
+
if (!COSEPublicKey) {
throw new Error('No public key was provided by authenticator (None)');
}
@@ -32,11 +36,6 @@ export default function verifyAttestationNone(
throw new Error('No credential ID was provided by authenticator (None)');
}
- // Make sure the (U)ser (P)resent for the attestation
- if (!flags.up) {
- throw new Error('User was not present for attestation (None)');
- }
-
const publicKey = convertCOSEtoPKCS(COSEPublicKey);
const toReturn: VerifiedAttestation = {
diff --git a/packages/server/src/attestation/verifications/verifyPacked.ts b/packages/server/src/attestation/verifications/verifyPacked.ts
index 98b4e66..a40385a 100644
--- a/packages/server/src/attestation/verifications/verifyPacked.ts
+++ b/packages/server/src/attestation/verifications/verifyPacked.ts
@@ -9,8 +9,7 @@ import toHash from "@helpers/toHash";
import convertASN1toPEM from '@helpers/convertASN1toPEM';
import getCertificateInfo from '@helpers/getCertificateInfo';
import verifySignature from '@helpers/verifySignature';
-
-import parseAttestationAuthData from '../parseAttestationAuthData';
+import parseAuthenticatorData from '@helpers/parseAuthenticatorData';
/**
@@ -22,10 +21,14 @@ export default function verifyAttestationPacked(attestationObject: AttestationOb
const { fmt, authData, attStmt } = attestationObject;
const { sig, x5c, ecdaaKeyId } = attStmt;
- const authDataStruct = parseAttestationAuthData(authData);
+ const authDataStruct = parseAuthenticatorData(authData);
const { COSEPublicKey, counter, credentialID, flags } = authDataStruct;
+ if (!flags.up) {
+ throw new Error('User was not present for attestation (Packed)');
+ }
+
if (!COSEPublicKey) {
throw new Error('No public key was provided by authenticator (Packed)');
}
diff --git a/packages/server/src/attestation/parseAttestationAuthData.ts b/packages/server/src/helpers/parseAuthenticatorData.ts
index b51af5f..a3dd868 100644
--- a/packages/server/src/attestation/parseAttestationAuthData.ts
+++ b/packages/server/src/helpers/parseAuthenticatorData.ts
@@ -1,9 +1,9 @@
-import { ParsedAttestationAuthData } from "@webauthntine/typescript-types";
+import { ParsedAuthenticatorData } from "@webauthntine/typescript-types";
/**
* Make sense of the authData buffer contained in an Attestation
*/
-export default function parseAttestationAuthData(authData: Buffer): ParsedAttestationAuthData {
+export default function parseAuthenticatorData(authData: Buffer): ParsedAuthenticatorData {
let intBuffer = authData;
const rpIdHash = intBuffer.slice(0, 32);