summaryrefslogtreecommitdiff
path: root/src/lib/libcrypto/ec/ec_convert.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lib/libcrypto/ec/ec_convert.c296
1 files changed, 295 insertions, 1 deletions
diff --git a/src/lib/libcrypto/ec/ec_convert.c b/src/lib/libcrypto/ec/ec_convert.c
index fd0182f420..123cf90ef9 100644
--- a/src/lib/libcrypto/ec/ec_convert.c
+++ b/src/lib/libcrypto/ec/ec_convert.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ec_convert.c,v 1.1 2024/10/30 18:14:49 tb Exp $ */ 1/* $OpenBSD: ec_convert.c,v 1.2 2024/10/30 18:16:34 tb Exp $ */
2/* 2/*
3 * Originally written by Bodo Moeller for the OpenSSL project. 3 * Originally written by Bodo Moeller for the OpenSSL project.
4 */ 4 */
@@ -69,6 +69,300 @@
69#include "asn1_local.h" 69#include "asn1_local.h"
70#include "ec_local.h" 70#include "ec_local.h"
71 71
72/*
73 * Only the last three bits of the leading octet of a point should be set.
74 * Bits 3 and 2 encode the conversion form for all points except the point
75 * at infinity. In compressed and hybrid form bit 1 indicates if the even
76 * or the odd solution of the quadratic equation for y should be used.
77 *
78 * The public point_conversion_t enum lacks the point at infinity, so we
79 * ignore it except at the API boundary.
80 */
81
82#define EC_OCT_YBIT 0x01
83
84#define EC_OCT_POINT_AT_INFINITY 0x00
85#define EC_OCT_POINT_COMPRESSED 0x02
86#define EC_OCT_POINT_UNCOMPRESSED 0x04
87#define EC_OCT_POINT_HYBRID 0x06
88#define EC_OCT_POINT_CONVERSION_MASK 0x06
89
90static int
91ec_oct_conversion_form_is_valid(uint8_t form)
92{
93 return (form & EC_OCT_POINT_CONVERSION_MASK) == form;
94}
95
96static int
97ec_oct_check_hybrid_ybit_is_consistent(uint8_t form, int ybit, const BIGNUM *y)
98{
99 if (form == EC_OCT_POINT_HYBRID && ybit != BN_is_odd(y)) {
100 ECerror(EC_R_INVALID_ENCODING);
101 return 0;
102 }
103
104 return 1;
105}
106
107/* Nonzero y-bit only makes sense with compressed or hybrid encoding. */
108static int
109ec_oct_nonzero_ybit_allowed(uint8_t form)
110{
111 return form == EC_OCT_POINT_COMPRESSED || form == EC_OCT_POINT_HYBRID;
112}
113
114static int
115ec_oct_add_leading_octet_cbb(CBB *cbb, uint8_t form, int ybit)
116{
117 if (ec_oct_nonzero_ybit_allowed(form) && ybit != 0)
118 form |= EC_OCT_YBIT;
119
120 return CBB_add_u8(cbb, form);
121}
122
123static int
124ec_oct_get_leading_octet_cbs(CBS *cbs, uint8_t *out_form, int *out_ybit)
125{
126 uint8_t octet;
127
128 if (!CBS_get_u8(cbs, &octet)) {
129 ECerror(EC_R_BUFFER_TOO_SMALL);
130 return 0;
131 }
132
133 *out_ybit = octet & EC_OCT_YBIT;
134 *out_form = octet & ~EC_OCT_YBIT;
135
136 if (!ec_oct_conversion_form_is_valid(*out_form)) {
137 ECerror(EC_R_INVALID_ENCODING);
138 return 0;
139 }
140
141 if (*out_ybit != 0 && !ec_oct_nonzero_ybit_allowed(*out_form)) {
142 ECerror(EC_R_INVALID_ENCODING);
143 return 0;
144 }
145
146 return 1;
147}
148
149static int
150ec_oct_encoded_length(const EC_GROUP *group, uint8_t form, size_t *out_len)
151{
152 switch (form) {
153 case EC_OCT_POINT_AT_INFINITY:
154 *out_len = 1;
155 return 1;
156 case EC_OCT_POINT_COMPRESSED:
157 *out_len = 1 + BN_num_bytes(&group->field);
158 return 1;
159 case EC_OCT_POINT_UNCOMPRESSED:
160 case EC_OCT_POINT_HYBRID:
161 *out_len = 1 + 2 * BN_num_bytes(&group->field);
162 return 1;
163 default:
164 return 0;
165 }
166}
167
168static int
169ec_oct_field_element_is_valid(const EC_GROUP *group, const BIGNUM *bn)
170{
171 /* Ensure bn is in the range [0, field). */
172 return !BN_is_negative(bn) && BN_cmp(&group->field, bn) > 0;
173}
174
175static int
176ec_oct_add_field_element_cbb(CBB *cbb, const EC_GROUP *group, const BIGNUM *bn)
177{
178 uint8_t *buf = NULL;
179 int buf_len = BN_num_bytes(&group->field);
180
181 if (!ec_oct_field_element_is_valid(group, bn)) {
182 ECerror(EC_R_BIGNUM_OUT_OF_RANGE);
183 return 0;
184 }
185 if (!CBB_add_space(cbb, &buf, buf_len)) {
186 ECerror(ERR_R_MALLOC_FAILURE);
187 return 0;
188 }
189 if (BN_bn2binpad(bn, buf, buf_len) != buf_len) {
190 ECerror(ERR_R_MALLOC_FAILURE);
191 return 0;
192 }
193
194 return 1;
195}
196
197static int
198ec_oct_get_field_element_cbs(CBS *cbs, const EC_GROUP *group, BIGNUM *bn)
199{
200 CBS field_element;
201
202 if (!CBS_get_bytes(cbs, &field_element, BN_num_bytes(&group->field))) {
203 ECerror(EC_R_INVALID_ENCODING);
204 return 0;
205 }
206 if (!BN_bin2bn(CBS_data(&field_element), CBS_len(&field_element), bn)) {
207 ECerror(ERR_R_MALLOC_FAILURE);
208 return 0;
209 }
210 if (!ec_oct_field_element_is_valid(group, bn)) {
211 ECerror(EC_R_BIGNUM_OUT_OF_RANGE);
212 return 0;
213 }
214
215 return 1;
216}
217
218size_t
219ec_GFp_simple_point2oct(const EC_GROUP *group, const EC_POINT *point,
220 point_conversion_form_t conversion_form, unsigned char *buf, size_t len,
221 BN_CTX *ctx)
222{
223 CBB cbb;
224 uint8_t form;
225 BIGNUM *x, *y;
226 size_t encoded_length;
227 size_t ret = 0;
228
229 if (conversion_form > UINT8_MAX) {
230 ECerror(EC_R_INVALID_FORM);
231 return 0;
232 }
233
234 form = conversion_form;
235
236 /*
237 * Established behavior is to reject a request for the form 0 for the
238 * point at infinity even if it is valid.
239 */
240 if (form == 0 || !ec_oct_conversion_form_is_valid(form)) {
241 ECerror(EC_R_INVALID_FORM);
242 return 0;
243 }
244
245 if (EC_POINT_is_at_infinity(group, point))
246 form = EC_OCT_POINT_AT_INFINITY;
247
248 if (!ec_oct_encoded_length(group, form, &encoded_length)) {
249 ECerror(EC_R_INVALID_FORM);
250 return 0;
251 }
252
253 if (buf == NULL)
254 return encoded_length;
255
256 if (len < encoded_length) {
257 ECerror(EC_R_BUFFER_TOO_SMALL);
258 return 0;
259 }
260
261 BN_CTX_start(ctx);
262 if (!CBB_init_fixed(&cbb, buf, len))
263 goto err;
264
265 if (form == EC_OCT_POINT_AT_INFINITY) {
266 if (!ec_oct_add_leading_octet_cbb(&cbb, form, 0))
267 goto err;
268
269 goto done;
270 }
271
272 if ((x = BN_CTX_get(ctx)) == NULL)
273 goto err;
274 if ((y = BN_CTX_get(ctx)) == NULL)
275 goto err;
276 if (!EC_POINT_get_affine_coordinates(group, point, x, y, ctx))
277 goto err;
278
279 if (!ec_oct_add_leading_octet_cbb(&cbb, form, BN_is_odd(y)))
280 goto err;
281
282 if (form == EC_OCT_POINT_COMPRESSED) {
283 if (!ec_oct_add_field_element_cbb(&cbb, group, x))
284 goto err;
285 } else {
286 if (!ec_oct_add_field_element_cbb(&cbb, group, x))
287 goto err;
288 if (!ec_oct_add_field_element_cbb(&cbb, group, y))
289 goto err;
290 }
291
292 done:
293 if (!CBB_finish(&cbb, NULL, &ret))
294 goto err;
295
296 if (ret != encoded_length) {
297 ret = 0;
298 goto err;
299 }
300
301 err:
302 CBB_cleanup(&cbb);
303 BN_CTX_end(ctx);
304
305 return ret;
306}
307
308int
309ec_GFp_simple_oct2point(const EC_GROUP *group, EC_POINT *point,
310 const unsigned char *buf, size_t len, BN_CTX *ctx)
311{
312 CBS cbs;
313 uint8_t form;
314 int ybit;
315 BIGNUM *x, *y;
316 int ret = 0;
317
318 BN_CTX_start(ctx);
319 CBS_init(&cbs, buf, len);
320
321 if (!ec_oct_get_leading_octet_cbs(&cbs, &form, &ybit))
322 goto err;
323
324 if (form == EC_OCT_POINT_AT_INFINITY) {
325 if (!EC_POINT_set_to_infinity(group, point))
326 goto err;
327
328 goto done;
329 }
330
331 if ((x = BN_CTX_get(ctx)) == NULL)
332 goto err;
333 if ((y = BN_CTX_get(ctx)) == NULL)
334 goto err;
335
336 if (form == EC_OCT_POINT_COMPRESSED) {
337 if (!ec_oct_get_field_element_cbs(&cbs, group, x))
338 goto err;
339 if (!EC_POINT_set_compressed_coordinates(group, point, x, ybit, ctx))
340 goto err;
341 } else {
342 if (!ec_oct_get_field_element_cbs(&cbs, group, x))
343 goto err;
344 if (!ec_oct_get_field_element_cbs(&cbs, group, y))
345 goto err;
346 if (!ec_oct_check_hybrid_ybit_is_consistent(form, ybit, y))
347 goto err;
348 if (!EC_POINT_set_affine_coordinates(group, point, x, y, ctx))
349 goto err;
350 }
351
352 done:
353 if (CBS_len(&cbs) > 0) {
354 ECerror(EC_R_INVALID_ENCODING);
355 goto err;
356 }
357
358 ret = 1;
359
360 err:
361 BN_CTX_end(ctx);
362
363 return ret;
364}
365
72int 366int
73ec_point_to_octets(const EC_GROUP *group, const EC_POINT *point, int form, 367ec_point_to_octets(const EC_GROUP *group, const EC_POINT *point, int form,
74 unsigned char **out_buf, size_t *out_len, BN_CTX *ctx) 368 unsigned char **out_buf, size_t *out_len, BN_CTX *ctx)