Skip to content

Commit

Permalink
11.0.0 (#21)
Browse files Browse the repository at this point in the history
* no message

* no message

* no message

* no message

* no message

* no message

* no message

* no message

* no message

* no message

* no message

* no message

* no message

* no message
  • Loading branch information
mmeyer2k committed Jul 1, 2019
1 parent e427000 commit ce8c782
Show file tree
Hide file tree
Showing 39 changed files with 551 additions and 697 deletions.
139 changes: 50 additions & 89 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,22 @@ For legacy PHP version support, look [here](https://github.com/mmeyer2k/dcrypt/b
- [Install](#install)
- [Features](#features)
- [Block Ciphers](#block-ciphers)
- [AES-256 GCM Encryption](#aes-256-gcm-encryption)
- [Other AES-256 Modes](#other-aes-256-modes)
- [Custom Encryption Suites](#sustom-encryption-suites)
- [Static Wrapper](#static-wrapper)
- [Class Overloading](#class-overloading)
- [Layered Encryption Factory](#layered-encryption-factory)
- [Message Authenticity Checking](#message-authenticity-checking)
- [Stream Ciphers](#stream-ciphers)
- [One Time Pad](#one-time-pad)
- [Show me some love](#show-me-some-love-heart_eyes) :heart_eyes::beer:

# Install
Add dcrypt to your composer.json file requirements.
Don't worry, dcrypt does not have any dependencies of its own.
```bash
composer require "mmeyer2k/dcrypt=^10.0"
composer require "mmeyer2k/dcrypt=^11.0"
```

# Features
Expand All @@ -30,20 +38,21 @@ composer require "mmeyer2k/dcrypt=^10.0"

The dcrypt library helps application developers avoid common mistakes in crypto implementations that leave data at risk while still providing flexibility in its options for crypto enthusiasts.
Dcrypt strives to make correct usage simple, but it _is_ possible to use dcrypt incorrectly.
Fully understanding the instructions is important.

__NOTE__: Dcrypt's default configurations assume the usage of a base64 encoded high entropy key with a minimum of 256 bytes.
Be sure to read the section on key hardening and pay close attention to the differences between `$key` and `$password`.

To generate a strong new key execute this command line:
Dcrypt's functions __require__ the use of a high entropy 256 byte (minimum) key encoded with base64.
To generate a new key quickly, execute this on the command line:

```bash
head -c 256 /dev/urandom | base64 -w 0 | xargs echo
```

Storing this key safely is up to you!

### AES-256 GCM Encryption

PHP 7.1 ships with support for new AEAD encryption modes, GCM being considered the safest of these.
Dcrypt will handle the 32 bit AEAD authentication tag, SHA-256 HMAC and initialization vector as a single string.
Since PHP 7.1 supports native AEAD encryption modes, using GCM would be safest option for most applications.
Dcrypt will handle the 32 bit AEAD authentication tag, SHA3-256 HMAC ([Keccak](https://en.wikipedia.org/wiki/SHA-3)), initialization vector and encrypted message as a single unencoded string.

```php
<?php
Expand All @@ -67,6 +76,7 @@ Several AES-256 encryption modes are supported out of the box via hardcoded clas
| `\Dcrypt\Aes256Gcm` | `aes-256-gcm` | [wiki](https://en.wikipedia.org/wiki/Galois/Counter_Mode) |
| `\Dcrypt\Aes256Cbc` | `aes-256-cbc` | [wiki](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation) |
| `\Dcrypt\Aes256Ctr` | `aes-256-ctr` | [wiki](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)) |
| `\Dcrypt\Aes256Ofb` | `aes-256-ofb` | [wiki](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_Feedback_(OFB)) |
| `\Dcrypt\Aes256Ecb` | `aes-256-ecb` | [wiki](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#ECB) |

### Custom Encryption Suites
Expand All @@ -80,9 +90,9 @@ Use any cipher/algo combination by calling the `OpensslStatic` class.

```php
<?php
$encrypted = \Dcrypt\OpensslStatic::encrypt('a secret', $key, 'des-ofb', 'md5');
$encrypted = \Dcrypt\OpensslStatic::encrypt('a secret', $key, 'bf-ofb', 'crc32');

$plaintext = \Dcrypt\OpensslStatic::decrypt($encrypted, $key, 'des-ofb', 'md5');
$plaintext = \Dcrypt\OpensslStatic::decrypt($encrypted, $key, 'bf-ofb', 'crc32');
```

#### Class Overloading
Expand All @@ -92,7 +102,7 @@ Dcrypt's internal functions are easily extendable by overloading the `OpensslBri
```php
<?php

class BlowfishCrc extends \Dcrypt\OpensslBridge
class BlowfishCrc32 extends \Dcrypt\OpensslBridge
{
const CIPHER = 'bf-ofb';

Expand All @@ -104,58 +114,12 @@ then...

```php
<?php
$encrypted = \BlowfishCrc::encrypt('a secret', $password);

$plaintext = \BlowfishCrc::decrypt($encrypted, $password);
```

### Message Authenticity Checking
By default, `\Dcrypt\Exceptions\InvalidChecksumException` exception will be raised before decryption is allowed to proceed when the supplied checksum is not valid.

```php
<?php
$encrypted = \Dcrypt\Aes256Gcm::encrypt('a secret', $key);

// Mangle the encrypted data by adding a single character
$encrypted = $encrypted . 'A';

try {
$decrypted = \Dcrypt\Aes256Gcm::decrypt($encrypted, $key);
} catch (\Dcrypt\Exceptions\InvalidChecksumException $ex) {
// ...
}
```

### PBKDF2 Key Hardening

Key-based encryption mode is _highly_ preferred because the PBKDF2 hardening process can be skipped, reducing overhead.
If using strong keys never use these options.

When using a source of low entropy for the password/key (or "passkey") parameter, a `$cost` value of appropriate size _must_ be chosen based on the requirements of the application.
High cost values could lead to DoS attacks if used improperly for your application, use caution when selecting this number.

The PBKDF2 cost can be defined in a custom class...

```php
<?php

class Aes256GcmWithCost extends \Dcrypt\Aes256Gcm
{
const COST = 1000000;
}
```

or by passing a third parameter to the (en|de)crypt calls.
The `$cost` parameter always overloads any value stored in the class's `const COST`.

```php
<?php
$encrypted = \Dcrypt\Aes256Gcm::encrypt('a secret', $password, 10000);
$encrypted = \BlowfishCrc32::encrypt('a secret', $key);

$plaintext = \Dcrypt\Aes256Gcm::decrypt($encrypted, $password, 10000);
$plaintext = \BlowfishCrc32::decrypt($encrypted, $key);
```

### Layered Encryption Factory
#### Layered Encryption Factory

Feeling especially paranoid?
Is the NSA monitoring your brainwaves?
Expand All @@ -176,15 +140,36 @@ $encrypted = $stack->encrypt('a secret');
$plaintext = $stack->decrypt($encrypted);
```

### Message Authenticity Checking

By default, `\Dcrypt\Exceptions\InvalidChecksumException` exception will be raised before decryption is allowed to proceed when the supplied checksum is not valid.

```php
<?php
$encrypted = \Dcrypt\Aes256Gcm::encrypt('a secret', $key);

// Mangle the encrypted data by adding a single character
$encrypted = $encrypted . 'A';

try {
$decrypted = \Dcrypt\Aes256Gcm::decrypt($encrypted, $key);
} catch (\Dcrypt\Exceptions\InvalidChecksumException $ex) {
// ...
}
```

## Stream Ciphers

Be sure you understand the risks and inherent issues of using a stream cipher before proceeding.
Read the relevant information before using a stream cipher for anything important

### One Time Pad Encryption
- [https://en.wikipedia.org/wiki/Stream_cipher_attacks](https://en.wikipedia.org/wiki/Stream_cipher_attacks)
- [https://jameshfisher.com/2018/01/01/making-a-stream-cipher/](https://jameshfisher.com/2018/01/01/making-a-stream-cipher/)

A fast symmetric stream cipher is quickly accessible with the `Otp` class.
`Otp` uses SHA-512 to output a keystream that is ⊕'d with the input in 512 bit chunks.
### One Time Pad

A fast symmetric stream cipher is quickly accessible with the `Otp` class.
`Otp` uses SHA3-512 to output a keystream that is ⊕'d with the input in 512 bit chunks.

```php
<?php
Expand All @@ -194,43 +179,19 @@ $plaintext = \Dcrypt\Otp::crypt($encrypted, $key);
```

`Otp` can also be configured to use any other hashing algorithm to generate the pseudorandom keystream.

```php
<?php
$encrypted = \Dcrypt\Otp::crypt('a secret', $key, 'whirlpool');

$plaintext = \Dcrypt\Otp::crypt($encrypted, $key, 'whirlpool');
```

### Rivest's Ciphers

`\Dcrypt\Rc4` and `\Dcrypt\Spritz` are pure PHP implementations of the immortal [RC4](https://en.wikipedia.org/wiki/RC4) cipher and its successor [Spritz](https://people.csail.mit.edu/rivest/pubs/RS14.pdf).

```php
<?php
$encrypted = \Dcrypt\Rc4::crypt('a secret', $password);

$plaintext = \Dcrypt\Rc4::crypt($encrypted, $password);
```
```php
<?php
$encrypted = \Dcrypt\Spritz::crypt('a secret', $password);

$plaintext = \Dcrypt\Spritz::crypt($encrypted, $password);
```

**NOTE**:
These implementations are for reference only and are fully marked as `@deprecated`.
The RC4 cipher in general has many known security problems, and the Spirtz implementation provided here has not been verified against known test vectors.
Both are very slow and inefficient.
This was just for fun.

**NOTE**:
Backwards compatibility breaking changes to these classes will not result in an incremented major version number.

# Show me some love :heart_eyes::beer:

Developing dcrypt has been a great journey for many years.
If you find dcrypt useful, please consider donating some Litecoin.

`LN97LrLCNiv14V6fntp247H2pj9UiFzUQZ`
__`LN97LrLCNiv14V6fntp247H2pj9UiFzUQZ`__

![litecoin address](https://mmeyer2k.github.io/images/litecoin-wallet.png)
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
},
"autoload-dev": {
"classmap": [
"examples/",
"tests/helpers/",
"examples/"
"tests/"
]
},
"minimum-stability": "dev"
Expand Down
51 changes: 51 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Changes in Dcrypt

## 11.0.0
- Move to default of SHA3-256 instead of SHA-256 for block ciphers
- Move to default of SHA3-512 instead of SHA-512 for OTP
- Fix error in Aes256Cbc cipher identifier
- Add tests for naming errors
- Remove all support for passwords in favor of strong keys
- All `$cost` related parameters removed
- Add namespaces to testing classes to prevent collisions (unlikely)
- Remove RC4 and Spritz support, will move to separate repo
- Made tests prettier
- Made vector generator more useful
- Removes some custom error handling by falling back on `strict_types=1`
- Removed unused exception types

## 10.0.1
- Add error handling when using cost with key
- Fixes to test readability
- Improvements to code comments

## 10.0.0
- More robust OO key management system for block ciphers and otp
- Moved to PHP minimum of 7.1
- More robust testing
- Stacking system to chain algorithms and ciphers
- Use `hash_hkdf()` to derive keys
- Drop support for some pointless code
- Move to circle-ci testing away from travis
- Removed ROT218

## 9.2.0
- Added ROT128 function class
- Added `examples/` folder
- Added type strictness to tests
- Revamped some docs

## 9.1.1
- Uses `strict_types=1` on all sources files (thank you PHP7)
- More functions are private/protected to keep API slim and consumable
- Isolated functions making dcrypt less likely to be misused
- Updates to code consistency

## 9.1.0
- Adds support for GCM encryption modes in PHP 7.1+
- Adds static encryption class for custom calls
- Improvements to structure
- Improvements to testing

## 9.0.0
- Removes some remains of mcrypt code
52 changes: 31 additions & 21 deletions docs/CRYPTO.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,45 @@ This document serves as a high level design document for the block cipher functi

## Definitions
- `SALT` initialization vector generated with `random_bytes`.
- `COST` integer which will ultimately be passed as cost parameter to `PBKDF2`. Can be set to `0` in cases where a high entropy key is supplied for `PASSKEY`.
- `CIPHER` the chosen cipher method as a string
- `ALGO` the chosen hmac algorithm as a string
- `PASSKEY` a password or key selected
- `KEY` high entropy key selected for symmetric encryption
- `ENCRINFO` is the string `encryptionKey` + `|` + `CIPHER`
- `AUTHINFO` is the string `authenticationKey` + `|` + `CIPHER`
- `MTEXT` the plaintext message to be encrypted
- `PBKDF2` is the password-based key derivation function supported by PHP (hash_pbkdf2). The parameters are:
- `ALGO`
- `PASSKEY`
- `SALT`
- `COST` number of iterations
- `HKDF` is the password-based key derivation function supported by PHP (hash_hkdf) and defined as ([RFC-5869](https://tools.ietf.org/html/rfc5869)). The parameters are:
- `ALGO`
- derived key generated by `PBKDF2`
- `HKDF` is the key derivation function supported by PHP ([hash_hkdf](https://www.php.net/manual/en/function.hash-hkdf.php)) and defined as ([RFC-5869](https://tools.ietf.org/html/rfc5869)). The parameters are:
- hashing algo to use
- key to hash with
- info string parameter
- `HMAC` is a HMAC checksum function supported by PHP (hash_hmac). The parameters are:
- input data to hash
- `ALGO`
- `AKEY`
- hashing algo to use
- key to hash with
- `OPENSSL_ENCRYPT`. The parameters are:
- input data to encrypt
- key to hash with
- iv
- `OPENSSL_DECRYPT`. The parameters are:
- input data to decrypt
- key to hash with
- iv
- tag

## Testing key validity
Before any encryption/decryption calls, a key derivation object must be created.
This object tests the key supplied to it to make sure that it:
1. Can be decoded as a base64 string.
1. The size after decoding meets or exceeds 256 bytes.

Providing a high quality key is __essential__ to the security level it provides.

## Steps for encryption
1. Obtain a new `SALT` of appropriate size for given `CIPHER`
1. Derive a new key `PKEY` from `PASSKEY` using `PBKDF2()` when `COST >= 1`. If `COST = 0` (default) then `PKEY = PASSKEY`.
1. Derive authentication key `AKEY` = `HKDF` with info parameter = `AUTHINFO`
1. Derive encryption key `EKEY` = `HKDF` with info parameter = `ENCRINFO`
1. Use `OPENSSL_ENCRYPT` to get the raw encrypted string `CTEXT`
1. Generate a checksum where `CHECKSUM = HMAC(CTEXT)`
1. Concatenate the following values
1. Derive authentication key `AKEY = HKDF(ALGO, KEY, AUTHINFO)`
1. Derive encryption key `EKEY = HKDF(ALGO, KEY, ENCRINFO)`
1. Encrypt the data as `CTEXT = OPENSSL_ENCRYPT(MTEXT, EKEY, SALT)`
1. Generate a checksum where `CHECKSUM = HMAC(CTEXT, ALGO, AKEY)`
1. Concatenate and return the following values
1. `SALT`
1. `CHECKSUM`
1. `TAG` (if required by `CIPHER`, otherwise skip)
Expand All @@ -42,9 +52,9 @@ This document serves as a high level design document for the block cipher functi
1. Pop `SALT` off front of `CTEXT`
1. Same as step 2 from above
1. Same as step 3 from above
1. Save as step 4 from above
1. Pop `CHECKSUM` from front of `CTEXT`
1. Pop `TAG` from front of `CTEXT`
1. Generate a checksum where `COMPUTED = HMAC(CTEXT)`
1. Generate a checksum where `COMPUTED = HMAC(CTEXT, ALGO, AKEY)`
1. If `COMPUTED != CHECKSUM` throw an exception
1. Use `OPENSSL_DECRYPT` to decrypt the raw data and return
1. Decrypt data as `MTEXT = OPENSSL_DECRYPT(CTEXT, EKEY, SALT, TAG)`
1. Return `MTEXT`
8 changes: 7 additions & 1 deletion docs/UPGRADE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Upgrade from 10.x to 11.x
- You are now _required_ to provide a key to all encrypt/decrypt functions
- All `cost` parameters have been remove, password-based key derivation is no longer offered
- `Rc4` and `Spritz` are removed and will be placed in a separate library
- SHA3 replaces SHA2 wherever default

# Upgrade from 9.x to 10.x
This is a major refactor of the core of dcrypt to focus on the most important features.
- Minimum PHP version is now 7.1
Expand All @@ -9,7 +15,7 @@ This is a major refactor of the core of dcrypt to focus on the most important fe
- **All data encrypted with Aes\*\*\* functions from prior versions will not be decryptable**

# Upgrade from 8.x to 9.x
Version 9 is a MAJOR update to dcrypt and breaks almost all backword compatibility.
Version 9 is a MAJOR update to dcrypt and breaks almost all backward compatibility.
It removes all legacy crutches and moves to use more a more modern design.

- All data encrypted with AesCtr and AesCbc prior to 9.0 will not be compatible in 9.0.
Expand Down
Loading

0 comments on commit ce8c782

Please sign in to comment.