@metamask/passkey-controller 4.0.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +23 -1
  2. package/README.md +101 -9
  3. package/dist/PasskeyController-method-action-types.d.ts +42 -1
  4. package/dist/PasskeyController-method-action-types.d.ts.map +1 -1
  5. package/dist/PasskeyController-method-action-types.js.map +1 -1
  6. package/dist/PasskeyController.d.ts +35 -0
  7. package/dist/PasskeyController.d.ts.map +1 -1
  8. package/dist/PasskeyController.js +206 -58
  9. package/dist/PasskeyController.js.map +1 -1
  10. package/dist/ceremony-manager.d.ts.map +1 -1
  11. package/dist/ceremony-manager.js +16 -1
  12. package/dist/ceremony-manager.js.map +1 -1
  13. package/dist/constants.d.ts +6 -0
  14. package/dist/constants.d.ts.map +1 -1
  15. package/dist/constants.js +6 -0
  16. package/dist/constants.js.map +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/types.d.ts +17 -3
  21. package/dist/types.d.ts.map +1 -1
  22. package/dist/types.js.map +1 -1
  23. package/dist/utils/crypto.d.ts.map +1 -1
  24. package/dist/utils/crypto.js +4 -2
  25. package/dist/utils/crypto.js.map +1 -1
  26. package/dist/webauthn/match-expected-rp-id.d.ts +2 -2
  27. package/dist/webauthn/match-expected-rp-id.d.ts.map +1 -1
  28. package/dist/webauthn/match-expected-rp-id.js +4 -5
  29. package/dist/webauthn/match-expected-rp-id.js.map +1 -1
  30. package/dist/webauthn/verify-authentication-response.d.ts.map +1 -1
  31. package/dist/webauthn/verify-authentication-response.js +6 -4
  32. package/dist/webauthn/verify-authentication-response.js.map +1 -1
  33. package/dist/webauthn/verify-registration-response.d.ts.map +1 -1
  34. package/dist/webauthn/verify-registration-response.js +3 -4
  35. package/dist/webauthn/verify-registration-response.js.map +1 -1
  36. package/dist/webauthn/verify-signature.d.ts +3 -2
  37. package/dist/webauthn/verify-signature.d.ts.map +1 -1
  38. package/dist/webauthn/verify-signature.js +12 -11
  39. package/dist/webauthn/verify-signature.js.map +1 -1
  40. package/package.json +3 -3
@@ -42,6 +42,9 @@ export const passkeyControllerSelectors = {
42
42
  const MESSENGER_EXPOSED_METHODS = [
43
43
  'isPasskeyEnrolled',
44
44
  'generateRegistrationOptions',
45
+ 'generatePasskeyReplacementRegistrationOptions',
46
+ 'completePasskeyReplacement',
47
+ 'cancelPasskeyReplacement',
45
48
  'generatePostRegistrationAuthenticationOptions',
46
49
  'generateAuthenticationOptions',
47
50
  'protectVaultKeyWithPasskey',
@@ -120,10 +123,42 @@ export class PasskeyController extends BaseController {
120
123
  * @returns Public key credential creation options for `navigator.credentials.create()`.
121
124
  */
122
125
  generateRegistrationOptions(creationOptionsConfig) {
123
- if (this.isPasskeyEnrolled()) {
126
+ return this.#generateRegistrationOptions({
127
+ includePrf: creationOptionsConfig?.prfAvailable !== false,
128
+ });
129
+ }
130
+ /**
131
+ * Builds WebAuthn credential creation options for replacing a userHandle
132
+ * passkey with a PRF-capable passkey.
133
+ *
134
+ * The existing passkey record is retained while the replacement ceremony is
135
+ * in flight.
136
+ *
137
+ * @returns Public key credential creation options for `navigator.credentials.create()`.
138
+ */
139
+ generatePasskeyReplacementRegistrationOptions() {
140
+ const record = this.#requireEnrolled();
141
+ if (record.keyDerivation.method !== 'userHandle') {
142
+ throw new PasskeyControllerError(PasskeyControllerErrorMessage.MigrationNotRequired, { code: PasskeyControllerErrorCode.MigrationNotRequired });
143
+ }
144
+ const excludeCredential = {
145
+ id: record.credential.id,
146
+ type: 'public-key',
147
+ ...(record.credential.transports
148
+ ? { transports: record.credential.transports }
149
+ : {}),
150
+ };
151
+ return this.#generateRegistrationOptions({
152
+ excludeCredentials: [excludeCredential],
153
+ includePrf: true,
154
+ isReplacement: true,
155
+ sourceCredentialId: record.credential.id,
156
+ });
157
+ }
158
+ #generateRegistrationOptions({ excludeCredentials, includePrf, isReplacement = false, sourceCredentialId, }) {
159
+ if (!isReplacement && this.isPasskeyEnrolled()) {
124
160
  throw new PasskeyControllerError(PasskeyControllerErrorMessage.AlreadyEnrolled, { code: PasskeyControllerErrorCode.AlreadyEnrolled });
125
161
  }
126
- const includePrf = creationOptionsConfig?.prfAvailable !== false;
127
162
  const prfSalt = includePrf ? randomBytesToBase64URL(32) : undefined;
128
163
  const userHandle = randomBytesToBase64URL(64);
129
164
  const challenge = randomBytesToBase64URL(32);
@@ -155,6 +190,7 @@ export class PasskeyController extends BaseController {
155
190
  },
156
191
  hints: ['client-device', 'hybrid'],
157
192
  attestation: 'none',
193
+ ...(excludeCredentials ? { excludeCredentials } : {}),
158
194
  ...(Object.keys(extensions).length > 0 ? { extensions } : {}),
159
195
  };
160
196
  this.#ceremonyManager.saveRegistrationCeremony(challenge, {
@@ -162,9 +198,74 @@ export class PasskeyController extends BaseController {
162
198
  prfSalt,
163
199
  challenge,
164
200
  createdAt: Date.now(),
201
+ ...(isReplacement ? { isReplacement: true, sourceCredentialId } : {}),
165
202
  });
166
203
  return options;
167
204
  }
205
+ /**
206
+ * Verifies and completes replacement of an enrolled userHandle passkey with
207
+ * a PRF-capable passkey.
208
+ *
209
+ * The existing passkey record remains active until the replacement
210
+ * registration and post-registration authentication have both been verified
211
+ * and the existing vault key has been wrapped with the new PRF-derived key.
212
+ *
213
+ * @param params - Replacement completion inputs.
214
+ * @param params.registrationResponse - Result of `navigator.credentials.create()`.
215
+ * @param params.authenticationResponse - Result of `navigator.credentials.get()`
216
+ * after {@link generatePostRegistrationAuthenticationOptions}.
217
+ * @returns Resolves when the replacement completes.
218
+ */
219
+ async completePasskeyReplacement(params) {
220
+ return this.#withOperationLock(() => this.#completePasskeyReplacement(params));
221
+ }
222
+ async #completePasskeyReplacement(params) {
223
+ const sourceRecord = this.#requireEnrolled();
224
+ if (sourceRecord.keyDerivation.method !== 'userHandle') {
225
+ throw new PasskeyControllerError(PasskeyControllerErrorMessage.MigrationNotRequired, { code: PasskeyControllerErrorCode.MigrationNotRequired });
226
+ }
227
+ const { challenge: registrationChallenge, ceremony: registrationCeremony } = this.#getRegistrationCeremony(params.registrationResponse);
228
+ if (!registrationCeremony?.isReplacement) {
229
+ throw new PasskeyControllerError(PasskeyControllerErrorMessage.NoRegistrationCeremony, { code: PasskeyControllerErrorCode.NoRegistrationCeremony });
230
+ }
231
+ if (registrationCeremony.sourceCredentialId !== sourceRecord.credential.id) {
232
+ throw new PasskeyControllerError(PasskeyControllerErrorMessage.ReplacementSourceChanged, { code: PasskeyControllerErrorCode.ReplacementSourceChanged });
233
+ }
234
+ // Migration should only run when keyring is unlocked.
235
+ // this will throw an error if the keyring is locked.
236
+ const vaultKey = await this.messenger.call('KeyringController:exportEncryptionKey');
237
+ try {
238
+ const credential = await this.#verifyRegistrationResponse(params.registrationResponse, registrationCeremony);
239
+ if (credential.id === sourceRecord.credential.id) {
240
+ throw new PasskeyControllerError(PasskeyControllerErrorMessage.RegistrationVerificationFailed, { code: PasskeyControllerErrorCode.RegistrationVerificationFailed });
241
+ }
242
+ const { newCounter } = await this.#verifyAuthenticationResponse(params.authenticationResponse, credential);
243
+ const keyDerivation = this.#getKeyDerivation(params.authenticationResponse, registrationCeremony, { requirePrf: true });
244
+ const replacementRecord = this.#createPasskeyRecord({
245
+ vaultKey,
246
+ authenticationResponse: params.authenticationResponse,
247
+ credential,
248
+ newCounter,
249
+ keyDerivation,
250
+ });
251
+ this.#savePasskeyRecord(replacementRecord, sourceRecord);
252
+ }
253
+ finally {
254
+ this.#ceremonyManager.deleteRegistrationCeremony(registrationChallenge);
255
+ }
256
+ }
257
+ /**
258
+ * Cancels an in-flight passkey replacement ceremony.
259
+ *
260
+ * @param registrationChallenge - Challenge returned by
261
+ * {@link generatePasskeyReplacementRegistrationOptions}.
262
+ */
263
+ cancelPasskeyReplacement(registrationChallenge) {
264
+ const registrationCeremony = this.#ceremonyManager.getRegistrationCeremony(registrationChallenge);
265
+ if (registrationCeremony?.isReplacement) {
266
+ this.#ceremonyManager.deleteRegistrationCeremony(registrationChallenge);
267
+ }
268
+ }
168
269
  /**
169
270
  * Builds WebAuthn credential request options for the post-registration
170
271
  * authentication step (between `create` and {@link protectVaultKeyWithPasskey}).
@@ -206,6 +307,7 @@ export class PasskeyController extends BaseController {
206
307
  // save auth ceremony
207
308
  this.#ceremonyManager.saveAuthenticationCeremony(challenge, {
208
309
  challenge,
310
+ registrationChallenge: regChallenge,
209
311
  createdAt: Date.now(),
210
312
  });
211
313
  return options;
@@ -265,74 +367,113 @@ export class PasskeyController extends BaseController {
265
367
  }
266
368
  await this.#assertEnrollmentAllowed(params.password);
267
369
  const vaultKey = await this.messenger.call('KeyringController:exportEncryptionKey');
268
- const { registrationResponse, authenticationResponse } = params;
269
- // get registration ceremony
270
- const challenge = this.#getChallengeFromClientData(registrationResponse.response.clientDataJSON);
271
- const registrationCeremony = this.#ceremonyManager.getRegistrationCeremony(challenge);
370
+ const { registrationResponse } = params;
371
+ const { challenge, ceremony: registrationCeremony } = this.#getRegistrationCeremony(registrationResponse);
272
372
  if (!registrationCeremony) {
273
373
  log('No active passkey registration ceremony for challenge');
274
374
  throw new PasskeyControllerError(PasskeyControllerErrorMessage.NoRegistrationCeremony, { code: PasskeyControllerErrorCode.NoRegistrationCeremony });
275
375
  }
276
376
  try {
277
- // verify registration response
278
- const { verified, registrationInfo } = await verifyRegistrationResponse({
279
- response: registrationResponse,
280
- expectedChallenge: registrationCeremony.challenge,
281
- expectedOrigin: this.#expectedOrigin,
282
- expectedRPIDs: this.#expectedRPIDs,
283
- requireUserVerification: true,
284
- }).catch((error) => {
285
- log('Error verifying passkey registration response', error);
286
- throw new PasskeyControllerError(PasskeyControllerErrorMessage.RegistrationVerificationFailed, {
287
- code: PasskeyControllerErrorCode.RegistrationVerificationFailed,
288
- cause: error instanceof Error ? error : new Error(String(error)),
289
- });
290
- });
291
- if (!verified || !registrationInfo) {
292
- log('Passkey registration verification returned unverified or missing registration info');
293
- throw new PasskeyControllerError(PasskeyControllerErrorMessage.RegistrationVerificationFailed, { code: PasskeyControllerErrorCode.RegistrationVerificationFailed });
294
- }
295
- // verify authentication response
296
- const credential = {
297
- id: registrationInfo.credentialId,
298
- publicKey: bytesToBase64URL(registrationInfo.publicKey),
299
- counter: registrationInfo.counter,
300
- transports: registrationInfo.transports,
301
- aaguid: registrationInfo.aaguid,
302
- };
303
- const { newCounter } = await this.#verifyAuthenticationResponse(authenticationResponse, credential);
304
- // determine key derivation method
305
- const prfFirst = authenticationResponse.clientExtensionResults?.prf?.results?.first;
306
- const authHasPrfOutput = typeof prfFirst === 'string' && prfFirst.length > 0;
307
- const keyDerivation = authHasPrfOutput && registrationCeremony.prfSalt
308
- ? { method: 'prf', prfSalt: registrationCeremony.prfSalt }
309
- : { method: 'userHandle' };
310
- if (keyDerivation.method === 'userHandle' &&
311
- authenticationResponse.response.userHandle !==
312
- registrationCeremony.userHandle) {
313
- log('Post-registration assertion userHandle does not match registration ceremony');
314
- throw new PasskeyControllerError(PasskeyControllerErrorMessage.AuthenticationVerificationFailed, { code: PasskeyControllerErrorCode.AuthenticationVerificationFailed });
315
- }
316
- // derive key and encrypt vault key
317
- const encKey = deriveKeyFromAuthenticationResponse(authenticationResponse, { credential, keyDerivation });
318
- const { ciphertext, iv } = encryptWithKey(vaultKey, encKey);
319
- // persist passkey record
320
- this.update((state) => {
321
- state.passkeyRecord = {
322
- credential: {
323
- ...credential,
324
- counter: Math.max(newCounter, credential.counter),
325
- },
326
- encryptedVaultKey: { ciphertext, iv },
327
- keyDerivation,
328
- };
377
+ const credential = await this.#verifyRegistrationResponse(registrationResponse, registrationCeremony);
378
+ const { newCounter } = await this.#verifyAuthenticationResponse(params.authenticationResponse, credential);
379
+ const keyDerivation = this.#getKeyDerivation(params.authenticationResponse, registrationCeremony);
380
+ const passkeyRecord = this.#createPasskeyRecord({
381
+ vaultKey,
382
+ authenticationResponse: params.authenticationResponse,
383
+ credential,
384
+ newCounter,
385
+ keyDerivation,
329
386
  });
387
+ this.#savePasskeyRecord(passkeyRecord);
330
388
  }
331
389
  finally {
332
390
  // delete registration ceremony
333
391
  this.#ceremonyManager.deleteRegistrationCeremony(challenge);
334
392
  }
335
393
  }
394
+ #getRegistrationCeremony(registrationResponse) {
395
+ const challenge = this.#getChallengeFromClientData(registrationResponse.response.clientDataJSON);
396
+ return {
397
+ challenge,
398
+ ceremony: this.#ceremonyManager.getRegistrationCeremony(challenge),
399
+ };
400
+ }
401
+ async #verifyRegistrationResponse(registrationResponse, registrationCeremony) {
402
+ const { verified, registrationInfo } = await verifyRegistrationResponse({
403
+ response: registrationResponse,
404
+ expectedChallenge: registrationCeremony.challenge,
405
+ expectedOrigin: this.#expectedOrigin,
406
+ expectedRPIDs: this.#expectedRPIDs,
407
+ requireUserVerification: true,
408
+ }).catch((error) => {
409
+ log('Error verifying passkey registration response', error);
410
+ throw new PasskeyControllerError(PasskeyControllerErrorMessage.RegistrationVerificationFailed, {
411
+ code: PasskeyControllerErrorCode.RegistrationVerificationFailed,
412
+ cause: error instanceof Error ? error : new Error(String(error)),
413
+ });
414
+ });
415
+ if (!verified || !registrationInfo) {
416
+ log('Passkey registration verification returned unverified or missing registration info');
417
+ throw new PasskeyControllerError(PasskeyControllerErrorMessage.RegistrationVerificationFailed, { code: PasskeyControllerErrorCode.RegistrationVerificationFailed });
418
+ }
419
+ const credential = {
420
+ id: registrationInfo.credentialId,
421
+ publicKey: bytesToBase64URL(registrationInfo.publicKey),
422
+ counter: registrationInfo.counter,
423
+ transports: registrationInfo.transports,
424
+ aaguid: registrationInfo.aaguid,
425
+ };
426
+ if (registrationResponse.id !== credential.id ||
427
+ registrationResponse.rawId !== credential.id) {
428
+ throw new PasskeyControllerError(PasskeyControllerErrorMessage.RegistrationVerificationFailed, { code: PasskeyControllerErrorCode.RegistrationVerificationFailed });
429
+ }
430
+ return credential;
431
+ }
432
+ #getKeyDerivation(authenticationResponse, registrationCeremony, options) {
433
+ const prfFirst = authenticationResponse.clientExtensionResults?.prf?.results?.first;
434
+ const authHasPrfOutput = typeof prfFirst === 'string' && prfFirst.length > 0;
435
+ if (options?.requirePrf) {
436
+ if (!authHasPrfOutput || !registrationCeremony.prfSalt) {
437
+ throw new PasskeyControllerError(PasskeyControllerErrorMessage.PrfRequired, { code: PasskeyControllerErrorCode.PrfRequired });
438
+ }
439
+ return { method: 'prf', prfSalt: registrationCeremony.prfSalt };
440
+ }
441
+ if (authHasPrfOutput && registrationCeremony.prfSalt) {
442
+ return { method: 'prf', prfSalt: registrationCeremony.prfSalt };
443
+ }
444
+ if (authenticationResponse.response.userHandle !==
445
+ registrationCeremony.userHandle) {
446
+ log('Post-registration assertion userHandle does not match registration ceremony');
447
+ throw new PasskeyControllerError(PasskeyControllerErrorMessage.AuthenticationVerificationFailed, { code: PasskeyControllerErrorCode.AuthenticationVerificationFailed });
448
+ }
449
+ return { method: 'userHandle' };
450
+ }
451
+ #createPasskeyRecord({ vaultKey, authenticationResponse, credential, newCounter, keyDerivation, }) {
452
+ const encKey = deriveKeyFromAuthenticationResponse(authenticationResponse, {
453
+ credential,
454
+ keyDerivation,
455
+ });
456
+ const { ciphertext, iv } = encryptWithKey(vaultKey, encKey);
457
+ return {
458
+ credential: {
459
+ ...credential,
460
+ counter: Math.max(newCounter, credential.counter),
461
+ },
462
+ encryptedVaultKey: { ciphertext, iv },
463
+ keyDerivation,
464
+ };
465
+ }
466
+ #savePasskeyRecord(passkeyRecord, expectedSourceRecord) {
467
+ this.update((state) => {
468
+ if (expectedSourceRecord &&
469
+ (state.passkeyRecord?.credential.id !==
470
+ expectedSourceRecord.credential.id ||
471
+ state.passkeyRecord?.keyDerivation.method !== 'userHandle')) {
472
+ throw new PasskeyControllerError(PasskeyControllerErrorMessage.ReplacementSourceChanged, { code: PasskeyControllerErrorCode.ReplacementSourceChanged });
473
+ }
474
+ state.passkeyRecord = passkeyRecord;
475
+ });
476
+ }
336
477
  /**
337
478
  * Verifies an authentication assertion and returns the decrypted vault key.
338
479
  *
@@ -604,6 +745,13 @@ export class PasskeyController extends BaseController {
604
745
  throw new PasskeyControllerError(PasskeyControllerErrorMessage.NoAuthenticationCeremony, { code: PasskeyControllerErrorCode.NoAuthenticationCeremony });
605
746
  }
606
747
  try {
748
+ if (authenticationResponse.id !== credential.id ||
749
+ authenticationResponse.rawId !== credential.id) {
750
+ log('Passkey authentication response credential ID does not match the expected credential');
751
+ throw new PasskeyControllerError(PasskeyControllerErrorMessage.AuthenticationVerificationFailed, {
752
+ code: PasskeyControllerErrorCode.AuthenticationVerificationFailed,
753
+ });
754
+ }
607
755
  // verify authentication response
608
756
  const result = await verifyAuthenticationResponse({
609
757
  response: authenticationResponse,
@@ -1 +1 @@
1
- {"version":3,"file":"PasskeyController.js","sourceRoot":"","sources":["../src/PasskeyController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StateMetadata } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport { areUint8ArraysEqual, stringToBytes } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\n\nimport { WEBAUTHN_TIMEOUT_MS, CeremonyManager } from './ceremony-manager.js';\nimport {\n controllerName,\n PasskeyControllerErrorCode,\n PasskeyControllerErrorMessage,\n} from './constants.js';\nimport { PasskeyControllerError } from './errors.js';\nimport { deriveKeyFromAuthenticationResponse } from './key-derivation.js';\nimport { createModuleLogger, projectLogger } from './logger.js';\nimport type {\n AuthenticatorTransportFuture,\n PasskeyControllerMessenger,\n PasskeyControllerOptions,\n PasskeyControllerState,\n PasskeyCredentialInfo,\n PasskeyKeyDerivation,\n PasskeyRecord,\n PrfClientExtensionResults,\n} from './types.js';\nimport {\n decryptWithKey,\n encryptWithKey,\n randomBytesToBase64URL,\n} from './utils/crypto.js';\nimport { base64URLToBytes, bytesToBase64URL } from './utils/encoding.js';\nimport { COSEALG } from './webauthn/constants.js';\nimport { decodeClientDataJSON } from './webauthn/decode-client-data-json.js';\nimport type {\n PasskeyAuthenticationOptions,\n PasskeyAuthenticationResponse,\n PasskeyRegistrationOptions,\n PasskeyRegistrationResponse,\n} from './webauthn/types.js';\nimport { verifyAuthenticationResponse } from './webauthn/verify-authentication-response.js';\nimport { verifyRegistrationResponse } from './webauthn/verify-registration-response.js';\n\nexport type {\n PasskeyControllerActions,\n PasskeyControllerAllowedActions,\n PasskeyControllerEvents,\n PasskeyControllerGetStateAction,\n PasskeyControllerMessenger,\n PasskeyControllerOptions,\n PasskeyControllerState,\n PasskeyControllerStateChangedEvent,\n} from './types.js';\n\n/**\n * Returns the default (empty) state for {@link PasskeyController}.\n *\n * @returns A fresh state object with no enrolled passkey.\n */\nexport function getDefaultPasskeyControllerState(): PasskeyControllerState {\n return { passkeyRecord: null };\n}\n\nconst passkeyControllerMetadata = {\n passkeyRecord: {\n persist: true,\n includeInDebugSnapshot: false,\n includeInStateLogs: false,\n usedInUi: true,\n },\n} satisfies StateMetadata<PasskeyControllerState>;\n\nconst log = createModuleLogger(projectLogger, controllerName);\n\n/**\n * Selectors for {@link PasskeyControllerState}.\n *\n * Use these instead of dedicated getter methods on the controller, so that\n * derived values can be consumed from Redux selectors and other places that\n * only have access to a state object.\n */\nexport const passkeyControllerSelectors = {\n selectIsPasskeyEnrolled: (state: PasskeyControllerState): boolean =>\n state.passkeyRecord !== null,\n};\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'isPasskeyEnrolled',\n 'generateRegistrationOptions',\n 'generatePostRegistrationAuthenticationOptions',\n 'generateAuthenticationOptions',\n 'protectVaultKeyWithPasskey',\n 'retrieveVaultKeyWithPasskey',\n 'unlockWithPasskey',\n 'verifyPasskeyAuthentication',\n 'renewVaultKeyProtection',\n 'changePasswordWithPasskeyVerification',\n 'exportSeedPhraseWithPasskey',\n 'exportAccountsWithPasskey',\n 'removePasskeyWithPasskeyVerification',\n 'removePasskeyWithPasswordVerification',\n 'clearState',\n 'destroy',\n] as const;\n\n/**\n * Controller that enrolls a WebAuthn passkey and uses it to protect and unlock\n * the vault encryption key.\n */\nexport class PasskeyController extends BaseController<\n typeof controllerName,\n PasskeyControllerState,\n PasskeyControllerMessenger\n> {\n readonly #ceremonyManager = new CeremonyManager();\n\n readonly #expectedRPIDs: string[];\n\n readonly #rpId: string | undefined;\n\n readonly #rpName: string;\n\n readonly #expectedOrigin: string | string[];\n\n readonly #userName: string;\n\n readonly #userDisplayName: string;\n\n readonly #getIsOnboardingCompleted: () => boolean;\n\n readonly #operationMutex = new Mutex();\n\n /**\n * Creates a passkey controller with WebAuthn relying-party settings.\n *\n * @param options - Constructor options.\n * @param options.messenger - The messenger to use for communication.\n * @param options.state - The initial state of the controller.\n * @param options.rpId - The relying party ID to use for the passkey.\n * @param options.expectedRPID - The expected relying party ID to use for the passkey.\n * @param options.rpName - The relying party name to use for the passkey.\n * @param options.expectedOrigin - The expected origin to use for the passkey.\n * @param options.userName - The user name to use for the passkey.\n * @param options.userDisplayName - The user display name to use for the passkey.\n * @param options.getIsOnboardingCompleted - The callback to use to check if onboarding is complete.\n */\n constructor({\n messenger,\n state = {},\n rpId,\n expectedRPID,\n rpName,\n expectedOrigin,\n userName,\n userDisplayName,\n getIsOnboardingCompleted,\n }: PasskeyControllerOptions) {\n super({\n messenger,\n metadata: passkeyControllerMetadata,\n name: controllerName,\n state: { ...getDefaultPasskeyControllerState(), ...state },\n });\n\n const expectedRPIDs = Array.isArray(expectedRPID)\n ? expectedRPID\n : [expectedRPID];\n this.#expectedRPIDs = [...expectedRPIDs];\n this.#rpId = rpId;\n this.#rpName = rpName;\n this.#expectedOrigin = expectedOrigin;\n this.#userName = userName ?? rpName;\n this.#userDisplayName = userDisplayName ?? rpName;\n this.#getIsOnboardingCompleted = getIsOnboardingCompleted;\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Whether a passkey is enrolled and vault key material is stored.\n *\n * @returns `true` if enrolled, otherwise `false`.\n */\n isPasskeyEnrolled(): boolean {\n return passkeyControllerSelectors.selectIsPasskeyEnrolled(this.state);\n }\n\n /**\n * Builds WebAuthn credential creation options for passkey enrollment.\n *\n * @param creationOptionsConfig - Optional creation behavior.\n * @param creationOptionsConfig.prfAvailable - Request the PRF extension unless `false`. Defaults to `true`.\n * @returns Public key credential creation options for `navigator.credentials.create()`.\n */\n generateRegistrationOptions(creationOptionsConfig?: {\n prfAvailable?: boolean;\n }): PasskeyRegistrationOptions {\n if (this.isPasskeyEnrolled()) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.AlreadyEnrolled,\n { code: PasskeyControllerErrorCode.AlreadyEnrolled },\n );\n }\n\n const includePrf = creationOptionsConfig?.prfAvailable !== false;\n const prfSalt = includePrf ? randomBytesToBase64URL(32) : undefined;\n const userHandle = randomBytesToBase64URL(64);\n const challenge = randomBytesToBase64URL(32);\n\n const extensions: Record<string, unknown> = {};\n if (prfSalt) {\n extensions.prf = { eval: { first: prfSalt } };\n }\n\n const options: PasskeyRegistrationOptions = {\n rp: {\n name: this.#rpName,\n id: this.#rpId,\n },\n user: {\n id: userHandle,\n name: this.#userName,\n displayName: this.#userDisplayName,\n },\n challenge,\n pubKeyCredParams: [\n { alg: COSEALG.EdDSA, type: 'public-key' },\n { alg: COSEALG.ES256, type: 'public-key' },\n { alg: COSEALG.RS256, type: 'public-key' },\n ],\n timeout: WEBAUTHN_TIMEOUT_MS,\n authenticatorSelection: {\n userVerification: 'required',\n authenticatorAttachment: 'platform',\n residentKey: 'preferred',\n },\n hints: ['client-device', 'hybrid'],\n attestation: 'none',\n ...(Object.keys(extensions).length > 0 ? { extensions } : {}),\n };\n\n this.#ceremonyManager.saveRegistrationCeremony(challenge, {\n userHandle,\n prfSalt,\n challenge,\n createdAt: Date.now(),\n });\n\n return options;\n }\n\n /**\n * Builds WebAuthn credential request options for the post-registration\n * authentication step (between `create` and {@link protectVaultKeyWithPasskey}).\n *\n * @param params - Input for the pending registration ceremony.\n * @param params.registrationResponse - Result of `navigator.credentials.create()`.\n * @returns Public key credential request options for `navigator.credentials.get()`.\n */\n generatePostRegistrationAuthenticationOptions(params: {\n registrationResponse: PasskeyRegistrationResponse;\n }): PasskeyAuthenticationOptions {\n // get registration ceremony\n const { registrationResponse } = params;\n const regChallenge = this.#getChallengeFromClientData(\n registrationResponse.response.clientDataJSON,\n );\n const registrationCeremony =\n this.#ceremonyManager.getRegistrationCeremony(regChallenge);\n if (!registrationCeremony) {\n log('No active passkey registration ceremony for challenge');\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.NoRegistrationCeremony,\n { code: PasskeyControllerErrorCode.NoRegistrationCeremony },\n );\n }\n\n // build auth options\n const challenge = randomBytesToBase64URL(32);\n const extensions: Record<string, unknown> = {};\n if (registrationCeremony.prfSalt) {\n extensions.prf = { eval: { first: registrationCeremony.prfSalt } };\n }\n const options: PasskeyAuthenticationOptions = {\n challenge,\n rpId: this.#rpId,\n allowCredentials: [\n {\n id: registrationResponse.id,\n type: 'public-key',\n transports: registrationResponse.response.transports as\n | AuthenticatorTransportFuture[]\n | undefined,\n },\n ],\n userVerification: 'required',\n hints: ['client-device', 'hybrid'],\n timeout: WEBAUTHN_TIMEOUT_MS,\n extensions,\n };\n\n // save auth ceremony\n this.#ceremonyManager.saveAuthenticationCeremony(challenge, {\n challenge,\n createdAt: Date.now(),\n });\n\n return options;\n }\n\n /**\n * Builds WebAuthn credential request options for the enrolled passkey.\n *\n * @returns Public key credential request options for `navigator.credentials.get()`.\n */\n generateAuthenticationOptions(): PasskeyAuthenticationOptions {\n const record = this.#requireEnrolled();\n\n const challenge = randomBytesToBase64URL(32);\n\n const extensions: Record<string, unknown> = {};\n if (record.keyDerivation.method === 'prf') {\n extensions.prf = { eval: { first: record.keyDerivation.prfSalt } };\n }\n\n const options: PasskeyAuthenticationOptions = {\n challenge,\n rpId: this.#rpId,\n allowCredentials: [\n {\n id: record.credential.id,\n type: 'public-key',\n transports: record.credential.transports,\n },\n ],\n userVerification: 'required',\n hints: ['client-device', 'hybrid'],\n timeout: WEBAUTHN_TIMEOUT_MS,\n extensions,\n };\n\n this.#ceremonyManager.saveAuthenticationCeremony(challenge, {\n challenge,\n createdAt: Date.now(),\n });\n\n return options;\n }\n\n /**\n * Verifies registration and post-registration authentication, then stores the\n * vault key encrypted under the new passkey.\n *\n * Fetches the current vault encryption key from KeyringController before wrapping.\n * When onboarding is complete, requires `password` for step-up verification first.\n *\n * @param params - Enrollment completion inputs.\n * @param params.registrationResponse - Result of `navigator.credentials.create()`.\n * @param params.authenticationResponse - Result of `navigator.credentials.get()` after {@link generatePostRegistrationAuthenticationOptions}.\n * @param params.password - Wallet password when onboarding is complete (step-up).\n * @returns Resolves when enrollment completes.\n */\n async protectVaultKeyWithPasskey(params: {\n registrationResponse: PasskeyRegistrationResponse;\n authenticationResponse: PasskeyAuthenticationResponse;\n password?: string;\n }): Promise<void> {\n return this.#withOperationLock(() =>\n this.#protectVaultKeyWithPasskey(params),\n );\n }\n\n async #protectVaultKeyWithPasskey(params: {\n registrationResponse: PasskeyRegistrationResponse;\n authenticationResponse: PasskeyAuthenticationResponse;\n password?: string;\n }): Promise<void> {\n if (this.isPasskeyEnrolled()) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.AlreadyEnrolled,\n { code: PasskeyControllerErrorCode.AlreadyEnrolled },\n );\n }\n\n await this.#assertEnrollmentAllowed(params.password);\n const vaultKey = await this.messenger.call(\n 'KeyringController:exportEncryptionKey',\n );\n\n const { registrationResponse, authenticationResponse } = params;\n\n // get registration ceremony\n const challenge = this.#getChallengeFromClientData(\n registrationResponse.response.clientDataJSON,\n );\n const registrationCeremony =\n this.#ceremonyManager.getRegistrationCeremony(challenge);\n if (!registrationCeremony) {\n log('No active passkey registration ceremony for challenge');\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.NoRegistrationCeremony,\n { code: PasskeyControllerErrorCode.NoRegistrationCeremony },\n );\n }\n\n try {\n // verify registration response\n const { verified, registrationInfo } = await verifyRegistrationResponse({\n response: registrationResponse,\n expectedChallenge: registrationCeremony.challenge,\n expectedOrigin: this.#expectedOrigin,\n expectedRPIDs: this.#expectedRPIDs,\n requireUserVerification: true,\n }).catch((error) => {\n log('Error verifying passkey registration response', error);\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.RegistrationVerificationFailed,\n {\n code: PasskeyControllerErrorCode.RegistrationVerificationFailed,\n cause: error instanceof Error ? error : new Error(String(error)),\n },\n );\n });\n if (!verified || !registrationInfo) {\n log(\n 'Passkey registration verification returned unverified or missing registration info',\n );\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.RegistrationVerificationFailed,\n { code: PasskeyControllerErrorCode.RegistrationVerificationFailed },\n );\n }\n\n // verify authentication response\n const credential = {\n id: registrationInfo.credentialId,\n publicKey: bytesToBase64URL(registrationInfo.publicKey),\n counter: registrationInfo.counter,\n transports: registrationInfo.transports,\n aaguid: registrationInfo.aaguid,\n };\n const { newCounter } = await this.#verifyAuthenticationResponse(\n authenticationResponse,\n credential,\n );\n\n // determine key derivation method\n const prfFirst = (\n authenticationResponse.clientExtensionResults as PrfClientExtensionResults\n )?.prf?.results?.first;\n const authHasPrfOutput =\n typeof prfFirst === 'string' && prfFirst.length > 0;\n const keyDerivation: PasskeyKeyDerivation =\n authHasPrfOutput && registrationCeremony.prfSalt\n ? { method: 'prf', prfSalt: registrationCeremony.prfSalt }\n : { method: 'userHandle' };\n\n if (\n keyDerivation.method === 'userHandle' &&\n authenticationResponse.response.userHandle !==\n registrationCeremony.userHandle\n ) {\n log(\n 'Post-registration assertion userHandle does not match registration ceremony',\n );\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.AuthenticationVerificationFailed,\n { code: PasskeyControllerErrorCode.AuthenticationVerificationFailed },\n );\n }\n\n // derive key and encrypt vault key\n const encKey = deriveKeyFromAuthenticationResponse(\n authenticationResponse,\n { credential, keyDerivation },\n );\n const { ciphertext, iv } = encryptWithKey(vaultKey, encKey);\n\n // persist passkey record\n this.update((state) => {\n state.passkeyRecord = {\n credential: {\n ...credential,\n counter: Math.max(newCounter, credential.counter),\n },\n encryptedVaultKey: { ciphertext, iv },\n keyDerivation,\n };\n });\n } finally {\n // delete registration ceremony\n this.#ceremonyManager.deleteRegistrationCeremony(challenge);\n }\n }\n\n /**\n * Verifies an authentication assertion and returns the decrypted vault key.\n *\n * Prefer orchestrated methods ({@link unlockWithPasskey},\n * {@link exportSeedPhraseWithPasskey}, {@link exportAccountsWithPasskey}) for product\n * flows instead of calling KeyringController with the returned key manually.\n *\n * @param authenticationResponse - Result of `navigator.credentials.get()`.\n * @returns The plaintext vault encryption key.\n */\n async retrieveVaultKeyWithPasskey(\n authenticationResponse: PasskeyAuthenticationResponse,\n ): Promise<string> {\n return this.#withOperationLock(() =>\n this.#retrieveVaultKeyWithPasskey(authenticationResponse),\n );\n }\n\n async #retrieveVaultKeyWithPasskey(\n authenticationResponse: PasskeyAuthenticationResponse,\n ): Promise<string> {\n const passkeyRecord = this.#requireEnrolled();\n\n // verify authentication response and update counter\n const { newCounter } = await this.#verifyAuthenticationResponse(\n authenticationResponse,\n passkeyRecord.credential,\n );\n this.update((state) => {\n if (!state.passkeyRecord) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.NotEnrolled,\n { code: PasskeyControllerErrorCode.NotEnrolled },\n );\n }\n state.passkeyRecord.credential.counter = Math.max(\n newCounter,\n state.passkeyRecord.credential.counter,\n );\n });\n\n // derive key\n const encKey = deriveKeyFromAuthenticationResponse(\n authenticationResponse,\n passkeyRecord,\n );\n\n // decrypt vault key\n try {\n const vaultKey = decryptWithKey(\n passkeyRecord.encryptedVaultKey.ciphertext,\n passkeyRecord.encryptedVaultKey.iv,\n encKey,\n );\n return vaultKey;\n } catch (cause) {\n log(\n 'Error decrypting vault key with passkey',\n cause instanceof Error ? cause : new Error(String(cause)),\n );\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.VaultKeyDecryptionFailed,\n {\n code: PasskeyControllerErrorCode.VaultKeyDecryptionFailed,\n cause: cause instanceof Error ? cause : new Error(String(cause)),\n },\n );\n }\n }\n\n /**\n * Unlocks the keyring using a passkey authentication assertion.\n *\n * @param authenticationResponse - Result of `navigator.credentials.get()`.\n * @returns Resolves when the keyring is unlocked.\n */\n async unlockWithPasskey(\n authenticationResponse: PasskeyAuthenticationResponse,\n ): Promise<void> {\n return this.#withOperationLock(async () => {\n const vaultKey = await this.#retrieveVaultKeyWithPasskey(\n authenticationResponse,\n );\n await this.messenger.call(\n 'KeyringController:submitEncryptionKey',\n vaultKey,\n );\n });\n }\n\n /**\n * Exports the seed phrase after passkey step-up authentication.\n *\n * @param authenticationResponse - Result of `navigator.credentials.get()`.\n * @param keyringId - Optional keyring id; defaults to the primary HD keyring.\n * @returns Raw seed phrase bytes from KeyringController.\n */\n async exportSeedPhraseWithPasskey(\n authenticationResponse: PasskeyAuthenticationResponse,\n keyringId?: string,\n ): Promise<Uint8Array> {\n return this.#withOperationLock(async () => {\n const vaultKey = await this.#retrieveVaultKeyWithPasskey(\n authenticationResponse,\n );\n return await this.messenger.call(\n 'KeyringController:exportSeedPhrase',\n { encryptionKey: vaultKey },\n keyringId,\n );\n });\n }\n\n /**\n * Exports private keys for the given addresses after passkey step-up authentication.\n *\n * @param authenticationResponse - Result of `navigator.credentials.get()`.\n * @param addresses - Account addresses to export.\n * @returns Private keys in the same order as `addresses`.\n */\n async exportAccountsWithPasskey(\n authenticationResponse: PasskeyAuthenticationResponse,\n addresses: string[],\n ): Promise<string[]> {\n return this.#withOperationLock(async () => {\n const vaultKey = await this.#retrieveVaultKeyWithPasskey(\n authenticationResponse,\n );\n\n const privateKeys: string[] = [];\n for (const address of addresses) {\n privateKeys.push(\n await this.messenger.call(\n 'KeyringController:exportAccount',\n { encryptionKey: vaultKey },\n address,\n ),\n );\n }\n return privateKeys;\n });\n }\n\n /**\n * Checks whether the given authentication assertion is valid for the enrolled passkey.\n *\n * On failure, returns `false` for {@link PasskeyControllerError} with a `code`;\n * other errors propagate.\n *\n * @param authenticationResponse - Result of `navigator.credentials.get()`.\n * @returns `true` if verification succeeds, otherwise `false`.\n */\n async verifyPasskeyAuthentication(\n authenticationResponse: PasskeyAuthenticationResponse,\n ): Promise<boolean> {\n return this.#withOperationLock(() =>\n this.#verifyPasskeyAuthentication(authenticationResponse),\n );\n }\n\n async #verifyPasskeyAuthentication(\n authenticationResponse: PasskeyAuthenticationResponse,\n ): Promise<boolean> {\n try {\n await this.#retrieveVaultKeyWithPasskey(authenticationResponse);\n return true;\n } catch (error: unknown) {\n if (error instanceof PasskeyControllerError && error.code !== undefined) {\n return false;\n }\n throw error;\n }\n }\n\n /**\n * Re-wraps the vault key after rotation. Updates persisted `encryptedVaultKey` on success.\n *\n * Does not verify WebAuthn or ceremony state—call only after your layer has authenticated\n * the user (passkey `get()` + verified assertion, or verified password). On passkey paths,\n * pass the same `authenticationResponse` you just verified (e.g. from\n * {@link retrieveVaultKeyWithPasskey} / {@link verifyPasskeyAuthentication}).\n *\n * For password change with passkey step-up, prefer\n * {@link changePasswordWithPasskeyVerification}, which orchestrates keyring export,\n * `changePassword`, and re-wrap in one call.\n *\n * @param params - Re-wrap inputs.\n * @param params.authenticationResponse - Used to derive the wrapping key.\n * @param params.oldVaultKey - Expected current vault key.\n * @param params.newVaultKey - New vault key to encrypt under the passkey.\n * @returns Resolves when the passkey record is updated.\n */\n async renewVaultKeyProtection(params: {\n authenticationResponse: PasskeyAuthenticationResponse;\n oldVaultKey: string;\n newVaultKey: string;\n }): Promise<void> {\n return this.#withOperationLock(() => this.#renewVaultKeyProtection(params));\n }\n\n async #renewVaultKeyProtection(params: {\n authenticationResponse: PasskeyAuthenticationResponse;\n oldVaultKey: string;\n newVaultKey: string;\n }): Promise<void> {\n const { authenticationResponse } = params;\n const passkeyRecord = this.#requireEnrolled();\n\n // derive key\n const encKey = deriveKeyFromAuthenticationResponse(\n authenticationResponse,\n passkeyRecord,\n );\n\n // decrypt vault key\n let decryptedVaultKey: string;\n try {\n decryptedVaultKey = decryptWithKey(\n passkeyRecord.encryptedVaultKey.ciphertext,\n passkeyRecord.encryptedVaultKey.iv,\n encKey,\n );\n } catch (error) {\n log(\n 'Error decrypting vault key during passkey vault key renewal',\n error instanceof Error ? error : new Error(String(error)),\n );\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.VaultKeyDecryptionFailed,\n {\n code: PasskeyControllerErrorCode.VaultKeyDecryptionFailed,\n cause: error instanceof Error ? error : new Error(String(error)),\n },\n );\n }\n\n // check if vault key matches\n const { oldVaultKey, newVaultKey } = params;\n if (\n !areUint8ArraysEqual(\n stringToBytes(decryptedVaultKey),\n stringToBytes(oldVaultKey),\n )\n ) {\n log(\n 'Passkey renewal rejected: decrypted vault key does not match oldVaultKey',\n );\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.VaultKeyMismatch,\n { code: PasskeyControllerErrorCode.VaultKeyMismatch },\n );\n }\n\n // encrypt new vault key\n const { ciphertext, iv } = encryptWithKey(newVaultKey, encKey);\n\n // persist passkey record (mutate current state only for vault key material)\n this.update((state) => {\n if (!state.passkeyRecord) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.NotEnrolled,\n {\n code: PasskeyControllerErrorCode.NotEnrolled,\n },\n );\n }\n state.passkeyRecord.encryptedVaultKey = { ciphertext, iv };\n });\n }\n\n /**\n * Changes the wallet password after passkey step-up authentication.\n *\n * When `renewVaultKeyProtection` is `true` (default), re-wraps the vault key under the\n * passkey after rotation. When `false`, removes the passkey instead.\n *\n * @param params - Change-password inputs.\n * @param params.newPassword - New wallet password.\n * @param params.authenticationResponse - Result of `navigator.credentials.get()`.\n * @param params.options - Optional flow controls.\n * @param params.options.renewVaultKeyProtection - Re-wrap vault key after password change.\n * @returns Resolves when the password change completes.\n */\n async changePasswordWithPasskeyVerification(params: {\n newPassword: string;\n authenticationResponse: PasskeyAuthenticationResponse;\n options?: { renewVaultKeyProtection?: boolean };\n }): Promise<void> {\n return this.#withOperationLock(() =>\n this.#changePasswordWithPasskeyVerification(params),\n );\n }\n\n async #changePasswordWithPasskeyVerification(params: {\n newPassword: string;\n authenticationResponse: PasskeyAuthenticationResponse;\n options?: { renewVaultKeyProtection?: boolean };\n }): Promise<void> {\n this.#requireEnrolled();\n\n const verified = await this.#verifyPasskeyAuthentication(\n params.authenticationResponse,\n );\n if (!verified) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.AuthenticationVerificationFailed,\n { code: PasskeyControllerErrorCode.AuthenticationVerificationFailed },\n );\n }\n\n const renewVaultKeyProtection =\n params.options?.renewVaultKeyProtection ?? true;\n\n if (!renewVaultKeyProtection) {\n await this.messenger.call(\n 'KeyringController:changePassword',\n params.newPassword,\n );\n this.#removePasskey();\n return;\n }\n\n const vaultKeyBefore = await this.messenger.call(\n 'KeyringController:exportEncryptionKey',\n );\n await this.messenger.call(\n 'KeyringController:changePassword',\n params.newPassword,\n );\n\n try {\n const vaultKeyAfter = await this.messenger.call(\n 'KeyringController:exportEncryptionKey',\n );\n await this.#renewVaultKeyProtection({\n authenticationResponse: params.authenticationResponse,\n oldVaultKey: vaultKeyBefore,\n newVaultKey: vaultKeyAfter,\n });\n } catch (error) {\n this.#removePasskey();\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.VaultKeyRenewalFailed,\n {\n code: PasskeyControllerErrorCode.VaultKeyRenewalFailed,\n cause: error instanceof Error ? error : new Error(String(error)),\n },\n );\n }\n }\n\n /**\n * Removes the enrolled passkey after verifying a passkey authentication assertion.\n *\n * @param authenticationResponse - Result of `navigator.credentials.get()`.\n * @returns Resolves when the passkey is removed.\n */\n async removePasskeyWithPasskeyVerification(\n authenticationResponse: PasskeyAuthenticationResponse,\n ): Promise<void> {\n return this.#withOperationLock(() =>\n this.#removePasskeyWithPasskeyVerification(authenticationResponse),\n );\n }\n\n async #removePasskeyWithPasskeyVerification(\n authenticationResponse: PasskeyAuthenticationResponse,\n ): Promise<void> {\n this.#requireEnrolled();\n\n const verified = await this.#verifyPasskeyAuthentication(\n authenticationResponse,\n );\n if (!verified) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.AuthenticationVerificationFailed,\n { code: PasskeyControllerErrorCode.AuthenticationVerificationFailed },\n );\n }\n\n this.#removePasskey();\n }\n\n /**\n * Removes the enrolled passkey after verifying the wallet password.\n *\n * @param password - Wallet password for step-up verification.\n * @returns Resolves when the passkey is removed.\n */\n async removePasskeyWithPasswordVerification(password: string): Promise<void> {\n return this.#withOperationLock(() =>\n this.#removePasskeyWithPasswordVerification(password),\n );\n }\n\n async #removePasskeyWithPasswordVerification(\n password: string,\n ): Promise<void> {\n this.#requireEnrolled();\n await this.messenger.call('KeyringController:verifyPassword', password);\n this.#removePasskey();\n }\n\n /**\n * Resets state and clears in-flight registration/authentication ceremonies.\n *\n * For user-facing passkey removal with step-up, use\n * {@link removePasskeyWithPasskeyVerification} or\n * {@link removePasskeyWithPasswordVerification}.\n */\n clearState(): void {\n this.#removePasskey();\n }\n\n /**\n * Releases all in-flight ceremony state and tears down the messenger.\n */\n destroy(): void {\n this.#ceremonyManager.clear();\n super.destroy();\n }\n\n /**\n * Validates a WebAuthn authentication response against stored credential data.\n *\n * @param authenticationResponse - Parsed authentication response from the client.\n * @param credential - Credential identifiers and public key material for verification.\n * @returns Updated authenticator signature counter.\n */\n async #verifyAuthenticationResponse(\n authenticationResponse: PasskeyAuthenticationResponse,\n credential: PasskeyCredentialInfo,\n ): Promise<{ newCounter: number }> {\n // get challenge\n const challenge = this.#getChallengeFromClientData(\n authenticationResponse.response.clientDataJSON,\n );\n\n // get authentication ceremony\n const authenticationCeremony =\n this.#ceremonyManager.getAuthenticationCeremony(challenge);\n if (!authenticationCeremony) {\n log('No active passkey authentication ceremony for challenge');\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.NoAuthenticationCeremony,\n { code: PasskeyControllerErrorCode.NoAuthenticationCeremony },\n );\n }\n\n try {\n // verify authentication response\n const result = await verifyAuthenticationResponse({\n response: authenticationResponse,\n expectedChallenge: authenticationCeremony.challenge,\n expectedOrigin: this.#expectedOrigin,\n expectedRPIDs: this.#expectedRPIDs,\n credential: {\n id: credential.id,\n publicKey: base64URLToBytes(credential.publicKey),\n counter: credential.counter,\n transports: credential.transports,\n },\n requireUserVerification: true,\n }).catch((error) => {\n log(\n 'Error verifying passkey authentication response',\n error instanceof Error ? error : new Error(String(error)),\n );\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.AuthenticationVerificationFailed,\n {\n code: PasskeyControllerErrorCode.AuthenticationVerificationFailed,\n cause: error instanceof Error ? error : new Error(String(error)),\n },\n );\n });\n if (!result.verified) {\n log('Passkey authentication verification returned unverified');\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.AuthenticationVerificationFailed,\n {\n code: PasskeyControllerErrorCode.AuthenticationVerificationFailed,\n },\n );\n }\n\n return { newCounter: result.authenticationInfo.newCounter };\n } finally {\n // delete authentication ceremony\n this.#ceremonyManager.deleteAuthenticationCeremony(challenge);\n }\n }\n\n /**\n * Serializes orchestrated passkey operations that mutate state or call KeyringController.\n *\n * @param callback - Operation to run while the mutex is held.\n * @returns The result of the callback.\n */\n async #withOperationLock<Result>(\n callback: () => Promise<Result>,\n ): Promise<Result> {\n return this.#operationMutex.runExclusive(callback);\n }\n\n async #assertEnrollmentAllowed(password?: string): Promise<void> {\n if (!this.#getIsOnboardingCompleted()) {\n return;\n }\n\n if (!password) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.EnrollmentPasswordRequired,\n {\n code: PasskeyControllerErrorCode.EnrollmentPasswordRequired,\n },\n );\n }\n\n await this.messenger.call('KeyringController:verifyPassword', password);\n }\n\n #requireEnrolled(): PasskeyRecord {\n const record = this.state.passkeyRecord;\n if (!record) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.NotEnrolled,\n {\n code: PasskeyControllerErrorCode.NotEnrolled,\n },\n );\n }\n return record;\n }\n\n #getChallengeFromClientData(clientDataJSON: string): string {\n return decodeClientDataJSON(clientDataJSON).challenge;\n }\n\n /**\n * Clears enrolled passkey state and in-flight ceremonies.\n */\n #removePasskey(): void {\n this.update(() => getDefaultPasskeyControllerState());\n this.#ceremonyManager.clear();\n }\n}\n"]}
1
+ {"version":3,"file":"PasskeyController.js","sourceRoot":"","sources":["../src/PasskeyController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StateMetadata } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport { areUint8ArraysEqual, stringToBytes } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\n\nimport { WEBAUTHN_TIMEOUT_MS, CeremonyManager } from './ceremony-manager.js';\nimport {\n controllerName,\n PasskeyControllerErrorCode,\n PasskeyControllerErrorMessage,\n} from './constants.js';\nimport { PasskeyControllerError } from './errors.js';\nimport { deriveKeyFromAuthenticationResponse } from './key-derivation.js';\nimport { createModuleLogger, projectLogger } from './logger.js';\nimport type {\n AuthenticatorTransportFuture,\n PasskeyControllerMessenger,\n PasskeyControllerOptions,\n PasskeyControllerState,\n PasskeyCredentialInfo,\n PasskeyKeyDerivation,\n PasskeyRegistrationCeremony,\n PasskeyRecord,\n PrfClientExtensionResults,\n} from './types.js';\nimport {\n decryptWithKey,\n encryptWithKey,\n randomBytesToBase64URL,\n} from './utils/crypto.js';\nimport { base64URLToBytes, bytesToBase64URL } from './utils/encoding.js';\nimport { COSEALG } from './webauthn/constants.js';\nimport { decodeClientDataJSON } from './webauthn/decode-client-data-json.js';\nimport type {\n PasskeyAuthenticationOptions,\n PasskeyAuthenticationResponse,\n PasskeyRegistrationOptions,\n PasskeyRegistrationResponse,\n} from './webauthn/types.js';\nimport { verifyAuthenticationResponse } from './webauthn/verify-authentication-response.js';\nimport { verifyRegistrationResponse } from './webauthn/verify-registration-response.js';\n\nexport type {\n PasskeyControllerActions,\n PasskeyControllerAllowedActions,\n PasskeyControllerEvents,\n PasskeyControllerGetStateAction,\n PasskeyControllerMessenger,\n PasskeyControllerOptions,\n PasskeyControllerState,\n PasskeyControllerStateChangedEvent,\n} from './types.js';\n\n/**\n * Returns the default (empty) state for {@link PasskeyController}.\n *\n * @returns A fresh state object with no enrolled passkey.\n */\nexport function getDefaultPasskeyControllerState(): PasskeyControllerState {\n return { passkeyRecord: null };\n}\n\nconst passkeyControllerMetadata = {\n passkeyRecord: {\n persist: true,\n includeInDebugSnapshot: false,\n includeInStateLogs: false,\n usedInUi: true,\n },\n} satisfies StateMetadata<PasskeyControllerState>;\n\nconst log = createModuleLogger(projectLogger, controllerName);\n\n/**\n * Selectors for {@link PasskeyControllerState}.\n *\n * Use these instead of dedicated getter methods on the controller, so that\n * derived values can be consumed from Redux selectors and other places that\n * only have access to a state object.\n */\nexport const passkeyControllerSelectors = {\n selectIsPasskeyEnrolled: (state: PasskeyControllerState): boolean =>\n state.passkeyRecord !== null,\n};\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'isPasskeyEnrolled',\n 'generateRegistrationOptions',\n 'generatePasskeyReplacementRegistrationOptions',\n 'completePasskeyReplacement',\n 'cancelPasskeyReplacement',\n 'generatePostRegistrationAuthenticationOptions',\n 'generateAuthenticationOptions',\n 'protectVaultKeyWithPasskey',\n 'retrieveVaultKeyWithPasskey',\n 'unlockWithPasskey',\n 'verifyPasskeyAuthentication',\n 'renewVaultKeyProtection',\n 'changePasswordWithPasskeyVerification',\n 'exportSeedPhraseWithPasskey',\n 'exportAccountsWithPasskey',\n 'removePasskeyWithPasskeyVerification',\n 'removePasskeyWithPasswordVerification',\n 'clearState',\n 'destroy',\n] as const;\n\n/**\n * Controller that enrolls a WebAuthn passkey and uses it to protect and unlock\n * the vault encryption key.\n */\nexport class PasskeyController extends BaseController<\n typeof controllerName,\n PasskeyControllerState,\n PasskeyControllerMessenger\n> {\n readonly #ceremonyManager = new CeremonyManager();\n\n readonly #expectedRPIDs: string[];\n\n readonly #rpId: string | undefined;\n\n readonly #rpName: string;\n\n readonly #expectedOrigin: string | string[];\n\n readonly #userName: string;\n\n readonly #userDisplayName: string;\n\n readonly #getIsOnboardingCompleted: () => boolean;\n\n readonly #operationMutex = new Mutex();\n\n /**\n * Creates a passkey controller with WebAuthn relying-party settings.\n *\n * @param options - Constructor options.\n * @param options.messenger - The messenger to use for communication.\n * @param options.state - The initial state of the controller.\n * @param options.rpId - The relying party ID to use for the passkey.\n * @param options.expectedRPID - The expected relying party ID to use for the passkey.\n * @param options.rpName - The relying party name to use for the passkey.\n * @param options.expectedOrigin - The expected origin to use for the passkey.\n * @param options.userName - The user name to use for the passkey.\n * @param options.userDisplayName - The user display name to use for the passkey.\n * @param options.getIsOnboardingCompleted - The callback to use to check if onboarding is complete.\n */\n constructor({\n messenger,\n state = {},\n rpId,\n expectedRPID,\n rpName,\n expectedOrigin,\n userName,\n userDisplayName,\n getIsOnboardingCompleted,\n }: PasskeyControllerOptions) {\n super({\n messenger,\n metadata: passkeyControllerMetadata,\n name: controllerName,\n state: { ...getDefaultPasskeyControllerState(), ...state },\n });\n\n const expectedRPIDs = Array.isArray(expectedRPID)\n ? expectedRPID\n : [expectedRPID];\n this.#expectedRPIDs = [...expectedRPIDs];\n this.#rpId = rpId;\n this.#rpName = rpName;\n this.#expectedOrigin = expectedOrigin;\n this.#userName = userName ?? rpName;\n this.#userDisplayName = userDisplayName ?? rpName;\n this.#getIsOnboardingCompleted = getIsOnboardingCompleted;\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Whether a passkey is enrolled and vault key material is stored.\n *\n * @returns `true` if enrolled, otherwise `false`.\n */\n isPasskeyEnrolled(): boolean {\n return passkeyControllerSelectors.selectIsPasskeyEnrolled(this.state);\n }\n\n /**\n * Builds WebAuthn credential creation options for passkey enrollment.\n *\n * @param creationOptionsConfig - Optional creation behavior.\n * @param creationOptionsConfig.prfAvailable - Request the PRF extension unless `false`. Defaults to `true`.\n * @returns Public key credential creation options for `navigator.credentials.create()`.\n */\n generateRegistrationOptions(creationOptionsConfig?: {\n prfAvailable?: boolean;\n }): PasskeyRegistrationOptions {\n return this.#generateRegistrationOptions({\n includePrf: creationOptionsConfig?.prfAvailable !== false,\n });\n }\n\n /**\n * Builds WebAuthn credential creation options for replacing a userHandle\n * passkey with a PRF-capable passkey.\n *\n * The existing passkey record is retained while the replacement ceremony is\n * in flight.\n *\n * @returns Public key credential creation options for `navigator.credentials.create()`.\n */\n generatePasskeyReplacementRegistrationOptions(): PasskeyRegistrationOptions {\n const record = this.#requireEnrolled();\n if (record.keyDerivation.method !== 'userHandle') {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.MigrationNotRequired,\n { code: PasskeyControllerErrorCode.MigrationNotRequired },\n );\n }\n\n const excludeCredential = {\n id: record.credential.id,\n type: 'public-key' as const,\n ...(record.credential.transports\n ? { transports: record.credential.transports }\n : {}),\n };\n\n return this.#generateRegistrationOptions({\n excludeCredentials: [excludeCredential],\n includePrf: true,\n isReplacement: true,\n sourceCredentialId: record.credential.id,\n });\n }\n\n #generateRegistrationOptions({\n excludeCredentials,\n includePrf,\n isReplacement = false,\n sourceCredentialId,\n }: {\n excludeCredentials?: PasskeyRegistrationOptions['excludeCredentials'];\n includePrf: boolean;\n isReplacement?: boolean;\n sourceCredentialId?: string;\n }): PasskeyRegistrationOptions {\n if (!isReplacement && this.isPasskeyEnrolled()) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.AlreadyEnrolled,\n { code: PasskeyControllerErrorCode.AlreadyEnrolled },\n );\n }\n\n const prfSalt = includePrf ? randomBytesToBase64URL(32) : undefined;\n const userHandle = randomBytesToBase64URL(64);\n const challenge = randomBytesToBase64URL(32);\n\n const extensions: Record<string, unknown> = {};\n if (prfSalt) {\n extensions.prf = { eval: { first: prfSalt } };\n }\n\n const options: PasskeyRegistrationOptions = {\n rp: {\n name: this.#rpName,\n id: this.#rpId,\n },\n user: {\n id: userHandle,\n name: this.#userName,\n displayName: this.#userDisplayName,\n },\n challenge,\n pubKeyCredParams: [\n { alg: COSEALG.EdDSA, type: 'public-key' },\n { alg: COSEALG.ES256, type: 'public-key' },\n { alg: COSEALG.RS256, type: 'public-key' },\n ],\n timeout: WEBAUTHN_TIMEOUT_MS,\n authenticatorSelection: {\n userVerification: 'required',\n authenticatorAttachment: 'platform',\n residentKey: 'preferred',\n },\n hints: ['client-device', 'hybrid'],\n attestation: 'none',\n ...(excludeCredentials ? { excludeCredentials } : {}),\n ...(Object.keys(extensions).length > 0 ? { extensions } : {}),\n };\n\n this.#ceremonyManager.saveRegistrationCeremony(challenge, {\n userHandle,\n prfSalt,\n challenge,\n createdAt: Date.now(),\n ...(isReplacement ? { isReplacement: true, sourceCredentialId } : {}),\n });\n\n return options;\n }\n\n /**\n * Verifies and completes replacement of an enrolled userHandle passkey with\n * a PRF-capable passkey.\n *\n * The existing passkey record remains active until the replacement\n * registration and post-registration authentication have both been verified\n * and the existing vault key has been wrapped with the new PRF-derived key.\n *\n * @param params - Replacement completion inputs.\n * @param params.registrationResponse - Result of `navigator.credentials.create()`.\n * @param params.authenticationResponse - Result of `navigator.credentials.get()`\n * after {@link generatePostRegistrationAuthenticationOptions}.\n * @returns Resolves when the replacement completes.\n */\n async completePasskeyReplacement(params: {\n registrationResponse: PasskeyRegistrationResponse;\n authenticationResponse: PasskeyAuthenticationResponse;\n }): Promise<void> {\n return this.#withOperationLock(() =>\n this.#completePasskeyReplacement(params),\n );\n }\n\n async #completePasskeyReplacement(params: {\n registrationResponse: PasskeyRegistrationResponse;\n authenticationResponse: PasskeyAuthenticationResponse;\n }): Promise<void> {\n const sourceRecord = this.#requireEnrolled();\n if (sourceRecord.keyDerivation.method !== 'userHandle') {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.MigrationNotRequired,\n { code: PasskeyControllerErrorCode.MigrationNotRequired },\n );\n }\n\n const { challenge: registrationChallenge, ceremony: registrationCeremony } =\n this.#getRegistrationCeremony(params.registrationResponse);\n if (!registrationCeremony?.isReplacement) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.NoRegistrationCeremony,\n { code: PasskeyControllerErrorCode.NoRegistrationCeremony },\n );\n }\n if (\n registrationCeremony.sourceCredentialId !== sourceRecord.credential.id\n ) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.ReplacementSourceChanged,\n { code: PasskeyControllerErrorCode.ReplacementSourceChanged },\n );\n }\n\n // Migration should only run when keyring is unlocked.\n // this will throw an error if the keyring is locked.\n const vaultKey = await this.messenger.call(\n 'KeyringController:exportEncryptionKey',\n );\n\n try {\n const credential = await this.#verifyRegistrationResponse(\n params.registrationResponse,\n registrationCeremony,\n );\n if (credential.id === sourceRecord.credential.id) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.RegistrationVerificationFailed,\n { code: PasskeyControllerErrorCode.RegistrationVerificationFailed },\n );\n }\n\n const { newCounter } = await this.#verifyAuthenticationResponse(\n params.authenticationResponse,\n credential,\n );\n\n const keyDerivation = this.#getKeyDerivation(\n params.authenticationResponse,\n registrationCeremony,\n { requirePrf: true },\n );\n const replacementRecord = this.#createPasskeyRecord({\n vaultKey,\n authenticationResponse: params.authenticationResponse,\n credential,\n newCounter,\n keyDerivation,\n });\n\n this.#savePasskeyRecord(replacementRecord, sourceRecord);\n } finally {\n this.#ceremonyManager.deleteRegistrationCeremony(registrationChallenge);\n }\n }\n\n /**\n * Cancels an in-flight passkey replacement ceremony.\n *\n * @param registrationChallenge - Challenge returned by\n * {@link generatePasskeyReplacementRegistrationOptions}.\n */\n cancelPasskeyReplacement(registrationChallenge: string): void {\n const registrationCeremony = this.#ceremonyManager.getRegistrationCeremony(\n registrationChallenge,\n );\n if (registrationCeremony?.isReplacement) {\n this.#ceremonyManager.deleteRegistrationCeremony(registrationChallenge);\n }\n }\n\n /**\n * Builds WebAuthn credential request options for the post-registration\n * authentication step (between `create` and {@link protectVaultKeyWithPasskey}).\n *\n * @param params - Input for the pending registration ceremony.\n * @param params.registrationResponse - Result of `navigator.credentials.create()`.\n * @returns Public key credential request options for `navigator.credentials.get()`.\n */\n generatePostRegistrationAuthenticationOptions(params: {\n registrationResponse: PasskeyRegistrationResponse;\n }): PasskeyAuthenticationOptions {\n // get registration ceremony\n const { registrationResponse } = params;\n const regChallenge = this.#getChallengeFromClientData(\n registrationResponse.response.clientDataJSON,\n );\n const registrationCeremony =\n this.#ceremonyManager.getRegistrationCeremony(regChallenge);\n if (!registrationCeremony) {\n log('No active passkey registration ceremony for challenge');\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.NoRegistrationCeremony,\n { code: PasskeyControllerErrorCode.NoRegistrationCeremony },\n );\n }\n\n // build auth options\n const challenge = randomBytesToBase64URL(32);\n const extensions: Record<string, unknown> = {};\n if (registrationCeremony.prfSalt) {\n extensions.prf = { eval: { first: registrationCeremony.prfSalt } };\n }\n const options: PasskeyAuthenticationOptions = {\n challenge,\n rpId: this.#rpId,\n allowCredentials: [\n {\n id: registrationResponse.id,\n type: 'public-key',\n transports: registrationResponse.response.transports as\n | AuthenticatorTransportFuture[]\n | undefined,\n },\n ],\n userVerification: 'required',\n hints: ['client-device', 'hybrid'],\n timeout: WEBAUTHN_TIMEOUT_MS,\n extensions,\n };\n\n // save auth ceremony\n this.#ceremonyManager.saveAuthenticationCeremony(challenge, {\n challenge,\n registrationChallenge: regChallenge,\n createdAt: Date.now(),\n });\n\n return options;\n }\n\n /**\n * Builds WebAuthn credential request options for the enrolled passkey.\n *\n * @returns Public key credential request options for `navigator.credentials.get()`.\n */\n generateAuthenticationOptions(): PasskeyAuthenticationOptions {\n const record = this.#requireEnrolled();\n\n const challenge = randomBytesToBase64URL(32);\n\n const extensions: Record<string, unknown> = {};\n if (record.keyDerivation.method === 'prf') {\n extensions.prf = { eval: { first: record.keyDerivation.prfSalt } };\n }\n\n const options: PasskeyAuthenticationOptions = {\n challenge,\n rpId: this.#rpId,\n allowCredentials: [\n {\n id: record.credential.id,\n type: 'public-key',\n transports: record.credential.transports,\n },\n ],\n userVerification: 'required',\n hints: ['client-device', 'hybrid'],\n timeout: WEBAUTHN_TIMEOUT_MS,\n extensions,\n };\n\n this.#ceremonyManager.saveAuthenticationCeremony(challenge, {\n challenge,\n createdAt: Date.now(),\n });\n\n return options;\n }\n\n /**\n * Verifies registration and post-registration authentication, then stores the\n * vault key encrypted under the new passkey.\n *\n * Fetches the current vault encryption key from KeyringController before wrapping.\n * When onboarding is complete, requires `password` for step-up verification first.\n *\n * @param params - Enrollment completion inputs.\n * @param params.registrationResponse - Result of `navigator.credentials.create()`.\n * @param params.authenticationResponse - Result of `navigator.credentials.get()` after {@link generatePostRegistrationAuthenticationOptions}.\n * @param params.password - Wallet password when onboarding is complete (step-up).\n * @returns Resolves when enrollment completes.\n */\n async protectVaultKeyWithPasskey(params: {\n registrationResponse: PasskeyRegistrationResponse;\n authenticationResponse: PasskeyAuthenticationResponse;\n password?: string;\n }): Promise<void> {\n return this.#withOperationLock(() =>\n this.#protectVaultKeyWithPasskey(params),\n );\n }\n\n async #protectVaultKeyWithPasskey(params: {\n registrationResponse: PasskeyRegistrationResponse;\n authenticationResponse: PasskeyAuthenticationResponse;\n password?: string;\n }): Promise<void> {\n if (this.isPasskeyEnrolled()) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.AlreadyEnrolled,\n { code: PasskeyControllerErrorCode.AlreadyEnrolled },\n );\n }\n\n await this.#assertEnrollmentAllowed(params.password);\n const vaultKey = await this.messenger.call(\n 'KeyringController:exportEncryptionKey',\n );\n\n const { registrationResponse } = params;\n const { challenge, ceremony: registrationCeremony } =\n this.#getRegistrationCeremony(registrationResponse);\n if (!registrationCeremony) {\n log('No active passkey registration ceremony for challenge');\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.NoRegistrationCeremony,\n { code: PasskeyControllerErrorCode.NoRegistrationCeremony },\n );\n }\n\n try {\n const credential = await this.#verifyRegistrationResponse(\n registrationResponse,\n registrationCeremony,\n );\n const { newCounter } = await this.#verifyAuthenticationResponse(\n params.authenticationResponse,\n credential,\n );\n\n const keyDerivation = this.#getKeyDerivation(\n params.authenticationResponse,\n registrationCeremony,\n );\n const passkeyRecord = this.#createPasskeyRecord({\n vaultKey,\n authenticationResponse: params.authenticationResponse,\n credential,\n newCounter,\n keyDerivation,\n });\n\n this.#savePasskeyRecord(passkeyRecord);\n } finally {\n // delete registration ceremony\n this.#ceremonyManager.deleteRegistrationCeremony(challenge);\n }\n }\n\n #getRegistrationCeremony(registrationResponse: PasskeyRegistrationResponse): {\n challenge: string;\n ceremony: PasskeyRegistrationCeremony | undefined;\n } {\n const challenge = this.#getChallengeFromClientData(\n registrationResponse.response.clientDataJSON,\n );\n return {\n challenge,\n ceremony: this.#ceremonyManager.getRegistrationCeremony(challenge),\n };\n }\n\n async #verifyRegistrationResponse(\n registrationResponse: PasskeyRegistrationResponse,\n registrationCeremony: PasskeyRegistrationCeremony,\n ): Promise<PasskeyCredentialInfo> {\n const { verified, registrationInfo } = await verifyRegistrationResponse({\n response: registrationResponse,\n expectedChallenge: registrationCeremony.challenge,\n expectedOrigin: this.#expectedOrigin,\n expectedRPIDs: this.#expectedRPIDs,\n requireUserVerification: true,\n }).catch((error) => {\n log('Error verifying passkey registration response', error);\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.RegistrationVerificationFailed,\n {\n code: PasskeyControllerErrorCode.RegistrationVerificationFailed,\n cause: error instanceof Error ? error : new Error(String(error)),\n },\n );\n });\n\n if (!verified || !registrationInfo) {\n log(\n 'Passkey registration verification returned unverified or missing registration info',\n );\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.RegistrationVerificationFailed,\n { code: PasskeyControllerErrorCode.RegistrationVerificationFailed },\n );\n }\n\n const credential = {\n id: registrationInfo.credentialId,\n publicKey: bytesToBase64URL(registrationInfo.publicKey),\n counter: registrationInfo.counter,\n transports: registrationInfo.transports,\n aaguid: registrationInfo.aaguid,\n };\n if (\n registrationResponse.id !== credential.id ||\n registrationResponse.rawId !== credential.id\n ) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.RegistrationVerificationFailed,\n { code: PasskeyControllerErrorCode.RegistrationVerificationFailed },\n );\n }\n\n return credential;\n }\n\n #getKeyDerivation(\n authenticationResponse: PasskeyAuthenticationResponse,\n registrationCeremony: PasskeyRegistrationCeremony,\n options?: { requirePrf?: boolean },\n ): PasskeyKeyDerivation {\n const prfFirst = (\n authenticationResponse.clientExtensionResults as PrfClientExtensionResults\n )?.prf?.results?.first;\n const authHasPrfOutput =\n typeof prfFirst === 'string' && prfFirst.length > 0;\n\n if (options?.requirePrf) {\n if (!authHasPrfOutput || !registrationCeremony.prfSalt) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.PrfRequired,\n { code: PasskeyControllerErrorCode.PrfRequired },\n );\n }\n return { method: 'prf', prfSalt: registrationCeremony.prfSalt };\n }\n\n if (authHasPrfOutput && registrationCeremony.prfSalt) {\n return { method: 'prf', prfSalt: registrationCeremony.prfSalt };\n }\n\n if (\n authenticationResponse.response.userHandle !==\n registrationCeremony.userHandle\n ) {\n log(\n 'Post-registration assertion userHandle does not match registration ceremony',\n );\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.AuthenticationVerificationFailed,\n { code: PasskeyControllerErrorCode.AuthenticationVerificationFailed },\n );\n }\n\n return { method: 'userHandle' };\n }\n\n #createPasskeyRecord({\n vaultKey,\n authenticationResponse,\n credential,\n newCounter,\n keyDerivation,\n }: {\n vaultKey: string;\n authenticationResponse: PasskeyAuthenticationResponse;\n credential: PasskeyCredentialInfo;\n newCounter: number;\n keyDerivation: PasskeyKeyDerivation;\n }): PasskeyRecord {\n const encKey = deriveKeyFromAuthenticationResponse(authenticationResponse, {\n credential,\n keyDerivation,\n });\n const { ciphertext, iv } = encryptWithKey(vaultKey, encKey);\n\n return {\n credential: {\n ...credential,\n counter: Math.max(newCounter, credential.counter),\n },\n encryptedVaultKey: { ciphertext, iv },\n keyDerivation,\n };\n }\n\n #savePasskeyRecord(\n passkeyRecord: PasskeyRecord,\n expectedSourceRecord?: PasskeyRecord,\n ): void {\n this.update((state) => {\n if (\n expectedSourceRecord &&\n (state.passkeyRecord?.credential.id !==\n expectedSourceRecord.credential.id ||\n state.passkeyRecord?.keyDerivation.method !== 'userHandle')\n ) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.ReplacementSourceChanged,\n { code: PasskeyControllerErrorCode.ReplacementSourceChanged },\n );\n }\n state.passkeyRecord = passkeyRecord;\n });\n }\n\n /**\n * Verifies an authentication assertion and returns the decrypted vault key.\n *\n * Prefer orchestrated methods ({@link unlockWithPasskey},\n * {@link exportSeedPhraseWithPasskey}, {@link exportAccountsWithPasskey}) for product\n * flows instead of calling KeyringController with the returned key manually.\n *\n * @param authenticationResponse - Result of `navigator.credentials.get()`.\n * @returns The plaintext vault encryption key.\n */\n async retrieveVaultKeyWithPasskey(\n authenticationResponse: PasskeyAuthenticationResponse,\n ): Promise<string> {\n return this.#withOperationLock(() =>\n this.#retrieveVaultKeyWithPasskey(authenticationResponse),\n );\n }\n\n async #retrieveVaultKeyWithPasskey(\n authenticationResponse: PasskeyAuthenticationResponse,\n ): Promise<string> {\n const passkeyRecord = this.#requireEnrolled();\n\n // verify authentication response and update counter\n const { newCounter } = await this.#verifyAuthenticationResponse(\n authenticationResponse,\n passkeyRecord.credential,\n );\n this.update((state) => {\n if (!state.passkeyRecord) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.NotEnrolled,\n { code: PasskeyControllerErrorCode.NotEnrolled },\n );\n }\n state.passkeyRecord.credential.counter = Math.max(\n newCounter,\n state.passkeyRecord.credential.counter,\n );\n });\n\n // derive key\n const encKey = deriveKeyFromAuthenticationResponse(\n authenticationResponse,\n passkeyRecord,\n );\n\n // decrypt vault key\n try {\n const vaultKey = decryptWithKey(\n passkeyRecord.encryptedVaultKey.ciphertext,\n passkeyRecord.encryptedVaultKey.iv,\n encKey,\n );\n return vaultKey;\n } catch (cause) {\n log(\n 'Error decrypting vault key with passkey',\n cause instanceof Error ? cause : new Error(String(cause)),\n );\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.VaultKeyDecryptionFailed,\n {\n code: PasskeyControllerErrorCode.VaultKeyDecryptionFailed,\n cause: cause instanceof Error ? cause : new Error(String(cause)),\n },\n );\n }\n }\n\n /**\n * Unlocks the keyring using a passkey authentication assertion.\n *\n * @param authenticationResponse - Result of `navigator.credentials.get()`.\n * @returns Resolves when the keyring is unlocked.\n */\n async unlockWithPasskey(\n authenticationResponse: PasskeyAuthenticationResponse,\n ): Promise<void> {\n return this.#withOperationLock(async () => {\n const vaultKey = await this.#retrieveVaultKeyWithPasskey(\n authenticationResponse,\n );\n await this.messenger.call(\n 'KeyringController:submitEncryptionKey',\n vaultKey,\n );\n });\n }\n\n /**\n * Exports the seed phrase after passkey step-up authentication.\n *\n * @param authenticationResponse - Result of `navigator.credentials.get()`.\n * @param keyringId - Optional keyring id; defaults to the primary HD keyring.\n * @returns Raw seed phrase bytes from KeyringController.\n */\n async exportSeedPhraseWithPasskey(\n authenticationResponse: PasskeyAuthenticationResponse,\n keyringId?: string,\n ): Promise<Uint8Array> {\n return this.#withOperationLock(async () => {\n const vaultKey = await this.#retrieveVaultKeyWithPasskey(\n authenticationResponse,\n );\n return await this.messenger.call(\n 'KeyringController:exportSeedPhrase',\n { encryptionKey: vaultKey },\n keyringId,\n );\n });\n }\n\n /**\n * Exports private keys for the given addresses after passkey step-up authentication.\n *\n * @param authenticationResponse - Result of `navigator.credentials.get()`.\n * @param addresses - Account addresses to export.\n * @returns Private keys in the same order as `addresses`.\n */\n async exportAccountsWithPasskey(\n authenticationResponse: PasskeyAuthenticationResponse,\n addresses: string[],\n ): Promise<string[]> {\n return this.#withOperationLock(async () => {\n const vaultKey = await this.#retrieveVaultKeyWithPasskey(\n authenticationResponse,\n );\n\n const privateKeys: string[] = [];\n for (const address of addresses) {\n privateKeys.push(\n await this.messenger.call(\n 'KeyringController:exportAccount',\n { encryptionKey: vaultKey },\n address,\n ),\n );\n }\n return privateKeys;\n });\n }\n\n /**\n * Checks whether the given authentication assertion is valid for the enrolled passkey.\n *\n * On failure, returns `false` for {@link PasskeyControllerError} with a `code`;\n * other errors propagate.\n *\n * @param authenticationResponse - Result of `navigator.credentials.get()`.\n * @returns `true` if verification succeeds, otherwise `false`.\n */\n async verifyPasskeyAuthentication(\n authenticationResponse: PasskeyAuthenticationResponse,\n ): Promise<boolean> {\n return this.#withOperationLock(() =>\n this.#verifyPasskeyAuthentication(authenticationResponse),\n );\n }\n\n async #verifyPasskeyAuthentication(\n authenticationResponse: PasskeyAuthenticationResponse,\n ): Promise<boolean> {\n try {\n await this.#retrieveVaultKeyWithPasskey(authenticationResponse);\n return true;\n } catch (error: unknown) {\n if (error instanceof PasskeyControllerError && error.code !== undefined) {\n return false;\n }\n throw error;\n }\n }\n\n /**\n * Re-wraps the vault key after rotation. Updates persisted `encryptedVaultKey` on success.\n *\n * Does not verify WebAuthn or ceremony state—call only after your layer has authenticated\n * the user (passkey `get()` + verified assertion, or verified password). On passkey paths,\n * pass the same `authenticationResponse` you just verified (e.g. from\n * {@link retrieveVaultKeyWithPasskey} / {@link verifyPasskeyAuthentication}).\n *\n * For password change with passkey step-up, prefer\n * {@link changePasswordWithPasskeyVerification}, which orchestrates keyring export,\n * `changePassword`, and re-wrap in one call.\n *\n * @param params - Re-wrap inputs.\n * @param params.authenticationResponse - Used to derive the wrapping key.\n * @param params.oldVaultKey - Expected current vault key.\n * @param params.newVaultKey - New vault key to encrypt under the passkey.\n * @returns Resolves when the passkey record is updated.\n */\n async renewVaultKeyProtection(params: {\n authenticationResponse: PasskeyAuthenticationResponse;\n oldVaultKey: string;\n newVaultKey: string;\n }): Promise<void> {\n return this.#withOperationLock(() => this.#renewVaultKeyProtection(params));\n }\n\n async #renewVaultKeyProtection(params: {\n authenticationResponse: PasskeyAuthenticationResponse;\n oldVaultKey: string;\n newVaultKey: string;\n }): Promise<void> {\n const { authenticationResponse } = params;\n const passkeyRecord = this.#requireEnrolled();\n\n // derive key\n const encKey = deriveKeyFromAuthenticationResponse(\n authenticationResponse,\n passkeyRecord,\n );\n\n // decrypt vault key\n let decryptedVaultKey: string;\n try {\n decryptedVaultKey = decryptWithKey(\n passkeyRecord.encryptedVaultKey.ciphertext,\n passkeyRecord.encryptedVaultKey.iv,\n encKey,\n );\n } catch (error) {\n log(\n 'Error decrypting vault key during passkey vault key renewal',\n error instanceof Error ? error : new Error(String(error)),\n );\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.VaultKeyDecryptionFailed,\n {\n code: PasskeyControllerErrorCode.VaultKeyDecryptionFailed,\n cause: error instanceof Error ? error : new Error(String(error)),\n },\n );\n }\n\n // check if vault key matches\n const { oldVaultKey, newVaultKey } = params;\n if (\n !areUint8ArraysEqual(\n stringToBytes(decryptedVaultKey),\n stringToBytes(oldVaultKey),\n )\n ) {\n log(\n 'Passkey renewal rejected: decrypted vault key does not match oldVaultKey',\n );\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.VaultKeyMismatch,\n { code: PasskeyControllerErrorCode.VaultKeyMismatch },\n );\n }\n\n // encrypt new vault key\n const { ciphertext, iv } = encryptWithKey(newVaultKey, encKey);\n\n // persist passkey record (mutate current state only for vault key material)\n this.update((state) => {\n if (!state.passkeyRecord) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.NotEnrolled,\n {\n code: PasskeyControllerErrorCode.NotEnrolled,\n },\n );\n }\n state.passkeyRecord.encryptedVaultKey = { ciphertext, iv };\n });\n }\n\n /**\n * Changes the wallet password after passkey step-up authentication.\n *\n * When `renewVaultKeyProtection` is `true` (default), re-wraps the vault key under the\n * passkey after rotation. When `false`, removes the passkey instead.\n *\n * @param params - Change-password inputs.\n * @param params.newPassword - New wallet password.\n * @param params.authenticationResponse - Result of `navigator.credentials.get()`.\n * @param params.options - Optional flow controls.\n * @param params.options.renewVaultKeyProtection - Re-wrap vault key after password change.\n * @returns Resolves when the password change completes.\n */\n async changePasswordWithPasskeyVerification(params: {\n newPassword: string;\n authenticationResponse: PasskeyAuthenticationResponse;\n options?: { renewVaultKeyProtection?: boolean };\n }): Promise<void> {\n return this.#withOperationLock(() =>\n this.#changePasswordWithPasskeyVerification(params),\n );\n }\n\n async #changePasswordWithPasskeyVerification(params: {\n newPassword: string;\n authenticationResponse: PasskeyAuthenticationResponse;\n options?: { renewVaultKeyProtection?: boolean };\n }): Promise<void> {\n this.#requireEnrolled();\n\n const verified = await this.#verifyPasskeyAuthentication(\n params.authenticationResponse,\n );\n if (!verified) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.AuthenticationVerificationFailed,\n { code: PasskeyControllerErrorCode.AuthenticationVerificationFailed },\n );\n }\n\n const renewVaultKeyProtection =\n params.options?.renewVaultKeyProtection ?? true;\n\n if (!renewVaultKeyProtection) {\n await this.messenger.call(\n 'KeyringController:changePassword',\n params.newPassword,\n );\n this.#removePasskey();\n return;\n }\n\n const vaultKeyBefore = await this.messenger.call(\n 'KeyringController:exportEncryptionKey',\n );\n await this.messenger.call(\n 'KeyringController:changePassword',\n params.newPassword,\n );\n\n try {\n const vaultKeyAfter = await this.messenger.call(\n 'KeyringController:exportEncryptionKey',\n );\n await this.#renewVaultKeyProtection({\n authenticationResponse: params.authenticationResponse,\n oldVaultKey: vaultKeyBefore,\n newVaultKey: vaultKeyAfter,\n });\n } catch (error) {\n this.#removePasskey();\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.VaultKeyRenewalFailed,\n {\n code: PasskeyControllerErrorCode.VaultKeyRenewalFailed,\n cause: error instanceof Error ? error : new Error(String(error)),\n },\n );\n }\n }\n\n /**\n * Removes the enrolled passkey after verifying a passkey authentication assertion.\n *\n * @param authenticationResponse - Result of `navigator.credentials.get()`.\n * @returns Resolves when the passkey is removed.\n */\n async removePasskeyWithPasskeyVerification(\n authenticationResponse: PasskeyAuthenticationResponse,\n ): Promise<void> {\n return this.#withOperationLock(() =>\n this.#removePasskeyWithPasskeyVerification(authenticationResponse),\n );\n }\n\n async #removePasskeyWithPasskeyVerification(\n authenticationResponse: PasskeyAuthenticationResponse,\n ): Promise<void> {\n this.#requireEnrolled();\n\n const verified = await this.#verifyPasskeyAuthentication(\n authenticationResponse,\n );\n if (!verified) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.AuthenticationVerificationFailed,\n { code: PasskeyControllerErrorCode.AuthenticationVerificationFailed },\n );\n }\n\n this.#removePasskey();\n }\n\n /**\n * Removes the enrolled passkey after verifying the wallet password.\n *\n * @param password - Wallet password for step-up verification.\n * @returns Resolves when the passkey is removed.\n */\n async removePasskeyWithPasswordVerification(password: string): Promise<void> {\n return this.#withOperationLock(() =>\n this.#removePasskeyWithPasswordVerification(password),\n );\n }\n\n async #removePasskeyWithPasswordVerification(\n password: string,\n ): Promise<void> {\n this.#requireEnrolled();\n await this.messenger.call('KeyringController:verifyPassword', password);\n this.#removePasskey();\n }\n\n /**\n * Resets state and clears in-flight registration/authentication ceremonies.\n *\n * For user-facing passkey removal with step-up, use\n * {@link removePasskeyWithPasskeyVerification} or\n * {@link removePasskeyWithPasswordVerification}.\n */\n clearState(): void {\n this.#removePasskey();\n }\n\n /**\n * Releases all in-flight ceremony state and tears down the messenger.\n */\n destroy(): void {\n this.#ceremonyManager.clear();\n super.destroy();\n }\n\n /**\n * Validates a WebAuthn authentication response against stored credential data.\n *\n * @param authenticationResponse - Parsed authentication response from the client.\n * @param credential - Credential identifiers and public key material for verification.\n * @returns Updated authenticator signature counter.\n */\n async #verifyAuthenticationResponse(\n authenticationResponse: PasskeyAuthenticationResponse,\n credential: PasskeyCredentialInfo,\n ): Promise<{ newCounter: number }> {\n // get challenge\n const challenge = this.#getChallengeFromClientData(\n authenticationResponse.response.clientDataJSON,\n );\n\n // get authentication ceremony\n const authenticationCeremony =\n this.#ceremonyManager.getAuthenticationCeremony(challenge);\n if (!authenticationCeremony) {\n log('No active passkey authentication ceremony for challenge');\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.NoAuthenticationCeremony,\n { code: PasskeyControllerErrorCode.NoAuthenticationCeremony },\n );\n }\n\n try {\n if (\n authenticationResponse.id !== credential.id ||\n authenticationResponse.rawId !== credential.id\n ) {\n log(\n 'Passkey authentication response credential ID does not match the expected credential',\n );\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.AuthenticationVerificationFailed,\n {\n code: PasskeyControllerErrorCode.AuthenticationVerificationFailed,\n },\n );\n }\n\n // verify authentication response\n const result = await verifyAuthenticationResponse({\n response: authenticationResponse,\n expectedChallenge: authenticationCeremony.challenge,\n expectedOrigin: this.#expectedOrigin,\n expectedRPIDs: this.#expectedRPIDs,\n credential: {\n id: credential.id,\n publicKey: base64URLToBytes(credential.publicKey),\n counter: credential.counter,\n transports: credential.transports,\n },\n requireUserVerification: true,\n }).catch((error) => {\n log(\n 'Error verifying passkey authentication response',\n error instanceof Error ? error : new Error(String(error)),\n );\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.AuthenticationVerificationFailed,\n {\n code: PasskeyControllerErrorCode.AuthenticationVerificationFailed,\n cause: error instanceof Error ? error : new Error(String(error)),\n },\n );\n });\n if (!result.verified) {\n log('Passkey authentication verification returned unverified');\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.AuthenticationVerificationFailed,\n {\n code: PasskeyControllerErrorCode.AuthenticationVerificationFailed,\n },\n );\n }\n\n return { newCounter: result.authenticationInfo.newCounter };\n } finally {\n // delete authentication ceremony\n this.#ceremonyManager.deleteAuthenticationCeremony(challenge);\n }\n }\n\n /**\n * Serializes orchestrated passkey operations that mutate state or call KeyringController.\n *\n * @param callback - Operation to run while the mutex is held.\n * @returns The result of the callback.\n */\n async #withOperationLock<Result>(\n callback: () => Promise<Result>,\n ): Promise<Result> {\n return this.#operationMutex.runExclusive(callback);\n }\n\n async #assertEnrollmentAllowed(password?: string): Promise<void> {\n if (!this.#getIsOnboardingCompleted()) {\n return;\n }\n\n if (!password) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.EnrollmentPasswordRequired,\n {\n code: PasskeyControllerErrorCode.EnrollmentPasswordRequired,\n },\n );\n }\n\n await this.messenger.call('KeyringController:verifyPassword', password);\n }\n\n #requireEnrolled(): PasskeyRecord {\n const record = this.state.passkeyRecord;\n if (!record) {\n throw new PasskeyControllerError(\n PasskeyControllerErrorMessage.NotEnrolled,\n {\n code: PasskeyControllerErrorCode.NotEnrolled,\n },\n );\n }\n return record;\n }\n\n #getChallengeFromClientData(clientDataJSON: string): string {\n return decodeClientDataJSON(clientDataJSON).challenge;\n }\n\n /**\n * Clears enrolled passkey state and in-flight ceremonies.\n */\n #removePasskey(): void {\n this.update(() => getDefaultPasskeyControllerState());\n this.#ceremonyManager.clear();\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ceremony-manager.d.ts","sourceRoot":"","sources":["../src/ceremony-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,6BAA6B,EAC7B,2BAA2B,EAC5B,MAAM,YAAY,CAAC;AAEpB,qEAAqE;AACrE,eAAO,MAAM,mBAAmB,QAAS,CAAC;AAE1C;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,SAAU,CAAC;AAE7C;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,QAA8C,CAAC;AAE/E;;;GAGG;AACH,eAAO,MAAM,iCAAiC,KAAK,CAAC;AAIpD;;;GAGG;AACH,qBAAa,eAAe;;IAuD1B;;;;;OAKG;IACH,wBAAwB,CACtB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,2BAA2B,GACpC,IAAI,CAIN;IAED;;;;;OAKG;IACH,0BAA0B,CACxB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,6BAA6B,GACtC,IAAI,CAIN;IAED;;;;;OAKG;IACH,uBAAuB,CACrB,SAAS,EAAE,MAAM,GAChB,2BAA2B,GAAG,SAAS,CAGzC;IAED;;;;;OAKG;IACH,yBAAyB,CACvB,SAAS,EAAE,MAAM,GAChB,6BAA6B,GAAG,SAAS,CAG3C;IAED;;;;;OAKG;IACH,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAErD;IAED;;;;;OAKG;IACH,4BAA4B,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEvD;IAED,sEAAsE;IACtE,KAAK,IAAI,IAAI,CAGZ;CACF"}
1
+ {"version":3,"file":"ceremony-manager.d.ts","sourceRoot":"","sources":["../src/ceremony-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,6BAA6B,EAC7B,2BAA2B,EAC5B,MAAM,YAAY,CAAC;AAEpB,qEAAqE;AACrE,eAAO,MAAM,mBAAmB,QAAS,CAAC;AAE1C;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,SAAU,CAAC;AAE7C;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,QAA8C,CAAC;AAE/E;;;GAGG;AACH,eAAO,MAAM,iCAAiC,KAAK,CAAC;AAIpD;;;GAGG;AACH,qBAAa,eAAe;;IAuE1B;;;;;OAKG;IACH,wBAAwB,CACtB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,2BAA2B,GACpC,IAAI,CAIN;IAED;;;;;OAKG;IACH,0BAA0B,CACxB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,6BAA6B,GACtC,IAAI,CAIN;IAED;;;;;OAKG;IACH,uBAAuB,CACrB,SAAS,EAAE,MAAM,GAChB,2BAA2B,GAAG,SAAS,CAGzC;IAED;;;;;OAKG;IACH,yBAAyB,CACvB,SAAS,EAAE,MAAM,GAChB,6BAA6B,GAAG,SAAS,CAG3C;IAED;;;;;OAKG;IACH,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAIrD;IAED;;;;;OAKG;IACH,4BAA4B,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEvD;IAED,sEAAsE;IACtE,KAAK,IAAI,IAAI,CAGZ;CACF"}