git / moonbite-core / src/crypto/blake3/main.c · raw

/* * This main file is intended for testing via `make test`. It does not build in * other settings. See README.md in this directory for examples of how to build * C code. */#include <assert.h>#include <errno.h>#include <stdbool.h>#include <stdint.h>#include <stdio.h>#include <stdlib.h>#include "blake3.h"#include "blake3_impl.h"#define HASH_MODE 0#define KEYED_HASH_MODE 1#define DERIVE_KEY_MODE 2static void hex_char_value(uint8_t c, uint8_t *value, bool *valid) {  if ('0' <= c && c <= '9') {    *value = c - '0';    *valid = true;  } else if ('a' <= c && c <= 'f') {    *value = 10 + c - 'a';    *valid = true;  } else {    *valid = false;  }}static int parse_key(char *hex_key, uint8_t out[BLAKE3_KEY_LEN]) {  size_t hex_len = strlen(hex_key);  if (hex_len != 64) {    fprintf(stderr, "Expected a 64-char hexadecimal key, got %zu chars.\n",            hex_len);    return 1;  }  for (size_t i = 0; i < 64; i++) {    uint8_t value;    bool valid;    hex_char_value(hex_key[i], &value, &valid);    if (!valid) {      fprintf(stderr, "Invalid hex char.\n");      return 1;    }    if (i % 2 == 0) {      out[i / 2] = 0;      value <<= 4;    }    out[i / 2] += value;  }  return 0;}/* A little repetition here */enum cpu_feature {  SSE2 = 1 << 0,  SSSE3 = 1 << 1,  SSE41 = 1 << 2,  AVX = 1 << 3,  AVX2 = 1 << 4,  AVX512F = 1 << 5,  AVX512VL = 1 << 6,  /* ... */  UNDEFINED = 1 << 30};extern enum cpu_feature g_cpu_features;enum cpu_feature get_cpu_features();int main(int argc, char **argv) {  size_t out_len = BLAKE3_OUT_LEN;  uint8_t key[BLAKE3_KEY_LEN];  char *context = "";  uint8_t mode = HASH_MODE;  while (argc > 1) {    if (argc <= 2) {      fprintf(stderr, "Odd number of arguments.\n");      return 1;    }    if (strcmp("--length", argv[1]) == 0) {      char *endptr = NULL;      errno = 0;      unsigned long long out_len_ll = strtoull(argv[2], &endptr, 10);      if (errno != 0 || out_len > SIZE_MAX || endptr == argv[2] ||          *endptr != 0) {        fprintf(stderr, "Bad length argument.\n");        return 1;      }      out_len = (size_t)out_len_ll;    } else if (strcmp("--keyed", argv[1]) == 0) {      mode = KEYED_HASH_MODE;      int ret = parse_key(argv[2], key);      if (ret != 0) {        return ret;      }    } else if (strcmp("--derive-key", argv[1]) == 0) {      mode = DERIVE_KEY_MODE;      context = argv[2];    } else {      fprintf(stderr, "Unknown flag.\n");      return 1;    }    argc -= 2;    argv += 2;  }  /*   * We're going to hash the input multiple times, so we need to buffer it all.   * This is just for test cases, so go ahead and assume that the input is less   * than 1 MiB.   */  size_t buf_capacity = 1 << 20;  uint8_t *buf = malloc(buf_capacity);  assert(buf != NULL);  size_t buf_len = 0;  while (1) {    size_t n = fread(&buf[buf_len], 1, buf_capacity - buf_len, stdin);    if (n == 0) {      break;    }    buf_len += n;    assert(buf_len < buf_capacity);  }  const int mask = get_cpu_features();  int feature = 0;  do {    fprintf(stderr, "Testing 0x%08X\n", feature);    g_cpu_features = feature;    blake3_hasher hasher;    switch (mode) {    case HASH_MODE:      blake3_hasher_init(&hasher);      break;    case KEYED_HASH_MODE:      blake3_hasher_init_keyed(&hasher, key);      break;    case DERIVE_KEY_MODE:      blake3_hasher_init_derive_key(&hasher, context);      break;    default:      abort();    }    blake3_hasher_update(&hasher, buf, buf_len);    /* TODO: An incremental output reader API to avoid this allocation. */    uint8_t *out = malloc(out_len);    if (out_len > 0 && out == NULL) {      fprintf(stderr, "malloc() failed.\n");      return 1;    }    blake3_hasher_finalize(&hasher, out, out_len);    for (size_t i = 0; i < out_len; i++) {      printf("%02x", out[i]);    }    printf("\n");    free(out);    feature = (feature - mask) & mask;  } while (feature != 0);  free(buf);  return 0;}