Repository URL to install this package:
|
Version:
3.0.2 ▾
|
<?php
namespace WPMailSMTP\Vendor\Aws\Crypto\Polyfill;
use WPMailSMTP\Vendor\Aws\Exception\CryptoPolyfillException;
use InvalidArgumentException;
use RangeException;
/**
* Class AesGcm
*
* This provides a polyfill for AES-GCM encryption/decryption, with caveats:
*
* 1. Only 96-bit nonces are supported.
* 2. Only 128-bit authentication tags are supported. (i.e. non-truncated)
*
* Supports AES key sizes of 128-bit, 192-bit, and 256-bit.
*
* @package Aws\Crypto\Polyfill
*/
class AesGcm
{
use NeedsTrait;
/** @var Key $aesKey */
private $aesKey;
/** @var int $keySize */
private $keySize;
/** @var int $blockSize */
protected $blockSize = 8192;
/**
* AesGcm constructor.
*
* @param Key $aesKey
* @param int $keySize
* @param int $blockSize
*
* @throws CryptoPolyfillException
* @throws InvalidArgumentException
* @throws RangeException
*/
public function __construct(\WPMailSMTP\Vendor\Aws\Crypto\Polyfill\Key $aesKey, $keySize = 256, $blockSize = 8192)
{
/* Preconditions: */
self::needs(\in_array($keySize, [128, 192, 256], \true), "Key size must be 128, 192, or 256 bits; {$keySize} given", \InvalidArgumentException::class);
self::needs(\is_int($blockSize) && $blockSize > 0 && $blockSize <= \PHP_INT_MAX, 'Block size must be a positive integer.', \RangeException::class);
self::needs($aesKey->length() << 3 === $keySize, 'Incorrect key size; expected ' . $keySize . ' bits, got ' . ($aesKey->length() << 3) . ' bits.');
$this->aesKey = $aesKey;
$this->keySize = $keySize;
}
/**
* Encryption interface for AES-GCM
*
* @param string $plaintext Message to be encrypted
* @param string $nonce Number to be used ONCE
* @param Key $key AES Key
* @param string $aad Additional authenticated data
* @param string &$tag Reference to variable to hold tag
* @param int $keySize Key size (bits)
* @param int $blockSize Block size (bytes) -- How much memory to buffer
* @return string
* @throws InvalidArgumentException
*/
public static function encrypt($plaintext, $nonce, \WPMailSMTP\Vendor\Aws\Crypto\Polyfill\Key $key, $aad, &$tag, $keySize = 256, $blockSize = 8192)
{
self::needs(self::strlen($nonce) === 12, 'Nonce must be exactly 12 bytes', \InvalidArgumentException::class);
$encryptor = new \WPMailSMTP\Vendor\Aws\Crypto\Polyfill\AesGcm($key, $keySize, $blockSize);
list($aadLength, $gmac) = $encryptor->gmacInit($nonce, $aad);
$ciphertext = \openssl_encrypt($plaintext, "aes-{$encryptor->keySize}-ctr", $key->get(), \OPENSSL_NO_PADDING | \OPENSSL_RAW_DATA, $nonce . "\0\0\0\2");
/* Calculate auth tag in a streaming fashion to minimize memory usage: */
$ciphertextLength = self::strlen($ciphertext);
for ($i = 0; $i < $ciphertextLength; $i += $encryptor->blockSize) {
$cBlock = new \WPMailSMTP\Vendor\Aws\Crypto\Polyfill\ByteArray(self::substr($ciphertext, $i, $encryptor->blockSize));
$gmac->update($cBlock);
}
$tag = $gmac->finish($aadLength, $ciphertextLength)->toString();
return $ciphertext;
}
/**
* Decryption interface for AES-GCM
*
* @param string $ciphertext Ciphertext to decrypt
* @param string $nonce Number to be used ONCE
* @param Key $key AES key
* @param string $aad Additional authenticated data
* @param string $tag Authentication tag
* @param int $keySize Key size (bits)
* @param int $blockSize Block size (bytes) -- How much memory to buffer
* @return string Plaintext
*
* @throws CryptoPolyfillException
* @throws InvalidArgumentException
*/
public static function decrypt($ciphertext, $nonce, \WPMailSMTP\Vendor\Aws\Crypto\Polyfill\Key $key, $aad, &$tag, $keySize = 256, $blockSize = 8192)
{
/* Precondition: */
self::needs(self::strlen($nonce) === 12, 'Nonce must be exactly 12 bytes', \InvalidArgumentException::class);
$encryptor = new \WPMailSMTP\Vendor\Aws\Crypto\Polyfill\AesGcm($key, $keySize, $blockSize);
list($aadLength, $gmac) = $encryptor->gmacInit($nonce, $aad);
/* Calculate auth tag in a streaming fashion to minimize memory usage: */
$ciphertextLength = self::strlen($ciphertext);
for ($i = 0; $i < $ciphertextLength; $i += $encryptor->blockSize) {
$cBlock = new \WPMailSMTP\Vendor\Aws\Crypto\Polyfill\ByteArray(self::substr($ciphertext, $i, $encryptor->blockSize));
$gmac->update($cBlock);
}
/* Validate auth tag in constant-time: */
$calc = $gmac->finish($aadLength, $ciphertextLength);
$expected = new \WPMailSMTP\Vendor\Aws\Crypto\Polyfill\ByteArray($tag);
self::needs($calc->equals($expected), 'Invalid authentication tag');
/* Return plaintext if auth tag check succeeded: */
return \openssl_decrypt($ciphertext, "aes-{$encryptor->keySize}-ctr", $key->get(), \OPENSSL_NO_PADDING | \OPENSSL_RAW_DATA, $nonce . "\0\0\0\2");
}
/**
* Initialize a Gmac object with the nonce and this object's key.
*
* @param string $nonce Must be exactly 12 bytes long.
* @param string|null $aad
* @return array
*/
protected function gmacInit($nonce, $aad = null)
{
$gmac = new \WPMailSMTP\Vendor\Aws\Crypto\Polyfill\Gmac($this->aesKey, $nonce . "\0\0\0\1", $this->keySize);
$aadBlock = new \WPMailSMTP\Vendor\Aws\Crypto\Polyfill\ByteArray($aad);
$aadLength = $aadBlock->count();
$gmac->update($aadBlock);
$gmac->flush();
return [$aadLength, $gmac];
}
/**
* Calculate the length of a string.
*
* Uses the appropriate PHP function without being brittle to
* mbstring.func_overload.
*
* @param string $string
* @return int
*/
protected static function strlen($string)
{
if (\is_callable('\\mb_strlen')) {
return (int) \mb_strlen($string, '8bit');
}
return (int) \strlen($string);
}
/**
* Return a substring of the provided string.
*
* Uses the appropriate PHP function without being brittle to
* mbstring.func_overload.
*
* @param string $string
* @param int $offset
* @param int|null $length
* @return string
*/
protected static function substr($string, $offset = 0, $length = null)
{
if (\is_callable('\\mb_substr')) {
return \mb_substr($string, $offset, $length, '8bit');
} elseif (!\is_null($length)) {
return \substr($string, $offset, $length);
}
return \substr($string, $offset);
}
}