Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
6ad9bff
Working POC for networking
LordofGhost Jul 5, 2026
f7e1460
Rename network files
LordofGhost Jul 5, 2026
1a64d60
Add renamed files
LordofGhost Jul 5, 2026
e6fb87c
Define functionality in README
LordofGhost Jul 7, 2026
2e70bb3
Change authentication concept in README
LordofGhost Jul 7, 2026
c1a2f69
Add connection UI page
LordofGhost Jul 8, 2026
7bcded5
Remove sender and receiver from protocol
LordofGhost Jul 8, 2026
1e14e8b
Update Readme description
LordofGhost Jul 8, 2026
f638e1f
Add SQLite to store messages and implement logic
LordofGhost Jul 8, 2026
398aef9
Add migrations
LordofGhost Jul 8, 2026
0b934c3
Add senderName to protocol
LordofGhost Jul 8, 2026
c7d13e0
Add username logic to client
LordofGhost Jul 8, 2026
a855d6c
Implement userName on server
LordofGhost Jul 8, 2026
dd22444
Link RSA
LordofGhost Jul 8, 2026
59e45eb
Add UI elements and popups for RSA key handling
LordofGhost Jul 8, 2026
cfbe332
Implement client local key and connection store
LordofGhost Jul 8, 2026
a52d28e
Fix dropdown and delete menu design
LordofGhost Jul 8, 2026
9849d8e
Update Readme
LordofGhost Jul 9, 2026
818f2d5
Update org name on the server
LordofGhost Jul 10, 2026
ea8e3a7
Update RSA submodule
LordofGhost Jul 11, 2026
1aad801
Implement key loading
LordofGhost Jul 11, 2026
db4a3eb
Add loading animation for key creation
LordofGhost Jul 11, 2026
92f51ea
Set UI style
LordofGhost Jul 11, 2026
6a7fbff
Add admin user
LordofGhost Jul 11, 2026
789ed1e
Add connection establishment concept
LordofGhost Jul 11, 2026
b3900b2
Improve chat messages UI
LordofGhost Jul 12, 2026
77f5f16
Add deserialization for public key on server
LordofGhost Jul 12, 2026
e7355c9
Expand protocol for authentication
LordofGhost Jul 12, 2026
e3c8080
Change networkManager start order
LordofGhost Jul 12, 2026
384e822
Handle new message types in db connector
LordofGhost Jul 12, 2026
cf8b243
Implement authentication flow in NetworkManager
LordofGhost Jul 12, 2026
a27dc74
Implement authentication handling in Session class
LordofGhost Jul 12, 2026
031e0ff
Revise connection establishment and authentication protocol details i…
LordofGhost Jul 12, 2026
6aa8323
Translate error messages and UI text from German to English
LordofGhost Jul 12, 2026
bca5fe5
Implement user registration logic
LordofGhost Jul 12, 2026
85b0ea2
Add functionality for listing and deleting users
LordofGhost Jul 12, 2026
b9e06f3
Add clear-history command to delete all stored chat messages
LordofGhost Jul 12, 2026
6381094
Add Images in Readme
LordofGhost Jul 12, 2026
ef153f3
Update RSA submodule
LordofGhost Jul 12, 2026
eef5438
Update Readme
LordofGhost Jul 12, 2026
da1d767
Apply Clang formatting
LordofGhost Jul 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,9 @@
/build
/Testing
/cmake-build-debug
.vscode
.vscode
*.db
*.sqlite
*.sqlite3
*.sqlite-wal
*.sqlite-shm
29 changes: 25 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
# Messenger

Messenger is a cross-platform client-server messenger application based on the RSA algorithm. The project is still in an early stage and currently provides the foundation for the client and server.
Messenger is a simple chat program with a client and a server. Users can connect to a server, send messages, receive messages, and see the previous chat history. The messenger is intentionally kept simple because it is mainly a learning project focused on networking and encryption, including RSA-based authentication built with a self-implemented RSA library.

<p align="center">
<img src="docs/img/client_start_screen.png" alt="Start screen" width="49%">
<img src="docs/img/client_chat.png" alt="Client chat" width="49%">
</p>

## Functionality

- **Client connection:** enter a server host, connect to the server, disconnect again when terminating the program, and see the current connection status in the UI.
- **Group chat authentication:** each server represents one group chat; clients request access with a name and their public key, without passwords. After server-side approval, they authenticate through RSA-based verification using the stored public key.
- **Message sending:** compose text messages and send them to the connected server.
- **Message receiving:** display incoming messages in the client window as they arrive from the server.
- **Group chat forwarding:** the server keeps track of the clients connected to its group chat and broadcasts chat messages to every active session.
- **Chat history:** the server stores the group chat history and sends all previous messages to newly connected clients.
- **Protocol validation:** client and server exchange structured messages and reject unsupported protocol versions.
- **Error handling:** connection errors and invalid client-side input are surfaced through the status text and message log.

## Architecture

The repository contains two applications: `Messenger-Client` and `Messenger-Server`. The client is built with Qt/QML; the server is a terminal program.
The repository contains two applications, `Messenger-Client` and `Messenger-Server`, as well as a shared protocol library. Communication is based on TCP and serialized with `QDataStream`.

- **Client:** the graphical Qt Quick/QML interface delegates connection handling to `NetworkManager`. It manages the TCP socket, the authentication state machine, RSA challenge signing, and the sending and receiving of protocol messages. `ConnectionStore` stores past connection and handles RSA keys.
- **Shared protocol:** `src/shared` defines the versioned message format, message types, protocol limits, nonce generation, and the canonical authentication transcript used by both applications.
- **Server:** the terminal-based server accepts connections through `QTcpServer` and creates one `Session` per client. A session owns its socket and authentication state, while the central server validates incoming messages, persists chat messages, and broadcasts them to authenticated sessions.
- **Persistence:** `MessageStore` encapsulates the SQLite database. It applies migrations and stores users, public keys, registration requests, and chat history. Private RSA keys remain exclusively on the clients.

Both applications use a singleton through `getInstance()`. The project is built with CMake and C++20, with the RSA library included as a submodule.
After a TCP connection is established, the client and server perform a challenge-response handshake. The client signs a transcript containing fresh client and server nonces with its private RSA key; the server verifies the signature with the stored public key. Only then is the session marked as authenticated and allowed to receive the chat history or exchange chat messages. Unknown users first create a registration request that must be approved through the server CLI. A detailed sequence is documented in [Connection Establishment and Client Authentication](docs/connection-establishment.md).

## Dependencies

Expand Down Expand Up @@ -45,7 +66,7 @@ git clone --recurse-submodules https://github.com/ParallelEngineering/Messenger.
If the repository has already been cloned without submodules, they can be initialized recursively with remote updates:

```bash
git submodule update --init --recursive --remote
git submodule update --init --recursive
```

## Build
Expand Down
204 changes: 204 additions & 0 deletions docs/connection-establishment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
# Connection Establishment and Client Authentication

## Purpose

Every client must authenticate immediately after establishing a TCP connection. Authentication proves that the client owns the private RSA key matching the public key stored for its user account in the server database.

The private key remains on the client. The server stores only public keys. There is no predefined administrator account: an unknown username creates a pending registration request that is approved or rejected with the server command-line interface.

## Protocol overview

```mermaid
sequenceDiagram
participant C as Client
participant S as Server
participant DB as Database

C->>S: Establish TCP connection
Note over C,S: Session state: AwaitingHello

C->>S: AuthHello(username, clientNonce)
S->>DB: Load user ID and public key
alt User exists
DB-->>S: User ID and public key
S->>S: Generate authId and serverNonce
S-->>C: AuthChallenge(authId, serverNonce)
Note over C,S: Session state: AwaitingProof

C->>C: Hash canonical authentication transcript
C->>C: Sign digest with private RSA key
C->>S: AuthProof(authId, signature)
S->>S: Verify signature with stored public key

alt Signature is valid
S->>S: Bind user ID and username to session
S->>S: Session state: Authenticated
S-->>C: AuthSuccess
S-->>C: Chat history
else Signature is invalid
S-->>C: AuthFailure
S->>S: Session state: Rejected
S->>S: Close connection
end
else User is unknown
C->>S: Public key included in AuthHello
S->>DB: Store pending registration request
S-->>C: RegistrationPending
S->>S: Close connection
end
```

## Authentication messages

Protocol version 3 defines the following message types:

```cpp
enum class MessageType : quint32 {
AuthHello = 1,
AuthChallenge = 2,
AuthProof = 3,
AuthSuccess = 4,
AuthFailure = 5,
RegistrationPending = 6,
RegistrationRejected = 7,
ChatMessage = 100,
SystemMessage = 101,
ErrorMessage = 102,
};
```

The handshake uses these fields:

| Message | Content |
|---|---|
| `AuthHello` | Username, a 32-byte `clientNonce`, and the client's public key |
| `AuthChallenge` | 16-byte `authId` and 32-byte `serverNonce` |
| `AuthProof` | Matching `authId` and RSA signature |
| `AuthSuccess` | Canonical authenticated username |
| `AuthFailure` | Generic authentication error |
| `RegistrationPending` | The unknown user's access request is waiting for server approval |
| `RegistrationRejected` | The matching access request was rejected |

All messages include the protocol version and message type. Field sizes and the expected message order are validated by both sides.

## Authentication proof

Client and server independently create the same canonical authentication transcript:

```text
domainSeparator = "MessengerAuth/v1"
protocolVersion = CurrentProtocolVersion
username = normalized username
authId = random authentication attempt ID
clientNonce = random client nonce
serverNonce = random server nonce
```

The values are encoded in a fixed order with `QDataStream`. The client calculates the digest with Qt:

```text
digest = SHA-256(authenticationTranscript)
```

The RSA library performs the signature operation using its existing `BigInt` and `modPow` implementation:

```text
digestInteger = BigInt(digest)
signature = digestInteger^d mod n
```

The server loads the user's public key from the database and verifies:

```text
verifiedDigest = signature^e mod n
valid = verifiedDigest == digestInteger
```

The signature has the fixed byte length of the RSA modulus. Empty, incorrectly sized, or out-of-range signatures are rejected.

## Session states

Each server-side connection has an authentication state:

```cpp
enum class AuthenticationState {
AwaitingHello,
AwaitingProof,
Authenticated,
Rejected,
};
```

| Session state | Accepted client message | Other messages |
|---|---|---|
| `AwaitingHello` | `AuthHello` | Reject and disconnect |
| `AwaitingProof` | `AuthProof` | Reject and disconnect |
| `Authenticated` | Authorized application messages | Reject invalid messages |
| `Rejected` | None | Disconnect |

Authentication must finish within 30 seconds. The challenge belongs to one TCP session, matches one `authId`, and can be used only once. Temporary authentication data is cleared after success, failure, timeout, or disconnect.

## Server behavior

After accepting a TCP connection, the server waits for `AuthHello` without sending chat history or other application data.

For `AuthHello`, the server:

1. validates the username and client nonce;
2. loads the user ID, canonical username, and public key from the database;
3. generates a random authentication ID and server nonce;
4. stores the authentication context in the session;
5. sends `AuthChallenge`.

If the username does not exist, the server validates the public key from `AuthHello`, stores an idempotent pending request for that username and key, sends `RegistrationPending`, and closes the connection. A rejected matching request produces `RegistrationRejected`. The administrator reviews requests with `Messenger-Server requests` and decides with `Messenger-Server approve <id>` or `Messenger-Server reject <id>`.

The administrator can list accounts with `Messenger-Server users` and remove an account with `Messenger-Server delete-user <id>`. Deletion removes the user, their stored messages, and all registration requests for that username in one transaction.

The command `Messenger-Server clear-history` deletes all stored chat messages without changing users or registration requests.

For a known user and `AuthProof`, the server reconstructs the transcript, calculates its SHA-256 digest, and verifies the signature with the stored public key. On success, it binds the user ID and username to the session, sends `AuthSuccess`, and then sends the chat history. On failure, it sends `AuthFailure` and closes the connection.

Only authenticated sessions may send or receive chat messages. The server sets the sender name and timestamp itself:

```cpp
Message verifiedMessage = incomingMessage;
verifiedMessage.senderName = session->userName();
verifiedMessage.timestamp = QDateTime::currentDateTimeUtc();
```

Chat history and broadcasts are sent only to authenticated sessions.

## Client behavior

The client distinguishes an established TCP socket from an authenticated connection:

```cpp
enum class ConnectionState {
Disconnected,
Connecting,
AwaitingChallenge,
SigningChallenge,
AwaitingAuthenticationResult,
Authenticated,
};
```

After the TCP socket connects, the client generates `clientNonce` and sends `AuthHello`, including the selected public key. After receiving `AuthChallenge`, it creates and signs the authentication digest and sends `AuthProof`.

For `RegistrationPending` or `RegistrationRejected`, the client shows the corresponding access status and disconnects. It does not reconnect automatically; after an approval the user simply tries to connect again.

The chat UI remains disabled until `AuthSuccess` is received. The existing status display reports the current step, including connection, challenge processing, signature creation, verification, success, timeout, and failure.

## Replay protection and failure handling

Every authentication attempt uses a new client nonce, server nonce, and authentication ID. All three values are covered by the signature, so a recorded `AuthProof` cannot authenticate another connection.

The server closes the connection for malformed messages, unexpected message types, invalid keys, an incorrect authentication ID, timeout, or an invalid signature. Unknown users receive a pending or rejected registration result. Private key material is never logged or transmitted.

## Connection rule

```text
TCP connected != authenticated
```

Before RSA verification succeeds, the connection may process only authentication messages. After verification, the database user is bound to the session and application messages are permitted.
Binary file added docs/img/client_chat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/client_start_screen.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion src/client/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
find_package(Qt6 COMPONENTS Quick Qml Network REQUIRED)
find_package(Qt6 COMPONENTS Quick Qml QuickControls2 Network REQUIRED)

qt_standard_project_setup(REQUIRES 6.5)

qt_add_executable(Messenger-Client
WIN32
MACOSX_BUNDLE
connection_store.cpp
network_manager.cpp
client.cpp
)

Expand All @@ -24,5 +26,8 @@ target_link_libraries(Messenger-Client
PRIVATE
Qt6::Quick
Qt6::Qml
Qt6::QuickControls2
Qt6::Network
Messenger-Shared
RSA
)
Loading
Loading