diff --git a/README.md b/README.md index b24685a..3e37fa1 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [ ] Do print stuff for EVERYTHING (with colors preferably) [x] Client [x] Server -[ ] rewrite the protocol.md +[x] rewrite the protocol.md [ ] Make a video that shows how everything works yada yada [ ] export everything and upload it [ ] get a nice drink to celebrate \ No newline at end of file diff --git a/client/Message.cs b/client/Message.cs index 828e2fd..e4b3e92 100644 --- a/client/Message.cs +++ b/client/Message.cs @@ -1,5 +1,3 @@ - - using System; using System.Collections.Generic; using System.Security.Cryptography; @@ -54,16 +52,17 @@ public class Message public byte[] Bytes() { + // initialize a list List msg = []; - // 0..4 + // 0..4 - message id msg.AddRange(BitConverter.GetBytes(Id)); - // 4..12 + // 4..12 - sender's phone number msg.AddRange(Utils.NumberToBytes(Sender)); - // 12 + // 12 - is this message an acknowledgement or a normal message 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)); - + // Turn into array, prepending the version byte return [0, .. msg]; } } \ No newline at end of file diff --git a/protocol.md b/protocol.md index 8fc2a02..8c032be 100644 --- a/protocol.md +++ b/protocol.md @@ -1,15 +1,67 @@ # 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: -- User sends a `Register` requset giving them a public key and encrypting using the server's public key -- 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) +- User sends a `Register` requset giving them a public key and a phone number. +- 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 - 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` 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 -to the server. +The server will hold on to the message until B will send a `GetMessages` +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 - Register: 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) 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: data: 8 bytes of user's phone + + Response: 16 bytes to be signed, no padding (1 block) - ConfirmLogin (signed hash): data: hash length + + Response UTF-8 string of either `OK` or `INVALID SIG`, PKCS7 padding - GetMessages: 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: extra data: 8 bytes (4 bits per digit) of whoever we want to get the key of + + Response: The key, PKCS7 padded - SendMessage: 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, @@ -53,7 +128,34 @@ I think it all can go into a: } = 16 bytes = 128 bits ``` -Encryption and Hashes used: - public keys: RSA-1024 (can be of somewhat arbitrary length) - Hashes: SHA3-256 - symmetric keys: AES-CFB-256 with PCKS7 padding (when needed as most stuff are made to fit in 1 block) +The SendMessage payload structure can be seen in `client/Message.cs:55` in +the `Bytes()` function. +The binary format contains: +- 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. \ No newline at end of file diff --git a/server/Data.cs b/server/Data.cs index e64f33e..1cec5a1 100644 --- a/server/Data.cs +++ b/server/Data.cs @@ -31,7 +31,7 @@ public class Data } else { - // generate a new queue because one doesnt already exists + // generate a new queue because one doesnt already exists Messages[Phone] = new Queue(); return []; // no messages were in the list so no reason to attempt to send any message }