Browse Source

Adding support for dual key/signature certificates. (#7112)

Adding support for dual key/signature certificates with X9.146. Enabled with `--enable-dual-alg-certs` or `WOLFSSL_DUAL_ALG_CERTS`.
Anthony Hu 3 months ago
parent
commit
9be390250d

+ 10 - 0
configure.ac

@@ -8097,6 +8097,12 @@ AC_ARG_ENABLE([sys-ca-certs],
     [ ENABLED_SYS_CA_CERTS=yes ]
     )
 
+AC_ARG_ENABLE([dual-alg-certs],
+    [AS_HELP_STRING([--enable-dual-alg-certs],[Enable support for dual key/signature certificates in TLS 1.3 as defined in X9.146 (default: disabled)])],
+    [ ENABLED_DUAL_ALG_CERTS=$enableval ],
+    [ ENABLED_DUAL_ALG_CERTS=no ]
+    )
+
 # check if should run the trusted peer certs test
 # (for now checking both C_FLAGS and C_EXTRA_FLAGS)
 AS_CASE(["$CFLAGS $CPPFLAGS"],[*'WOLFSSL_TRUST_PEER_CERT'*],[ENABLED_TRUSTED_PEER_CERT=yes])
@@ -8462,6 +8468,9 @@ AS_IF([test "x$ENABLED_ASN" = "xno"],
 AS_IF([test "x$ENABLED_SYS_CA_CERTS" = "xyes"],
       [AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_SYS_CA_CERTS"])
 
+AS_IF([test "x$ENABLED_DUAL_ALG_CERTS" = "xyes"],
+      [AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_DUAL_ALG_CERTS"])
+
 AS_IF([test "x$ENABLED_ALTNAMES" = "xyes"],
       [AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_ALT_NAMES"])
 
@@ -9579,6 +9588,7 @@ echo "   * NXP SE050:                  $ENABLED_SE050"
 echo "   * Maxim Integrated MAXQ10XX:  $ENABLED_MAXQ10XX"
 echo "   * PSA:                        $ENABLED_PSA"
 echo "   * System CA certs:            $ENABLED_SYS_CA_CERTS"
+echo "   * Dual alg cert support:      $ENABLED_DUAL_ALG_CERTS"
 echo "   * ERR Queues per Thread:      $ENABLED_ERRORQUEUEPERTHREAD"
 echo "   * rwlock:                     $ENABLED_RWLOCK"
 echo "   * keylog export:              $ENABLED_KEYLOG_EXPORT"

+ 18 - 0
examples/client/client.c

@@ -3558,6 +3558,24 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
         err_sys("unable to get SSL object");
     }
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    /* Set our preference for verfication to be for both the native and
+     * alternative chains. Ultimately, its the server's choice.
+     */
+    {
+        byte cks_order[3] = {
+            WOLFSSL_CKS_SIGSPEC_BOTH,
+            WOLFSSL_CKS_SIGSPEC_ALTERNATIVE,
+            WOLFSSL_CKS_SIGSPEC_NATIVE,
+        };
+
+        if (!wolfSSL_UseCKS(ssl, cks_order, sizeof(cks_order))) {
+            wolfSSL_CTX_free(ctx); ctx = NULL;
+            err_sys("unable to set the CKS order.");
+        }
+    }
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
 #ifndef NO_PSK
     if (usePsk) {
     #if defined(OPENSSL_EXTRA) && defined(WOLFSSL_TLS13) && defined(TEST_PSK_USE_SESSION)

+ 34 - 3
examples/server/server.c

@@ -969,11 +969,15 @@ static const char* server_usage_msg[][65] = {
 #endif
 #ifdef HAVE_SUPPORTED_CURVES
         "--onlyPskDheKe Must use DHE key exchange with PSK\n",          /* 64 */
+#endif
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+        "--altPrivKey <file> Generate alternative signature with this key.\n",
+                                                                        /* 65 */
 #endif
         "\n"
            "For simpler wolfSSL TLS server examples, visit\n"
            "https://github.com/wolfSSL/wolfssl-examples/tree/master/tls\n",
-                                                                        /* 65 */
+                                                                        /* 66 */
         NULL,
     },
 #ifndef NO_MULTIBYTE_PRINT
@@ -1159,11 +1163,16 @@ static const char* server_usage_msg[][65] = {
 #endif
 #ifdef HAVE_SUPPORTED_CURVES
         "--onlyPskDheKe Must use DHE key exchange with PSK\n",          /* 64 */
+#endif
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+        "--altPrivKey <file> Generate alternative signature with this key.\n",
+                                                                        /* 65 */
 #endif
         "\n"
         "より簡単なwolfSSL TSL クライアントの例については"
                                           "下記にアクセスしてください\n"
-        "https://github.com/wolfSSL/wolfssl-examples/tree/master/tls\n", /* 65 */
+        "https://github.com/wolfSSL/wolfssl-examples/tree/master/tls\n",
+                                                                        /* 66 */
         NULL,
     },
 #endif
@@ -1320,7 +1329,10 @@ static void Usage(void)
 #ifdef HAVE_SUPPORTED_CURVES
     printf("%s", msg[++msgId]);     /* --onlyPskDheKe */
 #endif
-    printf("%s", msg[++msgId]); /* Examples repo link */
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    printf("%s", msg[++msgId]);     /* --altPrivKey */
+#endif
+    printf("%s", msg[++msgId]);     /* Examples repo link */
 }
 
 #ifdef WOLFSSL_SRTP
@@ -1436,6 +1448,9 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
         {"crl-dir", 1, 265},
 #endif
         {"quieter", 0, 266},
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+        { "altPrivKey", 1, 267},
+#endif
         { 0, 0, 0 }
     };
 #endif
@@ -1600,6 +1615,7 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
     int useX448 = 0;
     int usePqc = 0;
     char* pqcAlg = NULL;
+    char* altPrivKey = NULL;
     int exitWithRet = 0;
     int loadCertKeyIntoSSLObj = 0;
 
@@ -1674,6 +1690,7 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
     (void)nonBlocking;
     (void)pqcAlg;
     (void)usePqc;
+    (void)altPrivKey;
 
 #ifdef WOLFSSL_TIRTOS
     fdOpenSession(Task_self());
@@ -2320,6 +2337,12 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
             quieter = 1;
             break;
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+        case 267:
+            altPrivKey = myoptarg;
+            break;
+#endif
+
         case -1:
             default:
                 Usage();
@@ -2697,6 +2720,14 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
                                          != WOLFSSL_SUCCESS)
             err_sys_ex(catastrophic, "can't load server private key file, "
                        "check file and run from wolfSSL home dir");
+        #ifdef WOLFSSL_DUAL_ALG_CERTS
+        if ((altPrivKey != NULL) &&
+            wolfSSL_CTX_use_AltPrivateKey_file(ctx, altPrivKey,
+                                               WOLFSSL_FILETYPE_PEM)
+                                               != WOLFSSL_SUCCESS)
+            err_sys_ex(catastrophic, "can't load alt private key file, "
+                       "check file and run from wolfSSL home dir");
+        #endif /* WOLFSSL_DUAL_ALG_CERTS */
     #else
         /* loads private key file using buffer API */
         load_buffer(ctx, ourKey, WOLFSSL_KEY);

+ 205 - 1
src/internal.c

@@ -2585,6 +2585,12 @@ void SSL_CtxResourceFree(WOLFSSL_CTX* ctx)
         ForceZero(ctx->privateKey->buffer, ctx->privateKey->length);
     }
     FreeDer(&ctx->privateKey);
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    if (ctx->altPrivateKey != NULL && ctx->altPrivateKey->buffer != NULL) {
+        ForceZero(ctx->altPrivateKey->buffer, ctx->altPrivateKey->length);
+    }
+    FreeDer(&ctx->altPrivateKey);
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
 #ifdef OPENSSL_ALL
     wolfSSL_EVP_PKEY_free(ctx->privateKeyPKey);
 #endif
@@ -4575,6 +4581,12 @@ void FreeX509(WOLFSSL_X509* x509)
         x509->altNames = NULL;
     }
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    XFREE(x509->sapkiDer, x509->heap, DYNAMIC_TYPE_X509_EXT);
+    XFREE(x509->altSigAlgDer, x509->heap, DYNAMIC_TYPE_X509_EXT);
+    XFREE(x509->altSigValDer, x509->heap, DYNAMIC_TYPE_X509_EXT);
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
     #if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)
         wolfSSL_RefFree(&x509->ref);
     #endif
@@ -6748,6 +6760,11 @@ int SetSSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
     ssl->buffers.keyLabel = ctx->privateKeyLabel;
     ssl->buffers.keySz    = ctx->privateKeySz;
     ssl->buffers.keyDevId = ctx->privateKeyDevId;
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    ssl->buffers.altKey     = ctx->altPrivateKey;
+    ssl->buffers.altKeySz   = ctx->altPrivateKeySz;
+    ssl->buffers.altKeyType = ctx->altPrivateKeyType;
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
 #endif
 #if !defined(WOLFSSL_NO_CLIENT_AUTH) && \
                ((defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)) || \
@@ -7553,7 +7570,10 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
     defined(WOLFSSL_SSLKEYLOGFILE) && defined(WOLFSSL_TLS13)
     (void)wolfSSL_set_tls13_secret_cb(ssl, tls13ShowSecrets, NULL);
 #endif
-
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    ssl->sigSpec = ctx->sigSpec;
+    ssl->sigSpecSz = ctx->sigSpecSz;
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
     return 0;
 }
 
@@ -7934,6 +7954,9 @@ void FreeKeyExchange(WOLFSSL* ssl)
 
     /* Free handshake key */
     FreeKey(ssl, ssl->hsType, &ssl->hsKey);
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    FreeKey(ssl, ssl->hsAltType, &ssl->hsAltKey);
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
 
 #ifndef NO_DH
     /* Free temp DH key */
@@ -8302,6 +8325,9 @@ void SSL_ResourceFree(WOLFSSL* ssl)
     wolfSSL_CTX_free(ssl->initial_ctx);
     ssl->initial_ctx = NULL;
 #endif
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    XFREE(ssl->peerSigSpec, ssl->heap, DYNAMIC_TYPE_TLSX);
+#endif
 }
 
 /* Free any handshake resources no longer needed */
@@ -12935,6 +12961,30 @@ int CopyDecodedToX509(WOLFSSL_X509* x509, DecodedCert* dCert)
     x509->pkCurveOID = dCert->pkCurveOID;
 #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    /* Copy over alternative sig and pubkey. In this case we will allocate new
+     * buffers for them as we have no knowledge of when the DecodedCert is
+     * freed. */
+    x509->sapkiDer = (byte*)XMALLOC(dCert->sapkiLen, x509->heap,
+                                    DYNAMIC_TYPE_X509_EXT);
+    x509->altSigAlgDer = (byte*)XMALLOC(dCert->altSigAlgLen, x509->heap,
+                                        DYNAMIC_TYPE_X509_EXT);
+    x509->altSigValDer = (byte*)XMALLOC(dCert->altSigValLen, x509->heap,
+                                        DYNAMIC_TYPE_X509_EXT);
+    if ((x509->sapkiDer != NULL) && (x509->altSigAlgDer != NULL) &&
+        (x509->altSigValDer != NULL)) {
+        XMEMCPY(x509->sapkiDer, dCert->sapkiDer, dCert->sapkiLen);
+        XMEMCPY(x509->altSigAlgDer, dCert->altSigAlgDer, dCert->altSigAlgLen);
+        XMEMCPY(x509->altSigValDer, dCert->altSigValDer, dCert->altSigValLen);
+        x509->sapkiLen = dCert->sapkiLen;
+        x509->altSigAlgLen = dCert->altSigAlgLen;
+        x509->altSigValLen = dCert->altSigValLen;
+    }
+    else {
+        ret = MEMORY_E;
+    }
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
     return ret;
 }
 
@@ -13881,6 +13931,39 @@ PRAGMA_GCC_DIAG_POP
         alreadySigner = AlreadySigner(SSL_CM(ssl), subjectHash);
     }
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    if ((ret == 0) && (args->dCert->sapkiDer != NULL)) {
+#ifndef WOLFSSL_SMALL_STACK
+        byte der[MAX_CERT_VERIFY_SZ];
+#else
+        byte *der = (byte*)XMALLOC(MAX_CERT_VERIFY_SZ, ssl->heap,
+                                   DYNAMIC_TYPE_DCERT);
+        if (der == NULL) {
+            ret = MEMORY_E;
+        }
+#endif /* ! WOLFSSL_SMALL_STACK */
+
+        if (ret == 0) {
+            ret = wc_GeneratePreTBS(args->dCert, der, MAX_CERT_VERIFY_SZ);
+
+            if (ret > 0) {
+                ret = wc_ConfirmAltSignature(der, ret,
+                    args->dCert->sapkiDer, args->dCert->sapkiLen,
+                    args->dCert->sapkiOID,
+                    args->dCert->altSigValDer, args->dCert->altSigValLen,
+                    args->dCert->altSigAlgOID, ssl->heap);
+            }
+#ifdef WOLFSSL_SMALL_STACK
+            XFREE(der, ssl->heap, DYNAMIC_TYPE_DCERT);
+#endif /* WOLFSSL_SMALL_STACK */
+
+            if (ret == 0) {
+               WOLFSSL_MSG("Alternative signature has been verified!");
+            }
+        }
+    }
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
 #ifdef WOLFSSL_SMALL_CERT_VERIFY
     /* get signature check failures from above */
     if (ret == 0)
@@ -27785,6 +27868,10 @@ int DecodePrivateKey(WOLFSSL *ssl, word16* length)
 #endif /* HAVE_ED448 && HAVE_ED448_KEY_IMPORT */
 #if defined(HAVE_PQC)
 #if defined(HAVE_FALCON)
+    #if !defined(NO_RSA) || defined(HAVE_ECC)
+        FreeKey(ssl, ssl->hsType, (void**)&ssl->hsKey);
+    #endif
+
     if (ssl->buffers.keyType == falcon_level1_sa_algo ||
         ssl->buffers.keyType == falcon_level5_sa_algo ||
         ssl->buffers.keyType == 0) {
@@ -27846,6 +27933,10 @@ int DecodePrivateKey(WOLFSSL *ssl, word16* length)
     }
 #endif /* HAVE_FALCON */
 #if defined(HAVE_DILITHIUM)
+    #if !defined(NO_RSA) || defined(HAVE_ECC)
+        FreeKey(ssl, ssl->hsType, (void**)&ssl->hsKey);
+    #endif
+
     if (ssl->buffers.keyType == dilithium_level2_sa_algo ||
         ssl->buffers.keyType == dilithium_level3_sa_algo ||
         ssl->buffers.keyType == dilithium_level5_sa_algo ||
@@ -27926,6 +28017,119 @@ exit_dpk:
     return ret;
 }
 
+#if defined(HAVE_PQC) && defined(WOLFSSL_DUAL_ALG_CERTS)
+/* This is just like the above, but only consider Falcon and Dilthium and
+ * only for the alternative key; not the native key. */
+int DecodeAltPrivateKey(WOLFSSL *ssl, word16* length)
+{
+    int      ret = BAD_FUNC_ARG;
+
+    /* make sure alt private key exists */
+    if (ssl->buffers.altKey == NULL || ssl->buffers.altKey->buffer == NULL) {
+        WOLFSSL_MSG("Alternative Private key missing!");
+        ERROR_OUT(NO_PRIVATE_KEY, exit_dapk);
+    }
+
+    if (ssl->buffers.altKeyType == falcon_level1_sa_algo ||
+        ssl->buffers.altKeyType == falcon_level5_sa_algo) {
+
+        ssl->hsAltType = DYNAMIC_TYPE_FALCON;
+        ret = AllocKey(ssl, ssl->hsAltType, &ssl->hsAltKey);
+        if (ret != 0) {
+            goto exit_dapk;
+        }
+
+        if (ssl->buffers.altKeyType == falcon_level1_sa_algo) {
+            ret = wc_falcon_set_level((falcon_key*)ssl->hsAltKey, 1);
+        }
+        else if (ssl->buffers.altKeyType == falcon_level5_sa_algo) {
+            ret = wc_falcon_set_level((falcon_key*)ssl->hsAltKey, 5);
+        }
+        else {
+            ret = ALGO_ID_E;
+        }
+
+        if (ret != 0) {
+            goto exit_dapk;
+        }
+        WOLFSSL_MSG("Trying Falcon private key");
+
+        /* Decode the key assuming it is a Falcon private key. */
+        ret = wc_falcon_import_private_only(ssl->buffers.altKey->buffer,
+                                            ssl->buffers.altKey->length,
+                                            (falcon_key*)ssl->hsAltKey);
+        if (ret == 0) {
+            WOLFSSL_MSG("Using Falcon private key");
+
+            /* Check it meets the minimum Falcon key size requirements. */
+            if (FALCON_MAX_KEY_SIZE < ssl->options.minFalconKeySz) {
+                WOLFSSL_MSG("Falcon key size too small");
+                ERROR_OUT(FALCON_KEY_SIZE_E, exit_dapk);
+            }
+
+            *length = wc_falcon_sig_size((falcon_key*)ssl->hsAltKey);
+
+            goto exit_dapk;
+        }
+    }
+    FreeKey(ssl, ssl->hsAltType, (void**)&ssl->hsAltKey);
+
+    if (ssl->buffers.altKeyType == dilithium_level2_sa_algo ||
+        ssl->buffers.altKeyType == dilithium_level3_sa_algo ||
+        ssl->buffers.altKeyType == dilithium_level5_sa_algo) {
+
+        ssl->hsAltType = DYNAMIC_TYPE_DILITHIUM;
+        ret = AllocKey(ssl, ssl->hsAltType, &ssl->hsAltKey);
+        if (ret != 0) {
+            goto exit_dapk;
+        }
+
+        if (ssl->buffers.altKeyType == dilithium_level2_sa_algo) {
+            ret = wc_dilithium_set_level((dilithium_key*)ssl->hsAltKey, 2);
+        }
+        else if (ssl->buffers.altKeyType == dilithium_level3_sa_algo) {
+            ret = wc_dilithium_set_level((dilithium_key*)ssl->hsAltKey, 3);
+        }
+        else if (ssl->buffers.altKeyType == dilithium_level5_sa_algo) {
+            ret = wc_dilithium_set_level((dilithium_key*)ssl->hsAltKey, 5);
+        }
+        else {
+            ret = ALGO_ID_E;
+        }
+
+        if (ret != 0) {
+            goto exit_dapk;
+        }
+
+        WOLFSSL_MSG("Trying Dilithium private key");
+
+        /* Decode the key assuming it is a Dilithium private key. */
+        ret = wc_dilithium_import_private_only(ssl->buffers.altKey->buffer,
+                                               ssl->buffers.altKey->length,
+                                               (dilithium_key*)ssl->hsAltKey);
+        if (ret == 0) {
+            WOLFSSL_MSG("Using Dilithium private key");
+
+            /* Check it meets the minimum Dilithium key size requirements. */
+            if (DILITHIUM_MAX_KEY_SIZE < ssl->options.minDilithiumKeySz) {
+                WOLFSSL_MSG("Dilithium key size too small");
+                ERROR_OUT(DILITHIUM_KEY_SIZE_E, exit_dapk);
+            }
+
+            *length = wc_dilithium_sig_size((dilithium_key*)ssl->hsAltKey);
+
+            goto exit_dapk;
+        }
+    }
+
+exit_dapk:
+    if (ret != 0) {
+        WOLFSSL_ERROR_VERBOSE(ret);
+    }
+
+    return ret;
+}
+#endif /* HAVE_PQC && WOLFSSL_DUAL_ALG_CERTS */
 #endif /* WOLFSSL_TLS13 || !NO_WOLFSSL_CLIENT */
 
 #if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_TLS12)

+ 170 - 34
src/ssl.c

@@ -5879,6 +5879,12 @@ int AddCA(WOLFSSL_CERT_MANAGER* cm, DerBuffer** pDer, int type, int verify)
             signer->publicKey      = cert->publicKey;
             signer->pubKeySize     = cert->pubKeySize;
         }
+
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+        signer->sapkiDer = cert->sapkiDer;
+        signer->sapkiLen = cert->sapkiLen;
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
         if (cert->subjectCNStored) {
             signer->nameLen        = cert->subjectCNLen;
             signer->name           = cert->subjectCN;
@@ -6851,12 +6857,13 @@ static int ProcessBufferTryDecodeEd448(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
 #if defined(HAVE_FALCON)
 static int ProcessBufferTryDecodeFalcon(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
     DerBuffer* der, int* keySz, word32* idx, int* resetSuites, int* keyFormat,
-    void* heap)
+    void* heap, int type)
 {
     int ret;
     /* make sure Falcon key can be used */
     falcon_key* key = (falcon_key*)XMALLOC(sizeof(falcon_key), heap,
                                            DYNAMIC_TYPE_FALCON);
+    (void) type;
     if (key == NULL) {
         return MEMORY_E;
     }
@@ -6889,22 +6896,50 @@ static int ProcessBufferTryDecodeFalcon(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
                 ret = FALCON_KEY_SIZE_E;
             }
             if (ssl) {
-                if (*keyFormat == FALCON_LEVEL1k) {
-                    ssl->buffers.keyType = falcon_level1_sa_algo;
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+                if (type == ALT_PRIVATEKEY_TYPE) {
+                    if (*keyFormat == FALCON_LEVEL1k) {
+                        ssl->buffers.altKeyType = falcon_level1_sa_algo;
+                    }
+                    else {
+                        ssl->buffers.altKeyType = falcon_level5_sa_algo;
+                    }
+                    ssl->buffers.altKeySz = *keySz;
                 }
-                else {
-                    ssl->buffers.keyType = falcon_level5_sa_algo;
+                else
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+                {
+                    if (*keyFormat == FALCON_LEVEL1k) {
+                        ssl->buffers.keyType = falcon_level1_sa_algo;
+                    }
+                    else {
+                        ssl->buffers.keyType = falcon_level5_sa_algo;
+                    }
+                    ssl->buffers.keySz = *keySz;
                 }
-                ssl->buffers.keySz = *keySz;
             }
             else {
-                if (*keyFormat == FALCON_LEVEL1k) {
-                    ctx->privateKeyType = falcon_level1_sa_algo;
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+                if (type == ALT_PRIVATEKEY_TYPE) {
+                    if (*keyFormat == FALCON_LEVEL1k) {
+                        ctx->altPrivateKeyType = falcon_level1_sa_algo;
+                    }
+                    else {
+                        ctx->altPrivateKeyType = falcon_level5_sa_algo;
+                    }
+                    ctx->altPrivateKeySz = *keySz;
                 }
-                else {
-                    ctx->privateKeyType = falcon_level5_sa_algo;
+                else
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+                {
+                    if (*keyFormat == FALCON_LEVEL1k) {
+                        ctx->privateKeyType = falcon_level1_sa_algo;
+                    }
+                    else {
+                        ctx->privateKeyType = falcon_level5_sa_algo;
+                    }
+                    ctx->privateKeySz = *keySz;
                 }
-                ctx->privateKeySz = *keySz;
             }
 
             if (ssl && ssl->options.side == WOLFSSL_SERVER_END) {
@@ -6921,12 +6956,13 @@ static int ProcessBufferTryDecodeFalcon(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
 #if defined(HAVE_DILITHIUM)
 static int ProcessBufferTryDecodeDilithium(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
     DerBuffer* der, int* keySz, word32* idx, int* resetSuites, int* keyFormat,
-    void* heap)
+    void* heap, int type)
 {
     int ret;
     /* make sure Dilithium key can be used */
     dilithium_key* key = (dilithium_key*)XMALLOC(sizeof(dilithium_key), heap,
                                                  DYNAMIC_TYPE_DILITHIUM);
+    (void) type;
     if (key == NULL) {
         return MEMORY_E;
     }
@@ -6962,28 +6998,62 @@ static int ProcessBufferTryDecodeDilithium(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
                 ret = DILITHIUM_KEY_SIZE_E;
             }
             if (ssl) {
-                if (*keyFormat == DILITHIUM_LEVEL2k) {
-                    ssl->buffers.keyType = dilithium_level2_sa_algo;
-                }
-                else if (*keyFormat == DILITHIUM_LEVEL3k) {
-                    ssl->buffers.keyType = dilithium_level3_sa_algo;
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+                if (type == ALT_PRIVATEKEY_TYPE) {
+                    if (*keyFormat == DILITHIUM_LEVEL2k) {
+                        ssl->buffers.altKeyType = dilithium_level2_sa_algo;
+                    }
+                    else if (*keyFormat == DILITHIUM_LEVEL3k) {
+                        ssl->buffers.altKeyType = dilithium_level3_sa_algo;
+                    }
+                    else if (*keyFormat == DILITHIUM_LEVEL5k) {
+                        ssl->buffers.altKeyType = dilithium_level5_sa_algo;
+                    }
+                    ssl->buffers.altKeySz = *keySz;
                 }
-                else if (*keyFormat == DILITHIUM_LEVEL5k) {
-                    ssl->buffers.keyType = dilithium_level5_sa_algo;
+                else
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+                {
+                    if (*keyFormat == DILITHIUM_LEVEL2k) {
+                        ssl->buffers.keyType = dilithium_level2_sa_algo;
+                    }
+                    else if (*keyFormat == DILITHIUM_LEVEL3k) {
+                        ssl->buffers.keyType = dilithium_level3_sa_algo;
+                    }
+                    else if (*keyFormat == DILITHIUM_LEVEL5k) {
+                        ssl->buffers.keyType = dilithium_level5_sa_algo;
+                    }
+                    ssl->buffers.keySz = *keySz;
                 }
-                ssl->buffers.keySz = *keySz;
             }
             else {
-                if (*keyFormat == DILITHIUM_LEVEL2k) {
-                    ctx->privateKeyType = dilithium_level2_sa_algo;
-                }
-                else if (*keyFormat == DILITHIUM_LEVEL3k) {
-                    ctx->privateKeyType = dilithium_level3_sa_algo;
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+                if (type == ALT_PRIVATEKEY_TYPE) {
+                    if (*keyFormat == DILITHIUM_LEVEL2k) {
+                        ctx->altPrivateKeyType = dilithium_level2_sa_algo;
+                    }
+                    else if (*keyFormat == DILITHIUM_LEVEL3k) {
+                        ctx->altPrivateKeyType = dilithium_level3_sa_algo;
+                    }
+                    else if (*keyFormat == DILITHIUM_LEVEL5k) {
+                        ctx->altPrivateKeyType = dilithium_level5_sa_algo;
+                    }
+                    ctx->altPrivateKeySz = *keySz;
                 }
-                else if (*keyFormat == DILITHIUM_LEVEL5k) {
-                    ctx->privateKeyType = dilithium_level5_sa_algo;
+                else
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+                {
+                    if (*keyFormat == DILITHIUM_LEVEL2k) {
+                        ctx->privateKeyType = dilithium_level2_sa_algo;
+                    }
+                    else if (*keyFormat == DILITHIUM_LEVEL3k) {
+                        ctx->privateKeyType = dilithium_level3_sa_algo;
+                    }
+                    else if (*keyFormat == DILITHIUM_LEVEL5k) {
+                        ctx->privateKeyType = dilithium_level5_sa_algo;
+                    }
+                    ctx->privateKeySz = *keySz;
                 }
-                ctx->privateKeySz = *keySz;
             }
 
             if (ssl && ssl->options.side == WOLFSSL_SERVER_END) {
@@ -7001,12 +7071,13 @@ static int ProcessBufferTryDecodeDilithium(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
 
 static int ProcessBufferTryDecode(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
     DerBuffer* der, int* keySz, word32* idx, int* resetSuites, int* keyFormat,
-    void* heap, int devId)
+    void* heap, int devId, int type)
 {
     int ret = 0;
 
     (void)heap;
     (void)devId;
+    (void)type;
 
     if (ctx == NULL && ssl == NULL)
         return BAD_FUNC_ARG;
@@ -7060,7 +7131,7 @@ static int ProcessBufferTryDecode(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
     if (((*keyFormat == 0) || (*keyFormat == FALCON_LEVEL1k) ||
          (*keyFormat == FALCON_LEVEL5k))) {
         ret = ProcessBufferTryDecodeFalcon(ctx, ssl, der, keySz, idx,
-            resetSuites, keyFormat, heap);
+            resetSuites, keyFormat, heap, type);
         if (ret != 0)
             return ret;
     }
@@ -7071,7 +7142,7 @@ static int ProcessBufferTryDecode(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
         (*keyFormat == DILITHIUM_LEVEL3k) ||
         (*keyFormat == DILITHIUM_LEVEL5k)) {
         ret = ProcessBufferTryDecodeDilithium(ctx, ssl, der, keySz, idx,
-            resetSuites, keyFormat, heap);
+            resetSuites, keyFormat, heap, type);
         if (ret != 0) {
             return ret;
         }
@@ -7304,6 +7375,35 @@ int ProcessBuffer(WOLFSSL_CTX* ctx, const unsigned char* buff,
 #endif
         }
     }
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    else if (type == ALT_PRIVATEKEY_TYPE) {
+        if (ssl != NULL) {
+             /* Make sure previous is free'd */
+            if (ssl->buffers.weOwnAltKey) {
+                ForceZero(ssl->buffers.altKey->buffer,
+                          ssl->buffers.altKey->length);
+                FreeDer(&ssl->buffers.altKey);
+            }
+            ssl->buffers.altKey = der;
+#ifdef WOLFSSL_CHECK_MEM_ZERO
+            wc_MemZero_Add("SSL Buffers key", der->buffer, der->length);
+#endif
+            ssl->buffers.weOwnAltKey = 1;
+        }
+        else if (ctx != NULL) {
+            if (ctx->altPrivateKey != NULL &&
+                ctx->altPrivateKey->buffer != NULL) {
+                ForceZero(ctx->altPrivateKey->buffer,
+                          ctx->altPrivateKey->length);
+            }
+            FreeDer(&ctx->altPrivateKey);
+            ctx->altPrivateKey = der;
+#ifdef WOLFSSL_CHECK_MEM_ZERO
+            wc_MemZero_Add("CTX private key", der->buffer, der->length);
+#endif
+        }
+    }
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
     else {
         FreeDer(&der);
         return WOLFSSL_BAD_CERTTYPE;
@@ -7312,9 +7412,13 @@ int ProcessBuffer(WOLFSSL_CTX* ctx, const unsigned char* buff,
     if (done == 1) {
         /* No operation, just skip the next section */
     }
-    else if (type == PRIVATEKEY_TYPE) {
+    else if (type == PRIVATEKEY_TYPE
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+             || type == ALT_PRIVATEKEY_TYPE
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+            ) {
         ret = ProcessBufferTryDecode(ctx, ssl, der, &keySz, &idx, &resetSuites,
-                &keyFormat, heap, devId);
+                &keyFormat, heap, devId, type);
 
     #if defined(WOLFSSL_ENCRYPTED_KEYS) && !defined(NO_PWDBASED)
         /* for WOLFSSL_FILETYPE_PEM, PemToDer manages the decryption */
@@ -7360,7 +7464,7 @@ int ProcessBuffer(WOLFSSL_CTX* ctx, const unsigned char* buff,
             wc_MemZero_Check(password, NAME_SZ);
         #endif
             ret = ProcessBufferTryDecode(ctx, ssl, der, &keySz, &idx,
-                &resetSuites, &keyFormat, heap, devId);
+                &resetSuites, &keyFormat, heap, devId, type);
         }
     #endif /* WOLFSSL_ENCRYPTED_KEYS && !NO_PWDBASED */
 
@@ -8861,7 +8965,20 @@ int wolfSSL_CTX_use_PrivateKey_file(WOLFSSL_CTX* ctx, const char* file,
     return WOLFSSL_FAILURE;
 }
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+int wolfSSL_CTX_use_AltPrivateKey_file(WOLFSSL_CTX* ctx, const char* file,
+                                       int format)
+{
+    WOLFSSL_ENTER("wolfSSL_CTX_use_AltPrivateKey_file");
+
+    if (ProcessFile(ctx, file, format, ALT_PRIVATEKEY_TYPE, NULL, 0, NULL,
+                    GET_VERIFY_SETTING_CTX(ctx)) == WOLFSSL_SUCCESS) {
+        return WOLFSSL_SUCCESS;
+    }
 
+    return WOLFSSL_FAILURE;
+}
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
 #endif /* NO_FILESYSTEM */
 
 
@@ -16111,6 +16228,15 @@ int wolfSSL_set_compression(WOLFSSL* ssl)
             ssl->buffers.weOwnKey = 0;
         }
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+        if (ssl->buffers.weOwnAltKey) {
+            WOLFSSL_MSG("Unloading alt key");
+            ForceZero(ssl->buffers.altKey->buffer, ssl->buffers.altKey->length);
+            FreeDer(&ssl->buffers.altKey);
+            ssl->buffers.weOwnAltKey = 0;
+        }
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
         return WOLFSSL_SUCCESS;
     }
 
@@ -27272,6 +27398,11 @@ void wolfSSL_certs_clear(WOLFSSL* ssl)
     ssl->buffers.keyLabel = 0;
     ssl->buffers.keySz    = 0;
     ssl->buffers.keyDevId = 0;
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    if (ssl->buffers.weOwnAltKey)
+        FreeDer(&ssl->buffers.altKey);
+    ssl->buffers.altKey   = NULL;
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
 }
 #endif
 
@@ -28072,6 +28203,11 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx)
     ssl->options.haveStaticECC    = ctx->haveStaticECC;
     ssl->options.haveFalconSig    = ctx->haveFalconSig;
     ssl->options.haveDilithiumSig = ctx->haveDilithiumSig;
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    ssl->buffers.altKey     = ctx->altPrivateKey;
+    ssl->buffers.altKeySz   = ctx->altPrivateKeySz;
+    ssl->buffers.altKeyType = ctx->altPrivateKeyType;
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
 #endif
 
 #ifdef WOLFSSL_SESSION_ID_CTX

+ 162 - 4
src/tls.c

@@ -1325,6 +1325,10 @@ static WC_INLINE word16 TLSX_ToSemaphore(word16 type)
 #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
         case TLSX_ECH: /* 0xfe0d */
             return 65;
+#endif
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+        case TLSX_CKS:
+            return 66;
 #endif
         default:
             if (type > 62) {
@@ -9474,6 +9478,121 @@ int TLSX_KeyShare_SetSupported(const WOLFSSL* ssl, TLSX** extensions)
     return ret;
 }
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+/* Writes the CKS objects of a list in a buffer. */
+static word16 CKS_WRITE(WOLFSSL* ssl, byte* output)
+{
+    XMEMCPY(output, ssl->sigSpec, ssl->sigSpecSz);
+    return ssl->sigSpecSz;
+}
+
+static int TLSX_UseCKS(TLSX** extensions, WOLFSSL* ssl, void* heap)
+{
+    int ret = 0;
+    TLSX* extension;
+
+    if (extensions == NULL) {
+        return BAD_FUNC_ARG;
+    }
+
+    extension = TLSX_Find(*extensions, TLSX_CKS);
+    /* If it is already present, do nothing. */
+    if (extension == NULL) {
+        /* The data required is in the ssl struct, so push it in. */
+        ret = TLSX_Push(extensions, TLSX_CKS, (void*)ssl, heap);
+    }
+
+    return ret;
+}
+
+int TLSX_CKS_Set(WOLFSSL* ssl, TLSX** extensions)
+{
+    int ret;
+    TLSX* extension;
+    /* Push new KeyShare extension. This will also free the old one */
+    ret = TLSX_Push(extensions, TLSX_CKS, NULL, ssl->heap);
+    if (ret != 0)
+        return ret;
+    /* Extension got pushed to head */
+    extension = *extensions;
+    /* Need ssl->sigSpecSz during extension length calculation. */
+    extension->data = ssl;
+    /* Set extension to be in response. */
+    extension->resp = 1;
+    return ret;
+}
+
+int TLSX_CKS_Parse(WOLFSSL* ssl, byte* input, word16 length,
+                   TLSX** extensions)
+{
+    (void) extensions;
+    int ret;
+    int i, j;
+
+    /* Validating the input. */
+    if (length == 0)
+        return BUFFER_ERROR;
+    for (i = 0; i < length; i++) {
+        switch (input[i])
+        {
+            case WOLFSSL_CKS_SIGSPEC_NATIVE:
+            case WOLFSSL_CKS_SIGSPEC_ALTERNATIVE:
+            case WOLFSSL_CKS_SIGSPEC_BOTH:
+                /* These are all valid values; do nothing */
+                break;
+            case WOLFSSL_CKS_SIGSPEC_EXTERNAL:
+            default:
+                /* All other values (including external) are not. */
+                return WOLFSSL_NOT_IMPLEMENTED;
+        }
+    }
+
+    /* Extension data is valid, but if we are the server and we don't have an
+     * alt private key, do not respond with CKS extension. */
+    if (wolfSSL_is_server(ssl) && ssl->buffers.altKey == NULL) {
+        ssl->sigSpec = NULL;
+        ssl->sigSpecSz = 0;
+        return 0;
+    }
+
+    /* Copy as the lifetime of input seems to be ephemeral. */
+    ssl->peerSigSpec = (byte*)XMALLOC(length, ssl->heap, DYNAMIC_TYPE_TLSX);
+    if (ssl->peerSigSpec == NULL) {
+        return BUFFER_ERROR;
+    }
+    XMEMCPY(ssl->peerSigSpec, input, length);
+    ssl->peerSigSpecSz = length;
+
+    /* If there is no preference set, use theirs... */
+    if (ssl->sigSpec == NULL) {
+        ret = wolfSSL_UseCKS(ssl, ssl->peerSigSpec, 1);
+        if (ret == WOLFSSL_SUCCESS) {
+            ret = TLSX_UseCKS(&ssl->extensions, ssl, ssl->heap);
+            TLSX_SetResponse(ssl, TLSX_CKS);
+        }
+        return ret;
+    }
+
+    /* ...otherwise, prioritize our preference. */
+    for (i = 0; i < ssl->sigSpecSz; i++) {
+        for (j = 0; j < length; j++) {
+            if (ssl->sigSpec[i] == input[j]) {
+                /* Got the match, set to this one. */
+                ret = wolfSSL_UseCKS(ssl, &ssl->peerSigSpec[i], 1);
+                if (ret == WOLFSSL_SUCCESS) {
+                    ret = TLSX_UseCKS(&ssl->extensions, ssl, ssl->heap);
+                    TLSX_SetResponse(ssl, TLSX_CKS);
+                }
+                return ret;
+            }
+        }
+    }
+
+    /* No match found. Cannot continue. */
+    return MATCH_SUITE_ERROR;
+}
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
 /* Server side KSE processing */
 int TLSX_KeyShare_Choose(const WOLFSSL *ssl, TLSX* extensions,
     byte cipherSuite0, byte cipherSuite, KeyShareEntry** kse, byte* searched)
@@ -12006,7 +12125,6 @@ void TLSX_FreeAll(TLSX* list, void* heap)
         list = extension->next;
 
         switch (extension->type) {
-
 #if defined(HAVE_RPK)
             case TLSX_CLIENT_CERTIFICATE_TYPE:
                 WOLFSSL_MSG("Client Certificate Type extension free");
@@ -12168,6 +12286,12 @@ void TLSX_FreeAll(TLSX* list, void* heap)
                 WOLFSSL_MSG("ECH extension free");
                 ECH_FREE((WOLFSSL_ECH*)extension->data, heap);
                 break;
+#endif
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+            case TLSX_CKS:
+                WOLFSSL_MSG("CKS extension free");
+                /* nothing to do */
+                break;
 #endif
             default:
                 break;
@@ -12209,7 +12333,11 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType,
         length += HELLO_EXT_TYPE_SZ + OPAQUE16_LEN;
 
         switch (extension->type) {
-
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+            case TLSX_CKS:
+                length += ((WOLFSSL*)extension->data)->sigSpecSz ;
+                break;
+#endif
 #ifdef HAVE_SNI
             case TLSX_SERVER_NAME:
                 /* SNI only sends the name on the request. */
@@ -12400,6 +12528,13 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore,
 
         /* extension data should be written internally. */
         switch (extension->type) {
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+            case TLSX_CKS:
+                WOLFSSL_MSG("CKS extension to write");
+                offset += CKS_WRITE(((WOLFSSL*)extension->data),
+                                    output + offset);
+                break;
+#endif
 #ifdef HAVE_SNI
             case TLSX_SERVER_NAME:
                 if (isRequest) {
@@ -12909,6 +13044,14 @@ int TLSX_PopulateExtensions(WOLFSSL* ssl, byte isServer)
                 return ret;
             }
         }
+#endif
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+        if ((IsAtLeastTLSv1_3(ssl->version)) && (ssl->sigSpec != NULL)) {
+            WOLFSSL_MSG("Adding CKS extension");
+            if ((ret = TLSX_UseCKS(&ssl->extensions, ssl, ssl->heap)) != 0) {
+                return ret;
+            }
+        }
 #endif
     } /* is not server */
 
@@ -13653,6 +13796,10 @@ int TLSX_WriteRequest(WOLFSSL* ssl, byte* output, byte msgType, word16* pOffset)
             TURN_ON(semaphore,
                     TLSX_ToSemaphore(TLSX_CERTIFICATE_AUTHORITIES));
         #endif
+        #ifdef WOLFSSL_DUAL_ALG_CERTS
+            TURN_ON(semaphore,
+                    TLSX_ToSemaphore(TLSX_CKS));
+        #endif
         }
     #endif
     #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
@@ -13747,7 +13894,6 @@ int TLSX_WriteRequest(WOLFSSL* ssl, byte* output, byte msgType, word16* pOffset)
 
     return ret;
 }
-
 #endif /* WOLFSSL_TLS13 || !NO_WOLFSSL_CLIENT */
 
 #if defined(WOLFSSL_TLS13) || !defined(NO_WOLFSSL_SERVER)
@@ -14278,7 +14424,19 @@ int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType,
                 ret = EC_PARSE(ssl, input + offset, size, isRequest,
                         &ssl->extensions);
                 break;
-
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+            case TLSX_CKS:
+                WOLFSSL_MSG("CKS extension received");
+                if (!IsAtLeastTLSv1_3(ssl->version) ||
+                    (msgType != client_hello &&
+                     msgType != encrypted_extensions)) {
+                        WOLFSSL_ERROR_VERBOSE(EXT_NOT_ALLOWED);
+                        return EXT_NOT_ALLOWED;
+                }
+                ret = TLSX_CKS_Parse(ssl, (byte *)(input + offset), size,
+                                     &ssl->extensions);
+            break;
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
             case TLSX_EC_POINT_FORMATS:
                 WOLFSSL_MSG("Point Formats extension received");
             #ifdef WOLFSSL_DEBUG_TLS

+ 592 - 33
src/tls13.c

@@ -117,6 +117,7 @@
 #include <wolfssl/wolfcrypt/asn.h>
 #include <wolfssl/wolfcrypt/dh.h>
 #include <wolfssl/wolfcrypt/kdf.h>
+#include <wolfssl/wolfcrypt/signature.h>
 #ifdef NO_INLINE
     #include <wolfssl/wolfcrypt/misc.h>
 #else
@@ -7784,6 +7785,54 @@ static WC_INLINE void EncodeSigAlg(byte hashAlgo, byte hsType, byte* output)
     }
 }
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+/* These match up with what the OQS team has defined. */
+#define HYBRID_SA_MAJOR 0xFE
+#define HYBRID_P256_DILITHIUM_LEVEL2_SA_MINOR    0xA1
+#define HYBRID_RSA3072_DILITHIUM_LEVEL2_SA_MINOR 0xA2
+#define HYBRID_P384_DILITHIUM_LEVEL3_SA_MINOR    0xA4
+#define HYBRID_P521_DILITHIUM_LEVEL5_SA_MINOR    0xA6
+#define HYBRID_P256_FALCON_LEVEL1_SA_MINOR       0x0C
+#define HYBRID_RSA3072_FALCON_LEVEL1_SA_MINOR    0x0D
+#define HYBRID_P521_FALCON_LEVEL5_SA_MINOR       0x0F
+
+static void EncodeDualSigAlg(byte sigAlg, byte altSigAlg, byte* output)
+{
+    /* Initialize output to error indicator. */
+    output[0] = 0x0;
+    output[1] = 0x0;
+
+    if (sigAlg == ecc_dsa_sa_algo && altSigAlg == dilithium_level2_sa_algo) {
+        output[1] = HYBRID_P256_DILITHIUM_LEVEL2_SA_MINOR;
+    }
+    else if (sigAlg == rsa_pss_sa_algo &&
+             altSigAlg == dilithium_level2_sa_algo) {
+        output[1] = HYBRID_RSA3072_DILITHIUM_LEVEL2_SA_MINOR;
+    }
+    else if (sigAlg == ecc_dsa_sa_algo &&
+             altSigAlg == dilithium_level3_sa_algo) {
+        output[1] = HYBRID_P384_DILITHIUM_LEVEL3_SA_MINOR;
+    }
+    else if (sigAlg == ecc_dsa_sa_algo &&
+             altSigAlg == dilithium_level5_sa_algo) {
+        output[1] = HYBRID_P521_DILITHIUM_LEVEL5_SA_MINOR;
+    }
+    else if (sigAlg == ecc_dsa_sa_algo && altSigAlg == falcon_level1_sa_algo) {
+        output[1] = HYBRID_P256_FALCON_LEVEL1_SA_MINOR;
+    }
+    else if (sigAlg == rsa_pss_sa_algo && altSigAlg == falcon_level1_sa_algo) {
+        output[1] = HYBRID_RSA3072_FALCON_LEVEL1_SA_MINOR;
+    }
+    else if (sigAlg == ecc_dsa_sa_algo && altSigAlg == falcon_level5_sa_algo) {
+        output[1] = HYBRID_P521_FALCON_LEVEL5_SA_MINOR;
+    }
+
+    if (output[1] != 0x0) {
+        output[0] = HYBRID_SA_MAJOR;
+    }
+}
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
 /* Decode the signature algorithm.
  *
  * input     The encoded signature algorithm.
@@ -7876,6 +7925,65 @@ static WC_INLINE int DecodeTls13SigAlg(byte* input, byte* hashAlgo,
     return ret;
 }
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+/* Decode the hybrid signature algorithm.
+ *
+ * input     The encoded signature algorithm.
+ * hashalgo  The hash algorithm.
+ * hsType    The signature type.
+ * returns INVALID_PARAMETER if not recognized and 0 otherwise.
+ */
+static WC_INLINE int DecodeTls13HybridSigAlg(byte* input, byte* hashAlg,
+                                             byte *sigAlg, byte *altSigAlg)
+{
+
+    if (input[0] != HYBRID_SA_MAJOR) {
+        return INVALID_PARAMETER;
+    }
+
+    if (input[1] == HYBRID_P256_DILITHIUM_LEVEL2_SA_MINOR) {
+        *sigAlg = ecc_dsa_sa_algo;
+        *hashAlg = 4; /* WC_HASH_TYPE_SHA? Reviewer? */
+        *altSigAlg = dilithium_level2_sa_algo;
+    }
+    else if (input[1] == HYBRID_RSA3072_DILITHIUM_LEVEL2_SA_MINOR) {
+        *sigAlg = rsa_pss_sa_algo;
+        *hashAlg = 4;
+        *altSigAlg = dilithium_level2_sa_algo;
+    }
+    else if (input[1] == HYBRID_P384_DILITHIUM_LEVEL3_SA_MINOR) {
+        *sigAlg = ecc_dsa_sa_algo;
+        *hashAlg = 5;
+        *altSigAlg = dilithium_level3_sa_algo;
+    }
+    else if (input[1] == HYBRID_P521_DILITHIUM_LEVEL5_SA_MINOR) {
+        *sigAlg = ecc_dsa_sa_algo;
+        *hashAlg = 6;
+        *altSigAlg = dilithium_level5_sa_algo;
+    }
+    else if (input[1] == HYBRID_P256_FALCON_LEVEL1_SA_MINOR) {
+        *sigAlg = ecc_dsa_sa_algo;
+        *hashAlg = 4;
+        *altSigAlg = falcon_level1_sa_algo;
+    }
+    else if (input[1] == HYBRID_RSA3072_FALCON_LEVEL1_SA_MINOR) {
+        *sigAlg = rsa_pss_sa_algo;
+        *hashAlg = 4;
+        *altSigAlg = falcon_level1_sa_algo;
+    }
+    else if (input[1] == HYBRID_P521_FALCON_LEVEL5_SA_MINOR) {
+        *sigAlg = ecc_dsa_sa_algo;
+        *hashAlg = 6;
+        *altSigAlg = falcon_level5_sa_algo;
+    }
+    else {
+        return INVALID_PARAMETER;
+    }
+
+    return 0;
+}
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
 /* Get the hash of the messages so far.
  *
  * ssl   The SSL/TLS object.
@@ -8529,6 +8637,10 @@ typedef struct Scv13Args {
     byte   sigAlgo;
     byte*  sigData;
     word16 sigDataSz;
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    byte   altSigAlgo;
+    word16 altSigLen;    /* Only used in the case of both native and alt. */
+#endif
 } Scv13Args;
 
 static void FreeScv13Args(WOLFSSL* ssl, void* pArgs)
@@ -8671,6 +8783,29 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl)
                     ERROR_OUT(NO_PRIVATE_KEY, exit_scv);
             }
             else {
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+                if (wolfSSL_is_server(ssl) &&
+                    ssl->sigSpec != NULL &&
+                    *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_ALTERNATIVE) {
+                    /* In the case of alternative, we swap in the alt. */
+                    if (ssl->ctx->altPrivateKey == NULL) {
+                        ERROR_OUT(NO_PRIVATE_KEY, exit_scv);
+                    }
+                    ssl->buffers.keyType = ssl->buffers.altKeyType;
+                    ssl->buffers.keySz = ssl->buffers.altKeySz;
+                    /* If we own it, free key before overriding it. */
+                    if (ssl->buffers.weOwnKey) {
+                        FreeDer(&ssl->buffers.key);
+                    }
+
+                    /* Transfer ownership. ssl->ctx always owns the alt private
+                     * key. */
+                    ssl->buffers.key = ssl->ctx->altPrivateKey;
+                    ssl->ctx->altPrivateKey = NULL;
+                    ssl->buffers.weOwnKey = 1;
+                    ssl->buffers.weOwnAltKey = 0;
+                }
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
                 ret = DecodePrivateKey(ssl, &args->length);
                 if (ret != 0)
                     goto exit_scv;
@@ -8752,7 +8887,51 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl)
             else {
                 ERROR_OUT(ALGO_ID_E, exit_scv);
             }
-            EncodeSigAlg(ssl->options.hashAlgo, args->sigAlgo, args->verify);
+
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+            if (ssl->peerSigSpec == NULL) {
+                /* The peer did not respond. We didn't send CKS or they don't
+                 * support it. Either way, we do not need to handle dual
+                 * key/sig case. */
+                ssl->sigSpec = NULL;
+                ssl->sigSpecSz = 0;
+            }
+
+            if (wolfSSL_is_server(ssl) &&
+                ssl->sigSpec != NULL &&
+                *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) {
+                /* The native was already decoded. Now we need to do the
+                 * alternative. Note that no swap was done because this case is
+                 * both native and alternative, not just alternative. */
+                if (ssl->ctx->altPrivateKey == NULL) {
+                    ERROR_OUT(NO_PRIVATE_KEY, exit_scv);
+                }
+
+                if (ssl->buffers.altKeyType == falcon_level1_sa_algo ||
+                    ssl->buffers.altKeyType == falcon_level5_sa_algo ||
+                    ssl->buffers.altKeyType == dilithium_level2_sa_algo ||
+                    ssl->buffers.altKeyType == dilithium_level3_sa_algo ||
+                    ssl->buffers.altKeyType == dilithium_level5_sa_algo) {
+                    args->altSigAlgo = ssl->buffers.altKeyType;
+                }
+                else {
+                    ERROR_OUT(ALGO_ID_E, exit_scv);
+                }
+                /* After this call, args->altSigLen has the length we need for
+                 * the alternative signature. */
+                ret = DecodeAltPrivateKey(ssl, &args->altSigLen);
+                if (ret != 0)
+                    goto exit_scv;
+
+                EncodeDualSigAlg(args->sigAlgo, args->altSigAlgo, args->verify);
+                if (args->verify[0] == 0) {
+                    ERROR_OUT(ALGO_ID_E, exit_scv);
+                }
+            }
+            else
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+                EncodeSigAlg(ssl->options.hashAlgo, args->sigAlgo,
+                             args->verify);
 
             if (args->sigData == NULL) {
                 if (ssl->hsType == DYNAMIC_TYPE_RSA) {
@@ -8831,6 +9010,7 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl)
                 }
                 sig->length = ED448_SIG_SIZE;
             }
+
         #endif /* HAVE_ED448 */
         #if defined(HAVE_PQC)
             #if defined(HAVE_FALCON)
@@ -8874,6 +9054,32 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl)
                 #endif
                     );
                 }
+
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+                if (wolfSSL_is_server(ssl) &&
+                    ssl->sigSpec != NULL &&
+                    *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) {
+                    if (ssl->hsAltType == DYNAMIC_TYPE_DILITHIUM) {
+                        /* note the + sig->length; we are appending. */
+                        ret = wc_dilithium_sign_msg(
+                                  args->sigData, args->sigDataSz,
+                                  args->verify + HASH_SIG_SIZE +
+                                  VERIFY_HEADER + sig->length,
+                                  (word32*)&args->altSigLen,
+                                  (dilithium_key*)ssl->hsAltKey, ssl->rng);
+
+                    }
+                    else if (ssl->hsAltType == DYNAMIC_TYPE_FALCON) {
+                        /* note the sig->length; we are appending. */
+                        ret = wc_falcon_sign_msg(args->sigData, args->sigDataSz,
+                                                 args->verify + HASH_SIG_SIZE +
+                                                 VERIFY_HEADER + sig->length,
+                                                 (word32*)&args->altSigLen,
+                                                 (falcon_key*)ssl->hsAltKey,
+                                                 ssl->rng);
+                    }
+                }
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
                 args->length = (word16)sig->length;
             }
         #endif /* HAVE_ECC */
@@ -8933,6 +9139,36 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl)
                     (RsaKey*)ssl->hsKey,
                     ssl->buffers.key
                 );
+
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+                /* In the case of RSA, we need to do the CKS both case here
+                 * BEFORE args->sigData is overwritten!! We keep the sig
+                 * separate and then append later so we don't interfere with the
+                 * checks below. */
+                if (wolfSSL_is_server(ssl) &&
+                    ssl->sigSpec != NULL &&
+                    *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) {
+                    if (ssl->hsAltType == DYNAMIC_TYPE_DILITHIUM) {
+                        /* note the + args->sigLen; we are appending. */
+                        ret = wc_dilithium_sign_msg(args->sigData,
+                                  args->sigDataSz,
+                                  args->verify + HASH_SIG_SIZE + VERIFY_HEADER +
+                                  args->sigLen,
+                                  (word32*)&args->altSigLen,
+                                  (dilithium_key*)ssl->hsAltKey, ssl->rng);
+                    }
+                    else if (ssl->hsAltType == DYNAMIC_TYPE_FALCON) {
+                        /* note the + args->sigLen; we are appending. */
+                        ret = wc_falcon_sign_msg(args->sigData, args->sigDataSz,
+                                                 args->verify + HASH_SIG_SIZE +
+                                                 VERIFY_HEADER + args->sigLen,
+                                                 (word32*)&args->altSigLen,
+                                                 (falcon_key*)ssl->hsAltKey,
+                                                 ssl->rng);
+                    }
+                }
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
                 if (ret == 0) {
                     args->length = (word16)args->sigLen;
 
@@ -8948,6 +9184,11 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl)
                 goto exit_scv;
             }
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+            /* Add in length of the alt sig which will be appended to the sig */
+            args->length += args->altSigLen;
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
             /* Add signature length. */
             c16toa(args->length, args->verify + HASH_SIG_SIZE);
 
@@ -9171,6 +9412,13 @@ typedef struct Dcv13Args {
 
     byte*  sigData;
     word16 sigDataSz;
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    byte   altSigAlgo;
+    byte*  altSigData;
+    word16 altSigDataSz;
+    word16 altSignatureSz;
+    byte   altPeerAuthGood;
+#endif
 } Dcv13Args;
 
 static void FreeDcv13Args(WOLFSSL* ssl, void* pArgs)
@@ -9181,10 +9429,82 @@ static void FreeDcv13Args(WOLFSSL* ssl, void* pArgs)
         XFREE(args->sigData, ssl->heap, DYNAMIC_TYPE_SIGNATURE);
         args->sigData = NULL;
     }
-
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    if (args && args->altSigData != NULL) {
+        XFREE(args->altSigData, ssl->heap, DYNAMIC_TYPE_SIGNATURE);
+        args->altSigData = NULL;
+    }
+#endif
     (void)ssl;
 }
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+/* ssl->peerCert->sapkiDer is the alternative public key. Hopefully it is a
+ * dilithium public key. Convert it into a usable public key. */
+static int decodeDilithiumKey(WOLFSSL* ssl, int level)
+{
+    int keyRet;
+    word32 tmpIdx = 0;
+
+    if (ssl->peerDilithiumKeyPresent)
+        return INVALID_PARAMETER;
+
+    keyRet = AllocKey(ssl, DYNAMIC_TYPE_DILITHIUM,
+                      (void**)&ssl->peerDilithiumKey);
+    if (keyRet != 0)
+        return PEER_KEY_ERROR;
+
+    ssl->peerDilithiumKeyPresent = 1;
+    keyRet = wc_dilithium_init(ssl->peerDilithiumKey);
+    if (keyRet != 0)
+        return PEER_KEY_ERROR;
+
+    keyRet = wc_dilithium_set_level(ssl->peerDilithiumKey, level);
+    if (keyRet != 0)
+        return PEER_KEY_ERROR;
+
+    keyRet = wc_Dilithium_PublicKeyDecode(ssl->peerCert.sapkiDer, &tmpIdx,
+                                          ssl->peerDilithiumKey,
+                                          ssl->peerCert.sapkiLen);
+    if (keyRet != 0)
+        return PEER_KEY_ERROR;
+
+    return 0;
+}
+
+/* ssl->peerCert->sapkiDer is the alternative public key. Hopefully it is a
+ * falcon public key. Convert it into a usable public key. */
+static int decodeFalconKey(WOLFSSL* ssl, int level)
+{
+    int keyRet;
+    word32 tmpIdx = 0;
+
+    if (ssl->peerFalconKeyPresent)
+        return INVALID_PARAMETER;
+
+    keyRet = AllocKey(ssl, DYNAMIC_TYPE_FALCON, (void**)&ssl->peerFalconKey);
+    if (keyRet != 0)
+        return PEER_KEY_ERROR;
+
+    ssl->peerFalconKeyPresent = 1;
+    keyRet = wc_falcon_init(ssl->peerFalconKey);
+    if (keyRet != 0)
+        return PEER_KEY_ERROR;
+
+    keyRet = wc_falcon_set_level(ssl->peerFalconKey, level);
+    if (keyRet != 0)
+        return PEER_KEY_ERROR;
+
+    keyRet = wc_Falcon_PublicKeyDecode(ssl->peerCert.sapkiDer, &tmpIdx,
+                                       ssl->peerFalconKey,
+                                       ssl->peerCert.sapkiLen);
+    if (keyRet != 0)
+        return PEER_KEY_ERROR;
+
+    return 0;
+}
+#endif
+
 /* handle processing TLS v1.3 certificate_verify (15) */
 /* Parse and handle a TLS v1.3 CertificateVerify message.
  *
@@ -9274,8 +9594,36 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input,
             if ((args->idx - args->begin) + ENUM_LEN + ENUM_LEN > totalSz) {
                 ERROR_OUT(BUFFER_ERROR, exit_dcv);
             }
-            ret = DecodeTls13SigAlg(input + args->idx, &args->hashAlgo,
-                                                                &args->sigAlgo);
+
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+            if (ssl->peerSigSpec == NULL) {
+                /* The peer did not respond. We didn't send CKS or they don't
+                 * support it. Either way, we do not need to handle dual
+                 * key/sig case. */
+                ssl->sigSpec = NULL;
+                ssl->sigSpecSz = 0;
+            }
+
+            /* If no CKS extension or either native or alternative, then just
+             * get a normal sigalgo.  But if BOTH, then get the native and alt
+             * sig algos. */
+            if (wolfSSL_is_server(ssl) ||
+                ssl->sigSpec == NULL ||
+                *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_NATIVE ||
+                *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_ALTERNATIVE) {
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+                ret = DecodeTls13SigAlg(input + args->idx, &args->hashAlgo,
+                                        &args->sigAlgo);
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+            }
+            else {
+                ret = DecodeTls13HybridSigAlg(input + args->idx,
+                                              &args->hashAlgo,
+                                              &args->sigAlgo,
+                                              &args->altSigAlgo);
+            }
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
             if (ret < 0)
                 goto exit_dcv;
             args->idx += OPAQUE16_LEN;
@@ -9293,6 +9641,95 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input,
                 ERROR_OUT(BUFFER_ERROR, exit_dcv);
             }
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+            if (!wolfSSL_is_server(ssl) &&
+                (ssl->sigSpec != NULL) &&
+                (*ssl->sigSpec != WOLFSSL_CKS_SIGSPEC_NATIVE)) {
+
+                word16 sa;
+                if (args->altSigAlgo == 0)
+                    sa = args->sigAlgo;
+                else
+                    sa = args->altSigAlgo;
+
+                switch(sa) {
+                case dilithium_level2_sa_algo:
+                    ret = decodeDilithiumKey(ssl, 2);
+                    break;
+                case dilithium_level3_sa_algo:
+                    ret = decodeDilithiumKey(ssl, 3);
+                    break;
+                case dilithium_level5_sa_algo:
+                    ret = decodeDilithiumKey(ssl, 5);
+                    break;
+                case falcon_level1_sa_algo:
+                    ret = decodeFalconKey(ssl, 1);
+                    break;
+                case falcon_level5_sa_algo:
+                    ret = decodeFalconKey(ssl, 5);
+                    break;
+                default:
+                    ERROR_OUT(PEER_KEY_ERROR, exit_dcv);
+                    break;
+                }
+
+                if (ret != 0)
+                    ERROR_OUT(ret, exit_dcv);
+
+                if (*ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_ALTERNATIVE) {
+                    /* Now swap in the alternative. We only support hybrid certs
+                     * where native is RSA or ECC so check that either of those
+                     * are present and then remove it. */
+                    if (ssl->peerRsaKeyPresent &&
+                        ssl->peerEccDsaKeyPresent) {
+                        /* They shouldn't both be present. */
+                        ERROR_OUT(PEER_KEY_ERROR, exit_dcv);
+                    }
+                    else if (ssl->peerRsaKeyPresent) {
+                        FreeKey(ssl, DYNAMIC_TYPE_RSA,
+                                (void**)&ssl->peerRsaKey);
+                        ssl->peerRsaKeyPresent = 0;
+                    }
+                    else if (ssl->peerEccDsaKeyPresent) {
+                        FreeKey(ssl, DYNAMIC_TYPE_ECC,
+                                (void**)&ssl->peerEccDsaKey);
+                        ssl->peerEccDsaKeyPresent = 0;
+                    }
+                    else {
+                        ERROR_OUT(WOLFSSL_NOT_IMPLEMENTED, exit_dcv);
+                    }
+                }
+                else if (*ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) {
+                    /* Use alternative public key to figure out the expected
+                     * alt sig size. We only support Post-quantum key as SAPKI.
+                     */
+                    switch(sa) {
+                    case dilithium_level2_sa_algo:
+                    case dilithium_level3_sa_algo:
+                    case dilithium_level5_sa_algo:
+                        ret = wc_dilithium_sig_size(ssl->peerDilithiumKey);
+                        break;
+                    case falcon_level1_sa_algo:
+                    case falcon_level5_sa_algo:
+                        ret = wc_falcon_sig_size(ssl->peerFalconKey);
+                        break;
+                    default:
+                        ERROR_OUT(PEER_KEY_ERROR, exit_dcv);
+                        break;
+                    }
+
+                    if (ret <= 0) {
+                        ERROR_OUT(PEER_KEY_ERROR, exit_dcv);
+                    }
+                    args->altSignatureSz = ret;
+                    ret = 0;
+                }
+                else {
+                    ERROR_OUT(WOLFSSL_NOT_IMPLEMENTED, exit_dcv);
+                }
+            }
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
             /* Check for public key of required type. */
             /* Assume invalid unless signature algo matches the key provided */
             validSigAlgo = 0;
@@ -9367,14 +9804,48 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input,
                 ERROR_OUT(SIG_VERIFY_E, exit_dcv);
             }
 
-            sig->buffer = (byte*)XMALLOC(args->sz, ssl->heap,
+            sig->length = args->sz;
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+            if (!wolfSSL_is_server(ssl) &&
+                ssl->sigSpec != NULL &&
+                *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) {
+                /* If its RSA, we only hybridize with RSA3072 which has a sig
+                 * size of 384. For ECC, this is actually encoded as an RFC5912
+                 * formatted signature which means we can use the ASN APIs to
+                 * figure out the length. Note that some post-quantum sig algs
+                 * have variable length signatures (Falcon). That is why we
+                 * don't do:
+                 *   sig->length -= args->altSignatureSz; */
+                #define RSA3072_SIG_LEN 384
+                if (args->sigAlgo == rsa_pss_sa_algo) {
+                    sig->length = RSA3072_SIG_LEN;
+                }
+                else if (args->sigAlgo == ecc_dsa_sa_algo) {
+                    word32 tmpIdx = args->idx;
+                    sig->length = wc_SignatureGetSize(WC_SIGNATURE_TYPE_ECC,
+                                      ssl->peerEccDsaKey,
+                                      sizeof(*ssl->peerEccDsaKey));
+                    if (GetSequence(input, &tmpIdx, (int*)&sig->length,
+                                    args->sz) < 0) {
+                        ERROR_OUT(SIG_VERIFY_E, exit_dcv);
+                    }
+                    /* We have to increment by the size of the header. */
+                    sig->length += tmpIdx - args->idx;
+                }
+                else {
+                    ERROR_OUT(WOLFSSL_NOT_IMPLEMENTED, exit_dcv);
+                }
+            }
+#endif
+
+            sig->buffer = (byte*)XMALLOC(sig->length, ssl->heap,
                                          DYNAMIC_TYPE_SIGNATURE);
+
             if (sig->buffer == NULL) {
                 ERROR_OUT(MEMORY_E, exit_dcv);
             }
-            sig->length = args->sz;
-            XMEMCPY(sig->buffer, input + args->idx, args->sz);
 
+            XMEMCPY(sig->buffer, input + args->idx, sig->length);
         #ifdef HAVE_ECC
             if (ssl->peerEccDsaKeyPresent) {
                 WOLFSSL_MSG("Doing ECC peer cert verify");
@@ -9430,29 +9901,29 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input,
             }
        #endif
        #ifdef HAVE_PQC
-            if (ssl->peerFalconKeyPresent) {
-                WOLFSSL_MSG("Doing Falcon peer cert verify");
-
-                args->sigData = (byte*)XMALLOC(MAX_SIG_DATA_SZ, ssl->heap,
+            if (ssl->peerFalconKeyPresent || ssl->peerDilithiumKeyPresent) {
+                word16 sigDataSz;
+                byte *sigData = (byte*)XMALLOC(MAX_SIG_DATA_SZ, ssl->heap,
                                                         DYNAMIC_TYPE_SIGNATURE);
-                if (args->sigData == NULL) {
+                if (sigData == NULL) {
                     ERROR_OUT(MEMORY_E, exit_dcv);
                 }
 
-                CreateSigData(ssl, args->sigData, &args->sigDataSz, 1);
-                ret = 0;
-            }
-
-            if (ssl->peerDilithiumKeyPresent) {
-                WOLFSSL_MSG("Doing Dilithium peer cert verify");
-
-                args->sigData = (byte*)XMALLOC(MAX_SIG_DATA_SZ, ssl->heap,
-                                                        DYNAMIC_TYPE_SIGNATURE);
-                if (args->sigData == NULL) {
-                    ERROR_OUT(MEMORY_E, exit_dcv);
+                CreateSigData(ssl, sigData, &sigDataSz, 1);
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+                if (!wolfSSL_is_server(ssl) &&
+                    ssl->sigSpec != NULL &&
+                    *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) {
+                    /* In this case (BOTH), the pq sig is the alternative. */
+                    args->altSigData = sigData;
+                    args->altSigDataSz = sigDataSz;
+                }
+                else
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+                {
+                    args->sigData = sigData;
+                    args->sigDataSz = sigDataSz;
                 }
-
-                CreateSigData(ssl, args->sigData, &args->sigDataSz, 1);
                 ret = 0;
             }
        #endif
@@ -9492,7 +9963,7 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input,
                 else
             #endif
                 {
-                    ret = EccVerify(ssl, input + args->idx, args->sz,
+                    ret = EccVerify(ssl, input + args->idx, sig->length,
                         args->sigData, args->sigDataSz,
                         ssl->peerEccDsaKey,
                     #ifdef HAVE_PK_CALLBACKS
@@ -9559,15 +10030,43 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input,
         #if defined(HAVE_PQC) && defined(HAVE_FALCON)
             if (ssl->peerFalconKeyPresent) {
                 int res = 0;
+                byte *sigIn = input + args->idx;
+                word32 sigInLen = args->sz;
+                byte *sigData = args->sigData;
+                word32 sigDataSz = args->sigDataSz;
                 WOLFSSL_MSG("Doing Falcon peer cert verify");
-                ret = wc_falcon_verify_msg(input + args->idx, args->sz,
-                                    args->sigData, args->sigDataSz,
-                                    &res, ssl->peerFalconKey);
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+                if (!wolfSSL_is_server(ssl) &&
+                    ssl->sigSpec != NULL &&
+                    *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) {
+                    /* Note: + sig->length; we are skipping the native sig. */
+                    sigIn = input + args->idx + sig->length;
+                    sigInLen = args->sz - sig->length;
+
+                    /* For RSA, something different was verified. */
+                    if (ssl->peerRsaKeyPresent) {
+                        sigData = args->altSigData;
+                        sigDataSz = args->altSigDataSz;
+                    }
+                }
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+                ret = wc_falcon_verify_msg(sigIn, sigInLen,
+                                           sigData, sigDataSz,
+                                           &res, ssl->peerFalconKey);
 
                 if ((ret >= 0) && (res == 1)) {
                     /* CLIENT/SERVER: data verified with public key from
                      * certificate. */
-                    ssl->options.peerAuthGood = 1;
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+                    if (!wolfSSL_is_server(ssl) &&
+                        ssl->sigSpec != NULL &&
+                        *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) {
+                        args->altPeerAuthGood = 1;
+                    }
+                    else
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+                        ssl->options.peerAuthGood = 1;
+
                     FreeKey(ssl, DYNAMIC_TYPE_FALCON,
                                                    (void**)&ssl->peerFalconKey);
                     ssl->peerFalconKeyPresent = 0;
@@ -9577,15 +10076,43 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input,
         #if defined(HAVE_PQC) && defined(HAVE_DILITHIUM)
             if (ssl->peerDilithiumKeyPresent) {
                 int res = 0;
+                byte *sigIn = input + args->idx;
+                word32 sigInLen = args->sz;
+                byte *sigData = args->sigData;
+                word32 sigDataSz = args->sigDataSz;
                 WOLFSSL_MSG("Doing Dilithium peer cert verify");
-                ret = wc_dilithium_verify_msg(input + args->idx, args->sz,
-                                              args->sigData, args->sigDataSz,
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+                if (!wolfSSL_is_server(ssl) &&
+                    ssl->sigSpec != NULL &&
+                    *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) {
+                    /* Go backwards from the end of the signature the size of
+                     * the alt sig to find the beginning of the alt sig. */
+                    sigIn = input + args->idx + args->sz - args->altSignatureSz;
+                    sigInLen = args->altSignatureSz;
+                    /* For RSA, something different was verified. */
+                    if (ssl->peerRsaKeyPresent) {
+                        sigData = args->altSigData;
+                        sigDataSz = args->altSigDataSz;
+                    }
+                }
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+                ret = wc_dilithium_verify_msg(sigIn, sigInLen,
+                                              sigData, sigDataSz,
                                               &res, ssl->peerDilithiumKey);
 
                 if ((ret >= 0) && (res == 1)) {
                     /* CLIENT/SERVER: data verified with public key from
                      * certificate. */
-                    ssl->options.peerAuthGood = 1;
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+                    if (!wolfSSL_is_server(ssl) &&
+                        ssl->sigSpec != NULL &&
+                        *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) {
+                        args->altPeerAuthGood = 1;
+                    }
+                    else
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+                        ssl->options.peerAuthGood = 1;
+
                     FreeKey(ssl, DYNAMIC_TYPE_DILITHIUM,
                             (void**)&ssl->peerDilithiumKey);
                     ssl->peerDilithiumKeyPresent = 0;
@@ -9627,6 +10154,14 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input,
 
         case TLS_ASYNC_FINALIZE:
         {
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+            if (!wolfSSL_is_server(ssl) &&
+                ssl->options.peerAuthGood &&
+                ssl->sigSpec != NULL &&
+                *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) {
+                ssl->options.peerAuthGood = args->altPeerAuthGood;
+            }
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
             ssl->options.havePeerVerify = 1;
 
             /* Set final index */
@@ -12510,6 +13045,30 @@ int wolfSSL_NoKeyShares(WOLFSSL* ssl)
 }
 #endif
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+int wolfSSL_UseCKS(WOLFSSL* ssl, byte *sigSpec, word16 sigSpecSz)
+{
+    if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->ctx->method->version) ||
+        sigSpec == NULL || sigSpecSz == 0)
+        return BAD_FUNC_ARG;
+
+    ssl->sigSpec = sigSpec;
+    ssl->sigSpecSz = sigSpecSz;
+    return WOLFSSL_SUCCESS;
+}
+
+int wolfSSL_CTX_UseCKS(WOLFSSL_CTX* ctx, byte *sigSpec, word16 sigSpecSz)
+{
+    if (ctx == NULL || !IsAtLeastTLSv1_3(ctx->method->version) ||
+        sigSpec == NULL || sigSpecSz == 0)
+        return BAD_FUNC_ARG;
+
+    ctx->sigSpec = sigSpec;
+    ctx->sigSpecSz = sigSpecSz;
+    return WOLFSSL_SUCCESS;
+}
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
 /* Do not send a ticket after TLS v1.3 handshake for resumption.
  *
  * ctx  The SSL/TLS CTX object.

+ 49 - 0
src/x509.c

@@ -7523,6 +7523,45 @@ int wolfSSL_i2d_X509(WOLFSSL_X509* x509, unsigned char** out)
     return derSz;
 }
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+int wc_GeneratePreTBS(DecodedCert* cert, byte *der, int derSz) {
+    int ret = 0;
+    WOLFSSL_X509 *x = NULL;
+
+    if ((cert == NULL) || (der == NULL) || (derSz <= 0)) {
+        return BAD_FUNC_ARG;
+    }
+
+    x = wolfSSL_X509_new();
+    if (x == NULL) {
+        ret = MEMORY_E;
+    }
+    else {
+        ret = CopyDecodedToX509(x, cert);
+    }
+
+    if (ret == 0) {
+        /* Remove the altsigval extension. */
+        XFREE(x->altSigValDer, x->heap, DYNAMIC_TYPE_X509_EXT);
+        x->altSigValDer = NULL;
+        x->altSigValDer = 0;
+        /* Remove sigOID so it won't be encoded. */
+        x->sigOID = 0;
+        /* We now have a PreTBS. Encode it. */
+        ret = wolfssl_x509_make_der(x, 0, der, &derSz, 0);
+        if (ret == WOLFSSL_SUCCESS) {
+            ret = derSz;
+        }
+    }
+
+    if (x != NULL) {
+        wolfSSL_X509_free(x);
+    }
+
+    return ret;
+}
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
 #ifndef NO_BIO
 /**
  * Converts the DER from bio and creates a WOLFSSL_X509 structure from it.
@@ -9928,6 +9967,16 @@ WOLF_STACK_OF(WOLFSSL_X509)* wolfSSL_X509_chain_up_ref(
             XMEMCPY(cert->crlInfo, x509->rawCRLInfo, x509->rawCRLInfoSz);
             cert->crlInfoSz = x509->rawCRLInfoSz;
         }
+
+    #ifdef WOLFSSL_DUAL_ALG_CERTS
+        /* We point to instance in x509 so DON'T need to be free'd. */
+        cert->sapkiDer = x509->sapkiDer;
+        cert->sapkiLen = x509->sapkiLen;
+        cert->altSigAlgDer = x509->altSigAlgDer;
+        cert->altSigAlgLen = x509->altSigAlgLen;
+        cert->altSigValDer = x509->altSigValDer;
+        cert->altSigValLen = x509->altSigValLen;
+    #endif /* WOLFSSL_DUAL_ALG_CERTS */
     #endif /* WOLFSSL_CERT_EXT */
 
     #ifdef WOLFSSL_CERT_REQ

+ 310 - 0
tests/api.c

@@ -835,6 +835,314 @@ static int test_wolfSSL_Method_Allocators(void)
     return EXPECT_RESULT();
 }
 
+#if defined(WOLFSSL_DUAL_ALG_CERTS) && !defined(NO_FILESYSTEM)
+/*----------------------------------------------------------------------------*
+ | Dual algorithm Certificate Tests
+ *----------------------------------------------------------------------------*/
+#define LARGE_TEMP_SZ 4096
+
+/* To better understand this, please see the X9.146 example in wolfssl-examples
+ * repo. */
+static int do_dual_alg_root_certgen(byte **out, char *caKeyFile,
+                                    char *sapkiFile, char *altPrivFile)
+{
+    EXPECT_DECLS;
+    FILE* file = NULL;
+    Cert newCert;
+    DecodedCert preTBS;
+
+    byte caKeyBuf[LARGE_TEMP_SZ];
+    word32 caKeySz = LARGE_TEMP_SZ;
+    byte sapkiBuf[LARGE_TEMP_SZ];
+    word32 sapkiSz = LARGE_TEMP_SZ;
+    byte altPrivBuf[LARGE_TEMP_SZ];
+    word32 altPrivSz = LARGE_TEMP_SZ;
+    byte altSigAlgBuf[LARGE_TEMP_SZ];
+    word32 altSigAlgSz = LARGE_TEMP_SZ;
+    byte scratchBuf[LARGE_TEMP_SZ];
+    word32 scratchSz = LARGE_TEMP_SZ;
+    byte preTbsBuf[LARGE_TEMP_SZ];
+    word32 preTbsSz = LARGE_TEMP_SZ;
+    byte altSigValBuf[LARGE_TEMP_SZ];
+    word32 altSigValSz = LARGE_TEMP_SZ;
+    byte *outBuf = NULL;
+    word32 outSz = LARGE_TEMP_SZ;
+    WC_RNG rng;
+    RsaKey caKey;
+    ecc_key altCaKey;
+    word32 idx = 0;
+    ExpectNotNull(outBuf = (byte*)XMALLOC(outSz, NULL,
+                  DYNAMIC_TYPE_TMP_BUFFER));
+    ExpectIntEQ(wc_InitRng(&rng), 0);
+    XMEMSET(caKeyBuf, 0, caKeySz);
+    ExpectNotNull(file = fopen(caKeyFile, "rb"));
+    ExpectIntGT(caKeySz = (word32)fread(caKeyBuf, 1, caKeySz, file), 0);
+    fclose(file);
+    ExpectIntEQ(wc_InitRsaKey_ex(&caKey, NULL, INVALID_DEVID), 0);
+    idx = 0;
+    ExpectIntEQ(wc_RsaPrivateKeyDecode(caKeyBuf, &idx, &caKey, caKeySz),
+                0);
+    XMEMSET(sapkiBuf, 0, sapkiSz);
+    ExpectNotNull(file = fopen(sapkiFile, "rb"));
+    ExpectIntGT(sapkiSz = (word32)fread(sapkiBuf, 1, sapkiSz, file), 0);
+    fclose(file);
+    XMEMSET(altPrivBuf, 0, altPrivSz);
+    ExpectNotNull(file = fopen(altPrivFile, "rb"));
+    ExpectIntGT(altPrivSz = (word32)fread(altPrivBuf, 1, altPrivSz, file), 0);
+    fclose(file);
+    wc_ecc_init(&altCaKey);
+    idx = 0;
+    ExpectIntEQ(wc_EccPrivateKeyDecode(altPrivBuf, &idx, &altCaKey,
+                                       (word32)altPrivSz), 0);
+    XMEMSET(altSigAlgBuf, 0, altSigAlgSz);
+    ExpectIntGT(altSigAlgSz = SetAlgoID(CTC_SHA256wECDSA, altSigAlgBuf,
+                                         oidSigType, 0), 0);
+    wc_InitCert(&newCert);
+    strncpy(newCert.subject.country, "US", CTC_NAME_SIZE);
+    strncpy(newCert.subject.state, "MT", CTC_NAME_SIZE);
+    strncpy(newCert.subject.locality, "Bozeman", CTC_NAME_SIZE);
+    strncpy(newCert.subject.org, "wolfSSL", CTC_NAME_SIZE);
+    strncpy(newCert.subject.unit, "Engineering", CTC_NAME_SIZE);
+    strncpy(newCert.subject.commonName, "www.wolfssl.com", CTC_NAME_SIZE);
+    strncpy(newCert.subject.email, "root@wolfssl.com", CTC_NAME_SIZE);
+    newCert.sigType = CTC_SHA256wRSA;
+    newCert.isCA    = 1;
+
+    ExpectIntEQ(wc_SetCustomExtension(&newCert, 0, "1.2.3.4.5",
+                (const byte *)"This is NOT a critical extension", 32), 0);
+    ExpectIntEQ(wc_SetCustomExtension(&newCert, 0, "2.5.29.72", sapkiBuf,
+                sapkiSz), 0);
+    ExpectIntEQ(wc_SetCustomExtension(&newCert, 0, "2.5.29.73", altSigAlgBuf,
+                                altSigAlgSz), 0);
+
+    XMEMSET(scratchBuf, 0, scratchSz);
+    ExpectIntGT(scratchSz = wc_MakeSelfCert(&newCert, scratchBuf, scratchSz,
+                &caKey, &rng), 0);
+    wc_InitDecodedCert(&preTBS, scratchBuf, scratchSz, 0);
+    ExpectIntEQ(wc_ParseCert(&preTBS, CERT_TYPE, NO_VERIFY, NULL), 0);
+
+    XMEMSET(preTbsBuf, 0, preTbsSz);
+    ExpectIntGT(preTbsSz = wc_GeneratePreTBS(&preTBS, preTbsBuf, preTbsSz), 0);
+    XMEMSET(altSigValBuf, 0, altSigValSz);
+    ExpectIntGT(altSigValSz = wc_MakeSigWithBitStr(altSigValBuf, altSigValSz,
+                CTC_SHA256wECDSA, preTbsBuf, preTbsSz, ECC_TYPE, &altCaKey,
+                &rng), 0);
+    ExpectIntEQ(wc_SetCustomExtension(&newCert, 0, "2.5.29.74", altSigValBuf,
+                altSigValSz), 0);
+
+    /* Finally, generate the new certificate. */
+    XMEMSET(outBuf, 0, outSz);
+    ExpectIntGT(outSz = wc_MakeSelfCert(&newCert, outBuf, outSz, &caKey, &rng),
+                0);
+    *out = outBuf;
+    wc_FreeRsaKey(&caKey);
+    wc_FreeRng(&rng);
+    return outSz;
+}
+
+static int do_dual_alg_server_certgen(byte **out, char *caKeyFile,
+                                      char *sapkiFile, char *altPrivFile,
+                                      char *serverKeyFile,
+                                      byte *caCertBuf, int caCertSz)
+{
+    EXPECT_DECLS;
+    FILE* file = NULL;
+    Cert newCert;
+    DecodedCert preTBS;
+
+    byte serverKeyBuf[LARGE_TEMP_SZ];
+    word32 serverKeySz = LARGE_TEMP_SZ;
+    byte caKeyBuf[LARGE_TEMP_SZ];
+    word32 caKeySz = LARGE_TEMP_SZ;
+    byte sapkiBuf[LARGE_TEMP_SZ];
+    word32 sapkiSz = LARGE_TEMP_SZ;
+    byte altPrivBuf[LARGE_TEMP_SZ];
+    word32 altPrivSz = LARGE_TEMP_SZ;
+    byte altSigAlgBuf[LARGE_TEMP_SZ];
+    word32 altSigAlgSz = LARGE_TEMP_SZ;
+    byte scratchBuf[LARGE_TEMP_SZ];
+    word32 scratchSz = LARGE_TEMP_SZ;
+    byte preTbsBuf[LARGE_TEMP_SZ];
+    word32 preTbsSz = LARGE_TEMP_SZ;
+    byte altSigValBuf[LARGE_TEMP_SZ];
+    word32 altSigValSz = LARGE_TEMP_SZ;
+    byte *outBuf = NULL;
+    word32 outSz = LARGE_TEMP_SZ;
+    WC_RNG rng;
+    RsaKey caKey;
+    RsaKey serverKey;
+    ecc_key altCaKey;
+    word32 idx = 0;
+    ExpectNotNull(outBuf = (byte*)XMALLOC(outSz, NULL,
+                  DYNAMIC_TYPE_TMP_BUFFER));
+    ExpectIntEQ(wc_InitRng(&rng), 0);
+    XMEMSET(serverKeyBuf, 0, serverKeySz);
+    ExpectNotNull(file = fopen(serverKeyFile, "rb"));
+    ExpectIntGT(serverKeySz = (word32)fread(serverKeyBuf, 1, serverKeySz, file),
+                0);
+    fclose(file);
+    ExpectIntEQ(wc_InitRsaKey_ex(&serverKey, NULL, INVALID_DEVID), 0);
+    idx = 0;
+    ExpectIntEQ(wc_RsaPrivateKeyDecode(serverKeyBuf, &idx, &serverKey,
+                (word32)serverKeySz), 0);
+    XMEMSET(caKeyBuf, 0, caKeySz);
+    ExpectNotNull(file = fopen(caKeyFile, "rb"));
+    ExpectIntGT(caKeySz = (word32)fread(caKeyBuf, 1, caKeySz, file), 0);
+    fclose(file);
+    ExpectIntEQ(wc_InitRsaKey_ex(&caKey, NULL, INVALID_DEVID), 0);
+    idx = 0;
+    ExpectIntEQ(wc_RsaPrivateKeyDecode(caKeyBuf, &idx, &caKey,
+                (word32)caKeySz), 0);
+    XMEMSET(sapkiBuf, 0, sapkiSz);
+    ExpectNotNull(file = fopen(sapkiFile, "rb"));
+    ExpectIntGT(sapkiSz = (word32)fread(sapkiBuf, 1, sapkiSz, file), 0);
+    fclose(file);
+    XMEMSET(altPrivBuf, 0, altPrivSz);
+    ExpectNotNull(file = fopen(altPrivFile, "rb"));
+    ExpectIntGT(altPrivSz = (word32)fread(altPrivBuf, 1, altPrivSz, file), 0);
+    fclose(file);
+    wc_ecc_init(&altCaKey);
+    idx = 0;
+    ExpectIntEQ(wc_EccPrivateKeyDecode(altPrivBuf, &idx, &altCaKey,
+                (word32)altPrivSz), 0);
+    XMEMSET(altSigAlgBuf, 0, altSigAlgSz);
+    ExpectIntGT(altSigAlgSz = SetAlgoID(CTC_SHA256wECDSA, altSigAlgBuf,
+                oidSigType, 0), 0);
+    wc_InitCert(&newCert);
+    strncpy(newCert.subject.country, "US", CTC_NAME_SIZE);
+    strncpy(newCert.subject.state, "MT", CTC_NAME_SIZE);
+    strncpy(newCert.subject.locality, "Bozeman", CTC_NAME_SIZE);
+    strncpy(newCert.subject.org, "wolfSSL", CTC_NAME_SIZE);
+    strncpy(newCert.subject.unit, "Engineering", CTC_NAME_SIZE);
+    strncpy(newCert.subject.commonName, "www.wolfssl.com", CTC_NAME_SIZE);
+    strncpy(newCert.subject.email, "server@wolfssl.com", CTC_NAME_SIZE);
+
+    newCert.sigType = CTC_SHA256wRSA;
+    newCert.isCA    = 0;
+    ExpectIntEQ(wc_SetIssuerBuffer(&newCert, caCertBuf, caCertSz), 0);
+    ExpectIntEQ(wc_SetCustomExtension(&newCert, 0, "1.2.3.4.5",
+                (const byte *)"This is NOT a critical extension", 32), 0);
+    ExpectIntEQ(wc_SetCustomExtension(&newCert, 0, "2.5.29.72", sapkiBuf,
+                sapkiSz), 0);
+    ExpectIntEQ(wc_SetCustomExtension(&newCert, 0, "2.5.29.73", altSigAlgBuf,
+                altSigAlgSz), 0);
+    XMEMSET(scratchBuf, 0, scratchSz);
+    ExpectIntGT(wc_MakeCert(&newCert, scratchBuf, scratchSz, &serverKey, NULL,
+                &rng), 0);
+    ExpectIntGT(scratchSz = wc_SignCert(newCert.bodySz, newCert.sigType,
+                scratchBuf, scratchSz, &caKey, NULL, &rng), 0);
+    wc_InitDecodedCert(&preTBS, scratchBuf, scratchSz, 0);
+    ExpectIntEQ(wc_ParseCert(&preTBS, CERT_TYPE, NO_VERIFY, NULL), 0);
+    XMEMSET(preTbsBuf, 0, preTbsSz);
+    ExpectIntGT(preTbsSz = wc_GeneratePreTBS(&preTBS, preTbsBuf, preTbsSz), 0);
+    XMEMSET(altSigValBuf, 0, altSigValSz);
+    ExpectIntGT(altSigValSz = wc_MakeSigWithBitStr(altSigValBuf, altSigValSz,
+                CTC_SHA256wECDSA, preTbsBuf, preTbsSz, ECC_TYPE, &altCaKey,
+                &rng), 0);
+    ExpectIntEQ(wc_SetCustomExtension(&newCert, 0, "2.5.29.74",
+                 altSigValBuf, altSigValSz), 0);
+    /* Finally, generate the new certificate. */
+    XMEMSET(outBuf, 0, outSz);
+    ExpectIntGT(wc_MakeCert(&newCert, outBuf, outSz, &serverKey, NULL, &rng),
+                0);
+    ExpectIntGT(outSz = wc_SignCert(newCert.bodySz, newCert.sigType, outBuf,
+                outSz, &caKey, NULL, &rng), 0);
+    *out = outBuf;
+    wc_FreeRsaKey(&caKey);
+    wc_FreeRsaKey(&serverKey);
+    wc_FreeRng(&rng);
+    return outSz;
+}
+
+static int do_dual_alg_tls13_connection(byte *caCert, word32 caCertSz,
+                                        byte *serverCert, word32 serverCertSz,
+                                        byte *serverKey, word32 serverKeySz,
+                                        int negative_test)
+{
+    EXPECT_DECLS;
+    WOLFSSL_CTX *ctx_c = NULL;
+    WOLFSSL_CTX *ctx_s = NULL;
+    WOLFSSL *ssl_c = NULL;
+    WOLFSSL *ssl_s = NULL;
+    struct test_memio_ctx test_ctx;
+
+    XMEMSET(&test_ctx, 0, sizeof(test_ctx));
+    ExpectIntEQ(test_memio_setup_ex(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
+                wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
+                caCert, caCertSz, serverCert, serverCertSz,
+                serverKey, serverKeySz), 0);
+    if (negative_test) {
+        ExpectTrue(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL) != 0);
+    }
+    else {
+        ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
+    }
+    wolfSSL_free(ssl_c);
+    wolfSSL_free(ssl_s);
+    wolfSSL_CTX_free(ctx_c);
+    wolfSSL_CTX_free(ctx_s);
+    return EXPECT_RESULT();
+}
+
+static int test_dual_alg_support(void)
+{
+    EXPECT_DECLS;
+    /* Root CA and server keys will be the same. This is only appropriate for
+     * testing. */
+    char keyFile[] = "./certs/ca-key.der";
+    char sapkiFile[] = "./certs/ecc-keyPub.der";
+    char altPrivFile[] = "./certs/ecc-key.der";
+    char wrongPrivFile[] = "./certs/ecc-client-key.der";
+    byte *serverKey = NULL;
+    size_t serverKeySz = 0;
+    byte *root = NULL;
+    int rootSz = 0;
+    byte *server = NULL;
+    int serverSz = 0;
+
+    ExpectIntEQ(load_file(keyFile, &serverKey, &serverKeySz), 0);
+
+    /* Base normal case. */
+    rootSz = do_dual_alg_root_certgen(&root, keyFile, sapkiFile, altPrivFile);
+    ExpectNotNull(root);
+    ExpectIntGT(rootSz, 0);
+    serverSz = do_dual_alg_server_certgen(&server, keyFile, sapkiFile,
+                                        altPrivFile, keyFile, root, rootSz);
+    ExpectNotNull(server);
+    ExpectIntGT(serverSz, 0);
+    ExpectIntEQ(do_dual_alg_tls13_connection(root, rootSz,
+                server, serverSz, serverKey, (word32)serverKeySz, 0),
+                TEST_SUCCESS);
+    XFREE(root, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+    XFREE(server, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+
+    /* Now we try a negative case. Note that we use wrongPrivFile to generate
+     * the alternative signature and then set negative_test to true for the
+     * call to do_dual_alg_tls13_connection(). Its expecting a failed connection
+     * because the signature won't verify. */
+    rootSz = do_dual_alg_root_certgen(&root, keyFile, sapkiFile, wrongPrivFile);
+    ExpectNotNull(root);
+    ExpectIntGT(rootSz, 0);
+    serverSz = do_dual_alg_server_certgen(&server, keyFile, sapkiFile,
+                                          wrongPrivFile, keyFile, root, rootSz);
+    ExpectNotNull(server);
+    ExpectIntGT(serverSz, 0);
+    ExpectIntEQ(do_dual_alg_tls13_connection(root, rootSz,
+                server, serverSz, serverKey, (word32)serverKeySz, 1),
+                TEST_SUCCESS);
+    XFREE(root, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+    XFREE(server, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+
+    free(serverKey);
+
+    return EXPECT_RESULT();
+}
+#else
+static int test_dual_alg_support(void)
+{
+    return TEST_SKIPPED;
+}
+#endif /* WOLFSSL_DUAL_ALG_CERTS && !NO_FILESYSTEM */
 
 /*----------------------------------------------------------------------------*
  | Context
@@ -69275,6 +69583,8 @@ TEST_CASE testCases[] = {
 
     TEST_DECL(test_wolfSSL_Init),
 
+    TEST_DECL(test_dual_alg_support),
+
     /*********************************
      * OpenSSL compatibility API tests
      *********************************/

+ 49 - 8
tests/utils.h

@@ -155,6 +155,12 @@ int test_memio_do_handshake(WOLFSSL *ssl_c, WOLFSSL *ssl_s,
 int test_memio_setup(struct test_memio_ctx *ctx,
     WOLFSSL_CTX **ctx_c, WOLFSSL_CTX **ctx_s, WOLFSSL **ssl_c, WOLFSSL **ssl_s,
     method_provider method_c, method_provider method_s);
+int test_memio_setup_ex(struct test_memio_ctx *ctx,
+    WOLFSSL_CTX **ctx_c, WOLFSSL_CTX **ctx_s, WOLFSSL **ssl_c, WOLFSSL **ssl_s,
+    method_provider method_c, method_provider method_s,
+    byte *caCert, int caCertSz, byte *serverCert, int serverCertSz,
+    byte *serverKey, int serverKeySz);
+
 
 static WC_INLINE int test_memio_write_cb(WOLFSSL *ssl, char *data, int sz,
     void *ctx)
@@ -273,18 +279,32 @@ int test_memio_do_handshake(WOLFSSL *ssl_c, WOLFSSL *ssl_s,
     return 0;
 }
 
-int test_memio_setup(struct test_memio_ctx *ctx,
+int test_memio_setup_ex(struct test_memio_ctx *ctx,
     WOLFSSL_CTX **ctx_c, WOLFSSL_CTX **ctx_s, WOLFSSL **ssl_c, WOLFSSL **ssl_s,
-    method_provider method_c, method_provider method_s)
+    method_provider method_c, method_provider method_s,
+    byte *caCert, int caCertSz, byte *serverCert, int serverCertSz,
+    byte *serverKey, int serverKeySz)
 {
     int ret;
+    (void)caCert;
+    (void)caCertSz;
+    (void)serverCert;
+    (void)serverCertSz;
+    (void)serverKey;
+    (void)serverKeySz;
 
     if (ctx_c != NULL && *ctx_c == NULL) {
         *ctx_c = wolfSSL_CTX_new(method_c());
         if (*ctx_c == NULL)
             return -1;
 #ifndef NO_CERTS
-        ret = wolfSSL_CTX_load_verify_locations(*ctx_c, caCertFile, 0);
+        if (caCert == NULL) {
+            ret = wolfSSL_CTX_load_verify_locations(*ctx_c, caCertFile, 0);
+        }
+        else {
+            ret = wolfSSL_CTX_load_verify_buffer(*ctx_c, caCert, (long)caCertSz,
+                                                 WOLFSSL_FILETYPE_ASN1);
+        }
         if (ret != WOLFSSL_SUCCESS)
             return -1;
 #endif /* NO_CERTS */
@@ -302,15 +322,28 @@ int test_memio_setup(struct test_memio_ctx *ctx,
         if (*ctx_s == NULL)
             return -1;
 #ifndef NO_CERTS
-        ret = wolfSSL_CTX_use_PrivateKey_file(*ctx_s, svrKeyFile,
-            WOLFSSL_FILETYPE_PEM);
+        if (serverKey == NULL) {
+            ret = wolfSSL_CTX_use_PrivateKey_file(*ctx_s, svrKeyFile,
+                WOLFSSL_FILETYPE_PEM);
+        }
+        else {
+            ret = wolfSSL_CTX_use_PrivateKey_buffer(*ctx_s, serverKey,
+                (long)serverKeySz, WOLFSSL_FILETYPE_ASN1);
+        }
         if (ret != WOLFSSL_SUCCESS)
             return- -1;
-        ret = wolfSSL_CTX_use_certificate_file(*ctx_s, svrCertFile,
-                                               WOLFSSL_FILETYPE_PEM);
+
+        if (serverCert == NULL) {
+            ret = wolfSSL_CTX_use_certificate_file(*ctx_s, svrCertFile,
+                                                   WOLFSSL_FILETYPE_PEM);
+        }
+        else {
+            ret = wolfSSL_CTX_use_certificate_chain_buffer_format(*ctx_s,
+                serverCert, (long)serverCertSz, WOLFSSL_FILETYPE_ASN1);
+        }
         if (ret != WOLFSSL_SUCCESS)
             return -1;
-#endif
+#endif /* NO_CERTS */
         wolfSSL_SetIORecv(*ctx_s, test_memio_read_cb);
         wolfSSL_SetIOSend(*ctx_s, test_memio_write_cb);
         if (ctx->s_ciphers != NULL) {
@@ -340,6 +373,14 @@ int test_memio_setup(struct test_memio_ctx *ctx,
 
     return 0;
 }
+
+int test_memio_setup(struct test_memio_ctx *ctx,
+    WOLFSSL_CTX **ctx_c, WOLFSSL_CTX **ctx_s, WOLFSSL **ssl_c, WOLFSSL **ssl_s,
+    method_provider method_c, method_provider method_s)
+{
+    return test_memio_setup_ex(ctx, ctx_c, ctx_s, ssl_c, ssl_s, method_c,
+                               method_s, NULL, 0, NULL, 0, NULL, 0);
+}
 #endif
 
 #if !defined(SINGLE_THREADED) && defined(WOLFSSL_COND)

+ 466 - 30
wolfcrypt/src/asn.c

@@ -16432,6 +16432,7 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
             #if defined(HAVE_FALCON)
                 case FALCON_LEVEL1k:
                 {
+                    word32 idx = 0;
                     sigCtx->verify = 0;
                     sigCtx->key.falcon =
                         (falcon_key*)XMALLOC(sizeof(falcon_key),
@@ -16447,8 +16448,8 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
                         < 0) {
                         goto exit_cs;
                     }
-                    if ((ret = wc_falcon_import_public(key, keySz,
-                        sigCtx->key.falcon)) < 0) {
+                    if ((ret = wc_Falcon_PublicKeyDecode(key, &idx,
+                        sigCtx->key.falcon, keySz)) < 0) {
                         WOLFSSL_MSG("ASN Key import error Falcon Level 1");
                         WOLFSSL_ERROR_VERBOSE(ret);
                         goto exit_cs;
@@ -16457,6 +16458,7 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
                 }
                 case FALCON_LEVEL5k:
                 {
+                    word32 idx = 0;
                     sigCtx->verify = 0;
                     sigCtx->key.falcon =
                         (falcon_key*)XMALLOC(sizeof(falcon_key),
@@ -16472,8 +16474,8 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
                         < 0) {
                         goto exit_cs;
                     }
-                    if ((ret = wc_falcon_import_public(key, keySz,
-                        sigCtx->key.falcon)) < 0) {
+                    if ((ret = wc_Falcon_PublicKeyDecode(key, &idx,
+                        sigCtx->key.falcon, keySz)) < 0) {
                         WOLFSSL_MSG("ASN Key import error Falcon Level 5");
                         WOLFSSL_ERROR_VERBOSE(ret);
                         goto exit_cs;
@@ -16484,6 +16486,7 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
             #if defined(HAVE_DILITHIUM)
                 case DILITHIUM_LEVEL2k:
                 {
+                    word32 idx = 0;
                     sigCtx->verify = 0;
                     sigCtx->key.dilithium =
                         (dilithium_key*)XMALLOC(sizeof(dilithium_key),
@@ -16500,8 +16503,8 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
                         < 0) {
                         goto exit_cs;
                     }
-                    if ((ret = wc_dilithium_import_public(key, keySz,
-                        sigCtx->key.dilithium)) < 0) {
+                    if ((ret = wc_Dilithium_PublicKeyDecode(key, &idx,
+                        sigCtx->key.dilithium, keySz)) < 0) {
                         WOLFSSL_MSG("ASN Key import error Dilithium Level 2");
                         goto exit_cs;
                     }
@@ -16509,6 +16512,7 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
                 }
                 case DILITHIUM_LEVEL3k:
                 {
+                    word32 idx = 0;
                     sigCtx->verify = 0;
                     sigCtx->key.dilithium =
                         (dilithium_key*)XMALLOC(sizeof(dilithium_key),
@@ -16525,15 +16529,16 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
                         < 0) {
                         goto exit_cs;
                     }
-                    if ((ret = wc_dilithium_import_public(key, keySz,
-                        sigCtx->key.dilithium)) < 0) {
-                        WOLFSSL_MSG("ASN Key import error Dilithium Level 5");
+                    if ((ret = wc_Dilithium_PublicKeyDecode(key, &idx,
+                        sigCtx->key.dilithium, keySz)) < 0) {
+                        WOLFSSL_MSG("ASN Key import error Dilithium Level 3");
                         goto exit_cs;
                     }
                     break;
                 }
                 case DILITHIUM_LEVEL5k:
                 {
+                    word32 idx = 0;
                     sigCtx->verify = 0;
                     sigCtx->key.dilithium =
                         (dilithium_key*)XMALLOC(sizeof(dilithium_key),
@@ -16550,8 +16555,8 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
                         < 0) {
                         goto exit_cs;
                     }
-                    if ((ret = wc_dilithium_import_public(key, keySz,
-                        sigCtx->key.dilithium)) < 0) {
+                    if ((ret = wc_Dilithium_PublicKeyDecode(key, &idx,
+                        sigCtx->key.dilithium, keySz)) < 0) {
                         WOLFSSL_MSG("ASN Key import error Dilithium Level 5");
                         goto exit_cs;
                     }
@@ -16561,6 +16566,7 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
             #if defined(HAVE_SPHINCS)
                 case SPHINCS_FAST_LEVEL1k:
                 {
+                    word32 idx = 0;
                     sigCtx->verify = 0;
                     sigCtx->key.sphincs =
                         (sphincs_key*)XMALLOC(sizeof(sphincs_key),
@@ -16577,8 +16583,8 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
                         < 0) {
                         goto exit_cs;
                     }
-                    if ((ret = wc_sphincs_import_public(key, keySz,
-                        sigCtx->key.sphincs)) < 0) {
+                    if ((ret = wc_Sphincs_PublicKeyDecode(key, &idx,
+                        sigCtx->key.sphincs, keySz)) < 0) {
                         WOLFSSL_MSG("ASN Key import err: Sphincs-fast Level1");
                         goto exit_cs;
                     }
@@ -16586,6 +16592,7 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
                 }
                 case SPHINCS_FAST_LEVEL3k:
                 {
+                    word32 idx = 0;
                     sigCtx->verify = 0;
                     sigCtx->key.sphincs =
                         (sphincs_key*)XMALLOC(sizeof(sphincs_key),
@@ -16602,8 +16609,8 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
                         < 0) {
                         goto exit_cs;
                     }
-                    if ((ret = wc_sphincs_import_public(key, keySz,
-                        sigCtx->key.sphincs)) < 0) {
+                    if ((ret = wc_Sphincs_PublicKeyDecode(key, &idx,
+                        sigCtx->key.sphincs, keySz)) < 0) {
                         WOLFSSL_MSG("ASN Key import err: Sphincs-fast Level3");
                         goto exit_cs;
                     }
@@ -16611,6 +16618,7 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
                 }
                 case SPHINCS_FAST_LEVEL5k:
                 {
+                    word32 idx = 0;
                     sigCtx->verify = 0;
                     sigCtx->key.sphincs =
                         (sphincs_key*)XMALLOC(sizeof(sphincs_key),
@@ -16627,16 +16635,16 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
                         < 0) {
                         goto exit_cs;
                     }
-                    if ((ret = wc_sphincs_import_public(key, keySz,
-                        sigCtx->key.sphincs)) < 0) {
+                    if ((ret = wc_Sphincs_PublicKeyDecode(key, &idx,
+                        sigCtx->key.sphincs, keySz)) < 0) {
                         WOLFSSL_MSG("ASN Key import err: Sphincs-fast Level5");
                         goto exit_cs;
                     }
                     break;
                 }
-
                 case SPHINCS_SMALL_LEVEL1k:
                 {
+                    word32 idx = 0;
                     sigCtx->verify = 0;
                     sigCtx->key.sphincs =
                         (sphincs_key*)XMALLOC(sizeof(sphincs_key),
@@ -16653,8 +16661,8 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
                         < 0) {
                         goto exit_cs;
                     }
-                    if ((ret = wc_sphincs_import_public(key, keySz,
-                        sigCtx->key.sphincs)) < 0) {
+                    if ((ret = wc_Sphincs_PublicKeyDecode(key, &idx,
+                        sigCtx->key.sphincs, keySz)) < 0) {
                         WOLFSSL_MSG("ASN Key import err: Sphincs-fast Level1");
                         goto exit_cs;
                     }
@@ -16662,6 +16670,7 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
                 }
                 case SPHINCS_SMALL_LEVEL3k:
                 {
+                    word32 idx = 0;
                     sigCtx->verify = 0;
                     sigCtx->key.sphincs =
                         (sphincs_key*)XMALLOC(sizeof(sphincs_key),
@@ -16678,8 +16687,8 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
                         < 0) {
                         goto exit_cs;
                     }
-                    if ((ret = wc_sphincs_import_public(key, keySz,
-                        sigCtx->key.sphincs)) < 0) {
+                    if ((ret = wc_Sphincs_PublicKeyDecode(key, &idx,
+                        sigCtx->key.sphincs, keySz)) < 0) {
                         WOLFSSL_MSG("ASN Key import err: Sphincs-fast Level3");
                         goto exit_cs;
                     }
@@ -16687,6 +16696,7 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
                 }
                 case SPHINCS_SMALL_LEVEL5k:
                 {
+                    word32 idx = 0;
                     sigCtx->verify = 0;
                     sigCtx->key.sphincs =
                         (sphincs_key*)XMALLOC(sizeof(sphincs_key),
@@ -16703,8 +16713,8 @@ static int ConfirmSignature(SignatureCtx* sigCtx,
                         < 0) {
                         goto exit_cs;
                     }
-                    if ((ret = wc_sphincs_import_public(key, keySz,
-                        sigCtx->key.sphincs)) < 0) {
+                    if ((ret = wc_Sphincs_PublicKeyDecode(key, &idx,
+                        sigCtx->key.sphincs, keySz)) < 0) {
                         WOLFSSL_MSG("ASN Key import err: Sphincs-fast Level5");
                         goto exit_cs;
                     }
@@ -17215,6 +17225,41 @@ exit_cs:
     return ret;
 }
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+int wc_ConfirmAltSignature(
+    const byte* buf, word32 bufSz,
+    const byte* key, word32 keySz, word32 keyOID,
+    const byte* sig, word32 sigSz, word32 sigOID,
+    void *heap)
+{
+    int ret = 0;
+#ifdef WOLFSSL_SMALL_STACK
+    SignatureCtx* sigCtx = (SignatureCtx*)XMALLOC(sizeof(*sigCtx), heap,
+        DYNAMIC_TYPE_SIGNATURE);
+    if (sigCtx == NULL) {
+        ret = MEMORY_E;
+    }
+#else
+    SignatureCtx  sigCtx[1];
+    (void)heap;
+#endif
+
+    if (ret == 0) {
+        InitSignatureCtx(sigCtx, heap, INVALID_DEVID);
+
+        ret = ConfirmSignature(sigCtx, buf, bufSz, key, keySz,
+             keyOID, sig, sigSz, sigOID, NULL, 0, NULL);
+
+        FreeSignatureCtx(sigCtx);
+    }
+
+#ifdef WOLFSSL_SMALL_STACK
+    if (sigCtx != NULL)
+        XFREE(sigCtx, heap, DYNAMIC_TYPE_SIGNATURE);
+#endif
+    return ret;
+}
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
 
 #ifndef IGNORE_NAME_CONSTRAINTS
 
@@ -20322,6 +20367,161 @@ static int DecodeSubjInfoAcc(const byte* input, word32 sz, DecodedCert* cert)
 }
 #endif /* WOLFSSL_SUBJ_INFO_ACC */
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+/* The subject alternative public key is an extension that holds the same thing
+ * as a subject public key. */
+static const ASNItem subjAltPubKeyInfoASN[] = {
+                           /* subjectPublicKeyInfo SubjectPublicKeyInfo */
+/* ALT_SPUBKEYINFO_SEQ          */      { 0, ASN_SEQUENCE, 1, 1, 0 },
+                           /* algorithm          AlgorithmIdentifier */
+                           /* AlgorithmIdentifier ::= SEQUENCE */
+/* ALT_SPUBKEYINFO_ALGO_SEQ     */         { 1, ASN_SEQUENCE, 1, 1, 0 },
+                          /* Algorithm    OBJECT IDENTIFIER */
+/* ALT_SPUBKEYINFO_ALGO_OID     */             { 2, ASN_OBJECT_ID, 0, 0, 0 },
+                           /* parameters   ANY defined by algorithm OPTIONAL */
+/* ALT_SPUBKEYINFO_ALGO_NULL    */             { 2, ASN_TAG_NULL, 0, 0, 1 },
+/* ALT_SPUBKEYINFO_ALGO_CURVEID */             { 2, ASN_OBJECT_ID, 0, 0, 1 },
+#ifdef WC_RSA_PSS
+/* ALT_SPUBKEYINFO_ALGO_P_SEQ   */             { 2, ASN_SEQUENCE, 1, 0, 1 },
+#endif
+                           /* subjectPublicKey   BIT STRING */
+/* ALT_SPUBKEYINFO_PUBKEY       */          { 1, ASN_BIT_STRING, 0, 0, 0 }
+};
+
+#define subjAltPubKeyInfoASN_Length (sizeof(subjAltPubKeyInfoASN) / \
+                                     sizeof(ASNItem))
+
+enum {
+    ALT_SPUBKEYINFO_SEQ = 0,
+    ALT_SPUBKEYINFO_ALGO_SEQ,
+    ALT_SPUBKEYINFO_ALGO_OID,
+    ALT_SPUBKEYINFO_ALGO_NULL,
+    ALT_SPUBKEYINFO_ALGO_CURVEID,
+#ifdef WC_RSA_PSS
+    ALT_SPUBKEYINFO_ALGO_P_SEQ,
+#endif
+    ALT_SPUBKEYINFO_PUBKEY
+};
+
+static int DecodeSubjAltPubKeyInfo(const byte* input, int sz, DecodedCert* cert)
+{
+    int ret = 0;
+    word32 idx = 0;
+    DECL_ASNGETDATA(dataASN, subjAltPubKeyInfoASN_Length);
+
+    WOLFSSL_ENTER("DecodeSubjAltPubKeyInfo");
+
+    if (ret == 0) {
+        CALLOC_ASNGETDATA(dataASN, subjAltPubKeyInfoASN_Length, ret,
+                          cert->heap);
+        (void)cert;
+    }
+
+    if (ret == 0) {
+        GetASN_OID(&dataASN[ALT_SPUBKEYINFO_ALGO_OID], oidKeyType);
+        GetASN_OID(&dataASN[ALT_SPUBKEYINFO_ALGO_CURVEID], oidCurveType);
+
+        ret = GetASN_Items(subjAltPubKeyInfoASN, dataASN,
+                           subjAltPubKeyInfoASN_Length, 1, input, &idx,
+                           (word32)sz);
+    }
+
+    if (ret == 0) {
+        /* dataASN[ALT_SPUBKEYINFO_SEQ].data.u8 */
+        cert->sapkiDer = (byte *)input;
+        /* dataASN[ALT_SPUBKEYINFO_SEQ].length */
+        cert->sapkiLen = sz;
+        cert->sapkiOID = dataASN[ALT_SPUBKEYINFO_ALGO_OID].data.oid.sum;
+    }
+
+    FREE_ASNGETDATA(dataASN, cert->heap);
+    WOLFSSL_LEAVE("DecodeSubjAltPubKeyInfo", ret);
+    return ret;
+}
+
+/* The alternative signature algorithm extension holds the same thing as a
+ * as a signature algorithm identifier. */
+static const ASNItem altSigAlgASN[] = {
+                          /* AltSigAlg            AlgorithmIdentifier */
+                          /* AlgorithmIdentifier ::= SEQUENCE */
+/* ALTSIG_ALGOID_SEQ                */ { 0, ASN_SEQUENCE, 1, 1, 0 },
+                          /* Algorithm    OBJECT IDENTIFIER */
+/* ALTSIG_ALGOID_OID                */     { 1, ASN_OBJECT_ID, 0, 0, 0 },
+                          /* parameters   ANY defined by algorithm OPTIONAL */
+/* ALTSIG_ALGOID_PARAMS_NULL        */     { 1, ASN_TAG_NULL, 0, 0, 1 },
+#ifdef WC_RSA_PSS
+/* ALTSIG_ALGOID_PARAMS             */     { 1, ASN_SEQUENCE, 1, 0, 1 },
+#endif
+};
+
+#define altSigAlgASN_Length (sizeof(altSigAlgASN) / sizeof(ASNItem))
+
+enum {
+    ALTSIG_ALGOID_SEQ = 0,
+    ALTSIG_ALGOID_OID,
+    ALTSIG_ALGOID_PARAMS_NULL,
+#ifdef WC_RSA_PSS
+    ALTSIG_ALGOID_PARAMS,
+#endif
+};
+
+static int DecodeAltSigAlg(const byte* input, int sz, DecodedCert* cert)
+{
+    int ret = 0;
+    word32 idx = 0;
+    DECL_ASNGETDATA(dataASN, altSigAlgASN_Length);
+
+    WOLFSSL_ENTER("DecodeAltSigAlg");
+
+    if (ret == 0) {
+        CALLOC_ASNGETDATA(dataASN, altSigAlgASN_Length, ret, cert->heap);
+        (void)cert;
+    }
+
+    if (ret == 0) {
+        GetASN_OID(&dataASN[ALTSIG_ALGOID_OID], oidSigType);
+
+        ret = GetASN_Items(altSigAlgASN, dataASN,
+                           altSigAlgASN_Length, 1, input, &idx,
+                           (word32)sz);
+    }
+
+    if (ret == 0) {
+        cert->altSigAlgDer = dataASN[ALTSIG_ALGOID_SEQ].data.u8;
+        cert->altSigAlgLen = dataASN[ALTSIG_ALGOID_SEQ].length;
+        cert->altSigAlgOID = dataASN[ALTSIG_ALGOID_OID].data.oid.sum;
+    }
+
+    FREE_ASNGETDATA(dataASN, cert->heap);
+    WOLFSSL_LEAVE("DecodeAltSigAlg", ret);
+    return ret;
+}
+
+/* The alternative signature value extension holds an ASN.1 bitstring just
+ * like a traditional signature in the certificate. */
+static int DecodeAltSigVal(const byte* input, int sz, DecodedCert* cert)
+{
+    (void)cert;
+    int ret = 0;
+    word32 idx = 0;
+    int len = 0;
+
+    WOLFSSL_ENTER("DecodeAltSigVal");
+
+    if (ret == 0) {
+        ret = CheckBitString(input, &idx, &len, sz, 1, NULL);
+    }
+
+    if (ret == 0) {
+        cert->altSigValDer = (byte *)input + idx;
+        cert->altSigValLen = len;
+    }
+
+    WOLFSSL_LEAVE("DecodeAltSigVal", ret);
+    return ret;
+}
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
 /* Macro to check if bit is set, if not sets and return success.
     Otherwise returns failure */
 /* Macro required here because bit-field operation */
@@ -20569,6 +20769,23 @@ static int DecodeExtensionType(const byte* input, word32 length, word32 oid,
                 return ASN_PARSE_E;
             break;
     #endif
+    #ifdef WOLFSSL_DUAL_ALG_CERTS
+        case SUBJ_ALT_PUB_KEY_INFO_OID:
+            VERIFY_AND_SET_OID(cert->extSapkiSet);
+            if (DecodeSubjAltPubKeyInfo(&input[idx], length, cert) < 0)
+                return ASN_PARSE_E;
+            break;
+        case ALT_SIG_ALG_OID:
+            VERIFY_AND_SET_OID(cert->extAltSigAlgSet);
+            if (DecodeAltSigAlg(&input[idx], length, cert) < 0)
+                return ASN_PARSE_E;
+            break;
+        case ALT_SIG_VAL_OID:
+            VERIFY_AND_SET_OID(cert->extAltSigValSet);
+            if (DecodeAltSigVal(&input[idx], length, cert) < 0)
+                return ASN_PARSE_E;
+            break;
+    #endif /* WOLFSSL_DUAL_ALG_CERTS */
         default:
             if (isUnknownExt != NULL)
                 *isUnknownExt = 1;
@@ -23688,6 +23905,9 @@ int wc_PemGetHeaderFooter(int type, const char** header, const char** footer)
     #endif
         case RSA_TYPE:
         case PRIVATEKEY_TYPE:
+    #ifdef WOLFSSL_DUAL_ALG_CERTS
+        case ALT_PRIVATEKEY_TYPE:
+    #endif
             if (header) *header = BEGIN_RSA_PRIV;
             if (footer) *footer = END_RSA_PRIV;
             ret = 0;
@@ -24195,7 +24415,11 @@ int PemToDer(const unsigned char* buff, long longSz, int type,
             break;
         }
 
-        if (type == PRIVATEKEY_TYPE) {
+        if (type == PRIVATEKEY_TYPE
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+            || type == ALT_PRIVATEKEY_TYPE
+#endif
+           ) {
             if (header == BEGIN_RSA_PRIV) {
                 header = BEGIN_PRIV_KEY;
                 footer = END_PRIV_KEY;
@@ -24268,7 +24492,11 @@ int PemToDer(const unsigned char* buff, long longSz, int type,
 
     if (!headerEnd) {
 #ifdef OPENSSL_EXTRA
-        if (type == PRIVATEKEY_TYPE) {
+        if (type == PRIVATEKEY_TYPE
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+            || type == ALT_PRIVATEKEY_TYPE
+#endif
+           ) {
             /* see if there is a -----BEGIN * PRIVATE KEY----- header */
             headerEnd = XSTRNSTR((char*)buff, PRIV_KEY_SUFFIX, sz);
             if (headerEnd) {
@@ -24347,7 +24575,11 @@ int PemToDer(const unsigned char* buff, long longSz, int type,
 
     if (keyFormat) {
         /* keyFormat is Key_Sum enum */
-        if (type == PRIVATEKEY_TYPE) {
+        if (type == PRIVATEKEY_TYPE
+        #ifdef WOLFSSL_DUAL_ALG_CERTS
+            || type == ALT_PRIVATEKEY_TYPE
+        #endif
+           ) {
         #ifndef NO_RSA
             if (header == BEGIN_RSA_PRIV)
                 *keyFormat = RSAk;
@@ -27627,6 +27859,17 @@ static const ASNItem static_certExtsASN[] = {
 /* CRLINFO_SEQ   */    { 0, ASN_SEQUENCE, 1, 1, 0 },
 /* CRLINFO_OID   */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
 /* CRLINFO_STR   */        { 1, ASN_OCTET_STRING, 0, 0, 0 },
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+/* SAPKI_SEQ     */    { 0, ASN_SEQUENCE, 1, 1, 0 },
+/* SAPKI_OID     */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
+/* SAPKI_STR     */        { 1, ASN_OCTET_STRING, 0, 0, 0 },
+/* ALTSIGALG_SEQ */    { 0, ASN_SEQUENCE, 1, 1, 0 },
+/* ALTSIGALG_OID */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
+/* ALTSIGALG_STR */        { 1, ASN_OCTET_STRING, 0, 0, 0 },
+/* ALTSIGVAL_SEQ */    { 0, ASN_SEQUENCE, 1, 1, 0 },
+/* ALTSIGVAL_OID */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
+/* ALTSIGVAL_STR */        { 1, ASN_OCTET_STRING, 0, 0, 0 },
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
 /* CUSTOM_SEQ    */    { 0, ASN_SEQUENCE, 1, 1, 0 },
 /* CUSTOM_OID    */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
 /* CUSTOM_STR    */        { 1, ASN_OCTET_STRING, 0, 0, 0 },
@@ -27670,6 +27913,17 @@ enum {
     CERTEXTSASN_IDX_CRLINFO_SEQ,
     CERTEXTSASN_IDX_CRLINFO_OID,
     CERTEXTSASN_IDX_CRLINFO_STR,
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    CERTEXTSASN_IDX_SAPKI_SEQ,
+    CERTEXTSASN_IDX_SAPKI_OID,
+    CERTEXTSASN_IDX_SAPKI_STR,
+    CERTEXTSASN_IDX_ALTSIGALG_SEQ,
+    CERTEXTSASN_IDX_ALTSIGALG_OID,
+    CERTEXTSASN_IDX_ALTSIGALG_STR,
+    CERTEXTSASN_IDX_ALTSIGVAL_SEQ,
+    CERTEXTSASN_IDX_ALTSIGVAL_OID,
+    CERTEXTSASN_IDX_ALTSIGVAL_STR,
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
     CERTEXTSASN_IDX_CUSTOM_SEQ,
     CERTEXTSASN_IDX_CUSTOM_OID,
     CERTEXTSASN_IDX_CUSTOM_STR,
@@ -27708,7 +27962,12 @@ static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz,
     static const byte nsCertOID[] = { 0x60, 0x86, 0x48, 0x01,
                                       0x86, 0xF8, 0x42, 0x01, 0x01 };
     static const byte crlInfoOID[] = { 0x55, 0x1D, 0x1F };
-#endif
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    static const byte sapkiOID[] = { 0x55, 0x1d, 0x48 };
+    static const byte altSigAlgOID[] = { 0x55, 0x1d, 0x49 };
+    static const byte altSigValOID[] = { 0x55, 0x1d, 0x4a };
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+#endif /* WOLFSSL_CERT_EXT */
 
 #ifdef WOLFSSL_SMALL_STACK
 #if defined(WOLFSSL_CUSTOM_OID) && defined(WOLFSSL_CERT_EXT)
@@ -27940,6 +28199,47 @@ static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz,
                     CERTEXTSASN_IDX_CRLINFO_STR);
         }
 
+    #ifdef WOLFSSL_DUAL_ALG_CERTS
+        if (cert->sapkiDer != NULL) {
+            /* Set subject alternative public key info OID and data. */
+            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_SAPKI_OID], sapkiOID,
+                    sizeof(sapkiOID));
+            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_SAPKI_STR], cert->sapkiDer,
+                    cert->sapkiLen);
+        }
+        else {
+            /* Don't write out subject alternative public key info. */
+            SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_SAPKI_SEQ,
+                    CERTEXTSASN_IDX_SAPKI_STR);
+        }
+
+        if (cert->altSigAlgDer != NULL) {
+            /* Set alternative signature algorithm OID and data. */
+            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_ALTSIGALG_OID], altSigAlgOID,
+                    sizeof(altSigAlgOID));
+            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_ALTSIGALG_STR],
+                    cert->altSigAlgDer, cert->altSigAlgLen);
+        }
+        else {
+            /* Don't write out alternative signature algorithm. */
+            SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_ALTSIGALG_SEQ,
+                    CERTEXTSASN_IDX_ALTSIGALG_STR);
+        }
+
+        if (cert->altSigValDer != NULL) {
+            /* Set alternative signature value OID and data. */
+            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_ALTSIGVAL_OID], altSigValOID,
+                    sizeof(altSigValOID));
+            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_ALTSIGVAL_STR],
+                    cert->altSigValDer, cert->altSigValLen);
+        }
+        else {
+            /* Don't write out alternative signature value. */
+            SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_ALTSIGVAL_SEQ,
+                    CERTEXTSASN_IDX_ALTSIGVAL_STR);
+        }
+    #endif /* WOLFSSL_DUAL_ALG_CERTS */
+
     #if defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_CUSTOM_OID)
         /* encode a custom oid and value */
         if (cert->extCustom.oidSz > 0) {
@@ -29280,8 +29580,24 @@ static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz,
                        (byte)cert->version);
         SetASN_Buffer(&dataASN[X509CERTASN_IDX_TBS_SERIAL], cert->serial,
                 (word32)cert->serialSz);
-        SetASN_OID(&dataASN[X509CERTASN_IDX_TBS_ALGOID_OID],
-                   (word32)cert->sigType, oidSigType);
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+        if (cert->sigType == 0) {
+            /* sigOID being 0 indicates preTBS. Do not encode signature. */
+            dataASN[X509CERTASN_IDX_TBS_ALGOID_SEQ].noOut = 1;
+            dataASN[X509CERTASN_IDX_TBS_ALGOID_OID].noOut = 1;
+            dataASN[X509CERTASN_IDX_TBS_ALGOID_PARAMS_NULL].noOut = 1;
+    #ifdef WC_RSA_PSS
+            dataASN[X509CERTASN_IDX_TBS_ALGOID_PARAMS].noOut = 1;
+    #endif
+
+        }
+        else
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+        {
+            SetASN_OID(&dataASN[X509CERTASN_IDX_TBS_ALGOID_OID],
+                       (word32)cert->sigType, oidSigType);
+        }
+
         if (IsSigAlgoECC((word32)cert->sigType)) {
             /* No NULL tagged item with ECDSA and EdDSA signature OIDs. */
             dataASN[X509CERTASN_IDX_TBS_ALGOID_PARAMS_NULL].noOut = 1;
@@ -30560,6 +30876,126 @@ static int SignCert(int requestSz, int sType, byte* buf, word32 buffSz,
     return sigSz;
 }
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+int wc_MakeSigWithBitStr(byte *sig, int sigSz, int sType, byte* buf,
+                         word32 bufSz, int keyType, void* key, WC_RNG* rng)
+{
+    RsaKey*            rsaKey = NULL;
+    ecc_key*           eccKey = NULL;
+    ed25519_key*       ed25519Key = NULL;
+    ed448_key*         ed448Key = NULL;
+    falcon_key*        falconKey = NULL;
+    dilithium_key*     dilithiumKey = NULL;
+    sphincs_key*       sphincsKey = NULL;
+
+    int ret = 0;
+    int headerSz;
+    void* heap = NULL;
+    CertSignCtx  certSignCtx_lcl;
+    CertSignCtx* certSignCtx = &certSignCtx_lcl;
+
+    if ((sig == NULL) || (sigSz <= 0)) {
+        return BAD_FUNC_ARG;
+    }
+
+    XMEMSET(certSignCtx, 0, sizeof(*certSignCtx));
+
+    switch (keyType)
+    {
+        case RSA_TYPE:
+            rsaKey = (RsaKey*)key;
+            break;
+        case ECC_TYPE:
+            eccKey = (ecc_key*)key;
+            break;
+        case ED25519_TYPE:
+            ed25519Key = (ed25519_key*)key;
+            break;
+        case ED448_TYPE:
+            ed448Key = (ed448_key*)key;
+            break;
+        case FALCON_LEVEL1_TYPE:
+        case FALCON_LEVEL5_TYPE:
+            falconKey = (falcon_key*)key;
+            break;
+        case DILITHIUM_LEVEL2_TYPE:
+        case DILITHIUM_LEVEL3_TYPE:
+        case DILITHIUM_LEVEL5_TYPE:
+            dilithiumKey = (dilithium_key*)key;
+            break;
+        case SPHINCS_FAST_LEVEL1_TYPE:
+        case SPHINCS_FAST_LEVEL3_TYPE:
+        case SPHINCS_FAST_LEVEL5_TYPE:
+        case SPHINCS_SMALL_LEVEL1_TYPE:
+        case SPHINCS_SMALL_LEVEL3_TYPE:
+        case SPHINCS_SMALL_LEVEL5_TYPE:
+            sphincsKey = (sphincs_key*)key;
+            break;
+        default:
+            return BAD_FUNC_ARG;
+    }
+
+    /* locate ctx */
+    if (rsaKey) {
+    #ifndef NO_RSA
+    #ifdef WOLFSSL_ASYNC_CRYPT
+        certSignCtx = &rsaKey->certSignCtx;
+    #endif
+        heap = rsaKey->heap;
+    #else
+        return NOT_COMPILED_IN;
+    #endif /* NO_RSA */
+    }
+    else if (eccKey) {
+    #ifdef HAVE_ECC
+    #ifdef WOLFSSL_ASYNC_CRYPT
+        certSignCtx = &eccKey->certSignCtx;
+    #endif
+        heap = eccKey->heap;
+    #else
+        return NOT_COMPILED_IN;
+    #endif /* HAVE_ECC */
+    }
+
+    if (certSignCtx->sig == NULL) {
+        certSignCtx->sig = (byte*)XMALLOC(MAX_ENCODED_SIG_SZ, heap,
+            DYNAMIC_TYPE_TMP_BUFFER);
+        if (certSignCtx->sig == NULL)
+            return MEMORY_E;
+    }
+
+    ret = MakeSignature(certSignCtx, buf, (word32)bufSz, certSignCtx->sig,
+        MAX_ENCODED_SIG_SZ, rsaKey, eccKey, ed25519Key, ed448Key,
+        falconKey, dilithiumKey, sphincsKey, rng, (word32)sType, heap);
+#ifdef WOLFSSL_ASYNC_CRYPT
+    if (ret == WC_PENDING_E) {
+        /* Not free'ing certSignCtx->sig here because it could still be in use
+         * with async operations. */
+        return ret;
+    }
+#endif
+
+    if (ret <= 0) {
+        return ret;
+    }
+
+    headerSz = SetBitString(ret, 0, NULL);
+    if (headerSz + ret > sigSz) {
+        ret = BUFFER_E;
+    }
+
+    if (ret > 0) {
+       sig += SetBitString(ret, 0, sig);
+       XMEMCPY(sig, certSignCtx->sig, ret);
+       ret += headerSz;
+    }
+
+    XFREE(certSignCtx->sig, heap, DYNAMIC_TYPE_TMP_BUFFER);
+    certSignCtx->sig = NULL;
+    return ret;
+}
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
 int wc_SignCert_ex(int requestSz, int sType, byte* buf, word32 buffSz,
                    int keyType, void* key, WC_RNG* rng)
 {

+ 5 - 0
wolfcrypt/src/dilithium.c

@@ -815,6 +815,11 @@ int wc_Dilithium_PublicKeyDecode(const byte* input, word32* inOutIdx,
         return BAD_FUNC_ARG;
     }
 
+    ret = wc_dilithium_import_public(input, inSz, key);
+    if (ret == 0) {
+        return 0;
+    }
+
     if (key->level == 2) {
         keytype = DILITHIUM_LEVEL2k;
     }

+ 5 - 0
wolfcrypt/src/falcon.c

@@ -747,6 +747,11 @@ int wc_Falcon_PublicKeyDecode(const byte* input, word32* inOutIdx,
         return BAD_FUNC_ARG;
     }
 
+    ret = wc_falcon_import_public(input, inSz, key);
+    if (ret == 0) {
+        return 0;
+    }
+
     if (key->level == 1) {
         keytype = FALCON_LEVEL1k;
     }

+ 5 - 0
wolfcrypt/src/sphincs.c

@@ -896,6 +896,11 @@ int wc_Sphincs_PublicKeyDecode(const byte* input, word32* inOutIdx,
         return BAD_FUNC_ARG;
     }
 
+    ret = wc_sphincs_import_public(input, inSz, key);
+    if (ret == 0) {
+        return 0;
+    }
+
     if ((key->level == 1) && (key->optim == FAST_VARIANT)) {
         keytype = SPHINCS_FAST_LEVEL1k;
     }

+ 57 - 5
wolfssl/internal.h

@@ -1549,7 +1549,8 @@ enum Misc {
 #endif
 #endif
 #ifdef HAVE_PQC
-    ENCRYPT_LEN     = 4600,     /* allow 4600 byte buffer for dilithium. */
+    ENCRYPT_LEN     = 5120,     /* Allow 5k byte buffer for dilithium and
+                                 * hybridization with other algs. */
 #else
 #ifndef NO_PSK
     ENCRYPT_LEN     = (ENCRYPT_BASE_BITS / 8) + MAX_PSK_ID_LEN + 2,
@@ -1797,6 +1798,8 @@ enum Misc {
 
 #if defined(HAVE_PQC)
     MAX_CERT_VERIFY_SZ = 6000,            /* For Dilithium */
+#elif defined(WOLFSSL_CERT_EXT)
+    MAX_CERT_VERIFY_SZ = 2048,            /* For larger extensions */
 #elif !defined(NO_RSA) && defined(WOLFSSL_MAX_RSA_BITS)
     MAX_CERT_VERIFY_SZ = WOLFSSL_MAX_RSA_BITS / 8, /* max RSA bytes */
 #elif defined(HAVE_ECC)
@@ -2172,6 +2175,9 @@ WOLFSSL_LOCAL int  CreateDevPrivateKey(void** pkey, byte* data, word32 length,
                                        void* heap, int devId);
 #endif
 WOLFSSL_LOCAL int  DecodePrivateKey(WOLFSSL *ssl, word16* length);
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+WOLFSSL_LOCAL int  DecodeAltPrivateKey(WOLFSSL *ssl, word16* length);
+#endif
 #ifdef WOLF_PRIVATE_KEY_ID
 WOLFSSL_LOCAL int GetPrivateKeySigSize(WOLFSSL* ssl);
 #ifndef NO_ASN
@@ -2849,6 +2855,10 @@ typedef enum {
     #ifdef WOLFSSL_QUIC
     TLSX_KEY_QUIC_TP_PARAMS         = 0x0039, /* RFC 9001, ch. 8.2 */
     #endif
+    #ifdef WOLFSSL_DUAL_ALG_CERTS
+    TLSX_CKS                        = 0xff92, /* X9.146; ff indcates personal
+                                               * use and 92 is hex for 146. */
+    #endif
 #endif
     TLSX_RENEGOTIATION_INFO         = 0xff01,
 #ifdef WOLFSSL_QUIC
@@ -3376,8 +3386,11 @@ WOLFSSL_LOCAL int TLSX_KeyShare_Parse(WOLFSSL* ssl, const byte* input,
         word16 length, byte msgType);
 WOLFSSL_LOCAL int TLSX_KeyShare_Parse_ClientHello(const WOLFSSL* ssl,
         const byte* input, word16 length, TLSX** extensions);
-
-
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+WOLFSSL_LOCAL int TLSX_CKS_Parse(WOLFSSL* ssl, byte* input,
+                                 word16 length, TLSX** extensions);
+WOLFSSL_LOCAL int TLSX_CKS_Set(WOLFSSL* ssl, TLSX** extensions);
+#endif
 #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
 
 enum PskDecryptReturn {
@@ -3558,6 +3571,12 @@ struct WOLFSSL_CTX {
     byte        privateKeyLabel:1;
     int         privateKeySz;
     int         privateKeyDevId;
+
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    DerBuffer*  altPrivateKey;
+    byte        altPrivateKeyType;
+    int         altPrivateKeySz;
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
 #ifdef OPENSSL_ALL
     WOLFSSL_EVP_PKEY* privateKeyPKey;
 #endif
@@ -3934,6 +3953,10 @@ struct WOLFSSL_CTX {
 #if defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS)
     byte doAppleNativeCertValidationFlag:1;
 #endif /* defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS) */
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    byte *sigSpec;
+    word16 sigSpecSz;
+#endif
 };
 
 WOLFSSL_LOCAL
@@ -4501,7 +4524,10 @@ typedef struct Buffers {
                                               when got WANT_WRITE            */
     byte            weOwnCert;             /* SSL own cert flag */
     byte            weOwnCertChain;        /* SSL own cert chain flag */
-    byte            weOwnKey;              /* SSL own key  flag */
+    byte            weOwnKey;              /* SSL own key flag */
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    byte            weOwnAltKey;           /* SSL own alt key flag */
+#endif
     byte            weOwnDH;               /* SSL own dh (p,g)  flag */
 #ifndef NO_DH
     buffer          serverDH_P;            /* WOLFSSL_CTX owns, unless we own */
@@ -4518,6 +4544,11 @@ typedef struct Buffers {
     byte            keyLabel:1;            /* Key data is a label not data */
     int             keySz;                 /* Size of RSA key */
     int             keyDevId;              /* Device Id for key */
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    DerBuffer*      altKey;                /* WOLFSSL_CTX owns, unless we own */
+    byte            altKeyType;            /* Type of key: dilithium, falcon */
+    int             altKeySz;              /* Size of key */
+#endif
     DerBuffer*      certChain;             /* WOLFSSL_CTX owns, unless we own */
                  /* chain after self, in DER, with leading size for each cert */
 #ifdef WOLFSSL_TLS13
@@ -5096,6 +5127,17 @@ struct WOLFSSL_X509 {
     byte            notBeforeData[CTC_DATE_SIZE];
     byte            notAfterData[CTC_DATE_SIZE];
 #endif
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    /* Subject Alternative Public Key Info */
+    byte *sapkiDer;
+    int sapkiLen;
+    /* Alternative Signature Algorithm */
+    byte *altSigAlgDer;
+    int altSigAlgLen;
+    /* Alternative Signature Value */
+    byte *altSigValDer;
+    int altSigValLen;
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
 };
 
 
@@ -5462,6 +5504,11 @@ struct WOLFSSL {
                                          * allocated from heap */
     word32          hsType;             /* Type of Handshake key (hsKey) */
     WOLFSSL_CIPHER  cipher;
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    void*           hsAltKey;           /* Handshake key (dilithium, falcon)
+                                         * allocated from heap */
+    word32          hsAltType;          /* Type of Handshake key (hsAltKey) */
+#endif
 #ifndef WOLFSSL_AEAD_ONLY
     hmacfp          hmac;
 #endif
@@ -5883,7 +5930,12 @@ struct WOLFSSL {
 #if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_KEYLOGFILE)
     SSLSnifferSecretCb snifferSecretCb;
 #endif /* WOLFSSL_SNIFFER && WOLFSSL_SNIFFER_KEYLOGFILE */
-
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    byte *sigSpec;         /* This pointer never owns the memory. */
+    word16 sigSpecSz;
+    byte *peerSigSpec;     /* This pointer always owns the memory. */
+    word16 peerSigSpecSz;
+#endif
 };
 
 /*

+ 15 - 2
wolfssl/ssl.h

@@ -1056,8 +1056,11 @@ WOLFSSL_ABI WOLFSSL_API int wolfSSL_CTX_use_certificate_file(
     WOLFSSL_CTX* ctx, const char* file, int format);
 WOLFSSL_ABI WOLFSSL_API int wolfSSL_CTX_use_PrivateKey_file(
     WOLFSSL_CTX* ctx, const char* file, int format);
-
-#endif
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+WOLFSSL_API int wolfSSL_CTX_use_AltPrivateKey_file(
+    WOLFSSL_CTX* ctx, const char* file, int format);
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+#endif /* !NO_FILESYSTEM && !NO_CERTS */
 
 #ifndef NO_CERTS
 #define WOLFSSL_LOAD_FLAG_NONE          0x00000000
@@ -4008,6 +4011,16 @@ WOLFSSL_API int wolfSSL_UseKeyShare(WOLFSSL* ssl, word16 group);
 WOLFSSL_API int wolfSSL_NoKeyShares(WOLFSSL* ssl);
 #endif
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+#define WOLFSSL_CKS_SIGSPEC_NATIVE      0x0001
+#define WOLFSSL_CKS_SIGSPEC_ALTERNATIVE 0x0002
+#define WOLFSSL_CKS_SIGSPEC_BOTH        0x0003
+#define WOLFSSL_CKS_SIGSPEC_EXTERNAL    0x0004
+
+WOLFSSL_API int wolfSSL_UseCKS(WOLFSSL* ssl, byte *sigSpec, word16 sigSpecSz);
+WOLFSSL_API int wolfSSL_CTX_UseCKS(WOLFSSL_CTX* ctx, byte *sigSpec,
+                                   word16 sigSpecSz);
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
 
 /* Secure Renegotiation */
 #if defined(HAVE_SECURE_RENEGOTIATION) || defined(HAVE_SERVER_RENEGOTIATION_INFO)

+ 44 - 3
wolfssl/wolfcrypt/asn.h

@@ -932,7 +932,11 @@ enum Misc_ASN {
     MIN_DATE_SIZE       =  12,
     MAX_DATE_SIZE       =  32,
     ASN_GEN_TIME_SZ     =  15,     /* 7 numbers * 2 + Zulu tag */
-#ifndef NO_RSA
+#ifdef HAVE_SPHINCS
+    MAX_ENCODED_SIG_SZ  = 51200,
+#elif defined(HAVE_PQC)
+    MAX_ENCODED_SIG_SZ  = 5120;
+#elif !defined(NO_RSA)
 #ifdef WOLFSSL_HAPROXY
     MAX_ENCODED_SIG_SZ  = 1024,    /* Supports 8192 bit keys */
 #else
@@ -1221,7 +1225,12 @@ enum Extensions_Sum {
     AKEY_PACKAGE_OID          = 1048, /* 2.16.840.1.101.2.1.2.78.5
                                         RFC 5958  - Asymmetric Key Packages */
     FASCN_OID = 419, /* 2.16.840.1.101.3.6.6 Federal PKI Policy FASC-N */
-    UPN_OID   = 265  /* 1.3.6.1.4.1.311.20.2.3 UPN */
+    UPN_OID   = 265, /* 1.3.6.1.4.1.311.20.2.3 UPN */
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    SUBJ_ALT_PUB_KEY_INFO_OID = 186, /* 2.5.29.72 subject alt public key info */
+    ALT_SIG_ALG_OID           = 187, /* 2.5.29.73 alt sig alg */
+    ALT_SIG_VAL_OID           = 188  /* 2.5.29.74 alt sig val */
+#endif
 };
 
 enum CertificatePolicy_Sum {
@@ -1926,6 +1935,11 @@ struct DecodedCert {
 #ifdef WOLFSSL_SUBJ_INFO_ACC
     byte extSubjInfoAccSet : 1;
 #endif
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    byte extSapkiSet : 1;
+    byte extAltSigAlgSet : 1;
+    byte extAltSigValSet : 1;
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
 #if defined(WOLFSSL_SEP) || defined(WOLFSSL_QT)
     byte extCertPolicyCrit : 1;
 #endif
@@ -1939,6 +1953,19 @@ struct DecodedCert {
     && defined(HAVE_OID_DECODING)
     wc_UnknownExtCallback unknownExtCallback;
 #endif
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    /* Subject Alternative Public Key Info */
+    byte *sapkiDer;
+    int sapkiLen;
+    word32 sapkiOID;
+    /* Alternative Signature Algorithm */
+    byte *altSigAlgDer;
+    int altSigAlgLen;
+    word32 altSigAlgOID;
+    /* Alternative Signature Value */
+    byte *altSigValDer;
+    int altSigValLen;
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
 };
 
 #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
@@ -1988,6 +2015,13 @@ struct Signer {
 #if defined(WOLFSSL_RENESAS_TSIP_TLS) || defined(WOLFSSL_RENESAS_FSPSM_TLS)
     word32 cm_idx;
 #endif
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    /* The Subject Alternative Public Key Info (SAPKI) will NOT be cached.
+     * Caching of it is NOT SUPPORTED yet. */
+    byte *sapkiDer;
+    int sapkiLen;
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
     Signer* next;
 };
 
@@ -2098,6 +2132,13 @@ WOLFSSL_API int wc_CheckCertSigPubKey(const byte* cert, word32 certSz,
                                       void* heap, const byte* pubKey,
                                       word32 pubKeySz, int pubKeyOID);
 #endif
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+WOLFSSL_LOCAL int wc_ConfirmAltSignature(
+    const byte* buf, word32 bufSz,
+    const byte* key, word32 keySz, word32 keyOID,
+    const byte* sig, word32 sigSz, word32 sigOID,
+    void *heap);
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
 #if (defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT) || \
     (defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT)))
 WOLFSSL_LOCAL int wc_CertGetPubKey(const byte* cert, word32 certSz,
@@ -2240,7 +2281,7 @@ WOLFSSL_LOCAL word32 SetBitString(word32 len, byte unusedBits, byte* output);
 WOLFSSL_LOCAL word32 SetImplicit(byte tag,byte number,word32 len,byte* output);
 WOLFSSL_LOCAL word32 SetExplicit(byte number, word32 len, byte* output);
 WOLFSSL_LOCAL word32 SetSet(word32 len, byte* output);
-WOLFSSL_LOCAL word32 SetAlgoID(int algoOID,byte* output,int type,int curveSz);
+WOLFSSL_API word32 SetAlgoID(int algoOID, byte* output, int type, int curveSz);
 WOLFSSL_LOCAL int SetMyVersion(word32 version, byte* output, int header);
 WOLFSSL_LOCAL int SetSerialNumber(const byte* sn, word32 snSz, byte* output,
     word32 outputSz, int maxSnSz);

+ 24 - 0
wolfssl/wolfcrypt/asn_public.h

@@ -141,6 +141,7 @@ enum EncPkcs8Types {
 enum CertType {
     CERT_TYPE       = 0,
     PRIVATEKEY_TYPE,
+    ALT_PRIVATEKEY_TYPE,
     DH_PARAM_TYPE,
     DSA_PARAM_TYPE,
     CRL_TYPE,
@@ -513,6 +514,19 @@ typedef struct Cert {
     byte     issRaw[sizeof(CertName)];   /* raw issuer info */
     byte     sbjRaw[sizeof(CertName)];   /* raw subject info */
 #endif
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+    /* These will not point to managed buffers. They will point to buffers that
+     * are managed by others. No cleanup neccessary. */
+    /* Subject Alternative Public Key Info */
+    byte *sapkiDer;
+    int sapkiLen;
+    /* Alternative Signature Algorithm */
+    byte *altSigAlgDer;
+    int altSigAlgLen;
+    /* Alternative Signature Value */
+    byte *altSigValDer;
+    int altSigValLen;
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
 #ifdef WOLFSSL_CERT_REQ
     char     challengePw[CTC_NAME_SIZE];
     char     unstructuredName[CTC_NAME_SIZE];
@@ -572,6 +586,11 @@ WOLFSSL_API int wc_SignCert_ex(int requestSz, int sType, byte* buf,
                                WC_RNG* rng);
 WOLFSSL_API int wc_SignCert(int requestSz, int sType, byte* buf, word32 buffSz,
                             RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng);
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+WOLFSSL_API int wc_MakeSigWithBitStr(byte *sig, int sigSz, int sType, byte* buf,
+                                     word32 bufSz, int keyType, void* key,
+                                     WC_RNG* rng);
+#endif
 WOLFSSL_ABI
 WOLFSSL_API int wc_MakeSelfCert(Cert* cert, byte* buf, word32 buffSz,
                                 RsaKey* key, WC_RNG* rng);
@@ -941,6 +960,11 @@ WOLFSSL_API int wc_GetFASCNFromCert(struct DecodedCert* cert,
                                     byte* fascn, word32* fascnSz);
 #endif /* WOLFSSL_FPKI */
 
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+WOLFSSL_API int wc_GeneratePreTBS(struct DecodedCert* cert, byte *der,
+                                  int derSz);
+#endif
+
 #if !defined(XFPRINTF) || defined(NO_FILESYSTEM) || \
     defined(NO_STDIO_FILESYSTEM) && defined(WOLFSSL_ASN_PRINT)
 #undef WOLFSSL_ASN_PRINT

+ 42 - 2
wolfssl/wolfcrypt/settings.h

@@ -316,7 +316,44 @@
     #endif
 #endif
 
-/* OpenSSL compat layer */
+/* ---------------------------------------------------------------------------
+ * Dual Algorithm Certificate Required Features.
+ * ---------------------------------------------------------------------------
+ */
+#ifdef WOLFSSL_DUAL_ALG_CERTS
+
+#ifndef WOLFSSL_ASN_TEMPLATE
+    #error "Dual alg cert support requires the ASN.1 template feature."
+#endif
+
+#ifdef NO_RSA
+    #error "Need RSA or else dual alg cert example will not work."
+#endif
+
+#ifndef HAVE_ECC
+    #error "Need ECDSA or else dual alg cert example will not work."
+#endif
+
+#undef WOLFSSL_CERT_GEN
+#define WOLFSSL_CERT_GEN
+
+#undef WOLFSSL_CUSTOM_OID
+#define WOLFSSL_CUSTOM_OID
+
+#undef HAVE_OID_ENCODING
+#define HAVE_OID_ENCODING
+
+#undef WOLFSSL_CERT_EXT
+#define WOLFSSL_CERT_EXT
+
+#undef OPENSSL_EXTRA
+#define OPENSSL_EXTRA
+#endif /* WOLFSSL_DUAL_ALG_CERTS */
+
+/* ---------------------------------------------------------------------------
+ * OpenSSL compat layer
+ * ---------------------------------------------------------------------------
+ */
 #if defined(OPENSSL_EXTRA) && !defined(OPENSSL_COEXIST)
 #undef  WOLFSSL_ALWAYS_VERIFY_CB
 #define WOLFSSL_ALWAYS_VERIFY_CB
@@ -343,7 +380,10 @@
 #define WOLFSSL_SESSION_ID_CTX
 #endif /* OPENSSL_EXTRA && !OPENSSL_COEXIST */
 
-/* Special small OpenSSL compat layer for certs */
+/* ---------------------------------------------------------------------------
+ * Special small OpenSSL compat layer for certs
+ * ---------------------------------------------------------------------------
+ */
 #ifdef OPENSSL_EXTRA_X509_SMALL
 #undef WOLFSSL_EKU_OID
 #define WOLFSSL_EKU_OID