• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Originally written by Bodo Moeller for the OpenSSL project.
2  * ====================================================================
3  * Copyright (c) 1998-2005 The OpenSSL Project.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * 3. All advertising materials mentioning features or use of this
18  *    software must display the following acknowledgment:
19  *    "This product includes software developed by the OpenSSL Project
20  *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
21  *
22  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
23  *    endorse or promote products derived from this software without
24  *    prior written permission. For written permission, please contact
25  *    openssl-core@openssl.org.
26  *
27  * 5. Products derived from this software may not be called "OpenSSL"
28  *    nor may "OpenSSL" appear in their names without prior written
29  *    permission of the OpenSSL Project.
30  *
31  * 6. Redistributions of any form whatsoever must retain the following
32  *    acknowledgment:
33  *    "This product includes software developed by the OpenSSL Project
34  *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
35  *
36  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
37  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
39  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
42  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
43  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
45  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
46  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
47  * OF THE POSSIBILITY OF SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This product includes cryptographic software written by Eric Young
51  * (eay@cryptsoft.com).  This product includes software written by Tim
52  * Hudson (tjh@cryptsoft.com).
53  *
54  */
55 /* ====================================================================
56  * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
57  *
58  * Portions of the attached software ("Contribution") are developed by
59  * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
60  *
61  * The Contribution is licensed pursuant to the OpenSSL open source
62  * license provided above.
63  *
64  * The elliptic curve binary polynomial software is originally written by
65  * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems
66  * Laboratories. */
67 
68 #include <openssl/ec.h>
69 
70 #include <openssl/bn.h>
71 #include <openssl/err.h>
72 
73 #include "internal.h"
74 
75 
ec_point_to_bytes(const EC_GROUP * group,const EC_AFFINE * point,point_conversion_form_t form,uint8_t * buf,size_t len)76 size_t ec_point_to_bytes(const EC_GROUP *group, const EC_AFFINE *point,
77                          point_conversion_form_t form, uint8_t *buf,
78                          size_t len) {
79   if (form != POINT_CONVERSION_COMPRESSED &&
80       form != POINT_CONVERSION_UNCOMPRESSED) {
81     OPENSSL_PUT_ERROR(EC, EC_R_INVALID_FORM);
82     return 0;
83   }
84 
85   const size_t field_len = BN_num_bytes(&group->field);
86   size_t output_len = 1 /* type byte */ + field_len;
87   if (form == POINT_CONVERSION_UNCOMPRESSED) {
88     // Uncompressed points have a second coordinate.
89     output_len += field_len;
90   }
91 
92   // if 'buf' is NULL, just return required length
93   if (buf != NULL) {
94     if (len < output_len) {
95       OPENSSL_PUT_ERROR(EC, EC_R_BUFFER_TOO_SMALL);
96       return 0;
97     }
98 
99     size_t field_len_out;
100     ec_felem_to_bytes(group, buf + 1, &field_len_out, &point->X);
101     assert(field_len_out == field_len);
102 
103     if (form == POINT_CONVERSION_UNCOMPRESSED) {
104       ec_felem_to_bytes(group, buf + 1 + field_len, &field_len_out, &point->Y);
105       assert(field_len_out == field_len);
106       buf[0] = form;
107     } else {
108       uint8_t y_buf[EC_MAX_BYTES];
109       ec_felem_to_bytes(group, y_buf, &field_len_out, &point->Y);
110       buf[0] = form + (y_buf[field_len_out - 1] & 1);
111     }
112   }
113 
114   return output_len;
115 }
116 
ec_point_from_uncompressed(const EC_GROUP * group,EC_AFFINE * out,const uint8_t * in,size_t len)117 int ec_point_from_uncompressed(const EC_GROUP *group, EC_AFFINE *out,
118                                const uint8_t *in, size_t len) {
119   const size_t field_len = BN_num_bytes(&group->field);
120   if (len != 1 + 2 * field_len || in[0] != POINT_CONVERSION_UNCOMPRESSED) {
121     OPENSSL_PUT_ERROR(EC, EC_R_INVALID_ENCODING);
122     return 0;
123   }
124 
125   EC_FELEM x, y;
126   if (!ec_felem_from_bytes(group, &x, in + 1, field_len) ||
127       !ec_felem_from_bytes(group, &y, in + 1 + field_len, field_len) ||
128       !ec_point_set_affine_coordinates(group, out, &x, &y)) {
129     return 0;
130   }
131 
132   return 1;
133 }
134 
ec_GFp_simple_oct2point(const EC_GROUP * group,EC_POINT * point,const uint8_t * buf,size_t len,BN_CTX * ctx)135 static int ec_GFp_simple_oct2point(const EC_GROUP *group, EC_POINT *point,
136                                    const uint8_t *buf, size_t len,
137                                    BN_CTX *ctx) {
138   if (len == 0) {
139     OPENSSL_PUT_ERROR(EC, EC_R_BUFFER_TOO_SMALL);
140     return 0;
141   }
142 
143   point_conversion_form_t form = buf[0];
144   if (form == POINT_CONVERSION_UNCOMPRESSED) {
145     EC_AFFINE affine;
146     if (!ec_point_from_uncompressed(group, &affine, buf, len)) {
147       // In the event of an error, defend against the caller not checking the
148       // return value by setting a known safe value.
149       ec_set_to_safe_point(group, &point->raw);
150       return 0;
151     }
152     ec_affine_to_jacobian(group, &point->raw, &affine);
153     return 1;
154   }
155 
156   const int y_bit = form & 1;
157   const size_t field_len = BN_num_bytes(&group->field);
158   form = form & ~1u;
159   if (form != POINT_CONVERSION_COMPRESSED ||
160       len != 1 /* type byte */ + field_len) {
161     OPENSSL_PUT_ERROR(EC, EC_R_INVALID_ENCODING);
162     return 0;
163   }
164 
165   // TODO(davidben): Integrate compressed coordinates with the lower-level EC
166   // abstractions. This requires a way to compute square roots, which is tricky
167   // for primes which are not 3 (mod 4), namely P-224 and custom curves. P-224's
168   // prime is particularly inconvenient for compressed coordinates. See
169   // https://cr.yp.to/papers/sqroot.pdf
170   BN_CTX *new_ctx = NULL;
171   if (ctx == NULL) {
172     ctx = new_ctx = BN_CTX_new();
173     if (ctx == NULL) {
174       return 0;
175     }
176   }
177 
178   int ret = 0;
179   BN_CTX_start(ctx);
180   BIGNUM *x = BN_CTX_get(ctx);
181   if (x == NULL || !BN_bin2bn(buf + 1, field_len, x)) {
182     goto err;
183   }
184   if (BN_ucmp(x, &group->field) >= 0) {
185     OPENSSL_PUT_ERROR(EC, EC_R_INVALID_ENCODING);
186     goto err;
187   }
188 
189   if (!EC_POINT_set_compressed_coordinates_GFp(group, point, x, y_bit, ctx)) {
190     goto err;
191   }
192 
193   ret = 1;
194 
195 err:
196   BN_CTX_end(ctx);
197   BN_CTX_free(new_ctx);
198   return ret;
199 }
200 
EC_POINT_oct2point(const EC_GROUP * group,EC_POINT * point,const uint8_t * buf,size_t len,BN_CTX * ctx)201 int EC_POINT_oct2point(const EC_GROUP *group, EC_POINT *point,
202                        const uint8_t *buf, size_t len, BN_CTX *ctx) {
203   if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
204     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
205     return 0;
206   }
207   return ec_GFp_simple_oct2point(group, point, buf, len, ctx);
208 }
209 
EC_POINT_point2oct(const EC_GROUP * group,const EC_POINT * point,point_conversion_form_t form,uint8_t * buf,size_t len,BN_CTX * ctx)210 size_t EC_POINT_point2oct(const EC_GROUP *group, const EC_POINT *point,
211                           point_conversion_form_t form, uint8_t *buf,
212                           size_t len, BN_CTX *ctx) {
213   if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
214     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
215     return 0;
216   }
217   EC_AFFINE affine;
218   if (!ec_jacobian_to_affine(group, &affine, &point->raw)) {
219     return 0;
220   }
221   return ec_point_to_bytes(group, &affine, form, buf, len);
222 }
223 
EC_POINT_set_compressed_coordinates_GFp(const EC_GROUP * group,EC_POINT * point,const BIGNUM * x,int y_bit,BN_CTX * ctx)224 int EC_POINT_set_compressed_coordinates_GFp(const EC_GROUP *group,
225                                             EC_POINT *point, const BIGNUM *x,
226                                             int y_bit, BN_CTX *ctx) {
227   if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
228     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
229     return 0;
230   }
231 
232   if (BN_is_negative(x) || BN_cmp(x, &group->field) >= 0) {
233     OPENSSL_PUT_ERROR(EC, EC_R_INVALID_COMPRESSED_POINT);
234     return 0;
235   }
236 
237   BN_CTX *new_ctx = NULL;
238   int ret = 0;
239 
240   ERR_clear_error();
241 
242   if (ctx == NULL) {
243     ctx = new_ctx = BN_CTX_new();
244     if (ctx == NULL) {
245       return 0;
246     }
247   }
248 
249   y_bit = (y_bit != 0);
250 
251   BN_CTX_start(ctx);
252   BIGNUM *tmp1 = BN_CTX_get(ctx);
253   BIGNUM *tmp2 = BN_CTX_get(ctx);
254   BIGNUM *a = BN_CTX_get(ctx);
255   BIGNUM *b = BN_CTX_get(ctx);
256   BIGNUM *y = BN_CTX_get(ctx);
257   if (y == NULL ||
258       !EC_GROUP_get_curve_GFp(group, NULL, a, b, ctx)) {
259     goto err;
260   }
261 
262   // Recover y.  We have a Weierstrass equation
263   //     y^2 = x^3 + a*x + b,
264   // so  y  is one of the square roots of  x^3 + a*x + b.
265 
266   // tmp1 := x^3
267   if (!BN_mod_sqr(tmp2, x, &group->field, ctx) ||
268       !BN_mod_mul(tmp1, tmp2, x, &group->field, ctx)) {
269     goto err;
270   }
271 
272   // tmp1 := tmp1 + a*x
273   if (group->a_is_minus3) {
274     if (!bn_mod_lshift1_consttime(tmp2, x, &group->field, ctx) ||
275         !bn_mod_add_consttime(tmp2, tmp2, x, &group->field, ctx) ||
276         !bn_mod_sub_consttime(tmp1, tmp1, tmp2, &group->field, ctx)) {
277       goto err;
278     }
279   } else {
280     if (!BN_mod_mul(tmp2, a, x, &group->field, ctx) ||
281         !bn_mod_add_consttime(tmp1, tmp1, tmp2, &group->field, ctx)) {
282       goto err;
283     }
284   }
285 
286   // tmp1 := tmp1 + b
287   if (!bn_mod_add_consttime(tmp1, tmp1, b, &group->field, ctx)) {
288     goto err;
289   }
290 
291   if (!BN_mod_sqrt(y, tmp1, &group->field, ctx)) {
292     unsigned long err = ERR_peek_last_error();
293 
294     if (ERR_GET_LIB(err) == ERR_LIB_BN &&
295         ERR_GET_REASON(err) == BN_R_NOT_A_SQUARE) {
296       ERR_clear_error();
297       OPENSSL_PUT_ERROR(EC, EC_R_INVALID_COMPRESSED_POINT);
298     } else {
299       OPENSSL_PUT_ERROR(EC, ERR_R_BN_LIB);
300     }
301     goto err;
302   }
303 
304   if (y_bit != BN_is_odd(y)) {
305     if (BN_is_zero(y)) {
306       OPENSSL_PUT_ERROR(EC, EC_R_INVALID_COMPRESSION_BIT);
307       goto err;
308     }
309     if (!BN_usub(y, &group->field, y)) {
310       goto err;
311     }
312   }
313   if (y_bit != BN_is_odd(y)) {
314     OPENSSL_PUT_ERROR(EC, ERR_R_INTERNAL_ERROR);
315     goto err;
316   }
317 
318   if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) {
319     goto err;
320   }
321 
322   ret = 1;
323 
324 err:
325   BN_CTX_end(ctx);
326   BN_CTX_free(new_ctx);
327   return ret;
328 }
329