<commit_before># PSR-7 Stream Encryption Decorators

[![Build Status](https://travis-ci.org/jeskew/php-encrypted-streams.svg?branch=master)](https://travis-ci.org/jeskew/php-encrypted-streams)
[![Total Downloads](https://img.shields.io/packagist/dt/jsq/psr7-stream-encryption.svg?style=flat)](https://packagist.org/packages/jsq/psr7-stream-encryption)
[![Author](http://img.shields.io/badge/author-@jreskew-blue.svg?style=flat-square)](https://twitter.com/jreskew)

PHP's built-in OpenSSL bindings provide a convenient means of encrypting and
decrypting data. The interface provided by `ext-openssl`, however, only operates
on strings, so decrypting a large ciphertext would require loading the entire
ciphertext into memory and receiving a string containing the entirety of the
decoded plaintext.

This package aims to allow the encryption and decryption of streams of arbitrary
size. It supports streaming encryption and decryption using AES-CBC, AES-CTR,
and AES-ECB.

> Using AES-ECB is **NOT RECOMMENDED** for new systems. It is included to allow
interoperability with older systems. Please consult [Wikipedia](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_Codebook_.28ECB.29)
for a discussion of the drawbacks of ECB.

## Usage

Decorate an instance of `Psr\Http\Message\StreamInterface` with an encrypting
decorator to incrementally encrypt the contents of the decorated stream as 
`read` is called on the decorating stream:

```php
$cipherMethod = new Jsq\EncryptionStreams\Cbc(
    random_bytes(openssl_cipher_iv_length('aes-256-cbc'))
);
$key = ... // a symmetric encryption key 
// Create a PSR-7 stream for a very large file.
$plaintext = new GuzzleHttp\Psr7\LazyOpenStream('/path/to/a/massive/file', 'r+);
// Create an encrypting stream.
$ciphertext = new Jsq\EncryptionStreams\AesEncryptingStream(
    $plaintext,
    $key,
    $cipherMethod
);

$encryptedChunk = $ciphertext->read(1024 * 1024);
```

No encryption is performed until `read` is called on the encrypting stream.

To calculate the HMAC of a cipher text, wrap a decorated stream with an instance
of `HashingStream`:

```php
$hash = null;
$ciphertext = new Jsq\EncryptionStreams\AesEncryptingStream(
    $plaintext,
    $key,
    $cipherMethod
);
$hashingDecorator = new Jsq\EncryptionStreams\HashingStream(
    $ciphertext,<commit_msg>Update first usage example<commit_after># PSR-7 Stream Encryption Decorators

[![Build Status](https://travis-ci.org/jeskew/php-encrypted-streams.svg?branch=master)](https://travis-ci.org/jeskew/php-encrypted-streams)
[![Total Downloads](https://img.shields.io/packagist/dt/jsq/psr7-stream-encryption.svg?style=flat)](https://packagist.org/packages/jsq/psr7-stream-encryption)
[![Author](http://img.shields.io/badge/author-@jreskew-blue.svg?style=flat-square)](https://twitter.com/jreskew)

PHP's built-in OpenSSL bindings provide a convenient means of encrypting and
decrypting data. The interface provided by `ext-openssl`, however, only operates
on strings, so decrypting a large ciphertext would require loading the entire
ciphertext into memory and receiving a string containing the entirety of the
decoded plaintext.

This package aims to allow the encryption and decryption of streams of arbitrary
size. It supports streaming encryption and decryption using AES-CBC, AES-CTR,
and AES-ECB.

> Using AES-ECB is **NOT RECOMMENDED** for new systems. It is included to allow
interoperability with older systems. Please consult [Wikipedia](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_Codebook_.28ECB.29)
for a discussion of the drawbacks of ECB.

## Usage

Decorate an instance of `Psr\Http\Message\StreamInterface` with an encrypting
decorator to incrementally encrypt the contents of the decorated stream as
`read` is called on the decorating stream:

```php
$iv = random_bytes(openssl_cipher_iv_length('aes-256-cbc'));
$cipherMethod = new Cbc($iv);
$key = 'some-secret-password-here';

$inStream = new Stream(fopen('some-input-file', 'r')); // Any PSR-7 stream will be fine here
$cipherTextStream = new AesEncryptingStream($inStream, $key, $cipherMethod); // Wrap the stream in an EncryptingStream
$cipherTextFile = Psr7\stream_for(fopen('encrypted.file', 'w'));
Psr7\copy_to_stream($cipherTextStream, $cipherTextFile); // When you read from the encrypting stream, the data will be encrypted.

// You'll also need to store the IV somewhere, because we'll need it later to decrypt the data.
// In this case, I'll base64 encode it and stick it in a file (but we could put it anywhere where we can retrieve it later, like a database column)
file_put_contents('encrypted.iv', base64_encode($iv));
```

No encryption is performed until `read` is called on the encrypting stream.

To calculate the HMAC of a cipher text, wrap a decorated stream with an instance
of `HashingStream`:

```php
$hash = null;
$ciphertext = new Jsq\EncryptionStreams\AesEncryptingStream(
    $plaintext,
    $key,
    $cipherMethod
);
$hashingDecorator = new Jsq\EncryptionStreams\HashingStream(
    $ciphertext,
{'fraction_non_alphanumeric': 0.10587769641796953, 'fraction_numerical': 0.007322382742924995, 'mean_word_length': 5.231812577065352, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 6, 'https://': 12, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1930119', 'n_tokens_mistral': 1651, 'n_tokens_neox': 1546, 'n_words': 491}