]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/OStatus/extlib/Crypt/RSA.php
generate keypairs for users, and put them in the XRD for discovery
[quix0rs-gnu-social.git] / plugins / OStatus / extlib / Crypt / RSA.php
1 <?php
2 /**
3  * Crypt_RSA allows to do following operations:
4  *     - key pair generation
5  *     - encryption and decryption
6  *     - signing and sign validation
7  *
8  * PHP versions 4 and 5
9  *
10  * LICENSE: This source file is subject to version 3.0 of the PHP license
11  * that is available through the world-wide-web at the following URI:
12  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
13  * the PHP License and are unable to obtain it through the web, please
14  * send a note to license@php.net so we can mail you a copy immediately.
15  *
16  * @category   Encryption
17  * @package    Crypt_RSA
18  * @author     Alexander Valyalkin <valyala@gmail.com>
19  * @copyright  2005, 2006 Alexander Valyalkin
20  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
21  * @version    1.2.0b
22  * @link       http://pear.php.net/package/Crypt_RSA
23  */
24
25 /**
26  * RSA error handling facilities
27  */
28 require_once 'Crypt/RSA/ErrorHandler.php';
29
30 /**
31  * loader for math wrappers
32  */
33 require_once 'Crypt/RSA/MathLoader.php';
34
35 /**
36  * helper class for mange single key
37  */
38 require_once 'Crypt/RSA/Key.php';
39
40 /**
41  * helper class for manage key pair
42  */
43 require_once 'Crypt/RSA/KeyPair.php';
44
45 /**
46  * Crypt_RSA class, derived from Crypt_RSA_ErrorHandler
47  *
48  * Provides the following functions:
49  *  - setParams($params) - sets parameters of current object
50  *  - encrypt($plain_data, $key = null) - encrypts data
51  *  - decrypt($enc_data, $key = null) - decrypts data
52  *  - createSign($doc, $private_key = null) - signs document by private key
53  *  - validateSign($doc, $signature, $public_key = null) - validates signature of document
54  *
55  * Example usage:
56  *     // creating an error handler
57  *     $error_handler = create_function('$obj', 'echo "error: ", $obj->getMessage(), "\n"');
58  *
59  *     // 1024-bit key pair generation
60  *     $key_pair = new Crypt_RSA_KeyPair(1024);
61  *
62  *     // check consistence of Crypt_RSA_KeyPair object
63  *     $error_handler($key_pair);
64  *
65  *     // creating Crypt_RSA object
66  *     $rsa_obj = new Crypt_RSA;
67  *
68  *     // check consistence of Crypt_RSA object
69  *     $error_handler($rsa_obj);
70  *
71  *     // set error handler on Crypt_RSA object ( see Crypt/RSA/ErrorHandler.php for details )
72  *     $rsa_obj->setErrorHandler($error_handler);
73  *
74  *     // encryption (usually using public key)
75  *     $enc_data = $rsa_obj->encrypt($plain_data, $key_pair->getPublicKey());
76  *
77  *     // decryption (usually using private key)
78  *     $plain_data = $rsa_obj->decrypt($enc_data, $key_pair->getPrivateKey());
79  *
80  *     // signing
81  *     $signature = $rsa_obj->createSign($document, $key_pair->getPrivateKey());
82  *
83  *     // signature checking
84  *     $is_valid = $rsa_obj->validateSign($document, $signature, $key_pair->getPublicKey());
85  *
86  *     // signing many documents by one private key
87  *     $rsa_obj = new Crypt_RSA(array('private_key' => $key_pair->getPrivateKey()));
88  *     // check consistence of Crypt_RSA object
89  *     $error_handler($rsa_obj);
90  *     // set error handler ( see Crypt/RSA/ErrorHandler.php for details )
91  *     $rsa_obj->setErrorHandler($error_handler);
92  *     // sign many documents
93  *     $sign_1 = $rsa_obj->sign($doc_1);
94  *     $sign_2 = $rsa_obj->sign($doc_2);
95  *     //...
96  *     $sign_n = $rsa_obj->sign($doc_n);
97  *
98  *     // changing default hash function, which is used for sign
99  *     // creating/validation
100  *     $rsa_obj->setParams(array('hash_func' => 'md5'));
101  *
102  *     // using factory() method instead of constructor (it returns PEAR_Error object on failure)
103  *     $rsa_obj = &Crypt_RSA::factory();
104  *     if (PEAR::isError($rsa_obj)) {
105  *         echo "error: ", $rsa_obj->getMessage(), "\n";
106  *     }
107  *
108  * @category   Encryption
109  * @package    Crypt_RSA
110  * @author     Alexander Valyalkin <valyala@gmail.com>
111  * @copyright  2005, 2006 Alexander Valyalkin
112  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
113  * @link       http://pear.php.net/package/Crypt_RSA
114  * @version    @package_version@
115  * @access     public
116  */
117 class Crypt_RSA extends Crypt_RSA_ErrorHandler
118 {
119     /**
120      * Reference to math wrapper, which is used to
121      * manipulate large integers in RSA algorithm.
122      *
123      * @var object of Crypt_RSA_Math_* class
124      * @access private
125      */
126     var $_math_obj;
127
128     /**
129      * key for encryption, which is used by encrypt() method
130      *
131      * @var object of Crypt_RSA_KEY class
132      * @access private
133      */
134     var $_enc_key;
135
136     /**
137      * key for decryption, which is used by decrypt() method
138      *
139      * @var object of Crypt_RSA_KEY class
140      * @access private
141      */
142     var $_dec_key;
143
144     /**
145      * public key, which is used by validateSign() method
146      *
147      * @var object of Crypt_RSA_KEY class
148      * @access private
149      */
150     var $_public_key;
151
152     /**
153      * private key, which is used by createSign() method
154      *
155      * @var object of Crypt_RSA_KEY class
156      * @access private
157      */
158     var $_private_key;
159
160     /**
161      * name of hash function, which is used by validateSign()
162      * and createSign() methods. Default hash function is SHA-1
163      *
164      * @var string
165      * @access private
166      */
167     var $_hash_func = 'sha1';
168
169     /**
170      * Crypt_RSA constructor.
171      *
172      * @param array $params
173      *        Optional associative array of parameters, such as:
174      *        enc_key, dec_key, private_key, public_key, hash_func.
175      *        See setParams() method for more detailed description of
176      *        these parameters.
177      * @param string $wrapper_name
178      *        Name of math wrapper, which will be used to
179      *        perform different operations with big integers.
180      *        See contents of Crypt/RSA/Math folder for examples of wrappers.
181      *        Read docs/Crypt_RSA/docs/math_wrappers.txt for details.
182      * @param string $error_handler   name of error handler function
183      *
184      * @access public
185      */
186     function Crypt_RSA($params = null, $wrapper_name = 'default', $error_handler = '')
187     {
188         // set error handler
189         $this->setErrorHandler($error_handler);
190         // try to load math wrapper
191         $obj = &Crypt_RSA_MathLoader::loadWrapper($wrapper_name);
192         if ($this->isError($obj)) {
193             // error during loading of math wrapper
194             // Crypt_RSA object is partially constructed.
195             $this->pushError($obj);
196             return;
197         }
198         $this->_math_obj = &$obj;
199
200         if (!is_null($params)) {
201             if (!$this->setParams($params)) {
202                 // error in Crypt_RSA::setParams() function
203                 return;
204             }
205         }
206     }
207
208     /**
209      * Crypt_RSA factory.
210      *
211      * @param array $params
212      *        Optional associative array of parameters, such as:
213      *        enc_key, dec_key, private_key, public_key, hash_func.
214      *        See setParams() method for more detailed description of
215      *        these parameters.
216      * @param string $wrapper_name
217      *        Name of math wrapper, which will be used to
218      *        perform different operations with big integers.
219      *        See contents of Crypt/RSA/Math folder for examples of wrappers.
220      *        Read docs/Crypt_RSA/docs/math_wrappers.txt for details.
221      * @param string $error_handler   name of error handler function
222      *
223      * @return object  new Crypt_RSA object on success or PEAR_Error object on failure
224      * @access public
225      */
226     function &factory($params = null, $wrapper_name = 'default', $error_handler = '')
227     {
228         $obj = &new Crypt_RSA($params, $wrapper_name, $error_handler);
229         if ($obj->isError()) {
230             // error during creating a new object. Retrurn PEAR_Error object
231             return $obj->getLastError();
232         }
233         // object created successfully. Return it
234         return $obj;
235     }
236
237     /**
238      * Accepts any combination of available parameters as associative array:
239      *     enc_key - encryption key for encrypt() method
240      *     dec_key - decryption key for decrypt() method
241      *     public_key - key for validateSign() method
242      *     private_key - key for createSign() method
243      *     hash_func - name of hash function, which will be used to create and validate sign
244      *
245      * @param array $params
246      *        associative array of permitted parameters (see above)
247      *
248      * @return bool   true on success or false on error
249      * @access public
250      */
251     function setParams($params)
252     {
253         if (!is_array($params)) {
254             $this->pushError('parameters must be passed to function as associative array', CRYPT_RSA_ERROR_WRONG_PARAMS);
255             return false;
256         }
257
258         if (isset($params['enc_key'])) {
259             if (Crypt_RSA_Key::isValid($params['enc_key'])) {
260                 $this->_enc_key = $params['enc_key'];
261             }
262             else {
263                 $this->pushError('wrong encryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
264                 return false;
265             }
266         }
267         if (isset($params['dec_key'])) {
268             if (Crypt_RSA_Key::isValid($params['dec_key'])) {
269                 $this->_dec_key = $params['dec_key'];
270             }
271             else {
272                 $this->pushError('wrong decryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
273                 return false;
274             }
275         }
276         if (isset($params['private_key'])) {
277             if (Crypt_RSA_Key::isValid($params['private_key'])) {
278                 if ($params['private_key']->getKeyType() != 'private') {
279                     $this->pushError('private key must have "private" attribute', CRYPT_RSA_ERROR_WRONG_KEY_TYPE);
280                     return false;
281                 }
282                 $this->_private_key = $params['private_key'];
283             }
284             else {
285                 $this->pushError('wrong private key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
286                 return false;
287             }
288         }
289         if (isset($params['public_key'])) {
290             if (Crypt_RSA_Key::isValid($params['public_key'])) {
291                 if ($params['public_key']->getKeyType() != 'public') {
292                     $this->pushError('public key must have "public" attribute', CRYPT_RSA_ERROR_WRONG_KEY_TYPE);
293                     return false;
294                 }
295                 $this->_public_key = $params['public_key'];
296             }
297             else {
298                 $this->pushError('wrong public key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
299                 return false;
300             }
301         }
302         if (isset($params['hash_func'])) {
303             if (!function_exists($params['hash_func'])) {
304                 $this->pushError('cannot find hash function with name [' . $params['hash_func'] . ']', CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
305                 return false;
306             }
307             $this->_hash_func = $params['hash_func'];
308         }
309         return true; // all ok
310     }
311
312     /**
313      * Ecnrypts $plain_data by the key $this->_enc_key or $key.
314      *
315      * @param string $plain_data  data, which must be encrypted
316      * @param object $key         encryption key (object of Crypt_RSA_Key class)
317      * @return mixed
318      *         encrypted data as string on success or false on error
319      *
320      * @access public
321      */
322     function encrypt($plain_data, $key = null)
323     {
324         $enc_data = $this->encryptBinary($plain_data, $key);
325         if ($enc_data !== false) {
326             return base64_encode($enc_data);
327         }
328         // error during encripting data
329         return false;
330     }
331
332     /**
333      * Ecnrypts $plain_data by the key $this->_enc_key or $key.
334      *
335      * @param string $plain_data  data, which must be encrypted
336      * @param object $key         encryption key (object of Crypt_RSA_Key class)
337      * @return mixed
338      *         encrypted data as binary string on success or false on error
339      *
340      * @access public
341      */
342     function encryptBinary($plain_data, $key = null)
343     {
344         if (is_null($key)) {
345             // use current encryption key
346             $key = $this->_enc_key;
347         }
348         else if (!Crypt_RSA_Key::isValid($key)) {
349             $this->pushError('invalid encryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
350             return false;
351         }
352
353         // append tail \x01 to plain data. It needs for correctly decrypting of data
354         $plain_data .= "\x01";
355
356         $plain_data = $this->_math_obj->bin2int($plain_data);
357         $exp = $this->_math_obj->bin2int($key->getExponent());
358         $modulus = $this->_math_obj->bin2int($key->getModulus());
359
360         // divide plain data into chunks
361         $data_len = $this->_math_obj->bitLen($plain_data);
362         $chunk_len = $key->getKeyLength() - 1;
363         $block_len = (int) ceil($chunk_len / 8);
364         $curr_pos = 0;
365         $enc_data = '';
366         while ($curr_pos < $data_len) {
367             $tmp = $this->_math_obj->subint($plain_data, $curr_pos, $chunk_len);
368             $enc_data .= str_pad(
369                 $this->_math_obj->int2bin($this->_math_obj->powmod($tmp, $exp, $modulus)),
370                 $block_len,
371                 "\0"
372             );
373             $curr_pos += $chunk_len;
374         }
375         return $enc_data;
376     }
377
378     /**
379      * Decrypts $enc_data by the key $this->_dec_key or $key.
380      *
381      * @param string $enc_data  encrypted data as string
382      * @param object $key       decryption key (object of RSA_Crypt_Key class)
383      * @return mixed
384      *         decrypted data as string on success or false on error
385      *
386      * @access public
387      */
388     function decrypt($enc_data, $key = null)
389     {
390         $enc_data = base64_decode($enc_data);
391         return $this->decryptBinary($enc_data, $key);
392     }
393
394     /**
395      * Decrypts $enc_data by the key $this->_dec_key or $key.
396      *
397      * @param string $enc_data  encrypted data as binary string
398      * @param object $key       decryption key (object of RSA_Crypt_Key class)
399      * @return mixed
400      *         decrypted data as string on success or false on error
401      *
402      * @access public
403      */
404     function decryptBinary($enc_data, $key = null)
405     {
406         if (is_null($key)) {
407             // use current decryption key
408             $key = $this->_dec_key;
409         }
410         else if (!Crypt_RSA_Key::isValid($key)) {
411             $this->pushError('invalid decryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
412             return false;
413         }
414
415         $exp = $this->_math_obj->bin2int($key->getExponent());
416         $modulus = $this->_math_obj->bin2int($key->getModulus());
417
418         $data_len = strlen($enc_data);
419         $chunk_len = $key->getKeyLength() - 1;
420         $block_len = (int) ceil($chunk_len / 8);
421         $curr_pos = 0;
422         $bit_pos = 0;
423         $plain_data = $this->_math_obj->bin2int("\0");
424         while ($curr_pos < $data_len) {
425             $tmp = $this->_math_obj->bin2int(substr($enc_data, $curr_pos, $block_len));
426             $tmp = $this->_math_obj->powmod($tmp, $exp, $modulus);
427             $plain_data = $this->_math_obj->bitOr($plain_data, $tmp, $bit_pos);
428             $bit_pos += $chunk_len;
429             $curr_pos += $block_len;
430         }
431         $result = $this->_math_obj->int2bin($plain_data);
432
433         // delete tail, containing of \x01
434         $tail = ord($result{strlen($result) - 1});
435         if ($tail != 1) {
436             $this->pushError("Error tail of decrypted text = {$tail}. Expected 1", CRYPT_RSA_ERROR_WRONG_TAIL);
437             return false;
438         }
439         return substr($result, 0, -1);
440     }
441
442     /**
443      * Creates sign for document $document, using $this->_private_key or $private_key
444      * as private key and $this->_hash_func or $hash_func as hash function.
445      *
446      * @param string $document     document, which must be signed
447      * @param object $private_key  private key (object of Crypt_RSA_Key type)
448      * @param string $hash_func    name of hash function, which will be used during signing
449      * @return mixed
450      *         signature of $document as string on success or false on error
451      *
452      * @access public
453      */
454     function createSign($document, $private_key = null, $hash_func = null)
455     {
456         // check private key
457         if (is_null($private_key)) {
458             $private_key = $this->_private_key;
459         }
460         else if (!Crypt_RSA_Key::isValid($private_key)) {
461             $this->pushError('invalid private key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
462             return false;
463         }
464         if ($private_key->getKeyType() != 'private') {
465             $this->pushError('signing key must be private', CRYPT_RSA_ERROR_NEED_PRV_KEY);
466             return false;
467         }
468
469         // check hash_func
470         if (is_null($hash_func)) {
471             $hash_func = $this->_hash_func;
472         }
473         if (!function_exists($hash_func)) {
474             $this->pushError("cannot find hash function with name [$hash_func]", CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
475             return false;
476         }
477
478         return $this->encrypt($hash_func($document), $private_key);
479     }
480
481     /**
482      * Validates $signature for document $document with public key $this->_public_key
483      * or $public_key and hash function $this->_hash_func or $hash_func.
484      *
485      * @param string $document    document, signature of which must be validated
486      * @param string $signature   signature, which must be validated
487      * @param object $public_key  public key (object of Crypt_RSA_Key class)
488      * @param string $hash_func   hash function, which will be used during validating signature
489      * @return mixed
490      *         true, if signature of document is valid
491      *         false, if signature of document is invalid
492      *         null on error
493      *
494      * @access public
495      */
496     function validateSign($document, $signature, $public_key = null, $hash_func = null)
497     {
498         // check public key
499         if (is_null($public_key)) {
500             $public_key = $this->_public_key;
501         }
502         else if (!Crypt_RSA_Key::isValid($public_key)) {
503             $this->pushError('invalid public key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
504             return null;
505         }
506         if ($public_key->getKeyType() != 'public') {
507             $this->pushError('validating key must be public', CRYPT_RSA_ERROR_NEED_PUB_KEY);
508             return null;
509         }
510
511         // check hash_func
512         if (is_null($hash_func)) {
513             $hash_func = $this->_hash_func;
514         }
515         if (!function_exists($hash_func)) {
516             $this->pushError("cannot find hash function with name [$hash_func]", CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
517             return null;
518         }
519
520         return $hash_func($document) == $this->decrypt($signature, $public_key);
521     }
522 }
523
524 ?>