From e819d251dacdc75d1338ec99502b8a6ec9485a2f Mon Sep 17 00:00:00 2001 From: Henrique Borba Date: Fri, 4 Nov 2022 22:08:51 -0300 Subject: [PATCH 1/2] Autocrypt working with DeltaChat Pending automation for public key import --- modules/imap/handler_modules.php | 38 ++++++++ modules/imap/output_modules.php | 17 ++++ modules/imap/site.css | 6 +- modules/pgp/modules.php | 145 ++++++++++++++++++++++++++++++- modules/pgp/setup.php | 27 +++++- modules/pgp/site.js | 64 +++++++++----- modules/smtp/hm-mime-message.php | 28 ++++-- modules/smtp/modules.php | 22 +++++ modules/smtp/setup.php | 6 +- modules/smtp/site.css | 3 +- 10 files changed, 323 insertions(+), 33 deletions(-) diff --git a/modules/imap/handler_modules.php b/modules/imap/handler_modules.php index ef91626fca..a898e45c44 100644 --- a/modules/imap/handler_modules.php +++ b/modules/imap/handler_modules.php @@ -1790,7 +1790,45 @@ public function process() { if ($part == 0 || (isset($msg_struct_current['type']) && strtolower($msg_struct_current['type'] == 'text'))) { $save_reply_text = true; } + $msg_headers = $imap->get_message_headers($form['imap_msg_uid']); + + if (strstr($msg_headers['Content-Type'], 'pgp-encrypted') && array_key_exists('Autocrypt', $msg_headers) + && ($this->request->post['imap_msg_part'] == "" || $this->request->post['imap_msg_part'] =="false")) { + //Autocrypt + foreach ($imap->get_message_structure($form['imap_msg_uid'])[0]['subs'] as $key => $sub) { + if ($sub['subtype'] == 'octet-stream') { + $encrypted_message = $imap->get_message_content($form['imap_msg_uid'], explode('.', $key)[1]); + } + } + + //Autocrypt Decrypt + $tmp_dir = ini_get('keyring_dir') ? ini_get('keyring_dir') : '/keyring'; + putenv(sprintf('GNUPGHOME=%s/.gnupg', $tmp_dir)); + $gpg = gnupg_init(); + gnupg_setarmor($gpg, 1); + gnupg_cleardecryptkeys($gpg); + $current_user_key = null; + $saved_keys = gnupg_keyinfo($gpg, ''); + foreach ($this->user_config->get('autocrypt_keys', array()) as $key) { + if (strstr($msg_headers['Delivered-To'], $key['email'])) { + $current_user_key = $key; + } + } + try { + gnupg_seterrormode($gpg, gnupg::ERROR_EXCEPTION); + gnupg_adddecryptkey($gpg, $current_user_key['key_fingerprint'], ''); + $decrypted_message = gnupg_decrypt($gpg, $encrypted_message); + } catch (Exception $e) { + + } + + $msg_text = $decrypted_message; + preg_match('/^Subject: .*$/m', $decrypted_message, $matches); + $subject = str_replace('Subject:', '', $matches[0]); + $msg_headers['Subject'] = mb_decode_mimeheader($subject); + } + $this->out('list_headers', get_list_headers($msg_headers)); $this->out('msg_headers', $msg_headers); $this->out('imap_prefecth', $prefetch); diff --git a/modules/imap/output_modules.php b/modules/imap/output_modules.php index 75a48eb63a..e94ba75d35 100644 --- a/modules/imap/output_modules.php +++ b/modules/imap/output_modules.php @@ -301,6 +301,23 @@ protected function output() { $txt .= ''; $txt .= ''; $txt .= ''; + $txt .= ''; + + + $headers = $this->get('msg_headers', array()); + if (array_key_exists('Autocrypt', $headers)) { + $exploded_autocrypt_header = explode('keydata=', $headers['Autocrypt']); + $exploded_autocrypt_email_header = explode('addr=', $headers['Autocrypt']); + if (count($exploded_autocrypt_header) >= 1) { + $pgp_key = end($exploded_autocrypt_header); + $exploded_email_header = explode(';', end($exploded_autocrypt_email_header)); + $key_email = reset($exploded_email_header); + $txt .= '
The sender sent their public key in the message header '; + $txt .= '
'; + $txt .= '
'; + } + } + $txt .= ''; $this->out('msg_headers', $txt, false); diff --git a/modules/imap/site.css b/modules/imap/site.css index 8f01cb5cc1..e04bd9c247 100644 --- a/modules/imap/site.css +++ b/modules/imap/site.css @@ -87,4 +87,8 @@ #archive_val { padding-left: 20px; } .attached_image { margin-right: 20px; margin-bottom: 20px; height: 200px; } -.attached_image_box { display: flex; flex-wrap: wrap; border-top: solid 1px #ddd; padding-top: 20px; padding-left: 20px; width: 100%; padding-bottom: 40px; } \ No newline at end of file +.attached_image_box { display: flex; flex-wrap: wrap; border-top: solid 1px #ddd; padding-top: 20px; padding-left: 20px; width: 100%; padding-bottom: 40px; } + +.autocrypt_key_header_import_warning { font-size: 12px; color: black; } +.autocrypt_key_header_import_warning > td { padding-left: 35px;} +.autocrypt_key_header_import_warning > td > span { background-color: #aaa; } \ No newline at end of file diff --git a/modules/pgp/modules.php b/modules/pgp/modules.php index f091b58cd9..b21342d682 100644 --- a/modules/pgp/modules.php +++ b/modules/pgp/modules.php @@ -8,6 +8,7 @@ if (!defined('DEBUG_MODE')) { die(); } +require_once APP_PATH.'modules/profiles/hm-profiles.php'; /** * @subpackage pgp/handler @@ -15,6 +16,80 @@ class Hm_Handler_load_pgp_data extends Hm_Handler_Module { public function process() { $this->out('pgp_public_keys', $this->user_config->get('pgp_public_keys', array())); + $this->out('autocrypt_keys', $this->user_config->get('autocrypt_keys', array())); + } +} + +/** + * @subpackage pgp/handler + */ +class Hm_Handler_ajax_encrypt_by_fingerprint extends Hm_Handler_Module +{ + public function process() + { + list($success, $form) = $this->process_form(array('body', 'fingerprint', 'from')); + $tmp_dir = ini_get('keyring_dir') ? ini_get('keyring_dir') : '/keyring'; + putenv(sprintf('GNUPGHOME=%s/.gnupg', $tmp_dir)); + $gpg = gnupg_init(); + gnupg_setarmor($gpg,1); + gnupg_addencryptkey($gpg ,$form['fingerprint']); + + $from_exploded = explode('.', $form['from']); + $from = reset($from_exploded); + $info = gnupg_keyinfo($gpg, ''); + foreach ($this->user_config->get('autocrypt_keys', array()) as $key) { + if ($key['email'] == $this->user_config->get('smtp_servers')[$from]['user']) { + gnupg_addsignkey($gpg, $key['key_fingerprint']); + gnupg_addencryptkey($gpg ,$key['key_fingerprint']); + $encrypted_msg = gnupg_sign($gpg, $form['body']); + $encrypted_msg = gnupg_encrypt($gpg, $encrypted_msg); + } + } + $this->out('encrypted_message', $encrypted_msg); + } +} + +/** + * @subpackage pgp/handler + */ +class Hm_Handler_ajax_public_key_import_string extends Hm_Handler_Module { + public function process() { + list($success, $form) = $this->process_form(array('public_key', 'public_key_email', 'autocrypt')); + + $gpg = new gnupg(); + $data = base64_decode($form['public_key']); + + $tmp_dir = ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : '/keyring'; + putenv(sprintf('GNUPGHOME=%s/.gnupg', $tmp_dir)); + + $info = $gpg->import($data); + + if (is_array($info) && array_key_exists('fingerprint', $info)) { + $fingerprint = $info['fingerprint']; + } + + if (!$info) { + Hm_Msgs::add('ERRUnable to import public key'); + return; + } + + $keys = $this->user_config->get('pgp_public_keys', array()); + + $key_exists = false; + $new_key = array('fingerprint' => $fingerprint, 'key' => $data, 'autocrypt' => $form['autocrypt'], 'email' => $form['public_key_email']); + foreach ($keys as $key => $values) { + if ($values['email'] == $form['public_key_email']) { + $key_exists = true; + $keys[$key] = $new_key; + } + } + + if ($key_exists == false) { + $keys[] = $new_key; + } + $this->session->set('pgp_public_keys', $keys, true); + $this->session->record_unsaved('Public key imported'); + Hm_Msgs::add('Public key imported'); } } @@ -39,6 +114,51 @@ public function process() { } } +class Hm_Handler_ajax_generate_autocrypt_keys extends Hm_Handler_Module { + public function process() { + $tmp_dir = ini_get('keyring_dir') ? ini_get('keyring_dir') : '/keyring'; + putenv(sprintf('GNUPGHOME=%s/.gnupg', $tmp_dir)); + $profiles = new Hm_Profiles($this); + $autocrypt_keys = []; + $gpg = gnupg_init(); + $saved_keys = gnupg_keyinfo($gpg, ''); + foreach ($profiles->list_all() as $profile) { + $key_exists = false; + foreach ($saved_keys as $saved_key) { + if($saved_key['uids'][0]['email'] == $profile['user']) { + $key_exists = $saved_key['subkeys'][0]['fingerprint']; + } + } + + if ($key_exists) { + $autocrypt_keys[] = [ + 'email' => $profile['address'], + 'key_fingerprint' => $key_exists + ]; + continue; + } + + $contents = "%no-protection\nKey-Type: 1\nKey-Length: 3072\nSubkey-Type: 1\nSubkey-Length: 2048\nName-Real: Autocrypt\nName-Email: ". $profile['address'] ."\nExpire-Date: 0\n"; + file_put_contents('.temp_key_info', $contents); + system("gpg --batch --gen-key .temp_key_info"); + $saved_keys = gnupg_keyinfo($gpg, ''); + + foreach ($saved_keys as $saved_key) { + if($saved_key['uids'][0]['email'] == $profile['user']) { + $autocrypt_keys[] = [ + 'email' => $profile['address'], + 'key_fingerprint' => $saved_key['subkeys'][0]['fingerprint'] + ]; + } + } + } + + $this->session->set('autocrypt_keys', $autocrypt_keys, true); + $this->session->record_unsaved('Autocrypt keys generated'); + Hm_Msgs::add('Keys Generated'); + } +} + /** * @subpackage pgp/handler */ @@ -117,7 +237,7 @@ protected function output() { $res .= ''; } @@ -185,6 +305,29 @@ protected function output() { } } +/** + * @subpackage pgp/output + */ +class Hm_Output_pgp_settings_autocrypt_private_keys extends Hm_Output_Module +{ + protected function output() + { + $res = '
' . $this->trans('Autocrypt Private Keys'); + $res .= '' . sprintf($this->trans('%s created'), 0) . '
'; + $res .= '
'; + $res .= $this->trans('Private keys never leave your browser, and are deleted when you logout'); + $res .= '

'; + $res .= '
' . $this->trans('Existing Keys') . ''; + $res .= ''; + foreach($this->get('autocrypt_keys') as $key) { + $res .= ''; + } + $res .= '
' . $this->trans('Identity') . '
'.$key['email'].'
'; + $res .= '
'; + return $res; + } +} + /** * @subpackage pgp/output */ diff --git a/modules/pgp/setup.php b/modules/pgp/setup.php index 79abb09ac1..e015652f31 100644 --- a/modules/pgp/setup.php +++ b/modules/pgp/setup.php @@ -12,6 +12,7 @@ add_output('pgp', 'pgp_settings_start', true, 'pgp', 'content_section_start', 'after'); add_output('pgp', 'pgp_settings_public_keys', true, 'pgp', 'pgp_settings_start', 'after'); add_output('pgp', 'pgp_settings_private_key', true, 'pgp', 'pgp_settings_public_keys', 'after'); +add_output('pgp', 'pgp_settings_autocrypt_private_keys', true, 'pgp', 'pgp_settings_private_key', 'after'); add_output('pgp', 'pgp_settings_end', true, 'pgp', 'pgp_settings_private_key', 'after'); add_output('message', 'pgp_msg_controls', true, 'pgp', 'message_start', 'before'); @@ -21,16 +22,38 @@ add_handler('compose', 'pgp_compose_data', true, 'pgp', 'load_user_data', 'after'); add_output('compose', 'pgp_compose_controls', true, 'pgp', 'compose_form_end', 'after'); +add_handler('ajax_public_key_import_string', 'load_imap_servers_from_config', true, 'imap', 'load_user_data', 'after'); +add_handler('ajax_public_key_import_string', 'load_smtp_servers_from_config', true, 'imap', 'load_user_data', 'after'); +add_handler('ajax_public_key_import_string', 'login', false, 'core'); +add_handler('ajax_public_key_import_string', 'load_user_data', true, 'core'); +add_handler('ajax_public_key_import_string', 'ajax_public_key_import_string', true); + +add_handler('ajax_generate_autocrypt_keys', 'load_imap_servers_from_config', true, 'imap', 'load_user_data', 'after'); +add_handler('ajax_generate_autocrypt_keys', 'load_smtp_servers_from_config', true, 'imap', 'load_user_data', 'after'); +add_handler('ajax_generate_autocrypt_keys', 'login', false, 'core'); +add_handler('ajax_generate_autocrypt_keys', 'load_user_data', true, 'core'); +add_handler('ajax_generate_autocrypt_keys', 'ajax_generate_autocrypt_keys', true); + +add_handler('ajax_encrypt_by_fingerprint', 'load_imap_servers_from_config', true, 'imap', 'load_user_data', 'after'); +add_handler('ajax_encrypt_by_fingerprint', 'load_smtp_servers_from_config', true, 'imap', 'load_user_data', 'after'); +add_handler('ajax_encrypt_by_fingerprint', 'login', false, 'core'); +add_handler('ajax_encrypt_by_fingerprint', 'load_user_data', true, 'core'); +add_handler('ajax_encrypt_by_fingerprint', 'ajax_encrypt_by_fingerprint', true); + return array( - 'allowed_pages' => array('pgp'), + 'allowed_pages' => array('pgp', 'ajax_public_key_import_string', 'ajax_generate_autocrypt_keys', 'ajax_encrypt_by_fingerprint'), 'allowed_output' => array( 'pgp_msg_part' => array(FILTER_VALIDATE_BOOLEAN, false), + 'encrypted_message' => array(FILTER_SANITIZE_STRING, false), ), 'allowed_get' => array(), 'allowed_post' => array( 'public_key' => FILTER_SANITIZE_FULL_SPECIAL_CHARS, 'public_key_email' => FILTER_SANITIZE_FULL_SPECIAL_CHARS, - 'delete_public_key_id' => FILTER_VALIDATE_INT + 'delete_public_key_id' => FILTER_VALIDATE_INT, + 'body' => FILTER_UNSAFE_RAW, + 'from' => FILTER_UNSAFE_RAW, + 'fingerprint' => FILTER_UNSAFE_RAW ) ); diff --git a/modules/pgp/site.js b/modules/pgp/site.js index 031e571c76..03bc5e3e8e 100644 --- a/modules/pgp/site.js +++ b/modules/pgp/site.js @@ -75,9 +75,11 @@ var Hm_Pgp = { } options['privateKeys'] = key; } + return openpgp.encrypt(options).then(function(ciphertext) { var encrypted = ciphertext.data; $('#compose_body').val(encrypted); + $('#autocrypt_pgp_key').val(keytext); Hm_Pgp.show_result(); return true; }); @@ -98,9 +100,11 @@ var Hm_Pgp = { var decrypted = await key.decrypt(pass); } catch (e) { - Hm_Pgp.error_msg = 'Could not unlock key with supplied passphrase'; - Hm_Pgp.show_result(); - return; + if (e.toString() != 'Error: Key packet is already decrypted.') { + Hm_Pgp.error_msg = 'Could not unlock key with supplied passphrase'; + Hm_Pgp.show_result(); + return; + } } return openpgp.decrypt({message: msg, privateKeys: [key]}).then(function(plaintext) { var plain = plaintext.data; @@ -172,23 +176,18 @@ var Hm_Pgp = { }, process_settings: async function() { - var sign = $('#pgp_sign').val(); - if (!sign) { - sign = false; - } - var encrypt = $('#pgp_encrypt').val(); - if (!encrypt) { - encrypt = false; - } - if (encrypt && sign) { - await Hm_Pgp.get_passphrase(Hm_Pgp.encrypt_text, encrypt, sign); - } - else if (encrypt) { - await Hm_Pgp.encrypt_text(false, encrypt); - } - else if (sign) { - await Hm_Pgp.get_passphrase(Hm_Pgp.sign_text, sign); - } + Hm_Ajax.request( + [{'name': 'hm_ajax_hook', 'value': 'ajax_encrypt_by_fingerprint'}, + {'name': 'body', 'value': $("#compose_body").val()}, + {'name': 'fingerprint', 'value': $("#pgp_encrypt").val()}, + {'name': 'from', 'value': $(".compose_server").val()}, + {'name': 'hm_page_key', 'value': $('#hm_page_key').val()}], + function(res) { + $("#compose_body").val(res.encrypted_message); + }, + [], + true + ); }, show_result: function() { @@ -260,6 +259,20 @@ var Hm_Pgp = { check_pgp_msg: async function(res) { var keylist = await Hm_Pgp.private_key_options(); + $("#import_public_key_form").on('submit', function (event) { + event.preventDefault(); + Hm_Ajax.request( + [{'name': 'public_key', 'value': $('#public_key_header_field').val()}, + {'name': 'hm_ajax_hook', 'value': 'ajax_public_key_import_string'}, + {'name': 'hm_page_key', 'value': $('#hm_page_key').val()}, + {'name': 'public_key_email', 'value': $('#key_import_email').val()}], + function(res) { + }, + [], + true + ); + }); + if (keylist && res.pgp_msg_part) { $('.pgp_private_keys').html(keylist); $('.pgp_msg_controls').show(); @@ -324,10 +337,21 @@ $(function() { } else if (hm_page_name() == 'pgp') { $('.priv_title').on("click", function() { $('.priv_keys').toggle(); }); + $('.priv_ac_title').on("click", function() { $('.priv_ac_keys').toggle(); }); $('.public_title').on("click", function() { $('.public_keys').toggle(); }); $('.delete_pgp_key').on("click", function() { return hm_delete_prompt(); }); $('#priv_key').on("change", function(evt) { Hm_Pgp.read_private_key(evt); }); Hm_Pgp.list_private_keys(); + $(document).on('click', '.generate_ac_keys_button', function() { + Hm_Ajax.request( + [{'name': 'hm_ajax_hook', 'value': 'ajax_generate_autocrypt_keys'}, + {'name': 'hm_page_key', 'value': $('#hm_page_key').val()}], + function(res) { + }, + [], + true + ); + }); if (window.location.hash == '#public_keys') { $('.public_keys').toggle(); } diff --git a/modules/smtp/hm-mime-message.php b/modules/smtp/hm-mime-message.php index 0c2c8dca8a..ebecdc084f 100644 --- a/modules/smtp/hm-mime-message.php +++ b/modules/smtp/hm-mime-message.php @@ -50,6 +50,12 @@ function __construct($to, $subject, $body, $from, $html=false, $cc='', $bcc='', $this->body = $body; } + function set_autocrypt_header($public_key = NULL) { + $pattern = '/[a-z0-9_\-\+\.]+@[a-z0-9\-]+\.([a-z]{2,4})(?:\.[a-z]{2})?/i'; + preg_match_all($pattern, $this->headers['From'], $matches); + $this->headers['Autocrypt'] = 'addr='.str_replace(['<', '>'], '', $matches[0][0]).'; prefer-encrypt=mutual; keydata='.$public_key; + } + /* return headers array */ function get_headers() { return $this->headers; @@ -88,8 +94,8 @@ function process_attachments() { } /* output mime message */ - function get_mime_msg() { - $this->prep_message_body(); + function get_mime_msg($encrypt=false) { + $this->prep_message_body($encrypt); $res = ''; $headers = ''; foreach ($this->headers as $name => $val) { @@ -224,15 +230,25 @@ function format_message_text($body) { return $this->qp_encode(implode('', $new_lines)); } - function prep_message_body() { + function prep_message_body($encrypt) { + $encrypt = true; $body = $this->body; if (!$this->html) { $body = mb_convert_encoding(trim($body), "HTML-ENTITIES", "UTF-8"); $body = mb_convert_encoding($body, "UTF-8", "HTML-ENTITIES"); if (!empty($this->attachments)) { - $this->headers['Content-Type'] = 'multipart/mixed; boundary='.$this->boundary; - $body = sprintf("--%s\r\nContent-Type: text/plain; charset=UTF-8; format=flowed\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\n%s", - $this->boundary, $this->format_message_text($body)); + if (array_key_exists('Autocrypt', $this->headers) && $encrypt) { + $this->headers['Content-Type'] = 'multipart/encrypted; protocol="application/pgp-encrypted"; boundary=' . $this->boundary; + $pgp_header = 'Content-Type: application/octet-stream; name="encrypted.asc"'."\r\n"; + $pgp_header .= 'Content-Description: OpenPGP encrypted message"'."\r\n"; + $pgp_header .= 'Content-Disposition: inline; filename="encrypted.asc";'."\r\n"; + $body = sprintf("--%s\r\nContent-Type: application/pgp-encrypted;\r\n".'Content-Description: PGP/MIME version identification'."\r\n\r\n".'Version: 1'."\r\n\r\n--".$this->boundary."\r\n".$pgp_header."\r\n"."%s"."\r\n\r\n"."--".$this->boundary.'--', + $this->boundary, $this->format_message_text($body)); + } else { + $this->headers['Content-Type'] = 'multipart/mixed; boundary=' . $this->boundary; + $body = sprintf("--%s\r\nContent-Type: text/plain; charset=UTF-8; format=flowed\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\n%s", + $this->boundary, $this->format_message_text($body)); + } } else { $this->headers['Content-Type'] = 'text/plain; charset=UTF-8; format=flowed'; diff --git a/modules/smtp/modules.php b/modules/smtp/modules.php index 8fd61c8aa2..677d3f0b32 100644 --- a/modules/smtp/modules.php +++ b/modules/smtp/modules.php @@ -342,6 +342,7 @@ public function process() { $this->out('compose_draft', $draft, false); $this->out('compose_draft_id', $draft_id); + $this->out('pgp_enabled', $this->module_is_supported('pgp')); if ($draft_id == 0 && array_key_exists('uid', $this->request->get)) { $draft_id = $this->request->get['uid']; @@ -717,6 +718,20 @@ public function process() { return; } + # Autocrypt + $autocrypt_keys = $this->user_config->get('autocrypt_keys'); + $tmp_dir = ini_get('keyring_dir') ? ini_get('keyring_dir') : '/keyring'; + putenv(sprintf('GNUPGHOME=%s/.gnupg', $tmp_dir)); + $res = gnupg_init(); + gnupg_setarmor($res, 0); + foreach ($autocrypt_keys as $autocrypt_key) { + if (strstr($from, $autocrypt_key['email'])) { + $mime->set_autocrypt_header(base64_encode(gnupg_export($res,$autocrypt_key['key_fingerprint']))); + } + } + #print_r($mime->get_mime_msg()); + #die; + /* send the message */ $err_msg = $smtp->send_message($from, $recipients, $mime->get_mime_msg()); if ($err_msg) { @@ -968,6 +983,7 @@ protected function output() { $msg_path = $this->get('list_path', ''); $msg_uid = $this->get('uid', ''); $from = $this->get('compose_from'); + $pgp_enabled = $this->get('pgp_enabled', 0); if (!$msg_path) { $msg_path = $this->get('compose_msg_path', ''); @@ -1113,6 +1129,12 @@ protected function output() { smtp_server_dropdown($this->module_output(), $this, $recip, $selected_id). ''; + // Autocrypt Header Key + if ($pgp_enabled) { + $res .= ''; + $res .= ''; + } + if ($this->get('list_path') && $reply_type == 'reply') { $res .= ''; } diff --git a/modules/smtp/setup.php b/modules/smtp/setup.php index 6bd9d73a68..8ab4d236a1 100644 --- a/modules/smtp/setup.php +++ b/modules/smtp/setup.php @@ -170,9 +170,11 @@ 'draft_in_reply_to' => FILTER_UNSAFE_RAW, 'draft_notice' => FILTER_VALIDATE_BOOLEAN, 'smtp_auto_bcc' => FILTER_VALIDATE_INT, - 'profile_value' => FILTER_SANITIZE_FULL_SPECIAL_CHARS, 'uploaded_files' => FILTER_SANITIZE_FULL_SPECIAL_CHARS, - 'send_uploaded_files' => FILTER_SANITIZE_FULL_SPECIAL_CHARS + 'send_uploaded_files' => FILTER_SANITIZE_FULL_SPECIAL_CHARS, + 'profile_value' => FILTER_SANITIZE_STRING, + 'autocrypt_send_public_key' => FILTER_SANITIZE_STRING, + 'autocrypt_pgp_key' => FILTER_SANITIZE_STRING ) ); diff --git a/modules/smtp/site.css b/modules/smtp/site.css index d7424254d0..519e032b8c 100644 --- a/modules/smtp/site.css +++ b/modules/smtp/site.css @@ -28,4 +28,5 @@ .meter { height: 5px; position: relative; background: #ede8e6; overflow: hidden;} .meter span {display: block; height: 100%; } .progress {background-color: #4323f5; animation: progressBar 1s ease-in-out; animation-fill-mode:both; } -@keyframes progressBar { 0% { width: 0; } 100% { width: 100%; }} \ No newline at end of file +@keyframes progressBar { 0% { width: 0; } 100% { width: 100%; }} +.send_public_key_check { margin-left: 15px;} \ No newline at end of file From d028e1ba60146ffa1f04d4b65478dcceab358b6d Mon Sep 17 00:00:00 2001 From: Henrique Borba Date: Fri, 11 Nov 2022 15:18:50 -0300 Subject: [PATCH 2/2] Automatic import and encryption for autocrypt level 1 --- modules/imap/handler_modules.php | 88 +++++++++++++++++++++----------- modules/imap/output_modules.php | 15 ------ modules/pgp/modules.php | 1 - modules/pgp/site.js | 1 + modules/smtp/modules.php | 63 +++++++++++++++-------- 5 files changed, 101 insertions(+), 67 deletions(-) diff --git a/modules/imap/handler_modules.php b/modules/imap/handler_modules.php index a898e45c44..af718bcfb4 100644 --- a/modules/imap/handler_modules.php +++ b/modules/imap/handler_modules.php @@ -1793,40 +1793,70 @@ public function process() { $msg_headers = $imap->get_message_headers($form['imap_msg_uid']); - if (strstr($msg_headers['Content-Type'], 'pgp-encrypted') && array_key_exists('Autocrypt', $msg_headers) - && ($this->request->post['imap_msg_part'] == "" || $this->request->post['imap_msg_part'] =="false")) { - //Autocrypt - foreach ($imap->get_message_structure($form['imap_msg_uid'])[0]['subs'] as $key => $sub) { - if ($sub['subtype'] == 'octet-stream') { - $encrypted_message = $imap->get_message_content($form['imap_msg_uid'], explode('.', $key)[1]); + // AUTOCRYPT + if ($this->module_is_supported('pgp')) { + if (strstr($msg_headers['Content-Type'], 'pgp-encrypted') && array_key_exists('Autocrypt', $msg_headers) + && ($this->request->post['imap_msg_part'] == "" || $this->request->post['imap_msg_part'] == "false")) { + $tmp_dir = ini_get('keyring_dir') ? ini_get('keyring_dir') : '/keyring'; + putenv(sprintf('GNUPGHOME=%s/.gnupg', $tmp_dir)); + $gpg = gnupg_init(); + + //Import autocrypt key automatically + $exploded_autocrypt_header = explode('keydata=', $msg_headers['Autocrypt']); + $exploded_autocrypt_email_header = explode('addr=', $msg_headers['Autocrypt']); + $exploded_email_header = explode(';', end($exploded_autocrypt_email_header)); + if (count($exploded_autocrypt_header) >= 1) { + $pgp_key = end($exploded_autocrypt_header); + $key_email = reset($exploded_email_header); + $keys = $this->user_config->get('pgp_public_keys', array()); + $found = false; + foreach ($keys as $id => $key) { + if ($key['email'] == $key_email & !$found) { + $data = base64_decode($pgp_key); + $info = gnupg_import($gpg, $data); + $keys[$id] = array('fingerprint' => $info['fingerprint'], 'key' => $data, 'email' => $key_email); + $found = true; + } + } + if (!$found) { + $data = base64_decode($pgp_key); + $info = gnupg_import($gpg, $data); + $keys[] = array('fingerprint' => $info['fingerprint'], 'key' => $data, 'email' => $key_email); + } + $this->session->record_unsaved('Public key imported'); + $this->session->set('pgp_public_keys', $keys, true); } - } - //Autocrypt Decrypt - $tmp_dir = ini_get('keyring_dir') ? ini_get('keyring_dir') : '/keyring'; - putenv(sprintf('GNUPGHOME=%s/.gnupg', $tmp_dir)); - $gpg = gnupg_init(); - gnupg_setarmor($gpg, 1); - gnupg_cleardecryptkeys($gpg); - $current_user_key = null; - $saved_keys = gnupg_keyinfo($gpg, ''); - foreach ($this->user_config->get('autocrypt_keys', array()) as $key) { - if (strstr($msg_headers['Delivered-To'], $key['email'])) { - $current_user_key = $key; + //Autocrypt + foreach ($imap->get_message_structure($form['imap_msg_uid'])[0]['subs'] as $key => $sub) { + if ($sub['subtype'] == 'octet-stream') { + $encrypted_message = $imap->get_message_content($form['imap_msg_uid'], explode('.', $key)[1]); + } } - } - try { - gnupg_seterrormode($gpg, gnupg::ERROR_EXCEPTION); - gnupg_adddecryptkey($gpg, $current_user_key['key_fingerprint'], ''); - $decrypted_message = gnupg_decrypt($gpg, $encrypted_message); - } catch (Exception $e) { - } + //Autocrypt Decrypt + gnupg_setarmor($gpg, 1); + gnupg_cleardecryptkeys($gpg); + $current_user_key = null; + $saved_keys = gnupg_keyinfo($gpg, ''); + foreach ($this->user_config->get('autocrypt_keys', array()) as $key) { + if (strstr($msg_headers['Delivered-To'], $key['email'])) { + $current_user_key = $key; + } + } + try { + gnupg_seterrormode($gpg, gnupg::ERROR_EXCEPTION); + gnupg_adddecryptkey($gpg, $current_user_key['key_fingerprint'], ''); + $decrypted_message = gnupg_decrypt($gpg, $encrypted_message); + } catch (Exception $e) { - $msg_text = $decrypted_message; - preg_match('/^Subject: .*$/m', $decrypted_message, $matches); - $subject = str_replace('Subject:', '', $matches[0]); - $msg_headers['Subject'] = mb_decode_mimeheader($subject); + } + + $msg_text = $decrypted_message; + preg_match('/^Subject: .*$/m', $decrypted_message, $matches); + $subject = str_replace('Subject:', '', $matches[0]); + $msg_headers['Subject'] = mb_decode_mimeheader($subject); + } } $this->out('list_headers', get_list_headers($msg_headers)); diff --git a/modules/imap/output_modules.php b/modules/imap/output_modules.php index e94ba75d35..3d74981ba5 100644 --- a/modules/imap/output_modules.php +++ b/modules/imap/output_modules.php @@ -303,21 +303,6 @@ protected function output() { $txt .= ''; $txt .= ''; - - $headers = $this->get('msg_headers', array()); - if (array_key_exists('Autocrypt', $headers)) { - $exploded_autocrypt_header = explode('keydata=', $headers['Autocrypt']); - $exploded_autocrypt_email_header = explode('addr=', $headers['Autocrypt']); - if (count($exploded_autocrypt_header) >= 1) { - $pgp_key = end($exploded_autocrypt_header); - $exploded_email_header = explode(';', end($exploded_autocrypt_email_header)); - $key_email = reset($exploded_email_header); - $txt .= '
The sender sent their public key in the message header '; - $txt .= '
'; - $txt .= '
'; - } - } - $txt .= ''; $this->out('msg_headers', $txt, false); diff --git a/modules/pgp/modules.php b/modules/pgp/modules.php index b21342d682..c8560b2dd4 100644 --- a/modules/pgp/modules.php +++ b/modules/pgp/modules.php @@ -33,7 +33,6 @@ public function process() $gpg = gnupg_init(); gnupg_setarmor($gpg,1); gnupg_addencryptkey($gpg ,$form['fingerprint']); - $from_exploded = explode('.', $form['from']); $from = reset($from_exploded); $info = gnupg_keyinfo($gpg, ''); diff --git a/modules/pgp/site.js b/modules/pgp/site.js index 03bc5e3e8e..be4812ca6b 100644 --- a/modules/pgp/site.js +++ b/modules/pgp/site.js @@ -347,6 +347,7 @@ $(function() { [{'name': 'hm_ajax_hook', 'value': 'ajax_generate_autocrypt_keys'}, {'name': 'hm_page_key', 'value': $('#hm_page_key').val()}], function(res) { + window.location = window.location; }, [], true diff --git a/modules/smtp/modules.php b/modules/smtp/modules.php index 677d3f0b32..5e0a7da55c 100644 --- a/modules/smtp/modules.php +++ b/modules/smtp/modules.php @@ -703,9 +703,48 @@ public function process() { return; } + $autocrypt_enabled = false; + // Autocrypt + if ($this->module_is_supported('pgp')) { + $tmp_dir = ini_get('keyring_dir') ? ini_get('keyring_dir') : '/keyring'; + putenv(sprintf('GNUPGHOME=%s/.gnupg', $tmp_dir)); + $gpg = gnupg_init(); + gnupg_setarmor($gpg,1); + + // Auto Encrypt + $keys = $this->user_config->get('pgp_public_keys', array()); + foreach ($keys as $key) { + if ($key['email'] == $to) { + gnupg_addencryptkey($gpg, $key['fingerprint']); + } + } + foreach ($this->user_config->get('autocrypt_keys', array()) as $key) { + if ($key['email'] == $from && !$autocrypt_enabled) { + gnupg_addsignkey($gpg, $key['key_fingerprint']); + gnupg_addencryptkey($gpg ,$key['key_fingerprint']); + $encrypted_msg = gnupg_sign($gpg, $body); + $encrypted_msg = gnupg_encrypt($gpg, $encrypted_msg); + $body = $encrypted_msg; + $autocrypt_enabled = true; + + } + } + } + /* build message */ $mime = new Hm_MIME_Msg($to, $subject, $body, $from, $body_type, $cc, $bcc, $in_reply_to, $from_name, $reply_to); + if ($this->module_is_supported('pgp')) { + # Autocrypt + $autocrypt_keys = $this->user_config->get('autocrypt_keys'); + gnupg_setarmor($gpg, 0); + foreach ($autocrypt_keys as $autocrypt_key) { + if (strstr($from, $autocrypt_key['email'])) { + $mime->set_autocrypt_header(base64_encode(gnupg_export($gpg, $autocrypt_key['key_fingerprint']))); + } + } + } + /* add attachments */ $mime->add_attachments($uploaded_files); $res = $mime->process_attachments(); @@ -718,22 +757,8 @@ public function process() { return; } - # Autocrypt - $autocrypt_keys = $this->user_config->get('autocrypt_keys'); - $tmp_dir = ini_get('keyring_dir') ? ini_get('keyring_dir') : '/keyring'; - putenv(sprintf('GNUPGHOME=%s/.gnupg', $tmp_dir)); - $res = gnupg_init(); - gnupg_setarmor($res, 0); - foreach ($autocrypt_keys as $autocrypt_key) { - if (strstr($from, $autocrypt_key['email'])) { - $mime->set_autocrypt_header(base64_encode(gnupg_export($res,$autocrypt_key['key_fingerprint']))); - } - } - #print_r($mime->get_mime_msg()); - #die; - /* send the message */ - $err_msg = $smtp->send_message($from, $recipients, $mime->get_mime_msg()); + $err_msg = $smtp->send_message($from, $recipients, $mime->get_mime_msg($autocrypt_enabled)); if ($err_msg) { Hm_Msgs::add(sprintf("ERR%s", $err_msg)); repopulate_compose_form($draft, $this); @@ -744,7 +769,7 @@ public function process() { $auto_bcc = $this->user_config->get('smtp_auto_bcc_setting', false); if ($auto_bcc) { $mime->set_auto_bcc($from); - $bcc_err_msg = $smtp->send_message($from, array($from), $mime->get_mime_msg()); + $bcc_err_msg = $smtp->send_message($from, array($from), $mime->get_mime_msg($autocrypt_enabled)); } /* check for associated IMAP server to save a copy */ @@ -1129,12 +1154,6 @@ protected function output() { smtp_server_dropdown($this->module_output(), $this, $recip, $selected_id). ''; - // Autocrypt Header Key - if ($pgp_enabled) { - $res .= ''; - $res .= ''; - } - if ($this->get('list_path') && $reply_type == 'reply') { $res .= ''; }