blob: 4d710f257d96670712bbc73be38f808fc1f82e4e [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file openssl/aes.c AES (Advanced Encryption Standard) using OpenSSL
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <string.h>
7#include <openssl/aes.h>
8#include <openssl/evp.h>
9#include <openssl/err.h>
10#include <re_types.h>
11#include <re_fmt.h>
12#include <re_mem.h>
13#include <re_aes.h>
14
15
16struct aes {
17 EVP_CIPHER_CTX *ctx;
18 enum aes_mode mode;
19 bool encr;
20};
21
22
23static const EVP_CIPHER *aes_cipher(enum aes_mode mode, size_t key_bits)
24{
25 if (mode == AES_MODE_CTR) {
26
27 switch (key_bits) {
28
29 case 128: return EVP_aes_128_ctr();
30 case 192: return EVP_aes_192_ctr();
31 case 256: return EVP_aes_256_ctr();
32 default:
33 return NULL;
34 }
35 }
36 else if (mode == AES_MODE_GCM) {
37
38 switch (key_bits) {
39
40 case 128: return EVP_aes_128_gcm();
41 case 256: return EVP_aes_256_gcm();
42 default:
43 return NULL;
44 }
45 }
46 else {
47 return NULL;
48 }
49}
50
51
52static inline bool set_crypt_dir(struct aes *aes, bool encr)
53{
54 if (aes->encr != encr) {
55
56 /* update the encrypt/decrypt direction */
57 if (!EVP_CipherInit_ex(aes->ctx, NULL, NULL,
58 NULL, NULL, encr)) {
59 ERR_clear_error();
60 return false;
61 }
62
63 aes->encr = encr;
64 }
65
66 return true;
67}
68
69
70static void destructor(void *arg)
71{
72 struct aes *st = arg;
73
74#if OPENSSL_VERSION_NUMBER >= 0x10100000L
75 if (st->ctx)
76 EVP_CIPHER_CTX_free(st->ctx);
77#else
78 if (st->ctx)
79 EVP_CIPHER_CTX_cleanup(st->ctx);
80 mem_deref(st->ctx);
81#endif
82}
83
84
85int aes_alloc(struct aes **aesp, enum aes_mode mode,
86 const uint8_t *key, size_t key_bits,
87 const uint8_t *iv)
88{
89 const EVP_CIPHER *cipher;
90 struct aes *st;
91 int err = 0, r;
92
93 if (!aesp || !key)
94 return EINVAL;
95
96 cipher = aes_cipher(mode, key_bits);
97 if (!cipher)
98 return ENOTSUP;
99
100 st = mem_zalloc(sizeof(*st), destructor);
101 if (!st)
102 return ENOMEM;
103
104 st->mode = mode;
105 st->encr = true;
106
107#if OPENSSL_VERSION_NUMBER >= 0x10100000L
108 st->ctx = EVP_CIPHER_CTX_new();
109 if (!st->ctx) {
110 ERR_clear_error();
111 err = ENOMEM;
112 goto out;
113 }
114
115#else
116 st->ctx = mem_zalloc(sizeof(*st->ctx), NULL);
117 if (!st->ctx) {
118 err = ENOMEM;
119 goto out;
120 }
121
122 EVP_CIPHER_CTX_init(st->ctx);
123#endif
124
125 r = EVP_EncryptInit_ex(st->ctx, cipher, NULL, key, iv);
126 if (!r) {
127 ERR_clear_error();
128 err = EPROTO;
129 }
130
131 out:
132 if (err)
133 mem_deref(st);
134 else
135 *aesp = st;
136
137 return err;
138}
139
140
141void aes_set_iv(struct aes *aes, const uint8_t *iv)
142{
143 int r;
144
145 if (!aes || !iv)
146 return;
147
148 r = EVP_CipherInit_ex(aes->ctx, NULL, NULL, NULL, iv, -1);
149 if (!r)
150 ERR_clear_error();
151}
152
153
154int aes_encr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len)
155{
156 int c_len = (int)len;
157
158 if (!aes || !in)
159 return EINVAL;
160
161 if (!set_crypt_dir(aes, true))
162 return EPROTO;
163
164 if (!EVP_EncryptUpdate(aes->ctx, out, &c_len, in, (int)len)) {
165 ERR_clear_error();
166 return EPROTO;
167 }
168
169 return 0;
170}
171
172
173int aes_decr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len)
174{
175 int c_len = (int)len;
176
177 if (!aes || !in)
178 return EINVAL;
179
180 if (!set_crypt_dir(aes, false))
181 return EPROTO;
182
183 if (!EVP_DecryptUpdate(aes->ctx, out, &c_len, in, (int)len)) {
184 ERR_clear_error();
185 return EPROTO;
186 }
187
188 return 0;
189}
190
191
192/**
193 * Get the authentication tag for an AEAD cipher (e.g. GCM)
194 *
195 * @param aes AES Context
196 * @param tag Authentication tag
197 * @param taglen Length of Authentication tag
198 *
199 * @return 0 if success, otherwise errorcode
200 */
201int aes_get_authtag(struct aes *aes, uint8_t *tag, size_t taglen)
202{
203 int tmplen;
204
205 if (!aes || !tag || !taglen)
206 return EINVAL;
207
208 switch (aes->mode) {
209
210 case AES_MODE_GCM:
211 if (!EVP_EncryptFinal_ex(aes->ctx, NULL, &tmplen)) {
212 ERR_clear_error();
213 return EPROTO;
214 }
215
216 if (!EVP_CIPHER_CTX_ctrl(aes->ctx, EVP_CTRL_GCM_GET_TAG,
217 (int)taglen, tag)) {
218 ERR_clear_error();
219 return EPROTO;
220 }
221
222 return 0;
223
224 default:
225 return ENOTSUP;
226 }
227}
228
229
230/**
231 * Authenticate a decryption tag for an AEAD cipher (e.g. GCM)
232 *
233 * @param aes AES Context
234 * @param tag Authentication tag
235 * @param taglen Length of Authentication tag
236 *
237 * @return 0 if success, otherwise errorcode
238 *
239 * @retval EAUTH if authentication failed
240 */
241int aes_authenticate(struct aes *aes, const uint8_t *tag, size_t taglen)
242{
243 int tmplen;
244
245 if (!aes || !tag || !taglen)
246 return EINVAL;
247
248 switch (aes->mode) {
249
250 case AES_MODE_GCM:
251 if (!EVP_CIPHER_CTX_ctrl(aes->ctx, EVP_CTRL_GCM_SET_TAG,
252 (int)taglen, (void *)tag)) {
253 ERR_clear_error();
254 return EPROTO;
255 }
256
257 if (EVP_DecryptFinal_ex(aes->ctx, NULL, &tmplen) <= 0) {
258 ERR_clear_error();
259 return EAUTH;
260 }
261
262 return 0;
263
264 default:
265 return ENOTSUP;
266 }
267}