Coverage Report

Created: 2026-08-08 07:14

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.get()) || !BN_is_odd(impl->p.get()) ||
34
0
      BN_num_bits(impl->p.get()) > 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 && (BN_is_negative(impl->q.get()) ||
41
0
                             BN_ucmp(impl->q.get(), impl->p.get()) > 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.get()) || BN_is_zero(impl->g.get()) ||
48
0
      BN_ucmp(impl->g.get(), impl->p.get()) >= 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.get()) || !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.get(), impl->p.get(), ctx.get(),
89
0
                         nullptr)) {
90
0
      return 0;
91
0
    }
92
0
    if (!BN_is_one(tmp)) {
93
0
      *out_flags |= DH_CHECK_PUBKEY_INVALID;
94
0
    }
95
0
  }
96
97
0
  return 1;
98
0
}
99
100
0
int DH_check(const DH *dh, int *out_flags) {
101
0
  auto *impl = FromOpaque(dh);
102
103
0
  *out_flags = 0;
104
0
  if (!dh_check_params_fast(dh)) {
105
0
    return 0;
106
0
  }
107
108
  // Check that p is a safe prime and if g is 2, 3 or 5, check that it is a
109
  // suitable generator where:
110
  //   for 2, p mod 24 == 11
111
  //   for 3, p mod 12 == 5
112
  //   for 5, p mod 10 == 3 or 7
113
  // should hold.
114
0
  UniquePtr<BN_CTX> ctx(BN_CTX_new());
115
0
  if (ctx == nullptr) {
116
0
    return 0;
117
0
  }
118
0
  BN_CTXScope scope(ctx.get());
119
0
  BIGNUM *t1 = BN_CTX_get(ctx.get());
120
0
  if (t1 == nullptr) {
121
0
    return 0;
122
0
  }
123
0
  BIGNUM *t2 = BN_CTX_get(ctx.get());
124
0
  if (t2 == nullptr) {
125
0
    return 0;
126
0
  }
127
128
0
  if (impl->q) {
129
0
    if (BN_cmp(impl->g.get(), BN_value_one()) <= 0) {
130
0
      *out_flags |= DH_CHECK_NOT_SUITABLE_GENERATOR;
131
0
    } else if (BN_cmp(impl->g.get(), impl->p.get()) >= 0) {
132
0
      *out_flags |= DH_CHECK_NOT_SUITABLE_GENERATOR;
133
0
    } else {
134
      // Check g^q == 1 mod p
135
0
      if (!BN_mod_exp_mont(t1, impl->g.get(), impl->q.get(), impl->p.get(),
136
0
                           ctx.get(), nullptr)) {
137
0
        return 0;
138
0
      }
139
0
      if (!BN_is_one(t1)) {
140
0
        *out_flags |= DH_CHECK_NOT_SUITABLE_GENERATOR;
141
0
      }
142
0
    }
143
0
    int r = BN_is_prime_ex(impl->q.get(), BN_prime_checks_for_validation,
144
0
                           ctx.get(), nullptr);
145
0
    if (r < 0) {
146
0
      return 0;
147
0
    }
148
0
    if (!r) {
149
0
      *out_flags |= DH_CHECK_Q_NOT_PRIME;
150
0
    }
151
    // Check p == 1 mod q  i.e. q divides p - 1
152
0
    if (!BN_div(t1, t2, impl->p.get(), impl->q.get(), ctx.get())) {
153
0
      return 0;
154
0
    }
155
0
    if (!BN_is_one(t2)) {
156
0
      *out_flags |= DH_CHECK_INVALID_Q_VALUE;
157
0
    }
158
0
  } else if (BN_is_word(impl->g.get(), DH_GENERATOR_2)) {
159
0
    BN_ULONG l = BN_mod_word(impl->p.get(), 24);
160
0
    if (l == (BN_ULONG)-1) {
161
0
      return 0;
162
0
    }
163
0
    if (l != 11) {
164
0
      *out_flags |= DH_CHECK_NOT_SUITABLE_GENERATOR;
165
0
    }
166
0
  } else if (BN_is_word(impl->g.get(), DH_GENERATOR_5)) {
167
0
    BN_ULONG l = BN_mod_word(impl->p.get(), 10);
168
0
    if (l == (BN_ULONG)-1) {
169
0
      return 0;
170
0
    }
171
0
    if (l != 3 && l != 7) {
172
0
      *out_flags |= DH_CHECK_NOT_SUITABLE_GENERATOR;
173
0
    }
174
0
  } else {
175
0
    *out_flags |= DH_CHECK_UNABLE_TO_CHECK_GENERATOR;
176
0
  }
177
178
0
  int r = BN_is_prime_ex(impl->p.get(), BN_prime_checks_for_validation,
179
0
                         ctx.get(), nullptr);
180
0
  if (r < 0) {
181
0
    return 0;
182
0
  }
183
0
  if (!r) {
184
0
    *out_flags |= DH_CHECK_P_NOT_PRIME;
185
0
  } else if (!impl->q) {
186
0
    if (!BN_rshift1(t1, impl->p.get())) {
187
0
      return 0;
188
0
    }
189
0
    r = BN_is_prime_ex(t1, BN_prime_checks_for_validation, ctx.get(), nullptr);
190
0
    if (r < 0) {
191
0
      return 0;
192
0
    }
193
0
    if (!r) {
194
0
      *out_flags |= DH_CHECK_P_NOT_SAFE_PRIME;
195
0
    }
196
0
  }
197
0
  return 1;
198
0
}