~aleteoryx/muditaos

66db7b0841f654a9c404ce3bd08348a50cbac82d — SP2FET 4 years ago 7bcc1c9
[EGD-5512] Usb security refactor and devices pairing

Move all security related functionality to single security endpoint
Paired devices can be added to avoid further passcode requests
M image/user/db/settings_v2_002.sql => image/user/db/settings_v2_002.sql +1 -0
@@ 21,6 21,7 @@ INSERT OR IGNORE INTO settings_tab (path, value) VALUES
    ('gs_eula_accepted', '0'),
    ('gs_onboarding_done', '0'),
    ('gs_usb_security', '1'),
    ('gs_usb_devices', ''),
    ('gs_os_update_version', '0.00.0'),
    ('gs_os_current_version', '0.00.0'),
    ('bt_state', '0'),

M module-apps/application-desktop/widgets/PinLockHandler.cpp => module-apps/application-desktop/widgets/PinLockHandler.cpp +0 -2
@@ 93,8 93,6 @@ namespace gui
            if (app->getCurrentWindow()->getName() == app::window::name::desktop_pin_lock) {
                app->switchWindow(app::window::name::desktop_main_window);
            }
            app->bus.sendUnicast(std::make_shared<sdesktop::passcode::ScreenPasscodeUnlocked>(),
                                 service::name::service_desktop);
            return;
        }


M module-bsp/board/rt1051/bsp/usb => module-bsp/board/rt1051/bsp/usb +1 -1
@@ 1,1 1,1 @@
Subproject commit ed8055cf9a7160772ab502b938689088e83f59b1
Subproject commit 9f9b3f988333f30631caa3b442d57d2b44f7e074

M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +1 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "endpoints/developerMode/event/ATRequest.hpp"

M module-services/service-db/agents/settings/Settings.cpp => module-services/service-db/agents/settings/Settings.cpp +4 -4
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <service-db/Settings.hpp>


@@ 132,7 132,7 @@ namespace settings
        }
        cbValues[path].first = std::move(cb);

        auto msg      = std::make_shared<settings::Messages::RegisterOnVariableChange>(path);
        auto msg = std::make_shared<settings::Messages::RegisterOnVariableChange>(path);
        sendMsg(std::move(msg));
    }



@@ 151,7 151,7 @@ namespace settings
        }
        cbValues[path].second = std::move(cb);

        auto msg      = std::make_shared<settings::Messages::RegisterOnVariableChange>(path);
        auto msg = std::make_shared<settings::Messages::RegisterOnVariableChange>(path);
        sendMsg(std::move(msg));
    }



@@ 171,7 171,7 @@ namespace settings
            cbValues.erase(it_cb);
        }

        auto msg      = std::make_shared<settings::Messages::UnregisterOnVariableChange>(path);
        auto msg = std::make_shared<settings::Messages::UnregisterOnVariableChange>(path);
        sendMsg(std::move(msg));
    }


M module-services/service-db/agents/settings/SystemSettings.hpp => module-services/service-db/agents/settings/SystemSettings.hpp +1 -0
@@ 10,6 10,7 @@ namespace settings
        constexpr inline auto activeSim                = "gs_active_sim";
        constexpr inline auto lockPassHash             = "gs_lock_pass_hash";
        constexpr inline auto usbSecurity              = "gs_usb_security";
        constexpr inline auto usbDevices               = "gs_usb_devices";
        constexpr inline auto lockScreenPasscodeIsOn   = "gs_lock_screen_passcode_is_on";
        constexpr inline auto lockTime                 = "gs_lock_time";
        constexpr inline auto displayLanguage          = "gs_display_language";

M module-services/service-desktop/CMakeLists.txt => module-services/service-desktop/CMakeLists.txt +2 -0
@@ 27,6 27,7 @@ set(SOURCES
    endpoints/filesystem/FilesystemEndpoint.cpp
    endpoints/calendarEvents/CalendarEventsHelper.cpp
    endpoints/calendarEvents/CalendarEventsEndpoint.cpp
    endpoints/security/SecurityEndpoint.cpp

    parser/HttpEnums.cpp
    parser/ParserFSM.cpp


@@ 37,6 38,7 @@ set(SOURCES
    DesktopMessages.cpp
    ServiceDesktop.cpp
    WorkerDesktop.cpp
    USBSecurityModel.cpp
)

add_library(${PROJECT_NAME} STATIC ${SOURCES})

M module-services/service-desktop/DesktopMessages.cpp => module-services/service-desktop/DesktopMessages.cpp +1 -0
@@ 52,4 52,5 @@ namespace sdesktop
            context.setResponseBody(json11::Json::object{{json::bluetooth::scan, json::bluetooth::btOff}});
        }
    } // namespace bluetooth

} // namespace sdesktop

M module-services/service-desktop/ServiceDesktop.cpp => module-services/service-desktop/ServiceDesktop.cpp +53 -20
@@ 60,8 60,9 @@ ServiceDesktop::ServiceDesktop() : sys::Service(service::name::service_desktop, 
{
    LOG_INFO("[ServiceDesktop] Initializing");

    updateOS = std::make_unique<UpdateMuditaOS>(this);
    settings = std::make_unique<settings::Settings>(this);
    updateOS         = std::make_unique<UpdateMuditaOS>(this);
    settings         = std::make_unique<settings::Settings>(this);
    usbSecurityModel = std::make_unique<sdesktop::USBSecurityModel>(this, settings.get());
}

ServiceDesktop::~ServiceDesktop()


@@ 71,7 72,7 @@ ServiceDesktop::~ServiceDesktop()

sys::ReturnCodes ServiceDesktop::InitHandler()
{
    desktopWorker = std::make_unique<WorkerDesktop>(this);
    desktopWorker = std::make_unique<WorkerDesktop>(this, *usbSecurityModel.get());
    const bool ret =
        desktopWorker->init({{sdesktop::RECEIVE_QUEUE_BUFFER_NAME, sizeof(std::string *), sdesktop::cdc_queue_len},
                             {sdesktop::SEND_QUEUE_BUFFER_NAME, sizeof(std::string *), sdesktop::cdc_queue_object_size},


@@ 94,7 95,7 @@ sys::ReturnCodes ServiceDesktop::InitHandler()
            request->event->send();
        }

        return std::make_shared<sys::ResponseMessage>();
        return sys::MessageNone{};
    });

    connect(sdesktop::BackupMessage(), [&](sys::Message *msg) {


@@ 105,7 106,7 @@ sys::ReturnCodes ServiceDesktop::InitHandler()
            backupStatus.location =
                (purefs::dir::getBackupOSPath() / backupStatus.task).replace_extension(purefs::extension::tar);
        }
        return std::make_shared<sys::ResponseMessage>();
        return sys::MessageNone{};
    });

    connect(sdesktop::RestoreMessage(), [&](sys::Message *msg) {


@@ 114,7 115,7 @@ sys::ReturnCodes ServiceDesktop::InitHandler()
            RemountFS();
            BackupRestore::RestoreUserFiles(this);
        }
        return std::make_shared<sys::ResponseMessage>();
        return sys::MessageNone{};
    });

    connect(sdesktop::FactoryMessage(), [&](sys::Message *msg) {


@@ 127,7 128,7 @@ sys::ReturnCodes ServiceDesktop::InitHandler()
            // this might theoretically cause filesystem corruption
            FactoryReset::Run(this);
        }
        return std::make_shared<sys::ResponseMessage>();
        return sys::MessageNone{};
    });

    connect(sdesktop::UpdateOsMessage(), [&](sys::Message *msg) {


@@ 159,7 160,7 @@ sys::ReturnCodes ServiceDesktop::InitHandler()
            }
        }

        return std::make_shared<sys::ResponseMessage>();
        return sys::MessageNone{};
    });

    connect(sdesktop::transfer::TransferTimerState(), [&](sys::Message *msg) {


@@ 184,34 185,52 @@ sys::ReturnCodes ServiceDesktop::InitHandler()
                break;
            }
        }
        return std::make_shared<sys::ResponseMessage>();
        return sys::MessageNone{};
    });

    connect(sdesktop::usb::USBConfigured(), [&](sys::Message *msg) {
        if (!desktopWorker->isEndpointSecurityEnabled()) {
        if (!usbSecurityModel->isSecurityEnabled()) {
            LOG_INFO("Endpoint security disabled.");
            return std::make_shared<sys::ResponseMessage>();
            return sys::MessageNone{};
        }

        LOG_INFO("USB connected with endpoint security enabled. Requesting passcode.");
        desktopWorker->setEndpointSecurity(EndpointSecurity::Block);
        usbSecurityModel->setEndpointSecurity(EndpointSecurity::Block);
        bus.sendUnicast(std::make_shared<sdesktop::passcode::ScreenPasscodeRequest>(),
                        app::manager::ApplicationManager::ServiceName);

        return std::make_shared<sys::ResponseMessage>();
        return sys::MessageNone{};
    });

    connect(sdesktop::usb::USBDisconnected(), [&](sys::Message *msg) {
        LOG_INFO("USB disconnected. Enabling secured endpoints.");
        bus.sendUnicast(std::make_shared<sdesktop::passcode::ScreenPasscodeRequest>(true),
                        app::manager::ApplicationManager::ServiceName);
        return std::make_shared<sys::ResponseMessage>();
        return sys::MessageNone{};
    });

    connect(sdesktop::usb::USBHandshake(), [&](sys::Message *msg) {
        auto handshakeMsg = dynamic_cast<sdesktop::usb::USBHandshake *>(msg);
        processUSBHandshake(handshakeMsg);
        return sys::MessageNone{};
    });

    connect(sdesktop::usb::USBSecurityOn(), [&](sys::Message *msg) {
        usbSecurityModel->enableEndpointSecurity(true);
        return sys::MessageNone{};
    });

    connect(sdesktop::usb::USBSecurityOff(), [&](sys::Message *msg) {
        usbSecurityModel->enableEndpointSecurity(false);
        return sys::MessageNone{};
    });

    connect(sdesktop::passcode::ScreenPasscodeUnlocked(), [&](sys::Message *msg) {
        LOG_INFO("Passcode accepted. Enabling secured endpoints.");
        desktopWorker->setEndpointSecurity(EndpointSecurity::Allow);
        return std::make_shared<sys::ResponseMessage>();
        LOG_INFO("Passcode accepted. Enabling endpoints.");
        bus.sendUnicast(std::make_shared<sdesktop::passcode::ScreenPasscodeRequest>(true),
                        app::manager::ApplicationManager::ServiceName);
        usbSecurityModel->setEndpointSecurity(EndpointSecurity::Allow);
        return sys::MessageNone{};
    });

    settings->registerValueChange(updateos::settings::history,


@@ 221,9 240,8 @@ sys::ReturnCodes ServiceDesktop::InitHandler()
        ::settings::SystemProperties::usbSecurity,
        [this](std::string value) {
            bool securityEnabled = utils::getNumericValue<bool>(value);
            LOG_INFO("Setting endpoint security: %d", securityEnabled);
            desktopWorker->enableEndpointSecurity(securityEnabled);
            desktopWorker->setEndpointSecurity(securityEnabled ? EndpointSecurity::Block : EndpointSecurity::Allow);
            usbSecurityModel->enableEndpointSecurity(securityEnabled);
            usbSecurityModel->setEndpointSecurity(securityEnabled ? EndpointSecurity::Block : EndpointSecurity::Allow);
        },
        settings::SettingsScope::Global);



@@ 280,3 298,18 @@ void ServiceDesktop::prepareBackupData()
    backupStatus.state         = false;
    backupStatus.backupTempDir = purefs::dir::getTemporaryPath() / backupStatus.task;
}

void ServiceDesktop::processUSBHandshake(sdesktop::usb::USBHandshake *msg)
{
    parserFSM::Context responseContext;
    responseContext.setEndpoint(parserFSM::EndpointType::usbSecurity);
    responseContext.setResponseStatus(parserFSM::http::Code::Forbidden);

    if (usbSecurityModel->processHandshake(msg)) {
        LOG_DEBUG("Handshake ok. Unlocking.");
        bus.sendUnicast(std::make_shared<sdesktop::passcode::ScreenPasscodeUnlocked>(), service::name::service_desktop);
        responseContext.setResponseStatus(parserFSM::http::Code::OK);
    }

    parserFSM::MessageHandler::putToSendQueue(responseContext.createSimpleResponse());
}

A module-services/service-desktop/USBSecurityModel.cpp => module-services/service-desktop/USBSecurityModel.cpp +116 -0
@@ 0,0 1,116 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "service-desktop/USBSecurityModel.hpp"

#include "Service/Service.hpp"
#include "service-desktop/DesktopMessages.hpp"
#include "service-desktop/WorkerDesktop.hpp"

#include <service-db/service-db/Settings.hpp>
#include <module-services/service-db/agents/settings/SystemSettings.hpp>

namespace sdesktop
{
    USBSecurityModel::USBSecurityModel(sys::Service *ownerSrv, settings::Settings *srvSettings)
    {
        settings = srvSettings;

        settings->registerValueChange(
            settings::SystemProperties::usbDevices,
            [this](const std::string &value) { bound = parseDevices(value); },
            ::settings::SettingsScope::Global);

        settings->registerValueChange(
            ::settings::SystemProperties::lockPassHash,
            [this](const std::string &value) {
                // test
                lockPassHash = utils::getNumericValue<unsigned int>(value);
            },
            ::settings::SettingsScope::Global);
    }

    auto USBSecurityModel::isBound(DeviceID id) const -> bool
    {
        return bound.find(id) != bound.end();
    }

    auto USBSecurityModel::addDevice(DeviceID id, Passcode passcode) -> bool
    {
        if (endpointSecurity == EndpointSecurity::Block && !checkPasscode(passcode)) {
            return false;
        }

        bound.insert(id);
        settings->setValue(settings::SystemProperties::usbDevices, dumpDevices(bound));

        return true;
    }

    bool USBSecurityModel::checkPasscode(const Passcode &passcode)
    {
        static std::hash<unsigned int> hashEngine;
        auto hash = hashEngine(passcode);
        return hash == lockPassHash;
    }

    auto USBSecurityModel::isSecurityEnabled() const -> bool
    {
#ifdef DISABLE_USB_SECURITY
        return false;
#else
        return utils::getNumericValue<bool>(
            settings->getValue(settings::SystemProperties::usbSecurity, settings::SettingsScope::Global));
#endif
    }

    void USBSecurityModel::enableEndpointSecurity(bool securityEnabled)
    {
        settings->setValue(settings::SystemProperties::usbSecurity,
                           utils::to_string(securityEnabled),
                           settings::SettingsScope::Global);
    }

    bool USBSecurityModel::processHandshake(const sdesktop::usb::USBHandshake *handshake)
    {
        auto id  = handshake->getId();
        auto pwd = handshake->getPasscode();

        LOG_DEBUG("Process handshake: id=%s; pwd=%d;", id.c_str(), pwd);

        if (isBound(id)) {
            LOG_DEBUG("Bounded device, handshake successfull");
            return true;
        }

        if (addDevice(id, pwd)) {
            LOG_DEBUG("Bounding device successfull");
            return true;
        }

        LOG_DEBUG("Handshake failed");
        return false;
    }

    void USBSecurityModel::setEndpointSecurity(EndpointSecurity security)
    {
        endpointSecurity = security;
    }

    auto USBSecurityModel::getEndpointSecurity() const -> EndpointSecurity
    {
        return endpointSecurity;
    }

    std::set<DeviceID> USBSecurityModel::parseDevices(const std::string &value) const
    {
        std::istringstream iss{value};
        return {std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>()};
    }
    std::string USBSecurityModel::dumpDevices(const std::set<DeviceID> &devices) const
    {
        std::stringstream ss;
        std::copy(devices.begin(), devices.end(), std::ostream_iterator<DeviceID>(ss, " "));
        return ss.str();
    }
} // namespace sdesktop
\ No newline at end of file

M module-services/service-desktop/WorkerDesktop.cpp => module-services/service-desktop/WorkerDesktop.cpp +4 -4
@@ 18,9 18,9 @@

inline constexpr auto uploadFailedMessage = "file upload terminated before all data transferred";

WorkerDesktop::WorkerDesktop(sys::Service *ownerServicePtr)
    : sys::Worker(ownerServicePtr, sdesktop::worker_stack), ownerService(ownerServicePtr), parser(ownerServicePtr),
      fileDes(nullptr)
WorkerDesktop::WorkerDesktop(sys::Service *ownerServicePtr, const sdesktop::USBSecurityModel &securityModel)
    : sys::Worker(ownerServicePtr, sdesktop::worker_stack), fileDes(nullptr), securityModel(securityModel),
      ownerService(ownerServicePtr), parser(ownerServicePtr)
{}

bool WorkerDesktop::init(std::list<sys::WorkerQueueInfo> queues)


@@ 64,7 64,7 @@ bool WorkerDesktop::handleMessage(uint32_t queueID)
            return false;
        }

        auto factory = std::make_unique<SecuredEndpointFactory>(endpointSecurity);
        auto factory = std::make_unique<SecuredEndpointFactory>(securityModel.getEndpointSecurity());
        auto handler = std::make_unique<parserFSM::MessageHandler>(ownerService, std::move(factory));

        parser.setMessageHandler(std::move(handler));

M module-services/service-desktop/endpoints/Context.hpp => module-services/service-desktop/endpoints/Context.hpp +4 -0
@@ 8,6 8,10 @@

namespace parserFSM
{
    constexpr http::Code toCode(bool r)
    {
        return r ? http::Code::OK : http::Code::InternalServerError;
    }

    namespace json
    {

M module-services/service-desktop/endpoints/EndpointFactory.hpp => module-services/service-desktop/endpoints/EndpointFactory.hpp +4 -1
@@ 19,6 19,7 @@
#include "restore/RestoreEndpoint.hpp"
#include "update/UpdateEndpoint.hpp"
#include <endpoints/bluetooth/BluetoothEndpoint.hpp>
#include <module-services/service-desktop/endpoints/security/SecurityEndpoint.hpp>

class EndpointFactory
{


@@ 53,6 54,8 @@ class EndpointFactory
            return std::make_unique<CalendarEventsEndpoint>(ownerServicePtr);
        case parserFSM::EndpointType::bluetooth:
            return std::make_unique<BluetoothEndpoint>(ownerServicePtr);
        case parserFSM::EndpointType::usbSecurity:
            return std::make_unique<SecurityEndpoint>(ownerServicePtr);
        default:
            return nullptr;
        }


@@ 67,7 70,7 @@ enum class EndpointSecurity

class SecuredEndpointFactory : public EndpointFactory
{
    static constexpr auto Whitelist = {parserFSM::EndpointType::developerMode};
    static constexpr auto Whitelist = {parserFSM::EndpointType::developerMode, parserFSM::EndpointType::usbSecurity};

  public:
    explicit SecuredEndpointFactory(EndpointSecurity security) : endpointSecurity(security)

M module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.cpp => module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.cpp +3 -6
@@ 33,10 33,6 @@ using namespace parserFSM;

namespace
{
    constexpr http::Code toCode(bool r)
    {
        return r ? http::Code::OK : http::Code::InternalServerError;
    }

    auto toTetheringState(const std::string &state) -> sys::phone_modes::Tethering
    {


@@ 116,13 112,14 @@ auto DeveloperModeHelper::processPut(Context &context) -> ProcessResult
                               service::name::system_manager);
        return {sent::delayed, std::nullopt};
    }
    else if (body[json::developerMode::usbSecurity].is_string()) {
    else if (body[json::developerMode::usbSecurityStatus].is_string()) {
        std::shared_ptr<sys::DataMessage> msg = std::make_shared<sdesktop::usb::USBConfigured>();
        if (body[json::developerMode::usbSecurity].string_value() == json::developerMode::usbUnlock) {
        if (body[json::developerMode::usbSecurityStatus].string_value() == json::developerMode::usbUnlocked) {
            msg = std::make_shared<sdesktop::passcode::ScreenPasscodeUnlocked>();
        }
        code = toCode(owner->bus.sendUnicast(std::move(msg), "ServiceDesktop"));
    }

    else {
        context.setResponseStatus(http::Code::BadRequest);
        MessageHandler::putToSendQueue(context.createSimpleResponse());

M module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.hpp => module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.hpp +4 -4
@@ 57,7 57,7 @@ namespace parserFSM
        inline constexpr auto changeCellularStateCmd = "changeCellularStateCmd";
        inline constexpr auto getInfo                = "getInfo";
        inline constexpr auto tethering              = "tethering";
        inline constexpr auto usbSecurity            = "usbSecurity";
        inline constexpr auto usbSecurityStatus      = "usbSecurityStatus";
        /// values for getInfo cmd
        inline constexpr auto simStateInfo      = "simState";
        inline constexpr auto cellularStateInfo = "cellularState";


@@ 69,9 69,9 @@ namespace parserFSM
        inline constexpr auto tetheringOn  = "on";
        inline constexpr auto tetheringOff = "off";

        /// values for usbSecurity
        inline constexpr auto usbLock   = "usbLock";
        inline constexpr auto usbUnlock = "usbUnlock";
        // values for usb security
        inline constexpr auto usbLocked   = "locked";
        inline constexpr auto usbUnlocked = "unlocked";

    } // namespace json::developerMode


A module-services/service-desktop/endpoints/security/SecurityEndpoint.cpp => module-services/service-desktop/endpoints/security/SecurityEndpoint.cpp +74 -0
@@ 0,0 1,74 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "SecurityEndpoint.hpp"

#include <endpoints/Context.hpp>
#include <parser/MessageHandler.hpp>
#include <service-desktop/service-desktop/ServiceDesktop.hpp>
#include <service-desktop/DesktopMessages.hpp>
#include <json/json11.hpp>

#include <string>

using namespace parserFSM;

auto SecurityEndpoint::handle(parserFSM::Context &context) -> void
{
    http::Code responseCode;
    switch (context.getMethod()) {
    case http::Method::get:
        responseCode = processStatus(context);
        context.setResponseStatus(responseCode);
        parserFSM::MessageHandler::putToSendQueue(context.createSimpleResponse());
        break;
    case http::Method::put:
        responseCode = processConfiguration(context);
        context.setResponseStatus(responseCode);
        parserFSM::MessageHandler::putToSendQueue(context.createSimpleResponse());
        break;
    case http::Method::post:
        processHandshake(context);
        break;
    default:
        responseCode = http::Code::BadRequest;
        context.setResponseStatus(responseCode);
        parserFSM::MessageHandler::putToSendQueue(context.createSimpleResponse());
        break;
    }
}

auto SecurityEndpoint::processHandshake(Context &context) -> http::Code
{
    auto body = context.getBody();
    if (!body[json::usb::id].is_string()) {
        return http::Code::BadRequest;
    }

    auto msg = std::make_shared<sdesktop::usb::USBHandshake>(body[json::usb::id].string_value(),
                                                             body[json::usb::passcode].int_value());
    return toCode(ownerServicePtr->bus.sendUnicast(msg, service::name::service_desktop));
}

auto SecurityEndpoint::processStatus(Context &context) -> http::Code
{
    auto desktopService = dynamic_cast<ServiceDesktop *>(ownerServicePtr);
    auto security       = desktopService->getSecurity()->getEndpointSecurity();
    return security == EndpointSecurity::Allow ? http::Code::OK : http::Code::Forbidden;
}

auto SecurityEndpoint::processConfiguration(Context &context) -> http::Code
{
    auto body = context.getBody();

    std::shared_ptr<sys::DataMessage> msg;

    if (body[json::usb::status].string_value() == json::usb::on) {
        msg = std::make_shared<sdesktop::usb::USBSecurityOn>();
    }
    if (body[json::usb::status].string_value() == json::usb::off) {
        msg = std::make_shared<sdesktop::usb::USBSecurityOff>();
    }

    return toCode(ownerServicePtr->bus.sendUnicast(std::move(msg), service::name::service_desktop));
}

A module-services/service-desktop/endpoints/security/SecurityEndpoint.hpp => module-services/service-desktop/endpoints/security/SecurityEndpoint.hpp +35 -0
@@ 0,0 1,35 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <Service/Service.hpp>
#include <endpoints/Endpoint.hpp>
#include <parser/ParserUtils.hpp>

#include <string>

namespace parserFSM
{
    class Context;
} // namespace parserFSM

namespace sys
{
    class Service;
} // namespace sys

class SecurityEndpoint : public parserFSM::Endpoint
{
  public:
    explicit SecurityEndpoint(sys::Service *ownerServicePtr) : Endpoint(ownerServicePtr)
    {
        debugName = "SecurityEndpoint";
    }
    auto handle(parserFSM::Context &context) -> void override;

  private:
    auto processHandshake(parserFSM::Context &context) -> parserFSM::http::Code;
    auto processConfiguration(parserFSM::Context &context) -> parserFSM::http::Code;
    auto processStatus(parserFSM::Context &context) -> parserFSM::http::Code;
};

M module-services/service-desktop/parser/ParserUtils.hpp => module-services/service-desktop/parser/ParserUtils.hpp +17 -2
@@ 27,10 27,11 @@ namespace parserFSM
        calllog,
        calendarEvents,
        developerMode,
        bluetooth
        bluetooth,
        usbSecurity
    };

    inline constexpr auto lastEndpoint = static_cast<int>(EndpointType::bluetooth);
    inline constexpr auto lastEndpoint = static_cast<int>(EndpointType::usbSecurity);
    // Message defs and utils
    namespace message
    {


@@ 169,6 170,20 @@ namespace parserFSM
            inline constexpr auto templateID         = "templateID";
        } // namespace messages

        namespace usb
        {
            inline constexpr auto passcode = "passcode";
            inline constexpr auto id       = "uniqueId";
            inline constexpr auto config   = "config";
            inline constexpr auto status   = "usbSecurityStatus";
            inline constexpr auto on       = "on";
            inline constexpr auto off      = "off";
            inline constexpr auto locked   = "locked";
            inline constexpr auto unlocked = "unlocked";
            inline constexpr auto security = "usbSecurity";

        } // namespace usb

    } // namespace json

} // namespace parserFSM

M module-services/service-desktop/service-desktop/DesktopMessages.hpp => module-services/service-desktop/service-desktop/DesktopMessages.hpp +41 -0
@@ 97,6 97,46 @@ namespace sdesktop
            ~USBDisconnected() override = default;
        };

        class USBSecurityOn : public sys::DataMessage
        {
          public:
            USBSecurityOn() : sys::DataMessage(MessageType::USBSecurityOn)
            {}
            ~USBSecurityOn() override = default;
        };

        class USBSecurityOff : public sys::DataMessage
        {
          public:
            USBSecurityOff() : sys::DataMessage(MessageType::USBSecurityOff)
            {}
            ~USBSecurityOff() override = default;
        };

        class USBHandshake : public sys::DataMessage
        {
          public:
            USBHandshake() : sys::DataMessage(MessageType::USBHandshake)
            {}
            USBHandshake(const std::string &id, unsigned int passcode)
                : sys::DataMessage(MessageType::USBHandshake), id(id), passcode(passcode)
            {}
            ~USBHandshake() override = default;

            auto getId() const -> std::string
            {
                return id;
            }
            auto getPasscode() const
            {
                return passcode;
            }

          private:
            const std::string id;
            const unsigned int passcode = 0;
        }; // namespace usb

    } // namespace usb

    namespace passcode


@@ 196,4 236,5 @@ namespace sdesktop
            ~TransferTimerState() override = default;
        };
    } // namespace transfer

} // namespace sdesktop

M module-services/service-desktop/service-desktop/ServiceDesktop.hpp => module-services/service-desktop/service-desktop/ServiceDesktop.hpp +10 -0
@@ 12,6 12,7 @@
#include "Service/Service.hpp" // for Service
#include "Constants.hpp"
#include "WorkerDesktop.hpp"
#include "USBSecurityModel.hpp"
#include <endpoints/update/UpdateMuditaOS.hpp>
#include <service-db/DBServiceName.hpp>



@@ 63,14 64,23 @@ class ServiceDesktop : public sys::Service

    std::unique_ptr<UpdateMuditaOS> updateOS;
    std::unique_ptr<WorkerDesktop> desktopWorker;

    void storeHistory(const std::string &historyValue);
    void prepareBackupData();
    const BackupStatus getBackupStatus()
    {
        return backupStatus;
    }
    const sdesktop::USBSecurityModel *getSecurity()
    {
        return usbSecurityModel.get();
    }

  private:
    void processUSBHandshake(sdesktop::usb::USBHandshake *msg);

  private:
    std::unique_ptr<sdesktop::USBSecurityModel> usbSecurityModel;
    std::unique_ptr<settings::Settings> settings;
    std::unique_ptr<sys::Timer> transferTimer;
};

A module-services/service-desktop/service-desktop/USBSecurityModel.hpp => module-services/service-desktop/service-desktop/USBSecurityModel.hpp +56 -0
@@ 0,0 1,56 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <service-desktop/endpoints/EndpointFactory.hpp>

namespace settings
{
    class Settings;
}

namespace sys
{
    class Service;
}

namespace sdesktop
{
    namespace usb
    {
        class USBHandshake;
    };

    using DeviceID = std::string;
    using Passcode = unsigned int;

    class USBSecurityModel
    {
      public:
        explicit USBSecurityModel(sys::Service *ownerSrv, settings::Settings *srvSettings);

        auto isBound(DeviceID id) const -> bool;
        auto addDevice(DeviceID id, Passcode passcode) -> bool;
        bool checkPasscode(const Passcode &passcode);

        auto isSecurityEnabled() const -> bool;
        void enableEndpointSecurity(bool securityEnabled);

        bool processHandshake(const sdesktop::usb::USBHandshake *handshake);

        void setEndpointSecurity(EndpointSecurity security);

        auto getEndpointSecurity() const -> EndpointSecurity;

      private:
        std::set<DeviceID> parseDevices(const std::string &value) const;
        std::string dumpDevices(const std::set<DeviceID> &devices) const;

      private:
        unsigned int lockPassHash = 0;
        EndpointSecurity endpointSecurity;
        std::set<DeviceID> bound;
        settings::Settings *settings;
    };
}; // namespace sdesktop

M module-services/service-desktop/service-desktop/WorkerDesktop.hpp => module-services/service-desktop/service-desktop/WorkerDesktop.hpp +5 -21
@@ 12,6 12,7 @@
#include "parser/ParserFSM.hpp"
#include "endpoints/EndpointFactory.hpp"
#include "bsp/usb/usb.hpp"
#include "USBSecurityModel.hpp"

class WorkerDesktop : public sys::Worker, public bsp::USBDeviceListener
{


@@ 26,15 27,12 @@ class WorkerDesktop : public sys::Worker, public bsp::USBDeviceListener
        CancelTransfer,
    };

    WorkerDesktop(sys::Service *ownerServicePtr);
    WorkerDesktop(sys::Service *ownerServicePtr, const sdesktop::USBSecurityModel &securityModel);

    virtual bool init(std::list<sys::WorkerQueueInfo> queues) override;
    virtual bool deinit() override;
    bool handleMessage(uint32_t queueID) override final;

    sys::Service *ownerService = nullptr;
    parserFSM::StateMachine parser;

    xQueueHandle getReceiveQueue()
    {
        return receiveQueue;


@@ 48,21 46,6 @@ class WorkerDesktop : public sys::Worker, public bsp::USBDeviceListener
    void rawDataReceived(void *dataPtr, uint32_t dataLen) override;
    bool getRawMode() const noexcept override;

    void setEndpointSecurity(EndpointSecurity security)
    {
        endpointSecurity = security;
    }

    void enableEndpointSecurity(bool enable)
    {
        endpointSecurityEnabled = enable;
    }

    bool isEndpointSecurityEnabled()
    {
        return endpointSecurityEnabled;
    }

  private:
    void uploadFileFailedResponse();



@@ 81,6 64,7 @@ class WorkerDesktop : public sys::Worker, public bsp::USBDeviceListener
    uint32_t writeFileDataWritten  = 0;
    std::filesystem::path filePath;
    std::atomic<bool> rawModeEnabled = false;
    bool endpointSecurityEnabled     = true;
    EndpointSecurity endpointSecurity;
    const sdesktop::USBSecurityModel &securityModel;
    sys::Service *ownerService = nullptr;
    parserFSM::StateMachine parser;
};

M source/MessageType.hpp => source/MessageType.hpp +3 -0
@@ 221,6 221,9 @@ enum class MessageType
    USBConnected,
    USBConfigured,
    USBDisconnected,
    USBSecurityOn,
    USBSecurityOff,
    USBHandshake,

    ScreenPasscodeRequest,
    ScreenPasscodeUnlocked,

M test/harness => test/harness +1 -1
@@ 1,1 1,1 @@
Subproject commit beb1c1351638839148f576cc63bc450e69d310a4
Subproject commit 630bd58022e56e5b58db3f762cccd3bdc040c608

M test/pytest/service-desktop/test_connection_security.py => test/pytest/service-desktop/test_connection_security.py +63 -12
@@ 6,36 6,87 @@ from harness.interface.defs import status
from harness import utils, log

@pytest.mark.service_desktop_test
@pytest.mark.usefixtures("usb_locked")
@pytest.mark.skip("not working ;/")
def test_secured_endpoint(harness):
    body = {}

    harness.lock_usb()

    ret = harness.endpoint_request("deviceInfo", "get", body)
    assert ret["status"] == status["Forbidden"]

    ret = harness.endpoint_request("messages", "get", body)
    harness.unlock_usb()

    ret = harness.endpoint_request("deviceInfo", "get", body)
    assert ret["status"] == status["OK"]

@pytest.mark.service_desktop_test
def test_secured_paired(harness):
    body = {
        "uniqueId": "11111",
        "passcode": 3333
    }

    harness.lock_usb()

    ret = harness.endpoint_request("deviceInfo", "get", {})
    assert ret["status"] == status["Forbidden"]

    ret = harness.endpoint_request("calllog", "get", body)
    ret = harness.endpoint_request("usbSecurity", "post", {
        "uniqueId": "11111",
        "passcode": 5555
    })
    assert ret["status"] == status["Forbidden"]

    ret = harness.endpoint_request("backup", "get", body)
    ret = harness.endpoint_request("usbSecurity", "post", body)
    assert ret["status"] == status["OK"]

    ret = harness.endpoint_request("calllog", "get", {})
    assert ret["status"] == status["OK"]

    harness.lock_usb()

    ret = harness.endpoint_request("backup", "get", {})
    assert ret["status"] == status["Forbidden"]

    ret = harness.endpoint_request("contacts", "get", body)
    ret = harness.endpoint_request("usbSecurity", "post", body)
    assert ret["status"] == status["OK"]

    ret = harness.endpoint_request("calllog", "get", {})
    assert ret["status"] == status["OK"]

    harness.lock_usb()

    ret = harness.endpoint_request("usbSecurity", "post", {"uniqueId": "11111"})
    assert ret["status"] == status["OK"]

    ret = harness.endpoint_request("calllog", "get", {})
    assert ret["status"] == status["OK"]


@pytest.mark.service_desktop_test
def test_usb_status(harness):
    body = {
        "uniqueId": "11111",
        "passcode": 3333
    }

    harness.lock_usb()

    ret = harness.endpoint_request("usbSecurity", "get", {})
    assert ret["status"] == status["Forbidden"]

    harness.unlock_usb()

    ret = harness.endpoint_request("deviceInfo", "get", body)
    ret = harness.endpoint_request("usbSecurity", "get", {})
    assert ret["status"] == status["OK"]

    ret = harness.endpoint_request("calllog", "get", body)
    assert ret["status"] == status["OK"]
    harness.lock_usb()

    ret = harness.endpoint_request("usbSecurity", "get", {})
    assert ret["status"] == status["Forbidden"]

    ret = harness.endpoint_request("backup", "get", body)
    assert ret["status"] == status["BadRequest"]
    ret = harness.endpoint_request("usbSecurity", "post", body)
    assert ret["status"] == status["OK"]

    ret = harness.endpoint_request("contacts", "get", body)
    ret = harness.endpoint_request("usbSecurity", "get", {})
    assert ret["status"] == status["OK"]