]> git.mxchange.org Git - friendica-addons.git/blob - securemail/vendor/singpolyma/openpgp-php/lib/openpgp_crypt_rsa.php
securemail: update pgp library
[friendica-addons.git] / securemail / vendor / singpolyma / openpgp-php / lib / openpgp_crypt_rsa.php
1 <?php
2 // This is free and unencumbered software released into the public domain.
3 /**
4  * OpenPGP_Crypt_RSA.php is a wrapper for using the classes from OpenPGP.php with Crypt_RSA
5  *
6  * @package OpenPGP
7  */
8
9 // From http://phpseclib.sourceforge.net/
10 require_once 'Crypt/RSA.php';
11
12 require_once dirname(__FILE__).'/openpgp.php';
13 @include_once dirname(__FILE__).'/openpgp_crypt_symmetric.php'; /* For encrypt/decrypt */
14
15 class OpenPGP_Crypt_RSA {
16   protected $key, $message;
17
18   // Construct a wrapper object from a key or a message packet
19   function __construct($packet) {
20     if(!is_object($packet)) $packet = OpenPGP_Message::parse($packet);
21     if($packet instanceof OpenPGP_PublicKeyPacket || $packet[0] instanceof OpenPGP_PublicKeyPacket) { // If it's a key (other keys are subclasses of this one)
22       $this->key = $packet;
23     } else {
24       $this->message = $packet;
25     }
26   }
27
28   function key($keyid=NULL) {
29     if(!$this->key) return NULL; // No key
30     if($this->key instanceof OpenPGP_Message) {
31       foreach($this->key as $p) {
32         if($p instanceof OpenPGP_PublicKeyPacket) {
33           if(!$keyid || strtoupper(substr($p->fingerprint, strlen($keyid)*-1)) == strtoupper($keyid)) return $p;
34         }
35       }
36     }
37     return $this->key;
38   }
39
40   // Get Crypt_RSA for the public key
41   function public_key($keyid=NULL) {
42     return self::convert_public_key($this->key($keyid));
43   }
44
45   // Get Crypt_RSA for the private key
46   function private_key($keyid=NULL) {
47     return self::convert_private_key($this->key($keyid));
48   }
49
50   // Pass a message to verify with this key, or a key (OpenPGP or Crypt_RSA) to check this message with
51   // Second optional parameter to specify which signature to verify (if there is more than one)
52   function verify($packet) {
53     $self = $this; // For old PHP
54     if(!is_object($packet)) $packet = OpenPGP_Message::parse($packet);
55     if(!$this->message) {
56       $m = $packet;
57       $verifier = function($m, $s) use($self) {
58         $key = $self->public_key($s->issuer());
59         if(!$key) return false;
60         $key->setHash(strtolower($s->hash_algorithm_name()));
61         return $key->verify($m, reset($s->data));
62       };
63     } else {
64       if(!($packet instanceof Crypt_RSA)) {
65         $packet = new self($packet);
66       }
67
68       $m = $this->message;
69       $verifier = function($m, $s) use($self, $packet) {
70         if(!($packet instanceof Crypt_RSA)) {
71           $key = $packet->public_key($s->issuer());
72         }
73         if(!$key) return false;
74         $key->setHash(strtolower($s->hash_algorithm_name()));
75         return $key->verify($m, reset($s->data));
76       };
77     }
78
79     return $m->verified_signatures(array('RSA' => array(
80       'MD5'    => $verifier,
81       'SHA1'   => $verifier,
82       'SHA224' => $verifier,
83       'SHA256' => $verifier,
84       'SHA384' => $verifier,
85       'SHA512' => $verifier
86     )));
87   }
88
89   // Pass a message to sign with this key, or a secret key to sign this message with
90   // Second parameter is hash algorithm to use (default SHA256)
91   // Third parameter is the 16-digit key ID to use... defaults to the key id in the key packet
92   function sign($packet, $hash='SHA256', $keyid=NULL) {
93     if(!is_object($packet)) {
94       if($this->key) {
95         $packet = new OpenPGP_LiteralDataPacket($packet);
96       } else {
97         $packet = OpenPGP_Message::parse($packet);
98       }
99     }
100
101     if($packet instanceof OpenPGP_SecretKeyPacket || $packet instanceof Crypt_RSA
102        || ($packet instanceof ArrayAccess && $packet[0] instanceof OpenPGP_SecretKeyPacket)) {
103       $key = $packet;
104       $message = $this->message;
105     } else {
106       $key = $this->key;
107       $message = $packet;
108     }
109
110     if(!$key || !$message) return NULL; // Missing some data
111
112     if($message instanceof OpenPGP_Message) {
113       $sign = $message->signatures();
114       $message = $sign[0][0];
115     }
116
117     if(!($key instanceof Crypt_RSA)) {
118       $key = new self($key);
119       if(!$keyid) $keyid = substr($key->key()->fingerprint, -16, 16);
120       $key = $key->private_key($keyid);
121     }
122     $key->setHash(strtolower($hash));
123
124     $sig = new OpenPGP_SignaturePacket($message, 'RSA', strtoupper($hash));
125     $sig->hashed_subpackets[] = new OpenPGP_SignaturePacket_IssuerPacket($keyid);
126     $sig->sign_data(array('RSA' => array($hash => function($data) use($key) {return array($key->sign($data));})));
127
128     return new OpenPGP_Message(array($sig, $message));
129   }
130
131   /** Pass a message with a key and userid packet to sign */
132   // TODO: merge this with the normal sign function
133   function sign_key_userid($packet, $hash='SHA256', $keyid=NULL) {
134     if(is_array($packet)) {
135       $packet = new OpenPGP_Message($packet);
136     } else if(!is_object($packet)) {
137       $packet = OpenPGP_Message::parse($packet);
138     }
139
140     $key = $this->private_key($keyid);
141     if(!$key || !$packet) return NULL; // Missing some data
142
143     if(!$keyid) $keyid = substr($this->key->fingerprint, -16);
144     $key->setHash(strtolower($hash));
145
146     $sig = NULL;
147     foreach($packet as $p) {
148       if($p instanceof OpenPGP_SignaturePacket) $sig = $p;
149     }
150     if(!$sig) {
151       $sig = new OpenPGP_SignaturePacket($packet, 'RSA', strtoupper($hash)); 
152       $sig->signature_type = 0x13;
153       $sig->hashed_subpackets[] = new OpenPGP_SignaturePacket_KeyFlagsPacket(array(0x01, 0x02));
154       $sig->hashed_subpackets[] = new OpenPGP_SignaturePacket_IssuerPacket($keyid);
155       $packet[] = $sig;
156     }
157
158     $sig->sign_data(array('RSA' => array($hash => function($data) use($key) {return array($key->sign($data));})));
159
160     return $packet;
161   }
162
163   function decrypt($packet) {
164     if(!is_object($packet)) $packet = OpenPGP_Message::parse($packet);
165
166     if($packet instanceof OpenPGP_SecretKeyPacket || $packet instanceof Crypt_RSA
167        || ($packet instanceof ArrayAccess && $packet[0] instanceof OpenPGP_SecretKeyPacket)) {
168       $keys = $packet;
169       $message = $this->message;
170     } else {
171       $keys = $this->key;
172       $message = $packet;
173     }
174
175     if(!$keys || !$message) return NULL; // Missing some data
176
177     if(!($keys instanceof Crypt_RSA)) {
178       $keys = new self($keys);
179     }
180
181     foreach($message as $p) {
182       if($p instanceof OpenPGP_AsymmetricSessionKeyPacket) {
183         if($keys instanceof Crypt_RSA) {
184           $sk = self::try_decrypt_session($keys, substr($p->encrypted_data, 2));
185         } else if(strlen(str_replace('0', '', $p->keyid)) < 1) {
186           foreach($keys->key as $k) {
187             $sk = self::try_decrypt_session(self::convert_private_key($k), substr($p->encrypted_data, 2));
188             if($sk) break;
189           }
190         } else {
191           $key = $keys->private_key($p->keyid);
192           $sk = self::try_decrypt_session($key, substr($p->encrypted_data, 2));
193         }
194
195         if(!$sk) continue;
196
197         $r = OpenPGP_Crypt_Symmetric::decryptPacket(OpenPGP_Crypt_Symmetric::getEncryptedData($message), $sk[0], $sk[1]);
198         if($r) return $r;
199       }
200     }
201
202     return NULL; /* Failed */
203   }
204
205   static function try_decrypt_session($key, $edata) {
206     $key->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
207     $data = $key->decrypt($edata);
208     $sk = substr($data, 1, strlen($data)-3);
209     $chk = unpack('n', substr($data, -2));
210     $chk = reset($chk);
211
212     $sk_chk = 0;
213     for($i = 0; $i < strlen($sk); $i++) {
214       $sk_chk = ($sk_chk + ord($sk{$i})) % 65536;
215     }
216
217     if($sk_chk != $chk) return NULL;
218     return array(ord($data{0}), $sk);
219   }
220
221   static function crypt_rsa_key($mod, $exp, $hash='SHA256') {
222     $rsa = new Crypt_RSA();
223     $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
224     $rsa->setHash(strtolower($hash));
225     $rsa->modulus = new Math_BigInteger($mod, 256);
226     $rsa->k = strlen($rsa->modulus->toBytes());
227     $rsa->exponent = new Math_BigInteger($exp, 256);
228     $rsa->setPublicKey();
229     return $rsa;
230   }
231
232   static function convert_key($packet, $private=false) {
233     if(!is_object($packet)) $packet = OpenPGP_Message::parse($packet);
234     if($packet instanceof OpenPGP_Message) $packet = $packet[0];
235
236     $mod = $packet->key['n'];
237     $exp = $packet->key['e'];
238     if($private) $exp = $packet->key['d'];
239     if(!$exp) return NULL; // Packet doesn't have needed data
240
241     $rsa = self::crypt_rsa_key($mod, $exp);
242
243     if($private) {
244       if($packet->key['p'] && $packet->key['q']) $rsa->primes = array($packet->key['p'], $packet->key['q']);
245       if($packet->key['u']) $rsa->coefficients = array($packet->key['u']);
246     }
247
248     return $rsa;
249   }
250
251   static function convert_public_key($packet) {
252     return self::convert_key($packet, false);
253   }
254
255   static function convert_private_key($packet) {
256     return self::convert_key($packet, true);
257   }
258
259 }
260
261 ?>