Check discoverable credentials capability 37/316337/4
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Mon, 19 Aug 2024 14:57:08 +0000 (16:57 +0200)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Fri, 6 Sep 2024 07:05:49 +0000 (09:05 +0200)
Add tests

Change-Id: I801720887e4579bcbe55b78f8615ccdcb02943d5

srcs/message.cpp
srcs/message.h
tests/man_tests.cpp
tests/message_tests.cpp

index 3ee47d58bef323f2f9f628b7b53fd05ff1f14c84..45dc71d4d8f52477284858bb6b44bc559592381b 100644 (file)
@@ -309,6 +309,21 @@ MakeCredentialCommand::MakeCredentialCommand(const wauthn_client_data_s &clientD
                                              PostHandshakeResponse::Options supportedOptions)
 : CtapCommand(Method::MAKE_CREDENTIAL, clientData, std::move(supportedOptions)), m_options(options)
 {
+    if (EffectiveRK() && !m_supportedOptions.rk)
+        THROW_UNSUPPORTED("Authenticator does not support requested discoverable credentials");
+}
+
+bool MakeCredentialCommand::EffectiveRK() const noexcept
+{
+    if (!m_options.authenticator_selection)
+        return false;
+
+    const auto &selection = *m_options.authenticator_selection;
+    return (selection.resident_key == WAUTHN_RESIDENT_KEY_REQUIREMENT_REQUIRED) ||
+        (selection.resident_key == WAUTHN_RESIDENT_KEY_REQUIREMENT_PREFERRED &&
+         m_supportedOptions.rk) ||
+        (selection.resident_key == WAUTHN_RESIDENT_KEY_REQUIREMENT_NONE &&
+         selection.require_resident_key);
 }
 
 void MakeCredentialCommand::Serialize(Buffer &output) const
@@ -418,12 +433,8 @@ void MakeCredentialCommand::Serialize(Buffer &output) const
             auto optionsMap = map.OpenMapAt(KEY_MC_CMD_OPTIONS, 2);
             const auto &selection = *m_options.authenticator_selection;
 
-            auto effectiveRk =
-                (selection.resident_key == WAUTHN_RESIDENT_KEY_REQUIREMENT_REQUIRED) ||
-                (selection.resident_key == WAUTHN_RESIDENT_KEY_REQUIREMENT_PREFERRED &&
-                 m_supportedOptions.rk) ||
-                (selection.resident_key == WAUTHN_RESIDENT_KEY_REQUIREMENT_NONE &&
-                 selection.require_resident_key);
+            auto effectiveRk = EffectiveRK();
+
             optionsMap.AppendBooleanAt("rk", effectiveRk);
 
             // TODO user presence "up" not mapped?
@@ -470,6 +481,9 @@ GetAssertionCommand::GetAssertionCommand(const wauthn_client_data_s &clientData,
                                          PostHandshakeResponse::Options supportedOptions)
 : CtapCommand(Method::GET_ASSERTION, clientData, std::move(supportedOptions)), m_options(options)
 {
+    if (!m_options.allow_credentials && !m_supportedOptions.rk)
+        THROW_UNSUPPORTED("Authenticator does not support discoverable credentials."
+                          "'allow_credentials' must not be empty.");
 }
 
 void GetAssertionCommand::Serialize(Buffer &output) const
index 4eca4adb336b3c291ab745650f8feb82acf41dba..cd016a13b9a67661e290e6d7da29e39f5a28efec 100644 (file)
@@ -127,6 +127,8 @@ public:
     void Serialize(Buffer &output) const override;
 
 private:
+    [[nodiscard]] bool EffectiveRK() const noexcept;
+
     const wauthn_pubkey_cred_creation_options_s &m_options;
 };
 
index 2e1feef1ba234576ab64f45ae1fcf64a17e32241..2d417399507ece542ae622d4ab73b0dfd96e9f30 100644 (file)
@@ -489,8 +489,8 @@ void UpdateLinkedDataCallbackImpl(const wauthn_hybrid_linked_data_s *linkedData,
     if (result == WAUTHN_ERROR_NONE)
         seenLastUpdateCallback = true;
     else if (result != WAUTHN_ERROR_NONE_AND_WAIT) {
-        std::cout << __FUNCTION__ << ": update_linked_data callback failed with code: "
-                  << get_error_message(result)
+        std::cout << __FUNCTION__
+                  << ": update_linked_data callback failed with code: " << get_error_message(result)
                   << std::endl; // NOLINT(performance-avoid-endl)
         testContents.succeeded = false;
         return;
index 931cd5ccbccabed13e81c96c8eefc67f35c0a3fb..63c9525727b5ae1307327631cdc3230d87403696 100644 (file)
@@ -222,6 +222,32 @@ void TestCredentials(T &msg, wauthn_pubkey_cred_descriptors_s *&credentials)
     credentials = nullptr;
 }
 
+constexpr unsigned char CLIENT_DATA_JSON_DATA[] = "\x00\x01\x02\x03";
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+wauthn_const_buffer_s CLIENT_DATA_JSON{CLIENT_DATA_JSON_DATA, sizeof(CLIENT_DATA_JSON_DATA) - 1};
+constexpr wauthn_client_data_s CLIENT_DATA{&CLIENT_DATA_JSON, WAUTHN_HASH_ALGORITHM_SHA_256};
+
+constexpr char RP_NAME[] = "Rp name";
+constexpr char RP_ID[] = "Rp.id";
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+wauthn_rp_entity_s RP{RP_NAME, RP_ID};
+
+constexpr unsigned char USER_ID_DATA[] = "\x00\x01\x02\x03\x04\x05\x06\x07";
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+wauthn_const_buffer_s USER_ID{USER_ID_DATA, sizeof(USER_ID_DATA) - 1};
+
+constexpr char USER_NAME[] = "User name";
+constexpr char USER_DISPLAY_NAME[] = "User display name";
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+wauthn_user_entity_s USER{USER_NAME, &USER_ID, USER_DISPLAY_NAME};
+
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+wauthn_pubkey_cred_param_s PUBKEY_PARAM[] = {
+    {WAUTHN_PUBKEY_CRED_TYPE_PUBLIC_KEY, WAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256}
+};
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+wauthn_pubkey_cred_params_s PUBKEY_PARAMS{1, PUBKEY_PARAM};
+
 } // namespace
 
 TEST(Messages, ParsePostHandshakeMessage1)
@@ -331,30 +357,6 @@ TEST(Messages, DomainName)
 
 TEST(Messages, SerializeMakeCredential1)
 {
-    unsigned char client_data_json_data[] = "\x00\x01\x02\x03";
-    wauthn_const_buffer_s client_data_json;
-    client_data_json.data = client_data_json_data;
-    client_data_json.size = sizeof(client_data_json_data) - 1;
-    wauthn_client_data_s client_data;
-    client_data.hash_alg = WAUTHN_HASH_ALGORITHM_SHA_256;
-    client_data.client_data_json = &client_data_json;
-
-    wauthn_rp_entity_s rp;
-    char rp_name[] = "Rp name";
-    rp.name = rp_name;
-    char rp_id[] = "Rp.id";
-    rp.id = rp_id;
-
-    wauthn_const_buffer_s user_id;
-    unsigned char user_id_data[] = "\x00\x01\x02\x03\x04\x05\x06\x07";
-    user_id.data = user_id_data;
-    user_id.size = sizeof(user_id_data) - 1;
-    wauthn_user_entity_s user;
-    char user_name[] = "User name";
-    user.name = user_name;
-    user.id = &user_id;
-    char user_display_name[] = "User display name";
-    user.display_name = user_display_name;
     wauthn_pubkey_cred_params_s pubkey_cred_params;
 
     constexpr std::array ALGS = {WAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256,
@@ -378,11 +380,11 @@ TEST(Messages, SerializeMakeCredential1)
     pubkey_cred_params.size = ALGS.size();
     pubkey_cred_params.params = params;
     wauthn_pubkey_cred_creation_options_s options;
-    options.rp = &rp;
-    options.user = &user;
+    options.rp = &RP;
+    options.user = &USER;
     options.pubkey_cred_params = &pubkey_cred_params;
     options.exclude_credentials = nullptr;
-    options.authenticator_selection = nullptr; // TODO
+    options.authenticator_selection = nullptr;
     options.hints = nullptr;
     options.attestation = WAUTHN_ATTESTATION_PREF_NONE;
     options.attestation_formats = nullptr;
@@ -391,7 +393,7 @@ TEST(Messages, SerializeMakeCredential1)
 
     // serialize MC command
     Buffer buffer;
-    const MakeCredentialCommand msg(client_data, options, {false, false});
+    const MakeCredentialCommand msg(CLIENT_DATA, options, {false, false});
 
     ASSERT_NO_THROW(msg.Serialize(buffer));
 
@@ -481,7 +483,7 @@ TEST(Messages, SerializeMakeCredential2)
 
     // serialize MC command
     Buffer buffer;
-    const MakeCredentialCommand msg(client_data, options, {false, false});
+    const MakeCredentialCommand msg(client_data, options, {true, false});
 
     ASSERT_NO_THROW(msg.Serialize(buffer));
     EXPECT_EQ(ToBufferView(buffer),
@@ -490,6 +492,52 @@ TEST(Messages, SerializeMakeCredential2)
     TestCredentials(msg, options.exclude_credentials);
 }
 
+TEST(Messages, MakeCredentialDiscoverableCredentials)
+{
+    wauthn_pubkey_cred_creation_options_s options;
+    options.rp = &RP;
+    options.user = &USER;
+    options.pubkey_cred_params = &PUBKEY_PARAMS;
+    options.exclude_credentials = nullptr;
+    options.authenticator_selection = nullptr;
+    options.hints = nullptr;
+    options.attestation = WAUTHN_ATTESTATION_PREF_NONE;
+    options.attestation_formats = nullptr;
+    options.extensions = nullptr;
+    options.linked_device = nullptr;
+
+    EXPECT_NO_THROW(MakeCredentialCommand(CLIENT_DATA, options, {false, false}));
+    EXPECT_NO_THROW(MakeCredentialCommand(CLIENT_DATA, options, {true, false}));
+
+    wauthn_authenticator_sel_cri_s authenticator_selection{
+        WAUTHN_AUTHENTICATOR_ATTACHMENT_NONE,
+        WAUTHN_RESIDENT_KEY_REQUIREMENT_NONE,
+        false,
+        WAUTHN_USER_VERIFICATION_REQUIREMENT_NONE};
+    options.authenticator_selection = &authenticator_selection;
+
+    auto testDiscoverable =
+        [&](bool requireRK, wauthn_resident_key_requirement_e requirement, bool throws) {
+            authenticator_selection.require_resident_key = requireRK;
+            authenticator_selection.resident_key = requirement;
+            EXPECT_NO_THROW(MakeCredentialCommand(CLIENT_DATA, options, {true, false}));
+            if (throws)
+                EXPECT_THROW(MakeCredentialCommand(CLIENT_DATA, options, {false, false}),
+                             Exception::NotSupported);
+            else
+                EXPECT_NO_THROW(MakeCredentialCommand(CLIENT_DATA, options, {false, false}));
+        };
+
+    testDiscoverable(false, WAUTHN_RESIDENT_KEY_REQUIREMENT_NONE, false);
+    testDiscoverable(false, WAUTHN_RESIDENT_KEY_REQUIREMENT_DISCOURAGED, false);
+    testDiscoverable(false, WAUTHN_RESIDENT_KEY_REQUIREMENT_PREFERRED, false);
+    testDiscoverable(false, WAUTHN_RESIDENT_KEY_REQUIREMENT_REQUIRED, true);
+    testDiscoverable(true, WAUTHN_RESIDENT_KEY_REQUIREMENT_NONE, true);
+    testDiscoverable(true, WAUTHN_RESIDENT_KEY_REQUIREMENT_DISCOURAGED, false);
+    testDiscoverable(true, WAUTHN_RESIDENT_KEY_REQUIREMENT_PREFERRED, false);
+    testDiscoverable(true, WAUTHN_RESIDENT_KEY_REQUIREMENT_REQUIRED, true);
+}
+
 TEST(Messages, SerializeGetAssertion)
 {
     unsigned char client_data_json_data[] =
@@ -554,6 +602,29 @@ TEST(Messages, SerializeGetAssertion)
     TestCredentials(msg, options.allow_credentials);
 }
 
+TEST(Messages, GetAssertionDiscoverableCredentials)
+{
+    wauthn_pubkey_cred_request_options_s options;
+    options.timeout = 0; // not serialized
+    options.rpId = RP_ID;
+    options.allow_credentials = nullptr;
+    options.user_verification = WAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED;
+    options.hints = nullptr;
+    options.attestation = WAUTHN_ATTESTATION_PREF_NONE;
+    options.attestation_formats = nullptr;
+    options.extensions = nullptr;
+    options.linked_device = nullptr;
+
+    EXPECT_THROW(GetAssertionCommand(CLIENT_DATA, options, {false, false}),
+                 Exception::NotSupported);
+    EXPECT_NO_THROW(GetAssertionCommand(CLIENT_DATA, options, {true, false}));
+
+    wauthn_pubkey_cred_descriptors_s pubkeyCredDescriptors{1, &GetPubkeyDescriptor()};
+    options.allow_credentials = &pubkeyCredDescriptors;
+    EXPECT_NO_THROW(GetAssertionCommand(CLIENT_DATA, options, {false, false}));
+    EXPECT_NO_THROW(GetAssertionCommand(CLIENT_DATA, options, {true, false}));
+}
+
 TEST(Messages, ShutdownMessage)
 {
     const ShutdownMessage shutdown;