#include "CommonRandomSPI.h"
#include <dispatch/dispatch.h>
#include <dispatch/queue.h>
#include <corecrypto/ccaes.h>
#include <corecrypto/ccdrbg.h>
#include <corecrypto/ccrng_CommonCrypto.h>
#include <corecrypto/ccrng_system.h>
#include "ccMemory.h"
#include "ccdebug.h"
typedef struct __CCRandom {
uint32_t rngtype;
union {
struct ccrng_system_state *devrandom;
struct ccrng_CommonCrypto_state *drbg;
} state;
union {
uint8_t *bytes;
struct ccdrbg_info *drbg;
} info;
struct ccdrbg_state *drbg_state;
} ccInternalRandom, *ccInternalRandomRef;
static const uint32_t rng_undefined = 0;
static const uint32_t rng_default = 1;
static const uint32_t rng_devrandom = 2;
static const uint32_t rng_created = 99;
ccInternalRandom ccRandomDefaultStruct = {
.rngtype = rng_default,
};
ccInternalRandom ccRandomDevRandomStruct = {
.rngtype = rng_devrandom,
};
CCRandomRef kCCRandomDefault = &ccRandomDefaultStruct;
CCRandomRef kCCRandomDevRandom = &ccRandomDevRandomStruct;
struct ccrng_state *
ccDevRandomGetRngState()
{
static dispatch_once_t rnginit;
dispatch_once(&rnginit, ^{
kCCRandomDevRandom->state.devrandom = (struct ccrng_state *) CC_XMALLOC(sizeof(struct ccrng_system_state));
ccrng_system_init(kCCRandomDevRandom->state.devrandom);
});
return kCCRandomDevRandom->state.devrandom;
}
int
ccDevRandomReadBytes(void *ptr, size_t length)
{
for(int retries = 5; retries && ccrng_generate(kCCRandomDevRandom->state.devrandom, length, ptr); retries--)
if(retries == 0) return -1;
return 0;
}
static int
ccInitDRBG(ccInternalRandomRef drbg, struct ccdrbg_nistctr_custom *options, int function_options)
{
CCRNGStatus retval = kCCSuccess;
retval = kCCMemoryFailure;
if((drbg->info.drbg = CC_XMALLOC(sizeof(struct ccdrbg_info))) == NULL) goto errOut;
ccdrbg_factory_nistctr(drbg->info.drbg, options);
if((drbg->drbg_state = CC_XMALLOC(drbg->info.drbg->size)) == NULL) goto errOut;
if((drbg->state.drbg = CC_XMALLOC(sizeof(struct ccrng_CommonCrypto_state))) == NULL) goto errOut;
if(ccrng_CommonCrypto_init(drbg->state.drbg, drbg->info.drbg, drbg->drbg_state, function_options)) {
retval = kCCDecodeError;
goto errOut;
}
return 0;
errOut:
if(drbg->info.drbg) {
if(drbg->state.drbg) CC_XFREE(drbg->state.drbg, sizeof(struct ccrng_CommonCrypto_state));
if(drbg->drbg_state) CC_XFREE(drbg->drbg_state, drbg->info.drbg->size);
CC_XFREE(drbg->info.drbg, sizeof(struct ccdrbg_info));
}
return retval;
}
#ifndef NDEBUG
#define ASSERT(s)
#else
#define ASSERT(s) assert(s)
#endif
static const struct ccdrbg_nistctr_custom CCDRGBcustom = {
.ecb = &ccaes_ltc_ecb_encrypt_mode,
.keylen = 16,
.strictFIPS = 1,
.use_df = 1
};
static struct ccdrbg_info CCDRGBinfo;
#define KNOWN_DRGB_STATE_SIZE 1160
static uint8_t CCDRGBstate[KNOWN_DRGB_STATE_SIZE];
struct ccrng_CommonCrypto_state CCDRGBrngstate;
struct ccrng_state *
ccDRBGGetRngState()
{
static dispatch_once_t rnginit;
dispatch_once(&rnginit, ^{
kCCRandomDefault->info.drbg = &CCDRGBinfo;
ccdrbg_factory_nistctr(kCCRandomDefault->info.drbg, &CCDRGBcustom);
ASSERT(kCCRandomDefault->info.drbg->size <= sizeof(CCDRGBstate));
kCCRandomDefault->drbg_state = CCDRGBstate;
kCCRandomDefault->state.drbg = &CCDRGBrngstate;
if(ccrng_CommonCrypto_init(&CCDRGBrngstate, &CCDRGBinfo, CCDRGBstate, 0)) {
kCCRandomDefault = NULL;
}
});
ASSERT(kCCRandomDefault != NULL);
if(kCCRandomDefault == NULL) return NULL;
return kCCRandomDefault->state.drbg;
}
int
ccDRBGReadBytes(struct ccrng_CommonCrypto_state *state, void *ptr, size_t length)
{
ccrng_generate(state, length, ptr);
return 0;
}
CCRNGStatus
CCRNGCreate(uint32_t options, CCRandomRef *rngRef)
{
CCRNGStatus retval;
ccInternalRandomRef ref;
struct ccdrbg_nistctr_custom custom_options;
CC_DEBUG_LOG(ASL_LEVEL_ERR, "Entering\n");
ref = CC_XMALLOC(sizeof(ccInternalRandom));
if(NULL == ref) return kCCMemoryFailure;
ref->rngtype = rng_created;
custom_options.ecb = &ccaes_ltc_ecb_encrypt_mode;
custom_options.keylen = 16;
custom_options.strictFIPS = 1;
custom_options.use_df = 1;
if(retval = ccInitDRBG(ref, &custom_options, options)) return retval;
*rngRef = ref;
return kCCSuccess;
}
CCRNGStatus
CCRNGRelease(CCRandomRef rng)
{
CC_DEBUG_LOG(ASL_LEVEL_ERR, "Entering\n");
if(rng->rngtype == rng_created) {
ccrng_CommonCrypto_done(rng->state.drbg);
CC_XFREE(rng, sizeof(ccInternalRandom));
}
return kCCSuccess;
}
int CCRandomCopyBytes(CCRandomRef rnd, void *bytes, size_t count)
{
struct ccrng_state *rng;
CC_DEBUG_LOG(ASL_LEVEL_ERR, "Entering rnd(NULL) = %s\n", (rnd == NULL) ? "TRUE": "FALSE");
if(NULL == bytes) return -1;
if(0 == count) return 0;
if(NULL == rnd) {
rng = ccDRBGGetRngState();
return ccDRBGReadBytes(rng, bytes, count);
}
switch(rnd->rngtype) {
case rng_default:
rng = ccDRBGGetRngState();
return ccDRBGReadBytes(rng, bytes, count);
break;
case rng_devrandom:
rng = ccDevRandomGetRngState();
return ccDevRandomReadBytes(bytes, count);
break;
case rng_created:
return ccDRBGReadBytes(rnd->state.drbg, bytes, count);
break;
default: rng = ccDRBGGetRngState();
return ccDRBGReadBytes(rng, bytes, count);
break;
}
}