diff --git a/src/internal.c b/src/internal.c index 18c542e6540..a3d540bc445 100644 --- a/src/internal.c +++ b/src/internal.c @@ -16823,6 +16823,36 @@ static int GetPeerCertType(const WOLFSSL* ssl) } #endif /* HAVE_RPK */ +#ifdef WOLFSSL_TRUST_PEER_CERT +/* Helper function to check peer certificate dates since the standard + * checks and parsing are skipped for trusted peer certificates. */ +static int CheckTrustedPeerDates(DecodedCert* cert) +{ + if (cert == NULL) + return BAD_FUNC_ARG; + +#ifndef NO_ASN_TIME + if (wc_AsnGetSkipDateCheck()) + return 0; + + if ((cert->beforeDate != NULL) && (cert->beforeDateLen > 2) && + (wc_ValidateDate(&cert->beforeDate[2], cert->beforeDate[0], + ASN_BEFORE, cert->beforeDate[1]) == 0)) { + WOLFSSL_MSG("Trusted peer cert is not yet valid"); + return ASN_BEFORE_DATE_E; + } + + if ((cert->afterDate != NULL) && (cert->afterDateLen > 2) && + (wc_ValidateDate(&cert->afterDate[2], cert->afterDate[0], + ASN_AFTER, cert->afterDate[1]) == 0)) { + WOLFSSL_MSG("Trusted peer cert has expired"); + return ASN_AFTER_DATE_E; + } +#endif + return 0; +} +#endif /* WOLFSSL_TRUST_PEER_CERT */ + static int ProcessPeerCertParse(WOLFSSL* ssl, ProcPeerCertArgs* args, int certType, int verify, byte** pSubjectHash, int* pAlreadySigner) { @@ -18495,6 +18525,27 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, if (tp) { WOLFSSL_MSG("Found matching trusted peer cert"); + ret = CheckTrustedPeerDates(args->dCert); + if (ret != 0) { + #if defined(OPENSSL_EXTRA) || \ + defined(OPENSSL_EXTRA_X509_SMALL) + /* report as the chain path would have */ + if (ssl->peerVerifyRet == 0) { + if (ret == + WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E)) { + ssl->peerVerifyRet = (unsigned long) + WOLFSSL_X509_V_ERR_CERT_NOT_YET_VALID; + } + else if (ret == + WC_NO_ERR_TRACE(ASN_AFTER_DATE_E)) { + ssl->peerVerifyRet = (unsigned long) + WOLFSSL_X509_V_ERR_CERT_HAS_EXPIRED; + } + } + #endif + WOLFSSL_ERROR_VERBOSE(ret); + goto exit_ppc; + } args->haveTrustPeer = 1; } else { diff --git a/tests/api/test_certman.c b/tests/api/test_certman.c index 266f995a0bb..4efd703156a 100644 --- a/tests/api/test_certman.c +++ b/tests/api/test_certman.c @@ -4203,3 +4203,57 @@ int test_wolfSSL_CertManagerNameConstraint_skid_disambiguates(void) #endif return EXPECT_RESULT(); } + +/* A certificate loaded as a trusted peer must still be subject to its own + * validity period. The trusted peer match skips chain processing, so the + * date check that the chain would have applied has to be made on that path + * as well; without it an expired pinned certificate is accepted. */ +int test_wolfSSL_trust_peer_cert_expired(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_TRUST_PEER_CERT) && !defined(NO_ASN_TIME) && \ + !defined(NO_RSA) && !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) && \ + defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) + test_ssl_cbf client_cb; + test_ssl_cbf server_cb; + int ret; + + XMEMSET(&client_cb, 0, sizeof(client_cb)); + XMEMSET(&server_cb, 0, sizeof(server_cb)); + + /* the server presents a certificate that expired in 2018 */ + server_cb.certPemFile = "certs/test/expired/expired-cert.pem"; + server_cb.keyPemFile = "certs/server-key.pem"; + + client_cb.ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()); + ExpectNotNull(client_cb.ctx); + client_cb.isSharedCtx = 1; + + /* The client pins that same expired certificate and loads no CA, so a + * trusted peer match is the only way the handshake could succeed. + * + * Whether the certificate can be loaded at all depends on the build: + * where WOLFSSL_LOAD_VERIFY_DEFAULT_FLAGS carries + * WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY the store takes it and the date has + * to be applied during the handshake, which is what this covers. + * Elsewhere the load itself refuses it, which is an equally good + * refusal. Either way the expired certificate must not authenticate a + * peer. */ + ret = wolfSSL_CTX_trust_peer_cert(client_cb.ctx, + "certs/test/expired/expired-cert.pem", WOLFSSL_FILETYPE_PEM); + if (ret == WOLFSSL_SUCCESS) { + wolfSSL_CTX_set_verify(client_cb.ctx, WOLFSSL_VERIFY_PEER, NULL); + + /* the pin must not revive an expired certificate */ + ExpectIntNE(test_wolfSSL_client_server_nofail_memio(&client_cb, + &server_cb, NULL), TEST_SUCCESS); + ExpectIntEQ(client_cb.last_err, WC_NO_ERR_TRACE(ASN_AFTER_DATE_E)); + } + else { + ExpectIntEQ(ret, WC_NO_ERR_TRACE(ASN_AFTER_DATE_E)); + } + + wolfSSL_CTX_free(client_cb.ctx); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_certman.h b/tests/api/test_certman.h index d46c4bd71e3..bb85108e90b 100644 --- a/tests/api/test_certman.h +++ b/tests/api/test_certman.h @@ -25,6 +25,7 @@ #include int test_wolfSSL_CertManagerAPI(void); +int test_wolfSSL_trust_peer_cert_expired(void); int test_wolfSSL_CertManagerLoadCABuffer(void); int test_wolfSSL_CertManagerLoadCABuffer_ex(void); int test_wolfSSL_CertManagerLoadCABufferType(void); @@ -107,6 +108,7 @@ int test_wolfSSL_CertManagerNameConstraint_skid_disambiguates(void); TEST_DECL_GROUP("certman", \ test_wolfSSL_CertManagerNameConstraint_valid_chain), \ TEST_DECL_GROUP("certman", \ - test_wolfSSL_CertManagerNameConstraint_skid_disambiguates) + test_wolfSSL_CertManagerNameConstraint_skid_disambiguates), \ + TEST_DECL_GROUP("certman", test_wolfSSL_trust_peer_cert_expired) #endif /* WOLFCRYPT_TEST_CERTMAN_H */