/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License (), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2019 Joyent, Inc. * Copyright 2020 Oxide Computer Company */ #include #include #include #include #include #include #include #include "cryptotest.h" #include "parser_runner.h" #define DATA_PATH "/opt/crypto-tests/share" /* * Parse NIST test vector data into a format that is simple to run the digest * tests against. The parsing logic is not meant to be especially robust given * that we control the data fed into it. */ struct parser_ctx { FILE *pc_file; size_t pc_hash_sz; char *pc_line_buf; size_t pc_line_sz; }; parser_ctx_t * parser_init(const char *path, size_t hash_len, int *errp) { FILE *fp; parser_ctx_t *ctx; /* sanity check for SHA1 -> SHA512 */ ASSERT(hash_len >= 20 && hash_len <= 64); fp = fopen(path, "r"); if (fp == NULL) { *errp = errno; return (NULL); } ctx = malloc(sizeof (*ctx)); if (ctx == NULL) { *errp = ENOMEM; (void) fclose(fp); return (NULL); } ctx->pc_file = fp; ctx->pc_hash_sz = hash_len; ctx->pc_line_buf = NULL; ctx->pc_line_sz = 0; return (ctx); } void parser_fini(parser_ctx_t *ctx) { free(ctx->pc_line_buf); (void) fclose(ctx->pc_file); free(ctx); } static size_t hex2bytes(const char *hexbuf, size_t hexlen, uchar_t *outbuf, size_t outlen) { size_t count = 0; /* naive and lazy conversion */ errno = 0; while (hexlen > 1) { long res; char buf[3] = {hexbuf[0], hexbuf[1], '\0'}; res = strtol(buf, NULL, 16); if (errno != 0) { break; } *outbuf = res & 0xff; hexbuf += 2; hexlen -= 2; outbuf += 1; outlen += 1; count++; if (outbuf == 0) { break; } } return (count); } static int read_len(parser_ctx_t *ctx, size_t *lenp, size_t *szp) { ssize_t sz; long parsed; const char *search = "Len = "; const size_t search_len = strlen(search); errno = 0; sz = getline(&ctx->pc_line_buf, &ctx->pc_line_sz, ctx->pc_file); if (sz < 1) { int err = errno; if (err == 0 || err == ENOENT) { /* EOF reached, bail */ return (-1); } else { return (err); } } *szp = sz; if (strncmp(ctx->pc_line_buf, search, search_len) != 0) { return (-1); } errno = 0; parsed = strtol(ctx->pc_line_buf + search_len, NULL, 10); if (parsed == 0 && errno != 0) { return (errno); } if (parsed < 0) { return (EINVAL); } /* length in file is in bits, while we want bytes */ *lenp = (size_t)parsed / 8; return (0); } static int read_msg(parser_ctx_t *ctx, uchar_t *msgbuf, size_t msglen) { ssize_t sz; const char *search = "Msg = "; const size_t search_len = strlen(search); sz = getline(&ctx->pc_line_buf, &ctx->pc_line_sz, ctx->pc_file); if (sz < 0) { return (errno); } if (strncmp(ctx->pc_line_buf, search, search_len) != 0) { return (-1); } if (msgbuf == NULL) { ASSERT(msglen == 0); return (0); } size_t parsed; parsed = hex2bytes(ctx->pc_line_buf + search_len, sz - search_len, msgbuf, msglen); if (parsed != msglen) { ASSERT3U(parsed, <, msglen); return (-1); } return (0); } static int read_md(parser_ctx_t *ctx, uchar_t *mdbuf, size_t mdlen) { ssize_t sz; const char *search = "MD = "; const size_t search_len = strlen(search); sz = getline(&ctx->pc_line_buf, &ctx->pc_line_sz, ctx->pc_file); if (sz < 0) { return (errno); } if (strncmp(ctx->pc_line_buf, search, search_len) != 0) { return (-1); } size_t parsed; parsed = hex2bytes(ctx->pc_line_buf + search_len, sz - search_len, mdbuf, mdlen); if (parsed != mdlen) { ASSERT3U(parsed, <, mdlen); return (-1); } return (0); } parser_entry_t * parser_read(parser_ctx_t *ctx, int *errp) { int err = 0; parser_entry_t *res = NULL; uchar_t *msgbuf = NULL; uchar_t *mdbuf = NULL; while (feof(ctx->pc_file) == 0) { int ret; size_t msglen, sz; ret = read_len(ctx, &msglen, &sz); if (ret == -1) { /* * Did not find a properly formatted "Len = ", but * no hard errors were incurred while looking for one, * so continue searching. */ continue; } else if (ret != 0) { err = ret; break; } if (msglen != 0) { msgbuf = calloc(msglen, 1); if (msgbuf == NULL) { err = ENOMEM; break; } } ret = read_msg(ctx, msgbuf, msglen); if (ret == -1) { /* * Did not find properly formatted "Msg = ". * Restart the search for a new record. */ free(msgbuf); msgbuf = NULL; continue; } else if (ret != 0) { err = ret; break; } mdbuf = calloc(1, ctx->pc_hash_sz); if (mdbuf == NULL) { err = ENOMEM; break; } ret = read_md(ctx, mdbuf, ctx->pc_hash_sz); if (ret == -1) { /* * Did not find properly formatted "MD = ". * Restart search for new record. */ free(msgbuf); free(mdbuf); msgbuf = mdbuf = NULL; continue; } else if (ret != 0) { err = ret; break; } res = malloc(sizeof (*res)); if (res == NULL) { err = ENOMEM; break; } res->pe_msg = msgbuf; res->pe_msglen = msglen; res->pe_hash = mdbuf; break; } if (err != 0) { ASSERT(res == NULL); free(msgbuf); free(mdbuf); } /* EOF status indicated by err == 0 and res == NULL */ *errp = err; return (res); } void parser_free(parser_entry_t *ent) { free(ent->pe_msg); free(ent->pe_hash); free(ent); } /* * With the above parser, run a the vectors through a given crypto test. */ int digest_runner(char *mech_name, const char *input_file, size_t digest_len) { int fails = 0, error; uint8_t N[1024]; size_t updatelens[] = { 1, 8, 33, 67, CTEST_UPDATELEN_WHOLE, CTEST_UPDATELEN_END }; cryptotest_t args = { .out = N, .outlen = sizeof (N), .mechname = mech_name, .updatelens = updatelens }; parser_ctx_t *ctx; parser_entry_t *ent; /* * XXX: This could be changed to generate a path relative to that of * the executable to find the data files */ char *path = NULL; if (asprintf(&path, "%s/%s", DATA_PATH, input_file) < 0) { err(EXIT_FAILURE, NULL); } ctx = parser_init(path, digest_len, &error); if (ctx == NULL) { err(EXIT_FAILURE, "%s", path); } free(path); error = 0; while ((ent = parser_read(ctx, &error)) != NULL) { args.in = ent->pe_msg; args.inlen = ent->pe_msglen; fails += run_test(&args, ent->pe_hash, digest_len, DIGEST_FG); parser_free(ent); } if (error != 0) { err(EXIT_FAILURE, NULL); } parser_fini(ctx); return (fails); }