Skip to content

Commit ef15310

Browse files
Merge pull request #7433 from SparkiDev/eddsa_fips_checks
EdDSA FIPS checks on public key
2 parents 9666e4d + 0bd5967 commit ef15310

4 files changed

Lines changed: 442 additions & 22 deletions

File tree

configure.ac

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4994,12 +4994,12 @@ AS_CASE([$FIPS_VERSION],
49944994
[AM_CFLAGS="$AM_CFLAGS -DECC_SHAMIR"])])
49954995
49964996
AS_IF([test "x$ENABLED_ED25519" != "xyes"],
4997-
[ENABLED_ED25519="yes"; AM_CFLAGS="$AM_CFLAGS -DHAVE_ED25519"])
4997+
[ENABLED_ED25519="yes"; AM_CFLAGS="$AM_CFLAGS -DHAVE_ED25519 -DHAVE_ED25519_KEY_IMPORT"])
49984998
AS_IF([test "x$ENABLED_CURVE25519" != "xyes"],
49994999
[ENABLED_CURVE25519="yes"; AM_CFLAGS="$AM_CFLAGS -DHAVE_CURVE25519"])
50005000
50015001
AS_IF([test "x$ENABLED_ED448" != "xyes"],
5002-
[ENABLED_ED448="yes"; AM_CFLAGS="$AM_CFLAGS -DHAVE_ED448"])
5002+
[ENABLED_ED448="yes"; AM_CFLAGS="$AM_CFLAGS -DHAVE_ED448 -DHAVE_ED448_KEY_IMPORT"])
50035003
AS_IF([test "x$ENABLED_CURVE448" != "xyes"],
50045004
[ENABLED_CURVE448="yes"; AM_CFLAGS="$AM_CFLAGS -DHAVE_CURVE448"])
50055005

wolfcrypt/src/ed25519.c

Lines changed: 127 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,56 @@ static int ed25519_hash(ed25519_key* key, const byte* in, word32 inLen,
208208
}
209209

210210
#ifdef HAVE_ED25519_MAKE_KEY
211+
#if FIPS_VERSION3_GE(6,0,0)
212+
/* Performs a Pairwise Consistency Test on an Ed25519 key pair.
213+
*
214+
* @param [in] key Ed25519 key to test.
215+
* @param [in] rng Random number generator to use to create random digest.
216+
* @return 0 on success.
217+
* @return ECC_PCT_E when signing or verification fail.
218+
* @return Other -ve when random number generation fails.
219+
*/
220+
static int ed25519_pairwise_consistency_test(ed25519_key* key, WC_RNG* rng)
221+
{
222+
int err = 0;
223+
byte digest[WC_SHA512_DIGEST_SIZE];
224+
word32 digestLen = WC_SHA512_DIGEST_SIZE;
225+
byte sig[ED25519_SIG_SIZE];
226+
word32 sigLen = ED25519_SIG_SIZE;
227+
int res = 0;
228+
229+
/* Generate a random digest to sign. */
230+
err = wc_RNG_GenerateBlock(rng, digest, digestLen);
231+
if (err == 0) {
232+
/* Sign digest without context. */
233+
err = wc_ed25519_sign_msg_ex(digest, digestLen, sig, &sigLen, key,
234+
Ed25519, NULL, 0);
235+
if (err != 0) {
236+
/* Any sign failure means test failed. */
237+
err = ECC_PCT_E;
238+
}
239+
}
240+
if (err == 0) {
241+
/* Verify digest without context. */
242+
err = wc_ed25519_verify_msg_ex(sig, sigLen, digest, digestLen, &res,
243+
key, Ed25519, NULL, 0);
244+
if (err != 0) {
245+
/* Any verification operation failure means test failed. */
246+
err = ECC_PCT_E;
247+
}
248+
/* Check whether the signature verified. */
249+
else if (res == 0) {
250+
/* Test failed. */
251+
err = ECC_PCT_E;
252+
}
253+
}
254+
255+
ForceZero(sig, sigLen);
256+
257+
return err;
258+
}
259+
#endif
260+
211261
int wc_ed25519_make_public(ed25519_key* key, unsigned char* pubKey,
212262
word32 pubKeySz)
213263
{
@@ -291,6 +341,13 @@ int wc_ed25519_make_key(WC_RNG* rng, int keySz, ed25519_key* key)
291341
/* put public key after private key, on the same buffer */
292342
XMEMMOVE(key->k + ED25519_KEY_SIZE, key->p, ED25519_PUB_KEY_SIZE);
293343

344+
#if FIPS_VERSION3_GE(6,0,0)
345+
ret = wc_ed25519_check_key(key);
346+
if (ret == 0) {
347+
ret = ed25519_pairwise_consistency_test(key, rng);
348+
}
349+
#endif
350+
294351
return ret;
295352
}
296353
#endif /* HAVE_ED25519_MAKE_KEY */
@@ -1077,7 +1134,7 @@ int wc_ed25519_import_public_ex(const byte* in, word32 inLen, ed25519_key* key,
10771134

10781135
if (ret == 0) {
10791136
key->pubKeySet = 1;
1080-
if (key->privKeySet && (!trusted)) {
1137+
if (!trusted) {
10811138
ret = wc_ed25519_check_key(key);
10821139
}
10831140
}
@@ -1278,23 +1335,84 @@ int wc_ed25519_export_key(ed25519_key* key,
12781335

12791336
#endif /* HAVE_ED25519_KEY_EXPORT */
12801337

1281-
/* check the private and public keys match */
1338+
/* Check the public key is valid.
1339+
*
1340+
* When private key available, check the calculated public key matches.
1341+
* When no private key, check Y is in range and an X is able to be calculated.
1342+
*
1343+
* @param [in] key Ed25519 private/public key.
1344+
* @return 0 otherwise.
1345+
* @return BAD_FUNC_ARG when key is NULL.
1346+
* @return PUBLIC_KEY_E when the public key is not set, doesn't match or is
1347+
* invalid.
1348+
* @return other -ve value on hash failure.
1349+
*/
12821350
int wc_ed25519_check_key(ed25519_key* key)
12831351
{
12841352
int ret = 0;
1285-
#ifdef HAVE_ED25519_MAKE_KEY
1286-
ALIGN16 unsigned char pubKey[ED25519_PUB_KEY_SIZE];
12871353

1288-
if (!key->pubKeySet)
1354+
/* Validate parameter. */
1355+
if (key == NULL) {
1356+
ret = BAD_FUNC_ARG;
1357+
}
1358+
1359+
/* Check we have a public key to check. */
1360+
if ((ret == 0) && (!key->pubKeySet)) {
12891361
ret = PUBLIC_KEY_E;
1290-
if (ret == 0)
1362+
}
1363+
1364+
#ifdef HAVE_ED25519_MAKE_KEY
1365+
/* If we have a private key just make the public key and compare. */
1366+
if ((ret == 0) && (key->privKeySet)) {
1367+
ALIGN16 unsigned char pubKey[ED25519_PUB_KEY_SIZE];
1368+
12911369
ret = wc_ed25519_make_public(key, pubKey, sizeof(pubKey));
1292-
if (ret == 0 && XMEMCMP(pubKey, key->p, ED25519_PUB_KEY_SIZE) != 0)
1293-
ret = PUBLIC_KEY_E;
1370+
if (ret == 0 && XMEMCMP(pubKey, key->p, ED25519_PUB_KEY_SIZE) != 0)
1371+
ret = PUBLIC_KEY_E;
1372+
}
12941373
#else
1295-
(void)key;
1374+
(void)key;
12961375
#endif /* HAVE_ED25519_MAKE_KEY */
12971376

1377+
/* No private key (or ability to make a public key), check Y is valid. */
1378+
if ((ret == 0)
1379+
#ifdef HAVE_ED25519_MAKE_KEY
1380+
&& (!key->privKeySet)
1381+
#endif
1382+
) {
1383+
/* Verify that Q is not identity element 0.
1384+
* 0 has no representation for Ed25519. */
1385+
1386+
/* Verify that xQ and yQ are integers in the interval [0, p - 1].
1387+
* Only have yQ so check that ordinate. p = 2^255 - 19 */
1388+
if ((key->p[ED25519_PUB_KEY_SIZE - 1] & 0x7f) == 0x7f) {
1389+
int i;
1390+
1391+
ret = PUBLIC_KEY_E;
1392+
/* Check up to last byte. */
1393+
for (i = ED25519_PUB_KEY_SIZE - 2; i > 0; i--) {
1394+
if (key->p[i] != 0xff) {
1395+
ret = 0;
1396+
break;
1397+
}
1398+
}
1399+
/* Bits are all one up to last byte - check less than -19. */
1400+
if ((ret == PUBLIC_KEY_E) && (key->p[0] < 0xed)) {
1401+
ret = 0;
1402+
}
1403+
}
1404+
1405+
if (ret == 0) {
1406+
/* Verify that Q is on the curve.
1407+
* Uncompressing the public key will validate yQ. */
1408+
ge_p3 A;
1409+
1410+
if (ge_frombytes_negate_vartime(&A, key->p) != 0) {
1411+
ret = PUBLIC_KEY_E;
1412+
}
1413+
}
1414+
}
1415+
12981416
return ret;
12991417
}
13001418

wolfcrypt/src/ed448.c

Lines changed: 127 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,56 @@ static int ed448_hash(ed448_key* key, const byte* in, word32 inLen,
187187
return ret;
188188
}
189189

190+
#if FIPS_VERSION3_GE(6,0,0)
191+
/* Performs a Pairwise Consistency Test on an Ed448 key pair.
192+
*
193+
* @param [in] key Ed448 key to test.
194+
* @param [in] rng Random number generator to use to create random digest.
195+
* @return 0 on success.
196+
* @return ECC_PCT_E when signing or verification fail.
197+
* @return Other -ve when random number generation fails.
198+
*/
199+
static int ed448_pairwise_consistency_test(ed448_key* key, WC_RNG* rng)
200+
{
201+
int err = 0;
202+
byte digest[WC_SHA256_DIGEST_SIZE];
203+
word32 digestLen = WC_SHA256_DIGEST_SIZE;
204+
byte sig[ED448_SIG_SIZE];
205+
word32 sigLen = ED448_SIG_SIZE;
206+
int res = 0;
207+
208+
/* Generate a random digest to sign. */
209+
err = wc_RNG_GenerateBlock(rng, digest, digestLen);
210+
if (err == 0) {
211+
/* Sign digest without context. */
212+
err = wc_ed448_sign_msg_ex(digest, digestLen, sig, &sigLen, key, Ed448,
213+
NULL, 0);
214+
if (err != 0) {
215+
/* Any sign failure means test failed. */
216+
err = ECC_PCT_E;
217+
}
218+
}
219+
if (err == 0) {
220+
/* Verify digest without context. */
221+
err = wc_ed448_verify_msg_ex(sig, sigLen, digest, digestLen, &res, key,
222+
Ed448, NULL, 0);
223+
if (err != 0) {
224+
/* Any verification operation failure means test failed. */
225+
err = ECC_PCT_E;
226+
}
227+
/* Check whether the signature verified. */
228+
else if (res == 0) {
229+
/* Test failed. */
230+
err = ECC_PCT_E;
231+
}
232+
}
233+
234+
ForceZero(sig, sigLen);
235+
236+
return err;
237+
}
238+
#endif
239+
190240
/* Derive the public key for the private key.
191241
*
192242
* key [in] Ed448 key object.
@@ -272,6 +322,13 @@ int wc_ed448_make_key(WC_RNG* rng, int keySz, ed448_key* key)
272322
if (ret == 0) {
273323
/* put public key after private key, on the same buffer */
274324
XMEMMOVE(key->k + ED448_KEY_SIZE, key->p, ED448_PUB_KEY_SIZE);
325+
326+
#if FIPS_VERSION3_GE(6,0,0)
327+
ret = wc_ed448_check_key(key);
328+
if (ret == 0) {
329+
ret = ed448_pairwise_consistency_test(key, rng);
330+
}
331+
#endif
275332
}
276333

277334
return ret;
@@ -966,7 +1023,7 @@ int wc_ed448_import_public_ex(const byte* in, word32 inLen, ed448_key* key,
9661023
ret = BAD_FUNC_ARG;
9671024
}
9681025

969-
if (inLen != ED448_PUB_KEY_SIZE) {
1026+
if ((inLen != ED448_PUB_KEY_SIZE) && (inLen != ED448_PUB_KEY_SIZE + 1)) {
9701027
ret = BAD_FUNC_ARG;
9711028
}
9721029

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

9961053
if (ret == 0) {
9971054
key->pubKeySet = 1;
998-
if (key->privKeySet && (!trusted)) {
1055+
if (!trusted) {
9991056
/* Check untrusted public key data matches private key. */
10001057
ret = wc_ed448_check_key(key);
10011058
}
@@ -1243,31 +1300,90 @@ int wc_ed448_export_key(ed448_key* key, byte* priv, word32 *privSz,
12431300

12441301
#endif /* HAVE_ED448_KEY_EXPORT */
12451302

1246-
/* Check the public key of the ed448 key matches the private key.
1303+
/* Check the public key is valid.
12471304
*
1248-
* key [in] Ed448 private/public key.
1249-
* returns BAD_FUNC_ARG when key is NULL,
1250-
* PUBLIC_KEY_E when the public key is not set or doesn't match,
1251-
* other -ve value on hash failure,
1252-
* 0 otherwise.
1305+
* When private key available, check the calculated public key matches.
1306+
* When no private key, check Y is in range and an X is able to be calculated.
1307+
*
1308+
* @param [in] key Ed448 private/public key.
1309+
* @return 0 otherwise.
1310+
* @return BAD_FUNC_ARG when key is NULL.
1311+
* @return PUBLIC_KEY_E when the public key is not set, doesn't match or is
1312+
* invalid.
1313+
* @return other -ve value on hash failure.
12531314
*/
12541315
int wc_ed448_check_key(ed448_key* key)
12551316
{
12561317
int ret = 0;
12571318
unsigned char pubKey[ED448_PUB_KEY_SIZE];
12581319

1320+
/* Validate parameter. */
12591321
if (key == NULL) {
12601322
ret = BAD_FUNC_ARG;
12611323
}
12621324

1325+
/* Check we have a public key to check. */
12631326
if (ret == 0 && !key->pubKeySet) {
12641327
ret = PUBLIC_KEY_E;
12651328
}
1266-
if (ret == 0) {
1329+
1330+
/* If we have a private key just make the public key and compare. */
1331+
if ((ret == 0) && key->privKeySet) {
12671332
ret = wc_ed448_make_public(key, pubKey, sizeof(pubKey));
1333+
if ((ret == 0) && (XMEMCMP(pubKey, key->p, ED448_PUB_KEY_SIZE) != 0)) {
1334+
ret = PUBLIC_KEY_E;
1335+
}
12681336
}
1269-
if ((ret == 0) && (XMEMCMP(pubKey, key->p, ED448_PUB_KEY_SIZE) != 0)) {
1270-
ret = PUBLIC_KEY_E;
1337+
/* No private key, check Y is valid. */
1338+
else if ((ret == 0) && (!key->privKeySet)) {
1339+
/* Verify that Q is not identity element 0.
1340+
* 0 has no representation for Ed448. */
1341+
1342+
/* Verify that xQ and yQ are integers in the interval [0, p - 1].
1343+
* Only have yQ so check that ordinate.
1344+
* p = 2^448-2^224-1 = 0xff..fe..ff
1345+
*/
1346+
{
1347+
int i;
1348+
ret = PUBLIC_KEY_E;
1349+
1350+
/* Check top part before 0xFE. */
1351+
for (i = ED448_PUB_KEY_SIZE - 1; i > ED448_PUB_KEY_SIZE/2; i--) {
1352+
if (key->p[i] < 0xff) {
1353+
ret = 0;
1354+
break;
1355+
}
1356+
}
1357+
if (ret == PUBLIC_KEY_E) {
1358+
/* Check against 0xFE. */
1359+
if (key->p[ED448_PUB_KEY_SIZE/2] < 0xfe) {
1360+
ret = 0;
1361+
}
1362+
else if (key->p[ED448_PUB_KEY_SIZE/2] == 0xfe) {
1363+
/* Check bottom part before last byte. */
1364+
for (i = ED448_PUB_KEY_SIZE/2 - 1; i > 0; i--) {
1365+
if (key->p[i] != 0xff) {
1366+
ret = 0;
1367+
break;
1368+
}
1369+
}
1370+
/* Check last byte. */
1371+
if ((ret == PUBLIC_KEY_E) && (key->p[0] < 0xff)) {
1372+
ret = 0;
1373+
}
1374+
}
1375+
}
1376+
}
1377+
1378+
if (ret == 0) {
1379+
/* Verify that Q is on the curve.
1380+
* Uncompressing the public key will validate yQ. */
1381+
ge448_p2 A;
1382+
1383+
if (ge448_from_bytes_negate_vartime(&A, key->p) != 0) {
1384+
ret = PUBLIC_KEY_E;
1385+
}
1386+
}
12711387
}
12721388

12731389
return ret;

0 commit comments

Comments
 (0)