summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--packages/server/src/helpers/convertPublicKeyToPEM.test.ts66
-rw-r--r--packages/server/src/helpers/convertPublicKeyToPEM.ts27
-rw-r--r--packages/server/src/helpers/decodeAttestationObject.test.ts14
-rw-r--r--packages/server/src/helpers/decodeAttestationObject.ts27
-rw-r--r--packages/server/src/metadata/verifyAttestationWithMetadata.ts8
-rw-r--r--packages/server/src/registration/verifications/tpm/verifyAttestationTPM.test.ts14
-rw-r--r--packages/server/src/registration/verifications/tpm/verifyAttestationTPM.ts18
-rw-r--r--packages/server/src/registration/verifications/verifyAttestationAndroidKey.ts4
-rw-r--r--packages/server/src/registration/verifications/verifyAttestationAndroidSafetyNet.test.ts8
-rw-r--r--packages/server/src/registration/verifications/verifyAttestationAndroidSafetyNet.ts19
-rw-r--r--packages/server/src/registration/verifications/verifyAttestationApple.ts2
-rw-r--r--packages/server/src/registration/verifications/verifyAttestationFIDOU2F.ts3
-rw-r--r--packages/server/src/registration/verifications/verifyAttestationPacked.ts5
13 files changed, 115 insertions, 100 deletions
diff --git a/packages/server/src/helpers/convertPublicKeyToPEM.test.ts b/packages/server/src/helpers/convertPublicKeyToPEM.test.ts
index 353a9eb..f7cd401 100644
--- a/packages/server/src/helpers/convertPublicKeyToPEM.test.ts
+++ b/packages/server/src/helpers/convertPublicKeyToPEM.test.ts
@@ -1,45 +1,45 @@
-import base64url from 'base64url';
-import cbor from 'cbor';
import { COSEKEYS } from './convertCOSEtoPKCS';
import { convertPublicKeyToPEM } from './convertPublicKeyToPEM';
+import * as cbor from './cbor';
+import * as uint8Array from './uint8Array';
-test('should return pem when input is base64URLString', () => {
- const mockCOSEKey = new Map<number, number | Buffer>();
+test('should return pem - EC2', () => {
+ const mockEC2Key = new Map<number, number | Uint8Array>();
- const x = Buffer.from('gh9MmXjtmcHFesofqWZ6iuxSdAYgoPVvfJqpv1818lo', 'base64');
- const y = Buffer.from('3BDZHsNvKUb5VbyGPqcAFf4FGuPhJ2Xy215oWDw_1jc', 'base64');
- mockCOSEKey.set(COSEKEYS.kty, 2);
- mockCOSEKey.set(COSEKEYS.alg, -7);
- mockCOSEKey.set(COSEKEYS.crv, 1);
- mockCOSEKey.set(COSEKEYS.x, x);
- mockCOSEKey.set(COSEKEYS.y, y);
+ const x = uint8Array.fromHex('821f4c9978ed99c1c57aca1fa9667a8aec52740620a0f56f7c9aa9bf5f35f25a');
+ const y = uint8Array.fromHex('dc10d91ec36f2946f955bc863ea70015fe051ae3e12765f2db5e68583c3fd637');
+ mockEC2Key.set(COSEKEYS.kty, 2);
+ mockEC2Key.set(COSEKEYS.alg, -7);
+ mockEC2Key.set(COSEKEYS.crv, 1);
+ mockEC2Key.set(COSEKEYS.x, x);
+ mockEC2Key.set(COSEKEYS.y, y);
- jest.spyOn(cbor, 'decodeAllSync').mockReturnValueOnce([mockCOSEKey]);
- const input = base64url.toBuffer('test');
- const actual = convertPublicKeyToPEM(input);
+ const pubKeyCBOR = cbor.encode(mockEC2Key);
+
+ const actual = convertPublicKeyToPEM(pubKeyCBOR);
expect(actual).toEqual(`-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgh9MmXjtmcHFesofqWZ6iuxSdAYg\noPVvfJqpv1818lrcENkew28pRvlVvIY+pwAV/gUa4+EnZfLbXmhYPD/WNw==
-----END PUBLIC KEY-----
`);
});
-test('should return pem when input is base64URLString', () => {
- const mockCOSEKey = new Map<number, number | Buffer>();
+test('should return pem - RSA', () => {
+ const mockRSAKey = new Map<number, number | Buffer>();
const n = Buffer.from(
'0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw',
'base64',
);
const e = Buffer.from('AQAB', 'base64');
- mockCOSEKey.set(COSEKEYS.kty, 3);
- mockCOSEKey.set(COSEKEYS.alg, -7);
- mockCOSEKey.set(COSEKEYS.crv, 1);
- mockCOSEKey.set(COSEKEYS.n, n);
- mockCOSEKey.set(COSEKEYS.e, e);
+ mockRSAKey.set(COSEKEYS.kty, 3);
+ mockRSAKey.set(COSEKEYS.alg, -7);
+ mockRSAKey.set(COSEKEYS.crv, 1);
+ mockRSAKey.set(COSEKEYS.n, n);
+ mockRSAKey.set(COSEKEYS.e, e);
+
+ const pubKeyCBOR = cbor.encode(mockRSAKey);
- jest.spyOn(cbor, 'decodeAllSync').mockReturnValueOnce([mockCOSEKey]);
- const input = base64url.toBuffer('test');
- const actual = convertPublicKeyToPEM(input);
+ const actual = convertPublicKeyToPEM(pubKeyCBOR);
expect(actual).toEqual(`-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0vx7agoebGcQSuuPiLJX
ZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tS
@@ -58,25 +58,25 @@ test('should return pem when input is base64URLString', () => {
mockCOSEKey.set(COSEKEYS.kty, 0);
mockCOSEKey.set(COSEKEYS.alg, -7);
- jest.spyOn(cbor, 'decodeAllSync').mockReturnValueOnce([mockCOSEKey]);
- const input = base64url.toBuffer('test');
+ const pubKeyCBOR = cbor.encode(mockCOSEKey);
+
try {
- convertPublicKeyToPEM(input);
+ convertPublicKeyToPEM(pubKeyCBOR);
} catch (err) {
expect((err as Error).message).toEqual('Public key was missing kty');
}
});
test('should raise error when kty is OKP (1)', () => {
- const mockCOSEKey = new Map<number, number | Buffer>();
+ const mockOKPKey = new Map<number, number | Buffer>();
- mockCOSEKey.set(COSEKEYS.kty, 1);
- mockCOSEKey.set(COSEKEYS.alg, -7);
+ mockOKPKey.set(COSEKEYS.kty, 1);
+ mockOKPKey.set(COSEKEYS.alg, -7);
+
+ const pubKeyCBOR = cbor.encode(mockOKPKey);
- jest.spyOn(cbor, 'decodeAllSync').mockReturnValueOnce([mockCOSEKey]);
- const input = base64url.toBuffer('test');
try {
- convertPublicKeyToPEM(input);
+ convertPublicKeyToPEM(pubKeyCBOR);
} catch (err) {
expect((err as Error).message).toEqual('Could not convert public key type 1 to PEM');
}
diff --git a/packages/server/src/helpers/convertPublicKeyToPEM.ts b/packages/server/src/helpers/convertPublicKeyToPEM.ts
index 40c8c5f..ff8396b 100644
--- a/packages/server/src/helpers/convertPublicKeyToPEM.ts
+++ b/packages/server/src/helpers/convertPublicKeyToPEM.ts
@@ -1,27 +1,28 @@
import jwkToPem from 'jwk-to-pem';
-import { COSEKEYS, COSEKTY, COSECRV } from './convertCOSEtoPKCS';
-import { decodeCborFirst } from './decodeCbor';
+import { COSEKEYS, COSEKTY, COSECRV, COSEPublicKey } from './convertCOSEtoPKCS';
+import * as cbor from './cbor';
+import * as base64url from './base64url';
export function convertPublicKeyToPEM(publicKey: Uint8Array): string {
let struct;
try {
- struct = decodeCborFirst(publicKey);
+ struct = cbor.decodeFirst<COSEPublicKey>(publicKey);
} catch (err) {
const _err = err as Error;
throw new Error(`Error decoding public key while converting to PEM: ${_err.message}`);
}
- const kty = struct[COSEKEYS.kty];
+ const kty = struct.get(COSEKEYS.kty);
if (!kty) {
throw new Error('Public key was missing kty');
}
if (kty === COSEKTY.EC2) {
- const crv = struct[COSEKEYS.crv];
- const x = struct[COSEKEYS.x];
- const y = struct[COSEKEYS.y];
+ const crv = struct.get(COSEKEYS.crv);
+ const x = struct.get(COSEKEYS.x);
+ const y = struct.get(COSEKEYS.y);
if (!crv) {
throw new Error('Public key was missing crv (EC2)');
@@ -39,14 +40,14 @@ export function convertPublicKeyToPEM(publicKey: Uint8Array): string {
kty: 'EC',
// Specify curve as "P-256" from "p256"
crv: COSECRV[crv as number].replace('p', 'P-'),
- x: (x as Buffer).toString('base64'),
- y: (y as Buffer).toString('base64'),
+ x: base64url.fromBuffer(x as Uint8Array, 'base64'),
+ y: base64url.fromBuffer(y as Uint8Array, 'base64'),
});
return ecPEM;
} else if (kty === COSEKTY.RSA) {
- const n = struct[COSEKEYS.n];
- const e = struct[COSEKEYS.e];
+ const n = struct.get(COSEKEYS.n);
+ const e = struct.get(COSEKEYS.e);
if (!n) {
throw new Error('Public key was missing n (RSA)');
@@ -58,8 +59,8 @@ export function convertPublicKeyToPEM(publicKey: Uint8Array): string {
const rsaPEM = jwkToPem({
kty: 'RSA',
- n: (n as Buffer).toString('base64'),
- e: (e as Buffer).toString('base64'),
+ n: base64url.fromBuffer(n as Uint8Array, 'base64'),
+ e: base64url.fromBuffer(e as Uint8Array, 'base64'),
});
return rsaPEM;
diff --git a/packages/server/src/helpers/decodeAttestationObject.test.ts b/packages/server/src/helpers/decodeAttestationObject.test.ts
index 1ba6bd0..b37d137 100644
--- a/packages/server/src/helpers/decodeAttestationObject.test.ts
+++ b/packages/server/src/helpers/decodeAttestationObject.test.ts
@@ -11,9 +11,9 @@ test('should decode base64url-encoded indirect attestationObject', () => {
),
);
- expect(decoded.fmt).toEqual('none');
- expect(decoded.attStmt).toEqual({});
- expect(decoded.authData).toBeDefined();
+ expect(decoded.get('fmt')).toEqual('none');
+ expect(decoded.get('attStmt')).toEqual(new Map());
+ expect(decoded.get('authData')).toBeDefined();
});
test('should decode base64url-encoded direct attestationObject', () => {
@@ -38,8 +38,8 @@ test('should decode base64url-encoded direct attestationObject', () => {
),
);
- expect(decoded.fmt).toEqual('fido-u2f');
- expect(decoded.attStmt.sig).toBeDefined();
- expect(decoded.attStmt.x5c).toBeDefined();
- expect(decoded.authData).toBeDefined();
+ expect(decoded.get('fmt')).toEqual('fido-u2f');
+ expect(decoded.get('attStmt').get('sig')).toBeDefined();
+ expect(decoded.get('attStmt').get('x5c')).toBeDefined();
+ expect(decoded.get('authData')).toBeDefined();
});
diff --git a/packages/server/src/helpers/decodeAttestationObject.ts b/packages/server/src/helpers/decodeAttestationObject.ts
index bab6ced..9d08e33 100644
--- a/packages/server/src/helpers/decodeAttestationObject.ts
+++ b/packages/server/src/helpers/decodeAttestationObject.ts
@@ -1,13 +1,12 @@
-import { decodeCborFirst } from './decodeCbor';
+import * as cbor from './cbor';
/**
* Convert an AttestationObject buffer to a proper object
*
* @param base64AttestationObject Attestation Object buffer
*/
-export function decodeAttestationObject(attestationObject: Buffer): AttestationObject {
- const toCBOR: AttestationObject = decodeCborFirst(attestationObject);
- return toCBOR;
+export function decodeAttestationObject(attestationObject: Uint8Array): AttestationObject {
+ return cbor.decodeFirst<AttestationObject>(attestationObject);
}
export type AttestationFormat =
@@ -20,17 +19,17 @@ export type AttestationFormat =
| 'none';
export type AttestationObject = {
- fmt: AttestationFormat;
- attStmt: AttestationStatement;
- authData: Buffer;
+ get(key: 'fmt'): AttestationFormat;
+ get(key: 'attStmt'): AttestationStatement;
+ get(key: 'authData'): Uint8Array;
};
export type AttestationStatement = {
- sig?: Buffer;
- x5c?: Buffer[];
- response?: Buffer;
- alg?: number;
- ver?: string;
- certInfo?: Buffer;
- pubArea?: Buffer;
+ get(key: 'sig'): Uint8Array | undefined;
+ get(key: 'x5c'): Uint8Array[] | undefined;
+ get(key: 'response'): Uint8Array | undefined;
+ get(key: 'alg'): number | undefined;
+ get(key: 'ver'): string | undefined;
+ get(key: 'certInfo'): Uint8Array | undefined;
+ get(key: 'pubArea'): Uint8Array | undefined;
};
diff --git a/packages/server/src/metadata/verifyAttestationWithMetadata.ts b/packages/server/src/metadata/verifyAttestationWithMetadata.ts
index 08cd8fe..aa45809 100644
--- a/packages/server/src/metadata/verifyAttestationWithMetadata.ts
+++ b/packages/server/src/metadata/verifyAttestationWithMetadata.ts
@@ -18,7 +18,7 @@ export async function verifyAttestationWithMetadata({
}: {
statement: MetadataStatement;
credentialPublicKey: Uint8Array;
- x5c: Buffer[] | Base64URLString[];
+ x5c: Uint8Array[] | Base64URLString[];
attestationStatementAlg?: number;
}): Promise<boolean> {
const {
@@ -43,9 +43,9 @@ export async function verifyAttestationWithMetadata({
const decodedPublicKey = decodeCredentialPublicKey(credentialPublicKey);
// Assume everything is a number because these values should be
const publicKeyCOSEInfo: COSEInfo = {
- kty: decodedPublicKey[COSEKEYS.kty] as number,
- alg: decodedPublicKey[COSEKEYS.alg] as number,
- crv: decodedPublicKey[COSEKEYS.crv] as number,
+ kty: decodedPublicKey.get(COSEKEYS.kty) as number,
+ alg: decodedPublicKey.get(COSEKEYS.alg) as number,
+ crv: decodedPublicKey.get(COSEKEYS.crv) as number,
};
if (!publicKeyCOSEInfo.crv) {
delete publicKeyCOSEInfo.crv;
diff --git a/packages/server/src/registration/verifications/tpm/verifyAttestationTPM.test.ts b/packages/server/src/registration/verifications/tpm/verifyAttestationTPM.test.ts
index 5652640..b69f24c 100644
--- a/packages/server/src/registration/verifications/tpm/verifyAttestationTPM.test.ts
+++ b/packages/server/src/registration/verifications/tpm/verifyAttestationTPM.test.ts
@@ -1,9 +1,9 @@
+import * as base64url from '../../../helpers/base64url';
import { verifyRegistrationResponse } from '../../verifyRegistrationResponse';
-import base64url from 'base64url';
test('should verify TPM response', async () => {
const expectedChallenge = 'a4de0d36-057d-4e9d-831a-2c578fa89170';
- jest.spyOn(base64url, 'encode').mockReturnValueOnce(expectedChallenge);
+ jest.spyOn(base64url, 'fromString').mockReturnValueOnce(expectedChallenge);
const verification = await verifyRegistrationResponse({
credential: {
id: 'SErwRhxIzjPowcnM3e-D-u89EQXLUe1NYewpshd7Mc0',
@@ -33,7 +33,7 @@ test('should verify SHA1 TPM response', async () => {
*/
const expectedChallenge =
'9JyUfJkg8PqoKZuD7FHzOE9dbyculC9urGTpGqBnEwnhKmni4rGRXxm3-ZBHK8x6riJQqIpC8qEa-T0qIFTKTQ';
- jest.spyOn(base64url, 'encode').mockReturnValueOnce(expectedChallenge);
+ jest.spyOn(base64url, 'fromString').mockReturnValueOnce(expectedChallenge);
const verification = await verifyRegistrationResponse({
credential: {
rawId: 'UJDoUJoGiDQF_EEZ3G_z9Lfq16_KFaXtMTjwTUrrRlc',
@@ -63,7 +63,7 @@ test('should verify SHA256 TPM response', async () => {
*/
const expectedChallenge =
'gHrAk4pNe2VlB0HLeKclI2P6QEa83PuGeijTHMtpbhY9KlybyhlwF_VzRe7yhabXagWuY6rkDWfvvhNqgh2o7A';
- jest.spyOn(base64url, 'encode').mockReturnValueOnce(expectedChallenge);
+ jest.spyOn(base64url, 'fromString').mockReturnValueOnce(expectedChallenge);
const verification = await verifyRegistrationResponse({
credential: {
rawId: 'h9XMhkVePN1Prq9Ks_VfwIsVZvt-jmSRTEnevTc-KB8',
@@ -100,7 +100,7 @@ test('should verify TPM response with spec-compliant tcgAtTpm SAN structure', as
* ]
*/
const expectedChallenge = 'VfmZXKDxqdoXFMHXO3SE2Q2b8u5Ki64OL_XICELcGKg';
- jest.spyOn(base64url, 'encode').mockReturnValueOnce(expectedChallenge);
+ jest.spyOn(base64url, 'fromString').mockReturnValueOnce(expectedChallenge);
const verification = await verifyRegistrationResponse({
credential: {
id: 'LVwzXx0fStkvsos_jdl9DTd6O3-6be8Ua4tcdXc5XeM',
@@ -133,7 +133,7 @@ test('should verify TPM response with non-spec-compliant tcgAtTpm SAN structure'
* ]
*/
const expectedChallenge = '4STWgmXrgJxzigqe6nFuIg';
- jest.spyOn(base64url, 'encode').mockReturnValueOnce(expectedChallenge);
+ jest.spyOn(base64url, 'fromString').mockReturnValueOnce(expectedChallenge);
const verification = await verifyRegistrationResponse({
credential: {
id: 'X7TPi7o8WfiIz1bP0Vciz1xRvSMyiitgOR1sUqY724s',
@@ -157,7 +157,7 @@ test('should verify TPM response with non-spec-compliant tcgAtTpm SAN structure'
test('should verify TPM response with ECC public area type', async () => {
const expectedChallenge = 'uzn9u0Tx-LBdtGgERsbkHRBjiUt5i2rvm2BBTZrWqEo';
- jest.spyOn(base64url, 'encode').mockReturnValueOnce(expectedChallenge);
+ jest.spyOn(base64url, 'fromString').mockReturnValueOnce(expectedChallenge);
const verification = await verifyRegistrationResponse({
credential: {
'id': 'hsS2ywFz_LWf9-lC35vC9uJTVD3ZCVdweZvESUbjXnQ',
diff --git a/packages/server/src/registration/verifications/tpm/verifyAttestationTPM.ts b/packages/server/src/registration/verifications/tpm/verifyAttestationTPM.ts
index c1c4306..efc2526 100644
--- a/packages/server/src/registration/verifications/tpm/verifyAttestationTPM.ts
+++ b/packages/server/src/registration/verifications/tpm/verifyAttestationTPM.ts
@@ -17,6 +17,7 @@ import { convertCertBufferToPEM } from '../../../helpers/convertCertBufferToPEM'
import { validateCertificatePath } from '../../../helpers/validateCertificatePath';
import { getCertificateInfo } from '../../../helpers/getCertificateInfo';
import { verifySignature } from '../../../helpers/verifySignature';
+import * as uint8Array from '../../../helpers/uint8Array';
import { MetadataService } from '../../../services/metadataService';
import { verifyAttestationWithMetadata } from '../../../metadata/verifyAttestationWithMetadata';
@@ -27,7 +28,12 @@ import { parsePubArea } from './parsePubArea';
export async function verifyAttestationTPM(options: AttestationFormatVerifierOpts): Promise<boolean> {
const { aaguid, attStmt, authData, credentialPublicKey, clientDataHash, rootCertificates } =
options;
- const { ver, sig, alg, x5c, pubArea, certInfo } = attStmt;
+ const ver = attStmt.get('ver');
+ const sig = attStmt.get('sig');
+ const alg = attStmt.get('alg');
+ const x5c = attStmt.get('x5c');
+ const pubArea = attStmt.get('pubArea');
+ const certInfo = attStmt.get('certInfo');
/**
* Verify structures
@@ -64,8 +70,8 @@ export async function verifyAttestationTPM(options: AttestationFormatVerifierOpt
const cosePublicKey = decodeCredentialPublicKey(credentialPublicKey);
if (pubType === 'TPM_ALG_RSA') {
- const n = cosePublicKey[COSEKEYS.n];
- const e = cosePublicKey[COSEKEYS.e];
+ const n = cosePublicKey.get(COSEKEYS.n);
+ const e = cosePublicKey.get(COSEKEYS.e);
if (!n) {
throw new Error('COSE public key missing n (TPM|RSA)');
@@ -93,9 +99,9 @@ export async function verifyAttestationTPM(options: AttestationFormatVerifierOpt
throw new Error(`Unexpected public key exp ${eSum}, expected ${pubAreaExponent} (TPM|RSA)`);
}
} else if (pubType === 'TPM_ALG_ECC') {
- const crv = cosePublicKey[COSEKEYS.crv];
- const x = cosePublicKey[COSEKEYS.x];
- const y = cosePublicKey[COSEKEYS.y];
+ const crv = cosePublicKey.get(COSEKEYS.crv);
+ const x = cosePublicKey.get(COSEKEYS.x);
+ const y = cosePublicKey.get(COSEKEYS.y);
if (!crv) {
throw new Error('COSE public key missing crv (TPM|ECC)');
diff --git a/packages/server/src/registration/verifications/verifyAttestationAndroidKey.ts b/packages/server/src/registration/verifications/verifyAttestationAndroidKey.ts
index 27bef78..e7ae6de 100644
--- a/packages/server/src/registration/verifications/verifyAttestationAndroidKey.ts
+++ b/packages/server/src/registration/verifications/verifyAttestationAndroidKey.ts
@@ -20,7 +20,9 @@ export async function verifyAttestationAndroidKey(
): Promise<boolean> {
const { authData, clientDataHash, attStmt, credentialPublicKey, aaguid, rootCertificates } =
options;
- const { x5c, sig, alg } = attStmt;
+ const x5c = attStmt.get('x5c');
+ const sig = attStmt.get('sig');
+ const alg = attStmt.get('alg');
if (!x5c) {
throw new Error('No attestation certificate provided in attestation statement (AndroidKey)');
diff --git a/packages/server/src/registration/verifications/verifyAttestationAndroidSafetyNet.test.ts b/packages/server/src/registration/verifications/verifyAttestationAndroidSafetyNet.test.ts
index 51b0f22..944593f 100644
--- a/packages/server/src/registration/verifications/verifyAttestationAndroidSafetyNet.test.ts
+++ b/packages/server/src/registration/verifications/verifyAttestationAndroidSafetyNet.test.ts
@@ -26,8 +26,8 @@ beforeEach(() => {
const { attestationObject, clientDataJSON } = attestationAndroidSafetyNet.response;
const decodedAttestationObject = decodeAttestationObject(base64url.toBuffer(attestationObject));
- authData = decodedAttestationObject.authData;
- attStmt = decodedAttestationObject.attStmt;
+ authData = decodedAttestationObject.get('authData');
+ attStmt = decodedAttestationObject.get('attStmt');
clientDataHash = toHash(base64url.toBuffer(clientDataJSON));
const parsedAuthData = parseAuthenticatorData(authData);
@@ -89,8 +89,8 @@ test('should validate response with cert path completed with GlobalSign R1 root
const { attestationObject, clientDataJSON } = safetyNetUsingGSR1RootCert.response;
const decodedAttestationObject = decodeAttestationObject(base64url.toBuffer(attestationObject));
- const _authData = decodedAttestationObject.authData;
- const _attStmt = decodedAttestationObject.attStmt;
+ const _authData = decodedAttestationObject.get('authData');
+ const _attStmt = decodedAttestationObject.get('attStmt');
const _clientDataHash = toHash(base64url.toBuffer(clientDataJSON));
const parsedAuthData = parseAuthenticatorData(_authData);
diff --git a/packages/server/src/registration/verifications/verifyAttestationAndroidSafetyNet.ts b/packages/server/src/registration/verifications/verifyAttestationAndroidSafetyNet.ts
index c32ca5e..45fb99b 100644
--- a/packages/server/src/registration/verifications/verifyAttestationAndroidSafetyNet.ts
+++ b/packages/server/src/registration/verifications/verifyAttestationAndroidSafetyNet.ts
@@ -25,7 +25,9 @@ export async function verifyAttestationAndroidSafetyNet(
verifyTimestampMS = true,
credentialPublicKey,
} = options;
- const { alg, response, ver } = attStmt;
+ const alg = attStmt.get('alg');
+ const response = attStmt.get('response');
+ const ver = attStmt.get('ver');
if (!ver) {
throw new Error('No ver value in attestation (SafetyNet)');
@@ -36,11 +38,11 @@ export async function verifyAttestationAndroidSafetyNet(
}
// Prepare to verify a JWT
- const jwt = response.toString('utf8');
+ const jwt = uint8Array.toUTF8String(response);
const jwtParts = jwt.split('.');
- const HEADER: SafetyNetJWTHeader = JSON.parse(base64url.decode(jwtParts[0]));
- const PAYLOAD: SafetyNetJWTPayload = JSON.parse(base64url.decode(jwtParts[1]));
+ const HEADER: SafetyNetJWTHeader = JSON.parse(base64url.toString(jwtParts[0]));
+ const PAYLOAD: SafetyNetJWTPayload = JSON.parse(base64url.toString(jwtParts[1]));
const SIGNATURE: SafetyNetJWTSignature = jwtParts[2];
/**
@@ -63,9 +65,9 @@ export async function verifyAttestationAndroidSafetyNet(
}
}
- const nonceBase = Buffer.concat([authData, clientDataHash]);
+ const nonceBase = uint8Array.concat([authData, clientDataHash]);
const nonceBuffer = toHash(nonceBase);
- const expectedNonce = uint8Array.toBase64(nonceBuffer);
+ const expectedNonce = base64url.fromBuffer(nonceBuffer, 'base64');
if (nonce !== expectedNonce) {
throw new Error('Could not verify payload nonce (SafetyNet)');
@@ -81,7 +83,8 @@ export async function verifyAttestationAndroidSafetyNet(
/**
* START Verify Header
*/
- const leafCertBuffer = base64url.toBuffer(HEADER.x5c[0]);
+ // `HEADER.x5c[0]` is definitely a base64 string
+ const leafCertBuffer = base64url.toBuffer(HEADER.x5c[0], 'base64');
const leafCertInfo = getCertificateInfo(leafCertBuffer);
const { subject } = leafCertInfo;
@@ -121,7 +124,7 @@ export async function verifyAttestationAndroidSafetyNet(
/**
* START Verify Signature
*/
- const signatureBaseBuffer = Buffer.from(`${jwtParts[0]}.${jwtParts[1]}`);
+ const signatureBaseBuffer = uint8Array.fromUTF8String(`${jwtParts[0]}.${jwtParts[1]}`);
const signatureBuffer = base64url.toBuffer(SIGNATURE);
const verified = await verifySignature({
diff --git a/packages/server/src/registration/verifications/verifyAttestationApple.ts b/packages/server/src/registration/verifications/verifyAttestationApple.ts
index 928f6c6..b53c94b 100644
--- a/packages/server/src/registration/verifications/verifyAttestationApple.ts
+++ b/packages/server/src/registration/verifications/verifyAttestationApple.ts
@@ -13,7 +13,7 @@ export async function verifyAttestationApple(
options: AttestationFormatVerifierOpts,
): Promise<boolean> {
const { attStmt, authData, clientDataHash, credentialPublicKey, rootCertificates } = options;
- const { x5c } = attStmt;
+ const x5c = attStmt.get('x5c');
if (!x5c) {
throw new Error('No attestation certificate provided in attestation statement (Apple)');
diff --git a/packages/server/src/registration/verifications/verifyAttestationFIDOU2F.ts b/packages/server/src/registration/verifications/verifyAttestationFIDOU2F.ts
index 591149f..ef6aa7c 100644
--- a/packages/server/src/registration/verifications/verifyAttestationFIDOU2F.ts
+++ b/packages/server/src/registration/verifications/verifyAttestationFIDOU2F.ts
@@ -33,7 +33,8 @@ export async function verifyAttestationFIDOU2F(
publicKey,
]);
- const { sig, x5c } = attStmt;
+ const sig = attStmt.get('sig');
+ const x5c = attStmt.get('x5c');
if (!x5c) {
throw new Error('No attestation certificate provided in attestation statement (FIDOU2F)');
diff --git a/packages/server/src/registration/verifications/verifyAttestationPacked.ts b/packages/server/src/registration/verifications/verifyAttestationPacked.ts
index aa2d4b9..c5db840 100644
--- a/packages/server/src/registration/verifications/verifyAttestationPacked.ts
+++ b/packages/server/src/registration/verifications/verifyAttestationPacked.ts
@@ -5,6 +5,7 @@ import { convertCertBufferToPEM } from '../../helpers/convertCertBufferToPEM';
import { validateCertificatePath } from '../../helpers/validateCertificatePath';
import { getCertificateInfo } from '../../helpers/getCertificateInfo';
import { verifySignature } from '../../helpers/verifySignature';
+import * as uint8Array from '../../helpers/uint8Array';
import { MetadataService } from '../../services/metadataService';
import { verifyAttestationWithMetadata } from '../../metadata/verifyAttestationWithMetadata';
@@ -17,7 +18,9 @@ export async function verifyAttestationPacked(
const { attStmt, clientDataHash, authData, credentialPublicKey, aaguid, rootCertificates } =
options;
- const { sig, x5c, alg } = attStmt;
+ const sig = attStmt.get('sig');
+ const x5c = attStmt.get('x5c');
+ const alg = attStmt.get('alg');
if (!sig) {
throw new Error('No attestation signature provided in attestation statement (Packed)');