1321936Shselasky/*
2321936Shselasky * Copyright 2001-2020 The OpenSSL Project Authors. All Rights Reserved.
3321936Shselasky *
4321936Shselasky * Licensed under the Apache License 2.0 (the "License").  You may not use
5321936Shselasky * this file except in compliance with the License.  You can obtain a copy
6321936Shselasky * in the file LICENSE in the source distribution or at
7321936Shselasky * https://www.openssl.org/source/license.html
8321936Shselasky */
9321936Shselasky
10321936Shselasky/*-
11321936Shselasky * S390X support for AES CCM.
12321936Shselasky * This file is included by cipher_aes_ccm_hw.c
13321936Shselasky */
14321936Shselasky
15321936Shselasky#define S390X_CCM_AAD_FLAG 0x40
16321936Shselasky
17321936Shselaskystatic int s390x_aes_ccm_initkey(PROV_CCM_CTX *ctx,
18321936Shselasky                                 const unsigned char *key, size_t keylen)
19321936Shselasky{
20321936Shselasky    PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx;
21321936Shselasky
22321936Shselasky    sctx->ccm.s390x.fc = S390X_AES_FC(keylen);
23321936Shselasky    memcpy(&sctx->ccm.s390x.kmac.k, key, keylen);
24321936Shselasky    /* Store encoded m and l. */
25321936Shselasky    sctx->ccm.s390x.nonce.b[0] = ((ctx->l - 1) & 0x7)
26321936Shselasky                                | (((ctx->m - 2) >> 1) & 0x7) << 3;
27321936Shselasky    memset(sctx->ccm.s390x.nonce.b + 1, 0, sizeof(sctx->ccm.s390x.nonce.b));
28321936Shselasky    sctx->ccm.s390x.blocks = 0;
29321936Shselasky    ctx->key_set = 1;
30321936Shselasky    return 1;
31321936Shselasky}
32321936Shselasky
33321936Shselaskystatic int s390x_aes_ccm_setiv(PROV_CCM_CTX *ctx,
34321936Shselasky                               const unsigned char *nonce, size_t noncelen,
35321936Shselasky                               size_t mlen)
36321936Shselasky{
37321936Shselasky    PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx;
38321936Shselasky
39321936Shselasky    sctx->ccm.s390x.nonce.b[0] &= ~S390X_CCM_AAD_FLAG;
40321936Shselasky    sctx->ccm.s390x.nonce.g[1] = mlen;
41321936Shselasky    memcpy(sctx->ccm.s390x.nonce.b + 1, nonce, 15 - ctx->l);
42321936Shselasky    return 1;
43321936Shselasky}
44321936Shselasky
45321936Shselasky/*-
46321936Shselasky * Process additional authenticated data. Code is big-endian.
47321936Shselasky */
48321936Shselaskystatic int s390x_aes_ccm_setaad(PROV_CCM_CTX *ctx,
49321936Shselasky                                const unsigned char *aad, size_t alen)
50321936Shselasky{
51321936Shselasky    PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx;
52321936Shselasky    unsigned char *ptr;
53321936Shselasky    int i, rem;
54321936Shselasky
55321936Shselasky    if (!alen)
56321936Shselasky        return 1;
57321936Shselasky
58321936Shselasky    sctx->ccm.s390x.nonce.b[0] |= S390X_CCM_AAD_FLAG;
59321936Shselasky
60321936Shselasky    /* Suppress 'type-punned pointer dereference' warning. */
61321936Shselasky    ptr = sctx->ccm.s390x.buf.b;
62321936Shselasky
63321936Shselasky    if (alen < ((1 << 16) - (1 << 8))) {
64321936Shselasky        *(uint16_t *)ptr = alen;
65321936Shselasky        i = 2;
66321936Shselasky    } else if (sizeof(alen) == 8
67321936Shselasky               && alen >= (size_t)1 << (32 % (sizeof(alen) * 8))) {
68321936Shselasky        *(uint16_t *)ptr = 0xffff;
69321936Shselasky        *(uint64_t *)(ptr + 2) = alen;
70321936Shselasky        i = 10;
71321936Shselasky    } else {
72321936Shselasky        *(uint16_t *)ptr = 0xfffe;
73321936Shselasky        *(uint32_t *)(ptr + 2) = alen;
74321936Shselasky        i = 6;
75321936Shselasky    }
76321936Shselasky
77321936Shselasky    while (i < 16 && alen) {
78321936Shselasky        sctx->ccm.s390x.buf.b[i] = *aad;
79321936Shselasky        ++aad;
80321936Shselasky        --alen;
81321936Shselasky        ++i;
82321936Shselasky    }
83321936Shselasky    while (i < 16) {
84321936Shselasky        sctx->ccm.s390x.buf.b[i] = 0;
85321936Shselasky        ++i;
86321936Shselasky    }
87321936Shselasky
88321936Shselasky    sctx->ccm.s390x.kmac.icv.g[0] = 0;
89321936Shselasky    sctx->ccm.s390x.kmac.icv.g[1] = 0;
90321936Shselasky    s390x_kmac(sctx->ccm.s390x.nonce.b, 32, sctx->ccm.s390x.fc,
91321936Shselasky               &sctx->ccm.s390x.kmac);
92321936Shselasky    sctx->ccm.s390x.blocks += 2;
93321936Shselasky
94321936Shselasky    rem = alen & 0xf;
95321936Shselasky    alen &= ~(size_t)0xf;
96321936Shselasky    if (alen) {
97321936Shselasky        s390x_kmac(aad, alen, sctx->ccm.s390x.fc, &sctx->ccm.s390x.kmac);
98321936Shselasky        sctx->ccm.s390x.blocks += alen >> 4;
99321936Shselasky        aad += alen;
100321936Shselasky    }
101321936Shselasky    if (rem) {
102321936Shselasky        for (i = 0; i < rem; i++)
103321936Shselasky            sctx->ccm.s390x.kmac.icv.b[i] ^= aad[i];
104321936Shselasky
105321936Shselasky        s390x_km(sctx->ccm.s390x.kmac.icv.b, 16,
106321936Shselasky                 sctx->ccm.s390x.kmac.icv.b, sctx->ccm.s390x.fc,
107321936Shselasky                 sctx->ccm.s390x.kmac.k);
108321936Shselasky        sctx->ccm.s390x.blocks++;
109321936Shselasky    }
110321936Shselasky    return 1;
111321936Shselasky}
112321936Shselasky
113321936Shselasky/*-
114321936Shselasky * En/de-crypt plain/cipher-text. Compute tag from plaintext. Returns 1 for
115321936Shselasky * success.
116321936Shselasky */
117321936Shselaskystatic int s390x_aes_ccm_auth_encdec(PROV_CCM_CTX *ctx,
118321936Shselasky                                     const unsigned char *in,
119321936Shselasky                                     unsigned char *out, size_t len, int enc)
120321936Shselasky{
121321936Shselasky    PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx;
122321936Shselasky    size_t n, rem;
123321936Shselasky    unsigned int i, l, num;
124321936Shselasky    unsigned char flags;
125321936Shselasky
126321936Shselasky    flags = sctx->ccm.s390x.nonce.b[0];
127321936Shselasky    if (!(flags & S390X_CCM_AAD_FLAG)) {
128321936Shselasky        s390x_km(sctx->ccm.s390x.nonce.b, 16, sctx->ccm.s390x.kmac.icv.b,
129321936Shselasky                 sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k);
130321936Shselasky        sctx->ccm.s390x.blocks++;
131321936Shselasky    }
132321936Shselasky    l = flags & 0x7;
133321936Shselasky    sctx->ccm.s390x.nonce.b[0] = l;
134321936Shselasky
135321936Shselasky    /*-
136321936Shselasky     * Reconstruct length from encoded length field
137321936Shselasky     * and initialize it with counter value.
138321936Shselasky     */
139321936Shselasky    n = 0;
140321936Shselasky    for (i = 15 - l; i < 15; i++) {
141321936Shselasky        n |= sctx->ccm.s390x.nonce.b[i];
142321936Shselasky        sctx->ccm.s390x.nonce.b[i] = 0;
143321936Shselasky        n <<= 8;
144321936Shselasky    }
145321936Shselasky    n |= sctx->ccm.s390x.nonce.b[15];
146321936Shselasky    sctx->ccm.s390x.nonce.b[15] = 1;
147321936Shselasky
148321936Shselasky    if (n != len)
149321936Shselasky        return 0;      /* length mismatch */
150321936Shselasky
151321936Shselasky    if (enc) {
152321936Shselasky        /* Two operations per block plus one for tag encryption */
153321936Shselasky        sctx->ccm.s390x.blocks += (((len + 15) >> 4) << 1) + 1;
154321936Shselasky        if (sctx->ccm.s390x.blocks > (1ULL << 61))
155321936Shselasky            return 0;      /* too much data */
156321936Shselasky    }
157321936Shselasky
158321936Shselasky    num = 0;
159321936Shselasky    rem = len & 0xf;
160321936Shselasky    len &= ~(size_t)0xf;
161321936Shselasky
162321936Shselasky    if (enc) {
163321936Shselasky        /* mac-then-encrypt */
164321936Shselasky        if (len)
165321936Shselasky            s390x_kmac(in, len, sctx->ccm.s390x.fc, &sctx->ccm.s390x.kmac);
166321936Shselasky        if (rem) {
167321936Shselasky            for (i = 0; i < rem; i++)
168321936Shselasky                sctx->ccm.s390x.kmac.icv.b[i] ^= in[len + i];
169321936Shselasky
170321936Shselasky            s390x_km(sctx->ccm.s390x.kmac.icv.b, 16,
171321936Shselasky                     sctx->ccm.s390x.kmac.icv.b,
172321936Shselasky                     sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k);
173321936Shselasky        }
174321936Shselasky
175321936Shselasky        CRYPTO_ctr128_encrypt_ctr32(in, out, len + rem, &sctx->ccm.ks.ks,
176321936Shselasky                                    sctx->ccm.s390x.nonce.b, sctx->ccm.s390x.buf.b,
177321936Shselasky                                    &num, (ctr128_f)AES_ctr32_encrypt);
178321936Shselasky    } else {
179321936Shselasky        /* decrypt-then-mac */
180321936Shselasky        CRYPTO_ctr128_encrypt_ctr32(in, out, len + rem, &sctx->ccm.ks.ks,
181321936Shselasky                                    sctx->ccm.s390x.nonce.b, sctx->ccm.s390x.buf.b,
182321936Shselasky                                    &num, (ctr128_f)AES_ctr32_encrypt);
183321936Shselasky
184321936Shselasky        if (len)
185321936Shselasky            s390x_kmac(out, len, sctx->ccm.s390x.fc, &sctx->ccm.s390x.kmac);
186321936Shselasky        if (rem) {
187321936Shselasky            for (i = 0; i < rem; i++)
188321936Shselasky                sctx->ccm.s390x.kmac.icv.b[i] ^= out[len + i];
189321936Shselasky
190321936Shselasky            s390x_km(sctx->ccm.s390x.kmac.icv.b, 16,
191321936Shselasky                     sctx->ccm.s390x.kmac.icv.b,
192321936Shselasky                     sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k);
193321936Shselasky        }
194321936Shselasky    }
195321936Shselasky    /* encrypt tag */
196321936Shselasky    for (i = 15 - l; i < 16; i++)
197321936Shselasky        sctx->ccm.s390x.nonce.b[i] = 0;
198321936Shselasky
199321936Shselasky    s390x_km(sctx->ccm.s390x.nonce.b, 16, sctx->ccm.s390x.buf.b,
200321936Shselasky             sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k);
201321936Shselasky    sctx->ccm.s390x.kmac.icv.g[0] ^= sctx->ccm.s390x.buf.g[0];
202321936Shselasky    sctx->ccm.s390x.kmac.icv.g[1] ^= sctx->ccm.s390x.buf.g[1];
203321936Shselasky
204321936Shselasky    sctx->ccm.s390x.nonce.b[0] = flags;    /* restore flags field */
205321936Shselasky    return 1;
206321936Shselasky}
207321936Shselasky
208321936Shselasky
209321936Shselaskystatic int s390x_aes_ccm_gettag(PROV_CCM_CTX *ctx,
210321936Shselasky                                unsigned char *tag, size_t tlen)
211321936Shselasky{
212321936Shselasky    PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx;
213321936Shselasky
214321936Shselasky    if (tlen > ctx->m)
215321936Shselasky        return 0;
216321936Shselasky    memcpy(tag, sctx->ccm.s390x.kmac.icv.b, tlen);
217321936Shselasky    return 1;
218321936Shselasky}
219321936Shselasky
220321936Shselaskystatic int s390x_aes_ccm_auth_encrypt(PROV_CCM_CTX *ctx,
221321936Shselasky                                      const unsigned char *in,
222321936Shselasky                                      unsigned char *out, size_t len,
223321936Shselasky                                      unsigned char *tag, size_t taglen)
224321936Shselasky{
225321936Shselasky    int rv;
226321936Shselasky
227321936Shselasky    rv = s390x_aes_ccm_auth_encdec(ctx, in, out, len, 1);
228321936Shselasky    if (rv && tag != NULL)
229321936Shselasky        rv = s390x_aes_ccm_gettag(ctx, tag, taglen);
230321936Shselasky    return rv;
231321936Shselasky}
232321936Shselasky
233321936Shselaskystatic int s390x_aes_ccm_auth_decrypt(PROV_CCM_CTX *ctx,
234321936Shselasky                                      const unsigned char *in,
235321936Shselasky                                      unsigned char *out, size_t len,
236321936Shselasky                                      unsigned char *expected_tag,
237321936Shselasky                                      size_t taglen)
238321936Shselasky{
239321936Shselasky    int rv = 0;
240321936Shselasky    PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx;
241321936Shselasky
242321936Shselasky    rv = s390x_aes_ccm_auth_encdec(ctx, in, out, len, 0);
243321936Shselasky    if (rv) {
244321936Shselasky        if (CRYPTO_memcmp(sctx->ccm.s390x.kmac.icv.b, expected_tag, ctx->m) != 0)
245321936Shselasky            rv = 0;
246321936Shselasky    }
247321936Shselasky    if (rv == 0)
248321936Shselasky        OPENSSL_cleanse(out, len);
249321936Shselasky    return rv;
250321936Shselasky}
251321936Shselasky
252321936Shselaskystatic const PROV_CCM_HW s390x_aes_ccm = {
253321936Shselasky    s390x_aes_ccm_initkey,
254321936Shselasky    s390x_aes_ccm_setiv,
255321936Shselasky    s390x_aes_ccm_setaad,
256321936Shselasky    s390x_aes_ccm_auth_encrypt,
257321936Shselasky    s390x_aes_ccm_auth_decrypt,
258321936Shselasky    s390x_aes_ccm_gettag
259321936Shselasky};
260321936Shselasky
261321936Shselaskyconst PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits)
262321936Shselasky{
263321936Shselasky    if ((keybits == 128 && S390X_aes_128_ccm_CAPABLE)
264321936Shselasky         || (keybits == 192 && S390X_aes_192_ccm_CAPABLE)
265321936Shselasky         || (keybits == 256 && S390X_aes_256_ccm_CAPABLE))
266321936Shselasky        return &s390x_aes_ccm;
267321936Shselasky    return &aes_ccm;
268321936Shselasky}
269321936Shselasky