using System.Runtime.InteropServices;
using ProtonVPN.OperatingSystems.WebAuthn.Enums;
using ProtonVPN.OperatingSystems.WebAuthn.Interop.Marshalers;
namespace ProtonVPN.OperatingSystems.WebAuthn.Interop.Structs;
///
/// Information about Extensions.
///
/// Corresponds to WEBAUTHN_EXTENSIONS.
[StructLayout(LayoutKind.Sequential)]
public class ExtensionsOut : SafeStructArrayOut
{
public bool? HmacSecret
{
get
{
// Below type definitions is for WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET
// MakeCredential Output Type: BOOL.
// - pvExtension will point to a BOOL with the value TRUE if credential
// was successfully created with HMAC_SECRET.
// - cbExtension will contain the sizeof(BOOL).
ExtensionOut foundExtension = Items?.FirstOrDefault(extension => extension.Identifier == ApiConstants.ExtensionIdentifierHmacSecret);
if (foundExtension == null)
{
// The hmac-secret extension is not present.
return null;
}
if (foundExtension.Data?.Length != sizeof(int))
{
// This should never happen if the Windows API is working correctly.
throw new ArgumentOutOfRangeException(nameof(foundExtension.Data));
}
return BitConverter.ToInt32(foundExtension.Data, 0) != 0;
}
}
public UserVerification? CredProtect
{
get
{
// Below type definitions is for WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_PROTECT
// MakeCredential Output Type: DWORD.
// - pvExtension will point to a DWORD with one of the above WEBAUTHN_USER_VERIFICATION_* values
// if credential was successfully created with CRED_PROTECT.
// - cbExtension will contain the sizeof(DWORD).
ExtensionOut foundExtension = Items?.FirstOrDefault(extension => extension.Identifier == ApiConstants.ExtensionIdentifierCredProtect);
if (foundExtension == null)
{
// The credProtect extension is not present.
return null;
}
if (foundExtension.Data?.Length != sizeof(int))
{
// This should never happen if the Windows API is working correctly.
throw new ArgumentOutOfRangeException(nameof(foundExtension.Data));
}
return (UserVerification)BitConverter.ToInt32(foundExtension.Data, 0);
}
}
public bool? CredBlobCreated
{
get
{
// Below type definitions is for WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_BLOB
// MakeCredential Output Type: BOOL.
// - pvExtension will point to a BOOL with the value TRUE if credBlob was successfully created
// - cbExtension will contain the sizeof(BOOL).
ExtensionOut foundExtension = Items?.FirstOrDefault(extension => extension.Identifier == ApiConstants.ExtensionIdentifierCredBlob);
if (foundExtension == null)
{
// The credBlob extension is not present.
return null;
}
if (foundExtension.Data?.Length != sizeof(int))
{
// This should never happen if the Windows API is working correctly.
throw new ArgumentOutOfRangeException(nameof(foundExtension.Data));
}
return BitConverter.ToInt32(foundExtension.Data, 0) != 0;
}
}
public byte[] CredBlob
{
get
{
// GetAssertion Output Type: WEBAUTHN_CRED_BLOB_EXTENSION.
// - pvExtension will point to a WEBAUTHN_CRED_BLOB_EXTENSION struct if the authenticator
// returns the credBlob in the signed extensions
// - cbExtension will contain the sizeof(WEBAUTHN_CRED_BLOB_EXTENSION).
ExtensionOut foundExtension = Items?.FirstOrDefault(extension => extension.Identifier == ApiConstants.ExtensionIdentifierCredBlob);
if (foundExtension == null)
{
// The credBlob extension is not present.
return null;
}
if (foundExtension.Data?.Length < sizeof(int) + sizeof(byte))
{
// This should never happen if the Windows API is working correctly.
throw new ArgumentOutOfRangeException(nameof(foundExtension.Data));
}
int blobLength = BitConverter.ToInt32(foundExtension.Data, 0);
if (blobLength != foundExtension.Data.Length - sizeof(int))
{
// This should never happen if the Windows API is working correctly.
throw new ArgumentOutOfRangeException(nameof(foundExtension.Data));
}
// Copy the credential blob from the inner WEBAUTHN_CRED_BLOB_EXTENSION structure.
byte[] blob = new byte[blobLength];
Array.Copy(foundExtension.Data, sizeof(int), blob, 0, blobLength);
return blob;
}
}
public uint? MinPinLength
{
get
{
// Below type definitions is for WEBAUTHN_EXTENSIONS_IDENTIFIER_MIN_PIN_LENGTH
// MakeCredential Output Type: DWORD.
// - pvExtension will point to a DWORD with the minimum pin length if returned by the authenticator
// - cbExtension will contain the sizeof(DWORD).
ExtensionOut foundExtension = Items?.FirstOrDefault(extension => extension.Identifier == ApiConstants.ExtensionIdentifierMinPinLength);
if (foundExtension == null)
{
// The minPinLength extension is not present.
return null;
}
if (foundExtension.Data?.Length != sizeof(int))
{
// This should never happen if the Windows API is working correctly.
throw new ArgumentOutOfRangeException(nameof(foundExtension.Data));
}
return BitConverter.ToUInt32(foundExtension.Data, 0);
}
}
private ExtensionsOut() : base() { }
}