More encrypt/decrypt improvements and fixes

This commit is contained in:
djmaze 2021-11-10 13:29:50 +01:00
parent 59cb0978c7
commit b1e895907f
6 changed files with 78 additions and 73 deletions

View file

@ -73,7 +73,7 @@ trait Accounts
$oAccountToChange = null; $oAccountToChange = null;
if ($oAccount->Email() === $sEmailToDelete && !empty($aAccounts[$sParentEmail])) { if ($oAccount->Email() === $sEmailToDelete && !empty($aAccounts[$sParentEmail])) {
$oAccountToChange = $this->GetAccountFromCustomToken($aAccounts[$sParentEmail], false); $oAccountToChange = $this->GetAccountFromCustomToken($aAccounts[$sParentEmail]);
if ($oAccountToChange) { if ($oAccountToChange) {
$this->SetAuthToken($oAccountToChange); $this->SetAuthToken($oAccountToChange);
} }

View file

@ -14,6 +14,7 @@ trait UserAuth
* @var string * @var string
*/ */
private $sSpecAuthToken = null; private $sSpecAuthToken = null;
private $oSpecAuthAccount = null;
/** /**
* Or use 'aes-256-xts' ? * Or use 'aes-256-xts' ?
@ -142,33 +143,14 @@ trait UserAuth
/** /**
* @throws \RainLoop\Exceptions\ClientException * @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)) { return empty($sToken) ? null
$aToken = $bQ ? Utils::DecodeKeyValuesQ($sToken) : Utils::DecodeKeyValues($sToken); : Account::NewInstanceFromTokenArray(
return Account::NewInstanceFromTokenArray(
$this, $this,
$bQ ? Utils::DecodeKeyValuesQ($sToken) : Utils::DecodeKeyValues($sToken), Utils::DecodeKeyValues($sToken),
$bThrowExceptionOnFalse 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 public function getAccountFromToken(bool $bThrowExceptionOnFalse = true): ?Account
{ {
if (!\is_string($this->sSpecAuthToken)) { if (!$this->oSpecAuthAccount) {
$this->sSpecAuthToken = ''; if (!\is_string($this->sSpecAuthToken)) {
if (isset($_COOKIE[self::AUTH_SPEC_LOGOUT_TOKEN_KEY])) { $this->sSpecAuthToken = '';
Utils::ClearCookie(self::AUTH_SPEC_LOGOUT_TOKEN_KEY); if (isset($_COOKIE[self::AUTH_SPEC_LOGOUT_TOKEN_KEY])) {
Utils::ClearCookie(self::AUTH_SIGN_ME_TOKEN_KEY); Utils::ClearCookie(self::AUTH_SPEC_LOGOUT_TOKEN_KEY);
// Utils::ClearCookie(self::AUTH_SPEC_TOKEN_KEY); Utils::ClearCookie(self::AUTH_SIGN_ME_TOKEN_KEY);
} else { // Utils::ClearCookie(self::AUTH_SPEC_TOKEN_KEY);
$sAuthAccountHash = Utils::GetCookie(self::AUTH_SPEC_TOKEN_KEY); } else {
if (empty($sAuthAccountHash)) { $sAuthAccountHash = Utils::GetCookie(self::AUTH_SPEC_TOKEN_KEY);
$oAccount = $this->GetAccountFromSignMeToken(); if (empty($sAuthAccountHash)) {
if ($oAccount) { $oAccount = $this->GetAccountFromSignMeToken();
$this->SetAuthToken($oAccount); if ($oAccount) {
$sAuthAccountHash = $this->sSpecAuthToken; $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 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->sSpecAuthToken = $sSpecAuthToken;
$this->oSpecAuthAccount = null;
Utils::SetCookie(self::AUTH_SPEC_TOKEN_KEY, $sSpecAuthToken); Utils::SetCookie(self::AUTH_SPEC_TOKEN_KEY, $sSpecAuthToken);
if (isset($aAccounts[$oAccount->Email()])) { if (isset($aAccounts[$oAccount->Email()])) {

View file

@ -174,14 +174,6 @@ class Account implements \JsonSerializable
return Utils::EncodeKeyValues($this->jsonSerialize()); 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 public static function NewInstanceByLogin(\RainLoop\Actions $oActions, string $sEmail, string $sLogin, string $sPassword, string $sClientCert = '', bool $bThrowException = false): ?self
{ {
$oAccount = null; $oAccount = null;

View file

@ -671,19 +671,19 @@ class ServiceActions
$sSsoSubData = $this->Cacher()->Get(KeyPathHelper::SsoCacherKey($sSsoHash)); $sSsoSubData = $this->Cacher()->Get(KeyPathHelper::SsoCacherKey($sSsoHash));
if (!empty($sSsoSubData)) if (!empty($sSsoSubData))
{ {
$mData = \array_map('base64_decode', \json_decode($sSsoSubData, true)); $aData = \json_decode($sSsoSubData, true);
$mData = \is_array($mData) ? \SnappyMail\Crypt::Decrypt($sSsoSubData, $sSsoHash) : null; $aData = \is_array($aData) ? \SnappyMail\Crypt::Decrypt(\array_map('base64_decode', $aData), $sSsoHash) : null;
$this->Cacher()->Delete(KeyPathHelper::SsoCacherKey($sSsoHash)); $this->Cacher()->Delete(KeyPathHelper::SsoCacherKey($sSsoHash));
if (\is_array($mData) && !empty($mData['Email']) && isset($mData['Password'], $mData['Time']) && if (\is_array($aData) && !empty($aData['Email']) && isset($aData['Password'], $aData['Time']) &&
(0 === $mData['Time'] || \time() - 10 < $mData['Time'])) (0 === $aData['Time'] || \time() - 10 < $aData['Time']))
{ {
$sEmail = \trim($mData['Email']); $sEmail = \trim($aData['Email']);
$sPassword = $mData['Password']; $sPassword = $aData['Password'];
$aAdditionalOptions = isset($mData['AdditionalOptions']) && \is_array($mData['AdditionalOptions']) && $aAdditionalOptions = isset($aData['AdditionalOptions']) && \is_array($aData['AdditionalOptions']) &&
\count($mData['AdditionalOptions']) ? $mData['AdditionalOptions'] : null; \count($aData['AdditionalOptions']) ? $aData['AdditionalOptions'] : null;
try try
{ {
@ -829,7 +829,7 @@ class ServiceActions
$aAccounts = $this->oActions->GetAccounts($oAccount); $aAccounts = $this->oActions->GetAccounts($oAccount);
if (isset($aAccounts[$sEmail])) if (isset($aAccounts[$sEmail]))
{ {
$oAccountToLogin = $this->oActions->GetAccountFromCustomToken($aAccounts[$sEmail], false); $oAccountToLogin = $this->oActions->GetAccountFromCustomToken($aAccounts[$sEmail]);
} }
} }

View file

@ -30,13 +30,20 @@ class Utils
public static function EncodeKeyValues(array $aValues, string $sCustomKey = '') : string public static function EncodeKeyValues(array $aValues, string $sCustomKey = '') : string
{ {
return \MailSo\Base\Utils::UrlSafeBase64Encode( 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 public static function DecodeKeyValues(string $sEncodedValues, string $sCustomKey = '') : array
{ {
return static::unserialize( 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)
)
); );
} }

View file

@ -78,9 +78,9 @@ abstract class Crypt
public static function Decrypt(array $data, string $key = null) /* : mixed */ public static function Decrypt(array $data, string $key = null) /* : mixed */
{ {
if (3 === \count($data) && isset($data[0], $data[1], $data[2])) { if (3 === \count($data) && isset($data[0], $data[1], $data[2])) {
$fn = "\\SnappyMail\\Crypt::{$data[0]}Decrypt"; $fn = "{$data[0]}Decrypt";
if (\method_exists($fn)) { if (\method_exists(__CLASS__, $fn)) {
return $fn($data[2], $data[1], $key); 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')) { if (!\is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_decrypt')) {
return null; return null;
} }
return \json_decode(\sodium_crypto_aead_xchacha20poly1305_ietf_decrypt( return \json_decode(\zlib_decode(\sodium_crypto_aead_xchacha20poly1305_ietf_decrypt(
$data, $data,
APP_SALT, APP_SALT,
$nonce, $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 public static function SodiumEncrypt($data, string $nonce, string $key = null) : ?string
@ -120,7 +120,7 @@ abstract class Crypt
return null; return null;
} }
return \sodium_crypto_aead_xchacha20poly1305_ietf_encrypt( return \sodium_crypto_aead_xchacha20poly1305_ietf_encrypt(
\json_encode($data), \zlib_encode(\json_encode($data), ZLIB_ENCODING_RAW, 9),
APP_SALT, APP_SALT,
$nonce, $nonce,
\str_pad('', \SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES, static::Passphrase($key)) \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')) { if (!$data || !$iv || !static::$cipher || !\is_callable('openssl_decrypt')) {
return null; return null;
} }
return \json_decode(\openssl_decrypt( return \json_decode(\zlib_decode(\openssl_decrypt(
$data, $data,
static::$cipher, static::$cipher,
static::Passphrase($key), static::Passphrase($key),
OPENSSL_RAW_DATA, OPENSSL_RAW_DATA,
$iv $iv
), true); )), true);
} }
public static function OpenSSLEncrypt($data, string $iv, string $key = null) : ?string public static function OpenSSLEncrypt($data, string $iv, string $key = null) : ?string
@ -147,7 +147,7 @@ abstract class Crypt
return null; return null;
} }
return \openssl_encrypt( return \openssl_encrypt(
\json_encode($data), \zlib_encode(\json_encode($data), ZLIB_ENCODING_RAW, 9),
static::$cipher, static::$cipher,
static::Passphrase($key), static::Passphrase($key),
OPENSSL_RAW_DATA, OPENSSL_RAW_DATA,
@ -161,10 +161,10 @@ abstract class Crypt
return null; return null;
} }
$key = $salt . static::Passphrase($key); $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) ? \xxtea_decrypt($data, $key)
: \MailSo\Base\Xxtea::decrypt($data, $key) : \MailSo\Base\Xxtea::decrypt($data, $key)
, true); ), true);
} }
public static function XxteaEncrypt($data, string $salt, string $key = null) : ?string public static function XxteaEncrypt($data, string $salt, string $key = null) : ?string
@ -172,7 +172,7 @@ abstract class Crypt
if (!$data || !$salt) { if (!$data || !$salt) {
return null; return null;
} }
$data = \json_encode($data); $data = \zlib_encode(\json_encode($data), ZLIB_ENCODING_RAW, 9);
$key = $salt . static::Passphrase($key); $key = $salt . static::Passphrase($key);
return \is_callable('xxtea_encrypt') return \is_callable('xxtea_encrypt')
? \xxtea_encrypt($data, $key) ? \xxtea_encrypt($data, $key)