356 lines
10 KiB
C
356 lines
10 KiB
C
/* Copyright (c) (2012,2014-2020) Apple Inc. All rights reserved.
|
||
*
|
||
* corecrypto is licensed under Apple Inc.’s Internal Use License Agreement (which
|
||
* is contained in the License.txt file distributed with corecrypto) and only to
|
||
* people who accept that license. IMPORTANT: Any license rights granted to you by
|
||
* Apple Inc. (if any) are limited to internal use within your organization only on
|
||
* devices and computers you own or control, for the sole purpose of verifying the
|
||
* security characteristics and correct functioning of the Apple Software. You may
|
||
* not, directly or indirectly, redistribute the Apple Software or any portions thereof.
|
||
*/
|
||
|
||
#include "cc_config.h"
|
||
#if CC_LINUX
|
||
#define _GNU_SOURCE // For Dl_info
|
||
#endif
|
||
|
||
#include <fcntl.h>
|
||
#include <stdarg.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <sys/stat.h>
|
||
#include <sys/types.h>
|
||
|
||
#include <stdbool.h>
|
||
#if defined(_WIN32)
|
||
static int optind = 1;
|
||
#else
|
||
#include <unistd.h>
|
||
#include <dlfcn.h>
|
||
#endif
|
||
|
||
#include "testmore.h"
|
||
#include "testenv.h"
|
||
|
||
#include <corecrypto/cc_macros.h>
|
||
#include <corecrypto/ccrng.h>
|
||
#include "../corecrypto_test/include/testbyteBuffer.h"
|
||
|
||
static int tests_printall(void);
|
||
|
||
#ifdef __ANDROID_API__
|
||
#include <android/log.h>
|
||
#define printf(...) __android_log_print(ANDROID_LOG_DEBUG, "corecrypto", __VA_ARGS__);
|
||
#define fprintf(FILE, ...) __android_log_print(ANDROID_LOG_DEBUG, "corecrypto", __VA_ARGS__);
|
||
#endif // __ANDROID_API__
|
||
|
||
static int
|
||
tests_summary(int verbose) {
|
||
int failed_tests = 0;
|
||
int todo_tests = 0;
|
||
int actual_tests = 0;
|
||
int planned_tests = 0;
|
||
int warning_tests = 0;
|
||
uint64_t duration_tests = 0;
|
||
|
||
// First compute the totals to help decide if we need to print headers or not.
|
||
for (int i = 0; testlist[i].name; ++i) {
|
||
if (testlist[i].executed) {
|
||
failed_tests += testlist[i].failed_tests;
|
||
todo_tests += testlist[i].todo_tests;
|
||
actual_tests += testlist[i].actual_tests;
|
||
planned_tests += testlist[i].planned_tests;
|
||
warning_tests += testlist[i].warning_tests;
|
||
duration_tests += testlist[i].duration;
|
||
}
|
||
}
|
||
|
||
fprintf(stdout, "\n[SUMMARY]\n");
|
||
|
||
// -v makes the summary verbose as well.
|
||
if (verbose || failed_tests || actual_tests != planned_tests || todo_tests || warning_tests) {
|
||
fprintf(stdout, "Test name failed warning todo ran planned\n");
|
||
fprintf(stdout, "============================================================================================\n");
|
||
}
|
||
for (int i = 0; testlist[i].name; ++i) {
|
||
if (testlist[i].executed) {
|
||
const char *token = NULL;
|
||
if (testlist[i].failed_tests) {
|
||
token = "FAIL";
|
||
} else if (testlist[i].actual_tests != testlist[i].planned_tests
|
||
|| (testlist[i].todo_tests)
|
||
|| (testlist[i].warning_tests)) {
|
||
token = "WARN";
|
||
} else if (verbose) {
|
||
token = "PASS";
|
||
}
|
||
if (token) {
|
||
fprintf(stdout, "[%s] %-49s %6d %6d %6d %6d %6d\n", token,
|
||
testlist[i].name,
|
||
testlist[i].failed_tests, testlist[i].warning_tests,
|
||
testlist[i].todo_tests,
|
||
testlist[i].actual_tests, testlist[i].planned_tests);
|
||
}
|
||
}
|
||
}
|
||
if (verbose || failed_tests || warning_tests || todo_tests || actual_tests != planned_tests) {
|
||
fprintf(stdout, "============================================================================================\n");
|
||
}
|
||
else {
|
||
fprintf(stdout, "Test name failed warning todo ran planned\n");
|
||
}
|
||
fprintf(stdout, "Totals (%6llus) %6d %6d %6d %6d %6d\n", duration_tests/1000, failed_tests, warning_tests, todo_tests, actual_tests, planned_tests);
|
||
return failed_tests;
|
||
}
|
||
|
||
#if defined(_WIN32)
|
||
static int tests_run_index(int i, int argc, char * const *argv, byteBuffer seed)
|
||
{
|
||
fprintf(stderr, "\n[BEGIN] %s\n", testlist[i].name);
|
||
|
||
run_one_test(&testlist[i], argc, argv, seed->len, seed->bytes);
|
||
if(testlist[i].failed_tests) {
|
||
fprintf(stderr, "[FAIL] %s\n", testlist[i].name);
|
||
} else {
|
||
fprintf(stderr, "duration: %llu ms\n", testlist[i].duration);
|
||
fprintf(stderr, "[PASS] %s\n", testlist[i].name);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
#else
|
||
static void usage(const char *progname)
|
||
{
|
||
fprintf(stderr, "usage: %s [-L][-v][-s seed][-w][testname [-v] ...]\n", progname);
|
||
fprintf(stderr, "\t-v verbose\n");
|
||
fprintf(stderr, "\t-s <seed> to provide a specific seed (ex 8686b151ec2aa17c4ec41a59e496d2ff), reused for each sub-test.\n");
|
||
fprintf(stderr, "\t-w sleep(100)\n");
|
||
fprintf(stderr, "\t-L list supported tests by test names\n");
|
||
fprintf(stderr, "Here is the list of supported tests:\n");
|
||
tests_printall();
|
||
exit(1);
|
||
}
|
||
|
||
static int tests_run_index(int i, int argc, char * const *argv, byteBuffer seed)
|
||
{
|
||
int verbose = 0;
|
||
int ch;
|
||
|
||
while ((ch = getopt(argc, argv, "v")) != -1) {
|
||
switch (ch) {
|
||
case 'v':
|
||
verbose++;
|
||
break;
|
||
default:
|
||
usage(argv[0]);
|
||
}
|
||
}
|
||
|
||
fprintf(stderr, "\n[BEGIN] %s\n", testlist[i].name);
|
||
run_one_test(&testlist[i], argc, argv, seed->len, seed->bytes);
|
||
|
||
if (testlist[i].failed_tests) {
|
||
fprintf(stderr, "[FAIL] %s\n", testlist[i].name);
|
||
} else {
|
||
fprintf(stderr, "duration: %llu ms\n", testlist[i].duration);
|
||
fprintf(stderr, "[PASS] %s\n", testlist[i].name);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int tests_named_index(const char *testcase)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; testlist[i].name; ++i) {
|
||
if (strcmp(testlist[i].name, testcase) == 0) {
|
||
return i;
|
||
}
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
#endif
|
||
|
||
static int tests_printall(void)
|
||
{
|
||
for (int i = 0; testlist[i].name; ++i) {
|
||
fprintf(stdout, "%s\n", testlist[i].name);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int tests_run_all(int argc, char * const *argv, byteBuffer seed)
|
||
{
|
||
int curroptind = optind;
|
||
int i;
|
||
|
||
for (i = 0; testlist[i].name; ++i) {
|
||
tests_run_index(i, argc, argv,seed);
|
||
optind = curroptind;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
#if CC_DARWIN || CC_LINUX
|
||
static off_t fsize(const char *fname)
|
||
{
|
||
struct stat st;
|
||
return (stat(fname, &st) == 0)? st.st_size:-1;
|
||
}
|
||
|
||
static void print_lib_path(void)
|
||
{
|
||
Dl_info dl_info;
|
||
if( dladdr((void *)cc_clear, &dl_info) != 0){
|
||
fprintf(stderr, "corecrypto loaded: %s\nDYLIB size %lld bytes\n\n", dl_info.dli_fname, fsize(dl_info.dli_fname));
|
||
}
|
||
}
|
||
#elif defined(_MSC_VER)
|
||
static void print_lib_path(void) {}
|
||
#endif
|
||
|
||
static int tests_init(byteBuffer *pSeedBuffer,const char *seedInput) {
|
||
printf("[TEST] === corecrypto ===\n");
|
||
print_lib_path();
|
||
int status=-1;
|
||
// Set a seed for reproducibility
|
||
if (seedInput!=NULL) {
|
||
*pSeedBuffer=hexStringToBytes(seedInput);
|
||
if (*pSeedBuffer) {
|
||
printByteBuffer(*pSeedBuffer,"\nInput seed value:");
|
||
status=0;
|
||
}
|
||
else{
|
||
printf("Error with input seed value: %s",seedInput);
|
||
}
|
||
} else {
|
||
// If the seed is not in the argument, we generate one
|
||
size_t entropy_size=16; // Default size of the seed
|
||
cc_require((*pSeedBuffer=mallocByteBuffer(entropy_size))!=NULL,errOut);
|
||
struct ccrng_state *rng = ccrng(&status);
|
||
cc_require(rng!=NULL, errOut);
|
||
cc_require((status=ccrng_generate(rng, (*pSeedBuffer)->len, (*pSeedBuffer)->bytes))==0, errOut);
|
||
printByteBuffer(*pSeedBuffer,"\nRandom seed value:");
|
||
printf("Seed used for every subtest. To reproduce a failure, you can run with '-s <seed> <subtest>'\n");
|
||
}
|
||
|
||
errOut:
|
||
return status;
|
||
}
|
||
|
||
#if defined(_WIN32)
|
||
int
|
||
tests_begin(int argc, char * const *argv)
|
||
{
|
||
const char *seed=NULL;
|
||
byteBuffer seedBuffer=NULL; //seed for test drbg
|
||
int list = 0;
|
||
int retval;
|
||
int verbose = 0;
|
||
|
||
printf("Command-line options are currently not supported on Windows.\n");
|
||
|
||
if ((retval=tests_init(&seedBuffer, seed)) != 0) {
|
||
printf("%08x unable to initialize tests", retval);
|
||
return -1;
|
||
}
|
||
tests_run_all(argc, argv, seedBuffer);
|
||
|
||
if (list) {
|
||
tests_printall();
|
||
retval = 0;
|
||
}
|
||
else {
|
||
retval = tests_summary(verbose);
|
||
}
|
||
/* Cleanups */
|
||
free(seedBuffer);
|
||
|
||
retval = tests_summary(verbose);
|
||
return retval;
|
||
}
|
||
#else
|
||
|
||
int
|
||
tests_begin(int argc, char * const *argv)
|
||
{
|
||
const char *seed=NULL;
|
||
byteBuffer seedBuffer=NULL;
|
||
int retval;
|
||
int verbose = 0;
|
||
const char *testcase = NULL;
|
||
bool initialized = false;
|
||
int testix = -1;
|
||
int ch;
|
||
|
||
for (;;) {
|
||
while (!testcase && (ch = getopt(argc, argv, "Lvws:")) != -1) {
|
||
switch (ch) {
|
||
case 's': // seed provided
|
||
// The same seed is reused for all of the tests
|
||
seed = optarg;
|
||
break;
|
||
case 'w': // wait
|
||
sleep(100);
|
||
break;
|
||
case 'v': // verbose
|
||
verbose=1;
|
||
break;
|
||
case 'L': // List test for test discovery
|
||
tests_printall();
|
||
exit(0);
|
||
case '?':
|
||
default:
|
||
printf("invalid option %c\n",ch);
|
||
usage(argv[0]);
|
||
}
|
||
}
|
||
|
||
if (optind < argc) {
|
||
testix = tests_named_index(argv[optind]);
|
||
if (testix < 0) {
|
||
printf("invalid test %s\n",argv[optind]);
|
||
usage(argv[0]);
|
||
}
|
||
testcase = argv[optind];
|
||
argc -= optind;
|
||
argv += optind;
|
||
optind = 1;
|
||
}
|
||
|
||
if (testix < 0) {
|
||
// Not test specified or reached end of list
|
||
if (!initialized) {
|
||
// Not test specified
|
||
if (tests_init(&seedBuffer, seed) != 0) {
|
||
return -1;
|
||
}
|
||
tests_run_all(argc, argv, seedBuffer);
|
||
}
|
||
break;
|
||
} else {
|
||
if (!initialized) {
|
||
if (tests_init(&seedBuffer, seed) != 0) {
|
||
return -1;
|
||
}
|
||
initialized = true;
|
||
}
|
||
tests_run_index(testix, argc, argv,seedBuffer);
|
||
testix = -1;
|
||
}
|
||
}
|
||
|
||
/* Cleanups */
|
||
free(seedBuffer);
|
||
|
||
retval=tests_summary(verbose);
|
||
return retval;
|
||
}
|
||
#endif
|