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 }