From 8f8736d09efce46147ff37d4840fb424aa6cb7e4 Mon Sep 17 00:00:00 2001 From: Kyren223 Date: Sat, 5 Jul 2025 23:24:17 +0300 Subject: [PATCH] Updated protocol spec to new V2 version --- internal/packet/packet.go | 22 ++++++++-- internal/packet/protocol.md | 81 ++++++++++++++++++++++++++++++++++++ internal/packet/protocol.txt | 59 -------------------------- internal/server/server.go | 1 - 4 files changed, 99 insertions(+), 64 deletions(-) create mode 100644 internal/packet/protocol.md delete mode 100644 internal/packet/protocol.txt diff --git a/internal/packet/packet.go b/internal/packet/packet.go index 2285065..8bc9669 100644 --- a/internal/packet/packet.go +++ b/internal/packet/packet.go @@ -60,6 +60,9 @@ const ( PacketAuthenticate + PacketUsersInfo + PacketGetUsers + PacketSetUserData PacketGetUserData @@ -94,9 +97,6 @@ const ( PacketBlockUser PacketBlockInfo - PacketGetUsers - PacketUsersInfo - PacketMax ) @@ -269,6 +269,19 @@ func (p Packet) DecodedPayload() (Payload, error) { case PacketError: payload = &Error{} + case PacketTosInfo: + payload = &TosInfo{} + case PacketAcceptTos: + payload = &AcceptTos{} + + case PacketGetNonce: + payload = &GetNonce{} + case PacketNonceInfo: + payload = &NonceInfo{} + + case PacketAuthenticate: + payload = &Authenticate{} + case PacketSetUserData: payload = &SetUserData{} case PacketGetUserData: @@ -335,7 +348,8 @@ func (p Packet) DecodedPayload() (Payload, error) { payload = &UsersInfo{} default: - assert.Never("unexpected packet.PacketType", "type", p.Type()) + assert.Assert(!p.Type().IsSupported(), "supported PackeType wasn't handled", "type", p.Type()) + return nil, fmt.Errorf("unsupported PackeType: %v", p.Type().String()) } err := p.DecodePayloadInto(payload) return payload, err diff --git a/internal/packet/protocol.md b/internal/packet/protocol.md new file mode 100644 index 0000000..6a50475 --- /dev/null +++ b/internal/packet/protocol.md @@ -0,0 +1,81 @@ +# Eko Protocol V2 + +## Packet Structure + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Version |En.| Type | Payload Length | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Payload... Payload Length bytes ... | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +Order of bytes is from left to right, top to bottom. +The first byte is always the version, any bytes after it +depend on the specific value of the version byte. + +- Encoding: 0-3, determines the way the payload was encoded + - 0: JSON + - 1: MsgPack + - 2: Reserved for future use + - 3: Reserved for future use +- Type: 0-63, determines the type ("schema"), of the payload +- Payload Length: 0-65531, determines how long the payload is in bytes +- Payload: 0 to 65531 bytes long, depending on the payload size (~64kb) + +## Handshake + +The first time a connection is established, the following packets are exchanged. + +- Server sends a TosInfo (Type 1) with the Terms of Service and Privacy Policy +- Client must respond with an AcceptTos (Type 2) with an agree boolean of true + - If the client sends any other type of packet, or the boolean is false, the server may close the connection + +After the handshake, the client may send any unauthenticated packets. + +### Authentication + +At any time, while authenticated or not, the client may request a nonce (GetNonce, Type 3). +The server then must responsd with NonceInfo (Type 4). + +It's recommended for the server to use a cryptographically secure random number generator, +and to rotate the value every minute. + +To authenticate, a client must send an Authenticate request (Type 5), +the request must include a public key, and a signature (64 bytes) of the nonce. + +The server must then respond in one of the following ways: + +- With an Error (Type 0) with any message, indicating failed authentication +- With a UsersInfo (Type 6) indicating success + - Must include exactly one UserID (corresponding to the authenticated user) + +Note: if the client takes too long between the nonce request, the nonce may have been rotated +and the client will need to redo these steps. + +## Error handling + +The server may close a connection only in these cases: + +- the client has already closed the connection +- the server has finished processing and sending all reqeusts +- the server experienced an abrupt termination (SIGKILL, powerloss, etc) +- after sending a TosInfo and a receiving any packet except of AcceptTos with a value of true + +The server may stop receiving data (and drop any partial requests) at any time (in response to a SIGINT/SIGTERM). + +The client may close a connection at any time but data loss may occur. + +### Malformed Packets + +- unsupported/invalid version: connection can be closed immediately +- unsupported encoding: server must respond with an error type, may use any encoding, client may close the connection +- unknown type: server must respond with an error, client may close the connection +- malformed paylod: server must respond with an error, client may close the connection + +For application errors such as a client asking to send a message in a non-existent Frequency, +the server must respond with an error packet. +For internal errors such as database failure, the server must respond, it may choose to +disclose as much information as it wants, or just say "internal server error". diff --git a/internal/packet/protocol.txt b/internal/packet/protocol.txt deleted file mode 100644 index 9a0070d..0000000 --- a/internal/packet/protocol.txt +++ /dev/null @@ -1,59 +0,0 @@ -# Eko Protocol V1 - -## Packet Structure - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Version |En.| Type | Payload Length | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Payload... Payload Length bytes ... | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - -Order of bytes is from left to right, top to bottom. -The first byte is always the version, any bytes after it -depend on the specific value of the first byte. - -- Encoding: 0-3, determines the way the payload was encoded - - 0: JSON - - 1: MsgPack - - 2: Reserved for future use - - 3: Reserved for future use -- Type: 0-63, determines the type ("schema"), of the payload -- Payload Length: 0-65531, determines how long the payload is in bytes -- Payload: 0 to 65531 bytes long, depending on the payload size (~64kb) - -## Handshake - -The first time a connection is established, the following packets are exchanged. - -- Server sends a special 1-byte for version then 32-byte the challenge nonce packet -- Client sends a Challenge Response packet with: - * version (1-byte long) - - the client's ed25519 public key (32-bytes long) - - the client's ed25519 signature for the server-given nonce (64-bytes long) - -After the handshake the server may close the connection, -for example due to an invalid signature. - -## Error handling - -The server may abruptly close a connection in these cases: - -- After the initial handshake -- After any response - A server may not close the connection if it received a request, it must first response then close. - -The client may abruptly close a connection at any time - -### Malformed Packets - -- unsupported/invalid version: connection can be closed immediately -- unsupported encoding: server must respond with an error type, may use any encoding, client may close the connection -- unknown type: server must respond with an error, client may close the connection -- malformed paylod: server must respond with an error, client may close the connection - -For application errors such as a client asking to send a message in a non-existent Frequency, -the server must respond with an error packet. -For internal errors such as database failure, the server must respond, it may choose to -disclose as much information as it wants, or just say "internal server error". diff --git a/internal/server/server.go b/internal/server/server.go index 1727961..32c1205 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -324,7 +324,6 @@ func processPacket(ctx context.Context, sess *session.Session, pkt packet.Packet } assert.NotNil(response, "response must always be assigned to") - log.Println(sess.Addr(), "sending", response.Type(), "response:", response) return packet.NewPacket(packet.NewMsgPackEncoder(response)) }