chibipub

experimental activitypub node in C
git clone git://jb55.com/chibipub
Log | Files | Refs | README | LICENSE

main.c (4038B)


      1 /*
      2  * This main file is intended for testing via `make test`. It does not build in
      3  * other settings. See README.md in this directory for examples of how to build
      4  * C code.
      5  */
      6 
      7 #include <assert.h>
      8 #include <errno.h>
      9 #include <stdbool.h>
     10 #include <stdint.h>
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 
     14 #include "blake3.h"
     15 #include "blake3_impl.h"
     16 
     17 #define HASH_MODE 0
     18 #define KEYED_HASH_MODE 1
     19 #define DERIVE_KEY_MODE 2
     20 
     21 static void hex_char_value(uint8_t c, uint8_t *value, bool *valid) {
     22   if ('0' <= c && c <= '9') {
     23     *value = c - '0';
     24     *valid = true;
     25   } else if ('a' <= c && c <= 'f') {
     26     *value = 10 + c - 'a';
     27     *valid = true;
     28   } else {
     29     *valid = false;
     30   }
     31 }
     32 
     33 static int parse_key(char *hex_key, uint8_t out[BLAKE3_KEY_LEN]) {
     34   size_t hex_len = strlen(hex_key);
     35   if (hex_len != 64) {
     36     fprintf(stderr, "Expected a 64-char hexadecimal key, got %zu chars.\n",
     37             hex_len);
     38     return 1;
     39   }
     40   for (size_t i = 0; i < 64; i++) {
     41     uint8_t value;
     42     bool valid;
     43     hex_char_value(hex_key[i], &value, &valid);
     44     if (!valid) {
     45       fprintf(stderr, "Invalid hex char.\n");
     46       return 1;
     47     }
     48     if (i % 2 == 0) {
     49       out[i / 2] = 0;
     50       value <<= 4;
     51     }
     52     out[i / 2] += value;
     53   }
     54   return 0;
     55 }
     56 
     57 /* A little repetition here */
     58 enum cpu_feature {
     59   SSE2 = 1 << 0,
     60   SSSE3 = 1 << 1,
     61   SSE41 = 1 << 2,
     62   AVX = 1 << 3,
     63   AVX2 = 1 << 4,
     64   AVX512F = 1 << 5,
     65   AVX512VL = 1 << 6,
     66   /* ... */
     67   UNDEFINED = 1 << 30
     68 };
     69 
     70 extern enum cpu_feature g_cpu_features;
     71 enum cpu_feature get_cpu_features();
     72 
     73 int main(int argc, char **argv) {
     74   size_t out_len = BLAKE3_OUT_LEN;
     75   uint8_t key[BLAKE3_KEY_LEN];
     76   char *context = "";
     77   uint8_t mode = HASH_MODE;
     78   while (argc > 1) {
     79     if (argc <= 2) {
     80       fprintf(stderr, "Odd number of arguments.\n");
     81       return 1;
     82     }
     83     if (strcmp("--length", argv[1]) == 0) {
     84       char *endptr = NULL;
     85       errno = 0;
     86       unsigned long long out_len_ll = strtoull(argv[2], &endptr, 10);
     87       if (errno != 0 || out_len > SIZE_MAX || endptr == argv[2] ||
     88           *endptr != 0) {
     89         fprintf(stderr, "Bad length argument.\n");
     90         return 1;
     91       }
     92       out_len = (size_t)out_len_ll;
     93     } else if (strcmp("--keyed", argv[1]) == 0) {
     94       mode = KEYED_HASH_MODE;
     95       int ret = parse_key(argv[2], key);
     96       if (ret != 0) {
     97         return ret;
     98       }
     99     } else if (strcmp("--derive-key", argv[1]) == 0) {
    100       mode = DERIVE_KEY_MODE;
    101       context = argv[2];
    102     } else {
    103       fprintf(stderr, "Unknown flag.\n");
    104       return 1;
    105     }
    106     argc -= 2;
    107     argv += 2;
    108   }
    109 
    110   /* 
    111    * We're going to hash the input multiple times, so we need to buffer it all.
    112    * This is just for test cases, so go ahead and assume that the input is less
    113    * than 1 MiB. 
    114    */
    115   size_t buf_capacity = 1 << 20;
    116   uint8_t *buf = malloc(buf_capacity);
    117   assert(buf != NULL);
    118   size_t buf_len = 0;
    119   while (1) {
    120     size_t n = fread(&buf[buf_len], 1, buf_capacity - buf_len, stdin);
    121     if (n == 0) {
    122       break;
    123     }
    124     buf_len += n;
    125     assert(buf_len < buf_capacity);
    126   }
    127 
    128   const int mask = get_cpu_features();
    129   int feature = 0;
    130   do {
    131     fprintf(stderr, "Testing 0x%08X\n", feature);
    132     g_cpu_features = feature;
    133     blake3_hasher hasher;
    134     switch (mode) {
    135     case HASH_MODE:
    136       blake3_hasher_init(&hasher);
    137       break;
    138     case KEYED_HASH_MODE:
    139       blake3_hasher_init_keyed(&hasher, key);
    140       break;
    141     case DERIVE_KEY_MODE:
    142       blake3_hasher_init_derive_key(&hasher, context);
    143       break;
    144     default:
    145       abort();
    146     }
    147 
    148     blake3_hasher_update(&hasher, buf, buf_len);
    149 
    150     /* TODO: An incremental output reader API to avoid this allocation. */
    151     uint8_t *out = malloc(out_len);
    152     if (out_len > 0 && out == NULL) {
    153       fprintf(stderr, "malloc() failed.\n");
    154       return 1;
    155     }
    156     blake3_hasher_finalize(&hasher, out, out_len);
    157     for (size_t i = 0; i < out_len; i++) {
    158       printf("%02x", out[i]);
    159     }
    160     printf("\n");
    161     free(out);
    162     feature = (feature - mask) & mask;
    163   } while (feature != 0);
    164   free(buf);
    165   return 0;
    166 }