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); } }