xref: /illumos-gate/usr/src/test/crypto-tests/tests/digest/parser_runner.c (revision 4d9fdb46b215739778ebc12079842c9905586999)
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 *
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
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
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
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
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
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 *
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
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
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