| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 | <?phpnamespace Aws\Crypto;use GuzzleHttp\Psr7\StreamDecoratorTrait;use \LogicException;use Psr\Http\Message\StreamInterface;use Aws\Crypto\Cipher\CipherMethod;/** * @internal Represents a stream of data to be encrypted with a passed cipher. */class AesEncryptingStream implements AesStreamInterface{    const BLOCK_SIZE = 16; // 128 bits    use StreamDecoratorTrait;    /**     * @var string     */    private $buffer = '';    /**     * @var CipherMethod     */    private $cipherMethod;    /**     * @var string     */    private $key;    /**     * @var StreamInterface     */    private $stream;    /**     * @param StreamInterface $plainText     * @param string $key     * @param CipherMethod $cipherMethod     */    public function __construct(        StreamInterface $plainText,        $key,        CipherMethod $cipherMethod    ) {        $this->stream = $plainText;        $this->key = $key;        $this->cipherMethod = clone $cipherMethod;    }    public function getOpenSslName()    {        return $this->cipherMethod->getOpenSslName();    }    public function getAesName()    {        return $this->cipherMethod->getAesName();    }    public function getCurrentIv()    {        return $this->cipherMethod->getCurrentIv();    }    public function getSize(): ?int    {        $plainTextSize = $this->stream->getSize();        if ($this->cipherMethod->requiresPadding() && $plainTextSize !== null) {            // PKCS7 padding requires that between 1 and self::BLOCK_SIZE be            // added to the plaintext to make it an even number of blocks.            $padding = self::BLOCK_SIZE - $plainTextSize % self::BLOCK_SIZE;            return $plainTextSize + $padding;        }        return $plainTextSize;    }    public function isWritable(): bool    {        return false;    }    public function read($length): string    {        if ($length > strlen($this->buffer)) {            $this->buffer .= $this->encryptBlock(                (int)                self::BLOCK_SIZE * ceil(($length - strlen($this->buffer)) / self::BLOCK_SIZE)            );        }        $data = substr($this->buffer, 0, $length);        $this->buffer = substr($this->buffer, $length);        return $data ? $data : '';    }    public function seek($offset, $whence = SEEK_SET): void    {        if ($whence === SEEK_CUR) {            $offset = $this->tell() + $offset;            $whence = SEEK_SET;        }        if ($whence === SEEK_SET) {            $this->buffer = '';            $wholeBlockOffset                = (int) ($offset / self::BLOCK_SIZE) * self::BLOCK_SIZE;            $this->stream->seek($wholeBlockOffset);            $this->cipherMethod->seek($wholeBlockOffset);            $this->read($offset - $wholeBlockOffset);        } else {            throw new LogicException('Unrecognized whence.');        }    }    private function encryptBlock($length)    {        if ($this->stream->eof()) {            return '';        }        $plainText = '';        do {            $plainText .= $this->stream->read((int) ($length - strlen($plainText)));        } while (strlen($plainText) < $length && !$this->stream->eof());        $options = OPENSSL_RAW_DATA;        if (!$this->stream->eof()            || $this->stream->getSize() !== $this->stream->tell()        ) {            $options |= OPENSSL_ZERO_PADDING;        }        $cipherText = openssl_encrypt(            $plainText,            $this->cipherMethod->getOpenSslName(),            $this->key,            $options,            $this->cipherMethod->getCurrentIv()        );        $this->cipherMethod->update($cipherText);        return $cipherText;    }}
 |