Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor rand functions with enhanced error handling and return code #1964

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 23 additions & 21 deletions src/common/rand/rand.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,67 +49,70 @@ OQS_API void OQS_randombytes_custom_algorithm(void (*algorithm_ptr)(uint8_t *, s
oqs_randombytes_algorithm = algorithm_ptr;
}

OQS_API void OQS_randombytes(uint8_t *random_array, size_t bytes_to_read) {
oqs_randombytes_algorithm(random_array, bytes_to_read);
OQS_API OQS_STATUS OQS_randombytes(uint8_t *random_array, size_t bytes_to_read) {
return oqs_randombytes_algorithm(random_array, bytes_to_read);
}

// Select the implementation for OQS_randombytes_system
#if defined(_WIN32)
void OQS_randombytes_system(uint8_t *random_array, size_t bytes_to_read) {
OQS_STATUS OQS_randombytes_system(uint8_t *random_array, size_t bytes_to_read) {
HCRYPTPROV hCryptProv;
if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) ||
!CryptGenRandom(hCryptProv, (DWORD) bytes_to_read, random_array)) {
exit(EXIT_FAILURE); // better to fail than to return bad random data
return OQS_ERROR;
}
CryptReleaseContext(hCryptProv, 0);
return OQS_SUCCESS;
}
#elif defined(__APPLE__)
void OQS_randombytes_system(uint8_t *random_array, size_t bytes_to_read) {
OQS_STATUS OQS_randombytes_system(uint8_t *random_array, size_t bytes_to_read) {
arc4random_buf(random_array, bytes_to_read);
return OQS_SUCCESS;
}
#elif defined(OQS_EMBEDDED_BUILD)
void OQS_randombytes_system(uint8_t *random_array, size_t bytes_to_read) {
OQS_STATUS OQS_randombytes_system(uint8_t *random_array, size_t bytes_to_read) {
fprintf(stderr, "OQS_randombytes_system is not available in an embedded build.\n");
fprintf(stderr, "Call OQS_randombytes_custom_algorithm() to set a custom method for your system.\n");
exit(EXIT_FAILURE);
return OQS_ERROR;
}
#elif defined(OQS_HAVE_GETENTROPY)
void OQS_randombytes_system(uint8_t *random_array, size_t bytes_to_read) {
OQS_STATUS OQS_randombytes_system(uint8_t *random_array, size_t bytes_to_read) {
while (bytes_to_read > 256) {
if (getentropy(random_array, 256)) {
exit(EXIT_FAILURE);
return OQS_ERROR;
}
random_array += 256;
bytes_to_read -= 256;
}
if (getentropy(random_array, bytes_to_read)) {
exit(EXIT_FAILURE);
return OQS_ERROR;
}
return OQS_SUCCESS;
}
#else
void OQS_randombytes_system(uint8_t *random_array, size_t bytes_to_read) {
OQS_STATUS OQS_randombytes_system(uint8_t *random_array, size_t bytes_to_read) {
FILE *handle;
size_t bytes_read;

handle = fopen("/dev/urandom", "rb");
if (!handle) {
perror("OQS_randombytes");
exit(EXIT_FAILURE);
return OQS_ERROR;
}

bytes_read = fread(random_array, 1, bytes_to_read, handle);
if (bytes_read < bytes_to_read || ferror(handle)) {
perror("OQS_randombytes");
exit(EXIT_FAILURE);
fclose(handle);

if (bytes_read < bytes_to_read) {
return OQS_ERROR;
}

fclose(handle);
return OQS_SUCCESS;
}
#endif

#ifdef OQS_USE_OPENSSL
#define OQS_RAND_POLL_RETRY 3 // in case failure to get randomness is a temporary problem, allow some repeats
void OQS_randombytes_openssl(uint8_t *random_array, size_t bytes_to_read) {
OQS_STATUS OQS_randombytes_openssl(uint8_t *random_array, size_t bytes_to_read) {
int rep = OQS_RAND_POLL_RETRY;
SIZE_T_TO_INT_OR_EXIT(bytes_to_read, bytes_to_read_int)
do {
Expand All @@ -120,9 +123,8 @@ void OQS_randombytes_openssl(uint8_t *random_array, size_t bytes_to_read) {
} while (rep-- >= 0);
if (OSSL_FUNC(RAND_bytes)(random_array, bytes_to_read_int) != 1) {
fprintf(stderr, "No OpenSSL randomness retrieved. DRBG available?\n");
// because of void signature we have no other way to signal the problem
// we cannot possibly return without randomness
exit(EXIT_FAILURE);
return OQS_ERROR;
}
return OQS_SUCCESS;
}
#endif
3 changes: 2 additions & 1 deletion src/common/rand/rand.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ OQS_API void OQS_randombytes_custom_algorithm(void (*algorithm_ptr)(uint8_t *, s
*
* @param[out] random_array Pointer to the memory to fill with (pseudo)random bytes
* @param[in] bytes_to_read The number of random bytes to read into memory
* @return OQS_SUCCESS on success, OQS_ERROR otherwise.
*/
OQS_API void OQS_randombytes(uint8_t *random_array, size_t bytes_to_read);
OQS_API OQS_STATUS OQS_randombytes(uint8_t *random_array, size_t bytes_to_read);

#if defined(__cplusplus)
} // extern "C"
Expand Down
51 changes: 36 additions & 15 deletions src/common/rand/rand_nist.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,32 @@ You are solely responsible for determining the appropriateness of using and dist
#include <oqs/aes.h>
#endif

void OQS_randombytes_nist_kat(unsigned char *x, size_t xlen);
OQS_STATUS OQS_randombytes_nist_kat(unsigned char *x, size_t xlen);

static OQS_NIST_DRBG_struct DRBG_ctx;
static void AES256_CTR_DRBG_Update(unsigned char *provided_data, unsigned char *Key, unsigned char *V);
static OQS_STATUS AES256_CTR_DRBG_Update(unsigned char *provided_data, unsigned char *Key, unsigned char *V);

// Use whatever AES implementation you have. This uses AES from openSSL library
// key - 256-bit AES key
// ctr - a 128-bit plaintext value
// buffer - a 128-bit ciphertext value
static void AES256_ECB(unsigned char *key, unsigned char *ctr, unsigned char *buffer) {
static OQS_STATUS AES256_ECB(unsigned char *key, unsigned char *ctr, unsigned char *buffer) {
#ifdef OQS_USE_OPENSSL
EVP_CIPHER_CTX *ctx;

int len;

/* Create and initialise the context */
ctx = OSSL_FUNC(EVP_CIPHER_CTX_new)();
OQS_EXIT_IF_NULLPTR(ctx, "OpenSSL");
if (ctx == NULL) {
return OQS_ERROR;
}

OQS_OPENSSL_GUARD(OSSL_FUNC(EVP_EncryptInit_ex)(ctx, oqs_aes_256_ecb(), NULL, key, NULL));
OQS_OPENSSL_GUARD(OSSL_FUNC(EVP_EncryptUpdate)(ctx, buffer, &len, ctr, 16));
if (OSSL_FUNC(EVP_EncryptInit_ex)(ctx, oqs_aes_256_ecb(), NULL, key, NULL) != 1 ||
OSSL_FUNC(EVP_EncryptUpdate)(ctx, buffer, &len, ctr, 16) != 1) {
OSSL_FUNC(EVP_CIPHER_CTX_free)(ctx);
return OQS_ERROR;
}

/* Clean up */
OSSL_FUNC(EVP_CIPHER_CTX_free)(ctx);
Expand All @@ -57,9 +62,10 @@ static void AES256_ECB(unsigned char *key, unsigned char *ctr, unsigned char *bu
OQS_AES256_ECB_enc(ctr, 16, key, buffer);
OQS_AES256_free_schedule(schedule);
#endif
return OQS_SUCCESS;
}

void OQS_randombytes_nist_kat_init_256bit(const uint8_t *entropy_input, const uint8_t *personalization_string) {
OQS_STATUS OQS_randombytes_nist_kat_init_256bit(const uint8_t *entropy_input, const uint8_t *personalization_string) {
unsigned char seed_material[48];

memcpy(seed_material, entropy_input, 48);
Expand All @@ -69,11 +75,14 @@ void OQS_randombytes_nist_kat_init_256bit(const uint8_t *entropy_input, const ui
}
memset(DRBG_ctx.Key, 0x00, 32);
memset(DRBG_ctx.V, 0x00, 16);
AES256_CTR_DRBG_Update(seed_material, DRBG_ctx.Key, DRBG_ctx.V);
if (AES256_CTR_DRBG_Update(seed_material, DRBG_ctx.Key, DRBG_ctx.V) != OQS_SUCCESS) {
return OQS_ERROR;
}
DRBG_ctx.reseed_counter = 1;
return OQS_SUCCESS;
}

void OQS_randombytes_nist_kat(unsigned char *x, size_t xlen) {
OQS_STATUS OQS_randombytes_nist_kat(unsigned char *x, size_t xlen) {
unsigned char block[16];
int i = 0;

Expand All @@ -87,7 +96,9 @@ void OQS_randombytes_nist_kat(unsigned char *x, size_t xlen) {
break;
}
}
AES256_ECB(DRBG_ctx.Key, DRBG_ctx.V, block);
if (AES256_ECB(DRBG_ctx.Key, DRBG_ctx.V, block) != OQS_SUCCESS) {
return OQS_ERROR;
}
if (xlen > 15) {
memcpy(x + i, block, 16);
i += 16;
Expand All @@ -97,29 +108,36 @@ void OQS_randombytes_nist_kat(unsigned char *x, size_t xlen) {
xlen = 0;
}
}
AES256_CTR_DRBG_Update(NULL, DRBG_ctx.Key, DRBG_ctx.V);
if (AES256_CTR_DRBG_Update(NULL, DRBG_ctx.Key, DRBG_ctx.V) != OQS_SUCCESS) {
return OQS_ERROR;
}
DRBG_ctx.reseed_counter++;
return OQS_SUCCESS;
}

void OQS_randombytes_nist_kat_get_state(void *out) {
OQS_STATUS OQS_randombytes_nist_kat_get_state(void *out) {
OQS_NIST_DRBG_struct *out_state = (OQS_NIST_DRBG_struct *)out;
if (out_state != NULL) {
memcpy(out_state->Key, DRBG_ctx.Key, sizeof(DRBG_ctx.Key));
memcpy(out_state->V, DRBG_ctx.V, sizeof(DRBG_ctx.V));
out_state->reseed_counter = DRBG_ctx.reseed_counter;
return OQS_SUCCESS;
}
return OQS_ERROR;
}

void OQS_randombytes_nist_kat_set_state(const void *in) {
OQS_STATUS OQS_randombytes_nist_kat_set_state(const void *in) {
const OQS_NIST_DRBG_struct *in_state = (const OQS_NIST_DRBG_struct *)in;
if (in_state != NULL) {
memcpy(DRBG_ctx.Key, in_state->Key, sizeof(DRBG_ctx.Key));
memcpy(DRBG_ctx.V, in_state->V, sizeof(DRBG_ctx.V));
DRBG_ctx.reseed_counter = in_state->reseed_counter;
return OQS_SUCCESS;
}
return OQS_ERROR;
}

static void AES256_CTR_DRBG_Update(unsigned char *provided_data, unsigned char *Key, unsigned char *V) {
static OQS_STATUS AES256_CTR_DRBG_Update(unsigned char *provided_data, unsigned char *Key, unsigned char *V) {
unsigned char temp[48];

for (int i = 0; i < 3; i++) {
Expand All @@ -133,12 +151,15 @@ static void AES256_CTR_DRBG_Update(unsigned char *provided_data, unsigned char *
}
}

AES256_ECB(Key, V, temp + 16 * i);
if (AES256_ECB(Key, V, temp + 16 * i) != OQS_SUCCESS) {
return OQS_ERROR;
}
}
if (provided_data != NULL)
for (int i = 0; i < 48; i++) {
temp[i] ^= provided_data[i];
}
memcpy(Key, temp, 32);
memcpy(V, temp + 32, 16);
return OQS_SUCCESS;
}
17 changes: 13 additions & 4 deletions src/common/rand/rand_nist.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include <stddef.h>
#include <stdint.h>
#include <oqs/common.h>

typedef struct {
unsigned char Key[32];
Expand All @@ -23,25 +24,33 @@ typedef struct {
* @param[in] entropy_input The seed; must be exactly 48 bytes
* @param[in] personalization_string An optional personalization string;
* may be NULL; if not NULL, must be at least 48 bytes long
* @return OQS_SUCCESS on success, OQS_ERROR otherwise
*/
void OQS_randombytes_nist_kat_init_256bit(const uint8_t *entropy_input, const uint8_t *personalization_string);
OQS_STATUS OQS_randombytes_nist_kat_init_256bit(const uint8_t *entropy_input, const uint8_t *personalization_string);

/**
* Fills the given memory with the requested number of pseudorandom bytes using the NIST DRBG.
*
* @param[out] random_array Pointer to the memory to fill with (pseudo)random bytes
* @param[in] bytes_to_read The number of random bytes to read into memory
* @return OQS_SUCCESS on success, OQS_ERROR otherwise
*/
void OQS_randombytes_nist_kat(uint8_t *random_array, size_t bytes_to_read);
OQS_STATUS OQS_randombytes_nist_kat(uint8_t *random_array, size_t bytes_to_read);

/**
* Writes the current state of the NIST DRBG into the provided memory.
*
* @param[out] out Pointer to the memory to write the state
* @return OQS_SUCCESS on success, OQS_ERROR otherwise
*/
void OQS_randombytes_nist_kat_get_state(void *out);
OQS_STATUS OQS_randombytes_nist_kat_get_state(void *out);

/**
* Overwrites the current state of the NIST DRBG from the provided memory.
*
* @param[in] in Pointer to the memory containing the state to set
* @return OQS_SUCCESS on success, OQS_ERROR otherwise
*/
void OQS_randombytes_nist_kat_set_state(const void *in);
OQS_STATUS OQS_randombytes_nist_kat_set_state(const void *in);

#endif // OQS_RAND_NIST_H
Loading