Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion .github/configs/os-check-linux.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,13 @@
"configure": ["--enable-all", "--enable-dtls13", "--enable-dtls-frag-ch",
"--disable-mlkem"]},
{"name": "all-ecc-blind-k", "minutes": 8.0,
"comment": "Only entry that sets WOLFSSL_ECC_BLIND_K (the blind-private-key entry sets WOLFSSL_BLIND_PRIVATE_KEY, which does not imply it). Keeps the read-only wc_ecc_key_get_priv() contract exercised in CI. pkcs11 is on because wc_pkcs11.c is not compiled anywhere else in this matrix.",
"comment": "WOLFSSL_ECC_BLIND_K with FP_ECC (the blind-private-key entry sets WOLFSSL_BLIND_PRIVATE_KEY, which does not imply it). Keeps the read-only wc_ecc_key_get_priv() contract exercised in CI. pkcs11 is on because wc_pkcs11.c is not compiled anywhere else in this matrix.",
"configure": ["--enable-all", "--enable-pkcs11",
"CPPFLAGS=-DWOLFSSL_ECC_BLIND_K"]},
{"name": "ecc-blind-k-small-curves", "minutes": 8.0,
"comment": "WOLFSSL_ECC_BLIND_K with the curves whose order is longer than the prime (secp160*, secp224k1), which FP_ECC compiles out.",
"configure": ["--enable-all", "--disable-fpecc", "--with-eccminsz=160",
"CPPFLAGS=-DWOLFSSL_ECC_BLIND_K"]},
{"name": "all-check-mem-zero", "minutes": 7.9,
"configure": ["--enable-all", "CPPFLAGS=-DWOLFSSL_CHECK_MEM_ZERO"]},
{"name": "all-faultharden-pk-privkey", "minutes": 7.8,
Expand Down Expand Up @@ -291,6 +295,10 @@
{"name": "no-client-auth", "minutes": 1.8,
"configure": ["CPPFLAGS=-DWOLFSSL_NO_CLIENT_AUTH"]},
{"name": "harden-tls", "minutes": 1.7, "configure": ["--enable-harden-tls"]},
{"name": "ecc-asn-original-small-curves", "minutes": 1.7,
"comment": "Original ASN.1 code on the curves whose order is longer than the prime.",
"configure": ["--enable-asn=original", "--with-eccminsz=160",
"--enable-ecccustcurves=all"]},
{"name": "no-sni-ecc-tls13-scr-info", "minutes": 1.7,
"configure": ["--disable-sni", "--disable-ecc", "--disable-tls13",
"--disable-secure-renegotiation-info"]},
Expand Down Expand Up @@ -542,6 +550,11 @@
"comment": "RSA verify-only with ECC cofactor ECDH (HAVE_ECC_CDH, set by --enable-ecccustcurves=all), whose wc_ecc_shared_secret_gen_sync() calls mp_mul_d. SAKKE stays off, so the HAVE_ECC_CDH term in settings.h is the only reason sp_mul_d is built. This gives link-time coverage of that term only: the mp_mul_d call runs only when cofactor != 1, and no curve at or above ECC_MIN_KEY_SZ has another cofactor.",
"configure": ["--enable-cryptonly", "--enable-rsavfy",
"--enable-ecccustcurves=all", "--disable-examples"]},
{"name": "ecc-no-asn-small-curves", "minutes": 0.5,
"comment": "Raw r||s ECDSA signatures (NO_ASN) on the curves whose order is longer than the prime.",
"configure": ["--enable-cryptonly", "--enable-asn=no", "--disable-rsa",
"--disable-dh", "--disable-fpecc", "--with-eccminsz=160",
"--enable-ecccustcurves=all"]},
{"name": "pkcs12-pbkdf-mp-api", "minutes": 1.0,
"comment": "WC_PKCS12_PBKDF_USING_MP_API selects a second wc_PKCS12_PBKDF_ex implementation that no configure option reaches.",
"configure": ["--enable-pkcs12", "--enable-des3", "--enable-opensslextra",
Expand Down
195 changes: 195 additions & 0 deletions tests/api/test_ecc.c
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,201 @@ int test_wc_ecc_shared_secret(void)
return EXPECT_RESULT();
} /* END tests_wc_ecc_shared_secret */

#if defined(HAVE_ECC) && defined(HAVE_ECC_DHE) && !defined(WC_NO_RNG) && \
defined(HAVE_ECC_KEY_IMPORT) && defined(HAVE_ECC_KEY_EXPORT) && \
!defined(WOLFSSL_SP_MATH) && !defined(WOLFSSL_VALIDATE_ECC_IMPORT) && \
!defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(HAVE_SELFTEST) && \
!defined(HAVE_FIPS) && !defined(NO_ECC_MAKE_PUB)
#define TEST_ECC_ORDER_WIDER_THAN_FIELD
/* Load a private key from raw bytes or DER and check its ECDH result with
* peer is expected. */
static int test_ecc_order_key_ecdh(const byte* priv, word32 privSz,
const byte* der, word32 derSz, ecc_key* peer, int curveId, WC_RNG* rng,
const byte* expected, word32 expectedSz)
{
EXPECT_DECLS;
ecc_key key;
byte secret[MAX_ECC_ORDER_BYTES];
word32 secretSz = (word32)sizeof(secret);
word32 idx = 0;

XMEMSET(&key, 0, sizeof(key));
ExpectIntEQ(wc_ecc_init(&key), 0);
if (der == NULL) {
ExpectIntEQ(wc_ecc_import_private_key_ex(priv, privSz, NULL, 0, &key,
curveId), 0);
}
#if !defined(NO_ASN) && !defined(NO_ASN_CRYPT)
else {
ExpectIntEQ(wc_EccPrivateKeyDecode(der, &idx, &key, derSz), 0);
}
#else
(void)derSz;
(void)idx;
#endif
#ifdef ECC_TIMING_RESISTANT
ExpectIntEQ(wc_ecc_set_rng(&key, rng), 0);
#else
(void)rng;
#endif
ExpectIntEQ(wc_ecc_shared_secret(&key, peer, secret, &secretSz), 0);
ExpectIntEQ(secretSz, expectedSz);
ExpectBufEQ(secret, expected, expectedSz);
wc_ecc_free(&key);

return EXPECT_RESULT();
}
#endif

/*
* Testing the private key n - 1 on curves whose order has one bit more than
* the field. [n - 1]G is -G, so its x-ordinate is Gx.
*/
int test_wc_ecc_order_wider_than_field(void)
{
EXPECT_DECLS;
#ifdef TEST_ECC_ORDER_WIDER_THAN_FIELD
static const int curves[] = {
ECC_SECP160R1, ECC_SECP160R2, ECC_SECP160K1, ECC_SECP224K1
};
const ecc_set_type* dp = NULL;
ecc_key key;
ecc_key peer;
WC_RNG rng;
char d[2 * MAX_ECC_ORDER_BYTES + 1];
byte secret[MAX_ECC_ORDER_BYTES];
byte gx[MAX_ECC_ORDER_BYTES];
byte gy[MAX_ECC_ORDER_BYTES];
byte x[MAX_ECC_ORDER_BYTES];
byte y[MAX_ECC_ORDER_BYTES];
byte priv[MAX_ECC_ORDER_BYTES];
#if !defined(NO_ASN) && !defined(NO_ASN_CRYPT)
byte der[ECC_BUFSIZE];
int derSz = 0;
#endif
word32 privSz;
word32 secretSz;
word32 gxSz;
word32 gySz;
word32 xSz;
word32 ySz;
size_t len = 0;
int i;

XMEMSET(&rng, 0, sizeof(rng));
ExpectIntEQ(wc_InitRng(&rng), 0);

for (i = 0; i < (int)(sizeof(curves) / sizeof(curves[0])); i++) {
if (wc_ecc_get_curve_idx(curves[i]) < 0) {
continue;
}
ExpectNotNull(dp = wc_ecc_get_curve_params(
wc_ecc_get_curve_idx(curves[i])));
if (dp == NULL) {
break;
}
/* The order is prime, so n - 1 only changes the last hex digit. */
if (dp != NULL) {
len = XSTRLEN(dp->order);
}
ExpectIntGT(len, 0);
ExpectIntLT(len, sizeof(d));
if (EXPECT_SUCCESS()) {
XMEMCPY(d, dp->order, len + 1);
d[len - 1]--;
}

XMEMSET(&key, 0, sizeof(key));
XMEMSET(&peer, 0, sizeof(peer));
ExpectIntEQ(wc_ecc_init(&key), 0);
ExpectIntEQ(wc_ecc_init(&peer), 0);
ExpectIntEQ(wc_ecc_import_raw_ex(&peer, dp->Gx, dp->Gy, NULL,
curves[i]), 0);
ExpectIntEQ(wc_ecc_import_raw_ex(&key, dp->Gx, dp->Gy, d, curves[i]),
0);
#ifdef ECC_TIMING_RESISTANT
ExpectIntEQ(wc_ecc_set_rng(&key, &rng), 0);
#endif
gxSz = (word32)sizeof(gx);
gySz = (word32)sizeof(gy);
ExpectIntEQ(wc_ecc_export_public_raw(&peer, gx, &gxSz, gy, &gySz), 0);

secretSz = (word32)sizeof(secret);
ExpectIntEQ(wc_ecc_shared_secret(&key, &peer, secret, &secretSz), 0);
ExpectIntEQ(secretSz, gxSz);
ExpectBufEQ(secret, gx, gxSz);

/* The public key made from n - 1 is -G: same x, other y. */
ExpectIntEQ(wc_ecc_make_pub(&key, NULL), 0);
xSz = (word32)sizeof(x);
ySz = (word32)sizeof(y);
ExpectIntEQ(wc_ecc_export_public_raw(&key, x, &xSz, y, &ySz), 0);
ExpectIntEQ(xSz, gxSz);
ExpectBufEQ(x, gx, gxSz);
ExpectIntEQ(ySz, gySz);
ExpectBufNE(y, gy, gySz);

/* The raw export stays at the prime size, which n - 1 exceeds. */
privSz = (word32)sizeof(priv);
ExpectIntEQ(wc_ecc_export_private_only(&key, priv, &privSz),
WC_NO_ERR_TRACE(BUFFER_E));

#if !defined(NO_ASN) && !defined(NO_ASN_CRYPT)
/* DER holds the private key at the size of the order. */
ExpectIntGT(derSz = wc_EccKeyToDer(&key, der, (word32)sizeof(der)), 0);
ExpectIntEQ(wc_EccKeyDerSize(&key, 1), derSz);
ExpectIntEQ(test_ecc_order_key_ecdh(NULL, 0, der, (word32)derSz,
&peer, curves[i], &rng, gx, gxSz), TEST_SUCCESS);
ExpectIntGT(derSz = wc_EccPrivateKeyToDer(&key, der,
(word32)sizeof(der)), 0);
ExpectIntEQ(wc_EccKeyDerSize(&key, 0), derSz);
ExpectIntEQ(der[5], ASN_OCTET_STRING);
ExpectIntEQ(der[6], gxSz + 1);
ExpectIntEQ(test_ecc_order_key_ecdh(NULL, 0, der, (word32)derSz,
&peer, curves[i], &rng, gx, gxSz), TEST_SUCCESS);
/* Without a curve id the order size still selects the curve. */
if ((curves[i] == ECC_SECP160R1) || (curves[i] == ECC_SECP224K1)) {
ExpectIntEQ(test_ecc_order_key_ecdh(der + 7, gxSz + 1, NULL, 0,
&peer, ECC_CURVE_DEF, &rng, gx, gxSz), TEST_SUCCESS);
}
ExpectIntLT(wc_EccPrivateKeyToDer(&key, der, (word32)derSz - 1), 0);
#endif

/* A small key exports raw at the prime size, which
* wc_ecc_import_unsigned() reads, and in DER at the order size. */
wc_ecc_free(&key);
XMEMSET(&key, 0, sizeof(key));
ExpectIntEQ(wc_ecc_init(&key), 0);
ExpectIntEQ(wc_ecc_import_raw_ex(&key, dp->Gx, dp->Gy, "01",
curves[i]), 0);
privSz = (word32)sizeof(priv);
ExpectIntEQ(wc_ecc_export_private_only(&key, priv, &privSz), 0);
ExpectIntEQ(privSz, gxSz);
ExpectIntEQ(priv[privSz - 1], 1);
ExpectIntEQ(test_ecc_order_key_ecdh(priv, privSz, NULL, 0, &peer,
curves[i], &rng, gx, gxSz), TEST_SUCCESS);
xSz = (word32)sizeof(x);
ySz = (word32)sizeof(y);
privSz = (word32)sizeof(priv);
ExpectIntEQ(wc_ecc_export_private_raw(&key, x, &xSz, y, &ySz, priv,
&privSz), 0);
ExpectIntEQ(privSz, gxSz);
ExpectIntEQ(priv[privSz - 1], 1);
#if !defined(NO_ASN) && !defined(NO_ASN_CRYPT)
ExpectIntGT(wc_EccPrivateKeyToDer(&key, der, (word32)sizeof(der)), 0);
ExpectIntEQ(der[6], gxSz + 1);
ExpectIntEQ(der[7], 0);
#endif

wc_ecc_free(&peer);
wc_ecc_free(&key);
}

DoExpectIntEQ(wc_FreeRng(&rng), 0);
#endif
return EXPECT_RESULT();
} /* END test_wc_ecc_order_wider_than_field */

/* ECC_INF_E rejection is not present in the frozen ecc.c of older
* FIPS-certified modules, so restrict to non-FIPS or FIPS v7 and later, and
* skip the CAVP selftest build. The hardware ports listed here either do not
Expand Down
2 changes: 2 additions & 0 deletions tests/api/test_ecc.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ int test_wc_ecc_size(void);
int test_wc_ecc_params(void);
int test_wc_ecc_signVerify_hash(void);
int test_wc_ecc_shared_secret(void);
int test_wc_ecc_order_wider_than_field(void);
int test_wc_ecc_shared_secret_at_infinity(void);
int test_wc_ecc_shared_secret_ex_peer_point(void);
int test_wc_ecc_shared_secret_size_bounds(void);
Expand Down Expand Up @@ -103,6 +104,7 @@ int test_wc_ecc_make_key_id_dev(void);
TEST_DECL_GROUP("ecc", test_wc_ecc_params), \
TEST_DECL_GROUP("ecc", test_wc_ecc_signVerify_hash), \
TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret), \
TEST_DECL_GROUP("ecc", test_wc_ecc_order_wider_than_field), \
TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret_at_infinity), \
TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret_ex_peer_point), \
TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret_size_bounds), \
Expand Down
6 changes: 3 additions & 3 deletions tests/unit-mcdc/test_ecc_whitebox.c
Original file line number Diff line number Diff line change
Expand Up @@ -1437,19 +1437,19 @@ static void wb_idx_dp_guard_export_paths(void)
key.idx = 9999; key.dp = cs;
qxLen = sizeof(qx); qyLen = sizeof(qy);
ret = _ecc_export_ex(&key, qx, &qxLen, qy, &qyLen, NULL, NULL,
WC_TYPE_UNSIGNED_BIN);
WC_TYPE_UNSIGNED_BIN, 0);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }

key.idx = idx; key.dp = NULL;
qxLen = sizeof(qx); qyLen = sizeof(qy);
ret = _ecc_export_ex(&key, qx, &qxLen, qy, &qyLen, NULL, NULL,
WC_TYPE_UNSIGNED_BIN);
WC_TYPE_UNSIGNED_BIN, 0);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }

key.idx = idx; key.dp = cs; /* baseline: real export */
qxLen = sizeof(qx); qyLen = sizeof(qy);
ret = _ecc_export_ex(&key, qx, &qxLen, qy, &qyLen, NULL, NULL,
WC_TYPE_UNSIGNED_BIN);
WC_TYPE_UNSIGNED_BIN, 0);
if (ret != MP_OKAY) { wb_fail = 1; }

/* ---- wc_ecc_export_public_raw: qx/qxLen/qy/qyLen NULL guard,
Expand Down
15 changes: 12 additions & 3 deletions wolfcrypt/src/asn.c
Original file line number Diff line number Diff line change
Expand Up @@ -35728,6 +35728,12 @@ int wc_EccPublicKeyDecode(const byte* input, word32* inOutIdx,
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#ifdef HAVE_ECC_KEY_EXPORT
#if !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)
/* The private key is encoded at the size of the order (RFC 5915). */
#define EccExportPrivateDer wc_ecc_export_private_order
#else
#define EccExportPrivateDer wc_ecc_export_private_only
#endif
/* build DER formatted ECC key, include optional public key if requested,
* return length on success, negative on error */
#ifdef WOLFSSL_ASN_TEMPLATE
Expand Down Expand Up @@ -35755,8 +35761,11 @@ int wc_BuildEccKeyDer(ecc_key* key, byte* output, word32 *outLen,
CALLOC_ASNSETDATA(dataASN, eccKeyASN_Length, ret, key->heap);

if (ret == 0) {
/* Private key size is the curve size. */
privSz = (word32)key->dp->size;
/* Private key size is reported by the export, else the curve size. */
if (EccExportPrivateDer(key, NULL, &privSz) !=
WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
privSz = (word32)key->dp->size;
}
if (pubIn) {
/* Get the length of the public key. */
PRIVATE_KEY_UNLOCK();
Expand Down Expand Up @@ -35830,7 +35839,7 @@ int wc_BuildEccKeyDer(ecc_key* key, byte* output, word32 *outLen,
/* Export the private value into the buffer. */
PRIVATE_KEY_UNLOCK();
/* safe cast -- the pointer is actually inside the output buffer. */
ret = wc_ecc_export_private_only(
ret = EccExportPrivateDer(
key,
(byte*)(wc_ptr_t)
dataASN[ECCKEYASN_IDX_PKEY].data.buffer.data,
Expand Down
25 changes: 14 additions & 11 deletions wolfcrypt/src/asn_orig.c
Original file line number Diff line number Diff line change
Expand Up @@ -8278,7 +8278,7 @@ int wc_BuildEccKeyDer(ecc_key* key, byte* output, word32 *inLen,
int privHdrSz = ASN_ECC_HEADER_SZ;
int pubHdrSz = ASN_ECC_CONTEXT_SZ + ASN_ECC_HEADER_SZ;
#ifdef WOLFSSL_NO_MALLOC
byte prv[MAX_ECC_BYTES + ASN_ECC_HEADER_SZ + MAX_SEQ_SZ];
byte prv[MAX_ECC_BYTES + 1 + ASN_ECC_HEADER_SZ + MAX_SEQ_SZ];
byte pub[(MAX_ECC_BYTES * 2) + 1 + ASN_ECC_CONTEXT_SZ +
ASN_ECC_HEADER_SZ + MAX_SEQ_SZ];
#else
Expand All @@ -8288,7 +8288,7 @@ int wc_BuildEccKeyDer(ecc_key* key, byte* output, word32 *inLen,
word32 idx = 0, prvidx = 0, pubidx = 0, curveidx = 0;
word32 seqSz, privSz, pubSz = ECC_BUFSIZE;

if (key == NULL || (output == NULL && inLen == NULL))
if (key == NULL || key->dp == NULL || (output == NULL && inLen == NULL))
return BAD_FUNC_ARG;

if (curveIn) {
Expand All @@ -8303,15 +8303,18 @@ int wc_BuildEccKeyDer(ecc_key* key, byte* output, word32 *inLen,
curveidx += (word32)curveSz;
}

/* private */
privSz = (word32)key->dp->size;

#ifdef WOLFSSL_QNX_CAAM
/* check if is a black key, and add MAC size if needed */
if (key->blackKey > 0 && key->blackKey != CAAM_BLACK_KEY_ECB) {
privSz = privSz + WC_CAAM_MAC_SZ;
/* private: the export reports its size, which is the size of the order or
* of a black key. */
if (EccExportPrivateDer(key, NULL, &privSz) !=
WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
privSz = (word32)key->dp->size;
#ifdef WOLFSSL_QNX_CAAM
/* check if is a black key, and add MAC size if needed */
if (key->blackKey > 0 && key->blackKey != CAAM_BLACK_KEY_ECB) {
privSz = privSz + WC_CAAM_MAC_SZ;
}
#endif
}
#endif

#ifndef WOLFSSL_NO_MALLOC
prv = (byte*)XMALLOC(privSz + (word32)privHdrSz + MAX_SEQ_SZ,
Expand All @@ -8331,7 +8334,7 @@ int wc_BuildEccKeyDer(ecc_key* key, byte* output, word32 *inLen,
prvidx += SetOctetString(privSz, &prv[prvidx]);
}
PRIVATE_KEY_UNLOCK();
ret = wc_ecc_export_private_only(key, prv + prvidx, &privSz);
ret = EccExportPrivateDer(key, prv + prvidx, &privSz);
PRIVATE_KEY_LOCK();
if (ret < 0) {
#ifndef WOLFSSL_NO_MALLOC
Expand Down
Loading
Loading