1
0
mirror of https://github.com/bitwarden/server.git synced 2025-05-27 22:34:54 -05:00

Update opaque login with password and update cipherconfig model

This commit is contained in:
Bernd Schoolmann 2025-03-14 16:22:53 +01:00
parent 0b34f09fc7
commit d617004435
No known key found for this signature in database
7 changed files with 73 additions and 11 deletions

View File

@ -15,6 +15,7 @@ using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.Entities;
using Bit.Core.Auth.Models.Api.Request.Accounts;
using Bit.Core.Auth.Models.Data;
using Bit.Core.Auth.Services;
using Bit.Core.Auth.UserFeatures.TdeOffboardingPassword.Interfaces;
using Bit.Core.Auth.UserFeatures.UserMasterPassword.Interfaces;
using Bit.Core.Entities;
@ -57,6 +58,7 @@ public class AccountsController : Controller
_organizationUserValidator;
private readonly IRotationValidator<IEnumerable<WebAuthnLoginRotateKeyRequestModel>, IEnumerable<WebAuthnLoginRotateKeyData>>
_webauthnKeyValidator;
private readonly IOpaqueKeyExchangeService _opaqueKeyExchangeService;
public AccountsController(
@ -76,7 +78,8 @@ public class AccountsController : Controller
emergencyAccessValidator,
IRotationValidator<IEnumerable<ResetPasswordWithOrgIdRequestModel>, IReadOnlyList<OrganizationUser>>
organizationUserValidator,
IRotationValidator<IEnumerable<WebAuthnLoginRotateKeyRequestModel>, IEnumerable<WebAuthnLoginRotateKeyData>> webAuthnKeyValidator
IRotationValidator<IEnumerable<WebAuthnLoginRotateKeyRequestModel>, IEnumerable<WebAuthnLoginRotateKeyData>> webAuthnKeyValidator,
IOpaqueKeyExchangeService opaqueKeyExchangeService
)
{
_organizationService = organizationService;
@ -94,6 +97,7 @@ public class AccountsController : Controller
_emergencyAccessValidator = emergencyAccessValidator;
_organizationUserValidator = organizationUserValidator;
_webauthnKeyValidator = webAuthnKeyValidator;
_opaqueKeyExchangeService = opaqueKeyExchangeService;
}
@ -209,8 +213,14 @@ public class AccountsController : Controller
throw new UnauthorizedAccessException();
}
Guid? sessionId = null;
if (model.OpaqueSessionId != null)
{
sessionId = Guid.Parse(model.OpaqueSessionId);
}
var result = await _userService.ChangePasswordAsync(user, model.MasterPasswordHash,
model.NewMasterPasswordHash, model.MasterPasswordHint, model.Key);
model.NewMasterPasswordHash, model.MasterPasswordHint, model.Key, sessionId);
if (result.Succeeded)
{
return;

View File

@ -27,7 +27,7 @@ public class OpaqueKeyExchangeController : Controller
public async Task<OpaqueRegistrationStartResponse> StartRegistrationAsync([FromBody] OpaqueRegistrationStartRequest request)
{
var user = await _userService.GetUserByPrincipalAsync(User);
var result = await _opaqueKeyExchangeService.StartRegistration(Convert.FromBase64String(request.RegistrationRequest), user, request.CipherConfiguration);
var result = await _opaqueKeyExchangeService.StartRegistration(Convert.FromBase64String(request.RegistrationRequest), user, request.CipherConfiguration.ToNativeConfiguration());
return new OpaqueRegistrationStartResponse(result.Item1, Convert.ToBase64String(result.Item2));
}

View File

@ -11,4 +11,5 @@ public class PasswordRequestModel : SecretVerificationRequestModel
public string MasterPasswordHint { get; set; }
[Required]
public string Key { get; set; }
public string OpaqueSessionId { get; set; }
}

View File

@ -1,12 +1,50 @@
using System.ComponentModel.DataAnnotations;
using Bitwarden.OPAQUE;
namespace Bit.Api.Auth.Models.Request.Opaque;
public class OpaqueRegistrationStartRequest
{
[Required]
public String RegistrationRequest { get; set; }
public string RegistrationRequest { get; set; }
[Required]
public CipherConfiguration CipherConfiguration { get; set; }
}
public class CipherConfiguration
{
static string OpaqueKe3Ristretto3DHArgonSuite = "OPAQUE_3_RISTRETTO255_OPRF_RISTRETTO255_KEGROUP_3DH_KEX_ARGON2ID13_KSF";
[Required]
public string CipherSuite { get; set; }
public Argon2KsfParameters Argon2Parameters { get; set; }
public Bitwarden.OPAQUE.CipherConfiguration ToNativeConfiguration()
{
if (CipherSuite == OpaqueKe3Ristretto3DHArgonSuite)
{
return new Bitwarden.OPAQUE.CipherConfiguration
{
OprfCS = Bitwarden.OPAQUE.OprfCS.Ristretto255,
KeGroup = Bitwarden.OPAQUE.KeGroup.Ristretto255,
KeyExchange = Bitwarden.OPAQUE.KeyExchange.TripleDH,
KSF = new Bitwarden.OPAQUE.Argon2id(Argon2Parameters.iterations, Argon2Parameters.memory, Argon2Parameters.parallelism)
};
}
else
{
throw new Exception("Unsupported cipher suite");
}
}
}
public class Argon2KsfParameters
{
// Memory in KiB
[Required]
public int memory;
[Required]
public int iterations;
[Required]
public int parallelism;
}

View File

@ -37,9 +37,6 @@ public class OpaqueKeyExchangeService : IOpaqueKeyExchangeService
{
var registrationFinish = _bitwardenOpaque.FinishRegistration(cipherConfiguration, registrationUpload);
SessionStore.RegisterSessions[sessionId].serverRegistration = registrationFinish.serverRegistration;
// todo move to changepassword
SetActive(sessionId, user);
}
catch (Exception e)
{

View File

@ -44,7 +44,7 @@ public interface IUserService
Task InitiateEmailChangeAsync(User user, string newEmail);
Task<IdentityResult> ChangeEmailAsync(User user, string masterPassword, string newEmail, string newMasterPassword,
string token, string key);
Task<IdentityResult> ChangePasswordAsync(User user, string masterPassword, string newMasterPassword, string passwordHint, string key);
Task<IdentityResult> ChangePasswordAsync(User user, string masterPassword, string newMasterPassword, string passwordHint, string key, Guid? opaqueSessionId);
Task<IdentityResult> SetKeyConnectorKeyAsync(User user, string key, string orgIdentifier);
Task<IdentityResult> ConvertToKeyConnectorAsync(User user);
Task<IdentityResult> AdminResetPasswordAsync(OrganizationUserType type, Guid orgId, Guid id, string newMasterPassword, string key);

View File

@ -11,6 +11,7 @@ using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Auth.Services;
using Bit.Core.Billing.Models;
using Bit.Core.Billing.Models.Sales;
using Bit.Core.Billing.Services;
@ -78,6 +79,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
private readonly IRevokeNonCompliantOrganizationUserCommand _revokeNonCompliantOrganizationUserCommand;
private readonly IDistributedCache _distributedCache;
private readonly IOpaqueKeyExchangeService _opaqueKeyExchangeService;
public UserService(
IUserRepository userRepository,
@ -115,7 +117,8 @@ public class UserService : UserManager<User>, IUserService, IDisposable
IPremiumUserBillingService premiumUserBillingService,
IRemoveOrganizationUserCommand removeOrganizationUserCommand,
IRevokeNonCompliantOrganizationUserCommand revokeNonCompliantOrganizationUserCommand,
IDistributedCache distributedCache)
IDistributedCache distributedCache,
IOpaqueKeyExchangeService opaqueKeyExchangeService)
: base(
store,
optionsAccessor,
@ -159,6 +162,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
_removeOrganizationUserCommand = removeOrganizationUserCommand;
_revokeNonCompliantOrganizationUserCommand = revokeNonCompliantOrganizationUserCommand;
_distributedCache = distributedCache;
_opaqueKeyExchangeService = opaqueKeyExchangeService;
}
public Guid? GetProperUserId(ClaimsPrincipal principal)
@ -643,7 +647,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
}
public async Task<IdentityResult> ChangePasswordAsync(User user, string masterPassword, string newMasterPassword, string passwordHint,
string key)
string key, Guid? opaqueSessionId)
{
if (user == null)
{
@ -664,6 +668,14 @@ public class UserService : UserManager<User>, IUserService, IDisposable
user.Key = key;
user.MasterPasswordHint = passwordHint;
if (opaqueSessionId != null)
{
_opaqueKeyExchangeService.SetActive((Guid)opaqueSessionId, user);
}
else
{
_opaqueKeyExchangeService.Unenroll(user);
}
await _userRepository.ReplaceAsync(user);
await _eventService.LogUserEventAsync(user.Id, EventType.User_ChangedPassword);
await _pushService.PushLogOutAsync(user.Id, true);
@ -804,6 +816,8 @@ public class UserService : UserManager<User>, IUserService, IDisposable
user.ForcePasswordReset = true;
user.Key = key;
// TODO: Add support
_opaqueKeyExchangeService.Unenroll(user);
await _userRepository.ReplaceAsync(user);
await _mailService.SendAdminResetPasswordEmailAsync(user.Email, user.Name, org.DisplayName());
await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_AdminResetPassword);
@ -830,6 +844,8 @@ public class UserService : UserManager<User>, IUserService, IDisposable
user.Key = key;
user.MasterPasswordHint = hint;
// TODO: Add support
_opaqueKeyExchangeService.Unenroll(user);
await _userRepository.ReplaceAsync(user);
await _mailService.SendUpdatedTempPasswordEmailAsync(user.Email, user.Name);
await _eventService.LogUserEventAsync(user.Id, EventType.User_UpdatedTempPassword);