import "core:bytes"
import "core:crypto/ed25519"
import "core:crypto/sha3"
+import "core:encoding/endian"
import "core:io"
import "core:net"
import "core:mem"
+import "core:time"
ROOM_KEY_SIZE :: 32
+MESSAGE_SIZE :: 1_000 * 4
-From_Bytes_Error {
- Not_Enough_Bytes,
+Request_Validity_Error :: enum {
+ Invalid_Inner_Kind,
Invalid_Public_Key,
}
-Write :: struct {
- room_key: [ROOM_KEY_SIZE]u8,
- message: string,
+From_Bytes_Error :: union {
+ Request_Validity_Error,
+ io.Error,
}
-Client_Request_Content :: union {
- Write,
+Request_Verify_Error :: enum {
+ Expired,
+ Invalid_Signature,
+}
+
+Join_Request :: struct {
+ room_id: [sha3.DIGEST_SIZE_256]u8,
+}
+
+join_request_from_bytes :: proc(stream: io.Stream) -> (request: Join_Request, err: From_Bytes_Error) {
+ room_id: [sha3.DIGEST_SIZE_256]u8
+ _ = io.read(stream, room_id[:]) or_return
+
+ return {
+ room_id = room_id,
+ },
+ nil
+}
+
+join_request_to_bytes :: proc(stream: io.Stream, request: Join_Request) -> io.Error {
+ request := request
+ _ = io.write(stream, request.room_id[:]) or_return
+
+ return nil
+}
+
+Leave_Request :: struct {
+ room_id: [sha3.DIGEST_SIZE_256]u8,
+}
+
+leave_request_from_bytes :: proc(stream: io.Stream) -> (request: Leave_Request, err: From_Bytes_Error) {
+ room_id: [sha3.DIGEST_SIZE_256]u8
+ _ = io.write(stream, request.room_id[:]) or_return
+
+ return {
+ room_id = room_id,
+ },
+ nil
+}
+
+leave_request_to_bytes :: proc(stream: io.Stream, request: Leave_Request) -> io.Error {
+ request := request
+ _ = io.write(stream, request.room_id[:]) or_return
+
+ return nil
+}
+
+Write_Request :: struct {
+ room_id: [sha3.DIGEST_SIZE_256]u8,
+ message: [MESSAGE_SIZE]u8,
+}
+
+write_request_from_bytes :: proc(stream: io.Stream) -> (request: Write_Request, err: From_Bytes_Error) {
+ room_id: [sha3.DIGEST_SIZE_256]u8
+ _ = io.read(stream, room_id[:]) or_return
+
+ message: [MESSAGE_SIZE]u8
+ _ = io.read(stream, message[:]) or_return
+
+ return {
+ room_id = room_id,
+ message = message,
+ },
+ nil
+}
+
+write_request_to_bytes :: proc(stream: io.Stream, request: Write_Request) -> io.Error {
+ request := request
+
+ _ = io.write(stream, request.room_id[:]) or_return
+ _ = io.write(stream, request.message[:]) or_return
+
+ return nil
+}
+
+Client_Request_Inner :: union {
+ Join_Request,
+ Leave_Request,
+ Write_Request,
+}
+
+client_request_inner_from_bytes :: proc(stream: io.Stream) -> (request: Client_Request_Inner, err: From_Bytes_Error) {
+ kind, err1 := io.read_byte(stream)
+ if err1 != nil do return {}, err1
+
+ inner: Client_Request_Inner
+
+ switch kind {
+ case 0: inner = join_request_from_bytes(stream) or_return
+ case 1: inner = leave_request_from_bytes(stream) or_return
+ case 2: inner = write_request_from_bytes(stream) or_return
+ case: return {}, Request_Validity_Error.Invalid_Inner_Kind
+ }
+
+ return inner, nil
+}
+
+client_request_inner_to_bytes :: proc(stream: io.Stream, inner: Client_Request_Inner) -> io.Error {
+ switch i in inner {
+ case Join_Request:
+ io.write_byte(stream, 0) or_return
+ join_request_to_bytes(stream, i) or_return
+
+ case Leave_Request:
+ io.write_byte(stream, 1) or_return
+ leave_request_to_bytes(stream, i) or_return
+
+ case Write_Request:
+ io.write_byte(stream, 2) or_return
+ write_request_to_bytes(stream, i) or_return
+ }
+
+ return nil
}
Client_Request :: struct {
public_key: ed25519.Public_Key,
- content: Client_Request_Content,
+ inner: Client_Request_Inner,
timestamp: i64,
- signature: [32]u8,
+ signature: [ed25519.SIGNATURE_SIZE]u8,
}
-client_request_from_bytes :: proc(bytes: []u8) -> (Client_Request, From_Bytes_Error) {
- bytes := bytes
- bytes_buffer := bytes.Buffer{ buf = mem.buffer_from_slice(bytes) }
+client_request_from_bytes :: proc(input: []u8) -> (request: Client_Request, err: From_Bytes_Error) {
+ input := input
+
+ bytes_buffer := bytes.Buffer{ buf = mem.buffer_from_slice(input) }
stream := bytes.buffer_to_stream(&bytes_buffer)
public_key_bytes: [ed25519.PUBLIC_KEY_SIZE]u8
- err1 := io.read_at_least(stream, public_key_bytes, len(public_key_bytes))
-
- if err1 != nil {
- return {}, .Not_Enough_Bytes
- }
+ _ = io.read(stream, public_key_bytes[:]) or_return
public_key: ed25519.Public_Key
ok := ed25519.public_key_set_bytes(&public_key, public_key_bytes[:])
+ if !ok do return {}, .Invalid_Public_Key
- if !ok {
- return {}, .Invalid_Public_Key
- }
+ inner := client_request_inner_from_bytes(stream) or_return
+
+ timestamp_bytes: [size_of(i64)]u8
+ _ = io.read(stream, timestamp_bytes[:]) or_return
+
+ timestamp, _ := endian.get_i64(timestamp_bytes[:], .Little)
+
+ signature: [ed25519.SIGNATURE_SIZE]u8
+ _ = io.read(stream, signature[:]) or_return
return {
public_key = public_key,
- content = content,
+ inner = inner,
timestamp = timestamp,
signature = signature,
},
nil
}
-client_request_to_bytes :: proc(stream: io.Stream, request: Client_Request) {
-
+client_request_to_bytes_signatureless :: proc(stream: io.Stream, request: Client_Request) -> io.Error {
+ request := request
+
+ public_key_bytes: [ed25519.PUBLIC_KEY_SIZE]u8
+ ed25519.public_key_bytes(&request.public_key, public_key_bytes[:])
+ _ = io.write(stream, public_key_bytes[:]) or_return
+
+ client_request_inner_to_bytes(stream, request.inner) or_return
+
+ timestamp_bytes: [size_of(i64)]u8
+ _ = endian.put_i64(timestamp_bytes[:], .Little, request.timestamp)
+ _ = io.write(stream, timestamp_bytes[:]) or_return
+
+ return nil
}
-client_request_verify :: proc(request: Client_Request, expiration: Maybe(i64)) -> bool {
- panic("TODO")
+client_request_to_bytes :: proc(stream: io.Stream, request: Client_Request) -> io.Error {
+ request := request
+
+ client_request_to_bytes_signatureless(stream, request) or_return
+ _ = io.write(stream, request.signature[:]) or_return
+
+ return nil
}
-send_client_request :: proc(server: net.TCP_Socket, request: Client_Request) {
- panic("TODO")
+client_request_signature :: proc(request: Client_Request, private_key: ^ed25519.Private_Key) -> [ed25519.SIGNATURE_SIZE]u8 {
+ bytes_buf: [10_000]u8
+ bytes_buffer: bytes.Buffer
+ bytes.buffer_init(&bytes_buffer, bytes_buf[:])
+ request_stream := bytes.buffer_to_stream(&bytes_buffer)
+
+ _ = client_request_to_bytes_signatureless(request_stream, request)
+
+ request_bytes := bytes.buffer_to_bytes(&bytes_buffer)
+
+ signature: [ed25519.SIGNATURE_SIZE]u8
+ ed25519.sign(private_key, request_bytes, signature[:])
+
+ return signature
+}
+
+client_request_verify :: proc(request: Client_Request, expiration: Maybe(i64)) -> Maybe(Request_Verify_Error) {
+ request := request
+
+ now := time.time_to_unix_nano(time.now())
+
+ if expir, ok := expiration.?; ok {
+ if now - request.timestamp > expir {
+ return .Expired
+ }
+ }
+
+ bytes_buf: [10_000]u8
+ bytes_buffer: bytes.Buffer
+ bytes.buffer_init(&bytes_buffer, bytes_buf[:])
+ request_stream := bytes.buffer_to_stream(&bytes_buffer)
+
+ _ = client_request_to_bytes_signatureless(request_stream, request)
+
+ request_bytes := bytes.buffer_to_bytes(&bytes_buffer)
+
+ if !ed25519.verify(&request.public_key, request_bytes, request.signature[:]) {
+ return .Invalid_Signature
+ }
+
+ return nil
+}
+
+send_client_request :: proc(server: net.TCP_Socket, request: Client_Request, private_key: ^ed25519.Private_Key) -> net.Network_Error {
+ bytes_buf: [10_000]u8
+ bytes_buffer: bytes.Buffer
+ bytes.buffer_init(&bytes_buffer, bytes_buf[:])
+ request_stream := bytes.buffer_to_stream(&bytes_buffer)
+
+ _ = client_request_to_bytes(request_stream, request)
+ _ = io.write_byte(request_stream, 0)
+
+ request_bytes := bytes.buffer_to_bytes(&bytes_buffer)
+
+ _ = net.send_tcp(server, request_bytes) or_return
+
+ return nil
+}
+
+request_server :: proc(server: net.TCP_Socket, inner: Client_Request_Inner, private_key: ^ed25519.Private_Key) -> net.Network_Error {
+ client_request: Client_Request
+ ed25519.public_key_set_priv(&client_request.public_key, private_key)
+ client_request.inner = inner
+ client_request.timestamp = time.time_to_unix_nano(time.now())
+ client_request.signature = client_request_signature(client_request, private_key)
+
+ send_client_request(server, client_request, private_key) or_return
+
+ return nil
}
Server_Request :: union {}
import "base:runtime"
+import "core:bytes"
import "core:fmt"
+import "core:io"
import "core:log"
import "core:mem"
import "core:net"
import "../common"
+Recv_Packet_Error :: union {
+ io.Error,
+ net.Network_Error,
+}
+
Handle_Client_Data :: struct {
clients_sockets: ^map[net.Endpoint]net.TCP_Socket,
mutex: ^sync.Mutex,
client_endpoint: net.Endpoint,
}
+receive_packet :: proc(socket: net.TCP_Socket, buffer: ^bytes.Buffer) -> (packet: []u8, err: Recv_Packet_Error) {
+ escape := false
+
+ for {
+ byte_buf: [1]u8
+ _ = net.recv_tcp(socket, byte_buf[:]) or_return
+
+ b := byte_buf[0]
+
+ if escape {
+ bytes.buffer_write_byte(buffer, b) or_return
+ escape = false
+ continue
+ }
+
+ switch b {
+ case 0:
+ return bytes.buffer_to_bytes(buffer), nil
+
+ case '\\':
+ escape = true
+ continue
+ }
+
+ bytes.buffer_write_byte(buffer, b) or_return
+ }
+}
+
handle_client :: proc(data: Handle_Client_Data) {
using data
delete_key(clients_sockets, client_endpoint)
}
- recv_buffer: [4_096]u8
- send_buffer: [4_096]u8
+ recv_buf: [10_000]u8
+ recv_buffer: bytes.Buffer
+ bytes.buffer_init(&recv_buffer, recv_buf[:])
+
+ send_buffer: [10_000]u8
response: []u8
for {
- bytes_read, err := net.recv(client_socket, recv_buffer[:])
+ packet, err1 := receive_packet(client_socket, &recv_buffer)
- if err != nil {
- log.errorf("Failed to receive message with error %v.", err)
- return
+ if err1 != nil {
+ log.errorf("Failed to receive packet with error %v", err1)
}
- if bytes_read == 0 {
- break
- }
-
- bytes := recv_buffer[:bytes_read]
-
defer net.send(client_socket, response)
- request, ok := common.client_request_parse(bytes)
+ request, err2 := common.client_request_from_bytes(packet)
- if !ok {
+ if err2 != nil {
response_string := "Invalid request"
response = transmute([]u8)response_string
continue
}
- if !common.client_request_verify(request, 1 * 60 * 60 * 1_000_000_000) {
+ if err3 := common.client_request_verify(request, 1 * 60 * 60 * 1_000_000_000); err3 != nil {
response_string := "Invalid authentication"
response = transmute([]u8)response_string
continue
}
- #partial switch content in request.content {
+ log.infof("Received request: %v", request)
+
+ #partial switch inner in request.inner {
case:
response_string := "Unhandled request type"
response = transmute([]u8)response_string