]>
git.proxmox.com Git - mirror_edk2.git/blob - CryptoPkg/Library/BaseCryptLib/Pk/CryptEc.c
2 Elliptic Curve and ECDH API implementation based on OpenSSL
4 Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include "InternalCryptLib.h"
10 #include <openssl/objects.h>
11 #include <openssl/bn.h>
12 #include <openssl/ec.h>
14 // =====================================================================================
15 // Basic Elliptic Curve Primitives
16 // =====================================================================================
19 Return the Nid of certain ECC curve.
21 @param[in] CryptoNid Identifying number for the ECC curve (Defined in
24 @retval !=-1 On success.
25 @retval -1 ECC curve not supported.
29 CryptoNidToOpensslNid (
36 case CRYPTO_NID_SECP256R1
:
37 Nid
= NID_X9_62_prime256v1
;
39 case CRYPTO_NID_SECP384R1
:
42 case CRYPTO_NID_SECP521R1
:
53 Initialize new opaque EcGroup object. This object represents an EC curve and
54 and is used for calculation within this group. This object should be freed
55 using EcGroupFree() function.
57 @param[in] CryptoNid Identifying number for the ECC curve (Defined in
60 @retval EcGroup object On success.
61 @retval NULL On failure.
71 Nid
= CryptoNidToOpensslNid (CryptoNid
);
77 return EC_GROUP_new_by_curve_name (Nid
);
81 Get EC curve parameters. While elliptic curve equation is Y^2 mod P = (X^3 + AX + B) Mod P.
82 This function will set the provided Big Number objects to the corresponding
83 values. The caller needs to make sure all the "out" BigNumber parameters
84 are properly initialized.
86 @param[in] EcGroup EC group object.
87 @param[out] BnPrime Group prime number.
88 @param[out] BnA A coefficient.
89 @param[out] BnB B coefficient..
90 @param[in] BnCtx BN context.
92 @retval TRUE On success.
93 @retval FALSE Otherwise.
98 IN CONST VOID
*EcGroup
,
105 return (BOOLEAN
)EC_GROUP_get_curve (EcGroup
, BnPrime
, BnA
, BnB
, BnCtx
);
110 This function will set the provided Big Number object to the corresponding
111 value. The caller needs to make sure that the "out" BigNumber parameter
112 is properly initialized.
114 @param[in] EcGroup EC group object.
115 @param[out] BnOrder Group prime number.
117 @retval TRUE On success.
118 @retval FALSE Otherwise.
127 return (BOOLEAN
)EC_GROUP_get_order (EcGroup
, BnOrder
, NULL
);
131 Free previously allocated EC group object using EcGroupInit().
133 @param[in] EcGroup EC group object to free.
141 EC_GROUP_free (EcGroup
);
145 Initialize new opaque EC Point object. This object represents an EC point
146 within the given EC group (curve).
148 @param[in] EC Group, properly initialized using EcGroupInit().
150 @retval EC Point object On success.
151 @retval NULL On failure.
156 IN CONST VOID
*EcGroup
159 return EC_POINT_new (EcGroup
);
163 Free previously allocated EC Point object using EcPointInit().
165 @param[in] EcPoint EC Point to free.
166 @param[in] Clear TRUE iff the memory should be cleared.
176 EC_POINT_clear_free (EcPoint
);
178 EC_POINT_free (EcPoint
);
183 Get EC point affine (x,y) coordinates.
184 This function will set the provided Big Number objects to the corresponding
185 values. The caller needs to make sure all the "out" BigNumber parameters
186 are properly initialized.
188 @param[in] EcGroup EC group object.
189 @param[in] EcPoint EC point object.
190 @param[out] BnX X coordinate.
191 @param[out] BnY Y coordinate.
192 @param[in] BnCtx BN context, created with BigNumNewContext().
194 @retval TRUE On success.
195 @retval FALSE Otherwise.
199 EcPointGetAffineCoordinates (
200 IN CONST VOID
*EcGroup
,
201 IN CONST VOID
*EcPoint
,
207 return (BOOLEAN
)EC_POINT_get_affine_coordinates (EcGroup
, EcPoint
, BnX
, BnY
, BnCtx
);
211 Set EC point affine (x,y) coordinates.
213 @param[in] EcGroup EC group object.
214 @param[in] EcPoint EC point object.
215 @param[in] BnX X coordinate.
216 @param[in] BnY Y coordinate.
217 @param[in] BnCtx BN context, created with BigNumNewContext().
219 @retval TRUE On success.
220 @retval FALSE Otherwise.
224 EcPointSetAffineCoordinates (
225 IN CONST VOID
*EcGroup
,
232 return (BOOLEAN
)EC_POINT_set_affine_coordinates (EcGroup
, EcPoint
, BnX
, BnY
, BnCtx
);
236 EC Point addition. EcPointResult = EcPointA + EcPointB.
238 @param[in] EcGroup EC group object.
239 @param[out] EcPointResult EC point to hold the result. The point should
240 be properly initialized.
241 @param[in] EcPointA EC Point.
242 @param[in] EcPointB EC Point.
243 @param[in] BnCtx BN context, created with BigNumNewContext().
245 @retval TRUE On success.
246 @retval FALSE Otherwise.
251 IN CONST VOID
*EcGroup
,
252 OUT VOID
*EcPointResult
,
253 IN CONST VOID
*EcPointA
,
254 IN CONST VOID
*EcPointB
,
258 return (BOOLEAN
)EC_POINT_add (EcGroup
, EcPointResult
, EcPointA
, EcPointB
, BnCtx
);
262 Variable EC point multiplication. EcPointResult = EcPoint * BnPScalar.
264 @param[in] EcGroup EC group object.
265 @param[out] EcPointResult EC point to hold the result. The point should
266 be properly initialized.
267 @param[in] EcPoint EC Point.
268 @param[in] BnPScalar P Scalar.
269 @param[in] BnCtx BN context, created with BigNumNewContext().
271 @retval TRUE On success.
272 @retval FALSE Otherwise.
277 IN CONST VOID
*EcGroup
,
278 OUT VOID
*EcPointResult
,
279 IN CONST VOID
*EcPoint
,
280 IN CONST VOID
*BnPScalar
,
284 return (BOOLEAN
)EC_POINT_mul (EcGroup
, EcPointResult
, NULL
, EcPoint
, BnPScalar
, BnCtx
);
288 Calculate the inverse of the supplied EC point.
290 @param[in] EcGroup EC group object.
291 @param[in,out] EcPoint EC point to invert.
292 @param[in] BnCtx BN context, created with BigNumNewContext().
294 @retval TRUE On success.
295 @retval FALSE Otherwise.
300 IN CONST VOID
*EcGroup
,
301 IN OUT VOID
*EcPoint
,
305 return (BOOLEAN
)EC_POINT_invert (EcGroup
, EcPoint
, BnCtx
);
309 Check if the supplied point is on EC curve.
311 @param[in] EcGroup EC group object.
312 @param[in] EcPoint EC point to check.
313 @param[in] BnCtx BN context, created with BigNumNewContext().
315 @retval TRUE On curve.
316 @retval FALSE Otherwise.
321 IN CONST VOID
*EcGroup
,
322 IN CONST VOID
*EcPoint
,
326 return EC_POINT_is_on_curve (EcGroup
, EcPoint
, BnCtx
) == 1;
330 Check if the supplied point is at infinity.
332 @param[in] EcGroup EC group object.
333 @param[in] EcPoint EC point to check.
335 @retval TRUE At infinity.
336 @retval FALSE Otherwise.
340 EcPointIsAtInfinity (
341 IN CONST VOID
*EcGroup
,
342 IN CONST VOID
*EcPoint
345 return EC_POINT_is_at_infinity (EcGroup
, EcPoint
) == 1;
349 Check if EC points are equal.
351 @param[in] EcGroup EC group object.
352 @param[in] EcPointA EC point A.
353 @param[in] EcPointB EC point B.
354 @param[in] BnCtx BN context, created with BigNumNewContext().
357 @retval FALSE Otherwise.
362 IN CONST VOID
*EcGroup
,
363 IN CONST VOID
*EcPointA
,
364 IN CONST VOID
*EcPointB
,
368 return EC_POINT_cmp (EcGroup
, EcPointA
, EcPointB
, BnCtx
) == 0;
372 Set EC point compressed coordinates. Points can be described in terms of
373 their compressed coordinates. For a point (x, y), for any given value for x
374 such that the point is on the curve there will only ever be two possible
375 values for y. Therefore, a point can be set using this function where BnX is
376 the x coordinate and YBit is a value 0 or 1 to identify which of the two
377 possible values for y should be used.
379 @param[in] EcGroup EC group object.
380 @param[in] EcPoint EC Point.
381 @param[in] BnX X coordinate.
382 @param[in] YBit 0 or 1 to identify which Y value is used.
383 @param[in] BnCtx BN context, created with BigNumNewContext().
385 @retval TRUE On success.
386 @retval FALSE Otherwise.
390 EcPointSetCompressedCoordinates (
391 IN CONST VOID
*EcGroup
,
398 return (BOOLEAN
)EC_POINT_set_compressed_coordinates (EcGroup
, EcPoint
, BnX
, YBit
, BnCtx
);
401 // =====================================================================================
402 // Elliptic Curve Diffie Hellman Primitives
403 // =====================================================================================
406 Allocates and Initializes one Elliptic Curve Context for subsequent use
409 @param[in] Nid Identifying number for the ECC curve (Defined in
411 @return Pointer to the Elliptic Curve Context that has been initialized.
412 If the allocations fails, EcNewByNid() returns NULL.
422 OpenSslNid
= CryptoNidToOpensslNid (Nid
);
423 if (OpenSslNid
< 0) {
427 return (VOID
*)EC_KEY_new_by_curve_name (OpenSslNid
);
431 Release the specified EC context.
433 @param[in] EcContext Pointer to the EC context to be released.
441 EC_KEY_free ((EC_KEY
*)EcContext
);
445 Generates EC key and returns EC public key (X, Y), Please note, this function uses
446 pseudo random number generator. The caller must make sure RandomSeed()
447 function was properly called before.
448 The Ec context should be correctly initialized by EcNewByNid.
449 This function generates random secret, and computes the public key (X, Y), which is
450 returned via parameter Public, PublicSize.
451 X is the first half of Public with size being PublicSize / 2,
452 Y is the second half of Public with size being PublicSize / 2.
453 EC context is updated accordingly.
454 If the Public buffer is too small to hold the public X, Y, FALSE is returned and
455 PublicSize is set to the required buffer size to obtain the public X, Y.
456 For P-256, the PublicSize is 64. First 32-byte is X, Second 32-byte is Y.
457 For P-384, the PublicSize is 96. First 48-byte is X, Second 48-byte is Y.
458 For P-521, the PublicSize is 132. First 66-byte is X, Second 66-byte is Y.
459 If EcContext is NULL, then return FALSE.
460 If PublicSize is NULL, then return FALSE.
461 If PublicSize is large enough but Public is NULL, then return FALSE.
462 @param[in, out] EcContext Pointer to the EC context.
463 @param[out] PublicKey Pointer to t buffer to receive generated public X,Y.
464 @param[in, out] PublicKeySize On input, the size of Public buffer in bytes.
465 On output, the size of data returned in Public buffer in bytes.
466 @retval TRUE EC public X,Y generation succeeded.
467 @retval FALSE EC public X,Y generation failed.
468 @retval FALSE PublicKeySize is not large enough.
473 IN OUT VOID
*EcContext
,
474 OUT UINT8
*PublicKey
,
475 IN OUT UINTN
*PublicKeySize
479 CONST EC_GROUP
*Group
;
480 CONST EC_POINT
*EcPoint
;
488 if ((EcContext
== NULL
) || (PublicKeySize
== NULL
)) {
492 if ((PublicKey
== NULL
) && (*PublicKeySize
!= 0)) {
496 EcKey
= (EC_KEY
*)EcContext
;
497 Group
= EC_KEY_get0_group (EcKey
);
498 HalfSize
= (EC_GROUP_get_degree (Group
) + 7) / 8;
500 // Assume RAND_seed was called
501 if (EC_KEY_generate_key (EcKey
) != 1) {
505 if (*PublicKeySize
< HalfSize
* 2) {
506 *PublicKeySize
= HalfSize
* 2;
510 *PublicKeySize
= HalfSize
* 2;
512 EcPoint
= EC_KEY_get0_public_key (EcKey
);
513 if (EcPoint
== NULL
) {
520 if ((BnX
== NULL
) || (BnY
== NULL
)) {
524 if (EC_POINT_get_affine_coordinates (Group
, EcPoint
, BnX
, BnY
, NULL
) != 1) {
528 XSize
= BN_num_bytes (BnX
);
529 YSize
= BN_num_bytes (BnY
);
530 if ((XSize
<= 0) || (YSize
<= 0)) {
534 ASSERT ((UINTN
)XSize
<= HalfSize
&& (UINTN
)YSize
<= HalfSize
);
536 ZeroMem (PublicKey
, *PublicKeySize
);
537 BN_bn2bin (BnX
, &PublicKey
[0 + HalfSize
- XSize
]);
538 BN_bn2bin (BnY
, &PublicKey
[HalfSize
+ HalfSize
- YSize
]);
549 Gets the public key component from the established EC context.
550 The Ec context should be correctly initialized by EcNewByNid, and successfully
551 generate key pair from EcGenerateKey().
552 For P-256, the PublicSize is 64. First 32-byte is X, Second 32-byte is Y.
553 For P-384, the PublicSize is 96. First 48-byte is X, Second 48-byte is Y.
554 For P-521, the PublicSize is 132. First 66-byte is X, Second 66-byte is Y.
555 @param[in, out] EcContext Pointer to EC context being set.
556 @param[out] PublicKey Pointer to t buffer to receive generated public X,Y.
557 @param[in, out] PublicKeySize On input, the size of Public buffer in bytes.
558 On output, the size of data returned in Public buffer in bytes.
559 @retval TRUE EC key component was retrieved successfully.
560 @retval FALSE Invalid EC key component.
565 IN OUT VOID
*EcContext
,
566 OUT UINT8
*PublicKey
,
567 IN OUT UINTN
*PublicKeySize
571 CONST EC_GROUP
*Group
;
572 CONST EC_POINT
*EcPoint
;
580 if ((EcContext
== NULL
) || (PublicKeySize
== NULL
)) {
584 if ((PublicKey
== NULL
) && (*PublicKeySize
!= 0)) {
588 EcKey
= (EC_KEY
*)EcContext
;
589 Group
= EC_KEY_get0_group (EcKey
);
590 HalfSize
= (EC_GROUP_get_degree (Group
) + 7) / 8;
591 if (*PublicKeySize
< HalfSize
* 2) {
592 *PublicKeySize
= HalfSize
* 2;
596 *PublicKeySize
= HalfSize
* 2;
598 EcPoint
= EC_KEY_get0_public_key (EcKey
);
599 if (EcPoint
== NULL
) {
606 if ((BnX
== NULL
) || (BnY
== NULL
)) {
610 if (EC_POINT_get_affine_coordinates (Group
, EcPoint
, BnX
, BnY
, NULL
) != 1) {
614 XSize
= BN_num_bytes (BnX
);
615 YSize
= BN_num_bytes (BnY
);
616 if ((XSize
<= 0) || (YSize
<= 0)) {
620 ASSERT ((UINTN
)XSize
<= HalfSize
&& (UINTN
)YSize
<= HalfSize
);
622 if (PublicKey
!= NULL
) {
623 ZeroMem (PublicKey
, *PublicKeySize
);
624 BN_bn2bin (BnX
, &PublicKey
[0 + HalfSize
- XSize
]);
625 BN_bn2bin (BnY
, &PublicKey
[HalfSize
+ HalfSize
- YSize
]);
637 Computes exchanged common key.
638 Given peer's public key (X, Y), this function computes the exchanged common key,
639 based on its own context including value of curve parameter and random secret.
640 X is the first half of PeerPublic with size being PeerPublicSize / 2,
641 Y is the second half of PeerPublic with size being PeerPublicSize / 2.
642 If public key is compressed, the PeerPublic will only contain half key (X).
643 If EcContext is NULL, then return FALSE.
644 If PeerPublic is NULL, then return FALSE.
645 If PeerPublicSize is 0, then return FALSE.
646 If Key is NULL, then return FALSE.
647 If KeySize is not large enough, then return FALSE.
648 For P-256, the PeerPublicSize is 64. First 32-byte is X, Second 32-byte is Y.
649 For P-384, the PeerPublicSize is 96. First 48-byte is X, Second 48-byte is Y.
650 For P-521, the PeerPublicSize is 132. First 66-byte is X, Second 66-byte is Y.
651 @param[in, out] EcContext Pointer to the EC context.
652 @param[in] PeerPublic Pointer to the peer's public X,Y.
653 @param[in] PeerPublicSize Size of peer's public X,Y in bytes.
654 @param[in] CompressFlag Flag of PeerPublic is compressed or not.
655 @param[out] Key Pointer to the buffer to receive generated key.
656 @param[in, out] KeySize On input, the size of Key buffer in bytes.
657 On output, the size of data returned in Key buffer in bytes.
658 @retval TRUE EC exchanged key generation succeeded.
659 @retval FALSE EC exchanged key generation failed.
660 @retval FALSE KeySize is not large enough.
665 IN OUT VOID
*EcContext
,
666 IN CONST UINT8
*PeerPublic
,
667 IN UINTN PeerPublicSize
,
668 IN CONST INT32
*CompressFlag
,
670 IN OUT UINTN
*KeySize
675 CONST EC_GROUP
*Group
;
683 if ((EcContext
== NULL
) || (PeerPublic
== NULL
) || (KeySize
== NULL
)) {
687 if ((Key
== NULL
) && (*KeySize
!= 0)) {
691 if (PeerPublicSize
> INT_MAX
) {
695 EcKey
= (EC_KEY
*)EcContext
;
696 Group
= EC_KEY_get0_group (EcKey
);
697 HalfSize
= (EC_GROUP_get_degree (Group
) + 7) / 8;
698 if ((CompressFlag
== NULL
) && (PeerPublicSize
!= HalfSize
* 2)) {
702 if ((CompressFlag
!= NULL
) && (PeerPublicSize
!= HalfSize
)) {
706 if (*KeySize
< HalfSize
) {
715 BnX
= BN_bin2bn (PeerPublic
, (INT32
)HalfSize
, NULL
);
717 Point
= EC_POINT_new (Group
);
719 if ((BnX
== NULL
) || (Point
== NULL
)) {
723 if (CompressFlag
== NULL
) {
724 BnY
= BN_bin2bn (PeerPublic
+ HalfSize
, (INT32
)HalfSize
, NULL
);
729 if (EC_POINT_set_affine_coordinates (Group
, Point
, BnX
, BnY
, NULL
) != 1) {
733 if (EC_POINT_set_compressed_coordinates (Group
, Point
, BnX
, *CompressFlag
, NULL
) != 1) {
738 // Validate NIST ECDH public key
739 OpenSslNid
= EC_GROUP_get_curve_name (Group
);
740 PeerEcKey
= EC_KEY_new_by_curve_name (OpenSslNid
);
741 if (PeerEcKey
== NULL
) {
745 if (EC_KEY_set_public_key (PeerEcKey
, Point
) != 1) {
749 if (EC_KEY_check_key (PeerEcKey
) != 1) {
753 if (ECDH_compute_key (Key
, *KeySize
, Point
, EcKey
, NULL
) <= 0) {
762 EC_POINT_free (Point
);
763 EC_KEY_free (PeerEcKey
);