diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index 0ac7dbbcb4..92cafbfe7b 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -1,6 +1,7 @@ #nullable enable using Bit.Core.Entities; using Bit.Core.Enums; +using Bit.Core.KeyManagement.Models.Data; using Bit.Core.Utilities; namespace Bit.Core.Auth.Models.Api.Request.Accounts; @@ -21,18 +22,25 @@ public class RegisterFinishRequestModel : IValidatableObject public required string Email { get; set; } public string? EmailVerificationToken { get; set; } + public MasterPasswordAuthenticationData? MasterPasswordAuthenticationData { get; set; } + public MasterPasswordUnlockData? MasterPasswordUnlockData { get; set; } + + // PM-28143 - Made to be optional as migrating to MasterPasswordUnlockData [StringLength(1000)] - public required string MasterPasswordHash { get; set; } + public required string? MasterPasswordHash { get; set; } [StringLength(50)] public string? MasterPasswordHint { get; set; } - public required string UserSymmetricKey { get; set; } + // PM-28143 - Remove line below (made optional during migration to MasterPasswordUnlockData + public string? UserSymmetricKey { get; set; } public required KeysRequestModel UserAsymmetricKeys { get; set; } - public required KdfType Kdf { get; set; } - public required int KdfIterations { get; set; } + // PM-28143 - Remove line below (made optional during migration to MasterPasswordUnlockData + public KdfType? Kdf { get; set; } + // PM-28143 - Remove line below (made optional during migration to MasterPasswordUnlockData + public int? KdfIterations { get; set; } public int? KdfMemory { get; set; } public int? KdfParallelism { get; set; } @@ -54,11 +62,13 @@ public class RegisterFinishRequestModel : IValidatableObject { Email = Email, MasterPasswordHint = MasterPasswordHint, - Kdf = Kdf, - KdfIterations = KdfIterations, - KdfMemory = KdfMemory, - KdfParallelism = KdfParallelism, - Key = UserSymmetricKey, + Kdf = MasterPasswordUnlockData?.Kdf.KdfType ?? Kdf ?? throw new Exception($"{nameof(Kdf)} is required"), + KdfIterations = MasterPasswordUnlockData?.Kdf.Iterations ?? KdfIterations ?? throw new Exception($"{nameof(KdfIterations)} is required"), + KdfMemory = MasterPasswordUnlockData?.Kdf.Memory ?? KdfMemory, + KdfParallelism = MasterPasswordUnlockData?.Kdf.Parallelism ?? KdfParallelism, + // PM-28827 To be added when MasterPasswordSalt is added to the user column + // MasterPasswordSalt = MasterPasswordUnlockData?.Salt ?? Email.ToLower().Trim(), + Key = MasterPasswordUnlockData?.MasterKeyWrappedUserKey ?? UserSymmetricKey, }; UserAsymmetricKeys.ToUser(user); @@ -95,6 +105,15 @@ public class RegisterFinishRequestModel : IValidatableObject public IEnumerable Validate(ValidationContext validationContext) { - return KdfSettingsValidator.Validate(Kdf, KdfIterations, KdfMemory, KdfParallelism); + var kdf = MasterPasswordUnlockData?.Kdf.KdfType ?? Kdf ?? throw new Exception($"{nameof(Kdf)} not found on RequestModel"); + var kdfIterations = MasterPasswordUnlockData?.Kdf.Iterations ?? KdfIterations ?? throw new Exception($"{nameof(KdfIterations)} not found on RequestModel"); + var kdfMemory = MasterPasswordUnlockData?.Kdf.Memory ?? KdfMemory; + var kdfParallelism = MasterPasswordUnlockData?.Kdf.Parallelism ?? KdfParallelism; + + // PM-28143 - Remove line below in favor of using the unlock data. + return KdfSettingsValidator.Validate(kdf, kdfIterations, kdfMemory, kdfParallelism); + + // PM-28143 - Uncomment + // return KdfSettingsValidator.Validate(MasterPasswordUnlockData); } } diff --git a/src/Core/Entities/User.cs b/src/Core/Entities/User.cs index 1ca6606779..75f35b7b32 100644 --- a/src/Core/Entities/User.cs +++ b/src/Core/Entities/User.cs @@ -7,8 +7,6 @@ using Bit.Core.KeyManagement.Models.Data; using Bit.Core.Utilities; using Microsoft.AspNetCore.Identity; -#nullable enable - namespace Bit.Core.Entities; public class User : ITableObject, IStorableSubscriber, IRevisable, ITwoFactorProvidersUser @@ -51,7 +49,7 @@ public class User : ITableObject, IStorableSubscriber, IRevisable, ITwoFac public string? Key { get; set; } /// /// The raw public key, without a signature from the user's signature key. - /// + /// public string? PublicKey { get; set; } /// /// User key wrapped private key. @@ -102,6 +100,8 @@ public class User : ITableObject, IStorableSubscriber, IRevisable, ITwoFac public DateTime? LastKeyRotationDate { get; set; } public DateTime? LastEmailChangeDate { get; set; } public bool VerifyDevices { get; set; } = true; + // PM-28827 Uncomment below line. + // public string? MasterPasswordSalt { get; set; } public string GetMasterPasswordSalt() { diff --git a/src/Core/Utilities/KdfSettingsValidator.cs b/src/Core/Utilities/KdfSettingsValidator.cs index f89e8ddb66..34241eda9f 100644 --- a/src/Core/Utilities/KdfSettingsValidator.cs +++ b/src/Core/Utilities/KdfSettingsValidator.cs @@ -36,6 +36,33 @@ public static class KdfSettingsValidator } } + public static IEnumerable Validate(MasterPasswordUnlockData masterPasswordUnlockData) + { + switch (masterPasswordUnlockData.Kdf.KdfType) + { + case KdfType.PBKDF2_SHA256: + if (!AuthConstants.PBKDF2_ITERATIONS.InsideRange(masterPasswordUnlockData.Kdf.Iterations)) + { + yield return new ValidationResult($"KDF iterations must be between {AuthConstants.PBKDF2_ITERATIONS.Min} and {AuthConstants.PBKDF2_ITERATIONS.Max}."); + } + break; + case KdfType.Argon2id: + if (!AuthConstants.ARGON2_ITERATIONS.InsideRange(masterPasswordUnlockData.Kdf.Iterations)) + { + yield return new ValidationResult($"Argon2 iterations must be between {AuthConstants.ARGON2_ITERATIONS.Min} and {AuthConstants.ARGON2_ITERATIONS.Max}."); + } + else if (!masterPasswordUnlockData.Kdf.Memory.HasValue || !AuthConstants.ARGON2_MEMORY.InsideRange(masterPasswordUnlockData.Kdf.Memory.Value)) + { + yield return new ValidationResult($"Argon2 memory must be between {AuthConstants.ARGON2_MEMORY.Min}mb and {AuthConstants.ARGON2_MEMORY.Max}mb."); + } + else if (!masterPasswordUnlockData.Kdf.Parallelism.HasValue || !AuthConstants.ARGON2_PARALLELISM.InsideRange(masterPasswordUnlockData.Kdf.Parallelism.Value)) + { + yield return new ValidationResult($"Argon2 parallelism must be between {AuthConstants.ARGON2_PARALLELISM.Min} and {AuthConstants.ARGON2_PARALLELISM.Max}."); + } + break; + } + } + public static IEnumerable Validate(KdfSettings settings) { return Validate(settings.KdfType, settings.Iterations, settings.Memory, settings.Parallelism);