diff --git a/plugins/enigma/README b/plugins/enigma/README index 1d8f7dd3b..1cd0e2def 100644 --- a/plugins/enigma/README +++ b/plugins/enigma/README @@ -17,7 +17,8 @@ Implemented features: + PGP: signatures verification + PGP: messages decryption + PGP: Sending of encrypted/signed messages -+ PGP: keys management UI (keys import and delete) ++ PGP: keys management UI (key import, delete) ++ PGP: key generation (client- or server-side) + Handling of PGP keys attached to incoming messages + User preferences to disable plugin features @@ -28,7 +29,6 @@ TODO (must have): TODO (later): ------------- - Handling of big messages with temp files -- Server-side keys generation (warning: no-entropy issue, max_execution_time issue) - Key info in contact details page (optional) - Extended key management: - disable, diff --git a/plugins/enigma/config.inc.php.dist b/plugins/enigma/config.inc.php.dist index 832f355b1..17e72deaa 100644 --- a/plugins/enigma/config.inc.php.dist +++ b/plugins/enigma/config.inc.php.dist @@ -28,3 +28,14 @@ $config['enigma_encrypt_all'] = false; // Default for how long to store private key passwords (in minutes). // When set to 0 passwords will be stored for the whole session. $config['enigma_password_time'] = 5; + +// Enables server-side keys generation which would be used +// if user browser does not support web-crypto features. +// +// WARNING: Key generation requires true random numbers, and as such can be +// slow. If the operating system runs out of entropy, key generation will +// block until more entropy is available. +// +// To solve that a hardware entropy generator or +// an entropy gathering daemon may be installed (e.g. randomsound). +$config['enigma_keygen_server'] = false; diff --git a/plugins/enigma/enigma.js b/plugins/enigma/enigma.js index a9b56eb61..8479c7944 100644 --- a/plugins/enigma/enigma.js +++ b/plugins/enigma/enigma.js @@ -97,7 +97,7 @@ rcube_webmail.prototype.enigma_key_create_save = function() openpgp.generateKeyPair(options).then(function(keypair) { // success - post = {_a: 'import', _keys: keypair.privateKeyArmored}; + var post = {_a: 'import', _keys: keypair.privateKeyArmored}; // send request to server rcmail.http_post('plugin.enigmakeys', post, lock); @@ -108,8 +108,13 @@ rcube_webmail.prototype.enigma_key_create_save = function() }); } // generate keys on the server + else if (rcmail.env.enigma_keygen_server) { + lock = this.set_busy(true, 'enigma.keygenerating'); + options = {_a: 'generate', _user: user, _password: password, _size: size}; + rcmail.http_post('plugin.enigmakeys', options, lock); + } else { - // @TODO + rcmail.display_message(rcmail.gettext('enigma.keygennosupport'), 'error'); } }; diff --git a/plugins/enigma/lib/enigma_driver.php b/plugins/enigma/lib/enigma_driver.php index 4c8340e08..6326ef79e 100644 --- a/plugins/enigma/lib/enigma_driver.php +++ b/plugins/enigma/lib/enigma_driver.php @@ -67,7 +67,7 @@ abstract class enigma_driver * * @return mixed Import status array or enigma_error */ - abstract function import($content, $isfile=false); + abstract function import($content, $isfile = false); /** * Keys listing. @@ -76,7 +76,7 @@ abstract class enigma_driver * * @return mixed Array of enigma_key objects or enigma_error */ - abstract function list_keys($pattern=''); + abstract function list_keys($pattern = ''); /** * Single key information. @@ -90,7 +90,7 @@ abstract class enigma_driver /** * Key pair generation. * - * @param array Key/User data + * @param array Key/User data (name, email, password, size) * * @return mixed Key (enigma_key) object or enigma_error */ diff --git a/plugins/enigma/lib/enigma_driver_gnupg.php b/plugins/enigma/lib/enigma_driver_gnupg.php index b9376e1b0..c7fc2dce2 100644 --- a/plugins/enigma/lib/enigma_driver_gnupg.php +++ b/plugins/enigma/lib/enigma_driver_gnupg.php @@ -188,8 +188,32 @@ class enigma_driver_gnupg extends enigma_driver return $list; } + /** + * Key pair generation. + * + * @param array Key/User data (user, email, password, size) + * + * @return mixed Key (enigma_key) object or enigma_error + */ public function gen_key($data) { + try { + $keygen = new Crypt_GPG_KeyGenerator(array( + 'homedir' => $this->homedir, + // 'binary' => '/usr/bin/gpg2', + // 'debug' => true, + )); + + $key = $keygen + ->setExpirationDate(0) + ->setPassphrase($data['password']) + ->generateKey($data['user'], $data['email']); + + return $this->parse_key($key); + } + catch (Exception $e) { + return $this->get_error_from_exception($e); + } } public function delete_key($keyid) @@ -263,12 +287,15 @@ class enigma_driver_gnupg extends enigma_driver $data['bad'] = $e->getBadPassphrases(); $data['missing'] = $e->getMissingPassphrases(); } - else if ($e instanceof Crypt_GPG_NoDataException) + else if ($e instanceof Crypt_GPG_NoDataException) { $error = enigma_error::E_NODATA; - else if ($e instanceof Crypt_GPG_DeletePrivateKeyException) + } + else if ($e instanceof Crypt_GPG_DeletePrivateKeyException) { $error = enigma_error::E_DELKEY; - else + } + else { $error = enigma_error::E_INTERNAL; + } $msg = $e->getMessage(); diff --git a/plugins/enigma/lib/enigma_engine.php b/plugins/enigma/lib/enigma_engine.php index 85c2882d3..58982ca0b 100644 --- a/plugins/enigma/lib/enigma_engine.php +++ b/plugins/enigma/lib/enigma_engine.php @@ -926,6 +926,29 @@ class enigma_engine return $result; } + /** + * PGP keys pair generation. + * + * @param array Key pair parameters + * + * @return mixed enigma_key or enigma_error + */ + function generate_key($data) + { + $this->load_pgp_driver(); + $result = $this->pgp_driver->gen_key($data); + + if ($result instanceof enigma_error) { + rcube::raise_error(array( + 'code' => 600, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Enigma plugin: " . $result->getMessage() + ), true, false); + } + + return $result; + } + /** * PGP keys/certs importing. * diff --git a/plugins/enigma/lib/enigma_ui.php b/plugins/enigma/lib/enigma_ui.php index d0c5e2975..8bb29d6df 100644 --- a/plugins/enigma/lib/enigma_ui.php +++ b/plugins/enigma/lib/enigma_ui.php @@ -59,6 +59,10 @@ class enigma_ui $this->key_import(); break; + case 'generate': + $this->key_generate(); + break; + case 'create': $this->key_create(); break; @@ -483,6 +487,45 @@ class enigma_ui return $out; } + /** + * Server-side key pair generation handler + */ + private function key_generate() + { + $user = rcube_utils::get_input_value('_user', rcube_utils::INPUT_POST, true); + $pass = rcube_utils::get_input_value('_password', rcube_utils::INPUT_POST, true); + $size = (int) rcube_utils::get_input_value('_size', rcube_utils::INPUT_POST); + + if ($size > 4096) { + $size = 4096; + } + + $ident = rcube_mime::decode_address_list($user, 1, false); + + if (empty($ident)) { + $this->rc->output->show_message('enigma.keygenerateerror', 'error'); + $this->rc->output->send(); + } + + $this->enigma->load_engine(); + $result = $this->enigma->engine->generate_key(array( + 'user' => $ident[1]['name'], + 'email' => $ident[1]['mailto'], + 'password' => $pass, + 'size' => $size, + )); + + if ($result instanceof enigma_key) { + $this->rc->output->command('enigma_key_create_success'); + $this->rc->output->show_message('enigma.keygeneratesuccess', 'confirmation'); + } + else { + $this->rc->output->show_message('enigma.keygenerateerror', 'error'); + } + + $this->rc->output->send(); + } + /** * Key generation page handler */ @@ -494,6 +537,8 @@ class enigma_ui 'keyform' => array($this, 'tpl_key_create_form'), )); + $this->rc->output->set_env('enigma_keygen_server', $this->rc->config->get('enigma_keygen_server')); + $this->rc->output->set_pagetitle($this->enigma->gettext('keygenerate')); $this->rc->output->send('enigma.keycreate'); } @@ -538,7 +583,8 @@ class enigma_ui $this->rc->output->add_gui_object('keyform', $attrib['id']); $this->rc->output->add_label('enigma.keygenerating', 'enigma.formerror', - 'enigma.passwordsdiffer', 'enigma.keygenerateerror', 'enigma.nonameident'); + 'enigma.passwordsdiffer', 'enigma.keygenerateerror', 'enigma.nonameident', + 'enigma.keygennosupport'); return $this->rc->output->form_tag(array(), $table->show($attrib)); } diff --git a/plugins/enigma/localization/en_US.inc b/plugins/enigma/localization/en_US.inc index 2cda1839b..f4f6d54b9 100644 --- a/plugins/enigma/localization/en_US.inc +++ b/plugins/enigma/localization/en_US.inc @@ -98,5 +98,6 @@ $messages['passwordsdiffer'] = 'Passwords do not match!'; $messages['nonameident'] = 'Idenity must have a user name defined!'; $messages['keygenerateerror'] = 'Failed to generate a key pair'; $messages['keygeneratesuccess'] = 'A key pair generated and imported successfully.'; +$messages['keygennosupport'] = 'Your web browser does not support cryptography. Unable to generate a key pair!'; ?>