diff --git a/CHANGELOG.md b/CHANGELOG.md index 1779361..9c449db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Version 4.0.3 (2018-01-30) + +* [#72](https://github.com/paragonie/halite/issues/72): Fixed forward-compatibility with + libsodium 1.0.15. + ## Version 4.0.2 (2017-12-08) This is mostly a boyscouting/documentation release. However, we now pass Psalm under the diff --git a/src/KeyFactory.php b/src/KeyFactory.php index 9a71bda..877cb66 100644 --- a/src/KeyFactory.php +++ b/src/KeyFactory.php @@ -135,7 +135,7 @@ public static function deriveAuthenticationKey( string $salt, string $level = self::INTERACTIVE ): AuthenticationKey { - $kdfLimits = self::getSecurityLevels($level); + $kdfLimits = self::getSecurityLevels($level, true); // VERSION 2+ (argon2) if (Util::safeStrlen($salt) !== \SODIUM_CRYPTO_PWHASH_SALTBYTES) { throw new InvalidSalt( @@ -147,7 +147,8 @@ public static function deriveAuthenticationKey( $password->getString(), $salt, $kdfLimits[0], - $kdfLimits[1] + $kdfLimits[1], + \SODIUM_CRYPTO_PWHASH_ALG_ARGON2I13 ); return new AuthenticationKey( new HiddenString($secretKey) @@ -172,7 +173,7 @@ public static function deriveEncryptionKey( string $salt, string $level = self::INTERACTIVE ): EncryptionKey { - $kdfLimits = self::getSecurityLevels($level); + $kdfLimits = self::getSecurityLevels($level, true); // VERSION 2+ (argon2) if (Util::safeStrlen($salt) !== \SODIUM_CRYPTO_PWHASH_SALTBYTES) { throw new InvalidSalt( @@ -184,7 +185,8 @@ public static function deriveEncryptionKey( $password->getString(), $salt, $kdfLimits[0], - $kdfLimits[1] + $kdfLimits[1], + \SODIUM_CRYPTO_PWHASH_ALG_ARGON2I13 ); return new EncryptionKey( new HiddenString($secretKey) @@ -208,7 +210,7 @@ public static function deriveEncryptionKeyPair( string $salt, string $level = self::INTERACTIVE ): EncryptionKeyPair { - $kdfLimits = self::getSecurityLevels($level); + $kdfLimits = self::getSecurityLevels($level, true); // VERSION 2+ (argon2) if (Util::safeStrlen($salt) !== \SODIUM_CRYPTO_PWHASH_SALTBYTES) { throw new InvalidSalt( @@ -221,7 +223,8 @@ public static function deriveEncryptionKeyPair( $password->getString(), $salt, $kdfLimits[0], - $kdfLimits[1] + $kdfLimits[1], + \SODIUM_CRYPTO_PWHASH_ALG_ARGON2I13 ); $keyPair = \sodium_crypto_box_seed_keypair($seed); $secretKey = \sodium_crypto_box_secretkey($keyPair); @@ -253,7 +256,7 @@ public static function deriveSignatureKeyPair( string $salt, string $level = self::INTERACTIVE ): SignatureKeyPair { - $kdfLimits = self::getSecurityLevels($level); + $kdfLimits = self::getSecurityLevels($level, true); // VERSION 2+ (argon2) if (Util::safeStrlen($salt) !== \SODIUM_CRYPTO_PWHASH_SALTBYTES) { throw new InvalidSalt( @@ -266,7 +269,8 @@ public static function deriveSignatureKeyPair( $password->getString(), $salt, $kdfLimits[0], - $kdfLimits[1] + $kdfLimits[1], + \SODIUM_CRYPTO_PWHASH_ALG_ARGON2I13 ); $keyPair = \sodium_crypto_sign_seed_keypair($seed); $secretKey = \sodium_crypto_sign_secretkey($keyPair); @@ -280,27 +284,38 @@ public static function deriveSignatureKeyPair( ); } + /** * Returns a 2D array [OPSLIMIT, MEMLIMIT] for the appropriate security level. * * @param string $level + * @param bool $compat * @return int[] * @throws InvalidType */ - public static function getSecurityLevels(string $level = self::INTERACTIVE): array + public static function getSecurityLevels(string $level = self::INTERACTIVE, bool $compat = false): array { switch ($level) { case self::INTERACTIVE: + if ($compat) { + return [4, 33554432]; + } return [ \SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, \SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE ]; case self::MODERATE: + if ($compat) { + return [6, 134217728]; + } return [ \SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE, \SODIUM_CRYPTO_PWHASH_MEMLIMIT_MODERATE ]; case self::SENSITIVE: + if ($compat) { + return [8, 536870912]; + } return [ \SODIUM_CRYPTO_PWHASH_OPSLIMIT_SENSITIVE, \SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE diff --git a/src/Password.php b/src/Password.php index 85e7d19..312817c 100644 --- a/src/Password.php +++ b/src/Password.php @@ -3,6 +3,7 @@ namespace ParagonIE\Halite; use ParagonIE\ConstantTime\Base64UrlSafe; +use ParagonIE\ConstantTime\Binary; use ParagonIE\ConstantTime\Hex; use ParagonIE\Halite\Alerts\{ CannotPerformOperation, @@ -108,7 +109,10 @@ public static function needsRehash( // Upon successful decryption, verify that we're using Argon2i if (!\hash_equals( Util::safeSubstr($hash_str, 0, 9), - \SODIUM_CRYPTO_PWHASH_STRPREFIX + '$argon2i$' + ) && !\hash_equals( + Util::safeSubstr($hash_str, 0, 10), + '$argon2id$' )) { return true; } @@ -119,16 +123,25 @@ public static function needsRehash( return !\hash_equals( '$argon2i$v=19$m=32768,t=4,p=1$', Util::safeSubstr($hash_str, 0, 30) + ) && !\hash_equals( + '$argon2id$v=19$m=65536,t=2,p=1$', + Util::safeSubstr($hash_str, 0, 31) ); case KeyFactory::MODERATE: return !\hash_equals( '$argon2i$v=19$m=131072,t=6,p=1$', Util::safeSubstr($hash_str, 0, 31) + ) && !\hash_equals( + '$argon2id$v=19$m=262144,t=3,p=1$', + Util::safeSubstr($hash_str, 0, 32) ); case KeyFactory::SENSITIVE: return !\hash_equals( '$argon2i$v=19$m=524288,t=8,p=1$', Util::safeSubstr($hash_str, 0, 31) + ) && !\hash_equals( + '$argon2id$v=19$m=1048576,t=4,p=1', + Util::safeSubstr($hash_str, 0, 33) ); default: return true;