Coverage Report

Created: 2024-05-20 06:23

/src/nss/fuzz/mpi_helper.cc
Line
Count
Source (jump to first uncovered line)
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
/* Helper functions for MPI fuzzing targets. */
6
7
#include "mpi_helper.h"
8
#include <cstdlib>
9
#include <random>
10
11
1.84k
char *to_char(const uint8_t *x) {
12
1.84k
  return reinterpret_cast<char *>(const_cast<unsigned char *>(x));
13
1.84k
}
14
15
0
void print_bn(std::string label, BIGNUM *x) {
16
0
  char *xc = BN_bn2hex(x);
17
0
  std::cout << label << ": " << std::hex << xc << std::endl;
18
0
  OPENSSL_free(xc);
19
0
}
20
21
// Check that the two numbers are equal.
22
2.76k
void check_equal(BIGNUM *b, mp_int *m, size_t max_size) {
23
2.76k
  char *bnBc = BN_bn2hex(b);
24
2.76k
  char mpiMc[max_size];
25
2.76k
  mp_tohex(m, mpiMc);
26
2.76k
  std::string bnA(bnBc);
27
2.76k
  std::string mpiA(mpiMc);
28
2.76k
  OPENSSL_free(bnBc);
29
  // We have to strip leading zeros from bignums, ignoring the sign.
30
2.76k
  if (bnA.at(0) != '-') {
31
2.76k
    bnA.erase(0, std::min(bnA.find_first_not_of('0'), bnA.size() - 1));
32
2.76k
  } else if (bnA.at(1) == '0') {
33
0
    bnA.erase(1, std::min(bnA.find_first_not_of('0', 1) - 1, bnA.size() - 1));
34
0
  }
35
36
2.76k
  if (mpiA != bnA) {
37
0
    std::cout << "openssl: " << std::hex << bnA << std::endl;
38
0
    std::cout << "nss:     " << std::hex << mpiA << std::endl;
39
0
  }
40
41
2.76k
  assert(mpiA == bnA);
42
2.76k
}
43
44
// Parse data into two numbers for MPI and OpenSSL Bignum.
45
void parse_input(const uint8_t *data, size_t size, BIGNUM *A, BIGNUM *B,
46
923
                 mp_int *a, mp_int *b) {
47
  // Note that b might overlap a.
48
923
  size_t len = (size_t)size / 2;
49
923
  assert(mp_read_raw(a, to_char(data), len) == MP_OKAY);
50
923
  assert(mp_read_raw(b, to_char(data) + len, len) == MP_OKAY);
51
  // Force a positive sign.
52
  // TODO: add tests for negatives.
53
923
  MP_SIGN(a) = MP_ZPOS;
54
923
  MP_SIGN(b) = MP_ZPOS;
55
56
  // Skip the first byte as it's interpreted as sign by NSS.
57
923
  assert(BN_bin2bn(data + 1, len - 1, A) != nullptr);
58
923
  assert(BN_bin2bn(data + len + 1, len - 1, B) != nullptr);
59
60
923
  check_equal(A, a, 2 * size + 1);
61
923
  check_equal(B, b, 2 * size + 1);
62
923
}
63
64
// Parse data into a number for MPI and OpenSSL Bignum.
65
0
void parse_input(const uint8_t *data, size_t size, BIGNUM *A, mp_int *a) {
66
0
  assert(mp_read_raw(a, to_char(data), size) == MP_OKAY);
67
68
  // Force a positive sign.
69
  // TODO: add tests for negatives.
70
0
  MP_SIGN(a) = MP_ZPOS;
71
72
  // Skip the first byte as it's interpreted as sign by NSS.
73
0
  assert(BN_bin2bn(data + 1, size - 1, A) != nullptr);
74
75
0
  check_equal(A, a, 4 * size + 1);
76
0
}
77
78
// Take a chunk in the middle of data and use it as modulus.
79
std::tuple<BIGNUM *, mp_int> get_modulus(const uint8_t *data, size_t size,
80
0
                                         BN_CTX *ctx) {
81
0
  BIGNUM *r1 = BN_CTX_get(ctx);
82
0
  mp_int r2;
83
0
  assert(mp_init(&r2) == MP_OKAY);
84
85
0
  size_t len = static_cast<size_t>(size / 4);
86
0
  if (len != 0) {
87
0
    assert(mp_read_raw(&r2, to_char(data + len), len) == MP_OKAY);
88
0
    MP_SIGN(&r2) = MP_ZPOS;
89
90
0
    assert(BN_bin2bn(data + len + 1, len - 1, r1) != nullptr);
91
0
    check_equal(r1, &r2, 2 * len + 1);
92
0
  }
93
94
  // If we happen to get 0 for the modulus, take a random number.
95
0
  if (mp_cmp_z(&r2) == 0 || len == 0) {
96
0
    mp_zero(&r2);
97
0
    BN_zero(r1);
98
0
    std::mt19937 rng(data[0]);
99
0
    std::uniform_int_distribution<mp_digit> dist(1, MP_DIGIT_MAX);
100
0
    mp_digit x = dist(rng);
101
0
    mp_add_d(&r2, x, &r2);
102
0
    BN_add_word(r1, x);
103
0
  }
104
105
0
  return std::make_tuple(r1, r2);
106
0
}