@@ -15992,6 +15992,43 @@ static int AdjustCMForParams(WOLFSSL* ssl)
1599215992}
1599315993#endif
1599415994
15995+ #if defined(HAVE_RPK)
15996+ /* Determine whether a received Raw Public Key (RFC 7250) has been pinned as
15997+ * trusted out of band. The on-wire RPK is exactly the DER SubjectPublicKeyInfo,
15998+ * so its SHA-256 digest is compared against the application-supplied pins.
15999+ *
16000+ * @param [in] ssl SSL/TLS object.
16001+ * @param [in] spki DER SubjectPublicKeyInfo received from the peer.
16002+ * @param [in] spkiSz Length of spki in bytes.
16003+ * @return 1 when the key matches a pin, 0 otherwise.
16004+ */
16005+ static int RpkIsTrusted(WOLFSSL* ssl, const byte* spki, word32 spkiSz)
16006+ {
16007+ #ifndef NO_SHA256
16008+ RpkConfig* cfg = &ssl->options.rpkConfig;
16009+
16010+ if ((cfg->expectedRpkCnt > 0) && (spki != NULL) && (spkiSz > 0)) {
16011+ byte digest[WC_SHA256_DIGEST_SIZE];
16012+ int i;
16013+
16014+ if (wc_Sha256Hash(spki, spkiSz, digest) == 0) {
16015+ for (i = 0; i < (int)cfg->expectedRpkCnt; i++) {
16016+ if (XMEMCMP(digest, cfg->expectedRpk[i],
16017+ WC_SHA256_DIGEST_SIZE) == 0) {
16018+ return 1;
16019+ }
16020+ }
16021+ }
16022+ }
16023+ #else
16024+ (void)ssl;
16025+ (void)spki;
16026+ (void)spkiSz;
16027+ #endif /* !NO_SHA256 */
16028+ return 0;
16029+ }
16030+ #endif /* HAVE_RPK */
16031+
1599516032int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
1599616033 word32 totalSz)
1599716034{
@@ -16815,34 +16852,52 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
1681516852 ssl->peerVerifyRet = WOLFSSL_X509_V_OK;
1681616853 #endif
1681716854
16818- #if defined(HAVE_RPK) && (defined(OPENSSL_EXTRA) || \
16819- defined(OPENSSL_EXTRA_X509_SMALL))
16855+ #if defined(HAVE_RPK)
1682016856 /* A Raw Public Key cert (RFC 7250) has no issuer and no
1682116857 * signature, so ParseCertRelative performed no peer
16822- * authentication. Unless an out-of-band trust mechanism
16823- * (DANE, key pinning, etc.) has bound this key, report the
16824- * peer as unauthenticated through wolfSSL_get_verify_result()
16825- * rather than leaving it at WOLFSSL_X509_V_OK. The handshake
16826- * is intentionally not failed here: per RFC 7250 the
16827- * application is responsible for validating the key out of
16828- * band. Applies to both peers - client checking the
16829- * server's RPK and server checking the client's RPK.
16830- * WOLFSSL_VERIFY_NONE leaves the result untouched. */
16831- if (args->dCert->isRPK && !ssl->options.verifyNone) {
16832- int rpkTrusted = 0;
16833- #if defined(HAVE_DANE)
16834- if (ssl->options.useDANE) {
16835- /* DANE authentication should be added; set
16836- * rpkTrusted = 1 on a successful match. */
16837- }
16838- #endif /* HAVE_DANE */
16858+ * authentication. The key is only authenticated if it was
16859+ * pinned out of band (expected RPK). Applies to both peers
16860+ * - client checking the server's RPK and server checking
16861+ * the client's RPK. */
16862+ if (args->dCert->isRPK) {
16863+ int rpkTrusted = RpkIsTrusted(ssl,
16864+ args->certs[args->certIdx].buffer,
16865+ args->certs[args->certIdx].length);
16866+
16867+ #if defined(OPENSSL_EXTRA) || \
16868+ defined(OPENSSL_EXTRA_X509_SMALL)
16869+ /* Report the status truthfully regardless of verify
16870+ * mode, mirroring OpenSSL: get_verify_result() reflects
16871+ * the actual key status even under WOLFSSL_VERIFY_NONE
16872+ * rather than leaving it at WOLFSSL_X509_V_OK. */
1683916873 if (!rpkTrusted &&
1684016874 ssl->peerVerifyRet == WOLFSSL_X509_V_OK) {
1684116875 ssl->peerVerifyRet = (unsigned long)
1684216876 WOLFSSL_X509_V_ERR_RPK_UNTRUSTED;
1684316877 }
16878+ #endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */
16879+
16880+ /* Fail closed when the peer is being authenticated: an
16881+ * untrusted RPK must abort the handshake rather than
16882+ * silently complete it (cf. OpenSSL CVE-2024-12797).
16883+ * The gate is !verifyNone, matching the X.509
16884+ * authentication gate above (the VERIFY/NO_VERIFY
16885+ * choice passed to ProcessPeerCertParse) - so a default
16886+ * authenticating client (verifyPeer unset, verifyNone
16887+ * unset) validating an RPK server is protected too, not
16888+ * just the explicit WOLFSSL_VERIFY_PEER case. The verify
16889+ * callback can still override, exactly as it can for an
16890+ * X.509 chain error. WOLFSSL_VERIFY_NONE keeps the
16891+ * "accept and let the application decide" behaviour per
16892+ * RFC 7250. */
16893+ if (!rpkTrusted && !ssl->options.verifyNone) {
16894+ WOLFSSL_MSG("Untrusted RPK and peer verification "
16895+ "is on; failing handshake");
16896+ ret = ASN_NO_SIGNER_E;
16897+ WOLFSSL_ERROR_VERBOSE(ret);
16898+ }
1684416899 }
16845- #endif /* HAVE_RPK && (OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL) */
16900+ #endif /* HAVE_RPK */
1684616901
1684716902 #if defined(SESSION_CERTS) && defined(WOLFSSL_ALT_CERT_CHAINS)
1684816903 /* if using alternate chain, store the cert used */
0 commit comments