Add TCs for basic BLE operations
authorDongsun Lee <ds73.lee@samsung.com>
Mon, 30 Oct 2023 00:47:20 +0000 (09:47 +0900)
committer이동선/Security Assurance Lab(SR)/삼성전자 <ds73.lee@samsung.com>
Mon, 6 Nov 2023 02:07:13 +0000 (11:07 +0900)
packaging/webauthn.spec
tests/CMakeLists.txt
tests/ble_test.cpp [new file with mode: 0644]
tests/main.cpp

index 9b08d24..9181346 100644 (file)
@@ -67,7 +67,9 @@ HAL API of Web Authentication Service (development files)
 Summary:    Web Authentication Service (unit test)
 License:    Apache-2.0
 Group:      Security/Development
-BuildRequires: gtest
+BuildRequires: pkgconfig(gtest)
+BuildRequires: pkgconfig(capi-network-bluetooth)
+BuildRequires: pkgconfig(capi-appfw-app-control)
 Requires:      %{name} = %{version}-%{release}
 
 %description unit-test
index 3b3bb96..2c3177d 100644 (file)
@@ -1,10 +1,14 @@
 PKG_CHECK_MODULES(UNIT_TESTS_DEPS
     REQUIRED
     dlog
+    gtest
+    capi-network-bluetooth
+    capi-appfw-app-control
     )
 
 SET(UNIT_TESTS_SOURCES
     ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/ble_test.cpp
 )
 
 SET_SOURCE_FILES_PROPERTIES(
diff --git a/tests/ble_test.cpp b/tests/ble_test.cpp
new file mode 100644 (file)
index 0000000..f14989b
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ *  Copyright (c) 2023 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
+ *
+ *
+ * @file        ble_test.cpp
+ * @version     1.0
+ * @brief       basic functional tests for tizen bluetooth API
+ */
+
+
+#include <string>
+#include <iostream>
+#include <gtest/gtest.h>
+#include <dlog.h>
+#include <unistd.h>
+#include <netinet/in.h>
+
+#include <bluetooth.h>
+#include <bluetooth_internal.h>
+#include <app_control.h>
+
+
+#define CODE_TO_STRING(name) case name: return #name
+
+#define BLUETOOTH_UUID_STRING_MAX       50
+
+const char * ble_error_to_string(int error) {
+    switch (error) {
+        CODE_TO_STRING(BT_ERROR_NONE);
+        CODE_TO_STRING(BT_ERROR_CANCELLED);
+        CODE_TO_STRING(BT_ERROR_INVALID_PARAMETER);
+        CODE_TO_STRING(BT_ERROR_OUT_OF_MEMORY);
+        CODE_TO_STRING(BT_ERROR_RESOURCE_BUSY);
+        CODE_TO_STRING(BT_ERROR_TIMED_OUT);
+        CODE_TO_STRING(BT_ERROR_NOW_IN_PROGRESS);
+        CODE_TO_STRING(BT_ERROR_PERMISSION_DENIED);
+        CODE_TO_STRING(BT_ERROR_QUOTA_EXCEEDED);
+        CODE_TO_STRING(BT_ERROR_NO_DATA);
+        CODE_TO_STRING(BT_ERROR_DEVICE_POLICY_RESTRICTION);
+        CODE_TO_STRING(BT_ERROR_NOT_INITIALIZED);
+        CODE_TO_STRING(BT_ERROR_NOT_ENABLED);
+        CODE_TO_STRING(BT_ERROR_ALREADY_DONE);
+        CODE_TO_STRING(BT_ERROR_OPERATION_FAILED);
+        CODE_TO_STRING(BT_ERROR_NOT_IN_PROGRESS);
+        CODE_TO_STRING(BT_ERROR_REMOTE_DEVICE_NOT_BONDED);
+        CODE_TO_STRING(BT_ERROR_AUTH_REJECTED);
+        CODE_TO_STRING(BT_ERROR_AUTH_FAILED);
+        CODE_TO_STRING(BT_ERROR_REMOTE_DEVICE_NOT_FOUND);
+        CODE_TO_STRING(BT_ERROR_REMOTE_DEVICE_NOT_CONNECTED);
+        CODE_TO_STRING(BT_ERROR_AGAIN);
+        CODE_TO_STRING(BT_ERROR_SERVICE_NOT_FOUND);
+        CODE_TO_STRING(BT_ERROR_AUTHORIZATION_REJECTED);
+        CODE_TO_STRING(BT_ERROR_MAX_CONNECTION);
+        default: return "Error not defined";
+    }
+}
+
+const char * ble_state_to_string(bt_adapter_state_e state) {
+    switch (state) {
+        CODE_TO_STRING(BT_ADAPTER_DISABLED);
+        CODE_TO_STRING(BT_ADAPTER_ENABLED);
+        default: return "State not defined";
+    }
+}
+
+const char * appctrl_error_to_string(int error) {
+    switch (error) {
+        CODE_TO_STRING(APP_CONTROL_ERROR_NONE);
+        CODE_TO_STRING(APP_CONTROL_ERROR_INVALID_PARAMETER);
+        CODE_TO_STRING(APP_CONTROL_ERROR_OUT_OF_MEMORY);
+        CODE_TO_STRING(APP_CONTROL_ERROR_APP_NOT_FOUND);
+        CODE_TO_STRING(APP_CONTROL_ERROR_KEY_NOT_FOUND);
+        CODE_TO_STRING(APP_CONTROL_ERROR_KEY_REJECTED);
+        CODE_TO_STRING(APP_CONTROL_ERROR_INVALID_DATA_TYPE);
+        CODE_TO_STRING(APP_CONTROL_ERROR_LAUNCH_REJECTED);
+        CODE_TO_STRING(APP_CONTROL_ERROR_PERMISSION_DENIED);
+        CODE_TO_STRING(APP_CONTROL_ERROR_LAUNCH_FAILED);
+        CODE_TO_STRING(APP_CONTROL_ERROR_TIMED_OUT);
+        CODE_TO_STRING(APP_CONTROL_ERROR_IO_ERROR);
+        CODE_TO_STRING(BT_ERROR_MAX_CONNECTION);
+        default: return "Error not defined";
+    }
+}
+
+void __uuid_hex_to_string(unsigned char *uuid, char *str) {
+       uint32_t uuid0, uuid4;
+       uint16_t uuid1, uuid2, uuid3, uuid5;
+
+       memcpy(&uuid0, &(uuid[0]), 4);
+       memcpy(&uuid1, &(uuid[4]), 2);
+       memcpy(&uuid2, &(uuid[6]), 2);
+       memcpy(&uuid3, &(uuid[8]), 2);
+       memcpy(&uuid4, &(uuid[10]), 4);
+       memcpy(&uuid5, &(uuid[14]), 2);
+
+       snprintf((char *)str, BLUETOOTH_UUID_STRING_MAX, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
+               ntohl(uuid0), ntohs(uuid1),
+               ntohs(uuid2), ntohs(uuid3),
+               ntohl(uuid4), ntohs(uuid5));
+       return;
+}
+
+void __bt_string_to_uuid_hex(const char *str, unsigned char *uuid) {
+       uint32_t uuid0, uuid4;
+       uint16_t uuid1, uuid2, uuid3, uuid5;
+
+       sscanf(str, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
+                       &uuid0, &uuid1, &uuid2, &uuid3, &uuid4, &uuid5);
+
+       uuid0 = htonl(uuid0);
+       uuid1 = htons(uuid1);
+       uuid2 = htons(uuid2);
+       uuid3 = htons(uuid3);
+       uuid4 = htonl(uuid4);
+       uuid5 = htons(uuid5);
+
+       memcpy(&(uuid[0]), &uuid0, 4);
+       memcpy(&(uuid[4]), &uuid1, 2);
+       memcpy(&(uuid[6]), &uuid2, 2);
+       memcpy(&(uuid[8]), &uuid3, 2);
+       memcpy(&(uuid[10]), &uuid4, 4);
+       memcpy(&(uuid[14]), &uuid5, 2);
+       return;
+}
+
+std::string __bin_to_hex(const char* source, int length) {
+    std::string dest;
+    for(int i = 0; i < length; i++) {
+        char digit[3];
+        sprintf(digit, "%02x", source[i]);
+        dest.append(digit);
+    }
+    return dest;
+}
+
+class BleTest : public ::testing::Test {
+protected:
+    void SetUp() override {
+        int ret = BT_ERROR_NONE;
+
+        ret = bt_initialize();
+        if (ret != BT_ERROR_NONE) {
+            std::cout << "[bt_initialize] failed." << std::endl;
+            return;
+        }
+    }
+
+    void TearDown() override {
+        int ret = BT_ERROR_NONE;
+
+        ret = bt_deinitialize();
+        if (ret != BT_ERROR_NONE) {
+            std::cout << "[bt_deinitialize] failed." << std::endl;
+            return;
+        }
+    }
+};
+
+void __state_changed_callback(int result, bt_adapter_state_e adapter_state, void *user_data);
+void __scan_result_cb(int result, bt_adapter_le_device_scan_result_info_s *info, void *user_data);
+
+class CallbackResultHolder {
+private:
+    GMainLoop *mainloop; // The GMainLoop is required for state_changed_callback() to be called.
+    int result;
+    bt_adapter_state_e adapter_state;
+    bt_adapter_le_service_data_s *service_data;
+
+public:
+    CallbackResultHolder() : result(BT_ERROR_NONE),
+                             adapter_state(BT_ADAPTER_DISABLED),
+                             service_data(NULL) {
+        mainloop = g_main_loop_new(NULL, FALSE);
+    }
+    ~CallbackResultHolder() {
+        g_main_loop_unref (mainloop);
+        if (service_data != NULL)
+            bt_adapter_le_free_service_data_list(service_data, 1);
+    }
+
+    void wait_async_result() {
+        g_main_loop_run(mainloop);
+        return;
+    }
+
+    void set_state_changed_cb() {
+        bt_adapter_set_state_changed_cb(__state_changed_callback, this);
+    }
+
+    void set_state_callback_result(int result, bt_adapter_state_e adapter_state) {
+        this->result = result;
+        this->adapter_state = adapter_state;
+        g_main_loop_quit(mainloop);
+    }
+
+    int get_changed_state(bt_adapter_state_e *adapter_state) {
+        *adapter_state = this->adapter_state;
+        return result;
+    }
+
+    void set_scan_callback_result(int result, bt_adapter_le_service_data_s *service_data) {
+        this->result = result;
+        this->service_data = service_data;
+        g_main_loop_quit(mainloop);
+    }
+
+    int get_scan_result(bt_adapter_le_service_data_s **service_data) {
+        (void) service_data; // to avoid -Werror=unused-but-set-parameter.
+        *service_data = this->service_data;
+        return result;
+    }
+};
+
+void __state_changed_callback(int result, bt_adapter_state_e adapter_state, void *user_data) {
+    CallbackResultHolder* holder = static_cast<CallbackResultHolder *>(user_data);
+    holder->set_state_callback_result(result, adapter_state);
+}
+
+void __scan_result_cb(int result, bt_adapter_le_device_scan_result_info_s *info, void *user_data) {
+    // Conversion between 16 bit UUID and 128 bit UUID
+    // - https://stackoverflow.com/questions/36212020/how-can-i-convert-a-bluetooth-16-bit-service-uuid-into-a-128-bit-uuid
+    // The UUID, FFF9, is the same as "0000fff9-0000-1000-8000-00805f9b34fb".
+    const std::string FIDO_CABLE_UUID16 = "FFF9"; // Used for Android devices
+    const std::string GOOGLE_CABLE_UUID16 = "FDE2"; // Used for Apple devices
+
+    if(result != BT_ERROR_NONE)
+        return;
+
+    bt_adapter_le_packet_type_e pkt_type = BT_ADAPTER_LE_PACKET_ADVERTISING;
+    bt_adapter_le_service_data_s *data_list;
+    int count;
+    int ret;
+    ret = bt_adapter_le_get_scan_result_service_data_list(info, pkt_type, &data_list, &count);
+    if (ret == BT_ERROR_NONE) { // To avoid BT_ERROR_NO_DATA
+        if (FIDO_CABLE_UUID16.compare(data_list[0].service_uuid) == 0
+                || GOOGLE_CABLE_UUID16.compare(data_list[0].service_uuid) == 0) {
+            std::cout   << "Scanned Service Data "
+                        << ": UUID=" << data_list[0].service_uuid
+                        << ", Data=" << __bin_to_hex(data_list[0].service_data, data_list[0].service_data_len)
+                        << std::endl;
+            CallbackResultHolder* holder = static_cast<CallbackResultHolder *>(user_data);
+            holder->set_scan_callback_result(result, data_list);
+        }
+    }
+}
+
+
+TEST_F(BleTest, testCheckStatus)
+{
+    int ret = BT_ERROR_NONE;
+    bt_adapter_state_e adapter_state;
+
+    ret = bt_adapter_get_state(&adapter_state);
+    EXPECT_EQ(ret, BT_ERROR_NONE)
+            << "[bt_adapter_get_state] failed. " << "ret=" << ble_error_to_string(ret) << std::endl;
+
+    std::cout << "BLE state=" << ble_state_to_string(adapter_state) << std::endl;
+}
+
+TEST_F(BleTest, testEnableBle)
+{
+    int ret = BT_ERROR_NONE;
+    bt_adapter_state_e adapter_state;
+    CallbackResultHolder result_holder;
+
+    ret = bt_adapter_get_state(&adapter_state);
+    EXPECT_EQ(ret, BT_ERROR_NONE);
+    std::cout << "Before enabling. state=" << ble_state_to_string(adapter_state) << std::endl;
+
+    if(adapter_state == BT_ADAPTER_ENABLED) {
+        result_holder.set_state_changed_cb();
+        ret = bt_adapter_disable();
+        EXPECT_EQ(ret, BT_ERROR_NONE);
+        // wait until bt_adapter_disable() is completed.
+        result_holder.wait_async_result();
+        ret = result_holder.get_changed_state(&adapter_state);
+        EXPECT_EQ(ret, BT_ERROR_NONE);
+        EXPECT_EQ(adapter_state, BT_ADAPTER_DISABLED);
+        std::cout << "After disabling. state=" << ble_state_to_string(adapter_state) << std::endl;
+    }
+
+    // enable bluetooth adapter.
+    result_holder.set_state_changed_cb();
+    ret = bt_adapter_enable();
+    EXPECT_EQ(ret, BT_ERROR_NONE)
+            << "[bt_adapter_enable] failed. " << "ret=" << ble_error_to_string(ret) << std::endl;
+
+    // wait until bt_adapter_enable() is completed.
+    result_holder.wait_async_result();
+    ret = result_holder.get_changed_state(&adapter_state);
+    EXPECT_EQ(ret, BT_ERROR_NONE);
+    EXPECT_EQ(adapter_state, BT_ADAPTER_ENABLED);
+    std::cout << "After enabling. state=" << ble_state_to_string(adapter_state) << std::endl;
+}
+
+TEST_F(BleTest, testEnableBleManually)
+{
+    int ret = BT_ERROR_NONE;
+    bt_adapter_state_e adapter_state;
+    CallbackResultHolder result_holder;
+
+    ret = bt_adapter_get_state(&adapter_state);
+    EXPECT_EQ(ret, BT_ERROR_NONE);
+    std::cout << "Before enabling. state=" << ble_state_to_string(adapter_state) << std::endl;
+
+    if(adapter_state == BT_ADAPTER_ENABLED) {
+        result_holder.set_state_changed_cb();
+        ret = bt_adapter_disable();
+        EXPECT_EQ(ret, BT_ERROR_NONE);
+        // wait until bt_adapter_disable() is completed.
+        result_holder.wait_async_result();
+        ret = result_holder.get_changed_state(&adapter_state);
+        EXPECT_EQ(ret, BT_ERROR_NONE);
+        EXPECT_EQ(adapter_state, BT_ADAPTER_DISABLED);
+        std::cout << "After disabling. state=" << ble_state_to_string(adapter_state) << std::endl;
+    }
+
+    // enable bluetooth adapter manually.
+    result_holder.set_state_changed_cb();
+    app_control_h service = NULL;
+    app_control_create(&service);
+    EXPECT_TRUE(service != NULL) << "service_create failed!";
+
+    app_control_set_operation(service, APP_CONTROL_OPERATION_SETTING_BT_ENABLE);
+    ret = app_control_send_launch_request(service, NULL, NULL);
+    EXPECT_EQ(ret, APP_CONTROL_ERROR_NONE)
+            << "[app_control_send_launch_request] failed. " << "ret=" << appctrl_error_to_string(ret);
+
+    std::cout << "Waiting for BLE to be enabled..." << std::endl;
+    std::cout << "... Enable BLE manually." << std::endl;
+
+    // wait until bt_adapter_enable() is completed.
+    result_holder.wait_async_result();
+    ret = result_holder.get_changed_state(&adapter_state);
+    EXPECT_EQ(ret, BT_ERROR_NONE);
+    EXPECT_EQ(adapter_state, BT_ADAPTER_ENABLED);
+    std::cout << "After enabling. state=" << ble_state_to_string(adapter_state) << std::endl;
+
+    // distory app control
+    app_control_destroy(service);
+}
+
+TEST_F(BleTest, testReadBleAdvert)
+{
+    int ret = BT_ERROR_NONE;
+    CallbackResultHolder result_holder;
+    bt_adapter_le_service_data_s *service_data;
+
+    // Starting Scanning
+    std::cout << "Starting BLE Scan..." << std::endl;
+    ret = bt_adapter_le_start_scan(__scan_result_cb, &result_holder);
+    if(ret == BT_ERROR_OPERATION_FAILED) {
+        // Wait a second for time passing after eanbling BLE state.
+        sleep(1);
+        ret = bt_adapter_le_start_scan(__scan_result_cb, &result_holder);
+    }
+    EXPECT_TRUE(ret == BT_ERROR_NONE || ret == BT_ERROR_NOW_IN_PROGRESS)
+            << "[bt_adapter_le_start_scan] failed. ret=" << ble_error_to_string(ret) << std::endl;
+
+    // Wait BLE Advert
+    std::cout << "Waiting BLE Advert..." << std::endl;
+    std::cout << "... Start a Passkey transaction with a roaming authenticator." << std::endl;
+    result_holder.wait_async_result();
+    ret = result_holder.get_scan_result(&service_data);
+    EXPECT_EQ(ret, BT_ERROR_NONE);
+
+    ret = bt_adapter_le_stop_scan();
+    EXPECT_EQ(ret, BT_ERROR_NONE)
+            << "[bt_adapter_le_stop_scan] failed. ret=" << ble_error_to_string(ret) << std::endl;
+}
index 06a3173..343f8ad 100644 (file)
  * @brief       unit tests for webauthn
  */
 
-int main(void)
+
+#include <gtest/gtest.h>
+
+int main(int argc, char *argv[])
 {
-       return 0;
+       try {
+               ::testing::InitGoogleTest(&argc, argv);
+               return RUN_ALL_TESTS();
+       } catch (...) {
+               return 1;
+       }
 }