Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
bbb2055
Boyscouting
paragonie-scott Oct 19, 2015
d3520c9
Prevent partial reads/writes
paragonie-scott Oct 19, 2015
fc2f5af
Revert inheritance removal
paragonie-scott Oct 19, 2015
666692e
Fix out-of-bounds read errors in encryptResource()
paragonie-scott Oct 19, 2015
e1d3e35
Comment consistency, don't throw a generic Exception.
paragonie-scott Oct 27, 2015
235bcac
Update README in v2.0 branch to specify AES-256
paragonie-scott Nov 3, 2015
c82cc0d
Increase HKDF-SHA256 salt size to 256 bits
paragonie-scott Nov 20, 2015
e73a15f
Merge pull request #137 from paragonie/v2.0-saltsize2
defuse Nov 21, 2015
3017bf5
Make File use Key objects, apply TOCTOU fixes.
paragonie-scott Dec 10, 2015
516f096
Harden the autoloader against unserialize -> LFI risks
paragonie-scott Dec 10, 2015
081f010
File::encryptFile() and File::decryptFile() should return true.
paragonie-scott Dec 10, 2015
1b4f9c0
Minor cleanup.
paragonie-scott Dec 10, 2015
3600dad
Add KeyConfig to classmap (see PR 128)
paragonie-scott Dec 10, 2015
16009d2
Fix Travis errors
paragonie-scott Dec 10, 2015
f026f8e
Remove unnecessary comment
paragonie-scott Dec 10, 2015
0520106
Merge pull request #142 from defuse/v2.0-travis-fix
paragonie-scott Dec 10, 2015
4e82043
Merge pull request #140 from paragonie/v2.0-autoloader
defuse Dec 11, 2015
d871915
Merge pull request #129 from paragonie/v2.0-stream-ops
defuse Dec 11, 2015
3d232f6
add phpunit tests for File
sethbattin Dec 12, 2015
898f623
remove previous scripts for File tests
sethbattin Dec 12, 2015
5a257d8
insert error cases from preivous test/stream/error.php
sethbattin Dec 12, 2015
529f794
Merge pull request #147 from sethbattin/issue/144_convert_file_tests
defuse Dec 12, 2015
22f43dc
Add helper method for openssl_cipher_iv_length().
twistor Dec 15, 2015
a2269c2
Add more instructions to the README
paragonie-scott Dec 21, 2015
a32bc7f
Merge pull request #152 from twistor/openssl_cipher_iv_length
defuse Dec 22, 2015
afda73a
Merge pull request #127 from paragonie/v2.0
paragonie-scott Jan 17, 2016
ac0b7c7
Merge pull request #128 from paragonie/v2.0-boyscouting
paragonie-scott Feb 7, 2016
5502769
* Update README to include hyperlinks
paragonie-scott Feb 12, 2016
3579f9e
Merge upstream/v2.0 into v2.0-readme
paragonie-scott Feb 12, 2016
81cc8cf
Update README.
paragonie-scott Feb 12, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 144 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,149 @@ This is a class for doing symmetric encryption in PHP. **Requires PHP 5.4 or new
Implementation
--------------

Messages are encrypted with AES-128 in CTR mode and are authenticated with
Messages are encrypted with AES-256 in CTR mode and are authenticated with
HMAC-SHA256 (Encrypt-then-Mac). HKDF is used to split the user-provided key into
two keys: one for encryption, and the other for authentication. It is
implemented using the `openssl_` and `hash_hmac` functions.

## Installing this Library

### Using Composer

```sh
composer require defuse/php-encryption
```

### Direct Installation (Phar)

Download the PHP Archive and public key. Extract

### Direct Installation (Manual)

Download the [latest release](https://github.com/defuse/php-encryption/releases). Extract all of the files into a directory on your webserver (e.g. `/var/www/lib/defuse/php-encryption`).

Then add this to your PHP scripts:

```php
require '/var/www/lib/defuse/php-encryption/autoload.php';
```

## Using this Library

1. Generate and store an encryption key.
2. Encrypt plaintext strings with your key to obtain ciphertext, using `Crypto`.
3. Decrypt ciphertext strings with your key to obtain plaintext, using `Crypto`.
4. Encrypt/decrypt files with your key, using `File`.

### Generate and Store an Encryption Key

Generate a new key:

```php
$key = \Defuse\Crypto\Key::createNewRandomKey();
````

The above command will generate a random encryption key, using a
cryptographically secure pseudorandom number generator. This will generally only
need to be done *once* if you need to reuse this key for multiple messages.

```php
$encryptionKeyDataForStorage = $key->saveToAsciiSafeString()
```

This returns an encoded string that you can use to persist a key across multiple
runs of the application. You might decide to copy it to a configuration file
not tracked by Git, for example. To load it again on the next script execution,
just do this:

```php
$key = \Defuse\Crypto\Key::LoadFromAsciiSafeString($storedKeyData);
```

### Encrypting Strings

Once you have a `Key` object, you're ready to encrypt data. All you have to do
is pass your desired string and the `Key` object to `Crypto::encrypt()`.

```php
try {
$ciphertext = \Defuse\Crypto\Crypto::encrypt("Test message", $key);
} catch (\Defuse\Crypto\Exception\CryptoTestFailedException $ex) {
die("Our platform is not secure enough to use this cryptography library.");
}
```

### Decrypting Strings

If encryption made sense, then the decryption API should be intuitive and
precisely what you expect it to be:


### Interlude: A Complete Example

First, generate a key and store it:

```php
<?php
require_once "/path/to/defuse/php-encryption/autoload.php';
$key = \Defuse\Crypto\Key::createNewRandomKey();
file_put_contents('shared_key.txt', $key->saveToAsciiSafeString());
```

The two scripts below, `encrypt_msg.php` and `decrypt_msg.php` are command-line
PHP scripts meant to encrypt/decrypt messages using a pre-shared-key.

Sender:

```php
<?php
use \Defuse\Crypto\Crypto;
use \Defuse\Crypto\Key;
require_once "/path/to/defuse/php-encryption/autoload.php';

$keyData = file_get_contents('shared_key.txt');
$key = Key::LoadFromAsciiSafeString($keyData);

$encrypted = Crypto::encrypt($argv[1], $key);
echo $encrypted, "\n";
```

Receiver:

```php
<?php
use \Defuse\Crypto\Crypto;
use \Defuse\Crypto\Key;
require_once "/path/to/defuse/php-encryption/autoload.php';

$keyData = file_get_contents('shared_key.txt');
$key = Key::LoadFromAsciiSafeString();

$decrypted = Crypto::decrypt($argv[1], $key);
echo $decrypted, "\n";
```

If you run this command:

php decrypt_msg.php `php encrypt_msg.php It\ Works\!`

It will print "It works!" into your console. Now, assuming you and your
recipient have the same `shared-key.txt`, you can send messages to/from them and
only they should be able to decrypt them.

### Encrypting and Decrypting Files

In addition to our standard `Crypto::encrypt()` and `Crypto::decrypt()`
interface, this library has a separate class for encrypting/decrypting files.

This is mostly useful for encrypting large files (say, 1.5 GB) on a machine with
very low memory usage (say, a maximum of 64 MB of RAM).

```php
\Defuse\Crypto\File::encryptFile($inputFilename, $outputFilename, $key);
\Defuse\Crypto\File::decryptFile($encryptedFile, $plaintextFile, $key);
```

Audit Status
-------------

Expand Down Expand Up @@ -74,16 +212,12 @@ This library is developed around several core values:

- Rule #4: The library should require no special installation.

> Some PHP encryption libraries, like libsodium-php [1], are not
> straightforward to install and cannot packaged with "just download and
> extract" applications. This library will always be just a handful of PHP
> files that you can copy to your source tree and require().

References:

[1] https://github.com/jedisct1/libsodium-php
> Some PHP encryption libraries, like [libsodium-php](https://github.com/jedisct1/libsodium-php),
> are not straightforward to install and cannot packaged with "just download
> and extract" applications. This library will always be just a handful of
> PHP files that you can copy to your source tree and require().

Authors
---------

This library is authored by Taylor Hornby and Scott Arciszewski.
This library is authored by [Taylor Hornby](https://bqp.io) and [Scott Arciszewski](https://paragonie.com/blog/author/scott-arciszewski).
57 changes: 42 additions & 15 deletions autoload.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/
\spl_autoload_register(function ($class) {
// Project-specific namespace prefix
$prefix = 'Defuse\\Crypto';
$prefix = 'Defuse\\Crypto\\';

// Base directory for the namespace prefix
$base_dir = __DIR__.'/src/';
Expand All @@ -18,19 +18,46 @@

// Get the relative class name
$relative_class = \substr($class, $len);

// Replace the namespace prefix with the base directory, replace namespace
// separators with directory separators in the relative class name, append
// with .php
$file = $base_dir.
\str_replace(
['\\', '_'],
'/',
$relative_class
).'.php';

// If the file exists, require it
if (\file_exists($file)) {
require $file;

/**
* unserialize() -> autoloader -> LFI hardening
*/
$classmap = array(
'Config' =>
'Config.php',
'Core' =>
'Core.php',
'Crypto' =>
'Crypto.php',
'Encoding' =>
'Encoding.php',
'ExceptionHandler' =>
'ExceptionHandler.php',
'File' =>
'File.php',
'FileConfig' =>
'FileConfig.php',
'Key' =>
'Key.php',
'KeyConfig' =>
'KeyConfig.php',
'RuntimeTests' =>
'RuntimeTests.php',
'StreamInterface' =>
'StreamInterface.php',
// Exceptions:
'Exception\\CannotPerformOperationException' =>
'Exception/CannotPerformOperationException.php',
'Exception\\CryptoException' =>
'Exception/CryptoException.php',
'Exception\\CryptoTestFailedException' =>
'Exception/CryptoTestFailedException.php',
'Exception\\InvalidCiphertextException' =>
'Exception/InvalidCiphertextException.php',
);
foreach ($classmap as $classname => $file) {
if ($classname === $relative_class) {
require $base_dir.$file;
}
}
});
29 changes: 22 additions & 7 deletions src/Core.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
namespace Defuse\Crypto;

use \Defuse\Crypto\Exception as Ex;
use \Defuse\Crypto\Crypto;

final class Core
{
Expand All @@ -27,12 +26,7 @@ public static function incrementCounter($ctr, $inc, &$config)
{
static $ivsize = null;
if ($ivsize === null) {
$ivsize = \openssl_cipher_iv_length($config->cipherMethod());
if ($ivsize === false) {
throw new Ex\CannotPerformOperationException(
"Problem obtaining the correct nonce length."
);
}
$ivsize = self::cipherIvLength($config->cipherMethod());
}

if (self::ourStrlen($ctr) !== $ivsize) {
Expand Down Expand Up @@ -73,6 +67,27 @@ public static function incrementCounter($ctr, $inc, &$config)
return $ctr;
}

/**
* Returns the cipher initialization vector (iv) length.
*
* @param string $method
* @return int
* @throws Ex\CannotPerformOperationException
*/
public static function cipherIvLength($method)
{
self::ensureFunctionExists('openssl_cipher_iv_length');
$ivsize = \openssl_cipher_iv_length($method);

if ($ivsize === false || $ivsize <= 0) {
throw new Ex\CannotPerformOperationException(
'Could not get the IV length from OpenSSL'
);
}

return $ivsize;
}

/**
* Returns a random binary string of length $octets bytes.
*
Expand Down
26 changes: 4 additions & 22 deletions src/Crypto.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,7 @@ public static function encrypt($plaintext, $key, $raw_binary = false)
);

// Generate a random initialization vector.
Core::ensureFunctionExists("openssl_cipher_iv_length");
$ivsize = \openssl_cipher_iv_length($config->cipherMethod());
if ($ivsize === false || $ivsize <= 0) {
throw new Ex\CannotPerformOperationException(
"Could not get the IV length from OpenSSL"
);
}
$ivsize = Core::cipherIvLength($config->cipherMethod());
$iv = Core::secureRandom($ivsize);

$ciphertext = $salt . $iv . self::plainEncrypt($plaintext, $ekey, $iv, $config);
Expand Down Expand Up @@ -210,13 +204,7 @@ public static function decrypt($ciphertext, $key, $raw_binary = false)
$ekey = Core::HKDF($config->hashFunctionName(), $key, $config->keyByteSize(), $config->encryptionInfoString(), $salt, $config);

// Extract the initialization vector from the ciphertext.
Core::EnsureFunctionExists("openssl_cipher_iv_length");
$ivsize = \openssl_cipher_iv_length($config->cipherMethod());
if ($ivsize === false || $ivsize <= 0) {
throw new Ex\CannotPerformOperationException(
"Could not get the IV length from OpenSSL"
);
}
$ivsize = Core::cipherIvLength($config->cipherMethod());
if (Core::ourStrlen($ciphertext) <= $ivsize) {
throw new Ex\InvalidCiphertextException(
"Ciphertext is too short."
Expand Down Expand Up @@ -302,13 +290,7 @@ public static function legacyDecrypt($ciphertext, $key)
);

// Extract the initialization vector from the ciphertext.
Core::EnsureFunctionExists("openssl_cipher_iv_length");
$ivsize = \openssl_cipher_iv_length($config->cipherMethod());
if ($ivsize === false || $ivsize <= 0) {
throw new Ex\CannotPerformOperationException(
"Could not get the IV length from OpenSSL"
);
}
$ivsize = Core::cipherIvLength($config->cipherMethod());
if (Core::ourStrlen($ciphertext) <= $ivsize) {
throw new Ex\InvalidCiphertextException(
"Ciphertext is too short."
Expand Down Expand Up @@ -467,7 +449,7 @@ protected static function getVersionConfigFromMajorMinor($major, $minor)
'cipher_method' => 'aes-256-ctr',
'block_byte_size' => 16,
'key_byte_size' => 32,
'salt_byte_size' => 16,
'salt_byte_size' => 32,
'hash_function_name' => 'sha256',
'mac_byte_size' => 32,
'encryption_info_string' => 'DefusePHP|V2|KeyForEncryption',
Expand Down
Loading