From 95575b8f9a32c17da37fcb301d5bdede96f3bc32 Mon Sep 17 00:00:00 2001 From: Paragon Initiative Enterprises Date: Sun, 18 Apr 2021 15:04:53 -0400 Subject: [PATCH 1/4] Migrate from Travis CI to Github Actions --- .github/workflows/ci.yml | 67 +++++++++++++++++++++++++++++ README.md | 2 +- composer.json | 2 +- psalm.xml | 1 + src/Cookie.php | 1 - src/File.php | 9 ---- src/KeyFactory.php | 6 +-- src/Password.php | 1 - src/Stream/MutableFile.php | 1 - src/Structure/MerkleTree.php | 6 +-- src/Structure/TrimmedMerkleTree.php | 1 - src/Symmetric/Crypto.php | 3 -- src/Util.php | 2 - 13 files changed, 73 insertions(+), 29 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ac0aea0 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,67 @@ +name: CI + +on: [push] + +jobs: + old: + name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} + runs-on: ${{ matrix.operating-system }} + strategy: + matrix: + operating-system: ['ubuntu-latest'] + php-versions: ['7.2', '7.3'] + phpunit-versions: ['latest'] + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: mbstring, intl, sodium + ini-values: post_max_size=256M, max_execution_time=180 + tools: psalm, phpunit:${{ matrix.phpunit-versions }} + + - name: Install dependencies + run: composer install + + - name: PHPUnit tests + uses: php-actions/phpunit@v2 + timeout-minutes: 30 + with: + memory_limit: 256M + - name: Static Analysis + run: vendor/bin/psalm + + modern: + name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} + runs-on: ${{ matrix.operating-system }} + strategy: + matrix: + operating-system: ['ubuntu-latest'] + php-versions: ['7.4', '8.0'] + phpunit-versions: ['latest'] + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: mbstring, intl, sodium + ini-values: post_max_size=256M, max_execution_time=180 + tools: psalm, phpunit:${{ matrix.phpunit-versions }} + + - name: Install dependencies + run: composer install + + - name: PHPUnit tests + uses: php-actions/phpunit@v2 + timeout-minutes: 30 + with: + memory_limit: 256M + + - name: Static Analysis + run: vendor/bin/psalm diff --git a/README.md b/README.md index 9aff86f..c935fc0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Halite -[![Build Status](https://travis-ci.org/paragonie/halite.svg?branch=master)](https://travis-ci.org/paragonie/halite) +[![Build Status](https://github.com/paragonie/halite/actions/workflows/ci.yml/badge.svg)](https://github.com/paragonie/halite/actions) [![Latest Stable Version](https://poser.pugx.org/paragonie/halite/v/stable)](https://packagist.org/packages/paragonie/halite) [![Latest Unstable Version](https://poser.pugx.org/paragonie/halite/v/unstable)](https://packagist.org/packages/paragonie/halite) [![License](https://poser.pugx.org/paragonie/halite/license)](https://packagist.org/packages/paragonie/halite) diff --git a/composer.json b/composer.json index 8b2ab44..fa03851 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "php": "^7.2|^8", "paragonie/constant_time_encoding": "^2", "paragonie/hidden-string": "^1|^2", - "paragonie/sodium_compat": "^1.14" + "paragonie/sodium_compat": "^1.15" }, "autoload": { "psr-4": { diff --git a/psalm.xml b/psalm.xml index fb40e6c..69c5b31 100644 --- a/psalm.xml +++ b/psalm.xml @@ -12,5 +12,6 @@ + diff --git a/src/Cookie.php b/src/Cookie.php index 9303e79..97991f1 100644 --- a/src/Cookie.php +++ b/src/Cookie.php @@ -119,7 +119,6 @@ protected static function getConfig(string $stored): SymmetricConfig ); } if (\hash_equals(Binary::safeSubstr($stored, 0, 5), Halite::VERSION_PREFIX)) { - /** @var string $decoded */ $decoded = Base64UrlSafe::decode($stored); return SymmetricConfig::getConfig( $decoded, diff --git a/src/File.php b/src/File.php index d72f96b..415b974 100644 --- a/src/File.php +++ b/src/File.php @@ -521,11 +521,9 @@ protected static function checksumData( while ($fileStream->remainingBytes() > 0) { // Don't go past the file size even if $config->BUFFER is not an even multiple of it: if (($fileStream->getPos() + (int) $config->BUFFER) > $size) { - /** @var int $amount_to_read */ $amount_to_read = ($size - $fileStream->getPos()); } else { // @codeCoverageIgnoreStart - /** @var int $amount_to_read */ $amount_to_read = (int) $config->BUFFER; // @codeCoverageIgnoreEnd } @@ -659,7 +657,6 @@ protected static function decryptData( ); } // Parse the header, ensuring we get 4 bytes - /** @var string $header */ $header = $input->readBytes(Halite::VERSION_TAG_LEN); // Load the config @@ -673,9 +670,7 @@ protected static function decryptData( } // Let's grab the first nonce and salt - /** @var string $firstNonce */ $firstNonce = $input->readBytes((int) $config->NONCE_BYTES); - /** @var string $hkdfSalt */ $hkdfSalt = $input->readBytes((int) $config->HKDF_SALT_LEN); // Split our keys, begin the HMAC instance @@ -1286,7 +1281,6 @@ final private static function streamDecrypt( array &$chunk_macs ): bool { $start = $input->getPos(); - /** @var int $cipher_end */ $cipher_end = $input->getSize() - (int) $config->MAC_SIZE; // Begin the streaming decryption $input->reset($start); @@ -1368,11 +1362,9 @@ final private static function streamVerify( $mac, Config $config ): array { - /** @var int $start */ $start = $input->getPos(); // Grab the stored MAC: - /** @var int $cipher_end */ $cipher_end = $input->getSize() - (int) $config->MAC_SIZE; $input->reset($cipher_end); $stored_mac = $input->readBytes((int) $config->MAC_SIZE); @@ -1402,7 +1394,6 @@ final private static function streamVerify( \sodium_crypto_generichash_update($mac, $read); $mac = (string) $mac; // Copy the hash state then store the MAC of this chunk - /** @var string $chunkMAC */ $chunkMAC = Util::safeStrcpy($mac); $chunkMACs []= \sodium_crypto_generichash_final( // @codeCoverageIgnoreStart diff --git a/src/KeyFactory.php b/src/KeyFactory.php index 0005aec..9d3d72a 100644 --- a/src/KeyFactory.php +++ b/src/KeyFactory.php @@ -168,7 +168,6 @@ public static function deriveAuthenticationKey( ); // @codeCoverageIgnoreEnd } - /** @var string $secretKey */ $secretKey = @\sodium_crypto_pwhash( \SODIUM_CRYPTO_AUTH_KEYBYTES, $password->getString(), @@ -214,8 +213,7 @@ public static function deriveEncryptionKey( ); // @codeCoverageIgnoreEnd } - /** @var string $secretKey */ - $secretKey = @\sodium_crypto_pwhash( + $secretKey = \sodium_crypto_pwhash( \SODIUM_CRYPTO_STREAM_KEYBYTES, $password->getString(), $salt, @@ -261,7 +259,6 @@ public static function deriveEncryptionKeyPair( // @codeCoverageIgnoreEnd } // Diffie Hellman key exchange key pair - /** @var string $seed */ $seed = @\sodium_crypto_pwhash( \SODIUM_CRYPTO_BOX_SEEDBYTES, $password->getString(), @@ -315,7 +312,6 @@ public static function deriveSignatureKeyPair( // @codeCoverageIgnoreEnd } // Digital signature keypair - /** @var string $seed */ $seed = @\sodium_crypto_pwhash( \SODIUM_CRYPTO_SIGN_SEEDBYTES, $password->getString(), diff --git a/src/Password.php b/src/Password.php index cacd3fa..6c0b0b7 100644 --- a/src/Password.php +++ b/src/Password.php @@ -165,7 +165,6 @@ protected static function getConfig(string $stored): SymmetricConfig || \hash_equals(Binary::safeSubstr($stored, 0, 5), Halite::VERSION_OLD_PREFIX) ) { - /** @var string $decoded */ $decoded = Base64UrlSafe::decode($stored); return SymmetricConfig::getConfig( $decoded, diff --git a/src/Stream/MutableFile.php b/src/Stream/MutableFile.php index 48050e6..acdcb9b 100644 --- a/src/Stream/MutableFile.php +++ b/src/Stream/MutableFile.php @@ -191,7 +191,6 @@ public function readBytes(int $num, bool $skipTests = false): string break; // @codeCoverageIgnoreEnd } - /** @var int $bufSize */ $bufSize = \min($remaining, self::CHUNK); /** @var string|bool $read */ $read = \fread($this->fp, $bufSize); diff --git a/src/Structure/MerkleTree.php b/src/Structure/MerkleTree.php index 1607a59..65b3e7a 100644 --- a/src/Structure/MerkleTree.php +++ b/src/Structure/MerkleTree.php @@ -225,10 +225,8 @@ protected function calculateRoot(): string ); // @codeCoverageIgnoreEnd } else { - /** @var string $curr */ - $curr = (string) ($hash[$i] ?? ''); - /** @var string $next */ - $next = (string) ($hash[$i + 1] ?? ''); + $curr = ($hash[$i] ?? ''); + $next = ($hash[$i + 1] ?? ''); $tmp[$j] = Util::raw_hash( self::MERKLE_BRANCH . $this->personalization . diff --git a/src/Structure/TrimmedMerkleTree.php b/src/Structure/TrimmedMerkleTree.php index 90a2288..a394450 100644 --- a/src/Structure/TrimmedMerkleTree.php +++ b/src/Structure/TrimmedMerkleTree.php @@ -78,7 +78,6 @@ protected function calculateRoot(): string } ++$j; } - /** @var array $hash */ $hash = $tmp; $size >>= 1; } while ($size > 1); diff --git a/src/Symmetric/Crypto.php b/src/Symmetric/Crypto.php index 9e88d65..241f3cb 100644 --- a/src/Symmetric/Crypto.php +++ b/src/Symmetric/Crypto.php @@ -69,7 +69,6 @@ public static function authenticate( Halite::HALITE_VERSION, 'auth' ); - /** @var string $mac */ $mac = self::calculateMAC( $message, $secretKey->getRawKeyMaterial(), @@ -199,7 +198,6 @@ public static function decryptWithAd( CryptoUtil::memzero($authKey); // crypto_stream_xor() can be used to encrypt and decrypt - /** @var string $plaintext */ $plaintext = \sodium_crypto_stream_xor( (string) $encrypted, (string) $nonce, @@ -283,7 +281,6 @@ public static function encryptWithAd( list($encKey, $authKey) = self::splitKeys($secretKey, $salt, $config); // Encrypt our message with the encryption key: - /** @var string $encrypted */ $encrypted = \sodium_crypto_stream_xor( $plaintext->getString(), $nonce, diff --git a/src/Util.php b/src/Util.php index 1a4d6ac..4503cbf 100644 --- a/src/Util.php +++ b/src/Util.php @@ -161,7 +161,6 @@ public static function hkdfBlake2b( $t .= $last_block; } // ORM = first L octets of T - /** @var string $orm */ $orm = Binary::safeSubstr($t, 0, $length); return $orm; } @@ -268,7 +267,6 @@ public static function safeStrcpy(string $string): string { $length = Binary::safeStrlen($string); $return = ''; - /** @var int $chunk */ $chunk = $length >> 1; if ($chunk < 1) { $chunk = 1; From 6a2f6aafb677c051be664d39a7a08842a41d489f Mon Sep 17 00:00:00 2001 From: Paragon Initiative Enterprises Date: Sun, 18 Apr 2021 15:11:51 -0400 Subject: [PATCH 2/4] Fix tests --- src/File.php | 2 +- test/unit/StreamTest.php | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/File.php b/src/File.php index 7c12b2d..9e8b5cd 100644 --- a/src/File.php +++ b/src/File.php @@ -51,7 +51,7 @@ final class File * @throws \Error * @codeCoverageIgnore */ - final private function __construct() + private function __construct() { throw new \Error('Do not instantiate'); } diff --git a/test/unit/StreamTest.php b/test/unit/StreamTest.php index 9703e1e..624d5fd 100644 --- a/test/unit/StreamTest.php +++ b/test/unit/StreamTest.php @@ -50,6 +50,11 @@ public function testUnreadableFile() $buf = random_bytes(65537); file_put_contents($filename, $buf); chmod($filename, 0000); + $perms = fileperms($filename); + if (!is_int($perms) || ($perms & 0777) !== 0) { + $this->markTestSkipped('chmod failed to remove read access, so the test will fail; skipping'); + return; + } try { new ReadOnlyFile($filename); From 68531b9729cba5614b6830f8dd7a20fd60dc8cef Mon Sep 17 00:00:00 2001 From: Paragon Initiative Enterprises Date: Sun, 18 Apr 2021 15:17:36 -0400 Subject: [PATCH 3/4] Let's troubleshoot CI --- test/unit/StreamTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/unit/StreamTest.php b/test/unit/StreamTest.php index 624d5fd..f93e110 100644 --- a/test/unit/StreamTest.php +++ b/test/unit/StreamTest.php @@ -55,6 +55,9 @@ public function testUnreadableFile() $this->markTestSkipped('chmod failed to remove read access, so the test will fail; skipping'); return; } + var_dump($perms); + $this->markTestSkipped('This is broken in CI; why?!'); + return; try { new ReadOnlyFile($filename); From b6fd5bd75bd815afa071062bdf7b7699899d4bb9 Mon Sep 17 00:00:00 2001 From: Paragon Initiative Enterprises Date: Sun, 18 Apr 2021 15:20:41 -0400 Subject: [PATCH 4/4] Skip this test if CI environment has weird perms --- test/unit/StreamTest.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/unit/StreamTest.php b/test/unit/StreamTest.php index f93e110..c433fa7 100644 --- a/test/unit/StreamTest.php +++ b/test/unit/StreamTest.php @@ -51,13 +51,10 @@ public function testUnreadableFile() file_put_contents($filename, $buf); chmod($filename, 0000); $perms = fileperms($filename); - if (!is_int($perms) || ($perms & 0777) !== 0) { + if (!is_int($perms) || ($perms & 0777) !== 0 || is_readable($filename)) { $this->markTestSkipped('chmod failed to remove read access, so the test will fail; skipping'); return; } - var_dump($perms); - $this->markTestSkipped('This is broken in CI; why?!'); - return; try { new ReadOnlyFile($filename);