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