/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "blapi.h" #include "blapit.h" #include "Hacl_Chacha20.h" #include "nssilock.h" #include "seccomon.h" #include "secerr.h" #include "prinit.h" #define GLOBAL_BYTES_SIZE 100 static PRUint8 globalBytes[GLOBAL_BYTES_SIZE]; static unsigned long globalNumCalls = 0; static PZLock *rng_lock = NULL; static PRCallOnceType coRNGInit; static const PRCallOnceType pristineCallOnce; static PRStatus rng_init(void) { rng_lock = PZ_NewLock(nssILockOther); if (!rng_lock) { PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return PR_FAILURE; } /* --- LOCKED --- */ PZ_Lock(rng_lock); memset(globalBytes, 0, GLOBAL_BYTES_SIZE); PZ_Unlock(rng_lock); /* --- UNLOCKED --- */ return PR_SUCCESS; } SECStatus RNG_RNGInit(void) { /* Allow only one call to initialize the context */ if (PR_CallOnce(&coRNGInit, rng_init) != PR_SUCCESS) { return SECFailure; } return SECSuccess; } /* Take min(size, GLOBAL_BYTES_SIZE) bytes from data and use as seed and reset * the rng state. */ SECStatus RNG_RandomUpdate(const void *data, size_t bytes) { /* Check for a valid RNG lock. */ PORT_Assert(rng_lock != NULL); if (rng_lock == NULL) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } /* --- LOCKED --- */ PZ_Lock(rng_lock); memset(globalBytes, 0, GLOBAL_BYTES_SIZE); globalNumCalls = 0; if (data) { memcpy(globalBytes, (PRUint8 *)data, PR_MIN(bytes, GLOBAL_BYTES_SIZE)); } PZ_Unlock(rng_lock); /* --- UNLOCKED --- */ return SECSuccess; } SECStatus RNG_GenerateGlobalRandomBytes(void *dest, size_t len) { static const uint8_t key[32] = { 0 }; uint8_t nonce[12] = { 0 }; /* Check for a valid RNG lock. */ PORT_Assert(rng_lock != NULL); if (rng_lock == NULL) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } /* --- LOCKED --- */ PZ_Lock(rng_lock); memcpy(nonce, &globalNumCalls, sizeof(globalNumCalls)); globalNumCalls++; ChaCha20Poly1305Context *cx = ChaCha20Poly1305_CreateContext(key, sizeof(key), 16); if (!cx) { PORT_SetError(SEC_ERROR_NO_MEMORY); PZ_Unlock(rng_lock); return SECFailure; } memset(dest, 0, len); memcpy(dest, globalBytes, PR_MIN(len, GLOBAL_BYTES_SIZE)); Hacl_Chacha20_chacha20_encrypt(len, (uint8_t *)dest, (uint8_t *)dest, (uint8_t *)key, nonce, 0); ChaCha20Poly1305_DestroyContext(cx, PR_TRUE); PZ_Unlock(rng_lock); /* --- UNLOCKED --- */ return SECSuccess; } void RNG_RNGShutdown(void) { if (rng_lock) { PZ_DestroyLock(rng_lock); rng_lock = NULL; } coRNGInit = pristineCallOnce; } /* Test functions are not implemented! */ SECStatus PRNGTEST_Instantiate(const PRUint8 *entropy, unsigned int entropy_len, const PRUint8 *nonce, unsigned int nonce_len, const PRUint8 *personal_string, unsigned int ps_len) { return SECFailure; } SECStatus PRNGTEST_Reseed(const PRUint8 *entropy, unsigned int entropy_len, const PRUint8 *additional, unsigned int additional_len) { return SECFailure; } SECStatus PRNGTEST_Generate(PRUint8 *bytes, unsigned int bytes_len, const PRUint8 *additional, unsigned int additional_len) { return SECFailure; } SECStatus PRNGTEST_Uninstantiate() { return SECFailure; } SECStatus PRNGTEST_RunHealthTests() { return SECFailure; } SECStatus PRNGTEST_Instantiate_Kat(const PRUint8 *entropy, unsigned int entropy_len, const PRUint8 *nonce, unsigned int nonce_len, const PRUint8 *personal_string, unsigned int ps_len) { return SECFailure; }