/* 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 #include #include #include #include #include #include #if defined(_WIN32) static int optind = 1; #else #include #include #endif #include "testmore.h" #include "testenv.h" #include #include #include "../corecrypto_test/include/testbyteBuffer.h" static int tests_printall(void); #ifdef __ANDROID_API__ #include #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 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 '\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