diff --git a/src/internal.c b/src/internal.c index 89384b30cf..6e1866e92d 100644 --- a/src/internal.c +++ b/src/internal.c @@ -7685,6 +7685,7 @@ static int SetSSL_CTX_CertsAndKeys(WOLFSSL* ssl, WOLFSSL_CTX* ctx) if (ret != 0) { return ret; } + ssl->buffers.weOwnAltKey = 1; /* Blind the private key for the SSL with new random mask. */ wolfssl_priv_der_blind_toggle(ssl->buffers.altKey, ctx->altPrivateKeyMask); diff --git a/src/ssl.c b/src/ssl.c index 9957a6d2f3..b071d53cf7 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -8861,6 +8861,19 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx) ssl->buffers.weOwnCertChain = 1; } #else + /* drop what this object allocated itself before pointing at the ctx's */ + if (ssl->buffers.weOwnCert) { + FreeDer(&ssl->buffers.certificate); + #ifdef KEEP_OUR_CERT + wolfSSL_X509_free(ssl->ourCert); + ssl->ourCert = NULL; + #endif + ssl->buffers.weOwnCert = 0; + } + if (ssl->buffers.weOwnCertChain) { + FreeDer(&ssl->buffers.certChain); + ssl->buffers.weOwnCertChain = 0; + } /* ctx owns certificate, certChain and key */ ssl->buffers.certificate = ctx->certificate; ssl->buffers.certChain = ctx->certChain; @@ -8870,8 +8883,9 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx) #endif #ifndef WOLFSSL_BLIND_PRIVATE_KEY #ifdef WOLFSSL_COPY_KEY - if (ssl->buffers.key != NULL && ssl->buffers.weOwnKey) { + if (ssl->buffers.weOwnKey) { FreeDer(&ssl->buffers.key); + ssl->buffers.weOwnKey = 0; } if (ctx->privateKey != NULL) { ret = AllocCopyDer(&ssl->buffers.key, ctx->privateKey->buffer, @@ -8887,12 +8901,18 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx) ssl->buffers.key = ctx->privateKey; } #else + if (ssl->buffers.weOwnKey) { + FreeDer(&ssl->buffers.key); + ssl->buffers.weOwnKey = 0; + } ssl->buffers.key = ctx->privateKey; #endif #else if (ctx->privateKey != NULL) { - if (ssl->buffers.key != NULL && ssl->buffers.weOwnKey) { + if (ssl->buffers.weOwnKey) { FreeDer(&ssl->buffers.key); + FreeDer(&ssl->buffers.keyMask); + ssl->buffers.weOwnKey = 0; } ret = AllocCopyDer(&ssl->buffers.key, ctx->privateKey->buffer, ctx->privateKey->length, ctx->privateKey->type, @@ -8900,6 +8920,7 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx) if (ret != 0) { return NULL; } + ssl->buffers.weOwnKey = 1; /* Blind the private key for the SSL with new random mask. */ wolfssl_priv_der_blind_toggle(ssl->buffers.key, ctx->privateKeyMask); ret = wolfssl_priv_der_blind(ssl->rng, ssl->buffers.key, @@ -8925,15 +8946,25 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx) ssl->options.haveSlhDsaSig = ctx->haveSlhDsaSig; #ifdef WOLFSSL_DUAL_ALG_CERTS #ifndef WOLFSSL_BLIND_PRIVATE_KEY + if (ssl->buffers.weOwnAltKey) { + FreeDer(&ssl->buffers.altKey); + ssl->buffers.weOwnAltKey = 0; + } ssl->buffers.altKey = ctx->altPrivateKey; #else if (ctx->altPrivateKey != NULL) { + if (ssl->buffers.weOwnAltKey) { + FreeDer(&ssl->buffers.altKey); + FreeDer(&ssl->buffers.altKeyMask); + ssl->buffers.weOwnAltKey = 0; + } ret = AllocCopyDer(&ssl->buffers.altKey, ctx->altPrivateKey->buffer, ctx->altPrivateKey->length, ctx->altPrivateKey->type, ctx->altPrivateKey->heap); if (ret != 0) { return NULL; } + ssl->buffers.weOwnAltKey = 1; /* Blind the private key for the SSL with new random mask. */ wolfssl_priv_der_blind_toggle(ssl->buffers.altKey, ctx->altPrivateKeyMask); diff --git a/src/tls13.c b/src/tls13.c index 193bbd3916..e161744069 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -10305,6 +10305,7 @@ typedef struct Scv13Args { byte sigAlgo; #ifdef WOLFSSL_DUAL_ALG_CERTS byte altSigAlgo; + byte altSwapped; /* alternative key already moved into buffers.key */ #endif byte fragActive; /* current fragment laid out, record build may pend */ } Scv13Args; @@ -10468,7 +10469,8 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl) else { #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->sigSpec != NULL && - *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_ALTERNATIVE) { + *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_ALTERNATIVE && + !args->altSwapped) { /* In the case of alternative, we swap in the alt. */ if (ssl->buffers.altKey == NULL) { ERROR_OUT(NO_PRIVATE_KEY, exit_scv); @@ -10483,12 +10485,19 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl) #endif } - /* Swap keys */ + /* Move the alternative key over. Ownership travels with + * the buffer so that only one field releases it. */ ssl->buffers.key = ssl->buffers.altKey; ssl->buffers.weOwnKey = ssl->buffers.weOwnAltKey; + ssl->buffers.altKey = NULL; + ssl->buffers.weOwnAltKey = 0; + /* This state is re-entered after a pending asynchronous + * operation, so only move the key across once. */ + args->altSwapped = 1; #ifdef WOLFSSL_BLIND_PRIVATE_KEY ssl->buffers.keyMask = ssl->buffers.altKeyMask; + ssl->buffers.altKeyMask = NULL; /* Unblind the alternative key before decoding */ wolfssl_priv_der_blind_toggle(ssl->buffers.key, ssl->buffers.keyMask); #endif diff --git a/tests/api.c b/tests/api.c index 2304f24f86..75f428f640 100644 --- a/tests/api.c +++ b/tests/api.c @@ -1141,6 +1141,7 @@ static int do_dual_alg_root_certgen(byte **out, char *caKeyFile, 0); *out = outBuf; + wc_ecc_free(&altCaKey); wc_FreeRsaKey(&caKey); wc_FreeRng(&rng); wc_FreeDecodedCert(&preTBS); @@ -1279,6 +1280,7 @@ static int do_dual_alg_server_certgen(byte **out, char *caKeyFile, ExpectIntGT(outSz = wc_SignCert(newCert.bodySz, newCert.sigType, outBuf, outSz, &caKey, NULL, &rng), 0); *out = outBuf; + wc_ecc_free(&altCaKey); wc_FreeRsaKey(&caKey); wc_FreeRsaKey(&serverKey); wc_FreeRng(&rng); @@ -1784,12 +1786,117 @@ static int test_dual_alg_support(void) return EXPECT_RESULT(); } + +/* Handshake that signs the CertificateVerify with the alternative key. The + * server loads that key onto the session, so the session owns it and has to + * release it exactly once. */ +static int do_dual_alg_tls13_alt_sig_connection(byte *caCert, word32 caCertSz, + byte *serverCert, word32 serverCertSz, byte *serverKey, word32 serverKeySz, + byte *altKey, word32 altKeySz) +{ + 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; + byte cks[1]; + + cks[0] = WOLFSSL_CKS_SIGSPEC_ALTERNATIVE; + + 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); + + /* the session's own copy, nothing in the context points at it */ + ExpectIntEQ(wolfSSL_use_AltPrivateKey_buffer(ssl_s, altKey, (long)altKeySz, + WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseCKS(ssl_c, cks, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseCKS(ssl_s, cks, 1), WOLFSSL_SUCCESS); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); +/* FreeHandshakeResources releases the key buffers on the server unless one of + * these is defined, so only then is the post-handshake state there to read. */ +#if defined(WOLFSSL_INT_H) && (defined(OPENSSL_EXTRA) || \ + defined(WOLFSSL_WPAS_SMALL)) + ExpectNotNull(ssl_s); + if (ssl_s != NULL) { + /* the handshake really did take the alternative path */ + ExpectNotNull(ssl_s->sigSpec); + if (ssl_s->sigSpec != NULL) { + ExpectIntEQ(*ssl_s->sigSpec, WOLFSSL_CKS_SIGSPEC_ALTERNATIVE); + } + /* signing moved the alternative key into the primary slot, so the + * alternate fields must no longer claim it */ + ExpectNotNull(ssl_s->buffers.key); + ExpectNull(ssl_s->buffers.altKey); + ExpectIntEQ(ssl_s->buffers.weOwnAltKey, 0); + } +#endif + + wolfSSL_free(ssl_c); + /* the alternative key must be released once here */ + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +static int test_dual_alg_alt_sig_key_ownership(void) +{ + EXPECT_DECLS; + char keyFile[] = "./certs/ca-key.der"; + char sapkiFile[] = "./certs/ecc-keyPub.der"; + char altPrivFile[] = "./certs/ecc-key.der"; + byte *serverKey = NULL; + size_t serverKeySz = 0; + byte *altKey = NULL; + size_t altKeySz = 0; + byte *root = NULL; + int rootSz = 0; + byte *server = NULL; + int serverSz = 0; + + ExpectIntEQ(load_file(keyFile, &serverKey, &serverKeySz), 0); + ExpectIntEQ(load_file(altPrivFile, &altKey, &altKeySz), 0); + + if (EXPECT_SUCCESS()) { + rootSz = do_dual_alg_root_certgen(&root, keyFile, sapkiFile, + altPrivFile); + } + ExpectNotNull(root); + ExpectIntGT(rootSz, 0); + if (EXPECT_SUCCESS()) { + serverSz = do_dual_alg_server_certgen(&server, keyFile, sapkiFile, + altPrivFile, keyFile, root, rootSz); + } + ExpectNotNull(server); + ExpectIntGT(serverSz, 0); + + ExpectIntEQ(do_dual_alg_tls13_alt_sig_connection(root, (word32)rootSz, + server, (word32)serverSz, serverKey, (word32)serverKeySz, + altKey, (word32)altKeySz), TEST_SUCCESS); + + XFREE(root, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(server, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(serverKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(altKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + return EXPECT_RESULT(); +} #else static int test_dual_alg_support(void) { return TEST_SKIPPED; } +static int test_dual_alg_alt_sig_key_ownership(void) +{ + return TEST_SKIPPED; +} + static int test_dual_alg_crit_ext_support(void) { return TEST_SKIPPED; @@ -30761,11 +30868,172 @@ static int test_wolfSSL_set_SSL_CTX(void) #endif /* defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL) */ return EXPECT_RESULT(); } + +/* A session that loaded its own certificate and key must release them, and + * clear the ownership flags, when it starts using the new context's. */ +static int test_wolfSSL_set_SSL_CTX_own_cert(void) +{ + EXPECT_DECLS; +#if (defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && !defined(NO_RSA) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(NO_FILESYSTEM) && \ + !defined(NO_CERTS) + WOLFSSL_CTX *ctx1 = NULL; + WOLFSSL_CTX *ctx2 = NULL; + WOLFSSL *ssl = NULL; + + ExpectNotNull(ctx1 = wolfSSL_CTX_new(wolfTLS_server_method())); + ExpectTrue(wolfSSL_CTX_use_certificate_file(ctx1, svrCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectTrue(wolfSSL_CTX_use_PrivateKey_file(ctx1, svrKeyFile, + WOLFSSL_FILETYPE_PEM)); + + ExpectNotNull(ctx2 = wolfSSL_CTX_new(wolfTLS_server_method())); + ExpectTrue(wolfSSL_CTX_use_certificate_file(ctx2, svrCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectTrue(wolfSSL_CTX_use_PrivateKey_file(ctx2, svrKeyFile, + WOLFSSL_FILETYPE_PEM)); + + ExpectNotNull(ssl = wolfSSL_new(ctx1)); + + /* what an SNI callback may do before it switches context */ + ExpectIntEQ(wolfSSL_use_certificate_file(ssl, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); +#ifdef WOLFSSL_INT_H + ExpectIntEQ(ssl->buffers.weOwnCert, 1); + ExpectIntEQ(ssl->buffers.weOwnKey, 1); +#endif + + ExpectNotNull(wolfSSL_set_SSL_CTX(ssl, ctx2)); + +#ifdef WOLFSSL_INT_H +#ifdef WOLFSSL_COPY_CERT + ExpectIntEQ(ssl->buffers.weOwnCert, 1); + ExpectFalse(ssl->buffers.certificate == ctx2->certificate); +#else + ExpectIntEQ(ssl->buffers.weOwnCert, 0); + ExpectTrue(ssl->buffers.certificate == ctx2->certificate); +#endif +#if defined(WOLFSSL_COPY_KEY) || defined(WOLFSSL_BLIND_PRIVATE_KEY) + ExpectIntEQ(ssl->buffers.weOwnKey, 1); + ExpectFalse(ssl->buffers.key == ctx2->privateKey); +#else + ExpectIntEQ(ssl->buffers.weOwnKey, 0); + ExpectTrue(ssl->buffers.key == ctx2->privateKey); +#endif +#endif + + /* releasing the session must leave the contexts' buffers alone */ + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx1); + wolfSSL_CTX_free(ctx2); +#endif /* defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL) */ + return EXPECT_RESULT(); +} #endif /* defined(OPENSSL_ALL) || (defined(OPENSSL_EXTRA) && \ (defined(HAVE_STUNNEL) || defined(WOLFSSL_NGINX) || \ defined(HAVE_LIGHTY) || defined(WOLFSSL_HAPROXY) || \ defined(WOLFSSL_OPENSSH) || defined(HAVE_SBLIM_SFCB))) */ +/* The alternate private key the session copies from the context is the + * session's own and must be flagged so that it gets released. */ +static int test_wolfSSL_alt_key_ownership(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_DUAL_ALG_CERTS) && defined(WOLFSSL_INT_H) && \ + !defined(NO_RSA) && !defined(NO_WOLFSSL_SERVER) && \ + !defined(NO_FILESYSTEM) && !defined(NO_CERTS) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLS_server_method())); + ExpectTrue(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectTrue(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM)); + /* only the ownership of the buffer matters here, not the algorithm */ + ExpectTrue(wolfSSL_CTX_use_AltPrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM)); + + ExpectNotNull(ssl = wolfSSL_new(ctx)); +#ifdef WOLFSSL_BLIND_PRIVATE_KEY + ExpectIntEQ(ssl->buffers.weOwnAltKey, 1); + ExpectFalse(ssl->buffers.altKey == ctx->altPrivateKey); +#else + ExpectIntEQ(ssl->buffers.weOwnAltKey, 0); + ExpectTrue(ssl->buffers.altKey == ctx->altPrivateKey); +#endif + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif /* WOLFSSL_DUAL_ALG_CERTS && WOLFSSL_INT_H */ + return EXPECT_RESULT(); +} + +/* A session holding its own alternate key must release it, and stop claiming + * it, when it switches to another context. */ +static int test_wolfSSL_set_SSL_CTX_alt_key(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_DUAL_ALG_CERTS) && defined(WOLFSSL_INT_H) && \ + (defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && !defined(NO_RSA) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(NO_FILESYSTEM) && \ + !defined(NO_CERTS) + WOLFSSL_CTX* ctx1 = NULL; + WOLFSSL_CTX* ctx2 = NULL; + WOLFSSL* ssl = NULL; + byte* altKey = NULL; + size_t altKeySz = 0; + + ExpectIntEQ(load_file(svrKeyFile, &altKey, &altKeySz), 0); + + ExpectNotNull(ctx1 = wolfSSL_CTX_new(wolfTLS_server_method())); + ExpectTrue(wolfSSL_CTX_use_certificate_file(ctx1, svrCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectTrue(wolfSSL_CTX_use_PrivateKey_file(ctx1, svrKeyFile, + WOLFSSL_FILETYPE_PEM)); + /* only the ownership of the buffer matters here, not the algorithm */ + ExpectTrue(wolfSSL_CTX_use_AltPrivateKey_file(ctx1, svrKeyFile, + WOLFSSL_FILETYPE_PEM)); + + ExpectNotNull(ctx2 = wolfSSL_CTX_new(wolfTLS_server_method())); + ExpectTrue(wolfSSL_CTX_use_certificate_file(ctx2, svrCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectTrue(wolfSSL_CTX_use_PrivateKey_file(ctx2, svrKeyFile, + WOLFSSL_FILETYPE_PEM)); + ExpectTrue(wolfSSL_CTX_use_AltPrivateKey_file(ctx2, svrKeyFile, + WOLFSSL_FILETYPE_PEM)); + + ExpectNotNull(ssl = wolfSSL_new(ctx1)); + + /* the session takes an alternate key of its own */ + ExpectIntEQ(wolfSSL_use_AltPrivateKey_buffer(ssl, altKey, (long)altKeySz, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(ssl->buffers.weOwnAltKey, 1); + ExpectFalse(ssl->buffers.altKey == ctx1->altPrivateKey); + + ExpectNotNull(wolfSSL_set_SSL_CTX(ssl, ctx2)); + +#ifdef WOLFSSL_BLIND_PRIVATE_KEY + /* released and replaced with this object's copy of the new context's */ + ExpectIntEQ(ssl->buffers.weOwnAltKey, 1); + ExpectNotNull(ssl->buffers.altKey); + ExpectFalse(ssl->buffers.altKey == ctx2->altPrivateKey); +#else + /* released, and the new context's buffer is used in place */ + ExpectIntEQ(ssl->buffers.weOwnAltKey, 0); + ExpectTrue(ssl->buffers.altKey == ctx2->altPrivateKey); +#endif + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx1); + wolfSSL_CTX_free(ctx2); + XFREE(altKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif /* WOLFSSL_DUAL_ALG_CERTS && WOLFSSL_INT_H && OPENSSL_EXTRA */ + return EXPECT_RESULT(); +} + static int test_wolfSSL_security_level(void) { EXPECT_DECLS; @@ -39973,6 +40241,7 @@ TEST_CASE testCases[] = { TEST_X509_DECLS, TEST_DECL(test_dual_alg_support), + TEST_DECL(test_dual_alg_alt_sig_key_ownership), TEST_DECL(test_dual_alg_crit_ext_support), TEST_DECL(test_dual_alg_ecdsa_mldsa), @@ -40289,7 +40558,10 @@ TEST_CASE testCases[] = { defined(HAVE_LIGHTY) || defined(WOLFSSL_HAPROXY) || \ defined(WOLFSSL_OPENSSH) || defined(HAVE_SBLIM_SFCB))) TEST_DECL(test_wolfSSL_set_SSL_CTX), + TEST_DECL(test_wolfSSL_set_SSL_CTX_own_cert), #endif + TEST_DECL(test_wolfSSL_alt_key_ownership), + TEST_DECL(test_wolfSSL_set_SSL_CTX_alt_key), TEST_DECL(test_wolfSSL_CTX_get_min_proto_version), TEST_DECL(test_wolfSSL_security_level), TEST_DECL(test_wolfSSL_crypto_policy),