Commit 774525b38b for openssl.org

commit 774525b38bd47b3e895249a81aa885239a7d2061
Author: Simo Sorce <simo@redhat.com>
Date:   Thu Jun 11 13:38:05 2026 -0400

    Migrate s390x AES-XTS to standard HW interface

    Move the s390x-specific AES-XTS implementation from `cipher_aes_xts_s390x.inc`
    to `cipher_aes_xts_hw.c`, adapting it to use the standard `PROV_CIPHER_HW`
    dispatch structure.

    This refactoring removes standalone initialization wrappers and integrates the
    s390x hardware backend more cleanly with the generic AES-XTS provider code. It
    also reduces code duplication by relying on the generic layer for common
    validations (such as the maximum blocks per data unit limit) before invoking
    the hardware-specific stream cipher.

    Signed-off-by: Simo Sorce <simo@redhat.com>

    Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
    Reviewed-by: Norbert Pocs <norbertp@openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
    MergeDate: Sat Jun 27 09:05:46 2026
    (Merged from https://github.com/openssl/openssl/pull/31472)

diff --git a/providers/implementations/ciphers/cipher_aes_xts.c b/providers/implementations/ciphers/cipher_aes_xts.c
index a630b25eb3..10c99c400e 100644
--- a/providers/implementations/ciphers/cipher_aes_xts.c
+++ b/providers/implementations/ciphers/cipher_aes_xts.c
@@ -63,10 +63,6 @@ static int aes_xts_check_keys_differ(const unsigned char *key, size_t bytes,
     return 1;
 }

-#ifdef AES_XTS_S390X
-#include "cipher_aes_xts_s390x.inc"
-#endif
-
 /*-
  * Provider dispatch functions
  */
@@ -85,6 +81,17 @@ static int aes_xts_init(void *vctx, const unsigned char *key, size_t keylen,
     if (iv != NULL) {
         if (!ossl_cipher_generic_initiv(vctx, iv, ivlen))
             return 0;
+#ifdef AES_XTS_S390X
+        if (key == NULL) {
+            /* special handle iv-only update */
+            if (ivlen > sizeof(xctx->plat.s390x.param.km.tweak)) {
+                ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+                return 0;
+            }
+            memcpy(xctx->plat.s390x.param.km.tweak, iv, ivlen);
+            xctx->plat.s390x.iv_set = 1;
+        }
+#endif
     }
     if (key != NULL) {
         if (keylen != ctx->keylen) {
@@ -103,10 +110,6 @@ static int aes_xts_einit(void *vctx, const unsigned char *key, size_t keylen,
     const unsigned char *iv, size_t ivlen,
     const OSSL_PARAM params[])
 {
-#ifdef AES_XTS_S390X
-    if (s390x_aes_xts_einit(vctx, key, keylen, iv, ivlen, params) == 1)
-        return 1;
-#endif
     return aes_xts_init(vctx, key, keylen, iv, ivlen, params, 1);
 }

@@ -114,10 +117,6 @@ static int aes_xts_dinit(void *vctx, const unsigned char *key, size_t keylen,
     const unsigned char *iv, size_t ivlen,
     const OSSL_PARAM params[])
 {
-#ifdef AES_XTS_S390X
-    if (s390x_aes_xts_dinit(vctx, key, keylen, iv, ivlen, params) == 1)
-        return 1;
-#endif
     return aes_xts_init(vctx, key, keylen, iv, ivlen, params, 0);
 }

@@ -152,11 +151,6 @@ static void *aes_xts_dupctx(void *vctx)
     if (!ossl_prov_is_running())
         return NULL;

-#ifdef AES_XTS_S390X
-    if (in->plat.s390x.fc)
-        return s390x_aes_xts_dupctx(vctx);
-#endif
-
     if (in->xts.key1 != NULL) {
         if (in->xts.key1 != &in->ks1)
             return NULL;
@@ -177,20 +171,23 @@ static int aes_xts_cipher(void *vctx, unsigned char *out, size_t *outl,
 {
     PROV_AES_XTS_CTX *ctx = (PROV_AES_XTS_CTX *)vctx;

-#ifdef AES_XTS_S390X
-    if (ctx->plat.s390x.fc)
-        return s390x_aes_xts_cipher(vctx, out, outl, outsize, in, inl);
-#endif
-
     if (!ossl_prov_is_running()
-        || ctx->xts.key1 == NULL
-        || ctx->xts.key2 == NULL
-        || !ctx->base.iv_set
-        || out == NULL
+        || inl < AES_BLOCK_SIZE
         || in == NULL
-        || inl < AES_BLOCK_SIZE)
+        || out == NULL)
         return 0;

+#ifdef AES_XTS_S390X
+    if (ctx->plat.s390x.fc) {
+        if (!ctx->plat.s390x.iv_set || !ctx->plat.s390x.key_set)
+            return 0;
+    } else
+#endif
+    {
+        if (ctx->xts.key1 == NULL || ctx->xts.key2 == NULL)
+            return 0;
+    }
+
     /*
      * Impose a limit of 2^20 blocks per data unit as specified by
      * IEEE Std 1619-2018.  The earlier and obsolete IEEE Std 1619-2007
@@ -202,6 +199,11 @@ static int aes_xts_cipher(void *vctx, unsigned char *out, size_t *outl,
         return 0;
     }

+#ifdef AES_XTS_S390X
+    if (ctx->plat.s390x.fc)
+        return s390x_aes_xts_cipher_stream(ctx, out, outl, in, inl);
+#endif
+
     if (ctx->stream != NULL)
         (*ctx->stream)(in, out, inl, ctx->xts.key1, ctx->xts.key2, ctx->base.iv);
     else if (CRYPTO_xts128_encrypt(&ctx->xts, ctx->base.iv, in, out, inl,
diff --git a/providers/implementations/ciphers/cipher_aes_xts.h b/providers/implementations/ciphers/cipher_aes_xts.h
index 18dc295e47..a2421ae9ef 100644
--- a/providers/implementations/ciphers/cipher_aes_xts.h
+++ b/providers/implementations/ciphers/cipher_aes_xts.h
@@ -60,6 +60,12 @@ typedef struct prov_aes_xts_ctx_st {
     } plat;
 } PROV_AES_XTS_CTX;

+#ifdef AES_XTS_S390X
+int s390x_aes_xts_cipher_stream(PROV_AES_XTS_CTX *xctx,
+    unsigned char *out, size_t *outl,
+    const unsigned char *in, size_t inl);
+#endif
+
 const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts(size_t keybits);

 #endif /* !defined(OSSL_PROVIDERS_IMPLEMENTATIONS_CIPHERS_CIPHER_AES_XTS_H) */
diff --git a/providers/implementations/ciphers/cipher_aes_xts_hw.c b/providers/implementations/ciphers/cipher_aes_xts_hw.c
index 6b8c7ab4b4..b9e21df4fd 100644
--- a/providers/implementations/ciphers/cipher_aes_xts_hw.c
+++ b/providers/implementations/ciphers/cipher_aes_xts_hw.c
@@ -276,6 +276,144 @@ static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_rv32i()
     return NULL;
 }

+#elif defined(AES_XTS_S390X)
+
+int s390x_aes_xts_cipher_stream(PROV_AES_XTS_CTX *xctx,
+    unsigned char *out, size_t *outl,
+    const unsigned char *in, size_t inl)
+{
+    S390X_KM_XTS_PARAMS *km = &xctx->plat.s390x.param.km;
+    unsigned char *param = (unsigned char *)km + xctx->plat.s390x.offset;
+    unsigned int fc = xctx->plat.s390x.fc;
+    unsigned char tmp[2][AES_BLOCK_SIZE];
+    unsigned char nap_n1[AES_BLOCK_SIZE];
+    unsigned char drop[AES_BLOCK_SIZE];
+    size_t len_incomplete, len_complete;
+
+    len_incomplete = inl % AES_BLOCK_SIZE;
+    len_complete = (len_incomplete == 0) ? inl : (inl / AES_BLOCK_SIZE - 1) * AES_BLOCK_SIZE;
+
+    if (len_complete > 0)
+        s390x_km(in, len_complete, out, fc, param);
+    if (len_incomplete == 0)
+        goto out;
+
+    memcpy(tmp, in + len_complete, AES_BLOCK_SIZE + len_incomplete);
+    /* swap NAP for decrypt */
+    if (fc & S390X_DECRYPT) {
+        memcpy(nap_n1, km->nap, AES_BLOCK_SIZE);
+        s390x_km(tmp[0], AES_BLOCK_SIZE, drop, fc, param);
+    }
+    s390x_km(tmp[0], AES_BLOCK_SIZE, tmp[0], fc, param);
+    if (fc & S390X_DECRYPT)
+        memcpy(km->nap, nap_n1, AES_BLOCK_SIZE);
+
+    memcpy(tmp[1] + len_incomplete, tmp[0] + len_incomplete,
+        AES_BLOCK_SIZE - len_incomplete);
+    s390x_km(tmp[1], AES_BLOCK_SIZE, out + len_complete, fc, param);
+    memcpy(out + len_complete + AES_BLOCK_SIZE, tmp[0], len_incomplete);
+
+    /* do not expose temporary data */
+    OPENSSL_cleanse(tmp, sizeof(tmp));
+out:
+    memcpy(xctx->base.iv, km->tweak, AES_BLOCK_SIZE);
+    *outl = inl;
+
+    return 1;
+}
+
+static int cipher_hw_aes_xts_s390x_initkey(PROV_CIPHER_CTX *ctx,
+    const unsigned char *key, size_t keylen)
+{
+    PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx;
+    S390X_KM_XTS_PARAMS *km = &xctx->plat.s390x.param.km;
+    unsigned int fc, offs;
+    unsigned int dec = 0;
+    int supported = 0;
+
+    switch (keylen) {
+    case 128 / 8 * 2:
+        fc = S390X_XTS_AES_128_MSA10;
+        offs = 32;
+        break;
+    case 256 / 8 * 2:
+        fc = S390X_XTS_AES_256_MSA10;
+        offs = 0;
+        break;
+    default:
+        fc = 0;
+        break;
+    }
+
+    if (fc != 0)
+        supported = (OPENSSL_s390xcap_P.km[1] && S390X_CAPBIT(fc));
+    if (!supported) {
+        xctx->plat.s390x.fc = 0;
+        xctx->plat.s390x.offset = 0;
+        return 0;
+    }
+
+    if (xctx->base.iv_set) {
+        if (xctx->base.ivlen > sizeof(km->tweak)) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+            return 0;
+        }
+        memcpy(km->tweak, xctx->base.iv, xctx->base.ivlen);
+        xctx->plat.s390x.iv_set = 1;
+    }
+
+    if (key != NULL) {
+        memcpy(km->key + offs, key, keylen);
+        xctx->plat.s390x.key_set = 1;
+    }
+
+    if (xctx->base.enc == 0)
+        dec = S390X_DECRYPT;
+
+    xctx->plat.s390x.fc = fc | dec;
+    xctx->plat.s390x.offset = offs;
+
+    memset(km->nap, 0, sizeof(km->nap));
+    km->nap[0] = 0x1;
+
+    return 1;
+}
+
+static void cipher_hw_aes_xts_s390x_copyctx(PROV_CIPHER_CTX *dst,
+    const PROV_CIPHER_CTX *src)
+{
+    PROV_AES_XTS_CTX *sctx = (PROV_AES_XTS_CTX *)src;
+    PROV_AES_XTS_CTX *dctx = (PROV_AES_XTS_CTX *)dst;
+
+    *dctx = *sctx;
+    dctx->xts.key1 = NULL;
+    dctx->xts.key2 = NULL;
+}
+
+static const PROV_CIPHER_HW aes_xts_s390x = {
+    cipher_hw_aes_xts_s390x_initkey,
+    NULL,
+    cipher_hw_aes_xts_s390x_copyctx
+};
+
+static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_s390x(size_t keybits)
+{
+    switch (keybits) {
+    case (128 * 2):
+        if (OPENSSL_s390xcap_P.km[1] && S390X_CAPBIT(S390X_XTS_AES_128_MSA10))
+            return &aes_xts_s390x;
+        break;
+    case (256 * 2):
+        if (OPENSSL_s390xcap_P.km[1] && S390X_CAPBIT(S390X_XTS_AES_256_MSA10))
+            return &aes_xts_s390x;
+        break;
+    default:
+        break;
+    }
+
+    return NULL;
+}
+
 #endif

 static const PROV_CIPHER_HW aes_generic_xts = {
@@ -296,6 +434,8 @@ const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts(size_t keybits)
     aes_xts_hw = ossl_prov_cipher_hw_aes_xts_rv64i();
 #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32
     aes_xts_hw = ossl_prov_cipher_hw_aes_xts_rv32i();
+#elif defined(AES_XTS_S390X)
+    aes_xts_hw = ossl_prov_cipher_hw_aes_xts_s390x(keybits);
 #endif

     if (aes_xts_hw == NULL)
diff --git a/providers/implementations/ciphers/cipher_aes_xts_s390x.inc b/providers/implementations/ciphers/cipher_aes_xts_s390x.inc
deleted file mode 100644
index b13d23581c..0000000000
--- a/providers/implementations/ciphers/cipher_aes_xts_s390x.inc
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License").  You may not use
- * this file except in compliance with the License.  You can obtain a copy
- * in the file LICENSE in the source distribution or at
- * https://www.openssl.org/source/license.html
- */
-
-#include "arch/s390x_arch.h"
-
-static OSSL_FUNC_cipher_encrypt_init_fn s390x_aes_xts_einit;
-static OSSL_FUNC_cipher_decrypt_init_fn s390x_aes_xts_dinit;
-static OSSL_FUNC_cipher_cipher_fn s390x_aes_xts_cipher;
-static OSSL_FUNC_cipher_dupctx_fn s390x_aes_xts_dupctx;
-
-static int s390x_aes_xts_init(void *vctx, const unsigned char *key,
-                              size_t keylen, const unsigned char *iv,
-                              size_t ivlen, const OSSL_PARAM params[],
-                              unsigned int dec)
-{
-    PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)vctx;
-    S390X_KM_XTS_PARAMS *km = &xctx->plat.s390x.param.km;
-    unsigned int fc, offs;
-
-    switch (xctx->base.keylen) {
-    case 128 / 8 * 2:
-        fc = S390X_XTS_AES_128_MSA10;
-        offs = 32;
-        break;
-    case 256 / 8 * 2:
-        fc = S390X_XTS_AES_256_MSA10;
-        offs = 0;
-        break;
-    default:
-        goto not_supported;
-    }
-
-    if (!(OPENSSL_s390xcap_P.km[1] && S390X_CAPBIT(fc)))
-        goto not_supported;
-
-    if (iv != NULL) {
-        if (ivlen != xctx->base.ivlen
-                || ivlen > sizeof(km->tweak)) {
-            ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
-            return 0;
-        }
-        memcpy(km->tweak, iv, ivlen);
-        xctx->plat.s390x.iv_set = 1;
-    }
-
-    if (key != NULL) {
-        if (keylen != xctx->base.keylen) {
-            ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
-            return 0;
-        }
-        if (!aes_xts_check_keys_differ(key, keylen / 2, !dec))
-            return 0;
-
-        memcpy(km->key + offs, key, keylen);
-        xctx->plat.s390x.key_set = 1;
-    }
-
-    xctx->plat.s390x.fc = fc | dec;
-    xctx->plat.s390x.offset = offs;
-
-    memset(km->nap, 0, sizeof(km->nap));
-    km->nap[0] = 0x1;
-
-    return aes_xts_set_ctx_params(xctx, params);
-
-not_supported:
-    xctx->plat.s390x.fc = 0;
-    xctx->plat.s390x.offset = 0;
-    return 0;
-}
-
-static int s390x_aes_xts_einit(void *vctx, const unsigned char *key,
-                               size_t keylen, const unsigned char *iv,
-                               size_t ivlen, const OSSL_PARAM params[])
-{
-    return s390x_aes_xts_init(vctx, key, keylen, iv, ivlen, params, 0);
-}
-
-static int s390x_aes_xts_dinit(void *vctx, const unsigned char *key,
-                               size_t keylen, const unsigned char *iv,
-                               size_t ivlen, const OSSL_PARAM params[])
-{
-    return s390x_aes_xts_init(vctx, key, keylen, iv, ivlen, params,
-                              S390X_DECRYPT);
-}
-
-static void *s390x_aes_xts_dupctx(void *vctx)
-{
-    PROV_AES_XTS_CTX *in = (PROV_AES_XTS_CTX *)vctx;
-    PROV_AES_XTS_CTX *ret = OPENSSL_zalloc(sizeof(*in));
-
-    if (ret != NULL)
-        *ret = *in;
-
-    return ret;
-}
-
-static int s390x_aes_xts_cipher(void *vctx, unsigned char *out, size_t *outl,
-                                size_t outsize, const unsigned char *in,
-                                size_t inl)
-{
-    PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)vctx;
-    S390X_KM_XTS_PARAMS *km = &xctx->plat.s390x.param.km;
-    unsigned char *param = (unsigned char *)km + xctx->plat.s390x.offset;
-    unsigned int fc = xctx->plat.s390x.fc;
-    unsigned char tmp[2][AES_BLOCK_SIZE];
-    unsigned char nap_n1[AES_BLOCK_SIZE];
-    unsigned char drop[AES_BLOCK_SIZE];
-    size_t len_incomplete, len_complete;
-
-    if (!ossl_prov_is_running()
-            || inl < AES_BLOCK_SIZE
-            || in == NULL
-            || out == NULL
-            || !xctx->plat.s390x.iv_set
-            || !xctx->plat.s390x.key_set)
-        return 0;
-
-    /*
-     * Impose a limit of 2^20 blocks per data unit as specified by
-     * IEEE Std 1619-2018.  The earlier and obsolete IEEE Std 1619-2007
-     * indicated that this was a SHOULD NOT rather than a MUST NOT.
-     * NIST SP 800-38E mandates the same limit.
-     */
-    if (inl > XTS_MAX_BLOCKS_PER_DATA_UNIT * AES_BLOCK_SIZE) {
-        ERR_raise(ERR_LIB_PROV, PROV_R_XTS_DATA_UNIT_IS_TOO_LARGE);
-        return 0;
-    }
-
-    len_incomplete = inl % AES_BLOCK_SIZE;
-    len_complete = (len_incomplete == 0) ? inl :
-                       (inl / AES_BLOCK_SIZE - 1) * AES_BLOCK_SIZE;
-
-    if (len_complete > 0)
-        s390x_km(in, len_complete, out, fc, param);
-    if (len_incomplete == 0)
-       goto out;
-
-    memcpy(tmp, in + len_complete, AES_BLOCK_SIZE + len_incomplete);
-    /* swap NAP for decrypt */
-    if (fc & S390X_DECRYPT) {
-        memcpy(nap_n1, km->nap, AES_BLOCK_SIZE);
-        s390x_km(tmp[0], AES_BLOCK_SIZE, drop, fc, param);
-    }
-    s390x_km(tmp[0], AES_BLOCK_SIZE, tmp[0], fc, param);
-    if (fc & S390X_DECRYPT)
-        memcpy(km->nap, nap_n1, AES_BLOCK_SIZE);
-
-    memcpy(tmp[1] + len_incomplete, tmp[0] + len_incomplete,
-           AES_BLOCK_SIZE - len_incomplete);
-    s390x_km(tmp[1], AES_BLOCK_SIZE, out + len_complete, fc, param);
-    memcpy(out + len_complete + AES_BLOCK_SIZE, tmp[0], len_incomplete);
-
-    /* do not expose temporary data */
-    OPENSSL_cleanse(tmp, sizeof(tmp));
-out:
-    memcpy(xctx->base.iv, km->tweak, AES_BLOCK_SIZE);
-    *outl = inl;
-
-    return 1;
-}