2 Commits 9a751205fb ... 883d0b1812

Autor SHA1 Mensaje Fecha
  Rain 883d0b1812 rewrites hace 3 días
  Rain b87f0491b1 Use sha2 library hace 1 mes
Se han modificado 75 ficheros con 1945 adiciones y 18 borrados
  1. 1 0
      .gitignore
  2. 27 0
      channel-c/Makefile
  3. 345 0
      channel-c/src/lib/aes.c
  4. 68 0
      channel-c/src/lib/allocator.c
  5. 127 0
      channel-c/src/lib/compress.c
  6. 17 0
      channel-c/src/lib/const.c
  7. 100 0
      channel-c/src/lib/http.c
  8. 17 0
      channel-c/src/lib/lib.c
  9. 319 0
      channel-c/src/lib/message.c
  10. 17 0
      channel-c/src/lib/sha256.c
  11. 48 0
      channel-c/src/lib/string.c
  12. 57 0
      channel-c/src/lib/string_op.c
  13. 13 0
      channel-c/src/lib/types.c
  14. 127 0
      channel-c/src/lib/utils.c
  15. 118 0
      channel-c/src/lib/vector.c
  16. 279 0
      channel-c/src/main.c
  17. 20 0
      channel-c/src/scratch.c
  18. 168 0
      channel-c/src/test.c
  19. 0 0
      channel-rs/Cargo.toml
  20. 0 0
      channel-rs/client/Cargo.toml
  21. 0 0
      channel-rs/client/src/actions.rs
  22. 0 0
      channel-rs/client/src/main.rs
  23. 0 0
      channel-rs/client/src/ui.rs
  24. 0 0
      channel-rs/client_cli/Cargo.toml
  25. 0 0
      channel-rs/client_cli/src/main.rs
  26. 0 0
      channel-rs/client_html/.gitignore
  27. 0 0
      channel-rs/client_html/Cargo.toml
  28. 0 0
      channel-rs/client_html/assets/index.html
  29. 0 0
      channel-rs/client_html/build.rs
  30. 0 0
      channel-rs/client_html/capabilities/default.json
  31. 0 0
      channel-rs/client_html/icons/128x128.png
  32. 0 0
      channel-rs/client_html/icons/128x128@2x.png
  33. 0 0
      channel-rs/client_html/icons/32x32.png
  34. 0 0
      channel-rs/client_html/icons/Square107x107Logo.png
  35. 0 0
      channel-rs/client_html/icons/Square142x142Logo.png
  36. 0 0
      channel-rs/client_html/icons/Square150x150Logo.png
  37. 0 0
      channel-rs/client_html/icons/Square284x284Logo.png
  38. 0 0
      channel-rs/client_html/icons/Square30x30Logo.png
  39. 0 0
      channel-rs/client_html/icons/Square310x310Logo.png
  40. 0 0
      channel-rs/client_html/icons/Square44x44Logo.png
  41. 0 0
      channel-rs/client_html/icons/Square71x71Logo.png
  42. 0 0
      channel-rs/client_html/icons/Square89x89Logo.png
  43. 0 0
      channel-rs/client_html/icons/StoreLogo.png
  44. 0 0
      channel-rs/client_html/icons/icon.icns
  45. 0 0
      channel-rs/client_html/icons/icon.ico
  46. 0 0
      channel-rs/client_html/icons/icon.png
  47. 0 0
      channel-rs/client_html/src/lib.rs
  48. 0 0
      channel-rs/client_html/src/main.rs
  49. 0 0
      channel-rs/client_html/tauri.conf.json
  50. 3 0
      channel-rs/client_shared/Cargo.toml
  51. 19 0
      channel-rs/client_shared/assets/logo-amy.txt
  52. 0 0
      channel-rs/client_shared/assets/logo-rei.txt
  53. 1 1
      channel-rs/client_shared/src/lib.rs
  54. 0 0
      channel-rs/client_shared/src/message.rs
  55. 0 0
      channel-rs/client_shared/src/persistence.rs
  56. 0 0
      channel-rs/client_shared/src/ui.rs
  57. 14 0
      channel-rs/client_shared/src/utils.rs
  58. 0 0
      channel-rs/logging/Cargo.toml
  59. 0 0
      channel-rs/logging/src/lib.rs
  60. 0 0
      channel-rs/procmacro/Cargo.toml
  61. 0 0
      channel-rs/procmacro/src/lib.rs
  62. 0 0
      channel-rs/server/Cargo.toml
  63. 2 16
      channel-rs/server/src/lib.rs
  64. 18 0
      channel-rs/server/src/main.rs
  65. 1 0
      channel-rs/utils/Cargo.toml
  66. 1 0
      channel-rs/utils/src/aes.rs
  67. 0 0
      channel-rs/utils/src/binary.rs
  68. 10 0
      channel-rs/utils/src/hash/mod.rs
  69. 8 1
      channel-rs/utils/src/hash/sha.rs
  70. 0 0
      channel-rs/utils/src/http.rs
  71. 0 0
      channel-rs/utils/src/lib.rs
  72. 0 0
      channel-rs/utils/src/rng.rs
  73. 0 0
      channel-rs/utils/src/serialize.rs
  74. 0 0
      channel-rs/utils/src/strings.rs
  75. 0 0
      channel-rs/utils/src/time.rs

+ 1 - 0
.gitignore

@@ -1,4 +1,5 @@
 target/
+build/
 Cargo.lock
 savedata.bin
 *-log.txt

+ 27 - 0
channel-c/Makefile

@@ -0,0 +1,27 @@
+APPNAME = app
+RELEASE_ARGS = -Wall -Wextra -Werror
+DEBUG_ARGS = -g -DDEBUG
+ARGS = -lcurl
+
+release: prelude
+	@gcc $(ARGS) $(RELEASE_ARGS) -o build/$(APPNAME) ./src/main.c
+	@./build/$(APPNAME)
+
+test: prelude
+	@gcc $(ARGS) $(DEBUG_ARGS) -o build/test ./src/test.c
+	@./build/test
+
+debug: prelude
+	@gcc $(ARGS) $(DEBUG_ARGS) -o build/$(APPNAME) ./src/main.c
+	@./build/$(APPNAME)
+
+lldb: prelude
+	@gcc $(ARGS) $(DEBUG_ARGS) -o build/$(APPNAME) ./src/main.c
+	@lldb ./build/$(APPNAME)
+
+scratch: prelude
+	@gcc $(ARGS) $(DEBUG_ARGS) -o build/scratch ./src/scratch.c
+	@./build/scratch
+
+prelude:
+	@mkdir -p build

+ 345 - 0
channel-c/src/lib/aes.c

@@ -0,0 +1,345 @@
+#ifndef AES_C
+#define AES_C
+
+#include "types.c"
+#include "utils.c"
+
+#define WORD_LENGTH 4
+#define ROUNDS 10
+
+#define BLOCK_LENGTH 16
+
+typedef enum aes_encryption_mode_t {
+  encryption,
+  decryption,
+} aes_encryption_mode_t;
+
+// --- PRIVATE ---
+
+static uint8_t S_BOX[256] = {
+    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b,
+    0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
+    0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26,
+    0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2,
+    0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
+    0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed,
+    0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f,
+    0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
+    0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec,
+    0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14,
+    0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
+    0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d,
+    0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f,
+    0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
+    0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11,
+    0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f,
+    0xb0, 0x54, 0xbb, 0x16,
+};
+
+static uint8_t INV_S_BOX[256] = {
+    0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E,
+    0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87,
+    0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32,
+    0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
+    0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49,
+    0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16,
+    0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50,
+    0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
+    0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05,
+    0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02,
+    0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41,
+    0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
+    0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8,
+    0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89,
+    0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B,
+    0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
+    0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59,
+    0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D,
+    0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D,
+    0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
+    0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63,
+    0x55, 0x21, 0x0C, 0x7D,
+};
+
+static uint8_t ROUND_CONSTANT[32] = {
+    0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36,
+    0x6C, 0xD8, 0xAB, 0x4D, 0x9A, 0x2F, 0x5E, 0xBC, 0x63, 0xC6, 0x97,
+    0x35, 0x6A, 0xD4, 0xB3, 0x7D, 0xFA, 0xEF, 0xC5, 0x91, 0x39,
+};
+
+static size_t aes__block_get(size_t x, size_t y) { return x * 4 + y; }
+
+static uint8_t aes__xtime(uint8_t x) {
+  if ((x & 0x80) > 0) {
+    return (uint8_t)((x << 1) ^ 0x1B) & 0xFF;
+  } else {
+    return (uint8_t)(x << 1) & 0xFF;
+  }
+}
+
+static void aes__shift_rows(uint8_t *block, aes_encryption_mode_t mode) {
+  uint8_t *word_original = malloc(sizeof(uint8_t) * WORD_LENGTH);
+
+  for (size_t word_n = 1; word_n < WORD_LENGTH; word_n++) {
+    size_t word_start_idx = word_n * WORD_LENGTH;
+
+    for (size_t word_idx = 0; word_idx < WORD_LENGTH; word_idx++) {
+      word_original[word_idx] = block[word_start_idx + word_idx];
+    }
+
+    for (size_t word_idx = 0; word_idx < WORD_LENGTH; word_idx++) {
+      if (mode == encryption) {
+        block[word_start_idx + word_idx] =
+            word_original[(word_idx + word_n) % WORD_LENGTH];
+      } else {
+        block[word_start_idx + word_idx] =
+            word_original[(word_idx - word_n) % WORD_LENGTH];
+      }
+    }
+  }
+
+  free(word_original);
+}
+
+static void aes__mix_words(uint8_t *block, aes_encryption_mode_t mode) {
+  if (mode == decryption) {
+    for (size_t idx = 0; idx < WORD_LENGTH; idx++) {
+      size_t start_idx = aes__block_get(idx, 0);
+
+      uint8_t a =
+          aes__xtime(aes__xtime(block[start_idx] ^ block[start_idx + 2]));
+      uint8_t b =
+          aes__xtime(aes__xtime(block[start_idx + 1] ^ block[start_idx + 3]));
+
+      block[start_idx + 0] ^= a;
+      block[start_idx + 2] ^= a;
+
+      block[start_idx + 1] ^= b;
+      block[start_idx + 3] ^= b;
+    }
+  }
+
+  for (size_t idx = 0; idx < WORD_LENGTH; idx++) {
+    size_t start_idx = aes__block_get(idx, 0);
+
+    uint8_t xor = block[start_idx] ^ block[start_idx + 1] ^
+                  block[start_idx + 2] ^ block[start_idx + 3];
+
+    uint8_t first = block[start_idx];
+
+    for (size_t i = 0; i < 3; i++) {
+      block[start_idx + i] ^=
+          aes__xtime(block[start_idx + i] ^ block[start_idx + i + 1]) ^ xor;
+    }
+
+    block[start_idx + 3] ^= aes__xtime(block[start_idx + 3] ^ first) ^ xor;
+  }
+}
+
+static void aes__add_round_key(uint8_t *block, uint8_t *round_key) {
+  xor_arrays(block, round_key, BLOCK_LENGTH);
+}
+
+// NOTE: malloc
+static uint8_t *aes__expand_key(uint8_t *key) {
+  // Original key + 10 round keys in an array
+  size_t round_keys_array_size = (ROUNDS + 1) * BLOCK_LENGTH;
+  uint8_t *round_keys =
+      (uint8_t *)malloc(sizeof(uint8_t) * round_keys_array_size);
+  size_t pos = 0;
+  size_t round_constant_idx = 0;
+
+  for (size_t idx = 0; idx < BLOCK_LENGTH; idx++) {
+    round_keys[idx] = key[idx];
+    pos += 1;
+  }
+
+  while (pos < round_keys_array_size) {
+    for (size_t idx = 0; idx < WORD_LENGTH; idx++) {
+      round_keys[idx + pos] = round_keys[idx + pos - WORD_LENGTH];
+    }
+
+    if (pos % BLOCK_LENGTH == 0) {
+      uint8_t first = round_keys[pos];
+      round_keys[pos] = round_keys[pos + 1];
+      round_keys[pos + 1] = round_keys[pos + 2];
+      round_keys[pos + 2] = round_keys[pos + 3];
+      round_keys[pos + 3] = first;
+
+      for (size_t idx = 0; idx < WORD_LENGTH; idx++) {
+        uint8_t value = round_keys[pos + idx];
+        round_keys[pos + idx] = S_BOX[value];
+      }
+
+      round_keys[pos] = round_keys[round_constant_idx];
+      round_constant_idx += 1;
+    }
+
+    uint8_t *previous_word = &round_keys[pos];
+    uint8_t *modifier_word = &round_keys[pos - BLOCK_LENGTH];
+    xor_arrays(previous_word, modifier_word, WORD_LENGTH);
+    pos += WORD_LENGTH;
+  }
+
+  return round_keys;
+}
+
+static void aes__encrypt_block(uint8_t *block, uint8_t *round_keys) {
+  aes__add_round_key(block, round_keys);
+  for (size_t round = 1; round <= ROUNDS; round++) {
+    for (size_t idx = 0; idx < BLOCK_LENGTH; idx++) {
+      block[idx] = S_BOX[block[idx]];
+    }
+
+    aes__shift_rows(block, encryption);
+
+    if (round != ROUNDS) {
+      aes__mix_words(block, encryption);
+    }
+
+    aes__add_round_key(block, &round_keys[round * BLOCK_LENGTH]);
+  }
+}
+
+static void aes__decrypt_block(uint8_t *block, uint8_t *round_keys) {
+  for (size_t round = ROUNDS; round >= 1; round--) {
+    aes__add_round_key(block, &round_keys[round * BLOCK_LENGTH]);
+
+    if (round != ROUNDS) {
+      aes__mix_words(block, decryption);
+    }
+
+    aes__shift_rows(block, decryption);
+
+    for (size_t idx = 0; idx < BLOCK_LENGTH; idx++) {
+      block[idx] = INV_S_BOX[block[idx]];
+    }
+  }
+
+  aes__add_round_key(block, round_keys);
+}
+
+static bool_t aes__is_valid_padding(uint8_t *block) {
+  uint8_t marker = block[BLOCK_LENGTH - 1];
+
+  if (marker == 0) {
+    return false;
+  }
+
+  for (size_t idx = BLOCK_LENGTH - marker; idx < BLOCK_LENGTH; idx++) {
+    if (block[idx] != marker) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+static byte_t *aes__pad_data(byte_t *data, size_t data_length,
+                             size_t *out_length) {
+  size_t data_length_before_pad = data_length - (data_length % BLOCK_LENGTH);
+  size_t marker = BLOCK_LENGTH - (data_length % BLOCK_LENGTH);
+
+  *out_length = data_length_before_pad + BLOCK_LENGTH;
+  byte_t *data_padded = malloc(sizeof(byte_t) * (*out_length));
+
+  memset(data_padded, (byte_t)marker, sizeof(byte_t) * (*out_length));
+  memcpy(data_padded, data, data_length);
+
+  return data_padded;
+}
+
+// --- PUBLIC ---
+
+// Old doesn't work with the new padding scheme
+// byte_t *aes_ecb(aes_encryption_mode_t mode, byte_t *data, size_t
+// *data_length,
+//                 uint8_t *key) {
+//   byte_t *data_padded;
+//   size_t data_padded_length;
+
+//   // TODO: pad data
+
+//   uint8_t *round_keys = aes__expand_key(key);
+
+//   for (size_t idx = 0; idx < data_padded.data_length_before_pad;
+//        idx += BLOCK_LENGTH) {
+//     if (mode == encryption) {
+//       aes__encrypt_block(&data_padded.data[idx], round_keys);
+//     } else {
+//       aes__decrypt_block(&data_padded.data[idx], round_keys);
+//     }
+//   }
+
+//   if (mode == encryption) {
+//     aes__encrypt_block(data_padded.padded_block, round_keys);
+//   } else {
+//     aes__decrypt_block(data_padded.padded_block, round_keys);
+//   }
+
+//   free(round_keys);
+
+//   return data_padded.data;
+// }
+
+byte_t *aes_cbc(aes_encryption_mode_t mode, byte_t *data, size_t data_length,
+                uint8_t *key, size_t *out_data_len) {
+  size_t data_padded_length;
+  byte_t *data_padded;
+  byte_t marker;
+
+  if (mode == encryption) {
+    data_padded = aes__pad_data(data, data_length, &data_padded_length);
+    marker = data_padded[data_padded_length - 1];
+  } else if (mode == decryption) {
+    data_padded = malloc(sizeof(byte_t) * data_length);
+    memcpy(data_padded, data, data_length);
+    data_padded_length = data_length;
+  } else {
+    return NULL;
+  }
+
+  uint8_t *round_keys = aes__expand_key(key);
+
+  // TODO: Derive this properly
+  uint8_t *previous_block = malloc(sizeof(uint8_t) * BLOCK_LENGTH * 2);
+  for (size_t idx = 0; idx < BLOCK_LENGTH; idx++) {
+    previous_block[idx] = 0;
+  }
+
+  for (size_t idx = 0; idx < data_padded_length; idx += BLOCK_LENGTH) {
+    if (mode == encryption) {
+      xor_arrays(&data_padded[idx], previous_block, BLOCK_LENGTH);
+      aes__encrypt_block(&data_padded[idx], round_keys);
+      clone_array(previous_block, &data_padded[idx], BLOCK_LENGTH);
+    } else {
+      clone_array(&previous_block[BLOCK_LENGTH], &data_padded[idx],
+                  BLOCK_LENGTH);
+      aes__decrypt_block(&data_padded[idx], round_keys);
+      xor_arrays(&data_padded[idx], previous_block, BLOCK_LENGTH);
+      clone_array(previous_block, &previous_block[BLOCK_LENGTH], BLOCK_LENGTH);
+    }
+  }
+
+  free(previous_block);
+  free(round_keys);
+
+  if (mode == encryption) {
+    *out_data_len = data_padded_length;
+  } else if (mode == decryption &&
+             aes__is_valid_padding(
+                 &data_padded[data_padded_length - BLOCK_LENGTH])) {
+    marker = data_padded[data_padded_length - 1];
+    *out_data_len = data_padded_length - marker;
+  } else {
+    return NULL;
+  }
+  return data_padded;
+}
+
+#endif

+ 68 - 0
channel-c/src/lib/allocator.c

@@ -0,0 +1,68 @@
+#ifndef ALLOCATOR_C
+#define ALLOCATOR_C
+
+#include "types.c"
+#include "vector.c"
+
+typedef struct {
+  vector_t *storage;
+} allocator_storage_t;
+
+allocator_storage_t allocator_storage_create(size_t size) {
+  allocator_storage_t a;
+  a.storage = vector_create(size);
+  return a;
+}
+
+void allocator_storage_free_inner_pointers(allocator_storage_t *a) {
+#ifdef DEBUG
+  printf("allocator_storage_free_inner_pointers | Freeing %zu pointers from "
+         "allocator storage\n",
+         a->storage->length);
+#endif
+
+  size_t size = a->storage->length;
+  for (size_t i = 0; i < size; i++) {
+    generic_pointer_t p = (generic_pointer_t)(a->storage->data[i]);
+    if (p != NULL) {
+      free(p);
+    }
+  }
+  vector_destroy(a->storage);
+  a->storage = vector_create(size);
+}
+
+typedef enum {
+  FAILED_TO_ADD_ITEM = -1,
+} allocator_storage_error_t;
+
+ssize_t allocator_storage_add(allocator_storage_t *a, generic_pointer_t p) {
+  if (p == NULL) {
+    return FAILED_TO_ADD_ITEM;
+  }
+
+  size_t position = a->storage->length;
+  vector_push(a->storage, p);
+
+  return position;
+}
+
+void allocator_storage_remove(allocator_storage_t *a, size_t index) {
+  vector_pop(a->storage, index);
+}
+
+#define ALLOCATOR_STORE(a_ptr, ptr)                                            \
+  allocator_storage_add(a_ptr, (generic_pointer_t)ptr);
+
+#define ALLOCATOR_STORE_OR_RETURN(a_ptr, ptr, ...)                             \
+  if (ptr == NULL) {                                                           \
+    allocator_storage_free_inner_pointers(a_ptr);                              \
+    return __VA_ARGS__;                                                        \
+  };                                                                           \
+  allocator_storage_add(a_ptr, (generic_pointer_t)ptr);
+
+#define ALLOCATOR_RETURN(a_ptr, ...)                                           \
+  allocator_storage_free_inner_pointers(a_ptr);                                \
+  return __VA_ARGS__;
+
+#endif

+ 127 - 0
channel-c/src/lib/compress.c

@@ -0,0 +1,127 @@
+#ifndef COMPRESS_C
+#define COMPRESS_C
+
+#include "types.c"
+#include <stdlib.h>
+
+// Compress data in-place and return new length
+byte_t *compress(byte_t *data, size_t length, size_t *out_compressed_len) {
+  byte_t *buffer = malloc(length * 2 * sizeof(byte_t));
+  if (buffer == NULL) {
+    return NULL;
+  }
+
+  size_t buffer_index = 0;
+  byte_t holder = 0;
+  size_t count = 0;
+  for (size_t i = 0; i < length; i++) {
+    if (i == 0) {
+      holder = data[i];
+      count = 1;
+      continue;
+    }
+
+    if (data[i] == holder) {
+      count++;
+    } else {
+      buffer[buffer_index++] = holder;
+      buffer[buffer_index++] = count;
+      holder = data[i];
+      count = 1;
+    }
+  }
+  if (count > 0) {
+    buffer[buffer_index++] = holder;
+    buffer[buffer_index++] = count;
+  }
+
+  *out_compressed_len = buffer_index;
+
+  byte_t *resized_buffer = malloc(length * sizeof(byte_t));
+  if (resized_buffer == NULL) {
+    free(buffer);
+    return NULL;
+  }
+
+  memcpy(resized_buffer, buffer, length * sizeof(byte_t));
+  free(buffer);
+  return resized_buffer;
+}
+
+byte_t *decompress(byte_t *data, size_t length,
+                   size_t *out_decompressed_length) {
+  size_t output_length = 0;
+  for (size_t i = 1; i < length; i += 2) {
+    output_length += data[i];
+  }
+
+  byte_t *buffer = malloc(output_length * sizeof(byte_t));
+  if (buffer == NULL) {
+    return NULL;
+  }
+
+  size_t buffer_index = 0;
+  for (size_t i = 1; i < length; i += 2) {
+    byte_t value = data[i - 1];
+    byte_t count = data[i];
+    for (size_t j = 0; j < count; j++) {
+      buffer[buffer_index++] = value;
+    }
+  }
+
+  *out_decompressed_length = buffer_index;
+  return buffer;
+}
+
+int test_compress() {
+  printf("--- START TEST:\t compress ---\n");
+  allocator_storage_t a = allocator_storage_create(2);
+
+  size_t length = 20;
+  byte_t *data = malloc(length * sizeof(byte_t));
+  if (data == NULL) {
+    allocator_storage_free_inner_pointers(&a);
+    return 1;
+  }
+  allocator_storage_add(&a, (generic_pointer_t)data);
+  memset(data, 0x22, 10);
+  memset(data + 10, 0x33, 10);
+
+  printf("Original data:\t\t");
+  print_array(data, length);
+
+  size_t new_data_len;
+  byte_t *new_data = compress((byte_t *)data, length, &new_data_len);
+  if (new_data == NULL) {
+    allocator_storage_free_inner_pointers(&a);
+    return 1;
+  }
+  allocator_storage_add(&a, (generic_pointer_t)new_data);
+
+  printf("Compressed data:\t");
+  print_array(new_data, new_data_len);
+
+  size_t decompressed_data_len;
+  byte_t *decompressed_data =
+      decompress(new_data, length, &decompressed_data_len);
+  if (decompressed_data == NULL) {
+    allocator_storage_free_inner_pointers(&a);
+    return 1;
+  }
+  allocator_storage_add(&a, (generic_pointer_t)decompressed_data);
+
+  printf("Decompressed data:\t");
+  print_array(decompressed_data, length);
+
+  if (memcmp(decompressed_data, data, length * sizeof(byte_t)) != 0) {
+    printf("Decompressed data does not match original data!\n");
+    allocator_storage_free_inner_pointers(&a);
+    return 1;
+  }
+
+  allocator_storage_free_inner_pointers(&a);
+  printf("--- END TEST:\t compress ---\n");
+  return 0;
+}
+
+#endif

+ 17 - 0
channel-c/src/lib/const.c

@@ -0,0 +1,17 @@
+#ifndef CONST_C
+#define CONST_C
+
+#include "types.c"
+#include <stdlib.h>
+
+const size_t MAX_SENDER_LEN = 16;
+const size_t MAX_CHANNEL_LEN = 16;
+const size_t MAX_CONTENT_LEN = 512;
+const size_t MAX_PASSWORD_LEN = 64;
+const size_t MAX_URL_LEN = 256;
+
+const char *DEFAULT_CHANNEL_NAME = "root";
+const char *DEFAULT_USER_NAME = "myst";
+const int TIMESTAMP_LEN = sizeof(timestamp_t);
+
+#endif

+ 100 - 0
channel-c/src/lib/http.c

@@ -0,0 +1,100 @@
+#ifndef HTTP_C
+#define HTTP_C
+
+#include "allocator.c"
+#include "utils.c"
+#include <curl/curl.h>
+
+typedef struct {
+  byte_t *data;
+  size_t size;
+} byte_sized_t;
+
+void byte_sized_free(byte_sized_t **b) {
+  if (b != NULL && *b != NULL) {
+    free((*b)->data);
+    (*b)->data = NULL;
+    (*b)->size = 0;
+    free(*b);
+    *b = NULL;
+  }
+}
+
+static size_t http_write_callback(void *contents, size_t size, size_t nmemb,
+                                  void *userp) {
+  size_t realsize = size * nmemb;
+  byte_sized_t **buffer = (byte_sized_t **)userp;
+
+  byte_t *new_buffer = malloc(realsize);
+  if (new_buffer == NULL) {
+    return 0;
+  }
+
+  if (*buffer == NULL) {
+    *buffer = malloc(sizeof(byte_sized_t));
+    (*buffer)->data = new_buffer;
+    (*buffer)->size = realsize;
+
+    memcpy((*buffer)->data, contents, realsize);
+  } else {
+    byte_t *combined_buffer = malloc((*buffer)->size + realsize);
+    if (combined_buffer == NULL) {
+      free(new_buffer);
+      return 0;
+    }
+
+    memcpy(combined_buffer, (*buffer)->data, (*buffer)->size);
+    memcpy(combined_buffer + (*buffer)->size, contents, realsize);
+
+    free((*buffer)->data);
+    (*buffer)->data = combined_buffer;
+    (*buffer)->size += realsize;
+
+    free(new_buffer);
+  }
+
+  return realsize;
+}
+
+byte_sized_t *http_get(char *url) {
+  byte_sized_t *buffer = NULL;
+
+  defer(curl_easy_cleanup) CURL *curl = curl_easy_init();
+  if (!curl) {
+    return NULL;
+  }
+
+  curl_easy_setopt(curl, CURLOPT_URL, url);
+  curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5L);
+
+  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_write_callback);
+  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
+  curl_easy_setopt(curl, CURLOPT_HTTP_TRANSFER_DECODING, 0);
+
+  CURLcode res = curl_easy_perform(curl);
+  if (res != CURLE_OK) {
+    return NULL;
+  }
+
+  return buffer;
+}
+
+int http_post(const char *url, char *data) {
+  defer(curl_easy_cleanup) CURL *curl = curl_easy_init();
+  if (!curl) {
+    return 1;
+  }
+
+  curl_easy_setopt(curl, CURLOPT_URL, url);
+  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
+  curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5L);
+
+  CURLcode res = curl_easy_perform(curl);
+  if (res != CURLE_OK) {
+    return 1;
+  }
+
+  return 0;
+}
+
+#endif

+ 17 - 0
channel-c/src/lib/lib.c

@@ -0,0 +1,17 @@
+#ifndef LIB_C
+#define LIB_C
+
+#include "aes.c"
+#include "allocator.c"
+#include "compress.c"
+#include "const.c"
+#include "http.c"
+#include "message.c"
+#include "sha256.c"
+#include "string.c"
+#include "string_op.c"
+#include "types.c"
+#include "utils.c"
+#include "vector.c"
+
+#endif

+ 319 - 0
channel-c/src/lib/message.c

@@ -0,0 +1,319 @@
+#ifndef MESSAGE_C
+#define MESSAGE_C
+
+#include "aes.c"
+#include "allocator.c"
+#include "compress.c"
+#include "const.c"
+#include "sha256.c"
+#include "types.c"
+#include "utils.c"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct {
+  char *sender;
+  char *content;
+  char *channel;
+  timestamp_t timestamp;
+} message_t;
+
+message_t *message_create(char *sender, char *channel, char *content) {
+  message_t *msg = malloc(sizeof(message_t));
+  if (msg == NULL) {
+    return NULL;
+  }
+
+  msg->sender = strndup(sender, MAX_SENDER_LEN);
+  msg->channel = strndup(channel, MAX_CHANNEL_LEN);
+  msg->content = strndup(content, MAX_CONTENT_LEN);
+  msg->timestamp = get_current_timestamp();
+  return msg;
+}
+
+void message_inner_free(message_t *msg) {
+  if (msg->sender != NULL) {
+    free(msg->sender);
+  }
+
+  if (msg->content != NULL) {
+    free(msg->content);
+  }
+
+  if (msg->channel != NULL) {
+    free(msg->channel);
+  }
+
+  if (msg != NULL) {
+    free(msg);
+  }
+}
+
+// malloc
+byte_t *message_serialize(message_t *msg, size_t *out_len) {
+  const size_t sender_len = strlen(msg->sender);
+  const size_t content_len = strlen(msg->content);
+
+  if (sender_len > MAX_SENDER_LEN) {
+    return NULL;
+  }
+  if (content_len > MAX_CONTENT_LEN) {
+    return NULL;
+  }
+
+  byte_t *buffer = malloc(
+      (TIMESTAMP_LEN + MAX_SENDER_LEN + MAX_CONTENT_LEN + MAX_CHANNEL_LEN) *
+      sizeof(byte_t));
+  if (buffer == NULL) {
+    return NULL;
+  }
+  memset(buffer, 0,
+         TIMESTAMP_LEN + MAX_SENDER_LEN + MAX_CONTENT_LEN + MAX_CHANNEL_LEN);
+
+  memcpy(buffer + TIMESTAMP_LEN, msg->sender, sender_len);
+  memcpy(buffer + TIMESTAMP_LEN + MAX_SENDER_LEN, msg->content, content_len);
+  memcpy(buffer + TIMESTAMP_LEN + MAX_SENDER_LEN + MAX_CONTENT_LEN,
+         msg->channel, strlen(msg->channel));
+  memcpy(buffer, &msg->timestamp, TIMESTAMP_LEN);
+  *out_len = TIMESTAMP_LEN + MAX_SENDER_LEN + MAX_CONTENT_LEN + MAX_CHANNEL_LEN;
+  return buffer;
+}
+
+// malloc
+message_t *message_deserialize(byte_t *buffer, size_t buffer_len) {
+  if (buffer_len !=
+      TIMESTAMP_LEN + MAX_SENDER_LEN + MAX_CONTENT_LEN + MAX_CHANNEL_LEN) {
+#ifdef DEBUG
+    printf("<!> Invalid buffer length for deserialization: %zu not %zu\n",
+           buffer_len,
+           TIMESTAMP_LEN + MAX_SENDER_LEN + MAX_CONTENT_LEN + MAX_CHANNEL_LEN);
+#endif
+    return NULL;
+  }
+
+  message_t *msg = malloc(sizeof(message_t));
+  if (msg == NULL) {
+    return NULL;
+  }
+
+  msg->sender = malloc(sizeof(char) * MAX_SENDER_LEN);
+  msg->content = malloc(sizeof(char) * MAX_CONTENT_LEN);
+  msg->channel = malloc(sizeof(char) * MAX_CHANNEL_LEN);
+  if (msg->sender == NULL) {
+    free(msg);
+    return NULL;
+  }
+  if (msg->content == NULL) {
+    free(msg->sender);
+    free(msg);
+    return NULL;
+  }
+  if (msg->channel == NULL) {
+    free(msg->sender);
+    free(msg->content);
+    free(msg);
+    return NULL;
+  }
+
+  memcpy(msg->sender, buffer + TIMESTAMP_LEN, MAX_SENDER_LEN);
+  memcpy(msg->content, buffer + TIMESTAMP_LEN + MAX_SENDER_LEN,
+         MAX_CONTENT_LEN);
+  memcpy(msg->channel,
+         buffer + TIMESTAMP_LEN + MAX_SENDER_LEN + MAX_CONTENT_LEN,
+         MAX_CHANNEL_LEN);
+  memcpy(&msg->timestamp, buffer, TIMESTAMP_LEN);
+
+  return msg;
+}
+
+char *message_to_transmittable(message_t *msg, bool_t do_compression,
+                               char *password, size_t *out_len) {
+  // Serialize
+  size_t serialized_msg_len = 0;
+  defer(free_checked) byte_t *serialized_msg =
+      message_serialize(msg, &serialized_msg_len);
+  if (serialized_msg == NULL) {
+#ifdef DEBUG
+    printf("<!> Failed to serialize message\n");
+#endif
+    return NULL;
+  }
+
+  // Compress
+  byte_t *compressed_msg;
+  size_t compressed_msg_len = 0;
+  if (do_compression) {
+    compressed_msg =
+        compress(serialized_msg, serialized_msg_len, &compressed_msg_len);
+    if (compressed_msg == NULL) {
+#ifdef DEBUG
+      printf("<!> Failed to compress message\n");
+#endif
+      return NULL;
+    }
+  } else {
+    compressed_msg = serialized_msg;
+    compressed_msg_len = serialized_msg_len;
+  }
+
+  // Encrypt
+  defer(free_checked) byte_t *hashed_password =
+      sha256((byte_t *)password, strlen(password));
+  if (hashed_password == NULL) {
+#ifdef DEBUG
+    printf("<!> Failed to hash password\n");
+#endif
+    return NULL;
+  }
+
+  size_t encrypted_msg_len = 0;
+  defer(free_checked) byte_t *encrypted_msg =
+      aes_cbc(encryption, compressed_msg, compressed_msg_len, hashed_password,
+              &encrypted_msg_len);
+  if (encrypted_msg == NULL) {
+#ifdef DEBUG
+    printf("<!> Failed to encrypt message\n");
+#endif
+    return NULL;
+  }
+
+  if (do_compression) {
+    free(compressed_msg);
+  }
+
+  // Checksum
+  defer(free_checked) byte_t *checksummed_msg =
+      malloc(sizeof(byte_t) * (encrypted_msg_len + 1));
+  if (checksummed_msg == NULL) {
+#ifdef DEBUG
+    printf("<!> Failed to allocate checksummed message\n");
+#endif
+    return NULL;
+  }
+  memcpy(checksummed_msg, encrypted_msg, encrypted_msg_len);
+  checksummed_msg[encrypted_msg_len] =
+      checksum(encrypted_msg, encrypted_msg_len);
+
+  // Hex
+  char *msg_transmittable = btoh(checksummed_msg, encrypted_msg_len + 1);
+  if (msg_transmittable == NULL) {
+#ifdef DEBUG
+    printf("<!> Failed to convert message to hex\n");
+#endif
+    return NULL;
+  }
+
+  *out_len = encrypted_msg_len + 1;
+  return msg_transmittable;
+}
+
+message_t *message_from_transmittable(char *transmittable,
+                                      bool_t do_compression, char *password) {
+  // Hex to bin
+  size_t checked_msg_len = 0;
+  byte_t *checked_msg = htob(transmittable, &checked_msg_len);
+  if (checked_msg == NULL) {
+#ifdef DEBUG
+    printf("<!> Failed to convert transmittable from hex\n");
+#endif
+    return NULL;
+  }
+
+  // Checksum
+  byte_t sum = checksum(checked_msg, checked_msg_len - 1);
+  if (sum != checked_msg[checked_msg_len - 1]) {
+#ifdef DEBUG
+    printf("<!> Checksum mismatch for transmittable message\n");
+#endif
+    return NULL;
+  }
+
+  // Decrypt
+  byte_t *hashed_password = sha256((byte_t *)password, strlen(password));
+  if (hashed_password == NULL) {
+#ifdef DEBUG
+    printf("<!> Failed to hash password\n");
+#endif
+    return NULL;
+  }
+
+  size_t decrypted_msg_len = 0;
+  byte_t *decrypted_msg = aes_cbc(decryption, checked_msg, checked_msg_len - 1,
+                                  hashed_password, &decrypted_msg_len);
+  if (decrypted_msg == NULL) {
+#ifdef DEBUG
+    printf("<!> Failed to decrypt message\n");
+#endif
+    return NULL;
+  }
+
+  // Decompress
+  byte_t *decompressed_msg;
+  size_t decompressed_msg_len = 0;
+
+  if (do_compression) {
+    decompressed_msg =
+        decompress(decrypted_msg, decrypted_msg_len, &decompressed_msg_len);
+    if (decompressed_msg == NULL) {
+#ifdef DEBUG
+      printf("<!> Failed to decompress message\n");
+#endif
+      return NULL;
+    }
+  } else {
+    decompressed_msg = decrypted_msg;
+    decompressed_msg_len = decrypted_msg_len;
+  }
+
+  // Deserialize
+  message_t *msg = message_deserialize(decompressed_msg, decompressed_msg_len);
+  if (msg == NULL) {
+#ifdef DEBUG
+    printf("<!> Failed to deserialize message\n");
+#endif
+    return NULL;
+  }
+
+  return msg;
+}
+
+void message_print(message_t *msg) {
+  printf("[%llu] %s#%s: %s\n", msg->timestamp, msg->sender, msg->channel,
+         msg->content);
+}
+
+int test_message() {
+  printf("--- START TEST:\t message ---\n");
+
+  message_t *msg = message_create("Alice", "root", "Hello, Bob!");
+  printf("Original message:\t");
+  message_print(msg);
+
+  size_t out_transmittable_len;
+  char *transmittable =
+      message_to_transmittable(msg, false, "password", &out_transmittable_len);
+  if (transmittable == NULL) {
+    return 1;
+  }
+  printf("Transmittable message:\t%s\n", transmittable);
+
+  message_t *from_transmittable_msg =
+      message_from_transmittable(transmittable, false, "password");
+  printf("From transmittable message:\t");
+  message_print(from_transmittable_msg);
+
+  if (strcmp(msg->sender, from_transmittable_msg->sender) != 0 ||
+      strcmp(msg->content, from_transmittable_msg->content) != 0 ||
+      strcmp(msg->channel, from_transmittable_msg->channel) != 0 ||
+      msg->timestamp != from_transmittable_msg->timestamp) {
+
+    return 4;
+  }
+
+  printf("--- END TEST:\t message ---\n");
+  return 0;
+}
+
+#endif

+ 17 - 0
channel-c/src/lib/sha256.c

@@ -0,0 +1,17 @@
+#ifndef SHA256_C
+#define SHA256_C
+
+#include "types.c"
+#include <CommonCrypto/CommonDigest.h>
+#include <string.h>
+
+byte_t *sha256(byte_t *data, size_t len) {
+  byte_t *hash = malloc(CC_SHA256_DIGEST_LENGTH);
+  if (hash == NULL) {
+    return NULL;
+  }
+  CC_SHA256(data, (CC_LONG)len, hash);
+  return hash;
+}
+
+#endif

+ 48 - 0
channel-c/src/lib/string.c

@@ -0,0 +1,48 @@
+#ifndef STRING_C
+#define STRING_C
+
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct {
+  char *data;
+  size_t length;
+} string_t;
+
+string_t string_create(const char *cstr) {
+  string_t s;
+  s.length = strlen(cstr);
+  s.data = malloc((s.length + 1) * sizeof(char));
+  if (s.data != NULL) {
+    strcpy(s.data, cstr);
+  }
+  return s;
+}
+
+void string_free(string_t *s) {
+  if (s->data != NULL) {
+    free(s->data);
+    s->data = NULL;
+    s->length = 0;
+  }
+}
+
+string_t string_adapt(char *cstr) {
+  string_t s;
+  s.length = strlen(cstr);
+  s.data = cstr;
+  return s;
+}
+
+string_t string_concat(string_t s1, string_t s2) {
+  string_t result;
+  result.length = s1.length + s2.length;
+  result.data = malloc((result.length + 1) * sizeof(char));
+  if (result.data != NULL) {
+    strcpy(result.data, s1.data);
+    strcat(result.data, s2.data);
+  }
+  return result;
+}
+
+#endif

+ 57 - 0
channel-c/src/lib/string_op.c

@@ -0,0 +1,57 @@
+#ifndef STRING_OP_C
+#define STRING_OP_C
+
+#include "vector.c"
+#include <stdlib.h>
+#include <string.h>
+
+vector_t *split_string(char *str, char delimiter) {
+  vector_t *result = vector_create(1);
+  size_t start = 0;
+  size_t len = strlen(str);
+
+  for (size_t i = 0; i <= len; i++) {
+    if (str[i] == delimiter || str[i] == '\0') {
+      size_t part_len = i - start;
+      char *part = malloc((part_len + 1) * sizeof(char));
+      if (part == NULL) {
+        vector_destroy(result);
+        return NULL;
+      }
+      strncpy(part, &str[start], part_len);
+      part[part_len] = '\0';
+      if (vector_push(result, part) != 0) {
+        free(part);
+        vector_destroy(result);
+        return NULL;
+      }
+      start = i + 1;
+    }
+  }
+
+  return result;
+}
+
+int test_split_string() {
+  printf("--- START TEST:\t split_string ---\n");
+  char *test_str = "hello,world,this,is,a,test";
+  vector_t *parts = split_string(test_str, ',');
+  if (parts == NULL) {
+    printf("split_string returned NULL\n");
+    return 1;
+  }
+
+  printf("Split parts:\n");
+  for (size_t i = 0; i < parts->length; i++) {
+    char *part = (char *)vector_get(parts, i);
+    printf("Part %zu: %s\n", i, part);
+    free(part);
+  }
+  vector_destroy(parts);
+
+  printf("--- END TEST:\t split_string ---\n");
+
+  return 0;
+}
+
+#endif

+ 13 - 0
channel-c/src/lib/types.c

@@ -0,0 +1,13 @@
+#ifndef TYPES_C
+#define TYPES_C
+
+typedef unsigned long long timestamp_t;
+typedef unsigned char byte_t;
+typedef void *generic_pointer_t;
+
+typedef enum {
+  true = 1,
+  false = 0,
+} bool_t;
+
+#endif

+ 127 - 0
channel-c/src/lib/utils.c

@@ -0,0 +1,127 @@
+#ifndef UTILS_C
+#define UTILS_C
+
+#include <sys/time.h>
+
+#include "string.c"
+#include "types.c"
+#include "vector.c"
+#include <stdio.h>
+
+void clear_screen() { printf("\033[H\033[J"); }
+
+#define defer(func) __attribute__((cleanup(func)))
+
+void pause() { getchar(); }
+
+void free_checked(void *ptr) {
+  void **p = (void **)ptr;
+  if (*p != NULL) {
+    free(*p);
+    *p = NULL;
+  }
+}
+
+byte_t checksum(byte_t *data, size_t len) {
+  int sum = 0;
+  for (size_t i = 0; i < len; i++) {
+    sum += data[i];
+  }
+  return (byte_t)(sum & 0xFF);
+}
+
+timestamp_t get_current_timestamp() {
+  struct timeval tv;
+  gettimeofday(&tv, NULL);
+  return (timestamp_t)(tv.tv_sec * 1000 + tv.tv_usec / 1000);
+}
+
+// malloc
+char *btoh(byte_t *bytes, size_t len) {
+  const char hex_chars[] = "0123456789abcdef";
+  char *hex_str = malloc((len * 2 + 1) * sizeof(char));
+  if (hex_str == NULL) {
+    return NULL;
+  }
+  for (size_t i = 0; i < len; i++) {
+    hex_str[i * 2] = hex_chars[(bytes[i] >> 4) & 0x0F];
+    hex_str[i * 2 + 1] = hex_chars[bytes[i] & 0x0F];
+  }
+  hex_str[len * 2] = '\0';
+  return hex_str;
+}
+
+// malloc
+byte_t *htob(char *hex_str, size_t *out_len) {
+  size_t len = strlen(hex_str);
+  if (len % 2 != 0) {
+    return NULL;
+  }
+  *out_len = len / 2;
+
+  byte_t *bytes = malloc(*out_len * sizeof(byte_t));
+  if (bytes == NULL) {
+    return NULL;
+  }
+
+  for (size_t i = 0; i < len; i += 2) {
+    char high_nibble = hex_str[i];
+    char low_nibble = hex_str[i + 1];
+    bytes[i / 2] =
+        ((high_nibble <= '9' ? high_nibble - '0' : high_nibble - 'a' + 10)
+         << 4) |
+        (low_nibble <= '9' ? low_nibble - '0' : low_nibble - 'a' + 10);
+  }
+  return bytes;
+}
+
+void print_array(byte_t *arr, size_t len) {
+  printf("[");
+  for (size_t i = 0; i < len; i++) {
+    printf("%u", arr[i]);
+    if (i < len - 1) {
+      printf(", ");
+    }
+  }
+  printf("]\n");
+}
+
+// --- ARRAY FUNCTIONS ---
+
+int arrays_eq(uint8_t *lhs, uint8_t *rhs, size_t length) {
+  for (size_t i = 0; i < length; i++) {
+    int cmp = lhs[i] == rhs[i];
+    if (cmp == 0) {
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+void xor_arrays(uint8_t *target, uint8_t *modifier, size_t length) {
+  for (size_t idx = 0; idx < length; idx++) {
+    target[idx] ^= modifier[idx];
+  }
+}
+
+void clone_array(uint8_t *target, uint8_t *modifier, size_t length) {
+  for (size_t idx = 0; idx < length; idx++) {
+    target[idx] = modifier[idx];
+  }
+}
+
+void print_int_array(uint8_t *array, size_t length) {
+  printf("{ ");
+  for (size_t i = 0; i < length; i++) {
+    printf("%d", array[i]);
+    if (i != (length - 1)) {
+      printf(", ");
+    }
+  }
+  printf(" }");
+}
+
+// --- END OF ARRAY FUNCTIONS ---
+
+#endif

+ 118 - 0
channel-c/src/lib/vector.c

@@ -0,0 +1,118 @@
+#ifndef VECTOR_C
+#define VECTOR_C
+
+#include "utils.c"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct {
+  size_t capacity;
+  size_t length;
+  generic_pointer_t *data;
+} vector_t;
+
+vector_t *vector_create(size_t initial_capacity) {
+  vector_t *vec = malloc(sizeof(vector_t));
+  vec->capacity = initial_capacity;
+  vec->length = 0;
+  vec->data = malloc(sizeof(generic_pointer_t) * initial_capacity);
+  return vec;
+}
+
+int vector_push(vector_t *vec, generic_pointer_t item) {
+  if (vec->length >= vec->capacity) {
+    vec->capacity *= 2;
+    vec->data = realloc(vec->data, sizeof(generic_pointer_t) * vec->capacity);
+    if (vec->data == NULL) {
+      printf("<!> Failed to reallocate vector data\n");
+      return -1;
+    }
+  }
+  vec->data[vec->length++] = item;
+  return 0;
+}
+
+generic_pointer_t vector_pop_at(vector_t *vec, size_t index) {
+  if (index >= vec->length) {
+    return NULL;
+  }
+
+  generic_pointer_t item = vec->data[index];
+  vec->data[index] = NULL;
+
+  if (index == vec->length - 1) {
+    vec->length--;
+  }
+
+  return item;
+}
+
+generic_pointer_t vector_pop_last(vector_t *vec) {
+  if (vec->length == 0) {
+    return NULL;
+  }
+  return vector_pop_at(vec, vec->length - 1);
+}
+
+#define vector_pop_select(_1, _2, NAME, ...) NAME
+#define vector_pop(...)                                                        \
+  vector_pop_select(__VA_ARGS__, vector_pop_at, vector_pop_last)(__VA_ARGS__)
+
+generic_pointer_t vector_get(vector_t *vec, size_t index) {
+  if (index < vec->length) {
+    return vec->data[index];
+  }
+  return NULL;
+}
+
+void vector_clear(vector_t *vec) {
+  for (size_t i = 0; i < vec->length; i++) {
+    vec->data[i] = NULL;
+  }
+  vec->length = 0;
+}
+
+void vector_destroy(vector_t *vec) {
+  free(vec->data);
+  vec->data = NULL;
+  vec->capacity = 0;
+  vec->length = 0;
+}
+
+int test_vector() {
+  printf("--- START TEST:\t vector ---\n");
+  vector_t *vec = vector_create(2);
+
+  vector_push(vec, (generic_pointer_t) "Hello");
+  vector_push(vec, (generic_pointer_t) "World");
+  vector_push(vec, (generic_pointer_t) "!");
+
+  for (size_t i = 0; i < vec->length; i++) {
+    printf("Item %zu: %s\n", i, (char *)vector_get(vec, i));
+  }
+
+  if (strcmp((char *)vector_pop(vec), "!") != 0) {
+    printf("Expected to pop '!' from vector!\n");
+    vector_destroy(vec);
+    return 1;
+  }
+
+  if (strcmp((char *)vector_pop(vec), "World") != 0) {
+    printf("Expected to pop 'World' from vector!\n");
+    vector_destroy(vec);
+    return 1;
+  }
+
+  if (strcmp((char *)vector_pop(vec), "Hello") != 0) {
+    printf("Expected to pop 'Hello' from vector!\n");
+    vector_destroy(vec);
+    return 1;
+  }
+
+  vector_destroy(vec);
+  printf("--- END TEST:\t vector ---\n");
+  return 0;
+}
+
+#endif

+ 279 - 0
channel-c/src/main.c

@@ -0,0 +1,279 @@
+#include <stdio.h>
+
+#include "./lib/lib.c"
+
+typedef struct {
+  char *nickname;
+  char *channel;
+  bool_t do_compression;
+  char *server_password;
+  char *server_url;
+  vector_t *cached_messages;
+  int return_code;
+} appdata_t;
+
+typedef enum {
+  ERROR_FAILED_TO_SERIALIZE_MESSAGE,
+  ERROR_FAILED_TO_COMPRESS_MESSAGE,
+  ERROR_FAILED_TO_DECOMPRESS_MESSAGE,
+} error_t;
+
+vector_t *rx_messages(char *server_url, char *server_password,
+                      bool_t do_compression) {
+  vector_t *parsed_messages = vector_create(1);
+  defer(byte_sized_free) byte_sized_t *data = http_get(server_url);
+  if (data == NULL) {
+#ifdef DEBUG
+    printf("<!> Failed to get data from server\n");
+#endif
+    return parsed_messages;
+  }
+
+#ifdef DEBUG
+  printf("Received data from server: %zu\n", data->size);
+#endif
+
+  vector_t *received_messages = split_string((char *)data->data, ',');
+  if (received_messages == NULL) {
+#ifdef DEBUG
+    printf("<!> Failed to split received data into messages\n");
+#endif
+    return parsed_messages;
+  }
+
+  for (size_t i = 0; i < received_messages->length; i++) {
+    char *received_message = (char *)vector_get(received_messages, i);
+    message_t *msg = message_from_transmittable(
+        received_message, do_compression, server_password);
+    if (msg == NULL) {
+#ifdef DEBUG
+      printf("<!> Failed to parse received message: %s\n", received_message);
+#endif
+      continue;
+    }
+
+#ifdef DEBUG
+    printf("<@> Parsed message: %s\n", received_message);
+#endif
+
+    if (vector_push(parsed_messages, msg) != 0) {
+#ifdef DEBUG
+      printf("<!> Failed to push parsed message: %s\n", received_message);
+#endif
+    }
+  }
+
+  return parsed_messages;
+}
+
+// --- START - COMMANDS ---
+
+typedef struct {
+  char *command;
+  char *arg1;
+} command_t;
+
+command_t *parse_command(char *input, size_t input_len) {
+  command_t *cmd = malloc(sizeof(command_t));
+  if (cmd == NULL) {
+    return NULL;
+  }
+  cmd->command = NULL;
+  cmd->arg1 = NULL;
+
+  if (input_len < 2 || input[0] != '/') {
+    free(cmd);
+    return NULL;
+  }
+
+  size_t command_name_len = 0;
+  size_t idx = 1;
+  for (; idx < input_len; idx++) {
+    if (input[idx] == ' ') {
+      command_name_len = idx;
+      break;
+    }
+  }
+  command_name_len = idx;
+
+  char *command_name = malloc(command_name_len);
+  if (command_name == NULL) {
+    free(cmd);
+    return NULL;
+  }
+  memcpy(command_name, input + 1, command_name_len - 1);
+  command_name[command_name_len] = '\0';
+  cmd->command = command_name;
+
+  size_t command_arg_1_len = 0;
+  char *command_arg_1 = NULL;
+  idx = command_name_len + 1;
+  for (; idx < input_len; idx++) {
+    if (input[idx] == ' ') {
+      command_arg_1_len = idx;
+      break;
+    }
+  }
+  command_arg_1_len = idx;
+
+  if (command_arg_1_len > command_name_len + 1) {
+    command_arg_1 = malloc(command_arg_1_len - command_name_len);
+    if (command_arg_1 == NULL) {
+      free(cmd);
+      free(command_name);
+      return NULL;
+    }
+    memcpy(command_arg_1, input + command_name_len + 1,
+           command_arg_1_len - command_name_len - 1);
+    command_arg_1[command_arg_1_len - command_name_len - 1] = '\0';
+  }
+  cmd->arg1 = command_arg_1;
+
+  return cmd;
+}
+
+void execute_command(appdata_t *appdata, char *input, size_t input_len) {
+  command_t *cmd = parse_command(input, input_len);
+  if (cmd == NULL) {
+    return;
+  }
+
+  if (cmd->command == NULL) {
+    printf("! Invalid command: %.*s\n", (int)input_len, input);
+    goto execute_command_end;
+  }
+
+  if (strcmp(cmd->command, "join") == 0) {
+    if (cmd->arg1 == NULL) {
+      printf("! Usage: /join <channel>\n");
+      goto execute_command_end;
+    } else if (strlen(cmd->arg1) > MAX_CHANNEL_LEN) {
+      printf("! Channel name too long: %s\n", cmd->arg1);
+      goto execute_command_end;
+    } else {
+      printf("Joining channel: %s\n", cmd->arg1);
+      strncpy(appdata->channel, cmd->arg1, MAX_CHANNEL_LEN);
+    }
+  } else if (strcmp(cmd->command, "nick") == 0) {
+    if (cmd->arg1 == NULL) {
+      printf("! Usage: /nick <new_nickname>\n");
+      goto execute_command_end;
+    } else if (strlen(cmd->arg1) > MAX_SENDER_LEN) {
+      printf("! Nickname too long: %s\n", cmd->arg1);
+      goto execute_command_end;
+    } else {
+      printf("Changing nickname to: %s\n", cmd->arg1);
+      strncpy(appdata->nickname, cmd->arg1, MAX_SENDER_LEN);
+    }
+  } else if (strcmp(cmd->command, "exit") == 0) {
+    printf("Exiting...\n");
+    appdata->return_code = 0;
+  } else {
+    printf("! Unknown command: %s\n", cmd->command);
+  }
+
+execute_command_end:
+  if (cmd->command != NULL) {
+    free(cmd->command);
+  }
+  if (cmd->arg1 != NULL) {
+    free(cmd->arg1);
+  }
+  free(cmd);
+  return;
+}
+
+// ---  END  - COMMANDS ---
+
+void update(appdata_t *appdata) {
+  clear_screen();
+
+  // Get messages
+  vector_t *messages = rx_messages(
+      appdata->server_url, appdata->server_password, appdata->do_compression);
+  if (messages == NULL) {
+    printf("<!> Failed to receive messages from server\n");
+    return;
+  }
+
+#ifdef DEBUG
+  printf("---\nReceived %zu messages\n", messages->length);
+#endif
+  for (size_t i = 0; i < messages->length; i++) {
+    message_t *msg = (message_t *)vector_get(messages, i);
+    if (msg != NULL) {
+      message_print(msg);
+      message_inner_free(msg);
+    }
+  }
+  vector_destroy(messages);
+
+  // Hud
+  printf("---\n");
+  printf("Nickname: %s\n", appdata->nickname);
+  printf("Channel: %s\n", appdata->channel);
+  printf("Enter a message (or command)\n");
+
+  // Input
+  printf("> ");
+  char *input = NULL;
+  size_t input_len = 0;
+  ssize_t read = getline(&input, &input_len, stdin);
+  if (read < 2) {
+    free(input);
+    return;
+  }
+  input[read - 1] = '\0';
+
+  message_t *msg = message_create(appdata->nickname, appdata->channel, input);
+  if (msg == NULL) {
+    printf("<!> Invalid message\n");
+    return;
+  }
+
+  // Execute
+  if (input[0] == '/') {
+    execute_command(appdata, input, input_len);
+  } else {
+    size_t serialized_msg_len;
+    defer(free_checked) char *serialized_msg =
+        message_to_transmittable(msg, appdata->do_compression,
+                                 appdata->server_password, &serialized_msg_len);
+
+    if (serialized_msg == NULL) {
+      printf("<!> Failed to send message\n");
+    }
+
+    http_post(appdata->server_url, serialized_msg);
+  }
+  free(input);
+}
+
+int main() {
+  allocator_storage_t a = allocator_storage_create(24);
+
+  appdata_t appdata = {
+      .nickname = malloc(MAX_SENDER_LEN),
+      .channel = malloc(MAX_CHANNEL_LEN),
+      .do_compression = false,
+      .server_password = malloc(MAX_PASSWORD_LEN),
+      .server_url = malloc(MAX_URL_LEN),
+      .cached_messages = NULL,
+      .return_code = -1,
+  };
+  ALLOCATOR_STORE_OR_RETURN(&a, appdata.nickname, 1);
+  ALLOCATOR_STORE_OR_RETURN(&a, appdata.channel, 1);
+  ALLOCATOR_STORE_OR_RETURN(&a, appdata.server_password, 1);
+  ALLOCATOR_STORE_OR_RETURN(&a, appdata.server_url, 1);
+  strncpy(appdata.nickname, "myst", MAX_SENDER_LEN);
+  strncpy(appdata.channel, "root", MAX_CHANNEL_LEN);
+  strncpy(appdata.server_password, "null", MAX_PASSWORD_LEN);
+  strncpy(appdata.server_url, "http://localhost:13337", MAX_URL_LEN);
+
+  while (appdata.return_code != 0) {
+    update(&appdata);
+  }
+
+  allocator_storage_free_inner_pointers(&a);
+  return 0;
+}

+ 20 - 0
channel-c/src/scratch.c

@@ -0,0 +1,20 @@
+
+#include "./lib/lib.c"
+
+int main() {
+  defer(allocator_storage_free_inner_pointers) allocator_storage_t a =
+      allocator_storage_create(8);
+
+  byte_sized_t *data = http_get("http://localhost:13337");
+  if (data == NULL) {
+    printf("Failed to get data from server\n");
+    return 1;
+  } else {
+    printf("Received %zu bytes from server: %.*s\n", data->size,
+           (int)data->size, data->data);
+  }
+
+  free(data);
+
+  return 0;
+}

+ 168 - 0
channel-c/src/test.c

@@ -0,0 +1,168 @@
+#include "./lib/lib.c"
+#include <stdio.h>
+
+void _test_defer_cleanup(int *x) { printf("Deferred: x = %d\n", *x); }
+int test_defer() {
+  printf("--- START TEST:\t defer ---\n");
+  {
+    defer(_test_defer_cleanup) int x = 42;
+    printf("Inside block: x = %d\n", x);
+  }
+  printf("--- END TEST:\t defer ---\n");
+  return 0;
+}
+
+int test_message_encryption() {
+  printf("--- START TEST:\t message_encryption ---\n");
+
+  allocator_storage_t a = allocator_storage_create(10);
+
+  message_t *msg = message_create("Alice", "root", "Hello, Bob!");
+
+  // Serialize
+  size_t msg_serialized_len;
+  byte_t *msg_serialized = message_serialize(msg, &msg_serialized_len);
+  ALLOCATOR_STORE_OR_RETURN(&a, msg_serialized, 1);
+
+  // Encrypt
+  byte_t *key = (byte_t *)"password";
+  size_t msg_serialized_encrypted_len;
+  byte_t *msg_serialized_encrypted =
+      aes_cbc(encryption, msg_serialized, msg_serialized_len, key,
+              &msg_serialized_encrypted_len);
+  ALLOCATOR_STORE_OR_RETURN(&a, msg_serialized_encrypted, 2);
+
+  // Decrypt
+  size_t msg_serialized_decrypted_len;
+  byte_t *msg_serialized_decrypted =
+      aes_cbc(decryption, msg_serialized_encrypted,
+              msg_serialized_encrypted_len, key, &msg_serialized_decrypted_len);
+  ALLOCATOR_STORE_OR_RETURN(&a, msg_serialized_decrypted, 3);
+
+  // Deserialize
+  message_t *msg_deserialized = message_deserialize(
+      msg_serialized_decrypted, msg_serialized_decrypted_len);
+
+  printf("Message: ");
+  message_print(msg);
+  printf("Serialized message: %s\n", btoh(msg_serialized, msg_serialized_len));
+  printf("Serialized message: %s\n",
+         btoh(msg_serialized_encrypted, msg_serialized_encrypted_len));
+  printf("Serialized message: %s\n",
+         btoh(msg_serialized_decrypted, msg_serialized_decrypted_len));
+  printf("Deserialized message: ");
+  message_print(msg_deserialized);
+
+  // Message == Decrypted deserialized message
+  if (strcmp(msg->sender, msg_deserialized->sender) != 0 ||
+      strcmp(msg->content, msg_deserialized->content) != 0 ||
+      msg->timestamp != msg_deserialized->timestamp) {
+    printf("Decrypted+deserialized message does not match original "
+           "message!\n");
+    allocator_storage_free_inner_pointers(&a);
+    message_inner_free(msg);
+    message_inner_free(msg_deserialized);
+    return 1;
+  }
+
+  // Enc != Dec
+  if (memcmp(msg_serialized, msg_serialized_encrypted, msg_serialized_len) ==
+      0) {
+    printf("Encrypted message should not match original serialized message!\n");
+    allocator_storage_free_inner_pointers(&a);
+    message_inner_free(msg);
+    message_inner_free(msg_deserialized);
+    return 1;
+  }
+
+  allocator_storage_free_inner_pointers(&a);
+  message_inner_free(msg);
+  message_inner_free(msg_deserialized);
+  printf("--- END TEST:\t compress_message ---\n");
+  return 0;
+}
+
+int test_compress_message() {
+  printf("--- START TEST:\t compress_message ---\n");
+  allocator_storage_t a = allocator_storage_create(2);
+
+  message_t *msg = message_create("Alice", "root", "Hello, Bob!");
+  printf("Original message:\t");
+  message_print(msg);
+
+  size_t ser_len;
+  byte_t *ser = message_serialize(msg, &ser_len);
+  ALLOCATOR_STORE_OR_RETURN(&a, ser, 1);
+  printf("Serialized message:\t%s\n", btoh(ser, ser_len));
+
+  size_t ser_compressed_len;
+  byte_t *ser_compressed = compress(ser, ser_len, &ser_compressed_len);
+  ALLOCATOR_STORE_OR_RETURN(&a, ser_compressed, 1);
+  printf("Compressed message:\t%s\n", btoh(ser_compressed, ser_compressed_len));
+
+  size_t ser_decompressed_len;
+  byte_t *ser_decompressed =
+      decompress(ser_compressed, ser_compressed_len, &ser_decompressed_len);
+  ALLOCATOR_STORE_OR_RETURN(&a, ser_decompressed, 1);
+  printf("Decompressed message:\t%s\n",
+         btoh(ser_decompressed, ser_decompressed_len));
+
+  // message_t *deserialized_msg = message_deserialize((char*)ser_decompressed);
+  // if (strcmp(msg->sender, deserialized_msg->sender) != 0 ||
+  //     strcmp(msg->content, deserialized_msg->content) != 0 ||
+  //     msg->timestamp != deserialized_msg->timestamp) {
+  //     printf("Decompressed message does not match original message!\n");
+  //     return 1;
+  // }
+
+  allocator_storage_free_inner_pointers(&a);
+  printf("--- END TEST:\t compress_message ---\n");
+  return 0;
+}
+
+int main() {
+  int err = test_message();
+  if (err != 0) {
+    printf("test_message failed with error code %d\n", err);
+    return 1;
+  }
+
+  err = test_compress();
+  if (err != 0) {
+    printf("test_compress failed with error code %d\n", err);
+    return 1;
+  }
+
+  err = test_compress_message();
+  if (err != 0) {
+    printf("test_compress_message failed with error code %d\n", err);
+    return 1;
+  }
+
+  err = test_message_encryption();
+  if (err != 0) {
+    printf("test_message_encryption failed with error code %d\n", err);
+    return 1;
+  }
+
+  err = test_vector();
+  if (err != 0) {
+    printf("test_vector failed with error code %d\n", err);
+    return 1;
+  }
+
+  err = test_defer();
+  if (err != 0) {
+    printf("test_defer failed with error code %d\n", err);
+    return 1;
+  }
+
+  err = test_split_string();
+  if (err != 0) {
+    printf("test_split_string failed with error code %d\n", err);
+    return 1;
+  }
+
+  printf("OK\n");
+  return 0;
+}

+ 0 - 0
Cargo.toml → channel-rs/Cargo.toml


+ 0 - 0
client/Cargo.toml → channel-rs/client/Cargo.toml


+ 0 - 0
client/src/actions.rs → channel-rs/client/src/actions.rs


+ 0 - 0
client/src/main.rs → channel-rs/client/src/main.rs


+ 0 - 0
client/src/ui.rs → channel-rs/client/src/ui.rs


+ 0 - 0
client_cli/Cargo.toml → channel-rs/client_cli/Cargo.toml


+ 0 - 0
client_cli/src/main.rs → channel-rs/client_cli/src/main.rs


+ 0 - 0
client_html/.gitignore → channel-rs/client_html/.gitignore


+ 0 - 0
client_html/Cargo.toml → channel-rs/client_html/Cargo.toml


+ 0 - 0
client_html/assets/index.html → channel-rs/client_html/assets/index.html


+ 0 - 0
client_html/build.rs → channel-rs/client_html/build.rs


+ 0 - 0
client_html/capabilities/default.json → channel-rs/client_html/capabilities/default.json


+ 0 - 0
client_html/icons/128x128.png → channel-rs/client_html/icons/128x128.png


+ 0 - 0
client_html/icons/128x128@2x.png → channel-rs/client_html/icons/128x128@2x.png


+ 0 - 0
client_html/icons/32x32.png → channel-rs/client_html/icons/32x32.png


+ 0 - 0
client_html/icons/Square107x107Logo.png → channel-rs/client_html/icons/Square107x107Logo.png


+ 0 - 0
client_html/icons/Square142x142Logo.png → channel-rs/client_html/icons/Square142x142Logo.png


+ 0 - 0
client_html/icons/Square150x150Logo.png → channel-rs/client_html/icons/Square150x150Logo.png


+ 0 - 0
client_html/icons/Square284x284Logo.png → channel-rs/client_html/icons/Square284x284Logo.png


+ 0 - 0
client_html/icons/Square30x30Logo.png → channel-rs/client_html/icons/Square30x30Logo.png


+ 0 - 0
client_html/icons/Square310x310Logo.png → channel-rs/client_html/icons/Square310x310Logo.png


+ 0 - 0
client_html/icons/Square44x44Logo.png → channel-rs/client_html/icons/Square44x44Logo.png


+ 0 - 0
client_html/icons/Square71x71Logo.png → channel-rs/client_html/icons/Square71x71Logo.png


+ 0 - 0
client_html/icons/Square89x89Logo.png → channel-rs/client_html/icons/Square89x89Logo.png


+ 0 - 0
client_html/icons/StoreLogo.png → channel-rs/client_html/icons/StoreLogo.png


+ 0 - 0
client_html/icons/icon.icns → channel-rs/client_html/icons/icon.icns


+ 0 - 0
client_html/icons/icon.ico → channel-rs/client_html/icons/icon.ico


+ 0 - 0
client_html/icons/icon.png → channel-rs/client_html/icons/icon.png


+ 0 - 0
client_html/src/lib.rs → channel-rs/client_html/src/lib.rs


+ 0 - 0
client_html/src/main.rs → channel-rs/client_html/src/main.rs


+ 0 - 0
client_html/tauri.conf.json → channel-rs/client_html/tauri.conf.json


+ 3 - 0
client_shared/Cargo.toml → channel-rs/client_shared/Cargo.toml

@@ -11,3 +11,6 @@ utils = { path = "../utils" }
 reqwest = { version = "0.12.15", features = [ "blocking" ] }
 
 url = "2.5.8"
+
+[dev-dependencies]
+server = { path = "../server" }

+ 19 - 0
channel-rs/client_shared/assets/logo-amy.txt

@@ -0,0 +1,19 @@
+███████████████████████████████████████████████████████████
+███                                                     ███
+███                                                     ███
+███                              ▄ █           █████    ███
+███                         ▟██▙ █ █         ██████     ███
+███                     ▟█▙ █▐▍█ ▜██       ██████       ███
+███                     █ █ █  █   █     ███████        ███
+███                     ███ █  ▜  ▟▛  ████████          ███
+███    ██████           █ █ ▛        ███████            ███
+███    ███████████      █ ▜          ████████████       ███
+███      ██████████████                    ████████     ███
+███          ██████████       █████                     ███
+███            ████████     ████                        ███
+███          ███████       ██                           ███
+███        ███████                                      ███
+███      █████                                          ███
+███                                                     ███
+███                                                     ███
+███████████████████████████████████████████████████████████

+ 0 - 0
client_shared/assets/logo.txt → channel-rs/client_shared/assets/logo-rei.txt


+ 1 - 1
client_shared/src/lib.rs → channel-rs/client_shared/src/lib.rs

@@ -14,7 +14,7 @@ pub const API_DEFAULT_PORT: u16 = 13337;
 
 pub const INVALID_MESSAGE_IDENT: &str = "ERR";
 
-pub const LOGO: &str = include_str!("../assets/logo.txt");
+pub const LOGO: &str = include_str!("../assets/logo-amy.txt");
 pub const LOGO_DURATION: u128 = 500;
 
 pub const SAVE_FILE: &str = "savedata.bin";

+ 0 - 0
client_shared/src/message.rs → channel-rs/client_shared/src/message.rs


+ 0 - 0
client_shared/src/persistence.rs → channel-rs/client_shared/src/persistence.rs


+ 0 - 0
client_shared/src/ui.rs → channel-rs/client_shared/src/ui.rs


+ 14 - 0
client_shared/src/utils.rs → channel-rs/client_shared/src/utils.rs

@@ -120,3 +120,17 @@ pub fn rx_messages(host: &str, password: &str, timeout_sec: u64) -> Result<Vec<M
 
     Ok(messages)
 }
+
+#[test]
+fn test_tx_rx() {
+    let host = "http://localhost:13337/";
+    let password = "null";
+
+    let message = Message::new("Alice", "Hello", "root");
+    tx_message(host, password, 5, message.clone()).expect("Failed to send message");
+    let messages = rx_messages(host, password, 5).expect("Failed to receive messages");
+
+    assert_eq!(messages.len(), 1);
+    assert_eq!(messages[0].sender, message.sender);
+    assert_eq!(messages[0].content, message.content);
+}

+ 0 - 0
logging/Cargo.toml → channel-rs/logging/Cargo.toml


+ 0 - 0
logging/src/lib.rs → channel-rs/logging/src/lib.rs


+ 0 - 0
procmacro/Cargo.toml → channel-rs/procmacro/Cargo.toml


+ 0 - 0
procmacro/src/lib.rs → channel-rs/procmacro/src/lib.rs


+ 0 - 0
server/Cargo.toml → channel-rs/server/Cargo.toml


+ 2 - 16
server/src/main.rs → channel-rs/server/src/lib.rs

@@ -1,15 +1,12 @@
-use std::env::args;
-
 use utils::{
     binary::checksum, http::{self, Request, Response}, strings::StaticString
 };
 
 const MAX_MESSAGE_LENGTH: usize = 2048;
 const MESSAGE_HISTORY_LENGTH: usize = 64;
-const DEFAULT_PORT: u16 = 13337;
 
 #[derive(Debug)]
-struct Appdata {
+pub struct Appdata {
     messages: [StaticString<MAX_MESSAGE_LENGTH>; MESSAGE_HISTORY_LENGTH],
     index: usize,
 }
@@ -78,7 +75,7 @@ fn index_post_sync(state: &mut Appdata, request: Request) -> Response {
     }
 }
 
-fn router(state: &mut Appdata, request: Request) -> Response {
+pub fn router(state: &mut Appdata, request: Request) -> Response {
     if request.url != "/" {
         return Response::not_found();
     }
@@ -89,14 +86,3 @@ fn router(state: &mut Appdata, request: Request) -> Response {
         http::Method::Other => Response::method_not_allowed(),
     }
 }
-
-fn main() -> http::Result<()> {
-    let port = args().nth(1).unwrap_or(DEFAULT_PORT.to_string());
-
-    println!("Starting on port {}", port);
-
-    let appdata = Appdata::new();
-    http::run(&format!("127.0.0.1:{}", port), appdata, router)?;
-
-    Ok(())
-}

+ 18 - 0
channel-rs/server/src/main.rs

@@ -0,0 +1,18 @@
+use std::env::args;
+
+use server::{Appdata, router};
+use utils::http;
+
+
+const DEFAULT_PORT: u16 = 13337;
+
+fn main() -> http::Result<()> {
+    let port = args().nth(1).unwrap_or(DEFAULT_PORT.to_string());
+
+    println!("Starting on port {}", port);
+
+    let appdata = Appdata::new();
+    http::run(&format!("127.0.0.1:{}", port), appdata, router)?;
+
+    Ok(())
+}

+ 1 - 0
utils/Cargo.toml → channel-rs/utils/Cargo.toml

@@ -4,3 +4,4 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
+sha2 = "0.10.9"

+ 1 - 0
utils/src/aes.rs → channel-rs/utils/src/aes.rs

@@ -571,6 +571,7 @@ mod test {
         let input = vec![1, 2, 3, 4, 5];
         let mut buf = input.clone();
         encrypt_cbc(&mut buf, &"password".to_string());
+        println!("Ciphertext: {:?}", buf);
         decrypt_cbc(&mut buf, &"password").unwrap();
         assert_eq!(buf, input);
 

+ 0 - 0
utils/src/binary.rs → channel-rs/utils/src/binary.rs


+ 10 - 0
utils/src/hash/mod.rs → channel-rs/utils/src/hash/mod.rs

@@ -41,6 +41,16 @@ impl ToString for Hash {
     }
 }
 
+#[test]
+fn test_hash() {
+    let hash = Hash::new("hello world");
+    println!("Hash: {}", hash.to_string());
+    assert_eq!(
+        hash.to_string(),
+        "96ff872b684ed4d037f9b6a66855351b51fa73d411967fc1918b40e5b45f4c7b"
+    );
+}
+
 pub trait Hashable {
     fn hash(&self) -> Hash;
 }

+ 8 - 1
utils/src/hash/sha.rs → channel-rs/utils/src/hash/sha.rs

@@ -1,5 +1,7 @@
 // Reference: https://github.com/keanemind/python-sha-256/
 
+use sha2::Digest;
+
 const K: [Number; 64] = [
     0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
     0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
@@ -48,6 +50,11 @@ fn rotate_right(num: Number, shift: Number) -> Number {
 }
 
 pub fn sha256(data: &[u8]) -> [u8; 256 / 8] {
+    return sha2::Sha256::digest(data).try_into().expect("SHA-256 output should be 32 bytes");
+
+    // Use library function for SHA-256 until the implementation is fixed
+    // TODO: Fix implementation
+
     let data_len_bits: u64 = (data.len() * 8) as u64;
 
     let padding_len = (512 - ((data.len() * 8 + 64) % 512)) / 8;
@@ -125,13 +132,13 @@ mod test {
 
     // TODO: This doesn't actually work but it's good enough for now.
     // #[test]
+    #[allow(dead_code)]
     fn test_sha256() {
         let input = vec![0, 1, 2, 3];
         let expected: [u8; 32] = [
             5, 78, 222, 193, 208, 33, 31, 98, 79, 237, 12, 188, 169, 212, 249, 64, 11, 14, 73, 28,
             67, 116, 42, 242, 197, 176, 171, 235, 240, 201, 144, 216,
         ];
-        // 054edec1d0211f624fed0cbca9d4f9400b0e491c43742af2c5b0abebf0c990d8
 
         assert_eq!(sha256(&input), expected)
     }

+ 0 - 0
utils/src/http.rs → channel-rs/utils/src/http.rs


+ 0 - 0
utils/src/lib.rs → channel-rs/utils/src/lib.rs


+ 0 - 0
utils/src/rng.rs → channel-rs/utils/src/rng.rs


+ 0 - 0
utils/src/serialize.rs → channel-rs/utils/src/serialize.rs


+ 0 - 0
utils/src/strings.rs → channel-rs/utils/src/strings.rs


+ 0 - 0
utils/src/time.rs → channel-rs/utils/src/time.rs