#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <err.h>
#include <sys/debug.h>
#include "cryptotest.h"
#include "parser_runner.h"
#define DATA_PATH "/opt/crypto-tests/share"
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;
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;
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) {
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);
}
*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) {
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) {
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) {
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);
}
*errp = err;
return (res);
}
void
parser_free(parser_entry_t *ent)
{
free(ent->pe_msg);
free(ent->pe_hash);
free(ent);
}
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;
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);
}