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 2404f1ef3..7d3783e99 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 @@ -54,6 +54,23 @@ trait Pgp : $this->FalseResponse(__FUNCTION__); } + public function DoGnupgGenerateKey() : array + { + $fingerprint = false; + $GPG = $this->GnuPG(); + if ($GPG) { + $sName = $this->GetActionParam('Name', ''); + $sEmail = $this->GetActionParam('Email', ''); + $fingerprint = $GPG->generateKey( + $sName ? "{$sName} <{$sEmail}>" : $sEmail, + $this->GetActionParam('Passphrase', '') + ); + } + return $fingerprint + ? $this->DefaultResponse(__FUNCTION__, $fingerprint) + : $this->FalseResponse(__FUNCTION__); + } + public function DoGnupgImportKey() : array { $sKey = $this->GetActionParam('Key', ''); 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 3444aaccd..f9ac08df3 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 @@ -401,6 +401,20 @@ class GnuPG return false; } + /** + * Generates a key + */ + 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); + } + /** * Imports a key */ @@ -446,12 +460,6 @@ class GnuPG // Public foreach ($GPG->keyinfo($pattern) as $info) { if (!$info['disabled'] && !$info['expired'] && !$info['revoked']) { -/* - $hasPrivateKey = false; - foreach ($info['subkeys'] as $key) { - $hasPrivateKey |= \is_file("{$this->homedir}/private-keys-v1.d/{$key['keygrip']}.key"); - } -*/ foreach ($info['uids'] as $uid) { $id = $uid['email']; if (isset($keys[$id])) { @@ -466,9 +474,15 @@ class GnuPG 'can_encrypt' => $info['can_encrypt'], // Private Key tasks 'can_sign' => false, - 'can_decrypt' => false + 'can_decrypt' => false, + // The keys + 'publicKeys' => [], + 'privateKeys' => [] ]; } + foreach ($info['subkeys'] as $key) { + $keys[$id]['publicKeys'][$key['fingerprint']] = $key; + } } } } @@ -489,9 +503,15 @@ class GnuPG 'can_encrypt' => false, // Private Key tasks 'can_sign' => $info['can_sign'], - 'can_decrypt' => $info['can_encrypt'] + 'can_decrypt' => $info['can_encrypt'], + // The keys + 'publicKeys' => [], + 'privateKeys' => [] ]; } + foreach ($info['subkeys'] as $key) { + $keys[$id]['privateKeys'][$key['fingerprint']] = $key; + } } } } 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 a360d316a..5b6c993ea 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 @@ -53,10 +53,10 @@ class GPG private $binary, - $version = '1.0', + $version = '2.0', $cipher_algorithms = ['IDEA', '3DES', 'CAST5', 'BLOWFISH', 'AES', 'AES192', 'AES256', 'TWOFISH', 'CAMELLIA128', 'CAMELLIA192', 'CAMELLIA256'], $hash_algorithms = ['SHA1', 'RIPEMD160', 'SHA256', 'SHA384', 'SHA512', 'SHA224'], - $pubkey_algorithms = ['RSA', 'DSA'], + $pubkey_algorithms = ['RSA', 'ELG', 'DSA', 'ECDH', 'ECDSA', 'EDDSA'], $compression = ['Uncompressed', 'ZIP', 'ZLIB'], $proc_resource, @@ -303,6 +303,85 @@ class GPG return $this; } + /** + * Generates a key + * Also saves revocation certificate in {homedir}/openpgp-revocs.d/ + */ + public function generateKey(string $uid, string $passphrase) /*: string|false*/ + { + /** + * https://www.gnupg.org/documentation/manuals/gnupg/Unattended-GPG-key-generation.html + * But it can't generate multiple subkeys + */ +/* + $this->_input = "Key-Type: ECDSA +Key-Curve: nistp256 +Key-Usage: sign +Subkey-Type: ecdh +Subkey-Curve: Curve25519 +Subkey-Usage: sign +Name-Real: John +Name-Comment: comment +Name-Email: john.doe@example.com +Expire-Date: 0 +Passphrase: {$passphrase} +%commit"; + +// gpg --full-generate-key +// gpg --quick-gen-key + $result = $this->exec(array( + '--batch', + '--yes', + '--full-gen-key' // '--gen-key' + )); +*/ + $arguments = array( + '--batch', + '--yes', + '--passphrase', \escapeshellarg($passphrase) + ); + + $result = $this->exec(\array_merge($arguments, array( + '--quick-generate-key', + \escapeshellarg(\rawurlencode($uid)), + 'ed25519', + 'cert', + '0' + ))); + $fingerprint = ''; + foreach ($result['status'] as $line) { + $tokens = \explode(' ', $line); + if ('KEY_CREATED' === $tokens[0]/* && 'P' === $tokens[1]*/) { + $fingerprint = $tokens[2]; + } + } + if (!$fingerprint) { + return false; + } + + $arguments[] = '--quick-add-key'; + $arguments[] = $fingerprint; + $this->exec(\array_merge($arguments, array( + 'ed25519', + 'sign', + '0' + ))); + $this->exec(\array_merge($arguments, array( + 'cv25519', + 'encrypt', + '0' + ))); +/* + [status][0] => KEY_NOT_CREATED + [errors][0] => gpg: -:3: specified Key-Usage not allowed for algo 22 + [errors][0] => gpg: key generation failed: Unknown elliptic curve + + [status][0] => KEY_CONSIDERED B2FD2BCADCC6A9E4B2C90DBBE776CADFF94D327F 0 + [status][1] => KEY_CREATED P B2FD2BCADCC6A9E4B2C90DBBE776CADFF94D327F +*/ + return $fingerprint; + } + protected function _importKey($input) /*: array|false*/ { $arguments = array('--import');