<?php
/**
* Pure-PHP implementation of Rijndael.
*
* Uses mcrypt, if available/possible, and an internal implementation, otherwise.
*
* PHP version 5
*
* If {@link self::setBlockLength() setBlockLength()} isn't called, it'll be assumed to be 128 bits. If
* {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
* {@link self::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's
* 136-bits it'll be null-padded to 192-bits and 192 bits will be the key length until
* {@link self::setKey() setKey()} is called, again, at which point, it'll be recalculated.
*
* Not all Rijndael implementations may support 160-bits or 224-bits as the block length / key length. mcrypt, for example,
* does not. AES, itself, only supports block lengths of 128 and key lengths of 128, 192, and 256.
* {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=10 Rijndael-ammended.pdf#page=10} defines the
* algorithm for block lengths of 192 and 256 but not for block lengths / key lengths of 160 and 224. Indeed, 160 and 224
* are first defined as valid key / block lengths in
* {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=44 Rijndael-ammended.pdf#page=44}:
* Extensions: Other block and Cipher Key lengths.
* Note: Use of 160/224-bit Keys must be explicitly set by setKeyLength(160) respectively setKeyLength(224).
*
* {@internal The variable names are the same as those in
* {@link http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf#page=10 fips-197.pdf#page=10}.}}
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $rijndael = new \phpseclib3\Crypt\Rijndael('ctr');
*
* $rijndael->setKey('abcdefghijklmnop');
*
* $size = 10 * 1024;
* $plaintext = '';
* for ($i = 0; $i < $size; $i++) {
* $plaintext.= 'a';
* }
*
* echo $rijndael->decrypt($rijndael->encrypt($plaintext));
* ?>
* </code>
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2008 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\BlockCipher;
use phpseclib3\Exception\BadDecryptionException;
use phpseclib3\Exception\BadModeException;
use phpseclib3\Exception\InconsistentSetupException;
use phpseclib3\Exception\InsufficientSetupException;
/**
* Pure-PHP implementation of Rijndael.
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class Rijndael extends BlockCipher
{
/**
* The mcrypt specific name of the cipher
*
* Mcrypt is useable for 128/192/256-bit $block_size/$key_length. For 160/224 not.
* \phpseclib3\Crypt\Rijndael determines automatically whether mcrypt is useable
* or not for the current $block_size/$key_length.
* In case of, $cipher_name_mcrypt will be set dynamically at run time accordingly.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt
* @see \phpseclib3\Crypt\Common\SymmetricKey::engine
* @see self::isValidEngine()
* @var string
*/
protected $cipher_name_mcrypt = 'rijndael-128';
/**
* The Key Schedule
*
* @see self::setup()
* @var array
*/
private $w;
/**
* The Inverse Key Schedule
*
* @see self::setup()
* @var array
*/
private $dw;
/**
* The Block Length divided by 32
*
* {@internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4. Exists in conjunction with $block_size
* because the encryption / decryption / key schedule creation requires this number and not $block_size. We could
* derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
* of that, we'll just precompute it once.}
*
* @see self::setBlockLength()
* @var int
*/
private $Nb = 4;
/**
* The Key Length (in bytes)
*
* {@internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk
* because the encryption / decryption / key schedule creation requires this number and not $key_length. We could
* derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
* of that, we'll just precompute it once.}
*
* @see self::setKeyLength()
* @var int
*/
protected $key_length = 16;
/**
* The Key Length divided by 32
*
* @see self::setKeyLength()
* @var int
* @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4
*/
private $Nk = 4;
/**
* The Number of Rounds
*
* {@internal The max value is 14, the min value is 10.}
*
* @var int
*/
private $Nr;
/**
* Shift offsets
*
* @var array
*/
private $c;
/**
* Holds the last used key- and block_size information
*
* @var array
*/
private $kl;
/**
* Default Constructor.
*
* @param string $mode
* @throws \InvalidArgumentException if an invalid / unsupported mode is provided
*/
public function __construct($mode)
{
parent::__construct($mode);
if ($this->mode == self::MODE_STREAM) {
throw new BadModeException('Block ciphers cannot be ran in stream mode');
}
}
/**
* Sets the key length.
*
* Valid key lengths are 128, 160, 192, 224, and 256.
*
* Note: phpseclib extends Rijndael (and AES) for using 160- and 224-bit keys but they are officially not defined
* and the most (if not all) implementations are not able using 160/224-bit keys but round/pad them up to
* 192/256 bits as, for example, mcrypt will do.
*
* That said, if you want be compatible with other Rijndael and AES implementations,
* you should not setKeyLength(160) or setKeyLength(224).
*
* Additional: In case of 160- and 224-bit keys, phpseclib will/can, for that reason, not use
* the mcrypt php extension, even if available.
* This results then in slower encryption.
*
* @throws \LengthException if the key length is invalid
* @param int $length
*/
public function setKeyLength($length)
{
switch ($length) {
case 128:
case 160:
case 192:
case 224:
case 256:
$this->key_length = $length >> 3;
break;
default:
throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported');
}
parent::setKeyLength($length);
}
/**
* Sets the key.
*
* Rijndael supports five different key lengths
*
* @see setKeyLength()
* @param string $key
* @throws \LengthException if the key length isn't supported
*/
public function setKey($key)
{
switch (strlen($key)) {
case 16:
case 20:
case 24:
case 28:
case 32:
break;
default:
throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 20, 24, 28 or 32 are supported');
}
parent::setKey($key);
}
/**
* Sets the block length
*
* Valid block lengths are 128, 160, 192, 224, and 256.
*
* @param int $length
*/
public function setBlockLength($length)
{
switch ($length) {
case 128:
case 160:
case 192:
case 224:
case 256:
break;
default:
throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported');
}
$this->Nb = $length >> 5;
$this->block_size = $length >> 3;
$this->changed = $this->nonIVChanged = true;
$this->setEngine();
}
/**
* Test for engine validity
*
* This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* @param int $engine
* @return bool
*/
protected function isValidEngineHelper($engine)
{
switch ($engine) {
case self::ENGINE_LIBSODIUM:
return function_exists('sodium_crypto_aead_aes256gcm_is_available') &&
sodium_crypto_aead_aes256gcm_is_available() &&
$this->mode == self::MODE_GCM &&
$this->key_length == 32 &&
$this->nonce && strlen($this->nonce) == 12 &&
$this->block_size == 16;
case self::ENGINE_OPENSSL_GCM:
if (!extension_loaded('openssl')) {
return false;
}
$methods = openssl_get_cipher_methods();
return $this->mode == self::MODE_GCM &&
version_compare(PHP_VERSION, '7.1.0', '>=') &&
in_array('aes-' . $this->getKeyLength() . '-gcm', $methods) &&
$this->block_size == 16;
case self::ENGINE_OPENSSL:
if ($this->block_size != 16) {
return false;
}
$this->cipher_name_openssl_ecb = 'aes-' . ($this->key_length << 3) . '-ecb';
$this->cipher_name_openssl = 'aes-' . ($this->key_length << 3) . '-' . $this->openssl_translate_mode();
break;
case self::ENGINE_MCRYPT:
$this->cipher_name_mcrypt = 'rijndael-' . ($this->block_size << 3);
if ($this->key_length % 8) { // is it a 160/224-bit key?
// mcrypt is not usable for them, only for 128/192/256-bit keys
return false;
}
}
return parent::isValidEngineHelper($engine);
}
/**
* Encrypts a block
*
* @param string $in
* @return string
*/
protected function encryptBlock($in)
{
static $tables;
if (empty($tables)) {
$tables = &$this->getTables();
}
$t0 = $tables[0];
$t1 = $tables[1];
$t2 = $tables[2];
$t3 = $tables[3];
$sbox = $tables[4];
$state = [];
$words = unpack('N*', $in);
$c = $this->c;
$w = $this->w;
$Nb = $this->Nb;
$Nr = $this->Nr;
// addRoundKey
$wc = $Nb - 1;
foreach ($words as $word) {
$state[] = $word ^ $w[++$wc];
}
// fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components -
// subBytes, shiftRows, mixColumns, and addRoundKey. fips-197.pdf#page=30, "Implementation Suggestions Regarding
// Various Platforms" suggests that performs enhanced implementations are described in Rijndael-ammended.pdf.
// Rijndael-ammended.pdf#page=20, "Implementation aspects / 32-bit processor", discusses such an optimization.
// Unfortunately, the description given there is not quite correct. Per aes.spec.v316.pdf#page=19 [1],
// equation (7.4.7) is supposed to use addition instead of subtraction, so we'll do that here, as well.
// [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf
$temp = [];
for ($round = 1; $round < $Nr; ++$round) {
$i = 0; // $c[0] == 0
$j = $c[1];
$k = $c[2];
$l = $c[3];
while ($i < $Nb) {
$temp[$i] = $t0[$state[$i] >> 24 & 0x000000FF] ^
$t1[$state[$j] >> 16 & 0x000000FF] ^
$t2[$state[$k] >> 8 & 0x000000FF] ^
$t3[$state[$l] & 0x000000FF] ^
$w[++$wc];
++$i;
$j = ($j + 1) % $Nb;
$k = ($k + 1) % $Nb;
$l = ($l + 1) % $Nb;
}
$state = $temp;
}
// subWord
for ($i = 0; $i < $Nb; ++$i) {
$state[$i] = $sbox[$state[$i] & 0x000000FF] |
($sbox[$state[$i] >> 8 & 0x000000FF] << 8) |
($sbox[$state[$i] >> 16 & 0x000000FF] << 16) |
($sbox[$state[$i] >> 24 & 0x000000FF] << 24);
}
// shiftRows + addRoundKey
$i = 0; // $c[0] == 0
$j = $c[1];
$k = $c[2];
$l = $c[3];
while ($i < $Nb) {
$temp[$i] = ($state[$i] & intval(0xFF000000)) ^
($state[$j] & 0x00FF0000) ^
($state[$k] & 0x0000FF00) ^
($state[$l] & 0x000000FF) ^
$w[$i];
++$i;
$j = ($j + 1) % $Nb;
$k = ($k + 1) % $Nb;
$l = ($l + 1) % $Nb;
}
return pack('N*', ...$temp);
}
/**
* Decrypts a block
*
* @param string $in
* @return string
*/
protected function decryptBlock($in)
{
static $invtables;
if (empty($invtables)) {
$invtables = &$this->getInvTables();
}
$dt0 = $invtables[0];
$dt1 = $invtables[1];
$dt2 = $invtables[2];
$dt3 = $invtables[3];
$isbox = $invtables[4];
$state = [];
$words = unpack('N*', $in);
$c = $this->c;
$dw = $this->dw;
$Nb = $this->Nb;
$Nr = $this->Nr;
// addRoundKey
$wc = $Nb - 1;
foreach ($words as $word) {
$state[] = $word ^ $dw[++$wc];
}
$temp = [];
for ($round = $Nr - 1; $round > 0; --$round) {
$i = 0; // $c[0] == 0
$j = $Nb - $c[1];
$k = $Nb - $c[2];
$l = $Nb - $c[3];
while ($i < $Nb) {
$temp[$i] = $dt0[$state[$i] >> 24 & 0x000000FF] ^
$dt1[$state[$j] >> 16 & 0x000000FF] ^
$dt2[$state[$k] >> 8 & 0x000000FF] ^
$dt3[$state[$l] & 0x000000FF] ^
$dw[++$wc];
++$i;
$j = ($j + 1) % $Nb;
$k = ($k + 1) % $Nb;
$l = ($l + 1) % $Nb;
}
$state = $temp;
}
// invShiftRows + invSubWord + addRoundKey
$i = 0; // $c[0] == 0
$j = $Nb - $c[1];
$k = $Nb - $c[2];
$l = $Nb - $c[3];
while ($i < $Nb) {
$word = ($state[$i] & intval(0xFF000000)) |
($state[$j] & 0x00FF0000) |
($state[$k] & 0x0000FF00) |
($state[$l] & 0x000000FF);
$temp[$i] = $dw[$i] ^ ($isbox[$word & 0x000000FF] |
($isbox[$word >> 8 & 0x000000FF] << 8) |
($isbox[$word >> 16 & 0x000000FF] << 16) |
($isbox[$word >> 24 & 0x000000FF] << 24));
++$i;
$j = ($j + 1) % $Nb;
$k = ($k + 1) % $Nb;
$l = ($l + 1) % $Nb;
}
return pack('N*', ...$temp);
}
/**
* Setup the self::ENGINE_INTERNAL $engine
*
* (re)init, if necessary, the internal cipher $engine and flush all $buffers
* Used (only) if $engine == self::ENGINE_INTERNAL
*
* _setup() will be called each time if $changed === true
* typically this happens when using one or more of following public methods:
*
* - setKey()
*
* - setIV()
*
* - disableContinuousBuffer()
*
* - First run of encrypt() / decrypt() with no init-settings
*
* {@internal setup() is always called before en/decryption.}
*
* {@internal Could, but not must, extend by the child Crypt_* class}
*
* @see self::setKey()
* @see self::setIV()
* @see self::disableContinuousBuffer()
*/
protected function setup()
{
if (!$this->changed) {
return;
}
parent::setup();
if (is_string($this->iv) && strlen($this->iv) != $this->block_size) {
throw new InconsistentSetupException('The IV length (' . strlen($this->iv) . ') does not match the block size (' . $this->block_size . ')');
}
}
/**
* Setup the key (expansion)
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::setupKey()
*/
protected function setupKey()
{
// Each number in $rcon is equal to the previous number multiplied by two in Rijndael's finite field.
// See http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse
static $rcon;
if (!isset($rcon)) {
$rcon = [0,
0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000,
0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000,
0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000,
0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000
];
$rcon = array_map('intval', $rcon);
}
if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_length === $this->kl['key_length'] && $this->block_size === $this->kl['block_size']) {
// already expanded
return;
}
$this->kl = ['key' => $this->key, 'key_length' => $this->key_length, 'block_size' => $this->block_size];
$this->Nk = $this->key_length >> 2;
// see Rijndael-ammended.pdf#page=44
$this->Nr = max($this->Nk, $this->Nb) + 6;
// shift offsets for Nb = 5, 7 are defined in Rijndael-ammended.pdf#page=44,
// "Table 8: Shift offsets in Shiftrow for the alternative block lengths"
// shift offsets for Nb = 4, 6, 8 are defined in Rijndael-ammended.pdf#page=14,
// "Table 2: Shift offsets for different block lengths"
switch ($this->Nb) {
case 4:
case 5:
case 6:
$this->c = [0, 1, 2, 3];
break;
case 7:
$this->c = [0, 1, 2, 4];
break;
case 8:
$this->c = [0, 1, 3, 4];
}
$w = array_values(unpack('N*words', $this->key));
$length = $this->Nb * ($this->Nr + 1);
for ($i = $this->Nk; $i < $length; $i++) {
$temp = $w[$i - 1];
if ($i % $this->Nk == 0) {
// according to <http://php.net/language.types.integer>, "the size of an integer is platform-dependent".
// on a 32-bit machine, it's 32-bits, and on a 64-bit machine, it's 64-bits. on a 32-bit machine,
// 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit machine, it equals 0xFFFFFFFF00. as such, doing 'and'
// with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is.
$temp = (($temp << 8) & intval(0xFFFFFF00)) | (($temp >> 24) & 0x000000FF); // rotWord
$temp = $this->subWord($temp) ^ $rcon[$i / $this->Nk];
} elseif ($this->Nk > 6 && $i % $this->Nk == 4) {
$temp = $this->subWord($temp);
}
$w[$i] = $w[$i - $this->Nk] ^ $temp;
}
// convert the key schedule from a vector of $Nb * ($Nr + 1) length to a matrix with $Nr + 1 rows and $Nb columns
// and generate the inverse key schedule. more specifically,
// according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=23> (section 5.3.3),
// "The key expansion for the Inverse Cipher is defined as follows:
// 1. Apply the Key Expansion.
// 2. Apply InvMixColumn to all Round Keys except the first and the last one."
// also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher"
list($dt0, $dt1, $dt2, $dt3) = $this->getInvTables();
$temp = $this->w = $this->dw = [];
for ($i = $row = $col = 0; $i < $length; $i++, $col++) {
if ($col == $this->Nb) {
if ($row == 0) {
$this->dw[0] = $this->w[0];
} else {
// subWord + invMixColumn + invSubWord = invMixColumn
$j = 0;
while ($j < $this->Nb) {
$dw = $this->subWord($this->w[$row][$j]);
$temp[$j] = $dt0[$dw >> 24 & 0x000000FF] ^
$dt1[$dw >> 16 & 0x000000FF] ^
$dt2[$dw >> 8 & 0x000000FF] ^
$dt3[$dw & 0x000000FF];
$j++;
}
$this->dw[$row] = $temp;
}
$col = 0;
$row++;
}
$this->w[$row][$col] = $w[$i];
}
$this->dw[$row] = $this->w[$row];
// Converting to 1-dim key arrays (both ascending)
$this->dw = array_reverse($this->dw);
$w = array_pop($this->w);
$dw = array_pop($this->dw);
foreach ($this->w as $r => $wr) {
foreach ($wr as $c => $wc) {
$w[] = $wc;
$dw[] = $this->dw[$r][$c];
}
}
$this->w = $w;
$this->dw = $dw;
}
/**
* Performs S-Box substitutions
*
* @return array
* @param int $word
*/
private function subWord($word)
{
static $sbox;
if (empty($sbox)) {
list(, , , , $sbox) = self::getTables();
}
return $sbox[$word & 0x000000FF] |
($sbox[$word >> 8 & 0x000000FF] << 8) |
($sbox[$word >> 16 & 0x000000FF] << 16) |
($sbox[$word >> 24 & 0x000000FF] << 24);
}
/**
* Provides the mixColumns and sboxes tables
*
* @see self::encryptBlock()
* @see self::setupInlineCrypt()
* @see self::subWord()
* @return array &$tables
*/
protected function &getTables()
{
static $tables;
if (empty($tables)) {
// according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=19> (section 5.2.1),
// precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so
// those are the names we'll use.
$t3 = array_map('intval', [
// with array_map('intval', ...) we ensure we have only int's and not
// some slower floats converted by php automatically on high values
0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC,
0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB,
0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B,
0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83,
0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A,
0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F,
0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA,
0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B,
0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713,
0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6,
0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85,
0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411,
0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B,
0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1,
0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF,
0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E,
0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6,
0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B,
0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD,
0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8,
0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2,
0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049,
0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810,
0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197,
0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F,
0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C,
0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927,
0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733,
0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5,
0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0,
0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C
]);
foreach ($t3 as $t3i) {
$t0[] = (($t3i << 24) & intval(0xFF000000)) | (($t3i >> 8) & 0x00FFFFFF);
$t1[] = (($t3i << 16) & intval(0xFFFF0000)) | (($t3i >> 16) & 0x0000FFFF);
$t2[] = (($t3i << 8) & intval(0xFFFFFF00)) | (($t3i >> 24) & 0x000000FF);
}
$tables = [
// The Precomputed mixColumns tables t0 - t3
$t0,
$t1,
$t2,
$t3,
// The SubByte S-Box
[
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
]
];
}
return $tables;
}
/**
* Provides the inverse mixColumns and inverse sboxes tables
*
* @see self::decryptBlock()
* @see self::setupInlineCrypt()
* @see self::setupKey()
* @return array &$tables
*/
protected function &getInvTables()
{
static $tables;
if (empty($tables)) {
$dt3 = array_map('intval', [
0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B,
0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5,
0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B,
0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E,
0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D,
0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9,
0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66,
0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED,
0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4,
0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD,
0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60,
0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79,
0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C,
0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24,
0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C,
0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814,
0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B,
0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084,
0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077,
0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22,
0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F,
0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582,
0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB,
0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF,
0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035,
0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17,
0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46,
0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D,
0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A,
0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678,
0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF,
0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0
]);
foreach ($dt3 as $dt3i) {
$dt0[] = (($dt3i << 24) & intval(0xFF000000)) | (($dt3i >> 8) & 0x00FFFFFF);
$dt1[] = (($dt3i << 16) & intval(0xFFFF0000)) | (($dt3i >> 16) & 0x0000FFFF);
$dt2[] = (($dt3i << 8) & intval(0xFFFFFF00)) | (($dt3i >> 24) & 0x000000FF);
};
$tables = [
// The Precomputed inverse mixColumns tables dt0 - dt3
$dt0,
$dt1,
$dt2,
$dt3,
// The inverse SubByte S-Box
[
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
]
];
}
return $tables;
}
/**
* Setup the performance-optimized function for de/encrypt()
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::setupInlineCrypt()
*/
protected function setupInlineCrypt()
{
$w = $this->w;
$dw = $this->dw;
$init_encrypt = '';
$init_decrypt = '';
$Nr = $this->Nr;
$Nb = $this->Nb;
$c = $this->c;
// Generating encrypt code:
$init_encrypt .= '
if (empty($tables)) {
$tables = &$this->getTables();
}
$t0 = $tables[0];
$t1 = $tables[1];
$t2 = $tables[2];
$t3 = $tables[3];
$sbox = $tables[4];
';
$s = 'e';
$e = 's';
$wc = $Nb - 1;
// Preround: addRoundKey
$encrypt_block = '$in = unpack("N*", $in);' . "\n";
for ($i = 0; $i < $Nb; ++$i) {
$encrypt_block .= '$s' . $i . ' = $in[' . ($i + 1) . '] ^ ' . $w[++$wc] . ";\n";
}
// Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
for ($round = 1; $round < $Nr; ++$round) {
list($s, $e) = [$e, $s];
for ($i = 0; $i < $Nb; ++$i) {
$encrypt_block .=
'$' . $e . $i . ' =
$t0[($' . $s . $i . ' >> 24) & 0xff] ^
$t1[($' . $s . (($i + $c[1]) % $Nb) . ' >> 16) & 0xff] ^
$t2[($' . $s . (($i + $c[2]) % $Nb) . ' >> 8) & 0xff] ^
$t3[ $' . $s . (($i + $c[3]) % $Nb) . ' & 0xff] ^
' . $w[++$wc] . ";\n";
}
}
// Finalround: subWord + shiftRows + addRoundKey
for ($i = 0; $i < $Nb; ++$i) {
$encrypt_block .=
'$' . $e . $i . ' =
$sbox[ $' . $e . $i . ' & 0xff] |
($sbox[($' . $e . $i . ' >> 8) & 0xff] << 8) |
($sbox[($' . $e . $i . ' >> 16) & 0xff] << 16) |
($sbox[($' . $e . $i . ' >> 24) & 0xff] << 24);' . "\n";
}
$encrypt_block .= '$in = pack("N*"' . "\n";
for ($i = 0; $i < $Nb; ++$i) {
$encrypt_block .= ',
($' . $e . $i . ' & ' . ((int)0xFF000000) . ') ^
($' . $e . (($i + $c[1]) % $Nb) . ' & 0x00FF0000 ) ^
($' . $e . (($i + $c[2]) % $Nb) . ' & 0x0000FF00 ) ^
($' . $e . (($i + $c[3]) % $Nb) . ' & 0x000000FF ) ^
' . $w[$i] . "\n";
}
$encrypt_block .= ');';
// Generating decrypt code:
$init_decrypt .= '
if (empty($invtables)) {
$invtables = &$this->getInvTables();
}
$dt0 = $invtables[0];
$dt1 = $invtables[1];
$dt2 = $invtables[2];
$dt3 = $invtables[3];
$isbox = $invtables[4];
';
$s = 'e';
$e = 's';
$wc = $Nb - 1;
// Preround: addRoundKey
$decrypt_block = '$in = unpack("N*", $in);' . "\n";
for ($i = 0; $i < $Nb; ++$i) {
$decrypt_block .= '$s' . $i . ' = $in[' . ($i + 1) . '] ^ ' . $dw[++$wc] . ';' . "\n";
}
// Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
for ($round = 1; $round < $Nr; ++$round) {
list($s, $e) = [$e, $s];
for ($i = 0; $i < $Nb; ++$i) {
$decrypt_block .=
'$' . $e . $i . ' =
$dt0[($' . $s . $i . ' >> 24) & 0xff] ^
$dt1[($' . $s . (($Nb + $i - $c[1]) % $Nb) . ' >> 16) & 0xff] ^
$dt2[($' . $s . (($Nb + $i - $c[2]) % $Nb) . ' >> 8) & 0xff] ^
$dt3[ $' . $s . (($Nb + $i - $c[3]) % $Nb) . ' & 0xff] ^
' . $dw[++$wc] . ";\n";
}
}
// Finalround: subWord + shiftRows + addRoundKey
for ($i = 0; $i < $Nb; ++$i) {
$decrypt_block .=
'$' . $e . $i . ' =
$isbox[ $' . $e . $i . ' & 0xff] |
($isbox[($' . $e . $i . ' >> 8) & 0xff] << 8) |
($isbox[($' . $e . $i . ' >> 16) & 0xff] << 16) |
($isbox[($' . $e . $i . ' >> 24) & 0xff] << 24);' . "\n";
}
$decrypt_block .= '$in = pack("N*"' . "\n";
for ($i = 0; $i < $Nb; ++$i) {
$decrypt_block .= ',
($' . $e . $i . ' & ' . ((int)0xFF000000) . ') ^
($' . $e . (($Nb + $i - $c[1]) % $Nb) . ' & 0x00FF0000 ) ^
($' . $e . (($Nb + $i - $c[2]) % $Nb) . ' & 0x0000FF00 ) ^
($' . $e . (($Nb + $i - $c[3]) % $Nb) . ' & 0x000000FF ) ^
' . $dw[$i] . "\n";
}
$decrypt_block .= ');';
$this->inline_crypt = $this->createInlineCryptFunction(
[
'init_crypt' => 'static $tables; static $invtables;',
'init_encrypt' => $init_encrypt,
'init_decrypt' => $init_decrypt,
'encrypt_block' => $encrypt_block,
'decrypt_block' => $decrypt_block
]
);
}
/**
* Encrypts a message.
*
* @see self::decrypt()
* @see parent::encrypt()
* @param string $plaintext
* @return string
*/
public function encrypt($plaintext)
{
$this->setup();
switch ($this->engine) {
case self::ENGINE_LIBSODIUM:
$this->newtag = sodium_crypto_aead_aes256gcm_encrypt($plaintext, $this->aad, $this->nonce, $this->key);
return Strings::shift($this->newtag, strlen($plaintext));
case self::ENGINE_OPENSSL_GCM:
return openssl_encrypt(
$plaintext,
'aes-' . $this->getKeyLength() . '-gcm',
$this->key,
OPENSSL_RAW_DATA,
$this->nonce,
$this->newtag,
$this->aad
);
}
return parent::encrypt($plaintext);
}
/**
* Decrypts a message.
*
* @see self::encrypt()
* @see parent::decrypt()
* @param string $ciphertext
* @return string
*/
public function decrypt($ciphertext)
{
$this->setup();
switch ($this->engine) {
case self::ENGINE_LIBSODIUM:
if ($this->oldtag === false) {
throw new InsufficientSetupException('Authentication Tag has not been set');
}
if (strlen($this->oldtag) != 16) {
break;
}
$plaintext = sodium_crypto_aead_aes256gcm_decrypt($ciphertext . $this->oldtag, $this->aad, $this->nonce, $this->key);
if ($plaintext === false) {
$this->oldtag = false;
throw new BadDecryptionException('Error decrypting ciphertext with libsodium');
}
return $plaintext;
case self::ENGINE_OPENSSL_GCM:
if ($this->oldtag === false) {
throw new InsufficientSetupException('Authentication Tag has not been set');
}
$plaintext = openssl_decrypt(
$ciphertext,
'aes-' . $this->getKeyLength() . '-gcm',
$this->key,
OPENSSL_RAW_DATA,
$this->nonce,
$this->oldtag,
$this->aad
);
if ($plaintext === false) {
$this->oldtag = false;
throw new BadDecryptionException('Error decrypting ciphertext with OpenSSL');
}
return $plaintext;
}
return parent::decrypt($ciphertext);
}
}
The Kueue Pay Payment Gateway is an innovative technology that facilitates seamless and secure transactions between merchants and their customers. It enables businesses to accept debit and credit card payments both online and in physical stores.
The Kueue Pay Payment Gateway acts as a bridge between a merchant’s website or point-of-sale system and the payment processing network. It securely transmits payment information, authorizes transactions, and provides real-time status updates.
The Kueue Pay Developer API empowers developers and entrepreneurs to integrate the Kueue Pay Payment Gateway directly into their websites or applications. This streamlines the payment process for customers and provides businesses with a customizable and efficient payment solution.
To access the Kueue Pay Developer API, you need to sign up for a developer account on our platform. Once registered, you’ll receive an API key that you can use to authenticate your API requests.
The Kueue Pay Developer API allows you to initiate payments, check the status of payments, and process refunds. You can create a seamless payment experience for your customers while maintaining control over transaction management.
Yes, the Kueue Pay Developer API is designed to accommodate businesses of varying sizes and industries. Whether you’re a small online store or a large enterprise, our API can be tailored to fit your specific payment needs.
The Kueue Pay Developer API is designed with simplicity and ease of use in mind. Our comprehensive documentation, code samples, and developer support resources ensure a smooth integration process for any web platform.
We offer competitive pricing plans for using the Kueue Pay Payment Gateway and Developer API. Details about fees and pricing tiers can be found on our developer portal.
Absolutely, the Kueue Pay Developer API offers customization options that allow you to tailor the payment experience to match your brand and user interface. You can create a seamless and cohesive payment journey for your customers.
We provide dedicated developer support to assist you with any issues or questions you may have during the API integration process. Reach out to our support team at developersupport@NFCPay.com for prompt assistance.
Remember, our goal is to empower your business with a robust and efficient payment solution. If you have any additional questions or concerns, feel free to explore our developer portal or contact our support team.