/** @file | |
Elliptic Curve and ECDH API implementation based on OpenSSL | |
Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "InternalCryptLib.h" | |
#include <openssl/objects.h> | |
#include <openssl/bn.h> | |
#include <openssl/ec.h> | |
// ===================================================================================== | |
// Basic Elliptic Curve Primitives | |
// ===================================================================================== | |
/** | |
Return the Nid of certain ECC curve. | |
@param[in] CryptoNid Identifying number for the ECC curve (Defined in | |
BaseCryptLib.h). | |
@retval !=-1 On success. | |
@retval -1 ECC curve not supported. | |
**/ | |
STATIC | |
INT32 | |
CryptoNidToOpensslNid ( | |
IN UINTN CryptoNid | |
) | |
{ | |
INT32 Nid; | |
switch (CryptoNid) { | |
case CRYPTO_NID_SECP256R1: | |
Nid = NID_X9_62_prime256v1; | |
break; | |
case CRYPTO_NID_SECP384R1: | |
Nid = NID_secp384r1; | |
break; | |
case CRYPTO_NID_SECP521R1: | |
Nid = NID_secp521r1; | |
break; | |
default: | |
return -1; | |
} | |
return Nid; | |
} | |
/** | |
Initialize new opaque EcGroup object. This object represents an EC curve and | |
and is used for calculation within this group. This object should be freed | |
using EcGroupFree() function. | |
@param[in] CryptoNid Identifying number for the ECC curve (Defined in | |
BaseCryptLib.h). | |
@retval EcGroup object On success. | |
@retval NULL On failure. | |
**/ | |
VOID * | |
EFIAPI | |
EcGroupInit ( | |
IN UINTN CryptoNid | |
) | |
{ | |
INT32 Nid; | |
Nid = CryptoNidToOpensslNid (CryptoNid); | |
if (Nid < 0) { | |
return NULL; | |
} | |
return EC_GROUP_new_by_curve_name (Nid); | |
} | |
/** | |
Get EC curve parameters. While elliptic curve equation is Y^2 mod P = (X^3 + AX + B) Mod P. | |
This function will set the provided Big Number objects to the corresponding | |
values. The caller needs to make sure all the "out" BigNumber parameters | |
are properly initialized. | |
@param[in] EcGroup EC group object. | |
@param[out] BnPrime Group prime number. | |
@param[out] BnA A coefficient. | |
@param[out] BnB B coefficient.. | |
@param[in] BnCtx BN context. | |
@retval TRUE On success. | |
@retval FALSE Otherwise. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EcGroupGetCurve ( | |
IN CONST VOID *EcGroup, | |
OUT VOID *BnPrime, | |
OUT VOID *BnA, | |
OUT VOID *BnB, | |
IN VOID *BnCtx | |
) | |
{ | |
return (BOOLEAN)EC_GROUP_get_curve (EcGroup, BnPrime, BnA, BnB, BnCtx); | |
} | |
/** | |
Get EC group order. | |
This function will set the provided Big Number object to the corresponding | |
value. The caller needs to make sure that the "out" BigNumber parameter | |
is properly initialized. | |
@param[in] EcGroup EC group object. | |
@param[out] BnOrder Group prime number. | |
@retval TRUE On success. | |
@retval FALSE Otherwise. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EcGroupGetOrder ( | |
IN VOID *EcGroup, | |
OUT VOID *BnOrder | |
) | |
{ | |
return (BOOLEAN)EC_GROUP_get_order (EcGroup, BnOrder, NULL); | |
} | |
/** | |
Free previously allocated EC group object using EcGroupInit(). | |
@param[in] EcGroup EC group object to free. | |
**/ | |
VOID | |
EFIAPI | |
EcGroupFree ( | |
IN VOID *EcGroup | |
) | |
{ | |
EC_GROUP_free (EcGroup); | |
} | |
/** | |
Initialize new opaque EC Point object. This object represents an EC point | |
within the given EC group (curve). | |
@param[in] EC Group, properly initialized using EcGroupInit(). | |
@retval EC Point object On success. | |
@retval NULL On failure. | |
**/ | |
VOID * | |
EFIAPI | |
EcPointInit ( | |
IN CONST VOID *EcGroup | |
) | |
{ | |
return EC_POINT_new (EcGroup); | |
} | |
/** | |
Free previously allocated EC Point object using EcPointInit(). | |
@param[in] EcPoint EC Point to free. | |
@param[in] Clear TRUE iff the memory should be cleared. | |
**/ | |
VOID | |
EFIAPI | |
EcPointDeInit ( | |
IN VOID *EcPoint, | |
IN BOOLEAN Clear | |
) | |
{ | |
if (Clear) { | |
EC_POINT_clear_free (EcPoint); | |
} else { | |
EC_POINT_free (EcPoint); | |
} | |
} | |
/** | |
Get EC point affine (x,y) coordinates. | |
This function will set the provided Big Number objects to the corresponding | |
values. The caller needs to make sure all the "out" BigNumber parameters | |
are properly initialized. | |
@param[in] EcGroup EC group object. | |
@param[in] EcPoint EC point object. | |
@param[out] BnX X coordinate. | |
@param[out] BnY Y coordinate. | |
@param[in] BnCtx BN context, created with BigNumNewContext(). | |
@retval TRUE On success. | |
@retval FALSE Otherwise. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EcPointGetAffineCoordinates ( | |
IN CONST VOID *EcGroup, | |
IN CONST VOID *EcPoint, | |
OUT VOID *BnX, | |
OUT VOID *BnY, | |
IN VOID *BnCtx | |
) | |
{ | |
return (BOOLEAN)EC_POINT_get_affine_coordinates (EcGroup, EcPoint, BnX, BnY, BnCtx); | |
} | |
/** | |
Set EC point affine (x,y) coordinates. | |
@param[in] EcGroup EC group object. | |
@param[in] EcPoint EC point object. | |
@param[in] BnX X coordinate. | |
@param[in] BnY Y coordinate. | |
@param[in] BnCtx BN context, created with BigNumNewContext(). | |
@retval TRUE On success. | |
@retval FALSE Otherwise. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EcPointSetAffineCoordinates ( | |
IN CONST VOID *EcGroup, | |
IN VOID *EcPoint, | |
IN CONST VOID *BnX, | |
IN CONST VOID *BnY, | |
IN VOID *BnCtx | |
) | |
{ | |
return (BOOLEAN)EC_POINT_set_affine_coordinates (EcGroup, EcPoint, BnX, BnY, BnCtx); | |
} | |
/** | |
EC Point addition. EcPointResult = EcPointA + EcPointB. | |
@param[in] EcGroup EC group object. | |
@param[out] EcPointResult EC point to hold the result. The point should | |
be properly initialized. | |
@param[in] EcPointA EC Point. | |
@param[in] EcPointB EC Point. | |
@param[in] BnCtx BN context, created with BigNumNewContext(). | |
@retval TRUE On success. | |
@retval FALSE Otherwise. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EcPointAdd ( | |
IN CONST VOID *EcGroup, | |
OUT VOID *EcPointResult, | |
IN CONST VOID *EcPointA, | |
IN CONST VOID *EcPointB, | |
IN VOID *BnCtx | |
) | |
{ | |
return (BOOLEAN)EC_POINT_add (EcGroup, EcPointResult, EcPointA, EcPointB, BnCtx); | |
} | |
/** | |
Variable EC point multiplication. EcPointResult = EcPoint * BnPScalar. | |
@param[in] EcGroup EC group object. | |
@param[out] EcPointResult EC point to hold the result. The point should | |
be properly initialized. | |
@param[in] EcPoint EC Point. | |
@param[in] BnPScalar P Scalar. | |
@param[in] BnCtx BN context, created with BigNumNewContext(). | |
@retval TRUE On success. | |
@retval FALSE Otherwise. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EcPointMul ( | |
IN CONST VOID *EcGroup, | |
OUT VOID *EcPointResult, | |
IN CONST VOID *EcPoint, | |
IN CONST VOID *BnPScalar, | |
IN VOID *BnCtx | |
) | |
{ | |
return (BOOLEAN)EC_POINT_mul (EcGroup, EcPointResult, NULL, EcPoint, BnPScalar, BnCtx); | |
} | |
/** | |
Calculate the inverse of the supplied EC point. | |
@param[in] EcGroup EC group object. | |
@param[in,out] EcPoint EC point to invert. | |
@param[in] BnCtx BN context, created with BigNumNewContext(). | |
@retval TRUE On success. | |
@retval FALSE Otherwise. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EcPointInvert ( | |
IN CONST VOID *EcGroup, | |
IN OUT VOID *EcPoint, | |
IN VOID *BnCtx | |
) | |
{ | |
return (BOOLEAN)EC_POINT_invert (EcGroup, EcPoint, BnCtx); | |
} | |
/** | |
Check if the supplied point is on EC curve. | |
@param[in] EcGroup EC group object. | |
@param[in] EcPoint EC point to check. | |
@param[in] BnCtx BN context, created with BigNumNewContext(). | |
@retval TRUE On curve. | |
@retval FALSE Otherwise. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EcPointIsOnCurve ( | |
IN CONST VOID *EcGroup, | |
IN CONST VOID *EcPoint, | |
IN VOID *BnCtx | |
) | |
{ | |
return EC_POINT_is_on_curve (EcGroup, EcPoint, BnCtx) == 1; | |
} | |
/** | |
Check if the supplied point is at infinity. | |
@param[in] EcGroup EC group object. | |
@param[in] EcPoint EC point to check. | |
@retval TRUE At infinity. | |
@retval FALSE Otherwise. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EcPointIsAtInfinity ( | |
IN CONST VOID *EcGroup, | |
IN CONST VOID *EcPoint | |
) | |
{ | |
return EC_POINT_is_at_infinity (EcGroup, EcPoint) == 1; | |
} | |
/** | |
Check if EC points are equal. | |
@param[in] EcGroup EC group object. | |
@param[in] EcPointA EC point A. | |
@param[in] EcPointB EC point B. | |
@param[in] BnCtx BN context, created with BigNumNewContext(). | |
@retval TRUE A == B. | |
@retval FALSE Otherwise. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EcPointEqual ( | |
IN CONST VOID *EcGroup, | |
IN CONST VOID *EcPointA, | |
IN CONST VOID *EcPointB, | |
IN VOID *BnCtx | |
) | |
{ | |
return EC_POINT_cmp (EcGroup, EcPointA, EcPointB, BnCtx) == 0; | |
} | |
/** | |
Set EC point compressed coordinates. Points can be described in terms of | |
their compressed coordinates. For a point (x, y), for any given value for x | |
such that the point is on the curve there will only ever be two possible | |
values for y. Therefore, a point can be set using this function where BnX is | |
the x coordinate and YBit is a value 0 or 1 to identify which of the two | |
possible values for y should be used. | |
@param[in] EcGroup EC group object. | |
@param[in] EcPoint EC Point. | |
@param[in] BnX X coordinate. | |
@param[in] YBit 0 or 1 to identify which Y value is used. | |
@param[in] BnCtx BN context, created with BigNumNewContext(). | |
@retval TRUE On success. | |
@retval FALSE Otherwise. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EcPointSetCompressedCoordinates ( | |
IN CONST VOID *EcGroup, | |
IN VOID *EcPoint, | |
IN CONST VOID *BnX, | |
IN UINT8 YBit, | |
IN VOID *BnCtx | |
) | |
{ | |
return (BOOLEAN)EC_POINT_set_compressed_coordinates (EcGroup, EcPoint, BnX, YBit, BnCtx); | |
} | |
// ===================================================================================== | |
// Elliptic Curve Diffie Hellman Primitives | |
// ===================================================================================== | |
/** | |
Allocates and Initializes one Elliptic Curve Context for subsequent use | |
with the NID. | |
@param[in] Nid Identifying number for the ECC curve (Defined in | |
BaseCryptLib.h). | |
@return Pointer to the Elliptic Curve Context that has been initialized. | |
If the allocations fails, EcNewByNid() returns NULL. | |
**/ | |
VOID * | |
EFIAPI | |
EcNewByNid ( | |
IN UINTN Nid | |
) | |
{ | |
INT32 OpenSslNid; | |
OpenSslNid = CryptoNidToOpensslNid (Nid); | |
if (OpenSslNid < 0) { | |
return NULL; | |
} | |
return (VOID *)EC_KEY_new_by_curve_name (OpenSslNid); | |
} | |
/** | |
Release the specified EC context. | |
@param[in] EcContext Pointer to the EC context to be released. | |
**/ | |
VOID | |
EFIAPI | |
EcFree ( | |
IN VOID *EcContext | |
) | |
{ | |
EC_KEY_free ((EC_KEY *)EcContext); | |
} | |
/** | |
Generates EC key and returns EC public key (X, Y), Please note, this function uses | |
pseudo random number generator. The caller must make sure RandomSeed() | |
function was properly called before. | |
The Ec context should be correctly initialized by EcNewByNid. | |
This function generates random secret, and computes the public key (X, Y), which is | |
returned via parameter Public, PublicSize. | |
X is the first half of Public with size being PublicSize / 2, | |
Y is the second half of Public with size being PublicSize / 2. | |
EC context is updated accordingly. | |
If the Public buffer is too small to hold the public X, Y, FALSE is returned and | |
PublicSize is set to the required buffer size to obtain the public X, Y. | |
For P-256, the PublicSize is 64. First 32-byte is X, Second 32-byte is Y. | |
For P-384, the PublicSize is 96. First 48-byte is X, Second 48-byte is Y. | |
For P-521, the PublicSize is 132. First 66-byte is X, Second 66-byte is Y. | |
If EcContext is NULL, then return FALSE. | |
If PublicSize is NULL, then return FALSE. | |
If PublicSize is large enough but Public is NULL, then return FALSE. | |
@param[in, out] EcContext Pointer to the EC context. | |
@param[out] PublicKey Pointer to t buffer to receive generated public X,Y. | |
@param[in, out] PublicKeySize On input, the size of Public buffer in bytes. | |
On output, the size of data returned in Public buffer in bytes. | |
@retval TRUE EC public X,Y generation succeeded. | |
@retval FALSE EC public X,Y generation failed. | |
@retval FALSE PublicKeySize is not large enough. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EcGenerateKey ( | |
IN OUT VOID *EcContext, | |
OUT UINT8 *PublicKey, | |
IN OUT UINTN *PublicKeySize | |
) | |
{ | |
EC_KEY *EcKey; | |
CONST EC_GROUP *Group; | |
CONST EC_POINT *EcPoint; | |
BOOLEAN RetVal; | |
BIGNUM *BnX; | |
BIGNUM *BnY; | |
UINTN HalfSize; | |
INTN XSize; | |
INTN YSize; | |
if ((EcContext == NULL) || (PublicKeySize == NULL)) { | |
return FALSE; | |
} | |
if ((PublicKey == NULL) && (*PublicKeySize != 0)) { | |
return FALSE; | |
} | |
EcKey = (EC_KEY *)EcContext; | |
Group = EC_KEY_get0_group (EcKey); | |
HalfSize = (EC_GROUP_get_degree (Group) + 7) / 8; | |
// Assume RAND_seed was called | |
if (EC_KEY_generate_key (EcKey) != 1) { | |
return FALSE; | |
} | |
if (*PublicKeySize < HalfSize * 2) { | |
*PublicKeySize = HalfSize * 2; | |
return FALSE; | |
} | |
*PublicKeySize = HalfSize * 2; | |
EcPoint = EC_KEY_get0_public_key (EcKey); | |
if (EcPoint == NULL) { | |
return FALSE; | |
} | |
RetVal = FALSE; | |
BnX = BN_new (); | |
BnY = BN_new (); | |
if ((BnX == NULL) || (BnY == NULL)) { | |
goto fail; | |
} | |
if (EC_POINT_get_affine_coordinates (Group, EcPoint, BnX, BnY, NULL) != 1) { | |
goto fail; | |
} | |
XSize = BN_num_bytes (BnX); | |
YSize = BN_num_bytes (BnY); | |
if ((XSize <= 0) || (YSize <= 0)) { | |
goto fail; | |
} | |
ASSERT ((UINTN)XSize <= HalfSize && (UINTN)YSize <= HalfSize); | |
ZeroMem (PublicKey, *PublicKeySize); | |
BN_bn2bin (BnX, &PublicKey[0 + HalfSize - XSize]); | |
BN_bn2bin (BnY, &PublicKey[HalfSize + HalfSize - YSize]); | |
RetVal = TRUE; | |
fail: | |
BN_free (BnX); | |
BN_free (BnY); | |
return RetVal; | |
} | |
/** | |
Gets the public key component from the established EC context. | |
The Ec context should be correctly initialized by EcNewByNid, and successfully | |
generate key pair from EcGenerateKey(). | |
For P-256, the PublicSize is 64. First 32-byte is X, Second 32-byte is Y. | |
For P-384, the PublicSize is 96. First 48-byte is X, Second 48-byte is Y. | |
For P-521, the PublicSize is 132. First 66-byte is X, Second 66-byte is Y. | |
@param[in, out] EcContext Pointer to EC context being set. | |
@param[out] PublicKey Pointer to t buffer to receive generated public X,Y. | |
@param[in, out] PublicKeySize On input, the size of Public buffer in bytes. | |
On output, the size of data returned in Public buffer in bytes. | |
@retval TRUE EC key component was retrieved successfully. | |
@retval FALSE Invalid EC key component. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EcGetPubKey ( | |
IN OUT VOID *EcContext, | |
OUT UINT8 *PublicKey, | |
IN OUT UINTN *PublicKeySize | |
) | |
{ | |
EC_KEY *EcKey; | |
CONST EC_GROUP *Group; | |
CONST EC_POINT *EcPoint; | |
BIGNUM *BnX; | |
BIGNUM *BnY; | |
UINTN HalfSize; | |
INTN XSize; | |
INTN YSize; | |
BOOLEAN RetVal; | |
if ((EcContext == NULL) || (PublicKeySize == NULL)) { | |
return FALSE; | |
} | |
if ((PublicKey == NULL) && (*PublicKeySize != 0)) { | |
return FALSE; | |
} | |
EcKey = (EC_KEY *)EcContext; | |
Group = EC_KEY_get0_group (EcKey); | |
HalfSize = (EC_GROUP_get_degree (Group) + 7) / 8; | |
if (*PublicKeySize < HalfSize * 2) { | |
*PublicKeySize = HalfSize * 2; | |
return FALSE; | |
} | |
*PublicKeySize = HalfSize * 2; | |
EcPoint = EC_KEY_get0_public_key (EcKey); | |
if (EcPoint == NULL) { | |
return FALSE; | |
} | |
RetVal = FALSE; | |
BnX = BN_new (); | |
BnY = BN_new (); | |
if ((BnX == NULL) || (BnY == NULL)) { | |
goto fail; | |
} | |
if (EC_POINT_get_affine_coordinates (Group, EcPoint, BnX, BnY, NULL) != 1) { | |
goto fail; | |
} | |
XSize = BN_num_bytes (BnX); | |
YSize = BN_num_bytes (BnY); | |
if ((XSize <= 0) || (YSize <= 0)) { | |
goto fail; | |
} | |
ASSERT ((UINTN)XSize <= HalfSize && (UINTN)YSize <= HalfSize); | |
if (PublicKey != NULL) { | |
ZeroMem (PublicKey, *PublicKeySize); | |
BN_bn2bin (BnX, &PublicKey[0 + HalfSize - XSize]); | |
BN_bn2bin (BnY, &PublicKey[HalfSize + HalfSize - YSize]); | |
} | |
RetVal = TRUE; | |
fail: | |
BN_free (BnX); | |
BN_free (BnY); | |
return RetVal; | |
} | |
/** | |
Computes exchanged common key. | |
Given peer's public key (X, Y), this function computes the exchanged common key, | |
based on its own context including value of curve parameter and random secret. | |
X is the first half of PeerPublic with size being PeerPublicSize / 2, | |
Y is the second half of PeerPublic with size being PeerPublicSize / 2. | |
If public key is compressed, the PeerPublic will only contain half key (X). | |
If EcContext is NULL, then return FALSE. | |
If PeerPublic is NULL, then return FALSE. | |
If PeerPublicSize is 0, then return FALSE. | |
If Key is NULL, then return FALSE. | |
If KeySize is not large enough, then return FALSE. | |
For P-256, the PeerPublicSize is 64. First 32-byte is X, Second 32-byte is Y. | |
For P-384, the PeerPublicSize is 96. First 48-byte is X, Second 48-byte is Y. | |
For P-521, the PeerPublicSize is 132. First 66-byte is X, Second 66-byte is Y. | |
@param[in, out] EcContext Pointer to the EC context. | |
@param[in] PeerPublic Pointer to the peer's public X,Y. | |
@param[in] PeerPublicSize Size of peer's public X,Y in bytes. | |
@param[in] CompressFlag Flag of PeerPublic is compressed or not. | |
@param[out] Key Pointer to the buffer to receive generated key. | |
@param[in, out] KeySize On input, the size of Key buffer in bytes. | |
On output, the size of data returned in Key buffer in bytes. | |
@retval TRUE EC exchanged key generation succeeded. | |
@retval FALSE EC exchanged key generation failed. | |
@retval FALSE KeySize is not large enough. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EcDhComputeKey ( | |
IN OUT VOID *EcContext, | |
IN CONST UINT8 *PeerPublic, | |
IN UINTN PeerPublicSize, | |
IN CONST INT32 *CompressFlag, | |
OUT UINT8 *Key, | |
IN OUT UINTN *KeySize | |
) | |
{ | |
EC_KEY *EcKey; | |
EC_KEY *PeerEcKey; | |
CONST EC_GROUP *Group; | |
BOOLEAN RetVal; | |
BIGNUM *BnX; | |
BIGNUM *BnY; | |
EC_POINT *Point; | |
INT32 OpenSslNid; | |
UINTN HalfSize; | |
if ((EcContext == NULL) || (PeerPublic == NULL) || (KeySize == NULL)) { | |
return FALSE; | |
} | |
if ((Key == NULL) && (*KeySize != 0)) { | |
return FALSE; | |
} | |
if (PeerPublicSize > INT_MAX) { | |
return FALSE; | |
} | |
EcKey = (EC_KEY *)EcContext; | |
Group = EC_KEY_get0_group (EcKey); | |
HalfSize = (EC_GROUP_get_degree (Group) + 7) / 8; | |
if ((CompressFlag == NULL) && (PeerPublicSize != HalfSize * 2)) { | |
return FALSE; | |
} | |
if ((CompressFlag != NULL) && (PeerPublicSize != HalfSize)) { | |
return FALSE; | |
} | |
if (*KeySize < HalfSize) { | |
*KeySize = HalfSize; | |
return FALSE; | |
} | |
*KeySize = HalfSize; | |
RetVal = FALSE; | |
Point = NULL; | |
BnX = BN_bin2bn (PeerPublic, (INT32)HalfSize, NULL); | |
BnY = NULL; | |
Point = EC_POINT_new (Group); | |
PeerEcKey = NULL; | |
if ((BnX == NULL) || (Point == NULL)) { | |
goto fail; | |
} | |
if (CompressFlag == NULL) { | |
BnY = BN_bin2bn (PeerPublic + HalfSize, (INT32)HalfSize, NULL); | |
if (BnY == NULL) { | |
goto fail; | |
} | |
if (EC_POINT_set_affine_coordinates (Group, Point, BnX, BnY, NULL) != 1) { | |
goto fail; | |
} | |
} else { | |
if (EC_POINT_set_compressed_coordinates (Group, Point, BnX, *CompressFlag, NULL) != 1) { | |
goto fail; | |
} | |
} | |
// Validate NIST ECDH public key | |
OpenSslNid = EC_GROUP_get_curve_name (Group); | |
PeerEcKey = EC_KEY_new_by_curve_name (OpenSslNid); | |
if (PeerEcKey == NULL) { | |
goto fail; | |
} | |
if (EC_KEY_set_public_key (PeerEcKey, Point) != 1) { | |
goto fail; | |
} | |
if (EC_KEY_check_key (PeerEcKey) != 1) { | |
goto fail; | |
} | |
if (ECDH_compute_key (Key, *KeySize, Point, EcKey, NULL) <= 0) { | |
goto fail; | |
} | |
RetVal = TRUE; | |
fail: | |
BN_free (BnX); | |
BN_free (BnY); | |
EC_POINT_free (Point); | |
EC_KEY_free (PeerEcKey); | |
return RetVal; | |
} | |
/** | |
Carries out the EC-DSA signature. | |
This function carries out the EC-DSA signature. | |
If the Signature buffer is too small to hold the contents of signature, FALSE | |
is returned and SigSize is set to the required buffer size to obtain the signature. | |
If EcContext is NULL, then return FALSE. | |
If MessageHash is NULL, then return FALSE. | |
If HashSize need match the HashNid. HashNid could be SHA256, SHA384, SHA512, SHA3_256, SHA3_384, SHA3_512. | |
If SigSize is large enough but Signature is NULL, then return FALSE. | |
For P-256, the SigSize is 64. First 32-byte is R, Second 32-byte is S. | |
For P-384, the SigSize is 96. First 48-byte is R, Second 48-byte is S. | |
For P-521, the SigSize is 132. First 66-byte is R, Second 66-byte is S. | |
@param[in] EcContext Pointer to EC context for signature generation. | |
@param[in] HashNid hash NID | |
@param[in] MessageHash Pointer to octet message hash to be signed. | |
@param[in] HashSize Size of the message hash in bytes. | |
@param[out] Signature Pointer to buffer to receive EC-DSA signature. | |
@param[in, out] SigSize On input, the size of Signature buffer in bytes. | |
On output, the size of data returned in Signature buffer in bytes. | |
@retval TRUE Signature successfully generated in EC-DSA. | |
@retval FALSE Signature generation failed. | |
@retval FALSE SigSize is too small. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EcDsaSign ( | |
IN VOID *EcContext, | |
IN UINTN HashNid, | |
IN CONST UINT8 *MessageHash, | |
IN UINTN HashSize, | |
OUT UINT8 *Signature, | |
IN OUT UINTN *SigSize | |
) | |
{ | |
EC_KEY *EcKey; | |
ECDSA_SIG *EcDsaSig; | |
INT32 OpenSslNid; | |
UINT8 HalfSize; | |
BIGNUM *R; | |
BIGNUM *S; | |
INTN RSize; | |
INTN SSize; | |
if ((EcContext == NULL) || (MessageHash == NULL)) { | |
return FALSE; | |
} | |
if (Signature == NULL) { | |
return FALSE; | |
} | |
EcKey = (EC_KEY *)EcContext; | |
OpenSslNid = EC_GROUP_get_curve_name (EC_KEY_get0_group (EcKey)); | |
switch (OpenSslNid) { | |
case NID_X9_62_prime256v1: | |
HalfSize = 32; | |
break; | |
case NID_secp384r1: | |
HalfSize = 48; | |
break; | |
case NID_secp521r1: | |
HalfSize = 66; | |
break; | |
default: | |
return FALSE; | |
} | |
if (*SigSize < (UINTN)(HalfSize * 2)) { | |
*SigSize = HalfSize * 2; | |
return FALSE; | |
} | |
*SigSize = HalfSize * 2; | |
ZeroMem (Signature, *SigSize); | |
switch (HashNid) { | |
case CRYPTO_NID_SHA256: | |
if (HashSize != SHA256_DIGEST_SIZE) { | |
return FALSE; | |
} | |
break; | |
case CRYPTO_NID_SHA384: | |
if (HashSize != SHA384_DIGEST_SIZE) { | |
return FALSE; | |
} | |
break; | |
case CRYPTO_NID_SHA512: | |
if (HashSize != SHA512_DIGEST_SIZE) { | |
return FALSE; | |
} | |
break; | |
default: | |
return FALSE; | |
} | |
EcDsaSig = ECDSA_do_sign ( | |
MessageHash, | |
(UINT32)HashSize, | |
(EC_KEY *)EcContext | |
); | |
if (EcDsaSig == NULL) { | |
return FALSE; | |
} | |
ECDSA_SIG_get0 (EcDsaSig, (CONST BIGNUM **)&R, (CONST BIGNUM **)&S); | |
RSize = BN_num_bytes (R); | |
SSize = BN_num_bytes (S); | |
if ((RSize <= 0) || (SSize <= 0)) { | |
ECDSA_SIG_free (EcDsaSig); | |
return FALSE; | |
} | |
ASSERT ((UINTN)RSize <= HalfSize && (UINTN)SSize <= HalfSize); | |
BN_bn2bin (R, &Signature[0 + HalfSize - RSize]); | |
BN_bn2bin (S, &Signature[HalfSize + HalfSize - SSize]); | |
ECDSA_SIG_free (EcDsaSig); | |
return TRUE; | |
} | |
/** | |
Verifies the EC-DSA signature. | |
If EcContext is NULL, then return FALSE. | |
If MessageHash is NULL, then return FALSE. | |
If Signature is NULL, then return FALSE. | |
If HashSize need match the HashNid. HashNid could be SHA256, SHA384, SHA512, SHA3_256, SHA3_384, SHA3_512. | |
For P-256, the SigSize is 64. First 32-byte is R, Second 32-byte is S. | |
For P-384, the SigSize is 96. First 48-byte is R, Second 48-byte is S. | |
For P-521, the SigSize is 132. First 66-byte is R, Second 66-byte is S. | |
@param[in] EcContext Pointer to EC context for signature verification. | |
@param[in] HashNid hash NID | |
@param[in] MessageHash Pointer to octet message hash to be checked. | |
@param[in] HashSize Size of the message hash in bytes. | |
@param[in] Signature Pointer to EC-DSA signature to be verified. | |
@param[in] SigSize Size of signature in bytes. | |
@retval TRUE Valid signature encoded in EC-DSA. | |
@retval FALSE Invalid signature or invalid EC context. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EcDsaVerify ( | |
IN VOID *EcContext, | |
IN UINTN HashNid, | |
IN CONST UINT8 *MessageHash, | |
IN UINTN HashSize, | |
IN CONST UINT8 *Signature, | |
IN UINTN SigSize | |
) | |
{ | |
INT32 Result; | |
EC_KEY *EcKey; | |
ECDSA_SIG *EcDsaSig; | |
INT32 OpenSslNid; | |
UINT8 HalfSize; | |
BIGNUM *R; | |
BIGNUM *S; | |
if ((EcContext == NULL) || (MessageHash == NULL) || (Signature == NULL)) { | |
return FALSE; | |
} | |
if ((SigSize > INT_MAX) || (SigSize == 0)) { | |
return FALSE; | |
} | |
EcKey = (EC_KEY *)EcContext; | |
OpenSslNid = EC_GROUP_get_curve_name (EC_KEY_get0_group (EcKey)); | |
switch (OpenSslNid) { | |
case NID_X9_62_prime256v1: | |
HalfSize = 32; | |
break; | |
case NID_secp384r1: | |
HalfSize = 48; | |
break; | |
case NID_secp521r1: | |
HalfSize = 66; | |
break; | |
default: | |
return FALSE; | |
} | |
if (SigSize != (UINTN)(HalfSize * 2)) { | |
return FALSE; | |
} | |
switch (HashNid) { | |
case CRYPTO_NID_SHA256: | |
if (HashSize != SHA256_DIGEST_SIZE) { | |
return FALSE; | |
} | |
break; | |
case CRYPTO_NID_SHA384: | |
if (HashSize != SHA384_DIGEST_SIZE) { | |
return FALSE; | |
} | |
break; | |
case CRYPTO_NID_SHA512: | |
if (HashSize != SHA512_DIGEST_SIZE) { | |
return FALSE; | |
} | |
break; | |
default: | |
return FALSE; | |
} | |
EcDsaSig = ECDSA_SIG_new (); | |
if (EcDsaSig == NULL) { | |
ECDSA_SIG_free (EcDsaSig); | |
return FALSE; | |
} | |
R = BN_bin2bn (Signature, (UINT32)HalfSize, NULL); | |
S = BN_bin2bn (Signature + HalfSize, (UINT32)HalfSize, NULL); | |
if ((R == NULL) || (S == NULL)) { | |
ECDSA_SIG_free (EcDsaSig); | |
return FALSE; | |
} | |
ECDSA_SIG_set0 (EcDsaSig, R, S); | |
Result = ECDSA_do_verify ( | |
MessageHash, | |
(UINT32)HashSize, | |
EcDsaSig, | |
(EC_KEY *)EcContext | |
); | |
ECDSA_SIG_free (EcDsaSig); | |
return (Result == 1); | |
} |