Skip to content

Commit ea81b93

Browse files
test: Add basic kem fuzz testing
Signed-off-by: Nathaniel Brough <[email protected]>
1 parent b75bfb8 commit ea81b93

File tree

2 files changed

+157
-0
lines changed

2 files changed

+157
-0
lines changed

tests/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,12 @@ if(OQS_BUILD_FUZZ_TESTS AND '${CMAKE_C_COMPILER_ID}' STREQUAL 'Clang')
118118
COMPILE_FLAGS "${FUZZING_COMPILE_FLAGS}"
119119
LINK_FLAGS "${FUZZING_LINK_FLAGS}"
120120
)
121+
add_executable(fuzz_test_kem fuzz_test_kem.c)
122+
target_link_libraries(fuzz_test_kem PRIVATE ${TEST_DEPS})
123+
set_target_properties(fuzz_test_kem PROPERTIES
124+
COMPILE_FLAGS "${FUZZING_COMPILE_FLAGS}"
125+
LINK_FLAGS "${FUZZING_LINK_FLAGS}"
126+
)
121127
endif()
122128

123129
# Stateful SIG API tests

tests/fuzz_test_kem.c

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* fuzz_test_kem.c
3+
*
4+
* Minimal fuzz test for liboqs.
5+
*
6+
* SPDX-License-Identifier: MIT
7+
*/
8+
9+
#include "oqs/kem.h"
10+
#include <stdbool.h>
11+
#include <stdio.h>
12+
#include <stdlib.h>
13+
#include <string.h>
14+
#include <assert.h>
15+
16+
#include <oqs/oqs.h>
17+
18+
size_t min(size_t a, size_t b) {
19+
return a < b ? a : b;
20+
}
21+
22+
typedef struct {
23+
uint32_t random_seed;
24+
uint32_t algorithm_index;
25+
} fuzz_init_ctx_t;
26+
27+
typedef struct {
28+
fuzz_init_ctx_t init;
29+
const uint8_t *data;
30+
size_t data_len;
31+
} fuzz_ctx_t;
32+
33+
fuzz_ctx_t init_fuzz_context(const uint8_t *data, size_t data_len);
34+
void fuzz_rand(uint8_t *random_array, size_t bytes_to_read);
35+
36+
void fuzz_rand(uint8_t *random_array, size_t bytes_to_read) {
37+
for (size_t i = 0; i < bytes_to_read; i++) {
38+
random_array[i] = (uint8_t)rand();
39+
}
40+
}
41+
42+
fuzz_ctx_t init_fuzz_context(const uint8_t *data, size_t data_len) {
43+
44+
fuzz_ctx_t ctx = {{0, 0}, NULL, 0};
45+
if (data_len > sizeof(fuzz_init_ctx_t)) {
46+
memcpy(&ctx.init, data, sizeof(fuzz_init_ctx_t));
47+
ctx.data = data + sizeof(fuzz_init_ctx_t);
48+
ctx.data_len = data_len - sizeof(fuzz_init_ctx_t);
49+
50+
ctx.init.algorithm_index %= OQS_KEM_algs_length;
51+
} else {
52+
ctx.data = data;
53+
ctx.data_len = data_len;
54+
}
55+
56+
srand(ctx.init.random_seed);
57+
OQS_randombytes_custom_algorithm(&fuzz_rand);
58+
59+
return ctx;
60+
}
61+
62+
void cleanup_heap(uint8_t *secret_key, uint8_t *shared_secret_e,
63+
uint8_t *shared_secret_d, uint8_t *public_key,
64+
uint8_t *ciphertext, OQS_KEM *kem);
65+
66+
67+
/** Fuzzing of the KEM */
68+
static OQS_STATUS fuzz_kem(const uint8_t* data, size_t data_len) {
69+
OQS_KEM *kem = NULL;
70+
uint8_t *public_key = NULL;
71+
uint8_t *secret_key = NULL;
72+
uint8_t *ciphertext = NULL;
73+
uint8_t *shared_secret_e = NULL;
74+
uint8_t *shared_secret_d = NULL;
75+
76+
fuzz_ctx_t ctx = init_fuzz_context(data, data_len);
77+
const char* algorithm = OQS_KEM_alg_identifier(ctx.init.algorithm_index);
78+
kem = OQS_KEM_new(algorithm);
79+
assert(kem != NULL);
80+
81+
public_key = OQS_MEM_malloc(kem->length_public_key);
82+
secret_key = OQS_MEM_malloc(kem->length_secret_key);
83+
ciphertext = OQS_MEM_malloc(kem->length_ciphertext);
84+
shared_secret_e = OQS_MEM_malloc(kem->length_shared_secret);
85+
shared_secret_d = OQS_MEM_malloc(kem->length_shared_secret);
86+
87+
if ((public_key == NULL) || (secret_key == NULL) || (ciphertext == NULL) ||
88+
(shared_secret_e == NULL) || (shared_secret_d == NULL)) {
89+
fprintf(stderr, "ERROR: OQS_MEM_malloc failed!\n");
90+
cleanup_heap(secret_key, shared_secret_e, shared_secret_d, public_key,
91+
ciphertext, kem);
92+
93+
return OQS_ERROR;
94+
}
95+
96+
memcpy(shared_secret_e, ctx.data, min(ctx.data_len, kem->length_shared_secret));
97+
OQS_STATUS rc = OQS_KEM_keypair(kem, public_key, secret_key);
98+
if (rc != OQS_SUCCESS) {
99+
fprintf(stderr, "ERROR: OQS_KEM_keypair failed!\n");
100+
cleanup_heap(secret_key, shared_secret_e, shared_secret_d, public_key,
101+
ciphertext, kem);
102+
103+
return rc;
104+
}
105+
rc = OQS_KEM_encaps(kem, ciphertext, shared_secret_e, public_key);
106+
if (rc != OQS_SUCCESS) {
107+
fprintf(stderr, "ERROR: OQS_KEM_encaps failed!\n");
108+
cleanup_heap(secret_key, shared_secret_e, shared_secret_d, public_key,
109+
ciphertext, kem);
110+
111+
return rc;
112+
}
113+
rc = OQS_KEM_decaps(kem, shared_secret_d, ciphertext, secret_key);
114+
assert(memcmp(shared_secret_d, shared_secret_e, kem->length_shared_secret));
115+
116+
if (rc != OQS_SUCCESS) {
117+
fprintf(stderr, "ERROR: OQS_KEM_decaps failed!\n");
118+
cleanup_heap(secret_key, shared_secret_e, shared_secret_d, public_key,
119+
ciphertext, kem);
120+
121+
return rc;
122+
}
123+
124+
cleanup_heap(secret_key, shared_secret_e, shared_secret_d, public_key,
125+
ciphertext, kem);
126+
127+
return OQS_SUCCESS; // success
128+
}
129+
130+
void cleanup_heap(uint8_t *secret_key, uint8_t *shared_secret_e,
131+
uint8_t *shared_secret_d, uint8_t *public_key,
132+
uint8_t *ciphertext, OQS_KEM *kem) {
133+
if (kem != NULL) {
134+
OQS_MEM_secure_free(secret_key, kem->length_secret_key);
135+
OQS_MEM_secure_free(shared_secret_e, kem->length_shared_secret);
136+
OQS_MEM_secure_free(shared_secret_d, kem->length_shared_secret);
137+
}
138+
OQS_MEM_insecure_free(public_key);
139+
OQS_MEM_insecure_free(ciphertext);
140+
OQS_KEM_free(kem);
141+
}
142+
143+
int LLVMFuzzerTestOneInput(const char *data, size_t size) {
144+
OQS_init();
145+
if (OQS_ERROR == fuzz_kem((const uint8_t *)data, size)) {
146+
// If we get an error prune testcase from corpus.
147+
return -1;
148+
}
149+
OQS_destroy();
150+
return 0;
151+
}

0 commit comments

Comments
 (0)