finished the protocol
This commit is contained in:
parent
84d9bd0ef0
commit
661c217a73
4 changed files with 121 additions and 20 deletions
|
@ -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
|
|
@ -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];
|
||||||
}
|
}
|
||||||
}
|
}
|
124
protocol.md
124
protocol.md
|
@ -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.
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue