Coverage Report

Created: 2026-06-28 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/boringssl/crypto/fipsmodule/bn/cmp.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/bn.h>
16
17
#include <assert.h>
18
19
#include <openssl/mem.h>
20
21
#include "internal.h"
22
#include "../../internal.h"
23
24
25
using namespace bssl;
26
27
static int bn_cmp_words_consttime(const BN_ULONG *a, size_t a_len,
28
642k
                                  const BN_ULONG *b, size_t b_len) {
29
642k
  static_assert(sizeof(BN_ULONG) <= sizeof(crypto_word_t),
30
642k
                "crypto_word_t is too small");
31
642k
  int ret = 0;
32
  // Process the common words in little-endian order.
33
642k
  size_t min = a_len < b_len ? a_len : b_len;
34
7.99M
  for (size_t i = 0; i < min; i++) {
35
7.35M
    crypto_word_t eq = constant_time_eq_w(a[i], b[i]);
36
7.35M
    crypto_word_t lt = constant_time_lt_w(a[i], b[i]);
37
7.35M
    ret =
38
7.35M
        constant_time_select_int(eq, ret, constant_time_select_int(lt, -1, 1));
39
7.35M
  }
40
41
  // If `a` or `b` has non-zero words beyond `min`, they take precedence.
42
642k
  if (a_len < b_len) {
43
69.5k
    crypto_word_t mask = 0;
44
1.07M
    for (size_t i = a_len; i < b_len; i++) {
45
1.00M
      mask |= b[i];
46
1.00M
    }
47
69.5k
    ret = constant_time_select_int(constant_time_is_zero_w(mask), ret, -1);
48
572k
  } else if (b_len < a_len) {
49
165k
    crypto_word_t mask = 0;
50
4.06M
    for (size_t i = b_len; i < a_len; i++) {
51
3.89M
      mask |= a[i];
52
3.89M
    }
53
165k
    ret = constant_time_select_int(constant_time_is_zero_w(mask), ret, 1);
54
165k
  }
55
56
642k
  return ret;
57
642k
}
58
59
421k
int BN_ucmp(const BIGNUM *a, const BIGNUM *b) {
60
421k
  return bn_cmp_words_consttime(a->d, a->width, b->d, b->width);
61
421k
}
62
63
80.8k
int BN_cmp(const BIGNUM *a, const BIGNUM *b) {
64
80.8k
  if ((a == nullptr) || (b == nullptr)) {
65
0
    if (a != nullptr) {
66
0
      return -1;
67
0
    } else if (b != nullptr) {
68
0
      return 1;
69
0
    } else {
70
0
      return 0;
71
0
    }
72
0
  }
73
74
  // We do not attempt to process the sign bit in constant time. Negative
75
  // `BIGNUM`s should never occur in crypto, only calculators.
76
80.8k
  if (a->neg != b->neg) {
77
0
    if (a->neg) {
78
0
      return -1;
79
0
    }
80
0
    return 1;
81
0
  }
82
83
80.8k
  int ret = BN_ucmp(a, b);
84
80.8k
  return a->neg ? -ret : ret;
85
80.8k
}
86
87
221k
int bssl::bn_less_than_words(const BN_ULONG *a, const BN_ULONG *b, size_t len) {
88
221k
  return bn_cmp_words_consttime(a, len, b, len) < 0;
89
221k
}
90
91
5.42M
int BN_abs_is_word(const BIGNUM *bn, BN_ULONG w) {
92
5.42M
  if (bn->width == 0) {
93
0
    return w == 0;
94
0
  }
95
5.42M
  BN_ULONG mask = bn->d[0] ^ w;
96
21.3M
  for (int i = 1; i < bn->width; i++) {
97
15.8M
    mask |= bn->d[i];
98
15.8M
  }
99
5.42M
  return mask == 0;
100
5.42M
}
101
102
0
int BN_cmp_word(const BIGNUM *a, BN_ULONG b) {
103
0
  BIGNUM b_bn;
104
0
  BN_init(&b_bn);
105
106
0
  b_bn.d = &b;
107
0
  b_bn.width = b > 0;
108
0
  b_bn.dmax = 1;
109
0
  b_bn.flags = BN_FLG_STATIC_DATA;
110
0
  return BN_cmp(a, &b_bn);
111
0
}
112
113
6.65M
int BN_is_zero(const BIGNUM *bn) {
114
6.65M
  return bn_fits_in_words(bn, 0);
115
6.65M
}
116
117
5.42M
int BN_is_one(const BIGNUM *bn) {
118
5.42M
  return bn->neg == 0 && BN_abs_is_word(bn, 1);
119
5.42M
}
120
121
0
int BN_is_word(const BIGNUM *bn, BN_ULONG w) {
122
0
  return BN_abs_is_word(bn, w) && (w == 0 || bn->neg == 0);
123
0
}
124
125
764k
int BN_is_odd(const BIGNUM *bn) {
126
764k
  return bn->width > 0 && (bn->d[0] & 1) == 1;
127
764k
}
128
129
0
int BN_is_pow2(const BIGNUM *bn) {
130
0
  int width = bn_minimal_width(bn);
131
0
  if (width == 0 || bn->neg) {
132
0
    return 0;
133
0
  }
134
135
0
  for (int i = 0; i < width - 1; i++) {
136
0
    if (bn->d[i] != 0) {
137
0
      return 0;
138
0
    }
139
0
  }
140
141
0
  return 0 == (bn->d[width-1] & (bn->d[width-1] - 1));
142
0
}
143
144
21.1k
int BN_equal_consttime(const BIGNUM *a, const BIGNUM *b) {
145
21.1k
  BN_ULONG mask = 0;
146
  // If `a` or `b` has more words than the other, all those words must be zero.
147
21.1k
  for (int i = a->width; i < b->width; i++) {
148
0
    mask |= b->d[i];
149
0
  }
150
21.1k
  for (int i = b->width; i < a->width; i++) {
151
0
    mask |= a->d[i];
152
0
  }
153
  // Common words must match.
154
21.1k
  int min = a->width < b->width ? a->width : b->width;
155
698k
  for (int i = 0; i < min; i++) {
156
677k
    mask |= (a->d[i] ^ b->d[i]);
157
677k
  }
158
  // The sign bit must match.
159
21.1k
  mask |= (a->neg ^ b->neg);
160
21.1k
  return mask == 0;
161
21.1k
}