1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-20 02:48:03 -05:00
This commit is contained in:
Bernd Schoolmann 2025-06-09 10:15:39 +02:00
parent d860bdc401
commit 8be2868670
No known key found for this signature in database

View File

@ -61,59 +61,85 @@ public class RotateUserAccountKeysCommand(
user.RevisionDate = user.AccountRevisionDate = now; user.RevisionDate = user.AccountRevisionDate = now;
user.LastKeyRotationDate = now; user.LastKeyRotationDate = now;
user.SecurityStamp = Guid.NewGuid().ToString(); user.SecurityStamp = Guid.NewGuid().ToString();
var currentSignatureKeyPair = await _userSignatureKeyPairRepository.GetByUserIdAsync(user.Id);
List<UpdateEncryptedDataForKeyRotation> saveEncryptedDataActions = []; List<UpdateEncryptedDataForKeyRotation> saveEncryptedDataActions = [];
if (!model.MasterPasswordUnlockData.ValidateForUser(user)) UpdateAccountKeys(model, user, saveEncryptedDataActions);
UpdateUnlockMethods(model, user, saveEncryptedDataActions);
UpdateUserData(model, user, saveEncryptedDataActions);
await _userRepository.UpdateUserKeyAndEncryptedDataV2Async(user, saveEncryptedDataActions);
await _pushService.PushLogOutAsync(user.Id);
return IdentityResult.Success;
}
async Task<bool> IsUserV2UserAsync(User user)
{
ArgumentNullException.ThrowIfNull(user);
var currentSignatureKeyPair = await _userSignatureKeyPairRepository.GetByUserIdAsync(user.Id);
return currentSignatureKeyPair != null;
}
async void ValidateRotationModelSignatureKeyPairForV2User(RotateUserAccountKeysData model, User user)
{
var currentSignatureKeyPair = await _userSignatureKeyPairRepository.GetByUserIdAsync(user.Id);
if (model.AccountKeys.SignatureKeyPairData == null)
{ {
throw new InvalidOperationException("The provided master password unlock data is not valid for this user."); throw new InvalidOperationException("The provided signing key data is null, but the user already has signing keys.");
} }
if (model.AccountKeys.SignatureKeyPairData.VerifyingKey != currentSignatureKeyPair.VerifyingKey)
{
throw new InvalidOperationException("The provided signing key data does not match the user's current signing key data.");
}
if (string.IsNullOrEmpty(model.AccountKeys.PublicKeyEncryptionKeyPairData.SignedPublicKey))
{
throw new InvalidOperationException("No signed public key provided, but the user already has a signature key pair.");
}
}
void ValidateRotationModelSignatureKeyPairForV1UserAndUpgradeToV2(RotateUserAccountKeysData model, User user, List<UpdateEncryptedDataForKeyRotation> saveEncryptedDataActions)
{
if (model.AccountKeys.SignatureKeyPairData != null)
{
// user is upgrading
if (string.IsNullOrEmpty(model.AccountKeys.SignatureKeyPairData.VerifyingKey))
{
throw new InvalidOperationException("The provided signing key data does not contain a valid verifying key.");
}
if (string.IsNullOrEmpty(model.AccountKeys.SignatureKeyPairData.WrappedSigningKey))
{
throw new InvalidOperationException("The provided signing key data does not contain a valid wrapped signing key.");
}
saveEncryptedDataActions.Add(_userSignatureKeyPairRepository.SetUserSignatureKeyPair(user.Id, model.AccountKeys.SignatureKeyPairData));
user.SignedPublicKey = model.AccountKeys.PublicKeyEncryptionKeyPairData.SignedPublicKey;
}
}
async void UpdateAccountKeys(RotateUserAccountKeysData model, User user, List<UpdateEncryptedDataForKeyRotation> saveEncryptedDataActions)
{
// Changing the public key encryption key pair is not supported during key rotation for now; so this ensures it is not accidentally changed
if (model.AccountKeys.PublicKeyEncryptionKeyPairData.PublicKey != user.PublicKey) if (model.AccountKeys.PublicKeyEncryptionKeyPairData.PublicKey != user.PublicKey)
{ {
throw new InvalidOperationException("The provided account public key does not match the user's current public key, and changing the account asymmetric keypair is currently not supported during key rotation."); throw new InvalidOperationException("The provided account public key does not match the user's current public key, and changing the account asymmetric keypair is currently not supported during key rotation.");
} }
// Private key is re-wrapped with new user key by client
user.PrivateKey = model.UserKeyEncryptedAccountPrivateKey;
if (currentSignatureKeyPair != null)
if (await IsUserV2UserAsync(user))
{ {
// user is already v2 user ValidateRotationModelSignatureKeyPairForV2User(model, user);
if (model.AccountKeys.SignatureKeyPairData == null)
{
throw new InvalidOperationException("The provided signing key data is null, but the user already has signing keys.");
}
if (model.AccountKeys.SignatureKeyPairData.VerifyingKey != currentSignatureKeyPair.VerifyingKey)
{
throw new InvalidOperationException("The provided signing key data does not match the user's current signing key data.");
}
if (string.IsNullOrEmpty(model.AccountKeys.PublicKeyEncryptionKeyPairData.SignedPublicKey))
{
throw new InvalidOperationException("No signed public key provided, but the user already has a signature key pair.");
}
} }
else else
{ {
if (model.AccountKeys.SignatureKeyPairData != null) ValidateRotationModelSignatureKeyPairForV1UserAndUpgradeToV2(model, user, saveEncryptedDataActions);
{
// user is upgrading
if (string.IsNullOrEmpty(model.AccountKeys.SignatureKeyPairData.VerifyingKey))
{
throw new InvalidOperationException("The provided signing key data does not contain a valid verifying key.");
}
if (string.IsNullOrEmpty(model.AccountKeys.SignatureKeyPairData.WrappedSigningKey))
{
throw new InvalidOperationException("The provided signing key data does not contain a valid wrapped signing key.");
}
saveEncryptedDataActions.Add(_userSignatureKeyPairRepository.SetUserSignatureKeyPair(user.Id, model.AccountKeys.SignatureKeyPairData));
user.SignedPublicKey = model.AccountKeys.PublicKeyEncryptionKeyPairData.SignedPublicKey;
}
} }
}
user.Key = model.MasterPasswordUnlockData.MasterKeyEncryptedUserKey; void UpdateUserData(RotateUserAccountKeysData model, User user, List<UpdateEncryptedDataForKeyRotation> saveEncryptedDataActions)
user.PrivateKey = model.UserKeyEncryptedAccountPrivateKey; {
user.MasterPassword = _passwordHasher.HashPassword(user, model.MasterPasswordUnlockData.MasterKeyAuthenticationHash);
user.MasterPasswordHint = model.MasterPasswordUnlockData.MasterPasswordHint;
if (model.Ciphers.Any()) if (model.Ciphers.Any())
{ {
saveEncryptedDataActions.Add(_cipherRepository.UpdateForKeyRotation(user.Id, model.Ciphers)); saveEncryptedDataActions.Add(_cipherRepository.UpdateForKeyRotation(user.Id, model.Ciphers));
@ -128,6 +154,18 @@ public class RotateUserAccountKeysCommand(
{ {
saveEncryptedDataActions.Add(_sendRepository.UpdateForKeyRotation(user.Id, model.Sends)); saveEncryptedDataActions.Add(_sendRepository.UpdateForKeyRotation(user.Id, model.Sends));
} }
}
void UpdateUnlockMethods(RotateUserAccountKeysData model, User user, List<UpdateEncryptedDataForKeyRotation> saveEncryptedDataActions)
{
if (!model.MasterPasswordUnlockData.ValidateForUser(user))
{
throw new InvalidOperationException("The provided master password unlock data is not valid for this user.");
}
// Update master password authentication & unlock
user.Key = model.MasterPasswordUnlockData.MasterKeyEncryptedUserKey;
user.MasterPassword = _passwordHasher.HashPassword(user, model.MasterPasswordUnlockData.MasterKeyAuthenticationHash);
user.MasterPasswordHint = model.MasterPasswordUnlockData.MasterPasswordHint;
if (model.EmergencyAccesses.Any()) if (model.EmergencyAccesses.Any())
{ {
@ -148,9 +186,5 @@ public class RotateUserAccountKeysCommand(
{ {
saveEncryptedDataActions.Add(_deviceRepository.UpdateKeysForRotationAsync(user.Id, model.DeviceKeys)); saveEncryptedDataActions.Add(_deviceRepository.UpdateKeysForRotationAsync(user.Id, model.DeviceKeys));
} }
await _userRepository.UpdateUserKeyAndEncryptedDataV2Async(user, saveEncryptedDataActions);
await _pushService.PushLogOutAsync(user.Id);
return IdentityResult.Success;
} }
} }