Add Log for debug 97/314697/3
authorYonggoo Kang <ygace.kang@samsung.com>
Wed, 17 Jul 2024 13:58:34 +0000 (22:58 +0900)
committerYonggoo Kang <ygace.kang@samsung.com>
Thu, 18 Jul 2024 14:23:40 +0000 (23:23 +0900)
- Add Request log for API parameters
- Add LinkedData log for reponse
- minor fix

Change-Id: Ibcd140a5f743a72e34fd5e6f0a59f8ad28359f72

srcs/client/client-request.h
srcs/client/client.cpp
srcs/common/utils.cpp
srcs/common/utils.h
srcs/common/webauthn-log.cpp
srcs/common/webauthn-log.h

index 5bf0a8e28285462bb7a017eb2f703dc6d15163a9..66a20a24446b463575b518305e09b9d24f2e5ab4 100644 (file)
 #include <stdexcept>
 #include <thread>
 #include <unistd.h>
-#include <utils.h>
 
 #include <connection.h>
 #include <message-buffer.h>
 #include <protocols.h>
-#include <webauthn-log.h>
+#include <utils.h>
 #include <webauthn-types.h>
 
 namespace WA {
@@ -222,14 +221,17 @@ public:
 template <typename A, typename B>
 void cb_worker(std::shared_ptr<GenericClientRequest> request, A &callbacks, B cred)
 {
+    static constexpr const char *REQUEST_KIND_PREFIX =
+         std::is_same_v<B, wauthn_pubkey_credential_attestation_s*> ? "MC: " : "GA: ";
+
     int ret = try_catch([&]() -> int {
         if (callbacks->qrcode_callback == nullptr)
-            LogDebug("There is no qrcode_callback");
+            LogDebug(REQUEST_KIND_PREFIX << "There is no qrcode_callback");
         else{ //callbacks->qrcode_callback != nullptr
             std::string qr_code;
             if (request->Recv(qr_code).Failed())
-                LogError("Error on receive qrcode");
-            LogDebug("Received qr_contents: " << qr_code);
+                LogError(REQUEST_KIND_PREFIX << "Error on receive qrcode");
+            LogDebug(REQUEST_KIND_PREFIX << "Received qr_contents: " << qr_code);
             callbacks->qrcode_callback(qr_code.c_str(), callbacks->user_data);
         }
         request->Recv(&cred);
@@ -237,7 +239,7 @@ void cb_worker(std::shared_ptr<GenericClientRequest> request, A &callbacks, B cr
                                      callbacks->user_data);
         if(request->Failed())
         {
-            LogError("Error on receive response: " << request->GetStatus());
+            LogError(REQUEST_KIND_PREFIX << "Error on receive response: " << request->GetStatus());
             return WAUTHN_ERROR_INVALID_STATE;
         }
 
@@ -245,17 +247,19 @@ void cb_worker(std::shared_ptr<GenericClientRequest> request, A &callbacks, B cr
 
         while (request->Recv(&linked_data).Incompleted())
         {
+            LogLinkedDevice(REQUEST_KIND_PREFIX, "[linked_data_callback]", linked_data);
             callbacks->linked_data_callback(linked_data, wauthn_error_e(request->GetStatus()),
                                             callbacks->user_data);
-            LogDebug("More update linked_data can be received. Waiting to recv()");
+            LogDebug(REQUEST_KIND_PREFIX << "More update linked_data can be received. Waiting to recv()");
         }
-        LogDebug("There is no more updated linked_data");
+        LogLinkedDevice(REQUEST_KIND_PREFIX, "[linked_data_callback]", linked_data);
+        LogDebug(REQUEST_KIND_PREFIX << "There is no more updated linked_data");
         callbacks->linked_data_callback(linked_data, wauthn_error_e(request->GetStatus()),
                                         callbacks->user_data);
         return WAUTHN_ERROR_NONE;
     });
     if (ret != WAUTHN_ERROR_NONE)
-        LogError("Error on handling callbacks");
+        LogError(REQUEST_KIND_PREFIX << "Error on handling callbacks");
 }
 
 template <typename T>
@@ -264,7 +268,6 @@ int wauthn_process(const wauthn_client_data_s *client_data,
                    typename T::Callbacks *callbacks)
 {
     return try_catch([&]() -> int {
-        checkParameters(client_data, options, callbacks);
         if (options->linked_device != nullptr)  // The qrcode_callback should not be called.
         {
             LogDebug("Adjust qrcode_callback to null");
index 84282653f18cbb4267f95060f20dfe74ece1b982..58e2401501e910ded2a57131849b08ed2bf86a86 100644 (file)
@@ -29,14 +29,22 @@ int wauthn_make_credential( const wauthn_client_data_s *client_data,
                             const wauthn_pubkey_cred_creation_options_s *options,
                             wauthn_mc_callbacks_s *callbacks)
 {
-    return wauthn_process<ClientRequestMC>(client_data, options, callbacks);
+    return try_catch([&]() -> int {
+        CheckParameters(client_data, options, callbacks);
+        LogRequest<RequestKind::MC>(*client_data, *options);
+        return wauthn_process<ClientRequestMC>(client_data, options, 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)
 {
-    return wauthn_process<ClientRequestGA>(client_data, options, callbacks);
+    return try_catch([&]() -> int {
+        CheckParameters(client_data, options, callbacks);
+        LogRequest<RequestKind::GA>(*client_data, *options);
+        return wauthn_process<ClientRequestGA>(client_data, options, callbacks);
+    });
 }
 
 int wauthn_cancel()
index 1147c6b21d8e8f9a6011f0c7f6602a548bb4f6f3..7b2562e45af5c05396b807829707ec5fe20f61ec 100644 (file)
 
 namespace WA {
 
-void checkParameters(const wauthn_client_data_s *client_data)
+void CheckParameters(const wauthn_client_data_s *client_data)
 {
     if (client_data == nullptr)
         ThrowMsg(ClientException::InvalidParameter, "Invalid client_data");
 }
-void checkParameters(const wauthn_pubkey_cred_creation_options_s *options)
+void CheckParameters(const wauthn_pubkey_cred_creation_options_s *options)
 {
     if (options == nullptr)
         ThrowMsg(ClientException::InvalidParameter, "Invalid options");
 }
-void checkParameters(const wauthn_pubkey_cred_request_options_s *options)
+void CheckParameters(const wauthn_pubkey_cred_request_options_s *options)
 {
     if (options == nullptr)
         ThrowMsg(ClientException::InvalidParameter, "Invalid options");
 }
-void checkParameters(wauthn_mc_callbacks_s *callbacks)
+void CheckParameters(wauthn_mc_callbacks_s *callbacks)
 {
     if (callbacks == nullptr || callbacks->response_callback == nullptr
         || callbacks->linked_data_callback == nullptr)
         ThrowMsg(ClientException::InvalidParameter, "Invalid callback");
 }
-void checkParameters(wauthn_ga_callbacks_s *callbacks)
+void CheckParameters(wauthn_ga_callbacks_s *callbacks)
 {
     if (callbacks == nullptr || callbacks->response_callback == nullptr
         || callbacks->linked_data_callback == nullptr)
         ThrowMsg(ClientException::InvalidParameter, "Invalid callback");
 }
-void checkParameters(wauthn_hybrid_linked_data_s *linked_device,
+void CheckParameters(wauthn_hybrid_linked_data_s *linked_device,
                     wauthn_cb_display_qrcode qrcode_callback)
 {
     if (linked_device == nullptr && qrcode_callback == nullptr)
         ThrowMsg(ClientException::InvalidParameter, "The qrcode_callback is missed");
 }
-void checkParameters(const wauthn_client_data_s *client_data,
+void CheckParameters(const wauthn_client_data_s *client_data,
                     const wauthn_pubkey_cred_creation_options_s *options,
                     wauthn_mc_callbacks_s *callbacks)
 {
-    checkParameters(client_data);
-    checkParameters(options);
-    checkParameters(callbacks);
-    checkParameters(options->linked_device, callbacks->qrcode_callback);
+    CheckParameters(client_data);
+    CheckParameters(options);
+    CheckParameters(callbacks);
+    CheckParameters(options->linked_device, callbacks->qrcode_callback);
 }
-void checkParameters(const wauthn_client_data_s *client_data,
+void CheckParameters(const wauthn_client_data_s *client_data,
                     const wauthn_pubkey_cred_request_options_s *options,
                     wauthn_ga_callbacks_s *callbacks)
 {
-    checkParameters(client_data);
-    checkParameters(options);
-    checkParameters(callbacks);
-    checkParameters(options->linked_device, callbacks->qrcode_callback);
+    CheckParameters(client_data);
+    CheckParameters(options);
+    CheckParameters(callbacks);
+    CheckParameters(options->linked_device, callbacks->qrcode_callback);
 }
 } /* namespace WebAuthn */
index f4d63f730a895c512f8f672574bfb7aba7a5495f..5bb8c95e021863d2e6e54ac0ae3c03e6e344cff0 100644 (file)
@@ -93,17 +93,17 @@ int try_catch(F &&f) {
     return WAUTHN_ERROR_NONE;
 }
 
-void checkParameters(const wauthn_client_data_s *client_data);
-void checkParameters(const wauthn_pubkey_cred_creation_options_s *options);
-void checkParameters(const wauthn_pubkey_cred_request_options_s *options);
-void checkParameters(wauthn_mc_callbacks_s *callbacks);
-void checkParameters(wauthn_ga_callbacks_s *callbacks);
-void checkParameters(wauthn_hybrid_linked_data_s *linked_device,
+void CheckParameters(const wauthn_client_data_s *client_data);
+void CheckParameters(const wauthn_pubkey_cred_creation_options_s *options);
+void CheckParameters(const wauthn_pubkey_cred_request_options_s *options);
+void CheckParameters(wauthn_mc_callbacks_s *callbacks);
+void CheckParameters(wauthn_ga_callbacks_s *callbacks);
+void CheckParameters(wauthn_hybrid_linked_data_s *linked_device,
                     wauthn_cb_display_qrcode qrcode_callback);
-void checkParameters(const wauthn_client_data_s *client_data,
+void CheckParameters(const wauthn_client_data_s *client_data,
                     const wauthn_pubkey_cred_creation_options_s *options,
                     wauthn_mc_callbacks_s *callbacks);
-void checkParameters(const wauthn_client_data_s *client_data,
+void CheckParameters(const wauthn_client_data_s *client_data,
                     const wauthn_pubkey_cred_request_options_s *options,
                     wauthn_ga_callbacks_s *callbacks);
 
index f76c889a8a69b2d75e7e86a8280fe277578fb7ae..ba6e711ca35138015ed7bc9612bdbbcd831d4929 100644 (file)
@@ -98,4 +98,341 @@ const char *WebAuthnLog::LocateSourceFileName(const char *filename)
     return ptr != NULL ? ptr + 1 : filename;
 }
 
+#ifdef BUILD_TYPE_DEBUG
+std::string ToStr(const char *str)
+{
+    if (!str)
+        return "null";
+    std::string res = "\"";
+    res += str;
+    res += '"';
+    return res;
+}
+
+std::string ToStr(wauthn_const_buffer_s *wcb)
+{
+    if (!wcb)
+        return "null";
+
+    std::string str = "\"";
+    str.append(wcb->data, wcb->data + wcb->size);
+    str += '"';
+    return str;
+}
+
+std::string ToHexStr(wauthn_const_buffer_s *wcb)
+{
+    return wcb ? LowercaseHexStringOf(BufferView{wcb->data, wcb->size}) : "null";
+}
+
+std::string ToStr(wauthn_cose_algorithm_e alg)
+{
+    switch (alg) {
+    case WAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256: return "ES256";
+    case WAUTHN_COSE_ALGORITHM_ECDSA_P384_WITH_SHA384: return "ES384";
+    case WAUTHN_COSE_ALGORITHM_ECDSA_P521_WITH_SHA512: return "ES512";
+    case WAUTHN_COSE_ALGORITHM_EDDSA: return "EdDSA";
+    case WAUTHN_COSE_ALGORITHM_RSA_PSS_WITH_SHA256: return "PS256";
+    case WAUTHN_COSE_ALGORITHM_RSA_PSS_WITH_SHA384: return "PS384";
+    case WAUTHN_COSE_ALGORITHM_RSA_PSS_WITH_SHA512: return "PS512";
+    case WAUTHN_COSE_ALGORITHM_RSASSA_PKCS1_V1_5_WITH_SHA256: return "RS256";
+    case WAUTHN_COSE_ALGORITHM_RSASSA_PKCS1_V1_5_WITH_SHA384: return "RS384";
+    case WAUTHN_COSE_ALGORITHM_RSASSA_PKCS1_V1_5_WITH_SHA512: return "RS512";
+    }
+    return std::to_string(alg);
+}
+
+std::string ToStr(wauthn_pubkey_cred_type_e pct)
+{
+    switch (pct) {
+    case PCT_PUBLIC_KEY: return "public-key";
+    }
+    return std::to_string(pct);
+}
+
+std::string ToStr(wauthn_authenticator_attachment_e authenticatorAttachment)
+{
+    switch (authenticatorAttachment) {
+    case AA_NONE: return "none";
+    case AA_PLATFORM: return "platform";
+    case AA_CROSS_PLATFORM: return "cross-platform";
+    }
+    return std::to_string(authenticatorAttachment);
+}
+
+std::string ToStr(wauthn_resident_key_requirement_e residentKeyRequirement)
+{
+    switch (residentKeyRequirement) {
+    case RKR_NONE: return "none";
+    case RKR_DISCOURAGED: return "discouraged";
+    case RKR_PREFERRED: return "preferred";
+    case RKR_REQUIRED: return "required";
+    }
+    return std::to_string(residentKeyRequirement);
+}
+
+std::string ToStr(wauthn_user_verification_requirement_e userVerificationRequirement)
+{
+    switch (userVerificationRequirement) {
+    case UVR_NONE: return "none";
+    case UVR_REQUIRED: return "required";
+    case UVR_PREFERRED: return "preferred";
+    case UVR_DISCOURAGED: return "discouraged";
+    }
+    return std::to_string(userVerificationRequirement);
+}
+
+std::string ToStr(wauthn_pubkey_cred_hint_e credHint)
+{
+    switch (credHint) {
+    case PCH_NONE: return "none";
+    case PCH_SECURITY_KEY: return "security-key";
+    case PCH_CLIENT_DEVICE: return "client-device";
+    case PCH_HYBRID: return "hybrid";
+    }
+    return std::to_string(credHint);
+}
+
+std::string ToStr(wauthn_attestation_pref_e attestationPref)
+{
+    switch (attestationPref) {
+    case AP_NONE: return "none";
+    case AP_INDIRECT: return "indirect";
+    case AP_DIRECT: return "direct";
+    case AP_ENTERPRISE: return "enterprise";
+    }
+    return std::to_string(attestationPref);
+}
+
+std::string TransportsToStr(unsigned int transports)
+{
+    if (transports == WAUTHN_TRANSPORT_NONE)
+        return "none";
+
+    struct TransportInfo {
+        wauthn_authenticator_transport_e mask;
+        const char *name;
+    };
+
+    static constexpr auto TRANSPORTS = std::array{
+        TransportInfo{WAUTHN_TRANSPORT_USB,       "usb"       },
+        TransportInfo{WAUTHN_TRANSPORT_NFC,       "nfc"       },
+        TransportInfo{WAUTHN_TRANSPORT_BLE,       "ble"       },
+        TransportInfo{WAUTHN_TRANSPORT_SMARTCARD, "smart-card"},
+        TransportInfo{WAUTHN_TRANSPORT_HYBRID,    "hybrid"    },
+        TransportInfo{WAUTHN_TRANSPORT_INTERNAL,  "internal"  },
+    };
+
+    std::string transportsStr;
+    auto transportsLeft = transports;
+    constexpr auto SEPARATOR = std::string_view{" | "};
+    for (const auto &ti : TRANSPORTS) {
+        if (transports & ti.mask) {
+            transportsStr += ti.name;
+            transportsStr += SEPARATOR;
+            transportsLeft &= ~ti.mask;
+        }
+    }
+    if (transportsLeft != 0)
+        transportsStr += std::to_string(transportsLeft);
+    else
+        transportsStr.resize(transportsStr.size() - SEPARATOR.size());
+
+    return transportsStr;
+}
+
+void LogExtensions(const char *prefix1,
+                   const char *prefix2,
+                   const wauthn_authentication_exts_s *extensions)
+{
+    if (!extensions)
+        LogDebug(prefix1 << prefix2 << "->extensions: null");
+    else {
+        LogDebug(prefix1 << prefix2 << "->extensions->size: " << extensions->size);
+        for (size_t i = 0; i < extensions->size; ++i) {
+            LogDebug(prefix1 << prefix2 << "->extensions[" << i
+                             << "]: " << ToStr(extensions->extensions[i].extension_id) << " -> "
+                             << ToHexStr(extensions->extensions[i].extension_value));
+        }
+    }
+}
+#endif
+
+void LogLinkedDevice(const char *prefix1,
+                     const char *prefix2,
+                     const wauthn_hybrid_linked_data_s *linkedDevice)
+{
+#ifdef BUILD_TYPE_DEBUG
+    if (!linkedDevice)
+        LogDebug(prefix1 << prefix2 << "linked_device: null");
+    else {
+        LogDebug(prefix1 << prefix2
+                         << "linked_device->contact_id: " << ToHexStr(linkedDevice->contact_id));
+        LogDebug(prefix1 << prefix2
+                         << "linked_device->link_id: " << ToHexStr(linkedDevice->link_id));
+        LogDebug(prefix1 << prefix2
+                         << "linked_device->link_secret: " << ToHexStr(linkedDevice->link_secret));
+        LogDebug(prefix1 << prefix2 << "linked_device->authenticator_pubkey: "
+                         << ToHexStr(linkedDevice->authenticator_pubkey));
+        LogDebug(prefix1 << prefix2 << "linked_device->authenticator_name: "
+                         << ToHexStr(linkedDevice->authenticator_name)
+                         << " == " << ToStr(linkedDevice->authenticator_name));
+        LogDebug(prefix1 << prefix2
+                         << "linked_device->signature: " << ToHexStr(linkedDevice->signature));
+        LogDebug(prefix1 << prefix2 << "linked_device->tunnel_server_domain: "
+                         << ToHexStr(linkedDevice->tunnel_server_domain)
+                         << " == " << ToStr(linkedDevice->tunnel_server_domain));
+        LogDebug(prefix1 << prefix2 << "linked_device->identity_key: "
+                         << ToHexStr(linkedDevice->identity_key));
+    }
+#else
+    (void)prefix1;
+    (void)prefix2;
+    (void) linkedDevice;
+#endif
+}
+
+template <RequestKind REQUEST_KIND>
+void LogRequest(
+    const wauthn_client_data_s &clientData,
+    const std::conditional_t<REQUEST_KIND == RequestKind::MC,
+                             wauthn_pubkey_cred_creation_options_s,
+                             wauthn_pubkey_cred_request_options_s> &options)
+
+{
+#ifdef BUILD_TYPE_DEBUG
+    static constexpr const char *REQUEST_KIND_PREFIX =
+        REQUEST_KIND == RequestKind::MC ? "MC: " : "GA: ";
+    if (!clientData.client_data_json)
+        LogDebug(REQUEST_KIND_PREFIX << "client_data->client_data_json: null");
+    else {
+        LogDebug(REQUEST_KIND_PREFIX
+                 << "client_data->client_data_json: " << ToHexStr(clientData.client_data_json)
+                 << " == " << ToStr(clientData.client_data_json));
+    }
+
+    LogDebug(REQUEST_KIND_PREFIX << "client_data->hash_alg: " << [&]() -> std::string {
+        switch (clientData.hash_alg) {
+        case WAUTHN_HASH_ALGORITHM_SHA_256: return "SHA-256";
+        }
+        return std::to_string(clientData.hash_alg);
+    }());
+
+    if constexpr (REQUEST_KIND == RequestKind::MC) {
+        if (!options.rp)
+            LogDebug(REQUEST_KIND_PREFIX << "options->rp: null");
+        else {
+            LogDebug(REQUEST_KIND_PREFIX << "options->rp->name: " << ToStr(options.rp->name));
+            LogDebug(REQUEST_KIND_PREFIX << "options->rp->id: " << ToStr(options.rp->id));
+        }
+
+        if (!options.user)
+            LogDebug(REQUEST_KIND_PREFIX << "options->user: null");
+        else {
+            LogDebug(REQUEST_KIND_PREFIX << "options->user->name: " << ToStr(options.user->name));
+            LogDebug(REQUEST_KIND_PREFIX << "options->user->id: " << ToHexStr(options.user->id));
+            LogDebug(REQUEST_KIND_PREFIX << "options->user->display_name: "
+                                         << ToStr(options.user->display_name));
+        }
+
+        if (!options.pubkey_cred_params)
+            LogDebug(REQUEST_KIND_PREFIX << "options->pubkey_cred_params: null");
+        else {
+            LogDebug(REQUEST_KIND_PREFIX << "options->pubkey_cred_params->size: "
+                                         << options.pubkey_cred_params->size);
+            for (size_t i = 0; i < options.pubkey_cred_params->size; ++i) {
+                LogDebug(REQUEST_KIND_PREFIX
+                         << "options->pubkey_cred_params->params[" << i
+                         << "]: {type: " << ToStr(options.pubkey_cred_params->params[i].type)
+                         << ", alg: " << ToStr(options.pubkey_cred_params->params[i].alg) << "}");
+            }
+        }
+    }
+
+    LogDebug(REQUEST_KIND_PREFIX << "options->timeout: " << options.timeout << " ms");
+
+    static constexpr auto logCredDescriptors =
+        [](const char *fieldName, wauthn_pubkey_cred_descriptors_s *credDescriptors) {
+            if (!credDescriptors)
+                LogDebug(REQUEST_KIND_PREFIX << "options->" << fieldName << ": null");
+            else {
+                LogDebug(REQUEST_KIND_PREFIX << "options->" << fieldName
+                                             << "->size: " << credDescriptors->size);
+                for (size_t i = 0; i < credDescriptors->size; ++i) {
+                    LogDebug(REQUEST_KIND_PREFIX
+                             << "options->" << fieldName << "->descriptors[" << i
+                             << "].type: " << ToStr(credDescriptors->descriptors[i].type));
+                    LogDebug(REQUEST_KIND_PREFIX
+                             << "options->" << fieldName << "->descriptors[" << i
+                             << "].id: " << ToHexStr(credDescriptors->descriptors[i].id));
+                    LogDebug(REQUEST_KIND_PREFIX
+                             << "options->" << fieldName << "->descriptors[" << i
+                             << "].transports: "
+                             << TransportsToStr(credDescriptors->descriptors[i].transports));
+                }
+            }
+        };
+
+    if constexpr (REQUEST_KIND == RequestKind::MC) {
+        logCredDescriptors("exclude_credentials", options.exclude_credentials);
+        if (!options.authenticator_selection)
+            LogDebug(REQUEST_KIND_PREFIX << "options->authenticator_selection: null");
+        else {
+            LogDebug(REQUEST_KIND_PREFIX << "options->authenticator_selection->attachment: "
+                                         << ToStr(options.authenticator_selection->attachment));
+            LogDebug(REQUEST_KIND_PREFIX << "options->authenticator_selection->resident_key: "
+                                         << ToStr(options.authenticator_selection->resident_key));
+            LogDebug(REQUEST_KIND_PREFIX
+                     << "options->authenticator_selection->require_resident_key: "
+                     << (options.authenticator_selection->require_resident_key ? "true" : "false"));
+            LogDebug(REQUEST_KIND_PREFIX
+                     << "options->authenticator_selection->user_verification: "
+                     << ToStr(options.authenticator_selection->user_verification));
+        }
+    } else {
+        LogDebug(REQUEST_KIND_PREFIX << "options->rpId: " << ToStr(options.rpId));
+        logCredDescriptors("allow_credentials", options.allow_credentials);
+        LogDebug(REQUEST_KIND_PREFIX << "options->user_verification: "
+                                     << ToStr(options.user_verification));
+    }
+
+    if (!options.hints)
+        LogDebug(REQUEST_KIND_PREFIX << "options->hints: null");
+    else {
+        LogDebug(REQUEST_KIND_PREFIX << "options->hints->size: " << options.hints->size);
+        for (size_t i = 0; i < options.hints->size; ++i)
+            LogDebug(REQUEST_KIND_PREFIX << "options->hints->hints[" << i
+                                         << "]: " << ToStr(options.hints->hints[i]));
+    }
+
+    LogDebug(REQUEST_KIND_PREFIX << "options->attestation: " << ToStr(options.attestation));
+
+    if (!options.attestation_formats)
+        LogDebug(REQUEST_KIND_PREFIX << "options->attestation_formats: null");
+    else {
+        LogDebug(REQUEST_KIND_PREFIX << "options->attestation_formats->size: "
+                                     << options.attestation_formats->size);
+        for (size_t i = 0; i < options.attestation_formats->size; ++i)
+            LogDebug(REQUEST_KIND_PREFIX
+                     << "options->attestation_formats->attestation_formats[" << i
+                     << "]: " << ToHexStr(options.attestation_formats->attestation_formats + i)
+                     << " == " << ToStr(options.attestation_formats->attestation_formats + i));
+    }
+
+    LogExtensions(REQUEST_KIND_PREFIX, "options", options.extensions);
+    LogLinkedDevice(REQUEST_KIND_PREFIX, "options->", options.linked_device);
+#else
+    (void)clientData;
+    (void)options;
+#endif
+}
+
+template void
+LogRequest<RequestKind::MC>(const wauthn_client_data_s &clientData,
+                            const wauthn_pubkey_cred_creation_options_s &options);
+
+template void
+LogRequest<RequestKind::GA>(const wauthn_client_data_s &clientData,
+                            const wauthn_pubkey_cred_request_options_s &options);
+
 } // namespace WebAuthn
\ No newline at end of file
index 33c859c575d009f0f65f7133bc79de5a7e70a5a0..9d551e515a397d0cfaf5449590e36fd6ddf7a970 100644 (file)
  */
 #pragma once
 
+#include <cstdint>
 #include <memory>
 #include <string>
 #include <singleton.h>
 #include <sstream>
+#include <webauthn-types.h>
 
 namespace WA {
 
@@ -73,6 +75,39 @@ typedef Singleton<WebAuthnLog> WebAuthnLogSingleton;
  * in every translation unit because there's an explicit instantiation somewhere.
  */
 extern template class Singleton<WebAuthnLog>;
+
+
+enum class RequestKind {
+    MC,
+    GA,
+};
+
+#ifdef BUILD_TYPE_DEBUG
+template <class Bytes>
+std::string LowercaseHexStringOf(const Bytes &bytes)
+{
+    static_assert(sizeof(typename Bytes::value_type) == 1);
+    std::string res;
+    for (const uint8_t byte : bytes) {
+        constexpr char digits[] = "0123456789abcdef";
+        res += digits[byte >> 4];
+        res += digits[byte & 15];
+    }
+    return res;
+}
+
+using BufferView = std::basic_string_view<uint8_t>;
+#endif
+void LogLinkedDevice(const char *prefix1,
+                     const char *prefix2,
+                     const wauthn_hybrid_linked_data_s *linkedDevice);
+
+template <RequestKind REQUEST_KIND>
+void LogRequest(const wauthn_client_data_s &clientData,
+                const std::conditional_t<REQUEST_KIND == RequestKind::MC,
+                                         wauthn_pubkey_cred_creation_options_s,
+                                         wauthn_pubkey_cred_request_options_s> &options);
+
 } // namespace WebAuthn
 
 #define WA_MACRO_FOR_LOGGING(message, function)           \