Provide an array shuffle function using standard random function calls.
This is ideal for use in randomizing packet header order for increase network packet security.
Add appropriate unit tests.
extern "C" {
#endif
+#ifndef _di_f_random_array_shuffle_
+ f_status_t f_random_array_shuffle(const unsigned int flag, const f_number_unsigned_t total, const unsigned short size, f_string_dynamic_t * const cache, void * const indexes) {
+ #ifndef _di_level_0_parameter_checking_
+ if (!cache) return F_status_set_error(F_parameter);
+ if (!indexes) return F_status_set_error(F_parameter);
+ #endif // _di_level_0_parameter_checking_
+
+ if (!size) return F_status_set_error(F_too_small);
+ if (!total) return F_data_not;
+
+ {
+ cache->used = 0;
+
+ const f_status_t status = f_memory_array_increase_by(sizeof(f_number_unsigned_t) + 1, sizeof(f_char_t), (void **) &cache->string, &cache->used, &cache->size);
+ if (F_status_is_error(status)) return status;
+ }
+
+ f_number_unsigned_t index = 0;
+ int8_t old[size];
+
+ for (f_number_unsigned_t max = total - 1; max; --max) {
+
+ if (getrandom((void *) (cache->string), sizeof(f_number_unsigned_t), flag) == -1) {
+ if (errno == EAGAIN) return F_status_set_error(F_again);
+ if (errno == EFAULT) return F_status_set_error(F_buffer);
+ if (errno == EINTR) return F_status_set_error(F_interrupt);
+ if (errno == EINVAL) return F_status_set_error(F_parameter);
+ if (errno == ENOSYS) return F_status_set_error(F_support_not);
+
+ return F_status_set_error(F_failure);
+ }
+
+ index = *((f_number_unsigned_t *) cache->string) % (max + 1);
+
+ if (index == max) continue;
+
+ memset(cache->string, 0, sizeof(f_number_unsigned_t));
+ cache->string[cache->used] = 0;
+
+ // Casting (void *) to (uint8_t *) should result in an increment of size 1 and avoids problems with (void *) having arithmetic issues.
+ memmove((void *) old, (void *) (((uint8_t *) indexes) + (size * max)), size);
+ memmove((void *) (((uint8_t *) indexes) + (size * max)), (void *) (((uint8_t *) indexes) + (size * index)), size);
+ memmove((void *) (((uint8_t *) indexes) + (size * index)), (void *) old, size);
+ } // for
+
+ return F_okay;
+ }
+#endif // _di_f_random_array_shuffle_
+
#ifndef _di_f_random_get_
f_status_t f_random_get(long * const destination) {
#ifndef _di_level_0_parameter_checking_
#endif
/**
+ * Re-organize an array in a random order by swapping the values.
+ *
+ * The standard behavior of this is to call getrandom().
+ *
+ * The behavior of this function might also be replaced with calls to other libraries that are highly security specialized.
+ * If this is done, the meaning behind the flags passed should not be changed.
+ *
+ * This calls the Linux-specific getrandom() by default.
+ * If this is not available or pure POSIX is desired, then the implementation must handle accessing the /dev/random and /dev/urandom themselves (or something equivalent).
+ *
+ * @param flag
+ * The flags to be passed to getrandom().
+ *
+ * Flag bits:
+ * - F_random_seed_flag_block_not_d: Does not block when getting the bits.
+ * - F_random_seed_flag_source_d: Random data is taken from the random source, such as /dev/random and not the urandom source.
+ * @param total
+ * The total number of index positions to randomize.
+ * @param size
+ * The type size of the array represented in indexes.
+ * @param cache
+ * A string used when allocating space when calling the getrandom() or similar.
+ * This changes the length of the cache array as needed.
+ *
+ * Must not be NULL.
+ * @param indexes
+ * The array in which the index positions that must be cast to a (void *).
+ * The index positions are determined by size parameter.
+ * The first memory address given represents index 0.
+ * The values at each index position are swapped.
+ *
+ * This must only be the address to a standard array structure, such as those represented by the syntax "[]".
+ *
+ * Must not be NULL.
+ *
+ * @return
+ * F_okay on success.
+ * F_data_not on success, but total is 0.
+ *
+ * F_again (with error bit) when in non-blocking mode but reading entropy source would block (such as when the entropy source is busy).
+ * F_buffer (with error bit) if the address represented by the buffer is outside accessible address space.
+ * F_interrupt (with error bit) if stopping due to an interrupt.
+ * F_parameter (with error bit) if a parameter is invalid.
+ * F_support_not (with error bit) if the running kernel does not support the getrandom() call.
+ * F_too_small (with error bit) if size is too small.
+ *
+ * Errors (with error bit) from: f_memory_array_increase_by().
+ *
+ * @see getrandom()
+ * @see memmove()
+ *
+ * @see f_memory_array_increase_by()
+ */
+#ifndef _di_f_random_array_shuffle_
+ extern f_status_t f_random_array_shuffle(const unsigned int flag, const f_number_unsigned_t total, const unsigned short size, f_string_dynamic_t * const cache, void * const indexes);
+#endif // _di_f_random_array_shuffle_
+
+/**
* Set the destination string by reading from the random or urandom entropy source directly.
*
* The standard behavior of this is to call random().
* F_okay on success.
*
* F_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see random()
*/
#ifndef _di_f_random_get_
extern f_status_t f_random_get(long * const destination);
* F_interrupt (with error bit) if stopping due to an interrupt.
* F_parameter (with error bit) if a parameter is invalid.
* F_support_not (with error bit) if the running kernel does not support the getrandom() call.
+ *
+ * @see getrandom()
*/
#ifndef _di_f_random_read_
extern f_status_t f_random_read(const unsigned int flag, const f_number_unsigned_t length, f_string_t * const destination, ssize_t * const total);
* F_okay on success.
*
* F_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see srandom()
*/
#ifndef _di_f_random_seed_set_
extern f_status_t f_random_seed_set(unsigned int seed);
f_type
f_status
+f_memory
+f_type_array
f_string
build_language c
build_libraries -lc
-build_libraries-individual -lf_memory -lf_string
+build_libraries-individual -lf_memory -lf_string -lf_type_array
build_sources_library random.c
build_libraries -lc -lcmocka
build_libraries-individual -lf_memory -lf_random
-build_sources_program test-random-get.c test-random-read.c test-random-seed.c test-random-seed_set.c
+build_sources_program test-random-array_shuffle.c test-random-get.c test-random-read.c test-random-seed.c test-random-seed_set.c
build_sources_program test-random.c
build_script no
extern "C" {
#endif
+int mock_unwrap = 0;
+
ssize_t __wrap_getrandom(void *buf, size_t buflen, unsigned int flags) {
+ if (mock_unwrap) {
+ return __real_getrandom(buf, buflen, flags);
+ }
+
const bool failure = mock_type(bool);
if (failure) {
}
long __wrap_random(void) {
+
+ if (mock_unwrap) {
+ return __real_random();
+ }
+
return mock_type(long);
}
void __wrap_srandom(unsigned seed) {
+
+ if (mock_unwrap) {
+ return __real_srandom(seed);
+ }
+
// Do nothing.
}
const static int mock_errno_generic = 32767;
+extern int mock_unwrap;
+
+extern ssize_t __real_getrandom(void *buf, size_t buflen, unsigned int flags);
+extern long __real_random(void);
+extern void __real_srandom(unsigned seed);
+
extern ssize_t __wrap_getrandom(void *buf, size_t buflen, unsigned int flags);
extern long __wrap_random(void);
extern void __wrap_srandom(unsigned seed);
--- /dev/null
+#include "test-random.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test__f_random_array_shuffle__fails(void **state) {
+
+ mock_unwrap = 0;
+
+ int errnos[] = {
+ EAGAIN,
+ EFAULT,
+ EINTR,
+ EINVAL,
+ ENOSYS,
+ mock_errno_generic,
+ };
+
+ f_status_t statuss[] = {
+ F_again,
+ F_buffer,
+ F_interrupt,
+ F_parameter,
+ F_support_not,
+ F_failure,
+ };
+
+ f_string_dynamic_t cache = f_string_dynamic_t_initialize;
+ f_number_unsigneds_t indexes = f_number_unsigneds_t_initialize;
+
+ f_number_unsigned_t indexes_array[5] = { 0, 1, 2, 3, 4 };
+ indexes.array = indexes_array;
+ indexes.used = 5;
+
+ for (uint8_t i = 0; i < 6; ++i) {
+
+ will_return(__wrap_getrandom, true);
+ will_return(__wrap_getrandom, errnos[i]);
+ will_return(__wrap_getrandom, -1);
+
+ const f_status_t status = f_random_array_shuffle(0, indexes.used, sizeof(f_number_unsigned_t), &cache, (void *) indexes.array);
+
+ if (status != F_status_set_error(statuss[i])) {
+ printf("[ ERROR ] --- At index %d, first loop.\n", i);
+ }
+
+ assert_int_equal(status, F_status_set_error(statuss[i]));
+ } // for
+
+ if (cache.string) free(cache.string);
+}
+
+void test__f_random_array_shuffle__parameter_checking(void **state) {
+
+ mock_unwrap = 0;
+
+ {
+ const f_status_t status = f_random_array_shuffle(0, 0, 0, 0, 0);
+
+ assert_int_equal(status, F_status_set_error(F_parameter));
+ }
+
+ {
+ f_string_dynamic_t cache = f_string_dynamic_t_initialize;
+
+ const f_status_t status = f_random_array_shuffle(0, 0, 0, &cache, 0);
+
+ assert_int_equal(status, F_status_set_error(F_parameter));
+ }
+
+ {
+ f_number_unsigneds_t indexes = f_number_unsigneds_t_initialize;
+
+ const f_status_t status = f_random_array_shuffle(0, 0, 0, 0, (void *) indexes.array);
+
+ assert_int_equal(status, F_status_set_error(F_parameter));
+ }
+}
+
+void test__f_random_array_shuffle__returns_data_not(void **state) {
+
+ mock_unwrap = 0;
+
+ f_string_dynamic_t cache = f_string_dynamic_t_initialize;
+ f_number_unsigneds_t indexes = f_number_unsigneds_t_initialize;
+
+ f_number_unsigned_t indexes_array[5] = { 0, 1, 2, 3, 4 };
+ indexes.array = indexes_array;
+ indexes.used = 5;
+
+ {
+ const f_status_t status = f_random_array_shuffle(0, 0, sizeof(f_number_unsigned_t), &cache, (void *) indexes.array);
+ assert_int_equal(status, F_data_not);
+ }
+
+ if (cache.string) free(cache.string);
+}
+
+void test__f_random_array_shuffle__returns_too_small(void **state) {
+
+ mock_unwrap = 0;
+
+ f_string_dynamic_t cache = f_string_dynamic_t_initialize;
+ f_number_unsigneds_t indexes = f_number_unsigneds_t_initialize;
+
+ f_number_unsigned_t indexes_array[5] = { 0, 1, 2, 3, 4 };
+ indexes.array = indexes_array;
+ indexes.used = 5;
+
+ {
+ const f_status_t status = f_random_array_shuffle(0, indexes.used, 0, &cache, (void *) indexes.array);
+ assert_int_equal(status, F_status_set_error(F_too_small));
+ }
+
+ if (cache.string) free(cache.string);
+}
+
+void test__f_random_array_shuffle__works(void **state) {
+
+ mock_unwrap = 1;
+
+ f_string_dynamic_t cache = f_string_dynamic_t_initialize;
+ f_number_unsigneds_t indexes = f_number_unsigneds_t_initialize;
+
+ f_number_unsigned_t indexes_array[5] = { 0, 1, 2, 3, 4 };
+ indexes.array = indexes_array;
+ indexes.used = 5;
+
+ uint8_t unchanged = F_true;
+ const uint8_t tries = 20;
+
+ for (uint8_t i = 0; i < tries; ++i) {
+
+ const f_status_t status = f_random_array_shuffle(0, indexes.used, sizeof(f_number_unsigned_t), &cache, (void *) indexes.array);
+ assert_int_equal(status, F_okay);
+
+ uint8_t founds[] = { F_false, F_false, F_false, F_false, F_false };
+
+ for (uint8_t j = 0; j < indexes.used; ++j) {
+ founds[indexes.array[j]] = F_true;
+
+ if (indexes.array[j] > indexes.used) {
+ printf("[ ERROR ] --- value (%lu) at index %d is too large.\n", indexes.array[j], j);
+ }
+
+ assert_true(indexes.array[j] <= indexes.used);
+ } // for
+
+ for (uint8_t j = 0; j < indexes.used; ++j) {
+
+ if (!founds[j]) {
+ printf("[ ERROR ] --- Did not find index %d, value=%lu.\n", j, indexes.array[j]);
+ }
+
+ assert_int_equal(founds[j], F_true);
+ } // for
+
+ // Because things are random, the values could randomly be the same { 0, 1, 2, 3, 4 }.
+ // Try multiple times to get a different value and if a different order is found, then break.
+ for (uint8_t j = 0; j < indexes.used; ++j) {
+
+ if (indexes.array[j] != j) {
+ unchanged = F_false;
+
+ break;
+ }
+ } // for
+ } // for
+
+ if (unchanged) {
+ printf("[ ERROR ] --- Failed to shuffle to a different result at least once in %d tries.\n", tries);
+ }
+
+ assert_false(unchanged);
+
+ if (cache.string) free(cache.string);
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 0
+ *
+ * Project: Random
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the array shuffle function in the random project.
+ */
+#ifndef _TEST__F_random_array_shuffle_h
+#define _TEST__F_random_array_shuffle_h
+
+/**
+ * Test that the function fails.
+ *
+ * @see f_random_array_shuffle()
+ */
+extern void test__f_random_array_shuffle__fails(void **state);
+
+/**
+ * Test that the function correctly fails on invalid parameter.
+ *
+ * @see f_random_array_shuffle()
+ */
+extern void test__f_random_array_shuffle__parameter_checking(void **state);
+
+/**
+ * Test that the function returns F_data_not.
+ *
+ * @see f_random_array_shuffle()
+ */
+extern void test__f_random_array_shuffle__returns_data_not(void **state);
+
+/**
+ * Test that the function returns F_too_small (with error bit).
+ *
+ * @see f_random_array_shuffle()
+ */
+extern void test__f_random_array_shuffle__returns_too_small(void **state);
+
+/**
+ * Test that the function works.
+ *
+ * @see f_random_array_shuffle()
+ */
+extern void test__f_random_array_shuffle__works(void **state);
+
+#endif // _TEST__F_random_array_shuffle_h
#include "test-random.h"
-#include "test-random-get.h"
#ifdef __cplusplus
extern "C" {
void test__f_random_get__works(void **state) {
+ mock_unwrap = 0;
+
const long mocked_data = 1234;
{
void test__f_random_get__parameter_checking(void **state) {
+ mock_unwrap = 0;
+
{
const f_status_t status = f_random_get(0);
#include "test-random.h"
-#include "test-random-read.h"
#ifdef __cplusplus
extern "C" {
void test__f_random_read__fails(void **state) {
+ mock_unwrap = 0;
+
int errnos[] = {
EAGAIN,
EFAULT,
const f_status_t status = f_random_read(0, 4, &buffer_ptr, &total);
+ if (status != F_status_set_error(statuss[i])) {
+ printf("[ ERROR ] --- At index %d, first loop.\n", i);
+ }
+
assert_int_equal(status, F_status_set_error(statuss[i]));
} // for
const f_status_t status = f_random_read(0, 4, &buffer_ptr, 0);
+ if (status != F_status_set_error(statuss[i])) {
+ printf("[ ERROR ] --- At index %d, second loop.\n", i);
+ }
+
assert_int_equal(status, F_status_set_error(statuss[i]));
} // for
}
void test__f_random_read__works(void **state) {
+ mock_unwrap = 0;
+
const f_number_unsigned_t length = 4;
{
void test__f_random_read__parameter_checking(void **state) {
+ mock_unwrap = 0;
+
{
const f_status_t status = f_random_read(0, 0, 0, 0);
#include "test-random.h"
-#include "test-random-seed.h"
#ifdef __cplusplus
extern "C" {
void test__f_random_seed__fails(void **state) {
+ mock_unwrap = 0;
+
int errnos[] = {
EAGAIN,
EFAULT,
const f_status_t status = f_random_seed(0);
+ if (status != F_status_set_error(statuss[i])) {
+ printf("[ ERROR ] --- At index %d.\n", i);
+ }
+
assert_int_equal(status, F_status_set_error(statuss[i]));
} // for
}
void test__f_random_seed__works(void **state) {
+ mock_unwrap = 0;
+
const f_number_unsigned_t length = 4;
{
#include "test-random.h"
-#include "test-random-seed_set.h"
#ifdef __cplusplus
extern "C" {
void test__f_random_seed_set__works(void **state) {
+ mock_unwrap = 0;
+
{
const f_status_t status = f_random_seed_set(1);
int main(void) {
const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test__f_random_array_shuffle__fails),
cmocka_unit_test(test__f_random_read__fails),
cmocka_unit_test(test__f_random_seed__fails),
+ cmocka_unit_test(test__f_random_array_shuffle__returns_data_not),
+ cmocka_unit_test(test__f_random_array_shuffle__returns_too_small),
+ cmocka_unit_test(test__f_random_array_shuffle__works),
cmocka_unit_test(test__f_random_get__works),
cmocka_unit_test(test__f_random_read__works),
cmocka_unit_test(test__f_random_seed__works),
cmocka_unit_test(test__f_random_seed_set__works),
#ifndef _di_level_0_parameter_checking_
+ cmocka_unit_test(test__f_random_array_shuffle__parameter_checking),
cmocka_unit_test(test__f_random_get__parameter_checking),
cmocka_unit_test(test__f_random_read__parameter_checking),
#include "mock-random.h"
// Test includes.
+#include "test-random-array_shuffle.h"
#include "test-random-get.h"
#include "test-random-read.h"
#include "test-random-seed.h"