Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
luckyrat committed Jan 16, 2025
1 parent b2dc719 commit 865775e
Show file tree
Hide file tree
Showing 11 changed files with 318 additions and 147 deletions.
Binary file modified assets/Demo.kdbx
Binary file not shown.
Binary file added assets/Demo.zip
Binary file not shown.
10 changes: 7 additions & 3 deletions lib/cubit/account_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -337,16 +337,15 @@ class AccountCubit extends Cubit<AccountState> {
final newEmailHashed = await hashString(newEmailAddress, EMAIL_ID_SALT);

final protectedValue = ProtectedValue.fromString(password);
final key = protectedValue.hash;
final passKeyConfirmation = await derivePassKey(user.email!, key);

await _userRepo.changeEmailAddress(user, newEmailAddress, newEmailHashed, passKeyConfirmation);
await _userRepo.changeEmailAddress(user, newEmailAddress, newEmailHashed, protectedValue);
final prefs = await SharedPreferences.getInstance();
await prefs.setString('user.current.email', newEmailAddress);
if (user.id?.isNotEmpty ?? false) {
await prefs.setString('user.authMaterialUserIdMap.${user.emailHashed}', user.id!);
}
final newUser = await User.fromEmail(newEmailAddress);
//TODO: what is the below on about? maybe that we need to make sure that the vault is locked when we do this operation, thus ensuring that leaving the user signed in due to network faults, manipulation, etc. doesn't leave them with access to their vault data. but what about taking exports? shouldn't that be allowed? probably yes, as long as they are currently verified and can thus log in normally. then cliking the button to change email address will lock the vault and kick them to the screen that ultimately sends their request to this function.
//TODO: verify that we can only get here if Vault is already locked. Throw exception earlier if we can detect that state?
emit(AccountChosen(newUser));
//await BlocProvider.of<AccountCubit>(context).forgetUser(vc.signout);
Expand All @@ -356,7 +355,12 @@ class AccountCubit extends Cubit<AccountState> {
l.w('Unable to changeEmailAddress due to a 403.');
emit(AccountEmailChangeRequested(user,
'Due to an authentication problem, we were unable to change your email address. Probably it has been too long since you last signed in with your previous email address. We have left you signed in using your old email address but you may find that you are signed out soon. Please sign out and then sign in again with your previous email address and try again when you have enough time to complete the operation within 10 minutes.'));
} on FormatException {
// Local validation
l.i('Unable to changeEmailAddress due to FormatException.');
emit(AccountEmailChangeRequested(user, 'Please enter the correct password for your existing Kee Vault account.'));
} on KeeInvalidRequestException {
// Local validation should mean this is unlikely to happen outside of malicious acts
l.i('Unable to changeEmailAddress due to 400 response.');
emit(AccountEmailChangeRequested(user,
'Please double check that you have entered the correct password for your existing Kee Vault account. Also check that you have entered a valid email address of no more than 70 characters.'));
Expand Down
33 changes: 33 additions & 0 deletions lib/generated/intl/messages_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,30 @@ class MessageLookup extends MessageLookupByLibrary {
"cancelExportOrImport": m3,
"changeCancelSubscription": MessageLookupByLibrary.simpleMessage(
"Change or Cancel Subscription"),
"changeEmail":
MessageLookupByLibrary.simpleMessage("Change email address"),
"changeEmailInfo1": MessageLookupByLibrary.simpleMessage(
"Your email address has a crucial role in the advanced security protections Kee Vault offers. Changing it securely is a far more complex task than for most of the places you might wish to change it. We are happy to finally offer this feature to you but please read the information carefully and don\'t proceed when you are in a rush."),
"changeEmailInfo2": MessageLookupByLibrary.simpleMessage(
"Your new email address will:"),
"changeEmailInfo2a":
MessageLookupByLibrary.simpleMessage("become your new sign-in ID"),
"changeEmailInfo2b": MessageLookupByLibrary.simpleMessage(
"need to be verified to confirm it belongs to you"),
"changeEmailInfo3":
MessageLookupByLibrary.simpleMessage("We recommend that you:"),
"changeEmailInfo3a": MessageLookupByLibrary.simpleMessage(
"1) Click the Cancel button below, sign in to Kee Vault again, Export your Vault to a KDBX file and store it somewhere safe as a backup."),
"changeEmailInfo3b": MessageLookupByLibrary.simpleMessage(
"2) Double check you enter the correct email address - you will need to type it exactly to sign in to your account in a moment."),
"changeEmailInfo3c": MessageLookupByLibrary.simpleMessage(
"3) Copy/paste what you have entered in the email address box and store somewhere like a note on your phone."),
"changeEmailInfo4": MessageLookupByLibrary.simpleMessage(
"If you make a mistake, you should be able to regain access to your Vault but in some cases you may need to create a new Kee Vault subscription and import from your previously exported KDBX file - this can result in additional hassle and costs since your current subscription would not automatically end."),
"changeEmailInfo5": MessageLookupByLibrary.simpleMessage(
"Your password will remain the same throughout the process. If you want to change that too, we first recommend signing in on multiple devices using your new email address and waiting at least an hour."),
"changeEmailInfo6": MessageLookupByLibrary.simpleMessage(
"I have read the above warnings, mitigated the risks and wish to continue"),
"changeEmailPrefs":
MessageLookupByLibrary.simpleMessage("Change email preferences"),
"changePassword":
Expand All @@ -161,6 +185,8 @@ class MessageLookup extends MessageLookupByLibrary {
"Colours allow you to categorise your entries in a more simplistic and visual way than Groups or Labels."),
"confirmItsYou": MessageLookupByLibrary.simpleMessage(
"To access your passwords please confirm it\'s you"),
"continueSigningIn":
MessageLookupByLibrary.simpleMessage("Continue signing in"),
"copySecret": MessageLookupByLibrary.simpleMessage("Copy Secret"),
"createNewEntry":
MessageLookupByLibrary.simpleMessage("Create new entry"),
Expand All @@ -175,6 +201,8 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Create secure password"),
"createVault": MessageLookupByLibrary.simpleMessage("Create my Vault"),
"creating": MessageLookupByLibrary.simpleMessage("Creating"),
"currentEmailAddress":
MessageLookupByLibrary.simpleMessage("Current email address"),
"currentPassword":
MessageLookupByLibrary.simpleMessage("Current Password"),
"currentPasswordNotCorrect": MessageLookupByLibrary.simpleMessage(
Expand Down Expand Up @@ -419,6 +447,8 @@ class MessageLookup extends MessageLookupByLibrary {
"The URL must match the hostname (domain and subdomains) and port."),
"move": MessageLookupByLibrary.simpleMessage("Move"),
"name": MessageLookupByLibrary.simpleMessage("name"),
"newEmailAddress":
MessageLookupByLibrary.simpleMessage("New email address"),
"newGroup": MessageLookupByLibrary.simpleMessage("New Group"),
"newPassword": MessageLookupByLibrary.simpleMessage("New password"),
"newPasswordRepeat": MessageLookupByLibrary.simpleMessage(
Expand All @@ -427,6 +457,9 @@ class MessageLookup extends MessageLookupByLibrary {
"newUser": MessageLookupByLibrary.simpleMessage("New User"),
"noEntriesCreateNewInstruction": MessageLookupByLibrary.simpleMessage(
"You have no password entries yet. Create one using the + button below. If you have passwords already stored in the standard KDBX (KeePass) format you can import them."),
"noLongerHaveAccessToUnverifiedEmail":
MessageLookupByLibrary.simpleMessage(
"No longer have access to your email address?"),
"notSignedIn": MessageLookupByLibrary.simpleMessage("Not signed in"),
"notes": MessageLookupByLibrary.simpleMessage("Notes"),
"occasionalNotifications": MessageLookupByLibrary.simpleMessage(
Expand Down
160 changes: 160 additions & 0 deletions lib/generated/l10n.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 18 additions & 1 deletion lib/l10n/intl_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -377,5 +377,22 @@
"resendVerification": "Resend the verification link",
"signInAgainWhenVerified": "When your email address is successfully verified you can click the button below to continue your sign-in.",
"expiredWhileSignedIn": "Your subscription has just expired. Please click \"Sign out\" from the main menu below, then sign-in and follow the instructions. Your data is still available at the moment so don't panic.",
"openInBrowser": "Open in browser"
"openInBrowser": "Open in browser",
"continueSigningIn": "Continue signing in",
"noLongerHaveAccessToUnverifiedEmail": "No longer have access to your email address?",
"changeEmail": "Change email address",
"changeEmailInfo1": "Your email address has a crucial role in the advanced security protections Kee Vault offers. Changing it securely is a far more complex task than for most of the places you might wish to change it. We are happy to finally offer this feature to you but please read the information carefully and don't proceed when you are in a rush.",
"changeEmailInfo2": "Your new email address will:",
"changeEmailInfo2a": "become your new sign-in ID",
"changeEmailInfo2b": "need to be verified to confirm it belongs to you",
"changeEmailInfo3": "We recommend that you:",
"changeEmailInfo3a": "1) Click the Cancel button below, sign in to Kee Vault again, Export your Vault to a KDBX file and store it somewhere safe as a backup.",
"changeEmailInfo3b": "2) Double check you enter the correct email address - you will need to type it exactly to sign in to your account in a moment.",
"changeEmailInfo3c": "3) Copy/paste what you have entered in the email address box and store somewhere like a note on your phone.",
"changeEmailInfo4": "If you make a mistake, you should be able to regain access to your Vault but in some cases you may need to create a new Kee Vault subscription and import from your previously exported KDBX file - this can result in additional hassle and costs since your current subscription would not automatically end.",
"changeEmailInfo5": "Your password will remain the same throughout the process. If you want to change that too, we first recommend signing in on multiple devices using your new email address and waiting at least an hour.",
"changeEmailInfo6": "I have read the above warnings, mitigated the risks and wish to continue",
"currentEmailAddress": "Current email address",
"newEmailAddress": "New email address"

}
5 changes: 3 additions & 2 deletions lib/user_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ class UserRepository {
return user;
}

Future<void> changeEmailAddress(User user, String newEmailAddress, String newEmailHashed, String newPassKey) async {
await userService.changeEmailAddress(user, newEmailAddress, newEmailHashed, newPassKey);
Future<void> changeEmailAddress(
User user, String newEmailAddress, String newEmailHashed, ProtectedValue password) async {
await userService.changeEmailAddress(user, newEmailAddress, newEmailHashed, password);
return;
}

Expand Down
15 changes: 12 additions & 3 deletions lib/vault_backend/user_service.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:convert/convert.dart';
import 'package:kdbx/kdbx.dart';
import 'package:keevault/logging/logger.dart';
import 'package:keevault/vault_backend/exceptions.dart';
import 'package:keevault/vault_backend/login_parameters.dart';
Expand Down Expand Up @@ -107,7 +108,6 @@ class UserService {
Future<User> createAccount(User user, int marketingEmailStatus, int subscriptionSource) async {
final hexSalt = generateSalt();
user.salt = hex2base64(hexSalt);
//TODO: Verify that changing from user.id to user.emailHashed has expected effect (nothing since we just defaulted the id to emailHashed anyway and the server will tell us the final random user ID after registration succeeds)
final privateKey = derivePrivateKey(hexSalt, user.emailHashed!, user.passKey!);
final verifier = deriveVerifier(privateKey);
final response = await _service.postRequest<String>('register', {
Expand Down Expand Up @@ -207,13 +207,22 @@ class UserService {

// We make no changes to the User model since we will sign the user out and ask them to
// sign in again, partly so that we can ensure they have verified their new email address.
Future<void> changeEmailAddress(User user, String newEmailAddress, String newEmailHashed, String newPassKey) async {
Future<void> changeEmailAddress(
User user, String newEmailAddress, String newEmailHashed, ProtectedValue password) async {
final key = password.hash;
final oldPassKey = await derivePassKey(user.email!, key);
final newPassKey = await derivePassKey(newEmailAddress, key);

if (user.passKey != oldPassKey) {
throw FormatException('Password does not match');
}

final newHexSalt = generateSalt();
final newSalt = hex2base64(newHexSalt);
final newPrivateKey = derivePrivateKey(newHexSalt, newEmailHashed, newPassKey);
final newVerifier = deriveVerifier(newPrivateKey);

final oldPrivateKey = derivePrivateKey(user.salt!, user.emailHashed!, user.passKey!);
final oldPrivateKey = derivePrivateKey(user.salt!, user.emailHashed!, oldPassKey);
final oldVerifier = deriveVerifier(oldPrivateKey);
final oldVerifierHashed = await hashBytes(Uint8List.fromList(hex.decode(oldVerifier)));

Expand Down
Loading

0 comments on commit 865775e

Please sign in to comment.