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 <[email protected]> --- 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

