Coverage Report

Created: 2026-10-27 07:01

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
587k
                                  const BN_ULONG *b, size_t b_len) {
29
587k
  static_assert(sizeof(BN_ULONG) <= sizeof(crypto_word_t),
30
587k
                "crypto_word_t is too small");
31
587k
  int ret = 0;
32
  // Process the common words in little-endian order.
33
587k
  size_t min = a_len < b_len ? a_len : b_len;
34
7.18M
  for (size_t i = 0; i < min; i++) {
35
6.59M
    crypto_word_t eq = constant_time_eq_w(a[i], b[i]);
36
6.59M
    crypto_word_t lt = constant_time_lt_w(a[i], b[i]);
37
6.59M
    ret =
38
6.59M
        constant_time_select_int(eq, ret, constant_time_select_int(lt, -1, 1));
39
6.59M
  }
40
41
  // If `a` or `b` has non-zero words beyond `min`, they take precedence.
42
587k
  if (a_len < b_len) {
43
65.1k
    crypto_word_t mask = 0;
44
1.55M
    for (size_t i = a_len; i < b_len; i++) {
45
1.49M
      mask |= b[i];
46
1.49M
    }
47
65.1k
    ret = constant_time_select_int(constant_time_is_zero_w(mask), ret, -1);
48
522k
  } else if (b_len < a_len) {
49
149k
    crypto_word_t mask = 0;
50
3.74M
    for (size_t i = b_len; i < a_len; i++) {
51
3.59M
      mask |= a[i];
52
3.59M
    }
53
149k
    ret = constant_time_select_int(constant_time_is_zero_w(mask), ret, 1);
54
149k
  }
55
56
587k
  return ret;
57
587k
}
58
59
366k
int BN_ucmp(const BIGNUM *a, const BIGNUM *b) {
60
366k
  return bn_cmp_words_consttime(a->d, a->width, b->d, b->width);
61
366k
}
62
63
62.1k
int BN_cmp(const BIGNUM *a, const BIGNUM *b) {
64
62.1k
  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
62.1k
  if (a->neg != b->neg) {
77
0
    if (a->neg) {
78
0
      return -1;
79
0
    }
80
0
    return 1;
81
0
  }
82
83
62.1k
  int ret = BN_ucmp(a, b);
84
62.1k
  return a->neg ? -ret : ret;
85
62.1k
}
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.39M
int BN_abs_is_word(const BIGNUM *bn, BN_ULONG w) {
92
5.39M
  if (bn->width == 0) {
93
0
    return w == 0;
94
0
  }
95
5.39M
  BN_ULONG mask = bn->d[0] ^ w;
96
21.1M
  for (int i = 1; i < bn->width; i++) {
97
15.7M
    mask |= bn->d[i];
98
15.7M
  }
99
5.39M
  return mask == 0;
100
5.39M
}
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.62M
int BN_is_zero(const BIGNUM *bn) {
114
6.62M
  return bn_fits_in_words(bn, 0);
115
6.62M
}
116
117
5.39M
int BN_is_one(const BIGNUM *bn) {
118
5.39M
  return bn->neg == 0 && BN_abs_is_word(bn, 1);
119
5.39M
}
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
712k
int BN_is_odd(const BIGNUM *bn) {
126
712k
  return bn->width > 0 && (bn->d[0] & 1) == 1;
127
712k
}
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
17.8k
int BN_equal_consttime(const BIGNUM *a, const BIGNUM *b) {
145
17.8k
  BN_ULONG mask = 0;
146
  // If `a` or `b` has more words than the other, all those words must be zero.
147
17.8k
  for (int i = a->width; i < b->width; i++) {
148
0
    mask |= b->d[i];
149
0
  }
150
17.8k
  for (int i = b->width; i < a->width; i++) {
151
0
    mask |= a->d[i];
152
0
  }
153
  // Common words must match.
154
17.8k
  int min = a->width < b->width ? a->width : b->width;
155
589k
  for (int i = 0; i < min; i++) {
156
572k
    mask |= (a->d[i] ^ b->d[i]);
157
572k
  }
158
  // The sign bit must match.
159
17.8k
  mask |= (a->neg ^ b->neg);
160
17.8k
  return mask == 0;
161
17.8k
}