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 
hex_char_value(uint8_t c,uint8_t * value,bool * valid)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 
parse_key(char * hex_key,uint8_t out[BLAKE3_KEY_LEN])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 
main(int argc,char ** argv)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_ll > 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 }
167