diff --git a/README.md b/README.md index 3a3fe414..98c1b6c2 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 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 += 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) @@ -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 \ No newline at end of file +1. Decrypt data as `MTEXT = OPENSSL_DECRYPT(CTEXT, EKEY, SALT, TAG)` +1. Return `MTEXT` \ No newline at end of file diff --git a/docs/UPGRADE.md b/docs/UPGRADE.md index 946ed3b3..f06f3346 100644 --- a/docs/UPGRADE.md +++ b/docs/UPGRADE.md @@ -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 @@ -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. diff --git a/examples/support.php b/examples/support.php index 35c173fd..5a5cd7d5 100644 --- a/examples/support.php +++ b/examples/support.php @@ -10,7 +10,7 @@ require __DIR__ . '/../vendor/autoload.php'; -$key = \Dcrypt\OpensslKeyGenerator::newKey(); +$key = \Dcrypt\OpensslKey::newKey(); echo "\nCIPHERS ----------------------------------------------------------------------------------------------\n"; diff --git a/examples/vectors.php b/examples/vectors.php index 80871253..8680a7a7 100644 --- a/examples/vectors.php +++ b/examples/vectors.php @@ -1,48 +1,54 @@ $key, + 'algos' => [], + 'ciphers' => [], + 'aes256' => [], + 'otp' => [], +]; foreach (\Dcrypt\OpensslSupported::ciphers() as $cipher) { - $cipher = strtolower($cipher); - if (isset($out[$cipher])) { + if (strtolower($cipher) !== $cipher) { continue; } try { - $out[$cipher] = base64_encode(OpensslStatic::encrypt('hello', 'world', $cipher, 'sha256', 1000)); + $out['ciphers'][$cipher] = base64_encode(OpensslStatic::encrypt('a secret', $key, $cipher, 'sha3-256')); } catch (\Exception|\Error $e) { } } -file_put_contents(__DIR__ . '/../tests/vectors/openssl-static-ciphers.json', \json_encode($out, JSON_PRETTY_PRINT)); - -$out = []; - foreach (\Dcrypt\OpensslSupported::algos() as $algo) { - $cipher = strtolower($algo); - if (isset($out[$algo])) { + if (strtolower($algo) !== $algo) { continue; } - $out[$algo] = base64_encode(OpensslStatic::encrypt('hello', 'world', 'aes-256-gcm', $algo, 1000)); -} -file_put_contents(__DIR__ . '/../tests/vectors/openssl-static-algos.json', \json_encode($out, JSON_PRETTY_PRINT)); + $out['algos'][$algo] = base64_encode(OpensslStatic::encrypt('a secret', $key, 'aes-256-gcm', $algo)); +} -$out = []; +foreach(['Gcm', 'Ctr', 'Ofb', 'Cbc', 'Ecb'] as $mode){ + $c = "\\Dcrypt\\Aes256$mode"; + $out['aes256'][$c] = base64_encode($c::encrypt('a secret', $key)); +} foreach (range(1, 10) as $r) { $mult = $r * $r * 10; - $out[$mult] = \base64_encode(\Dcrypt\Otp::crypt(str_repeat('A', $mult), 'password', 1000)); + $out['otp'][$mult] = \base64_encode(\Dcrypt\Otp::crypt(str_repeat('A', $mult), $key)); } -file_put_contents(__DIR__ . '/../tests/vectors/otp.json', \json_encode($out, JSON_PRETTY_PRINT)); \ No newline at end of file +file_put_contents(__DIR__ . '/../tests/.vectors.json', \json_encode($out, JSON_PRETTY_PRINT)); \ No newline at end of file diff --git a/src/Aes256Cbc.php b/src/Aes256Cbc.php index b5bf4296..c97bf809 100644 --- a/src/Aes256Cbc.php +++ b/src/Aes256Cbc.php @@ -30,5 +30,5 @@ class Aes256Cbc extends Aes256Gcm * * @var string */ - const CIPHER = 'aes-256-cfb'; + const CIPHER = 'aes-256-cbc'; } diff --git a/src/Aes256Gcm.php b/src/Aes256Gcm.php index 85f37974..6b5ada64 100644 --- a/src/Aes256Gcm.php +++ b/src/Aes256Gcm.php @@ -33,9 +33,9 @@ class Aes256Gcm extends OpensslBridge const CIPHER = 'aes-256-gcm'; /** - * Use SHA-256 hashing algo to authenticate messages + * Use SHA3-256 hashing algo to authenticate messages * * @var string */ - const ALGO = 'sha256'; + const ALGO = 'sha3-256'; } diff --git a/src/Aes256Ofb.php b/src/Aes256Ofb.php new file mode 100644 index 00000000..b24c9a98 --- /dev/null +++ b/src/Aes256Ofb.php @@ -0,0 +1,34 @@ + + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + */ + +namespace Dcrypt; + +/** + * Symmetric AES-256-GCM encryption functions powered by OpenSSL. + * + * @category Dcrypt + * @package Dcrypt + * @author Michael Meyer (mmeyer2k) + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + */ +class Aes256Ofb extends Aes256Gcm +{ + /** + * AES-256 cipher identifier that will be passed to openssl + * + * @var string + */ + const CIPHER = 'aes-256-ofb'; +} diff --git a/src/Exceptions/InvalidAlgoException.php b/src/Exceptions/InvalidAlgoException.php deleted file mode 100644 index 9e4720b6..00000000 --- a/src/Exceptions/InvalidAlgoException.php +++ /dev/null @@ -1,8 +0,0 @@ -key = $passkey; - } else { - // Make sure that the user is not attempting to use a key in password word mode - if (Str::strlen($passkey) >= 256) { - throw new InvalidPasswordException("Passwords must be less than 256 bytes."); - } - - // Derive the key from the password and store in object - $this->key = \hash_pbkdf2($algo, $passkey, $ivr, $cost, 0, true); + // Store the key as what was supplied + $this->key = \base64_decode($key); + + // Make sure key was properly decoded and meets minimum required length + if (!is_string($this->key) || Str::strlen($this->key) < 256) { + throw new InvalidKeyException("Key must be at least 256 bytes and base64 encoded."); } // Store the cipher string @@ -114,9 +99,8 @@ public function encryptionKey(): string /** * Derive a key with differing authinfo strings * - * @param string $info + * @param string $info Info parameter to provide to hash_hkdf * @return string - * @throws \Exception */ public function deriveKey(string $info): string { @@ -124,10 +108,6 @@ public function deriveKey(string $info): string $key = \hash_hkdf($this->algo, $this->key, 0, $info, $this->ivr); - if ($key === false) { - throw new Exceptions\InvalidAlgoException("Hash algo $this->algo is not supported by hash_hkdf."); - } - return $key; } @@ -141,7 +121,7 @@ public function deriveKey(string $info): string public static function newKey(int $bytes = 256): string { if ($bytes < 256) { - throw new InvalidKeyException('Key must be at least 256 bytes long.'); + throw new InvalidKeyException('Keys must be at least 256 bytes long.'); } return \base64_encode(\random_bytes($bytes)); diff --git a/src/OpensslStack.php b/src/OpensslStack.php index f903d9da..185693a4 100644 --- a/src/OpensslStack.php +++ b/src/OpensslStack.php @@ -33,29 +33,23 @@ class OpensslStack /** * @var string */ - private $passkey; - - /** - * @var int - */ - private $cost; + private $key; /** * OpensslStack constructor. * * @param string $passkey Password or key - * @param int $cost Cost when using password mode */ - public function __construct(string $passkey, int $cost = 0) + public function __construct(string $key) { - $this->passkey = $passkey; - - $this->cost = $cost; + $this->key = $key; } /** - * @param string $cipher - * @param string $algo + * Add a new cipher/algo combo to the execution stack + * + * @param string $cipher Cipher mode to use + * @param string $algo Hashing algo to use * @return OpensslStack */ public function add(string $cipher, string $algo): self @@ -75,7 +69,7 @@ public function add(string $cipher, string $algo): self public function encrypt(string $data): string { foreach ($this->stack as $s) { - $data = OpensslStatic::encrypt($data, $this->passkey, $s[0], $s[1], $this->cost); + $data = OpensslStatic::encrypt($data, $this->key, $s[0], $s[1]); } return $data; @@ -91,7 +85,7 @@ public function encrypt(string $data): string public function decrypt(string $data): string { foreach (\array_reverse($this->stack) as $s) { - $data = OpensslStatic::decrypt($data, $this->passkey, $s[0], $s[1], $this->cost); + $data = OpensslStatic::decrypt($data, $this->key, $s[0], $s[1]); } return $data; diff --git a/src/OpensslStatic.php b/src/OpensslStatic.php index e81d4ad2..7e9f626d 100644 --- a/src/OpensslStatic.php +++ b/src/OpensslStatic.php @@ -14,6 +14,8 @@ namespace Dcrypt; +use \Dcrypt\Exceptions\InvalidChecksumException; + /** * Static functions that handle encryption/decryption with openssl. * @@ -28,15 +30,14 @@ final class OpensslStatic extends OpensslWrapper /** * Decrypt raw data string * - * @param string $data Data to be decrypted - * @param string $passkey Password or key - * @param string $cipher OpenSSL cipher name - * @param string $algo Hashing and key derivation algo name - * @param int $cost Cost parameter for key derivation or 0 for raw key mode + * @param string $data Data to be decrypted + * @param string $key Key material + * @param string $cipher OpenSSL cipher name + * @param string $algo Hashing and key derivation algo name * @return string * @throws \Exception */ - public static function decrypt(string $data, string $passkey, string $cipher, string $algo, int $cost = 0): string + public static function decrypt(string $data, string $key, string $cipher, string $algo): string { // Calculate the hash checksum size in bytes for the specified algo $hsz = Str::hashSize($algo); @@ -60,14 +61,14 @@ public static function decrypt(string $data, string $passkey, string $cipher, st $msg = Str::substr($data, $isz + $hsz + $tsz); // Create a new password derivation object - $key = new OpensslKeyGenerator($algo, $passkey, $cipher, $ivr, $cost); + $key = new OpensslKey($algo, $key, $cipher, $ivr); // Calculate checksum of message payload for verification $chk = \hash_hmac($algo, $msg, $key->authenticationKey(), true); // Compare given checksum against computed checksum using a time-safe function if (!Str::equal($chk, $sum)) { - throw new Exceptions\InvalidChecksumException('Decryption can not proceed due to invalid cyphertext checksum.'); + throw new InvalidChecksumException('Decryption can not proceed due to invalid cyphertext checksum.'); } // Decrypt message and return @@ -77,21 +78,20 @@ public static function decrypt(string $data, string $passkey, string $cipher, st /** * Encrypt raw string * - * @param string $data Data to be encrypted - * @param string $passkey Password or key - * @param string $cipher OpenSSL cipher name - * @param string $algo Hashing and key derivation algo name - * @param int $cost Cost parameter for key derivation or 0 for raw key mode + * @param string $data Data to be encrypted + * @param string $key Key material + * @param string $cipher OpenSSL cipher name + * @param string $algo Hashing and key derivation algo name * @return string * @throws \Exception */ - public static function encrypt(string $data, string $passkey, string $cipher, string $algo, int $cost = 0): string + public static function encrypt(string $data, string $key, string $cipher, string $algo): string { // Generate IV of appropriate size $ivr = parent::ivGenerate($cipher); - // Create password derivation object - $key = new OpensslKeyGenerator($algo, $passkey, $cipher, $ivr, $cost); + // Create key derivation object + $key = new OpensslKey($algo, $key, $cipher, $ivr); // Create a placeholder for the authentication tag to be passed by reference $tag = ''; diff --git a/src/OpensslWrapper.php b/src/OpensslWrapper.php index aecb4044..6f65b43f 100644 --- a/src/OpensslWrapper.php +++ b/src/OpensslWrapper.php @@ -37,12 +37,10 @@ class OpensslWrapper protected static function openssl_encrypt(string $input, string $method, string $key, string $iv, string &$tag): string { if (OpensslStatic::tagRequired($method)) { - $ret = \openssl_encrypt($input, $method, $key, OPENSSL_RAW_DATA, $iv, $tag, '', 4); + return \openssl_encrypt($input, $method, $key, OPENSSL_RAW_DATA, $iv, $tag, '', 4); } else { - $ret = \openssl_encrypt($input, $method, $key, OPENSSL_RAW_DATA, $iv); + return \openssl_encrypt($input, $method, $key, OPENSSL_RAW_DATA, $iv); } - - return self::returnOrException($ret); } /** @@ -57,28 +55,10 @@ protected static function openssl_encrypt(string $input, string $method, string protected static function openssl_decrypt(string $input, string $method, string $key, string $iv, string $tag): string { if (OpensslStatic::tagRequired($method)) { - $ret = \openssl_decrypt($input, $method, $key, OPENSSL_RAW_DATA, $iv, $tag, ''); + return \openssl_decrypt($input, $method, $key, OPENSSL_RAW_DATA, $iv, $tag, ''); } else { - $ret = \openssl_decrypt($input, $method, $key, OPENSSL_RAW_DATA, $iv); + return \openssl_decrypt($input, $method, $key, OPENSSL_RAW_DATA, $iv); } - - return self::returnOrException($ret); - } - - /** - * Throw an exception if openssl function returns false - * - * @param string|bool $data - * @return string - * @throws \Exception - */ - private static function returnOrException($data): string - { - if ($data === false) { - throw new \Exception('OpenSSL failed to encrypt/decrypt message.'); - } - - return $data; } /** @@ -86,16 +66,11 @@ private static function returnOrException($data): string * * @param string $cipher * @return int - * @throws \Exception */ protected static function ivSize(string $cipher): int { $ret = \openssl_cipher_iv_length($cipher); - if ($ret === false) { - throw new \Exception("Failed to determine correct IV size."); - } - return $ret; } diff --git a/src/Otp.php b/src/Otp.php index c01c3aef..ed4023ff 100644 --- a/src/Otp.php +++ b/src/Otp.php @@ -29,22 +29,21 @@ class Otp /** * Encrypt or decrypt a binary input string. * - * @param string $input Input data to encrypt - * @param string $passkey Encryption/decryption key to use on input - * @param int $cost Cost value to harden password with, or 0 if using a key - * @param string $algo Hashing algo to generate keystream + * @param string $input Input data to encrypt + * @param string $key Encryption/decryption key to use on input + * @param string $algo Hashing algo to generate keystream * @return string */ - public static function crypt(string $input, string $passkey, int $cost = 0, string $algo = 'sha512'): string + public static function crypt(string $input, string $key, string $algo = 'sha3-512'): string { $chunks = \str_split($input, Str::hashSize($algo)); $length = Str::strlen($input); - $key = new OpensslKeyGenerator($algo, $passkey, '', (string)$length, $cost); + $key = new OpensslKey($algo, $key, '', (string)$length); foreach ($chunks as $i => &$chunk) { - $info = $length . $i . $cost; + $info = $length . $i; $chunk = $chunk ^ $key->deriveKey($info); } diff --git a/src/Rc4.php b/src/Rc4.php deleted file mode 100644 index 6fb024cd..00000000 --- a/src/Rc4.php +++ /dev/null @@ -1,78 +0,0 @@ - - * @license http://opensource.org/licenses/MIT The MIT License (MIT) - * @link https://github.com/mmeyer2k/dcrypt - */ - -namespace Dcrypt; - -/** - * An implementation of RC4 symmetric encryption. - * - * @deprecated - * @category Dcrypt - * @package Dcrypt - * @author Michael Meyer (mmeyer2k) - * @license http://opensource.org/licenses/MIT The MIT License (MIT) - * @link https://github.com/mmeyer2k/dcrypt - * @link http://en.wikipedia.org/wiki/Stream_cipher - * @link https://en.wikipedia.org/wiki/RC4 - */ -class Rc4 -{ - /** - * Perform (en/de)cryption - * - * @deprecated - * @param string $str String to be encrypted - * @param string $key Key to use for encryption - * @return string - */ - public static function crypt(string $str, string $key): string - { - $s = self::initializeState($key); - $i = $j = 0; - $res = ''; - $size = Str::strlen($str); - for ($y = 0; $y < $size; $y++) { - $i = ($i + 1) % 256; - $j = ($j + $s[$i]) % 256; - $x = $s[$i]; - $s[$i] = $s[$j]; - $s[$j] = $x; - $res .= $str[$y] ^ \chr($s[($s[$i] + $s[$j]) % 256]); - } - - return $res; - } - - /** - * Create the initial byte matrix that will be used for swaps. This code - * is identical between RC4 and Spritz. - * - * @deprecated - * @param string $key - * @return array - */ - protected static function initializeState(string $key): array - { - $s = \range(0, 255); - $j = 0; - foreach (\range(0, 255) as $i) { - $j = ($j + $s[$i] + \ord($key[$i % Str::strlen($key)])) % 256; - $x = $s[$i]; - $s[$i] = $s[$j]; - $s[$j] = $x; - } - - return $s; - } -} diff --git a/src/Spritz.php b/src/Spritz.php deleted file mode 100644 index a016dadc..00000000 --- a/src/Spritz.php +++ /dev/null @@ -1,60 +0,0 @@ - - * @license http://opensource.org/licenses/MIT The MIT License (MIT) - * @link https://github.com/mmeyer2k/dcrypt - */ - -namespace Dcrypt; - -/** - * An implementation of Spritz symmetric encryption. - * - * @deprecated - * @category Dcrypt - * @package Dcrypt - * @author Michael Meyer (mmeyer2k) - * @license http://opensource.org/licenses/MIT The MIT License (MIT) - * @link https://github.com/mmeyer2k/dcrypt - * @link http://en.wikipedia.org/wiki/Stream_cipher - * @link https://en.wikipedia.org/wiki/RC4 - * @link http://people.csail.mit.edu/rivest/pubs/RS14.pdf - */ -class Spritz extends Rc4 -{ - /** - * Perform (en/de)cryption - * - * @deprecated - * @param string $str String to be encrypted - * @param string $key Key to use for encryption - * @return string - */ - public static function crypt(string $str, string $key): string - { - $s = self::initializeState($key); - $i = $j = $k = $z = 0; - $w = 1; - $res = ''; - $size = Str::strlen($str); - for ($y = 0; $y < $size; $y++) { - $i = ($i + $w) % 256; - $j = ($k + $s[($j + $s[$i]) % 256]) % 256; - $k = ($i + $k + $s[$j]) % 256; - $x = $s[$i]; - $s[$i] = $s[$j]; - $s[$j] = $x; - $z = $s[($j + $s[($i + $s[($z + $k) % 256]) % 256]) % 256]; - $res .= $str[$y] ^ \chr($z); - } - - return $res; - } -} diff --git a/tests/.vectors.json b/tests/.vectors.json new file mode 100644 index 00000000..b58b5162 --- /dev/null +++ b/tests/.vectors.json @@ -0,0 +1,162 @@ +{ + "key": "\/y+iXY2IqPZwPYix2JTIKN0lvdpjvfuf5iFSOYjR0FAP4Y+omMDKknTchFHn9\/8mSlwAQrW6GeKST+2\/iMcJdxml8wbXDBxW\/1Q4RKoilfFacsQn2n9MaQ1MZLk87tvKBHK\/Hqmp\/suXZVAjF8pBtSMc70bMvWwawAMqgIyG0iIKas2O64yoME5yOgxci6kfczQnXuz7OYVFQn84zzz4Ss4UnVw1A2uXZfwYjHQ1aNRZcBh5Rv8V456Q7EUfYEky3HOGR94wFfXHOKP+Mnb\/cAOHXZLdEgJd38iQnYiIVMstKZOHUVChWNWnyqfDNlWg6VItqU9PqkZinNhMKwvfwg==", + "algos": { + "md2": "zAzMzD3EAGiN+fTZ\/gamwUYDP4Jwq5UgN46KLkcfHaVP7tyTz\/S8eA==", + "md4": "3PQ7twv+H+USuJRANYP7MegDBGK1x8Skho5usR5lPOCm931RoYMrCA==", + "md5": "wiYg8ApEQsrPjn\/fv3WDKNURvUzSxMXLfMbOcUOJrye1Na6F20YNcg==", + "sha1": "q3lHEBQY3eu+tQ4TRVck7Pd345idRF0vtGVMojH3zwnnPqNgmu2gyr1qIek=", + "sha224": "4LfI636qpwWLoLyPO2Tj4g6ldUNlniPni5ftOM\/ff\/YMB466Bra2ffLLxfBdSTRQ\/hSlEw==", + "sha256": "PY7eptBcYs4PXsoKSOaiMpvdiAm4pycayS7thEEkbh1sEcs1EizBT58IgazWNUUyNdSPDOdo55s=", + "sha384": "8\/SHMhPiZzfWqBo8OJA8tfkuSkJx4uO9rdCRZHfn\/LrIRg4+60+lCmeViY+GvljRgXeg1DL+dfzF6eVtkJ+454459paytigC", + "sha512\/224": "WSisaikxj\/DA8mxwtaE3JtNAPgeUTb3aikUIu3IGBEAPebdYN42nDE7Ykm7hEd2kdtEXwQ==", + "sha512\/256": "EqeC+16BDB04cExsGtmFdvvTE0ZsDSzxJbIMqPAsyLWxJ6doqAe0Lk8QOFlwNroGnwraZONyEDI=", + "sha512": "t2weacUL7l6qh+626Sako+7Ry6yyxg0MkLdGGw7MfdGvoOd2BdNRYDYKFfwGVL4qxyySLucaRWDEkXY9NI\/t8YmyZ4TPfN\/pmDNm43f2H5P\/rjuIgZodeA==", + "sha3-224": "6qGccAK4Y\/CN1spQ4N5c07jVUbZy8rwQoG0oFjsJJimkHl\/CA9uDmHmfwUNH+6EJFtTzOg==", + "sha3-256": "K3Y79CGWRx3DSAImXZB+h8+e3LKpa1qqGJ61OMo2nodzkQmWrIx+KyysAwqIutSCaY7dUxBuNIc=", + "sha3-384": "oEwJpR9kiorkN5HEn\/6aY\/HRL8rXYb3D8XaANN2+wXvJMe9f3aW4M4+IwOqWL+XzS4UyP6TIOQLAYgsSuypaYLBoQaMCyag5", + "sha3-512": "8FeebDL98TEZKx\/mHHThuIeSmYOq0WNDUE37JHVXRoFh9N\/6y2xorq4OGsrWJnpGR9BnXG7zg4eeQtppK+n0AFTomdt2T3DEC8T01NMW26S2Owvy8Mt6cQ==", + "ripemd128": "AKz769kpNeeDJWr01nZtFg7ZcQKs4pD4GTJ1oKyt2+dCCxgdpPEr4Q==", + "ripemd160": "dDqyqDD1H0hYGNB29gLYTCjjr7g8ZIBn6iJ42oT\/ocBQBOUkFzp70rh5R\/M=", + "ripemd256": "T9d7WhGZqrQGcC3LF8eoq3VK\/Zrrb7tsyMLOBSZ\/gitsRqeVK8Jh3JSCBpSp0OE7R8sU9sK1Fq0=", + "ripemd320": "G5yG5gcXb8gqqKPzCGqeA\/yq3GBmOgJOXsWLMG27lYexHDt4n5x9Ee3sav52hJblZAlJd9T6XaQavSCxdWH1vQ==", + "whirlpool": "OxAuVAepU\/yfH23VbSH1ZSv+JTi+k3XpdO6xaTMrK90oc1ZVP9wgc\/ajuUALVLUSXyBMugLawRyRLBb+k2ddlgPTz4b0vNHefXFij9sMPDjbszZtWqAFdw==", + "tiger128,3": "QtpKVSZIpHf47QD5fehvcoam4s9ujt7GrGyj1uKgLASqs8rlPaKLpg==", + "tiger160,3": "bRZxL47gwYUpU60Wt2fPp6F+1JxriucRWLre8dvrDaJubHJKXGTy2Ar4MXU=", + "tiger192,3": "wrQbvHCpZY+BdyS9Vtf\/iI07az1jRIiW9m9ZOf\/C6zCjPfRcARlR3QyjD++hzo94", + "tiger128,4": "H8kwugcj1rywjOzsrnHh7R0TDFXGb7lMzp9s9tQ4JHW5BQQNlg1DWw==", + "tiger160,4": "bJRSyP3hGFEbthtwNspcy3lriPp5py0yJl5Hl1lnCN1CaJ3u12wenlXPiyY=", + "tiger192,4": "lM2sVFaGQ6mKoJCYG3U\/l6Q50rTWAEA+uFDJYABOx7gNXEw7oQ2YkLAD8btPLRAw", + "snefru": "p2eJspgw+q8hCV+65I0fHQ91pLG7RVF3S6e30b48fp8vBpjrkiQ76yRR\/eYSJXyx57tDm10hEk0=", + "snefru256": "i6Mj5tGijqNsezkYl6gea9DZZkD6pogba61MBK5p6SWGb\/ybogQf6J\/kcMjlxfwv6yArDnTL2nk=", + "gost": "CCbFZq+KmavY3tW8sdyk0Q4LWiqsLG0WBbZyL7Yi\/yL0EFYOCI+Tkv7VE+44nYW3\/WcX99rycDg=", + "gost-crypto": "41RBUyKTSI4oeEzjZVjxsVgThLQhjV1k7vhSL0OmnvrrKszOKqa+51FE2blDXgsdtuZUYvLT1fI=", + "haval128,3": "uIajn36C1YnKzTf\/wzuWeSZ0iIKTzFCyP10ABYTn77SCOeKo6NzUzw==", + "haval160,3": "HcLjTBPlWnf7dUZOl3hjCIqVCHyfnfmy14CVTSBub7KaIEzVNW4m2yyIc30=", + "haval192,3": "ZAuQafeIUj9t2GJ6r8XWvdJQAMwiCF4CsHlKnUb4+Mf1xrQzPkh7wQrOWgjzCXZp", + "haval224,3": "nwegg8oxWYBZ5ccmBSsYFy\/MfvlHSuaoy0VHvPnhXmmoT19bL24wJ2gU\/MWd5QImzDpicw==", + "haval256,3": "3UHWnWtO5nmS1wnXgSIiru4zGYwMZMs\/g2edeqm1BDGoXQwmTrul5SSN64cfOtqaItgc52jHvQI=", + "haval128,4": "eFVXn\/0mX5MRKQNr5CXX08gpC3xkbalLyPdgMwXOOUHJGwdfiXKxzg==", + "haval160,4": "7MS2RikocaIoOze987X2C3DmFog1jOgsG5rFak1jM6EATs+EUe1iDBxIXq0=", + "haval192,4": "4Ei79UtPkO6rWxkXOzowClUIUAfr0mjhGcTbucul9zBYCeuGhHBHz0HhoiGV9h1j", + "haval224,4": "6wpOQu6+7As2pbiu9CAJoGSl8RvCbT+kdJmRBs10uHvFMUiPdCKet\/j\/hKEV8LhDDKbesw==", + "haval256,4": "XCA8GWZ7zVcU3vwQ9tzFMnm8lZmIe8NLtXPsmTPKJIHUvbxXS7YUc3NyHatmdYb8AHghmOJYagk=", + "haval128,5": "fNFeMpEI3WjzFukxpv15Ri5G1NbBXIKNxRwTpGyWTozfD4C0wKajEg==", + "haval160,5": "8DTZWyP2IjLV1RvOrZa5uojEJtYThsayRtftAwiE3dfWn9oC+\/\/3DnCwE\/A=", + "haval192,5": "MxZrcybnlTHdj13skoCuD3gg4\/qxLbvLz6XfjdChQXL+8hFuyc2bwUi6StPwKKhC", + "haval224,5": "jZ+GLeZ4gbVEDhJD2YYB8tiYrjRxizbfU26ReiOYAJhOFyBGU4uW4FeS+QT32\/tkU3bdYw==", + "haval256,5": "AO9L9x5HSXh\/3vMZPhDGbAqEOoRXtevlVbISf\/9tZ7tIapUHRgygaNDMkTjWsB+SwUA1x9N7Vpc=" + }, + "ciphers": { + "aes-128-cbc": "uyNzrlXx8j\/2EnQlz+m039oVl0pX4t7KwOu3rZbEKkCk5GMRgl4HdJXhsvk\/MlI9yg8DBFxkjr7rErkNFRAZaw==", + "aes-128-cbc-hmac-sha1": "Y83u8fKvxvlQt8meYuly0RJqdE6\/Pu+0bw5sINl2NsyvPNM+LUBe2+2INQL0iIM9inBnpzla5iovd54zc0HDXA==", + "aes-128-cbc-hmac-sha256": "dAghUg7\/t8EOcc19\/SIZUIMW4nJhDdiCKXxo6krptqeprqgjuDZg24rQX6lSLjqDcReq31nIdDIvrRGJk1dWQA==", + "aes-128-ccm": "OWt2uKw+XdNE6Y0QCRCGd7hSmlVbYoQWLMtleNs+zhJSr7H5teZ8uPlpdfOP4rs2rJFKMAhEG44=", + "aes-128-cfb": "aFve6DDQotMTchEbBCyeUcDeFzxHCgMSF8zcaRpF4sB6ozxTx1p48pwAw8sTkGgnQWSrMI43dPE=", + "aes-128-cfb1": "7mgP8xaqN5+V9flAhMxku6E8\/cblY+33tIpX7ZmplpTc6m7gRFQHe9zO6Bigx2jPIX2F6WKRVTI=", + "aes-128-cfb8": "xAWUOSgu1KS+FPFLgjzloKNlAKrSuKXS6cCajNYLZgpkP4nPcw9x0bzQkRhIRzFiDt24ledWXDE=", + "aes-128-ctr": "Vm9IwqtJ6AEUA0vWpOXOUw3mpjKb4+pjUDAfGL2NyV0wZJH4oqEiz6q27e\/XOgY5wGR0koGAWNw=", + "aes-128-ecb": "YnkuV0pcFUwN9kwP4YCpwqavizHjwcH4VO2NCFPNpBhknED8cCd\/ZGvIP6BpQSVd", + "aes-128-gcm": "FJWtKUOb6ULRcobXA2RQm1J6cHJNcmIRjBHceaom7xauNQVG+ITQ28ikkkMpYtkcMDDkmLMjFEY=", + "aes-128-ofb": "igutJonNNeNxnOgXXMj5lNk5X+28+G+ttj2rMMDvPuFLviiuvCcTMjYbZXRvnBchP4QCgX8qpOY=", + "aes-192-cbc": "pVT\/eRMC6kcNpTVFUC8c1BUhTWMgAX6TwZvOCPX8hEi9+55s3gnd0U7QtoBg+eDm9nsbimDJ\/l5yf0kOa8xPJA==", + "aes-192-ccm": "yYevGbokPyT24UmKjRnivjwNd+tx3f8HvYmCIVgsZBPVXi\/TSinpoklrUHDWM4+uzEAQOAWIoSw=", + "aes-192-cfb": "yrGrTl5HzelvMWEy523obHp68oBoddMROdReHEiykLqGM+\/vh3kDKYtAHGfj47VmbgDUGV4P2Cw=", + "aes-192-cfb1": "QiFR9qrqhHilvHcygR2mNFxRvXM+kfr2pVwfHhykPBVId2FLQZNtkVnCPsgNf7SWBJi6NJPdttA=", + "aes-192-cfb8": "8ZsmicsDu40fKOD2sJbXX7sYSAVZle1wSyuVJpNGS2MjAME1fuXBarVpLbv9X+ibEkHblW6MFT0=", + "aes-192-ctr": "sfDJwMW5cGKGPVq405BJt\/STDxJp8cLnMhYjDLHouIujRaY9HIjydVnsK0uB1n4tKGLvP+Lvwoo=", + "aes-192-ecb": "BdPwMFmyQhO\/gLuwhNUNLfg7kQrsRAPqeAwqJm\/EyaHIO5DdtUjpOnWG8uKk3s6t", + "aes-192-gcm": "Q36caxHj4ziVgwgA3gvfx357vBTdUsMhmiyJHXMcvVdkungLgCJ7ibMA55AsM1eo2lPWWCCxmeA=", + "aes-192-ofb": "sjsuK6rV2jettR58G\/RiDA5GlmZt8Irr07saKnQYV0kfa4YWzaCG+Wu7PGJWD7of0Zmxt+Sh9Uc=", + "aes-256-cbc": "c66pnjrn3Rn2kZTXqpViyGZlvwLBfN5IjwUSy7raiYpu5pCNAZOmKY+BR3ju5A7gzu1YCMaJxlSIVsEfqrBpiQ==", + "aes-256-cbc-hmac-sha1": "3xfDkaZtO\/xliXgjCcofCZ+D5eWEUEZLZLMiVaD8RCeL7RPYwfcLfrC1vcp\/PULJvx6r7pFQB9WBO08Pw\/LiCA==", + "aes-256-cbc-hmac-sha256": "WdumWoQ4kvHzojEyqrHB6gD0GFjUTS4\/63QYRnJfyMjJn9bUTZkWrEJiNz1L5WQEaN\/L42SuoKdSRQjR9IZx8w==", + "aes-256-ccm": "VZN3qaGSGGDmJMPGgfx0KYLY3EH\/EqwkHz00HRli6PAH1UmV8f7z+M26nYJ\/8CzMrmuit370mQk=", + "aes-256-cfb": "q5gTROtq1plIAxCL3WLdpSgKkGfB7YxnBtGhjbu6oF6TnQ1X1Vfq1j\/2MUBobUbHw+tafnK+JkY=", + "aes-256-cfb1": "trlkYEq3JmGT2RbBGSQrKWSKvoZmdVUUBjYdMRPeRo\/9p4SjB+APWfaqpD6DlApBk9Ss2jbDruQ=", + "aes-256-cfb8": "kERfF714keUTI92C2NF3eETQS3GBOr3dGqj30afIN24j15DHedks14nE36arpJND9mDMXx2UnY8=", + "aes-256-ctr": "KhsKkt919FyULrOJBDAEOapebIsaSoC2mr9T738rCMYtWenaoVb2Hcjh54pdO6h1ZAwX\/NzUaqw=", + "aes-256-ecb": "rrVklJiSVwD2py0pzMVDdxbd7PLp8Ruxrx0aFPmSYKPgQMruBfC\/pO4FgKW69Hm7", + "aes-256-gcm": "nC6rqijgM48B+ORuVfMYs1\/ZVryfhG7GXuNkgoRgJbNhekk6qb0idE7tMphSsRUuXvVEOy6eMHE=", + "aes-256-ofb": "6W6Q9IrrsneAp\/QGVIyVZuUhM41iRbait339jtLceXK+Yrbgj2PtnYdan6M2ydXwTGph3ENZoXo=", + "bf-cbc": "5qFi3ahaK6\/k0+69GYKFAags1upwgx4Jh7A6iB8r3jK21eBcOTYZ4e7PYaRVsnJBxveiihyN7W0=", + "bf-cfb": "pxGBS4c\/CeWqG7WyncbASh6uU0qKFM0o9pMeLX+v1\/clgttMvHGLU0VQujgR+J4L", + "bf-ecb": "Blr8ZObyhTRa65YaeBXANt2pvMI16uzodd9gzZmS4GrUKGKpMaPXM7R9l\/Ml70\/x", + "bf-ofb": "L5vKBJN1bb2DxOgd8PaDpjw0IVSjU+\/yvlvu1KY7T0w1mnyLB1oolczxlAl3Hk1N", + "camellia-128-cbc": "ahUXkfwwLB+xMsfMZSampVm7qJPZ4TZMjRGIXVK9rBd0SU92FezRrnE+d20O501\/tcyumjlcgwbmYD6OYV+0JA==", + "camellia-128-cfb": "dRQSGm1VfTCGup+LuAeTJtZL6CUUuANZh1Xzp3yfP+jUkXjPgG6eKbCG2VQiIvBOPeLdGdp6oEw=", + "camellia-128-cfb1": "YlftlBNdBhTmF7ULORic2JCaooVv\/\/7\/guqi5gJwXmLUZEFqvkM8YeZvYAMvzHdA9bHeFRQyPx4=", + "camellia-128-cfb8": "m1clJsNoRnvixUnLZKeKqPJ6zRwqGoKg7n5+vHXXA3mBIhZrs4mRN\/mtPKYk\/X48so7eZb+WWno=", + "camellia-128-ctr": "jtk987fPHKGr3qPSN6NkpEBoNhcXhIYh1mlxi6CO4gkBl5GpSvJSFCZalzN3WxgnYUks\/NUHayQ=", + "camellia-128-ecb": "Wg3xOwRH5zUGXTm1bfGao7ZVG0koEJDjIa6bHI+yimPDV70esWWGCmBmYq5zTowG", + "camellia-128-ofb": "ZOAympDCggnOibJ2l6ybsM5fdFLj6hDPxWwj7kIAgBYaoGUgreLwTkucpcPS0TARYPMHor+xx0w=", + "camellia-192-cbc": "4taJKP7RYAYR0obIffKjTHRH7MraDHvpFyOhSCsYijU4\/D0UGtJcT0k29kCVEYXf9V\/podoDIAUds92wq4\/\/xA==", + "camellia-192-cfb": "+MmfmBHP5+gsc+jhmYCK1kq8hIrm7ZUkD9183r0bL3SEmrWxYTpKdIL\/WjwSneOJDsHDxRb+HOo=", + "camellia-192-cfb1": "fkG1FScv1IN67wEjYA8XbB5RKEu2af7OdLCPfp882v\/3CIKlqm7TxiFO67EY1QLUCsniUTleeyI=", + "camellia-192-cfb8": "RHY9VAfyPo223ISk4C0tsON5LaNjiCxrdzUBFdTvCFRm6XFAUb9Z6Cs4U0EmF21+cLpJ+3vEMYU=", + "camellia-192-ctr": "qRoYwcMFu7bON67SYQiLgthFVCohLv8y\/FmOlzGrbhgA7qs5nUbXaWwWili9nPfbmtrtb2WZV4c=", + "camellia-192-ecb": "xxJkC\/+YOS+HYUwEOWB28kMdtU6XH47j6HxmYHGkZGC0yG6X\/wLmE6fMsFYXDwEz", + "camellia-192-ofb": "sE4Yc6WFYebHvw8cZ3goOIf1XHLn\/TACnw2PXXH8cRGZDwt3RIJTZuChMg\/ecg7KcuPss2rn7nc=", + "camellia-256-cbc": "F5kLu2XAJdTwwm59lO+n1bfmmJc9IxKElWhb4YLHimU4DLQ7jDKadsGg+faswcGyCCEX6T90MQCKDPcC8777gA==", + "camellia-256-cfb": "VStw6DKmlZQmR+qfxP3HWWYjoeNqpqZBbKymspknP6u+t1NwnTGia\/tbv4a+uF9FNKG1xpSQey0=", + "camellia-256-cfb1": "F3syJL5cHGSpHBzgCAxMo\/pq3clqI7qgNkswe1T0lJqpH9NQozmu3zAThHFFDRmkbubOoAj9QZg=", + "camellia-256-cfb8": "UUPRLaRMNnE+XKz6WSdmz4+Tx0NwHdIWRxZEdgp+Yhammq\/HtnbvT1Ho5NANoEB4NUC4PqnCJsI=", + "camellia-256-ctr": "E7\/QvvRRF4LeLC0\/NT2+qj6TTJ8LCaB\/sE1niBRNyaxngR7\/06qC7d5HYtoUPyP7UYvf5JaGAdA=", + "camellia-256-ecb": "uilkB7ODk25tmosdC2aSTS2svB1M19d0RaZBSYXF\/nIaPgambPKJmZTciGJxo0I1", + "camellia-256-ofb": "wjQirgfcrdrNUiPLHAUJMFbAuIbgzxlAGWKIKDtH+9cVTI3rtdmyz73WCw9ggxggAnxwnxoCPoM=", + "cast5-cbc": "WK\/UvjCskf+KfsqPCC4tKItrGkwbiwAifDvW5pY+77567JhcimXT8sIO97sn\/qgOiMsi+IKWgUQ=", + "cast5-cfb": "VpDCxi+Z8+llO0Tcp9PsFhAKXC1qBhyX0H9q9lff0YO1GSvPoemXjYNfkZH0s7+c", + "cast5-ecb": "SG2pfFUPTiG2xgS2Ra9YbwF5TsdwWsJnNPiJi9l8G7RKZW00JhU5cvRO2KRWnMIY", + "cast5-ofb": "LD7VLoVRqwnS7Uik0SVVInklKy8mSH5Dhsi4wpRx65P8pSQXmuWyRVrwzNAQ6I\/7", + "chacha20": "vyxibo0qwJ07KoI8QK+ED4lI\/0F3UChWCLdh5UqhJljoVVreC0Oa17pGhIRQas\/pIgGlpWind\/g=", + "chacha20-poly1305": "Ed2V6hBpVbPFwih120tns3JiBHhOCDehrkIel4spColRz2z9QwhrUS1NTR+95WKpbUodbQ==", + "des-cbc": "WzRwe5I7VN5kCJyeNL7DsIv4uO39jZWNL796yF+1f1iD8tIsGlW9JdhU5Cyu\/U6B3uVxZ4abcck=", + "des-cfb": "JXIwM\/KC0MjINbIxW48f1ypRYgCwg2nDb46hgVTweAKd7gZIQwwSl\/3HbRqPZieK", + "des-cfb1": "Oq0OzFWx95N6MhMBxbJw1TPFzRifMxohjpOscLIWBPK8HMzGMsHTlvva6E9NRIUL", + "des-cfb8": "3\/vp64I41U9rwvpxGgCb2GsQSDQVdKE2teyOxiddDfOk1sSJPz1cQInUQdwYlVZj", + "des-ecb": "R7JEfil\/cb+wfczdjRtc28208EbVqjf9na33N7NUB0hCpdoOo0W11OqBpaIXSry1", + "des-ede": "Cnzowqk5nF3+DKl2+fHuxia1VDUqZCFP\/PfEiiTv+SSjWAJ6R2lstcMRH3iXXOjm", + "des-ede-cbc": "y8J2rUMpi9zm1Kk6mrhj5gDrLEVVlGW2\/ckwCObgLph\/+GKDGnjAm7Dy\/OPmQD2o2gLQxLZxjOQ=", + "des-ede-cfb": "xc58lRz9254dIRjVwlf1nOB2PyssdIwn91kJgOdZTExSEnCgvtAi1Pp1O4daDvCq", + "des-ede-ofb": "Wns4KS\/268JUGWgLLXQILMDY2dmpqOXOIwxv41\/JBghHojn69I5KMHPYdW7MjJ8F", + "des-ede3": "RQzmL8Qh5dO1PTFeSGQDeHOoU8tKzoxl7W4jpXJMf9SsVTd\/dSZAcZCTT2a5dDUF", + "des-ede3-cbc": "1R+99ge6qaGa\/DCwO62PUXY6Ruq9CDCxJC5qKotayz6PqsTb9rxXneIfBxX1K64QiWacMWv3kZs=", + "des-ede3-cfb": "dQGtGQ3z1HYvnAmBCMTPw3291F91J\/5xpB1M+7Vy6J+XTVKEvaCU1e5rZVIxlBSz", + "des-ede3-cfb1": "8flMQE6MPUR4sjshfiBGUvnXIrKFBr02BwFNVQ51c8\/\/R4K220QZ4YiHjHUWJLes", + "des-ede3-cfb8": "inJ+PnOnAJ+1dJakjx9bRiHKlq4uGfpBtD9X7swYlNHuT7WXOy+DESzPwmoqV9\/4", + "des-ede3-ofb": "KeJ+KDM+PaVNiEjCwhlJ3Wc2DfHfQrpd1YHZiGwVPYMNwprv4NjuWEQ+SlTkpylM", + "des-ofb": "6paXyc8xLM9EztgW2MA79LmJTIl3eZZQsGei9B33is1XNt\/amdSZy\/kA1apa2hrF", + "desx-cbc": "IffIThrbR59TpKRMpXoqaMc2\/rRv\/L9aKzfgsPNd7bHUReapsYbChSvY3ITI6RxoOheUxSn8NRU=", + "rc2-40-cbc": "8rU+rkF2\/\/2Z8jwIzL7+TpeBePuKObLogGLq5WnGeJtWJw+NJZKDa7WY13Ex9Y9Vcjf8TAj\/ols=", + "rc2-64-cbc": "0ruthlP1dQCbCp0U9YYrBsEwCc1EG9OfE5ahppH545WfqkKtvjiVevbKro49IdKPQhdTpazW2QY=", + "rc2-cbc": "T0qZVcfl0d4bbOKEN5vGAYIEAsyX2LSxaqLM+TsX\/YwYuHD7FNRdANxpiGHhVvuwBGuW1K2sj3k=", + "rc2-cfb": "XeuR9FKz03i0YyByBt+jWaJNPle5\/Ml1sDyyMEiVkUpu1x\/m8qBa8w9\/HhK5HY1c", + "rc2-ecb": "4F5OrY3Wl5b4qwYOEZ5uR7Jj0JZ6UZ2zDWSgV15Tf3qn0jI0UPBK3J3W3VzhvbZA", + "rc2-ofb": "+t2awNcgrokNFH4SQ+xLRY2\/1XWBmXDSqmU3ERdsqSqJTvmSosMDrZ+IjzqwAqq4", + "rc4": "g55sIFdxNBqfPcWXLzMsVrJPLIhzgX1acp2wTdafw9d\/d5RPiIwPng==", + "rc4-40": "JW+VuGE3FbWCMlbjGKY2eJTRnu9vWr5I2\/ufqf+nxTT7UFoDkGOoOg==", + "rc4-hmac-md5": "tO4YqX\/KXrBJzvZx02R8ew4fy9JiX0btdoq\/f+9U2mYiuOHFjTzhjQ==", + "seed-cbc": "7pwJh2Q1t+jpZT0nGfuSAzcq+PRWL4NXN92Q0y+ysk9WsY\/c981oisvYrpuwo4GTD2n+y3WJ9grZ5zcfllQs\/A==", + "seed-cfb": "y3XKauHNsOxjIGnQZKx2cBa5Bx2J0ytCAg2TSQZ1EczrOZ\/IGH77Yyj+drejPgBwqlx1TZrOEXs=", + "seed-ecb": "9A\/gGqk9H\/m09241jSfRQf7iOk\/lC15DnQfyZwQS1z42pFK49TBDDdBDHUAN\/mBc", + "seed-ofb": "HmV5L6whsDmiIndGJebAwsdSfb2\/Dl8d75dWtXcrsCXfXP\/WwKxFPagghGVXEHJU7GDrXBrvvuw=" + }, + "aes256": { + "\\Dcrypt\\Aes256Gcm": "UhXsDGo2zfyv5fa\/7L3fjLPOL9\/wD+fx89YUYBBbLQKwle7htii57aqFjLPxwoyfiuZoliWt6DI=", + "\\Dcrypt\\Aes256Ctr": "FZU8SXUnO62KpoFUHT48pxxYbnJAsWOm+2PuDBcGb8N+WWfvmfvJ5uESSJqXAmcjFuC7A\/+j508=", + "\\Dcrypt\\Aes256Ofb": "gh9WviN\/5TbIcvthhOcIuQoxBxX8tuLhzjy9LTdpq6zqvxqykRiYQdwaWTnX6KlnskVApl5c+3I=", + "\\Dcrypt\\Aes256Cbc": "jkA5OW1j1YcPleg659lkcl8xeEwU\/gzFUMqwVRo0HfYH675s3OKgjAHyhuLUI5DllGSduOD+0s0LucjnTrNJgw==", + "\\Dcrypt\\Aes256Ecb": "rrVklJiSVwD2py0pzMVDdxbd7PLp8Ruxrx0aFPmSYKPgQMruBfC\/pO4FgKW69Hm7" + }, + "otp": { + "10": "liPstDyaGOWc4Q==", + "40": "NG+jpsHT2TsEH57GpOe+MsgWrXv62fjiOiJyC2apWzkhrVlhDBWBoQ==", + "90": "VZO\/lLECvU9dEx\/260ZWYkg0Qhy+7A4wUNlooqnX750GUZkSTwlKnvt7C8VOGin\/qO5ZP2NvGS6G2ipHt0d+GEjwZzNYf7NgitkHY8cHKk7cOAaNBQkVkcyP", + "160": "ta4sRhnq90YygVkZyaPAEm2lsORzFk\/r9EpgDLHlZHaa+JPYPn1pVcnR3H7JeVx9g4GHprkMicowVVNUJevxTk3szh+9O5fOBwNL62G2wI4Lr6EpnxIwFeV1UfODgtre1hMnFsk61PkxKX4JipXr4u\/k0JGC\/0L9ZGVCgStY9O65A6N806oqmoBTgs5C5BgG5gJyYIvR8Uiw+8jpvtXOJQ==", + "250": "4pn0krAHFJ015XEcc2tUeoNVmG73IPeytLKAN9GeCkD147rILmmDiu4gYeWAIX5h1FcJaUYT1iOPSV0DROqr69JYPP3\/rI8sZuw\/rhQ1v8qTzKqq25O\/EGWL8vsf55HNAy+VLry3EEz\/EBrcCzvBDD0PIP8FFzShcJ\/Gc305c+e+c+4iy9HkNIkw+5tWev0P8eNvVihjGl5VweeAWSkQtrvVUgDnerc0rJ2QZro7TZr4e+eVtSF+dVBeKZvRrqvEnaGLMVLJkRSf6\/tL7dseu+Nj1vpFTb98GU\/mdPPvsO+BK3CBk0FdkU5+BgG12A4hmNpjdjPIFnwh9w==", + "360": "DvSaV4w3Ie7yAu6QBQDALr2DuOCvmm4sTg5xz\/SSw1451D1PoGDdUN95m55j\/bFywHV1Io05qIF2rLz8zc8bz7N\/D5q6MGTcuqSsynP08qwyMHDGvbffXD9e+L54u3lE7yI+8NSn++tkQgPEyzQPrq3BcDBM+sL6r4xp7FLfbt8sOjuIs0H5v\/DMxTOrkRP5C1ZjOGC0GbQrWUevoUn1UKfopYWdU5t2MmxRCR3pjqyrQuRl7x+kamkPIwQALqJxwvf0aPNZZpbGX8A7KvdvomW2pmdhjWDzbWfJuGnJon5B3\/OAszua3yktJxk9tGPDqh5uiieMXjYPvF16OKg+jBGAiv6NdYibzxK78v5ixN7vRoN0lwD2JI28bhD6D\/x+lSz6eyyOR\/Bs3e5\/owN+KwFRsGsyxQIJgbdnpzoYFDo4ONKMrBTqiJQ0FcwJrq4SIwtzzUPWAj2TI3pF3S\/oOiS0yADQjfUh", + "490": "r9DpZavLZa1+9CLhV+kEw+v2N836nWUDx3LfY3YPoVdQfiDDFwu4j\/EtlIUGN3h0JEN26ZsLLVMEsHtSKnlreva1kdCYW62CzntEtis45WC\/lkojKVBRwFNTs0zZUysBwTsWcrXuJhaDCvzuWBMKKbmujxKVhzo7+W45chW93C3arXM9dPbFAi0KpZ\/An04a5441ajNwcklfMVMZIM5dsv1Jd1Zf4p\/jdq5z97v6guFHkDjHsycOho+hdOdX5ngQ44+MMgZ9pH9Ird7crmaeYUSh83Qa\/4KYsslOjwajXSO84Y1kan71dN9XLSm3te5A0qU0bGOlaqINkda6EaNIGV+yKD1vTrfb4mr9qROD0I3CO4P7PoFMhnrMkxsWSwWpjd0mqRfD4gwEg2U4Xcp\/+CW84d+yAxZjl1K2Z5FJl3QsDJqPROfkiHJtymckmTKakm\/R4L3zQVcbp+agqsntCZKp8ADxu9A19NJUEd5HyD\/MAs\/qNI2RRvzLXPSFt7\/IVhINyxigzPBjwrwCEdBhgd70LjrVDRUjx\/u4EEpRTYuVcovOLSTZKUpljnQ2DUI22BV9YetxxmVPc\/t\/Ra11Mu+ohTDb1sZj423NR8iXGC1zrs2e9tU+mLYmOxyFU4ty6YUbsIaBzduaXw==", + "640": "\/3nJBBYBCdYGp4tdB2iYZ0BK6HWLyxJxyq8I9hPj5Hx+3Rv0ptDVuJZrK67ekidEl6ljIaxANWWiaez\/2vmvYE2qEh87zv7\/abfy67LN9htxxfBQaEtbjAnfRi8XWpFX2YmF0nJhiiXoQEA4UgKr4xYOKYE7J5aJkzs27bx20ZnCVMUZ8TtiktVLU9a5ae2B+++t+z8hCExmP7kYpHdJW9Zv8ZbB+WZNdgjIGtKnqNORAe8xb6QvOGQUAmUFLsERssU7NgQUzs18z8hH8cHgYAcC\/D5znWrGbYqjg19wsk252uHoj8xy7u0kWlRxbzMECyU+IW\/V5uyBenFhjr4BV3HCBMIrXCkHrmQn+1ql9\/ZjUdowRn1E3yKmVtxEX1Mi3Hojh7BUGujj8tzGwoYwGc5tAYtScDW76Xe+cw97LW+j1cJIB2vi7JukQfisjihtTWvJ8DNb3+Dhyt3P3G9iCaVs80451jXiFFhqEYq9Rx3GYjiB9n1y9aLGkQjYDWmT1zH3lH6AeOlIdRV31yd1pRI2guanl4AYlXLbJPgyCQ\/33HlcA6tl+Qkj5HKVj2ENlXY89LRZGio1L9Oh1tK9IuzT5vl3kALKBosUfyxj\/gxesm+PUD\/HJiDy\/lesjBSKI9w\/+Emf4tS4WsgnjLt3JvrxpFruIUp3eeFQevlPTRtIedkOTuohnE8X5A7k4whlDWkSVZyiIEwRIGuSzBgtMWilFHAOcHi0FucjwPF0\/Omdjhoeinj+cqpz4re6dTFfCDdYeUb+pTc4J2liUA66tH9Oiuuyhx1+4IEbVAusILpfjIBGCNEVMjS0fnSRaj67kepXjcHdzCc7NZNRaHKTfg==", + "810": "KfOJk8buujuQvpsdI+UMjl3kY61EwL1PiE3rWIomWOd8\/UUP\/QeVtwd1xONPhtXDDDZRg1yoR\/EE1TgxRwGTn\/net6Ttl+G4wknFa7HdFEjFXQgs5KvDNrnVCe3cDu5RvOjxPDH2rJ7U08GHbNfrRLpFH1B89GlGpmx9o2CaNxX\/IZvJF+V5TntnSg9QNyb8kZ2sqSCPopu5EuN7ErqPyDvVr4aL\/wXoFCfPiTOOGpjALNeqwpN9631RzW3DPxskGxam\/fm8hk3JP5B+nk8l2goAsIs0axFCdv7TwBB+Tj4C7pNmTjugyKhAzH7vfZNRnmeTFKS4RCo126chiuZRdt8V0IcJrqkSotalhFkShjB9oSBznxYUeibt+aGqVklTOFVnCx5EPuWU3J6AMHx9UzsmrJXcY7SbWMLFP+6kThknrcdQ80P\/KGlQbFr5oS2YmWLH\/KwKYHvDiY95DMAxVZ31sQFCdjOaTa9H9idh4YWVxZHetmXnEzLIaiOVAUH5XyFKgt261bwQJxilFljQT\/zOtfGBBZT4uSsWR92NLQtkO9lfq1r7n5b4BLzzZRwSKhRxRHE5IuwsJLxKOgEJbSwBYWUx4Wm3XhFtNmqv+QzRpL6tVVwVWlywoGGdxxUDKPeUDInz\/NRz5dmshigXLYoN5tddUF5+AH9eiLS5r589yPke6nd39v+pxpStSbS+G+M9\/4NEa6yYPcV0Xeuv13LWutOa5ILk2LqKRVCfIiZh3UxAlJRTBoAOva8ECNVK7\/5nYb6Uk\/bnKpR7pxIMjmcFBzAiReznd6mZgQx4aWQ6VDykIgbHgEYxNZsMYtfZ6sBZoEWibu5ezoWIpXQewd\/pD6Gzn98vJwi4hV9Iqle+FJtm\/LKTOI07y\/jzggp3+ozQu2mMgiRVFsKJQO+wGZDUAl7AN3LFnrVBFpYoBb1TSmkEBCENg9W1JLw+N6J0b2P1iBF\/9b2KQpA1KkrpLJAQ9Rk25DD\/X1xZXAS0NQJxb466mfCRPljSUQ05jX26DULvpsd\/KKu6d6Jm0ya6szNI0P6024TWa2+0LANbuKhqz8gJKvZml+Qs", + "1000": "SZiJdbPaNx3XQboRVgQElZDn3ZCFPauuEZfNdPT6Ed++WDrfM\/atFM8vX1qFStyxLmMF2SQoUMCVjlx4AhNb6RaUtmma1IprYbIlFWOIDlablMg+WN4l\/QOMy2\/sxSZd9wfOXd3SWKi7rxmPBigbAYyG0fN8EH9AJ0gM6XCh0CSBLXNevCs0F0tENsYR2ql4iqA99a+q82wbhSR2nLkvY8mKynyJ8lR3t3+jYiTB9lD0zbiwjlYoXDm385GdnQ93B4yhTfsyKJYUOokYD\/SpZLIritAvgsdhmx\/EA3pVFjSiCnN\/fISJJm0j0yml5k\/U+bUyt4H0nSS3MNXs7+G4ylGjmQQ7W6YRT8ohAv\/NpTvB1HylcWu0yfCinor6m7OVOg7wSINBRMLxBMYYIhVbkqJyRYq6LWuNDLiGCG\/Ktb2mnqdviECSA3IgGtmoMu+AJnrf0u\/ve8VGtoMge4fTUDxTtzlLzk0bwUGFiD83qhhQdEX4Mr0e2vqp0M8rIGq7hC8F6B7DP8V2qWirkVE1fYo1tQYIJDGTnj82mhWViYZppuYlQdzahrbzUBlCsWGEd2Okg9TQPI20ezBrJkVjS6D2QqwYYrDjyVe6eN12HFHpjSIc0hV+Ng0QTFNPQIzqIyg2HWIpH2qAbSJPzzJxmIRf3oow5pFSburJlJ5claINfYg+V4tdtHQVD\/fNoKpsnvx9uEDLjJs7DW12HJCwtQWMklkKZZK4iMw4NjK4XB\/AWcb4fpopN9qspiXC+5\/ZW9H5xEE3i++X6i4Oz4W2s559xhIRyEAEb6B8O0oCZ8P\/y1bSHnMofx7ymEruTZ7WHelBimwHoA8pfzpQDdMAH73d6xb4qvSCrs4kG0yI7e05JNnj3Yrl1M6UpG4sl1mt6LTpWjtlOptd49rDCZ4n33lGD8eCrYEKXMrJc8KPonG9pj\/Kp2aGJi3JjnaYzR8iTm0rDJOHu\/RjUVPE6yEZ\/3QCFtM9BjJNT5DauyyLfKnGx+itS\/7yuu\/hCEWRzBUYw8NgAFVy6e0r8gEKPTJBpObudf6UiynYcRD7SCruO43mKOTjukPNKsQsaA9b9WWlT3GRJ+cU2v8Qb1bHRWVKf4X9D4CQBxaZ7G\/+1wLKzPdHWXX9lWkNCg4Qk6JVoG44jjbmiJMnk7nj1aQqd9lMaN7KMDwtE2YL5WGhZIpg8s9x7AB0mfbK4LT1dlzo\/k7\/2NTe8Vtez3IwlUzNyF8+336BH4oxUYzwMZ4s04h5cUCDdEQYrtKkwct3DsrVBb9e5TbqgKim6iZiMeuIUV5JAMbNbe43Wg+2CRDICbGuHiNdcY1hqD6GJg==" + } +} \ No newline at end of file diff --git a/tests/Aes256CbcTest.php b/tests/Aes256CbcTest.php index c05576be..1ce40d12 100644 --- a/tests/Aes256CbcTest.php +++ b/tests/Aes256CbcTest.php @@ -1,5 +1,7 @@ assertEquals('a secret', $decrypted); - } - public function testEngineInKeyMode() { - $key = \Dcrypt\OpensslKeyGenerator::newKey(); + $key = \Dcrypt\OpensslKey::newKey(); $encrypted = static::$class::encrypt('a secret', $key); $decrypted = static::$class::decrypt($encrypted, $key); @@ -24,8 +16,8 @@ public function testEngineInKeyMode() public function testEngineWithSomeRandomnessWhileInKeyMode() { - $input = \random_bytes(256); - $key = \Dcrypt\OpensslKeyGenerator::newKey(); + $input = random_bytes(256); + $key = \Dcrypt\OpensslKey::newKey(); $encrypted = static::$class::encrypt($input, $key); $decrypted = static::$class::decrypt($encrypted, $key); @@ -35,7 +27,7 @@ public function testEngineWithSomeRandomnessWhileInKeyMode() public function testCorruptDataUsingKeyMode() { - $key = \Dcrypt\OpensslKeyGenerator::newKey(); + $key = \Dcrypt\OpensslKey::newKey(); $encrypted = static::$class::encrypt('a secret', $key); @@ -55,16 +47,21 @@ public function testInvalidKeyEncoding() static::$class::encrypt('a secret', $crazyKey); } - public function testCorruptDataUsingPassword() + public function testNameMatch() { - $key = \Dcrypt\OpensslKeyGenerator::newKey(); + // Make sure that the name has the cipher in it so that there can never be a mismatch between + // the name of the cipher and the cipher given to openssl + $testname1 = strtolower(str_replace('-', '', static::$class::CIPHER)); + $testname2 = strtolower(static::$class); - $encrypted = static::$class::encrypt('a secret', $key); - - $this->assertEquals('a secret', static::$class::decrypt($encrypted, $key)); - - $this->expectException(\Dcrypt\Exceptions\InvalidChecksumException::class); + $this->assertStringContainsString($testname1, $testname2); + } - static::$class::decrypt($encrypted . 'A', $key); + public function testKnownVector() + { + $json = json_decode(file_get_contents(__DIR__ . '/.vectors.json')); + $c = $json->aes256->{static::$class}; + $d = static::$class::decrypt(base64_decode($c), $json->key); + $this->assertEquals('a secret', $d); } } diff --git a/tests/OpensslKeyGeneratorTest.php b/tests/OpensslKeyGeneratorTest.php deleted file mode 100644 index 635518e8..00000000 --- a/tests/OpensslKeyGeneratorTest.php +++ /dev/null @@ -1,25 +0,0 @@ -expectException(InvalidKeyException::class); - - \Dcrypt\OpensslKeyGenerator::newKey(128); - } - - public function testKeyWithCostException() - { - $key = \Dcrypt\OpensslKeyGenerator::newKey(256); - - $this->expectException(InvalidPasswordException::class); - - new \Dcrypt\OpensslKeyGenerator('sha256', $key, 'aes-256-gcm', \random_bytes(128), 10000); - } -} diff --git a/tests/OpensslKeyTest.php b/tests/OpensslKeyTest.php new file mode 100644 index 00000000..d2647886 --- /dev/null +++ b/tests/OpensslKeyTest.php @@ -0,0 +1,17 @@ +expectException(InvalidKeyException::class); + + \Dcrypt\OpensslKey::newKey(128); + } +} diff --git a/tests/OpensslStackTest.php b/tests/OpensslStackTest.php index d8c2a27f..c68abe6a 100644 --- a/tests/OpensslStackTest.php +++ b/tests/OpensslStackTest.php @@ -1,10 +1,14 @@ add('rc4-40', 'md2') ->add('bf-cbc', 'sha256') ->add('bf-cfb', 'sha256') @@ -29,7 +33,7 @@ public function testAes256StackWithPassword() public function testAes256StackWithKeyFromReadmeFile() { - $key = \Dcrypt\OpensslKeyGenerator::newKey(); + $key = \Dcrypt\OpensslKey::newKey(); $stack = (new \Dcrypt\OpensslStack($key)) ->add('aes-256-ecb', 'snefru') diff --git a/tests/OpensslStaticTest.php b/tests/OpensslStaticTest.php index ea91c2cd..2ccb084c 100644 --- a/tests/OpensslStaticTest.php +++ b/tests/OpensslStaticTest.php @@ -1,38 +1,72 @@ $data) { + foreach ($json->algos as $algo => $data) { try { - $plaintext = \Dcrypt\OpensslStatic::decrypt(base64_decode($data), 'world', 'aes-256-gcm', $algo, 1000); + $plaintext = \Dcrypt\OpensslStatic::decrypt(base64_decode($data), $json->key, 'aes-256-gcm', $algo); } catch (\Exception|\Error $e) { throw new \Exception("Failure in [$algo]: " . $e->getMessage()); } - $this->assertEquals('hello', $plaintext); + $this->assertEquals('a secret', $plaintext); } } public function testVectorsCiphers() { - $json = file_get_contents(__DIR__ . '/vectors/openssl-static-ciphers.json'); + $json = file_get_contents(__DIR__ . '/.vectors.json'); $json = json_decode($json); - foreach ($json as $cipher => $data) { + foreach ($json->ciphers as $cipher => $data) { try { - $plaintext = \Dcrypt\OpensslStatic::decrypt(base64_decode($data), 'world', $cipher, 'sha256', 1000); + $plaintext = \Dcrypt\OpensslStatic::decrypt(base64_decode($data), $json->key, $cipher, 'sha3-256'); } catch (\Exception|\Error $e) { throw new \Exception("Failure in [$cipher]: " . $e->getMessage()); } - $this->assertEquals('hello', $plaintext); + $this->assertEquals('a secret', $plaintext); + } + } + + public function testBadCipherException() + { + $key = \Dcrypt\OpensslKey::newKey(); + + $pass = false; + + try { + OpensslStatic::encrypt('a secret', $key, 'lol this cipher doesnt exist', 'sha3-256'); + } catch(\Exception $e) { + $pass = true; } + + $this->assertTrue($pass); + } + + public function testBadAlgoException() + { + $key = \Dcrypt\OpensslKey::newKey(); + + $pass = false; + + try { + OpensslStatic::encrypt('a secret', $key, 'aes-256-gcm', 'lol this algo doesnt exist'); + } catch(\Exception $e) { + $pass = true; + } + + $this->assertTrue($pass); } } \ No newline at end of file diff --git a/tests/OtpTest.php b/tests/OtpTest.php index 98537eed..f8e38316 100644 --- a/tests/OtpTest.php +++ b/tests/OtpTest.php @@ -1,38 +1,36 @@ assertEquals(strlen($input), strlen($encrypted)); $this->assertNotEquals($input, $encrypted); - /* - * Test decryption - */ - $decrypted = Otp::crypt($encrypted, $key, 1000); + $decrypted = Otp::crypt($encrypted, $key); $this->assertEquals($input, $decrypted); } } public function testVector() { - $json = json_decode(file_get_contents(__DIR__ . '/vectors/otp.json')); + $json = json_decode(file_get_contents(__DIR__ . '/.vectors.json')); - foreach ($json as $mult => $data) { + foreach ($json->otp as $mult => $data) { $data = base64_decode($data); $expected = str_repeat('A', (int)$mult); - $this->assertEquals($expected, Otp::crypt($data, 'password', 1000)); + $this->assertEquals($expected, Otp::crypt($data, $json->key)); } } } diff --git a/tests/Rc4Test.php b/tests/Rc4Test.php deleted file mode 100644 index 115cb7fa..00000000 --- a/tests/Rc4Test.php +++ /dev/null @@ -1,37 +0,0 @@ -assertEquals(strlen($input), strlen($encrypted)); - $this->assertNotEquals($input, $encrypted); - - /* - * Test decryption - */ - $decrypted = Rc4::crypt($encrypted, $key); - $this->assertEquals($input, $decrypted); - } - - public function testVector() - { - /* - * Test that known cypher text decrypts properly - */ - $cyphertext = hex2bin('140ad3d278a229ff3c487d'); - $plain = 'Hello World'; - $key = 'asdf'; - - $this->assertEquals($plain, Rc4::crypt($cyphertext, $key)); - } -} diff --git a/tests/SpritzTest.php b/tests/SpritzTest.php deleted file mode 100644 index a13fbab9..00000000 --- a/tests/SpritzTest.php +++ /dev/null @@ -1,25 +0,0 @@ -assertEquals(strlen($input), strlen($encrypted)); - $this->assertNotEquals($input, $encrypted); - - /* - * Test decryption - */ - $decrypted = Spritz::crypt($encrypted, $key); - $this->assertEquals($input, $decrypted); - } -} diff --git a/tests/StrTest.php b/tests/StrTest.php index 3f7010e1..22f61103 100644 --- a/tests/StrTest.php +++ b/tests/StrTest.php @@ -1,5 +1,7 @@