xref: /optee_os/core/crypto/aes-cts.c (revision 5a913ee74d3c71af2a2860ce8a4e7aeab2916f9b)
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 <tee_api_types.h>
11 #include <tee/tee_cryp_utl.h>
12 #include <util.h>
13 
14 
15 /* From libtomcrypt doc:
16  *	Ciphertext stealing is a method of dealing with messages
17  *	in CBC mode which are not a multiple of the block
18  *	length.  This is accomplished by encrypting the last
19  *	ciphertext block in ECB mode, and XOR'ing the output
20  *	against the last partial block of plaintext. LibTomCrypt
21  *	does not support this mode directly but it is fairly
22  *	easy to emulate with a call to the cipher's
23  *	ecb encrypt() callback function.
24  *	The more sane way to deal with partial blocks is to pad
25  *	them with zeroes, and then use CBC normally
26  */
27 
28 /*
29  * From Global Platform: CTS = CBC-CS3
30  */
31 
32 struct cts_ctx {
33 	struct crypto_cipher_ctx ctx;
34 	struct crypto_cipher_ctx *ecb;
35 	struct crypto_cipher_ctx *cbc;
36 	TEE_OperationMode mode;
37 };
38 
39 static const struct crypto_cipher_ops cts_ops;
40 
41 static struct cts_ctx *to_cts_ctx(struct crypto_cipher_ctx *ctx)
42 {
43 	assert(ctx && ctx->ops == &cts_ops);
44 
45 	return container_of(ctx, struct cts_ctx, ctx);
46 }
47 
48 static TEE_Result cts_init(struct crypto_cipher_ctx *ctx,
49 			   TEE_OperationMode mode, const uint8_t *key1,
50 			   size_t key1_len, const uint8_t *key2,
51 			   size_t key2_len, const uint8_t *iv, size_t iv_len)
52 {
53 	TEE_Result res = TEE_SUCCESS;
54 	struct cts_ctx *c = to_cts_ctx(ctx);
55 
56 	c->mode = mode;
57 
58 	res = crypto_cipher_init(c->ecb, TEE_ALG_AES_ECB_NOPAD, mode, key1,
59 				 key1_len, key2, key2_len, iv, iv_len);
60 	if (res)
61 		return res;
62 
63 	return crypto_cipher_init(c->cbc, TEE_ALG_AES_CBC_NOPAD, mode, key1,
64 				  key1_len, key2, key2_len, iv, iv_len);
65 }
66 
67 static TEE_Result cts_update(struct crypto_cipher_ctx *ctx, bool last_block,
68 			     const uint8_t *data, size_t len, uint8_t *dst)
69 {
70 	struct cts_ctx *c = to_cts_ctx(ctx);
71 
72 	return tee_aes_cbc_cts_update(c->cbc, c->ecb, c->mode, last_block,
73 				      data, len, dst);
74 }
75 
76 static void cts_final(struct crypto_cipher_ctx *ctx)
77 {
78 	struct cts_ctx *c = to_cts_ctx(ctx);
79 
80 	crypto_cipher_final(c->cbc, TEE_ALG_AES_CBC_NOPAD);
81 	crypto_cipher_final(c->ecb, TEE_ALG_AES_ECB_NOPAD);
82 }
83 
84 static void cts_free_ctx(struct crypto_cipher_ctx *ctx)
85 {
86 	struct cts_ctx *c = to_cts_ctx(ctx);
87 
88 	crypto_cipher_free_ctx(c->cbc, TEE_ALG_AES_CBC_NOPAD);
89 	crypto_cipher_free_ctx(c->ecb, TEE_ALG_AES_ECB_NOPAD);
90 	free(c);
91 }
92 
93 static void cts_copy_state(struct crypto_cipher_ctx *dst_ctx,
94 			   struct crypto_cipher_ctx *src_ctx)
95 {
96 	struct cts_ctx *src = to_cts_ctx(src_ctx);
97 	struct cts_ctx *dst = to_cts_ctx(dst_ctx);
98 
99 	crypto_cipher_copy_state(dst->cbc, src->cbc, TEE_ALG_AES_CBC_NOPAD);
100 	crypto_cipher_copy_state(dst->ecb, src->ecb, TEE_ALG_AES_ECB_NOPAD);
101 }
102 
103 static const struct crypto_cipher_ops cts_ops = {
104 	.init = cts_init,
105 	.update = cts_update,
106 	.final = cts_final,
107 	.free_ctx = cts_free_ctx,
108 	.copy_state = cts_copy_state,
109 };
110 
111 TEE_Result crypto_aes_cts_alloc_ctx(struct crypto_cipher_ctx **ctx)
112 {
113 	TEE_Result res = TEE_SUCCESS;
114 	struct cts_ctx *c = calloc(1, sizeof(*c));
115 
116 	if (!c)
117 		return TEE_ERROR_OUT_OF_MEMORY;
118 
119 	res = crypto_aes_ecb_alloc_ctx(&c->ecb);
120 	if (res)
121 		goto err;
122 	res = crypto_aes_cbc_alloc_ctx(&c->cbc);
123 	if (res)
124 		goto err;
125 
126 	c->ctx.ops = &cts_ops;
127 	*ctx = &c->ctx;
128 
129 	return TEE_SUCCESS;
130 err:
131 	crypto_cipher_free_ctx(c->ecb, TEE_ALG_AES_ECB_NOPAD);
132 	free(c);
133 
134 	return res;
135 }
136