1/*
2 * Copyright 2024 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#include "crypto/s390x_arch.h"
11
12static OSSL_FUNC_cipher_encrypt_init_fn s390x_aes_xts_einit;
13static OSSL_FUNC_cipher_decrypt_init_fn s390x_aes_xts_dinit;
14static OSSL_FUNC_cipher_cipher_fn s390x_aes_xts_cipher;
15static OSSL_FUNC_cipher_dupctx_fn s390x_aes_xts_dupctx;
16
17static int s390x_aes_xts_init(void *vctx, const unsigned char *key,
18                              size_t keylen, const unsigned char *iv,
19                              size_t ivlen, const OSSL_PARAM params[],
20                              unsigned int dec)
21{
22    PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)vctx;
23    S390X_KM_XTS_PARAMS *km = &xctx->plat.s390x.param.km;
24    unsigned int fc, offs;
25
26    switch (xctx->base.keylen) {
27    case 128 / 8 * 2:
28        fc = S390X_XTS_AES_128_MSA10;
29        offs = 32;
30        break;
31    case 256 / 8 * 2:
32        fc = S390X_XTS_AES_256_MSA10;
33        offs = 0;
34        break;
35    default:
36        goto not_supported;
37    }
38
39    if (!(OPENSSL_s390xcap_P.km[1] && S390X_CAPBIT(fc)))
40        goto not_supported;
41
42    if (iv != NULL) {
43        if (ivlen != xctx->base.ivlen
44                || ivlen > sizeof(km->tweak)) {
45            ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
46            return 0;
47        }
48        memcpy(km->tweak, iv, ivlen);
49        xctx->plat.s390x.iv_set = 1;
50    }
51
52    if (key != NULL) {
53        if (keylen != xctx->base.keylen) {
54            ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
55            return 0;
56        }
57        if (!aes_xts_check_keys_differ(key, keylen / 2, !dec))
58            return 0;
59
60        memcpy(km->key + offs, key, keylen);
61        xctx->plat.s390x.key_set = 1;
62    }
63
64    xctx->plat.s390x.fc = fc | dec;
65    xctx->plat.s390x.offset = offs;
66
67    memset(km->nap, 0, sizeof(km->nap));
68    km->nap[0] = 0x1;
69
70    return aes_xts_set_ctx_params(xctx, params);
71
72not_supported:
73    xctx->plat.s390x.fc = 0;
74    xctx->plat.s390x.offset = 0;
75    return 0;
76}
77
78static int s390x_aes_xts_einit(void *vctx, const unsigned char *key,
79                               size_t keylen, const unsigned char *iv,
80                               size_t ivlen, const OSSL_PARAM params[])
81{
82    return s390x_aes_xts_init(vctx, key, keylen, iv, ivlen, params, 0);
83}
84
85static int s390x_aes_xts_dinit(void *vctx, const unsigned char *key,
86                               size_t keylen, const unsigned char *iv,
87                               size_t ivlen, const OSSL_PARAM params[])
88{
89    return s390x_aes_xts_init(vctx, key, keylen, iv, ivlen, params,
90                              S390X_DECRYPT);
91}
92
93static void *s390x_aes_xts_dupctx(void *vctx)
94{
95    PROV_AES_XTS_CTX *in = (PROV_AES_XTS_CTX *)vctx;
96    PROV_AES_XTS_CTX *ret = OPENSSL_zalloc(sizeof(*in));
97
98    if (ret != NULL)
99        *ret = *in;
100
101    return ret;
102}
103
104static int s390x_aes_xts_cipher(void *vctx, unsigned char *out, size_t *outl,
105                                size_t outsize, const unsigned char *in,
106                                size_t inl)
107{
108    PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)vctx;
109    S390X_KM_XTS_PARAMS *km = &xctx->plat.s390x.param.km;
110    unsigned char *param = (unsigned char *)km + xctx->plat.s390x.offset;
111    unsigned int fc = xctx->plat.s390x.fc;
112    unsigned char tmp[2][AES_BLOCK_SIZE];
113    unsigned char nap_n1[AES_BLOCK_SIZE];
114    unsigned char drop[AES_BLOCK_SIZE];
115    size_t len_incomplete, len_complete;
116
117    if (!ossl_prov_is_running()
118            || inl < AES_BLOCK_SIZE
119            || in == NULL
120            || out == NULL
121            || !xctx->plat.s390x.iv_set
122            || !xctx->plat.s390x.key_set)
123        return 0;
124
125    /*
126     * Impose a limit of 2^20 blocks per data unit as specified by
127     * IEEE Std 1619-2018.  The earlier and obsolete IEEE Std 1619-2007
128     * indicated that this was a SHOULD NOT rather than a MUST NOT.
129     * NIST SP 800-38E mandates the same limit.
130     */
131    if (inl > XTS_MAX_BLOCKS_PER_DATA_UNIT * AES_BLOCK_SIZE) {
132        ERR_raise(ERR_LIB_PROV, PROV_R_XTS_DATA_UNIT_IS_TOO_LARGE);
133        return 0;
134    }
135
136    len_incomplete = inl % AES_BLOCK_SIZE;
137    len_complete = (len_incomplete == 0) ? inl :
138                       (inl / AES_BLOCK_SIZE - 1) * AES_BLOCK_SIZE;
139
140    if (len_complete > 0)
141        s390x_km(in, len_complete, out, fc, param);
142    if (len_incomplete == 0)
143       goto out;
144
145    memcpy(tmp, in + len_complete, AES_BLOCK_SIZE + len_incomplete);
146    /* swap NAP for decrypt */
147    if (fc & S390X_DECRYPT) {
148        memcpy(nap_n1, km->nap, AES_BLOCK_SIZE);
149        s390x_km(tmp[0], AES_BLOCK_SIZE, drop, fc, param);
150    }
151    s390x_km(tmp[0], AES_BLOCK_SIZE, tmp[0], fc, param);
152    if (fc & S390X_DECRYPT)
153        memcpy(km->nap, nap_n1, AES_BLOCK_SIZE);
154
155    memcpy(tmp[1] + len_incomplete, tmp[0] + len_incomplete,
156           AES_BLOCK_SIZE - len_incomplete);
157    s390x_km(tmp[1], AES_BLOCK_SIZE, out + len_complete, fc, param);
158    memcpy(out + len_complete + AES_BLOCK_SIZE, tmp[0], len_incomplete);
159
160    /* do not expose temporary data */
161    OPENSSL_cleanse(tmp, sizeof(tmp));
162out:
163    memcpy(xctx->base.iv, km->tweak, AES_BLOCK_SIZE);
164    *outl = inl;
165
166    return 1;
167}
168