all repos — circe-vpn @ 4d3404231847d860083059474562e28a12afcf61

Circe VPN

Further work on parsing, switched to Zig CC for compilation
Juniper Beatitudes [email protected]
Sat, 25 Jul 2026 12:42:22 -0500
commit

4d3404231847d860083059474562e28a12afcf61

parent

7105ecea863e8330ab95c89a34f2e96e71c90f41

M .gitmodules.gitmodules

@@ -10,3 +10,6 @@ url = https://gitea.eyes-like-fire.org/juniper/TinyCrypT.git

[submodule "subprojects/Unity"] path = subprojects/Unity url = https://github.com/ThrowTheSwitch/Unity.git +[submodule "contrib/pba"] + path = contrib/pba + url = https://gitea.eyes-like-fire.org/juniper/pba.git
M README.mdREADME.md

@@ -1,3 +1,5 @@

# circe-vpn -Tiny, fast, and paranoid VPN. Use at your own risk.+Tiny, fast, and paranoid VPN. Use at your own risk. + +![*Circe Offering The Cup To Ulysses* by John William Waterhouse, 1891](assets/Circe_Offering_the_Cup_to_Odysseus.jpg)
D common/cbor.c

@@ -1,448 +0,0 @@

-#include "cbor.h" -#include <stdint.h> -#include <stdbool.h> - -#define CIRCE_CBOR_ASSERT(x) \ - do \ - { \ - if (!(x)) \ - { \ - return false; \ - } \ - } \ - while (false) - -/* DECODING FUNCTIONS */ - -bool -circe_cbor_get_argument (struct circe_cbor_decoder *dc, uint64_t *out, bool *is_definite) -{ - CIRCE_CBOR_ASSERT (dc->cursor - dc->buf_start + 1 <= dc->buf_len); - *out = *(dc->cursor) & 0b11111; - ++dc->cursor; - switch (*out) - { - case 24: - CIRCE_CBOR_ASSERT (dc->cursor - dc->buf_start + 1 <= dc->buf_len); - *out = *dc->cursor; - ++dc->cursor; - break; - case 25: - CIRCE_CBOR_ASSERT (dc->cursor - dc->buf_start + 2 <= dc->buf_len); - *out = 0; - for (unsigned int i = 0; i < 2; ++i) - { - *out <<= 8; - *out |= dc->cursor[i]; - } - dc->cursor += 2; - break; - case 26: - CIRCE_CBOR_ASSERT (dc->cursor - dc->buf_start + 4 <= dc->buf_len); - *out = 0; - for (unsigned int i = 0; i < 4; ++i) - { - *out <<= 8; - *out |= dc->cursor[i]; - } - dc->cursor += 4; - break; - case 27: - CIRCE_CBOR_ASSERT (dc->cursor - dc->buf_start + 8 <= dc->buf_len); - *out = 0; - for (unsigned int i = 0; i < 8; ++i) - { - *out <<= 8; - *out |= dc->cursor[i]; - } - dc->cursor += 8; - break; - case 28: - case 29: - case 30: - CIRCE_CBOR_ASSERT (false); - } - *is_definite = !(*out == 31); - return true; -} - -bool -circe_cbor_get_major_type (struct circe_cbor_decoder *dc, enum circe_cbor_major_type *out) -{ - CIRCE_CBOR_ASSERT (dc->cursor - dc->buf_start < dc->buf_len); - *out = (enum circe_cbor_major_type) (*dc->cursor >> 5); - return true; -} - -bool -circe_cbor_get_simple_type (struct circe_cbor_decoder *dc, enum circe_cbor_simple_type *out) -{ - CIRCE_CBOR_ASSERT (dc->cursor - dc->buf_start < dc->buf_len); - CIRCE_CBOR_ASSERT ((*dc->cursor & 0b11111) != 24); - *out = (enum circe_cbor_simple_type) (*dc->cursor & 0b11111); - return true; -} - -bool -circe_cbor_extract_length (struct circe_cbor_decoder *dc, uint64_t *out, bool *is_definite) -{ - CIRCE_CBOR_ASSERT (circe_cbor_get_argument (dc, out, is_definite)); - return true; -} - -bool -circe_cbor_skip (struct circe_cbor_decoder *dc, uint64_t max_depth) -{ - if (max_depth == 0) - { - return false; - } - enum circe_cbor_major_type type; - CIRCE_CBOR_ASSERT (circe_cbor_get_major_type (dc, &type)); - uint64_t num_throwaway; - struct circe_cbor_buffer buf_throwaway; - uint64_t len; - bool is_definite; - enum circe_cbor_simple_type st; - switch (type) - { - case CIRCE_CBOR_MAJOR_UNSIGNED: - CIRCE_CBOR_ASSERT (circe_cbor_extract_unsigned (dc, &num_throwaway)); - break; - case CIRCE_CBOR_MAJOR_NEGATIVE: - CIRCE_CBOR_ASSERT (circe_cbor_extract_negative (dc, &num_throwaway)); - break; - case CIRCE_CBOR_MAJOR_BYTESTR: - CIRCE_CBOR_ASSERT (circe_cbor_extract_bytestr (dc, &buf_throwaway)); - break; - case CIRCE_CBOR_MAJOR_TEXTSTR: - CIRCE_CBOR_ASSERT (circe_cbor_extract_bytestr (dc, &buf_throwaway)); - break; - case CIRCE_CBOR_MAJOR_ARRAY: - CIRCE_CBOR_ASSERT (circe_cbor_extract_length (dc, &len, &is_definite)); - if (!is_definite) - { - while (true) - { - CIRCE_CBOR_ASSERT (circe_cbor_get_major_type (dc, &type)); - if (type == CIRCE_CBOR_MAJOR_SIMPLE) - { - enum circe_cbor_simple_type st; - CIRCE_CBOR_ASSERT (circe_cbor_get_simple_type (dc, &st)); - if (st == CIRCE_CBOR_SIMPLE_BREAK) - { - break; - } - } - circe_cbor_skip (dc, max_depth - 1); - } - } - else - { - for (uint64_t i = 0; i < len; ++i) - { - circe_cbor_skip (dc, max_depth - 1); - } - } - break; - case CIRCE_CBOR_MAJOR_MAPPING: - CIRCE_CBOR_ASSERT (circe_cbor_extract_length (dc, &len, &is_definite)); - if (!is_definite) - { - while (true) - { - CIRCE_CBOR_ASSERT (circe_cbor_get_major_type (dc, &type)); - if (type == CIRCE_CBOR_MAJOR_SIMPLE) - { - CIRCE_CBOR_ASSERT (circe_cbor_get_simple_type (dc, &st)); - if (st == CIRCE_CBOR_SIMPLE_BREAK) - { - break; - } - } - circe_cbor_skip (dc, max_depth - 1); - circe_cbor_skip (dc, max_depth - 1); - } - } - else - { - for (uint64_t i = 0; i < len; ++i) - { - circe_cbor_skip (dc, max_depth - 1); - circe_cbor_skip (dc, max_depth - 1); - } - } - break; - case CIRCE_CBOR_MAJOR_TAG: - CIRCE_CBOR_ASSERT (circe_cbor_extract_tag (dc, &num_throwaway)); - circe_cbor_skip (dc, max_depth - 1); - break; - case CIRCE_CBOR_MAJOR_SIMPLE: - CIRCE_CBOR_ASSERT (circe_cbor_get_simple_type (dc, &st)); - CIRCE_CBOR_ASSERT (dc->cursor - dc->buf_start + 1 <= dc->buf_len); - ++dc->cursor; - if (st == CIRCE_CBOR_SIMPLE_SINGLE) - { - CIRCE_CBOR_ASSERT (dc->cursor - dc->buf_start + 4 <= dc->buf_len); - dc->cursor += 4; - } - else if (st == CIRCE_CBOR_SIMPLE_DOUBLE) - { - CIRCE_CBOR_ASSERT (dc->cursor - dc->buf_start + 8 <= dc->buf_len); - dc->cursor += 8; - } - } - return true; -} - -bool -circe_cbor_extract_unsigned (struct circe_cbor_decoder *dc, uint64_t *out) -{ - bool _; - CIRCE_CBOR_ASSERT (circe_cbor_get_argument (dc, out, &_)); - return true; -} - -bool -circe_cbor_extract_negative (struct circe_cbor_decoder *dc, uint64_t *magnitude_out) -{ - bool _; - CIRCE_CBOR_ASSERT (circe_cbor_get_argument (dc, magnitude_out, &_)); - return true; -} - -bool -circe_cbor_extract_bytestr (struct circe_cbor_decoder *dc, struct circe_cbor_buffer *out) -{ - bool is_definite; - uint64_t len; - CIRCE_CBOR_ASSERT (circe_cbor_get_argument (dc, &len, &is_definite)); - if (is_definite) - { - CIRCE_CBOR_ASSERT (dc->cursor - dc->buf_start + len <= dc->buf_len); - *out = (struct circe_cbor_buffer){ - .buf_start = dc->cursor, - .buf_len = len, - }; - dc->cursor += len; - } - else - { - uint8_t *start = dc->cursor; - while (dc->cursor - dc->buf_start < dc->buf_len && *dc->cursor != 0xFF) - { - ++dc->cursor; - } - CIRCE_CBOR_ASSERT (*dc->cursor == 0xFF); - ++dc->cursor; - *out = (struct circe_cbor_buffer){ - .buf_start = start, - .buf_len = dc->cursor - start, - }; - } - return true; -} - -bool -circe_cbor_extract_textstr (struct circe_cbor_decoder *dc, struct circe_cbor_buffer *out) -{ - return circe_cbor_extract_bytestr (dc, out); -} - -bool -circe_cbor_extract_float (struct circe_cbor_decoder *dc, float *out) -{ - uint32_t raw = 0; - CIRCE_CBOR_ASSERT (dc->cursor - dc->buf_start + 4 <= dc->buf_len); - for (unsigned int i = 4; i > 0; --i) - { - // TODO: adapt for non-little-endian - raw <<= 8; - raw |= *(dc->cursor + i - 1); - } - *out = *((float *)&raw); - return true; -} - -bool -circe_cbor_extract_double (struct circe_cbor_decoder *dc, double *out) -{ - uint64_t raw = 0; - CIRCE_CBOR_ASSERT (dc->cursor - dc->buf_start + 4 <= dc->buf_len); - for (unsigned int i = 8; i > 0; --i) - { - // TODO: adapt for non-little-endian - raw <<= 8; - raw |= *(dc->cursor + i - 1); - } - *out = *((double *)&raw); - return true; -} - -bool -circe_cbor_extract_tag (struct circe_cbor_decoder *dc, uint64_t *out) -{ - bool _; - CIRCE_CBOR_ASSERT (circe_cbor_get_argument (dc, out, &_)); - return true; -} - -/* ENCODING FUNCTIONS */ - -bool -circe_cbor_emit_with_arg (struct circe_cbor_encoder *ec, enum circe_cbor_major_type t, uint64_t arg) -{ - unsigned int needed = 1; - if (arg >= 24) - { - ++needed; - } - if (arg >= UINT8_MAX) - { - ++needed; - } - if (arg >= UINT16_MAX) - { - needed += 2; - } - if (arg >= UINT32_MAX) - { - needed += 4; - } - CIRCE_CBOR_ASSERT (ec->cursor - ec->buf_start + needed <= ec->buf_len); - *ec->cursor = (uint8_t)t << 5; - switch (needed) - { - case 1: - *ec->cursor |= (uint8_t)arg; - break; - case 2: - *ec->cursor |= 24; - *(ec->cursor + 1) = (uint8_t)arg; - break; - case 3: - *ec->cursor |= 25; - for (unsigned int i = 0; i < 2; ++i) - { - *(ec->cursor + 1 + i) = (uint8_t)(arg & 0xff); - arg >>= 8; - } - break; - case 5: - *ec->cursor |= 26; - for (unsigned int i = 0; i < 4; ++i) - { - *(ec->cursor + 1 + i) = (uint8_t)(arg & 0xff); - arg >>= 8; - } - break; - case 9: - *ec->cursor |= 27; - for (unsigned int i = 0; i < 8; ++i) - { - *(ec->cursor + 1 + i) = (uint8_t)(arg & 0xff); - arg >>= 8; - } - break; - } - ec->cursor += needed; - return true; -} - -bool -circe_cbor_emit_unsigned (struct circe_cbor_encoder *ec, uint64_t n) -{ - CIRCE_CBOR_ASSERT (circe_cbor_emit_with_arg (ec, CIRCE_CBOR_MAJOR_UNSIGNED, n)); - return true; -} - -bool -circe_cbor_emit_negative (struct circe_cbor_encoder *ec, uint64_t magnitude) -{ - CIRCE_CBOR_ASSERT (circe_cbor_emit_with_arg (ec, CIRCE_CBOR_MAJOR_NEGATIVE, magnitude)); - return true; -} - -bool -circe_cbor_emit_bytestr (struct circe_cbor_encoder *ec, struct circe_cbor_buffer contents) -{ - CIRCE_CBOR_ASSERT (circe_cbor_emit_with_arg (ec, CIRCE_CBOR_MAJOR_BYTESTR, contents.buf_len)); - CIRCE_CBOR_ASSERT (ec->cursor - ec->buf_start + contents.buf_len <= ec->buf_len); - for (uint64_t i = 0; i < contents.buf_len; ++i) - { - ec->cursor[i] = contents.buf_start[i]; - } - ec->cursor += contents.buf_len; - return true; -} - -bool -circe_cbor_emit_textstr (struct circe_cbor_encoder *ec, struct circe_cbor_buffer contents) -{ - CIRCE_CBOR_ASSERT (circe_cbor_emit_with_arg (ec, CIRCE_CBOR_MAJOR_TEXTSTR, contents.buf_len)); - CIRCE_CBOR_ASSERT (ec->cursor - ec->buf_start + contents.buf_len <= ec->buf_len); - for (uint64_t i = 0; i < contents.buf_len; ++i) - { - ec->cursor[i] = contents.buf_start[i]; - } - ec->cursor += contents.buf_len; - return true; -} - -bool -circe_cbor_emit_simple (struct circe_cbor_encoder *ec, enum circe_cbor_simple_type t) -{ - CIRCE_CBOR_ASSERT (ec->cursor - ec->buf_start + 1 <= ec->buf_len); - *ec->cursor = ((uint8_t)CIRCE_CBOR_MAJOR_SIMPLE << 5) | (uint8_t)t; - ++ec->cursor; - return true; -} - -bool -circe_cbor_emit_float (struct circe_cbor_encoder *ec, float x) -{ - CIRCE_CBOR_ASSERT (ec->cursor - ec->buf_start + 4 <= ec->buf_len); - uint32_t raw = *((uint32_t *)&x); - for (unsigned int i = 0; i < 4; ++i) - { - ec->cursor[i] = (uint8_t)(raw & 0xff); - raw >>= 8; - } - ec->cursor += 4; - return true; -} - -bool -circe_cbor_emit_double (struct circe_cbor_encoder *ec, double x) -{ - CIRCE_CBOR_ASSERT (ec->cursor - ec->buf_start + 4 <= ec->buf_len); - uint64_t raw = *((uint64_t *)&x); - for (unsigned int i = 0; i < 8; ++i) - { - ec->cursor[i] = (uint8_t)(raw & 0xff); - raw >>= 8; - } - ec->cursor += 4; - return true; -} - -bool -circe_cbor_emit_tag (struct circe_cbor_encoder *ec, uint64_t tagnum) -{ - CIRCE_CBOR_ASSERT (circe_cbor_emit_with_arg (ec, CIRCE_CBOR_MAJOR_TAG, tagnum)); - return true; -} - -bool -circe_cbor_emit_mapping_header (struct circe_cbor_encoder *ec, uint64_t len) -{ - CIRCE_CBOR_ASSERT (circe_cbor_emit_with_arg (ec, CIRCE_CBOR_MAJOR_MAPPING, len)); - return true; -} - -bool -circe_cbor_emit_array_header (struct circe_cbor_encoder *ec, uint64_t len) -{ - CIRCE_CBOR_ASSERT (circe_cbor_emit_with_arg (ec, CIRCE_CBOR_MAJOR_ARRAY, len)); - return true; -}
D common/cbor.h

@@ -1,98 +0,0 @@

-#ifndef CIRCE_CBOR_H -#define CIRCE_CBOR_H - -#include <stdbool.h> -#include <stdint.h> - -/// Represents all major types in the standard -enum circe_cbor_major_type -{ - CIRCE_CBOR_MAJOR_UNSIGNED = 0, - CIRCE_CBOR_MAJOR_NEGATIVE, - CIRCE_CBOR_MAJOR_BYTESTR, - CIRCE_CBOR_MAJOR_TEXTSTR, - CIRCE_CBOR_MAJOR_ARRAY, - CIRCE_CBOR_MAJOR_MAPPING, - CIRCE_CBOR_MAJOR_TAG, - /// Also includes floating-point numbers - CIRCE_CBOR_MAJOR_SIMPLE, -}; - -/// Represents most simple value types, missing IEEE 754 half-precision floats -/// because those are not standard in C without stdlib access -enum circe_cbor_simple_type -{ - CIRCE_CBOR_SIMPLE_FALSE = 20, - CIRCE_CBOR_SIMPLE_TRUE = 21, - CIRCE_CBOR_SIMPLE_NULL = 22, - CIRCE_CBOR_SIMPLE_UNDEFINED = 23, - CIRCE_CBOR_SIMPLE_SINGLE = 26, - CIRCE_CBOR_SIMPLE_DOUBLE = 27, - CIRCE_CBOR_SIMPLE_BREAK = 31, -}; - -/// Decoder context object -struct circe_cbor_decoder -{ - const uint8_t *buf_start; - const uint64_t buf_len; - const uint8_t *cursor; -}; - -/// Used to tie together buffer attributes in a single struct -struct circe_cbor_buffer -{ - uint8_t *buf_start; - uint64_t buf_len; -}; - -bool circe_cbor_get_major_type (struct circe_cbor_decoder *dc, enum circe_cbor_major_type *out); - -bool circe_cbor_get_simple_type (struct circe_cbor_decoder *dc, enum circe_cbor_simple_type *out); - -bool circe_cbor_extract_length (struct circe_cbor_decoder *dc, uint64_t *out, bool *is_definite); - -bool circe_cbor_skip (struct circe_cbor_decoder *dc, uint64_t max_depth); - -bool circe_cbor_extract_unsigned (struct circe_cbor_decoder *dc, uint64_t *out); - -bool circe_cbor_extract_negative (struct circe_cbor_decoder *dc, uint64_t *magnitude_out); - -bool circe_cbor_extract_bytestr (struct circe_cbor_decoder *dc, struct circe_cbor_buffer *out); - -bool circe_cbor_extract_textstr (struct circe_cbor_decoder *dc, struct circe_cbor_buffer *out); - -bool circe_cbor_extract_float (struct circe_cbor_decoder *dc, float *out); - -bool circe_cbor_extract_double (struct circe_cbor_decoder *dc, double *out); - -bool circe_cbor_extract_tag (struct circe_cbor_decoder *dc, uint64_t *out); - -struct circe_cbor_encoder -{ - uint8_t *buf_start; - const uint64_t buf_len; - uint8_t *cursor; -}; - -bool circe_cbor_emit_unsigned (struct circe_cbor_encoder *ec, uint64_t n); - -bool circe_cbor_emit_negative (struct circe_cbor_encoder *ec, uint64_t magnitude); - -bool circe_cbor_emit_bytestr (struct circe_cbor_encoder *ec, struct circe_cbor_buffer contents); - -bool circe_cbor_emit_textstr (struct circe_cbor_encoder *ec, struct circe_cbor_buffer contents); - -bool circe_cbor_emit_simple (struct circe_cbor_encoder *ec, enum circe_cbor_simple_type t); - -bool circe_cbor_emit_float (struct circe_cbor_encoder *ec, float x); - -bool circe_cbor_emit_double (struct circe_cbor_encoder *ec, double x); - -bool circe_cbor_emit_tag (struct circe_cbor_encoder *ec, uint64_t tagnum); - -bool circe_cbor_emit_mapping_header (struct circe_cbor_encoder *ec, uint64_t len); - -bool circe_cbor_emit_array_header (struct circe_cbor_encoder *ec, uint64_t len); - -#endif
M contrib/meson.buildcontrib/meson.build

@@ -52,7 +52,7 @@

# --------------------------------------------------------------------------- # mlkem-native -> ML-KEM-768 # --------------------------------------------------------------------------- -mlkem_inc = include_directories('mlkem-native/mlkem') +mlkem_inc = include_directories('mlkem-native') mlkem_config_arg = '-DMLK_CONFIG_FILE="circe_mlkem_config.h"' mlkem_lib = static_library(

@@ -77,7 +77,7 @@

# --------------------------------------------------------------------------- # mldsa-native -> ML-DSA-44 # --------------------------------------------------------------------------- -mldsa_inc = include_directories('mldsa-native/mldsa') +mldsa_inc = include_directories('mldsa-native') mldsa_config_arg = '-DMLD_CONFIG_FILE="circe_mldsa_config.h"' mldsa_lib = static_library(
M core/core.ccore/core.c

@@ -1,14 +1,11 @@

-#include "core.h" -#include "cbor.h" -#include "chacha20_poly1305.h" -#include "kangarootwelve128.h" -#include "memory.h" -#include "mldsa_native.h" -#include "mlkem_native.h" -#include "packet.h" -#include "peer_table.h" -#include "sha2.h" -#include "x25519.h" +#include "core/core.h" +#include "common/memory.h" +#include "core/peer_table.h" +#include "mldsa/mldsa_native.h" +#include "mlkem/mlkem_native.h" +#include "proto_circe.h" +#include "tinycrypt/kangarootwelve128.h" +#include "tinycrypt/x25519.h" #include <stdbool.h> #include <stddef.h> #include <stdint.h>

@@ -76,16 +73,17 @@ }

static enum circe_result peer_table_lookup (struct circe_context *ctx, - const uint8_t identity[CIRCE_IDENTITY_LEN], + const uint8_t tunnel_id[CIRCE_TUNNEL_ID_LEN], struct circe_peer **const out) { - size_t start_idx = identity[0] % CIRCE_PEER_TABLE_LEN; + size_t start_idx = tunnel_id[0] % CIRCE_PEER_TABLE_LEN; for (size_t i = 0; i < CIRCE_PEER_TABLE_LEN; ++i) { if (circe_memory_equal ( - identity, - ctx->peer_table[(i + start_idx) % CIRCE_PEER_TABLE_LEN].identity, - CIRCE_IDENTITY_LEN)) + tunnel_id, + ctx->peer_table[(i + start_idx) % CIRCE_PEER_TABLE_LEN] + .tunnel_id, + CIRCE_TUNNEL_ID_LEN)) { *out = &(ctx->peer_table[(i + start_idx) % CIRCE_PEER_TABLE_LEN]); return CIRCE_RESULT_SUCCESS;

@@ -96,7 +94,7 @@ if (out_idx == 0)

{ return CIRCE_RESULT_PEER_TABLE_FULL; } - if (!ctx->recall_peer_callback (ctx->ctx_handle, identity, + if (!ctx->recall_peer_callback (ctx->ctx_handle, tunnel_id, &ctx->peer_table[out_idx - 1])) { return CIRCE_RESULT_UNKNOWN_REMOTE;

@@ -104,71 +102,12 @@ }

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; + (*out)->last_handshake_stage = CIRCE_HANDSHAKE_STAGE_OPEN; + proto_circe_v1_rx_init (&(*out)->dgram_rx_state, (*out)->dgram_rx_buffer, + sizeof ((*out)->dgram_rx_buffer)); return CIRCE_RESULT_HANDSHAKE_PENDING; } -static enum circe_result -unseal_payload (struct circe_peer *peer, struct circe_cbor_decoder *cd, - uint8_t *out, size_t *out_len) -{ - enum circe_cbor_major_type major_type; - uint64_t field; - uint64_t sequence_number; - - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_UNSIGNED, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_extract_unsigned (cd, &field), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (field == CIRCE_PACKET_FIELD_SEQ_NUMBER, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_UNSIGNED, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_extract_unsigned (cd, &sequence_number), - CIRCE_RESULT_INVALID_PACKET); - - const uint8_t *end_of_aad = cd->cursor; - - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_UNSIGNED, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_extract_unsigned (cd, &field), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (field == CIRCE_PACKET_FIELD_DATA_PAYLOAD, - CIRCE_RESULT_INVALID_PACKET); - - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_BYTESTR, - CIRCE_RESULT_INVALID_PACKET); - struct circe_cbor_buffer sealed; - CIRCE_CORE_ASSERT (circe_cbor_extract_bytestr (cd, &sealed), - CIRCE_RESULT_INVALID_PACKET); - // Make sure we got the full payload - CIRCE_CORE_ASSERT (sealed.buf_len - <= cd->buf_len - (cd->cursor - cd->buf_start), - CIRCE_RESULT_INVALID_PACKET); - uint8_t nonce[sizeof (uint64_t)]; - for (unsigned int i = 0; i < sizeof (uint64_t); ++i) - { - nonce[i] = (sequence_number >> (i * 8)) & 0xff; - } - if (!tct_aead_chacha20_poly1305_decrypt_and_verify ( - cd->buf_start, end_of_aad - cd->buf_start, peer->rx_key, nonce, - sealed.buf_start, sealed.buf_len, out)) - { - return CIRCE_RESULT_INVALID_MAC; - } - *out_len - = sealed.buf_len - CIRCE_P1305_MAC_LEN; // Don't include the MAC's length - return CIRCE_RESULT_SUCCESS; -} - static void close_without_saying_goodbye (struct circe_context *ctx, struct circe_peer *remote_pt_entry)

@@ -199,641 +138,6 @@ }

/* END GENERAL HELPER FUNCTIONS */ -/* HANDSHAKE PROCESSING FUNCTIONS */ - -static enum circe_result -process_opentunnel (struct circe_context *ctx, struct circe_cbor_decoder *cd, - struct circe_location remote, - struct circe_peer *remote_pt_entry) -{ - - enum circe_cbor_major_type major_type; - uint64_t field; - - if (remote_pt_entry->next_handshake_stage != CIRCE_HANDSHAKE_STAGE_OPEN) - { - return CIRCE_RESULT_HS_OUT_OF_ORDER; - } - - uint8_t remote_ecdh_frag[32]; - // ECDH fragment - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_UNSIGNED, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_extract_unsigned (cd, &field), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (field == CIRCE_PACKET_FIELD_ECDH_FRAGMENT, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_BYTESTR, - CIRCE_RESULT_INVALID_PACKET); - struct circe_cbor_buffer buf; - CIRCE_CORE_ASSERT (circe_cbor_extract_bytestr (cd, &buf), - CIRCE_RESULT_INVALID_PACKET); - // Size of a Curve25519 ECDH fragment - CIRCE_CORE_ASSERT (buf.buf_len == 32, CIRCE_RESULT_INVALID_PACKET); - circe_memory_copy (buf.buf_start, remote_ecdh_frag, 32); - - // MLKEM-768 public key - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_UNSIGNED, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_extract_unsigned (cd, &field), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (field == CIRCE_PACKET_FIELD_MLKEM_PUBKEY, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_BYTESTR, - CIRCE_RESULT_INVALID_PACKET); - struct circe_cbor_buffer mlkem_pubkey; - CIRCE_CORE_ASSERT (circe_cbor_extract_bytestr (cd, &mlkem_pubkey), - 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 (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); - 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 (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); - 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 (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); - 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]; - 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, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; - uint8_t my_ecdh_frag[32]; - tct_x25519 (ecdh_privkey, u, my_ecdh_frag); - - 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 = 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; - // Version, identity, type, ECDH fragment, MLKEM encapsulated, payload == 6 - // entries - CIRCE_CORE_ASSERT_WITH_CLEANUP (circe_cbor_emit_mapping_header (&ec, 6), - CIRCE_RESULT_INSUFFICIENT_MEM, err, - process_opentunnel_cleanup); - - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_VERSION), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_VERSION_V1), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - 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 = ctx->my_identity, - .buf_len = CIRCE_IDENTITY_LEN }), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_TYPE), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_ACKOPEN1), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_ECDH_FRAGMENT), - 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_ecdh_frag, - .buf_len = sizeof (my_ecdh_frag) }), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - - uint8_t qk_shared[MLKEM_BYTES]; - uint8_t encapsulated[MLKEM_CIPHERTEXTBYTES (768)]; - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - crypto_kem_enc (encapsulated, qk_shared, mlkem_pubkey.buf_start), - CIRCE_RESULT_KEX_FAILED, err, process_opentunnel_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_MLKEM_ENCAPSULATED), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_bytestr ( - &ec, (struct circe_cbor_buffer){ .buf_start = encapsulated, - .buf_len = sizeof (encapsulated) }), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - 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); - tct_turboshake128_init (remote_pt_entry->prk); - const uint8_t SALT[] = "circe/v1"; - tct_turboshake128_absorb (remote_pt_entry->prk, SALT, - (uint64_t)(sizeof (SALT) - 1), 0x0); - tct_turboshake128_absorb (remote_pt_entry->prk, ek_shared, - (uint64_t)sizeof (ek_shared), 0x1); - tct_turboshake128_absorb (remote_pt_entry->prk, qk_shared, - (uint64_t)sizeof (qk_shared), 0x2); - uint8_t prk_to_expand[TCT_TURBOSHAKE128_STATE_LEN]; - circe_memory_copy (remote_pt_entry->prk, prk_to_expand, - TCT_TURBOSHAKE128_STATE_LEN); - const uint8_t EXPANSION_DATA[] = "hs eurylochus"; - tct_turboshake128_absorb (prk_to_expand, EXPANSION_DATA, - (uint64_t)(sizeof (EXPANSION_DATA) - 1), 0x3); - uint8_t th2[64]; - tct_sha512 (cd->buf_start, cd->buf_len, th2); - tct_turboshake128_absorb (prk_to_expand, th2, sizeof (th2), 0x4); - uint8_t k_hs_e[32]; - 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 (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, - ctx->my_mldsa_privkey) - == 0, - CIRCE_RESULT_SIGNATURE_FAILED, err, process_opentunnel_cleanup); - 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 = 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 - CIRCE_CORE_ASSERT_WITH_CLEANUP (circe_cbor_emit_mapping_header (&ec, 4), - CIRCE_RESULT_INSUFFICIENT_MEM, err, - process_opentunnel_cleanup); - - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_VERSION), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_VERSION_V1), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - 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 = ctx->my_identity, - .buf_len = CIRCE_IDENTITY_LEN }), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_TYPE), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_ACKOPEN2), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - - tct_aead_chacha20_poly1305_encrypt ( - ec.buf_start, ec.cursor - ec.buf_start, k_hs_e, nonce, payload_clear, - sizeof (payload_clear) / 2, encrypted_buf, - encrypted_buf + sizeof (payload_clear) / 2); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_ID_PAYLOAD1), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_bytestr ( - &ec, - (struct circe_cbor_buffer){ .buf_start = encrypted_buf, - .buf_len = sizeof (encrypted_buf) }), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - ctx->outgoing_packets[ack2_idx - 1].length = ec.cursor - ec.buf_start; - - 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 - CIRCE_CORE_ASSERT_WITH_CLEANUP (circe_cbor_emit_mapping_header (&ec, 4), - CIRCE_RESULT_INSUFFICIENT_MEM, err, - process_opentunnel_cleanup); - - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_VERSION), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_VERSION_V1), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - 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 = ctx->my_identity, - .buf_len = CIRCE_IDENTITY_LEN }), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_TYPE), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_ACKOPEN2), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - - nonce[0]++; - tct_aead_chacha20_poly1305_encrypt ( - ec.buf_start, ec.cursor - ec.buf_start, k_hs_e, nonce, - payload_clear + sizeof (payload_clear) / 2, sizeof (payload_clear) / 2, - encrypted_buf, encrypted_buf + sizeof (payload_clear) / 2); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_ID_PAYLOAD2), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_bytestr ( - &ec, - (struct circe_cbor_buffer){ .buf_start = encrypted_buf, - .buf_len = sizeof (encrypted_buf) }), - CIRCE_RESULT_INSUFFICIENT_MEM, err, process_opentunnel_cleanup); - ctx->outgoing_packets[ack3_idx - 1].length = ec.cursor - ec.buf_start; - -process_opentunnel_cleanup: - if (err != CIRCE_RESULT_SUCCESS) - { - 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 *)&ctx->outgoing_packets[ack2_idx - 1], 0x0, - sizeof (struct circe_outgoing_packet)); - circe_memory_set ((uint8_t *)&ctx->outgoing_packets[ack3_idx - 1], 0x0, - sizeof (struct circe_outgoing_packet)); - } - circe_memory_set (ecdh_privkey, 0x0, 32); - circe_memory_set (remote_ecdh_frag, 0x0, 32); - circe_memory_set (mlkem_pubkey.buf_start, 0x0, MLKEM_PUBLICKEYBYTES (768)); - circe_memory_set (my_ecdh_frag, 0x0, 32); - circe_memory_set (qk_shared, 0x0, sizeof (qk_shared)); - circe_memory_set (ek_shared, 0x0, sizeof (ek_shared)); - circe_memory_set (encapsulated, 0x0, sizeof (encapsulated)); - return err; -} - -static enum circe_result -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 (ctx); - ctx->outgoing_packet_table_free_mask &= ~(1 << (id1_idx - 1)); - if (id1_idx == 0) - { - 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 (ctx); - ctx->outgoing_packet_table_free_mask &= ~(1 << (id2_idx - 1)); - if (id2_idx == 0) - { - 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; - } - - uint8_t k_hs_p[32]; - uint8_t EXPANSION_DATA[] = "hs polites"; - uint8_t prk_to_expand[TCT_TURBOSHAKE128_STATE_LEN]; - circe_memory_copy (remote_pt_entry->prk, prk_to_expand, - TCT_TURBOSHAKE128_STATE_LEN); - tct_turboshake128_absorb (prk_to_expand, EXPANSION_DATA, - sizeof (EXPANSION_DATA) - 1, 0x3); - tct_turboshake128_absorb (prk_to_expand, th3, 64, 0x4); - tct_turboshake128_squeeze_destructive (prk_to_expand, k_hs_p, - sizeof (k_hs_p)); - circe_memory_set (prk_to_expand, 0x0, TCT_TURBOSHAKE128_STATE_LEN); - - struct circe_cbor_encoder ec = { - .buf_len = CIRCE_MAX_UDP_PAYLOAD, - }; - uint8_t id_payload[CIRCE_IDENTITY_LEN + MLDSA_BYTES (44)]; - 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, - 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) ? 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, - dispatch_identity_packets_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_VERSION), - CIRCE_RESULT_INSUFFICIENT_MEM, err, - dispatch_identity_packets_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_VERSION_V1), - CIRCE_RESULT_INSUFFICIENT_MEM, err, - dispatch_identity_packets_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_IDENTITY), - 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 = ctx->my_identity }), - CIRCE_RESULT_INSUFFICIENT_MEM, err, - dispatch_identity_packets_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_TYPE), - CIRCE_RESULT_INSUFFICIENT_MEM, err, - dispatch_identity_packets_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, (i == 0) ? CIRCE_PACKET_IDENTIFY1 - : CIRCE_PACKET_IDENTIFY2), - CIRCE_RESULT_INSUFFICIENT_MEM, err, - dispatch_identity_packets_cleanup); - uint8_t *old_cursor = ec.cursor; - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, (i == 0) - ? CIRCE_PACKET_FIELD_ID_PAYLOAD1 - : 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 - + 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 }; - tct_aead_chacha20_poly1305_encrypt ( - ec.buf_start, old_cursor - ec.buf_start, k_hs_p, nonce, - id_payload + i * (CIRCE_IDENTITY_LEN + MLDSA_BYTES (44)) / 2, - (CIRCE_IDENTITY_LEN + MLDSA_BYTES (44)) / 2, out_buf.buf_start, - 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); - ctx->outgoing_packets[(i == 0) ? (id1_idx - 1) : (id2_idx - 1)] - .destination - = remote; - 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) - { - 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 *)&ctx->outgoing_packets[id2_idx - 1], 0x0, - sizeof (struct circe_outgoing_packet)); - } - circe_memory_set (id_payload, 0x0, sizeof (id_payload)); - return err; -} - -static enum circe_result -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; - enum circe_cbor_major_type major_type; - struct circe_cbor_buffer buf; - uint64_t field; - size_t aad_len - = cd->cursor - cd->buf_start; // Will be the same for both stage 2 and 3 - - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - !(remote_pt_entry->packets_received_mask & (1 << (stage - 1))), - CIRCE_RESULT_DUPLICATE_HS_PACKET, err, process_openack_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - remote_pt_entry->next_handshake_stage == CIRCE_HANDSHAKE_STAGE_ACK, - CIRCE_RESULT_HS_OUT_OF_ORDER, err, process_openack_cleanup); - - uint8_t remote_ecdh_frag[32]; - struct circe_cbor_buffer mlkem_encapsulated; - uint8_t qk_shared[32]; - uint8_t ek_shared[32]; - - switch (stage) - { - case 1: - // ECDH fragment - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_UNSIGNED, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_extract_unsigned (cd, &field), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (field == CIRCE_PACKET_FIELD_ECDH_FRAGMENT, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_BYTESTR, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_extract_bytestr (cd, &buf), - CIRCE_RESULT_INVALID_PACKET); - // Size of a Curve25519 ECDH fragment - CIRCE_CORE_ASSERT (buf.buf_len == 32, CIRCE_RESULT_INVALID_PACKET); - circe_memory_copy (buf.buf_start, remote_ecdh_frag, 32); - - // MLKEM-768 public key - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_UNSIGNED, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_extract_unsigned (cd, &field), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (field == CIRCE_PACKET_FIELD_MLKEM_ENCAPSULATED, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_BYTESTR, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_extract_bytestr (cd, &mlkem_encapsulated), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (mlkem_encapsulated.buf_len - == MLKEM_CIPHERTEXTBYTES (768), - CIRCE_RESULT_INVALID_PACKET); - - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - crypto_kem_dec (qk_shared, mlkem_encapsulated.buf_start, - remote_pt_entry->my_mlkem_privkey) - == 0, - CIRCE_RESULT_KEX_FAILED, err, process_openack_cleanup); - tct_x25519 (remote_pt_entry->my_ecdh_privkey, remote_ecdh_frag, - ek_shared); - tct_turboshake128_init (remote_pt_entry->prk); - const uint8_t SALT[] = "circe/v1"; - tct_turboshake128_absorb (remote_pt_entry->prk, SALT, - (uint64_t)(sizeof (SALT) - 1), 0x0); - tct_turboshake128_absorb (remote_pt_entry->prk, ek_shared, - (uint64_t)sizeof (ek_shared), 0x1); - tct_turboshake128_absorb (remote_pt_entry->prk, qk_shared, - (uint64_t)sizeof (qk_shared), 0x2); - break; - case 2: - case 3: - circe_memory_copy (cd->buf_start, - remote_pt_entry->id_payload_aads[stage - 2], - cd->cursor - cd->buf_start); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_get_major_type (cd, &major_type), - CIRCE_RESULT_INVALID_PACKET, err, process_openack_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP (circe_cbor_extract_unsigned (cd, &field), - CIRCE_RESULT_INVALID_PACKET, err, - process_openack_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - (stage == 2) ? (field == CIRCE_PACKET_FIELD_ID_PAYLOAD1) - : (field == CIRCE_PACKET_FIELD_ID_PAYLOAD2), - CIRCE_RESULT_INVALID_PACKET, err, process_openack_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_get_major_type (cd, &major_type), - CIRCE_RESULT_INVALID_PACKET, err, process_openack_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP (major_type == CIRCE_CBOR_MAJOR_BYTESTR, - CIRCE_RESULT_INVALID_PACKET, err, - process_openack_cleanup); - 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 - + 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], - buf.buf_len); - break; - default: - CIRCE_CORE_ASSERT_WITH_CLEANUP (false, CIRCE_RESULT_INTERNAL_ERROR, err, - process_openack_cleanup); - break; - } -process_openack_cleanup: - circe_memory_set (remote_ecdh_frag, 0x0, sizeof (remote_ecdh_frag)); - circe_memory_set (ek_shared, 0x0, sizeof (ek_shared)); - circe_memory_set (qk_shared, 0x0, sizeof (qk_shared)); - if (err == CIRCE_RESULT_SUCCESS) - { - remote_pt_entry->packets_received_mask |= 1 << (stage - 1); - if (remote_pt_entry->packets_received_mask == 0b111) - { - remote_pt_entry->packets_received_mask = 0x0; - remote_pt_entry->next_handshake_stage = CIRCE_HANDSHAKE_STAGE_ID; - uint8_t prk_to_expand[TCT_TURBOSHAKE128_STATE_LEN]; - circe_memory_copy (remote_pt_entry->prk, prk_to_expand, - TCT_TURBOSHAKE128_STATE_LEN); - const uint8_t EXPANSION_DATA[] = "hs eurylochus"; - tct_turboshake128_absorb (prk_to_expand, EXPANSION_DATA, - sizeof (EXPANSION_DATA) - 1, 0x3); - tct_turboshake128_absorb (prk_to_expand, remote_pt_entry->my_th2, - sizeof (remote_pt_entry->my_th2), 0x4); - uint8_t k_hs_e[32]; - tct_turboshake128_squeeze_destructive (prk_to_expand, k_hs_e, - sizeof (k_hs_e)); - circe_memory_set (prk_to_expand, 0x0, TCT_TURBOSHAKE128_STATE_LEN); - uint8_t nonce[sizeof (uint64_t)] - = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; - uint8_t id_payload[CIRCE_IDENTITY_LEN + MLDSA_BYTES (44)]; - if (!tct_aead_chacha20_poly1305_decrypt_and_verify ( - remote_pt_entry->id_payload_aads[0], aad_len, k_hs_e, nonce, - remote_pt_entry->id_payload_halves[0], - (CIRCE_IDENTITY_LEN + MLDSA_BYTES (44)) / 2, id_payload)) - { - circe_memory_set (id_payload, 0x0, sizeof (id_payload)); - return CIRCE_RESULT_INVALID_MAC; - } - nonce[0]++; - if (!tct_aead_chacha20_poly1305_decrypt_and_verify ( - remote_pt_entry->id_payload_aads[1], aad_len, k_hs_e, nonce, - remote_pt_entry->id_payload_halves[1], - (CIRCE_IDENTITY_LEN + MLDSA_BYTES (44)) / 2, - id_payload + (CIRCE_IDENTITY_LEN + MLDSA_BYTES (44)) / 2)) - { - circe_memory_set (id_payload, 0x0, sizeof (id_payload)); - return CIRCE_RESULT_INVALID_MAC; - } - if (crypto_sign_verify (id_payload + CIRCE_IDENTITY_LEN, - MLDSA_BYTES (44), id_payload, - CIRCE_IDENTITY_LEN, NULL, 0, - remote_pt_entry->mldsa_pubkey) - != 0) - { - circe_memory_set (id_payload, 0x0, sizeof (id_payload)); - return CIRCE_RESULT_INVALID_SIGNATURE; - } - 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 (ctx, remote, remote_pt_entry, th3); - } - } - return err; -} - -static enum circe_result -process_identify (struct circe_cbor_decoder *cd, struct circe_location remote, - struct circe_peer *remote_pt_entry, size_t stage) -{ -} - -static enum circe_result -dispatch_opentunnel (struct circe_location remote, - struct circe_peer *remote_pt_entry) -{ -} - -/* END HANDSHAKE PROCESSING FUNCTIONS */ - /* PUBLIC API */ enum circe_result

@@ -868,343 +172,26 @@ 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) { - *n_events = 0; - struct circe_cbor_decoder cd = { - .buf_start = in, - .buf_len = len, - .cursor = in, - }; - enum circe_cbor_major_type major_type; - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (&cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_MAPPING, - CIRCE_RESULT_INVALID_PACKET); - uint64_t num_fields; - CIRCE_CORE_ASSERT (circe_cbor_extract_unsigned (&cd, &num_fields), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (num_fields >= 2, CIRCE_RESULT_INVALID_PACKET); - - // Version - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (&cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_UNSIGNED, - CIRCE_RESULT_INVALID_PACKET); - uint64_t field; - CIRCE_CORE_ASSERT (circe_cbor_extract_unsigned (&cd, &field), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (field == CIRCE_PACKET_FIELD_VERSION, - CIRCE_RESULT_INVALID_PACKET); - uint64_t version; - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (&cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_UNSIGNED, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_extract_unsigned (&cd, &version), - CIRCE_RESULT_INVALID_PACKET); - // If it's anything but v1 we can't handle it (there is no v2) - CIRCE_CORE_ASSERT (field == CIRCE_VERSION_V1, CIRCE_RESULT_INVALID_PACKET); - - // Remote's identity - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (&cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_UNSIGNED, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_extract_unsigned (&cd, &field), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (field == CIRCE_PACKET_FIELD_IDENTITY, - CIRCE_RESULT_INVALID_PACKET); - struct circe_cbor_buffer remote_id_buf; - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (&cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_BYTESTR, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_extract_bytestr (&cd, &remote_id_buf), - CIRCE_RESULT_INVALID_PACKET); - 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 (ctx, remote_id_buf.buf_start, &remote_pt_entry); - if (peer_in_ram == CIRCE_RESULT_UNKNOWN_REMOTE) - { - return CIRCE_RESULT_UNKNOWN_REMOTE; - } - - // Packet type - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (&cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_UNSIGNED, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_extract_unsigned (&cd, &field), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (field == CIRCE_PACKET_FIELD_TYPE, - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (circe_cbor_get_major_type (&cd, &major_type), - CIRCE_RESULT_INVALID_PACKET); - CIRCE_CORE_ASSERT (major_type == CIRCE_CBOR_MAJOR_UNSIGNED, - CIRCE_RESULT_INVALID_PACKET); - uint64_t packet_type; - CIRCE_CORE_ASSERT (circe_cbor_extract_unsigned (&cd, &packet_type), - CIRCE_RESULT_INVALID_PACKET); - - if ((packet_type != CIRCE_PACKET_ROUTE_CHALLENGE1) - && (packet_type != CIRCE_PACKET_OPENTUNNEL) - && (peer_in_ram == CIRCE_RESULT_SUCCESS)) + proto_circe_v1_frag_t fragment; + if (proto_circe_v1_peek (in, len, &fragment) != PBA_OK) { - // Verify that this came from our known remote location - bool good_location; - switch (remote_pt_entry->current_route) - { - case CIRCE_ROUTE_LAN0: - good_location = locations_equal (remote_pt_entry->lan0_loc, remote); - break; - case CIRCE_ROUTE_LAN1: - good_location = locations_equal (remote_pt_entry->lan1_loc, remote); - break; - case CIRCE_ROUTE_WAN: - good_location = locations_equal (remote_pt_entry->wan_loc, remote); - break; - case CIRCE_ROUTE_RELAY: - // What? - good_location = false; - break; - default: - return CIRCE_RESULT_INTERNAL_ERROR; - } - if (!good_location) - { - (*n_events)++; - events[0] = (struct circe_event){ - .event_type = CIRCE_EVENT_REJECTED_REMOTE, - }; - return CIRCE_RESULT_SUCCESS; - } - } - - enum circe_result err; - switch (packet_type) - { - case CIRCE_PACKET_OPENTUNNEL: - if (peer_in_ram == CIRCE_RESULT_SUCCESS) - { - // We already have an open session, something is wrong - events[*n_events] = (struct circe_event){ - .event_type = CIRCE_EVENT_REJECTED_REMOTE, - }; - (*n_events)++; - return CIRCE_RESULT_SUCCESS; - } - else if (peer_in_ram == CIRCE_RESULT_HANDSHAKE_PENDING) - { - // This is the expected path - err = process_opentunnel (ctx, &cd, remote, remote_pt_entry); - if (err != CIRCE_RESULT_SUCCESS) - { - close_without_saying_goodbye (ctx, remote_pt_entry); - events[*n_events] = (struct circe_event){ - .event_type = CIRCE_EVENT_REJECTED_REMOTE, - }; - (*n_events)++; - return err; - } - events[*n_events] = (struct circe_event){ - .event_type = CIRCE_EVENT_NEW_REMOTE, - }; - (*n_events)++; - return CIRCE_RESULT_SUCCESS; - } - else - { - events[*n_events] = (struct circe_event){ - .event_type = CIRCE_EVENT_REJECTED_REMOTE, - }; - (*n_events)++; - return peer_in_ram; - } - case CIRCE_PACKET_ACKOPEN1: - case CIRCE_PACKET_ACKOPEN2: - case CIRCE_PACKET_ACKOPEN3: - err = process_openack (ctx, &cd, remote, remote_pt_entry, - (packet_type - CIRCE_PACKET_ACKOPEN1) + 1); - if (err != CIRCE_RESULT_SUCCESS) - { - close_without_saying_goodbye (ctx, remote_pt_entry); - events[*n_events] = (struct circe_event){ - .event_type = CIRCE_EVENT_REJECTED_REMOTE, - }; - (*n_events)++; - return err; - } - return CIRCE_RESULT_SUCCESS; - case CIRCE_PACKET_DATA: - events[*n_events] = (struct circe_event){ - .event_type = CIRCE_EVENT_DATA_RECEIVED, - }; - (*n_events)++; - return unseal_payload (remote_pt_entry, &cd, out, out_len); - case CIRCE_PACKET_HEARTBEAT: - case CIRCE_PACKET_CLOSETUNNEL1: - case CIRCE_PACKET_CLOSETUNNEL2: - case CIRCE_PACKET_ROUTE_CHALLENGE1: - case CIRCE_PACKET_ROUTE_CHALLENGE2: - default: return CIRCE_RESULT_INVALID_PACKET; } + struct circe_peer *remote_pt_entry; + peer_table_lookup (ctx, fragment.hdr.tunnel_id, &remote_pt_entry); + uint8_t *frag_key; + return CIRCE_RESULT_SUCCESS; } enum circe_result 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) -{ - if (len > CIRCE_MTU) - { - return CIRCE_RESULT_OVER_MTU; - } - struct circe_peer *remote_pt_entry; - enum circe_result peer_in_ram - = 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 - // handshake - return peer_in_ram; - } - - 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; - } - ctx->outgoing_packet_table_free_mask &= ~(1ull << (next_entry_idx - 1)); - - struct circe_cbor_encoder ec = { - .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++; - - enum circe_result err = CIRCE_RESULT_SUCCESS; - - // Version, identity, type, sequence number, payload == 5 pairs - CIRCE_CORE_ASSERT_WITH_CLEANUP (circe_cbor_emit_mapping_header (&ec, 5), - CIRCE_RESULT_INSUFFICIENT_MEM, err, - circe_send_outgoing_packet_cleanup); - - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_VERSION), - CIRCE_RESULT_INSUFFICIENT_MEM, err, circe_send_outgoing_packet_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_VERSION_V1), - CIRCE_RESULT_INSUFFICIENT_MEM, err, circe_send_outgoing_packet_cleanup); - - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_IDENTITY), - 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 = ctx->my_identity }), - CIRCE_RESULT_INSUFFICIENT_MEM, err, circe_send_outgoing_packet_cleanup); - - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_TYPE), - CIRCE_RESULT_INSUFFICIENT_MEM, err, circe_send_outgoing_packet_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_DATA), - CIRCE_RESULT_INSUFFICIENT_MEM, err, circe_send_outgoing_packet_cleanup); - - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_SEQ_NUMBER), - CIRCE_RESULT_INSUFFICIENT_MEM, err, circe_send_outgoing_packet_cleanup); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, remote_pt_entry->last_seqn_tx), - CIRCE_RESULT_INSUFFICIENT_MEM, err, circe_send_outgoing_packet_cleanup); - - uint8_t sealed[CIRCE_MTU + CIRCE_P1305_MAC_LEN]; // Encrypted with MAC - uint8_t nonce[sizeof (uint64_t)]; - for (size_t i = 0; i < sizeof (uint64_t); ++i) - { - nonce[i] = 0xff & (remote_pt_entry->last_seqn_tx >> (i * 8)); - } - tct_aead_chacha20_poly1305_encrypt (ec.buf_start, ec.cursor - ec.buf_start, - remote_pt_entry->tx_key, nonce, in, len, - sealed, sealed + len); - CIRCE_CORE_ASSERT_WITH_CLEANUP ( - circe_cbor_emit_unsigned (&ec, CIRCE_PACKET_FIELD_DATA_PAYLOAD), - 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 = len + CIRCE_P1305_MAC_LEN, - .buf_start = sealed }), - CIRCE_RESULT_INSUFFICIENT_MEM, err, circe_send_outgoing_packet_cleanup); - - enum circe_result relay_server_in_ram; - struct circe_peer *const relay_server_pt_entry = NULL; - switch (remote_pt_entry->current_route) - { - case CIRCE_ROUTE_LAN0: - ctx->outgoing_packets[next_entry_idx - 1].destination - = remote_pt_entry->lan0_loc; - break; - case CIRCE_ROUTE_LAN1: - ctx->outgoing_packets[next_entry_idx - 1].destination - = remote_pt_entry->lan1_loc; - break; - case CIRCE_ROUTE_WAN: - 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 ( - 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); - ctx->outgoing_packets[next_entry_idx - 1].destination - = relay_server_pt_entry->wan_loc; - break; - } + struct circe_event *events, size_t *n_events); - ctx->outgoing_packets[next_entry_idx - 1].length = ec.cursor - ec.buf_start; +enum circe_deadline circe_next_deadline (struct circe_context *ctx, + uint64_t *ms_timestamp); -circe_send_outgoing_packet_cleanup: - if (err != CIRCE_RESULT_SUCCESS) - { - remote_pt_entry->last_seqn_tx--; - ctx->outgoing_packet_table_free_mask |= (1ull << (next_entry_idx - 1)); - } - return err; -} - -enum circe_result -circe_tick (struct circe_context *ctx, uint64_t now_ms, - struct circe_outgoing_packet *out, bool *new_packet_out) -{ - if (now_ms < ctx->last_tick_ms) - { - return CIRCE_RESULT_INVALID_TIMESTAMP; - } - 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 = ctx->outgoing_packets[packet_idx - 1]; - circe_memory_set ((uint8_t *)(&ctx->outgoing_packets[packet_idx - 1]), 0x0, - CIRCE_MAX_UDP_PAYLOAD); - ctx->outgoing_packet_table_free_mask |= (1ull << (packet_idx - 1)); - return CIRCE_RESULT_SUCCESS; -} - -/* END PUBLIC API */+enum circe_result circe_tick (struct circe_context *ctx, uint64_t now_ms, + struct circe_outgoing_packet *out, + bool *new_packet_out);
M core/core.hcore/core.h

@@ -1,9 +1,9 @@

#ifndef CIRCE_CORE_H #define CIRCE_CORE_H -#include "mldsa_native.h" -#include "packet.h" -#include "peer_table.h" +#include "core/peer_table.h" +#include "mldsa/mldsa_native.h" +#include "proto_circe.h" #include <stddef.h> #include <stdint.h>

@@ -51,6 +51,13 @@ {

CIRCE_DEADLINE_NOW, CIRCE_DEADLINE_INDEFINITE, CIRCE_DEADLINE_AS_INDICATED, +}; + +struct circe_outgoing_packet +{ + uint8_t buffer[PROTO_CIRCE_MTU]; + size_t length; + struct circe_location destination; }; struct circe_context

@@ -112,4 +119,4 @@ enum circe_result circe_tick (struct circe_context *ctx, uint64_t now_ms,

struct circe_outgoing_packet *out, bool *new_packet_out); -#endif+#endif
D core/packet.h

@@ -1,51 +0,0 @@

-#ifndef CIRCE_CORE_PACKET_H -#define CIRCE_CORE_PACKET_H - -#define CIRCE_MTU 1280 -#define CIRCE_MAX_UDP_PAYLOAD 1472 - -#include "peer_table.h" -#include <stddef.h> -#include <stdint.h> - -enum circe_packet_type -{ - // Some packets need to be splintered because of UDP MTU - - CIRCE_PACKET_OPENTUNNEL = 0x0, - CIRCE_PACKET_ACKOPEN1, - CIRCE_PACKET_ACKOPEN2, - CIRCE_PACKET_ACKOPEN3, - CIRCE_PACKET_IDENTIFY1, - CIRCE_PACKET_IDENTIFY2, - CIRCE_PACKET_DATA, - CIRCE_PACKET_HEARTBEAT, - CIRCE_PACKET_CLOSETUNNEL1, - CIRCE_PACKET_CLOSETUNNEL2, - CIRCE_PACKET_ROUTE_CHALLENGE1, - CIRCE_PACKET_ROUTE_CHALLENGE2, -}; - -enum circe_packet_field -{ - CIRCE_PACKET_FIELD_VERSION = 0x0, - CIRCE_PACKET_FIELD_IDENTITY, - CIRCE_PACKET_FIELD_TYPE, - CIRCE_PACKET_FIELD_ECDH_FRAGMENT, - CIRCE_PACKET_FIELD_MLKEM_PUBKEY, - CIRCE_PACKET_FIELD_MLKEM_ENCAPSULATED, - CIRCE_PACKET_FIELD_SEQ_NUMBER, - CIRCE_PACKET_FIELD_DATA_PAYLOAD, - CIRCE_PACKET_FIELD_ID_PAYLOAD1, - CIRCE_PACKET_FIELD_ID_PAYLOAD2, - CIRCE_PACKET_FIELD_OVERMAX, -}; - -struct circe_outgoing_packet -{ - struct circe_location destination; - uint8_t buffer[CIRCE_MAX_UDP_PAYLOAD]; - size_t length; -}; - -#endif
M core/peer_table.hcore/peer_table.h

@@ -1,9 +1,10 @@

#ifndef CIRCE_CORE_PEER_TABLE_H #define CIRCE_CORE_PEER_TABLE_H -#include "kangarootwelve128.h" -#include "mldsa_native.h" -#include "mlkem_native.h" +#include "mldsa/mldsa_native.h" +#include "mlkem/mlkem_native.h" +#include "proto_circe.h" +#include "tinycrypt/kangarootwelve128.h" #include <stdbool.h> #include <stdint.h>

@@ -24,6 +25,7 @@ };

#define CIRCE_PEER_TABLE_LEN 64 #define CIRCE_IDENTITY_LEN 64 // SHA-512 hash of hostname || island name +#define CIRCE_TUNNEL_ID_LEN 64 // Random bytes enum circe_handshake_stage {

@@ -41,8 +43,10 @@ struct circe_location lan1_loc;

struct circe_location wan_loc; uint8_t relay_server_identity[CIRCE_IDENTITY_LEN]; // SERVERS DON'T USE THIS, // ONLY CLIENTS DO - uint8_t lan0_hash[32]; // SHA-256 + + uint8_t lan0_hash[32]; // SHA-256 uint8_t lan1_hash[32]; + enum { CIRCE_ROUTE_LAN0,

@@ -52,19 +56,25 @@ CIRCE_ROUTE_RELAY,

} current_route; uint8_t identity[CIRCE_IDENTITY_LEN]; - uint8_t mldsa_pubkey[1312]; // ML-DSA-44 + uint8_t mldsa_pubkey[MLDSA_PUBLICKEYBYTES (44)]; uint8_t rx_key[32]; uint8_t tx_key[32]; + uint8_t tunnel_id[64]; + uint64_t next_heartbeat_ms; uint64_t next_kex_ms; uint64_t last_seqn_tx; uint64_t last_seqn_rx; - enum circe_handshake_stage next_handshake_stage; - uint64_t packets_received_mask; + enum circe_handshake_stage last_handshake_stage; + + uint8_t dgram_rx_buffer[PROTO_CIRCE_V1_MAX_MSG_LEN]; + uint8_t dgram_tx_buffer[PROTO_CIRCE_V1_MAX_MSG_LEN]; + proto_circe_v1_rx_t dgram_rx_state; + proto_circe_v1_tx_t dgram_tx_state; // ZERO ALL OF THESE AFTER KEY EXCHANGE IS COMPLETE uint8_t my_th2[64]; // SHA-512

@@ -72,9 +82,6 @@ uint8_t my_th3[64]; // SHA-512

uint8_t my_ecdh_privkey[32]; uint8_t my_mlkem_privkey[MLKEM_SECRETKEYBYTES (768)]; uint8_t prk[TCT_TURBOSHAKE128_STATE_LEN]; - uint8_t id_payload_halves[2] - [(CIRCE_IDENTITY_LEN + MLDSA_BYTES (44)) / 2 + 16]; - uint8_t id_payload_aads[2][16]; // Yeah that should be enough }; -#endif+#endif
A core/protocol.pba

@@ -0,0 +1,67 @@

+protocol proto_circe +mtu 1472 + +version 1 { + header { + u8[64] tunnel_id; + } + + message open_tunnel { + clear { + u8[32] ecdh_fragment + u8[1184] mlkem_pubkey + } + digest sha512 + } + + message ack_open (u8[32] k_hs_e) { + clear { + u8[32] ecdh_fragment + u8[1088] mlkem_encaps + } + cc20_p1305 (nonce:0, key:@k_hs_e, aad:$preceding) { + u8[64] identity + u8[2420] signature + } + digest sha512 + } + + message identify (u8[32] k_hs_p) { + cc20_p1305 (nonce:0, key:@k_hs_p, aad:$preceding) { + u8[64] identity + u8[2420] signature + } + digest sha512 + } + + message data (u8[32] k_d) { + clear { + u64 sequence_number + } + cc20_p1305 (nonce:@sequence_number, key:@k_d, aad:$preceding) { + u8 n_random_bytes + u8[] payload + u8[@n_random_bytes] padding + } + } + + message close_tunnel (u8[32] k_d) { + clear { + u64 sequence_number + } + cc20_p1305 (nonce:@sequence_number, key:@k_d, aad:$preceding) { + u8[64] identity + u8[2420] signature + } + } + + message path_challenge (u8[32] k_d) { + clear { + u64 sequence_number + } + cc20_p1305 (nonce:@sequence_number, key:@k_d, aad:$preceding) { + u8[64] identity + u8[2420] signature + } + } +}
M crossfiles/aarch64-linux.inicrossfiles/aarch64-linux.ini

@@ -1,12 +1,13 @@

[binaries] -c = 'aarch64-unknown-linux-musl-gcc' -cpp = 'aarch64-unknown-linux-musl-g++' -ar = 'aarch64-unknown-linux-musl-ar' -windres = 'aarch64-unknown-linux-musl-windres' -strip = 'aarch64-unknown-linux-musl-strip' +c = ['zig', 'cc', '-target', 'aarch64-linux-musl'] +cpp = ['zig', 'c++', '-target', 'aarch64-linux-musl'] +ar = ['zig', 'ar'] +ranlib = ['zig', 'ranlib', '-target', 'aarch64-linux-musl'] +lib = ['zig', 'lib', '-target', 'aarch64-linux-musl'] +dlltool = ['zig', 'dlltool', '-target', 'aarch64-linux-musl'] [host_machine] system = 'linux' cpu_family = 'aarch64' cpu = 'aarch64' -endian = 'little'+endian = 'little'
M crossfiles/x86_64-linux.inicrossfiles/x86_64-linux.ini

@@ -1,9 +1,10 @@

[binaries] -c = 'gcc' -cpp = 'g++' -ar = 'ar' -windres = 'windres' -strip = 'strip' +c = ['zig', 'cc'] +cpp = ['zig', 'c++'] +ar = ['zig', 'ar'] +ranlib = ['zig', 'ranlib'] +lib = ['zig', 'lib'] +dlltool = ['zig', 'dlltool'] [host_machine] system = 'linux'

@@ -12,4 +13,4 @@ cpu = 'x86_64'

endian = 'little' [properties] -needs_exe_wrapper = false+needs_exe_wrapper = false
M flake.nixflake.nix

@@ -27,27 +27,19 @@ tokei

ruby just-lsp just-formatter + ocaml + dune_3 + zig_0_16 ] ); main_shl = pkgs.mkShell { name = "circe-tooling-env-x86_64-glibc"; - packages = base ++ (with pkgs; [ gcc ]); - NIX_ENFORCE_NO_NATIVE = 0; - hardeningDisable = [ "fortify" ]; - }; - aarch64_musl_shl = pkgs.mkShell { - name = "circe-tooling-env-aarch64-musl"; - packages = - base - ++ (with pkgs; [ - pkgsCross.aarch64-multiplatform-musl.gcc - ]); + packages = base; }; in { devShells.${system} = { default = main_shl; - aarch64_musl = aarch64_musl_shl; }; }; }
M meson.buildmeson.build

@@ -1,5 +1,23 @@

project('Circe', 'c', version: '0.1.0') +base_incdir = include_directories('./') + +## PBA-GENERATED FILES + +pba_generated = custom_target( + 'pba-parsing', + output: ['proto_circe.h', 'proto_circe.c'], + input: ['core/protocol.pba'], + command: [ + 'dune', + 'exec', + '--root=@SOURCE_ROOT@/contrib/pba', + '--', 'pba', + '-o', '@OUTDIR@', + '@INPUT@', + ], +) + ## TINYCRYPT tct_subproj = subproject('TinyCrypT')

@@ -7,29 +25,20 @@ tct_dependency = tct_subproj.get_variable('TinyCrypT_dep')

## LIBCIRCE-COMMON -common_build_args = [ - '-O3', - '-ffreestanding', - '-nostdlib', - '-lgcc', - '-fno-stack-protector' -] +common_build_args = ['-O3', '-ffreestanding', '-nostdlib', '-lgcc', '-fno-stack-protector'] common_sources = [ - 'common/cbor.c', - 'common/memory.c', + 'common/memory.c', ] - -common_incdir = include_directories('common/') common_target = static_library( - 'circe-common', - common_sources, - c_args: common_build_args, - include_directories: common_incdir, + 'circe-common', + common_sources, + c_args: common_build_args, + include_directories: base_incdir, ) -common_dep = declare_dependency(include_directories: common_incdir, link_with: common_target) +common_dep = declare_dependency(include_directories: base_incdir, link_with: common_target) set_variable(meson.project_name() + '_common_dep', common_dep) ## PQC

@@ -41,28 +50,27 @@

## LIBCIRCE-CORE core_build_args = [ - '-O3', - '-ffreestanding', - '-nostdlib', - '-lgcc', - '-fno-stack-protector', + '-O3', + '-ffreestanding', + '-nostdlib', + '-lgcc', + '-fno-stack-protector', ] core_sources = [ - 'core/core.c', + 'core/core.c', + pba_generated, ] - -core_incdir = include_directories('core/') core_target = static_library( - 'circe-core', - core_sources, - c_args: core_build_args, - include_directories: core_incdir, - dependencies: [common_dep, tct_dependency, mlkem_dependency, mldsa_dependency], + 'circe-core', + core_sources, + c_args: core_build_args, + include_directories: base_incdir, + dependencies: [common_dep, tct_dependency, mlkem_dependency, mldsa_dependency], ) -core_dep = declare_dependency(include_directories: core_incdir, link_with: core_target) +core_dep = declare_dependency(include_directories: base_incdir, link_with: core_target) set_variable(meson.project_name() + '_core_dep', core_dep) ## UNITY TESTS

@@ -71,20 +79,18 @@ unity_proj = subproject('Unity')

unity_dep = unity_proj.get_variable('unity_dep') runner_gen = unity_proj.get_variable('gen_test_runner') -if not meson.is_subproject() and host_machine.system() == build_machine.system() and host_machine.cpu() == build_machine.cpu() - # Technically every target is 'cross-compiling', but Meson fails to properly detect usually if we can still run the test binaries. - message('Also building tests!') - example_test = meson.project_source_root() / 'test/test_example.c' - example_test_runner = runner_gen.process(example_test) - test( - 'example_tests', - executable( - 'example_tests', - [example_test_runner, example_test], - include_directories: [], - link_with: [], - dependencies: unity_dep, - install: false, - ), - ) +if not meson.is_subproject() and meson.can_run_host_binaries() + example_test = meson.project_source_root() / 'test/test_example.c' + example_test_runner = runner_gen.process(example_test) + test( + 'example_tests', + executable( + 'example_tests', + [example_test_runner, example_test], + include_directories: [], + link_with: [], + dependencies: unity_dep, + install: false, + ), + ) endif