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