@@ -26,6 +26,9 @@ import {
26
26
stringToBytes ,
27
27
bigIntToHex ,
28
28
} from '@metamask/utils' ;
29
+ import { gcm } from '@noble/ciphers/aes' ;
30
+ import { utf8ToBytes } from '@noble/ciphers/utils' ;
31
+ import { managedNonce } from '@noble/ciphers/webcrypto' ;
29
32
import type { webcrypto } from 'node:crypto' ;
30
33
31
34
import {
@@ -396,10 +399,14 @@ async function createMockVault(
396
399
const { vault : encryptedMockVault , exportedKeyString } =
397
400
await encryptor . encryptWithDetail ( MOCK_PASSWORD , serializedKeyData ) ;
398
401
402
+ const aes = managedNonce ( gcm ) ( encKey ) ;
403
+ const encryptedPassword = aes . encrypt ( utf8ToBytes ( MOCK_PASSWORD ) ) ;
404
+
399
405
return {
400
406
encryptedMockVault,
401
407
vaultEncryptionKey : exportedKeyString ,
402
408
vaultEncryptionSalt : JSON . parse ( encryptedMockVault ) . salt ,
409
+ encryptedPassword,
403
410
revokeToken : mockRevokeToken ,
404
411
} ;
405
412
}
@@ -445,6 +452,7 @@ async function decryptVault(vault: string, password: string) {
445
452
* @param options.vault - The mock vault data.
446
453
* @param options.vaultEncryptionKey - The mock vault encryption key.
447
454
* @param options.vaultEncryptionSalt - The mock vault encryption salt.
455
+ * @param options.encryptedPassword - The mock encrypted password.
448
456
* @returns The initial controller state with the mock authenticated user.
449
457
*/
450
458
function getMockInitialControllerState ( options ?: {
@@ -455,6 +463,7 @@ function getMockInitialControllerState(options?: {
455
463
vault ?: string ;
456
464
vaultEncryptionKey ?: string ;
457
465
vaultEncryptionSalt ?: string ;
466
+ encryptedPassword ?: string ;
458
467
} ) : Partial < SeedlessOnboardingControllerState > {
459
468
const state = getDefaultSeedlessOnboardingControllerState ( ) ;
460
469
@@ -486,6 +495,10 @@ function getMockInitialControllerState(options?: {
486
495
state . authPubKey = options . authPubKey ?? MOCK_AUTH_PUB_KEY ;
487
496
}
488
497
498
+ if ( options ?. encryptedPassword ) {
499
+ state . encryptedPassword = options . encryptedPassword ;
500
+ }
501
+
489
502
return state ;
490
503
}
491
504
@@ -2955,6 +2968,14 @@ describe('SeedlessOnboardingController', () => {
2955
2968
} ) ,
2956
2969
} ,
2957
2970
async ( { controller, toprfClient } ) => {
2971
+ await mockCreateToprfKeyAndBackupSeedPhrase (
2972
+ toprfClient ,
2973
+ controller ,
2974
+ RECOVERED_PASSWORD ,
2975
+ MOCK_SEED_PHRASE ,
2976
+ MOCK_KEYRING_ID ,
2977
+ ) ;
2978
+
2958
2979
// Mock recoverEncKey for the global password
2959
2980
const mockToprfEncryptor = createMockToprfEncryptor ( ) ;
2960
2981
const encKey = mockToprfEncryptor . deriveEncKey ( GLOBAL_PASSWORD ) ;
@@ -2968,8 +2989,10 @@ describe('SeedlessOnboardingController', () => {
2968
2989
} ) ;
2969
2990
2970
2991
// Mock toprfClient.recoverPassword
2992
+ const recoveredEncKey =
2993
+ mockToprfEncryptor . deriveEncKey ( RECOVERED_PASSWORD ) ;
2971
2994
jest . spyOn ( toprfClient , 'recoverPassword' ) . mockResolvedValueOnce ( {
2972
- password : RECOVERED_PASSWORD ,
2995
+ password : bytesToBase64 ( recoveredEncKey ) ,
2973
2996
} ) ;
2974
2997
2975
2998
const result = await controller . recoverCurrentDevicePassword ( {
@@ -2983,6 +3006,45 @@ describe('SeedlessOnboardingController', () => {
2983
3006
) ;
2984
3007
} ) ;
2985
3008
3009
+ it ( 'should throw if encryptedPassword not set' , async ( ) => {
3010
+ await withController (
3011
+ {
3012
+ state : getMockInitialControllerState ( {
3013
+ withMockAuthenticatedUser : true ,
3014
+ withMockAuthPubKey : true ,
3015
+ } ) ,
3016
+ } ,
3017
+ async ( { controller, toprfClient } ) => {
3018
+ // Mock recoverEncKey for the global password
3019
+ const mockToprfEncryptor = createMockToprfEncryptor ( ) ;
3020
+ const encKey = mockToprfEncryptor . deriveEncKey ( GLOBAL_PASSWORD ) ;
3021
+ const authKeyPair =
3022
+ mockToprfEncryptor . deriveAuthKeyPair ( GLOBAL_PASSWORD ) ;
3023
+ jest . spyOn ( toprfClient , 'recoverEncKey' ) . mockResolvedValueOnce ( {
3024
+ encKey,
3025
+ authKeyPair,
3026
+ rateLimitResetResult : Promise . resolve ( ) ,
3027
+ keyShareIndex : 1 ,
3028
+ } ) ;
3029
+
3030
+ // Mock toprfClient.recoverPassword
3031
+ const recoveredEncKey =
3032
+ mockToprfEncryptor . deriveEncKey ( RECOVERED_PASSWORD ) ;
3033
+ jest . spyOn ( toprfClient , 'recoverPassword' ) . mockResolvedValueOnce ( {
3034
+ password : bytesToBase64 ( recoveredEncKey ) ,
3035
+ } ) ;
3036
+
3037
+ await expect (
3038
+ controller . recoverCurrentDevicePassword ( {
3039
+ globalPassword : GLOBAL_PASSWORD ,
3040
+ } ) ,
3041
+ ) . rejects . toThrow (
3042
+ SeedlessOnboardingControllerErrorMessage . CouldNotRecoverPassword ,
3043
+ ) ;
3044
+ } ,
3045
+ ) ;
3046
+ } ) ;
3047
+
2986
3048
it ( 'should throw SRPNotBackedUpError if no authPubKey in state' , async ( ) => {
2987
3049
await withController (
2988
3050
{
@@ -4071,6 +4133,7 @@ describe('SeedlessOnboardingController', () => {
4071
4133
let INITIAL_AUTH_PUB_KEY : string ;
4072
4134
let initialAuthKeyPair : KeyPair ; // Store initial keypair for vault creation
4073
4135
let initialEncKey : Uint8Array ; // Store initial encKey for vault creation
4136
+ let initialEncryptedPassword : Uint8Array ;
4074
4137
4075
4138
// Generate initial keys and vault state before tests run
4076
4139
beforeAll ( async ( ) => {
@@ -4090,6 +4153,7 @@ describe('SeedlessOnboardingController', () => {
4090
4153
MOCK_VAULT = mockResult . encryptedMockVault ;
4091
4154
MOCK_VAULT_ENCRYPTION_KEY = mockResult . vaultEncryptionKey ;
4092
4155
MOCK_VAULT_ENCRYPTION_SALT = mockResult . vaultEncryptionSalt ;
4156
+ initialEncryptedPassword = mockResult . encryptedPassword ;
4093
4157
} ) ;
4094
4158
4095
4159
it ( 'should retry recoverCurrentDevicePassword after refreshing expired tokens' , async ( ) => {
@@ -4101,6 +4165,7 @@ describe('SeedlessOnboardingController', () => {
4101
4165
vault : MOCK_VAULT ,
4102
4166
vaultEncryptionKey : MOCK_VAULT_ENCRYPTION_KEY ,
4103
4167
vaultEncryptionSalt : MOCK_VAULT_ENCRYPTION_SALT ,
4168
+ encryptedPassword : bytesToBase64 ( initialEncryptedPassword ) ,
4104
4169
} ) ,
4105
4170
} ,
4106
4171
async ( { controller, toprfClient, mockRefreshJWTToken } ) => {
@@ -4122,7 +4187,7 @@ describe('SeedlessOnboardingController', () => {
4122
4187
) ;
4123
4188
} )
4124
4189
. mockResolvedValueOnce ( {
4125
- password : MOCK_PASSWORD ,
4190
+ password : bytesToBase64 ( initialEncKey ) ,
4126
4191
} ) ;
4127
4192
4128
4193
// Mock authenticate for token refresh
0 commit comments