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;
if ($oAccount->Email() === $sEmailToDelete && !empty($aAccounts[$sParentEmail])) {
$oAccountToChange = $this->GetAccountFromCustomToken($aAccounts[$sParentEmail], false);
$oAccountToChange = $this->GetAccountFromCustomToken($aAccounts[$sParentEmail]);
if ($oAccountToChange) {
$this->SetAuthToken($oAccountToChange);
}

View file

@ -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()])) {

View file

@ -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;

View file

@ -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]);
}
}

View file

@ -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)
)
);
}

View file

@ -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)