1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License (), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2019 Joyent, Inc.
14 * Copyright 2020 Oxide Computer Company
15 */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <strings.h>
21 #include <errno.h>
22 #include <err.h>
23
24 #include <sys/debug.h>
25
26 #include "cryptotest.h"
27 #include "parser_runner.h"
28
29 #define DATA_PATH "/opt/crypto-tests/share"
30
31 /*
32 * Parse NIST test vector data into a format that is simple to run the digest
33 * tests against. The parsing logic is not meant to be especially robust given
34 * that we control the data fed into it.
35 */
36
37 struct parser_ctx {
38 FILE *pc_file;
39 size_t pc_hash_sz;
40 char *pc_line_buf;
41 size_t pc_line_sz;
42 };
43
44 parser_ctx_t *
parser_init(const char * path,size_t hash_len,int * errp)45 parser_init(const char *path, size_t hash_len, int *errp)
46 {
47 FILE *fp;
48 parser_ctx_t *ctx;
49
50 /* sanity check for SHA1 -> SHA512 */
51 ASSERT(hash_len >= 20 && hash_len <= 64);
52
53 fp = fopen(path, "r");
54 if (fp == NULL) {
55 *errp = errno;
56 return (NULL);
57 }
58 ctx = malloc(sizeof (*ctx));
59 if (ctx == NULL) {
60 *errp = ENOMEM;
61 (void) fclose(fp);
62 return (NULL);
63 }
64 ctx->pc_file = fp;
65 ctx->pc_hash_sz = hash_len;
66 ctx->pc_line_buf = NULL;
67 ctx->pc_line_sz = 0;
68
69 return (ctx);
70 }
71
72 void
parser_fini(parser_ctx_t * ctx)73 parser_fini(parser_ctx_t *ctx)
74 {
75 free(ctx->pc_line_buf);
76 (void) fclose(ctx->pc_file);
77 free(ctx);
78 }
79
80 static size_t
hex2bytes(const char * hexbuf,size_t hexlen,uchar_t * outbuf,size_t outlen)81 hex2bytes(const char *hexbuf, size_t hexlen, uchar_t *outbuf, size_t outlen)
82 {
83 size_t count = 0;
84 /* naive and lazy conversion */
85 errno = 0;
86 while (hexlen > 1) {
87 long res;
88 char buf[3] = {hexbuf[0], hexbuf[1], '\0'};
89
90 res = strtol(buf, NULL, 16);
91 if (errno != 0) {
92 break;
93 }
94 *outbuf = res & 0xff;
95
96 hexbuf += 2;
97 hexlen -= 2;
98 outbuf += 1;
99 outlen += 1;
100 count++;
101
102 if (outbuf == 0) {
103 break;
104 }
105 }
106
107 return (count);
108 }
109
110 static int
read_len(parser_ctx_t * ctx,size_t * lenp,size_t * szp)111 read_len(parser_ctx_t *ctx, size_t *lenp, size_t *szp)
112 {
113 ssize_t sz;
114 long parsed;
115 const char *search = "Len = ";
116 const size_t search_len = strlen(search);
117
118 errno = 0;
119 sz = getline(&ctx->pc_line_buf, &ctx->pc_line_sz, ctx->pc_file);
120 if (sz < 1) {
121 int err = errno;
122
123 if (err == 0 || err == ENOENT) {
124 /* EOF reached, bail */
125 return (-1);
126 } else {
127 return (err);
128 }
129 }
130 *szp = sz;
131 if (strncmp(ctx->pc_line_buf, search, search_len) != 0) {
132 return (-1);
133 }
134
135 errno = 0;
136 parsed = strtol(ctx->pc_line_buf + search_len, NULL, 10);
137 if (parsed == 0 && errno != 0) {
138 return (errno);
139 }
140 if (parsed < 0) {
141 return (EINVAL);
142 }
143
144 /* length in file is in bits, while we want bytes */
145 *lenp = (size_t)parsed / 8;
146 return (0);
147 }
148
149 static int
read_msg(parser_ctx_t * ctx,uchar_t * msgbuf,size_t msglen)150 read_msg(parser_ctx_t *ctx, uchar_t *msgbuf, size_t msglen)
151 {
152 ssize_t sz;
153 const char *search = "Msg = ";
154 const size_t search_len = strlen(search);
155
156 sz = getline(&ctx->pc_line_buf, &ctx->pc_line_sz, ctx->pc_file);
157 if (sz < 0) {
158 return (errno);
159 }
160 if (strncmp(ctx->pc_line_buf, search, search_len) != 0) {
161 return (-1);
162 }
163
164 if (msgbuf == NULL) {
165 ASSERT(msglen == 0);
166 return (0);
167 }
168
169 size_t parsed;
170 parsed = hex2bytes(ctx->pc_line_buf + search_len, sz - search_len,
171 msgbuf, msglen);
172 if (parsed != msglen) {
173 ASSERT3U(parsed, <, msglen);
174 return (-1);
175 }
176
177 return (0);
178 }
179
180 static int
read_md(parser_ctx_t * ctx,uchar_t * mdbuf,size_t mdlen)181 read_md(parser_ctx_t *ctx, uchar_t *mdbuf, size_t mdlen)
182 {
183 ssize_t sz;
184 const char *search = "MD = ";
185 const size_t search_len = strlen(search);
186
187 sz = getline(&ctx->pc_line_buf, &ctx->pc_line_sz, ctx->pc_file);
188 if (sz < 0) {
189 return (errno);
190 }
191 if (strncmp(ctx->pc_line_buf, search, search_len) != 0) {
192 return (-1);
193 }
194
195 size_t parsed;
196 parsed = hex2bytes(ctx->pc_line_buf + search_len, sz - search_len,
197 mdbuf, mdlen);
198 if (parsed != mdlen) {
199 ASSERT3U(parsed, <, mdlen);
200 return (-1);
201 }
202
203 return (0);
204 }
205
206 parser_entry_t *
parser_read(parser_ctx_t * ctx,int * errp)207 parser_read(parser_ctx_t *ctx, int *errp)
208 {
209 int err = 0;
210 parser_entry_t *res = NULL;
211 uchar_t *msgbuf = NULL;
212 uchar_t *mdbuf = NULL;
213
214 while (feof(ctx->pc_file) == 0) {
215 int ret;
216 size_t msglen, sz;
217
218 ret = read_len(ctx, &msglen, &sz);
219 if (ret == -1) {
220 /*
221 * Did not find a properly formatted "Len = <num>", but
222 * no hard errors were incurred while looking for one,
223 * so continue searching.
224 */
225 continue;
226 } else if (ret != 0) {
227 err = ret;
228 break;
229 }
230
231 if (msglen != 0) {
232 msgbuf = calloc(msglen, 1);
233 if (msgbuf == NULL) {
234 err = ENOMEM;
235 break;
236 }
237 }
238
239 ret = read_msg(ctx, msgbuf, msglen);
240 if (ret == -1) {
241 /*
242 * Did not find properly formatted "Msg = <hex data>".
243 * Restart the search for a new record.
244 */
245 free(msgbuf);
246 msgbuf = NULL;
247 continue;
248 } else if (ret != 0) {
249 err = ret;
250 break;
251 }
252
253 mdbuf = calloc(1, ctx->pc_hash_sz);
254 if (mdbuf == NULL) {
255 err = ENOMEM;
256 break;
257 }
258 ret = read_md(ctx, mdbuf, ctx->pc_hash_sz);
259 if (ret == -1) {
260 /*
261 * Did not find properly formatted "MD = <hash>".
262 * Restart search for new record.
263 */
264 free(msgbuf);
265 free(mdbuf);
266 msgbuf = mdbuf = NULL;
267 continue;
268 } else if (ret != 0) {
269 err = ret;
270 break;
271 }
272
273 res = malloc(sizeof (*res));
274 if (res == NULL) {
275 err = ENOMEM;
276 break;
277 }
278 res->pe_msg = msgbuf;
279 res->pe_msglen = msglen;
280 res->pe_hash = mdbuf;
281 break;
282 }
283
284 if (err != 0) {
285 ASSERT(res == NULL);
286 free(msgbuf);
287 free(mdbuf);
288 }
289
290 /* EOF status indicated by err == 0 and res == NULL */
291 *errp = err;
292 return (res);
293 }
294
295 void
parser_free(parser_entry_t * ent)296 parser_free(parser_entry_t *ent)
297 {
298 free(ent->pe_msg);
299 free(ent->pe_hash);
300 free(ent);
301 }
302
303 /*
304 * With the above parser, run a the vectors through a given crypto test.
305 */
306 int
digest_runner(char * mech_name,const char * input_file,size_t digest_len)307 digest_runner(char *mech_name, const char *input_file, size_t digest_len)
308 {
309 int fails = 0, error;
310 uint8_t N[1024];
311 size_t updatelens[] = {
312 1, 8, 33, 67, CTEST_UPDATELEN_WHOLE, CTEST_UPDATELEN_END
313 };
314 cryptotest_t args = {
315 .out = N,
316 .outlen = sizeof (N),
317 .mechname = mech_name,
318 .updatelens = updatelens
319 };
320 parser_ctx_t *ctx;
321 parser_entry_t *ent;
322
323 /*
324 * XXX: This could be changed to generate a path relative to that of
325 * the executable to find the data files
326 */
327 char *path = NULL;
328 if (asprintf(&path, "%s/%s", DATA_PATH, input_file) < 0) {
329 err(EXIT_FAILURE, NULL);
330 }
331
332 ctx = parser_init(path, digest_len, &error);
333 if (ctx == NULL) {
334 err(EXIT_FAILURE, "%s", path);
335 }
336 free(path);
337
338 error = 0;
339 while ((ent = parser_read(ctx, &error)) != NULL) {
340 args.in = ent->pe_msg;
341 args.inlen = ent->pe_msglen;
342
343 fails += run_test(&args, ent->pe_hash, digest_len, DIGEST_FG);
344 parser_free(ent);
345 }
346 if (error != 0) {
347 err(EXIT_FAILURE, NULL);
348 }
349 parser_fini(ctx);
350
351 return (fails);
352 }
353