feat: 초기 프로젝트 설정 및 룰.md 파일 추가
This commit is contained in:
46
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/generateRegistrationOptions.d.ts
generated
vendored
Normal file
46
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/generateRegistrationOptions.d.ts
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { AuthenticationExtensionsClientInputs, AuthenticatorSelectionCriteria, AuthenticatorTransportFuture, Base64URLString, COSEAlgorithmIdentifier, PublicKeyCredentialCreationOptionsJSON } from '../types/index.js';
|
||||
export type GenerateRegistrationOptionsOpts = Parameters<typeof generateRegistrationOptions>[0];
|
||||
/**
|
||||
* Supported crypto algo identifiers
|
||||
* See https://w3c.github.io/webauthn/#sctn-alg-identifier
|
||||
* and https://www.iana.org/assignments/cose/cose.xhtml#algorithms
|
||||
*/
|
||||
export declare const supportedCOSEAlgorithmIdentifiers: COSEAlgorithmIdentifier[];
|
||||
/**
|
||||
* Prepare a value to pass into navigator.credentials.create(...) for authenticator registration
|
||||
*
|
||||
* **Options:**
|
||||
*
|
||||
* @param rpName - User-visible, "friendly" website/service name
|
||||
* @param rpID - Valid domain name (after `https://`)
|
||||
* @param userName - User's website-specific username (email, etc...)
|
||||
* @param userID **(Optional)** - User's website-specific unique ID. Defaults to generating a random identifier
|
||||
* @param challenge **(Optional)** - Random value the authenticator needs to sign and pass back. Defaults to generating a random value
|
||||
* @param userDisplayName **(Optional)** - User's actual name. Defaults to `""`
|
||||
* @param timeout **(Optional)** - How long (in ms) the user can take to complete attestation. Defaults to `60000`
|
||||
* @param attestationType **(Optional)** - Specific attestation statement. Defaults to `"none"`
|
||||
* @param excludeCredentials **(Optional)** - Authenticators registered by the user so the user can't register the same credential multiple times. Defaults to `[]`
|
||||
* @param authenticatorSelection **(Optional)** - Advanced criteria for restricting the types of authenticators that may be used. Defaults to `{ residentKey: 'preferred', userVerification: 'preferred' }`
|
||||
* @param extensions **(Optional)** - Additional plugins the authenticator or browser should use during attestation
|
||||
* @param supportedAlgorithmIDs **(Optional)** - Array of numeric COSE algorithm identifiers supported for attestation by this RP. See https://www.iana.org/assignments/cose/cose.xhtml#algorithms. Defaults to `[-8, -7, -257]`
|
||||
* @param preferredAuthenticatorType **(Optional)** - Encourage the browser to prompt the user to register a specific type of authenticator
|
||||
*/
|
||||
export declare function generateRegistrationOptions(options: {
|
||||
rpName: string;
|
||||
rpID: string;
|
||||
userName: string;
|
||||
userID?: Uint8Array;
|
||||
challenge?: string | Uint8Array;
|
||||
userDisplayName?: string;
|
||||
timeout?: number;
|
||||
attestationType?: 'direct' | 'enterprise' | 'none';
|
||||
excludeCredentials?: {
|
||||
id: Base64URLString;
|
||||
transports?: AuthenticatorTransportFuture[];
|
||||
}[];
|
||||
authenticatorSelection?: AuthenticatorSelectionCriteria;
|
||||
extensions?: AuthenticationExtensionsClientInputs;
|
||||
supportedAlgorithmIDs?: COSEAlgorithmIdentifier[];
|
||||
preferredAuthenticatorType?: 'securityKey' | 'localDevice' | 'remoteDevice';
|
||||
}): Promise<PublicKeyCredentialCreationOptionsJSON>;
|
||||
//# sourceMappingURL=generateRegistrationOptions.d.ts.map
|
||||
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/generateRegistrationOptions.d.ts.map
generated
vendored
Normal file
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/generateRegistrationOptions.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"generateRegistrationOptions.d.ts","sourceRoot":"","sources":["../../src/registration/generateRegistrationOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oCAAoC,EACpC,8BAA8B,EAC9B,4BAA4B,EAC5B,eAAe,EACf,uBAAuB,EACvB,sCAAsC,EAGvC,MAAM,mBAAmB,CAAC;AAK3B,MAAM,MAAM,+BAA+B,GAAG,UAAU,CAAC,OAAO,2BAA2B,CAAC,CAAC,CAAC,CAAC,CAAC;AAEhG;;;;GAIG;AACH,eAAO,MAAM,iCAAiC,EAAE,uBAAuB,EAqBtE,CAAC;AAsBF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE;IACP,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,QAAQ,GAAG,YAAY,GAAG,MAAM,CAAC;IACnD,kBAAkB,CAAC,EAAE;QACnB,EAAE,EAAE,eAAe,CAAC;QACpB,UAAU,CAAC,EAAE,4BAA4B,EAAE,CAAC;KAC7C,EAAE,CAAC;IACJ,sBAAsB,CAAC,EAAE,8BAA8B,CAAC;IACxD,UAAU,CAAC,EAAE,oCAAoC,CAAC;IAClD,qBAAqB,CAAC,EAAE,uBAAuB,EAAE,CAAC;IAClD,0BAA0B,CAAC,EAAE,aAAa,GAAG,aAAa,GAAG,cAAc,CAAC;CAC7E,GACA,OAAO,CAAC,sCAAsC,CAAC,CAqIjD"}
|
||||
185
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/generateRegistrationOptions.js
generated
vendored
Normal file
185
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/generateRegistrationOptions.js
generated
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.supportedCOSEAlgorithmIdentifiers = void 0;
|
||||
exports.generateRegistrationOptions = generateRegistrationOptions;
|
||||
const generateChallenge_js_1 = require("../helpers/generateChallenge.js");
|
||||
const generateUserID_js_1 = require("../helpers/generateUserID.js");
|
||||
const index_js_1 = require("../helpers/iso/index.js");
|
||||
/**
|
||||
* Supported crypto algo identifiers
|
||||
* See https://w3c.github.io/webauthn/#sctn-alg-identifier
|
||||
* and https://www.iana.org/assignments/cose/cose.xhtml#algorithms
|
||||
*/
|
||||
exports.supportedCOSEAlgorithmIdentifiers = [
|
||||
// EdDSA (In first position to encourage authenticators to use this over ES256)
|
||||
-8,
|
||||
// ECDSA w/ SHA-256
|
||||
-7,
|
||||
// ECDSA w/ SHA-512
|
||||
-36,
|
||||
// RSASSA-PSS w/ SHA-256
|
||||
-37,
|
||||
// RSASSA-PSS w/ SHA-384
|
||||
-38,
|
||||
// RSASSA-PSS w/ SHA-512
|
||||
-39,
|
||||
// RSASSA-PKCS1-v1_5 w/ SHA-256
|
||||
-257,
|
||||
// RSASSA-PKCS1-v1_5 w/ SHA-384
|
||||
-258,
|
||||
// RSASSA-PKCS1-v1_5 w/ SHA-512
|
||||
-259,
|
||||
// RSASSA-PKCS1-v1_5 w/ SHA-1 (Deprecated; here for legacy support)
|
||||
-65535,
|
||||
];
|
||||
/**
|
||||
* Set up some default authenticator selection options as per the latest spec:
|
||||
* https://www.w3.org/TR/webauthn-2/#dictdef-authenticatorselectioncriteria
|
||||
*
|
||||
* Helps with some older platforms (e.g. Android 7.0 Nougat) that may not be aware of these
|
||||
* defaults.
|
||||
*/
|
||||
const defaultAuthenticatorSelection = {
|
||||
residentKey: 'preferred',
|
||||
userVerification: 'preferred',
|
||||
};
|
||||
/**
|
||||
* Use the most commonly-supported algorithms
|
||||
* See the following:
|
||||
* - https://www.iana.org/assignments/cose/cose.xhtml#algorithms
|
||||
* - https://w3c.github.io/webauthn/#dom-publickeycredentialcreationoptions-pubkeycredparams
|
||||
*/
|
||||
const defaultSupportedAlgorithmIDs = [-8, -7, -257];
|
||||
/**
|
||||
* Prepare a value to pass into navigator.credentials.create(...) for authenticator registration
|
||||
*
|
||||
* **Options:**
|
||||
*
|
||||
* @param rpName - User-visible, "friendly" website/service name
|
||||
* @param rpID - Valid domain name (after `https://`)
|
||||
* @param userName - User's website-specific username (email, etc...)
|
||||
* @param userID **(Optional)** - User's website-specific unique ID. Defaults to generating a random identifier
|
||||
* @param challenge **(Optional)** - Random value the authenticator needs to sign and pass back. Defaults to generating a random value
|
||||
* @param userDisplayName **(Optional)** - User's actual name. Defaults to `""`
|
||||
* @param timeout **(Optional)** - How long (in ms) the user can take to complete attestation. Defaults to `60000`
|
||||
* @param attestationType **(Optional)** - Specific attestation statement. Defaults to `"none"`
|
||||
* @param excludeCredentials **(Optional)** - Authenticators registered by the user so the user can't register the same credential multiple times. Defaults to `[]`
|
||||
* @param authenticatorSelection **(Optional)** - Advanced criteria for restricting the types of authenticators that may be used. Defaults to `{ residentKey: 'preferred', userVerification: 'preferred' }`
|
||||
* @param extensions **(Optional)** - Additional plugins the authenticator or browser should use during attestation
|
||||
* @param supportedAlgorithmIDs **(Optional)** - Array of numeric COSE algorithm identifiers supported for attestation by this RP. See https://www.iana.org/assignments/cose/cose.xhtml#algorithms. Defaults to `[-8, -7, -257]`
|
||||
* @param preferredAuthenticatorType **(Optional)** - Encourage the browser to prompt the user to register a specific type of authenticator
|
||||
*/
|
||||
async function generateRegistrationOptions(options) {
|
||||
const { rpName, rpID, userName, userID, challenge = await (0, generateChallenge_js_1.generateChallenge)(), userDisplayName = '', timeout = 60000, attestationType = 'none', excludeCredentials = [], authenticatorSelection = defaultAuthenticatorSelection, extensions, supportedAlgorithmIDs = defaultSupportedAlgorithmIDs, preferredAuthenticatorType, } = options;
|
||||
/**
|
||||
* Prepare pubKeyCredParams from the array of algorithm ID's
|
||||
*/
|
||||
const pubKeyCredParams = supportedAlgorithmIDs.map((id) => ({
|
||||
alg: id,
|
||||
type: 'public-key',
|
||||
}));
|
||||
/**
|
||||
* Capture some of the nuances of how `residentKey` and `requireResidentKey` how either is set
|
||||
* depending on when either is defined in the options
|
||||
*/
|
||||
if (authenticatorSelection.residentKey === undefined) {
|
||||
/**
|
||||
* `residentKey`: "If no value is given then the effective value is `required` if
|
||||
* requireResidentKey is true or `discouraged` if it is false or absent."
|
||||
*
|
||||
* See https://www.w3.org/TR/webauthn-2/#dom-authenticatorselectioncriteria-residentkey
|
||||
*/
|
||||
if (authenticatorSelection.requireResidentKey) {
|
||||
authenticatorSelection.residentKey = 'required';
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* FIDO Conformance v1.7.2 fails the first test if we do this, even though this is
|
||||
* technically compatible with the WebAuthn L2 spec...
|
||||
*/
|
||||
// authenticatorSelection.residentKey = 'discouraged';
|
||||
}
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* `requireResidentKey`: "Relying Parties SHOULD set it to true if, and only if, residentKey is
|
||||
* set to "required""
|
||||
*
|
||||
* Spec says this property defaults to `false` so we should still be okay to assign `false` too
|
||||
*
|
||||
* See https://www.w3.org/TR/webauthn-2/#dom-authenticatorselectioncriteria-requireresidentkey
|
||||
*/
|
||||
authenticatorSelection.requireResidentKey = authenticatorSelection.residentKey === 'required';
|
||||
}
|
||||
/**
|
||||
* Preserve ability to specify `string` values for challenges
|
||||
*/
|
||||
let _challenge = challenge;
|
||||
if (typeof _challenge === 'string') {
|
||||
_challenge = index_js_1.isoUint8Array.fromUTF8String(_challenge);
|
||||
}
|
||||
/**
|
||||
* Explicitly disallow use of strings for userID anymore because `isoBase64URL.fromBuffer()` below
|
||||
* will return an empty string if one gets through!
|
||||
*/
|
||||
if (typeof userID === 'string') {
|
||||
throw new Error(`String values for \`userID\` are no longer supported. See https://simplewebauthn.dev/docs/advanced/server/custom-user-ids`);
|
||||
}
|
||||
/**
|
||||
* Generate a user ID if one is not provided
|
||||
*/
|
||||
let _userID = userID;
|
||||
if (!_userID) {
|
||||
_userID = await (0, generateUserID_js_1.generateUserID)();
|
||||
}
|
||||
/**
|
||||
* Map authenticator preference to hints. Map to authenticatorAttachment as well for
|
||||
* backwards-compatibility.
|
||||
*/
|
||||
const hints = [];
|
||||
if (preferredAuthenticatorType) {
|
||||
if (preferredAuthenticatorType === 'securityKey') {
|
||||
hints.push('security-key');
|
||||
authenticatorSelection.authenticatorAttachment = 'cross-platform';
|
||||
}
|
||||
else if (preferredAuthenticatorType === 'localDevice') {
|
||||
hints.push('client-device');
|
||||
authenticatorSelection.authenticatorAttachment = 'platform';
|
||||
}
|
||||
else if (preferredAuthenticatorType === 'remoteDevice') {
|
||||
hints.push('hybrid');
|
||||
authenticatorSelection.authenticatorAttachment = 'cross-platform';
|
||||
}
|
||||
}
|
||||
return {
|
||||
challenge: index_js_1.isoBase64URL.fromBuffer(_challenge),
|
||||
rp: {
|
||||
name: rpName,
|
||||
id: rpID,
|
||||
},
|
||||
user: {
|
||||
id: index_js_1.isoBase64URL.fromBuffer(_userID),
|
||||
name: userName,
|
||||
displayName: userDisplayName,
|
||||
},
|
||||
pubKeyCredParams,
|
||||
timeout,
|
||||
attestation: attestationType,
|
||||
excludeCredentials: excludeCredentials.map((cred) => {
|
||||
if (!index_js_1.isoBase64URL.isBase64URL(cred.id)) {
|
||||
throw new Error(`excludeCredential id "${cred.id}" is not a valid base64url string`);
|
||||
}
|
||||
return {
|
||||
...cred,
|
||||
id: index_js_1.isoBase64URL.trimPadding(cred.id),
|
||||
type: 'public-key',
|
||||
};
|
||||
}),
|
||||
authenticatorSelection,
|
||||
extensions: {
|
||||
...extensions,
|
||||
credProps: true,
|
||||
},
|
||||
hints,
|
||||
};
|
||||
}
|
||||
48
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/constants.d.ts
generated
vendored
Normal file
48
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/constants.d.ts
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* A whole lotta domain knowledge is captured here, with hazy connections to source
|
||||
* documents. Good places to start searching for more info on these values are the
|
||||
* following Trusted Computing Group TPM Library docs linked in the WebAuthn API:
|
||||
*
|
||||
* - https://www.trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-1-Architecture-01.38.pdf
|
||||
* - https://www.trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf
|
||||
* - https://www.trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-3-Commands-01.38.pdf
|
||||
*/
|
||||
/**
|
||||
* 6.9 TPM_ST (Structure Tags)
|
||||
*/
|
||||
export declare const TPM_ST: {
|
||||
[key: number]: string;
|
||||
};
|
||||
/**
|
||||
* 6.3 TPM_ALG_ID
|
||||
*/
|
||||
export declare const TPM_ALG: {
|
||||
[key: number]: string;
|
||||
};
|
||||
/**
|
||||
* 6.4 TPM_ECC_CURVE
|
||||
*/
|
||||
export declare const TPM_ECC_CURVE: {
|
||||
[key: number]: string;
|
||||
};
|
||||
type ManufacturerInfo = {
|
||||
name: string;
|
||||
id: string;
|
||||
};
|
||||
/**
|
||||
* Sourced from https://trustedcomputinggroup.org/resource/vendor-id-registry/
|
||||
*
|
||||
* Latest version:
|
||||
* https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-Vendor-ID-Registry-Version-1.02-Revision-1.00.pdf
|
||||
*/
|
||||
export declare const TPM_MANUFACTURERS: {
|
||||
[key: string]: ManufacturerInfo;
|
||||
};
|
||||
/**
|
||||
* Match TPM public area curve ID's to `crv` numbers used in COSE public keys
|
||||
*/
|
||||
export declare const TPM_ECC_CURVE_COSE_CRV_MAP: {
|
||||
[key: string]: number;
|
||||
};
|
||||
export {};
|
||||
//# sourceMappingURL=constants.d.ts.map
|
||||
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/constants.d.ts.map
generated
vendored
Normal file
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/constants.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/registration/verifications/tpm/constants.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AAEH;;GAEG;AACH,eAAO,MAAM,MAAM,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAkB3C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,OAAO,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAsC5C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAUlD,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAAA;CAgChE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,0BAA0B,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAM/D,CAAC"}
|
||||
140
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/constants.js
generated
vendored
Normal file
140
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/constants.js
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
"use strict";
|
||||
// deno-lint-ignore-file no-dupe-keys
|
||||
/**
|
||||
* A whole lotta domain knowledge is captured here, with hazy connections to source
|
||||
* documents. Good places to start searching for more info on these values are the
|
||||
* following Trusted Computing Group TPM Library docs linked in the WebAuthn API:
|
||||
*
|
||||
* - https://www.trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-1-Architecture-01.38.pdf
|
||||
* - https://www.trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf
|
||||
* - https://www.trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-3-Commands-01.38.pdf
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.TPM_ECC_CURVE_COSE_CRV_MAP = exports.TPM_MANUFACTURERS = exports.TPM_ECC_CURVE = exports.TPM_ALG = exports.TPM_ST = void 0;
|
||||
/**
|
||||
* 6.9 TPM_ST (Structure Tags)
|
||||
*/
|
||||
exports.TPM_ST = {
|
||||
0x00c4: 'TPM_ST_RSP_COMMAND',
|
||||
0x8000: 'TPM_ST_NULL',
|
||||
0x8001: 'TPM_ST_NO_SESSIONS',
|
||||
0x8002: 'TPM_ST_SESSIONS',
|
||||
0x8014: 'TPM_ST_ATTEST_NV',
|
||||
0x8015: 'TPM_ST_ATTEST_COMMAND_AUDIT',
|
||||
0x8016: 'TPM_ST_ATTEST_SESSION_AUDIT',
|
||||
0x8017: 'TPM_ST_ATTEST_CERTIFY',
|
||||
0x8018: 'TPM_ST_ATTEST_QUOTE',
|
||||
0x8019: 'TPM_ST_ATTEST_TIME',
|
||||
0x801a: 'TPM_ST_ATTEST_CREATION',
|
||||
0x8021: 'TPM_ST_CREATION',
|
||||
0x8022: 'TPM_ST_VERIFIED',
|
||||
0x8023: 'TPM_ST_AUTH_SECRET',
|
||||
0x8024: 'TPM_ST_HASHCHECK',
|
||||
0x8025: 'TPM_ST_AUTH_SIGNED',
|
||||
0x8029: 'TPM_ST_FU_MANIFEST',
|
||||
};
|
||||
/**
|
||||
* 6.3 TPM_ALG_ID
|
||||
*/
|
||||
exports.TPM_ALG = {
|
||||
0x0000: 'TPM_ALG_ERROR',
|
||||
0x0001: 'TPM_ALG_RSA',
|
||||
0x0004: 'TPM_ALG_SHA',
|
||||
// @ts-ignore 2300
|
||||
0x0004: 'TPM_ALG_SHA1',
|
||||
0x0005: 'TPM_ALG_HMAC',
|
||||
0x0006: 'TPM_ALG_AES',
|
||||
0x0007: 'TPM_ALG_MGF1',
|
||||
0x0008: 'TPM_ALG_KEYEDHASH',
|
||||
0x000a: 'TPM_ALG_XOR',
|
||||
0x000b: 'TPM_ALG_SHA256',
|
||||
0x000c: 'TPM_ALG_SHA384',
|
||||
0x000d: 'TPM_ALG_SHA512',
|
||||
0x0010: 'TPM_ALG_NULL',
|
||||
0x0012: 'TPM_ALG_SM3_256',
|
||||
0x0013: 'TPM_ALG_SM4',
|
||||
0x0014: 'TPM_ALG_RSASSA',
|
||||
0x0015: 'TPM_ALG_RSAES',
|
||||
0x0016: 'TPM_ALG_RSAPSS',
|
||||
0x0017: 'TPM_ALG_OAEP',
|
||||
0x0018: 'TPM_ALG_ECDSA',
|
||||
0x0019: 'TPM_ALG_ECDH',
|
||||
0x001a: 'TPM_ALG_ECDAA',
|
||||
0x001b: 'TPM_ALG_SM2',
|
||||
0x001c: 'TPM_ALG_ECSCHNORR',
|
||||
0x001d: 'TPM_ALG_ECMQV',
|
||||
0x0020: 'TPM_ALG_KDF1_SP800_56A',
|
||||
0x0021: 'TPM_ALG_KDF2',
|
||||
0x0022: 'TPM_ALG_KDF1_SP800_108',
|
||||
0x0023: 'TPM_ALG_ECC',
|
||||
0x0025: 'TPM_ALG_SYMCIPHER',
|
||||
0x0026: 'TPM_ALG_CAMELLIA',
|
||||
0x0040: 'TPM_ALG_CTR',
|
||||
0x0041: 'TPM_ALG_OFB',
|
||||
0x0042: 'TPM_ALG_CBC',
|
||||
0x0043: 'TPM_ALG_CFB',
|
||||
0x0044: 'TPM_ALG_ECB',
|
||||
};
|
||||
/**
|
||||
* 6.4 TPM_ECC_CURVE
|
||||
*/
|
||||
exports.TPM_ECC_CURVE = {
|
||||
0x0000: 'TPM_ECC_NONE',
|
||||
0x0001: 'TPM_ECC_NIST_P192',
|
||||
0x0002: 'TPM_ECC_NIST_P224',
|
||||
0x0003: 'TPM_ECC_NIST_P256',
|
||||
0x0004: 'TPM_ECC_NIST_P384',
|
||||
0x0005: 'TPM_ECC_NIST_P521',
|
||||
0x0010: 'TPM_ECC_BN_P256',
|
||||
0x0011: 'TPM_ECC_BN_P638',
|
||||
0x0020: 'TPM_ECC_SM2_P256',
|
||||
};
|
||||
/**
|
||||
* Sourced from https://trustedcomputinggroup.org/resource/vendor-id-registry/
|
||||
*
|
||||
* Latest version:
|
||||
* https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-Vendor-ID-Registry-Version-1.02-Revision-1.00.pdf
|
||||
*/
|
||||
exports.TPM_MANUFACTURERS = {
|
||||
'id:414D4400': { name: 'AMD', id: 'AMD' },
|
||||
'id:414E5400': { name: 'Ant Group', id: 'ANT' },
|
||||
'id:41544D4C': { name: 'Atmel', id: 'ATML' },
|
||||
'id:4252434D': { name: 'Broadcom', id: 'BRCM' },
|
||||
'id:4353434F': { name: 'Cisco', id: 'CSCO' },
|
||||
'id:464C5953': { name: 'Flyslice Technologies', id: 'FLYS' },
|
||||
'id:524F4343': { name: 'Fuzhou Rockchip', id: 'ROCC' },
|
||||
'id:474F4F47': { name: 'Google', id: 'GOOG' },
|
||||
'id:48504900': { name: 'HPI', id: 'HPI' },
|
||||
'id:48504500': { name: 'HPE', id: 'HPE' },
|
||||
'id:48495349': { name: 'Huawei', id: 'HISI' },
|
||||
'id:49424d00': { name: 'IBM', id: 'IBM' },
|
||||
'id:49424D00': { name: 'IBM', id: 'IBM' }, // Same ID for IBM as above, except the "D" is capitalized as per TPM spec
|
||||
'id:49465800': { name: 'Infineon', id: 'IFX' },
|
||||
'id:494E5443': { name: 'Intel', id: 'INTC' },
|
||||
'id:4C454E00': { name: 'Lenovo', id: 'LEN' },
|
||||
'id:4D534654': { name: 'Microsoft', id: 'MSFT' },
|
||||
'id:4E534D20': { name: 'National Semiconductor', id: 'NSM' },
|
||||
'id:4E545A00': { name: 'Nationz', id: 'NTZ' },
|
||||
'id:4E534700': { name: 'NSING', id: 'NSG' },
|
||||
'id:4E544300': { name: 'Nuvoton Technology', id: 'NTC' },
|
||||
'id:51434F4D': { name: 'Qualcomm', id: 'QCOM' },
|
||||
'id:534D534E': { name: 'Samsung', id: 'SMSN' },
|
||||
'id:53454345': { name: 'SecEdge', id: 'SECE' },
|
||||
'id:534E5300': { name: 'Sinosun', id: 'SNS' },
|
||||
'id:534D5343': { name: 'SMSC', id: 'SMSC' },
|
||||
'id:53544D20': { name: 'STMicroelectronics', id: 'STM' },
|
||||
'id:54584E00': { name: 'Texas Instruments', id: 'TXN' },
|
||||
'id:57454300': { name: 'Winbond', id: 'WEC' },
|
||||
'id:5345414C': { name: 'Wisekey', id: 'SEAL' },
|
||||
'id:FFFFF1D0': { name: 'FIDO Alliance', id: 'FIDO' }, // FIDO Conformance
|
||||
};
|
||||
/**
|
||||
* Match TPM public area curve ID's to `crv` numbers used in COSE public keys
|
||||
*/
|
||||
exports.TPM_ECC_CURVE_COSE_CRV_MAP = {
|
||||
TPM_ECC_NIST_P256: 1, // p256
|
||||
TPM_ECC_NIST_P384: 2, // p384
|
||||
TPM_ECC_NIST_P521: 3, // p521
|
||||
TPM_ECC_BN_P256: 1, // p256
|
||||
TPM_ECC_SM2_P256: 1, // p256
|
||||
};
|
||||
25
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/parseCertInfo.d.ts
generated
vendored
Normal file
25
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/parseCertInfo.d.ts
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Cut up a TPM attestation's certInfo into intelligible chunks
|
||||
*/
|
||||
export declare function parseCertInfo(certInfo: Uint8Array): ParsedCertInfo;
|
||||
type ParsedCertInfo = {
|
||||
magic: number;
|
||||
type: string;
|
||||
qualifiedSigner: Uint8Array;
|
||||
extraData: Uint8Array;
|
||||
clockInfo: {
|
||||
clock: Uint8Array;
|
||||
resetCount: number;
|
||||
restartCount: number;
|
||||
safe: boolean;
|
||||
};
|
||||
firmwareVersion: Uint8Array;
|
||||
attested: {
|
||||
nameAlg: string;
|
||||
nameAlgBuffer: Uint8Array;
|
||||
name: Uint8Array;
|
||||
qualifiedName: Uint8Array;
|
||||
};
|
||||
};
|
||||
export {};
|
||||
//# sourceMappingURL=parseCertInfo.d.ts.map
|
||||
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/parseCertInfo.d.ts.map
generated
vendored
Normal file
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/parseCertInfo.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"parseCertInfo.d.ts","sourceRoot":"","sources":["../../../../src/registration/verifications/tpm/parseCertInfo.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,UAAU,GAAG,cAAc,CAkElE;AAED,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,UAAU,CAAC;IAC5B,SAAS,EAAE,UAAU,CAAC;IACtB,SAAS,EAAE;QACT,KAAK,EAAE,UAAU,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,IAAI,EAAE,OAAO,CAAC;KACf,CAAC;IACF,eAAe,EAAE,UAAU,CAAC;IAC5B,QAAQ,EAAE;QACR,OAAO,EAAE,MAAM,CAAC;QAChB,aAAa,EAAE,UAAU,CAAC;QAC1B,IAAI,EAAE,UAAU,CAAC;QACjB,aAAa,EAAE,UAAU,CAAC;KAC3B,CAAC;CACH,CAAC"}
|
||||
61
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/parseCertInfo.js
generated
vendored
Normal file
61
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/parseCertInfo.js
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.parseCertInfo = parseCertInfo;
|
||||
const constants_js_1 = require("./constants.js");
|
||||
const index_js_1 = require("../../../helpers/iso/index.js");
|
||||
/**
|
||||
* Cut up a TPM attestation's certInfo into intelligible chunks
|
||||
*/
|
||||
function parseCertInfo(certInfo) {
|
||||
let pointer = 0;
|
||||
const dataView = index_js_1.isoUint8Array.toDataView(certInfo);
|
||||
// Get a magic constant
|
||||
const magic = dataView.getUint32(pointer);
|
||||
pointer += 4;
|
||||
// Determine the algorithm used for attestation
|
||||
const typeBuffer = dataView.getUint16(pointer);
|
||||
pointer += 2;
|
||||
const type = constants_js_1.TPM_ST[typeBuffer];
|
||||
// The name of a parent entity, can be ignored
|
||||
const qualifiedSignerLength = dataView.getUint16(pointer);
|
||||
pointer += 2;
|
||||
const qualifiedSigner = certInfo.slice(pointer, pointer += qualifiedSignerLength);
|
||||
// Get the expected hash of `attsToBeSigned`
|
||||
const extraDataLength = dataView.getUint16(pointer);
|
||||
pointer += 2;
|
||||
const extraData = certInfo.slice(pointer, pointer += extraDataLength);
|
||||
// Information about the TPM device's internal clock, can be ignored
|
||||
const clock = certInfo.slice(pointer, pointer += 8);
|
||||
const resetCount = dataView.getUint32(pointer);
|
||||
pointer += 4;
|
||||
const restartCount = dataView.getUint32(pointer);
|
||||
pointer += 4;
|
||||
const safe = !!certInfo.slice(pointer, pointer += 1);
|
||||
const clockInfo = { clock, resetCount, restartCount, safe };
|
||||
// TPM device firmware version
|
||||
const firmwareVersion = certInfo.slice(pointer, pointer += 8);
|
||||
// Attested Name
|
||||
const attestedNameLength = dataView.getUint16(pointer);
|
||||
pointer += 2;
|
||||
const attestedName = certInfo.slice(pointer, pointer += attestedNameLength);
|
||||
const attestedNameDataView = index_js_1.isoUint8Array.toDataView(attestedName);
|
||||
// Attested qualified name, can be ignored
|
||||
const qualifiedNameLength = dataView.getUint16(pointer);
|
||||
pointer += 2;
|
||||
const qualifiedName = certInfo.slice(pointer, pointer += qualifiedNameLength);
|
||||
const attested = {
|
||||
nameAlg: constants_js_1.TPM_ALG[attestedNameDataView.getUint16(0)],
|
||||
nameAlgBuffer: attestedName.slice(0, 2),
|
||||
name: attestedName,
|
||||
qualifiedName,
|
||||
};
|
||||
return {
|
||||
magic,
|
||||
type,
|
||||
qualifiedSigner,
|
||||
extraData,
|
||||
clockInfo,
|
||||
firmwareVersion,
|
||||
attested,
|
||||
};
|
||||
}
|
||||
44
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/parsePubArea.d.ts
generated
vendored
Normal file
44
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/parsePubArea.d.ts
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Break apart a TPM attestation's pubArea buffer
|
||||
*
|
||||
* See 12.2.4 TPMT_PUBLIC here:
|
||||
* https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-00.96-130315.pdf
|
||||
*/
|
||||
export declare function parsePubArea(pubArea: Uint8Array): ParsedPubArea;
|
||||
type ParsedPubArea = {
|
||||
type: 'TPM_ALG_RSA' | 'TPM_ALG_ECC';
|
||||
nameAlg: string;
|
||||
objectAttributes: {
|
||||
fixedTPM: boolean;
|
||||
stClear: boolean;
|
||||
fixedParent: boolean;
|
||||
sensitiveDataOrigin: boolean;
|
||||
userWithAuth: boolean;
|
||||
adminWithPolicy: boolean;
|
||||
noDA: boolean;
|
||||
encryptedDuplication: boolean;
|
||||
restricted: boolean;
|
||||
decrypt: boolean;
|
||||
signOrEncrypt: boolean;
|
||||
};
|
||||
authPolicy: Uint8Array;
|
||||
parameters: {
|
||||
rsa?: RSAParameters;
|
||||
ecc?: ECCParameters;
|
||||
};
|
||||
unique: Uint8Array;
|
||||
};
|
||||
type RSAParameters = {
|
||||
symmetric: string;
|
||||
scheme: string;
|
||||
keyBits: number;
|
||||
exponent: number;
|
||||
};
|
||||
type ECCParameters = {
|
||||
symmetric: string;
|
||||
scheme: string;
|
||||
curveID: string;
|
||||
kdf: string;
|
||||
};
|
||||
export {};
|
||||
//# sourceMappingURL=parsePubArea.d.ts.map
|
||||
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/parsePubArea.d.ts.map
generated
vendored
Normal file
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/parsePubArea.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"parsePubArea.d.ts","sourceRoot":"","sources":["../../../../src/registration/verifications/tpm/parsePubArea.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,UAAU,GAAG,aAAa,CAyG/D;AAED,KAAK,aAAa,GAAG;IACnB,IAAI,EAAE,aAAa,GAAG,aAAa,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE;QAChB,QAAQ,EAAE,OAAO,CAAC;QAClB,OAAO,EAAE,OAAO,CAAC;QACjB,WAAW,EAAE,OAAO,CAAC;QACrB,mBAAmB,EAAE,OAAO,CAAC;QAC7B,YAAY,EAAE,OAAO,CAAC;QACtB,eAAe,EAAE,OAAO,CAAC;QACzB,IAAI,EAAE,OAAO,CAAC;QACd,oBAAoB,EAAE,OAAO,CAAC;QAC9B,UAAU,EAAE,OAAO,CAAC;QACpB,OAAO,EAAE,OAAO,CAAC;QACjB,aAAa,EAAE,OAAO,CAAC;KACxB,CAAC;IACF,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,EAAE;QACV,GAAG,CAAC,EAAE,aAAa,CAAC;QACpB,GAAG,CAAC,EAAE,aAAa,CAAC;KACrB,CAAC;IACF,MAAM,EAAE,UAAU,CAAC;CACpB,CAAC;AAEF,KAAK,aAAa,GAAG;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,KAAK,aAAa,GAAG;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC"}
|
||||
97
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/parsePubArea.js
generated
vendored
Normal file
97
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/parsePubArea.js
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.parsePubArea = parsePubArea;
|
||||
const constants_js_1 = require("./constants.js");
|
||||
const index_js_1 = require("../../../helpers/iso/index.js");
|
||||
/**
|
||||
* Break apart a TPM attestation's pubArea buffer
|
||||
*
|
||||
* See 12.2.4 TPMT_PUBLIC here:
|
||||
* https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-00.96-130315.pdf
|
||||
*/
|
||||
function parsePubArea(pubArea) {
|
||||
let pointer = 0;
|
||||
const dataView = index_js_1.isoUint8Array.toDataView(pubArea);
|
||||
const type = constants_js_1.TPM_ALG[dataView.getUint16(pointer)];
|
||||
pointer += 2;
|
||||
const nameAlg = constants_js_1.TPM_ALG[dataView.getUint16(pointer)];
|
||||
pointer += 2;
|
||||
// Get some authenticator attributes(?)
|
||||
// const objectAttributesInt = pubArea.slice(pointer, (pointer += 4)).readUInt32BE(0);
|
||||
const objectAttributesInt = dataView.getUint32(pointer);
|
||||
pointer += 4;
|
||||
const objectAttributes = {
|
||||
fixedTPM: !!(objectAttributesInt & 1),
|
||||
stClear: !!(objectAttributesInt & 2),
|
||||
fixedParent: !!(objectAttributesInt & 8),
|
||||
sensitiveDataOrigin: !!(objectAttributesInt & 16),
|
||||
userWithAuth: !!(objectAttributesInt & 32),
|
||||
adminWithPolicy: !!(objectAttributesInt & 64),
|
||||
noDA: !!(objectAttributesInt & 512),
|
||||
encryptedDuplication: !!(objectAttributesInt & 1024),
|
||||
restricted: !!(objectAttributesInt & 32768),
|
||||
decrypt: !!(objectAttributesInt & 65536),
|
||||
signOrEncrypt: !!(objectAttributesInt & 131072),
|
||||
};
|
||||
// Slice out the authPolicy of dynamic length
|
||||
const authPolicyLength = dataView.getUint16(pointer);
|
||||
pointer += 2;
|
||||
const authPolicy = pubArea.slice(pointer, pointer += authPolicyLength);
|
||||
// Extract additional curve params according to type
|
||||
const parameters = {};
|
||||
let unique = Uint8Array.from([]);
|
||||
if (type === 'TPM_ALG_RSA') {
|
||||
const symmetric = constants_js_1.TPM_ALG[dataView.getUint16(pointer)];
|
||||
pointer += 2;
|
||||
const scheme = constants_js_1.TPM_ALG[dataView.getUint16(pointer)];
|
||||
pointer += 2;
|
||||
const keyBits = dataView.getUint16(pointer);
|
||||
pointer += 2;
|
||||
const exponent = dataView.getUint32(pointer);
|
||||
pointer += 4;
|
||||
parameters.rsa = { symmetric, scheme, keyBits, exponent };
|
||||
/**
|
||||
* See 11.2.4.5 TPM2B_PUBLIC_KEY_RSA here:
|
||||
* https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-00.96-130315.pdf
|
||||
*/
|
||||
// const uniqueLength = pubArea.slice(pointer, (pointer += 2)).readUInt16BE(0);
|
||||
const uniqueLength = dataView.getUint16(pointer);
|
||||
pointer += 2;
|
||||
unique = pubArea.slice(pointer, pointer += uniqueLength);
|
||||
}
|
||||
else if (type === 'TPM_ALG_ECC') {
|
||||
const symmetric = constants_js_1.TPM_ALG[dataView.getUint16(pointer)];
|
||||
pointer += 2;
|
||||
const scheme = constants_js_1.TPM_ALG[dataView.getUint16(pointer)];
|
||||
pointer += 2;
|
||||
const curveID = constants_js_1.TPM_ECC_CURVE[dataView.getUint16(pointer)];
|
||||
pointer += 2;
|
||||
const kdf = constants_js_1.TPM_ALG[dataView.getUint16(pointer)];
|
||||
pointer += 2;
|
||||
parameters.ecc = { symmetric, scheme, curveID, kdf };
|
||||
/**
|
||||
* See 11.2.5.1 TPM2B_ECC_PARAMETER here:
|
||||
* https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-00.96-130315.pdf
|
||||
*/
|
||||
// Retrieve X
|
||||
const uniqueXLength = dataView.getUint16(pointer);
|
||||
pointer += 2;
|
||||
const uniqueX = pubArea.slice(pointer, pointer += uniqueXLength);
|
||||
// Retrieve Y
|
||||
const uniqueYLength = dataView.getUint16(pointer);
|
||||
pointer += 2;
|
||||
const uniqueY = pubArea.slice(pointer, pointer += uniqueYLength);
|
||||
unique = index_js_1.isoUint8Array.concat([uniqueX, uniqueY]);
|
||||
}
|
||||
else {
|
||||
throw new Error(`Unexpected type "${type}" (TPM)`);
|
||||
}
|
||||
return {
|
||||
type,
|
||||
nameAlg,
|
||||
objectAttributes,
|
||||
authPolicy,
|
||||
parameters,
|
||||
unique,
|
||||
};
|
||||
}
|
||||
3
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/verifyAttestationTPM.d.ts
generated
vendored
Normal file
3
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/verifyAttestationTPM.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import type { AttestationFormatVerifierOpts } from '../../verifyRegistrationResponse.js';
|
||||
export declare function verifyAttestationTPM(options: AttestationFormatVerifierOpts): Promise<boolean>;
|
||||
//# sourceMappingURL=verifyAttestationTPM.d.ts.map
|
||||
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/verifyAttestationTPM.d.ts.map
generated
vendored
Normal file
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/verifyAttestationTPM.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"verifyAttestationTPM.d.ts","sourceRoot":"","sources":["../../../../src/registration/verifications/tpm/verifyAttestationTPM.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,qCAAqC,CAAC;AAuBzF,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,CA+VlB"}
|
||||
334
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/verifyAttestationTPM.js
generated
vendored
Normal file
334
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/tpm/verifyAttestationTPM.js
generated
vendored
Normal file
@@ -0,0 +1,334 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.verifyAttestationTPM = verifyAttestationTPM;
|
||||
const asn1_schema_1 = require("@peculiar/asn1-schema");
|
||||
const asn1_x509_1 = require("@peculiar/asn1-x509");
|
||||
const decodeCredentialPublicKey_js_1 = require("../../../helpers/decodeCredentialPublicKey.js");
|
||||
const cose_js_1 = require("../../../helpers/cose.js");
|
||||
const toHash_js_1 = require("../../../helpers/toHash.js");
|
||||
const convertCertBufferToPEM_js_1 = require("../../../helpers/convertCertBufferToPEM.js");
|
||||
const validateCertificatePath_js_1 = require("../../../helpers/validateCertificatePath.js");
|
||||
const getCertificateInfo_js_1 = require("../../../helpers/getCertificateInfo.js");
|
||||
const verifySignature_js_1 = require("../../../helpers/verifySignature.js");
|
||||
const index_js_1 = require("../../../helpers/iso/index.js");
|
||||
const validateExtFIDOGenCEAAGUID_js_1 = require("../../../helpers/validateExtFIDOGenCEAAGUID.js");
|
||||
const metadataService_js_1 = require("../../../services/metadataService.js");
|
||||
const verifyAttestationWithMetadata_js_1 = require("../../../metadata/verifyAttestationWithMetadata.js");
|
||||
const constants_js_1 = require("./constants.js");
|
||||
const parseCertInfo_js_1 = require("./parseCertInfo.js");
|
||||
const parsePubArea_js_1 = require("./parsePubArea.js");
|
||||
async function verifyAttestationTPM(options) {
|
||||
const { aaguid, attStmt, authData, credentialPublicKey, clientDataHash, rootCertificates, } = options;
|
||||
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
|
||||
*/
|
||||
if (ver !== '2.0') {
|
||||
throw new Error(`Unexpected ver "${ver}", expected "2.0" (TPM)`);
|
||||
}
|
||||
if (!sig) {
|
||||
throw new Error('No attestation signature provided in attestation statement (TPM)');
|
||||
}
|
||||
if (!alg) {
|
||||
throw new Error(`Attestation statement did not contain alg (TPM)`);
|
||||
}
|
||||
if (!(0, cose_js_1.isCOSEAlg)(alg)) {
|
||||
throw new Error(`Attestation statement contained invalid alg ${alg} (TPM)`);
|
||||
}
|
||||
if (!x5c) {
|
||||
throw new Error('No attestation certificate provided in attestation statement (TPM)');
|
||||
}
|
||||
if (!pubArea) {
|
||||
throw new Error('Attestation statement did not contain pubArea (TPM)');
|
||||
}
|
||||
if (!certInfo) {
|
||||
throw new Error('Attestation statement did not contain certInfo (TPM)');
|
||||
}
|
||||
const parsedPubArea = (0, parsePubArea_js_1.parsePubArea)(pubArea);
|
||||
const { unique, type: pubType, parameters } = parsedPubArea;
|
||||
// Verify that the public key specified by the parameters and unique fields of pubArea is
|
||||
// identical to the credentialPublicKey in the attestedCredentialData in authenticatorData.
|
||||
const cosePublicKey = (0, decodeCredentialPublicKey_js_1.decodeCredentialPublicKey)(credentialPublicKey);
|
||||
if (pubType === 'TPM_ALG_RSA') {
|
||||
if (!(0, cose_js_1.isCOSEPublicKeyRSA)(cosePublicKey)) {
|
||||
throw new Error(`Credential public key with kty ${cosePublicKey.get(cose_js_1.COSEKEYS.kty)} did not match ${pubType}`);
|
||||
}
|
||||
const n = cosePublicKey.get(cose_js_1.COSEKEYS.n);
|
||||
const e = cosePublicKey.get(cose_js_1.COSEKEYS.e);
|
||||
if (!n) {
|
||||
throw new Error('COSE public key missing n (TPM|RSA)');
|
||||
}
|
||||
if (!e) {
|
||||
throw new Error('COSE public key missing e (TPM|RSA)');
|
||||
}
|
||||
if (!index_js_1.isoUint8Array.areEqual(unique, n)) {
|
||||
throw new Error('PubArea unique is not same as credentialPublicKey (TPM|RSA)');
|
||||
}
|
||||
if (!parameters.rsa) {
|
||||
throw new Error(`Parsed pubArea type is RSA, but missing parameters.rsa (TPM|RSA)`);
|
||||
}
|
||||
const eBuffer = e;
|
||||
// If `exponent` is equal to 0x00, then exponent is the default RSA exponent of 2^16+1 (65537)
|
||||
const pubAreaExponent = parameters.rsa.exponent || 65537;
|
||||
// Do some bit shifting to get to an integer
|
||||
const eSum = eBuffer[0] + (eBuffer[1] << 8) + (eBuffer[2] << 16);
|
||||
if (pubAreaExponent !== eSum) {
|
||||
throw new Error(`Unexpected public key exp ${eSum}, expected ${pubAreaExponent} (TPM|RSA)`);
|
||||
}
|
||||
}
|
||||
else if (pubType === 'TPM_ALG_ECC') {
|
||||
if (!(0, cose_js_1.isCOSEPublicKeyEC2)(cosePublicKey)) {
|
||||
throw new Error(`Credential public key with kty ${cosePublicKey.get(cose_js_1.COSEKEYS.kty)} did not match ${pubType}`);
|
||||
}
|
||||
const crv = cosePublicKey.get(cose_js_1.COSEKEYS.crv);
|
||||
const x = cosePublicKey.get(cose_js_1.COSEKEYS.x);
|
||||
const y = cosePublicKey.get(cose_js_1.COSEKEYS.y);
|
||||
if (!crv) {
|
||||
throw new Error('COSE public key missing crv (TPM|ECC)');
|
||||
}
|
||||
if (!x) {
|
||||
throw new Error('COSE public key missing x (TPM|ECC)');
|
||||
}
|
||||
if (!y) {
|
||||
throw new Error('COSE public key missing y (TPM|ECC)');
|
||||
}
|
||||
if (!index_js_1.isoUint8Array.areEqual(unique, index_js_1.isoUint8Array.concat([x, y]))) {
|
||||
throw new Error('PubArea unique is not same as public key x and y (TPM|ECC)');
|
||||
}
|
||||
if (!parameters.ecc) {
|
||||
throw new Error(`Parsed pubArea type is ECC, but missing parameters.ecc (TPM|ECC)`);
|
||||
}
|
||||
const pubAreaCurveID = parameters.ecc.curveID;
|
||||
const pubAreaCurveIDMapToCOSECRV = constants_js_1.TPM_ECC_CURVE_COSE_CRV_MAP[pubAreaCurveID];
|
||||
if (pubAreaCurveIDMapToCOSECRV !== crv) {
|
||||
throw new Error(`Public area key curve ID "${pubAreaCurveID}" mapped to "${pubAreaCurveIDMapToCOSECRV}" which did not match public key crv of "${crv}" (TPM|ECC)`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new Error(`Unsupported pubArea.type "${pubType}"`);
|
||||
}
|
||||
const parsedCertInfo = (0, parseCertInfo_js_1.parseCertInfo)(certInfo);
|
||||
const { magic, type: certType, attested, extraData } = parsedCertInfo;
|
||||
if (magic !== 0xff544347) {
|
||||
throw new Error(`Unexpected magic value "${magic}", expected "0xff544347" (TPM)`);
|
||||
}
|
||||
if (certType !== 'TPM_ST_ATTEST_CERTIFY') {
|
||||
throw new Error(`Unexpected type "${certType}", expected "TPM_ST_ATTEST_CERTIFY" (TPM)`);
|
||||
}
|
||||
// Hash pubArea to create pubAreaHash using the nameAlg in attested
|
||||
const pubAreaHash = await (0, toHash_js_1.toHash)(pubArea, attestedNameAlgToCOSEAlg(attested.nameAlg));
|
||||
// Concatenate attested.nameAlg and pubAreaHash to create attestedName.
|
||||
const attestedName = index_js_1.isoUint8Array.concat([
|
||||
attested.nameAlgBuffer,
|
||||
pubAreaHash,
|
||||
]);
|
||||
// Check that certInfo.attested.name is equals to attestedName.
|
||||
if (!index_js_1.isoUint8Array.areEqual(attested.name, attestedName)) {
|
||||
throw new Error(`Attested name comparison failed (TPM)`);
|
||||
}
|
||||
// Concatenate authData with clientDataHash to create attToBeSigned
|
||||
const attToBeSigned = index_js_1.isoUint8Array.concat([authData, clientDataHash]);
|
||||
// Hash attToBeSigned using the algorithm specified in attStmt.alg to create attToBeSignedHash
|
||||
const attToBeSignedHash = await (0, toHash_js_1.toHash)(attToBeSigned, alg);
|
||||
// Check that certInfo.extraData is equals to attToBeSignedHash.
|
||||
if (!index_js_1.isoUint8Array.areEqual(extraData, attToBeSignedHash)) {
|
||||
throw new Error('CertInfo extra data did not equal hashed attestation (TPM)');
|
||||
}
|
||||
/**
|
||||
* Verify signature
|
||||
*/
|
||||
if (x5c.length < 1) {
|
||||
throw new Error('No certificates present in x5c array (TPM)');
|
||||
}
|
||||
// Pick a leaf AIK certificate of the x5c array and parse it.
|
||||
const leafCertInfo = (0, getCertificateInfo_js_1.getCertificateInfo)(x5c[0]);
|
||||
const { basicConstraintsCA, version, subject, notAfter, notBefore } = leafCertInfo;
|
||||
if (basicConstraintsCA) {
|
||||
throw new Error('Certificate basic constraints CA was not `false` (TPM)');
|
||||
}
|
||||
// Check that certificate is of version 3 (value must be set to 2).
|
||||
if (version !== 2) {
|
||||
throw new Error('Certificate version was not `3` (ASN.1 value of 2) (TPM)');
|
||||
}
|
||||
// Check that Subject sequence is empty.
|
||||
if (subject.combined.length > 0) {
|
||||
throw new Error('Certificate subject was not empty (TPM)');
|
||||
}
|
||||
// Check that certificate is currently valid
|
||||
let now = new Date();
|
||||
if (notBefore > now) {
|
||||
throw new Error(`Certificate not good before "${notBefore.toString()}" (TPM)`);
|
||||
}
|
||||
// Check that certificate has not expired
|
||||
now = new Date();
|
||||
if (notAfter < now) {
|
||||
throw new Error(`Certificate not good after "${notAfter.toString()}" (TPM)`);
|
||||
}
|
||||
/**
|
||||
* Plumb the depths of the certificate's ASN.1-formatted data for some values we need to verify
|
||||
*/
|
||||
const parsedCert = asn1_schema_1.AsnParser.parse(x5c[0], asn1_x509_1.Certificate);
|
||||
if (!parsedCert.tbsCertificate.extensions) {
|
||||
throw new Error('Certificate was missing extensions (TPM)');
|
||||
}
|
||||
let subjectAltNamePresent;
|
||||
let extKeyUsage;
|
||||
parsedCert.tbsCertificate.extensions.forEach((ext) => {
|
||||
if (ext.extnID === asn1_x509_1.id_ce_subjectAltName) {
|
||||
subjectAltNamePresent = asn1_schema_1.AsnParser.parse(ext.extnValue, asn1_x509_1.SubjectAlternativeName);
|
||||
}
|
||||
else if (ext.extnID === asn1_x509_1.id_ce_extKeyUsage) {
|
||||
extKeyUsage = asn1_schema_1.AsnParser.parse(ext.extnValue, asn1_x509_1.ExtendedKeyUsage);
|
||||
}
|
||||
});
|
||||
// Check that certificate contains subjectAltName (2.5.29.17) extension,
|
||||
if (!subjectAltNamePresent) {
|
||||
throw new Error('Certificate did not contain subjectAltName extension (TPM)');
|
||||
}
|
||||
// TPM-specific values are buried within `directoryName`, so first make sure there are values
|
||||
// there.
|
||||
if (!subjectAltNamePresent[0].directoryName?.[0].length) {
|
||||
throw new Error('Certificate subjectAltName extension directoryName was empty (TPM)');
|
||||
}
|
||||
const { tcgAtTpmManufacturer, tcgAtTpmModel, tcgAtTpmVersion } = getTcgAtTpmValues(subjectAltNamePresent[0].directoryName);
|
||||
if (!tcgAtTpmManufacturer || !tcgAtTpmModel || !tcgAtTpmVersion) {
|
||||
throw new Error('Certificate contained incomplete subjectAltName data (TPM)');
|
||||
}
|
||||
if (!extKeyUsage) {
|
||||
throw new Error('Certificate did not contain ExtendedKeyUsage extension (TPM)');
|
||||
}
|
||||
// Check that tcpaTpmManufacturer (2.23.133.2.1) field is set to a valid manufacturer ID.
|
||||
if (!constants_js_1.TPM_MANUFACTURERS[tcgAtTpmManufacturer]) {
|
||||
throw new Error(`Could not match TPM manufacturer "${tcgAtTpmManufacturer}" (TPM)`);
|
||||
}
|
||||
// Check that certificate contains extKeyUsage (2.5.29.37) extension and it must contain
|
||||
// tcg-kp-AIKCertificate (2.23.133.8.3) OID.
|
||||
if (extKeyUsage[0] !== '2.23.133.8.3') {
|
||||
throw new Error(`Unexpected extKeyUsage "${extKeyUsage[0]}", expected "2.23.133.8.3" (TPM)`);
|
||||
}
|
||||
// Validate attestation statement AAGUID against leaf cert AAGUID
|
||||
try {
|
||||
await (0, validateExtFIDOGenCEAAGUID_js_1.validateExtFIDOGenCEAAGUID)(parsedCert.tbsCertificate.extensions, aaguid);
|
||||
}
|
||||
catch (err) {
|
||||
const _err = err;
|
||||
throw new Error(`${_err.message} (TPM)`);
|
||||
}
|
||||
// Run some metadata checks if a statement exists for this authenticator
|
||||
const statement = await metadataService_js_1.MetadataService.getStatement(aaguid);
|
||||
if (statement) {
|
||||
try {
|
||||
await (0, verifyAttestationWithMetadata_js_1.verifyAttestationWithMetadata)({
|
||||
statement,
|
||||
credentialPublicKey,
|
||||
x5c,
|
||||
attestationStatementAlg: alg,
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
const _err = err;
|
||||
throw new Error(`${_err.message} (TPM)`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
// Try validating the certificate path using the root certificates set via SettingsService
|
||||
await (0, validateCertificatePath_js_1.validateCertificatePath)(x5c.map(convertCertBufferToPEM_js_1.convertCertBufferToPEM), rootCertificates);
|
||||
}
|
||||
catch (err) {
|
||||
const _err = err;
|
||||
throw new Error(`${_err.message} (TPM)`);
|
||||
}
|
||||
}
|
||||
// Verify signature over certInfo with the public key extracted from AIK certificate.
|
||||
// In the wise words of Yuriy Ackermann: "Get Martini friend, you are done!"
|
||||
return (0, verifySignature_js_1.verifySignature)({
|
||||
signature: sig,
|
||||
data: certInfo,
|
||||
x509Certificate: x5c[0],
|
||||
hashAlgorithm: alg,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Contain logic for pulling TPM-specific values out of subjectAlternativeName extension
|
||||
*/
|
||||
function getTcgAtTpmValues(root) {
|
||||
const oidManufacturer = '2.23.133.2.1';
|
||||
const oidModel = '2.23.133.2.2';
|
||||
const oidVersion = '2.23.133.2.3';
|
||||
let tcgAtTpmManufacturer;
|
||||
let tcgAtTpmModel;
|
||||
let tcgAtTpmVersion;
|
||||
/**
|
||||
* Iterate through the following potential structures:
|
||||
*
|
||||
* (Good, follows the spec)
|
||||
* https://trustedcomputinggroup.org/wp-content/uploads/TCG_IWG_EKCredentialProfile_v2p3_r2_pub.pdf (page 33)
|
||||
* Name [
|
||||
* RelativeDistinguishedName [
|
||||
* AttributeTypeAndValue { type, value }
|
||||
* ]
|
||||
* RelativeDistinguishedName [
|
||||
* AttributeTypeAndValue { type, value }
|
||||
* ]
|
||||
* RelativeDistinguishedName [
|
||||
* AttributeTypeAndValue { type, value }
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* (Bad, does not follow the spec)
|
||||
* Name [
|
||||
* RelativeDistinguishedName [
|
||||
* AttributeTypeAndValue { type, value }
|
||||
* AttributeTypeAndValue { type, value }
|
||||
* AttributeTypeAndValue { type, value }
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* Both structures have been seen in the wild and need to be supported
|
||||
*/
|
||||
root.forEach((relName) => {
|
||||
relName.forEach((attr) => {
|
||||
if (attr.type === oidManufacturer) {
|
||||
tcgAtTpmManufacturer = attr.value.toString();
|
||||
}
|
||||
else if (attr.type === oidModel) {
|
||||
tcgAtTpmModel = attr.value.toString();
|
||||
}
|
||||
else if (attr.type === oidVersion) {
|
||||
tcgAtTpmVersion = attr.value.toString();
|
||||
}
|
||||
});
|
||||
});
|
||||
return {
|
||||
tcgAtTpmManufacturer,
|
||||
tcgAtTpmModel,
|
||||
tcgAtTpmVersion,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert TPM-specific SHA algorithm ID's with COSE-specific equivalents. Note that the choice to
|
||||
* use ECDSA SHA IDs is arbitrary; any such COSEALG that would map to SHA-256 in
|
||||
* `mapCoseAlgToWebCryptoAlg()`
|
||||
*
|
||||
* SHA IDs referenced from here:
|
||||
*
|
||||
* https://trustedcomputinggroup.org/wp-content/uploads/TCG_TPM2_r1p59_Part2_Structures_pub.pdf
|
||||
*/
|
||||
function attestedNameAlgToCOSEAlg(alg) {
|
||||
if (alg === 'TPM_ALG_SHA256') {
|
||||
return cose_js_1.COSEALG.ES256;
|
||||
}
|
||||
else if (alg === 'TPM_ALG_SHA384') {
|
||||
return cose_js_1.COSEALG.ES384;
|
||||
}
|
||||
else if (alg === 'TPM_ALG_SHA512') {
|
||||
return cose_js_1.COSEALG.ES512;
|
||||
}
|
||||
throw new Error(`Unexpected TPM attested name alg ${alg}`);
|
||||
}
|
||||
6
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationAndroidKey.d.ts
generated
vendored
Normal file
6
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationAndroidKey.d.ts
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { AttestationFormatVerifierOpts } from '../verifyRegistrationResponse.js';
|
||||
/**
|
||||
* Verify an attestation response with fmt 'android-key'
|
||||
*/
|
||||
export declare function verifyAttestationAndroidKey(options: AttestationFormatVerifierOpts): Promise<boolean>;
|
||||
//# sourceMappingURL=verifyAttestationAndroidKey.d.ts.map
|
||||
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationAndroidKey.d.ts.map
generated
vendored
Normal file
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationAndroidKey.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"verifyAttestationAndroidKey.d.ts","sourceRoot":"","sources":["../../../src/registration/verifications/verifyAttestationAndroidKey.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AAUtF;;GAEG;AACH,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,CA0JlB"}
|
||||
123
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationAndroidKey.js
generated
vendored
Normal file
123
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationAndroidKey.js
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.verifyAttestationAndroidKey = verifyAttestationAndroidKey;
|
||||
const asn1_schema_1 = require("@peculiar/asn1-schema");
|
||||
const asn1_x509_1 = require("@peculiar/asn1-x509");
|
||||
const asn1_android_1 = require("@peculiar/asn1-android");
|
||||
const convertCertBufferToPEM_js_1 = require("../../helpers/convertCertBufferToPEM.js");
|
||||
const validateCertificatePath_js_1 = require("../../helpers/validateCertificatePath.js");
|
||||
const verifySignature_js_1 = require("../../helpers/verifySignature.js");
|
||||
const convertCOSEtoPKCS_js_1 = require("../../helpers/convertCOSEtoPKCS.js");
|
||||
const cose_js_1 = require("../../helpers/cose.js");
|
||||
const index_js_1 = require("../../helpers/iso/index.js");
|
||||
const metadataService_js_1 = require("../../services/metadataService.js");
|
||||
const verifyAttestationWithMetadata_js_1 = require("../../metadata/verifyAttestationWithMetadata.js");
|
||||
/**
|
||||
* Verify an attestation response with fmt 'android-key'
|
||||
*/
|
||||
async function verifyAttestationAndroidKey(options) {
|
||||
const { authData, clientDataHash, attStmt, credentialPublicKey, aaguid, rootCertificates, } = options;
|
||||
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 (Android Key)');
|
||||
}
|
||||
if (!sig) {
|
||||
throw new Error('No attestation signature provided in attestation statement (Android Key)');
|
||||
}
|
||||
if (!alg) {
|
||||
throw new Error(`Attestation statement did not contain alg (Android Key)`);
|
||||
}
|
||||
if (!(0, cose_js_1.isCOSEAlg)(alg)) {
|
||||
throw new Error(`Attestation statement contained invalid alg ${alg} (Android Key)`);
|
||||
}
|
||||
/**
|
||||
* Verify that the public key in the first certificate in x5c matches the credentialPublicKey in
|
||||
* the attestedCredentialData in authenticatorData.
|
||||
*/
|
||||
// Find the public cert in the certificate as PKCS
|
||||
const parsedCert = asn1_schema_1.AsnParser.parse(x5c[0], asn1_x509_1.Certificate);
|
||||
const parsedCertPubKey = new Uint8Array(parsedCert.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey);
|
||||
// Convert the credentialPublicKey to PKCS
|
||||
const credPubKeyPKCS = (0, convertCOSEtoPKCS_js_1.convertCOSEtoPKCS)(credentialPublicKey);
|
||||
if (!index_js_1.isoUint8Array.areEqual(credPubKeyPKCS, parsedCertPubKey)) {
|
||||
throw new Error('Credential public key does not equal leaf cert public key (Android Key)');
|
||||
}
|
||||
/**
|
||||
* Verify that the attestationChallenge field in the attestation certificate extension data is
|
||||
* identical to clientDataHash.
|
||||
*/
|
||||
// Find Android KeyStore Extension in certificate extensions
|
||||
const extKeyStore = parsedCert.tbsCertificate.extensions?.find((ext) => ext.extnID === asn1_android_1.id_ce_keyDescription);
|
||||
if (!extKeyStore) {
|
||||
throw new Error('Certificate did not contain extKeyStore (Android Key)');
|
||||
}
|
||||
const parsedExtKeyStore = asn1_schema_1.AsnParser.parse(extKeyStore.extnValue, asn1_android_1.KeyDescription);
|
||||
// Verify extKeyStore values
|
||||
const { attestationChallenge, teeEnforced, softwareEnforced } = parsedExtKeyStore;
|
||||
if (!index_js_1.isoUint8Array.areEqual(new Uint8Array(attestationChallenge.buffer), clientDataHash)) {
|
||||
throw new Error('Attestation challenge was not equal to client data hash (Android Key)');
|
||||
}
|
||||
/**
|
||||
* The AuthorizationList.allApplications field is not present on either authorization list
|
||||
* (softwareEnforced nor teeEnforced), since PublicKeyCredential MUST be scoped to the RP ID.
|
||||
*
|
||||
* (i.e. These shouldn't contain the [600] tag)
|
||||
*/
|
||||
if (teeEnforced.allApplications !== undefined) {
|
||||
throw new Error('teeEnforced contained "allApplications [600]" tag (Android Key)');
|
||||
}
|
||||
if (softwareEnforced.allApplications !== undefined) {
|
||||
throw new Error('teeEnforced contained "allApplications [600]" tag (Android Key)');
|
||||
}
|
||||
const statement = await metadataService_js_1.MetadataService.getStatement(aaguid);
|
||||
if (statement) {
|
||||
try {
|
||||
await (0, verifyAttestationWithMetadata_js_1.verifyAttestationWithMetadata)({
|
||||
statement,
|
||||
credentialPublicKey,
|
||||
x5c,
|
||||
attestationStatementAlg: alg,
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
const _err = err;
|
||||
throw new Error(`${_err.message} (Android Key)`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* Verify that x5c contains a full certificate path.
|
||||
*/
|
||||
const x5cNoRootPEM = x5c.slice(0, -1).map(convertCertBufferToPEM_js_1.convertCertBufferToPEM);
|
||||
const x5cRootPEM = x5c.slice(-1).map(convertCertBufferToPEM_js_1.convertCertBufferToPEM);
|
||||
try {
|
||||
await (0, validateCertificatePath_js_1.validateCertificatePath)(x5cNoRootPEM, x5cRootPEM);
|
||||
}
|
||||
catch (err) {
|
||||
const _err = err;
|
||||
throw new Error(`${_err.message} (Android Key)`);
|
||||
}
|
||||
/**
|
||||
* Make sure the root certificate is one of the Google Hardware Attestation Root certificates
|
||||
*
|
||||
* https://developer.android.com/privacy-and-security/security-key-attestation#root_certificate
|
||||
*/
|
||||
if (rootCertificates.length > 0 && rootCertificates.indexOf(x5cRootPEM[0]) < 0) {
|
||||
throw new Error('x5c root certificate was not a known root certificate (Android Key)');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Verify that sig is a valid signature over the concatenation of authenticatorData and
|
||||
* clientDataHash using the public key in the first certificate in x5c with the algorithm
|
||||
* specified in alg.
|
||||
*/
|
||||
const signatureBase = index_js_1.isoUint8Array.concat([authData, clientDataHash]);
|
||||
return (0, verifySignature_js_1.verifySignature)({
|
||||
signature: sig,
|
||||
data: signatureBase,
|
||||
x509Certificate: x5c[0],
|
||||
hashAlgorithm: alg,
|
||||
});
|
||||
}
|
||||
6
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationAndroidSafetyNet.d.ts
generated
vendored
Normal file
6
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationAndroidSafetyNet.d.ts
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { AttestationFormatVerifierOpts } from '../verifyRegistrationResponse.js';
|
||||
/**
|
||||
* Verify an attestation response with fmt 'android-safetynet'
|
||||
*/
|
||||
export declare function verifyAttestationAndroidSafetyNet(options: AttestationFormatVerifierOpts): Promise<boolean>;
|
||||
//# sourceMappingURL=verifyAttestationAndroidSafetyNet.d.ts.map
|
||||
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationAndroidSafetyNet.d.ts.map
generated
vendored
Normal file
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationAndroidSafetyNet.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"verifyAttestationAndroidSafetyNet.d.ts","sourceRoot":"","sources":["../../../src/registration/verifications/verifyAttestationAndroidSafetyNet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AAWtF;;GAEG;AACH,wBAAsB,iCAAiC,CACrD,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,CA2IlB"}
|
||||
115
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationAndroidSafetyNet.js
generated
vendored
Normal file
115
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationAndroidSafetyNet.js
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.verifyAttestationAndroidSafetyNet = verifyAttestationAndroidSafetyNet;
|
||||
const toHash_js_1 = require("../../helpers/toHash.js");
|
||||
const verifySignature_js_1 = require("../../helpers/verifySignature.js");
|
||||
const getCertificateInfo_js_1 = require("../../helpers/getCertificateInfo.js");
|
||||
const validateCertificatePath_js_1 = require("../../helpers/validateCertificatePath.js");
|
||||
const convertCertBufferToPEM_js_1 = require("../../helpers/convertCertBufferToPEM.js");
|
||||
const index_js_1 = require("../../helpers/iso/index.js");
|
||||
const metadataService_js_1 = require("../../services/metadataService.js");
|
||||
const verifyAttestationWithMetadata_js_1 = require("../../metadata/verifyAttestationWithMetadata.js");
|
||||
/**
|
||||
* Verify an attestation response with fmt 'android-safetynet'
|
||||
*/
|
||||
async function verifyAttestationAndroidSafetyNet(options) {
|
||||
const { attStmt, clientDataHash, authData, aaguid, rootCertificates, verifyTimestampMS = true, credentialPublicKey, } = options;
|
||||
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)');
|
||||
}
|
||||
if (!response) {
|
||||
throw new Error('No response was included in attStmt by authenticator (SafetyNet)');
|
||||
}
|
||||
// Prepare to verify a JWT
|
||||
const jwt = index_js_1.isoUint8Array.toUTF8String(response);
|
||||
const jwtParts = jwt.split('.');
|
||||
const HEADER = JSON.parse(index_js_1.isoBase64URL.toUTF8String(jwtParts[0]));
|
||||
const PAYLOAD = JSON.parse(index_js_1.isoBase64URL.toUTF8String(jwtParts[1]));
|
||||
const SIGNATURE = jwtParts[2];
|
||||
/**
|
||||
* START Verify PAYLOAD
|
||||
*/
|
||||
const { nonce, ctsProfileMatch, timestampMs } = PAYLOAD;
|
||||
if (verifyTimestampMS) {
|
||||
// Make sure timestamp is in the past
|
||||
let now = Date.now();
|
||||
if (timestampMs > Date.now()) {
|
||||
throw new Error(`Payload timestamp "${timestampMs}" was later than "${now}" (SafetyNet)`);
|
||||
}
|
||||
// Consider a SafetyNet attestation valid within a minute of it being performed
|
||||
const timestampPlusDelay = timestampMs + 60 * 1000;
|
||||
now = Date.now();
|
||||
if (timestampPlusDelay < now) {
|
||||
throw new Error(`Payload timestamp "${timestampPlusDelay}" has expired (SafetyNet)`);
|
||||
}
|
||||
}
|
||||
const nonceBase = index_js_1.isoUint8Array.concat([authData, clientDataHash]);
|
||||
const nonceBuffer = await (0, toHash_js_1.toHash)(nonceBase);
|
||||
const expectedNonce = index_js_1.isoBase64URL.fromBuffer(nonceBuffer, 'base64');
|
||||
if (nonce !== expectedNonce) {
|
||||
throw new Error('Could not verify payload nonce (SafetyNet)');
|
||||
}
|
||||
if (!ctsProfileMatch) {
|
||||
throw new Error('Could not verify device integrity (SafetyNet)');
|
||||
}
|
||||
/**
|
||||
* END Verify PAYLOAD
|
||||
*/
|
||||
/**
|
||||
* START Verify Header
|
||||
*/
|
||||
// `HEADER.x5c[0]` is definitely a base64 string
|
||||
const leafCertBuffer = index_js_1.isoBase64URL.toBuffer(HEADER.x5c[0], 'base64');
|
||||
const leafCertInfo = (0, getCertificateInfo_js_1.getCertificateInfo)(leafCertBuffer);
|
||||
const { subject } = leafCertInfo;
|
||||
// Ensure the certificate was issued to this hostname
|
||||
// See https://developer.android.com/training/safetynet/attestation#verify-attestation-response
|
||||
if (subject.CN !== 'attest.android.com') {
|
||||
throw new Error('Certificate common name was not "attest.android.com" (SafetyNet)');
|
||||
}
|
||||
const statement = await metadataService_js_1.MetadataService.getStatement(aaguid);
|
||||
if (statement) {
|
||||
try {
|
||||
await (0, verifyAttestationWithMetadata_js_1.verifyAttestationWithMetadata)({
|
||||
statement,
|
||||
credentialPublicKey,
|
||||
x5c: HEADER.x5c,
|
||||
attestationStatementAlg: alg,
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
const _err = err;
|
||||
throw new Error(`${_err.message} (SafetyNet)`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
// Try validating the certificate path using the root certificates set via SettingsService
|
||||
await (0, validateCertificatePath_js_1.validateCertificatePath)(HEADER.x5c.map(convertCertBufferToPEM_js_1.convertCertBufferToPEM), rootCertificates);
|
||||
}
|
||||
catch (err) {
|
||||
const _err = err;
|
||||
throw new Error(`${_err.message} (SafetyNet)`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* END Verify Header
|
||||
*/
|
||||
/**
|
||||
* START Verify Signature
|
||||
*/
|
||||
const signatureBaseBuffer = index_js_1.isoUint8Array.fromUTF8String(`${jwtParts[0]}.${jwtParts[1]}`);
|
||||
const signatureBuffer = index_js_1.isoBase64URL.toBuffer(SIGNATURE);
|
||||
const verified = await (0, verifySignature_js_1.verifySignature)({
|
||||
signature: signatureBuffer,
|
||||
data: signatureBaseBuffer,
|
||||
x509Certificate: leafCertBuffer,
|
||||
});
|
||||
/**
|
||||
* END Verify Signature
|
||||
*/
|
||||
return verified;
|
||||
}
|
||||
3
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationApple.d.ts
generated
vendored
Normal file
3
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationApple.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import type { AttestationFormatVerifierOpts } from '../verifyRegistrationResponse.js';
|
||||
export declare function verifyAttestationApple(options: AttestationFormatVerifierOpts): Promise<boolean>;
|
||||
//# sourceMappingURL=verifyAttestationApple.d.ts.map
|
||||
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationApple.d.ts.map
generated
vendored
Normal file
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationApple.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"verifyAttestationApple.d.ts","sourceRoot":"","sources":["../../../src/registration/verifications/verifyAttestationApple.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AAOtF,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,CA6ElB"}
|
||||
61
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationApple.js
generated
vendored
Normal file
61
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationApple.js
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.verifyAttestationApple = verifyAttestationApple;
|
||||
const asn1_schema_1 = require("@peculiar/asn1-schema");
|
||||
const asn1_x509_1 = require("@peculiar/asn1-x509");
|
||||
const validateCertificatePath_js_1 = require("../../helpers/validateCertificatePath.js");
|
||||
const convertCertBufferToPEM_js_1 = require("../../helpers/convertCertBufferToPEM.js");
|
||||
const toHash_js_1 = require("../../helpers/toHash.js");
|
||||
const convertCOSEtoPKCS_js_1 = require("../../helpers/convertCOSEtoPKCS.js");
|
||||
const index_js_1 = require("../../helpers/iso/index.js");
|
||||
async function verifyAttestationApple(options) {
|
||||
const { attStmt, authData, clientDataHash, credentialPublicKey, rootCertificates, } = options;
|
||||
const x5c = attStmt.get('x5c');
|
||||
if (!x5c) {
|
||||
throw new Error('No attestation certificate provided in attestation statement (Apple)');
|
||||
}
|
||||
/**
|
||||
* Verify certificate path
|
||||
*/
|
||||
try {
|
||||
await (0, validateCertificatePath_js_1.validateCertificatePath)(x5c.map(convertCertBufferToPEM_js_1.convertCertBufferToPEM), rootCertificates);
|
||||
}
|
||||
catch (err) {
|
||||
const _err = err;
|
||||
throw new Error(`${_err.message} (Apple)`);
|
||||
}
|
||||
/**
|
||||
* Compare nonce in certificate extension to computed nonce
|
||||
*/
|
||||
const parsedCredCert = asn1_schema_1.AsnParser.parse(x5c[0], asn1_x509_1.Certificate);
|
||||
const { extensions, subjectPublicKeyInfo } = parsedCredCert.tbsCertificate;
|
||||
if (!extensions) {
|
||||
throw new Error('credCert missing extensions (Apple)');
|
||||
}
|
||||
const extCertNonce = extensions.find((ext) => ext.extnID === '1.2.840.113635.100.8.2');
|
||||
if (!extCertNonce) {
|
||||
throw new Error('credCert missing "1.2.840.113635.100.8.2" extension (Apple)');
|
||||
}
|
||||
const nonceToHash = index_js_1.isoUint8Array.concat([authData, clientDataHash]);
|
||||
const nonce = await (0, toHash_js_1.toHash)(nonceToHash);
|
||||
/**
|
||||
* Ignore the first six ASN.1 structure bytes that define the nonce as an OCTET STRING. Should
|
||||
* trim off <Buffer 30 24 a1 22 04 20>
|
||||
*
|
||||
* TODO: Try and get @peculiar (GitHub) to add a schema for "1.2.840.113635.100.8.2" when we
|
||||
* find out where it's defined (doesn't seem to be publicly documented at the moment...)
|
||||
*/
|
||||
const extNonce = new Uint8Array(extCertNonce.extnValue.buffer).slice(6);
|
||||
if (!index_js_1.isoUint8Array.areEqual(nonce, extNonce)) {
|
||||
throw new Error(`credCert nonce was not expected value (Apple)`);
|
||||
}
|
||||
/**
|
||||
* Verify credential public key matches the Subject Public Key of credCert
|
||||
*/
|
||||
const credPubKeyPKCS = (0, convertCOSEtoPKCS_js_1.convertCOSEtoPKCS)(credentialPublicKey);
|
||||
const credCertSubjectPublicKey = new Uint8Array(subjectPublicKeyInfo.subjectPublicKey);
|
||||
if (!index_js_1.isoUint8Array.areEqual(credPubKeyPKCS, credCertSubjectPublicKey)) {
|
||||
throw new Error('Credential public key does not equal credCert public key (Apple)');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
6
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationFIDOU2F.d.ts
generated
vendored
Normal file
6
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationFIDOU2F.d.ts
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { AttestationFormatVerifierOpts } from '../verifyRegistrationResponse.js';
|
||||
/**
|
||||
* Verify an attestation response with fmt 'fido-u2f'
|
||||
*/
|
||||
export declare function verifyAttestationFIDOU2F(options: AttestationFormatVerifierOpts): Promise<boolean>;
|
||||
//# sourceMappingURL=verifyAttestationFIDOU2F.d.ts.map
|
||||
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationFIDOU2F.d.ts.map
generated
vendored
Normal file
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationFIDOU2F.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"verifyAttestationFIDOU2F.d.ts","sourceRoot":"","sources":["../../../src/registration/verifications/verifyAttestationFIDOU2F.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AAStF;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,CA4DlB"}
|
||||
51
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationFIDOU2F.js
generated
vendored
Normal file
51
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationFIDOU2F.js
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.verifyAttestationFIDOU2F = verifyAttestationFIDOU2F;
|
||||
const convertCOSEtoPKCS_js_1 = require("../../helpers/convertCOSEtoPKCS.js");
|
||||
const convertCertBufferToPEM_js_1 = require("../../helpers/convertCertBufferToPEM.js");
|
||||
const validateCertificatePath_js_1 = require("../../helpers/validateCertificatePath.js");
|
||||
const verifySignature_js_1 = require("../../helpers/verifySignature.js");
|
||||
const index_js_1 = require("../../helpers/iso/index.js");
|
||||
const cose_js_1 = require("../../helpers/cose.js");
|
||||
/**
|
||||
* Verify an attestation response with fmt 'fido-u2f'
|
||||
*/
|
||||
async function verifyAttestationFIDOU2F(options) {
|
||||
const { attStmt, clientDataHash, rpIdHash, credentialID, credentialPublicKey, aaguid, rootCertificates, } = options;
|
||||
const reservedByte = Uint8Array.from([0x00]);
|
||||
const publicKey = (0, convertCOSEtoPKCS_js_1.convertCOSEtoPKCS)(credentialPublicKey);
|
||||
const signatureBase = index_js_1.isoUint8Array.concat([
|
||||
reservedByte,
|
||||
rpIdHash,
|
||||
clientDataHash,
|
||||
credentialID,
|
||||
publicKey,
|
||||
]);
|
||||
const sig = attStmt.get('sig');
|
||||
const x5c = attStmt.get('x5c');
|
||||
if (!x5c) {
|
||||
throw new Error('No attestation certificate provided in attestation statement (FIDOU2F)');
|
||||
}
|
||||
if (!sig) {
|
||||
throw new Error('No attestation signature provided in attestation statement (FIDOU2F)');
|
||||
}
|
||||
// FIDO spec says that aaguid _must_ equal 0x00 here to be legit
|
||||
const aaguidToHex = Number.parseInt(index_js_1.isoUint8Array.toHex(aaguid), 16);
|
||||
if (aaguidToHex !== 0x00) {
|
||||
throw new Error(`AAGUID "${aaguidToHex}" was not expected value`);
|
||||
}
|
||||
try {
|
||||
// Try validating the certificate path using the root certificates set via SettingsService
|
||||
await (0, validateCertificatePath_js_1.validateCertificatePath)(x5c.map(convertCertBufferToPEM_js_1.convertCertBufferToPEM), rootCertificates);
|
||||
}
|
||||
catch (err) {
|
||||
const _err = err;
|
||||
throw new Error(`${_err.message} (FIDOU2F)`);
|
||||
}
|
||||
return (0, verifySignature_js_1.verifySignature)({
|
||||
signature: sig,
|
||||
data: signatureBase,
|
||||
x509Certificate: x5c[0],
|
||||
hashAlgorithm: cose_js_1.COSEALG.ES256,
|
||||
});
|
||||
}
|
||||
6
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationPacked.d.ts
generated
vendored
Normal file
6
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationPacked.d.ts
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { AttestationFormatVerifierOpts } from '../verifyRegistrationResponse.js';
|
||||
/**
|
||||
* Verify an attestation response with fmt 'packed'
|
||||
*/
|
||||
export declare function verifyAttestationPacked(options: AttestationFormatVerifierOpts): Promise<boolean>;
|
||||
//# sourceMappingURL=verifyAttestationPacked.d.ts.map
|
||||
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationPacked.d.ts.map
generated
vendored
Normal file
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationPacked.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"verifyAttestationPacked.d.ts","sourceRoot":"","sources":["../../../src/registration/verifications/verifyAttestationPacked.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AAYtF;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,CAyJlB"}
|
||||
115
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationPacked.js
generated
vendored
Normal file
115
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifications/verifyAttestationPacked.js
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.verifyAttestationPacked = verifyAttestationPacked;
|
||||
const cose_js_1 = require("../../helpers/cose.js");
|
||||
const convertCertBufferToPEM_js_1 = require("../../helpers/convertCertBufferToPEM.js");
|
||||
const validateCertificatePath_js_1 = require("../../helpers/validateCertificatePath.js");
|
||||
const getCertificateInfo_js_1 = require("../../helpers/getCertificateInfo.js");
|
||||
const verifySignature_js_1 = require("../../helpers/verifySignature.js");
|
||||
const index_js_1 = require("../../helpers/iso/index.js");
|
||||
const validateExtFIDOGenCEAAGUID_js_1 = require("../../helpers/validateExtFIDOGenCEAAGUID.js");
|
||||
const metadataService_js_1 = require("../../services/metadataService.js");
|
||||
const verifyAttestationWithMetadata_js_1 = require("../../metadata/verifyAttestationWithMetadata.js");
|
||||
/**
|
||||
* Verify an attestation response with fmt 'packed'
|
||||
*/
|
||||
async function verifyAttestationPacked(options) {
|
||||
const { attStmt, clientDataHash, authData, credentialPublicKey, aaguid, rootCertificates, } = options;
|
||||
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)');
|
||||
}
|
||||
if (!alg) {
|
||||
throw new Error('Attestation statement did not contain alg (Packed)');
|
||||
}
|
||||
if (!(0, cose_js_1.isCOSEAlg)(alg)) {
|
||||
throw new Error(`Attestation statement contained invalid alg ${alg} (Packed)`);
|
||||
}
|
||||
const signatureBase = index_js_1.isoUint8Array.concat([authData, clientDataHash]);
|
||||
let verified = false;
|
||||
if (x5c) {
|
||||
const { subject, basicConstraintsCA, version, notBefore, notAfter, parsedCertificate, } = (0, getCertificateInfo_js_1.getCertificateInfo)(x5c[0]);
|
||||
const { OU, CN, O, C } = subject;
|
||||
if (OU !== 'Authenticator Attestation') {
|
||||
throw new Error('Certificate OU was not "Authenticator Attestation" (Packed|Full)');
|
||||
}
|
||||
if (!CN) {
|
||||
throw new Error('Certificate CN was empty (Packed|Full)');
|
||||
}
|
||||
if (!O) {
|
||||
throw new Error('Certificate O was empty (Packed|Full)');
|
||||
}
|
||||
if (!C || C.length !== 2) {
|
||||
throw new Error('Certificate C was not two-character ISO 3166 code (Packed|Full)');
|
||||
}
|
||||
if (basicConstraintsCA) {
|
||||
throw new Error('Certificate basic constraints CA was not `false` (Packed|Full)');
|
||||
}
|
||||
if (version !== 2) {
|
||||
throw new Error('Certificate version was not `3` (ASN.1 value of 2) (Packed|Full)');
|
||||
}
|
||||
let now = new Date();
|
||||
if (notBefore > now) {
|
||||
throw new Error(`Certificate not good before "${notBefore.toString()}" (Packed|Full)`);
|
||||
}
|
||||
now = new Date();
|
||||
if (notAfter < now) {
|
||||
throw new Error(`Certificate not good after "${notAfter.toString()}" (Packed|Full)`);
|
||||
}
|
||||
// Validate attestation statement AAGUID against leaf cert AAGUID
|
||||
try {
|
||||
await (0, validateExtFIDOGenCEAAGUID_js_1.validateExtFIDOGenCEAAGUID)(parsedCertificate.tbsCertificate.extensions, aaguid);
|
||||
}
|
||||
catch (err) {
|
||||
const _err = err;
|
||||
throw new Error(`${_err.message} (Packed|Full)`);
|
||||
}
|
||||
// If available, validate attestation alg and x5c with info in the metadata statement
|
||||
const statement = await metadataService_js_1.MetadataService.getStatement(aaguid);
|
||||
if (statement) {
|
||||
// The presence of x5c means this is a full attestation. Check to see if attestationTypes
|
||||
// includes packed attestations.
|
||||
if (statement.attestationTypes.indexOf('basic_full') < 0) {
|
||||
throw new Error('Metadata does not indicate support for full attestations (Packed|Full)');
|
||||
}
|
||||
try {
|
||||
await (0, verifyAttestationWithMetadata_js_1.verifyAttestationWithMetadata)({
|
||||
statement,
|
||||
credentialPublicKey,
|
||||
x5c,
|
||||
attestationStatementAlg: alg,
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
const _err = err;
|
||||
throw new Error(`${_err.message} (Packed|Full)`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
// Try validating the certificate path using the root certificates set via SettingsService
|
||||
await (0, validateCertificatePath_js_1.validateCertificatePath)(x5c.map(convertCertBufferToPEM_js_1.convertCertBufferToPEM), rootCertificates);
|
||||
}
|
||||
catch (err) {
|
||||
const _err = err;
|
||||
throw new Error(`${_err.message} (Packed|Full)`);
|
||||
}
|
||||
}
|
||||
verified = await (0, verifySignature_js_1.verifySignature)({
|
||||
signature: sig,
|
||||
data: signatureBase,
|
||||
x509Certificate: x5c[0],
|
||||
});
|
||||
}
|
||||
else {
|
||||
verified = await (0, verifySignature_js_1.verifySignature)({
|
||||
signature: sig,
|
||||
data: signatureBase,
|
||||
credentialPublicKey,
|
||||
hashAlgorithm: alg,
|
||||
});
|
||||
}
|
||||
return verified;
|
||||
}
|
||||
88
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifyRegistrationResponse.d.ts
generated
vendored
Normal file
88
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifyRegistrationResponse.d.ts
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
import type { COSEAlgorithmIdentifier, CredentialDeviceType, RegistrationResponseJSON, WebAuthnCredential } from '../types/index.js';
|
||||
import { type AttestationFormat, type AttestationStatement } from '../helpers/decodeAttestationObject.js';
|
||||
import type { AuthenticationExtensionsAuthenticatorOutputs } from '../helpers/decodeAuthenticatorExtensions.js';
|
||||
/**
|
||||
* Configurable options when calling `verifyRegistrationResponse()`
|
||||
*/
|
||||
export type VerifyRegistrationResponseOpts = Parameters<typeof verifyRegistrationResponse>[0];
|
||||
/**
|
||||
* Verify that the user has legitimately completed the registration process
|
||||
*
|
||||
* **Options:**
|
||||
*
|
||||
* @param response - Response returned by **@simplewebauthn/browser**'s `startAuthentication()`
|
||||
* @param expectedChallenge - The base64url-encoded `options.challenge` returned by `generateRegistrationOptions()`
|
||||
* @param expectedOrigin - Website URL (or array of URLs) that the registration should have occurred on
|
||||
* @param expectedRPID - RP ID (or array of IDs) that was specified in the registration options
|
||||
* @param expectedType **(Optional)** - The response type expected ('webauthn.create')
|
||||
* @param requireUserPresence **(Optional)** - Enforce user presence by the authenticator (or skip it during auto registration) Defaults to `true`
|
||||
* @param requireUserVerification **(Optional)** - Enforce user verification by the authenticator (via PIN, fingerprint, etc...) Defaults to `true`
|
||||
* @param supportedAlgorithmIDs **(Optional)** - Array of numeric COSE algorithm identifiers supported for attestation by this RP. See https://www.iana.org/assignments/cose/cose.xhtml#algorithms. Defaults to all supported algorithm IDs
|
||||
*/
|
||||
export declare function verifyRegistrationResponse(options: {
|
||||
response: RegistrationResponseJSON;
|
||||
expectedChallenge: string | ((challenge: string) => boolean | Promise<boolean>);
|
||||
expectedOrigin: string | string[];
|
||||
expectedRPID?: string | string[];
|
||||
expectedType?: string | string[];
|
||||
requireUserPresence?: boolean;
|
||||
requireUserVerification?: boolean;
|
||||
supportedAlgorithmIDs?: COSEAlgorithmIdentifier[];
|
||||
}): Promise<VerifiedRegistrationResponse>;
|
||||
/**
|
||||
* Result of registration verification
|
||||
*
|
||||
* @param verified If the assertion response could be verified
|
||||
* @param registrationInfo.fmt Type of attestation
|
||||
* @param registrationInfo.counter The number of times the authenticator reported it has been used.
|
||||
* **Should be kept in a DB for later reference to help prevent replay attacks!**
|
||||
* @param registrationInfo.aaguid Authenticator's Attestation GUID indicating the type of the
|
||||
* authenticator
|
||||
* @param registrationInfo.credentialPublicKey The credential's public key
|
||||
* @param registrationInfo.credentialID The credential's credential ID for the public key above
|
||||
* @param registrationInfo.credentialType The type of the credential returned by the browser
|
||||
* @param registrationInfo.userVerified Whether the user was uniquely identified during attestation
|
||||
* @param registrationInfo.attestationObject The raw `response.attestationObject` Buffer returned by
|
||||
* the authenticator
|
||||
* @param registrationInfo.credentialDeviceType Whether this is a single-device or multi-device
|
||||
* credential. **Should be kept in a DB for later reference!**
|
||||
* @param registrationInfo.credentialBackedUp Whether or not the multi-device credential has been
|
||||
* backed up. Always `false` for single-device credentials. **Should be kept in a DB for later
|
||||
* reference!**
|
||||
* @param registrationInfo.origin The origin of the website that the registration occurred on
|
||||
* @param registrationInfo?.rpID The RP ID that the registration occurred on, if one or more were
|
||||
* specified in the registration options
|
||||
* @param registrationInfo?.authenticatorExtensionResults The authenticator extensions returned
|
||||
* by the browser
|
||||
*/
|
||||
export type VerifiedRegistrationResponse = {
|
||||
verified: boolean;
|
||||
registrationInfo?: {
|
||||
fmt: AttestationFormat;
|
||||
aaguid: string;
|
||||
credential: WebAuthnCredential;
|
||||
credentialType: 'public-key';
|
||||
attestationObject: Uint8Array;
|
||||
userVerified: boolean;
|
||||
credentialDeviceType: CredentialDeviceType;
|
||||
credentialBackedUp: boolean;
|
||||
origin: string;
|
||||
rpID?: string;
|
||||
authenticatorExtensionResults?: AuthenticationExtensionsAuthenticatorOutputs;
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Values passed to all attestation format verifiers, from which they are free to use as they please
|
||||
*/
|
||||
export type AttestationFormatVerifierOpts = {
|
||||
aaguid: Uint8Array;
|
||||
attStmt: AttestationStatement;
|
||||
authData: Uint8Array;
|
||||
clientDataHash: Uint8Array;
|
||||
credentialID: Uint8Array;
|
||||
credentialPublicKey: Uint8Array;
|
||||
rootCertificates: string[];
|
||||
rpIdHash: Uint8Array;
|
||||
verifyTimestampMS?: boolean;
|
||||
};
|
||||
//# sourceMappingURL=verifyRegistrationResponse.d.ts.map
|
||||
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifyRegistrationResponse.d.ts.map
generated
vendored
Normal file
1
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifyRegistrationResponse.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"verifyRegistrationResponse.d.ts","sourceRoot":"","sources":["../../src/registration/verifyRegistrationResponse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,oBAAoB,EACpB,wBAAwB,EACxB,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAE1B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,KAAK,EAAE,4CAA4C,EAAE,MAAM,6CAA6C,CAAC;AAoBhH;;GAEG;AACH,MAAM,MAAM,8BAA8B,GAAG,UAAU,CAAC,OAAO,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAC;AAE9F;;;;;;;;;;;;;GAaG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE;IACP,QAAQ,EAAE,wBAAwB,CAAC;IACnC,iBAAiB,EAAE,MAAM,GAAG,CAAC,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IAChF,cAAc,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACjC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,qBAAqB,CAAC,EAAE,uBAAuB,EAAE,CAAC;CACnD,GACA,OAAO,CAAC,4BAA4B,CAAC,CAsPvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,MAAM,4BAA4B,GAAG;IACzC,QAAQ,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE;QACjB,GAAG,EAAE,iBAAiB,CAAC;QACvB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,kBAAkB,CAAC;QAC/B,cAAc,EAAE,YAAY,CAAC;QAC7B,iBAAiB,EAAE,UAAU,CAAC;QAC9B,YAAY,EAAE,OAAO,CAAC;QACtB,oBAAoB,EAAE,oBAAoB,CAAC;QAC3C,kBAAkB,EAAE,OAAO,CAAC;QAC5B,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,6BAA6B,CAAC,EAAE,4CAA4C,CAAC;KAC9E,CAAC;CACH,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,6BAA6B,GAAG;IAC1C,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,oBAAoB,CAAC;IAC9B,QAAQ,EAAE,UAAU,CAAC;IACrB,cAAc,EAAE,UAAU,CAAC;IAC3B,YAAY,EAAE,UAAU,CAAC;IACzB,mBAAmB,EAAE,UAAU,CAAC;IAChC,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,EAAE,UAAU,CAAC;IACrB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC"}
|
||||
214
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifyRegistrationResponse.js
generated
vendored
Normal file
214
api.hyungi.net/node_modules/@simplewebauthn/server/script/registration/verifyRegistrationResponse.js
generated
vendored
Normal file
@@ -0,0 +1,214 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.verifyRegistrationResponse = verifyRegistrationResponse;
|
||||
const decodeAttestationObject_js_1 = require("../helpers/decodeAttestationObject.js");
|
||||
const decodeClientDataJSON_js_1 = require("../helpers/decodeClientDataJSON.js");
|
||||
const parseAuthenticatorData_js_1 = require("../helpers/parseAuthenticatorData.js");
|
||||
const toHash_js_1 = require("../helpers/toHash.js");
|
||||
const decodeCredentialPublicKey_js_1 = require("../helpers/decodeCredentialPublicKey.js");
|
||||
const cose_js_1 = require("../helpers/cose.js");
|
||||
const convertAAGUIDToString_js_1 = require("../helpers/convertAAGUIDToString.js");
|
||||
const parseBackupFlags_js_1 = require("../helpers/parseBackupFlags.js");
|
||||
const matchExpectedRPID_js_1 = require("../helpers/matchExpectedRPID.js");
|
||||
const index_js_1 = require("../helpers/iso/index.js");
|
||||
const settingsService_js_1 = require("../services/settingsService.js");
|
||||
const generateRegistrationOptions_js_1 = require("./generateRegistrationOptions.js");
|
||||
const verifyAttestationFIDOU2F_js_1 = require("./verifications/verifyAttestationFIDOU2F.js");
|
||||
const verifyAttestationPacked_js_1 = require("./verifications/verifyAttestationPacked.js");
|
||||
const verifyAttestationAndroidSafetyNet_js_1 = require("./verifications/verifyAttestationAndroidSafetyNet.js");
|
||||
const verifyAttestationTPM_js_1 = require("./verifications/tpm/verifyAttestationTPM.js");
|
||||
const verifyAttestationAndroidKey_js_1 = require("./verifications/verifyAttestationAndroidKey.js");
|
||||
const verifyAttestationApple_js_1 = require("./verifications/verifyAttestationApple.js");
|
||||
/**
|
||||
* Verify that the user has legitimately completed the registration process
|
||||
*
|
||||
* **Options:**
|
||||
*
|
||||
* @param response - Response returned by **@simplewebauthn/browser**'s `startAuthentication()`
|
||||
* @param expectedChallenge - The base64url-encoded `options.challenge` returned by `generateRegistrationOptions()`
|
||||
* @param expectedOrigin - Website URL (or array of URLs) that the registration should have occurred on
|
||||
* @param expectedRPID - RP ID (or array of IDs) that was specified in the registration options
|
||||
* @param expectedType **(Optional)** - The response type expected ('webauthn.create')
|
||||
* @param requireUserPresence **(Optional)** - Enforce user presence by the authenticator (or skip it during auto registration) Defaults to `true`
|
||||
* @param requireUserVerification **(Optional)** - Enforce user verification by the authenticator (via PIN, fingerprint, etc...) Defaults to `true`
|
||||
* @param supportedAlgorithmIDs **(Optional)** - Array of numeric COSE algorithm identifiers supported for attestation by this RP. See https://www.iana.org/assignments/cose/cose.xhtml#algorithms. Defaults to all supported algorithm IDs
|
||||
*/
|
||||
async function verifyRegistrationResponse(options) {
|
||||
const { response, expectedChallenge, expectedOrigin, expectedRPID, expectedType, requireUserPresence = true, requireUserVerification = true, supportedAlgorithmIDs = generateRegistrationOptions_js_1.supportedCOSEAlgorithmIdentifiers, } = options;
|
||||
const { id, rawId, type: credentialType, response: attestationResponse } = response;
|
||||
// Ensure credential specified an ID
|
||||
if (!id) {
|
||||
throw new Error('Missing credential ID');
|
||||
}
|
||||
// Ensure ID is base64url-encoded
|
||||
if (id !== rawId) {
|
||||
throw new Error('Credential ID was not base64url-encoded');
|
||||
}
|
||||
// Make sure credential type is public-key
|
||||
if (credentialType !== 'public-key') {
|
||||
throw new Error(`Unexpected credential type ${credentialType}, expected "public-key"`);
|
||||
}
|
||||
const clientDataJSON = (0, decodeClientDataJSON_js_1.decodeClientDataJSON)(attestationResponse.clientDataJSON);
|
||||
const { type, origin, challenge, tokenBinding } = clientDataJSON;
|
||||
// Make sure we're handling an registration
|
||||
if (Array.isArray(expectedType)) {
|
||||
if (!expectedType.includes(type)) {
|
||||
const joinedExpectedType = expectedType.join(', ');
|
||||
throw new Error(`Unexpected registration response type "${type}", expected one of: ${joinedExpectedType}`);
|
||||
}
|
||||
}
|
||||
else if (expectedType) {
|
||||
if (type !== expectedType) {
|
||||
throw new Error(`Unexpected registration response type "${type}", expected "${expectedType}"`);
|
||||
}
|
||||
}
|
||||
else if (type !== 'webauthn.create') {
|
||||
throw new Error(`Unexpected registration response type: ${type}`);
|
||||
}
|
||||
// Ensure the device provided the challenge we gave it
|
||||
if (typeof expectedChallenge === 'function') {
|
||||
if (!(await expectedChallenge(challenge))) {
|
||||
throw new Error(`Custom challenge verifier returned false for registration response challenge "${challenge}"`);
|
||||
}
|
||||
}
|
||||
else if (challenge !== expectedChallenge) {
|
||||
throw new Error(`Unexpected registration response challenge "${challenge}", expected "${expectedChallenge}"`);
|
||||
}
|
||||
// Check that the origin is our site
|
||||
if (Array.isArray(expectedOrigin)) {
|
||||
if (!expectedOrigin.includes(origin)) {
|
||||
throw new Error(`Unexpected registration response origin "${origin}", expected one of: ${expectedOrigin.join(', ')}`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (origin !== expectedOrigin) {
|
||||
throw new Error(`Unexpected registration response origin "${origin}", expected "${expectedOrigin}"`);
|
||||
}
|
||||
}
|
||||
if (tokenBinding) {
|
||||
if (typeof tokenBinding !== 'object') {
|
||||
throw new Error(`Unexpected value for TokenBinding "${tokenBinding}"`);
|
||||
}
|
||||
if (['present', 'supported', 'not-supported'].indexOf(tokenBinding.status) < 0) {
|
||||
throw new Error(`Unexpected tokenBinding.status value of "${tokenBinding.status}"`);
|
||||
}
|
||||
}
|
||||
const attestationObject = index_js_1.isoBase64URL.toBuffer(attestationResponse.attestationObject);
|
||||
const decodedAttestationObject = (0, decodeAttestationObject_js_1.decodeAttestationObject)(attestationObject);
|
||||
const fmt = decodedAttestationObject.get('fmt');
|
||||
const authData = decodedAttestationObject.get('authData');
|
||||
const attStmt = decodedAttestationObject.get('attStmt');
|
||||
const parsedAuthData = (0, parseAuthenticatorData_js_1.parseAuthenticatorData)(authData);
|
||||
const { aaguid, rpIdHash, flags, credentialID, counter, credentialPublicKey, extensionsData, } = parsedAuthData;
|
||||
// Make sure the response's RP ID is ours
|
||||
let matchedRPID;
|
||||
if (expectedRPID) {
|
||||
let expectedRPIDs = [];
|
||||
if (typeof expectedRPID === 'string') {
|
||||
expectedRPIDs = [expectedRPID];
|
||||
}
|
||||
else {
|
||||
expectedRPIDs = expectedRPID;
|
||||
}
|
||||
matchedRPID = await (0, matchExpectedRPID_js_1.matchExpectedRPID)(rpIdHash, expectedRPIDs);
|
||||
}
|
||||
// Make sure someone was physically present
|
||||
if (requireUserPresence && !flags.up) {
|
||||
throw new Error('User presence was required, but user was not present');
|
||||
}
|
||||
// Enforce user verification if specified
|
||||
if (requireUserVerification && !flags.uv) {
|
||||
throw new Error('User verification was required, but user could not be verified');
|
||||
}
|
||||
if (!credentialID) {
|
||||
throw new Error('No credential ID was provided by authenticator');
|
||||
}
|
||||
if (!credentialPublicKey) {
|
||||
throw new Error('No public key was provided by authenticator');
|
||||
}
|
||||
if (!aaguid) {
|
||||
throw new Error('No AAGUID was present during registration');
|
||||
}
|
||||
const decodedPublicKey = (0, decodeCredentialPublicKey_js_1.decodeCredentialPublicKey)(credentialPublicKey);
|
||||
const alg = decodedPublicKey.get(cose_js_1.COSEKEYS.alg);
|
||||
if (typeof alg !== 'number') {
|
||||
throw new Error('Credential public key was missing numeric alg');
|
||||
}
|
||||
// Make sure the key algorithm is one we specified within the registration options
|
||||
if (!supportedAlgorithmIDs.includes(alg)) {
|
||||
const supported = supportedAlgorithmIDs.join(', ');
|
||||
throw new Error(`Unexpected public key alg "${alg}", expected one of "${supported}"`);
|
||||
}
|
||||
const clientDataHash = await (0, toHash_js_1.toHash)(index_js_1.isoBase64URL.toBuffer(attestationResponse.clientDataJSON));
|
||||
const rootCertificates = settingsService_js_1.SettingsService.getRootCertificates({
|
||||
identifier: fmt,
|
||||
});
|
||||
// Prepare arguments to pass to the relevant verification method
|
||||
const verifierOpts = {
|
||||
aaguid,
|
||||
attStmt,
|
||||
authData,
|
||||
clientDataHash,
|
||||
credentialID,
|
||||
credentialPublicKey,
|
||||
rootCertificates,
|
||||
rpIdHash,
|
||||
};
|
||||
/**
|
||||
* Verification can only be performed when attestation = 'direct'
|
||||
*/
|
||||
let verified = false;
|
||||
if (fmt === 'fido-u2f') {
|
||||
verified = await (0, verifyAttestationFIDOU2F_js_1.verifyAttestationFIDOU2F)(verifierOpts);
|
||||
}
|
||||
else if (fmt === 'packed') {
|
||||
verified = await (0, verifyAttestationPacked_js_1.verifyAttestationPacked)(verifierOpts);
|
||||
}
|
||||
else if (fmt === 'android-safetynet') {
|
||||
verified = await (0, verifyAttestationAndroidSafetyNet_js_1.verifyAttestationAndroidSafetyNet)(verifierOpts);
|
||||
}
|
||||
else if (fmt === 'android-key') {
|
||||
verified = await (0, verifyAttestationAndroidKey_js_1.verifyAttestationAndroidKey)(verifierOpts);
|
||||
}
|
||||
else if (fmt === 'tpm') {
|
||||
verified = await (0, verifyAttestationTPM_js_1.verifyAttestationTPM)(verifierOpts);
|
||||
}
|
||||
else if (fmt === 'apple') {
|
||||
verified = await (0, verifyAttestationApple_js_1.verifyAttestationApple)(verifierOpts);
|
||||
}
|
||||
else if (fmt === 'none') {
|
||||
if (attStmt.size > 0) {
|
||||
throw new Error('None attestation had unexpected attestation statement');
|
||||
}
|
||||
// This is the weaker of the attestations, so there's nothing else to really check
|
||||
verified = true;
|
||||
}
|
||||
else {
|
||||
throw new Error(`Unsupported Attestation Format: ${fmt}`);
|
||||
}
|
||||
const toReturn = {
|
||||
verified,
|
||||
};
|
||||
if (toReturn.verified) {
|
||||
const { credentialDeviceType, credentialBackedUp } = (0, parseBackupFlags_js_1.parseBackupFlags)(flags);
|
||||
toReturn.registrationInfo = {
|
||||
fmt,
|
||||
aaguid: (0, convertAAGUIDToString_js_1.convertAAGUIDToString)(aaguid),
|
||||
credentialType,
|
||||
credential: {
|
||||
id: index_js_1.isoBase64URL.fromBuffer(credentialID),
|
||||
publicKey: credentialPublicKey,
|
||||
counter,
|
||||
transports: response.response.transports,
|
||||
},
|
||||
attestationObject,
|
||||
userVerified: flags.uv,
|
||||
credentialDeviceType,
|
||||
credentialBackedUp,
|
||||
origin: clientDataJSON.origin,
|
||||
rpID: matchedRPID,
|
||||
authenticatorExtensionResults: extensionsData,
|
||||
};
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
Reference in New Issue
Block a user