1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 07:36:14 -05:00

[PM-15608] Create more KDF defaults for prelogin (#5122)

* kdf defaults on null map to email hash

* cleanup code. add some randomness as well

* remove null check

* fix test

* move to private method

* remove random options

* tests for random defaults

* SetDefaultKdfHmacKey for old test
This commit is contained in:
Kyle Spearrin
2025-01-10 15:54:53 -05:00
committed by GitHub
parent 730f83b425
commit aa0b35a345
3 changed files with 132 additions and 9 deletions

View File

@ -82,6 +82,7 @@ public class GlobalSettings : IGlobalSettings
public virtual ILaunchDarklySettings LaunchDarkly { get; set; } = new LaunchDarklySettings();
public virtual string DevelopmentDirectory { get; set; }
public virtual bool EnableEmailVerification { get; set; }
public virtual string KdfDefaultHashKey { get; set; }
public virtual string PricingUri { get; set; }
public string BuildExternalUri(string explicitValue, string name)

View File

@ -1,4 +1,5 @@
using System.Diagnostics;
using System.Text;
using Bit.Core;
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models.Api.Request.Accounts;
@ -15,6 +16,7 @@ using Bit.Core.Exceptions;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tokens;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
@ -44,6 +46,41 @@ public class AccountsController : Controller
private readonly IFeatureService _featureService;
private readonly IDataProtectorTokenFactory<RegistrationEmailVerificationTokenable> _registrationEmailVerificationTokenDataFactory;
private readonly byte[] _defaultKdfHmacKey = null;
private static readonly List<UserKdfInformation> _defaultKdfResults =
[
// The first result (index 0) should always return the "normal" default.
new()
{
Kdf = KdfType.PBKDF2_SHA256,
KdfIterations = AuthConstants.PBKDF2_ITERATIONS.Default,
},
// We want more weight for this default, so add it again
new()
{
Kdf = KdfType.PBKDF2_SHA256,
KdfIterations = AuthConstants.PBKDF2_ITERATIONS.Default,
},
// Add some other possible defaults...
new()
{
Kdf = KdfType.PBKDF2_SHA256,
KdfIterations = 100_000,
},
new()
{
Kdf = KdfType.PBKDF2_SHA256,
KdfIterations = 5_000,
},
new()
{
Kdf = KdfType.Argon2id,
KdfIterations = AuthConstants.ARGON2_ITERATIONS.Default,
KdfMemory = AuthConstants.ARGON2_MEMORY.Default,
KdfParallelism = AuthConstants.ARGON2_PARALLELISM.Default,
}
];
public AccountsController(
ICurrentContext currentContext,
ILogger<AccountsController> logger,
@ -55,7 +92,8 @@ public class AccountsController : Controller
ISendVerificationEmailForRegistrationCommand sendVerificationEmailForRegistrationCommand,
IReferenceEventService referenceEventService,
IFeatureService featureService,
IDataProtectorTokenFactory<RegistrationEmailVerificationTokenable> registrationEmailVerificationTokenDataFactory
IDataProtectorTokenFactory<RegistrationEmailVerificationTokenable> registrationEmailVerificationTokenDataFactory,
GlobalSettings globalSettings
)
{
_currentContext = currentContext;
@ -69,6 +107,11 @@ public class AccountsController : Controller
_referenceEventService = referenceEventService;
_featureService = featureService;
_registrationEmailVerificationTokenDataFactory = registrationEmailVerificationTokenDataFactory;
if (CoreHelpers.SettingHasValue(globalSettings.KdfDefaultHashKey))
{
_defaultKdfHmacKey = Encoding.UTF8.GetBytes(globalSettings.KdfDefaultHashKey);
}
}
[HttpPost("register")]
@ -217,11 +260,7 @@ public class AccountsController : Controller
var kdfInformation = await _userRepository.GetKdfInformationByEmailAsync(model.Email);
if (kdfInformation == null)
{
kdfInformation = new UserKdfInformation
{
Kdf = KdfType.PBKDF2_SHA256,
KdfIterations = AuthConstants.PBKDF2_ITERATIONS.Default,
};
kdfInformation = GetDefaultKdf(model.Email);
}
return new PreloginResponseModel(kdfInformation);
}
@ -240,4 +279,26 @@ public class AccountsController : Controller
Token = token
};
}
private UserKdfInformation GetDefaultKdf(string email)
{
if (_defaultKdfHmacKey == null)
{
return _defaultKdfResults[0];
}
else
{
// Compute the HMAC hash of the email
var hmacMessage = Encoding.UTF8.GetBytes(email.Trim().ToLowerInvariant());
using var hmac = new System.Security.Cryptography.HMACSHA256(_defaultKdfHmacKey);
var hmacHash = hmac.ComputeHash(hmacMessage);
// Convert the hash to a number
var hashHex = BitConverter.ToString(hmacHash).Replace("-", string.Empty).ToLowerInvariant();
var hashFirst8Bytes = hashHex.Substring(0, 16);
var hashNumber = long.Parse(hashFirst8Bytes, System.Globalization.NumberStyles.HexNumber);
// Find the default KDF value for this hash number
var hashIndex = (int)(Math.Abs(hashNumber) % _defaultKdfResults.Count);
return _defaultKdfResults[hashIndex];
}
}
}