mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
[PM-5731] feat: scaffold make credential
This commit is contained in:
@@ -4,6 +4,7 @@ namespace Bit.Core.Abstractions
|
||||
{
|
||||
public interface IFido2AuthenticatorService
|
||||
{
|
||||
Task<Fido2AuthenticatorMakeCredentialResult> MakeCredentialAsync(Fido2AuthenticatorMakeCredentialParams makeCredentialParams);
|
||||
Task<Fido2AuthenticatorGetAssertionResult> GetAssertionAsync(Fido2AuthenticatorGetAssertionParams assertionParams);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,118 +116,123 @@ namespace Bit.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<List<CipherView>> FindCredentialsById(PublicKeyCredentialDescriptor[] credentials, string rpId)
|
||||
{
|
||||
var ids = new List<string>();
|
||||
public Task<Fido2AuthenticatorMakeCredentialResult> MakeCredentialAsync(Fido2AuthenticatorMakeCredentialParams makeCredentialParams) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
foreach (var credential in credentials)
|
||||
private async Task<List<CipherView>> FindCredentialsById(PublicKeyCredentialDescriptor[] credentials, string rpId)
|
||||
{
|
||||
try
|
||||
var ids = new List<string>();
|
||||
|
||||
foreach (var credential in credentials)
|
||||
{
|
||||
ids.Add(GuidToStandardFormat(credential.Id));
|
||||
try
|
||||
{
|
||||
ids.Add(GuidToStandardFormat(credential.Id));
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
catch {}
|
||||
|
||||
if (ids.Count == 0)
|
||||
{
|
||||
return new List<CipherView>();
|
||||
}
|
||||
|
||||
var ciphers = await _cipherService.GetAllDecryptedAsync();
|
||||
return ciphers.FindAll((cipher) =>
|
||||
!cipher.IsDeleted &&
|
||||
cipher.Type == CipherType.Login &&
|
||||
cipher.Login.HasFido2Credentials &&
|
||||
cipher.Login.MainFido2Credential.RpId == rpId &&
|
||||
ids.Contains(cipher.Login.MainFido2Credential.CredentialId)
|
||||
);
|
||||
}
|
||||
|
||||
if (ids.Count == 0)
|
||||
private async Task<List<CipherView>> FindCredentialsByRp(string rpId)
|
||||
{
|
||||
return new List<CipherView>();
|
||||
var ciphers = await _cipherService.GetAllDecryptedAsync();
|
||||
return ciphers.FindAll((cipher) =>
|
||||
!cipher.IsDeleted &&
|
||||
cipher.Type == CipherType.Login &&
|
||||
cipher.Login.HasFido2Credentials &&
|
||||
cipher.Login.MainFido2Credential.RpId == rpId &&
|
||||
cipher.Login.MainFido2Credential.IsDiscoverable
|
||||
);
|
||||
}
|
||||
|
||||
var ciphers = await _cipherService.GetAllDecryptedAsync();
|
||||
return ciphers.FindAll((cipher) =>
|
||||
!cipher.IsDeleted &&
|
||||
cipher.Type == CipherType.Login &&
|
||||
cipher.Login.HasFido2Credentials &&
|
||||
cipher.Login.MainFido2Credential.RpId == rpId &&
|
||||
ids.Contains(cipher.Login.MainFido2Credential.CredentialId)
|
||||
);
|
||||
}
|
||||
private async Task<byte[]> GenerateAuthData(
|
||||
string rpId,
|
||||
bool userVerification,
|
||||
bool userPresence,
|
||||
int counter
|
||||
// byte[] credentialId,
|
||||
// CryptoKey? cryptoKey - only needed for attestation
|
||||
) {
|
||||
List<byte> authData = new List<byte>();
|
||||
|
||||
private async Task<List<CipherView>> FindCredentialsByRp(string rpId)
|
||||
{
|
||||
var ciphers = await _cipherService.GetAllDecryptedAsync();
|
||||
return ciphers.FindAll((cipher) =>
|
||||
!cipher.IsDeleted &&
|
||||
cipher.Type == CipherType.Login &&
|
||||
cipher.Login.HasFido2Credentials &&
|
||||
cipher.Login.MainFido2Credential.RpId == rpId &&
|
||||
cipher.Login.MainFido2Credential.IsDiscoverable
|
||||
);
|
||||
}
|
||||
var rpIdHash = await _cryptoFunctionService.HashAsync(rpId, CryptoHashAlgorithm.Sha256);
|
||||
authData.AddRange(rpIdHash);
|
||||
|
||||
private async Task<byte[]> GenerateAuthData(
|
||||
string rpId,
|
||||
bool userVerification,
|
||||
bool userPresence,
|
||||
int counter
|
||||
// byte[] credentialId,
|
||||
// CryptoKey? cryptoKey - only needed for attestation
|
||||
) {
|
||||
List<byte> authData = new List<byte>();
|
||||
var flags = AuthDataFlags(false, false, userVerification, userPresence);
|
||||
authData.Add(flags);
|
||||
|
||||
var rpIdHash = await _cryptoFunctionService.HashAsync(rpId, CryptoHashAlgorithm.Sha256);
|
||||
authData.AddRange(rpIdHash);
|
||||
authData.AddRange([
|
||||
(byte)(counter >> 24),
|
||||
(byte)(counter >> 16),
|
||||
(byte)(counter >> 8),
|
||||
(byte)counter
|
||||
]);
|
||||
|
||||
var flags = AuthDataFlags(false, false, userVerification, userPresence);
|
||||
authData.Add(flags);
|
||||
|
||||
authData.AddRange([
|
||||
(byte)(counter >> 24),
|
||||
(byte)(counter >> 16),
|
||||
(byte)(counter >> 8),
|
||||
(byte)counter
|
||||
]);
|
||||
|
||||
return authData.ToArray();
|
||||
}
|
||||
|
||||
private byte AuthDataFlags(bool extensionData, bool attestationData, bool userVerification, bool userPresence) {
|
||||
byte flags = 0;
|
||||
|
||||
if (extensionData) {
|
||||
flags |= 0b1000000;
|
||||
return authData.ToArray();
|
||||
}
|
||||
|
||||
if (attestationData) {
|
||||
flags |= 0b01000000;
|
||||
private byte AuthDataFlags(bool extensionData, bool attestationData, bool userVerification, bool userPresence) {
|
||||
byte flags = 0;
|
||||
|
||||
if (extensionData) {
|
||||
flags |= 0b1000000;
|
||||
}
|
||||
|
||||
if (attestationData) {
|
||||
flags |= 0b01000000;
|
||||
}
|
||||
|
||||
if (userVerification) {
|
||||
flags |= 0b00000100;
|
||||
}
|
||||
|
||||
if (userPresence) {
|
||||
flags |= 0b00000001;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
if (userVerification) {
|
||||
flags |= 0b00000100;
|
||||
}
|
||||
|
||||
if (userPresence) {
|
||||
flags |= 0b00000001;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
private async Task<byte[]> GenerateSignature(
|
||||
byte[] authData,
|
||||
byte[] clientDataHash,
|
||||
byte[] privateKey
|
||||
)
|
||||
{
|
||||
var sigBase = authData.Concat(clientDataHash).ToArray();
|
||||
var signature = await _cryptoFunctionService.SignAsync(sigBase, privateKey, new CryptoSignEcdsaOptions
|
||||
private async Task<byte[]> GenerateSignature(
|
||||
byte[] authData,
|
||||
byte[] clientDataHash,
|
||||
byte[] privateKey
|
||||
)
|
||||
{
|
||||
Algorithm = CryptoSignEcdsaOptions.EcdsaAlgorithm.EcdsaP256Sha256,
|
||||
SignatureFormat = CryptoSignEcdsaOptions.DsaSignatureFormat.Rfc3279DerSequence
|
||||
});
|
||||
var sigBase = authData.Concat(clientDataHash).ToArray();
|
||||
var signature = await _cryptoFunctionService.SignAsync(sigBase, privateKey, new CryptoSignEcdsaOptions
|
||||
{
|
||||
Algorithm = CryptoSignEcdsaOptions.EcdsaAlgorithm.EcdsaP256Sha256,
|
||||
SignatureFormat = CryptoSignEcdsaOptions.DsaSignatureFormat.Rfc3279DerSequence
|
||||
});
|
||||
|
||||
return signature;
|
||||
}
|
||||
return signature;
|
||||
}
|
||||
|
||||
private string GuidToStandardFormat(byte[] bytes)
|
||||
{
|
||||
return new Guid(bytes).ToString();
|
||||
}
|
||||
private string GuidToStandardFormat(byte[] bytes)
|
||||
{
|
||||
return new Guid(bytes).ToString();
|
||||
}
|
||||
|
||||
private byte[] GuidToRawFormat(string guid)
|
||||
{
|
||||
return Guid.Parse(guid).ToByteArray();
|
||||
}
|
||||
|
||||
private byte[] GuidToRawFormat(string guid)
|
||||
{
|
||||
return Guid.Parse(guid).ToByteArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
namespace Bit.Core.Utilities.Fido2
|
||||
{
|
||||
public class Fido2AuthenticatorMakeCredentialParams
|
||||
{
|
||||
///<summary>
|
||||
/// The caller’s RP ID, as determined by the user agent and the client. */
|
||||
///</summary>
|
||||
public string RpId { get; set; }
|
||||
|
||||
///<summary>
|
||||
/// The Relying Party's PublicKeyCredentialRpEntity. */
|
||||
///</summary>
|
||||
public PublicKeyCredentialUserEntity UserEntity { get; set; }
|
||||
|
||||
///<summary>
|
||||
/// The hash of the serialized client data, provided by the client. */
|
||||
///</summary>
|
||||
public byte[] Hash { get; set; }
|
||||
|
||||
///<summary>
|
||||
/// 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. */
|
||||
///</summary>
|
||||
public PublicKeyCredentialDescriptor[] CredTypesAndPubKeyAlgs { get; set; }
|
||||
|
||||
///<summary>
|
||||
/// The effective resident key requirement for credential creation, a Boolean value determined by the client. Resident is synonymous with discoverable. */
|
||||
///</summary>
|
||||
public bool RequireResidentKey { get; set; }
|
||||
|
||||
///<summary>
|
||||
/// The effective user verification requirement for assertion, a Boolean value provided by the client. */
|
||||
///</summary>
|
||||
public bool RequireUserVerification { get; set; }
|
||||
|
||||
///<summary>
|
||||
/// CTAP2 authenticators support setting this to false, but we only support the WebAuthn authenticator model which does not have that option. */
|
||||
///</summary>
|
||||
// public bool RequireUserPresence { get; set; } // Always required
|
||||
|
||||
/// <summary>
|
||||
/// The authenticator's attestation preference, a string provided by the client. This is a hint that the client gives to the authenticator about what kind of attestation statement it would like. The authenticator makes a best-effort to satisfy the preference.
|
||||
/// Note: Attestation statements are not supported at this time.
|
||||
/// </summary>
|
||||
// public string AttestationPreference { get; set; }
|
||||
|
||||
public object Extensions { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
namespace Bit.Core.Utilities.Fido2
|
||||
{
|
||||
public class Fido2AuthenticatorMakeCredentialResult
|
||||
{
|
||||
public byte[] CredentialId { get; set; }
|
||||
|
||||
public byte[] AttestationObject { get; set; }
|
||||
|
||||
public byte[] AuthData { get; set; }
|
||||
|
||||
public byte[] PublicKey { get; set; }
|
||||
|
||||
public int PublicKeyAlgorithm { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Bit.Core.Utilities.Fido2
|
||||
{
|
||||
public class PublicKeyCredentialUserEntity {
|
||||
public byte[] Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
public string Icon { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user