diff --git a/dev/Stores/User/Pgp.js b/dev/Stores/User/Pgp.js index f26347014..a61dccd3a 100644 --- a/dev/Stores/User/Pgp.js +++ b/dev/Stores/User/Pgp.js @@ -187,7 +187,24 @@ export const PgpUserStore = new class { } ); } - } + }; + key.view = () => { + let pass = isPrivate ? prompt('Passphrase') : true; + if (pass) { + Remote.request('GnupgExportKey', + (iError, oData) => { + if (oData && oData.Result) { + key.armor = oData.Result; + showScreenPopup(ViewOpenPgpKeyPopupView, [key]); + } + }, { + KeyId: key.id, + isPrivate: isPrivate, + Passphrase: isPrivate ? pass : '' + } + ); + } + }; return key; }; this.gnupgPublicKeys(oData.Result.public.map(key => initKey(key, 0))); diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index 4e4eb3257..19eb52ae5 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -460,7 +460,7 @@ class ComposePopupView extends AbstractViewPopup { try { const keys = PgpUserStore.getOpenPGPPrivateKeyFor(this.currentIdentity().email()); if (keys[0]) { - keys[0].decrypt(window.prompt('Password', '')); + keys[0].decrypt(window.prompt('Passphrase')); cfg.privateKey = privateKey = keys[0]; } } catch (e) { 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 0e54963a1..8924d4e05 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 @@ -98,6 +98,15 @@ trait Pgp return $this->DefaultResponse(__FUNCTION__, $GPG ? $GPG->keyInfo('') : false); } + public function DoGnupgExportKey() : array + { + $GPG = $this->GnuPG(); + return $this->DefaultResponse(__FUNCTION__, $GPG ? $GPG->export( + $this->GetActionParam('KeyId', ''), + $this->GetActionParam('Passphrase', '') + ) : false); + } + public function DoGnupgGenerateKey() : array { $fingerprint = false; 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 f5d2d3122..b58dd6cc7 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 @@ -38,8 +38,7 @@ class GnuPG public static function isSupported() : bool { - return \class_exists('gnupg') - || \SnappyMail\PGP\GPG::isSupported(); + return \class_exists('gnupg') || GPG::isSupported(); } private static $instance; @@ -56,13 +55,14 @@ class GnuPG return $this->GnuPG ?: $this->GPG; } - public function getGPG() + public function getGPG(bool $throw = true) : ?GPG { if (!$this->GPG) { - if (!\SnappyMail\PGP\GPG::isSupported()) { + if (GPG::isSupported()) { + $this->GPG = new GPG($this->homedir); + } else if ($throw) { throw new \Exception('GnuPG not supported'); } - $this->GPG = new \SnappyMail\PGP\GPG($this->homedir); } return $this->GPG; } @@ -214,8 +214,13 @@ class GnuPG /** * Exports a key */ - public function export(string $fingerprint) /*: string|false*/ + public function export(string $fingerprint, string $passphrase = '') /*: string|false*/ { + if ($passphrase) { + return $this->getGPG() + ->addPassphrase($fingerprint, $passphrase) + ->exportPrivateKey($fingerprint); + } return $this->GnuPG ? $this->GnuPG->export($fingerprint) : $this->GPG->export($fingerprint); @@ -258,13 +263,8 @@ class GnuPG */ public function generateKey(string $uid, string $passphrase) /*: string|false*/ { - if (!$this->GPG) { - if (!\SnappyMail\PGP\GPG::isSupported()) { - return false; - } - $this->GPG = new \SnappyMail\PGP\GPG($homedir); - } - return $this->GPG->generateKey($uid, $passphrase); + $GPG = $this->getGPG(false); + return $GPG ? $GPG->generateKey($uid, $passphrase) : false; } /** diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gpg.php b/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gpg.php index e35a9bcb0..968452001 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gpg.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gpg.php @@ -341,12 +341,37 @@ class GPG return false; } + protected function _exportKey($keyId, $private = false) + { + $keys = $this->keyInfo($keyId, $private ? 1 : 0); + if (!$keys) { + throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId); + } + if ($private && $this->passphrases) { + $_ENV['PINENTRY_USER_DATA'] = \json_encode($this->passphrases); + } + $result = $this->exec([ + $private ? '--export-secret-keys' : '--export', + '--armor', + \escapeshellarg($keys[0]['subkeys'][0]['fingerprint']), + ]); + return $result['output']; + } + /** - * Exports a key + * Exports a public key */ public function export(string $fingerprint) /*: string|false*/ { - return false; + return $this->_exportKey($fingerprint); + } + + /** + * Exports a private key + */ + public function exportPrivateKey(string $fingerprint) /*: string|false*/ + { + return $this->_exportKey($fingerprint, true); } /** @@ -388,7 +413,7 @@ class GPG public function addPassphrase($keyId, $passphrase) { - $this->passphrases[$key] = $passphrase; + $this->passphrases[$keyId] = $passphrase; return $this; } diff --git a/snappymail/v/0.0.0/app/templates/Views/User/SettingsOpenPGP.html b/snappymail/v/0.0.0/app/templates/Views/User/SettingsOpenPGP.html index 7e8a25b4a..1c5bf54d0 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/SettingsOpenPGP.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/SettingsOpenPGP.html @@ -24,7 +24,7 @@ Private keys - + 🔓 @@ -44,7 +44,7 @@ Public keys - + 🔒