finished the protocol

This commit is contained in:
Rusty Striker 2025-01-07 18:40:03 +02:00
parent 84d9bd0ef0
commit 661c217a73
Signed by: RustyStriker
GPG key ID: 87E4D691632DFF15
4 changed files with 121 additions and 20 deletions

View file

@ -3,7 +3,7 @@
[ ] Do print stuff for EVERYTHING (with colors preferably) [ ] Do print stuff for EVERYTHING (with colors preferably)
[x] Client [x] Client
[x] Server [x] Server
[ ] rewrite the protocol.md [x] rewrite the protocol.md
[ ] Make a video that shows how everything works yada yada [ ] Make a video that shows how everything works yada yada
[ ] export everything and upload it [ ] export everything and upload it
[ ] get a nice drink to celebrate [ ] get a nice drink to celebrate

View file

@ -1,5 +1,3 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Security.Cryptography; using System.Security.Cryptography;
@ -54,16 +52,17 @@ public class Message
public byte[] Bytes() public byte[] Bytes()
{ {
// initialize a list
List<byte> msg = []; List<byte> msg = [];
// 0..4 // 0..4 - message id
msg.AddRange(BitConverter.GetBytes(Id)); msg.AddRange(BitConverter.GetBytes(Id));
// 4..12 // 4..12 - sender's phone number
msg.AddRange(Utils.NumberToBytes(Sender)); msg.AddRange(Utils.NumberToBytes(Sender));
// 12 // 12 - is this message an acknowledgement or a normal message
msg.Add((byte)(IsAck ? 1 : 0)); msg.Add((byte)(IsAck ? 1 : 0));
// 13..(len - 32) // 13.. the message itself (should be empty for an acknowledgement)
msg.AddRange(Encoding.UTF8.GetBytes(Content)); msg.AddRange(Encoding.UTF8.GetBytes(Content));
// Turn into array, prepending the version byte
return [0, .. msg]; return [0, .. msg];
} }
} }

View file

@ -1,15 +1,67 @@
# The Protocol # The Protocol
1. Encryptions used:
- RSA-1024 (can be of somewhat arbitrary length):
used for initial server communication,
client to client communication and for client authentication.
- SHA3-256 hash:
verification of information by signing with the RSA keys (messages and authentication).
- AES-CFB-256:
Used for encryption of communication between the client and the server.
- both PKCS7 and padding-less messages are being sent as
most requests are made to be exactly 1 block (128 bits),
which technically results in a cookbook style encrytion.
2. Registration is done by getting a public key from the user, and in a
second packet getting the 6 digit code, the code is then signed to make
sure the key given in the first packet is indeed the expected key.
For more details see Registration below
3. Public keys are generated by the client when it wants to register, it is
then the clien't job to save private key in a secure fashion
(currently it is saved as an unencrypted file in pem format for ease of debug).
The AES session keys are also generated by the client, but they are not
saved anywhere as they are only for the current session (if a disconnect
happend, a new key needs to be generated). The session key is sent to the
server as the first packet, signed with the server's public key, so only
the server could obtain them.
The server saves each user public key (and each client can cache other
users public keys to reduce network usage), while the keys should be saved
in a secure fashion, it is more important for them to not be changed rather
then not having general access, as getting someone's public key is as easy
as registering and making a simple request.
Passing keys between users is not of a great security risk, as all keys
passed are public keys.
4. Messages are first encrypted using the recipient's public key, then signed
using the sender's private key (the signature is appended). This allows the recipient to verify the message's claimed origin. Message
Acknowledgements are special messages, being sent as normal messages with
the same id as the acknowledged message but with the IsAck flag set to true.
5. See Passing messages below
6. See Requests below
7. The server uses 2 Dictionaries as the data structure (see `server/Data.cs`)
(not saved to disk due to the non-trivial nature of it),
1 dictionary is used to save the public keys, and the other acts as a
mailbox for each client.
### Key Derivation Function ### About Key Derivation Function
doesnt seem to be any reason to use a key derivation function. KDF isnt necessary here, as the client already has the server's public key and no key exchange needs to happen,
the client can send an encrypted symmetric key to the server, and a secure connection can be established using only 1 request.
NOTE: the server in this case trusts the client to create a good symmetric key. This is not a big issue in my opinion,
as the client (if malicious) can send a third party the transfered data regardless.
## Initializing a connection
First packet of each connection needs to be an encrypted packet
(using the server's public key) of an AES-256 session key appended by
some random IV.
## Registration: ## Registration:
- User sends a `Register` requset giving them a public key and encrypting using the server's public key - User sends a `Register` requset giving them a public key and a phone number.
- Server sends the user a `VerificationRequired` (not in current code, as the 6 digit code does that for now) message & a 6-digit code (Secure channel) - Server sends the user a `VerificationRequired` message (not in current code, as the 6 digit code does that for now) & a 6-digit code (Secure channel)
- User sends the server a `ConfirmRegister` with the 6-digit code, signed using the key provided at previous stage - User sends the server a `ConfirmRegister` with the 6-digit code, signed using the key provided at previous stage
- Server verifies the signature and code, and if both are valid it sends a last `Confirm` and the registration process is done - Server verifies the signature and code, and if both are valid it sends a last `Confirm` and the registration process is done
@ -24,26 +76,49 @@ In order to send a message from A to B, A will ask the server for B's key,
A will then encrypt the message using B's key, append a signature, and send a `SendMessage` A will then encrypt the message using B's key, append a signature, and send a `SendMessage`
request with the payload having the structure of `Enc_b(Message object) + Signature_A(Message object)`. request with the payload having the structure of `Enc_b(Message object) + Signature_A(Message object)`.
The server will hold on to the message until B will send a `GetMessages` request The server will hold on to the message until B will send a `GetMessages`
to the server. request to the server.
NOTE: The server actually doesnt know (and care) how the message is
constructed, allowing the change of the message structure without updating
the server itself.
## Requests ## Requests
- Register: - Register:
data: Phone - 8 bytes, RSA key size (payload length) - 2 bytes data: Phone - 8 bytes, RSA key size (payload length) - 2 bytes
Response: a simple `ValidationRequired`, not implemented as the 6 digit
code covers that.
- ConfirmRegister (signed & encrypted 6 digit code) - ConfirmRegister (signed & encrypted 6 digit code)
data: 6 bytes for the 6 digit code, 4 bytes for signature length data: 6 bytes for the 6 digit code, 4 bytes for signature length
Response: UTF-8 string of either `OK` or `SIG INVALID`, with PKCS7 padding
- Login: - Login:
data: 8 bytes of user's phone data: 8 bytes of user's phone
Response: 16 bytes to be signed, no padding (1 block)
- ConfirmLogin (signed hash): - ConfirmLogin (signed hash):
data: hash length data: hash length
Response UTF-8 string of either `OK` or `INVALID SIG`, PKCS7 padding
- GetMessages: - GetMessages:
data: EMPTY data: EMPTY
Response: 8 shorts, appended with the messages themselves.
The last short marks if there are more messages to be read, the rest
are the lengths of each corresponding message (by order).
No padding as only the 8 shorts are encrypted using the session key.
- GetUserKey: - GetUserKey:
extra data: 8 bytes (4 bits per digit) of whoever we want to get the key of extra data: 8 bytes (4 bits per digit) of whoever we want to get the key of
Response: The key, PKCS7 padded
- SendMessage: - SendMessage:
extra data: 8 bytes (4 bits per digit) of who to send the data, 4 bytes (32bit) for length in bytes extra data: 8 bytes (4 bits per digit) of who to send the data, 4 bytes (32bit) for length in bytes
I think it all can go into a:
Response: NONE
Request format (see `lib/Request.cs:3` - `#RequestType` for request type numbers):
``` ```
{ {
Version byte (0) - 1 byte, Version byte (0) - 1 byte,
@ -53,7 +128,34 @@ I think it all can go into a:
} = 16 bytes = 128 bits } = 16 bytes = 128 bits
``` ```
Encryption and Hashes used: The SendMessage payload structure can be seen in `client/Message.cs:55` in
public keys: RSA-1024 (can be of somewhat arbitrary length) the `Bytes()` function.
Hashes: SHA3-256 The binary format contains:
symmetric keys: AES-CFB-256 with PCKS7 padding (when needed as most stuff are made to fit in 1 block) - 1 version byte
- 4 bytes for a message ID
- 8 bytes for the sender's phone number
- 1 byte for flags (IsAck)
- The rest is filled with the message itself
## Notes and assumptions
- Print statements are color coded: RED - unencrypted, GREEN - encrypted, ORANGE - General server stuff, WHITE - Messages.
- The given code does not handle errors nicely (connection will simply shutdown and client will crash).
- Stream handling is also not ideal, as both server and client reads all
that was sent and not actually read only the amount needed.
- Client does not get messages automatically,
instead a `/get` command (among others such as `/pull` and `/fetch`) are
present in order to get new messages from the server
- The client does not handle getting message properly, as not extra reading
and no proper buffering is being done (if the incoming messages are
too long for the buffer, it will crash).
- The server does not save its current data and does not load from disk.
Saving the data and is not as trivial as in python since the RSA class
does not contain a constructor.
# Part 3
Availability cannot be guaranteed, as the server can always get hit by a rocket,
a malicious actor can attempt to scramble or change the packets in transit (which only
a reconnect attempt can really solve this, assuming the connection will not be tempered with
this time), the client's or server's area might have an extended power/internet outage and a meteor might hit earth and society might collapse.

View file

@ -31,7 +31,7 @@ public class Data
} }
else else
{ {
// generate a new queue because one doesnt already exists // generate a new queue because one doesnt already exists
Messages[Phone] = new Queue<byte[]>(); Messages[Phone] = new Queue<byte[]>();
return []; // no messages were in the list so no reason to attempt to send any message return []; // no messages were in the list so no reason to attempt to send any message
} }