1/*
2 * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License").  You may not use
5 * this file except in compliance with the License.  You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10/*-
11 * IBM S390X support for AES GCM.
12 * This file is included by cipher_aes_gcm_hw.c
13 */
14
15/* iv + padding length for iv lengths != 12 */
16#define S390X_gcm_ivpadlen(i)  ((((i) + 15) >> 4 << 4) + 16)
17
18/* Additional flag or'ed to fc for decryption */
19#define S390X_gcm_decrypt_flag(ctx) (((ctx)->enc) ? 0 : S390X_DECRYPT)
20
21#define S390X_gcm_fc(A,C) ((A)->plat.s390x.fc | (A)->plat.s390x.hsflag |\
22                            S390X_gcm_decrypt_flag((C)))
23
24static int s390x_aes_gcm_initkey(PROV_GCM_CTX *ctx,
25                                 const unsigned char *key, size_t keylen)
26{
27    PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
28
29    ctx->key_set = 1;
30    memcpy(&actx->plat.s390x.param.kma.k, key, keylen);
31    actx->plat.s390x.fc = S390X_AES_FC(keylen);
32    return 1;
33}
34
35static int s390x_aes_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv,
36                               size_t ivlen)
37{
38    PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
39    S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma;
40
41    kma->t.g[0] = 0;
42    kma->t.g[1] = 0;
43    kma->tpcl = 0;
44    kma->taadl = 0;
45    actx->plat.s390x.mreslen = 0;
46    actx->plat.s390x.areslen = 0;
47    actx->plat.s390x.kreslen = 0;
48
49    if (ivlen == GCM_IV_DEFAULT_SIZE) {
50        memcpy(&kma->j0, iv, ivlen);
51        kma->j0.w[3] = 1;
52        kma->cv.w = 1;
53        actx->plat.s390x.hsflag = 0;
54    } else {
55        unsigned long long ivbits = ivlen << 3;
56        size_t len = S390X_gcm_ivpadlen(ivlen);
57        unsigned char iv_zero_pad[S390X_gcm_ivpadlen(GCM_IV_MAX_SIZE)];
58        /*
59         * The IV length needs to be zero padded to be a multiple of 16 bytes
60         * followed by 8 bytes of zeros and 8 bytes for the IV length.
61         * The GHASH of this value can then be calculated.
62         */
63        memcpy(iv_zero_pad, iv, ivlen);
64        memset(iv_zero_pad + ivlen, 0, len - ivlen);
65        memcpy(iv_zero_pad + len - sizeof(ivbits), &ivbits, sizeof(ivbits));
66        /*
67         * Calculate the ghash of the iv - the result is stored into the tag
68         * param.
69         */
70        s390x_kma(iv_zero_pad, len, NULL, 0, NULL, actx->plat.s390x.fc, kma);
71        actx->plat.s390x.hsflag = S390X_KMA_HS; /* The hash subkey is set */
72
73        /* Copy the 128 bit GHASH result into J0 and clear the tag */
74        kma->j0.g[0] = kma->t.g[0];
75        kma->j0.g[1] = kma->t.g[1];
76        kma->t.g[0] = 0;
77        kma->t.g[1] = 0;
78        /* Set the 32 bit counter */
79        kma->cv.w = kma->j0.w[3];
80    }
81    return 1;
82}
83
84static int s390x_aes_gcm_cipher_final(PROV_GCM_CTX *ctx, unsigned char *tag)
85{
86    PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
87    S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma;
88    unsigned char out[AES_BLOCK_SIZE];
89    unsigned int fc;
90    int rc;
91
92    kma->taadl <<= 3;
93    kma->tpcl <<= 3;
94    fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD | S390X_KMA_LPC;
95    s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen,
96              actx->plat.s390x.mres, actx->plat.s390x.mreslen, out,
97              fc, kma);
98
99    /* gctx->mres already returned to the caller */
100    OPENSSL_cleanse(out, actx->plat.s390x.mreslen);
101
102    if (ctx->enc) {
103        ctx->taglen = GCM_TAG_MAX_SIZE;
104        memcpy(tag, kma->t.b, ctx->taglen);
105        rc = 1;
106    } else {
107        rc = (CRYPTO_memcmp(tag, kma->t.b, ctx->taglen) == 0);
108    }
109    return rc;
110}
111
112static int s390x_aes_gcm_one_shot(PROV_GCM_CTX *ctx,
113                                  unsigned char *aad, size_t aad_len,
114                                  const unsigned char *in, size_t in_len,
115                                  unsigned char *out,
116                                  unsigned char *tag, size_t taglen)
117{
118    PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
119    S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma;
120    unsigned int fc;
121    int rc;
122
123    kma->taadl = aad_len << 3;
124    kma->tpcl = in_len << 3;
125    fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD | S390X_KMA_LPC;
126    s390x_kma(aad, aad_len, in, in_len, out, fc, kma);
127
128    if (ctx->enc) {
129        memcpy(tag, kma->t.b, taglen);
130        rc = 1;
131    } else {
132        rc = (CRYPTO_memcmp(tag, kma->t.b, taglen) == 0);
133    }
134    return rc;
135}
136
137/*
138 * Process additional authenticated data. Returns 1 on success. Code is
139 * big-endian.
140 */
141static int s390x_aes_gcm_aad_update(PROV_GCM_CTX *ctx,
142                                    const unsigned char *aad, size_t len)
143{
144    PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
145    S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma;
146    unsigned long long alen;
147    unsigned int fc;
148    int n, rem;
149
150    /* If already processed pt/ct then error */
151    if (kma->tpcl != 0)
152        return 0;
153
154    /* update the total aad length */
155    alen = kma->taadl + len;
156    if (alen > (U64(1) << 61) || (sizeof(len) == 8 && alen < len))
157        return 0;
158    kma->taadl = alen;
159
160    /* check if there is any existing aad data from a previous add */
161    n = actx->plat.s390x.areslen;
162    if (n) {
163        /* add additional data to a buffer until it has 16 bytes */
164        while (n && len) {
165            actx->plat.s390x.ares[n] = *aad;
166            ++aad;
167            --len;
168            n = (n + 1) & 0xf;
169        }
170        /* ctx->ares contains a complete block if offset has wrapped around */
171        if (!n) {
172            fc = S390X_gcm_fc(actx, ctx);
173            s390x_kma(actx->plat.s390x.ares, 16, NULL, 0, NULL, fc, kma);
174            actx->plat.s390x.hsflag = S390X_KMA_HS;
175        }
176        actx->plat.s390x.areslen = n;
177    }
178
179    /* If there are leftover bytes (< 128 bits) save them for next time */
180    rem = len & 0xf;
181    /* Add any remaining 16 byte blocks (128 bit each) */
182    len &= ~(size_t)0xf;
183    if (len) {
184        fc = S390X_gcm_fc(actx, ctx);
185        s390x_kma(aad, len, NULL, 0, NULL, fc, kma);
186        actx->plat.s390x.hsflag = S390X_KMA_HS;
187        aad += len;
188    }
189
190    if (rem) {
191        actx->plat.s390x.areslen = rem;
192
193        do {
194            --rem;
195            actx->plat.s390x.ares[rem] = aad[rem];
196        } while (rem);
197    }
198    return 1;
199}
200
201/*-
202 * En/de-crypt plain/cipher-text and authenticate ciphertext. Returns 1 for
203 * success. Code is big-endian.
204 */
205static int s390x_aes_gcm_cipher_update(PROV_GCM_CTX *ctx,
206                                       const unsigned char *in, size_t len,
207                                       unsigned char *out)
208{
209    PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
210    S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma;
211    const unsigned char *inptr;
212    unsigned long long mlen;
213    unsigned int fc;
214    union {
215        unsigned int w[4];
216        unsigned char b[16];
217    } buf;
218    size_t inlen;
219    int n, rem, i;
220
221    mlen = kma->tpcl + len;
222    if (mlen > ((U64(1) << 36) - 32) || (sizeof(len) == 8 && mlen < len))
223        return 0;
224    kma->tpcl = mlen;
225
226    fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD;
227    n = actx->plat.s390x.mreslen;
228    if (n) {
229        inptr = in;
230        inlen = len;
231        while (n && inlen) {
232            actx->plat.s390x.mres[n] = *inptr;
233            n = (n + 1) & 0xf;
234            ++inptr;
235            --inlen;
236        }
237        /* ctx->mres contains a complete block if offset has wrapped around */
238        if (!n) {
239            s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen,
240                      actx->plat.s390x.mres, 16, buf.b, fc, kma);
241            actx->plat.s390x.hsflag = S390X_KMA_HS;
242            fc |= S390X_KMA_HS;
243            actx->plat.s390x.areslen = 0;
244
245            /* previous call already encrypted/decrypted its remainder,
246             * see comment below */
247            n = actx->plat.s390x.mreslen;
248            while (n) {
249                *out = buf.b[n];
250                n = (n + 1) & 0xf;
251                ++out;
252                ++in;
253                --len;
254            }
255            actx->plat.s390x.mreslen = 0;
256        }
257    }
258
259    rem = len & 0xf;
260
261    len &= ~(size_t)0xf;
262    if (len) {
263        s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen, in, len, out,
264                  fc, kma);
265        in += len;
266        out += len;
267        actx->plat.s390x.hsflag = S390X_KMA_HS;
268        actx->plat.s390x.areslen = 0;
269    }
270
271    /*-
272     * If there is a remainder, it has to be saved such that it can be
273     * processed by kma later. However, we also have to do the for-now
274     * unauthenticated encryption/decryption part here and now...
275     */
276    if (rem) {
277        if (!actx->plat.s390x.mreslen) {
278            buf.w[0] = kma->j0.w[0];
279            buf.w[1] = kma->j0.w[1];
280            buf.w[2] = kma->j0.w[2];
281            buf.w[3] = kma->cv.w + 1;
282            s390x_km(buf.b, 16, actx->plat.s390x.kres,
283                     fc & 0x1f, &kma->k);
284        }
285
286        n = actx->plat.s390x.mreslen;
287        for (i = 0; i < rem; i++) {
288            actx->plat.s390x.mres[n + i] = in[i];
289            out[i] = in[i] ^ actx->plat.s390x.kres[n + i];
290        }
291        actx->plat.s390x.mreslen += rem;
292    }
293    return 1;
294}
295
296static const PROV_GCM_HW s390x_aes_gcm = {
297    s390x_aes_gcm_initkey,
298    s390x_aes_gcm_setiv,
299    s390x_aes_gcm_aad_update,
300    s390x_aes_gcm_cipher_update,
301    s390x_aes_gcm_cipher_final,
302    s390x_aes_gcm_one_shot
303};
304
305const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits)
306{
307    if ((keybits == 128 && S390X_aes_128_gcm_CAPABLE)
308         || (keybits == 192 && S390X_aes_192_gcm_CAPABLE)
309         || (keybits == 256 && S390X_aes_256_gcm_CAPABLE))
310        return &s390x_aes_gcm;
311    return &aes_gcm;
312}
313