Skip to content

Commit

Permalink
Merge pull request #7433 from SparkiDev/eddsa_fips_checks
Browse files Browse the repository at this point in the history
EdDSA FIPS checks on public key
  • Loading branch information
JacobBarthelmeh authored Apr 18, 2024
2 parents 9666e4d + 0bd5967 commit ef15310
Show file tree
Hide file tree
Showing 4 changed files with 442 additions and 22 deletions.
4 changes: 2 additions & 2 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -4994,12 +4994,12 @@ AS_CASE([$FIPS_VERSION],
[AM_CFLAGS="$AM_CFLAGS -DECC_SHAMIR"])])
AS_IF([test "x$ENABLED_ED25519" != "xyes"],
[ENABLED_ED25519="yes"; AM_CFLAGS="$AM_CFLAGS -DHAVE_ED25519"])
[ENABLED_ED25519="yes"; AM_CFLAGS="$AM_CFLAGS -DHAVE_ED25519 -DHAVE_ED25519_KEY_IMPORT"])
AS_IF([test "x$ENABLED_CURVE25519" != "xyes"],
[ENABLED_CURVE25519="yes"; AM_CFLAGS="$AM_CFLAGS -DHAVE_CURVE25519"])
AS_IF([test "x$ENABLED_ED448" != "xyes"],
[ENABLED_ED448="yes"; AM_CFLAGS="$AM_CFLAGS -DHAVE_ED448"])
[ENABLED_ED448="yes"; AM_CFLAGS="$AM_CFLAGS -DHAVE_ED448 -DHAVE_ED448_KEY_IMPORT"])
AS_IF([test "x$ENABLED_CURVE448" != "xyes"],
[ENABLED_CURVE448="yes"; AM_CFLAGS="$AM_CFLAGS -DHAVE_CURVE448"])
Expand Down
136 changes: 127 additions & 9 deletions wolfcrypt/src/ed25519.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,56 @@ static int ed25519_hash(ed25519_key* key, const byte* in, word32 inLen,
}

#ifdef HAVE_ED25519_MAKE_KEY
#if FIPS_VERSION3_GE(6,0,0)
/* Performs a Pairwise Consistency Test on an Ed25519 key pair.
*
* @param [in] key Ed25519 key to test.
* @param [in] rng Random number generator to use to create random digest.
* @return 0 on success.
* @return ECC_PCT_E when signing or verification fail.
* @return Other -ve when random number generation fails.
*/
static int ed25519_pairwise_consistency_test(ed25519_key* key, WC_RNG* rng)
{
int err = 0;
byte digest[WC_SHA512_DIGEST_SIZE];
word32 digestLen = WC_SHA512_DIGEST_SIZE;
byte sig[ED25519_SIG_SIZE];
word32 sigLen = ED25519_SIG_SIZE;
int res = 0;

/* Generate a random digest to sign. */
err = wc_RNG_GenerateBlock(rng, digest, digestLen);
if (err == 0) {
/* Sign digest without context. */
err = wc_ed25519_sign_msg_ex(digest, digestLen, sig, &sigLen, key,
Ed25519, NULL, 0);
if (err != 0) {
/* Any sign failure means test failed. */
err = ECC_PCT_E;
}
}
if (err == 0) {
/* Verify digest without context. */
err = wc_ed25519_verify_msg_ex(sig, sigLen, digest, digestLen, &res,
key, Ed25519, NULL, 0);
if (err != 0) {
/* Any verification operation failure means test failed. */
err = ECC_PCT_E;
}
/* Check whether the signature verified. */
else if (res == 0) {
/* Test failed. */
err = ECC_PCT_E;
}
}

ForceZero(sig, sigLen);

return err;
}
#endif

int wc_ed25519_make_public(ed25519_key* key, unsigned char* pubKey,
word32 pubKeySz)
{
Expand Down Expand Up @@ -291,6 +341,13 @@ int wc_ed25519_make_key(WC_RNG* rng, int keySz, ed25519_key* key)
/* put public key after private key, on the same buffer */
XMEMMOVE(key->k + ED25519_KEY_SIZE, key->p, ED25519_PUB_KEY_SIZE);

#if FIPS_VERSION3_GE(6,0,0)
ret = wc_ed25519_check_key(key);
if (ret == 0) {
ret = ed25519_pairwise_consistency_test(key, rng);
}
#endif

return ret;
}
#endif /* HAVE_ED25519_MAKE_KEY */
Expand Down Expand Up @@ -1077,7 +1134,7 @@ int wc_ed25519_import_public_ex(const byte* in, word32 inLen, ed25519_key* key,

if (ret == 0) {
key->pubKeySet = 1;
if (key->privKeySet && (!trusted)) {
if (!trusted) {
ret = wc_ed25519_check_key(key);
}
}
Expand Down Expand Up @@ -1278,23 +1335,84 @@ int wc_ed25519_export_key(ed25519_key* key,

#endif /* HAVE_ED25519_KEY_EXPORT */

/* check the private and public keys match */
/* Check the public key is valid.
*
* When private key available, check the calculated public key matches.
* When no private key, check Y is in range and an X is able to be calculated.
*
* @param [in] key Ed25519 private/public key.
* @return 0 otherwise.
* @return BAD_FUNC_ARG when key is NULL.
* @return PUBLIC_KEY_E when the public key is not set, doesn't match or is
* invalid.
* @return other -ve value on hash failure.
*/
int wc_ed25519_check_key(ed25519_key* key)
{
int ret = 0;
#ifdef HAVE_ED25519_MAKE_KEY
ALIGN16 unsigned char pubKey[ED25519_PUB_KEY_SIZE];

if (!key->pubKeySet)
/* Validate parameter. */
if (key == NULL) {
ret = BAD_FUNC_ARG;
}

/* Check we have a public key to check. */
if ((ret == 0) && (!key->pubKeySet)) {
ret = PUBLIC_KEY_E;
if (ret == 0)
}

#ifdef HAVE_ED25519_MAKE_KEY
/* If we have a private key just make the public key and compare. */
if ((ret == 0) && (key->privKeySet)) {
ALIGN16 unsigned char pubKey[ED25519_PUB_KEY_SIZE];

ret = wc_ed25519_make_public(key, pubKey, sizeof(pubKey));
if (ret == 0 && XMEMCMP(pubKey, key->p, ED25519_PUB_KEY_SIZE) != 0)
ret = PUBLIC_KEY_E;
if (ret == 0 && XMEMCMP(pubKey, key->p, ED25519_PUB_KEY_SIZE) != 0)
ret = PUBLIC_KEY_E;
}
#else
(void)key;
(void)key;
#endif /* HAVE_ED25519_MAKE_KEY */

/* No private key (or ability to make a public key), check Y is valid. */
if ((ret == 0)
#ifdef HAVE_ED25519_MAKE_KEY
&& (!key->privKeySet)
#endif
) {
/* Verify that Q is not identity element 0.
* 0 has no representation for Ed25519. */

/* Verify that xQ and yQ are integers in the interval [0, p - 1].
* Only have yQ so check that ordinate. p = 2^255 - 19 */
if ((key->p[ED25519_PUB_KEY_SIZE - 1] & 0x7f) == 0x7f) {
int i;

ret = PUBLIC_KEY_E;
/* Check up to last byte. */
for (i = ED25519_PUB_KEY_SIZE - 2; i > 0; i--) {
if (key->p[i] != 0xff) {
ret = 0;
break;
}
}
/* Bits are all one up to last byte - check less than -19. */
if ((ret == PUBLIC_KEY_E) && (key->p[0] < 0xed)) {
ret = 0;
}
}

if (ret == 0) {
/* Verify that Q is on the curve.
* Uncompressing the public key will validate yQ. */
ge_p3 A;

if (ge_frombytes_negate_vartime(&A, key->p) != 0) {
ret = PUBLIC_KEY_E;
}
}
}

return ret;
}

Expand Down
138 changes: 127 additions & 11 deletions wolfcrypt/src/ed448.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,56 @@ static int ed448_hash(ed448_key* key, const byte* in, word32 inLen,
return ret;
}

#if FIPS_VERSION3_GE(6,0,0)
/* Performs a Pairwise Consistency Test on an Ed448 key pair.
*
* @param [in] key Ed448 key to test.
* @param [in] rng Random number generator to use to create random digest.
* @return 0 on success.
* @return ECC_PCT_E when signing or verification fail.
* @return Other -ve when random number generation fails.
*/
static int ed448_pairwise_consistency_test(ed448_key* key, WC_RNG* rng)
{
int err = 0;
byte digest[WC_SHA256_DIGEST_SIZE];
word32 digestLen = WC_SHA256_DIGEST_SIZE;
byte sig[ED448_SIG_SIZE];
word32 sigLen = ED448_SIG_SIZE;
int res = 0;

/* Generate a random digest to sign. */
err = wc_RNG_GenerateBlock(rng, digest, digestLen);
if (err == 0) {
/* Sign digest without context. */
err = wc_ed448_sign_msg_ex(digest, digestLen, sig, &sigLen, key, Ed448,
NULL, 0);
if (err != 0) {
/* Any sign failure means test failed. */
err = ECC_PCT_E;
}
}
if (err == 0) {
/* Verify digest without context. */
err = wc_ed448_verify_msg_ex(sig, sigLen, digest, digestLen, &res, key,
Ed448, NULL, 0);
if (err != 0) {
/* Any verification operation failure means test failed. */
err = ECC_PCT_E;
}
/* Check whether the signature verified. */
else if (res == 0) {
/* Test failed. */
err = ECC_PCT_E;
}
}

ForceZero(sig, sigLen);

return err;
}
#endif

/* Derive the public key for the private key.
*
* key [in] Ed448 key object.
Expand Down Expand Up @@ -272,6 +322,13 @@ int wc_ed448_make_key(WC_RNG* rng, int keySz, ed448_key* key)
if (ret == 0) {
/* put public key after private key, on the same buffer */
XMEMMOVE(key->k + ED448_KEY_SIZE, key->p, ED448_PUB_KEY_SIZE);

#if FIPS_VERSION3_GE(6,0,0)
ret = wc_ed448_check_key(key);
if (ret == 0) {
ret = ed448_pairwise_consistency_test(key, rng);
}
#endif
}

return ret;
Expand Down Expand Up @@ -966,7 +1023,7 @@ int wc_ed448_import_public_ex(const byte* in, word32 inLen, ed448_key* key,
ret = BAD_FUNC_ARG;
}

if (inLen != ED448_PUB_KEY_SIZE) {
if ((inLen != ED448_PUB_KEY_SIZE) && (inLen != ED448_PUB_KEY_SIZE + 1)) {
ret = BAD_FUNC_ARG;
}

Expand Down Expand Up @@ -995,7 +1052,7 @@ int wc_ed448_import_public_ex(const byte* in, word32 inLen, ed448_key* key,

if (ret == 0) {
key->pubKeySet = 1;
if (key->privKeySet && (!trusted)) {
if (!trusted) {
/* Check untrusted public key data matches private key. */
ret = wc_ed448_check_key(key);
}
Expand Down Expand Up @@ -1243,31 +1300,90 @@ int wc_ed448_export_key(ed448_key* key, byte* priv, word32 *privSz,

#endif /* HAVE_ED448_KEY_EXPORT */

/* Check the public key of the ed448 key matches the private key.
/* Check the public key is valid.
*
* key [in] Ed448 private/public key.
* returns BAD_FUNC_ARG when key is NULL,
* PUBLIC_KEY_E when the public key is not set or doesn't match,
* other -ve value on hash failure,
* 0 otherwise.
* When private key available, check the calculated public key matches.
* When no private key, check Y is in range and an X is able to be calculated.
*
* @param [in] key Ed448 private/public key.
* @return 0 otherwise.
* @return BAD_FUNC_ARG when key is NULL.
* @return PUBLIC_KEY_E when the public key is not set, doesn't match or is
* invalid.
* @return other -ve value on hash failure.
*/
int wc_ed448_check_key(ed448_key* key)
{
int ret = 0;
unsigned char pubKey[ED448_PUB_KEY_SIZE];

/* Validate parameter. */
if (key == NULL) {
ret = BAD_FUNC_ARG;
}

/* Check we have a public key to check. */
if (ret == 0 && !key->pubKeySet) {
ret = PUBLIC_KEY_E;
}
if (ret == 0) {

/* If we have a private key just make the public key and compare. */
if ((ret == 0) && key->privKeySet) {
ret = wc_ed448_make_public(key, pubKey, sizeof(pubKey));
if ((ret == 0) && (XMEMCMP(pubKey, key->p, ED448_PUB_KEY_SIZE) != 0)) {
ret = PUBLIC_KEY_E;
}
}
if ((ret == 0) && (XMEMCMP(pubKey, key->p, ED448_PUB_KEY_SIZE) != 0)) {
ret = PUBLIC_KEY_E;
/* No private key, check Y is valid. */
else if ((ret == 0) && (!key->privKeySet)) {
/* Verify that Q is not identity element 0.
* 0 has no representation for Ed448. */

/* Verify that xQ and yQ are integers in the interval [0, p - 1].
* Only have yQ so check that ordinate.
* p = 2^448-2^224-1 = 0xff..fe..ff
*/
{
int i;
ret = PUBLIC_KEY_E;

/* Check top part before 0xFE. */
for (i = ED448_PUB_KEY_SIZE - 1; i > ED448_PUB_KEY_SIZE/2; i--) {
if (key->p[i] < 0xff) {
ret = 0;
break;
}
}
if (ret == PUBLIC_KEY_E) {
/* Check against 0xFE. */
if (key->p[ED448_PUB_KEY_SIZE/2] < 0xfe) {
ret = 0;
}
else if (key->p[ED448_PUB_KEY_SIZE/2] == 0xfe) {
/* Check bottom part before last byte. */
for (i = ED448_PUB_KEY_SIZE/2 - 1; i > 0; i--) {
if (key->p[i] != 0xff) {
ret = 0;
break;
}
}
/* Check last byte. */
if ((ret == PUBLIC_KEY_E) && (key->p[0] < 0xff)) {
ret = 0;
}
}
}
}

if (ret == 0) {
/* Verify that Q is on the curve.
* Uncompressing the public key will validate yQ. */
ge448_p2 A;

if (ge448_from_bytes_negate_vartime(&A, key->p) != 0) {
ret = PUBLIC_KEY_E;
}
}
}

return ret;
Expand Down
Loading

0 comments on commit ef15310

Please sign in to comment.