From ee1c5140872553c382276bccabc4d549fc882636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Wed, 11 May 2016 19:35:31 +0300 Subject: [PATCH 01/14] Add Makefile for building .phar (WIP). --- .gitignore | 8 +++++++- composer.json | 7 ++++--- dist/Makefile | 38 ++++++++++++++++++++++++++++++++++++++ dist/box.json | 27 +++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 dist/Makefile create mode 100644 dist/box.json diff --git a/.gitignore b/.gitignore index 2d0575c..087d255 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,7 @@ -test/unit/File/big-generated-file +*~ +/test/unit/File/big-generated-file +/composer.lock +/vendor +defuse-crypto.phar +composer.phar +box.phar diff --git a/composer.json b/composer.json index 62d4f88..eafe87d 100644 --- a/composer.json +++ b/composer.json @@ -11,11 +11,12 @@ } ], "autoload": { - "files": ["autoload.php"] + "classmap": ["src"] }, "require": { - "php": ">=5.4.0", - "ext-openssl": "*" + "paragonie/random_compat": "~2.0", + "ext-openssl": "*", + "php": ">=5.4.0" }, "require-dev": { "nikic/php-parser": "^2.0" diff --git a/dist/Makefile b/dist/Makefile new file mode 100644 index 0000000..01dd2c1 --- /dev/null +++ b/dist/Makefile @@ -0,0 +1,38 @@ +TARGETS := defuse-crypto.phar + +define install_tool + @echo 'Required tool \"$1\" not installed, see docs/InstallingAndVerifying.md'; false +endef +define which + $(shell PATH=$$PATH:. which $1.phar 2>/dev/null || which $1 2>/dev/null || { echo "$(call install_tool,$(1))"; }) +endef +define find_tool + $(call which,$1) +endef + +box := $(call find_tool,box) +composer := $(call find_tool,composer) +php := $(call find_tool,php) + +all: dist-phar + +phar: $(TARGETS) + +composer.lock: + $(composer) install --no-dev + +%.phar: dist/Makefile dist/box.json composer.lock + cp dist/box.json . + $(php) -d phar.readonly=0 $(box) build -c box.json -v + +# ensure we run in clean tree. export git tree and run there. +dist-phar: + rm -rf worktree + install -d worktree + (cd $(CURDIR)/..; git archive HEAD) | tar -x -C worktree + $(MAKE) -f $(CURDIR)/Makefile -C worktree phar + mv worktree/*.phar . + rm -rf worktree + +clean: + rm -vf $(TARGETS) diff --git a/dist/box.json b/dist/box.json new file mode 100644 index 0000000..9acd514 --- /dev/null +++ b/dist/box.json @@ -0,0 +1,27 @@ +{ + "chmod": "0755", + "finder": [ + { + "in": "src", + "name": "*.php", + "exclude": "random_compat" + }, + { + "in": "vendor/composer", + "name": "*.php", + "exclude": "other" + }, + { + "in": "vendor/paragonie", + "name": "*.php", + "exclude": "other" + } + ], + "compactors": [ + "Herrera\\Box\\Compactor\\Php" + ], + "compression": "GZ", + "main": "vendor/autoload.php", + "output": "defuse-crypto.phar", + "stub": true +} From 7439b87fa667c84cda0112b3acd4ab9fa31f5d4e Mon Sep 17 00:00:00 2001 From: Taylor Hornby Date: Sun, 15 May 2016 12:33:13 -0600 Subject: [PATCH 02/14] Finish .phar-building Makefile and update docs. --- dist/Makefile | 51 ++++++++++++++++----------------- dist/defuse-crypto.phar | Bin 124302 -> 0 bytes dist/defuse-crypto.phar.sig | 17 ----------- {other => dist}/signingkey.asc | 0 docs/InstallingAndVerifying.md | 28 +++++++++--------- docs/InternalDeveloperDocs.md | 28 +++++++++++++++--- other/build-phar.sh | 10 ------- other/build_phar.php | 51 --------------------------------- other/sign-release.sh | 25 ---------------- 9 files changed, 62 insertions(+), 148 deletions(-) delete mode 100644 dist/defuse-crypto.phar delete mode 100644 dist/defuse-crypto.phar.sig rename {other => dist}/signingkey.asc (100%) delete mode 100755 other/build-phar.sh delete mode 100755 other/build_phar.php delete mode 100755 other/sign-release.sh diff --git a/dist/Makefile b/dist/Makefile index 01dd2c1..fc9a125 100644 --- a/dist/Makefile +++ b/dist/Makefile @@ -1,38 +1,37 @@ -TARGETS := defuse-crypto.phar +# 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. -define install_tool - @echo 'Required tool \"$1\" not installed, see docs/InstallingAndVerifying.md'; false -endef -define which - $(shell PATH=$$PATH:. which $1.phar 2>/dev/null || which $1 2>/dev/null || { echo "$(call install_tool,$(1))"; }) -endef -define find_tool - $(call which,$1) -endef +box := $(shell which box) +composer := "composer" -box := $(call find_tool,box) -composer := $(call find_tool,composer) -php := $(call find_tool,php) +.PHONY: all +all: sign-phar -all: dist-phar - -phar: $(TARGETS) - -composer.lock: - $(composer) install --no-dev - -%.phar: dist/Makefile dist/box.json composer.lock - cp dist/box.json . - $(php) -d phar.readonly=0 $(box) build -c box.json -v +.PHONY: sign-phar +sign-phar: build-phar + gpg -u 7B4B2D98 --armor --output dist/defuse-crypto.phar.sig --detach-sig dist/defuse-crypto.phar # ensure we run in clean tree. export git tree and run there. -dist-phar: +.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 phar + $(MAKE) -f $(CURDIR)/Makefile -C worktree defuse-crypto.phar mv worktree/*.phar . rm -rf worktree +.PHONY: clean clean: - rm -vf $(TARGETS) + rm -vf defuse-crypto.phar + +# 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 + diff --git a/dist/defuse-crypto.phar b/dist/defuse-crypto.phar deleted file mode 100644 index b9986f43db260a0f5caef2f558c5365daaf06a43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 124302 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 diff --git a/dist/defuse-crypto.phar.sig b/dist/defuse-crypto.phar.sig deleted file mode 100644 index c935db2..0000000 --- a/dist/defuse-crypto.phar.sig +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN PGP SIGNATURE----- -Version: GnuPG v2 - -iQIcBAABCAAGBQJXHEa0AAoJEDhe4FWhKRU4ZS8QAJzo8p9K7UqGyiRMHd4rsV7f -e3fUlr9YHnvrxHKtxBeY/817+lng8d8yc3PdLQHnmUDB04zEbeh+U95ZYZT3Jm9W -6fU8y8n5EzzxZBbkDGeDM+ds/BZZeGjNfl9HUz2huERwNueiASOmpUYLFaWiGziw -ivDiVWISfFnxU9IIeWUUX8wbRO3kL23AuNsRHXnjaL8OWLdZHg+562QaBP8l/vGp -JXSGdknIzb0hHTQgrk/fqMl8S2eHzgl/ZK6xI15RUPWQ85L4wIno5rkeD0jSdPuu -5EoQoBkcdC5H88dBcd6zhnAcBgBRl715jvLbZU+Jr3CQEGWD41MRRSwa7sAPYR1l -MjUdwvU4MVK/VHBbRD2JlgCrR/HXchfWpYwdSIx+2UcF8wEH5nqqRPAi3LTCN+bu -qA6CD9/rQh2dlYEZgq60VoDiXKHPM6kPcpyyIC/iaYgfif4M4reBDu9aVCCmYmwk -33oQ0S+nu0+q7IaQrIY8ohD7lguVloYTqhdysUrWQaEYo+uKaCz6WlZuhJz3RrhX -u5lNTeajWKXhPGhBfE0prQ6kdGfMGEb5iV9JxzM1f/9nq2ow5fg1QtBmKcegNP52 -FKU29BM6DU82dOUckJY+KICgQ2XWka+Laqj1x26a2Hml+xx3eiSmHQjO+v6CvZfo -Am8gGvNnyc6DgDKXVXzN -=BZoQ ------END PGP SIGNATURE----- diff --git a/other/signingkey.asc b/dist/signingkey.asc similarity index 100% rename from other/signingkey.asc rename to dist/signingkey.asc diff --git a/docs/InstallingAndVerifying.md b/docs/InstallingAndVerifying.md index 640a3b0..0b28782 100644 --- a/docs/InstallingAndVerifying.md +++ b/docs/InstallingAndVerifying.md @@ -23,16 +23,21 @@ 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. Simply check out the tag with the -version you want, for example for version 2.0.0 you would do: +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. -``` -git checkout v2.0.0 -``` +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). -You'll find the `.phar` file for that release in `dist/defuse-crypto.phar`. -Install it to somewhere on your filesystem, e.g. -`/var/www/lib/defuse-crypto.phar`. You can now use it in your code like this: +Once you have verified the signature, it is safe to use the `.phar`. Install it +to somewhere on your file system, e.g. `/var/www/lib/defuse-crypto.phar`, and +then you can use it like this: ```php ``` -Run the sign-release script (note this will make a commit on your current -branch): +Generate and sign the `.phar`: ``` -./other/sign-release.sh +cd dist +make ``` -Push the branch and tag up to GitHub. +Tag the release: + +``` +git -c user.signingkey=7B4B2D98 tag -s "" -m "" +``` + +`` should be in the format `v2.0.0` and `` 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. diff --git a/other/build-phar.sh b/other/build-phar.sh deleted file mode 100755 index aa20926..0000000 --- a/other/build-phar.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -basedir=$( dirname $( readlink -f ${BASH_SOURCE[0]} ) ) - -cp ./autoload.php ./src/index.php -sed -i "s/__DIR__.'\/src\/'/'phar:\/\/defuse-crypto.phar\/'/" ./src/index.php - -php -dphar.readonly=0 "$basedir/build_phar.php" $* - -rm ./src/index.php diff --git a/other/build_phar.php b/other/build_phar.php deleted file mode 100755 index f9bfea0..0000000 --- a/other/build_phar.php +++ /dev/null @@ -1,51 +0,0 @@ -buildFromDirectory(dirname(__DIR__) . '/src'); - -/** - * 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 . '/defuse-crypto.phar.pubkey')) { - $details = \openssl_pkey_get_details($private); - \file_put_contents( - $dist . '/defuse-crypto.phar.pubkey', - $details['key'] - ); - } - } else { - echo 'An error occurred reading the private key from OpenSSL.', "\n"; - exit(255); - } -} diff --git a/other/sign-release.sh b/other/sign-release.sh deleted file mode 100755 index 0455e18..0000000 --- a/other/sign-release.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -set -e - -current_repo_has_unsaved_changes () { - if git diff --exit-code && ! [[ $(git clean -dfx --dry-run) ]]; then - return 1 - else - return 0 - fi -} - -if current_repo_has_unsaved_changes; then - echo "It's best to run this from a fresh clone." - exit 1 -fi - -./other/build-phar.sh -./test.sh dist/defuse-crypto.phar -gpg -u 7B4B2D98 --armor --output dist/defuse-crypto.phar.sig --detach-sig dist/defuse-crypto.phar - -git add dist -git commit -m "Automatic commit of dist/" - -git -c user.signingkey=7B4B2D98 tag -s "$1" -m "$2" From b769781c7db43d9b0a0e0adbe98868718c0a7479 Mon Sep 17 00:00:00 2001 From: Taylor Hornby Date: Sun, 15 May 2016 13:36:59 -0600 Subject: [PATCH 03/14] Delete autoload.php and random_compat --- autoload.php | 68 ------ src/random_compat/byte_safe_strings.php | 181 ---------------- src/random_compat/cast_to_int.php | 71 ------- src/random_compat/error_polyfill.php | 42 ---- src/random_compat/random.php | 197 ------------------ src/random_compat/random_bytes_com_dotnet.php | 81 ------- .../random_bytes_dev_urandom.php | 148 ------------- src/random_compat/random_bytes_libsodium.php | 86 -------- .../random_bytes_libsodium_legacy.php | 86 -------- src/random_compat/random_bytes_mcrypt.php | 76 ------- src/random_compat/random_int.php | 191 ----------------- 11 files changed, 1227 deletions(-) delete mode 100644 autoload.php delete mode 100644 src/random_compat/byte_safe_strings.php delete mode 100644 src/random_compat/cast_to_int.php delete mode 100644 src/random_compat/error_polyfill.php delete mode 100644 src/random_compat/random.php delete mode 100644 src/random_compat/random_bytes_com_dotnet.php delete mode 100644 src/random_compat/random_bytes_dev_urandom.php delete mode 100644 src/random_compat/random_bytes_libsodium.php delete mode 100644 src/random_compat/random_bytes_libsodium_legacy.php delete mode 100644 src/random_compat/random_bytes_mcrypt.php delete mode 100644 src/random_compat/random_int.php diff --git a/autoload.php b/autoload.php deleted file mode 100644 index a13aea2..0000000 --- a/autoload.php +++ /dev/null @@ -1,68 +0,0 @@ - autoloader -> LFI hardening - */ - $classmap = array( - 'Core' => - 'Core.php', - 'Crypto' => - 'Crypto.php', - 'DerivedKeys' => - 'DerivedKeys.php', - 'Encoding' => - 'Encoding.php', - 'ExceptionHandler' => - 'ExceptionHandler.php', - 'File' => - 'File.php', - 'Key' => - 'Key.php', - 'KeyOrPassword' => - 'KeyOrPassword.php', - 'KeyProtectedByPassword' => - 'KeyProtectedByPassword.php', - 'Salt' => - 'Salt.php', - 'RuntimeTests' => - 'RuntimeTests.php', - // Exceptions: - 'Exception\\BadFormatException' => - 'Exception/BadFormatException.php', - 'Exception\\EnvironmentIsBrokenException' => - 'Exception/EnvironmentIsBrokenException.php', - 'Exception\\CryptoException' => - 'Exception/CryptoException.php', - 'Exception\\IOException' => - 'Exception/IOException.php', - 'Exception\\WrongKeyOrModifiedCiphertextException' => - 'Exception/WrongKeyOrModifiedCiphertextException.php', - ); - foreach ($classmap as $classname => $file) { - if ($classname === $relative_class) { - require $base_dir.$file; - } - } -}); diff --git a/src/random_compat/byte_safe_strings.php b/src/random_compat/byte_safe_strings.php deleted file mode 100644 index dec5d30..0000000 --- a/src/random_compat/byte_safe_strings.php +++ /dev/null @@ -1,181 +0,0 @@ - RandomCompat_strlen($binary_string)) { - return false; - } - - return 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 substr($binary_string, $start, $length); - } - - return substr($binary_string, $start); - } - } -} diff --git a/src/random_compat/cast_to_int.php b/src/random_compat/cast_to_int.php deleted file mode 100644 index f441c5d..0000000 --- a/src/random_compat/cast_to_int.php +++ /dev/null @@ -1,71 +0,0 @@ - operators might accidentally let a float - * through. - * - * @param int|float $number The number we want to convert to an int - * @param boolean $fail_open Set to true to not throw an exception - * - * @return int (or float if $fail_open) - * - * @throws TypeError - */ - function RandomCompat_intval($number, $fail_open = false) - { - if (is_numeric($number)) { - $number += 0; - } - - if ( - is_float($number) - && - $number > ~PHP_INT_MAX - && - $number < PHP_INT_MAX - ) { - $number = (int) $number; - } - - if (is_int($number) || $fail_open) { - return $number; - } - - throw new TypeError( - 'Expected an integer.' - ); - } -} diff --git a/src/random_compat/error_polyfill.php b/src/random_compat/error_polyfill.php deleted file mode 100644 index 57cfefd..0000000 --- a/src/random_compat/error_polyfill.php +++ /dev/null @@ -1,42 +0,0 @@ -GetRandom() - * 5. openssl_random_pseudo_bytes() (absolute last resort) - * - * See ERRATA.md for our reasoning behind this particular order - */ - if (extension_loaded('libsodium')) { - // See random_bytes_libsodium.php - if (PHP_VERSION_ID >= 50300 && function_exists('\\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 ( - !function_exists('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() - */ - if ( - !function_exists('random_bytes') - && - PHP_VERSION_ID >= 50307 - && - extension_loaded('mcrypt') - && - (DIRECTORY_SEPARATOR !== '/' || $RandomCompatUrandom) - ) { - // Prevent this code from hanging indefinitely on non-Windows; - // see https://bugs.php.net/bug.php?id=69833 - if ( - DIRECTORY_SEPARATOR !== '/' || - (PHP_VERSION_ID <= 50609 || PHP_VERSION_ID >= 50613) - ) { - // See random_bytes_mcrypt.php - require_once $RandomCompatDIR.'/random_bytes_mcrypt.php'; - } - } - $RandomCompatUrandom = null; - - if ( - !function_exists('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 (!function_exists('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. - */ - function random_bytes($length) - { - throw new Exception( - 'There is no suitable CSPRNG installed on your system' - ); - } - } - } - - if (!function_exists('random_int')) { - require_once $RandomCompatDIR.'/random_int.php'; - } - - $RandomCompatDIR = null; -} diff --git a/src/random_compat/random_bytes_com_dotnet.php b/src/random_compat/random_bytes_com_dotnet.php deleted file mode 100644 index 3422825..0000000 --- a/src/random_compat/random_bytes_com_dotnet.php +++ /dev/null @@ -1,81 +0,0 @@ -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' - ); -} diff --git a/src/random_compat/random_bytes_dev_urandom.php b/src/random_compat/random_bytes_dev_urandom.php deleted file mode 100644 index db93b07..0000000 --- a/src/random_compat/random_bytes_dev_urandom.php +++ /dev/null @@ -1,148 +0,0 @@ - 0); - - /** - * Is our result valid? - */ - if ($buf !== false) { - 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' - ); -} diff --git a/src/random_compat/random_bytes_libsodium.php b/src/random_compat/random_bytes_libsodium.php deleted file mode 100644 index f802d4e..0000000 --- a/src/random_compat/random_bytes_libsodium.php +++ /dev/null @@ -1,86 +0,0 @@ - 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' - ); -} diff --git a/src/random_compat/random_bytes_libsodium_legacy.php b/src/random_compat/random_bytes_libsodium_legacy.php deleted file mode 100644 index 44fddbf..0000000 --- a/src/random_compat/random_bytes_libsodium_legacy.php +++ /dev/null @@ -1,86 +0,0 @@ - 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' - ); -} diff --git a/src/random_compat/random_bytes_mcrypt.php b/src/random_compat/random_bytes_mcrypt.php deleted file mode 100644 index 7ac9d91..0000000 --- a/src/random_compat/random_bytes_mcrypt.php +++ /dev/null @@ -1,76 +0,0 @@ - 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 $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; - } - - /** - * 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); - if ($randomByteString === false) { - throw new Exception( - 'Random number generator failure' - ); - } - - /** - * 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; -} From c1ac25989bcf57fb4f833d843f9d96213f6f96a7 Mon Sep 17 00:00:00 2001 From: Taylor Hornby Date: Sun, 15 May 2016 13:43:26 -0600 Subject: [PATCH 04/14] Require composer for running tests and document it. --- .gitignore | 3 +++ docs/InstallingAndVerifying.md | 15 +++++++-------- docs/InternalDeveloperDocs.md | 6 ++++-- test.sh | 2 +- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 087d255..53b64ad 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ defuse-crypto.phar composer.phar box.phar +phpunit.phar +phpunit.phar.asc +test/unit/File/tmp diff --git a/docs/InstallingAndVerifying.md b/docs/InstallingAndVerifying.md index 0b28782..e68f762 100644 --- a/docs/InstallingAndVerifying.md +++ b/docs/InstallingAndVerifying.md @@ -4,7 +4,7 @@ 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. +2. `require_once` a single `.phar` file in your application. Option 1: Using Composer ------------------------- @@ -23,21 +23,20 @@ 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 +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 +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`. Install it -to somewhere on your file system, e.g. `/var/www/lib/defuse-crypto.phar`, and -then you can use it like this: +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 Date: Sun, 15 May 2016 13:44:55 -0600 Subject: [PATCH 05/14] Add comment to save users some time --- test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/test.sh b/test.sh index c134d21..ace184f 100755 --- a/test.sh +++ b/test.sh @@ -10,6 +10,7 @@ fi if [ -n "$1" ]; then BOOTSTRAP="$1" else + # You need to run `composer install` to generate this file. BOOTSTRAP="vendor/autoload.php" fi From d95073d57a4bc65c7aa0b7035d19dd70c3d1216e Mon Sep 17 00:00:00 2001 From: Taylor Hornby Date: Sun, 15 May 2016 13:55:39 -0600 Subject: [PATCH 06/14] Clean up the .sig file. --- dist/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/Makefile b/dist/Makefile index fc9a125..8f43432 100644 --- a/dist/Makefile +++ b/dist/Makefile @@ -24,7 +24,7 @@ build-phar: .PHONY: clean clean: - rm -vf defuse-crypto.phar + rm -vf defuse-crypto.phar defuse-crypto.phar.sig # Inside workdir/: From a3b1981fc4d8ca3a70442b0c27e26d46a357b1b2 Mon Sep 17 00:00:00 2001 From: Taylor Hornby Date: Sun, 15 May 2016 14:03:23 -0600 Subject: [PATCH 07/14] Fix my email in composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index eafe87d..65c5905 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "authors": [ { "name": "Taylor Hornby", - "email": "havoc@defuse.ca" + "email": "taylor@defuse.ca" } ], "autoload": { From 64c38854d3ab0cb9c3538adbc51eae2bdfc53994 Mon Sep 17 00:00:00 2001 From: Taylor Hornby Date: Sun, 15 May 2016 14:10:06 -0600 Subject: [PATCH 08/14] Add Scott to the composer.json author list. --- composer.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 65c5905..c4ae44b 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,13 @@ "authors": [ { "name": "Taylor Hornby", - "email": "taylor@defuse.ca" + "email": "taylor@defuse.ca", + "homepage": "https://defuse.ca/" + }, + { + "name": "Scott Arciszewski", + "email": "info@paragonie.com", + "homepage": "https://paragonie.com" } ], "autoload": { From 45168a8f61c896b293405ec058813b13495ce8f6 Mon Sep 17 00:00:00 2001 From: Taylor Hornby Date: Sun, 15 May 2016 15:59:03 -0600 Subject: [PATCH 09/14] Add composer install to .travis.yml --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index db09559..06ba31a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,4 +10,8 @@ sudo: false matrix: fast_finish: true -script: ./test.sh +install: + - composer install + +script: + - ./test.sh From 0119106671a233199ba06ecf1d2c7c0e72875fa8 Mon Sep 17 00:00:00 2001 From: Taylor Hornby Date: Mon, 16 May 2016 10:06:56 -0600 Subject: [PATCH 10/14] Disable compression and remove unneeded exclusion of random_compat --- dist/box.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dist/box.json b/dist/box.json index 9acd514..01ed82b 100644 --- a/dist/box.json +++ b/dist/box.json @@ -3,8 +3,7 @@ "finder": [ { "in": "src", - "name": "*.php", - "exclude": "random_compat" + "name": "*.php" }, { "in": "vendor/composer", @@ -20,7 +19,6 @@ "compactors": [ "Herrera\\Box\\Compactor\\Php" ], - "compression": "GZ", "main": "vendor/autoload.php", "output": "defuse-crypto.phar", "stub": true From cc3249e9eefe0070cfd6c973ab1be82eb4aa6f55 Mon Sep 17 00:00:00 2001 From: Taylor Hornby Date: Mon, 16 May 2016 10:16:17 -0600 Subject: [PATCH 11/14] Fix paths in GPG command. --- dist/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/Makefile b/dist/Makefile index 8f43432..15b2494 100644 --- a/dist/Makefile +++ b/dist/Makefile @@ -9,7 +9,7 @@ all: sign-phar .PHONY: sign-phar sign-phar: build-phar - gpg -u 7B4B2D98 --armor --output dist/defuse-crypto.phar.sig --detach-sig dist/defuse-crypto.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 From 6576cf526844566f9f3abf2837b433c2a1e57da5 Mon Sep 17 00:00:00 2001 From: Taylor Hornby Date: Mon, 16 May 2016 10:20:24 -0600 Subject: [PATCH 12/14] Add phar building to Travis-CI --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 06ba31a..2fcff88 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,11 @@ matrix: install: - composer install + - curl -LSs https://box-project.github.io/box2/installer.php | php + - mkdir ~/box + - mv box.phar ~/box/box script: - ./test.sh + - PATH=$PATH:~/box/ make -C dist/ build-phar + - ./test.sh dist/defuse-crypto.phar From f7f1c88e3cd38d88ff108a4d688eb3d315b09877 Mon Sep 17 00:00:00 2001 From: Taylor Hornby Date: Mon, 16 May 2016 10:20:54 -0600 Subject: [PATCH 13/14] Add signed phar to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 53b64ad..987c516 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /composer.lock /vendor defuse-crypto.phar +defuse-crypto.phar.sig composer.phar box.phar phpunit.phar From e42d3a9ffe924ba005200bbdb4bed675c278954c Mon Sep 17 00:00:00 2001 From: Taylor Hornby Date: Mon, 16 May 2016 13:21:06 -0600 Subject: [PATCH 14/14] Remove unnecessary box.json line --- dist/box.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dist/box.json b/dist/box.json index 01ed82b..fe74393 100644 --- a/dist/box.json +++ b/dist/box.json @@ -7,8 +7,7 @@ }, { "in": "vendor/composer", - "name": "*.php", - "exclude": "other" + "name": "*.php" }, { "in": "vendor/paragonie",