Coverage Report

Created: 2026-07-16 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/liboqs/tests/fuzz_test_kem.c
Line
Count
Source
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
1.74k
size_t min(size_t a, size_t b) {
19
1.74k
  return a < b ? a : b;
20
1.74k
}
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
19.5k
void fuzz_rand(uint8_t *random_array, size_t bytes_to_read) {
37
4.46M
  for (size_t i = 0; i < bytes_to_read; i++) {
38
4.44M
    random_array[i] = (uint8_t)rand();
39
4.44M
  }
40
19.5k
}
41
42
7.23k
fuzz_ctx_t init_fuzz_context(const uint8_t *data, size_t data_len) {
43
44
7.23k
  fuzz_ctx_t ctx = {{0, 0}, NULL, 0};
45
7.23k
  if (data_len > sizeof(fuzz_init_ctx_t)) {
46
7.07k
    memcpy(&ctx.init, data, sizeof(fuzz_init_ctx_t));
47
7.07k
    ctx.data = data + sizeof(fuzz_init_ctx_t);
48
7.07k
    ctx.data_len = data_len - sizeof(fuzz_init_ctx_t);
49
50
7.07k
    ctx.init.algorithm_index %= OQS_KEM_algs_length;
51
7.07k
  } else {
52
160
    ctx.data = data;
53
160
    ctx.data_len = data_len;
54
160
  }
55
56
7.23k
  srand(ctx.init.random_seed);
57
7.23k
  OQS_randombytes_custom_algorithm(&fuzz_rand);
58
59
7.23k
  return ctx;
60
7.23k
}
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
1.74k
static OQS_STATUS fuzz_kem(const uint8_t *data, size_t data_len) {
69
1.74k
  OQS_KEM *kem = NULL;
70
1.74k
  uint8_t *public_key = NULL;
71
1.74k
  uint8_t *secret_key = NULL;
72
1.74k
  uint8_t *ciphertext = NULL;
73
1.74k
  uint8_t *shared_secret_e = NULL;
74
1.74k
  uint8_t *shared_secret_d = NULL;
75
76
1.74k
  fuzz_ctx_t ctx = init_fuzz_context(data, data_len);
77
1.74k
  const char *algorithm = OQS_KEM_alg_identifier(ctx.init.algorithm_index);
78
1.74k
  kem = OQS_KEM_new(algorithm);
79
1.74k
  if (kem == NULL) {
80
0
    printf("%s was not enabled at compile-time.\n", algorithm);
81
0
    return OQS_ERROR;
82
0
  }
83
84
1.74k
  public_key = OQS_MEM_malloc(kem->length_public_key);
85
1.74k
  secret_key = OQS_MEM_malloc(kem->length_secret_key);
86
1.74k
  ciphertext = OQS_MEM_malloc(kem->length_ciphertext);
87
1.74k
  shared_secret_e = OQS_MEM_malloc(kem->length_shared_secret);
88
1.74k
  shared_secret_d = OQS_MEM_malloc(kem->length_shared_secret);
89
90
1.74k
  if ((public_key == NULL) || (secret_key == NULL) || (ciphertext == NULL) ||
91
1.74k
          (shared_secret_e == NULL) || (shared_secret_d == NULL)) {
92
0
    fprintf(stderr, "ERROR: OQS_MEM_malloc failed!\n");
93
0
    cleanup_heap(secret_key, shared_secret_e, shared_secret_d, public_key,
94
0
                 ciphertext, kem);
95
96
0
    return OQS_ERROR;
97
0
  }
98
99
1.74k
  memcpy(shared_secret_e, ctx.data, min(ctx.data_len, kem->length_shared_secret));
100
1.74k
  OQS_STATUS rc = OQS_KEM_keypair(kem, public_key, secret_key);
101
1.74k
  if (rc != OQS_SUCCESS) {
102
0
    fprintf(stderr, "ERROR: OQS_KEM_keypair failed!\n");
103
0
    cleanup_heap(secret_key, shared_secret_e, shared_secret_d, public_key,
104
0
                 ciphertext, kem);
105
106
0
    return rc;
107
0
  }
108
1.74k
  rc = OQS_KEM_encaps(kem, ciphertext, shared_secret_e, public_key);
109
1.74k
  if (rc != OQS_SUCCESS) {
110
0
    fprintf(stderr, "ERROR: OQS_KEM_encaps failed!\n");
111
0
    cleanup_heap(secret_key, shared_secret_e, shared_secret_d, public_key,
112
0
                 ciphertext, kem);
113
114
0
    return rc;
115
0
  }
116
1.74k
  rc = OQS_KEM_decaps(kem, shared_secret_d, ciphertext, secret_key);
117
1.74k
  if (rc != OQS_SUCCESS) {
118
0
    fprintf(stderr, "ERROR: OQS_KEM_decaps failed! with algorithm index %u\n", ctx.init.algorithm_index);
119
0
    cleanup_heap(secret_key, shared_secret_e, shared_secret_d, public_key,
120
0
                 ciphertext, kem);
121
122
0
    return rc;
123
0
  }
124
1.74k
  assert(memcmp(shared_secret_d, shared_secret_e, kem->length_shared_secret) == 0);
125
126
1.74k
  cleanup_heap(secret_key, shared_secret_e, shared_secret_d, public_key,
127
1.74k
               ciphertext, kem);
128
129
1.74k
  return OQS_SUCCESS; // success
130
1.74k
}
131
132
void cleanup_heap(uint8_t *secret_key, uint8_t *shared_secret_e,
133
                  uint8_t *shared_secret_d, uint8_t *public_key,
134
7.23k
                  uint8_t *ciphertext, OQS_KEM *kem) {
135
7.23k
  if (kem != NULL) {
136
7.23k
    OQS_MEM_secure_free(secret_key, kem->length_secret_key);
137
7.23k
    OQS_MEM_secure_free(shared_secret_e, kem->length_shared_secret);
138
7.23k
    OQS_MEM_secure_free(shared_secret_d, kem->length_shared_secret);
139
7.23k
  }
140
7.23k
  OQS_MEM_insecure_free(public_key);
141
7.23k
  OQS_MEM_insecure_free(ciphertext);
142
7.23k
  OQS_KEM_free(kem);
143
7.23k
}
144
145
7.23k
int LLVMFuzzerTestOneInput(const char *data, size_t size) {
146
7.23k
  OQS_init();
147
7.23k
  if (OQS_ERROR == fuzz_kem((const uint8_t *)data, size)) {
148
    // If we get an error prune testcase from corpus.
149
160
    return -1;
150
160
  }
151
7.07k
  OQS_destroy();
152
7.07k
  return 0;
153
7.23k
}