From 105a208968bac60a2aafc85f64615ffc18458036 Mon Sep 17 00:00:00 2001 From: Osmium Sorcerer Date: Wed, 23 Sep 2026 23:17:50 +0000 Subject: Rewrite authentication flow to handle new keys Previously, running /auth command would immediately send off request with the provided username, and all keyring actions will only happen once the client receives the challenge. This isn't possible now that we have both software X25519 and hardware P-256 keys because we must signal which key type we're using in advance. Rather than deferring everything until the challenge, perform the key setup immediately. Parse it, load it into the auth state, and send the full request. When the challenge is received, all that's left is to unlock the already resolved key. In case user cancels the prompt, open key selection dialog again. Make auth state machine stricter, so it's more consistent and easier to reason about, and only expose small interface. In particular, it provides key switching without restarting the auth flow. The state itself is only initialized once at program start, fully static, and avoids superflous allocations and deletions which could result in invalid states. Unify auth-related structures under it that were previously separate, such as saved hostname-username pairs. Send signals about request and response being ready instead of directly using the network functions, which makes it especially better fit in context of two asynchronous dialogs. To properly encode hardware or software key type in the intitial request without creating a separate packet, an extension to subprotocol 2 was introduced in a format-violating manner: an extension bit set means a hardware key is used, and thus we'll expect an ephemeral P-256 key as the challenge. The entire credential message format will be completely rewritten in version 3, this is a prototype implementation. --- src/auth_flow.h | 58 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 18 deletions(-) (limited to 'src/auth_flow.h') diff --git a/src/auth_flow.h b/src/auth_flow.h index 942e1aa..4a89673 100644 --- a/src/auth_flow.h +++ b/src/auth_flow.h @@ -1,51 +1,57 @@ #pragma once #include +#include -#include "aoapplication.h" #include "ext_packet.h" #include "keyring.h" +#include "saved_auth.h" class KeySelectDialog : public QDialog { Q_OBJECT public: - explicit KeySelectDialog(KeyringModel *model, QStringView username, QWidget *parent = nullptr); + explicit KeySelectDialog(KeyringModel *model, QStringView username, QStringView hostname, QWidget *parent = nullptr); private: KeyringModel *m_model; signals: - void key_selected(QByteArrayView key_id, QStringView key_name); + void key_selected(KeyringKey key); }; -class KeyPassphraseDialog : public QDialog +class KeyActivationDialog : public QDialog { Q_OBJECT public: - explicit KeyPassphraseDialog(QStringView key_name, QWidget *parent = nullptr); + enum class Mode { + none, + passphrase, + pin, + }; + explicit KeyActivationDialog(Mode mode, QStringView key_name, QWidget *parent = nullptr); void display_error(ResponseResult error) const; private: QLabel *m_err_lbl; - QLineEdit *m_pw_line; + QLineEdit *m_line; signals: - void passphrase_submitted(QByteArrayView passphrase); + void input_submitted(QByteArrayView input); }; -// The only reason this inherits QObject is so I can call deleteLater() -// when the authentication finishes. Auth flow involves coordinating two -// asynchronous dialogs emitting signals, so I'd rather let Qt's event loop -// clean everything up to be safe. +// The only reason this inherits QObject is signals. class AuthFlow : public QObject { Q_OBJECT public: - explicit AuthFlow(AOApplication *ao_app, const AuthChallenge &challenge, QWidget *parent = nullptr); + void initialize(KeyringModel *model); + void start(QString user, QString host); + bool awaiting_challenge(void) const; + void respond(const AuthChallenge &challenge); private: enum class FlowMode @@ -54,13 +60,29 @@ private: Saved }; + enum class AuthPhase { + none, + requested, + }; + + QString m_username; + QString m_hostname; FlowMode m_mode; - AOApplication *m_ao_app; AuthChallenge m_challenge; - KeySelectDialog *m_key_dlg; - KeyPassphraseDialog *m_pwd_dlg; + KeySelectDialog *m_key_dlg = nullptr; + KeyActivationDialog *m_input_dlg = nullptr; + AuthPhase m_phase = AuthPhase::none; + SavedAuth m_saved_auth; + KeyringKey m_key; + KeyringModel *m_model; - void on_key_selected(QByteArrayView key_id, QStringView key_name); -}; + void on_key_selected(KeyringKey key); + void call_input_dialog(KeyActivationDialog::Mode mode); + void authenticate(QByteArrayView input); + void reject_and_select_new_key(void); + void reset(void); -void start_auth_flow(AOApplication *ao_app, QString username); +signals: + void request_ready(AuthRequest req); + void response_ready(AuthResponse response); +}; -- cgit