mirror of https://github.com/acidanthera/audk.git
1024 lines
27 KiB
C
1024 lines
27 KiB
C
/** @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);
|
|
}
|