@metamask/passkey-controller 3.1.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -1
- package/README.md +101 -9
- package/dist/{PasskeyController-method-action-types.d.mts → PasskeyController-method-action-types.d.ts} +44 -3
- package/dist/PasskeyController-method-action-types.d.ts.map +1 -0
- package/dist/{PasskeyController-method-action-types.mjs → PasskeyController-method-action-types.js} +1 -1
- package/dist/PasskeyController-method-action-types.js.map +1 -0
- package/dist/{PasskeyController.d.cts → PasskeyController.d.ts} +41 -6
- package/dist/PasskeyController.d.ts.map +1 -0
- package/dist/PasskeyController.js +828 -0
- package/dist/PasskeyController.js.map +1 -0
- package/dist/{ceremony-manager.d.cts → ceremony-manager.d.ts} +2 -2
- package/dist/ceremony-manager.d.ts.map +1 -0
- package/dist/ceremony-manager.js +150 -0
- package/dist/ceremony-manager.js.map +1 -0
- package/dist/{constants.d.mts → constants.d.ts} +18 -12
- package/dist/constants.d.ts.map +1 -0
- package/dist/{constants.mjs → constants.js} +7 -1
- package/dist/constants.js.map +1 -0
- package/dist/{errors.d.mts → errors.d.ts} +2 -2
- package/dist/errors.d.ts.map +1 -0
- package/dist/{errors.mjs → errors.js} +4 -1
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/{key-derivation.d.cts → key-derivation.d.ts} +3 -3
- package/dist/key-derivation.d.ts.map +1 -0
- package/dist/{key-derivation.mjs → key-derivation.js} +5 -5
- package/dist/key-derivation.js.map +1 -0
- package/dist/logger.d.ts +4 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +6 -0
- package/dist/logger.js.map +1 -0
- package/dist/{types.d.cts → types.d.ts} +23 -9
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/{crypto.d.mts → crypto.d.ts} +1 -1
- package/dist/utils/crypto.d.ts.map +1 -0
- package/dist/utils/{crypto.mjs → crypto.js} +10 -8
- package/dist/utils/crypto.js.map +1 -0
- package/dist/utils/{encoding.d.cts → encoding.d.ts} +1 -1
- package/dist/utils/encoding.d.ts.map +1 -0
- package/dist/utils/{encoding.mjs → encoding.js} +2 -2
- package/dist/utils/encoding.js.map +1 -0
- package/dist/webauthn/{constants.d.mts → constants.d.ts} +1 -1
- package/dist/webauthn/constants.d.ts.map +1 -0
- package/dist/webauthn/{constants.mjs → constants.js} +1 -1
- package/dist/webauthn/constants.js.map +1 -0
- package/dist/webauthn/{decode-attestation-object.d.mts → decode-attestation-object.d.ts} +2 -2
- package/dist/webauthn/decode-attestation-object.d.ts.map +1 -0
- package/dist/webauthn/{decode-attestation-object.mjs → decode-attestation-object.js} +2 -2
- package/dist/webauthn/decode-attestation-object.js.map +1 -0
- package/dist/webauthn/{decode-client-data-json.d.cts → decode-client-data-json.d.ts} +2 -2
- package/dist/webauthn/decode-client-data-json.d.ts.map +1 -0
- package/dist/webauthn/{decode-client-data-json.mjs → decode-client-data-json.js} +2 -2
- package/dist/webauthn/decode-client-data-json.js.map +1 -0
- package/dist/webauthn/{match-expected-rp-id.d.cts → match-expected-rp-id.d.ts} +3 -3
- package/dist/webauthn/match-expected-rp-id.d.ts.map +1 -0
- package/dist/webauthn/{match-expected-rp-id.mjs → match-expected-rp-id.js} +6 -7
- package/dist/webauthn/match-expected-rp-id.js.map +1 -0
- package/dist/webauthn/{parse-authenticator-data.d.cts → parse-authenticator-data.d.ts} +2 -2
- package/dist/webauthn/parse-authenticator-data.d.ts.map +1 -0
- package/dist/webauthn/{parse-authenticator-data.mjs → parse-authenticator-data.js} +2 -2
- package/dist/webauthn/parse-authenticator-data.js.map +1 -0
- package/dist/webauthn/{types.d.mts → types.d.ts} +2 -2
- package/dist/webauthn/types.d.ts.map +1 -0
- package/dist/webauthn/types.js +2 -0
- package/dist/webauthn/types.js.map +1 -0
- package/dist/webauthn/{verify-authentication-response.d.cts → verify-authentication-response.d.ts} +3 -3
- package/dist/webauthn/verify-authentication-response.d.ts.map +1 -0
- package/dist/webauthn/{verify-authentication-response.mjs → verify-authentication-response.js} +13 -11
- package/dist/webauthn/verify-authentication-response.js.map +1 -0
- package/dist/webauthn/{verify-registration-response.d.mts → verify-registration-response.d.ts} +3 -3
- package/dist/webauthn/verify-registration-response.d.ts.map +1 -0
- package/dist/webauthn/{verify-registration-response.mjs → verify-registration-response.js} +12 -13
- package/dist/webauthn/verify-registration-response.js.map +1 -0
- package/dist/webauthn/{verify-signature.d.cts → verify-signature.d.ts} +4 -3
- package/dist/webauthn/verify-signature.d.ts.map +1 -0
- package/dist/webauthn/{verify-signature.mjs → verify-signature.js} +17 -16
- package/dist/webauthn/verify-signature.js.map +1 -0
- package/package.json +16 -21
- package/dist/PasskeyController-method-action-types.cjs +0 -7
- package/dist/PasskeyController-method-action-types.cjs.map +0 -1
- package/dist/PasskeyController-method-action-types.d.cts +0 -204
- package/dist/PasskeyController-method-action-types.d.cts.map +0 -1
- package/dist/PasskeyController-method-action-types.d.mts.map +0 -1
- package/dist/PasskeyController-method-action-types.mjs.map +0 -1
- package/dist/PasskeyController.cjs +0 -685
- package/dist/PasskeyController.cjs.map +0 -1
- package/dist/PasskeyController.d.cts.map +0 -1
- package/dist/PasskeyController.d.mts +0 -208
- package/dist/PasskeyController.d.mts.map +0 -1
- package/dist/PasskeyController.mjs +0 -680
- package/dist/PasskeyController.mjs.map +0 -1
- package/dist/ceremony-manager.cjs +0 -135
- package/dist/ceremony-manager.cjs.map +0 -1
- package/dist/ceremony-manager.d.cts.map +0 -1
- package/dist/ceremony-manager.d.mts +0 -72
- package/dist/ceremony-manager.d.mts.map +0 -1
- package/dist/ceremony-manager.mjs +0 -131
- package/dist/ceremony-manager.mjs.map +0 -1
- package/dist/constants.cjs +0 -39
- package/dist/constants.cjs.map +0 -1
- package/dist/constants.d.cts +0 -36
- package/dist/constants.d.cts.map +0 -1
- package/dist/constants.d.mts.map +0 -1
- package/dist/constants.mjs.map +0 -1
- package/dist/errors.cjs +0 -57
- package/dist/errors.cjs.map +0 -1
- package/dist/errors.d.cts +0 -34
- package/dist/errors.d.cts.map +0 -1
- package/dist/errors.d.mts.map +0 -1
- package/dist/errors.mjs.map +0 -1
- package/dist/index.cjs +0 -15
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -8
- package/dist/index.d.cts.map +0 -1
- package/dist/index.d.mts +0 -8
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs +0 -5
- package/dist/index.mjs.map +0 -1
- package/dist/key-derivation.cjs +0 -44
- package/dist/key-derivation.cjs.map +0 -1
- package/dist/key-derivation.d.cts.map +0 -1
- package/dist/key-derivation.d.mts +0 -20
- package/dist/key-derivation.d.mts.map +0 -1
- package/dist/key-derivation.mjs.map +0 -1
- package/dist/logger.cjs +0 -9
- package/dist/logger.cjs.map +0 -1
- package/dist/logger.d.cts +0 -5
- package/dist/logger.d.cts.map +0 -1
- package/dist/logger.d.mts +0 -5
- package/dist/logger.d.mts.map +0 -1
- package/dist/logger.mjs +0 -6
- package/dist/logger.mjs.map +0 -1
- package/dist/types.cjs +0 -4
- package/dist/types.cjs.map +0 -1
- package/dist/types.d.cts.map +0 -1
- package/dist/types.d.mts +0 -132
- package/dist/types.d.mts.map +0 -1
- package/dist/types.mjs +0 -2
- package/dist/types.mjs.map +0 -1
- package/dist/utils/crypto.cjs +0 -66
- package/dist/utils/crypto.cjs.map +0 -1
- package/dist/utils/crypto.d.cts +0 -37
- package/dist/utils/crypto.d.cts.map +0 -1
- package/dist/utils/crypto.d.mts.map +0 -1
- package/dist/utils/crypto.mjs.map +0 -1
- package/dist/utils/encoding.cjs +0 -42
- package/dist/utils/encoding.cjs.map +0 -1
- package/dist/utils/encoding.d.cts.map +0 -1
- package/dist/utils/encoding.d.mts +0 -22
- package/dist/utils/encoding.d.mts.map +0 -1
- package/dist/utils/encoding.mjs.map +0 -1
- package/dist/webauthn/constants.cjs +0 -74
- package/dist/webauthn/constants.cjs.map +0 -1
- package/dist/webauthn/constants.d.cts +0 -68
- package/dist/webauthn/constants.d.cts.map +0 -1
- package/dist/webauthn/constants.d.mts.map +0 -1
- package/dist/webauthn/constants.mjs.map +0 -1
- package/dist/webauthn/decode-attestation-object.cjs +0 -18
- package/dist/webauthn/decode-attestation-object.cjs.map +0 -1
- package/dist/webauthn/decode-attestation-object.d.cts +0 -10
- package/dist/webauthn/decode-attestation-object.d.cts.map +0 -1
- package/dist/webauthn/decode-attestation-object.d.mts.map +0 -1
- package/dist/webauthn/decode-attestation-object.mjs.map +0 -1
- package/dist/webauthn/decode-client-data-json.cjs +0 -17
- package/dist/webauthn/decode-client-data-json.cjs.map +0 -1
- package/dist/webauthn/decode-client-data-json.d.cts.map +0 -1
- package/dist/webauthn/decode-client-data-json.d.mts +0 -9
- package/dist/webauthn/decode-client-data-json.d.mts.map +0 -1
- package/dist/webauthn/decode-client-data-json.mjs.map +0 -1
- package/dist/webauthn/match-expected-rp-id.cjs +0 -26
- package/dist/webauthn/match-expected-rp-id.cjs.map +0 -1
- package/dist/webauthn/match-expected-rp-id.d.cts.map +0 -1
- package/dist/webauthn/match-expected-rp-id.d.mts +0 -11
- package/dist/webauthn/match-expected-rp-id.d.mts.map +0 -1
- package/dist/webauthn/match-expected-rp-id.mjs.map +0 -1
- package/dist/webauthn/parse-authenticator-data.cjs +0 -69
- package/dist/webauthn/parse-authenticator-data.cjs.map +0 -1
- package/dist/webauthn/parse-authenticator-data.d.cts.map +0 -1
- package/dist/webauthn/parse-authenticator-data.d.mts +0 -10
- package/dist/webauthn/parse-authenticator-data.d.mts.map +0 -1
- package/dist/webauthn/parse-authenticator-data.mjs.map +0 -1
- package/dist/webauthn/types.cjs +0 -3
- package/dist/webauthn/types.cjs.map +0 -1
- package/dist/webauthn/types.d.cts +0 -113
- package/dist/webauthn/types.d.cts.map +0 -1
- package/dist/webauthn/types.d.mts.map +0 -1
- package/dist/webauthn/types.mjs +0 -2
- package/dist/webauthn/types.mjs.map +0 -1
- package/dist/webauthn/verify-authentication-response.cjs +0 -133
- package/dist/webauthn/verify-authentication-response.cjs.map +0 -1
- package/dist/webauthn/verify-authentication-response.d.cts.map +0 -1
- package/dist/webauthn/verify-authentication-response.d.mts +0 -64
- package/dist/webauthn/verify-authentication-response.d.mts.map +0 -1
- package/dist/webauthn/verify-authentication-response.mjs.map +0 -1
- package/dist/webauthn/verify-registration-response.cjs +0 -242
- package/dist/webauthn/verify-registration-response.cjs.map +0 -1
- package/dist/webauthn/verify-registration-response.d.cts +0 -81
- package/dist/webauthn/verify-registration-response.d.cts.map +0 -1
- package/dist/webauthn/verify-registration-response.d.mts.map +0 -1
- package/dist/webauthn/verify-registration-response.mjs.map +0 -1
- package/dist/webauthn/verify-signature.cjs +0 -176
- package/dist/webauthn/verify-signature.cjs.map +0 -1
- package/dist/webauthn/verify-signature.d.cts.map +0 -1
- package/dist/webauthn/verify-signature.d.mts +0 -21
- package/dist/webauthn/verify-signature.d.mts.map +0 -1
- package/dist/webauthn/verify-signature.mjs.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { ControllerGetStateAction, ControllerStateChangedEvent } from
|
|
2
|
-
import type { KeyringControllerChangePasswordAction, KeyringControllerExportAccountAction, KeyringControllerExportEncryptionKeyAction, KeyringControllerExportSeedPhraseAction, KeyringControllerSubmitEncryptionKeyAction, KeyringControllerVerifyPasswordAction } from
|
|
3
|
-
import type { Messenger } from
|
|
4
|
-
import { controllerName } from
|
|
5
|
-
import type { PasskeyControllerMethodActions } from
|
|
1
|
+
import type { ControllerGetStateAction, ControllerStateChangedEvent } from '@metamask/base-controller';
|
|
2
|
+
import type { KeyringControllerChangePasswordAction, KeyringControllerExportAccountAction, KeyringControllerExportEncryptionKeyAction, KeyringControllerExportSeedPhraseAction, KeyringControllerSubmitEncryptionKeyAction, KeyringControllerVerifyPasswordAction } from '@metamask/keyring-controller';
|
|
3
|
+
import type { Messenger } from '@metamask/messenger';
|
|
4
|
+
import { controllerName } from './constants.js';
|
|
5
|
+
import type { PasskeyControllerMethodActions } from './PasskeyController-method-action-types.js';
|
|
6
6
|
export type Base64String = string;
|
|
7
7
|
export type Base64URLString = string;
|
|
8
8
|
export type AuthenticatorTransportFuture = 'ble' | 'cable' | 'hybrid' | 'internal' | 'nfc' | 'smart-card' | 'usb';
|
|
@@ -60,9 +60,10 @@ export type PasskeyRecord = {
|
|
|
60
60
|
};
|
|
61
61
|
/**
|
|
62
62
|
* In-memory state for one **in-flight** WebAuthn **registration** ceremony
|
|
63
|
-
* (from `create()` options until
|
|
64
|
-
* not a user login session; it is keyed by challenge
|
|
65
|
-
* spec ceremony (which includes the authenticator
|
|
63
|
+
* (from `create()` options until the enrollment or replacement completion
|
|
64
|
+
* method finishes). This is not a user login session; it is keyed by challenge
|
|
65
|
+
* and distinct from the full spec ceremony (which includes the authenticator
|
|
66
|
+
* round-trip).
|
|
66
67
|
*/
|
|
67
68
|
export type PasskeyRegistrationCeremony = {
|
|
68
69
|
userHandle: Base64URLString;
|
|
@@ -70,6 +71,14 @@ export type PasskeyRegistrationCeremony = {
|
|
|
70
71
|
challenge: Base64URLString;
|
|
71
72
|
/** When this ceremony was started (ms since epoch); used for TTL pruning. */
|
|
72
73
|
createdAt: number;
|
|
74
|
+
/**
|
|
75
|
+
* Whether this ceremony is for replacing an existing passkey.
|
|
76
|
+
*
|
|
77
|
+
* Omitted or `false` for existing enrollment ceremonies.
|
|
78
|
+
*/
|
|
79
|
+
isReplacement?: boolean;
|
|
80
|
+
/** Credential ID of the record being replaced. */
|
|
81
|
+
sourceCredentialId?: Base64URLString;
|
|
73
82
|
};
|
|
74
83
|
/**
|
|
75
84
|
* In-memory state for one **in-flight** WebAuthn **authentication** ceremony
|
|
@@ -77,6 +86,11 @@ export type PasskeyRegistrationCeremony = {
|
|
|
77
86
|
*/
|
|
78
87
|
export type PasskeyAuthenticationCeremony = {
|
|
79
88
|
challenge: Base64URLString;
|
|
89
|
+
/**
|
|
90
|
+
* Registration challenge when this authentication is the post-registration
|
|
91
|
+
* step of an enrollment or replacement ceremony.
|
|
92
|
+
*/
|
|
93
|
+
registrationChallenge?: Base64URLString;
|
|
80
94
|
/** When this ceremony was started (ms since epoch); used for TTL pruning. */
|
|
81
95
|
createdAt: number;
|
|
82
96
|
};
|
|
@@ -129,4 +143,4 @@ export type PasskeyControllerOptions = {
|
|
|
129
143
|
*/
|
|
130
144
|
getIsOnboardingCompleted: () => boolean;
|
|
131
145
|
};
|
|
132
|
-
//# sourceMappingURL=types.d.
|
|
146
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangedEvent,\n} from '@metamask/base-controller';\nimport type {\n KeyringControllerChangePasswordAction,\n KeyringControllerExportAccountAction,\n KeyringControllerExportEncryptionKeyAction,\n KeyringControllerExportSeedPhraseAction,\n KeyringControllerSubmitEncryptionKeyAction,\n KeyringControllerVerifyPasswordAction,\n} from '@metamask/keyring-controller';\nimport type { Messenger } from '@metamask/messenger';\n\nimport { controllerName } from './constants.js';\nimport type { PasskeyControllerMethodActions } from './PasskeyController-method-action-types.js';\n\nexport type Base64String = string;\n\nexport type Base64URLString = string;\n\nexport type AuthenticatorTransportFuture =\n | 'ble'\n | 'cable'\n | 'hybrid'\n | 'internal'\n | 'nfc'\n | 'smart-card'\n | 'usb';\n\n/**\n * WebAuthn credential metadata used to identify the passkey and verify\n * subsequent assertions.\n */\nexport type PasskeyCredentialInfo = {\n /** WebAuthn credential ID (base64url). */\n id: Base64URLString;\n /** COSE-encoded credential public key (base64url) used to verify assertions. */\n publicKey: Base64URLString;\n /** Authenticator signature counter for replay/clone detection. */\n counter: number;\n /** Authenticator transports hint for `allowCredentials`. */\n transports?: AuthenticatorTransportFuture[];\n /** Authenticator AAGUID captured from attested credential data at registration. */\n aaguid: string;\n};\n\n/**\n * Vault key wrapped under the passkey-derived AES-256-GCM key.\n */\nexport type EncryptedVaultKey = {\n /** Base64-encoded AES-256-GCM ciphertext of the vault key. */\n ciphertext: Base64String;\n /** Base64-encoded AES-GCM IV used during encryption. */\n iv: Base64String;\n};\n\n/**\n * Parameters needed to reproduce the AES-256 wrapping key at unlock time.\n *\n * Encoded as a discriminated union so PRF-only fields (e.g. `prfSalt`) can\n * only exist on the PRF branch, removing the \"optional but actually\n * required\" footgun.\n */\nexport type PasskeyKeyDerivation =\n | {\n method: 'prf';\n /**\n * PRF salt sent in `get()` extension options to reproduce the same PRF\n * output that was generated at registration.\n */\n prfSalt: Base64URLString;\n }\n | { method: 'userHandle' };\n\n/** Discriminator value for {@link PasskeyKeyDerivation}. */\nexport type PasskeyDerivationMethod = PasskeyKeyDerivation['method'];\n\nexport type PasskeyRecord = {\n /** WebAuthn credential metadata used for assertion verification & re-discovery. */\n credential: PasskeyCredentialInfo;\n /** Vault key wrapped under the passkey-derived key. */\n encryptedVaultKey: EncryptedVaultKey;\n /** How the wrapping key is reconstructed at unlock time. */\n keyDerivation: PasskeyKeyDerivation;\n};\n\n/**\n * In-memory state for one **in-flight** WebAuthn **registration** ceremony\n * (from `create()` options until the enrollment or replacement completion\n * method finishes). This is not a user login session; it is keyed by challenge\n * and distinct from the full spec ceremony (which includes the authenticator\n * round-trip).\n */\nexport type PasskeyRegistrationCeremony = {\n userHandle: Base64URLString;\n prfSalt?: Base64URLString;\n challenge: Base64URLString;\n /** When this ceremony was started (ms since epoch); used for TTL pruning. */\n createdAt: number;\n /**\n * Whether this ceremony is for replacing an existing passkey.\n *\n * Omitted or `false` for existing enrollment ceremonies.\n */\n isReplacement?: boolean;\n /** Credential ID of the record being replaced. */\n sourceCredentialId?: Base64URLString;\n};\n\n/**\n * In-memory state for one **in-flight** WebAuthn **authentication** ceremony\n * (`get()` options until the assertion is verified). Not a user login session.\n */\nexport type PasskeyAuthenticationCeremony = {\n challenge: Base64URLString;\n /**\n * Registration challenge when this authentication is the post-registration\n * step of an enrollment or replacement ceremony.\n */\n registrationChallenge?: Base64URLString;\n /** When this ceremony was started (ms since epoch); used for TTL pruning. */\n createdAt: number;\n};\n\n/**\n * PRF extension types not covered by DOM typings.\n */\nexport type PrfEvalExtension = {\n eval: {\n first: Base64URLString;\n };\n};\n\nexport type PrfClientExtensionResults = {\n prf?: {\n enabled?: boolean;\n results?: { first?: Base64URLString };\n };\n};\n\nexport type PasskeyControllerState = {\n passkeyRecord: PasskeyRecord | null;\n};\n\nexport type PasskeyControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n PasskeyControllerState\n>;\n\n/**\n * KeyringController actions that {@link PasskeyController} may call during\n * orchestrated passkey flows. The restricted messenger must allow these at init.\n */\nexport type PasskeyControllerAllowedActions =\n | KeyringControllerVerifyPasswordAction\n | KeyringControllerExportEncryptionKeyAction\n | KeyringControllerSubmitEncryptionKeyAction\n | KeyringControllerChangePasswordAction\n | KeyringControllerExportSeedPhraseAction\n | KeyringControllerExportAccountAction;\n\n/**\n * Actions exposed by {@link PasskeyController} on its messenger, including\n * `:getState`, enrollment/unlock ceremony methods, and lifecycle helpers.\n */\nexport type PasskeyControllerActions =\n | PasskeyControllerGetStateAction\n | PasskeyControllerMethodActions;\n\nexport type PasskeyControllerStateChangedEvent = ControllerStateChangedEvent<\n typeof controllerName,\n PasskeyControllerState\n>;\n\nexport type PasskeyControllerEvents = PasskeyControllerStateChangedEvent;\n\nexport type PasskeyControllerMessenger = Messenger<\n typeof controllerName,\n PasskeyControllerActions | PasskeyControllerAllowedActions,\n PasskeyControllerEvents\n>;\n\nexport type PasskeyControllerOptions = {\n messenger: PasskeyControllerMessenger;\n state?: Partial<PasskeyControllerState>;\n rpId?: string;\n expectedRPID: string | string[];\n rpName: string;\n expectedOrigin: string | string[];\n userName?: string;\n userDisplayName?: string;\n /**\n * Returns whether wallet onboarding is complete. When `true`, enrollment\n * requires password step-up. The integrator typically supplies\n * `() => onboardingController.state.completedOnboarding`.\n */\n getIsOnboardingCompleted: () => boolean;\n};\n"]}
|
|
@@ -34,4 +34,4 @@ export declare function encryptWithKey(plaintext: string, key: Uint8Array): {
|
|
|
34
34
|
* @returns Decrypted UTF-8 string.
|
|
35
35
|
*/
|
|
36
36
|
export declare function decryptWithKey(ciphertext: string, iv: string, key: Uint8Array): string;
|
|
37
|
-
//# sourceMappingURL=crypto.d.
|
|
37
|
+
//# sourceMappingURL=crypto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/utils/crypto.ts"],"names":[],"mappings":"AAYA;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,UAAU,EACf,IAAI,EAAE,UAAU,GACf,UAAU,CAIZ;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,UAAU,GACd;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CASpC;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,UAAU,GACd,MAAM,CAMR"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { bytesToBase64, base64ToBytes } from
|
|
2
|
-
import { gcm } from
|
|
3
|
-
import { randomBytes } from
|
|
4
|
-
import { hkdf } from
|
|
5
|
-
import { sha256 } from
|
|
6
|
-
import { bytesToBase64URL } from
|
|
1
|
+
import { bytesToBase64, base64ToBytes } from '@metamask/utils';
|
|
2
|
+
import { gcm } from '@noble/ciphers/aes';
|
|
3
|
+
import { randomBytes } from '@noble/ciphers/webcrypto';
|
|
4
|
+
import { hkdf } from '@noble/hashes/hkdf';
|
|
5
|
+
import { sha256 as nobleSha256 } from '@noble/hashes/sha2';
|
|
6
|
+
import { bytesToBase64URL } from './encoding.js';
|
|
7
7
|
const PASSKEY_HKDF_INFO = 'metamask:passkey:encryption-key:v1';
|
|
8
8
|
const AES_GCM_IV_LENGTH = 12;
|
|
9
9
|
/**
|
|
@@ -24,7 +24,9 @@ export function randomBytesToBase64URL(byteLength) {
|
|
|
24
24
|
* @returns 32-byte derived encryption key.
|
|
25
25
|
*/
|
|
26
26
|
export function deriveEncryptionKey(ikm, salt) {
|
|
27
|
-
|
|
27
|
+
// @noble/hashes/hkdf accepts only a synchronous hash callback, so this
|
|
28
|
+
// synchronous HKDF path intentionally retains Noble SHA-256.
|
|
29
|
+
return hkdf(nobleSha256, ikm, salt, PASSKEY_HKDF_INFO, 32);
|
|
28
30
|
}
|
|
29
31
|
/**
|
|
30
32
|
* Encrypts plaintext with an AES-256-GCM key.
|
|
@@ -56,4 +58,4 @@ export function decryptWithKey(ciphertext, iv, key) {
|
|
|
56
58
|
const plaintext = gcm(key, ivBytes).decrypt(ciphertextBytes);
|
|
57
59
|
return new TextDecoder().decode(plaintext);
|
|
58
60
|
}
|
|
59
|
-
//# sourceMappingURL=crypto.
|
|
61
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/utils/crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,MAAM,iBAAiB,GAAG,oCAAoC,CAAC;AAE/D,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAE7B;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,UAAkB;IACvD,OAAO,gBAAgB,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC;AACnD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAAe,EACf,IAAgB;IAEhB,uEAAuE;IACvE,6DAA6D;IAC7D,OAAO,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE,IAAI,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,SAAiB,EACjB,GAAe;IAEf,MAAM,EAAE,GAAG,WAAW,CAAC,iBAAiB,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpD,MAAM,eAAe,GAAG,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAEtD,OAAO;QACL,UAAU,EAAE,aAAa,CAAC,eAAe,CAAC;QAC1C,EAAE,EAAE,aAAa,CAAC,EAAE,CAAC;KACtB,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAC5B,UAAkB,EAClB,EAAU,EACV,GAAe;IAEf,MAAM,eAAe,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;IAClC,MAAM,SAAS,GAAG,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAE7D,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;AAC7C,CAAC","sourcesContent":["import { bytesToBase64, base64ToBytes } from '@metamask/utils';\nimport { gcm } from '@noble/ciphers/aes';\nimport { randomBytes } from '@noble/ciphers/webcrypto';\nimport { hkdf } from '@noble/hashes/hkdf';\nimport { sha256 as nobleSha256 } from '@noble/hashes/sha2';\n\nimport { bytesToBase64URL } from './encoding.js';\n\nconst PASSKEY_HKDF_INFO = 'metamask:passkey:encryption-key:v1';\n\nconst AES_GCM_IV_LENGTH = 12;\n\n/**\n * Generates random bytes and returns them as a base64url string (no padding).\n *\n * @param byteLength - Number of bytes to generate (e.g. WebAuthn challenge length).\n * @returns Base64url-encoded random bytes.\n */\nexport function randomBytesToBase64URL(byteLength: number): string {\n return bytesToBase64URL(randomBytes(byteLength));\n}\n\n/**\n * Derives an AES-256 encryption key from input key material and a credential ID\n * using HKDF-SHA256.\n *\n * @param ikm - Input key material (e.g. PRF output or userHandle).\n * @param salt - HKDF salt.\n * @returns 32-byte derived encryption key.\n */\nexport function deriveEncryptionKey(\n ikm: Uint8Array,\n salt: Uint8Array,\n): Uint8Array {\n // @noble/hashes/hkdf accepts only a synchronous hash callback, so this\n // synchronous HKDF path intentionally retains Noble SHA-256.\n return hkdf(nobleSha256, ikm, salt, PASSKEY_HKDF_INFO, 32);\n}\n\n/**\n * Encrypts plaintext with an AES-256-GCM key.\n *\n * @param plaintext - UTF-8 string to encrypt.\n * @param key - 32-byte AES-256 key from {@link deriveEncryptionKey}.\n * @returns Base64-encoded ciphertext and IV.\n */\nexport function encryptWithKey(\n plaintext: string,\n key: Uint8Array,\n): { ciphertext: string; iv: string } {\n const iv = randomBytes(AES_GCM_IV_LENGTH);\n const encoded = new TextEncoder().encode(plaintext);\n const ciphertextBytes = gcm(key, iv).encrypt(encoded);\n\n return {\n ciphertext: bytesToBase64(ciphertextBytes),\n iv: bytesToBase64(iv),\n };\n}\n\n/**\n * Decrypts AES-256-GCM ciphertext with the given key.\n *\n * @param ciphertext - Base64-encoded ciphertext.\n * @param iv - Base64-encoded initialization vector.\n * @param key - 32-byte AES-256 key from {@link deriveEncryptionKey}.\n * @returns Decrypted UTF-8 string.\n */\nexport function decryptWithKey(\n ciphertext: string,\n iv: string,\n key: Uint8Array,\n): string {\n const ciphertextBytes = base64ToBytes(ciphertext);\n const ivBytes = base64ToBytes(iv);\n const plaintext = gcm(key, ivBytes).decrypt(ciphertextBytes);\n\n return new TextDecoder().decode(plaintext);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encoding.d.ts","sourceRoot":"","sources":["../../src/utils/encoding.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAK1D;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAI1D;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAIpD"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { bytesToBase64, base64ToBytes } from
|
|
1
|
+
import { bytesToBase64, base64ToBytes } from '@metamask/utils';
|
|
2
2
|
/**
|
|
3
3
|
* Encode a byte array as a base64url string (RFC 4648 §5).
|
|
4
4
|
*
|
|
@@ -33,4 +33,4 @@ export function bytesToHex(bytes) {
|
|
|
33
33
|
.map((byte) => byte.toString(16).padStart(2, '0'))
|
|
34
34
|
.join('');
|
|
35
35
|
}
|
|
36
|
-
//# sourceMappingURL=encoding.
|
|
36
|
+
//# sourceMappingURL=encoding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encoding.js","sourceRoot":"","sources":["../../src/utils/encoding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAiB;IAChD,OAAO,aAAa,CAAC,KAAK,CAAC;SACxB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAClD,OAAO,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AAC1E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,KAAiB;IAC1C,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;SACrB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SACjD,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC","sourcesContent":["import { bytesToBase64, base64ToBytes } from '@metamask/utils';\n\n/**\n * Encode a byte array as a base64url string (RFC 4648 §5).\n *\n * @param bytes - The bytes to encode.\n * @returns Base64url-encoded string without padding.\n */\nexport function bytesToBase64URL(bytes: Uint8Array): string {\n return bytesToBase64(bytes)\n .replace(/\\+/gu, '-')\n .replace(/\\//gu, '_')\n .replace(/[=]+$/u, '');\n}\n\n/**\n * Decode a base64url string (RFC 4648 §5) into bytes.\n *\n * @param value - Base64url-encoded string.\n * @returns Decoded bytes.\n */\nexport function base64URLToBytes(value: string): Uint8Array {\n const standard = value.replace(/-/gu, '+').replace(/_/gu, '/');\n const padLength = (4 - (standard.length % 4)) % 4;\n return Uint8Array.from(base64ToBytes(standard + '='.repeat(padLength)));\n}\n\n/**\n * Encode a byte array as a hexadecimal string.\n *\n * @param bytes - The bytes to encode.\n * @returns Hex-encoded string.\n */\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('');\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/webauthn/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,oBAAY,OAAO;IACjB,KAAK,KAAK;IACV,KAAK,KAAK;IACV,KAAK,MAAM;IACX,KAAK,MAAM;IACX,KAAK,MAAM;IACX,KAAK,MAAM;IACX,KAAK,MAAM;IACX,MAAM,MAAM;IACZ,KAAK,OAAO;IACZ,KAAK,OAAO;IACZ,KAAK,OAAO;IACZ,GAAG,SAAS;CACb;AAED;;;;GAIG;AACH,oBAAY,OAAO;IACjB,GAAG,IAAI;IACP,GAAG,IAAI;IACP,GAAG,IAAI;CACR;AAED;;;;GAIG;AACH,oBAAY,OAAO;IACjB,IAAI,IAAI;IACR,IAAI,IAAI;IACR,IAAI,IAAI;IACR,OAAO,IAAI;IACX,SAAS,IAAI;CACd;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,QAAQ;IACnB,wBAAwB;aACxB,GAAG,EAAE,CAAC;IACN,yBAAyB;aACzB,GAAG,EAAE,CAAC;IAEN,kCAAkC;aAClC,GAAG,EAAE,CAAC,CAAC;IACP,0CAA0C;aAC1C,CAAC,EAAE,CAAC,CAAC;IACL,wBAAwB;aACxB,CAAC,EAAE,CAAC,CAAC;IAEL,qDAAqD;aACrD,CAAC,EAAE,CAAC,CAAC;IACL,oDAAoD;aACpD,CAAC,EAAE,CAAC,CAAC;CACG,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/webauthn/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAN,IAAY,OAaX;AAbD,WAAY,OAAO;IACjB,wCAAU,CAAA;IACV,wCAAU,CAAA;IACV,yCAAW,CAAA;IACX,yCAAW,CAAA;IACX,yCAAW,CAAA;IACX,yCAAW,CAAA;IACX,yCAAW,CAAA;IACX,2CAAY,CAAA;IACZ,0CAAY,CAAA;IACZ,0CAAY,CAAA;IACZ,0CAAY,CAAA;IACZ,wCAAY,CAAA;AACd,CAAC,EAbW,OAAO,KAAP,OAAO,QAalB;AAED;;;;GAIG;AACH,MAAM,CAAN,IAAY,OAIX;AAJD,WAAY,OAAO;IACjB,mCAAO,CAAA;IACP,mCAAO,CAAA;IACP,mCAAO,CAAA;AACT,CAAC,EAJW,OAAO,KAAP,OAAO,QAIlB;AAED;;;;GAIG;AACH,MAAM,CAAN,IAAY,OAMX;AAND,WAAY,OAAO;IACjB,qCAAQ,CAAA;IACR,qCAAQ,CAAA;IACR,qCAAQ,CAAA;IACR,2CAAW,CAAA;IACX,+CAAa,CAAA;AACf,CAAC,EANW,OAAO,KAAP,OAAO,QAMlB;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,wBAAwB;IACxB,GAAG,EAAE,CAAC;IACN,yBAAyB;IACzB,GAAG,EAAE,CAAC;IAEN,kCAAkC;IAClC,GAAG,EAAE,CAAC,CAAC;IACP,0CAA0C;IAC1C,CAAC,EAAE,CAAC,CAAC;IACL,wBAAwB;IACxB,CAAC,EAAE,CAAC,CAAC;IAEL,qDAAqD;IACrD,CAAC,EAAE,CAAC,CAAC;IACL,oDAAoD;IACpD,CAAC,EAAE,CAAC,CAAC;CACG,CAAC","sourcesContent":["/**\n * COSE Algorithms\n *\n * @see https://www.iana.org/assignments/cose/cose.xhtml#algorithms\n */\nexport enum COSEALG {\n ES256 = -7,\n EdDSA = -8,\n ES384 = -35,\n ES512 = -36,\n PS256 = -37,\n PS384 = -38,\n PS512 = -39,\n ES256K = -47,\n RS256 = -257,\n RS384 = -258,\n RS512 = -259,\n RS1 = -65535,\n}\n\n/**\n * COSE Key Types\n *\n * @see https://www.iana.org/assignments/cose/cose.xhtml#key-type\n */\nexport enum COSEKTY {\n OKP = 1,\n EC2 = 2,\n RSA = 3,\n}\n\n/**\n * COSE Curves\n *\n * @see https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves\n */\nexport enum COSECRV {\n P256 = 1,\n P384 = 2,\n P521 = 3,\n ED25519 = 6,\n SECP256K1 = 8,\n}\n\n/**\n * COSE Key common and type-specific parameter labels.\n *\n * EC2 and RSA re-use the same numeric labels (-1, -2, -3) with different\n * semantics, so this is a plain object instead of an enum to avoid\n * duplicate-value violations.\n *\n * @see https://www.iana.org/assignments/cose/cose.xhtml#key-common-parameters\n * @see https://www.iana.org/assignments/cose/cose.xhtml#key-type-parameters\n */\nexport const COSEKEYS = {\n /** Key Type (common) */\n Kty: 1,\n /** Algorithm (common) */\n Alg: 3,\n\n /** EC2 / OKP: curve identifier */\n Crv: -1,\n /** EC2: x-coordinate / OKP: public key */\n X: -2,\n /** EC2: y-coordinate */\n Y: -3,\n\n /** RSA: modulus n (shares numeric label with Crv) */\n N: -1,\n /** RSA: exponent e (shares numeric label with X) */\n E: -2,\n} as const;\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AttestationObject } from
|
|
1
|
+
import type { AttestationObject } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* CBOR-decode an attestationObject buffer into a Map with `fmt`, `attStmt`,
|
|
4
4
|
* and `authData` entries.
|
|
@@ -7,4 +7,4 @@ import type { AttestationObject } from "./types.mjs";
|
|
|
7
7
|
* @returns Decoded AttestationObject map.
|
|
8
8
|
*/
|
|
9
9
|
export declare function decodeAttestationObject(attestationObject: Uint8Array): AttestationObject;
|
|
10
|
-
//# sourceMappingURL=decode-attestation-object.d.
|
|
10
|
+
//# sourceMappingURL=decode-attestation-object.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decode-attestation-object.d.ts","sourceRoot":"","sources":["../../src/webauthn/decode-attestation-object.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,iBAAiB,EAAE,UAAU,GAC5B,iBAAiB,CAInB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { decodePartialCBOR } from
|
|
1
|
+
import { decodePartialCBOR } from '@levischuck/tiny-cbor';
|
|
2
2
|
/**
|
|
3
3
|
* CBOR-decode an attestationObject buffer into a Map with `fmt`, `attStmt`,
|
|
4
4
|
* and `authData` entries.
|
|
@@ -11,4 +11,4 @@ export function decodeAttestationObject(attestationObject) {
|
|
|
11
11
|
const [decoded] = decodePartialCBOR(copy, 0);
|
|
12
12
|
return decoded;
|
|
13
13
|
}
|
|
14
|
-
//# sourceMappingURL=decode-attestation-object.
|
|
14
|
+
//# sourceMappingURL=decode-attestation-object.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decode-attestation-object.js","sourceRoot":"","sources":["../../src/webauthn/decode-attestation-object.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAI1D;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CACrC,iBAA6B;IAE7B,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAC/C,MAAM,CAAC,OAAO,CAAC,GAAG,iBAAiB,CAAC,IAAI,EAAE,CAAC,CAAgC,CAAC;IAC5E,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["import { decodePartialCBOR } from '@levischuck/tiny-cbor';\n\nimport type { AttestationObject } from './types.js';\n\n/**\n * CBOR-decode an attestationObject buffer into a Map with `fmt`, `attStmt`,\n * and `authData` entries.\n *\n * @param attestationObject - Raw attestation object bytes.\n * @returns Decoded AttestationObject map.\n */\nexport function decodeAttestationObject(\n attestationObject: Uint8Array,\n): AttestationObject {\n const copy = new Uint8Array(attestationObject);\n const [decoded] = decodePartialCBOR(copy, 0) as [AttestationObject, number];\n return decoded;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ClientDataJSON } from
|
|
1
|
+
import type { ClientDataJSON } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Decode an authenticator's base64url-encoded clientDataJSON to JSON.
|
|
4
4
|
*
|
|
@@ -6,4 +6,4 @@ import type { ClientDataJSON } from "./types.cjs";
|
|
|
6
6
|
* @returns Parsed ClientDataJSON object.
|
|
7
7
|
*/
|
|
8
8
|
export declare function decodeClientDataJSON(data: string): ClientDataJSON;
|
|
9
|
-
//# sourceMappingURL=decode-client-data-json.d.
|
|
9
|
+
//# sourceMappingURL=decode-client-data-json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decode-client-data-json.d.ts","sourceRoot":"","sources":["../../src/webauthn/decode-client-data-json.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAIjE"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { base64URLToBytes } from
|
|
1
|
+
import { base64URLToBytes } from '../utils/encoding.js';
|
|
2
2
|
/**
|
|
3
3
|
* Decode an authenticator's base64url-encoded clientDataJSON to JSON.
|
|
4
4
|
*
|
|
@@ -10,4 +10,4 @@ export function decodeClientDataJSON(data) {
|
|
|
10
10
|
const text = new TextDecoder().decode(bytes);
|
|
11
11
|
return JSON.parse(text);
|
|
12
12
|
}
|
|
13
|
-
//# sourceMappingURL=decode-client-data-json.
|
|
13
|
+
//# sourceMappingURL=decode-client-data-json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decode-client-data-json.js","sourceRoot":"","sources":["../../src/webauthn/decode-client-data-json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAGxD;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAmB,CAAC;AAC5C,CAAC","sourcesContent":["import { base64URLToBytes } from '../utils/encoding.js';\nimport type { ClientDataJSON } from './types.js';\n\n/**\n * Decode an authenticator's base64url-encoded clientDataJSON to JSON.\n *\n * @param data - Base64url-encoded clientDataJSON string.\n * @returns Parsed ClientDataJSON object.\n */\nexport function decodeClientDataJSON(data: string): ClientDataJSON {\n const bytes = base64URLToBytes(data);\n const text = new TextDecoder().decode(bytes);\n return JSON.parse(text) as ClientDataJSON;\n}\n"]}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @param rpIdHash - The rpIdHash from authenticatorData (32 bytes).
|
|
6
6
|
* @param expectedRPIDs - One or more RP ID strings to check against.
|
|
7
|
-
* @returns
|
|
7
|
+
* @returns A promise for the matching RP ID string.
|
|
8
8
|
* @throws If no expected RP ID matches.
|
|
9
9
|
*/
|
|
10
|
-
export declare function matchExpectedRPID(rpIdHash: Uint8Array, expectedRPIDs: string[]): string
|
|
11
|
-
//# sourceMappingURL=match-expected-rp-id.d.
|
|
10
|
+
export declare function matchExpectedRPID(rpIdHash: Uint8Array, expectedRPIDs: string[]): Promise<string>;
|
|
11
|
+
//# sourceMappingURL=match-expected-rp-id.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"match-expected-rp-id.d.ts","sourceRoot":"","sources":["../../src/webauthn/match-expected-rp-id.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,UAAU,EACpB,aAAa,EAAE,MAAM,EAAE,GACtB,OAAO,CAAC,MAAM,CAAC,CAQjB"}
|
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
import { areUint8ArraysEqual } from
|
|
2
|
-
import {
|
|
3
|
-
import { bytesToHex } from "../utils/encoding.mjs";
|
|
1
|
+
import { areUint8ArraysEqual, sha256 } from '@metamask/utils';
|
|
2
|
+
import { bytesToHex } from '../utils/encoding.js';
|
|
4
3
|
/**
|
|
5
4
|
* Verify that an authenticator data rpIdHash matches one of the expected
|
|
6
5
|
* RP IDs by SHA-256 hashing each candidate and comparing.
|
|
7
6
|
*
|
|
8
7
|
* @param rpIdHash - The rpIdHash from authenticatorData (32 bytes).
|
|
9
8
|
* @param expectedRPIDs - One or more RP ID strings to check against.
|
|
10
|
-
* @returns
|
|
9
|
+
* @returns A promise for the matching RP ID string.
|
|
11
10
|
* @throws If no expected RP ID matches.
|
|
12
11
|
*/
|
|
13
|
-
export function matchExpectedRPID(rpIdHash, expectedRPIDs) {
|
|
12
|
+
export async function matchExpectedRPID(rpIdHash, expectedRPIDs) {
|
|
14
13
|
for (const rpID of expectedRPIDs) {
|
|
15
|
-
const expectedHash = sha256(new TextEncoder().encode(rpID));
|
|
14
|
+
const expectedHash = await sha256(new TextEncoder().encode(rpID));
|
|
16
15
|
if (areUint8ArraysEqual(rpIdHash, expectedHash)) {
|
|
17
16
|
return rpID;
|
|
18
17
|
}
|
|
19
18
|
}
|
|
20
19
|
throw new Error(`Unexpected RP ID hash: received ${bytesToHex(rpIdHash)}`);
|
|
21
20
|
}
|
|
22
|
-
//# sourceMappingURL=match-expected-rp-id.
|
|
21
|
+
//# sourceMappingURL=match-expected-rp-id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"match-expected-rp-id.js","sourceRoot":"","sources":["../../src/webauthn/match-expected-rp-id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE9D,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,QAAoB,EACpB,aAAuB;IAEvB,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QAClE,IAAI,mBAAmB,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,CAAC;YAChD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,mCAAmC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC7E,CAAC","sourcesContent":["import { areUint8ArraysEqual, sha256 } from '@metamask/utils';\n\nimport { bytesToHex } from '../utils/encoding.js';\n\n/**\n * Verify that an authenticator data rpIdHash matches one of the expected\n * RP IDs by SHA-256 hashing each candidate and comparing.\n *\n * @param rpIdHash - The rpIdHash from authenticatorData (32 bytes).\n * @param expectedRPIDs - One or more RP ID strings to check against.\n * @returns A promise for the matching RP ID string.\n * @throws If no expected RP ID matches.\n */\nexport async function matchExpectedRPID(\n rpIdHash: Uint8Array,\n expectedRPIDs: string[],\n): Promise<string> {\n for (const rpID of expectedRPIDs) {\n const expectedHash = await sha256(new TextEncoder().encode(rpID));\n if (areUint8ArraysEqual(rpIdHash, expectedHash)) {\n return rpID;\n }\n }\n throw new Error(`Unexpected RP ID hash: received ${bytesToHex(rpIdHash)}`);\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ParsedAuthenticatorData } from
|
|
1
|
+
import type { ParsedAuthenticatorData } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Parse an authenticator data buffer per §6.1 of the WebAuthn spec.
|
|
4
4
|
*
|
|
@@ -7,4 +7,4 @@ import type { ParsedAuthenticatorData } from "./types.cjs";
|
|
|
7
7
|
* optional attested credential data.
|
|
8
8
|
*/
|
|
9
9
|
export declare function parseAuthenticatorData(authData: Uint8Array): ParsedAuthenticatorData;
|
|
10
|
-
//# sourceMappingURL=parse-authenticator-data.d.
|
|
10
|
+
//# sourceMappingURL=parse-authenticator-data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-authenticator-data.d.ts","sourceRoot":"","sources":["../../src/webauthn/parse-authenticator-data.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,uBAAuB,EAExB,MAAM,YAAY,CAAC;AAIpB;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,UAAU,GACnB,uBAAuB,CAkFzB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { decodePartialCBOR } from
|
|
1
|
+
import { decodePartialCBOR } from '@levischuck/tiny-cbor';
|
|
2
2
|
/* eslint-disable no-bitwise */
|
|
3
3
|
/**
|
|
4
4
|
* Parse an authenticator data buffer per §6.1 of the WebAuthn spec.
|
|
@@ -62,4 +62,4 @@ export function parseAuthenticatorData(authData) {
|
|
|
62
62
|
return result;
|
|
63
63
|
}
|
|
64
64
|
/* eslint-enable no-bitwise */
|
|
65
|
-
//# sourceMappingURL=parse-authenticator-data.
|
|
65
|
+
//# sourceMappingURL=parse-authenticator-data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-authenticator-data.js","sourceRoot":"","sources":["../../src/webauthn/parse-authenticator-data.ts"],"names":[],"mappings":"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","sourcesContent":["import { decodePartialCBOR } from '@levischuck/tiny-cbor';\n\nimport type {\n ParsedAuthenticatorData,\n AuthenticatorDataFlags,\n} from './types.js';\n\n/* eslint-disable no-bitwise */\n\n/**\n * Parse an authenticator data buffer per §6.1 of the WebAuthn spec.\n *\n * @param authData - Raw authenticator data bytes.\n * @returns Parsed authenticator data with flags, rpIdHash, counter, and\n * optional attested credential data.\n */\nexport function parseAuthenticatorData(\n authData: Uint8Array,\n): ParsedAuthenticatorData {\n if (authData.byteLength < 37) {\n throw new Error(\n `authenticatorData is ${authData.byteLength} bytes, expected at least 37`,\n );\n }\n\n let pointer = 0;\n\n const rpIdHash = authData.slice(pointer, pointer + 32);\n pointer += 32;\n\n const flagsByte = authData[pointer];\n const flags: AuthenticatorDataFlags = {\n up: Boolean(flagsByte & (1 << 0)),\n uv: Boolean(flagsByte & (1 << 2)),\n be: Boolean(flagsByte & (1 << 3)),\n bs: Boolean(flagsByte & (1 << 4)),\n at: Boolean(flagsByte & (1 << 6)),\n ed: Boolean(flagsByte & (1 << 7)),\n flagsByte,\n };\n pointer += 1;\n\n const counterView = new DataView(\n authData.buffer,\n authData.byteOffset + pointer,\n 4,\n );\n const counter = counterView.getUint32(0, false);\n pointer += 4;\n\n const result: ParsedAuthenticatorData = {\n rpIdHash,\n flags,\n counter,\n };\n\n if (flags.at) {\n const aaguid = authData.slice(pointer, pointer + 16);\n pointer += 16;\n\n const credIDLenView = new DataView(\n authData.buffer,\n authData.byteOffset + pointer,\n 2,\n );\n const credIDLen = credIDLenView.getUint16(0, false);\n pointer += 2;\n\n const credentialID = authData.slice(pointer, pointer + credIDLen);\n pointer += credIDLen;\n\n const pubKeyBytes = authData.slice(pointer);\n const [, nextOffset] = decodePartialCBOR(\n new Uint8Array(pubKeyBytes),\n 0,\n ) as [unknown, number];\n const credentialPublicKey = authData.slice(pointer, pointer + nextOffset);\n pointer += nextOffset;\n\n result.aaguid = aaguid;\n result.credentialID = credentialID;\n result.credentialPublicKey = credentialPublicKey;\n }\n\n if (flags.ed) {\n const remaining = authData.slice(pointer);\n const [decoded, consumed] = decodePartialCBOR(\n new Uint8Array(remaining),\n 0,\n ) as [Map<string, unknown>, number];\n result.extensionsData = decoded;\n result.extensionsDataBuffer = remaining.slice(0, consumed);\n pointer += consumed;\n }\n\n if (authData.byteLength > pointer) {\n throw new Error('Leftover bytes detected while parsing authenticator data');\n }\n\n return result;\n}\n\n/* eslint-enable no-bitwise */\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthenticatorTransportFuture, Base64URLString as Base64URL } from
|
|
1
|
+
import type { AuthenticatorTransportFuture, Base64URLString as Base64URL } from '../types.js';
|
|
2
2
|
export type PublicKeyCredentialDescriptorJSON = {
|
|
3
3
|
id: Base64URL;
|
|
4
4
|
type: 'public-key';
|
|
@@ -110,4 +110,4 @@ export type ParsedAuthenticatorData = {
|
|
|
110
110
|
extensionsData?: Map<string, unknown>;
|
|
111
111
|
extensionsDataBuffer?: Uint8Array;
|
|
112
112
|
};
|
|
113
|
-
//# sourceMappingURL=types.d.
|
|
113
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/webauthn/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/webauthn/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n AuthenticatorTransportFuture,\n Base64URLString as Base64URL,\n} from '../types.js';\n\nexport type PublicKeyCredentialDescriptorJSON = {\n id: Base64URL;\n type: 'public-key';\n transports?: AuthenticatorTransportFuture[];\n};\n\nexport type PublicKeyCredentialHint =\n | 'hybrid'\n | 'security-key'\n | 'client-device';\n\nexport type PasskeyRegistrationOptions = {\n rp: { name: string; id?: string };\n user: {\n id: Base64URL;\n name: string;\n displayName: string;\n };\n challenge: Base64URL;\n pubKeyCredParams: { alg: number; type: 'public-key' }[];\n timeout?: number;\n excludeCredentials?: PublicKeyCredentialDescriptorJSON[];\n authenticatorSelection?: {\n authenticatorAttachment?: 'cross-platform' | 'platform';\n residentKey?: 'discouraged' | 'preferred' | 'required';\n requireResidentKey?: boolean;\n userVerification?: 'discouraged' | 'preferred' | 'required';\n };\n hints?: PublicKeyCredentialHint[];\n attestation?: 'direct' | 'enterprise' | 'indirect' | 'none';\n extensions?: Record<string, unknown>;\n};\n\nexport type PasskeyRegistrationResponse = {\n id: Base64URL;\n rawId: Base64URL;\n type: 'public-key';\n response: {\n clientDataJSON: Base64URL;\n attestationObject: Base64URL;\n transports?: string[];\n publicKeyAlgorithm?: number;\n publicKey?: Base64URL;\n authenticatorData?: Base64URL;\n };\n authenticatorAttachment?: 'cross-platform' | 'platform';\n clientExtensionResults: Record<string, unknown>;\n};\n\nexport type PasskeyAuthenticationOptions = {\n challenge: Base64URL;\n timeout?: number;\n rpId?: string;\n allowCredentials?: PublicKeyCredentialDescriptorJSON[];\n userVerification?: 'discouraged' | 'preferred' | 'required';\n hints?: PublicKeyCredentialHint[];\n extensions?: Record<string, unknown>;\n};\n\nexport type PasskeyAuthenticationResponse = {\n id: Base64URL;\n rawId: Base64URL;\n type: 'public-key';\n response: {\n clientDataJSON: Base64URL;\n authenticatorData: Base64URL;\n signature: Base64URL;\n userHandle?: Base64URL;\n };\n authenticatorAttachment?: 'cross-platform' | 'platform';\n clientExtensionResults: Record<string, unknown>;\n};\n\nexport type ClientDataJSON = {\n type: string;\n challenge: string;\n origin: string;\n crossOrigin?: boolean;\n tokenBinding?: {\n id?: string;\n status: 'present' | 'supported' | 'not-supported';\n };\n};\n\nexport type AttestationFormat =\n | 'fido-u2f'\n | 'packed'\n | 'android-safetynet'\n | 'android-key'\n | 'tpm'\n | 'apple'\n | 'none';\n\nexport type AttestationObject = {\n get(key: 'fmt'): AttestationFormat;\n get(key: 'attStmt'): AttestationStatement;\n get(key: 'authData'): Uint8Array;\n};\n\nexport type AttestationStatement = {\n get(key: 'sig'): Uint8Array | undefined;\n get(key: 'x5c'): Uint8Array[] | undefined;\n get(key: 'alg'): number | undefined;\n readonly size: number;\n};\n\nexport type AuthenticatorDataFlags = {\n up: boolean;\n uv: boolean;\n be: boolean;\n bs: boolean;\n at: boolean;\n ed: boolean;\n flagsByte: number;\n};\n\nexport type ParsedAuthenticatorData = {\n rpIdHash: Uint8Array;\n flags: AuthenticatorDataFlags;\n counter: number;\n aaguid?: Uint8Array;\n credentialID?: Uint8Array;\n credentialPublicKey?: Uint8Array;\n extensionsData?: Map<string, unknown>;\n extensionsDataBuffer?: Uint8Array;\n};\n"]}
|
package/dist/webauthn/{verify-authentication-response.d.cts → verify-authentication-response.d.ts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AuthenticatorTransportFuture } from
|
|
2
|
-
import type { PasskeyAuthenticationResponse } from
|
|
1
|
+
import type { AuthenticatorTransportFuture } from '../types.js';
|
|
2
|
+
import type { PasskeyAuthenticationResponse } from './types.js';
|
|
3
3
|
export type VerifiedAuthenticationResponse = {
|
|
4
4
|
verified: false;
|
|
5
5
|
authenticationInfo?: never;
|
|
@@ -61,4 +61,4 @@ export declare function verifyAuthenticationResponse(opts: {
|
|
|
61
61
|
};
|
|
62
62
|
requireUserVerification?: boolean;
|
|
63
63
|
}): Promise<VerifiedAuthenticationResponse>;
|
|
64
|
-
//# sourceMappingURL=verify-authentication-response.d.
|
|
64
|
+
//# sourceMappingURL=verify-authentication-response.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-authentication-response.d.ts","sourceRoot":"","sources":["../../src/webauthn/verify-authentication-response.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAMhE,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAC;AAGhE,MAAM,MAAM,8BAA8B,GACtC;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,kBAAkB,CAAC,EAAE,KAAK,CAAA;CAAE,GAC/C;IACE,QAAQ,EAAE,IAAI,CAAC;IACf,kBAAkB,EAAE;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,OAAO,CAAC;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,uFAAuF;QACvF,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH,CAAC;AAEN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAsB,4BAA4B,CAAC,IAAI,EAAE;IACvD,QAAQ,EAAE,6BAA6B,CAAC;IACxC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAClC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,EAAE;QACV,EAAE,EAAE,MAAM,CAAC;QACX,SAAS,EAAE,UAAU,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,4BAA4B,EAAE,CAAC;KAC7C,CAAC;IACF,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC,GAAG,OAAO,CAAC,8BAA8B,CAAC,CAiJ1C"}
|
package/dist/webauthn/{verify-authentication-response.mjs → verify-authentication-response.js}
RENAMED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { decodePartialCBOR } from
|
|
2
|
-
import { concatBytes } from
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { verifySignature } from "./verify-signature.mjs";
|
|
1
|
+
import { decodePartialCBOR } from '@levischuck/tiny-cbor';
|
|
2
|
+
import { concatBytes, sha256 } from '@metamask/utils';
|
|
3
|
+
import { base64URLToBytes } from '../utils/encoding.js';
|
|
4
|
+
import { decodeClientDataJSON } from './decode-client-data-json.js';
|
|
5
|
+
import { matchExpectedRPID } from './match-expected-rp-id.js';
|
|
6
|
+
import { parseAuthenticatorData } from './parse-authenticator-data.js';
|
|
7
|
+
import { verifySignature } from './verify-signature.js';
|
|
9
8
|
/**
|
|
10
9
|
* Verifies a WebAuthn authentication (assertion) response per
|
|
11
10
|
* W3C WebAuthn Level 3 §7.2.
|
|
@@ -90,7 +89,10 @@ export async function verifyAuthenticationResponse(opts) {
|
|
|
90
89
|
const authDataBuffer = base64URLToBytes(assertionResponse.authenticatorData);
|
|
91
90
|
const parsedAuthData = parseAuthenticatorData(authDataBuffer);
|
|
92
91
|
const { rpIdHash, flags, counter } = parsedAuthData;
|
|
93
|
-
|
|
92
|
+
let matchedRPID = '';
|
|
93
|
+
if (expectedRPIDs.length > 0) {
|
|
94
|
+
matchedRPID = await matchExpectedRPID(rpIdHash, expectedRPIDs);
|
|
95
|
+
}
|
|
94
96
|
// WebAuthn only requires the user presence flag be true
|
|
95
97
|
if (!flags.up) {
|
|
96
98
|
throw new Error('User not present during authentication');
|
|
@@ -99,7 +101,7 @@ export async function verifyAuthenticationResponse(opts) {
|
|
|
99
101
|
if (requireUserVerification && !flags.uv) {
|
|
100
102
|
throw new Error('User verification required, but user could not be verified');
|
|
101
103
|
}
|
|
102
|
-
const clientDataHash = sha256(base64URLToBytes(assertionResponse.clientDataJSON));
|
|
104
|
+
const clientDataHash = await sha256(base64URLToBytes(assertionResponse.clientDataJSON));
|
|
103
105
|
const signatureBase = concatBytes([authDataBuffer, clientDataHash]);
|
|
104
106
|
const signature = base64URLToBytes(assertionResponse.signature);
|
|
105
107
|
const cosePublicKey = decodePartialCBOR(new Uint8Array(credential.publicKey), 0)[0];
|
|
@@ -126,4 +128,4 @@ export async function verifyAuthenticationResponse(opts) {
|
|
|
126
128
|
},
|
|
127
129
|
};
|
|
128
130
|
}
|
|
129
|
-
//# sourceMappingURL=verify-authentication-response.
|
|
131
|
+
//# sourceMappingURL=verify-authentication-response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-authentication-response.js","sourceRoot":"","sources":["../../src/webauthn/verify-authentication-response.ts"],"names":[],"mappings":"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","sourcesContent":["import { decodePartialCBOR } from '@levischuck/tiny-cbor';\nimport { concatBytes, sha256 } from '@metamask/utils';\n\nimport type { AuthenticatorTransportFuture } from '../types.js';\nimport { base64URLToBytes } from '../utils/encoding.js';\nimport { decodeClientDataJSON } from './decode-client-data-json.js';\nimport { matchExpectedRPID } from './match-expected-rp-id.js';\nimport { parseAuthenticatorData } from './parse-authenticator-data.js';\nimport type { ParsedAuthenticatorData } from './types.js';\nimport type { PasskeyAuthenticationResponse } from './types.js';\nimport { verifySignature } from './verify-signature.js';\n\nexport type VerifiedAuthenticationResponse =\n | { verified: false; authenticationInfo?: never }\n | {\n verified: true;\n authenticationInfo: {\n credentialId: string;\n newCounter: number;\n userVerified: boolean;\n origin: string;\n /** Matched RP ID, or `\"\"` when `expectedRPIDs` is empty (RP ID hash check skipped). */\n rpID: string;\n };\n };\n\n/**\n * Verifies a WebAuthn authentication (assertion) response per\n * W3C WebAuthn Level 3 §7.2.\n *\n * Performs the following checks in order:\n * 1. Credential ID presence, base64url consistency, and type.\n * 2. `clientDataJSON` -- type is `\"webauthn.get\"`, challenge and origin\n * match.\n * 3. `authenticatorData` -- RP ID hash matches, user-presence flag is\n * set, and optional user-verification flag is checked.\n * 4. Signature verification -- `signature` is verified over\n * `authData || SHA-256(clientDataJSON)` using the stored credential\n * public key (COSE-encoded).\n * 5. Counter monotonicity -- if either the stored or returned counter\n * is non-zero, the new counter must exceed the stored value.\n *\n * @param opts - Verification options.\n * @param opts.response - The `PublicKeyCredential` result from\n * `navigator.credentials.get()`, serialized as JSON.\n * @param opts.expectedChallenge - The base64url challenge that was issued\n * for this ceremony.\n * @param opts.expectedOrigin - One or more acceptable origins.\n * @param opts.expectedRPIDs - Relying Party ID strings to match against `rpIdHash`.\n * @param opts.credential - The stored credential record to verify against.\n * @param opts.credential.id - The credential ID (base64url).\n * @param opts.credential.publicKey - The COSE-encoded public key bytes\n * persisted during registration.\n * @param opts.credential.counter - The last known signature counter value.\n * @param opts.credential.transports - Optional authenticator transports.\n * @param opts.requireUserVerification - When `true`, verification fails\n * if the UV flag is not set. Defaults to `false`.\n * @returns Verification result containing `verified` status and parsed\n * authentication info (new counter, origin, RP ID).\n */\nexport async function verifyAuthenticationResponse(opts: {\n response: PasskeyAuthenticationResponse;\n expectedChallenge: string;\n expectedOrigin: string | string[];\n expectedRPIDs: string[];\n credential: {\n id: string;\n publicKey: Uint8Array;\n counter: number;\n transports?: AuthenticatorTransportFuture[];\n };\n requireUserVerification?: boolean;\n}): Promise<VerifiedAuthenticationResponse> {\n const {\n response,\n expectedChallenge,\n expectedOrigin,\n expectedRPIDs,\n credential,\n requireUserVerification = false,\n } = opts;\n\n const {\n id,\n rawId,\n type: credentialType,\n response: assertionResponse,\n } = response;\n\n // Ensure credential specified an ID\n if (!id) {\n throw new Error('Missing credential ID');\n }\n\n // Ensure ID is base64url-encoded\n if (id !== rawId) {\n throw new Error('Credential ID was not base64url-encoded');\n }\n\n // Make sure credential type is public-key\n if (credentialType !== 'public-key') {\n throw new Error(\n `Unexpected credential type ${String(credentialType)}, expected \"public-key\"`,\n );\n }\n\n if (typeof assertionResponse?.clientDataJSON !== 'string') {\n throw new Error('Credential response clientDataJSON was not a string');\n }\n\n const clientDataJSON = decodeClientDataJSON(assertionResponse.clientDataJSON);\n const { type, challenge, origin, tokenBinding } = clientDataJSON;\n\n // Make sure we're handling an authentication\n if (type !== 'webauthn.get') {\n throw new Error(`Unexpected authentication response type: ${type}`);\n }\n\n // Ensure the device provided the challenge we gave it\n if (challenge !== expectedChallenge) {\n throw new Error(\n `Unexpected authentication response challenge \"${challenge}\", expected \"${expectedChallenge}\"`,\n );\n }\n\n // Check that the origin is our site\n const expectedOrigins = Array.isArray(expectedOrigin)\n ? expectedOrigin\n : [expectedOrigin];\n if (!expectedOrigins.includes(origin)) {\n throw new Error(\n `Unexpected authentication response origin \"${origin}\", expected one of: ${expectedOrigins.join(', ')}`,\n );\n }\n\n if (\n assertionResponse.userHandle &&\n typeof assertionResponse.userHandle !== 'string'\n ) {\n throw new Error('Credential response userHandle was not a string');\n }\n\n if (tokenBinding) {\n if (typeof tokenBinding !== 'object') {\n throw new Error('ClientDataJSON tokenBinding was not an object');\n }\n\n if (\n !['present', 'supported', 'not-supported'].includes(tokenBinding.status)\n ) {\n throw new Error(`Unexpected tokenBinding status ${tokenBinding.status}`);\n }\n }\n\n const authDataBuffer = base64URLToBytes(assertionResponse.authenticatorData);\n const parsedAuthData: ParsedAuthenticatorData =\n parseAuthenticatorData(authDataBuffer);\n const { rpIdHash, flags, counter } = parsedAuthData;\n\n let matchedRPID = '';\n if (expectedRPIDs.length > 0) {\n matchedRPID = await matchExpectedRPID(rpIdHash, expectedRPIDs);\n }\n\n // WebAuthn only requires the user presence flag be true\n if (!flags.up) {\n throw new Error('User not present during authentication');\n }\n\n // Enforce user verification if required\n if (requireUserVerification && !flags.uv) {\n throw new Error(\n 'User verification required, but user could not be verified',\n );\n }\n\n const clientDataHash = await sha256(\n base64URLToBytes(assertionResponse.clientDataJSON),\n );\n const signatureBase = concatBytes([authDataBuffer, clientDataHash]);\n\n const signature = base64URLToBytes(assertionResponse.signature);\n\n const cosePublicKey = decodePartialCBOR(\n new Uint8Array(credential.publicKey),\n 0,\n )[0] as Map<number, number | Uint8Array>;\n\n const verified = await verifySignature({\n cosePublicKey,\n signature,\n data: signatureBase,\n });\n\n if (!verified) {\n return { verified: false };\n }\n\n if (\n (counter > 0 || credential.counter > 0) &&\n counter <= credential.counter\n ) {\n throw new Error(\n `Response counter value ${counter} must be greater than stored counter ${credential.counter}`,\n );\n }\n\n return {\n verified: true,\n authenticationInfo: {\n credentialId: credential.id,\n newCounter: counter,\n userVerified: flags.uv,\n origin: clientDataJSON.origin,\n rpID: matchedRPID,\n },\n };\n}\n"]}
|
package/dist/webauthn/{verify-registration-response.d.mts → verify-registration-response.d.ts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AuthenticatorTransportFuture } from
|
|
2
|
-
import type { PasskeyRegistrationResponse } from
|
|
1
|
+
import type { AuthenticatorTransportFuture } from '../types.js';
|
|
2
|
+
import type { PasskeyRegistrationResponse } from './types.js';
|
|
3
3
|
export type VerifiedRegistrationResponse = {
|
|
4
4
|
verified: false;
|
|
5
5
|
registrationInfo?: never;
|
|
@@ -78,4 +78,4 @@ export declare function verifyRegistrationResponse(opts: {
|
|
|
78
78
|
* @throws If the attestation object or its authenticator data is malformed.
|
|
79
79
|
*/
|
|
80
80
|
export declare function getAAGUIDFromRegistrationResponse(registrationResponse: PasskeyRegistrationResponse): string | undefined;
|
|
81
|
-
//# sourceMappingURL=verify-registration-response.d.
|
|
81
|
+
//# sourceMappingURL=verify-registration-response.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-registration-response.d.ts","sourceRoot":"","sources":["../../src/webauthn/verify-registration-response.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAWhE,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAG9D,MAAM,MAAM,4BAA4B,GACpC;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,gBAAgB,CAAC,EAAE,KAAK,CAAA;CAAE,GAC7C;IACE,QAAQ,EAAE,IAAI,CAAC;IACf,gBAAgB,EAAE;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,UAAU,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,4BAA4B,EAAE,CAAC;QAC5C,MAAM,EAAE,MAAM,CAAC;QACf,iBAAiB,EAAE,MAAM,CAAC;QAC1B,YAAY,EAAE,OAAO,CAAC;KACvB,CAAC;CACH,CAAC;AAEN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAsB,0BAA0B,CAAC,IAAI,EAAE;IACrD,QAAQ,EAAE,2BAA2B,CAAC;IACtC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAClC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;CAClC,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAkLxC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iCAAiC,CAC/C,oBAAoB,EAAE,2BAA2B,GAChD,MAAM,GAAG,SAAS,CAMpB"}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { decodePartialCBOR } from
|
|
2
|
-
import { concatBytes } from
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { verifySignature } from "./verify-signature.mjs";
|
|
1
|
+
import { decodePartialCBOR } from '@levischuck/tiny-cbor';
|
|
2
|
+
import { concatBytes, sha256 } from '@metamask/utils';
|
|
3
|
+
import { base64URLToBytes, bytesToBase64URL, bytesToHex, } from '../utils/encoding.js';
|
|
4
|
+
import { COSEALG, COSEKEYS } from './constants.js';
|
|
5
|
+
import { decodeAttestationObject } from './decode-attestation-object.js';
|
|
6
|
+
import { decodeClientDataJSON } from './decode-client-data-json.js';
|
|
7
|
+
import { matchExpectedRPID } from './match-expected-rp-id.js';
|
|
8
|
+
import { parseAuthenticatorData } from './parse-authenticator-data.js';
|
|
9
|
+
import { verifySignature } from './verify-signature.js';
|
|
11
10
|
/**
|
|
12
11
|
* Verifies a WebAuthn registration (attestation) response per
|
|
13
12
|
* W3C WebAuthn Level 3 §7.1.
|
|
@@ -90,7 +89,7 @@ export async function verifyRegistrationResponse(opts) {
|
|
|
90
89
|
const parsedAuthData = parseAuthenticatorData(authData);
|
|
91
90
|
const { rpIdHash, flags, counter, credentialID, credentialPublicKey, aaguid, } = parsedAuthData;
|
|
92
91
|
if (expectedRPIDs.length > 0) {
|
|
93
|
-
matchExpectedRPID(rpIdHash, expectedRPIDs);
|
|
92
|
+
await matchExpectedRPID(rpIdHash, expectedRPIDs);
|
|
94
93
|
}
|
|
95
94
|
// Make sure someone was physically present
|
|
96
95
|
if (!flags.up) {
|
|
@@ -226,7 +225,7 @@ async function verifyPackedAttestation(attStmt, authData, clientDataJSONB64url,
|
|
|
226
225
|
if (attStmtAlg !== credAlg) {
|
|
227
226
|
throw new Error(`Packed attestation alg ${attStmtAlg} does not match credential alg ${credAlg}`);
|
|
228
227
|
}
|
|
229
|
-
const clientDataHash = sha256(base64URLToBytes(clientDataJSONB64url));
|
|
228
|
+
const clientDataHash = await sha256(base64URLToBytes(clientDataJSONB64url));
|
|
230
229
|
const signatureBase = concatBytes([authData, clientDataHash]);
|
|
231
230
|
return verifySignature({
|
|
232
231
|
cosePublicKey,
|
|
@@ -234,4 +233,4 @@ async function verifyPackedAttestation(attStmt, authData, clientDataJSONB64url,
|
|
|
234
233
|
data: signatureBase,
|
|
235
234
|
});
|
|
236
235
|
}
|
|
237
|
-
//# sourceMappingURL=verify-registration-response.
|
|
236
|
+
//# sourceMappingURL=verify-registration-response.js.map
|