Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?phpnamespace Aws\Crypto\Polyfill;use 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(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,Key $key,$aad,&$tag,$keySize = 256,$blockSize = 8192) {self::needs(self::strlen($nonce) === 12,'Nonce must be exactly 12 bytes',InvalidArgumentException::class);$encryptor = new 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 . "\x00\x00\x00\x02");/* 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 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,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 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 ByteArray(self::substr($ciphertext, $i, $encryptor->blockSize));$gmac->update($cBlock);}/* Validate auth tag in constant-time: */$calc = $gmac->finish($aadLength, $ciphertextLength);$expected = new 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 . "\x00\x00\x00\x02");}/*** 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 Gmac($this->aesKey,$nonce . "\x00\x00\x00\x01",$this->keySize);$aadBlock = new 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);}}