Location: PHPKode > projects > CsWebmail > cswebmail-3.10/cswebmail-3.10/include/gpg/simple_gpg.php
<?if(VALID_DOCUMENT != 1) die('what?');

/**
 * More simple wrapepr for gpg.php SquirrelMail library.
 * TODO: use http://ru.php.net/manual/en/ref.gnupg.php
 */
require_once('gpg.php');

class SimpleGPG{
  var $gpg;
  var $username;
  var $passphrase=false;
  var $sign_encrypted=false;
  
  function SimpleGPG($username=false,$create_dir=true){
    $this->username = $username?$username:$GLOBALS['MAIL_USER_NAME'];
    
    $this->gpg = new GnuPG;
    // popen or exec
    $this->gpg->force_exec=false;
    // use increase
    $this->gpg->allowEntropyIncrease=false;

    //$this->gpg->debug=true;
    
    $this->gpg->setHomeDir(get_user_local_dir($this->username).'gnupg/');

    if($create_dir && !is_dir($this->gpg->gpgHomeDir)){
      mkdir($this->gpg->gpgHomeDir);
      chmod($this->gpg->gpgHomeDir,0700);
    }
    
    $this->gpg->gpg_exe = '/usr/bin/gpg';

    $this->loadPassphrase();

    $this->gpg->getKeys();
  }

  function loadPassphrase(){
    if(Cache::haveCachedGPGPassphrase())
      $this->passphrase = Cache::getCachedGPGPassphrase();
  }

  // load passphrase from request (return success or not)
  // if already passphrase cached returns true
  function loadPassphraseFromRequest($field_name='password'){
    if(Cache::haveCachedGPGPassphrase()){
      return true;
    }
    $password = get_var($field_name);
    $skey = $this->getSecretKey();
    
    if($skey){
      $ret = $this->verifyPassphrase($skey,$password);
      if($ret['verified']){
        Cache::cacheGPGPassphrase($password);
        $this->loadPassphrase();
      }
        
      return $ret['verified'];
    }
    else
      return false;
  }

  // for fancy key printing ([0] is fancy key, [1] is fancy algo)
  function getKeyInfo($key){

    $algo = '';
    switch($key->algorithm){
    case GNUPG_PUBKEY_RSA: $algo='RSA';break;
    case GNUPG_PUBKEY_RSA_E: $algo='RSA-E';break;
    case GNUPG_PUBKEY_RSA_S: $algo='RSA-S';break;
    case GNUPG_PUBKEY_ELGAMAL: $algo='ELG';break;
    case GNUPG_PUBKEY_ELGAMAL_E: $algo='ELG-E';break;
    case GNUPG_PUBKEY_DSA: $algo='DSA';break;
    }
    return array(substr($key->id,8),$key->len.'-bit '.$algo);
  }
  
  // for fancy signature printing
  function getSignatureInfo($sig){
    if(!is_object($sig)) return $sig;
    
    $ret = '';
    if(isset($sig->uid->email_addr))
      $ret .= h($sig->uid->email_name.' <'.$sig->uid->email_addr.'>').'<br/>';
    $ret .= 'Key ID: '.$sig->id.' / Signed on: '.date('r',$sig->timestamp);

    return $ret;
  }

  // create new key
  function generateKey($name=false,$passphrase=false){
    $ret = $this->gpg->generateKey($name ? $name : $this->username,
                                   $this->username.'@'.MAILSUFFIX,
                                   $passphrase,// password
                                   '',// comment
                                   1024,// length
                                   0 // expired
                                   );

    if($ret['returnval'] === 0){
      Cache::cacheGPGPassphrase($passphrase);
      $this->loadPassphrase();
    }
    
    return $ret;
  }

  // get secret key obj
  function getSecretKey(){
    $secret_keys = $this->gpg->fetchKeys('','secret');
    return current($secret_keys['keys']);
  }

  // get secret key id
  function getSecretKeyId(){
    $key = $this->getSecretKey();
    if($key)
      return $key->id;
    else
      return false;
  }

  // get public keys
  function getPublicKeys(){
    $public_keys = $this->gpg->fetchKeys('','public');
    $skey = $this->getSecretKey();
    if($skey)
      unset($public_keys['keys'][$skey->fingerprint]);
    return $public_keys['keys'];
  }

  // return key object by id
  function getKey($id){
    return $this->gpg->getKey($id);
  }

  // get public keys
  function getKeyByEmail($email){
    $public_keys = $this->gpg->fetchKeys($email,'public');
    return current($public_keys['keys']);
  }

  // delete some key (type in {all,private})
  function deleteKey($key,$type='all'){
    return $this->gpg->deleteKey($key->fingerprint,$type);
  }

  // export key as ascii text
  function exportKey($id){
    $ret = $this->gpg->getExportText($id);
    return $ret['output'];
  }

  // import some key
  function importKey($text){
    $ret = $this->gpg->importKey_text($text);
    return $ret;
  }

  // search for some person local public key and import it
  function importLocalPersonKey($username){
    $gpg_other = new SimpleGPG($username,false);
    $key_id = $gpg_other->getSecretKeyId();
    if($key_id)
      return $this->importKey($gpg_other->exportKey());
    else
      return array('returnval'=>255,'errors'=>array('User <b>'.$username.'</b> have no public key'));
  }


  // encrypt
  // [cyphertext]   for cyphered text
  // [returnval]    for return value
  // [skipped_keys] keys that skipped
  function encrypt($body,$send_to_list){
    $this->gpg->force_exec=true;
    if($this->sign_encrypted){
      $skey = $this->getSecretKey();
      $ret = $this->gpg->encrypt($body,$send_to_list,'true',$this->passphrase,'',$skey->fingerprint);
    }
    else
      $ret = $this->gpg->encrypt($body,$send_to_list,'false');
    $this->gpg->force_exec=false;
    return $ret;
  }
  
  // decrypt
  // [plaintext]  is for decrypted data
  // [returnval]  for return value
  function decrypt($secret){
    // if(is_test_user()) $this->gpg->debug = true;
    $this->gpg->force_exec=true;
    $ret = $this->gpg->decrypt($secret,$this->passphrase);
    $this->gpg->force_exec=false;
    return $ret;
  }

  // check text for inline pgp message
  function isInlinePGPMessage($text){
    return strpos($text,'-----BEGIN PGP MESSAGE-----') !== false &&
           strpos($text,'-----END PGP MESSAGE-----')   !== false;
  }

  // [0] text before pgp message, [1] pgp message
  function separatePGPMessage($text){
    $pgp_start = strpos($text,'-----BEGIN PGP MESSAGE-----');
    $pgp_end = strpos($text,$end='-----END PGP MESSAGE-----');
    $pgp_end = $pgp_end+strlen($end)+1;
    return array(substr($text,0,$pgp_start),
                 substr($text,$pgp_start,$pgp_end),
                 substr($text,$pgp_end));
  }

  // check text for inline pgp signature
  function isInlinePGPSignature($text){
    return strpos($text,'-----BEGIN PGP SIGNED MESSAGE-----') !== false &&
           strpos($text,'-----BEGIN PGP SIGNATURE-----') !== false &&
           strpos($text,'-----END PGP SIGNATURE-----')   !== false;
  }

  // [0] text before pgp message, [1] pgp message
  function separatePGPSignature($text){
    $pgp_start = strpos($text,'-----BEGIN PGP SIGNED MESSAGE-----');
    $pgp_end = strpos($text,$end='-----END PGP SIGNATURE-----');
    $pgp_end = $pgp_end+strlen($end)+1;
    return array(substr($text,0,$pgp_start),
                 substr($text,$pgp_start,$pgp_end),
                 substr($text,$pgp_end));
  }

  // change password
  function changePassphrase($key,$password,$new_password){
    return $this->gpg->changePassphrase($key->fingerprint,$password,$new_password);
  }

  // verify password ([verified] = {true,false})
  function verifyPassphrase($key,$password){
    return $this->gpg->verifyPassphrase($password,$key->fingerprint);
  }
  
  // sign some data
  function sign($data){
    $skey = $this->getSecretKey();
    
    // if(is_test_user()) $this->gpg->debug = true;

    // using pipes, buggy for big files
    // $ret = $this->gpg->sign($data,$skey->fingerprint,$this->passphrase,'clear');

    // using files
    $ret = $this->gpg->signUsingFiles($data,$this->passphrase,$skey->fingerprint);
    
    // some hack
    if(isset($ret['output'])){
      // debug($ret['output']);
      // parse signature only
      $pos = strpos($ret['output'],'-----BEGIN PGP SIGNATURE-----');
      $ret['signature'] = substr($ret['output'],$pos);
    }
    return $ret;
  }

  // verify some data
  function verify($data,$signature){
    //if($data[strlen($data)-1]!="\n") $data .= "\n";
    // debug($data);
    // debug($signature);
    //return array('returnval'=>255);
    return $this->gpg->verifyUsingFiles($data,$signature);
  }

  // verify inline signed data
  function verifyInline($signed){
    return $this->gpg->verify($signed);
  }

  // make encrypted envelope and body (array consists from parts)
  // from given envelope and body
  function makeEncryptedEnvelopeBody($envelope,$body,$use_recipients=true){
    $return = array('returnval'=>0,
                    'envelope'=>$envelope,
                    'body'=>array());

    // recipients for encrypt
    $recipients = array();
    
    // check all keys, export exist
    if($use_recipients){
      foreach($return['envelope']['all-recipients'] as $email){
        $key = $this->getKeyByEmail($email);
        // if key is not in keys ring
        if(!$key){
          list($username,$domain) = explode('@',$email);
          // try to take from local repository
          if($domain == MAILSUFFIX){
            $ret = $this->importLocalPersonKey($username);
            if($ret['returnval'] == 0){
              $return['info'] .= $email." pub key added to your keys ring.\n";
            }
            else{
              $return['error'] .= implode("<br/>",$ret['errors']).'<br/>';
            }
          }
          else
            $return['error'] .= $email." have no pub key.<br/>";
        }
      }
      if($return['error'] != ''){
        $return['returnval'] = 2;
        return $return;
      }
      // all recipients from envelope
      $recipients = $return['envelope']['all-recipients'];
    }
    
    // encrypt by current user's public key at least
    $recipients[] = get_current_user_email();

    // make unique
    $recipients = array_unique($recipients);
    
    // add opengpg header
    if(list($key_id) = $this->getKeyInfo($this->getSecretKey()))
      $return['envelope']['custom_headers'][] = 'OpenGPG: id='.$key_id;

    // encrypt body by recipients public keys
    $ret = $this->encrypt(build_mail(array(),$body),$recipients);
    
    if($ret['returnval'] != 0){
      $return['returnval'] = 1;
      return $return;
    }
    // encrypted tag
    $return['envelope']['encrypted'] = true;
    
    // recreate body
    // first part
    $return['body'][] = array('type'=>'application/pgp-encrypted',
                              'description'=>'PGP/MIME version identification',
                              'contents.data'=>"Version: 1\n");
    // encrypted part
    $return['body'][] = array('type'=>'application/octet-stream; name="encrypted.asc"',
                              'description'=>'OpenPGP encrypted message',
                              'disposition'=>'inline; filename="encrypted.asc"',
                              'contents.data'=>$ret['cyphertext']);    
    return $return;
  }

  // make signed envelope and body from given envelope and body
  function makeSignedEnvelopeBody($envelope,$body){
    $return = array('returnval'=>0,
                    'envelope'=>$envelope,
                    'body'=>array());

    $for_sign = build_mail(array(),$body);

    // $for_sign = rtrim($for_sign)."\n";
    // because extra newline
    // $for_sign .= "\n";

    $ret = $this->sign($for_sign);
    
    if($ret['returnval'] === 0){
      $return['envelope']['signed'] = true;
      // test
      // $return['body'][] = array('type'=>'text/plain',
                                // 'disposition'=>'attachment; filename="all.txt"',
                                // 'contents.data'=>$ret['output']);
      
      // add last part

      $return['body'][] = array('envelope'=>array(),
                                'body'=>$body,
                                'composite'=>true);

      $return['body'][] = array('type'=>'application/pgp-signature',
                                'disposition'=>'attachment; filename="signature.asc"',
                                'description'=>'OpenPGP digital signature',
                                'contents.data'=>$ret['signature']);
      $return['info'] = "Signature added.\n";
    }
    else{
      $return['returnval'] = 1;
      $return['error'] = implode("<br/>",$ret['errors']);
    }
    
    return $return;
  }
}

?>
Return current item: CsWebmail