diff --git a/dev/Stores/User/GnuPG.js b/dev/Stores/User/GnuPG.js index a3bdfba56..e390162d5 100644 --- a/dev/Stores/User/GnuPG.js +++ b/dev/Stores/User/GnuPG.js @@ -69,10 +69,10 @@ export const GnuPGUserStore = new class { } }; if (isPrivate) { - key.password = async (btnTxt = 'SIGN') => { + key.password = async (btnTxt = 'CRYPTO/SIGN') => { const pass = await Passphrases.ask(key, 'GnuPG key
' + key.id + ' ' + key.emails[0], - 'OPENPGP/'+btnTxt + btnTxt ); pass && pass.remember && Passphrases.set(key, pass.password); return pass?.password; @@ -82,7 +82,7 @@ export const GnuPGUserStore = new class { if (key.armor) { callback && callback(); } else { - let pass = isPrivate ? await key.password('POPUP_VIEW_TITLE') : ''; + let pass = isPrivate ? await key.password('OPENPGP/POPUP_VIEW_TITLE') : ''; if (null != pass) try { const result = await Remote.post('GnupgExportKey', null, { keyId: key.id, @@ -184,15 +184,20 @@ export const GnuPGUserStore = new class { uid: message.uid, partId: pgpInfo.partId, keyId: key.id, - passphrase: await key.password('DECRYPT'), + passphrase: await key.password('CRYPTO/DECRYPT'), data: '' // message.plain() optional } - if (null !== params.passphrase) { - const result = await Remote.post('GnupgDecrypt', null, params); - if (result?.Result?.data) { - return result.Result; + if (null != params.passphrase) { + try { + const response = await Remote.post('GnupgDecrypt', null, params); + if (response?.Result?.data) { + return response.Result; + } + throw response; + } catch (e) { + Passphrases.delete(key); + throw e; } - Passphrases.delete(key); } } } diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 551915799..70c113131 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -587,10 +587,13 @@ export class MailMessageView extends AbstractViewRight { } } else { // TODO: translate - alert('Decryption failed, canceled or not possible'); + throw 'Decryption failed, canceled or not possible'; } }) - .catch(e => console.error(e)); + .catch(e => { + console.error(e) + alert(e.message); + }); } pgpVerify(/*self, event*/) { diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php index ac427165f..59e10e655 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php @@ -120,7 +120,7 @@ trait Pgp $sData = $this->GetActionParam('data', ''); $oPart = null; $result = [ - 'data' => '', + 'data' => null, 'signatures' => [] ]; if ($sData) { @@ -130,11 +130,13 @@ trait Pgp $this->initMailClientConnection(); $this->MailClient()->MessageMimeStream( function ($rResource) use ($GPG, &$result, &$oPart) { - if (\is_resource($rResource)) { + if (\is_resource($rResource)) try { $result['data'] = $GPG->decryptStream($rResource); // $oPart = \MailSo\Mime\Part::FromString($result); // $GPG->decryptStream($rResource, $rStreamHandle); // $oPart = \MailSo\Mime\Part::FromStream($rStreamHandle); + } catch (\Throwable $e) { + $result = $e; } }, $this->GetActionParam('folder', ''), @@ -148,6 +150,10 @@ trait Pgp // $result['signatures'] = $oPart->SubParts[0]; } + if ($result instanceof \Throwable) { + throw $result; + } + return $this->DefaultResponse($result); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php index 446d3fcc4..a61ba47fc 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php @@ -45,9 +45,10 @@ trait Response public function ExceptionResponse(\Throwable $oException) : array { - $iErrorCode = 0; - $sErrorMessage = ''; + $iErrorCode = Notifications::UnknownError; + $sErrorMessage = $oException->getMessage(); $sErrorMessageAdditional = ''; + $iExceptionCode = 0; if ($oException instanceof \RainLoop\Exceptions\ClientException) { $iErrorCode = $oException->getCode(); @@ -56,8 +57,7 @@ trait Response } $sErrorMessageAdditional = $oException->getAdditionalMessage(); } else { - $iErrorCode = Notifications::UnknownError; - $sErrorMessage = $oException->getCode().' - '.$oException->getMessage(); + $iExceptionCode = $oException->getCode(); } $this->logException($oException->getPrevious() ?: $oException); @@ -65,7 +65,8 @@ trait Response return $this->DefaultResponse(false, [ 'ErrorCode' => $iErrorCode, 'ErrorMessage' => $sErrorMessage, - 'ErrorMessageAdditional' => $sErrorMessageAdditional + 'ErrorMessageAdditional' => $sErrorMessageAdditional, + 'ExceptionCode' => $iExceptionCode ]); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php index 627c24a9b..5e7092b26 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php @@ -114,14 +114,15 @@ class ServiceActions if ($_POST) { $this->oActions->SetActionParams($_POST, $sMethodName); - foreach ($_POST as $key => $value) { + $aPost = $_POST; + foreach ($aPost as $key => $value) { // password & passphrase if (false !== \stripos($key, 'pass')) { -// $_POST[$key] = '*******'; - $this->oActions->logMask($value); + $aPost[$key] = '*******'; +// $this->oActions->logMask($value); } } - $this->oActions->logWrite(Utils::jsonEncode($_POST), \LOG_INFO, 'POST'); + $this->oActions->logWrite(Utils::jsonEncode($aPost), \LOG_INFO, 'POST'); } else if (3 < \count($this->aPaths) && $this->oHttp->IsGet()) { $this->oActions->SetActionParams(array( 'RawKey' => empty($this->aPaths[3]) ? '' : $this->aPaths[3] diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/gpg/base.php b/snappymail/v/0.0.0/app/libraries/snappymail/gpg/base.php index 3436e2339..168f8f63f 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/gpg/base.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/gpg/base.php @@ -243,7 +243,7 @@ abstract class Base /** * Signs a given file */ - public function signStream($fp, /*string|resource*/ $output = null) /*: array|false*/ + public function signStream($fp, /*string|resource*/ $output = null) /*: string|false*/ { return false; } diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/gpg/pgp.php b/snappymail/v/0.0.0/app/libraries/snappymail/gpg/pgp.php index eef6ab062..40b08252a 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/gpg/pgp.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/gpg/pgp.php @@ -666,7 +666,7 @@ class PGP extends Base /** * Signs a given file */ - public function signStream($fp, /*string|resource*/ $output = null) /*: array|false*/ + public function signStream($fp, /*string|resource*/ $output = null) /*: string|false*/ { if (!$fp || !\is_resource($fp)) { throw new \Exception('Invalid stream resource'); @@ -674,7 +674,7 @@ class PGP extends Base return $this->_sign($fp, $output); } - protected function _verify($input, string $signature) + protected function _verify($input, string $signature) /*: array|false*/ { $arguments = ['--verify']; if ($signature) { diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/gpg/smime.php b/snappymail/v/0.0.0/app/libraries/snappymail/gpg/smime.php index b958e2d6b..8adff5a7a 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/gpg/smime.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/gpg/smime.php @@ -540,7 +540,7 @@ class SMIME extends Base /** * Signs a given file */ - public function signStream($fp, /*string|resource*/ $output = null) /*: array|false*/ + public function signStream($fp, /*string|resource*/ $output = null) /*: string|false*/ { if (!$fp || !\is_resource($fp)) { throw new \Exception('Invalid stream resource'); @@ -548,7 +548,7 @@ class SMIME extends Base return $this->_sign($fp, $output); } - protected function _verify($input, string $signature) + protected function _verify($input, string $signature) /*: array|false*/ { $arguments = ['--verify']; if ($signature) { diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gnupg.php b/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gnupg.php index 799d15142..88215493e 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gnupg.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gnupg.php @@ -36,6 +36,7 @@ class GnuPG ]); // Output is ASCII $this->GnuPG->setarmor(1); + \SnappyMail\Log::info('GnuPG', 'Using PECL'); } else { $this->getGPG(); } @@ -52,6 +53,12 @@ class GnuPG */ } + function __destruct() + { + $this->clearDecryptKeys(); + $this->clearSignKeys(); + } + public static function isSupported() : bool { return \class_exists('gnupg') || GPG::isSupported(); @@ -71,11 +78,20 @@ class GnuPG return $this->GnuPG ?: $this->GPG; } + public function gnupgError() + { + $error = $this->GnuPG ? $this->GnuPG->geterrorinfo() : null; + if ($error) { + throw new \Exception("{$error['gpgme_source']} {$error['generic_message']}. {$error['gpgme_message']}", $error['gpgme_code']); + } + } + public function getGPG(bool $throw = true) : ?GPG { if (!$this->GPG) { if (GPG::isSupported()) { $this->GPG = new GPG($this->homedir); + \SnappyMail\Log::info('GnuPG', 'Using ' . GPG::class); } else if ($throw) { throw new \Exception('GnuPG not supported'); } @@ -88,7 +104,9 @@ class GnuPG */ public function addDecryptKey(string $fingerprint, \SnappyMail\SensitiveString $passphrase) : bool { - return $this->handler()->adddecryptkey($fingerprint, $passphrase); + return $this->GnuPG + ? $this->GnuPG->adddecryptkey($fingerprint, \strval($passphrase)) || $this->gnupgError() + : $this->GPG->addDecryptKey($fingerprint, $passphrase); } /** @@ -104,7 +122,9 @@ class GnuPG */ public function addSignKey(string $fingerprint, \SnappyMail\SensitiveString $passphrase) : bool { - return $this->handler()->addsignkey($fingerprint, $passphrase); + return $this->GnuPG + ? $this->GnuPG->addsignkey($fingerprint, \strval($passphrase)) || $this->gnupgError() + : $this->GPG->addSignKey($fingerprint, $passphrase); } /** @@ -136,9 +156,12 @@ class GnuPG */ public function decrypt(string $text) /*: string|false */ { - return $this->GnuPG - ? $this->GnuPG->decrypt($text) - : $this->GPG->decrypt($text); + if ($this->GnuPG) { + $result = $this->GnuPG->decrypt($text); + (false === $result) && $this->gnupgError(); + return $result; + } + return $this->GPG->decrypt($text); } /** @@ -146,9 +169,12 @@ class GnuPG */ public function decryptFile(string $filename) /*: string|false */ { - return $this->GnuPG - ? $this->GnuPG->decrypt(\file_get_contents($filename)) - : $this->GPG->decryptFile($filename); + if ($this->GnuPG) { + $result = $this->GnuPG->decrypt(\file_get_contents($filename)); + (false === $result) && $this->gnupgError(); + return $result; + } + return $this->GPG->decryptFile($filename); } /** @@ -159,9 +185,12 @@ class GnuPG if (!$fp || !\is_resource($fp)) { throw new \Exception('Invalid stream resource'); } - return $this->GnuPG - ? $this->GnuPG->decrypt(\stream_get_contents($fp)) - : $this->GPG->decryptStream($fp, $output); + if ($this->GnuPG) { + $result = $this->GnuPG->decrypt(\stream_get_contents($fp)); + (false === $result) && $this->gnupgError(); + return $result; + } + return $this->GPG->decryptStream($fp, $output); } /** @@ -169,9 +198,12 @@ class GnuPG */ public function decryptVerify(string $text, string &$plaintext) /*: array|false*/ { - return $this->GnuPG - ? $this->GnuPG->decryptverify($text, $plaintext) - : $this->GPG->decryptverify($text, $plaintext); + if ($this->GnuPG) { + $result = $this->GnuPG->decryptverify($text, $plaintext); + (false === $result) && $this->gnupgError(); + return $result; + } + return $this->GPG->decryptverify($text, $plaintext); } /** @@ -179,9 +211,12 @@ class GnuPG */ public function decryptVerifyFile(string $filename, string &$plaintext) /*: array|false*/ { - return $this->GnuPG - ? $this->GnuPG->decryptverify(\file_get_contents($filename), $plaintext) - : $this->GPG->decryptverifyFile($filename, $plaintext); + if ($this->GnuPG) { + $result = $this->GnuPG->decryptverify(\file_get_contents($filename), $plaintext); + (false === $result) && $this->gnupgError(); + return $result; + } + return $this->GPG->decryptverifyFile($filename, $plaintext); } public function deleteKey(string $keyId, bool $private) : bool @@ -195,7 +230,7 @@ class GnuPG public function encrypt(string $plaintext) /*: string|false*/ { return $this->GnuPG - ? $this->GnuPG->encrypt($plaintext) + ? $this->GnuPG->encrypt($plaintext) ?: $this->gnupgError() : $this->GPG->encrypt($plaintext); } @@ -205,7 +240,7 @@ class GnuPG public function encryptFile(string $filename) /*: string|false*/ { return $this->GnuPG - ? $this->GnuPG->encrypt(\file_get_contents($filename)) + ? $this->GnuPG->encrypt(\file_get_contents($filename)) ?: $this->gnupgError() : $this->GPG->encryptFile($filename); } @@ -213,7 +248,7 @@ class GnuPG { \rewind($fp); return $this->GnuPG - ? $this->GnuPG->encrypt(\stream_get_contents($fp)) + ? $this->GnuPG->encrypt(\stream_get_contents($fp)) ?: $this->gnupgError() : $this->GPG->encryptStream($fp); } @@ -228,7 +263,7 @@ class GnuPG ->exportPrivateKey($fingerprint); } return $this->GnuPG - ? $this->GnuPG->export($fingerprint) + ? $this->GnuPG->export($fingerprint) ?: $this->gnupgError() : $this->GPG->export($fingerprint); } @@ -287,7 +322,7 @@ class GnuPG public function importFile(string $filename) /*: array|false*/ { return $this->GnuPG - ? $this->GnuPG->import(\file_get_contents($filename)) + ? $this->GnuPG->import(\file_get_contents($filename)) ?: $this->gnupgError() : $this->GPG->importFile($filename); } @@ -350,7 +385,7 @@ class GnuPG public function sign(string $plaintext) /*: string|false*/ { return $this->GnuPG - ? $this->GnuPG->sign($plaintext) + ? $this->GnuPG->sign($plaintext) ?: $this->gnupgError() : $this->GPG->sign($plaintext); } @@ -360,18 +395,18 @@ class GnuPG public function signFile(string $filename) /*: string|false*/ { return $this->GnuPG - ? $this->GnuPG->sign(\file_get_contents($filename)) + ? $this->GnuPG->sign(\file_get_contents($filename)) ?: $this->gnupgError() : $this->GPG->signFile($filename); } /** * Signs a given file */ - public function signStream($fp, /*string|resource*/ $output = null) /*: array|false*/ + public function signStream($fp, /*string|resource*/ $output = null) /*: string|false*/ { \rewind($fp); return $this->GnuPG - ? $this->GnuPG->sign(\stream_get_contents($fp)) + ? $this->GnuPG->sign(\stream_get_contents($fp)) ?: $this->gnupgError() : $this->GPG->signStream($fp); } @@ -381,7 +416,7 @@ class GnuPG public function verify(string $signed_text, string $signature, string &$plaintext = null) /*: array|false*/ { $result = $this->GnuPG - ? $this->GnuPG->verify($signed_text, $signature ?: false, $plaintext) + ? $this->GnuPG->verify($signed_text, $signature ?: false, $plaintext) ?: $this->gnupgError() : $this->GPG->verify($signed_text, $signature, $plaintext); if (!$result) { if ($this->GnuPG) { @@ -400,18 +435,21 @@ class GnuPG public function verifyFile(string $filename, string $signature, string &$plaintext = null) /*: array|false*/ { return $this->GnuPG - ? $this->GnuPG->verify(\file_get_contents($filename), $signature, $plaintext) + ? $this->GnuPG->verify(\file_get_contents($filename), $signature, $plaintext) ?: $this->gnupgError() : $this->GPG->verifyFile($filename, $signature, $plaintext); } /** * Verifies a given resource */ - public function verifyStream(/*resource*/ $fp, string $signature, string &$plaintext = null) /*: string|false */ + public function verifyStream(/*resource*/ $fp, string $signature, string &$plaintext = null) /*: array|false */ { if (!$fp || !\is_resource($fp)) { throw new \Exception('Invalid stream resource'); } +// return $this->GnuPG +// ? $this->GnuPG->verify(\stream_get_contents($fp), $signature, $plaintext) ?: $this->gnupgError() +// : $this->GPG->verifyStream($fp, $signature, $plaintext); return $this->getGPG()->verifyStream($fp, $signature, $plaintext); }