1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 #include "pal_ecc_import_export.h"
6 #include "pal_utilities.h"
7
MethodToCurveType(EC_METHOD * method)8 ECCurveType MethodToCurveType(EC_METHOD* method)
9 {
10 if (method == EC_GFp_mont_method())
11 return ECCurveType::PrimeMontgomery;
12
13 int fieldType = EC_METHOD_get_field_type(method);
14
15 if (fieldType == NID_X9_62_characteristic_two_field)
16 return ECCurveType::Characteristic2;
17
18 if (fieldType == NID_X9_62_prime_field)
19 return ECCurveType::PrimeShortWeierstrass;
20
21 return ECCurveType::Unspecified;
22 }
23
CurveTypeToMethod(ECCurveType curveType)24 const EC_METHOD* CurveTypeToMethod(ECCurveType curveType)
25 {
26 if (curveType == ECCurveType::PrimeShortWeierstrass)
27 return EC_GFp_simple_method();
28
29 if (curveType == ECCurveType::PrimeMontgomery)
30 return EC_GFp_mont_method();
31
32 #if HAVE_OPENSSL_EC2M
33 if (API_EXISTS(EC_GF2m_simple_method) && (curveType == ECCurveType::Characteristic2))
34 return EC_GF2m_simple_method();
35 #endif
36
37 return nullptr; //Edwards and others
38 }
39
CryptoNative_EcKeyGetCurveType(const EC_KEY * key)40 extern "C" ECCurveType CryptoNative_EcKeyGetCurveType(
41 const EC_KEY* key)
42 {
43 const EC_GROUP* group = EC_KEY_get0_group(key);
44 if (!group) return ECCurveType::Unspecified;
45
46 const EC_METHOD* method = EC_GROUP_method_of(group);
47 if (!method) return ECCurveType::Unspecified;
48
49 return MethodToCurveType(const_cast<EC_METHOD*>(method));
50 }
51
CryptoNative_GetECKeyParameters(const EC_KEY * key,int32_t includePrivate,BIGNUM ** qx,int32_t * cbQx,BIGNUM ** qy,int32_t * cbQy,BIGNUM ** d,int32_t * cbD)52 extern "C" int32_t CryptoNative_GetECKeyParameters(
53 const EC_KEY* key,
54 int32_t includePrivate,
55 BIGNUM** qx, int32_t* cbQx,
56 BIGNUM** qy, int32_t* cbQy,
57 BIGNUM** d, int32_t* cbD)
58 {
59 // Verify the out parameters. Note out parameters used to minimize pinvoke calls.
60 if (!key ||
61 !qx || !cbQx ||
62 !qy || !cbQy ||
63 (includePrivate && (!d || !cbD)))
64 {
65 assert(false);
66
67 // Since these parameters are 'out' parameters in managed code, ensure they are initialized
68 if (qx) *qx = nullptr; if (cbQx) *cbQx = 0;
69 if (qy) *qy = nullptr; if (cbQy) *cbQy = 0;
70 if (d) *d = nullptr; if (cbD) *cbD = 0;
71
72 return 0;
73 }
74
75 // Get the public key and curve
76 int rc = 0;
77 BIGNUM *xBn = nullptr;
78 BIGNUM *yBn = nullptr;
79
80 ECCurveType curveType = CryptoNative_EcKeyGetCurveType(key);
81 const EC_POINT* Q = EC_KEY_get0_public_key(key);
82 const EC_GROUP* group = EC_KEY_get0_group(key);
83 if (curveType == ECCurveType::Unspecified || !Q || !group)
84 goto error;
85
86 // Extract qx and qy
87 xBn = BN_new();
88 yBn = BN_new();
89 if (!xBn || !yBn)
90 goto error;
91
92 #if HAVE_OPENSSL_EC2M
93 if (API_EXISTS(EC_POINT_get_affine_coordinates_GF2m) && (curveType == ECCurveType::Characteristic2))
94 {
95 if (!EC_POINT_get_affine_coordinates_GF2m(group, Q, xBn, yBn, nullptr))
96 goto error;
97 }
98 else
99 #endif
100 {
101 if (!EC_POINT_get_affine_coordinates_GFp(group, Q, xBn, yBn, nullptr))
102 goto error;
103 }
104
105 // Success; assign variables
106 *qx = xBn; *cbQx = BN_num_bytes(xBn);
107 *qy = yBn; *cbQy = BN_num_bytes(yBn);
108
109 if (includePrivate)
110 {
111 const BIGNUM* const_bignum_privateKey = EC_KEY_get0_private_key(key);
112 if (const_bignum_privateKey != nullptr)
113 {
114 *d = const_cast<BIGNUM*>(const_bignum_privateKey);
115 *cbD = BN_num_bytes(*d);
116 }
117 else
118 {
119 rc = -1;
120 goto error;
121 }
122 }
123 else
124 {
125 if (d)
126 *d = nullptr;
127
128 if (cbD)
129 *cbD = 0;
130 }
131
132 // success
133 return 1;
134
135 error:
136 *cbQx = *cbQy = 0;
137 *qx = *qy = 0;
138 if (d) *d = nullptr;
139 if (cbD) *cbD = 0;
140 if (xBn) BN_free(xBn);
141 if (yBn) BN_free(yBn);
142 return rc;
143 }
144
CryptoNative_GetECCurveParameters(const EC_KEY * key,int32_t includePrivate,ECCurveType * curveType,BIGNUM ** qx,int32_t * cbQx,BIGNUM ** qy,int32_t * cbQy,BIGNUM ** d,int32_t * cbD,BIGNUM ** p,int32_t * cbP,BIGNUM ** a,int32_t * cbA,BIGNUM ** b,int32_t * cbB,BIGNUM ** gx,int32_t * cbGx,BIGNUM ** gy,int32_t * cbGy,BIGNUM ** order,int32_t * cbOrder,BIGNUM ** cofactor,int32_t * cbCofactor,BIGNUM ** seed,int32_t * cbSeed)145 extern "C" int32_t CryptoNative_GetECCurveParameters(
146 const EC_KEY* key,
147 int32_t includePrivate,
148 ECCurveType* curveType,
149 BIGNUM** qx, int32_t* cbQx,
150 BIGNUM** qy, int32_t* cbQy,
151 BIGNUM** d, int32_t* cbD,
152 BIGNUM** p, int32_t* cbP,
153 BIGNUM** a, int32_t* cbA,
154 BIGNUM** b, int32_t* cbB,
155 BIGNUM** gx, int32_t* cbGx,
156 BIGNUM** gy, int32_t* cbGy,
157 BIGNUM** order, int32_t* cbOrder,
158 BIGNUM** cofactor, int32_t* cbCofactor,
159 BIGNUM** seed, int32_t* cbSeed)
160 {
161 // Get the public key parameters first in case any of its 'out' parameters are not initialized
162 int32_t rc = CryptoNative_GetECKeyParameters(key, includePrivate, qx, cbQx, qy, cbQy, d, cbD);
163
164 // Verify the out parameters. Note out parameters used to minimize pinvoke calls.
165 if (!p || !cbP ||
166 !a || !cbA ||
167 !b || !cbB ||
168 !gx || !cbGx ||
169 !gy || !cbGy ||
170 !order || !cbOrder ||
171 !cofactor || !cbCofactor ||
172 !seed || !cbSeed)
173 {
174 assert(false);
175
176 // Since these parameters are 'out' parameters in managed code, ensure they are initialized
177 if (p) *p = nullptr; if (cbP) *cbP = 0;
178 if (a) *a = nullptr; if (cbA) *cbA = 0;
179 if (b) *b = nullptr; if (cbB) *cbB = 0;
180 if (gx) *gx = nullptr; if (cbGx) *cbGx = 0;
181 if (gy) *gy = nullptr; if (cbGy) *cbGy = 0;
182 if (order) *order = nullptr; if (cbOrder) *cbOrder = 0;
183 if (cofactor) *cofactor = nullptr; if (cbCofactor) *cbCofactor = 0;
184 if (seed) *seed = nullptr; if (cbSeed) *cbSeed = 0;
185
186 return 0;
187 }
188
189 EC_GROUP* group = nullptr;
190 EC_POINT* G = nullptr;
191 EC_METHOD* curveMethod = nullptr;
192 BIGNUM* xBn = nullptr;
193 BIGNUM* yBn = nullptr;
194 BIGNUM* pBn = nullptr;
195 BIGNUM* aBn = nullptr;
196 BIGNUM* bBn = nullptr;
197 BIGNUM* orderBn = nullptr;
198 BIGNUM* cofactorBn = nullptr;
199 BIGNUM* seedBn = nullptr;
200
201 // Exit if CryptoNative_GetECKeyParameters failed
202 if (rc != 1)
203 goto error;
204
205 xBn = BN_new();
206 yBn = BN_new();
207 pBn = BN_new();
208 aBn = BN_new();
209 bBn = BN_new();
210 orderBn = BN_new();
211 cofactorBn = BN_new();
212
213 if (!xBn || !yBn || !pBn || !aBn || !bBn || !orderBn || !cofactorBn)
214 goto error;
215
216 group = const_cast<EC_GROUP*>(EC_KEY_get0_group(key)); // curve
217 if (!group)
218 goto error;
219
220 curveMethod = const_cast<EC_METHOD*>(EC_GROUP_method_of(group));
221 if (!curveMethod)
222 goto error;
223
224 *curveType = MethodToCurveType(curveMethod);
225 if (*curveType == ECCurveType::Unspecified)
226 goto error;
227
228 // Extract p, a, b
229 #if HAVE_OPENSSL_EC2M
230 if (API_EXISTS(EC_GROUP_get_curve_GF2m) && (*curveType == ECCurveType::Characteristic2))
231 {
232 // pBn represents the binary polynomial
233 if (!EC_GROUP_get_curve_GF2m(group, pBn, aBn, bBn, nullptr))
234 goto error;
235 }
236 else
237 #endif
238 {
239 // pBn represents the prime
240 if (!EC_GROUP_get_curve_GFp(group, pBn, aBn, bBn, nullptr))
241 goto error;
242 }
243
244 // Extract gx and gy
245 G = const_cast<EC_POINT*>(EC_GROUP_get0_generator(group));
246 #if HAVE_OPENSSL_EC2M
247 if (API_EXISTS(EC_POINT_get_affine_coordinates_GF2m) && (*curveType == ECCurveType::Characteristic2))
248 {
249 if (!EC_POINT_get_affine_coordinates_GF2m(group, G, xBn, yBn, NULL))
250 goto error;
251 }
252 else
253 #endif
254 {
255 if (!EC_POINT_get_affine_coordinates_GFp(group, G, xBn, yBn, NULL))
256 goto error;
257 }
258
259 // Extract order (n)
260 if (!EC_GROUP_get_order(group, orderBn, nullptr))
261 goto error;
262
263 // Extract cofactor (h)
264 if (!EC_GROUP_get_cofactor(group, cofactorBn, nullptr))
265 goto error;
266
267 // Extract seed (optional)
268 if (EC_GROUP_get0_seed(group))
269 {
270 seedBn = BN_bin2bn(EC_GROUP_get0_seed(group),
271 static_cast<int>(EC_GROUP_get_seed_len(group)), NULL);
272
273 *seed = seedBn;
274 *cbSeed = BN_num_bytes(seedBn);
275
276 /*
277 To implement SEC 1 standard and align to Windows, we also want to extract the nid
278 to the algorithm (e.g. NID_sha256) that was used to generate seed but this
279 metadata does not appear to exist in openssl (see openssl's ec_curve.c) so we may
280 eventually want to add that metadata, but that could be done on the managed side.
281 */
282 }
283 else
284 {
285 *seed = nullptr;
286 *cbSeed = 0;
287 }
288
289 // Success; assign variables
290 *gx = xBn; *cbGx = BN_num_bytes(xBn);
291 *gy = yBn; *cbGy = BN_num_bytes(yBn);
292 *p = pBn; *cbP = BN_num_bytes(pBn);
293 *a = aBn; *cbA = BN_num_bytes(aBn);
294 *b = bBn; *cbB = BN_num_bytes(bBn);
295 *order = orderBn; *cbOrder = BN_num_bytes(orderBn);
296 *cofactor = cofactorBn; *cbCofactor = BN_num_bytes(cofactorBn);
297
298 rc = 1;
299 goto exit;
300
301 error:
302 // Clear out variables from CryptoNative_GetECKeyParameters
303 *cbQx = *cbQy = 0;
304 *qx = *qy = nullptr;
305 if (d) *d = nullptr;
306 if (cbD) *cbD = 0;
307
308 // Clear our out variables
309 *curveType = ECCurveType::Unspecified;
310 *cbP = *cbA = *cbB = *cbGx = *cbGy = *cbOrder = *cbCofactor = *cbSeed = 0;
311 *p = *a = *b = *gx = *gy = *order = *cofactor = *seed = nullptr;
312
313 if (xBn) BN_free(xBn);
314 if (yBn) BN_free(yBn);
315 if (pBn) BN_free(pBn);
316 if (aBn) BN_free(aBn);
317 if (bBn) BN_free(bBn);
318 if (orderBn) BN_free(orderBn);
319 if (cofactorBn) BN_free(cofactorBn);
320 if (seedBn) BN_free(seedBn);
321
322 exit:
323 return rc;
324 }
325
CryptoNative_EcKeyCreateByKeyParameters(EC_KEY ** key,const char * oid,uint8_t * qx,int32_t qxLength,uint8_t * qy,int32_t qyLength,uint8_t * d,int32_t dLength)326 extern "C" int32_t CryptoNative_EcKeyCreateByKeyParameters(EC_KEY** key, const char* oid, uint8_t* qx, int32_t qxLength, uint8_t* qy, int32_t qyLength, uint8_t* d, int32_t dLength)
327 {
328 if (!key || !oid)
329 {
330 assert(false);
331 return 0;
332 }
333
334 *key = nullptr;
335
336 // oid can be friendly name or value
337 int nid = OBJ_txt2nid(oid);
338 if (!nid)
339 return -1;
340
341 *key = EC_KEY_new_by_curve_name(nid);
342 if (!(*key))
343 return -1;
344
345 BIGNUM* dBn = nullptr;
346 BIGNUM* qxBn = nullptr;
347 BIGNUM* qyBn = nullptr;
348
349 // If key values specified, use them, otherwise a key will be generated later
350 if (qx && qy)
351 {
352 qxBn = BN_bin2bn(qx, qxLength, nullptr);
353 qyBn = BN_bin2bn(qy, qyLength, nullptr);
354 if (!qxBn || !qyBn)
355 goto error;
356
357 if (!EC_KEY_set_public_key_affine_coordinates(*key, qxBn, qyBn))
358 goto error;
359
360 // Set private key (optional)
361 if (d && dLength > 0)
362 {
363 dBn = BN_bin2bn(d, dLength, nullptr);
364 if (!dBn)
365 goto error;
366
367 if (!EC_KEY_set_private_key(*key, dBn))
368 goto error;
369 }
370
371 // Validate key
372 if (!EC_KEY_check_key(*key))
373 goto error;
374 }
375
376 // Success
377 return 1;
378
379 error:
380 if (qxBn) BN_free(qxBn);
381 if (qyBn) BN_free(qyBn);
382 if (dBn) BN_free(dBn);
383 if (*key)
384 {
385 EC_KEY_free(*key);
386 *key = nullptr;
387 }
388 return 0;
389 }
390
CryptoNative_EcKeyCreateByExplicitParameters(ECCurveType curveType,uint8_t * qx,int32_t qxLength,uint8_t * qy,int32_t qyLength,uint8_t * d,int32_t dLength,uint8_t * p,int32_t pLength,uint8_t * a,int32_t aLength,uint8_t * b,int32_t bLength,uint8_t * gx,int32_t gxLength,uint8_t * gy,int32_t gyLength,uint8_t * order,int32_t orderLength,uint8_t * cofactor,int32_t cofactorLength,uint8_t * seed,int32_t seedLength)391 extern "C" EC_KEY* CryptoNative_EcKeyCreateByExplicitParameters(
392 ECCurveType curveType,
393 uint8_t* qx, int32_t qxLength,
394 uint8_t* qy, int32_t qyLength,
395 uint8_t* d, int32_t dLength,
396 uint8_t* p, int32_t pLength,
397 uint8_t* a, int32_t aLength,
398 uint8_t* b, int32_t bLength,
399 uint8_t* gx, int32_t gxLength,
400 uint8_t* gy, int32_t gyLength,
401 uint8_t* order, int32_t orderLength,
402 uint8_t* cofactor, int32_t cofactorLength,
403 uint8_t* seed, int32_t seedLength)
404 {
405 if (!p || !a || !b || !gx || !gy || !order || !cofactor)
406 {
407 // qx, qy, d and seed are optional
408 assert(false);
409 return 0;
410 }
411
412 EC_KEY* key = nullptr;
413 EC_POINT* G = nullptr;
414
415 BIGNUM* qxBn = nullptr;
416 BIGNUM* qyBn = nullptr;
417 BIGNUM* dBn = nullptr;
418 BIGNUM* pBn = nullptr; // p = either the char2 polynomial or the prime
419 BIGNUM* aBn = nullptr;
420 BIGNUM* bBn = nullptr;
421 BIGNUM* gxBn = nullptr;
422 BIGNUM* gyBn = nullptr;
423 BIGNUM* orderBn = nullptr;
424 BIGNUM* cofactorBn = nullptr;
425
426 // Create the group. Explicitly specify the curve type because using EC_GROUP_new_curve_GFp
427 // will default to montgomery curve
428 const EC_METHOD* curveMethod = CurveTypeToMethod(curveType);
429 if (!curveMethod) return nullptr;
430
431 EC_GROUP* group = EC_GROUP_new(curveMethod);
432 if (!group) return nullptr;
433
434 pBn = BN_bin2bn(p, pLength, nullptr);
435 // At this point we should use 'goto error' since we allocated memory
436 aBn = BN_bin2bn(a, aLength, nullptr);
437 bBn = BN_bin2bn(b, bLength, nullptr);
438
439 #if HAVE_OPENSSL_EC2M
440 if (API_EXISTS(EC_GROUP_set_curve_GF2m) && (curveType == ECCurveType::Characteristic2))
441 {
442 if (!EC_GROUP_set_curve_GF2m(group, pBn, aBn, bBn, nullptr))
443 goto error;
444 }
445 else
446 #endif
447 {
448 if (!EC_GROUP_set_curve_GFp(group, pBn, aBn, bBn, nullptr))
449 goto error;
450 }
451
452 // Set generator, order and cofactor
453 G = EC_POINT_new(group);
454 gxBn = BN_bin2bn(gx, gxLength, nullptr);
455 gyBn = BN_bin2bn(gy, gyLength, nullptr);
456
457 #if HAVE_OPENSSL_EC2M
458 if (API_EXISTS(EC_POINT_set_affine_coordinates_GF2m) && (curveType == ECCurveType::Characteristic2))
459 {
460 EC_POINT_set_affine_coordinates_GF2m(group, G, gxBn, gyBn, nullptr);
461 }
462 else
463 #endif
464 {
465 EC_POINT_set_affine_coordinates_GFp(group, G, gxBn, gyBn, nullptr);
466 }
467
468 orderBn = BN_bin2bn(order, orderLength, nullptr);
469 cofactorBn = BN_bin2bn(cofactor, cofactorLength, nullptr);
470 EC_GROUP_set_generator(group, G, orderBn, cofactorBn);
471
472 // Set seed (optional)
473 if (seed && seedLength > 0)
474 {
475 if (!EC_GROUP_set_seed(group, seed, static_cast<size_t>(seedLength)))
476 goto error;
477 }
478
479 // Validate group
480 if (!EC_GROUP_check(group, nullptr))
481 goto error;
482
483 // Create key
484 key = EC_KEY_new();
485 if (!key)
486 goto error;
487
488 if (!EC_KEY_set_group(key, group))
489 goto error;
490
491 // Set the public and private key values
492 if (qx && qy)
493 {
494 qxBn = BN_bin2bn(qx, qxLength, nullptr);
495 qyBn = BN_bin2bn(qy, qyLength, nullptr);
496 if (!qxBn || !qyBn)
497 goto error;
498
499 if (!EC_KEY_set_public_key_affine_coordinates(key, qxBn, qyBn))
500 goto error;
501
502 // Set private key (optional)
503 if (d && dLength)
504 {
505 dBn = BN_bin2bn(d, dLength, nullptr);
506 if (!dBn)
507 goto error;
508
509 if (!EC_KEY_set_private_key(key, dBn))
510 goto error;
511 }
512
513 // Validate key
514 if (!EC_KEY_check_key(key))
515 goto error;
516 }
517
518 // Success
519 return key;
520
521 error:
522 if (qxBn) BN_free(qxBn);
523 if (qyBn) BN_free(qyBn);
524 if (dBn) BN_free(dBn);
525 if (pBn) BN_free(pBn);
526 if (aBn) BN_free(aBn);
527 if (bBn) BN_free(bBn);
528 if (gxBn) BN_free(gxBn);
529 if (gyBn) BN_free(gyBn);
530 if (orderBn) BN_free(orderBn);
531 if (cofactorBn) BN_free(cofactorBn);
532 if (G) EC_POINT_free(G);
533 if (group) EC_GROUP_free(group);
534 if (key) EC_KEY_free(key);
535 return nullptr;
536 }
537