diff --git a/src/Core/Abstractions/IFido2UserInterface.cs b/src/Core/Abstractions/IFido2UserInterface.cs
index f3476cff3..d5707c8c1 100644
--- a/src/Core/Abstractions/IFido2UserInterface.cs
+++ b/src/Core/Abstractions/IFido2UserInterface.cs
@@ -34,6 +34,37 @@ namespace Bit.Core.Abstractions
public bool UserVerified { get; set; }
}
+ public struct Fido2ConfirmNewCredentialParams
+ {
+ ///
+ /// The name of the credential.
+ ///
+ public string CredentialName { get; set; }
+
+ ///
+ /// The name of the user.
+ ///
+ public string UserName { get; set; }
+
+ ///
+ /// Whether or not the user must be verified before completing the operation.
+ ///
+ public bool UserVerification { get; set; }
+ }
+
+ public struct Fido2ConfirmNewCredentialResult
+ {
+ ///
+ /// The name of the user.
+ ///
+ public string CipherId { get; set; }
+
+ ///
+ /// Whether or not the user was verified.
+ ///
+ public bool UserVerified { get; set; }
+ }
+
public interface IFido2UserInterface
{
///
@@ -49,5 +80,12 @@ namespace Bit.Core.Abstractions
/// The IDs of the excluded credentials.
/// When user has confirmed the message
Task InformExcludedCredential(string[] existingCipherIds);
+
+ ///
+ /// Ask the user to confirm the creation of a new credential.
+ ///
+ /// The parameters to use when asking the user to confirm the creation of a new credential.
+ /// The ID of the cipher where the new credential should be saved.
+ Task ConfirmNewCredentialAsync(Fido2ConfirmNewCredentialParams confirmNewCredentialParams);
}
}
diff --git a/src/Core/Services/Fido2AuthenticatorService.cs b/src/Core/Services/Fido2AuthenticatorService.cs
index f2deb4376..84ba74aa4 100644
--- a/src/Core/Services/Fido2AuthenticatorService.cs
+++ b/src/Core/Services/Fido2AuthenticatorService.cs
@@ -48,7 +48,20 @@ namespace Bit.Core.Services
throw new NotAllowedError();
}
- throw new NotImplementedException();
+ var response = await _userInterface.ConfirmNewCredentialAsync(new Fido2ConfirmNewCredentialParams {
+ CredentialName = makeCredentialParams.RpEntity.Name,
+ UserName = makeCredentialParams.UserEntity.Name,
+ UserVerification = makeCredentialParams.RequireUserVerification
+ });
+
+ return new Fido2AuthenticatorMakeCredentialResult
+ {
+ CredentialId = GuidToRawFormat(Guid.NewGuid().ToString()),
+ AttestationObject = Array.Empty(),
+ AuthData = Array.Empty(),
+ PublicKey = Array.Empty(),
+ PublicKeyAlgorithm = (int) Fido2AlgorithmIdentifier.ES256,
+ };
}
public async Task GetAssertionAsync(Fido2AuthenticatorGetAssertionParams assertionParams)
diff --git a/src/Core/Utilities/Fido2/Fido2AuthenticatorMakeCredentialParams.cs b/src/Core/Utilities/Fido2/Fido2AuthenticatorMakeCredentialParams.cs
index f2b1e5186..59b070905 100644
--- a/src/Core/Utilities/Fido2/Fido2AuthenticatorMakeCredentialParams.cs
+++ b/src/Core/Utilities/Fido2/Fido2AuthenticatorMakeCredentialParams.cs
@@ -2,44 +2,44 @@ namespace Bit.Core.Utilities.Fido2
{
public class Fido2AuthenticatorMakeCredentialParams
{
- ///
- /// The caller’s RP ID, as determined by the user agent and the client. */
- ///
- public string RpId { get; set; }
+ ///
+ /// The Relying Party's PublicKeyCredentialRpEntity.
+ ///
+ public PublicKeyCredentialRpEntity RpEntity { get; set; }
- ///
- /// The Relying Party's PublicKeyCredentialRpEntity. */
- ///
+ ///
+ /// The Relying Party's PublicKeyCredentialRpEntity.
+ ///
public PublicKeyCredentialUserEntity UserEntity { get; set; }
- ///
- /// The hash of the serialized client data, provided by the client. */
- ///
+ ///
+ /// The hash of the serialized client data, provided by the client.
+ ///
public byte[] Hash { get; set; }
- ///
- /// A sequence of pairs of PublicKeyCredentialType and public key algorithms (COSEAlgorithmIdentifier) requested by the Relying Party. This sequence is ordered from most preferred to least preferred. The authenticator makes a best-effort to create the most preferred credential that it can. */
- ///
+ ///
+ /// A sequence of pairs of PublicKeyCredentialType and public key algorithms (COSEAlgorithmIdentifier) requested by the Relying Party. This sequence is ordered from most preferred to least preferred. The authenticator makes a best-effort to create the most preferred credential that it can.
+ ///
public PublicKeyCredentialAlgorithmDescriptor[] CredTypesAndPubKeyAlgs { get; set; }
- ///
+ ///
///An OPTIONAL list of PublicKeyCredentialDescriptor objects provided by the Relying Party with the intention that, if any of these are known to the authenticator, it SHOULD NOT create a new credential. excludeCredentialDescriptorList contains a list of known credentials.
- ///
+ ///
public PublicKeyCredentialDescriptor[] ExcludeCredentialDescriptorList { get; set; }
- ///
+ ///
/// The effective resident key requirement for credential creation, a Boolean value determined by the client. Resident is synonymous with discoverable. */
- ///
+ ///
public bool RequireResidentKey { get; set; }
- ///
- /// The effective user verification requirement for assertion, a Boolean value provided by the client. */
- ///
+ ///
+ /// The effective user verification requirement for assertion, a Boolean value provided by the client.
+ ///
public bool RequireUserVerification { get; set; }
- ///
- /// CTAP2 authenticators support setting this to false, but we only support the WebAuthn authenticator model which does not have that option. */
- ///
+ ///
+ /// CTAP2 authenticators support setting this to false, but we only support the WebAuthn authenticator model which does not have that option.
+ ///
// public bool RequireUserPresence { get; set; } // Always required
///
diff --git a/src/Core/Utilities/Fido2/PublicKeyCredentialRpEntity.cs b/src/Core/Utilities/Fido2/PublicKeyCredentialRpEntity.cs
new file mode 100644
index 000000000..abbf52cb0
--- /dev/null
+++ b/src/Core/Utilities/Fido2/PublicKeyCredentialRpEntity.cs
@@ -0,0 +1,9 @@
+namespace Bit.Core.Utilities.Fido2
+{
+ public class PublicKeyCredentialRpEntity
+ {
+ public string Id { get; set; }
+
+ public string Name { get; set; }
+ }
+}
diff --git a/test/Core.Test/Services/Fido2AuthenticatorMakeCredentialTests.cs b/test/Core.Test/Services/Fido2AuthenticatorMakeCredentialTests.cs
index 08d6d46f0..6170c4429 100644
--- a/test/Core.Test/Services/Fido2AuthenticatorMakeCredentialTests.cs
+++ b/test/Core.Test/Services/Fido2AuthenticatorMakeCredentialTests.cs
@@ -59,7 +59,7 @@ namespace Bit.Core.Test.Services
Algorithm = -7 // ES256
}
];
- mParams.RpId = "bitwarden.com";
+ mParams.RpEntity = new PublicKeyCredentialRpEntity { Id = "bitwarden.com" };
mParams.RequireUserVerification = false;
mParams.ExcludeCredentialDescriptorList = [
new PublicKeyCredentialDescriptor {
@@ -96,7 +96,7 @@ namespace Bit.Core.Test.Services
Algorithm = -7 // ES256
}
];
- mParams.RpId = "bitwarden.com";
+ mParams.RpEntity = new PublicKeyCredentialRpEntity { Id = "bitwarden.com" };
mParams.RequireUserVerification = false;
mParams.ExcludeCredentialDescriptorList = [
new PublicKeyCredentialDescriptor {
@@ -126,7 +126,7 @@ namespace Bit.Core.Test.Services
Algorithm = -7 // ES256
}
];
- mParams.RpId = "bitwarden.com";
+ mParams.RpEntity = new PublicKeyCredentialRpEntity { Id = "bitwarden.com" };
mParams.RequireUserVerification = false;
mParams.ExcludeCredentialDescriptorList = [
new PublicKeyCredentialDescriptor {
@@ -146,6 +146,74 @@ namespace Bit.Core.Test.Services
#endregion
+ #region credential creation
+
+ [Theory]
+ [InlineCustomAutoData(new[] { typeof(SutProviderCustomization) })]
+ public async Task MakeCredentialAsync_RequestsUserVerification_ParamsRequireUserVerification(SutProvider sutProvider, Fido2AuthenticatorMakeCredentialParams mParams)
+ {
+ // Common Arrange
+ var credentialIds = new[] { Guid.NewGuid(), Guid.NewGuid() };
+ List ciphers = [
+ CreateCipherView(credentialIds[0].ToString(), "bitwarden.com", false),
+ CreateCipherView(credentialIds[1].ToString(), "bitwarden.com", true)
+ ];
+ mParams.CredTypesAndPubKeyAlgs = [
+ new PublicKeyCredentialAlgorithmDescriptor {
+ Type = "public-key",
+ Algorithm = -7 // ES256
+ }
+ ];
+ mParams.RpEntity = new PublicKeyCredentialRpEntity { Id = "bitwarden.com" };
+ mParams.RequireUserVerification = false;
+ sutProvider.GetDependency().GetAllDecryptedAsync().Returns(ciphers);
+
+ // Arrange
+ mParams.RequireUserVerification = true;
+
+ // Act
+ await sutProvider.Sut.MakeCredentialAsync(mParams);
+
+ // Assert
+ await sutProvider.GetDependency().Received().ConfirmNewCredentialAsync(Arg.Is(
+ (p) => p.UserVerification == true
+ ));
+ }
+
+ [Theory]
+ [InlineCustomAutoData(new[] { typeof(SutProviderCustomization) })]
+ public async Task MakeCredentialAsync_DoesNotRequestUserVerification_ParamsDoNotRequireUserVerification(SutProvider sutProvider, Fido2AuthenticatorMakeCredentialParams mParams)
+ {
+ // Common Arrange
+ var credentialIds = new[] { Guid.NewGuid(), Guid.NewGuid() };
+ List ciphers = [
+ CreateCipherView(credentialIds[0].ToString(), "bitwarden.com", false),
+ CreateCipherView(credentialIds[1].ToString(), "bitwarden.com", true)
+ ];
+ mParams.CredTypesAndPubKeyAlgs = [
+ new PublicKeyCredentialAlgorithmDescriptor {
+ Type = "public-key",
+ Algorithm = -7 // ES256
+ }
+ ];
+ mParams.RpEntity = new PublicKeyCredentialRpEntity { Id = "bitwarden.com" };
+ mParams.RequireUserVerification = false;
+ sutProvider.GetDependency().GetAllDecryptedAsync().Returns(ciphers);
+
+ // Arrange
+ mParams.RequireUserVerification = false;
+
+ // Act
+ await sutProvider.Sut.MakeCredentialAsync(mParams);
+
+ // Assert
+ await sutProvider.GetDependency().Received().ConfirmNewCredentialAsync(Arg.Is(
+ (p) => p.UserVerification == false
+ ));
+ }
+
+ #endregion
+
private byte[] RandomBytes(int length)
{
var bytes = new byte[length];