mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-09-06 07:57:02 +03:00
More encrypt/decrypt improvements and fixes
This commit is contained in:
parent
59cb0978c7
commit
b1e895907f
6 changed files with 78 additions and 73 deletions
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()])) {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue