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
60 changes: 60 additions & 0 deletions tests/api/test_asn.c
Original file line number Diff line number Diff line change
Expand Up @@ -2481,3 +2481,63 @@ int test_wc_AsnFeatureCoverage(void)
#endif /* !NO_ASN && HAVE_ECC && USE_CERT_BUFFERS_256 && !HAVE_FIPS */
return EXPECT_RESULT();
}

/* wc_EccPrivateKeyDecode should derive and cache the public point (best
* effort) when it decodes a SEC1 private key whose optional public point
* was omitted, so the key comes out fully usable. */
int test_wc_EccPrivateKeyDecode_derive_pub(void)
{
EXPECT_DECLS;
#if !defined(NO_ASN) && defined(HAVE_ECC) && !defined(NO_ECC_MAKE_PUB) && \
defined(USE_CERT_BUFFERS_256) && !defined(HAVE_FIPS) && \
!defined(WOLFSSL_ATECC508A) && !defined(WOLFSSL_ATECC608A) && \
!defined(WOLFSSL_MICROCHIP_TA100) && !defined(WOLFSSL_CRYPTOCELL) && \
!defined(WOLFSSL_SILABS_SE_ACCEL) && !defined(WOLFSSL_KCAPI_ECC) && \
!defined(WOLFSSL_QNX_CAAM) && !defined(WOLFSSL_IMXRT1170_CAAM)
ecc_key fullKey;
ecc_key privOnlyKey;
word32 idx;
byte privOnlyDer[256];
int privOnlyDerSz;
byte fullPub[256];
word32 fullPubSz = sizeof(fullPub);
byte derivedPub[256];
word32 derivedPubSz = sizeof(derivedPub);

XMEMSET(&fullKey, 0, sizeof(fullKey));
XMEMSET(&privOnlyKey, 0, sizeof(privOnlyKey));

ExpectIntEQ(wc_ecc_init(&fullKey), 0);
idx = 0;
ExpectIntEQ(wc_EccPrivateKeyDecode(ecc_clikey_der_256, &idx, &fullKey,
sizeof_ecc_clikey_der_256), 0);
ExpectIntEQ(fullKey.type, ECC_PRIVATEKEY);
PRIVATE_KEY_UNLOCK();
ExpectIntEQ(wc_ecc_export_x963(&fullKey, fullPub, &fullPubSz), 0);
PRIVATE_KEY_LOCK();

/* Re-encode as a private-key-only SEC1 DER (no public point). */
ExpectIntGT(privOnlyDerSz = wc_EccPrivateKeyToDer(&fullKey, privOnlyDer,
sizeof(privOnlyDer)), 0);

ExpectIntEQ(wc_ecc_init(&privOnlyKey), 0);
idx = 0;
ExpectIntEQ(wc_EccPrivateKeyDecode(privOnlyDer, &idx, &privOnlyKey,
(word32)privOnlyDerSz), 0);

/* The public point should have been derived automatically, making the
* key fully usable rather than left as ECC_PRIVATEKEY_ONLY. */
ExpectIntEQ(privOnlyKey.type, ECC_PRIVATEKEY);
PRIVATE_KEY_UNLOCK();
ExpectIntEQ(wc_ecc_export_x963(&privOnlyKey, derivedPub, &derivedPubSz),
0);
PRIVATE_KEY_LOCK();
ExpectIntEQ(derivedPubSz, fullPubSz);
ExpectBufEQ(derivedPub, fullPub, fullPubSz);

wc_ecc_free(&privOnlyKey);
wc_ecc_free(&fullKey);
#endif /* !NO_ASN && HAVE_ECC && !NO_ECC_MAKE_PUB && USE_CERT_BUFFERS_256 &&
* !HAVE_FIPS */
return EXPECT_RESULT();
}
4 changes: 3 additions & 1 deletion tests/api/test_asn.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ int test_ToTraditional_ex_negative(void);
int test_ToTraditional_ex_mldsa_bad_params(void);
int test_wc_AsnDecisionCoverage(void);
int test_wc_AsnFeatureCoverage(void);
int test_wc_EccPrivateKeyDecode_derive_pub(void);

#define TEST_ASN_DECLS \
TEST_DECL_GROUP("asn", test_SetAsymKeyDer), \
Expand All @@ -65,6 +66,7 @@ int test_wc_AsnFeatureCoverage(void);
TEST_DECL_GROUP("asn", test_ToTraditional_ex_negative), \
TEST_DECL_GROUP("asn", test_ToTraditional_ex_mldsa_bad_params), \
TEST_DECL_GROUP("asn", test_wc_AsnDecisionCoverage), \
TEST_DECL_GROUP("asn", test_wc_AsnFeatureCoverage)
TEST_DECL_GROUP("asn", test_wc_AsnFeatureCoverage), \
TEST_DECL_GROUP("asn", test_wc_EccPrivateKeyDecode_derive_pub)

#endif /* WOLFCRYPT_TEST_ASN_H */
173 changes: 173 additions & 0 deletions tests/api/test_mldsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -7763,6 +7763,179 @@ int test_mldsa_make_key_from_seed(void)
return EXPECT_RESULT();
}

int test_mldsa_make_public_key(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_MLDSA) && defined(WOLFSSL_MLDSA_PRIVATE_KEY) && \
!defined(WOLFSSL_MLDSA_ASSIGN_KEY) && !defined(WOLFSSL_MLDSA_NO_MAKE_KEY)
wc_MlDsaKey* key;

key = (wc_MlDsaKey*)XMALLOC(sizeof(*key), NULL, DYNAMIC_TYPE_TMP_BUFFER);
ExpectNotNull(key);
if (key != NULL) {
XMEMSET(key, 0, sizeof(*key));
}

/* NULL key. */
ExpectIntEQ(wc_MlDsaKey_MakePublicKey(NULL),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));

#ifndef WOLFSSL_NO_ML_DSA_44
ExpectIntEQ(wc_MlDsaKey_Init(key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_MlDsaKey_SetParams(key, WC_ML_DSA_44), 0);

/* Private key not set yet. */
ExpectIntEQ(wc_MlDsaKey_MakePublicKey(key), WC_NO_ERR_TRACE(BAD_FUNC_ARG));

/* Import a known private-only key (no public key attached) and derive
* the public key from it. */
ExpectIntEQ(wc_MlDsaKey_ImportPrivRaw(key, bench_mldsa_44_key,
sizeof_bench_mldsa_44_key), 0);
ExpectIntEQ(key->pubKeySet, 0);

ExpectIntEQ(wc_MlDsaKey_MakePublicKey(key), 0);
ExpectIntEQ(key->pubKeySet, 1);
ExpectIntEQ(XMEMCMP(key->p, bench_mldsa_44_pubkey,
sizeof_bench_mldsa_44_pubkey), 0);

/* No-op when the public key is already set. */
ExpectIntEQ(wc_MlDsaKey_MakePublicKey(key), 0);

wc_MlDsaKey_Free(key);
#endif /* !WOLFSSL_NO_ML_DSA_44 */

#ifndef WOLFSSL_NO_ML_DSA_65
ExpectIntEQ(wc_MlDsaKey_Init(key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_MlDsaKey_SetParams(key, WC_ML_DSA_65), 0);

ExpectIntEQ(wc_MlDsaKey_ImportPrivRaw(key, bench_mldsa_65_key,
sizeof_bench_mldsa_65_key), 0);
ExpectIntEQ(key->pubKeySet, 0);

ExpectIntEQ(wc_MlDsaKey_MakePublicKey(key), 0);
ExpectIntEQ(key->pubKeySet, 1);
ExpectIntEQ(XMEMCMP(key->p, bench_mldsa_65_pubkey,
sizeof_bench_mldsa_65_pubkey), 0);

wc_MlDsaKey_Free(key);
#endif /* !WOLFSSL_NO_ML_DSA_65 */

#ifndef WOLFSSL_NO_ML_DSA_87
ExpectIntEQ(wc_MlDsaKey_Init(key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_MlDsaKey_SetParams(key, WC_ML_DSA_87), 0);

ExpectIntEQ(wc_MlDsaKey_ImportPrivRaw(key, bench_mldsa_87_key,
sizeof_bench_mldsa_87_key), 0);
ExpectIntEQ(key->pubKeySet, 0);

ExpectIntEQ(wc_MlDsaKey_MakePublicKey(key), 0);
ExpectIntEQ(key->pubKeySet, 1);
ExpectIntEQ(XMEMCMP(key->p, bench_mldsa_87_pubkey,
sizeof_bench_mldsa_87_pubkey), 0);

wc_MlDsaKey_Free(key);
#endif /* !WOLFSSL_NO_ML_DSA_87 */

XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return EXPECT_RESULT();
}

int test_mldsa_private_key_decode_derives_public_key(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_ASN1) && \
!defined(WOLFSSL_MLDSA_ASSIGN_KEY) && !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \
!defined(WOLFSSL_MLDSA_NO_SIGN) && !defined(WOLFSSL_MLDSA_NO_VERIFY) && \
defined(WOLFSSL_MLDSA_PUBLIC_KEY)
wc_MlDsaKey* privKey;
wc_MlDsaKey* pubKey;
word32 idx;

privKey = (wc_MlDsaKey*)XMALLOC(sizeof(*privKey), NULL,
DYNAMIC_TYPE_TMP_BUFFER);
ExpectNotNull(privKey);
pubKey = (wc_MlDsaKey*)XMALLOC(sizeof(*pubKey), NULL,
DYNAMIC_TYPE_TMP_BUFFER);
ExpectNotNull(pubKey);
if (privKey != NULL) {
XMEMSET(privKey, 0, sizeof(*privKey));
}
if (pubKey != NULL) {
XMEMSET(pubKey, 0, sizeof(*pubKey));
}

#ifndef WOLFSSL_NO_ML_DSA_44
ExpectIntEQ(wc_MlDsaKey_Init(privKey, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_MlDsaKey_SetParams(privKey, WC_ML_DSA_44), 0);

/* mldsa44_priv_only holds a private-key-only DER (no embedded public
* point). wc_MlDsaKey_PrivateKeyDecode should derive and cache the
* public key as a best-effort side effect of decoding it. */
idx = 0;
ExpectIntEQ(wc_MlDsaKey_PrivateKeyDecode(privKey, mldsa44_priv_only,
sizeof_mldsa44_priv_only, &idx), 0);
ExpectIntEQ(privKey->pubKeySet, 1);

/* Confirm the derived public key matches the known public key for the
* same key pair. */
ExpectIntEQ(wc_MlDsaKey_Init(pubKey, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_MlDsaKey_SetParams(pubKey, WC_ML_DSA_44), 0);
idx = 0;
ExpectIntEQ(wc_MlDsaKey_PublicKeyDecode(pubKey, mldsa44_pub_spki,
sizeof_mldsa44_pub_spki, &idx), 0);
ExpectIntEQ(XMEMCMP(privKey->p, pubKey->p, WC_MLDSA_44_PUB_KEY_SIZE), 0);

wc_MlDsaKey_Free(privKey);
wc_MlDsaKey_Free(pubKey);
#endif /* !WOLFSSL_NO_ML_DSA_44 */

#ifndef WOLFSSL_NO_ML_DSA_65
ExpectIntEQ(wc_MlDsaKey_Init(privKey, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_MlDsaKey_SetParams(privKey, WC_ML_DSA_65), 0);

idx = 0;
ExpectIntEQ(wc_MlDsaKey_PrivateKeyDecode(privKey, mldsa65_priv_only,
sizeof_mldsa65_priv_only, &idx), 0);
ExpectIntEQ(privKey->pubKeySet, 1);

ExpectIntEQ(wc_MlDsaKey_Init(pubKey, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_MlDsaKey_SetParams(pubKey, WC_ML_DSA_65), 0);
idx = 0;
ExpectIntEQ(wc_MlDsaKey_PublicKeyDecode(pubKey, mldsa65_pub_spki,
sizeof_mldsa65_pub_spki, &idx), 0);
ExpectIntEQ(XMEMCMP(privKey->p, pubKey->p, WC_MLDSA_65_PUB_KEY_SIZE), 0);

wc_MlDsaKey_Free(privKey);
wc_MlDsaKey_Free(pubKey);
#endif /* !WOLFSSL_NO_ML_DSA_65 */

#ifndef WOLFSSL_NO_ML_DSA_87
ExpectIntEQ(wc_MlDsaKey_Init(privKey, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_MlDsaKey_SetParams(privKey, WC_ML_DSA_87), 0);

idx = 0;
ExpectIntEQ(wc_MlDsaKey_PrivateKeyDecode(privKey, mldsa87_priv_only,
sizeof_mldsa87_priv_only, &idx), 0);
ExpectIntEQ(privKey->pubKeySet, 1);

ExpectIntEQ(wc_MlDsaKey_Init(pubKey, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_MlDsaKey_SetParams(pubKey, WC_ML_DSA_87), 0);
idx = 0;
ExpectIntEQ(wc_MlDsaKey_PublicKeyDecode(pubKey, mldsa87_pub_spki,
sizeof_mldsa87_pub_spki, &idx), 0);
ExpectIntEQ(XMEMCMP(privKey->p, pubKey->p, WC_MLDSA_87_PUB_KEY_SIZE), 0);

wc_MlDsaKey_Free(privKey);
wc_MlDsaKey_Free(pubKey);
#endif /* !WOLFSSL_NO_ML_DSA_87 */

XFREE(privKey, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(pubKey, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return EXPECT_RESULT();
}

int test_mldsa_sig_kats(void)
{
EXPECT_DECLS;
Expand Down
4 changes: 4 additions & 0 deletions tests/api/test_mldsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ int test_mldsa_public_der_decode(void);
int test_mldsa_der(void);
int test_mldsa_oneasymkey_version(void);
int test_mldsa_make_key_from_seed(void);
int test_mldsa_make_public_key(void);
int test_mldsa_private_key_decode_derives_public_key(void);
int test_mldsa_sig_kats(void);
int test_mldsa_sign_ctx_kats(void);
int test_mldsa_verify_ctx_kats(void);
Expand Down Expand Up @@ -75,6 +77,8 @@ int test_mldsa_legacy_shim(void);
TEST_DECL_GROUP("mldsa", test_mldsa_der), \
TEST_DECL_GROUP("mldsa", test_mldsa_oneasymkey_version), \
TEST_DECL_GROUP("mldsa", test_mldsa_make_key_from_seed), \
TEST_DECL_GROUP("mldsa", test_mldsa_make_public_key), \
TEST_DECL_GROUP("mldsa", test_mldsa_private_key_decode_derives_public_key), \
TEST_DECL_GROUP("mldsa", test_mldsa_sig_kats), \
TEST_DECL_GROUP("mldsa", test_mldsa_sign_ctx_kats), \
TEST_DECL_GROUP("mldsa", test_mldsa_verify_ctx_kats), \
Expand Down
25 changes: 25 additions & 0 deletions wolfcrypt/src/asn.c
Original file line number Diff line number Diff line change
Expand Up @@ -32719,6 +32719,31 @@ int wc_EccPrivateKeyDecode(const byte* input, word32* inOutIdx, ecc_key* key,
key, curve_id);
}

/* Based on ecc.c's HAVE_ECC_MAKE_PUB condition, which is local to
* that file. This guard is intentionally a superset: QNX_CAAM and
* IMXRT1170_CAAM are added because those ports store the private
* scalar as an opaque hardware "black key" where a software point
* multiply would be meaningless or unsafe. */
#if !defined(NO_ECC_MAKE_PUB) && !defined(WOLFSSL_ATECC508A) && \
!defined(WOLFSSL_ATECC608A) && !defined(WOLFSSL_MICROCHIP_TA100) && \
!defined(WOLFSSL_CRYPTOCELL) && !defined(WOLFSSL_SILABS_SE_ACCEL) && \
!defined(WOLFSSL_KCAPI_ECC) && !defined(WOLFSSL_QNX_CAAM) && \
!defined(WOLFSSL_IMXRT1170_CAAM)
if ((ret == 0) && (key->type == ECC_PRIVATEKEY_ONLY)) {
/* SEC1 allows omitting the public point; derive it. Best-effort:
* failure must not fail decoding of an otherwise valid key. */
int pubRet;
#ifdef ECC_TIMING_RESISTANT
pubRet = wc_ecc_make_pub_ex(key, NULL, key->rng);
#else
pubRet = wc_ecc_make_pub_ex(key, NULL, NULL);
#endif
if (pubRet != 0) {
WOLFSSL_MSG("Best-effort ECC public key derivation failed");
}
}
#endif

FREE_ASNGETDATA(dataASN, key != NULL ? key->heap : NULL);
return ret;
}
Expand Down
25 changes: 25 additions & 0 deletions wolfcrypt/src/asn_orig.c
Original file line number Diff line number Diff line change
Expand Up @@ -7707,6 +7707,31 @@ int wc_EccPrivateKeyDecode(const byte* input, word32* inOutIdx, ecc_key* key,
(word32)pubSz, key, curve_id);
}

/* Based on ecc.c's HAVE_ECC_MAKE_PUB condition, which is local to
* that file. This guard is intentionally a superset: QNX_CAAM and
* IMXRT1170_CAAM are added because those ports store the private
* scalar as an opaque hardware "black key" where a software point
* multiply would be meaningless or unsafe. */
#if !defined(NO_ECC_MAKE_PUB) && !defined(WOLFSSL_ATECC508A) && \
!defined(WOLFSSL_ATECC608A) && !defined(WOLFSSL_MICROCHIP_TA100) && \
!defined(WOLFSSL_CRYPTOCELL) && !defined(WOLFSSL_SILABS_SE_ACCEL) && \
!defined(WOLFSSL_KCAPI_ECC) && !defined(WOLFSSL_QNX_CAAM) && \
!defined(WOLFSSL_IMXRT1170_CAAM)
if ((ret == 0) && (key->type == ECC_PRIVATEKEY_ONLY)) {
/* SEC1 allows omitting the public point; derive it. Best-effort:
* failure must not fail decoding of an otherwise valid key. */
int pubRet;
#ifdef ECC_TIMING_RESISTANT
pubRet = wc_ecc_make_pub_ex(key, NULL, key->rng);
#else
pubRet = wc_ecc_make_pub_ex(key, NULL, NULL);
#endif
if (pubRet != 0) {
WOLFSSL_MSG("Best-effort ECC public key derivation failed");
}
}
#endif

WC_FREE_VAR_EX(priv, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
WC_FREE_VAR_EX(pub, key->heap, DYNAMIC_TYPE_TMP_BUFFER);

Expand Down
Loading
Loading