<?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;
}
}
?>