all repos — circe-vpn @ 111047a8c5cbec3275b7ac76c83c2d93581f8668

Circe VPN

core/core.c (view raw)

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
#include "core.h"
#include "cbor.h"
#include "chacha20_poly1305.h"
#include "memory.h"
#include "packet.h"
#include "peer_table.h"
#include <stdbool.h>
#include <stdint.h>

#define CIRCE_CORE_ASSERT(x, err)                                             \
  do                                                                          \
    {                                                                         \
      if (!(x))                                                               \
        {                                                                     \
          return err;                                                         \
        }                                                                     \
    }                                                                         \
  while (false)

static struct circe_peer peer_table[CIRCE_PEER_TABLE_LEN];

static bool
peer_table_lookup (uint8_t identity[CIRCE_IDENTITY_LEN],
                   struct circe_peer **out)
{
  uint8_t start_idx = identity[0] % CIRCE_PEER_TABLE_LEN;
  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,
              CIRCE_IDENTITY_LEN))
        {
          *out = &(peer_table[(i + start_idx) % CIRCE_PEER_TABLE_LEN]);
          return true;
        }
    }
  return false;
}

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);

  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_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, cd->cursor - cd->buf_start, peer->rx_key, nonce,
          sealed.buf_start, sealed.buf_len, out))
    {
      return CIRCE_RESULT_INVALID_MAC;
    }
  *out_len = sealed.buf_len - 16; // Don't include the MAC's length
  return CIRCE_RESULT_SUCCESS;
}

static bool
locations_equal (const struct circe_location a, const struct circe_location b)
{
  if (a.ip_type != b.ip_type)
    {
      return false;
    }
  else if (a.port != b.port)
    {
      return false;
    }
  else if (a.ip_type == CIRCE_ADDRESS_IPV4)
    {
      return circe_memory_equal (a.ip_addr.ipv4, b.ip_addr.ipv4,
                                 sizeof (a.ip_addr.ipv4));
    }
  else if (a.ip_type == CIRCE_ADDRESS_IPV6)
    {
      return circe_memory_equal (a.ip_addr.ipv6, b.ip_addr.ipv6,
                                 sizeof (a.ip_addr.ipv6));
    }
  // What?
  return false;
}

enum circe_result
circe_handle_packet (const struct circe_location remote, const uint8_t *in,
                     size_t len, uint64_t now_ms, uint8_t *out,
                     size_t *out_len, struct circe_event *events,
                     size_t *n_events)
{
  *n_events = 0;
  *out_len = 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;
  bool peer_in_ram
      = peer_table_lookup (remote_id_buf.buf_start, &remote_pt_entry);

  // 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_CHALLENGE
      && packet_type != CIRCE_PACKET_OPENTUNNEL && peer_in_ram)
    {
      // 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;
        }
    }

  switch (packet_type)
    {
    case CIRCE_PACKET_OPENTUNNEL:
    case CIRCE_PACKET_ACKOPEN:
    case CIRCE_PACKET_IDENTIFY:
    case CIRCE_PACKET_DATA:
      (*n_events)++;
      events[0] = (struct circe_event){
        .event_type = CIRCE_EVENT_DATA_RECEIVED,
      };
      return unseal_payload (remote_pt_entry, &cd, out, out_len);
    case CIRCE_PACKET_COMMAND:
    }
  return CIRCE_RESULT_INVALID_PACKET;
}