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