all repos — circe-vpn @ 4caa48014756dca502bd36be18bc81fc46f3f1ee

Circe VPN

Tore out global state into a context object passed in to the API
Juniper Beatitudes [email protected]
Sun, 19 Jul 2026 13:47:20 -0500
commit

4caa48014756dca502bd36be18bc81fc46f3f1ee

parent

6af25f7545c545d038b45921402dcdeed392c9d7

3 files changed, 180 insertions(+), 170 deletions(-)

jump to
M core/core.ccore/core.c

@@ -7,7 +7,6 @@ #include "mldsa_native.h"

#include "mlkem_native.h" #include "packet.h" #include "peer_table.h" -#include "platform.h" #include "sha2.h" #include "x25519.h" #include <stdbool.h>

@@ -37,39 +36,18 @@ } \

} \ while (false) -/* CIRCE-CORE GLOBAL STATE */ - -static struct circe_peer peer_table[CIRCE_PEER_TABLE_LEN]; - -static uint64_t peer_handshake_pending_mask; - -static uint64_t peer_table_free_mask; // 1 if free, 0 if full - -static struct circe_outgoing_packet - outgoing_packets[CIRCE_MAX_OUTGOING_PACKETS]; - -static uint64_t outgoing_packet_table_free_mask; // 1 if free, 0 if full - -static uint64_t last_tick_ms; - -static uint8_t my_identity[CIRCE_IDENTITY_LEN]; - -static uint8_t my_mldsa_privkey[MLDSA_SECRETKEYBYTES (44)]; +/* GENERAL HELPER FUNCTIONS */ #if defined(__GNUC__) || defined(__clang__) -#define CIRCE_FIRST_FREE_OUTGOING_PACKET_ENTRY() \ - __builtin_ffsll (outgoing_packet_table_free_mask) -#define CIRCE_FIRST_TAKEN_OUTGOING_PACKET_ENTRY() \ - __builtin_ffsll (~outgoing_packet_table_free_mask) -#define CIRCE_FIRST_FREE_PEER_TABLE_ENTRY() \ - __builtin_ffsll (peer_table_free_mask) +#define CIRCE_FIRST_FREE_OUTGOING_PACKET_ENTRY(ctx) \ + __builtin_ffsll ((ctx)->outgoing_packet_table_free_mask) +#define CIRCE_FIRST_TAKEN_OUTGOING_PACKET_ENTRY(ctx) \ + __builtin_ffsll (~((ctx)->outgoing_packet_table_free_mask)) +#define CIRCE_FIRST_FREE_PEER_TABLE_ENTRY(ctx) \ + __builtin_ffsll ((ctx)->peer_table_free_mask) #else #error Circe can currently only be built with GCC or Clang. #endif - -/* END CIRCE-CORE GLOBAL STATE */ - -/* GENERAL HELPER FUNCTIONS */ static bool locations_equal (const struct circe_location a, const struct circe_location b)

@@ -97,7 +75,8 @@ return false;

} static enum circe_result -peer_table_lookup (const uint8_t identity[CIRCE_IDENTITY_LEN], +peer_table_lookup (struct circe_context *ctx, + const uint8_t identity[CIRCE_IDENTITY_LEN], struct circe_peer **const out) { size_t start_idx = identity[0] % CIRCE_PEER_TABLE_LEN;

@@ -105,25 +84,26 @@ for (size_t i = 0; i < CIRCE_PEER_TABLE_LEN; ++i)

{ if (circe_memory_equal ( identity, - peer_table[(i + start_idx) % CIRCE_PEER_TABLE_LEN].identity, + ctx->peer_table[(i + start_idx) % CIRCE_PEER_TABLE_LEN].identity, CIRCE_IDENTITY_LEN)) { - *out = &(peer_table[(i + start_idx) % CIRCE_PEER_TABLE_LEN]); + *out = &(ctx->peer_table[(i + start_idx) % CIRCE_PEER_TABLE_LEN]); return CIRCE_RESULT_SUCCESS; } } - size_t out_idx = CIRCE_FIRST_FREE_PEER_TABLE_ENTRY (); + size_t out_idx = CIRCE_FIRST_FREE_PEER_TABLE_ENTRY (ctx); if (out_idx == 0) { return CIRCE_RESULT_PEER_TABLE_FULL; } - if (!circe_platform_recall_peer (identity, &peer_table[out_idx - 1])) + if (!ctx->recall_peer_callback (ctx->ctx_handle, identity, + &ctx->peer_table[out_idx - 1])) { return CIRCE_RESULT_UNKNOWN_REMOTE; } - peer_table_free_mask &= ~(1ull << (out_idx - 1)); - peer_handshake_pending_mask |= (1ull << (out_idx - 1)); - *out = &(peer_table[out_idx - 1]); + ctx->peer_table_free_mask &= ~(1ull << (out_idx - 1)); + ctx->peer_handshake_pending_mask |= (1ull << (out_idx - 1)); + *out = &(ctx->peer_table[out_idx - 1]); (*out)->next_handshake_stage = CIRCE_HANDSHAKE_STAGE_OPEN; return CIRCE_RESULT_HANDSHAKE_PENDING; }

@@ -190,7 +170,8 @@ return CIRCE_RESULT_SUCCESS;

} static void -close_without_saying_goodbye (struct circe_peer *remote_pt_entry) +close_without_saying_goodbye (struct circe_context *ctx, + struct circe_peer *remote_pt_entry) { size_t start_idx = remote_pt_entry->identity[0] % CIRCE_PEER_TABLE_LEN; size_t peer_idx = CIRCE_PEER_TABLE_LEN;

@@ -198,7 +179,7 @@ for (size_t i = 0; i < CIRCE_PEER_TABLE_LEN; ++i)

{ if (circe_memory_equal ( remote_pt_entry->identity, - peer_table[(i + start_idx) % CIRCE_PEER_TABLE_LEN].identity, + ctx->peer_table[(i + start_idx) % CIRCE_PEER_TABLE_LEN].identity, CIRCE_IDENTITY_LEN)) { peer_idx = start_idx;

@@ -207,12 +188,12 @@ }

} if (peer_idx == CIRCE_PEER_TABLE_LEN) { - // What? + // Double-free, perhaps; just ignore it return; } - peer_handshake_pending_mask &= ~(1ull << peer_idx); - peer_table_free_mask |= 1ull << peer_idx; - circe_memory_set ((uint8_t *)&peer_table[peer_idx], 0x0, + ctx->peer_handshake_pending_mask &= ~(1ull << peer_idx); + ctx->peer_table_free_mask |= 1ull << peer_idx; + circe_memory_set ((uint8_t *)&(ctx->peer_table[peer_idx]), 0x0, sizeof (struct circe_peer)); }

@@ -221,7 +202,7 @@

/* HANDSHAKE PROCESSING FUNCTIONS */ static enum circe_result -process_opentunnel (struct circe_cbor_decoder *cd, +process_opentunnel (struct circe_context *ctx, struct circe_cbor_decoder *cd, struct circe_location remote, struct circe_peer *remote_pt_entry) {

@@ -274,41 +255,42 @@ CIRCE_RESULT_INVALID_PACKET);

CIRCE_CORE_ASSERT (mlkem_pubkey.buf_len == MLKEM_PUBLICKEYBYTES (768), CIRCE_RESULT_INVALID_PACKET); - size_t ack1_idx = CIRCE_FIRST_FREE_OUTGOING_PACKET_ENTRY (); - outgoing_packet_table_free_mask &= ~(1 << (ack1_idx - 1)); + size_t ack1_idx = CIRCE_FIRST_FREE_OUTGOING_PACKET_ENTRY (ctx); + ctx->outgoing_packet_table_free_mask &= ~(1 << (ack1_idx - 1)); if (ack1_idx == 0) { circe_memory_set (mlkem_pubkey.buf_start, 0x0, MLKEM_PUBLICKEYBYTES (768)); circe_memory_set (remote_ecdh_frag, 0x0, 32); - outgoing_packet_table_free_mask |= (1 << (ack1_idx - 1)); + ctx->outgoing_packet_table_free_mask |= (1 << (ack1_idx - 1)); return CIRCE_RESULT_PACKET_TABLE_FULL; } - size_t ack2_idx = CIRCE_FIRST_FREE_OUTGOING_PACKET_ENTRY (); - outgoing_packet_table_free_mask &= ~(1 << (ack2_idx - 1)); + size_t ack2_idx = CIRCE_FIRST_FREE_OUTGOING_PACKET_ENTRY (ctx); + ctx->outgoing_packet_table_free_mask &= ~(1 << (ack2_idx - 1)); if (ack2_idx == 0) { circe_memory_set (mlkem_pubkey.buf_start, 0x0, MLKEM_PUBLICKEYBYTES (768)); circe_memory_set (remote_ecdh_frag, 0x0, 32); - outgoing_packet_table_free_mask |= (1 << (ack1_idx - 1)); - outgoing_packet_table_free_mask |= (1 << (ack2_idx - 1)); + ctx->outgoing_packet_table_free_mask |= (1 << (ack1_idx - 1)); + ctx->outgoing_packet_table_free_mask |= (1 << (ack2_idx - 1)); return CIRCE_RESULT_PACKET_TABLE_FULL; } - size_t ack3_idx = CIRCE_FIRST_FREE_OUTGOING_PACKET_ENTRY (); - outgoing_packet_table_free_mask &= ~(1 << (ack3_idx - 1)); + size_t ack3_idx = CIRCE_FIRST_FREE_OUTGOING_PACKET_ENTRY (ctx); + ctx->outgoing_packet_table_free_mask &= ~(1 << (ack3_idx - 1)); if (ack3_idx == 0) { circe_memory_set (mlkem_pubkey.buf_start, 0x0, MLKEM_PUBLICKEYBYTES (768)); circe_memory_set (remote_ecdh_frag, 0x0, 32); - outgoing_packet_table_free_mask |= (1 << (ack1_idx - 1)); - outgoing_packet_table_free_mask |= (1 << (ack2_idx - 1)); - outgoing_packet_table_free_mask |= (1 << (ack3_idx - 1)); + ctx->outgoing_packet_table_free_mask |= (1 << (ack1_idx - 1)); + ctx->outgoing_packet_table_free_mask |= (1 << (ack2_idx - 1)); + ctx->outgoing_packet_table_free_mask |= (1 << (ack3_idx - 1)); return CIRCE_RESULT_PACKET_TABLE_FULL; } uint8_t ecdh_privkey[32]; - circe_platform_get_random_bytes (ecdh_privkey, sizeof (ecdh_privkey)); + ctx->random_bytes_callback (ctx->ctx_handle, ecdh_privkey, + sizeof (ecdh_privkey)); const uint8_t u[32] = { 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,

@@ -316,12 +298,12 @@ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };

uint8_t my_ecdh_frag[32]; tct_x25519 (ecdh_privkey, u, my_ecdh_frag); - outgoing_packets[ack1_idx - 1].destination = remote; - outgoing_packets[ack2_idx - 1].destination = remote; - outgoing_packets[ack3_idx - 1].destination = remote; + ctx->outgoing_packets[ack1_idx - 1].destination = remote; + ctx->outgoing_packets[ack2_idx - 1].destination = remote; + ctx->outgoing_packets[ack3_idx - 1].destination = remote; struct circe_cbor_encoder ec = { - .buf_start = outgoing_packets[ack1_idx - 1].buffer, - .cursor = outgoing_packets[ack1_idx - 1].buffer, + .buf_start = ctx->outgoing_packets[ack1_idx - 1].buffer, + .cursor = ctx->outgoing_packets[ack1_idx - 1].buffer, .buf_len = CIRCE_MAX_UDP_PAYLOAD, }; enum circe_result err = CIRCE_RESULT_SUCCESS;

@@ -343,7 +325,7 @@ circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_IDENTITY),

CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); CIRCE_CORE_ASSERT_WITH_CLEANUP ( circe_cbor_emit_bytestr ( - &ec, (struct circe_cbor_buffer){ .buf_start = my_identity, + &ec, (struct circe_cbor_buffer){ .buf_start = ctx->my_identity, .buf_len = CIRCE_IDENTITY_LEN }), CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup);

@@ -376,7 +358,7 @@ circe_cbor_emit_bytestr (

&ec, (struct circe_cbor_buffer){ .buf_start = encapsulated, .buf_len = sizeof (encapsulated) }), CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - outgoing_packets[ack1_idx - 1].length = ec.cursor - ec.buf_start; + ctx->outgoing_packets[ack1_idx - 1].length = ec.cursor - ec.buf_start; uint8_t ek_shared[32]; tct_x25519 (ecdh_privkey, remote_ecdh_frag, ek_shared);

@@ -402,20 +384,20 @@ tct_turboshake128_squeeze_destructive (prk_to_expand, k_hs_e,

sizeof (k_hs_e)); uint8_t payload_clear[CIRCE_IDENTITY_LEN + MLDSA_BYTES (44)]; - circe_memory_copy (my_identity, payload_clear, CIRCE_IDENTITY_LEN); + circe_memory_copy (ctx->my_identity, payload_clear, CIRCE_IDENTITY_LEN); size_t _; CIRCE_CORE_ASSERT_WITH_CLEANUP ( crypto_sign_signature (payload_clear + CIRCE_IDENTITY_LEN, &_, payload_clear, CIRCE_IDENTITY_LEN, NULL, 0, - my_mldsa_privkey) + ctx->my_mldsa_privkey) == 0, CIRCE_RESULT_SIGNATURE_FAILED, err, process_opentunnel_cleanup); - uint8_t encrypted_buf[sizeof (payload_clear) + 16]; + uint8_t encrypted_buf[sizeof (payload_clear) + CIRCE_P1305_MAC_LEN]; uint8_t nonce[sizeof (uint64_t)] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; - ec.buf_start = outgoing_packets[ack2_idx - 1].buffer; - ec.cursor = outgoing_packets[ack2_idx - 1].buffer; + ec.buf_start = ctx->outgoing_packets[ack2_idx - 1].buffer; + ec.cursor = ctx->outgoing_packets[ack2_idx - 1].buffer; // buf_len stays the same, which is good because it's const // Version, identity, type, ID payload 1 == 4 entries

@@ -435,7 +417,7 @@ circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_IDENTITY),

CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); CIRCE_CORE_ASSERT_WITH_CLEANUP ( circe_cbor_emit_bytestr ( - &ec, (struct circe_cbor_buffer){ .buf_start = my_identity, + &ec, (struct circe_cbor_buffer){ .buf_start = ctx->my_identity, .buf_len = CIRCE_IDENTITY_LEN }), CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup);

@@ -459,10 +441,10 @@ &ec,

(struct circe_cbor_buffer){ .buf_start = encrypted_buf, .buf_len = sizeof (encrypted_buf) }), CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - outgoing_packets[ack2_idx - 1].length = ec.cursor - ec.buf_start; + ctx->outgoing_packets[ack2_idx - 1].length = ec.cursor - ec.buf_start; - ec.buf_start = outgoing_packets[ack3_idx - 1].buffer; - ec.cursor = outgoing_packets[ack3_idx - 1].buffer; + ec.buf_start = ctx->outgoing_packets[ack3_idx - 1].buffer; + ec.cursor = ctx->outgoing_packets[ack3_idx - 1].buffer; // buf_len once again stays the same // Version, identity, type, ID payload 1 == 4 entries

@@ -482,7 +464,7 @@ circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_IDENTITY),

CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); CIRCE_CORE_ASSERT_WITH_CLEANUP ( circe_cbor_emit_bytestr ( - &ec, (struct circe_cbor_buffer){ .buf_start = my_identity, + &ec, (struct circe_cbor_buffer){ .buf_start = ctx->my_identity, .buf_len = CIRCE_IDENTITY_LEN }), CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup);

@@ -507,19 +489,19 @@ &ec,

(struct circe_cbor_buffer){ .buf_start = encrypted_buf, .buf_len = sizeof (encrypted_buf) }), CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - outgoing_packets[ack3_idx - 1].length = ec.cursor - ec.buf_start; + ctx->outgoing_packets[ack3_idx - 1].length = ec.cursor - ec.buf_start; process_opentunnel_cleanup: if (err != CIRCE_RESULT_SUCCESS) { - outgoing_packet_table_free_mask |= (1 << (ack1_idx - 1)); - outgoing_packet_table_free_mask |= (1 << (ack2_idx - 1)); - outgoing_packet_table_free_mask |= (1 << (ack3_idx - 1)); - circe_memory_set ((uint8_t *)&outgoing_packets[ack1_idx - 1], 0x0, + ctx->outgoing_packet_table_free_mask |= (1 << (ack1_idx - 1)); + ctx->outgoing_packet_table_free_mask |= (1 << (ack2_idx - 1)); + ctx->outgoing_packet_table_free_mask |= (1 << (ack3_idx - 1)); + circe_memory_set ((uint8_t *)&ctx->outgoing_packets[ack1_idx - 1], 0x0, sizeof (struct circe_outgoing_packet)); - circe_memory_set ((uint8_t *)&outgoing_packets[ack2_idx - 1], 0x0, + circe_memory_set ((uint8_t *)&ctx->outgoing_packets[ack2_idx - 1], 0x0, sizeof (struct circe_outgoing_packet)); - circe_memory_set ((uint8_t *)&outgoing_packets[ack3_idx - 1], 0x0, + circe_memory_set ((uint8_t *)&ctx->outgoing_packets[ack3_idx - 1], 0x0, sizeof (struct circe_outgoing_packet)); } circe_memory_set (ecdh_privkey, 0x0, 32);

@@ -533,24 +515,25 @@ return err;

} static enum circe_result -dispatch_identity_packets (struct circe_location remote, +dispatch_identity_packets (struct circe_context *ctx, + struct circe_location remote, struct circe_peer *remote_pt_entry, uint8_t th3[64]) { enum circe_result err = CIRCE_RESULT_SUCCESS; - size_t id1_idx = CIRCE_FIRST_FREE_OUTGOING_PACKET_ENTRY (); - outgoing_packet_table_free_mask &= ~(1 << (id1_idx - 1)); + size_t id1_idx = CIRCE_FIRST_FREE_OUTGOING_PACKET_ENTRY (ctx); + ctx->outgoing_packet_table_free_mask &= ~(1 << (id1_idx - 1)); if (id1_idx == 0) { - outgoing_packet_table_free_mask |= (1 << (id1_idx - 1)); + ctx->outgoing_packet_table_free_mask |= (1 << (id1_idx - 1)); return CIRCE_RESULT_PACKET_TABLE_FULL; } - size_t id2_idx = CIRCE_FIRST_FREE_OUTGOING_PACKET_ENTRY (); - outgoing_packet_table_free_mask &= ~(1 << (id2_idx - 1)); + size_t id2_idx = CIRCE_FIRST_FREE_OUTGOING_PACKET_ENTRY (ctx); + ctx->outgoing_packet_table_free_mask &= ~(1 << (id2_idx - 1)); if (id2_idx == 0) { - outgoing_packet_table_free_mask |= (1 << (id1_idx - 1)); - outgoing_packet_table_free_mask |= (1 << (id2_idx - 1)); + ctx->outgoing_packet_table_free_mask |= (1 << (id1_idx - 1)); + ctx->outgoing_packet_table_free_mask |= (1 << (id2_idx - 1)); return CIRCE_RESULT_PACKET_TABLE_FULL; }

@@ -570,18 +553,19 @@ struct circe_cbor_encoder ec = {

.buf_len = CIRCE_MAX_UDP_PAYLOAD, }; uint8_t id_payload[CIRCE_IDENTITY_LEN + MLDSA_BYTES (44)]; - circe_memory_copy (my_identity, id_payload, CIRCE_IDENTITY_LEN); + circe_memory_copy (ctx->my_identity, id_payload, CIRCE_IDENTITY_LEN); size_t _; CIRCE_CORE_ASSERT_WITH_CLEANUP ( crypto_sign_signature (id_payload + CIRCE_IDENTITY_LEN, &_, id_payload, - CIRCE_IDENTITY_LEN, NULL, 0x0, my_mldsa_privkey) + CIRCE_IDENTITY_LEN, NULL, 0x0, + ctx->my_mldsa_privkey) == 0, CIRCE_RESULT_SIGNATURE_FAILED, err, dispatch_identity_packets_cleanup); for (size_t i = 0; i < 2; ++i) { - ec.buf_start = ec.cursor = (i == 0) - ? outgoing_packets[id1_idx - 1].buffer - : outgoing_packets[id2_idx - 1].buffer; + ec.buf_start = ec.cursor + = (i == 0) ? ctx->outgoing_packets[id1_idx - 1].buffer + : ctx->outgoing_packets[id2_idx - 1].buffer; // Version, identity, type, ID payload 1/2 == 4 entries CIRCE_CORE_ASSERT_WITH_CLEANUP (circe_cbor_emit_mapping_header (&ec, 4), CIRCE_RESULT_INSUFFICIENT_MEM, err,

@@ -600,8 +584,9 @@ CIRCE_RESULT_INSUFFICIENT_MEM, err,

dispatch_identity_packets_cleanup); CIRCE_CORE_ASSERT_WITH_CLEANUP ( circe_cbor_emit_bytestr ( - &ec, (struct circe_cbor_buffer){ .buf_len = CIRCE_IDENTITY_LEN, - .buf_start = my_identity }), + &ec, + (struct circe_cbor_buffer){ .buf_len = CIRCE_IDENTITY_LEN, + .buf_start = ctx->my_identity }), CIRCE_RESULT_INSUFFICIENT_MEM, err, dispatch_identity_packets_cleanup); CIRCE_CORE_ASSERT_WITH_CLEANUP (

@@ -621,7 +606,8 @@ : CIRCE_PACKET_FIELD_ID_PAYLOAD2),

CIRCE_RESULT_INSUFFICIENT_MEM, err, dispatch_identity_packets_cleanup); struct circe_cbor_buffer out_buf - = { .buf_len = (CIRCE_IDENTITY_LEN + MLDSA_BYTES (44)) / 2 + 16, + = { .buf_len = (CIRCE_IDENTITY_LEN + MLDSA_BYTES (44)) / 2 + + CIRCE_P1305_MAC_LEN, .buf_start = remote_pt_entry->id_payload_halves[i] }; uint8_t nonce[sizeof (uint64_t)] = { i, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };

@@ -633,19 +619,20 @@ out_buf.buf_start + (CIRCE_IDENTITY_LEN + MLDSA_BYTES (44)) / 2);

CIRCE_CORE_ASSERT_WITH_CLEANUP (circe_cbor_emit_bytestr (&ec, out_buf), CIRCE_RESULT_INSUFFICIENT_MEM, err, dispatch_identity_packets_cleanup); - outgoing_packets[(i == 0) ? (id1_idx - 1) : (id2_idx - 1)].destination + ctx->outgoing_packets[(i == 0) ? (id1_idx - 1) : (id2_idx - 1)] + .destination = remote; - outgoing_packets[(i == 0) ? (id1_idx - 1) : (id2_idx - 1)].length + ctx->outgoing_packets[(i == 0) ? (id1_idx - 1) : (id2_idx - 1)].length = ec.cursor - ec.buf_start; } dispatch_identity_packets_cleanup: if (err != CIRCE_RESULT_SUCCESS) { - outgoing_packet_table_free_mask |= (1 << (id1_idx - 1)); - outgoing_packet_table_free_mask |= (1 << (id2_idx - 1)); - circe_memory_set ((uint8_t *)&outgoing_packets[id1_idx - 1], 0x0, + ctx->outgoing_packet_table_free_mask |= (1 << (id1_idx - 1)); + ctx->outgoing_packet_table_free_mask |= (1 << (id2_idx - 1)); + circe_memory_set ((uint8_t *)&ctx->outgoing_packets[id1_idx - 1], 0x0, sizeof (struct circe_outgoing_packet)); - circe_memory_set ((uint8_t *)&outgoing_packets[id2_idx - 1], 0x0, + circe_memory_set ((uint8_t *)&ctx->outgoing_packets[id2_idx - 1], 0x0, sizeof (struct circe_outgoing_packet)); } circe_memory_set (id_payload, 0x0, sizeof (id_payload));

@@ -653,7 +640,8 @@ return err;

} static enum circe_result -process_openack (struct circe_cbor_decoder *cd, struct circe_location remote, +process_openack (struct circe_context *ctx, struct circe_cbor_decoder *cd, + struct circe_location remote, struct circe_peer *remote_pt_entry, size_t stage) { enum circe_result err = CIRCE_RESULT_SUCCESS;

@@ -757,7 +745,9 @@ CIRCE_CORE_ASSERT_WITH_CLEANUP (circe_cbor_extract_bytestr (cd, &buf),

CIRCE_RESULT_INVALID_PACKET, err, process_openack_cleanup); CIRCE_CORE_ASSERT_WITH_CLEANUP ( - buf.buf_len == (CIRCE_IDENTITY_LEN + MLDSA_BYTES (44)) / 2 + 16, + buf.buf_len + == (CIRCE_IDENTITY_LEN + MLDSA_BYTES (44)) / 2 + + CIRCE_P1305_MAC_LEN, CIRCE_RESULT_INVALID_PACKET, err, process_openack_cleanup); circe_memory_copy (buf.buf_start, remote_pt_entry->id_payload_halves[stage - 1],

@@ -824,7 +814,7 @@ }

circe_memory_set (id_payload, 0x0, sizeof (id_payload)); uint8_t th3[64]; tct_sha512 (id_payload, sizeof (id_payload), th3); - return dispatch_identity_packets (remote, remote_pt_entry, th3); + return dispatch_identity_packets (ctx, remote, remote_pt_entry, th3); } } return err;

@@ -847,25 +837,33 @@

/* PUBLIC API */ enum circe_result -circe_initialize_core (uint8_t identity[CIRCE_IDENTITY_LEN], - uint8_t mldsa_privkey[MLDSA_SECRETKEYBYTES (44)], - uint64_t now_ms) +circe_initialize_core ( + struct circe_context *ctx, uint8_t identity[CIRCE_IDENTITY_LEN], + uint8_t mldsa_privkey[MLDSA_SECRETKEYBYTES (44)], uint64_t now_ms, + void *ctx_handle, + void (*random_bytes_callback) (void *, uint8_t *, uint32_t), + bool (*recall_peer_callback) (void *, const uint8_t[CIRCE_IDENTITY_LEN], + struct circe_peer *)) { - last_tick_ms = now_ms; - circe_memory_copy (identity, my_identity, CIRCE_IDENTITY_LEN); - circe_memory_copy (mldsa_privkey, my_mldsa_privkey, + ctx->last_tick_ms = now_ms; + circe_memory_copy (identity, ctx->my_identity, CIRCE_IDENTITY_LEN); + circe_memory_copy (mldsa_privkey, ctx->my_mldsa_privkey, MLDSA_SECRETKEYBYTES (44)); - peer_handshake_pending_mask = 0x0ull; - peer_table_free_mask = ~0x0ull; - outgoing_packet_table_free_mask = ~0x0ull; - circe_memory_set ((uint8_t *)outgoing_packets, 0x0, - sizeof (outgoing_packets)); - circe_memory_set ((uint8_t *)peer_table, 0x0, sizeof (peer_table)); + ctx->peer_handshake_pending_mask = 0x0ull; + ctx->peer_table_free_mask = ~0x0ull; + ctx->outgoing_packet_table_free_mask = ~0x0ull; + circe_memory_set ((uint8_t *)ctx->outgoing_packets, 0x0, + sizeof (ctx->outgoing_packets)); + circe_memory_set ((uint8_t *)ctx->peer_table, 0x0, sizeof (ctx->peer_table)); + ctx->ctx_handle = ctx_handle; + ctx->random_bytes_callback = random_bytes_callback; + ctx->recall_peer_callback = recall_peer_callback; return CIRCE_RESULT_SUCCESS; } enum circe_result -circe_handle_packet (const struct circe_location remote, +circe_handle_packet (struct circe_context *ctx, + const struct circe_location remote, const uint8_t *restrict in, size_t len, uint8_t *restrict out, size_t *out_len, uint64_t now_ms, struct circe_event *events, size_t *n_events)

@@ -926,7 +924,7 @@ CIRCE_CORE_ASSERT (remote_id_buf.buf_len == CIRCE_IDENTITY_LEN,

CIRCE_RESULT_INVALID_PACKET); struct circe_peer *remote_pt_entry; enum circe_result peer_in_ram - = peer_table_lookup (remote_id_buf.buf_start, &remote_pt_entry); + = peer_table_lookup (ctx, remote_id_buf.buf_start, &remote_pt_entry); if (peer_in_ram == CIRCE_RESULT_UNKNOWN_REMOTE) { return CIRCE_RESULT_UNKNOWN_REMOTE;

@@ -999,10 +997,10 @@ }

else if (peer_in_ram == CIRCE_RESULT_HANDSHAKE_PENDING) { // This is the expected path - err = process_opentunnel (&cd, remote, remote_pt_entry); + err = process_opentunnel (ctx, &cd, remote, remote_pt_entry); if (err != CIRCE_RESULT_SUCCESS) { - close_without_saying_goodbye (remote_pt_entry); + close_without_saying_goodbye (ctx, remote_pt_entry); events[*n_events] = (struct circe_event){ .event_type = CIRCE_EVENT_REJECTED_REMOTE, };

@@ -1026,11 +1024,11 @@ }

case CIRCE_PACKET_ACKOPEN1: case CIRCE_PACKET_ACKOPEN2: case CIRCE_PACKET_ACKOPEN3: - err = process_openack (&cd, remote, remote_pt_entry, + err = process_openack (ctx, &cd, remote, remote_pt_entry, (packet_type - CIRCE_PACKET_ACKOPEN1) + 1); if (err != CIRCE_RESULT_SUCCESS) { - close_without_saying_goodbye (remote_pt_entry); + close_without_saying_goodbye (ctx, remote_pt_entry); events[*n_events] = (struct circe_event){ .event_type = CIRCE_EVENT_REJECTED_REMOTE, };

@@ -1055,7 +1053,8 @@ }

} enum circe_result -circe_send_outgoing_packet (const uint8_t remote_identity[CIRCE_IDENTITY_LEN], +circe_send_outgoing_packet (struct circe_context *ctx, + const uint8_t remote_identity[CIRCE_IDENTITY_LEN], const uint8_t *in, size_t len, uint64_t now_ms, struct circe_event *events, size_t *n_events) {

@@ -1065,7 +1064,7 @@ return CIRCE_RESULT_OVER_MTU;

} struct circe_peer *remote_pt_entry; enum circe_result peer_in_ram - = peer_table_lookup (remote_identity, &remote_pt_entry); + = peer_table_lookup (ctx, remote_identity, &remote_pt_entry); if (peer_in_ram != CIRCE_RESULT_SUCCESS) { // Might return if e.g. unknown peer or if we need to finish the

@@ -1073,18 +1072,18 @@ // handshake

return peer_in_ram; } - size_t next_entry_idx = CIRCE_FIRST_FREE_OUTGOING_PACKET_ENTRY (); + size_t next_entry_idx = CIRCE_FIRST_FREE_OUTGOING_PACKET_ENTRY (ctx); if (next_entry_idx == 0) { // No free entries return CIRCE_RESULT_PACKET_TABLE_FULL; } - outgoing_packet_table_free_mask &= ~(1ull << (next_entry_idx - 1)); + ctx->outgoing_packet_table_free_mask &= ~(1ull << (next_entry_idx - 1)); struct circe_cbor_encoder ec = { - .buf_start = outgoing_packets[next_entry_idx].buffer, - .buf_len = sizeof (outgoing_packets[next_entry_idx].buffer), - .cursor = outgoing_packets[next_entry_idx].buffer, + .buf_start = ctx->outgoing_packets[next_entry_idx].buffer, + .buf_len = sizeof (ctx->outgoing_packets[next_entry_idx].buffer), + .cursor = ctx->outgoing_packets[next_entry_idx].buffer, }; remote_pt_entry->last_seqn_tx++;

@@ -1109,7 +1108,7 @@ CIRCE_RESULT_INSUFFICIENT_MEM, err, circe_send_outgoing_packet_cleanup);

CIRCE_CORE_ASSERT_WITH_CLEANUP ( circe_cbor_emit_bytestr ( &ec, (struct circe_cbor_buffer){ .buf_len = CIRCE_IDENTITY_LEN, - .buf_start = my_identity }), + .buf_start = ctx->my_identity }), CIRCE_RESULT_INSUFFICIENT_MEM, err, circe_send_outgoing_packet_cleanup); CIRCE_CORE_ASSERT_WITH_CLEANUP (

@@ -1150,61 +1149,61 @@ struct circe_peer *const relay_server_pt_entry = NULL;

switch (remote_pt_entry->current_route) { case CIRCE_ROUTE_LAN0: - outgoing_packets[next_entry_idx - 1].destination + ctx->outgoing_packets[next_entry_idx - 1].destination = remote_pt_entry->lan0_loc; break; case CIRCE_ROUTE_LAN1: - outgoing_packets[next_entry_idx - 1].destination + ctx->outgoing_packets[next_entry_idx - 1].destination = remote_pt_entry->lan1_loc; break; case CIRCE_ROUTE_WAN: - outgoing_packets[next_entry_idx - 1].destination + ctx->outgoing_packets[next_entry_idx - 1].destination = remote_pt_entry->wan_loc; break; case CIRCE_ROUTE_RELAY: relay_server_in_ram = peer_table_lookup ( - remote_pt_entry->relay_server_identity, + ctx, remote_pt_entry->relay_server_identity, (struct circe_peer * *const)&relay_server_pt_entry); CIRCE_CORE_ASSERT_WITH_CLEANUP (relay_server_in_ram == CIRCE_RESULT_SUCCESS, CIRCE_RESULT_RELAY_LOOKUP_FAILED, err, circe_send_outgoing_packet_cleanup); - outgoing_packets[next_entry_idx - 1].destination + ctx->outgoing_packets[next_entry_idx - 1].destination = relay_server_pt_entry->wan_loc; break; } - outgoing_packets[next_entry_idx - 1].length = ec.cursor - ec.buf_start; + ctx->outgoing_packets[next_entry_idx - 1].length = ec.cursor - ec.buf_start; circe_send_outgoing_packet_cleanup: if (err != CIRCE_RESULT_SUCCESS) { remote_pt_entry->last_seqn_tx--; - outgoing_packet_table_free_mask |= (1ull << (next_entry_idx - 1)); + ctx->outgoing_packet_table_free_mask |= (1ull << (next_entry_idx - 1)); } return err; } enum circe_result -circe_tick (uint64_t now_ms, struct circe_outgoing_packet *out, - bool *new_packet_out) +circe_tick (struct circe_context *ctx, uint64_t now_ms, + struct circe_outgoing_packet *out, bool *new_packet_out) { - if (now_ms < last_tick_ms) + if (now_ms < ctx->last_tick_ms) { return CIRCE_RESULT_INVALID_TIMESTAMP; } - last_tick_ms = now_ms; - size_t packet_idx = CIRCE_FIRST_TAKEN_OUTGOING_PACKET_ENTRY (); + ctx->last_tick_ms = now_ms; + size_t packet_idx = CIRCE_FIRST_TAKEN_OUTGOING_PACKET_ENTRY (ctx); if (packet_idx == 0) { *new_packet_out = false; return CIRCE_RESULT_SUCCESS; } *new_packet_out = true; - *out = outgoing_packets[packet_idx - 1]; - circe_memory_set ((uint8_t *)(&outgoing_packets[packet_idx - 1]), 0x0, + *out = ctx->outgoing_packets[packet_idx - 1]; + circe_memory_set ((uint8_t *)(&ctx->outgoing_packets[packet_idx - 1]), 0x0, CIRCE_MAX_UDP_PAYLOAD); - outgoing_packet_table_free_mask |= (1ull << (packet_idx - 1)); + ctx->outgoing_packet_table_free_mask |= (1ull << (packet_idx - 1)); return CIRCE_RESULT_SUCCESS; }
M core/core.hcore/core.h

@@ -8,7 +8,7 @@ #include <stddef.h>

#include <stdint.h> #define CIRCE_MAX_OUTGOING_PACKETS 64 -#define CIRCE_HEARTBEAT_PERIOD_MS 100 +#define CIRCE_HEARTBEAT_PERIOD_MS 1000 enum circe_result {

@@ -53,14 +53,36 @@ CIRCE_DEADLINE_INDEFINITE,

CIRCE_DEADLINE_AS_INDICATED, }; -/// Initialize Circe core's global state -enum circe_result -circe_initialize_core (uint8_t identity[CIRCE_IDENTITY_LEN], - uint8_t mldsa_privkey[MLDSA_SECRETKEYBYTES (44)], - uint64_t now_ms); +struct circe_context +{ + struct circe_peer peer_table[CIRCE_PEER_TABLE_LEN]; + uint64_t peer_handshake_pending_mask; + uint64_t peer_table_free_mask; // 1 if free, 0 if full + struct circe_outgoing_packet outgoing_packets[CIRCE_MAX_OUTGOING_PACKETS]; + uint64_t outgoing_packet_table_free_mask; // 1 if free, 0 if full + uint64_t last_tick_ms; + uint8_t my_identity[CIRCE_IDENTITY_LEN]; + uint8_t my_mldsa_privkey[MLDSA_SECRETKEYBYTES (44)]; + + void *ctx_handle; + void (*random_bytes_callback) (void *ctx_handle, uint8_t *out, uint32_t len); + bool (*recall_peer_callback) (void *ctx_handle, + const uint8_t identity[CIRCE_IDENTITY_LEN], + struct circe_peer *out); +}; + +/// Initialize Circe core's context state +enum circe_result circe_initialize_core ( + struct circe_context *ctx, uint8_t identity[CIRCE_IDENTITY_LEN], + uint8_t mldsa_privkey[MLDSA_SECRETKEYBYTES (44)], uint64_t now_ms, + void *ctx_handle, + void (*random_bytes_callback) (void *, uint8_t *, uint32_t), + bool (*recall_peer_callback) (void *, const uint8_t[CIRCE_IDENTITY_LEN], + struct circe_peer *)); /// Process a new incoming raw Circe packet. -enum circe_result circe_handle_packet (const struct circe_location remote, +enum circe_result circe_handle_packet (struct circe_context *ctx, + const struct circe_location remote, const uint8_t *restrict in, size_t len, uint8_t *restrict out, size_t *out_len, uint64_t now_ms,

@@ -70,7 +92,8 @@

/// Put outgoing packet into Circe's internal logic. DOES NOT ACTUALLY SEND IT /// OVER THE NETWORK; that is handled opaquely by the caller and `circe_tick`. enum circe_result -circe_send_outgoing_packet (const uint8_t remote_identity[CIRCE_IDENTITY_LEN], +circe_send_outgoing_packet (struct circe_context *ctx, + const uint8_t remote_identity[CIRCE_IDENTITY_LEN], const uint8_t *in, size_t len, uint64_t now_ms, struct circe_event *events, size_t *n_events);

@@ -78,13 +101,14 @@ /// Get deadline and corresponding millisecond timestamp (for

/// `CIRCE_DEADLINE_AS_INDICATED`) for next timer deadline, at which point /// `circe_tick` should be called. Should always be called immediately after /// any other core API function is called to stay up-to-date. -enum circe_deadline circe_next_deadline (uint64_t *ms_timestamp); +enum circe_deadline circe_next_deadline (struct circe_context *ctx, + uint64_t *ms_timestamp); /// Iterate the Circe core logic. Assumes `now_ms` increases monotonically with /// time. If `new_packet_out` is true (do not pass in NULL for `out`), then /// `out` contains a new raw Circe packet to be sent out to the location /// `out.destination`. -enum circe_result circe_tick (uint64_t now_ms, +enum circe_result circe_tick (struct circe_context *ctx, uint64_t now_ms, struct circe_outgoing_packet *out, bool *new_packet_out);
D core/platform.h

@@ -1,13 +0,0 @@

-#ifndef CIRCE_CORE_PLATFORM_H -#define CIRCE_CORE_PLATFORM_H - -#include "peer_table.h" -#include <stdbool.h> -#include <stdint.h> - -void circe_platform_get_random_bytes (uint8_t *out, uint32_t len); - -bool circe_platform_recall_peer (const uint8_t identity[CIRCE_IDENTITY_LEN], - struct circe_peer *out); - -#endif