[dev] gpg/pgp patches (multiple recipients, search in prefs, group encryption)

Andreas Maag andreas at maagical.ch
Mon Dec 6 00:30:42 PST 2004


Hi,
after several version of this patch (first to squirrelmail ;-)) but now to the
much nicer horde i'm having it ready for public and hopefully for the
CVS-Version?

the patch:
http://www.immerda.ch/patches/immerda-gpg_enhancement.patch

this patch to the CVS-HEAD Version of the horde-project (http://www.horde.org)
(mostly framework and imp) enables the follwing fuctionality
* allowes encryption to several recipients (if we have the keys for all of them)
* searches for the keys in the prefs, searches for the key too if we do not have
a fingerprint
* adds a key for groups (beta)

for more informations (and a few other patches) visit:
http://www.immerda.ch/index.php/Horde-Patches

bye
a.
-------------- next part --------------
diff -Naur aktuell-041127-ORIG/framework/Crypt/Crypt/pgp.php aktuell-041127/framework/Crypt/Crypt/pgp.php
--- aktuell-041127-ORIG/framework/Crypt/Crypt/pgp.php	2004-11-23 08:06:33.000000000 +0100
+++ aktuell-041127/framework/Crypt/Crypt/pgp.php	2004-12-06 07:01:50.000000000 +0100
@@ -697,6 +697,9 @@
                                 $timeout = PGP_KEYSERVER_TIMEOUT)
     {
         /* Get the 8 character fingerprint string. */
+
+/* fingerprint can also be an emailaddress!! in that case just search with that */
+        if (strpos($fprint, '@') == 0) {
         if (strpos($fprint, '0x') === 0) {
             $fprint = substr($fprint, 2);
         }
@@ -704,6 +707,7 @@
             $fprint = substr($fprint, 8);
         }
         $fprint = '0x' . $fprint;
+        } // end fingerprint can be emailadress
 
         /* Connect to the public keyserver. */
         $cmd = 'GET /pks/lookup?op=get&exact=on&search=' . $fprint . ' HTTP/1.0';
@@ -907,7 +911,7 @@
             '--fast-import',
             $keyring
         );
-        $this->_callGpg($cmdline, 'w', array_values($keys));
+        $res = $this->_callGpg($cmdline, 'w', array_values($keys),true,true);
 
         return $keyring;
     }
@@ -927,6 +931,9 @@
      * 'email'   =>  E-mail address of recipient. If not present, or not found
      *               in the public key, the first e-mail address found in the
      *               key will be used instead. (Optional)
+     * OR
+     * 'email'     is an array of emails
+     * 'pubkey' is an array of public keys (corresp. emails)
      * </pre>
      *
      * @return string  The encrypted message.
@@ -941,9 +948,40 @@
             return PEAR::raiseError(_("A public PGP key is required to encrypt a message."), 'horde.error');
         }
 
+        // allow encryption for several emails
+        $multiple = false;
+        $pubkeycount = 1;
+        if(array_key_exists('email', $params)){
+            if(count($params['pubkey']) > 1 || count($params['email']) > 1){
+                $multiple = true;
+                $pubkeycount = count($params['pubkey']);
+            };
+        }
+        $i = 0;
+        $emailarguments = "";
+        $emailarguments2 = "";
+        $pubkeys = array();
+        $pubkey = $params['pubkey'];
+        for($i=0; $i < $pubkeycount; $i++){
+            if(array_key_exists('email', $params)){
+                if($multiple){
+                    $pubkey = $params['pubkey'][$i];
+                    $email = $params['email'][$i];
+                }else{ // just 1 key, ie. for the test stuff etc.
+                    if(is_array($params['pubkey'])){
+                        $pubkey = $params['pubkey'][0];
+                        $email = $params['email'][0];
+                    }else{
+                        $pubkey = $params['pubkey'];
+                        $email = $params['email'];
+                    }
+                }
+            }
+        // continue with for-loop
+
         /* Get information on the key. */
         if (isset($params['email'])) {
-            $key_info = $this->pgpPacketSignature($params['pubkey'], $params['email']);
+            $key_info = $this->pgpPacketSignature($pubkey, $email);
             if (!empty($key_info)) {
                 $email = $key_info['email'];
             }
@@ -952,7 +990,7 @@
         /* If we have no email address at this point, use the first email
            address found in the public key. */
         if (empty($email)) {
-            $key_info = $this->pgpPacketInformation($params['pubkey']);
+            $key_info = $this->pgpPacketInformation($pubkey);
             if (isset($key_info['signature']['id1']['email'])) {
                 $email = $key_info['signature']['id1']['email'];
             } else {
@@ -960,8 +998,13 @@
             }
         }
 
+           array_push($pubkeys,$pubkey);
+           $emailarguments .= ' --recipient ' . $email;
+           $emailarguments2 .= ' --encrypt-to ' . $email;
+       } // end for-loop for all the keys
+
         /* Store public key in temporary keyring. */
-        $keyring = $this->_putInKeyring($params['pubkey']);
+        $keyring = $this->_putInKeyring($pubkeys);
 
         /* Encrypt the document. */
         $cmdline = array(
@@ -969,10 +1012,24 @@
             '--batch',
             '--always-trust',
             '--recipient ' . $email,
+            $emailarguments,
             $keyring,
             '--encrypt'
         );
-        $result = $this->_callGpg($cmdline, 'w', $text, true);
+        $cmdline2 = array(
+            '--armor',
+            '--batch',
+            '--always-trust',
+            $emailarguments2,
+            $keyring,
+            '--encrypt'
+        );
+        $result = $this->_callGpg($cmdline, 'w', $text, true,true);
+        if (empty($result->output)) {
+            // try it again with --encrypt-to instead of --recipient
+            $result = $this->_callGpg($cmdline2, 'w', $text, true,true);
+        }
+
         if (empty($result->output)) {
             return PEAR::raiseError(_("Could not PGP encrypt message."), 'horde.error');
         }
@@ -1425,7 +1482,6 @@
 
         /* Build the command line string now. */
         $cmdline = implode(' ', array_merge($this->_gnupg, $options));
-
         if ($mode == 'w') {
             $fp = popen($cmdline, 'w');
             $win32 = substr(PHP_OS, 0, 3) == 'WIN';
diff -Naur aktuell-041127-ORIG/framework/Group/Group.php aktuell-041127/framework/Group/Group.php
--- aktuell-041127-ORIG/framework/Group/Group.php	2004-08-25 13:44:32.000000000 +0200
+++ aktuell-041127/framework/Group/Group.php	2004-12-05 19:36:10.000000000 +0100
@@ -628,6 +628,15 @@
         $attributes[] = array('name' => 'email',
                               'key' => '',
                               'value' => $this->get('email'));
+        $attributes[] = array('name' => 'public_key',
+                              'key' => '',
+                              'value' => $this->get('public_key'));
+        $attributes[] = array('name' => 'private_key',
+                              'key' => '',
+                              'value' => $this->get('private_key'));
+        $attributes[] = array('name' => 'private_key_password',
+                              'key' => '',
+                              'value' => $this->get('private_key_password'));
 
         return $attributes;
     }
diff -Naur aktuell-041127-ORIG/horde/admin/groups.php aktuell-041127/horde/admin/groups.php
--- aktuell-041127-ORIG/horde/admin/groups.php	2004-11-19 21:25:04.000000000 +0100
+++ aktuell-041127/horde/admin/groups.php	2004-12-05 19:36:10.000000000 +0100
@@ -131,6 +131,11 @@
     // Set the email address of the group.
     $group->set('email', Util::getFormData('email'));
 
+    // Set private and public keys for that group
+    $group->set('public_key', Util::getFormData('public_key'));
+    $group->set('private_key', Util::getFormData('private_key'));
+    $group->set('private_key_password', Util::getFormData('private_key_password'));
+
     // Save the group to the backend.
     $group->save();
 
diff -Naur aktuell-041127-ORIG/horde/templates/admin/groups/edit.inc aktuell-041127/horde/templates/admin/groups/edit.inc
--- aktuell-041127-ORIG/horde/templates/admin/groups/edit.inc	2004-10-19 17:22:17.000000000 +0200
+++ aktuell-041127/horde/templates/admin/groups/edit.inc	2004-12-06 05:36:55.000000000 +0100
@@ -12,6 +12,18 @@
   <td class="light" nowrap="nowrap"><?php echo _("Email Address") ?></td>
   <td colspan="2"><input type="text" name="email" size="50" value="<?php echo htmlspecialchars($group->get('email')) ?>" /></td>
 </tr>
+<tr>
+  <td class="light" nowrap="nowrap"><?php echo _("Public Key") ?></td>
+  <td colspan="2"><input type="text" name="public_key" size="80" value="<?php echo htmlspecialchars($group->get('public_key')) ?>" /></td>
+</tr>
+<tr>
+  <td class="light" nowrap="nowrap"><?php echo _("Private Key") ?> </td>
+  <td colspan="2"><input type="text" name="private_key" size="80" value="<?php echo htmlspecialchars($group->get('private_key')) ?>" /></td>
+</tr>
+<tr>
+  <td class="light" nowrap="nowrap"><?php echo _("Private Key")." "._("Password") ?></td>
+  <td colspan="2"><input type="text" name="private_key_password" size="80" value="<?php echo htmlspecialchars($group->get('private_key_password')) ?>" /></td>
+</tr>
 <tr><td>&nbsp;</td></tr>
 
 <tr valign="middle">
diff -Naur aktuell-041127-ORIG/imp/lib/Crypt/PGP.php aktuell-041127/imp/lib/Crypt/PGP.php
--- aktuell-041127-ORIG/imp/lib/Crypt/PGP.php	2004-11-24 08:51:24.000000000 +0100
+++ aktuell-041127/imp/lib/Crypt/PGP.php	2004-12-06 07:05:39.000000000 +0100
@@ -178,6 +178,16 @@
         return $key_info;
     }
 
+    function _getPublicKeyFromPrefs($address)
+    {
+        // from framework/Prefs/Prefs.php
+        // from ingo/scripts/convert_imp_filters.php
+        global $conf;
+        $userprefs = &Prefs::singleton($conf['prefs']['driver'],'imp', $address, '', null, false);
+        $userprefs->retrieve();
+        return $pk = $userprefs->getValue('pgp_public_key');
+    }
+
     /**
      * Retrieves a public key by e-mail.
      * First, the key will be attempted to be retrieved from a user's
@@ -196,9 +206,20 @@
     function getPublicKey($address, $fingerprint = null)
     {
         /* Try retrieving by e-mail only first. */
+        /* 1. try users database */
+        $prefs_key = $this->_getPublicKeyFromPrefs($address);
+        if(strlen($prefs_key) > 100 && 
+            preg_match('/-----BEGIN PGP ([^-]+)-----/', $prefs_key)){
+            return $prefs_key;
+        }
+
+        /* 2. try retrieving from Contacts */
         $result = $GLOBALS['registry']->call('contacts/getField', array($address, IMP_PGP_PUBKEY_FIELD, $this->_sources));
 
         /* TODO: Retrieve by ID. */
+        if($fingerprint == null){
+            $fingerprint = $address;
+        }
 
         /* Try retrieving via a PGP public keyserver. */
         if (is_a($result, 'PEAR_Error') && !empty($fingerprint)) {
@@ -508,8 +529,57 @@
      */
     function decryptMessage($text)
     {
+        // try to decrypt with different keys (loop)
+        // 0. arrays
+        $pubkeyarray = array();
+        $privkeyarray = array();
+        $pwarray = array();
+        array_push($pubkeyarray, $this->getPersonalPublicKey());
+        array_push($privkeyarray, $this->getPersonalPrivateKey());
+        array_push($pwarray, $this->getPassphrase()); // ?????? add loop for several addresses<->pws
+
+        // 1. if the user is in a group(s), get the keys from that group(s), if any
+        require_once 'Horde/Group.php';
+        require_once 'Horde/Tree.php';
+        $groups = &Group::singleton();
+ 
+        // 1a. authentification
+        $user = Auth::getAuth();
+        $group_list = $groups -> getGroupMemberships($user);
+ 
+        // 1b. geta data
+        if (!empty($group_list)) {
+            foreach ($group_list as $gr) {
+                $group = &$groups->getGroup($gr);
+                $gr_email = $group->get('email');
+                $gr_pk = $group->get('public_key');
+                $gr_sk = $group->get('private_key');
+                $gr_pw = $group->get('private_key_password');
+                if((strlen($gr_email)>0) && (strlen($gr_pk)>0) && (strlen($gr_sk)>0)) {
+                    array_push($pubkeyarray, $gr_pk);
+                    array_push($privkeyarray, $gr_sk);
+                    array_push($pwarray, $gr_pw);
+                }
+            }
+        }
+        // 2. decrypt with these keys (loop over the arrays)
+        $i = 0;
+        $c = count($privkeyarray);
+        while($i < $c){
+
         /* decrypt() returns a PEAR_Error object on error. */
-        return $this->decrypt($text, array('type' => 'message', 'pubkey' => $this->getPersonalPublicKey(), 'privkey' => $this->getPersonalPrivateKey(), 'passphrase' => $this->getPassphrase()));
+        $r = $this->decrypt($text, array('type' => 'message', 'pubkey' => $pubkeyarray[$i], 'privkey' => $privkeyarray[$i], 'passphrase' => $pwarray[$i]));
+        if (is_a($r, 'PEAR_Error')) {
+            // echo "no success<BR>";
+            // try the next key / password
+        }else{
+            // echo "success<BR>";
+            // return $r;
+            $i = $c;
+        }
+        $i++;
+      } // end while loop over the keys
+      return $r;
     }
 
     /**
@@ -642,9 +712,15 @@
      */
     function _encryptParameters($to_address)
     {
-        /* We can only encrypt if we are sending to a single person. */
+        /* I'd like to encrypt to whatever number of recipients there are, if they all have a public key! */
         $addrOb = IMP::bareAddress($to_address, true);
-        $key_addr = array_pop($addrOb);
+        $pubkeyarray = array();
+        $emailarray = array();
+ 
+        $i = 0;
+        while ($i < count($addrOb)){
+            $key_addr = $addrOb[$i];
+            if($key_addr != "INVALID_ADDRESS at .SYNTAX-ERROR."){
 
         /* Get the public key for the address. */
         $public_key = $this->getPublicKey($key_addr);
@@ -652,7 +728,12 @@
             return $public_key;
         }
 
-        return array('pubkey' => $public_key, 'email' => $key_addr);
+           array_push($emailarray, $key_addr);
+           array_push($pubkeyarray, $public_key);
+           }
+               $i++;
+        }
+        return array('pubkey' => $pubkeyarray, 'email' => $emailarray);
     }
 
     /**


More information about the dev mailing list