From 8dc949f15c50657c8fe532411f6188c42039a768 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 26 Jan 2022 12:15:36 +0100 Subject: [PATCH] Added DoGnupgDecrypt(), DoGetStoredPGPKeys() and DoStorePGPKey() for #89 --- .../libraries/RainLoop/Actions/Messages.php | 6 +- .../app/libraries/RainLoop/Actions/Pgp.php | 111 ++++++++++++++++++ .../app/libraries/snappymail/pgp/gnupg.php | 23 ++++ 3 files changed, 138 insertions(+), 2 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php index 772c4d83a..c3f07dd76 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php @@ -696,12 +696,14 @@ trait Messages 'text' => \preg_replace('/\\R/s', "\r\n", $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sBodyPartId.']') ), - 'signature' => false + 'signature' => '' ]; } - if (\class_exists('gnupg')) { + $GPG = $this->GnuPG(); + if ($GPG) { $info = $this->GnuPG()->verify($result['text'], $result['signature'])[0]; +// $info = $this->GnuPG()->verifyStream($fp, $result['signature'])[0]; /** * https://code.woboq.org/qt5/include/gpg-error.h.html 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 7d3783e99..ec26affdd 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 @@ -4,6 +4,10 @@ namespace RainLoop\Actions; trait Pgp { + /** + * Also see trait Messages::DoMessagePgpVerify + */ + /** * @throws \MailSo\Base\Exceptions\Exception */ @@ -46,6 +50,43 @@ trait Pgp return \SnappyMail\PGP\GnuPG::getInstance($homedir); } + public function DoGnupgDecrypt() : array + { + $GPG = $this->GnuPG(); + if (!$GPG) { + return $this->FalseResponse(__FUNCTION__); + } + + $GPG->addDecryptKey( + $this->GetActionParam('KeyId', ''), + $this->GetActionParam('Passphrase', '') + ); + + $sData = $this->GetActionParam('Data', ''); + $oPart = null; + if ($sData) { + $result = $GPG->decrypt($sData); + $oPart = \MailSo\Mime\Part::FromString($result); + } else { + $this->initMailClientConnection(); + $this->MailClient()->MessageMimeStream( + function ($rResource) use ($GPG, $oPart) { + if (\is_resource($rResource)) { + $result = $GPG->decryptStream($rResource); + $oPart = \MailSo\Mime\Part::FromString($result); +// $GPG->decryptStream($rResource, $rStreamHandle); +// $oPart = \MailSo\Mime\Part::FromStream($rStreamHandle); + } + }, + $this->GetActionParam('Folder', ''), + (int) $this->GetActionParam('Uid', ''), + $this->GetActionParam('PartId', '') + ); + } + + return $this->DefaultResponse(__FUNCTION__, $oPart); + } + public function DoGnupgGetKeys() : array { $GPG = $this->GnuPG(); @@ -113,4 +154,74 @@ trait Pgp ? $this->DefaultResponse(__FUNCTION__, $GPG->import($sKey)) : $this->FalseResponse(__FUNCTION__); } + + /** + * Used to import keys in OpenPGP.js + * Handy when using multiple browsers + */ + public function DoGetStoredPGPKeys() : array + { + $oAccount = $this->getMainAccountFromToken(); + if (!$oAccount) { + return null; + } + + $dir = $this->StorageProvider()->GenerateFilePath( + $oAccount, + \RainLoop\Providers\Storage\Enumerations\StorageType::PGP + ); + + $keys = []; + foreach (\glob("{$dir}/*") as $file) { + if (\is_file($file)) { + if ('.asc' === \substr($file, -4)) { + $keys[] = \file_get_contents($file); + } else if ('.key' === \substr($file, -4)) { + $data = \json_decode(\file_get_contents($file), true); + $mac = \array_pop($sKey); + $hash = $oAccount->CryptKey(); + if ($mac === \hash_hmac('sha1', $data[2], $hash)) { + $keys[] = \SnappyMail\Crypt::Decrypt($data, $hash); + } + } + } + } + + return $this->DefaultResponse(__FUNCTION__, $keys); + } + + /** + * Used to store key from OpenPGP.js + * Handy when using multiple browsers + */ + public function DoStorePGPKey() : array + { + $oAccount = $this->getMainAccountFromToken(); + if (!$oAccount) { + return null; + } + + $key = $this->GetActionParam('Key', ''); + $keyId = $this->GetActionParam('KeyId', ''); + $result = false; + if ($key && $keyId) { + $dir = $this->StorageProvider()->GenerateFilePath( + $oAccount, + \RainLoop\Providers\Storage\Enumerations\StorageType::PGP + ); + if (\str_contains($key, 'PGP PRIVATE KEY')) { + $hash = $oAccount->CryptKey(); + $key = \SnappyMail\Crypt::Encrypt($key, $hash); + $key[] = \hash_hmac('sha1', $key[2], $hash); + $result = \file_put_contents("{$dir}/0x{$keyId}.key", \json_encode($key)); + } else if (\str_contains($key, 'PGP PUBLIC KEY')) { + $result = \file_put_contents("{$dir}/0x{$keyId}_public.asc", $key); + } + } + + return $result + ? $this->TrueResponse(__FUNCTION__) + : $this->FalseResponse(__FUNCTION__); + } + } 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 c337818bb..92ce1b60a 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 @@ -135,6 +135,17 @@ class GnuPG : $this->GPG->decryptFile($filename); } + /** + * Decrypts a given resource + */ + public function decryptStream(/*resource*/ $fp, /*string|resource*/ $output = null) /*: string|false */ + { + if (!$fp || !\is_resource($fp)) { + throw new \Exception('Invalid stream resource'); + } + return $this->getGPG()->decryptStream($fp, $output); + } + /** * Decrypts and verifies a given text */ @@ -404,4 +415,16 @@ class GnuPG ? $this->GnuPG->verify(\file_get_contents($filename), $signature, $plaintext) : $this->GPG->verifyFile($filename, $signature, $plaintext); } + + /** + * Verifies a given resource + */ + public function verifyStream(/*resource*/ $fp, string $signature, string &$plaintext = null) /*: string|false */ + { + if (!$fp || !\is_resource($fp)) { + throw new \Exception('Invalid stream resource'); + } + return $this->getGPG()->verifyStream($fp, $signature, $plaintext); + } + }