mirror of
https://github.com/bitwarden/server
synced 2025-12-27 13:43:18 +00:00
Remove the u2f lib (#1820)
This commit is contained in:
@@ -1,221 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using U2F.Core.Exceptions;
|
||||
using U2F.Core.Models;
|
||||
using U2F.Core.Utils;
|
||||
using U2fLib = U2F.Core.Crypto.U2F;
|
||||
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
public class U2fTokenProvider : IUserTwoFactorTokenProvider<User>
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IU2fRepository _u2fRepository;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public U2fTokenProvider(
|
||||
IServiceProvider serviceProvider,
|
||||
IU2fRepository u2fRepository,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_u2fRepository = u2fRepository;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
||||
{
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
||||
if (!HasProperMetaData(provider))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return await userService.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.U2f, user);
|
||||
}
|
||||
|
||||
public async Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
||||
{
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
||||
var keys = LoadKeys(provider);
|
||||
if (keys.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
await _u2fRepository.DeleteManyByUserIdAsync(user.Id);
|
||||
|
||||
try
|
||||
{
|
||||
var challengeBytes = U2fLib.Crypto.GenerateChallenge();
|
||||
var appId = Utilities.CoreHelpers.U2fAppIdUrl(_globalSettings);
|
||||
var oldChallenges = new List<object>();
|
||||
var challengeKeys = new List<object>();
|
||||
foreach (var key in keys)
|
||||
{
|
||||
var registration = new DeviceRegistration(key.Item2.KeyHandleBytes, key.Item2.PublicKeyBytes,
|
||||
key.Item2.CertificateBytes, key.Item2.Counter);
|
||||
var auth = U2fLib.StartAuthentication(appId, registration, challengeBytes);
|
||||
|
||||
// TODO: Maybe move this to a bulk create?
|
||||
await _u2fRepository.CreateAsync(new U2f
|
||||
{
|
||||
AppId = auth.AppId,
|
||||
Challenge = auth.Challenge,
|
||||
KeyHandle = auth.KeyHandle,
|
||||
Version = auth.Version,
|
||||
UserId = user.Id,
|
||||
CreationDate = DateTime.UtcNow
|
||||
});
|
||||
|
||||
challengeKeys.Add(new
|
||||
{
|
||||
keyHandle = auth.KeyHandle,
|
||||
version = auth.Version
|
||||
});
|
||||
|
||||
// TODO: Old challenges array is here for backwards compat. Remove in the future.
|
||||
oldChallenges.Add(new
|
||||
{
|
||||
appId = auth.AppId,
|
||||
challenge = auth.Challenge,
|
||||
keyHandle = auth.KeyHandle,
|
||||
version = auth.Version
|
||||
});
|
||||
}
|
||||
|
||||
var oldToken = JsonSerializer.Serialize(oldChallenges);
|
||||
var token = JsonSerializer.Serialize(new
|
||||
{
|
||||
appId = appId,
|
||||
challenge = challengeBytes.ByteArrayToBase64String(),
|
||||
keys = challengeKeys
|
||||
});
|
||||
return $"{token}|{oldToken}";
|
||||
}
|
||||
catch (U2fException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
||||
{
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)) || string.IsNullOrWhiteSpace(token))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
||||
var keys = LoadKeys(provider);
|
||||
if (keys.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var authenticateResponse = BaseModel.FromJson<AuthenticateResponse>(token);
|
||||
var key = keys.FirstOrDefault(f => f.Item2.KeyHandle == authenticateResponse.KeyHandle);
|
||||
|
||||
if (key == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var challenges = await _u2fRepository.GetManyByUserIdAsync(user.Id);
|
||||
if (challenges.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// User will have a authentication request for each device they have registered so get the one that matches
|
||||
// the device key handle
|
||||
var challenge = challenges.FirstOrDefault(c => c.KeyHandle == authenticateResponse.KeyHandle);
|
||||
if (challenge == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var success = true;
|
||||
var registration = new DeviceRegistration(key.Item2.KeyHandleBytes, key.Item2.PublicKeyBytes,
|
||||
key.Item2.CertificateBytes, key.Item2.Counter);
|
||||
try
|
||||
{
|
||||
var auth = new StartedAuthentication(challenge.Challenge, challenge.AppId, challenge.KeyHandle);
|
||||
U2fLib.FinishAuthentication(auth, authenticateResponse, registration);
|
||||
}
|
||||
catch (U2fException)
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
|
||||
// Update database
|
||||
await _u2fRepository.DeleteManyByUserIdAsync(user.Id);
|
||||
key.Item2.Counter = registration.Counter;
|
||||
if (key.Item2.Counter > 0)
|
||||
{
|
||||
key.Item2.Compromised = registration.IsCompromised;
|
||||
}
|
||||
|
||||
var providers = user.GetTwoFactorProviders();
|
||||
providers[TwoFactorProviderType.U2f].MetaData[key.Item1] = key.Item2;
|
||||
user.SetTwoFactorProviders(providers);
|
||||
await manager.UpdateAsync(user);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private bool HasProperMetaData(TwoFactorProvider provider)
|
||||
{
|
||||
return (provider?.MetaData?.Count ?? 0) > 0;
|
||||
}
|
||||
|
||||
private List<Tuple<string, TwoFactorProvider.U2fMetaData>> LoadKeys(TwoFactorProvider provider)
|
||||
{
|
||||
var keys = new List<Tuple<string, TwoFactorProvider.U2fMetaData>>();
|
||||
if (!HasProperMetaData(provider))
|
||||
{
|
||||
return keys;
|
||||
}
|
||||
|
||||
// Support up to 5 keys
|
||||
for (var i = 1; i <= 5; i++)
|
||||
{
|
||||
var keyName = $"Key{i}";
|
||||
if (provider.MetaData.ContainsKey(keyName))
|
||||
{
|
||||
var key = new TwoFactorProvider.U2fMetaData((dynamic)provider.MetaData[keyName]);
|
||||
if (!key?.Compromised ?? false)
|
||||
{
|
||||
keys.Add(new Tuple<string, TwoFactorProvider.U2fMetaData>(keyName, key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user