"ezyang/htmlpurifier": "~4.7.0",\r
"mobiledetect/mobiledetectlib": "2.8.*",\r
"league/html-to-markdown": "~4.4.1",\r
- "defuse/php-encryption": "2.*",\r
+ "defuse/php-encryption": "1.*",\r
"pear/Text_LanguageDetect": "1.*",\r
"pear-pear.php.net/Text_Highlighter": "*"\r
},\r
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "content-hash": "ce088458d9f01920ccee128082ef924a",
+ "content-hash": "a6a3dae4b15752d8f377b1fc1e5a2b47",
"packages": [
{
"name": "defuse/php-encryption",
- "version": "v2.1.0",
+ "version": "v1.2.1",
"source": {
"type": "git",
"url": "https://github.com/defuse/php-encryption.git",
- "reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689"
+ "reference": "b87737b2eec06b13f025cabea847338fa203d1b4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/defuse/php-encryption/zipball/5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689",
- "reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689",
+ "url": "https://api.github.com/repos/defuse/php-encryption/zipball/b87737b2eec06b13f025cabea847338fa203d1b4",
+ "reference": "b87737b2eec06b13f025cabea847338fa203d1b4",
"shasum": ""
},
"require": {
+ "ext-mcrypt": "*",
"ext-openssl": "*",
- "paragonie/random_compat": "~2.0",
"php": ">=5.4.0"
},
- "require-dev": {
- "nikic/php-parser": "^2.0|^3.0",
- "phpunit/phpunit": "^4|^5"
- },
- "bin": [
- "bin/generate-defuse-key"
- ],
"type": "library",
"autoload": {
- "psr-4": {
- "Defuse\\Crypto\\": "src"
- }
+ "files": [
+ "Crypto.php"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"authors": [
{
"name": "Taylor Hornby",
- "email": "taylor@defuse.ca",
- "homepage": "https://defuse.ca/"
- },
- {
- "name": "Scott Arciszewski",
- "email": "info@paragonie.com",
- "homepage": "https://paragonie.com"
+ "email": "havoc@defuse.ca"
}
],
"description": "Secure PHP Encryption Library",
"keywords": [
"aes",
- "authenticated encryption",
"cipher",
- "crypto",
- "cryptography",
- "encrypt",
"encryption",
- "openssl",
- "security",
- "symmetric key cryptography"
+ "mcrypt",
+ "security"
],
- "time": "2017-05-18T21:28:48+00:00"
+ "time": "2015-03-14T20:27:45+00:00"
},
{
"name": "ezyang/htmlpurifier",
],
"time": "2017-08-29T18:23:54+00:00"
},
- {
- "name": "paragonie/random_compat",
- "version": "v2.0.11",
- "source": {
- "type": "git",
- "url": "https://github.com/paragonie/random_compat.git",
- "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/paragonie/random_compat/zipball/5da4d3c796c275c55f057af5a643ae297d96b4d8",
- "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8",
- "shasum": ""
- },
- "require": {
- "php": ">=5.2.0"
- },
- "require-dev": {
- "phpunit/phpunit": "4.*|5.*"
- },
- "suggest": {
- "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
- },
- "type": "library",
- "autoload": {
- "files": [
- "lib/random.php"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Paragon Initiative Enterprises",
- "email": "security@paragonie.com",
- "homepage": "https://paragonie.com"
- }
- ],
- "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
- "keywords": [
- "csprng",
- "pseudorandom",
- "random"
- ],
- "time": "2017-09-27T21:40:39+00:00"
- },
{
"name": "pear-pear.php.net/Archive_Tar",
"version": "1.4.3",
+++ /dev/null
-#!/usr/bin/env sh
-
-dir=$(d=${0%[/\\]*}; cd "$d"; cd "../defuse/php-encryption/bin" && pwd)
-
-# See if we are running in Cygwin by checking for cygpath program
-if command -v 'cygpath' >/dev/null 2>&1; then
- # Cygwin paths start with /cygdrive/ which will break windows PHP,
- # so we need to translate the dir path to windows format. However
- # we could be using cygwin PHP which does not require this, so we
- # test if the path to PHP starts with /cygdrive/ rather than /usr/bin
- if [[ $(which php) == /cygdrive/* ]]; then
- dir=$(cygpath -m "$dir");
- fi
-fi
-
-dir=$(echo $dir | sed 's/ /\ /g')
-"${dir}/generate-defuse-key" "$@"
+++ /dev/null
-@ECHO OFF\r
-setlocal DISABLEDELAYEDEXPANSION\r
-SET BIN_TARGET=%~dp0/../defuse/php-encryption/bin/generate-defuse-key\r
-php "%BIN_TARGET%" %*\r
return array(
'Archive_Tar' => $vendorDir . '/pear-pear.php.net/Archive_Tar/Archive/Tar.php',
'Console_Getopt' => $vendorDir . '/pear-pear.php.net/Console_Getopt/Console/Getopt.php',
- 'Defuse\\Crypto\\Core' => $vendorDir . '/defuse/php-encryption/src/Core.php',
- 'Defuse\\Crypto\\Crypto' => $vendorDir . '/defuse/php-encryption/src/Crypto.php',
- 'Defuse\\Crypto\\DerivedKeys' => $vendorDir . '/defuse/php-encryption/src/DerivedKeys.php',
- 'Defuse\\Crypto\\Encoding' => $vendorDir . '/defuse/php-encryption/src/Encoding.php',
- 'Defuse\\Crypto\\Exception\\BadFormatException' => $vendorDir . '/defuse/php-encryption/src/Exception/BadFormatException.php',
- 'Defuse\\Crypto\\Exception\\CryptoException' => $vendorDir . '/defuse/php-encryption/src/Exception/CryptoException.php',
- 'Defuse\\Crypto\\Exception\\EnvironmentIsBrokenException' => $vendorDir . '/defuse/php-encryption/src/Exception/EnvironmentIsBrokenException.php',
- 'Defuse\\Crypto\\Exception\\IOException' => $vendorDir . '/defuse/php-encryption/src/Exception/IOException.php',
- 'Defuse\\Crypto\\Exception\\WrongKeyOrModifiedCiphertextException' => $vendorDir . '/defuse/php-encryption/src/Exception/WrongKeyOrModifiedCiphertextException.php',
- 'Defuse\\Crypto\\File' => $vendorDir . '/defuse/php-encryption/src/File.php',
- 'Defuse\\Crypto\\Key' => $vendorDir . '/defuse/php-encryption/src/Key.php',
- 'Defuse\\Crypto\\KeyOrPassword' => $vendorDir . '/defuse/php-encryption/src/KeyOrPassword.php',
- 'Defuse\\Crypto\\KeyProtectedByPassword' => $vendorDir . '/defuse/php-encryption/src/KeyProtectedByPassword.php',
- 'Defuse\\Crypto\\RuntimeTests' => $vendorDir . '/defuse/php-encryption/src/RuntimeTests.php',
'Detection\\MobileDetect' => $vendorDir . '/mobiledetect/mobiledetectlib/namespaced/Detection/MobileDetect.php',
'Friendica\\App' => $baseDir . '/src/App.php',
'Friendica\\Core\\Config' => $baseDir . '/src/Core/Config.php',
$baseDir = dirname($vendorDir);
return array(
- '5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
'2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
+ '8170285c807a9f24f165f37b15bc9a36' => $vendorDir . '/defuse/php-encryption/Crypto.php',
);
return array(
'League\\HTMLToMarkdown\\' => array($vendorDir . '/league/html-to-markdown/src'),
'Friendica\\' => array($baseDir . '/src'),
- 'Defuse\\Crypto\\' => array($vendorDir . '/defuse/php-encryption/src'),
);
class ComposerStaticInitFriendica
{
public static $files = array (
- '5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
'2cffec82183ee1cea088009cef9a6fc3' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
+ '8170285c807a9f24f165f37b15bc9a36' => __DIR__ . '/..' . '/defuse/php-encryption/Crypto.php',
);
public static $prefixLengthsPsr4 = array (
array (
'Friendica\\' => 10,
),
- 'D' =>
- array (
- 'Defuse\\Crypto\\' => 14,
- ),
);
public static $prefixDirsPsr4 = array (
array (
0 => __DIR__ . '/../..' . '/src',
),
- 'Defuse\\Crypto\\' =>
- array (
- 0 => __DIR__ . '/..' . '/defuse/php-encryption/src',
- ),
);
public static $prefixesPsr0 = array (
public static $classMap = array (
'Archive_Tar' => __DIR__ . '/..' . '/pear-pear.php.net/Archive_Tar/Archive/Tar.php',
'Console_Getopt' => __DIR__ . '/..' . '/pear-pear.php.net/Console_Getopt/Console/Getopt.php',
- 'Defuse\\Crypto\\Core' => __DIR__ . '/..' . '/defuse/php-encryption/src/Core.php',
- 'Defuse\\Crypto\\Crypto' => __DIR__ . '/..' . '/defuse/php-encryption/src/Crypto.php',
- 'Defuse\\Crypto\\DerivedKeys' => __DIR__ . '/..' . '/defuse/php-encryption/src/DerivedKeys.php',
- 'Defuse\\Crypto\\Encoding' => __DIR__ . '/..' . '/defuse/php-encryption/src/Encoding.php',
- 'Defuse\\Crypto\\Exception\\BadFormatException' => __DIR__ . '/..' . '/defuse/php-encryption/src/Exception/BadFormatException.php',
- 'Defuse\\Crypto\\Exception\\CryptoException' => __DIR__ . '/..' . '/defuse/php-encryption/src/Exception/CryptoException.php',
- 'Defuse\\Crypto\\Exception\\EnvironmentIsBrokenException' => __DIR__ . '/..' . '/defuse/php-encryption/src/Exception/EnvironmentIsBrokenException.php',
- 'Defuse\\Crypto\\Exception\\IOException' => __DIR__ . '/..' . '/defuse/php-encryption/src/Exception/IOException.php',
- 'Defuse\\Crypto\\Exception\\WrongKeyOrModifiedCiphertextException' => __DIR__ . '/..' . '/defuse/php-encryption/src/Exception/WrongKeyOrModifiedCiphertextException.php',
- 'Defuse\\Crypto\\File' => __DIR__ . '/..' . '/defuse/php-encryption/src/File.php',
- 'Defuse\\Crypto\\Key' => __DIR__ . '/..' . '/defuse/php-encryption/src/Key.php',
- 'Defuse\\Crypto\\KeyOrPassword' => __DIR__ . '/..' . '/defuse/php-encryption/src/KeyOrPassword.php',
- 'Defuse\\Crypto\\KeyProtectedByPassword' => __DIR__ . '/..' . '/defuse/php-encryption/src/KeyProtectedByPassword.php',
- 'Defuse\\Crypto\\RuntimeTests' => __DIR__ . '/..' . '/defuse/php-encryption/src/RuntimeTests.php',
'Detection\\MobileDetect' => __DIR__ . '/..' . '/mobiledetect/mobiledetectlib/namespaced/Detection/MobileDetect.php',
'Friendica\\App' => __DIR__ . '/../..' . '/src/App.php',
'Friendica\\Core\\Config' => __DIR__ . '/../..' . '/src/Core/Config.php',
"description": "Identify human languages from text samples",
"homepage": "http://pear.php.net/package/Text_LanguageDetect"
},
- {
- "name": "paragonie/random_compat",
- "version": "v2.0.11",
- "version_normalized": "2.0.11.0",
- "source": {
- "type": "git",
- "url": "https://github.com/paragonie/random_compat.git",
- "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/paragonie/random_compat/zipball/5da4d3c796c275c55f057af5a643ae297d96b4d8",
- "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8",
- "shasum": ""
- },
- "require": {
- "php": ">=5.2.0"
- },
- "require-dev": {
- "phpunit/phpunit": "4.*|5.*"
- },
- "suggest": {
- "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
- },
- "time": "2017-09-27T21:40:39+00:00",
- "type": "library",
- "installation-source": "dist",
- "autoload": {
- "files": [
- "lib/random.php"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Paragon Initiative Enterprises",
- "email": "security@paragonie.com",
- "homepage": "https://paragonie.com"
- }
- ],
- "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
- "keywords": [
- "csprng",
- "pseudorandom",
- "random"
- ]
- },
{
"name": "defuse/php-encryption",
- "version": "v2.1.0",
- "version_normalized": "2.1.0.0",
+ "version": "v1.2.1",
+ "version_normalized": "1.2.1.0",
"source": {
"type": "git",
"url": "https://github.com/defuse/php-encryption.git",
- "reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689"
+ "reference": "b87737b2eec06b13f025cabea847338fa203d1b4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/defuse/php-encryption/zipball/5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689",
- "reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689",
+ "url": "https://api.github.com/repos/defuse/php-encryption/zipball/b87737b2eec06b13f025cabea847338fa203d1b4",
+ "reference": "b87737b2eec06b13f025cabea847338fa203d1b4",
"shasum": ""
},
"require": {
+ "ext-mcrypt": "*",
"ext-openssl": "*",
- "paragonie/random_compat": "~2.0",
"php": ">=5.4.0"
},
- "require-dev": {
- "nikic/php-parser": "^2.0|^3.0",
- "phpunit/phpunit": "^4|^5"
- },
- "time": "2017-05-18T21:28:48+00:00",
- "bin": [
- "bin/generate-defuse-key"
- ],
+ "time": "2015-03-14T20:27:45+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
- "psr-4": {
- "Defuse\\Crypto\\": "src"
- }
+ "files": [
+ "Crypto.php"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"authors": [
{
"name": "Taylor Hornby",
- "email": "taylor@defuse.ca",
- "homepage": "https://defuse.ca/"
- },
- {
- "name": "Scott Arciszewski",
- "email": "info@paragonie.com",
- "homepage": "https://paragonie.com"
+ "email": "havoc@defuse.ca"
}
],
"description": "Secure PHP Encryption Library",
"keywords": [
"aes",
- "authenticated encryption",
"cipher",
- "crypto",
- "cryptography",
- "encrypt",
"encryption",
- "openssl",
- "security",
- "symmetric key cryptography"
+ "mcrypt",
+ "security"
]
}
]
+++ /dev/null
-*~
-/test/unit/File/big-generated-file
-/composer.lock
-/vendor
-defuse-crypto.phar
-defuse-crypto.phar.sig
-composer.phar
-box.phar
-phpunit.phar
-phpunit.phar.asc
-test/unit/File/tmp
+++ /dev/null
-<?php
-
-$config = Symfony\CS\Config\Config::create()
- ->level(Symfony\CS\FixerInterface::PSR2_LEVEL)
- ->fixers([
- 'blankline_after_open_tag',
- 'empty_return',
- 'extra_empty_lines',
- 'function_typehint_space',
- 'join_function',
- 'method_argument_default_value',
- 'multiline_array_trailing_comma',
- 'no_blank_lines_after_class_opening',
- 'no_empty_lines_after_phpdocs',
- 'phpdoc_indent',
- 'phpdoc_no_access',
- 'phpdoc_no_empty_return',
- 'phpdoc_no_package',
- 'phpdoc_params',
- 'phpdoc_scalar',
- 'phpdoc_separation',
- 'phpdoc_trim',
- 'phpdoc_type_to_var',
- 'phpdoc_types',
- 'phpdoc_var_without_name',
- 'remove_leading_slash_use',
- 'remove_lines_between_uses',
- 'short_bool_cast',
- 'single_quote',
- 'spaces_after_semicolon',
- 'spaces_before_semicolon',
- 'spaces_cast',
- 'standardize_not_equal',
- 'ternary_spaces',
- 'trim_array_spaces',
- 'unneeded_control_parentheses',
- 'unused_use',
- 'whitespacy_lines',
- 'align_double_arrow',
- 'concat_with_spaces',
- 'logical_not_operators_with_successor_space',
- 'multiline_spaces_before_semicolon',
- 'newline_after_open_tag',
- 'ordered_use',
- 'php_unit_construct',
- 'phpdoc_order',
- 'short_array_syntax',
- ]);
-
-if (null === $input->getArgument('path')) {
- $config
- ->finder(
- Symfony\CS\Finder\DefaultFinder::create()
- ->in('src')
- ->in('test')
- ->exclude('vendor')
- );
-}
-
-return $config;
--- /dev/null
+language: php
+php:
+ - "5.6"
+ - "5.5"
+ - "5.4"
+ - "5.3"
+ - "5.2"
+# Versions below here are not installed on travis-ci
+# - "5.1"
+# - "5.0"
+# - "4.4"
+# - "4.3"
+# - "4.2"
+# - "4.1"
+# - "4.0"
+
+matrix:
+ allow_failures:
+ - php: "5.3"
+ - php: "5.2"
+
+script: ./test.sh
--- /dev/null
+<?php
+
+/*
+ * PHP Encryption Library
+ * Copyright (c) 2014, Taylor Hornby
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Web: https://defuse.ca/secure-php-encryption.htm
+ * GitHub: https://github.com/defuse/php-encryption
+ *
+ * WARNING: This encryption library is not a silver bullet. It only provides
+ * symmetric encryption given a uniformly random key. This means you MUST NOT
+ * use an ASCII string like a password as the key parameter, it MUST be
+ * a uniformly random key generated by CreateNewRandomKey(). If you want to
+ * encrypt something with a password, apply a password key derivation function
+ * like PBKDF2 or scrypt with a random salt to generate a key.
+ *
+ * WARNING: Error handling is very important, especially for crypto code!
+ *
+ * How to use this code:
+ *
+ * Generating a Key
+ * ----------------
+ * try {
+ * $key = self::CreateNewRandomKey();
+ * // WARNING: Do NOT encode $key with bin2hex() or base64_encode(),
+ * // they may leak the key to the attacker through side channels.
+ * } catch (CryptoTestFailedException $ex) {
+ * die('Cannot safely create a key');
+ * } catch (CannotPerformOperationException $ex) {
+ * die('Cannot safely create a key');
+ * }
+ *
+ * Encrypting a Message
+ * --------------------
+ * $message = "ATTACK AT DAWN";
+ * try {
+ * $ciphertext = self::Encrypt($message, $key);
+ * } catch (CryptoTestFailedException $ex) {
+ * die('Cannot safely perform encryption');
+ * } catch (CannotPerformOperationException $ex) {
+ * die('Cannot safely perform decryption');
+ * }
+ *
+ * Decrypting a Message
+ * --------------------
+ * try {
+ * $decrypted = self::Decrypt($ciphertext, $key);
+ * } catch (InvalidCiphertextException $ex) { // VERY IMPORTANT
+ * // Either:
+ * // 1. The ciphertext was modified by the attacker,
+ * // 2. The key is wrong, or
+ * // 3. $ciphertext is not a valid ciphertext or was corrupted.
+ * // Assume the worst.
+ * die('DANGER! DANGER! The ciphertext has been tampered with!');
+ * } catch (CryptoTestFailedException $ex) {
+ * die('Cannot safely perform encryption');
+ * } catch (CannotPerformOperationException $ex) {
+ * die('Cannot safely perform decryption');
+ * }
+ */
+
+/*
+ * Raised by Decrypt() when one of the following conditions are met:
+ * - The key is wrong.
+ * - The ciphertext is invalid or not in the correct format.
+ * - The attacker modified the ciphertext.
+ */
+class InvalidCiphertextException extends Exception {}
+/* If you see these, it means it is NOT SAFE to do encryption on your system. */
+class CannotPerformOperationException extends Exception {}
+class CryptoTestFailedException extends Exception {}
+
+final class Crypto
+{
+ // Ciphertext format: [____HMAC____][____IV____][____CIPHERTEXT____].
+
+ /* DO NOT CHANGE THESE CONSTANTS!
+ *
+ * We spent *weeks* testing this code, making sure it is as perfect and
+ * correct as possible. Are you going to do the same after making your
+ * changes? Probably not. Besides, any change to these constants will break
+ * the runtime tests, which are extremely important for your security.
+ * You're literally millions of times more likely to screw up your own
+ * security by changing something here than you are to fall victim to an
+ * 128-bit key brute-force attack. You're also breaking your own
+ * compatibility with future updates to this library, so you'll be left
+ * vulnerable if we ever find a security bug and release a fix.
+ *
+ * So, PLEASE, do not change these constants.
+ */
+ const CIPHER = 'aes-128';
+ const KEY_BYTE_SIZE = 16;
+ const CIPHER_MODE = 'cbc';
+ const HASH_FUNCTION = 'sha256';
+ const MAC_BYTE_SIZE = 32;
+ const ENCRYPTION_INFO = 'DefusePHP|KeyForEncryption';
+ const AUTHENTICATION_INFO = 'DefusePHP|KeyForAuthentication';
+
+ /*
+ * Use this to generate a random encryption key.
+ */
+ public static function CreateNewRandomKey()
+ {
+ self::RuntimeTest();
+ return self::SecureRandom(self::KEY_BYTE_SIZE);
+ }
+
+ /*
+ * Encrypts a message.
+ * $plaintext is the message to encrypt.
+ * $key is the encryption key, a value generated by CreateNewRandomKey().
+ * You MUST catch exceptions thrown by this function. See docs above.
+ */
+ public static function Encrypt($plaintext, $key)
+ {
+ self::RuntimeTest();
+
+ if (self::our_strlen($key) !== self::KEY_BYTE_SIZE)
+ {
+ throw new CannotPerformOperationException("Bad key.");
+ }
+
+ $method = self::CIPHER.'-'.self::CIPHER_MODE;
+
+ self::EnsureFunctionExists('openssl_get_cipher_methods');
+ if (in_array($method, openssl_get_cipher_methods()) === FALSE) {
+ throw new CannotPerformOperationException("Cipher method not supported.");
+ }
+
+ // Generate a sub-key for encryption.
+ $keysize = self::KEY_BYTE_SIZE;
+ $ekey = self::HKDF(self::HASH_FUNCTION, $key, $keysize, self::ENCRYPTION_INFO);
+
+ // Generate a random initialization vector.
+ self::EnsureFunctionExists("openssl_cipher_iv_length");
+ $ivsize = openssl_cipher_iv_length($method);
+ if ($ivsize === FALSE || $ivsize <= 0) {
+ throw new CannotPerformOperationException();
+ }
+ $iv = self::SecureRandom($ivsize);
+
+ $ciphertext = $iv . self::PlainEncrypt($plaintext, $ekey, $iv);
+
+ // Generate a sub-key for authentication and apply the HMAC.
+ $akey = self::HKDF(self::HASH_FUNCTION, $key, self::KEY_BYTE_SIZE, self::AUTHENTICATION_INFO);
+ $auth = hash_hmac(self::HASH_FUNCTION, $ciphertext, $akey, true);
+ $ciphertext = $auth . $ciphertext;
+
+ return $ciphertext;
+ }
+
+ /*
+ * Decrypts a ciphertext.
+ * $ciphertext is the ciphertext to decrypt.
+ * $key is the key that the ciphertext was encrypted with.
+ * You MUST catch exceptions thrown by this function. See docs above.
+ */
+ public static function Decrypt($ciphertext, $key)
+ {
+ self::RuntimeTest();
+
+ $method = self::CIPHER.'-'.self::CIPHER_MODE;
+
+ self::EnsureFunctionExists('openssl_get_cipher_methods');
+ if (in_array($method, openssl_get_cipher_methods()) === FALSE) {
+ throw new CannotPerformOperationException("Cipher method not supported.");
+ }
+
+ // Extract the HMAC from the front of the ciphertext.
+ if (self::our_strlen($ciphertext) <= self::MAC_BYTE_SIZE) {
+ throw new InvalidCiphertextException();
+ }
+ $hmac = self::our_substr($ciphertext, 0, self::MAC_BYTE_SIZE);
+ if ($hmac === FALSE) {
+ throw new CannotPerformOperationException();
+ }
+ $ciphertext = self::our_substr($ciphertext, self::MAC_BYTE_SIZE);
+ if ($ciphertext === FALSE) {
+ throw new CannotPerformOperationException();
+ }
+
+ // Regenerate the same authentication sub-key.
+ $akey = self::HKDF(self::HASH_FUNCTION, $key, self::KEY_BYTE_SIZE, self::AUTHENTICATION_INFO);
+
+ if (self::VerifyHMAC($hmac, $ciphertext, $akey))
+ {
+ // Regenerate the same encryption sub-key.
+ $keysize = self::KEY_BYTE_SIZE;
+ $ekey = self::HKDF(self::HASH_FUNCTION, $key, $keysize, self::ENCRYPTION_INFO);
+
+ // Extract the initialization vector from the ciphertext.
+ self::EnsureFunctionExists("openssl_cipher_iv_length");
+ $ivsize = openssl_cipher_iv_length($method);
+ if ($ivsize === FALSE || $ivsize <= 0) {
+ throw new CannotPerformOperationException();
+ }
+ if (self::our_strlen($ciphertext) <= $ivsize) {
+ throw new InvalidCiphertextException();
+ }
+ $iv = self::our_substr($ciphertext, 0, $ivsize);
+ if ($iv === FALSE) {
+ throw new CannotPerformOperationException();
+ }
+ $ciphertext = self::our_substr($ciphertext, $ivsize);
+ if ($ciphertext === FALSE) {
+ throw new CannotPerformOperationException();
+ }
+
+ $plaintext = self::PlainDecrypt($ciphertext, $ekey, $iv);
+
+ return $plaintext;
+ }
+ else
+ {
+ /*
+ * We throw an exception instead of returning FALSE because we want
+ * a script that doesn't handle this condition to CRASH, instead
+ * of thinking the ciphertext decrypted to the value FALSE.
+ */
+ throw new InvalidCiphertextException();
+ }
+ }
+
+ /*
+ * Runs tests.
+ * Raises CannotPerformOperationException or CryptoTestFailedException if
+ * one of the tests fail. If any tests fails, your system is not capable of
+ * performing encryption, so make sure you fail safe in that case.
+ */
+ public static function RuntimeTest()
+ {
+ // 0: Tests haven't been run yet.
+ // 1: Tests have passed.
+ // 2: Tests are running right now.
+ // 3: Tests have failed.
+ static $test_state = 0;
+
+ if ($test_state === 1 || $test_state === 2) {
+ return;
+ }
+
+ try {
+ $test_state = 2;
+ self::AESTestVector();
+ self::HMACTestVector();
+ self::HKDFTestVector();
+
+ self::TestEncryptDecrypt();
+ if (self::our_strlen(self::CreateNewRandomKey()) != self::KEY_BYTE_SIZE) {
+ throw new CryptoTestFailedException();
+ }
+
+ if (self::ENCRYPTION_INFO == self::AUTHENTICATION_INFO) {
+ throw new CryptoTestFailedException();
+ }
+ } catch (CryptoTestFailedException $ex) {
+ // Do this, otherwise it will stay in the "tests are running" state.
+ $test_state = 3;
+ throw $ex;
+ }
+
+ // Change this to '0' make the tests always re-run (for benchmarking).
+ $test_state = 1;
+ }
+
+ /*
+ * Never call this method directly!
+ */
+ private static function PlainEncrypt($plaintext, $key, $iv)
+ {
+
+ $method = self::CIPHER.'-'.self::CIPHER_MODE;
+
+ self::EnsureConstantExists("OPENSSL_RAW_DATA");
+ self::EnsureFunctionExists("openssl_encrypt");
+ $ciphertext = openssl_encrypt(
+ $plaintext,
+ $method,
+ $key,
+ OPENSSL_RAW_DATA,
+ $iv
+ );
+
+ if ($ciphertext === false) {
+ throw new CannotPerformOperationException();
+ }
+
+ return $ciphertext;
+ }
+
+ /*
+ * Never call this method directly!
+ */
+ private static function PlainDecrypt($ciphertext, $key, $iv)
+ {
+
+ $method = self::CIPHER.'-'.self::CIPHER_MODE;
+
+ self::EnsureConstantExists("OPENSSL_RAW_DATA");
+ self::EnsureFunctionExists("openssl_encrypt");
+ $plaintext = openssl_decrypt(
+ $ciphertext,
+ $method,
+ $key,
+ OPENSSL_RAW_DATA,
+ $iv
+ );
+ if ($plaintext === FALSE) {
+ throw new CannotPerformOperationException();
+ }
+
+ return $plaintext;
+ }
+
+ /*
+ * Returns a random binary string of length $octets bytes.
+ */
+ private static function SecureRandom($octets)
+ {
+ self::EnsureFunctionExists("mcrypt_create_iv");
+ $random = mcrypt_create_iv($octets, MCRYPT_DEV_URANDOM);
+ if ($random === FALSE) {
+ throw new CannotPerformOperationException();
+ } else {
+ return $random;
+ }
+ }
+
+ /*
+ * Use HKDF to derive multiple keys from one.
+ * http://tools.ietf.org/html/rfc5869
+ */
+ private static function HKDF($hash, $ikm, $length, $info = '', $salt = NULL)
+ {
+ // Find the correct digest length as quickly as we can.
+ $digest_length = self::MAC_BYTE_SIZE;
+ if ($hash != self::HASH_FUNCTION) {
+ $digest_length = self::our_strlen(hash_hmac($hash, '', '', true));
+ }
+
+ // Sanity-check the desired output length.
+ if (empty($length) || !is_int($length) ||
+ $length < 0 || $length > 255 * $digest_length) {
+ throw new CannotPerformOperationException();
+ }
+
+ // "if [salt] not provided, is set to a string of HashLen zeroes."
+ if (is_null($salt)) {
+ $salt = str_repeat("\x00", $digest_length);
+ }
+
+ // HKDF-Extract:
+ // PRK = HMAC-Hash(salt, IKM)
+ // The salt is the HMAC key.
+ $prk = hash_hmac($hash, $ikm, $salt, true);
+
+ // HKDF-Expand:
+
+ // This check is useless, but it serves as a reminder to the spec.
+ if (self::our_strlen($prk) < $digest_length) {
+ throw new CannotPerformOperationException();
+ }
+
+ // T(0) = ''
+ $t = '';
+ $last_block = '';
+ for ($block_index = 1; self::our_strlen($t) < $length; $block_index++) {
+ // T(i) = HMAC-Hash(PRK, T(i-1) | info | 0x??)
+ $last_block = hash_hmac(
+ $hash,
+ $last_block . $info . chr($block_index),
+ $prk,
+ true
+ );
+ // T = T(1) | T(2) | T(3) | ... | T(N)
+ $t .= $last_block;
+ }
+
+ // ORM = first L octets of T
+ $orm = self::our_substr($t, 0, $length);
+ if ($orm === FALSE) {
+ throw new CannotPerformOperationException();
+ }
+ return $orm;
+ }
+
+ private static function VerifyHMAC($correct_hmac, $message, $key)
+ {
+ $message_hmac = hash_hmac(self::HASH_FUNCTION, $message, $key, true);
+
+ // We can't just compare the strings with '==', since it would make
+ // timing attacks possible. We could use the XOR-OR constant-time
+ // comparison algorithm, but I'm not sure if that's good enough way up
+ // here in an interpreted language. So we use the method of HMACing the
+ // strings we want to compare with a random key, then comparing those.
+
+ // NOTE: This leaks information when the strings are not the same
+ // length, but they should always be the same length here. Enforce it:
+ if (self::our_strlen($correct_hmac) !== self::our_strlen($message_hmac)) {
+ throw new CannotPerformOperationException();
+ }
+
+ $blind = self::CreateNewRandomKey();
+ $message_compare = hash_hmac(self::HASH_FUNCTION, $message_hmac, $blind);
+ $correct_compare = hash_hmac(self::HASH_FUNCTION, $correct_hmac, $blind);
+ return $correct_compare === $message_compare;
+ }
+
+ private static function TestEncryptDecrypt()
+ {
+ $key = self::CreateNewRandomKey();
+ $data = "EnCrYpT EvErYThInG\x00\x00";
+
+ // Make sure encrypting then decrypting doesn't change the message.
+ $ciphertext = self::Encrypt($data, $key);
+ try {
+ $decrypted = self::Decrypt($ciphertext, $key);
+ } catch (InvalidCiphertextException $ex) {
+ // It's important to catch this and change it into a
+ // CryptoTestFailedException, otherwise a test failure could trick
+ // the user into thinking it's just an invalid ciphertext!
+ throw new CryptoTestFailedException();
+ }
+ if($decrypted !== $data)
+ {
+ throw new CryptoTestFailedException();
+ }
+
+ // Modifying the ciphertext: Appending a string.
+ try {
+ self::Decrypt($ciphertext . "a", $key);
+ throw new CryptoTestFailedException();
+ } catch (InvalidCiphertextException $e) { /* expected */ }
+
+ // Modifying the ciphertext: Changing an IV byte.
+ try {
+ $ciphertext[0] = chr((ord($ciphertext[0]) + 1) % 256);
+ self::Decrypt($ciphertext, $key);
+ throw new CryptoTestFailedException();
+ } catch (InvalidCiphertextException $e) { /* expected */ }
+
+ // Decrypting with the wrong key.
+ $key = self::CreateNewRandomKey();
+ $data = "abcdef";
+ $ciphertext = self::Encrypt($data, $key);
+ $wrong_key = self::CreateNewRandomKey();
+ try {
+ self::Decrypt($ciphertext, $wrong_key);
+ throw new CryptoTestFailedException();
+ } catch (InvalidCiphertextException $e) { /* expected */ }
+
+ // Ciphertext too small (shorter than HMAC).
+ $key = self::CreateNewRandomKey();
+ $ciphertext = str_repeat("A", self::MAC_BYTE_SIZE - 1);
+ try {
+ self::Decrypt($ciphertext, $key);
+ throw new CryptoTestFailedException();
+ } catch (InvalidCiphertextException $e) { /* expected */ }
+ }
+
+ private static function HKDFTestVector()
+ {
+ // HKDF test vectors from RFC 5869
+
+ // Test Case 1
+ $ikm = str_repeat("\x0b", 22);
+ $salt = self::hexToBytes("000102030405060708090a0b0c");
+ $info = self::hexToBytes("f0f1f2f3f4f5f6f7f8f9");
+ $length = 42;
+ $okm = self::hexToBytes(
+ "3cb25f25faacd57a90434f64d0362f2a" .
+ "2d2d0a90cf1a5a4c5db02d56ecc4c5bf" .
+ "34007208d5b887185865"
+ );
+ $computed_okm = self::HKDF("sha256", $ikm, $length, $info, $salt);
+ if ($computed_okm !== $okm) {
+ throw new CryptoTestFailedException();
+ }
+
+ // Test Case 7
+ $ikm = str_repeat("\x0c", 22);
+ $length = 42;
+ $okm = self::hexToBytes(
+ "2c91117204d745f3500d636a62f64f0a" .
+ "b3bae548aa53d423b0d1f27ebba6f5e5" .
+ "673a081d70cce7acfc48"
+ );
+ $computed_okm = self::HKDF("sha1", $ikm, $length);
+ if ($computed_okm !== $okm) {
+ throw new CryptoTestFailedException();
+ }
+
+ }
+
+ private static function HMACTestVector()
+ {
+ // HMAC test vector From RFC 4231 (Test Case 1)
+ $key = str_repeat("\x0b", 20);
+ $data = "Hi There";
+ $correct = "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7";
+ if (hash_hmac(self::HASH_FUNCTION, $data, $key) != $correct) {
+ throw new CryptoTestFailedException();
+ }
+ }
+
+ private static function AESTestVector()
+ {
+ // AES CBC mode test vector from NIST SP 800-38A
+ $key = self::hexToBytes("2b7e151628aed2a6abf7158809cf4f3c");
+ $iv = self::hexToBytes("000102030405060708090a0b0c0d0e0f");
+ $plaintext = self::hexToBytes(
+ "6bc1bee22e409f96e93d7e117393172a" .
+ "ae2d8a571e03ac9c9eb76fac45af8e51" .
+ "30c81c46a35ce411e5fbc1191a0a52ef" .
+ "f69f2445df4f9b17ad2b417be66c3710"
+ );
+ $ciphertext = self::hexToBytes(
+ "7649abac8119b246cee98e9b12e9197d" .
+ "5086cb9b507219ee95db113a917678b2" .
+ "73bed6b8e3c1743b7116e69e22229516" .
+ "3ff1caa1681fac09120eca307586e1a7" .
+ /* Block due to padding. Not from NIST test vector.
+ Padding Block: 10101010101010101010101010101010
+ Ciphertext: 3ff1caa1681fac09120eca307586e1a7
+ (+) 2fe1dab1780fbc19021eda206596f1b7
+ AES 8cb82807230e1321d3fae00d18cc2012
+
+ */
+ "8cb82807230e1321d3fae00d18cc2012"
+ );
+
+ $computed_ciphertext = self::PlainEncrypt($plaintext, $key, $iv);
+ if ($computed_ciphertext !== $ciphertext) {
+ throw new CryptoTestFailedException();
+ }
+
+ $computed_plaintext = self::PlainDecrypt($ciphertext, $key, $iv);
+ if ($computed_plaintext !== $plaintext) {
+ throw new CryptoTestFailedException();
+ }
+ }
+
+ /* WARNING: Do not call this function on secrets. It creates side channels. */
+ private static function hexToBytes($hex_string)
+ {
+ return pack("H*", $hex_string);
+ }
+
+ private static function EnsureConstantExists($name)
+ {
+ if (!defined($name)) {
+ throw new CannotPerformOperationException();
+ }
+ }
+
+ private static function EnsureFunctionExists($name)
+ {
+ if (!function_exists($name)) {
+ throw new CannotPerformOperationException();
+ }
+ }
+
+ /*
+ * We need these strlen() and substr() functions because when
+ * 'mbstring.func_overload' is set in php.ini, the standard strlen() and
+ * substr() are replaced by mb_strlen() and mb_substr().
+ */
+
+ private static function our_strlen($str)
+ {
+ if (function_exists('mb_strlen')) {
+ $length = mb_strlen($str, '8bit');
+ if ($length === FALSE) {
+ throw new CannotPerformOperationException();
+ }
+ return $length;
+ } else {
+ return strlen($str);
+ }
+ }
+
+ private static function our_substr($str, $start, $length = NULL)
+ {
+ if (function_exists('mb_substr'))
+ {
+ // mb_substr($str, 0, NULL, '8bit') returns an empty string on PHP
+ // 5.3, so we have to find the length ourselves.
+ if (!isset($length)) {
+ if ($start >= 0) {
+ $length = self::our_strlen($str) - $start;
+ } else {
+ $length = -$start;
+ }
+ }
+
+ return mb_substr($str, $start, $length, '8bit');
+ }
+
+ // Unlike mb_substr(), substr() doesn't accept NULL for length
+ if (isset($length)) {
+ return substr($str, $start, $length);
+ } else {
+ return substr($str, $start);
+ }
+ }
+
+}
+
+/*
+ * We want to catch all uncaught exceptions that come from the Crypto class,
+ * since by default, PHP will leak the key in the stack trace from an uncaught
+ * exception. This is a really ugly hack, but I think it's justified.
+ *
+ * Everything up to handler() getting called should be reliable, so this should
+ * reliably suppress the stack traces. The rest is just a bonus so that we don't
+ * make it impossible to debug other exceptions.
+ *
+ * This bit of code was adapted from: http://stackoverflow.com/a/7939492
+ */
+
+class CryptoExceptionHandler
+{
+ private $rethrow = NULL;
+
+ public function __construct()
+ {
+ set_exception_handler(array($this, "handler"));
+ }
+
+ public function handler($ex)
+ {
+ if (
+ $ex instanceof InvalidCiphertextException ||
+ $ex instanceof CannotPerformOperationException ||
+ $ex instanceof CryptoTestFailedException
+ ) {
+ echo "FATAL ERROR: Uncaught crypto exception. Suppresssing output.\n";
+ } else {
+ /* Re-throw the exception in the destructor. */
+ $this->rethrow = $ex;
+ }
+ }
+
+ public function __destruct() {
+ if ($this->rethrow) {
+ throw $this->rethrow;
+ }
+ }
+}
+
+$crypto_exception_handler_object_dont_touch_me = new CryptoExceptionHandler();
+
+++ /dev/null
-The MIT License (MIT)
-
-Copyright (c) 2016 Taylor Hornby <https://defuse.ca> and Paragon Initiative
-Enterprises <https://paragonie.com>.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
php-encryption
===============
-[](https://travis-ci.org/defuse/php-encryption)
-
-This is a library for encrypting data with a key or password in PHP. **It
-requires PHP 5.4 or newer.** The current version is v2.0.0, which is expected to
-remain stable and supported by its authors with security and bugfixes until at
-least January 1st, 2019.
+This is a class for doing symmetric encryption in PHP. **Requires PHP 5.4 or newer.**
-The library is a joint effort between [Taylor Hornby](https://defuse.ca/) and
-[Scott Arciszewski](https://paragonie.com/blog/author/scott-arcizewski) as well
-as numerous open-source contributors.
+[](https://travis-ci.org/defuse/php-encryption)
-What separates this library from other PHP encryption libraries is, firstly,
-that it is secure. The authors used to encounter insecure PHP encryption code on
-a daily basis, so they created this library to bring more security to the
-ecosystem. Secondly, this library is "difficult to misuse." Like
-[libsodium](https://github.com/jedisct1/libsodium), its API is designed to be
-easy to use in a secure way and hard to use in an insecure way.
+Implementation
+--------------
-Dependencies
-------------
+Messages are encrypted with AES-128 in CBC mode and are authenticated with
+HMAC-SHA256 (Encrypt-then-Mac). PKCS7 padding is used to pad the message to
+a multiple of the block size. 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.
-This library requres no special dependencies except for PHP 5.4 or newer with
-the OpenSSL extensions enabled (this is the default). It uses
-[random\_compat](https://github.com/paragonie/random_compat), which is bundled
-in with this library so that your users will not need to follow any special
-installation steps.
+Warning
+--------
-Getting Started
-----------------
+This is new code, and it hasn't received much review by experts. I have spent
+many hours making it as secure as possible (extensive runtime tests, secure
+coding practices), and auditing it for problems, but I may have missed some
+issues. So be careful. Don't trust it with your life. Check out the open GitHub
+issues for a list of known issues. If you find a problem with this library,
+please report it by opening a GitHub issue.
-Start with the [**Tutorial**](docs/Tutorial.md). You can find instructions for
-obtaining this library's code securely in the [Installing and
-Verifying](docs/InstallingAndVerifying.md) documentation.
+That said, you're probably much better off using this library than any other
+encryption library written in PHP.
-After you've read the tutorial and got the code, refer to the formal
-documentation for each of the classes this library provides:
+Philosophy
+-----------
-- [Crypto](docs/classes/Crypto.md)
-- [File](docs/classes/File.md)
-- [Key](docs/classes/Key.md)
-- [KeyProtectedByPassword](docs/classes/KeyProtectedByPassword.md)
+This library was created after noticing how much insecure PHP encryption code
+there is. I once did a Google search for "php encryption" and found insecure
+code or advice on 9 of the top 10 results.
-If you encounter difficulties, see the [FAQ](docs/FAQ.md) answers. The fixes to
-the most commonly-reported problems are explained there.
+Encryption is becoming an essential component of modern websites. This library
+aims to fulfil a subset of that need: Authenticated symmetric encryption of
+short strings, given a random key.
-If you're a cryptographer and want to understand the nitty-gritty details of how
-this library works, look at the [Cryptography Details](docs/CryptoDetails.md)
-documentation.
+This library is developed around several core values:
-If you're interested in contributing to this library, see the [Internal
-Developer Documentation](docs/InternalDeveloperDocs.md).
+- Rule #1: Security is prioritized over everything else.
-Examples
----------
+ > Whenever there is a conflict between security and some other property,
+ > security will be favored. For example, the library has runtime tests,
+ > which make it slower, but will hopefully stop it from encrypting stuff
+ > if the platform it's running on is broken.
-If the documentation is not enough for you to understand how to use this
-library, then you can look at an example project that uses this library:
+- Rule #2: It should be difficult to misuse the library.
-- [encutil](https://github.com/defuse/encutil)
-- [fileencrypt](https://github.com/tsusanka/fileencrypt)
+ > We assume the developers using this library have no experience with
+ > cryptography. We only assume that they know that the "key" is something
+ > you need to encrypt and decrypt the messages, and that it must be
+ > protected. Whenever possible, the library should refuse to encrypt or
+ > decrypt messages when it is not being used correctly.
-Security Audit Status
----------------------
+- Rule #3: The library aims only to be compatible with itself.
-This code has not been subjected to a formal, paid, security audit. However, it
-has received lots of review from members of the PHP security community, and the
-authors are experienced with cryptography. In all likelihood, you are safer
-using this library than almost any other encryption library for PHP.
+ > Other PHP encryption libraries try to support every possible type of
+ > encryption, even the insecure ones (e.g. ECB mode). Because there are so
+ > many options, inexperienced developers must make decisions between
+ > things like "CBC" mode and "ECB" mode, knowing nothing about either one,
+ > which inevitably creates vulnerabilities.
-If you use this library as a part of your business and would like to help fund
-a formal audit, please [contact Taylor Hornby](https://defuse.ca/contact.htm).
+ > This library will only support one secure mode. A developer using this
+ > library will call "encrypt" and "decrypt" not caring about how they are
+ > implemented.
-Public Keys
-------------
+- Rule #4: The library should consist of a single PHP file and nothing more.
-The GnuPG public key used to sign releases is available in
-[dist/signingkey.asc](https://github.com/defuse/php-encryption/raw/master/dist/signingkey.asc). Its fingerprint is:
+ > 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 one PHP file
+ > that you can put in your source tree and require().
-```
-2FA6 1D8D 99B9 2658 6BAC 3D53 385E E055 A129 1538
-```
+References:
-You can verify it against the Taylor Hornby's [contact
-page](https://defuse.ca/contact.htm) and
-[twitter](https://twitter.com/DefuseSec/status/723741424253059074).
+ [1] https://github.com/jedisct1/libsodium-php
--- /dev/null
+<?php
+
+require_once('Crypto.php');
+
+// Note: By default, the runtime tests are "cached" and not re-executed for
+// every call. To disable this, look at the RuntimeTest() function.
+
+$start = microtime(true);
+for ($i = 0; $i < 1000; $i++) {
+ $key = Crypto::CreateNewRandomKey();
+}
+$end = microtime(true);
+showResults("CreateNewRandomKey()", $start, $end, 1000);
+
+$start = microtime(true);
+for ($i = 0; $i < 100; $i++) {
+ $ciphertext = Crypto::Encrypt(
+ str_repeat("A", 1024*1024),
+ str_repeat("B", 16)
+ );
+}
+$end = microtime(true);
+showResults("Encrypt(1MB)", $start, $end, 100);
+
+$start = microtime(true);
+for ($i = 0; $i < 1000; $i++) {
+ $ciphertext = Crypto::Encrypt(
+ str_repeat("A", 1024),
+ str_repeat("B", 16)
+ );
+}
+$end = microtime(true);
+showResults("Encrypt(1KB)", $start, $end, 1000);
+
+function showResults($type, $start, $end, $count)
+{
+ $time = $end - $start;
+ $rate = $count / $time;
+ echo "$type: $rate calls/s\n";
+}
+
+?>
+++ /dev/null
-#!/usr/bin/env php
-<?php
-
-use Defuse\Crypto\Key;
-
-foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) {
- if (file_exists($file)) {
- require $file;
- break;
- }
-}
-
-$key = Key::createNewRandomKey();
-echo $key->saveToAsciiSafeString(), "\n";
"name": "defuse/php-encryption",
"description": "Secure PHP Encryption Library",
"license": "MIT",
- "keywords": ["security", "encryption", "AES", "openssl", "cipher", "cryptography", "symmetric key cryptography", "crypto", "encrypt", "authenticated encryption"],
+ "keywords": ["security", "encryption", "AES", "mcrypt", "cipher"],
"authors": [
{
"name": "Taylor Hornby",
- "email": "taylor@defuse.ca",
- "homepage": "https://defuse.ca/"
- },
- {
- "name": "Scott Arciszewski",
- "email": "info@paragonie.com",
- "homepage": "https://paragonie.com"
+ "email": "havoc@defuse.ca"
}
],
"autoload": {
- "psr-4": {
- "Defuse\\Crypto\\": "src"
- }
+ "files": ["Crypto.php"]
},
"require": {
- "paragonie/random_compat": "~2.0",
+ "php": ">=5.4.0",
"ext-openssl": "*",
- "php": ">=5.4.0"
- },
- "require-dev": {
- "phpunit/phpunit": "^4|^5",
- "nikic/php-parser": "^2.0|^3.0"
- },
- "bin": [
- "bin/generate-defuse-key"
- ]
+ "ext-mcrypt": "*"
+ }
}
+++ /dev/null
-# This builds defuse-crypto.phar. To run this Makefile, `box` and `composer`
-# must be installed and in your $PATH. Run it from inside the dist/ directory.
-
-box := $(shell which box)
-composer := "composer"
-
-.PHONY: all
-all: build-phar
-
-.PHONY: sign-phar
-sign-phar:
- gpg -u 7B4B2D98 --armor --output defuse-crypto.phar.sig --detach-sig defuse-crypto.phar
-
-# ensure we run in clean tree. export git tree and run there.
-.PHONY: build-phar
-build-phar:
- @echo "Creating .phar from revision $(shell git rev-parse HEAD)."
- rm -rf worktree
- install -d worktree
- (cd $(CURDIR)/..; git archive HEAD) | tar -x -C worktree
- $(MAKE) -f $(CURDIR)/Makefile -C worktree defuse-crypto.phar
- mv worktree/*.phar .
- rm -rf worktree
-
-.PHONY: clean
-clean:
- rm -vf defuse-crypto.phar defuse-crypto.phar.sig
-
-# Inside workdir/:
-
-defuse-crypto.phar: dist/box.json composer.lock
- cp dist/box.json .
- php -d phar.readonly=0 $(box) build -c box.json -v
-
-composer.lock:
- $(composer) install --no-dev
-
+++ /dev/null
-{
- "chmod": "0755",
- "finder": [
- {
- "in": "src",
- "name": "*.php"
- },
- {
- "in": "vendor/composer",
- "name": "*.php"
- },
- {
- "in": "vendor/paragonie",
- "name": "*.php",
- "exclude": "other"
- }
- ],
- "compactors": [
- "Herrera\\Box\\Compactor\\Php"
- ],
- "main": "vendor/autoload.php",
- "output": "defuse-crypto.phar",
- "shebang": false,
- "stub": true
-}
+++ /dev/null
------BEGIN PGP PUBLIC KEY BLOCK-----
-Version: GnuPG v2
-
-mQINBFarvO4BEACdQBaLt6SUBx1cB5liUu1qo+YwVLh9bxTregQtmEREMdTVqXYt
-e5b79uL4pQp2GlKHcEyRURS+6rIIruM0oh9ZYGTJYPAkCDzJxaU2awZeFbfBvpCm
-iF66/O4ZJI4mlT8dFKmxBJxDhfeOR2UmmhDiEsJK9FxBKUzvo/dWrX2pBzf8Y122
-iIaVraSo+tymaf7vriaIf/NnSKhDw8dtQYGM4NMrxxsPTfbCF8XiboDgTkoD2A+6
-NpOJYxA4Veedsf2TP9YLhljH4m5yYlfjjqBzbBCPWuE6Hhy5Xze9mncgDr7LKenm
-Ctf2NxW6y4O3RCI+9eLlBfFWB+KuGV87/b5daetX7NNLbjID8z2rqEa+d6wu5xA5
-Ta2uiVkAOEovr3XnkayZ9zth+Za7w7Ai0ln0N/LVMkM+Gu4z/pJv6HjmTGDM2wJb
-fs+UOM0TFdg+N81Do67XT2M4o0MeHyUqsIiWpYa2Qf1PNmqTQNJnRk8uZZ9I96Nh
-eCgNuCbhsQiYBMicox+xmuWAlGAfA06y0kCtmqGhiBGArdJlWvUqPqGiZ4Hln9z0
-FJmXDOh0Q/FIPxcDg8mKRRbx+lOP389PLsPpj4b2B/4PEgfpCCOwuKpLotATZxC1
-9JwFk0Y/cvUUkq4a+nAJBNtBbtRJkEesuuUnRq6XexmnE3uUucDcV0XCSwARAQAB
-tCBUYXlsb3IgSG9ybmJ5IDx0YXlsb3JAZGVmdXNlLmNhPokCPQQTAQgAJwUCVqu8
-7gIbAwUJB4TOAAULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRA4XuBVoSkVOJbx
-EACG0F9blPMAsK05EWyNnnS4mw25zPfbaqqEvYbquAeM0nBpRDm7sRn2MNR0AL4g
-7XrtxE/4qYkdEl6f2wFCQeRhZgxE3w22llredzLme11Hic8hn4i7ysdIw0r9dMMR
-kjgR5UcWpv8iU847czyK09PkKW2EaLRbX2qbA7rNU5qCFKeD4Sy4bBTteISeVsHo
-Vr9o1/bRrMhgZ++ts8hYf0LmujIf5cxp+qcdKwCXSnS/gmmXaKRMCPv/Wdlq9bt6
-LX9jZB9lXBdGxcBJeFOsTG+QRDiVjg3d6i3o3TAKV87ALBI4v2ADEYtN8lviHo3/
-SovVKv6zrUsZHxhoGiLTiksNrYsKMmiMxdJCoOazmtUPnZ4UOtT8NdqMPoKvdoRz
-f4rhZ+f5jSVD9OuX2PDmfyq21Rdiym7Vcgr+uTIFJ3ShRHjWb/ytCwoB2FeGY6+G
-AKY58bTQvUIqEJvSov/+TAqZ4BfOuSdTLcHglV1OdUu2SFZvU2gmyVp0l5elGv5t
-FyUlBJUkQT9MtvsdLOR7vQi8QapV+9LWpqwvaj9hyEJz848DQ2sdYTphUytFHv7H
-k58DAtVhTrVjHyeefjiYtMl6vSAgTjy5LWAUpo5TfhdGrAi0Tdd/GD7amHoWoDy8
-EKXKq2xPLo3JOdkWYQUi5NErzEskfsSzpCOgyDJmGetWK7kCDQRWq7zuARAAu7/i
-cm8cjgLhHEX/bgfwOT2hLOLSjjve0O8YFSuJO9XqIHXqmfVOrqWtfG0Mh4bwlfqc
-MAvBfF5NSSPfAE4ftBAQ1e5jEv8hJeqICpq3IHTFX4etBs49NfNkyveQl/amVTu1
-+/O5J4CuIcsEf3y0Xuu38n7EB3SfMQCWLcOR1NyZoX3bI+CGRpOVVoFse3ljSWL4
-LhLVl0WiEMXULsussEoN+c6x9KCyAi/jFOrxrTrFC//sZesKj6KucoqKGfwMWrrv
-IeRT9Ga8Wn5MJnQu0aWg+zVVYqTedXZLNLODgQIInFnXO0seBXy15yDok1y5bkx2
-sinKg4+mueYaGUpoUti0hM3J3yaC34i6Cwa8MQoLNw1JIS/oNtKjpMxyV10w8aoc
-PHRK3n7UYp10mJHx7aM+lldSKvVS1NTQmI4vloNLwMp324H5ANDFAlRUz7mysVnu
-DEEvigPSPxs5ZYENu/i7pCQC5qHfhrlBrQwTjhegr0pQPcumy2fO5SGC9l/5B7ev
-bqQSZmDeWWoTvh2w2wl5/RWAsgZKx6rDtkCqYx7sSBY17uorrxP24LP4zhq7NxRV
-nfdsLogbCFNVQ66u7qvq5zFccdFtg9h1HQWdS7wbnKSBGZoo5gl6js7GGtxfGbb0
-oQ9kp6eciF4U92r6POhVgbRe4CfPo50nqgZBddkAEQEAAYkCJQQYAQgADwUCVqu8
-7gIbDAUJB4TOAAAKCRA4XuBVoSkVOFJ8D/9J8IJ4XWUU3FYIaHJ3XeSoxDmTi7d5
-WmNdf1lmwz82MQjG4uw17oCbvQzmj4/a/CM1Ly4v0WwBhUf9aiNErD0ByHASFnuc
-tlQBLVJdk0vRyD0fZakGg64qCA76hiySjMhlGHkQFyP2mDORc2GNu/OqFGm79pXT
-ZUplXxd431E603/agM5xJrweutMMpP1nBFTSEMJvbMNzDVN8I1A1CH4zVmAVxOUk
-sQ5L5rXW+KeXWyiMF24+l2CMnkQ2CxfHpkcpfPJs1Cbt+TIBSSofIqK8QJXrb/2f
-Zpl/ftqW7Xe86rJFrB/Y/77LDWx10rqWEvfCqrBxrMj7ONAQfbKQF/IjAwDN17Wf
-1K74rqKnRu+KHCyNM89s1iDbQC9kzZfzYt4AEOvAH/ZQDMZffzPSbnfkBerExFpa
-93XMuiR66jiBsf9IXIQeydpJD4Ogl2sSUSxFEJxJ/bBSxPxC5w7/BVMA7Am1y8Zo
-3hrpqnX2PBzxG7L0FZ6fYkfR3p8JS7vI6nByBf2IDv8W32wn43olPf+u6uobHLvt
-ttapOjwPAhPDalRuxs9U6WSg06QJkT/0F8TFUPWpsFmKTl+G4Ty7PHWsjeeNHJCL
-7/5RQboFY3k8Jy3/sIofABO6Un9LJivDuu9PxqA0IgvaS6Mja8JdCCk9Nyk4vHB7
-IEgAL/CYqrk38w==
-=lmD7
------END PGP PUBLIC KEY BLOCK-----
+++ /dev/null
-Cryptography Details
-=====================
-
-Here is a high-level description of how this library works. Any discrepancy
-between this documentation and the actual implementation will be considered
-a security bug.
-
-Let's start with the following definitions:
-
-- HKDF-SHA256(*k*, *n*, *info*, *s*) is the key derivation function specified in
- RFC 5869 (using the SHA256 hash function). The parameters are:
- - *k*: The initial keying material.
- - *n*: The number of output bytes.
- - *info*: The info string.
- - *s*: The salt.
-- AES-256-CTR(*m*, *k*, *iv*) is AES-256 encryption in CTR mode. The parameters
- are:
- - *m*: An arbitrary-length (possibly zero-length) message.
- - *k*: A 32-byte key.
- - *iv*: A 16-byte initialization vector (nonce).
-- PBKDF2-SHA256(*p*, *s*, *i*, *n*) is the password-based key derivation
- function defined in RFC 2898 (using the SHA256 hash function). The parameters
- are:
- - *p*: The password string.
- - *s*: The salt string.
- - *i*: The iteration count.
- - *n*: The output length in bytes.
-- VERSION is the string `"\xDE\xF5\x02\x00"`.
-- AUTHINFO is the string `"DefusePHP|V2|KeyForAuthentication"`.
-- ENCRINFO is the string `"DefusePHP|V2|KeyForEncryption"`.
-
-To encrypt a message *m* using a 32-byte key *k*, the following steps are taken:
-
-1. Generate a random 32-byte string *salt*.
-2. Derive the 32-byte authentication key *akey* = HKDF-SHA256(*k*, 32, AUTHINFO, *salt*).
-3. Derive the 32-byte encryption key *ekey* = HKDF-SHA256(*k*, 32, ENCRINFO, *salt*).
-4. Generate a random 16-byte initialization vector *iv*.
-5. Compute *c* = AES-256-CTR(*m*, *ekey*, *iv*).
-6. Combine *ctxt* = VERSION || *salt* || *iv* || *c*.
-7. Compute *h* = HMAC-SHA256(*ctxt*, *akey*).
-8. Output *ctxt* || *h*.
-
-Decryption is roughly the reverse process (see the code for details, since the
-security of the decryption routine is highly implementation-dependent).
-
-For encryption using a password *p*, steps 1-3 above are replaced by:
-
-1. Generate a random 32-byte string *salt*.
-2. Compute *k* = PBKDF2-SHA256(SHA256(*p*), *salt*, 100000, 32).
-3. Derive the 32-byte authentication key *akey* = HKDF-SHA256(*k*, 32, AUTHINFO, *salt*)
-4. Derive the 32-byte encryption key *ekey* = HKDF-SHA256(*k*, 32, ENCRINFO, *salt*)
-
-The remainder of the process is the same. Notice the reuse of the same *salt*
-for PBKDF2-SHA256 and HKDF-SHA256. The prehashing of the password in step 2 is
-done to prevent a [DoS attack using long
-passwords](https://github.com/defuse/php-encryption/issues/230).
-
-For `KeyProtectedByPassword`, the serialized key is encrypted according to the
-password encryption defined above. However, the actual password used for
-encryption is the SHA256 hash of the password the user provided. This is done in
-order to provide domain separation between the message encryption in the user's
-application and the internal key encryption done by this library. It fixes
-a [key replacement chosen-protocol
-attack](https://github.com/defuse/php-encryption/issues/240).
+++ /dev/null
-Frequently Asked Questions
-===========================
-
-How do I use this library to encrypt passwords?
-------------------------------------------------
-
-Passwords should not be encrypted, they should be hashed with a *slow* password
-hashing function that's designed to slow down password guessing attacks. See
-[How to Safely Store Your Users' Passwords in
-2016](https://paragonie.com/blog/2016/02/how-safely-store-password-in-2016).
-
-How do I give it the same key every time instead of a new random key?
-----------------------------------------------------------------------
-
-A `Key` object can be saved to a string by calling its `saveToAsciiSafeString()`
-method. You will have to save that string somewhere safe, and then load it back
-into a `Key` object using `Key`'s `loadFromAsciiSafeString` static method.
-
-Where you store the string depends on your application. For example if you are
-using `KeyProtectedByPassword` to encrypt files with a user's login password,
-then you should not store the `Key` at all. If you are protecting sensitive data
-on a server that may be compromised, then you should store it in a hardware
-security module. When in doubt, consult a security expert.
-
-Why is an EnvironmentIsBrokenException getting thrown?
--------------------------------------------------------
-
-Either you've encountered a bug in this library, or your system doesn't support
-the use of this library. For example, if your system does not have a secure
-random number generator, this library will refuse to run, by throwing that
-exception, instead of falling back to an insecure random number generator.
-
-Why am I getting a BadFormatException when loading a Key from a string?
-------------------------------------------------------------------------
-
-If you're getting this exception, then the string you're giving to
-`loadFromAsciiSafeString()` is *not* the same as the string you got from
-`saveToAsciiSafeString()`. Perhaps your database column isn't wide enough and
-it's truncating the string as you insert it?
+++ /dev/null
-Getting The Code
-=================
-
-There are two ways to use this library in your applications. You can either:
-
-1. Use [Composer](https://getcomposer.org/), or
-2. `require_once` a single `.phar` file in your application.
-
-If you are not using either option (for example, because you're using Git submodules), you may need to write your own autoloader ([example](https://gist.github.com/paragonie-scott/949daee819bb7f19c50e5e103170b400)).
-
-Option 1: Using Composer
--------------------------
-
-Run this inside the directory of your composer-enabled project:
-
-```sh
-composer require defuse/php-encryption
-```
-
-Unfortunately, composer doesn't provide a way for you to verify that the code
-you're getting was signed by this library's authors. If you want a more secure
-option, use the `.phar` method described below.
-
-Option 2: Including a PHAR
-----------------------------
-
-The `.phar` option lets you include this library into your project simply by
-calling `require_once` on a single file. Download `defuse-crypto.phar` and
-`defuse-crypto.phar.sig` from this project's
-[releases](https://github.com/defuse/php-encryption/releases) page.
-
-You should verify the integrity of the `.phar`. The `defuse-crypto.phar.sig`
-contains the signature of `defuse-crypto.phar`. It is signed with Taylor
-Hornby's PGP key. You can find Taylor's public key in `dist/signingkey.asc`. You
-can verify the public key's fingerprint against the Taylor Hornby's [contact
-page](https://defuse.ca/contact.htm) and
-[twitter](https://twitter.com/DefuseSec/status/723741424253059074).
-
-Once you have verified the signature, it is safe to use the `.phar`. Place it
-somewhere in your file system, e.g. `/var/www/lib/defuse-crypto.phar`, and then
-pass that path to `require_once`.
-
-```php
-<?php
-
- require_once('/var/www/lib/defuse-crypto.phar');
-
- // ... the Crypto, File, Key, and KeyProtectedByPassword classes are now
- // available ...
-
- // ...
-```
-
+++ /dev/null
-Information for the Developers of php-encryption
-=================================================
-
-Status
--------
-
-This library is currently frozen under a long-term support release. We do not
-plan to add any new features. We will maintain the library by fixing any bugs
-that are reported, or security vulnerabilities that are found.
-
-Development Environment
-------------------------
-
-Development is done on Linux. To run the tests, you will need to have the
-following tools installed:
-
-- `php` (with OpenSSL enabled, if you're compiling from source).
-- `gpg`
-- `composer`
-
-Running the Tests
-------------------
-
-First do `composer install` and then you can run the tests by running
-`./test.sh`. This will download a PHPUnit PHAR, verify its cryptographic
-signatures, and then use it to run the tests in `test/unit`.
-
-Getting and Using Psalm
------------------------
-
-[Psalm](https://github.com/vimeo/psalm) is a static analysis suite for PHP projects.
-We use Psalm to ensure type safety throughout our library.
-
-To install Psalm, you just need to run one command:
-
- composer require --dev "vimeo/psalm:dev-master"
-
-To verify that your code changes are still strictly type-safe, run the following
-command:
-
- vendor/bin/psalm
-
-Reporting Bugs
----------------
-
-Please report bugs, even critical security vulnerabilities, by opening an issue
-on GitHub. We recommend disclosing security vulnerabilities found in this
-library *publicly* as soon as possible.
-
-Philosophy
------------
-
-This library is developed around several core values:
-
-- Rule #1: Security is prioritized over everything else.
-
- > Whenever there is a conflict between security and some other property,
- > security will be favored. For example, the library has runtime tests,
- > which make it slower, but will hopefully stop it from encrypting stuff
- > if the platform it's running on is broken.
-
-- Rule #2: It should be difficult to misuse the library.
-
- > We assume the developers using this library have no experience with
- > cryptography. We only assume that they know that the "key" is something
- > you need to encrypt and decrypt the messages, and that it must be kept
- > secret. Whenever possible, the library should refuse to encrypt or decrypt
- > messages when it is not being used correctly.
-
-- Rule #3: The library aims only to be compatible with itself.
-
- > Other PHP encryption libraries try to support every possible type of
- > encryption, even the insecure ones (e.g. ECB mode). Because there are so
- > many options, inexperienced developers must decide whether to use "CBC
- > mode" or "ECB mode" when both are meaningless terms to them. This
- > inevitably leads to vulnerabilities.
-
- > This library will only support one secure mode. A developer using this
- > library will call "encrypt" and "decrypt" methods without worrying about
- > how they are implemented.
-
-- Rule #4: The library should require no special installation.
-
- > Some PHP encryption libraries, like 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().
-
-Publishing Releases
---------------------
-
-To make a release, you will need to install [composer](https://getcomposer.org/)
-and [box](https://github.com/box-project/box2) on your system. They will need to
-be available in your `$PATH` so that running the commands `composer` and `box`
-in your terminal run them, respectively. You will also need the private key for
-signing (ID: 7B4B2D98) available.
-
-Once you have those tools installed and the key available follow these steps:
-
-**Remember to set the version number in `composer.json`!**
-
-Make a fresh clone of the repository:
-
-```
-git clone <url>
-```
-
-Check out the branch you want to release:
-
-```
-git checkout <branchname>
-```
-
-Check that the version number in composer.json is correct:
-
-```
-cat composer.json
-```
-
-Run the tests:
-
-```
-composer install
-./test.sh
-```
-
-Generate the `.phar`:
-
-```
-cd dist
-make build-phar
-```
-
-Test the `.phar`:
-
-```
-cd ../
-./test.sh dist/defuse-crypto.phar
-```
-
-Sign the `.phar`:
-
-```
-cd dist
-make sign-phar
-```
-
-Tag the release:
-
-```
-git -c user.signingkey=7B4B2D98 tag -s "<TAG NAME>" -m "<TAG MESSAGE>"
-```
-
-`<TAG NAME>` should be in the format `v2.0.0` and `<TAG MESSAGE>` should look
-like "Release of v2.0.0."
-
-Push the tag to github, then use the
-[releases](https://github.com/defuse/php-encryption/releases) page to draft
-a new release for that tag. Upload the `.phar` and the `.phar.sig` file to be
-included as part of that release.
+++ /dev/null
-Tutorial
-=========
-
-Hello! If you're reading this file, it's because you want to add encryption to
-one of your PHP projects. My job, as the person writing this documentation, is
-to help you make sure you're doing the right thing and then show you how to use
-this library to do it. To help me help you, please read the documentation
-*carefully* and *deliberately*.
-
-A Word of Caution
-------------------
-
-Encryption is not magic dust you can sprinkle on a system to make it more
-secure. The way encryption is integrated into a system's design needs to be
-carefully thought out. Sometimes, encryption is the wrong thing to use. Other
-times, encryption needs to be used in a very specific way in order for it to
-work as intended. Even if you are sure of what you are doing, we strongly
-recommend seeking advice from an expert.
-
-The first step is to think about your application's threat model. Ask yourself
-the following questions. Who will want to attack my application, and what will
-they get out of it? Are they trying to steal some information? Trying to alter
-or destroy some information? Or just trying to make the system go down so people
-can't access it? Then ask yourself how encryption can help combat those threats.
-If you're going to add encryption to your application, you should have a very
-clear idea of exactly which kinds of attacks it's helping to secure your
-application against. Once you have your threat model, think about what kinds of
-attacks it *does not* cover, and whether or not you should improve your threat
-model to include those attacks.
-
-**This isn't for storing user login passwords:** The most common use of
-cryptography in web applications is to protect the users' login passwords. If
-you're trying to use this library to "encrypt" your users' passwords, you're in
-the wrong place. Passwords shouldn't be *encrypted*, they should be *hashed*
-with a slow computation-heavy function that makes password guessing attacks more
-expensive. See [How to Safely Store Your Users' Passwords in
-2016](https://paragonie.com/blog/2016/02/how-safely-store-password-in-2016).
-
-**This isn't for encrypting network communication:** Likewise, if you're trying
-to encrypt messages sent between two parties over the Internet, you don't want
-to be using this library. For that, set up a TLS connection between the two
-points, or, if it's a chat app, use the [Signal
-Protocol](https://whispersystems.org/blog/advanced-ratcheting/).
-
-What this library provides is symmetric encryption for "data at rest." This
-means it is not suitable for use in building protocols where "data is in motion"
-(i.e. moving over a network) except in limited set of cases.
-
-Getting the Code
------------------
-
-There are several different ways to obtain this library's code and to add it to
-your project. Even if you've already cloned the code from GitHub, you should
-take steps to verify the cryptographic signatures to make sure the code you got
-was not intercepted and modified by an attacker.
-
-Please head over to the [**Installing and
-Verifying**](InstallingAndVerifying.md) documentation to get the code, and then
-come back here to continue the tutorial.
-
-Using the Library
-------------------
-
-I'm going to assume you know what symmetric encryption is, and the difference
-between symmetric and asymmetric encryption. If you don't, I recommend taking
-[Dan Boneh's Cryptography I course](https://www.coursera.org/learn/crypto/) on
-Coursera.
-
-To give you a quick introduction to the library, I'm going to explain how it
-would be used in two sterotypical scenarios. Hopefully, one of these sterotypes
-is close enough to what you want to do that you'll be able to figure out what
-needs to be different on your own.
-
-### Formal Documentation
-
-While this tutorial should get you up and running fast, it's important to
-understand how this library behaves. Please make sure to read the formal
-documentation of all of the functions you're using, since there are some
-important security warnings there.
-
-The following classes are available for you to use:
-
-- [Crypto](classes/Crypto.md): Encrypting and decrypting strings.
-- [File](classes/File.md): Encrypting and decrypting files.
-- [Key](classes/Key.md): Represents a secret encryption key.
-- [KeyProtectedByPassword](classes/KeyProtectedByPassword.md): Represents
- a secret encryption key that needs to be "unlocked" by a password before it
- can be used.
-
-### Scenario #1: Keep data secret from the database administrator
-
-In this scenario, our threat model is as follows. Alice is a server
-administrator responsible for managing a trusted web server. Eve is a database
-administrator responsible for managing a database server. Dave is a web
-developer working on code that will eventually run on the trusted web server.
-
-Let's say Alice and Dave trust each other, and Alice is going to host Dave's
-application on her server. But both Alice and Dave don't trust Eve. They know
-Eve is a good database administrator, but she might have incentive to steal the
-data from the database. They want to keep some of the web application's data
-secret from Eve.
-
-In order to do that, Alice will use the included `generate-defuse-key` script
-which generates a random encryption key and prints it to standard output:
-
-```sh
-$ composer require defuse/php-encryption
-$ vendor/bin/generate-defuse-key
-```
-
-Alice will run this script once and save the output to a configuration file, say
-in `/etc/daveapp-secret-key.txt` and set the file permissions so that only the
-user that the website PHP scripts run as can access it.
-
-Dave will write his code to load the key from the configuration file:
-
-```php
-<?php
-use Defuse\Crypto\Key;
-
-function loadEncryptionKeyFromConfig()
-{
- $keyAscii = // ... load the contents of /etc/daveapp-secret-key.txt
- return Key::loadFromAsciiSafeString($keyAscii);
-}
-```
-
-Then, whenever Dave wants to save a secret value to the database, he will first
-encrypt it:
-
-```php
-<?php
-use Defuse\Crypto\Crypto;
-
-// ...
-$key = loadEncryptionKeyFromConfig();
-// ...
-$ciphertext = Crypto::encrypt($secret_data, $key);
-// ... save $ciphertext into the database ...
-```
-
-Whenever Dave wants to get the value back from the database, he must decrypt it
-using the same key:
-
-```php
-<?php
-use Defuse\Crypto\Crypto;
-
-// ...
-$key = loadEncryptionKeyFromConfig();
-// ...
-$ciphertext = // ... load $ciphertext from the database
-try {
- $secret_data = Crypto::decrypt($ciphertext, $key);
-} catch (\Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
- // An attack! Either the wrong key was loaded, or the ciphertext has
- // changed since it was created -- either corrupted in the database or
- // intentionally modified by Eve trying to carry out an attack.
-
- // ... handle this case in a way that's suitable to your application ...
-}
-```
-
-Note that if anyone ever steals the key from Alice's server, they can decrypt
-all of the ciphertexts that are stored in the database. As part of our threat
-model, we are assuming Alice's server administration skills and Dave's secure
-coding skills are good enough to stop Eve from being able to steal the key.
-Under those assumptions, this solution will prevent Eve from seeing data that's
-stored in the database.
-
-However, notice that our threat model says nothing about what could happen if
-Eve wants to *modify* the data. With this solution, Eve will not be able to
-alter any individual ciphertext (because each ciphertext has its own
-cryptographic integrity check), but Eve *will* be able to swap ciphertexts for
-one another, and revert ciphertexts to what they used to be at previous times.
-If we needed to defend against such attacks, we would have to re-design our
-threat model and come up with a different solution.
-
-### Scenario #2: Encrypting account data with the user's login password
-
-This scenario is like Scenario 1, but subtly different. The threat model is as
-follows. We have Alice, a server administrator, and Dave, the developer. Alice
-and Dave trust each other, and Alice wants to host Dave's web application,
-including its database, on her server. Alice is worried about her server getting
-hacked. The application will store the users' credit card numbers, and Alice
-wants to protect them in case the server gets hacked.
-
-We can model the situation like this: after the server gets hacked, the attacker
-will have read and write access to all data on it until the attack is detected
-and Alice rebuilds the server. We'll call the time the attacker has access to
-the server the *exposure window.* One idea to minimize loss is to encrypt the
-users' credit card numbers using a key made from their login password. Then, as
-long as the users all have strong passwords, and they are never logged in during
-the exposure window, their credit cards will be protected from the attacker.
-
-To implement this, Dave will use the `KeyProtectedByPassword` class. When a new
-user account is created, Dave will save a new key to their account, one that's
-protected by their login password:
-
-```php
-<?php
-use Defuse\Crypto\KeyProtectedByPassword;
-
-function CreateUserAccount($username, $password)
-{
- // ... other user account creation stuff, including password hashing
-
- $protected_key = KeyProtectedByPassword::createRandomPasswordProtectedKey($password);
- $protected_key_encoded = $protected_key->saveToAsciiSafeString();
- // ... save $protected_key_encoded into the user's account record
-}
-```
-
-Then, when the user logs in, Dave's code will load the protected key from the
-user's account record, unlock it to get a `Key` object, and save the `Key`
-object somewhere safe (like temporary memory-backed session storage). Note that
-wherever Dave's code saves the key, it must be destroyed once the user logs out,
-or else the attacker might be able to find users' keys even if they were never
-logged in during the attack.
-
-```php
-<?php
-use Defuse\Crypto\KeyProtectedByPassword;
-
-// ... authenticate the user using a good password hashing scheme
-// keep the user's password in $password
-
-$protected_key_encoded = // ... load it from the user's account record
-$protected_key = KeyProtectedByPassword::loadFromAsciiSafeString($protected_key_encoded);
-$user_key = $protected_key->unlockKey($password);
-$user_key_encoded = $user_key->saveToAsciiSafeString();
-// ... save $user_key_encoded in the session
-```
-
-```php
-<?php
-// ... when the user is logging out ...
-// ... securely wipe the saved $user_key_encoded from the system ...
-```
-
-When a user adds their credit card number, Dave's code will get the key from the
-session and use it to encrypt the credit card number:
-
-```php
-<?php
-use Defuse\Crypto\Crypto;
-use Defuse\Crypto\Key;
-
-// ...
-
-$user_key_encoded = // ... get it out of the session ...
-$user_key = Key::loadFromAsciiSafeString($user_key_encoded);
-
-// ...
-
-$credit_card_number = // ... get credit card number from the user
-$encrypted_card_number = Crypto::encrypt($credit_card_number, $user_key);
-// ... save $encrypted_card_number in the database
-```
-
-When the application needs to use the credit card number, it will decrypt it:
-
-```php
-<?php
-use Defuse\Crypto\Crypto;
-use Defuse\Crypto\Key;
-
-// ...
-
-$user_key_encoded = // ... get it out of the session
-$user_key = Key::loadFromAsciiSafeString($user_key_encoded);
-
-// ...
-
-$encrypted_card_number = // ... load it from the database ...
-try {
- $credit_card_number = Crypto::decrypt($encrypted_card_number, $user_key);
-} catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
- // Either there's a bug in our code, we're trying to decrypt with the
- // wrong key, or the encrypted credit card number was corrupted in the
- // database.
-
- // ... handle this case ...
-}
-```
-
-With all caveats carefully heeded, this solution limits credit card number
-exposure in the case where Alice's server gets hacked for a short amount of
-time. Remember to think about the attacks that *aren't* included in our threat
-model. The attacker is still free to do all sorts of harmful things like
-modifying the server's data which may go undetected if Alice doesn't have secure
-backups to compare against.
-
-Getting Help
--------------
-
-If you're having difficulty using the library, see if your problem is already
-solved by an answer in the [FAQ](FAQ.md).
+++ /dev/null
-Upgrading From Version 1.2
-===========================
-
-With version 2.0.0 of this library came major changes to the ciphertext format,
-algorithms used for encryption, and API.
-
-In version 1.2, keys were represented by 16-byte string variables. In version
-2.0.0, keys are represented by objects, instances of the `Key` class. This
-change was made in order to make it harder to misuse the API. For example, in
-version 1.2, you could pass in *any* 16-byte string, but in version 2.0.0 you
-need a `Key` object, which you can only get if you're "doing the right thing."
-
-This means that for all of your old version 1.2 keys, you'll have to:
-
-1. Generate a new version 2.0.0 key.
-2. For all of the ciphertexts encrypted under the old key:
- 1. Decrypt the ciphertext using the old version 1.2 key.
- 2. Re-encrypt it using the new version 2.0.0 key.
-
-Use the special `Crypto::legacyDecrypt()` method to decrypt the old ciphertexts
-using the old key and then re-encrypt them using `Crypto::encrypt()` with the
-new key. Your code will look something like the following. To avoid data loss,
-securely back up your keys and data before running your upgrade code.
-
-```php
-<?php
-
- // ...
-
- $legacy_ciphertext = // ... get the ciphertext you want to upgrade ...
- $legacy_key = // ... get the key to decrypt this ciphertext ...
-
- // Generate the new key that we'll re-encrypt the ciphertext with.
- $new_key = Key::createNewRandomKey();
- // ... save it somewhere ...
-
- // Decrypt it.
- try {
- $plaintext = Crypto::legacyDecrypt($legacy_ciphertext, $legacy_key);
- } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex)
- {
- // ... TODO: handle this case appropriately ...
- }
-
- // Re-encrypt it.
- $new_ciphertext = Crypto::encrypt($plaintext, $new_key);
-
- // ... replace the old $legacy_ciphertext with the new $new_ciphertext
-
- // ...
-```
+++ /dev/null
-Class: Defuse\Crypto\Crypto
-============================
-
-The `Crypto` class provides encryption and decryption of strings either using
-a secret key or secret password. For encryption and decryption of large files,
-see the `File` class.
-
-This code for this class is in `src/Crypto.php`.
-
-Instance Methods
------------------
-
-This class has no instance methods.
-
-Static Methods
----------------
-
-### Crypto::encrypt($plaintext, Key $key, $raw\_binary = false)
-
-**Description:**
-
-Encrypts a plaintext string using a secret key.
-
-**Parameters:**
-
-1. `$plaintext` is the string to encrypt.
-2. `$key` is an instance of `Key` containing the secret key for encryption.
-3. `$raw_binary` determines whether the output will be a byte string (true) or
- hex encoded (false, the default).
-
-**Return value:**
-
-Returns a ciphertext string representing `$plaintext` encrypted with the key
-`$key`. Knowledge of `$key` is required in order to decrypt the ciphertext and
-recover the plaintext.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-**Side-effects and performance:**
-
-This method runs a small and very fast set of self-tests if it is the very first
-time one of the `Crypto` methods has been called. The performance overhead is
-negligible and can be safely ignored in all applications.
-
-**Cautions:**
-
-The ciphertext returned by this method is decryptable by anyone with knowledge
-of the key `$key`. It is the caller's responsibility to keep `$key` secret.
-Where `$key` should be stored is up to the caller and depends on the threat
-model the caller is designing their application under. If you are unsure where
-to store `$key`, consult with a professional cryptographer to get help designing
-your application.
-
-### Crypto::decrypt($ciphertext, Key $key, $raw\_binary = false)
-
-**Description:**
-
-Decrypts a ciphertext string using a secret key.
-
-**Parameters:**
-
-1. `$ciphertext` is the ciphertext to be decrypted.
-2. `$key` is an instance of `Key` containing the secret key for decryption.
-3. `$raw_binary` must have the same value as the `$raw_binary` given to the
- call to `encrypt()` that generated `$ciphertext`.
-
-**Return value:**
-
-If the decryption succeeds, returns a string containing the same value as the
-string that was passed to `encrypt()` when `$ciphertext` was produced. Upon
-a successful return, the caller can be assured that `$ciphertext` could not have
-been produced except by someone with knowledge of `$key`.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-- `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
- the `$key` is not the correct key for the given ciphertext, or if the
- ciphertext has been modified (possibly maliciously). There is no way to
- distinguish between these two cases.
-
-**Side-effects and performance:**
-
-This method runs a small and very fast set of self-tests if it is the very first
-time one of the `Crypto` methods has been called. The performance overhead is
-negligible and can be safely ignored in all applications.
-
-**Cautions:**
-
-It is impossible in principle to distinguish between the case where you attempt
-to decrypt with the wrong key and the case where you attempt to decrypt
-a modified (corrupted) ciphertext. It is up to the caller how to best deal with
-this ambiguity, as it depends on the application this library is being used in.
-If in doubt, consult with a professional cryptographer.
-
-### Crypto::encryptWithPassword($plaintext, $password, $raw\_binary = false)
-
-**Description:**
-
-Encrypts a plaintext string using a secret password.
-
-**Parameters:**
-
-1. `$plaintext` is the string to encrypt.
-2. `$password` is a string containing the secret password used for encryption.
-3. `$raw_binary` determines whether the output will be a byte string (true) or
- hex encoded (false, the default).
-
-**Return value:**
-
-Returns a ciphertext string representing `$plaintext` encrypted with a key
-derived from `$password`. Knowledge of `$password` is required in order to
-decrypt the ciphertext and recover the plaintext.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-**Side-effects and performance:**
-
-This method is intentionally slow, using a lot of CPU resources for a fraction
-of a second. It applies key stretching to the password in order to make password
-guessing attacks more computationally expensive. If you need a faster way to
-encrypt multiple ciphertexts under the same password, see the
-`KeyProtectedByPassword` class.
-
-This method runs a small and very fast set of self-tests if it is the very first
-time one of the `Crypto` methods has been called. The performance overhead is
-negligible and can be safely ignored in all applications.
-
-**Cautions:**
-
-PHP stack traces display (portions of) the arguments passed to methods on the
-call stack. If an exception is thrown inside this call, and it is uncaught, the
-value of `$password` may be leaked out to an attacker through the stack trace.
-We recommend configuring PHP to never output stack traces (either displaying
-them to the user or saving them to log files).
-
-### Crypto::decryptWithPassword($ciphertext, $password, $raw\_binary = false)
-
-**Description:**
-
-Decrypts a ciphertext string using a secret password.
-
-**Parameters:**
-
-1. `$ciphertext` is the ciphertext to be decrypted.
-2. `$password` is a string containing the secret password used for decryption.
-3. `$raw_binary` must have the same value as the `$raw_binary` given to the
- call to `encryptWithPassword()` that generated `$ciphertext`.
-
-**Return value:**
-
-If the decryption succeeds, returns a string containing the same value as the
-string that was passed to `encryptWithPassword()` when `$ciphertext` was
-produced. Upon a successful return, the caller can be assured that `$ciphertext`
-could not have been produced except by someone with knowledge of `$password`.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-- `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
- the `$password` is not the correct password for the given ciphertext, or if
- the ciphertext has been modified (possibly maliciously). There is no way to
- distinguish between these two cases.
-
-**Side-effects:**
-
-This method is intentionally slow. It applies key stretching to the password in
-order to make password guessing attacks more computationally expensive. If you
-need a faster way to encrypt multiple ciphertexts under the same password, see
-the `KeyProtectedByPassword` class.
-
-This method runs a small and very fast set of self-tests if it is the very first
-time one of the `Crypto` methods has been called. The performance overhead is
-negligible and can be safely ignored in all applications.
-
-**Cautions:**
-
-PHP stack traces display (portions of) the arguments passed to methods on the
-call stack. If an exception is thrown inside this call, and it is uncaught, the
-value of `$password` may be leaked out to an attacker through the stack trace.
-We recommend configuring PHP to never output stack traces (either displaying
-them to the user or saving them to log files).
-
-It is impossible in principle to distinguish between the case where you attempt
-to decrypt with the wrong password and the case where you attempt to decrypt
-a modified (corrupted) ciphertext. It is up to the caller how to best deal with
-this ambiguity, as it depends on the application this library is being used in.
-If in doubt, consult with a professional cryptographer.
-
-### Crypto::legacyDecrypt($ciphertext, $key)
-
-**Description:**
-
-Decrypts a ciphertext produced by version 1 of this library so that the
-plaintext can be re-encrypted into a version 2 ciphertext. See [Upgrading from
-v1.2](../UpgradingFromV1.2.md).
-
-**Parameters:**
-
-1. `$ciphertext` is a ciphertext produced by version 1.x of this library.
-2. `$key` is a 16-byte string (*not* a Key object) containing the key that was
- used with version 1.x of this library to produce `$ciphertext`.
-
-**Return value:**
-
-If the decryption succeeds, returns the string that was encrypted to make
-`$ciphertext` by version 1.x of this library. Upon a successful return, the
-caller can be assured that `$ciphertext` could not have been produced except by
-someone with knowledge of `$key`.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-- `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
- the `$key` is not the correct key for the given ciphertext, or if the
- ciphertext has been modified (possibly maliciously). There is no way to
- distinguish between these two cases.
-
-**Side-effects:**
-
-This method runs a small and very fast set of self-tests if it is the very first
-time one of the `Crypto` methods has been called. The performance overhead is
-negligible and can be safely ignored in all applications.
-
-**Cautions:**
-
-PHP stack traces display (portions of) the arguments passed to methods on the
-call stack. If an exception is thrown inside this call, and it is uncaught, the
-value of `$key` may be leaked out to an attacker through the stack trace. We
-recommend configuring PHP to never output stack traces (either displaying them
-to the user or saving them to log files).
-
-It is impossible in principle to distinguish between the case where you attempt
-to decrypt with the wrong key and the case where you attempt to decrypt
-a modified (corrupted) ciphertext. It is up to the caller how to best deal with
-this ambiguity, as it depends on the application this library is being used in.
-If in doubt, consult with a professional cryptographer.
-
+++ /dev/null
-Class: Defuse\Crypto\File
-==========================
-
-Instance Methods
------------------
-
-This class has no instance methods.
-
-Static Methods
----------------
-
-### File::encryptFile($inputFilename, $outputFilename, Key $key)
-
-**Description:**
-
-Encrypts a file using a secret key.
-
-**Parameters:**
-
-1. `$inputFilename` is the path to a file containing the plaintext to encrypt.
-2. `$outputFilename` is the path to save the ciphertext file.
-3. `$key` is an instance of `Key` containing the secret key for encryption.
-
-**Behavior:**
-
-Encrypts the contents of the input file, writing the result to the output file.
-If the output file already exists, it is overwritten.
-
-**Return value:**
-
-Does not return a value.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-**Side-effects and performance:**
-
-None.
-
-**Cautions:**
-
-The ciphertext output by this method is decryptable by anyone with knowledge of
-the key `$key`. It is the caller's responsibility to keep `$key` secret. Where
-`$key` should be stored is up to the caller and depends on the threat model the
-caller is designing their application under. If you are unsure where to store
-`$key`, consult with a professional cryptographer to get help designing your
-application.
-
-### File::decryptFile($inputFilename, $outputFilename, Key $key)
-
-**Description:**
-
-Decrypts a file using a secret key.
-
-**Parameters:**
-
-1. `$inputFilename` is the path to a file containing the ciphertext to decrypt.
-2. `$outputFilename` is the path to save the decrypted plaintext file.
-3. `$key` is an instance of `Key` containing the secret key for decryption.
-
-**Behavior:**
-
-Decrypts the contents of the input file, writing the result to the output file.
-If the output file already exists, it is overwritten.
-
-**Return value:**
-
-Does not return a value.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-- `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
- the `$key` is not the correct key for the given ciphertext, or if the
- ciphertext has been modified (possibly maliciously). There is no way to
- distinguish between these two cases.
-
-**Side-effects and performance:**
-
-The input ciphertext is processed in two passes. The first pass verifies the
-integrity and the second pass performs the actual decryption of the file and
-writing to the output file. This is done in a streaming manner so that only
-a small part of the file is ever loaded into memory at a time.
-
-**Cautions:**
-
-Be aware that when `Defuse\Crypto\WrongKeyOrModifiedCiphertextException` is
-thrown, some partial plaintext data may have been written to the output. Any
-plaintext data that is output is guaranteed to be a prefix of the original
-plaintext (i.e. at worst it was truncated). This can only happen if an attacker
-modifies the input between the first pass (integrity check) and the second pass
-(decryption) over the file.
-
-It is impossible in principle to distinguish between the case where you attempt
-to decrypt with the wrong key and the case where you attempt to decrypt
-a modified (corrupted) ciphertext. It is up to the caller how to best deal with
-this ambiguity, as it depends on the application this library is being used in.
-If in doubt, consult with a professional cryptographer.
-
-### File::encryptFileWithPassword($inputFilename, $outputFilename, $password)
-
-**Description:**
-
-Encrypts a file with a password.
-
-**Parameters:**
-
-1. `$inputFilename` is the path to a file containing the plaintext to encrypt.
-2. `$outputFilename` is the path to save the ciphertext file.
-3. `$password` is the password used for decryption.
-
-**Behavior:**
-
-Encrypts the contents of the input file, writing the result to the output file.
-If the output file already exists, it is overwritten.
-
-**Return value:**
-
-Does not return a value.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-**Side-effects and performance:**
-
-This method is intentionally slow, using a lot of CPU resources for a fraction
-of a second. It applies key stretching to the password in order to make password
-guessing attacks more computationally expensive. If you need a faster way to
-encrypt multiple ciphertexts under the same password, see the
-`KeyProtectedByPassword` class.
-
-**Cautions:**
-
-PHP stack traces display (portions of) the arguments passed to methods on the
-call stack. If an exception is thrown inside this call, and it is uncaught, the
-value of `$password` may be leaked out to an attacker through the stack trace.
-We recommend configuring PHP to never output stack traces (either displaying
-them to the user or saving them to log files).
-
-### File::decryptFileWithPassword($inputFilename, $outputFilename, $password)
-
-**Description:**
-
-Decrypts a file with a password.
-
-**Parameters:**
-
-1. `$inputFilename` is the path to a file containing the ciphertext to decrypt.
-2. `$outputFilename` is the path to save the decrypted plaintext file.
-3. `$password` is the password used for decryption.
-
-**Behavior:**
-
-Decrypts the contents of the input file, writing the result to the output file.
-If the output file already exists, it is overwritten.
-
-**Return value:**
-
-Does not return a value.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-- `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
- the `$password` is not the correct key for the given ciphertext, or if the
- ciphertext has been modified (possibly maliciously). There is no way to
- distinguish between these two cases.
-
-**Side-effects and performance:**
-
-This method is intentionally slow, using a lot of CPU resources for a fraction
-of a second. It applies key stretching to the password in order to make password
-guessing attacks more computationally expensive. If you need a faster way to
-encrypt multiple ciphertexts under the same password, see the
-`KeyProtectedByPassword` class.
-
-The input ciphertext is processed in two passes. The first pass verifies the
-integrity and the second pass performs the actual decryption of the file and
-writing to the output file. This is done in a streaming manner so that only
-a small part of the file is ever loaded into memory at a time.
-
-**Cautions:**
-
-PHP stack traces display (portions of) the arguments passed to methods on the
-call stack. If an exception is thrown inside this call, and it is uncaught, the
-value of `$password` may be leaked out to an attacker through the stack trace.
-We recommend configuring PHP to never output stack traces (either displaying
-them to the user or saving them to log files).
-
-Be aware that when `Defuse\Crypto\WrongKeyOrModifiedCiphertextException` is
-thrown, some partial plaintext data may have been written to the output. Any
-plaintext data that is output is guaranteed to be a prefix of the original
-plaintext (i.e. at worst it was truncated). This can only happen if an attacker
-modifies the input between the first pass (integrity check) and the second pass
-(decryption) over the file.
-
-It is impossible in principle to distinguish between the case where you attempt
-to decrypt with the wrong password and the case where you attempt to decrypt
-a modified (corrupted) ciphertext. It is up to the caller how to best deal with
-this ambiguity, as it depends on the application this library is being used in.
-If in doubt, consult with a professional cryptographer.
-
-### File::encryptResource($inputHandle, $outputHandle, Key $key)
-
-**Description:**
-
-Encrypts a resource (stream) with a secret key.
-
-**Parameters:**
-
-1. `$inputHandle` is a handle to a resource (like a file pointer) containing the
- plaintext to encrypt.
-2. `$outputHandle` is a handle to a resource (like a file pointer) that the
- ciphertext will be written to.
-3. `$key` is an instance of `Key` containing the secret key for encryption.
-
-**Behavior:**
-
-Encrypts the data read from the input stream and writes it to the output stream.
-
-**Return value:**
-
-Does not return a value.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-**Side-effects and performance:**
-
-None.
-
-**Cautions:**
-
-The ciphertext output by this method is decryptable by anyone with knowledge of
-the key `$key`. It is the caller's responsibility to keep `$key` secret. Where
-`$key` should be stored is up to the caller and depends on the threat model the
-caller is designing their application under. If you are unsure where to store
-`$key`, consult with a professional cryptographer to get help designing your
-application.
-
-### File::decryptResource($inputHandle, $outputHandle, Key $key)
-
-**Description:**
-
-Decrypts a resource (stream) with a secret key.
-
-**Parameters:**
-
-1. `$inputHandle` is a handle to a file-backed resource containing the
- ciphertext to decrypt. It must be a file not a network stream or standard
- input.
-2. `$outputHandle` is a handle to a resource (like a file pointer) that the
- plaintext will be written to.
-3. `$key` is an instance of `Key` containing the secret key for decryption.
-
-**Behavior:**
-
-Decrypts the data read from the input stream and writes it to the output stream.
-
-**Return value:**
-
-Does not return a value.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-- `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
- the `$key` is not the correct key for the given ciphertext, or if the
- ciphertext has been modified (possibly maliciously). There is no way to
- distinguish between these two cases.
-
-**Side-effects and performance:**
-
-The input ciphertext is processed in two passes. The first pass verifies the
-integrity and the second pass performs the actual decryption of the file and
-writing to the output file. This is done in a streaming manner so that only
-a small part of the file is ever loaded into memory at a time.
-
-**Cautions:**
-
-Be aware that when `Defuse\Crypto\WrongKeyOrModifiedCiphertextException` is
-thrown, some partial plaintext data may have been written to the output. Any
-plaintext data that is output is guaranteed to be a prefix of the original
-plaintext (i.e. at worst it was truncated). This can only happen if an attacker
-modifies the input between the first pass (integrity check) and the second pass
-(decryption) over the file.
-
-It is impossible in principle to distinguish between the case where you attempt
-to decrypt with the wrong key and the case where you attempt to decrypt
-a modified (corrupted) ciphertext. It is up to the caller how to best deal with
-this ambiguity, as it depends on the application this library is being used in.
-If in doubt, consult with a professional cryptographer.
-
-### File::encryptResourceWithPassword($inputHandle, $outputHandle, $password)
-
-**Description:**
-
-Encrypts a resource (stream) with a password.
-
-**Parameters:**
-
-1. `$inputHandle` is a handle to a resource (like a file pointer) containing the
- plaintext to encrypt.
-2. `$outputHandle` is a handle to a resource (like a file pointer) that the
- ciphertext will be written to.
-3. `$password` is the password used for encryption.
-
-**Behavior:**
-
-Encrypts the data read from the input stream and writes it to the output stream.
-
-**Return value:**
-
-Does not return a value.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-**Side-effects and performance:**
-
-This method is intentionally slow, using a lot of CPU resources for a fraction
-of a second. It applies key stretching to the password in order to make password
-guessing attacks more computationally expensive. If you need a faster way to
-encrypt multiple ciphertexts under the same password, see the
-`KeyProtectedByPassword` class.
-
-**Cautions:**
-
-PHP stack traces display (portions of) the arguments passed to methods on the
-call stack. If an exception is thrown inside this call, and it is uncaught, the
-value of `$password` may be leaked out to an attacker through the stack trace.
-We recommend configuring PHP to never output stack traces (either displaying
-them to the user or saving them to log files).
-
-### File::decryptResourceWithPassword($inputHandle, $outputHandle, $password)
-
-**Description:**
-
-Decrypts a resource (stream) with a password.
-
-**Parameters:**
-
-1. `$inputHandle` is a handle to a file-backed resource containing the
- ciphertext to decrypt. It must be a file not a network stream or standard
- input.
-2. `$outputHandle` is a handle to a resource (like a file pointer) that the
- plaintext will be written to.
-3. `$password` is the password used for decryption.
-
-**Behavior:**
-
-Decrypts the data read from the input stream and writes it to the output stream.
-
-**Return value:**
-
-Does not return a value.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-- `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
- the `$password` is not the correct key for the given ciphertext, or if the
- ciphertext has been modified (possibly maliciously). There is no way to
- distinguish between these two cases.
-
-**Side-effects and performance:**
-
-This method is intentionally slow, using a lot of CPU resources for a fraction
-of a second. It applies key stretching to the password in order to make password
-guessing attacks more computationally expensive. If you need a faster way to
-encrypt multiple ciphertexts under the same password, see the
-`KeyProtectedByPassword` class.
-
-The input ciphertext is processed in two passes. The first pass verifies the
-integrity and the second pass performs the actual decryption of the file and
-writing to the output file. This is done in a streaming manner so that only
-a small part of the file is ever loaded into memory at a time.
-
-**Cautions:**
-
-PHP stack traces display (portions of) the arguments passed to methods on the
-call stack. If an exception is thrown inside this call, and it is uncaught, the
-value of `$password` may be leaked out to an attacker through the stack trace.
-We recommend configuring PHP to never output stack traces (either displaying
-them to the user or saving them to log files).
-
-Be aware that when `Defuse\Crypto\WrongKeyOrModifiedCiphertextException` is
-thrown, some partial plaintext data may have been written to the output. Any
-plaintext data that is output is guaranteed to be a prefix of the original
-plaintext (i.e. at worst it was truncated). This can only happen if an attacker
-modifies the input between the first pass (integrity check) and the second pass
-(decryption) over the file.
-
-It is impossible in principle to distinguish between the case where you attempt
-to decrypt with the wrong password and the case where you attempt to decrypt
-a modified (corrupted) ciphertext. It is up to the caller how to best deal with
-this ambiguity, as it depends on the application this library is being used in.
-If in doubt, consult with a professional cryptographer.
+++ /dev/null
-Class: Defuse\Crypto\Key
-=========================
-
-The `Key` class represents a secret key used for encrypting and decrypting. Once
-you have a `Key` instance, you can use it with the `Crypto` class to encrypt and
-decrypt strings and with the `File` class to encrypt and decrypt files.
-
-Instance Methods
------------------
-
-### saveToAsciiSafeString()
-
-**Description:**
-
-Saves the encryption key to a string of printable ASCII characters, which can be
-loaded again into a `Key` instance using `Key::loadFromAsciiSafeString()`.
-
-**Parameters:**
-
-This method does not take any parameters.
-
-**Return value:**
-
-Returns a string of printable ASCII characters representing this `Key` instance,
-which can be loaded back into an instance of `Key` using
-`Key::loadFromAsciiSafeString()`.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-**Side-effects and performance:**
-
-None.
-
-**Cautions:**
-
-This method currently returns a hexadecimal string. You should not rely on this
-behavior. For example, it may be improved in the future to return a base64
-string.
-
-Static Methods
----------------
-
-### Key::createNewRandomKey()
-
-**Description:**
-
-Generates a new random key and returns an instance of `Key`.
-
-**Parameters:**
-
-This method does not take any parameters.
-
-**Return value:**
-
-Returns an instance of `Key` containing a randomly-generated encryption key.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-**Side-effects and performance:**
-
-None.
-
-**Cautions:**
-
-None.
-
-### Key::loadFromAsciiSafeString($saved\_key\_string, $do\_not\_trim = false)
-
-**Description:**
-
-Loads an instance of `Key` that was saved to a string by
-`saveToAsciiSafeString()`.
-
-By default, this function will call `Encoding::trimTrailingWhitespace()`
-to remove trailing CR, LF, NUL, TAB, and SPACE characters, which are commonly
-appended to files when working with text editors.
-
-**Parameters:**
-
-1. `$saved_key_string` is the string returned from `saveToAsciiSafeString()`
- when the original `Key` instance was saved.
-2. `$do_not_trim` should be set to `TRUE` if you do not wish for the library
- to automatically strip trailing whitespace from the string.
-
-**Return value:**
-
-Returns an instance of `Key` representing the same encryption key as the one
-that was represented by the `Key` instance that got saved into
-`$saved_key_string` by a call to `saveToAsciiSafeString()`.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-- `Defuse\Crypto\Exception\BadFormatException` is thrown whenever
- `$saved_key_string` does not represent a valid `Key` instance.
-
-**Side-effects and performance:**
-
-None.
-
-**Cautions:**
-
-None.
+++ /dev/null
-Class: Defuse\Crypto\KeyProtectedByPassword
-============================================
-
-The `KeyProtectedByPassword` class represents a key that is "locked" with
-a password. In order to obtain an instance of `Key` that you can use for
-encrypting and decrypting, a `KeyProtectedByPassword` must first be "unlocked"
-by providing the correct password.
-
-`KeyProtectedByPassword` provides an alternative to using the
-`encryptWithPassword()`, `decryptWithPassword()`, `encryptFileWithPassword()`,
-and `decryptFileWithPassword()` methods with several advantages:
-
-- The slow and computationally-expensive key stretching is run only once when
- you unlock a `KeyProtectedByPassword` to obtain the `Key`.
-- You do not have to keep the original password in memory to encrypt and decrypt
- things. After you've obtained the `Key` from a `KeyProtectedByPassword`, the
- password is no longer necessary.
-
-Instance Methods
------------------
-
-### saveToAsciiSafeString()
-
-**Description:**
-
-Saves the protected key to a string of printable ASCII characters, which can be
-loaded again into a `KeyProtectedByPassword` instance using
-`KeyProtectedByPassword::loadFromAsciiSafeString()`.
-
-**Parameters:**
-
-This method does not take any parameters.
-
-**Return value:**
-
-Returns a string of printable ASCII characters representing this
-`KeyProtectedByPassword` instance, which can be loaded back into an instance of
-`KeyProtectedByPassword` using
-`KeyProtectedByPassword::loadFromAsciiSafeString()`.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-**Side-effects and performance:**
-
-None.
-
-**Cautions:**
-
-This method currently returns a hexadecimal string. You should not rely on this
-behavior. For example, it may be improved in the future to return a base64
-string.
-
-### unlockKey($password)
-
-**Description:**
-
-Unlocks the password-protected key, obtaining a `Key` which can be used for
-encryption and decryption.
-
-**Parameters:**
-
-1. `$password` is the password required to unlock this `KeyProtectedByPassword`
- to obtain the `Key`.
-
-**Return value:**
-
-If `$password` is the correct password, then this method returns an instance of
-the `Key` class.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-- `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
- either the given `$password` is not the correct password for this
- `KeyProtectedByPassword` or the ciphertext stored internally by this object
- has been modified, i.e. it was accidentally corrupted or intentionally
- corrupted by an attacker. There is no way for the caller to distinguish
- between these two cases.
-
-**Side-effects and performance:**
-
-This method runs a small and very fast set of self-tests if it is the very first
-time this method or one of the `Crypto` methods has been called. The performance
-overhead is negligible and can be safely ignored in all applications.
-
-**Cautions:**
-
-PHP stack traces display (portions of) the arguments passed to methods on the
-call stack. If an exception is thrown inside this call, and it is uncaught, the
-value of `$password` may be leaked out to an attacker through the stack trace.
-We recommend configuring PHP to never output stack traces (either displaying
-them to the user or saving them to log files).
-
-It is impossible in principle to distinguish between the case where you attempt
-to unlock with the wrong password and the case where you attempt to unlock
-a modified (corrupted) `KeyProtectedByPassword. It is up to the caller how to
-best deal with this ambiguity, as it depends on the application this library is
-being used in. If in doubt, consult with a professional cryptographer.
-
-Static Methods
----------------
-
-### KeyProtectedByPassword::createRandomPasswordProtectedKey($password)
-
-**Description:**
-
-Generates a new random key that's protected by the string `$password` and
-returns an instance of `KeyProtectedByPassword`.
-
-**Parameters:**
-
-1. `$password` is the password used to protect the random key.
-
-**Return value:**
-
-Returns an instance of `KeyProtectedByPassword` containing a randomly-generated
-encryption key that's protected by the password `$password`.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-**Side-effects and performance:**
-
-This method runs a small and very fast set of self-tests if it is the very first
-time this method or one of the `Crypto` methods has been called. The performance
-overhead is negligible and can be safely ignored in all applications.
-
-**Cautions:**
-
-PHP stack traces display (portions of) the arguments passed to methods on the
-call stack. If an exception is thrown inside this call, and it is uncaught, the
-value of `$password` may be leaked out to an attacker through the stack trace.
-We recommend configuring PHP to never output stack traces (either displaying
-them to the user or saving them to log files).
-
-Be aware that if you protecting multiple keys with the same password, an
-attacker with write access to your system will be able to swap the protected
-keys around so that the wrong key gets used next time it is unlocked. This could
-lead to data being encrypted with the wrong key, perhaps one that the attacker
-knows.
-
-### KeyProtectedByPassword::loadFromAsciiSafeString($saved\_key\_string)
-
-**Description:**
-
-Loads an instance of `KeyProtectedByPassword` that was saved to a string by
-`saveToAsciiSafeString()`.
-
-**Parameters:**
-
-1. `$saved_key_string` is the string returned from `saveToAsciiSafeString()`
- when the original `KeyProtectedByPassword` instance was saved.
-
-**Return value:**
-
-Returns an instance of `KeyProtectedByPassword` representing the same
-password-protected key as the one that was represented by the
-`KeyProtectedByPassword` instance that got saved into `$saved_key_string` by
-a call to `saveToAsciiSafeString()`.
-
-**Exceptions:**
-
-- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
- the platform the code is running on cannot safely perform encryption for some
- reason (e.g. it lacks a secure random number generator), or the runtime tests
- detected a bug in this library.
-
-- `Defuse\Crypto\Exception\BadFormatException` is thrown whenever
- `$saved_key_string` does not represent a valid `KeyProtectedByPassword`
- instance.
-
-**Side-effects and performance:**
-
-None.
-
-**Cautions:**
-
-None.
--- /dev/null
+<?php
+require_once('Crypto.php');
+ try {
+ $key = Crypto::CreateNewRandomKey();
+ // WARNING: Do NOT encode $key with bin2hex() or base64_encode(),
+ // they may leak the key to the attacker through side channels.
+ } catch (CryptoTestFailedException $ex) {
+ die('Cannot safely create a key');
+ } catch (CannotPerformOperationException $ex) {
+ die('Cannot safely create a key');
+ }
+
+ $message = "ATTACK AT DAWN";
+ try {
+ $ciphertext = Crypto::Encrypt($message, $key);
+ } catch (CryptoTestFailedException $ex) {
+ die('Cannot safely perform encryption');
+ } catch (CannotPerformOperationException $ex) {
+ die('Cannot safely perform decryption');
+ }
+
+ try {
+ $decrypted = Crypto::Decrypt($ciphertext, $key);
+ } catch (InvalidCiphertextException $ex) { // VERY IMPORTANT
+ // Either:
+ // 1. The ciphertext was modified by the attacker,
+ // 2. The key is wrong, or
+ // 3. $ciphertext is not a valid ciphertext or was corrupted.
+ // Assume the worst.
+ die('DANGER! DANGER! The ciphertext has been tampered with!');
+ } catch (CryptoTestFailedException $ex) {
+ die('Cannot safely perform encryption');
+ } catch (CannotPerformOperationException $ex) {
+ die('Cannot safely perform decryption');
+ }
+?>
+++ /dev/null
-<?xml version="1.0"?>
-<psalm
- stopOnFirstError="false"
- useDocblockTypes="true"
->
- <projectFiles>
- <directory name="src" />
- </projectFiles>
-</psalm>
+++ /dev/null
-<?php
-
-namespace Defuse\Crypto;
-
-use Defuse\Crypto\Exception as Ex;
-
-final class Core
-{
- const HEADER_VERSION_SIZE = 4;
- const MINIMUM_CIPHERTEXT_SIZE = 84;
-
- const CURRENT_VERSION = "\xDE\xF5\x02\x00";
-
- const CIPHER_METHOD = 'aes-256-ctr';
- const BLOCK_BYTE_SIZE = 16;
- const KEY_BYTE_SIZE = 32;
- const SALT_BYTE_SIZE = 32;
- const MAC_BYTE_SIZE = 32;
- const HASH_FUNCTION_NAME = 'sha256';
- const ENCRYPTION_INFO_STRING = 'DefusePHP|V2|KeyForEncryption';
- const AUTHENTICATION_INFO_STRING = 'DefusePHP|V2|KeyForAuthentication';
- const BUFFER_BYTE_SIZE = 1048576;
-
- const LEGACY_CIPHER_METHOD = 'aes-128-cbc';
- const LEGACY_BLOCK_BYTE_SIZE = 16;
- const LEGACY_KEY_BYTE_SIZE = 16;
- const LEGACY_HASH_FUNCTION_NAME = 'sha256';
- const LEGACY_MAC_BYTE_SIZE = 32;
- const LEGACY_ENCRYPTION_INFO_STRING = 'DefusePHP|KeyForEncryption';
- const LEGACY_AUTHENTICATION_INFO_STRING = 'DefusePHP|KeyForAuthentication';
-
- /*
- * V2.0 Format: VERSION (4 bytes) || SALT (32 bytes) || IV (16 bytes) ||
- * CIPHERTEXT (varies) || HMAC (32 bytes)
- *
- * V1.0 Format: HMAC (32 bytes) || IV (16 bytes) || CIPHERTEXT (varies).
- */
-
- /**
- * Adds an integer to a block-sized counter.
- *
- * @param string $ctr
- * @param int $inc
- *
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return string
- */
- public static function incrementCounter($ctr, $inc)
- {
- if (Core::ourStrlen($ctr) !== Core::BLOCK_BYTE_SIZE) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Trying to increment a nonce of the wrong size.'
- );
- }
-
- if (! \is_int($inc)) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Trying to increment nonce by a non-integer.'
- );
- }
-
- if ($inc < 0) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Trying to increment nonce by a negative amount.'
- );
- }
-
- if ($inc > PHP_INT_MAX - 255) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Integer overflow may occur.'
- );
- }
-
- /*
- * We start at the rightmost byte (big-endian)
- * So, too, does OpenSSL: http://stackoverflow.com/a/3146214/2224584
- */
- for ($i = Core::BLOCK_BYTE_SIZE - 1; $i >= 0; --$i) {
- $sum = \ord($ctr[$i]) + $inc;
-
- /* Detect integer overflow and fail. */
- if (! \is_int($sum)) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Integer overflow in CTR mode nonce increment.'
- );
- }
-
- $ctr[$i] = \pack('C', $sum & 0xFF);
- $inc = $sum >> 8;
- }
- return $ctr;
- }
-
- /**
- * Returns a random byte string of the specified length.
- *
- * @param int $octets
- *
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return string
- */
- public static function secureRandom($octets)
- {
- self::ensureFunctionExists('random_bytes');
- try {
- return \random_bytes($octets);
- } catch (\Exception $ex) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Your system does not have a secure random number generator.'
- );
- }
- }
-
- /**
- * Computes the HKDF key derivation function specified in
- * http://tools.ietf.org/html/rfc5869.
- *
- * @param string $hash Hash Function
- * @param string $ikm Initial Keying Material
- * @param int $length How many bytes?
- * @param string $info What sort of key are we deriving?
- * @param string $salt
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @psalm-suppress UndefinedFunction - We're checking if the function exists first.
- *
- * @return string
- */
- public static function HKDF($hash, $ikm, $length, $info = '', $salt = null)
- {
- static $nativeHKDF = null;
- if ($nativeHKDF === null) {
- $nativeHKDF = \is_callable('\\hash_hkdf');
- }
- if ($nativeHKDF) {
- return \hash_hkdf($hash, $ikm, $length, $info, $salt);
- }
-
- $digest_length = Core::ourStrlen(\hash_hmac($hash, '', '', true));
-
- // Sanity-check the desired output length.
- if (empty($length) || ! \is_int($length) ||
- $length < 0 || $length > 255 * $digest_length) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Bad output length requested of HKDF.'
- );
- }
-
- // "if [salt] not provided, is set to a string of HashLen zeroes."
- if (\is_null($salt)) {
- $salt = \str_repeat("\x00", $digest_length);
- }
-
- // HKDF-Extract:
- // PRK = HMAC-Hash(salt, IKM)
- // The salt is the HMAC key.
- $prk = \hash_hmac($hash, $ikm, $salt, true);
-
- // HKDF-Expand:
-
- // This check is useless, but it serves as a reminder to the spec.
- if (Core::ourStrlen($prk) < $digest_length) {
- throw new Ex\EnvironmentIsBrokenException();
- }
-
- // T(0) = ''
- $t = '';
- $last_block = '';
- for ($block_index = 1; Core::ourStrlen($t) < $length; ++$block_index) {
- // T(i) = HMAC-Hash(PRK, T(i-1) | info | 0x??)
- $last_block = \hash_hmac(
- $hash,
- $last_block . $info . \chr($block_index),
- $prk,
- true
- );
- // T = T(1) | T(2) | T(3) | ... | T(N)
- $t .= $last_block;
- }
-
- // ORM = first L octets of T
- /** @var string $orm */
- $orm = Core::ourSubstr($t, 0, $length);
- if (!\is_string($orm)) {
- throw new Ex\EnvironmentIsBrokenException();
- }
- return $orm;
- }
-
- /**
- * Checks if two equal-length strings are the same without leaking
- * information through side channels.
- *
- * @param string $expected
- * @param string $given
- *
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return bool
- */
- public static function hashEquals($expected, $given)
- {
- static $native = null;
- if ($native === null) {
- $native = \function_exists('hash_equals');
- }
- if ($native) {
- return \hash_equals($expected, $given);
- }
-
- // We can't just compare the strings with '==', since it would make
- // timing attacks possible. We could use the XOR-OR constant-time
- // comparison algorithm, but that may not be a reliable defense in an
- // interpreted language. So we use the approach of HMACing both strings
- // with a random key and comparing the HMACs.
-
- // We're not attempting to make variable-length string comparison
- // secure, as that's very difficult. Make sure the strings are the same
- // length.
- if (Core::ourStrlen($expected) !== Core::ourStrlen($given)) {
- throw new Ex\EnvironmentIsBrokenException();
- }
-
- $blind = Core::secureRandom(32);
- $message_compare = \hash_hmac(Core::HASH_FUNCTION_NAME, $given, $blind);
- $correct_compare = \hash_hmac(Core::HASH_FUNCTION_NAME, $expected, $blind);
- return $correct_compare === $message_compare;
- }
- /**
- * Throws an exception if the constant doesn't exist.
- *
- * @param string $name
- * @return void
- *
- * @throws Ex\EnvironmentIsBrokenException
- */
- public static function ensureConstantExists($name)
- {
- if (! \defined($name)) {
- throw new Ex\EnvironmentIsBrokenException();
- }
- }
-
- /**
- * Throws an exception if the function doesn't exist.
- *
- * @param string $name
- * @return void
- *
- * @throws Ex\EnvironmentIsBrokenException
- */
- public static function ensureFunctionExists($name)
- {
- if (! \function_exists($name)) {
- throw new Ex\EnvironmentIsBrokenException();
- }
- }
-
- /*
- * We need these strlen() and substr() functions because when
- * 'mbstring.func_overload' is set in php.ini, the standard strlen() and
- * substr() are replaced by mb_strlen() and mb_substr().
- */
-
- /**
- * Computes the length of a string in bytes.
- *
- * @param string $str
- *
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return int
- */
- public static function ourStrlen($str)
- {
- static $exists = null;
- if ($exists === null) {
- $exists = \function_exists('mb_strlen');
- }
- if ($exists) {
- $length = \mb_strlen($str, '8bit');
- if ($length === false) {
- throw new Ex\EnvironmentIsBrokenException();
- }
- return $length;
- } else {
- return \strlen($str);
- }
- }
-
- /**
- * Behaves roughly like the function substr() in PHP 7 does.
- *
- * @param string $str
- * @param int $start
- * @param int $length
- *
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return string|bool
- */
- public static function ourSubstr($str, $start, $length = null)
- {
- static $exists = null;
- if ($exists === null) {
- $exists = \function_exists('mb_substr');
- }
-
- if ($exists) {
- // mb_substr($str, 0, NULL, '8bit') returns an empty string on PHP
- // 5.3, so we have to find the length ourselves.
- if (! isset($length)) {
- if ($start >= 0) {
- $length = Core::ourStrlen($str) - $start;
- } else {
- $length = -$start;
- }
- }
-
- // This is required to make mb_substr behavior identical to substr.
- // Without this, mb_substr() would return false, contra to what the
- // PHP documentation says (it doesn't say it can return false.)
- if ($start === Core::ourStrlen($str) && $length === 0) {
- return '';
- }
-
- if ($start > Core::ourStrlen($str)) {
- return false;
- }
-
- $substr = \mb_substr($str, $start, $length, '8bit');
- if (Core::ourStrlen($substr) !== $length) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Your version of PHP has bug #66797. Its implementation of
- mb_substr() is incorrect. See the details here:
- https://bugs.php.net/bug.php?id=66797'
- );
- }
- return $substr;
- }
-
- // Unlike mb_substr(), substr() doesn't accept NULL for length
- if (isset($length)) {
- return \substr($str, $start, $length);
- } else {
- return \substr($str, $start);
- }
- }
-
- /**
- * Computes the PBKDF2 password-based key derivation function.
- *
- * The PBKDF2 function is defined in RFC 2898. Test vectors can be found in
- * RFC 6070. This implementation of PBKDF2 was originally created by Taylor
- * Hornby, with improvements from http://www.variations-of-shadow.com/.
- *
- * @param string $algorithm The hash algorithm to use. Recommended: SHA256
- * @param string $password The password.
- * @param string $salt A salt that is unique to the password.
- * @param int $count Iteration count. Higher is better, but slower. Recommended: At least 1000.
- * @param int $key_length The length of the derived key in bytes.
- * @param bool $raw_output If true, the key is returned in raw binary format. Hex encoded otherwise.
- *
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return string A $key_length-byte key derived from the password and salt.
- */
- public static function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
- {
- // Type checks:
- if (! \is_string($algorithm)) {
- throw new \InvalidArgumentException(
- 'pbkdf2(): algorithm must be a string'
- );
- }
- if (! \is_string($password)) {
- throw new \InvalidArgumentException(
- 'pbkdf2(): password must be a string'
- );
- }
- if (! \is_string($salt)) {
- throw new \InvalidArgumentException(
- 'pbkdf2(): salt must be a string'
- );
- }
- // Coerce strings to integers with no information loss or overflow
- $count += 0;
- $key_length += 0;
-
- $algorithm = \strtolower($algorithm);
- if (! \in_array($algorithm, \hash_algos(), true)) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Invalid or unsupported hash algorithm.'
- );
- }
-
- // Whitelist, or we could end up with people using CRC32.
- $ok_algorithms = [
- 'sha1', 'sha224', 'sha256', 'sha384', 'sha512',
- 'ripemd160', 'ripemd256', 'ripemd320', 'whirlpool',
- ];
- if (! \in_array($algorithm, $ok_algorithms, true)) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Algorithm is not a secure cryptographic hash function.'
- );
- }
-
- if ($count <= 0 || $key_length <= 0) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Invalid PBKDF2 parameters.'
- );
- }
-
- if (\function_exists('hash_pbkdf2')) {
- // The output length is in NIBBLES (4-bits) if $raw_output is false!
- if (! $raw_output) {
- $key_length = $key_length * 2;
- }
- return \hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
- }
-
- $hash_length = Core::ourStrlen(\hash($algorithm, '', true));
- $block_count = \ceil($key_length / $hash_length);
-
- $output = '';
- for ($i = 1; $i <= $block_count; $i++) {
- // $i encoded as 4 bytes, big endian.
- $last = $salt . \pack('N', $i);
- // first iteration
- $last = $xorsum = \hash_hmac($algorithm, $last, $password, true);
- // perform the other $count - 1 iterations
- for ($j = 1; $j < $count; $j++) {
- $xorsum ^= ($last = \hash_hmac($algorithm, $last, $password, true));
- }
- $output .= $xorsum;
- }
-
- if ($raw_output) {
- return (string) Core::ourSubstr($output, 0, $key_length);
- } else {
- return Encoding::binToHex((string) Core::ourSubstr($output, 0, $key_length));
- }
- }
-}
+++ /dev/null
-<?php
-
-namespace Defuse\Crypto;
-
-use Defuse\Crypto\Exception as Ex;
-
-class Crypto
-{
- /**
- * Encrypts a string with a Key.
- *
- * @param string $plaintext
- * @param Key $key
- * @param bool $raw_binary
- *
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return string
- */
- public static function encrypt($plaintext, Key $key, $raw_binary = false)
- {
- return self::encryptInternal(
- $plaintext,
- KeyOrPassword::createFromKey($key),
- $raw_binary
- );
- }
-
- /**
- * Encrypts a string with a password, using a slow key derivation function
- * to make password cracking more expensive.
- *
- * @param string $plaintext
- * @param string $password
- * @param bool $raw_binary
- *
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return string
- */
- public static function encryptWithPassword($plaintext, $password, $raw_binary = false)
- {
- return self::encryptInternal(
- $plaintext,
- KeyOrPassword::createFromPassword($password),
- $raw_binary
- );
- }
-
- /**
- * Decrypts a ciphertext to a string with a Key.
- *
- * @param string $ciphertext
- * @param Key $key
- * @param bool $raw_binary
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @throws Ex\WrongKeyOrModifiedCiphertextException
- *
- * @return string
- */
- public static function decrypt($ciphertext, Key $key, $raw_binary = false)
- {
- return self::decryptInternal(
- $ciphertext,
- KeyOrPassword::createFromKey($key),
- $raw_binary
- );
- }
-
- /**
- * Decrypts a ciphertext to a string with a password, using a slow key
- * derivation function to make password cracking more expensive.
- *
- * @param string $ciphertext
- * @param string $password
- * @param bool $raw_binary
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @throws Ex\WrongKeyOrModifiedCiphertextException
- *
- * @return string
- */
- public static function decryptWithPassword($ciphertext, $password, $raw_binary = false)
- {
- return self::decryptInternal(
- $ciphertext,
- KeyOrPassword::createFromPassword($password),
- $raw_binary
- );
- }
-
- /**
- * Decrypts a legacy ciphertext produced by version 1 of this library.
- *
- * @param string $ciphertext
- * @param string $key
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @throws Ex\WrongKeyOrModifiedCiphertextException
- *
- * @return string
- */
- public static function legacyDecrypt($ciphertext, $key)
- {
- RuntimeTests::runtimeTest();
-
- // Extract the HMAC from the front of the ciphertext.
- if (Core::ourStrlen($ciphertext) <= Core::LEGACY_MAC_BYTE_SIZE) {
- throw new Ex\WrongKeyOrModifiedCiphertextException(
- 'Ciphertext is too short.'
- );
- }
- /**
- * @var string
- */
- $hmac = Core::ourSubstr($ciphertext, 0, Core::LEGACY_MAC_BYTE_SIZE);
- if (!\is_string($hmac)) {
- throw new Ex\EnvironmentIsBrokenException();
- }
- /**
- * @var string
- */
- $ciphertext = Core::ourSubstr($ciphertext, Core::LEGACY_MAC_BYTE_SIZE);
- if (!\is_string($ciphertext)) {
- throw new Ex\EnvironmentIsBrokenException();
- }
-
- // Regenerate the same authentication sub-key.
- $akey = Core::HKDF(
- Core::LEGACY_HASH_FUNCTION_NAME,
- $key,
- Core::LEGACY_KEY_BYTE_SIZE,
- Core::LEGACY_AUTHENTICATION_INFO_STRING,
- null
- );
-
- if (self::verifyHMAC($hmac, $ciphertext, $akey)) {
- // Regenerate the same encryption sub-key.
- $ekey = Core::HKDF(
- Core::LEGACY_HASH_FUNCTION_NAME,
- $key,
- Core::LEGACY_KEY_BYTE_SIZE,
- Core::LEGACY_ENCRYPTION_INFO_STRING,
- null
- );
-
- // Extract the IV from the ciphertext.
- if (Core::ourStrlen($ciphertext) <= Core::LEGACY_BLOCK_BYTE_SIZE) {
- throw new Ex\WrongKeyOrModifiedCiphertextException(
- 'Ciphertext is too short.'
- );
- }
- /**
- * @var string
- */
- $iv = Core::ourSubstr($ciphertext, 0, Core::LEGACY_BLOCK_BYTE_SIZE);
- if (!\is_string($iv)) {
- throw new Ex\EnvironmentIsBrokenException();
- }
-
- /**
- * @var string
- */
- $actualCiphertext = Core::ourSubstr($ciphertext, Core::LEGACY_BLOCK_BYTE_SIZE);
- if (!\is_string($actualCiphertext)) {
- throw new Ex\EnvironmentIsBrokenException();
- }
-
- // Do the decryption.
- $plaintext = self::plainDecrypt($actualCiphertext, $ekey, $iv, Core::LEGACY_CIPHER_METHOD);
- return $plaintext;
- } else {
- throw new Ex\WrongKeyOrModifiedCiphertextException(
- 'Integrity check failed.'
- );
- }
- }
-
- /**
- * Encrypts a string with either a key or a password.
- *
- * @param string $plaintext
- * @param KeyOrPassword $secret
- * @param bool $raw_binary
- *
- * @return string
- */
- private static function encryptInternal($plaintext, KeyOrPassword $secret, $raw_binary)
- {
- RuntimeTests::runtimeTest();
-
- $salt = Core::secureRandom(Core::SALT_BYTE_SIZE);
- $keys = $secret->deriveKeys($salt);
- $ekey = $keys->getEncryptionKey();
- $akey = $keys->getAuthenticationKey();
- $iv = Core::secureRandom(Core::BLOCK_BYTE_SIZE);
-
- $ciphertext = Core::CURRENT_VERSION . $salt . $iv . self::plainEncrypt($plaintext, $ekey, $iv);
- $auth = \hash_hmac(Core::HASH_FUNCTION_NAME, $ciphertext, $akey, true);
- $ciphertext = $ciphertext . $auth;
-
- if ($raw_binary) {
- return $ciphertext;
- }
- return Encoding::binToHex($ciphertext);
- }
-
- /**
- * Decrypts a ciphertext to a string with either a key or a password.
- *
- * @param string $ciphertext
- * @param KeyOrPassword $secret
- * @param bool $raw_binary
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @throws Ex\WrongKeyOrModifiedCiphertextException
- *
- * @return string
- */
- private static function decryptInternal($ciphertext, KeyOrPassword $secret, $raw_binary)
- {
- RuntimeTests::runtimeTest();
-
- if (! $raw_binary) {
- try {
- $ciphertext = Encoding::hexToBin($ciphertext);
- } catch (Ex\BadFormatException $ex) {
- throw new Ex\WrongKeyOrModifiedCiphertextException(
- 'Ciphertext has invalid hex encoding.'
- );
- }
- }
-
- if (Core::ourStrlen($ciphertext) < Core::MINIMUM_CIPHERTEXT_SIZE) {
- throw new Ex\WrongKeyOrModifiedCiphertextException(
- 'Ciphertext is too short.'
- );
- }
-
- // Get and check the version header.
- /** @var string $header */
- $header = Core::ourSubstr($ciphertext, 0, Core::HEADER_VERSION_SIZE);
- if ($header !== Core::CURRENT_VERSION) {
- throw new Ex\WrongKeyOrModifiedCiphertextException(
- 'Bad version header.'
- );
- }
-
- // Get the salt.
- /** @var string $salt */
- $salt = Core::ourSubstr(
- $ciphertext,
- Core::HEADER_VERSION_SIZE,
- Core::SALT_BYTE_SIZE
- );
- if (!\is_string($salt)) {
- throw new Ex\EnvironmentIsBrokenException();
- }
-
- // Get the IV.
- /** @var string $iv */
- $iv = Core::ourSubstr(
- $ciphertext,
- Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE,
- Core::BLOCK_BYTE_SIZE
- );
- if (!\is_string($iv)) {
- throw new Ex\EnvironmentIsBrokenException();
- }
-
- // Get the HMAC.
- /** @var string $hmac */
- $hmac = Core::ourSubstr(
- $ciphertext,
- Core::ourStrlen($ciphertext) - Core::MAC_BYTE_SIZE,
- Core::MAC_BYTE_SIZE
- );
- if (!\is_string($hmac)) {
- throw new Ex\EnvironmentIsBrokenException();
- }
-
- // Get the actual encrypted ciphertext.
- /** @var string $encrypted */
- $encrypted = Core::ourSubstr(
- $ciphertext,
- Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE +
- Core::BLOCK_BYTE_SIZE,
- Core::ourStrlen($ciphertext) - Core::MAC_BYTE_SIZE - Core::SALT_BYTE_SIZE -
- Core::BLOCK_BYTE_SIZE - Core::HEADER_VERSION_SIZE
- );
- if (!\is_string($encrypted)) {
- throw new Ex\EnvironmentIsBrokenException();
- }
-
- // Derive the separate encryption and authentication keys from the key
- // or password, whichever it is.
- $keys = $secret->deriveKeys($salt);
-
- if (self::verifyHMAC($hmac, $header . $salt . $iv . $encrypted, $keys->getAuthenticationKey())) {
- $plaintext = self::plainDecrypt($encrypted, $keys->getEncryptionKey(), $iv, Core::CIPHER_METHOD);
- return $plaintext;
- } else {
- throw new Ex\WrongKeyOrModifiedCiphertextException(
- 'Integrity check failed.'
- );
- }
- }
-
- /**
- * Raw unauthenticated encryption (insecure on its own).
- *
- * @param string $plaintext
- * @param string $key
- * @param string $iv
- *
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return string
- */
- protected static function plainEncrypt($plaintext, $key, $iv)
- {
- Core::ensureConstantExists('OPENSSL_RAW_DATA');
- Core::ensureFunctionExists('openssl_encrypt');
- /** @var string $ciphertext */
- $ciphertext = \openssl_encrypt(
- $plaintext,
- Core::CIPHER_METHOD,
- $key,
- OPENSSL_RAW_DATA,
- $iv
- );
-
- if (!\is_string($ciphertext)) {
- throw new Ex\EnvironmentIsBrokenException(
- 'openssl_encrypt() failed.'
- );
- }
-
- return $ciphertext;
- }
-
- /**
- * Raw unauthenticated decryption (insecure on its own).
- *
- * @param string $ciphertext
- * @param string $key
- * @param string $iv
- * @param string $cipherMethod
- *
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return string
- */
- protected static function plainDecrypt($ciphertext, $key, $iv, $cipherMethod)
- {
- Core::ensureConstantExists('OPENSSL_RAW_DATA');
- Core::ensureFunctionExists('openssl_decrypt');
-
- /** @var string $plaintext */
- $plaintext = \openssl_decrypt(
- $ciphertext,
- $cipherMethod,
- $key,
- OPENSSL_RAW_DATA,
- $iv
- );
- if (!\is_string($plaintext)) {
- throw new Ex\EnvironmentIsBrokenException(
- 'openssl_decrypt() failed.'
- );
- }
-
- return $plaintext;
- }
-
- /**
- * Verifies an HMAC without leaking information through side-channels.
- *
- * @param string $expected_hmac
- * @param string $message
- * @param string $key
- *
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return bool
- */
- protected static function verifyHMAC($expected_hmac, $message, $key)
- {
- $message_hmac = \hash_hmac(Core::HASH_FUNCTION_NAME, $message, $key, true);
- return Core::hashEquals($message_hmac, $expected_hmac);
- }
-}
+++ /dev/null
-<?php
-
-namespace Defuse\Crypto;
-
-/**
- * Class DerivedKeys
- * @package Defuse\Crypto
- */
-final class DerivedKeys
-{
- /**
- * @var string
- */
- private $akey = '';
-
- /**
- * @var string
- */
- private $ekey = '';
-
- /**
- * Returns the authentication key.
- * @return string
- */
- public function getAuthenticationKey()
- {
- return $this->akey;
- }
-
- /**
- * Returns the encryption key.
- * @return string
- */
- public function getEncryptionKey()
- {
- return $this->ekey;
- }
-
- /**
- * Constructor for DerivedKeys.
- *
- * @param string $akey
- * @param string $ekey
- */
- public function __construct($akey, $ekey)
- {
- $this->akey = $akey;
- $this->ekey = $ekey;
- }
-}
+++ /dev/null
-<?php
-
-namespace Defuse\Crypto;
-
-use Defuse\Crypto\Exception as Ex;
-
-final class Encoding
-{
- const CHECKSUM_BYTE_SIZE = 32;
- const CHECKSUM_HASH_ALGO = 'sha256';
- const SERIALIZE_HEADER_BYTES = 4;
-
- /**
- * Converts a byte string to a hexadecimal string without leaking
- * information through side channels.
- *
- * @param string $byte_string
- *
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return string
- */
- public static function binToHex($byte_string)
- {
- $hex = '';
- $len = Core::ourStrlen($byte_string);
- for ($i = 0; $i < $len; ++$i) {
- $c = \ord($byte_string[$i]) & 0xf;
- $b = \ord($byte_string[$i]) >> 4;
- $hex .= \pack(
- 'CC',
- 87 + $b + ((($b - 10) >> 8) & ~38),
- 87 + $c + ((($c - 10) >> 8) & ~38)
- );
- }
- return $hex;
- }
-
- /**
- * Converts a hexadecimal string into a byte string without leaking
- * information through side channels.
- *
- * @param string $hex_string
- *
- * @throws Ex\BadFormatException
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return string
- */
- public static function hexToBin($hex_string)
- {
- $hex_pos = 0;
- $bin = '';
- $hex_len = Core::ourStrlen($hex_string);
- $state = 0;
- $c_acc = 0;
-
- while ($hex_pos < $hex_len) {
- $c = \ord($hex_string[$hex_pos]);
- $c_num = $c ^ 48;
- $c_num0 = ($c_num - 10) >> 8;
- $c_alpha = ($c & ~32) - 55;
- $c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8;
- if (($c_num0 | $c_alpha0) === 0) {
- throw new Ex\BadFormatException(
- 'Encoding::hexToBin() input is not a hex string.'
- );
- }
- $c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0);
- if ($state === 0) {
- $c_acc = $c_val * 16;
- } else {
- $bin .= \pack('C', $c_acc | $c_val);
- }
- $state ^= 1;
- ++$hex_pos;
- }
- return $bin;
- }
-
- /**
- * Remove trialing whitespace without table look-ups or branches.
- *
- * Calling this function may leak the length of the string as well as the
- * number of trailing whitespace characters through side-channels.
- *
- * @param string $string
- * @return string
- */
- public static function trimTrailingWhitespace($string = '')
- {
- $length = Core::ourStrlen($string);
- if ($length < 1) {
- return '';
- }
- do {
- $prevLength = $length;
- $last = $length - 1;
- $chr = \ord($string[$last]);
-
- /* Null Byte (0x00), a.k.a. \0 */
- // if ($chr === 0x00) $length -= 1;
- $sub = (($chr - 1) >> 8 ) & 1;
- $length -= $sub;
- $last -= $sub;
-
- /* Horizontal Tab (0x09) a.k.a. \t */
- $chr = \ord($string[$last]);
- // if ($chr === 0x09) $length -= 1;
- $sub = (((0x08 - $chr) & ($chr - 0x0a)) >> 8) & 1;
- $length -= $sub;
- $last -= $sub;
-
- /* New Line (0x0a), a.k.a. \n */
- $chr = \ord($string[$last]);
- // if ($chr === 0x0a) $length -= 1;
- $sub = (((0x09 - $chr) & ($chr - 0x0b)) >> 8) & 1;
- $length -= $sub;
- $last -= $sub;
-
- /* Carriage Return (0x0D), a.k.a. \r */
- $chr = \ord($string[$last]);
- // if ($chr === 0x0d) $length -= 1;
- $sub = (((0x0c - $chr) & ($chr - 0x0e)) >> 8) & 1;
- $length -= $sub;
- $last -= $sub;
-
- /* Space */
- $chr = \ord($string[$last]);
- // if ($chr === 0x20) $length -= 1;
- $sub = (((0x1f - $chr) & ($chr - 0x21)) >> 8) & 1;
- $length -= $sub;
- } while ($prevLength !== $length && $length > 0);
- return (string) Core::ourSubstr($string, 0, $length);
- }
-
- /*
- * SECURITY NOTE ON APPLYING CHECKSUMS TO SECRETS:
- *
- * The checksum introduces a potential security weakness. For example,
- * suppose we apply a checksum to a key, and that an adversary has an
- * exploit against the process containing the key, such that they can
- * overwrite an arbitrary byte of memory and then cause the checksum to
- * be verified and learn the result.
- *
- * In this scenario, the adversary can extract the key one byte at
- * a time by overwriting it with their guess of its value and then
- * asking if the checksum matches. If it does, their guess was right.
- * This kind of attack may be more easy to implement and more reliable
- * than a remote code execution attack.
- *
- * This attack also applies to authenticated encryption as a whole, in
- * the situation where the adversary can overwrite a byte of the key
- * and then cause a valid ciphertext to be decrypted, and then
- * determine whether the MAC check passed or failed.
- *
- * By using the full SHA256 hash instead of truncating it, I'm ensuring
- * that both ways of going about the attack are equivalently difficult.
- * A shorter checksum of say 32 bits might be more useful to the
- * adversary as an oracle in case their writes are coarser grained.
- *
- * Because the scenario assumes a serious vulnerability, we don't try
- * to prevent attacks of this style.
- */
-
- /**
- * INTERNAL USE ONLY: Applies a version header, applies a checksum, and
- * then encodes a byte string into a range of printable ASCII characters.
- *
- * @param string $header
- * @param string $bytes
- *
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return string
- */
- public static function saveBytesToChecksummedAsciiSafeString($header, $bytes)
- {
- // Headers must be a constant length to prevent one type's header from
- // being a prefix of another type's header, leading to ambiguity.
- if (Core::ourStrlen($header) !== self::SERIALIZE_HEADER_BYTES) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Header must be ' . self::SERIALIZE_HEADER_BYTES . ' bytes.'
- );
- }
-
- return Encoding::binToHex(
- $header .
- $bytes .
- \hash(
- self::CHECKSUM_HASH_ALGO,
- $header . $bytes,
- true
- )
- );
- }
-
- /**
- * INTERNAL USE ONLY: Decodes, verifies the header and checksum, and returns
- * the encoded byte string.
- *
- * @param string $expected_header
- * @param string $string
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @throws Ex\BadFormatException
- *
- * @return string
- */
- public static function loadBytesFromChecksummedAsciiSafeString($expected_header, $string)
- {
- // Headers must be a constant length to prevent one type's header from
- // being a prefix of another type's header, leading to ambiguity.
- if (Core::ourStrlen($expected_header) !== self::SERIALIZE_HEADER_BYTES) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Header must be 4 bytes.'
- );
- }
-
- /* If you get an exception here when attempting to load from a file, first pass your
- key to Encoding::trimTrailingWhitespace() to remove newline characters, etc. */
- $bytes = Encoding::hexToBin($string);
-
- /* Make sure we have enough bytes to get the version header and checksum. */
- if (Core::ourStrlen($bytes) < self::SERIALIZE_HEADER_BYTES + self::CHECKSUM_BYTE_SIZE) {
- throw new Ex\BadFormatException(
- 'Encoded data is shorter than expected.'
- );
- }
-
- /* Grab the version header. */
- $actual_header = (string) Core::ourSubstr($bytes, 0, self::SERIALIZE_HEADER_BYTES);
-
- if ($actual_header !== $expected_header) {
- throw new Ex\BadFormatException(
- 'Invalid header.'
- );
- }
-
- /* Grab the bytes that are part of the checksum. */
- $checked_bytes = (string) Core::ourSubstr(
- $bytes,
- 0,
- Core::ourStrlen($bytes) - self::CHECKSUM_BYTE_SIZE
- );
-
- /* Grab the included checksum. */
- $checksum_a = (string) Core::ourSubstr(
- $bytes,
- Core::ourStrlen($bytes) - self::CHECKSUM_BYTE_SIZE,
- self::CHECKSUM_BYTE_SIZE
- );
-
- /* Re-compute the checksum. */
- $checksum_b = \hash(self::CHECKSUM_HASH_ALGO, $checked_bytes, true);
-
- /* Check if the checksum matches. */
- if (! Core::hashEquals($checksum_a, $checksum_b)) {
- throw new Ex\BadFormatException(
- "Data is corrupted, the checksum doesn't match"
- );
- }
-
- return (string) Core::ourSubstr(
- $bytes,
- self::SERIALIZE_HEADER_BYTES,
- Core::ourStrlen($bytes) - self::SERIALIZE_HEADER_BYTES - self::CHECKSUM_BYTE_SIZE
- );
- }
-}
+++ /dev/null
-<?php
-
-namespace Defuse\Crypto\Exception;
-
-class BadFormatException extends \Defuse\Crypto\Exception\CryptoException
-{
-}
+++ /dev/null
-<?php
-
-namespace Defuse\Crypto\Exception;
-
-class CryptoException extends \Exception
-{
-}
+++ /dev/null
-<?php
-
-namespace Defuse\Crypto\Exception;
-
-class EnvironmentIsBrokenException extends \Defuse\Crypto\Exception\CryptoException
-{
-}
+++ /dev/null
-<?php
-
-namespace Defuse\Crypto\Exception;
-
-class IOException extends \Defuse\Crypto\Exception\CryptoException
-{
-}
+++ /dev/null
-<?php
-
-namespace Defuse\Crypto\Exception;
-
-class WrongKeyOrModifiedCiphertextException extends \Defuse\Crypto\Exception\CryptoException
-{
-}
+++ /dev/null
-<?php
-
-namespace Defuse\Crypto;
-
-use Defuse\Crypto\Exception as Ex;
-
-final class File
-{
- /**
- * Encrypts the input file, saving the ciphertext to the output file.
- *
- * @param string $inputFilename
- * @param string $outputFilename
- * @param Key $key
- * @return void
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @throws Ex\IOException
- */
- public static function encryptFile($inputFilename, $outputFilename, Key $key)
- {
- self::encryptFileInternal(
- $inputFilename,
- $outputFilename,
- KeyOrPassword::createFromKey($key)
- );
- }
-
- /**
- * Encrypts a file with a password, using a slow key derivation function to
- * make password cracking more expensive.
- *
- * @param string $inputFilename
- * @param string $outputFilename
- * @param string $password
- * @return void
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @throws Ex\IOException
- */
- public static function encryptFileWithPassword($inputFilename, $outputFilename, $password)
- {
- self::encryptFileInternal(
- $inputFilename,
- $outputFilename,
- KeyOrPassword::createFromPassword($password)
- );
- }
-
- /**
- * Decrypts the input file, saving the plaintext to the output file.
- *
- * @param string $inputFilename
- * @param string $outputFilename
- * @param Key $key
- * @return void
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @throws Ex\IOException
- * @throws Ex\WrongKeyOrModifiedCiphertextException
- */
- public static function decryptFile($inputFilename, $outputFilename, Key $key)
- {
- self::decryptFileInternal(
- $inputFilename,
- $outputFilename,
- KeyOrPassword::createFromKey($key)
- );
- }
-
- /**
- * Decrypts a file with a password, using a slow key derivation function to
- * make password cracking more expensive.
- *
- * @param string $inputFilename
- * @param string $outputFilename
- * @param string $password
- * @return void
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @throws Ex\IOException
- * @throws Ex\WrongKeyOrModifiedCiphertextException
- */
- public static function decryptFileWithPassword($inputFilename, $outputFilename, $password)
- {
- self::decryptFileInternal(
- $inputFilename,
- $outputFilename,
- KeyOrPassword::createFromPassword($password)
- );
- }
-
- /**
- * Takes two resource handles and encrypts the contents of the first,
- * writing the ciphertext into the second.
- *
- * @param resource $inputHandle
- * @param resource $outputHandle
- * @param Key $key
- * @return void
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @throws Ex\WrongKeyOrModifiedCiphertextException
- */
- public static function encryptResource($inputHandle, $outputHandle, Key $key)
- {
- self::encryptResourceInternal(
- $inputHandle,
- $outputHandle,
- KeyOrPassword::createFromKey($key)
- );
- }
-
- /**
- * Encrypts the contents of one resource handle into another with a
- * password, using a slow key derivation function to make password cracking
- * more expensive.
- *
- * @param resource $inputHandle
- * @param resource $outputHandle
- * @param string $password
- * @return void
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @throws Ex\IOException
- * @throws Ex\WrongKeyOrModifiedCiphertextException
- */
- public static function encryptResourceWithPassword($inputHandle, $outputHandle, $password)
- {
- self::encryptResourceInternal(
- $inputHandle,
- $outputHandle,
- KeyOrPassword::createFromPassword($password)
- );
- }
-
- /**
- * Takes two resource handles and decrypts the contents of the first,
- * writing the plaintext into the second.
- *
- * @param resource $inputHandle
- * @param resource $outputHandle
- * @param Key $key
- * @return void
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @throws Ex\IOException
- * @throws Ex\WrongKeyOrModifiedCiphertextException
- */
- public static function decryptResource($inputHandle, $outputHandle, Key $key)
- {
- self::decryptResourceInternal(
- $inputHandle,
- $outputHandle,
- KeyOrPassword::createFromKey($key)
- );
- }
-
- /**
- * Decrypts the contents of one resource into another with a password, using
- * a slow key derivation function to make password cracking more expensive.
- *
- * @param resource $inputHandle
- * @param resource $outputHandle
- * @param string $password
- * @return void
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @throws Ex\IOException
- * @throws Ex\WrongKeyOrModifiedCiphertextException
- */
- public static function decryptResourceWithPassword($inputHandle, $outputHandle, $password)
- {
- self::decryptResourceInternal(
- $inputHandle,
- $outputHandle,
- KeyOrPassword::createFromPassword($password)
- );
- }
-
- /**
- * Encrypts a file with either a key or a password.
- *
- * @param string $inputFilename
- * @param string $outputFilename
- * @param KeyOrPassword $secret
- * @return void
- *
- * @throws Ex\CryptoException
- * @throws Ex\IOException
- */
- private static function encryptFileInternal($inputFilename, $outputFilename, KeyOrPassword $secret)
- {
- /* Open the input file. */
- $if = @\fopen($inputFilename, 'rb');
- if ($if === false) {
- throw new Ex\IOException(
- 'Cannot open input file for encrypting: ' .
- self::getLastErrorMessage()
- );
- }
- if (\is_callable('\\stream_set_read_buffer')) {
- /* This call can fail, but the only consequence is performance. */
- \stream_set_read_buffer($if, 0);
- }
-
- /* Open the output file. */
- $of = @\fopen($outputFilename, 'wb');
- if ($of === false) {
- \fclose($if);
- throw new Ex\IOException(
- 'Cannot open output file for encrypting: ' .
- self::getLastErrorMessage()
- );
- }
- if (\is_callable('\\stream_set_write_buffer')) {
- /* This call can fail, but the only consequence is performance. */
- \stream_set_write_buffer($of, 0);
- }
-
- /* Perform the encryption. */
- try {
- self::encryptResourceInternal($if, $of, $secret);
- } catch (Ex\CryptoException $ex) {
- \fclose($if);
- \fclose($of);
- throw $ex;
- }
-
- /* Close the input file. */
- if (\fclose($if) === false) {
- \fclose($of);
- throw new Ex\IOException(
- 'Cannot close input file after encrypting'
- );
- }
-
- /* Close the output file. */
- if (\fclose($of) === false) {
- throw new Ex\IOException(
- 'Cannot close output file after encrypting'
- );
- }
- }
-
- /**
- * Decrypts a file with either a key or a password.
- *
- * @param string $inputFilename
- * @param string $outputFilename
- * @param KeyOrPassword $secret
- * @return void
- *
- * @throws Ex\CryptoException
- * @throws Ex\IOException
- */
- private static function decryptFileInternal($inputFilename, $outputFilename, KeyOrPassword $secret)
- {
- /* Open the input file. */
- $if = @\fopen($inputFilename, 'rb');
- if ($if === false) {
- throw new Ex\IOException(
- 'Cannot open input file for decrypting: ' .
- self::getLastErrorMessage()
- );
- }
-
- if (\is_callable('\\stream_set_read_buffer')) {
- /* This call can fail, but the only consequence is performance. */
- \stream_set_read_buffer($if, 0);
- }
-
- /* Open the output file. */
- $of = @\fopen($outputFilename, 'wb');
- if ($of === false) {
- \fclose($if);
- throw new Ex\IOException(
- 'Cannot open output file for decrypting: ' .
- self::getLastErrorMessage()
- );
- }
-
- if (\is_callable('\\stream_set_write_buffer')) {
- /* This call can fail, but the only consequence is performance. */
- \stream_set_write_buffer($of, 0);
- }
-
- /* Perform the decryption. */
- try {
- self::decryptResourceInternal($if, $of, $secret);
- } catch (Ex\CryptoException $ex) {
- \fclose($if);
- \fclose($of);
- throw $ex;
- }
-
- /* Close the input file. */
- if (\fclose($if) === false) {
- \fclose($of);
- throw new Ex\IOException(
- 'Cannot close input file after decrypting'
- );
- }
-
- /* Close the output file. */
- if (\fclose($of) === false) {
- throw new Ex\IOException(
- 'Cannot close output file after decrypting'
- );
- }
- }
-
- /**
- * Encrypts a resource with either a key or a password.
- *
- * @param resource $inputHandle
- * @param resource $outputHandle
- * @param KeyOrPassword $secret
- * @return void
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @throws Ex\IOException
- */
- private static function encryptResourceInternal($inputHandle, $outputHandle, KeyOrPassword $secret)
- {
- if (! \is_resource($inputHandle)) {
- throw new Ex\IOException(
- 'Input handle must be a resource!'
- );
- }
- if (! \is_resource($outputHandle)) {
- throw new Ex\IOException(
- 'Output handle must be a resource!'
- );
- }
-
- $inputStat = \fstat($inputHandle);
- $inputSize = $inputStat['size'];
-
- $file_salt = Core::secureRandom(Core::SALT_BYTE_SIZE);
- $keys = $secret->deriveKeys($file_salt);
- $ekey = $keys->getEncryptionKey();
- $akey = $keys->getAuthenticationKey();
-
- $ivsize = Core::BLOCK_BYTE_SIZE;
- $iv = Core::secureRandom($ivsize);
-
- /* Initialize a streaming HMAC state. */
- /** @var resource $hmac */
- $hmac = \hash_init(Core::HASH_FUNCTION_NAME, HASH_HMAC, $akey);
- if (!\is_resource($hmac)) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Cannot initialize a hash context'
- );
- }
-
- /* Write the header, salt, and IV. */
- self::writeBytes(
- $outputHandle,
- Core::CURRENT_VERSION . $file_salt . $iv,
- Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE + $ivsize
- );
-
- /* Add the header, salt, and IV to the HMAC. */
- \hash_update($hmac, Core::CURRENT_VERSION);
- \hash_update($hmac, $file_salt);
- \hash_update($hmac, $iv);
-
- /* $thisIv will be incremented after each call to the encryption. */
- $thisIv = $iv;
-
- /* How many blocks do we encrypt at a time? We increment by this value. */
- $inc = (int) (Core::BUFFER_BYTE_SIZE / Core::BLOCK_BYTE_SIZE);
-
- /* Loop until we reach the end of the input file. */
- $at_file_end = false;
- while (! (\feof($inputHandle) || $at_file_end)) {
- /* Find out if we can read a full buffer, or only a partial one. */
- /** @var int */
- $pos = \ftell($inputHandle);
- if (!\is_int($pos)) {
- throw new Ex\IOException(
- 'Could not get current position in input file during encryption'
- );
- }
- if ($pos + Core::BUFFER_BYTE_SIZE >= $inputSize) {
- /* We're at the end of the file, so we need to break out of the loop. */
- $at_file_end = true;
- $read = self::readBytes(
- $inputHandle,
- $inputSize - $pos
- );
- } else {
- $read = self::readBytes(
- $inputHandle,
- Core::BUFFER_BYTE_SIZE
- );
- }
-
- /* Encrypt this buffer. */
- /** @var string */
- $encrypted = \openssl_encrypt(
- $read,
- Core::CIPHER_METHOD,
- $ekey,
- OPENSSL_RAW_DATA,
- $thisIv
- );
-
- if (!\is_string($encrypted)) {
- throw new Ex\EnvironmentIsBrokenException(
- 'OpenSSL encryption error'
- );
- }
-
- /* Write this buffer's ciphertext. */
- self::writeBytes($outputHandle, $encrypted, Core::ourStrlen($encrypted));
- /* Add this buffer's ciphertext to the HMAC. */
- \hash_update($hmac, $encrypted);
-
- /* Increment the counter by the number of blocks in a buffer. */
- $thisIv = Core::incrementCounter($thisIv, $inc);
- /* WARNING: Usually, unless the file is a multiple of the buffer
- * size, $thisIv will contain an incorrect value here on the last
- * iteration of this loop. */
- }
-
- /* Get the HMAC and append it to the ciphertext. */
- $final_mac = \hash_final($hmac, true);
- self::writeBytes($outputHandle, $final_mac, Core::MAC_BYTE_SIZE);
- }
-
- /**
- * Decrypts a file-backed resource with either a key or a password.
- *
- * @param resource $inputHandle
- * @param resource $outputHandle
- * @param KeyOrPassword $secret
- * @return void
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @throws Ex\IOException
- * @throws Ex\WrongKeyOrModifiedCiphertextException
- */
- public static function decryptResourceInternal($inputHandle, $outputHandle, KeyOrPassword $secret)
- {
- if (! \is_resource($inputHandle)) {
- throw new Ex\IOException(
- 'Input handle must be a resource!'
- );
- }
- if (! \is_resource($outputHandle)) {
- throw new Ex\IOException(
- 'Output handle must be a resource!'
- );
- }
-
- /* Make sure the file is big enough for all the reads we need to do. */
- $stat = \fstat($inputHandle);
- if ($stat['size'] < Core::MINIMUM_CIPHERTEXT_SIZE) {
- throw new Ex\WrongKeyOrModifiedCiphertextException(
- 'Input file is too small to have been created by this library.'
- );
- }
-
- /* Check the version header. */
- $header = self::readBytes($inputHandle, Core::HEADER_VERSION_SIZE);
- if ($header !== Core::CURRENT_VERSION) {
- throw new Ex\WrongKeyOrModifiedCiphertextException(
- 'Bad version header.'
- );
- }
-
- /* Get the salt. */
- $file_salt = self::readBytes($inputHandle, Core::SALT_BYTE_SIZE);
-
- /* Get the IV. */
- $ivsize = Core::BLOCK_BYTE_SIZE;
- $iv = self::readBytes($inputHandle, $ivsize);
-
- /* Derive the authentication and encryption keys. */
- $keys = $secret->deriveKeys($file_salt);
- $ekey = $keys->getEncryptionKey();
- $akey = $keys->getAuthenticationKey();
-
- /* We'll store the MAC of each buffer-sized chunk as we verify the
- * actual MAC, so that we can check them again when decrypting. */
- $macs = [];
-
- /* $thisIv will be incremented after each call to the decryption. */
- $thisIv = $iv;
-
- /* How many blocks do we encrypt at a time? We increment by this value. */
- $inc = (int) (Core::BUFFER_BYTE_SIZE / Core::BLOCK_BYTE_SIZE);
-
- /* Get the HMAC. */
- if (\fseek($inputHandle, (-1 * Core::MAC_BYTE_SIZE), SEEK_END) === false) {
- throw new Ex\IOException(
- 'Cannot seek to beginning of MAC within input file'
- );
- }
-
- /* Get the position of the last byte in the actual ciphertext. */
- /** @var int $cipher_end */
- $cipher_end = \ftell($inputHandle);
- if (!\is_int($cipher_end)) {
- throw new Ex\IOException(
- 'Cannot read input file'
- );
- }
- /* We have the position of the first byte of the HMAC. Go back by one. */
- --$cipher_end;
-
- /* Read the HMAC. */
- /** @var string $stored_mac */
- $stored_mac = self::readBytes($inputHandle, Core::MAC_BYTE_SIZE);
-
- /* Initialize a streaming HMAC state. */
- /** @var resource $hmac */
- $hmac = \hash_init(Core::HASH_FUNCTION_NAME, HASH_HMAC, $akey);
- if (!\is_resource($hmac)) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Cannot initialize a hash context'
- );
- }
-
- /* Reset file pointer to the beginning of the file after the header */
- if (\fseek($inputHandle, Core::HEADER_VERSION_SIZE, SEEK_SET) === false) {
- throw new Ex\IOException(
- 'Cannot read seek within input file'
- );
- }
-
- /* Seek to the start of the actual ciphertext. */
- if (\fseek($inputHandle, Core::SALT_BYTE_SIZE + $ivsize, SEEK_CUR) === false) {
- throw new Ex\IOException(
- 'Cannot seek input file to beginning of ciphertext'
- );
- }
-
- /* PASS #1: Calculating the HMAC. */
-
- \hash_update($hmac, $header);
- \hash_update($hmac, $file_salt);
- \hash_update($hmac, $iv);
- /** @var resource $hmac2 */
- $hmac2 = \hash_copy($hmac);
-
- $break = false;
- while (! $break) {
- /** @var int $pos */
- $pos = \ftell($inputHandle);
- if (!\is_int($pos)) {
- throw new Ex\IOException(
- 'Could not get current position in input file during decryption'
- );
- }
-
- /* Read the next buffer-sized chunk (or less). */
- if ($pos + Core::BUFFER_BYTE_SIZE >= $cipher_end) {
- $break = true;
- $read = self::readBytes(
- $inputHandle,
- $cipher_end - $pos + 1
- );
- } else {
- $read = self::readBytes(
- $inputHandle,
- Core::BUFFER_BYTE_SIZE
- );
- }
-
- /* Update the HMAC. */
- \hash_update($hmac, $read);
-
- /* Remember this buffer-sized chunk's HMAC. */
- /** @var resource $chunk_mac */
- $chunk_mac = \hash_copy($hmac);
- if (!\is_resource($chunk_mac)) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Cannot duplicate a hash context'
- );
- }
- $macs []= \hash_final($chunk_mac);
- }
-
- /* Get the final HMAC, which should match the stored one. */
- /** @var string $final_mac */
- $final_mac = \hash_final($hmac, true);
-
- /* Verify the HMAC. */
- if (! Core::hashEquals($final_mac, $stored_mac)) {
- throw new Ex\WrongKeyOrModifiedCiphertextException(
- 'Integrity check failed.'
- );
- }
-
- /* PASS #2: Decrypt and write output. */
-
- /* Rewind to the start of the actual ciphertext. */
- if (\fseek($inputHandle, Core::SALT_BYTE_SIZE + $ivsize + Core::HEADER_VERSION_SIZE, SEEK_SET) === false) {
- throw new Ex\IOException(
- 'Could not move the input file pointer during decryption'
- );
- }
-
- $at_file_end = false;
- while (! $at_file_end) {
- /** @var int $pos */
- $pos = \ftell($inputHandle);
- if (!\is_int($pos)) {
- throw new Ex\IOException(
- 'Could not get current position in input file during decryption'
- );
- }
-
- /* Read the next buffer-sized chunk (or less). */
- if ($pos + Core::BUFFER_BYTE_SIZE >= $cipher_end) {
- $at_file_end = true;
- $read = self::readBytes(
- $inputHandle,
- $cipher_end - $pos + 1
- );
- } else {
- $read = self::readBytes(
- $inputHandle,
- Core::BUFFER_BYTE_SIZE
- );
- }
-
- /* Recalculate the MAC (so far) and compare it with the one we
- * remembered from pass #1 to ensure attackers didn't change the
- * ciphertext after MAC verification. */
- \hash_update($hmac2, $read);
- /** @var resource $calc_mac */
- $calc_mac = \hash_copy($hmac2);
- if (!\is_resource($calc_mac)) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Cannot duplicate a hash context'
- );
- }
- $calc = \hash_final($calc_mac);
-
- if (empty($macs)) {
- throw new Ex\WrongKeyOrModifiedCiphertextException(
- 'File was modified after MAC verification'
- );
- } elseif (! Core::hashEquals(\array_shift($macs), $calc)) {
- throw new Ex\WrongKeyOrModifiedCiphertextException(
- 'File was modified after MAC verification'
- );
- }
-
- /* Decrypt this buffer-sized chunk. */
- /** @var string $decrypted */
- $decrypted = \openssl_decrypt(
- $read,
- Core::CIPHER_METHOD,
- $ekey,
- OPENSSL_RAW_DATA,
- $thisIv
- );
- if (!\is_string($decrypted)) {
- throw new Ex\EnvironmentIsBrokenException(
- 'OpenSSL decryption error'
- );
- }
-
- /* Write the plaintext to the output file. */
- self::writeBytes(
- $outputHandle,
- $decrypted,
- Core::ourStrlen($decrypted)
- );
-
- /* Increment the IV by the amount of blocks in a buffer. */
- /** @var string $thisIv */
- $thisIv = Core::incrementCounter($thisIv, $inc);
- /* WARNING: Usually, unless the file is a multiple of the buffer
- * size, $thisIv will contain an incorrect value here on the last
- * iteration of this loop. */
- }
- }
-
- /**
- * Read from a stream; prevent partial reads.
- *
- * @param resource $stream
- * @param int $num_bytes
- * @return string
- *
- * @throws Ex\IOException
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return string
- */
- public static function readBytes($stream, $num_bytes)
- {
- if ($num_bytes < 0) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Tried to read less than 0 bytes'
- );
- } elseif ($num_bytes === 0) {
- return '';
- }
- $buf = '';
- $remaining = $num_bytes;
- while ($remaining > 0 && ! \feof($stream)) {
- /** @var string $read */
- $read = \fread($stream, $remaining);
- if (!\is_string($read)) {
- throw new Ex\IOException(
- 'Could not read from the file'
- );
- }
- $buf .= $read;
- $remaining -= Core::ourStrlen($read);
- }
- if (Core::ourStrlen($buf) !== $num_bytes) {
- throw new Ex\IOException(
- 'Tried to read past the end of the file'
- );
- }
- return $buf;
- }
-
- /**
- * Write to a stream; prevents partial writes.
- *
- * @param resource $stream
- * @param string $buf
- * @param int $num_bytes
- * @return int
- *
- * @throws Ex\IOException
- *
- * @return string
- */
- public static function writeBytes($stream, $buf, $num_bytes = null)
- {
- $bufSize = Core::ourStrlen($buf);
- if ($num_bytes === null) {
- $num_bytes = $bufSize;
- }
- if ($num_bytes > $bufSize) {
- throw new Ex\IOException(
- 'Trying to write more bytes than the buffer contains.'
- );
- }
- if ($num_bytes < 0) {
- throw new Ex\IOException(
- 'Tried to write less than 0 bytes'
- );
- }
- $remaining = $num_bytes;
- while ($remaining > 0) {
- /** @var int $written */
- $written = \fwrite($stream, $buf, $remaining);
- if (!\is_int($written)) {
- throw new Ex\IOException(
- 'Could not write to the file'
- );
- }
- $buf = (string) Core::ourSubstr($buf, $written, null);
- $remaining -= $written;
- }
- return $num_bytes;
- }
-
- /**
- * Returns the last PHP error's or warning's message string.
- *
- * @return string
- */
- private static function getLastErrorMessage()
- {
- $error = error_get_last();
- if ($error === null) {
- return '[no PHP error]';
- } else {
- return $error['message'];
- }
- }
-}
+++ /dev/null
-<?php
-
-namespace Defuse\Crypto;
-
-use Defuse\Crypto\Exception as Ex;
-
-final class Key
-{
- const KEY_CURRENT_VERSION = "\xDE\xF0\x00\x00";
- const KEY_BYTE_SIZE = 32;
-
- /**
- * @var string
- */
- private $key_bytes;
-
- /**
- * Creates new random key.
- *
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return Key
- */
- public static function createNewRandomKey()
- {
- return new Key(Core::secureRandom(self::KEY_BYTE_SIZE));
- }
-
- /**
- * Loads a Key from its encoded form.
- *
- * By default, this function will call Encoding::trimTrailingWhitespace()
- * to remove trailing CR, LF, NUL, TAB, and SPACE characters, which are
- * commonly appended to files when working with text editors.
- *
- * @param string $saved_key_string
- * @param bool $do_not_trim (default: false)
- *
- * @throws Ex\BadFormatException
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return Key
- */
- public static function loadFromAsciiSafeString($saved_key_string, $do_not_trim = false)
- {
- if (!$do_not_trim) {
- $saved_key_string = Encoding::trimTrailingWhitespace($saved_key_string);
- }
- $key_bytes = Encoding::loadBytesFromChecksummedAsciiSafeString(self::KEY_CURRENT_VERSION, $saved_key_string);
- return new Key($key_bytes);
- }
-
- /**
- * Encodes the Key into a string of printable ASCII characters.
- *
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return string
- */
- public function saveToAsciiSafeString()
- {
- return Encoding::saveBytesToChecksummedAsciiSafeString(
- self::KEY_CURRENT_VERSION,
- $this->key_bytes
- );
- }
-
- /**
- * Gets the raw bytes of the key.
- *
- * @return string
- */
- public function getRawBytes()
- {
- return $this->key_bytes;
- }
-
- /**
- * Constructs a new Key object from a string of raw bytes.
- *
- * @param string $bytes
- *
- * @throws Ex\EnvironmentIsBrokenException
- */
- private function __construct($bytes)
- {
- if (Core::ourStrlen($bytes) !== self::KEY_BYTE_SIZE) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Bad key length.'
- );
- }
- $this->key_bytes = $bytes;
- }
-
-}
+++ /dev/null
-<?php
-
-namespace Defuse\Crypto;
-
-use Defuse\Crypto\Exception as Ex;
-
-final class KeyOrPassword
-{
- const PBKDF2_ITERATIONS = 100000;
- const SECRET_TYPE_KEY = 1;
- const SECRET_TYPE_PASSWORD = 2;
-
- /**
- * @var int
- */
- private $secret_type = 0;
-
- /**
- * @var Key|string
- */
- private $secret;
-
- /**
- * Initializes an instance of KeyOrPassword from a key.
- *
- * @param Key $key
- *
- * @return KeyOrPassword
- */
- public static function createFromKey(Key $key)
- {
- return new KeyOrPassword(self::SECRET_TYPE_KEY, $key);
- }
-
- /**
- * Initializes an instance of KeyOrPassword from a password.
- *
- * @param string $password
- *
- * @return KeyOrPassword
- */
- public static function createFromPassword($password)
- {
- return new KeyOrPassword(self::SECRET_TYPE_PASSWORD, $password);
- }
-
- /**
- * Derives authentication and encryption keys from the secret, using a slow
- * key derivation function if the secret is a password.
- *
- * @param string $salt
- *
- * @throws Ex\CryptoException
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return DerivedKeys
- */
- public function deriveKeys($salt)
- {
- if (Core::ourStrlen($salt) !== Core::SALT_BYTE_SIZE) {
- throw new Ex\EnvironmentIsBrokenException('Bad salt.');
- }
-
- if ($this->secret_type === self::SECRET_TYPE_KEY) {
- if (!($this->secret instanceof Key)) {
- throw new Ex\CryptoException('Expected a Key object');
- }
- $akey = Core::HKDF(
- Core::HASH_FUNCTION_NAME,
- $this->secret->getRawBytes(),
- Core::KEY_BYTE_SIZE,
- Core::AUTHENTICATION_INFO_STRING,
- $salt
- );
- $ekey = Core::HKDF(
- Core::HASH_FUNCTION_NAME,
- $this->secret->getRawBytes(),
- Core::KEY_BYTE_SIZE,
- Core::ENCRYPTION_INFO_STRING,
- $salt
- );
- return new DerivedKeys($akey, $ekey);
- } elseif ($this->secret_type === self::SECRET_TYPE_PASSWORD) {
- if (!\is_string($this->secret)) {
- throw new Ex\CryptoException('Expected a string');
- }
- /* Our PBKDF2 polyfill is vulnerable to a DoS attack documented in
- * GitHub issue #230. The fix is to pre-hash the password to ensure
- * it is short. We do the prehashing here instead of in pbkdf2() so
- * that pbkdf2() still computes the function as defined by the
- * standard. */
- $prehash = \hash(Core::HASH_FUNCTION_NAME, $this->secret, true);
- $prekey = Core::pbkdf2(
- Core::HASH_FUNCTION_NAME,
- $prehash,
- $salt,
- self::PBKDF2_ITERATIONS,
- Core::KEY_BYTE_SIZE,
- true
- );
- $akey = Core::HKDF(
- Core::HASH_FUNCTION_NAME,
- $prekey,
- Core::KEY_BYTE_SIZE,
- Core::AUTHENTICATION_INFO_STRING,
- $salt
- );
- /* Note the cryptographic re-use of $salt here. */
- $ekey = Core::HKDF(
- Core::HASH_FUNCTION_NAME,
- $prekey,
- Core::KEY_BYTE_SIZE,
- Core::ENCRYPTION_INFO_STRING,
- $salt
- );
- return new DerivedKeys($akey, $ekey);
- } else {
- throw new Ex\EnvironmentIsBrokenException('Bad secret type.');
- }
- }
-
- /**
- * Constructor for KeyOrPassword.
- *
- * @param int $secret_type
- * @param mixed $secret (either a Key or a password string)
- */
- private function __construct($secret_type, $secret)
- {
- $this->secret_type = $secret_type;
- $this->secret = $secret;
- }
-}
+++ /dev/null
-<?php
-
-namespace Defuse\Crypto;
-
-use Defuse\Crypto\Exception as Ex;
-
-final class KeyProtectedByPassword
-{
- const PASSWORD_KEY_CURRENT_VERSION = "\xDE\xF1\x00\x00";
-
- /**
- * @var string
- */
- private $encrypted_key = '';
-
- /**
- * Creates a random key protected by the provided password.
- *
- * @param string $password
- *
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return KeyProtectedByPassword
- */
- public static function createRandomPasswordProtectedKey($password)
- {
- $inner_key = Key::createNewRandomKey();
- /* The password is hashed as a form of poor-man's domain separation
- * between this use of encryptWithPassword() and other uses of
- * encryptWithPassword() that the user may also be using as part of the
- * same protocol. */
- $encrypted_key = Crypto::encryptWithPassword(
- $inner_key->saveToAsciiSafeString(),
- \hash(Core::HASH_FUNCTION_NAME, $password, true),
- true
- );
-
- return new KeyProtectedByPassword($encrypted_key);
- }
-
- /**
- * Loads a KeyProtectedByPassword from its encoded form.
- *
- * @param string $saved_key_string
- *
- * @throws Ex\BadFormatException
- *
- * @return KeyProtectedByPassword
- */
- public static function loadFromAsciiSafeString($saved_key_string)
- {
- $encrypted_key = Encoding::loadBytesFromChecksummedAsciiSafeString(
- self::PASSWORD_KEY_CURRENT_VERSION,
- $saved_key_string
- );
- return new KeyProtectedByPassword($encrypted_key);
- }
-
- /**
- * Encodes the KeyProtectedByPassword into a string of printable ASCII
- * characters.
- *
- * @throws Ex\EnvironmentIsBrokenException
- *
- * @return string
- */
- public function saveToAsciiSafeString()
- {
- return Encoding::saveBytesToChecksummedAsciiSafeString(
- self::PASSWORD_KEY_CURRENT_VERSION,
- $this->encrypted_key
- );
- }
-
- /**
- * Decrypts the protected key, returning an unprotected Key object that can
- * be used for encryption and decryption.
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @throws Ex\WrongKeyOrModifiedCiphertextException
- *
- * @return Key
- */
- public function unlockKey($password)
- {
- try {
- $inner_key_encoded = Crypto::decryptWithPassword(
- $this->encrypted_key,
- \hash(Core::HASH_FUNCTION_NAME, $password, true),
- true
- );
- return Key::loadFromAsciiSafeString($inner_key_encoded);
- } catch (Ex\BadFormatException $ex) {
- /* This should never happen unless an attacker replaced the
- * encrypted key ciphertext with some other ciphertext that was
- * encrypted with the same password. We transform the exception type
- * here in order to make the API simpler, avoiding the need to
- * document that this method might throw an Ex\BadFormatException. */
- throw new Ex\WrongKeyOrModifiedCiphertextException(
- "The decrypted key was found to be in an invalid format. " .
- "This very likely indicates it was modified by an attacker."
- );
- }
- }
-
- /**
- * Constructor for KeyProtectedByPassword.
- *
- * @param string $encrypted_key
- */
- private function __construct($encrypted_key)
- {
- $this->encrypted_key = $encrypted_key;
- }
-}
+++ /dev/null
-<?php
-
-namespace Defuse\Crypto;
-
-use Defuse\Crypto\Exception as Ex;
-
-/*
- * We're using static class inheritance to get access to protected methods
- * inside Crypto. To make it easy to know where the method we're calling can be
- * found, within this file, prefix calls with `Crypto::` or `RuntimeTests::`,
- * and don't use `self::`.
- */
-
-class RuntimeTests extends Crypto
-{
- /**
- * Runs the runtime tests.
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @return void
- */
- public static function runtimeTest()
- {
- // 0: Tests haven't been run yet.
- // 1: Tests have passed.
- // 2: Tests are running right now.
- // 3: Tests have failed.
- static $test_state = 0;
-
- if ($test_state === 1 || $test_state === 2) {
- return;
- }
-
- if ($test_state === 3) {
- /* If an intermittent problem caused a test to fail previously, we
- * want that to be indicated to the user with every call to this
- * library. This way, if the user first does something they really
- * don't care about, and just ignores all exceptions, they won't get
- * screwed when they then start to use the library for something
- * they do care about. */
- throw new Ex\EnvironmentIsBrokenException('Tests failed previously.');
- }
-
- try {
- $test_state = 2;
-
- Core::ensureFunctionExists('openssl_get_cipher_methods');
- if (\in_array(Core::CIPHER_METHOD, \openssl_get_cipher_methods()) === false) {
- throw new Ex\EnvironmentIsBrokenException(
- 'Cipher method not supported. This is normally caused by an outdated ' .
- 'version of OpenSSL (and/or OpenSSL compiled for FIPS compliance). ' .
- 'Please upgrade to a newer version of OpenSSL that supports ' .
- Core::CIPHER_METHOD . ' to use this library.'
- );
- }
-
- RuntimeTests::AESTestVector();
- RuntimeTests::HMACTestVector();
- RuntimeTests::HKDFTestVector();
-
- RuntimeTests::testEncryptDecrypt();
- if (Core::ourStrlen(Key::createNewRandomKey()->getRawBytes()) != Core::KEY_BYTE_SIZE) {
- throw new Ex\EnvironmentIsBrokenException();
- }
-
- if (Core::ENCRYPTION_INFO_STRING == Core::AUTHENTICATION_INFO_STRING) {
- throw new Ex\EnvironmentIsBrokenException();
- }
- } catch (Ex\EnvironmentIsBrokenException $ex) {
- // Do this, otherwise it will stay in the "tests are running" state.
- $test_state = 3;
- throw $ex;
- }
-
- // Change this to '0' make the tests always re-run (for benchmarking).
- $test_state = 1;
- }
-
- /**
- * High-level tests of Crypto operations.
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @return void
- */
- private static function testEncryptDecrypt()
- {
- $key = Key::createNewRandomKey();
- $data = "EnCrYpT EvErYThInG\x00\x00";
-
- // Make sure encrypting then decrypting doesn't change the message.
- $ciphertext = Crypto::encrypt($data, $key, true);
- try {
- $decrypted = Crypto::decrypt($ciphertext, $key, true);
- } catch (Ex\WrongKeyOrModifiedCiphertextException $ex) {
- // It's important to catch this and change it into a
- // Ex\EnvironmentIsBrokenException, otherwise a test failure could trick
- // the user into thinking it's just an invalid ciphertext!
- throw new Ex\EnvironmentIsBrokenException();
- }
- if ($decrypted !== $data) {
- throw new Ex\EnvironmentIsBrokenException();
- }
-
- // Modifying the ciphertext: Appending a string.
- try {
- Crypto::decrypt($ciphertext . 'a', $key, true);
- throw new Ex\EnvironmentIsBrokenException();
- } catch (Ex\WrongKeyOrModifiedCiphertextException $e) { /* expected */
- }
-
- // Modifying the ciphertext: Changing an HMAC byte.
- $indices_to_change = [
- 0, // The header.
- Core::HEADER_VERSION_SIZE + 1, // the salt
- Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE + 1, // the IV
- Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE + Core::BLOCK_BYTE_SIZE + 1, // the ciphertext
- ];
-
- foreach ($indices_to_change as $index) {
- try {
- $ciphertext[$index] = \chr((\ord($ciphertext[$index]) + 1) % 256);
- Crypto::decrypt($ciphertext, $key, true);
- throw new Ex\EnvironmentIsBrokenException();
- } catch (Ex\WrongKeyOrModifiedCiphertextException $e) { /* expected */
- }
- }
-
- // Decrypting with the wrong key.
- $key = Key::createNewRandomKey();
- $data = 'abcdef';
- $ciphertext = Crypto::encrypt($data, $key, true);
- $wrong_key = Key::createNewRandomKey();
- try {
- Crypto::decrypt($ciphertext, $wrong_key, true);
- throw new Ex\EnvironmentIsBrokenException();
- } catch (Ex\WrongKeyOrModifiedCiphertextException $e) { /* expected */
- }
-
- // Ciphertext too small.
- $key = Key::createNewRandomKey();
- $ciphertext = \str_repeat('A', Core::MINIMUM_CIPHERTEXT_SIZE - 1);
- try {
- Crypto::decrypt($ciphertext, $key, true);
- throw new Ex\EnvironmentIsBrokenException();
- } catch (Ex\WrongKeyOrModifiedCiphertextException $e) { /* expected */
- }
- }
-
- /**
- * Test HKDF against test vectors.
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @return void
- */
- private static function HKDFTestVector()
- {
- // HKDF test vectors from RFC 5869
-
- // Test Case 1
- $ikm = \str_repeat("\x0b", 22);
- $salt = Encoding::hexToBin('000102030405060708090a0b0c');
- $info = Encoding::hexToBin('f0f1f2f3f4f5f6f7f8f9');
- $length = 42;
- $okm = Encoding::hexToBin(
- '3cb25f25faacd57a90434f64d0362f2a' .
- '2d2d0a90cf1a5a4c5db02d56ecc4c5bf' .
- '34007208d5b887185865'
- );
- $computed_okm = Core::HKDF('sha256', $ikm, $length, $info, $salt);
- if ($computed_okm !== $okm) {
- throw new Ex\EnvironmentIsBrokenException();
- }
-
- // Test Case 7
- $ikm = \str_repeat("\x0c", 22);
- $length = 42;
- $okm = Encoding::hexToBin(
- '2c91117204d745f3500d636a62f64f0a' .
- 'b3bae548aa53d423b0d1f27ebba6f5e5' .
- '673a081d70cce7acfc48'
- );
- $computed_okm = Core::HKDF('sha1', $ikm, $length, '', null);
- if ($computed_okm !== $okm) {
- throw new Ex\EnvironmentIsBrokenException();
- }
- }
-
- /**
- * Test HMAC against test vectors.
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @return void
- */
- private static function HMACTestVector()
- {
- // HMAC test vector From RFC 4231 (Test Case 1)
- $key = \str_repeat("\x0b", 20);
- $data = 'Hi There';
- $correct = 'b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7';
- if (\hash_hmac(Core::HASH_FUNCTION_NAME, $data, $key) !== $correct) {
- throw new Ex\EnvironmentIsBrokenException();
- }
- }
-
- /**
- * Test AES against test vectors.
- *
- * @throws Ex\EnvironmentIsBrokenException
- * @return void
- */
- private static function AESTestVector()
- {
- // AES CTR mode test vector from NIST SP 800-38A
- $key = Encoding::hexToBin(
- '603deb1015ca71be2b73aef0857d7781' .
- '1f352c073b6108d72d9810a30914dff4'
- );
- $iv = Encoding::hexToBin('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff');
- $plaintext = Encoding::hexToBin(
- '6bc1bee22e409f96e93d7e117393172a' .
- 'ae2d8a571e03ac9c9eb76fac45af8e51' .
- '30c81c46a35ce411e5fbc1191a0a52ef' .
- 'f69f2445df4f9b17ad2b417be66c3710'
- );
- $ciphertext = Encoding::hexToBin(
- '601ec313775789a5b7a7f504bbf3d228' .
- 'f443e3ca4d62b59aca84e990cacaf5c5' .
- '2b0930daa23de94ce87017ba2d84988d' .
- 'dfc9c58db67aada613c2dd08457941a6'
- );
-
- $computed_ciphertext = Crypto::plainEncrypt($plaintext, $key, $iv);
- if ($computed_ciphertext !== $ciphertext) {
- echo \str_repeat("\n", 30);
- echo \bin2hex($computed_ciphertext);
- echo "\n---\n";
- echo \bin2hex($ciphertext);
- echo \str_repeat("\n", 30);
- throw new Ex\EnvironmentIsBrokenException();
- }
-
- $computed_plaintext = Crypto::plainDecrypt($ciphertext, $key, $iv, Core::CIPHER_METHOD);
- if ($computed_plaintext !== $plaintext) {
- throw new Ex\EnvironmentIsBrokenException();
- }
- }
-}
--- /dev/null
+#!/bin/bash
+
+echo "Normal"
+echo "--------------------------------------------------"
+php -d mbstring.func_overload=0 tests/runtime.php
+if [ $? -ne 0 ]; then
+ echo "FAIL."
+ exit 1
+fi
+echo "--------------------------------------------------"
+
+echo ""
+
+echo "Multibyte"
+echo "--------------------------------------------------"
+php -d mbstring.func_overload=7 tests/runtime.php
+if [ $? -ne 0 ]; then
+ echo "FAIL."
+ exit 1
+fi
+echo "--------------------------------------------------"
+
+echo ""
+
+if [ -z "$(php Crypto.php)" ]; then
+ echo "PASS: Crypto.php output is empty."
+else
+ echo "FAIL: Crypto.php output is not empty."
+ exit 1
+fi
--- /dev/null
+<?php
+
+// Set the encoding to something more "challenging."
+$ret = mb_internal_encoding('UTF-8');
+if ($ret === FALSE) {
+ echo "Couldn't set encoding.";
+ exit(1);
+}
+
+// Dump out the settings / encoding for future reference.
+$val = ini_get("mbstring.func_overload");
+echo "Settings: \n";
+echo " func_overload: " . $val . "\n";
+echo " mb_internal_encoding(): " . mb_internal_encoding() . "\n";
+
+// Perform the tests.
+require_once('Crypto.php');
+try {
+ Crypto::RuntimeTest();
+ echo "TEST PASSED!\n";
+ exit(0);
+} catch (CryptoTestFailedException $ex) {
+ echo "TEST FAILED!\n";
+ var_dump($ex);
+ exit(1);
+} catch (CannotPerformOperationException $ex) {
+ echo "TEST FAILED\n";
+ var_dump($ex);
+ exit(1);
+}
+
+?>
+++ /dev/null
-The MIT License (MIT)
-
-Copyright (c) 2015 Paragon Initiative Enterprises
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
+++ /dev/null
-#!/usr/bin/env bash
-
-basedir=$( dirname $( readlink -f ${BASH_SOURCE[0]} ) )
-
-php -dphar.readonly=0 "$basedir/other/build_phar.php" $*
\ No newline at end of file
+++ /dev/null
-{
- "name": "paragonie/random_compat",
- "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
- "keywords": [
- "csprng",
- "random",
- "pseudorandom"
- ],
- "license": "MIT",
- "type": "library",
- "authors": [
- {
- "name": "Paragon Initiative Enterprises",
- "email": "security@paragonie.com",
- "homepage": "https://paragonie.com"
- }
- ],
- "support": {
- "issues": "https://github.com/paragonie/random_compat/issues",
- "email": "info@paragonie.com",
- "source": "https://github.com/paragonie/random_compat"
- },
- "require": {
- "php": ">=5.2.0"
- },
- "require-dev": {
- "phpunit/phpunit": "4.*|5.*"
- },
- "suggest": {
- "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
- },
- "autoload": {
- "files": [
- "lib/random.php"
- ]
- }
-}
+++ /dev/null
------BEGIN PUBLIC KEY-----
-MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEEd+wCqJDrx5B4OldM0dQE0ZMX+lx1ZWm
-pui0SUqD4G29L3NGsz9UhJ/0HjBdbnkhIK5xviT0X5vtjacF6ajgcCArbTB+ds+p
-+h7Q084NuSuIpNb6YPfoUFgC/CL9kAoc
------END PUBLIC KEY-----
+++ /dev/null
------BEGIN PGP SIGNATURE-----
-Version: GnuPG v2.0.22 (MingW32)
-
-iQEcBAABAgAGBQJWtW1hAAoJEGuXocKCZATaJf0H+wbZGgskK1dcRTsuVJl9IWip
-QwGw/qIKI280SD6/ckoUMxKDCJiFuPR14zmqnS36k7N5UNPnpdTJTS8T11jttSpg
-1LCmgpbEIpgaTah+cELDqFCav99fS+bEiAL5lWDAHBTE/XPjGVCqeehyPYref4IW
-NDBIEsvnHPHPLsn6X5jq4+Yj5oUixgxaMPiR+bcO4Sh+RzOVB6i2D0upWfRXBFXA
-NNnsg9/zjvoC7ZW73y9uSH+dPJTt/Vgfeiv52/v41XliyzbUyLalf02GNPY+9goV
-JHG1ulEEBJOCiUD9cE1PUIJwHA/HqyhHIvV350YoEFiHl8iSwm7SiZu5kPjaq74=
-=B6+8
------END PGP SIGNATURE-----
+++ /dev/null
-<?php
-/**
- * Random_* Compatibility Library
- * for using the new PHP 7 random_* API in PHP 5 projects
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-if (!is_callable('RandomCompat_strlen')) {
- if (
- defined('MB_OVERLOAD_STRING') &&
- ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING
- ) {
- /**
- * strlen() implementation that isn't brittle to mbstring.func_overload
- *
- * This version uses mb_strlen() in '8bit' mode to treat strings as raw
- * binary rather than UTF-8, ISO-8859-1, etc
- *
- * @param string $binary_string
- *
- * @throws TypeError
- *
- * @return int
- */
- function RandomCompat_strlen($binary_string)
- {
- if (!is_string($binary_string)) {
- throw new TypeError(
- 'RandomCompat_strlen() expects a string'
- );
- }
-
- return (int) mb_strlen($binary_string, '8bit');
- }
-
- } else {
- /**
- * strlen() implementation that isn't brittle to mbstring.func_overload
- *
- * This version just used the default strlen()
- *
- * @param string $binary_string
- *
- * @throws TypeError
- *
- * @return int
- */
- function RandomCompat_strlen($binary_string)
- {
- if (!is_string($binary_string)) {
- throw new TypeError(
- 'RandomCompat_strlen() expects a string'
- );
- }
- return (int) strlen($binary_string);
- }
- }
-}
-
-if (!is_callable('RandomCompat_substr')) {
-
- if (
- defined('MB_OVERLOAD_STRING')
- &&
- ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING
- ) {
- /**
- * substr() implementation that isn't brittle to mbstring.func_overload
- *
- * This version uses mb_substr() in '8bit' mode to treat strings as raw
- * binary rather than UTF-8, ISO-8859-1, etc
- *
- * @param string $binary_string
- * @param int $start
- * @param int $length (optional)
- *
- * @throws TypeError
- *
- * @return string
- */
- function RandomCompat_substr($binary_string, $start, $length = null)
- {
- if (!is_string($binary_string)) {
- throw new TypeError(
- 'RandomCompat_substr(): First argument should be a string'
- );
- }
-
- if (!is_int($start)) {
- throw new TypeError(
- 'RandomCompat_substr(): Second argument should be an integer'
- );
- }
-
- if ($length === null) {
- /**
- * mb_substr($str, 0, NULL, '8bit') returns an empty string on
- * PHP 5.3, so we have to find the length ourselves.
- */
- $length = RandomCompat_strlen($binary_string) - $start;
- } elseif (!is_int($length)) {
- throw new TypeError(
- 'RandomCompat_substr(): Third argument should be an integer, or omitted'
- );
- }
-
- // Consistency with PHP's behavior
- if ($start === RandomCompat_strlen($binary_string) && $length === 0) {
- return '';
- }
- if ($start > RandomCompat_strlen($binary_string)) {
- return '';
- }
-
- return (string) mb_substr($binary_string, $start, $length, '8bit');
- }
-
- } else {
-
- /**
- * substr() implementation that isn't brittle to mbstring.func_overload
- *
- * This version just uses the default substr()
- *
- * @param string $binary_string
- * @param int $start
- * @param int $length (optional)
- *
- * @throws TypeError
- *
- * @return string
- */
- function RandomCompat_substr($binary_string, $start, $length = null)
- {
- if (!is_string($binary_string)) {
- throw new TypeError(
- 'RandomCompat_substr(): First argument should be a string'
- );
- }
-
- if (!is_int($start)) {
- throw new TypeError(
- 'RandomCompat_substr(): Second argument should be an integer'
- );
- }
-
- if ($length !== null) {
- if (!is_int($length)) {
- throw new TypeError(
- 'RandomCompat_substr(): Third argument should be an integer, or omitted'
- );
- }
-
- return (string) substr($binary_string, $start, $length);
- }
-
- return (string) substr($binary_string, $start);
- }
- }
-}
+++ /dev/null
-<?php
-/**
- * Random_* Compatibility Library
- * for using the new PHP 7 random_* API in PHP 5 projects
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-if (!is_callable('RandomCompat_intval')) {
-
- /**
- * Cast to an integer if we can, safely.
- *
- * If you pass it a float in the range (~PHP_INT_MAX, PHP_INT_MAX)
- * (non-inclusive), it will sanely cast it to an int. If you it's equal to
- * ~PHP_INT_MAX or PHP_INT_MAX, we let it fail as not an integer. Floats
- * lose precision, so the <= and => operators might accidentally let a float
- * through.
- *
- * @param int|float $number The number we want to convert to an int
- * @param bool $fail_open Set to true to not throw an exception
- *
- * @return float|int
- * @psalm-suppress InvalidReturnType
- *
- * @throws TypeError
- */
- function RandomCompat_intval($number, $fail_open = false)
- {
- if (is_int($number) || is_float($number)) {
- $number += 0;
- } elseif (is_numeric($number)) {
- $number += 0;
- }
-
- if (
- is_float($number)
- &&
- $number > ~PHP_INT_MAX
- &&
- $number < PHP_INT_MAX
- ) {
- $number = (int) $number;
- }
-
- if (is_int($number)) {
- return (int) $number;
- } elseif (!$fail_open) {
- throw new TypeError(
- 'Expected an integer.'
- );
- }
- return $number;
- }
-}
+++ /dev/null
-<?php
-/**
- * Random_* Compatibility Library
- * for using the new PHP 7 random_* API in PHP 5 projects
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-if (!class_exists('Error', false)) {
- // We can't really avoid making this extend Exception in PHP 5.
- class Error extends Exception
- {
-
- }
-}
-
-if (!class_exists('TypeError', false)) {
- if (is_subclass_of('Error', 'Exception')) {
- class TypeError extends Error
- {
-
- }
- } else {
- class TypeError extends Exception
- {
-
- }
- }
-}
+++ /dev/null
-<?php
-/**
- * Random_* Compatibility Library
- * for using the new PHP 7 random_* API in PHP 5 projects
- *
- * @version 2.0.10
- * @released 2017-03-13
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-if (!defined('PHP_VERSION_ID')) {
- // This constant was introduced in PHP 5.2.7
- $RandomCompatversion = array_map('intval', explode('.', PHP_VERSION));
- define(
- 'PHP_VERSION_ID',
- $RandomCompatversion[0] * 10000
- + $RandomCompatversion[1] * 100
- + $RandomCompatversion[2]
- );
- $RandomCompatversion = null;
-}
-
-/**
- * PHP 7.0.0 and newer have these functions natively.
- */
-if (PHP_VERSION_ID >= 70000) {
- return;
-}
-
-if (!defined('RANDOM_COMPAT_READ_BUFFER')) {
- define('RANDOM_COMPAT_READ_BUFFER', 8);
-}
-
-$RandomCompatDIR = dirname(__FILE__);
-
-require_once $RandomCompatDIR . '/byte_safe_strings.php';
-require_once $RandomCompatDIR . '/cast_to_int.php';
-require_once $RandomCompatDIR . '/error_polyfill.php';
-
-if (!is_callable('random_bytes')) {
- /**
- * PHP 5.2.0 - 5.6.x way to implement random_bytes()
- *
- * We use conditional statements here to define the function in accordance
- * to the operating environment. It's a micro-optimization.
- *
- * In order of preference:
- * 1. Use libsodium if available.
- * 2. fread() /dev/urandom if available (never on Windows)
- * 3. mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM)
- * 4. COM('CAPICOM.Utilities.1')->GetRandom()
- *
- * See RATIONALE.md for our reasoning behind this particular order
- */
- if (extension_loaded('libsodium')) {
- // See random_bytes_libsodium.php
- if (PHP_VERSION_ID >= 50300 && is_callable('\\Sodium\\randombytes_buf')) {
- require_once $RandomCompatDIR . '/random_bytes_libsodium.php';
- } elseif (method_exists('Sodium', 'randombytes_buf')) {
- require_once $RandomCompatDIR . '/random_bytes_libsodium_legacy.php';
- }
- }
-
- /**
- * Reading directly from /dev/urandom:
- */
- if (DIRECTORY_SEPARATOR === '/') {
- // DIRECTORY_SEPARATOR === '/' on Unix-like OSes -- this is a fast
- // way to exclude Windows.
- $RandomCompatUrandom = true;
- $RandomCompat_basedir = ini_get('open_basedir');
-
- if (!empty($RandomCompat_basedir)) {
- $RandomCompat_open_basedir = explode(
- PATH_SEPARATOR,
- strtolower($RandomCompat_basedir)
- );
- $RandomCompatUrandom = (array() !== array_intersect(
- array('/dev', '/dev/', '/dev/urandom'),
- $RandomCompat_open_basedir
- ));
- $RandomCompat_open_basedir = null;
- }
-
- if (
- !is_callable('random_bytes')
- &&
- $RandomCompatUrandom
- &&
- @is_readable('/dev/urandom')
- ) {
- // Error suppression on is_readable() in case of an open_basedir
- // or safe_mode failure. All we care about is whether or not we
- // can read it at this point. If the PHP environment is going to
- // panic over trying to see if the file can be read in the first
- // place, that is not helpful to us here.
-
- // See random_bytes_dev_urandom.php
- require_once $RandomCompatDIR . '/random_bytes_dev_urandom.php';
- }
- // Unset variables after use
- $RandomCompat_basedir = null;
- } else {
- $RandomCompatUrandom = false;
- }
-
- /**
- * mcrypt_create_iv()
- *
- * We only want to use mcypt_create_iv() if:
- *
- * - random_bytes() hasn't already been defined
- * - the mcrypt extensions is loaded
- * - One of these two conditions is true:
- * - We're on Windows (DIRECTORY_SEPARATOR !== '/')
- * - We're not on Windows and /dev/urandom is readabale
- * (i.e. we're not in a chroot jail)
- * - Special case:
- * - If we're not on Windows, but the PHP version is between
- * 5.6.10 and 5.6.12, we don't want to use mcrypt. It will
- * hang indefinitely. This is bad.
- * - If we're on Windows, we want to use PHP >= 5.3.7 or else
- * we get insufficient entropy errors.
- */
- if (
- !is_callable('random_bytes')
- &&
- // Windows on PHP < 5.3.7 is broken, but non-Windows is not known to be.
- (DIRECTORY_SEPARATOR === '/' || PHP_VERSION_ID >= 50307)
- &&
- // Prevent this code from hanging indefinitely on non-Windows;
- // see https://bugs.php.net/bug.php?id=69833
- (
- DIRECTORY_SEPARATOR !== '/' ||
- (PHP_VERSION_ID <= 50609 || PHP_VERSION_ID >= 50613)
- )
- &&
- extension_loaded('mcrypt')
- ) {
- // See random_bytes_mcrypt.php
- require_once $RandomCompatDIR . '/random_bytes_mcrypt.php';
- }
- $RandomCompatUrandom = null;
-
- /**
- * This is a Windows-specific fallback, for when the mcrypt extension
- * isn't loaded.
- */
- if (
- !is_callable('random_bytes')
- &&
- extension_loaded('com_dotnet')
- &&
- class_exists('COM')
- ) {
- $RandomCompat_disabled_classes = preg_split(
- '#\s*,\s*#',
- strtolower(ini_get('disable_classes'))
- );
-
- if (!in_array('com', $RandomCompat_disabled_classes)) {
- try {
- $RandomCompatCOMtest = new COM('CAPICOM.Utilities.1');
- if (method_exists($RandomCompatCOMtest, 'GetRandom')) {
- // See random_bytes_com_dotnet.php
- require_once $RandomCompatDIR . '/random_bytes_com_dotnet.php';
- }
- } catch (com_exception $e) {
- // Don't try to use it.
- }
- }
- $RandomCompat_disabled_classes = null;
- $RandomCompatCOMtest = null;
- }
-
- /**
- * throw new Exception
- */
- if (!is_callable('random_bytes')) {
- /**
- * We don't have any more options, so let's throw an exception right now
- * and hope the developer won't let it fail silently.
- *
- * @param mixed $length
- * @return void
- * @throws Exception
- */
- function random_bytes($length)
- {
- unset($length); // Suppress "variable not used" warnings.
- throw new Exception(
- 'There is no suitable CSPRNG installed on your system'
- );
- }
- }
-}
-
-if (!is_callable('random_int')) {
- require_once $RandomCompatDIR . '/random_int.php';
-}
-
-$RandomCompatDIR = null;
+++ /dev/null
-<?php
-/**
- * Random_* Compatibility Library
- * for using the new PHP 7 random_* API in PHP 5 projects
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-if (!is_callable('random_bytes')) {
- /**
- * Windows with PHP < 5.3.0 will not have the function
- * openssl_random_pseudo_bytes() available, so let's use
- * CAPICOM to work around this deficiency.
- *
- * @param int $bytes
- *
- * @throws Exception
- *
- * @return string
- */
- function random_bytes($bytes)
- {
- try {
- $bytes = RandomCompat_intval($bytes);
- } catch (TypeError $ex) {
- throw new TypeError(
- 'random_bytes(): $bytes must be an integer'
- );
- }
-
- if ($bytes < 1) {
- throw new Error(
- 'Length must be greater than 0'
- );
- }
-
- $buf = '';
- if (!class_exists('COM')) {
- throw new Error(
- 'COM does not exist'
- );
- }
- $util = new COM('CAPICOM.Utilities.1');
- $execCount = 0;
-
- /**
- * Let's not let it loop forever. If we run N times and fail to
- * get N bytes of random data, then CAPICOM has failed us.
- */
- do {
- $buf .= base64_decode($util->GetRandom($bytes, 0));
- if (RandomCompat_strlen($buf) >= $bytes) {
- /**
- * Return our random entropy buffer here:
- */
- return RandomCompat_substr($buf, 0, $bytes);
- }
- ++$execCount;
- } while ($execCount < $bytes);
-
- /**
- * If we reach here, PHP has failed us.
- */
- throw new Exception(
- 'Could not gather sufficient random data'
- );
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-/**
- * Random_* Compatibility Library
- * for using the new PHP 7 random_* API in PHP 5 projects
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-if (!defined('RANDOM_COMPAT_READ_BUFFER')) {
- define('RANDOM_COMPAT_READ_BUFFER', 8);
-}
-
-if (!is_callable('random_bytes')) {
- /**
- * Unless open_basedir is enabled, use /dev/urandom for
- * random numbers in accordance with best practices
- *
- * Why we use /dev/urandom and not /dev/random
- * @ref http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers
- *
- * @param int $bytes
- *
- * @throws Exception
- *
- * @return string
- */
- function random_bytes($bytes)
- {
- static $fp = null;
- /**
- * This block should only be run once
- */
- if (empty($fp)) {
- /**
- * We use /dev/urandom if it is a char device.
- * We never fall back to /dev/random
- */
- $fp = fopen('/dev/urandom', 'rb');
- if (!empty($fp)) {
- $st = fstat($fp);
- if (($st['mode'] & 0170000) !== 020000) {
- fclose($fp);
- $fp = false;
- }
- }
-
- if (!empty($fp)) {
- /**
- * stream_set_read_buffer() does not exist in HHVM
- *
- * If we don't set the stream's read buffer to 0, PHP will
- * internally buffer 8192 bytes, which can waste entropy
- *
- * stream_set_read_buffer returns 0 on success
- */
- if (is_callable('stream_set_read_buffer')) {
- stream_set_read_buffer($fp, RANDOM_COMPAT_READ_BUFFER);
- }
- if (is_callable('stream_set_chunk_size')) {
- stream_set_chunk_size($fp, RANDOM_COMPAT_READ_BUFFER);
- }
- }
- }
-
- try {
- $bytes = RandomCompat_intval($bytes);
- } catch (TypeError $ex) {
- throw new TypeError(
- 'random_bytes(): $bytes must be an integer'
- );
- }
-
- if ($bytes < 1) {
- throw new Error(
- 'Length must be greater than 0'
- );
- }
-
- /**
- * This if() block only runs if we managed to open a file handle
- *
- * It does not belong in an else {} block, because the above
- * if (empty($fp)) line is logic that should only be run once per
- * page load.
- */
- if (!empty($fp)) {
- /**
- * @var int
- */
- $remaining = $bytes;
-
- /**
- * @var string|bool
- */
- $buf = '';
-
- /**
- * We use fread() in a loop to protect against partial reads
- */
- do {
- /**
- * @var string|bool
- */
- $read = fread($fp, $remaining);
- if (!is_string($read)) {
- if ($read === false) {
- /**
- * We cannot safely read from the file. Exit the
- * do-while loop and trigger the exception condition
- *
- * @var string|bool
- */
- $buf = false;
- break;
- }
- }
- /**
- * Decrease the number of bytes returned from remaining
- */
- $remaining -= RandomCompat_strlen($read);
- /**
- * @var string|bool
- */
- $buf = $buf . $read;
- } while ($remaining > 0);
-
- /**
- * Is our result valid?
- */
- if (is_string($buf)) {
- if (RandomCompat_strlen($buf) === $bytes) {
- /**
- * Return our random entropy buffer here:
- */
- return $buf;
- }
- }
- }
-
- /**
- * If we reach here, PHP has failed us.
- */
- throw new Exception(
- 'Error reading from source device'
- );
- }
-}
+++ /dev/null
-<?php
-/**
- * Random_* Compatibility Library
- * for using the new PHP 7 random_* API in PHP 5 projects
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-if (!is_callable('random_bytes')) {
- /**
- * If the libsodium PHP extension is loaded, we'll use it above any other
- * solution.
- *
- * libsodium-php project:
- * @ref https://github.com/jedisct1/libsodium-php
- *
- * @param int $bytes
- *
- * @throws Exception
- *
- * @return string
- */
- function random_bytes($bytes)
- {
- try {
- $bytes = RandomCompat_intval($bytes);
- } catch (TypeError $ex) {
- throw new TypeError(
- 'random_bytes(): $bytes must be an integer'
- );
- }
-
- if ($bytes < 1) {
- throw new Error(
- 'Length must be greater than 0'
- );
- }
-
- /**
- * \Sodium\randombytes_buf() doesn't allow more than 2147483647 bytes to be
- * generated in one invocation.
- */
- if ($bytes > 2147483647) {
- $buf = '';
- for ($i = 0; $i < $bytes; $i += 1073741824) {
- $n = ($bytes - $i) > 1073741824
- ? 1073741824
- : $bytes - $i;
- $buf .= \Sodium\randombytes_buf($n);
- }
- } else {
- $buf = \Sodium\randombytes_buf($bytes);
- }
-
- if ($buf !== false) {
- if (RandomCompat_strlen($buf) === $bytes) {
- return $buf;
- }
- }
-
- /**
- * If we reach here, PHP has failed us.
- */
- throw new Exception(
- 'Could not gather sufficient random data'
- );
- }
-}
+++ /dev/null
-<?php
-/**
- * Random_* Compatibility Library
- * for using the new PHP 7 random_* API in PHP 5 projects
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-if (!is_callable('random_bytes')) {
- /**
- * If the libsodium PHP extension is loaded, we'll use it above any other
- * solution.
- *
- * libsodium-php project:
- * @ref https://github.com/jedisct1/libsodium-php
- *
- * @param int $bytes
- *
- * @throws Exception
- *
- * @return string
- */
- function random_bytes($bytes)
- {
- try {
- $bytes = RandomCompat_intval($bytes);
- } catch (TypeError $ex) {
- throw new TypeError(
- 'random_bytes(): $bytes must be an integer'
- );
- }
-
- if ($bytes < 1) {
- throw new Error(
- 'Length must be greater than 0'
- );
- }
-
- /**
- * @var string
- */
- $buf = '';
-
- /**
- * \Sodium\randombytes_buf() doesn't allow more than 2147483647 bytes to be
- * generated in one invocation.
- */
- if ($bytes > 2147483647) {
- for ($i = 0; $i < $bytes; $i += 1073741824) {
- $n = ($bytes - $i) > 1073741824
- ? 1073741824
- : $bytes - $i;
- $buf .= Sodium::randombytes_buf((int) $n);
- }
- } else {
- $buf .= Sodium::randombytes_buf((int) $bytes);
- }
-
- if (is_string($buf)) {
- if (RandomCompat_strlen($buf) === $bytes) {
- return $buf;
- }
- }
-
- /**
- * If we reach here, PHP has failed us.
- */
- throw new Exception(
- 'Could not gather sufficient random data'
- );
- }
-}
+++ /dev/null
-<?php
-/**
- * Random_* Compatibility Library
- * for using the new PHP 7 random_* API in PHP 5 projects
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-if (!is_callable('random_bytes')) {
- /**
- * Powered by ext/mcrypt (and thankfully NOT libmcrypt)
- *
- * @ref https://bugs.php.net/bug.php?id=55169
- * @ref https://github.com/php/php-src/blob/c568ffe5171d942161fc8dda066bce844bdef676/ext/mcrypt/mcrypt.c#L1321-L1386
- *
- * @param int $bytes
- *
- * @throws Exception
- *
- * @return string
- */
- function random_bytes($bytes)
- {
- try {
- $bytes = RandomCompat_intval($bytes);
- } catch (TypeError $ex) {
- throw new TypeError(
- 'random_bytes(): $bytes must be an integer'
- );
- }
-
- if ($bytes < 1) {
- throw new Error(
- 'Length must be greater than 0'
- );
- }
-
- $buf = @mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM);
- if (
- $buf !== false
- &&
- RandomCompat_strlen($buf) === $bytes
- ) {
- /**
- * Return our random entropy buffer here:
- */
- return $buf;
- }
-
- /**
- * If we reach here, PHP has failed us.
- */
- throw new Exception(
- 'Could not gather sufficient random data'
- );
- }
-}
+++ /dev/null
-<?php
-
-if (!is_callable('random_int')) {
- /**
- * Random_* Compatibility Library
- * for using the new PHP 7 random_* API in PHP 5 projects
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
- /**
- * Fetch a random integer between $min and $max inclusive
- *
- * @param int $min
- * @param int $max
- *
- * @throws Exception
- *
- * @return int
- */
- function random_int($min, $max)
- {
- /**
- * Type and input logic checks
- *
- * If you pass it a float in the range (~PHP_INT_MAX, PHP_INT_MAX)
- * (non-inclusive), it will sanely cast it to an int. If you it's equal to
- * ~PHP_INT_MAX or PHP_INT_MAX, we let it fail as not an integer. Floats
- * lose precision, so the <= and => operators might accidentally let a float
- * through.
- */
-
- try {
- $min = RandomCompat_intval($min);
- } catch (TypeError $ex) {
- throw new TypeError(
- 'random_int(): $min must be an integer'
- );
- }
-
- try {
- $max = RandomCompat_intval($max);
- } catch (TypeError $ex) {
- throw new TypeError(
- 'random_int(): $max must be an integer'
- );
- }
-
- /**
- * Now that we've verified our weak typing system has given us an integer,
- * let's validate the logic then we can move forward with generating random
- * integers along a given range.
- */
- if ($min > $max) {
- throw new Error(
- 'Minimum value must be less than or equal to the maximum value'
- );
- }
-
- if ($max === $min) {
- return (int) $min;
- }
-
- /**
- * Initialize variables to 0
- *
- * We want to store:
- * $bytes => the number of random bytes we need
- * $mask => an integer bitmask (for use with the &) operator
- * so we can minimize the number of discards
- */
- $attempts = $bits = $bytes = $mask = $valueShift = 0;
-
- /**
- * At this point, $range is a positive number greater than 0. It might
- * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to
- * a float and we will lose some precision.
- */
- $range = $max - $min;
-
- /**
- * Test for integer overflow:
- */
- if (!is_int($range)) {
-
- /**
- * Still safely calculate wider ranges.
- * Provided by @CodesInChaos, @oittaa
- *
- * @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435
- *
- * We use ~0 as a mask in this case because it generates all 1s
- *
- * @ref https://eval.in/400356 (32-bit)
- * @ref http://3v4l.org/XX9r5 (64-bit)
- */
- $bytes = PHP_INT_SIZE;
- $mask = ~0;
-
- } else {
-
- /**
- * $bits is effectively ceil(log($range, 2)) without dealing with
- * type juggling
- */
- while ($range > 0) {
- if ($bits % 8 === 0) {
- ++$bytes;
- }
- ++$bits;
- $range >>= 1;
- $mask = $mask << 1 | 1;
- }
- $valueShift = $min;
- }
-
- $val = 0;
- /**
- * Now that we have our parameters set up, let's begin generating
- * random integers until one falls between $min and $max
- */
- do {
- /**
- * The rejection probability is at most 0.5, so this corresponds
- * to a failure probability of 2^-128 for a working RNG
- */
- if ($attempts > 128) {
- throw new Exception(
- 'random_int: RNG is broken - too many rejections'
- );
- }
-
- /**
- * Let's grab the necessary number of random bytes
- */
- $randomByteString = random_bytes($bytes);
-
- /**
- * Let's turn $randomByteString into an integer
- *
- * This uses bitwise operators (<< and |) to build an integer
- * out of the values extracted from ord()
- *
- * Example: [9F] | [6D] | [32] | [0C] =>
- * 159 + 27904 + 3276800 + 201326592 =>
- * 204631455
- */
- $val &= 0;
- for ($i = 0; $i < $bytes; ++$i) {
- $val |= ord($randomByteString[$i]) << ($i * 8);
- }
-
- /**
- * Apply mask
- */
- $val &= $mask;
- $val += $valueShift;
-
- ++$attempts;
- /**
- * If $val overflows to a floating point number,
- * ... or is larger than $max,
- * ... or smaller than $min,
- * then try again.
- */
- } while (!is_int($val) || $val > $max || $val < $min);
-
- return (int) $val;
- }
-}
+++ /dev/null
-<?php
-$dist = dirname(__DIR__).'/dist';
-if (!is_dir($dist)) {
- mkdir($dist, 0755);
-}
-if (file_exists($dist.'/random_compat.phar')) {
- unlink($dist.'/random_compat.phar');
-}
-$phar = new Phar(
- $dist.'/random_compat.phar',
- FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::KEY_AS_FILENAME,
- 'random_compat.phar'
-);
-rename(
- dirname(__DIR__).'/lib/random.php',
- dirname(__DIR__).'/lib/index.php'
-);
-$phar->buildFromDirectory(dirname(__DIR__).'/lib');
-rename(
- dirname(__DIR__).'/lib/index.php',
- dirname(__DIR__).'/lib/random.php'
-);
-
-/**
- * If we pass an (optional) path to a private key as a second argument, we will
- * sign the Phar with OpenSSL.
- *
- * If you leave this out, it will produce an unsigned .phar!
- */
-if ($argc > 1) {
- if (!@is_readable($argv[1])) {
- echo 'Could not read the private key file:', $argv[1], "\n";
- exit(255);
- }
- $pkeyFile = file_get_contents($argv[1]);
-
- $private = openssl_get_privatekey($pkeyFile);
- if ($private !== false) {
- $pkey = '';
- openssl_pkey_export($private, $pkey);
- $phar->setSignatureAlgorithm(Phar::OPENSSL, $pkey);
-
- /**
- * Save the corresponding public key to the file
- */
- if (!@is_readable($dist.'/random_compat.phar.pubkey')) {
- $details = openssl_pkey_get_details($private);
- file_put_contents(
- $dist.'/random_compat.phar.pubkey',
- $details['key']
- );
- }
- } else {
- echo 'An error occurred reading the private key from OpenSSL.', "\n";
- exit(255);
- }
-}
+++ /dev/null
-<?php
-
-require_once 'lib/byte_safe_strings.php';
-require_once 'lib/cast_to_int.php';
-require_once 'lib/error_polyfill.php';
-require_once 'other/ide_stubs/libsodium.php';
-require_once 'lib/random.php';
-
-$int = random_int(0, 65536);
+++ /dev/null
-<?xml version="1.0"?>
-<psalm
- autoloader="psalm-autoload.php"
- stopOnFirstError="false"
- useDocblockTypes="true"
->
- <projectFiles>
- <directory name="lib" />
- </projectFiles>
- <issueHandlers>
- <DuplicateClass errorLevel="info" />
- <InvalidOperand errorLevel="info" />
- <UndefinedConstant errorLevel="info" />
- <MissingReturnType errorLevel="info" />
- </issueHandlers>
-</psalm>