mirror of
https://github.com/bitwarden/mobile
synced 2025-12-20 10:13:42 +00:00
feat: add support for credProps.rk extension (#3132)
This commit is contained in:
@@ -4,6 +4,7 @@ using Bit.Core.Abstractions;
|
|||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.Core.Utilities.Fido2;
|
using Bit.Core.Utilities.Fido2;
|
||||||
|
using Bit.Core.Utilities.Fido2.Extensions;
|
||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
@@ -124,6 +125,15 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
var makeCredentialResult = await _fido2AuthenticatorService.MakeCredentialAsync(makeCredentialParams, _makeCredentialUserInterface);
|
var makeCredentialResult = await _fido2AuthenticatorService.MakeCredentialAsync(makeCredentialParams, _makeCredentialUserInterface);
|
||||||
|
|
||||||
|
Fido2CredPropsResult credProps = null;
|
||||||
|
if (createCredentialParams.Extensions?.CredProps == true)
|
||||||
|
{
|
||||||
|
credProps = new Fido2CredPropsResult
|
||||||
|
{
|
||||||
|
Rk = makeCredentialParams.RequireResidentKey
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return new Fido2ClientCreateCredentialResult
|
return new Fido2ClientCreateCredentialResult
|
||||||
{
|
{
|
||||||
CredentialId = makeCredentialResult.CredentialId,
|
CredentialId = makeCredentialResult.CredentialId,
|
||||||
@@ -132,7 +142,11 @@ namespace Bit.Core.Services
|
|||||||
ClientDataJSON = clientDataJSONBytes,
|
ClientDataJSON = clientDataJSONBytes,
|
||||||
PublicKey = makeCredentialResult.PublicKey,
|
PublicKey = makeCredentialResult.PublicKey,
|
||||||
PublicKeyAlgorithm = makeCredentialResult.PublicKeyAlgorithm,
|
PublicKeyAlgorithm = makeCredentialResult.PublicKeyAlgorithm,
|
||||||
Transports = createCredentialParams.Rp.Id == "google.com" ? new string[] { "internal", "usb" } : new string[] { "internal" } // workaround for a bug on Google's side
|
Transports = createCredentialParams.Rp.Id == "google.com" ? new string[] { "internal", "usb" } : new string[] { "internal" }, // workaround for a bug on Google's side
|
||||||
|
Extensions = new Fido2CreateCredentialExtensionsResult
|
||||||
|
{
|
||||||
|
CredProps = credProps
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
catch (InvalidStateError)
|
catch (InvalidStateError)
|
||||||
@@ -249,7 +263,8 @@ namespace Bit.Core.Services
|
|||||||
Fido2ClientAssertCredentialParams assertCredentialParams,
|
Fido2ClientAssertCredentialParams assertCredentialParams,
|
||||||
byte[] cliendDataHash)
|
byte[] cliendDataHash)
|
||||||
{
|
{
|
||||||
return new Fido2AuthenticatorGetAssertionParams {
|
return new Fido2AuthenticatorGetAssertionParams
|
||||||
|
{
|
||||||
RpId = assertCredentialParams.RpId,
|
RpId = assertCredentialParams.RpId,
|
||||||
Challenge = assertCredentialParams.Challenge,
|
Challenge = assertCredentialParams.Challenge,
|
||||||
AllowCredentialDescriptorList = assertCredentialParams.AllowCredentials,
|
AllowCredentialDescriptorList = assertCredentialParams.AllowCredentials,
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Bit.Core.Utilities.Fido2.Extensions
|
||||||
|
{
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
public class Fido2CreateCredentialExtensionsParams
|
||||||
|
{
|
||||||
|
public bool CredProps { get; set; } = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Bit.Core.Utilities.Fido2.Extensions
|
||||||
|
{
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
public class Fido2CreateCredentialExtensionsResult
|
||||||
|
{
|
||||||
|
public Fido2CredPropsResult? CredProps { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Bit.Core.Utilities.Fido2.Extensions
|
||||||
|
{
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
public class Fido2CredPropsResult
|
||||||
|
{
|
||||||
|
public bool? Rk { get; set; } = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
|
using Bit.Core.Utilities.Fido2.Extensions;
|
||||||
|
|
||||||
namespace Bit.Core.Utilities.Fido2
|
namespace Bit.Core.Utilities.Fido2
|
||||||
{
|
{
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parameters for creating a new credential.
|
/// Parameters for creating a new credential.
|
||||||
@@ -42,9 +44,8 @@ namespace Bit.Core.Utilities.Fido2
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This member contains additional parameters requesting additional processing by the client and authenticator.
|
/// This member contains additional parameters requesting additional processing by the client and authenticator.
|
||||||
/// Not currently supported.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object? Extensions { get; set; }
|
public Fido2CreateCredentialExtensionsParams? Extensions { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This member contains information about the desired properties of the credential to be created.
|
/// This member contains information about the desired properties of the credential to be created.
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Bit.Core.Utilities.Fido2.Extensions;
|
||||||
|
|
||||||
namespace Bit.Core.Utilities.Fido2
|
namespace Bit.Core.Utilities.Fido2
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -15,5 +17,6 @@ namespace Bit.Core.Utilities.Fido2
|
|||||||
public byte[] PublicKey { get; set; }
|
public byte[] PublicKey { get; set; }
|
||||||
public int PublicKeyAlgorithm { get; set; }
|
public int PublicKeyAlgorithm { get; set; }
|
||||||
public string[] Transports { get; set; }
|
public string[] Transports { get; set; }
|
||||||
|
public Fido2CreateCredentialExtensionsResult Extensions { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using Bit.Core.Abstractions;
|
|||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.Core.Utilities.Fido2;
|
using Bit.Core.Utilities.Fido2;
|
||||||
|
using Bit.Core.Utilities.Fido2.Extensions;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using NSubstitute.ExceptionExtensions;
|
using NSubstitute.ExceptionExtensions;
|
||||||
@@ -20,6 +21,7 @@ namespace Bit.Core.Test.Services
|
|||||||
private readonly SutProvider<Fido2ClientService> _sutProvider = new SutProvider<Fido2ClientService>().Create();
|
private readonly SutProvider<Fido2ClientService> _sutProvider = new SutProvider<Fido2ClientService>().Create();
|
||||||
|
|
||||||
private Fido2ClientCreateCredentialParams _params;
|
private Fido2ClientCreateCredentialParams _params;
|
||||||
|
private Fido2AuthenticatorMakeCredentialResult _authenticatorResult;
|
||||||
|
|
||||||
public Fido2ClientCreateCredentialTests()
|
public Fido2ClientCreateCredentialTests()
|
||||||
{
|
{
|
||||||
@@ -37,17 +39,28 @@ namespace Bit.Core.Test.Services
|
|||||||
Alg = (int) Fido2AlgorithmIdentifier.ES256
|
Alg = (int) Fido2AlgorithmIdentifier.ES256
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Rp = new PublicKeyCredentialRpEntity {
|
Rp = new PublicKeyCredentialRpEntity
|
||||||
|
{
|
||||||
Id = "bitwarden.com",
|
Id = "bitwarden.com",
|
||||||
Name = "Bitwarden"
|
Name = "Bitwarden"
|
||||||
},
|
},
|
||||||
User = new PublicKeyCredentialUserEntity {
|
User = new PublicKeyCredentialUserEntity
|
||||||
|
{
|
||||||
Id = RandomBytes(32),
|
Id = RandomBytes(32),
|
||||||
Name = "user@bitwarden.com",
|
Name = "user@bitwarden.com",
|
||||||
DisplayName = "User"
|
DisplayName = "User"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_authenticatorResult = new Fido2AuthenticatorMakeCredentialResult
|
||||||
|
{
|
||||||
|
CredentialId = RandomBytes(32),
|
||||||
|
AttestationObject = RandomBytes(32),
|
||||||
|
AuthData = RandomBytes(32),
|
||||||
|
PublicKey = RandomBytes(32),
|
||||||
|
PublicKeyAlgorithm = (int)Fido2AlgorithmIdentifier.ES256,
|
||||||
|
};
|
||||||
|
|
||||||
_sutProvider.GetDependency<IStateService>().GetAutofillBlacklistedUrisAsync().Returns(Task.FromResult(new List<string>()));
|
_sutProvider.GetDependency<IStateService>().GetAutofillBlacklistedUrisAsync().Returns(Task.FromResult(new List<string>()));
|
||||||
_sutProvider.GetDependency<IStateService>().IsAuthenticatedAsync().Returns(true);
|
_sutProvider.GetDependency<IStateService>().IsAuthenticatedAsync().Returns(true);
|
||||||
}
|
}
|
||||||
@@ -198,16 +211,18 @@ namespace Bit.Core.Test.Services
|
|||||||
public async Task CreateCredentialAsync_ReturnsNewCredential()
|
public async Task CreateCredentialAsync_ReturnsNewCredential()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
_params.AuthenticatorSelection = new AuthenticatorSelectionCriteria {
|
_params.AuthenticatorSelection = new AuthenticatorSelectionCriteria
|
||||||
|
{
|
||||||
ResidentKey = "required",
|
ResidentKey = "required",
|
||||||
UserVerification = "required"
|
UserVerification = "required"
|
||||||
};
|
};
|
||||||
var authenticatorResult = new Fido2AuthenticatorMakeCredentialResult {
|
var authenticatorResult = new Fido2AuthenticatorMakeCredentialResult
|
||||||
|
{
|
||||||
CredentialId = RandomBytes(32),
|
CredentialId = RandomBytes(32),
|
||||||
AttestationObject = RandomBytes(32),
|
AttestationObject = RandomBytes(32),
|
||||||
AuthData = RandomBytes(32),
|
AuthData = RandomBytes(32),
|
||||||
PublicKey = RandomBytes(32),
|
PublicKey = RandomBytes(32),
|
||||||
PublicKeyAlgorithm = (int) Fido2AlgorithmIdentifier.ES256,
|
PublicKeyAlgorithm = (int)Fido2AlgorithmIdentifier.ES256,
|
||||||
};
|
};
|
||||||
_sutProvider.GetDependency<IFido2AuthenticatorService>()
|
_sutProvider.GetDependency<IFido2AuthenticatorService>()
|
||||||
.MakeCredentialAsync(Arg.Any<Fido2AuthenticatorMakeCredentialParams>(), _sutProvider.GetDependency<IFido2MakeCredentialUserInterface>())
|
.MakeCredentialAsync(Arg.Any<Fido2AuthenticatorMakeCredentialParams>(), _sutProvider.GetDependency<IFido2MakeCredentialUserInterface>())
|
||||||
@@ -246,7 +261,8 @@ namespace Bit.Core.Test.Services
|
|||||||
public async Task CreateCredentialAsync_ThrowsInvalidStateError_AuthenticatorThrowsInvalidStateError()
|
public async Task CreateCredentialAsync_ThrowsInvalidStateError_AuthenticatorThrowsInvalidStateError()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
_params.AuthenticatorSelection = new AuthenticatorSelectionCriteria {
|
_params.AuthenticatorSelection = new AuthenticatorSelectionCriteria
|
||||||
|
{
|
||||||
ResidentKey = "required",
|
ResidentKey = "required",
|
||||||
UserVerification = "required"
|
UserVerification = "required"
|
||||||
};
|
};
|
||||||
@@ -304,6 +320,66 @@ namespace Bit.Core.Test.Services
|
|||||||
Assert.Equal(Fido2ClientException.ErrorCode.NotAllowedError, exception.Code);
|
Assert.Equal(Fido2ClientException.ErrorCode.NotAllowedError, exception.Code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CreateCredentialAsync_ReturnsCredPropsRkTrue_WhenCreatingDiscoverableCredential()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
_params.AuthenticatorSelection = new AuthenticatorSelectionCriteria
|
||||||
|
{
|
||||||
|
ResidentKey = "required"
|
||||||
|
};
|
||||||
|
_params.Extensions = new Fido2CreateCredentialExtensionsParams { CredProps = true };
|
||||||
|
_sutProvider.GetDependency<IFido2AuthenticatorService>()
|
||||||
|
.MakeCredentialAsync(Arg.Any<Fido2AuthenticatorMakeCredentialParams>(), _sutProvider.GetDependency<IFido2MakeCredentialUserInterface>())
|
||||||
|
.Returns(_authenticatorResult);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _sutProvider.Sut.CreateCredentialAsync(_params);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result.Extensions.CredProps?.Rk);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CreateCredentialAsync_ReturnsCredPropsRkFalse_WhenCreatingNonDiscoverableCredential()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
_params.AuthenticatorSelection = new AuthenticatorSelectionCriteria
|
||||||
|
{
|
||||||
|
ResidentKey = "discouraged"
|
||||||
|
};
|
||||||
|
_params.Extensions = new Fido2CreateCredentialExtensionsParams { CredProps = true };
|
||||||
|
_sutProvider.GetDependency<IFido2AuthenticatorService>()
|
||||||
|
.MakeCredentialAsync(Arg.Any<Fido2AuthenticatorMakeCredentialParams>(), _sutProvider.GetDependency<IFido2MakeCredentialUserInterface>())
|
||||||
|
.Returns(_authenticatorResult);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _sutProvider.Sut.CreateCredentialAsync(_params);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result.Extensions.CredProps?.Rk);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CreateCredentialAsync_ReturnsCredPropsUndefined_WhenExtensionIsNotRequested()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
_params.AuthenticatorSelection = new AuthenticatorSelectionCriteria
|
||||||
|
{
|
||||||
|
ResidentKey = "required"
|
||||||
|
};
|
||||||
|
_params.Extensions = new Fido2CreateCredentialExtensionsParams();
|
||||||
|
_sutProvider.GetDependency<IFido2AuthenticatorService>()
|
||||||
|
.MakeCredentialAsync(Arg.Any<Fido2AuthenticatorMakeCredentialParams>(), _sutProvider.GetDependency<IFido2MakeCredentialUserInterface>())
|
||||||
|
.Returns(_authenticatorResult);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _sutProvider.Sut.CreateCredentialAsync(_params);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(result.Extensions.CredProps);
|
||||||
|
}
|
||||||
|
|
||||||
private byte[] RandomBytes(int length)
|
private byte[] RandomBytes(int length)
|
||||||
{
|
{
|
||||||
var bytes = new byte[length];
|
var bytes = new byte[length];
|
||||||
|
|||||||
Reference in New Issue
Block a user