From e4c66080a8d7e3a329258498b35e8fc55eea5130 Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Mon, 07 Sep 2015 03:47:16 -0400
Subject: [PATCH] Improved encrypt/decrypt methods with option to choose the cipher_method (#1489719)

---
 program/lib/Roundcube/rcube.php        |   27 ++++---------
 CHANGELOG                              |    1 
 installer/config.php                   |    3 -
 program/lib/Roundcube/rcube_config.php |   25 +++++++-----
 config/defaults.inc.php                |   11 ++++-
 5 files changed, 34 insertions(+), 33 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 6a14ab8..f97f1fd 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 CHANGELOG Roundcube Webmail
 ===========================
 
+- Improved encrypt/decrypt methods with option to choose the cipher_method (#1489719)
 - Make optional adding of standard signature separator - sig_separator (#1487768)
 - Optimize folder_size() on Cyrus IMAP by using special folder annotation (#1490514)
 - Make optional hidding of folders with name starting with a dot - imap_skip_hidden_folders (#1490468)
diff --git a/config/defaults.inc.php b/config/defaults.inc.php
index a3b7b53..45d2c7b 100644
--- a/config/defaults.inc.php
+++ b/config/defaults.inc.php
@@ -446,11 +446,16 @@
 // Possible values: sameorigin|deny. Set to false in order to disable sending them
 $config['x_frame_options'] = 'sameorigin';
 
-// this key is used to encrypt the users imap password which is stored
-// in the session record (and the client cookie if remember password is enabled).
-// please provide a string of exactly 24 chars.
+// This key is used for encrypting purposes, like storing of imap password
+// in the session. For historical reasons it's called DES_key, but it's used
+// with any configured cipher_method (see below).
 $config['des_key'] = 'rcmail-!24ByteDESkey*Str';
 
+// Encryption algorithm. You can use any method supported by openssl.
+// Default is set for backward compatibility to DES-EDE3-CBC,
+// but you can choose e.g. AES-256-CBC which we consider a better choice.
+$config['cipher_method'] = 'DES-EDE3-CBC';
+
 // Automatically add this domain to user names for login
 // Only for IMAP servers that require full e-mail addresses for login
 // Specify an array with 'host' => 'domain' values to support multiple hosts
diff --git a/installer/config.php b/installer/config.php
index 4882c1d..588e14d 100644
--- a/installer/config.php
+++ b/installer/config.php
@@ -128,8 +128,7 @@
 
 ?>
 <div>This key is used to encrypt the users imap password before storing in the session record</div>
-<p class="hint">It's a random generated string to ensure that every installation has its own key.
-If you enter it manually please provide a string of exactly 24 chars.</p>
+<p class="hint">It's a random generated string to ensure that every installation has its own key.</p>
 </dd>
 
 <dt class="propname">ip_check</dt>
diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php
index 4d80dc0..37b5a3a 100644
--- a/program/lib/Roundcube/rcube.php
+++ b/program/lib/Roundcube/rcube.php
@@ -810,26 +810,22 @@
     }
 
     /**
-     * Encrypt using 3DES
+     * Encrypt a string
      *
      * @param string  $clear  Clear text input
      * @param string  $key    Encryption key to retrieve from the configuration, defaults to 'des_key'
      * @param boolean $base64 Whether or not to base64_encode() the result before returning
      *
-     * @return string encrypted text
+     * @return string Encrypted text
      */
     public function encrypt($clear, $key = 'des_key', $base64 = true)
     {
-        if (!$clear) {
+        if (!is_string($clear) || !strlen($clear)) {
             return '';
         }
 
-        // Add a single canary byte to the end of the clear text, which
-        // will help find out how much of padding will need to be removed
-        // upon decryption; see http://php.net/mcrypt_generic#68082.
-        $clear  = pack("a*H2", $clear, "80");
         $ckey   = $this->config->get_crypto_key($key);
-        $method = 'DES-EDE3-CBC';
+        $method = $this->config->get_crypto_method();
         $opts   = defined('OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : true;
         $iv     = rcube_utils::random_bytes(openssl_cipher_iv_length($method), true);
         $cipher = $iv . openssl_encrypt($clear, $method, $ckey, $opts, $iv);
@@ -838,13 +834,13 @@
     }
 
     /**
-     * Decrypt 3DES-encrypted string
+     * Decrypt a string
      *
      * @param string  $cipher Encrypted text
      * @param string  $key    Encryption key to retrieve from the configuration, defaults to 'des_key'
      * @param boolean $base64 Whether or not input is base64-encoded
      *
-     * @return string decrypted text
+     * @return string Decrypted text
      */
     public function decrypt($cipher, $key = 'des_key', $base64 = true)
     {
@@ -852,10 +848,9 @@
             return '';
         }
 
-        $cipher = $base64 ? base64_decode($cipher) : $cipher;
-        $ckey   = $this->config->get_crypto_key($key);
-
-        $method  = 'DES-EDE3-CBC';
+        $cipher  = $base64 ? base64_decode($cipher) : $cipher;
+        $ckey    = $this->config->get_crypto_key($key);
+        $method  = $this->config->get_crypto_method();
         $opts    = defined('OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : true;
         $iv_size = openssl_cipher_iv_length($method);
         $iv      = substr($cipher, 0, $iv_size);
@@ -867,10 +862,6 @@
 
         $cipher = substr($cipher, $iv_size);
         $clear  = openssl_decrypt($cipher, $method, $ckey, $opts, $iv);
-
-        // Trim PHP's padding and the canary byte; see note in
-        // rcube::encrypt() and http://php.net/mcrypt_generic#68082
-        $clear = substr(rtrim($clear, "\0"), 0, -1);
 
         return $clear;
     }
diff --git a/program/lib/Roundcube/rcube_config.php b/program/lib/Roundcube/rcube_config.php
index 3dd54e3..799552f 100644
--- a/program/lib/Roundcube/rcube_config.php
+++ b/program/lib/Roundcube/rcube_config.php
@@ -505,7 +505,7 @@
     public function get_crypto_key($key)
     {
         // Bomb out if the requested key does not exist
-        if (!array_key_exists($key, $this->prop)) {
+        if (!array_key_exists($key, $this->prop) || empty($this->prop[$key])) {
             rcube::raise_error(array(
                 'code' => 500, 'type' => 'php',
                 'file' => __FILE__, 'line' => __LINE__,
@@ -513,18 +513,23 @@
             ), true, true);
         }
 
-        $key = $this->prop[$key];
+        return $this->prop[$key];
+    }
 
-        // Bomb out if the configured key is not exactly 24 bytes long
-        if (strlen($key) != 24) {
-            rcube::raise_error(array(
-                'code' => 500, 'type' => 'php',
-                'file' => __FILE__, 'line' => __LINE__,
-                'message' => "Configured crypto key '$key' is not exactly 24 bytes long"
-            ), true, true);
+    /**
+     * Return configured crypto method.
+     *
+     * @return string Crypto method
+     */
+    public function get_crypto_method()
+    {
+        $method = $this->get('cipher_method');
+
+        if (empty($method)) {
+            $method = 'DES-EDE3-CBC';
         }
 
-        return $key;
+        return $method;
     }
 
     /**

--
Gitblit v1.9.1