Coverage Report

Created: 2025-11-17 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/boringssl/crypto/fipsmodule/ec/oct.cc.inc
Line
Count
Source
1
// Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
2
// Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
3
//
4
// Licensed under the Apache License, Version 2.0 (the "License");
5
// you may not use this file except in compliance with the License.
6
// You may obtain a copy of the License at
7
//
8
//     https://www.apache.org/licenses/LICENSE-2.0
9
//
10
// Unless required by applicable law or agreed to in writing, software
11
// distributed under the License is distributed on an "AS IS" BASIS,
12
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
// See the License for the specific language governing permissions and
14
// limitations under the License.
15
16
#include <openssl/ec.h>
17
18
#include <openssl/bn.h>
19
#include <openssl/err.h>
20
21
#include "internal.h"
22
23
24
76.6k
size_t ec_point_byte_len(const EC_GROUP *group, point_conversion_form_t form) {
25
76.6k
  if (form != POINT_CONVERSION_COMPRESSED &&
26
76.6k
      form != POINT_CONVERSION_UNCOMPRESSED) {
27
0
    OPENSSL_PUT_ERROR(EC, EC_R_INVALID_FORM);
28
0
    return 0;
29
0
  }
30
31
76.6k
  const size_t field_len = BN_num_bytes(&group->field.N);
32
76.6k
  size_t output_len = 1 /* type byte */ + field_len;
33
76.6k
  if (form == POINT_CONVERSION_UNCOMPRESSED) {
34
    // Uncompressed points have a second coordinate.
35
76.6k
    output_len += field_len;
36
76.6k
  }
37
76.6k
  return output_len;
38
76.6k
}
39
40
size_t ec_point_to_bytes(const EC_GROUP *group, const EC_AFFINE *point,
41
                         point_conversion_form_t form, uint8_t *buf,
42
38.3k
                         size_t max_out) {
43
38.3k
  size_t output_len = ec_point_byte_len(group, form);
44
38.3k
  if (max_out < output_len) {
45
0
    OPENSSL_PUT_ERROR(EC, EC_R_BUFFER_TOO_SMALL);
46
0
    return 0;
47
0
  }
48
49
38.3k
  size_t field_len;
50
38.3k
  ec_felem_to_bytes(group, buf + 1, &field_len, &point->X);
51
38.3k
  assert(field_len == BN_num_bytes(&group->field.N));
52
53
38.3k
  if (form == POINT_CONVERSION_UNCOMPRESSED) {
54
38.3k
    ec_felem_to_bytes(group, buf + 1 + field_len, &field_len, &point->Y);
55
38.3k
    assert(field_len == BN_num_bytes(&group->field.N));
56
38.3k
    buf[0] = form;
57
38.3k
  } else {
58
3
    uint8_t y_buf[EC_MAX_BYTES];
59
3
    ec_felem_to_bytes(group, y_buf, &field_len, &point->Y);
60
3
    buf[0] = form + (y_buf[field_len - 1] & 1);
61
3
  }
62
63
38.3k
  return output_len;
64
38.3k
}
65
66
int ec_point_from_uncompressed(const EC_GROUP *group, EC_AFFINE *out,
67
84.2k
                               const uint8_t *in, size_t len) {
68
84.2k
  const size_t field_len = BN_num_bytes(&group->field.N);
69
84.2k
  if (len != 1 + 2 * field_len || in[0] != POINT_CONVERSION_UNCOMPRESSED) {
70
175
    OPENSSL_PUT_ERROR(EC, EC_R_INVALID_ENCODING);
71
175
    return 0;
72
175
  }
73
74
84.0k
  EC_FELEM x, y;
75
84.0k
  if (!ec_felem_from_bytes(group, &x, in + 1, field_len) ||
76
83.9k
      !ec_felem_from_bytes(group, &y, in + 1 + field_len, field_len) ||
77
83.7k
      !ec_point_set_affine_coordinates(group, out, &x, &y)) {
78
14.9k
    return 0;
79
14.9k
  }
80
81
69.0k
  return 1;
82
84.0k
}
83
84
static int ec_GFp_simple_oct2point(const EC_GROUP *group, EC_POINT *point,
85
                                   const uint8_t *buf, size_t len,
86
89.0k
                                   BN_CTX *ctx) {
87
89.0k
  if (len == 0) {
88
8
    OPENSSL_PUT_ERROR(EC, EC_R_BUFFER_TOO_SMALL);
89
8
    return 0;
90
8
  }
91
92
89.0k
  uint8_t form = buf[0];
93
89.0k
  if (form == static_cast<uint8_t>(POINT_CONVERSION_UNCOMPRESSED)) {
94
84.2k
    EC_AFFINE affine;
95
84.2k
    if (!ec_point_from_uncompressed(group, &affine, buf, len)) {
96
      // In the event of an error, defend against the caller not checking the
97
      // return value by setting a known safe value.
98
15.1k
      ec_set_to_safe_point(group, &point->raw);
99
15.1k
      return 0;
100
15.1k
    }
101
69.0k
    ec_affine_to_jacobian(group, &point->raw, &affine);
102
69.0k
    return 1;
103
84.2k
  }
104
105
4.81k
  const int y_bit = form & 1;
106
4.81k
  const size_t field_len = BN_num_bytes(&group->field.N);
107
4.81k
  form = form & ~1u;
108
4.81k
  if (form != static_cast<uint8_t>(POINT_CONVERSION_COMPRESSED) ||
109
3.89k
      len != 1 /* type byte */ + field_len) {
110
1.10k
    OPENSSL_PUT_ERROR(EC, EC_R_INVALID_ENCODING);
111
1.10k
    return 0;
112
1.10k
  }
113
114
  // TODO(davidben): Integrate compressed coordinates with the lower-level EC
115
  // abstractions. This requires a way to compute square roots, which is tricky
116
  // for primes which are not 3 (mod 4), namely P-224 and custom curves. P-224's
117
  // prime is particularly inconvenient for compressed coordinates. See
118
  // https://cr.yp.to/papers/sqroot.pdf
119
3.70k
  bssl::UniquePtr<BN_CTX> new_ctx;
120
3.70k
  if (ctx == nullptr) {
121
3.70k
    new_ctx.reset(BN_CTX_new());
122
3.70k
    if (new_ctx == nullptr) {
123
0
      return 0;
124
0
    }
125
3.70k
    ctx = new_ctx.get();
126
3.70k
  }
127
128
3.70k
  bssl::BN_CTXScope scope(ctx);
129
3.70k
  BIGNUM *x = BN_CTX_get(ctx);
130
3.70k
  if (x == nullptr || !BN_bin2bn(buf + 1, field_len, x)) {
131
0
    return 0;
132
0
  }
133
3.70k
  if (BN_ucmp(x, &group->field.N) >= 0) {
134
18
    OPENSSL_PUT_ERROR(EC, EC_R_INVALID_ENCODING);
135
18
    return 0;
136
18
  }
137
138
3.69k
  if (!EC_POINT_set_compressed_coordinates_GFp(group, point, x, y_bit, ctx)) {
139
776
    return 0;
140
776
  }
141
142
2.91k
  return 1;
143
3.69k
}
144
145
int EC_POINT_oct2point(const EC_GROUP *group, EC_POINT *point,
146
89.0k
                       const uint8_t *buf, size_t len, BN_CTX *ctx) {
147
89.0k
  if (EC_GROUP_cmp(group, point->group, nullptr) != 0) {
148
0
    OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
149
0
    return 0;
150
0
  }
151
89.0k
  return ec_GFp_simple_oct2point(group, point, buf, len, ctx);
152
89.0k
}
153
154
size_t EC_POINT_point2oct(const EC_GROUP *group, const EC_POINT *point,
155
                          point_conversion_form_t form, uint8_t *buf,
156
76.6k
                          size_t max_out, BN_CTX *ctx) {
157
76.6k
  if (EC_GROUP_cmp(group, point->group, nullptr) != 0) {
158
0
    OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
159
0
    return 0;
160
0
  }
161
76.6k
  if (buf == nullptr) {
162
    // When |buf| is NULL, just return the number of bytes that would be
163
    // written, without doing an expensive Jacobian-to-affine conversion.
164
38.3k
    if (ec_GFp_simple_is_at_infinity(group, &point->raw)) {
165
0
      OPENSSL_PUT_ERROR(EC, EC_R_POINT_AT_INFINITY);
166
0
      return 0;
167
0
    }
168
38.3k
    return ec_point_byte_len(group, form);
169
38.3k
  }
170
38.3k
  EC_AFFINE affine;
171
38.3k
  if (!ec_jacobian_to_affine(group, &affine, &point->raw)) {
172
0
    return 0;
173
0
  }
174
38.3k
  return ec_point_to_bytes(group, &affine, form, buf, max_out);
175
38.3k
}
176
177
size_t EC_POINT_point2buf(const EC_GROUP *group, const EC_POINT *point,
178
                          point_conversion_form_t form, uint8_t **out_buf,
179
1.85k
                          BN_CTX *ctx) {
180
1.85k
  *out_buf = nullptr;
181
1.85k
  size_t len = EC_POINT_point2oct(group, point, form, nullptr, 0, ctx);
182
1.85k
  if (len == 0) {
183
0
    return 0;
184
0
  }
185
1.85k
  uint8_t *buf = reinterpret_cast<uint8_t *>(OPENSSL_malloc(len));
186
1.85k
  if (buf == nullptr) {
187
0
    return 0;
188
0
  }
189
1.85k
  len = EC_POINT_point2oct(group, point, form, buf, len, ctx);
190
1.85k
  if (len == 0) {
191
0
    OPENSSL_free(buf);
192
0
    return 0;
193
0
  }
194
1.85k
  *out_buf = buf;
195
1.85k
  return len;
196
1.85k
}
197
198
int EC_POINT_set_compressed_coordinates_GFp(const EC_GROUP *group,
199
                                            EC_POINT *point, const BIGNUM *x,
200
3.69k
                                            int y_bit, BN_CTX *ctx) {
201
3.69k
  if (EC_GROUP_cmp(group, point->group, nullptr) != 0) {
202
0
    OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
203
0
    return 0;
204
0
  }
205
206
3.69k
  const BIGNUM *field = &group->field.N;
207
3.69k
  if (BN_is_negative(x) || BN_cmp(x, field) >= 0) {
208
0
    OPENSSL_PUT_ERROR(EC, EC_R_INVALID_COMPRESSED_POINT);
209
0
    return 0;
210
0
  }
211
212
3.69k
  ERR_clear_error();
213
214
3.69k
  bssl::UniquePtr<BN_CTX> new_ctx;
215
3.69k
  if (ctx == nullptr) {
216
0
    new_ctx.reset(BN_CTX_new());
217
0
    if (new_ctx == nullptr) {
218
0
      return 0;
219
0
    }
220
0
    ctx = new_ctx.get();
221
0
  }
222
223
3.69k
  y_bit = (y_bit != 0);
224
225
3.69k
  bssl::BN_CTXScope scope(ctx);
226
3.69k
  BIGNUM *tmp1 = BN_CTX_get(ctx);
227
3.69k
  BIGNUM *tmp2 = BN_CTX_get(ctx);
228
3.69k
  BIGNUM *a = BN_CTX_get(ctx);
229
3.69k
  BIGNUM *b = BN_CTX_get(ctx);
230
3.69k
  BIGNUM *y = BN_CTX_get(ctx);
231
3.69k
  if (y == nullptr || !EC_GROUP_get_curve_GFp(group, nullptr, a, b, ctx)) {
232
0
    return 0;
233
0
  }
234
235
  // Recover y.  We have a Weierstrass equation
236
  //     y^2 = x^3 + a*x + b,
237
  // so  y  is one of the square roots of  x^3 + a*x + b.
238
239
  // tmp1 := x^3
240
3.69k
  if (!BN_mod_sqr(tmp2, x, field, ctx) ||
241
3.69k
      !BN_mod_mul(tmp1, tmp2, x, field, ctx)) {
242
0
    return 0;
243
0
  }
244
245
  // tmp1 := tmp1 + a*x
246
3.69k
  if (group->a_is_minus3) {
247
3.69k
    if (!bn_mod_lshift1_consttime(tmp2, x, field, ctx) ||
248
3.69k
        !bn_mod_add_consttime(tmp2, tmp2, x, field, ctx) ||
249
3.69k
        !bn_mod_sub_consttime(tmp1, tmp1, tmp2, field, ctx)) {
250
0
      return 0;
251
0
    }
252
3.69k
  } else {
253
0
    if (!BN_mod_mul(tmp2, a, x, field, ctx) ||
254
0
        !bn_mod_add_consttime(tmp1, tmp1, tmp2, field, ctx)) {
255
0
      return 0;
256
0
    }
257
0
  }
258
259
  // tmp1 := tmp1 + b
260
3.69k
  if (!bn_mod_add_consttime(tmp1, tmp1, b, field, ctx)) {
261
0
    return 0;
262
0
  }
263
264
3.69k
  if (!BN_mod_sqrt(y, tmp1, field, ctx)) {
265
776
    if (ERR_equals(ERR_peek_last_error(), ERR_LIB_BN, BN_R_NOT_A_SQUARE)) {
266
776
      ERR_clear_error();
267
776
      OPENSSL_PUT_ERROR(EC, EC_R_INVALID_COMPRESSED_POINT);
268
776
    } else {
269
0
      OPENSSL_PUT_ERROR(EC, ERR_R_BN_LIB);
270
0
    }
271
776
    return 0;
272
776
  }
273
274
2.91k
  if (y_bit != BN_is_odd(y)) {
275
1.65k
    if (BN_is_zero(y)) {
276
0
      OPENSSL_PUT_ERROR(EC, EC_R_INVALID_COMPRESSION_BIT);
277
0
      return 0;
278
0
    }
279
1.65k
    if (!BN_usub(y, field, y)) {
280
0
      return 0;
281
0
    }
282
1.65k
  }
283
2.91k
  if (y_bit != BN_is_odd(y)) {
284
0
    OPENSSL_PUT_ERROR(EC, ERR_R_INTERNAL_ERROR);
285
0
    return 0;
286
0
  }
287
288
2.91k
  if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) {
289
0
    return 0;
290
0
  }
291
292
2.91k
  return 1;
293
2.91k
}