From 31fb11fd43a36a7ef3cdf36b0493cd34a1c31464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Sun, 25 Aug 2019 16:38:13 +0200 Subject: [PATCH] Add LoginPINDialog --- gui/CMakeLists.txt | 4 ++- gui/include/loginpindialog.h | 44 ++++++++++++++++++++++++++++ gui/include/streamsession.h | 3 ++ gui/include/streamwindow.h | 1 + gui/src/loginpindialog.cpp | 57 ++++++++++++++++++++++++++++++++++++ gui/src/streamsession.cpp | 9 ++++++ gui/src/streamwindow.cpp | 40 ++++++++++++++++++++----- lib/include/chiaki/ctrl.h | 4 +-- lib/include/chiaki/rpcrypt.h | 4 +-- lib/include/chiaki/session.h | 2 +- lib/src/ctrl.c | 41 +++++++++++++++++++++----- lib/src/rpcrypt.c | 6 ++-- lib/src/session.c | 19 ++++++++++-- 13 files changed, 208 insertions(+), 26 deletions(-) create mode 100644 gui/include/loginpindialog.h create mode 100644 gui/src/loginpindialog.cpp diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 45bc9df..65e8d1c 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -63,7 +63,9 @@ add_executable(chiaki WIN32 src/manualhostdialog.cpp res/resources.qrc include/controllermanager.h - src/controllermanager.cpp) + src/controllermanager.cpp + include/loginpindialog.h + src/loginpindialog.cpp) target_include_directories(chiaki PRIVATE include) target_link_libraries(chiaki chiaki-lib) diff --git a/gui/include/loginpindialog.h b/gui/include/loginpindialog.h new file mode 100644 index 0000000..2784694 --- /dev/null +++ b/gui/include/loginpindialog.h @@ -0,0 +1,44 @@ +/* + * This file is part of Chiaki. + * + * Chiaki is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chiaki is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chiaki. If not, see . + */ + +#ifndef CHIAKI_LOGINPINDIALOG_H +#define CHIAKI_LOGINPINDIALOG_H + +#include + +class QLineEdit; +class QDialogButtonBox; + +class LoginPINDialog : public QDialog +{ + Q_OBJECT + + private: + QString pin; + + QLineEdit *pin_edit; + QDialogButtonBox *button_box; + + void UpdateButtons(); + + public: + explicit LoginPINDialog(QWidget *parent = nullptr); + + QString GetPIN() { return pin; } +}; + +#endif // CHIAKI_LOGINPINDIALOG_H diff --git a/gui/include/streamsession.h b/gui/include/streamsession.h index b555577..c695e0b 100644 --- a/gui/include/streamsession.h +++ b/gui/include/streamsession.h @@ -92,6 +92,8 @@ class StreamSession : public QObject void Start(); void Stop(); + void SetLoginPIN(const QString &pin); + #if CHIAKI_GUI_ENABLE_QT_GAMEPAD QGamepad *GetGamepad() { return gamepad; } #endif @@ -103,6 +105,7 @@ class StreamSession : public QObject signals: void CurrentImageUpdated(); void SessionQuit(ChiakiQuitReason reason, const QString &reason_str); + void LoginPINRequested(); private slots: void UpdateGamepads(); diff --git a/gui/include/streamwindow.h b/gui/include/streamwindow.h index ffe3524..ab698f6 100644 --- a/gui/include/streamwindow.h +++ b/gui/include/streamwindow.h @@ -47,6 +47,7 @@ class StreamWindow: public QMainWindow private slots: void SessionQuit(ChiakiQuitReason reason, const QString &reason_str); + void LoginPINRequested(); void ToggleFullscreen(); }; diff --git a/gui/src/loginpindialog.cpp b/gui/src/loginpindialog.cpp new file mode 100644 index 0000000..a9a35ea --- /dev/null +++ b/gui/src/loginpindialog.cpp @@ -0,0 +1,57 @@ +/* + * This file is part of Chiaki. + * + * Chiaki is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chiaki is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chiaki. If not, see . + */ + +#include + +#include +#include +#include +#include +#include +#include + +#define PIN_LENGTH 4 + +static const QRegularExpression pin_re(QString("[0-9]").repeated(PIN_LENGTH)); + +LoginPINDialog::LoginPINDialog(QWidget *parent) : QDialog(parent) +{ + auto layout = new QVBoxLayout(this); + setLayout(layout); + + pin_edit = new QLineEdit(this); + pin_edit->setPlaceholderText(tr("Login PIN")); + pin_edit->setValidator(new QRegularExpressionValidator(pin_re, pin_edit)); + layout->addWidget(pin_edit); + connect(pin_edit, &QLineEdit::textChanged, this, [this](const QString &text) { + this->pin = text; + UpdateButtons(); + }); + + button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + layout->addWidget(button_box); + + connect(button_box, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); + + UpdateButtons(); +} + +void LoginPINDialog::UpdateButtons() +{ + button_box->button(QDialogButtonBox::Ok)->setEnabled(pin_edit->text().length() == PIN_LENGTH); +} \ No newline at end of file diff --git a/gui/src/streamsession.cpp b/gui/src/streamsession.cpp index 244f4f1..7dbd244 100644 --- a/gui/src/streamsession.cpp +++ b/gui/src/streamsession.cpp @@ -124,6 +124,12 @@ void StreamSession::Stop() chiaki_session_stop(&session); } +void StreamSession::SetLoginPIN(const QString &pin) +{ + QByteArray data = pin.toUtf8(); + chiaki_session_set_login_pin(&session, (const uint8_t *)data.constData(), data.size()); +} + void StreamSession::HandleKeyboardEvent(QKeyEvent *event) { uint64_t button_mask; @@ -325,6 +331,9 @@ void StreamSession::Event(ChiakiEvent *event) case CHIAKI_EVENT_QUIT: emit SessionQuit(event->quit.reason, event->quit.reason_str ? QString::fromUtf8(event->quit.reason_str) : QString()); break; + case CHIAKI_EVENT_LOGIN_PIN_REQUEST: + emit LoginPINRequested(); + break; } } diff --git a/gui/src/streamwindow.cpp b/gui/src/streamwindow.cpp index b5959ea..4bb4ccb 100644 --- a/gui/src/streamwindow.cpp +++ b/gui/src/streamwindow.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -29,14 +30,16 @@ StreamWindow::StreamWindow(const StreamSessionConnectInfo &connect_info, QWidget { setAttribute(Qt::WA_DeleteOnClose); setWindowTitle(qApp->applicationName()); + + session = nullptr; + av_widget = nullptr; + try { Init(connect_info); } catch(const Exception &e) { - session = nullptr; - av_widget = nullptr; QMessageBox::critical(this, tr("Stream failed"), tr("Failed to initialize Stream Session: %1").arg(e.what())); close(); } @@ -53,6 +56,7 @@ void StreamWindow::Init(const StreamSessionConnectInfo &connect_info) session = new StreamSession(connect_info, this); connect(session, &StreamSession::SessionQuit, this, &StreamWindow::SessionQuit); + connect(session, &StreamSession::LoginPINRequested, this, &StreamWindow::LoginPINRequested); av_widget = new AVOpenGLWidget(session->GetVideoDecoder(), this); setCentralWidget(av_widget); @@ -90,15 +94,35 @@ void StreamWindow::closeEvent(QCloseEvent *) void StreamWindow::SessionQuit(ChiakiQuitReason reason, const QString &reason_str) { - if(reason == CHIAKI_QUIT_REASON_STOPPED) - return; - QString m = tr("Chiaki Session has quit") + ":\n" + chiaki_quit_reason_string(reason); - if(!reason_str.isEmpty()) - m += "\n" + tr("Reason") + ": \"" + reason_str + "\""; - QMessageBox::critical(this, tr("Session has quit"), m); + if(reason != CHIAKI_QUIT_REASON_STOPPED) + { + QString m = tr("Chiaki Session has quit") + ":\n" + chiaki_quit_reason_string(reason); + if(!reason_str.isEmpty()) + m += "\n" + tr("Reason") + ": \"" + reason_str + "\""; + QMessageBox::critical(this, tr("Session has quit"), m); + } close(); } +void StreamWindow::LoginPINRequested() +{ + auto dialog = new LoginPINDialog(this); + dialog->setAttribute(Qt::WA_DeleteOnClose); + connect(dialog, &QDialog::finished, this, [this, dialog](int result) { + grabKeyboard(); + + if(!session) + return; + + if(result == QDialog::Accepted) + session->SetLoginPIN(dialog->GetPIN()); + else + session->Stop(); + }); + releaseKeyboard(); + dialog->show(); +} + void StreamWindow::ToggleFullscreen() { if(isFullScreen()) diff --git a/lib/include/chiaki/ctrl.h b/lib/include/chiaki/ctrl.h index 4e0258b..e2ab838 100644 --- a/lib/include/chiaki/ctrl.h +++ b/lib/include/chiaki/ctrl.h @@ -40,7 +40,7 @@ typedef struct chiaki_ctrl_t bool should_stop; bool login_pin_entered; - uint8_t *login_pin; // not owned + uint8_t *login_pin; size_t login_pin_size; ChiakiStopPipe notif_pipe; ChiakiMutex notif_mutex; @@ -55,7 +55,7 @@ typedef struct chiaki_ctrl_t CHIAKI_EXPORT ChiakiErrorCode chiaki_ctrl_start(ChiakiCtrl *ctrl, struct chiaki_session_t *session); CHIAKI_EXPORT void chiaki_ctrl_stop(ChiakiCtrl *ctrl); CHIAKI_EXPORT ChiakiErrorCode chiaki_ctrl_join(ChiakiCtrl *ctrl); -CHIAKI_EXPORT void chiaki_ctrl_set_login_pin(ChiakiCtrl *ctrl, uint8_t *pin, size_t pin_size); +CHIAKI_EXPORT void chiaki_ctrl_set_login_pin(ChiakiCtrl *ctrl, const uint8_t *pin, size_t pin_size); #ifdef __cplusplus } diff --git a/lib/include/chiaki/rpcrypt.h b/lib/include/chiaki/rpcrypt.h index d5b9dcc..12d0705 100644 --- a/lib/include/chiaki/rpcrypt.h +++ b/lib/include/chiaki/rpcrypt.h @@ -41,8 +41,8 @@ CHIAKI_EXPORT void chiaki_rpcrypt_aeropause(uint8_t *aeropause, const uint8_t *a CHIAKI_EXPORT void chiaki_rpcrypt_init_auth(ChiakiRPCrypt *rpcrypt, const uint8_t *nonce, const uint8_t *morning); CHIAKI_EXPORT void chiaki_rpcrypt_init_regist(ChiakiRPCrypt *rpcrypt, const uint8_t *ambassador, uint32_t pin); CHIAKI_EXPORT ChiakiErrorCode chiaki_rpcrypt_generate_iv(ChiakiRPCrypt *rpcrypt, uint8_t *iv, uint64_t counter); -CHIAKI_EXPORT ChiakiErrorCode chiaki_rpcrypt_encrypt(ChiakiRPCrypt *rpcrypt, uint64_t counter, uint8_t *in, uint8_t *out, size_t sz); -CHIAKI_EXPORT ChiakiErrorCode chiaki_rpcrypt_decrypt(ChiakiRPCrypt *rpcrypt, uint64_t counter, uint8_t *in, uint8_t *out, size_t sz); +CHIAKI_EXPORT ChiakiErrorCode chiaki_rpcrypt_encrypt(ChiakiRPCrypt *rpcrypt, uint64_t counter, const uint8_t *in, uint8_t *out, size_t sz); +CHIAKI_EXPORT ChiakiErrorCode chiaki_rpcrypt_decrypt(ChiakiRPCrypt *rpcrypt, uint64_t counter, const uint8_t *in, uint8_t *out, size_t sz); #ifdef __cplusplus } diff --git a/lib/include/chiaki/session.h b/lib/include/chiaki/session.h index 8942cc1..f4811da 100644 --- a/lib/include/chiaki/session.h +++ b/lib/include/chiaki/session.h @@ -198,7 +198,7 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_session_start(ChiakiSession *session); CHIAKI_EXPORT ChiakiErrorCode chiaki_session_stop(ChiakiSession *session); CHIAKI_EXPORT ChiakiErrorCode chiaki_session_join(ChiakiSession *session); CHIAKI_EXPORT ChiakiErrorCode chiaki_session_set_controller_state(ChiakiSession *session, ChiakiControllerState *state); -CHIAKI_EXPORT ChiakiErrorCode chiaki_session_set_login_pin(ChiakiSession *session, uint8_t *pin, size_t pin_size); +CHIAKI_EXPORT ChiakiErrorCode chiaki_session_set_login_pin(ChiakiSession *session, const uint8_t *pin, size_t pin_size); static inline void chiaki_session_set_event_cb(ChiakiSession *session, ChiakiEventCallback cb, void *user) { diff --git a/lib/src/ctrl.c b/lib/src/ctrl.c index 750838c..fc6651e 100644 --- a/lib/src/ctrl.c +++ b/lib/src/ctrl.c @@ -104,15 +104,22 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_ctrl_join(ChiakiCtrl *ctrl) ChiakiErrorCode err = chiaki_thread_join(&ctrl->thread, NULL); chiaki_stop_pipe_fini(&ctrl->notif_pipe); chiaki_mutex_fini(&ctrl->notif_mutex); + free(ctrl->login_pin); return err; } -CHIAKI_EXPORT void chiaki_ctrl_set_login_pin(ChiakiCtrl *ctrl, uint8_t *pin, size_t pin_size) +CHIAKI_EXPORT void chiaki_ctrl_set_login_pin(ChiakiCtrl *ctrl, const uint8_t *pin, size_t pin_size) { + uint8_t *buf = malloc(pin_size); + if(!buf) + return; + memcpy(buf, pin, pin_size); ChiakiErrorCode err = chiaki_mutex_lock(&ctrl->notif_mutex); assert(err == CHIAKI_ERR_SUCCESS); + if(ctrl->login_pin_entered) + free(ctrl->login_pin); ctrl->login_pin_entered = true; - ctrl->login_pin = pin; + ctrl->login_pin = buf; ctrl->login_pin_size = pin_size; chiaki_stop_pipe_stop(&ctrl->notif_pipe); chiaki_mutex_unlock(&ctrl->notif_mutex); @@ -194,8 +201,12 @@ static void *ctrl_thread_func(void *user) if(ctrl->login_pin_entered) { + CHIAKI_LOGI(ctrl->session->log, "Ctrl received entered Login PIN, sending to console"); ctrl_message_send(ctrl, CTRL_MESSAGE_TYPE_LOGIN_PIN_REP, ctrl->login_pin, ctrl->login_pin_size); ctrl->login_pin_entered = false; + free(ctrl->login_pin); + ctrl->login_pin = NULL; + ctrl->login_pin_size = 0; chiaki_stop_pipe_reset(&ctrl->notif_pipe); } else @@ -232,6 +243,21 @@ static ChiakiErrorCode ctrl_message_send(ChiakiCtrl *ctrl, CtrlMessageType type, { assert(payload_size == 0 || payload); + uint8_t *enc = NULL; + if(payload && payload_size) + { + enc = malloc(payload_size); + if(!enc) + return CHIAKI_ERR_MEMORY; + ChiakiErrorCode err = chiaki_rpcrypt_encrypt(&ctrl->session->rpcrypt, ctrl->crypt_counter_local++, payload, enc, payload_size); + if(err != CHIAKI_ERR_SUCCESS) + { + CHIAKI_LOGE(ctrl->session->log, "Ctrl failed to encrypt payload"); + free(enc); + return err; + } + } + uint8_t header[8]; *((uint32_t *)header) = htonl((uint32_t)payload_size); *((uint16_t *)(header + 4)) = htons(type); @@ -244,9 +270,10 @@ static ChiakiErrorCode ctrl_message_send(ChiakiCtrl *ctrl, CtrlMessageType type, return CHIAKI_ERR_NETWORK; } - if(payload) + if(enc) { - sent = send(ctrl->sock, payload, payload_size, 0); + sent = send(ctrl->sock, enc, payload_size, 0); + free(enc); if(sent < 0) { CHIAKI_LOGE(ctrl->session->log, "Failed to send Ctrl Message Payload"); @@ -437,7 +464,7 @@ static ChiakiErrorCode ctrl_connect(ChiakiCtrl *ctrl) uint8_t auth_enc[CHIAKI_RPCRYPT_KEY_SIZE]; - ChiakiErrorCode err = chiaki_rpcrypt_encrypt(&session->rpcrypt, ctrl->crypt_counter_local++, (uint8_t *)session->connect_info.regist_key, auth_enc, CHIAKI_RPCRYPT_KEY_SIZE); + ChiakiErrorCode err = chiaki_rpcrypt_encrypt(&session->rpcrypt, ctrl->crypt_counter_local++, (const uint8_t *)session->connect_info.regist_key, auth_enc, CHIAKI_RPCRYPT_KEY_SIZE); if(err != CHIAKI_ERR_SUCCESS) goto error; char auth_b64[CHIAKI_RPCRYPT_KEY_SIZE*2]; @@ -446,7 +473,7 @@ static ChiakiErrorCode ctrl_connect(ChiakiCtrl *ctrl) goto error; uint8_t did_enc[CHIAKI_RP_DID_SIZE]; - err = chiaki_rpcrypt_encrypt(&session->rpcrypt, ctrl->crypt_counter_local++, (uint8_t *)session->connect_info.did, did_enc, CHIAKI_RP_DID_SIZE); + err = chiaki_rpcrypt_encrypt(&session->rpcrypt, ctrl->crypt_counter_local++, session->connect_info.did, did_enc, CHIAKI_RP_DID_SIZE); if(err != CHIAKI_ERR_SUCCESS) goto error; char did_b64[CHIAKI_RP_DID_SIZE*2]; @@ -458,7 +485,7 @@ static ChiakiErrorCode ctrl_connect(ChiakiCtrl *ctrl) size_t ostype_len = strlen(SESSION_OSTYPE) + 1; if(ostype_len > sizeof(ostype_enc)) goto error; - err = chiaki_rpcrypt_encrypt(&session->rpcrypt, ctrl->crypt_counter_local++, (uint8_t *)SESSION_OSTYPE, ostype_enc, ostype_len); + err = chiaki_rpcrypt_encrypt(&session->rpcrypt, ctrl->crypt_counter_local++, (const uint8_t *)SESSION_OSTYPE, ostype_enc, ostype_len); if(err != CHIAKI_ERR_SUCCESS) goto error; char ostype_b64[256]; diff --git a/lib/src/rpcrypt.c b/lib/src/rpcrypt.c index 4181b09..708947f 100644 --- a/lib/src/rpcrypt.c +++ b/lib/src/rpcrypt.c @@ -104,7 +104,7 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_rpcrypt_generate_iv(ChiakiRPCrypt *rpcrypt, return CHIAKI_ERR_SUCCESS; } -static ChiakiErrorCode chiaki_rpcrypt_crypt(ChiakiRPCrypt *rpcrypt, uint64_t counter, uint8_t *in, uint8_t *out, size_t sz, bool encrypt) +static ChiakiErrorCode chiaki_rpcrypt_crypt(ChiakiRPCrypt *rpcrypt, uint64_t counter, const uint8_t *in, uint8_t *out, size_t sz, bool encrypt) { EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); if(!ctx) @@ -151,12 +151,12 @@ static ChiakiErrorCode chiaki_rpcrypt_crypt(ChiakiRPCrypt *rpcrypt, uint64_t cou return CHIAKI_ERR_SUCCESS; } -CHIAKI_EXPORT ChiakiErrorCode chiaki_rpcrypt_encrypt(ChiakiRPCrypt *rpcrypt, uint64_t counter, uint8_t *in, uint8_t *out, size_t sz) +CHIAKI_EXPORT ChiakiErrorCode chiaki_rpcrypt_encrypt(ChiakiRPCrypt *rpcrypt, uint64_t counter, const uint8_t *in, uint8_t *out, size_t sz) { return chiaki_rpcrypt_crypt(rpcrypt, counter, in, out, sz, true); } -CHIAKI_EXPORT ChiakiErrorCode chiaki_rpcrypt_decrypt(ChiakiRPCrypt *rpcrypt, uint64_t counter, uint8_t *in, uint8_t *out, size_t sz) +CHIAKI_EXPORT ChiakiErrorCode chiaki_rpcrypt_decrypt(ChiakiRPCrypt *rpcrypt, uint64_t counter, const uint8_t *in, uint8_t *out, size_t sz) { return chiaki_rpcrypt_crypt(rpcrypt, counter, in, out, sz, false); } diff --git a/lib/src/session.c b/lib/src/session.c index 112221e..23528c4 100644 --- a/lib/src/session.c +++ b/lib/src/session.c @@ -258,12 +258,12 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_session_set_controller_state(ChiakiSession return CHIAKI_ERR_SUCCESS; } -CHIAKI_EXPORT ChiakiErrorCode chiaki_session_set_login_pin(ChiakiSession *session, uint8_t *pin, size_t pin_size) +CHIAKI_EXPORT ChiakiErrorCode chiaki_session_set_login_pin(ChiakiSession *session, const uint8_t *pin, size_t pin_size) { uint8_t *buf = malloc(pin_size); - memcpy(buf, pin, pin_size); if(!buf) return CHIAKI_ERR_MEMORY; + memcpy(buf, pin, pin_size); ChiakiErrorCode err = chiaki_mutex_lock(&session->state_mutex); assert(err == CHIAKI_ERR_SUCCESS); if(session->login_pin_entered) @@ -272,6 +272,8 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_session_set_login_pin(ChiakiSession *sessio session->login_pin = buf; session->login_pin_size = pin_size; chiaki_mutex_unlock(&session->state_mutex); + chiaki_cond_signal(&session->state_cond); + return CHIAKI_ERR_SUCCESS; } static void session_send_event(ChiakiSession *session, ChiakiEvent *event) @@ -308,6 +310,14 @@ static bool session_check_state_pred_pin(void *user) || session->login_pin_entered; } +static bool session_check_state_pred_session_id(void *user) +{ + ChiakiSession *session = user; + return session->should_stop + || session->ctrl_failed + || session->ctrl_session_id_received; +} + #define ENABLE_SENKUSHA static void *session_thread_func(void *arg) @@ -369,11 +379,16 @@ static void *session_thread_func(void *arg) goto ctrl_failed; assert(session->login_pin_entered && session->login_pin); + CHIAKI_LOGI(session->log, "Session received entered Login PIN, forwarding to Ctrl"); chiaki_ctrl_set_login_pin(&session->ctrl, session->login_pin, session->login_pin_size); session->login_pin_entered = false; free(session->login_pin); session->login_pin = NULL; session->login_pin_size = 0; + + // wait for session id again + chiaki_cond_timedwait_pred(&session->state_cond, &session->state_mutex, SESSION_EXPECT_TIMEOUT_MS, session_check_state_pred_session_id, session); + CHECK_STOP(quit_ctrl); } if(!session->ctrl_session_id_received)