Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions PROTOCOL.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,14 @@ PKT_PONG = 0x07

## Handshake

Handshake is unencrypted and establishes a shared session key.
Handshake is unencrypted and establishes a shared session key. It also carries optional protocol metadata.

Payload format:
```
[32 bytes] Ed25519 public key
[N bytes] hostname (null‑terminated)
[1 byte] protocol version (optional)
[1 byte] protocol flags (optional)
```

Flow:
Expand All @@ -60,6 +62,8 @@ Flow:
4. Client derives the same shared secret.
5. Both sides set `session.authenticated = true` and start encrypted traffic.

If version/flags are missing, peers assume protocol version 1 and flags 0.

## Encryption

For all non‑handshake packets:
Expand Down Expand Up @@ -144,7 +148,7 @@ CTRL_DISCONNECT 0x07
## Versioning

Protocol version is in the header. Compatibility rules:
- Major version must match.
- Accept versions in `[PROTOCOL_MIN_VERSION, PROTOCOL_VERSION]`.
- New fields should be appended to payloads with safe defaults.
- Unknown packet types should be ignored.

Expand Down
5 changes: 5 additions & 0 deletions include/rootstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
*/

#define ROOTSTREAM_VERSION "1.0.0"
#define PROTOCOL_VERSION 1
#define PROTOCOL_MIN_VERSION 1
#define PROTOCOL_FLAGS 0
#define MAX_DISPLAYS 4
#define MAX_PACKET_SIZE 1400
#define MAX_PEERS 16
Expand Down Expand Up @@ -279,6 +282,8 @@ typedef struct {
size_t video_rx_received; /* Bytes received so far */
uint64_t last_sent; /* Last outbound packet time (ms) */
uint64_t last_ping; /* Last keepalive ping time (ms) */
uint8_t protocol_version; /* Peer protocol version */
uint8_t protocol_flags; /* Peer protocol flags */
} peer_t;

/* ============================================================================
Expand Down
49 changes: 42 additions & 7 deletions src/network.c
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ int rootstream_net_send_encrypted(rootstream_ctx_t *ctx, peer_t *peer,

/* Build header */
hdr->magic = PACKET_MAGIC;
hdr->version = 1;
hdr->version = PROTOCOL_VERSION;
hdr->type = type;
hdr->flags = 0;
hdr->nonce = nonce;
Expand Down Expand Up @@ -431,10 +431,38 @@ int rootstream_net_recv(rootstream_ctx_t *ctx, int timeout_ms) {
memcpy(peer_public_key, payload, CRYPTO_PUBLIC_KEY_BYTES);

/* Extract hostname (null-terminated string after public key) */
size_t hostname_len = hdr->payload_size - CRYPTO_PUBLIC_KEY_BYTES;
if (hostname_len > 0 && hostname_len < sizeof(peer_hostname)) {
memcpy(peer_hostname, payload + CRYPTO_PUBLIC_KEY_BYTES, hostname_len);
peer_hostname[hostname_len] = '\0'; /* Ensure null termination */
size_t hostname_len = 0;
if (hdr->payload_size > CRYPTO_PUBLIC_KEY_BYTES) {
size_t max_len = hdr->payload_size - CRYPTO_PUBLIC_KEY_BYTES;
size_t i = 0;
for (; i < max_len; i++) {
if (payload[CRYPTO_PUBLIC_KEY_BYTES + i] == '\0') {
hostname_len = i;
break;
}
}
if (hostname_len > 0 && hostname_len < sizeof(peer_hostname)) {
memcpy(peer_hostname, payload + CRYPTO_PUBLIC_KEY_BYTES, hostname_len);
peer_hostname[hostname_len] = '\0'; /* Ensure null termination */
}
}

/* Optional protocol version + flags after hostname */
size_t extensions_offset = CRYPTO_PUBLIC_KEY_BYTES;
if (hdr->payload_size > CRYPTO_PUBLIC_KEY_BYTES + hostname_len) {
extensions_offset = CRYPTO_PUBLIC_KEY_BYTES + hostname_len + 1;
}
uint8_t peer_version = PROTOCOL_VERSION;
uint8_t peer_flags = 0;
if (hdr->payload_size >= extensions_offset + 2) {
peer_version = payload[extensions_offset];
peer_flags = payload[extensions_offset + 1];
}

if (peer_version < PROTOCOL_MIN_VERSION || peer_version > PROTOCOL_VERSION) {
fprintf(stderr, "WARNING: Peer protocol version %u unsupported\n", peer_version);
peer->state = PEER_DISCONNECTED;
return 0;
}

printf("✓ Received handshake from %s\n", peer_hostname[0] ? peer_hostname : "unknown");
Expand All @@ -444,6 +472,8 @@ int rootstream_net_recv(rootstream_ctx_t *ctx, int timeout_ms) {
if (peer_hostname[0]) {
snprintf(peer->hostname, sizeof(peer->hostname), "%s", peer_hostname);
}
peer->protocol_version = peer_version;
peer->protocol_flags = peer_flags;

/* Create encryption session (derive shared secret) */
if (crypto_create_session(&peer->session, ctx->keypair.secret_key,
Expand Down Expand Up @@ -748,13 +778,18 @@ int rootstream_net_handshake(rootstream_ctx_t *ctx, peer_t *peer) {
uint8_t payload[256];
memcpy(payload, ctx->keypair.public_key, CRYPTO_PUBLIC_KEY_BYTES);
strcpy((char*)(payload + CRYPTO_PUBLIC_KEY_BYTES), ctx->keypair.identity);

size_t payload_len = CRYPTO_PUBLIC_KEY_BYTES + strlen(ctx->keypair.identity) + 1;
if (payload_len + 2 <= sizeof(payload)) {
payload[payload_len] = PROTOCOL_VERSION;
payload[payload_len + 1] = PROTOCOL_FLAGS;
payload_len += 2;
}

/* Send handshake (unencrypted for initial key exchange) */
packet_header_t hdr = {0};
hdr.magic = PACKET_MAGIC;
hdr.version = 1;
hdr.version = PROTOCOL_VERSION;
hdr.type = PKT_HANDSHAKE;
hdr.payload_size = payload_len;

Expand Down
2 changes: 1 addition & 1 deletion src/packet_validate.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ int rootstream_net_validate_packet(const uint8_t *buffer, size_t len) {
return -1;
}

if (hdr->version != 1) {
if (hdr->version < PROTOCOL_MIN_VERSION || hdr->version > PROTOCOL_VERSION) {
return -1;
}

Expand Down
Loading