| /* 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(dest, (uint8_t *)dest, len, (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() |
| { |
| return SECFailure; |
| } |