Permalink
Browse files

Crypt_RSA 1.2.0b

git-svn-id: http://svn.php.net/repository/pear/packages/Crypt_RSA/trunk@232769 c90b9560-bf6c-de11-be94-00142212c4b1
  • Loading branch information...
1 parent 9e7045a commit c029790a80782c26808f4a66e36e57857d26ed62 @cweiske cweiske committed Mar 27, 2007
Showing with 666 additions and 293 deletions.
  1. +35 −44 RSA.php
  2. +10 −12 RSA/ErrorHandler.php
  3. +4 −6 RSA/Key.php
  4. +309 −105 RSA/KeyPair.php
  5. +93 −75 RSA/Math/BCMath.php
  6. +41 −4 RSA/Math/BigInt.php
  7. +42 −11 RSA/Math/GMP.php
  8. +12 −6 RSA/MathLoader.php
  9. +2 −0 docs/math_wrappers.txt
  10. +1 −1 examples/web_interface.php
  11. +29 −18 package.xml
  12. +62 −0 tests/bench.php
  13. +25 −10 tests/test1.php
  14. +1 −1 tests/test2.php
View
79 RSA.php
@@ -18,7 +18,7 @@
* @author Alexander Valyalkin <valyala@gmail.com>
* @copyright 2005, 2006 Alexander Valyalkin
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version 1.1.0
+ * @version 1.2.0b
* @link http://pear.php.net/package/Crypt_RSA
*/
@@ -189,7 +189,7 @@ function Crypt_RSA($params = null, $wrapper_name = 'default', $error_handler = '
$this->setErrorHandler($error_handler);
// try to load math wrapper
$obj = &Crypt_RSA_MathLoader::loadWrapper($wrapper_name);
- if (PEAR::isError($obj)) {
+ if ($this->isError($obj)) {
// error during loading of math wrapper
// Crypt_RSA object is partially constructed.
$this->pushError($obj);
@@ -251,61 +251,57 @@ function &factory($params = null, $wrapper_name = 'default', $error_handler = ''
function setParams($params)
{
if (!is_array($params)) {
- $obj = PEAR::raiseError('parameters must be passed to function as associative array', CRYPT_RSA_ERROR_WRONG_PARAMS);
- $this->pushError($obj);
+ $this->pushError('parameters must be passed to function as associative array', CRYPT_RSA_ERROR_WRONG_PARAMS);
return false;
}
if (isset($params['enc_key'])) {
if (Crypt_RSA_Key::isValid($params['enc_key'])) {
$this->_enc_key = $params['enc_key'];
- } else {
- $obj = PEAR::raiseError('wrong encryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
- $this->pushError($obj);
+ }
+ else {
+ $this->pushError('wrong encryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
return false;
}
}
if (isset($params['dec_key'])) {
if (Crypt_RSA_Key::isValid($params['dec_key'])) {
$this->_dec_key = $params['dec_key'];
- } else {
- $obj = PEAR::raiseError('wrong decryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
- $this->pushError($obj);
+ }
+ else {
+ $this->pushError('wrong decryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
return false;
}
}
if (isset($params['private_key'])) {
if (Crypt_RSA_Key::isValid($params['private_key'])) {
if ($params['private_key']->getKeyType() != 'private') {
- $obj = PEAR::raiseError('private key must have "private" attribute', CRYPT_RSA_ERROR_WRONG_KEY_TYPE);
- $this->pushError($obj);
+ $this->pushError('private key must have "private" attribute', CRYPT_RSA_ERROR_WRONG_KEY_TYPE);
return false;
}
$this->_private_key = $params['private_key'];
- } else {
- $obj = PEAR::raiseError('wrong private key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
- $this->pushError($obj);
+ }
+ else {
+ $this->pushError('wrong private key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
return false;
}
}
if (isset($params['public_key'])) {
if (Crypt_RSA_Key::isValid($params['public_key'])) {
if ($params['public_key']->getKeyType() != 'public') {
- $obj = PEAR::raiseError('public key must have "public" attribute', CRYPT_RSA_ERROR_WRONG_KEY_TYPE);
- $this->pushError($obj);
+ $this->pushError('public key must have "public" attribute', CRYPT_RSA_ERROR_WRONG_KEY_TYPE);
return false;
}
$this->_public_key = $params['public_key'];
- } else {
- $obj = PEAR::raiseError('wrong public key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
- $this->pushError($obj);
+ }
+ else {
+ $this->pushError('wrong public key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
return false;
}
}
if (isset($params['hash_func'])) {
if (!function_exists($params['hash_func'])) {
- $obj = PEAR::raiseError('cannot find hash function with name [' . $params['hash_func'] . ']', CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
- $this->pushError($obj);
+ $this->pushError('cannot find hash function with name [' . $params['hash_func'] . ']', CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
return false;
}
$this->_hash_func = $params['hash_func'];
@@ -348,9 +344,9 @@ function encryptBinary($plain_data, $key = null)
if (is_null($key)) {
// use current encryption key
$key = $this->_enc_key;
- } elseif (!Crypt_RSA_Key::isValid($key)) {
- $obj = PEAR::raiseError('invalid encryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
- $this->pushError($obj);
+ }
+ else if (!Crypt_RSA_Key::isValid($key)) {
+ $this->pushError('invalid encryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
return false;
}
@@ -410,9 +406,9 @@ function decryptBinary($enc_data, $key = null)
if (is_null($key)) {
// use current decryption key
$key = $this->_dec_key;
- } elseif (!Crypt_RSA_Key::isValid($key)) {
- $obj = PEAR::raiseError('invalid decryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
- $this->pushError($obj);
+ }
+ else if (!Crypt_RSA_Key::isValid($key)) {
+ $this->pushError('invalid decryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
return false;
}
@@ -437,8 +433,7 @@ function decryptBinary($enc_data, $key = null)
// delete tail, containing of \x01
$tail = ord($result{strlen($result) - 1});
if ($tail != 1) {
- $obj = PEAR::raiseError("Error tail of decrypted text = {$tail}. Expected 1", CRYPT_RSA_ERROR_WRONG_TAIL);
- $this->pushError($obj);
+ $this->pushError("Error tail of decrypted text = {$tail}. Expected 1", CRYPT_RSA_ERROR_WRONG_TAIL);
return false;
}
return substr($result, 0, -1);
@@ -461,14 +456,13 @@ function createSign($document, $private_key = null, $hash_func = null)
// check private key
if (is_null($private_key)) {
$private_key = $this->_private_key;
- } elseif (!Crypt_RSA_Key::isValid($private_key)) {
- $obj = PEAR::raiseError('invalid private key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
- $this->pushError($obj);
+ }
+ else if (!Crypt_RSA_Key::isValid($private_key)) {
+ $this->pushError('invalid private key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
return false;
}
if ($private_key->getKeyType() != 'private') {
- $obj = PEAR::raiseError('signing key must be private', CRYPT_RSA_ERROR_NEED_PRV_KEY);
- $this->pushError($obj);
+ $this->pushError('signing key must be private', CRYPT_RSA_ERROR_NEED_PRV_KEY);
return false;
}
@@ -477,8 +471,7 @@ function createSign($document, $private_key = null, $hash_func = null)
$hash_func = $this->_hash_func;
}
if (!function_exists($hash_func)) {
- $obj = PEAR::raiseError('cannot find hash function with name [' . $hash_func . ']', CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
- $this->pushError($obj);
+ $this->pushError("cannot find hash function with name [$hash_func]", CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
return false;
}
@@ -505,14 +498,13 @@ function validateSign($document, $signature, $public_key = null, $hash_func = nu
// check public key
if (is_null($public_key)) {
$public_key = $this->_public_key;
- } elseif (!Crypt_RSA_Key::isValid($public_key)) {
- $obj = PEAR::raiseError('invalid public key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
- $this->pushError($obj);
+ }
+ else if (!Crypt_RSA_Key::isValid($public_key)) {
+ $this->pushError('invalid public key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
return null;
}
if ($public_key->getKeyType() != 'public') {
- $obj = PEAR::raiseError('validating key must be public', CRYPT_RSA_ERROR_NEED_PUB_KEY);
- $this->pushError($obj);
+ $this->pushError('validating key must be public', CRYPT_RSA_ERROR_NEED_PUB_KEY);
return null;
}
@@ -521,8 +513,7 @@ function validateSign($document, $signature, $public_key = null, $hash_func = nu
$hash_func = $this->_hash_func;
}
if (!function_exists($hash_func)) {
- $obj = PEAR::raiseError('cannot find hash function with name [' . $hash_func . ']', CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
- $this->pushError($obj);
+ $this->pushError("cannot find hash function with name [$hash_func]", CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
return null;
}
View
22 RSA/ErrorHandler.php
@@ -18,7 +18,7 @@
* @author Alexander Valyalkin <valyala@gmail.com>
* @copyright 2005, 2006 Alexander Valyalkin
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version 1.1.0
+ * @version 1.2.0b
* @link http://pear.php.net/package/Crypt_RSA
*/
@@ -106,7 +106,7 @@
* - isError() - returns true, if list contains errors, else returns false
* - getErrorList() - returns error list
* - getLastError() - returns last error from error list or false, if list is empty
- * - pushError($error) - pushes $error into the error list
+ * - pushError($errstr) - pushes $errstr into the error list
* - setErrorHandler($new_error_handler) - sets error handler function
* - getErrorHandler() - returns name of error handler function
*
@@ -140,12 +140,13 @@ class Crypt_RSA_ErrorHandler
/**
* Returns true if list of errors is not empty, else returns false
*
- * @return bool true, if list of errors is not empty, else false
+ * @param object
+ * @return bool true, if list of errors is not empty or $err is PEAR_Error object, else false
* @access public
*/
- function isError()
+ function isError($err = null)
{
- return sizeof($this->_errors) > 0;
+ return is_null($err) ? (sizeof($this->_errors) > 0) : PEAR::isError($err);
}
/**
@@ -177,17 +178,14 @@ function getLastError()
/**
* pushes error object $error to the error list
*
- * @param object $error error object of PEAR_Error class
+ * @param string $errstr error string
+ * @param int $errno error number
* @return bool true on success, false on error
* @access public
*/
- function pushError($error)
+ function pushError($errstr, $errno = 0)
{
- if (!PEAR::isError($error)) {
- // $error must be a PEAR_Error object
- return false;
- }
- $this->_errors[] = $error;
+ $this->_errors[] = PEAR::raiseError($errstr, $errno);
if ($this->_error_handler != '') {
// call user defined error handler
View
10 RSA/Key.php
@@ -18,7 +18,7 @@
* @author Alexander Valyalkin <valyala@gmail.com>
* @copyright 2005, 2006 Alexander Valyalkin
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version 1.1.0
+ * @version 1.2.0b
* @link http://pear.php.net/package/Crypt_RSA
*/
@@ -156,7 +156,7 @@ function Crypt_RSA_Key($modulus, $exp, $key_type, $wrapper_name = 'default', $er
$this->setErrorHandler($error_handler);
// try to load math wrapper $wrapper_name
$obj = &Crypt_RSA_MathLoader::loadWrapper($wrapper_name);
- if (PEAR::isError($obj)) {
+ if ($this->isError($obj)) {
// error during loading of math wrapper
$this->pushError($obj); // push error object into error list
return;
@@ -167,8 +167,7 @@ function Crypt_RSA_Key($modulus, $exp, $key_type, $wrapper_name = 'default', $er
$this->_exp = $exp;
if (!in_array($key_type, array('private', 'public'))) {
- $obj = PEAR::raiseError('invalid key type. It must be private or public', CRYPT_RSA_ERROR_WRONG_KEY_TYPE);
- $this->pushError($obj); // push error object into error list
+ $this->pushError('invalid key type. It must be private or public', CRYPT_RSA_ERROR_WRONG_KEY_TYPE);
return;
}
$this->_key_type = $key_type;
@@ -178,8 +177,7 @@ function Crypt_RSA_Key($modulus, $exp, $key_type, $wrapper_name = 'default', $er
$exp_num = $this->_math_obj->bin2int($this->_exp);
if ($this->_math_obj->cmpAbs($mod_num, $exp_num) <= 0) {
- $obj = PEAR::raiseError('modulus must be greater than exponent', CRYPT_RSA_ERROR_EXP_GE_MOD);
- $this->pushError($obj); // push error object into error list
+ $this->pushError('modulus must be greater than exponent', CRYPT_RSA_ERROR_EXP_GE_MOD);
return;
}
// determine key length
View
414 RSA/KeyPair.php
@@ -18,7 +18,7 @@
* @author Alexander Valyalkin <valyala@gmail.com>
* @copyright 2005, 2006 Alexander Valyalkin
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version 1.1.0
+ * @version 1.2.0b
* @link http://pear.php.net/package/Crypt_RSA
*/
@@ -46,7 +46,9 @@
* - getPrivateKey() - returns private key
* - getKeyLength() - returns bit key length
* - setRandomGenerator($func_name) - sets random generator to $func_name
- * - fromPEMString($str) - retrieves key pair from PEM-encoded string
+ * - fromPEMString($str) - retrieves keypair from PEM-encoded string
+ * - toPEMString() - stores keypair to PEM-encoded string
+ * - isEqual($keypair2) - compares current keypair to $keypair2
*
* Example usage:
* // create new 1024-bit key pair
@@ -80,7 +82,7 @@
*
* // set random generator to $func_name, where $func_name
* // consists name of random generator function. See comments
- * // befor setRandomGenerator() method for details
+ * // before setRandomGenerator() method for details
* $key_pair->setRandomGenerator($func_name);
*
* // error check
@@ -106,6 +108,19 @@
* $str = file_get_contents('private.pem');
* $keypair = Crypt_RSA_KeyPair::fromPEMString($str);
*
+ * // generate and write 1024-bit key pair to .pem file 'private_new.pem'
+ * $keypair = new Crypt_RSA_KeyPair(1024);
+ * $str = $keypair->toPEMString();
+ * file_put_contents('private_new.pem', $str);
+ *
+ * // compare $keypair1 to $keypair2
+ * if ($keypair1->isEqual($keypair2)) {
+ * echo "keypair1 = keypair2\n";
+ * }
+ * else {
+ * echo "keypair1 != keypair2\n";
+ * }
+ *
* @category Encryption
* @package Crypt_RSA
* @author Alexander Valyalkin <valyala@gmail.com>
@@ -159,23 +174,40 @@ class Crypt_RSA_KeyPair extends Crypt_RSA_ErrorHandler
var $_random_generator;
/**
- * Parse ASN.1 string [$str] starting form position [$pos].
+ * RSA keypair attributes [version, n, e, d, p, q, dmp1, dmq1, iqmp] as associative array
+ *
+ * @var array
+ * @access private
+ */
+ var $_attrs;
+
+ /**
+ * Returns names of keypair attributes from $this->_attrs array
+ *
+ * @return array Array of keypair attributes names
+ * @access private
+ */
+ function _get_attr_names() {
+ return array('version', 'n', 'e', 'd', 'p', 'q', 'dmp1', 'dmq1', 'iqmp');
+ }
+
+ /**
+ * Parses ASN.1 string [$str] starting form position [$pos].
* Returns tag and string value of parsed object.
*
* @param string $str
* @param int $pos
- * @param object $err_handler
+ * @param Crypt_RSA_ErrorHandler $err_handler
*
- * @return mixed Array('tag' => ..., 'str' => ...) on success, PEAR_Error object on error
+ * @return mixed Array('tag' => ..., 'str' => ...) on success, false on error
* @access private
*/
- function _ASN1Parse($str, &$pos, $err_handler)
+ function _ASN1Parse($str, &$pos, &$err_handler)
{
$max_pos = strlen($str);
if ($max_pos < 2) {
- $err = PEAR::raiseError("ASN.1 string too short");
- $err_handler->pushError($err);
- return $err;
+ $err_handler->pushError("ASN.1 string too short");
+ return false;
}
// get ASN.1 tag value
@@ -189,9 +221,8 @@ function _ASN1Parse($str, &$pos, $err_handler)
} while (($n & 0x80) && $pos < $max_pos);
}
if ($pos >= $max_pos) {
- $err = PEAR::raiseError("ASN.1 string too short");
- $err_handler->pushError($err);
- return $err;
+ $err_handler->pushError("ASN.1 string too short");
+ return false;
}
// get ASN.1 object length
@@ -205,9 +236,8 @@ function _ASN1Parse($str, &$pos, $err_handler)
}
}
if ($pos >= $max_pos || $len > $max_pos - $pos) {
- $err = PEAR::raiseError("ASN.1 string too short");
- $err_handler->pushError($err);
- return $err;
+ $err_handler->pushError("ASN.1 string too short");
+ return false;
}
// get string value of ASN.1 object
@@ -220,35 +250,99 @@ function _ASN1Parse($str, &$pos, $err_handler)
}
/**
- * Parse ASN.1 sting [$str] starting from position [$pos].
+ * Parses ASN.1 sting [$str] starting from position [$pos].
* Returns string representation of number, which can be passed
* in bin2int() function of math wrapper.
*
* @param string $str
* @param int $pos
- * @param object $err_handler
+ * @param object Crypt_RSA_ErrorHandler $err_handler
*
- * @return mixed string representation of parsed number on success, PEAR_Error object on error
+ * @return mixed string representation of parsed number on success, false on error
* @access private
*/
- function _ASN1ParseInt($str, &$pos, $err_handler)
+ function _ASN1ParseInt($str, &$pos, &$err_handler)
{
$tmp = Crypt_RSA_KeyPair::_ASN1Parse($str, $pos, $err_handler);
- if (PEAR::isError($tmp)) {
- return $tmp;
+ if ($err_handler->isError()) {
+ return false;
}
if ($tmp['tag'] != 0x02) {
$errstr = sprintf("wrong ASN tag value: 0x%02x. Expected 0x02 (INTEGER)", $tmp['tag']);
- $err = PEAR::raiseError($errstr);
- $err_handler->pushError($err);
- return $err;
+ $err_handler->pushError($errstr);
+ return false;
}
$pos += strlen($tmp['str']);
return strrev($tmp['str']);
}
/**
+ * Constructs ASN.1 string from tag $tag and object $str
+ *
+ * @param string $str ASN.1 object string
+ * @param int $tag ASN.1 tag value
+ * @param bool $is_constructed
+ * @param bool $is_private
+ *
+ * @return ASN.1-encoded string
+ * @access private
+ */
+ function _ASN1Store($str, $tag, $is_constructed = false, $is_private = false)
+ {
+ $out = '';
+
+ // encode ASN.1 tag value
+ $tag_ext = ($is_constructed ? 0x20 : 0) | ($is_private ? 0xc0 : 0);
+ if ($tag < 0x1f) {
+ $out .= chr($tag | $tag_ext) ;
+ }
+ else {
+ $out .= chr($tag_ext | 0x1f);
+ $tmp = chr($tag & 0x7f);
+ $tag >>= 7;
+ while ($tag) {
+ $tmp .= chr(($tag & 0x7f) | 0x80);
+ $tag >>= 7;
+ }
+ $out .= strrev($tmp);
+ }
+
+ // encode ASN.1 object length
+ $len = strlen($str);
+ if ($len < 0x7f) {
+ $out .= chr($len);
+ }
+ else {
+ $tmp = '';
+ $n = 0;
+ while ($len) {
+ $tmp .= chr($len & 0xff);
+ $len >>= 8;
+ $n++;
+ }
+ $out .= chr($n | 0x80);
+ $out .= strrev($tmp);
+ }
+
+ return $out . $str;
+ }
+
+ /**
+ * Constructs ASN.1 string from binary representation of big integer
+ *
+ * @param string $str binary representation of big integer
+ *
+ * @return ASN.1-encoded string
+ * @access private
+ */
+ function _ASN1StoreInt($str)
+ {
+ $str = strrev($str);
+ return Crypt_RSA_KeyPair::_ASN1Store($str, 0x02);
+ }
+
+ /**
* Crypt_RSA_KeyPair constructor.
*
* @param int $key_len bit length of key pair, which will be generated in constructor
@@ -258,54 +352,103 @@ function _ASN1ParseInt($str, &$pos, $err_handler)
* See contents of Crypt/RSA/Math folder for examples of wrappers.
* Read docs/Crypt_RSA/docs/math_wrappers.txt for details.
* @param string $error_handler name of error handler function
+ * @param callback $random_generator function which will be used as random generator
*
* @access public
*/
- function Crypt_RSA_KeyPair($key_len, $wrapper_name = 'default', $error_handler = '')
+ function Crypt_RSA_KeyPair($key_len, $wrapper_name = 'default', $error_handler = '', $random_generator = null)
{
// set error handler
$this->setErrorHandler($error_handler);
// try to load math wrapper
$obj = &Crypt_RSA_MathLoader::loadWrapper($wrapper_name);
- if (PEAR::isError($obj)) {
+ if ($this->isError($obj)) {
// error during loading of math wrapper
$this->pushError($obj);
return;
}
$this->_math_obj = &$obj;
- // set default random generator
- if (!$this->setRandomGenerator()) {
+ // set random generator
+ if (!$this->setRandomGenerator($random_generator)) {
// error in setRandomGenerator() function
return;
}
if (is_array($key_len)) {
- // ugly BC hack - it is possible to pass array of [n, e, d] instead of key length
- list($n, $e, $d) = $key_len;
-
- // check 2^(e*d) = 2 (mod n)
- $a_int = $this->_math_obj->bin2int("\x02");
- $n_int = $this->_math_obj->bin2int($n);
- $e_int = $this->_math_obj->bin2int($e);
- $d_int = $this->_math_obj->bin2int($d);
- $b_int = $this->_math_obj->powMod($a_int, $e_int, $n_int);
- $b_int = $this->_math_obj->powMod($b_int, $d_int, $n_int);
- if ($this->_math_obj->cmpAbs($a_int, $b_int)) {
- $this->pushError(PEAR::raiseError("Incorrect [n, e, d] numbers"));
+ // ugly BC hack - it is possible to pass RSA private key attributes [version, n, e, d, p, q, dmp1, dmq1, iqmp]
+ // as associative array instead of key length to Crypt_RSA_KeyPair constructor
+ $rsa_attrs = $key_len;
+
+ // convert attributes to big integers
+ $attr_names = $this->_get_attr_names();
+ foreach ($attr_names as $attr) {
+ if (!isset($rsa_attrs[$attr])) {
+ $this->pushError("missing required RSA attribute [$attr]");
+ return;
+ }
+ ${$attr} = $this->_math_obj->bin2int($rsa_attrs[$attr]);
+ }
+
+ // check primality of p and q
+ if (!$this->_math_obj->isPrime($p)) {
+ $this->pushError("[p] must be prime");
+ return;
+ }
+ if (!$this->_math_obj->isPrime($q)) {
+ $this->pushError("[q] must be prime");
+ return;
+ }
+
+ // check n = p * q
+ $n1 = $this->_math_obj->mul($p, $q);
+ if ($this->_math_obj->cmpAbs($n, $n1)) {
+ $this->pushError("n != p * q");
+ return;
+ }
+
+ // check e * d = 1 mod (p-1) * (q-1)
+ $p1 = $this->_math_obj->dec($p);
+ $q1 = $this->_math_obj->dec($q);
+ $p1q1 = $this->_math_obj->mul($p1, $q1);
+ $ed = $this->_math_obj->mul($e, $d);
+ $one = $this->_math_obj->mod($ed, $p1q1);
+ if (!$this->_math_obj->isOne($one)) {
+ $this->pushError("e * d != 1 mod (p-1)*(q-1)");
+ return;
+ }
+
+ // check dmp1 = d mod (p-1)
+ $dmp = $this->_math_obj->mod($d, $p1);
+ if ($this->_math_obj->cmpAbs($dmp, $dmp1)) {
+ $this->pushError("dmp1 != d mod (p-1)");
+ return;
+ }
+
+ // check dmq1 = d mod (q-1)
+ $dmq = $this->_math_obj->mod($d, $q1);
+ if ($this->_math_obj->cmpAbs($dmq, $dmq1)) {
+ $this->pushError("dmq1 != d mod (q-1)");
+ return;
+ }
+
+ // check iqmp = 1/q mod p
+ $q1 = $this->_math_obj->invmod($iqmp, $p);
+ if ($this->_math_obj->cmpAbs($q, $q1)) {
+ $this->pushError("iqmp != 1/q mod p");
return;
}
// try to create public key object
- $public_key = &new Crypt_RSA_Key($n, $e, 'public');
+ $public_key = &new Crypt_RSA_Key($rsa_attrs['n'], $rsa_attrs['e'], 'public', $wrapper_name, $error_handler);
if ($public_key->isError()) {
// error during creating public object
$this->pushError($public_key->getLastError());
return;
}
// try to create private key object
- $private_key = &new Crypt_RSA_Key($n, $d, 'private');
+ $private_key = &new Crypt_RSA_Key($rsa_attrs['n'], $rsa_attrs['d'], 'private', $wrapper_name, $error_handler);
if ($private_key->isError()) {
// error during creating private key object
$this->pushError($private_key->getLastError());
@@ -315,6 +458,7 @@ function Crypt_RSA_KeyPair($key_len, $wrapper_name = 'default', $error_handler =
$this->_public_key = $public_key;
$this->_private_key = $private_key;
$this->_key_len = $public_key->getKeyLength();
+ $this->_attrs = $rsa_attrs;
}
else {
// generate key pair
@@ -335,13 +479,14 @@ function Crypt_RSA_KeyPair($key_len, $wrapper_name = 'default', $error_handler =
* See contents of Crypt/RSA/Math folder for examples of wrappers.
* Read docs/Crypt_RSA/docs/math_wrappers.txt for details.
* @param string $error_handler name of error handler function
+ * @param callback $random_generator function which will be used as random generator
*
* @return object new Crypt_RSA_KeyPair object on success or PEAR_Error object on failure
* @access public
*/
- function &factory($key_len, $wrapper_name = 'default', $error_handler = '')
+ function &factory($key_len, $wrapper_name = 'default', $error_handler = '', $random_generator = null)
{
- $obj = &new Crypt_RSA_KeyPair($key_len, $wrapper_name, $error_handler);
+ $obj = &new Crypt_RSA_KeyPair($key_len, $wrapper_name, $error_handler, $random_generator);
if ($obj->isError()) {
// error during creating a new object. Retrurn PEAR_Error object
return $obj->getLastError();
@@ -364,14 +509,14 @@ function generate($key_len = null)
// use an old key length
$key_len = $this->_key_len;
if (is_null($key_len)) {
- $obj = PEAR::raiseError('missing key_len parameter', CRYPT_RSA_ERROR_MISSING_KEY_LEN);
- $this->pushError($obj);
+ $this->pushError('missing key_len parameter', CRYPT_RSA_ERROR_MISSING_KEY_LEN);
return false;
}
}
- // align $key_len to 8 bits
- if ($key_len & 7) {
- $key_len += 8 - ($key_len % 8);
+
+ // minimal key length is 8 bit ;)
+ if ($key_len < 8) {
+ $key_len = 8;
}
// store key length in the _key_len property
$this->_key_len = $key_len;
@@ -404,6 +549,9 @@ function generate($key_len = null)
$tmp = $p;
$p = $q;
$q = $tmp;
+ $tmp = $p1;
+ $p1 = $q1;
+ $q1 = $tmp;
}
// calculate n = p * q
$n = $this->_math_obj->mul($p, $q);
@@ -413,19 +561,34 @@ function generate($key_len = null)
$pq = $this->_math_obj->mul($p1, $q1);
$d = $this->_math_obj->invmod($e, $pq);
- // convert [n], [e] and [d] into binary representation
- $modulus = $this->_math_obj->int2bin($n);
- $public_exp = $this->_math_obj->int2bin($e);
- $private_exp = $this->_math_obj->int2bin($d);
+ // calculate dmp1 = d mod (p - 1)
+ $dmp1 = $this->_math_obj->mod($d, $p1);
+
+ // calculate dmq1 = d mod (q - 1)
+ $dmq1 = $this->_math_obj->mod($d, $q1);
+
+ // calculate iqmp = 1/q mod p
+ $iqmp = $this->_math_obj->invmod($q, $p);
+
+ // store RSA keypair attributes
+ $this->_attrs = array(
+ 'version' => "\x00",
+ 'n' => $this->_math_obj->int2bin($n),
+ 'e' => $this->_math_obj->int2bin($e),
+ 'd' => $this->_math_obj->int2bin($d),
+ 'p' => $this->_math_obj->int2bin($p),
+ 'q' => $this->_math_obj->int2bin($q),
+ 'dmp1' => $this->_math_obj->int2bin($dmp1),
+ 'dmq1' => $this->_math_obj->int2bin($dmq1),
+ 'iqmp' => $this->_math_obj->int2bin($iqmp),
+ );
+
+ $n = $this->_attrs['n'];
+ $e = $this->_attrs['e'];
+ $d = $this->_attrs['d'];
// try to create public key object
- $obj = &new Crypt_RSA_Key(
- $modulus,
- $public_exp,
- 'public',
- $this->_math_obj->getWrapperName(),
- $this->_error_handler
- );
+ $obj = &new Crypt_RSA_Key($n, $e, 'public', $this->_math_obj->getWrapperName(), $this->_error_handler);
if ($obj->isError()) {
// error during creating public object
$this->pushError($obj->getLastError());
@@ -434,13 +597,7 @@ function generate($key_len = null)
$this->_public_key = &$obj;
// try to create private key object
- $obj = &new Crypt_RSA_Key(
- $modulus,
- $private_exp,
- 'private',
- $this->_math_obj->getWrapperName(),
- $this->_error_handler
- );
+ $obj = &new Crypt_RSA_Key($n, $d, 'private', $this->_math_obj->getWrapperName(), $this->_error_handler);
if ($obj->isError()) {
// error during creating private key object
$this->pushError($obj->getLastError());
@@ -491,12 +648,12 @@ function setRandomGenerator($random_generator = null)
if (is_string($random_generator)) {
// set user's random generator
if (!function_exists($random_generator)) {
- $obj = PEAR::raiseError("can't find random generator function with name [{$random_generator}]");
- $this->pushError($obj);
+ $this->pushError("can't find random generator function with name [{$random_generator}]");
return false;
}
$this->_random_generator = $random_generator;
- } else {
+ }
+ else {
// set default random generator
$this->_random_generator = is_null($default_random_generator) ?
($default_random_generator = create_function('', '$a=explode(" ",microtime());return(int)($a[0]*1000000);')) :
@@ -517,7 +674,7 @@ function getKeyLength()
}
/**
- * Retrieve RSA keypair from PEM-encoded string, containing RSA private key.
+ * Retrieves RSA keypair from PEM-encoded string, containing RSA private key.
* Example of such string:
* -----BEGIN RSA PRIVATE KEY-----
* MCsCAQACBHtvbSECAwEAAQIEeYrk3QIDAOF3AgMAjCcCAmdnAgJMawIDALEk
@@ -536,61 +693,108 @@ function getKeyLength()
*/
function &fromPEMString($str, $wrapper_name = 'default', $error_handler = '')
{
- // search for base64-encoded private key
+ if (isset($this)) {
+ if ($wrapper_name == 'default') {
+ $wrapper_name = $this->_math_obj->getWrapperName();
+ }
+ if ($error_handler == '') {
+ $error_handler = $this->_error_handler;
+ }
+ }
$err_handler = &new Crypt_RSA_ErrorHandler;
$err_handler->setErrorHandler($error_handler);
- if (!preg_match('/-----BEGIN RSA PRIVATE KEY-----[\\r\\n]+([^-]+)-----END RSA PRIVATE KEY-----/', $str, $matches)) {
- $err = PEAR::raiseError("can't find RSA private key in the string [{$str}]");
- $err_handler->pushError($err);
- return $err;
+ // search for base64-encoded private key
+ if (!preg_match('/-----BEGIN RSA PRIVATE KEY-----([^-]+)-----END RSA PRIVATE KEY-----/', $str, $matches)) {
+ $err_handler->pushError("can't find RSA private key in the string [{$str}]");
+ return $err_handler->getLastError();
}
// parse private key. It is ASN.1-encoded
$str = base64_decode($matches[1]);
$pos = 0;
$tmp = Crypt_RSA_KeyPair::_ASN1Parse($str, $pos, $err_handler);
- if (PEAR::isError($tmp)) {
- return $tmp;
+ if ($err_handler->isError()) {
+ return $err_handler->getLastError();
}
if ($tmp['tag'] != 0x10) {
$errstr = sprintf("wrong ASN tag value: 0x%02x. Expected 0x10 (SEQUENCE)", $tmp['tag']);
- $err = PEAR::raiseError($errstr);
- $err_handler->pushError($err);
- return $err;
+ $err_handler->pushError($errstr);
+ return $err_handler->getLastError();
}
- // skip [version] field
- $tmp = Crypt_RSA_KeyPair::_ASN1ParseInt($str, $pos, $err_handler);
- if (PEAR::isError($tmp)) {
- return $tmp;
+ // parse ASN.1 SEQUENCE for RSA private key
+ $attr_names = Crypt_RSA_KeyPair::_get_attr_names();
+ $n = sizeof($attr_names);
+ $rsa_attrs = array();
+ for ($i = 0; $i < $n; $i++) {
+ $tmp = Crypt_RSA_KeyPair::_ASN1ParseInt($str, $pos, $err_handler);
+ if ($err_handler->isError()) {
+ return $err_handler->getLastError();
+ }
+ $attr = $attr_names[$i];
+ $rsa_attrs[$attr] = $tmp;
}
- // get [n]
- $n = Crypt_RSA_KeyPair::_ASN1ParseInt($str, $pos, $err_handler);
- if (PEAR::isError($n)) {
- return $n;
+ // create Crypt_RSA_KeyPair object.
+ $keypair = &new Crypt_RSA_KeyPair($rsa_attrs, $wrapper_name, $error_handler);
+ if ($keypair->isError()) {
+ return $keypair->getLastError();
}
- // get [e]
- $e = Crypt_RSA_KeyPair::_ASN1ParseInt($str, $pos, $err_handler);
- if (PEAR::isError($e)) {
- return $e;
- }
+ return $keypair;
+ }
- // get [d]
- $d = Crypt_RSA_KeyPair::_ASN1ParseInt($str, $pos, $err_handler);
- if (PEAR::isError($d)) {
- return $d;
+ /**
+ * converts keypair to PEM-encoded string, which can be stroed in
+ * .pem compatible files, contianing RSA private key.
+ *
+ * @return string PEM-encoded keypair on success, false on error
+ * @access public
+ */
+ function toPEMString()
+ {
+ // store RSA private key attributes into ASN.1 string
+ $str = '';
+ $attr_names = $this->_get_attr_names();
+ $n = sizeof($attr_names);
+ $rsa_attrs = $this->_attrs;
+ for ($i = 0; $i < $n; $i++) {
+ $attr = $attr_names[$i];
+ if (!isset($rsa_attrs[$attr])) {
+ $this->pushError("Cannot find value for ASN.1 attribute [$attr]");
+ return false;
+ }
+ $tmp = $rsa_attrs[$attr];
+ $str .= Crypt_RSA_KeyPair::_ASN1StoreInt($tmp);
}
- // create Crypt_RSA_KeyPair object.
- $obj = &new Crypt_RSA_KeyPair(array($n, $e, $d), $wrapper_name, $error_handler);
- if ($obj->isError()) {
- return $obj->getLastError();
- }
+ // prepend $str by ASN.1 SEQUENCE (0x10) header
+ $str = Crypt_RSA_KeyPair::_ASN1Store($str, 0x10, true);
- return $obj;
+ // encode and format PEM string
+ $str = base64_encode($str);
+ $str = chunk_split($str, 64, "\n");
+ return "-----BEGIN RSA PRIVATE KEY-----\n$str-----END RSA PRIVATE KEY-----\n";
+ }
+
+ /**
+ * Compares keypairs in Crypt_RSA_KeyPair objects $this and $key_pair
+ *
+ * @param Crypt_RSA_KeyPair object $key_pair keypair to compare
+ *
+ * @return bool true, if keypair stored in $this equal to keypair stored in $key_pair
+ * @access public
+ */
+ function isEqual($key_pair)
+ {
+ $attr_names = $this->_get_attr_names();
+ foreach ($attr_names as $attr) {
+ if ($this->_attrs[$attr] != $key_pair->_attrs[$attr]) {
+ return false;
+ }
+ }
+ return true;
}
}
View
168 RSA/Math/BCMath.php
@@ -18,7 +18,7 @@
* @author Alexander Valyalkin <valyala@gmail.com>
* @copyright 2006 Alexander Valyalkin
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version 1.1.0
+ * @version 1.2.0b
* @link http://pear.php.net/package/Crypt_RSA
*/
@@ -49,69 +49,6 @@ class Crypt_RSA_Math_BCMath
var $errstr = '';
/**
- * Tests $num on primality. Returns true, if $num is strong pseudoprime.
- * Else returns false.
- *
- * @param string $num
- * @return bool
- * @access private
- */
- function _isPrime($num)
- {
- static $primes = null;
- static $primes_cnt = 0;
- if (is_null($primes)) {
- // generate all primes up to 10000
- $primes = array();
- for ($i = 0; $i < 10000; $i++) {
- $primes[] = $i;
- }
- $primes[0] = $primes[1] = 0;
- for ($i = 2; $i < 100; $i++) {
- while (!$primes[$i]) {
- $i++;
- }
- $j = $i;
- for ($j += $i; $j < 10000; $j += $i) {
- $primes[$j] = 0;
- }
- }
- $j = 0;
- for ($i = 0; $i < 10000; $i++) {
- if ($primes[$i]) {
- $primes[$j++] = $primes[$i];
- }
- }
- $primes_cnt = $j;
- }
-
- // try to divide number by small primes
- for ($i = 0; $i < $primes_cnt; $i++) {
- if (bccomp($num, $primes[$i]) <= 0) {
- // number is prime
- return true;
- }
- if (!bccomp(bcmod($num, $primes[$i]), '0')) {
- // number divides by $primes[$i]
- return false;
- }
- }
-
- /*
- try Miller-Rabin's probable-primality test for first
- 7 primes as bases
- */
- for ($i = 0; $i < 7; $i++) {
- if (!$this->_millerTest($num, $primes[$i])) {
- // $num is composite
- return false;
- }
- }
- // $num is strong pseudoprime
- return true;
- }
-
- /**
* Performs Miller-Rabin primality test for number $num
* with base $base. Returns true, if $num is strong pseudoprime
* by base $base. Else returns false.
@@ -254,6 +191,19 @@ function mul($num1, $num2)
}
/**
+ * Calculates $num1 % $num2
+ *
+ * @param string $num1
+ * @param string $num2
+ * @return string
+ * @access public
+ */
+ function mod($num1, $num2)
+ {
+ return bcmod($num1, $num2);
+ }
+
+ /**
* Compares abs($num1) to abs($num2).
* Returns:
* -1, if abs($num1) < abs($num2)
@@ -271,6 +221,69 @@ function cmpAbs($num1, $num2)
}
/**
+ * Tests $num on primality. Returns true, if $num is strong pseudoprime.
+ * Else returns false.
+ *
+ * @param string $num
+ * @return bool
+ * @access private
+ */
+ function isPrime($num)
+ {
+ static $primes = null;
+ static $primes_cnt = 0;
+ if (is_null($primes)) {
+ // generate all primes up to 10000
+ $primes = array();
+ for ($i = 0; $i < 10000; $i++) {
+ $primes[] = $i;
+ }
+ $primes[0] = $primes[1] = 0;
+ for ($i = 2; $i < 100; $i++) {
+ while (!$primes[$i]) {
+ $i++;
+ }
+ $j = $i;
+ for ($j += $i; $j < 10000; $j += $i) {
+ $primes[$j] = 0;
+ }
+ }
+ $j = 0;
+ for ($i = 0; $i < 10000; $i++) {
+ if ($primes[$i]) {
+ $primes[$j++] = $primes[$i];
+ }
+ }
+ $primes_cnt = $j;
+ }
+
+ // try to divide number by small primes
+ for ($i = 0; $i < $primes_cnt; $i++) {
+ if (bccomp($num, $primes[$i]) <= 0) {
+ // number is prime
+ return true;
+ }
+ if (!bccomp(bcmod($num, $primes[$i]), '0')) {
+ // number divides by $primes[$i]
+ return false;
+ }
+ }
+
+ /*
+ try Miller-Rabin's probable-primality test for first
+ 7 primes as bases
+ */
+ for ($i = 0; $i < 7; $i++) {
+ if (!$this->_millerTest($num, $primes[$i])) {
+ // $num is composite
+ return false;
+ }
+ }
+ // $num is strong pseudoprime
+ return true;
+ }
+
+ /**
* Generates prime number with length $bits_cnt
* using $random_generator as random generator function.
*
@@ -280,21 +293,24 @@ function cmpAbs($num1, $num2)
*/
function getPrime($bits_cnt, $random_generator)
{
- $bytes_cnt = intval($bits_cnt / 8);
- $bits_cnt %= 8;
+ $bytes_n = intval($bits_cnt / 8);
+ $bits_n = $bits_cnt % 8;
do {
- // generate random number with length [$bits_cnt]
- $num = 1;
- for ($i = 0; $i <= $bytes_cnt; $i++) {
- $num = bcadd(bcmul($num, '256'), call_user_func($random_generator) & 0xff);
+ $str = '';
+ for ($i = 0; $i < $bytes_n; $i++) {
+ $str .= chr(call_user_func($random_generator) & 0xff);
}
- $num = bcdiv($num, 1 << (8 - $bits_cnt));
+ $n = call_user_func($random_generator) & 0xff;
+ $n |= 0x80;
+ $n >>= 8 - $bits_n;
+ $str .= chr($n);
+ $num = $this->bin2int($str);
- // search next closest prime number after [$num]
+ // search for the next closest prime number after [$num]
if (!bccomp(bcmod($num, '2'), '0')) {
$num = bcadd($num, '1');
}
- while (!$this->_isPrime($num)) {
+ while (!$this->isPrime($num)) {
$num = bcadd($num, '2');
}
} while ($this->bitLen($num) != $bits_cnt);
@@ -387,7 +403,8 @@ function bitLen($num)
$tmp = ord($tmp{strlen($tmp) - 1});
if (!$tmp) {
$bit_len -= 8;
- } else {
+ }
+ else {
while (!($tmp & 0x80)) {
$bit_len--;
$tmp <<= 1;
@@ -417,7 +434,8 @@ function bitOr($num1, $num2, $start_pos)
if ($start_byte < strlen($tmp1)) {
$tmp2 |= substr($tmp1, $start_byte);
$tmp1 = substr($tmp1, 0, $start_byte) . $tmp2;
- } else {
+ }
+ else {
$tmp1 = str_pad($tmp1, $start_byte, "\0") . $tmp2;
}
return $this->bin2int($tmp1);
View
45 RSA/Math/BigInt.php
@@ -18,7 +18,7 @@
* @author Alexander Valyalkin <valyala@gmail.com>
* @copyright 2005, 2006 Alexander Valyalkin
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version 1.1.0
+ * @version 1.2.0b
* @link http://pear.php.net/package/Crypt_RSA
*/
@@ -134,6 +134,19 @@ function mul($num1, $num2)
}
/**
+ * Calculates $num1 % $num2
+ *
+ * @param string $num1
+ * @param string $num2
+ * @return string
+ * @access public
+ */
+ function mod($num1, $num2)
+ {
+ return bi_mod($num1, $num2);
+ }
+
+ /**
* Compares abs($num1) to abs($num2).
* Returns:
* -1, if abs($num1) < abs($num2)
@@ -151,6 +164,19 @@ function cmpAbs($num1, $num2)
}
/**
+ * Tests $num on primality. Returns true, if $num is strong pseudoprime.
+ * Else returns false.
+ *
+ * @param string $num
+ * @return bool
+ * @access private
+ */
+ function isPrime($num)
+ {
+ return bi_is_prime($num) ? true : false;
+ }
+
+ /**
* Generates prime number with length $bits_cnt
* using $random_generator as random generator function.
*
@@ -160,11 +186,22 @@ function cmpAbs($num1, $num2)
*/
function getPrime($bits_cnt, $random_generator)
{
+ $bytes_n = intval($bits_cnt / 8);
+ $bits_n = $bits_cnt % 8;
do {
- $num = bi_rand($bits_cnt, $random_generator);
- bi_set_bit($num, $bits_cnt - 1);
+ $str = '';
+ for ($i = 0; $i < $bytes_n; $i++) {
+ $str .= chr(call_user_func($random_generator) & 0xff);
+ }
+ $n = call_user_func($random_generator) & 0xff;
+ $n |= 0x80;
+ $n >>= 8 - $bits_n;
+ $str .= chr($n);
+ $num = $this->bin2int($str);
+
+ // search for the next closest prime number after [$num]
$num = bi_next_prime($num);
- } while (bi_bit_len($num) != $bits_cnt);
+ } while ($this->bitLen($num) != $bits_cnt);
return $num;
}
View
53 RSA/Math/GMP.php
@@ -18,7 +18,7 @@
* @author Alexander Valyalkin <valyala@gmail.com>
* @copyright 2005, 2006 Alexander Valyalkin
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version 1.1.0
+ * @version 1.2.0b
* @link http://pear.php.net/package/Crypt_RSA
*/
@@ -141,6 +141,19 @@ function mul($num1, $num2)
}
/**
+ * Calculates $num1 % $num2
+ *
+ * @param string $num1
+ * @param string $num2
+ * @return string
+ * @access public
+ */
+ function mod($num1, $num2)
+ {
+ return gmp_mod($num1, $num2);
+ }
+
+ /**
* Compares abs($num1) to abs($num2).
* Returns:
* -1, if abs($num1) < abs($num2)
@@ -158,6 +171,19 @@ function cmpAbs($num1, $num2)
}
/**
+ * Tests $num on primality. Returns true, if $num is strong pseudoprime.
+ * Else returns false.
+ *
+ * @param string $num
+ * @return bool
+ * @access private
+ */
+ function isPrime($num)
+ {
+ return gmp_prob_prime($num) ? true : false;
+ }
+
+ /**
* Generates prime number with length $bits_cnt
* using $random_generator as random generator function.
*
@@ -167,17 +193,20 @@ function cmpAbs($num1, $num2)
*/
function getPrime($bits_cnt, $random_generator)
{
- $bytes_cnt = intval($bits_cnt / 8);
- $bits_cnt %= 8;
+ $bytes_n = intval($bits_cnt / 8);
+ $bits_n = $bits_cnt % 8;
do {
- // generate random number with length [$bits_cnt]
- $num = 1;
- for ($i = 0; $i <= $bytes_cnt; $i++) {
- $num = gmp_add(gmp_mul($num, '256'), call_user_func($random_generator) & 0xff);
+ $str = '';
+ for ($i = 0; $i < $bytes_n; $i++) {
+ $str .= chr(call_user_func($random_generator) & 0xff);
}
- $num = gmp_div($num, 1 << (8 - $bits_cnt));
+ $n = call_user_func($random_generator) & 0xff;
+ $n |= 0x80;
+ $n >>= 8 - $bits_n;
+ $str .= chr($n);
+ $num = $this->bin2int($str);
- // search next closest prime number after [$num]
+ // search for the next closest prime number after [$num]
if (!gmp_cmp(gmp_mod($num, '2'), '0')) {
$num = gmp_add($num, '1');
}
@@ -253,7 +282,8 @@ function bitLen($num)
$tmp = ord($tmp{strlen($tmp) - 1});
if (!$tmp) {
$bit_len -= 8;
- } else {
+ }
+ else {
while (!($tmp & 0x80)) {
$bit_len--;
$tmp <<= 1;
@@ -283,7 +313,8 @@ function bitOr($num1, $num2, $start_pos)
if ($start_byte < strlen($tmp1)) {
$tmp2 |= substr($tmp1, $start_byte);
$tmp1 = substr($tmp1, 0, $start_byte) . $tmp2;
- } else {
+ }
+ else {
$tmp1 = str_pad($tmp1, $start_byte, "\0") . $tmp2;
}
return $this->bin2int($tmp1);
View
18 RSA/MathLoader.php
@@ -18,7 +18,7 @@
* @author Alexander Valyalkin <valyala@gmail.com>
* @copyright 2005, 2006 Alexander Valyalkin
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version 1.1.0
+ * @version 1.2.0b
* @link http://pear.php.net/package/Crypt_RSA
*/
@@ -88,37 +88,43 @@ function &loadWrapper($wrapper_name = 'default')
return $math_objects[$wrapper_name];
}
+ $err_handler = &new Crypt_RSA_ErrorHandler;
+
if ($wrapper_name === 'default') {
// try to load the most suitable wrapper
$n = sizeof($math_wrappers);
for ($i = 0; $i < $n; $i++) {
$obj = &Crypt_RSA_MathLoader::loadWrapper($math_wrappers[$i]);
- if (!PEAR::isError($obj)) {
+ if (!$err_handler->isError($obj)) {
// wrapper for $math_wrappers[$i] successfully loaded
// register it as default wrapper and return reference to it
return $math_objects['default'] = &$obj;
}
}
// can't load any wrapper
- return PEAR::raiseError("can't load any wrapper for existing math libraries", CRYPT_RSA_ERROR_NO_WRAPPERS);
+ $err_handler->pushError("can't load any wrapper for existing math libraries", CRYPT_RSA_ERROR_NO_WRAPPERS);
+ return $err_handler->getLastError();
}
$class_name = 'Crypt_RSA_Math_' . $wrapper_name;
$class_filename = dirname(__FILE__) . '/Math/' . $wrapper_name . '.php';
if (!is_file($class_filename)) {
- return PEAR::raiseError("can't find file [{$class_filename}] for RSA math wrapper [{$wrapper_name}]", CRYPT_RSA_ERROR_NO_FILE);
+ $err_handler->pushError("can't find file [{$class_filename}] for RSA math wrapper [{$wrapper_name}]", CRYPT_RSA_ERROR_NO_FILE);
+ return $err_handler->getLastError();
}
require_once($class_filename);
if (!class_exists($class_name)) {
- return PEAR::raiseError("can't find class [{$class_name}] in file [{$class_filename}]", CRYPT_RSA_ERROR_NO_CLASS);
+ $err_handler->pushError("can't find class [{$class_name}] in file [{$class_filename}]", CRYPT_RSA_ERROR_NO_CLASS);
+ return $err_handler->getLastError();
}
// create and return wrapper object on success or PEAR_Error object on error
$obj = &new $class_name;
if ($obj->errstr) {
// cannot load required extension for math wrapper
- $obj = PEAR::raiseError($obj->errstr, CRYPT_RSA_ERROR_NO_EXT);
+ $err_handler->pushError($obj->errstr, CRYPT_RSA_ERROR_NO_EXT);
+ return $err_handler->getLastError();
}
return $math_objects[$wrapper_name] = &$obj;
}
View
2 docs/math_wrappers.txt
@@ -13,7 +13,9 @@ Math wrapper class must provide interface with following functions:
int2bin($num) - transforms large integer from native form into binary representation
powmod($a, $b, $mod) - calculates ($a ^ $b) % $mod
mul($a, $b) - calculates $a * $b
+ mod($a, $b) - calculates $a % $b
cmpAbs($a, $b) - compares abs($a) with abs($b)
+ isPrime($num) - returns true if $num is strong pseudoprime
getPrime($bits_cnt, $rnd_generator) - returns prime with length $bits_cnt
dec($num) - calculates $num - 1
isOne($num) - returns true, if $num is one
View
2 examples/web_interface.php
@@ -21,7 +21,7 @@
* @author Alexander Valyalkin <valyala@gmail.com>
* @copyright 2005, 2006 Alexander Valyalkin
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version 1.1.0
+ * @version 1.2.0b
* @link http://pear.php.net/package/Crypt_RSA
*/
View
47 package.xml
@@ -18,14 +18,16 @@ It uses one of the following extensions for math calculations:
</maintainer>
</maintainers>
<release>
- <version>1.1.0b</version>
- <date>2006-02-23</date>
+ <version>1.2.0b</version>
+ <date>2006-02-26</date>
<license>PHP</license>
<state>beta</state>
- <notes>* added fromPEMString() function in Crypt_RSA_KeyPair class. This function allows to retrieve RSA keypair from PEM-encoded strings. Thanks to all the people who sponsored .pem developement
-* modified key generation algorithm
-* added optional &apos;error_handler&apos; parameter to factory() methods in all classes
-* modified math wrapper interface: isZero(), nextPrime() and getRand() substituted by isOne() and getPrime() functions
+ <notes>* added toPEMString() function in Crypt_RSA_KeyPair class. It allows to store keypair in .pem file as RSA private key
+* added isEqual() function in Crypt_RSA_KeyPair class. It returns true if two keypairs are equal
+* added mod() and isPrime() functions to math wrappers interface
+* fixed bug in getPrime() function of GMP and BCMath math wrappers, which hangs up key generating process
+* added optional $random_generator parameter to Crypt_RSA_KeyPair constructor and factory() method
+* added benchmark in tests/bench.php, which clearly shows performance of different math wrappers (GMP, BigInt, BCMath). In short: GMP is 5 times faster than BigInt, BCMath is 50 times slower than BigInt.
</notes>
<deps>
<dep type="ext" rel="has" optional="yes">bcmath</dep>
@@ -70,6 +72,8 @@ It uses one of the following extensions for math calculations:
<provides type="function" name="Crypt_RSA_KeyPair::setRandomGenerator" />
<provides type="function" name="Crypt_RSA_KeyPair::getKeyLength" />
<provides type="function" name="Crypt_RSA_KeyPair::fromPEMString" />
+ <provides type="function" name="Crypt_RSA_KeyPair::toPEMString" />
+ <provides type="function" name="Crypt_RSA_KeyPair::isEqual" />
<provides type="function" name="Crypt_RSA_KeyPair::isError" />
<provides type="function" name="Crypt_RSA_KeyPair::getErrorList" />
<provides type="function" name="Crypt_RSA_KeyPair::getLastError" />
@@ -90,7 +94,9 @@ It uses one of the following extensions for math calculations:
<provides type="function" name="Crypt_RSA_Math_BigInt::int2bin" />
<provides type="function" name="Crypt_RSA_Math_BigInt::powmod" />
<provides type="function" name="Crypt_RSA_Math_BigInt::mul" />
+ <provides type="function" name="Crypt_RSA_Math_BigInt::mod" />
<provides type="function" name="Crypt_RSA_Math_BigInt::cmpAbs" />
+ <provides type="function" name="Crypt_RSA_Math_BigInt::isPrime" />
<provides type="function" name="Crypt_RSA_Math_BigInt::getPrime" />
<provides type="function" name="Crypt_RSA_Math_BigInt::dec" />
<provides type="function" name="Crypt_RSA_Math_BigInt::isOne" />
@@ -105,7 +111,9 @@ It uses one of the following extensions for math calculations:
<provides type="function" name="Crypt_RSA_Math_BCMath::int2bin" />
<provides type="function" name="Crypt_RSA_Math_BCMath::powmod" />
<provides type="function" name="Crypt_RSA_Math_BCMath::mul" />
+ <provides type="function" name="Crypt_RSA_Math_BCMath::mod" />
<provides type="function" name="Crypt_RSA_Math_BCMath::cmpAbs" />
+ <provides type="function" name="Crypt_RSA_Math_BCMath::isPrime" />
<provides type="function" name="Crypt_RSA_Math_BCMath::getPrime" />
<provides type="function" name="Crypt_RSA_Math_BCMath::dec" />
<provides type="function" name="Crypt_RSA_Math_BCMath::isOne" />
@@ -120,7 +128,9 @@ It uses one of the following extensions for math calculations:
<provides type="function" name="Crypt_RSA_Math_GMP::int2bin" />
<provides type="function" name="Crypt_RSA_Math_GMP::powmod" />
<provides type="function" name="Crypt_RSA_Math_GMP::mul" />
+ <provides type="function" name="Crypt_RSA_Math_GMP::mod" />
<provides type="function" name="Crypt_RSA_Math_GMP::cmpAbs" />
+ <provides type="function" name="Crypt_RSA_Math_GMP::isPrime" />
<provides type="function" name="Crypt_RSA_Math_GMP::getPrime" />
<provides type="function" name="Crypt_RSA_Math_GMP::dec" />
<provides type="function" name="Crypt_RSA_Math_GMP::isOne" />
@@ -131,21 +141,22 @@ It uses one of the following extensions for math calculations:
<provides type="function" name="Crypt_RSA_Math_GMP::subint" />
<provides type="function" name="Crypt_RSA_Math_GMP::getWrapperName" />
<filelist>
- <file role="php" baseinstalldir="Crypt" md5sum="4dc4fb4926c0fe36966b2926b0efa840" name="RSA.php"/>
- <file role="php" baseinstalldir="Crypt" md5sum="77076008a77505ce72a7fcc0c48f9656" name="RSA/ErrorHandler.php"/>
- <file role="php" baseinstalldir="Crypt" md5sum="b6a063ef6c25c2589e92401c72bb9ba8" name="RSA/Key.php"/>
- <file role="php" baseinstalldir="Crypt" md5sum="5a8c889b028febf7c5858b8d25ac6d20" name="RSA/KeyPair.php"/>
- <file role="php" baseinstalldir="Crypt" md5sum="a1bb53e6b89e5afb9f8fddcb7327442e" name="RSA/MathLoader.php"/>
- <file role="php" baseinstalldir="Crypt" md5sum="2c15981a896aa267b340ce34157355fc" name="RSA/Math/BCMath.php"/>
- <file role="php" baseinstalldir="Crypt" md5sum="da034f2ea0e1acff984b3ee0490b78dd" name="RSA/Math/BigInt.php"/>
- <file role="php" baseinstalldir="Crypt" md5sum="036f2082efdac1111d5ea668f90732f3" name="RSA/Math/GMP.php"/>
+ <file role="php" baseinstalldir="Crypt" md5sum="6a9c4ac22b2bc7dfb8272a9f675e1c4a" name="RSA.php"/>
+ <file role="php" baseinstalldir="Crypt" md5sum="f1ac2bf277a5de66735e06e164940073" name="RSA/ErrorHandler.php"/>
+ <file role="php" baseinstalldir="Crypt" md5sum="5a0d2996e49700fa88236a73a79bd2c0" name="RSA/Key.php"/>
+ <file role="php" baseinstalldir="Crypt" md5sum="047a4cd0081b0dc67ede1c03261f0c54" name="RSA/KeyPair.php"/>
+ <file role="php" baseinstalldir="Crypt" md5sum="4b9e9f3c34bcaeec0c322b099b726b02" name="RSA/MathLoader.php"/>
+ <file role="php" baseinstalldir="Crypt" md5sum="78eb088a0e4424c2aa2829ebe4ede5c0" name="RSA/Math/BCMath.php"/>
+ <file role="php" baseinstalldir="Crypt" md5sum="0e998825b30b34878c4ca87423ee4e87" name="RSA/Math/BigInt.php"/>
+ <file role="php" baseinstalldir="Crypt" md5sum="7ab0d5da8962f3bf4cc471f2d9879111" name="RSA/Math/GMP.php"/>
<file role="doc" baseinstalldir="Crypt" md5sum="8e699cdffe3e4e3d1341e2f1dc81d29d" name="CREDITS"/>
<file role="doc" baseinstalldir="Crypt" md5sum="f4c7bb36cd36382f0cf5fd4594f73fee" name="LICENSE"/>
<file role="doc" baseinstalldir="Crypt" md5sum="ca9d024c219dba927b4aee2dc1fb5737" name="docs/Crypt_RSA.txt"/>
- <file role="doc" baseinstalldir="Crypt" md5sum="78b93d6745a22efeafea6a851c488443" name="docs/math_wrappers.txt"/>
- <file role="doc" baseinstalldir="Crypt" md5sum="2f6ff6d854f5ca83fcaf3ce922cba3da" name="examples/web_interface.php"/>
- <file role="test" baseinstalldir="Crypt" md5sum="5e6d9026f1907003a0af23c08f9f27cb" name="tests/test1.php"/>
- <file role="test" baseinstalldir="Crypt" md5sum="4c5a5a7bd18ee31cb21b57bf2eb96b43" name="tests/test2.php"/>
+ <file role="doc" baseinstalldir="Crypt" md5sum="b93c59f9eb0f92a4c7ff235423f299d8" name="docs/math_wrappers.txt"/>
+ <file role="doc" baseinstalldir="Crypt" md5sum="73f96fcd914179a3500c71d96d77489a" name="examples/web_interface.php"/>
+ <file role="test" baseinstalldir="Crypt" md5sum="269bb4ce3ddc98efa5cc02c5fa36d71d" name="tests/bench.php"/>
+ <file role="test" baseinstalldir="Crypt" md5sum="ced4107d21a124ccfdc20f49e91e5c39" name="tests/test1.php"/>
+ <file role="test" baseinstalldir="Crypt" md5sum="4e5cea66200222ba82d7869cf4356caa" name="tests/test2.php"/>
</filelist>
</release>
</package>
View
62 tests/bench.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Crypt_RSA allows to do following operations:
+ * - key pair generation
+ * - encryption and decryption
+ * - signing and sign validation
+ *
+ * This module requires the PHP BCMath extension.
+ * See http://us2.php.net/manual/en/ref.bc.php for details.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Encryption
+ * @package Crypt_RSA
+ * @author Alexander Valyalkin <valyala@gmail.com>
+ * @copyright 2005, 2006 Alexander Valyalkin
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version 1.2.0b
+ * @link http://pear.php.net/package/Crypt_RSA
+ */
+
+/**
+ * This script tries to generate 4096-bit keys by using different math wrappers
+ */
+
+require_once 'Crypt/RSA.php';
+
+define('KEY_LENGTH', 2048);
+
+echo "key length: " . KEY_LENGTH . " bit\n";
+go('GMP');
+go('BigInt');
+go('BCMath');
+
+
+function getmicrotime()
+{
+ list($usec, $sec) = explode(" ", microtime());
+ return ((float)$usec + (float)$sec);
+}
+
+function go($math_wrapper)
+{
+ echo "Test $math_wrapper: ";
+ mt_srand(1);
+ $start = getmicrotime();
+ $keypair = &Crypt_RSA_KeyPair::factory(KEY_LENGTH, $math_wrapper, '', 'mt_rand');
+ if (PEAR::isError($obj)) {
+ echo 'failed: ', $obj->getMessage(), "\n";
+ return;
+ }
+ $time = getmicrotime() - $start;
+ printf("done. Time: %.3f seconds\n", $time);
+}
+
+?>
View
35 tests/test1.php
@@ -21,7 +21,7 @@
* @author Alexander Valyalkin <valyala@gmail.com>
* @copyright 2005, 2006 Alexander Valyalkin
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version 1.1.0
+ * @version 1.2.0b
* @link http://pear.php.net/package/Crypt_RSA
*/
@@ -44,8 +44,8 @@
See contents of /RSA/MathClasses folder for examples.
BigInt is much faster than BCMath.
*/
-define('MATH_LIBRARY', 'BigInt');
//define('MATH_LIBRARY', 'GMP');
+//define('MATH_LIBRARY', 'BigInt');
//define('MATH_LIBRARY', 'BCMath');
$errors = array();
@@ -65,8 +65,8 @@
$errors[] = "wrong result returned from Crypt_RSA_KeyPair::getKeyLength() function";
}
-// check Crypt_RSA_KeyPair::fromPEMString() function
-$str = "
+// check fromPEMString() and toPEMString() functions of Crypt_RSA_KeyPair class
+$str_in = "
-----BEGIN RSA PRIVATE KEY-----
MIIBPAIBAAJBAKSLT0KZTXYxHr6U/9GYBbnV8vxGkIleDE4aiVMRxuofOjcHDCoI
qsrVjgP78BrVqWMAAeQ9i0dXxz9zhy0+h7MCAwEAAQJBAI6OL1Yo0Uaj2doN5vDk
@@ -78,14 +78,14 @@
-----END RSA PRIVATE KEY-----
";
-$keypair = Crypt_RSA_KeyPair::fromPEMString($str, MATH_LIBRARY, 'check_error');
+$key_pair = Crypt_RSA_KeyPair::fromPEMString($str_in, MATH_LIBRARY, 'check_error');
-$public_key = $keypair->getPublicKey();
-$private_key = $keypair->getPrivateKey();
-$key_length = $keypair->getKeyLength();
+$public_key = $key_pair->getPublicKey();
+$private_key = $key_pair->getPrivateKey();
+$key_length = $key_pair->getKeyLength();
if ($key_length != 512) {
- $errors[] = "incorrect key length retrieved from PEM file";
+ $errors[] = "incorrect key length retrieved from PEM string";
}
$rsa_obj = new Crypt_RSA(array(), MATH_LIBRARY, 'check_error');
@@ -98,9 +98,24 @@
$errors[] = "decrypted text differs from encrypted text in Crypt_RSA_KeyPair::fromPEMString() check";
}
-// try to generate 256-bit key pair
+$str_out = $key_pair->toPEMString();
+
+$str_in = str_replace($str_in, array("\r", "\n"), '');
+$str_out = str_replace($str_out, array("\r", "\n"), '');
+if ($str_in != $str_out) {
+ $errors[] = "PEM strings handling seems to be broken";
+}
+
+// try to generate 256-bit keypair and convert it to PEM string,
+// then convert this string to another keypair and compare them
$key_pair->generate(256);
+$str1 = $key_pair->toPEMString();
+$key_pair2 = $key_pair->fromPEMString($str1);
+if (!$key_pair->isEqual($key_pair2) || !$key_pair2->isEqual($key_pair)) {
+ $errors[] = "RSA_KeyPair::isEqual() is broken";
+}
+
///////////////////////////////////////////////
// test all functionality of Crypt_RSA_Key class
///////////////////////////////////////////////
View
2 tests/test2.php
@@ -21,7 +21,7 @@
* @author Alexander Valyalkin <valyala@gmail.com>
* @copyright 2005, 2006 Alexander Valyalkin
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version 1.1.0
+ * @version 1.2.0b
* @link http://pear.php.net/package/Crypt_RSA
*/

0 comments on commit c029790

Please sign in to comment.