From 9be390250d531d46e621bdafc4b66d0c1430c27d Mon Sep 17 00:00:00 2001 From: Anthony Hu Date: Thu, 18 Jan 2024 16:20:57 -0500 Subject: [PATCH] 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`. --- configure.ac | 10 + examples/client/client.c | 18 + examples/server/server.c | 37 +- src/internal.c | 206 ++++++++++- src/ssl.c | 204 +++++++++-- src/tls.c | 166 ++++++++- src/tls13.c | 625 +++++++++++++++++++++++++++++++-- src/x509.c | 49 +++ tests/api.c | 310 ++++++++++++++++ tests/utils.h | 57 ++- wolfcrypt/src/asn.c | 496 ++++++++++++++++++++++++-- wolfcrypt/src/dilithium.c | 5 + wolfcrypt/src/falcon.c | 5 + wolfcrypt/src/sphincs.c | 5 + wolfssl/internal.h | 62 +++- wolfssl/ssl.h | 17 +- wolfssl/wolfcrypt/asn.h | 47 ++- wolfssl/wolfcrypt/asn_public.h | 24 ++ wolfssl/wolfcrypt/settings.h | 44 ++- 19 files changed, 2262 insertions(+), 125 deletions(-) diff --git a/configure.ac b/configure.ac index e72bb7247..1250784fb 100644 --- a/configure.ac +++ b/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" diff --git a/examples/client/client.c b/examples/client/client.c index 0141be1fb..020e0f55d 100644 --- a/examples/client/client.c +++ b/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) diff --git a/examples/server/server.c b/examples/server/server.c index c88f37594..50b6d0a88 100644 --- a/examples/server/server.c +++ b/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 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 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); diff --git a/src/internal.c b/src/internal.c index 31334eb43..528ad1dc7 100644 --- a/src/internal.c +++ b/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) diff --git a/src/ssl.c b/src/ssl.c index 1cdd8b3b2..950a0a4f8 100644 --- a/src/ssl.c +++ b/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; +#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_LEVEL3k) { - ssl->buffers.keyType = dilithium_level3_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; } - else if (*keyFormat == DILITHIUM_LEVEL5k) { - ssl->buffers.keyType = dilithium_level5_sa_algo; - } - ssl->buffers.keySz = *keySz; } else { - if (*keyFormat == DILITHIUM_LEVEL2k) { - ctx->privateKeyType = dilithium_level2_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_LEVEL3k) { - ctx->privateKeyType = dilithium_level3_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; } - else if (*keyFormat == DILITHIUM_LEVEL5k) { - ctx->privateKeyType = dilithium_level5_sa_algo; - } - 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 diff --git a/src/tls.c b/src/tls.c index b8b7e8c54..0b4cea8e5 100644 --- a/src/tls.c +++ b/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 diff --git a/src/tls13.c b/src/tls13.c index aea324339..20f066f58 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -117,6 +117,7 @@ #include #include #include +#include #ifdef NO_INLINE #include #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. diff --git a/src/x509.c b/src/x509.c index 73369f3d3..13b7f32dd 100644 --- a/src/x509.c +++ b/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 diff --git a/tests/api.c b/tests/api.c index df8584bc8..cf2b3fe5c 100644 --- a/tests/api.c +++ b/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 *********************************/ diff --git a/tests/utils.h b/tests/utils.h index dbc816051..601c56ca5 100644 --- a/tests/utils.h +++ b/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) diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 24c6d0a04..a326e9dc9 100644 --- a/wolfcrypt/src/asn.c +++ b/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) { diff --git a/wolfcrypt/src/dilithium.c b/wolfcrypt/src/dilithium.c index 93e79e965..9f8eda48a 100644 --- a/wolfcrypt/src/dilithium.c +++ b/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; } diff --git a/wolfcrypt/src/falcon.c b/wolfcrypt/src/falcon.c index ae9947ef8..957e97c81 100644 --- a/wolfcrypt/src/falcon.c +++ b/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; } diff --git a/wolfcrypt/src/sphincs.c b/wolfcrypt/src/sphincs.c index e26ab5181..a0196ce39 100644 --- a/wolfcrypt/src/sphincs.c +++ b/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; } diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 752495b28..7e418b996 100644 --- a/wolfssl/internal.h +++ b/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 }; /* diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 907b3691b..fb68c675f 100644 --- a/wolfssl/ssl.h +++ b/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) diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 25f81fefd..a1a5e0f62 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/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); diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index 4085b8177..0f58152df 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/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 diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 53825ae62..c34f7f996 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/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