mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 23:52:50 -05:00
WebAuthn (#903)
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Table;
|
||||
using Fido2NetLib;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
@ -223,14 +224,14 @@ namespace Bit.Core.Models.Api
|
||||
}
|
||||
}
|
||||
|
||||
public class TwoFactorU2fRequestModel : TwoFactorU2fDeleteRequestModel
|
||||
public class TwoFactorWebAuthnRequestModel : TwoFactorWebAuthnDeleteRequestModel
|
||||
{
|
||||
[Required]
|
||||
public string DeviceResponse { get; set; }
|
||||
public AuthenticatorAttestationRawResponse DeviceResponse { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
public class TwoFactorU2fDeleteRequestModel : TwoFactorRequestModel, IValidatableObject
|
||||
public class TwoFactorWebAuthnDeleteRequestModel : TwoFactorRequestModel, IValidatableObject
|
||||
{
|
||||
[Required]
|
||||
public int? Id { get; set; }
|
||||
|
@ -1,59 +0,0 @@
|
||||
using System;
|
||||
using Bit.Core.Models.Table;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Enums;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Bit.Core.Models.Api
|
||||
{
|
||||
public class TwoFactorU2fResponseModel : ResponseModel
|
||||
{
|
||||
public TwoFactorU2fResponseModel(User user)
|
||||
: base("twoFactorU2f")
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
||||
Enabled = provider?.Enabled ?? false;
|
||||
Keys = provider?.MetaData?.Select(k => new KeyModel(k.Key,
|
||||
new TwoFactorProvider.U2fMetaData((dynamic)k.Value)));
|
||||
}
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
public IEnumerable<KeyModel> Keys { get; set; }
|
||||
|
||||
public class KeyModel
|
||||
{
|
||||
public KeyModel(string id, TwoFactorProvider.U2fMetaData data)
|
||||
{
|
||||
Name = data.Name;
|
||||
Id = Convert.ToInt32(id.Replace("Key", string.Empty));
|
||||
Compromised = data.Compromised;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public int Id { get; set; }
|
||||
public bool Compromised { get; set; }
|
||||
}
|
||||
|
||||
public class ChallengeModel
|
||||
{
|
||||
public ChallengeModel(User user, U2fRegistration registration)
|
||||
{
|
||||
UserId = user.Id.ToString();
|
||||
AppId = registration.AppId;
|
||||
Challenge = registration.Challenge;
|
||||
Version = registration.Version;
|
||||
}
|
||||
|
||||
public string UserId { get; set; }
|
||||
public string AppId { get; set; }
|
||||
public string Challenge { get; set; }
|
||||
public string Version { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using Bit.Core.Models.Table;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Enums;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Bit.Core.Models.Api
|
||||
{
|
||||
public class TwoFactorWebAuthnResponseModel : ResponseModel
|
||||
{
|
||||
public TwoFactorWebAuthnResponseModel(User user)
|
||||
: base("twoFactorWebAuthn")
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);
|
||||
Enabled = provider?.Enabled ?? false;
|
||||
Keys = provider?.MetaData?
|
||||
.Where(k => k.Key.StartsWith("Key"))
|
||||
.Select(k => new KeyModel(k.Key, new TwoFactorProvider.WebAuthnData((dynamic)k.Value)));
|
||||
}
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
public IEnumerable<KeyModel> Keys { get; set; }
|
||||
|
||||
public class KeyModel
|
||||
{
|
||||
public KeyModel(string id, TwoFactorProvider.WebAuthnData data)
|
||||
{
|
||||
Name = data.Name;
|
||||
Id = Convert.ToInt32(id.Replace("Key", string.Empty));
|
||||
Migrated = data.Migrated;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public int Id { get; set; }
|
||||
public bool Migrated { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
public class U2fRegistration
|
||||
{
|
||||
public string AppId { get; set; }
|
||||
public string Challenge { get; set; }
|
||||
public string Version { get; set; }
|
||||
}
|
||||
}
|
@ -1,6 +1,11 @@
|
||||
using Bit.Core.Enums;
|
||||
using Fido2NetLib.Objects;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using PeterO.Cbor;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using U2F.Core.Utils;
|
||||
|
||||
namespace Bit.Core.Models
|
||||
@ -39,6 +44,84 @@ namespace Bit.Core.Models
|
||||
string.IsNullOrWhiteSpace(Certificate) ? null : Utils.Base64StringToByteArray(Certificate);
|
||||
public uint Counter { get; set; }
|
||||
public bool Compromised { get; set; }
|
||||
|
||||
private static CBORObject CreatePublicKeyFromU2fRegistrationData(byte[] keyHandleData, byte[] publicKeyData)
|
||||
{
|
||||
var x = new byte[32];
|
||||
var y = new byte[32];
|
||||
Buffer.BlockCopy(publicKeyData, 1, x, 0, 32);
|
||||
Buffer.BlockCopy(publicKeyData, 33, y, 0, 32);
|
||||
|
||||
var point = new ECPoint
|
||||
{
|
||||
X = x,
|
||||
Y = y,
|
||||
};
|
||||
|
||||
var coseKey = CBORObject.NewMap();
|
||||
|
||||
coseKey.Add(COSE.KeyCommonParameter.KeyType, COSE.KeyType.EC2);
|
||||
coseKey.Add(COSE.KeyCommonParameter.Alg, -7);
|
||||
|
||||
coseKey.Add(COSE.KeyTypeParameter.Crv, COSE.EllipticCurve.P256);
|
||||
|
||||
coseKey.Add(COSE.KeyTypeParameter.X, point.X);
|
||||
coseKey.Add(COSE.KeyTypeParameter.Y, point.Y);
|
||||
|
||||
return coseKey;
|
||||
}
|
||||
|
||||
public WebAuthnData ToWebAuthnData()
|
||||
{
|
||||
return new WebAuthnData
|
||||
{
|
||||
Name = Name,
|
||||
Descriptor = new PublicKeyCredentialDescriptor
|
||||
{
|
||||
Id = KeyHandleBytes,
|
||||
Type = PublicKeyCredentialType.PublicKey
|
||||
},
|
||||
PublicKey = CreatePublicKeyFromU2fRegistrationData(KeyHandleBytes, PublicKeyBytes).EncodeToBytes(),
|
||||
SignatureCounter = Counter,
|
||||
Migrated = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class WebAuthnData
|
||||
{
|
||||
public WebAuthnData() { }
|
||||
|
||||
public WebAuthnData(dynamic o)
|
||||
{
|
||||
Name = o.Name;
|
||||
try
|
||||
{
|
||||
Descriptor = o.Descriptor;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Handle newtonsoft parsing
|
||||
Descriptor = JsonConvert.DeserializeObject<PublicKeyCredentialDescriptor>(o.Descriptor.ToString());
|
||||
}
|
||||
PublicKey = o.PublicKey;
|
||||
UserHandle = o.UserHandle;
|
||||
SignatureCounter = o.SignatureCounter;
|
||||
CredType = o.CredType;
|
||||
RegDate = o.RegDate;
|
||||
AaGuid = o.AaGuid;
|
||||
Migrated = o.Migrated;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public PublicKeyCredentialDescriptor Descriptor { get; internal set; }
|
||||
public byte[] PublicKey { get; internal set; }
|
||||
public byte[] UserHandle { get; internal set; }
|
||||
public uint SignatureCounter { get; set; }
|
||||
public string CredType { get; internal set; }
|
||||
public DateTime RegDate { get; internal set; }
|
||||
public Guid AaGuid { get; internal set; }
|
||||
public bool Migrated { get; internal set; }
|
||||
}
|
||||
|
||||
public static bool RequiresPremium(TwoFactorProviderType type)
|
||||
|
Reference in New Issue
Block a user