Skip to content

Commit 305172d

Browse files
committed
asn: ConfirmSignature accepts SPKI DER for Ed25519 / Ed448 alt-sig verify
The Ed25519 and Ed448 legs of ConfirmSignature called wc_ed{25519,448}_import_public on the supplied key blob, which expects the *raw* public key bytes. That works for the primary-signature verify path because StoreKey strips the SubjectPublicKeyInfo BIT STRING wrapper into cert->publicKey, but it broke the X9.146 alternative-signature verify path: dual-alg sapki is preserved as the full SubjectPublicKeyInfo DER (cert->ca->sapkiDer in ParseCertRelative), so for an Ed25519/Ed448 sapki the import call returned BAD_FUNC_ARG and the cert was rejected before DecodePeerAltPubKey could run. Try the raw import first (preserves the existing primary path) and fall back to wc_Ed{25519,448}PublicKeyDecode (which strips the SPKI wrapper) when the raw form doesn't fit. Same dual-input pattern as the comment notes; one extra call only on the SPKI path. This unblocks the test_dual_alg_unsupported_alt_native test, which is now a real TLS 1.3 handshake exercising: - issuer-side: build a self-signed cert with primary ECDSA + alt Ed25519 (real key, real Ed25519 alt signature), - parse-time: ConfirmSignature verifies the Ed25519 alt sig against the cert's own Ed25519 sapki (via the new SPKI fallback), - DecodePeerAltPubKey: hits its default "log + skip" branch for the unrecognised Ed25519 sapki OID, - handshake: completes successfully because sigSpec == NATIVE never consumes the alt key. Verified with --enable-dual-alg-certs --enable-experimental (default) and additionally with --enable-dilithium --enable-mldsa --enable-certreq --enable-certgen --enable-keygen --enable-ed25519 + -DWOLFSSL_CUSTOM_OID -DHAVE_OID_ENCODING -DHAVE_OID_DECODING: - Both configs build clean under -Werror. - All 10 dual-alg tests pass. - test_dual_alg_unsupported_alt_native is now an end-to-end TLS handshake (was a parser-only fallback in the previous commit).
1 parent 54f14f9 commit 305172d

2 files changed

Lines changed: 68 additions & 46 deletions

File tree

tests/api.c

Lines changed: 41 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2619,76 +2619,75 @@ static int do_build_dual_alg_self_signed_ed25519alt(byte* out, word32 outSz,
26192619
}
26202620
#endif
26212621

2622-
/* Parser tolerance for an unsupported alt-key OID.
2623-
*
2624-
* Per the soft-skip behaviour in DecodePeerAltPubKey, an alt-key OID we
2625-
* don't implement (e.g. Ed25519 - not in the cert/CSR alt-key dispatch
2626-
* switch) must not break a purely-native (sigSpec == NATIVE) handshake.
2627-
*
2628-
* A pure TLS-handshake regression test isn't currently possible: the
2629-
* cert's alt signature has to verify before ProcessPeerCerts /
2630-
* DecodePeerAltPubKey is reached, and the Ed25519 leg of ConfirmSignature
2631-
* (used for alt-sig verify) calls wc_ed25519_import_public on
2632-
* cert->ca->sapkiDer - which is the SPKI DER, not the raw 32-byte key
2633-
* the function expects. That's a pre-existing wolfSSL gap independent of
2634-
* this PR (the ECC and RSA cases use wc_*PublicKeyDecode which accepts
2635-
* SPKI DER).
2636-
*
2637-
* What we *can* test directly: that the parser accepts a cert carrying
2638-
* an Ed25519 sapki with NO_VERIFY, and that wc_GeneratePreTBS handles
2639-
* the resulting DecodedCert (the receive-side tolerance fires from
2640-
* DecodePeerAltPubKey on top of this). */
2622+
/* Unsupported alt-key OID + NATIVE handshake: build a self-signed cert
2623+
* with primary ECDSA + alt Ed25519 (real key, real Ed25519 alt
2624+
* signature). The cert validates cleanly during ParseCertRelative -
2625+
* ConfirmSignature now parses SPKI DER for Ed25519/Ed448 sapki, the
2626+
* companion fix in this PR - but DecodePeerAltPubKey has no case for
2627+
* Ed25519. Per the soft-skip behaviour the handshake must still succeed
2628+
* when sigSpec is NATIVE (the alt key just isn't decoded into any
2629+
* peer*Key slot, since NATIVE never consumes it). */
26412630
static int test_dual_alg_unsupported_alt_native(void)
26422631
{
26432632
EXPECT_DECLS;
26442633
#if defined(WOLFSSL_DUAL_ALG_CERTS) && defined(HAVE_ECC) && \
26452634
defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) && \
2646-
defined(HAVE_ED25519_SIGN) && !defined(WC_NO_RNG) && \
2647-
!defined(WOLFSSL_SMALL_STACK) && \
2648-
defined(WOLFSSL_CUSTOM_OID) && defined(HAVE_OID_ENCODING)
2635+
defined(HAVE_ED25519_KEY_IMPORT) && \
2636+
defined(HAVE_ED25519_SIGN) && defined(HAVE_ED25519_VERIFY) && \
2637+
!defined(WC_NO_RNG) && !defined(WOLFSSL_SMALL_STACK) && \
2638+
defined(WOLFSSL_CUSTOM_OID) && defined(HAVE_OID_ENCODING) && \
2639+
!defined(NO_TLS) && defined(WOLFSSL_TLS13)
26492640
WC_RNG rng;
26502641
ecc_key primaryKey;
26512642
ed25519_key altKey;
2652-
DecodedCert d_cert;
2643+
byte primaryKeyDer[256];
2644+
int primaryKeyDerSz;
26532645
byte certDer[2 * LARGE_TEMP_SZ];
26542646
int certDerSz;
2655-
byte tbs[2 * LARGE_TEMP_SZ];
2656-
int tbsSz;
2647+
WOLFSSL_CTX *ctx_c = NULL;
2648+
WOLFSSL_CTX *ctx_s = NULL;
2649+
WOLFSSL *ssl_c = NULL;
2650+
WOLFSSL *ssl_s = NULL;
2651+
struct test_memio_ctx test_ctx;
26572652

26582653
XMEMSET(&rng, 0, sizeof(rng));
26592654
XMEMSET(&primaryKey, 0, sizeof(primaryKey));
26602655
XMEMSET(&altKey, 0, sizeof(altKey));
2661-
XMEMSET(&d_cert, 0, sizeof(d_cert));
2656+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
26622657

26632658
ExpectIntEQ(wc_InitRng(&rng), 0);
26642659
ExpectIntEQ(wc_ecc_init(&primaryKey), 0);
26652660
ExpectIntEQ(wc_ecc_make_key(&rng, KEY32, &primaryKey), 0);
26662661
ExpectIntEQ(wc_ed25519_init(&altKey), 0);
26672662
ExpectIntEQ(wc_ed25519_make_key(&rng, ED25519_KEY_SIZE, &altKey), 0);
26682663

2664+
primaryKeyDerSz = wc_EccKeyToDer(&primaryKey, primaryKeyDer,
2665+
sizeof(primaryKeyDer));
2666+
ExpectIntGT(primaryKeyDerSz, 0);
2667+
26692668
if (EXPECT_SUCCESS()) {
26702669
certDerSz = do_build_dual_alg_self_signed_ed25519alt(certDer,
26712670
sizeof(certDer), &primaryKey, &altKey, &rng);
26722671
ExpectIntGT(certDerSz, 0);
26732672

2674-
/* Parser must accept the cert (NO_VERIFY skips alt-sig verify;
2675-
* sapkiOID lands as ED25519k, which is what DecodePeerAltPubKey's
2676-
* default branch is designed to tolerate). */
2677-
wc_InitDecodedCert(&d_cert, certDer, (word32)certDerSz, 0);
2678-
ExpectIntEQ(wc_ParseCert(&d_cert, CERT_TYPE, NO_VERIFY, NULL), 0);
2679-
ExpectIntNE(d_cert.extSapkiSet, 0);
2680-
ExpectIntNE(d_cert.extAltSigAlgSet, 0);
2681-
ExpectIntNE(d_cert.extAltSigValSet, 0);
2682-
2683-
/* wc_GeneratePreTBS must still excise altSigValue cleanly even
2684-
* though the alt key uses an algorithm DecodePeerAltPubKey
2685-
* doesn't dispatch on. */
2686-
tbsSz = wc_GeneratePreTBS(&d_cert, tbs, (int)sizeof(tbs));
2687-
ExpectIntGT(tbsSz, 0);
2688-
ExpectIntLT((word32)tbsSz, d_cert.sigIndex - d_cert.certBegin);
2673+
ExpectIntEQ(test_memio_setup_ex(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
2674+
&ssl_s, wolfTLSv1_3_client_method,
2675+
wolfTLSv1_3_server_method,
2676+
certDer, certDerSz,
2677+
certDer, certDerSz,
2678+
primaryKeyDer, primaryKeyDerSz), 0);
2679+
2680+
/* Default sigSpec is NATIVE; handshake must succeed. The Ed25519
2681+
* alt sig is verified at parse time (ConfirmSignature now handles
2682+
* SPKI DER for Ed25519), and DecodePeerAltPubKey hits its default
2683+
* "log + skip" branch for the unrecognised OID. */
2684+
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
26892685
}
26902686

2691-
wc_FreeDecodedCert(&d_cert);
2687+
wolfSSL_free(ssl_c);
2688+
wolfSSL_free(ssl_s);
2689+
wolfSSL_CTX_free(ctx_c);
2690+
wolfSSL_CTX_free(ctx_s);
26922691
wc_ecc_free(&primaryKey);
26932692
wc_ed25519_free(&altKey);
26942693
wc_FreeRng(&rng);

wolfcrypt/src/asn.c

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16584,8 +16584,22 @@ int ConfirmSignature(SignatureCtx* sigCtx,
1658416584
sigCtx->heap, sigCtx->devId)) < 0) {
1658516585
goto exit_cs;
1658616586
}
16587-
if ((ret = wc_ed25519_import_public(key, keySz,
16588-
sigCtx->key.ed25519)) < 0) {
16587+
/* Two callers feed this path with different key
16588+
* encodings: the primary-signature verify hands us a raw
16589+
* 32-byte public key (StoreKey strips the SPKI BIT
16590+
* STRING wrapper into cert->publicKey), while the X9.146
16591+
* alt-signature verify hands us the full
16592+
* SubjectPublicKeyInfo DER from cert->ca->sapkiDer.
16593+
* Try the raw form first and fall back to SPKI parsing
16594+
* if that doesn't fit. */
16595+
ret = wc_ed25519_import_public(key, keySz,
16596+
sigCtx->key.ed25519);
16597+
if (ret != 0) {
16598+
word32 idx = 0;
16599+
ret = wc_Ed25519PublicKeyDecode(key, &idx,
16600+
sigCtx->key.ed25519, keySz);
16601+
}
16602+
if (ret < 0) {
1658916603
WOLFSSL_MSG("ASN Key import error ED25519");
1659016604
WOLFSSL_ERROR_VERBOSE(ret);
1659116605
goto exit_cs;
@@ -16611,8 +16625,17 @@ int ConfirmSignature(SignatureCtx* sigCtx,
1661116625
if ((ret = wc_ed448_init(sigCtx->key.ed448)) < 0) {
1661216626
goto exit_cs;
1661316627
}
16614-
if ((ret = wc_ed448_import_public(key, keySz,
16615-
sigCtx->key.ed448)) < 0) {
16628+
/* Same dual-input pattern as ED25519 above: raw key for
16629+
* the primary-signature path, SPKI DER for the X9.146
16630+
* alt-signature path. */
16631+
ret = wc_ed448_import_public(key, keySz,
16632+
sigCtx->key.ed448);
16633+
if (ret != 0) {
16634+
word32 idx = 0;
16635+
ret = wc_Ed448PublicKeyDecode(key, &idx,
16636+
sigCtx->key.ed448, keySz);
16637+
}
16638+
if (ret < 0) {
1661616639
WOLFSSL_MSG("ASN Key import error ED448");
1661716640
WOLFSSL_ERROR_VERBOSE(ret);
1661816641
goto exit_cs;

0 commit comments

Comments
 (0)