
#ifdef HAVE_CONFIG_H
#include "../../ext_config.h"
#endif

#include <php.h>
#include "../../php_ext.h"
#include "../../ext.h"

#include <Zend/zend_operators.h>
#include <Zend/zend_exceptions.h>
#include <Zend/zend_interfaces.h>

#include "kernel/main.h"
#include "kernel/operators.h"
#include "kernel/memory.h"
#include "kernel/fcall.h"
#include "kernel/object.h"
#include "kernel/exception.h"
#include "kernel/array.h"
#include "kernel/string.h"
#include "kernel/concat.h"


/**
 * This file is part of the Phalcon Framework.
 *
 * (c) Phalcon Team <team@phalcon.io>
 *
 * For the full copyright and license information, please view the LICENSE.txt
 * file that was distributed with this source code.
 */
/**
 * Provides encryption capabilities to Phalcon applications.
 *
 * ```php
 * use Phalcon\Crypt;
 *
 * $crypt = new Crypt();
 *
 * $crypt->setCipher("aes-256-ctr");
 *
 * $key  =
 * "T4\xb1\x8d\xa9\x98\x05\\\x8c\xbe\x1d\x07&[\x99\x18\xa4~Lc1\xbeW\xb3";
 * $input = "The message to be encrypted";
 *
 * $encrypted = $crypt->encrypt($input, $key);
 *
 * echo $crypt->decrypt($encrypted, $key);
 * ```
 */
ZEPHIR_INIT_CLASS(Phalcon_Encryption_Crypt)
{
	ZEPHIR_REGISTER_CLASS(Phalcon\\Encryption, Crypt, phalcon, encryption_crypt, phalcon_encryption_crypt_method_entry, 0);

	/**
	 * @var string
	 */
	zend_declare_property_string(phalcon_encryption_crypt_ce, SL("authData"), "", ZEND_ACC_PROTECTED);
	/**
	 * @var string
	 */
	zend_declare_property_string(phalcon_encryption_crypt_ce, SL("authTag"), "", ZEND_ACC_PROTECTED);
	/**
	 * @var int
	 */
	zend_declare_property_long(phalcon_encryption_crypt_ce, SL("authTagLength"), 16, ZEND_ACC_PROTECTED);
	/**
	 * Available cipher methods.
	 *
	 * @var array
	 */
	zend_declare_property_null(phalcon_encryption_crypt_ce, SL("availableCiphers"), ZEND_ACC_PROTECTED);
	/**
	 * @var string
	 */
	zend_declare_property_string(phalcon_encryption_crypt_ce, SL("cipher"), "aes-256-cfb", ZEND_ACC_PROTECTED);
	/**
	 * The name of hashing algorithm.
	 *
	 * @var string
	 */
	zend_declare_property_string(phalcon_encryption_crypt_ce, SL("hashAlgorithm"), "sha256", ZEND_ACC_PROTECTED);
	/**
	 * Memoized `strlen(hash($algo, "", true))` results, keyed by
	 * algorithm name. The hash output length is deterministic for a
	 * given algorithm, so this collapses the per-decrypt strlen+hash
	 * call to a single hash lookup after warm-up.
	 *
	 * @var array
	 */
	zend_declare_property_null(phalcon_encryption_crypt_ce, SL("hashLengthCache"), ZEND_ACC_PROTECTED);
	/**
	 * The cipher iv length.
	 *
	 * @var int
	 */
	zend_declare_property_long(phalcon_encryption_crypt_ce, SL("ivLength"), 16, ZEND_ACC_PROTECTED);
	/**
	 * @var string
	 */
	zend_declare_property_string(phalcon_encryption_crypt_ce, SL("key"), "", ZEND_ACC_PROTECTED);
	/**
	 * @var int
	 */
	zend_declare_property_long(phalcon_encryption_crypt_ce, SL("padding"), 0, ZEND_ACC_PROTECTED);
	/**
	 * @var PadFactory
	 */
	zend_declare_property_null(phalcon_encryption_crypt_ce, SL("padFactory"), ZEND_ACC_PROTECTED);
	/**
	 * Whether calculating message digest enabled or not.
	 *
	 * @var bool
	 */
	zend_declare_property_bool(phalcon_encryption_crypt_ce, SL("useSigning"), 1, ZEND_ACC_PROTECTED);
	phalcon_encryption_crypt_ce->create_object = zephir_init_properties_Phalcon_Encryption_Crypt;
	/**
	 * @var string
	 */
	zephir_declare_class_constant_string(phalcon_encryption_crypt_ce, SL("DEFAULT_ALGORITHM"), "sha256");

	/**
	 * @var string
	 */
	zephir_declare_class_constant_string(phalcon_encryption_crypt_ce, SL("DEFAULT_CIPHER"), "aes-256-cfb");

	/**
	 * Padding
	 *
	 * @var int
	 */
	zephir_declare_class_constant_long(phalcon_encryption_crypt_ce, SL("PADDING_ANSI_X_923"), 1);

	/**
	 * @var int
	 */
	zephir_declare_class_constant_long(phalcon_encryption_crypt_ce, SL("PADDING_DEFAULT"), 0);

	/**
	 * @var int
	 */
	zephir_declare_class_constant_long(phalcon_encryption_crypt_ce, SL("PADDING_ISO_10126"), 3);

	/**
	 * @var int
	 */
	zephir_declare_class_constant_long(phalcon_encryption_crypt_ce, SL("PADDING_ISO_IEC_7816_4"), 4);

	/**
	 * @var int
	 */
	zephir_declare_class_constant_long(phalcon_encryption_crypt_ce, SL("PADDING_PKCS7"), 2);

	/**
	 * @var int
	 */
	zephir_declare_class_constant_long(phalcon_encryption_crypt_ce, SL("PADDING_SPACE"), 6);

	/**
	 * @var int
	 */
	zephir_declare_class_constant_long(phalcon_encryption_crypt_ce, SL("PADDING_ZERO"), 5);

	zend_class_implements(phalcon_encryption_crypt_ce, 1, phalcon_encryption_crypt_cryptinterface_ce);
	return SUCCESS;
}

/**
 * Crypt constructor.
 *
 * @param string          $cipher
 * @param bool            $useSigning
 * @param PadFactory|null $padFactory
 *
 * @throws Exception
 */
PHP_METHOD(Phalcon_Encryption_Crypt, __construct)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zend_bool useSigning;
	zval *cipher_param = NULL, *useSigning_param = NULL, *padFactory = NULL, padFactory_sub, __$null, _0, _1, _2, _3;
	zval cipher;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&cipher);
	ZVAL_UNDEF(&padFactory_sub);
	ZVAL_NULL(&__$null);
	ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_1);
	ZVAL_UNDEF(&_2);
	ZVAL_UNDEF(&_3);
	static zend_string *_zephir_prop_0 = NULL;
	static zend_string *_zephir_prop_1 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("padFactory", 10, 1);
	}
	if (UNEXPECTED(!_zephir_prop_1)) {
		_zephir_prop_1 = zend_string_init("hashAlgorithm", 13, 1);
	}

	bool is_null_true = 1;
	ZEND_PARSE_PARAMETERS_START(0, 3)
		Z_PARAM_OPTIONAL
		Z_PARAM_ZVAL(cipher_param)
		Z_PARAM_BOOL(useSigning)
		Z_PARAM_OBJECT_OF_CLASS_OR_NULL(padFactory, phalcon_encryption_crypt_padfactory_ce)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_fetch_params(1, 0, 3, &cipher_param, &useSigning_param, &padFactory);
	if (!cipher_param) {
		ZEPHIR_INIT_VAR(&cipher);
		ZVAL_STRING(&cipher, "aes-256-cfb");
	} else {
		zephir_get_strval(&cipher, cipher_param);
	}
	if (!useSigning_param) {
		useSigning = 1;
	} else {
		}
	if (!padFactory) {
		padFactory = &padFactory_sub;
		ZEPHIR_CPY_WRT(padFactory, &__$null);
	} else {
		ZEPHIR_SEPARATE_PARAM(padFactory);
	}
	if (Z_TYPE_P(padFactory) == IS_NULL) {
		ZEPHIR_INIT_NVAR(padFactory);
		object_init_ex(padFactory, phalcon_encryption_crypt_padfactory_ce);
		ZEPHIR_CALL_METHOD(NULL, padFactory, "__construct", NULL, 0);
		zephir_check_call_status();
	}
	zephir_update_property_zval_cached(this_ptr, _zephir_prop_0, 683, padFactory);
	ZEPHIR_INIT_VAR(&_0);
	ZEPHIR_INIT_NVAR(&_0);
	ZVAL_STRING(&_0, "sha256");
	zephir_update_property_zval_cached(this_ptr, _zephir_prop_1, 684, &_0);
	ZEPHIR_CALL_METHOD(&_1, this_ptr, "initializeavailableciphers", NULL, 0);
	zephir_check_call_status();
	ZEPHIR_CALL_METHOD(&_2, &_1, "setcipher", NULL, 0, &cipher);
	zephir_check_call_status();
	if (useSigning) {
		ZVAL_BOOL(&_3, 1);
	} else {
		ZVAL_BOOL(&_3, 0);
	}
	ZEPHIR_CALL_METHOD(NULL, &_2, "usesigning", NULL, 0, &_3);
	zephir_check_call_status();
	ZEPHIR_MM_RESTORE();
}

/**
 * Decrypts an encrypted text.
 *
 * ```php
 * $encrypted = $crypt->decrypt(
 *     $encrypted,
 *     "T4\xb1\x8d\xa9\x98\x05\\\x8c\xbe\x1d\x07&[\x99\x18\xa4~Lc1\xbeW\xb3"
 * );
 * ```
 *
 * @param string      $input
 * @param string|null $key
 *
 * @return string
 * @throws Exception
 * @throws InvalidDecryptLength
 * @throws Mismatch
 */
PHP_METHOD(Phalcon_Encryption_Crypt, decrypt)
{
	zend_bool _20$$9;
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval input_zv, key_zv, __$null, blockSize, cipher, cipherText, decrypted, decryptKey, digest, hashAlgorithm, hashLength, iv, ivLength, mode, _1, _2, _3, _12, _0$$4, _4$$5, _5$$6, _9$$6, _10$$6, _6$$7, _7$$7, _8$$7, _11$$8, _13$$9, _14$$9, _15$$9, _16$$9, _17$$9, _18$$9, _19$$9;
	zend_string *input = NULL, *key = NULL;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&input_zv);
	ZVAL_UNDEF(&key_zv);
	ZVAL_NULL(&__$null);
	ZVAL_UNDEF(&blockSize);
	ZVAL_UNDEF(&cipher);
	ZVAL_UNDEF(&cipherText);
	ZVAL_UNDEF(&decrypted);
	ZVAL_UNDEF(&decryptKey);
	ZVAL_UNDEF(&digest);
	ZVAL_UNDEF(&hashAlgorithm);
	ZVAL_UNDEF(&hashLength);
	ZVAL_UNDEF(&iv);
	ZVAL_UNDEF(&ivLength);
	ZVAL_UNDEF(&mode);
	ZVAL_UNDEF(&_1);
	ZVAL_UNDEF(&_2);
	ZVAL_UNDEF(&_3);
	ZVAL_UNDEF(&_12);
	ZVAL_UNDEF(&_0$$4);
	ZVAL_UNDEF(&_4$$5);
	ZVAL_UNDEF(&_5$$6);
	ZVAL_UNDEF(&_9$$6);
	ZVAL_UNDEF(&_10$$6);
	ZVAL_UNDEF(&_6$$7);
	ZVAL_UNDEF(&_7$$7);
	ZVAL_UNDEF(&_8$$7);
	ZVAL_UNDEF(&_11$$8);
	ZVAL_UNDEF(&_13$$9);
	ZVAL_UNDEF(&_14$$9);
	ZVAL_UNDEF(&_15$$9);
	ZVAL_UNDEF(&_16$$9);
	ZVAL_UNDEF(&_17$$9);
	ZVAL_UNDEF(&_18$$9);
	ZVAL_UNDEF(&_19$$9);
	static zend_string *_zephir_prop_0 = NULL;
	static zend_string *_zephir_prop_1 = NULL;
	static zend_string *_zephir_prop_2 = NULL;
	static zend_string *_zephir_prop_3 = NULL;
	static zend_string *_zephir_prop_4 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("key", 3, 1);
	}
	if (UNEXPECTED(!_zephir_prop_1)) {
		_zephir_prop_1 = zend_string_init("cipher", 6, 1);
	}
	if (UNEXPECTED(!_zephir_prop_2)) {
		_zephir_prop_2 = zend_string_init("ivLength", 8, 1);
	}
	if (UNEXPECTED(!_zephir_prop_3)) {
		_zephir_prop_3 = zend_string_init("useSigning", 10, 1);
	}
	if (UNEXPECTED(!_zephir_prop_4)) {
		_zephir_prop_4 = zend_string_init("hashLengthCache", 15, 1);
	}

	bool is_null_true = 1;
	ZEND_PARSE_PARAMETERS_START(1, 2)
		Z_PARAM_STR(input)
		Z_PARAM_OPTIONAL
		Z_PARAM_STR_OR_NULL(key)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_memory_observe(&input_zv);
	ZVAL_STR_COPY(&input_zv, input);
	if (!key) {
		ZEPHIR_INIT_VAR(&key_zv);
	} else {
		zephir_memory_observe(&key_zv);
	ZVAL_STR_COPY(&key_zv, key);
	}
	zephir_memory_observe(&decryptKey);
	zephir_read_property_cached(&decryptKey, this_ptr, _zephir_prop_0, 685, PH_NOISY_CC);
	if (1 != ZEPHIR_IS_EMPTY(&key_zv)) {
		ZEPHIR_CPY_WRT(&decryptKey, &key_zv);
	}
	if (1 == ZEPHIR_IS_EMPTY(&decryptKey)) {
		ZEPHIR_INIT_VAR(&_0$$4);
		object_init_ex(&_0$$4, phalcon_encryption_crypt_exception_emptydecryptionkey_ce);
		ZEPHIR_CALL_METHOD(NULL, &_0$$4, "__construct", NULL, 0);
		zephir_check_call_status();
		zephir_throw_exception_debug(&_0$$4, "phalcon/Encryption/Crypt.zep", 230);
		ZEPHIR_MM_RESTORE();
		return;
	}
	zephir_read_property_cached(&_1, this_ptr, _zephir_prop_1, 686, PH_NOISY_CC | PH_READONLY);
	ZEPHIR_CPY_WRT(&cipher, &_1);
	zephir_read_property_cached(&_1, this_ptr, _zephir_prop_2, 687, PH_NOISY_CC | PH_READONLY);
	ZEPHIR_CPY_WRT(&ivLength, &_1);
	ZEPHIR_INIT_VAR(&_2);
	ZVAL_STRING(&_2, "cipher");
	ZEPHIR_CALL_METHOD(NULL, this_ptr, "checkcipherhashisavailable", NULL, 0, &cipher, &_2);
	zephir_check_call_status();
	ZEPHIR_CALL_METHOD(&_3, this_ptr, "isvaliddecryptlength", NULL, 0, &input_zv);
	zephir_check_call_status();
	if (!ZEPHIR_IS_TRUE_IDENTICAL(&_3)) {
		ZEPHIR_INIT_VAR(&_4$$5);
		object_init_ex(&_4$$5, phalcon_encryption_crypt_exception_invaliddecryptlength_ce);
		ZEPHIR_CALL_METHOD(NULL, &_4$$5, "__construct", NULL, 0);
		zephir_check_call_status();
		zephir_throw_exception_debug(&_4$$5, "phalcon/Encryption/Crypt.zep", 239);
		ZEPHIR_MM_RESTORE();
		return;
	}
	ZEPHIR_CALL_METHOD(&mode, this_ptr, "getmode", NULL, 0);
	zephir_check_call_status();
	ZEPHIR_CALL_METHOD(&blockSize, this_ptr, "getblocksize", NULL, 0, &mode);
	zephir_check_call_status();
	ZVAL_LONG(&_1, 0);
	ZEPHIR_INIT_NVAR(&_2);
	ZVAL_STRING(&_2, "8bit");
	ZEPHIR_CALL_FUNCTION(&iv, "mb_substr", NULL, 307, &input_zv, &_1, &ivLength, &_2);
	zephir_check_call_status();
	ZEPHIR_INIT_VAR(&digest);
	ZVAL_STRING(&digest, "");
	ZEPHIR_CALL_METHOD(&hashAlgorithm, this_ptr, "gethashalgorithm", NULL, 0);
	zephir_check_call_status();
	zephir_read_property_cached(&_1, this_ptr, _zephir_prop_3, 688, PH_NOISY_CC | PH_READONLY);
	if (ZEPHIR_IS_TRUE_IDENTICAL(&_1)) {
		zephir_memory_observe(&hashLength);
		zephir_read_property_cached(&_5$$6, this_ptr, _zephir_prop_4, 689, PH_NOISY_CC | PH_READONLY);
		if (!(zephir_array_isset_fetch(&hashLength, &_5$$6, &hashAlgorithm, 0))) {
			ZEPHIR_INIT_VAR(&_7$$7);
			ZVAL_STRING(&_7$$7, "");
			ZVAL_BOOL(&_8$$7, 1);
			ZEPHIR_CALL_METHOD(&_6$$7, this_ptr, "phphash", NULL, 0, &hashAlgorithm, &_7$$7, &_8$$7);
			zephir_check_call_status();
			ZEPHIR_INIT_NVAR(&hashLength);
			ZVAL_LONG(&hashLength, zephir_fast_strlen_ev(&_6$$7));
			zephir_update_property_array(this_ptr, SL("hashLengthCache"), &hashAlgorithm, &hashLength);
		}
		ZEPHIR_INIT_VAR(&_9$$6);
		ZVAL_STRING(&_9$$6, "8bit");
		ZEPHIR_CALL_FUNCTION(&digest, "mb_substr", NULL, 307, &input_zv, &ivLength, &hashLength, &_9$$6);
		zephir_check_call_status();
		ZEPHIR_INIT_VAR(&_10$$6);
		zephir_add_function(&_10$$6, &ivLength, &hashLength);
		ZEPHIR_INIT_NVAR(&_9$$6);
		ZVAL_STRING(&_9$$6, "8bit");
		ZEPHIR_CALL_FUNCTION(&cipherText, "mb_substr", NULL, 307, &input_zv, &_10$$6, &__$null, &_9$$6);
		zephir_check_call_status();
	} else {
		ZEPHIR_INIT_VAR(&_11$$8);
		ZVAL_STRING(&_11$$8, "8bit");
		ZEPHIR_CALL_FUNCTION(&cipherText, "mb_substr", NULL, 307, &input_zv, &ivLength, &__$null, &_11$$8);
		zephir_check_call_status();
	}
	zephir_read_property_cached(&_12, this_ptr, _zephir_prop_3, 688, PH_NOISY_CC | PH_READONLY);
	if (ZEPHIR_IS_TRUE_IDENTICAL(&_12)) {

		/* try_start_1: */

			ZEPHIR_CALL_METHOD(&decrypted, this_ptr, "decryptgcmccmauth", NULL, 0, &mode, &cipherText, &decryptKey, &iv);
			zephir_check_call_status_or_jump(try_end_1);

		try_end_1:

		if (EG(exception)) {
			ZEPHIR_INIT_VAR(&_13$$9);
			ZVAL_OBJ(&_13$$9, EG(exception));
			Z_ADDREF_P(&_13$$9);
			ZEPHIR_INIT_VAR(&_14$$9);
			if (zephir_instance_of_ev(&_13$$9, phalcon_encryption_crypt_exception_decryptionfailed_ce)) {
				zend_clear_exception();
				ZEPHIR_CPY_WRT(&_14$$9, &_13$$9);
				ZEPHIR_INIT_NVAR(&decrypted);
				ZVAL_BOOL(&decrypted, 0);
			}
		}
		ZEPHIR_INIT_VAR(&_17$$9);
		if (ZEPHIR_IS_FALSE_IDENTICAL(&decrypted)) {
			ZVAL_LONG(&_18$$9, 0);
			ZEPHIR_CALL_FUNCTION(&_19$$9, "chr", NULL, 0, &_18$$9);
			zephir_check_call_status();
			ZVAL_LONG(&_18$$9, zephir_fast_strlen_ev(&cipherText));
			ZEPHIR_CALL_FUNCTION(&_17$$9, "str_repeat", NULL, 7, &_19$$9, &_18$$9);
			zephir_check_call_status();
		} else {
			ZEPHIR_CPY_WRT(&_17$$9, &decrypted);
		}
		ZVAL_BOOL(&_18$$9, 1);
		ZEPHIR_CALL_METHOD(&_16$$9, this_ptr, "phphashhmac", NULL, 0, &hashAlgorithm, &_17$$9, &decryptKey, &_18$$9);
		zephir_check_call_status();
		ZEPHIR_CALL_METHOD(&_15$$9, this_ptr, "phphashequals", NULL, 0, &_16$$9, &digest);
		zephir_check_call_status();
		_20$$9 = !ZEPHIR_IS_TRUE_IDENTICAL(&_15$$9);
		if (!(_20$$9)) {
			_20$$9 = ZEPHIR_IS_FALSE_IDENTICAL(&decrypted);
		}
		if (_20$$9) {
			ZEPHIR_THROW_EXCEPTION_DEBUG_STR(phalcon_encryption_crypt_exception_mismatch_ce, "Hash does not match.", "phalcon/Encryption/Crypt.zep", 293);
			return;
		}
		ZEPHIR_RETURN_CALL_METHOD(this_ptr, "decryptgetunpadded", NULL, 0, &mode, &blockSize, &decrypted);
		zephir_check_call_status();
		RETURN_MM();
	}
	ZEPHIR_CALL_METHOD(&decrypted, this_ptr, "decryptgcmccmauth", NULL, 0, &mode, &cipherText, &decryptKey, &iv);
	zephir_check_call_status();
	ZEPHIR_RETURN_CALL_METHOD(this_ptr, "decryptgetunpadded", NULL, 0, &mode, &blockSize, &decrypted);
	zephir_check_call_status();
	RETURN_MM();
}

/**
 * Decrypt a text that is coded as a base64 string.
 *
 * @param string     $input
 * @param mixed|null $key
 * @param bool       $safe
 *
 * @return string
 * @throws Exception
 * @throws Mismatch
 */
PHP_METHOD(Phalcon_Encryption_Crypt, decryptBase64)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zend_bool safe;
	zval input_zv, key_zv, *safe_param = NULL, _0$$3, _1;
	zend_string *input = NULL, *key = NULL;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&input_zv);
	ZVAL_UNDEF(&key_zv);
	ZVAL_UNDEF(&_0$$3);
	ZVAL_UNDEF(&_1);
	bool is_null_true = 1;
	ZEND_PARSE_PARAMETERS_START(1, 3)
		Z_PARAM_STR(input)
		Z_PARAM_OPTIONAL
		Z_PARAM_STR_OR_NULL(key)
		Z_PARAM_BOOL(safe)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	if (ZEND_NUM_ARGS() > 2) {
		safe_param = ZEND_CALL_ARG(execute_data, 3);
	}
	zephir_memory_observe(&input_zv);
	ZVAL_STR_COPY(&input_zv, input);
	if (!key) {
		ZEPHIR_INIT_VAR(&key_zv);
	} else {
		zephir_memory_observe(&key_zv);
	ZVAL_STR_COPY(&key_zv, key);
	}
	if (!safe_param) {
		safe = 0;
	} else {
		}
	if (safe) {
		ZEPHIR_CALL_METHOD(&_0$$3, this_ptr, "dodecodeurl", NULL, 0, &input_zv);
		zephir_check_call_status();
		ZEPHIR_RETURN_CALL_METHOD(this_ptr, "decrypt", NULL, 0, &_0$$3, &key_zv);
		zephir_check_call_status();
		RETURN_MM();
	}
	ZEPHIR_CALL_FUNCTION(&_1, "base64_decode", NULL, 0, &input_zv);
	zephir_check_call_status();
	ZEPHIR_RETURN_CALL_METHOD(this_ptr, "decrypt", NULL, 0, &_1, &key_zv);
	zephir_check_call_status();
	RETURN_MM();
}

/**
 * Encrypts a text.
 *
 * ```php
 * $encrypted = $crypt->encrypt(
 *     "Top secret",
 *     "T4\xb1\x8d\xa9\x98\x05\\\x8c\xbe\x1d\x07&[\x99\x18\xa4~Lc1\xbeW\xb3"
 * );
 * ```
 *
 * @param string      $input
 * @param string|null $key
 *
 * @return string
 * @throws Exception
 */
PHP_METHOD(Phalcon_Encryption_Crypt, encrypt)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval input_zv, key_zv, blockSize, cipher, digest, encryptKey, encrypted, iv, ivLength, mode, padded, _1, _2, _3, _0$$4, _4$$6, _5$$7, _6$$7;
	zend_string *input = NULL, *key = NULL;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&input_zv);
	ZVAL_UNDEF(&key_zv);
	ZVAL_UNDEF(&blockSize);
	ZVAL_UNDEF(&cipher);
	ZVAL_UNDEF(&digest);
	ZVAL_UNDEF(&encryptKey);
	ZVAL_UNDEF(&encrypted);
	ZVAL_UNDEF(&iv);
	ZVAL_UNDEF(&ivLength);
	ZVAL_UNDEF(&mode);
	ZVAL_UNDEF(&padded);
	ZVAL_UNDEF(&_1);
	ZVAL_UNDEF(&_2);
	ZVAL_UNDEF(&_3);
	ZVAL_UNDEF(&_0$$4);
	ZVAL_UNDEF(&_4$$6);
	ZVAL_UNDEF(&_5$$7);
	ZVAL_UNDEF(&_6$$7);
	static zend_string *_zephir_prop_0 = NULL;
	static zend_string *_zephir_prop_1 = NULL;
	static zend_string *_zephir_prop_2 = NULL;
	static zend_string *_zephir_prop_3 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("key", 3, 1);
	}
	if (UNEXPECTED(!_zephir_prop_1)) {
		_zephir_prop_1 = zend_string_init("cipher", 6, 1);
	}
	if (UNEXPECTED(!_zephir_prop_2)) {
		_zephir_prop_2 = zend_string_init("ivLength", 8, 1);
	}
	if (UNEXPECTED(!_zephir_prop_3)) {
		_zephir_prop_3 = zend_string_init("useSigning", 10, 1);
	}

	bool is_null_true = 1;
	ZEND_PARSE_PARAMETERS_START(1, 2)
		Z_PARAM_STR(input)
		Z_PARAM_OPTIONAL
		Z_PARAM_STR_OR_NULL(key)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_memory_observe(&input_zv);
	ZVAL_STR_COPY(&input_zv, input);
	if (!key) {
		ZEPHIR_INIT_VAR(&key_zv);
	} else {
		zephir_memory_observe(&key_zv);
	ZVAL_STR_COPY(&key_zv, key);
	}
	zephir_memory_observe(&encryptKey);
	zephir_read_property_cached(&encryptKey, this_ptr, _zephir_prop_0, 685, PH_NOISY_CC);
	if (1 != ZEPHIR_IS_EMPTY(&key_zv)) {
		ZEPHIR_CPY_WRT(&encryptKey, &key_zv);
	}
	if (1 == ZEPHIR_IS_EMPTY(&encryptKey)) {
		ZEPHIR_INIT_VAR(&_0$$4);
		object_init_ex(&_0$$4, phalcon_encryption_crypt_exception_emptyencryptionkey_ce);
		ZEPHIR_CALL_METHOD(NULL, &_0$$4, "__construct", NULL, 0);
		zephir_check_call_status();
		zephir_throw_exception_debug(&_0$$4, "phalcon/Encryption/Crypt.zep", 367);
		ZEPHIR_MM_RESTORE();
		return;
	}
	zephir_read_property_cached(&_1, this_ptr, _zephir_prop_1, 686, PH_NOISY_CC | PH_READONLY);
	ZEPHIR_CPY_WRT(&cipher, &_1);
	zephir_read_property_cached(&_1, this_ptr, _zephir_prop_2, 687, PH_NOISY_CC | PH_READONLY);
	ZEPHIR_CPY_WRT(&ivLength, &_1);
	ZEPHIR_INIT_VAR(&_2);
	ZVAL_STRING(&_2, "cipher");
	ZEPHIR_CALL_METHOD(NULL, this_ptr, "checkcipherhashisavailable", NULL, 0, &cipher, &_2);
	zephir_check_call_status();
	ZEPHIR_CALL_METHOD(&mode, this_ptr, "getmode", NULL, 0);
	zephir_check_call_status();
	ZEPHIR_CALL_METHOD(&blockSize, this_ptr, "getblocksize", NULL, 0, &mode);
	zephir_check_call_status();

	/* try_start_1: */

		ZEPHIR_CALL_METHOD(&iv, this_ptr, "phpopensslrandompseudobytes", NULL, 0, &ivLength);
		zephir_check_call_status_or_jump(try_end_1);

	try_end_1:

	if (EG(exception)) {
		ZEPHIR_INIT_NVAR(&_2);
		ZVAL_OBJ(&_2, EG(exception));
		Z_ADDREF_P(&_2);
		ZEPHIR_INIT_VAR(&_3);
		if (zephir_is_instance_of(&_2, SL("Throwable"))) {
			zend_clear_exception();
			ZEPHIR_CPY_WRT(&_3, &_2);
			ZEPHIR_INIT_VAR(&_4$$6);
			object_init_ex(&_4$$6, phalcon_encryption_crypt_exception_randombytesgenerationfailed_ce);
			ZEPHIR_CALL_METHOD(NULL, &_4$$6, "__construct", NULL, 0);
			zephir_check_call_status();
			zephir_throw_exception_debug(&_4$$6, "phalcon/Encryption/Crypt.zep", 381);
			ZEPHIR_MM_RESTORE();
			return;
		}
	}
	ZEPHIR_CALL_METHOD(&padded, this_ptr, "encryptgetpadded", NULL, 0, &mode, &input_zv, &blockSize);
	zephir_check_call_status();
	ZEPHIR_CALL_METHOD(&encrypted, this_ptr, "encryptgcmccm", NULL, 0, &mode, &padded, &encryptKey, &iv);
	zephir_check_call_status();
	zephir_read_property_cached(&_1, this_ptr, _zephir_prop_3, 688, PH_NOISY_CC | PH_READONLY);
	if (ZEPHIR_IS_TRUE_IDENTICAL(&_1)) {
		ZEPHIR_CALL_METHOD(&_5$$7, this_ptr, "gethashalgorithm", NULL, 0);
		zephir_check_call_status();
		ZVAL_BOOL(&_6$$7, 1);
		ZEPHIR_CALL_METHOD(&digest, this_ptr, "phphashhmac", NULL, 0, &_5$$7, &padded, &encryptKey, &_6$$7);
		zephir_check_call_status();
		ZEPHIR_CONCAT_VVV(return_value, &iv, &digest, &encrypted);
		RETURN_MM();
	}
	ZEPHIR_CONCAT_VV(return_value, &iv, &encrypted);
	RETURN_MM();
}

/**
 * Encrypts a text returning the result as a base64 string.
 *
 * @param string     $input
 * @param mixed|null $key
 * @param bool       $safe
 *
 * @return string
 * @throws Exception
 */
PHP_METHOD(Phalcon_Encryption_Crypt, encryptBase64)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zend_bool safe;
	zval input_zv, key_zv, *safe_param = NULL, _0$$3, _1$$3, _2$$3, _3$$3, _4$$3, _5;
	zend_string *input = NULL, *key = NULL;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&input_zv);
	ZVAL_UNDEF(&key_zv);
	ZVAL_UNDEF(&_0$$3);
	ZVAL_UNDEF(&_1$$3);
	ZVAL_UNDEF(&_2$$3);
	ZVAL_UNDEF(&_3$$3);
	ZVAL_UNDEF(&_4$$3);
	ZVAL_UNDEF(&_5);
	bool is_null_true = 1;
	ZEND_PARSE_PARAMETERS_START(1, 3)
		Z_PARAM_STR(input)
		Z_PARAM_OPTIONAL
		Z_PARAM_STR_OR_NULL(key)
		Z_PARAM_BOOL(safe)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	if (ZEND_NUM_ARGS() > 2) {
		safe_param = ZEND_CALL_ARG(execute_data, 3);
	}
	zephir_memory_observe(&input_zv);
	ZVAL_STR_COPY(&input_zv, input);
	if (!key) {
		ZEPHIR_INIT_VAR(&key_zv);
	} else {
		zephir_memory_observe(&key_zv);
	ZVAL_STR_COPY(&key_zv, key);
	}
	if (!safe_param) {
		safe = 0;
	} else {
		}
	if (safe) {
		ZEPHIR_CALL_METHOD(&_0$$3, this_ptr, "encrypt", NULL, 0, &input_zv, &key_zv);
		zephir_check_call_status();
		ZEPHIR_CALL_FUNCTION(&_1$$3, "base64_encode", NULL, 0, &_0$$3);
		zephir_check_call_status();
		ZEPHIR_INIT_VAR(&_2$$3);
		ZVAL_STRING(&_2$$3, "+/");
		ZEPHIR_INIT_VAR(&_3$$3);
		ZVAL_STRING(&_3$$3, "-_");
		ZEPHIR_CALL_FUNCTION(&_4$$3, "strtr", NULL, 4, &_1$$3, &_2$$3, &_3$$3);
		zephir_check_call_status();
		ZEPHIR_INIT_NVAR(&_2$$3);
		ZVAL_STRING(&_2$$3, "=");
		zephir_fast_trim(return_value, &_4$$3, &_2$$3, ZEPHIR_TRIM_RIGHT);
		RETURN_MM();
	}
	ZEPHIR_CALL_METHOD(&_5, this_ptr, "encrypt", NULL, 0, &input_zv, &key_zv);
	zephir_check_call_status();
	ZEPHIR_RETURN_CALL_FUNCTION("base64_encode", NULL, 0, &_5);
	zephir_check_call_status();
	RETURN_MM();
}

/**
 * Returns a list of available ciphers.
 *
 * @phpstan-return array<array-key, string>
 */
PHP_METHOD(Phalcon_Encryption_Crypt, getAvailableCiphers)
{

	RETURN_MEMBER_TYPED(getThis(), "availableCiphers", IS_ARRAY);
}

/**
 * Returns the auth data
 *
 * @return string
 */
PHP_METHOD(Phalcon_Encryption_Crypt, getAuthData)
{

	RETURN_MEMBER_TYPED(getThis(), "authData", IS_STRING);
}

/**
 * Returns the auth tag
 *
 * @return string
 */
PHP_METHOD(Phalcon_Encryption_Crypt, getAuthTag)
{

	RETURN_MEMBER_TYPED(getThis(), "authTag", IS_STRING);
}

/**
 * Returns the auth tag length
 *
 * @return int
 */
PHP_METHOD(Phalcon_Encryption_Crypt, getAuthTagLength)
{

	RETURN_MEMBER_TYPED(getThis(), "authTagLength", IS_LONG);
}

/**
 * Return a list of registered hashing algorithms suitable for hash_hmac.
 *
 * @return array
 */
PHP_METHOD(Phalcon_Encryption_Crypt, getAvailableHashAlgorithms)
{
	zval _0, _1;
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_1);
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);

	ZEPHIR_INIT_VAR(&_1);
	ZVAL_STRING(&_1, "hash_hmac_algos");
	ZEPHIR_CALL_METHOD(&_0, this_ptr, "phpfunctionexists", NULL, 0, &_1);
	zephir_check_call_status();
	if (ZEPHIR_IS_TRUE_IDENTICAL(&_0)) {
		ZEPHIR_RETURN_CALL_FUNCTION("hash_hmac_algos", NULL, 0);
		zephir_check_call_status();
		RETURN_MM();
	}
	ZEPHIR_RETURN_CALL_FUNCTION("hash_algos", NULL, 0);
	zephir_check_call_status();
	RETURN_MM();
}

/**
 * Get the name of hashing algorithm.
 *
 * @return string
 */
PHP_METHOD(Phalcon_Encryption_Crypt, getHashAlgorithm)
{

	RETURN_MEMBER_TYPED(getThis(), "hashAlgorithm", IS_STRING);
}

/**
 * Returns the current cipher
 *
 * @return string
 */
PHP_METHOD(Phalcon_Encryption_Crypt, getCipher)
{

	RETURN_MEMBER_TYPED(getThis(), "cipher", IS_STRING);
}

/**
 * Returns the encryption key
 *
 * @return string
 */
PHP_METHOD(Phalcon_Encryption_Crypt, getKey)
{

	RETURN_MEMBER_TYPED(getThis(), "key", IS_STRING);
}

/**
 * Returns if the input length for decryption is valid or not
 * (number of bytes required by the cipher).
 *
 * @param string $input
 *
 * @return bool
 */
PHP_METHOD(Phalcon_Encryption_Crypt, isValidDecryptLength)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval input_zv, length, _0;
	zend_string *input = NULL;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&input_zv);
	ZVAL_UNDEF(&length);
	ZVAL_UNDEF(&_0);
	static zend_string *_zephir_prop_0 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("cipher", 6, 1);
	}

	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_STR(input)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_memory_observe(&input_zv);
	ZVAL_STR_COPY(&input_zv, input);
	zephir_read_property_cached(&_0, this_ptr, _zephir_prop_0, 686, PH_NOISY_CC | PH_READONLY);
	ZEPHIR_CALL_METHOD(&length, this_ptr, "phpopensslcipherivlength", NULL, 0, &_0);
	zephir_check_call_status();
	if (ZEPHIR_IS_FALSE_IDENTICAL(&length)) {
		RETURN_MM_BOOL(0);
	}
	RETURN_MM_BOOL(ZEPHIR_LE_LONG(&length, zephir_fast_strlen_ev(&input_zv)));
}

/**
 * @param string $data
 *
 * @return CryptInterface
 */
PHP_METHOD(Phalcon_Encryption_Crypt, setAuthData)
{
	zval data_zv;
	zend_string *data = NULL;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&data_zv);
	static zend_string *_zephir_prop_0 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("authData", 8, 1);
	}

	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_STR(data)
	ZEND_PARSE_PARAMETERS_END();
	ZVAL_STR(&data_zv, data);
	zephir_update_property_zval_cached(this_ptr, _zephir_prop_0, 690, &data_zv);
	RETURN_THISW();
}

/**
 * @param string $tag
 *
 * @return CryptInterface
 */
PHP_METHOD(Phalcon_Encryption_Crypt, setAuthTag)
{
	zval tag_zv;
	zend_string *tag = NULL;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&tag_zv);
	static zend_string *_zephir_prop_0 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("authTag", 7, 1);
	}

	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_STR(tag)
	ZEND_PARSE_PARAMETERS_END();
	ZVAL_STR(&tag_zv, tag);
	zephir_update_property_zval_cached(this_ptr, _zephir_prop_0, 691, &tag_zv);
	RETURN_THISW();
}

/**
 * @param int $length
 *
 * @return CryptInterface
 * @throws InvalidAuthTagLength
 */
PHP_METHOD(Phalcon_Encryption_Crypt, setAuthTagLength)
{
	zend_bool _0;
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zval *length_param = NULL, _2, _1$$3;
	zend_long length, ZEPHIR_LAST_CALL_STATUS;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&_2);
	ZVAL_UNDEF(&_1$$3);
	static zend_string *_zephir_prop_0 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("authTagLength", 13, 1);
	}

	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_LONG(length)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_fetch_params(1, 1, 0, &length_param);
	_0 = length < 4;
	if (!(_0)) {
		_0 = length > 16;
	}
	if (_0) {
		ZEPHIR_INIT_VAR(&_1$$3);
		object_init_ex(&_1$$3, phalcon_encryption_crypt_exception_invalidauthtaglength_ce);
		ZEPHIR_CALL_METHOD(NULL, &_1$$3, "__construct", NULL, 0);
		zephir_check_call_status();
		zephir_throw_exception_debug(&_1$$3, "phalcon/Encryption/Crypt.zep", 575);
		ZEPHIR_MM_RESTORE();
		return;
	}
	ZVAL_UNDEF(&_2);
	ZVAL_LONG(&_2, length);
	zephir_update_property_zval_cached(this_ptr, _zephir_prop_0, 692, &_2);
	RETURN_THIS();
}

/**
 * Sets the cipher algorithm for data encryption and decryption.
 *
 * @param string $cipher
 *
 * @return CryptInterface
 * @throws Exception
 */
PHP_METHOD(Phalcon_Encryption_Crypt, setCipher)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval cipher_zv, _0, _1;
	zend_string *cipher = NULL;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&cipher_zv);
	ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_1);
	static zend_string *_zephir_prop_0 = NULL;
	static zend_string *_zephir_prop_1 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("ivLength", 8, 1);
	}
	if (UNEXPECTED(!_zephir_prop_1)) {
		_zephir_prop_1 = zend_string_init("cipher", 6, 1);
	}

	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_STR(cipher)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_memory_observe(&cipher_zv);
	ZVAL_STR_COPY(&cipher_zv, cipher);
	ZEPHIR_INIT_VAR(&_0);
	ZVAL_STRING(&_0, "cipher");
	ZEPHIR_CALL_METHOD(NULL, this_ptr, "checkcipherhashisavailable", NULL, 0, &cipher_zv, &_0);
	zephir_check_call_status();
	ZEPHIR_CALL_METHOD(&_1, this_ptr, "getivlength", NULL, 0, &cipher_zv);
	zephir_check_call_status();
	zephir_update_property_zval_cached(this_ptr, _zephir_prop_0, 687, &_1);
	zephir_update_property_zval_cached(this_ptr, _zephir_prop_1, 686, &cipher_zv);
	RETURN_THIS();
}

/**
 * Sets the encryption key.
 *
 * The `$key` should have been previously generated in a cryptographically
 * safe way.
 *
 * Bad key:
 * "le password"
 *
 * Better (but still unsafe) ->
 * "#1dj8$=dp?.ak//j1V$~%*0X"
 *
 * Good key:
 * "T4\xb1\x8d\xa9\x98\x05\\\x8c\xbe\x1d\x07&[\x99\x18\xa4~Lc1\xbeW\xb3"
 *
 * @param string $key
 *
 * @return CryptInterface
 */
PHP_METHOD(Phalcon_Encryption_Crypt, setKey)
{
	zval key_zv;
	zend_string *key = NULL;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&key_zv);
	static zend_string *_zephir_prop_0 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("key", 3, 1);
	}

	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_STR(key)
	ZEND_PARSE_PARAMETERS_END();
	ZVAL_STR(&key_zv, key);
	zephir_update_property_zval_cached(this_ptr, _zephir_prop_0, 685, &key_zv);
	RETURN_THISW();
}

/**
 * Set the name of hashing algorithm.
 *
 * @param string $hashAlgorithm
 *
 * @return static
 * @throws Exception
 */
PHP_METHOD(Phalcon_Encryption_Crypt, setHashAlgorithm)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval hashAlgorithm_zv, _0;
	zend_string *hashAlgorithm = NULL;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&hashAlgorithm_zv);
	ZVAL_UNDEF(&_0);
	static zend_string *_zephir_prop_0 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("hashAlgorithm", 13, 1);
	}

	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_STR(hashAlgorithm)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_memory_observe(&hashAlgorithm_zv);
	ZVAL_STR_COPY(&hashAlgorithm_zv, hashAlgorithm);
	ZEPHIR_INIT_VAR(&_0);
	ZVAL_STRING(&_0, "hash");
	ZEPHIR_CALL_METHOD(NULL, this_ptr, "checkcipherhashisavailable", NULL, 0, &hashAlgorithm_zv, &_0);
	zephir_check_call_status();
	zephir_update_property_zval_cached(this_ptr, _zephir_prop_0, 684, &hashAlgorithm_zv);
	RETURN_THIS();
}

/**
 * Changes the padding scheme used.
 *
 * @param int $scheme
 *
 * @return CryptInterface
 */
PHP_METHOD(Phalcon_Encryption_Crypt, setPadding)
{
	zval *scheme_param = NULL, _0;
	zend_long scheme;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&_0);
	static zend_string *_zephir_prop_0 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("padding", 7, 1);
	}

	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_LONG(scheme)
	ZEND_PARSE_PARAMETERS_END();
	zephir_fetch_params_without_memory_grow(1, 0, &scheme_param);
	ZVAL_UNDEF(&_0);
	ZVAL_LONG(&_0, scheme);
	zephir_update_property_zval_cached(this_ptr, _zephir_prop_0, 693, &_0);
	RETURN_THISW();
}

/**
 * Sets if the calculating message digest must used.
 *
 * @param bool $useSigning
 *
 * @return CryptInterface
 */
PHP_METHOD(Phalcon_Encryption_Crypt, useSigning)
{
	zval *useSigning_param = NULL, __$true, __$false;
	zend_bool useSigning;
	zval *this_ptr = getThis();

	ZVAL_BOOL(&__$true, 1);
	ZVAL_BOOL(&__$false, 0);
	static zend_string *_zephir_prop_0 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("useSigning", 10, 1);
	}

	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_BOOL(useSigning)
	ZEND_PARSE_PARAMETERS_END();
	zephir_fetch_params_without_memory_grow(1, 0, &useSigning_param);
	if (useSigning) {
		zephir_update_property_zval_cached(this_ptr, _zephir_prop_0, 688, &__$true);
	} else {
		zephir_update_property_zval_cached(this_ptr, _zephir_prop_0, 688, &__$false);
	}
	RETURN_THISW();
}

/**
 * Checks if a cipher or a hash algorithm is available
 *
 * @param string $cipher
 * @param string $type
 *
 * @throws Exception
 */
PHP_METHOD(Phalcon_Encryption_Crypt, checkCipherHashIsAvailable)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval cipher_zv, type_zv, available, lower, method, _0, _1$$5;
	zend_string *cipher = NULL, *type = NULL;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&cipher_zv);
	ZVAL_UNDEF(&type_zv);
	ZVAL_UNDEF(&available);
	ZVAL_UNDEF(&lower);
	ZVAL_UNDEF(&method);
	ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_1$$5);
	ZEND_PARSE_PARAMETERS_START(2, 2)
		Z_PARAM_STR(cipher)
		Z_PARAM_STR(type)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_memory_observe(&cipher_zv);
	ZVAL_STR_COPY(&cipher_zv, cipher);
	zephir_memory_observe(&type_zv);
	ZVAL_STR_COPY(&type_zv, type);
	ZEPHIR_INIT_VAR(&_0);
	ZVAL_STRING(&_0, "hash");
	ZEPHIR_INIT_VAR(&method);
	if (ZEPHIR_IS_IDENTICAL(&_0, &type_zv)) {
		ZVAL_STRING(&method, "getAvailableHashAlgorithms");
	} else {
		ZVAL_STRING(&method, "getAvailableCiphers");
	}
	ZEPHIR_CALL_METHOD_ZVAL(&available, this_ptr, &method, NULL, 0);
	zephir_check_call_status();
	ZEPHIR_CALL_FUNCTION(&lower, "mb_strtolower", NULL, 16, &cipher_zv);
	zephir_check_call_status();
	if (1 != zephir_fast_in_array(&lower, &available)) {
		ZEPHIR_INIT_VAR(&_1$$5);
		object_init_ex(&_1$$5, phalcon_encryption_crypt_exception_unsupportedalgorithm_ce);
		ZEPHIR_CALL_METHOD(NULL, &_1$$5, "__construct", NULL, 0, &type_zv, &cipher_zv);
		zephir_check_call_status();
		zephir_throw_exception_debug(&_1$$5, "phalcon/Encryption/Crypt.zep", 693);
		ZEPHIR_MM_RESTORE();
		return;
	}
	ZEPHIR_MM_RESTORE();
}

/**
 * Pads texts before encryption. See
 * [cryptopad](https://www.di-mgt.com.au/cryptopad.html)
 *
 * @param string $input
 * @param string $mode
 * @param int    $blockSize
 * @param int    $paddingType
 *
 * @return string
 * @throws Exception
 */
PHP_METHOD(Phalcon_Encryption_Crypt, cryptPadText)
{
	zend_bool _3$$3;
	zval _1;
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long blockSize, paddingType, ZEPHIR_LAST_CALL_STATUS, paddingSize = 0;
	zval input_zv, mode_zv, *blockSize_param = NULL, *paddingType_param = NULL, padding, service, _0, _2, _9, _10, _5$$3, _6$$3, _7$$3, _8$$3, _4$$4;
	zend_string *input = NULL, *mode = NULL;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&input_zv);
	ZVAL_UNDEF(&mode_zv);
	ZVAL_UNDEF(&padding);
	ZVAL_UNDEF(&service);
	ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_2);
	ZVAL_UNDEF(&_9);
	ZVAL_UNDEF(&_10);
	ZVAL_UNDEF(&_5$$3);
	ZVAL_UNDEF(&_6$$3);
	ZVAL_UNDEF(&_7$$3);
	ZVAL_UNDEF(&_8$$3);
	ZVAL_UNDEF(&_4$$4);
	ZVAL_UNDEF(&_1);
	static zend_string *_zephir_prop_0 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("padFactory", 10, 1);
	}

	ZEND_PARSE_PARAMETERS_START(4, 4)
		Z_PARAM_STR(input)
		Z_PARAM_STR(mode)
		Z_PARAM_LONG(blockSize)
		Z_PARAM_LONG(paddingType)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	blockSize_param = ZEND_CALL_ARG(execute_data, 3);
	paddingType_param = ZEND_CALL_ARG(execute_data, 4);
	zephir_memory_observe(&input_zv);
	ZVAL_STR_COPY(&input_zv, input);
	zephir_memory_observe(&mode_zv);
	ZVAL_STR_COPY(&mode_zv, mode);
	ZEPHIR_INIT_VAR(&padding);
	ZVAL_STRING(&padding, "");
	paddingSize = 0;
	ZEPHIR_INIT_VAR(&_1);
	zephir_create_array(&_1, 1, 0);
	ZEPHIR_INIT_VAR(&_2);
	ZVAL_STRING(&_2, "cbc");
	zephir_array_fast_append(&_1, &_2);
	ZEPHIR_CALL_METHOD(&_0, this_ptr, "checkismode", NULL, 0, &_1, &mode_zv);
	zephir_check_call_status();
	if (ZEPHIR_IS_TRUE_IDENTICAL(&_0)) {
		paddingSize = (blockSize - (zephir_safe_mod_long_long(zephir_fast_strlen_ev(&input_zv), blockSize)));
		_3$$3 = paddingSize >= 256;
		if (!(_3$$3)) {
			_3$$3 = paddingSize < 0;
		}
		if (_3$$3) {
			ZEPHIR_INIT_VAR(&_4$$4);
			object_init_ex(&_4$$4, phalcon_encryption_crypt_exception_invalidpaddingsize_ce);
			ZEPHIR_CALL_METHOD(NULL, &_4$$4, "__construct", NULL, 0);
			zephir_check_call_status();
			zephir_throw_exception_debug(&_4$$4, "phalcon/Encryption/Crypt.zep", 724);
			ZEPHIR_MM_RESTORE();
			return;
		}
		zephir_read_property_cached(&_5$$3, this_ptr, _zephir_prop_0, 683, PH_NOISY_CC | PH_READONLY);
		ZVAL_LONG(&_6$$3, paddingType);
		ZEPHIR_CALL_METHOD(&service, &_5$$3, "padnumbertoservice", NULL, 0, &_6$$3);
		zephir_check_call_status();
		zephir_read_property_cached(&_6$$3, this_ptr, _zephir_prop_0, 683, PH_NOISY_CC | PH_READONLY);
		ZEPHIR_CALL_METHOD(&_7$$3, &_6$$3, "newinstance", NULL, 0, &service);
		zephir_check_call_status();
		ZVAL_LONG(&_8$$3, paddingSize);
		ZEPHIR_CALL_METHOD(&padding, &_7$$3, "pad", NULL, 0, &_8$$3);
		zephir_check_call_status();
	}
	if (0 == paddingSize) {
		RETURN_MM_STR(zend_string_copy(input));
	}
	ZVAL_LONG(&_9, 0);
	ZVAL_LONG(&_10, paddingSize);
	ZEPHIR_INIT_NVAR(&_2);
	zephir_substr(&_2, &padding, 0 , zephir_get_intval(&_10), 0);
	ZEPHIR_CONCAT_VV(return_value, &input_zv, &_2);
	RETURN_MM();
}

/**
 * Removes a padding from a text.
 *
 * If the function detects that the text was not padded, it will return it
 * unmodified.
 *
 * @param string $input
 * @param string $mode
 * @param int    $blockSize
 * @param int    $paddingType
 *
 * @return string
 * @throws Exception
 */
PHP_METHOD(Phalcon_Encryption_Crypt, cryptUnpadText)
{
	zval _3;
	zend_bool _0, _1;
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long blockSize, paddingType, ZEPHIR_LAST_CALL_STATUS;
	zval input_zv, mode_zv, *blockSize_param = NULL, *paddingType_param = NULL, length, paddingSize, service, _2, _4, _5$$3, _6$$3, _7$$3, _8$$3, _9$$6, _10$$6;
	zend_string *input = NULL, *mode = NULL;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&input_zv);
	ZVAL_UNDEF(&mode_zv);
	ZVAL_UNDEF(&length);
	ZVAL_UNDEF(&paddingSize);
	ZVAL_UNDEF(&service);
	ZVAL_UNDEF(&_2);
	ZVAL_UNDEF(&_4);
	ZVAL_UNDEF(&_5$$3);
	ZVAL_UNDEF(&_6$$3);
	ZVAL_UNDEF(&_7$$3);
	ZVAL_UNDEF(&_8$$3);
	ZVAL_UNDEF(&_9$$6);
	ZVAL_UNDEF(&_10$$6);
	ZVAL_UNDEF(&_3);
	static zend_string *_zephir_prop_0 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("padFactory", 10, 1);
	}

	ZEND_PARSE_PARAMETERS_START(4, 4)
		Z_PARAM_STR(input)
		Z_PARAM_STR(mode)
		Z_PARAM_LONG(blockSize)
		Z_PARAM_LONG(paddingType)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	blockSize_param = ZEND_CALL_ARG(execute_data, 3);
	paddingType_param = ZEND_CALL_ARG(execute_data, 4);
	zephir_memory_observe(&input_zv);
	ZVAL_STR_COPY(&input_zv, input);
	zephir_memory_observe(&mode_zv);
	ZVAL_STR_COPY(&mode_zv, mode);
	ZEPHIR_INIT_VAR(&length);
	ZVAL_LONG(&length, zephir_fast_strlen_ev(&input_zv));
	ZEPHIR_INIT_VAR(&paddingSize);
	ZVAL_LONG(&paddingSize, 0);
	_0 = ZEPHIR_GT_LONG(&length, 0);
	if (_0) {
		_0 = (zephir_safe_mod_zval_long(&length, blockSize) == 0);
	}
	_1 = _0;
	if (_1) {
		ZEPHIR_INIT_VAR(&_3);
		zephir_create_array(&_3, 1, 0);
		ZEPHIR_INIT_VAR(&_4);
		ZVAL_STRING(&_4, "cbc");
		zephir_array_fast_append(&_3, &_4);
		ZEPHIR_CALL_METHOD(&_2, this_ptr, "checkismode", NULL, 0, &_3, &mode_zv);
		zephir_check_call_status();
		_1 = ZEPHIR_IS_TRUE_IDENTICAL(&_2);
	}
	if (_1) {
		zephir_read_property_cached(&_5$$3, this_ptr, _zephir_prop_0, 683, PH_NOISY_CC | PH_READONLY);
		ZVAL_LONG(&_6$$3, paddingType);
		ZEPHIR_CALL_METHOD(&service, &_5$$3, "padnumbertoservice", NULL, 0, &_6$$3);
		zephir_check_call_status();
		zephir_read_property_cached(&_6$$3, this_ptr, _zephir_prop_0, 683, PH_NOISY_CC | PH_READONLY);
		ZEPHIR_CALL_METHOD(&_7$$3, &_6$$3, "newinstance", NULL, 0, &service);
		zephir_check_call_status();
		ZVAL_LONG(&_8$$3, blockSize);
		ZEPHIR_CALL_METHOD(&paddingSize, &_7$$3, "unpad", NULL, 0, &input_zv, &_8$$3);
		zephir_check_call_status();
		if (ZEPHIR_GT_LONG(&paddingSize, 0)) {
			if (ZEPHIR_LE_LONG(&paddingSize, blockSize)) {
				if (ZEPHIR_LT(&paddingSize, &length)) {
					ZEPHIR_INIT_VAR(&_9$$6);
					zephir_sub_function(&_9$$6, &length, &paddingSize);
					ZVAL_LONG(&_10$$6, 0);
					zephir_substr(return_value, &input_zv, 0 , zephir_get_intval(&_9$$6), 0);
					RETURN_MM();
				}
				RETURN_MM_STRING("");
			}
			ZEPHIR_INIT_NVAR(&paddingSize);
			ZVAL_LONG(&paddingSize, 0);
		}
	}
	if (ZEPHIR_IS_LONG_IDENTICAL(&paddingSize, 0)) {
		RETURN_MM_STR(zend_string_copy(input));
	}
	RETURN_MM_STRING("");
}

/**
 * @param string $mode
 * @param int    $blockSize
 * @param string $decrypted
 *
 * @return string
 */
PHP_METHOD(Phalcon_Encryption_Crypt, decryptGetUnpadded)
{
	zval _1;
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long blockSize, ZEPHIR_LAST_CALL_STATUS;
	zval mode_zv, *blockSize_param = NULL, decrypted_zv, localDecrypted, padding, _0, _2, _3$$3;
	zend_string *mode = NULL, *decrypted = NULL;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&mode_zv);
	ZVAL_UNDEF(&decrypted_zv);
	ZVAL_UNDEF(&localDecrypted);
	ZVAL_UNDEF(&padding);
	ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_2);
	ZVAL_UNDEF(&_3$$3);
	ZVAL_UNDEF(&_1);
	static zend_string *_zephir_prop_0 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("padding", 7, 1);
	}

	ZEND_PARSE_PARAMETERS_START(3, 3)
		Z_PARAM_STR(mode)
		Z_PARAM_LONG(blockSize)
		Z_PARAM_STR(decrypted)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	blockSize_param = ZEND_CALL_ARG(execute_data, 2);
	zephir_memory_observe(&mode_zv);
	ZVAL_STR_COPY(&mode_zv, mode);
	zephir_memory_observe(&decrypted_zv);
	ZVAL_STR_COPY(&decrypted_zv, decrypted);
	ZEPHIR_CPY_WRT(&localDecrypted, &decrypted_zv);
	ZEPHIR_INIT_VAR(&_1);
	zephir_create_array(&_1, 1, 0);
	ZEPHIR_INIT_VAR(&_2);
	ZVAL_STRING(&_2, "cbc");
	zephir_array_fast_append(&_1, &_2);
	ZEPHIR_CALL_METHOD(&_0, this_ptr, "checkismode", NULL, 0, &_1, &mode_zv);
	zephir_check_call_status();
	if (ZEPHIR_IS_TRUE_IDENTICAL(&_0)) {
		zephir_read_property_cached(&_3$$3, this_ptr, _zephir_prop_0, 693, PH_NOISY_CC | PH_READONLY);
		ZEPHIR_CPY_WRT(&padding, &_3$$3);
		ZVAL_LONG(&_3$$3, blockSize);
		ZEPHIR_CALL_METHOD(&localDecrypted, this_ptr, "cryptunpadtext", NULL, 0, &decrypted_zv, &mode_zv, &_3$$3, &padding);
		zephir_check_call_status();
	}
	RETURN_CCTOR(&localDecrypted);
}

/**
 * @param string $mode
 * @param string $cipherText
 * @param string $decryptKey
 * @param string $iv
 *
 * @return string
 * @throws Exception
 */
PHP_METHOD(Phalcon_Encryption_Crypt, decryptGcmCcmAuth)
{
	zval _2;
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval mode_zv, cipherText_zv, decryptKey_zv, iv_zv, authData, authTag, authTagLength, cipher, cipherLength, encrypted, decrypted, _0, _1, _3, _4$$3, _5$$3, _6$$3, _7$$3, _8$$4, _9$$5;
	zend_string *mode = NULL, *cipherText = NULL, *decryptKey = NULL, *iv = NULL;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&mode_zv);
	ZVAL_UNDEF(&cipherText_zv);
	ZVAL_UNDEF(&decryptKey_zv);
	ZVAL_UNDEF(&iv_zv);
	ZVAL_UNDEF(&authData);
	ZVAL_UNDEF(&authTag);
	ZVAL_UNDEF(&authTagLength);
	ZVAL_UNDEF(&cipher);
	ZVAL_UNDEF(&cipherLength);
	ZVAL_UNDEF(&encrypted);
	ZVAL_UNDEF(&decrypted);
	ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_1);
	ZVAL_UNDEF(&_3);
	ZVAL_UNDEF(&_4$$3);
	ZVAL_UNDEF(&_5$$3);
	ZVAL_UNDEF(&_6$$3);
	ZVAL_UNDEF(&_7$$3);
	ZVAL_UNDEF(&_8$$4);
	ZVAL_UNDEF(&_9$$5);
	ZVAL_UNDEF(&_2);
	static zend_string *_zephir_prop_0 = NULL;
	static zend_string *_zephir_prop_1 = NULL;
	static zend_string *_zephir_prop_2 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("cipher", 6, 1);
	}
	if (UNEXPECTED(!_zephir_prop_1)) {
		_zephir_prop_1 = zend_string_init("authData", 8, 1);
	}
	if (UNEXPECTED(!_zephir_prop_2)) {
		_zephir_prop_2 = zend_string_init("authTagLength", 13, 1);
	}

	ZEND_PARSE_PARAMETERS_START(4, 4)
		Z_PARAM_STR(mode)
		Z_PARAM_STR(cipherText)
		Z_PARAM_STR(decryptKey)
		Z_PARAM_STR(iv)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_memory_observe(&mode_zv);
	ZVAL_STR_COPY(&mode_zv, mode);
	zephir_memory_observe(&cipherText_zv);
	ZVAL_STR_COPY(&cipherText_zv, cipherText);
	zephir_memory_observe(&decryptKey_zv);
	ZVAL_STR_COPY(&decryptKey_zv, decryptKey);
	zephir_memory_observe(&iv_zv);
	ZVAL_STR_COPY(&iv_zv, iv);
	zephir_read_property_cached(&_0, this_ptr, _zephir_prop_0, 686, PH_NOISY_CC | PH_READONLY);
	ZEPHIR_CPY_WRT(&cipher, &_0);
	ZEPHIR_INIT_VAR(&_2);
	zephir_create_array(&_2, 2, 0);
	ZEPHIR_INIT_VAR(&_3);
	ZVAL_STRING(&_3, "ccm");
	zephir_array_fast_append(&_2, &_3);
	ZEPHIR_INIT_NVAR(&_3);
	ZVAL_STRING(&_3, "gcm");
	zephir_array_fast_append(&_2, &_3);
	ZEPHIR_CALL_METHOD(&_1, this_ptr, "checkismode", NULL, 0, &_2, &mode_zv);
	zephir_check_call_status();
	if (ZEPHIR_IS_TRUE_IDENTICAL(&_1)) {
		zephir_read_property_cached(&_4$$3, this_ptr, _zephir_prop_1, 690, PH_NOISY_CC | PH_READONLY);
		ZEPHIR_CPY_WRT(&authData, &_4$$3);
		zephir_read_property_cached(&_4$$3, this_ptr, _zephir_prop_2, 692, PH_NOISY_CC | PH_READONLY);
		ZEPHIR_CPY_WRT(&authTagLength, &_4$$3);
		ZEPHIR_INIT_VAR(&cipherLength);
		ZVAL_LONG(&cipherLength, zephir_fast_strlen_ev(&cipherText_zv));
		ZEPHIR_INIT_VAR(&_5$$3);
		zephir_sub_function(&_5$$3, &cipherLength, &authTagLength);
		ZEPHIR_INIT_VAR(&authTag);
		zephir_substr(&authTag, &cipherText_zv, zephir_get_intval(&_5$$3), 0, ZEPHIR_SUBSTR_NO_LENGTH);
		ZEPHIR_INIT_VAR(&_6$$3);
		zephir_sub_function(&_6$$3, &cipherLength, &authTagLength);
		ZVAL_LONG(&_4$$3, 0);
		ZEPHIR_INIT_VAR(&encrypted);
		zephir_substr(&encrypted, &cipherText_zv, 0 , zephir_get_intval(&_6$$3), 0);
		ZVAL_LONG(&_7$$3, 1);
		ZEPHIR_CALL_FUNCTION(&decrypted, "openssl_decrypt", NULL, 0, &encrypted, &cipher, &decryptKey_zv, &_7$$3, &iv_zv, &authTag, &authData);
		zephir_check_call_status();
	} else {
		ZVAL_LONG(&_8$$4, 1);
		ZEPHIR_CALL_FUNCTION(&decrypted, "openssl_decrypt", NULL, 0, &cipherText_zv, &cipher, &decryptKey_zv, &_8$$4, &iv_zv);
		zephir_check_call_status();
	}
	if (ZEPHIR_IS_FALSE_IDENTICAL(&decrypted)) {
		ZEPHIR_INIT_VAR(&_9$$5);
		object_init_ex(&_9$$5, phalcon_encryption_crypt_exception_decryptionfailed_ce);
		ZEPHIR_CALL_METHOD(NULL, &_9$$5, "__construct", NULL, 0);
		zephir_check_call_status();
		zephir_throw_exception_debug(&_9$$5, "phalcon/Encryption/Crypt.zep", 868);
		ZEPHIR_MM_RESTORE();
		return;
	}
	RETURN_CCTOR(&decrypted);
}

/**
 * @param string $mode
 * @param string $input
 * @param int    $blockSize
 *
 * @return string
 * @throws Exception
 */
PHP_METHOD(Phalcon_Encryption_Crypt, encryptGetPadded)
{
	zval _3;
	zend_bool _1;
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long blockSize, ZEPHIR_LAST_CALL_STATUS;
	zval mode_zv, input_zv, *blockSize_param = NULL, _0, _2, _4, _5$$3, _6$$3;
	zend_string *mode = NULL, *input = NULL;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&mode_zv);
	ZVAL_UNDEF(&input_zv);
	ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_2);
	ZVAL_UNDEF(&_4);
	ZVAL_UNDEF(&_5$$3);
	ZVAL_UNDEF(&_6$$3);
	ZVAL_UNDEF(&_3);
	static zend_string *_zephir_prop_0 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("padding", 7, 1);
	}

	ZEND_PARSE_PARAMETERS_START(3, 3)
		Z_PARAM_STR(mode)
		Z_PARAM_STR(input)
		Z_PARAM_LONG(blockSize)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	blockSize_param = ZEND_CALL_ARG(execute_data, 3);
	zephir_memory_observe(&mode_zv);
	ZVAL_STR_COPY(&mode_zv, mode);
	zephir_memory_observe(&input_zv);
	ZVAL_STR_COPY(&input_zv, input);
	zephir_read_property_cached(&_0, this_ptr, _zephir_prop_0, 693, PH_NOISY_CC | PH_READONLY);
	_1 = !ZEPHIR_IS_LONG_IDENTICAL(&_0, 0);
	if (_1) {
		ZEPHIR_INIT_VAR(&_3);
		zephir_create_array(&_3, 1, 0);
		ZEPHIR_INIT_VAR(&_4);
		ZVAL_STRING(&_4, "cbc");
		zephir_array_fast_append(&_3, &_4);
		ZEPHIR_CALL_METHOD(&_2, this_ptr, "checkismode", NULL, 0, &_3, &mode_zv);
		zephir_check_call_status();
		_1 = ZEPHIR_IS_TRUE_IDENTICAL(&_2);
	}
	if (_1) {
		zephir_read_property_cached(&_5$$3, this_ptr, _zephir_prop_0, 693, PH_NOISY_CC | PH_READONLY);
		ZVAL_LONG(&_6$$3, blockSize);
		ZEPHIR_RETURN_CALL_METHOD(this_ptr, "cryptpadtext", NULL, 0, &input_zv, &mode_zv, &_6$$3, &_5$$3);
		zephir_check_call_status();
		RETURN_MM();
	}
	RETURN_MM_STR(zend_string_copy(input));
}

/**
 * @param string $mode
 * @param string $padded
 * @param string $encryptKey
 * @param string $iv
 *
 * @return string
 * @throws Exception
 */
PHP_METHOD(Phalcon_Encryption_Crypt, encryptGcmCcm)
{
	zval _2;
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval mode_zv, padded_zv, encryptKey_zv, iv_zv, authData, authTag, authTagLength, cipher, encrypted, _0, _1, _3, _4$$3, _5$$4, _6$$5, _7$$6;
	zend_string *mode = NULL, *padded = NULL, *encryptKey = NULL, *iv = NULL;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&mode_zv);
	ZVAL_UNDEF(&padded_zv);
	ZVAL_UNDEF(&encryptKey_zv);
	ZVAL_UNDEF(&iv_zv);
	ZVAL_UNDEF(&authData);
	ZVAL_UNDEF(&authTag);
	ZVAL_UNDEF(&authTagLength);
	ZVAL_UNDEF(&cipher);
	ZVAL_UNDEF(&encrypted);
	ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_1);
	ZVAL_UNDEF(&_3);
	ZVAL_UNDEF(&_4$$3);
	ZVAL_UNDEF(&_5$$4);
	ZVAL_UNDEF(&_6$$5);
	ZVAL_UNDEF(&_7$$6);
	ZVAL_UNDEF(&_2);
	static zend_string *_zephir_prop_0 = NULL;
	static zend_string *_zephir_prop_1 = NULL;
	static zend_string *_zephir_prop_2 = NULL;
	static zend_string *_zephir_prop_3 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("cipher", 6, 1);
	}
	if (UNEXPECTED(!_zephir_prop_1)) {
		_zephir_prop_1 = zend_string_init("authData", 8, 1);
	}
	if (UNEXPECTED(!_zephir_prop_2)) {
		_zephir_prop_2 = zend_string_init("authTag", 7, 1);
	}
	if (UNEXPECTED(!_zephir_prop_3)) {
		_zephir_prop_3 = zend_string_init("authTagLength", 13, 1);
	}

	ZEND_PARSE_PARAMETERS_START(4, 4)
		Z_PARAM_STR(mode)
		Z_PARAM_STR(padded)
		Z_PARAM_STR(encryptKey)
		Z_PARAM_STR(iv)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_memory_observe(&mode_zv);
	ZVAL_STR_COPY(&mode_zv, mode);
	zephir_memory_observe(&padded_zv);
	ZVAL_STR_COPY(&padded_zv, padded);
	zephir_memory_observe(&encryptKey_zv);
	ZVAL_STR_COPY(&encryptKey_zv, encryptKey);
	zephir_memory_observe(&iv_zv);
	ZVAL_STR_COPY(&iv_zv, iv);
	zephir_read_property_cached(&_0, this_ptr, _zephir_prop_0, 686, PH_NOISY_CC | PH_READONLY);
	ZEPHIR_CPY_WRT(&cipher, &_0);
	ZEPHIR_INIT_VAR(&authTag);
	ZVAL_STRING(&authTag, "");
	ZEPHIR_INIT_VAR(&_2);
	zephir_create_array(&_2, 2, 0);
	ZEPHIR_INIT_VAR(&_3);
	ZVAL_STRING(&_3, "ccm");
	zephir_array_fast_append(&_2, &_3);
	ZEPHIR_INIT_NVAR(&_3);
	ZVAL_STRING(&_3, "gcm");
	zephir_array_fast_append(&_2, &_3);
	ZEPHIR_CALL_METHOD(&_1, this_ptr, "checkismode", NULL, 0, &_2, &mode_zv);
	zephir_check_call_status();
	if (ZEPHIR_IS_TRUE_IDENTICAL(&_1)) {
		zephir_read_property_cached(&_4$$3, this_ptr, _zephir_prop_1, 690, PH_NOISY_CC | PH_READONLY);
		ZEPHIR_CPY_WRT(&authData, &_4$$3);
		if (1 == ZEPHIR_IS_EMPTY(&authData)) {
			ZEPHIR_INIT_VAR(&_5$$4);
			object_init_ex(&_5$$4, phalcon_encryption_crypt_exception_missingauthdata_ce);
			ZEPHIR_CALL_METHOD(NULL, &_5$$4, "__construct", NULL, 0);
			zephir_check_call_status();
			zephir_throw_exception_debug(&_5$$4, "phalcon/Encryption/Crypt.zep", 925);
			ZEPHIR_MM_RESTORE();
			return;
		}
		zephir_read_property_cached(&_4$$3, this_ptr, _zephir_prop_2, 691, PH_NOISY_CC | PH_READONLY);
		ZEPHIR_CPY_WRT(&authTag, &_4$$3);
		zephir_read_property_cached(&_4$$3, this_ptr, _zephir_prop_3, 692, PH_NOISY_CC | PH_READONLY);
		ZEPHIR_CPY_WRT(&authTagLength, &_4$$3);
		ZVAL_LONG(&_4$$3, 1);
		ZEPHIR_MAKE_REF(&authTag);
		ZEPHIR_CALL_FUNCTION(&encrypted, "openssl_encrypt", NULL, 0, &padded_zv, &cipher, &encryptKey_zv, &_4$$3, &iv_zv, &authTag, &authData, &authTagLength);
		ZEPHIR_UNREF(&authTag);
		zephir_check_call_status();
		zephir_update_property_zval_cached(this_ptr, _zephir_prop_2, 691, &authTag);
	} else {
		ZVAL_LONG(&_6$$5, 1);
		ZEPHIR_CALL_FUNCTION(&encrypted, "openssl_encrypt", NULL, 0, &padded_zv, &cipher, &encryptKey_zv, &_6$$5, &iv_zv);
		zephir_check_call_status();
	}
	if (ZEPHIR_IS_FALSE_IDENTICAL(&encrypted)) {
		ZEPHIR_INIT_VAR(&_7$$6);
		object_init_ex(&_7$$6, phalcon_encryption_crypt_exception_encryptionfailed_ce);
		ZEPHIR_CALL_METHOD(NULL, &_7$$6, "__construct", NULL, 0);
		zephir_check_call_status();
		zephir_throw_exception_debug(&_7$$6, "phalcon/Encryption/Crypt.zep", 954);
		ZEPHIR_MM_RESTORE();
		return;
	}
	ZEPHIR_CONCAT_VV(return_value, &encrypted, &authTag);
	RETURN_MM();
}

/**
 * Initialize available cipher algorithms.
 *
 * @return static
 * @throws Exception
 */
PHP_METHOD(Phalcon_Encryption_Crypt, initializeAvailableCiphers)
{
	zend_bool _8, _4$$4, _5$$4, _6$$4, _9$$6, _10$$6, _11$$6;
	zval allowed;
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zval __$true, available, cipher, _0, _1, *_3, _7, _2$$3;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval *this_ptr = getThis();

	ZVAL_BOOL(&__$true, 1);
	ZVAL_UNDEF(&available);
	ZVAL_UNDEF(&cipher);
	ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_1);
	ZVAL_UNDEF(&_7);
	ZVAL_UNDEF(&_2$$3);
	ZVAL_UNDEF(&allowed);
	static zend_string *_zephir_prop_0 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("availableCiphers", 16, 1);
	}
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);

	ZEPHIR_INIT_VAR(&_1);
	ZVAL_STRING(&_1, "openssl_get_cipher_methods");
	ZEPHIR_CALL_METHOD(&_0, this_ptr, "phpfunctionexists", NULL, 0, &_1);
	zephir_check_call_status();
	if (!ZEPHIR_IS_TRUE_IDENTICAL(&_0)) {
		ZEPHIR_INIT_VAR(&_2$$3);
		object_init_ex(&_2$$3, phalcon_encryption_crypt_exception_missingopensslextension_ce);
		ZEPHIR_CALL_METHOD(NULL, &_2$$3, "__construct", NULL, 0);
		zephir_check_call_status();
		zephir_throw_exception_debug(&_2$$3, "phalcon/Encryption/Crypt.zep", 976);
		ZEPHIR_MM_RESTORE();
		return;
	}
	ZEPHIR_CALL_FUNCTION(&available, "openssl_get_cipher_methods", NULL, 0, &__$true);
	zephir_check_call_status();
	ZEPHIR_INIT_VAR(&allowed);
	array_init(&allowed);
	zephir_is_iterable(&available, 0, "phalcon/Encryption/Crypt.zep", 993);
	if (Z_TYPE_P(&available) == IS_ARRAY) {
		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(&available), _3)
		{
			ZEPHIR_INIT_NVAR(&cipher);
			ZVAL_COPY(&cipher, _3);
			_4$$4 = 1 != zephir_start_with_str(&cipher, SL("des"));
			if (_4$$4) {
				_4$$4 = 1 != zephir_start_with_str(&cipher, SL("rc2"));
			}
			_5$$4 = _4$$4;
			if (_5$$4) {
				_5$$4 = 1 != zephir_start_with_str(&cipher, SL("rc4"));
			}
			_6$$4 = _5$$4;
			if (_6$$4) {
				_6$$4 = 1 != zephir_end_with_str(&cipher, SL("ecb"));
			}
			if (_6$$4) {
				zephir_array_update_zval(&allowed, &cipher, &cipher, PH_COPY | PH_SEPARATE);
			}
		} ZEND_HASH_FOREACH_END();
	} else {
		ZEPHIR_CALL_METHOD(NULL, &available, "rewind", NULL, 0);
		zephir_check_call_status();
		_8 = 1;
		while (1) {
			if (_8) {
				_8 = 0;
			} else {
				ZEPHIR_CALL_METHOD(NULL, &available, "next", NULL, 0);
				zephir_check_call_status();
			}
			ZEPHIR_CALL_METHOD(&_7, &available, "valid", NULL, 0);
			zephir_check_call_status();
			if (!zend_is_true(&_7)) {
				break;
			}
			ZEPHIR_CALL_METHOD(&cipher, &available, "current", NULL, 0);
			zephir_check_call_status();
				_9$$6 = 1 != zephir_start_with_str(&cipher, SL("des"));
				if (_9$$6) {
					_9$$6 = 1 != zephir_start_with_str(&cipher, SL("rc2"));
				}
				_10$$6 = _9$$6;
				if (_10$$6) {
					_10$$6 = 1 != zephir_start_with_str(&cipher, SL("rc4"));
				}
				_11$$6 = _10$$6;
				if (_11$$6) {
					_11$$6 = 1 != zephir_end_with_str(&cipher, SL("ecb"));
				}
				if (_11$$6) {
					zephir_array_update_zval(&allowed, &cipher, &cipher, PH_COPY | PH_SEPARATE);
				}
		}
	}
	ZEPHIR_INIT_NVAR(&cipher);
	zephir_update_property_zval_cached(this_ptr, _zephir_prop_0, 694, &allowed);
	RETURN_THIS();
}

/**
 * Checks if a mode (string) is in the values to compare (modes array)
 *
 * @param array  $modes
 * @param string $mode
 *
 * @return bool
 */
PHP_METHOD(Phalcon_Encryption_Crypt, checkIsMode)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_string *mode = NULL;
	zval *modes_param = NULL, mode_zv;
	zval modes;

	ZVAL_UNDEF(&modes);
	ZVAL_UNDEF(&mode_zv);
	ZEND_PARSE_PARAMETERS_START(2, 2)
		ZEPHIR_Z_PARAM_ARRAY(modes, modes_param)
		Z_PARAM_STR(mode)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	modes_param = ZEND_CALL_ARG(execute_data, 1);
	zephir_get_arrval(&modes, modes_param);
	zephir_memory_observe(&mode_zv);
	ZVAL_STR_COPY(&mode_zv, mode);
	RETURN_MM_BOOL(zephir_fast_in_array(&mode_zv, &modes));
}

/**
 * Returns the block size
 *
 * @param string $mode
 *
 * @return int
 * @throws Exception
 */
PHP_METHOD(Phalcon_Encryption_Crypt, getBlockSize)
{
	zval _1;
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval mode_zv, _0, _2, _3, _4;
	zend_string *mode = NULL;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&mode_zv);
	ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_2);
	ZVAL_UNDEF(&_3);
	ZVAL_UNDEF(&_4);
	ZVAL_UNDEF(&_1);
	static zend_string *_zephir_prop_0 = NULL;
	static zend_string *_zephir_prop_1 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("ivLength", 8, 1);
	}
	if (UNEXPECTED(!_zephir_prop_1)) {
		_zephir_prop_1 = zend_string_init("cipher", 6, 1);
	}

	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_STR(mode)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_memory_observe(&mode_zv);
	ZVAL_STR_COPY(&mode_zv, mode);
	zephir_read_property_cached(&_0, this_ptr, _zephir_prop_0, 687, PH_NOISY_CC | PH_READONLY);
	if (ZEPHIR_GT_LONG(&_0, 0)) {
		RETURN_MM_MEMBER_TYPED(getThis(), "ivLength", IS_LONG);
	}
	ZEPHIR_INIT_VAR(&_1);
	ZEPHIR_CONCAT_SV(&_1, "-", &mode_zv);
	zephir_read_property_cached(&_2, this_ptr, _zephir_prop_1, 686, PH_NOISY_CC | PH_READONLY);
	ZEPHIR_INIT_VAR(&_3);
	ZVAL_STRING(&_3, "");
	ZEPHIR_CALL_FUNCTION(&_4, "str_ireplace", NULL, 0, &_1, &_3, &_2);
	zephir_check_call_status();
	ZEPHIR_RETURN_CALL_METHOD(this_ptr, "getivlength", NULL, 0, &_4);
	zephir_check_call_status();
	RETURN_MM();
}

/**
 * Initialize available cipher algorithms.
 *
 * @param string $cipher
 *
 * @return int
 * @throws Exception
 */
PHP_METHOD(Phalcon_Encryption_Crypt, getIvLength)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval cipher_zv, length, _0$$3;
	zend_string *cipher = NULL;

	ZVAL_UNDEF(&cipher_zv);
	ZVAL_UNDEF(&length);
	ZVAL_UNDEF(&_0$$3);
	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_STR(cipher)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_memory_observe(&cipher_zv);
	ZVAL_STR_COPY(&cipher_zv, cipher);
	ZEPHIR_CALL_FUNCTION(&length, "openssl_cipher_iv_length", NULL, 0, &cipher_zv);
	zephir_check_call_status();
	if (ZEPHIR_IS_FALSE_IDENTICAL(&length)) {
		ZEPHIR_INIT_VAR(&_0$$3);
		object_init_ex(&_0$$3, phalcon_encryption_crypt_exception_ivlengthcalculationfailed_ce);
		ZEPHIR_CALL_METHOD(NULL, &_0$$3, "__construct", NULL, 0);
		zephir_check_call_status();
		zephir_throw_exception_debug(&_0$$3, "phalcon/Encryption/Crypt.zep", 1044);
		ZEPHIR_MM_RESTORE();
		return;
	}
	RETURN_CCTOR(&length);
}

/**
 * Returns the mode (last few characters of the cipher)
 *
 * @return string
 */
PHP_METHOD(Phalcon_Encryption_Crypt, getMode)
{
	zval position, _0, _1, _2, _3, _4, _5;
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&position);
	ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_1);
	ZVAL_UNDEF(&_2);
	ZVAL_UNDEF(&_3);
	ZVAL_UNDEF(&_4);
	ZVAL_UNDEF(&_5);
	static zend_string *_zephir_prop_0 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("cipher", 6, 1);
	}
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);

	zephir_read_property_cached(&_0, this_ptr, _zephir_prop_0, 686, PH_NOISY_CC | PH_READONLY);
	ZEPHIR_INIT_VAR(&_1);
	ZVAL_STRING(&_1, "-");
	ZEPHIR_CALL_FUNCTION(&_2, "strrpos", NULL, 0, &_0, &_1);
	zephir_check_call_status();
	ZEPHIR_INIT_VAR(&position);
	ZVAL_LONG(&position, zephir_get_intval(&_2));
	zephir_read_property_cached(&_3, this_ptr, _zephir_prop_0, 686, PH_NOISY_CC | PH_READONLY);
	zephir_read_property_cached(&_4, this_ptr, _zephir_prop_0, 686, PH_NOISY_CC | PH_READONLY);
	ZVAL_LONG(&_5, ((zephir_get_numberval(&position) - zephir_fast_strlen_ev(&_4)) + 1));
	ZEPHIR_INIT_NVAR(&_1);
	zephir_substr(&_1, &_3, zephir_get_intval(&_5), 0, ZEPHIR_SUBSTR_NO_LENGTH);
	ZEPHIR_RETURN_CALL_FUNCTION("mb_strtolower", NULL, 16, &_1);
	zephir_check_call_status();
	RETURN_MM();
}

/**
 * Decode a Base64 URL string
 *
 * @param string $input
 *
 * @return string
 */
PHP_METHOD(Phalcon_Encryption_Crypt, doDecodeUrl)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS, remainder = 0;
	zval *input_param = NULL, data, _0, _4, _5, _6, _1$$3, _2$$3, _3$$3;
	zval input;

	ZVAL_UNDEF(&input);
	ZVAL_UNDEF(&data);
	ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_4);
	ZVAL_UNDEF(&_5);
	ZVAL_UNDEF(&_6);
	ZVAL_UNDEF(&_1$$3);
	ZVAL_UNDEF(&_2$$3);
	ZVAL_UNDEF(&_3$$3);
	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_ZVAL(input_param)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_fetch_params(1, 1, 0, &input_param);
	zephir_get_strval(&input, input_param);
	ZEPHIR_CALL_FUNCTION(&_0, "mb_strlen", NULL, 0, &input);
	zephir_check_call_status();
	remainder = (long) (zephir_safe_mod_zval_long(&_0, 4));
	if (remainder) {
		ZEPHIR_INIT_VAR(&_1$$3);
		ZVAL_STRING(&_1$$3, "=");
		ZVAL_LONG(&_2$$3, (4 - remainder));
		ZEPHIR_CALL_FUNCTION(&_3$$3, "str_repeat", NULL, 7, &_1$$3, &_2$$3);
		zephir_check_call_status();
		zephir_concat_self(&input, &_3$$3);
	}
	ZEPHIR_INIT_VAR(&_4);
	ZVAL_STRING(&_4, "-_");
	ZEPHIR_INIT_VAR(&_5);
	ZVAL_STRING(&_5, "+/");
	ZEPHIR_CALL_FUNCTION(&_6, "strtr", NULL, 4, &input, &_4, &_5);
	zephir_check_call_status();
	ZEPHIR_CALL_FUNCTION(&data, "base64_decode", NULL, 0, &_6);
	zephir_check_call_status();
	if (ZEPHIR_IS_FALSE_IDENTICAL(&data)) {
		ZEPHIR_INIT_NVAR(&data);
		ZVAL_STRING(&data, "");
	}
	RETURN_CCTOR(&data);
}

/**
 * Encode a string in Base64 URL format
 *
 * @param string $input
 *
 * @return string
 */
PHP_METHOD(Phalcon_Encryption_Crypt, doEncodeUrl)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval input_zv, _0, _1, _2, _3, _4;
	zend_string *input = NULL;

	ZVAL_UNDEF(&input_zv);
	ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_1);
	ZVAL_UNDEF(&_2);
	ZVAL_UNDEF(&_3);
	ZVAL_UNDEF(&_4);
	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_STR(input)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_memory_observe(&input_zv);
	ZVAL_STR_COPY(&input_zv, input);
	ZEPHIR_CALL_FUNCTION(&_0, "base64_encode", NULL, 0, &input_zv);
	zephir_check_call_status();
	ZEPHIR_INIT_VAR(&_1);
	ZVAL_STRING(&_1, "+/");
	ZEPHIR_INIT_VAR(&_2);
	ZVAL_STRING(&_2, "-_");
	ZEPHIR_CALL_FUNCTION(&_3, "strtr", NULL, 4, &_0, &_1, &_2);
	zephir_check_call_status();
	ZEPHIR_INIT_NVAR(&_1);
	ZVAL_STRING(&_1, "=");
	ZEPHIR_INIT_NVAR(&_2);
	ZVAL_STRING(&_2, "");
	ZEPHIR_INIT_VAR(&_4);
	zephir_fast_str_replace(&_4, &_1, &_2, &_3);
	RETURN_CCTOR(&_4);
}

/**
 * @param string $input
 * @param bool   $strict
 *
 * @return string|false
 *
 * @link https://php.net/manual/en/function.base64-decode.php
 */
PHP_METHOD(Phalcon_Encryption_Crypt, phpBase64Decode)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zend_bool strict;
	zval input_zv, *strict_param = NULL, _0;
	zend_string *input = NULL;

	ZVAL_UNDEF(&input_zv);
	ZVAL_UNDEF(&_0);
	ZEND_PARSE_PARAMETERS_START(1, 2)
		Z_PARAM_STR(input)
		Z_PARAM_OPTIONAL
		Z_PARAM_BOOL(strict)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	if (ZEND_NUM_ARGS() > 1) {
		strict_param = ZEND_CALL_ARG(execute_data, 2);
	}
	zephir_memory_observe(&input_zv);
	ZVAL_STR_COPY(&input_zv, input);
	if (!strict_param) {
		strict = 0;
	} else {
		}
	ZVAL_BOOL(&_0, (strict ? 1 : 0));
	ZEPHIR_RETURN_CALL_FUNCTION("base64_decode", NULL, 0, &input_zv, &_0);
	zephir_check_call_status();
	RETURN_MM();
}

/**
 * @param string $input
 *
 * @return string
 *
 * @link https://php.net/manual/en/function.base64-encode.php
 */
PHP_METHOD(Phalcon_Encryption_Crypt, phpBase64Encode)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval input_zv;
	zend_string *input = NULL;

	ZVAL_UNDEF(&input_zv);
	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_STR(input)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_memory_observe(&input_zv);
	ZVAL_STR_COPY(&input_zv, input);
	ZEPHIR_RETURN_CALL_FUNCTION("base64_encode", NULL, 0, &input_zv);
	zephir_check_call_status();
	RETURN_MM();
}

/**
 * @param string $algorithm
 * @param string $data
 * @param bool   $binary
 *
 * @return string
 *
 * @link https://php.net/manual/en/function.hash.php
 */
PHP_METHOD(Phalcon_Encryption_Crypt, phpHash)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zend_bool binary;
	zval algorithm_zv, data_zv, *binary_param = NULL, _0;
	zend_string *algorithm = NULL, *data = NULL;

	ZVAL_UNDEF(&algorithm_zv);
	ZVAL_UNDEF(&data_zv);
	ZVAL_UNDEF(&_0);
	ZEND_PARSE_PARAMETERS_START(2, 3)
		Z_PARAM_STR(algorithm)
		Z_PARAM_STR(data)
		Z_PARAM_OPTIONAL
		Z_PARAM_BOOL(binary)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	if (ZEND_NUM_ARGS() > 2) {
		binary_param = ZEND_CALL_ARG(execute_data, 3);
	}
	zephir_memory_observe(&algorithm_zv);
	ZVAL_STR_COPY(&algorithm_zv, algorithm);
	zephir_memory_observe(&data_zv);
	ZVAL_STR_COPY(&data_zv, data);
	if (!binary_param) {
		binary = 0;
	} else {
		}
	ZVAL_BOOL(&_0, (binary ? 1 : 0));
	ZEPHIR_RETURN_CALL_FUNCTION("hash", NULL, 168, &algorithm_zv, &data_zv, &_0);
	zephir_check_call_status();
	RETURN_MM();
}

/**
 * @param string $knownString
 * @param string $userString
 *
 * @return bool
 *
 * @link https://php.net/manual/en/function.hash-equals.php
 */
PHP_METHOD(Phalcon_Encryption_Crypt, phpHashEquals)
{
	zval knownString_zv, userString_zv;
	zend_string *knownString = NULL, *userString = NULL;

	ZVAL_UNDEF(&knownString_zv);
	ZVAL_UNDEF(&userString_zv);
	ZEND_PARSE_PARAMETERS_START(2, 2)
		Z_PARAM_STR(knownString)
		Z_PARAM_STR(userString)
	ZEND_PARSE_PARAMETERS_END();
	ZVAL_STR(&knownString_zv, knownString);
	ZVAL_STR(&userString_zv, userString);
	RETURN_BOOL(zephir_hash_equals(&knownString_zv, &userString_zv));
}

/**
 * @param string $algorithm
 * @param string $data
 * @param string $key
 * @param bool   $binary
 *
 * @return string
 *
 * @link https://php.net/manual/en/function.hash-hmac.php
 */
PHP_METHOD(Phalcon_Encryption_Crypt, phpHashHmac)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zend_bool binary;
	zval algorithm_zv, data_zv, key_zv, *binary_param = NULL, _0;
	zend_string *algorithm = NULL, *data = NULL, *key = NULL;

	ZVAL_UNDEF(&algorithm_zv);
	ZVAL_UNDEF(&data_zv);
	ZVAL_UNDEF(&key_zv);
	ZVAL_UNDEF(&_0);
	ZEND_PARSE_PARAMETERS_START(3, 4)
		Z_PARAM_STR(algorithm)
		Z_PARAM_STR(data)
		Z_PARAM_STR(key)
		Z_PARAM_OPTIONAL
		Z_PARAM_BOOL(binary)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	if (ZEND_NUM_ARGS() > 3) {
		binary_param = ZEND_CALL_ARG(execute_data, 4);
	}
	zephir_memory_observe(&algorithm_zv);
	ZVAL_STR_COPY(&algorithm_zv, algorithm);
	zephir_memory_observe(&data_zv);
	ZVAL_STR_COPY(&data_zv, data);
	zephir_memory_observe(&key_zv);
	ZVAL_STR_COPY(&key_zv, key);
	if (!binary_param) {
		binary = 0;
	} else {
		}
	ZVAL_BOOL(&_0, (binary ? 1 : 0));
	ZEPHIR_RETURN_CALL_FUNCTION("hash_hmac", NULL, 169, &algorithm_zv, &data_zv, &key_zv, &_0);
	zephir_check_call_status();
	RETURN_MM();
}

/**
 * Find out whether an extension is loaded
 *
 * @param string $name
 *
 * @return bool
 *
 * @link https://php.net/manual/en/function.extension-loaded.php
 */
PHP_METHOD(Phalcon_Encryption_Crypt, phpExtensionLoaded)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval name_zv;
	zend_string *name = NULL;

	ZVAL_UNDEF(&name_zv);
	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_STR(name)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_memory_observe(&name_zv);
	ZVAL_STR_COPY(&name_zv, name);
	ZEPHIR_RETURN_CALL_FUNCTION("extension_loaded", NULL, 469, &name_zv);
	zephir_check_call_status();
	RETURN_MM();
}

/**
 * Return true if the given function has been defined
 *
 * @param string $functionName
 *
 * @return bool
 *
 * @link https://php.net/manual/en/function.function-exists.php
 */
PHP_METHOD(Phalcon_Encryption_Crypt, phpFunctionExists)
{
	zval functionName_zv;
	zend_string *functionName = NULL;

	ZVAL_UNDEF(&functionName_zv);
	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_STR(functionName)
	ZEND_PARSE_PARAMETERS_END();
	ZVAL_STR(&functionName_zv, functionName);
	RETURN_BOOL((zephir_function_exists(&functionName_zv) == SUCCESS));
}

/**
 * @param string $cipher
 *
 * @return int|bool
 *
 * @link https://php.net/manual/en/function.openssl-cipher-iv-length.php
 */
PHP_METHOD(Phalcon_Encryption_Crypt, phpOpensslCipherIvLength)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval cipher_zv;
	zend_string *cipher = NULL;

	ZVAL_UNDEF(&cipher_zv);
	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_STR(cipher)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_memory_observe(&cipher_zv);
	ZVAL_STR_COPY(&cipher_zv, cipher);
	ZEPHIR_RETURN_CALL_FUNCTION("openssl_cipher_iv_length", NULL, 0, &cipher_zv);
	zephir_check_call_status();
	RETURN_MM();
}

/**
 * @param int $length
 *
 * @return string|false
 *
 * @link https://php.net/manual/en/function.openssl-random-pseudo-bytes.php
 */
PHP_METHOD(Phalcon_Encryption_Crypt, phpOpensslRandomPseudoBytes)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zval *length_param = NULL, _0;
	zend_long length, ZEPHIR_LAST_CALL_STATUS;

	ZVAL_UNDEF(&_0);
	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_LONG(length)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_fetch_params(1, 1, 0, &length_param);
	ZVAL_LONG(&_0, length);
	ZEPHIR_RETURN_CALL_FUNCTION("openssl_random_pseudo_bytes", NULL, 0, &_0);
	zephir_check_call_status();
	RETURN_MM();
}

zend_object *zephir_init_properties_Phalcon_Encryption_Crypt(zend_class_entry *class_type)
{
		zval _0, _2, _1$$3, _3$$4;
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
		ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_2);
	ZVAL_UNDEF(&_1$$3);
	ZVAL_UNDEF(&_3$$4);
	

		ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
		zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	
	{
		zval local_this_ptr, *this_ptr = &local_this_ptr;
		ZEPHIR_CREATE_OBJECT(this_ptr, class_type);
		zephir_read_property_ex(&_0, this_ptr, ZEND_STRL("hashLengthCache"), PH_NOISY_CC | PH_READONLY);
		if (Z_TYPE_P(&_0) == IS_NULL) {
			ZEPHIR_INIT_VAR(&_1$$3);
			array_init(&_1$$3);
			zephir_update_property_zval_ex(this_ptr, ZEND_STRL("hashLengthCache"), &_1$$3);
		}
		zephir_read_property_ex(&_2, this_ptr, ZEND_STRL("availableCiphers"), PH_NOISY_CC | PH_READONLY);
		if (Z_TYPE_P(&_2) == IS_NULL) {
			ZEPHIR_INIT_VAR(&_3$$4);
			array_init(&_3$$4);
			zephir_update_property_zval_ex(this_ptr, ZEND_STRL("availableCiphers"), &_3$$4);
		}
		ZEPHIR_MM_RESTORE();
		return Z_OBJ_P(this_ptr);
	}
}

