[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> </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