diff --git a/include/rpc/protocol.h b/include/rpc/protocol.h index f384d01..aa0317f 100644 --- a/include/rpc/protocol.h +++ b/include/rpc/protocol.h @@ -9,7 +9,7 @@ extern "C" { #endif -#define RPC_HEADER_SIZE 22u +#define RPC_HEADER_SIZE 26u #define RPC_MAX_PAYLOAD_SIZE (1024u * 1024u) typedef enum rpc_op { @@ -66,6 +66,7 @@ typedef struct rpc_header { uint64_t proc_id; uint32_t size; uint64_t call_id; + uint32_t checksum; } rpc_header; typedef struct rpc_writer { @@ -80,6 +81,9 @@ void rpc_get_allocator(rpc_allocator *out_allocator); int rpc_header_encode(const rpc_header *header, uint8_t out[RPC_HEADER_SIZE]); int rpc_header_decode(const uint8_t in[RPC_HEADER_SIZE], rpc_header *out); +uint32_t rpc_packet_checksum(const rpc_header *header, const uint8_t *payload, size_t payload_len); +int rpc_packet_sign(rpc_header *header, const uint8_t *payload, size_t payload_len); +int rpc_packet_verify(const rpc_header *header, const uint8_t *payload, size_t payload_len); void rpc_writer_init(rpc_writer *writer); void rpc_writer_reset(rpc_writer *writer); diff --git a/src/client.c b/src/client.c index 9058b76..8a51bc0 100644 --- a/src/client.c +++ b/src/client.c @@ -114,6 +114,11 @@ static int send_packet(rpc_client *client, rpc_op op, uint64_t proc_id, uint64_t }; uint8_t header_buf[RPC_HEADER_SIZE]; + if (rpc_packet_sign(&header, payload ? payload->data : NULL, payload ? payload->len : 0) != 0) { + set_error(client, "send failed"); + rpc_trace_end(RPC_TRACE_CLIENT_SEND, trace); + return -1; + } if (rpc_header_encode(&header, header_buf) != 0) { set_error(client, "send failed"); rpc_trace_end(RPC_TRACE_CLIENT_SEND, trace); @@ -161,13 +166,20 @@ static int recv_packet(rpc_client *client, rpc_header *header, uint8_t **body) { return -1; } + const uint8_t *payload = client->read_buf + client->read_off + RPC_HEADER_SIZE; + if (rpc_packet_verify(header, payload, header->size) != 0) { + set_error(client, "packet checksum failed"); + rpc_trace_end(RPC_TRACE_CLIENT_RECV, trace); + return -1; + } + *body = rpc_mem_alloc(header->size ? header->size : 1); if (!*body) { set_error(client, "out of memory"); rpc_trace_end(RPC_TRACE_CLIENT_RECV, trace); return -1; } - if (header->size > 0) { memcpy(*body, client->read_buf + client->read_off + RPC_HEADER_SIZE, header->size); } + if (header->size > 0) { memcpy(*body, payload, header->size); } client->read_off += packet_size; if (client->read_off == client->read_len) { client->read_off = 0; diff --git a/src/protocol.c b/src/protocol.c index 6b40120..8fed9b1 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -39,6 +39,21 @@ static uint64_t get_u64(const uint8_t *in) { return value; } +static uint32_t checksum_update(uint32_t crc, const uint8_t *data, size_t len) { + static const uint32_t table[16] = { + 0x00000000u, 0x105ec76fu, 0x20bd8edeu, 0x30e349b1u, 0x417b1dbcu, 0x5125dad3u, + 0x61c69362u, 0x7198540du, 0x82f63b78u, 0x92a8fc17u, 0xa24bb5a6u, 0xb21572c9u, + 0xc38d26c4u, 0xd3d3e1abu, 0xe330a81au, 0xf36e6f75u, + }; + + for (size_t i = 0; i < len; ++i) { + crc ^= data[i]; + crc = (crc >> 4u) ^ table[crc & 0x0fu]; + crc = (crc >> 4u) ^ table[crc & 0x0fu]; + } + return crc; +} + static int valid_op(uint8_t op) { return op == RPC_OP_RPC || op == RPC_OP_PING || op == RPC_OP_DISCONNECT || op == RPC_OP_RESPONSE || op == RPC_OP_ERROR; @@ -52,6 +67,7 @@ int rpc_header_encode(const rpc_header *header, uint8_t out[RPC_HEADER_SIZE]) { put_u64(out + 2, header->proc_id); put_u32(out + 10, header->size); put_u64(out + 14, header->call_id); + put_u32(out + 22, header->checksum); return 0; } @@ -64,6 +80,39 @@ int rpc_header_decode(const uint8_t in[RPC_HEADER_SIZE], rpc_header *out) { out->proc_id = get_u64(in + 2); out->size = get_u32(in + 10); out->call_id = get_u64(in + 14); + out->checksum = get_u32(in + 22); if (out->size > RPC_MAX_PAYLOAD_SIZE) { return -1; } return 0; } + +uint32_t rpc_packet_checksum(const rpc_header *header, const uint8_t *payload, size_t payload_len) { + if (!header || (!payload && payload_len > 0) || payload_len > RPC_MAX_PAYLOAD_SIZE) { return 0; } + + rpc_header signed_header = *header; + signed_header.checksum = 0; + + uint8_t header_buf[RPC_HEADER_SIZE]; + if (rpc_header_encode(&signed_header, header_buf) != 0) { return 0; } + + uint32_t crc = 0xffffffffu; + crc = checksum_update(crc, header_buf, sizeof(header_buf)); + crc = checksum_update(crc, payload, payload_len); + return ~crc; +} + +int rpc_packet_sign(rpc_header *header, const uint8_t *payload, size_t payload_len) { + if (!header || (!payload && payload_len > 0) || payload_len > RPC_MAX_PAYLOAD_SIZE || + header->size != payload_len) { + return -1; + } + header->checksum = rpc_packet_checksum(header, payload, payload_len); + return 0; +} + +int rpc_packet_verify(const rpc_header *header, const uint8_t *payload, size_t payload_len) { + if (!header || (!payload && payload_len > 0) || payload_len > RPC_MAX_PAYLOAD_SIZE || + header->size != payload_len) { + return -1; + } + return rpc_packet_checksum(header, payload, payload_len) == header->checksum ? 0 : -1; +} diff --git a/src/server.c b/src/server.c index 15d01c9..257483f 100644 --- a/src/server.c +++ b/src/server.c @@ -168,6 +168,7 @@ static int queue_packet(rpc_connection *conn, rpc_op op, uint8_t flags, uint64_t .size = (uint32_t)payload_len, .call_id = call_id, }; + if (rpc_packet_sign(&header, payload, payload_len) != 0) { return -1; } if (rpc_header_encode(&header, header_buf) != 0) { return -1; } if (append_bytes(&conn->write_buf, &conn->write_len, &conn->write_cap, header_buf, sizeof(header_buf)) != 0) { return -1; @@ -333,6 +334,10 @@ static void parse_available(rpc_connection *conn) { } if (conn->read_len - off - RPC_HEADER_SIZE < header.size) { break; } const uint8_t *payload = conn->read_buf + off + RPC_HEADER_SIZE; + if (rpc_packet_verify(&header, payload, header.size) != 0) { + conn->closing = 1; + break; + } if (handle_packet(conn, &header, payload) != 0) { conn->closing = 1; break; diff --git a/tests/test_protocol.c b/tests/test_protocol.c index 0a3abac..8561cba 100644 --- a/tests/test_protocol.c +++ b/tests/test_protocol.c @@ -38,6 +38,9 @@ int main(void) { .size = 9, .call_id = 0x0102030405060708ull, }; + uint8_t packet_payload[9] = {RPC_TYPE_I64, 0, 0, 0, 0, 0, 0, 0, 42}; + assert(rpc_packet_sign(&h, packet_payload, sizeof(packet_payload)) == 0); + assert(h.checksum != 0); assert(rpc_header_encode(&h, buf) == 0); rpc_header out; assert(rpc_header_decode(buf, &out) == 0); @@ -46,6 +49,11 @@ int main(void) { assert(out.proc_id == h.proc_id); assert(out.size == h.size); assert(out.call_id == h.call_id); + assert(out.checksum == h.checksum); + assert(rpc_packet_verify(&out, packet_payload, sizeof(packet_payload)) == 0); + packet_payload[8] = 43; + assert(rpc_packet_verify(&out, packet_payload, sizeof(packet_payload)) != 0); + packet_payload[8] = 42; buf[0] = 99; assert(rpc_header_decode(buf, &out) != 0);