Improved data encryption for Contacts sync

This commit is contained in:
djmaze 2021-11-10 14:49:49 +01:00
parent b1e895907f
commit b102fc0e4c
6 changed files with 54 additions and 26 deletions

View file

@ -22,14 +22,17 @@ trait Contacts
$mData = $this->getContactsSyncData($oAccount); $mData = $this->getContactsSyncData($oAccount);
$bResult = $this->StorageProvider()->Put($oAccount, $sPassword = APP_DUMMY === $sPassword && isset($mData['Password'])
? $mData['Password'] : (APP_DUMMY === $sPassword ? '' : $sPassword);
$bResult = $this->StorageProvider()->Put(
$oAccount,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
'contacts_sync', 'contacts_sync',
\RainLoop\Utils::EncodeKeyValues(array( \json_encode(array(
'Enable' => $bEnabled, 'Enable' => $bEnabled,
'User' => $sUser, 'User' => $sUser,
'Password' => APP_DUMMY === $sPassword && isset($mData['Password']) ? 'Password' => $sPassword ? \SnappyMail\Crypt::Encrypt($sPassword, $oAccount->Password() ?: APP_SALT) : null,
$mData['Password'] : (APP_DUMMY === $sPassword ? '' : $sPassword),
'Url' => $sUrl 'Url' => $sUrl
)) ))
); );
@ -240,15 +243,25 @@ trait Contacts
protected function getContactsSyncData(\RainLoop\Model\Account $oAccount) : ?array protected function getContactsSyncData(\RainLoop\Model\Account $oAccount) : ?array
{ {
$mResult = null;
$sData = $this->StorageProvider()->Get($oAccount, $sData = $this->StorageProvider()->Get($oAccount,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
'contacts_sync' 'contacts_sync'
); );
if (!empty($sData)) { if (!empty($sData)) {
$aData = \json_decode($sData);
if ($aData) {
if ($aData['Password']) {
$aData['Password'] = \SnappyMail\Crypt::DecryptFromJSON(
$aData['Password'],
$oAccount->Password() ?: APP_SALT
);
}
return $aData;
}
$aData = \RainLoop\Utils::DecodeKeyValues($sData); $aData = \RainLoop\Utils::DecodeKeyValues($sData);
if ($aData) { if ($aData) {
$mResult = array( return array(
'Enable' => isset($aData['Enable']) ? !!$aData['Enable'] : false, 'Enable' => isset($aData['Enable']) ? !!$aData['Enable'] : false,
'Url' => isset($aData['Url']) ? \trim($aData['Url']) : '', 'Url' => isset($aData['Url']) ? \trim($aData['Url']) : '',
'User' => isset($aData['User']) ? \trim($aData['User']) : '', 'User' => isset($aData['User']) ? \trim($aData['User']) : '',
@ -256,7 +269,7 @@ trait Contacts
); );
} }
} }
return $mResult; return null;
} }
public function RawContactsVcf() : bool public function RawContactsVcf() : bool

View file

@ -178,8 +178,10 @@ trait UserAuth
} }
} }
$aData = \json_decode(\MailSo\Base\Utils::UrlSafeBase64Decode($this->sSpecAuthToken), true); $aData = \SnappyMail\Crypt::DecryptFromJSON(
$aData = \is_array($aData) ? \SnappyMail\Crypt::Decrypt(\array_map('base64_decode', $aData)) : null; \MailSo\Base\Utils::UrlSafeBase64Decode($this->sSpecAuthToken),
APP_SALT . Utils::GetShortToken()
);
if (!empty($aData)) { if (!empty($aData)) {
$this->oSpecAuthAccount = Account::NewInstanceFromTokenArray( $this->oSpecAuthAccount = Account::NewInstanceFromTokenArray(
$this, $this,
@ -198,13 +200,10 @@ trait UserAuth
public function SetAuthToken(Account $oAccount): void public function SetAuthToken(Account $oAccount): void
{ {
$sSpecAuthToken = \MailSo\Base\Utils::UrlSafeBase64Encode(\json_encode(\array_map( $sSpecAuthToken = \MailSo\Base\Utils::UrlSafeBase64Encode(\SnappyMail\Crypt::EncryptToJSON(
'base64_encode', $oAccount,
\SnappyMail\Crypt::Encrypt( APP_SALT . Utils::GetShortToken()
$oAccount, ));
APP_SALT . Utils::GetShortToken()
)
)));
$this->sSpecAuthToken = $sSpecAuthToken; $this->sSpecAuthToken = $sSpecAuthToken;
$this->oSpecAuthAccount = null; $this->oSpecAuthAccount = null;

View file

@ -165,16 +165,14 @@ class Api
{ {
$sSsoHash = \MailSo\Base\Utils::Sha1Rand(\sha1($sPassword.$sEmail)); $sSsoHash = \MailSo\Base\Utils::Sha1Rand(\sha1($sPassword.$sEmail));
$data = \SnappyMail\Crypt::Encrypt(array(
'Email' => $sEmail,
'Password' => $sPassword,
'AdditionalOptions' => $aAdditionalOptions,
'Time' => $bUseTimeout ? \time() : 0
), $sSsoHash);
return static::Actions()->Cacher()->Set( return static::Actions()->Cacher()->Set(
KeyPathHelper::SsoCacherKey($sSsoHash), KeyPathHelper::SsoCacherKey($sSsoHash),
\json_encode(\array_map('base64_encode', $data)) \SnappyMail\Crypt::EncryptToJSON(array(
'Email' => $sEmail,
'Password' => $sPassword,
'AdditionalOptions' => $aAdditionalOptions,
'Time' => $bUseTimeout ? \time() : 0
), $sSsoHash)
) ? $sSsoHash : null; ) ? $sSsoHash : null;
} }

View file

@ -172,6 +172,14 @@ class Account implements \JsonSerializable
public function GetAuthToken() : string public function GetAuthToken() : string
{ {
return Utils::EncodeKeyValues($this->jsonSerialize()); return Utils::EncodeKeyValues($this->jsonSerialize());
/*
return \MailSo\Base\Utils::UrlSafeBase64Encode(
\MailSo\Base\Crypt::Encrypt(
\json_encode($this),
\md5(APP_SALT.$sCustomKey)
)
);
*/
} }
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

View file

@ -671,8 +671,7 @@ class ServiceActions
$sSsoSubData = $this->Cacher()->Get(KeyPathHelper::SsoCacherKey($sSsoHash)); $sSsoSubData = $this->Cacher()->Get(KeyPathHelper::SsoCacherKey($sSsoHash));
if (!empty($sSsoSubData)) if (!empty($sSsoSubData))
{ {
$aData = \json_decode($sSsoSubData, true); $aData = \SnappyMail\Crypt::DecryptFromJSON($sSsoSubData, $sSsoHash);
$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));

View file

@ -85,6 +85,12 @@ abstract class Crypt
} }
} }
public static function DecryptFromJSON(string $data, string $key = null) /* : mixed */
{
$aData = \json_decode($data, true);
$aData = \is_array($aData) ? static::Decrypt(\array_map('base64_decode', $aData), $key) : null;
}
public static function Encrypt($data, string $key = null) : array public static function Encrypt($data, string $key = null) : array
{ {
if (\is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_encrypt')) { if (\is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_encrypt')) {
@ -101,6 +107,11 @@ abstract class Crypt
return ['xxtea', $salt, static::XxteaEncrypt($data, $salt)]; return ['xxtea', $salt, static::XxteaEncrypt($data, $salt)];
} }
public static function EncryptToJSON($data, string $key = null) : string
{
return \json_encode(\array_map('base64_encode', static::Encrypt($data, $key)));
}
public static function SodiumDecrypt(string $data, string $nonce, string $key = null) /* : mixed */ public static function SodiumDecrypt(string $data, string $nonce, string $key = null) /* : mixed */
{ {
if (!\is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_decrypt')) { if (!\is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_decrypt')) {