diff --git a/ctaocrypt/src/error.c b/ctaocrypt/src/error.c index eebc6cfb7..ce9d1d046 100644 --- a/ctaocrypt/src/error.c +++ b/ctaocrypt/src/error.c @@ -274,6 +274,9 @@ const char* CTaoCryptGetErrorString(int error) case ASN_NAME_INVALID_E: return "Name Constraint error"; + case RNG_FAILURE_E: + return "Random Number Generator failed"; + default: return "unknown error number"; diff --git a/ctaocrypt/src/random.c b/ctaocrypt/src/random.c index 22643e3ad..7442fe123 100644 --- a/ctaocrypt/src/random.c +++ b/ctaocrypt/src/random.c @@ -30,10 +30,16 @@ */ +#ifdef HAVE_FIPS + /* set NO_WRAPPERS before headers, use direct internal f()s not wrappers */ + #define FIPS_NO_WRAPPERS +#endif + #include #include #if defined(HAVE_HASHDRBG) || defined(NO_RC4) + #include #ifdef NO_INLINE @@ -74,9 +80,16 @@ #define NONCE_SZ (ENTROPY_SZ/2) #define ENTROPY_NONCE_SZ (ENTROPY_SZ+NONCE_SZ) -#define DRBG_SUCCESS 0 -#define DRBG_ERROR 1 -#define DRBG_NEED_RESEED 2 +/* Internal return codes */ +#define DRBG_SUCCESS 0 +#define DRBG_ERROR 1 +#define DRBG_FAILURE 2 +#define DRBG_NEED_RESEED 3 + +/* RNG health states */ +#define DRBG_NOT_INIT 0 +#define DRBG_OK 1 +#define DRBG_FAILED 2 enum { @@ -88,10 +101,11 @@ enum { }; +/* Hash Derivation Function */ +/* Returns: DRBG_SUCCESS or DRBG_FAILURE */ static int Hash_df(RNG* rng, byte* out, word32 outSz, byte type, - byte* inA, word32 inASz, - byte* inB, word32 inBSz, - byte* inC, word32 inCSz) + const byte* inA, word32 inASz, + const byte* inB, word32 inBSz) { byte ctr; int i; @@ -107,33 +121,29 @@ static int Hash_df(RNG* rng, byte* out, word32 outSz, byte type, for (i = 0, ctr = 1; i < len; i++, ctr++) { if (InitSha256(&rng->sha) != 0) - return DRBG_ERROR; + return DRBG_FAILURE; if (Sha256Update(&rng->sha, &ctr, sizeof(ctr)) != 0) - return DRBG_ERROR; + return DRBG_FAILURE; if (Sha256Update(&rng->sha, (byte*)&bits, sizeof(bits)) != 0) - return DRBG_ERROR; + return DRBG_FAILURE; /* churning V is the only string that doesn't have * the type added */ if (type != drbgInitV) if (Sha256Update(&rng->sha, &type, sizeof(type)) != 0) - return DRBG_ERROR; + return DRBG_FAILURE; if (Sha256Update(&rng->sha, inA, inASz) != 0) - return DRBG_ERROR; + return DRBG_FAILURE; if (inB != NULL && inBSz > 0) if (Sha256Update(&rng->sha, inB, inBSz) != 0) - return DRBG_ERROR; - - if (inC != NULL && inCSz > 0) - if (Sha256Update(&rng->sha, inC, inCSz) != 0) - return DRBG_ERROR; + return DRBG_FAILURE; if (Sha256Final(&rng->sha, rng->digest) != 0) - return DRBG_ERROR; + return DRBG_FAILURE; if (outSz > OUTPUT_BLOCK_LEN) { XMEMCPY(out, rng->digest, OUTPUT_BLOCK_LEN); @@ -149,26 +159,26 @@ static int Hash_df(RNG* rng, byte* out, word32 outSz, byte type, } -static int Hash_DRBG_Reseed(RNG* rng, byte* entropy, word32 entropySz) +/* Returns: DRBG_SUCCESS or DRBG_FAILURE */ +static int Hash_DRBG_Reseed(RNG* rng, const byte* entropy, word32 entropySz) { - int ret; byte seed[DRBG_SEED_LEN]; - ret = Hash_df(rng, seed, sizeof(seed), drbgReseed, rng->V, sizeof(rng->V), - entropy, entropySz, NULL, 0); - if (ret != 0) - return ret; + if (Hash_df(rng, seed, sizeof(seed), drbgReseed, rng->V, sizeof(rng->V), + entropy, entropySz) != DRBG_SUCCESS) { + return DRBG_FAILURE; + } XMEMCPY(rng->V, seed, sizeof(rng->V)); XMEMSET(seed, 0, sizeof(seed)); - ret = Hash_df(rng, rng->C, sizeof(rng->C), drbgInitC, rng->V, - sizeof(rng->V), NULL, 0, NULL, 0); - if (ret != 0) - return ret; + if (Hash_df(rng, rng->C, sizeof(rng->C), drbgInitC, rng->V, + sizeof(rng->V), NULL, 0) != DRBG_SUCCESS) { + return DRBG_FAILURE; + } rng->reseedCtr = 1; - return 0; + return DRBG_SUCCESS; } static INLINE void array_add_one(byte* data, word32 dataSz) @@ -182,26 +192,23 @@ static INLINE void array_add_one(byte* data, word32 dataSz) } } -static int Hash_gen(RNG* rng, byte* out, word32 outSz, byte* V) + +/* Returns: DRBG_SUCCESS or DRBG_FAILURE */ +static int Hash_gen(RNG* rng, byte* out, word32 outSz, const byte* V) { byte data[DRBG_SEED_LEN]; - int i, ret; + int i; int len = (outSz / OUTPUT_BLOCK_LEN) + ((outSz % OUTPUT_BLOCK_LEN) ? 1 : 0); XMEMCPY(data, V, sizeof(data)); for (i = 0; i < len; i++) { - ret = InitSha256(&rng->sha); - if (ret != 0) - return ret; + if (InitSha256(&rng->sha) != 0 || + Sha256Update(&rng->sha, data, sizeof(data)) != 0 || + Sha256Final(&rng->sha, rng->digest) != 0) { - ret = Sha256Update(&rng->sha, data, sizeof(data)); - if (ret != 0) - return ret; - - ret = Sha256Final(&rng->sha, rng->digest); - if (ret != 0) - return ret; + return DRBG_FAILURE; + } if (outSz > OUTPUT_BLOCK_LEN) { XMEMCPY(out, rng->digest, OUTPUT_BLOCK_LEN); @@ -215,11 +222,11 @@ static int Hash_gen(RNG* rng, byte* out, word32 outSz, byte* V) } XMEMSET(data, 0, sizeof(data)); - return 0; + return DRBG_SUCCESS; } -static INLINE void array_add(byte* d, word32 dLen, byte* s, word32 sLen) +static INLINE void array_add(byte* d, word32 dLen, const byte* s, word32 sLen) { word16 carry = 0; @@ -238,74 +245,67 @@ static INLINE void array_add(byte* d, word32 dLen, byte* s, word32 sLen) } +/* Returns: DRBG_SUCCESS, DRBG_NEED_RESEED, or DRBG_FAILURE */ static int Hash_DRBG_Generate(RNG* rng, byte* out, word32 outSz) { - int ret; + int ret = DRBG_NEED_RESEED; if (rng->reseedCtr != RESEED_INTERVAL) { byte type = drbgGenerateH; word32 reseedCtr = rng->reseedCtr; rng->reseedCtr++; - if (Hash_gen(rng, out, outSz, rng->V) != 0) - return DRBG_ERROR; - if (InitSha256(&rng->sha) != 0) - return DRBG_ERROR; - if (Sha256Update(&rng->sha, &type, sizeof(type)) != 0) - return DRBG_ERROR; - if (Sha256Update(&rng->sha, rng->V, sizeof(rng->V)) != 0) - return DRBG_ERROR; - if (Sha256Final(&rng->sha, rng->digest) != 0) - return DRBG_ERROR; + if (Hash_gen(rng, out, outSz, rng->V) != 0 || + InitSha256(&rng->sha) != 0 || + Sha256Update(&rng->sha, &type, sizeof(type)) != 0 || + Sha256Update(&rng->sha, rng->V, sizeof(rng->V)) != 0 || + Sha256Final(&rng->sha, rng->digest) != 0) { - array_add(rng->V, sizeof(rng->V), rng->digest, sizeof(rng->digest)); - array_add(rng->V, sizeof(rng->V), rng->C, sizeof(rng->C)); - #ifdef LITTLE_ENDIAN_ORDER - reseedCtr = ByteReverseWord32(reseedCtr); - #endif - array_add(rng->V, sizeof(rng->V), (byte*)&reseedCtr, sizeof(reseedCtr)); - ret = DRBG_SUCCESS; - } - else { - ret = DRBG_NEED_RESEED; + ret = DRBG_FAILURE; + } + else { + array_add(rng->V, sizeof(rng->V), rng->digest, sizeof(rng->digest)); + array_add(rng->V, sizeof(rng->V), rng->C, sizeof(rng->C)); + #ifdef LITTLE_ENDIAN_ORDER + reseedCtr = ByteReverseWord32(reseedCtr); + #endif + array_add(rng->V, sizeof(rng->V), + (byte*)&reseedCtr, sizeof(reseedCtr)); + ret = DRBG_SUCCESS; + } } + return ret; } -static int Hash_DRBG_Instantiate(RNG* rng, byte* seed, word32 seedSz, - byte* nonce, word32 nonceSz, byte* personal, word32 personalSz) +/* Returns: DRBG_SUCCESS or DRBG_FAILURE */ +static int Hash_DRBG_Instantiate(RNG* rng, const byte* seed, word32 seedSz, + const byte* nonce, word32 nonceSz) { - int ret; + int ret = DRBG_FAILURE; XMEMSET(rng, 0, sizeof(*rng)); - ret = Hash_df(rng, rng->V, sizeof(rng->V), drbgInitV, seed, seedSz, - nonce, nonceSz, personal, personalSz); - if (ret != 0) - return ret; + if (Hash_df(rng, rng->V, sizeof(rng->V), drbgInitV, seed, seedSz, + nonce, nonceSz) == DRBG_SUCCESS && + Hash_df(rng, rng->C, sizeof(rng->C), drbgInitC, rng->V, + sizeof(rng->V), NULL, 0) == DRBG_SUCCESS) { - ret = Hash_df(rng, rng->C, sizeof(rng->C), drbgInitC, rng->V, - sizeof(rng->V), NULL, 0, NULL, 0); - if (ret != 0) - return ret; + rng->reseedCtr = 1; + ret = DRBG_SUCCESS; + } - rng->reseedCtr = 1; - - return 0; + return ret; } +/* Returns: DRBG_SUCCESS */ static int Hash_DRBG_Uninstantiate(RNG* rng) { - int result = DRBG_ERROR; + XMEMSET(rng, 0, sizeof(*rng)); - if (rng != NULL) { - XMEMSET(rng, 0, sizeof(*rng)); - result = DRBG_SUCCESS; - } - - return result; + return DRBG_SUCCESS; } /* End NIST DRBG Code */ @@ -314,17 +314,27 @@ static int Hash_DRBG_Uninstantiate(RNG* rng) /* Get seed and key cipher */ int InitRng(RNG* rng) { - byte entropy[ENTROPY_NONCE_SZ]; - int ret = DRBG_ERROR; + int ret = BAD_FUNC_ARG; - /* This doesn't use a separate nonce. The entropy input will be - * the default size plus the size of the nonce making the seed - * size. */ - if (GenerateSeed(&rng->seed, entropy, ENTROPY_NONCE_SZ) == 0) - ret = Hash_DRBG_Instantiate(rng, entropy, ENTROPY_NONCE_SZ, - NULL, 0, NULL, 0); + if (rng != NULL) { + byte entropy[ENTROPY_NONCE_SZ]; - XMEMSET(entropy, 0, ENTROPY_NONCE_SZ); + /* This doesn't use a separate nonce. The entropy input will be + * the default size plus the size of the nonce making the seed + * size. */ + if (GenerateSeed(&rng->seed, entropy, ENTROPY_NONCE_SZ) == 0 && + Hash_DRBG_Instantiate(rng, entropy, ENTROPY_NONCE_SZ, + NULL, 0) == DRBG_SUCCESS) { + rng->status = DRBG_OK; + ret = 0; + } + else { + rng->status = DRBG_FAILED; + ret = RNG_FAILURE_E; + } + + XMEMSET(entropy, 0, ENTROPY_NONCE_SZ); + } return ret; } @@ -335,24 +345,36 @@ int RNG_GenerateBlock(RNG* rng, byte* output, word32 sz) { int ret; - XMEMSET(output, 0, sz); - ret = Hash_DRBG_Generate(rng, output, sz); + if (rng == NULL || output == NULL || sz > MAX_REQUEST_LEN) + return BAD_FUNC_ARG; - if (ret == DRBG_NEED_RESEED) { + if (rng->status != DRBG_OK) + return RNG_FAILURE_E; + + ret = Hash_DRBG_Generate(rng, output, sz); + if (ret == DRBG_SUCCESS) { + ret = 0; + } + else if (ret == DRBG_NEED_RESEED) { byte entropy[ENTROPY_SZ]; - ret = GenerateSeed(&rng->seed, entropy, ENTROPY_SZ); - if (ret == 0) { - ret = Hash_DRBG_Reseed(rng, entropy, ENTROPY_SZ); + if (GenerateSeed(&rng->seed, entropy, ENTROPY_SZ) == 0 && + Hash_DRBG_Reseed(rng, entropy, ENTROPY_SZ) == DRBG_SUCCESS && + Hash_DRBG_Generate(rng, output, sz) == DRBG_SUCCESS) { - if (ret == 0) - ret = Hash_DRBG_Generate(rng, output, sz); + ret = 0; + } + else { + ret = RNG_FAILURE_E; + rng->status = DRBG_FAILED; } - else - ret = DRBG_ERROR; XMEMSET(entropy, 0, ENTROPY_SZ); } + else { + ret = RNG_FAILURE_E; + rng->status = DRBG_FAILED; + } return ret; } @@ -364,11 +386,59 @@ int RNG_GenerateByte(RNG* rng, byte* b) } -void FreeRng(RNG* rng) +int FreeRng(RNG* rng) { - Hash_DRBG_Uninstantiate(rng); + int ret = BAD_FUNC_ARG; + + if (rng != NULL) { + if (Hash_DRBG_Uninstantiate(rng) == DRBG_SUCCESS) + ret = 0; + else + ret = RNG_FAILURE_E; + } + + return ret; } + +int RNG_HealthTest(int reseed, const byte* entropyA, word32 entropyASz, + const byte* entropyB, word32 entropyBSz, + const byte* output, word32 outputSz) +{ + RNG rng; + byte check[SHA256_DIGEST_SIZE * 4]; + + if (Hash_DRBG_Instantiate(&rng, entropyA, entropyASz, NULL, 0) != 0) + return -1; + + if (reseed) { + if (Hash_DRBG_Reseed(&rng, entropyB, entropyBSz) != 0) { + Hash_DRBG_Uninstantiate(&rng); + return -1; + } + } + + if (Hash_DRBG_Generate(&rng, check, sizeof(check)) != 0) { + Hash_DRBG_Uninstantiate(&rng); + return -1; + } + + if (Hash_DRBG_Generate(&rng, check, sizeof(check)) != 0) { + Hash_DRBG_Uninstantiate(&rng); + return -1; + } + + if (outputSz != sizeof(check) || XMEMCMP(output, check, sizeof(check))) { + Hash_DRBG_Uninstantiate(&rng); + return -1; + } + + Hash_DRBG_Uninstantiate(&rng); + + return 0; +} + + #else /* HAVE_HASHDRBG || NO_RC4 */ /* Get seed and key cipher */ diff --git a/ctaocrypt/test/test.c b/ctaocrypt/test/test.c index e19675bd9..776dbe57d 100644 --- a/ctaocrypt/test/test.c +++ b/ctaocrypt/test/test.c @@ -2581,6 +2581,74 @@ int camellia_test(void) #endif /* HAVE_CAMELLIA */ +#if defined(HAVE_HASHDRBG) || defined(NO_RC4) + +int random_test(void) +{ + const byte test1Entropy[] = + { + 0xa6, 0x5a, 0xd0, 0xf3, 0x45, 0xdb, 0x4e, 0x0e, 0xff, 0xe8, 0x75, 0xc3, + 0xa2, 0xe7, 0x1f, 0x42, 0xc7, 0x12, 0x9d, 0x62, 0x0f, 0xf5, 0xc1, 0x19, + 0xa9, 0xef, 0x55, 0xf0, 0x51, 0x85, 0xe0, 0xfb, 0x85, 0x81, 0xf9, 0x31, + 0x75, 0x17, 0x27, 0x6e, 0x06, 0xe9, 0x60, 0x7d, 0xdb, 0xcb, 0xcc, 0x2e + }; + const byte test1Output[] = + { + 0xd3, 0xe1, 0x60, 0xc3, 0x5b, 0x99, 0xf3, 0x40, 0xb2, 0x62, 0x82, 0x64, + 0xd1, 0x75, 0x10, 0x60, 0xe0, 0x04, 0x5d, 0xa3, 0x83, 0xff, 0x57, 0xa5, + 0x7d, 0x73, 0xa6, 0x73, 0xd2, 0xb8, 0xd8, 0x0d, 0xaa, 0xf6, 0xa6, 0xc3, + 0x5a, 0x91, 0xbb, 0x45, 0x79, 0xd7, 0x3f, 0xd0, 0xc8, 0xfe, 0xd1, 0x11, + 0xb0, 0x39, 0x13, 0x06, 0x82, 0x8a, 0xdf, 0xed, 0x52, 0x8f, 0x01, 0x81, + 0x21, 0xb3, 0xfe, 0xbd, 0xc3, 0x43, 0xe7, 0x97, 0xb8, 0x7d, 0xbb, 0x63, + 0xdb, 0x13, 0x33, 0xde, 0xd9, 0xd1, 0xec, 0xe1, 0x77, 0xcf, 0xa6, 0xb7, + 0x1f, 0xe8, 0xab, 0x1d, 0xa4, 0x66, 0x24, 0xed, 0x64, 0x15, 0xe5, 0x1c, + 0xcd, 0xe2, 0xc7, 0xca, 0x86, 0xe2, 0x83, 0x99, 0x0e, 0xea, 0xeb, 0x91, + 0x12, 0x04, 0x15, 0x52, 0x8b, 0x22, 0x95, 0x91, 0x02, 0x81, 0xb0, 0x2d, + 0xd4, 0x31, 0xf4, 0xc9, 0xf7, 0x04, 0x27, 0xdf + }; + const byte test2EntropyA[] = + { + 0x63, 0x36, 0x33, 0x77, 0xe4, 0x1e, 0x86, 0x46, 0x8d, 0xeb, 0x0a, 0xb4, + 0xa8, 0xed, 0x68, 0x3f, 0x6a, 0x13, 0x4e, 0x47, 0xe0, 0x14, 0xc7, 0x00, + 0x45, 0x4e, 0x81, 0xe9, 0x53, 0x58, 0xa5, 0x69, 0x80, 0x8a, 0xa3, 0x8f, + 0x2a, 0x72, 0xa6, 0x23, 0x59, 0x91, 0x5a, 0x9f, 0x8a, 0x04, 0xca, 0x68 + }; + const byte test2EntropyB[] = + { + 0xe6, 0x2b, 0x8a, 0x8e, 0xe8, 0xf1, 0x41, 0xb6, 0x98, 0x05, 0x66, 0xe3, + 0xbf, 0xe3, 0xc0, 0x49, 0x03, 0xda, 0xd4, 0xac, 0x2c, 0xdf, 0x9f, 0x22, + 0x80, 0x01, 0x0a, 0x67, 0x39, 0xbc, 0x83, 0xd3 + }; + const byte test2Output[] = + { + 0x04, 0xee, 0xc6, 0x3b, 0xb2, 0x31, 0xdf, 0x2c, 0x63, 0x0a, 0x1a, 0xfb, + 0xe7, 0x24, 0x94, 0x9d, 0x00, 0x5a, 0x58, 0x78, 0x51, 0xe1, 0xaa, 0x79, + 0x5e, 0x47, 0x73, 0x47, 0xc8, 0xb0, 0x56, 0x62, 0x1c, 0x18, 0xbd, 0xdc, + 0xdd, 0x8d, 0x99, 0xfc, 0x5f, 0xc2, 0xb9, 0x20, 0x53, 0xd8, 0xcf, 0xac, + 0xfb, 0x0b, 0xb8, 0x83, 0x12, 0x05, 0xfa, 0xd1, 0xdd, 0xd6, 0xc0, 0x71, + 0x31, 0x8a, 0x60, 0x18, 0xf0, 0x3b, 0x73, 0xf5, 0xed, 0xe4, 0xd4, 0xd0, + 0x71, 0xf9, 0xde, 0x03, 0xfd, 0x7a, 0xea, 0x10, 0x5d, 0x92, 0x99, 0xb8, + 0xaf, 0x99, 0xaa, 0x07, 0x5b, 0xdb, 0x4d, 0xb9, 0xaa, 0x28, 0xc1, 0x8d, + 0x17, 0x4b, 0x56, 0xee, 0x2a, 0x01, 0x4d, 0x09, 0x88, 0x96, 0xff, 0x22, + 0x82, 0xc9, 0x55, 0xa8, 0x19, 0x69, 0xe0, 0x69, 0xfa, 0x8c, 0xe0, 0x07, + 0xa1, 0x80, 0x18, 0x3a, 0x07, 0xdf, 0xae, 0x17 + }; + int ret; + + ret = RNG_HealthTest(0, test1Entropy, sizeof(test1Entropy), NULL, 0, + test1Output, sizeof(test1Output)); + if (ret != 0) return -39; + + ret = RNG_HealthTest(1, test2EntropyA, sizeof(test2EntropyA), + test2EntropyB, sizeof(test2EntropyB), + test2Output, sizeof(test2Output)); + if (ret != 0) return -40; + + return 0; +} + +#else /* HAVE_HASHDRBG || NO_RC4 */ + int random_test(void) { RNG rng; @@ -2600,6 +2668,8 @@ int random_test(void) return 0; } +#endif /* HAVE_HASHDRBG || NO_RC4 */ + #ifdef HAVE_NTRU diff --git a/cyassl/ctaocrypt/error-crypt.h b/cyassl/ctaocrypt/error-crypt.h index 131e248af..ced5a4748 100644 --- a/cyassl/ctaocrypt/error-crypt.h +++ b/cyassl/ctaocrypt/error-crypt.h @@ -125,6 +125,8 @@ enum { FIPS_NOT_ALLOWED_E = -197, /* FIPS not allowed error */ ASN_NAME_INVALID_E = -198, /* ASN name constraint error */ + RNG_FAILURE_E = -199, /* RNG Failed, Reinitialize */ + MIN_CODE_E = -200 /* errors -101 - -199 */ }; diff --git a/cyassl/ctaocrypt/random.h b/cyassl/ctaocrypt/random.h index 8111ac494..728c22209 100644 --- a/cyassl/ctaocrypt/random.h +++ b/cyassl/ctaocrypt/random.h @@ -84,6 +84,7 @@ typedef struct RNG { byte V[DRBG_SEED_LEN]; byte C[DRBG_SEED_LEN]; word32 reseedCtr; + byte status; } RNG; @@ -119,10 +120,33 @@ CYASSL_API int RNG_GenerateByte(RNG*, byte*); #if defined(HAVE_HASHDRBG) || defined(NO_RC4) - CYASSL_API void FreeRng(RNG*); + CYASSL_API int FreeRng(RNG*); + CYASSL_API int RNG_HealthTest(int reseed, + const byte* entropyA, word32 entropyASz, + const byte* entropyB, word32 entropyBSz, + const byte* output, word32 outputSz); #endif /* HAVE_HASHDRBG || NO_RC4 */ +#ifdef HAVE_FIPS + /* fips wrapper calls, user can call direct */ + CYASSL_API int InitRng_fips(RNG* rng); + CYASSL_API int FreeRng_fips(RNG* rng); + CYASSL_API int RNG_GenerateBlock_fips(RNG* rng, byte* buf, word32 bufSz); + CYASSL_API int RNG_HealthTest_fips(int reseed, + const byte* entropyA, word32 entropyASz, + const byte* entropyB, word32 entropyBSz, + const byte* output, word32 outputSz); + #ifndef FIPS_NO_WRAPPERS + /* if not impl or fips.c impl wrapper force fips calls if fips build */ + #define InitRng InitRng_fips + #define FreeRng FreeRng_fips + #define RNG_GenerateBlock RNG_GenerateBlock_fips + #define RNG_HealthTest RNG_HealthTest_fips + #endif /* FIPS_NO_WRAPPERS */ +#endif /* HAVE_FIPS */ + + #ifdef __cplusplus } /* extern "C" */ #endif