mirror of
https://github.com/bitwarden/server.git
synced 2025-06-20 02:48:03 -05:00
Add migration to user encryption v2
This commit is contained in:
parent
68237d0b6d
commit
ec955565e6
@ -108,6 +108,7 @@ public class AccountsKeyManagementController : Controller
|
||||
|
||||
UserKeyEncryptedAccountPrivateKey = model.AccountKeys.UserKeyEncryptedAccountPrivateKey,
|
||||
AccountPublicKey = model.AccountKeys.AccountPublicKey,
|
||||
AccountKeys = model.AccountKeys,
|
||||
|
||||
MasterPasswordUnlockData = model.AccountUnlockData.MasterPasswordUnlockData.ToUnlockData(),
|
||||
EmergencyAccesses = await _emergencyAccessValidator.ValidateAsync(user, model.AccountUnlockData.EmergencyAccessUnlockData),
|
||||
|
@ -1,4 +1,6 @@
|
||||
#nullable enable
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.KeyManagement.Models.Data.Models;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Api.KeyManagement.Models.Requests;
|
||||
@ -7,4 +9,31 @@ public class AccountKeysRequestModel
|
||||
{
|
||||
[EncryptedString] public required string UserKeyEncryptedAccountPrivateKey { get; set; }
|
||||
public required string AccountPublicKey { get; set; }
|
||||
|
||||
public PublicKeyEncryptionKeyPairRequestModel? PublicKeyEncryptionKeyPair { get; set; }
|
||||
public SignatureKeyPairRequestModel? SignatureKeyPair { get; set; }
|
||||
|
||||
public UserAccountKeysData ToAccountKeysData()
|
||||
{
|
||||
// This will be cleaned up, after a compatibility period, at which point PublicKeyEncryptionKeyPair and SignatureKeyPair will be required.
|
||||
if (PublicKeyEncryptionKeyPair == null || SignatureKeyPair == null)
|
||||
{
|
||||
return new UserAccountKeysData
|
||||
{
|
||||
PublicKeyEncryptionKeyPairData = new PublicKeyEncryptionKeyPairData
|
||||
(
|
||||
UserKeyEncryptedAccountPrivateKey,
|
||||
AccountPublicKey
|
||||
),
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return new UserAccountKeysData
|
||||
{
|
||||
PublicKeyEncryptionKeyPairData = PublicKeyEncryptionKeyPair.ToPublicKeyEncryptionKeyPairData(),
|
||||
SignatureKeyPairData = SignatureKeyPair.ToSignatureKeyPairData()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
#nullable enable
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Api.KeyManagement.Models.Requests;
|
||||
|
||||
public class PrivateEncryptionKeyPairRequestModel
|
||||
{
|
||||
[EncryptedString] public required string WrappedPrivateKey { get; set; }
|
||||
public required string PublicKey { get; set; }
|
||||
public required string SignedPublicKey { get; set; }
|
||||
|
||||
public PublicKeyEncryptionKeyPairData ToPrivateKeyEncryptionKeyPairData()
|
||||
{
|
||||
return new PublicKeyEncryptionKeyPairData(
|
||||
WrappedPrivateKey,
|
||||
PublicKey,
|
||||
SignedPublicKey
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
#nullable enable
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Api.KeyManagement.Models.Requests;
|
||||
|
||||
public class PublicKeyEncryptionKeyPairRequestModel
|
||||
{
|
||||
[EncryptedString] public required string WrappedPrivateKey { get; set; }
|
||||
public required string PublicKey { get; set; }
|
||||
public required string SignedPublicKey { get; set; }
|
||||
|
||||
public PublicKeyEncryptionKeyPairData ToPublicKeyEncryptionKeyPairData()
|
||||
{
|
||||
return new PublicKeyEncryptionKeyPairData(
|
||||
WrappedPrivateKey,
|
||||
PublicKey,
|
||||
SignedPublicKey
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
#nullable enable
|
||||
using Bit.Core.KeyManagement.Enums;
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Api.KeyManagement.Models.Requests;
|
||||
|
||||
public class SignatureKeyPairRequestModel
|
||||
{
|
||||
public required SignatureAlgorithm SignatureAlgorithm { get; set; }
|
||||
[EncryptedString] public required string WrappedSigningKey { get; set; }
|
||||
public required string VerifyingKey { get; set; }
|
||||
|
||||
public SignatureKeyPairData ToSignatureKeyPairData()
|
||||
{
|
||||
return new SignatureKeyPairData(
|
||||
SignatureAlgorithm,
|
||||
WrappedSigningKey,
|
||||
VerifyingKey
|
||||
);
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Auth.Models.Data;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.KeyManagement.Models.Data.Models;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
|
||||
@ -12,9 +13,13 @@ public class RotateUserAccountKeysData
|
||||
public string OldMasterKeyAuthenticationHash { get; set; }
|
||||
|
||||
// Other keys encrypted by the userkey
|
||||
[Obsolete("Use AccountKeys instead")]
|
||||
public string UserKeyEncryptedAccountPrivateKey { get; set; }
|
||||
[Obsolete("Use AccountKeys instead")]
|
||||
public string AccountPublicKey { get; set; }
|
||||
|
||||
public UserAccountKeysData AccountKeys { get; set; }
|
||||
|
||||
// All methods to get to the userkey
|
||||
public MasterPasswordUnlockData MasterPasswordUnlockData { get; set; }
|
||||
public IEnumerable<EmergencyAccess> EmergencyAccesses { get; set; }
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace Bit.Core.KeyManagement.Models.Data;
|
||||
namespace Bit.Core.KeyManagement.Models.Data.Models;
|
||||
|
||||
#nullable enable
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
using Bit.Core.Auth.Repositories;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.KeyManagement.Repositories;
|
||||
using Bit.Core.Platform.Push;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
@ -26,6 +27,7 @@ namespace Bit.Core.KeyManagement.UserKey.Implementations;
|
||||
/// <param name="_pushService">Logs out user from other devices after successful rotation</param>
|
||||
/// <param name="_identityErrorDescriber">Provides a password mismatch error if master password hash validation fails</param>
|
||||
/// <param name="_credentialRepository">Provides a method to update re-encrypted WebAuthn keys</param>
|
||||
/// <param name="_userSignatureKeyPairRepository">Provides a method to update re-encrypted signature keys</param>
|
||||
public class RotateUserAccountKeysCommand(
|
||||
IUserService _userService,
|
||||
IUserRepository _userRepository,
|
||||
@ -38,7 +40,8 @@ public class RotateUserAccountKeysCommand(
|
||||
IPasswordHasher<User> _passwordHasher,
|
||||
IPushNotificationService _pushService,
|
||||
IdentityErrorDescriber _identityErrorDescriber,
|
||||
IWebAuthnCredentialRepository _credentialRepository
|
||||
IWebAuthnCredentialRepository _credentialRepository,
|
||||
IUserSignatureKeyPairRepository _userSignatureKeyPairRepository
|
||||
) : IRotateUserAccountKeysCommand
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@ -58,17 +61,54 @@ public class RotateUserAccountKeysCommand(
|
||||
user.RevisionDate = user.AccountRevisionDate = now;
|
||||
user.LastKeyRotationDate = now;
|
||||
user.SecurityStamp = Guid.NewGuid().ToString();
|
||||
List<UpdateEncryptedDataForKeyRotation> saveEncryptedDataActions = new();
|
||||
var currentSignatureKeyPair = await _userSignatureKeyPairRepository.GetByUserIdAsync(user.Id);
|
||||
List<UpdateEncryptedDataForKeyRotation> saveEncryptedDataActions = [];
|
||||
|
||||
if (!model.MasterPasswordUnlockData.ValidateForUser(user))
|
||||
{
|
||||
throw new InvalidOperationException("The provided master password unlock data is not valid for this user.");
|
||||
}
|
||||
if (model.AccountPublicKey != 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.");
|
||||
}
|
||||
|
||||
if (currentSignatureKeyPair != null)
|
||||
{
|
||||
// user is already v2 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
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
user.Key = model.MasterPasswordUnlockData.MasterKeyEncryptedUserKey;
|
||||
user.PrivateKey = model.UserKeyEncryptedAccountPrivateKey;
|
||||
user.MasterPassword = _passwordHasher.HashPassword(user, model.MasterPasswordUnlockData.MasterKeyAuthenticationHash);
|
||||
|
Loading…
x
Reference in New Issue
Block a user