/* Copyright (c) (2010-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 "testmore.h" #include "testbyteBuffer.h" #if (CCRNG == 0) entryPoint(ccrng_tests,"ccrng") #else #include #include #include #include #include #include #include #include #include #include "../ccdrbg/src/ccdrbg_nistctr.h" #include "ccrng_cryptographic_priv.h" #include #include "ccrng_internal.h" #include "crypto_test_rng.h" static int verbose=0; //============================================================================== // // HELPERS // //============================================================================== static int compare64_t(const void *va, const void *vb) { uint64_t a = *((const uint64_t *) va); uint64_t b = *((const uint64_t *) vb); if(a < b) return -1; if(a == b) return 0; return 1; } int find_duplicate_uint64(uint64_t *array, size_t array_size) { qsort(array, array_size, sizeof(uint64_t), compare64_t); int rc=1; // success for(size_t i = 0; i < (array_size-1); i++) { if(verbose) diag("Comparing %d\n", i); rc&=ok(array[i] != array[i+1], "items %i and %i are the same:%"PRIx64, i, i+1, array[i]); } return rc; } static int no_repeat(struct ccrng_state *rng) { uint64_t results[128]; int status; for(int i = 0; i<128; i++) { if(verbose) diag("Creating %d\n", i); status = ccrng_generate(rng, 8, &results[i]); ok_or_fail(status == 0, "got random bytes"); } return find_duplicate_uint64(results,128); } static int zero_length(struct ccrng_state *rng) { int status = ccrng_generate(rng, 0, NULL); if (status!=0) { status=0; // fail goto errOut; } status=1; errOut: return status; } static int must_repeat(struct ccrng_state *rng) { uint64_t results[2]; int status; status = ccrng_generate(rng, 8, &results[0]); ok_or_fail(status == 0, "got bytes"); status = ccrng_generate(rng, 8, &results[1]); ok_or_fail(status == 0, "got bytes"); ok_or_fail(results[0] == results[1], "results match"); return 1; } static int too_many(struct ccrng_state *rng, size_t limit, size_t split) { // Assume that there are "limit" bytes available, no less, no more. uint64_t results[limit+1]; int status; size_t done=0; for (size_t i=0;i= CCRNG_CSPRNG_TEST_ENTROPY_LENGTH) { *entropy_len = CCRNG_CSPRNG_TEST_ENTROPY_LENGTH; } return cc_get_entropy(*entropy_len, entropy); } static bool ccrng_csprng_test_needreseed_always(void *reseed_ctx) { (void) reseed_ctx; return true; } static void ccrng_csprng_test_reseedcomplete_always(void *reseed_ctx) { (void) reseed_ctx; } static int csprng_test(void) { struct ccrng_csprng_state rng; rng.getentropy = entropy_system; rng.getentropy_ctx = NULL; rng.needreseed = ccrng_csprng_test_needreseed_always; rng.reseedcomplete = ccrng_csprng_test_reseedcomplete_always; rng.reseed_ctx = NULL; struct ccdrbg_nistctr_custom drbg_custom; drbg_custom.ctr_info = ccaes_ctr_crypt_mode(); drbg_custom.keylen = 32; drbg_custom.strictFIPS = 1; drbg_custom.use_df = 1; ccdrbg_factory_nistctr(&rng.drbg_info, &drbg_custom); // DO NOT INSTANTIATE ccrng_csprng_state like this int err = ccrng_csprng_init(&rng, sizeof(salt), salt, sizeof(salt), salt, sizeof(salt), salt); if (err != CCERR_OK) { diag("ccrng_csprng init failure"); return -1; } ok(no_repeat((struct ccrng_state *)&rng), "csprng no repeat"); return 0; } //============================================================================== // // Test RNGs one by one // //============================================================================== int ccrng_tests(TM_UNUSED int argc, TM_UNUSED char *const *argv) { int test_system_rng = 1; int test_crypto_nist_rng = 1; int test_pbkdf2_rng = 1; int test_drbg_rng = 1; int test_sequence = 1; int status; plan_tests(266116 + 264000 + /*N_TEST 250 and 500*/ 2099208 /* ccrng_uniform tests */ ); fortuna_test_kat(); csprng_test_kat(); csprng_test(); #if CC_RNG_MULTITHREAD_USER pthread_atfork(ccrng_cryptographic_atfork_prepare, ccrng_cryptographic_atfork_parent, ccrng_cryptographic_atfork_child); #endif //========================================================================== // // CCRNG_DRBG // //========================================================================== if(test_drbg_rng) { if(verbose) diag("Testing drbg rng\n"); struct ccrng_drbg_state drbg_ctx; struct ccdrbg_info info; char *expect_result_init; char *expect_result_reseed; // DRBG - NIST CTR struct ccdrbg_nistctr_custom custom_ctr = { .ctr_info = ccaes_ctr_crypt_mode(), .keylen = 16, .strictFIPS = 0, .use_df = 1, }; // DRBG - NIST HMAC struct ccdrbg_nisthmac_custom custom_hmac = { .di = ccsha256_di(), .strictFIPS = 0, }; for (size_t i=0;i<2;i++) { switch (i) { case 0: { ccdrbg_factory_nistctr(&info, &custom_ctr); expect_result_init="0e5bc77112a93a7089420072c7bb4627"; expect_result_reseed="4a218a3d3a0d76e77108099993cc4028"; break; } case 1: { ccdrbg_factory_nisthmac(&info, &custom_hmac); expect_result_init="65d8e3c527fb85df1a943b1dea7bd647"; expect_result_reseed="c8cdc03ade8233b9e5f769a970148561"; break; } default: cc_assert(false); // should never get here expect_result_init=NULL; expect_result_reseed=NULL; break; } // Actual tests // Preferred interface struct ccdrbg_state *state = malloc(info.size); const char const_seed[]="corecrypto drbg based rng"; const char drbg_init_salt[]="init 17byte seed"; const char drbg_reseed_salt[]="reseed 19byte seed"; status = ccdrbg_init(&info, state, sizeof(drbg_init_salt), drbg_init_salt, sizeof(drbg_init_salt), drbg_init_salt, sizeof(const_seed), const_seed); ok(status == 0, "Initialize DRBG"); status = ccrng_drbg_init_withdrbg(&drbg_ctx, &info, state); ok(status == 0, "Make RNG-DRBG context"); if(status == 0) { ok(must_be((struct ccrng_state *)&drbg_ctx, 16, expect_result_init), "Got expected pattern init"); ok(no_repeat((struct ccrng_state *)&drbg_ctx), "successful generation"); status = ccdrbg_init(&info, state, sizeof(drbg_init_salt), drbg_init_salt, sizeof(drbg_init_salt), drbg_init_salt, sizeof(const_seed), const_seed); ok(status == 0, "Reinitialize DRBG"); status = ccdrbg_reseed(&info, state, sizeof(drbg_reseed_salt), drbg_reseed_salt, 0, NULL); ok(status == 0, "Reseed DRBG"); ok(must_be((struct ccrng_state *)&drbg_ctx, 16, expect_result_reseed), "Got expected pattern reseed"); ok(no_repeat((struct ccrng_state *)&drbg_ctx), "successful generation"); ok(zero_length((struct ccrng_state *)&drbg_ctx), "zero length generation"); } ccdrbg_done(&info, state); // Legacy interface #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" status = ccrng_drbg_init(&drbg_ctx, &info, state, sizeof(drbg_init_salt), drbg_init_salt); ok(status == 0, "Made drbg context"); if(status == 0) { ok(must_be((struct ccrng_state *)&drbg_ctx, 16, expect_result_init), "Got expected pattern init"); ok(no_repeat((struct ccrng_state *)&drbg_ctx), "successful generation"); status = ccrng_drbg_init(&drbg_ctx, &info, state, sizeof(drbg_init_salt), drbg_init_salt); ok(status == 0, "Re-Init drbg context"); status = ccrng_drbg_reseed(&drbg_ctx, sizeof(drbg_reseed_salt), drbg_reseed_salt, 0, NULL); ok(status == 0, "Reseed drbg context"); ok(must_be((struct ccrng_state *)&drbg_ctx, 16, expect_result_reseed), "Got expected pattern reseed"); ok(no_repeat((struct ccrng_state *)&drbg_ctx), "successful generation"); ok(zero_length((struct ccrng_state *)&drbg_ctx), "zero length generation"); } ccrng_drbg_done(&drbg_ctx); free(state); #pragma clang diagnostic pop } } //========================================================================== // // ccrng_system (deprecated, use ccrng_cryptographic under the hood) // //========================================================================== #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" if(test_system_rng) { if(verbose) diag("Testing system rng\n"); struct ccrng_system_state system_ctx; status = ccrng_system_init(&system_ctx); ok(status >= 0, "Made system_rng context"); if(status >= 0) { struct ccrng_state *crypto_ctx = ccrng(NULL); isnt(crypto_ctx, NULL, "Made crypto context"); is(system_ctx.generate, crypto_ctx->generate, "System RNG passthrough"); ccrng_system_done(&system_ctx); } } #pragma clang diagnostic pop //========================================================================== // // ccrng_cryptographic // //========================================================================== if(test_crypto_nist_rng) { if(verbose) diag("Testing crypto rng\n"); struct ccrng_state* crypto_ctx=ccrng(&status); ok(status == 0, "Crypto RNG Made crypto context"); // Test basic no repeatance if(status == 0) { ok(no_repeat(crypto_ctx), "Crypto RNG successful generation"); } // Generate more than the reseed limit in a single call size_t tmp_size = CCDRBG_MAX_REQUEST_SIZE+8; uint8_t *tmp = malloc(tmp_size); status = ccrng_generate(crypto_ctx, tmp_size, tmp); ok_or_fail(status == 0, "Crypto RNG got random bytes"); // Generate multiple times to trigger prediction_break for (size_t i=0;i<10;i++) { status = ccrng_generate(crypto_ctx, 10, tmp); ok_or_fail(status == 0, "Crypto RNG got random bytes"); } // Thread safe test ok(multi_thread_test(crypto_ctx), "Crypto RNG thread safe test"); // Try again without getting the error code crypto_ctx=ccrng(NULL); ok(no_repeat(crypto_ctx), "Crypto RNG successful generation"); free(tmp); } //========================================================================== // // ccrng_pbkdf2 // //========================================================================== if(test_pbkdf2_rng) { if(verbose) diag("Testing pbkdf2 rng\n"); struct ccrng_pbkdf2_prng_state pbkdf2rng_ctx; status = ccrng_pbkdf2_prng_init(&pbkdf2rng_ctx, 4096, 14, "Test_test_test", saltlen, salt, 1024); ok(status == 0, "Made pbkdf2_rng context"); if(status == 0) { ok(must_be((struct ccrng_state *)&pbkdf2rng_ctx, 16, "7bad1f07c4ff2c9d287974f05cd5ea55"), "Got expected pattern"); ok(no_repeat((struct ccrng_state *)&pbkdf2rng_ctx), "successful generation"); ok(zero_length((struct ccrng_state *)&pbkdf2rng_ctx), "zero length generation"); } // Negative testing to limits, one shot ok(ccrng_pbkdf2_prng_init(&pbkdf2rng_ctx, 64, 14, "Test_test_test", saltlen, salt, 10)==0,"Reinit context"); ok(too_many((struct ccrng_state *)&pbkdf2rng_ctx,64,0), "too many"); // Negative testing to limits, 2 calls ok(ccrng_pbkdf2_prng_init(&pbkdf2rng_ctx, 64, 14, "Test_test_test", saltlen, salt, 10)==0,"Reinit context"); ok(too_many((struct ccrng_state *)&pbkdf2rng_ctx,64,1), "too many"); // Negative testing to limits, 4 calls ok(ccrng_pbkdf2_prng_init(&pbkdf2rng_ctx, 64, 14, "Test_test_test", saltlen, salt, 10)==0,"Reinit context"); ok(too_many((struct ccrng_state *)&pbkdf2rng_ctx,64,3), "too many"); // Negative testing, invalid initialization ok(ccrng_pbkdf2_prng_init(&pbkdf2rng_ctx, sizeof(pbkdf2rng_ctx.random_buffer)+1, 14, "Test_test_test", saltlen, salt, 10)==CCERR_PARAMETER,"Reinit context"); ok(too_many((struct ccrng_state *)&pbkdf2rng_ctx,0,0), "any is too many when init fails"); } //========================================================================== // // ccrng_sequence (for testing only) // //========================================================================== if(test_sequence) { if(verbose) diag("Testing sequence rng\n"); struct ccrng_sequence_state sequence_ctx; status = ccrng_sequence_init(&sequence_ctx, saltlen, salt); ok(status == 0, "Made sequence context"); if(status == 0) { ok(must_repeat((struct ccrng_state *)&sequence_ctx), "successful generation"); ok(must_be((struct ccrng_state *)&sequence_ctx, saltlen, "000102030405060708090a0b0c0d0e0f"), "Got expected pattern"); ok(zero_length((struct ccrng_state *)&sequence_ctx), "zero length generation"); } } // ccrng_uniform ok(test_rng_uniform(), "ccrng_uniform tests"); return 1; } #endif