Skip to content

Commit

Permalink
Merge pull request #251 from MasterKale/fix/metadata-attstmt-alg-check
Browse files Browse the repository at this point in the history
fix/metadata-attstmt-alg-check
  • Loading branch information
MasterKale authored Aug 15, 2022
2 parents 8aa4cc6 + 6d6e1a0 commit 3b14ba5
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 25 deletions.
1 change: 1 addition & 0 deletions packages/server/src/metadata/mdsTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,4 +292,5 @@ export type AuthenticatorGetInfo = {
};
maxMsgSize?: number;
pinProtocols?: number[];
algorithms?: { type: 'public-key', alg: number }[];
};
24 changes: 12 additions & 12 deletions packages/server/src/metadata/verifyAttestationWithMetadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ test('should verify attestation with metadata (android-safetynet)', async () =>
const credentialPublicKey =
'pQECAyYgASFYIAKH2NrGZT-lUEA3tbBXR9owjW_7OnA1UqoL1UuKY_VCIlggpjeOH0xyBCpGDya55JLXXKrzyOieQN3dvG1pV-Qs-Gs';

const verified = await verifyAttestationWithMetadata(
metadataStatementJSONSafetyNet,
base64url.toBuffer(credentialPublicKey),
const verified = await verifyAttestationWithMetadata({
statement: metadataStatementJSONSafetyNet,
credentialPublicKey: base64url.toBuffer(credentialPublicKey),
x5c,
);
});

expect(verified).toEqual(true);
});
Expand Down Expand Up @@ -97,11 +97,11 @@ test('should verify attestation with rsa_emsa_pkcs1_sha256_raw authenticator alg
];
const credentialPublicKey = 'pAEDAzkBACBZAQC3X5SKwYUkxFxxyvCnz_37Z57eSdsgQuiBLDaBOd1R6VEZReAV3nVr_7jiRgmWfu1C-S3Aro65eSG5shcDCgIvY3KdEI8K5ENEPlmucjnFILBAE_MZtPmZlkEDmVCDcVspHX2iKqiVWYV6IFzVX1QUf0SAlWijV9NEfKDbij34ddV0qfG2nEMA0_xVpN2OK2BVXonFg6tS3T00XlFh4MdzIauIHTDT63eAdHlkFrMqU53T5IqDvL3VurBmBjYRJ3VDT9mA2sm7fSrJNXhSVLPst-ZsiOioVKrpzFE9sJmyCQvq2nGZ2RhDo8FfAKiw0kvJRkCSSe1ddxryk9_VSCprIUMBAAE';

const verified = await verifyAttestationWithMetadata(
metadataStatement,
base64url.toBuffer(credentialPublicKey),
const verified = await verifyAttestationWithMetadata({
statement: metadataStatement,
credentialPublicKey: base64url.toBuffer(credentialPublicKey),
x5c,
);
});

expect(verified).toEqual(true);
});
Expand Down Expand Up @@ -154,11 +154,11 @@ test('should not validate certificate path when authenticator is self-referencin
];
const credentialPublicKey = 'pQECAyYgASFYIBdmUVOxrn-OOtkVwGP_vAspH3VkgzcGXVlu3-acb7EZIlggKgDTs0fr2d51sLR6uL3KP2cqR3iIUkKMCjyMJhYOkf4';

const verified = await verifyAttestationWithMetadata(
metadataStatement,
base64url.toBuffer(credentialPublicKey),
const verified = await verifyAttestationWithMetadata({
statement: metadataStatement,
credentialPublicKey: base64url.toBuffer(credentialPublicKey),
x5c,
);
});

expect(verified).toEqual(true);
});
40 changes: 32 additions & 8 deletions packages/server/src/metadata/verifyAttestationWithMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,26 @@ import { COSEKEYS, COSEKTY } from '../helpers/convertCOSEtoPKCS';
* Match properties of the authenticator's attestation statement against expected values as
* registered with the FIDO Alliance Metadata Service
*/
export async function verifyAttestationWithMetadata(
statement: MetadataStatement,
credentialPublicKey: Buffer,
x5c: Buffer[] | Base64URLString[],
): Promise<boolean> {
export async function verifyAttestationWithMetadata({
statement,
credentialPublicKey,
x5c,
attestationStatementAlg,
}: {
statement: MetadataStatement;
credentialPublicKey: Buffer;
x5c: Buffer[] | Base64URLString[];
attestationStatementAlg?: number;
}): Promise<boolean> {
const {
authenticationAlgorithms,
authenticatorGetInfo,
attestationRootCertificates,
} = statement;

// Make sure the alg in the attestation statement matches one of the ones specified in metadata
const keypairCOSEAlgs: Set<COSEInfo> = new Set();
statement.authenticationAlgorithms.forEach(algSign => {
authenticationAlgorithms.forEach(algSign => {
// Map algSign string to { kty, alg, crv }
const algSignCOSEINFO = algSignToCOSEInfoMap[algSign];

Expand Down Expand Up @@ -78,7 +90,7 @@ export async function verifyAttestationWithMetadata(
* ]
* ```
*/
const debugMDSAlgs = statement.authenticationAlgorithms
const debugMDSAlgs = authenticationAlgorithms
.map((algSign) => `'${algSign}' (COSE info: ${stringifyCOSEInfo(algSignToCOSEInfoMap[algSign])})`);
const strMDSAlgs = JSON.stringify(debugMDSAlgs, null, 2).replace(/"/g, '');

Expand All @@ -92,9 +104,21 @@ export async function verifyAttestationWithMetadata(
);
}

/**
* Confirm the attestation statement's algorithm is one supported according to metadata
*/
if (attestationStatementAlg !== undefined && authenticatorGetInfo?.algorithms !== undefined) {
const getInfoAlgs = authenticatorGetInfo.algorithms.map(_alg => _alg.alg);
if (getInfoAlgs.indexOf(attestationStatementAlg) < 0) {
throw new Error(
`Attestation statement alg ${attestationStatementAlg} did not match one of ${getInfoAlgs}`,
);
}
}

// Prepare to check the certificate chain
const authenticatorCerts = x5c.map(convertCertBufferToPEM);
const statementRootCerts = statement.attestationRootCertificates.map(convertCertBufferToPEM);
const statementRootCerts = attestationRootCertificates.map(convertCertBufferToPEM);

/**
* If an authenticator returns exactly one certificate in its x5c, and that cert is found in the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,12 @@ export async function verifyAttestationTPM(options: AttestationFormatVerifierOpt
const statement = await MetadataService.getStatement(aaguid);
if (statement) {
try {
await verifyAttestationWithMetadata(statement, credentialPublicKey, x5c);
await verifyAttestationWithMetadata({
statement,
credentialPublicKey,
x5c,
attestationStatementAlg: alg,
});
} catch (err) {
const _err = err as Error;
throw new Error(`${_err.message} (TPM)`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,12 @@ export async function verifyAttestationAndroidKey(
const statement = await MetadataService.getStatement(aaguid);
if (statement) {
try {
await verifyAttestationWithMetadata(statement, credentialPublicKey, x5c);
await verifyAttestationWithMetadata({
statement,
credentialPublicKey,
x5c,
attestationStatementAlg: alg,
});
} catch (err) {
const _err = err as Error;
throw new Error(`${_err.message} (AndroidKey)`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export async function verifyAttestationAndroidSafetyNet(
verifyTimestampMS = true,
credentialPublicKey,
} = options;
const { response, ver } = attStmt;
const { alg, response, ver } = attStmt;

if (!ver) {
throw new Error('No ver value in attestation (SafetyNet)');
Expand Down Expand Up @@ -95,7 +95,12 @@ export async function verifyAttestationAndroidSafetyNet(
const statement = await MetadataService.getStatement(aaguid);
if (statement) {
try {
await verifyAttestationWithMetadata(statement, credentialPublicKey, HEADER.x5c);
await verifyAttestationWithMetadata({
statement,
credentialPublicKey,
x5c: HEADER.x5c,
attestationStatementAlg: alg,
});
} catch (err) {
const _err = err as Error;
throw new Error(`${_err.message} (SafetyNet)`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,12 @@ export async function verifyAttestationPacked(
}

try {
await verifyAttestationWithMetadata(statement, credentialPublicKey, x5c);
await verifyAttestationWithMetadata({
statement,
credentialPublicKey,
x5c,
attestationStatementAlg: alg,
});
} catch (err) {
const _err = err as Error;
throw new Error(`${_err.message} (Packed|Full)`);
Expand Down

0 comments on commit 3b14ba5

Please sign in to comment.