diff --git a/src/Api/Controllers/TwoFactorController.cs b/src/Api/Controllers/TwoFactorController.cs index 9a02e2c937..e0828d217f 100644 --- a/src/Api/Controllers/TwoFactorController.cs +++ b/src/Api/Controllers/TwoFactorController.cs @@ -218,11 +218,16 @@ namespace Bit.Api.Controllers public async Task PutU2f([FromBody]TwoFactorU2fRequestModel model) { var user = await CheckAsync(model.MasterPasswordHash, true); - await _userService.CompleteU2fRegistrationAsync(user, model.Id.Value, model.Name, model.DeviceResponse); + var success = await _userService.CompleteU2fRegistrationAsync( + user, model.Id.Value, model.Name, model.DeviceResponse); + if(!success) + { + throw new BadRequestException("Unable to complete U2F key registration."); + } var response = new TwoFactorU2fResponseModel(user); return response; } - + [HttpDelete("u2f")] public async Task DeleteU2f([FromBody]TwoFactorU2fDeleteRequestModel model) { diff --git a/src/Core/Identity/U2fTokenProvider.cs b/src/Core/Identity/U2fTokenProvider.cs index 5ad3502e23..73feeafc4a 100644 --- a/src/Core/Identity/U2fTokenProvider.cs +++ b/src/Core/Identity/U2fTokenProvider.cs @@ -10,7 +10,6 @@ using System.Linq; using U2fLib = U2F.Core.Crypto.U2F; using U2F.Core.Models; using U2F.Core.Exceptions; -using U2F.Core.Utils; using System; using Bit.Core.Services; using Microsoft.Extensions.DependencyInjection; @@ -67,37 +66,44 @@ namespace Bit.Core.Identity await _u2fRepository.DeleteManyByUserIdAsync(user.Id); - var challengeBytes = U2fLib.Crypto.GenerateChallenge(); - var challenges = new List(); - foreach(var key in keys) + try { - var registration = new DeviceRegistration(key.Item2.KeyHandleBytes, key.Item2.PublicKeyBytes, - key.Item2.CertificateBytes, key.Item2.Counter); - var auth = U2fLib.StartAuthentication(Utilities.CoreHelpers.U2fAppIdUrl(_globalSettings), registration, - challengeBytes); - - // TODO: Maybe move this to a bulk create? - await _u2fRepository.CreateAsync(new U2f + var challengeBytes = U2fLib.Crypto.GenerateChallenge(); + var challenges = new List(); + foreach(var key in keys) { - AppId = auth.AppId, - Challenge = auth.Challenge, - KeyHandle = auth.KeyHandle, - Version = auth.Version, - UserId = user.Id, - CreationDate = DateTime.UtcNow - }); + var registration = new DeviceRegistration(key.Item2.KeyHandleBytes, key.Item2.PublicKeyBytes, + key.Item2.CertificateBytes, key.Item2.Counter); + var auth = U2fLib.StartAuthentication(Utilities.CoreHelpers.U2fAppIdUrl(_globalSettings), registration, + challengeBytes); - challenges.Add(new - { - appId = auth.AppId, - challenge = auth.Challenge, - keyHandle = auth.KeyHandle, - version = auth.Version - }); + // 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 + }); + + challenges.Add(new + { + appId = auth.AppId, + challenge = auth.Challenge, + keyHandle = auth.KeyHandle, + version = auth.Version + }); + } + + var token = JsonConvert.SerializeObject(challenges); + return token; + } + catch(U2fException) + { + return null; } - - var token = JsonConvert.SerializeObject(challenges); - return token; } public async Task ValidateAsync(string purpose, string token, UserManager manager, User user) diff --git a/src/Core/Services/Implementations/UserService.cs b/src/Core/Services/Implementations/UserService.cs index 9bd1e1a76c..44db54fc66 100644 --- a/src/Core/Services/Implementations/UserService.cs +++ b/src/Core/Services/Implementations/UserService.cs @@ -19,6 +19,7 @@ using Bit.Core.Utilities; using System.IO; using Newtonsoft.Json; using Microsoft.AspNetCore.DataProtection; +using U2F.Core.Exceptions; namespace Bit.Core.Services { @@ -322,60 +323,67 @@ namespace Bit.Core.Services var registerResponse = BaseModel.FromJson(deviceResponse); - var challenge = challenges.OrderBy(i => i.Id).Last(i => i.KeyHandle == null); - var startedReg = new StartedRegistration(challenge.Challenge, challenge.AppId); - var reg = U2fLib.FinishRegistration(startedReg, registerResponse); + try + { + var challenge = challenges.OrderBy(i => i.Id).Last(i => i.KeyHandle == null); + var startedReg = new StartedRegistration(challenge.Challenge, challenge.AppId); + var reg = U2fLib.FinishRegistration(startedReg, registerResponse); - await _u2fRepository.DeleteManyByUserIdAsync(user.Id); + await _u2fRepository.DeleteManyByUserIdAsync(user.Id); - // Add device - var providers = user.GetTwoFactorProviders(); - if(providers == null) - { - providers = new Dictionary(); - } - var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f); - if(provider == null) - { - provider = new TwoFactorProvider(); - } - if(provider.MetaData == null) - { - provider.MetaData = new Dictionary(); - } + // Add device + var providers = user.GetTwoFactorProviders(); + if(providers == null) + { + providers = new Dictionary(); + } + var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f); + if(provider == null) + { + provider = new TwoFactorProvider(); + } + if(provider.MetaData == null) + { + provider.MetaData = new Dictionary(); + } - if(provider.MetaData.Count >= 5) + if(provider.MetaData.Count >= 5) + { + // Can only register up to 5 keys + return false; + } + + var keyId = $"Key{id}"; + if(provider.MetaData.ContainsKey(keyId)) + { + provider.MetaData.Remove(keyId); + } + + provider.Enabled = true; + provider.MetaData.Add(keyId, new TwoFactorProvider.U2fMetaData + { + Name = name, + KeyHandle = reg.KeyHandle == null ? null : Utils.ByteArrayToBase64String(reg.KeyHandle), + PublicKey = reg.PublicKey == null ? null : Utils.ByteArrayToBase64String(reg.PublicKey), + Certificate = reg.AttestationCert == null ? null : Utils.ByteArrayToBase64String(reg.AttestationCert), + Compromised = false, + Counter = reg.Counter + }); + + if(providers.ContainsKey(TwoFactorProviderType.U2f)) + { + providers.Remove(TwoFactorProviderType.U2f); + } + + providers.Add(TwoFactorProviderType.U2f, provider); + user.SetTwoFactorProviders(providers); + await UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.U2f); + return true; + } + catch(U2fException) { - // Can only register up to 5 keys return false; } - - var keyId = $"Key{id}"; - if(provider.MetaData.ContainsKey(keyId)) - { - provider.MetaData.Remove(keyId); - } - - provider.Enabled = true; - provider.MetaData.Add(keyId, new TwoFactorProvider.U2fMetaData - { - Name = name, - KeyHandle = reg.KeyHandle == null ? null : Utils.ByteArrayToBase64String(reg.KeyHandle), - PublicKey = reg.PublicKey == null ? null : Utils.ByteArrayToBase64String(reg.PublicKey), - Certificate = reg.AttestationCert == null ? null : Utils.ByteArrayToBase64String(reg.AttestationCert), - Compromised = false, - Counter = reg.Counter - }); - - if(providers.ContainsKey(TwoFactorProviderType.U2f)) - { - providers.Remove(TwoFactorProviderType.U2f); - } - - providers.Add(TwoFactorProviderType.U2f, provider); - user.SetTwoFactorProviders(providers); - await UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.U2f); - return true; } public async Task DeleteU2fKeyAsync(User user, int id)