1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2013-2016 Jakub Zelenka                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Author: Jakub Zelenka <bukka@php.net>                                |
16   +----------------------------------------------------------------------+
17 */
18 
19 #define PHPC_SMART_CSTR_INCLUDE 1
20 
21 #include "php.h"
22 #include "php_crypto.h"
23 #include "php_crypto_cipher.h"
24 #include "php_crypto_object.h"
25 #include "zend_exceptions.h"
26 #include "ext/standard/php_string.h"
27 
28 #include <openssl/evp.h>
29 
30 /* ERRORS */
31 
32 PHP_CRYPTO_EXCEPTION_DEFINE(Cipher)
33 PHP_CRYPTO_ERROR_INFO_BEGIN(Cipher)
34 PHP_CRYPTO_ERROR_INFO_ENTRY(
35 	ALGORITHM_NOT_FOUND,
36 	"Cipher '%s' algorithm not found"
37 )
38 PHP_CRYPTO_ERROR_INFO_ENTRY(
39 	STATIC_METHOD_NOT_FOUND,
40 	"Cipher static method '%s' not found"
41 )
42 PHP_CRYPTO_ERROR_INFO_ENTRY(
43 	STATIC_METHOD_TOO_MANY_ARGS,
44 	"Cipher static method %s can accept max two arguments"
45 )
46 PHP_CRYPTO_ERROR_INFO_ENTRY(
47 	MODE_NOT_FOUND,
48 	"Cipher mode not found"
49 )
50 PHP_CRYPTO_ERROR_INFO_ENTRY(
51 	MODE_NOT_AVAILABLE,
52 	"Cipher mode %s is not available in installed OpenSSL library"
53 )
54 PHP_CRYPTO_ERROR_INFO_ENTRY(
55 	AUTHENTICATION_NOT_SUPPORTED,
56 	"The authentication is not supported for %s cipher mode"
57 )
58 PHP_CRYPTO_ERROR_INFO_ENTRY(
59 	KEY_LENGTH_INVALID,
60 	"Invalid length of key for cipher '%s' algorithm "
61 	"(required length: %d)"
62 )
63 PHP_CRYPTO_ERROR_INFO_ENTRY(
64 	IV_LENGTH_INVALID,
65 	"Invalid length of initial vector for cipher '%s' algorithm "
66 	"(required length: %d)"
67 )
68 PHP_CRYPTO_ERROR_INFO_ENTRY(
69 	AAD_SETTER_FORBIDDEN,
70 	"AAD setter has to be called before encryption or decryption"
71 )
72 PHP_CRYPTO_ERROR_INFO_ENTRY(
73 	AAD_SETTER_FAILED,
74 	"AAD setter failed"
75 )
76 PHP_CRYPTO_ERROR_INFO_ENTRY(
77 	AAD_LENGTH_HIGH,
78 	"AAD length can't exceed max integer length"
79 )
80 PHP_CRYPTO_ERROR_INFO_ENTRY(
81 	TAG_GETTER_FORBIDDEN,
82 	"Tag getter has to be called after encryption"
83 )
84 PHP_CRYPTO_ERROR_INFO_ENTRY(
85 	TAG_SETTER_FORBIDDEN,
86 	"Tag setter has to be called before decryption"
87 )
88 PHP_CRYPTO_ERROR_INFO_ENTRY(
89 	TAG_GETTER_FAILED,
90 	"Tag getter failed"
91 )
92 PHP_CRYPTO_ERROR_INFO_ENTRY(
93 	TAG_SETTER_FAILED,
94 	"Tag setter failed"
95 )
96 PHP_CRYPTO_ERROR_INFO_ENTRY(
97 	TAG_LENGTH_SETTER_FORBIDDEN,
98 	"Tag length setter has to be called before encryption"
99 )
100 PHP_CRYPTO_ERROR_INFO_ENTRY(
101 	TAG_LENGTH_LOW,
102 	"Tag length can't be lower than 32 bits (4 characters)"
103 )
104 PHP_CRYPTO_ERROR_INFO_ENTRY(
105 	TAG_LENGTH_HIGH,
106 	"Tag length can't exceed 128 bits (16 characters)"
107 )
108 PHP_CRYPTO_ERROR_INFO_ENTRY(
109 	TAG_VERIFY_FAILED,
110 	"Tag verifycation failed"
111 )
112 PHP_CRYPTO_ERROR_INFO_ENTRY(
113 	INIT_ALG_FAILED,
114 	"Initialization of cipher algorithm failed"
115 )
116 PHP_CRYPTO_ERROR_INFO_ENTRY(
117 	INIT_CTX_FAILED,
118 	"Initialization of cipher context failed"
119 )
120 PHP_CRYPTO_ERROR_INFO_ENTRY(
121 	INIT_ENCRYPT_FORBIDDEN,
122 	"Cipher object is already used for decryption"
123 )
124 PHP_CRYPTO_ERROR_INFO_ENTRY(
125 	INIT_DECRYPT_FORBIDDEN,
126 	"Cipher object is already used for encryption"
127 )
128 PHP_CRYPTO_ERROR_INFO_ENTRY(
129 	UPDATE_FAILED,
130 	"Updating of cipher failed"
131 )
132 PHP_CRYPTO_ERROR_INFO_ENTRY(
133 	UPDATE_ENCRYPT_FORBIDDEN,
134 	"Cipher object is not initialized for encryption"
135 )
136 PHP_CRYPTO_ERROR_INFO_ENTRY(
137 	UPDATE_DECRYPT_FORBIDDEN,
138 	"Cipher object is not initialized for decryption"
139 )
140 PHP_CRYPTO_ERROR_INFO_ENTRY(
141 	FINISH_FAILED,
142 	"Finalizing of cipher failed"
143 )
144 PHP_CRYPTO_ERROR_INFO_ENTRY(
145 	FINISH_ENCRYPT_FORBIDDEN,
146 	"Cipher object is not initialized for encryption"
147 )
148 PHP_CRYPTO_ERROR_INFO_ENTRY(
149 	FINISH_DECRYPT_FORBIDDEN,
150 	"Cipher object is not initialized for decryption"
151 )
152 PHP_CRYPTO_ERROR_INFO_ENTRY(
153 	INPUT_DATA_LENGTH_HIGH,
154 	"Input data length can't exceed max integer length"
155 )
156 PHP_CRYPTO_ERROR_INFO_END()
157 
158 
159 /* ARG INFOS */
160 
161 ZEND_BEGIN_ARG_INFO(arginfo_crypto_cipher_algorithm, 0)
162 ZEND_ARG_INFO(0, algorithm)
163 ZEND_END_ARG_INFO()
164 
165 ZEND_BEGIN_ARG_INFO(arginfo_crypto_cipher_data, 0)
166 ZEND_ARG_INFO(0, data)
167 ZEND_END_ARG_INFO()
168 
169 ZEND_BEGIN_ARG_INFO_EX(arginfo_crypto_cipher_list, 0, 0, 0)
170 ZEND_ARG_INFO(0, aliases)
171 ZEND_ARG_INFO(0, prefix)
172 ZEND_END_ARG_INFO()
173 
174 ZEND_BEGIN_ARG_INFO(arginfo_crypto_cipher_static, 0)
175 ZEND_ARG_INFO(0, name)
176 ZEND_ARG_INFO(0, arguments)
177 ZEND_END_ARG_INFO()
178 
179 ZEND_BEGIN_ARG_INFO_EX(arginfo_crypto_cipher_construct, 0, 0, 1)
180 ZEND_ARG_INFO(0, algorithm)
181 ZEND_ARG_INFO(0, mode)
182 ZEND_ARG_INFO(0, key_size)
183 ZEND_END_ARG_INFO()
184 
185 ZEND_BEGIN_ARG_INFO_EX(arginfo_crypto_cipher_init, 0, 0, 1)
186 ZEND_ARG_INFO(0, key)
187 ZEND_ARG_INFO(0, iv)
188 ZEND_END_ARG_INFO()
189 
190 ZEND_BEGIN_ARG_INFO(arginfo_crypto_cipher_mode, 0)
191 ZEND_ARG_INFO(0, mode)
192 ZEND_END_ARG_INFO()
193 
194 ZEND_BEGIN_ARG_INFO(arginfo_crypto_cipher_set_tag_len, 0)
195 ZEND_ARG_INFO(0, tag_length)
196 ZEND_END_ARG_INFO()
197 
198 ZEND_BEGIN_ARG_INFO(arginfo_crypto_cipher_set_tag, 0)
199 ZEND_ARG_INFO(0, tag)
200 ZEND_END_ARG_INFO()
201 
202 ZEND_BEGIN_ARG_INFO(arginfo_crypto_cipher_set_aad, 0)
203 ZEND_ARG_INFO(0, aad)
204 ZEND_END_ARG_INFO()
205 
206 ZEND_BEGIN_ARG_INFO_EX(arginfo_crypto_cipher_crypt, 0, 0, 2)
207 ZEND_ARG_INFO(0, data)
208 ZEND_ARG_INFO(0, key)
209 ZEND_ARG_INFO(0, iv)
210 ZEND_END_ARG_INFO()
211 
212 
213 static const zend_function_entry php_crypto_cipher_object_methods[] = {
214 	PHP_CRYPTO_ME(
215 		Cipher, getAlgorithms,
216 		arginfo_crypto_cipher_list,
217 		ZEND_ACC_STATIC|ZEND_ACC_PUBLIC
218 	)
219 	PHP_CRYPTO_ME(
220 		Cipher, hasAlgorithm,
221 		arginfo_crypto_cipher_algorithm,
222 		ZEND_ACC_STATIC|ZEND_ACC_PUBLIC
223 	)
224 	PHP_CRYPTO_ME(
225 		Cipher, hasMode,
226 		arginfo_crypto_cipher_mode,
227 		ZEND_ACC_STATIC|ZEND_ACC_PUBLIC
228 	)
229 	PHP_CRYPTO_ME(
230 		Cipher, __callStatic,
231 		arginfo_crypto_cipher_static,
232 		ZEND_ACC_STATIC|ZEND_ACC_PUBLIC
233 	)
234 	PHP_CRYPTO_ME(
235 		Cipher, __construct,
236 		arginfo_crypto_cipher_construct,
237 		ZEND_ACC_CTOR|ZEND_ACC_PUBLIC
238 	)
239 	PHP_CRYPTO_ME(
240 		Cipher, getAlgorithmName,
241 		NULL,
242 		ZEND_ACC_PUBLIC
243 	)
244 	PHP_CRYPTO_ME(
245 		Cipher, encryptInit,
246 		arginfo_crypto_cipher_init,
247 		ZEND_ACC_PUBLIC
248 	)
249 	PHP_CRYPTO_ME(
250 		Cipher, encryptUpdate,
251 		arginfo_crypto_cipher_data,
252 		ZEND_ACC_PUBLIC
253 	)
254 	PHP_CRYPTO_ME(
255 		Cipher, encryptFinish,
256 		NULL,
257 		ZEND_ACC_PUBLIC
258 	)
259 	PHP_CRYPTO_ME(
260 		Cipher, encrypt,
261 		arginfo_crypto_cipher_crypt,
262 		ZEND_ACC_PUBLIC
263 	)
264 	PHP_CRYPTO_ME(
265 		Cipher, decryptInit,
266 		arginfo_crypto_cipher_init,
267 		ZEND_ACC_PUBLIC
268 	)
269 	PHP_CRYPTO_ME(
270 		Cipher, decryptUpdate,
271 		arginfo_crypto_cipher_data,
272 		ZEND_ACC_PUBLIC
273 	)
274 	PHP_CRYPTO_ME(
275 		Cipher, decryptFinish,
276 		NULL,
277 		ZEND_ACC_PUBLIC
278 	)
279 	PHP_CRYPTO_ME(
280 		Cipher, decrypt,
281 		arginfo_crypto_cipher_crypt,
282 		ZEND_ACC_PUBLIC
283 	)
284 	PHP_CRYPTO_ME(
285 		Cipher, getBlockSize,
286 		NULL,
287 		ZEND_ACC_PUBLIC
288 	)
289 	PHP_CRYPTO_ME(
290 		Cipher, getKeyLength,
291 		NULL,
292 		ZEND_ACC_PUBLIC
293 	)
294 	PHP_CRYPTO_ME(
295 		Cipher, getIVLength,
296 		NULL,
297 		ZEND_ACC_PUBLIC
298 	)
299 	PHP_CRYPTO_ME(
300 		Cipher, getMode,
301 		NULL,
302 		ZEND_ACC_PUBLIC
303 	)
304 	PHP_CRYPTO_ME(
305 		Cipher, getTag,
306 		NULL,
307 		ZEND_ACC_PUBLIC
308 	)
309 	PHP_CRYPTO_ME(
310 		Cipher, setTag,
311 		arginfo_crypto_cipher_set_tag,
312 		ZEND_ACC_PUBLIC
313 	)
314 	PHP_CRYPTO_ME(
315 		Cipher, setTagLength,
316 		arginfo_crypto_cipher_set_tag_len,
317 		ZEND_ACC_PUBLIC
318 	)
319 	PHP_CRYPTO_ME(
320 		Cipher, setAAD,
321 		arginfo_crypto_cipher_set_aad,
322 		ZEND_ACC_PUBLIC
323 	)
324 	PHPC_FE_END
325 };
326 
327 /* cipher modes lookup table */
328 static const php_crypto_cipher_mode php_crypto_cipher_modes[] = {
329 	PHP_CRYPTO_CIPHER_MODE_ENTRY(ECB)
330 	PHP_CRYPTO_CIPHER_MODE_ENTRY(CBC)
331 	PHP_CRYPTO_CIPHER_MODE_ENTRY(CFB)
332 	PHP_CRYPTO_CIPHER_MODE_ENTRY(OFB)
333 #ifdef EVP_CIPH_CTR_MODE
334 	PHP_CRYPTO_CIPHER_MODE_ENTRY(CTR)
335 #else
336 	PHP_CRYPTO_CIPHER_MODE_ENTRY_NOT_DEFINED(CTR)
337 #endif
338 #ifdef EVP_CIPH_GCM_MODE
339 	PHP_CRYPTO_CIPHER_MODE_ENTRY_EX(GCM, 1, 0,
340 			EVP_CTRL_GCM_SET_IVLEN,
341 			EVP_CTRL_GCM_SET_TAG, EVP_CTRL_GCM_GET_TAG)
342 #else
343 	PHP_CRYPTO_CIPHER_MODE_ENTRY_NOT_DEFINED(GCM)
344 #endif
345 #ifdef EVP_CIPH_CCM_MODE
346 	PHP_CRYPTO_CIPHER_MODE_ENTRY_EX(CCM, 1, 1,
347 			EVP_CTRL_CCM_SET_IVLEN,
348 			EVP_CTRL_CCM_SET_TAG, EVP_CTRL_CCM_GET_TAG)
349 #else
350 	PHP_CRYPTO_CIPHER_MODE_ENTRY_NOT_DEFINED(CCM)
351 #endif
352 #ifdef EVP_CIPH_XTS_MODE
353 	PHP_CRYPTO_CIPHER_MODE_ENTRY(XTS)
354 #else
355 	PHP_CRYPTO_CIPHER_MODE_ENTRY_NOT_DEFINED(XTS)
356 #endif
357 	PHP_CRYPTO_CIPHER_MODE_ENTRY_END
358 };
359 
360 /* class entry */
361 PHP_CRYPTO_API zend_class_entry *php_crypto_cipher_ce;
362 
363 /* object handler */
364 PHPC_OBJ_DEFINE_HANDLER_VAR(crypto_cipher);
365 
366 /* algorithm name getter macros */
367 #define PHP_CRYPTO_CIPHER_GET_ALGORITHM_NAME_EX(this_object) \
368 	PHPC_READ_PROPERTY(php_crypto_cipher_ce, this_object, \
369 		"algorithm", sizeof("algorithm")-1, 1)
370 
371 #define PHP_CRYPTO_CIPHER_GET_ALGORITHM_NAME(this_object) \
372 	Z_STRVAL_P(PHP_CRYPTO_CIPHER_GET_ALGORITHM_NAME_EX(this_object))
373 
374 
375 /* {{{ crypto_cipher free object handler */
PHPC_OBJ_HANDLER_FREE(crypto_cipher)376 PHPC_OBJ_HANDLER_FREE(crypto_cipher)
377 {
378 	PHPC_OBJ_HANDLER_FREE_INIT(crypto_cipher);
379 
380 	EVP_CIPHER_CTX_free(PHP_CRYPTO_CIPHER_CTX(PHPC_THIS));
381 
382 	if (PHP_CRYPTO_CIPHER_AAD(PHPC_THIS)) {
383 		efree(PHP_CRYPTO_CIPHER_AAD(PHPC_THIS));
384 	}
385 	if (PHP_CRYPTO_CIPHER_TAG(PHPC_THIS)) {
386 		efree(PHP_CRYPTO_CIPHER_TAG(PHPC_THIS));
387 	}
388 
389 	PHPC_OBJ_HANDLER_FREE_DESTROY();
390 }
391 /* }}} */
392 
393 /* {{{ crypto_cipher create_ex object helper */
PHPC_OBJ_HANDLER_CREATE_EX(crypto_cipher)394 PHPC_OBJ_HANDLER_CREATE_EX(crypto_cipher)
395 {
396 	PHPC_OBJ_HANDLER_CREATE_EX_INIT(crypto_cipher);
397 
398 	PHP_CRYPTO_CIPHER_CTX(PHPC_THIS) = EVP_CIPHER_CTX_new();
399 	if (!PHP_CRYPTO_CIPHER_CTX(PHPC_THIS)) {
400 		php_error(E_ERROR, "Creating Cipher object failed");
401 	}
402 
403 	PHP_CRYPTO_CIPHER_AAD(PHPC_THIS) = NULL;
404 	PHP_CRYPTO_CIPHER_AAD_LEN(PHPC_THIS) = 0;
405 	PHP_CRYPTO_CIPHER_TAG(PHPC_THIS) = NULL;
406 	/* this is a default len for the tag */
407 	PHP_CRYPTO_CIPHER_TAG_LEN(PHPC_THIS) =
408 			PHP_CRYPTO_CIPHER_AUTH_TAG_LENGTH_DEFAULT;
409 
410 	PHPC_OBJ_HANDLER_CREATE_EX_RETURN(crypto_cipher);
411 }
412 /* }}} */
413 
414 /* {{{ crypto_cipher create object handler */
PHPC_OBJ_HANDLER_CREATE(crypto_cipher)415 PHPC_OBJ_HANDLER_CREATE(crypto_cipher)
416 {
417 	PHPC_OBJ_HANDLER_CREATE_RETURN(crypto_cipher);
418 }
419 /* }}} */
420 
421 /* {{{ crypto_cipher clone object handler */
PHPC_OBJ_HANDLER_CLONE(crypto_cipher)422 PHPC_OBJ_HANDLER_CLONE(crypto_cipher)
423 {
424 	zend_bool copy_success;
425 	PHPC_OBJ_HANDLER_CLONE_INIT(crypto_cipher);
426 
427 	PHPC_THAT->status = PHPC_THIS->status;
428 	if (PHP_CRYPTO_CIPHER_TAG(PHPC_THIS)) {
429 		PHP_CRYPTO_CIPHER_TAG(PHPC_THAT) = emalloc(
430 				PHP_CRYPTO_CIPHER_TAG_LEN(PHPC_THIS));
431 		memcpy(PHP_CRYPTO_CIPHER_TAG(PHPC_THAT),
432 				PHP_CRYPTO_CIPHER_TAG(PHPC_THIS),
433 				PHP_CRYPTO_CIPHER_TAG_LEN(PHPC_THIS));
434 		PHP_CRYPTO_CIPHER_TAG_LEN(PHPC_THAT) =
435 				PHP_CRYPTO_CIPHER_TAG_LEN(PHPC_THIS);
436 	}
437 	if (PHP_CRYPTO_CIPHER_AAD(PHPC_THIS)) {
438 		PHP_CRYPTO_CIPHER_AAD(PHPC_THIS) = emalloc(
439 				PHP_CRYPTO_CIPHER_AAD_LEN(PHPC_THIS));
440 		memcpy(PHP_CRYPTO_CIPHER_AAD(PHPC_THAT),
441 				PHP_CRYPTO_CIPHER_AAD(PHPC_THIS),
442 				PHP_CRYPTO_CIPHER_AAD_LEN(PHPC_THIS));
443 		PHP_CRYPTO_CIPHER_AAD_LEN(PHPC_THAT) =
444 				PHP_CRYPTO_CIPHER_AAD_LEN(PHPC_THIS);
445 	}
446 
447 #ifdef PHP_CRYPTO_HAS_CIPHER_CTX_COPY
448 	copy_success = EVP_CIPHER_CTX_copy(
449 			PHP_CRYPTO_CIPHER_CTX(PHPC_THAT),
450 			PHP_CRYPTO_CIPHER_CTX(PHPC_THIS));
451 #else
452 	memcpy(PHP_CRYPTO_CIPHER_CTX(PHPC_THAT),
453 			PHP_CRYPTO_CIPHER_CTX(PHPC_THIS),
454 			sizeof *(PHP_CRYPTO_CIPHER_CTX(PHPC_THAT)));
455 
456 	copy_success = 1;
457 	if (PHP_CRYPTO_CIPHER_CTX(PHPC_THIS)->cipher_data &&
458 			PHP_CRYPTO_CIPHER_CTX(PHPC_THIS)->cipher->ctx_size) {
459 		PHP_CRYPTO_CIPHER_CTX(PHPC_THAT)->cipher_data = OPENSSL_malloc(
460 				PHP_CRYPTO_CIPHER_CTX(PHPC_THIS)->cipher->ctx_size);
461 		if (!PHP_CRYPTO_CIPHER_CTX(PHPC_THAT)->cipher_data) {
462 			copy_success = 0;
463 		}
464 		memcpy(PHP_CRYPTO_CIPHER_CTX(PHPC_THAT)->cipher_data,
465 				PHP_CRYPTO_CIPHER_CTX(PHPC_THIS)->cipher_data,
466 				PHP_CRYPTO_CIPHER_CTX(PHPC_THIS)->cipher->ctx_size);
467 	}
468 #endif
469 
470 	PHP_CRYPTO_CIPHER_ALG(PHPC_THAT) = EVP_CIPHER_CTX_cipher(PHP_CRYPTO_CIPHER_CTX(PHPC_THIS));
471 
472 	if (!copy_success) {
473 		php_error(E_ERROR, "Cloning of Cipher object failed");
474 	}
475 
476 	PHPC_OBJ_HANDLER_CLONE_RETURN();
477 }
478 /* }}} */
479 
480 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(crypto_cipher)481 PHP_MINIT_FUNCTION(crypto_cipher)
482 {
483 	zend_class_entry ce;
484 	const php_crypto_cipher_mode *mode;
485 
486 	/* CipherException registration */
487 	PHP_CRYPTO_EXCEPTION_REGISTER(ce, Cipher);
488 	PHP_CRYPTO_ERROR_INFO_REGISTER(Cipher);
489 
490 	/* Cipher class */
491 	INIT_CLASS_ENTRY(ce, PHP_CRYPTO_CLASS_NAME(Cipher), php_crypto_cipher_object_methods);
492 	PHPC_CLASS_SET_HANDLER_CREATE(ce, crypto_cipher);
493 	php_crypto_cipher_ce = PHPC_CLASS_REGISTER(ce);
494 	PHPC_OBJ_INIT_HANDLERS(crypto_cipher);
495 	PHPC_OBJ_SET_HANDLER_OFFSET(crypto_cipher);
496 	PHPC_OBJ_SET_HANDLER_FREE(crypto_cipher);
497 	PHPC_OBJ_SET_HANDLER_CLONE(crypto_cipher);
498 	zend_declare_property_null(php_crypto_cipher_ce,
499 			"algorithm", sizeof("algorithm")-1, ZEND_ACC_PROTECTED TSRMLS_CC);
500 
501 	/* Cipher constants for modes */
502 	for (mode = php_crypto_cipher_modes; mode->name[0]; mode++) {
503 		zend_declare_class_constant_long(php_crypto_cipher_ce,
504 				mode->constant, strlen(mode->constant), mode->value TSRMLS_CC);
505 	}
506 
507 	return SUCCESS;
508 }
509 /* }}} */
510 
511 /* METHODS */
512 
513 /* {{{ php_crypto_get_algorithm_object_ex */
php_crypto_cipher_set_algorithm_name(zval * object,char * algorithm,phpc_str_size_t algorithm_len TSRMLS_DC)514 static inline void php_crypto_cipher_set_algorithm_name(zval *object,
515 		char *algorithm, phpc_str_size_t algorithm_len TSRMLS_DC)
516 {
517 	php_strtoupper(algorithm, algorithm_len);
518 	zend_update_property_stringl(php_crypto_cipher_ce, object,
519 			"algorithm", sizeof("algorithm")-1, algorithm, algorithm_len TSRMLS_CC);
520 }
521 /* }}} */
522 
523 /* {{{ php_crypto_get_cipher_algorithm */
php_crypto_get_cipher_algorithm(char * algorithm,phpc_str_size_t algorithm_len)524 PHP_CRYPTO_API const EVP_CIPHER *php_crypto_get_cipher_algorithm(
525 		char *algorithm, phpc_str_size_t algorithm_len)
526 {
527 	const EVP_CIPHER *cipher;
528 
529 	if (algorithm_len > PHP_CRYPTO_CIPHER_ALGORITHM_LEN_MAX) {
530 		return NULL;
531 	}
532 
533 	php_strtoupper(algorithm, algorithm_len);
534 	cipher = EVP_get_cipherbyname(algorithm);
535 	if (!cipher) {
536 		php_strtolower(algorithm, algorithm_len);
537 		cipher = EVP_get_cipherbyname(algorithm);
538 	}
539 	return cipher;
540 }
541 /* }}} */
542 
543 /* {{{ php_crypto_get_cipher_algorithm_from_params_ex */
php_crypto_get_cipher_algorithm_from_params_ex(zval * object,char * algorithm,phpc_str_size_t algorithm_len,zval * pz_mode,zval * pz_key_size,zend_bool is_static TSRMLS_DC)544 static const EVP_CIPHER *php_crypto_get_cipher_algorithm_from_params_ex(
545 		zval *object, char *algorithm, phpc_str_size_t algorithm_len, zval *pz_mode,
546 		zval *pz_key_size, zend_bool is_static TSRMLS_DC)
547 {
548 	const EVP_CIPHER *cipher;
549 	phpc_smart_cstr alg_buf = {0};
550 
551 	/* if mode is not set, then it is already contained in the algorithm string */
552 	if (!pz_mode || Z_TYPE_P(pz_mode) == IS_NULL) {
553 		cipher = php_crypto_get_cipher_algorithm(algorithm, algorithm_len);
554 		if (!cipher) {
555 			if (is_static) {
556 				php_crypto_error_ex(PHP_CRYPTO_ERROR_ARGS(Cipher, STATIC_METHOD_NOT_FOUND),
557 						algorithm);
558 			} else {
559 				php_crypto_error_ex(PHP_CRYPTO_ERROR_ARGS(Cipher, ALGORITHM_NOT_FOUND),
560 						algorithm);
561 			}
562 		} else if (object) {
563 			php_crypto_cipher_set_algorithm_name(object, algorithm, algorithm_len TSRMLS_CC);
564 		}
565 		return cipher;
566 	}
567 
568 	phpc_smart_cstr_appendl(&alg_buf, algorithm, algorithm_len);
569 	phpc_smart_cstr_appendc(&alg_buf, '-');
570 
571 	/* copy key size if available */
572 	if (pz_key_size && Z_TYPE_P(pz_key_size) != IS_NULL) {
573 		if (Z_TYPE_P(pz_key_size) == IS_STRING) {
574 			phpc_smart_cstr_appendl(&alg_buf, Z_STRVAL_P(pz_key_size), Z_STRLEN_P(pz_key_size));
575 		} else {
576 			zval z_key_size = *pz_key_size;
577 			zval_copy_ctor(&z_key_size);
578 			convert_to_string(&z_key_size);
579 			phpc_smart_cstr_appendl(&alg_buf, Z_STRVAL(z_key_size), Z_STRLEN(z_key_size));
580 			phpc_smart_cstr_appendc(&alg_buf, '-');
581 			zval_dtor(&z_key_size);
582 		}
583 	}
584 
585 	/* copy mode */
586 	if (Z_TYPE_P(pz_mode) == IS_LONG) {
587 		const php_crypto_cipher_mode *mode = php_crypto_get_cipher_mode_ex(Z_LVAL_P(pz_mode));
588 		if (!mode) {
589 			php_crypto_error_ex(PHP_CRYPTO_ERROR_ARGS(Cipher, MODE_NOT_FOUND));
590 			phpc_smart_cstr_free(&alg_buf);
591 			return NULL;
592 		}
593 		if (mode->value == PHP_CRYPTO_CIPHER_MODE_NOT_DEFINED) {
594 			php_crypto_error_ex(PHP_CRYPTO_ERROR_ARGS(Cipher, MODE_NOT_AVAILABLE), mode->name);
595 			phpc_smart_cstr_free(&alg_buf);
596 			return NULL;
597 		}
598 		phpc_smart_cstr_appendl(&alg_buf, mode->name, PHP_CRYPTO_CIPHER_MODE_LEN);
599 	} else if (Z_TYPE_P(pz_mode) == IS_STRING) {
600 		phpc_smart_cstr_appendl(&alg_buf, Z_STRVAL_P(pz_mode), Z_STRLEN_P(pz_mode));
601 	} else {
602 		zval z_mode = *pz_mode;
603 		zval_copy_ctor(&z_mode);
604 		convert_to_string(&z_mode);
605 		phpc_smart_cstr_appendl(&alg_buf, Z_STRVAL(z_mode), Z_STRLEN(z_mode));
606 		zval_dtor(&z_mode);
607 	}
608 
609 	phpc_smart_cstr_0(&alg_buf);
610 	cipher = php_crypto_get_cipher_algorithm(alg_buf.c, alg_buf.len);
611 	if (!cipher) {
612 		if (is_static) {
613 			php_crypto_error_ex(PHP_CRYPTO_ERROR_ARGS(Cipher, STATIC_METHOD_NOT_FOUND), alg_buf.c);
614 		} else {
615 			php_crypto_error_ex(PHP_CRYPTO_ERROR_ARGS(Cipher, ALGORITHM_NOT_FOUND), alg_buf.c);
616 		}
617 	} else if (object) {
618 		php_crypto_cipher_set_algorithm_name(object, alg_buf.c, alg_buf.len TSRMLS_CC);
619 	}
620 	phpc_smart_cstr_free(&alg_buf);
621 	return cipher;
622 }
623 /* }}} */
624 
625 /* {{{ php_crypto_get_cipher_algorithm_from_params */
php_crypto_get_cipher_algorithm_from_params(char * algorithm,phpc_str_size_t algorithm_len,zval * pz_mode,zval * pz_key_size TSRMLS_DC)626 PHP_CRYPTO_API const EVP_CIPHER *php_crypto_get_cipher_algorithm_from_params(
627 		char *algorithm, phpc_str_size_t algorithm_len, zval *pz_mode, zval *pz_key_size TSRMLS_DC)
628 {
629 	return php_crypto_get_cipher_algorithm_from_params_ex(
630 			NULL, algorithm, algorithm_len, pz_mode, pz_key_size, 0 TSRMLS_CC);
631 }
632 /* }}} */
633 
634 /* {{{ php_crypto_set_cipher_algorithm_ex */
php_crypto_set_cipher_algorithm_ex(PHPC_THIS_DECLARE (crypto_cipher),char * algorithm,phpc_str_size_t algorithm_len TSRMLS_DC)635 static int php_crypto_set_cipher_algorithm_ex(PHPC_THIS_DECLARE(crypto_cipher),
636 		char *algorithm, phpc_str_size_t algorithm_len TSRMLS_DC)
637 {
638 	const EVP_CIPHER *cipher = php_crypto_get_cipher_algorithm(algorithm, algorithm_len);
639 	if (!cipher) {
640 		return FAILURE;
641 	}
642 	PHP_CRYPTO_CIPHER_ALG(PHPC_THIS) = cipher;
643 	return SUCCESS;
644 }
645 /* }}} */
646 
647 /* {{{ php_crypto_set_cipher_algorithm */
php_crypto_set_cipher_algorithm(zval * object,char * algorithm,phpc_str_size_t algorithm_len TSRMLS_DC)648 static int php_crypto_set_cipher_algorithm(zval *object,
649 		char *algorithm, phpc_str_size_t algorithm_len TSRMLS_DC)
650 {
651 	PHPC_THIS_DECLARE_AND_FETCH_FROM_ZVAL(crypto_cipher, object);
652 	php_crypto_cipher_set_algorithm_name(object, algorithm, algorithm_len TSRMLS_CC);
653 	return php_crypto_set_cipher_algorithm_ex(PHPC_THIS, algorithm, algorithm_len TSRMLS_CC);
654 }
655 /* }}} */
656 
657 /* {{{ php_crypto_set_cipher_algorithm_from_params_ex */
php_crypto_set_cipher_algorithm_from_params_ex(zval * object,char * algorithm,phpc_str_size_t algorithm_len,zval * pz_mode,zval * pz_key_size,zend_bool is_static TSRMLS_DC)658 static int php_crypto_set_cipher_algorithm_from_params_ex(
659 		zval *object, char *algorithm, phpc_str_size_t algorithm_len,
660 		zval *pz_mode, zval *pz_key_size, zend_bool is_static TSRMLS_DC)
661 {
662 	PHPC_THIS_DECLARE_AND_FETCH_FROM_ZVAL(crypto_cipher, object);
663 	const EVP_CIPHER *cipher = php_crypto_get_cipher_algorithm_from_params_ex(
664 			object, algorithm, algorithm_len, pz_mode, pz_key_size, is_static TSRMLS_CC);
665 
666 	if (!cipher) {
667 		return FAILURE;
668 	}
669 
670 	PHP_CRYPTO_CIPHER_ALG(PHPC_THIS) = cipher;
671 	return SUCCESS;
672 }
673 /* }}} */
674 
675 /* {{{ php_crypto_set_cipher_algorithm_from_params */
php_crypto_set_cipher_algorithm_from_params(zval * object,char * algorithm,phpc_str_size_t algorithm_len,zval * pz_mode,zval * pz_key_size TSRMLS_DC)676 static int php_crypto_set_cipher_algorithm_from_params(
677 		zval *object, char *algorithm, phpc_str_size_t algorithm_len,
678 		zval *pz_mode, zval *pz_key_size TSRMLS_DC)
679 {
680 	return php_crypto_set_cipher_algorithm_from_params_ex(
681 			object, algorithm, algorithm_len, pz_mode, pz_key_size, 0 TSRMLS_CC);
682 }
683 /* }}} */
684 
685 /* {{{ php_crypto_get_cipher_mode_ex */
php_crypto_get_cipher_mode_ex(long mode_value)686 PHP_CRYPTO_API const php_crypto_cipher_mode *php_crypto_get_cipher_mode_ex(long mode_value)
687 {
688 	const php_crypto_cipher_mode *mode;
689 
690 	for (mode = php_crypto_cipher_modes; mode->name[0]; mode++) {
691 		if (mode_value == mode->value) {
692 			return mode;
693 		}
694 	}
695 	return NULL;
696 }
697 /* }}} */
698 
699 /* {{{ php_crypto_get_cipher_mode_ex */
php_crypto_get_cipher_mode(const EVP_CIPHER * cipher)700 PHP_CRYPTO_API const php_crypto_cipher_mode *php_crypto_get_cipher_mode(const EVP_CIPHER *cipher)
701 {
702 	return php_crypto_get_cipher_mode_ex(EVP_CIPHER_mode(cipher));
703 }
704 /* }}} */
705 
706 /* {{{ php_crypto_cipher_is_mode_authenticated_ex */
php_crypto_cipher_is_mode_authenticated_ex(const php_crypto_cipher_mode * mode TSRMLS_DC)707 static int php_crypto_cipher_is_mode_authenticated_ex(const php_crypto_cipher_mode *mode TSRMLS_DC)
708 {
709 	if (!mode) { /* this should never happen */
710 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, MODE_NOT_FOUND));
711 		return FAILURE;
712 	}
713 	if (!mode->auth_enc) {
714 		php_crypto_error_ex(PHP_CRYPTO_ERROR_ARGS(Cipher, AUTHENTICATION_NOT_SUPPORTED),
715 				mode->name);
716 		return FAILURE;
717 	}
718 	return SUCCESS;
719 }
720 
721 /* {{{ php_crypto_cipher_is_mode_authenticated */
php_crypto_cipher_is_mode_authenticated(PHPC_THIS_DECLARE (crypto_cipher)TSRMLS_DC)722 static int php_crypto_cipher_is_mode_authenticated(PHPC_THIS_DECLARE(crypto_cipher) TSRMLS_DC)
723 {
724 	return php_crypto_cipher_is_mode_authenticated_ex(
725 			php_crypto_get_cipher_mode_ex(PHP_CRYPTO_CIPHER_MODE_VALUE(PHPC_THIS)) TSRMLS_CC);
726 }
727 /* }}} */
728 
729 /* {{{ php_crypto_cipher_set_tag */
php_crypto_cipher_set_tag(EVP_CIPHER_CTX * cipher_ctx,const php_crypto_cipher_mode * mode,unsigned char * tag,int tag_len TSRMLS_DC)730 PHP_CRYPTO_API int php_crypto_cipher_set_tag(EVP_CIPHER_CTX *cipher_ctx,
731 		const php_crypto_cipher_mode *mode, unsigned char *tag, int tag_len TSRMLS_DC)
732 {
733 	if (!tag) {
734 		return SUCCESS;
735 	}
736 	if (!EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->auth_set_tag_flag, tag_len, tag)) {
737 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, TAG_SETTER_FAILED));
738 		return FAILURE;
739 	}
740 	return SUCCESS;
741 }
742 /* }}} */
743 
744 /* {{{ php_crypto_cipher_check_tag_len */
php_crypto_cipher_check_tag_len(int tag_len TSRMLS_DC)745 static int php_crypto_cipher_check_tag_len(int tag_len TSRMLS_DC)
746 {
747 	if (tag_len < PHP_CRYPTO_CIPHER_AUTH_TAG_LENGTH_MIN) {
748 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, TAG_LENGTH_LOW));
749 		return FAILURE;
750 	}
751 	if (tag_len > PHP_CRYPTO_CIPHER_AUTH_TAG_LENGTH_MAX) {
752 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, TAG_LENGTH_HIGH));
753 		return FAILURE;
754 	}
755 	return SUCCESS;
756 }
757 /* }}} */
758 
759 /* {{{ php_crypto_cipher_check_key_len */
php_crypto_cipher_check_key_len(zval * zobject,PHPC_THIS_DECLARE (crypto_cipher),phpc_str_size_t key_len TSRMLS_DC)760 static int php_crypto_cipher_check_key_len(zval *zobject, PHPC_THIS_DECLARE(crypto_cipher),
761 		phpc_str_size_t key_len TSRMLS_DC)
762 {
763 	int int_key_len, alg_key_len = EVP_CIPHER_key_length(PHP_CRYPTO_CIPHER_ALG(PHPC_THIS));
764 	PHPC_READ_PROPERTY_RV_DECLARE;
765 
766 	if (php_crypto_str_size_to_int(key_len, &int_key_len) == SUCCESS &&
767 			int_key_len != alg_key_len &&
768 			!EVP_CIPHER_CTX_set_key_length(PHP_CRYPTO_CIPHER_CTX(PHPC_THIS), int_key_len)) {
769 		php_crypto_error_ex(PHP_CRYPTO_ERROR_ARGS(Cipher, KEY_LENGTH_INVALID),
770 				PHP_CRYPTO_CIPHER_GET_ALGORITHM_NAME(zobject), alg_key_len);
771 		return FAILURE;
772 	}
773 	return SUCCESS;
774 }
775 /* }}} */
776 
777 /* {{{ php_crypto_cipher_check_iv_len */
php_crypto_cipher_check_iv_len(zval * zobject,PHPC_THIS_DECLARE (crypto_cipher),const php_crypto_cipher_mode * mode,phpc_str_size_t iv_len TSRMLS_DC)778 static int php_crypto_cipher_check_iv_len(zval *zobject, PHPC_THIS_DECLARE(crypto_cipher),
779 		const php_crypto_cipher_mode *mode, phpc_str_size_t iv_len TSRMLS_DC)
780 {
781 	int int_iv_len, alg_iv_len = EVP_CIPHER_iv_length(PHP_CRYPTO_CIPHER_ALG(PHPC_THIS));
782 	PHPC_READ_PROPERTY_RV_DECLARE;
783 
784 	if (php_crypto_str_size_to_int(iv_len, &int_iv_len) == FAILURE) {
785 		return FAILURE;
786 	}
787 
788 	if (int_iv_len == alg_iv_len) {
789 		return SUCCESS;
790 	}
791 
792 	if (!mode->auth_enc || int_iv_len == INT_MAX ||
793 			!EVP_CIPHER_CTX_ctrl(PHP_CRYPTO_CIPHER_CTX(PHPC_THIS),
794 				mode->auth_ivlen_flag, int_iv_len, NULL)) {
795 		php_crypto_error_ex(PHP_CRYPTO_ERROR_ARGS(Cipher, IV_LENGTH_INVALID),
796 				PHP_CRYPTO_CIPHER_GET_ALGORITHM_NAME(zobject), alg_iv_len);
797 		return FAILURE;
798 	}
799 	return SUCCESS;
800 }
801 /* }}} */
802 
803 /* {{{ php_crypto_cipher_init_ex */
PHPC_OBJ_STRUCT_NAME(crypto_cipher)804 static PHPC_OBJ_STRUCT_NAME(crypto_cipher) *php_crypto_cipher_init_ex(
805 		zval *zobject, char *key, phpc_str_size_t key_len,
806 		char *iv, phpc_str_size_t iv_len, int enc TSRMLS_DC)
807 {
808 	const php_crypto_cipher_mode *mode;
809 	PHPC_THIS_DECLARE_AND_FETCH_FROM_ZVAL(crypto_cipher, zobject);
810 
811 	/* check algorithm status */
812 	if (enc && PHP_CRYPTO_CIPHER_IS_INITIALIZED_FOR_DECRYPTION(PHPC_THIS)) {
813 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, INIT_ENCRYPT_FORBIDDEN));
814 		return NULL;
815 	} else if (!enc && PHP_CRYPTO_CIPHER_IS_INITIALIZED_FOR_ENCRYPTION(PHPC_THIS)) {
816 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, INIT_DECRYPT_FORBIDDEN));
817 		return NULL;
818 	}
819 
820 	/* initialize encryption/decryption */
821 	if (!EVP_CipherInit_ex(PHP_CRYPTO_CIPHER_CTX(PHPC_THIS), PHP_CRYPTO_CIPHER_ALG(PHPC_THIS),
822 			NULL, NULL, NULL, enc)) {
823 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, INIT_ALG_FAILED));
824 		return NULL;
825 	}
826 
827 	/* check key length */
828 	if (php_crypto_cipher_check_key_len(zobject, PHPC_THIS, key_len TSRMLS_CC) == FAILURE) {
829 		return NULL;
830 	}
831 
832 	/* get mode */
833 	mode = php_crypto_get_cipher_mode_ex(PHP_CRYPTO_CIPHER_MODE_VALUE(PHPC_THIS));
834 
835 	/* mode with inlen init requires also pre-setting tag length */
836 	if (mode->auth_inlen_init && enc) {
837 		EVP_CIPHER_CTX_ctrl(PHP_CRYPTO_CIPHER_CTX(PHPC_THIS), mode->auth_set_tag_flag,
838 				PHP_CRYPTO_CIPHER_TAG_LEN(PHPC_THIS), NULL);
839 	}
840 
841 	/* check initialization vector length */
842 	if (php_crypto_cipher_check_iv_len(zobject, PHPC_THIS, mode, iv_len TSRMLS_CC) == FAILURE) {
843 		return NULL;
844 	}
845 
846 	if (mode->auth_enc && !enc &&
847 			php_crypto_cipher_set_tag(PHP_CRYPTO_CIPHER_CTX(PHPC_THIS), mode,
848 				PHP_CRYPTO_CIPHER_TAG(PHPC_THIS),
849 				PHP_CRYPTO_CIPHER_TAG(PHPC_THIS) ? PHP_CRYPTO_CIPHER_TAG_LEN(PHPC_THIS) : 0
850 				TSRMLS_CC) == FAILURE) {
851 		return NULL;
852 	}
853 
854 	/* initialize encryption */
855 	if (!EVP_CipherInit_ex(PHP_CRYPTO_CIPHER_CTX(PHPC_THIS), NULL, NULL,
856 			(unsigned char *) key, (unsigned char *) iv, enc)) {
857 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, INIT_CTX_FAILED));
858 		return NULL;
859 	}
860 	PHP_CRYPTO_CIPHER_SET_STATUS(PHPC_THIS, enc, INIT);
861 
862 
863 	return PHPC_THIS;
864 }
865 /* }}} */
866 
867 /* {{{ php_crypto_cipher_init */
php_crypto_cipher_init(INTERNAL_FUNCTION_PARAMETERS,int enc)868 static inline void php_crypto_cipher_init(INTERNAL_FUNCTION_PARAMETERS, int enc)
869 {
870 	char *key, *iv = NULL;
871 	phpc_str_size_t key_len, iv_len = 0;
872 
873 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s",
874 			&key, &key_len, &iv, &iv_len) == FAILURE) {
875 		return;
876 	}
877 
878 	if (php_crypto_cipher_init_ex(getThis(), key, key_len, iv, iv_len, enc TSRMLS_CC)) {
879 		RETURN_TRUE;
880 	} else {
881 		RETURN_FALSE;
882 	}
883 }
884 /* }}} */
885 
886 /* {{{ php_crypto_cipher_write_aad */
php_crypto_cipher_write_aad(EVP_CIPHER_CTX * cipher_ctx,unsigned char * aad,int aad_len TSRMLS_DC)887 PHP_CRYPTO_API int php_crypto_cipher_write_aad(
888 		EVP_CIPHER_CTX *cipher_ctx, unsigned char *aad, int aad_len TSRMLS_DC)
889 {
890 	int outlen, ret;
891 
892 	if (aad) {
893 		ret = EVP_CipherUpdate(cipher_ctx, NULL, &outlen, aad, aad_len);
894 	} else {
895 		unsigned char buf[4];
896 		ret = EVP_CipherUpdate(cipher_ctx, NULL, &outlen, buf, 0);
897 	}
898 
899 	if (!ret) {
900 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, AAD_SETTER_FAILED));
901 		return FAILURE;
902 	}
903 	return SUCCESS;
904 }
905 /* }}} */
906 
907 /* {{{ php_crypto_cipher_write_inlen */
php_crypto_cipher_write_inlen(EVP_CIPHER_CTX * cipher_ctx,int inlen TSRMLS_DC)908 static int php_crypto_cipher_write_inlen(
909 		EVP_CIPHER_CTX *cipher_ctx, int inlen TSRMLS_DC)
910 {
911 	int outlen;
912 
913 	if (!EVP_CipherUpdate(cipher_ctx, NULL, &outlen, NULL, inlen)) {
914 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, UPDATE_FAILED));
915 		return FAILURE;
916 	}
917 	return SUCCESS;
918 }
919 /* }}} */
920 
921 /* {{{ php_crypto_cipher_auth_init */
php_crypto_cipher_auth_init(PHPC_THIS_DECLARE (crypto_cipher),int inlen TSRMLS_DC)922 static int php_crypto_cipher_auth_init(
923 		PHPC_THIS_DECLARE(crypto_cipher), int inlen TSRMLS_DC)
924 {
925 	EVP_CIPHER_CTX *cipher_ctx = PHP_CRYPTO_CIPHER_CTX(PHPC_THIS);
926 	const php_crypto_cipher_mode *mode = php_crypto_get_cipher_mode_ex(
927 				PHP_CRYPTO_CIPHER_MODE_VALUE(PHPC_THIS));
928 
929 	/* auth init is just for auth modes */
930 	if (!mode->auth_enc) {
931 		return SUCCESS;
932 	}
933 
934 	/* check if plain text length needs to be initialized (CCM mode) */
935 	if (mode->auth_inlen_init && php_crypto_cipher_write_inlen(
936 			cipher_ctx, inlen TSRMLS_CC) == FAILURE) {
937 		return FAILURE;
938 	}
939 
940 	/* write additional authenticated data */
941 	if (php_crypto_cipher_write_aad(
942 			cipher_ctx,
943 			PHP_CRYPTO_CIPHER_AAD(PHPC_THIS),
944 			PHP_CRYPTO_CIPHER_AAD_LEN(PHPC_THIS) TSRMLS_CC) == FAILURE) {
945 		return FAILURE;
946 	}
947 
948 	return SUCCESS;
949 }
950 /* }}} */
951 
952 /* {{{ php_crypto_cipher_update */
php_crypto_cipher_update(INTERNAL_FUNCTION_PARAMETERS,int enc)953 static inline void php_crypto_cipher_update(INTERNAL_FUNCTION_PARAMETERS, int enc)
954 {
955 	PHPC_THIS_DECLARE(crypto_cipher);
956 	PHPC_STR_DECLARE(out);
957 	char *data;
958 	phpc_str_size_t data_str_size;
959 	int out_len, update_len, data_len;
960 
961 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &data_str_size) == FAILURE) {
962 		return;
963 	}
964 
965 	if (php_crypto_str_size_to_int(data_str_size, &data_len)) {
966 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, INPUT_DATA_LENGTH_HIGH));
967 		RETURN_FALSE;
968 	}
969 
970 	PHPC_THIS_FETCH(crypto_cipher);
971 
972 	/* check algorithm status */
973 	if (enc && !PHP_CRYPTO_CIPHER_IS_INITIALIZED_FOR_ENCRYPTION(PHPC_THIS)) {
974 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, UPDATE_ENCRYPT_FORBIDDEN));
975 		RETURN_FALSE;
976 	} else if (!enc && !PHP_CRYPTO_CIPHER_IS_INITIALIZED_FOR_DECRYPTION(PHPC_THIS)) {
977 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, UPDATE_DECRYPT_FORBIDDEN));
978 		RETURN_FALSE;
979 	}
980 
981 	/* if the crypto is in init state (first update), then do auth init */
982 	if (PHP_CRYPTO_CIPHER_IS_IN_INIT_STATE(PHPC_THIS) &&
983 			php_crypto_cipher_auth_init(PHPC_THIS, data_len TSRMLS_CC) == FAILURE) {
984 		RETURN_FALSE;
985 	}
986 
987 	out_len = data_len + EVP_CIPHER_block_size(PHP_CRYPTO_CIPHER_ALG(PHPC_THIS));
988 	update_len = out_len;
989 	PHPC_STR_ALLOC(out, out_len);
990 
991 	/* update encryption context */
992 	if (!EVP_CipherUpdate(PHP_CRYPTO_CIPHER_CTX(PHPC_THIS),
993 			(unsigned char *) PHPC_STR_VAL(out), &update_len,
994 			(unsigned char *) data, data_len)) {
995 		/* get mode info */
996 		const php_crypto_cipher_mode *mode = php_crypto_get_cipher_mode_ex(
997 					PHP_CRYPTO_CIPHER_MODE_VALUE(PHPC_THIS));
998 
999 		if (!enc && mode->auth_inlen_init) {
1000 			php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, TAG_VERIFY_FAILED));
1001 		} else {
1002 			php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, UPDATE_FAILED));
1003 		}
1004 		PHPC_STR_RELEASE(out);
1005 		RETURN_FALSE;
1006 	}
1007 	PHP_CRYPTO_CIPHER_SET_STATUS(PHPC_THIS, enc, UPDATE);
1008 	if (out_len > update_len) {
1009 		PHPC_STR_REALLOC(out, update_len);
1010 	}
1011 	PHPC_STR_VAL(out)[update_len] = 0;
1012 	PHPC_STR_RETURN(out);
1013 }
1014 /* }}} */
1015 
1016 /* {{{ php_crypto_cipher_finish */
php_crypto_cipher_finish(INTERNAL_FUNCTION_PARAMETERS,int enc)1017 static inline void php_crypto_cipher_finish(INTERNAL_FUNCTION_PARAMETERS, int enc)
1018 {
1019 	PHPC_THIS_DECLARE(crypto_cipher);
1020 	PHPC_STR_DECLARE(out);
1021 	const php_crypto_cipher_mode *mode;
1022 	int out_len, final_len = 0;
1023 
1024 	if (zend_parse_parameters_none() == FAILURE) {
1025 		return;
1026 	}
1027 
1028 	PHPC_THIS_FETCH(crypto_cipher);
1029 
1030 	/* check algorithm status */
1031 	if (enc && !PHP_CRYPTO_CIPHER_IS_INITIALIZED_FOR_ENCRYPTION(PHPC_THIS)) {
1032 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, FINISH_ENCRYPT_FORBIDDEN));
1033 		RETURN_FALSE;
1034 	} else if (!enc && !PHP_CRYPTO_CIPHER_IS_INITIALIZED_FOR_DECRYPTION(PHPC_THIS)) {
1035 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, FINISH_DECRYPT_FORBIDDEN));
1036 		RETURN_FALSE;
1037 	}
1038 
1039 	out_len = EVP_CIPHER_block_size(PHP_CRYPTO_CIPHER_ALG(PHPC_THIS));
1040 	PHPC_STR_ALLOC(out, out_len);
1041 
1042 	/* get mode info */
1043 	mode = php_crypto_get_cipher_mode_ex(PHP_CRYPTO_CIPHER_MODE_VALUE(PHPC_THIS));
1044 
1045 	/* finalize cipher context */
1046 	if ((enc || !mode->auth_inlen_init) && !EVP_CipherFinal_ex(PHP_CRYPTO_CIPHER_CTX(PHPC_THIS),
1047 			(unsigned char *) PHPC_STR_VAL(out), &final_len)) {
1048 		if (!enc && mode->auth_enc) {
1049 			php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, TAG_VERIFY_FAILED));
1050 		} else {
1051 			php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, FINISH_FAILED));
1052 		}
1053 		PHPC_STR_RELEASE(out);
1054 		RETURN_FALSE;
1055 	}
1056 	PHP_CRYPTO_CIPHER_SET_STATUS(PHPC_THIS, enc, FINAL);
1057 	if (out_len > final_len) {
1058 		PHPC_STR_REALLOC(out, final_len);
1059 	}
1060 	PHPC_STR_VAL(out)[final_len] = 0;
1061 	PHPC_STR_RETURN(out);
1062 }
1063 /* }}} */
1064 
1065 /* {{{ php_crypto_cipher_crypt */
php_crypto_cipher_crypt(INTERNAL_FUNCTION_PARAMETERS,int enc)1066 static inline void php_crypto_cipher_crypt(INTERNAL_FUNCTION_PARAMETERS, int enc)
1067 {
1068 	PHPC_THIS_DECLARE(crypto_cipher);
1069 	PHPC_STR_DECLARE(out);
1070 	const php_crypto_cipher_mode *mode;
1071 	char *data, *key, *iv = NULL;
1072 	phpc_str_size_t data_str_size, key_len, iv_len = 0;
1073 	int data_len, update_len, out_len, final_len = 0;
1074 
1075 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|s",
1076 			&data, &data_str_size, &key, &key_len, &iv, &iv_len) == FAILURE) {
1077 		return;
1078 	}
1079 
1080 	if (php_crypto_str_size_to_int(data_str_size, &data_len)) {
1081 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, INPUT_DATA_LENGTH_HIGH));
1082 		RETURN_FALSE;
1083 	}
1084 
1085 	PHPC_THIS = php_crypto_cipher_init_ex(getThis(), key, key_len, iv, iv_len, enc TSRMLS_CC);
1086 	if (PHPC_THIS == NULL) {
1087 		RETURN_FALSE;
1088 	}
1089 
1090 	/* do auth init */
1091 	if (php_crypto_cipher_auth_init(PHPC_THIS, data_len TSRMLS_CC) == FAILURE) {
1092 		RETURN_FALSE;
1093 	}
1094 
1095 	out_len = data_len + EVP_CIPHER_block_size(PHP_CRYPTO_CIPHER_ALG(PHPC_THIS));
1096 	PHPC_STR_ALLOC(out, out_len);
1097 
1098 	/* get mode info */
1099 	mode = php_crypto_get_cipher_mode_ex(PHP_CRYPTO_CIPHER_MODE_VALUE(PHPC_THIS));
1100 
1101 	/* update encryption context */
1102 	if (!EVP_CipherUpdate(PHP_CRYPTO_CIPHER_CTX(PHPC_THIS),
1103 			(unsigned char *) PHPC_STR_VAL(out), &update_len,
1104 			(unsigned char *) data, data_len)) {
1105 		if (!enc && mode->auth_inlen_init) {
1106 			php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, TAG_VERIFY_FAILED));
1107 		} else {
1108 			php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, UPDATE_FAILED));
1109 		}
1110 		PHPC_STR_RELEASE(out);
1111 		RETURN_FALSE;
1112 	}
1113 
1114 	/* finalize cipher context */
1115 	if ((enc || !mode->auth_inlen_init) && !EVP_CipherFinal_ex(PHP_CRYPTO_CIPHER_CTX(PHPC_THIS),
1116 			(unsigned char *) (PHPC_STR_VAL(out) + update_len), &final_len)) {
1117 		if (!enc && mode->auth_enc) {
1118 			php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, TAG_VERIFY_FAILED));
1119 		} else {
1120 			php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, FINISH_FAILED));
1121 		}
1122 		PHPC_STR_RELEASE(out);
1123 		RETURN_FALSE;
1124 	}
1125 	PHP_CRYPTO_CIPHER_SET_STATUS(PHPC_THIS, enc, FINAL);
1126 
1127 	final_len += update_len;
1128 	if (out_len > final_len) {
1129 		PHPC_STR_REALLOC(out, final_len);
1130 	}
1131 	PHPC_STR_VAL(out)[final_len] = 0;
1132 	PHPC_STR_RETURN(out);
1133 }
1134 /* }}} */
1135 
1136 /* {{{ proto static string Crypto\Cipher::getAlgorithms(bool $aliases = false,
1137 			string $prefix = null)
1138 	Returns cipher algorithms */
PHP_CRYPTO_METHOD(Cipher,getAlgorithms)1139 PHP_CRYPTO_METHOD(Cipher, getAlgorithms)
1140 {
1141 	php_crypto_object_fn_get_names(INTERNAL_FUNCTION_PARAM_PASSTHRU,
1142 			OBJ_NAME_TYPE_CIPHER_METH);
1143 }
1144 /* }}} */
1145 
1146 /* {{{ proto static bool Crypto\Cipher::hasAlgorithm(string $algorithm)
1147 	Finds out whether algorithm exists */
PHP_CRYPTO_METHOD(Cipher,hasAlgorithm)1148 PHP_CRYPTO_METHOD(Cipher, hasAlgorithm)
1149 {
1150 	char *algorithm;
1151 	phpc_str_size_t algorithm_len;
1152 
1153 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
1154 			&algorithm, &algorithm_len) == FAILURE) {
1155 		return;
1156 	}
1157 
1158 	if (php_crypto_get_cipher_algorithm(algorithm, algorithm_len)) {
1159 		RETURN_TRUE;
1160 	} else {
1161 		RETURN_FALSE;
1162 	}
1163 }
1164 /* }}} */
1165 
1166 /* {{{ proto static bool Crypto\Cipher::hasMode(int $mode)
1167 	Finds out whether the cipher mode is defined in the used OpenSSL library */
PHP_CRYPTO_METHOD(Cipher,hasMode)1168 PHP_CRYPTO_METHOD(Cipher, hasMode)
1169 {
1170 	phpc_long_t mode;
1171 
1172 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &mode) == FAILURE) {
1173 		return;
1174 	}
1175 
1176 	RETURN_BOOL(mode != PHP_CRYPTO_CIPHER_MODE_NOT_DEFINED && (mode & EVP_CIPH_MODE));
1177 }
1178 /* }}} */
1179 
1180 /* {{{ proto static Crypto\Cipher::__callStatic(string $name, array $arguments)
1181 	Cipher magic method for calling static methods */
PHP_CRYPTO_METHOD(Cipher,__callStatic)1182 PHP_CRYPTO_METHOD(Cipher, __callStatic)
1183 {
1184 	char *algorithm;
1185 	int argc;
1186 	phpc_str_size_t algorithm_len;
1187 	phpc_val *ppv_mode;
1188 	zval *pz_mode, *pz_key_size, *args;
1189 
1190 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa",
1191 			&algorithm, &algorithm_len, &args) == FAILURE) {
1192 		return;
1193 	}
1194 
1195 	argc = PHPC_HASH_NUM_ELEMENTS(Z_ARRVAL_P(args));
1196 	if (argc > 2) {
1197 		php_crypto_error_ex(PHP_CRYPTO_ERROR_ARGS(Cipher, STATIC_METHOD_TOO_MANY_ARGS), algorithm);
1198 		return;
1199 	}
1200 
1201 	object_init_ex(return_value, php_crypto_cipher_ce);
1202 
1203 	if (argc == 0) {
1204 		if (php_crypto_set_cipher_algorithm(
1205 				return_value, algorithm, algorithm_len TSRMLS_CC) == FAILURE) {
1206 			php_crypto_error_ex(PHP_CRYPTO_ERROR_ARGS(Cipher, STATIC_METHOD_NOT_FOUND), algorithm);
1207 		}
1208 		return;
1209 	}
1210 
1211 	PHPC_HASH_INTERNAL_POINTER_RESET(Z_ARRVAL_P(args));
1212 	PHPC_HASH_GET_CURRENT_DATA(Z_ARRVAL_P(args), ppv_mode);
1213 	PHPC_PVAL_TO_PZVAL(ppv_mode, pz_mode);
1214 	if (argc == 1) {
1215 		pz_key_size = NULL;
1216 	} else {
1217 		phpc_val *ppv_key_size;
1218 		PHPC_HASH_MOVE_FORWARD(Z_ARRVAL_P(args));
1219 		PHPC_HASH_GET_CURRENT_DATA(Z_ARRVAL_P(args), ppv_key_size);
1220 		PHPC_PVAL_TO_PZVAL(ppv_key_size, pz_key_size);
1221 	}
1222 	php_crypto_set_cipher_algorithm_from_params_ex(
1223 			return_value, algorithm, algorithm_len, pz_mode, pz_key_size, 1 TSRMLS_CC);
1224 }
1225 /* }}} */
1226 
1227 /* {{{ proto Crypto\Cipher::__construct(string $algorithm, int $mode = NULL, string $key_size = NULL)
1228 	Cipher constructor */
PHP_CRYPTO_METHOD(Cipher,__construct)1229 PHP_CRYPTO_METHOD(Cipher, __construct)
1230 {
1231 	char *algorithm, *algorithm_uc;
1232 	phpc_str_size_t algorithm_len;
1233 	zval *mode = NULL, *key_size = NULL;
1234 
1235 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|zz",
1236 			&algorithm, &algorithm_len, &mode, &key_size) == FAILURE) {
1237 		return;
1238 	}
1239 
1240 	algorithm_uc = estrdup(algorithm);
1241 	php_crypto_set_cipher_algorithm_from_params(
1242 			getThis(), algorithm_uc, strlen(algorithm_uc), mode, key_size TSRMLS_CC);
1243 	efree(algorithm_uc);
1244 }
1245 /* }}} */
1246 
1247 /* {{{ proto string Crypto\Cipher::getAlgorithmName()
1248 	Returns cipher algorithm string */
PHP_CRYPTO_METHOD(Cipher,getAlgorithmName)1249 PHP_CRYPTO_METHOD(Cipher, getAlgorithmName)
1250 {
1251 	zval *algorithm;
1252 	PHPC_READ_PROPERTY_RV_DECLARE;
1253 
1254 	algorithm = PHP_CRYPTO_CIPHER_GET_ALGORITHM_NAME_EX(getThis());
1255 	RETURN_ZVAL(algorithm, 1, 0);
1256 }
1257 /* }}} */
1258 
1259 /* {{{ proto bool Crypto\Cipher::encryptInit(string $key, string $iv = null)
1260 	Initializes cipher encryption */
PHP_CRYPTO_METHOD(Cipher,encryptInit)1261 PHP_CRYPTO_METHOD(Cipher, encryptInit)
1262 {
1263 	php_crypto_cipher_init(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1264 }
1265 
1266 /* {{{ proto string Crypto\Cipher::encryptUpdate(string $data)
1267 	Updates cipher encryption */
PHP_CRYPTO_METHOD(Cipher,encryptUpdate)1268 PHP_CRYPTO_METHOD(Cipher, encryptUpdate)
1269 {
1270 	php_crypto_cipher_update(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1271 }
1272 
1273 /* {{{ proto string Crypto\Cipher::encryptFinish()
1274 	Finalizes cipher encryption */
PHP_CRYPTO_METHOD(Cipher,encryptFinish)1275 PHP_CRYPTO_METHOD(Cipher, encryptFinish)
1276 {
1277 	php_crypto_cipher_finish(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1278 }
1279 
1280 /* {{{ proto string Crypto\Cipher::encrypt(string $data, string $key, string $iv = null)
1281 	Encrypts text to ciphertext */
PHP_CRYPTO_METHOD(Cipher,encrypt)1282 PHP_CRYPTO_METHOD(Cipher, encrypt)
1283 {
1284 	php_crypto_cipher_crypt(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1285 }
1286 
1287 /* {{{ proto void Crypto\Cipher::decryptInit(string $key, string $iv = null)
1288 	Initializes cipher decryption */
PHP_CRYPTO_METHOD(Cipher,decryptInit)1289 PHP_CRYPTO_METHOD(Cipher, decryptInit)
1290 {
1291 	php_crypto_cipher_init(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1292 }
1293 
1294 /* {{{ proto string Crypto\Cipher::decryptUpdate(string $data)
1295 	Updates cipher decryption */
PHP_CRYPTO_METHOD(Cipher,decryptUpdate)1296 PHP_CRYPTO_METHOD(Cipher, decryptUpdate)
1297 {
1298 	php_crypto_cipher_update(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1299 }
1300 
1301 /* {{{ proto string Crypto\Cipher::decryptFinish()
1302 	Finalizes cipher decryption */
PHP_CRYPTO_METHOD(Cipher,decryptFinish)1303 PHP_CRYPTO_METHOD(Cipher, decryptFinish)
1304 {
1305 	php_crypto_cipher_finish(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1306 }
1307 
1308 /* {{{ proto string Crypto\Cipher::decrypt(string $data, string $key, string $iv = null)
1309 	Decrypts ciphertext to decrypted text */
PHP_CRYPTO_METHOD(Cipher,decrypt)1310 PHP_CRYPTO_METHOD(Cipher, decrypt)
1311 {
1312 	php_crypto_cipher_crypt(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1313 }
1314 
1315 /* {{{ proto int Crypto\Cipher::getBlockSize()
1316 	Returns cipher block size */
PHP_CRYPTO_METHOD(Cipher,getBlockSize)1317 PHP_CRYPTO_METHOD(Cipher, getBlockSize)
1318 {
1319 	PHPC_THIS_DECLARE(crypto_cipher);
1320 
1321 	if (zend_parse_parameters_none() == FAILURE) {
1322 		return;
1323 	}
1324 
1325 	PHPC_THIS_FETCH(crypto_cipher);
1326 	RETURN_LONG(EVP_CIPHER_block_size(PHP_CRYPTO_CIPHER_ALG(PHPC_THIS)));
1327 }
1328 
1329 /* {{{ proto int Crypto\Cipher::getKeyLength()
1330 	Returns cipher key length */
PHP_CRYPTO_METHOD(Cipher,getKeyLength)1331 PHP_CRYPTO_METHOD(Cipher, getKeyLength)
1332 {
1333 	PHPC_THIS_DECLARE(crypto_cipher);
1334 
1335 	if (zend_parse_parameters_none() == FAILURE) {
1336 		return;
1337 	}
1338 
1339 	PHPC_THIS_FETCH(crypto_cipher);
1340 	RETURN_LONG(EVP_CIPHER_key_length(PHP_CRYPTO_CIPHER_ALG(PHPC_THIS)));
1341 }
1342 
1343 /* {{{ proto int Crypto\Cipher::getIVLength()
1344 	Returns cipher IV length */
PHP_CRYPTO_METHOD(Cipher,getIVLength)1345 PHP_CRYPTO_METHOD(Cipher, getIVLength)
1346 {
1347 	PHPC_THIS_DECLARE(crypto_cipher);
1348 
1349 	if (zend_parse_parameters_none() == FAILURE) {
1350 		return;
1351 	}
1352 
1353 	PHPC_THIS_FETCH(crypto_cipher);
1354 	RETURN_LONG(EVP_CIPHER_iv_length(PHP_CRYPTO_CIPHER_ALG(PHPC_THIS)));
1355 }
1356 
1357 /* {{{ proto int Crypto\Cipher::getMode()
1358 	Returns cipher mode */
PHP_CRYPTO_METHOD(Cipher,getMode)1359 PHP_CRYPTO_METHOD(Cipher, getMode)
1360 {
1361 	PHPC_THIS_DECLARE(crypto_cipher);
1362 
1363 	if (zend_parse_parameters_none() == FAILURE) {
1364 		return;
1365 	}
1366 
1367 	PHPC_THIS_FETCH(crypto_cipher);
1368 	RETURN_LONG(PHP_CRYPTO_CIPHER_MODE_VALUE(PHPC_THIS));
1369 }
1370 /* }}} */
1371 
1372 /* {{{ proto string Crypto\Cipher::getTag()
1373 	Returns authentication tag */
PHP_CRYPTO_METHOD(Cipher,getTag)1374 PHP_CRYPTO_METHOD(Cipher, getTag)
1375 {
1376 	PHPC_THIS_DECLARE(crypto_cipher);
1377 	const php_crypto_cipher_mode *mode;
1378 	PHPC_STR_DECLARE(tag);
1379 	int tag_len;
1380 
1381 	if (zend_parse_parameters_none() == FAILURE) {
1382 		return;
1383 	}
1384 
1385 	PHPC_THIS_FETCH(crypto_cipher);
1386 	mode = php_crypto_get_cipher_mode_ex(PHP_CRYPTO_CIPHER_MODE_VALUE(PHPC_THIS));
1387 	if (php_crypto_cipher_is_mode_authenticated_ex(mode TSRMLS_CC) == FAILURE) {
1388 		RETURN_FALSE;
1389 	}
1390 
1391 	if (PHPC_THIS->status != PHP_CRYPTO_CIPHER_STATUS_ENCRYPT_FINAL) {
1392 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, TAG_GETTER_FORBIDDEN));
1393 		RETURN_FALSE;
1394 	}
1395 
1396 	tag_len = PHP_CRYPTO_CIPHER_TAG_LEN(PHPC_THIS);
1397 	PHPC_STR_ALLOC(tag, tag_len);
1398 	PHPC_STR_VAL(tag)[tag_len] = 0;
1399 
1400 	if (!EVP_CIPHER_CTX_ctrl(PHP_CRYPTO_CIPHER_CTX(PHPC_THIS),
1401 			mode->auth_get_tag_flag, tag_len, PHPC_STR_VAL(tag))) {
1402 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, TAG_GETTER_FAILED));
1403 		RETURN_FALSE;
1404 	}
1405 
1406 	PHPC_STR_RETURN(tag);
1407 }
1408 /* }}} */
1409 
1410 /* {{{ proto bool Crypto\Cipher::setTag(string $tag)
1411 	Sets authentication tag */
PHP_CRYPTO_METHOD(Cipher,setTag)1412 PHP_CRYPTO_METHOD(Cipher, setTag)
1413 {
1414 	PHPC_THIS_DECLARE(crypto_cipher);
1415 	const php_crypto_cipher_mode *mode;
1416 	char *tag;
1417 	phpc_str_size_t tag_str_size;
1418 	int tag_len;
1419 
1420 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &tag, &tag_str_size) == FAILURE) {
1421 		return;
1422 	}
1423 
1424 	PHPC_THIS_FETCH(crypto_cipher);
1425 	mode = php_crypto_get_cipher_mode_ex(PHP_CRYPTO_CIPHER_MODE_VALUE(PHPC_THIS));
1426 	if (php_crypto_cipher_is_mode_authenticated_ex(mode TSRMLS_CC) == FAILURE ||
1427 			php_crypto_str_size_to_int(tag_str_size, &tag_len) == FAILURE ||
1428 			php_crypto_cipher_check_tag_len(tag_len TSRMLS_CC) == FAILURE) {
1429 		RETURN_FALSE;
1430 	}
1431 
1432 	if (PHPC_THIS->status == PHP_CRYPTO_CIPHER_STATUS_CLEAR) {
1433 		if (!PHP_CRYPTO_CIPHER_TAG(PHPC_THIS)) {
1434 			PHP_CRYPTO_CIPHER_TAG(PHPC_THIS) = emalloc(tag_len + 1);
1435 		} else if (PHP_CRYPTO_CIPHER_TAG_LEN(PHPC_THIS) < tag_len) {
1436 			PHP_CRYPTO_CIPHER_TAG(PHPC_THIS) = erealloc(
1437 					PHP_CRYPTO_CIPHER_TAG(PHPC_THIS), tag_len + 1);
1438 		}
1439 		memcpy(PHP_CRYPTO_CIPHER_TAG(PHPC_THIS), tag, tag_len + 1);
1440 		PHP_CRYPTO_CIPHER_TAG_LEN(PHPC_THIS) = tag_len;
1441 	} else if (PHPC_THIS->status == PHP_CRYPTO_CIPHER_STATUS_DECRYPT_INIT) {
1442 		php_crypto_cipher_set_tag(PHP_CRYPTO_CIPHER_CTX(PHPC_THIS), mode,
1443 				(unsigned char *) tag, tag_len TSRMLS_CC);
1444 	} else {
1445 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, TAG_SETTER_FORBIDDEN));
1446 		RETURN_FALSE;
1447 	}
1448 	RETURN_TRUE;
1449 }
1450 /* }}} */
1451 
1452 /* {{{ proto bool Crypto\Cipher::setTagLength(int $tag_length)
1453 	Set authentication tag length */
PHP_CRYPTO_METHOD(Cipher,setTagLength)1454 PHP_CRYPTO_METHOD(Cipher, setTagLength)
1455 {
1456 	PHPC_THIS_DECLARE(crypto_cipher);
1457 	const php_crypto_cipher_mode *mode;
1458 	phpc_long_t tag_len_long;
1459 	int tag_len;
1460 
1461 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &tag_len_long) == FAILURE) {
1462 		return;
1463 	}
1464 
1465 	PHPC_THIS_FETCH(crypto_cipher);
1466 	mode = php_crypto_get_cipher_mode_ex(PHP_CRYPTO_CIPHER_MODE_VALUE(PHPC_THIS));
1467 	if (php_crypto_cipher_is_mode_authenticated_ex(mode TSRMLS_CC) == FAILURE ||
1468 			PHP_CRYPTO_CIPHER_TAG(PHPC_THIS) ||
1469 			php_crypto_long_to_int(tag_len_long, &tag_len) == FAILURE ||
1470 			php_crypto_cipher_check_tag_len(tag_len TSRMLS_CC) == FAILURE) {
1471 		RETURN_FALSE;
1472 	}
1473 
1474 	if (PHPC_THIS->status != PHP_CRYPTO_CIPHER_STATUS_ENCRYPT_INIT &&
1475 			PHPC_THIS->status != PHP_CRYPTO_CIPHER_STATUS_CLEAR) {
1476 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, TAG_LENGTH_SETTER_FORBIDDEN));
1477 		RETURN_FALSE;
1478 	}
1479 
1480 	PHP_CRYPTO_CIPHER_TAG_LEN(PHPC_THIS) = tag_len;
1481 
1482 	RETURN_TRUE;
1483 }
1484 /* }}} */
1485 
1486 /* {{{ proto bool Crypto\Cipher::setAAD(string $aad)
1487 	Sets additional application data for authenticated encryption */
PHP_CRYPTO_METHOD(Cipher,setAAD)1488 PHP_CRYPTO_METHOD(Cipher, setAAD)
1489 {
1490 	PHPC_THIS_DECLARE(crypto_cipher);
1491 	char *aad;
1492 	phpc_str_size_t aad_str_size;
1493 	int aad_len;
1494 
1495 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &aad, &aad_str_size) == FAILURE) {
1496 		return;
1497 	}
1498 
1499 	PHPC_THIS_FETCH(crypto_cipher);
1500 	if (php_crypto_cipher_is_mode_authenticated(PHPC_THIS TSRMLS_CC) == FAILURE) {
1501 		RETURN_FALSE;
1502 	}
1503 
1504 	if (php_crypto_str_size_to_int(aad_str_size, &aad_len) == FAILURE) {
1505 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, AAD_LENGTH_HIGH));
1506 		RETURN_FALSE;
1507 	} else if (PHPC_THIS->status == PHP_CRYPTO_CIPHER_STATUS_CLEAR ||
1508 			PHPC_THIS->status == PHP_CRYPTO_CIPHER_STATUS_ENCRYPT_INIT ||
1509 			PHPC_THIS->status == PHP_CRYPTO_CIPHER_STATUS_DECRYPT_INIT) {
1510 		if (!PHP_CRYPTO_CIPHER_AAD(PHPC_THIS)) {
1511 			PHP_CRYPTO_CIPHER_AAD(PHPC_THIS) = emalloc(aad_len + 1);
1512 		} else if (PHP_CRYPTO_CIPHER_AAD_LEN(PHPC_THIS) < aad_len) {
1513 			PHP_CRYPTO_CIPHER_AAD(PHPC_THIS) = erealloc(
1514 					PHP_CRYPTO_CIPHER_AAD(PHPC_THIS), aad_len + 1);
1515 		}
1516 		memcpy(PHP_CRYPTO_CIPHER_AAD(PHPC_THIS), aad, aad_len + 1);
1517 		PHP_CRYPTO_CIPHER_AAD_LEN(PHPC_THIS) = aad_len;
1518 	} else {
1519 		php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Cipher, AAD_SETTER_FORBIDDEN));
1520 		RETURN_FALSE;
1521 	}
1522 	RETURN_TRUE;
1523 }
1524 /* }}} */
1525