Fix CipherInit on s390x.
authorJuergen Christ <jchrist@linux.ibm.com>
Fri, 28 May 2021 13:02:52 +0000 (15:02 +0200)
committerPauli <pauli@openssl.org>
Tue, 8 Jun 2021 04:32:44 +0000 (14:32 +1000)
Various different initialization sequences led to bugs on s390x due to caching
and processing during key setting.  Since, e.g., the direction does not
necessarily have to be correct during initialization, this produced bugs in
s390x which were not present on other architectures.  Fix this by recomputing
the function codes on the fly during updates and final operations.

Signed-off-by: Juergen Christ <jchrist@linux.ibm.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/15521)

providers/implementations/ciphers/cipher_aes_gcm.h
providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.inc
providers/implementations/ciphers/cipher_aes_hw_s390x.inc

index bcffa15871d82f70b40c12f1f02a96dc3d0de1ae..46b5ee3859dadab29f934c258831dfc2c7bfdcd9 100644 (file)
@@ -29,6 +29,7 @@ typedef struct prov_aes_gcm_ctx_st {
                 S390X_KMA_PARAMS kma;
             } param;
             unsigned int fc;
+            unsigned int hsflag;    /* hash subkey set flag */
             unsigned char ares[16];
             unsigned char mres[16];
             unsigned char kres[16];
index f7970939283e14e2afb6cbbb09eae485626aba0b..c45657952b722c666aab78388b386bad7aa490e6 100644 (file)
 /* iv + padding length for iv lengths != 12 */
 #define S390X_gcm_ivpadlen(i)  ((((i) + 15) >> 4 << 4) + 16)
 
+/* Additional flag or'ed to fc for decryption */
+#define S390X_gcm_decrypt_flag(ctx) (((ctx)->enc) ? 0 : S390X_DECRYPT)
+
+#define S390X_gcm_fc(A,C) ((A)->plat.s390x.fc | (A)->plat.s390x.hsflag |\
+                            S390X_gcm_decrypt_flag((C)))
+
 static int s390x_aes_gcm_initkey(PROV_GCM_CTX *ctx,
                                  const unsigned char *key, size_t keylen)
 {
@@ -23,8 +29,6 @@ static int s390x_aes_gcm_initkey(PROV_GCM_CTX *ctx,
     ctx->key_set = 1;
     memcpy(&actx->plat.s390x.param.kma.k, key, keylen);
     actx->plat.s390x.fc = S390X_AES_FC(keylen);
-    if (!ctx->enc)
-        actx->plat.s390x.fc |= S390X_DECRYPT;
     return 1;
 }
 
@@ -46,6 +50,7 @@ static int s390x_aes_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv,
         memcpy(&kma->j0, iv, ivlen);
         kma->j0.w[3] = 1;
         kma->cv.w = 1;
+        actx->plat.s390x.hsflag = 0;
     } else {
         unsigned long long ivbits = ivlen << 3;
         size_t len = S390X_gcm_ivpadlen(ivlen);
@@ -63,7 +68,7 @@ static int s390x_aes_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv,
          * param.
          */
         s390x_kma(iv_zero_pad, len, NULL, 0, NULL, actx->plat.s390x.fc, kma);
-        actx->plat.s390x.fc |= S390X_KMA_HS; /* The hash subkey is set */
+        actx->plat.s390x.hsflag = S390X_KMA_HS; /* The hash subkey is set */
 
         /* Copy the 128 bit GHASH result into J0 and clear the tag */
         kma->j0.g[0] = kma->t.g[0];
@@ -81,13 +86,15 @@ static int s390x_aes_gcm_cipher_final(PROV_GCM_CTX *ctx, unsigned char *tag)
     PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
     S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma;
     unsigned char out[AES_BLOCK_SIZE];
+    unsigned int fc;
     int rc;
 
     kma->taadl <<= 3;
     kma->tpcl <<= 3;
+    fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD | S390X_KMA_LPC;
     s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen,
               actx->plat.s390x.mres, actx->plat.s390x.mreslen, out,
-              actx->plat.s390x.fc | S390X_KMA_LAAD | S390X_KMA_LPC, kma);
+              fc, kma);
 
     /* gctx->mres already returned to the caller */
     OPENSSL_cleanse(out, actx->plat.s390x.mreslen);
@@ -110,12 +117,13 @@ static int s390x_aes_gcm_one_shot(PROV_GCM_CTX *ctx,
 {
     PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
     S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma;
+    unsigned int fc;
     int rc;
 
     kma->taadl = aad_len << 3;
     kma->tpcl = in_len << 3;
-    s390x_kma(aad, aad_len, in, in_len, out,
-              actx->plat.s390x.fc | S390X_KMA_LAAD | S390X_KMA_LPC, kma);
+    fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD | S390X_KMA_LPC;
+    s390x_kma(aad, aad_len, in, in_len, out, fc, kma);
 
     if (ctx->enc) {
         memcpy(tag, kma->t.b, taglen);
@@ -136,6 +144,7 @@ static int s390x_aes_gcm_aad_update(PROV_GCM_CTX *ctx,
     PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
     S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma;
     unsigned long long alen;
+    unsigned int fc;
     int n, rem;
 
     /* If already processed pt/ct then error */
@@ -160,9 +169,9 @@ static int s390x_aes_gcm_aad_update(PROV_GCM_CTX *ctx,
         }
         /* ctx->ares contains a complete block if offset has wrapped around */
         if (!n) {
-            s390x_kma(actx->plat.s390x.ares, 16, NULL, 0, NULL,
-                      actx->plat.s390x.fc, kma);
-            actx->plat.s390x.fc |= S390X_KMA_HS;
+            fc = S390X_gcm_fc(actx, ctx);
+            s390x_kma(actx->plat.s390x.ares, 16, NULL, 0, NULL, fc, kma);
+            actx->plat.s390x.hsflag = S390X_KMA_HS;
         }
         actx->plat.s390x.areslen = n;
     }
@@ -172,8 +181,9 @@ static int s390x_aes_gcm_aad_update(PROV_GCM_CTX *ctx,
     /* Add any remaining 16 byte blocks (128 bit each) */
     len &= ~(size_t)0xf;
     if (len) {
-        s390x_kma(aad, len, NULL, 0, NULL, actx->plat.s390x.fc, kma);
-        actx->plat.s390x.fc |= S390X_KMA_HS;
+        fc = S390X_gcm_fc(actx, ctx);
+        s390x_kma(aad, len, NULL, 0, NULL, fc, kma);
+        actx->plat.s390x.hsflag = S390X_KMA_HS;
         aad += len;
     }
 
@@ -200,6 +210,7 @@ static int s390x_aes_gcm_cipher_update(PROV_GCM_CTX *ctx,
     S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma;
     const unsigned char *inptr;
     unsigned long long mlen;
+    unsigned int fc;
     union {
         unsigned int w[4];
         unsigned char b[16];
@@ -212,6 +223,7 @@ static int s390x_aes_gcm_cipher_update(PROV_GCM_CTX *ctx,
         return 0;
     kma->tpcl = mlen;
 
+    fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD;
     n = actx->plat.s390x.mreslen;
     if (n) {
         inptr = in;
@@ -225,9 +237,9 @@ static int s390x_aes_gcm_cipher_update(PROV_GCM_CTX *ctx,
         /* ctx->mres contains a complete block if offset has wrapped around */
         if (!n) {
             s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen,
-                      actx->plat.s390x.mres, 16, buf.b,
-                      actx->plat.s390x.fc | S390X_KMA_LAAD, kma);
-            actx->plat.s390x.fc |= S390X_KMA_HS;
+                      actx->plat.s390x.mres, 16, buf.b, fc, kma);
+            actx->plat.s390x.hsflag = S390X_KMA_HS;
+            fc |= S390X_KMA_HS;
             actx->plat.s390x.areslen = 0;
 
             /* previous call already encrypted/decrypted its remainder,
@@ -249,10 +261,10 @@ static int s390x_aes_gcm_cipher_update(PROV_GCM_CTX *ctx,
     len &= ~(size_t)0xf;
     if (len) {
         s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen, in, len, out,
-                  actx->plat.s390x.fc | S390X_KMA_LAAD, kma);
+                  fc, kma);
         in += len;
         out += len;
-        actx->plat.s390x.fc |= S390X_KMA_HS;
+        actx->plat.s390x.hsflag = S390X_KMA_HS;
         actx->plat.s390x.areslen = 0;
     }
 
@@ -268,7 +280,7 @@ static int s390x_aes_gcm_cipher_update(PROV_GCM_CTX *ctx,
             buf.w[2] = kma->j0.w[2];
             buf.w[3] = kma->cv.w + 1;
             s390x_km(buf.b, 16, actx->plat.s390x.kres,
-                     actx->plat.s390x.fc & 0x1f, &kma->k);
+                     fc & 0x1f, &kma->k);
         }
 
         n = actx->plat.s390x.mreslen;
index c298dfafd75c76b9d16de8420169406e741a40a5..c8282dbd08a9cdd0d18495c2c7ed3df295490768 100644 (file)
@@ -14,6 +14,8 @@
 
 #include "s390x_arch.h"
 
+#include <stdio.h>
+
 #define s390x_aes_cbc_initkey    cipher_hw_aes_initkey
 #define s390x_aes_cfb1_initkey   cipher_hw_aes_initkey
 #define s390x_aes_ctr_initkey    cipher_hw_aes_initkey
@@ -34,9 +36,6 @@ static int s390x_aes_ecb_initkey(PROV_CIPHER_CTX *dat,
     PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
 
     adat->plat.s390x.fc = S390X_AES_FC(keylen);
-    if (!dat->enc)
-        adat->plat.s390x.fc |= S390X_DECRYPT;
-
     memcpy(adat->plat.s390x.param.km.k, key, keylen);
     return 1;
 }
@@ -45,8 +44,10 @@ static int s390x_aes_ecb_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out,
                                    const unsigned char *in, size_t len)
 {
     PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
+    unsigned int modifier = adat->base.enc ? 0 : S390X_DECRYPT;
 
-    s390x_km(in, len, out, adat->plat.s390x.fc, &adat->plat.s390x.param.km);
+    s390x_km(in, len, out, adat->plat.s390x.fc | modifier,
+             &adat->plat.s390x.param.km);
     return 1;
 }
 
@@ -90,7 +91,8 @@ static int s390x_aes_ofb128_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out,
 
     if (rem) {
         s390x_km(adat->plat.s390x.param.kmo_kmf.cv, 16,
-                 adat->plat.s390x.param.kmo_kmf.cv, adat->plat.s390x.fc,
+                 adat->plat.s390x.param.kmo_kmf.cv,
+                 adat->plat.s390x.fc,
                  adat->plat.s390x.param.kmo_kmf.k);
 
         while (rem--) {
@@ -111,9 +113,6 @@ static int s390x_aes_cfb128_initkey(PROV_CIPHER_CTX *dat,
 
     adat->plat.s390x.fc = S390X_AES_FC(keylen);
     adat->plat.s390x.fc |= 16 << 24;   /* 16 bytes cipher feedback */
-    if (!dat->enc)
-        adat->plat.s390x.fc |= S390X_DECRYPT;
-
     adat->plat.s390x.res = 0;
     memcpy(adat->plat.s390x.param.kmo_kmf.k, key, keylen);
     return 1;
@@ -123,6 +122,7 @@ static int s390x_aes_cfb128_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out,
                                       const unsigned char *in, size_t len)
 {
     PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
+    unsigned int modifier = adat->base.enc ? 0 : S390X_DECRYPT;
     int n = adat->plat.s390x.res;
     int rem;
     unsigned char tmp;
@@ -142,7 +142,7 @@ static int s390x_aes_cfb128_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out,
 
     len &= ~(size_t)0xf;
     if (len) {
-        s390x_kmf(in, len, out, adat->plat.s390x.fc,
+        s390x_kmf(in, len, out, adat->plat.s390x.fc | modifier,
                   &adat->plat.s390x.param.kmo_kmf);
 
         out += len;
@@ -152,7 +152,8 @@ static int s390x_aes_cfb128_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out,
     if (rem) {
         s390x_km(adat->plat.s390x.param.kmo_kmf.cv, 16,
                  adat->plat.s390x.param.kmo_kmf.cv,
-                 S390X_AES_FC(dat->keylen), adat->plat.s390x.param.kmo_kmf.k);
+                 S390X_AES_FC(dat->keylen),
+                 adat->plat.s390x.param.kmo_kmf.k);
 
         while (rem--) {
             tmp = in[n];
@@ -174,9 +175,6 @@ static int s390x_aes_cfb8_initkey(PROV_CIPHER_CTX *dat,
 
     adat->plat.s390x.fc = S390X_AES_FC(keylen);
     adat->plat.s390x.fc |= 1 << 24;   /* 1 byte cipher feedback */
-    if (!dat->enc)
-        adat->plat.s390x.fc |= S390X_DECRYPT;
-
     memcpy(adat->plat.s390x.param.kmo_kmf.k, key, keylen);
     return 1;
 }
@@ -185,9 +183,10 @@ static int s390x_aes_cfb8_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out,
                                     const unsigned char *in, size_t len)
 {
     PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
+    unsigned int modifier = adat->base.enc ? 0 : S390X_DECRYPT;
 
     memcpy(adat->plat.s390x.param.kmo_kmf.cv, dat->iv, dat->ivlen);
-    s390x_kmf(in, len, out, adat->plat.s390x.fc,
+    s390x_kmf(in, len, out, adat->plat.s390x.fc | modifier,
               &adat->plat.s390x.param.kmo_kmf);
     memcpy(dat->iv, adat->plat.s390x.param.kmo_kmf.cv, dat->ivlen);
     return 1;