summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorMatthew Miller <matthew@millerti.me>2020-05-20 08:44:12 -0700
committerMatthew Miller <matthew@millerti.me>2020-05-20 08:44:12 -0700
commit2c51287bec3592ebf7d40d886c41da8fb51cbc21 (patch)
tree603ec65541ea461cbe78c198c34574238eebd10a /src
parentd9074ec54935aa2155151d2dd9dea0974f33da29 (diff)
Initialize lerna project and move code to `server`
Diffstat (limited to 'src')
-rw-r--r--src/assertion/generateAssertionCredentials.ts29
-rw-r--r--src/assertion/parseAssertionAuthData.ts28
-rw-r--r--src/assertion/verifyAssertionResponse.test.ts25
-rw-r--r--src/assertion/verifyAssertionResponse.ts90
-rw-r--r--src/attestation/generateAttestationCredentials.ts45
-rw-r--r--src/attestation/parseAttestationAuthData.ts63
-rw-r--r--src/attestation/verifications/verifyAndroidSafetyNet.ts166
-rw-r--r--src/attestation/verifications/verifyFIDOU2F.ts80
-rw-r--r--src/attestation/verifications/verifyNone.ts61
-rw-r--r--src/attestation/verifications/verifyPacked.ts210
-rw-r--r--src/attestation/verifyAttestationResponse.test.ts197
-rw-r--r--src/attestation/verifyAttestationResponse.ts72
-rw-r--r--src/helpers/asciiToBinary.ts8
-rw-r--r--src/helpers/convertASN1toPEM.ts48
-rw-r--r--src/helpers/convertCOSEtoPKCS.ts49
-rw-r--r--src/helpers/decodeAttestationObject.test.ts39
-rw-r--r--src/helpers/decodeAttestationObject.ts17
-rw-r--r--src/helpers/decodeClientDataJSON.test.ts16
-rw-r--r--src/helpers/decodeClientDataJSON.ts11
-rw-r--r--src/helpers/getCertificateInfo.ts31
-rw-r--r--src/helpers/toHash.ts10
-rw-r--r--src/helpers/validateCertificatePath.ts55
-rw-r--r--src/helpers/verifySignature.ts18
-rw-r--r--src/index.ts19
-rw-r--r--src/libTypes.ts155
-rw-r--r--src/setupTests.ts3
26 files changed, 0 insertions, 1545 deletions
diff --git a/src/assertion/generateAssertionCredentials.ts b/src/assertion/generateAssertionCredentials.ts
deleted file mode 100644
index 71f9e44..0000000
--- a/src/assertion/generateAssertionCredentials.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import base64url from 'base64url';
-
-import { AssertionCredentials } from '@libTypes';
-
-/**
- * Prepare credentials for user registration via navigator.credentials.get(...)
- *
- * @param challenge Random string the authenticator needs to sign and pass back
- * @param base64CredentialIDs Array of base64-encoded authenticator IDs registered by the user for
- * assertion
- * @param timeout How long (in ms) the user can take to complete attestation
- */
-export default function generateAssertionCredentials(
- challenge: string,
- base64CredentialIDs: string[],
- timeout: number = 60000,
-): AssertionCredentials {
- return {
- publicKey: {
- challenge: Uint8Array.from(challenge, c => c.charCodeAt(0)),
- allowCredentials: base64CredentialIDs.map(id => ({
- id: base64url.toBuffer(id),
- type: 'public-key',
- transports: ['usb', 'ble', 'nfc'],
- })),
- timeout,
- },
- };
-}
diff --git a/src/assertion/parseAssertionAuthData.ts b/src/assertion/parseAssertionAuthData.ts
deleted file mode 100644
index e6aa011..0000000
--- a/src/assertion/parseAssertionAuthData.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { ParsedAssertionAuthData } from "@libTypes";
-
-/**
- * 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/src/assertion/verifyAssertionResponse.test.ts b/src/assertion/verifyAssertionResponse.test.ts
deleted file mode 100644
index ba76943..0000000
--- a/src/assertion/verifyAssertionResponse.test.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import verifyAssertionResponse from './verifyAssertionResponse';
-
-test('', () => {
- const verification = verifyAssertionResponse(
- {
- base64AuthenticatorData: 'PdxHEOnAiLIp26idVjIguzn3Ipr_RlsKZWsa-5qK-KABAAAAhw',
- base64ClientDataJSON: 'eyJjaGFsbGVuZ2UiOiJXRzVRU21RM1oyOTROR2gyTVROUk56WnViVmhMTlZZMWMwOHRP' +
- 'V3BLVG5JIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoi' +
- 'aHR0cHM6Ly9kZXYuZG9udG5lZWRhLnB3IiwidHlwZSI6IndlYmF1dGhuLmdldCJ9',
- base64Signature: 'MEQCIHZYFY3LsKzI0T9XRwEACl7YsYZysZ2HUw3q9f7tlq3wAiBNbyBbQMNM56P6Z00tBEZ6v' +
- 'II4f9Al-p4pZw7OBpSaog',
- userHandle: null,
- },
- 'https://dev.dontneeda.pw',
- {
- base64PublicKey: 'BBMQEnZRfg4ASys9kfGUj99Xlsa028wqYJZw8xuGahPQJWN3K9D9DajLxzKlY7uf_ulA5D6gh' +
- 'UJ9hrouDX84S_I',
- base64CredentialID: 'wJZRtQbYjKlpiRnzet7yyVizdsj_oUhi11kFbKyO0hc5gIg-4xeaTC9YC9y9sfow6gO3jE' +
- 'MoONBKNX4SmSclmQ',
- counter: 134,
- },
- );
-
- expect(verification.verified).toEqual(true);
-});
diff --git a/src/assertion/verifyAssertionResponse.ts b/src/assertion/verifyAssertionResponse.ts
deleted file mode 100644
index 49cc905..0000000
--- a/src/assertion/verifyAssertionResponse.ts
+++ /dev/null
@@ -1,90 +0,0 @@
-import base64url from 'base64url';
-
-import {
- EncodedAuthenticatorAssertionResponse,
- U2F_USER_PRESENTED,
- AuthenticatorDevice,
- VerifiedAssertion,
-} from "@libTypes";
-import decodeClientDataJSON from "@helpers/decodeClientDataJSON";
-
-import parseAssertionAuthData from './parseAssertionAuthData';
-import toHash from '@helpers/toHash';
-import convertASN1toPEM from '@helpers/convertASN1toPEM';
-import verifySignature from '@helpers/verifySignature';
-
-/**
- * Verify that the user has legitimately completed the login process
- *
- * @param response Authenticator attestation response with base64-encoded values
- * @param expectedOrigin Expected URL of website attestation should have occurred on
- */
-export default function verifyAssertionResponse(
- response: EncodedAuthenticatorAssertionResponse,
- expectedOrigin: string,
- authenticator: AuthenticatorDevice,
-): VerifiedAssertion {
- const { base64AuthenticatorData, base64ClientDataJSON, base64Signature } = response;
- const clientDataJSON = decodeClientDataJSON(base64ClientDataJSON);
-
- console.debug('decodedClientDataJSON:', clientDataJSON);
-
- const { type, origin } = clientDataJSON;
-
- // Check that the origin is our site
- if (origin !== expectedOrigin) {
- console.error('client origin did not equal our origin');
- console.debug('expectedOrigin:', expectedOrigin);
- console.debug('assertion\'s origin:', origin);
- throw new Error('Assertion origin was an unexpected value');
- }
-
- // Make sure we're handling an assertion
- if (type !== 'webauthn.get') {
- console.error('type did not equal "webauthn.get"');
- console.debug('attestation\'s type:', type);
- throw new Error('Attestation type was an unexpected value');
- }
-
- const authDataBuffer = base64url.toBuffer(base64AuthenticatorData);
- const authData = parseAssertionAuthData(authDataBuffer);
- console.log('parsed authData:', authData);
-
- if (!(authData.flags & U2F_USER_PRESENTED)) {
- throw new Error('User was NOT present during authentication!');
- }
-
- const {
- rpIdHash,
- flagsBuf,
- counterBuf,
- counter,
- } = authData;
-
- const clientDataHash = toHash(base64url.toBuffer(base64ClientDataJSON));
- const signatureBase = Buffer.concat([
- rpIdHash,
- flagsBuf,
- counterBuf,
- clientDataHash,
- ]);
-
- const publicKey = convertASN1toPEM(base64url.toBuffer(authenticator.base64PublicKey));
- const signature = base64url.toBuffer(base64Signature);
-
- const toReturn = {
- verified: verifySignature(signature, signatureBase, publicKey),
- };
-
- if (toReturn.verified) {
- 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
- // used for this client. If this happens, then someone's somehow increased the counter
- // on the device without going through this site
- throw new Error(`Device's counter ${counter} isn't greater than ${authenticator.counter}!`);
- }
- }
-
- return toReturn;
-}
diff --git a/src/attestation/generateAttestationCredentials.ts b/src/attestation/generateAttestationCredentials.ts
deleted file mode 100644
index a45c21d..0000000
--- a/src/attestation/generateAttestationCredentials.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { AttestationCredentials } from '@libTypes';
-
-/**
- * Prepare credentials for user registration via navigator.credentials.create(...)
- *
- * @param serviceName Friendly user-visible website name
- * @param rpID Valid domain name (after `https://`)
- * @param challenge Random string the authenticator needs to sign and pass back
- * @param userID User's website-specific unique ID
- * @param username User's website-specific username
- * @param timeout How long (in ms) the user can take to complete attestation
- * @param attestationType Request a full ("direct") or anonymized ("indirect") attestation statement
- */
-export default function generateAttestationCredentials(
- serviceName: string,
- rpID: string,
- challenge: string,
- userID: string,
- username: string,
- timeout: number = 60000,
- attestationType: 'direct' | 'indirect' = 'direct',
-): AttestationCredentials {
- return {
- publicKey: {
- // Cryptographically random bytes to prevent replay attacks
- challenge: Uint8Array.from(challenge, c => c.charCodeAt(0)),
- // The organization registering and authenticating the user
- rp: {
- name: serviceName,
- id: rpID,
- },
- user: {
- id: Uint8Array.from(userID, c => c.charCodeAt(0)),
- name: username,
- displayName: username,
- },
- pubKeyCredParams: [{
- alg: -7,
- type: 'public-key',
- }],
- timeout,
- attestation: attestationType,
- },
- };
-}
diff --git a/src/attestation/parseAttestationAuthData.ts b/src/attestation/parseAttestationAuthData.ts
deleted file mode 100644
index 996967d..0000000
--- a/src/attestation/parseAttestationAuthData.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import { ParsedAttestationAuthData } from "@libTypes";
-
-/**
- * Make sense of the authData buffer contained in an Attestation
- */
-export default function parseAttestationAuthData(authData: Buffer): ParsedAttestationAuthData {
- console.log('parsing attestation auth data');
-
- let intBuffer = authData;
-
- const rpIdHash = intBuffer.slice(0, 32);
- intBuffer = intBuffer.slice(32);
-
- const flagsBuf = intBuffer.slice(0, 1);
- intBuffer = intBuffer.slice(1);
-
- const flagsInt = flagsBuf[0];
-
- const flags = {
- up: !!(flagsInt & 0x01),
- uv: !!(flagsInt & 0x04),
- at: !!(flagsInt & 0x40),
- ed: !!(flagsInt & 0x80),
- flagsInt,
- };
-
- console.debug('flags:', flags);
-
- const counterBuf = intBuffer.slice(0, 4);
- intBuffer = intBuffer.slice(4);
-
- const counter = counterBuf.readUInt32BE(0);
-
- let aaguid: Buffer | undefined = undefined;
- let credentialID: Buffer | undefined = undefined;
- let COSEPublicKey: Buffer | undefined = undefined;
-
- if (flags.at) {
- aaguid = intBuffer.slice(0, 16);
- intBuffer = intBuffer.slice(16);
-
- const credIDLenBuf = intBuffer.slice(0, 2);
- intBuffer = intBuffer.slice(2);
-
- const credIDLen = credIDLenBuf.readUInt16BE(0);
-
- credentialID = intBuffer.slice(0, credIDLen);
- intBuffer = intBuffer.slice(credIDLen);
-
- COSEPublicKey = intBuffer;
- }
-
- return {
- rpIdHash,
- flagsBuf,
- flags,
- counter,
- counterBuf,
- aaguid,
- credentialID,
- COSEPublicKey,
- };
-}
diff --git a/src/attestation/verifications/verifyAndroidSafetyNet.ts b/src/attestation/verifications/verifyAndroidSafetyNet.ts
deleted file mode 100644
index 874c388..0000000
--- a/src/attestation/verifications/verifyAndroidSafetyNet.ts
+++ /dev/null
@@ -1,166 +0,0 @@
-import base64url from 'base64url';
-
-import {
- AttestationObject,
- VerifiedAttestation,
- SafetyNetJWTHeader,
- SafetyNetJWTPayload,
- SafetyNetJWTSignature,
-} from "@libTypes";
-import toHash from "@helpers/toHash";
-import verifySignature from '@helpers/verifySignature';
-import convertCOSEtoPKCS from '@helpers/convertCOSEtoPKCS';
-import getCertificateInfo from '@helpers/getCertificateInfo';
-
-import parseAttestationAuthData from '../parseAttestationAuthData';
-
-
-/**
- * Verify an attestation response with fmt 'android-safetynet'
- */
-export default function verifyAttestationAndroidSafetyNet(
- attestationObject: AttestationObject,
- base64ClientDataJSON: string,
-): VerifiedAttestation {
- const { attStmt, authData, fmt } = attestationObject;
-
- if (!attStmt.response) {
- throw new Error('No response was included in attStmt by authenticator');
- }
-
- // Prepare to verify a JWT
- const jwt = attStmt.response.toString('utf8');
- const jwtParts = jwt.split('.');
-
- const HEADER: SafetyNetJWTHeader = JSON.parse(base64url.decode(jwtParts[0]));
- const PAYLOAD: SafetyNetJWTPayload = JSON.parse(base64url.decode(jwtParts[1]));
- const SIGNATURE: SafetyNetJWTSignature = jwtParts[2];
-
- console.debug('HEADER:', HEADER);
- console.debug('PAYLOAD:', PAYLOAD);
- console.debug('SIGNATURE:', SIGNATURE);
-
- /**
- * START Verify PAYLOAD
- */
- const { nonce, ctsProfileMatch } = PAYLOAD;
- const clientDataHash = toHash(base64url.toBuffer(base64ClientDataJSON));
-
- const nonceBase = Buffer.concat([
- authData,
- clientDataHash,
- ]);
- const nonceBuffer = toHash(nonceBase);
- const expectedNonce = nonceBuffer.toString('base64');
-
- if (nonce !== expectedNonce) {
- console.error('Payload nonce was not the expected value!');
- console.debug('payload nonce:', PAYLOAD.nonce);
- console.debug('expected nonce:', expectedNonce);
- throw new Error('Could not verify response payload nonce');
- }
-
- if (!ctsProfileMatch) {
- console.error('ctsProfileMatch was false!');
- console.debug('ctsProfileMatch:', ctsProfileMatch);
- throw new Error('Could not verify response payload profile');
- }
- /**
- * END Verify PAYLOAD
- */
-
- /**
- * START Verify Header
- */
- // Generate an array of certs constituting a full certificate chain
- const fullpathCert = HEADER.x5c.concat([GlobalSignRootCAR2]).map((cert) => {
- let pem = '';
- // Take a string of characters and chop them up into 64-char lines (just like a PEM cert)
- for (let i = 0; i < cert.length; i += 64) {
- pem += `${cert.slice(i, i + 64)}\n`;
- }
-
- return `-----BEGIN CERTIFICATE-----\n${pem}-----END CERTIFICATE-----`;
- });
-
- console.debug('fullpathCert:', fullpathCert);
-
- const certificate = fullpathCert[0];
-
- const commonCertInfo = getCertificateInfo(certificate);
- console.debug('commonCertInfo:', commonCertInfo);
-
- const { subject } = commonCertInfo;
-
- // TODO: Find out where this CN string is specified and if it might change
- if (subject.CN !== 'attest.android.com') {
- console.error('common name was not "attest.android.com"');
- throw new Error('Could not verify certificate common name');
- }
-
- // TODO: Re-investigate this if we decide to "use MDS or Metadata Statements"
- // validateCertificatePath(fullpathCert);
- /**
- * END Verify Header
- */
-
- /**
- * START Verify Signature
- */
- const signatureBaseBuffer = Buffer.from(`${jwtParts[0]}.${jwtParts[1]}`);
- const signatureBuffer = base64url.toBuffer(SIGNATURE);
-
- const toReturn: VerifiedAttestation = {
- verified: verifySignature(signatureBuffer, signatureBaseBuffer, certificate),
- };
- /**
- * END Verify Signature
- */
-
-
- if (toReturn.verified) {
- const authDataStruct = parseAttestationAuthData(authData);
- console.debug('authDataStruct:', authDataStruct);
- const { counter, credentialID, COSEPublicKey } = authDataStruct;
-
- if (!COSEPublicKey) {
- throw new Error('No public key was provided by authenticator');
- }
-
- if (!credentialID) {
- throw new Error('No credential ID was provided by authenticator');
- }
-
- const publicKey = convertCOSEtoPKCS(COSEPublicKey);
-
- toReturn.authenticatorInfo = {
- fmt,
- counter,
- base64PublicKey: base64url.encode(publicKey),
- base64CredentialID: base64url.encode(credentialID),
- };
- }
-
- return toReturn;
-}
-
-/**
- * This "GS Root R2" root certificate was downloaded from https://pki.goog/gsr2/GSR2.crt
- * on 08/10/2019 and then run through `base64url.encode()` to get this representation.
- *
- * The certificate is valid until Dec 15, 2021
- */
-const GlobalSignRootCAR2 = 'MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UEC' +
- 'xMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhc' +
- 'NMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEGA' +
- '1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKb' +
- 'PJA6-Lm8omUVCxKs-IVSbC9N_hHD6ErPLv4dfxn-G07IwXNb9rfF73OX4YJYJkhD10FPe-3t-c4isUoh7SqbKSaZeqKeMW' +
- 'hG8eoLrvozps6yWJQeXSpkqBy-0Hne_ig-1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjNS7SgfQx' +
- '5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ_gk' +
- 'wpRl4pazq-r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQY' +
- 'wDwYDVR0TAQH_BAUwAwEB_zAdBgNVHQ4EFgQUm-IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0c' +
- 'DovL2NybC5nbG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjANBgk' +
- 'qhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0_WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk' +
- '7mpM0sYmsL4h4hO291xNBrBVNpGP-DTKqttVCL1OmLNIG-6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavSot-3i9DAgBkcRcA' +
- 'tjOj4LaR0VknFBbVPFd5uRHg5h6h-u_N5GJG79G-dwfCMNYxdAfvDbbnvRG15RjF-Cv6pgsH_76tuIMRQyV-dTZsXjAzlA' +
- 'cmgQWpzU_qlULRuJQ_7TBj0_VLZjmmx6BEP3ojY-x1J96relc8geMJgEtslQIxq_H5COEBkEveegeGTLg';
diff --git a/src/attestation/verifications/verifyFIDOU2F.ts b/src/attestation/verifications/verifyFIDOU2F.ts
deleted file mode 100644
index 9464053..0000000
--- a/src/attestation/verifications/verifyFIDOU2F.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-import base64url from 'base64url';
-
-import { AttestationObject, VerifiedAttestation, U2F_USER_PRESENTED } from '@libTypes';
-import toHash from '@helpers/toHash';
-import convertCOSEtoPKCS from '@helpers/convertCOSEtoPKCS';
-import convertASN1toPEM from '@helpers/convertASN1toPEM';
-import verifySignature from '@helpers/verifySignature';
-
-import parseAttestationAuthData from '../parseAttestationAuthData';
-
-
-/**
- * Verify an attestation response with fmt 'fido-u2f'
- */
-export default function verifyAttestationFIDOU2F(
- attestationObject: AttestationObject,
- base64ClientDataJSON: string,
-): VerifiedAttestation {
- const { fmt, authData, attStmt } = attestationObject;
-
- const authDataStruct = parseAttestationAuthData(authData);
- const {
- flags,
- COSEPublicKey,
- rpIdHash,
- credentialID,
- counter,
- } = authDataStruct;
-
- if (!(flags.flagsInt & U2F_USER_PRESENTED)) {
- throw new Error('User was NOT present during authentication');
- }
-
- if (!COSEPublicKey) {
- throw new Error('No public key was provided by authenticator');
- }
-
- if (!credentialID) {
- throw new Error('No credential ID was provided by authenticator');
- }
-
- const clientDataHash = toHash(base64url.toBuffer(base64ClientDataJSON));
- const reservedByte = Buffer.from([0x00]);
- const publicKey = convertCOSEtoPKCS(COSEPublicKey);
-
- const signatureBase = Buffer.concat([
- reservedByte,
- rpIdHash,
- clientDataHash,
- credentialID,
- publicKey,
- ]);
-
- const { sig, x5c } = attStmt;
-
- if (!x5c) {
- throw new Error('No attestation certificate provided in attestation statement');
- }
-
- if (!sig) {
- throw new Error('No attestation signature provided in attestation statement');
- }
-
- const publicKeyCertPEM = convertASN1toPEM(x5c[0]);
-
- const toReturn: VerifiedAttestation = {
- verified: verifySignature(sig, signatureBase, publicKeyCertPEM),
- };
-
- if (toReturn.verified) {
- toReturn.authenticatorInfo = {
- fmt,
- counter,
- base64PublicKey: base64url.encode(publicKey),
- base64CredentialID: base64url.encode(credentialID),
- };
- }
-
- return toReturn;
-}
diff --git a/src/attestation/verifications/verifyNone.ts b/src/attestation/verifications/verifyNone.ts
deleted file mode 100644
index 18e9417..0000000
--- a/src/attestation/verifications/verifyNone.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import base64url from 'base64url';
-
-import { AttestationObject, VerifiedAttestation } from "@libTypes";
-import convertCOSEtoPKCS from "@helpers/convertCOSEtoPKCS";
-
-import parseAttestationAuthData from '../parseAttestationAuthData';
-
-
-/**
- * Verify an attestation response with fmt 'none'
- *
- * This is the weaker of the assertions, so there are only so many checks we can perform
- */
-export default function verifyAttestationNone(
- attestationObject: AttestationObject,
-): VerifiedAttestation {
- const { fmt, authData } = attestationObject;
- const authDataStruct = parseAttestationAuthData(authData);
-
- console.log('authDataStruct:', authDataStruct);
-
- const {
- credentialID,
- COSEPublicKey,
- counter,
- flags,
- } = authDataStruct;
-
- if (!COSEPublicKey) {
- throw new Error('No public key was provided by authenticator');
- }
-
- if (!credentialID) {
- throw new Error('No credential ID was provided by authenticator');
- }
-
- // Make sure the (U)ser (P)resent for the attestation
- if (!flags.up) {
- console.error('User was not Present for attestation');
- console.debug('attestation\'s flags:', flags);
- throw new Error('User presence could not be verified');
- }
-
- if (!flags.uv) {
- console.warn('The authenticator could not uniquely Verify the user');
- }
-
- const publicKey = convertCOSEtoPKCS(COSEPublicKey);
-
- const toReturn: VerifiedAttestation = {
- verified: true,
- authenticatorInfo: {
- fmt,
- counter,
- base64PublicKey: base64url.encode(publicKey),
- base64CredentialID: base64url.encode(credentialID),
- },
- };
-
- return toReturn;
-}
diff --git a/src/attestation/verifications/verifyPacked.ts b/src/attestation/verifications/verifyPacked.ts
deleted file mode 100644
index b63fef0..0000000
--- a/src/attestation/verifications/verifyPacked.ts
+++ /dev/null
@@ -1,210 +0,0 @@
-import base64url from 'base64url';
-import cbor from 'cbor';
-import elliptic from 'elliptic';
-import NodeRSA, { SigningSchemeHash } from 'node-rsa';
-
-import { AttestationObject, VerifiedAttestation, COSEKEYS, COSEPublicKey } from "@libTypes";
-import convertCOSEtoPKCS from "@helpers/convertCOSEtoPKCS";
-import toHash from "@helpers/toHash";
-import convertASN1toPEM from '@helpers/convertASN1toPEM';
-import getCertificateInfo from '@helpers/getCertificateInfo';
-import verifySignature from '@helpers/verifySignature';
-
-import parseAttestationAuthData from '../parseAttestationAuthData';
-
-
-/**
- * Verify an attestation response with fmt 'packed'
- */
-export default function verifyAttestationPacked(attestationObject: AttestationObject,
- base64ClientDataJSON: string,
-): VerifiedAttestation {
- const { fmt, authData, attStmt } = attestationObject;
- const { sig, x5c, ecdaaKeyId } = attStmt;
-
- const authDataStruct = parseAttestationAuthData(authData);
-
- const { COSEPublicKey, counter, credentialID } = authDataStruct;
-
- if (!COSEPublicKey) {
- throw new Error('No public key was provided by authenticator');
- }
-
- if (!credentialID) {
- throw new Error('No credential ID was provided by authenticator');
- }
-
- if (!sig) {
- throw new Error('No attestation signature provided in attestation statement');
- }
-
- const clientDataHash = toHash(base64url.toBuffer(base64ClientDataJSON));
-
- const signatureBase = Buffer.concat([
- authData,
- clientDataHash,
- ]);
-
- const toReturn: VerifiedAttestation = { verified: false };
- const publicKey = convertCOSEtoPKCS(COSEPublicKey);
-
- if (x5c) {
- console.log('FULL Attestation');
-
- const leafCert = convertASN1toPEM(x5c[0]);
- const leafCertInfo = getCertificateInfo(leafCert);
-
- const { subject, basicConstraintsCA, version } = leafCertInfo;
- const {
- OU,
- CN,
- O,
- C,
- } = subject;
-
- if (OU !== 'Authenticator Attestation') {
- throw new Error('Batch certificate OU MUST be set strictly to "Authenticator Attestation"!');
- }
-
- if (!CN) {
- throw new Error('Batch certificate CN MUST no be empty!');
- }
-
- if (!O) {
- throw new Error('Batch certificate CN MUST no be empty!');
- }
-
- if (!C || C.length !== 2) {
- throw new Error('Batch certificate C MUST be set to two character ISO 3166 code!');
- }
-
- if (basicConstraintsCA) {
- throw new Error('Batch certificate basic constraints CA MUST be false!');
- }
-
- if (version !== 3) {
- throw new Error('Batch certificate version MUST be 3(ASN1 2)!');
- }
-
- toReturn.verified = verifySignature(sig, signatureBase, leafCert);
- } else if (ecdaaKeyId) {
- throw new Error('ECDAA not supported yet');
- } else {
- console.log('SELF Attestation');
-
- const cosePublicKey: COSEPublicKey = cbor.decodeAllSync(COSEPublicKey)[0];
-
- const kty = cosePublicKey.get(COSEKEYS.kty);
- const alg = cosePublicKey.get(COSEKEYS.alg);
-
- if (!alg) {
- throw new Error('COSE public key was missing alg');
- }
-
- if (!kty) {
- throw new Error('COSE public key was missing kty');
- }
-
- const hashAlg: string = COSEALGHASH[(alg as number)];
-
- if (kty === COSEKTY.EC2) {
- console.log('EC2');
-
- const crv = cosePublicKey.get(COSEKEYS.crv);
-
- if (!crv) {
- throw new Error('COSE public key was missing kty crv');
- }
-
- const pkcsPublicKey = convertCOSEtoPKCS(cosePublicKey);
- const signatureBaseHash = toHash(signatureBase, hashAlg);
-
- const ec = new elliptic.ec(COSECRV[(crv as number)]);
- const key = ec.keyFromPublic(pkcsPublicKey);
-
- toReturn.verified = key.verify(signatureBaseHash, sig);
- } else if (kty === COSEKTY.RSA) {
- console.log('RSA');
-
- const n = cosePublicKey.get(COSEKEYS.n);
-
- if (!n) {
- throw new Error('COSE public key was missing n');
- }
-
- const signingScheme = COSERSASCHEME[alg as number];
-
- // TODO: Verify this works
- const key = new NodeRSA();
- key.setOptions({ signingScheme });
- key.importKey({
- n: (n as Buffer),
- e: 65537,
- }, 'components-public');
-
- toReturn.verified = key.verify(signatureBase, sig);
- } else if (kty === COSEKTY.OKP) {
- console.log('OKP');
-
- const x = cosePublicKey.get(COSEKEYS.x);
-
- if (!x) {
- throw new Error('COSE public key was missing x');
- }
-
- const signatureBaseHash = toHash(signatureBase, hashAlg);
-
- const key = new elliptic.eddsa('ed25519');
- key.keyFromPublic((x as Buffer));
-
- // TODO: is `publicKey` right here?
- toReturn.verified = key.verify(signatureBaseHash, sig, publicKey);
- }
- }
-
- if (toReturn.verified) {
- toReturn.authenticatorInfo = {
- fmt,
- counter,
- base64PublicKey: base64url.encode(publicKey),
- base64CredentialID: base64url.encode(credentialID),
- };
- }
-
- return toReturn;
-}
-
-enum COSEKTY {
- OKP = 1,
- EC2 = 2,
- RSA = 3,
-}
-
-const COSERSASCHEME: { [key: string]: SigningSchemeHash } = {
- '-3': 'pss-sha256',
- '-39': 'pss-sha512',
- '-38': 'pss-sha384',
- '-65535': 'pkcs1-sha1',
- '-257': 'pkcs1-sha256',
- '-258': 'pkcs1-sha384',
- '-259': 'pkcs1-sha512'
-}
-
-const COSECRV: { [key: number]: string } = {
- 1: 'p256',
- 2: 'p384',
- 3: 'p521',
-};
-
-const COSEALGHASH: { [key: string]: string } = {
- '-257': 'sha256',
- '-258': 'sha384',
- '-259': 'sha512',
- '-65535': 'sha1',
- '-39': 'sha512',
- '-38': 'sha384',
- '-37': 'sha256',
- '-7': 'sha256',
- '-8': 'sha512',
- '-36': 'sha512'
-}
diff --git a/src/attestation/verifyAttestationResponse.test.ts b/src/attestation/verifyAttestationResponse.test.ts
deleted file mode 100644
index 7b31dd0..0000000
--- a/src/attestation/verifyAttestationResponse.test.ts
+++ /dev/null
@@ -1,197 +0,0 @@
-import verifyAttestationResponse from './verifyAttestationResponse';
-
-test('should verify FIDO U2F attestation', () => {
- const verification = verifyAttestationResponse(
- {
- base64AttestationObject: 'o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEgwRgIhAK40WxA0t7py7AjEXvwGw' +
- 'TlmqlvrOks5g9lf+9zXzRiVAiEA3bv60xyXveKDOusYzniD7CDSostCet9PYK7FLdnTdZNjeDVjgVkCwTCCAr0wg' +
- 'gGloAMCAQICBCrnYmMwDQYJKoZIhvcNAQELBQAwLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhb' +
- 'CA0NTcyMDA2MzEwIBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMG4xCzAJBgNVBAYTAlNFMRIwEAYDV' +
- 'QQKDAlZdWJpY28gQUIxIjAgBgNVBAsMGUF1dGhlbnRpY2F0b3IgQXR0ZXN0YXRpb24xJzAlBgNVBAMMHll1Ymljb' +
- 'yBVMkYgRUUgU2VyaWFsIDcxOTgwNzA3NTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCoDhl5gQ9meEf8QqiVUV' +
- '4S/Ca+Oax47MhcpIW9VEhqM2RDTmd3HaL3+SnvH49q8YubSRp/1Z1uP+okMynSGnj+jbDBqMCIGCSsGAQQBgsQKA' +
- 'gQVMS4zLjYuMS40LjEuNDE0ODIuMS4xMBMGCysGAQQBguUcAgEBBAQDAgQwMCEGCysGAQQBguUcAQEEBBIEEG1Eu' +
- 'pv27C5JuTAMj+kgy3MwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAclfQPNzD4RVphJDW+A75W1MHI' +
- '3PZ5kcyYysR3Nx3iuxr1ZJtB+F7nFQweI3jL05HtFh2/4xVIgKb6Th4eVcjMecncBaCinEbOcdP1sEli9Hk2eVm1' +
- 'XB5A0faUjXAPw/+QLFCjgXG6ReZ5HVUcWkB7riLsFeJNYitiKrTDXFPLy+sNtVNutcQnFsCerDKuM81TvEAigkIb' +
- 'KCGlq8M/NvBg5j83wIxbCYiyV7mIr3RwApHieShzLdJo1S6XydgQjC+/64G5r8C+8AVvNFR3zXXCpio5C3KRIj88' +
- 'HEEIYjf6h1fdLfqeIsq+cUUqbq5T+c4nNoZUZCysTB9v5EY4akp+GhhdXRoRGF0YVjEAbElFazplpnc037DORGDZ' +
- 'NjDq86cN9vm6+APoAM20wtBAAAAAAAAAAAAAAAAAAAAAAAAAAAAQGFYevaR71ptU5YtXOSnVzPQTsGgK+gLiBKnq' +
- 'PWBmZXNRvjISqlLxiwApzlrfkTc3lEMYMatjeACCnsijOkNEGOlAQIDJiABIVggdWLG6UvGyHFw/k/bv6/k6z/LL' +
- 'gSO5KXzXw2EcUxkEX8iWCBeaVLz/cbyoKvRIg/q+q7tan0VN+i3WR0BOBCcuNP7yw==',
- base64ClientDataJSON: 'eyJjaGFsbGVuZ2UiOiJVMmQ0TjNZME0wOU1jbGRQYjFSNVpFeG5UbG95IiwiY2xpZW50' +
- 'RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9jbG92ZXIu' +
- 'bWlsbGVydGltZS5kZXY6MzAwMCIsInR5cGUiOiJ3ZWJhdXRobi5jcmVhdGUifQ==',
- },
- 'https://clover.millertime.dev:3000',
- );
-
- expect(verification.verified).toEqual(true);
- expect(verification.authenticatorInfo?.fmt).toEqual('fido-u2f');
- expect(verification.authenticatorInfo?.counter).toEqual(0);
- expect(verification.authenticatorInfo?.base64PublicKey).toEqual(
- 'BHVixulLxshxcP5P27-v5Os_yy4EjuSl818NhHFMZBF_XmlS8_3G8qCr0SIP6vqu7Wp9FTfot1kdATgQnLjT-8s',
- );
- expect(verification.authenticatorInfo?.base64CredentialID).toEqual(
- 'YVh69pHvWm1Tli1c5KdXM9BOwaAr6AuIEqeo9YGZlc1G-MhKqUvGLACnOWt-RNzeUQxgxq2N4AIKeyKM6Q0QYw',
- );
-});
-
-test('should verify Packed (EC2) attestation', () => {
- const verification = verifyAttestationResponse(
- {
- base64AttestationObject: 'o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZyZjc2lnWEcwRQIhANvrPZMUFrl_rvlgR' +
- 'qz6lCPlF6B4y885FYUCCrhrzAYXAiAb4dQKXbP3IimsTTadkwXQlrRVdxzlbmPXt847-Oh6r2hhdXRoRGF0YVjhP' +
- 'dxHEOnAiLIp26idVjIguzn3Ipr_RlsKZWsa-5qK-KBFXsOO-a3OAAI1vMYKZIsLJfHwVQMAXQGE4WNXLCDWOCa2x' +
- '8hpqk5dZy_xdc4wBd4UgCJ4M_JAHI7oJgDDVb8WUcKqRB_mzRxwCL9vdTl-ZKPXg3_-Zrt1Adgb7EnK9ivqaTOKM' +
- 'DqRrKsIObWYJaqpsSJtUKUBAgMmIAEhWCBKMVVaivqCBpqqAxMjuCo5jMeUdh3jDOC0EF4fLBNNTyJYILc7rqDDe' +
- 'X1pwCLrl3ZX7IThrtZNwKQVLQyfHiorqP-n',
- base64ClientDataJSON: 'eyJjaGFsbGVuZ2UiOiJjelpRU1dKQ2JsQlFibkpIVGxOQ2VFNWtkRVJ5VkRkVmNsWlpT' +
- 'a3M1U0UwIiwib3JpZ2luIjoiaHR0cHM6Ly9kZXYuZG9udG5lZWRhLnB3IiwidHlwZSI6IndlYmF1dGhuLmNyZWF0' +
- 'ZSJ9',
- },
- 'https://dev.dontneeda.pw'
- )
-
- expect(verification.verified).toEqual(true);
- expect(verification.authenticatorInfo?.fmt).toEqual('packed');
- expect(verification.authenticatorInfo?.counter).toEqual(1589874425);
- expect(verification.authenticatorInfo?.base64PublicKey).toEqual(
- 'BEoxVVqK-oIGmqoDEyO4KjmMx5R2HeMM4LQQXh8sE01PtzuuoMN5fWnAIuuXdlfshOGu1k3ApBUtDJ8eKiuo_6c',
- );
- expect(verification.authenticatorInfo?.base64CredentialID).toEqual(
- 'AYThY1csINY4JrbHyGmqTl1nL_F1zjAF3hSAIngz8kAcjugmAMNVvxZRwqpEH-bNHHAIv291OX5ko9eDf_5mu3U' +
- 'B2BvsScr2K-ppM4owOpGsqwg5tZglqqmxIm1Q',
- );
-});
-
-test('should verify None attestation', () => {
- const verification = verifyAttestationResponse(
- {
- base64AttestationObject: 'o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFPdxHEOnAiLIp26idVjIguzn3I' +
- 'pr_RlsKZWsa-5qK-KBFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQHSlyRHIdWleVqO24-6ix7JFWODqDWo_arvEz3Se5E' +
- 'gIFHkcVjZ4F5XDSBreIHsWRilRnKmaaqlqK3V2_4XtYs2pQECAyYgASFYID5PQTZQQg6haZFQWFzqfAOyQ_ENsMH8x' +
- 'xQ4GRiNPsqrIlggU8IVUOV8qpgk_Jh-OTaLuZL52KdX1fTht07X4DiQPow',
- base64ClientDataJSON: 'eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiYUVWalkxQlhkWHBw' +
- 'VURBd1NEQndOV2Q0YURKZmRUVmZVRU0wVG1WWloyUSIsIm9yaWdpbiI6Imh0dHBzOlwvXC9kZXYuZG9udG5lZWRhLn' +
- 'B3IiwiYW5kcm9pZFBhY2thZ2VOYW1lIjoib3JnLm1vemlsbGEuZmlyZWZveCJ9'
- },
- 'https://dev.dontneeda.pw'
- )
-
- expect(verification.verified).toEqual(true);
- expect(verification.authenticatorInfo?.fmt).toEqual('none');
- expect(verification.authenticatorInfo?.counter).toEqual(0);
- expect(verification.authenticatorInfo?.base64PublicKey).toEqual(
- 'BD5PQTZQQg6haZFQWFzqfAOyQ_ENsMH8xxQ4GRiNPsqrU8IVUOV8qpgk_Jh-OTaLuZL52KdX1fTht07X4DiQPow',
- );
- expect(verification.authenticatorInfo?.base64CredentialID).toEqual(
- 'AdKXJEch1aV5Wo7bj7qLHskVY4OoNaj9qu8TPdJ7kSAgUeRxWNngXlcNIGt4gexZGKVGcqZpqqWordXb_he1izY',
- );
-});
-
-test('should verify Android SafetyNet attestation', () => {
- const verification = verifyAttestationResponse(
- {
- base64AttestationObject: 'o2NmbXRxYW5kcm9pZC1zYWZldHluZXRnYXR0U3RtdKJjdmVyaDE3MTIyMDM3aHJlc' +
- '3BvbnNlWRS9ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbmcxWXlJNld5Sk5TVWxHYTJwRFEwSkljV2RCZDBsQ1FXZEpVV' +
- 'kpZY205T01GcFBaRkpyUWtGQlFVRkJRVkIxYm5wQlRrSm5hM0ZvYTJsSE9YY3dRa0ZSYzBaQlJFSkRUVkZ6ZDBOU' +
- 'ldVUldVVkZIUlhkS1ZsVjZSV1ZOUW5kSFFURlZSVU5vVFZaU01qbDJXako0YkVsR1VubGtXRTR3U1VaT2JHTnVXb' +
- 'kJaTWxaNlRWSk5kMFZSV1VSV1VWRkVSWGR3U0ZaR1RXZFJNRVZuVFZVNGVFMUNORmhFVkVVMFRWUkJlRTFFUVROT' +
- 'lZHc3dUbFp2V0VSVVJUVk5WRUYzVDFSQk0wMVVhekJPVm05M1lrUkZURTFCYTBkQk1WVkZRbWhOUTFaV1RYaEZla' +
- '0ZTUW1kT1ZrSkJaMVJEYTA1b1lrZHNiV0l6U25WaFYwVjRSbXBCVlVKblRsWkNRV05VUkZVeGRtUlhOVEJaVjJ4M' +
- 'VNVWmFjRnBZWTNoRmVrRlNRbWRPVmtKQmIxUkRhMlIyWWpKa2MxcFRRazFVUlUxNFIzcEJXa0puVGxaQ1FVMVVSV' +
- 'zFHTUdSSFZucGtRelZvWW0xU2VXSXliR3RNYlU1MllsUkRRMEZUU1hkRVVWbEtTMjlhU1doMlkwNUJVVVZDUWxGQ' +
- 'lJHZG5SVkJCUkVORFFWRnZRMmRuUlVKQlRtcFlhM293WlVzeFUwVTBiU3N2UnpWM1QyOHJXRWRUUlVOeWNXUnVPR' +
- 'Gh6UTNCU04yWnpNVFJtU3pCU2FETmFRMWxhVEVaSWNVSnJOa0Z0V2xaM01rczVSa2N3VHpseVVsQmxVVVJKVmxKN' +
- 'VJUTXdVWFZ1VXpsMVowaEROR1ZuT1c5MmRrOXRLMUZrV2pKd09UTllhSHAxYmxGRmFGVlhXRU40UVVSSlJVZEtTe' +
- 'k5UTW1GQlpucGxPVGxRVEZNeU9XaE1ZMUYxV1ZoSVJHRkROMDlhY1U1dWIzTnBUMGRwWm5NNGRqRnFhVFpJTDNob' +
- '2JIUkRXbVV5YkVvck4wZDFkSHBsZUV0d2VIWndSUzkwV2xObVlsazVNRFZ4VTJ4Q2FEbG1jR293TVRWamFtNVJSb' +
- 'XRWYzBGVmQyMUxWa0ZWZFdWVmVqUjBTMk5HU3pSd1pYWk9UR0Y0UlVGc0swOXJhV3hOZEVsWlJHRmpSRFZ1Wld3M' +
- 'GVFcHBlWE0wTVROb1lXZHhWekJYYUdnMVJsQXpPV2hIYXpsRkwwSjNVVlJxWVhwVGVFZGtkbGd3YlRaNFJsbG9hQ' +
- 'zh5VmsxNVdtcFVORXQ2VUVwRlEwRjNSVUZCWVU5RFFXeG5kMmRuU2xWTlFUUkhRVEZWWkVSM1JVSXZkMUZGUVhkS' +
- 'lJtOUVRVlJDWjA1V1NGTlZSVVJFUVV0Q1oyZHlRbWRGUmtKUlkwUkJWRUZOUW1kT1ZraFNUVUpCWmpoRlFXcEJRV' +
- 'TFDTUVkQk1WVmtSR2RSVjBKQ1VYRkNVWGRIVjI5S1FtRXhiMVJMY1hWd2J6UlhObmhVTm1veVJFRm1RbWRPVmtoV' +
- 'FRVVkhSRUZYWjBKVFdUQm1hSFZGVDNaUWJTdDRaMjU0YVZGSE5rUnlabEZ1T1V0NlFtdENaMmR5UW1kRlJrSlJZM' +
- 'EpCVVZKWlRVWlpkMHAzV1VsTGQxbENRbEZWU0UxQlIwZEhNbWd3WkVoQk5reDVPWFpaTTA1M1RHNUNjbUZUTlc1a' +
- 'U1qbHVUREprTUdONlJuWk5WRUZ5UW1kbmNrSm5SVVpDVVdOM1FXOVpabUZJVWpCalJHOTJURE5DY21GVE5XNWlNa' +
- 'mx1VERKa2VtTnFTWFpTTVZKVVRWVTRlRXh0VG5sa1JFRmtRbWRPVmtoU1JVVkdha0ZWWjJoS2FHUklVbXhqTTFGM' +
- 'VdWYzFhMk50T1hCYVF6VnFZakl3ZDBsUldVUldVakJuUWtKdmQwZEVRVWxDWjFwdVoxRjNRa0ZuU1hkRVFWbExTM' +
- '2RaUWtKQlNGZGxVVWxHUVhwQmRrSm5UbFpJVWpoRlMwUkJiVTFEVTJkSmNVRm5hR2cxYjJSSVVuZFBhVGgyV1ROS' +
- '2MweHVRbkpoVXpWdVlqSTVia3d3WkZWVmVrWlFUVk0xYW1OdGQzZG5aMFZGUW1kdmNrSm5SVVZCWkZvMVFXZFJRM' +
- 'EpKU0RGQ1NVaDVRVkJCUVdSM1EydDFVVzFSZEVKb1dVWkpaVGRGTmt4TldqTkJTMUJFVjFsQ1VHdGlNemRxYW1RN' +
- 'E1FOTVRVE5qUlVGQlFVRlhXbVJFTTFCTVFVRkJSVUYzUWtsTlJWbERTVkZEVTFwRFYyVk1Tblp6YVZaWE5rTm5LM' +
- 'mRxTHpsM1dWUktVbnAxTkVocGNXVTBaVmswWXk5dGVYcHFaMGxvUVV4VFlta3ZWR2g2WTNweGRHbHFNMlJyTTNaa' +
- 'VRHTkpWek5NYkRKQ01HODNOVWRSWkdoTmFXZGlRbWRCU0ZWQlZtaFJSMjFwTDFoM2RYcFVPV1ZIT1ZKTVNTdDRNR' +
- 'm95ZFdKNVdrVldla0UzTlZOWlZtUmhTakJPTUVGQlFVWnRXRkU1ZWpWQlFVRkNRVTFCVW1wQ1JVRnBRbU5EZDBFN' +
- 'WFqZE9WRWRZVURJM09IbzBhSEl2ZFVOSWFVRkdUSGx2UTNFeVN6QXJlVXhTZDBwVlltZEpaMlk0WjBocWRuQjNNb' +
- 'TFDTVVWVGFuRXlUMll6UVRCQlJVRjNRMnR1UTJGRlMwWlZlVm8zWmk5UmRFbDNSRkZaU2t0dldrbG9kbU5PUVZGR' +
- 'lRFSlJRVVJuWjBWQ1FVazVibFJtVWt0SlYyZDBiRmRzTTNkQ1REVTFSVlJXTm10aGVuTndhRmN4ZVVGak5VUjFiV' +
- 'FpZVHpReGExcDZkMG8yTVhkS2JXUlNVbFF2VlhORFNYa3hTMFYwTW1Nd1JXcG5iRzVLUTBZeVpXRjNZMFZYYkV4U' +
- 'ldUSllVRXg1Um1wclYxRk9ZbE5vUWpGcE5GY3lUbEpIZWxCb2RETnRNV0kwT1doaWMzUjFXRTAyZEZnMVEzbEZTR' +
- 'zVVYURoQ2IyMDBMMWRzUm1sb2VtaG5iamd4Ukd4a2IyZDZMMHN5VlhkTk5sTTJRMEl2VTBWNGEybFdabllyZW1KS' +
- '01ISnFkbWM1TkVGc1pHcFZabFYzYTBrNVZrNU5ha1ZRTldVNGVXUkNNMjlNYkRabmJIQkRaVVkxWkdkbVUxZzBWV' +
- 'Gw0TXpWdmFpOUpTV1F6VlVVdlpGQndZaTl4WjBkMmMydG1aR1Y2ZEcxVmRHVXZTMU50Y21sM1kyZFZWMWRsV0daV' +
- 'Vlra3plbk5wYTNkYVltdHdiVkpaUzIxcVVHMW9kalJ5YkdsNlIwTkhkRGhRYmpod2NUaE5Na3RFWmk5UU0ydFdiM' +
- '1F6WlRFNFVUMGlMQ0pOU1VsRlUycERRMEY2UzJkQmQwbENRV2RKVGtGbFR6QnRjVWRPYVhGdFFrcFhiRkYxUkVGT' +
- '1FtZHJjV2hyYVVjNWR6QkNRVkZ6UmtGRVFrMU5VMEYzU0dkWlJGWlJVVXhGZUdSSVlrYzVhVmxYZUZSaFYyUjFTV' +
- 'VpLZG1JelVXZFJNRVZuVEZOQ1UwMXFSVlJOUWtWSFFURlZSVU5vVFV0U01uaDJXVzFHYzFVeWJHNWlha1ZVVFVKR' +
- 'lIwRXhWVVZCZUUxTFVqSjRkbGx0Um5OVk1teHVZbXBCWlVaM01IaE9la0V5VFZSVmQwMUVRWGRPUkVwaFJuY3dlV' +
- 'TFVUlhsTlZGVjNUVVJCZDA1RVNtRk5SVWw0UTNwQlNrSm5UbFpDUVZsVVFXeFdWRTFTTkhkSVFWbEVWbEZSUzBWN' +
- 'FZraGlNamx1WWtkVloxWklTakZqTTFGblZUSldlV1J0YkdwYVdFMTRSWHBCVWtKblRsWkNRVTFVUTJ0a1ZWVjVRa' +
- '1JSVTBGNFZIcEZkMmRuUldsTlFUQkhRMU54UjFOSllqTkVVVVZDUVZGVlFVRTBTVUpFZDBGM1oyZEZTMEZ2U1VKQ' +
- 'lVVUlJSMDA1UmpGSmRrNHdOWHByVVU4NUszUk9NWEJKVW5aS2VucDVUMVJJVnpWRWVrVmFhRVF5WlZCRGJuWlZRV' +
- 'EJSYXpJNFJtZEpRMlpMY1VNNVJXdHpRelJVTW1aWFFsbHJMMnBEWmtNelVqTldXazFrVXk5a1RqUmFTME5GVUZwU' +
- '2NrRjZSSE5wUzFWRWVsSnliVUpDU2pWM2RXUm5lbTVrU1UxWlkweGxMMUpIUjBac05YbFBSRWxMWjJwRmRpOVRTa' +
- '2d2VlV3clpFVmhiSFJPTVRGQ2JYTkxLMlZSYlUxR0t5dEJZM2hIVG1oeU5UbHhUUzg1YVd3M01Va3laRTQ0Umtkb' +
- 'VkyUmtkM1ZoWldvMFlsaG9jREJNWTFGQ1ltcDRUV05KTjBwUU1HRk5NMVEwU1N0RWMyRjRiVXRHYzJKcWVtRlVUa' +
- '001ZFhwd1JteG5UMGxuTjNKU01qVjRiM2x1VlhoMk9IWk9iV3R4TjNwa1VFZElXR3Q0VjFrM2IwYzVhaXRLYTFKN' +
- 'VFrRkNhemRZY2twbWIzVmpRbHBGY1VaS1NsTlFhemRZUVRCTVMxY3dXVE42Tlc5Nk1rUXdZekYwU2t0M1NFRm5UV' +
- 'UpCUVVkcVoyZEZlazFKU1VKTWVrRlBRbWRPVmtoUk9FSkJaamhGUWtGTlEwRlpXWGRJVVZsRVZsSXdiRUpDV1hkR' +
- '1FWbEpTM2RaUWtKUlZVaEJkMFZIUTBOelIwRlJWVVpDZDAxRFRVSkpSMEV4VldSRmQwVkNMM2RSU1UxQldVSkJaa' +
- 'mhEUVZGQmQwaFJXVVJXVWpCUFFrSlpSVVpLYWxJclJ6UlJOamdyWWpkSFEyWkhTa0ZpYjA5ME9VTm1NSEpOUWpoS' +
- 'FFURlZaRWwzVVZsTlFtRkJSa3AyYVVJeFpHNUlRamRCWVdkaVpWZGlVMkZNWkM5alIxbFpkVTFFVlVkRFEzTkhRV' +
- 'kZWUmtKM1JVSkNRMnQzU25wQmJFSm5aM0pDWjBWR1FsRmpkMEZaV1ZwaFNGSXdZMFJ2ZGt3eU9XcGpNMEYxWTBkM' +
- 'GNFeHRaSFppTW1OMldqTk9lVTFxUVhsQ1owNVdTRkk0UlV0NlFYQk5RMlZuU21GQmFtaHBSbTlrU0ZKM1QyazRkb' +
- 'Gt6U25OTWJrSnlZVk0xYm1JeU9XNU1NbVI2WTJwSmRsb3pUbmxOYVRWcVkyMTNkMUIzV1VSV1VqQm5Ra1JuZDA1c' +
- 'VFUQkNaMXB1WjFGM1FrRm5TWGRMYWtGdlFtZG5ja0puUlVaQ1VXTkRRVkpaWTJGSVVqQmpTRTAyVEhrNWQyRXlhM' +
- '1ZhTWpsMlduazVlVnBZUW5aak1td3dZak5LTlV4NlFVNUNaMnR4YUd0cFJ6bDNNRUpCVVhOR1FVRlBRMEZSUlVGS' +
- 'GIwRXJUbTV1TnpoNU5uQlNhbVE1V0d4UlYwNWhOMGhVWjJsYUwzSXpVazVIYTIxVmJWbElVRkZ4TmxOamRHazVVR' +
- 'VZoYW5aM1VsUXlhVmRVU0ZGeU1ESm1aWE54VDNGQ1dUSkZWRlYzWjFwUksyeHNkRzlPUm5ab2MwODVkSFpDUTA5S' +
- 'llYcHdjM2RYUXpsaFNqbDRhblUwZEZkRVVVZzRUbFpWTmxsYVdpOVlkR1ZFVTBkVk9WbDZTbkZRYWxrNGNUTk5SS' +
- 'Gh5ZW0xeFpYQkNRMlkxYnpodGR5OTNTalJoTWtjMmVIcFZjalpHWWpaVU9FMWpSRTh5TWxCTVVrdzJkVE5OTkZSN' +
- 'mN6TkJNazB4YWpaaWVXdEtXV2s0ZDFkSlVtUkJka3RNVjFwMUwyRjRRbFppZWxsdGNXMTNhMjAxZWt4VFJGYzFia' +
- '2xCU21KRlRFTlJRMXAzVFVnMU5uUXlSSFp4YjJaNGN6WkNRbU5EUmtsYVZWTndlSFUyZURaMFpEQldOMU4yU2tOR' +
- 'GIzTnBjbE50U1dGMGFpODVaRk5UVmtSUmFXSmxkRGh4THpkVlN6UjJORnBWVGpnd1lYUnVXbm94ZVdjOVBTSmRmU' +
- 'S5leUp1YjI1alpTSTZJbkZyYjB4dE9XSnJUeXNyYzJoMFZITnZheXRqUW1GRmJFcEJXa1pXTUcxRlFqQTVVbWcxV' +
- 'TNKWVpGVTlJaXdpZEdsdFpYTjBZVzF3VFhNaU9qRTFOalUwTWpReU5qSTNOek1zSW1Gd2ExQmhZMnRoWjJWT1lXM' +
- 'WxJam9pWTI5dExtZHZiMmRzWlM1aGJtUnliMmxrTG1kdGN5SXNJbUZ3YTBScFoyVnpkRk5vWVRJMU5pSTZJaXR0Y' +
- '0ZKQ016RjRRemRTYUdsaWN5OWxWbUVyTDNWQ05XNTFaMVVyV0UxRFFXa3plSFZKZGpaMGIwMDlJaXdpWTNSelVIS' +
- 'nZabWxzWlUxaGRHTm9JanAwY25WbExDSmhjR3REWlhKMGFXWnBZMkYwWlVScFoyVnpkRk5vWVRJMU5pSTZXeUk0V' +
- 'URGelZ6QkZVRXBqYzJ4M04xVjZVbk5wV0V3Mk5IY3JUelV3UldRclVrSkpRM1JoZVRGbk1qUk5QU0pkTENKaVlYT' +
- 'nBZMGx1ZEdWbmNtbDBlU0k2ZEhKMVpYMC5yUW5Ib2FZVGgxTEU2VVZwaU1lZWFidDdUeWJ3dzdXZk42RzJ5R01tZ' +
- 'kVjbTFabjRWalZkenpoY1BqTS1WR052aWl1RGxyZ2VuWEViZ082V05YNlYzc0hHVjN1VGxGMlBuOUZsY3YxWmItS' +
- '2NGVHZUd29iYnY3LUp5VUZzTlhTSnhHZFRTOWxwNU5EdDFnWGJ6OVpORWhzVXI3ajBqbWNyaU9rR29PRzM4MXRSa' +
- '0Vqdk5aa0hpMkF1UDF2MWM4RXg3cEpZc09ISzJxaDlmSHFuSlAzcGowUFc3WThpcDBSTVZaNF9xZzFqc0dMMnZ0O' +
- 'G12cEJFMjg5dE1fcnROdm94TWU2aEx0Q1ZkdE9ZRjIzMWMtWVFJd2FEbnZWdDcwYW5XLUZYdUx3R1J5dWhfRlpNM' +
- '3FCSlhhcXdCNjNITk5uMmh5MFRDdHQ4RDdIMmI4MGltWkZRX1FoYXV0aERhdGFYxT3cRxDpwIiyKduonVYyILs59' +
- 'yKa_0ZbCmVrGvuaivigRQAAAAC5P9lh8uZGL7EiggAiR954AEEBDL2BKZVhBca7N3j3asDaoSrA3tJgT_E4KN25T' +
- 'hBVqBHCdffSZt9bvku7hPBcd76BzU7Y-ckXslUkD13Imbzde6UBAgMmIAEhWCCT4hId3ByJ_agRyznv1xIazx2nl' +
- 'VEGyvN7intoZr7C2CJYIKo3XB-cca9aUOLC-xhp3GfhyfTS0hjws5zL_bT_N1AL',
- base64ClientDataJSON: 'eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiWDNaV1VHOUZOREpF' +
- 'YUMxM2F6Tmlka2h0WVd0MGFWWjJSVmxETFV4M1FsZyIsIm9yaWdpbiI6Imh0dHBzOlwvXC9kZXYuZG9udG5lZWRh' +
- 'LnB3IiwiYW5kcm9pZFBhY2thZ2VOYW1lIjoiY29tLmFuZHJvaWQuY2hyb21lIn0'
- },
- 'https://dev.dontneeda.pw'
- )
-
- expect(verification.verified).toEqual(true);
- expect(verification.authenticatorInfo?.fmt).toEqual('android-safetynet');
- expect(verification.authenticatorInfo?.counter).toEqual(0);
- expect(verification.authenticatorInfo?.base64PublicKey).toEqual(
- 'BJPiEh3cHIn9qBHLOe_XEhrPHaeVUQbK83uKe2hmvsLYqjdcH5xxr1pQ4sL7GGncZ-HJ9NLSGPCznMv9tP83UAs',
- );
- expect(verification.authenticatorInfo?.base64CredentialID).toEqual(
- 'AQy9gSmVYQXGuzd492rA2qEqwN7SYE_xOCjduU4QVagRwnX30mbfW75Lu4TwXHe-gc1O2PnJF7JVJA9dyJm83Xs',
- );
-});
diff --git a/src/attestation/verifyAttestationResponse.ts b/src/attestation/verifyAttestationResponse.ts
deleted file mode 100644
index 775a150..0000000
--- a/src/attestation/verifyAttestationResponse.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import decodeAttestationObject from '@helpers/decodeAttestationObject';
-import decodeClientDataJSON from '@helpers/decodeClientDataJSON';
-import { ATTESTATION_FORMATS, EncodedAuthenticatorAttestationResponse, VerifiedAttestation } from '@libTypes';
-
-import verifyFIDOU2F from './verifications/verifyFIDOU2F';
-import verifyPacked from './verifications/verifyPacked';
-import verifyNone from './verifications/verifyNone';
-import verifyAndroidSafetynet from './verifications/verifyAndroidSafetyNet';
-
-/**
- * Verify that the user has legitimately completed the registration process
- *
- * @param response Authenticator attestation response with base64-encoded values
- * @param expectedOrigin Expected URL of website attestation should have occurred on
- */
-export default function verifyAttestationResponse(
- response: EncodedAuthenticatorAttestationResponse,
- expectedOrigin: string,
-): VerifiedAttestation {
- const { base64AttestationObject, base64ClientDataJSON } = response;
- const attestationObject = decodeAttestationObject(base64AttestationObject);
- const clientDataJSON = decodeClientDataJSON(base64ClientDataJSON);
-
- console.debug('decoded attestationObject:', attestationObject);
- console.debug('decoded clientDataJSON:', clientDataJSON);
-
- const { type, origin } = clientDataJSON;
-
- // Check that the origin is our site
- if (origin !== expectedOrigin) {
- console.error('client origin did not equal our origin');
- console.debug('Expected Origin:', expectedOrigin);
- console.debug('attestation\'s origin:', origin);
- throw new Error('Attestation origin was an unexpected value');
- }
-
- // Make sure we're handling an attestation
- if (type !== 'webauthn.create') {
- console.error('type did not equal "webauthn.create"');
- console.debug('attestation\'s type:', type);
- throw new Error('Attestation type was an unexpected value');
- }
-
- const { fmt } = attestationObject;
-
- /**
- * Verification can only be performed when attestation = 'direct'
- */
- if (fmt === ATTESTATION_FORMATS.FIDO_U2F) {
- console.log('Decoding FIDO-U2F attestation');
- return verifyFIDOU2F(attestationObject, base64ClientDataJSON);
- }
-
- if (fmt === ATTESTATION_FORMATS.PACKED) {
- console.log('Decoding Packed attestation');
- return verifyPacked(attestationObject, base64ClientDataJSON);
- }
-
- if (fmt === ATTESTATION_FORMATS.ANDROID_SAFETYNET) {
- console.log('Decoding Android Safetynet attestation');
- return verifyAndroidSafetynet(attestationObject, base64ClientDataJSON);
- }
-
- if (fmt === ATTESTATION_FORMATS.NONE) {
- console.log('Decoding None attestation');
- return verifyNone(attestationObject);
- }
-
- const reason = `Unsupported Attestation Format: ${fmt}`;
- console.error(reason);
- throw new Error(reason);
-}
diff --git a/src/helpers/asciiToBinary.ts b/src/helpers/asciiToBinary.ts
deleted file mode 100644
index b006edd..0000000
--- a/src/helpers/asciiToBinary.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * Decode a base64-encoded string to a binary string
- *
- * @param input Base64-encoded string
- */
-export default function asciiToBinary(input: string) {
- return Buffer.from(input, 'base64').toString('binary');
-}
diff --git a/src/helpers/convertASN1toPEM.ts b/src/helpers/convertASN1toPEM.ts
deleted file mode 100644
index c282e15..0000000
--- a/src/helpers/convertASN1toPEM.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * Convert binary certificate or public key to an OpenSSL-compatible PEM text format.
- *
- * @param buffer - Cert or PubKey buffer
- * @return PEM
- */
-export default function convertASN1toPEM(pkBuffer: Buffer) {
- let buffer = pkBuffer;
-
- let type;
- if (buffer.length === 65 && buffer[0] === 0x04) {
- /**
- * If needed, we encode rawpublic key to ASN structure, adding metadata:
- *
- * SEQUENCE {
- * SEQUENCE {
- * OBJECTIDENTIFIER 1.2.840.10045.2.1 (ecPublicKey)
- * OBJECTIDENTIFIER 1.2.840.10045.3.1.7 (P-256)
- * }
- * BITSTRING <raw public key>
- * }
- *
- * Luckily, to do that, we just need to prefix it with constant 26 bytes (metadata is
- * constant).
- */
- buffer = Buffer.concat([
- Buffer.from('3059301306072a8648ce3d020106082a8648ce3d030107034200', 'hex'),
- buffer,
- ]);
-
- type = 'PUBLIC KEY';
- } else {
- type = 'CERTIFICATE';
- }
-
- const b64cert = buffer.toString('base64');
-
- let PEMKey = '';
- for (let i = 0; i < Math.ceil(b64cert.length / 64); i += 1) {
- const start = 64 * i;
-
- PEMKey += `${b64cert.substr(start, 64)}\n`;
- }
-
- PEMKey = `-----BEGIN ${type}-----\n${PEMKey}-----END ${type}-----\n`;
-
- return PEMKey;
-}
diff --git a/src/helpers/convertCOSEtoPKCS.ts b/src/helpers/convertCOSEtoPKCS.ts
deleted file mode 100644
index b7784d4..0000000
--- a/src/helpers/convertCOSEtoPKCS.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import cbor from 'cbor';
-
-import { COSEKEYS, COSEPublicKey } from '@libTypes';
-
-/**
- * Takes COSE-encoded public key and converts it to PKCS key
- *
- * @param cosePublicKey COSE-encoded public key
- * @return RAW PKCS encoded public key
- */
-export default function convertCOSEtoPKCS(cosePublicKey: Buffer | COSEPublicKey) {
- /*
- +------+-------+-------+---------+----------------------------------+
- | name | key | label | type | description |
- | | type | | | |
- +------+-------+-------+---------+----------------------------------+
- | crv | 2 | -1 | int / | EC Curve identifier - Taken from |
- | | | | tstr | the COSE Curves registry |
- | | | | | |
- | x | 2 | -2 | bstr | X Coordinate |
- | | | | | |
- | y | 2 | -3 | bstr / | Y Coordinate |
- | | | | bool | |
- | | | | | |
- | d | 2 | -4 | bstr | Private key |
- +------+-------+-------+---------+----------------------------------+
- */
- let struct: COSEPublicKey;
- if (cosePublicKey instanceof Buffer) {
- struct = cbor.decodeFirstSync(cosePublicKey);
- } else {
- struct = cosePublicKey;
- }
-
- const tag = Buffer.from([0x04]);
- const x = struct.get(COSEKEYS.x);
- const y = struct.get(COSEKEYS.y);
-
-
- if (!x) {
- throw new Error('COSE public key was missing x');
- }
-
- if (!y) {
- throw new Error('COSE public key was missing y');
- }
-
- return Buffer.concat([tag, (x as Buffer), (y as Buffer)]);
-}
diff --git a/src/helpers/decodeAttestationObject.test.ts b/src/helpers/decodeAttestationObject.test.ts
deleted file mode 100644
index d36201e..0000000
--- a/src/helpers/decodeAttestationObject.test.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import decodeAttestationObject from './decodeAttestationObject';
-
-test('should decode base64-encoded indirect attestationObject', () => {
- const decoded = decodeAttestationObject(
- 'o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjEAbElFazplpnc037DORGDZNjDq86cN9vm6' +
- '+APoAM20wtBAAAAAAAAAAAAAAAAAAAAAAAAAAAAQKmPuEwByQJ3e89TccUSrCGDkNWquhevjLLn/' +
- 'KNZZaxQQ0steueoG2g12dvnUNbiso8kVJDyLa+6UiA34eniujWlAQIDJiABIVggiUk8wN2j' +
- '+3fkKI7KSiLBkKzs3FfhPZxHgHPnGLvOY/YiWCBv7+XyTqArnMVtQ947/8Xk8fnVCdLMRWJGM1VbNevVcQ=='
- );
-
- expect(decoded.fmt).toEqual('none');
- expect(decoded.attStmt).toEqual({});
- expect(decoded.authData).toBeDefined();
-});
-
-test('should decode base64-encoded direct attestationObject', () => {
- const decoded = decodeAttestationObject(
- 'o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEgwRgIhAK40WxA0t7py7AjEXvwGwTlmqlvrOk' +
- 's5g9lf+9zXzRiVAiEA3bv60xyXveKDOusYzniD7CDSostCet9PYK7FLdnTdZNjeDVjgVkCwTCCAr0wggGloAMCAQICBCrn' +
- 'YmMwDQYJKoZIhvcNAQELBQAwLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMT' +
- 'QwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMG4xCzAJBgNVBAYTAlNFMRIwEAYDVQQKDAlZdWJpY28gQUIxIjAgBgNV' +
- 'BAsMGUF1dGhlbnRpY2F0b3IgQXR0ZXN0YXRpb24xJzAlBgNVBAMMHll1YmljbyBVMkYgRUUgU2VyaWFsIDcxOTgwNzA3NT' +
- 'BZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCoDhl5gQ9meEf8QqiVUV4S/Ca+Oax47MhcpIW9VEhqM2RDTmd3HaL3+SnvH' +
- '49q8YubSRp/1Z1uP+okMynSGnj+jbDBqMCIGCSsGAQQBgsQKAgQVMS4zLjYuMS40LjEuNDE0ODIuMS4xMBMGCysGAQQBgu' +
- 'UcAgEBBAQDAgQwMCEGCysGAQQBguUcAQEEBBIEEG1Eupv27C5JuTAMj+kgy3MwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0B' +
- 'AQsFAAOCAQEAclfQPNzD4RVphJDW+A75W1MHI3PZ5kcyYysR3Nx3iuxr1ZJtB+F7nFQweI3jL05HtFh2/4xVIgKb6Th4eV' +
- 'cjMecncBaCinEbOcdP1sEli9Hk2eVm1XB5A0faUjXAPw/+QLFCjgXG6ReZ5HVUcWkB7riLsFeJNYitiKrTDXFPLy+sNtVN' +
- 'utcQnFsCerDKuM81TvEAigkIbKCGlq8M/NvBg5j83wIxbCYiyV7mIr3RwApHieShzLdJo1S6XydgQjC+/64G5r8C+8AVvN' +
- 'FR3zXXCpio5C3KRIj88HEEIYjf6h1fdLfqeIsq+cUUqbq5T+c4nNoZUZCysTB9v5EY4akp+GhhdXRoRGF0YVjEAbElFazp' +
- 'lpnc037DORGDZNjDq86cN9vm6+APoAM20wtBAAAAAAAAAAAAAAAAAAAAAAAAAAAAQGFYevaR71ptU5YtXOSnVzPQTsGgK+' +
- 'gLiBKnqPWBmZXNRvjISqlLxiwApzlrfkTc3lEMYMatjeACCnsijOkNEGOlAQIDJiABIVggdWLG6UvGyHFw/k/bv6/k6z/L' +
- 'LgSO5KXzXw2EcUxkEX8iWCBeaVLz/cbyoKvRIg/q+q7tan0VN+i3WR0BOBCcuNP7yw=='
- );
-
- expect(decoded.fmt).toEqual('fido-u2f');
- expect(decoded.attStmt.sig).toBeDefined();
- expect(decoded.attStmt.x5c).toBeDefined();
- expect(decoded.authData).toBeDefined();
-});
diff --git a/src/helpers/decodeAttestationObject.ts b/src/helpers/decodeAttestationObject.ts
deleted file mode 100644
index 224734e..0000000
--- a/src/helpers/decodeAttestationObject.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import base64url from 'base64url';
-import cbor from 'cbor';
-
-import { AttestationObject } from '@libTypes';
-
-/**
- * Convert an AttestationObject from base64 string to a proper object
- *
- * @param base64AttestationObject Base64-encoded Attestation Object
- */
-export default function decodeAttestationObject(
- base64AttestationObject: string,
-): AttestationObject {
- const toBuffer = base64url.toBuffer(base64AttestationObject);
- const toCBOR: AttestationObject = cbor.decodeAllSync(toBuffer)[0];
- return toCBOR;
-}
diff --git a/src/helpers/decodeClientDataJSON.test.ts b/src/helpers/decodeClientDataJSON.test.ts
deleted file mode 100644
index b9868f8..0000000
--- a/src/helpers/decodeClientDataJSON.test.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import decodeClientDataJSON from './decodeClientDataJSON';
-
-test('should convert base64-encoded attestation clientDataJSON to JSON', () => {
- expect(
- decodeClientDataJSON(
- 'eyJjaGFsbGVuZ2UiOiJVMmQ0TjNZME0wOU1jbGRQYjFSNVpFeG5UbG95IiwiY2xpZW50RXh0ZW5zaW9ucyI6e30' +
- 'sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9jbG92ZXIubWlsbGVydGltZS5kZX' +
- 'Y6MzAwMCIsInR5cGUiOiJ3ZWJhdXRobi5jcmVhdGUifQ=='
- )).toEqual({
- challenge: 'U2d4N3Y0M09McldPb1R5ZExnTloy',
- clientExtensions: {},
- hashAlgorithm: 'SHA-256',
- origin: 'https://clover.millertime.dev:3000',
- type: 'webauthn.create'
- });
-});
diff --git a/src/helpers/decodeClientDataJSON.ts b/src/helpers/decodeClientDataJSON.ts
deleted file mode 100644
index 7aae023..0000000
--- a/src/helpers/decodeClientDataJSON.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { ClientDataJSON } from '@libTypes';
-
-import asciiToBinary from './asciiToBinary';
-
-/**
- * Decode an authenticator's base64-encoded clientDataJSON to JSON
- */
-export default function decodeClientDataJSON(data: string): ClientDataJSON {
- const toString = asciiToBinary(data);
- return JSON.parse(toString);
-}
diff --git a/src/helpers/getCertificateInfo.ts b/src/helpers/getCertificateInfo.ts
deleted file mode 100644
index 1779bb3..0000000
--- a/src/helpers/getCertificateInfo.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import jsrsasign from 'jsrsasign';
-
-import { CertificateInfo } from '@libTypes';
-
-/**
- * Extract PEM certificate info
- *
- * @param pemCertificate Result from call to `convertASN1toPEM(x5c[0])`
- */
-export default function getCertificateInfo(pemCertificate: string): CertificateInfo {
- const subjectCert = new jsrsasign.X509();
- subjectCert.readCertPEM(pemCertificate);
-
- const subjectString = subjectCert.getSubjectString();
- const subjectParts = subjectString.slice(1).split('/');
-
- const subject: { [key: string]: string } = {};
- subjectParts.forEach((field) => {
- const [key, val] = field.split('=');
- subject[key] = val;
- });
-
- const { getVersion } = subjectCert;
- const basicConstraintsCA = !!subjectCert.getExtBasicConstraints().cA;
-
- return {
- subject,
- version: getVersion(),
- basicConstraintsCA,
- };
-}
diff --git a/src/helpers/toHash.ts b/src/helpers/toHash.ts
deleted file mode 100644
index 6e8db1d..0000000
--- a/src/helpers/toHash.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import crypto from 'crypto';
-
-/**
- * Returns hash digest of the given data using the given algorithm.
- * @param data Data to hash
- * @return The hash
- */
-export default function toHash(data: Buffer, algo: string = 'SHA256'): Buffer {
- return crypto.createHash(algo).update(data).digest();
-}
diff --git a/src/helpers/validateCertificatePath.ts b/src/helpers/validateCertificatePath.ts
deleted file mode 100644
index 685ddd8..0000000
--- a/src/helpers/validateCertificatePath.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-export default function validateCertificatePath(certificates: any[]) {
- console.log('certificates', certificates);
- return false;
- // TODO: Re-investigate this if we decide to "use MDS or Metadata Statements"
- // console.debug('validating certificate path');
-
- // const uniqueCerts = new Set(certificates);
-
- // if (uniqueCerts.size !== certificates.length) {
- // throw new Error('Certificate path could not be verified due to duplicate certificates');
- // }
-
- // certificates.forEach((subjectPEM, index) => {
- // const subjectCert = new jsrsasign.X509();
- // subjectCert.readCertPEM(subjectPEM);
-
- // let issuerPEM;
- // if (index + 1 >= certificates.length) {
- // console.debug('using subjectPEM as issuerPEM');
- // issuerPEM = subjectPEM;
- // } else {
- // console.debug('using next cert as issuerPEM');
- // issuerPEM = certificates[index + 1];
- // }
-
- // const issuerCert = new jsrsasign.X509();
- // issuerCert.readCertPEM(issuerPEM);
-
- // const subjectCertString = subjectCert.getSubjectString();
- // const issuerCertString = issuerCert.getSubjectString();
- // if (subjectCertString !== issuerCertString) {
- // console.error('subject strings didn\'t match');
- // console.debug('subjectCertString:', subjectCertString);
- // console.debug('issuerCertString:', issuerCertString);
- // throw new Error('Certificate issuers didn\'t match');
- // }
-
- // const subjectCertStruct = jsrsasign.ASN1HEX.getTLVbyList(subjectCert.hex, 0, [0]);
- // const algorithm = subjectCert.getSignatureAlgorithmField();
- // const signatureHex = subjectCert.getSignatureValueHex();
-
- // const Signature = new jsrsasign.crypto.Signature({ alg: algorithm });
- // Signature.init(issuerPEM);
- // Signature.updateHex(subjectCertStruct);
-
- // const sigVerified = Signature.verify(signatureHex);
- // if (!sigVerified) {
- // console.error('failed to validate certificate path');
- // console.debug('sigVerified:', sigVerified);
- // throw new Error('Certificate path could not be validated');
- // }
- // });
-
- // return true;
-}
diff --git a/src/helpers/verifySignature.ts b/src/helpers/verifySignature.ts
deleted file mode 100644
index c938a23..0000000
--- a/src/helpers/verifySignature.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import crypto from 'crypto';
-
-/**
- * Verify an authenticator's signature
- *
- * @param signature attStmt.sig
- * @param signatureBase Output from Buffer.concat()
- * @param publicKey Authenticator's public key as a PEM certificate
- */
-export default function verifySignature(
- signature: Buffer,
- signatureBase: Buffer,
- publicKey: string,
-): boolean {
- return crypto.createVerify('SHA256')
- .update(signatureBase)
- .verify(publicKey, signature);
-}
diff --git a/src/index.ts b/src/index.ts
deleted file mode 100644
index 356ec2e..0000000
--- a/src/index.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import generateAttestationCredentials from './attestation/generateAttestationCredentials';
-import verifyAttestationResponse from './attestation/verifyAttestationResponse';
-import generateAssertionCredentials from './assertion/generateAssertionCredentials';
-import verifyAssertionResponse from './assertion/verifyAssertionResponse';
-
-export {
- generateAssertionCredentials,
- verifyAttestationResponse,
- generateAttestationCredentials,
- verifyAssertionResponse,
-};
-
-export {
- EncodedAuthenticatorAssertionResponse,
- EncodedAuthenticatorAttestationResponse,
- VerifiedAttestation,
- VerifiedAssertion,
- AuthenticatorDevice,
-} from './libTypes';
diff --git a/src/libTypes.ts b/src/libTypes.ts
deleted file mode 100644
index 58cc90c..0000000
--- a/src/libTypes.ts
+++ /dev/null
@@ -1,155 +0,0 @@
-/**
- * An object that can be passed into navigator.credentials.create(...) in the browser
- */
-export type AttestationCredentials = {
- publicKey: PublicKeyCredentialCreationOptions,
-};
-
-/**
- * An object that can be passed into navigator.credentials.get(...) in the browser
- */
-export type AssertionCredentials = {
- publicKey: PublicKeyCredentialRequestOptions,
-};
-
-/**
- * A slightly-modified AuthenticatorAttestationResponse to simplify working with ArrayBuffers that
- * are base64-encoded in the browser so that they can be sent as JSON to the server.
- */
-export interface EncodedAuthenticatorAttestationResponse extends Omit<
-AuthenticatorAttestationResponse, 'clientDataJSON' | 'attestationObject'
-> {
- base64ClientDataJSON: string,
- base64AttestationObject: string;
-}
-
-/**
- * A slightly-modified AuthenticatorAttestationResponse to simplify working with ArrayBuffers that
- * are base64-encoded in the browser so that they can be sent as JSON to the server.
- */
-export interface EncodedAuthenticatorAssertionResponse extends Omit<
-AuthenticatorAssertionResponse, 'clientDataJSON' | 'authenticatorData' | 'signature'
-> {
- base64AuthenticatorData: string;
- base64ClientDataJSON: string;
- base64Signature: string;
-}
-
-export enum ATTESTATION_FORMATS {
- FIDO_U2F = 'fido-u2f',
- PACKED = 'packed',
- ANDROID_SAFETYNET = 'android-safetynet',
- NONE = 'none',
-}
-
-export type AttestationObject = {
- fmt: ATTESTATION_FORMATS,
- attStmt: {
- sig?: Buffer,
- x5c?: Buffer[],
- ecdaaKeyId?: Buffer,
- response?: Buffer,
- },
- authData: Buffer,
-};
-
-export type ParsedAttestationAuthData = {
- rpIdHash: Buffer,
- flagsBuf: Buffer,
- flags: {
- up: boolean,
- uv: boolean,
- at: boolean,
- ed: boolean,
- flagsInt: number,
- },
- counter: number,
- counterBuf: Buffer,
- aaguid?: Buffer,
- credentialID?: Buffer,
- COSEPublicKey?: Buffer,
-};
-
-export type ClientDataJSON = {
- type: string,
- challenge: string,
- origin: string,
-};
-
-/**
- * Result of attestation verification
- */
-export type VerifiedAttestation = {
- verified: boolean,
- authenticatorInfo?: {
- fmt: ATTESTATION_FORMATS,
- counter: number,
- base64PublicKey: string,
- base64CredentialID: string,
- },
-};
-
-/**
- * Result of assertion verification
- */
-export type VerifiedAssertion = {
- verified: boolean;
-};
-
-export type CertificateInfo = {
- subject: { [key: string]: string },
- version: number,
- basicConstraintsCA: boolean,
-};
-
-export enum COSEKEYS {
- kty = 1,
- alg = 3,
- crv = -1,
- x = -2,
- y = -3,
- n = -1,
- e = -2,
-}
-
-export type COSEPublicKey = Map<COSEAlgorithmIdentifier, number | Buffer>;
-
-export type SafetyNetJWTHeader = {
- alg: 'string',
- x5c: string[],
-};
-
-export type SafetyNetJWTPayload = {
- nonce: string,
- timestampMs: number,
- apkPackageName: string,
- apkDigestSha256: string,
- ctsProfileMatch: boolean,
- apkCertificateDigestSha256: string[],
- basicIntegrity: boolean,
-};
-
-export type SafetyNetJWTSignature = string;
-
-export type ParsedAssertionAuthData = {
- rpIdHash: Buffer,
- flagsBuf: Buffer,
- flags: number,
- counter: number,
- counterBuf: Buffer,
-};
-
-/**
- * U2F Presence constant
- */
-export const U2F_USER_PRESENTED = 0x01;
-
-/**
- * A WebAuthn-compatible device and the information needed to verify assertions by it
- */
-export type AuthenticatorDevice = {
- base64PublicKey: string,
- base64CredentialID: string,
- // Number of times this device is expected to have been used
- counter: number,
-};
diff --git a/src/setupTests.ts b/src/setupTests.ts
deleted file mode 100644
index 4cf23af..0000000
--- a/src/setupTests.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-// Silence some console output
-jest.spyOn(console, 'log').mockImplementation();
-jest.spyOn(console, 'debug').mockImplementation();