mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-08 22:18:28 +03:00
More encrypt/decrypt improvements to revamp SSO data to be properly encrypted
This commit is contained in:
parent
cda88f438a
commit
9dae4cfa45
5 changed files with 64 additions and 48 deletions
|
|
@ -216,7 +216,7 @@ trait UserAuth
|
|||
if (empty($sSignMeToken)) {
|
||||
return null;
|
||||
}
|
||||
$aResult = \SnappyMail\Crypt::DecryptCookie($sSignMeToken);
|
||||
$aResult = \SnappyMail\Crypt::DecryptUrlSafe($sSignMeToken);
|
||||
return \is_array($aResult) ? $aResult : null;
|
||||
}
|
||||
|
||||
|
|
@ -224,7 +224,7 @@ trait UserAuth
|
|||
{
|
||||
Utils::SetCookie(
|
||||
self::AUTH_SIGN_ME_TOKEN_KEY,
|
||||
\SnappyMail\Crypt::EncryptCookie($aData),
|
||||
\SnappyMail\Crypt::EncryptUrlSafe($aData),
|
||||
\time() + 3600 * 24 * 30 // 30 days
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class Api
|
|||
return $bOne;
|
||||
}
|
||||
|
||||
public static function Actions() : Actions
|
||||
final public static function Actions() : Actions
|
||||
{
|
||||
static $oActions = null;
|
||||
if (null === $oActions)
|
||||
|
|
@ -161,18 +161,21 @@ class Api
|
|||
return APP_VERSION;
|
||||
}
|
||||
|
||||
public static function GetUserSsoHash(string $sEmail, string $sPassword, array $aAdditionalOptions = array(), bool $bUseTimeout = true) : ?string
|
||||
public static function CreateUserSsoHash(string $sEmail, string $sPassword, array $aAdditionalOptions = array(), bool $bUseTimeout = true) : ?string
|
||||
{
|
||||
$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(
|
||||
KeyPathHelper::SsoCacherKey($sSsoHash),
|
||||
Utils::EncodeKeyValuesQ(array(
|
||||
'Email' => $sEmail,
|
||||
'Password' => $sPassword,
|
||||
'AdditionalOptions' => $aAdditionalOptions,
|
||||
'Time' => $bUseTimeout ? \time() : 0
|
||||
))) ? $sSsoHash : null;
|
||||
\json_encode(\array_map('base64_encode', $data))
|
||||
) ? $sSsoHash : null;
|
||||
}
|
||||
|
||||
public static function ClearUserSsoHash(string $sSsoHash) : bool
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ class KeyPathHelper
|
|||
|
||||
static public function PublicFile(string $sHash) : string
|
||||
{
|
||||
return '/Public/Files/'.sha1($sHash).'/Data/';
|
||||
return '/Public/Files/'.\sha1($sHash).'/Data/';
|
||||
}
|
||||
|
||||
static public function SsoCacherKey(string $sSsoHash) : string
|
||||
|
|
@ -15,21 +15,11 @@ class KeyPathHelper
|
|||
return '/Sso/Data/'.$sSsoHash.'/Login/';
|
||||
}
|
||||
|
||||
static public function RsaCacherKey(string $sHash) : string
|
||||
{
|
||||
return '/Rsa/Data/'.$sHash.'/';
|
||||
}
|
||||
|
||||
static public function RepositoryCacheFile(string $sRepo, string $sRepoFile) : string
|
||||
{
|
||||
return '/RepositoryCache/Repo/'.$sRepo.'/File/'.$sRepoFile;
|
||||
}
|
||||
|
||||
static public function RepositoryCacheCore(string $sRepo) : string
|
||||
{
|
||||
return '/RepositoryCache/CoreRepo/'.$sRepo;
|
||||
}
|
||||
|
||||
static public function ReadReceiptCache(string $sEmail, string $sFolderFullName, int $iUid) : string
|
||||
{
|
||||
return '/ReadReceipt/'.$sEmail.'/'.$sFolderFullName.'/'.$iUid;
|
||||
|
|
|
|||
|
|
@ -652,6 +652,9 @@ class ServiceActions
|
|||
return 'Pong';
|
||||
}
|
||||
|
||||
/**
|
||||
* Login with the \RainLoop\API::CreateUserSsoHash() generated hash
|
||||
*/
|
||||
public function ServiceSso() : string
|
||||
{
|
||||
$this->oHttp->ServerNoCache();
|
||||
|
|
@ -668,7 +671,9 @@ class ServiceActions
|
|||
$sSsoSubData = $this->Cacher()->Get(KeyPathHelper::SsoCacherKey($sSsoHash));
|
||||
if (!empty($sSsoSubData))
|
||||
{
|
||||
$mData = Utils::DecodeKeyValuesQ($sSsoSubData);
|
||||
$mData = \array_map('base64_decode', \json_decode($sSsoSubData, true));
|
||||
$mData = \is_array($mData) ? \SnappyMail\Crypt::Decrypt($sSsoSubData, $sSsoHash) : null;
|
||||
|
||||
$this->Cacher()->Delete(KeyPathHelper::SsoCacherKey($sSsoHash));
|
||||
|
||||
if (\is_array($mData) && !empty($mData['Email']) && isset($mData['Password'], $mData['Time']) &&
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
<?php
|
||||
/**
|
||||
* This class encrypts any data into JSON format.
|
||||
* Decrypted Objects are returned as Array.
|
||||
*/
|
||||
|
||||
namespace SnappyMail;
|
||||
|
||||
abstract class Crypt
|
||||
{
|
||||
/**
|
||||
* Or use 'aes-256-xts' ?
|
||||
*/
|
||||
protected static $cipher = '';
|
||||
|
||||
public static function listCiphers() : array
|
||||
|
|
@ -36,39 +37,55 @@ abstract class Crypt
|
|||
return false;
|
||||
}
|
||||
|
||||
private static function Passphrase() : string
|
||||
/**
|
||||
* When $key is empty, it will use a fingerprint of the user agent.
|
||||
*/
|
||||
private static function Passphrase(?string $key) : string
|
||||
{
|
||||
return \sha1(\preg_replace('/[^a-z]+/i', '', \explode(')', $_SERVER['HTTP_USER_AGENT'])[0]) . APP_SALT, true);
|
||||
return \sha1(
|
||||
($key ?: \preg_replace('/[^a-z]+/i', '', \explode(')', $_SERVER['HTTP_USER_AGENT'])[0])) . APP_SALT,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
public static function DecryptCookie($sData)
|
||||
public static function DecryptUrlSafe($sData, string $key = null) /* : mixed */
|
||||
{
|
||||
return \json_decode(
|
||||
\zlib_decode(
|
||||
\MailSo\Base\Crypt::Decrypt(
|
||||
static::XxteaDecrypt(
|
||||
\MailSo\Base\Utils::UrlSafeBase64Decode($sData),
|
||||
static::Passphrase()
|
||||
static::Passphrase($key)
|
||||
)
|
||||
),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
public static function EncryptCookie($mData) : string
|
||||
public static function EncryptUrlSafe($mData, string $key = null) : string
|
||||
{
|
||||
return \MailSo\Base\Utils::UrlSafeBase64Encode(
|
||||
\MailSo\Base\Crypt::Encrypt(
|
||||
static::XxteaEncrypt(
|
||||
\zlib_encode(
|
||||
\json_encode($mData),
|
||||
ZLIB_ENCODING_RAW,
|
||||
9
|
||||
),
|
||||
static::Passphrase()
|
||||
static::Passphrase($key)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static function Encrypt($data) : array
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function Encrypt($data, string $key = null) : array
|
||||
{
|
||||
if (\is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_encrypt')) {
|
||||
$nonce = \random_bytes(24);
|
||||
|
|
@ -84,7 +101,7 @@ abstract class Crypt
|
|||
return ['xxtea', $salt, static::XxteaEncrypt($data, $salt)];
|
||||
}
|
||||
|
||||
public static function SodiumDecrypt(string $data, string $nonce)
|
||||
public static function SodiumDecrypt(string $data, string $nonce, string $key = null) /* : mixed */
|
||||
{
|
||||
if (!\is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_decrypt')) {
|
||||
return null;
|
||||
|
|
@ -93,12 +110,11 @@ abstract class Crypt
|
|||
$data,
|
||||
APP_SALT,
|
||||
$nonce,
|
||||
static::Passphrase()
|
||||
|
||||
static::Passphrase($key)
|
||||
));
|
||||
}
|
||||
|
||||
public static function SodiumEncrypt($data, string $nonce) : ?string
|
||||
public static function SodiumEncrypt($data, string $nonce, string $key = null) : ?string
|
||||
{
|
||||
if (!\is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_encrypt')) {
|
||||
return null;
|
||||
|
|
@ -107,11 +123,11 @@ abstract class Crypt
|
|||
\json_encode($data),
|
||||
APP_SALT,
|
||||
$nonce,
|
||||
\str_pad('', \SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES, static::Passphrase())
|
||||
\str_pad('', \SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES, static::Passphrase($key))
|
||||
);
|
||||
}
|
||||
|
||||
public static function OpenSSLDecrypt(string $data, string $iv)
|
||||
public static function OpenSSLDecrypt(string $data, string $iv, string $key = null) /* : mixed */
|
||||
{
|
||||
if (!$data || !$iv || !static::$cipher || !\is_callable('openssl_decrypt')) {
|
||||
return null;
|
||||
|
|
@ -119,13 +135,13 @@ abstract class Crypt
|
|||
return \json_decode(\openssl_decrypt(
|
||||
$data,
|
||||
static::$cipher,
|
||||
static::Passphrase(),
|
||||
static::Passphrase($key),
|
||||
OPENSSL_RAW_DATA,
|
||||
$iv
|
||||
), true);
|
||||
}
|
||||
|
||||
public static function OpenSSLEncrypt($data, string $iv) : ?string
|
||||
public static function OpenSSLEncrypt($data, string $iv, string $key = null) : ?string
|
||||
{
|
||||
if (!$data || !$iv || !static::$cipher || !\is_callable('openssl_encrypt')) {
|
||||
return null;
|
||||
|
|
@ -133,31 +149,31 @@ abstract class Crypt
|
|||
return \openssl_encrypt(
|
||||
\json_encode($data),
|
||||
static::$cipher,
|
||||
static::Passphrase(),
|
||||
static::Passphrase($key),
|
||||
OPENSSL_RAW_DATA,
|
||||
$iv
|
||||
);
|
||||
}
|
||||
|
||||
public static function XxteaDecrypt(string $data, string $salt)
|
||||
public static function XxteaDecrypt(string $data, string $salt, string $key = null) /* : mixed */
|
||||
{
|
||||
if (!$data || !$salt) {
|
||||
return null;
|
||||
}
|
||||
$key = $salt . static::Passphrase();
|
||||
$key = $salt . static::Passphrase($key);
|
||||
return \json_decode(\is_callable('xxtea_decrypt')
|
||||
? \xxtea_decrypt($data, $key)
|
||||
: \MailSo\Base\Xxtea::decrypt($data, $key)
|
||||
, true);
|
||||
}
|
||||
|
||||
public static function XxteaEncrypt($data, string $salt) : ?string
|
||||
public static function XxteaEncrypt($data, string $salt, string $key = null) : ?string
|
||||
{
|
||||
if (!$data || !$salt) {
|
||||
return null;
|
||||
}
|
||||
$data = \json_encode($data);
|
||||
$key = $salt . static::Passphrase();
|
||||
$key = $salt . static::Passphrase($key);
|
||||
return \is_callable('xxtea_encrypt')
|
||||
? \xxtea_encrypt($data, $key)
|
||||
: \MailSo\Base\Xxtea::encrypt($data, $key);
|
||||
|
|
@ -165,4 +181,6 @@ abstract class Crypt
|
|||
|
||||
}
|
||||
|
||||
\SnappyMail\Crypt::setCipher(\RainLoop\API::Config()->Get('security', 'encrypt_cipher', 'aes-256-cbc-hmac-sha1'));
|
||||
\SnappyMail\Crypt::setCipher(\RainLoop\API::Config()->Get('security', 'encrypt_cipher', 'aes-256-cbc-hmac-sha1'))
|
||||
|| \SnappyMail\Crypt::setCipher('aes-256-cbc-hmac-sha1')
|
||||
|| \SnappyMail\Crypt::setCipher('aes-256-xts');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue