[WebAuthn] Add WebAuthn API (#6311)
authorFilip Skrzeczkowski <110346745+feedop@users.noreply.github.com>
Thu, 19 Sep 2024 12:04:46 +0000 (14:04 +0200)
committerGitHub <noreply@github.com>
Thu, 19 Sep 2024 12:04:46 +0000 (14:04 +0200)
[WebAuthn] Add WebAuthn module and public API

Provide the implementation and documentation for the public API of the
WebAuthn module implementing the passkey functionality.

Co-authored-by: Piotr Szydełko <wiertel@users.sourceforge.net>
36 files changed:
src/Tizen.Security.WebAuthn/Interop/Interop.Libweabuthn.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Interop/Interop.Types.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn.csproj [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn.sln [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticationExtension.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticationSelectionCriteria.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Authenticator.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorAssertionResponse.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorAttestationResponse.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorStorage.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/ClientData.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/AttestationPref.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/AuthenticatorAttachment.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/AuthenticatorTransport.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/CoseAlgorithm.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/HashAlogithm.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/PubkeyCredHint.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/PubkeyCredType.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/ResidentKeyRequirement.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/UserVerificationRequirement.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/WauthnError.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/ErrorFactory.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/GetAssertionCallbacks.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/HybridLinkedData.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/MakeCredentialCallbacks.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/NullSafeMarshal.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/PubkeyCredAssertion.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/PubkeyCredAttestation.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/PubkeyCredCreationOptions.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/PubkeyCredDescriptor.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/PubkeyCredParam.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/PubkeyCredRequestOptions.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/RelyingPartyEntity.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/UnmanagedMemory.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/UserEntity.cs [new file with mode: 0644]
src/Tizen.Security.WebAuthn/doc/api/Tizen.Security.WebAuthn.md [new file with mode: 0644]

diff --git a/src/Tizen.Security.WebAuthn/Interop/Interop.Libweabuthn.cs b/src/Tizen.Security.WebAuthn/Interop/Interop.Libweabuthn.cs
new file mode 100644 (file)
index 0000000..27ba520
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *  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;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+    internal static partial class Libraries
+    {
+        public const string Libwebauthn = "libwebauthn-client.so.1";
+    }
+
+    internal static partial class Libwebauthn
+    {
+        [DllImport(Libraries.Libwebauthn, EntryPoint = "wauthn_set_api_version", CallingConvention = CallingConvention.Cdecl)]
+        public static extern int SetApiVersion(int apiVersionNumber);
+        // int wauthn_set_api_version(int api_version_number);
+
+        [DllImport(Libraries.Libwebauthn, EntryPoint = "wauthn_supported_authenticators", CallingConvention = CallingConvention.Cdecl)]
+        public static extern int SupportedAuthenticators(out uint supported);
+        // int wauthn_supported_authenticators(unsigned int *supported);
+
+        [DllImport(Libraries.Libwebauthn, EntryPoint = "wauthn_make_credential", CallingConvention = CallingConvention.Cdecl)]
+        public static extern int MakeCredential([In] WauthnClientData clientData, [In] WauthnPubkeyCredCreationOptions options, [In, Out] WauthnMcCallbacks callbacks);
+        // int wauthn_make_credential( const wauthn_client_data_s *client_data, const wauthn_pubkey_cred_creation_options_s *options, wauthn_mc_callbacks_s *callbacks);
+
+        [DllImport(Libraries.Libwebauthn, EntryPoint = "wauthn_get_assertion", CallingConvention = CallingConvention.Cdecl)]
+        public static extern int GetAssertion([In] WauthnClientData clientData, [In] WauthnPubkeyCredRequestOptions options, [In, Out] WauthnGaCallbacks callbacks);
+        // int wauthn_get_assertion( const wauthn_client_data_s *client_data, const wauthn_pubkey_cred_request_options_s *options, wauthn_ga_callbacks_s *callbacks);
+
+        [DllImport(Libraries.Libwebauthn, EntryPoint = "wauthn_cancel", CallingConvention = CallingConvention.Cdecl)]
+        public static extern int Cancel();
+        // int wauthn_cancel();
+    }
+}
diff --git a/src/Tizen.Security.WebAuthn/Interop/Interop.Types.cs b/src/Tizen.Security.WebAuthn/Interop/Interop.Types.cs
new file mode 100644 (file)
index 0000000..c85dbd0
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+ *  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;
+using System.Runtime.InteropServices;
+using Tizen.Internals;
+using Tizen.Security.WebAuthn;
+
+internal static partial class Interop
+{
+    private const string pkg = "webauthn";
+
+    #region Delegates
+
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    internal delegate void WauthnDisplayQrcodeCallback([In][MarshalAs(UnmanagedType.LPStr)] string qrContents, IntPtr userData);
+
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    internal delegate void WauthnMcOnResponseCallback([In] WauthnPubkeyCredentialAttestation pubkeyCred, WauthnError result, IntPtr userData);
+
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    internal delegate void WauthnGaOnResponseCallback([In] WauthnPubkeyCredentialAssertion pubkeyCred, WauthnError result, IntPtr userData);
+
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    internal delegate void WauthnUpdateLinkedDataCallback([In] IntPtr linkedData, WauthnError result, IntPtr userData);
+
+    #endregion
+    #region Classes
+
+    [NativeStruct("wauthn_rp_entity_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal class WauthnRpEntity
+    {
+        public readonly IntPtr name;                        // string
+        public readonly IntPtr id;                          // string
+
+        public WauthnRpEntity(IntPtr name, IntPtr id)
+        {
+            this.name = name;
+            this.id = id;
+        }
+    }
+
+    [NativeStruct("wauthn_user_entity_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal class WauthnUserEntity
+    {
+        public readonly IntPtr name;                        // string
+        public readonly IntPtr id;                          // WauthnConstBuffer*
+        public readonly IntPtr displayName;                 // string
+
+        public WauthnUserEntity(IntPtr name, IntPtr id, IntPtr displayName)
+        {
+            this.name = name;
+            this.id = id;
+            this.displayName = displayName;
+        }
+    }
+
+    [NativeStruct("wauthn_pubkey_cred_params_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal class WauthnPubkeyCredParams
+    {
+        public readonly nuint size;
+        public readonly IntPtr parameters;                  // WauthnPubkeyCredParam[]
+
+        public WauthnPubkeyCredParams(nuint size, IntPtr parameters)
+        {
+            this.size = size;
+            this.parameters = parameters;
+        }
+    }
+
+    [NativeStruct("wauthn_pubkey_cred_descriptors_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal class WauthnPubkeyCredDescriptors
+    {
+        public readonly nuint size;
+        public readonly IntPtr descriptors;                 // WauthnPubkeyCredDescriptor[]
+
+        public WauthnPubkeyCredDescriptors(nuint size, IntPtr descriptors)
+        {
+            this.size = size;
+            this.descriptors = descriptors;
+        }
+    }
+
+    [NativeStruct("wauthn_authenticator_sel_cri_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal class WauthnAuthenticationSelCri
+    {
+        public readonly AuthenticatorAttachment attachment;
+        public readonly ResidentKeyRequirement residentKey;
+        public readonly byte requireResidentKey;
+        public readonly UserVerificationRequirement userVerification;
+
+        public WauthnAuthenticationSelCri(
+            AuthenticatorAttachment attachment,
+            ResidentKeyRequirement residentKey,
+            byte requireResidentKey,
+            UserVerificationRequirement userVerification)
+        {
+            this.attachment = attachment;
+            this.residentKey = residentKey;
+            this.requireResidentKey = requireResidentKey;
+            this.userVerification = userVerification;
+        }
+    }
+
+    [NativeStruct("wauthn_pubkey_cred_hints_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal class WauthnPubkeyCredHints
+    {
+        public readonly nuint size;
+        public readonly IntPtr hints;                       // PubkeyCredHint[]
+
+        public WauthnPubkeyCredHints(nuint size, IntPtr hints)
+        {
+            this.size = size;
+            this.hints = hints;
+        }
+    }
+
+    [NativeStruct("wauthn_attestation_formats_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal class WauthnAttestationFormats
+    {
+        public readonly nuint size;
+        public readonly IntPtr attestationFormats;          // WauthnConstBuffer[]
+
+        public WauthnAttestationFormats(nuint size, IntPtr attestationFormats)
+        {
+            this.size = size;
+            this.attestationFormats = attestationFormats;
+        }
+    }
+
+    [NativeStruct("wauthn_pubkey_cred_creation_options_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal class WauthnPubkeyCredCreationOptions
+    {
+        public readonly IntPtr rp;                          // WauthnRpEntity*
+        public readonly IntPtr user;                        // WauthnUserEntity*
+        public readonly IntPtr pubkeyCredParams;            // WauthnPubkeyCredParams*
+        public readonly nuint timeout;
+        public readonly IntPtr excludeCredentials;          // WauthnPubkeyCredDescriptors*
+        public readonly IntPtr authenticatorSelection;      // WauthnAuthenticationSelCri*
+        public readonly IntPtr hints;                       // WauthnPubkeyCredHints*
+        public readonly AttestationPref attestation;
+        public readonly IntPtr attestationFormats;          // WauthnAttestationFormats*
+        public readonly IntPtr extensions;                  // WauthnAuthenticationExts*
+        public readonly IntPtr linkedDevice;                // WauthnHybridLinkedData*
+
+        public WauthnPubkeyCredCreationOptions(
+            IntPtr rp,
+            IntPtr user,
+            IntPtr pubkeyCredParams,
+            nuint timeout,
+            IntPtr excludeCredentials,
+            IntPtr authenticatorSelection,
+            IntPtr hints,
+            AttestationPref attestation,
+            IntPtr attestationFormats,
+            IntPtr extensions,
+            IntPtr linkedDevice)
+        {
+            this.rp = rp;
+            this.user = user;
+            this.pubkeyCredParams = pubkeyCredParams;
+            this.timeout = timeout;
+            this.excludeCredentials = excludeCredentials;
+            this.authenticatorSelection = authenticatorSelection;
+            this.hints = hints;
+            this.attestation = attestation;
+            this.attestationFormats = attestationFormats;
+            this.extensions = extensions;
+            this.linkedDevice = linkedDevice;
+        }
+    }
+
+    [NativeStruct("wauthn_pubkey_cred_request_options_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal class WauthnPubkeyCredRequestOptions
+    {
+        public readonly nuint timeout;
+        [MarshalAs(UnmanagedType.LPStr)]
+        public readonly string rpId;
+        public readonly IntPtr allowCredentials;            // WauthnPubkeyCredDescriptors*
+        public readonly UserVerificationRequirement userVerification;
+        public readonly IntPtr hints;                       // WauthnPubkeyCredHints*
+        public readonly AttestationPref attestation;
+        public readonly IntPtr attestationFormats;          // WauthnAttestationFormats*
+        public readonly IntPtr extensions;                  // WauthnAuthenticationExt*
+        public readonly IntPtr linkedDevice;                // WauthnHybridLinkedData*
+
+        public WauthnPubkeyCredRequestOptions(
+            nuint timeout,
+            string rpId,
+            IntPtr allowCredentials,
+            UserVerificationRequirement userVerification,
+            IntPtr hints,
+            AttestationPref attestation,
+            IntPtr attestationFormats,
+            IntPtr extensions,
+            IntPtr linkedDevice)
+        {
+            this.timeout = timeout;
+            this.rpId = rpId;
+            this.allowCredentials = allowCredentials;
+            this.userVerification = userVerification;
+            this.hints = hints;
+            this.attestation = attestation;
+            this.attestationFormats = attestationFormats;
+            this.extensions = extensions;
+            this.linkedDevice = linkedDevice;
+        }
+    }
+
+    [NativeStruct("wauthn_pubkey_credential_attestation_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal class WauthnPubkeyCredentialAttestation
+    {
+        public readonly IntPtr id;                          // WauthnConstBuffer*
+        public readonly PubkeyCredType type;
+        public readonly IntPtr rawId;                       // WauthnConstBuffer*
+        public readonly IntPtr response;                    // WauthnAuthenticatorAttestationResponse*
+        public readonly AuthenticatorAttachment authenticatorAttachment;
+        public readonly IntPtr extensions;                  // WauthnAuthenticationExts*
+        public readonly IntPtr linkedDevice;                // WauthnHybridLinkedData*
+
+        public WauthnPubkeyCredentialAttestation(
+            IntPtr id,
+            PubkeyCredType type,
+            IntPtr rawId,
+            IntPtr response,
+            AuthenticatorAttachment authenticatorAttachment,
+            IntPtr extensions,
+            IntPtr linkedDevice)
+        {
+            this.id = id;
+            this.type = type;
+            this.rawId = rawId;
+            this.response = response;
+            this.authenticatorAttachment = authenticatorAttachment;
+            this.extensions = extensions;
+            this.linkedDevice = linkedDevice;
+        }
+    }
+
+    [NativeStruct("wauthn_pubkey_credential_assertion_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal class WauthnPubkeyCredentialAssertion
+    {
+        public readonly IntPtr id;                          // WauthnConstBuffer*
+        public readonly PubkeyCredType type;
+        public readonly IntPtr rawId;                       // WauthnConstBuffer*
+        public readonly IntPtr response;                    // WauthnAuthenticatorAssertionResponse*
+        public readonly AuthenticatorAttachment authenticatorAttachment;
+        public readonly IntPtr extensions;                  // WauthnAuthenticationExts*
+        public readonly IntPtr linkedDevice;                // WauthnHybridLinkedData*
+
+        public WauthnPubkeyCredentialAssertion(
+            IntPtr id,
+            IntPtr rawId,
+            IntPtr response,
+            AuthenticatorAttachment authenticatorAttachment,
+            IntPtr extensions,
+            IntPtr linkedDevice)
+        {
+            this.id = id;
+            this.rawId = rawId;
+            this.response = response;
+            this.authenticatorAttachment = authenticatorAttachment;
+            this.extensions = extensions;
+            this.linkedDevice = linkedDevice;
+        }
+    }
+
+    [NativeStruct("wauthn_client_data_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal class WauthnClientData
+    {
+        public readonly IntPtr clientDataJson;              // WauthnConstBuffer*
+        public readonly HashAlgorithm hashAlgo;
+
+        public WauthnClientData(IntPtr clientDataJson, HashAlgorithm hashAlgo)
+        {
+            this.clientDataJson = clientDataJson;
+            this.hashAlgo = hashAlgo;
+        }
+    }
+
+    [NativeStruct("wauthn_mc_callbacks_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal class WauthnMcCallbacks
+    {
+        [MarshalAs(UnmanagedType.FunctionPtr)]
+        public readonly WauthnDisplayQrcodeCallback qrcodeCallback;
+        [MarshalAs(UnmanagedType.FunctionPtr)]
+        public readonly WauthnMcOnResponseCallback responseCallback;
+        [MarshalAs(UnmanagedType.FunctionPtr)]
+        public readonly WauthnUpdateLinkedDataCallback linkedDataCallback;
+        public readonly IntPtr userData = IntPtr.Zero;
+
+        public WauthnMcCallbacks(
+            WauthnDisplayQrcodeCallback qrcodeCallback,
+            WauthnMcOnResponseCallback responseCallback,
+            WauthnUpdateLinkedDataCallback linkedDataCallback)
+        {
+            this.qrcodeCallback = qrcodeCallback;
+            this.responseCallback = responseCallback;
+            this.linkedDataCallback = linkedDataCallback;
+        }
+    }
+
+    [NativeStruct("wauthn_ga_callbacks_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal class WauthnGaCallbacks
+    {
+        [MarshalAs(UnmanagedType.FunctionPtr)]
+        public readonly WauthnDisplayQrcodeCallback qrcodeCallback;
+        [MarshalAs(UnmanagedType.FunctionPtr)]
+        public readonly WauthnGaOnResponseCallback responseCallback;
+        [MarshalAs(UnmanagedType.FunctionPtr)]
+        public readonly WauthnUpdateLinkedDataCallback linkedDataCallback;
+        public readonly IntPtr userData = IntPtr.Zero;
+
+        public WauthnGaCallbacks(
+            WauthnDisplayQrcodeCallback qrcodeCallback,
+            WauthnGaOnResponseCallback responseCallback,
+            WauthnUpdateLinkedDataCallback linkedDataCallback)
+        {
+            this.qrcodeCallback = qrcodeCallback;
+            this.responseCallback = responseCallback;
+            this.linkedDataCallback = linkedDataCallback;
+        }
+    }
+    #endregion
+    #region Structs
+
+    [NativeStruct("wauthn_const_buffer_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal struct WauthnConstBuffer
+    {
+        public readonly IntPtr data;                        // byte[]
+        public readonly nuint size;
+
+        public WauthnConstBuffer(IntPtr data, nuint size)
+        {
+            this.data = data;
+            this.size = size;
+        }
+
+        public readonly byte[] ToArray()
+        {
+            var ret = new byte[size];
+            Marshal.Copy(data, ret, 0, (int)size);
+            return ret;
+        }
+    }
+
+    [NativeStruct("wauthn_authenticator_attestation_response_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal struct WauthnAuthenticatorAttestationResponse
+    {
+        public readonly IntPtr clientDataJson;              // WauthnConstBuffer*
+        public readonly IntPtr attestationObject;           // WauthnConstBuffer*
+        public readonly uint transports;
+        public readonly IntPtr authenticatorData;           // WauthnConstBuffer*
+        public readonly IntPtr subjectPubkeyInfo;           // WauthnConstBuffer*
+        public readonly CoseAlgorithm pubkeyAlg;
+
+        public WauthnAuthenticatorAttestationResponse(
+            IntPtr clientDataJson,
+            IntPtr attestationObject,
+            uint transports,
+            IntPtr authenticatorData,
+            IntPtr subjectPubkeyInfo,
+            CoseAlgorithm pubkeyAlg)
+        {
+            this.clientDataJson = clientDataJson;
+            this.attestationObject = attestationObject;
+            this.transports = transports;
+            this.authenticatorData = authenticatorData;
+            this.subjectPubkeyInfo = subjectPubkeyInfo;
+            this.pubkeyAlg = pubkeyAlg;
+        }
+    }
+
+    [NativeStruct("wauthn_authenticator_assertion_response_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal struct WauthnAuthenticatorAssertionResponse
+    {
+        public readonly IntPtr clientDataJson;            // WauthnConstBuffer*
+        public readonly IntPtr authenticatorData;         // WauthnConstBuffer*
+        public readonly IntPtr signature;                 // WauthnConstBuffer*
+        public readonly IntPtr userHandle;                // WauthnConstBuffer*
+        public readonly IntPtr attestationObject;         // WauthnConstBuffer*
+
+        public WauthnAuthenticatorAssertionResponse(
+            IntPtr clientDataJson,
+            IntPtr authenticatorData,
+            IntPtr signature,
+            IntPtr userHandle,
+            IntPtr attestationObject)
+        {
+            this.clientDataJson = clientDataJson;
+            this.authenticatorData = authenticatorData;
+            this.signature = signature;
+            this.userHandle = userHandle;
+            this.attestationObject = attestationObject;
+        }
+    }
+
+    [NativeStruct("wauthn_pubkey_cred_param_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal struct WauthnPubkeyCredParam
+    {
+        public readonly PubkeyCredType type;
+        public readonly CoseAlgorithm alg;
+
+        public WauthnPubkeyCredParam(PubkeyCredType type, CoseAlgorithm alg)
+        {
+            this.type = type;
+            this.alg = alg;
+        }
+    }
+
+    [NativeStruct("wauthn_pubkey_cred_descriptor_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal struct WauthnPubkeyCredDescriptor
+    {
+        public readonly PubkeyCredType type;
+        public readonly IntPtr id;                          // WauthnConstBuffer*
+        public readonly AuthenticatorTransport transport;
+
+        public WauthnPubkeyCredDescriptor(PubkeyCredType type, IntPtr id, AuthenticatorTransport transport)
+        {
+            this.type = type;
+            this.id = id;
+            this.transport = transport;
+        }
+    }
+
+    [NativeStruct("wauthn_authentication_ext_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal struct WauthnAuthenticationExt
+    {
+        public readonly IntPtr extensionId;                 // WauthnConstBuffer*
+        public readonly IntPtr extensionValue;              // WauthnConstBuffer*
+
+        public WauthnAuthenticationExt(IntPtr extensionId, IntPtr extensionValue)
+        {
+            this.extensionId = extensionId;
+            this.extensionValue = extensionValue;
+        }
+    }
+
+    [NativeStruct("wauthn_authentication_exts_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal struct WauthnAuthenticationExts
+    {
+        public readonly nuint size;
+        public readonly IntPtr descriptors;                 // WauthnAuthenticationExt[]
+
+        public WauthnAuthenticationExts(nuint size, IntPtr descriptors)
+        {
+            this.size = size;
+            this.descriptors = descriptors;
+        }
+    }
+
+    [NativeStruct("wauthn_hybrid_linked_data_s", Include="webauthn-types.h", PkgConfig=pkg)]
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    internal struct WauthnHybridLinkedData
+    {
+        public readonly IntPtr contactId;                   // WauthnConstBuffer*
+        public readonly IntPtr linkId;                      // WauthnConstBuffer*
+        public readonly IntPtr linkSecret;                  // WauthnConstBuffer*
+        public readonly IntPtr authenticatorPubkey;         // WauthnConstBuffer*
+        public readonly IntPtr authenticatorName;           // WauthnConstBuffer*
+        public readonly IntPtr signature;                   // WauthnConstBuffer*
+        public readonly IntPtr tunnelServerDomain;          // WauthnConstBuffer*
+        public readonly IntPtr identityKey;                 // WauthnConstBuffer*
+
+        public WauthnHybridLinkedData(
+            IntPtr contactId,
+            IntPtr linkId,
+            IntPtr linkSecret,
+            IntPtr authenticatorPubkey,
+            IntPtr authenticatorName,
+            IntPtr signature,
+            IntPtr tunnelServerDomain,
+            IntPtr identityKey)
+        {
+            this.contactId = contactId;
+            this.linkId = linkId;
+            this.linkSecret = linkSecret;
+            this.authenticatorPubkey = authenticatorPubkey;
+            this.authenticatorName = authenticatorName;
+            this.signature = signature;
+            this.tunnelServerDomain = tunnelServerDomain;
+            this.identityKey = identityKey;
+        }
+    }
+
+    #endregion
+}
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn.csproj b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn.csproj
new file mode 100644 (file)
index 0000000..bb1db21
--- /dev/null
@@ -0,0 +1,11 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>library</OutputType>
+    <TargetFramework>net6.0</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\Tizen\Tizen.csproj" />
+  </ItemGroup>
+</Project>
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn.sln b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn.sln
new file mode 100644 (file)
index 0000000..c045ea9
--- /dev/null
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.002.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.Security.WebAuthn", "Tizen.Security.WebAuthn.csproj", "{CEC2F310-E4BA-4FDB-8B06-8AA73C006ABC}"
+EndProject
+Global
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Debug|Any CPU = Debug|Any CPU
+               Release|Any CPU = Release|Any CPU
+       EndGlobalSection
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {CEC2F310-E4BA-4FDB-8B06-8AA73C006ABC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {CEC2F310-E4BA-4FDB-8B06-8AA73C006ABC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {CEC2F310-E4BA-4FDB-8B06-8AA73C006ABC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {CEC2F310-E4BA-4FDB-8B06-8AA73C006ABC}.Release|Any CPU.Build.0 = Release|Any CPU
+       EndGlobalSection
+       GlobalSection(SolutionProperties) = preSolution
+               HideSolutionNode = FALSE
+       EndGlobalSection
+       GlobalSection(ExtensibilityGlobals) = postSolution
+               SolutionGuid = {52E8477B-782E-40D2-B5F2-9047EE144C1A}
+       EndGlobalSection
+EndGlobal
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticationExtension.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticationExtension.cs
new file mode 100644 (file)
index 0000000..6304611
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *  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;
+using static Tizen.Security.WebAuthn.ErrorFactory;
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// Authenticator extension.
+    /// </summary>
+    /// <since_tizen> 12 </since_tizen>
+    public class AuthenticationExtension
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AuthenticationExtension"/> class.
+        /// </summary>
+        /// <since_tizen> 12 </since_tizen>
+        /// <param name="extensionId">
+        /// Extension Identifier defined in the following registry:
+        /// https://www.iana.org/assignments/webauthn/webauthn.xhtml#webauthn-extension-ids
+        /// </param>
+        /// <param name="extensionValue">Extension value.</param>
+        public AuthenticationExtension(byte[] extensionId, byte[] extensionValue)
+        {
+            ExtensionId = extensionId;
+            ExtensionValue = extensionValue;
+        }
+
+        internal AuthenticationExtension(WauthnAuthenticationExt ext)
+        {
+            ExtensionId = NullSafeMarshal.PtrToArray(ext.extensionId);
+            ExtensionValue = NullSafeMarshal.PtrToArray(ext.extensionValue);
+        }
+
+        /// <summary>
+        /// Extension identifier.
+        /// </summary>
+        public byte[] ExtensionId { get; init; }
+        /// <summary>
+        /// Extension value.
+        /// </summary>
+        public byte[] ExtensionValue { get; init; }
+    }
+}
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticationSelectionCriteria.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticationSelectionCriteria.cs
new file mode 100644 (file)
index 0000000..1f4714c
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ *  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
+ */
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// Authenticator selection criteria.
+    /// </summary>
+    /// <since_tizen> 12 </since_tizen>
+    public class AuthenticationSelectionCriteria
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AuthenticationSelectionCriteria"/> class.
+        /// </summary>
+        /// <since_tizen> 12 </since_tizen>
+        /// <param name="attachment">Authenticator attachment modality.</param>
+        /// <param name="residentKey">Specifies the extent to which the Relying Party desires to create a client-side discoverable credential.</param>
+        /// <param name="requireResidentKey">Relying Parties SHOULD set it to true if, and only if, residentKey is set to required.</param>
+        /// <param name="userVerification">Specifies the Relying Party's requirements regarding user verification.</param>
+        public AuthenticationSelectionCriteria(
+            AuthenticatorAttachment attachment,
+            ResidentKeyRequirement residentKey,
+            bool requireResidentKey,
+            UserVerificationRequirement userVerification)
+        {
+            Attachment = attachment;
+            ResidentKey = residentKey;
+            RequireResidentKey = requireResidentKey;
+            UserVerification = userVerification;
+        }
+
+        /// <summary>
+        /// Authenticator attachment modality.
+        /// </summary>
+        public AuthenticatorAttachment Attachment { get; init; }
+        /// <summary>
+        /// The extent to which the Relying Party desires to create a client-side discoverable credential.
+        /// </summary>
+        public ResidentKeyRequirement ResidentKey { get; init; }
+        /// <summary>
+        /// Whether residentKey is required.
+        /// </summary>
+        public bool RequireResidentKey { get; init; }
+        /// <summary>
+        /// The Relying Party's requirements regarding user verification.
+        /// </summary>
+        public UserVerificationRequirement UserVerification { get; init; }
+    }
+}
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Authenticator.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Authenticator.cs
new file mode 100644 (file)
index 0000000..68707c8
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ *  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;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using static Interop;
+using static Tizen.Security.WebAuthn.ErrorFactory;
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// Public web authentication API.
+    /// </summary>
+    /// <since_tizen> 12 </since_tizen>
+    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;
+
+        #region Public API
+        /// <summary>
+        /// Gets information on authenticator types that the client platform supports.
+        /// </summary>
+        /// <since_tizen> 12 </since_tizen>
+        /// <feature>http://tizen.org/feature/security.webauthn</feature>
+        /// <returns>An enum with the collection of all supported authenticator types.</returns>
+        /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
+        public static AuthenticatorTransport SupportedAuthenticators()
+        {
+            int ret = Libwebauthn.SupportedAuthenticators(out uint supported);
+            CheckErrNThrow(ret, "Get supported authenticators");
+
+            return (AuthenticatorTransport)supported;
+        }
+
+        /// <summary>
+        /// Makes a new web authentication credential and stores it to authenticator.
+        /// </summary>
+        /// <remarks>
+        /// Refer to the following W3C specification for more information.
+        /// https://www.w3.org/TR/webauthn-3/#sctn-createCredential
+        /// </remarks>
+        /// <since_tizen> 12 </since_tizen>
+        /// <privilege>http://tizen.org/privilege/bluetooth</privilege>
+        /// <privilege>http://tizen.org/privilege/internet</privilege>
+        /// <privlevel>public</privlevel>
+        /// <feature>
+        /// http://tizen.org/feature/security.webauthn
+        /// http://tizen.org/feature/network.bluetooth.le
+        /// and at least one of the following:
+        /// http://tizen.org/feature/network.wifi
+        /// http://tizen.org/feature/network.ethernet
+        /// http://tizen.org/feature/network.telephony
+        /// </feature>
+        /// <param name="clientData">UTF-8 encoded JSON serialization of the client data.</param>
+        /// <param name="options">Specifies the desired attributes of the to-be-created public key credential.</param>
+        /// <param name="callbacks">The callback functions to be invoked.</param>
+        /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
+        /// <exception cref="UnauthorizedAccessException">Required privilege is missing.</exception>
+        /// <exception cref="ArgumentException">Input parameter is invalid.</exception>
+        /// <exception cref="InvalidOperationException">Operation invalid in current state.</exception>
+        /// <exception cref="OperationCanceledException">Canceled by a cancel request.</exception>
+        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);
+
+                // Create callback wrappers
+                WrapMcCallbacks(callbacks);
+                AuthenticatorStorage.SetDataForMakeCredential(clientData, options);
+
+                int ret = Libwebauthn.MakeCredential(
+                    AuthenticatorStorage.WauthnClientData,
+                    AuthenticatorStorage.WauthnPubkeyCredCreationOptions,
+                    _wauthnMcCallbacks);
+                CheckErrNThrow(ret, "Make Credential");
+            }
+            catch
+            {
+                Cleanup();
+                throw;
+            }
+        }
+
+        /// <summary>
+        /// Gets assertion from the authenticator.
+        /// </summary>
+        /// <remarks>
+        /// Refer to the following W3C specification for more information.
+        /// https://www.w3.org/TR/webauthn-3/#sctn-getAssertion
+        /// </remarks>
+        /// <since_tizen> 12 </since_tizen>
+        /// <privilege>http://tizen.org/privilege/bluetooth</privilege>
+        /// <privilege>http://tizen.org/privilege/internet</privilege>
+        /// <privlevel>public</privlevel>
+        /// <feature>
+        /// http://tizen.org/feature/security.webauthn
+        /// http://tizen.org/feature/network.bluetooth.le
+        /// and at least one of the following:
+        /// http://tizen.org/feature/network.wifi
+        /// http://tizen.org/feature/network.ethernet
+        /// http://tizen.org/feature/network.telephony
+        /// </feature>
+        /// <param name="clientData">UTF-8 encoded JSON serialization of the client data.</param>
+        /// <param name="options">Specifies the desired attributes of the public key credential to discover.</param>
+        /// <param name="callbacks">The callback functions to be invoked.</param>
+        /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
+        /// <exception cref="UnauthorizedAccessException">Required privilege is missing.</exception>
+        /// <exception cref="ArgumentException">Input parameter is invalid.</exception>
+        /// <exception cref="InvalidOperationException">Operation invalid in current state.</exception>
+        /// <exception cref="OperationCanceledException">Canceled by a cancel request.</exception>
+        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);
+
+                // Create callback wrappers
+                WrapGaCallbacks(callbacks);
+                AuthenticatorStorage.SetDataForGetAssertion(clientData, options);
+
+                int ret = Libwebauthn.GetAssertion(
+                    AuthenticatorStorage.WauthnClientData,
+                    AuthenticatorStorage.WauthnPubkeyCredRequestOptions,
+                    _wauthnGaCallbacks);
+                CheckErrNThrow(ret, "Get Assertion");
+            }
+            catch
+            {
+                Cleanup();
+                throw;
+            }
+        }
+
+        /// <summary>
+        /// Stops the previous <see cref="MakeCredential"/> or <see cref="GetAssertion"/> call.
+        /// </summary>
+        /// <since_tizen> 12 </since_tizen>
+        /// <feature>http://tizen.org/feature/security.webauthn</feature>
+        /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
+        /// <exception cref="InvalidOperationException">Not allowed in the current context.</exception>
+        public static void Cancel()
+        {
+            int ret = Libwebauthn.Cancel();
+            CheckErrNThrow(ret, "Cancel operation");
+        }
+
+        #endregion
+        #region Helper methods
+
+        private static void SetApiVersion(int apiVersionNumber)
+        {
+            int ret = Libwebauthn.SetApiVersion(apiVersionNumber);
+            CheckErrNThrow(ret, "Set API version");
+            _apiVersionSet = true;
+        }
+        private static void WrapMcCallbacks(MakeCredentialCallbacks callbacks)
+        {
+            _userData = callbacks.UserData;
+
+            void qrCodeWrapper(string qrContents, IntPtr _)
+            {
+                callbacks.QrcodeCallback(qrContents, _userData);
+            }
+
+            void onResponseWrapper(WauthnPubkeyCredentialAttestation pubkeyCred, WauthnError result, IntPtr _)
+            {
+                PubkeyCredAttestation pubkeyCredManaged = pubkeyCred is not null ? new(pubkeyCred) : null;
+                callbacks.ResponseCallback(pubkeyCredManaged, result, _userData);
+
+                if (result != WauthnError.None)
+                    Cleanup();
+            }
+
+            void linkedDataWrapper(IntPtr linkedData, WauthnError result, IntPtr _)
+            {
+                HybridLinkedData linkedDataManaged = linkedData != IntPtr.Zero ? new(Marshal.PtrToStructure<WauthnHybridLinkedData>(linkedData)) : null;
+                callbacks.LinkedDataCallback(linkedDataManaged, result, _userData);
+
+                if (result != WauthnError.NoneAndWait)
+                    Cleanup();
+            }
+
+            _qrCodeCallback = new WauthnDisplayQrcodeCallback(qrCodeWrapper);
+            _mcResponseCallback = new WauthnMcOnResponseCallback(onResponseWrapper);
+            _linkedDataCallback = new WauthnUpdateLinkedDataCallback(linkedDataWrapper);
+
+            _wauthnMcCallbacks = new WauthnMcCallbacks(_qrCodeCallback, _mcResponseCallback, _linkedDataCallback);
+        }
+
+        private static void WrapGaCallbacks(GetAssertionCallbacks callbacks)
+        {
+            _userData = callbacks.UserData;
+
+            void qrCodeWrapper(string qrContents, IntPtr _)
+            {
+                callbacks.QrcodeCallback(qrContents, _userData);
+            }
+
+            void onResponseWrapper(WauthnPubkeyCredentialAssertion pubkeyCred, WauthnError result, IntPtr _)
+            {
+                PubkeyCredAssertion pubkeyCredManaged = pubkeyCred is not null ? new(pubkeyCred) : null;
+                callbacks.ResponseCallback(pubkeyCredManaged, result, _userData);
+
+                if (result != WauthnError.None)
+                    Cleanup();
+            }
+
+            void linkedDataWrapper(IntPtr linkedData, WauthnError result, IntPtr _)
+            {
+                HybridLinkedData linkedDataManaged = linkedData != IntPtr.Zero ? new(Marshal.PtrToStructure<WauthnHybridLinkedData>(linkedData)) : null;
+                callbacks.LinkedDataCallback(linkedDataManaged, result, _userData);
+
+                if (result != WauthnError.NoneAndWait)
+                    Cleanup();
+            }
+            _qrCodeCallback = new WauthnDisplayQrcodeCallback(qrCodeWrapper);
+            _gaResponseCallback = new WauthnGaOnResponseCallback(onResponseWrapper);
+            _linkedDataCallback = new WauthnUpdateLinkedDataCallback(linkedDataWrapper);
+
+            _wauthnGaCallbacks = new WauthnGaCallbacks(_qrCodeCallback, _gaResponseCallback, _linkedDataCallback);
+        }
+
+        private static void CheckPreconditions()
+        {
+            if (!_apiVersionSet)
+                SetApiVersion(API_VERSION_NUMBER);
+            if (_busy)
+                throw new InvalidOperationException("Authenticator busy");
+
+            _busy = true;
+        }
+
+        private static void Cleanup()
+        {
+            _busy = false;
+            AuthenticatorStorage.Cleanup();
+        }
+
+        #endregion
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorAssertionResponse.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorAssertionResponse.cs
new file mode 100644 (file)
index 0000000..c8bc672
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *  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
+{
+    /// <summary>
+    /// The response of GetAssertion().
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#authenticatorassertionresponse
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public class AuthenticatorAssertionResponse
+    {
+        internal AuthenticatorAssertionResponse(WauthnAuthenticatorAssertionResponse wauthnResponse)
+        {
+            ClientDataJson = NullSafeMarshal.PtrToArray(wauthnResponse.clientDataJson);
+            AuthenticatorData = NullSafeMarshal.PtrToArray(wauthnResponse.attestationObject);
+            Signature = NullSafeMarshal.PtrToArray(wauthnResponse.signature);
+            UserHandle = NullSafeMarshal.PtrToArray(wauthnResponse.userHandle);
+            AttestationObject = NullSafeMarshal.PtrToArray(wauthnResponse.attestationObject);
+        }
+
+        /// <summary>
+        /// JSON-compatible serialization of client data.
+        /// </summary>
+        public byte[] ClientDataJson { get; init; }
+        /// <summary>
+        /// The authenticator data contained within attestation_object.
+        /// For more information, refer to https://www.w3.org/TR/webauthn-3/#sctn-authenticator-data
+        /// </summary>
+        public byte[] AuthenticatorData { get; init; }
+        /// <summary>
+        /// The raw signature returned from the authenticator.
+        /// For more information, refer to https://www.w3.org/TR/webauthn-3/#sctn-op-get-assertion
+        /// </summary>
+        public byte[] Signature { get; init; }
+        /// <summary>
+        /// The user handle returned from the authenticator,
+        /// or null if the authenticator did not return a user handle.
+        /// </summary>
+        public byte[] UserHandle { get; init; }
+        /// <summary>
+        /// This OPTIONAL attribute contains an attestation object,
+        /// if the authenticator supports attestation in assertions.
+        /// </summary>
+        public byte[] AttestationObject { get; init; }
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorAttestationResponse.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorAttestationResponse.cs
new file mode 100644 (file)
index 0000000..e4209c0
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ *  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
+{
+    /// <summary>
+    /// The response of MakeCredential().
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#authenticatorattestationresponse
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public class AuthenticatorAttestationResponse
+    {
+        internal AuthenticatorAttestationResponse(WauthnAuthenticatorAttestationResponse wauthnResponse)
+        {
+            ClientDataJson = NullSafeMarshal.PtrToArray(wauthnResponse.clientDataJson);
+            AttestationObject = NullSafeMarshal.PtrToArray(wauthnResponse.attestationObject);
+            Transports = (AuthenticatorTransport)wauthnResponse.transports;
+            AuthenticatorData = NullSafeMarshal.PtrToArray(wauthnResponse.authenticatorData);
+            SubjectPubkeyInfo = NullSafeMarshal.PtrToArray(wauthnResponse.subjectPubkeyInfo);
+            PubkeyAlg = wauthnResponse.pubkeyAlg;
+        }
+
+        /// <summary>
+        /// JSON-compatible serialization of client data.
+        /// </summary>
+        public byte[] ClientDataJson { get; init; }
+        /// <summary>
+        /// The CBOR encoded Attestation Object to be returned to the RP.
+        /// </summary>
+        public byte[] AttestationObject { get; init; }
+        /// <summary>
+        /// To represent multiple transports, <see cref="AuthenticatorTransport"/> can be ORed multiple times.
+        /// </summary>
+        public AuthenticatorTransport Transports { get; init; }
+        /// <summary>
+        /// The authenticator data contained within attestation_object.
+        /// For more information, refer to https://www.w3.org/TR/webauthn-3/#sctn-authenticator-data
+        /// </summary>
+        public byte[] AuthenticatorData { get; init; }
+        /// <summary>
+        /// DER SubjectPublicKeyInfo of the new credential, or null if this is not available.
+        /// For more information, refer to https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.7
+        /// </summary>
+        public byte[] SubjectPubkeyInfo { get; init; }
+        /// <summary>
+        /// The COSEAlgorithmIdentifier of the new credential.
+        /// </summary>
+        public CoseAlgorithm PubkeyAlg { get; init; }
+
+    }
+}
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorStorage.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/AuthenticatorStorage.cs
new file mode 100644 (file)
index 0000000..a47c671
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ *  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;
+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
+    {
+        #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();
+        #endregion
+
+        public static void SetDataForMakeCredential(ClientData clientData, PubkeyCredCreationOptions options)
+        {
+            CopyClientData(clientData);
+            CopyCredCreationOptions(options);
+        }
+
+        public static void SetDataForGetAssertion(ClientData clientData, PubkeyCredRequestOptions options)
+        {
+            CopyClientData(clientData);
+            CopyCredRequestOptions(options);
+        }
+
+        public static void Cleanup()
+        {
+            CleanupArray(_credentialsIdUnmanagedDataArray);
+            CleanupArray(_credentialsIdUnmanagedConstBufferArray);
+            CleanupArray(_attestationFormatsUnmanagedDataArray);
+            CleanupArray(_attestationFormatsUnmanagedConstBufferArray);
+            CleanupArray(_extensionIdUnmanagedDataArray);
+            CleanupArray(_extensionIdUnmanagedConstBufferArray);
+            CleanupArray(_extensionValueUnmanagedDataArray);
+            CleanupArray(_extensionValueUnmanagedConstBufferArray);
+
+            _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();
+            _attestationFormatsUnmanaged.Dispose();
+            _extensionsArrayUnmanaged.Dispose();
+            _extensionsUnmanaged.Dispose();
+            _contactIdDataUnmanaged.Dispose();
+            _contactIdUnmanaged.Dispose();
+            _linkIdDataUnmanaged.Dispose();
+            _linkIdUnmanaged.Dispose();
+            _linkSecretDataUnmanaged.Dispose();
+            _linkSecretUnmanaged.Dispose();
+            _authenticatorPubkeyDataUnmanaged.Dispose();
+            _authenticatorPubkeyUnmanaged.Dispose();
+            _authenticatorNameDataUnmanaged.Dispose();
+            _authenticatorNameUnmanaged.Dispose();
+            _signatureDataUnmanaged.Dispose();
+            _signatureUnmanaged.Dispose();
+            _tunnelServerDomainDataUnmanaged.Dispose();
+            _tunnelServerDomainUnmanaged.Dispose();
+            _identityKeyDataUnmanaged.Dispose();
+            _identityKeyUnmanaged.Dispose();
+            _linkedDeviceUnmanaged.Dispose();
+        }
+
+        private static 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<PubkeyCredParam> 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<PubkeyCredDescriptor> credentials)
+        {
+            if (credentials is null || !credentials.Any())
+                return;
+
+            var credentialsCount = credentials.Count();
+            _credentialsIdUnmanagedDataArray = new UnmanagedMemory[credentialsCount];
+            _credentialsIdUnmanagedConstBufferArray = new UnmanagedMemory[credentialsCount];
+            var credentialsStructArray = new WauthnPubkeyCredDescriptor[credentialsCount];
+
+            for (int i = 0; i < credentialsCount; i++)
+            {
+                PubkeyCredDescriptor descriptor = credentials.ElementAt(i);
+                _credentialsIdUnmanagedDataArray[i] = UnmanagedMemory.PinArray(descriptor.Id);
+                var credentialIdUnmanagedConstBuffer = new UnmanagedMemory(new WauthnConstBuffer(_credentialsIdUnmanagedDataArray[i], (nuint)descriptor.Id.Length));
+                _credentialsIdUnmanagedConstBufferArray[i] = credentialIdUnmanagedConstBuffer;
+                credentialsStructArray[i] = new WauthnPubkeyCredDescriptor(descriptor.Type, credentialIdUnmanagedConstBuffer, descriptor.Transport);
+            }
+            _credentialsDescriptorsUnmanaged = UnmanagedMemory.PinArray(credentialsStructArray);
+            _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<PubkeyCredHint> hints)
+        {
+            if (hints is null || !hints.Any())
+                return;
+
+            int[] hintsArray = hints.Select((PubkeyCredHint hint) => (int)hint).ToArray();
+            _hintsArrayUnmanaged = UnmanagedMemory.PinArray(hintsArray);
+            _hintsUnmanaged = new UnmanagedMemory(new WauthnPubkeyCredHints((nuint)hintsArray.Length, _hintsArrayUnmanaged));
+        }
+
+        private static void CopyAttestationFormats(IEnumerable<byte[]> attestationFormats)
+        {
+            if (attestationFormats is null || !attestationFormats.Any())
+                return;
+
+            var attestationFormatsCount = attestationFormats.Count();
+            _attestationFormatsUnmanagedDataArray = new UnmanagedMemory[attestationFormatsCount];
+            _attestationFormatsUnmanagedConstBufferArray = new UnmanagedMemory[attestationFormatsCount];
+            var attestationFormatConstBufferStructArray = new WauthnConstBuffer[attestationFormatsCount];
+
+
+            for (int i = 0; i < attestationFormatsCount; i++)
+            {
+                byte[] attestationFormat = attestationFormats.ElementAt(i);
+                _attestationFormatsUnmanagedDataArray[i] = UnmanagedMemory.PinArray(attestationFormat);
+                attestationFormatConstBufferStructArray[i] = new WauthnConstBuffer(_attestationFormatsUnmanagedDataArray[i], (nuint)attestationFormat.Length);
+                _attestationFormatsUnmanagedConstBufferArray[i] = new UnmanagedMemory(attestationFormatConstBufferStructArray[i]);
+            }
+            _attestationFormatsArrayUnmanaged = UnmanagedMemory.PinArray(attestationFormatConstBufferStructArray);
+            _attestationFormatsUnmanaged = new UnmanagedMemory(new WauthnAttestationFormats((nuint)attestationFormatsCount, _attestationFormatsArrayUnmanaged));
+        }
+
+        private static void CopyExtensions(IEnumerable<AuthenticationExtension> extensions)
+        {
+            if (extensions is null || !extensions.Any())
+                return;
+
+            var extensionCount = extensions.Count();
+            var extensionStructArray = new WauthnAuthenticationExt[extensionCount];
+            _extensionIdUnmanagedDataArray = new UnmanagedMemory[extensionCount];
+            _extensionIdUnmanagedConstBufferArray = new UnmanagedMemory[extensionCount];
+            _extensionValueUnmanagedDataArray = new UnmanagedMemory[extensionCount];
+            _extensionValueUnmanagedConstBufferArray = new UnmanagedMemory[extensionCount];
+
+            for (int i = 0; i < extensionCount; i++)
+            {
+                AuthenticationExtension ext = extensions.ElementAt(i);
+                _extensionIdUnmanagedDataArray[i] = UnmanagedMemory.PinArray(ext.ExtensionId);
+                var extensionIdUnmanagedConstBuffer = new UnmanagedMemory(new WauthnConstBuffer(_extensionIdUnmanagedDataArray[i], (nuint)ext.ExtensionId.Length));
+                _extensionIdUnmanagedConstBufferArray[i] = extensionIdUnmanagedConstBuffer;
+
+                _extensionValueUnmanagedDataArray[i] = UnmanagedMemory.PinArray(ext.ExtensionValue);
+                var extensionValueUnmanagedConstBuffer = new UnmanagedMemory(new WauthnConstBuffer(_extensionValueUnmanagedDataArray[i], (nuint)ext.ExtensionValue.Length));
+                _extensionValueUnmanagedConstBufferArray[i] = extensionValueUnmanagedConstBuffer;
+
+                extensionStructArray[i] = new WauthnAuthenticationExt(extensionIdUnmanagedConstBuffer, extensionValueUnmanagedConstBuffer);
+            }
+            _extensionsArrayUnmanaged = UnmanagedMemory.PinArray(extensionStructArray);
+            _extensionsUnmanaged = new UnmanagedMemory(new WauthnAuthenticationExts((nuint)extensionCount, _extensionsArrayUnmanaged));
+        }
+
+        private static void CopyLinkedDevice(HybridLinkedData linkedDevice)
+        {
+            if (linkedDevice is null)
+                return;
+
+            _contactIdDataUnmanaged = UnmanagedMemory.PinArray(linkedDevice.ContactId);
+            _contactIdUnmanaged = new UnmanagedMemory(new WauthnConstBuffer(_contactIdDataUnmanaged, (nuint)linkedDevice.ContactId.Length));
+
+            _linkIdDataUnmanaged = UnmanagedMemory.PinArray(linkedDevice.LinkId);
+            _linkIdUnmanaged = new UnmanagedMemory(new WauthnConstBuffer(_linkIdDataUnmanaged, (nuint)linkedDevice.LinkId.Length));
+
+            _linkSecretDataUnmanaged = UnmanagedMemory.PinArray(linkedDevice.LinkSecret);
+            _linkSecretUnmanaged = new UnmanagedMemory(new WauthnConstBuffer(_linkSecretDataUnmanaged, (nuint)linkedDevice.LinkSecret.Length));
+
+            _authenticatorPubkeyDataUnmanaged = UnmanagedMemory.PinArray(linkedDevice.AuthenticatorPubkey);
+            _authenticatorPubkeyUnmanaged = new UnmanagedMemory(new WauthnConstBuffer(_authenticatorPubkeyDataUnmanaged, (nuint)linkedDevice.AuthenticatorPubkey.Length));
+
+            _authenticatorNameDataUnmanaged = UnmanagedMemory.PinArray(linkedDevice.AuthenticatorName);
+            _authenticatorNameUnmanaged = new UnmanagedMemory(new WauthnConstBuffer(_authenticatorNameDataUnmanaged, (nuint)linkedDevice.AuthenticatorName.Length));
+
+            _signatureDataUnmanaged = UnmanagedMemory.PinArray(linkedDevice.Signature);
+            _signatureUnmanaged = new UnmanagedMemory(new WauthnConstBuffer(_signatureDataUnmanaged, (nuint)linkedDevice.Signature.Length));
+
+            _tunnelServerDomainDataUnmanaged = UnmanagedMemory.PinArray(linkedDevice.TunnelServerDomain);
+            _tunnelServerDomainUnmanaged = new UnmanagedMemory(new WauthnConstBuffer(_tunnelServerDomainDataUnmanaged, (nuint)linkedDevice.TunnelServerDomain.Length));
+
+            _identityKeyDataUnmanaged = UnmanagedMemory.PinArray(linkedDevice.IdentityKey);
+            _identityKeyUnmanaged = new UnmanagedMemory(new WauthnConstBuffer(_identityKeyDataUnmanaged, (nuint)linkedDevice.IdentityKey.Length));
+
+            _linkedDeviceUnmanaged = new UnmanagedMemory(new WauthnHybridLinkedData(
+                _contactIdUnmanaged,
+                _linkIdUnmanaged,
+                _linkSecretUnmanaged,
+                _authenticatorPubkeyUnmanaged,
+                _authenticatorNameUnmanaged,
+                _signatureUnmanaged,
+                _tunnelServerDomainUnmanaged,
+                _identityKeyUnmanaged));
+
+            if (_linkedDeviceUnmanaged == IntPtr.Zero)
+                throw new TimeoutException("linked null");
+        }
+
+        public static void CleanupArray(UnmanagedMemory[] array)
+        {
+            if (array is null)
+                return;
+            foreach (var memory in array)
+                memory.Dispose();
+        }
+
+        public static WauthnClientData WauthnClientData { get; private set; }
+        public static WauthnPubkeyCredCreationOptions WauthnPubkeyCredCreationOptions { get; private set; }
+        public static WauthnPubkeyCredRequestOptions WauthnPubkeyCredRequestOptions { get; private set; }
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/ClientData.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/ClientData.cs
new file mode 100644 (file)
index 0000000..609cfb4
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ *  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 Tizen.Security.WebAuthn.ErrorFactory;
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// Client data JSON.
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification about how to encode jsonData.
+    /// https://www.w3.org/TR/webauthn-3/#collectedclientdata-json-compatible-serialization-of-client-data
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public class ClientData
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ClientData"/> class.
+        /// </summary>
+        /// <since_tizen> 12 </since_tizen>
+        /// <param name="jsonData">UTF-8 encoded JSON serialization of the client data.</param>
+        /// <param name="hashAlgo">Hash algorithm used to hash the JsonData property.</param>
+        public ClientData(byte[] jsonData, HashAlgorithm hashAlgo)
+        {
+            JsonData = jsonData;
+            HashAlgo = hashAlgo;
+        }
+
+        /// <summary>
+        /// UTF-8 encoded JSON serialization of the client data.
+        /// </summary>
+        public byte[] JsonData { get; init; }
+        /// <summary>
+        /// Hash algorithm used to hash the JsonData property.
+        /// </summary>
+        public HashAlgorithm HashAlgo{ get; init; }
+    }
+}
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/AttestationPref.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/AttestationPref.cs
new file mode 100644 (file)
index 0000000..72e82da
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *  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
+ */
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// WebAuthn attestation preference.
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#enumdef-attestationconveyancepreference
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public enum AttestationPref
+    {
+        /// <summary>
+        /// Relying party not interested in authenticator attestation.
+        /// </summary>
+        None                        = 0,
+        /// <summary>
+        /// Indirect attestation preferred.
+        /// </summary>
+        Indirect                    = 1,
+        /// <summary>
+        /// Direct attestation preferred.
+        /// </summary>
+        Direct                      = 2,
+        /// <summary>
+        /// Enterprise attestation preferred.
+        /// </summary>
+        Enterprise                  = 3,
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/AuthenticatorAttachment.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/AuthenticatorAttachment.cs
new file mode 100644 (file)
index 0000000..ca0b6d8
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *  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
+ */
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// WebAuthn authentication attachment value.
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#enumdef-authenticatorattachment
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public enum AuthenticatorAttachment
+    {
+        /// <summary>
+        /// No attachment.
+        /// </summary>
+        None                        = 0,
+        /// <summary>
+        /// Platform attachment.
+        /// </summary>
+        Platform                    = 1,
+        /// <summary>
+        /// Cross-platform attachment.
+        /// </summary>
+        CrossPlatform               = 2,
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/AuthenticatorTransport.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/AuthenticatorTransport.cs
new file mode 100644 (file)
index 0000000..1204281
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ *  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;
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// WebAuthn authenticator transports.
+    /// </summary>
+    /// <remarks>
+    /// Multiple transport values can be combined using bit-wise operation.
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#enum-transport
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    [Flags]
+    public enum AuthenticatorTransport : uint
+    {
+        /// <summary>
+        /// No transport specified.
+        /// </summary>
+        None                        = 0x00000000,
+        /// <summary>
+        /// Authenticator reachable over USB.
+        /// </summary>
+        Usb                         = 0x00000001,
+        /// <summary>
+        /// Authenticator reachable over NFC.
+        /// </summary>
+        Nfc                         = 0x00000002,
+        /// <summary>
+        /// Authenticator reachable over BLE.
+        /// </summary>
+        Ble                         = 0x00000004,
+        /// <summary>
+        /// Authenticator reachable using Smart Card.
+        /// </summary>
+        Smartcard                   = 0x00000008,
+        /// <summary>
+        /// Authenticator reachable using a combination of mechanisms.
+        /// </summary>
+        Hybrid                      = 0x00000010,
+        /// <summary>
+        /// Authenticator reachable using a client device-specific transport.
+        /// </summary>
+        Internal                    = 0x00000020,
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/CoseAlgorithm.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/CoseAlgorithm.cs
new file mode 100644 (file)
index 0000000..cf88cde
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ *  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
+ */
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// WebAuthn COSE (CBOR Object Signing and Encryption) algorithms.
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#sctn-alg-identifier
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public enum CoseAlgorithm
+    {
+        /// <summary>
+        /// ES256
+        /// </summary>
+        EcdsaP256WithSha256         = -7,
+        /// <summary>
+        /// ES384
+        /// </summary>
+        EcdsaP384WithSha384         = -35,
+        /// <summary>
+        /// ES512
+        /// </summary>
+        EcdsaP521WithSha512         = -36,
+        /// <summary>
+        /// EdDSA
+        /// </summary>
+        Eddsa                       = -8,
+        /// <summary>
+        /// PS256
+        /// </summary>
+        RsaPssWithSha256            = -37,
+        /// <summary>
+        /// PS384
+        /// </summary>
+        RsaPssWithSha384            = -38,
+        /// <summary>
+        /// PS512
+        /// </summary>
+        RsaPssWithSha512            = -39,
+        /// <summary>
+        /// RS256
+        /// </summary>
+        RsaSsaPkcs1V1_5WithSha256   = -257,
+        /// <summary>
+        /// RS384
+        /// </summary>
+        RsaSsaPkcs1V1_5WithSha384   = -258,
+        /// <summary>
+        /// RS512
+        /// </summary>
+        RsaSsaPkcs1V1_5WithSha512   = -259,
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/HashAlogithm.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/HashAlogithm.cs
new file mode 100644 (file)
index 0000000..1ca2a48
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *  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
+ */
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// WebAuthn hash algorithms.
+    /// Currently one hash algorithm is used, namely "SHA-256".
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#collectedclientdata-hash-of-the-serialized-client-data
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public enum HashAlgorithm
+    {
+        /// <summary>
+        /// SHA-256
+        /// </summary>
+        Sha256                      = 1,
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/PubkeyCredHint.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/PubkeyCredHint.cs
new file mode 100644 (file)
index 0000000..cc09494
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *  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
+ */
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// WebAuthn public key credential hint value.
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#enumdef-publickeycredentialhints
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public enum PubkeyCredHint
+    {
+        /// <summary>
+        /// None.
+        /// </summary>
+        None                        = 0,
+        /// <summary>
+        /// Physical security key.
+        /// </summary>
+        SecurityKey                 = 1,
+        /// <summary>
+        /// Platform authenticator attached to a client device.
+        /// </summary>
+        ClientDevice                = 2,
+        /// <summary>
+        /// General-purpose authenticator.
+        /// </summary>
+        Hybrid                      = 3, // Hybrid
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/PubkeyCredType.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/PubkeyCredType.cs
new file mode 100644 (file)
index 0000000..6a4207a
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *  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
+ */
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// WebAuthn credential type.
+    /// Currently one credential type is defined, namely "public-key".
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#enumdef-publickeycredentialtype
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public enum PubkeyCredType
+    {
+        /// <summary>
+        /// Public-key.
+        /// </summary>
+        PublicKey                   = 1,
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/ResidentKeyRequirement.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/ResidentKeyRequirement.cs
new file mode 100644 (file)
index 0000000..8def5ff
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *  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
+ */
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// WebAuthn resident key requirement value.
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#enum-residentKeyRequirement
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public enum ResidentKeyRequirement
+    {
+        /// <summary>
+        /// None.
+        /// </summary>
+        None                        = 0,
+        /// <summary>
+        /// Resident key discouraged.
+        /// </summary>
+        Discouraged                 = 1,
+        /// <summary>
+        /// Resident key preferred.
+        /// </summary>
+        Preferred                   = 2,
+        /// <summary>
+        /// Resident key required.
+        /// </summary>
+        Required                    = 3,
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/UserVerificationRequirement.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/UserVerificationRequirement.cs
new file mode 100644 (file)
index 0000000..9052bd7
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *  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
+ */
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// WebAuthn user verification requirement value.
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#enumdef-userverificationrequirement
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public enum UserVerificationRequirement
+    {
+        /// <summary>
+        /// None.
+        /// </summary>
+        None                        = 0,
+        /// <summary>
+        /// User verification required.
+        /// </summary>
+        Required                    = 1,
+        /// <summary>
+        /// User verification preferred.
+        /// </summary>
+        Preferred                   = 2,
+        /// <summary>
+        /// User verification discouraged.
+        /// </summary>
+        Discouraged                 = 3,
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/WauthnError.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/Enums/WauthnError.cs
new file mode 100644 (file)
index 0000000..abd7e81
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ *  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 Tizen.Internals.Errors;
+using static Tizen.Security.WebAuthn.ErrorFactory;
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// WebAuthn error code.
+    /// </summary>
+    public enum WauthnError
+    {
+        /// <summary>
+        /// Successful.
+        /// </summary>
+        None                    = ErrorCode.None,
+        /// <summary>
+        /// Unknown error.
+        /// </summary>
+        Unknown                 = ErrorCode.Unknown,
+        /// <summary>
+        /// Invalid function parameter.
+        /// </summary>
+        InvalidParameter        = ErrorCode.InvalidParameter,
+        /// <summary>
+        /// Permission denied.
+        /// </summary>
+        PermissionDenied        = ErrorCode.PermissionDenied,
+        /// <summary>
+        /// Not supported operation.
+        /// </summary>
+        NotSupported            = ErrorCode.NotSupported,
+        /// <summary>
+        /// Memory error.
+        /// </summary>
+        OutOfMemory             = ErrorCode.OutOfMemory,
+        /// <summary>
+        /// Canceled by cancel request.
+        /// </summary>
+        Canceled                = ErrorCode.Canceled,
+        /// <summary>
+        /// Timeout.
+        /// </summary>
+        TimedOut                = ErrorCode.TimedOut,
+        /// <summary>
+        /// Authenticator is uncontactable.
+        /// </summary>
+        ConnectionRefused       = ErrorCode.ConnectionRefused,
+        /// <summary>
+        /// Successful and needs to wait for other result.
+        /// </summary>
+        NoneAndWait             = TizenErrorWebAuthn | 0x01,
+        /// <summary>
+        /// Not allowed in the current context.
+        /// </summary>
+        NotAllowed              = TizenErrorWebAuthn | 0x02,
+        /// <summary>
+        /// Invalid State.
+        /// </summary>
+        InvalidState            = TizenErrorWebAuthn | 0x03,
+        /// <summary>
+        /// Encoding operation failed.
+        /// </summary>
+        EncodingFailed          = TizenErrorWebAuthn | 0x04,
+        /// <summary>
+        /// Socket error.
+        /// </summary>
+        Socket                  = TizenErrorWebAuthn | 0x05,
+        /// <summary>
+        /// Socket operation on non-socket error.
+        /// </summary>
+        NoSuchDevice            = TizenErrorWebAuthn | 0x06,
+        /// <summary>
+        /// Socket access denied.
+        /// </summary>
+        AccessDenied            = TizenErrorWebAuthn | 0x07,
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/ErrorFactory.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/ErrorFactory.cs
new file mode 100644 (file)
index 0000000..0015d13
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *  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;
+using System.Runtime.CompilerServices;
+using Tizen.Internals.Errors;
+
+namespace Tizen.Security.WebAuthn
+{
+    internal static class ErrorFactory
+    {
+        internal const int TizenErrorWebAuthn = -0x03100000;
+        private const string LogTag = "Tizen.Security.WebAuthn";
+
+        internal static void CheckErrNThrow(int err, string msg)
+        {
+            string fullMessage = $"[{LogTag}] {msg}, error={ErrorFacts.GetErrorMessage(err)}";
+
+            switch (err)
+            {
+                case (int)WauthnError.None:
+                    return;
+                case (int)WauthnError.InvalidParameter:
+                    throw new ArgumentException(fullMessage);
+                case (int)WauthnError.PermissionDenied:
+                    throw new UnauthorizedAccessException(fullMessage);
+                case (int)WauthnError.NotSupported:
+                    throw new NotSupportedException(fullMessage);
+                case (int)WauthnError.Canceled:
+                    throw new OperationCanceledException(fullMessage);
+                case (int)WauthnError.TimedOut:
+                    throw new TimeoutException(fullMessage);
+                default:
+                    throw new InvalidOperationException(fullMessage);
+            }
+        }
+
+        internal static void CheckNullNThrow(object obj, [CallerArgumentExpression("obj")] string name = null)
+        {
+            if (obj is null)
+                throw new ArgumentNullException(name);
+        }
+    }
+}
+
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/GetAssertionCallbacks.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/GetAssertionCallbacks.cs
new file mode 100644 (file)
index 0000000..b3a79da
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *  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;
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// Callback function list used to get assertion with <see cref="Authenticator.GetAssertion"/>.
+    /// </summary>
+    /// <since_tizen> 12 </since_tizen>
+    public class GetAssertionCallbacks
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="GetAssertionCallbacks"/> class.
+        /// </summary>
+        /// <remarks>
+        /// Provided callbacks MUST NOT THROW.
+        /// </remarks>
+        /// <since_tizen> 12 </since_tizen>
+        /// <param name="qrcodeCallback">
+        /// Callback function for displaying QR code.
+        /// The qr_contents are encoded as you can see in the encodeQRContents() function of the
+        /// FIDO specification:
+        /// https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#hybrid-qr-initiated.
+        /// The qr_contents is encoded like "FIDO:/0254318383..........7406596245".
+        /// The image to be displayed shall be created from qr_contents
+        /// with media vision API (<see cref="Tizen.Multimedia.Vision.BarcodeGenerator"/>).
+        /// If the request does not need to display a QR code
+        /// then this callback function won't be invoked.
+        /// </param>
+        /// <param name="responseCallback">
+        /// Callback function for getting the final response.
+        /// Invoked when the response for the GetAssertion request need to be returned.
+        /// The result of the GetAssertion request may be one of the following:
+        ///  * <see cref="WauthnError.None"/> if the request is completed well,
+        ///  * <see cref="WauthnError.Canceled"/> if the request is cancelled by a Cancel() request.
+        ///  * <see cref="WauthnError.InvalidState"/> if the server entered invalid state. Known causes:
+        ///    - proxy issues,
+        ///    - reached the limit of credentials stored by the authenticator.
+        ///  * <see cref="WauthnError.TimedOut"/> if the request times out. Known causes:
+        ///    - authenticator does not respond during state assisted transactions due to
+        ///      lack of push notifications support (e.g. missing Google Account).
+        /// </param>
+        /// <param name="linkedDataCallback">
+        /// Callback function for getting the updated linked device data. May be called multiple times.
+        /// Invoked when the response for the <see cref="Authenticator.GetAssertion"/> request
+        /// needs to be returned. The result of this request may be one of the following:
+        ///  * <see cref="WauthnError.None"/> if the request is completed well,
+        ///  * <see cref="WauthnError.Canceled"/> if the request is cancelled by a Cancel() request.
+        ///  * <see cref="WauthnError.InvalidState"/> if the server entered invalid state. Known causes:
+        ///    - proxy issues,
+        ///    - reached the limit of credentials stored by the authenticator.
+        ///  * <see cref="WauthnError.TimedOut"/> if the request times out. Known causes:
+        ///    - authenticator does not respond during state assisted transactions due to
+        ///      lack of push notifications support (e.g. missing Google Account).
+        /// </param>
+        /// <param name="userData">User data to be passed to <see cref="QrcodeCallback"/>, <see cref="ResponseCallback"/> and <see cref="LinkedDataCallback"/>.</param>
+        public GetAssertionCallbacks(
+            Action<string, object> qrcodeCallback,
+            Action<PubkeyCredAssertion, WauthnError, object> responseCallback,
+            Action<HybridLinkedData, WauthnError, object> linkedDataCallback,
+            object userData)
+        {
+            QrcodeCallback = qrcodeCallback;
+            ResponseCallback = responseCallback;
+            LinkedDataCallback = linkedDataCallback;
+            UserData = userData;
+        }
+
+        /// <summary>
+        /// Callback function for displaying QR code.
+        /// </summary>
+        public Action<string, object> QrcodeCallback { get; init; }
+        /// <summary>
+        /// Callback function for getting the final response.
+        /// </summary>
+        public Action<PubkeyCredAssertion, WauthnError, object> ResponseCallback { get; init; }
+        /// <summary>
+        /// Callback function for getting the updated linked device data.
+        /// </summary>
+        public Action<HybridLinkedData, WauthnError, object> LinkedDataCallback { get; init; }
+        /// <summary>
+        /// User data to be passed to <see cref="QrcodeCallback"/>, <see cref="ResponseCallback"/> and <see cref="LinkedDataCallback"/>.
+        /// </summary>
+        public object UserData { get; init; }
+    }
+}
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/HybridLinkedData.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/HybridLinkedData.cs
new file mode 100644 (file)
index 0000000..f12d885
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ *  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;
+using static Tizen.Security.WebAuthn.ErrorFactory;
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// Linked device data.
+    /// </summary>
+    /// <remarks>
+    /// The linked device data is used for state assisted transaction.
+    /// From the successful QR initiated transaction, the linked device data
+    /// might be returned from an authenticator to a webauthn client
+    /// via <see cref="PubkeyCredAttestation"/> or <see cref="PubkeyCredAssertion"/>.
+    /// Then the client can store the linked device data and use it in the next call
+    /// for <see cref="PubkeyCredCreationOptions"/> or <see cref="PubkeyCredRequestOptions"/>.
+    /// Then the stated assisted transaction will start instead of QR initiated transaction.
+    ///
+    /// For more information, find a section with the keyword, "linking map",
+    /// from the following specification.
+    /// https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html
+    ///
+    /// For more information about state assisted transaction, refer to the following.
+    /// https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#hybrid-state-assisted
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public class HybridLinkedData
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="HybridLinkedData"/> class.
+        /// </summary>
+        /// <remarks>
+        /// More information on the CBOR format can be found in the following specification:
+        /// https://www.rfc-editor.org/rfc/rfc8949.html
+        /// </remarks>
+        /// <param name="contactId">CBOR:"1".</param>
+        /// <param name="linkId">CBOR:"2".</param>
+        /// <param name="linkSecret">CBOR:"3".</param>
+        /// <param name="authenticatorPubkey">CBOR:"4".</param>
+        /// <param name="authenticatorName">CBOR:"5".</param>
+        /// <param name="signature">CBOR:"6".</param>
+        /// <param name="tunnelServerDomain">Domain String of tunnel server.</param>
+        /// <param name="identityKey">Identity Key created during QR initiated transaction.</param>
+        public HybridLinkedData(
+            byte[] contactId,
+            byte[] linkId,
+            byte[] linkSecret,
+            byte[] authenticatorPubkey,
+            byte[] authenticatorName,
+            byte[] signature,
+            byte[] tunnelServerDomain,
+            byte[] identityKey)
+        {
+            ContactId = contactId;
+            LinkId = linkId;
+            LinkSecret = linkSecret;
+            AuthenticatorPubkey = authenticatorPubkey;
+            AuthenticatorName = authenticatorName;
+            Signature = signature;
+            TunnelServerDomain = tunnelServerDomain;
+            IdentityKey = identityKey;
+        }
+
+        internal HybridLinkedData(WauthnHybridLinkedData linkedData)
+        {
+            ContactId = NullSafeMarshal.PtrToArray(linkedData.contactId);
+            LinkId = NullSafeMarshal.PtrToArray(linkedData.linkId);
+            LinkSecret = NullSafeMarshal.PtrToArray(linkedData.linkSecret);
+            AuthenticatorPubkey = NullSafeMarshal.PtrToArray(linkedData.authenticatorPubkey);
+            AuthenticatorName = NullSafeMarshal.PtrToArray(linkedData.authenticatorName);
+            Signature = NullSafeMarshal.PtrToArray(linkedData.signature);
+            TunnelServerDomain = NullSafeMarshal.PtrToArray(linkedData.tunnelServerDomain);
+            IdentityKey = NullSafeMarshal.PtrToArray(linkedData.identityKey);
+        }
+
+        /// <summary>
+        /// CBOR:"1".
+        /// </summary>
+        public byte[] ContactId { get; init; }
+        /// <summary>
+        /// CBOR:"3".
+        /// </summary>
+        public byte[] LinkId { get; init; }
+        /// <summary>
+        /// CBOR:"3".
+        /// </summary>
+        public byte[] LinkSecret { get; init; }
+        /// <summary>
+        /// CBOR:"4".
+        /// </summary>
+        public byte[] AuthenticatorPubkey { get; init; }
+        /// <summary>
+        /// CBOR:"5".
+        /// </summary>
+        public byte[] AuthenticatorName { get; init; }
+        /// <summary>
+        /// CBOR:"6".
+        /// </summary>
+        public byte[] Signature { get; init; }
+        /// <summary>
+        /// Domain String of tunnel server.
+        /// </summary>
+        public byte[] TunnelServerDomain { get; init; }
+        /// <summary>
+        /// Identity Key created during QR initiated transaction.
+        /// </summary>
+        public byte[] IdentityKey { get; init; }
+    }
+}
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/MakeCredentialCallbacks.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/MakeCredentialCallbacks.cs
new file mode 100644 (file)
index 0000000..9d3ecf1
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *  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;
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// Callback function list used to make credential with <see cref="Authenticator.MakeCredential"/>.
+    /// </summary>
+    /// <since_tizen> 12 </since_tizen>
+    public class MakeCredentialCallbacks
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="MakeCredentialCallbacks"/> class.
+        /// </summary>
+        /// <remarks>
+        /// Provided callbacks MUST NOT THROW.
+        /// </remarks>
+        /// <since_tizen> 12 </since_tizen>
+        /// <param name="qrcodeCallback">
+        /// Callback function for displaying QR code.
+        /// The qr_contents are encoded as you can see in the encodeQRContents() function of the
+        /// FIDO specification:
+        /// https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#hybrid-qr-initiated.
+        /// The qr_contents is encoded like "FIDO:/0254318383..........7406596245".
+        /// The image to be displayed shall be created from qr_contents
+        /// with media vision API (<see cref="Tizen.Multimedia.Vision.BarcodeGenerator"/>).
+        /// If the request does not need to display a QR code
+        /// then this callback function won't be invoked.
+        /// </param>
+        /// <param name="responseCallback">
+        /// Callback function for getting the final response.
+        /// Invoked when the response for the <see cref="Authenticator.MakeCredential"/> request
+        /// needs to be returned. The result of this request may be one of the following:
+        ///  * <see cref="WauthnError.None"/> if the request is completed well,
+        ///  * <see cref="WauthnError.Canceled"/> if the request is cancelled by a Cancel() request.
+        ///  * <see cref="WauthnError.InvalidState"/> if the server entered invalid state. Known causes:
+        ///    - proxy issues,
+        ///    - reached the limit of credentials stored by the authenticator.
+        ///  * <see cref="WauthnError.TimedOut"/> if the request times out. Known causes:
+        ///    - authenticator does not respond during state assisted transactions due to
+        ///      lack of push notifications support (e.g. missing Google Account).
+        /// </param>
+        /// <param name="linkedDataCallback">
+        /// Callback function for getting the updated linked device data. May be called multiple times.
+        /// Invoked when the response for the get assertion request needs to be returned.
+        /// The result of the MakeCredential request may be one of the following:
+        ///  * <see cref="WauthnError.None"/> if the request is completed well,
+        ///  * <see cref="WauthnError.Canceled"/> if the request is cancelled by a Cancel() request.
+        ///  * <see cref="WauthnError.InvalidState"/> if the server entered invalid state. Known causes:
+        ///    - proxy issues,
+        ///    - reached the limit of credentials stored by the authenticator.
+        ///  * <see cref="WauthnError.TimedOut"/> if the request times out. Known causes:
+        ///    - authenticator does not respond during state assisted transactions due to
+        ///      lack of push notifications support (e.g. missing Google Account).
+        /// </param>
+        /// <param name="userData">User data to be passed to <see cref="QrcodeCallback"/>, <see cref="ResponseCallback"/> and <see cref="LinkedDataCallback"/>.</param>
+        public MakeCredentialCallbacks(
+            Action<string, object> qrcodeCallback,
+            Action<PubkeyCredAttestation, WauthnError, object> responseCallback,
+            Action<HybridLinkedData, WauthnError, object> linkedDataCallback,
+            object userData)
+        {
+            QrcodeCallback = qrcodeCallback;
+            ResponseCallback = responseCallback;
+            LinkedDataCallback = linkedDataCallback;
+            UserData = userData;
+        }
+
+        /// <summary>
+        /// Callback function for displaying QR code.
+        /// </summary>
+        public Action<string, object> QrcodeCallback { get; init; }
+        /// <summary>
+        /// Callback function for getting the final response.
+        /// </summary>
+        public Action<PubkeyCredAttestation, WauthnError, object> ResponseCallback { get; init; }
+        /// <summary>
+        /// Callback function for getting the updated linked device data.
+        /// </summary>
+        public Action<HybridLinkedData, WauthnError, object> LinkedDataCallback { get; init; }
+        /// <summary>
+        /// User data to be passed to <see cref="QrcodeCallback"/>, <see cref="ResponseCallback"/> and <see cref="LinkedDataCallback"/>.
+        /// </summary>
+        public object UserData { get; init; }
+    }
+}
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/NullSafeMarshal.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/NullSafeMarshal.cs
new file mode 100644 (file)
index 0000000..36dc2f6
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ *  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;
+using System.Runtime.InteropServices;
+using static Interop;
+
+namespace Tizen.Security.WebAuthn
+{
+    internal static class NullSafeMarshal
+    {
+        internal static byte[] PtrToArray(IntPtr ptr)
+        {
+            if (ptr == IntPtr.Zero)
+                return null;
+
+            return Marshal.PtrToStructure<WauthnConstBuffer>(ptr).ToArray();
+        }
+    }
+}
+
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/PubkeyCredAssertion.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/PubkeyCredAssertion.cs
new file mode 100644 (file)
index 0000000..292a30e
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ *  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;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using static Interop;
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// Public key credential response for <see cref="Authenticator.GetAssertion"/>.
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#iface-pkcredential
+    /// https://www.w3.org/TR/webauthn-3/#sctn-credentialrequestoptions-extension
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public class PubkeyCredAssertion
+    {
+        internal PubkeyCredAssertion(WauthnPubkeyCredentialAssertion assertion)
+        {
+            Id = NullSafeMarshal.PtrToArray(assertion.id);
+            Type = assertion.type;
+            RawId = NullSafeMarshal.PtrToArray(assertion.rawId);
+            Response = assertion.response != IntPtr.Zero ? new AuthenticatorAssertionResponse(
+                Marshal.PtrToStructure<WauthnAuthenticatorAssertionResponse>(assertion.response)) :
+                null;
+            AuthenticatorAttachment = assertion.authenticatorAttachment;
+
+            if (assertion.extensions != IntPtr.Zero)
+            {
+                var wauthnExts = Marshal.PtrToStructure<WauthnAuthenticationExts>(assertion.extensions);
+                var extensionsArray = new AuthenticationExtension[wauthnExts.size];
+                unsafe
+                {
+                    var extPtr = (WauthnAuthenticationExt*)wauthnExts.descriptors;
+                    for (int i = 0; i < (int)wauthnExts.size; i++)
+                    {
+                        var wauthnExt = Marshal.PtrToStructure<WauthnAuthenticationExt>(new IntPtr(extPtr + i * sizeof(WauthnAuthenticationExt)));
+                        extensionsArray[i] = new AuthenticationExtension(wauthnExt);
+                    }
+                }
+                Extensions = extensionsArray;
+            }
+            else
+            {
+                Extensions = null;
+            }
+
+            LinkedDevice = assertion.linkedDevice != IntPtr.Zero ?
+                new HybridLinkedData(Marshal.PtrToStructure<WauthnHybridLinkedData>(assertion.linkedDevice)) :
+                null;
+        }
+
+        /// <summary>
+        /// The base64url encoding of credential’s identifier.
+        /// </summary>
+        public byte[] Id { get; init; }
+        /// <summary>
+        /// The credential’s type.
+        /// </summary>
+        public PubkeyCredType Type { get; init; }
+        /// <summary>
+        /// The raw value of credential’s identifier.
+        /// </summary>
+        public byte[] RawId { get; init; }
+        /// <summary>
+        /// Authenticator's response.
+        /// </summary>
+        public AuthenticatorAssertionResponse Response { get; init; }
+        /// <summary>
+        /// Authenticator attachment modality.
+        /// </summary>
+        public AuthenticatorAttachment AuthenticatorAttachment { get; init; }
+        /// <summary>
+        /// The results of processing client extensions requested by the Relying Party
+        /// upon the Relying Party's invocation of GetAssertion(). (optional)
+        /// </summary>
+        public IEnumerable<AuthenticationExtension> Extensions { get; init; }
+        /// <summary>
+        /// Linked Device Connection Info (optional).
+        /// If not null, the caller has to store this value and use this
+        /// in the next transaction to invoke state assisted transaction.
+        /// </summary>
+        public HybridLinkedData LinkedDevice { get; init; }
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/PubkeyCredAttestation.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/PubkeyCredAttestation.cs
new file mode 100644 (file)
index 0000000..12368be
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ *  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;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using static Interop;
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// Public key credential response for <see cref="Authenticator.MakeCredential"/>.
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#iface-pkcredential
+    /// https://www.w3.org/TR/webauthn-3/#sctn-credentialcreationoptions-extension
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public class PubkeyCredAttestation
+    {
+        internal PubkeyCredAttestation(WauthnPubkeyCredentialAttestation attestation)
+        {
+            Id = NullSafeMarshal.PtrToArray(attestation.id);
+            Type = attestation.type;
+            RawId = NullSafeMarshal.PtrToArray(attestation.rawId);
+            Response = attestation.response != IntPtr.Zero ?
+                new AuthenticatorAttestationResponse(Marshal.PtrToStructure<WauthnAuthenticatorAttestationResponse>(attestation.response)) :
+                null;
+            AuthenticatorAttachment = attestation.authenticatorAttachment;
+
+            if (attestation.extensions != IntPtr.Zero)
+            {
+                var wauthnExts = Marshal.PtrToStructure<WauthnAuthenticationExts>(attestation.extensions);
+                var extensionsArray = new AuthenticationExtension[wauthnExts.size];
+                unsafe
+                {
+                    var extPtr = (WauthnAuthenticationExt*)wauthnExts.descriptors;
+                    for (int i = 0; i < (int)wauthnExts.size; i++)
+                    {
+                        var wauthnExt = Marshal.PtrToStructure<WauthnAuthenticationExt>(new IntPtr(extPtr + i * sizeof(WauthnAuthenticationExt)));
+                        extensionsArray[i] = new AuthenticationExtension(wauthnExt);
+                    }
+                }
+                Extensions = extensionsArray;
+            }
+            else
+            {
+                Extensions = null;
+            }
+
+            LinkedDevice = attestation.linkedDevice != IntPtr.Zero ?
+                new HybridLinkedData(Marshal.PtrToStructure<WauthnHybridLinkedData>(attestation.linkedDevice)) :
+                null;
+        }
+
+        /// <summary>
+        /// The base64url encoding of credential’s identifier.
+        /// </summary>
+        public byte[] Id { get; init; }
+        /// <summary>
+        /// The credential’s type.
+        /// </summary>
+        public PubkeyCredType Type { get; init; }
+        /// <summary>
+        /// The raw value of credential’s identifier.
+        /// </summary>
+        public byte[] RawId { get; init; }
+        /// <summary>
+        /// Authenticator's response.
+        /// </summary>
+        public AuthenticatorAttestationResponse Response { get; init; }
+        /// <summary>
+        /// Authenticator attachment modality.
+        /// </summary>
+        public AuthenticatorAttachment AuthenticatorAttachment { get; init; }
+        /// <summary>
+        /// The results of processing client extensions requested by the Relying Party
+        /// upon the Relying Party's invocation of MakeCredential(). (optional)
+        /// </summary>
+        public IEnumerable<AuthenticationExtension> Extensions { get; init; }
+        /// <summary>
+        /// Linked Device Connection Info (optional).
+        /// If not null, the caller has to store this value and use thi
+        /// in the next transaction to invoke state assisted transaction.
+        /// </summary>
+        public HybridLinkedData LinkedDevice { get; init; }
+    }
+}
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/PubkeyCredCreationOptions.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/PubkeyCredCreationOptions.cs
new file mode 100644 (file)
index 0000000..9f81204
--- /dev/null
@@ -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 static Tizen.Security.WebAuthn.ErrorFactory;
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// Make credential options.
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialcreationoptions
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public class PubkeyCredCreationOptions
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="PubkeyCredCreationOptions"/> class.
+        /// </summary>
+        /// /// <since_tizen> 12 </since_tizen>
+        /// <param name="rp">Contains a name and an identifier for the Relying Party responsible for the request.</param>
+        /// <param name="user">Contains names and an identifier for the user account performing the registration.</param>
+        /// <param name="pubkeyCredParams">
+        /// Lists the key types and signature algorithms the Relying Party supports,
+        /// ordered from most preferred to least preferred.
+        /// </param>
+        /// <param name="timeout">
+        /// Specifies a time, in milliseconds,
+        /// that the Relying Party is willing to wait for the call to complete.
+        /// This is treated as a hint, and MAY be overridden by the client.
+        /// The value, '0', means no timeout is set. (optional)
+        /// </param>
+        /// <param name="excludeCredentials">
+        /// The Relying Party SHOULD use this argument to list any existing credentials mapped
+        /// to this user account (as identified by user.id). (optional)
+        /// </param>
+        /// <param name="authenticatorSelection">
+        /// The Relying Party MAY use his argument to specify capabilities and settings that the
+        /// authenticator MUST or SHOULD satisfy to participate in this operation. (optional)
+        /// </param>
+        /// <param name="hints">
+        /// Contains zero or more elements from <see cref="PubkeyCredHint"/> to
+        /// guide the user agent in interacting with the user. (optional)
+        /// </param>
+        /// <param name="attestation">
+        /// The Relying Party MAY use this argument to specify a preference regarding attestation conveyance.
+        /// The default value is <see cref="AttestationPref.None"/>. (optional)
+        /// </param>
+        /// <param name="attestationFormats">
+        /// The Relying Party MAY use this argument to specify a preference regarding the attestation
+        /// statement format used by the authenticator. The default value is the empty list, which
+        /// indicates no preference. (optional)
+        /// </param>
+        /// <param name="extensions">
+        /// The Relying Party MAY use this argument to provide client extension inputs requesting
+        /// additional processing by the client and authenticator. (optional)
+        /// </param>
+        /// <param name="linkedDevice">
+        /// Linked Device Connection Info. Optional.
+        /// If not null, the state assisted transaction will start.
+        /// </param>
+        public PubkeyCredCreationOptions(
+            RelyingPartyEntity rp,
+            UserEntity user,
+            IEnumerable<PubkeyCredParam> pubkeyCredParams,
+            ulong timeout = 0,
+            IEnumerable<PubkeyCredDescriptor> excludeCredentials = null,
+            AuthenticationSelectionCriteria authenticatorSelection = null,
+            IEnumerable<PubkeyCredHint> hints = null,
+            AttestationPref attestation = AttestationPref.None,
+            IEnumerable<byte[]> attestationFormats = null,
+            IEnumerable<AuthenticationExtension> extensions = null,
+            HybridLinkedData linkedDevice = null)
+        {
+            Rp = rp;
+            User = user;
+            PubkeyCredParams = pubkeyCredParams;
+            Timeout = timeout;
+            ExcludeCredentials = excludeCredentials;
+            AuthenticatorSelection = authenticatorSelection;
+            Hints = hints;
+            Attestation = attestation;
+            AttestationFormats = attestationFormats;
+            Extensions = extensions;
+            LinkedDevice = linkedDevice;
+        }
+
+        /// <summary>
+        /// Contains a name and an identifier for the Relying Party responsible for the request.
+        /// </summary>
+        public RelyingPartyEntity Rp { get; init; }
+        /// <summary>
+        /// Contains names and an identifier for the user account performing the registration.
+        /// </summary>
+        public UserEntity User { get; init; }
+        /// <summary>
+        /// Lists the key types and signature algorithms the Relying Party supports,
+        /// ordered from most preferred to least preferred.
+        /// </summary>
+        public IEnumerable<PubkeyCredParam> PubkeyCredParams { get; init; }
+        /// <summary>
+        /// Specifies a time, in milliseconds, that the Relying Party is willing to wait for the
+        /// call to complete. This is treated as a hint, and MAY be overridden by the client.
+        /// The value, '0', means no timeout is set.
+        /// </summary>
+        public ulong Timeout { get; init; }
+        /// <summary>
+        /// Lists any existing credentials mapped to this user account (as identified by user.id).
+        /// </summary>
+        public IEnumerable<PubkeyCredDescriptor> ExcludeCredentials { get; init; }
+        /// <summary>
+        /// Specifies capabilities and settings that the authenticator MUST or SHOULD satisfy
+        /// to participate in this operation.
+        /// </summary>
+        public AuthenticationSelectionCriteria AuthenticatorSelection { get; init; }
+        /// <summary>
+        /// Contains zero or more elements from <see cref="PubkeyCredHint"/> to
+        /// guide the user agent in interacting with the user.
+        /// </summary>
+        public IEnumerable<PubkeyCredHint> Hints { get; init; }
+        /// <summary>
+        /// Specifies a preference regarding attestation conveyance.
+        /// </summary>
+        public AttestationPref Attestation { get; init; }
+        /// <summary>
+        /// Specifies a preference regarding the attestation statement format used by the authenticator.
+        /// </summary>
+        public IEnumerable<byte[]> AttestationFormats { get; init; }
+        /// <summary>
+        /// Client extension inputs requesting additional processing by the client and authenticator.
+        /// </summary>
+        public IEnumerable<AuthenticationExtension> Extensions { get; init; }
+        /// <summary>
+        /// Linked Device Connection Info. If not null, the state assisted transaction will start.
+        /// </summary>
+        public HybridLinkedData LinkedDevice { get; init; }
+    }
+}
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/PubkeyCredDescriptor.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/PubkeyCredDescriptor.cs
new file mode 100644 (file)
index 0000000..f1a6523
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *  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 Tizen.Security.WebAuthn.ErrorFactory;
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// Public key credential descriptor.
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialdescriptor
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public class PubkeyCredDescriptor
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="PubkeyCredDescriptor"/> class.
+        /// </summary>
+        /// <param name="type">The type of the public key credential.</param>
+        /// <param name="id">The credential ID of the public key credential.</param>
+        /// <param name="transport">To represent multiple transports, this enum can be ORed multiple times.</param>
+        public PubkeyCredDescriptor(PubkeyCredType type, byte[] id, AuthenticatorTransport transport)
+        {
+            Type = type;
+            Id = id;
+            Transport = transport;
+        }
+        /// <summary>
+        /// The type of the public key credential.
+        /// </summary>
+        public PubkeyCredType Type { get; init; }
+        /// <summary>
+        /// The credential ID of the public key credential.
+        /// </summary>
+        public byte[] Id { get; init; }
+        /// <summary>
+        /// Transport types.
+        /// </summary>
+        public AuthenticatorTransport Transport { get; init; }
+    }
+}
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/PubkeyCredParam.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/PubkeyCredParam.cs
new file mode 100644 (file)
index 0000000..762b119
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *  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
+ */
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// Parameter for credential generation.
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialparameters
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public class PubkeyCredParam
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="PubkeyCredParam"/> class.
+        /// </summary>
+        /// <since_tizen> 12 </since_tizen>
+        /// <param name="type">Well-known credential type specifying a credential to create.</param>
+        /// <param name="alg">Well-known COSE algorithm specifying the algorithm to use for the credential.</param>
+        public PubkeyCredParam(PubkeyCredType type, CoseAlgorithm alg)
+        {
+            Type = type;
+            Alg = alg;
+        }
+        /// <summary>
+        /// Well-known credential type specifying a credential to create.
+        /// </summary>
+        public PubkeyCredType Type { get; init; }
+        /// <summary>
+        /// Well-known COSE algorithm specifying the algorithm to use for the credential.
+        /// </summary>
+        public CoseAlgorithm Alg { get; init; }
+    }
+}
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/PubkeyCredRequestOptions.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/PubkeyCredRequestOptions.cs
new file mode 100644 (file)
index 0000000..3df50f3
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ *  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;
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// Get assertion options.
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#dictionary-assertion-options
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public class PubkeyCredRequestOptions
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="PubkeyCredRequestOptions"/> class.
+        /// </summary>
+        /// <since_tizen> 12 </since_tizen>
+        /// <param name="timeout">
+        /// Specifies a time, in milliseconds, that the Relying Party is willing to wait for the
+        /// call to complete. This is treated as a hint, and MAY be overridden by the client.
+        /// The value, '0', means no timeout is set. (optional)
+        /// </param>
+        /// <param name="rpId">
+        /// Specifies the RP ID claimed by the Relying Party. (optional)
+        /// </param>
+        /// <param name="allowCredentials">
+        /// Used by the client to find authenticators eligible for this authentication ceremony. (optional)
+        /// </param>
+        /// <param name="userVerification">
+        /// Specifies the Relying Party's requirements regarding user verification for the
+        /// GetAssertion() operation. The default value is <see cref="UserVerificationRequirement.Preferred"/>. (optional)
+        /// </param>
+        /// <param name="hints">
+        /// Contains zero or more elements from <see cref="PubkeyCredHint"/> to
+        /// guide the user agent in interacting with the user. (optional)
+        /// </param>
+        /// <param name="attestation">
+        /// The Relying Party MAY use this argument to specify a preference regarding
+        /// attestation conveyance. The default value is <see cref="AttestationPref.None"/>. (optional)
+        /// </param>
+        /// <param name="attestationFormats">
+        /// The Relying Party MAY use this argument to specify a preference regarding the attestation
+        /// statement format used by the authenticator. The default value is the empty list,
+        /// which indicates no preference. (optional)
+        /// </param>
+        /// <param name="extensions">
+        /// The Relying Party MAY use this argument to provide client extension inputs requesting
+        /// additional processing by the client and authenticator. (optional)
+        /// </param>
+        /// <param name="linkedDevice">
+        /// Linked Device Connection Info. Optional.
+        /// If not null, the state assisted transaction will start.
+        /// </param>
+        public PubkeyCredRequestOptions(
+            ulong timeout = 0,
+            string rpId = null,
+            IEnumerable<PubkeyCredDescriptor> allowCredentials = null,
+            UserVerificationRequirement userVerification = UserVerificationRequirement.Preferred,
+            IEnumerable<PubkeyCredHint> hints = null,
+            AttestationPref attestation = AttestationPref.None,
+            IEnumerable<byte[]> attestationFormats = null,
+            IEnumerable<AuthenticationExtension> extensions = null,
+            HybridLinkedData linkedDevice = null)
+        {
+            Timeout = timeout;
+            RpId = rpId;
+            AllowCredentials = allowCredentials;
+            UserVerification = userVerification;
+            Hints = hints;
+            Attestation = attestation;
+            AttestationFormats = attestationFormats;
+            Extensions = extensions;
+            LinkedDevice = linkedDevice;
+        }
+
+        /// <summary>
+        /// Specifies a time, in milliseconds, that the Relying Party is willing to wait for the
+        /// call to complete. This is treated as a hint, and MAY be overridden by the client.
+        /// The value, '0', means no timeout is set.
+        /// </summary>
+        public ulong Timeout { get; init; }
+        /// <summary>
+        /// Specifies the RP ID claimed by the Relying Party.
+        /// </summary>
+        public string RpId { get; init; }
+        /// <summary>
+        /// Used by the client to find authenticators eligible for this authentication ceremony.
+        /// </summary>
+        public IEnumerable<PubkeyCredDescriptor> AllowCredentials { get; init; }
+        /// <summary>
+        /// Specifies the Relying Party's requirements regarding user verification for the GetAssertion() operation.
+        /// </summary>
+        public UserVerificationRequirement UserVerification { get; init; }
+        /// <summary>
+        /// Contains zero or more elements from <see cref="PubkeyCredHint"/> to
+        /// guide the user agent in interacting with the user.
+        /// </summary>
+        public IEnumerable<PubkeyCredHint> Hints { get; init; }
+        /// <summary>
+        /// Specifies a preference regarding attestation conveyance.
+        /// </summary>
+        public AttestationPref Attestation { get; init; }
+        /// <summary>
+        /// Specifies a preference regarding the attestation statement format used by the authenticator.
+        /// </summary>
+        public IEnumerable<byte[]> AttestationFormats { get; init; }
+        /// <summary>
+        /// Client extension inputs requesting additional processing by the client and authenticator.
+        /// </summary>
+        public IEnumerable<AuthenticationExtension> Extensions { get; init; }
+        /// <summary>
+        /// Linked Device Connection Info. If not null, the state assisted transaction will start.
+        /// </summary>
+        public HybridLinkedData LinkedDevice { get; init; }
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/RelyingPartyEntity.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/RelyingPartyEntity.cs
new file mode 100644 (file)
index 0000000..f00fdec
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ *  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
+ */
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// Relying Party entity.
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrpentity
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public class RelyingPartyEntity
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="RelyingPartyEntity"/> class.
+        /// </summary>
+        /// <since_tizen> 12 </since_tizen>
+        /// <param name="name">The name of RP.</param>
+        /// <param name="id">The RPID.</param>
+        public RelyingPartyEntity(string name, string id)
+        {
+            Name = name;
+            Id = id;
+        }
+
+        /// <summary>
+        /// The name of RP.
+        /// </summary>
+        public string Name { get; init; }
+        /// <summary>
+        /// The RPID.
+        /// </summary>
+        public string Id { get; init; }
+    }
+}
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/UnmanagedMemory.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/UnmanagedMemory.cs
new file mode 100644 (file)
index 0000000..2ada7c0
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ *  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;
+using System.Runtime.InteropServices;
+
+namespace Tizen.Security.WebAuthn
+{
+    internal class UnmanagedMemory : IDisposable
+    {
+        private bool _disposed = false;
+        IntPtr _memory = IntPtr.Zero;
+
+        public UnmanagedMemory()
+        {}
+
+        public UnmanagedMemory(object obj)
+        {
+            _memory = Marshal.AllocHGlobal(Marshal.SizeOf(obj));
+            Marshal.StructureToPtr(obj, _memory, false);
+        }
+
+
+        public UnmanagedMemory(string obj)
+        {
+            _memory = Marshal.StringToHGlobalAnsi(obj);
+        }
+
+        unsafe private UnmanagedMemory(IntPtr pinned, int size)
+        {
+            _memory = Marshal.AllocHGlobal(size);
+            Buffer.MemoryCopy((void*)pinned, (void*)_memory, size, size);
+        }
+
+        ~UnmanagedMemory()
+        {
+            Dispose(false);
+        }
+
+        public static UnmanagedMemory PinArray<T>(T[] array) where T : struct
+        {
+            if (array is null)
+                return new UnmanagedMemory();
+
+            GCHandle pinnedArray = GCHandle.Alloc(array, GCHandleType.Pinned);
+            var ret = new UnmanagedMemory(pinnedArray.AddrOfPinnedObject(), Marshal.SizeOf(array[0]) * array.Length);
+            pinnedArray.Free();
+            return ret;
+        }
+
+        public static implicit operator IntPtr(UnmanagedMemory pinned)
+        {
+            return pinned._memory;
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        protected virtual void Dispose(bool disposing)
+        {
+            if (_disposed)
+                return;
+            if (_memory != IntPtr.Zero)
+                Marshal.FreeHGlobal(_memory);
+
+            _memory = IntPtr.Zero;
+            _disposed = true;
+        }
+    }
+}
diff --git a/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/UserEntity.cs b/src/Tizen.Security.WebAuthn/Tizen.Security.WebAuthn/UserEntity.cs
new file mode 100644 (file)
index 0000000..6eb65e1
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *  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 Tizen.Security.WebAuthn.ErrorFactory;
+
+namespace Tizen.Security.WebAuthn
+{
+    /// <summary>
+    /// User entity.
+    /// </summary>
+    /// <remarks>
+    /// Refer to the following W3C specification for more information.
+    /// https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialuserentity
+    /// </remarks>
+    /// <since_tizen> 12 </since_tizen>
+    public class UserEntity
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="RelyingPartyEntity"/> class.
+        /// </summary>
+        /// <since_tizen> 12 </since_tizen>
+        /// <param name="name">A human-palatable name for the entity.</param>
+        /// <param name="id">
+        /// The ID of the user account. An ID is a byte sequence with a maximum size of 64 bytes,
+        /// and is not meant to be displayed to the user.
+        /// </param>
+        /// <param name="displayName">A human-palatable name for the user account, intended only for display.</param>
+        public UserEntity(string name, byte[] id, string displayName)
+        {
+            Name = name;
+            Id = id;
+            DisplayName = displayName;
+        }
+
+        /// <summary>
+        /// A human-palatable name for the entity.
+        /// </summary>
+        public string Name { get; init; }
+        /// <summary>
+        /// The ID of the user account.
+        /// </summary>
+        public byte[] Id { get; init; }
+        /// <summary>
+        /// A human-palatable name for the user account, intended only for display.
+        /// </summary>
+        public string DisplayName { get; init; }
+    }
+}
diff --git a/src/Tizen.Security.WebAuthn/doc/api/Tizen.Security.WebAuthn.md b/src/Tizen.Security.WebAuthn/doc/api/Tizen.Security.WebAuthn.md
new file mode 100644 (file)
index 0000000..1a66141
--- /dev/null
@@ -0,0 +1,21 @@
+---
+uid: Tizen.Security.WebAuthn
+summary: The Web Authentication module provides a C# API enabling the creation and use of
+  strong, attested, scoped, public key-based credentials by web applications, for the
+  purpose of strongly authenticating users
+remarks: *content
+---
+## Overview
+It provides an [Authenticator](xref:Tizen.Security.WebAuthn.Authenticator) class containing methods for creating public key-based credentials
+([Authenticator.MakeCredential()](xref:Tizen.Security.WebAuthn.Authenticator.MakeCredential(Tizen.Security.WebAuthn.ClientData,Tizen.Security.WebAuthn.PubkeyCredCreationOptions,Tizen.Security.WebAuthn.MakeCredentialCallbacks))) and using them ([Authenticator.GetAssertion()](xref:Tizen.Security.WebAuthn.Authenticator.GetAssertion(Tizen.Security.WebAuthn.ClientData,Tizen.Security.WebAuthn.PubkeyCredRequestOptions,Tizen.Security.WebAuthn.GetAssertionCallbacks))). Both these operations are performed asynchronously. Callbacks passed as arguments are used to notify about the progress
+or when user's interaction is necessary. Due to significant amount of time required to complete both
+requests, cancelation is also possible with the help of Authenticator.Cancel(). The module also
+provides a variety of data types based on W3C Web Authentication API (https://www.w3.org/TR/webauthn-3/) 
+used to control the credential creation and assertion process.
+
+## Related features
+This module is related with the following features:
+ * http://tizen.org/feature/security.webauthn
+ * http://tizen.org/feature/network.bluetooth.le
+ * and network connection features (http://tizen.org/feature/network.wifi, http://tizen.org/feature/network.ethernet, http://tizen.org/feature/network.telephony)
+