xref: /optee_os/core/crypto/aes-cts.c (revision cbda709118504f8b9b6812419d14f8360c40ad27)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2014-2019, Linaro Limited
4  */
5 
6 #include <assert.h>
7 #include <crypto/crypto.h>
8 #include <crypto/crypto_impl.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <tee_api_types.h>
12 #include <tee/tee_cryp_utl.h>
13 #include <util.h>
14 
15 
16 /* From libtomcrypt doc:
17  *	Ciphertext stealing is a method of dealing with messages
18  *	in CBC mode which are not a multiple of the block
19  *	length.  This is accomplished by encrypting the last
20  *	ciphertext block in ECB mode, and XOR'ing the output
21  *	against the last partial block of plaintext. LibTomCrypt
22  *	does not support this mode directly but it is fairly
23  *	easy to emulate with a call to the cipher's
24  *	ecb encrypt() callback function.
25  *	The more sane way to deal with partial blocks is to pad
26  *	them with zeroes, and then use CBC normally
27  */
28 
29 /*
30  * From Global Platform: CTS = CBC-CS3
31  */
32 
33 struct cts_ctx {
34 	struct crypto_cipher_ctx ctx;
35 	struct crypto_cipher_ctx *ecb;
36 	struct crypto_cipher_ctx *cbc;
37 	TEE_OperationMode mode;
38 };
39 
40 static const struct crypto_cipher_ops cts_ops;
41 
42 static struct cts_ctx *to_cts_ctx(struct crypto_cipher_ctx *ctx)
43 {
44 	assert(ctx && ctx->ops == &cts_ops);
45 
46 	return container_of(ctx, struct cts_ctx, ctx);
47 }
48 
49 static TEE_Result cts_init(struct crypto_cipher_ctx *ctx,
50 			   TEE_OperationMode mode, const uint8_t *key1,
51 			   size_t key1_len, const uint8_t *key2,
52 			   size_t key2_len, const uint8_t *iv, size_t iv_len)
53 {
54 	TEE_Result res = TEE_SUCCESS;
55 	struct cts_ctx *c = to_cts_ctx(ctx);
56 
57 	c->mode = mode;
58 
59 	res = crypto_cipher_init(c->ecb, mode, key1, key1_len, key2, key2_len,
60 				 iv, iv_len);
61 	if (res)
62 		return res;
63 
64 	return crypto_cipher_init(c->cbc, mode, key1, key1_len, key2, key2_len,
65 				  iv, iv_len);
66 }
67 
68 /*
69  * From http://en.wikipedia.org/wiki/Ciphertext_stealing
70  * CBC ciphertext stealing encryption using a standard
71  * CBC interface:
72  *	1. Pad the last partial plaintext block with 0.
73  *	2. Encrypt the whole padded plaintext using the
74  *	   standard CBC mode.
75  *	3. Swap the last two ciphertext blocks.
76  *	4. Truncate the ciphertext to the length of the
77  *	   original plaintext.
78  *
79  * CBC ciphertext stealing decryption using a standard
80  * CBC interface
81  *	1. Dn = Decrypt (K, Cn-1). Decrypt the second to last
82  *	   ciphertext block.
83  *	2. Cn = Cn || Tail (Dn, B-M). Pad the ciphertext to the
84  *	   nearest multiple of the block size using the last
85  *	   B-M bits of block cipher decryption of the
86  *	   second-to-last ciphertext block.
87  *	3. Swap the last two ciphertext blocks.
88  *	4. Decrypt the (modified) ciphertext using the standard
89  *	   CBC mode.
90  *	5. Truncate the plaintext to the length of the original
91  *	   ciphertext.
92  */
93 static TEE_Result cbc_cts_update(void *cbc_ctx, void *ecb_ctx,
94 				 TEE_OperationMode mode, bool last_block,
95 				 const uint8_t *data, size_t len, uint8_t *dst)
96 {
97 	TEE_Result res;
98 	int nb_blocks, len_last_block, block_size = 16;
99 	uint8_t tmp_block[64], tmp2_block[64];
100 
101 	if (!last_block)
102 		return tee_do_cipher_update(cbc_ctx, TEE_ALG_AES_CBC_NOPAD,
103 					     mode, last_block, data, len, dst);
104 
105 	/* Compute the last block length and check constraints */
106 	nb_blocks = ((len + block_size - 1) / block_size);
107 	if (nb_blocks < 2)
108 		return TEE_ERROR_BAD_STATE;
109 	len_last_block = len % block_size;
110 	if (len_last_block == 0)
111 		len_last_block = block_size;
112 
113 	if (mode == TEE_MODE_ENCRYPT) {
114 		memcpy(tmp_block,
115 		       data + ((nb_blocks - 1) * block_size),
116 		       len_last_block);
117 		memset(tmp_block + len_last_block,
118 		       0,
119 		       block_size - len_last_block);
120 
121 		res = tee_do_cipher_update(cbc_ctx, TEE_ALG_AES_CBC_NOPAD,
122 					   mode, 0, data,
123 					   (nb_blocks - 1) * block_size, dst);
124 		if (res != TEE_SUCCESS)
125 			return res;
126 
127 		memcpy(dst + (nb_blocks - 1) * block_size,
128 		       dst + (nb_blocks - 2) * block_size,
129 		       len_last_block);
130 
131 		res = tee_do_cipher_update(cbc_ctx, TEE_ALG_AES_CBC_NOPAD,
132 					   mode, 0, tmp_block, block_size,
133 					   dst + (nb_blocks - 2) * block_size);
134 		if (res != TEE_SUCCESS)
135 			return res;
136 	} else {
137 		/* 1. Decrypt the second to last ciphertext block */
138 		res = tee_do_cipher_update(ecb_ctx, TEE_ALG_AES_ECB_NOPAD,
139 					   mode, 0,
140 					   data + (nb_blocks - 2) * block_size,
141 					   block_size, tmp2_block);
142 		if (res != TEE_SUCCESS)
143 			return res;
144 
145 		/* 2. Cn = Cn || Tail (Dn, B-M) */
146 		memcpy(tmp_block, data + ((nb_blocks - 1) * block_size),
147 		       len_last_block);
148 		memcpy(tmp_block + len_last_block, tmp2_block + len_last_block,
149 		       block_size - len_last_block);
150 
151 		/* 3. Swap the last two ciphertext blocks */
152 		/* done by passing the correct buffers in step 4. */
153 
154 		/* 4. Decrypt the (modified) ciphertext */
155 		if (nb_blocks > 2) {
156 			res = tee_do_cipher_update(cbc_ctx,
157 						   TEE_ALG_AES_CBC_NOPAD, mode,
158 						   0, data,
159 						   (nb_blocks - 2) *
160 						   block_size, dst);
161 			if (res != TEE_SUCCESS)
162 				return res;
163 		}
164 
165 		res = tee_do_cipher_update(cbc_ctx, TEE_ALG_AES_CBC_NOPAD,
166 					   mode, 0, tmp_block, block_size,
167 					   dst +
168 					   ((nb_blocks - 2) * block_size));
169 		if (res != TEE_SUCCESS)
170 			return res;
171 
172 		res = tee_do_cipher_update(cbc_ctx, TEE_ALG_AES_CBC_NOPAD,
173 					   mode, 0, data +
174 					   ((nb_blocks - 2) * block_size),
175 					   block_size, tmp_block);
176 		if (res != TEE_SUCCESS)
177 			return res;
178 
179 		/* 5. Truncate the plaintext */
180 		memcpy(dst + (nb_blocks - 1) * block_size, tmp_block,
181 		       len_last_block);
182 	}
183 	return TEE_SUCCESS;
184 }
185 
186 static TEE_Result cts_update(struct crypto_cipher_ctx *ctx, bool last_block,
187 			     const uint8_t *data, size_t len, uint8_t *dst)
188 {
189 	struct cts_ctx *c = to_cts_ctx(ctx);
190 
191 	return cbc_cts_update(c->cbc, c->ecb, c->mode, last_block, data, len,
192 			      dst);
193 }
194 
195 static void cts_final(struct crypto_cipher_ctx *ctx)
196 {
197 	struct cts_ctx *c = to_cts_ctx(ctx);
198 
199 	crypto_cipher_final(c->cbc);
200 	crypto_cipher_final(c->ecb);
201 }
202 
203 static void cts_free_ctx(struct crypto_cipher_ctx *ctx)
204 {
205 	struct cts_ctx *c = to_cts_ctx(ctx);
206 
207 	crypto_cipher_free_ctx(c->cbc);
208 	crypto_cipher_free_ctx(c->ecb);
209 	free(c);
210 }
211 
212 static void cts_copy_state(struct crypto_cipher_ctx *dst_ctx,
213 			   struct crypto_cipher_ctx *src_ctx)
214 {
215 	struct cts_ctx *src = to_cts_ctx(src_ctx);
216 	struct cts_ctx *dst = to_cts_ctx(dst_ctx);
217 
218 	crypto_cipher_copy_state(dst->cbc, src->cbc);
219 	crypto_cipher_copy_state(dst->ecb, src->ecb);
220 }
221 
222 static const struct crypto_cipher_ops cts_ops = {
223 	.init = cts_init,
224 	.update = cts_update,
225 	.final = cts_final,
226 	.free_ctx = cts_free_ctx,
227 	.copy_state = cts_copy_state,
228 };
229 
230 TEE_Result crypto_aes_cts_alloc_ctx(struct crypto_cipher_ctx **ctx)
231 {
232 	TEE_Result res = TEE_SUCCESS;
233 	struct cts_ctx *c = calloc(1, sizeof(*c));
234 
235 	if (!c)
236 		return TEE_ERROR_OUT_OF_MEMORY;
237 
238 	res = crypto_aes_ecb_alloc_ctx(&c->ecb);
239 	if (res)
240 		goto err;
241 	res = crypto_aes_cbc_alloc_ctx(&c->cbc);
242 	if (res)
243 		goto err;
244 
245 	c->ctx.ops = &cts_ops;
246 	*ctx = &c->ctx;
247 
248 	return TEE_SUCCESS;
249 err:
250 	crypto_cipher_free_ctx(c->ecb);
251 	free(c);
252 
253 	return res;
254 }
255