From effa4f5d1efffcb52d4a95562e0b4831f7bd3435 Mon Sep 17 00:00:00 2001 From: James Walker Date: Mon, 22 Feb 2010 01:39:00 -0500 Subject: [PATCH 1/9] adding extlib for Crypt_RSA --- plugins/OStatus/OStatusPlugin.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 60bb144a83..24ed23a002 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -24,6 +24,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } +set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . '/extlib/'); + class FeedSubException extends Exception { } From 47300a2ae9a51108fbf59a57cf5ab6e8867b54a6 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 22 Feb 2010 01:21:34 -0800 Subject: [PATCH 2/9] Upgrade profile-based activity noun to have more complete set of profile fields --- classes/Profile.php | 45 ++++++++++++++++++++++++++++++++++++-- lib/atom10feed.php | 4 ++-- lib/atomusernoticefeed.php | 3 +-- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/classes/Profile.php b/classes/Profile.php index 6b396c8c34..4f67fc0bcc 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -792,9 +792,11 @@ class Profile extends Memcached_DataObject * Returns an XML string fragment with profile information as an * Activity Streams noun object with the given element type. * - * Assumes that 'activity' namespace has been previously defined. + * Assumes that 'activity', 'georss', and 'poco' namespace has been + * previously defined. * * @param string $element one of 'actor', 'subject', 'object', 'target' + * * @return string */ function asActivityNoun($element) @@ -811,9 +813,46 @@ class Profile extends Memcached_DataObject 'id', null, $this->getUri() - ); + ); + + // title should contain fullname $xs->element('title', null, $this->getBestName()); + // Portable Contacts stuff + + if (isset($this->bio)) { + + // XXX: Possible to use OpenSocial's aboutMe? + + $xs->element('poco:note', null, $this->bio); + } + + if (isset($this->homepage)) { + + $xs->elementStart('poco:urls'); + $xs->element('poco:value', null, $this->homepage); + $xs->element('poco:type', null, 'homepage'); + $xs->element('poco:primary', null, 'true'); + $xs->elementEnd('poco:urls'); + } + + if (isset($this->location)) { + $xs->elementStart('poco:address'); + $xs->element('poco:formatted', null, $this->location); + $xs->elementEnd('poco:address'); + } + + if (isset($this->lat) && isset($this->lon)) { + $this->element( + 'georss:point', + null, + (float)$this->lat . ' ' . (float)$this->lon + ); + } + + // XXX: Should we send all avatar sizes we have? I think + // cliqset does -Z + $avatar = $this->getAvatar(AVATAR_PROFILE_SIZE); $xs->element( @@ -829,6 +868,8 @@ class Profile extends Memcached_DataObject $xs->elementEnd('activity:' . $element); + // XXX: Add people tags with plural? + return $xs->getString(); } diff --git a/lib/atom10feed.php b/lib/atom10feed.php index 5e17b20d3a..8842840d56 100644 --- a/lib/atom10feed.php +++ b/lib/atom10feed.php @@ -109,11 +109,11 @@ class Atom10Feed extends XMLStringer ); } - if (!is_null($uri)) { + if (isset($uri)) { $xs->element('uri', null, $uri); } - if (!is_null(email)) { + if (isset($email)) { $xs->element('email', null, $email); } diff --git a/lib/atomusernoticefeed.php b/lib/atomusernoticefeed.php index f71c721fec..2ad8de4550 100644 --- a/lib/atomusernoticefeed.php +++ b/lib/atomusernoticefeed.php @@ -60,8 +60,7 @@ class AtomUserNoticeFeed extends AtomNoticeFeed $this->user = $user; if (!empty($user)) { $profile = $user->getProfile(); - $this->addAuthor($profile->getBestName(), - $user->uri); + $this->addAuthor($profile->nickname, $user->uri); } } From e94800ced9884f20df73cd6325d0fefb33ac2236 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 22 Feb 2010 07:08:57 -0500 Subject: [PATCH 3/9] fix broken link in OpenID documentation --- plugins/OpenID/doc-src/openid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/OpenID/doc-src/openid b/plugins/OpenID/doc-src/openid index c741e36741..f2dc610a55 100644 --- a/plugins/OpenID/doc-src/openid +++ b/plugins/OpenID/doc-src/openid @@ -3,7 +3,7 @@ If you already have an account on %%site.name%%, you can [login](%%action.login%%) with your username and password as usual. To use OpenID in the future, you can [add an OpenID to your account](%%action.openidsettings%%) after you have logged in normally. -There are many [Public OpenID providers](http://wiki.openid.net/Public_OpenID_providers), and you may already have an OpenID-enabled account on another service. +There are many [Public OpenID providers](http://wiki.openid.net/OpenID-Providers), and you may already have an OpenID-enabled account on another service. * On wikis: If you have an account on an OpenID-enabled wiki, like [Wikitravel](http://wikitravel.org/), [wikiHow](http://www.wikihow.com/), [Vinismo](http://vinismo.com/), [AboutUs](http://aboutus.org/) or [Keiki](http://kei.ki/), you can log in to %%site.name%% by entering the **full URL** of your user page on that other wiki in the box above. For example, *http://kei.ki/en/User:Evan*. * [Yahoo!](http://openid.yahoo.com/) : If you have an account with Yahoo!, you can log in to this site by entering your Yahoo!-provided OpenID in the box above. Yahoo! OpenID URLs have the form *https://me.yahoo.com/yourusername*. From fae5a15a885b0c108efc4c5e28094f15ffbe8694 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 22 Feb 2010 07:40:20 -0500 Subject: [PATCH 4/9] add strongly-suggested link to Profile::asActivityNoun() --- classes/Profile.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/classes/Profile.php b/classes/Profile.php index 1ba3281ff6..faa6367b9c 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -818,6 +818,10 @@ class Profile extends Memcached_DataObject // title should contain fullname $xs->element('title', null, $this->getBestName()); + $xs->element('link', array('rel' => 'alternate', + 'type' => 'text/html'), + $this->profileurl); + // Portable Contacts stuff if (isset($this->bio)) { From b79d4ed6a1e61c600fdd382f3bdfde62aaa15b3d Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 22 Feb 2010 07:43:12 -0500 Subject: [PATCH 5/9] add PoCo preferredUsername for nickname in Profile::asActivityNoun() --- classes/Profile.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/classes/Profile.php b/classes/Profile.php index faa6367b9c..7fb2b87bc3 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -822,6 +822,8 @@ class Profile extends Memcached_DataObject 'type' => 'text/html'), $this->profileurl); + $xs->element('poco:preferredUsername', null, $this->nickname); + // Portable Contacts stuff if (isset($this->bio)) { From 75fdef209245bf9424d4b995a42e4dfd980469d2 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 22 Feb 2010 07:57:44 -0500 Subject: [PATCH 6/9] handle poco nicknames in Ostatus_profile --- plugins/OStatus/classes/Ostatus_profile.php | 4 +++- plugins/OStatus/lib/activity.php | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 71885bcdce..0e12f8fc6e 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -832,7 +832,9 @@ class Ostatus_profile extends Memcached_DataObject protected static function getActivityObjectNickname($object, $hints=array()) { - // XXX: check whatever PoCo calls a nickname first + if (!empty($object->nickname)) { + return common_nicknamize($object->nickname); + } // Try the definitive ID diff --git a/plugins/OStatus/lib/activity.php b/plugins/OStatus/lib/activity.php index af83f8bc66..a26248f199 100644 --- a/plugins/OStatus/lib/activity.php +++ b/plugins/OStatus/lib/activity.php @@ -31,6 +31,12 @@ if (!defined('STATUSNET')) { exit(1); } +class PoCo +{ + const NS = 'http://portablecontacts.net/spec/1.0'; + const USERNAME = 'preferredUsername'; +} + /** * Utilities for turning DOMish things into Activityish things * @@ -319,7 +325,8 @@ class ActivityObject $this->displayName = $this->title; // @fixme we may have multiple avatars with different resolutions specified - $this->avatar = ActivityUtils::getLink($element, 'avatar'); + $this->avatar = ActivityUtils::getLink($element, 'avatar'); + $this->nickname = ActivityUtils::childContent($element, PoCo::USERNAME, PoCo::NS); } } From e0388cc1d3922002596c2ec0531ac2f06d91806a Mon Sep 17 00:00:00 2001 From: James Walker Date: Mon, 22 Feb 2010 09:05:32 -0500 Subject: [PATCH 7/9] adding magic sig stuff --- plugins/OStatus/extlib/Crypt/RSA.php | 524 ++++++++++++ .../OStatus/extlib/Crypt/RSA/ErrorHandler.php | 234 +++++ plugins/OStatus/extlib/Crypt/RSA/Key.php | 315 +++++++ plugins/OStatus/extlib/Crypt/RSA/KeyPair.php | 804 ++++++++++++++++++ .../OStatus/extlib/Crypt/RSA/Math/BCMath.php | 482 +++++++++++ .../OStatus/extlib/Crypt/RSA/Math/BigInt.php | 313 +++++++ plugins/OStatus/extlib/Crypt/RSA/Math/GMP.php | 361 ++++++++ .../OStatus/extlib/Crypt/RSA/MathLoader.php | 135 +++ plugins/OStatus/lib/magicenvelope.php | 174 ++++ plugins/OStatus/lib/magicsig.php | 159 ++++ 10 files changed, 3501 insertions(+) create mode 100644 plugins/OStatus/extlib/Crypt/RSA.php create mode 100644 plugins/OStatus/extlib/Crypt/RSA/ErrorHandler.php create mode 100644 plugins/OStatus/extlib/Crypt/RSA/Key.php create mode 100644 plugins/OStatus/extlib/Crypt/RSA/KeyPair.php create mode 100644 plugins/OStatus/extlib/Crypt/RSA/Math/BCMath.php create mode 100644 plugins/OStatus/extlib/Crypt/RSA/Math/BigInt.php create mode 100644 plugins/OStatus/extlib/Crypt/RSA/Math/GMP.php create mode 100644 plugins/OStatus/extlib/Crypt/RSA/MathLoader.php create mode 100644 plugins/OStatus/lib/magicenvelope.php create mode 100644 plugins/OStatus/lib/magicsig.php diff --git a/plugins/OStatus/extlib/Crypt/RSA.php b/plugins/OStatus/extlib/Crypt/RSA.php new file mode 100644 index 0000000000..16dfa54d48 --- /dev/null +++ b/plugins/OStatus/extlib/Crypt/RSA.php @@ -0,0 +1,524 @@ + + * @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 + */ + +/** + * RSA error handling facilities + */ +require_once 'Crypt/RSA/ErrorHandler.php'; + +/** + * loader for math wrappers + */ +require_once 'Crypt/RSA/MathLoader.php'; + +/** + * helper class for mange single key + */ +require_once 'Crypt/RSA/Key.php'; + +/** + * helper class for manage key pair + */ +require_once 'Crypt/RSA/KeyPair.php'; + +/** + * Crypt_RSA class, derived from Crypt_RSA_ErrorHandler + * + * Provides the following functions: + * - setParams($params) - sets parameters of current object + * - encrypt($plain_data, $key = null) - encrypts data + * - decrypt($enc_data, $key = null) - decrypts data + * - createSign($doc, $private_key = null) - signs document by private key + * - validateSign($doc, $signature, $public_key = null) - validates signature of document + * + * Example usage: + * // creating an error handler + * $error_handler = create_function('$obj', 'echo "error: ", $obj->getMessage(), "\n"'); + * + * // 1024-bit key pair generation + * $key_pair = new Crypt_RSA_KeyPair(1024); + * + * // check consistence of Crypt_RSA_KeyPair object + * $error_handler($key_pair); + * + * // creating Crypt_RSA object + * $rsa_obj = new Crypt_RSA; + * + * // check consistence of Crypt_RSA object + * $error_handler($rsa_obj); + * + * // set error handler on Crypt_RSA object ( see Crypt/RSA/ErrorHandler.php for details ) + * $rsa_obj->setErrorHandler($error_handler); + * + * // encryption (usually using public key) + * $enc_data = $rsa_obj->encrypt($plain_data, $key_pair->getPublicKey()); + * + * // decryption (usually using private key) + * $plain_data = $rsa_obj->decrypt($enc_data, $key_pair->getPrivateKey()); + * + * // signing + * $signature = $rsa_obj->createSign($document, $key_pair->getPrivateKey()); + * + * // signature checking + * $is_valid = $rsa_obj->validateSign($document, $signature, $key_pair->getPublicKey()); + * + * // signing many documents by one private key + * $rsa_obj = new Crypt_RSA(array('private_key' => $key_pair->getPrivateKey())); + * // check consistence of Crypt_RSA object + * $error_handler($rsa_obj); + * // set error handler ( see Crypt/RSA/ErrorHandler.php for details ) + * $rsa_obj->setErrorHandler($error_handler); + * // sign many documents + * $sign_1 = $rsa_obj->sign($doc_1); + * $sign_2 = $rsa_obj->sign($doc_2); + * //... + * $sign_n = $rsa_obj->sign($doc_n); + * + * // changing default hash function, which is used for sign + * // creating/validation + * $rsa_obj->setParams(array('hash_func' => 'md5')); + * + * // using factory() method instead of constructor (it returns PEAR_Error object on failure) + * $rsa_obj = &Crypt_RSA::factory(); + * if (PEAR::isError($rsa_obj)) { + * echo "error: ", $rsa_obj->getMessage(), "\n"; + * } + * + * @category Encryption + * @package Crypt_RSA + * @author Alexander Valyalkin + * @copyright 2005, 2006 Alexander Valyalkin + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @link http://pear.php.net/package/Crypt_RSA + * @version @package_version@ + * @access public + */ +class Crypt_RSA extends Crypt_RSA_ErrorHandler +{ + /** + * Reference to math wrapper, which is used to + * manipulate large integers in RSA algorithm. + * + * @var object of Crypt_RSA_Math_* class + * @access private + */ + var $_math_obj; + + /** + * key for encryption, which is used by encrypt() method + * + * @var object of Crypt_RSA_KEY class + * @access private + */ + var $_enc_key; + + /** + * key for decryption, which is used by decrypt() method + * + * @var object of Crypt_RSA_KEY class + * @access private + */ + var $_dec_key; + + /** + * public key, which is used by validateSign() method + * + * @var object of Crypt_RSA_KEY class + * @access private + */ + var $_public_key; + + /** + * private key, which is used by createSign() method + * + * @var object of Crypt_RSA_KEY class + * @access private + */ + var $_private_key; + + /** + * name of hash function, which is used by validateSign() + * and createSign() methods. Default hash function is SHA-1 + * + * @var string + * @access private + */ + var $_hash_func = 'sha1'; + + /** + * Crypt_RSA constructor. + * + * @param array $params + * Optional associative array of parameters, such as: + * enc_key, dec_key, private_key, public_key, hash_func. + * See setParams() method for more detailed description of + * these parameters. + * @param string $wrapper_name + * Name of math wrapper, which will be used to + * perform different operations with big integers. + * 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 + * + * @access public + */ + function Crypt_RSA($params = null, $wrapper_name = 'default', $error_handler = '') + { + // set error handler + $this->setErrorHandler($error_handler); + // try to load math wrapper + $obj = &Crypt_RSA_MathLoader::loadWrapper($wrapper_name); + if ($this->isError($obj)) { + // error during loading of math wrapper + // Crypt_RSA object is partially constructed. + $this->pushError($obj); + return; + } + $this->_math_obj = &$obj; + + if (!is_null($params)) { + if (!$this->setParams($params)) { + // error in Crypt_RSA::setParams() function + return; + } + } + } + + /** + * Crypt_RSA factory. + * + * @param array $params + * Optional associative array of parameters, such as: + * enc_key, dec_key, private_key, public_key, hash_func. + * See setParams() method for more detailed description of + * these parameters. + * @param string $wrapper_name + * Name of math wrapper, which will be used to + * perform different operations with big integers. + * 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 + * + * @return object new Crypt_RSA object on success or PEAR_Error object on failure + * @access public + */ + function &factory($params = null, $wrapper_name = 'default', $error_handler = '') + { + $obj = &new Crypt_RSA($params, $wrapper_name, $error_handler); + if ($obj->isError()) { + // error during creating a new object. Retrurn PEAR_Error object + return $obj->getLastError(); + } + // object created successfully. Return it + return $obj; + } + + /** + * Accepts any combination of available parameters as associative array: + * enc_key - encryption key for encrypt() method + * dec_key - decryption key for decrypt() method + * public_key - key for validateSign() method + * private_key - key for createSign() method + * hash_func - name of hash function, which will be used to create and validate sign + * + * @param array $params + * associative array of permitted parameters (see above) + * + * @return bool true on success or false on error + * @access public + */ + function setParams($params) + { + if (!is_array($params)) { + $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 { + $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 { + $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') { + $this->pushError('private key must have "private" attribute', CRYPT_RSA_ERROR_WRONG_KEY_TYPE); + return false; + } + $this->_private_key = $params['private_key']; + } + 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') { + $this->pushError('public key must have "public" attribute', CRYPT_RSA_ERROR_WRONG_KEY_TYPE); + return false; + } + $this->_public_key = $params['public_key']; + } + 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'])) { + $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']; + } + return true; // all ok + } + + /** + * Ecnrypts $plain_data by the key $this->_enc_key or $key. + * + * @param string $plain_data data, which must be encrypted + * @param object $key encryption key (object of Crypt_RSA_Key class) + * @return mixed + * encrypted data as string on success or false on error + * + * @access public + */ + function encrypt($plain_data, $key = null) + { + $enc_data = $this->encryptBinary($plain_data, $key); + if ($enc_data !== false) { + return base64_encode($enc_data); + } + // error during encripting data + return false; + } + + /** + * Ecnrypts $plain_data by the key $this->_enc_key or $key. + * + * @param string $plain_data data, which must be encrypted + * @param object $key encryption key (object of Crypt_RSA_Key class) + * @return mixed + * encrypted data as binary string on success or false on error + * + * @access public + */ + function encryptBinary($plain_data, $key = null) + { + if (is_null($key)) { + // use current encryption key + $key = $this->_enc_key; + } + 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; + } + + // append tail \x01 to plain data. It needs for correctly decrypting of data + $plain_data .= "\x01"; + + $plain_data = $this->_math_obj->bin2int($plain_data); + $exp = $this->_math_obj->bin2int($key->getExponent()); + $modulus = $this->_math_obj->bin2int($key->getModulus()); + + // divide plain data into chunks + $data_len = $this->_math_obj->bitLen($plain_data); + $chunk_len = $key->getKeyLength() - 1; + $block_len = (int) ceil($chunk_len / 8); + $curr_pos = 0; + $enc_data = ''; + while ($curr_pos < $data_len) { + $tmp = $this->_math_obj->subint($plain_data, $curr_pos, $chunk_len); + $enc_data .= str_pad( + $this->_math_obj->int2bin($this->_math_obj->powmod($tmp, $exp, $modulus)), + $block_len, + "\0" + ); + $curr_pos += $chunk_len; + } + return $enc_data; + } + + /** + * Decrypts $enc_data by the key $this->_dec_key or $key. + * + * @param string $enc_data encrypted data as string + * @param object $key decryption key (object of RSA_Crypt_Key class) + * @return mixed + * decrypted data as string on success or false on error + * + * @access public + */ + function decrypt($enc_data, $key = null) + { + $enc_data = base64_decode($enc_data); + return $this->decryptBinary($enc_data, $key); + } + + /** + * Decrypts $enc_data by the key $this->_dec_key or $key. + * + * @param string $enc_data encrypted data as binary string + * @param object $key decryption key (object of RSA_Crypt_Key class) + * @return mixed + * decrypted data as string on success or false on error + * + * @access public + */ + function decryptBinary($enc_data, $key = null) + { + if (is_null($key)) { + // use current decryption key + $key = $this->_dec_key; + } + 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; + } + + $exp = $this->_math_obj->bin2int($key->getExponent()); + $modulus = $this->_math_obj->bin2int($key->getModulus()); + + $data_len = strlen($enc_data); + $chunk_len = $key->getKeyLength() - 1; + $block_len = (int) ceil($chunk_len / 8); + $curr_pos = 0; + $bit_pos = 0; + $plain_data = $this->_math_obj->bin2int("\0"); + while ($curr_pos < $data_len) { + $tmp = $this->_math_obj->bin2int(substr($enc_data, $curr_pos, $block_len)); + $tmp = $this->_math_obj->powmod($tmp, $exp, $modulus); + $plain_data = $this->_math_obj->bitOr($plain_data, $tmp, $bit_pos); + $bit_pos += $chunk_len; + $curr_pos += $block_len; + } + $result = $this->_math_obj->int2bin($plain_data); + + // delete tail, containing of \x01 + $tail = ord($result{strlen($result) - 1}); + if ($tail != 1) { + $this->pushError("Error tail of decrypted text = {$tail}. Expected 1", CRYPT_RSA_ERROR_WRONG_TAIL); + return false; + } + return substr($result, 0, -1); + } + + /** + * Creates sign for document $document, using $this->_private_key or $private_key + * as private key and $this->_hash_func or $hash_func as hash function. + * + * @param string $document document, which must be signed + * @param object $private_key private key (object of Crypt_RSA_Key type) + * @param string $hash_func name of hash function, which will be used during signing + * @return mixed + * signature of $document as string on success or false on error + * + * @access public + */ + function createSign($document, $private_key = null, $hash_func = null) + { + // check private key + if (is_null($private_key)) { + $private_key = $this->_private_key; + } + 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') { + $this->pushError('signing key must be private', CRYPT_RSA_ERROR_NEED_PRV_KEY); + return false; + } + + // check hash_func + if (is_null($hash_func)) { + $hash_func = $this->_hash_func; + } + if (!function_exists($hash_func)) { + $this->pushError("cannot find hash function with name [$hash_func]", CRYPT_RSA_ERROR_WRONG_HASH_FUNC); + return false; + } + + return $this->encrypt($hash_func($document), $private_key); + } + + /** + * Validates $signature for document $document with public key $this->_public_key + * or $public_key and hash function $this->_hash_func or $hash_func. + * + * @param string $document document, signature of which must be validated + * @param string $signature signature, which must be validated + * @param object $public_key public key (object of Crypt_RSA_Key class) + * @param string $hash_func hash function, which will be used during validating signature + * @return mixed + * true, if signature of document is valid + * false, if signature of document is invalid + * null on error + * + * @access public + */ + function validateSign($document, $signature, $public_key = null, $hash_func = null) + { + // check public key + if (is_null($public_key)) { + $public_key = $this->_public_key; + } + 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') { + $this->pushError('validating key must be public', CRYPT_RSA_ERROR_NEED_PUB_KEY); + return null; + } + + // check hash_func + if (is_null($hash_func)) { + $hash_func = $this->_hash_func; + } + if (!function_exists($hash_func)) { + $this->pushError("cannot find hash function with name [$hash_func]", CRYPT_RSA_ERROR_WRONG_HASH_FUNC); + return null; + } + + return $hash_func($document) == $this->decrypt($signature, $public_key); + } +} + +?> \ No newline at end of file diff --git a/plugins/OStatus/extlib/Crypt/RSA/ErrorHandler.php b/plugins/OStatus/extlib/Crypt/RSA/ErrorHandler.php new file mode 100644 index 0000000000..8f39741e02 --- /dev/null +++ b/plugins/OStatus/extlib/Crypt/RSA/ErrorHandler.php @@ -0,0 +1,234 @@ + + * @copyright 2005 Alexander Valyalkin + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: ErrorHandler.php,v 1.4 2009/01/05 08:30:29 clockwerx Exp $ + * @link http://pear.php.net/package/Crypt_RSA + */ + +/** + * uses PEAR's error handling + */ +require_once 'PEAR.php'; + +/** + * cannot load required extension for math wrapper + */ +define('CRYPT_RSA_ERROR_NO_EXT', 1); + +/** + * cannot load any math wrappers. + * Possible reasons: + * - there is no any wrappers (they must exist in Crypt/RSA/Math folder ) + * - all available wrappers are incorrect (read docs/Crypt_RSA/docs/math_wrappers.txt ) + * - cannot load any extension, required by available wrappers + */ +define('CRYPT_RSA_ERROR_NO_WRAPPERS', 2); + +/** + * cannot find file, containing requested math wrapper + */ +define('CRYPT_RSA_ERROR_NO_FILE', 3); + +/** + * cannot find math wrapper class in the math wrapper file + */ +define('CRYPT_RSA_ERROR_NO_CLASS', 4); + +/** + * invalid key type passed to function (it must be 'public' or 'private') + */ +define('CRYPT_RSA_ERROR_WRONG_KEY_TYPE', 5); + +/** + * key modulus must be greater than key exponent + */ +define('CRYPT_RSA_ERROR_EXP_GE_MOD', 6); + +/** + * missing $key_len parameter in Crypt_RSA_KeyPair::generate($key_len) function + */ +define('CRYPT_RSA_ERROR_MISSING_KEY_LEN', 7); + +/** + * wrong key object passed to function (it must be an object of Crypt_RSA_Key class) + */ +define('CRYPT_RSA_ERROR_WRONG_KEY', 8); + +/** + * wrong name of hash function passed to Crypt_RSA::setParams() function + */ +define('CRYPT_RSA_ERROR_WRONG_HASH_FUNC', 9); + +/** + * key, used for signing, must be private + */ +define('CRYPT_RSA_ERROR_NEED_PRV_KEY', 10); + +/** + * key, used for sign validating, must be public + */ +define('CRYPT_RSA_ERROR_NEED_PUB_KEY', 11); + +/** + * parameters must be passed to function as associative array + */ +define('CRYPT_RSA_ERROR_WRONG_PARAMS', 12); + +/** + * error tail of decrypted text. Maybe, wrong decryption key? + */ +define('CRYPT_RSA_ERROR_WRONG_TAIL', 13); + +/** + * Crypt_RSA_ErrorHandler class. + * + * This class is used as base for Crypt_RSA, Crypt_RSA_Key + * and Crypt_RSA_KeyPair classes. + * + * It provides following functions: + * - 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($errstr) - pushes $errstr into the error list + * - setErrorHandler($new_error_handler) - sets error handler function + * - getErrorHandler() - returns name of error handler function + * + * @category Encryption + * @package Crypt_RSA + * @author Alexander Valyalkin + * @copyright 2005 Alexander Valyalkin + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Crypt_RSA + * @access public + */ +class Crypt_RSA_ErrorHandler +{ + /** + * array of error objects, pushed by $this->pushError() + * + * @var array + * @access private + */ + var $_errors = array(); + + /** + * name of error handler - function, which calls on $this->pushError() call + * + * @var string + * @access private + */ + var $_error_handler = ''; + + /** + * Returns true if list of errors is not empty, else returns false + * + * @param mixed $err Check if the object is an error + * + * @return bool true, if list of errors is not empty or $err is PEAR_Error object, else false + * @access public + */ + function isError($err = null) + { + return is_null($err) ? (sizeof($this->_errors) > 0) : PEAR::isError($err); + } + + /** + * Returns list of all errors, pushed to error list by $this->pushError() + * + * @return array list of errors (usually it contains objects of PEAR_Error class) + * @access public + */ + function getErrorList() + { + return $this->_errors; + } + + /** + * Returns last error from errors list or false, if list is empty + * + * @return mixed + * last error from errors list (usually it is PEAR_Error object) + * or false, if list is empty. + * + * @access public + */ + function getLastError() + { + $len = sizeof($this->_errors); + return $len ? $this->_errors[$len - 1] : false; + } + + /** + * pushes error object $error to the error list + * + * @param string $errstr error string + * @param int $errno error number + * + * @return bool true on success, false on error + * @access public + */ + function pushError($errstr, $errno = 0) + { + $this->_errors[] = PEAR::raiseError($errstr, $errno); + + if ($this->_error_handler != '') { + // call user defined error handler + $func = $this->_error_handler; + $func($this); + } + return true; + } + + /** + * sets error handler to function with name $func_name. + * Function $func_name must accept one parameter - current + * object, which triggered error. + * + * @param string $func_name name of error handler function + * + * @return bool true on success, false on error + * @access public + */ + function setErrorHandler($func_name = '') + { + if ($func_name == '') { + $this->_error_handler = ''; + } + if (!function_exists($func_name)) { + return false; + } + $this->_error_handler = $func_name; + return true; + } + + /** + * returns name of current error handler, or null if there is no error handler + * + * @return mixed error handler name as string or null, if there is no error handler + * @access public + */ + function getErrorHandler() + { + return $this->_error_handler; + } +} + +?> diff --git a/plugins/OStatus/extlib/Crypt/RSA/Key.php b/plugins/OStatus/extlib/Crypt/RSA/Key.php new file mode 100644 index 0000000000..6595302291 --- /dev/null +++ b/plugins/OStatus/extlib/Crypt/RSA/Key.php @@ -0,0 +1,315 @@ + + * @copyright 2005 Alexander Valyalkin + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: Key.php,v 1.6 2009/01/05 08:30:29 clockwerx Exp $ + * @link http://pear.php.net/package/Crypt_RSA + */ + +/** + * RSA error handling facilities + */ +require_once 'Crypt/RSA/ErrorHandler.php'; + +/** + * loader for RSA math wrappers + */ +require_once 'Crypt/RSA/MathLoader.php'; + +/** + * Crypt_RSA_Key class, derived from Crypt_RSA_ErrorHandler + * + * Provides the following functions: + * - getKeyLength() - returns bit key length + * - getExponent() - returns key exponent as binary string + * - getModulus() - returns key modulus as binary string + * - getKeyType() - returns type of the key (public or private) + * - toString() - returns serialized key as string + * - fromString($key_str) - static function; returns key, unserialized from string + * - isValid($key) - static function for validating of $key + * + * Example usage: + * // create new 1024-bit key pair + * $key_pair = new Crypt_RSA_KeyPair(1024); + * + * // get public key (its class is Crypt_RSA_Key) + * $key = $key_pair->getPublicKey(); + * + * // get key length + * $len = $key->getKeyLength(); + * + * // get modulus as string + * $modulus = $key->getModulus(); + * + * // get exponent as string + * $exponent = $key->getExponent(); + * + * // get string represenation of key (use it instead of serialization of Crypt_RSA_Key object) + * $key_in_str = $key->toString(); + * + * // restore key object from string using 'BigInt' math wrapper + * $key = Crypt_RSA_Key::fromString($key_in_str, 'BigInt'); + * + * // error check + * if ($key->isError()) { + * echo "error while unserializing key object:\n"; + * $erorr = $key->getLastError(); + * echo $error->getMessage(), "\n"; + * } + * + * // validate key + * if (Crypt_RSA_Key::isValid($key)) echo 'valid key'; + * else echo 'invalid key'; + * + * // using factory() method instead of constructor (it returns PEAR_Error object on failure) + * $rsa_obj = &Crypt_RSA_Key::factory($modulus, $exp, $key_type); + * if (PEAR::isError($rsa_obj)) { + * echo "error: ", $rsa_obj->getMessage(), "\n"; + * } + * + * @category Encryption + * @package Crypt_RSA + * @author Alexander Valyalkin + * @copyright 2005 Alexander Valyalkin + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Crypt_RSA + * @access public + */ +class Crypt_RSA_Key extends Crypt_RSA_ErrorHandler +{ + /** + * Reference to math wrapper object, which is used to + * manipulate large integers in RSA algorithm. + * + * @var object of Crypt_RSA_Math_* class + * @access private + */ + var $_math_obj; + + /** + * shared modulus + * + * @var string + * @access private + */ + var $_modulus; + + /** + * exponent + * + * @var string + * @access private + */ + var $_exp; + + /** + * key type (private or public) + * + * @var string + * @access private + */ + var $_key_type; + + /** + * key length in bits + * + * @var int + * @access private + */ + var $_key_len; + + /** + * Crypt_RSA_Key constructor. + * + * You should pass in the name of math wrapper, which will be used to + * perform different operations with big integers. + * See contents of Crypt/RSA/Math folder for examples of wrappers. + * Read docs/Crypt_RSA/docs/math_wrappers.txt for details. + * + * @param string $modulus key modulus + * @param string $exp key exponent + * @param string $key_type type of the key (public or private) + * @param string $wrapper_name wrapper to use + * @param string $error_handler name of error handler function + * + * @access public + */ + function Crypt_RSA_Key($modulus, $exp, $key_type, $wrapper_name = 'default', $error_handler = '') + { + // set error handler + $this->setErrorHandler($error_handler); + // try to load math wrapper $wrapper_name + $obj = &Crypt_RSA_MathLoader::loadWrapper($wrapper_name); + if ($this->isError($obj)) { + // error during loading of math wrapper + $this->pushError($obj); // push error object into error list + return; + } + $this->_math_obj = &$obj; + + $this->_modulus = $modulus; + $this->_exp = $exp; + + if (!in_array($key_type, array('private', 'public'))) { + $this->pushError('invalid key type. It must be private or public', CRYPT_RSA_ERROR_WRONG_KEY_TYPE); + return; + } + $this->_key_type = $key_type; + + /* check length of modulus & exponent ( abs(modulus) > abs(exp) ) */ + $mod_num = $this->_math_obj->bin2int($this->_modulus); + $exp_num = $this->_math_obj->bin2int($this->_exp); + + if ($this->_math_obj->cmpAbs($mod_num, $exp_num) <= 0) { + $this->pushError('modulus must be greater than exponent', CRYPT_RSA_ERROR_EXP_GE_MOD); + return; + } + + // determine key length + $this->_key_len = $this->_math_obj->bitLen($mod_num); + } + + /** + * Crypt_RSA_Key factory. + * + * @param string $modulus key modulus + * @param string $exp key exponent + * @param string $key_type type of the key (public or private) + * @param string $wrapper_name wrapper to use + * @param string $error_handler name of error handler function + * + * @return object new Crypt_RSA_Key object on success or PEAR_Error object on failure + * @access public + */ + function factory($modulus, $exp, $key_type, $wrapper_name = 'default', $error_handler = '') + { + $obj = new Crypt_RSA_Key($modulus, $exp, $key_type, $wrapper_name, $error_handler); + if ($obj->isError()) { + // error during creating a new object. Retrurn PEAR_Error object + return $obj->getLastError(); + } + // object created successfully. Return it + return $obj; + } + + /** + * Calculates bit length of the key + * + * @return int bit length of key + * @access public + */ + function getKeyLength() + { + return $this->_key_len; + } + + /** + * Returns modulus part of the key as binary string, + * which can be used to construct new Crypt_RSA_Key object. + * + * @return string modulus as binary string + * @access public + */ + function getModulus() + { + return $this->_modulus; + } + + /** + * Returns exponent part of the key as binary string, + * which can be used to construct new Crypt_RSA_Key object. + * + * @return string exponent as binary string + * @access public + */ + function getExponent() + { + return $this->_exp; + } + + /** + * Returns key type (public, private) + * + * @return string key type (public, private) + * @access public + */ + function getKeyType() + { + return $this->_key_type; + } + + /** + * Returns string representation of key + * + * @return string key, serialized to string + * @access public + */ + function toString() + { + return base64_encode( + serialize( + array($this->_modulus, $this->_exp, $this->_key_type) + ) + ); + } + + /** + * Returns Crypt_RSA_Key object, unserialized from + * string representation of key. + * + * optional parameter $wrapper_name - is the name of math wrapper, + * which will be used during unserialization of this object. + * + * This function can be called statically: + * $key = Crypt_RSA_Key::fromString($key_in_string, 'BigInt'); + * + * @param string $key_str RSA key, serialized into string + * @param string $wrapper_name optional math wrapper name + * + * @return object key as Crypt_RSA_Key object + * @access public + * @static + */ + function fromString($key_str, $wrapper_name = 'default') + { + list($modulus, $exponent, $key_type) = unserialize(base64_decode($key_str)); + $obj = new Crypt_RSA_Key($modulus, $exponent, $key_type, $wrapper_name); + return $obj; + } + + /** + * Validates key + * This function can be called statically: + * $is_valid = Crypt_RSA_Key::isValid($key) + * + * Returns true, if $key is valid Crypt_RSA key, else returns false + * + * @param object $key Crypt_RSA_Key object for validating + * + * @return bool true if $key is valid, else false + * @access public + */ + function isValid($key) + { + return (is_object($key) && strtolower(get_class($key)) === strtolower(__CLASS__)); + } +} + +?> diff --git a/plugins/OStatus/extlib/Crypt/RSA/KeyPair.php b/plugins/OStatus/extlib/Crypt/RSA/KeyPair.php new file mode 100644 index 0000000000..ecc0b7dc7e --- /dev/null +++ b/plugins/OStatus/extlib/Crypt/RSA/KeyPair.php @@ -0,0 +1,804 @@ + + * @copyright 2005 Alexander Valyalkin + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: KeyPair.php,v 1.7 2009/01/05 08:30:29 clockwerx Exp $ + * @link http://pear.php.net/package/Crypt_RSA + */ + +/** + * RSA error handling facilities + */ +require_once 'Crypt/RSA/ErrorHandler.php'; + +/** + * loader for RSA math wrappers + */ +require_once 'Crypt/RSA/MathLoader.php'; + +/** + * helper class for single key managing + */ +require_once 'Crypt/RSA/Key.php'; + +/** + * Crypt_RSA_KeyPair class, derived from Crypt_RSA_ErrorHandler + * + * Provides the following functions: + * - generate($key) - generates new key pair + * - getPublicKey() - returns public key + * - getPrivateKey() - returns private key + * - getKeyLength() - returns bit key length + * - setRandomGenerator($func_name) - sets random generator to $func_name + * - 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 + * $key_pair = new Crypt_RSA_KeyPair(1024); + * + * // error check + * if ($key_pair->isError()) { + * echo "error while initializing Crypt_RSA_KeyPair object:\n"; + * $erorr = $key_pair->getLastError(); + * echo $error->getMessage(), "\n"; + * } + * + * // get public key + * $public_key = $key_pair->getPublicKey(); + * + * // get private key + * $private_key = $key_pair->getPrivateKey(); + * + * // generate new 512-bit key pair + * $key_pair->generate(512); + * + * // error check + * if ($key_pair->isError()) { + * echo "error while generating key pair:\n"; + * $erorr = $key_pair->getLastError(); + * echo $error->getMessage(), "\n"; + * } + * + * // get key pair length + * $length = $key_pair->getKeyLength(); + * + * // set random generator to $func_name, where $func_name + * // consists name of random generator function. See comments + * // before setRandomGenerator() method for details + * $key_pair->setRandomGenerator($func_name); + * + * // error check + * if ($key_pair->isError()) { + * echo "error while changing random generator:\n"; + * $erorr = $key_pair->getLastError(); + * echo $error->getMessage(), "\n"; + * } + * + * // using factory() method instead of constructor (it returns PEAR_Error object on failure) + * $rsa_obj = &Crypt_RSA_KeyPair::factory($key_len); + * if (PEAR::isError($rsa_obj)) { + * echo "error: ", $rsa_obj->getMessage(), "\n"; + * } + * + * // read key pair from PEM-encoded string: + * $str = "-----BEGIN RSA PRIVATE KEY-----" + * . "MCsCAQACBHr5LDkCAwEAAQIEBc6jbQIDAOCfAgMAjCcCAk3pAgJMawIDAL41" + * . "-----END RSA PRIVATE KEY-----"; + * $keypair = Crypt_RSA_KeyPair::fromPEMString($str); + * + * // read key pair from .pem file 'private.pem': + * $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 + * @copyright 2005 Alexander Valyalkin + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Crypt_RSA + * @access public + */ +class Crypt_RSA_KeyPair extends Crypt_RSA_ErrorHandler +{ + /** + * Reference to math wrapper object, which is used to + * manipulate large integers in RSA algorithm. + * + * @var object of Crypt_RSA_Math_* class + * @access private + */ + var $_math_obj; + + /** + * length of each key in the key pair + * + * @var int + * @access private + */ + var $_key_len; + + /** + * public key + * + * @var object of Crypt_RSA_KEY class + * @access private + */ + var $_public_key; + + /** + * private key + * + * @var object of Crypt_RSA_KEY class + * @access private + */ + var $_private_key; + + /** + * name of function, which is used as random generator + * + * @var string + * @access private + */ + var $_random_generator; + + /** + * 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 Crypt_RSA_ErrorHandler &$err_handler + * + * @return mixed Array('tag' => ..., 'str' => ...) on success, false on error + * @access private + */ + function _ASN1Parse($str, &$pos, &$err_handler) + { + $max_pos = strlen($str); + if ($max_pos < 2) { + $err_handler->pushError("ASN.1 string too short"); + return false; + } + + // get ASN.1 tag value + $tag = ord($str[$pos++]) & 0x1f; + if ($tag == 0x1f) { + $tag = 0; + do { + $n = ord($str[$pos++]); + $tag <<= 7; + $tag |= $n & 0x7f; + } while (($n & 0x80) && $pos < $max_pos); + } + if ($pos >= $max_pos) { + $err_handler->pushError("ASN.1 string too short"); + return false; + } + + // get ASN.1 object length + $len = ord($str[$pos++]); + if ($len & 0x80) { + $n = $len & 0x1f; + $len = 0; + while ($n-- && $pos < $max_pos) { + $len <<= 8; + $len |= ord($str[$pos++]); + } + } + if ($pos >= $max_pos || $len > $max_pos - $pos) { + $err_handler->pushError("ASN.1 string too short"); + return false; + } + + // get string value of ASN.1 object + $str = substr($str, $pos, $len); + + return array( + 'tag' => $tag, + 'str' => $str, + ); + } + + /** + * 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 Crypt_RSA_ErrorHandler &$err_handler + * + * @return mixed string representation of parsed number on success, false on error + * @access private + */ + function _ASN1ParseInt($str, &$pos, &$err_handler) + { + $tmp = Crypt_RSA_KeyPair::_ASN1Parse($str, $pos, $err_handler); + if ($err_handler->isError()) { + return false; + } + if ($tmp['tag'] != 0x02) { + $errstr = sprintf("wrong ASN tag value: 0x%02x. Expected 0x02 (INTEGER)", $tmp['tag']); + $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. + * + * Wrapper: name of math wrapper, which will be used to + * perform different operations with big integers. + * See contents of Crypt/RSA/Math folder for examples of wrappers. + * Read docs/Crypt_RSA/docs/math_wrappers.txt for details. + * + * @param int $key_len bit length of key pair, which will be generated in constructor + * @param string $wrapper_name wrapper name + * @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 = '', $random_generator = null) + { + // set error handler + $this->setErrorHandler($error_handler); + // try to load math wrapper + $obj = &Crypt_RSA_MathLoader::loadWrapper($wrapper_name); + if ($this->isError($obj)) { + // error during loading of math wrapper + $this->pushError($obj); + return; + } + $this->_math_obj = &$obj; + + // 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 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($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($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()); + return; + } + + $this->_public_key = $public_key; + $this->_private_key = $private_key; + $this->_key_len = $public_key->getKeyLength(); + $this->_attrs = $rsa_attrs; + } else { + // generate key pair + if (!$this->generate($key_len)) { + // error during generating key pair + return; + } + } + } + + /** + * Crypt_RSA_KeyPair factory. + * + * Wrapper - Name of math wrapper, which will be used to + * perform different operations with big integers. + * See contents of Crypt/RSA/Math folder for examples of wrappers. + * Read docs/Crypt_RSA/docs/math_wrappers.txt for details. + * + * @param int $key_len bit length of key pair, which will be generated in constructor + * @param string $wrapper_name wrapper name + * @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 = '', $random_generator = null) + { + $obj = &new Crypt_RSA_KeyPair($key_len, $wrapper_name, $error_handler, $random_generator); + if ($obj->isError()) { + // error during creating a new object. Return PEAR_Error object + return $obj->getLastError(); + } + // object created successfully. Return it + return $obj; + } + + /** + * Generates new Crypt_RSA key pair with length $key_len. + * If $key_len is missed, use an old key length from $this->_key_len + * + * @param int $key_len bit length of key pair, which will be generated + * + * @return bool true on success or false on error + * @access public + */ + function generate($key_len = null) + { + if (is_null($key_len)) { + // use an old key length + $key_len = $this->_key_len; + if (is_null($key_len)) { + $this->pushError('missing key_len parameter', CRYPT_RSA_ERROR_MISSING_KEY_LEN); + return false; + } + } + + // 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; + + // set [e] to 0x10001 (65537) + $e = $this->_math_obj->bin2int("\x01\x00\x01"); + + // generate [p], [q] and [n] + $p_len = intval(($key_len + 1) / 2); + $q_len = $key_len - $p_len; + $p1 = $q1 = 0; + do { + // generate prime number [$p] with length [$p_len] with the following condition: + // GCD($e, $p - 1) = 1 + do { + $p = $this->_math_obj->getPrime($p_len, $this->_random_generator); + $p1 = $this->_math_obj->dec($p); + $tmp = $this->_math_obj->GCD($e, $p1); + } while (!$this->_math_obj->isOne($tmp)); + // generate prime number [$q] with length [$q_len] with the following conditions: + // GCD($e, $q - 1) = 1 + // $q != $p + do { + $q = $this->_math_obj->getPrime($q_len, $this->_random_generator); + $q1 = $this->_math_obj->dec($q); + $tmp = $this->_math_obj->GCD($e, $q1); + } while (!$this->_math_obj->isOne($tmp) && !$this->_math_obj->cmpAbs($q, $p)); + // if (p < q), then exchange them + if ($this->_math_obj->cmpAbs($p, $q) < 0) { + $tmp = $p; + $p = $q; + $q = $tmp; + $tmp = $p1; + $p1 = $q1; + $q1 = $tmp; + } + // calculate n = p * q + $n = $this->_math_obj->mul($p, $q); + } while ($this->_math_obj->bitLen($n) != $key_len); + + // calculate d = 1/e mod (p - 1) * (q - 1) + $pq = $this->_math_obj->mul($p1, $q1); + $d = $this->_math_obj->invmod($e, $pq); + + // 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($n, $e, 'public', $this->_math_obj->getWrapperName(), $this->_error_handler); + if ($obj->isError()) { + // error during creating public object + $this->pushError($obj->getLastError()); + return false; + } + $this->_public_key = &$obj; + + // try to create private key object + $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()); + return false; + } + $this->_private_key = &$obj; + + return true; // key pair successfully generated + } + + /** + * Returns public key from the pair + * + * @return object public key object of class Crypt_RSA_Key + * @access public + */ + function getPublicKey() + { + return $this->_public_key; + } + + /** + * Returns private key from the pair + * + * @return object private key object of class Crypt_RSA_Key + * @access public + */ + function getPrivateKey() + { + return $this->_private_key; + } + + /** + * Sets name of random generator function for key generation. + * If parameter is skipped, then sets to default random generator. + * + * Random generator function must return integer with at least 8 lower + * significant bits, which will be used as random values. + * + * @param string $random_generator name of random generator function + * + * @return bool true on success or false on error + * @access public + */ + function setRandomGenerator($random_generator = null) + { + static $default_random_generator = null; + + if (is_string($random_generator)) { + // set user's random generator + if (!function_exists($random_generator)) { + $this->pushError("can't find random generator function with name [{$random_generator}]"); + return false; + } + $this->_random_generator = $random_generator; + } 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);')) : + $default_random_generator; + } + return true; + } + + /** + * Returns length of each key in the key pair + * + * @return int bit length of each key in key pair + * @access public + */ + function getKeyLength() + { + return $this->_key_len; + } + + /** + * Retrieves RSA keypair from PEM-encoded string, containing RSA private key. + * Example of such string: + * -----BEGIN RSA PRIVATE KEY----- + * MCsCAQACBHtvbSECAwEAAQIEeYrk3QIDAOF3AgMAjCcCAmdnAgJMawIDALEk + * -----END RSA PRIVATE KEY----- + * + * Wrapper: Name of math wrapper, which will be used to + * perform different operations with big integers. + * See contents of Crypt/RSA/Math folder for examples of wrappers. + * Read docs/Crypt_RSA/docs/math_wrappers.txt for details. + * + * @param string $str PEM-encoded string + * @param string $wrapper_name Wrapper name + * @param string $error_handler name of error handler function + * + * @return Crypt_RSA_KeyPair object on success, PEAR_Error object on error + * @access public + * @static + */ + function &fromPEMString($str, $wrapper_name = 'default', $error_handler = '') + { + 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); + + // 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 ($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_handler->pushError($errstr); + return $err_handler->getLastError(); + } + + // 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; + } + + // create Crypt_RSA_KeyPair object. + $keypair = &new Crypt_RSA_KeyPair($rsa_attrs, $wrapper_name, $error_handler); + if ($keypair->isError()) { + return $keypair->getLastError(); + } + + return $keypair; + } + + /** + * 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); + } + + // prepend $str by ASN.1 SEQUENCE (0x10) header + $str = Crypt_RSA_KeyPair::_ASN1Store($str, 0x10, true); + + // 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 $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; + } +} + +?> diff --git a/plugins/OStatus/extlib/Crypt/RSA/Math/BCMath.php b/plugins/OStatus/extlib/Crypt/RSA/Math/BCMath.php new file mode 100644 index 0000000000..646ff67103 --- /dev/null +++ b/plugins/OStatus/extlib/Crypt/RSA/Math/BCMath.php @@ -0,0 +1,482 @@ + + * @copyright 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 + */ + +/** + * Crypt_RSA_Math_BCMath class. + * + * Provides set of math functions, which are used by Crypt_RSA package + * This class is a wrapper for PHP BCMath extension. + * See http://php.net/manual/en/ref.bc.php for details. + * + * @category Encryption + * @package Crypt_RSA + * @author Alexander Valyalkin + * @copyright 2005, 2006 Alexander Valyalkin + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @link http://pear.php.net/package/Crypt_RSA + * @version @package_version@ + * @access public + */ +class Crypt_RSA_Math_BCMath +{ + /** + * error description + * + * @var string + * @access public + */ + var $errstr = ''; + + /** + * Performs Miller-Rabin primality test for number $num + * with base $base. Returns true, if $num is strong pseudoprime + * by base $base. Else returns false. + * + * @param string $num + * @param string $base + * @return bool + * @access private + */ + function _millerTest($num, $base) + { + if (!bccomp($num, '1')) { + // 1 is not prime ;) + return false; + } + $tmp = bcsub($num, '1'); + + $zero_bits = 0; + while (!bccomp(bcmod($tmp, '2'), '0')) { + $zero_bits++; + $tmp = bcdiv($tmp, '2'); + } + + $tmp = $this->powmod($base, $tmp, $num); + if (!bccomp($tmp, '1')) { + // $num is probably prime + return true; + } + + while ($zero_bits--) { + if (!bccomp(bcadd($tmp, '1'), $num)) { + // $num is probably prime + return true; + } + $tmp = $this->powmod($tmp, '2', $num); + } + // $num is composite + return false; + } + + /** + * Crypt_RSA_Math_BCMath constructor. + * Checks an existance of PHP BCMath extension. + * On failure saves error description in $this->errstr + * + * @access public + */ + function Crypt_RSA_Math_BCMath() + { + if (!extension_loaded('bcmath')) { + if (!@dl('bcmath.' . PHP_SHLIB_SUFFIX) && !@dl('php_bcmath.' . PHP_SHLIB_SUFFIX)) { + // cannot load BCMath extension. Set error string + $this->errstr = 'Crypt_RSA package requires the BCMath extension. See http://php.net/manual/en/ref.bc.php for details'; + return; + } + } + } + + /** + * Transforms binary representation of large integer into its native form. + * + * Example of transformation: + * $str = "\x12\x34\x56\x78\x90"; + * $num = 0x9078563412; + * + * @param string $str + * @return string + * @access public + */ + function bin2int($str) + { + $result = '0'; + $n = strlen($str); + do { + $result = bcadd(bcmul($result, '256'), ord($str{--$n})); + } while ($n > 0); + return $result; + } + + /** + * Transforms large integer into binary representation. + * + * Example of transformation: + * $num = 0x9078563412; + * $str = "\x12\x34\x56\x78\x90"; + * + * @param string $num + * @return string + * @access public + */ + function int2bin($num) + { + $result = ''; + do { + $result .= chr(bcmod($num, '256')); + $num = bcdiv($num, '256'); + } while (bccomp($num, '0')); + return $result; + } + + /** + * Calculates pow($num, $pow) (mod $mod) + * + * @param string $num + * @param string $pow + * @param string $mod + * @return string + * @access public + */ + function powmod($num, $pow, $mod) + { + if (function_exists('bcpowmod')) { + // bcpowmod is only available under PHP5 + return bcpowmod($num, $pow, $mod); + } + + // emulate bcpowmod + $result = '1'; + do { + if (!bccomp(bcmod($pow, '2'), '1')) { + $result = bcmod(bcmul($result, $num), $mod); + } + $num = bcmod(bcpow($num, '2'), $mod); + $pow = bcdiv($pow, '2'); + } while (bccomp($pow, '0')); + return $result; + } + + /** + * Calculates $num1 * $num2 + * + * @param string $num1 + * @param string $num2 + * @return string + * @access public + */ + function mul($num1, $num2) + { + return bcmul($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) + * 0, if abs($num1) == abs($num2) + * 1, if abs($num1) > abs($num2) + * + * @param string $num1 + * @param string $num2 + * @return int + * @access public + */ + function cmpAbs($num1, $num2) + { + return bccomp($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. + * + * @param int $bits_cnt + * @param string $rnd_generator + * @access public + */ + function getPrime($bits_cnt, $random_generator) + { + $bytes_n = intval($bits_cnt / 8); + $bits_n = $bits_cnt % 8; + do { + $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] + if (!bccomp(bcmod($num, '2'), '0')) { + $num = bcadd($num, '1'); + } + while (!$this->isPrime($num)) { + $num = bcadd($num, '2'); + } + } while ($this->bitLen($num) != $bits_cnt); + return $num; + } + + /** + * Calculates $num - 1 + * + * @param string $num + * @return string + * @access public + */ + function dec($num) + { + return bcsub($num, '1'); + } + + /** + * Returns true, if $num is equal to one. Else returns false + * + * @param string $num + * @return bool + * @access public + */ + function isOne($num) + { + return !bccomp($num, '1'); + } + + /** + * Finds greatest common divider (GCD) of $num1 and $num2 + * + * @param string $num1 + * @param string $num2 + * @return string + * @access public + */ + function GCD($num1, $num2) + { + do { + $tmp = bcmod($num1, $num2); + $num1 = $num2; + $num2 = $tmp; + } while (bccomp($num2, '0')); + return $num1; + } + + /** + * Finds inverse number $inv for $num by modulus $mod, such as: + * $inv * $num = 1 (mod $mod) + * + * @param string $num + * @param string $mod + * @return string + * @access public + */ + function invmod($num, $mod) + { + $x = '1'; + $y = '0'; + $num1 = $mod; + do { + $tmp = bcmod($num, $num1); + $q = bcdiv($num, $num1); + $num = $num1; + $num1 = $tmp; + + $tmp = bcsub($x, bcmul($y, $q)); + $x = $y; + $y = $tmp; + } while (bccomp($num1, '0')); + if (bccomp($x, '0') < 0) { + $x = bcadd($x, $mod); + } + return $x; + } + + /** + * Returns bit length of number $num + * + * @param string $num + * @return int + * @access public + */ + function bitLen($num) + { + $tmp = $this->int2bin($num); + $bit_len = strlen($tmp) * 8; + $tmp = ord($tmp{strlen($tmp) - 1}); + if (!$tmp) { + $bit_len -= 8; + } + else { + while (!($tmp & 0x80)) { + $bit_len--; + $tmp <<= 1; + } + } + return $bit_len; + } + + /** + * Calculates bitwise or of $num1 and $num2, + * starting from bit $start_pos for number $num1 + * + * @param string $num1 + * @param string $num2 + * @param int $start_pos + * @return string + * @access public + */ + function bitOr($num1, $num2, $start_pos) + { + $start_byte = intval($start_pos / 8); + $start_bit = $start_pos % 8; + $tmp1 = $this->int2bin($num1); + + $num2 = bcmul($num2, 1 << $start_bit); + $tmp2 = $this->int2bin($num2); + if ($start_byte < strlen($tmp1)) { + $tmp2 |= substr($tmp1, $start_byte); + $tmp1 = substr($tmp1, 0, $start_byte) . $tmp2; + } + else { + $tmp1 = str_pad($tmp1, $start_byte, "\0") . $tmp2; + } + return $this->bin2int($tmp1); + } + + /** + * Returns part of number $num, starting at bit + * position $start with length $length + * + * @param string $num + * @param int start + * @param int length + * @return string + * @access public + */ + function subint($num, $start, $length) + { + $start_byte = intval($start / 8); + $start_bit = $start % 8; + $byte_length = intval($length / 8); + $bit_length = $length % 8; + if ($bit_length) { + $byte_length++; + } + $num = bcdiv($num, 1 << $start_bit); + $tmp = substr($this->int2bin($num), $start_byte, $byte_length); + $tmp = str_pad($tmp, $byte_length, "\0"); + $tmp = substr_replace($tmp, $tmp{$byte_length - 1} & chr(0xff >> (8 - $bit_length)), $byte_length - 1, 1); + return $this->bin2int($tmp); + } + + /** + * Returns name of current wrapper + * + * @return string name of current wrapper + * @access public + */ + function getWrapperName() + { + return 'BCMath'; + } +} + +?> \ No newline at end of file diff --git a/plugins/OStatus/extlib/Crypt/RSA/Math/BigInt.php b/plugins/OStatus/extlib/Crypt/RSA/Math/BigInt.php new file mode 100644 index 0000000000..b7ac24cb66 --- /dev/null +++ b/plugins/OStatus/extlib/Crypt/RSA/Math/BigInt.php @@ -0,0 +1,313 @@ + + * @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 + */ + +/** + * Crypt_RSA_Math_BigInt class. + * + * Provides set of math functions, which are used by Crypt_RSA package + * This class is a wrapper for big_int PECL extension, + * which could be loaded from http://pecl.php.net/packages/big_int + * + * @category Encryption + * @package Crypt_RSA + * @author Alexander Valyalkin + * @copyright 2005, 2006 Alexander Valyalkin + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @link http://pear.php.net/package/Crypt_RSA + * @version @package_version@ + * @access public + */ +class Crypt_RSA_Math_BigInt +{ + /** + * error description + * + * @var string + * @access public + */ + var $errstr = ''; + + /** + * Crypt_RSA_Math_BigInt constructor. + * Checks an existance of big_int PECL math package. + * This package is available at http://pecl.php.net/packages/big_int + * On failure saves error description in $this->errstr + * + * @access public + */ + function Crypt_RSA_Math_BigInt() + { + if (!extension_loaded('big_int')) { + if (!@dl('big_int.' . PHP_SHLIB_SUFFIX) && !@dl('php_big_int.' . PHP_SHLIB_SUFFIX)) { + // cannot load big_int extension + $this->errstr = 'Crypt_RSA package requires big_int PECL package. ' . + 'It is available at http://pecl.php.net/packages/big_int'; + return; + } + } + + // check version of big_int extension ( Crypt_RSA requires version 1.0.2 and higher ) + if (!in_array('bi_info', get_extension_funcs('big_int'))) { + // there is no bi_info() function in versions, older than 1.0.2 + $this->errstr = 'Crypt_RSA package requires big_int package version 1.0.2 and higher'; + } + } + + /** + * Transforms binary representation of large integer into its native form. + * + * Example of transformation: + * $str = "\x12\x34\x56\x78\x90"; + * $num = 0x9078563412; + * + * @param string $str + * @return big_int resource + * @access public + */ + function bin2int($str) + { + return bi_unserialize($str); + } + + /** + * Transforms large integer into binary representation. + * + * Example of transformation: + * $num = 0x9078563412; + * $str = "\x12\x34\x56\x78\x90"; + * + * @param big_int resource $num + * @return string + * @access public + */ + function int2bin($num) + { + return bi_serialize($num); + } + + /** + * Calculates pow($num, $pow) (mod $mod) + * + * @param big_int resource $num + * @param big_int resource $pow + * @param big_int resource $mod + * @return big_int resource + * @access public + */ + function powmod($num, $pow, $mod) + { + return bi_powmod($num, $pow, $mod); + } + + /** + * Calculates $num1 * $num2 + * + * @param big_int resource $num1 + * @param big_int resource $num2 + * @return big_int resource + * @access public + */ + function mul($num1, $num2) + { + return bi_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) + * 0, if abs($num1) == abs($num2) + * 1, if abs($num1) > abs($num2) + * + * @param big_int resource $num1 + * @param big_int resource $num2 + * @return int + * @access public + */ + function cmpAbs($num1, $num2) + { + return bi_cmp_abs($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. + * + * @param int $bits_cnt + * @param string $rnd_generator + * @access public + */ + function getPrime($bits_cnt, $random_generator) + { + $bytes_n = intval($bits_cnt / 8); + $bits_n = $bits_cnt % 8; + do { + $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 ($this->bitLen($num) != $bits_cnt); + return $num; + } + + /** + * Calculates $num - 1 + * + * @param big_int resource $num + * @return big_int resource + * @access public + */ + function dec($num) + { + return bi_dec($num); + } + + /** + * Returns true, if $num is equal to 1. Else returns false + * + * @param big_int resource $num + * @return bool + * @access public + */ + function isOne($num) + { + return bi_is_one($num); + } + + /** + * Finds greatest common divider (GCD) of $num1 and $num2 + * + * @param big_int resource $num1 + * @param big_int resource $num2 + * @return big_int resource + * @access public + */ + function GCD($num1, $num2) + { + return bi_gcd($num1, $num2); + } + + /** + * Finds inverse number $inv for $num by modulus $mod, such as: + * $inv * $num = 1 (mod $mod) + * + * @param big_int resource $num + * @param big_int resource $mod + * @return big_int resource + * @access public + */ + function invmod($num, $mod) + { + return bi_invmod($num, $mod); + } + + /** + * Returns bit length of number $num + * + * @param big_int resource $num + * @return int + * @access public + */ + function bitLen($num) + { + return bi_bit_len($num); + } + + /** + * Calculates bitwise or of $num1 and $num2, + * starting from bit $start_pos for number $num1 + * + * @param big_int resource $num1 + * @param big_int resource $num2 + * @param int $start_pos + * @return big_int resource + * @access public + */ + function bitOr($num1, $num2, $start_pos) + { + return bi_or($num1, $num2, $start_pos); + } + + /** + * Returns part of number $num, starting at bit + * position $start with length $length + * + * @param big_int resource $num + * @param int start + * @param int length + * @return big_int resource + * @access public + */ + function subint($num, $start, $length) + { + return bi_subint($num, $start, $length); + } + + /** + * Returns name of current wrapper + * + * @return string name of current wrapper + * @access public + */ + function getWrapperName() + { + return 'BigInt'; + } +} + +?> \ No newline at end of file diff --git a/plugins/OStatus/extlib/Crypt/RSA/Math/GMP.php b/plugins/OStatus/extlib/Crypt/RSA/Math/GMP.php new file mode 100644 index 0000000000..54e4c34fce --- /dev/null +++ b/plugins/OStatus/extlib/Crypt/RSA/Math/GMP.php @@ -0,0 +1,361 @@ + + * @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 + */ + +/** + * Crypt_RSA_Math_GMP class. + * + * Provides set of math functions, which are used by Crypt_RSA package + * This class is a wrapper for PHP GMP extension. + * See http://php.net/gmp for details. + * + * @category Encryption + * @package Crypt_RSA + * @author Alexander Valyalkin + * @copyright 2005, 2006 Alexander Valyalkin + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @link http://pear.php.net/package/Crypt_RSA + * @version @package_version@ + * @access public + */ +class Crypt_RSA_Math_GMP +{ + /** + * error description + * + * @var string + * @access public + */ + var $errstr = ''; + + /** + * Crypt_RSA_Math_GMP constructor. + * Checks an existance of PHP GMP package. + * See http://php.net/gmp for details. + * + * On failure saves error description in $this->errstr + * + * @access public + */ + function Crypt_RSA_Math_GMP() + { + if (!extension_loaded('gmp')) { + if (!@dl('gmp.' . PHP_SHLIB_SUFFIX) && !@dl('php_gmp.' . PHP_SHLIB_SUFFIX)) { + // cannot load GMP extension + $this->errstr = 'Crypt_RSA package requires PHP GMP package. ' . + 'See http://php.net/gmp for details'; + return; + } + } + } + + /** + * Transforms binary representation of large integer into its native form. + * + * Example of transformation: + * $str = "\x12\x34\x56\x78\x90"; + * $num = 0x9078563412; + * + * @param string $str + * @return gmp resource + * @access public + */ + function bin2int($str) + { + $result = 0; + $n = strlen($str); + do { + // dirty hack: GMP returns FALSE, when second argument equals to int(0). + // so, it must be converted to string '0' + $result = gmp_add(gmp_mul($result, 256), strval(ord($str{--$n}))); + } while ($n > 0); + return $result; + } + + /** + * Transforms large integer into binary representation. + * + * Example of transformation: + * $num = 0x9078563412; + * $str = "\x12\x34\x56\x78\x90"; + * + * @param gmp resource $num + * @return string + * @access public + */ + function int2bin($num) + { + $result = ''; + do { + $result .= chr(gmp_intval(gmp_mod($num, 256))); + $num = gmp_div($num, 256); + } while (gmp_cmp($num, 0)); + return $result; + } + + /** + * Calculates pow($num, $pow) (mod $mod) + * + * @param gmp resource $num + * @param gmp resource $pow + * @param gmp resource $mod + * @return gmp resource + * @access public + */ + function powmod($num, $pow, $mod) + { + return gmp_powm($num, $pow, $mod); + } + + /** + * Calculates $num1 * $num2 + * + * @param gmp resource $num1 + * @param gmp resource $num2 + * @return gmp resource + * @access public + */ + function mul($num1, $num2) + { + return gmp_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) + * 0, if abs($num1) == abs($num2) + * 1, if abs($num1) > abs($num2) + * + * @param gmp resource $num1 + * @param gmp resource $num2 + * @return int + * @access public + */ + function cmpAbs($num1, $num2) + { + return gmp_cmp($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. + * + * @param int $bits_cnt + * @param string $rnd_generator + * @access public + */ + function getPrime($bits_cnt, $random_generator) + { + $bytes_n = intval($bits_cnt / 8); + $bits_n = $bits_cnt % 8; + do { + $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] + if (!gmp_cmp(gmp_mod($num, '2'), '0')) { + $num = gmp_add($num, '1'); + } + while (!gmp_prob_prime($num)) { + $num = gmp_add($num, '2'); + } + } while ($this->bitLen($num) != $bits_cnt); + return $num; + } + + /** + * Calculates $num - 1 + * + * @param gmp resource $num + * @return gmp resource + * @access public + */ + function dec($num) + { + return gmp_sub($num, 1); + } + + /** + * Returns true, if $num is equal to one. Else returns false + * + * @param gmp resource $num + * @return bool + * @access public + */ + function isOne($num) + { + return !gmp_cmp($num, 1); + } + + /** + * Finds greatest common divider (GCD) of $num1 and $num2 + * + * @param gmp resource $num1 + * @param gmp resource $num2 + * @return gmp resource + * @access public + */ + function GCD($num1, $num2) + { + return gmp_gcd($num1, $num2); + } + + /** + * Finds inverse number $inv for $num by modulus $mod, such as: + * $inv * $num = 1 (mod $mod) + * + * @param gmp resource $num + * @param gmp resource $mod + * @return gmp resource + * @access public + */ + function invmod($num, $mod) + { + return gmp_invert($num, $mod); + } + + /** + * Returns bit length of number $num + * + * @param gmp resource $num + * @return int + * @access public + */ + function bitLen($num) + { + $tmp = $this->int2bin($num); + $bit_len = strlen($tmp) * 8; + $tmp = ord($tmp{strlen($tmp) - 1}); + if (!$tmp) { + $bit_len -= 8; + } + else { + while (!($tmp & 0x80)) { + $bit_len--; + $tmp <<= 1; + } + } + return $bit_len; + } + + /** + * Calculates bitwise or of $num1 and $num2, + * starting from bit $start_pos for number $num1 + * + * @param gmp resource $num1 + * @param gmp resource $num2 + * @param int $start_pos + * @return gmp resource + * @access public + */ + function bitOr($num1, $num2, $start_pos) + { + $start_byte = intval($start_pos / 8); + $start_bit = $start_pos % 8; + $tmp1 = $this->int2bin($num1); + + $num2 = gmp_mul($num2, 1 << $start_bit); + $tmp2 = $this->int2bin($num2); + if ($start_byte < strlen($tmp1)) { + $tmp2 |= substr($tmp1, $start_byte); + $tmp1 = substr($tmp1, 0, $start_byte) . $tmp2; + } + else { + $tmp1 = str_pad($tmp1, $start_byte, "\0") . $tmp2; + } + return $this->bin2int($tmp1); + } + + /** + * Returns part of number $num, starting at bit + * position $start with length $length + * + * @param gmp resource $num + * @param int start + * @param int length + * @return gmp resource + * @access public + */ + function subint($num, $start, $length) + { + $start_byte = intval($start / 8); + $start_bit = $start % 8; + $byte_length = intval($length / 8); + $bit_length = $length % 8; + if ($bit_length) { + $byte_length++; + } + $num = gmp_div($num, 1 << $start_bit); + $tmp = substr($this->int2bin($num), $start_byte, $byte_length); + $tmp = str_pad($tmp, $byte_length, "\0"); + $tmp = substr_replace($tmp, $tmp{$byte_length - 1} & chr(0xff >> (8 - $bit_length)), $byte_length - 1, 1); + return $this->bin2int($tmp); + } + + /** + * Returns name of current wrapper + * + * @return string name of current wrapper + * @access public + */ + function getWrapperName() + { + return 'GMP'; + } +} + +?> \ No newline at end of file diff --git a/plugins/OStatus/extlib/Crypt/RSA/MathLoader.php b/plugins/OStatus/extlib/Crypt/RSA/MathLoader.php new file mode 100644 index 0000000000..de6c94642f --- /dev/null +++ b/plugins/OStatus/extlib/Crypt/RSA/MathLoader.php @@ -0,0 +1,135 @@ + + * @copyright Alexander Valyalkin 2005 + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: MathLoader.php,v 1.5 2009/01/05 08:30:29 clockwerx Exp $ + * @link http://pear.php.net/package/Crypt_RSA + */ + +/** + * RSA error handling facilities + */ +require_once 'Crypt/RSA/ErrorHandler.php'; + +/** + * Crypt_RSA_MathLoader class. + * + * Provides static function: + * - loadWrapper($wrapper_name) - loads RSA math wrapper with name $wrapper_name + * or most suitable wrapper if $wrapper_name == 'default' + * + * Example usage: + * // load BigInt wrapper + * $big_int_wrapper = Crypt_RSA_MathLoader::loadWrapper('BigInt'); + * + * // load BCMath wrapper + * $bcmath_wrapper = Crypt_RSA_MathLoader::loadWrapper('BCMath'); + * + * // load the most suitable wrapper + * $bcmath_wrapper = Crypt_RSA_MathLoader::loadWrapper(); + * + * @category Encryption + * @package Crypt_RSA + * @author Alexander Valyalkin + * @copyright Alexander Valyalkin 2005 + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Crypt_RSA + * @access public + */ +class Crypt_RSA_MathLoader +{ + /** + * Loads RSA math wrapper with name $wrapper_name. + * Implemented wrappers can be found at Crypt/RSA/Math folder. + * Read docs/Crypt_RSA/docs/math_wrappers.txt for details + * + * This is a static function: + * // load BigInt wrapper + * $big_int_wrapper = &Crypt_RSA_MathLoader::loadWrapper('BigInt'); + * + * // load BCMath wrapper + * $bcmath_wrapper = &Crypt_RSA_MathLoader::loadWrapper('BCMath'); + * + * @param string $wrapper_name Name of wrapper + * + * @return object + * Reference to object of wrapper with name $wrapper_name on success + * or PEAR_Error object on error + * + * @access public + */ + function loadWrapper($wrapper_name = 'default') + { + static $math_objects = array(); + // ordered by performance. GMP is the fastest math library, BCMath - the slowest. + static $math_wrappers = array('GMP', 'BigInt', 'BCMath',); + + if (isset($math_objects[$wrapper_name])) { + /* + wrapper with name $wrapper_name is already loaded and created. + Return reference to existing copy of wrapper + */ + 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 (!$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 + $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)) { + $err_handler->pushError("can't find file [{$class_filename}] for RSA math wrapper [{$wrapper_name}]", CRYPT_RSA_ERROR_NO_FILE); + return $err_handler->getLastError(); + } + + include_once $class_filename; + if (!class_exists($class_name)) { + $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 + $err_handler->pushError($obj->errstr, CRYPT_RSA_ERROR_NO_EXT); + return $err_handler->getLastError(); + } + return $math_objects[$wrapper_name] = $obj; + } +} + +?> diff --git a/plugins/OStatus/lib/magicenvelope.php b/plugins/OStatus/lib/magicenvelope.php new file mode 100644 index 0000000000..1ae80d70c1 --- /dev/null +++ b/plugins/OStatus/lib/magicenvelope.php @@ -0,0 +1,174 @@ +. + * + * @package StatusNet + * @author James Walker + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +require_once 'magicsig.php'; + +class MagicEnvelope +{ + const ENCODING = 'base64url'; + + const NS = 'http://salmon-protocol.org/ns/magic-env'; + + private function normalizeUser($user_id) + { + if (substr($user_id, 0, 5) == 'http:' || + substr($user_id, 0, 6) == 'https:' || + substr($user_id, 0, 5) == 'acct:') { + return $user_id; + } + + if (strpos($user_id, '@') !== FALSE) { + return 'acct:' . $user_id; + } + + return 'http://' . $user_id; + } + + public function getKeyPair($signer_uri) + { + return 'RSA.79_L2gq-TD72Nsb5yGS0r9stLLpJZF5AHXyxzWmQmlqKl276LEJEs8CppcerLcR90MbYQUwt-SX9slx40Yq3vA==.AQAB.AR-jo5KMfSISmDAT2iMs2_vNFgWRjl5rbJVvA0SpGIEWyPdCGxlPtCbTexp8-0ZEIe8a4SyjatBECH5hxgMTpw=='; + } + + + public function signMessage($text, $mimetype, $signer_uri) + { + $signer_uri = $this->normalizeUser($signer_uri); + + if (!$this->checkAuthor($text, $signer_uri)) { + return false; + } + + $signature_alg = new MagicsigRsaSha256($this->getKeyPair($signer_uri)); + $armored_text = base64_encode($text); + + return array( + 'data' => $armored_text, + 'encoding' => MagicEnvelope::ENCODING, + 'data_type' => $mimetype, + 'sig' => $signature_alg->sign($armored_text), + 'alg' => $signature_alg->getName() + ); + + + } + + public function unfold($env) + { + $dom = new DOMDocument(); + $dom->loadXML(base64_decode($env['data'])); + + if ($dom->documentElement->tagName != 'entry') { + return false; + } + + $prov = $dom->createElementNS(MagicEnvelope::NS, 'me:provenance'); + $prov->setAttribute('xmlns:me', MagicEnvelope::NS); + $data = $dom->createElementNS(MagicEnvelope::NS, 'me:data', $env['data']); + $data->setAttribute('type', $env['data_type']); + $prov->appendChild($data); + $enc = $dom->createElementNS(MagicEnvelope::NS, 'me:encoding', $env['encoding']); + $prov->appendChild($enc); + $alg = $dom->createElementNS(MagicEnvelope::NS, 'me:alg', $env['alg']); + $prov->appendChild($alg); + $sig = $dom->createElementNS(MagicEnvelope::NS, 'me:sig', $env['sig']); + $prov->appendChild($sig); + + $dom->documentElement->appendChild($prov); + + return $dom->saveXML(); + } + + public function getAuthor($text) { + $doc = new DOMDocument(); + if (!$doc->loadXML($text)) { + return FALSE; + } + + if ($doc->documentElement->tagName == 'entry') { + $authors = $doc->documentElement->getElementsByTagName('author'); + foreach ($authors as $author) { + $uris = $author->getElementsByTagName('uri'); + foreach ($uris as $uri) { + return $this->normalizeUser($uri->nodeValue); + } + } + } + } + + public function checkAuthor($text, $signer_uri) + { + return ($this->getAuthor($text) == $signer_uri); + } + + public function verify($env) + { + if ($env['alg'] != 'RSA-SHA256') { + return false; + } + + if ($env['encoding'] != MagicEnvelope::ENCODING) { + return false; + } + + $text = base64_decode($env['data']); + $signer_uri = $this->getAuthor($text); + + $verifier = new MagicsigRsaSha256($this->getKeyPair($signer_uri)); + + return $verifier->verify($env['data'], $env['sig']); + } + + public function parse($text) + { + $dom = DOMDocument::loadXML($text); + return $this->fromDom($dom); + } + + public function fromDom($dom) + { + if ($dom->documentElement->tagName == 'entry') { + $env_element = $dom->getElementsByTagNameNS(MagicEnvelope::NS, 'provenance')->item(0); + } else if ($dom->documentElement->tagName == 'me:env') { + $env_element = $dom->documentElement; + } else { + return false; + } + + $data_element = $env_element->getElementsByTagNameNS(MagicEnvelope::NS, 'data')->item(0); + + return array( + 'data' => trim($data_element->nodeValue), + 'data_type' => $data_element->getAttribute('type'), + 'encoding' => $env_element->getElementsByTagNameNS(MagicEnvelope::NS, 'encoding')->item(0)->nodeValue, + 'alg' => $env_element->getElementsByTagNameNS(MagicEnvelope::NS, 'alg')->item(0)->nodeValue, + 'sig' => $env_element->getElementsByTagNameNS(MagicEnvelope::NS, 'sig')->item(0)->nodeValue, + ); + } + +} diff --git a/plugins/OStatus/lib/magicsig.php b/plugins/OStatus/lib/magicsig.php new file mode 100644 index 0000000000..af65bad046 --- /dev/null +++ b/plugins/OStatus/lib/magicsig.php @@ -0,0 +1,159 @@ +. + * + * @package StatusNet + * @author James Walker + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +require_once 'Crypt/RSA.php'; + +interface Magicsig +{ + + public function sign($bytes); + + public function verify($signed, $signature_b64); +} + +class MagicsigRsaSha256 +{ + + public $keypair; + + public function __construct($init = null) + { + if (is_null($init)) { + $this->generate(); + } else { + $this->fromString($init); + } + } + + + public function generate($key_length = 512) + { + $keypair = new Crypt_RSA_KeyPair($key_length); + $params['public_key'] = $keypair->getPublicKey(); + $params['private_key'] = $keypair->getPrivateKey(); + + $this->keypair = new Crypt_RSA($params); + } + + + public function toString($full_pair = true) + { + $public_key = $this->keypair->_public_key; + $private_key = $this->keypair->_private_key; + + $mod = base64_url_encode($public_key->getModulus()); + $exp = base64_url_encode($public_key->getExponent()); + $private_exp = ''; + if ($full_pair && $private_key->getExponent()) { + $private_exp = '.' . base64_url_encode($private_key->getExponent()); + } + + return 'RSA.' . $mod . '.' . $exp . $private_exp; + } + + public function fromString($text) + { + // remove whitespace + $text = preg_replace('/\s+/', '', $text); + + // parse components + if (!preg_match('/RSA\.([^\.]+)\.([^\.]+)(.([^\.]+))?/', $text, $matches)) { + return false; + } + + + $mod = base64_url_decode($matches[1]); + $exp = base64_url_decode($matches[2]); + if ($matches[4]) { + $private_exp = base64_url_decode($matches[4]); + } + + $params['public_key'] = new Crypt_RSA_KEY($mod, $exp, 'public'); + if ($params['public_key']->isError()) { + $error = $params['public_key']->getLastError(); + print $error->getMessage(); + exit; + } + if ($private_exp) { + $params['private_key'] = new Crypt_RSA_KEY($mod, $private_exp, 'private'); + if ($params['private_key']->isError()) { + $error = $params['private_key']->getLastError(); + print $error->getMessage(); + exit; + } + } + + $this->keypair = new Crypt_RSA($params); + } + + public function getName() + { + return 'RSA-SHA256'; + } + + public function sign($bytes) + { + $sig = $this->keypair->createSign($bytes, null, 'sha256'); + if ($this->keypair->isError()) { + $error = $this->keypair->getLastError(); + common_log(LOG_DEBUG, 'RSA Error: '. $error->getMessage()); + } + + return $sig; + } + + public function verify($signed_bytes, $signature) + { + $result = $this->keypair->validateSign($signed_bytes, $signature, null, 'sha256'); + if ($this->keypair->isError()) { + $error = $this->keypair->getLastError(); + //common_log(LOG_DEBUG, 'RSA Error: '. $error->getMessage()); + print $error->getMessage(); + } + return $result; + } + +} + +// Define a sha256 function for hashing +// (Crypt_RSA should really be updated to use hash() ) +function sha256($bytes) +{ + return hash('sha256', $bytes); +} + +function base64_url_encode($input) +{ + return strtr(base64_encode($input), '+/', '-_'); +} + +function base64_url_decode($input) +{ + return base64_decode(strtr($input, '-_', '+/')); +} \ No newline at end of file From 2f65fa646acc9a0739e779de9e472b9957c2e7eb Mon Sep 17 00:00:00 2001 From: James Walker Date: Mon, 22 Feb 2010 09:05:52 -0500 Subject: [PATCH 8/9] wiring in magicsig --- plugins/OStatus/lib/salmon.php | 17 ++++++++++++++--- plugins/OStatus/lib/salmonaction.php | 8 +++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/plugins/OStatus/lib/salmon.php b/plugins/OStatus/lib/salmon.php index df17a70068..53925dc3f4 100644 --- a/plugins/OStatus/lib/salmon.php +++ b/plugins/OStatus/lib/salmon.php @@ -34,6 +34,8 @@ class Salmon return FALSE; } + $xml = $this->createMagicEnv($xml); + $headers = array('Content-type: application/atom+xml'); try { @@ -52,16 +54,25 @@ class Salmon } - public function createMagicEnv($text, $userid) + public function createMagicEnv($text) { + $magic_env = new MagicEnvelope(); + // TODO: Should probably be getting the signer uri as an argument? + $signer_uri = $magic_env->getAuthor($text); + $env = $magic_env->signMessage($text, 'application/atom+xml', $signer_uri); + + return $magic_env->unfold($env); } - public function verifyMagicEnv($env) + public function verifyMagicEnv($dom) { + $magic_env = new MagicEnvelope(); + + $env = $magic_env->fromDom($dom); - + return $magic_env->verify($env); } } diff --git a/plugins/OStatus/lib/salmonaction.php b/plugins/OStatus/lib/salmonaction.php index 4aba20cc40..09a042975d 100644 --- a/plugins/OStatus/lib/salmonaction.php +++ b/plugins/OStatus/lib/salmonaction.php @@ -54,8 +54,14 @@ class SalmonAction extends Action common_log(LOG_DEBUG, "Got invalid Salmon post: $xml"); $this->clientError(_m('Salmon post must be an Atom entry.')); } - // XXX: check the signature + // Check the signature + $salmon = new Salmon; + if (!$salmon->verifyMagicEnv($dom)) { + common_log(LOG_DEBUG, "Salmon signature verification failed."); + $this->clientError(_m('Salmon signature verification failed.')); + } + $this->act = new Activity($dom->documentElement); return true; } From 5a6967db6cbe0e864c8d542700008bba99a7b095 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 22 Feb 2010 11:03:56 -0500 Subject: [PATCH 9/9] clear the site owner when profile changes --- actions/profilesettings.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/actions/profilesettings.php b/actions/profilesettings.php index 0d67778791..161e35b111 100644 --- a/actions/profilesettings.php +++ b/actions/profilesettings.php @@ -285,6 +285,10 @@ class ProfilesettingsAction extends AccountSettingsAction } else { // Re-initialize language environment if it changed common_init_language(); + // Clear the site owner, in case nickname changed + if ($user->hasRole(Profile_role::OWNER)) { + User::blow('user:site_owner'); + } } }