From 4cda61e8b8647914e9d3a4e63759ba5a0a73f9d4 Mon Sep 17 00:00:00 2001 From: Filip Skrzeczkowski <110346745+feedop@users.noreply.github.com> Date: Thu, 24 Oct 2024 09:52:45 +0200 Subject: [PATCH] [WebAuthn] Enable concurrent Authenticator requests where supported by the native API (#6389) Make concurrent calls forwarded unconditionally. It is now up to the native API whether it will honor the request or return an error. Additionally, fix an error in AuthenticatorAssertionResponse data marshalling. --- .../Tizen.Security.WebAuthn.csproj | 1 - .../Tizen.Security.WebAuthn/Authenticator.cs | 129 +++++----- .../AuthenticatorAssertionResponse.cs | 2 +- .../AuthenticatorGetAssertionStorage.cs | 76 ++++++ .../AuthenticatorMakeCredentialStorage.cs | 153 ++++++++++++ .../AuthenticatorStorage.cs | 226 +++++------------- 6 files changed, 351 insertions(+), 236 deletions(-) create mode 100644 src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorGetAssertionStorage.cs create mode 100644 src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorMakeCredentialStorage.cs diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn.csproj b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn.csproj index bb1db212d74..96a7e89bf0b 100644 --- a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn.csproj +++ b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn.csproj @@ -1,7 +1,6 @@  - library net6.0 diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Authenticator.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Authenticator.cs index 68707c8b3a7..803c3915604 100644 --- a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Authenticator.cs +++ b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Authenticator.cs @@ -16,7 +16,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; using static Interop; using static Tizen.Security.WebAuthn.ErrorFactory; @@ -31,14 +30,9 @@ public static class Authenticator { private const int API_VERSION_NUMBER = 0x00000001; private static bool _apiVersionSet = false; - private static bool _busy = false; private static object _userData = null; - private static WauthnDisplayQrcodeCallback _qrCodeCallback; - private static WauthnMcOnResponseCallback _mcResponseCallback; - private static WauthnGaOnResponseCallback _gaResponseCallback; - private static WauthnUpdateLinkedDataCallback _linkedDataCallback; - private static WauthnMcCallbacks _wauthnMcCallbacks; - private static WauthnGaCallbacks _wauthnGaCallbacks; + private static Dictionary _apiCalls = new(); + private static int _callId = 0; #region Public API /// @@ -86,32 +80,36 @@ public static AuthenticatorTransport SupportedAuthenticators() public static void MakeCredential(ClientData clientData, PubkeyCredCreationOptions options, MakeCredentialCallbacks callbacks) { CheckPreconditions(); - try - { - CheckNullNThrow(clientData); - CheckNullNThrow(clientData.JsonData); - CheckNullNThrow(options); - CheckNullNThrow(options.Rp); - CheckNullNThrow(options.User); - CheckNullNThrow(options.PubkeyCredParams); - CheckNullNThrow(callbacks); - CheckNullNThrow(callbacks.QrcodeCallback); - CheckNullNThrow(callbacks.ResponseCallback); - CheckNullNThrow(callbacks.LinkedDataCallback); + CheckNullNThrow(clientData); + CheckNullNThrow(clientData.JsonData); + CheckNullNThrow(options); + CheckNullNThrow(options.Rp); + CheckNullNThrow(options.User); + CheckNullNThrow(options.PubkeyCredParams); + CheckNullNThrow(callbacks); + CheckNullNThrow(callbacks.QrcodeCallback); + CheckNullNThrow(callbacks.ResponseCallback); + CheckNullNThrow(callbacks.LinkedDataCallback); - // Create callback wrappers - WrapMcCallbacks(callbacks); - AuthenticatorStorage.SetDataForMakeCredential(clientData, options); + int id = unchecked(_callId++); + // Copy data to unmanaged memory + var storage = new AuthenticatorMakeCredentialStorage(clientData, options); + // Create callback wrappers + WrapMcCallbacks(callbacks, storage, id); + // Add the storage to a static dictionary to prevent GC from premature collecting + _apiCalls.Add(id, storage); + try + { int ret = Libwebauthn.MakeCredential( - AuthenticatorStorage.WauthnClientData, - AuthenticatorStorage.WauthnPubkeyCredCreationOptions, - _wauthnMcCallbacks); + storage.WauthnClientData, + storage.WauthnPubkeyCredCreationOptions, + storage.WauthnMcCallbacks); CheckErrNThrow(ret, "Make Credential"); } catch { - Cleanup(); + Cleanup(id); throw; } } @@ -146,29 +144,33 @@ public static void MakeCredential(ClientData clientData, PubkeyCredCreationOptio public static void GetAssertion(ClientData clientData, PubkeyCredRequestOptions options, GetAssertionCallbacks callbacks) { CheckPreconditions(); - try - { - CheckNullNThrow(clientData); - CheckNullNThrow(clientData.JsonData); - CheckNullNThrow(options); - CheckNullNThrow(callbacks); - CheckNullNThrow(callbacks.QrcodeCallback); - CheckNullNThrow(callbacks.ResponseCallback); - CheckNullNThrow(callbacks.LinkedDataCallback); + CheckNullNThrow(clientData); + CheckNullNThrow(clientData.JsonData); + CheckNullNThrow(options); + CheckNullNThrow(callbacks); + CheckNullNThrow(callbacks.QrcodeCallback); + CheckNullNThrow(callbacks.ResponseCallback); + CheckNullNThrow(callbacks.LinkedDataCallback); - // Create callback wrappers - WrapGaCallbacks(callbacks); - AuthenticatorStorage.SetDataForGetAssertion(clientData, options); + int id = unchecked(_callId++); + // Copy data to unmanaged memory + var storage = new AuthenticatorGetAssertionStorage(clientData, options); + // Create callback wrappers + WrapGaCallbacks(callbacks, storage, id); + // Add the storage to a static dictionary to prevent GC from premature collecting + _apiCalls.Add(id, storage); + try + { int ret = Libwebauthn.GetAssertion( - AuthenticatorStorage.WauthnClientData, - AuthenticatorStorage.WauthnPubkeyCredRequestOptions, - _wauthnGaCallbacks); + storage.WauthnClientData, + storage.WauthnPubkeyCredRequestOptions, + storage.WauthnGaCallbacks); CheckErrNThrow(ret, "Get Assertion"); } catch { - Cleanup(); + Cleanup(id); throw; } } @@ -195,7 +197,7 @@ private static void SetApiVersion(int apiVersionNumber) CheckErrNThrow(ret, "Set API version"); _apiVersionSet = true; } - private static void WrapMcCallbacks(MakeCredentialCallbacks callbacks) + private static void WrapMcCallbacks(MakeCredentialCallbacks callbacks, AuthenticatorMakeCredentialStorage storage, int id) { _userData = callbacks.UserData; @@ -210,7 +212,7 @@ void onResponseWrapper(WauthnPubkeyCredentialAttestation pubkeyCred, WauthnError callbacks.ResponseCallback(pubkeyCredManaged, result, _userData); if (result != WauthnError.None) - Cleanup(); + Cleanup(id); } void linkedDataWrapper(IntPtr linkedData, WauthnError result, IntPtr _) @@ -219,17 +221,17 @@ void linkedDataWrapper(IntPtr linkedData, WauthnError result, IntPtr _) callbacks.LinkedDataCallback(linkedDataManaged, result, _userData); if (result != WauthnError.NoneAndWait) - Cleanup(); + Cleanup(id); } - _qrCodeCallback = new WauthnDisplayQrcodeCallback(qrCodeWrapper); - _mcResponseCallback = new WauthnMcOnResponseCallback(onResponseWrapper); - _linkedDataCallback = new WauthnUpdateLinkedDataCallback(linkedDataWrapper); - - _wauthnMcCallbacks = new WauthnMcCallbacks(_qrCodeCallback, _mcResponseCallback, _linkedDataCallback); + storage.SetCallbacks( + new WauthnDisplayQrcodeCallback(qrCodeWrapper), + new WauthnMcOnResponseCallback(onResponseWrapper), + new WauthnUpdateLinkedDataCallback(linkedDataWrapper) + ); } - private static void WrapGaCallbacks(GetAssertionCallbacks callbacks) + private static void WrapGaCallbacks(GetAssertionCallbacks callbacks, AuthenticatorGetAssertionStorage storage, int id) { _userData = callbacks.UserData; @@ -244,7 +246,7 @@ void onResponseWrapper(WauthnPubkeyCredentialAssertion pubkeyCred, WauthnError r callbacks.ResponseCallback(pubkeyCredManaged, result, _userData); if (result != WauthnError.None) - Cleanup(); + Cleanup(id); } void linkedDataWrapper(IntPtr linkedData, WauthnError result, IntPtr _) @@ -253,29 +255,26 @@ void linkedDataWrapper(IntPtr linkedData, WauthnError result, IntPtr _) callbacks.LinkedDataCallback(linkedDataManaged, result, _userData); if (result != WauthnError.NoneAndWait) - Cleanup(); + Cleanup(id); } - _qrCodeCallback = new WauthnDisplayQrcodeCallback(qrCodeWrapper); - _gaResponseCallback = new WauthnGaOnResponseCallback(onResponseWrapper); - _linkedDataCallback = new WauthnUpdateLinkedDataCallback(linkedDataWrapper); - _wauthnGaCallbacks = new WauthnGaCallbacks(_qrCodeCallback, _gaResponseCallback, _linkedDataCallback); + storage.SetCallbacks( + new WauthnDisplayQrcodeCallback(qrCodeWrapper), + new WauthnGaOnResponseCallback(onResponseWrapper), + new WauthnUpdateLinkedDataCallback(linkedDataWrapper) + ); } private static void CheckPreconditions() { if (!_apiVersionSet) SetApiVersion(API_VERSION_NUMBER); - if (_busy) - throw new InvalidOperationException("Authenticator busy"); - - _busy = true; } - private static void Cleanup() + private static void Cleanup(int id) { - _busy = false; - AuthenticatorStorage.Cleanup(); + _apiCalls[id]?.Dispose(); + _apiCalls.Remove(id); } #endregion diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorAssertionResponse.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorAssertionResponse.cs index c8bc6723d9f..b17fa49726c 100644 --- a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorAssertionResponse.cs +++ b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorAssertionResponse.cs @@ -31,7 +31,7 @@ public class AuthenticatorAssertionResponse internal AuthenticatorAssertionResponse(WauthnAuthenticatorAssertionResponse wauthnResponse) { ClientDataJson = NullSafeMarshal.PtrToArray(wauthnResponse.clientDataJson); - AuthenticatorData = NullSafeMarshal.PtrToArray(wauthnResponse.attestationObject); + AuthenticatorData = NullSafeMarshal.PtrToArray(wauthnResponse.authenticatorData); Signature = NullSafeMarshal.PtrToArray(wauthnResponse.signature); UserHandle = NullSafeMarshal.PtrToArray(wauthnResponse.userHandle); AttestationObject = NullSafeMarshal.PtrToArray(wauthnResponse.attestationObject); diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorGetAssertionStorage.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorGetAssertionStorage.cs new file mode 100644 index 00000000000..f483fbf16aa --- /dev/null +++ b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorGetAssertionStorage.cs @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +using static Interop; + +namespace Tizen.Security.WebAuthn +{ + internal class AuthenticatorGetAssertionStorage : AuthenticatorStorage + { + private WauthnGaOnResponseCallback _responseCallback; + private bool _disposed = false; + + public AuthenticatorGetAssertionStorage(ClientData clientData, PubkeyCredRequestOptions options) + { + CopyClientData(clientData); + CopyCredRequestOptions(options); + } + + public void SetCallbacks( + WauthnDisplayQrcodeCallback qrcodeCallback, + WauthnGaOnResponseCallback responseCallback, + WauthnUpdateLinkedDataCallback linkedDataCallback) + { + _qrcodeCallback = qrcodeCallback; + _responseCallback = responseCallback; + _linkedDataCallback = linkedDataCallback; + + WauthnGaCallbacks = new WauthnGaCallbacks(_qrcodeCallback, _responseCallback, _linkedDataCallback); + } + + public override void Dispose() + { + if (_disposed) + return; + + base.Dispose(); + + _disposed = true; + } + + private void CopyCredRequestOptions(PubkeyCredRequestOptions options) + { + CopyCredentials(options.AllowCredentials); + CopyHints(options.Hints); + CopyAttestationFormats(options.AttestationFormats); + CopyExtensions(options.Extensions); + CopyLinkedDevice(options.LinkedDevice); + WauthnPubkeyCredRequestOptions = new WauthnPubkeyCredRequestOptions( + (nuint)options.Timeout, + options.RpId, + _credentialsUnmanaged, + options.UserVerification, + _hintsUnmanaged, + options.Attestation, + _attestationFormatsUnmanaged, + _extensionsUnmanaged, + _linkedDeviceUnmanaged); + } + + public WauthnGaCallbacks WauthnGaCallbacks { get; private set; } + public WauthnPubkeyCredRequestOptions WauthnPubkeyCredRequestOptions { get; private set; } + } +} \ No newline at end of file diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorMakeCredentialStorage.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorMakeCredentialStorage.cs new file mode 100644 index 00000000000..8cbbb63a3c1 --- /dev/null +++ b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorMakeCredentialStorage.cs @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +using System.Collections.Generic; +using System.Linq; +using static Interop; + +namespace Tizen.Security.WebAuthn +{ + internal class AuthenticatorMakeCredentialStorage : AuthenticatorStorage + { + #region Internal unmanaged memory + private UnmanagedMemory _rpNameUnmanaged = new(); + private UnmanagedMemory _rpIdUnmanaged = new(); + private UnmanagedMemory _rpUnmanaged = new(); + private UnmanagedMemory _userNameUnmanaged = new(); + private UnmanagedMemory _userIdDataUnmanaged = new(); + private UnmanagedMemory _userIdConstBufferUnmanaged = new(); + private UnmanagedMemory _userDisplayNameUnmanaged = new(); + private UnmanagedMemory _userUnmanaged = new(); + private UnmanagedMemory _pubkeyCredParamsParametersUnmanaged = new(); + private UnmanagedMemory _pubkeyCredParamsUnmanaged = new(); + private UnmanagedMemory _authenticatorSelectionUnmanaged = new(); + + private WauthnMcOnResponseCallback _responseCallback; + #endregion + + private bool _disposed = false; + + public AuthenticatorMakeCredentialStorage(ClientData clientData, PubkeyCredCreationOptions options) + { + CopyClientData(clientData); + CopyCredCreationOptions(options); + } + + public void SetCallbacks( + WauthnDisplayQrcodeCallback qrcodeCallback, + WauthnMcOnResponseCallback responseCallback, + WauthnUpdateLinkedDataCallback linkedDataCallback) + { + _qrcodeCallback = qrcodeCallback; + _responseCallback = responseCallback; + _linkedDataCallback = linkedDataCallback; + + WauthnMcCallbacks = new WauthnMcCallbacks(_qrcodeCallback, _responseCallback, _linkedDataCallback); + } + + public override void Dispose() + { + if (_disposed) + return; + + _rpNameUnmanaged.Dispose(); + _rpIdUnmanaged.Dispose(); + _rpUnmanaged.Dispose(); + _userNameUnmanaged.Dispose(); + _userIdConstBufferUnmanaged.Dispose(); + _userDisplayNameUnmanaged.Dispose(); + _userUnmanaged.Dispose(); + _pubkeyCredParamsParametersUnmanaged.Dispose(); + _pubkeyCredParamsUnmanaged.Dispose(); + + base.Dispose(); + + _disposed = true; + } + + #region Data marshalling + private void CopyCredCreationOptions(PubkeyCredCreationOptions options) + { + CopyRp(options.Rp); + CopyUser(options.User); + CopyCredParams(options.PubkeyCredParams); + CopyCredentials(options.ExcludeCredentials); + CopyAuthenticatorSelection(options.AuthenticatorSelection); + CopyHints(options.Hints); + CopyAttestationFormats(options.AttestationFormats); + CopyExtensions(options.Extensions); + CopyLinkedDevice(options.LinkedDevice); + + WauthnPubkeyCredCreationOptions = new WauthnPubkeyCredCreationOptions( + _rpUnmanaged, + _userUnmanaged, + _pubkeyCredParamsUnmanaged, + (nuint)options.Timeout, + _credentialsUnmanaged, + _authenticatorSelectionUnmanaged, + _hintsUnmanaged, + options.Attestation, + _attestationFormatsUnmanaged, + _extensionsUnmanaged, + _linkedDeviceUnmanaged); + } + + private void CopyRp(RelyingPartyEntity rp) + { + _rpNameUnmanaged = new UnmanagedMemory(rp.Name); + _rpIdUnmanaged = new UnmanagedMemory(rp.Id); + _rpUnmanaged = new UnmanagedMemory(new WauthnRpEntity(_rpNameUnmanaged, _rpIdUnmanaged)); + } + + private void CopyUser(UserEntity user) + { + _userNameUnmanaged = new UnmanagedMemory(user.Name); + _userIdDataUnmanaged = UnmanagedMemory.PinArray(user.Id); + _userIdConstBufferUnmanaged = new UnmanagedMemory(new WauthnConstBuffer(_userIdDataUnmanaged, (nuint)user.Id.Length)); + _userDisplayNameUnmanaged = new UnmanagedMemory(user.DisplayName); + _userUnmanaged = new UnmanagedMemory(new WauthnUserEntity( + _userNameUnmanaged, + _userIdConstBufferUnmanaged, + _userDisplayNameUnmanaged)); + } + + private void CopyCredParams(IEnumerable credParams) + { + if (credParams is null || !credParams.Any()) + return; + + WauthnPubkeyCredParam[] pubkeyCredParamStructArray = credParams.Select((PubkeyCredParam param) => new WauthnPubkeyCredParam(param.Type, param.Alg)).ToArray(); + _pubkeyCredParamsParametersUnmanaged = UnmanagedMemory.PinArray(pubkeyCredParamStructArray); + _pubkeyCredParamsUnmanaged = new UnmanagedMemory(new WauthnPubkeyCredParams((nuint)pubkeyCredParamStructArray.Length, _pubkeyCredParamsParametersUnmanaged)); + } + + protected void CopyAuthenticatorSelection(AuthenticationSelectionCriteria selection) + { + if (selection is null) + return; + + _authenticatorSelectionUnmanaged = new UnmanagedMemory(new WauthnAuthenticationSelCri( + selection.Attachment, + selection.ResidentKey, + (byte)(selection.RequireResidentKey ? 1 : 0), + selection.UserVerification)); + } + #endregion + + public WauthnMcCallbacks WauthnMcCallbacks { get; private set; } + public WauthnPubkeyCredCreationOptions WauthnPubkeyCredCreationOptions { get; private set; } + } +} \ No newline at end of file diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorStorage.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorStorage.cs index a47c6718f89..6456426be5f 100644 --- a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorStorage.cs +++ b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorStorage.cs @@ -17,77 +17,62 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.InteropServices; using static Interop; -using static Tizen.Security.WebAuthn.ErrorFactory; namespace Tizen.Security.WebAuthn { - internal static class AuthenticatorStorage + internal abstract class AuthenticatorStorage : IDisposable { #region Internal unmanaged memory - private static UnmanagedMemory[] _credentialsIdUnmanagedDataArray; - private static UnmanagedMemory[] _credentialsIdUnmanagedConstBufferArray; - private static UnmanagedMemory[] _attestationFormatsUnmanagedDataArray; - private static UnmanagedMemory[] _attestationFormatsUnmanagedConstBufferArray; - private static UnmanagedMemory[] _extensionIdUnmanagedDataArray; - private static UnmanagedMemory[] _extensionIdUnmanagedConstBufferArray; - private static UnmanagedMemory[] _extensionValueUnmanagedDataArray; - private static UnmanagedMemory[] _extensionValueUnmanagedConstBufferArray; - private static UnmanagedMemory _jsonDataUnmanaged = new(); - private static UnmanagedMemory _jsonDataConstBufferUnmanaged = new(); - private static UnmanagedMemory _rpNameUnmanaged = new(); - private static UnmanagedMemory _rpIdUnmanaged = new(); - private static UnmanagedMemory _rpUnmanaged = new(); - private static UnmanagedMemory _userNameUnmanaged = new(); - private static UnmanagedMemory _userIdDataUnmanaged = new(); - private static UnmanagedMemory _userIdConstBufferUnmanaged = new(); - private static UnmanagedMemory _userDisplayNameUnmanaged = new(); - private static UnmanagedMemory _userUnmanaged = new(); - private static UnmanagedMemory _pubkeyCredParamsParametersUnmanaged = new(); - private static UnmanagedMemory _pubkeyCredParamsUnmanaged = new(); - private static UnmanagedMemory _credentialsDescriptorsUnmanaged = new(); - private static UnmanagedMemory _credentialsUnmanaged = new(); - private static UnmanagedMemory _authenticatorSelectionUnmanaged = new(); - private static UnmanagedMemory _hintsArrayUnmanaged = new(); - private static UnmanagedMemory _hintsUnmanaged = new(); - private static UnmanagedMemory _attestationFormatsArrayUnmanaged = new(); - private static UnmanagedMemory _attestationFormatsUnmanaged = new(); - private static UnmanagedMemory _extensionsArrayUnmanaged = new(); - private static UnmanagedMemory _extensionsUnmanaged = new(); - private static UnmanagedMemory _contactIdDataUnmanaged = new(); - private static UnmanagedMemory _contactIdUnmanaged = new(); - private static UnmanagedMemory _linkIdDataUnmanaged = new(); - private static UnmanagedMemory _linkIdUnmanaged = new(); - private static UnmanagedMemory _linkSecretDataUnmanaged = new(); - private static UnmanagedMemory _linkSecretUnmanaged = new(); - private static UnmanagedMemory _authenticatorPubkeyDataUnmanaged = new(); - private static UnmanagedMemory _authenticatorPubkeyUnmanaged = new(); - private static UnmanagedMemory _authenticatorNameDataUnmanaged = new(); - private static UnmanagedMemory _authenticatorNameUnmanaged = new(); - private static UnmanagedMemory _signatureDataUnmanaged = new(); - private static UnmanagedMemory _signatureUnmanaged = new(); - private static UnmanagedMemory _tunnelServerDomainDataUnmanaged = new(); - private static UnmanagedMemory _tunnelServerDomainUnmanaged = new(); - private static UnmanagedMemory _identityKeyDataUnmanaged = new(); - private static UnmanagedMemory _identityKeyUnmanaged = new(); - private static UnmanagedMemory _linkedDeviceUnmanaged = new(); + protected UnmanagedMemory _credentialsUnmanaged = new(); + protected UnmanagedMemory _hintsUnmanaged = new(); + protected UnmanagedMemory _attestationFormatsUnmanaged = new(); + protected UnmanagedMemory _extensionsUnmanaged = new(); + protected UnmanagedMemory _linkedDeviceUnmanaged = new(); + + protected WauthnDisplayQrcodeCallback _qrcodeCallback; + protected WauthnUpdateLinkedDataCallback _linkedDataCallback; + + private UnmanagedMemory[] _credentialsIdUnmanagedDataArray; + private UnmanagedMemory[] _credentialsIdUnmanagedConstBufferArray; + private UnmanagedMemory[] _attestationFormatsUnmanagedDataArray; + private UnmanagedMemory[] _attestationFormatsUnmanagedConstBufferArray; + private UnmanagedMemory[] _extensionIdUnmanagedDataArray; + private UnmanagedMemory[] _extensionIdUnmanagedConstBufferArray; + private UnmanagedMemory[] _extensionValueUnmanagedDataArray; + private UnmanagedMemory[] _extensionValueUnmanagedConstBufferArray; + + private UnmanagedMemory _jsonDataUnmanaged = new(); + private UnmanagedMemory _jsonDataConstBufferUnmanaged = new(); + private UnmanagedMemory _credentialsDescriptorsUnmanaged = new(); + private UnmanagedMemory _hintsArrayUnmanaged = new(); + private UnmanagedMemory _attestationFormatsArrayUnmanaged = new(); + private UnmanagedMemory _extensionsArrayUnmanaged = new(); + private UnmanagedMemory _contactIdDataUnmanaged = new(); + private UnmanagedMemory _contactIdUnmanaged = new(); + private UnmanagedMemory _linkIdDataUnmanaged = new(); + private UnmanagedMemory _linkIdUnmanaged = new(); + private UnmanagedMemory _linkSecretDataUnmanaged = new(); + private UnmanagedMemory _linkSecretUnmanaged = new(); + private UnmanagedMemory _authenticatorPubkeyDataUnmanaged = new(); + private UnmanagedMemory _authenticatorPubkeyUnmanaged = new(); + private UnmanagedMemory _authenticatorNameDataUnmanaged = new(); + private UnmanagedMemory _authenticatorNameUnmanaged = new(); + private UnmanagedMemory _signatureDataUnmanaged = new(); + private UnmanagedMemory _signatureUnmanaged = new(); + private UnmanagedMemory _tunnelServerDomainDataUnmanaged = new(); + private UnmanagedMemory _tunnelServerDomainUnmanaged = new(); + private UnmanagedMemory _identityKeyDataUnmanaged = new(); + private UnmanagedMemory _identityKeyUnmanaged = new(); #endregion - public static void SetDataForMakeCredential(ClientData clientData, PubkeyCredCreationOptions options) - { - CopyClientData(clientData); - CopyCredCreationOptions(options); - } + private bool _disposed = false; - public static void SetDataForGetAssertion(ClientData clientData, PubkeyCredRequestOptions options) + public virtual void Dispose() { - CopyClientData(clientData); - CopyCredRequestOptions(options); - } + if (_disposed) + return; - public static void Cleanup() - { CleanupArray(_credentialsIdUnmanagedDataArray); CleanupArray(_credentialsIdUnmanagedConstBufferArray); CleanupArray(_attestationFormatsUnmanagedDataArray); @@ -99,18 +84,8 @@ public static void Cleanup() _jsonDataUnmanaged.Dispose(); _jsonDataConstBufferUnmanaged.Dispose(); - _rpNameUnmanaged.Dispose(); - _rpIdUnmanaged.Dispose(); - _rpUnmanaged.Dispose(); - _userNameUnmanaged.Dispose(); - _userIdConstBufferUnmanaged.Dispose(); - _userDisplayNameUnmanaged.Dispose(); - _userUnmanaged.Dispose(); - _pubkeyCredParamsParametersUnmanaged.Dispose(); - _pubkeyCredParamsUnmanaged.Dispose(); _credentialsDescriptorsUnmanaged.Dispose(); _credentialsUnmanaged.Dispose(); - _authenticatorSelectionUnmanaged.Dispose(); _hintsArrayUnmanaged.Dispose(); _hintsUnmanaged.Dispose(); _attestationFormatsArrayUnmanaged.Dispose(); @@ -134,90 +109,19 @@ public static void Cleanup() _identityKeyDataUnmanaged.Dispose(); _identityKeyUnmanaged.Dispose(); _linkedDeviceUnmanaged.Dispose(); + + _disposed = true; } - private static void CopyClientData(ClientData clientData) + #region Data marshalling + protected void CopyClientData(ClientData clientData) { _jsonDataUnmanaged = UnmanagedMemory.PinArray(clientData.JsonData); _jsonDataConstBufferUnmanaged = new UnmanagedMemory(new WauthnConstBuffer(_jsonDataUnmanaged, (nuint)clientData.JsonData.Length)); WauthnClientData = new WauthnClientData(_jsonDataConstBufferUnmanaged, clientData.HashAlgo); } - private static void CopyCredCreationOptions(PubkeyCredCreationOptions options) - { - CopyRp(options.Rp); - CopyUser(options.User); - CopyCredParams(options.PubkeyCredParams); - CopyCredentials(options.ExcludeCredentials); - CopyAuthenticatorSelection(options.AuthenticatorSelection); - CopyHints(options.Hints); - CopyAttestationFormats(options.AttestationFormats); - CopyExtensions(options.Extensions); - CopyLinkedDevice(options.LinkedDevice); - - WauthnPubkeyCredCreationOptions = new WauthnPubkeyCredCreationOptions( - _rpUnmanaged, - _userUnmanaged, - _pubkeyCredParamsUnmanaged, - (nuint)options.Timeout, - _credentialsUnmanaged, - _authenticatorSelectionUnmanaged, - _hintsUnmanaged, - options.Attestation, - _attestationFormatsUnmanaged, - _extensionsUnmanaged, - _linkedDeviceUnmanaged); - } - - private static void CopyCredRequestOptions(PubkeyCredRequestOptions options) - { - CopyCredentials(options.AllowCredentials); - CopyHints(options.Hints); - CopyAttestationFormats(options.AttestationFormats); - CopyExtensions(options.Extensions); - CopyLinkedDevice(options.LinkedDevice); - WauthnPubkeyCredRequestOptions = new WauthnPubkeyCredRequestOptions( - (nuint)options.Timeout, - options.RpId, - _credentialsUnmanaged, - options.UserVerification, - _hintsUnmanaged, - options.Attestation, - _attestationFormatsUnmanaged, - _extensionsUnmanaged, - _linkedDeviceUnmanaged); - } - - private static void CopyRp(RelyingPartyEntity rp) - { - _rpNameUnmanaged = new UnmanagedMemory(rp.Name); - _rpIdUnmanaged = new UnmanagedMemory(rp.Id); - _rpUnmanaged = new UnmanagedMemory(new WauthnRpEntity(_rpNameUnmanaged, _rpIdUnmanaged)); - } - - private static void CopyUser(UserEntity user) - { - _userNameUnmanaged = new UnmanagedMemory(user.Name); - _userIdDataUnmanaged = UnmanagedMemory.PinArray(user.Id); - _userIdConstBufferUnmanaged = new UnmanagedMemory(new WauthnConstBuffer(_userIdDataUnmanaged, (nuint)user.Id.Length)); - _userDisplayNameUnmanaged = new UnmanagedMemory(user.DisplayName); - _userUnmanaged = new UnmanagedMemory(new WauthnUserEntity( - _userNameUnmanaged, - _userIdConstBufferUnmanaged, - _userDisplayNameUnmanaged)); - } - - private static void CopyCredParams(IEnumerable credParams) - { - if (credParams is null || !credParams.Any()) - return; - - WauthnPubkeyCredParam[] pubkeyCredParamStructArray = credParams.Select((PubkeyCredParam param) => new WauthnPubkeyCredParam(param.Type, param.Alg)).ToArray(); - _pubkeyCredParamsParametersUnmanaged = UnmanagedMemory.PinArray(pubkeyCredParamStructArray); - _pubkeyCredParamsUnmanaged = new UnmanagedMemory(new WauthnPubkeyCredParams((nuint)pubkeyCredParamStructArray.Length, _pubkeyCredParamsParametersUnmanaged)); - } - - private static void CopyCredentials(IEnumerable credentials) + protected void CopyCredentials(IEnumerable credentials) { if (credentials is null || !credentials.Any()) return; @@ -239,19 +143,7 @@ private static void CopyCredentials(IEnumerable credential _credentialsUnmanaged = new UnmanagedMemory(new WauthnPubkeyCredDescriptors((nuint)credentialsCount, _credentialsDescriptorsUnmanaged)); } - private static void CopyAuthenticatorSelection(AuthenticationSelectionCriteria selection) - { - if (selection is null) - return; - - _authenticatorSelectionUnmanaged = new UnmanagedMemory(new WauthnAuthenticationSelCri( - selection.Attachment, - selection.ResidentKey, - (byte)(selection.RequireResidentKey ? 1 : 0), - selection.UserVerification)); - } - - private static void CopyHints(IEnumerable hints) + protected void CopyHints(IEnumerable hints) { if (hints is null || !hints.Any()) return; @@ -261,7 +153,7 @@ private static void CopyHints(IEnumerable hints) _hintsUnmanaged = new UnmanagedMemory(new WauthnPubkeyCredHints((nuint)hintsArray.Length, _hintsArrayUnmanaged)); } - private static void CopyAttestationFormats(IEnumerable attestationFormats) + protected void CopyAttestationFormats(IEnumerable attestationFormats) { if (attestationFormats is null || !attestationFormats.Any()) return; @@ -283,7 +175,7 @@ private static void CopyAttestationFormats(IEnumerable attestationFormat _attestationFormatsUnmanaged = new UnmanagedMemory(new WauthnAttestationFormats((nuint)attestationFormatsCount, _attestationFormatsArrayUnmanaged)); } - private static void CopyExtensions(IEnumerable extensions) + protected void CopyExtensions(IEnumerable extensions) { if (extensions is null || !extensions.Any()) return; @@ -312,7 +204,7 @@ private static void CopyExtensions(IEnumerable extensio _extensionsUnmanaged = new UnmanagedMemory(new WauthnAuthenticationExts((nuint)extensionCount, _extensionsArrayUnmanaged)); } - private static void CopyLinkedDevice(HybridLinkedData linkedDevice) + protected void CopyLinkedDevice(HybridLinkedData linkedDevice) { if (linkedDevice is null) return; @@ -350,12 +242,10 @@ private static void CopyLinkedDevice(HybridLinkedData linkedDevice) _signatureUnmanaged, _tunnelServerDomainUnmanaged, _identityKeyUnmanaged)); - - if (_linkedDeviceUnmanaged == IntPtr.Zero) - throw new TimeoutException("linked null"); } + #endregion - public static void CleanupArray(UnmanagedMemory[] array) + protected static void CleanupArray(UnmanagedMemory[] array) { if (array is null) return; @@ -363,8 +253,6 @@ public static void CleanupArray(UnmanagedMemory[] array) memory.Dispose(); } - public static WauthnClientData WauthnClientData { get; private set; } - public static WauthnPubkeyCredCreationOptions WauthnPubkeyCredCreationOptions { get; private set; } - public static WauthnPubkeyCredRequestOptions WauthnPubkeyCredRequestOptions { get; private set; } + public WauthnClientData WauthnClientData { get; private set; } } } \ No newline at end of file