test: poll_timeout; see php-memcached-dev/php-memcached#531
[awesomized/libmemcached] / src / libhashkit / aes.cc
1 /*
2 +--------------------------------------------------------------------+
3 | libmemcached-awesome - C/C++ Client Library for memcached |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted under the terms of the BSD license. |
7 | You should have received a copy of the license in a bundled file |
8 | named LICENSE; in case you did not receive a copy you can review |
9 | the terms online at: https://opensource.org/licenses/BSD-3-Clause |
10 +--------------------------------------------------------------------+
11 | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ |
12 | Copyright (c) 2020-2021 Michael Wallner https://awesome.co/ |
13 +--------------------------------------------------------------------+
14 */
15
16 #include "libhashkit/common.h"
17
18 #include <cstring>
19
20 #ifdef HAVE_OPENSSL_CRYPTO
21
22 #include <openssl/evp.h>
23
24 #define DIGEST_ROUNDS 5
25
26 #define AES_KEY_NBYTES 32
27 #define AES_IV_NBYTES 32
28
29 struct aes_key_t {
30 EVP_CIPHER_CTX *encryption_context;
31 EVP_CIPHER_CTX *decryption_context;
32 };
33
34
35 aes_key_t *aes_create_key(const char *key, const size_t key_length) {
36 unsigned char aes_key[AES_KEY_NBYTES];
37 unsigned char aes_iv[AES_IV_NBYTES];
38 const unsigned char *ukey = (const unsigned char *) key;
39
40 if (!key) {
41 return NULL;
42 }
43
44 int i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha256(), NULL, ukey, key_length, DIGEST_ROUNDS,
45 aes_key, aes_iv);
46 if (i != AES_KEY_NBYTES) {
47 return NULL;
48 }
49
50 aes_key_t *aes_ctx = (aes_key_t *) malloc(sizeof(aes_key_t));
51
52 if (!(aes_ctx->encryption_context = EVP_CIPHER_CTX_new())) {
53 return NULL;
54 }
55 if (!(aes_ctx->decryption_context = EVP_CIPHER_CTX_new())) {
56 EVP_CIPHER_CTX_free(aes_ctx->encryption_context);
57 return NULL;
58 }
59
60 EVP_CIPHER_CTX_init(aes_ctx->encryption_context);
61 EVP_CIPHER_CTX_init(aes_ctx->decryption_context);
62 if (EVP_EncryptInit_ex(aes_ctx->encryption_context, EVP_aes_256_cbc(), NULL, ukey, aes_iv) != 1
63 || EVP_DecryptInit_ex(aes_ctx->decryption_context, EVP_aes_256_cbc(), NULL, ukey, aes_iv) != 1)
64 {
65 aes_free_key(aes_ctx);
66 return NULL;
67 }
68
69 return aes_ctx;
70 }
71
72 hashkit_string_st *aes_encrypt(aes_key_t *ctx, const char *source, size_t source_length) {
73 EVP_CIPHER_CTX *encryption_context = ctx->encryption_context;
74 int cipher_length = source_length + EVP_CIPHER_CTX_block_size(encryption_context);
75 int final_length = 0;
76 const unsigned char *usource = (const unsigned char *) source;
77 unsigned char *cipher_text = (unsigned char *) malloc(cipher_length);
78 if (!cipher_text) {
79 return NULL;
80 }
81 if (EVP_EncryptInit_ex(encryption_context, NULL, NULL, NULL, NULL) != 1
82 || EVP_EncryptUpdate(encryption_context, cipher_text, &cipher_length, usource, source_length) != 1
83 || EVP_EncryptFinal_ex(encryption_context, cipher_text + cipher_length, &final_length) != 1)
84 {
85 free(cipher_text);
86 return NULL;
87 }
88
89 hashkit_string_st *destination = hashkit_string_create(cipher_length + final_length);
90 if (!destination) {
91 return NULL;
92 }
93 char *dest = hashkit_string_c_str_mutable(destination);
94 memcpy(dest, cipher_text, cipher_length + final_length);
95 hashkit_string_set_length(destination, cipher_length + final_length);
96 return destination;
97 }
98
99 hashkit_string_st *aes_decrypt(aes_key_t *ctx, const char *source, size_t source_length) {
100 EVP_CIPHER_CTX *decryption_context = ctx->decryption_context;
101 int plain_text_length = source_length;
102 int final_length = 0;
103 const unsigned char *usource = (const unsigned char *) source;
104 unsigned char *plain_text = (unsigned char *) malloc(plain_text_length);
105 if (!plain_text) {
106 return NULL;
107 }
108 if (EVP_DecryptInit_ex(decryption_context, NULL, NULL, NULL, NULL) != 1
109 || EVP_DecryptUpdate(decryption_context, plain_text, &plain_text_length, usource, source_length) != 1
110 || EVP_DecryptFinal_ex(decryption_context, plain_text + plain_text_length, &final_length) != 1)
111 {
112 free(plain_text);
113 return NULL;
114 }
115
116 hashkit_string_st *destination = hashkit_string_create(plain_text_length + final_length);
117 if (!destination) {
118 return NULL;
119 }
120 char *dest = hashkit_string_c_str_mutable(destination);
121 memcpy(dest, plain_text, plain_text_length + final_length);
122 hashkit_string_set_length(destination, plain_text_length + final_length);
123 return destination;
124 }
125
126 aes_key_t *aes_clone_key(aes_key_t *old_context) {
127 if (!old_context) {
128 return NULL;
129 }
130
131 aes_key_t *new_context = (aes_key_t *) malloc(sizeof(aes_key_t));
132 if (new_context) {
133 new_context->encryption_context = EVP_CIPHER_CTX_new();
134 new_context->decryption_context = EVP_CIPHER_CTX_new();
135 if (!new_context->encryption_context || !new_context->decryption_context) {
136 aes_free_key(new_context);
137 return NULL;
138 }
139 EVP_CIPHER_CTX_copy(new_context->encryption_context, old_context->encryption_context);
140 EVP_CIPHER_CTX_copy(new_context->decryption_context, old_context->decryption_context);
141 }
142
143 return new_context;
144 }
145
146 void aes_free_key(aes_key_t *context) {
147 if (context) {
148 if (context->encryption_context) {
149 EVP_CIPHER_CTX_free(context->encryption_context);
150 context->encryption_context = NULL;
151 }
152 if (context->decryption_context) {
153 EVP_CIPHER_CTX_free(context->decryption_context);
154 context->decryption_context = NULL;
155 }
156 free(context);
157 }
158 }
159
160 #else
161
162 # include "libhashkit/rijndael.hpp"
163
164 # define AES_KEY_LENGTH 256 /* 128, 192, 256 */
165 # define AES_BLOCK_SIZE 16
166
167 enum encrypt_t { AES_ENCRYPT, AES_DECRYPT };
168
169 struct _key_t {
170 int nr;
171 uint32_t rk[4 * (AES_MAXNR + 1)];
172 };
173
174 struct aes_key_t {
175 _key_t encode_key;
176 _key_t decode_key;
177 };
178
179 aes_key_t *aes_create_key(const char *key, const size_t key_length) {
180 aes_key_t *_aes_key = (aes_key_t *) (calloc(1, sizeof(aes_key_t)));
181 if (_aes_key) {
182 uint8_t rkey[AES_KEY_LENGTH / 8];
183 uint8_t *rkey_end = rkey + AES_KEY_LENGTH / 8;
184 const char *key_end = key + key_length;
185
186 memset(rkey, 0, sizeof(rkey)); /* Set initial key */
187
188 uint8_t *ptr = rkey;
189 const char *sptr = key;
190 for (; sptr < key_end; ptr++, sptr++) {
191 if (ptr == rkey_end) {
192 ptr = rkey; /* Just loop over tmp_key until we used all key */
193 }
194 *ptr ^= (uint8_t) (*sptr);
195 }
196
197 _aes_key->decode_key.nr = rijndaelKeySetupDec(_aes_key->decode_key.rk, rkey, AES_KEY_LENGTH);
198 _aes_key->encode_key.nr = rijndaelKeySetupEnc(_aes_key->encode_key.rk, rkey, AES_KEY_LENGTH);
199 }
200
201 return _aes_key;
202 }
203
204 aes_key_t *aes_clone_key(aes_key_t *_aes_key) {
205 if (!_aes_key) {
206 return NULL;
207 }
208
209 aes_key_t *_aes_clone_key = (aes_key_t *) (calloc(1, sizeof(aes_key_t)));
210 if (_aes_clone_key) {
211 memcpy(_aes_clone_key, _aes_key, sizeof(aes_key_t));
212 }
213
214 return _aes_clone_key;
215 }
216
217 hashkit_string_st *aes_encrypt(aes_key_t *_aes_key, const char *source, size_t source_length) {
218 if (!_aes_key) {
219 return NULL;
220 }
221
222 size_t num_blocks = source_length / AES_BLOCK_SIZE;
223
224 hashkit_string_st *destination = hashkit_string_create(source_length);
225 if (destination) {
226 char *dest = hashkit_string_c_str_mutable(destination);
227
228 for (size_t x = num_blocks; x > 0; x--) /* Encode complete blocks */ {
229 rijndaelEncrypt(_aes_key->encode_key.rk, _aes_key->encode_key.nr, (const uint8_t *) (source),
230 (uint8_t *) (dest));
231 source += AES_BLOCK_SIZE;
232 dest += AES_BLOCK_SIZE;
233 }
234
235 uint8_t block[AES_BLOCK_SIZE];
236 char pad_len = AES_BLOCK_SIZE - (source_length - AES_BLOCK_SIZE * num_blocks);
237 memcpy(block, source, 16 - pad_len);
238 memset(block + AES_BLOCK_SIZE - pad_len, pad_len, pad_len);
239 rijndaelEncrypt(_aes_key->encode_key.rk, _aes_key->encode_key.nr, block, (uint8_t *) (dest));
240 hashkit_string_set_length(destination, AES_BLOCK_SIZE * (num_blocks + 1));
241 }
242
243 return destination;
244 }
245
246 hashkit_string_st *aes_decrypt(aes_key_t *_aes_key, const char *source, size_t source_length) {
247 if (!_aes_key) {
248 return NULL;
249 }
250
251 size_t num_blocks = source_length / AES_BLOCK_SIZE;
252 if ((source_length != num_blocks * AES_BLOCK_SIZE) or num_blocks == 0) {
253 return NULL;
254 }
255
256 hashkit_string_st *destination = hashkit_string_create(source_length);
257 if (destination) {
258 char *dest = hashkit_string_c_str_mutable(destination);
259
260 for (size_t x = num_blocks - 1; x > 0; x--) {
261 rijndaelDecrypt(_aes_key->decode_key.rk, _aes_key->decode_key.nr, (const uint8_t *) (source),
262 (uint8_t *) (dest));
263 source += AES_BLOCK_SIZE;
264 dest += AES_BLOCK_SIZE;
265 }
266
267 uint8_t block[AES_BLOCK_SIZE];
268 rijndaelDecrypt(_aes_key->decode_key.rk, _aes_key->decode_key.nr, (const uint8_t *) (source),
269 block);
270 /* Use last char in the block as size */
271 unsigned int pad_len = (unsigned int) (unsigned char) (block[AES_BLOCK_SIZE - 1]);
272 if (pad_len > AES_BLOCK_SIZE) {
273 hashkit_string_free(destination);
274 return NULL;
275 }
276
277 /* We could also check whole padding but we do not really need this */
278
279 memcpy(dest, block, AES_BLOCK_SIZE - pad_len);
280 hashkit_string_set_length(destination, AES_BLOCK_SIZE * num_blocks - pad_len);
281 }
282
283 return destination;
284 }
285
286 void aes_free_key(aes_key_t *key) {
287 if (key) {
288 free(key);
289 }
290 }
291
292 #endif