3.8 KiB
The Protocol
All encryptions are made using RSA (key size to be determined), no symmetric encryptions are used due to messages being short.
a. Encryption will be a-symmetric because we can assume messages are short
b. See "Registration" below
c. Server keeps public keys for everyone, and gives them to a user when they want to
send a message to someone (only the relevant key)
d. Every e2e message is both signed and encrypted aka, message M from A to B
EM = E_Bpub(E_Apriv(N))
e. message is sent to the server in a E_server(E_Apriv(metadata + EM from d))
,
message ack is sent to the server as a MessageAck(msg_id)
, which will be retrieved
by the user when it pulls for messages.
f. TODO: properly explain the different api structs
g. TODO: properly explain how the server handles data
Key Derivation Function
as there is no use of symmetric keys (at least for this scope), I dont believe there is a need to use KDF, it will reduce the keyspace and we already have the public key of the server in the clients
Registration:
- User sends a register request to server
(phone number, RSA public key)
, giving them a public key and encrypting using the server's public key - Server sends the user a
VerificationRequired
message & a 6-digit code (Secure channel) - User sends the server the 6-digit code, signed using the key provided at stage 1 (this message is very short and funny, because its a 6-digit code, signed using the user's private key, and encrypted using the server's public key)
- Server sends a last
Confirm
and the registration process is done
"Login"
The users dont need to login, as they dont really need to hold a conenction to the server, only thing that matters is that every message to the server should be signed with the user's private key, thus the key acts as a form of credentials
Passing messages
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, signed with A, wrapped in a SendMessage
request and ultimately signed by A and encrypted using the server's key.
The server will hold on to the message until B will send a GetMessages
request
to the server.
"Control" requests
- Register
(phone, pub RSA key)
extra data: RSA key size (payload length) - ConfirmRegister (signed & encrypted 6 digit code) extra data: 6 bytes for the 6 digit code (can use less but it will be padding otherwise)
- GetMessages (signed & encrypted to not allow someone to "flush" someone else's msgs) extra data: EMPTY
- GetUserKey (dont think it needs any signing or encryption technically, as it is very simple registering and "stealing" a key outside the system, keys are assumed to be unbreakable) extra data: 8 bytes (4 bits per digit) of whoever we want to get the key of
- SendMessage
(to_user, msg_id, EM from d above)
extra data: 8 bytes (4 bits per digit) of who to send the data, 4 bytes (32bit) for length - SendAck
(to_user, msg_id - signed and encrypted)
- server doesnt know if the msg itself is indeed valid and not tempered with extra data: 8 bytes (4 bits per digit) of who to send the data, 4 bytes (32bit) for length
I think it all can go into a:
{
Version byte (0) - 1 byte
RequestType - 1 byte,
Phone number - 8 bytes (every 4 bits is a number, so we have 16 numbers),
UTC timestamp - 8 bytes (long),
extra data (based on the request given) - up to 18 bytes
} = 32 bytes = 256 bits
and we can just append encrypted payloads to it (in SendMessage and SendAck)
To each message we also append a sha3-256 signed hash, this shit really feels like overkill and im not sure about it, on the other i do want to have a bigger key size and 512 seems like an okay-ish thing to have, as 256 is short and 1024 is amusingly long for the amount of data the server needs to encrypt
enc_server( request - 256 bits, signed sha3-256 )
which means: Keys: RSA-512 Hashes: SHA3-256