1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright 2018-2021 NXP
4  *
5  * Implementation of Cipher XTS functions
6  */
7 #include <caam_common.h>
8 #include <caam_utils_mem.h>
9 #include <caam_utils_status.h>
10 #include <mm/core_memprot.h>
11 #include <string.h>
12 
13 #include "local.h"
14 
15 /*
16  * Galois Multiplication
17  *
18  * @buf  [in/out] buffer to multiply
19  */
do_galois_mult(struct caambuf * buf)20 static void do_galois_mult(struct caambuf *buf)
21 {
22 	size_t idx = 0;
23 	uint8_t tmp = 0;
24 	uint8_t tmptmp = 0;
25 
26 	for (idx = 0; idx < buf->length; idx++) {
27 		tmptmp = buf->data[idx] >> 7;
28 		buf->data[idx] = (buf->data[idx] << 1) | tmp;
29 		tmp = tmptmp;
30 	}
31 
32 	if (tmptmp)
33 		buf->data[0] ^= 0x87;
34 }
35 
36 /*
37  * Tweak a cipher block (XTS mode)
38  *
39  * @ctx        Cipher context
40  * @enc_tweak  [in/out] Encrypted tweak (Galois multiplication)
41  * @srcbuf     Source data to encrypt/decrypt
42  * @dstbuf     [out] Destination data encrypted/decrypted
43  * @tmp        Temporary data buffer
44  */
do_tweak_block(struct cipherdata * ctx,struct caambuf * enc_tweak,struct caambuf * srcbuf,struct caambuf * dstbuf,struct caamdmaobj * tmp)45 static TEE_Result do_tweak_block(struct cipherdata *ctx,
46 				 struct caambuf *enc_tweak,
47 				 struct caambuf *srcbuf, struct caambuf *dstbuf,
48 				 struct caamdmaobj *tmp)
49 {
50 	enum caam_status retstatus = CAAM_FAILURE;
51 	unsigned int idx = 0;
52 
53 	/*
54 	 * TODO: Optimization by using CAAM to do it with MATH op in the
55 	 * operation description
56 	 */
57 	for (idx = 0; idx < ctx->alg->size_block; idx++)
58 		tmp->orig.data[idx] = srcbuf->data[idx] ^ enc_tweak->data[idx];
59 
60 	retstatus = caam_cipher_block(ctx, false, NEED_KEY1, ctx->encrypt, tmp,
61 				      tmp);
62 
63 	if (retstatus != CAAM_NO_ERROR)
64 		return caam_status_to_tee_result(retstatus);
65 
66 	caam_dmaobj_copy_to_orig(tmp);
67 
68 	for (idx = 0; idx < ctx->alg->size_block; idx++)
69 		dstbuf->data[idx] = tmp->orig.data[idx] ^ enc_tweak->data[idx];
70 
71 	/* Galois field multiplication of the tweak */
72 	do_galois_mult(enc_tweak);
73 
74 	return TEE_SUCCESS;
75 }
76 
caam_cipher_update_xts(struct drvcrypt_cipher_update * dupdate)77 TEE_Result caam_cipher_update_xts(struct drvcrypt_cipher_update *dupdate)
78 {
79 	TEE_Result ret = TEE_ERROR_GENERIC;
80 	enum caam_status retstatus = CAAM_FAILURE;
81 	struct cipherdata *ctx = dupdate->ctx;
82 	struct caambuf tmpsrc = { };
83 	struct caamdmaobj tmpdst = { };
84 	struct caamdmaobj tweak = { };
85 	struct caamdmaobj enc_tweak = { };
86 	struct caambuf srcbuf = { };
87 	struct caambuf dstbuf = { };
88 	size_t idx = 0;
89 	size_t fullsize = 0;
90 	size_t lastblk = 0;
91 	paddr_t psrc = 0;
92 	paddr_t pdst = 0;
93 
94 	CIPHER_TRACE("Algo AES XTS length=%zu - %s", dupdate->src.length,
95 		     ctx->encrypt ? "Encrypt" : " Decrypt");
96 
97 	psrc = virt_to_phys(dupdate->src.data);
98 	pdst = virt_to_phys(dupdate->dst.data);
99 
100 	/* Check the payload/cipher physical addresses */
101 	if (!psrc || !pdst) {
102 		CIPHER_TRACE("Bad Addr (src %#" PRIxPA ") (dst %#" PRIxPA ")",
103 			     psrc, pdst);
104 		return TEE_ERROR_GENERIC;
105 	}
106 
107 	ret = caam_dmaobj_input_sgtbuf(&tweak, ctx->tweak.data,
108 				       ctx->tweak.length);
109 	if (ret)
110 		goto out;
111 
112 	/*
113 	 * First operation is to encrypt the tweak with the key #2
114 	 * Allocate the encrypted tweak buffer
115 	 */
116 	ret = caam_dmaobj_output_sgtbuf(&enc_tweak, NULL, 0, ctx->tweak.length);
117 	if (ret)
118 		goto out;
119 
120 	ret = caam_dmaobj_output_sgtbuf(&tmpdst, NULL, 0, ctx->alg->size_block);
121 	if (ret)
122 		goto out;
123 
124 	retstatus = caam_cipher_block(ctx, false, NEED_KEY2, true, &tweak,
125 				      &enc_tweak);
126 	if (retstatus != CAAM_NO_ERROR) {
127 		CIPHER_TRACE("Tweak encryption error");
128 		ret = caam_status_to_tee_result(retstatus);
129 		goto out;
130 	}
131 
132 	caam_dmaobj_copy_to_orig(&enc_tweak);
133 
134 	/*
135 	 * Encrypt or Decrypt input data.
136 	 * Check if the last block is partial or not
137 	 *  - if last block is partial, rebuild a complete
138 	 *    block using the penultimate complete block
139 	 *    encryption/decryption.
140 	 *  - else do all blocks.
141 	 */
142 
143 	/* Calculate the number of complete block */
144 	fullsize = dupdate->src.length;
145 	lastblk = fullsize % ctx->alg->size_block;
146 	fullsize -= lastblk;
147 
148 	/* One full block is needed */
149 	if (!fullsize) {
150 		ret = TEE_ERROR_BAD_PARAMETERS;
151 		goto out;
152 	}
153 
154 	if (lastblk)
155 		fullsize -= ctx->alg->size_block;
156 
157 	srcbuf.data = dupdate->src.data;
158 	srcbuf.length = ctx->alg->size_block;
159 	srcbuf.paddr = psrc;
160 
161 	dstbuf.data = dupdate->dst.data;
162 	dstbuf.length = ctx->alg->size_block;
163 	dstbuf.paddr = pdst;
164 
165 	for (; fullsize > 0; fullsize -= ctx->alg->size_block) {
166 		CIPHER_TRACE("Tweak block fullsize %zu", fullsize);
167 		ret = do_tweak_block(ctx, &enc_tweak.orig, &srcbuf, &dstbuf,
168 				     &tmpdst);
169 
170 		CIPHER_TRACE("Tweak block ret 0x%" PRIx32, ret);
171 		if (ret)
172 			goto out;
173 
174 		CIPHER_DUMPBUF("Source", srcbuf.data, srcbuf.length);
175 		CIPHER_DUMPBUF("Dest", dstbuf.data, dstbuf.length);
176 
177 		/* Increment the source and destination block */
178 		srcbuf.data += ctx->alg->size_block;
179 		srcbuf.paddr += ctx->alg->size_block;
180 
181 		dstbuf.data += ctx->alg->size_block;
182 		dstbuf.paddr += ctx->alg->size_block;
183 	}
184 
185 	if (lastblk) {
186 		CIPHER_TRACE("Last block size is %zu", lastblk);
187 
188 		/*
189 		 * Allocate the temporary buffer containing the
190 		 * penultimate block computed
191 		 */
192 		retstatus = caam_alloc_align_buf(&tmpsrc, ctx->alg->size_block);
193 		if (retstatus != CAAM_NO_ERROR) {
194 			ret = caam_status_to_tee_result(retstatus);
195 			goto out;
196 		}
197 
198 		if (!ctx->encrypt) {
199 			/*
200 			 * In case of decryption, need to multiply
201 			 * the tweak first
202 			 */
203 			memcpy(tmpsrc.data, enc_tweak.orig.data,
204 			       enc_tweak.orig.length);
205 			do_galois_mult(&tmpsrc);
206 
207 			ret = do_tweak_block(ctx, &tmpsrc, &srcbuf,
208 					     &tmpdst.orig, &tmpdst);
209 		} else {
210 			ret = do_tweak_block(ctx, &enc_tweak.orig, &srcbuf,
211 					     &tmpdst.orig, &tmpdst);
212 		}
213 
214 		CIPHER_TRACE("Tweak penultimate block ret 0x%" PRIx32, ret);
215 
216 		if (ret)
217 			goto out;
218 
219 		/* Build the last block and create the last destination block */
220 		for (idx = 0; idx < lastblk; idx++) {
221 			tmpsrc.data[idx] =
222 				srcbuf.data[ctx->alg->size_block + idx];
223 			dstbuf.data[ctx->alg->size_block + idx] =
224 				tmpdst.orig.data[idx];
225 		}
226 
227 		for (; idx < ctx->alg->size_block; idx++)
228 			tmpsrc.data[idx] = tmpdst.orig.data[idx];
229 
230 		ret = do_tweak_block(ctx, &enc_tweak.orig, &tmpsrc, &dstbuf,
231 				     &tmpdst);
232 
233 		CIPHER_TRACE("Tweak last block ret 0x%" PRIx32, ret);
234 		if (ret)
235 			goto out;
236 
237 		CIPHER_DUMPBUF("Source", tmpsrc.data, tmpsrc.length);
238 		CIPHER_DUMPBUF("Dest", dstbuf.data, dstbuf.length);
239 	}
240 
241 	/* Finalize by decrypting the tweak back */
242 	retstatus = caam_cipher_block(ctx, false, NEED_KEY2, false, &enc_tweak,
243 				      &tweak);
244 	if (retstatus != CAAM_NO_ERROR) {
245 		CIPHER_TRACE("Tweak decryption error");
246 		ret = caam_status_to_tee_result(retstatus);
247 		goto out;
248 	}
249 
250 	caam_dmaobj_copy_to_orig(&tweak);
251 
252 	ret = TEE_SUCCESS;
253 out:
254 	caam_free_buf(&tmpsrc);
255 	caam_dmaobj_free(&tmpdst);
256 	caam_dmaobj_free(&tweak);
257 	caam_dmaobj_free(&enc_tweak);
258 
259 	return ret;
260 }
261