Skip to content

Commit 0c47b38

Browse files
committed
Fixes from review
1 parent 91076a0 commit 0c47b38

6 files changed

Lines changed: 566 additions & 36 deletions

File tree

src/internal.c

Lines changed: 75 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
1599516032
int 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 */

src/ssl_api_cert.c

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,82 @@ int wolfSSL_get_negotiated_server_cert_type(WOLFSSL* ssl, int* tp)
430430
}
431431
return ret;
432432
}
433+
434+
#ifndef NO_SHA256
435+
/* Store the SHA-256 digest of a DER SubjectPublicKeyInfo as an expected Raw
436+
* Public Key (RFC 7250) for out-of-band trust.
437+
*
438+
* @param [in] cfg RPK configuration to add the pin to.
439+
* @param [in] spki DER-encoded SubjectPublicKeyInfo.
440+
* @param [in] spkiSz Length of spki in bytes.
441+
* @return 0 on success.
442+
* @return BAD_FUNC_ARG when cfg or spki is NULL, or spkiSz is 0.
443+
* @return BUFFER_E when no more pins can be stored.
444+
* @return negative hashing error on failure.
445+
*/
446+
static int rpk_add_expected(RpkConfig* cfg, const unsigned char* spki,
447+
unsigned int spkiSz)
448+
{
449+
int ret;
450+
451+
if ((cfg == NULL) || (spki == NULL) || (spkiSz == 0)) {
452+
return BAD_FUNC_ARG;
453+
}
454+
if (cfg->expectedRpkCnt >= WOLFSSL_MAX_RPK_PINS) {
455+
return BUFFER_E;
456+
}
457+
458+
ret = wc_Sha256Hash(spki, spkiSz, cfg->expectedRpk[cfg->expectedRpkCnt]);
459+
if (ret == 0) {
460+
cfg->expectedRpkCnt++;
461+
}
462+
return ret;
463+
}
464+
465+
/* Pin an expected peer Raw Public Key on the SSL/TLS CTX object.
466+
*
467+
* @param [in] ctx SSL/TLS CTX object.
468+
* @param [in] spki DER-encoded SubjectPublicKeyInfo the peer will present.
469+
* @param [in] spkiSz Length of spki in bytes.
470+
* @return WOLFSSL_SUCCESS on success.
471+
* @return BAD_FUNC_ARG when ctx or spki is NULL, or spkiSz is 0.
472+
* @return BUFFER_E when the pin table is full (WOLFSSL_MAX_RPK_PINS reached).
473+
* @return Other negative error code on hashing failure.
474+
*/
475+
int wolfSSL_CTX_set_expected_rpk(WOLFSSL_CTX* ctx, const unsigned char* spki,
476+
unsigned int spkiSz)
477+
{
478+
int ret;
479+
480+
if (ctx == NULL) {
481+
return BAD_FUNC_ARG;
482+
}
483+
ret = rpk_add_expected(&ctx->rpkConfig, spki, spkiSz);
484+
return (ret == 0) ? WOLFSSL_SUCCESS : ret;
485+
}
486+
487+
/* Pin an expected peer Raw Public Key on the SSL/TLS object.
488+
*
489+
* @param [in] ssl SSL/TLS object.
490+
* @param [in] spki DER-encoded SubjectPublicKeyInfo the peer will present.
491+
* @param [in] spkiSz Length of spki in bytes.
492+
* @return WOLFSSL_SUCCESS on success.
493+
* @return BAD_FUNC_ARG when ssl or spki is NULL, or spkiSz is 0.
494+
* @return BUFFER_E when the pin table is full (WOLFSSL_MAX_RPK_PINS reached).
495+
* @return Other negative error code on hashing failure.
496+
*/
497+
int wolfSSL_set_expected_rpk(WOLFSSL* ssl, const unsigned char* spki,
498+
unsigned int spkiSz)
499+
{
500+
int ret;
501+
502+
if (ssl == NULL) {
503+
return BAD_FUNC_ARG;
504+
}
505+
ret = rpk_add_expected(&ssl->options.rpkConfig, spki, spkiSz);
506+
return (ret == 0) ? WOLFSSL_SUCCESS : ret;
507+
}
508+
#endif /* !NO_SHA256 */
433509
#endif /* HAVE_RPK */
434510

435511
/* Certificate verification options. */

0 commit comments

Comments
 (0)