aes-test: Add script to test AES implementations according to AESAVS/GCMVS
This commit is contained in:
parent
f0c54e8c15
commit
8972c72237
|
@ -5,7 +5,7 @@ AM_CPPFLAGS = \
|
|||
|
||||
noinst_PROGRAMS = bin2array bin2sql id2sql key2keyid keyid2sql oid2der \
|
||||
thread_analysis dh_speed pubkey_speed crypt_burn hash_burn fetch \
|
||||
dnssec malloc_speed
|
||||
dnssec malloc_speed aes-test
|
||||
|
||||
if USE_TLS
|
||||
noinst_PROGRAMS += tls_test
|
||||
|
@ -39,6 +39,7 @@ hash_burn_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la
|
|||
malloc_speed_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la $(RTLIB)
|
||||
fetch_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la
|
||||
dnssec_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la
|
||||
aes_test_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la
|
||||
|
||||
key2keyid.o : $(top_builddir)/config.status
|
||||
|
||||
|
|
|
@ -0,0 +1,653 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Tobias Brunner
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <library.h>
|
||||
|
||||
/** plugins to load */
|
||||
#undef PLUGINS
|
||||
#define PLUGINS "openssl"
|
||||
|
||||
/**
|
||||
* Context
|
||||
*/
|
||||
static struct {
|
||||
/** input file */
|
||||
FILE *in;
|
||||
/** output file */
|
||||
FILE *out;
|
||||
/** whether to use GCM or CBC */
|
||||
bool use_gcm;
|
||||
/** whether to run the Monte Carlo Test */
|
||||
bool use_mct;
|
||||
/** whether to test encryption or decryption */
|
||||
bool decrypt;
|
||||
/** IV length in bits in case of GCM */
|
||||
int ivlen;
|
||||
/** ICV length in bits in case of GCM */
|
||||
int icvlen;
|
||||
} ctx;
|
||||
|
||||
/**
|
||||
* Types of parameters of a test vector
|
||||
*/
|
||||
typedef enum {
|
||||
PARAM_UNKNOWN,
|
||||
PARAM_COUNT,
|
||||
PARAM_KEY,
|
||||
PARAM_IV,
|
||||
PARAM_PLAINTEXT,
|
||||
PARAM_CIPHERTEXT,
|
||||
PARAM_AAD,
|
||||
PARAM_ICV,
|
||||
} param_t;
|
||||
|
||||
static param_t parse_parameter(char *param)
|
||||
{
|
||||
if (strcaseeq(param, "COUNT"))
|
||||
{
|
||||
return PARAM_COUNT;
|
||||
}
|
||||
if (strcaseeq(param, "KEY"))
|
||||
{
|
||||
return PARAM_KEY;
|
||||
}
|
||||
if (strcaseeq(param, "IV"))
|
||||
{
|
||||
return PARAM_IV;
|
||||
}
|
||||
if (strcaseeq(param, "PLAINTEXT") ||
|
||||
strcaseeq(param, "PT"))
|
||||
{
|
||||
return PARAM_PLAINTEXT;
|
||||
}
|
||||
if (strcaseeq(param, "CIPHERTEXT") ||
|
||||
strcaseeq(param, "CT"))
|
||||
{
|
||||
return PARAM_CIPHERTEXT;
|
||||
}
|
||||
if (strcaseeq(param, "AAD"))
|
||||
{
|
||||
return PARAM_AAD;
|
||||
}
|
||||
if (strcaseeq(param, "TAG"))
|
||||
{
|
||||
return PARAM_ICV;
|
||||
}
|
||||
return PARAM_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test vector
|
||||
*/
|
||||
typedef struct {
|
||||
/** encryption/decryption key */
|
||||
chunk_t key;
|
||||
/** initialization vector */
|
||||
chunk_t iv;
|
||||
/** plain text */
|
||||
chunk_t plain;
|
||||
/** cipher text */
|
||||
chunk_t cipher;
|
||||
/** associated data */
|
||||
chunk_t aad;
|
||||
/** ICV/tag */
|
||||
chunk_t icv;
|
||||
/** whether the IV was provided */
|
||||
bool external_iv;
|
||||
/** whether the decryption/verification in GCM mode was successful */
|
||||
bool success;
|
||||
} test_vector_t;
|
||||
|
||||
static void test_vector_free(test_vector_t *test)
|
||||
{
|
||||
chunk_free(&test->key);
|
||||
chunk_free(&test->iv);
|
||||
chunk_free(&test->plain);
|
||||
chunk_free(&test->cipher);
|
||||
chunk_free(&test->aad);
|
||||
chunk_free(&test->icv);
|
||||
}
|
||||
|
||||
static void print_result(test_vector_t *test)
|
||||
{
|
||||
if (ctx.use_gcm)
|
||||
{
|
||||
if (ctx.decrypt)
|
||||
{
|
||||
if (test->success)
|
||||
{
|
||||
fprintf(ctx.out, "PT = %+B\n", &test->plain);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(ctx.out, "FAIL\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!test->external_iv)
|
||||
{
|
||||
fprintf(ctx.out, "IV = %+B\n", &test->iv);
|
||||
}
|
||||
fprintf(ctx.out, "CT = %+B\n", &test->cipher);
|
||||
fprintf(ctx.out, "Tag = %+B\n", &test->icv);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(ctx.out, "%s = %+B\n", ctx.decrypt ? "PLAINTEXT" : "CIPHERTEXT",
|
||||
ctx.decrypt ? &test->plain : &test->cipher);
|
||||
}
|
||||
}
|
||||
|
||||
static bool get_next_test_vector(test_vector_t *test)
|
||||
{
|
||||
param_t param = PARAM_UNKNOWN;
|
||||
char line[512];
|
||||
|
||||
memset(test, 0, sizeof(test_vector_t));
|
||||
|
||||
while (fgets(line, sizeof(line), ctx.in))
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
chunk_t value;
|
||||
char *token;
|
||||
int i;
|
||||
|
||||
switch (line[0])
|
||||
{
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '#':
|
||||
case '\0':
|
||||
/* copy comments, empty lines etc. directly to the output */
|
||||
if (param != PARAM_UNKNOWN)
|
||||
{ /* seems we got a complete test vector */
|
||||
return TRUE;
|
||||
}
|
||||
fputs(line, ctx.out);
|
||||
continue;
|
||||
case '[':
|
||||
/* control directives */
|
||||
fputs(line, ctx.out);
|
||||
if (strpfx(line, "[ENCRYPT]"))
|
||||
{
|
||||
ctx.decrypt = FALSE;
|
||||
}
|
||||
else if (strpfx(line, "[DECRYPT]"))
|
||||
{
|
||||
ctx.decrypt = TRUE;
|
||||
}
|
||||
else if (strcasepfx(line, "[IVlen = "))
|
||||
{
|
||||
ctx.ivlen = atoi(line + strlen("[IVlen = "));
|
||||
}
|
||||
else if (strcasepfx(line, "[Taglen = "))
|
||||
{
|
||||
ctx.icvlen = atoi(line + strlen("[Taglen = "));
|
||||
}
|
||||
continue;
|
||||
default:
|
||||
/* we assume the rest of the lines are PARAM = VALUE pairs*/
|
||||
fputs(line, ctx.out);
|
||||
break;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
enumerator = enumerator_create_token(line, "=", " \n\r");
|
||||
while (enumerator->enumerate(enumerator, &token))
|
||||
{
|
||||
switch (i++)
|
||||
{
|
||||
case 0: /* PARAM */
|
||||
param = parse_parameter(token);
|
||||
continue;
|
||||
case 1: /* VALUE */
|
||||
if (param != PARAM_UNKNOWN && param != PARAM_COUNT)
|
||||
{
|
||||
value = chunk_from_hex(chunk_from_str(token), NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = chunk_empty;
|
||||
}
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
if (i < 2)
|
||||
{
|
||||
value = chunk_empty;
|
||||
}
|
||||
switch (param)
|
||||
{
|
||||
case PARAM_KEY:
|
||||
test->key = value;
|
||||
break;
|
||||
case PARAM_IV:
|
||||
test->iv = value;
|
||||
test->external_iv = TRUE;
|
||||
break;
|
||||
case PARAM_PLAINTEXT:
|
||||
test->plain = value;
|
||||
break;
|
||||
case PARAM_CIPHERTEXT:
|
||||
test->cipher = value;
|
||||
break;
|
||||
case PARAM_AAD:
|
||||
test->aad = value;
|
||||
break;
|
||||
case PARAM_ICV:
|
||||
test->icv = value;
|
||||
break;
|
||||
default:
|
||||
chunk_free(&value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool verify_test_vector(test_vector_t *test)
|
||||
{
|
||||
if (ctx.use_gcm)
|
||||
{
|
||||
if (ctx.decrypt)
|
||||
{
|
||||
return test->key.ptr && test->iv.ptr && test->cipher.ptr &&
|
||||
test->icv.ptr;
|
||||
}
|
||||
return test->key.ptr && test->plain.ptr;
|
||||
}
|
||||
if (ctx.decrypt)
|
||||
{
|
||||
return test->key.ptr && test->iv.ptr && test->cipher.ptr;
|
||||
}
|
||||
return test->key.ptr && test->iv.ptr && test->plain.ptr;
|
||||
}
|
||||
|
||||
static bool do_test_gcm(test_vector_t *test)
|
||||
{
|
||||
encryption_algorithm_t alg;
|
||||
chunk_t key, iv;
|
||||
aead_t *aead;
|
||||
size_t saltlen, ivlen;
|
||||
|
||||
switch (ctx.icvlen / 8)
|
||||
{
|
||||
case 8:
|
||||
alg = ENCR_AES_GCM_ICV8;
|
||||
break;
|
||||
case 12:
|
||||
alg = ENCR_AES_GCM_ICV12;
|
||||
break;
|
||||
case 16:
|
||||
alg = ENCR_AES_GCM_ICV16;
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_APP, "unsupported ICV length: %d", ctx.icvlen);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
aead = lib->crypto->create_aead(lib->crypto, alg, test->key.len);
|
||||
if (!aead)
|
||||
{
|
||||
DBG1(DBG_APP, "algorithm %N or key length (%d bits) not supported",
|
||||
encryption_algorithm_names, alg, test->key.len * 8);
|
||||
return FALSE;
|
||||
}
|
||||
/* our API is quite RFC 4106 specific, that is, part of the IV is provided
|
||||
* at the end of the key. */
|
||||
saltlen = aead->get_key_size(aead) - test->key.len;
|
||||
ivlen = aead->get_iv_size(aead);
|
||||
if (ctx.ivlen / 8 != saltlen + ivlen)
|
||||
{
|
||||
DBG1(DBG_APP, "unsupported IV length: %d", ctx.ivlen);
|
||||
aead->destroy(aead);
|
||||
return FALSE;
|
||||
}
|
||||
if (!test->external_iv)
|
||||
{
|
||||
rng_t *rng;
|
||||
|
||||
/* the IV consists of saltlen random bytes (usually additional keymat)
|
||||
* followed by a counter, zero here */
|
||||
test->iv = chunk_alloc(saltlen + ivlen);
|
||||
memset(test->iv.ptr, 0, test->iv.len);
|
||||
rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
|
||||
if (!rng || !rng->get_bytes(rng, saltlen, test->iv.ptr))
|
||||
{
|
||||
DBG1(DBG_APP, "failed to generate IV");
|
||||
DESTROY_IF(rng);
|
||||
aead->destroy(aead);
|
||||
return FALSE;
|
||||
}
|
||||
rng->destroy(rng);
|
||||
}
|
||||
key = chunk_alloca(test->key.len + saltlen);
|
||||
memcpy(key.ptr, test->key.ptr, test->key.len);
|
||||
memcpy(key.ptr + test->key.len, test->iv.ptr, saltlen);
|
||||
iv = chunk_alloca(ivlen);
|
||||
memcpy(iv.ptr, test->iv.ptr + saltlen, iv.len);
|
||||
if (!aead->set_key(aead, key))
|
||||
{
|
||||
DBG1(DBG_APP, "failed to set key");
|
||||
aead->destroy(aead);
|
||||
return FALSE;
|
||||
}
|
||||
if (ctx.decrypt)
|
||||
{
|
||||
/* the ICV is expected to follow the cipher text */
|
||||
chunk_t cipher = chunk_cata("cc", test->cipher, test->icv);
|
||||
/* store if the verification of the ICV verification is successful */
|
||||
test->success = aead->decrypt(aead, cipher, test->aad, iv,
|
||||
&test->plain);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!aead->encrypt(aead, test->plain, test->aad, iv, &test->cipher))
|
||||
{
|
||||
DBG1(DBG_APP, "encryption failed");
|
||||
aead->destroy(aead);
|
||||
return FALSE;
|
||||
}
|
||||
/* copy ICV from the end of the cipher text */
|
||||
test->icv = chunk_alloc(ctx.icvlen / 8);
|
||||
test->cipher.len -= test->icv.len;
|
||||
memcpy(test->icv.ptr, test->cipher.ptr + test->cipher.len,
|
||||
test->icv.len);
|
||||
}
|
||||
aead->destroy(aead);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool crypt(crypter_t *crypter, test_vector_t *test)
|
||||
{
|
||||
if (ctx.decrypt)
|
||||
{
|
||||
if (!crypter->decrypt(crypter, test->cipher, test->iv, &test->plain))
|
||||
{
|
||||
DBG1(DBG_APP, "decryption failed");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!crypter->encrypt(crypter, test->plain, test->iv, &test->cipher))
|
||||
{
|
||||
DBG1(DBG_APP, "encryption failed");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool do_test_cbc(test_vector_t *test)
|
||||
{
|
||||
crypter_t *crypter;
|
||||
|
||||
crypter = lib->crypto->create_crypter(lib->crypto, ENCR_AES_CBC,
|
||||
test->key.len);
|
||||
if (!crypter)
|
||||
{
|
||||
DBG1(DBG_APP, "algorithm %N or key length (%d bits) not supported",
|
||||
encryption_algorithm_names, ENCR_AES_CBC, test->key.len * 8);
|
||||
return FALSE;
|
||||
}
|
||||
if (!crypter->set_key(crypter, test->key))
|
||||
{
|
||||
DBG1(DBG_APP, "failed to set key");
|
||||
crypter->destroy(crypter);
|
||||
return FALSE;
|
||||
}
|
||||
if (!crypt(crypter, test))
|
||||
{
|
||||
crypter->destroy(crypter);
|
||||
return FALSE;
|
||||
}
|
||||
crypter->destroy(crypter);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool do_test_mct(test_vector_t *test)
|
||||
{
|
||||
crypter_t *crypter;
|
||||
chunk_t prev, *input, *output;
|
||||
int i, j;
|
||||
|
||||
crypter = lib->crypto->create_crypter(lib->crypto, ENCR_AES_CBC,
|
||||
test->key.len);
|
||||
if (!crypter)
|
||||
{
|
||||
DBG1(DBG_APP, "algorithm %N or key length (%d bits) not supported",
|
||||
encryption_algorithm_names, ENCR_AES_CBC, test->key.len * 8);
|
||||
return FALSE;
|
||||
}
|
||||
input = ctx.decrypt ? &test->cipher : &test->plain;
|
||||
output = ctx.decrypt ? &test->plain : &test->cipher;
|
||||
if (crypter->get_block_size(crypter) != input->len)
|
||||
{
|
||||
DBG1(DBG_APP, "MCT only works for input with a length of one block");
|
||||
crypter->destroy(crypter);
|
||||
return FALSE;
|
||||
}
|
||||
prev = chunk_alloca(input->len);
|
||||
/* assume initial IV as previous output */
|
||||
*output = chunk_clone(test->iv);
|
||||
for (i = 0; i < 100; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
{ /* we copied the original lines already */
|
||||
fprintf(ctx.out, "COUNT = %d\n", i);
|
||||
fprintf(ctx.out, "KEY = %+B\n", &test->key);
|
||||
fprintf(ctx.out, "IV = %+B\n", &test->iv);
|
||||
fprintf(ctx.out, "%s = %+B\n",
|
||||
ctx.decrypt ? "CIPHERTEXT" : "PLAINTEXT", input);
|
||||
}
|
||||
if (!crypter->set_key(crypter, test->key))
|
||||
{
|
||||
DBG1(DBG_APP, "failed to set key");
|
||||
return FALSE;
|
||||
}
|
||||
for (j = 0; j < 1000; j++)
|
||||
{
|
||||
/* store previous output as it is used as input after next */
|
||||
memcpy(prev.ptr, output->ptr, prev.len);
|
||||
chunk_free(output);
|
||||
if (!crypt(crypter, test))
|
||||
{
|
||||
crypter->destroy(crypter);
|
||||
return FALSE;
|
||||
}
|
||||
/* prepare the next IV (our API does not allow incremental calls) */
|
||||
if (ctx.decrypt)
|
||||
{
|
||||
memcpy(test->iv.ptr, input->ptr, test->iv.len);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(test->iv.ptr, output->ptr, test->iv.len);
|
||||
}
|
||||
/* the previous output is the next input */
|
||||
memcpy(input->ptr, prev.ptr, input->len);
|
||||
}
|
||||
fprintf(ctx.out, "%s = %+B\n\n",
|
||||
ctx.decrypt ? "PLAINTEXT" : "CIPHERTEXT", output);
|
||||
/* derive key for next round */
|
||||
switch (test->key.len)
|
||||
{
|
||||
case 16:
|
||||
memxor(test->key.ptr, output->ptr, output->len);
|
||||
break;
|
||||
case 24:
|
||||
memxor(test->key.ptr, prev.ptr + 8, 8);
|
||||
memxor(test->key.ptr + 8, output->ptr, output->len);
|
||||
break;
|
||||
case 32:
|
||||
memxor(test->key.ptr, prev.ptr, prev.len);
|
||||
memxor(test->key.ptr + prev.len, output->ptr, output->len);
|
||||
break;
|
||||
}
|
||||
/* the current output is used as IV for the next round */
|
||||
memcpy(test->iv.ptr, output->ptr, test->iv.len);
|
||||
}
|
||||
crypter->destroy(crypter);
|
||||
/* we return FALSE as we print the output ourselves */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool do_test(test_vector_t *test)
|
||||
{
|
||||
if (ctx.use_gcm)
|
||||
{
|
||||
return do_test_gcm(test);
|
||||
}
|
||||
if (ctx.use_mct)
|
||||
{
|
||||
return do_test_mct(test);
|
||||
}
|
||||
return do_test_cbc(test);
|
||||
}
|
||||
|
||||
static void usage(FILE *out, char *name)
|
||||
{
|
||||
fprintf(out, "Test AES implementation according to the AES Algorithm Validation Suite (AESAVS)\n");
|
||||
fprintf(out, "and the GCM Validation System (GCMVS)\n\n");
|
||||
fprintf(out, "%s [OPTIONS]\n\n", name);
|
||||
fprintf(out, "Options:\n");
|
||||
fprintf(out, " -h, --help print this help.\n");
|
||||
fprintf(out, " -d, --debug=LEVEL set debug level (default 1).\n");
|
||||
fprintf(out, " -m, --mode=MODE mode to test, either CBC or GCM (default CBC).\n");
|
||||
fprintf(out, " -t, --mct run Monte Carlo Test (MCT), only for CBC.\n");
|
||||
fprintf(out, " -x, --decrypt test decryption (not needed for CBC as files contain control directives).\n");
|
||||
fprintf(out, " -i, --in=FILE request file (default STDIN).\n");
|
||||
fprintf(out, " -o, --out=FILE response file (default STDOUT).\n");
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
test_vector_t test;
|
||||
|
||||
ctx.in = stdin;
|
||||
ctx.out = stdout;
|
||||
|
||||
library_init(NULL);
|
||||
atexit(library_deinit);
|
||||
|
||||
while (true)
|
||||
{
|
||||
struct option long_opts[] = {
|
||||
{"help", no_argument, NULL, 'h' },
|
||||
{"debug", required_argument, NULL, 'd' },
|
||||
{"mode", required_argument, NULL, 'm' },
|
||||
{"mct", no_argument, NULL, 't' },
|
||||
{"decrypt", no_argument, NULL, 'x' },
|
||||
{"in", required_argument, NULL, 'i' },
|
||||
{"out", required_argument, NULL, 'o' },
|
||||
{0,0,0,0 },
|
||||
};
|
||||
switch (getopt_long(argc, argv, "hd:m:txi:o:", long_opts, NULL))
|
||||
{
|
||||
case EOF:
|
||||
break;
|
||||
case 'h':
|
||||
usage(stdout, argv[0]);
|
||||
return 0;
|
||||
case 'd':
|
||||
dbg_default_set_level(atoi(optarg));
|
||||
continue;
|
||||
case 'm':
|
||||
if (strcaseeq(optarg, "GCM"))
|
||||
{
|
||||
ctx.use_gcm = TRUE;
|
||||
}
|
||||
else if (!strcaseeq(optarg, "CBC"))
|
||||
{
|
||||
usage(stderr, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
continue;
|
||||
case 't':
|
||||
ctx.use_mct = TRUE;
|
||||
continue;
|
||||
case 'x':
|
||||
ctx.decrypt = TRUE;
|
||||
continue;
|
||||
case 'i':
|
||||
ctx.in = fopen(optarg, "r");
|
||||
if (!ctx.in)
|
||||
{
|
||||
fprintf(stderr, "failed to open '%s': %s\n", optarg,
|
||||
strerror(errno));
|
||||
usage(stderr, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
continue;
|
||||
case 'o':
|
||||
ctx.out = fopen(optarg, "w");
|
||||
if (!ctx.out)
|
||||
{
|
||||
fprintf(stderr, "failed to open '%s': %s\n", optarg,
|
||||
strerror(errno));
|
||||
usage(stderr, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
continue;
|
||||
default:
|
||||
usage(stderr, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* TODO: maybe make plugins configurable */
|
||||
lib->plugins->load(lib->plugins, PLUGINS);
|
||||
lib->plugins->status(lib->plugins, LEVEL_CTRL);
|
||||
|
||||
while (get_next_test_vector(&test))
|
||||
{
|
||||
if (verify_test_vector(&test))
|
||||
{
|
||||
if (do_test(&test))
|
||||
{
|
||||
print_result(&test);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_APP, "test vector with missing data encountered");
|
||||
}
|
||||
fprintf(ctx.out, "\n");
|
||||
test_vector_free(&test);
|
||||
}
|
||||
|
||||
if (ctx.in != stdin)
|
||||
{
|
||||
fclose(ctx.in);
|
||||
}
|
||||
if (ctx.out != stdout)
|
||||
{
|
||||
fclose(ctx.out);
|
||||
}
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue