Coverage Report

Created: 2026-02-16 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/boringssl/crypto/fipsmodule/dh/check.cc.inc
Line
Count
Source
1
// Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#include <openssl/dh.h>
16
17
#include <openssl/bn.h>
18
#include <openssl/err.h>
19
20
#include "../bn/internal.h"
21
#include "internal.h"
22
23
24
using namespace bssl;
25
26
static_assert(OPENSSL_DH_MAX_MODULUS_BITS <= BN_MONTGOMERY_MAX_WORDS * BN_BITS2,
27
              "Max DH size too big for Montgomery arithmetic");
28
29
0
int bssl::dh_check_params_fast(const DH *dh) {
30
0
  auto *impl = FromOpaque(dh);
31
32
  // Most operations scale with p and q.
33
0
  if (BN_is_negative(impl->p) || !BN_is_odd(impl->p) ||
34
0
      BN_num_bits(impl->p) > OPENSSL_DH_MAX_MODULUS_BITS) {
35
0
    OPENSSL_PUT_ERROR(DH, DH_R_INVALID_PARAMETERS);
36
0
    return 0;
37
0
  }
38
39
  // q must be bounded by p.
40
0
  if (impl->q != nullptr &&
41
0
      (BN_is_negative(impl->q) || BN_ucmp(impl->q, impl->p) > 0)) {
42
0
    OPENSSL_PUT_ERROR(DH, DH_R_INVALID_PARAMETERS);
43
0
    return 0;
44
0
  }
45
46
  // g must be an element of p's multiplicative group.
47
0
  if (BN_is_negative(impl->g) || BN_is_zero(impl->g) ||
48
0
      BN_ucmp(impl->g, impl->p) >= 0) {
49
0
    OPENSSL_PUT_ERROR(DH, DH_R_INVALID_PARAMETERS);
50
0
    return 0;
51
0
  }
52
53
0
  return 1;
54
0
}
55
56
0
int DH_check_pub_key(const DH *dh, const BIGNUM *pub_key, int *out_flags) {
57
0
  auto *impl = FromOpaque(dh);
58
59
0
  *out_flags = 0;
60
0
  if (!dh_check_params_fast(dh)) {
61
0
    return 0;
62
0
  }
63
64
0
  UniquePtr<BN_CTX> ctx(BN_CTX_new());
65
0
  if (ctx == nullptr) {
66
0
    return 0;
67
0
  }
68
0
  BN_CTXScope scope(ctx.get());
69
70
  // Check |pub_key| is greater than 1.
71
0
  if (BN_cmp(pub_key, BN_value_one()) <= 0) {
72
0
    *out_flags |= DH_CHECK_PUBKEY_TOO_SMALL;
73
0
  }
74
75
  // Check |pub_key| is less than |impl->p| - 1.
76
0
  BIGNUM *tmp = BN_CTX_get(ctx.get());
77
0
  if (tmp == nullptr || !BN_copy(tmp, impl->p) || !BN_sub_word(tmp, 1)) {
78
0
    return 0;
79
0
  }
80
0
  if (BN_cmp(pub_key, tmp) >= 0) {
81
0
    *out_flags |= DH_CHECK_PUBKEY_TOO_LARGE;
82
0
  }
83
84
0
  if (impl->q != nullptr) {
85
    // Check |pub_key|^|impl->q| is 1 mod |impl->p|. This is necessary for RFC
86
    // 5114 groups which are not safe primes but pick a generator on a
87
    // prime-order subgroup of size |impl->q|.
88
0
    if (!BN_mod_exp_mont(tmp, pub_key, impl->q, impl->p, ctx.get(), nullptr)) {
89
0
      return 0;
90
0
    }
91
0
    if (!BN_is_one(tmp)) {
92
0
      *out_flags |= DH_CHECK_PUBKEY_INVALID;
93
0
    }
94
0
  }
95
96
0
  return 1;
97
0
}
98
99
0
int DH_check(const DH *dh, int *out_flags) {
100
0
  auto *impl = FromOpaque(dh);
101
102
0
  *out_flags = 0;
103
0
  if (!dh_check_params_fast(dh)) {
104
0
    return 0;
105
0
  }
106
107
  // Check that p is a safe prime and if g is 2, 3 or 5, check that it is a
108
  // suitable generator where:
109
  //   for 2, p mod 24 == 11
110
  //   for 3, p mod 12 == 5
111
  //   for 5, p mod 10 == 3 or 7
112
  // should hold.
113
0
  UniquePtr<BN_CTX> ctx(BN_CTX_new());
114
0
  if (ctx == nullptr) {
115
0
    return 0;
116
0
  }
117
0
  BN_CTXScope scope(ctx.get());
118
0
  BIGNUM *t1 = BN_CTX_get(ctx.get());
119
0
  if (t1 == nullptr) {
120
0
    return 0;
121
0
  }
122
0
  BIGNUM *t2 = BN_CTX_get(ctx.get());
123
0
  if (t2 == nullptr) {
124
0
    return 0;
125
0
  }
126
127
0
  if (impl->q) {
128
0
    if (BN_cmp(impl->g, BN_value_one()) <= 0) {
129
0
      *out_flags |= DH_CHECK_NOT_SUITABLE_GENERATOR;
130
0
    } else if (BN_cmp(impl->g, impl->p) >= 0) {
131
0
      *out_flags |= DH_CHECK_NOT_SUITABLE_GENERATOR;
132
0
    } else {
133
      // Check g^q == 1 mod p
134
0
      if (!BN_mod_exp_mont(t1, impl->g, impl->q, impl->p, ctx.get(), nullptr)) {
135
0
        return 0;
136
0
      }
137
0
      if (!BN_is_one(t1)) {
138
0
        *out_flags |= DH_CHECK_NOT_SUITABLE_GENERATOR;
139
0
      }
140
0
    }
141
0
    int r = BN_is_prime_ex(impl->q, BN_prime_checks_for_validation, ctx.get(),
142
0
                           nullptr);
143
0
    if (r < 0) {
144
0
      return 0;
145
0
    }
146
0
    if (!r) {
147
0
      *out_flags |= DH_CHECK_Q_NOT_PRIME;
148
0
    }
149
    // Check p == 1 mod q  i.e. q divides p - 1
150
0
    if (!BN_div(t1, t2, impl->p, impl->q, ctx.get())) {
151
0
      return 0;
152
0
    }
153
0
    if (!BN_is_one(t2)) {
154
0
      *out_flags |= DH_CHECK_INVALID_Q_VALUE;
155
0
    }
156
0
  } else if (BN_is_word(impl->g, DH_GENERATOR_2)) {
157
0
    BN_ULONG l = BN_mod_word(impl->p, 24);
158
0
    if (l == (BN_ULONG)-1) {
159
0
      return 0;
160
0
    }
161
0
    if (l != 11) {
162
0
      *out_flags |= DH_CHECK_NOT_SUITABLE_GENERATOR;
163
0
    }
164
0
  } else if (BN_is_word(impl->g, DH_GENERATOR_5)) {
165
0
    BN_ULONG l = BN_mod_word(impl->p, 10);
166
0
    if (l == (BN_ULONG)-1) {
167
0
      return 0;
168
0
    }
169
0
    if (l != 3 && l != 7) {
170
0
      *out_flags |= DH_CHECK_NOT_SUITABLE_GENERATOR;
171
0
    }
172
0
  } else {
173
0
    *out_flags |= DH_CHECK_UNABLE_TO_CHECK_GENERATOR;
174
0
  }
175
176
0
  int r = BN_is_prime_ex(impl->p, BN_prime_checks_for_validation, ctx.get(),
177
0
                         nullptr);
178
0
  if (r < 0) {
179
0
    return 0;
180
0
  }
181
0
  if (!r) {
182
0
    *out_flags |= DH_CHECK_P_NOT_PRIME;
183
0
  } else if (!impl->q) {
184
0
    if (!BN_rshift1(t1, impl->p)) {
185
0
      return 0;
186
0
    }
187
0
    r = BN_is_prime_ex(t1, BN_prime_checks_for_validation, ctx.get(), nullptr);
188
0
    if (r < 0) {
189
0
      return 0;
190
0
    }
191
0
    if (!r) {
192
0
      *out_flags |= DH_CHECK_P_NOT_SAFE_PRIME;
193
0
    }
194
0
  }
195
0
  return 1;
196
0
}