Skip to content

Commit

Permalink
fix(cat-voices): account recovery keychain password (#1151)
Browse files Browse the repository at this point in the history
* fix: account recovery keychain password

* chore: clean up commented out code
  • Loading branch information
damian-molinski authored Nov 7, 2024
1 parent ea17789 commit ac0ae5b
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class _UnlockPasswordPanelState extends State<UnlockPasswordPanel> {
),
const SizedBox(height: 22),
_BlocNavigation(
onNextTap: _createKeychain,
onBackTap: _clearPasswordAndGoBack,
),
],
Expand All @@ -74,6 +75,16 @@ class _UnlockPasswordPanelState extends State<UnlockPasswordPanel> {
RegistrationCubit.of(context).recover.setConfirmPassword(confirmPassword);
}

Future<void> _createKeychain() async {
final cubit = RegistrationCubit.of(context);

final success = await cubit.recover.createKeychain();

if (success) {
cubit.nextStep();
}
}

void _clearPasswordAndGoBack() {
final registration = RegistrationCubit.of(context);

Expand Down Expand Up @@ -124,9 +135,11 @@ class _BlocUnlockPasswordForm extends StatelessWidget {
}

class _BlocNavigation extends StatelessWidget {
final VoidCallback onNextTap;
final VoidCallback onBackTap;

const _BlocNavigation({
required this.onNextTap,
required this.onBackTap,
});

Expand All @@ -138,6 +151,7 @@ class _BlocNavigation extends StatelessWidget {
builder: (context, state) {
return RegistrationBackNextNavigation(
isNextEnabled: state,
onNextTap: onNextTap,
onBackTap: onBackTap,
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ abstract interface class RecoverManager implements UnlockPasswordManager {

void setSeedPhraseWords(List<SeedPhraseWord> words);

Future<void> recoverAccount();
Future<bool> recoverAccount();

Future<bool> createKeychain();

Future<void> reset();
}
Expand Down Expand Up @@ -69,22 +71,19 @@ final class RecoverCubit extends Cubit<RecoverStateData>
}

@override
Future<void> recoverAccount() async {
Future<bool> recoverAccount() async {
try {
emit(state.copyWith(accountDetails: const Optional.empty()));

final seedPhrase = _seedPhrase;
final lockFactor = PasswordLockFactor(password.value);

if (seedPhrase == null) {
const exception = LocalizedRegistrationSeedPhraseNotFoundException();
emit(state.copyWith(accountDetails: Optional(Failure(exception))));
return;
return false;
}

final account = await _registrationService.recoverAccount(
seedPhrase: seedPhrase,
lockFactor: lockFactor,
);

_recoveredAccount = account;
Expand All @@ -107,17 +106,51 @@ final class RecoverCubit extends Cubit<RecoverStateData>
);

emit(state.copyWith(accountDetails: Optional(Success(accountDetails))));

return true;
} on RegistrationException catch (error, stack) {
_logger.severe('recover account', error, stack);

_recoveredAccount = null;

final exception = LocalizedRegistrationException.from(error);
emit(state.copyWith(accountDetails: Optional(Failure(exception))));

return false;
} catch (error, stack) {
_logger.severe('recover account', error, stack);

_recoveredAccount = null;

const exception = LocalizedUnknownException();
emit(state.copyWith(accountDetails: Optional(Failure(exception))));

return false;
}
}

@override
Future<bool> createKeychain() async {
final account = _recoveredAccount;
final seedPhrase = _seedPhrase;
final password = this.password;

if (account == null || seedPhrase == null || password.isNotValid) {
emitError(const LocalizedRegistrationUnknownException());
return false;
}

final lockFactor = PasswordLockFactor(password.value);

await _registrationService.createKeychainFor(
account: account,
seedPhrase: seedPhrase,
lockFactor: lockFactor,
);

await _userService.useAccount(account);

return true;
}

@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,15 @@ abstract interface class RegistrationService {
required Set<AccountRole> roles,
});

/// Loads account related to this [seedPhrase]. Throws exception if non found.
Future<Account> recoverAccount({
required SeedPhrase seedPhrase,
});

/// Creates [Keychain] for given [account] with [lockFactor].
Future<Keychain> createKeychainFor({
required Account account,
required SeedPhrase seedPhrase,
required LockFactor lockFactor,
});

Expand Down Expand Up @@ -138,7 +145,6 @@ final class RegistrationServiceImpl implements RegistrationService {
@override
Future<Account> recoverAccount({
required SeedPhrase seedPhrase,
required LockFactor lockFactor,
}) async {
await Future<void>.delayed(const Duration(milliseconds: 200));

Expand All @@ -148,17 +154,7 @@ final class RegistrationServiceImpl implements RegistrationService {
}

final roles = {AccountRole.root};
// TODO(dtscalac): Update key value when derivation is final.
final keyPair = await deriveAccountRoleKeyPair(
seedPhrase: seedPhrase,
roles: roles,
);

final keychainId = const Uuid().v4();
final keychain = await _keychainProvider.create(keychainId);
await keychain.setLock(lockFactor);
await keychain.unlock(lockFactor);
await keychain.setMasterKey(keyPair.privateKey);

// Note. with rootKey query backend for account details.
return Account(
Expand All @@ -172,6 +168,27 @@ final class RegistrationServiceImpl implements RegistrationService {
);
}

@override
Future<Keychain> createKeychainFor({
required Account account,
required SeedPhrase seedPhrase,
required LockFactor lockFactor,
}) async {
final keychainId = account.keychainId;

final keyPair = await deriveAccountRoleKeyPair(
seedPhrase: seedPhrase,
roles: account.roles,
);

final keychain = await _keychainProvider.create(keychainId);
await keychain.setLock(lockFactor);
await keychain.unlock(lockFactor);
await keychain.setMasterKey(keyPair.privateKey);

return keychain;
}

@override
Future<Transaction> prepareRegistration({
required CardanoWallet wallet,
Expand Down

0 comments on commit ac0ae5b

Please sign in to comment.