From b1e895907f47d7b43286c4d978faa2a19de228a6 Mon Sep 17 00:00:00 2001 From: djmaze Date: Wed, 10 Nov 2021 13:29:50 +0100 Subject: [PATCH] More encrypt/decrypt improvements and fixes --- .../libraries/RainLoop/Actions/Accounts.php | 2 +- .../libraries/RainLoop/Actions/UserAuth.php | 86 ++++++++++--------- .../app/libraries/RainLoop/Model/Account.php | 8 -- .../app/libraries/RainLoop/ServiceActions.php | 18 ++-- .../v/0.0.0/app/libraries/RainLoop/Utils.php | 11 ++- .../0.0.0/app/libraries/snappymail/crypt.php | 26 +++--- 6 files changed, 78 insertions(+), 73 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php index 893e760bf..6248de519 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php @@ -73,7 +73,7 @@ trait Accounts $oAccountToChange = null; if ($oAccount->Email() === $sEmailToDelete && !empty($aAccounts[$sParentEmail])) { - $oAccountToChange = $this->GetAccountFromCustomToken($aAccounts[$sParentEmail], false); + $oAccountToChange = $this->GetAccountFromCustomToken($aAccounts[$sParentEmail]); if ($oAccountToChange) { $this->SetAuthToken($oAccountToChange); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php index 6900dfa9f..c94b67599 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php @@ -14,6 +14,7 @@ trait UserAuth * @var string */ private $sSpecAuthToken = null; + private $oSpecAuthAccount = null; /** * Or use 'aes-256-xts' ? @@ -142,33 +143,14 @@ trait UserAuth /** * @throws \RainLoop\Exceptions\ClientException */ - public function GetAccountFromCustomToken(string $sToken, bool $bValidateShortToken = true, bool $bQ = false, bool $bThrowExceptionOnFalse = false): ?Account + public function GetAccountFromCustomToken(string $sToken): ?Account { - if (!empty($sToken)) { - $aToken = $bQ ? Utils::DecodeKeyValuesQ($sToken) : Utils::DecodeKeyValues($sToken); - return Account::NewInstanceFromTokenArray( + return empty($sToken) ? null + : Account::NewInstanceFromTokenArray( $this, - $bQ ? Utils::DecodeKeyValuesQ($sToken) : Utils::DecodeKeyValues($sToken), - $bThrowExceptionOnFalse + Utils::DecodeKeyValues($sToken), + false ); - } - - if ($bThrowExceptionOnFalse) { - throw new ClientException(\RainLoop\Notifications::AuthError); - } - - return null; - } - - public function GetShortLifeSpecAuthToken(int $iLife = 60): string - { - $aAccountHash = Utils::DecodeKeyValues($this->sSpecAuthToken); - if (!empty($aAccountHash[0]) && 'token' === $aAccountHash[0]) { - $aAccountHash[10] = \time() + $iLife; - return Utils::EncodeKeyValues($aAccountHash); - } - - return ''; } /** @@ -176,32 +158,56 @@ trait UserAuth */ public function getAccountFromToken(bool $bThrowExceptionOnFalse = true): ?Account { - if (!\is_string($this->sSpecAuthToken)) { - $this->sSpecAuthToken = ''; - if (isset($_COOKIE[self::AUTH_SPEC_LOGOUT_TOKEN_KEY])) { - Utils::ClearCookie(self::AUTH_SPEC_LOGOUT_TOKEN_KEY); - Utils::ClearCookie(self::AUTH_SIGN_ME_TOKEN_KEY); -// Utils::ClearCookie(self::AUTH_SPEC_TOKEN_KEY); - } else { - $sAuthAccountHash = Utils::GetCookie(self::AUTH_SPEC_TOKEN_KEY); - if (empty($sAuthAccountHash)) { - $oAccount = $this->GetAccountFromSignMeToken(); - if ($oAccount) { - $this->SetAuthToken($oAccount); - $sAuthAccountHash = $this->sSpecAuthToken; + if (!$this->oSpecAuthAccount) { + if (!\is_string($this->sSpecAuthToken)) { + $this->sSpecAuthToken = ''; + if (isset($_COOKIE[self::AUTH_SPEC_LOGOUT_TOKEN_KEY])) { + Utils::ClearCookie(self::AUTH_SPEC_LOGOUT_TOKEN_KEY); + Utils::ClearCookie(self::AUTH_SIGN_ME_TOKEN_KEY); +// Utils::ClearCookie(self::AUTH_SPEC_TOKEN_KEY); + } else { + $sAuthAccountHash = Utils::GetCookie(self::AUTH_SPEC_TOKEN_KEY); + if (empty($sAuthAccountHash)) { + $oAccount = $this->GetAccountFromSignMeToken(); + if ($oAccount) { + $this->SetAuthToken($oAccount); + } + } else { + $this->sSpecAuthToken = $sAuthAccountHash; } } - $this->sSpecAuthToken = $sAuthAccountHash ?: ''; + } + + $aData = \json_decode(\MailSo\Base\Utils::UrlSafeBase64Decode($this->sSpecAuthToken), true); + $aData = \is_array($aData) ? \SnappyMail\Crypt::Decrypt(\array_map('base64_decode', $aData)) : null; + if (!empty($aData)) { + $this->oSpecAuthAccount = Account::NewInstanceFromTokenArray( + $this, + $aData, + $bThrowExceptionOnFalse + ); + } + + if ($bThrowExceptionOnFalse && !$this->oSpecAuthAccount) { + throw new ClientException(\RainLoop\Notifications::AuthError); } } - return $this->GetAccountFromCustomToken($this->sSpecAuthToken, true, true, $bThrowExceptionOnFalse); + + return $this->oSpecAuthAccount; } public function SetAuthToken(Account $oAccount): void { - $sSpecAuthToken = $oAccount->GetAuthTokenQ(); + $sSpecAuthToken = \MailSo\Base\Utils::UrlSafeBase64Encode(\json_encode(\array_map( + 'base64_encode', + \SnappyMail\Crypt::Encrypt( + $oAccount, + APP_SALT . Utils::GetShortToken() + ) + ))); $this->sSpecAuthToken = $sSpecAuthToken; + $this->oSpecAuthAccount = null; Utils::SetCookie(self::AUTH_SPEC_TOKEN_KEY, $sSpecAuthToken); if (isset($aAccounts[$oAccount->Email()])) { diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Account.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Account.php index b1cbb6381..8c2c2f764 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Account.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Account.php @@ -174,14 +174,6 @@ class Account implements \JsonSerializable return Utils::EncodeKeyValues($this->jsonSerialize()); } - public function GetAuthTokenQ() : string - { - return Utils::EncodeKeyValuesQ($this->jsonSerialize()); - return \MailSo\Base\Utils::UrlSafeBase64Encode( - \MailSo\Base\Crypt::Encrypt(\json_encode($this), \sha1(APP_SALT)) - ); - } - public static function NewInstanceByLogin(\RainLoop\Actions $oActions, string $sEmail, string $sLogin, string $sPassword, string $sClientCert = '', bool $bThrowException = false): ?self { $oAccount = null; diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php index 93c0c3c1c..31f47efb6 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php @@ -671,19 +671,19 @@ class ServiceActions $sSsoSubData = $this->Cacher()->Get(KeyPathHelper::SsoCacherKey($sSsoHash)); if (!empty($sSsoSubData)) { - $mData = \array_map('base64_decode', \json_decode($sSsoSubData, true)); - $mData = \is_array($mData) ? \SnappyMail\Crypt::Decrypt($sSsoSubData, $sSsoHash) : null; + $aData = \json_decode($sSsoSubData, true); + $aData = \is_array($aData) ? \SnappyMail\Crypt::Decrypt(\array_map('base64_decode', $aData), $sSsoHash) : null; $this->Cacher()->Delete(KeyPathHelper::SsoCacherKey($sSsoHash)); - if (\is_array($mData) && !empty($mData['Email']) && isset($mData['Password'], $mData['Time']) && - (0 === $mData['Time'] || \time() - 10 < $mData['Time'])) + if (\is_array($aData) && !empty($aData['Email']) && isset($aData['Password'], $aData['Time']) && + (0 === $aData['Time'] || \time() - 10 < $aData['Time'])) { - $sEmail = \trim($mData['Email']); - $sPassword = $mData['Password']; + $sEmail = \trim($aData['Email']); + $sPassword = $aData['Password']; - $aAdditionalOptions = isset($mData['AdditionalOptions']) && \is_array($mData['AdditionalOptions']) && - \count($mData['AdditionalOptions']) ? $mData['AdditionalOptions'] : null; + $aAdditionalOptions = isset($aData['AdditionalOptions']) && \is_array($aData['AdditionalOptions']) && + \count($aData['AdditionalOptions']) ? $aData['AdditionalOptions'] : null; try { @@ -829,7 +829,7 @@ class ServiceActions $aAccounts = $this->oActions->GetAccounts($oAccount); if (isset($aAccounts[$sEmail])) { - $oAccountToLogin = $this->oActions->GetAccountFromCustomToken($aAccounts[$sEmail], false); + $oAccountToLogin = $this->oActions->GetAccountFromCustomToken($aAccounts[$sEmail]); } } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Utils.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Utils.php index 8e7fed8bb..b1f215d22 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Utils.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Utils.php @@ -30,13 +30,20 @@ class Utils public static function EncodeKeyValues(array $aValues, string $sCustomKey = '') : string { return \MailSo\Base\Utils::UrlSafeBase64Encode( - \MailSo\Base\Crypt::Encrypt(\json_encode($aValues), \md5(APP_SALT.$sCustomKey))); + \MailSo\Base\Crypt::Encrypt( + \json_encode($aValues), + \md5(APP_SALT.$sCustomKey) + ) + ); } public static function DecodeKeyValues(string $sEncodedValues, string $sCustomKey = '') : array { return static::unserialize( - \MailSo\Base\Crypt::Decrypt(\MailSo\Base\Utils::UrlSafeBase64Decode($sEncodedValues), \md5(APP_SALT.$sCustomKey)) + \MailSo\Base\Crypt::Decrypt( + \MailSo\Base\Utils::UrlSafeBase64Decode($sEncodedValues), + \md5(APP_SALT.$sCustomKey) + ) ); } diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/crypt.php b/snappymail/v/0.0.0/app/libraries/snappymail/crypt.php index 634c39474..ed75cab59 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/crypt.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/crypt.php @@ -78,9 +78,9 @@ abstract class Crypt public static function Decrypt(array $data, string $key = null) /* : mixed */ { if (3 === \count($data) && isset($data[0], $data[1], $data[2])) { - $fn = "\\SnappyMail\\Crypt::{$data[0]}Decrypt"; - if (\method_exists($fn)) { - return $fn($data[2], $data[1], $key); + $fn = "{$data[0]}Decrypt"; + if (\method_exists(__CLASS__, $fn)) { + return \SnappyMail\Crypt::{$fn}($data[2], $data[1], $key); } } } @@ -106,12 +106,12 @@ abstract class Crypt if (!\is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_decrypt')) { return null; } - return \json_decode(\sodium_crypto_aead_xchacha20poly1305_ietf_decrypt( + return \json_decode(\zlib_decode(\sodium_crypto_aead_xchacha20poly1305_ietf_decrypt( $data, APP_SALT, $nonce, - static::Passphrase($key) - )); + \str_pad('', \SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES, static::Passphrase($key)) + ))); } public static function SodiumEncrypt($data, string $nonce, string $key = null) : ?string @@ -120,7 +120,7 @@ abstract class Crypt return null; } return \sodium_crypto_aead_xchacha20poly1305_ietf_encrypt( - \json_encode($data), + \zlib_encode(\json_encode($data), ZLIB_ENCODING_RAW, 9), APP_SALT, $nonce, \str_pad('', \SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES, static::Passphrase($key)) @@ -132,13 +132,13 @@ abstract class Crypt if (!$data || !$iv || !static::$cipher || !\is_callable('openssl_decrypt')) { return null; } - return \json_decode(\openssl_decrypt( + return \json_decode(\zlib_decode(\openssl_decrypt( $data, static::$cipher, static::Passphrase($key), OPENSSL_RAW_DATA, $iv - ), true); + )), true); } public static function OpenSSLEncrypt($data, string $iv, string $key = null) : ?string @@ -147,7 +147,7 @@ abstract class Crypt return null; } return \openssl_encrypt( - \json_encode($data), + \zlib_encode(\json_encode($data), ZLIB_ENCODING_RAW, 9), static::$cipher, static::Passphrase($key), OPENSSL_RAW_DATA, @@ -161,10 +161,10 @@ abstract class Crypt return null; } $key = $salt . static::Passphrase($key); - return \json_decode(\is_callable('xxtea_decrypt') + return \json_decode(\zlib_decode(\is_callable('xxtea_decrypt') ? \xxtea_decrypt($data, $key) : \MailSo\Base\Xxtea::decrypt($data, $key) - , true); + ), true); } public static function XxteaEncrypt($data, string $salt, string $key = null) : ?string @@ -172,7 +172,7 @@ abstract class Crypt if (!$data || !$salt) { return null; } - $data = \json_encode($data); + $data = \zlib_encode(\json_encode($data), ZLIB_ENCODING_RAW, 9); $key = $salt . static::Passphrase($key); return \is_callable('xxtea_encrypt') ? \xxtea_encrypt($data, $key)