[PATCH 09/10] crypto/openssl: add RSA-PSS support for RSA operations
Sucharitha Sarananaga
ssarananaga at marvell.com
Mon Aug 31 12:23:27 CEST 2026
Add RSA-PSS padding support to the OpenSSL crypto PMD.
Store PSS-specific parameters (hash, MGF1 hash, and salt length)
in the RSA session, advertise PSS capability, and configure the
OpenSSL EVP context accordingly for sign and verify operations.
Introduce a dedicated RSA-PSS verification path using
EVP_PKEY_verify(), while retaining verify-recover for supported
deterministic padding schemes. Reject unsupported RSA-PSS usage
for non-sign/verify operations.
Signed-off-by: Sucharitha Sarananaga <ssarananaga at marvell.com>
---
drivers/crypto/openssl/openssl_pmd_private.h | 6 +
drivers/crypto/openssl/rte_openssl_pmd.c | 314 +++++++++++++++----
drivers/crypto/openssl/rte_openssl_pmd_ops.c | 27 +-
3 files changed, 292 insertions(+), 55 deletions(-)
diff --git a/drivers/crypto/openssl/openssl_pmd_private.h b/drivers/crypto/openssl/openssl_pmd_private.h
index 8704e1915a..8a6db6066f 100644
--- a/drivers/crypto/openssl/openssl_pmd_private.h
+++ b/drivers/crypto/openssl/openssl_pmd_private.h
@@ -5,6 +5,8 @@
#ifndef _OPENSSL_PMD_PRIVATE_H_
#define _OPENSSL_PMD_PRIVATE_H_
+#include <rte_common.h>
+
#include <openssl/evp.h>
#include <openssl/cmac.h>
#include <openssl/hmac.h>
@@ -186,6 +188,10 @@ struct __rte_cache_aligned openssl_asym_session {
uint8_t *label;
uint32_t label_len;
+
+ const EVP_MD *pss_md;
+ const EVP_MD *pss_mgf1_md;
+ int pss_saltlen;
} r;
struct exp {
BIGNUM *exp;
diff --git a/drivers/crypto/openssl/rte_openssl_pmd.c b/drivers/crypto/openssl/rte_openssl_pmd.c
index 4fbbb73bfa..1b33470c8f 100644
--- a/drivers/crypto/openssl/rte_openssl_pmd.c
+++ b/drivers/crypto/openssl/rte_openssl_pmd.c
@@ -2327,6 +2327,216 @@ openssl_rsa_set_oaep_params(EVP_PKEY_CTX *ctx,
return 0;
}
+/**
+ * Configure RSA-PSS padding parameters, including the signature digest,
+ * on an initialized EVP_PKEY_CTX. Must be called after
+ * EVP_PKEY_CTX_set_rsa_padding().
+ *
+ * @return 0 on success, -1 on failure.
+ */
+static int
+openssl_rsa_set_pss_params(EVP_PKEY_CTX *ctx,
+ const struct openssl_asym_session *sess)
+{
+ /*
+ * Tells OpenSSL which hash algorithm was used to create the
+ * input message digest (rte_crypto_rsa_padding::hash), so it
+ * knows the expected digest length and can embed the correct
+ * algorithm identifier while PSS-encoding it. This does not
+ * cause the digest to be (re-)computed here: EVP_PKEY_sign()/
+ * EVP_PKEY_verify() operate on the digest bytes as-is.
+ */
+ if (EVP_PKEY_CTX_set_signature_md(ctx, sess->u.r.pss_md) <= 0)
+ return -1;
+
+ if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, sess->u.r.pss_mgf1_md) <= 0)
+ return -1;
+
+ /* pss_saltlen is a literal byte count (0 is valid: no salt) */
+ if (EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, sess->u.r.pss_saltlen) <= 0)
+ return -1;
+
+ return 0;
+}
+
+/**
+ * Sign a message using RSA-PSS. Per rte_crypto_rsa_op_param::message and
+ * rte_crypto_rsa_padding::hash, the input is a digest already hashed by
+ * the caller with the configured algorithm, not the raw message, so
+ * EVP_PKEY_sign() is used directly on it (no internal re-hashing). This
+ * matches the pattern used for PKCS#1 v1.5/unpadded RSA signing in
+ * process_openssl_rsa_op_evp().
+ *
+ * The OpenSSL PMD does not advertise rte_crypto_rsa_capa::pss_explicit_salt,
+ * so an application-supplied rte_crypto_rsa_op_param::pss_salt is rejected
+ * by the caller before this function is invoked; the salt is always
+ * generated internally by OpenSSL's RNG via EVP_PKEY_sign().
+ *
+ * @return 0 on success, -1 on failure.
+ */
+static int
+openssl_rsa_pss_sign(uint32_t pad, const struct openssl_asym_session *sess,
+ struct rte_crypto_asym_op *op)
+{
+ EVP_PKEY_CTX *ctx = sess->u.r.ctx;
+ size_t outlen = 0;
+
+ if (EVP_PKEY_sign_init(ctx) <= 0)
+ return -1;
+
+ if (EVP_PKEY_CTX_set_rsa_padding(ctx, pad) <= 0)
+ return -1;
+
+ if (openssl_rsa_set_pss_params(ctx, sess) < 0)
+ return -1;
+
+ if (EVP_PKEY_sign(ctx, NULL, &outlen,
+ op->rsa.message.data, op->rsa.message.length) <= 0)
+ return -1;
+
+ if (outlen == 0 || outlen > op->rsa.sign.length)
+ return -1;
+
+ outlen = op->rsa.sign.length;
+ if (EVP_PKEY_sign(ctx, op->rsa.sign.data, &outlen,
+ op->rsa.message.data, op->rsa.message.length) <= 0)
+ return -1;
+
+ op->rsa.sign.length = outlen;
+ return 0;
+}
+
+/**
+ * Verify an RSA-PSS signature against a pre-computed message digest.
+ * Per rte_crypto_rsa_op_param::message and rte_crypto_rsa_padding::hash,
+ * the input is already a digest, so EVP_PKEY_verify() is used directly
+ * on it (no internal re-hashing). PSS does not support verify-recover,
+ * so this also gives a direct pass/fail result.
+ *
+ * A signature mismatch (including one caused by OpenSSL rejecting a
+ * malformed signature outright, e.g. wrong size) is a normal outcome,
+ * not a processing error, so it must not fail the enqueue operation.
+ *
+ * @return 0 if the signature is valid, 1 if invalid/mismatched,
+ * -1 on a setup/processing failure unrelated to the signature.
+ */
+static int
+openssl_rsa_pss_verify(uint32_t pad, const struct openssl_asym_session *sess,
+ struct rte_crypto_asym_op *op)
+{
+ EVP_PKEY_CTX *ctx = sess->u.r.ctx;
+ int ret;
+
+ if (EVP_PKEY_verify_init(ctx) <= 0)
+ return -1;
+
+ if (EVP_PKEY_CTX_set_rsa_padding(ctx, pad) <= 0)
+ return -1;
+
+ if (openssl_rsa_set_pss_params(ctx, sess) < 0)
+ return -1;
+
+ /*
+ * EVP_PKEY_verify() returns 1 for a valid signature, 0 for an
+ * invalid one, and a negative value only for setup/library errors
+ * (see EVP_PKEY_verify(3)); a malformed signature is reported via
+ * a 0 return here too, not a negative one.
+ */
+ ret = EVP_PKEY_verify(ctx,
+ op->rsa.sign.data, op->rsa.sign.length,
+ op->rsa.message.data, op->rsa.message.length);
+ if (ret < 0)
+ return -1;
+
+ if (ret == 0) {
+ OPENSSL_LOG(DEBUG, "RSA-PSS signature verification failed");
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Verify an RSA signature using verify-recover, for deterministic
+ * padding schemes (PKCS#1 v1.5, no padding). Not applicable to PSS,
+ * since OpenSSL does not support recover-mode verification for PSS
+ * (RSA-PSS is a probabilistic scheme and cannot be undone to recover
+ * the original digest).
+ *
+ * A signature mismatch is a normal outcome, not a processing error, so
+ * it must not fail the enqueue operation. Note that EVP_PKEY_verify_recover()
+ * itself can return <= 0 for a mismatch too, e.g. when the signature does not
+ * decode to a validly padded value (OpenSSL then reports it as a hard
+ * "data too large for modulus"/padding error rather than a soft 0 return),
+ * so that case is treated the same as a successful-but-mismatching recover.
+ *
+ * @return 0 if the signature is valid, 1 if invalid/mismatched,
+ * -1 on a setup/processing failure unrelated to the signature.
+ */
+static int
+openssl_rsa_verify_recover(EVP_PKEY_CTX *ctx, uint32_t pad,
+ struct rte_crypto_asym_op *op)
+{
+ uint8_t *tmp;
+ size_t outlen = 0;
+ int ret;
+
+ if (EVP_PKEY_verify_recover_init(ctx) <= 0)
+ return -1;
+
+ if (EVP_PKEY_CTX_set_rsa_padding(ctx, pad) <= 0)
+ return -1;
+
+ if (EVP_PKEY_verify_recover(ctx, NULL, &outlen,
+ op->rsa.sign.data,
+ op->rsa.sign.length) <= 0) {
+ OPENSSL_LOG(ERR, "RSA sign Verification failed");
+ return 1;
+ }
+
+ if ((outlen <= 0) || (outlen != op->rsa.sign.length)) {
+ OPENSSL_LOG(ERR, "RSA sign Verification failed");
+ return 1;
+ }
+
+ tmp = OPENSSL_malloc(outlen);
+ if (tmp == NULL) {
+ OPENSSL_LOG(ERR, "Memory allocation failed");
+ return -1;
+ }
+
+ ret = EVP_PKEY_verify_recover(ctx, tmp, &outlen,
+ op->rsa.sign.data,
+ op->rsa.sign.length);
+ if (ret <= 0) {
+ /*
+ * A malformed/corrupted signature can make the underlying
+ * RSA op itself fail (e.g. invalid padding), rather than
+ * just returning a recovered value that fails to compare.
+ * Both cases mean verification failed, not that processing
+ * broke, so still let the op complete successfully.
+ */
+ OPENSSL_free(tmp);
+ OPENSSL_LOG(ERR, "RSA sign Verification failed");
+ return 1;
+ }
+
+ OPENSSL_LOG(DEBUG,
+ "Length of public_decrypt %zu "
+ "length of message %zd",
+ outlen, op->rsa.message.length);
+ if (outlen != op->rsa.message.length ||
+ CRYPTO_memcmp(tmp, op->rsa.message.data,
+ op->rsa.message.length) != 0) {
+ OPENSSL_free(tmp);
+ OPENSSL_LOG(ERR, "RSA sign Verification failed");
+ return 1;
+ }
+ OPENSSL_free(tmp);
+
+ return 0;
+}
+
/* process rsa operations */
static int
process_openssl_rsa_op_evp(struct rte_crypto_op *cop,
@@ -2334,7 +2544,6 @@ process_openssl_rsa_op_evp(struct rte_crypto_op *cop,
{
struct rte_crypto_asym_op *op = cop->asym;
uint32_t pad = sess->u.r.pad;
- uint8_t *tmp;
size_t outlen = 0;
int ret = -1;
@@ -2352,6 +2561,15 @@ process_openssl_rsa_op_evp(struct rte_crypto_op *cop,
return ret;
}
+ /* PSS is only valid for sign/verify */
+ if (sess->u.r.pad == RTE_CRYPTO_RSA_PADDING_PSS &&
+ op->rsa.op_type != RTE_CRYPTO_ASYM_OP_SIGN &&
+ op->rsa.op_type != RTE_CRYPTO_ASYM_OP_VERIFY) {
+ OPENSSL_LOG(ERR, "PSS supports sign/verify only");
+ cop->status = RTE_CRYPTO_OP_STATUS_INVALID_ARGS;
+ return ret;
+ }
+
switch (pad) {
case RTE_CRYPTO_RSA_PADDING_PKCS1_5:
pad = RSA_PKCS1_PADDING;
@@ -2362,6 +2580,9 @@ process_openssl_rsa_op_evp(struct rte_crypto_op *cop,
case RTE_CRYPTO_RSA_PADDING_OAEP:
pad = RSA_PKCS1_OAEP_PADDING;
break;
+ case RTE_CRYPTO_RSA_PADDING_PSS:
+ pad = RSA_PKCS1_PSS_PADDING;
+ break;
default:
cop->status = RTE_CRYPTO_OP_STATUS_INVALID_ARGS;
OPENSSL_LOG(ERR,
@@ -2426,70 +2647,55 @@ process_openssl_rsa_op_evp(struct rte_crypto_op *cop,
break;
case RTE_CRYPTO_ASYM_OP_SIGN:
- if (EVP_PKEY_sign_init(rsa_ctx) <= 0)
- goto err_rsa;
+ if (sess->u.r.pad == RTE_CRYPTO_RSA_PADDING_PSS) {
+ if (op->rsa.pss_salt.data != NULL) {
+ OPENSSL_LOG(ERR, "Explicit RSA-PSS salt is not supported");
+ cop->status = RTE_CRYPTO_OP_STATUS_INVALID_ARGS;
+ return ret;
+ }
+ if (openssl_rsa_pss_sign(pad, sess, op) < 0)
+ goto err_rsa;
+ } else {
+ if (EVP_PKEY_sign_init(rsa_ctx) <= 0)
+ goto err_rsa;
- if (EVP_PKEY_CTX_set_rsa_padding(rsa_ctx, pad) <= 0)
- goto err_rsa;
+ if (EVP_PKEY_CTX_set_rsa_padding(rsa_ctx, pad) <= 0)
+ goto err_rsa;
- if (EVP_PKEY_sign(rsa_ctx, NULL, &outlen,
- op->rsa.message.data,
- op->rsa.message.length) <= 0)
- goto err_rsa;
+ if (EVP_PKEY_sign(rsa_ctx, NULL, &outlen,
+ op->rsa.message.data,
+ op->rsa.message.length) <= 0)
+ goto err_rsa;
- if (outlen <= 0)
- goto err_rsa;
+ if (outlen <= 0)
+ goto err_rsa;
- if (EVP_PKEY_sign(rsa_ctx, op->rsa.sign.data, &outlen,
- op->rsa.message.data,
- op->rsa.message.length) <= 0)
- goto err_rsa;
- op->rsa.sign.length = outlen;
+ if (EVP_PKEY_sign(rsa_ctx, op->rsa.sign.data, &outlen,
+ op->rsa.message.data,
+ op->rsa.message.length) <= 0)
+ goto err_rsa;
+ op->rsa.sign.length = outlen;
+ }
break;
case RTE_CRYPTO_ASYM_OP_VERIFY:
- if (EVP_PKEY_verify_recover_init(rsa_ctx) <= 0)
- goto err_rsa;
-
- if (EVP_PKEY_CTX_set_rsa_padding(rsa_ctx, pad) <= 0)
- goto err_rsa;
+ if (sess->u.r.pad == RTE_CRYPTO_RSA_PADDING_PSS)
+ ret = openssl_rsa_pss_verify(pad, sess, op);
+ else
+ ret = openssl_rsa_verify_recover(rsa_ctx, pad, op);
- if (EVP_PKEY_verify_recover(rsa_ctx, NULL, &outlen,
- op->rsa.sign.data,
- op->rsa.sign.length) <= 0)
+ if (ret < 0)
goto err_rsa;
- if ((outlen <= 0) || (outlen != op->rsa.sign.length))
- goto err_rsa;
-
- tmp = OPENSSL_malloc(outlen);
- if (tmp == NULL) {
- OPENSSL_LOG(ERR, "Memory allocation failed");
- goto err_rsa;
- }
-
- ret = EVP_PKEY_verify_recover(rsa_ctx, tmp, &outlen,
- op->rsa.sign.data,
- op->rsa.sign.length);
- if (ret <= 0) {
- /* OpenSSL RSA verification returns one on
- * successful verification, otherwise 0. Hence,
- * this enqueue operation should succeed even if
- * invalid signature has been requested in verify.
- */
- OPENSSL_free(tmp);
- goto err_rsa;
- }
-
- OPENSSL_LOG(DEBUG,
- "Length of public_decrypt %zu "
- "length of message %zd",
- outlen, op->rsa.message.length);
- if (CRYPTO_memcmp(tmp, op->rsa.message.data,
- op->rsa.message.length)) {
- OPENSSL_LOG(ERR, "RSA sign Verification failed");
+ /*
+ * ret == 1 means the signature did not verify; that is a
+ * normal outcome, so the op still completes (with an error
+ * status) instead of failing the enqueue itself.
+ */
+ if (ret > 0) {
+ cop->status = RTE_CRYPTO_OP_STATUS_ERROR;
+ return 0;
}
- OPENSSL_free(tmp);
break;
default:
diff --git a/drivers/crypto/openssl/rte_openssl_pmd_ops.c b/drivers/crypto/openssl/rte_openssl_pmd_ops.c
index 2504cfb9f5..efc11339be 100644
--- a/drivers/crypto/openssl/rte_openssl_pmd_ops.c
+++ b/drivers/crypto/openssl/rte_openssl_pmd_ops.c
@@ -741,9 +741,11 @@ static const struct rte_cryptodev_capabilities openssl_pmd_capabilities[] = {
.increment = 1
},
#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
+ /* pss_explicit_salt not supported, defaults to false */
.pad_types = ((1 << RTE_CRYPTO_RSA_PADDING_NONE) |
(1 << RTE_CRYPTO_RSA_PADDING_PKCS1_5) |
- (1 << RTE_CRYPTO_RSA_PADDING_OAEP)),
+ (1 << RTE_CRYPTO_RSA_PADDING_OAEP) |
+ (1 << RTE_CRYPTO_RSA_PADDING_PSS)),
.mgf1_hash_algos = (RTE_BIT64(RTE_CRYPTO_AUTH_SHA1) |
RTE_BIT64(RTE_CRYPTO_AUTH_SHA224) |
RTE_BIT64(RTE_CRYPTO_AUTH_SHA256) |
@@ -1324,6 +1326,29 @@ static int openssl_set_asym_session_parameters(
asym_session->u.r.label_len = 0;
asym_session->u.r.label = NULL;
}
+ } else if (xform->rsa.padding.type == RTE_CRYPTO_RSA_PADDING_PSS) {
+ asym_session->u.r.pss_md = openssl_get_md(xform->rsa.padding.hash);
+
+ if (asym_session->u.r.pss_md == NULL) {
+ OPENSSL_LOG(ERR,
+ "Unsupported PSS hash algorithm %u",
+ xform->rsa.padding.hash);
+ goto err_rsa;
+ }
+
+ enum rte_crypto_auth_algorithm mgf1 = xform->rsa.padding.mgf1hash;
+
+ if (mgf1 == 0)
+ mgf1 = xform->rsa.padding.hash;
+
+ asym_session->u.r.pss_mgf1_md = openssl_get_md(mgf1);
+ if (asym_session->u.r.pss_mgf1_md == NULL) {
+ OPENSSL_LOG(ERR,
+ "Unsupported PSS MGF1 hash algorithm %u", mgf1);
+ goto err_rsa;
+ }
+
+ asym_session->u.r.pss_saltlen = xform->rsa.padding.pss_saltlen;
}
OSSL_PARAM_BLD * param_bld = OSSL_PARAM_BLD_new();
--
2.54.0
More information about the dev
mailing list