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 2b6d96e..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');
}
@@ -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 @@ 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 @@ 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 @@ 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;
diff --git a/test/unit/StreamTest.php b/test/unit/StreamTest.php
index 9703e1e..c433fa7 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 || is_readable($filename)) {
+ $this->markTestSkipped('chmod failed to remove read access, so the test will fail; skipping');
+ return;
+ }
try {
new ReadOnlyFile($filename);