1
0
mirror of https://github.com/bitwarden/server.git synced 2025-05-28 23:04:50 -05:00

u2f token provider

This commit is contained in:
Kyle Spearrin 2017-06-22 09:09:51 -04:00
parent 731a1e31b9
commit 3ae96bd510
4 changed files with 184 additions and 6 deletions

View File

@ -0,0 +1,167 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Bit.Core.Models.Table;
using Bit.Core.Enums;
using Bit.Core.Models;
using Bit.Core.Services;
using Bit.Core.Repositories;
using U2F.Core.Models;
using U2fLib = U2F.Core.Crypto.U2F;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
using U2F.Core.Exceptions;
namespace Bit.Core.Identity
{
public class U2fTokenProvider : IUserTwoFactorTokenProvider<User>
{
private readonly IU2fRepository _u2fRepository;
private readonly IUserService _userService;
private readonly GlobalSettings _globalSettings;
public U2fTokenProvider(
IU2fRepository u2fRepository,
IUserService userService,
GlobalSettings globalSettings)
{
_u2fRepository = u2fRepository;
_userService = userService;
_globalSettings = globalSettings;
}
public Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
{
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
var canGenerate = user.TwoFactorProviderIsEnabled(TwoFactorProviderType.U2f) && HasProperMetaData(provider);
return Task.FromResult(canGenerate);
}
public async Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
{
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
if(!HasProperMetaData(provider))
{
return null;
}
var keys = new List<TwoFactorProvider.U2fMetaData>();
var key1 = provider.MetaData["Key1"] as TwoFactorProvider.U2fMetaData;
if(!key1?.Compromised ?? false)
{
keys.Add(key1);
}
if(keys.Count == 0)
{
return null;
}
await _u2fRepository.DeleteManyByUserIdAsync(user.Id);
var challenges = new List<object>();
foreach(var key in keys)
{
var registration = new DeviceRegistration(key.KeyHandleBytes, key.PublicKeyBytes,
key.CertificateBytes, key.Counter);
var auth = U2fLib.StartAuthentication(_globalSettings.U2f.AppId, registration);
// Maybe move this to a bulk create when we support more than 1 key?
await _u2fRepository.CreateAsync(new U2f
{
AppId = auth.AppId,
Challenge = auth.Challenge,
KeyHandle = auth.KeyHandle,
Version = auth.Version,
UserId = user.Id
});
challenges.Add(new
{
appId = auth.AppId,
challenge = auth.Challenge,
keyHandle = auth.KeyHandle,
version = auth.Version
});
}
var token = JsonConvert.SerializeObject(challenges);
return token;
}
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
{
if(string.IsNullOrWhiteSpace(token))
{
return false;
}
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
if(!HasProperMetaData(provider))
{
return false;
}
var keys = new List<TwoFactorProvider.U2fMetaData>();
var key1 = provider.MetaData["Key1"] as TwoFactorProvider.U2fMetaData;
if(!key1?.Compromised ?? false)
{
keys.Add(key1);
}
if(keys.Count == 0)
{
return false;
}
var authenticateResponse = BaseModel.FromJson<AuthenticateResponse>(token);
var key = keys.FirstOrDefault(f => f.KeyHandle == authenticateResponse.KeyHandle);
if(key == null)
{
return false;
}
var challenges = await _u2fRepository.GetManyByUserIdAsync(user.Id);
if(challenges.Count == 0)
{
return false;
}
var success = true;
// 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.First(c => c.KeyHandle == authenticateResponse.KeyHandle);
var registration = new DeviceRegistration(key.KeyHandleBytes, key.PublicKeyBytes, key.CertificateBytes,
key.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.Counter = registration.Counter;
key.Compromised = registration.IsCompromised;
var providers = user.GetTwoFactorProviders();
providers[TwoFactorProviderType.U2f].MetaData["Key1"] = key;
user.SetTwoFactorProviders(providers);
await _userService.SaveUserAsync(user);
return success;
}
private bool HasProperMetaData(TwoFactorProvider provider)
{
return provider?.MetaData != null && provider.MetaData.ContainsKey("Key1");
}
}
}

View File

@ -193,8 +193,10 @@ namespace Bit.Core.IdentityServer
} }
else if(type == TwoFactorProviderType.U2f) else if(type == TwoFactorProviderType.U2f)
{ {
// TODO: U2F challenge return new Dictionary<string, object>
return new Dictionary<string, object> { }; {
["Challenges"] = token
};
} }
return null; return null;
default: default:

View File

@ -1,4 +1,6 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using U2F.Core.Utils;
namespace Bit.Core.Models namespace Bit.Core.Models
{ {
@ -10,8 +12,14 @@ namespace Bit.Core.Models
public class U2fMetaData public class U2fMetaData
{ {
public string KeyHandle { get; set; } public string KeyHandle { get; set; }
public byte[] KeyHandleBytes =>
string.IsNullOrWhiteSpace(KeyHandle) ? null : Utils.Base64StringToByteArray(KeyHandle);
public string PublicKey { get; set; } public string PublicKey { get; set; }
public byte[] PublicKeyBytes =>
string.IsNullOrWhiteSpace(PublicKey) ? null : Utils.Base64StringToByteArray(PublicKey);
public string Certificate { get; set; } public string Certificate { get; set; }
public byte[] CertificateBytes =>
string.IsNullOrWhiteSpace(Certificate) ? null : Utils.Base64StringToByteArray(Certificate);
public uint Counter { get; set; } public uint Counter { get; set; }
public bool Compromised { get; set; } public bool Compromised { get; set; }
} }

View File

@ -14,6 +14,7 @@ using U2fLib = U2F.Core.Crypto.U2F;
using U2F.Core.Models; using U2F.Core.Models;
using Bit.Core.Models; using Bit.Core.Models;
using Bit.Core.Models.Business; using Bit.Core.Models.Business;
using U2F.Core.Utils;
namespace Bit.Core.Services namespace Bit.Core.Services
{ {
@ -273,9 +274,9 @@ namespace Bit.Core.Services
{ {
["Key1"] = new TwoFactorProvider.U2fMetaData ["Key1"] = new TwoFactorProvider.U2fMetaData
{ {
KeyHandle = reg.KeyHandle == null ? null : Convert.ToBase64String(reg.KeyHandle), KeyHandle = reg.KeyHandle == null ? null : Utils.ByteArrayToBase64String(reg.KeyHandle),
PublicKey = reg.PublicKey == null ? null : Convert.ToBase64String(reg.PublicKey), PublicKey = reg.PublicKey == null ? null : Utils.ByteArrayToBase64String(reg.PublicKey),
Certificate = reg.AttestationCert == null ? null : Convert.ToBase64String(reg.AttestationCert), Certificate = reg.AttestationCert == null ? null : Utils.ByteArrayToBase64String(reg.AttestationCert),
Compromised = false, Compromised = false,
Counter = reg.Counter Counter = reg.Counter
} }