Commit b8e2dba6bb for openssl.org
commit b8e2dba6bbaa1b1817f17097b57bc2598ecb3df3
Author: Ingo Franzki <ifranzki@linux.ibm.com>
Date: Tue May 19 15:12:24 2026 +0200
s390x: Disable mod-expo offloading to a crypto card for small exponents
Add a new keyword 'cex_min_bits:<bits>' for the OPENSSL_s390xcap environment
variable to specify the minimum number of bits that the exponent must have
to offload the operation to the crypto card. Operations with smaller exponents
fall back to software calculation. Mod-expo operations with small exponents
are faster in software than via the crypto card.
The default minimum exponent size is 2048 bits, but this can be changed via
the OPENSSL_s390xcap environment variable.
For mod-expo operations the exponent is checked, and for RSA CRT operations
the modulus is checked to have the minimum bit size. RSA public key operations
usually have small exponents, so they are most likely always performed in
software.
Checking the bit size of a BIGNUM may or may not be constant-time, dependent
on the BN_FLG_CONSTTIME flag of the BIGNUM. For RSA CRT operations, the bit
size of the modulus is checked. The modulus is public information, so no
constant-time issue here. For mod-expo operations with the public exponent
it is also not a constant-time issue, because the public exponent is also
public information. For mod-expo operations with the private exponent, the
size of the private exponent is usually the same as the size of the modulus,
which is public information. For DH mod-expo operations with the private key,
the size of the private key is usually dependent on the DH group that is
used, and this is also public information. Nevertheless, private exponents
better have the BN_FLG_CONSTTIME flag set. For RSA this is the case, and for
DH it is also the case when flag DH_FLAG_CACHE_MONT_P is set, which is always
done (dh_init() sets the flag unconditionally). Furthermore, flag
BN_FLG_CONSTTIME is set for the private key in ossl_dh_generate_public_key().
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
Reviewed-by: Neil Horman <nhorman@openssl.org>
MergeDate: Fri Oct 23 15:20:45 2026
(Merged from https://github.com/openssl/openssl/pull/31630)
diff --git a/CHANGES.md b/CHANGES.md
index e936d437ae..664f4741aa 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -33,6 +33,17 @@ OpenSSL 4.2
### Changes between 4.1 and 4.2 [xx XXX xxxx]
+ * s390x: Disable mod-expo offloading to a crypto card for small exponents.
+ Add a new keyword `cex_min_bits:<bits>` for the `OPENSSL_s390xcap`
+ environment variable to specify the minimum number of bits that the
+ exponent must have to offload the operation to the crypto card.
+ Operations with smaller exponents fall back to software calculation.
+ Mod-expo operations with small exponents are faster in software than
+ via the crypto card. The default minimum exponent size is 2048 bits,
+ but this can be changed via the `OPENSSL_s390xcap` environment variable.
+
+ *Ingo Franzki*
+
* Changed TLS 1.3 clients to offer a PSK, and to attempt 0-RTT early data,
only when it can actually be used. A resumption ticket or external PSK is
offered only if some offered ciphersuite shares its handshake digest, and
diff --git a/crypto/bn/bn_s390x.c b/crypto/bn/bn_s390x.c
index 64842cf458..f744878902 100644
--- a/crypto/bn/bn_s390x.c
+++ b/crypto/bn/bn_s390x.c
@@ -34,7 +34,11 @@ static int s390x_mod_exp_hw(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
if (OPENSSL_s390xcex == -1 || OPENSSL_s390xcex_nodev)
return -1;
+ if (BN_num_bits(p) < (int)OPENSSL_s390xcex_min_bits)
+ return -1;
size = BN_num_bytes(m);
+ if (size > 4096 / 8) /* Acceleration only works up to 4096 bits modulus */
+ return -1;
buffer = OPENSSL_calloc(size, 4);
if (buffer == NULL)
return 0;
@@ -92,7 +96,7 @@ int s390x_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
* SW-fallback.
*/
int s390x_crt(BIGNUM *r, const BIGNUM *i, const BIGNUM *p, const BIGNUM *q,
- const BIGNUM *dmp, const BIGNUM *dmq, const BIGNUM *iqmp)
+ const BIGNUM *dmp, const BIGNUM *dmq, const BIGNUM *iqmp, const BIGNUM *n)
{
struct ica_rsa_modexpo_crt crt;
unsigned char *buffer, *part;
@@ -101,6 +105,11 @@ int s390x_crt(BIGNUM *r, const BIGNUM *i, const BIGNUM *p, const BIGNUM *q,
if (OPENSSL_s390xcex == -1 || OPENSSL_s390xcex_nodev)
return -1;
+ size = BN_num_bits(n);
+ if (size < OPENSSL_s390xcex_min_bits)
+ return -1;
+ if (size > 4096) /* Acceleration only works up to 4096 bits modulus */
+ return -1;
/*-
* Hardware-accelerated CRT can only deal with p>q. Fall back to
* software in the (hopefully rare) other cases.
@@ -172,7 +181,7 @@ int s390x_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
}
int s390x_crt(BIGNUM *r, const BIGNUM *i, const BIGNUM *p, const BIGNUM *q,
- const BIGNUM *dmp, const BIGNUM *dmq, const BIGNUM *iqmp)
+ const BIGNUM *dmp, const BIGNUM *dmq, const BIGNUM *iqmp, const BIGNUM *n)
{
return 0;
}
diff --git a/crypto/rsa/rsa_ossl.c b/crypto/rsa/rsa_ossl.c
index f444e98e61..a01047e416 100644
--- a/crypto/rsa/rsa_ossl.c
+++ b/crypto/rsa/rsa_ossl.c
@@ -1201,7 +1201,7 @@ static int rsa_ossl_s390x_mod_exp(BIGNUM *r0, const BIGNUM *i, RSA *rsa,
int rc;
if (rsa->version != RSA_ASN1_VERSION_MULTI) {
- rc = s390x_crt(r0, i, rsa->p, rsa->q, rsa->dmp1, rsa->dmq1, rsa->iqmp);
+ rc = s390x_crt(r0, i, rsa->p, rsa->q, rsa->dmp1, rsa->dmq1, rsa->iqmp, rsa->n);
if (rc >= 0)
return rc;
}
diff --git a/crypto/s390xcap.c b/crypto/s390xcap.c
index 54db50c39b..b105a8ed0a 100644
--- a/crypto/s390xcap.c
+++ b/crypto/s390xcap.c
@@ -82,7 +82,8 @@ void OPENSSL_vx_probe(void);
#endif
static const char *env;
-static int parse_env(struct OPENSSL_s390xcap_st *cap, int *cex);
+static int parse_env(struct OPENSSL_s390xcap_st *cap, int *cex,
+ unsigned int *cex_min_bits);
void OPENSSL_s390x_facilities(void);
void OPENSSL_s390x_functions(void);
@@ -92,6 +93,7 @@ struct OPENSSL_s390xcap_st OPENSSL_s390xcap_P;
#ifdef S390X_MOD_EXP
int OPENSSL_s390xcex;
int OPENSSL_s390xcex_nodev;
+unsigned int OPENSSL_s390xcex_min_bits;
#if defined(__GNUC__)
__attribute__((visibility("hidden")))
@@ -117,6 +119,7 @@ void OPENSSL_cpuid_setup(void)
{
struct OPENSSL_s390xcap_st cap;
int cex = 1;
+ unsigned int cex_min_bits = S390X_CEX_MIN_BITS_DEFAULT;
if (OPENSSL_s390xcap_P.stfle[0])
return;
@@ -177,7 +180,7 @@ void OPENSSL_cpuid_setup(void)
env = getenv("OPENSSL_s390xcap");
if (env != NULL) {
- if (!parse_env(&cap, &cex))
+ if (!parse_env(&cap, &cex, &cex_min_bits))
env = NULL;
}
@@ -223,10 +226,12 @@ void OPENSSL_cpuid_setup(void)
OPENSSL_s390xcex = open("/dev/z90crypt", O_RDWR | O_CLOEXEC);
}
OPENSSL_s390xcex_nodev = 0;
+ OPENSSL_s390xcex_min_bits = cex_min_bits;
#endif
}
-static int parse_env(struct OPENSSL_s390xcap_st *cap, int *cex)
+static int parse_env(struct OPENSSL_s390xcap_st *cap, int *cex,
+ unsigned int *cex_min_bits)
{
/* clang-format off */
/*-
@@ -831,6 +836,7 @@ static int parse_env(struct OPENSSL_s390xcap_st *cap, int *cex)
char *tok_begin, *tok_end, *buff, tok[S390X_STFLE_MAX][LEN + 1];
int rc, off, i, n;
+ unsigned int val;
buff = malloc(strlen(env) + 1);
if (buff == NULL)
@@ -894,6 +900,12 @@ static int parse_env(struct OPENSSL_s390xcap_st *cap, int *cex)
*cex = 0;
}
+ /* cex_min_bits to deactivate cex support for small key sizes */
+ else if (sscanf(tok_begin, " cex_min_bits : %u %" STR(LEN) "s ",
+ &val, tok[0]) == 1) {
+ *cex_min_bits = val;
+ }
+
/* whitespace(ignored) or invalid tokens */
else {
while (*tok_begin != '\0') {
diff --git a/doc/man3/OPENSSL_s390xcap.pod b/doc/man3/OPENSSL_s390xcap.pod
index a4e7fc0e04..d2261b979a 100644
--- a/doc/man3/OPENSSL_s390xcap.pod
+++ b/doc/man3/OPENSSL_s390xcap.pod
@@ -56,6 +56,13 @@ instruction is set to the specified 192-bit mask.
Deactivate modular exponentiation and CRT operation offloading to
Crypto Express Adapters.
+=item cex_min_bits:<bits>
+
+Deactivate modular exponentiation and CRT operation offloading to
+Crypto Express Adapters for exponents less than the specified bit size.
+The default is to only offload operations with an exponent of at least 2048
+bits.
+
=back
The 64-bit masks are specified in hexadecimal notation. The 0x prefix is
diff --git a/include/arch/s390x_arch.h b/include/arch/s390x_arch.h
index 5b8e92ec99..334c549767 100644
--- a/include/arch/s390x_arch.h
+++ b/include/arch/s390x_arch.h
@@ -89,7 +89,13 @@ extern int OPENSSL_s390xcex;
__attribute__((visibility("hidden")))
#endif
extern int OPENSSL_s390xcex_nodev;
+#if defined(__GNUC__) && defined(__linux)
+__attribute__((visibility("hidden")))
#endif
+extern unsigned int OPENSSL_s390xcex_min_bits;
+#endif
+
+#define S390X_CEX_MIN_BITS_DEFAULT 2048
/* Max number of 64-bit words currently returned by STFLE */
#define S390X_STFLE_MAX 3
diff --git a/include/crypto/bn.h b/include/crypto/bn.h
index 1336cb8d16..a4e2f2e4c6 100644
--- a/include/crypto/bn.h
+++ b/include/crypto/bn.h
@@ -136,7 +136,7 @@ extern const BIGNUM ossl_bn_inv_sqrt_2;
int s390x_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx);
int s390x_crt(BIGNUM *r, const BIGNUM *i, const BIGNUM *p, const BIGNUM *q,
- const BIGNUM *dmp, const BIGNUM *dmq, const BIGNUM *iqmp);
+ const BIGNUM *dmp, const BIGNUM *dmq, const BIGNUM *iqmp, const BIGNUM *n);
#endif