mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-09-07 16:37:02 +03:00
Merge branch 'feature/1343-secure-parameters'
This commit is contained in:
commit
b7b266defc
20 changed files with 304 additions and 80 deletions
|
|
@ -110,9 +110,9 @@ class ImapClient extends \MailSo\Net\NetClient
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($oSettings->ProxyAuthUser) && !empty($oSettings->ProxyAuthPassword)) {
|
if (!empty($oSettings->ProxyAuthUser) && $oSettings->ProxyAuthPassword) {
|
||||||
$sLogin = $oSettings->ProxyAuthUser;
|
$sLogin = $oSettings->ProxyAuthUser;
|
||||||
$sPassword = $oSettings->ProxyAuthPassword;
|
$sPassword = $oSettings->ProxyAuthPassword->getValue();
|
||||||
$sProxyAuthUser = $oSettings->Login;
|
$sProxyAuthUser = $oSettings->Login;
|
||||||
} else {
|
} else {
|
||||||
$sLogin = $oSettings->Login;
|
$sLogin = $oSettings->Login;
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,10 @@ trait Inherit
|
||||||
$this->oLogger && $this->oLogger->WriteException($oException, $iType, $sName);
|
$this->oLogger && $this->oLogger->WriteException($oException, $iType, $sName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function logMask(string $sWord): void
|
public function logMask(
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $sWord
|
||||||
|
): void
|
||||||
{
|
{
|
||||||
$this->oLogger && $this->oLogger->AddSecret($sWord);
|
$this->oLogger && $this->oLogger->AddSecret($sWord);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,10 @@ class Logger extends \SplFixedArray
|
||||||
return 0 < $this->count();
|
return 0 < $this->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function AddSecret(string $sWord) : void
|
public function AddSecret(
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $sWord
|
||||||
|
) : void
|
||||||
{
|
{
|
||||||
// $this->bShowSecrets && $this->Write("AddSecret '{$sWord}'", \LOG_INFO, '', false);
|
// $this->bShowSecrets && $this->Write("AddSecret '{$sWord}'", \LOG_INFO, '', false);
|
||||||
$sWord = \trim($sWord);
|
$sWord = \trim($sWord);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
namespace MailSo\Net;
|
namespace MailSo\Net;
|
||||||
|
|
||||||
|
use SnappyMail\SensitiveString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @category MailSo
|
* @category MailSo
|
||||||
* @package Net
|
* @package Net
|
||||||
|
|
@ -47,15 +49,29 @@ class ConnectSettings implements \JsonSerializable
|
||||||
'LOGIN'
|
'LOGIN'
|
||||||
];
|
];
|
||||||
public string $Login = '';
|
public string $Login = '';
|
||||||
public string $Password = '';
|
private ?SensitiveString $Password = null;
|
||||||
public string $ProxyAuthUser = '';
|
public string $ProxyAuthUser = '';
|
||||||
public string $ProxyAuthPassword = '';
|
public ?SensitiveString $ProxyAuthPassword = null;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->ssl = new SSLContext;
|
$this->ssl = new SSLContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function __get(string $name)
|
||||||
|
{
|
||||||
|
if ('Password' === $name) {
|
||||||
|
return $this->Password ? $this->Password->getValue() : '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set(string $name, $value)
|
||||||
|
{
|
||||||
|
if ('Password' === $name) {
|
||||||
|
$this->Password = \is_string($value) ? new SensitiveString($value) : $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function Host() : string
|
public static function Host() : string
|
||||||
{
|
{
|
||||||
return \SnappyMail\IDN::toAscii($this->host);
|
return \SnappyMail\IDN::toAscii($this->host);
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,10 @@ trait UserAuth
|
||||||
/**
|
/**
|
||||||
* @throws \RainLoop\Exceptions\ClientException
|
* @throws \RainLoop\Exceptions\ClientException
|
||||||
*/
|
*/
|
||||||
public function resolveLoginCredentials(string &$sEmail, string &$sPassword, string &$sLogin): void
|
public function resolveLoginCredentials(string &$sEmail,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string &$sPassword,
|
||||||
|
string &$sLogin): void
|
||||||
{
|
{
|
||||||
$this->Plugins()->RunHook('login.credentials.step-1', array(&$sEmail));
|
$this->Plugins()->RunHook('login.credentials.step-1', array(&$sEmail));
|
||||||
|
|
||||||
|
|
@ -113,7 +116,11 @@ trait UserAuth
|
||||||
/**
|
/**
|
||||||
* @throws \RainLoop\Exceptions\ClientException
|
* @throws \RainLoop\Exceptions\ClientException
|
||||||
*/
|
*/
|
||||||
public function LoginProcess(string &$sEmail, string &$sPassword, bool $bMainAccount = true): Account
|
public function LoginProcess(string &$sEmail,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string &$sPassword,
|
||||||
|
bool $bMainAccount = true
|
||||||
|
): Account
|
||||||
{
|
{
|
||||||
$sInputEmail = $sEmail;
|
$sInputEmail = $sEmail;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,11 @@ abstract class Api
|
||||||
return APP_VERSION;
|
return APP_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function CreateUserSsoHash(string $sEmail, string $sPassword, array $aAdditionalOptions = array(), bool $bUseTimeout = true) : ?string
|
public static function CreateUserSsoHash(string $sEmail,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $sPassword,
|
||||||
|
array $aAdditionalOptions = array(), bool $bUseTimeout = true
|
||||||
|
) : ?string
|
||||||
{
|
{
|
||||||
$sSsoHash = \MailSo\Base\Utils::Sha1Rand(\sha1($sPassword.$sEmail));
|
$sSsoHash = \MailSo\Base\Utils::Sha1Rand(\sha1($sPassword.$sEmail));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,12 +116,18 @@ class Application extends \RainLoop\Config\AbstractConfig
|
||||||
parent::Set($sSectionKey, $sParamKey, $mParamValue);
|
parent::Set($sSectionKey, $sParamKey, $mParamValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SetPassword(string $sPassword) : void
|
public function SetPassword(
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $sPassword
|
||||||
|
) : void
|
||||||
{
|
{
|
||||||
$this->Set('security', 'admin_password', \password_hash($sPassword, PASSWORD_DEFAULT));
|
$this->Set('security', 'admin_password', \password_hash($sPassword, PASSWORD_DEFAULT));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ValidatePassword(string $sPassword) : bool
|
public function ValidatePassword(
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $sPassword
|
||||||
|
) : bool
|
||||||
{
|
{
|
||||||
$sConfigPassword = (string) $this->Get('security', 'admin_password', '');
|
$sConfigPassword = (string) $this->Get('security', 'admin_password', '');
|
||||||
if (32 == \strlen($sPassword) && \md5(APP_SALT.$sPassword.APP_SALT) === $sConfigPassword) {
|
if (32 == \strlen($sPassword) && \md5(APP_SALT.$sPassword.APP_SALT) === $sConfigPassword) {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ namespace RainLoop\Model;
|
||||||
use RainLoop\Utils;
|
use RainLoop\Utils;
|
||||||
use RainLoop\Notifications;
|
use RainLoop\Notifications;
|
||||||
use RainLoop\Exceptions\ClientException;
|
use RainLoop\Exceptions\ClientException;
|
||||||
|
use SnappyMail\SensitiveString;
|
||||||
|
|
||||||
abstract class Account implements \JsonSerializable
|
abstract class Account implements \JsonSerializable
|
||||||
{
|
{
|
||||||
|
|
@ -14,15 +15,15 @@ abstract class Account implements \JsonSerializable
|
||||||
|
|
||||||
private string $sLogin = '';
|
private string $sLogin = '';
|
||||||
|
|
||||||
private string $sPassword = '';
|
private ?SensitiveString $sPassword = null;
|
||||||
|
|
||||||
private string $sSmtpLogin = '';
|
private string $sSmtpLogin = '';
|
||||||
|
|
||||||
private string $sSmtpPassword = '';
|
private ?SensitiveString $sSmtpPassword = null;
|
||||||
|
|
||||||
private string $sProxyAuthUser = '';
|
private string $sProxyAuthUser = '';
|
||||||
|
|
||||||
private string $sProxyAuthPassword = '';
|
private ?SensitiveString $sProxyAuthPassword = null;
|
||||||
|
|
||||||
private Domain $oDomain;
|
private Domain $oDomain;
|
||||||
|
|
||||||
|
|
@ -45,7 +46,7 @@ abstract class Account implements \JsonSerializable
|
||||||
|
|
||||||
public function IncPassword() : string
|
public function IncPassword() : string
|
||||||
{
|
{
|
||||||
return $this->sPassword;
|
return $this->sPassword ? $this->sPassword->getValue() : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function OutLogin() : string
|
public function OutLogin() : string
|
||||||
|
|
@ -69,14 +70,20 @@ abstract class Account implements \JsonSerializable
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SetPassword(string $sPassword) : void
|
public function SetPassword(
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $sPassword
|
||||||
|
) : void
|
||||||
{
|
{
|
||||||
$this->sPassword = $sPassword;
|
$this->sPassword = new SensitiveString($sPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SetSmtpPassword(string $sPassword) : void
|
public function SetSmtpPassword(
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $sPassword
|
||||||
|
) : void
|
||||||
{
|
{
|
||||||
$this->sSmtpPassword = $sPassword;
|
$this->sSmtpPassword = new SensitiveString($sPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SetProxyAuthUser(string $sProxyAuthUser) : void
|
public function SetProxyAuthUser(string $sProxyAuthUser) : void
|
||||||
|
|
@ -84,39 +91,43 @@ abstract class Account implements \JsonSerializable
|
||||||
$this->sProxyAuthUser = $sProxyAuthUser;
|
$this->sProxyAuthUser = $sProxyAuthUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SetProxyAuthPassword(string $sProxyAuthPassword) : void
|
public function SetProxyAuthPassword(
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $sProxyAuthPassword
|
||||||
|
) : void
|
||||||
{
|
{
|
||||||
$this->sProxyAuthPassword = $sProxyAuthPassword;
|
$this->sProxyAuthPassword = new SensitiveString($sProxyAuthPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[\ReturnTypeWillChange]
|
#[\ReturnTypeWillChange]
|
||||||
public function jsonSerialize()
|
public function jsonSerialize()
|
||||||
{
|
{
|
||||||
$result = [
|
$result = [
|
||||||
// 'account', // 0
|
'email' => $this->sEmail,
|
||||||
'email' => $this->sEmail, // 1
|
'login' => $this->sLogin,
|
||||||
'login' => $this->sLogin, // 2
|
'pass' => $this->IncPassword(),
|
||||||
'pass' => $this->sPassword, // 3
|
|
||||||
// '', // 4 sClientCert
|
|
||||||
'name' => $this->sName
|
'name' => $this->sName
|
||||||
];
|
];
|
||||||
if ($this->sSmtpLogin && $this->sSmtpPassword) {
|
if ($this->sSmtpLogin && $this->sSmtpPassword) {
|
||||||
$result['smtp'] = [
|
$result['smtp'] = [
|
||||||
'user' => $this->sSmtpLogin,
|
'user' => $this->sSmtpLogin,
|
||||||
'pass' => $this->sSmtpPassword
|
'pass' => $this->sSmtpPassword->getValue()
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if ($this->sProxyAuthUser && $this->sProxyAuthPassword) {
|
if ($this->sProxyAuthUser && $this->sProxyAuthPassword) {
|
||||||
$result['proxy'] = [
|
$result['proxy'] = [
|
||||||
'user' => $this->sProxyAuthUser, // 5
|
'user' => $this->sProxyAuthUser,
|
||||||
'pass' => $this->sProxyAuthPassword // 6
|
'pass' => $this->sProxyAuthPassword->getValue()
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function NewInstanceFromCredentials(\RainLoop\Actions $oActions,
|
public static function NewInstanceFromCredentials(\RainLoop\Actions $oActions,
|
||||||
string $sEmail, string $sLogin, string $sPassword, bool $bThrowException = false): ?self
|
string $sEmail, string $sLogin,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $sPassword,
|
||||||
|
bool $bThrowException = false): ?self
|
||||||
{
|
{
|
||||||
$oAccount = null;
|
$oAccount = null;
|
||||||
if ($sEmail && $sLogin && $sPassword) {
|
if ($sEmail && $sLogin && $sPassword) {
|
||||||
|
|
@ -127,7 +138,7 @@ abstract class Account implements \JsonSerializable
|
||||||
|
|
||||||
$oAccount->sEmail = \MailSo\Base\Utils::IdnToAscii($sEmail, true);
|
$oAccount->sEmail = \MailSo\Base\Utils::IdnToAscii($sEmail, true);
|
||||||
$oAccount->sLogin = \MailSo\Base\Utils::IdnToAscii($sLogin);
|
$oAccount->sLogin = \MailSo\Base\Utils::IdnToAscii($sLogin);
|
||||||
$oAccount->sPassword = $sPassword;
|
$oAccount->SetPassword($sPassword);
|
||||||
$oAccount->oDomain = $oDomain;
|
$oAccount->oDomain = $oDomain;
|
||||||
|
|
||||||
$oActions->Plugins()->RunHook('filter.account', array($oAccount));
|
$oActions->Plugins()->RunHook('filter.account', array($oAccount));
|
||||||
|
|
@ -190,17 +201,17 @@ abstract class Account implements \JsonSerializable
|
||||||
if (isset($aAccountHash['name'])) {
|
if (isset($aAccountHash['name'])) {
|
||||||
$oAccount->sName = $aAccountHash['name'];
|
$oAccount->sName = $aAccountHash['name'];
|
||||||
}
|
}
|
||||||
$oActions->logMask($oAccount->sPassword);
|
$oActions->logMask($oAccount->IncPassword());
|
||||||
// init smtp user/password
|
// init smtp user/password
|
||||||
if (isset($aAccountHash['smtp'])) {
|
if (isset($aAccountHash['smtp'])) {
|
||||||
$oAccount->sSmtpLogin = $aAccountHash['smtp']['user'];
|
$oAccount->sSmtpLogin = $aAccountHash['smtp']['user'];
|
||||||
$oAccount->sSmtpPassword = $aAccountHash['smtp']['pass'];
|
$oAccount->SetSmtpPassword($aAccountHash['smtp']['pass']);
|
||||||
$oActions->logMask($oAccount->sSmtpPassword);
|
$oActions->logMask($oAccount->sSmtpPassword);
|
||||||
}
|
}
|
||||||
// init proxy user/password
|
// init proxy user/password
|
||||||
if (isset($aAccountHash['proxy'])) {
|
if (isset($aAccountHash['proxy'])) {
|
||||||
$oAccount->sProxyAuthUser = $aAccountHash['proxy']['user'];
|
$oAccount->sProxyAuthUser = $aAccountHash['proxy']['user'];
|
||||||
$oAccount->sProxyAuthPassword = $aAccountHash['proxy']['pass'];
|
$oAccount->SetProxyAuthPassword($aAccountHash['proxy']['pass']);
|
||||||
$oActions->logMask($oAccount->sProxyAuthPassword);
|
$oActions->logMask($oAccount->sProxyAuthPassword);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -208,11 +219,6 @@ abstract class Account implements \JsonSerializable
|
||||||
return $oAccount;
|
return $oAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
public function ImapConnectAndLoginHelper(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Imap\ImapClient $oImapClient, \RainLoop\Config\Application $oConfig) : bool
|
|
||||||
{
|
|
||||||
return $this->ImapConnectAndLogin($oPlugins, $oImapClient, $oConfig);
|
|
||||||
}
|
|
||||||
public function ImapConnectAndLogin(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Imap\ImapClient $oImapClient, \RainLoop\Config\Application $oConfig) : bool
|
public function ImapConnectAndLogin(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Imap\ImapClient $oImapClient, \RainLoop\Config\Application $oConfig) : bool
|
||||||
{
|
{
|
||||||
$oSettings = $this->Domain()->ImapSettings();
|
$oSettings = $this->Domain()->ImapSettings();
|
||||||
|
|
@ -234,7 +240,7 @@ abstract class Account implements \JsonSerializable
|
||||||
$oImapClient->Connect($oSettings);
|
$oImapClient->Connect($oSettings);
|
||||||
$oPlugins->RunHook('imap.after-connect', array($this, $oImapClient, $oSettings));
|
$oPlugins->RunHook('imap.after-connect', array($this, $oImapClient, $oSettings));
|
||||||
|
|
||||||
$oSettings->Password = $this->IncPassword();
|
$oSettings->Password = $this->sPassword;
|
||||||
return $this->netClientLogin($oImapClient, $oPlugins);
|
return $this->netClientLogin($oImapClient, $oPlugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -273,7 +279,7 @@ abstract class Account implements \JsonSerializable
|
||||||
$oSieveClient->Connect($oSettings);
|
$oSieveClient->Connect($oSettings);
|
||||||
$oPlugins->RunHook('sieve.after-connect', array($this, $oSieveClient, $oSettings));
|
$oPlugins->RunHook('sieve.after-connect', array($this, $oSieveClient, $oSettings));
|
||||||
|
|
||||||
$oSettings->Password = $this->IncPassword();
|
$oSettings->Password = $this->sPassword;
|
||||||
return $this->netClientLogin($oSieveClient, $oPlugins);
|
return $this->netClientLogin($oSieveClient, $oPlugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,10 @@ trait CardDAV
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getContactsPaths(DAVClient $oClient, string $sPath, string $sUser, string $sPassword, string $sProxy = '') : array
|
private function getContactsPaths(DAVClient $oClient, string $sPath, string $sUser,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $sPassword,
|
||||||
|
string $sProxy = '') : array
|
||||||
{
|
{
|
||||||
$aContactsPaths = array();
|
$aContactsPaths = array();
|
||||||
|
|
||||||
|
|
@ -291,7 +294,11 @@ trait CardDAV
|
||||||
return $bGood;
|
return $bGood;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getDavClientFromUrl(string $sUrl, string $sUser, string $sPassword, string $sProxy = '') : DAVClient
|
private function getDavClientFromUrl(string $sUrl, string $sUser,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $sPassword,
|
||||||
|
string $sProxy = ''
|
||||||
|
) : DAVClient
|
||||||
{
|
{
|
||||||
if (!\preg_match('/^http[s]?:\/\//i', $sUrl)) {
|
if (!\preg_match('/^http[s]?:\/\//i', $sUrl)) {
|
||||||
$sUrl = \preg_replace('/^fruux\.com/i', 'dav.fruux.com', $sUrl);
|
$sUrl = \preg_replace('/^fruux\.com/i', 'dav.fruux.com', $sUrl);
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,10 @@ abstract class Crypt
|
||||||
/**
|
/**
|
||||||
* When $key is empty, it will use the smctoken.
|
* When $key is empty, it will use the smctoken.
|
||||||
*/
|
*/
|
||||||
private static function Passphrase(?string $key) : string
|
private static function Passphrase(
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
?string $key
|
||||||
|
) : string
|
||||||
{
|
{
|
||||||
if (!$key) {
|
if (!$key) {
|
||||||
if (empty($_COOKIE['smctoken'])) {
|
if (empty($_COOKIE['smctoken'])) {
|
||||||
|
|
@ -55,7 +58,10 @@ abstract class Crypt
|
||||||
return \sha1($key . APP_SALT, true);
|
return \sha1($key . APP_SALT, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function Decrypt(array $data, string $key = null) /* : mixed */
|
public static function Decrypt(array $data,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $key = null
|
||||||
|
) /* : mixed */
|
||||||
{
|
{
|
||||||
if (3 === \count($data) && isset($data[0], $data[1], $data[2]) && \strlen($data[0])) {
|
if (3 === \count($data) && isset($data[0], $data[1], $data[2]) && \strlen($data[0])) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -75,7 +81,10 @@ abstract class Crypt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function DecryptFromJSON(string $data, string $key = null) /* : mixed */
|
public static function DecryptFromJSON(string $data,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $key = null
|
||||||
|
) /* : mixed */
|
||||||
{
|
{
|
||||||
$data = static::jsonDecode($data);
|
$data = static::jsonDecode($data);
|
||||||
if (!\is_array($data)) {
|
if (!\is_array($data)) {
|
||||||
|
|
@ -85,7 +94,10 @@ abstract class Crypt
|
||||||
return static::Decrypt(\array_map('base64_decode', $data), $key);
|
return static::Decrypt(\array_map('base64_decode', $data), $key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function DecryptUrlSafe(string $data, string $key = null) /* : mixed */
|
public static function DecryptUrlSafe(string $data,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $key = null
|
||||||
|
) /* : mixed */
|
||||||
{
|
{
|
||||||
$data = \explode('.', $data);
|
$data = \explode('.', $data);
|
||||||
if (!\is_array($data)) {
|
if (!\is_array($data)) {
|
||||||
|
|
@ -95,7 +107,12 @@ abstract class Crypt
|
||||||
return static::Decrypt(\array_map('MailSo\\Base\\Utils::UrlSafeBase64Decode', $data), $key);
|
return static::Decrypt(\array_map('MailSo\\Base\\Utils::UrlSafeBase64Decode', $data), $key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function Encrypt($data, string $key = null) : array
|
public static function Encrypt(
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
$data,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $key = null
|
||||||
|
) : array
|
||||||
{
|
{
|
||||||
$data = \json_encode($data);
|
$data = \json_encode($data);
|
||||||
|
|
||||||
|
|
@ -128,17 +145,30 @@ abstract class Crypt
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function EncryptToJSON($data, string $key = null) : string
|
public static function EncryptToJSON(
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
$data,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $key = null
|
||||||
|
) : string
|
||||||
{
|
{
|
||||||
return \json_encode(\array_map('base64_encode', static::Encrypt($data, $key)));
|
return \json_encode(\array_map('base64_encode', static::Encrypt($data, $key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function EncryptUrlSafe($data, string $key = null) : string
|
public static function EncryptUrlSafe(
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
$data,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $key = null
|
||||||
|
) : string
|
||||||
{
|
{
|
||||||
return \implode('.', \array_map('MailSo\\Base\\Utils::UrlSafeBase64Encode', static::Encrypt($data, $key)));
|
return \implode('.', \array_map('MailSo\\Base\\Utils::UrlSafeBase64Encode', static::Encrypt($data, $key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function SodiumDecrypt(string $data, string $nonce, string $key = null) /* : string|false */
|
public static function SodiumDecrypt(string $data, string $nonce,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $key = null
|
||||||
|
) /* : string|false */
|
||||||
{
|
{
|
||||||
if (!\is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_decrypt')) {
|
if (!\is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_decrypt')) {
|
||||||
throw new \Exception('sodium_crypto_aead_xchacha20poly1305_ietf_decrypt not callable');
|
throw new \Exception('sodium_crypto_aead_xchacha20poly1305_ietf_decrypt not callable');
|
||||||
|
|
@ -151,7 +181,13 @@ abstract class Crypt
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function SodiumEncrypt(string $data, string $nonce, string $key = null) : string
|
public static function SodiumEncrypt(
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $data,
|
||||||
|
string $nonce,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $key = null
|
||||||
|
) : string
|
||||||
{
|
{
|
||||||
if (!\is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_encrypt')) {
|
if (!\is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_encrypt')) {
|
||||||
throw new \Exception('sodium_crypto_aead_xchacha20poly1305_ietf_encrypt not callable');
|
throw new \Exception('sodium_crypto_aead_xchacha20poly1305_ietf_encrypt not callable');
|
||||||
|
|
@ -168,7 +204,10 @@ abstract class Crypt
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function OpenSSLDecrypt(string $data, string $iv, string $key = null) /* : string|false */
|
public static function OpenSSLDecrypt(string $data, string $iv,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $key = null
|
||||||
|
) /* : string|false */
|
||||||
{
|
{
|
||||||
if (!$data || !$iv) {
|
if (!$data || !$iv) {
|
||||||
throw new \InvalidArgumentException('$data or $iv is empty string');
|
throw new \InvalidArgumentException('$data or $iv is empty string');
|
||||||
|
|
@ -189,7 +228,13 @@ abstract class Crypt
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function OpenSSLEncrypt(string $data, string $iv, string $key = null) : string
|
public static function OpenSSLEncrypt(
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $data,
|
||||||
|
string $iv,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $key = null
|
||||||
|
) : string
|
||||||
{
|
{
|
||||||
if (!$data || !$iv) {
|
if (!$data || !$iv) {
|
||||||
throw new \InvalidArgumentException('$data or $iv is empty string');
|
throw new \InvalidArgumentException('$data or $iv is empty string');
|
||||||
|
|
@ -214,7 +259,10 @@ abstract class Crypt
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function XxteaDecrypt(string $data, string $salt, string $key = null) /* : mixed */
|
public static function XxteaDecrypt(string $data, string $salt,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $key = null
|
||||||
|
) /* : mixed */
|
||||||
{
|
{
|
||||||
if (!$data || !$salt) {
|
if (!$data || !$salt) {
|
||||||
throw new \InvalidArgumentException('$data or $salt is empty string');
|
throw new \InvalidArgumentException('$data or $salt is empty string');
|
||||||
|
|
@ -225,7 +273,13 @@ abstract class Crypt
|
||||||
: \MailSo\Base\Xxtea::decrypt($data, $key);
|
: \MailSo\Base\Xxtea::decrypt($data, $key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function XxteaEncrypt(string $data, string $salt, string $key = null) : string
|
public static function XxteaEncrypt(
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $data,
|
||||||
|
string $salt,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $key = null
|
||||||
|
) : string
|
||||||
{
|
{
|
||||||
if (!$data || !$salt) {
|
if (!$data || !$salt) {
|
||||||
throw new \InvalidArgumentException('$data or $salt is empty string');
|
throw new \InvalidArgumentException('$data or $salt is empty string');
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,10 @@ abstract class Request
|
||||||
$this->user_agent = 'SnappyMail/' . APP_VERSION;
|
$this->user_agent = 'SnappyMail/' . APP_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setAuth(int $type, string $user, string $pass) : void
|
public function setAuth(int $type, string $user,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $pass
|
||||||
|
) : void
|
||||||
{
|
{
|
||||||
$this->auth = [
|
$this->auth = [
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,10 @@ class GnuPG
|
||||||
/**
|
/**
|
||||||
* Add a key for decryption
|
* Add a key for decryption
|
||||||
*/
|
*/
|
||||||
public function addDecryptKey(string $fingerprint, string $passphrase) : bool
|
public function addDecryptKey(string $fingerprint,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $passphrase
|
||||||
|
) : bool
|
||||||
{
|
{
|
||||||
return $this->handler()->adddecryptkey($fingerprint, $passphrase);
|
return $this->handler()->adddecryptkey($fingerprint, $passphrase);
|
||||||
}
|
}
|
||||||
|
|
@ -100,7 +103,10 @@ class GnuPG
|
||||||
/**
|
/**
|
||||||
* Add a key for signing
|
* Add a key for signing
|
||||||
*/
|
*/
|
||||||
public function addSignKey(string $fingerprint, ?string $passphrase) : bool
|
public function addSignKey(string $fingerprint,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
?string $passphrase
|
||||||
|
) : bool
|
||||||
{
|
{
|
||||||
return $this->handler()->addsignkey($fingerprint, $passphrase);
|
return $this->handler()->addsignkey($fingerprint, $passphrase);
|
||||||
}
|
}
|
||||||
|
|
@ -218,7 +224,10 @@ class GnuPG
|
||||||
/**
|
/**
|
||||||
* Exports a key
|
* Exports a key
|
||||||
*/
|
*/
|
||||||
public function export(string $fingerprint, string $passphrase = '') /*: string|false*/
|
public function export(string $fingerprint,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $passphrase = ''
|
||||||
|
) /*: string|false*/
|
||||||
{
|
{
|
||||||
if ($passphrase) {
|
if ($passphrase) {
|
||||||
return $this->getGPG()
|
return $this->getGPG()
|
||||||
|
|
@ -265,7 +274,10 @@ class GnuPG
|
||||||
/**
|
/**
|
||||||
* Generates a key
|
* Generates a key
|
||||||
*/
|
*/
|
||||||
public function generateKey(string $uid, string $passphrase) /*: string|false*/
|
public function generateKey(string $uid,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $passphrase
|
||||||
|
) /*: string|false*/
|
||||||
{
|
{
|
||||||
$GPG = $this->getGPG(false);
|
$GPG = $this->getGPG(false);
|
||||||
return $GPG ? $GPG->generateKey($uid, $passphrase) : false;
|
return $GPG ? $GPG->generateKey($uid, $passphrase) : false;
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,10 @@ class GPG
|
||||||
/**
|
/**
|
||||||
* Add a key for decryption
|
* Add a key for decryption
|
||||||
*/
|
*/
|
||||||
public function addDecryptKey(string $fingerprint, string $passphrase) : bool
|
public function addDecryptKey(string $fingerprint,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $passphrase
|
||||||
|
) : bool
|
||||||
{
|
{
|
||||||
$this->decryptKeys[$fingerprint] = $passphrase;
|
$this->decryptKeys[$fingerprint] = $passphrase;
|
||||||
// $this->decryptKeys[\substr($fingerprint, -16)] = $passphrase;
|
// $this->decryptKeys[\substr($fingerprint, -16)] = $passphrase;
|
||||||
|
|
@ -155,7 +158,10 @@ class GPG
|
||||||
/**
|
/**
|
||||||
* Add a key for signing
|
* Add a key for signing
|
||||||
*/
|
*/
|
||||||
public function addSignKey(string $fingerprint, string $passphrase) : bool
|
public function addSignKey(string $fingerprint,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $passphrase
|
||||||
|
) : bool
|
||||||
{
|
{
|
||||||
$this->signKeys[$fingerprint] = $passphrase;
|
$this->signKeys[$fingerprint] = $passphrase;
|
||||||
// $this->signKeys[\substr($fingerprint, -16)] = $passphrase;
|
// $this->signKeys[\substr($fingerprint, -16)] = $passphrase;
|
||||||
|
|
@ -419,7 +425,10 @@ class GPG
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addPassphrase($keyId, $passphrase)
|
public function addPassphrase($keyId,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
$passphrase
|
||||||
|
)
|
||||||
{
|
{
|
||||||
$this->passphrases[$keyId] = $passphrase;
|
$this->passphrases[$keyId] = $passphrase;
|
||||||
return $this;
|
return $this;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,11 @@ class Cram extends \SnappyMail\SASL
|
||||||
$this->algo = $algo;
|
$this->algo = $algo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function authenticate(string $authcid, string $passphrase, ?string $challenge = null) : string
|
public function authenticate(string $authcid,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $passphrase,
|
||||||
|
?string $challenge = null
|
||||||
|
) : string
|
||||||
{
|
{
|
||||||
return $this->encode($authcid . ' ' . \hash_hmac($this->algo, $this->decode($challenge), $passphrase));
|
return $this->encode($authcid . ' ' . \hash_hmac($this->algo, $this->decode($challenge), $passphrase));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,19 +2,24 @@
|
||||||
|
|
||||||
namespace SnappyMail\SASL;
|
namespace SnappyMail\SASL;
|
||||||
|
|
||||||
|
use SnappyMail\SensitiveString;
|
||||||
|
|
||||||
class Login extends \SnappyMail\SASL
|
class Login extends \SnappyMail\SASL
|
||||||
{
|
{
|
||||||
protected
|
protected
|
||||||
$passphrase;
|
SensitiveString $passphrase;
|
||||||
|
|
||||||
public function authenticate(string $username, string $passphrase, ?string $challenge = null) : string
|
public function authenticate(string $username,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $passphrase,
|
||||||
|
?string $challenge = null) : string
|
||||||
{
|
{
|
||||||
// $challenge should be 'VXNlcm5hbWU6', but broken on some systems
|
// $challenge should be 'VXNlcm5hbWU6', but broken on some systems
|
||||||
// See https://github.com/the-djmaze/snappymail/issues/693
|
// See https://github.com/the-djmaze/snappymail/issues/693
|
||||||
if ($challenge && !\str_starts_with($this->decode($challenge), 'Username:')) {
|
if ($challenge && !\str_starts_with($this->decode($challenge), 'Username:')) {
|
||||||
throw new \Exception("Invalid response: {$this->decode($challenge)}");
|
throw new \Exception("Invalid response: {$this->decode($challenge)}");
|
||||||
}
|
}
|
||||||
$this->passphrase = $passphrase;
|
$this->passphrase = new SensitiveString($passphrase);
|
||||||
return $this->encode($username);
|
return $this->encode($username);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -24,7 +29,7 @@ class Login extends \SnappyMail\SASL
|
||||||
if ($challenge && 'Password:' !== $this->decode($challenge)) {
|
if ($challenge && 'Password:' !== $this->decode($challenge)) {
|
||||||
throw new \Exception("invalid response: {$challenge}");
|
throw new \Exception("invalid response: {$challenge}");
|
||||||
}
|
}
|
||||||
return $this->encode($this->passphrase);
|
return $this->encode($this->passphrase->getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function isSupported(string $param) : bool
|
public static function isSupported(string $param) : bool
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,15 @@
|
||||||
* https://developers.google.com/gmail/imap/xoauth2-protocol
|
* https://developers.google.com/gmail/imap/xoauth2-protocol
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace SnappyMail\SASL;
|
namespace SnappyMail\SASL;
|
||||||
|
|
||||||
class OAuthBearer extends \SnappyMail\SASL
|
class OAuthBearer extends \SnappyMail\SASL
|
||||||
{
|
{
|
||||||
public function authenticate(string $username, string $passphrase, ?string $authzid = null) : string
|
public function authenticate(string $username,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $passphrase,
|
||||||
|
?string $authzid = null
|
||||||
|
) : string
|
||||||
{
|
{
|
||||||
return $this->encode("n,a={$username},\x01auth=Bearer {$passphrase}\x01\x01");
|
return $this->encode("n,a={$username},\x01auth=Bearer {$passphrase}\x01\x01");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@ namespace SnappyMail\SASL;
|
||||||
class Plain extends \SnappyMail\SASL
|
class Plain extends \SnappyMail\SASL
|
||||||
{
|
{
|
||||||
|
|
||||||
public function authenticate(string $username, string $passphrase, ?string $authzid = null) : string
|
public function authenticate(string $username,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $passphrase,
|
||||||
|
?string $authzid = null) : string
|
||||||
{
|
{
|
||||||
return $this->encode("{$authzid}\x00{$username}\x00{$passphrase}");
|
return $this->encode("{$authzid}\x00{$username}\x00{$passphrase}");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,16 @@
|
||||||
* https://tools.ietf.org/html/rfc7677
|
* https://tools.ietf.org/html/rfc7677
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace SnappyMail\SASL;
|
namespace SnappyMail\SASL;
|
||||||
|
|
||||||
|
use SnappyMail\SensitiveString;
|
||||||
|
|
||||||
class Scram extends \SnappyMail\SASL
|
class Scram extends \SnappyMail\SASL
|
||||||
{
|
{
|
||||||
|
protected ?SensitiveString $passphrase;
|
||||||
protected
|
protected
|
||||||
$algo,
|
$algo,
|
||||||
$nonce,
|
$nonce,
|
||||||
$passphrase,
|
|
||||||
$gs2_header,
|
$gs2_header,
|
||||||
$auth_message,
|
$auth_message,
|
||||||
$server_key;
|
$server_key;
|
||||||
|
|
@ -31,13 +31,17 @@ class Scram extends \SnappyMail\SASL
|
||||||
$this->algo = $algo;
|
$this->algo = $algo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function authenticate(string $authcid, string $passphrase, ?string $authzid = null) : string
|
public function authenticate(string $authcid,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $passphrase,
|
||||||
|
?string $authzid = null
|
||||||
|
) : string
|
||||||
{
|
{
|
||||||
// SASLprep
|
// SASLprep
|
||||||
$authcid = \str_replace(array('=',','), array('=3D','=2C'), $authcid);
|
$authcid = \str_replace(array('=',','), array('=3D','=2C'), $authcid);
|
||||||
|
|
||||||
$this->nonce = \bin2hex(\random_bytes(16));
|
$this->nonce = \bin2hex(\random_bytes(16));
|
||||||
$this->passphrase = $passphrase;
|
$this->passphrase = new SensitiveString($passphrase);
|
||||||
$this->gs2_header = 'n,' . (empty($authzid) ? '' : 'a=' . $authzid) . ',';
|
$this->gs2_header = 'n,' . (empty($authzid) ? '' : 'a=' . $authzid) . ',';
|
||||||
$this->auth_message = "n={$authcid},r={$this->nonce}";
|
$this->auth_message = "n={$authcid},r={$this->nonce}";
|
||||||
return $this->encode($this->gs2_header . $this->auth_message);
|
return $this->encode($this->gs2_header . $this->auth_message);
|
||||||
|
|
@ -67,7 +71,7 @@ class Scram extends \SnappyMail\SASL
|
||||||
throw new \Exception('Server invalid salt');
|
throw new \Exception('Server invalid salt');
|
||||||
}
|
}
|
||||||
|
|
||||||
$pass = \hash_pbkdf2($this->algo, $this->passphrase, $salt, \intval($values['i']), 0, true);
|
$pass = \hash_pbkdf2($this->algo, $this->passphrase->getValue(), $salt, \intval($values['i']), 0, true);
|
||||||
$this->passphrase = null;
|
$this->passphrase = null;
|
||||||
|
|
||||||
$ckey = \hash_hmac($this->algo, 'Client Key', $pass, true);
|
$ckey = \hash_hmac($this->algo, 'Client Key', $pass, true);
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,15 @@
|
||||||
* https://developers.google.com/gmail/imap/xoauth2-protocol
|
* https://developers.google.com/gmail/imap/xoauth2-protocol
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace SnappyMail\SASL;
|
namespace SnappyMail\SASL;
|
||||||
|
|
||||||
class XOAuth2 extends \SnappyMail\SASL
|
class XOAuth2 extends \SnappyMail\SASL
|
||||||
{
|
{
|
||||||
public function authenticate(string $username, string $passphrase, ?string $authzid = null) : string
|
public function authenticate(string $username,
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $passphrase,
|
||||||
|
?string $authzid = null
|
||||||
|
) : string
|
||||||
{
|
{
|
||||||
return $this->encode("user={$username}\x01auth=Bearer {$passphrase}\x01\x01");
|
return $this->encode("user={$username}\x01auth=Bearer {$passphrase}\x01\x01");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SnappyMail;
|
||||||
|
|
||||||
|
function xorIt(
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $value
|
||||||
|
) : string
|
||||||
|
{
|
||||||
|
$key = APP_SALT;
|
||||||
|
$kl = \strlen($key);
|
||||||
|
$i = \strlen($value);
|
||||||
|
while ($i--) {
|
||||||
|
$value[$i] = $value[$i] ^ $key[$i % $kl];
|
||||||
|
}
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SensitiveString /* extends SensitiveParameterValue | SensitiveParameter */ implements \Stringable
|
||||||
|
{
|
||||||
|
private string $value, $nonce;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $value
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$this->setValue($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValue(): string
|
||||||
|
{
|
||||||
|
if (\is_callable('sodium_crypto_secretbox')) {
|
||||||
|
return \sodium_crypto_secretbox_open($this->value, $this->nonce, APP_SALT);
|
||||||
|
}
|
||||||
|
return xorIt($this->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setValue(
|
||||||
|
#[\SensitiveParameter]
|
||||||
|
string $value
|
||||||
|
) : void
|
||||||
|
{
|
||||||
|
if (\is_callable('sodium_crypto_secretbox')) {
|
||||||
|
$this->nonce = \random_bytes(\SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
|
||||||
|
// $this->key = \sodium_crypto_secretbox_keygen();
|
||||||
|
$this->value = \sodium_crypto_secretbox($value, $this->nonce, APP_SALT);
|
||||||
|
} else {
|
||||||
|
$this->value = xorIt($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return $this->getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __debugInfo(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __serialize(): array
|
||||||
|
{
|
||||||
|
throw new \Exception("Serialization of 'SensitiveString' is not allowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __unserialize(array $data): void
|
||||||
|
{
|
||||||
|
throw new \Exception("Unserialization of 'SensitiveString' is not allowed");
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue