using System.Runtime.InteropServices;
using ProtonVPN.OperatingSystems.WebAuthn.Enums;
using ProtonVPN.OperatingSystems.WebAuthn.Interop.Enums;
using ProtonVPN.OperatingSystems.WebAuthn.Interop.Marshalers;
using ProtonVPN.OperatingSystems.WebAuthn.Interop.StructVersions;
namespace ProtonVPN.OperatingSystems.WebAuthn.Interop.Structs.GetAssertion;
///
/// A structure that contains the data needed to get an assertion.
///
/// Corresponds to WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class AuthenticatorGetAssertionOptions : IDisposable
{
///
/// The version of this structure.
///
private AuthenticatorGetAssertionOptionsVersion _version = AuthenticatorGetAssertionOptionsVersion.Version7;
///
/// Time that the operation is expected to complete within.
///
/// This is used as guidance, and can be overridden by the platform.
public int TimeoutMilliseconds { get; set; } = ApiConstants.DefaultTimeoutMilliseconds;
///
/// The list of allowed credentials to be used in the assertion.
///
private Credentials _allowCredentials;
///
/// A CBOR map from extension identifiers to their authenticator extension inputs,
/// created by the client based on the extensions requested by the Relying Party.
/// These are optional extensions to parse when performing the operation.
///
private ExtensionsIn _extensions;
///
/// Platform vs Cross-Platform Authenticators. (Optional)
///
public AuthenticatorAttachment AuthenticatorAttachment { get; set; }
///
/// The effective user verification requirement.
///
public UserVerificationRequirement UserVerificationRequirement { get; set; }
///
/// Flags
///
private AssertionOptionsFlags _flags;
///
/// Optional identifier for the U2F AppId. Converted to UTF8 before being hashed. Not lower-cased.
///
/// This field has been added in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_2.
[MarshalAs(UnmanagedType.LPUTF8Str)]
private string _u2fAppId;
///
/// If the following is non-NULL, then, set to TRUE if the above U2fAppid was used instead of RpId
/// Note that this value is modified by WebAuthNAuthenticatorGetAssertion
///
/// This field has been added in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_2.
private nint _isU2fAppIdUsed = nint.Zero;
///
/// Cancellation Id (Optional).
///
/// This field has been added in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_3.
private nint _cancellationId = nint.Zero;
///
/// An optional list of public key credential descriptors describing credentials acceptable to the Relying Party (possibly filtered by the client), if any.
/// If present, CredentialList will be ignored.
///
/// This field has been added in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_4.
private nint _allowCredentialList = nint.Zero;
///
/// The large blob operation.
///
/// This field has been added in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_5.
public CredentialLargeBlobOperation LargeBlobOperation { get; set; }
///
/// Size of _largeBlob.
///
/// This field has been added in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_5.
private int _largeBlobLength;
///
/// A pointer to the large credential blob.
///
/// This field has been added in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_5.
private ByteArrayIn _largeBlob;
///
/// PRF values which will be converted into HMAC-SECRET values according to WebAuthn Specification.
///
/// This field has been added in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_6.
private nint _hmacSecretSaltValues;
///
/// Indicates whether the browser is in private mode. Defaulting to false.
///
/// This field has been added in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_6.
public bool BrowserInPrivateMode { get; set; }
///
/// Linked Device Connection Info.
///
/// This field has been added in WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_7.
private nint _linkedDevice { get; set; }
///
/// Allowlist MUST contain 1 credential applicable for Hybrid transport.
///
/// This field has been added in WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_7.
bool AutoFill { get; set; }
///
/// Size of JSON extension.
///
/// This field has been added in WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_7.
private int _jsonExtLength;
///
/// JSON extension.
///
/// This field has been added in WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_7.
private ByteArrayIn _jsonExt;
///
/// Optional identifier for the U2F AppId. Converted to UTF8 before being hashed. Not lower cased.
///
public string U2fAppId
{
get
{
return _u2fAppId;
}
set
{
_u2fAppId = value;
if (_isU2fAppIdUsed == nint.Zero)
{
_isU2fAppIdUsed = Marshal.AllocHGlobal(sizeof(int));
}
Marshal.WriteInt32(_isU2fAppIdUsed, Convert.ToInt32(value != null));
}
}
///
/// Cancellation Id (Optional)
///
public Guid? CancellationId
{
set
{
if (value.HasValue)
{
if (_cancellationId == nint.Zero)
{
_cancellationId = Marshal.AllocHGlobal(Marshal.SizeOf());
}
Marshal.StructureToPtr(value.Value, _cancellationId, false);
}
else
{
FreeCancellationId();
}
}
}
///
/// Allowed Credentials List.
///
public Credentials AllowCredentials
{
set
{
_allowCredentials?.Dispose();
_allowCredentials = value;
}
}
///
/// Allow Credential List. If present, "AllowCredentials" will be ignored.
///
public CredentialList AllowCredentialsEx
{
set
{
if (value != null)
{
if (_allowCredentialList == nint.Zero)
{
_allowCredentialList = Marshal.AllocHGlobal(Marshal.SizeOf());
}
Marshal.StructureToPtr(value, _allowCredentialList, false);
}
else
{
FreeAllowCredentialList();
}
}
}
///
/// Version of this structure, to allow for modifications in the future.
///
/// This is a V7 struct. If V8 arrives, new fields will need to be added.
public AuthenticatorGetAssertionOptionsVersion Version
{
get
{
return _version;
}
set
{
if (value > AuthenticatorGetAssertionOptionsVersion.Version7)
{
// We only support older struct versions.
throw new ArgumentOutOfRangeException(nameof(value), "The requested data structure version is not yet supported.");
}
_version = value;
}
}
///
/// Extensions to parse when performing the operation. (Optional)
///
public ExtensionsIn Extensions
{
set
{
_extensions?.Dispose();
_extensions = value;
}
}
///
/// Credential Large Blob.
///
public byte[] LargeBlob
{
get
{
return _largeBlob?.Read(_largeBlobLength);
}
set
{
// Get rid of any previous blob first
_largeBlob?.Dispose();
// Now replace the previous value with the new one
_largeBlobLength = value?.Length ?? 0;
_largeBlob = new ByteArrayIn(value);
}
}
///
/// PRF values which will be converted into HMAC-SECRET values according to WebAuthn Specification.
///
/// This field has been added in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_6.
public HmacSecretSaltValuesIn HmacSecretSaltValues
{
set
{
if (value?.HasGlobalHmacSalt ?? false)
{
if (_hmacSecretSaltValues == nint.Zero)
{
_hmacSecretSaltValues = Marshal.AllocHGlobal(Marshal.SizeOf());
}
Marshal.StructureToPtr(value, _hmacSecretSaltValues, false);
// Set flag
_flags |= AssertionOptionsFlags.AuthenticatorHmacSecretValues;
}
else
{
FreeHmacSecretSaltValues();
// Unset flag
_flags &= ~AssertionOptionsFlags.AuthenticatorHmacSecretValues;
}
}
}
///
/// Linked Device Connection Info.
///
/// This field has been added in WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_7.
public HybridStorageLinkedData LinkedDevice
{
set
{
if (value != null)
{
if (_linkedDevice == nint.Zero)
{
_linkedDevice = Marshal.AllocHGlobal(Marshal.SizeOf());
}
Marshal.StructureToPtr(value, _linkedDevice, false);
}
else
{
FreeLinkedDevice();
}
}
}
///
/// JSON extension.
///
/// This field has been added in WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_7.
public byte[] JsonExt
{
get
{
return _jsonExt?.Read(_jsonExtLength);
}
set
{
// Get rid of any previous blob first
_jsonExt?.Dispose();
// Now replace the previous value with a new one
_jsonExtLength = value?.Length ?? 0;
_jsonExt = new ByteArrayIn(value);
}
}
public void Dispose()
{
_extensions?.Dispose();
_extensions = null;
_allowCredentials?.Dispose();
_allowCredentials = null;
_largeBlob?.Dispose();
_largeBlob = null;
_jsonExt?.Dispose();
_jsonExt = null;
FreeAllowCredentialList();
FreeHmacSecretSaltValues();
FreeLinkedDevice();
if (_isU2fAppIdUsed != nint.Zero)
{
Marshal.FreeHGlobal(_isU2fAppIdUsed);
_isU2fAppIdUsed = nint.Zero;
}
FreeCancellationId();
}
private void FreeLinkedDevice()
{
if (_linkedDevice != nint.Zero)
{
Marshal.FreeHGlobal(_linkedDevice);
_linkedDevice = nint.Zero;
}
}
private void FreeHmacSecretSaltValues()
{
if (_hmacSecretSaltValues != nint.Zero)
{
Marshal.FreeHGlobal(_hmacSecretSaltValues);
_hmacSecretSaltValues = nint.Zero;
}
}
private void FreeAllowCredentialList()
{
if (_allowCredentialList != nint.Zero)
{
Marshal.FreeHGlobal(_allowCredentialList);
_allowCredentialList = nint.Zero;
}
}
private void FreeCancellationId()
{
if (_cancellationId != nint.Zero)
{
Marshal.FreeHGlobal(_cancellationId);
_cancellationId = nint.Zero;
}
}
}