summaryrefslogtreecommitdiffhomepage
path: root/packages/server/src/assertion
diff options
context:
space:
mode:
Diffstat (limited to 'packages/server/src/assertion')
-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
3 files changed, 44 insertions, 56 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;