mirror of
https://github.com/bitwarden/mobile
synced 2025-12-15 07:43:37 +00:00
* [PM-5731] feat: implement get assertion params object * [PM-5731] feat: add first test * [PM-5731] feat: add rp mismatch test * [PM-5731] feat: ask for credentials when found * [PM-5731] feat: find discoverable credentials * [PM-5731] feat: add tests for successful UV requests * [PM-5731] feat: add user does not consent test * [PM-5731] feat: check for UV when reprompt is active * [PM-5731] fix: tests a bit, needed some additional "arrange" steps * [PM-5731] feat: add support for counter * [PM-5731] feat: implement assertion without signature * [PM-5732] feat: finish authenticator assertion implementation note: CryptoFunctionService still needs Sign implemenation * [PM-5731] chore: minor clean up * [PM-5731] feat: scaffold make credential * [PM-5731] feat: start implementing attestation * [PM-5731] feat: implement credential exclusion * [PM-5731] feat: add new credential confirmaiton * [PM-5731] feat: implement credential creation * [PM-5731] feat: add user verification checks * [PM-5731] feat: add unknown error handling * [PM-5731] chore: clean up unusued params * [PM-5731] feat: partial attestation implementation * [PM-5731] feat: implement key generation * [PM-5731] feat: return public key in DER format * [PM-5731] feat: implement signing * [PM-5731] feat: remove logging * [PM-5731] chore: use primary constructor * [PM-5731] chore: add Async to method names * [PM-5731] feat: add support for silent discoverability * [PM-5731] feat: add support for specifying user presence requirement * [PM-5731] feat: ensure unlocked vault * [PM-5731] chore: clean up and refactor assertion tests * [PM-5731] chore: clean up and refactor attestation tests * [PM-5731] chore: add user presence todo comment * [PM-5731] feat: scaffold fido2 client * PM-5731 Fix build updating discoverable flag * [PM-5731] fix: failing test * [PM-5731] feat: add sameOriginWithAncestor and user id length checks * [PM-5731] feat: add incomplete rpId verification * [PM-5731] chore: document uri helpers * [PM-5731] feat: implement fido2 client createCredential * [PM-5731] feat: implement credential assertion in client * fix wrong signature format (cherry picked from commita1c9ebf01f) * [PM-5731] fix: issues after cherry-pick * Fix incompatible GUID conversions (cherry picked from commitc801b2fc3a) * [PM-5731] chore: remove default constructor * [PM-5731] feat: refactor user interface to increase flexibility * [PM-5731] feat: implement generic assertion user interface class * [PM-5731] feat: remove ability to make user presence optional * [PM-5731] chore: remove logging comments * [PM-5731] feat: add native reprompt support to the authenticator * [PM-5731] feat: allow pre and post UV * [PM-5731] chore: add `Async` to method name. Remove `I` from struct * [PM-5731] fix: discoverable string repr lowercase * [PM-5731] chore: don't use C# 12 features * [PM-5731] fix: replace magic strings and numbers with contants and enums * [PM-5731] fix: use UTC creation date * [PM-5731] fix: formatting * [PM-5731] chore: use properties for public fields * [PM-5731] chore: remove TODO * [PM-5731] fix: IsValidRpId --------- Co-authored-by: Federico Maccaroni <fedemkr@gmail.com> Co-authored-by: mpbw2 <59324545+mpbw2@users.noreply.github.com>
125 lines
5.4 KiB
C#
125 lines
5.4 KiB
C#
using System;
|
|
using System.Threading.Tasks;
|
|
using Bit.Core.Abstractions;
|
|
using Bit.Core.Services;
|
|
using Bit.Core.Models.View;
|
|
using Bit.Core.Enums;
|
|
using Bit.Test.Common.AutoFixture;
|
|
using Bit.Test.Common.AutoFixture.Attributes;
|
|
using NSubstitute;
|
|
using Xunit;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using Bit.Core.Utilities;
|
|
|
|
namespace Bit.Core.Test.Services
|
|
{
|
|
public class Fido2AuthenticatorSilentCredentialDiscoveryTests
|
|
{
|
|
[Theory]
|
|
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) })]
|
|
public async Task SilentCredentialDiscoveryAsync_ReturnsEmptyArray_NoCredentialsExist(SutProvider<Fido2AuthenticatorService> sutProvider)
|
|
{
|
|
sutProvider.GetDependency<ICipherService>().GetAllDecryptedAsync().Returns([]);
|
|
|
|
var result = await sutProvider.Sut.SilentCredentialDiscoveryAsync("bitwarden.com");
|
|
|
|
Assert.Empty(result);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) })]
|
|
public async Task SilentCredentialDiscoveryAsync_ReturnsEmptyArray_OnlyNonDiscoverableCredentialsExist(SutProvider<Fido2AuthenticatorService> sutProvider)
|
|
{
|
|
sutProvider.GetDependency<ICipherService>().GetAllDecryptedAsync().Returns([
|
|
CreateCipherView("bitwarden.com", false),
|
|
CreateCipherView("bitwarden.com", false),
|
|
CreateCipherView("bitwarden.com", false)
|
|
]);
|
|
|
|
var result = await sutProvider.Sut.SilentCredentialDiscoveryAsync("bitwarden.com");
|
|
|
|
Assert.Empty(result);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) })]
|
|
public async Task SilentCredentialDiscoveryAsync_ReturnsEmptyArray_NoCredentialsWithMatchingRpIdExist(SutProvider<Fido2AuthenticatorService> sutProvider)
|
|
{
|
|
sutProvider.GetDependency<ICipherService>().GetAllDecryptedAsync().Returns([
|
|
CreateCipherView("a.bitwarden.com", true),
|
|
CreateCipherView("example.com", true)
|
|
]);
|
|
|
|
var result = await sutProvider.Sut.SilentCredentialDiscoveryAsync("bitwarden.com");
|
|
|
|
Assert.Empty(result);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) })]
|
|
public async Task SilentCredentialDiscoveryAsync_ReturnsCredentials_DiscoverableCredentialsWithMatchingRpIdExist(SutProvider<Fido2AuthenticatorService> sutProvider)
|
|
{
|
|
var matchingCredentials = new List<CipherView> {
|
|
CreateCipherView("bitwarden.com", true),
|
|
CreateCipherView("bitwarden.com", true)
|
|
};
|
|
var nonMatchingCredentials = new List<CipherView> {
|
|
CreateCipherView("example.com", true)
|
|
};
|
|
sutProvider.GetDependency<ICipherService>().GetAllDecryptedAsync().Returns(
|
|
matchingCredentials.Concat(nonMatchingCredentials).ToList()
|
|
);
|
|
|
|
var result = await sutProvider.Sut.SilentCredentialDiscoveryAsync("bitwarden.com");
|
|
|
|
Assert.True(
|
|
result.SequenceEqual(matchingCredentials.Select(c => new Fido2AuthenticatorDiscoverableCredentialMetadata {
|
|
Type = Constants.DefaultFido2CredentialType,
|
|
Id = c.Login.MainFido2Credential.CredentialId.GuidToRawFormat(),
|
|
RpId = "bitwarden.com",
|
|
UserHandle = c.Login.MainFido2Credential.UserHandleValue,
|
|
UserName = c.Login.MainFido2Credential.UserName
|
|
}), new MetadataComparer())
|
|
);
|
|
}
|
|
|
|
private byte[] RandomBytes(int length)
|
|
{
|
|
var bytes = new byte[length];
|
|
new Random().NextBytes(bytes);
|
|
return bytes;
|
|
}
|
|
|
|
#nullable enable
|
|
private CipherView CreateCipherView(string rpId, bool discoverable)
|
|
{
|
|
return new CipherView {
|
|
Type = CipherType.Login,
|
|
Id = Guid.NewGuid().ToString(),
|
|
Reprompt = CipherRepromptType.None,
|
|
Login = new LoginView {
|
|
Fido2Credentials = new List<Fido2CredentialView> {
|
|
new Fido2CredentialView {
|
|
CredentialId = Guid.NewGuid().ToString(),
|
|
RpId = rpId ?? "null.com",
|
|
DiscoverableValue = discoverable,
|
|
UserHandleValue = RandomBytes(32),
|
|
KeyValue = "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgO4wC7AlY4eJP7uedRUJGYsAIJAd6gN1Vp7uJh6xXAp6hRANCAARGvr56F_t27DEG1Tzl-qJRhrTUtC7jOEbasAEEZcE3TiMqoWCan0sxKDPylhRYk-1qyrBC_feN1UtGWH57sROa"
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
private class MetadataComparer : IEqualityComparer<Fido2AuthenticatorDiscoverableCredentialMetadata>
|
|
{
|
|
public int GetHashCode([DisallowNull] Fido2AuthenticatorDiscoverableCredentialMetadata obj) => throw new NotImplementedException();
|
|
|
|
public bool Equals(Fido2AuthenticatorDiscoverableCredentialMetadata? a, Fido2AuthenticatorDiscoverableCredentialMetadata? b) =>
|
|
a != null && b != null && a.Type == b.Type && a.RpId == b.RpId && a.UserName == b.UserName && a.Id.SequenceEqual(b.Id) && a.UserHandle.SequenceEqual(b.UserHandle);
|
|
}
|
|
}
|
|
}
|