Version Description
- 2019-07-18 =
- Fixed: "Redirect URI mismatch" error for "Gmail" mailer when trying to re-authorize an account that was initially created with version < v1.5.0.
- Changed: Make "Authentication" setting in "Other SMTP" mailer ON by default for new users.
- Changed: Mailers docs links now point to wpmailsmtp.com own site.
Download this release
Release Info
Developer | slaFFik |
Plugin | WP Mail SMTP by WPForms |
Version | 1.5.2 |
Comparing to | |
See all releases |
Code changes from version 1.5.1 to 1.5.2
- assets/languages/wp-mail-smtp.pot +8 -4
- readme.txt +13 -6
- src/Admin/Area.php +1 -0
- src/Admin/PageAbstract.php +4 -0
- src/Admin/Pages/Test.php +1 -1
- src/Options.php +1 -0
- src/Providers/AuthAbstract.php +4 -0
- src/Providers/Gmail/Auth.php +27 -1
- src/Providers/Gmail/Options.php +1 -1
- src/Providers/Mailgun/Options.php +1 -1
- src/Providers/SMTP/Options.php +1 -1
- src/Providers/Sendgrid/Options.php +1 -1
- vendor/guzzlehttp/guzzle/LICENSE +19 -19
- vendor/guzzlehttp/guzzle/src/Client.php +422 -422
- vendor/guzzlehttp/guzzle/src/ClientInterface.php +84 -84
- vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php +314 -314
- vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php +84 -84
- vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php +90 -90
- vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php +71 -71
- vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php +403 -403
- vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php +27 -27
- vendor/guzzlehttp/guzzle/src/Exception/ClientException.php +7 -7
- vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php +37 -37
- vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php +13 -13
- vendor/guzzlehttp/guzzle/src/Exception/RequestException.php +217 -217
- vendor/guzzlehttp/guzzle/src/Exception/SeekException.php +27 -27
- vendor/guzzlehttp/guzzle/src/Exception/ServerException.php +7 -7
- vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php +4 -4
- vendor/guzzlehttp/guzzle/src/Exception/TransferException.php +4 -4
- vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php +565 -565
- vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php +27 -27
- vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php +45 -45
- vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php +199 -199
- vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php +92 -92
- vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php +189 -189
- vendor/guzzlehttp/guzzle/src/Handler/Proxy.php +55 -55
- vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php +532 -532
- vendor/guzzlehttp/guzzle/src/HandlerStack.php +273 -273
- vendor/guzzlehttp/guzzle/src/MessageFormatter.php +180 -180
- vendor/guzzlehttp/guzzle/src/Middleware.php +255 -255
- vendor/guzzlehttp/guzzle/src/Pool.php +123 -123
- vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php +106 -106
- vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php +237 -237
- vendor/guzzlehttp/guzzle/src/RequestOptions.php +255 -255
- vendor/guzzlehttp/guzzle/src/RetryMiddleware.php +112 -112
- vendor/guzzlehttp/guzzle/src/TransferStats.php +126 -126
- vendor/guzzlehttp/guzzle/src/UriTemplate.php +237 -237
- vendor/guzzlehttp/guzzle/src/functions.php +333 -333
- vendor/guzzlehttp/guzzle/src/functions_include.php +6 -6
- vendor/phpseclib/phpseclib/LICENSE +20 -21
- vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php +126 -126
- vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php +3201 -3201
- vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php +274 -274
- vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php +936 -936
- vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php +3779 -3779
- vendor/phpseclib/phpseclib/phpseclib/bootstrap.php +16 -16
- vendor/phpseclib/phpseclib/phpseclib/openssl.cnf +6 -6
- vendor/phpseclib/phpseclib/travis/code_coverage_id_rsa +30 -0
- wp_mail_smtp.php +2 -2
assets/languages/wp-mail-smtp.pot
CHANGED
@@ -1,13 +1,13 @@
|
|
1 |
msgid ""
|
2 |
msgstr ""
|
3 |
-
"Project-Id-Version: WP Mail SMTP 1.5.
|
4 |
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wp-mail-smtp\n"
|
5 |
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
6 |
"Language-Team: LANGUAGE <LL@li.org>\n"
|
7 |
"MIME-Version: 1.0\n"
|
8 |
"Content-Type: text/plain; charset=UTF-8\n"
|
9 |
"Content-Transfer-Encoding: 8bit\n"
|
10 |
-
"POT-Creation-Date: 2019-07-
|
11 |
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
12 |
"X-Generator: WP-CLI 2.2.0\n"
|
13 |
"X-Domain: wp-mail-smtp\n"
|
@@ -128,7 +128,7 @@ msgstr ""
|
|
128 |
msgid "WP Mail SMTP Pro related message was successfully dismissed."
|
129 |
msgstr ""
|
130 |
|
131 |
-
#: src/Admin/PageAbstract.php:
|
132 |
msgid "Save Settings"
|
133 |
msgstr ""
|
134 |
|
@@ -733,7 +733,7 @@ msgid "This means the connection to your SMTP host was made successfully, but th
|
|
733 |
msgstr ""
|
734 |
|
735 |
#: src/Admin/Pages/Test.php:600
|
736 |
-
msgid "Typically this error is returned when
|
737 |
msgstr ""
|
738 |
|
739 |
#: src/Admin/Pages/Test.php:603
|
@@ -1086,6 +1086,10 @@ msgstr ""
|
|
1086 |
msgid "Consider running an email test after fixing it."
|
1087 |
msgstr ""
|
1088 |
|
|
|
|
|
|
|
|
|
1089 |
#: src/Providers/Gmail/Options.php:32
|
1090 |
msgid "Gmail"
|
1091 |
msgstr ""
|
1 |
msgid ""
|
2 |
msgstr ""
|
3 |
+
"Project-Id-Version: WP Mail SMTP 1.5.2\n"
|
4 |
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wp-mail-smtp\n"
|
5 |
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
6 |
"Language-Team: LANGUAGE <LL@li.org>\n"
|
7 |
"MIME-Version: 1.0\n"
|
8 |
"Content-Type: text/plain; charset=UTF-8\n"
|
9 |
"Content-Transfer-Encoding: 8bit\n"
|
10 |
+
"POT-Creation-Date: 2019-07-18T11:22:11+03:00\n"
|
11 |
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
12 |
"X-Generator: WP-CLI 2.2.0\n"
|
13 |
"X-Domain: wp-mail-smtp\n"
|
128 |
msgid "WP Mail SMTP Pro related message was successfully dismissed."
|
129 |
msgstr ""
|
130 |
|
131 |
+
#: src/Admin/PageAbstract.php:81
|
132 |
msgid "Save Settings"
|
133 |
msgstr ""
|
134 |
|
733 |
msgstr ""
|
734 |
|
735 |
#: src/Admin/Pages/Test.php:600
|
736 |
+
msgid "Typically this error is returned when you are sending too many e-mails or e-mails that have been identified as spam."
|
737 |
msgstr ""
|
738 |
|
739 |
#: src/Admin/Pages/Test.php:603
|
1086 |
msgid "Consider running an email test after fixing it."
|
1087 |
msgstr ""
|
1088 |
|
1089 |
+
#: src/Providers/Gmail/Auth.php:170
|
1090 |
+
msgid "There was an error while processing the Google authentication request. Please make sure that you have Client ID and Client Secret both valid and saved."
|
1091 |
+
msgstr ""
|
1092 |
+
|
1093 |
#: src/Providers/Gmail/Options.php:32
|
1094 |
msgid "Gmail"
|
1095 |
msgstr ""
|
readme.txt
CHANGED
@@ -3,7 +3,7 @@ Contributors: wpforms, jaredatch, smub, slaFFik
|
|
3 |
Tags: smtp, wp mail smtp, wordpress smtp, gmail smtp, sendgrid smtp, mailgun smtp, mail, mailer, phpmailer, wp_mail, email, mailgun, sengrid, gmail, wp smtp
|
4 |
Requires at least: 4.9
|
5 |
Tested up to: 5.2
|
6 |
-
Stable tag: 1.5.
|
7 |
Requires PHP: 5.3
|
8 |
|
9 |
The most popular WordPress SMTP and PHP Mailer plugin. Trusted by over 1 million sites.
|
@@ -12,7 +12,7 @@ The most popular WordPress SMTP and PHP Mailer plugin. Trusted by over 1 million
|
|
12 |
|
13 |
### WordPress Mail SMTP Plugin
|
14 |
|
15 |
-
Having problems with your WordPress site not sending emails? You're not alone. Over 1 million websites use WP Mail SMTP to send their emails
|
16 |
|
17 |
Our goal is to make email deliverability easy and reliable. We want to ensure your emails reach the inbox.
|
18 |
|
@@ -59,7 +59,7 @@ Mailgun SMTP is a popular SMTP service provider that allows you to send large qu
|
|
59 |
|
60 |
WP Mail SMTP plugin offers a native integration with MailGun. All you have to do is connect your Mailgun account, and you will improve your email deliverability.
|
61 |
|
62 |
-
Read our <a href="https://
|
63 |
|
64 |
= Gmail SMTP =
|
65 |
|
@@ -69,13 +69,13 @@ This allows you to use your <a href="http://www.wpbeginner.com/beginners-guide/h
|
|
69 |
|
70 |
Unlike other Gmail SMTP plugins, our Gmail SMTP option uses OAuth to authenticate your Google account, keeping your login information 100% secure.
|
71 |
|
72 |
-
Read our <a href="https://
|
73 |
|
74 |
= SendGrid SMTP =
|
75 |
|
76 |
SendGrid has a free SMTP plan that you can use to send up to 100 emails per day. With our native SendGrid SMTP integration, you can easily and securely set up SendGrid SMTP on your WordPress site.
|
77 |
|
78 |
-
Read our <a href="https://
|
79 |
|
80 |
= Microsoft SMTP (Outlook.com and Office 365) =
|
81 |
|
@@ -85,6 +85,8 @@ Many business use Outlook.com or Office 365 to their to power their email. For t
|
|
85 |
|
86 |
Advanced or technical users can harness the power of Amazon AWS (Amazon Web Services) with the Amazon SES mailer. With this integration, you can send a high volume of emails at a very reasonable rate.
|
87 |
|
|
|
|
|
88 |
= Other SMTP =
|
89 |
|
90 |
WP Mail SMTP plugin also works with all major email services such as Gmail, Yahoo, Outlook, Microsoft Live, and any other email sending service that offers SMTP.
|
@@ -97,7 +99,7 @@ You can set the following options:
|
|
97 |
* Choose to use SMTP authentication or not.
|
98 |
* Specify an SMTP username and password.
|
99 |
|
100 |
-
To see recommended settings for the popular services as well as troubleshooting tips, check out our <a href="https://
|
101 |
|
102 |
We hope that you find WP Mail SMTP plugin helpful!
|
103 |
|
@@ -195,6 +197,11 @@ By all means please contact us to discuss features or options you'd like to see
|
|
195 |
|
196 |
== Changelog ==
|
197 |
|
|
|
|
|
|
|
|
|
|
|
198 |
= 1.5.1 - 2019-07-12 =
|
199 |
* Fixed: Duplicated emails sent to the first recipient in a loop (and others not receiving their emails).
|
200 |
|
3 |
Tags: smtp, wp mail smtp, wordpress smtp, gmail smtp, sendgrid smtp, mailgun smtp, mail, mailer, phpmailer, wp_mail, email, mailgun, sengrid, gmail, wp smtp
|
4 |
Requires at least: 4.9
|
5 |
Tested up to: 5.2
|
6 |
+
Stable tag: 1.5.2
|
7 |
Requires PHP: 5.3
|
8 |
|
9 |
The most popular WordPress SMTP and PHP Mailer plugin. Trusted by over 1 million sites.
|
12 |
|
13 |
### WordPress Mail SMTP Plugin
|
14 |
|
15 |
+
Having problems with your WordPress site not sending emails? You're not alone. Over 1 million websites use WP Mail SMTP to send their emails reliably.
|
16 |
|
17 |
Our goal is to make email deliverability easy and reliable. We want to ensure your emails reach the inbox.
|
18 |
|
59 |
|
60 |
WP Mail SMTP plugin offers a native integration with MailGun. All you have to do is connect your Mailgun account, and you will improve your email deliverability.
|
61 |
|
62 |
+
Read our <a href="https://wpmailsmtp.com/docs/how-to-set-up-the-mailgun-mailer-in-wp-mail-smtp/" rel="friend">Mailgun documentation</a> for more details.
|
63 |
|
64 |
= Gmail SMTP =
|
65 |
|
69 |
|
70 |
Unlike other Gmail SMTP plugins, our Gmail SMTP option uses OAuth to authenticate your Google account, keeping your login information 100% secure.
|
71 |
|
72 |
+
Read our <a href="https://wpmailsmtp.com/docs/how-to-set-up-the-gmail-mailer-in-wp-mail-smtp/" rel="friend">Gmail documentation</a> for more details.
|
73 |
|
74 |
= SendGrid SMTP =
|
75 |
|
76 |
SendGrid has a free SMTP plan that you can use to send up to 100 emails per day. With our native SendGrid SMTP integration, you can easily and securely set up SendGrid SMTP on your WordPress site.
|
77 |
|
78 |
+
Read our <a href="https://wpmailsmtp.com/docs/how-to-set-up-the-sendgrid-mailer-in-wp-mail-smtp/" rel="friend">SendGrid documentation</a> for more details.
|
79 |
|
80 |
= Microsoft SMTP (Outlook.com and Office 365) =
|
81 |
|
85 |
|
86 |
Advanced or technical users can harness the power of Amazon AWS (Amazon Web Services) with the Amazon SES mailer. With this integration, you can send a high volume of emails at a very reasonable rate.
|
87 |
|
88 |
+
Read our <a href="https://wpmailsmtp.com/docs/how-to-set-up-the-amazon-ses-mailer-in-wp-mail-smtp/" rel="friend">Amazon SES documentation</a> for more details.
|
89 |
+
|
90 |
= Other SMTP =
|
91 |
|
92 |
WP Mail SMTP plugin also works with all major email services such as Gmail, Yahoo, Outlook, Microsoft Live, and any other email sending service that offers SMTP.
|
99 |
* Choose to use SMTP authentication or not.
|
100 |
* Specify an SMTP username and password.
|
101 |
|
102 |
+
To see recommended settings for the popular services as well as troubleshooting tips, check out our <a href="https://wpmailsmtp.com/docs/how-to-set-up-the-other-smtp-mailer-in-wp-mail-smtp/" rel="friend">SMTP documentation</a>.
|
103 |
|
104 |
We hope that you find WP Mail SMTP plugin helpful!
|
105 |
|
197 |
|
198 |
== Changelog ==
|
199 |
|
200 |
+
= 1.5.2 - 2019-07-18 =
|
201 |
+
* Fixed: "Redirect URI mismatch" error for "Gmail" mailer when trying to re-authorize an account that was initially created with version < v1.5.0.
|
202 |
+
* Changed: Make "Authentication" setting in "Other SMTP" mailer ON by default for new users.
|
203 |
+
* Changed: Mailers docs links now point to wpmailsmtp.com own site.
|
204 |
+
|
205 |
= 1.5.1 - 2019-07-12 =
|
206 |
* Fixed: Duplicated emails sent to the first recipient in a loop (and others not receiving their emails).
|
207 |
|
src/Admin/Area.php
CHANGED
@@ -731,6 +731,7 @@ class Area {
|
|
731 |
* Get plugin admin area page URL.
|
732 |
*
|
733 |
* @since 1.0.0
|
|
|
734 |
*
|
735 |
* @param string $page
|
736 |
*
|
731 |
* Get plugin admin area page URL.
|
732 |
*
|
733 |
* @since 1.0.0
|
734 |
+
* @since 1.5.0 URL is changed to support the top level position of the plugin admin area.
|
735 |
*
|
736 |
* @param string $page
|
737 |
*
|
src/Admin/PageAbstract.php
CHANGED
@@ -18,6 +18,7 @@ abstract class PageAbstract implements PageInterface {
|
|
18 |
* @inheritdoc
|
19 |
*/
|
20 |
public function get_link() {
|
|
|
21 |
return esc_url(
|
22 |
add_query_arg(
|
23 |
'tab',
|
@@ -51,6 +52,7 @@ abstract class PageAbstract implements PageInterface {
|
|
51 |
* @since 1.0.0
|
52 |
*/
|
53 |
public function wp_nonce_field() {
|
|
|
54 |
wp_nonce_field( Area::SLUG . '-' . $this->slug );
|
55 |
}
|
56 |
|
@@ -61,6 +63,7 @@ abstract class PageAbstract implements PageInterface {
|
|
61 |
* @since 1.0.0
|
62 |
*/
|
63 |
public function check_admin_referer() {
|
|
|
64 |
check_admin_referer( Area::SLUG . '-' . $this->slug );
|
65 |
}
|
66 |
|
@@ -70,6 +73,7 @@ abstract class PageAbstract implements PageInterface {
|
|
70 |
* @since 1.5.0
|
71 |
*/
|
72 |
public function display_save_btn() {
|
|
|
73 |
?>
|
74 |
|
75 |
<p class="wp-mail-smtp-submit">
|
18 |
* @inheritdoc
|
19 |
*/
|
20 |
public function get_link() {
|
21 |
+
|
22 |
return esc_url(
|
23 |
add_query_arg(
|
24 |
'tab',
|
52 |
* @since 1.0.0
|
53 |
*/
|
54 |
public function wp_nonce_field() {
|
55 |
+
|
56 |
wp_nonce_field( Area::SLUG . '-' . $this->slug );
|
57 |
}
|
58 |
|
63 |
* @since 1.0.0
|
64 |
*/
|
65 |
public function check_admin_referer() {
|
66 |
+
|
67 |
check_admin_referer( Area::SLUG . '-' . $this->slug );
|
68 |
}
|
69 |
|
73 |
* @since 1.5.0
|
74 |
*/
|
75 |
public function display_save_btn() {
|
76 |
+
|
77 |
?>
|
78 |
|
79 |
<p class="wp-mail-smtp-submit">
|
src/Admin/Pages/Test.php
CHANGED
@@ -597,7 +597,7 @@ Lead Developer, WP Mail SMTP';
|
|
597 |
'description' => array(
|
598 |
'<strong>' . esc_html__( 'Error due to unsolicited and/or bulk e-mail.', 'wp-mail-smtp' ) . '</strong>',
|
599 |
esc_html__( 'This means the connection to your SMTP host was made successfully, but the host rejected the email.', 'wp-mail-smtp' ),
|
600 |
-
esc_html__( 'Typically this error is returned when
|
601 |
),
|
602 |
'steps' => array(
|
603 |
esc_html__( 'Check the emails that are sending are sending individually. Example: email is not sending to 30 recipients. You can install any WordPress e-mail logging plugin to do that.', 'wp-mail-smtp' ),
|
597 |
'description' => array(
|
598 |
'<strong>' . esc_html__( 'Error due to unsolicited and/or bulk e-mail.', 'wp-mail-smtp' ) . '</strong>',
|
599 |
esc_html__( 'This means the connection to your SMTP host was made successfully, but the host rejected the email.', 'wp-mail-smtp' ),
|
600 |
+
esc_html__( 'Typically this error is returned when you are sending too many e-mails or e-mails that have been identified as spam.', 'wp-mail-smtp' ),
|
601 |
),
|
602 |
'steps' => array(
|
603 |
esc_html__( 'Check the emails that are sending are sending individually. Example: email is not sending to 30 recipients. You can install any WordPress e-mail logging plugin to do that.', 'wp-mail-smtp' ),
|
src/Options.php
CHANGED
@@ -146,6 +146,7 @@ class Options {
|
|
146 |
),
|
147 |
'smtp' => array(
|
148 |
'autotls' => true,
|
|
|
149 |
),
|
150 |
);
|
151 |
}
|
146 |
),
|
147 |
'smtp' => array(
|
148 |
'autotls' => true,
|
149 |
+
'auth' => true,
|
150 |
),
|
151 |
);
|
152 |
}
|
src/Providers/AuthAbstract.php
CHANGED
@@ -51,6 +51,7 @@ abstract class AuthAbstract implements AuthInterface {
|
|
51 |
* @since 1.0.0
|
52 |
*/
|
53 |
protected function include_vendor_lib() {
|
|
|
54 |
require_once wp_mail_smtp()->plugin_path . '/vendor/autoload.php';
|
55 |
}
|
56 |
|
@@ -62,6 +63,7 @@ abstract class AuthAbstract implements AuthInterface {
|
|
62 |
* @return string
|
63 |
*/
|
64 |
public static function get_plugin_auth_url() {
|
|
|
65 |
return add_query_arg( 'tab', 'auth', wp_mail_smtp()->get_admin()->get_admin_page_url() );
|
66 |
}
|
67 |
|
@@ -132,6 +134,7 @@ abstract class AuthAbstract implements AuthInterface {
|
|
132 |
* @inheritdoc
|
133 |
*/
|
134 |
public function is_clients_saved() {
|
|
|
135 |
return ! empty( $this->options['client_id'] ) && ! empty( $this->options['client_secret'] );
|
136 |
}
|
137 |
|
@@ -139,6 +142,7 @@ abstract class AuthAbstract implements AuthInterface {
|
|
139 |
* @inheritdoc
|
140 |
*/
|
141 |
public function is_auth_required() {
|
|
|
142 |
return empty( $this->options['access_token'] ) || empty( $this->options['refresh_token'] );
|
143 |
}
|
144 |
}
|
51 |
* @since 1.0.0
|
52 |
*/
|
53 |
protected function include_vendor_lib() {
|
54 |
+
|
55 |
require_once wp_mail_smtp()->plugin_path . '/vendor/autoload.php';
|
56 |
}
|
57 |
|
63 |
* @return string
|
64 |
*/
|
65 |
public static function get_plugin_auth_url() {
|
66 |
+
|
67 |
return add_query_arg( 'tab', 'auth', wp_mail_smtp()->get_admin()->get_admin_page_url() );
|
68 |
}
|
69 |
|
134 |
* @inheritdoc
|
135 |
*/
|
136 |
public function is_clients_saved() {
|
137 |
+
|
138 |
return ! empty( $this->options['client_id'] ) && ! empty( $this->options['client_secret'] );
|
139 |
}
|
140 |
|
142 |
* @inheritdoc
|
143 |
*/
|
144 |
public function is_auth_required() {
|
145 |
+
|
146 |
return empty( $this->options['access_token'] ) || empty( $this->options['refresh_token'] );
|
147 |
}
|
148 |
}
|
src/Providers/Gmail/Auth.php
CHANGED
@@ -2,6 +2,7 @@
|
|
2 |
|
3 |
namespace WPMailSMTP\Providers\Gmail;
|
4 |
|
|
|
5 |
use WPMailSMTP\Debug;
|
6 |
use WPMailSMTP\Options as PluginOptions;
|
7 |
use WPMailSMTP\Providers\AuthAbstract;
|
@@ -37,6 +38,24 @@ class Auth extends AuthAbstract {
|
|
37 |
}
|
38 |
}
|
39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
/**
|
41 |
* Init and get the Google Client object.
|
42 |
*
|
@@ -140,9 +159,16 @@ class Auth extends AuthAbstract {
|
|
140 |
*/
|
141 |
public function process() {
|
142 |
|
|
|
|
|
|
|
|
|
|
|
143 |
// We can't process without saved client_id/secret.
|
144 |
if ( ! $this->is_clients_saved() ) {
|
145 |
-
Debug::set(
|
|
|
|
|
146 |
wp_safe_redirect(
|
147 |
add_query_arg(
|
148 |
'error',
|
2 |
|
3 |
namespace WPMailSMTP\Providers\Gmail;
|
4 |
|
5 |
+
use WPMailSMTP\Admin\Area;
|
6 |
use WPMailSMTP\Debug;
|
7 |
use WPMailSMTP\Options as PluginOptions;
|
8 |
use WPMailSMTP\Providers\AuthAbstract;
|
38 |
}
|
39 |
}
|
40 |
|
41 |
+
/**
|
42 |
+
* Get the url, that users will be redirected back to finish the OAuth process.
|
43 |
+
*
|
44 |
+
* @since 1.5.2 Returned to the old, pre-1.5, structure of the link to preserve BC.
|
45 |
+
*
|
46 |
+
* @return string
|
47 |
+
*/
|
48 |
+
public static function get_plugin_auth_url() {
|
49 |
+
|
50 |
+
return add_query_arg(
|
51 |
+
array(
|
52 |
+
'page' => Area::SLUG,
|
53 |
+
'tab' => 'auth',
|
54 |
+
),
|
55 |
+
admin_url( 'options-general.php' )
|
56 |
+
);
|
57 |
+
}
|
58 |
+
|
59 |
/**
|
60 |
* Init and get the Google Client object.
|
61 |
*
|
159 |
*/
|
160 |
public function process() {
|
161 |
|
162 |
+
if ( ! ( isset( $_GET['tab'] ) && $_GET['tab'] === 'auth' ) ) {
|
163 |
+
wp_safe_redirect( wp_mail_smtp()->get_admin()->get_admin_page_url() );
|
164 |
+
exit;
|
165 |
+
}
|
166 |
+
|
167 |
// We can't process without saved client_id/secret.
|
168 |
if ( ! $this->is_clients_saved() ) {
|
169 |
+
Debug::set(
|
170 |
+
esc_html__( 'There was an error while processing the Google authentication request. Please make sure that you have Client ID and Client Secret both valid and saved.', 'wp-mail-smtp' )
|
171 |
+
);
|
172 |
wp_safe_redirect(
|
173 |
add_query_arg(
|
174 |
'error',
|
src/Providers/Gmail/Options.php
CHANGED
@@ -43,7 +43,7 @@ class Options extends OptionsAbstract {
|
|
43 |
),
|
44 |
)
|
45 |
),
|
46 |
-
'<a href="https://
|
47 |
'</a>'
|
48 |
),
|
49 |
'php' => '5.5',
|
43 |
),
|
44 |
)
|
45 |
),
|
46 |
+
'<a href="https://wpmailsmtp.com/docs/how-to-set-up-the-gmail-mailer-in-wp-mail-smtp/" target="_blank" rel="noopener noreferrer">',
|
47 |
'</a>'
|
48 |
),
|
49 |
'php' => '5.5',
|
src/Providers/Mailgun/Options.php
CHANGED
@@ -38,7 +38,7 @@ class Options extends OptionsAbstract {
|
|
38 |
),
|
39 |
'<a href="https://www.mailgun.com" target="_blank" rel="noopener noreferrer">',
|
40 |
'</a>',
|
41 |
-
'<a href="https://
|
42 |
'</a>'
|
43 |
),
|
44 |
)
|
38 |
),
|
39 |
'<a href="https://www.mailgun.com" target="_blank" rel="noopener noreferrer">',
|
40 |
'</a>',
|
41 |
+
'<a href="https://wpmailsmtp.com/docs/how-to-set-up-the-mailgun-mailer-in-wp-mail-smtp/" target="_blank" rel="noopener noreferrer">',
|
42 |
'</a>'
|
43 |
),
|
44 |
)
|
src/Providers/SMTP/Options.php
CHANGED
@@ -36,7 +36,7 @@ class Options extends OptionsAbstract {
|
|
36 |
),
|
37 |
)
|
38 |
),
|
39 |
-
'https://
|
40 |
),
|
41 |
)
|
42 |
);
|
36 |
),
|
37 |
)
|
38 |
),
|
39 |
+
'https://wpmailsmtp.com/docs/how-to-set-up-the-other-smtp-mailer-in-wp-mail-smtp/'
|
40 |
),
|
41 |
)
|
42 |
);
|
src/Providers/Sendgrid/Options.php
CHANGED
@@ -38,7 +38,7 @@ class Options extends OptionsAbstract {
|
|
38 |
),
|
39 |
'<a href="https://sendgrid.com" target="_blank" rel="noopener noreferrer">',
|
40 |
'</a>',
|
41 |
-
'<a href="https://
|
42 |
'</a>'
|
43 |
),
|
44 |
)
|
38 |
),
|
39 |
'<a href="https://sendgrid.com" target="_blank" rel="noopener noreferrer">',
|
40 |
'</a>',
|
41 |
+
'<a href="https://wpmailsmtp.com/docs/how-to-set-up-the-sendgrid-mailer-in-wp-mail-smtp/" target="_blank" rel="noopener noreferrer">',
|
42 |
'</a>'
|
43 |
),
|
44 |
)
|
vendor/guzzlehttp/guzzle/LICENSE
CHANGED
@@ -1,19 +1,19 @@
|
|
1 |
-
Copyright (c) 2011-2018 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
|
2 |
-
|
3 |
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4 |
-
of this software and associated documentation files (the "Software"), to deal
|
5 |
-
in the Software without restriction, including without limitation the rights
|
6 |
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7 |
-
copies of the Software, and to permit persons to whom the Software is
|
8 |
-
furnished to do so, subject to the following conditions:
|
9 |
-
|
10 |
-
The above copyright notice and this permission notice shall be included in
|
11 |
-
all copies or substantial portions of the Software.
|
12 |
-
|
13 |
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14 |
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15 |
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16 |
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17 |
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18 |
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19 |
-
THE SOFTWARE.
|
1 |
+
Copyright (c) 2011-2018 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
|
2 |
+
|
3 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4 |
+
of this software and associated documentation files (the "Software"), to deal
|
5 |
+
in the Software without restriction, including without limitation the rights
|
6 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7 |
+
copies of the Software, and to permit persons to whom the Software is
|
8 |
+
furnished to do so, subject to the following conditions:
|
9 |
+
|
10 |
+
The above copyright notice and this permission notice shall be included in
|
11 |
+
all copies or substantial portions of the Software.
|
12 |
+
|
13 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19 |
+
THE SOFTWARE.
|
vendor/guzzlehttp/guzzle/src/Client.php
CHANGED
@@ -1,422 +1,422 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp;
|
3 |
-
|
4 |
-
use GuzzleHttp\Cookie\CookieJar;
|
5 |
-
use GuzzleHttp\Promise;
|
6 |
-
use GuzzleHttp\Psr7;
|
7 |
-
use Psr\Http\Message\UriInterface;
|
8 |
-
use Psr\Http\Message\RequestInterface;
|
9 |
-
use Psr\Http\Message\ResponseInterface;
|
10 |
-
|
11 |
-
/**
|
12 |
-
* @method ResponseInterface get(string|UriInterface $uri, array $options = [])
|
13 |
-
* @method ResponseInterface head(string|UriInterface $uri, array $options = [])
|
14 |
-
* @method ResponseInterface put(string|UriInterface $uri, array $options = [])
|
15 |
-
* @method ResponseInterface post(string|UriInterface $uri, array $options = [])
|
16 |
-
* @method ResponseInterface patch(string|UriInterface $uri, array $options = [])
|
17 |
-
* @method ResponseInterface delete(string|UriInterface $uri, array $options = [])
|
18 |
-
* @method Promise\PromiseInterface getAsync(string|UriInterface $uri, array $options = [])
|
19 |
-
* @method Promise\PromiseInterface headAsync(string|UriInterface $uri, array $options = [])
|
20 |
-
* @method Promise\PromiseInterface putAsync(string|UriInterface $uri, array $options = [])
|
21 |
-
* @method Promise\PromiseInterface postAsync(string|UriInterface $uri, array $options = [])
|
22 |
-
* @method Promise\PromiseInterface patchAsync(string|UriInterface $uri, array $options = [])
|
23 |
-
* @method Promise\PromiseInterface deleteAsync(string|UriInterface $uri, array $options = [])
|
24 |
-
*/
|
25 |
-
class Client implements ClientInterface
|
26 |
-
{
|
27 |
-
/** @var array Default request options */
|
28 |
-
private $config;
|
29 |
-
|
30 |
-
/**
|
31 |
-
* Clients accept an array of constructor parameters.
|
32 |
-
*
|
33 |
-
* Here's an example of creating a client using a base_uri and an array of
|
34 |
-
* default request options to apply to each request:
|
35 |
-
*
|
36 |
-
* $client = new Client([
|
37 |
-
* 'base_uri' => 'http://www.foo.com/1.0/',
|
38 |
-
* 'timeout' => 0,
|
39 |
-
* 'allow_redirects' => false,
|
40 |
-
* 'proxy' => '192.168.16.1:10'
|
41 |
-
* ]);
|
42 |
-
*
|
43 |
-
* Client configuration settings include the following options:
|
44 |
-
*
|
45 |
-
* - handler: (callable) Function that transfers HTTP requests over the
|
46 |
-
* wire. The function is called with a Psr7\Http\Message\RequestInterface
|
47 |
-
* and array of transfer options, and must return a
|
48 |
-
* GuzzleHttp\Promise\PromiseInterface that is fulfilled with a
|
49 |
-
* Psr7\Http\Message\ResponseInterface on success. "handler" is a
|
50 |
-
* constructor only option that cannot be overridden in per/request
|
51 |
-
* options. If no handler is provided, a default handler will be created
|
52 |
-
* that enables all of the request options below by attaching all of the
|
53 |
-
* default middleware to the handler.
|
54 |
-
* - base_uri: (string|UriInterface) Base URI of the client that is merged
|
55 |
-
* into relative URIs. Can be a string or instance of UriInterface.
|
56 |
-
* - **: any request option
|
57 |
-
*
|
58 |
-
* @param array $config Client configuration settings.
|
59 |
-
*
|
60 |
-
* @see \GuzzleHttp\RequestOptions for a list of available request options.
|
61 |
-
*/
|
62 |
-
public function __construct(array $config = [])
|
63 |
-
{
|
64 |
-
if (!isset($config['handler'])) {
|
65 |
-
$config['handler'] = HandlerStack::create();
|
66 |
-
} elseif (!is_callable($config['handler'])) {
|
67 |
-
throw new \InvalidArgumentException('handler must be a callable');
|
68 |
-
}
|
69 |
-
|
70 |
-
// Convert the base_uri to a UriInterface
|
71 |
-
if (isset($config['base_uri'])) {
|
72 |
-
$config['base_uri'] = Psr7\uri_for($config['base_uri']);
|
73 |
-
}
|
74 |
-
|
75 |
-
$this->configureDefaults($config);
|
76 |
-
}
|
77 |
-
|
78 |
-
public function __call($method, $args)
|
79 |
-
{
|
80 |
-
if (count($args) < 1) {
|
81 |
-
throw new \InvalidArgumentException('Magic request methods require a URI and optional options array');
|
82 |
-
}
|
83 |
-
|
84 |
-
$uri = $args[0];
|
85 |
-
$opts = isset($args[1]) ? $args[1] : [];
|
86 |
-
|
87 |
-
return substr($method, -5) === 'Async'
|
88 |
-
? $this->requestAsync(substr($method, 0, -5), $uri, $opts)
|
89 |
-
: $this->request($method, $uri, $opts);
|
90 |
-
}
|
91 |
-
|
92 |
-
public function sendAsync(RequestInterface $request, array $options = [])
|
93 |
-
{
|
94 |
-
// Merge the base URI into the request URI if needed.
|
95 |
-
$options = $this->prepareDefaults($options);
|
96 |
-
|
97 |
-
return $this->transfer(
|
98 |
-
$request->withUri($this->buildUri($request->getUri(), $options), $request->hasHeader('Host')),
|
99 |
-
$options
|
100 |
-
);
|
101 |
-
}
|
102 |
-
|
103 |
-
public function send(RequestInterface $request, array $options = [])
|
104 |
-
{
|
105 |
-
$options[RequestOptions::SYNCHRONOUS] = true;
|
106 |
-
return $this->sendAsync($request, $options)->wait();
|
107 |
-
}
|
108 |
-
|
109 |
-
public function requestAsync($method, $uri = '', array $options = [])
|
110 |
-
{
|
111 |
-
$options = $this->prepareDefaults($options);
|
112 |
-
// Remove request modifying parameter because it can be done up-front.
|
113 |
-
$headers = isset($options['headers']) ? $options['headers'] : [];
|
114 |
-
$body = isset($options['body']) ? $options['body'] : null;
|
115 |
-
$version = isset($options['version']) ? $options['version'] : '1.1';
|
116 |
-
// Merge the URI into the base URI.
|
117 |
-
$uri = $this->buildUri($uri, $options);
|
118 |
-
if (is_array($body)) {
|
119 |
-
$this->invalidBody();
|
120 |
-
}
|
121 |
-
$request = new Psr7\Request($method, $uri, $headers, $body, $version);
|
122 |
-
// Remove the option so that they are not doubly-applied.
|
123 |
-
unset($options['headers'], $options['body'], $options['version']);
|
124 |
-
|
125 |
-
return $this->transfer($request, $options);
|
126 |
-
}
|
127 |
-
|
128 |
-
public function request($method, $uri = '', array $options = [])
|
129 |
-
{
|
130 |
-
$options[RequestOptions::SYNCHRONOUS] = true;
|
131 |
-
return $this->requestAsync($method, $uri, $options)->wait();
|
132 |
-
}
|
133 |
-
|
134 |
-
public function getConfig($option = null)
|
135 |
-
{
|
136 |
-
return $option === null
|
137 |
-
? $this->config
|
138 |
-
: (isset($this->config[$option]) ? $this->config[$option] : null);
|
139 |
-
}
|
140 |
-
|
141 |
-
private function buildUri($uri, array $config)
|
142 |
-
{
|
143 |
-
// for BC we accept null which would otherwise fail in uri_for
|
144 |
-
$uri = Psr7\uri_for($uri === null ? '' : $uri);
|
145 |
-
|
146 |
-
if (isset($config['base_uri'])) {
|
147 |
-
$uri = Psr7\UriResolver::resolve(Psr7\uri_for($config['base_uri']), $uri);
|
148 |
-
}
|
149 |
-
|
150 |
-
return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri;
|
151 |
-
}
|
152 |
-
|
153 |
-
/**
|
154 |
-
* Configures the default options for a client.
|
155 |
-
*
|
156 |
-
* @param array $config
|
157 |
-
*/
|
158 |
-
private function configureDefaults(array $config)
|
159 |
-
{
|
160 |
-
$defaults = [
|
161 |
-
'allow_redirects' => RedirectMiddleware::$defaultSettings,
|
162 |
-
'http_errors' => true,
|
163 |
-
'decode_content' => true,
|
164 |
-
'verify' => true,
|
165 |
-
'cookies' => false
|
166 |
-
];
|
167 |
-
|
168 |
-
// Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set.
|
169 |
-
|
170 |
-
// We can only trust the HTTP_PROXY environment variable in a CLI
|
171 |
-
// process due to the fact that PHP has no reliable mechanism to
|
172 |
-
// get environment variables that start with "HTTP_".
|
173 |
-
if (php_sapi_name() == 'cli' && getenv('HTTP_PROXY')) {
|
174 |
-
$defaults['proxy']['http'] = getenv('HTTP_PROXY');
|
175 |
-
}
|
176 |
-
|
177 |
-
if ($proxy = getenv('HTTPS_PROXY')) {
|
178 |
-
$defaults['proxy']['https'] = $proxy;
|
179 |
-
}
|
180 |
-
|
181 |
-
if ($noProxy = getenv('NO_PROXY')) {
|
182 |
-
$cleanedNoProxy = str_replace(' ', '', $noProxy);
|
183 |
-
$defaults['proxy']['no'] = explode(',', $cleanedNoProxy);
|
184 |
-
}
|
185 |
-
|
186 |
-
$this->config = $config + $defaults;
|
187 |
-
|
188 |
-
if (!empty($config['cookies']) && $config['cookies'] === true) {
|
189 |
-
$this->config['cookies'] = new CookieJar();
|
190 |
-
}
|
191 |
-
|
192 |
-
// Add the default user-agent header.
|
193 |
-
if (!isset($this->config['headers'])) {
|
194 |
-
$this->config['headers'] = ['User-Agent' => default_user_agent()];
|
195 |
-
} else {
|
196 |
-
// Add the User-Agent header if one was not already set.
|
197 |
-
foreach (array_keys($this->config['headers']) as $name) {
|
198 |
-
if (strtolower($name) === 'user-agent') {
|
199 |
-
return;
|
200 |
-
}
|
201 |
-
}
|
202 |
-
$this->config['headers']['User-Agent'] = default_user_agent();
|
203 |
-
}
|
204 |
-
}
|
205 |
-
|
206 |
-
/**
|
207 |
-
* Merges default options into the array.
|
208 |
-
*
|
209 |
-
* @param array $options Options to modify by reference
|
210 |
-
*
|
211 |
-
* @return array
|
212 |
-
*/
|
213 |
-
private function prepareDefaults($options)
|
214 |
-
{
|
215 |
-
$defaults = $this->config;
|
216 |
-
|
217 |
-
if (!empty($defaults['headers'])) {
|
218 |
-
// Default headers are only added if they are not present.
|
219 |
-
$defaults['_conditional'] = $defaults['headers'];
|
220 |
-
unset($defaults['headers']);
|
221 |
-
}
|
222 |
-
|
223 |
-
// Special handling for headers is required as they are added as
|
224 |
-
// conditional headers and as headers passed to a request ctor.
|
225 |
-
if (array_key_exists('headers', $options)) {
|
226 |
-
// Allows default headers to be unset.
|
227 |
-
if ($options['headers'] === null) {
|
228 |
-
$defaults['_conditional'] = null;
|
229 |
-
unset($options['headers']);
|
230 |
-
} elseif (!is_array($options['headers'])) {
|
231 |
-
throw new \InvalidArgumentException('headers must be an array');
|
232 |
-
}
|
233 |
-
}
|
234 |
-
|
235 |
-
// Shallow merge defaults underneath options.
|
236 |
-
$result = $options + $defaults;
|
237 |
-
|
238 |
-
// Remove null values.
|
239 |
-
foreach ($result as $k => $v) {
|
240 |
-
if ($v === null) {
|
241 |
-
unset($result[$k]);
|
242 |
-
}
|
243 |
-
}
|
244 |
-
|
245 |
-
return $result;
|
246 |
-
}
|
247 |
-
|
248 |
-
/**
|
249 |
-
* Transfers the given request and applies request options.
|
250 |
-
*
|
251 |
-
* The URI of the request is not modified and the request options are used
|
252 |
-
* as-is without merging in default options.
|
253 |
-
*
|
254 |
-
* @param RequestInterface $request
|
255 |
-
* @param array $options
|
256 |
-
*
|
257 |
-
* @return Promise\PromiseInterface
|
258 |
-
*/
|
259 |
-
private function transfer(RequestInterface $request, array $options)
|
260 |
-
{
|
261 |
-
// save_to -> sink
|
262 |
-
if (isset($options['save_to'])) {
|
263 |
-
$options['sink'] = $options['save_to'];
|
264 |
-
unset($options['save_to']);
|
265 |
-
}
|
266 |
-
|
267 |
-
// exceptions -> http_errors
|
268 |
-
if (isset($options['exceptions'])) {
|
269 |
-
$options['http_errors'] = $options['exceptions'];
|
270 |
-
unset($options['exceptions']);
|
271 |
-
}
|
272 |
-
|
273 |
-
$request = $this->applyOptions($request, $options);
|
274 |
-
$handler = $options['handler'];
|
275 |
-
|
276 |
-
try {
|
277 |
-
return Promise\promise_for($handler($request, $options));
|
278 |
-
} catch (\Exception $e) {
|
279 |
-
return Promise\rejection_for($e);
|
280 |
-
}
|
281 |
-
}
|
282 |
-
|
283 |
-
/**
|
284 |
-
* Applies the array of request options to a request.
|
285 |
-
*
|
286 |
-
* @param RequestInterface $request
|
287 |
-
* @param array $options
|
288 |
-
*
|
289 |
-
* @return RequestInterface
|
290 |
-
*/
|
291 |
-
private function applyOptions(RequestInterface $request, array &$options)
|
292 |
-
{
|
293 |
-
$modify = [
|
294 |
-
'set_headers' => [],
|
295 |
-
];
|
296 |
-
|
297 |
-
if (isset($options['headers'])) {
|
298 |
-
$modify['set_headers'] = $options['headers'];
|
299 |
-
unset($options['headers']);
|
300 |
-
}
|
301 |
-
|
302 |
-
if (isset($options['form_params'])) {
|
303 |
-
if (isset($options['multipart'])) {
|
304 |
-
throw new \InvalidArgumentException('You cannot use '
|
305 |
-
. 'form_params and multipart at the same time. Use the '
|
306 |
-
. 'form_params option if you want to send application/'
|
307 |
-
. 'x-www-form-urlencoded requests, and the multipart '
|
308 |
-
. 'option to send multipart/form-data requests.');
|
309 |
-
}
|
310 |
-
$options['body'] = http_build_query($options['form_params'], '', '&');
|
311 |
-
unset($options['form_params']);
|
312 |
-
// Ensure that we don't have the header in different case and set the new value.
|
313 |
-
$options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
|
314 |
-
$options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded';
|
315 |
-
}
|
316 |
-
|
317 |
-
if (isset($options['multipart'])) {
|
318 |
-
$options['body'] = new Psr7\MultipartStream($options['multipart']);
|
319 |
-
unset($options['multipart']);
|
320 |
-
}
|
321 |
-
|
322 |
-
if (isset($options['json'])) {
|
323 |
-
$options['body'] = \GuzzleHttp\json_encode($options['json']);
|
324 |
-
unset($options['json']);
|
325 |
-
// Ensure that we don't have the header in different case and set the new value.
|
326 |
-
$options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
|
327 |
-
$options['_conditional']['Content-Type'] = 'application/json';
|
328 |
-
}
|
329 |
-
|
330 |
-
if (!empty($options['decode_content'])
|
331 |
-
&& $options['decode_content'] !== true
|
332 |
-
) {
|
333 |
-
// Ensure that we don't have the header in different case and set the new value.
|
334 |
-
$options['_conditional'] = Psr7\_caseless_remove(['Accept-Encoding'], $options['_conditional']);
|
335 |
-
$modify['set_headers']['Accept-Encoding'] = $options['decode_content'];
|
336 |
-
}
|
337 |
-
|
338 |
-
if (isset($options['body'])) {
|
339 |
-
if (is_array($options['body'])) {
|
340 |
-
$this->invalidBody();
|
341 |
-
}
|
342 |
-
$modify['body'] = Psr7\stream_for($options['body']);
|
343 |
-
unset($options['body']);
|
344 |
-
}
|
345 |
-
|
346 |
-
if (!empty($options['auth']) && is_array($options['auth'])) {
|
347 |
-
$value = $options['auth'];
|
348 |
-
$type = isset($value[2]) ? strtolower($value[2]) : 'basic';
|
349 |
-
switch ($type) {
|
350 |
-
case 'basic':
|
351 |
-
// Ensure that we don't have the header in different case and set the new value.
|
352 |
-
$modify['set_headers'] = Psr7\_caseless_remove(['Authorization'], $modify['set_headers']);
|
353 |
-
$modify['set_headers']['Authorization'] = 'Basic '
|
354 |
-
. base64_encode("$value[0]:$value[1]");
|
355 |
-
break;
|
356 |
-
case 'digest':
|
357 |
-
// @todo: Do not rely on curl
|
358 |
-
$options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST;
|
359 |
-
$options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]";
|
360 |
-
break;
|
361 |
-
case 'ntlm':
|
362 |
-
$options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_NTLM;
|
363 |
-
$options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]";
|
364 |
-
break;
|
365 |
-
}
|
366 |
-
}
|
367 |
-
|
368 |
-
if (isset($options['query'])) {
|
369 |
-
$value = $options['query'];
|
370 |
-
if (is_array($value)) {
|
371 |
-
$value = http_build_query($value, null, '&', PHP_QUERY_RFC3986);
|
372 |
-
}
|
373 |
-
if (!is_string($value)) {
|
374 |
-
throw new \InvalidArgumentException('query must be a string or array');
|
375 |
-
}
|
376 |
-
$modify['query'] = $value;
|
377 |
-
unset($options['query']);
|
378 |
-
}
|
379 |
-
|
380 |
-
// Ensure that sink is not an invalid value.
|
381 |
-
if (isset($options['sink'])) {
|
382 |
-
// TODO: Add more sink validation?
|
383 |
-
if (is_bool($options['sink'])) {
|
384 |
-
throw new \InvalidArgumentException('sink must not be a boolean');
|
385 |
-
}
|
386 |
-
}
|
387 |
-
|
388 |
-
$request = Psr7\modify_request($request, $modify);
|
389 |
-
if ($request->getBody() instanceof Psr7\MultipartStream) {
|
390 |
-
// Use a multipart/form-data POST if a Content-Type is not set.
|
391 |
-
// Ensure that we don't have the header in different case and set the new value.
|
392 |
-
$options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
|
393 |
-
$options['_conditional']['Content-Type'] = 'multipart/form-data; boundary='
|
394 |
-
. $request->getBody()->getBoundary();
|
395 |
-
}
|
396 |
-
|
397 |
-
// Merge in conditional headers if they are not present.
|
398 |
-
if (isset($options['_conditional'])) {
|
399 |
-
// Build up the changes so it's in a single clone of the message.
|
400 |
-
$modify = [];
|
401 |
-
foreach ($options['_conditional'] as $k => $v) {
|
402 |
-
if (!$request->hasHeader($k)) {
|
403 |
-
$modify['set_headers'][$k] = $v;
|
404 |
-
}
|
405 |
-
}
|
406 |
-
$request = Psr7\modify_request($request, $modify);
|
407 |
-
// Don't pass this internal value along to middleware/handlers.
|
408 |
-
unset($options['_conditional']);
|
409 |
-
}
|
410 |
-
|
411 |
-
return $request;
|
412 |
-
}
|
413 |
-
|
414 |
-
private function invalidBody()
|
415 |
-
{
|
416 |
-
throw new \InvalidArgumentException('Passing in the "body" request '
|
417 |
-
. 'option as an array to send a POST request has been deprecated. '
|
418 |
-
. 'Please use the "form_params" request option to send a '
|
419 |
-
. 'application/x-www-form-urlencoded request, or the "multipart" '
|
420 |
-
. 'request option to send a multipart/form-data request.');
|
421 |
-
}
|
422 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp;
|
3 |
+
|
4 |
+
use GuzzleHttp\Cookie\CookieJar;
|
5 |
+
use GuzzleHttp\Promise;
|
6 |
+
use GuzzleHttp\Psr7;
|
7 |
+
use Psr\Http\Message\UriInterface;
|
8 |
+
use Psr\Http\Message\RequestInterface;
|
9 |
+
use Psr\Http\Message\ResponseInterface;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @method ResponseInterface get(string|UriInterface $uri, array $options = [])
|
13 |
+
* @method ResponseInterface head(string|UriInterface $uri, array $options = [])
|
14 |
+
* @method ResponseInterface put(string|UriInterface $uri, array $options = [])
|
15 |
+
* @method ResponseInterface post(string|UriInterface $uri, array $options = [])
|
16 |
+
* @method ResponseInterface patch(string|UriInterface $uri, array $options = [])
|
17 |
+
* @method ResponseInterface delete(string|UriInterface $uri, array $options = [])
|
18 |
+
* @method Promise\PromiseInterface getAsync(string|UriInterface $uri, array $options = [])
|
19 |
+
* @method Promise\PromiseInterface headAsync(string|UriInterface $uri, array $options = [])
|
20 |
+
* @method Promise\PromiseInterface putAsync(string|UriInterface $uri, array $options = [])
|
21 |
+
* @method Promise\PromiseInterface postAsync(string|UriInterface $uri, array $options = [])
|
22 |
+
* @method Promise\PromiseInterface patchAsync(string|UriInterface $uri, array $options = [])
|
23 |
+
* @method Promise\PromiseInterface deleteAsync(string|UriInterface $uri, array $options = [])
|
24 |
+
*/
|
25 |
+
class Client implements ClientInterface
|
26 |
+
{
|
27 |
+
/** @var array Default request options */
|
28 |
+
private $config;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Clients accept an array of constructor parameters.
|
32 |
+
*
|
33 |
+
* Here's an example of creating a client using a base_uri and an array of
|
34 |
+
* default request options to apply to each request:
|
35 |
+
*
|
36 |
+
* $client = new Client([
|
37 |
+
* 'base_uri' => 'http://www.foo.com/1.0/',
|
38 |
+
* 'timeout' => 0,
|
39 |
+
* 'allow_redirects' => false,
|
40 |
+
* 'proxy' => '192.168.16.1:10'
|
41 |
+
* ]);
|
42 |
+
*
|
43 |
+
* Client configuration settings include the following options:
|
44 |
+
*
|
45 |
+
* - handler: (callable) Function that transfers HTTP requests over the
|
46 |
+
* wire. The function is called with a Psr7\Http\Message\RequestInterface
|
47 |
+
* and array of transfer options, and must return a
|
48 |
+
* GuzzleHttp\Promise\PromiseInterface that is fulfilled with a
|
49 |
+
* Psr7\Http\Message\ResponseInterface on success. "handler" is a
|
50 |
+
* constructor only option that cannot be overridden in per/request
|
51 |
+
* options. If no handler is provided, a default handler will be created
|
52 |
+
* that enables all of the request options below by attaching all of the
|
53 |
+
* default middleware to the handler.
|
54 |
+
* - base_uri: (string|UriInterface) Base URI of the client that is merged
|
55 |
+
* into relative URIs. Can be a string or instance of UriInterface.
|
56 |
+
* - **: any request option
|
57 |
+
*
|
58 |
+
* @param array $config Client configuration settings.
|
59 |
+
*
|
60 |
+
* @see \GuzzleHttp\RequestOptions for a list of available request options.
|
61 |
+
*/
|
62 |
+
public function __construct(array $config = [])
|
63 |
+
{
|
64 |
+
if (!isset($config['handler'])) {
|
65 |
+
$config['handler'] = HandlerStack::create();
|
66 |
+
} elseif (!is_callable($config['handler'])) {
|
67 |
+
throw new \InvalidArgumentException('handler must be a callable');
|
68 |
+
}
|
69 |
+
|
70 |
+
// Convert the base_uri to a UriInterface
|
71 |
+
if (isset($config['base_uri'])) {
|
72 |
+
$config['base_uri'] = Psr7\uri_for($config['base_uri']);
|
73 |
+
}
|
74 |
+
|
75 |
+
$this->configureDefaults($config);
|
76 |
+
}
|
77 |
+
|
78 |
+
public function __call($method, $args)
|
79 |
+
{
|
80 |
+
if (count($args) < 1) {
|
81 |
+
throw new \InvalidArgumentException('Magic request methods require a URI and optional options array');
|
82 |
+
}
|
83 |
+
|
84 |
+
$uri = $args[0];
|
85 |
+
$opts = isset($args[1]) ? $args[1] : [];
|
86 |
+
|
87 |
+
return substr($method, -5) === 'Async'
|
88 |
+
? $this->requestAsync(substr($method, 0, -5), $uri, $opts)
|
89 |
+
: $this->request($method, $uri, $opts);
|
90 |
+
}
|
91 |
+
|
92 |
+
public function sendAsync(RequestInterface $request, array $options = [])
|
93 |
+
{
|
94 |
+
// Merge the base URI into the request URI if needed.
|
95 |
+
$options = $this->prepareDefaults($options);
|
96 |
+
|
97 |
+
return $this->transfer(
|
98 |
+
$request->withUri($this->buildUri($request->getUri(), $options), $request->hasHeader('Host')),
|
99 |
+
$options
|
100 |
+
);
|
101 |
+
}
|
102 |
+
|
103 |
+
public function send(RequestInterface $request, array $options = [])
|
104 |
+
{
|
105 |
+
$options[RequestOptions::SYNCHRONOUS] = true;
|
106 |
+
return $this->sendAsync($request, $options)->wait();
|
107 |
+
}
|
108 |
+
|
109 |
+
public function requestAsync($method, $uri = '', array $options = [])
|
110 |
+
{
|
111 |
+
$options = $this->prepareDefaults($options);
|
112 |
+
// Remove request modifying parameter because it can be done up-front.
|
113 |
+
$headers = isset($options['headers']) ? $options['headers'] : [];
|
114 |
+
$body = isset($options['body']) ? $options['body'] : null;
|
115 |
+
$version = isset($options['version']) ? $options['version'] : '1.1';
|
116 |
+
// Merge the URI into the base URI.
|
117 |
+
$uri = $this->buildUri($uri, $options);
|
118 |
+
if (is_array($body)) {
|
119 |
+
$this->invalidBody();
|
120 |
+
}
|
121 |
+
$request = new Psr7\Request($method, $uri, $headers, $body, $version);
|
122 |
+
// Remove the option so that they are not doubly-applied.
|
123 |
+
unset($options['headers'], $options['body'], $options['version']);
|
124 |
+
|
125 |
+
return $this->transfer($request, $options);
|
126 |
+
}
|
127 |
+
|
128 |
+
public function request($method, $uri = '', array $options = [])
|
129 |
+
{
|
130 |
+
$options[RequestOptions::SYNCHRONOUS] = true;
|
131 |
+
return $this->requestAsync($method, $uri, $options)->wait();
|
132 |
+
}
|
133 |
+
|
134 |
+
public function getConfig($option = null)
|
135 |
+
{
|
136 |
+
return $option === null
|
137 |
+
? $this->config
|
138 |
+
: (isset($this->config[$option]) ? $this->config[$option] : null);
|
139 |
+
}
|
140 |
+
|
141 |
+
private function buildUri($uri, array $config)
|
142 |
+
{
|
143 |
+
// for BC we accept null which would otherwise fail in uri_for
|
144 |
+
$uri = Psr7\uri_for($uri === null ? '' : $uri);
|
145 |
+
|
146 |
+
if (isset($config['base_uri'])) {
|
147 |
+
$uri = Psr7\UriResolver::resolve(Psr7\uri_for($config['base_uri']), $uri);
|
148 |
+
}
|
149 |
+
|
150 |
+
return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri;
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
* Configures the default options for a client.
|
155 |
+
*
|
156 |
+
* @param array $config
|
157 |
+
*/
|
158 |
+
private function configureDefaults(array $config)
|
159 |
+
{
|
160 |
+
$defaults = [
|
161 |
+
'allow_redirects' => RedirectMiddleware::$defaultSettings,
|
162 |
+
'http_errors' => true,
|
163 |
+
'decode_content' => true,
|
164 |
+
'verify' => true,
|
165 |
+
'cookies' => false
|
166 |
+
];
|
167 |
+
|
168 |
+
// Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set.
|
169 |
+
|
170 |
+
// We can only trust the HTTP_PROXY environment variable in a CLI
|
171 |
+
// process due to the fact that PHP has no reliable mechanism to
|
172 |
+
// get environment variables that start with "HTTP_".
|
173 |
+
if (php_sapi_name() == 'cli' && getenv('HTTP_PROXY')) {
|
174 |
+
$defaults['proxy']['http'] = getenv('HTTP_PROXY');
|
175 |
+
}
|
176 |
+
|
177 |
+
if ($proxy = getenv('HTTPS_PROXY')) {
|
178 |
+
$defaults['proxy']['https'] = $proxy;
|
179 |
+
}
|
180 |
+
|
181 |
+
if ($noProxy = getenv('NO_PROXY')) {
|
182 |
+
$cleanedNoProxy = str_replace(' ', '', $noProxy);
|
183 |
+
$defaults['proxy']['no'] = explode(',', $cleanedNoProxy);
|
184 |
+
}
|
185 |
+
|
186 |
+
$this->config = $config + $defaults;
|
187 |
+
|
188 |
+
if (!empty($config['cookies']) && $config['cookies'] === true) {
|
189 |
+
$this->config['cookies'] = new CookieJar();
|
190 |
+
}
|
191 |
+
|
192 |
+
// Add the default user-agent header.
|
193 |
+
if (!isset($this->config['headers'])) {
|
194 |
+
$this->config['headers'] = ['User-Agent' => default_user_agent()];
|
195 |
+
} else {
|
196 |
+
// Add the User-Agent header if one was not already set.
|
197 |
+
foreach (array_keys($this->config['headers']) as $name) {
|
198 |
+
if (strtolower($name) === 'user-agent') {
|
199 |
+
return;
|
200 |
+
}
|
201 |
+
}
|
202 |
+
$this->config['headers']['User-Agent'] = default_user_agent();
|
203 |
+
}
|
204 |
+
}
|
205 |
+
|
206 |
+
/**
|
207 |
+
* Merges default options into the array.
|
208 |
+
*
|
209 |
+
* @param array $options Options to modify by reference
|
210 |
+
*
|
211 |
+
* @return array
|
212 |
+
*/
|
213 |
+
private function prepareDefaults($options)
|
214 |
+
{
|
215 |
+
$defaults = $this->config;
|
216 |
+
|
217 |
+
if (!empty($defaults['headers'])) {
|
218 |
+
// Default headers are only added if they are not present.
|
219 |
+
$defaults['_conditional'] = $defaults['headers'];
|
220 |
+
unset($defaults['headers']);
|
221 |
+
}
|
222 |
+
|
223 |
+
// Special handling for headers is required as they are added as
|
224 |
+
// conditional headers and as headers passed to a request ctor.
|
225 |
+
if (array_key_exists('headers', $options)) {
|
226 |
+
// Allows default headers to be unset.
|
227 |
+
if ($options['headers'] === null) {
|
228 |
+
$defaults['_conditional'] = null;
|
229 |
+
unset($options['headers']);
|
230 |
+
} elseif (!is_array($options['headers'])) {
|
231 |
+
throw new \InvalidArgumentException('headers must be an array');
|
232 |
+
}
|
233 |
+
}
|
234 |
+
|
235 |
+
// Shallow merge defaults underneath options.
|
236 |
+
$result = $options + $defaults;
|
237 |
+
|
238 |
+
// Remove null values.
|
239 |
+
foreach ($result as $k => $v) {
|
240 |
+
if ($v === null) {
|
241 |
+
unset($result[$k]);
|
242 |
+
}
|
243 |
+
}
|
244 |
+
|
245 |
+
return $result;
|
246 |
+
}
|
247 |
+
|
248 |
+
/**
|
249 |
+
* Transfers the given request and applies request options.
|
250 |
+
*
|
251 |
+
* The URI of the request is not modified and the request options are used
|
252 |
+
* as-is without merging in default options.
|
253 |
+
*
|
254 |
+
* @param RequestInterface $request
|
255 |
+
* @param array $options
|
256 |
+
*
|
257 |
+
* @return Promise\PromiseInterface
|
258 |
+
*/
|
259 |
+
private function transfer(RequestInterface $request, array $options)
|
260 |
+
{
|
261 |
+
// save_to -> sink
|
262 |
+
if (isset($options['save_to'])) {
|
263 |
+
$options['sink'] = $options['save_to'];
|
264 |
+
unset($options['save_to']);
|
265 |
+
}
|
266 |
+
|
267 |
+
// exceptions -> http_errors
|
268 |
+
if (isset($options['exceptions'])) {
|
269 |
+
$options['http_errors'] = $options['exceptions'];
|
270 |
+
unset($options['exceptions']);
|
271 |
+
}
|
272 |
+
|
273 |
+
$request = $this->applyOptions($request, $options);
|
274 |
+
$handler = $options['handler'];
|
275 |
+
|
276 |
+
try {
|
277 |
+
return Promise\promise_for($handler($request, $options));
|
278 |
+
} catch (\Exception $e) {
|
279 |
+
return Promise\rejection_for($e);
|
280 |
+
}
|
281 |
+
}
|
282 |
+
|
283 |
+
/**
|
284 |
+
* Applies the array of request options to a request.
|
285 |
+
*
|
286 |
+
* @param RequestInterface $request
|
287 |
+
* @param array $options
|
288 |
+
*
|
289 |
+
* @return RequestInterface
|
290 |
+
*/
|
291 |
+
private function applyOptions(RequestInterface $request, array &$options)
|
292 |
+
{
|
293 |
+
$modify = [
|
294 |
+
'set_headers' => [],
|
295 |
+
];
|
296 |
+
|
297 |
+
if (isset($options['headers'])) {
|
298 |
+
$modify['set_headers'] = $options['headers'];
|
299 |
+
unset($options['headers']);
|
300 |
+
}
|
301 |
+
|
302 |
+
if (isset($options['form_params'])) {
|
303 |
+
if (isset($options['multipart'])) {
|
304 |
+
throw new \InvalidArgumentException('You cannot use '
|
305 |
+
. 'form_params and multipart at the same time. Use the '
|
306 |
+
. 'form_params option if you want to send application/'
|
307 |
+
. 'x-www-form-urlencoded requests, and the multipart '
|
308 |
+
. 'option to send multipart/form-data requests.');
|
309 |
+
}
|
310 |
+
$options['body'] = http_build_query($options['form_params'], '', '&');
|
311 |
+
unset($options['form_params']);
|
312 |
+
// Ensure that we don't have the header in different case and set the new value.
|
313 |
+
$options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
|
314 |
+
$options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded';
|
315 |
+
}
|
316 |
+
|
317 |
+
if (isset($options['multipart'])) {
|
318 |
+
$options['body'] = new Psr7\MultipartStream($options['multipart']);
|
319 |
+
unset($options['multipart']);
|
320 |
+
}
|
321 |
+
|
322 |
+
if (isset($options['json'])) {
|
323 |
+
$options['body'] = \GuzzleHttp\json_encode($options['json']);
|
324 |
+
unset($options['json']);
|
325 |
+
// Ensure that we don't have the header in different case and set the new value.
|
326 |
+
$options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
|
327 |
+
$options['_conditional']['Content-Type'] = 'application/json';
|
328 |
+
}
|
329 |
+
|
330 |
+
if (!empty($options['decode_content'])
|
331 |
+
&& $options['decode_content'] !== true
|
332 |
+
) {
|
333 |
+
// Ensure that we don't have the header in different case and set the new value.
|
334 |
+
$options['_conditional'] = Psr7\_caseless_remove(['Accept-Encoding'], $options['_conditional']);
|
335 |
+
$modify['set_headers']['Accept-Encoding'] = $options['decode_content'];
|
336 |
+
}
|
337 |
+
|
338 |
+
if (isset($options['body'])) {
|
339 |
+
if (is_array($options['body'])) {
|
340 |
+
$this->invalidBody();
|
341 |
+
}
|
342 |
+
$modify['body'] = Psr7\stream_for($options['body']);
|
343 |
+
unset($options['body']);
|
344 |
+
}
|
345 |
+
|
346 |
+
if (!empty($options['auth']) && is_array($options['auth'])) {
|
347 |
+
$value = $options['auth'];
|
348 |
+
$type = isset($value[2]) ? strtolower($value[2]) : 'basic';
|
349 |
+
switch ($type) {
|
350 |
+
case 'basic':
|
351 |
+
// Ensure that we don't have the header in different case and set the new value.
|
352 |
+
$modify['set_headers'] = Psr7\_caseless_remove(['Authorization'], $modify['set_headers']);
|
353 |
+
$modify['set_headers']['Authorization'] = 'Basic '
|
354 |
+
. base64_encode("$value[0]:$value[1]");
|
355 |
+
break;
|
356 |
+
case 'digest':
|
357 |
+
// @todo: Do not rely on curl
|
358 |
+
$options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST;
|
359 |
+
$options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]";
|
360 |
+
break;
|
361 |
+
case 'ntlm':
|
362 |
+
$options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_NTLM;
|
363 |
+
$options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]";
|
364 |
+
break;
|
365 |
+
}
|
366 |
+
}
|
367 |
+
|
368 |
+
if (isset($options['query'])) {
|
369 |
+
$value = $options['query'];
|
370 |
+
if (is_array($value)) {
|
371 |
+
$value = http_build_query($value, null, '&', PHP_QUERY_RFC3986);
|
372 |
+
}
|
373 |
+
if (!is_string($value)) {
|
374 |
+
throw new \InvalidArgumentException('query must be a string or array');
|
375 |
+
}
|
376 |
+
$modify['query'] = $value;
|
377 |
+
unset($options['query']);
|
378 |
+
}
|
379 |
+
|
380 |
+
// Ensure that sink is not an invalid value.
|
381 |
+
if (isset($options['sink'])) {
|
382 |
+
// TODO: Add more sink validation?
|
383 |
+
if (is_bool($options['sink'])) {
|
384 |
+
throw new \InvalidArgumentException('sink must not be a boolean');
|
385 |
+
}
|
386 |
+
}
|
387 |
+
|
388 |
+
$request = Psr7\modify_request($request, $modify);
|
389 |
+
if ($request->getBody() instanceof Psr7\MultipartStream) {
|
390 |
+
// Use a multipart/form-data POST if a Content-Type is not set.
|
391 |
+
// Ensure that we don't have the header in different case and set the new value.
|
392 |
+
$options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
|
393 |
+
$options['_conditional']['Content-Type'] = 'multipart/form-data; boundary='
|
394 |
+
. $request->getBody()->getBoundary();
|
395 |
+
}
|
396 |
+
|
397 |
+
// Merge in conditional headers if they are not present.
|
398 |
+
if (isset($options['_conditional'])) {
|
399 |
+
// Build up the changes so it's in a single clone of the message.
|
400 |
+
$modify = [];
|
401 |
+
foreach ($options['_conditional'] as $k => $v) {
|
402 |
+
if (!$request->hasHeader($k)) {
|
403 |
+
$modify['set_headers'][$k] = $v;
|
404 |
+
}
|
405 |
+
}
|
406 |
+
$request = Psr7\modify_request($request, $modify);
|
407 |
+
// Don't pass this internal value along to middleware/handlers.
|
408 |
+
unset($options['_conditional']);
|
409 |
+
}
|
410 |
+
|
411 |
+
return $request;
|
412 |
+
}
|
413 |
+
|
414 |
+
private function invalidBody()
|
415 |
+
{
|
416 |
+
throw new \InvalidArgumentException('Passing in the "body" request '
|
417 |
+
. 'option as an array to send a POST request has been deprecated. '
|
418 |
+
. 'Please use the "form_params" request option to send a '
|
419 |
+
. 'application/x-www-form-urlencoded request, or the "multipart" '
|
420 |
+
. 'request option to send a multipart/form-data request.');
|
421 |
+
}
|
422 |
+
}
|
vendor/guzzlehttp/guzzle/src/ClientInterface.php
CHANGED
@@ -1,84 +1,84 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp;
|
3 |
-
|
4 |
-
use GuzzleHttp\Promise\PromiseInterface;
|
5 |
-
use GuzzleHttp\Exception\GuzzleException;
|
6 |
-
use Psr\Http\Message\RequestInterface;
|
7 |
-
use Psr\Http\Message\ResponseInterface;
|
8 |
-
use Psr\Http\Message\UriInterface;
|
9 |
-
|
10 |
-
/**
|
11 |
-
* Client interface for sending HTTP requests.
|
12 |
-
*/
|
13 |
-
interface ClientInterface
|
14 |
-
{
|
15 |
-
const VERSION = '6.3.3';
|
16 |
-
|
17 |
-
/**
|
18 |
-
* Send an HTTP request.
|
19 |
-
*
|
20 |
-
* @param RequestInterface $request Request to send
|
21 |
-
* @param array $options Request options to apply to the given
|
22 |
-
* request and to the transfer.
|
23 |
-
*
|
24 |
-
* @return ResponseInterface
|
25 |
-
* @throws GuzzleException
|
26 |
-
*/
|
27 |
-
public function send(RequestInterface $request, array $options = []);
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Asynchronously send an HTTP request.
|
31 |
-
*
|
32 |
-
* @param RequestInterface $request Request to send
|
33 |
-
* @param array $options Request options to apply to the given
|
34 |
-
* request and to the transfer.
|
35 |
-
*
|
36 |
-
* @return PromiseInterface
|
37 |
-
*/
|
38 |
-
public function sendAsync(RequestInterface $request, array $options = []);
|
39 |
-
|
40 |
-
/**
|
41 |
-
* Create and send an HTTP request.
|
42 |
-
*
|
43 |
-
* Use an absolute path to override the base path of the client, or a
|
44 |
-
* relative path to append to the base path of the client. The URL can
|
45 |
-
* contain the query string as well.
|
46 |
-
*
|
47 |
-
* @param string $method HTTP method.
|
48 |
-
* @param string|UriInterface $uri URI object or string.
|
49 |
-
* @param array $options Request options to apply.
|
50 |
-
*
|
51 |
-
* @return ResponseInterface
|
52 |
-
* @throws GuzzleException
|
53 |
-
*/
|
54 |
-
public function request($method, $uri, array $options = []);
|
55 |
-
|
56 |
-
/**
|
57 |
-
* Create and send an asynchronous HTTP request.
|
58 |
-
*
|
59 |
-
* Use an absolute path to override the base path of the client, or a
|
60 |
-
* relative path to append to the base path of the client. The URL can
|
61 |
-
* contain the query string as well. Use an array to provide a URL
|
62 |
-
* template and additional variables to use in the URL template expansion.
|
63 |
-
*
|
64 |
-
* @param string $method HTTP method
|
65 |
-
* @param string|UriInterface $uri URI object or string.
|
66 |
-
* @param array $options Request options to apply.
|
67 |
-
*
|
68 |
-
* @return PromiseInterface
|
69 |
-
*/
|
70 |
-
public function requestAsync($method, $uri, array $options = []);
|
71 |
-
|
72 |
-
/**
|
73 |
-
* Get a client configuration option.
|
74 |
-
*
|
75 |
-
* These options include default request options of the client, a "handler"
|
76 |
-
* (if utilized by the concrete client), and a "base_uri" if utilized by
|
77 |
-
* the concrete client.
|
78 |
-
*
|
79 |
-
* @param string|null $option The config option to retrieve.
|
80 |
-
*
|
81 |
-
* @return mixed
|
82 |
-
*/
|
83 |
-
public function getConfig($option = null);
|
84 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp;
|
3 |
+
|
4 |
+
use GuzzleHttp\Promise\PromiseInterface;
|
5 |
+
use GuzzleHttp\Exception\GuzzleException;
|
6 |
+
use Psr\Http\Message\RequestInterface;
|
7 |
+
use Psr\Http\Message\ResponseInterface;
|
8 |
+
use Psr\Http\Message\UriInterface;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Client interface for sending HTTP requests.
|
12 |
+
*/
|
13 |
+
interface ClientInterface
|
14 |
+
{
|
15 |
+
const VERSION = '6.3.3';
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Send an HTTP request.
|
19 |
+
*
|
20 |
+
* @param RequestInterface $request Request to send
|
21 |
+
* @param array $options Request options to apply to the given
|
22 |
+
* request and to the transfer.
|
23 |
+
*
|
24 |
+
* @return ResponseInterface
|
25 |
+
* @throws GuzzleException
|
26 |
+
*/
|
27 |
+
public function send(RequestInterface $request, array $options = []);
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Asynchronously send an HTTP request.
|
31 |
+
*
|
32 |
+
* @param RequestInterface $request Request to send
|
33 |
+
* @param array $options Request options to apply to the given
|
34 |
+
* request and to the transfer.
|
35 |
+
*
|
36 |
+
* @return PromiseInterface
|
37 |
+
*/
|
38 |
+
public function sendAsync(RequestInterface $request, array $options = []);
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Create and send an HTTP request.
|
42 |
+
*
|
43 |
+
* Use an absolute path to override the base path of the client, or a
|
44 |
+
* relative path to append to the base path of the client. The URL can
|
45 |
+
* contain the query string as well.
|
46 |
+
*
|
47 |
+
* @param string $method HTTP method.
|
48 |
+
* @param string|UriInterface $uri URI object or string.
|
49 |
+
* @param array $options Request options to apply.
|
50 |
+
*
|
51 |
+
* @return ResponseInterface
|
52 |
+
* @throws GuzzleException
|
53 |
+
*/
|
54 |
+
public function request($method, $uri, array $options = []);
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Create and send an asynchronous HTTP request.
|
58 |
+
*
|
59 |
+
* Use an absolute path to override the base path of the client, or a
|
60 |
+
* relative path to append to the base path of the client. The URL can
|
61 |
+
* contain the query string as well. Use an array to provide a URL
|
62 |
+
* template and additional variables to use in the URL template expansion.
|
63 |
+
*
|
64 |
+
* @param string $method HTTP method
|
65 |
+
* @param string|UriInterface $uri URI object or string.
|
66 |
+
* @param array $options Request options to apply.
|
67 |
+
*
|
68 |
+
* @return PromiseInterface
|
69 |
+
*/
|
70 |
+
public function requestAsync($method, $uri, array $options = []);
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Get a client configuration option.
|
74 |
+
*
|
75 |
+
* These options include default request options of the client, a "handler"
|
76 |
+
* (if utilized by the concrete client), and a "base_uri" if utilized by
|
77 |
+
* the concrete client.
|
78 |
+
*
|
79 |
+
* @param string|null $option The config option to retrieve.
|
80 |
+
*
|
81 |
+
* @return mixed
|
82 |
+
*/
|
83 |
+
public function getConfig($option = null);
|
84 |
+
}
|
vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php
CHANGED
@@ -1,314 +1,314 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Cookie;
|
3 |
-
|
4 |
-
use Psr\Http\Message\RequestInterface;
|
5 |
-
use Psr\Http\Message\ResponseInterface;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* Cookie jar that stores cookies as an array
|
9 |
-
*/
|
10 |
-
class CookieJar implements CookieJarInterface
|
11 |
-
{
|
12 |
-
/** @var SetCookie[] Loaded cookie data */
|
13 |
-
private $cookies = [];
|
14 |
-
|
15 |
-
/** @var bool */
|
16 |
-
private $strictMode;
|
17 |
-
|
18 |
-
/**
|
19 |
-
* @param bool $strictMode Set to true to throw exceptions when invalid
|
20 |
-
* cookies are added to the cookie jar.
|
21 |
-
* @param array $cookieArray Array of SetCookie objects or a hash of
|
22 |
-
* arrays that can be used with the SetCookie
|
23 |
-
* constructor
|
24 |
-
*/
|
25 |
-
public function __construct($strictMode = false, $cookieArray = [])
|
26 |
-
{
|
27 |
-
$this->strictMode = $strictMode;
|
28 |
-
|
29 |
-
foreach ($cookieArray as $cookie) {
|
30 |
-
if (!($cookie instanceof SetCookie)) {
|
31 |
-
$cookie = new SetCookie($cookie);
|
32 |
-
}
|
33 |
-
$this->setCookie($cookie);
|
34 |
-
}
|
35 |
-
}
|
36 |
-
|
37 |
-
/**
|
38 |
-
* Create a new Cookie jar from an associative array and domain.
|
39 |
-
*
|
40 |
-
* @param array $cookies Cookies to create the jar from
|
41 |
-
* @param string $domain Domain to set the cookies to
|
42 |
-
*
|
43 |
-
* @return self
|
44 |
-
*/
|
45 |
-
public static function fromArray(array $cookies, $domain)
|
46 |
-
{
|
47 |
-
$cookieJar = new self();
|
48 |
-
foreach ($cookies as $name => $value) {
|
49 |
-
$cookieJar->setCookie(new SetCookie([
|
50 |
-
'Domain' => $domain,
|
51 |
-
'Name' => $name,
|
52 |
-
'Value' => $value,
|
53 |
-
'Discard' => true
|
54 |
-
]));
|
55 |
-
}
|
56 |
-
|
57 |
-
return $cookieJar;
|
58 |
-
}
|
59 |
-
|
60 |
-
/**
|
61 |
-
* @deprecated
|
62 |
-
*/
|
63 |
-
public static function getCookieValue($value)
|
64 |
-
{
|
65 |
-
return $value;
|
66 |
-
}
|
67 |
-
|
68 |
-
/**
|
69 |
-
* Evaluate if this cookie should be persisted to storage
|
70 |
-
* that survives between requests.
|
71 |
-
*
|
72 |
-
* @param SetCookie $cookie Being evaluated.
|
73 |
-
* @param bool $allowSessionCookies If we should persist session cookies
|
74 |
-
* @return bool
|
75 |
-
*/
|
76 |
-
public static function shouldPersist(
|
77 |
-
SetCookie $cookie,
|
78 |
-
$allowSessionCookies = false
|
79 |
-
) {
|
80 |
-
if ($cookie->getExpires() || $allowSessionCookies) {
|
81 |
-
if (!$cookie->getDiscard()) {
|
82 |
-
return true;
|
83 |
-
}
|
84 |
-
}
|
85 |
-
|
86 |
-
return false;
|
87 |
-
}
|
88 |
-
|
89 |
-
/**
|
90 |
-
* Finds and returns the cookie based on the name
|
91 |
-
*
|
92 |
-
* @param string $name cookie name to search for
|
93 |
-
* @return SetCookie|null cookie that was found or null if not found
|
94 |
-
*/
|
95 |
-
public function getCookieByName($name)
|
96 |
-
{
|
97 |
-
// don't allow a null name
|
98 |
-
if ($name === null) {
|
99 |
-
return null;
|
100 |
-
}
|
101 |
-
foreach ($this->cookies as $cookie) {
|
102 |
-
if ($cookie->getName() !== null && strcasecmp($cookie->getName(), $name) === 0) {
|
103 |
-
return $cookie;
|
104 |
-
}
|
105 |
-
}
|
106 |
-
}
|
107 |
-
|
108 |
-
public function toArray()
|
109 |
-
{
|
110 |
-
return array_map(function (SetCookie $cookie) {
|
111 |
-
return $cookie->toArray();
|
112 |
-
}, $this->getIterator()->getArrayCopy());
|
113 |
-
}
|
114 |
-
|
115 |
-
public function clear($domain = null, $path = null, $name = null)
|
116 |
-
{
|
117 |
-
if (!$domain) {
|
118 |
-
$this->cookies = [];
|
119 |
-
return;
|
120 |
-
} elseif (!$path) {
|
121 |
-
$this->cookies = array_filter(
|
122 |
-
$this->cookies,
|
123 |
-
function (SetCookie $cookie) use ($path, $domain) {
|
124 |
-
return !$cookie->matchesDomain($domain);
|
125 |
-
}
|
126 |
-
);
|
127 |
-
} elseif (!$name) {
|
128 |
-
$this->cookies = array_filter(
|
129 |
-
$this->cookies,
|
130 |
-
function (SetCookie $cookie) use ($path, $domain) {
|
131 |
-
return !($cookie->matchesPath($path) &&
|
132 |
-
$cookie->matchesDomain($domain));
|
133 |
-
}
|
134 |
-
);
|
135 |
-
} else {
|
136 |
-
$this->cookies = array_filter(
|
137 |
-
$this->cookies,
|
138 |
-
function (SetCookie $cookie) use ($path, $domain, $name) {
|
139 |
-
return !($cookie->getName() == $name &&
|
140 |
-
$cookie->matchesPath($path) &&
|
141 |
-
$cookie->matchesDomain($domain));
|
142 |
-
}
|
143 |
-
);
|
144 |
-
}
|
145 |
-
}
|
146 |
-
|
147 |
-
public function clearSessionCookies()
|
148 |
-
{
|
149 |
-
$this->cookies = array_filter(
|
150 |
-
$this->cookies,
|
151 |
-
function (SetCookie $cookie) {
|
152 |
-
return !$cookie->getDiscard() && $cookie->getExpires();
|
153 |
-
}
|
154 |
-
);
|
155 |
-
}
|
156 |
-
|
157 |
-
public function setCookie(SetCookie $cookie)
|
158 |
-
{
|
159 |
-
// If the name string is empty (but not 0), ignore the set-cookie
|
160 |
-
// string entirely.
|
161 |
-
$name = $cookie->getName();
|
162 |
-
if (!$name && $name !== '0') {
|
163 |
-
return false;
|
164 |
-
}
|
165 |
-
|
166 |
-
// Only allow cookies with set and valid domain, name, value
|
167 |
-
$result = $cookie->validate();
|
168 |
-
if ($result !== true) {
|
169 |
-
if ($this->strictMode) {
|
170 |
-
throw new \RuntimeException('Invalid cookie: ' . $result);
|
171 |
-
} else {
|
172 |
-
$this->removeCookieIfEmpty($cookie);
|
173 |
-
return false;
|
174 |
-
}
|
175 |
-
}
|
176 |
-
|
177 |
-
// Resolve conflicts with previously set cookies
|
178 |
-
foreach ($this->cookies as $i => $c) {
|
179 |
-
|
180 |
-
// Two cookies are identical, when their path, and domain are
|
181 |
-
// identical.
|
182 |
-
if ($c->getPath() != $cookie->getPath() ||
|
183 |
-
$c->getDomain() != $cookie->getDomain() ||
|
184 |
-
$c->getName() != $cookie->getName()
|
185 |
-
) {
|
186 |
-
continue;
|
187 |
-
}
|
188 |
-
|
189 |
-
// The previously set cookie is a discard cookie and this one is
|
190 |
-
// not so allow the new cookie to be set
|
191 |
-
if (!$cookie->getDiscard() && $c->getDiscard()) {
|
192 |
-
unset($this->cookies[$i]);
|
193 |
-
continue;
|
194 |
-
}
|
195 |
-
|
196 |
-
// If the new cookie's expiration is further into the future, then
|
197 |
-
// replace the old cookie
|
198 |
-
if ($cookie->getExpires() > $c->getExpires()) {
|
199 |
-
unset($this->cookies[$i]);
|
200 |
-
continue;
|
201 |
-
}
|
202 |
-
|
203 |
-
// If the value has changed, we better change it
|
204 |
-
if ($cookie->getValue() !== $c->getValue()) {
|
205 |
-
unset($this->cookies[$i]);
|
206 |
-
continue;
|
207 |
-
}
|
208 |
-
|
209 |
-
// The cookie exists, so no need to continue
|
210 |
-
return false;
|
211 |
-
}
|
212 |
-
|
213 |
-
$this->cookies[] = $cookie;
|
214 |
-
|
215 |
-
return true;
|
216 |
-
}
|
217 |
-
|
218 |
-
public function count()
|
219 |
-
{
|
220 |
-
return count($this->cookies);
|
221 |
-
}
|
222 |
-
|
223 |
-
public function getIterator()
|
224 |
-
{
|
225 |
-
return new \ArrayIterator(array_values($this->cookies));
|
226 |
-
}
|
227 |
-
|
228 |
-
public function extractCookies(
|
229 |
-
RequestInterface $request,
|
230 |
-
ResponseInterface $response
|
231 |
-
) {
|
232 |
-
if ($cookieHeader = $response->getHeader('Set-Cookie')) {
|
233 |
-
foreach ($cookieHeader as $cookie) {
|
234 |
-
$sc = SetCookie::fromString($cookie);
|
235 |
-
if (!$sc->getDomain()) {
|
236 |
-
$sc->setDomain($request->getUri()->getHost());
|
237 |
-
}
|
238 |
-
if (0 !== strpos($sc->getPath(), '/')) {
|
239 |
-
$sc->setPath($this->getCookiePathFromRequest($request));
|
240 |
-
}
|
241 |
-
$this->setCookie($sc);
|
242 |
-
}
|
243 |
-
}
|
244 |
-
}
|
245 |
-
|
246 |
-
/**
|
247 |
-
* Computes cookie path following RFC 6265 section 5.1.4
|
248 |
-
*
|
249 |
-
* @link https://tools.ietf.org/html/rfc6265#section-5.1.4
|
250 |
-
*
|
251 |
-
* @param RequestInterface $request
|
252 |
-
* @return string
|
253 |
-
*/
|
254 |
-
private function getCookiePathFromRequest(RequestInterface $request)
|
255 |
-
{
|
256 |
-
$uriPath = $request->getUri()->getPath();
|
257 |
-
if ('' === $uriPath) {
|
258 |
-
return '/';
|
259 |
-
}
|
260 |
-
if (0 !== strpos($uriPath, '/')) {
|
261 |
-
return '/';
|
262 |
-
}
|
263 |
-
if ('/' === $uriPath) {
|
264 |
-
return '/';
|
265 |
-
}
|
266 |
-
if (0 === $lastSlashPos = strrpos($uriPath, '/')) {
|
267 |
-
return '/';
|
268 |
-
}
|
269 |
-
|
270 |
-
return substr($uriPath, 0, $lastSlashPos);
|
271 |
-
}
|
272 |
-
|
273 |
-
public function withCookieHeader(RequestInterface $request)
|
274 |
-
{
|
275 |
-
$values = [];
|
276 |
-
$uri = $request->getUri();
|
277 |
-
$scheme = $uri->getScheme();
|
278 |
-
$host = $uri->getHost();
|
279 |
-
$path = $uri->getPath() ?: '/';
|
280 |
-
|
281 |
-
foreach ($this->cookies as $cookie) {
|
282 |
-
if ($cookie->matchesPath($path) &&
|
283 |
-
$cookie->matchesDomain($host) &&
|
284 |
-
!$cookie->isExpired() &&
|
285 |
-
(!$cookie->getSecure() || $scheme === 'https')
|
286 |
-
) {
|
287 |
-
$values[] = $cookie->getName() . '='
|
288 |
-
. $cookie->getValue();
|
289 |
-
}
|
290 |
-
}
|
291 |
-
|
292 |
-
return $values
|
293 |
-
? $request->withHeader('Cookie', implode('; ', $values))
|
294 |
-
: $request;
|
295 |
-
}
|
296 |
-
|
297 |
-
/**
|
298 |
-
* If a cookie already exists and the server asks to set it again with a
|
299 |
-
* null value, the cookie must be deleted.
|
300 |
-
*
|
301 |
-
* @param SetCookie $cookie
|
302 |
-
*/
|
303 |
-
private function removeCookieIfEmpty(SetCookie $cookie)
|
304 |
-
{
|
305 |
-
$cookieValue = $cookie->getValue();
|
306 |
-
if ($cookieValue === null || $cookieValue === '') {
|
307 |
-
$this->clear(
|
308 |
-
$cookie->getDomain(),
|
309 |
-
$cookie->getPath(),
|
310 |
-
$cookie->getName()
|
311 |
-
);
|
312 |
-
}
|
313 |
-
}
|
314 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Cookie;
|
3 |
+
|
4 |
+
use Psr\Http\Message\RequestInterface;
|
5 |
+
use Psr\Http\Message\ResponseInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Cookie jar that stores cookies as an array
|
9 |
+
*/
|
10 |
+
class CookieJar implements CookieJarInterface
|
11 |
+
{
|
12 |
+
/** @var SetCookie[] Loaded cookie data */
|
13 |
+
private $cookies = [];
|
14 |
+
|
15 |
+
/** @var bool */
|
16 |
+
private $strictMode;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @param bool $strictMode Set to true to throw exceptions when invalid
|
20 |
+
* cookies are added to the cookie jar.
|
21 |
+
* @param array $cookieArray Array of SetCookie objects or a hash of
|
22 |
+
* arrays that can be used with the SetCookie
|
23 |
+
* constructor
|
24 |
+
*/
|
25 |
+
public function __construct($strictMode = false, $cookieArray = [])
|
26 |
+
{
|
27 |
+
$this->strictMode = $strictMode;
|
28 |
+
|
29 |
+
foreach ($cookieArray as $cookie) {
|
30 |
+
if (!($cookie instanceof SetCookie)) {
|
31 |
+
$cookie = new SetCookie($cookie);
|
32 |
+
}
|
33 |
+
$this->setCookie($cookie);
|
34 |
+
}
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Create a new Cookie jar from an associative array and domain.
|
39 |
+
*
|
40 |
+
* @param array $cookies Cookies to create the jar from
|
41 |
+
* @param string $domain Domain to set the cookies to
|
42 |
+
*
|
43 |
+
* @return self
|
44 |
+
*/
|
45 |
+
public static function fromArray(array $cookies, $domain)
|
46 |
+
{
|
47 |
+
$cookieJar = new self();
|
48 |
+
foreach ($cookies as $name => $value) {
|
49 |
+
$cookieJar->setCookie(new SetCookie([
|
50 |
+
'Domain' => $domain,
|
51 |
+
'Name' => $name,
|
52 |
+
'Value' => $value,
|
53 |
+
'Discard' => true
|
54 |
+
]));
|
55 |
+
}
|
56 |
+
|
57 |
+
return $cookieJar;
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* @deprecated
|
62 |
+
*/
|
63 |
+
public static function getCookieValue($value)
|
64 |
+
{
|
65 |
+
return $value;
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Evaluate if this cookie should be persisted to storage
|
70 |
+
* that survives between requests.
|
71 |
+
*
|
72 |
+
* @param SetCookie $cookie Being evaluated.
|
73 |
+
* @param bool $allowSessionCookies If we should persist session cookies
|
74 |
+
* @return bool
|
75 |
+
*/
|
76 |
+
public static function shouldPersist(
|
77 |
+
SetCookie $cookie,
|
78 |
+
$allowSessionCookies = false
|
79 |
+
) {
|
80 |
+
if ($cookie->getExpires() || $allowSessionCookies) {
|
81 |
+
if (!$cookie->getDiscard()) {
|
82 |
+
return true;
|
83 |
+
}
|
84 |
+
}
|
85 |
+
|
86 |
+
return false;
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Finds and returns the cookie based on the name
|
91 |
+
*
|
92 |
+
* @param string $name cookie name to search for
|
93 |
+
* @return SetCookie|null cookie that was found or null if not found
|
94 |
+
*/
|
95 |
+
public function getCookieByName($name)
|
96 |
+
{
|
97 |
+
// don't allow a null name
|
98 |
+
if ($name === null) {
|
99 |
+
return null;
|
100 |
+
}
|
101 |
+
foreach ($this->cookies as $cookie) {
|
102 |
+
if ($cookie->getName() !== null && strcasecmp($cookie->getName(), $name) === 0) {
|
103 |
+
return $cookie;
|
104 |
+
}
|
105 |
+
}
|
106 |
+
}
|
107 |
+
|
108 |
+
public function toArray()
|
109 |
+
{
|
110 |
+
return array_map(function (SetCookie $cookie) {
|
111 |
+
return $cookie->toArray();
|
112 |
+
}, $this->getIterator()->getArrayCopy());
|
113 |
+
}
|
114 |
+
|
115 |
+
public function clear($domain = null, $path = null, $name = null)
|
116 |
+
{
|
117 |
+
if (!$domain) {
|
118 |
+
$this->cookies = [];
|
119 |
+
return;
|
120 |
+
} elseif (!$path) {
|
121 |
+
$this->cookies = array_filter(
|
122 |
+
$this->cookies,
|
123 |
+
function (SetCookie $cookie) use ($path, $domain) {
|
124 |
+
return !$cookie->matchesDomain($domain);
|
125 |
+
}
|
126 |
+
);
|
127 |
+
} elseif (!$name) {
|
128 |
+
$this->cookies = array_filter(
|
129 |
+
$this->cookies,
|
130 |
+
function (SetCookie $cookie) use ($path, $domain) {
|
131 |
+
return !($cookie->matchesPath($path) &&
|
132 |
+
$cookie->matchesDomain($domain));
|
133 |
+
}
|
134 |
+
);
|
135 |
+
} else {
|
136 |
+
$this->cookies = array_filter(
|
137 |
+
$this->cookies,
|
138 |
+
function (SetCookie $cookie) use ($path, $domain, $name) {
|
139 |
+
return !($cookie->getName() == $name &&
|
140 |
+
$cookie->matchesPath($path) &&
|
141 |
+
$cookie->matchesDomain($domain));
|
142 |
+
}
|
143 |
+
);
|
144 |
+
}
|
145 |
+
}
|
146 |
+
|
147 |
+
public function clearSessionCookies()
|
148 |
+
{
|
149 |
+
$this->cookies = array_filter(
|
150 |
+
$this->cookies,
|
151 |
+
function (SetCookie $cookie) {
|
152 |
+
return !$cookie->getDiscard() && $cookie->getExpires();
|
153 |
+
}
|
154 |
+
);
|
155 |
+
}
|
156 |
+
|
157 |
+
public function setCookie(SetCookie $cookie)
|
158 |
+
{
|
159 |
+
// If the name string is empty (but not 0), ignore the set-cookie
|
160 |
+
// string entirely.
|
161 |
+
$name = $cookie->getName();
|
162 |
+
if (!$name && $name !== '0') {
|
163 |
+
return false;
|
164 |
+
}
|
165 |
+
|
166 |
+
// Only allow cookies with set and valid domain, name, value
|
167 |
+
$result = $cookie->validate();
|
168 |
+
if ($result !== true) {
|
169 |
+
if ($this->strictMode) {
|
170 |
+
throw new \RuntimeException('Invalid cookie: ' . $result);
|
171 |
+
} else {
|
172 |
+
$this->removeCookieIfEmpty($cookie);
|
173 |
+
return false;
|
174 |
+
}
|
175 |
+
}
|
176 |
+
|
177 |
+
// Resolve conflicts with previously set cookies
|
178 |
+
foreach ($this->cookies as $i => $c) {
|
179 |
+
|
180 |
+
// Two cookies are identical, when their path, and domain are
|
181 |
+
// identical.
|
182 |
+
if ($c->getPath() != $cookie->getPath() ||
|
183 |
+
$c->getDomain() != $cookie->getDomain() ||
|
184 |
+
$c->getName() != $cookie->getName()
|
185 |
+
) {
|
186 |
+
continue;
|
187 |
+
}
|
188 |
+
|
189 |
+
// The previously set cookie is a discard cookie and this one is
|
190 |
+
// not so allow the new cookie to be set
|
191 |
+
if (!$cookie->getDiscard() && $c->getDiscard()) {
|
192 |
+
unset($this->cookies[$i]);
|
193 |
+
continue;
|
194 |
+
}
|
195 |
+
|
196 |
+
// If the new cookie's expiration is further into the future, then
|
197 |
+
// replace the old cookie
|
198 |
+
if ($cookie->getExpires() > $c->getExpires()) {
|
199 |
+
unset($this->cookies[$i]);
|
200 |
+
continue;
|
201 |
+
}
|
202 |
+
|
203 |
+
// If the value has changed, we better change it
|
204 |
+
if ($cookie->getValue() !== $c->getValue()) {
|
205 |
+
unset($this->cookies[$i]);
|
206 |
+
continue;
|
207 |
+
}
|
208 |
+
|
209 |
+
// The cookie exists, so no need to continue
|
210 |
+
return false;
|
211 |
+
}
|
212 |
+
|
213 |
+
$this->cookies[] = $cookie;
|
214 |
+
|
215 |
+
return true;
|
216 |
+
}
|
217 |
+
|
218 |
+
public function count()
|
219 |
+
{
|
220 |
+
return count($this->cookies);
|
221 |
+
}
|
222 |
+
|
223 |
+
public function getIterator()
|
224 |
+
{
|
225 |
+
return new \ArrayIterator(array_values($this->cookies));
|
226 |
+
}
|
227 |
+
|
228 |
+
public function extractCookies(
|
229 |
+
RequestInterface $request,
|
230 |
+
ResponseInterface $response
|
231 |
+
) {
|
232 |
+
if ($cookieHeader = $response->getHeader('Set-Cookie')) {
|
233 |
+
foreach ($cookieHeader as $cookie) {
|
234 |
+
$sc = SetCookie::fromString($cookie);
|
235 |
+
if (!$sc->getDomain()) {
|
236 |
+
$sc->setDomain($request->getUri()->getHost());
|
237 |
+
}
|
238 |
+
if (0 !== strpos($sc->getPath(), '/')) {
|
239 |
+
$sc->setPath($this->getCookiePathFromRequest($request));
|
240 |
+
}
|
241 |
+
$this->setCookie($sc);
|
242 |
+
}
|
243 |
+
}
|
244 |
+
}
|
245 |
+
|
246 |
+
/**
|
247 |
+
* Computes cookie path following RFC 6265 section 5.1.4
|
248 |
+
*
|
249 |
+
* @link https://tools.ietf.org/html/rfc6265#section-5.1.4
|
250 |
+
*
|
251 |
+
* @param RequestInterface $request
|
252 |
+
* @return string
|
253 |
+
*/
|
254 |
+
private function getCookiePathFromRequest(RequestInterface $request)
|
255 |
+
{
|
256 |
+
$uriPath = $request->getUri()->getPath();
|
257 |
+
if ('' === $uriPath) {
|
258 |
+
return '/';
|
259 |
+
}
|
260 |
+
if (0 !== strpos($uriPath, '/')) {
|
261 |
+
return '/';
|
262 |
+
}
|
263 |
+
if ('/' === $uriPath) {
|
264 |
+
return '/';
|
265 |
+
}
|
266 |
+
if (0 === $lastSlashPos = strrpos($uriPath, '/')) {
|
267 |
+
return '/';
|
268 |
+
}
|
269 |
+
|
270 |
+
return substr($uriPath, 0, $lastSlashPos);
|
271 |
+
}
|
272 |
+
|
273 |
+
public function withCookieHeader(RequestInterface $request)
|
274 |
+
{
|
275 |
+
$values = [];
|
276 |
+
$uri = $request->getUri();
|
277 |
+
$scheme = $uri->getScheme();
|
278 |
+
$host = $uri->getHost();
|
279 |
+
$path = $uri->getPath() ?: '/';
|
280 |
+
|
281 |
+
foreach ($this->cookies as $cookie) {
|
282 |
+
if ($cookie->matchesPath($path) &&
|
283 |
+
$cookie->matchesDomain($host) &&
|
284 |
+
!$cookie->isExpired() &&
|
285 |
+
(!$cookie->getSecure() || $scheme === 'https')
|
286 |
+
) {
|
287 |
+
$values[] = $cookie->getName() . '='
|
288 |
+
. $cookie->getValue();
|
289 |
+
}
|
290 |
+
}
|
291 |
+
|
292 |
+
return $values
|
293 |
+
? $request->withHeader('Cookie', implode('; ', $values))
|
294 |
+
: $request;
|
295 |
+
}
|
296 |
+
|
297 |
+
/**
|
298 |
+
* If a cookie already exists and the server asks to set it again with a
|
299 |
+
* null value, the cookie must be deleted.
|
300 |
+
*
|
301 |
+
* @param SetCookie $cookie
|
302 |
+
*/
|
303 |
+
private function removeCookieIfEmpty(SetCookie $cookie)
|
304 |
+
{
|
305 |
+
$cookieValue = $cookie->getValue();
|
306 |
+
if ($cookieValue === null || $cookieValue === '') {
|
307 |
+
$this->clear(
|
308 |
+
$cookie->getDomain(),
|
309 |
+
$cookie->getPath(),
|
310 |
+
$cookie->getName()
|
311 |
+
);
|
312 |
+
}
|
313 |
+
}
|
314 |
+
}
|
vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php
CHANGED
@@ -1,84 +1,84 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Cookie;
|
3 |
-
|
4 |
-
use Psr\Http\Message\RequestInterface;
|
5 |
-
use Psr\Http\Message\ResponseInterface;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* Stores HTTP cookies.
|
9 |
-
*
|
10 |
-
* It extracts cookies from HTTP requests, and returns them in HTTP responses.
|
11 |
-
* CookieJarInterface instances automatically expire contained cookies when
|
12 |
-
* necessary. Subclasses are also responsible for storing and retrieving
|
13 |
-
* cookies from a file, database, etc.
|
14 |
-
*
|
15 |
-
* @link http://docs.python.org/2/library/cookielib.html Inspiration
|
16 |
-
*/
|
17 |
-
interface CookieJarInterface extends \Countable, \IteratorAggregate
|
18 |
-
{
|
19 |
-
/**
|
20 |
-
* Create a request with added cookie headers.
|
21 |
-
*
|
22 |
-
* If no matching cookies are found in the cookie jar, then no Cookie
|
23 |
-
* header is added to the request and the same request is returned.
|
24 |
-
*
|
25 |
-
* @param RequestInterface $request Request object to modify.
|
26 |
-
*
|
27 |
-
* @return RequestInterface returns the modified request.
|
28 |
-
*/
|
29 |
-
public function withCookieHeader(RequestInterface $request);
|
30 |
-
|
31 |
-
/**
|
32 |
-
* Extract cookies from an HTTP response and store them in the CookieJar.
|
33 |
-
*
|
34 |
-
* @param RequestInterface $request Request that was sent
|
35 |
-
* @param ResponseInterface $response Response that was received
|
36 |
-
*/
|
37 |
-
public function extractCookies(
|
38 |
-
RequestInterface $request,
|
39 |
-
ResponseInterface $response
|
40 |
-
);
|
41 |
-
|
42 |
-
/**
|
43 |
-
* Sets a cookie in the cookie jar.
|
44 |
-
*
|
45 |
-
* @param SetCookie $cookie Cookie to set.
|
46 |
-
*
|
47 |
-
* @return bool Returns true on success or false on failure
|
48 |
-
*/
|
49 |
-
public function setCookie(SetCookie $cookie);
|
50 |
-
|
51 |
-
/**
|
52 |
-
* Remove cookies currently held in the cookie jar.
|
53 |
-
*
|
54 |
-
* Invoking this method without arguments will empty the whole cookie jar.
|
55 |
-
* If given a $domain argument only cookies belonging to that domain will
|
56 |
-
* be removed. If given a $domain and $path argument, cookies belonging to
|
57 |
-
* the specified path within that domain are removed. If given all three
|
58 |
-
* arguments, then the cookie with the specified name, path and domain is
|
59 |
-
* removed.
|
60 |
-
*
|
61 |
-
* @param string $domain Clears cookies matching a domain
|
62 |
-
* @param string $path Clears cookies matching a domain and path
|
63 |
-
* @param string $name Clears cookies matching a domain, path, and name
|
64 |
-
*
|
65 |
-
* @return CookieJarInterface
|
66 |
-
*/
|
67 |
-
public function clear($domain = null, $path = null, $name = null);
|
68 |
-
|
69 |
-
/**
|
70 |
-
* Discard all sessions cookies.
|
71 |
-
*
|
72 |
-
* Removes cookies that don't have an expire field or a have a discard
|
73 |
-
* field set to true. To be called when the user agent shuts down according
|
74 |
-
* to RFC 2965.
|
75 |
-
*/
|
76 |
-
public function clearSessionCookies();
|
77 |
-
|
78 |
-
/**
|
79 |
-
* Converts the cookie jar to an array.
|
80 |
-
*
|
81 |
-
* @return array
|
82 |
-
*/
|
83 |
-
public function toArray();
|
84 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Cookie;
|
3 |
+
|
4 |
+
use Psr\Http\Message\RequestInterface;
|
5 |
+
use Psr\Http\Message\ResponseInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Stores HTTP cookies.
|
9 |
+
*
|
10 |
+
* It extracts cookies from HTTP requests, and returns them in HTTP responses.
|
11 |
+
* CookieJarInterface instances automatically expire contained cookies when
|
12 |
+
* necessary. Subclasses are also responsible for storing and retrieving
|
13 |
+
* cookies from a file, database, etc.
|
14 |
+
*
|
15 |
+
* @link http://docs.python.org/2/library/cookielib.html Inspiration
|
16 |
+
*/
|
17 |
+
interface CookieJarInterface extends \Countable, \IteratorAggregate
|
18 |
+
{
|
19 |
+
/**
|
20 |
+
* Create a request with added cookie headers.
|
21 |
+
*
|
22 |
+
* If no matching cookies are found in the cookie jar, then no Cookie
|
23 |
+
* header is added to the request and the same request is returned.
|
24 |
+
*
|
25 |
+
* @param RequestInterface $request Request object to modify.
|
26 |
+
*
|
27 |
+
* @return RequestInterface returns the modified request.
|
28 |
+
*/
|
29 |
+
public function withCookieHeader(RequestInterface $request);
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Extract cookies from an HTTP response and store them in the CookieJar.
|
33 |
+
*
|
34 |
+
* @param RequestInterface $request Request that was sent
|
35 |
+
* @param ResponseInterface $response Response that was received
|
36 |
+
*/
|
37 |
+
public function extractCookies(
|
38 |
+
RequestInterface $request,
|
39 |
+
ResponseInterface $response
|
40 |
+
);
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Sets a cookie in the cookie jar.
|
44 |
+
*
|
45 |
+
* @param SetCookie $cookie Cookie to set.
|
46 |
+
*
|
47 |
+
* @return bool Returns true on success or false on failure
|
48 |
+
*/
|
49 |
+
public function setCookie(SetCookie $cookie);
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Remove cookies currently held in the cookie jar.
|
53 |
+
*
|
54 |
+
* Invoking this method without arguments will empty the whole cookie jar.
|
55 |
+
* If given a $domain argument only cookies belonging to that domain will
|
56 |
+
* be removed. If given a $domain and $path argument, cookies belonging to
|
57 |
+
* the specified path within that domain are removed. If given all three
|
58 |
+
* arguments, then the cookie with the specified name, path and domain is
|
59 |
+
* removed.
|
60 |
+
*
|
61 |
+
* @param string $domain Clears cookies matching a domain
|
62 |
+
* @param string $path Clears cookies matching a domain and path
|
63 |
+
* @param string $name Clears cookies matching a domain, path, and name
|
64 |
+
*
|
65 |
+
* @return CookieJarInterface
|
66 |
+
*/
|
67 |
+
public function clear($domain = null, $path = null, $name = null);
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Discard all sessions cookies.
|
71 |
+
*
|
72 |
+
* Removes cookies that don't have an expire field or a have a discard
|
73 |
+
* field set to true. To be called when the user agent shuts down according
|
74 |
+
* to RFC 2965.
|
75 |
+
*/
|
76 |
+
public function clearSessionCookies();
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Converts the cookie jar to an array.
|
80 |
+
*
|
81 |
+
* @return array
|
82 |
+
*/
|
83 |
+
public function toArray();
|
84 |
+
}
|
vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php
CHANGED
@@ -1,90 +1,90 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Cookie;
|
3 |
-
|
4 |
-
/**
|
5 |
-
* Persists non-session cookies using a JSON formatted file
|
6 |
-
*/
|
7 |
-
class FileCookieJar extends CookieJar
|
8 |
-
{
|
9 |
-
/** @var string filename */
|
10 |
-
private $filename;
|
11 |
-
|
12 |
-
/** @var bool Control whether to persist session cookies or not. */
|
13 |
-
private $storeSessionCookies;
|
14 |
-
|
15 |
-
/**
|
16 |
-
* Create a new FileCookieJar object
|
17 |
-
*
|
18 |
-
* @param string $cookieFile File to store the cookie data
|
19 |
-
* @param bool $storeSessionCookies Set to true to store session cookies
|
20 |
-
* in the cookie jar.
|
21 |
-
*
|
22 |
-
* @throws \RuntimeException if the file cannot be found or created
|
23 |
-
*/
|
24 |
-
public function __construct($cookieFile, $storeSessionCookies = false)
|
25 |
-
{
|
26 |
-
$this->filename = $cookieFile;
|
27 |
-
$this->storeSessionCookies = $storeSessionCookies;
|
28 |
-
|
29 |
-
if (file_exists($cookieFile)) {
|
30 |
-
$this->load($cookieFile);
|
31 |
-
}
|
32 |
-
}
|
33 |
-
|
34 |
-
/**
|
35 |
-
* Saves the file when shutting down
|
36 |
-
*/
|
37 |
-
public function __destruct()
|
38 |
-
{
|
39 |
-
$this->save($this->filename);
|
40 |
-
}
|
41 |
-
|
42 |
-
/**
|
43 |
-
* Saves the cookies to a file.
|
44 |
-
*
|
45 |
-
* @param string $filename File to save
|
46 |
-
* @throws \RuntimeException if the file cannot be found or created
|
47 |
-
*/
|
48 |
-
public function save($filename)
|
49 |
-
{
|
50 |
-
$json = [];
|
51 |
-
foreach ($this as $cookie) {
|
52 |
-
/** @var SetCookie $cookie */
|
53 |
-
if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
|
54 |
-
$json[] = $cookie->toArray();
|
55 |
-
}
|
56 |
-
}
|
57 |
-
|
58 |
-
$jsonStr = \GuzzleHttp\json_encode($json);
|
59 |
-
if (false === file_put_contents($filename, $jsonStr)) {
|
60 |
-
throw new \RuntimeException("Unable to save file {$filename}");
|
61 |
-
}
|
62 |
-
}
|
63 |
-
|
64 |
-
/**
|
65 |
-
* Load cookies from a JSON formatted file.
|
66 |
-
*
|
67 |
-
* Old cookies are kept unless overwritten by newly loaded ones.
|
68 |
-
*
|
69 |
-
* @param string $filename Cookie file to load.
|
70 |
-
* @throws \RuntimeException if the file cannot be loaded.
|
71 |
-
*/
|
72 |
-
public function load($filename)
|
73 |
-
{
|
74 |
-
$json = file_get_contents($filename);
|
75 |
-
if (false === $json) {
|
76 |
-
throw new \RuntimeException("Unable to load file {$filename}");
|
77 |
-
} elseif ($json === '') {
|
78 |
-
return;
|
79 |
-
}
|
80 |
-
|
81 |
-
$data = \GuzzleHttp\json_decode($json, true);
|
82 |
-
if (is_array($data)) {
|
83 |
-
foreach (json_decode($json, true) as $cookie) {
|
84 |
-
$this->setCookie(new SetCookie($cookie));
|
85 |
-
}
|
86 |
-
} elseif (strlen($data)) {
|
87 |
-
throw new \RuntimeException("Invalid cookie file: {$filename}");
|
88 |
-
}
|
89 |
-
}
|
90 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Cookie;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Persists non-session cookies using a JSON formatted file
|
6 |
+
*/
|
7 |
+
class FileCookieJar extends CookieJar
|
8 |
+
{
|
9 |
+
/** @var string filename */
|
10 |
+
private $filename;
|
11 |
+
|
12 |
+
/** @var bool Control whether to persist session cookies or not. */
|
13 |
+
private $storeSessionCookies;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Create a new FileCookieJar object
|
17 |
+
*
|
18 |
+
* @param string $cookieFile File to store the cookie data
|
19 |
+
* @param bool $storeSessionCookies Set to true to store session cookies
|
20 |
+
* in the cookie jar.
|
21 |
+
*
|
22 |
+
* @throws \RuntimeException if the file cannot be found or created
|
23 |
+
*/
|
24 |
+
public function __construct($cookieFile, $storeSessionCookies = false)
|
25 |
+
{
|
26 |
+
$this->filename = $cookieFile;
|
27 |
+
$this->storeSessionCookies = $storeSessionCookies;
|
28 |
+
|
29 |
+
if (file_exists($cookieFile)) {
|
30 |
+
$this->load($cookieFile);
|
31 |
+
}
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Saves the file when shutting down
|
36 |
+
*/
|
37 |
+
public function __destruct()
|
38 |
+
{
|
39 |
+
$this->save($this->filename);
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Saves the cookies to a file.
|
44 |
+
*
|
45 |
+
* @param string $filename File to save
|
46 |
+
* @throws \RuntimeException if the file cannot be found or created
|
47 |
+
*/
|
48 |
+
public function save($filename)
|
49 |
+
{
|
50 |
+
$json = [];
|
51 |
+
foreach ($this as $cookie) {
|
52 |
+
/** @var SetCookie $cookie */
|
53 |
+
if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
|
54 |
+
$json[] = $cookie->toArray();
|
55 |
+
}
|
56 |
+
}
|
57 |
+
|
58 |
+
$jsonStr = \GuzzleHttp\json_encode($json);
|
59 |
+
if (false === file_put_contents($filename, $jsonStr)) {
|
60 |
+
throw new \RuntimeException("Unable to save file {$filename}");
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Load cookies from a JSON formatted file.
|
66 |
+
*
|
67 |
+
* Old cookies are kept unless overwritten by newly loaded ones.
|
68 |
+
*
|
69 |
+
* @param string $filename Cookie file to load.
|
70 |
+
* @throws \RuntimeException if the file cannot be loaded.
|
71 |
+
*/
|
72 |
+
public function load($filename)
|
73 |
+
{
|
74 |
+
$json = file_get_contents($filename);
|
75 |
+
if (false === $json) {
|
76 |
+
throw new \RuntimeException("Unable to load file {$filename}");
|
77 |
+
} elseif ($json === '') {
|
78 |
+
return;
|
79 |
+
}
|
80 |
+
|
81 |
+
$data = \GuzzleHttp\json_decode($json, true);
|
82 |
+
if (is_array($data)) {
|
83 |
+
foreach (json_decode($json, true) as $cookie) {
|
84 |
+
$this->setCookie(new SetCookie($cookie));
|
85 |
+
}
|
86 |
+
} elseif (strlen($data)) {
|
87 |
+
throw new \RuntimeException("Invalid cookie file: {$filename}");
|
88 |
+
}
|
89 |
+
}
|
90 |
+
}
|
vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php
CHANGED
@@ -1,71 +1,71 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Cookie;
|
3 |
-
|
4 |
-
/**
|
5 |
-
* Persists cookies in the client session
|
6 |
-
*/
|
7 |
-
class SessionCookieJar extends CookieJar
|
8 |
-
{
|
9 |
-
/** @var string session key */
|
10 |
-
private $sessionKey;
|
11 |
-
|
12 |
-
/** @var bool Control whether to persist session cookies or not. */
|
13 |
-
private $storeSessionCookies;
|
14 |
-
|
15 |
-
/**
|
16 |
-
* Create a new SessionCookieJar object
|
17 |
-
*
|
18 |
-
* @param string $sessionKey Session key name to store the cookie
|
19 |
-
* data in session
|
20 |
-
* @param bool $storeSessionCookies Set to true to store session cookies
|
21 |
-
* in the cookie jar.
|
22 |
-
*/
|
23 |
-
public function __construct($sessionKey, $storeSessionCookies = false)
|
24 |
-
{
|
25 |
-
$this->sessionKey = $sessionKey;
|
26 |
-
$this->storeSessionCookies = $storeSessionCookies;
|
27 |
-
$this->load();
|
28 |
-
}
|
29 |
-
|
30 |
-
/**
|
31 |
-
* Saves cookies to session when shutting down
|
32 |
-
*/
|
33 |
-
public function __destruct()
|
34 |
-
{
|
35 |
-
$this->save();
|
36 |
-
}
|
37 |
-
|
38 |
-
/**
|
39 |
-
* Save cookies to the client session
|
40 |
-
*/
|
41 |
-
public function save()
|
42 |
-
{
|
43 |
-
$json = [];
|
44 |
-
foreach ($this as $cookie) {
|
45 |
-
/** @var SetCookie $cookie */
|
46 |
-
if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
|
47 |
-
$json[] = $cookie->toArray();
|
48 |
-
}
|
49 |
-
}
|
50 |
-
|
51 |
-
$_SESSION[$this->sessionKey] = json_encode($json);
|
52 |
-
}
|
53 |
-
|
54 |
-
/**
|
55 |
-
* Load the contents of the client session into the data array
|
56 |
-
*/
|
57 |
-
protected function load()
|
58 |
-
{
|
59 |
-
if (!isset($_SESSION[$this->sessionKey])) {
|
60 |
-
return;
|
61 |
-
}
|
62 |
-
$data = json_decode($_SESSION[$this->sessionKey], true);
|
63 |
-
if (is_array($data)) {
|
64 |
-
foreach ($data as $cookie) {
|
65 |
-
$this->setCookie(new SetCookie($cookie));
|
66 |
-
}
|
67 |
-
} elseif (strlen($data)) {
|
68 |
-
throw new \RuntimeException("Invalid cookie data");
|
69 |
-
}
|
70 |
-
}
|
71 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Cookie;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Persists cookies in the client session
|
6 |
+
*/
|
7 |
+
class SessionCookieJar extends CookieJar
|
8 |
+
{
|
9 |
+
/** @var string session key */
|
10 |
+
private $sessionKey;
|
11 |
+
|
12 |
+
/** @var bool Control whether to persist session cookies or not. */
|
13 |
+
private $storeSessionCookies;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Create a new SessionCookieJar object
|
17 |
+
*
|
18 |
+
* @param string $sessionKey Session key name to store the cookie
|
19 |
+
* data in session
|
20 |
+
* @param bool $storeSessionCookies Set to true to store session cookies
|
21 |
+
* in the cookie jar.
|
22 |
+
*/
|
23 |
+
public function __construct($sessionKey, $storeSessionCookies = false)
|
24 |
+
{
|
25 |
+
$this->sessionKey = $sessionKey;
|
26 |
+
$this->storeSessionCookies = $storeSessionCookies;
|
27 |
+
$this->load();
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Saves cookies to session when shutting down
|
32 |
+
*/
|
33 |
+
public function __destruct()
|
34 |
+
{
|
35 |
+
$this->save();
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Save cookies to the client session
|
40 |
+
*/
|
41 |
+
public function save()
|
42 |
+
{
|
43 |
+
$json = [];
|
44 |
+
foreach ($this as $cookie) {
|
45 |
+
/** @var SetCookie $cookie */
|
46 |
+
if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
|
47 |
+
$json[] = $cookie->toArray();
|
48 |
+
}
|
49 |
+
}
|
50 |
+
|
51 |
+
$_SESSION[$this->sessionKey] = json_encode($json);
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Load the contents of the client session into the data array
|
56 |
+
*/
|
57 |
+
protected function load()
|
58 |
+
{
|
59 |
+
if (!isset($_SESSION[$this->sessionKey])) {
|
60 |
+
return;
|
61 |
+
}
|
62 |
+
$data = json_decode($_SESSION[$this->sessionKey], true);
|
63 |
+
if (is_array($data)) {
|
64 |
+
foreach ($data as $cookie) {
|
65 |
+
$this->setCookie(new SetCookie($cookie));
|
66 |
+
}
|
67 |
+
} elseif (strlen($data)) {
|
68 |
+
throw new \RuntimeException("Invalid cookie data");
|
69 |
+
}
|
70 |
+
}
|
71 |
+
}
|
vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php
CHANGED
@@ -1,403 +1,403 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Cookie;
|
3 |
-
|
4 |
-
/**
|
5 |
-
* Set-Cookie object
|
6 |
-
*/
|
7 |
-
class SetCookie
|
8 |
-
{
|
9 |
-
/** @var array */
|
10 |
-
private static $defaults = [
|
11 |
-
'Name' => null,
|
12 |
-
'Value' => null,
|
13 |
-
'Domain' => null,
|
14 |
-
'Path' => '/',
|
15 |
-
'Max-Age' => null,
|
16 |
-
'Expires' => null,
|
17 |
-
'Secure' => false,
|
18 |
-
'Discard' => false,
|
19 |
-
'HttpOnly' => false
|
20 |
-
];
|
21 |
-
|
22 |
-
/** @var array Cookie data */
|
23 |
-
private $data;
|
24 |
-
|
25 |
-
/**
|
26 |
-
* Create a new SetCookie object from a string
|
27 |
-
*
|
28 |
-
* @param string $cookie Set-Cookie header string
|
29 |
-
*
|
30 |
-
* @return self
|
31 |
-
*/
|
32 |
-
public static function fromString($cookie)
|
33 |
-
{
|
34 |
-
// Create the default return array
|
35 |
-
$data = self::$defaults;
|
36 |
-
// Explode the cookie string using a series of semicolons
|
37 |
-
$pieces = array_filter(array_map('trim', explode(';', $cookie)));
|
38 |
-
// The name of the cookie (first kvp) must exist and include an equal sign.
|
39 |
-
if (empty($pieces[0]) || !strpos($pieces[0], '=')) {
|
40 |
-
return new self($data);
|
41 |
-
}
|
42 |
-
|
43 |
-
// Add the cookie pieces into the parsed data array
|
44 |
-
foreach ($pieces as $part) {
|
45 |
-
$cookieParts = explode('=', $part, 2);
|
46 |
-
$key = trim($cookieParts[0]);
|
47 |
-
$value = isset($cookieParts[1])
|
48 |
-
? trim($cookieParts[1], " \n\r\t\0\x0B")
|
49 |
-
: true;
|
50 |
-
|
51 |
-
// Only check for non-cookies when cookies have been found
|
52 |
-
if (empty($data['Name'])) {
|
53 |
-
$data['Name'] = $key;
|
54 |
-
$data['Value'] = $value;
|
55 |
-
} else {
|
56 |
-
foreach (array_keys(self::$defaults) as $search) {
|
57 |
-
if (!strcasecmp($search, $key)) {
|
58 |
-
$data[$search] = $value;
|
59 |
-
continue 2;
|
60 |
-
}
|
61 |
-
}
|
62 |
-
$data[$key] = $value;
|
63 |
-
}
|
64 |
-
}
|
65 |
-
|
66 |
-
return new self($data);
|
67 |
-
}
|
68 |
-
|
69 |
-
/**
|
70 |
-
* @param array $data Array of cookie data provided by a Cookie parser
|
71 |
-
*/
|
72 |
-
public function __construct(array $data = [])
|
73 |
-
{
|
74 |
-
$this->data = array_replace(self::$defaults, $data);
|
75 |
-
// Extract the Expires value and turn it into a UNIX timestamp if needed
|
76 |
-
if (!$this->getExpires() && $this->getMaxAge()) {
|
77 |
-
// Calculate the Expires date
|
78 |
-
$this->setExpires(time() + $this->getMaxAge());
|
79 |
-
} elseif ($this->getExpires() && !is_numeric($this->getExpires())) {
|
80 |
-
$this->setExpires($this->getExpires());
|
81 |
-
}
|
82 |
-
}
|
83 |
-
|
84 |
-
public function __toString()
|
85 |
-
{
|
86 |
-
$str = $this->data['Name'] . '=' . $this->data['Value'] . '; ';
|
87 |
-
foreach ($this->data as $k => $v) {
|
88 |
-
if ($k !== 'Name' && $k !== 'Value' && $v !== null && $v !== false) {
|
89 |
-
if ($k === 'Expires') {
|
90 |
-
$str .= 'Expires=' . gmdate('D, d M Y H:i:s \G\M\T', $v) . '; ';
|
91 |
-
} else {
|
92 |
-
$str .= ($v === true ? $k : "{$k}={$v}") . '; ';
|
93 |
-
}
|
94 |
-
}
|
95 |
-
}
|
96 |
-
|
97 |
-
return rtrim($str, '; ');
|
98 |
-
}
|
99 |
-
|
100 |
-
public function toArray()
|
101 |
-
{
|
102 |
-
return $this->data;
|
103 |
-
}
|
104 |
-
|
105 |
-
/**
|
106 |
-
* Get the cookie name
|
107 |
-
*
|
108 |
-
* @return string
|
109 |
-
*/
|
110 |
-
public function getName()
|
111 |
-
{
|
112 |
-
return $this->data['Name'];
|
113 |
-
}
|
114 |
-
|
115 |
-
/**
|
116 |
-
* Set the cookie name
|
117 |
-
*
|
118 |
-
* @param string $name Cookie name
|
119 |
-
*/
|
120 |
-
public function setName($name)
|
121 |
-
{
|
122 |
-
$this->data['Name'] = $name;
|
123 |
-
}
|
124 |
-
|
125 |
-
/**
|
126 |
-
* Get the cookie value
|
127 |
-
*
|
128 |
-
* @return string
|
129 |
-
*/
|
130 |
-
public function getValue()
|
131 |
-
{
|
132 |
-
return $this->data['Value'];
|
133 |
-
}
|
134 |
-
|
135 |
-
/**
|
136 |
-
* Set the cookie value
|
137 |
-
*
|
138 |
-
* @param string $value Cookie value
|
139 |
-
*/
|
140 |
-
public function setValue($value)
|
141 |
-
{
|
142 |
-
$this->data['Value'] = $value;
|
143 |
-
}
|
144 |
-
|
145 |
-
/**
|
146 |
-
* Get the domain
|
147 |
-
*
|
148 |
-
* @return string|null
|
149 |
-
*/
|
150 |
-
public function getDomain()
|
151 |
-
{
|
152 |
-
return $this->data['Domain'];
|
153 |
-
}
|
154 |
-
|
155 |
-
/**
|
156 |
-
* Set the domain of the cookie
|
157 |
-
*
|
158 |
-
* @param string $domain
|
159 |
-
*/
|
160 |
-
public function setDomain($domain)
|
161 |
-
{
|
162 |
-
$this->data['Domain'] = $domain;
|
163 |
-
}
|
164 |
-
|
165 |
-
/**
|
166 |
-
* Get the path
|
167 |
-
*
|
168 |
-
* @return string
|
169 |
-
*/
|
170 |
-
public function getPath()
|
171 |
-
{
|
172 |
-
return $this->data['Path'];
|
173 |
-
}
|
174 |
-
|
175 |
-
/**
|
176 |
-
* Set the path of the cookie
|
177 |
-
*
|
178 |
-
* @param string $path Path of the cookie
|
179 |
-
*/
|
180 |
-
public function setPath($path)
|
181 |
-
{
|
182 |
-
$this->data['Path'] = $path;
|
183 |
-
}
|
184 |
-
|
185 |
-
/**
|
186 |
-
* Maximum lifetime of the cookie in seconds
|
187 |
-
*
|
188 |
-
* @return int|null
|
189 |
-
*/
|
190 |
-
public function getMaxAge()
|
191 |
-
{
|
192 |
-
return $this->data['Max-Age'];
|
193 |
-
}
|
194 |
-
|
195 |
-
/**
|
196 |
-
* Set the max-age of the cookie
|
197 |
-
*
|
198 |
-
* @param int $maxAge Max age of the cookie in seconds
|
199 |
-
*/
|
200 |
-
public function setMaxAge($maxAge)
|
201 |
-
{
|
202 |
-
$this->data['Max-Age'] = $maxAge;
|
203 |
-
}
|
204 |
-
|
205 |
-
/**
|
206 |
-
* The UNIX timestamp when the cookie Expires
|
207 |
-
*
|
208 |
-
* @return mixed
|
209 |
-
*/
|
210 |
-
public function getExpires()
|
211 |
-
{
|
212 |
-
return $this->data['Expires'];
|
213 |
-
}
|
214 |
-
|
215 |
-
/**
|
216 |
-
* Set the unix timestamp for which the cookie will expire
|
217 |
-
*
|
218 |
-
* @param int $timestamp Unix timestamp
|
219 |
-
*/
|
220 |
-
public function setExpires($timestamp)
|
221 |
-
{
|
222 |
-
$this->data['Expires'] = is_numeric($timestamp)
|
223 |
-
? (int) $timestamp
|
224 |
-
: strtotime($timestamp);
|
225 |
-
}
|
226 |
-
|
227 |
-
/**
|
228 |
-
* Get whether or not this is a secure cookie
|
229 |
-
*
|
230 |
-
* @return null|bool
|
231 |
-
*/
|
232 |
-
public function getSecure()
|
233 |
-
{
|
234 |
-
return $this->data['Secure'];
|
235 |
-
}
|
236 |
-
|
237 |
-
/**
|
238 |
-
* Set whether or not the cookie is secure
|
239 |
-
*
|
240 |
-
* @param bool $secure Set to true or false if secure
|
241 |
-
*/
|
242 |
-
public function setSecure($secure)
|
243 |
-
{
|
244 |
-
$this->data['Secure'] = $secure;
|
245 |
-
}
|
246 |
-
|
247 |
-
/**
|
248 |
-
* Get whether or not this is a session cookie
|
249 |
-
*
|
250 |
-
* @return null|bool
|
251 |
-
*/
|
252 |
-
public function getDiscard()
|
253 |
-
{
|
254 |
-
return $this->data['Discard'];
|
255 |
-
}
|
256 |
-
|
257 |
-
/**
|
258 |
-
* Set whether or not this is a session cookie
|
259 |
-
*
|
260 |
-
* @param bool $discard Set to true or false if this is a session cookie
|
261 |
-
*/
|
262 |
-
public function setDiscard($discard)
|
263 |
-
{
|
264 |
-
$this->data['Discard'] = $discard;
|
265 |
-
}
|
266 |
-
|
267 |
-
/**
|
268 |
-
* Get whether or not this is an HTTP only cookie
|
269 |
-
*
|
270 |
-
* @return bool
|
271 |
-
*/
|
272 |
-
public function getHttpOnly()
|
273 |
-
{
|
274 |
-
return $this->data['HttpOnly'];
|
275 |
-
}
|
276 |
-
|
277 |
-
/**
|
278 |
-
* Set whether or not this is an HTTP only cookie
|
279 |
-
*
|
280 |
-
* @param bool $httpOnly Set to true or false if this is HTTP only
|
281 |
-
*/
|
282 |
-
public function setHttpOnly($httpOnly)
|
283 |
-
{
|
284 |
-
$this->data['HttpOnly'] = $httpOnly;
|
285 |
-
}
|
286 |
-
|
287 |
-
/**
|
288 |
-
* Check if the cookie matches a path value.
|
289 |
-
*
|
290 |
-
* A request-path path-matches a given cookie-path if at least one of
|
291 |
-
* the following conditions holds:
|
292 |
-
*
|
293 |
-
* - The cookie-path and the request-path are identical.
|
294 |
-
* - The cookie-path is a prefix of the request-path, and the last
|
295 |
-
* character of the cookie-path is %x2F ("/").
|
296 |
-
* - The cookie-path is a prefix of the request-path, and the first
|
297 |
-
* character of the request-path that is not included in the cookie-
|
298 |
-
* path is a %x2F ("/") character.
|
299 |
-
*
|
300 |
-
* @param string $requestPath Path to check against
|
301 |
-
*
|
302 |
-
* @return bool
|
303 |
-
*/
|
304 |
-
public function matchesPath($requestPath)
|
305 |
-
{
|
306 |
-
$cookiePath = $this->getPath();
|
307 |
-
|
308 |
-
// Match on exact matches or when path is the default empty "/"
|
309 |
-
if ($cookiePath === '/' || $cookiePath == $requestPath) {
|
310 |
-
return true;
|
311 |
-
}
|
312 |
-
|
313 |
-
// Ensure that the cookie-path is a prefix of the request path.
|
314 |
-
if (0 !== strpos($requestPath, $cookiePath)) {
|
315 |
-
return false;
|
316 |
-
}
|
317 |
-
|
318 |
-
// Match if the last character of the cookie-path is "/"
|
319 |
-
if (substr($cookiePath, -1, 1) === '/') {
|
320 |
-
return true;
|
321 |
-
}
|
322 |
-
|
323 |
-
// Match if the first character not included in cookie path is "/"
|
324 |
-
return substr($requestPath, strlen($cookiePath), 1) === '/';
|
325 |
-
}
|
326 |
-
|
327 |
-
/**
|
328 |
-
* Check if the cookie matches a domain value
|
329 |
-
*
|
330 |
-
* @param string $domain Domain to check against
|
331 |
-
*
|
332 |
-
* @return bool
|
333 |
-
*/
|
334 |
-
public function matchesDomain($domain)
|
335 |
-
{
|
336 |
-
// Remove the leading '.' as per spec in RFC 6265.
|
337 |
-
// http://tools.ietf.org/html/rfc6265#section-5.2.3
|
338 |
-
$cookieDomain = ltrim($this->getDomain(), '.');
|
339 |
-
|
340 |
-
// Domain not set or exact match.
|
341 |
-
if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) {
|
342 |
-
return true;
|
343 |
-
}
|
344 |
-
|
345 |
-
// Matching the subdomain according to RFC 6265.
|
346 |
-
// http://tools.ietf.org/html/rfc6265#section-5.1.3
|
347 |
-
if (filter_var($domain, FILTER_VALIDATE_IP)) {
|
348 |
-
return false;
|
349 |
-
}
|
350 |
-
|
351 |
-
return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/', $domain);
|
352 |
-
}
|
353 |
-
|
354 |
-
/**
|
355 |
-
* Check if the cookie is expired
|
356 |
-
*
|
357 |
-
* @return bool
|
358 |
-
*/
|
359 |
-
public function isExpired()
|
360 |
-
{
|
361 |
-
return $this->getExpires() !== null && time() > $this->getExpires();
|
362 |
-
}
|
363 |
-
|
364 |
-
/**
|
365 |
-
* Check if the cookie is valid according to RFC 6265
|
366 |
-
*
|
367 |
-
* @return bool|string Returns true if valid or an error message if invalid
|
368 |
-
*/
|
369 |
-
public function validate()
|
370 |
-
{
|
371 |
-
// Names must not be empty, but can be 0
|
372 |
-
$name = $this->getName();
|
373 |
-
if (empty($name) && !is_numeric($name)) {
|
374 |
-
return 'The cookie name must not be empty';
|
375 |
-
}
|
376 |
-
|
377 |
-
// Check if any of the invalid characters are present in the cookie name
|
378 |
-
if (preg_match(
|
379 |
-
'/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5c\x7b\x7d\x7f]/',
|
380 |
-
$name
|
381 |
-
)) {
|
382 |
-
return 'Cookie name must not contain invalid characters: ASCII '
|
383 |
-
. 'Control characters (0-31;127), space, tab and the '
|
384 |
-
. 'following characters: ()<>@,;:\"/?={}';
|
385 |
-
}
|
386 |
-
|
387 |
-
// Value must not be empty, but can be 0
|
388 |
-
$value = $this->getValue();
|
389 |
-
if (empty($value) && !is_numeric($value)) {
|
390 |
-
return 'The cookie value must not be empty';
|
391 |
-
}
|
392 |
-
|
393 |
-
// Domains must not be empty, but can be 0
|
394 |
-
// A "0" is not a valid internet domain, but may be used as server name
|
395 |
-
// in a private network.
|
396 |
-
$domain = $this->getDomain();
|
397 |
-
if (empty($domain) && !is_numeric($domain)) {
|
398 |
-
return 'The cookie domain must not be empty';
|
399 |
-
}
|
400 |
-
|
401 |
-
return true;
|
402 |
-
}
|
403 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Cookie;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Set-Cookie object
|
6 |
+
*/
|
7 |
+
class SetCookie
|
8 |
+
{
|
9 |
+
/** @var array */
|
10 |
+
private static $defaults = [
|
11 |
+
'Name' => null,
|
12 |
+
'Value' => null,
|
13 |
+
'Domain' => null,
|
14 |
+
'Path' => '/',
|
15 |
+
'Max-Age' => null,
|
16 |
+
'Expires' => null,
|
17 |
+
'Secure' => false,
|
18 |
+
'Discard' => false,
|
19 |
+
'HttpOnly' => false
|
20 |
+
];
|
21 |
+
|
22 |
+
/** @var array Cookie data */
|
23 |
+
private $data;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Create a new SetCookie object from a string
|
27 |
+
*
|
28 |
+
* @param string $cookie Set-Cookie header string
|
29 |
+
*
|
30 |
+
* @return self
|
31 |
+
*/
|
32 |
+
public static function fromString($cookie)
|
33 |
+
{
|
34 |
+
// Create the default return array
|
35 |
+
$data = self::$defaults;
|
36 |
+
// Explode the cookie string using a series of semicolons
|
37 |
+
$pieces = array_filter(array_map('trim', explode(';', $cookie)));
|
38 |
+
// The name of the cookie (first kvp) must exist and include an equal sign.
|
39 |
+
if (empty($pieces[0]) || !strpos($pieces[0], '=')) {
|
40 |
+
return new self($data);
|
41 |
+
}
|
42 |
+
|
43 |
+
// Add the cookie pieces into the parsed data array
|
44 |
+
foreach ($pieces as $part) {
|
45 |
+
$cookieParts = explode('=', $part, 2);
|
46 |
+
$key = trim($cookieParts[0]);
|
47 |
+
$value = isset($cookieParts[1])
|
48 |
+
? trim($cookieParts[1], " \n\r\t\0\x0B")
|
49 |
+
: true;
|
50 |
+
|
51 |
+
// Only check for non-cookies when cookies have been found
|
52 |
+
if (empty($data['Name'])) {
|
53 |
+
$data['Name'] = $key;
|
54 |
+
$data['Value'] = $value;
|
55 |
+
} else {
|
56 |
+
foreach (array_keys(self::$defaults) as $search) {
|
57 |
+
if (!strcasecmp($search, $key)) {
|
58 |
+
$data[$search] = $value;
|
59 |
+
continue 2;
|
60 |
+
}
|
61 |
+
}
|
62 |
+
$data[$key] = $value;
|
63 |
+
}
|
64 |
+
}
|
65 |
+
|
66 |
+
return new self($data);
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* @param array $data Array of cookie data provided by a Cookie parser
|
71 |
+
*/
|
72 |
+
public function __construct(array $data = [])
|
73 |
+
{
|
74 |
+
$this->data = array_replace(self::$defaults, $data);
|
75 |
+
// Extract the Expires value and turn it into a UNIX timestamp if needed
|
76 |
+
if (!$this->getExpires() && $this->getMaxAge()) {
|
77 |
+
// Calculate the Expires date
|
78 |
+
$this->setExpires(time() + $this->getMaxAge());
|
79 |
+
} elseif ($this->getExpires() && !is_numeric($this->getExpires())) {
|
80 |
+
$this->setExpires($this->getExpires());
|
81 |
+
}
|
82 |
+
}
|
83 |
+
|
84 |
+
public function __toString()
|
85 |
+
{
|
86 |
+
$str = $this->data['Name'] . '=' . $this->data['Value'] . '; ';
|
87 |
+
foreach ($this->data as $k => $v) {
|
88 |
+
if ($k !== 'Name' && $k !== 'Value' && $v !== null && $v !== false) {
|
89 |
+
if ($k === 'Expires') {
|
90 |
+
$str .= 'Expires=' . gmdate('D, d M Y H:i:s \G\M\T', $v) . '; ';
|
91 |
+
} else {
|
92 |
+
$str .= ($v === true ? $k : "{$k}={$v}") . '; ';
|
93 |
+
}
|
94 |
+
}
|
95 |
+
}
|
96 |
+
|
97 |
+
return rtrim($str, '; ');
|
98 |
+
}
|
99 |
+
|
100 |
+
public function toArray()
|
101 |
+
{
|
102 |
+
return $this->data;
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Get the cookie name
|
107 |
+
*
|
108 |
+
* @return string
|
109 |
+
*/
|
110 |
+
public function getName()
|
111 |
+
{
|
112 |
+
return $this->data['Name'];
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Set the cookie name
|
117 |
+
*
|
118 |
+
* @param string $name Cookie name
|
119 |
+
*/
|
120 |
+
public function setName($name)
|
121 |
+
{
|
122 |
+
$this->data['Name'] = $name;
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Get the cookie value
|
127 |
+
*
|
128 |
+
* @return string
|
129 |
+
*/
|
130 |
+
public function getValue()
|
131 |
+
{
|
132 |
+
return $this->data['Value'];
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Set the cookie value
|
137 |
+
*
|
138 |
+
* @param string $value Cookie value
|
139 |
+
*/
|
140 |
+
public function setValue($value)
|
141 |
+
{
|
142 |
+
$this->data['Value'] = $value;
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Get the domain
|
147 |
+
*
|
148 |
+
* @return string|null
|
149 |
+
*/
|
150 |
+
public function getDomain()
|
151 |
+
{
|
152 |
+
return $this->data['Domain'];
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* Set the domain of the cookie
|
157 |
+
*
|
158 |
+
* @param string $domain
|
159 |
+
*/
|
160 |
+
public function setDomain($domain)
|
161 |
+
{
|
162 |
+
$this->data['Domain'] = $domain;
|
163 |
+
}
|
164 |
+
|
165 |
+
/**
|
166 |
+
* Get the path
|
167 |
+
*
|
168 |
+
* @return string
|
169 |
+
*/
|
170 |
+
public function getPath()
|
171 |
+
{
|
172 |
+
return $this->data['Path'];
|
173 |
+
}
|
174 |
+
|
175 |
+
/**
|
176 |
+
* Set the path of the cookie
|
177 |
+
*
|
178 |
+
* @param string $path Path of the cookie
|
179 |
+
*/
|
180 |
+
public function setPath($path)
|
181 |
+
{
|
182 |
+
$this->data['Path'] = $path;
|
183 |
+
}
|
184 |
+
|
185 |
+
/**
|
186 |
+
* Maximum lifetime of the cookie in seconds
|
187 |
+
*
|
188 |
+
* @return int|null
|
189 |
+
*/
|
190 |
+
public function getMaxAge()
|
191 |
+
{
|
192 |
+
return $this->data['Max-Age'];
|
193 |
+
}
|
194 |
+
|
195 |
+
/**
|
196 |
+
* Set the max-age of the cookie
|
197 |
+
*
|
198 |
+
* @param int $maxAge Max age of the cookie in seconds
|
199 |
+
*/
|
200 |
+
public function setMaxAge($maxAge)
|
201 |
+
{
|
202 |
+
$this->data['Max-Age'] = $maxAge;
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* The UNIX timestamp when the cookie Expires
|
207 |
+
*
|
208 |
+
* @return mixed
|
209 |
+
*/
|
210 |
+
public function getExpires()
|
211 |
+
{
|
212 |
+
return $this->data['Expires'];
|
213 |
+
}
|
214 |
+
|
215 |
+
/**
|
216 |
+
* Set the unix timestamp for which the cookie will expire
|
217 |
+
*
|
218 |
+
* @param int $timestamp Unix timestamp
|
219 |
+
*/
|
220 |
+
public function setExpires($timestamp)
|
221 |
+
{
|
222 |
+
$this->data['Expires'] = is_numeric($timestamp)
|
223 |
+
? (int) $timestamp
|
224 |
+
: strtotime($timestamp);
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* Get whether or not this is a secure cookie
|
229 |
+
*
|
230 |
+
* @return null|bool
|
231 |
+
*/
|
232 |
+
public function getSecure()
|
233 |
+
{
|
234 |
+
return $this->data['Secure'];
|
235 |
+
}
|
236 |
+
|
237 |
+
/**
|
238 |
+
* Set whether or not the cookie is secure
|
239 |
+
*
|
240 |
+
* @param bool $secure Set to true or false if secure
|
241 |
+
*/
|
242 |
+
public function setSecure($secure)
|
243 |
+
{
|
244 |
+
$this->data['Secure'] = $secure;
|
245 |
+
}
|
246 |
+
|
247 |
+
/**
|
248 |
+
* Get whether or not this is a session cookie
|
249 |
+
*
|
250 |
+
* @return null|bool
|
251 |
+
*/
|
252 |
+
public function getDiscard()
|
253 |
+
{
|
254 |
+
return $this->data['Discard'];
|
255 |
+
}
|
256 |
+
|
257 |
+
/**
|
258 |
+
* Set whether or not this is a session cookie
|
259 |
+
*
|
260 |
+
* @param bool $discard Set to true or false if this is a session cookie
|
261 |
+
*/
|
262 |
+
public function setDiscard($discard)
|
263 |
+
{
|
264 |
+
$this->data['Discard'] = $discard;
|
265 |
+
}
|
266 |
+
|
267 |
+
/**
|
268 |
+
* Get whether or not this is an HTTP only cookie
|
269 |
+
*
|
270 |
+
* @return bool
|
271 |
+
*/
|
272 |
+
public function getHttpOnly()
|
273 |
+
{
|
274 |
+
return $this->data['HttpOnly'];
|
275 |
+
}
|
276 |
+
|
277 |
+
/**
|
278 |
+
* Set whether or not this is an HTTP only cookie
|
279 |
+
*
|
280 |
+
* @param bool $httpOnly Set to true or false if this is HTTP only
|
281 |
+
*/
|
282 |
+
public function setHttpOnly($httpOnly)
|
283 |
+
{
|
284 |
+
$this->data['HttpOnly'] = $httpOnly;
|
285 |
+
}
|
286 |
+
|
287 |
+
/**
|
288 |
+
* Check if the cookie matches a path value.
|
289 |
+
*
|
290 |
+
* A request-path path-matches a given cookie-path if at least one of
|
291 |
+
* the following conditions holds:
|
292 |
+
*
|
293 |
+
* - The cookie-path and the request-path are identical.
|
294 |
+
* - The cookie-path is a prefix of the request-path, and the last
|
295 |
+
* character of the cookie-path is %x2F ("/").
|
296 |
+
* - The cookie-path is a prefix of the request-path, and the first
|
297 |
+
* character of the request-path that is not included in the cookie-
|
298 |
+
* path is a %x2F ("/") character.
|
299 |
+
*
|
300 |
+
* @param string $requestPath Path to check against
|
301 |
+
*
|
302 |
+
* @return bool
|
303 |
+
*/
|
304 |
+
public function matchesPath($requestPath)
|
305 |
+
{
|
306 |
+
$cookiePath = $this->getPath();
|
307 |
+
|
308 |
+
// Match on exact matches or when path is the default empty "/"
|
309 |
+
if ($cookiePath === '/' || $cookiePath == $requestPath) {
|
310 |
+
return true;
|
311 |
+
}
|
312 |
+
|
313 |
+
// Ensure that the cookie-path is a prefix of the request path.
|
314 |
+
if (0 !== strpos($requestPath, $cookiePath)) {
|
315 |
+
return false;
|
316 |
+
}
|
317 |
+
|
318 |
+
// Match if the last character of the cookie-path is "/"
|
319 |
+
if (substr($cookiePath, -1, 1) === '/') {
|
320 |
+
return true;
|
321 |
+
}
|
322 |
+
|
323 |
+
// Match if the first character not included in cookie path is "/"
|
324 |
+
return substr($requestPath, strlen($cookiePath), 1) === '/';
|
325 |
+
}
|
326 |
+
|
327 |
+
/**
|
328 |
+
* Check if the cookie matches a domain value
|
329 |
+
*
|
330 |
+
* @param string $domain Domain to check against
|
331 |
+
*
|
332 |
+
* @return bool
|
333 |
+
*/
|
334 |
+
public function matchesDomain($domain)
|
335 |
+
{
|
336 |
+
// Remove the leading '.' as per spec in RFC 6265.
|
337 |
+
// http://tools.ietf.org/html/rfc6265#section-5.2.3
|
338 |
+
$cookieDomain = ltrim($this->getDomain(), '.');
|
339 |
+
|
340 |
+
// Domain not set or exact match.
|
341 |
+
if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) {
|
342 |
+
return true;
|
343 |
+
}
|
344 |
+
|
345 |
+
// Matching the subdomain according to RFC 6265.
|
346 |
+
// http://tools.ietf.org/html/rfc6265#section-5.1.3
|
347 |
+
if (filter_var($domain, FILTER_VALIDATE_IP)) {
|
348 |
+
return false;
|
349 |
+
}
|
350 |
+
|
351 |
+
return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/', $domain);
|
352 |
+
}
|
353 |
+
|
354 |
+
/**
|
355 |
+
* Check if the cookie is expired
|
356 |
+
*
|
357 |
+
* @return bool
|
358 |
+
*/
|
359 |
+
public function isExpired()
|
360 |
+
{
|
361 |
+
return $this->getExpires() !== null && time() > $this->getExpires();
|
362 |
+
}
|
363 |
+
|
364 |
+
/**
|
365 |
+
* Check if the cookie is valid according to RFC 6265
|
366 |
+
*
|
367 |
+
* @return bool|string Returns true if valid or an error message if invalid
|
368 |
+
*/
|
369 |
+
public function validate()
|
370 |
+
{
|
371 |
+
// Names must not be empty, but can be 0
|
372 |
+
$name = $this->getName();
|
373 |
+
if (empty($name) && !is_numeric($name)) {
|
374 |
+
return 'The cookie name must not be empty';
|
375 |
+
}
|
376 |
+
|
377 |
+
// Check if any of the invalid characters are present in the cookie name
|
378 |
+
if (preg_match(
|
379 |
+
'/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5c\x7b\x7d\x7f]/',
|
380 |
+
$name
|
381 |
+
)) {
|
382 |
+
return 'Cookie name must not contain invalid characters: ASCII '
|
383 |
+
. 'Control characters (0-31;127), space, tab and the '
|
384 |
+
. 'following characters: ()<>@,;:\"/?={}';
|
385 |
+
}
|
386 |
+
|
387 |
+
// Value must not be empty, but can be 0
|
388 |
+
$value = $this->getValue();
|
389 |
+
if (empty($value) && !is_numeric($value)) {
|
390 |
+
return 'The cookie value must not be empty';
|
391 |
+
}
|
392 |
+
|
393 |
+
// Domains must not be empty, but can be 0
|
394 |
+
// A "0" is not a valid internet domain, but may be used as server name
|
395 |
+
// in a private network.
|
396 |
+
$domain = $this->getDomain();
|
397 |
+
if (empty($domain) && !is_numeric($domain)) {
|
398 |
+
return 'The cookie domain must not be empty';
|
399 |
+
}
|
400 |
+
|
401 |
+
return true;
|
402 |
+
}
|
403 |
+
}
|
vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php
CHANGED
@@ -1,27 +1,27 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Exception;
|
3 |
-
|
4 |
-
use Psr\Http\Message\RequestInterface;
|
5 |
-
use Psr\Http\Message\ResponseInterface;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* Exception when an HTTP error occurs (4xx or 5xx error)
|
9 |
-
*/
|
10 |
-
class BadResponseException extends RequestException
|
11 |
-
{
|
12 |
-
public function __construct(
|
13 |
-
$message,
|
14 |
-
RequestInterface $request,
|
15 |
-
ResponseInterface $response = null,
|
16 |
-
\Exception $previous = null,
|
17 |
-
array $handlerContext = []
|
18 |
-
) {
|
19 |
-
if (null === $response) {
|
20 |
-
@trigger_error(
|
21 |
-
'Instantiating the ' . __CLASS__ . ' class without a Response is deprecated since version 6.3 and will be removed in 7.0.',
|
22 |
-
E_USER_DEPRECATED
|
23 |
-
);
|
24 |
-
}
|
25 |
-
parent::__construct($message, $request, $response, $previous, $handlerContext);
|
26 |
-
}
|
27 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Exception;
|
3 |
+
|
4 |
+
use Psr\Http\Message\RequestInterface;
|
5 |
+
use Psr\Http\Message\ResponseInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Exception when an HTTP error occurs (4xx or 5xx error)
|
9 |
+
*/
|
10 |
+
class BadResponseException extends RequestException
|
11 |
+
{
|
12 |
+
public function __construct(
|
13 |
+
$message,
|
14 |
+
RequestInterface $request,
|
15 |
+
ResponseInterface $response = null,
|
16 |
+
\Exception $previous = null,
|
17 |
+
array $handlerContext = []
|
18 |
+
) {
|
19 |
+
if (null === $response) {
|
20 |
+
@trigger_error(
|
21 |
+
'Instantiating the ' . __CLASS__ . ' class without a Response is deprecated since version 6.3 and will be removed in 7.0.',
|
22 |
+
E_USER_DEPRECATED
|
23 |
+
);
|
24 |
+
}
|
25 |
+
parent::__construct($message, $request, $response, $previous, $handlerContext);
|
26 |
+
}
|
27 |
+
}
|
vendor/guzzlehttp/guzzle/src/Exception/ClientException.php
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Exception;
|
3 |
-
|
4 |
-
/**
|
5 |
-
* Exception when a client error is encountered (4xx codes)
|
6 |
-
*/
|
7 |
-
class ClientException extends BadResponseException {}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Exception;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Exception when a client error is encountered (4xx codes)
|
6 |
+
*/
|
7 |
+
class ClientException extends BadResponseException {}
|
vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php
CHANGED
@@ -1,37 +1,37 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Exception;
|
3 |
-
|
4 |
-
use Psr\Http\Message\RequestInterface;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* Exception thrown when a connection cannot be established.
|
8 |
-
*
|
9 |
-
* Note that no response is present for a ConnectException
|
10 |
-
*/
|
11 |
-
class ConnectException extends RequestException
|
12 |
-
{
|
13 |
-
public function __construct(
|
14 |
-
$message,
|
15 |
-
RequestInterface $request,
|
16 |
-
\Exception $previous = null,
|
17 |
-
array $handlerContext = []
|
18 |
-
) {
|
19 |
-
parent::__construct($message, $request, null, $previous, $handlerContext);
|
20 |
-
}
|
21 |
-
|
22 |
-
/**
|
23 |
-
* @return null
|
24 |
-
*/
|
25 |
-
public function getResponse()
|
26 |
-
{
|
27 |
-
return null;
|
28 |
-
}
|
29 |
-
|
30 |
-
/**
|
31 |
-
* @return bool
|
32 |
-
*/
|
33 |
-
public function hasResponse()
|
34 |
-
{
|
35 |
-
return false;
|
36 |
-
}
|
37 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Exception;
|
3 |
+
|
4 |
+
use Psr\Http\Message\RequestInterface;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Exception thrown when a connection cannot be established.
|
8 |
+
*
|
9 |
+
* Note that no response is present for a ConnectException
|
10 |
+
*/
|
11 |
+
class ConnectException extends RequestException
|
12 |
+
{
|
13 |
+
public function __construct(
|
14 |
+
$message,
|
15 |
+
RequestInterface $request,
|
16 |
+
\Exception $previous = null,
|
17 |
+
array $handlerContext = []
|
18 |
+
) {
|
19 |
+
parent::__construct($message, $request, null, $previous, $handlerContext);
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @return null
|
24 |
+
*/
|
25 |
+
public function getResponse()
|
26 |
+
{
|
27 |
+
return null;
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* @return bool
|
32 |
+
*/
|
33 |
+
public function hasResponse()
|
34 |
+
{
|
35 |
+
return false;
|
36 |
+
}
|
37 |
+
}
|
vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php
CHANGED
@@ -1,13 +1,13 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Exception;
|
3 |
-
|
4 |
-
/**
|
5 |
-
* @method string getMessage()
|
6 |
-
* @method \Throwable|null getPrevious()
|
7 |
-
* @method mixed getCode()
|
8 |
-
* @method string getFile()
|
9 |
-
* @method int getLine()
|
10 |
-
* @method array getTrace()
|
11 |
-
* @method string getTraceAsString()
|
12 |
-
*/
|
13 |
-
interface GuzzleException {}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Exception;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* @method string getMessage()
|
6 |
+
* @method \Throwable|null getPrevious()
|
7 |
+
* @method mixed getCode()
|
8 |
+
* @method string getFile()
|
9 |
+
* @method int getLine()
|
10 |
+
* @method array getTrace()
|
11 |
+
* @method string getTraceAsString()
|
12 |
+
*/
|
13 |
+
interface GuzzleException {}
|
vendor/guzzlehttp/guzzle/src/Exception/RequestException.php
CHANGED
@@ -1,217 +1,217 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Exception;
|
3 |
-
|
4 |
-
use Psr\Http\Message\RequestInterface;
|
5 |
-
use Psr\Http\Message\ResponseInterface;
|
6 |
-
use GuzzleHttp\Promise\PromiseInterface;
|
7 |
-
use Psr\Http\Message\UriInterface;
|
8 |
-
|
9 |
-
/**
|
10 |
-
* HTTP Request exception
|
11 |
-
*/
|
12 |
-
class RequestException extends TransferException
|
13 |
-
{
|
14 |
-
/** @var RequestInterface */
|
15 |
-
private $request;
|
16 |
-
|
17 |
-
/** @var ResponseInterface */
|
18 |
-
private $response;
|
19 |
-
|
20 |
-
/** @var array */
|
21 |
-
private $handlerContext;
|
22 |
-
|
23 |
-
public function __construct(
|
24 |
-
$message,
|
25 |
-
RequestInterface $request,
|
26 |
-
ResponseInterface $response = null,
|
27 |
-
\Exception $previous = null,
|
28 |
-
array $handlerContext = []
|
29 |
-
) {
|
30 |
-
// Set the code of the exception if the response is set and not future.
|
31 |
-
$code = $response && !($response instanceof PromiseInterface)
|
32 |
-
? $response->getStatusCode()
|
33 |
-
: 0;
|
34 |
-
parent::__construct($message, $code, $previous);
|
35 |
-
$this->request = $request;
|
36 |
-
$this->response = $response;
|
37 |
-
$this->handlerContext = $handlerContext;
|
38 |
-
}
|
39 |
-
|
40 |
-
/**
|
41 |
-
* Wrap non-RequestExceptions with a RequestException
|
42 |
-
*
|
43 |
-
* @param RequestInterface $request
|
44 |
-
* @param \Exception $e
|
45 |
-
*
|
46 |
-
* @return RequestException
|
47 |
-
*/
|
48 |
-
public static function wrapException(RequestInterface $request, \Exception $e)
|
49 |
-
{
|
50 |
-
return $e instanceof RequestException
|
51 |
-
? $e
|
52 |
-
: new RequestException($e->getMessage(), $request, null, $e);
|
53 |
-
}
|
54 |
-
|
55 |
-
/**
|
56 |
-
* Factory method to create a new exception with a normalized error message
|
57 |
-
*
|
58 |
-
* @param RequestInterface $request Request
|
59 |
-
* @param ResponseInterface $response Response received
|
60 |
-
* @param \Exception $previous Previous exception
|
61 |
-
* @param array $ctx Optional handler context.
|
62 |
-
*
|
63 |
-
* @return self
|
64 |
-
*/
|
65 |
-
public static function create(
|
66 |
-
RequestInterface $request,
|
67 |
-
ResponseInterface $response = null,
|
68 |
-
\Exception $previous = null,
|
69 |
-
array $ctx = []
|
70 |
-
) {
|
71 |
-
if (!$response) {
|
72 |
-
return new self(
|
73 |
-
'Error completing request',
|
74 |
-
$request,
|
75 |
-
null,
|
76 |
-
$previous,
|
77 |
-
$ctx
|
78 |
-
);
|
79 |
-
}
|
80 |
-
|
81 |
-
$level = (int) floor($response->getStatusCode() / 100);
|
82 |
-
if ($level === 4) {
|
83 |
-
$label = 'Client error';
|
84 |
-
$className = ClientException::class;
|
85 |
-
} elseif ($level === 5) {
|
86 |
-
$label = 'Server error';
|
87 |
-
$className = ServerException::class;
|
88 |
-
} else {
|
89 |
-
$label = 'Unsuccessful request';
|
90 |
-
$className = __CLASS__;
|
91 |
-
}
|
92 |
-
|
93 |
-
$uri = $request->getUri();
|
94 |
-
$uri = static::obfuscateUri($uri);
|
95 |
-
|
96 |
-
// Client Error: `GET /` resulted in a `404 Not Found` response:
|
97 |
-
// <html> ... (truncated)
|
98 |
-
$message = sprintf(
|
99 |
-
'%s: `%s %s` resulted in a `%s %s` response',
|
100 |
-
$label,
|
101 |
-
$request->getMethod(),
|
102 |
-
$uri,
|
103 |
-
$response->getStatusCode(),
|
104 |
-
$response->getReasonPhrase()
|
105 |
-
);
|
106 |
-
|
107 |
-
$summary = static::getResponseBodySummary($response);
|
108 |
-
|
109 |
-
if ($summary !== null) {
|
110 |
-
$message .= ":\n{$summary}\n";
|
111 |
-
}
|
112 |
-
|
113 |
-
return new $className($message, $request, $response, $previous, $ctx);
|
114 |
-
}
|
115 |
-
|
116 |
-
/**
|
117 |
-
* Get a short summary of the response
|
118 |
-
*
|
119 |
-
* Will return `null` if the response is not printable.
|
120 |
-
*
|
121 |
-
* @param ResponseInterface $response
|
122 |
-
*
|
123 |
-
* @return string|null
|
124 |
-
*/
|
125 |
-
public static function getResponseBodySummary(ResponseInterface $response)
|
126 |
-
{
|
127 |
-
$body = $response->getBody();
|
128 |
-
|
129 |
-
if (!$body->isSeekable()) {
|
130 |
-
return null;
|
131 |
-
}
|
132 |
-
|
133 |
-
$size = $body->getSize();
|
134 |
-
|
135 |
-
if ($size === 0) {
|
136 |
-
return null;
|
137 |
-
}
|
138 |
-
|
139 |
-
$summary = $body->read(120);
|
140 |
-
$body->rewind();
|
141 |
-
|
142 |
-
if ($size > 120) {
|
143 |
-
$summary .= ' (truncated...)';
|
144 |
-
}
|
145 |
-
|
146 |
-
// Matches any printable character, including unicode characters:
|
147 |
-
// letters, marks, numbers, punctuation, spacing, and separators.
|
148 |
-
if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) {
|
149 |
-
return null;
|
150 |
-
}
|
151 |
-
|
152 |
-
return $summary;
|
153 |
-
}
|
154 |
-
|
155 |
-
/**
|
156 |
-
* Obfuscates URI if there is an username and a password present
|
157 |
-
*
|
158 |
-
* @param UriInterface $uri
|
159 |
-
*
|
160 |
-
* @return UriInterface
|
161 |
-
*/
|
162 |
-
private static function obfuscateUri($uri)
|
163 |
-
{
|
164 |
-
$userInfo = $uri->getUserInfo();
|
165 |
-
|
166 |
-
if (false !== ($pos = strpos($userInfo, ':'))) {
|
167 |
-
return $uri->withUserInfo(substr($userInfo, 0, $pos), '***');
|
168 |
-
}
|
169 |
-
|
170 |
-
return $uri;
|
171 |
-
}
|
172 |
-
|
173 |
-
/**
|
174 |
-
* Get the request that caused the exception
|
175 |
-
*
|
176 |
-
* @return RequestInterface
|
177 |
-
*/
|
178 |
-
public function getRequest()
|
179 |
-
{
|
180 |
-
return $this->request;
|
181 |
-
}
|
182 |
-
|
183 |
-
/**
|
184 |
-
* Get the associated response
|
185 |
-
*
|
186 |
-
* @return ResponseInterface|null
|
187 |
-
*/
|
188 |
-
public function getResponse()
|
189 |
-
{
|
190 |
-
return $this->response;
|
191 |
-
}
|
192 |
-
|
193 |
-
/**
|
194 |
-
* Check if a response was received
|
195 |
-
*
|
196 |
-
* @return bool
|
197 |
-
*/
|
198 |
-
public function hasResponse()
|
199 |
-
{
|
200 |
-
return $this->response !== null;
|
201 |
-
}
|
202 |
-
|
203 |
-
/**
|
204 |
-
* Get contextual information about the error from the underlying handler.
|
205 |
-
*
|
206 |
-
* The contents of this array will vary depending on which handler you are
|
207 |
-
* using. It may also be just an empty array. Relying on this data will
|
208 |
-
* couple you to a specific handler, but can give more debug information
|
209 |
-
* when needed.
|
210 |
-
*
|
211 |
-
* @return array
|
212 |
-
*/
|
213 |
-
public function getHandlerContext()
|
214 |
-
{
|
215 |
-
return $this->handlerContext;
|
216 |
-
}
|
217 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Exception;
|
3 |
+
|
4 |
+
use Psr\Http\Message\RequestInterface;
|
5 |
+
use Psr\Http\Message\ResponseInterface;
|
6 |
+
use GuzzleHttp\Promise\PromiseInterface;
|
7 |
+
use Psr\Http\Message\UriInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* HTTP Request exception
|
11 |
+
*/
|
12 |
+
class RequestException extends TransferException
|
13 |
+
{
|
14 |
+
/** @var RequestInterface */
|
15 |
+
private $request;
|
16 |
+
|
17 |
+
/** @var ResponseInterface */
|
18 |
+
private $response;
|
19 |
+
|
20 |
+
/** @var array */
|
21 |
+
private $handlerContext;
|
22 |
+
|
23 |
+
public function __construct(
|
24 |
+
$message,
|
25 |
+
RequestInterface $request,
|
26 |
+
ResponseInterface $response = null,
|
27 |
+
\Exception $previous = null,
|
28 |
+
array $handlerContext = []
|
29 |
+
) {
|
30 |
+
// Set the code of the exception if the response is set and not future.
|
31 |
+
$code = $response && !($response instanceof PromiseInterface)
|
32 |
+
? $response->getStatusCode()
|
33 |
+
: 0;
|
34 |
+
parent::__construct($message, $code, $previous);
|
35 |
+
$this->request = $request;
|
36 |
+
$this->response = $response;
|
37 |
+
$this->handlerContext = $handlerContext;
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Wrap non-RequestExceptions with a RequestException
|
42 |
+
*
|
43 |
+
* @param RequestInterface $request
|
44 |
+
* @param \Exception $e
|
45 |
+
*
|
46 |
+
* @return RequestException
|
47 |
+
*/
|
48 |
+
public static function wrapException(RequestInterface $request, \Exception $e)
|
49 |
+
{
|
50 |
+
return $e instanceof RequestException
|
51 |
+
? $e
|
52 |
+
: new RequestException($e->getMessage(), $request, null, $e);
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Factory method to create a new exception with a normalized error message
|
57 |
+
*
|
58 |
+
* @param RequestInterface $request Request
|
59 |
+
* @param ResponseInterface $response Response received
|
60 |
+
* @param \Exception $previous Previous exception
|
61 |
+
* @param array $ctx Optional handler context.
|
62 |
+
*
|
63 |
+
* @return self
|
64 |
+
*/
|
65 |
+
public static function create(
|
66 |
+
RequestInterface $request,
|
67 |
+
ResponseInterface $response = null,
|
68 |
+
\Exception $previous = null,
|
69 |
+
array $ctx = []
|
70 |
+
) {
|
71 |
+
if (!$response) {
|
72 |
+
return new self(
|
73 |
+
'Error completing request',
|
74 |
+
$request,
|
75 |
+
null,
|
76 |
+
$previous,
|
77 |
+
$ctx
|
78 |
+
);
|
79 |
+
}
|
80 |
+
|
81 |
+
$level = (int) floor($response->getStatusCode() / 100);
|
82 |
+
if ($level === 4) {
|
83 |
+
$label = 'Client error';
|
84 |
+
$className = ClientException::class;
|
85 |
+
} elseif ($level === 5) {
|
86 |
+
$label = 'Server error';
|
87 |
+
$className = ServerException::class;
|
88 |
+
} else {
|
89 |
+
$label = 'Unsuccessful request';
|
90 |
+
$className = __CLASS__;
|
91 |
+
}
|
92 |
+
|
93 |
+
$uri = $request->getUri();
|
94 |
+
$uri = static::obfuscateUri($uri);
|
95 |
+
|
96 |
+
// Client Error: `GET /` resulted in a `404 Not Found` response:
|
97 |
+
// <html> ... (truncated)
|
98 |
+
$message = sprintf(
|
99 |
+
'%s: `%s %s` resulted in a `%s %s` response',
|
100 |
+
$label,
|
101 |
+
$request->getMethod(),
|
102 |
+
$uri,
|
103 |
+
$response->getStatusCode(),
|
104 |
+
$response->getReasonPhrase()
|
105 |
+
);
|
106 |
+
|
107 |
+
$summary = static::getResponseBodySummary($response);
|
108 |
+
|
109 |
+
if ($summary !== null) {
|
110 |
+
$message .= ":\n{$summary}\n";
|
111 |
+
}
|
112 |
+
|
113 |
+
return new $className($message, $request, $response, $previous, $ctx);
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Get a short summary of the response
|
118 |
+
*
|
119 |
+
* Will return `null` if the response is not printable.
|
120 |
+
*
|
121 |
+
* @param ResponseInterface $response
|
122 |
+
*
|
123 |
+
* @return string|null
|
124 |
+
*/
|
125 |
+
public static function getResponseBodySummary(ResponseInterface $response)
|
126 |
+
{
|
127 |
+
$body = $response->getBody();
|
128 |
+
|
129 |
+
if (!$body->isSeekable()) {
|
130 |
+
return null;
|
131 |
+
}
|
132 |
+
|
133 |
+
$size = $body->getSize();
|
134 |
+
|
135 |
+
if ($size === 0) {
|
136 |
+
return null;
|
137 |
+
}
|
138 |
+
|
139 |
+
$summary = $body->read(120);
|
140 |
+
$body->rewind();
|
141 |
+
|
142 |
+
if ($size > 120) {
|
143 |
+
$summary .= ' (truncated...)';
|
144 |
+
}
|
145 |
+
|
146 |
+
// Matches any printable character, including unicode characters:
|
147 |
+
// letters, marks, numbers, punctuation, spacing, and separators.
|
148 |
+
if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) {
|
149 |
+
return null;
|
150 |
+
}
|
151 |
+
|
152 |
+
return $summary;
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* Obfuscates URI if there is an username and a password present
|
157 |
+
*
|
158 |
+
* @param UriInterface $uri
|
159 |
+
*
|
160 |
+
* @return UriInterface
|
161 |
+
*/
|
162 |
+
private static function obfuscateUri($uri)
|
163 |
+
{
|
164 |
+
$userInfo = $uri->getUserInfo();
|
165 |
+
|
166 |
+
if (false !== ($pos = strpos($userInfo, ':'))) {
|
167 |
+
return $uri->withUserInfo(substr($userInfo, 0, $pos), '***');
|
168 |
+
}
|
169 |
+
|
170 |
+
return $uri;
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* Get the request that caused the exception
|
175 |
+
*
|
176 |
+
* @return RequestInterface
|
177 |
+
*/
|
178 |
+
public function getRequest()
|
179 |
+
{
|
180 |
+
return $this->request;
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Get the associated response
|
185 |
+
*
|
186 |
+
* @return ResponseInterface|null
|
187 |
+
*/
|
188 |
+
public function getResponse()
|
189 |
+
{
|
190 |
+
return $this->response;
|
191 |
+
}
|
192 |
+
|
193 |
+
/**
|
194 |
+
* Check if a response was received
|
195 |
+
*
|
196 |
+
* @return bool
|
197 |
+
*/
|
198 |
+
public function hasResponse()
|
199 |
+
{
|
200 |
+
return $this->response !== null;
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* Get contextual information about the error from the underlying handler.
|
205 |
+
*
|
206 |
+
* The contents of this array will vary depending on which handler you are
|
207 |
+
* using. It may also be just an empty array. Relying on this data will
|
208 |
+
* couple you to a specific handler, but can give more debug information
|
209 |
+
* when needed.
|
210 |
+
*
|
211 |
+
* @return array
|
212 |
+
*/
|
213 |
+
public function getHandlerContext()
|
214 |
+
{
|
215 |
+
return $this->handlerContext;
|
216 |
+
}
|
217 |
+
}
|
vendor/guzzlehttp/guzzle/src/Exception/SeekException.php
CHANGED
@@ -1,27 +1,27 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Exception;
|
3 |
-
|
4 |
-
use Psr\Http\Message\StreamInterface;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* Exception thrown when a seek fails on a stream.
|
8 |
-
*/
|
9 |
-
class SeekException extends \RuntimeException implements GuzzleException
|
10 |
-
{
|
11 |
-
private $stream;
|
12 |
-
|
13 |
-
public function __construct(StreamInterface $stream, $pos = 0, $msg = '')
|
14 |
-
{
|
15 |
-
$this->stream = $stream;
|
16 |
-
$msg = $msg ?: 'Could not seek the stream to position ' . $pos;
|
17 |
-
parent::__construct($msg);
|
18 |
-
}
|
19 |
-
|
20 |
-
/**
|
21 |
-
* @return StreamInterface
|
22 |
-
*/
|
23 |
-
public function getStream()
|
24 |
-
{
|
25 |
-
return $this->stream;
|
26 |
-
}
|
27 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Exception;
|
3 |
+
|
4 |
+
use Psr\Http\Message\StreamInterface;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Exception thrown when a seek fails on a stream.
|
8 |
+
*/
|
9 |
+
class SeekException extends \RuntimeException implements GuzzleException
|
10 |
+
{
|
11 |
+
private $stream;
|
12 |
+
|
13 |
+
public function __construct(StreamInterface $stream, $pos = 0, $msg = '')
|
14 |
+
{
|
15 |
+
$this->stream = $stream;
|
16 |
+
$msg = $msg ?: 'Could not seek the stream to position ' . $pos;
|
17 |
+
parent::__construct($msg);
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @return StreamInterface
|
22 |
+
*/
|
23 |
+
public function getStream()
|
24 |
+
{
|
25 |
+
return $this->stream;
|
26 |
+
}
|
27 |
+
}
|
vendor/guzzlehttp/guzzle/src/Exception/ServerException.php
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Exception;
|
3 |
-
|
4 |
-
/**
|
5 |
-
* Exception when a server error is encountered (5xx codes)
|
6 |
-
*/
|
7 |
-
class ServerException extends BadResponseException {}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Exception;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Exception when a server error is encountered (5xx codes)
|
6 |
+
*/
|
7 |
+
class ServerException extends BadResponseException {}
|
vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Exception;
|
3 |
-
|
4 |
-
class TooManyRedirectsException extends RequestException {}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Exception;
|
3 |
+
|
4 |
+
class TooManyRedirectsException extends RequestException {}
|
vendor/guzzlehttp/guzzle/src/Exception/TransferException.php
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Exception;
|
3 |
-
|
4 |
-
class TransferException extends \RuntimeException implements GuzzleException {}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Exception;
|
3 |
+
|
4 |
+
class TransferException extends \RuntimeException implements GuzzleException {}
|
vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php
CHANGED
@@ -1,565 +1,565 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Handler;
|
3 |
-
|
4 |
-
use GuzzleHttp\Exception\RequestException;
|
5 |
-
use GuzzleHttp\Exception\ConnectException;
|
6 |
-
use GuzzleHttp\Promise\FulfilledPromise;
|
7 |
-
use GuzzleHttp\Psr7;
|
8 |
-
use GuzzleHttp\Psr7\LazyOpenStream;
|
9 |
-
use GuzzleHttp\TransferStats;
|
10 |
-
use Psr\Http\Message\RequestInterface;
|
11 |
-
|
12 |
-
/**
|
13 |
-
* Creates curl resources from a request
|
14 |
-
*/
|
15 |
-
class CurlFactory implements CurlFactoryInterface
|
16 |
-
{
|
17 |
-
/** @var array */
|
18 |
-
private $handles = [];
|
19 |
-
|
20 |
-
/** @var int Total number of idle handles to keep in cache */
|
21 |
-
private $maxHandles;
|
22 |
-
|
23 |
-
/**
|
24 |
-
* @param int $maxHandles Maximum number of idle handles.
|
25 |
-
*/
|
26 |
-
public function __construct($maxHandles)
|
27 |
-
{
|
28 |
-
$this->maxHandles = $maxHandles;
|
29 |
-
}
|
30 |
-
|
31 |
-
public function create(RequestInterface $request, array $options)
|
32 |
-
{
|
33 |
-
if (isset($options['curl']['body_as_string'])) {
|
34 |
-
$options['_body_as_string'] = $options['curl']['body_as_string'];
|
35 |
-
unset($options['curl']['body_as_string']);
|
36 |
-
}
|
37 |
-
|
38 |
-
$easy = new EasyHandle;
|
39 |
-
$easy->request = $request;
|
40 |
-
$easy->options = $options;
|
41 |
-
$conf = $this->getDefaultConf($easy);
|
42 |
-
$this->applyMethod($easy, $conf);
|
43 |
-
$this->applyHandlerOptions($easy, $conf);
|
44 |
-
$this->applyHeaders($easy, $conf);
|
45 |
-
unset($conf['_headers']);
|
46 |
-
|
47 |
-
// Add handler options from the request configuration options
|
48 |
-
if (isset($options['curl'])) {
|
49 |
-
$conf = array_replace($conf, $options['curl']);
|
50 |
-
}
|
51 |
-
|
52 |
-
$conf[CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy);
|
53 |
-
$easy->handle = $this->handles
|
54 |
-
? array_pop($this->handles)
|
55 |
-
: curl_init();
|
56 |
-
curl_setopt_array($easy->handle, $conf);
|
57 |
-
|
58 |
-
return $easy;
|
59 |
-
}
|
60 |
-
|
61 |
-
public function release(EasyHandle $easy)
|
62 |
-
{
|
63 |
-
$resource = $easy->handle;
|
64 |
-
unset($easy->handle);
|
65 |
-
|
66 |
-
if (count($this->handles) >= $this->maxHandles) {
|
67 |
-
curl_close($resource);
|
68 |
-
} else {
|
69 |
-
// Remove all callback functions as they can hold onto references
|
70 |
-
// and are not cleaned up by curl_reset. Using curl_setopt_array
|
71 |
-
// does not work for some reason, so removing each one
|
72 |
-
// individually.
|
73 |
-
curl_setopt($resource, CURLOPT_HEADERFUNCTION, null);
|
74 |
-
curl_setopt($resource, CURLOPT_READFUNCTION, null);
|
75 |
-
curl_setopt($resource, CURLOPT_WRITEFUNCTION, null);
|
76 |
-
curl_setopt($resource, CURLOPT_PROGRESSFUNCTION, null);
|
77 |
-
curl_reset($resource);
|
78 |
-
$this->handles[] = $resource;
|
79 |
-
}
|
80 |
-
}
|
81 |
-
|
82 |
-
/**
|
83 |
-
* Completes a cURL transaction, either returning a response promise or a
|
84 |
-
* rejected promise.
|
85 |
-
*
|
86 |
-
* @param callable $handler
|
87 |
-
* @param EasyHandle $easy
|
88 |
-
* @param CurlFactoryInterface $factory Dictates how the handle is released
|
89 |
-
*
|
90 |
-
* @return \GuzzleHttp\Promise\PromiseInterface
|
91 |
-
*/
|
92 |
-
public static function finish(
|
93 |
-
callable $handler,
|
94 |
-
EasyHandle $easy,
|
95 |
-
CurlFactoryInterface $factory
|
96 |
-
) {
|
97 |
-
if (isset($easy->options['on_stats'])) {
|
98 |
-
self::invokeStats($easy);
|
99 |
-
}
|
100 |
-
|
101 |
-
if (!$easy->response || $easy->errno) {
|
102 |
-
return self::finishError($handler, $easy, $factory);
|
103 |
-
}
|
104 |
-
|
105 |
-
// Return the response if it is present and there is no error.
|
106 |
-
$factory->release($easy);
|
107 |
-
|
108 |
-
// Rewind the body of the response if possible.
|
109 |
-
$body = $easy->response->getBody();
|
110 |
-
if ($body->isSeekable()) {
|
111 |
-
$body->rewind();
|
112 |
-
}
|
113 |
-
|
114 |
-
return new FulfilledPromise($easy->response);
|
115 |
-
}
|
116 |
-
|
117 |
-
private static function invokeStats(EasyHandle $easy)
|
118 |
-
{
|
119 |
-
$curlStats = curl_getinfo($easy->handle);
|
120 |
-
$stats = new TransferStats(
|
121 |
-
$easy->request,
|
122 |
-
$easy->response,
|
123 |
-
$curlStats['total_time'],
|
124 |
-
$easy->errno,
|
125 |
-
$curlStats
|
126 |
-
);
|
127 |
-
call_user_func($easy->options['on_stats'], $stats);
|
128 |
-
}
|
129 |
-
|
130 |
-
private static function finishError(
|
131 |
-
callable $handler,
|
132 |
-
EasyHandle $easy,
|
133 |
-
CurlFactoryInterface $factory
|
134 |
-
) {
|
135 |
-
// Get error information and release the handle to the factory.
|
136 |
-
$ctx = [
|
137 |
-
'errno' => $easy->errno,
|
138 |
-
'error' => curl_error($easy->handle),
|
139 |
-
] + curl_getinfo($easy->handle);
|
140 |
-
$factory->release($easy);
|
141 |
-
|
142 |
-
// Retry when nothing is present or when curl failed to rewind.
|
143 |
-
if (empty($easy->options['_err_message'])
|
144 |
-
&& (!$easy->errno || $easy->errno == 65)
|
145 |
-
) {
|
146 |
-
return self::retryFailedRewind($handler, $easy, $ctx);
|
147 |
-
}
|
148 |
-
|
149 |
-
return self::createRejection($easy, $ctx);
|
150 |
-
}
|
151 |
-
|
152 |
-
private static function createRejection(EasyHandle $easy, array $ctx)
|
153 |
-
{
|
154 |
-
static $connectionErrors = [
|
155 |
-
CURLE_OPERATION_TIMEOUTED => true,
|
156 |
-
CURLE_COULDNT_RESOLVE_HOST => true,
|
157 |
-
CURLE_COULDNT_CONNECT => true,
|
158 |
-
CURLE_SSL_CONNECT_ERROR => true,
|
159 |
-
CURLE_GOT_NOTHING => true,
|
160 |
-
];
|
161 |
-
|
162 |
-
// If an exception was encountered during the onHeaders event, then
|
163 |
-
// return a rejected promise that wraps that exception.
|
164 |
-
if ($easy->onHeadersException) {
|
165 |
-
return \GuzzleHttp\Promise\rejection_for(
|
166 |
-
new RequestException(
|
167 |
-
'An error was encountered during the on_headers event',
|
168 |
-
$easy->request,
|
169 |
-
$easy->response,
|
170 |
-
$easy->onHeadersException,
|
171 |
-
$ctx
|
172 |
-
)
|
173 |
-
);
|
174 |
-
}
|
175 |
-
|
176 |
-
$message = sprintf(
|
177 |
-
'cURL error %s: %s (%s)',
|
178 |
-
$ctx['errno'],
|
179 |
-
$ctx['error'],
|
180 |
-
'see http://curl.haxx.se/libcurl/c/libcurl-errors.html'
|
181 |
-
);
|
182 |
-
|
183 |
-
// Create a connection exception if it was a specific error code.
|
184 |
-
$error = isset($connectionErrors[$easy->errno])
|
185 |
-
? new ConnectException($message, $easy->request, null, $ctx)
|
186 |
-
: new RequestException($message, $easy->request, $easy->response, null, $ctx);
|
187 |
-
|
188 |
-
return \GuzzleHttp\Promise\rejection_for($error);
|
189 |
-
}
|
190 |
-
|
191 |
-
private function getDefaultConf(EasyHandle $easy)
|
192 |
-
{
|
193 |
-
$conf = [
|
194 |
-
'_headers' => $easy->request->getHeaders(),
|
195 |
-
CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(),
|
196 |
-
CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''),
|
197 |
-
CURLOPT_RETURNTRANSFER => false,
|
198 |
-
CURLOPT_HEADER => false,
|
199 |
-
CURLOPT_CONNECTTIMEOUT => 150,
|
200 |
-
];
|
201 |
-
|
202 |
-
if (defined('CURLOPT_PROTOCOLS')) {
|
203 |
-
$conf[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
|
204 |
-
}
|
205 |
-
|
206 |
-
$version = $easy->request->getProtocolVersion();
|
207 |
-
if ($version == 1.1) {
|
208 |
-
$conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1;
|
209 |
-
} elseif ($version == 2.0) {
|
210 |
-
$conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0;
|
211 |
-
} else {
|
212 |
-
$conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
|
213 |
-
}
|
214 |
-
|
215 |
-
return $conf;
|
216 |
-
}
|
217 |
-
|
218 |
-
private function applyMethod(EasyHandle $easy, array &$conf)
|
219 |
-
{
|
220 |
-
$body = $easy->request->getBody();
|
221 |
-
$size = $body->getSize();
|
222 |
-
|
223 |
-
if ($size === null || $size > 0) {
|
224 |
-
$this->applyBody($easy->request, $easy->options, $conf);
|
225 |
-
return;
|
226 |
-
}
|
227 |
-
|
228 |
-
$method = $easy->request->getMethod();
|
229 |
-
if ($method === 'PUT' || $method === 'POST') {
|
230 |
-
// See http://tools.ietf.org/html/rfc7230#section-3.3.2
|
231 |
-
if (!$easy->request->hasHeader('Content-Length')) {
|
232 |
-
$conf[CURLOPT_HTTPHEADER][] = 'Content-Length: 0';
|
233 |
-
}
|
234 |
-
} elseif ($method === 'HEAD') {
|
235 |
-
$conf[CURLOPT_NOBODY] = true;
|
236 |
-
unset(
|
237 |
-
$conf[CURLOPT_WRITEFUNCTION],
|
238 |
-
$conf[CURLOPT_READFUNCTION],
|
239 |
-
$conf[CURLOPT_FILE],
|
240 |
-
$conf[CURLOPT_INFILE]
|
241 |
-
);
|
242 |
-
}
|
243 |
-
}
|
244 |
-
|
245 |
-
private function applyBody(RequestInterface $request, array $options, array &$conf)
|
246 |
-
{
|
247 |
-
$size = $request->hasHeader('Content-Length')
|
248 |
-
? (int) $request->getHeaderLine('Content-Length')
|
249 |
-
: null;
|
250 |
-
|
251 |
-
// Send the body as a string if the size is less than 1MB OR if the
|
252 |
-
// [curl][body_as_string] request value is set.
|
253 |
-
if (($size !== null && $size < 1000000) ||
|
254 |
-
!empty($options['_body_as_string'])
|
255 |
-
) {
|
256 |
-
$conf[CURLOPT_POSTFIELDS] = (string) $request->getBody();
|
257 |
-
// Don't duplicate the Content-Length header
|
258 |
-
$this->removeHeader('Content-Length', $conf);
|
259 |
-
$this->removeHeader('Transfer-Encoding', $conf);
|
260 |
-
} else {
|
261 |
-
$conf[CURLOPT_UPLOAD] = true;
|
262 |
-
if ($size !== null) {
|
263 |
-
$conf[CURLOPT_INFILESIZE] = $size;
|
264 |
-
$this->removeHeader('Content-Length', $conf);
|
265 |
-
}
|
266 |
-
$body = $request->getBody();
|
267 |
-
if ($body->isSeekable()) {
|
268 |
-
$body->rewind();
|
269 |
-
}
|
270 |
-
$conf[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) {
|
271 |
-
return $body->read($length);
|
272 |
-
};
|
273 |
-
}
|
274 |
-
|
275 |
-
// If the Expect header is not present, prevent curl from adding it
|
276 |
-
if (!$request->hasHeader('Expect')) {
|
277 |
-
$conf[CURLOPT_HTTPHEADER][] = 'Expect:';
|
278 |
-
}
|
279 |
-
|
280 |
-
// cURL sometimes adds a content-type by default. Prevent this.
|
281 |
-
if (!$request->hasHeader('Content-Type')) {
|
282 |
-
$conf[CURLOPT_HTTPHEADER][] = 'Content-Type:';
|
283 |
-
}
|
284 |
-
}
|
285 |
-
|
286 |
-
private function applyHeaders(EasyHandle $easy, array &$conf)
|
287 |
-
{
|
288 |
-
foreach ($conf['_headers'] as $name => $values) {
|
289 |
-
foreach ($values as $value) {
|
290 |
-
$value = (string) $value;
|
291 |
-
if ($value === '') {
|
292 |
-
// cURL requires a special format for empty headers.
|
293 |
-
// See https://github.com/guzzle/guzzle/issues/1882 for more details.
|
294 |
-
$conf[CURLOPT_HTTPHEADER][] = "$name;";
|
295 |
-
} else {
|
296 |
-
$conf[CURLOPT_HTTPHEADER][] = "$name: $value";
|
297 |
-
}
|
298 |
-
}
|
299 |
-
}
|
300 |
-
|
301 |
-
// Remove the Accept header if one was not set
|
302 |
-
if (!$easy->request->hasHeader('Accept')) {
|
303 |
-
$conf[CURLOPT_HTTPHEADER][] = 'Accept:';
|
304 |
-
}
|
305 |
-
}
|
306 |
-
|
307 |
-
/**
|
308 |
-
* Remove a header from the options array.
|
309 |
-
*
|
310 |
-
* @param string $name Case-insensitive header to remove
|
311 |
-
* @param array $options Array of options to modify
|
312 |
-
*/
|
313 |
-
private function removeHeader($name, array &$options)
|
314 |
-
{
|
315 |
-
foreach (array_keys($options['_headers']) as $key) {
|
316 |
-
if (!strcasecmp($key, $name)) {
|
317 |
-
unset($options['_headers'][$key]);
|
318 |
-
return;
|
319 |
-
}
|
320 |
-
}
|
321 |
-
}
|
322 |
-
|
323 |
-
private function applyHandlerOptions(EasyHandle $easy, array &$conf)
|
324 |
-
{
|
325 |
-
$options = $easy->options;
|
326 |
-
if (isset($options['verify'])) {
|
327 |
-
if ($options['verify'] === false) {
|
328 |
-
unset($conf[CURLOPT_CAINFO]);
|
329 |
-
$conf[CURLOPT_SSL_VERIFYHOST] = 0;
|
330 |
-
$conf[CURLOPT_SSL_VERIFYPEER] = false;
|
331 |
-
} else {
|
332 |
-
$conf[CURLOPT_SSL_VERIFYHOST] = 2;
|
333 |
-
$conf[CURLOPT_SSL_VERIFYPEER] = true;
|
334 |
-
if (is_string($options['verify'])) {
|
335 |
-
// Throw an error if the file/folder/link path is not valid or doesn't exist.
|
336 |
-
if (!file_exists($options['verify'])) {
|
337 |
-
throw new \InvalidArgumentException(
|
338 |
-
"SSL CA bundle not found: {$options['verify']}"
|
339 |
-
);
|
340 |
-
}
|
341 |
-
// If it's a directory or a link to a directory use CURLOPT_CAPATH.
|
342 |
-
// If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO.
|
343 |
-
if (is_dir($options['verify']) ||
|
344 |
-
(is_link($options['verify']) && is_dir(readlink($options['verify'])))) {
|
345 |
-
$conf[CURLOPT_CAPATH] = $options['verify'];
|
346 |
-
} else {
|
347 |
-
$conf[CURLOPT_CAINFO] = $options['verify'];
|
348 |
-
}
|
349 |
-
}
|
350 |
-
}
|
351 |
-
}
|
352 |
-
|
353 |
-
if (!empty($options['decode_content'])) {
|
354 |
-
$accept = $easy->request->getHeaderLine('Accept-Encoding');
|
355 |
-
if ($accept) {
|
356 |
-
$conf[CURLOPT_ENCODING] = $accept;
|
357 |
-
} else {
|
358 |
-
$conf[CURLOPT_ENCODING] = '';
|
359 |
-
// Don't let curl send the header over the wire
|
360 |
-
$conf[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:';
|
361 |
-
}
|
362 |
-
}
|
363 |
-
|
364 |
-
if (isset($options['sink'])) {
|
365 |
-
$sink = $options['sink'];
|
366 |
-
if (!is_string($sink)) {
|
367 |
-
$sink = \GuzzleHttp\Psr7\stream_for($sink);
|
368 |
-
} elseif (!is_dir(dirname($sink))) {
|
369 |
-
// Ensure that the directory exists before failing in curl.
|
370 |
-
throw new \RuntimeException(sprintf(
|
371 |
-
'Directory %s does not exist for sink value of %s',
|
372 |
-
dirname($sink),
|
373 |
-
$sink
|
374 |
-
));
|
375 |
-
} else {
|
376 |
-
$sink = new LazyOpenStream($sink, 'w+');
|
377 |
-
}
|
378 |
-
$easy->sink = $sink;
|
379 |
-
$conf[CURLOPT_WRITEFUNCTION] = function ($ch, $write) use ($sink) {
|
380 |
-
return $sink->write($write);
|
381 |
-
};
|
382 |
-
} else {
|
383 |
-
// Use a default temp stream if no sink was set.
|
384 |
-
$conf[CURLOPT_FILE] = fopen('php://temp', 'w+');
|
385 |
-
$easy->sink = Psr7\stream_for($conf[CURLOPT_FILE]);
|
386 |
-
}
|
387 |
-
$timeoutRequiresNoSignal = false;
|
388 |
-
if (isset($options['timeout'])) {
|
389 |
-
$timeoutRequiresNoSignal |= $options['timeout'] < 1;
|
390 |
-
$conf[CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000;
|
391 |
-
}
|
392 |
-
|
393 |
-
// CURL default value is CURL_IPRESOLVE_WHATEVER
|
394 |
-
if (isset($options['force_ip_resolve'])) {
|
395 |
-
if ('v4' === $options['force_ip_resolve']) {
|
396 |
-
$conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
|
397 |
-
} elseif ('v6' === $options['force_ip_resolve']) {
|
398 |
-
$conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V6;
|
399 |
-
}
|
400 |
-
}
|
401 |
-
|
402 |
-
if (isset($options['connect_timeout'])) {
|
403 |
-
$timeoutRequiresNoSignal |= $options['connect_timeout'] < 1;
|
404 |
-
$conf[CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000;
|
405 |
-
}
|
406 |
-
|
407 |
-
if ($timeoutRequiresNoSignal && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
|
408 |
-
$conf[CURLOPT_NOSIGNAL] = true;
|
409 |
-
}
|
410 |
-
|
411 |
-
if (isset($options['proxy'])) {
|
412 |
-
if (!is_array($options['proxy'])) {
|
413 |
-
$conf[CURLOPT_PROXY] = $options['proxy'];
|
414 |
-
} else {
|
415 |
-
$scheme = $easy->request->getUri()->getScheme();
|
416 |
-
if (isset($options['proxy'][$scheme])) {
|
417 |
-
$host = $easy->request->getUri()->getHost();
|
418 |
-
if (!isset($options['proxy']['no']) ||
|
419 |
-
!\GuzzleHttp\is_host_in_noproxy($host, $options['proxy']['no'])
|
420 |
-
) {
|
421 |
-
$conf[CURLOPT_PROXY] = $options['proxy'][$scheme];
|
422 |
-
}
|
423 |
-
}
|
424 |
-
}
|
425 |
-
}
|
426 |
-
|
427 |
-
if (isset($options['cert'])) {
|
428 |
-
$cert = $options['cert'];
|
429 |
-
if (is_array($cert)) {
|
430 |
-
$conf[CURLOPT_SSLCERTPASSWD] = $cert[1];
|
431 |
-
$cert = $cert[0];
|
432 |
-
}
|
433 |
-
if (!file_exists($cert)) {
|
434 |
-
throw new \InvalidArgumentException(
|
435 |
-
"SSL certificate not found: {$cert}"
|
436 |
-
);
|
437 |
-
}
|
438 |
-
$conf[CURLOPT_SSLCERT] = $cert;
|
439 |
-
}
|
440 |
-
|
441 |
-
if (isset($options['ssl_key'])) {
|
442 |
-
$sslKey = $options['ssl_key'];
|
443 |
-
if (is_array($sslKey)) {
|
444 |
-
$conf[CURLOPT_SSLKEYPASSWD] = $sslKey[1];
|
445 |
-
$sslKey = $sslKey[0];
|
446 |
-
}
|
447 |
-
if (!file_exists($sslKey)) {
|
448 |
-
throw new \InvalidArgumentException(
|
449 |
-
"SSL private key not found: {$sslKey}"
|
450 |
-
);
|
451 |
-
}
|
452 |
-
$conf[CURLOPT_SSLKEY] = $sslKey;
|
453 |
-
}
|
454 |
-
|
455 |
-
if (isset($options['progress'])) {
|
456 |
-
$progress = $options['progress'];
|
457 |
-
if (!is_callable($progress)) {
|
458 |
-
throw new \InvalidArgumentException(
|
459 |
-
'progress client option must be callable'
|
460 |
-
);
|
461 |
-
}
|
462 |
-
$conf[CURLOPT_NOPROGRESS] = false;
|
463 |
-
$conf[CURLOPT_PROGRESSFUNCTION] = function () use ($progress) {
|
464 |
-
$args = func_get_args();
|
465 |
-
// PHP 5.5 pushed the handle onto the start of the args
|
466 |
-
if (is_resource($args[0])) {
|
467 |
-
array_shift($args);
|
468 |
-
}
|
469 |
-
call_user_func_array($progress, $args);
|
470 |
-
};
|
471 |
-
}
|
472 |
-
|
473 |
-
if (!empty($options['debug'])) {
|
474 |
-
$conf[CURLOPT_STDERR] = \GuzzleHttp\debug_resource($options['debug']);
|
475 |
-
$conf[CURLOPT_VERBOSE] = true;
|
476 |
-
}
|
477 |
-
}
|
478 |
-
|
479 |
-
/**
|
480 |
-
* This function ensures that a response was set on a transaction. If one
|
481 |
-
* was not set, then the request is retried if possible. This error
|
482 |
-
* typically means you are sending a payload, curl encountered a
|
483 |
-
* "Connection died, retrying a fresh connect" error, tried to rewind the
|
484 |
-
* stream, and then encountered a "necessary data rewind wasn't possible"
|
485 |
-
* error, causing the request to be sent through curl_multi_info_read()
|
486 |
-
* without an error status.
|
487 |
-
*/
|
488 |
-
private static function retryFailedRewind(
|
489 |
-
callable $handler,
|
490 |
-
EasyHandle $easy,
|
491 |
-
array $ctx
|
492 |
-
) {
|
493 |
-
try {
|
494 |
-
// Only rewind if the body has been read from.
|
495 |
-
$body = $easy->request->getBody();
|
496 |
-
if ($body->tell() > 0) {
|
497 |
-
$body->rewind();
|
498 |
-
}
|
499 |
-
} catch (\RuntimeException $e) {
|
500 |
-
$ctx['error'] = 'The connection unexpectedly failed without '
|
501 |
-
. 'providing an error. The request would have been retried, '
|
502 |
-
. 'but attempting to rewind the request body failed. '
|
503 |
-
. 'Exception: ' . $e;
|
504 |
-
return self::createRejection($easy, $ctx);
|
505 |
-
}
|
506 |
-
|
507 |
-
// Retry no more than 3 times before giving up.
|
508 |
-
if (!isset($easy->options['_curl_retries'])) {
|
509 |
-
$easy->options['_curl_retries'] = 1;
|
510 |
-
} elseif ($easy->options['_curl_retries'] == 2) {
|
511 |
-
$ctx['error'] = 'The cURL request was retried 3 times '
|
512 |
-
. 'and did not succeed. The most likely reason for the failure '
|
513 |
-
. 'is that cURL was unable to rewind the body of the request '
|
514 |
-
. 'and subsequent retries resulted in the same error. Turn on '
|
515 |
-
. 'the debug option to see what went wrong. See '
|
516 |
-
. 'https://bugs.php.net/bug.php?id=47204 for more information.';
|
517 |
-
return self::createRejection($easy, $ctx);
|
518 |
-
} else {
|
519 |
-
$easy->options['_curl_retries']++;
|
520 |
-
}
|
521 |
-
|
522 |
-
return $handler($easy->request, $easy->options);
|
523 |
-
}
|
524 |
-
|
525 |
-
private function createHeaderFn(EasyHandle $easy)
|
526 |
-
{
|
527 |
-
if (isset($easy->options['on_headers'])) {
|
528 |
-
$onHeaders = $easy->options['on_headers'];
|
529 |
-
|
530 |
-
if (!is_callable($onHeaders)) {
|
531 |
-
throw new \InvalidArgumentException('on_headers must be callable');
|
532 |
-
}
|
533 |
-
} else {
|
534 |
-
$onHeaders = null;
|
535 |
-
}
|
536 |
-
|
537 |
-
return function ($ch, $h) use (
|
538 |
-
$onHeaders,
|
539 |
-
$easy,
|
540 |
-
&$startingResponse
|
541 |
-
) {
|
542 |
-
$value = trim($h);
|
543 |
-
if ($value === '') {
|
544 |
-
$startingResponse = true;
|
545 |
-
$easy->createResponse();
|
546 |
-
if ($onHeaders !== null) {
|
547 |
-
try {
|
548 |
-
$onHeaders($easy->response);
|
549 |
-
} catch (\Exception $e) {
|
550 |
-
// Associate the exception with the handle and trigger
|
551 |
-
// a curl header write error by returning 0.
|
552 |
-
$easy->onHeadersException = $e;
|
553 |
-
return -1;
|
554 |
-
}
|
555 |
-
}
|
556 |
-
} elseif ($startingResponse) {
|
557 |
-
$startingResponse = false;
|
558 |
-
$easy->headers = [$value];
|
559 |
-
} else {
|
560 |
-
$easy->headers[] = $value;
|
561 |
-
}
|
562 |
-
return strlen($h);
|
563 |
-
};
|
564 |
-
}
|
565 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Handler;
|
3 |
+
|
4 |
+
use GuzzleHttp\Exception\RequestException;
|
5 |
+
use GuzzleHttp\Exception\ConnectException;
|
6 |
+
use GuzzleHttp\Promise\FulfilledPromise;
|
7 |
+
use GuzzleHttp\Psr7;
|
8 |
+
use GuzzleHttp\Psr7\LazyOpenStream;
|
9 |
+
use GuzzleHttp\TransferStats;
|
10 |
+
use Psr\Http\Message\RequestInterface;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Creates curl resources from a request
|
14 |
+
*/
|
15 |
+
class CurlFactory implements CurlFactoryInterface
|
16 |
+
{
|
17 |
+
/** @var array */
|
18 |
+
private $handles = [];
|
19 |
+
|
20 |
+
/** @var int Total number of idle handles to keep in cache */
|
21 |
+
private $maxHandles;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @param int $maxHandles Maximum number of idle handles.
|
25 |
+
*/
|
26 |
+
public function __construct($maxHandles)
|
27 |
+
{
|
28 |
+
$this->maxHandles = $maxHandles;
|
29 |
+
}
|
30 |
+
|
31 |
+
public function create(RequestInterface $request, array $options)
|
32 |
+
{
|
33 |
+
if (isset($options['curl']['body_as_string'])) {
|
34 |
+
$options['_body_as_string'] = $options['curl']['body_as_string'];
|
35 |
+
unset($options['curl']['body_as_string']);
|
36 |
+
}
|
37 |
+
|
38 |
+
$easy = new EasyHandle;
|
39 |
+
$easy->request = $request;
|
40 |
+
$easy->options = $options;
|
41 |
+
$conf = $this->getDefaultConf($easy);
|
42 |
+
$this->applyMethod($easy, $conf);
|
43 |
+
$this->applyHandlerOptions($easy, $conf);
|
44 |
+
$this->applyHeaders($easy, $conf);
|
45 |
+
unset($conf['_headers']);
|
46 |
+
|
47 |
+
// Add handler options from the request configuration options
|
48 |
+
if (isset($options['curl'])) {
|
49 |
+
$conf = array_replace($conf, $options['curl']);
|
50 |
+
}
|
51 |
+
|
52 |
+
$conf[CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy);
|
53 |
+
$easy->handle = $this->handles
|
54 |
+
? array_pop($this->handles)
|
55 |
+
: curl_init();
|
56 |
+
curl_setopt_array($easy->handle, $conf);
|
57 |
+
|
58 |
+
return $easy;
|
59 |
+
}
|
60 |
+
|
61 |
+
public function release(EasyHandle $easy)
|
62 |
+
{
|
63 |
+
$resource = $easy->handle;
|
64 |
+
unset($easy->handle);
|
65 |
+
|
66 |
+
if (count($this->handles) >= $this->maxHandles) {
|
67 |
+
curl_close($resource);
|
68 |
+
} else {
|
69 |
+
// Remove all callback functions as they can hold onto references
|
70 |
+
// and are not cleaned up by curl_reset. Using curl_setopt_array
|
71 |
+
// does not work for some reason, so removing each one
|
72 |
+
// individually.
|
73 |
+
curl_setopt($resource, CURLOPT_HEADERFUNCTION, null);
|
74 |
+
curl_setopt($resource, CURLOPT_READFUNCTION, null);
|
75 |
+
curl_setopt($resource, CURLOPT_WRITEFUNCTION, null);
|
76 |
+
curl_setopt($resource, CURLOPT_PROGRESSFUNCTION, null);
|
77 |
+
curl_reset($resource);
|
78 |
+
$this->handles[] = $resource;
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Completes a cURL transaction, either returning a response promise or a
|
84 |
+
* rejected promise.
|
85 |
+
*
|
86 |
+
* @param callable $handler
|
87 |
+
* @param EasyHandle $easy
|
88 |
+
* @param CurlFactoryInterface $factory Dictates how the handle is released
|
89 |
+
*
|
90 |
+
* @return \GuzzleHttp\Promise\PromiseInterface
|
91 |
+
*/
|
92 |
+
public static function finish(
|
93 |
+
callable $handler,
|
94 |
+
EasyHandle $easy,
|
95 |
+
CurlFactoryInterface $factory
|
96 |
+
) {
|
97 |
+
if (isset($easy->options['on_stats'])) {
|
98 |
+
self::invokeStats($easy);
|
99 |
+
}
|
100 |
+
|
101 |
+
if (!$easy->response || $easy->errno) {
|
102 |
+
return self::finishError($handler, $easy, $factory);
|
103 |
+
}
|
104 |
+
|
105 |
+
// Return the response if it is present and there is no error.
|
106 |
+
$factory->release($easy);
|
107 |
+
|
108 |
+
// Rewind the body of the response if possible.
|
109 |
+
$body = $easy->response->getBody();
|
110 |
+
if ($body->isSeekable()) {
|
111 |
+
$body->rewind();
|
112 |
+
}
|
113 |
+
|
114 |
+
return new FulfilledPromise($easy->response);
|
115 |
+
}
|
116 |
+
|
117 |
+
private static function invokeStats(EasyHandle $easy)
|
118 |
+
{
|
119 |
+
$curlStats = curl_getinfo($easy->handle);
|
120 |
+
$stats = new TransferStats(
|
121 |
+
$easy->request,
|
122 |
+
$easy->response,
|
123 |
+
$curlStats['total_time'],
|
124 |
+
$easy->errno,
|
125 |
+
$curlStats
|
126 |
+
);
|
127 |
+
call_user_func($easy->options['on_stats'], $stats);
|
128 |
+
}
|
129 |
+
|
130 |
+
private static function finishError(
|
131 |
+
callable $handler,
|
132 |
+
EasyHandle $easy,
|
133 |
+
CurlFactoryInterface $factory
|
134 |
+
) {
|
135 |
+
// Get error information and release the handle to the factory.
|
136 |
+
$ctx = [
|
137 |
+
'errno' => $easy->errno,
|
138 |
+
'error' => curl_error($easy->handle),
|
139 |
+
] + curl_getinfo($easy->handle);
|
140 |
+
$factory->release($easy);
|
141 |
+
|
142 |
+
// Retry when nothing is present or when curl failed to rewind.
|
143 |
+
if (empty($easy->options['_err_message'])
|
144 |
+
&& (!$easy->errno || $easy->errno == 65)
|
145 |
+
) {
|
146 |
+
return self::retryFailedRewind($handler, $easy, $ctx);
|
147 |
+
}
|
148 |
+
|
149 |
+
return self::createRejection($easy, $ctx);
|
150 |
+
}
|
151 |
+
|
152 |
+
private static function createRejection(EasyHandle $easy, array $ctx)
|
153 |
+
{
|
154 |
+
static $connectionErrors = [
|
155 |
+
CURLE_OPERATION_TIMEOUTED => true,
|
156 |
+
CURLE_COULDNT_RESOLVE_HOST => true,
|
157 |
+
CURLE_COULDNT_CONNECT => true,
|
158 |
+
CURLE_SSL_CONNECT_ERROR => true,
|
159 |
+
CURLE_GOT_NOTHING => true,
|
160 |
+
];
|
161 |
+
|
162 |
+
// If an exception was encountered during the onHeaders event, then
|
163 |
+
// return a rejected promise that wraps that exception.
|
164 |
+
if ($easy->onHeadersException) {
|
165 |
+
return \GuzzleHttp\Promise\rejection_for(
|
166 |
+
new RequestException(
|
167 |
+
'An error was encountered during the on_headers event',
|
168 |
+
$easy->request,
|
169 |
+
$easy->response,
|
170 |
+
$easy->onHeadersException,
|
171 |
+
$ctx
|
172 |
+
)
|
173 |
+
);
|
174 |
+
}
|
175 |
+
|
176 |
+
$message = sprintf(
|
177 |
+
'cURL error %s: %s (%s)',
|
178 |
+
$ctx['errno'],
|
179 |
+
$ctx['error'],
|
180 |
+
'see http://curl.haxx.se/libcurl/c/libcurl-errors.html'
|
181 |
+
);
|
182 |
+
|
183 |
+
// Create a connection exception if it was a specific error code.
|
184 |
+
$error = isset($connectionErrors[$easy->errno])
|
185 |
+
? new ConnectException($message, $easy->request, null, $ctx)
|
186 |
+
: new RequestException($message, $easy->request, $easy->response, null, $ctx);
|
187 |
+
|
188 |
+
return \GuzzleHttp\Promise\rejection_for($error);
|
189 |
+
}
|
190 |
+
|
191 |
+
private function getDefaultConf(EasyHandle $easy)
|
192 |
+
{
|
193 |
+
$conf = [
|
194 |
+
'_headers' => $easy->request->getHeaders(),
|
195 |
+
CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(),
|
196 |
+
CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''),
|
197 |
+
CURLOPT_RETURNTRANSFER => false,
|
198 |
+
CURLOPT_HEADER => false,
|
199 |
+
CURLOPT_CONNECTTIMEOUT => 150,
|
200 |
+
];
|
201 |
+
|
202 |
+
if (defined('CURLOPT_PROTOCOLS')) {
|
203 |
+
$conf[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
|
204 |
+
}
|
205 |
+
|
206 |
+
$version = $easy->request->getProtocolVersion();
|
207 |
+
if ($version == 1.1) {
|
208 |
+
$conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1;
|
209 |
+
} elseif ($version == 2.0) {
|
210 |
+
$conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0;
|
211 |
+
} else {
|
212 |
+
$conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
|
213 |
+
}
|
214 |
+
|
215 |
+
return $conf;
|
216 |
+
}
|
217 |
+
|
218 |
+
private function applyMethod(EasyHandle $easy, array &$conf)
|
219 |
+
{
|
220 |
+
$body = $easy->request->getBody();
|
221 |
+
$size = $body->getSize();
|
222 |
+
|
223 |
+
if ($size === null || $size > 0) {
|
224 |
+
$this->applyBody($easy->request, $easy->options, $conf);
|
225 |
+
return;
|
226 |
+
}
|
227 |
+
|
228 |
+
$method = $easy->request->getMethod();
|
229 |
+
if ($method === 'PUT' || $method === 'POST') {
|
230 |
+
// See http://tools.ietf.org/html/rfc7230#section-3.3.2
|
231 |
+
if (!$easy->request->hasHeader('Content-Length')) {
|
232 |
+
$conf[CURLOPT_HTTPHEADER][] = 'Content-Length: 0';
|
233 |
+
}
|
234 |
+
} elseif ($method === 'HEAD') {
|
235 |
+
$conf[CURLOPT_NOBODY] = true;
|
236 |
+
unset(
|
237 |
+
$conf[CURLOPT_WRITEFUNCTION],
|
238 |
+
$conf[CURLOPT_READFUNCTION],
|
239 |
+
$conf[CURLOPT_FILE],
|
240 |
+
$conf[CURLOPT_INFILE]
|
241 |
+
);
|
242 |
+
}
|
243 |
+
}
|
244 |
+
|
245 |
+
private function applyBody(RequestInterface $request, array $options, array &$conf)
|
246 |
+
{
|
247 |
+
$size = $request->hasHeader('Content-Length')
|
248 |
+
? (int) $request->getHeaderLine('Content-Length')
|
249 |
+
: null;
|
250 |
+
|
251 |
+
// Send the body as a string if the size is less than 1MB OR if the
|
252 |
+
// [curl][body_as_string] request value is set.
|
253 |
+
if (($size !== null && $size < 1000000) ||
|
254 |
+
!empty($options['_body_as_string'])
|
255 |
+
) {
|
256 |
+
$conf[CURLOPT_POSTFIELDS] = (string) $request->getBody();
|
257 |
+
// Don't duplicate the Content-Length header
|
258 |
+
$this->removeHeader('Content-Length', $conf);
|
259 |
+
$this->removeHeader('Transfer-Encoding', $conf);
|
260 |
+
} else {
|
261 |
+
$conf[CURLOPT_UPLOAD] = true;
|
262 |
+
if ($size !== null) {
|
263 |
+
$conf[CURLOPT_INFILESIZE] = $size;
|
264 |
+
$this->removeHeader('Content-Length', $conf);
|
265 |
+
}
|
266 |
+
$body = $request->getBody();
|
267 |
+
if ($body->isSeekable()) {
|
268 |
+
$body->rewind();
|
269 |
+
}
|
270 |
+
$conf[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) {
|
271 |
+
return $body->read($length);
|
272 |
+
};
|
273 |
+
}
|
274 |
+
|
275 |
+
// If the Expect header is not present, prevent curl from adding it
|
276 |
+
if (!$request->hasHeader('Expect')) {
|
277 |
+
$conf[CURLOPT_HTTPHEADER][] = 'Expect:';
|
278 |
+
}
|
279 |
+
|
280 |
+
// cURL sometimes adds a content-type by default. Prevent this.
|
281 |
+
if (!$request->hasHeader('Content-Type')) {
|
282 |
+
$conf[CURLOPT_HTTPHEADER][] = 'Content-Type:';
|
283 |
+
}
|
284 |
+
}
|
285 |
+
|
286 |
+
private function applyHeaders(EasyHandle $easy, array &$conf)
|
287 |
+
{
|
288 |
+
foreach ($conf['_headers'] as $name => $values) {
|
289 |
+
foreach ($values as $value) {
|
290 |
+
$value = (string) $value;
|
291 |
+
if ($value === '') {
|
292 |
+
// cURL requires a special format for empty headers.
|
293 |
+
// See https://github.com/guzzle/guzzle/issues/1882 for more details.
|
294 |
+
$conf[CURLOPT_HTTPHEADER][] = "$name;";
|
295 |
+
} else {
|
296 |
+
$conf[CURLOPT_HTTPHEADER][] = "$name: $value";
|
297 |
+
}
|
298 |
+
}
|
299 |
+
}
|
300 |
+
|
301 |
+
// Remove the Accept header if one was not set
|
302 |
+
if (!$easy->request->hasHeader('Accept')) {
|
303 |
+
$conf[CURLOPT_HTTPHEADER][] = 'Accept:';
|
304 |
+
}
|
305 |
+
}
|
306 |
+
|
307 |
+
/**
|
308 |
+
* Remove a header from the options array.
|
309 |
+
*
|
310 |
+
* @param string $name Case-insensitive header to remove
|
311 |
+
* @param array $options Array of options to modify
|
312 |
+
*/
|
313 |
+
private function removeHeader($name, array &$options)
|
314 |
+
{
|
315 |
+
foreach (array_keys($options['_headers']) as $key) {
|
316 |
+
if (!strcasecmp($key, $name)) {
|
317 |
+
unset($options['_headers'][$key]);
|
318 |
+
return;
|
319 |
+
}
|
320 |
+
}
|
321 |
+
}
|
322 |
+
|
323 |
+
private function applyHandlerOptions(EasyHandle $easy, array &$conf)
|
324 |
+
{
|
325 |
+
$options = $easy->options;
|
326 |
+
if (isset($options['verify'])) {
|
327 |
+
if ($options['verify'] === false) {
|
328 |
+
unset($conf[CURLOPT_CAINFO]);
|
329 |
+
$conf[CURLOPT_SSL_VERIFYHOST] = 0;
|
330 |
+
$conf[CURLOPT_SSL_VERIFYPEER] = false;
|
331 |
+
} else {
|
332 |
+
$conf[CURLOPT_SSL_VERIFYHOST] = 2;
|
333 |
+
$conf[CURLOPT_SSL_VERIFYPEER] = true;
|
334 |
+
if (is_string($options['verify'])) {
|
335 |
+
// Throw an error if the file/folder/link path is not valid or doesn't exist.
|
336 |
+
if (!file_exists($options['verify'])) {
|
337 |
+
throw new \InvalidArgumentException(
|
338 |
+
"SSL CA bundle not found: {$options['verify']}"
|
339 |
+
);
|
340 |
+
}
|
341 |
+
// If it's a directory or a link to a directory use CURLOPT_CAPATH.
|
342 |
+
// If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO.
|
343 |
+
if (is_dir($options['verify']) ||
|
344 |
+
(is_link($options['verify']) && is_dir(readlink($options['verify'])))) {
|
345 |
+
$conf[CURLOPT_CAPATH] = $options['verify'];
|
346 |
+
} else {
|
347 |
+
$conf[CURLOPT_CAINFO] = $options['verify'];
|
348 |
+
}
|
349 |
+
}
|
350 |
+
}
|
351 |
+
}
|
352 |
+
|
353 |
+
if (!empty($options['decode_content'])) {
|
354 |
+
$accept = $easy->request->getHeaderLine('Accept-Encoding');
|
355 |
+
if ($accept) {
|
356 |
+
$conf[CURLOPT_ENCODING] = $accept;
|
357 |
+
} else {
|
358 |
+
$conf[CURLOPT_ENCODING] = '';
|
359 |
+
// Don't let curl send the header over the wire
|
360 |
+
$conf[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:';
|
361 |
+
}
|
362 |
+
}
|
363 |
+
|
364 |
+
if (isset($options['sink'])) {
|
365 |
+
$sink = $options['sink'];
|
366 |
+
if (!is_string($sink)) {
|
367 |
+
$sink = \GuzzleHttp\Psr7\stream_for($sink);
|
368 |
+
} elseif (!is_dir(dirname($sink))) {
|
369 |
+
// Ensure that the directory exists before failing in curl.
|
370 |
+
throw new \RuntimeException(sprintf(
|
371 |
+
'Directory %s does not exist for sink value of %s',
|
372 |
+
dirname($sink),
|
373 |
+
$sink
|
374 |
+
));
|
375 |
+
} else {
|
376 |
+
$sink = new LazyOpenStream($sink, 'w+');
|
377 |
+
}
|
378 |
+
$easy->sink = $sink;
|
379 |
+
$conf[CURLOPT_WRITEFUNCTION] = function ($ch, $write) use ($sink) {
|
380 |
+
return $sink->write($write);
|
381 |
+
};
|
382 |
+
} else {
|
383 |
+
// Use a default temp stream if no sink was set.
|
384 |
+
$conf[CURLOPT_FILE] = fopen('php://temp', 'w+');
|
385 |
+
$easy->sink = Psr7\stream_for($conf[CURLOPT_FILE]);
|
386 |
+
}
|
387 |
+
$timeoutRequiresNoSignal = false;
|
388 |
+
if (isset($options['timeout'])) {
|
389 |
+
$timeoutRequiresNoSignal |= $options['timeout'] < 1;
|
390 |
+
$conf[CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000;
|
391 |
+
}
|
392 |
+
|
393 |
+
// CURL default value is CURL_IPRESOLVE_WHATEVER
|
394 |
+
if (isset($options['force_ip_resolve'])) {
|
395 |
+
if ('v4' === $options['force_ip_resolve']) {
|
396 |
+
$conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
|
397 |
+
} elseif ('v6' === $options['force_ip_resolve']) {
|
398 |
+
$conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V6;
|
399 |
+
}
|
400 |
+
}
|
401 |
+
|
402 |
+
if (isset($options['connect_timeout'])) {
|
403 |
+
$timeoutRequiresNoSignal |= $options['connect_timeout'] < 1;
|
404 |
+
$conf[CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000;
|
405 |
+
}
|
406 |
+
|
407 |
+
if ($timeoutRequiresNoSignal && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
|
408 |
+
$conf[CURLOPT_NOSIGNAL] = true;
|
409 |
+
}
|
410 |
+
|
411 |
+
if (isset($options['proxy'])) {
|
412 |
+
if (!is_array($options['proxy'])) {
|
413 |
+
$conf[CURLOPT_PROXY] = $options['proxy'];
|
414 |
+
} else {
|
415 |
+
$scheme = $easy->request->getUri()->getScheme();
|
416 |
+
if (isset($options['proxy'][$scheme])) {
|
417 |
+
$host = $easy->request->getUri()->getHost();
|
418 |
+
if (!isset($options['proxy']['no']) ||
|
419 |
+
!\GuzzleHttp\is_host_in_noproxy($host, $options['proxy']['no'])
|
420 |
+
) {
|
421 |
+
$conf[CURLOPT_PROXY] = $options['proxy'][$scheme];
|
422 |
+
}
|
423 |
+
}
|
424 |
+
}
|
425 |
+
}
|
426 |
+
|
427 |
+
if (isset($options['cert'])) {
|
428 |
+
$cert = $options['cert'];
|
429 |
+
if (is_array($cert)) {
|
430 |
+
$conf[CURLOPT_SSLCERTPASSWD] = $cert[1];
|
431 |
+
$cert = $cert[0];
|
432 |
+
}
|
433 |
+
if (!file_exists($cert)) {
|
434 |
+
throw new \InvalidArgumentException(
|
435 |
+
"SSL certificate not found: {$cert}"
|
436 |
+
);
|
437 |
+
}
|
438 |
+
$conf[CURLOPT_SSLCERT] = $cert;
|
439 |
+
}
|
440 |
+
|
441 |
+
if (isset($options['ssl_key'])) {
|
442 |
+
$sslKey = $options['ssl_key'];
|
443 |
+
if (is_array($sslKey)) {
|
444 |
+
$conf[CURLOPT_SSLKEYPASSWD] = $sslKey[1];
|
445 |
+
$sslKey = $sslKey[0];
|
446 |
+
}
|
447 |
+
if (!file_exists($sslKey)) {
|
448 |
+
throw new \InvalidArgumentException(
|
449 |
+
"SSL private key not found: {$sslKey}"
|
450 |
+
);
|
451 |
+
}
|
452 |
+
$conf[CURLOPT_SSLKEY] = $sslKey;
|
453 |
+
}
|
454 |
+
|
455 |
+
if (isset($options['progress'])) {
|
456 |
+
$progress = $options['progress'];
|
457 |
+
if (!is_callable($progress)) {
|
458 |
+
throw new \InvalidArgumentException(
|
459 |
+
'progress client option must be callable'
|
460 |
+
);
|
461 |
+
}
|
462 |
+
$conf[CURLOPT_NOPROGRESS] = false;
|
463 |
+
$conf[CURLOPT_PROGRESSFUNCTION] = function () use ($progress) {
|
464 |
+
$args = func_get_args();
|
465 |
+
// PHP 5.5 pushed the handle onto the start of the args
|
466 |
+
if (is_resource($args[0])) {
|
467 |
+
array_shift($args);
|
468 |
+
}
|
469 |
+
call_user_func_array($progress, $args);
|
470 |
+
};
|
471 |
+
}
|
472 |
+
|
473 |
+
if (!empty($options['debug'])) {
|
474 |
+
$conf[CURLOPT_STDERR] = \GuzzleHttp\debug_resource($options['debug']);
|
475 |
+
$conf[CURLOPT_VERBOSE] = true;
|
476 |
+
}
|
477 |
+
}
|
478 |
+
|
479 |
+
/**
|
480 |
+
* This function ensures that a response was set on a transaction. If one
|
481 |
+
* was not set, then the request is retried if possible. This error
|
482 |
+
* typically means you are sending a payload, curl encountered a
|
483 |
+
* "Connection died, retrying a fresh connect" error, tried to rewind the
|
484 |
+
* stream, and then encountered a "necessary data rewind wasn't possible"
|
485 |
+
* error, causing the request to be sent through curl_multi_info_read()
|
486 |
+
* without an error status.
|
487 |
+
*/
|
488 |
+
private static function retryFailedRewind(
|
489 |
+
callable $handler,
|
490 |
+
EasyHandle $easy,
|
491 |
+
array $ctx
|
492 |
+
) {
|
493 |
+
try {
|
494 |
+
// Only rewind if the body has been read from.
|
495 |
+
$body = $easy->request->getBody();
|
496 |
+
if ($body->tell() > 0) {
|
497 |
+
$body->rewind();
|
498 |
+
}
|
499 |
+
} catch (\RuntimeException $e) {
|
500 |
+
$ctx['error'] = 'The connection unexpectedly failed without '
|
501 |
+
. 'providing an error. The request would have been retried, '
|
502 |
+
. 'but attempting to rewind the request body failed. '
|
503 |
+
. 'Exception: ' . $e;
|
504 |
+
return self::createRejection($easy, $ctx);
|
505 |
+
}
|
506 |
+
|
507 |
+
// Retry no more than 3 times before giving up.
|
508 |
+
if (!isset($easy->options['_curl_retries'])) {
|
509 |
+
$easy->options['_curl_retries'] = 1;
|
510 |
+
} elseif ($easy->options['_curl_retries'] == 2) {
|
511 |
+
$ctx['error'] = 'The cURL request was retried 3 times '
|
512 |
+
. 'and did not succeed. The most likely reason for the failure '
|
513 |
+
. 'is that cURL was unable to rewind the body of the request '
|
514 |
+
. 'and subsequent retries resulted in the same error. Turn on '
|
515 |
+
. 'the debug option to see what went wrong. See '
|
516 |
+
. 'https://bugs.php.net/bug.php?id=47204 for more information.';
|
517 |
+
return self::createRejection($easy, $ctx);
|
518 |
+
} else {
|
519 |
+
$easy->options['_curl_retries']++;
|
520 |
+
}
|
521 |
+
|
522 |
+
return $handler($easy->request, $easy->options);
|
523 |
+
}
|
524 |
+
|
525 |
+
private function createHeaderFn(EasyHandle $easy)
|
526 |
+
{
|
527 |
+
if (isset($easy->options['on_headers'])) {
|
528 |
+
$onHeaders = $easy->options['on_headers'];
|
529 |
+
|
530 |
+
if (!is_callable($onHeaders)) {
|
531 |
+
throw new \InvalidArgumentException('on_headers must be callable');
|
532 |
+
}
|
533 |
+
} else {
|
534 |
+
$onHeaders = null;
|
535 |
+
}
|
536 |
+
|
537 |
+
return function ($ch, $h) use (
|
538 |
+
$onHeaders,
|
539 |
+
$easy,
|
540 |
+
&$startingResponse
|
541 |
+
) {
|
542 |
+
$value = trim($h);
|
543 |
+
if ($value === '') {
|
544 |
+
$startingResponse = true;
|
545 |
+
$easy->createResponse();
|
546 |
+
if ($onHeaders !== null) {
|
547 |
+
try {
|
548 |
+
$onHeaders($easy->response);
|
549 |
+
} catch (\Exception $e) {
|
550 |
+
// Associate the exception with the handle and trigger
|
551 |
+
// a curl header write error by returning 0.
|
552 |
+
$easy->onHeadersException = $e;
|
553 |
+
return -1;
|
554 |
+
}
|
555 |
+
}
|
556 |
+
} elseif ($startingResponse) {
|
557 |
+
$startingResponse = false;
|
558 |
+
$easy->headers = [$value];
|
559 |
+
} else {
|
560 |
+
$easy->headers[] = $value;
|
561 |
+
}
|
562 |
+
return strlen($h);
|
563 |
+
};
|
564 |
+
}
|
565 |
+
}
|
vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php
CHANGED
@@ -1,27 +1,27 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Handler;
|
3 |
-
|
4 |
-
use Psr\Http\Message\RequestInterface;
|
5 |
-
|
6 |
-
interface CurlFactoryInterface
|
7 |
-
{
|
8 |
-
/**
|
9 |
-
* Creates a cURL handle resource.
|
10 |
-
*
|
11 |
-
* @param RequestInterface $request Request
|
12 |
-
* @param array $options Transfer options
|
13 |
-
*
|
14 |
-
* @return EasyHandle
|
15 |
-
* @throws \RuntimeException when an option cannot be applied
|
16 |
-
*/
|
17 |
-
public function create(RequestInterface $request, array $options);
|
18 |
-
|
19 |
-
/**
|
20 |
-
* Release an easy handle, allowing it to be reused or closed.
|
21 |
-
*
|
22 |
-
* This function must call unset on the easy handle's "handle" property.
|
23 |
-
*
|
24 |
-
* @param EasyHandle $easy
|
25 |
-
*/
|
26 |
-
public function release(EasyHandle $easy);
|
27 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Handler;
|
3 |
+
|
4 |
+
use Psr\Http\Message\RequestInterface;
|
5 |
+
|
6 |
+
interface CurlFactoryInterface
|
7 |
+
{
|
8 |
+
/**
|
9 |
+
* Creates a cURL handle resource.
|
10 |
+
*
|
11 |
+
* @param RequestInterface $request Request
|
12 |
+
* @param array $options Transfer options
|
13 |
+
*
|
14 |
+
* @return EasyHandle
|
15 |
+
* @throws \RuntimeException when an option cannot be applied
|
16 |
+
*/
|
17 |
+
public function create(RequestInterface $request, array $options);
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Release an easy handle, allowing it to be reused or closed.
|
21 |
+
*
|
22 |
+
* This function must call unset on the easy handle's "handle" property.
|
23 |
+
*
|
24 |
+
* @param EasyHandle $easy
|
25 |
+
*/
|
26 |
+
public function release(EasyHandle $easy);
|
27 |
+
}
|
vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php
CHANGED
@@ -1,45 +1,45 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Handler;
|
3 |
-
|
4 |
-
use GuzzleHttp\Psr7;
|
5 |
-
use Psr\Http\Message\RequestInterface;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* HTTP handler that uses cURL easy handles as a transport layer.
|
9 |
-
*
|
10 |
-
* When using the CurlHandler, custom curl options can be specified as an
|
11 |
-
* associative array of curl option constants mapping to values in the
|
12 |
-
* **curl** key of the "client" key of the request.
|
13 |
-
*/
|
14 |
-
class CurlHandler
|
15 |
-
{
|
16 |
-
/** @var CurlFactoryInterface */
|
17 |
-
private $factory;
|
18 |
-
|
19 |
-
/**
|
20 |
-
* Accepts an associative array of options:
|
21 |
-
*
|
22 |
-
* - factory: Optional curl factory used to create cURL handles.
|
23 |
-
*
|
24 |
-
* @param array $options Array of options to use with the handler
|
25 |
-
*/
|
26 |
-
public function __construct(array $options = [])
|
27 |
-
{
|
28 |
-
$this->factory = isset($options['handle_factory'])
|
29 |
-
? $options['handle_factory']
|
30 |
-
: new CurlFactory(3);
|
31 |
-
}
|
32 |
-
|
33 |
-
public function __invoke(RequestInterface $request, array $options)
|
34 |
-
{
|
35 |
-
if (isset($options['delay'])) {
|
36 |
-
usleep($options['delay'] * 1000);
|
37 |
-
}
|
38 |
-
|
39 |
-
$easy = $this->factory->create($request, $options);
|
40 |
-
curl_exec($easy->handle);
|
41 |
-
$easy->errno = curl_errno($easy->handle);
|
42 |
-
|
43 |
-
return CurlFactory::finish($this, $easy, $this->factory);
|
44 |
-
}
|
45 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Handler;
|
3 |
+
|
4 |
+
use GuzzleHttp\Psr7;
|
5 |
+
use Psr\Http\Message\RequestInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* HTTP handler that uses cURL easy handles as a transport layer.
|
9 |
+
*
|
10 |
+
* When using the CurlHandler, custom curl options can be specified as an
|
11 |
+
* associative array of curl option constants mapping to values in the
|
12 |
+
* **curl** key of the "client" key of the request.
|
13 |
+
*/
|
14 |
+
class CurlHandler
|
15 |
+
{
|
16 |
+
/** @var CurlFactoryInterface */
|
17 |
+
private $factory;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Accepts an associative array of options:
|
21 |
+
*
|
22 |
+
* - factory: Optional curl factory used to create cURL handles.
|
23 |
+
*
|
24 |
+
* @param array $options Array of options to use with the handler
|
25 |
+
*/
|
26 |
+
public function __construct(array $options = [])
|
27 |
+
{
|
28 |
+
$this->factory = isset($options['handle_factory'])
|
29 |
+
? $options['handle_factory']
|
30 |
+
: new CurlFactory(3);
|
31 |
+
}
|
32 |
+
|
33 |
+
public function __invoke(RequestInterface $request, array $options)
|
34 |
+
{
|
35 |
+
if (isset($options['delay'])) {
|
36 |
+
usleep($options['delay'] * 1000);
|
37 |
+
}
|
38 |
+
|
39 |
+
$easy = $this->factory->create($request, $options);
|
40 |
+
curl_exec($easy->handle);
|
41 |
+
$easy->errno = curl_errno($easy->handle);
|
42 |
+
|
43 |
+
return CurlFactory::finish($this, $easy, $this->factory);
|
44 |
+
}
|
45 |
+
}
|
vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php
CHANGED
@@ -1,199 +1,199 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Handler;
|
3 |
-
|
4 |
-
use GuzzleHttp\Promise as P;
|
5 |
-
use GuzzleHttp\Promise\Promise;
|
6 |
-
use GuzzleHttp\Psr7;
|
7 |
-
use Psr\Http\Message\RequestInterface;
|
8 |
-
|
9 |
-
/**
|
10 |
-
* Returns an asynchronous response using curl_multi_* functions.
|
11 |
-
*
|
12 |
-
* When using the CurlMultiHandler, custom curl options can be specified as an
|
13 |
-
* associative array of curl option constants mapping to values in the
|
14 |
-
* **curl** key of the provided request options.
|
15 |
-
*
|
16 |
-
* @property resource $_mh Internal use only. Lazy loaded multi-handle.
|
17 |
-
*/
|
18 |
-
class CurlMultiHandler
|
19 |
-
{
|
20 |
-
/** @var CurlFactoryInterface */
|
21 |
-
private $factory;
|
22 |
-
private $selectTimeout;
|
23 |
-
private $active;
|
24 |
-
private $handles = [];
|
25 |
-
private $delays = [];
|
26 |
-
|
27 |
-
/**
|
28 |
-
* This handler accepts the following options:
|
29 |
-
*
|
30 |
-
* - handle_factory: An optional factory used to create curl handles
|
31 |
-
* - select_timeout: Optional timeout (in seconds) to block before timing
|
32 |
-
* out while selecting curl handles. Defaults to 1 second.
|
33 |
-
*
|
34 |
-
* @param array $options
|
35 |
-
*/
|
36 |
-
public function __construct(array $options = [])
|
37 |
-
{
|
38 |
-
$this->factory = isset($options['handle_factory'])
|
39 |
-
? $options['handle_factory'] : new CurlFactory(50);
|
40 |
-
$this->selectTimeout = isset($options['select_timeout'])
|
41 |
-
? $options['select_timeout'] : 1;
|
42 |
-
}
|
43 |
-
|
44 |
-
public function __get($name)
|
45 |
-
{
|
46 |
-
if ($name === '_mh') {
|
47 |
-
return $this->_mh = curl_multi_init();
|
48 |
-
}
|
49 |
-
|
50 |
-
throw new \BadMethodCallException();
|
51 |
-
}
|
52 |
-
|
53 |
-
public function __destruct()
|
54 |
-
{
|
55 |
-
if (isset($this->_mh)) {
|
56 |
-
curl_multi_close($this->_mh);
|
57 |
-
unset($this->_mh);
|
58 |
-
}
|
59 |
-
}
|
60 |
-
|
61 |
-
public function __invoke(RequestInterface $request, array $options)
|
62 |
-
{
|
63 |
-
$easy = $this->factory->create($request, $options);
|
64 |
-
$id = (int) $easy->handle;
|
65 |
-
|
66 |
-
$promise = new Promise(
|
67 |
-
[$this, 'execute'],
|
68 |
-
function () use ($id) {
|
69 |
-
return $this->cancel($id);
|
70 |
-
}
|
71 |
-
);
|
72 |
-
|
73 |
-
$this->addRequest(['easy' => $easy, 'deferred' => $promise]);
|
74 |
-
|
75 |
-
return $promise;
|
76 |
-
}
|
77 |
-
|
78 |
-
/**
|
79 |
-
* Ticks the curl event loop.
|
80 |
-
*/
|
81 |
-
public function tick()
|
82 |
-
{
|
83 |
-
// Add any delayed handles if needed.
|
84 |
-
if ($this->delays) {
|
85 |
-
$currentTime = microtime(true);
|
86 |
-
foreach ($this->delays as $id => $delay) {
|
87 |
-
if ($currentTime >= $delay) {
|
88 |
-
unset($this->delays[$id]);
|
89 |
-
curl_multi_add_handle(
|
90 |
-
$this->_mh,
|
91 |
-
$this->handles[$id]['easy']->handle
|
92 |
-
);
|
93 |
-
}
|
94 |
-
}
|
95 |
-
}
|
96 |
-
|
97 |
-
// Step through the task queue which may add additional requests.
|
98 |
-
P\queue()->run();
|
99 |
-
|
100 |
-
if ($this->active &&
|
101 |
-
curl_multi_select($this->_mh, $this->selectTimeout) === -1
|
102 |
-
) {
|
103 |
-
// Perform a usleep if a select returns -1.
|
104 |
-
// See: https://bugs.php.net/bug.php?id=61141
|
105 |
-
usleep(250);
|
106 |
-
}
|
107 |
-
|
108 |
-
while (curl_multi_exec($this->_mh, $this->active) === CURLM_CALL_MULTI_PERFORM);
|
109 |
-
|
110 |
-
$this->processMessages();
|
111 |
-
}
|
112 |
-
|
113 |
-
/**
|
114 |
-
* Runs until all outstanding connections have completed.
|
115 |
-
*/
|
116 |
-
public function execute()
|
117 |
-
{
|
118 |
-
$queue = P\queue();
|
119 |
-
|
120 |
-
while ($this->handles || !$queue->isEmpty()) {
|
121 |
-
// If there are no transfers, then sleep for the next delay
|
122 |
-
if (!$this->active && $this->delays) {
|
123 |
-
usleep($this->timeToNext());
|
124 |
-
}
|
125 |
-
$this->tick();
|
126 |
-
}
|
127 |
-
}
|
128 |
-
|
129 |
-
private function addRequest(array $entry)
|
130 |
-
{
|
131 |
-
$easy = $entry['easy'];
|
132 |
-
$id = (int) $easy->handle;
|
133 |
-
$this->handles[$id] = $entry;
|
134 |
-
if (empty($easy->options['delay'])) {
|
135 |
-
curl_multi_add_handle($this->_mh, $easy->handle);
|
136 |
-
} else {
|
137 |
-
$this->delays[$id] = microtime(true) + ($easy->options['delay'] / 1000);
|
138 |
-
}
|
139 |
-
}
|
140 |
-
|
141 |
-
/**
|
142 |
-
* Cancels a handle from sending and removes references to it.
|
143 |
-
*
|
144 |
-
* @param int $id Handle ID to cancel and remove.
|
145 |
-
*
|
146 |
-
* @return bool True on success, false on failure.
|
147 |
-
*/
|
148 |
-
private function cancel($id)
|
149 |
-
{
|
150 |
-
// Cannot cancel if it has been processed.
|
151 |
-
if (!isset($this->handles[$id])) {
|
152 |
-
return false;
|
153 |
-
}
|
154 |
-
|
155 |
-
$handle = $this->handles[$id]['easy']->handle;
|
156 |
-
unset($this->delays[$id], $this->handles[$id]);
|
157 |
-
curl_multi_remove_handle($this->_mh, $handle);
|
158 |
-
curl_close($handle);
|
159 |
-
|
160 |
-
return true;
|
161 |
-
}
|
162 |
-
|
163 |
-
private function processMessages()
|
164 |
-
{
|
165 |
-
while ($done = curl_multi_info_read($this->_mh)) {
|
166 |
-
$id = (int) $done['handle'];
|
167 |
-
curl_multi_remove_handle($this->_mh, $done['handle']);
|
168 |
-
|
169 |
-
if (!isset($this->handles[$id])) {
|
170 |
-
// Probably was cancelled.
|
171 |
-
continue;
|
172 |
-
}
|
173 |
-
|
174 |
-
$entry = $this->handles[$id];
|
175 |
-
unset($this->handles[$id], $this->delays[$id]);
|
176 |
-
$entry['easy']->errno = $done['result'];
|
177 |
-
$entry['deferred']->resolve(
|
178 |
-
CurlFactory::finish(
|
179 |
-
$this,
|
180 |
-
$entry['easy'],
|
181 |
-
$this->factory
|
182 |
-
)
|
183 |
-
);
|
184 |
-
}
|
185 |
-
}
|
186 |
-
|
187 |
-
private function timeToNext()
|
188 |
-
{
|
189 |
-
$currentTime = microtime(true);
|
190 |
-
$nextTime = PHP_INT_MAX;
|
191 |
-
foreach ($this->delays as $time) {
|
192 |
-
if ($time < $nextTime) {
|
193 |
-
$nextTime = $time;
|
194 |
-
}
|
195 |
-
}
|
196 |
-
|
197 |
-
return max(0, $nextTime - $currentTime) * 1000000;
|
198 |
-
}
|
199 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Handler;
|
3 |
+
|
4 |
+
use GuzzleHttp\Promise as P;
|
5 |
+
use GuzzleHttp\Promise\Promise;
|
6 |
+
use GuzzleHttp\Psr7;
|
7 |
+
use Psr\Http\Message\RequestInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Returns an asynchronous response using curl_multi_* functions.
|
11 |
+
*
|
12 |
+
* When using the CurlMultiHandler, custom curl options can be specified as an
|
13 |
+
* associative array of curl option constants mapping to values in the
|
14 |
+
* **curl** key of the provided request options.
|
15 |
+
*
|
16 |
+
* @property resource $_mh Internal use only. Lazy loaded multi-handle.
|
17 |
+
*/
|
18 |
+
class CurlMultiHandler
|
19 |
+
{
|
20 |
+
/** @var CurlFactoryInterface */
|
21 |
+
private $factory;
|
22 |
+
private $selectTimeout;
|
23 |
+
private $active;
|
24 |
+
private $handles = [];
|
25 |
+
private $delays = [];
|
26 |
+
|
27 |
+
/**
|
28 |
+
* This handler accepts the following options:
|
29 |
+
*
|
30 |
+
* - handle_factory: An optional factory used to create curl handles
|
31 |
+
* - select_timeout: Optional timeout (in seconds) to block before timing
|
32 |
+
* out while selecting curl handles. Defaults to 1 second.
|
33 |
+
*
|
34 |
+
* @param array $options
|
35 |
+
*/
|
36 |
+
public function __construct(array $options = [])
|
37 |
+
{
|
38 |
+
$this->factory = isset($options['handle_factory'])
|
39 |
+
? $options['handle_factory'] : new CurlFactory(50);
|
40 |
+
$this->selectTimeout = isset($options['select_timeout'])
|
41 |
+
? $options['select_timeout'] : 1;
|
42 |
+
}
|
43 |
+
|
44 |
+
public function __get($name)
|
45 |
+
{
|
46 |
+
if ($name === '_mh') {
|
47 |
+
return $this->_mh = curl_multi_init();
|
48 |
+
}
|
49 |
+
|
50 |
+
throw new \BadMethodCallException();
|
51 |
+
}
|
52 |
+
|
53 |
+
public function __destruct()
|
54 |
+
{
|
55 |
+
if (isset($this->_mh)) {
|
56 |
+
curl_multi_close($this->_mh);
|
57 |
+
unset($this->_mh);
|
58 |
+
}
|
59 |
+
}
|
60 |
+
|
61 |
+
public function __invoke(RequestInterface $request, array $options)
|
62 |
+
{
|
63 |
+
$easy = $this->factory->create($request, $options);
|
64 |
+
$id = (int) $easy->handle;
|
65 |
+
|
66 |
+
$promise = new Promise(
|
67 |
+
[$this, 'execute'],
|
68 |
+
function () use ($id) {
|
69 |
+
return $this->cancel($id);
|
70 |
+
}
|
71 |
+
);
|
72 |
+
|
73 |
+
$this->addRequest(['easy' => $easy, 'deferred' => $promise]);
|
74 |
+
|
75 |
+
return $promise;
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Ticks the curl event loop.
|
80 |
+
*/
|
81 |
+
public function tick()
|
82 |
+
{
|
83 |
+
// Add any delayed handles if needed.
|
84 |
+
if ($this->delays) {
|
85 |
+
$currentTime = microtime(true);
|
86 |
+
foreach ($this->delays as $id => $delay) {
|
87 |
+
if ($currentTime >= $delay) {
|
88 |
+
unset($this->delays[$id]);
|
89 |
+
curl_multi_add_handle(
|
90 |
+
$this->_mh,
|
91 |
+
$this->handles[$id]['easy']->handle
|
92 |
+
);
|
93 |
+
}
|
94 |
+
}
|
95 |
+
}
|
96 |
+
|
97 |
+
// Step through the task queue which may add additional requests.
|
98 |
+
P\queue()->run();
|
99 |
+
|
100 |
+
if ($this->active &&
|
101 |
+
curl_multi_select($this->_mh, $this->selectTimeout) === -1
|
102 |
+
) {
|
103 |
+
// Perform a usleep if a select returns -1.
|
104 |
+
// See: https://bugs.php.net/bug.php?id=61141
|
105 |
+
usleep(250);
|
106 |
+
}
|
107 |
+
|
108 |
+
while (curl_multi_exec($this->_mh, $this->active) === CURLM_CALL_MULTI_PERFORM);
|
109 |
+
|
110 |
+
$this->processMessages();
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Runs until all outstanding connections have completed.
|
115 |
+
*/
|
116 |
+
public function execute()
|
117 |
+
{
|
118 |
+
$queue = P\queue();
|
119 |
+
|
120 |
+
while ($this->handles || !$queue->isEmpty()) {
|
121 |
+
// If there are no transfers, then sleep for the next delay
|
122 |
+
if (!$this->active && $this->delays) {
|
123 |
+
usleep($this->timeToNext());
|
124 |
+
}
|
125 |
+
$this->tick();
|
126 |
+
}
|
127 |
+
}
|
128 |
+
|
129 |
+
private function addRequest(array $entry)
|
130 |
+
{
|
131 |
+
$easy = $entry['easy'];
|
132 |
+
$id = (int) $easy->handle;
|
133 |
+
$this->handles[$id] = $entry;
|
134 |
+
if (empty($easy->options['delay'])) {
|
135 |
+
curl_multi_add_handle($this->_mh, $easy->handle);
|
136 |
+
} else {
|
137 |
+
$this->delays[$id] = microtime(true) + ($easy->options['delay'] / 1000);
|
138 |
+
}
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Cancels a handle from sending and removes references to it.
|
143 |
+
*
|
144 |
+
* @param int $id Handle ID to cancel and remove.
|
145 |
+
*
|
146 |
+
* @return bool True on success, false on failure.
|
147 |
+
*/
|
148 |
+
private function cancel($id)
|
149 |
+
{
|
150 |
+
// Cannot cancel if it has been processed.
|
151 |
+
if (!isset($this->handles[$id])) {
|
152 |
+
return false;
|
153 |
+
}
|
154 |
+
|
155 |
+
$handle = $this->handles[$id]['easy']->handle;
|
156 |
+
unset($this->delays[$id], $this->handles[$id]);
|
157 |
+
curl_multi_remove_handle($this->_mh, $handle);
|
158 |
+
curl_close($handle);
|
159 |
+
|
160 |
+
return true;
|
161 |
+
}
|
162 |
+
|
163 |
+
private function processMessages()
|
164 |
+
{
|
165 |
+
while ($done = curl_multi_info_read($this->_mh)) {
|
166 |
+
$id = (int) $done['handle'];
|
167 |
+
curl_multi_remove_handle($this->_mh, $done['handle']);
|
168 |
+
|
169 |
+
if (!isset($this->handles[$id])) {
|
170 |
+
// Probably was cancelled.
|
171 |
+
continue;
|
172 |
+
}
|
173 |
+
|
174 |
+
$entry = $this->handles[$id];
|
175 |
+
unset($this->handles[$id], $this->delays[$id]);
|
176 |
+
$entry['easy']->errno = $done['result'];
|
177 |
+
$entry['deferred']->resolve(
|
178 |
+
CurlFactory::finish(
|
179 |
+
$this,
|
180 |
+
$entry['easy'],
|
181 |
+
$this->factory
|
182 |
+
)
|
183 |
+
);
|
184 |
+
}
|
185 |
+
}
|
186 |
+
|
187 |
+
private function timeToNext()
|
188 |
+
{
|
189 |
+
$currentTime = microtime(true);
|
190 |
+
$nextTime = PHP_INT_MAX;
|
191 |
+
foreach ($this->delays as $time) {
|
192 |
+
if ($time < $nextTime) {
|
193 |
+
$nextTime = $time;
|
194 |
+
}
|
195 |
+
}
|
196 |
+
|
197 |
+
return max(0, $nextTime - $currentTime) * 1000000;
|
198 |
+
}
|
199 |
+
}
|
vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php
CHANGED
@@ -1,92 +1,92 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Handler;
|
3 |
-
|
4 |
-
use GuzzleHttp\Psr7\Response;
|
5 |
-
use Psr\Http\Message\RequestInterface;
|
6 |
-
use Psr\Http\Message\ResponseInterface;
|
7 |
-
use Psr\Http\Message\StreamInterface;
|
8 |
-
|
9 |
-
/**
|
10 |
-
* Represents a cURL easy handle and the data it populates.
|
11 |
-
*
|
12 |
-
* @internal
|
13 |
-
*/
|
14 |
-
final class EasyHandle
|
15 |
-
{
|
16 |
-
/** @var resource cURL resource */
|
17 |
-
public $handle;
|
18 |
-
|
19 |
-
/** @var StreamInterface Where data is being written */
|
20 |
-
public $sink;
|
21 |
-
|
22 |
-
/** @var array Received HTTP headers so far */
|
23 |
-
public $headers = [];
|
24 |
-
|
25 |
-
/** @var ResponseInterface Received response (if any) */
|
26 |
-
public $response;
|
27 |
-
|
28 |
-
/** @var RequestInterface Request being sent */
|
29 |
-
public $request;
|
30 |
-
|
31 |
-
/** @var array Request options */
|
32 |
-
public $options = [];
|
33 |
-
|
34 |
-
/** @var int cURL error number (if any) */
|
35 |
-
public $errno = 0;
|
36 |
-
|
37 |
-
/** @var \Exception Exception during on_headers (if any) */
|
38 |
-
public $onHeadersException;
|
39 |
-
|
40 |
-
/**
|
41 |
-
* Attach a response to the easy handle based on the received headers.
|
42 |
-
*
|
43 |
-
* @throws \RuntimeException if no headers have been received.
|
44 |
-
*/
|
45 |
-
public function createResponse()
|
46 |
-
{
|
47 |
-
if (empty($this->headers)) {
|
48 |
-
throw new \RuntimeException('No headers have been received');
|
49 |
-
}
|
50 |
-
|
51 |
-
// HTTP-version SP status-code SP reason-phrase
|
52 |
-
$startLine = explode(' ', array_shift($this->headers), 3);
|
53 |
-
$headers = \GuzzleHttp\headers_from_lines($this->headers);
|
54 |
-
$normalizedKeys = \GuzzleHttp\normalize_header_keys($headers);
|
55 |
-
|
56 |
-
if (!empty($this->options['decode_content'])
|
57 |
-
&& isset($normalizedKeys['content-encoding'])
|
58 |
-
) {
|
59 |
-
$headers['x-encoded-content-encoding']
|
60 |
-
= $headers[$normalizedKeys['content-encoding']];
|
61 |
-
unset($headers[$normalizedKeys['content-encoding']]);
|
62 |
-
if (isset($normalizedKeys['content-length'])) {
|
63 |
-
$headers['x-encoded-content-length']
|
64 |
-
= $headers[$normalizedKeys['content-length']];
|
65 |
-
|
66 |
-
$bodyLength = (int) $this->sink->getSize();
|
67 |
-
if ($bodyLength) {
|
68 |
-
$headers[$normalizedKeys['content-length']] = $bodyLength;
|
69 |
-
} else {
|
70 |
-
unset($headers[$normalizedKeys['content-length']]);
|
71 |
-
}
|
72 |
-
}
|
73 |
-
}
|
74 |
-
|
75 |
-
// Attach a response to the easy handle with the parsed headers.
|
76 |
-
$this->response = new Response(
|
77 |
-
$startLine[1],
|
78 |
-
$headers,
|
79 |
-
$this->sink,
|
80 |
-
substr($startLine[0], 5),
|
81 |
-
isset($startLine[2]) ? (string) $startLine[2] : null
|
82 |
-
);
|
83 |
-
}
|
84 |
-
|
85 |
-
public function __get($name)
|
86 |
-
{
|
87 |
-
$msg = $name === 'handle'
|
88 |
-
? 'The EasyHandle has been released'
|
89 |
-
: 'Invalid property: ' . $name;
|
90 |
-
throw new \BadMethodCallException($msg);
|
91 |
-
}
|
92 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Handler;
|
3 |
+
|
4 |
+
use GuzzleHttp\Psr7\Response;
|
5 |
+
use Psr\Http\Message\RequestInterface;
|
6 |
+
use Psr\Http\Message\ResponseInterface;
|
7 |
+
use Psr\Http\Message\StreamInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Represents a cURL easy handle and the data it populates.
|
11 |
+
*
|
12 |
+
* @internal
|
13 |
+
*/
|
14 |
+
final class EasyHandle
|
15 |
+
{
|
16 |
+
/** @var resource cURL resource */
|
17 |
+
public $handle;
|
18 |
+
|
19 |
+
/** @var StreamInterface Where data is being written */
|
20 |
+
public $sink;
|
21 |
+
|
22 |
+
/** @var array Received HTTP headers so far */
|
23 |
+
public $headers = [];
|
24 |
+
|
25 |
+
/** @var ResponseInterface Received response (if any) */
|
26 |
+
public $response;
|
27 |
+
|
28 |
+
/** @var RequestInterface Request being sent */
|
29 |
+
public $request;
|
30 |
+
|
31 |
+
/** @var array Request options */
|
32 |
+
public $options = [];
|
33 |
+
|
34 |
+
/** @var int cURL error number (if any) */
|
35 |
+
public $errno = 0;
|
36 |
+
|
37 |
+
/** @var \Exception Exception during on_headers (if any) */
|
38 |
+
public $onHeadersException;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Attach a response to the easy handle based on the received headers.
|
42 |
+
*
|
43 |
+
* @throws \RuntimeException if no headers have been received.
|
44 |
+
*/
|
45 |
+
public function createResponse()
|
46 |
+
{
|
47 |
+
if (empty($this->headers)) {
|
48 |
+
throw new \RuntimeException('No headers have been received');
|
49 |
+
}
|
50 |
+
|
51 |
+
// HTTP-version SP status-code SP reason-phrase
|
52 |
+
$startLine = explode(' ', array_shift($this->headers), 3);
|
53 |
+
$headers = \GuzzleHttp\headers_from_lines($this->headers);
|
54 |
+
$normalizedKeys = \GuzzleHttp\normalize_header_keys($headers);
|
55 |
+
|
56 |
+
if (!empty($this->options['decode_content'])
|
57 |
+
&& isset($normalizedKeys['content-encoding'])
|
58 |
+
) {
|
59 |
+
$headers['x-encoded-content-encoding']
|
60 |
+
= $headers[$normalizedKeys['content-encoding']];
|
61 |
+
unset($headers[$normalizedKeys['content-encoding']]);
|
62 |
+
if (isset($normalizedKeys['content-length'])) {
|
63 |
+
$headers['x-encoded-content-length']
|
64 |
+
= $headers[$normalizedKeys['content-length']];
|
65 |
+
|
66 |
+
$bodyLength = (int) $this->sink->getSize();
|
67 |
+
if ($bodyLength) {
|
68 |
+
$headers[$normalizedKeys['content-length']] = $bodyLength;
|
69 |
+
} else {
|
70 |
+
unset($headers[$normalizedKeys['content-length']]);
|
71 |
+
}
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
// Attach a response to the easy handle with the parsed headers.
|
76 |
+
$this->response = new Response(
|
77 |
+
$startLine[1],
|
78 |
+
$headers,
|
79 |
+
$this->sink,
|
80 |
+
substr($startLine[0], 5),
|
81 |
+
isset($startLine[2]) ? (string) $startLine[2] : null
|
82 |
+
);
|
83 |
+
}
|
84 |
+
|
85 |
+
public function __get($name)
|
86 |
+
{
|
87 |
+
$msg = $name === 'handle'
|
88 |
+
? 'The EasyHandle has been released'
|
89 |
+
: 'Invalid property: ' . $name;
|
90 |
+
throw new \BadMethodCallException($msg);
|
91 |
+
}
|
92 |
+
}
|
vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php
CHANGED
@@ -1,189 +1,189 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Handler;
|
3 |
-
|
4 |
-
use GuzzleHttp\Exception\RequestException;
|
5 |
-
use GuzzleHttp\HandlerStack;
|
6 |
-
use GuzzleHttp\Promise\PromiseInterface;
|
7 |
-
use GuzzleHttp\Promise\RejectedPromise;
|
8 |
-
use GuzzleHttp\TransferStats;
|
9 |
-
use Psr\Http\Message\RequestInterface;
|
10 |
-
use Psr\Http\Message\ResponseInterface;
|
11 |
-
|
12 |
-
/**
|
13 |
-
* Handler that returns responses or throw exceptions from a queue.
|
14 |
-
*/
|
15 |
-
class MockHandler implements \Countable
|
16 |
-
{
|
17 |
-
private $queue = [];
|
18 |
-
private $lastRequest;
|
19 |
-
private $lastOptions;
|
20 |
-
private $onFulfilled;
|
21 |
-
private $onRejected;
|
22 |
-
|
23 |
-
/**
|
24 |
-
* Creates a new MockHandler that uses the default handler stack list of
|
25 |
-
* middlewares.
|
26 |
-
*
|
27 |
-
* @param array $queue Array of responses, callables, or exceptions.
|
28 |
-
* @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
|
29 |
-
* @param callable $onRejected Callback to invoke when the return value is rejected.
|
30 |
-
*
|
31 |
-
* @return HandlerStack
|
32 |
-
*/
|
33 |
-
public static function createWithMiddleware(
|
34 |
-
array $queue = null,
|
35 |
-
callable $onFulfilled = null,
|
36 |
-
callable $onRejected = null
|
37 |
-
) {
|
38 |
-
return HandlerStack::create(new self($queue, $onFulfilled, $onRejected));
|
39 |
-
}
|
40 |
-
|
41 |
-
/**
|
42 |
-
* The passed in value must be an array of
|
43 |
-
* {@see Psr7\Http\Message\ResponseInterface} objects, Exceptions,
|
44 |
-
* callables, or Promises.
|
45 |
-
*
|
46 |
-
* @param array $queue
|
47 |
-
* @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
|
48 |
-
* @param callable $onRejected Callback to invoke when the return value is rejected.
|
49 |
-
*/
|
50 |
-
public function __construct(
|
51 |
-
array $queue = null,
|
52 |
-
callable $onFulfilled = null,
|
53 |
-
callable $onRejected = null
|
54 |
-
) {
|
55 |
-
$this->onFulfilled = $onFulfilled;
|
56 |
-
$this->onRejected = $onRejected;
|
57 |
-
|
58 |
-
if ($queue) {
|
59 |
-
call_user_func_array([$this, 'append'], $queue);
|
60 |
-
}
|
61 |
-
}
|
62 |
-
|
63 |
-
public function __invoke(RequestInterface $request, array $options)
|
64 |
-
{
|
65 |
-
if (!$this->queue) {
|
66 |
-
throw new \OutOfBoundsException('Mock queue is empty');
|
67 |
-
}
|
68 |
-
|
69 |
-
if (isset($options['delay'])) {
|
70 |
-
usleep($options['delay'] * 1000);
|
71 |
-
}
|
72 |
-
|
73 |
-
$this->lastRequest = $request;
|
74 |
-
$this->lastOptions = $options;
|
75 |
-
$response = array_shift($this->queue);
|
76 |
-
|
77 |
-
if (isset($options['on_headers'])) {
|
78 |
-
if (!is_callable($options['on_headers'])) {
|
79 |
-
throw new \InvalidArgumentException('on_headers must be callable');
|
80 |
-
}
|
81 |
-
try {
|
82 |
-
$options['on_headers']($response);
|
83 |
-
} catch (\Exception $e) {
|
84 |
-
$msg = 'An error was encountered during the on_headers event';
|
85 |
-
$response = new RequestException($msg, $request, $response, $e);
|
86 |
-
}
|
87 |
-
}
|
88 |
-
|
89 |
-
if (is_callable($response)) {
|
90 |
-
$response = call_user_func($response, $request, $options);
|
91 |
-
}
|
92 |
-
|
93 |
-
$response = $response instanceof \Exception
|
94 |
-
? \GuzzleHttp\Promise\rejection_for($response)
|
95 |
-
: \GuzzleHttp\Promise\promise_for($response);
|
96 |
-
|
97 |
-
return $response->then(
|
98 |
-
function ($value) use ($request, $options) {
|
99 |
-
$this->invokeStats($request, $options, $value);
|
100 |
-
if ($this->onFulfilled) {
|
101 |
-
call_user_func($this->onFulfilled, $value);
|
102 |
-
}
|
103 |
-
if (isset($options['sink'])) {
|
104 |
-
$contents = (string) $value->getBody();
|
105 |
-
$sink = $options['sink'];
|
106 |
-
|
107 |
-
if (is_resource($sink)) {
|
108 |
-
fwrite($sink, $contents);
|
109 |
-
} elseif (is_string($sink)) {
|
110 |
-
file_put_contents($sink, $contents);
|
111 |
-
} elseif ($sink instanceof \Psr\Http\Message\StreamInterface) {
|
112 |
-
$sink->write($contents);
|
113 |
-
}
|
114 |
-
}
|
115 |
-
|
116 |
-
return $value;
|
117 |
-
},
|
118 |
-
function ($reason) use ($request, $options) {
|
119 |
-
$this->invokeStats($request, $options, null, $reason);
|
120 |
-
if ($this->onRejected) {
|
121 |
-
call_user_func($this->onRejected, $reason);
|
122 |
-
}
|
123 |
-
return \GuzzleHttp\Promise\rejection_for($reason);
|
124 |
-
}
|
125 |
-
);
|
126 |
-
}
|
127 |
-
|
128 |
-
/**
|
129 |
-
* Adds one or more variadic requests, exceptions, callables, or promises
|
130 |
-
* to the queue.
|
131 |
-
*/
|
132 |
-
public function append()
|
133 |
-
{
|
134 |
-
foreach (func_get_args() as $value) {
|
135 |
-
if ($value instanceof ResponseInterface
|
136 |
-
|| $value instanceof \Exception
|
137 |
-
|| $value instanceof PromiseInterface
|
138 |
-
|| is_callable($value)
|
139 |
-
) {
|
140 |
-
$this->queue[] = $value;
|
141 |
-
} else {
|
142 |
-
throw new \InvalidArgumentException('Expected a response or '
|
143 |
-
. 'exception. Found ' . \GuzzleHttp\describe_type($value));
|
144 |
-
}
|
145 |
-
}
|
146 |
-
}
|
147 |
-
|
148 |
-
/**
|
149 |
-
* Get the last received request.
|
150 |
-
*
|
151 |
-
* @return RequestInterface
|
152 |
-
*/
|
153 |
-
public function getLastRequest()
|
154 |
-
{
|
155 |
-
return $this->lastRequest;
|
156 |
-
}
|
157 |
-
|
158 |
-
/**
|
159 |
-
* Get the last received request options.
|
160 |
-
*
|
161 |
-
* @return array
|
162 |
-
*/
|
163 |
-
public function getLastOptions()
|
164 |
-
{
|
165 |
-
return $this->lastOptions;
|
166 |
-
}
|
167 |
-
|
168 |
-
/**
|
169 |
-
* Returns the number of remaining items in the queue.
|
170 |
-
*
|
171 |
-
* @return int
|
172 |
-
*/
|
173 |
-
public function count()
|
174 |
-
{
|
175 |
-
return count($this->queue);
|
176 |
-
}
|
177 |
-
|
178 |
-
private function invokeStats(
|
179 |
-
RequestInterface $request,
|
180 |
-
array $options,
|
181 |
-
ResponseInterface $response = null,
|
182 |
-
$reason = null
|
183 |
-
) {
|
184 |
-
if (isset($options['on_stats'])) {
|
185 |
-
$stats = new TransferStats($request, $response, 0, $reason);
|
186 |
-
call_user_func($options['on_stats'], $stats);
|
187 |
-
}
|
188 |
-
}
|
189 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Handler;
|
3 |
+
|
4 |
+
use GuzzleHttp\Exception\RequestException;
|
5 |
+
use GuzzleHttp\HandlerStack;
|
6 |
+
use GuzzleHttp\Promise\PromiseInterface;
|
7 |
+
use GuzzleHttp\Promise\RejectedPromise;
|
8 |
+
use GuzzleHttp\TransferStats;
|
9 |
+
use Psr\Http\Message\RequestInterface;
|
10 |
+
use Psr\Http\Message\ResponseInterface;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Handler that returns responses or throw exceptions from a queue.
|
14 |
+
*/
|
15 |
+
class MockHandler implements \Countable
|
16 |
+
{
|
17 |
+
private $queue = [];
|
18 |
+
private $lastRequest;
|
19 |
+
private $lastOptions;
|
20 |
+
private $onFulfilled;
|
21 |
+
private $onRejected;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Creates a new MockHandler that uses the default handler stack list of
|
25 |
+
* middlewares.
|
26 |
+
*
|
27 |
+
* @param array $queue Array of responses, callables, or exceptions.
|
28 |
+
* @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
|
29 |
+
* @param callable $onRejected Callback to invoke when the return value is rejected.
|
30 |
+
*
|
31 |
+
* @return HandlerStack
|
32 |
+
*/
|
33 |
+
public static function createWithMiddleware(
|
34 |
+
array $queue = null,
|
35 |
+
callable $onFulfilled = null,
|
36 |
+
callable $onRejected = null
|
37 |
+
) {
|
38 |
+
return HandlerStack::create(new self($queue, $onFulfilled, $onRejected));
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* The passed in value must be an array of
|
43 |
+
* {@see Psr7\Http\Message\ResponseInterface} objects, Exceptions,
|
44 |
+
* callables, or Promises.
|
45 |
+
*
|
46 |
+
* @param array $queue
|
47 |
+
* @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
|
48 |
+
* @param callable $onRejected Callback to invoke when the return value is rejected.
|
49 |
+
*/
|
50 |
+
public function __construct(
|
51 |
+
array $queue = null,
|
52 |
+
callable $onFulfilled = null,
|
53 |
+
callable $onRejected = null
|
54 |
+
) {
|
55 |
+
$this->onFulfilled = $onFulfilled;
|
56 |
+
$this->onRejected = $onRejected;
|
57 |
+
|
58 |
+
if ($queue) {
|
59 |
+
call_user_func_array([$this, 'append'], $queue);
|
60 |
+
}
|
61 |
+
}
|
62 |
+
|
63 |
+
public function __invoke(RequestInterface $request, array $options)
|
64 |
+
{
|
65 |
+
if (!$this->queue) {
|
66 |
+
throw new \OutOfBoundsException('Mock queue is empty');
|
67 |
+
}
|
68 |
+
|
69 |
+
if (isset($options['delay'])) {
|
70 |
+
usleep($options['delay'] * 1000);
|
71 |
+
}
|
72 |
+
|
73 |
+
$this->lastRequest = $request;
|
74 |
+
$this->lastOptions = $options;
|
75 |
+
$response = array_shift($this->queue);
|
76 |
+
|
77 |
+
if (isset($options['on_headers'])) {
|
78 |
+
if (!is_callable($options['on_headers'])) {
|
79 |
+
throw new \InvalidArgumentException('on_headers must be callable');
|
80 |
+
}
|
81 |
+
try {
|
82 |
+
$options['on_headers']($response);
|
83 |
+
} catch (\Exception $e) {
|
84 |
+
$msg = 'An error was encountered during the on_headers event';
|
85 |
+
$response = new RequestException($msg, $request, $response, $e);
|
86 |
+
}
|
87 |
+
}
|
88 |
+
|
89 |
+
if (is_callable($response)) {
|
90 |
+
$response = call_user_func($response, $request, $options);
|
91 |
+
}
|
92 |
+
|
93 |
+
$response = $response instanceof \Exception
|
94 |
+
? \GuzzleHttp\Promise\rejection_for($response)
|
95 |
+
: \GuzzleHttp\Promise\promise_for($response);
|
96 |
+
|
97 |
+
return $response->then(
|
98 |
+
function ($value) use ($request, $options) {
|
99 |
+
$this->invokeStats($request, $options, $value);
|
100 |
+
if ($this->onFulfilled) {
|
101 |
+
call_user_func($this->onFulfilled, $value);
|
102 |
+
}
|
103 |
+
if (isset($options['sink'])) {
|
104 |
+
$contents = (string) $value->getBody();
|
105 |
+
$sink = $options['sink'];
|
106 |
+
|
107 |
+
if (is_resource($sink)) {
|
108 |
+
fwrite($sink, $contents);
|
109 |
+
} elseif (is_string($sink)) {
|
110 |
+
file_put_contents($sink, $contents);
|
111 |
+
} elseif ($sink instanceof \Psr\Http\Message\StreamInterface) {
|
112 |
+
$sink->write($contents);
|
113 |
+
}
|
114 |
+
}
|
115 |
+
|
116 |
+
return $value;
|
117 |
+
},
|
118 |
+
function ($reason) use ($request, $options) {
|
119 |
+
$this->invokeStats($request, $options, null, $reason);
|
120 |
+
if ($this->onRejected) {
|
121 |
+
call_user_func($this->onRejected, $reason);
|
122 |
+
}
|
123 |
+
return \GuzzleHttp\Promise\rejection_for($reason);
|
124 |
+
}
|
125 |
+
);
|
126 |
+
}
|
127 |
+
|
128 |
+
/**
|
129 |
+
* Adds one or more variadic requests, exceptions, callables, or promises
|
130 |
+
* to the queue.
|
131 |
+
*/
|
132 |
+
public function append()
|
133 |
+
{
|
134 |
+
foreach (func_get_args() as $value) {
|
135 |
+
if ($value instanceof ResponseInterface
|
136 |
+
|| $value instanceof \Exception
|
137 |
+
|| $value instanceof PromiseInterface
|
138 |
+
|| is_callable($value)
|
139 |
+
) {
|
140 |
+
$this->queue[] = $value;
|
141 |
+
} else {
|
142 |
+
throw new \InvalidArgumentException('Expected a response or '
|
143 |
+
. 'exception. Found ' . \GuzzleHttp\describe_type($value));
|
144 |
+
}
|
145 |
+
}
|
146 |
+
}
|
147 |
+
|
148 |
+
/**
|
149 |
+
* Get the last received request.
|
150 |
+
*
|
151 |
+
* @return RequestInterface
|
152 |
+
*/
|
153 |
+
public function getLastRequest()
|
154 |
+
{
|
155 |
+
return $this->lastRequest;
|
156 |
+
}
|
157 |
+
|
158 |
+
/**
|
159 |
+
* Get the last received request options.
|
160 |
+
*
|
161 |
+
* @return array
|
162 |
+
*/
|
163 |
+
public function getLastOptions()
|
164 |
+
{
|
165 |
+
return $this->lastOptions;
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Returns the number of remaining items in the queue.
|
170 |
+
*
|
171 |
+
* @return int
|
172 |
+
*/
|
173 |
+
public function count()
|
174 |
+
{
|
175 |
+
return count($this->queue);
|
176 |
+
}
|
177 |
+
|
178 |
+
private function invokeStats(
|
179 |
+
RequestInterface $request,
|
180 |
+
array $options,
|
181 |
+
ResponseInterface $response = null,
|
182 |
+
$reason = null
|
183 |
+
) {
|
184 |
+
if (isset($options['on_stats'])) {
|
185 |
+
$stats = new TransferStats($request, $response, 0, $reason);
|
186 |
+
call_user_func($options['on_stats'], $stats);
|
187 |
+
}
|
188 |
+
}
|
189 |
+
}
|
vendor/guzzlehttp/guzzle/src/Handler/Proxy.php
CHANGED
@@ -1,55 +1,55 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Handler;
|
3 |
-
|
4 |
-
use GuzzleHttp\RequestOptions;
|
5 |
-
use Psr\Http\Message\RequestInterface;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* Provides basic proxies for handlers.
|
9 |
-
*/
|
10 |
-
class Proxy
|
11 |
-
{
|
12 |
-
/**
|
13 |
-
* Sends synchronous requests to a specific handler while sending all other
|
14 |
-
* requests to another handler.
|
15 |
-
*
|
16 |
-
* @param callable $default Handler used for normal responses
|
17 |
-
* @param callable $sync Handler used for synchronous responses.
|
18 |
-
*
|
19 |
-
* @return callable Returns the composed handler.
|
20 |
-
*/
|
21 |
-
public static function wrapSync(
|
22 |
-
callable $default,
|
23 |
-
callable $sync
|
24 |
-
) {
|
25 |
-
return function (RequestInterface $request, array $options) use ($default, $sync) {
|
26 |
-
return empty($options[RequestOptions::SYNCHRONOUS])
|
27 |
-
? $default($request, $options)
|
28 |
-
: $sync($request, $options);
|
29 |
-
};
|
30 |
-
}
|
31 |
-
|
32 |
-
/**
|
33 |
-
* Sends streaming requests to a streaming compatible handler while sending
|
34 |
-
* all other requests to a default handler.
|
35 |
-
*
|
36 |
-
* This, for example, could be useful for taking advantage of the
|
37 |
-
* performance benefits of curl while still supporting true streaming
|
38 |
-
* through the StreamHandler.
|
39 |
-
*
|
40 |
-
* @param callable $default Handler used for non-streaming responses
|
41 |
-
* @param callable $streaming Handler used for streaming responses
|
42 |
-
*
|
43 |
-
* @return callable Returns the composed handler.
|
44 |
-
*/
|
45 |
-
public static function wrapStreaming(
|
46 |
-
callable $default,
|
47 |
-
callable $streaming
|
48 |
-
) {
|
49 |
-
return function (RequestInterface $request, array $options) use ($default, $streaming) {
|
50 |
-
return empty($options['stream'])
|
51 |
-
? $default($request, $options)
|
52 |
-
: $streaming($request, $options);
|
53 |
-
};
|
54 |
-
}
|
55 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Handler;
|
3 |
+
|
4 |
+
use GuzzleHttp\RequestOptions;
|
5 |
+
use Psr\Http\Message\RequestInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Provides basic proxies for handlers.
|
9 |
+
*/
|
10 |
+
class Proxy
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Sends synchronous requests to a specific handler while sending all other
|
14 |
+
* requests to another handler.
|
15 |
+
*
|
16 |
+
* @param callable $default Handler used for normal responses
|
17 |
+
* @param callable $sync Handler used for synchronous responses.
|
18 |
+
*
|
19 |
+
* @return callable Returns the composed handler.
|
20 |
+
*/
|
21 |
+
public static function wrapSync(
|
22 |
+
callable $default,
|
23 |
+
callable $sync
|
24 |
+
) {
|
25 |
+
return function (RequestInterface $request, array $options) use ($default, $sync) {
|
26 |
+
return empty($options[RequestOptions::SYNCHRONOUS])
|
27 |
+
? $default($request, $options)
|
28 |
+
: $sync($request, $options);
|
29 |
+
};
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Sends streaming requests to a streaming compatible handler while sending
|
34 |
+
* all other requests to a default handler.
|
35 |
+
*
|
36 |
+
* This, for example, could be useful for taking advantage of the
|
37 |
+
* performance benefits of curl while still supporting true streaming
|
38 |
+
* through the StreamHandler.
|
39 |
+
*
|
40 |
+
* @param callable $default Handler used for non-streaming responses
|
41 |
+
* @param callable $streaming Handler used for streaming responses
|
42 |
+
*
|
43 |
+
* @return callable Returns the composed handler.
|
44 |
+
*/
|
45 |
+
public static function wrapStreaming(
|
46 |
+
callable $default,
|
47 |
+
callable $streaming
|
48 |
+
) {
|
49 |
+
return function (RequestInterface $request, array $options) use ($default, $streaming) {
|
50 |
+
return empty($options['stream'])
|
51 |
+
? $default($request, $options)
|
52 |
+
: $streaming($request, $options);
|
53 |
+
};
|
54 |
+
}
|
55 |
+
}
|
vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php
CHANGED
@@ -1,532 +1,532 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp\Handler;
|
3 |
-
|
4 |
-
use GuzzleHttp\Exception\RequestException;
|
5 |
-
use GuzzleHttp\Exception\ConnectException;
|
6 |
-
use GuzzleHttp\Promise\FulfilledPromise;
|
7 |
-
use GuzzleHttp\Promise\PromiseInterface;
|
8 |
-
use GuzzleHttp\Psr7;
|
9 |
-
use GuzzleHttp\TransferStats;
|
10 |
-
use Psr\Http\Message\RequestInterface;
|
11 |
-
use Psr\Http\Message\ResponseInterface;
|
12 |
-
use Psr\Http\Message\StreamInterface;
|
13 |
-
|
14 |
-
/**
|
15 |
-
* HTTP handler that uses PHP's HTTP stream wrapper.
|
16 |
-
*/
|
17 |
-
class StreamHandler
|
18 |
-
{
|
19 |
-
private $lastHeaders = [];
|
20 |
-
|
21 |
-
/**
|
22 |
-
* Sends an HTTP request.
|
23 |
-
*
|
24 |
-
* @param RequestInterface $request Request to send.
|
25 |
-
* @param array $options Request transfer options.
|
26 |
-
*
|
27 |
-
* @return PromiseInterface
|
28 |
-
*/
|
29 |
-
public function __invoke(RequestInterface $request, array $options)
|
30 |
-
{
|
31 |
-
// Sleep if there is a delay specified.
|
32 |
-
if (isset($options['delay'])) {
|
33 |
-
usleep($options['delay'] * 1000);
|
34 |
-
}
|
35 |
-
|
36 |
-
$startTime = isset($options['on_stats']) ? microtime(true) : null;
|
37 |
-
|
38 |
-
try {
|
39 |
-
// Does not support the expect header.
|
40 |
-
$request = $request->withoutHeader('Expect');
|
41 |
-
|
42 |
-
// Append a content-length header if body size is zero to match
|
43 |
-
// cURL's behavior.
|
44 |
-
if (0 === $request->getBody()->getSize()) {
|
45 |
-
$request = $request->withHeader('Content-Length', 0);
|
46 |
-
}
|
47 |
-
|
48 |
-
return $this->createResponse(
|
49 |
-
$request,
|
50 |
-
$options,
|
51 |
-
$this->createStream($request, $options),
|
52 |
-
$startTime
|
53 |
-
);
|
54 |
-
} catch (\InvalidArgumentException $e) {
|
55 |
-
throw $e;
|
56 |
-
} catch (\Exception $e) {
|
57 |
-
// Determine if the error was a networking error.
|
58 |
-
$message = $e->getMessage();
|
59 |
-
// This list can probably get more comprehensive.
|
60 |
-
if (strpos($message, 'getaddrinfo') // DNS lookup failed
|
61 |
-
|| strpos($message, 'Connection refused')
|
62 |
-
|| strpos($message, "couldn't connect to host") // error on HHVM
|
63 |
-
|| strpos($message, "connection attempt failed")
|
64 |
-
) {
|
65 |
-
$e = new ConnectException($e->getMessage(), $request, $e);
|
66 |
-
}
|
67 |
-
$e = RequestException::wrapException($request, $e);
|
68 |
-
$this->invokeStats($options, $request, $startTime, null, $e);
|
69 |
-
|
70 |
-
return \GuzzleHttp\Promise\rejection_for($e);
|
71 |
-
}
|
72 |
-
}
|
73 |
-
|
74 |
-
private function invokeStats(
|
75 |
-
array $options,
|
76 |
-
RequestInterface $request,
|
77 |
-
$startTime,
|
78 |
-
ResponseInterface $response = null,
|
79 |
-
$error = null
|
80 |
-
) {
|
81 |
-
if (isset($options['on_stats'])) {
|
82 |
-
$stats = new TransferStats(
|
83 |
-
$request,
|
84 |
-
$response,
|
85 |
-
microtime(true) - $startTime,
|
86 |
-
$error,
|
87 |
-
[]
|
88 |
-
);
|
89 |
-
call_user_func($options['on_stats'], $stats);
|
90 |
-
}
|
91 |
-
}
|
92 |
-
|
93 |
-
private function createResponse(
|
94 |
-
RequestInterface $request,
|
95 |
-
array $options,
|
96 |
-
$stream,
|
97 |
-
$startTime
|
98 |
-
) {
|
99 |
-
$hdrs = $this->lastHeaders;
|
100 |
-
$this->lastHeaders = [];
|
101 |
-
$parts = explode(' ', array_shift($hdrs), 3);
|
102 |
-
$ver = explode('/', $parts[0])[1];
|
103 |
-
$status = $parts[1];
|
104 |
-
$reason = isset($parts[2]) ? $parts[2] : null;
|
105 |
-
$headers = \GuzzleHttp\headers_from_lines($hdrs);
|
106 |
-
list($stream, $headers) = $this->checkDecode($options, $headers, $stream);
|
107 |
-
$stream = Psr7\stream_for($stream);
|
108 |
-
$sink = $stream;
|
109 |
-
|
110 |
-
if (strcasecmp('HEAD', $request->getMethod())) {
|
111 |
-
$sink = $this->createSink($stream, $options);
|
112 |
-
}
|
113 |
-
|
114 |
-
$response = new Psr7\Response($status, $headers, $sink, $ver, $reason);
|
115 |
-
|
116 |
-
if (isset($options['on_headers'])) {
|
117 |
-
try {
|
118 |
-
$options['on_headers']($response);
|
119 |
-
} catch (\Exception $e) {
|
120 |
-
$msg = 'An error was encountered during the on_headers event';
|
121 |
-
$ex = new RequestException($msg, $request, $response, $e);
|
122 |
-
return \GuzzleHttp\Promise\rejection_for($ex);
|
123 |
-
}
|
124 |
-
}
|
125 |
-
|
126 |
-
// Do not drain when the request is a HEAD request because they have
|
127 |
-
// no body.
|
128 |
-
if ($sink !== $stream) {
|
129 |
-
$this->drain(
|
130 |
-
$stream,
|
131 |
-
$sink,
|
132 |
-
$response->getHeaderLine('Content-Length')
|
133 |
-
);
|
134 |
-
}
|
135 |
-
|
136 |
-
$this->invokeStats($options, $request, $startTime, $response, null);
|
137 |
-
|
138 |
-
return new FulfilledPromise($response);
|
139 |
-
}
|
140 |
-
|
141 |
-
private function createSink(StreamInterface $stream, array $options)
|
142 |
-
{
|
143 |
-
if (!empty($options['stream'])) {
|
144 |
-
return $stream;
|
145 |
-
}
|
146 |
-
|
147 |
-
$sink = isset($options['sink'])
|
148 |
-
? $options['sink']
|
149 |
-
: fopen('php://temp', 'r+');
|
150 |
-
|
151 |
-
return is_string($sink)
|
152 |
-
? new Psr7\LazyOpenStream($sink, 'w+')
|
153 |
-
: Psr7\stream_for($sink);
|
154 |
-
}
|
155 |
-
|
156 |
-
private function checkDecode(array $options, array $headers, $stream)
|
157 |
-
{
|
158 |
-
// Automatically decode responses when instructed.
|
159 |
-
if (!empty($options['decode_content'])) {
|
160 |
-
$normalizedKeys = \GuzzleHttp\normalize_header_keys($headers);
|
161 |
-
if (isset($normalizedKeys['content-encoding'])) {
|
162 |
-
$encoding = $headers[$normalizedKeys['content-encoding']];
|
163 |
-
if ($encoding[0] === 'gzip' || $encoding[0] === 'deflate') {
|
164 |
-
$stream = new Psr7\InflateStream(
|
165 |
-
Psr7\stream_for($stream)
|
166 |
-
);
|
167 |
-
$headers['x-encoded-content-encoding']
|
168 |
-
= $headers[$normalizedKeys['content-encoding']];
|
169 |
-
// Remove content-encoding header
|
170 |
-
unset($headers[$normalizedKeys['content-encoding']]);
|
171 |
-
// Fix content-length header
|
172 |
-
if (isset($normalizedKeys['content-length'])) {
|
173 |
-
$headers['x-encoded-content-length']
|
174 |
-
= $headers[$normalizedKeys['content-length']];
|
175 |
-
|
176 |
-
$length = (int) $stream->getSize();
|
177 |
-
if ($length === 0) {
|
178 |
-
unset($headers[$normalizedKeys['content-length']]);
|
179 |
-
} else {
|
180 |
-
$headers[$normalizedKeys['content-length']] = [$length];
|
181 |
-
}
|
182 |
-
}
|
183 |
-
}
|
184 |
-
}
|
185 |
-
}
|
186 |
-
|
187 |
-
return [$stream, $headers];
|
188 |
-
}
|
189 |
-
|
190 |
-
/**
|
191 |
-
* Drains the source stream into the "sink" client option.
|
192 |
-
*
|
193 |
-
* @param StreamInterface $source
|
194 |
-
* @param StreamInterface $sink
|
195 |
-
* @param string $contentLength Header specifying the amount of
|
196 |
-
* data to read.
|
197 |
-
*
|
198 |
-
* @return StreamInterface
|
199 |
-
* @throws \RuntimeException when the sink option is invalid.
|
200 |
-
*/
|
201 |
-
private function drain(
|
202 |
-
StreamInterface $source,
|
203 |
-
StreamInterface $sink,
|
204 |
-
$contentLength
|
205 |
-
) {
|
206 |
-
// If a content-length header is provided, then stop reading once
|
207 |
-
// that number of bytes has been read. This can prevent infinitely
|
208 |
-
// reading from a stream when dealing with servers that do not honor
|
209 |
-
// Connection: Close headers.
|
210 |
-
Psr7\copy_to_stream(
|
211 |
-
$source,
|
212 |
-
$sink,
|
213 |
-
(strlen($contentLength) > 0 && (int) $contentLength > 0) ? (int) $contentLength : -1
|
214 |
-
);
|
215 |
-
|
216 |
-
$sink->seek(0);
|
217 |
-
$source->close();
|
218 |
-
|
219 |
-
return $sink;
|
220 |
-
}
|
221 |
-
|
222 |
-
/**
|
223 |
-
* Create a resource and check to ensure it was created successfully
|
224 |
-
*
|
225 |
-
* @param callable $callback Callable that returns stream resource
|
226 |
-
*
|
227 |
-
* @return resource
|
228 |
-
* @throws \RuntimeException on error
|
229 |
-
*/
|
230 |
-
private function createResource(callable $callback)
|
231 |
-
{
|
232 |
-
$errors = null;
|
233 |
-
set_error_handler(function ($_, $msg, $file, $line) use (&$errors) {
|
234 |
-
$errors[] = [
|
235 |
-
'message' => $msg,
|
236 |
-
'file' => $file,
|
237 |
-
'line' => $line
|
238 |
-
];
|
239 |
-
return true;
|
240 |
-
});
|
241 |
-
|
242 |
-
$resource = $callback();
|
243 |
-
restore_error_handler();
|
244 |
-
|
245 |
-
if (!$resource) {
|
246 |
-
$message = 'Error creating resource: ';
|
247 |
-
foreach ($errors as $err) {
|
248 |
-
foreach ($err as $key => $value) {
|
249 |
-
$message .= "[$key] $value" . PHP_EOL;
|
250 |
-
}
|
251 |
-
}
|
252 |
-
throw new \RuntimeException(trim($message));
|
253 |
-
}
|
254 |
-
|
255 |
-
return $resource;
|
256 |
-
}
|
257 |
-
|
258 |
-
private function createStream(RequestInterface $request, array $options)
|
259 |
-
{
|
260 |
-
static $methods;
|
261 |
-
if (!$methods) {
|
262 |
-
$methods = array_flip(get_class_methods(__CLASS__));
|
263 |
-
}
|
264 |
-
|
265 |
-
// HTTP/1.1 streams using the PHP stream wrapper require a
|
266 |
-
// Connection: close header
|
267 |
-
if ($request->getProtocolVersion() == '1.1'
|
268 |
-
&& !$request->hasHeader('Connection')
|
269 |
-
) {
|
270 |
-
$request = $request->withHeader('Connection', 'close');
|
271 |
-
}
|
272 |
-
|
273 |
-
// Ensure SSL is verified by default
|
274 |
-
if (!isset($options['verify'])) {
|
275 |
-
$options['verify'] = true;
|
276 |
-
}
|
277 |
-
|
278 |
-
$params = [];
|
279 |
-
$context = $this->getDefaultContext($request);
|
280 |
-
|
281 |
-
if (isset($options['on_headers']) && !is_callable($options['on_headers'])) {
|
282 |
-
throw new \InvalidArgumentException('on_headers must be callable');
|
283 |
-
}
|
284 |
-
|
285 |
-
if (!empty($options)) {
|
286 |
-
foreach ($options as $key => $value) {
|
287 |
-
$method = "add_{$key}";
|
288 |
-
if (isset($methods[$method])) {
|
289 |
-
$this->{$method}($request, $context, $value, $params);
|
290 |
-
}
|
291 |
-
}
|
292 |
-
}
|
293 |
-
|
294 |
-
if (isset($options['stream_context'])) {
|
295 |
-
if (!is_array($options['stream_context'])) {
|
296 |
-
throw new \InvalidArgumentException('stream_context must be an array');
|
297 |
-
}
|
298 |
-
$context = array_replace_recursive(
|
299 |
-
$context,
|
300 |
-
$options['stream_context']
|
301 |
-
);
|
302 |
-
}
|
303 |
-
|
304 |
-
// Microsoft NTLM authentication only supported with curl handler
|
305 |
-
if (isset($options['auth'])
|
306 |
-
&& is_array($options['auth'])
|
307 |
-
&& isset($options['auth'][2])
|
308 |
-
&& 'ntlm' == $options['auth'][2]
|
309 |
-
) {
|
310 |
-
throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler');
|
311 |
-
}
|
312 |
-
|
313 |
-
$uri = $this->resolveHost($request, $options);
|
314 |
-
|
315 |
-
$context = $this->createResource(
|
316 |
-
function () use ($context, $params) {
|
317 |
-
return stream_context_create($context, $params);
|
318 |
-
}
|
319 |
-
);
|
320 |
-
|
321 |
-
return $this->createResource(
|
322 |
-
function () use ($uri, &$http_response_header, $context, $options) {
|
323 |
-
$resource = fopen((string) $uri, 'r', null, $context);
|
324 |
-
$this->lastHeaders = $http_response_header;
|
325 |
-
|
326 |
-
if (isset($options['read_timeout'])) {
|
327 |
-
$readTimeout = $options['read_timeout'];
|
328 |
-
$sec = (int) $readTimeout;
|
329 |
-
$usec = ($readTimeout - $sec) * 100000;
|
330 |
-
stream_set_timeout($resource, $sec, $usec);
|
331 |
-
}
|
332 |
-
|
333 |
-
return $resource;
|
334 |
-
}
|
335 |
-
);
|
336 |
-
}
|
337 |
-
|
338 |
-
private function resolveHost(RequestInterface $request, array $options)
|
339 |
-
{
|
340 |
-
$uri = $request->getUri();
|
341 |
-
|
342 |
-
if (isset($options['force_ip_resolve']) && !filter_var($uri->getHost(), FILTER_VALIDATE_IP)) {
|
343 |
-
if ('v4' === $options['force_ip_resolve']) {
|
344 |
-
$records = dns_get_record($uri->getHost(), DNS_A);
|
345 |
-
if (!isset($records[0]['ip'])) {
|
346 |
-
throw new ConnectException(sprintf("Could not resolve IPv4 address for host '%s'", $uri->getHost()), $request);
|
347 |
-
}
|
348 |
-
$uri = $uri->withHost($records[0]['ip']);
|
349 |
-
} elseif ('v6' === $options['force_ip_resolve']) {
|
350 |
-
$records = dns_get_record($uri->getHost(), DNS_AAAA);
|
351 |
-
if (!isset($records[0]['ipv6'])) {
|
352 |
-
throw new ConnectException(sprintf("Could not resolve IPv6 address for host '%s'", $uri->getHost()), $request);
|
353 |
-
}
|
354 |
-
$uri = $uri->withHost('[' . $records[0]['ipv6'] . ']');
|
355 |
-
}
|
356 |
-
}
|
357 |
-
|
358 |
-
return $uri;
|
359 |
-
}
|
360 |
-
|
361 |
-
private function getDefaultContext(RequestInterface $request)
|
362 |
-
{
|
363 |
-
$headers = '';
|
364 |
-
foreach ($request->getHeaders() as $name => $value) {
|
365 |
-
foreach ($value as $val) {
|
366 |
-
$headers .= "$name: $val\r\n";
|
367 |
-
}
|
368 |
-
}
|
369 |
-
|
370 |
-
$context = [
|
371 |
-
'http' => [
|
372 |
-
'method' => $request->getMethod(),
|
373 |
-
'header' => $headers,
|
374 |
-
'protocol_version' => $request->getProtocolVersion(),
|
375 |
-
'ignore_errors' => true,
|
376 |
-
'follow_location' => 0,
|
377 |
-
],
|
378 |
-
];
|
379 |
-
|
380 |
-
$body = (string) $request->getBody();
|
381 |
-
|
382 |
-
if (!empty($body)) {
|
383 |
-
$context['http']['content'] = $body;
|
384 |
-
// Prevent the HTTP handler from adding a Content-Type header.
|
385 |
-
if (!$request->hasHeader('Content-Type')) {
|
386 |
-
$context['http']['header'] .= "Content-Type:\r\n";
|
387 |
-
}
|
388 |
-
}
|
389 |
-
|
390 |
-
$context['http']['header'] = rtrim($context['http']['header']);
|
391 |
-
|
392 |
-
return $context;
|
393 |
-
}
|
394 |
-
|
395 |
-
private function add_proxy(RequestInterface $request, &$options, $value, &$params)
|
396 |
-
{
|
397 |
-
if (!is_array($value)) {
|
398 |
-
$options['http']['proxy'] = $value;
|
399 |
-
} else {
|
400 |
-
$scheme = $request->getUri()->getScheme();
|
401 |
-
if (isset($value[$scheme])) {
|
402 |
-
if (!isset($value['no'])
|
403 |
-
|| !\GuzzleHttp\is_host_in_noproxy(
|
404 |
-
$request->getUri()->getHost(),
|
405 |
-
$value['no']
|
406 |
-
)
|
407 |
-
) {
|
408 |
-
$options['http']['proxy'] = $value[$scheme];
|
409 |
-
}
|
410 |
-
}
|
411 |
-
}
|
412 |
-
}
|
413 |
-
|
414 |
-
private function add_timeout(RequestInterface $request, &$options, $value, &$params)
|
415 |
-
{
|
416 |
-
if ($value > 0) {
|
417 |
-
$options['http']['timeout'] = $value;
|
418 |
-
}
|
419 |
-
}
|
420 |
-
|
421 |
-
private function add_verify(RequestInterface $request, &$options, $value, &$params)
|
422 |
-
{
|
423 |
-
if ($value === true) {
|
424 |
-
// PHP 5.6 or greater will find the system cert by default. When
|
425 |
-
// < 5.6, use the Guzzle bundled cacert.
|
426 |
-
if (PHP_VERSION_ID < 50600) {
|
427 |
-
$options['ssl']['cafile'] = \GuzzleHttp\default_ca_bundle();
|
428 |
-
}
|
429 |
-
} elseif (is_string($value)) {
|
430 |
-
$options['ssl']['cafile'] = $value;
|
431 |
-
if (!file_exists($value)) {
|
432 |
-
throw new \RuntimeException("SSL CA bundle not found: $value");
|
433 |
-
}
|
434 |
-
} elseif ($value === false) {
|
435 |
-
$options['ssl']['verify_peer'] = false;
|
436 |
-
$options['ssl']['verify_peer_name'] = false;
|
437 |
-
return;
|
438 |
-
} else {
|
439 |
-
throw new \InvalidArgumentException('Invalid verify request option');
|
440 |
-
}
|
441 |
-
|
442 |
-
$options['ssl']['verify_peer'] = true;
|
443 |
-
$options['ssl']['verify_peer_name'] = true;
|
444 |
-
$options['ssl']['allow_self_signed'] = false;
|
445 |
-
}
|
446 |
-
|
447 |
-
private function add_cert(RequestInterface $request, &$options, $value, &$params)
|
448 |
-
{
|
449 |
-
if (is_array($value)) {
|
450 |
-
$options['ssl']['passphrase'] = $value[1];
|
451 |
-
$value = $value[0];
|
452 |
-
}
|
453 |
-
|
454 |
-
if (!file_exists($value)) {
|
455 |
-
throw new \RuntimeException("SSL certificate not found: {$value}");
|
456 |
-
}
|
457 |
-
|
458 |
-
$options['ssl']['local_cert'] = $value;
|
459 |
-
}
|
460 |
-
|
461 |
-
private function add_progress(RequestInterface $request, &$options, $value, &$params)
|
462 |
-
{
|
463 |
-
$this->addNotification(
|
464 |
-
$params,
|
465 |
-
function ($code, $a, $b, $c, $transferred, $total) use ($value) {
|
466 |
-
if ($code == STREAM_NOTIFY_PROGRESS) {
|
467 |
-
$value($total, $transferred, null, null);
|
468 |
-
}
|
469 |
-
}
|
470 |
-
);
|
471 |
-
}
|
472 |
-
|
473 |
-
private function add_debug(RequestInterface $request, &$options, $value, &$params)
|
474 |
-
{
|
475 |
-
if ($value === false) {
|
476 |
-
return;
|
477 |
-
}
|
478 |
-
|
479 |
-
static $map = [
|
480 |
-
STREAM_NOTIFY_CONNECT => 'CONNECT',
|
481 |
-
STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED',
|
482 |
-
STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT',
|
483 |
-
STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS',
|
484 |
-
STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS',
|
485 |
-
STREAM_NOTIFY_REDIRECTED => 'REDIRECTED',
|
486 |
-
STREAM_NOTIFY_PROGRESS => 'PROGRESS',
|
487 |
-
STREAM_NOTIFY_FAILURE => 'FAILURE',
|
488 |
-
STREAM_NOTIFY_COMPLETED => 'COMPLETED',
|
489 |
-
STREAM_NOTIFY_RESOLVE => 'RESOLVE',
|
490 |
-
];
|
491 |
-
static $args = ['severity', 'message', 'message_code',
|
492 |
-
'bytes_transferred', 'bytes_max'];
|
493 |
-
|
494 |
-
$value = \GuzzleHttp\debug_resource($value);
|
495 |
-
$ident = $request->getMethod() . ' ' . $request->getUri()->withFragment('');
|
496 |
-
$this->addNotification(
|
497 |
-
$params,
|
498 |
-
function () use ($ident, $value, $map, $args) {
|
499 |
-
$passed = func_get_args();
|
500 |
-
$code = array_shift($passed);
|
501 |
-
fprintf($value, '<%s> [%s] ', $ident, $map[$code]);
|
502 |
-
foreach (array_filter($passed) as $i => $v) {
|
503 |
-
fwrite($value, $args[$i] . ': "' . $v . '" ');
|
504 |
-
}
|
505 |
-
fwrite($value, "\n");
|
506 |
-
}
|
507 |
-
);
|
508 |
-
}
|
509 |
-
|
510 |
-
private function addNotification(array &$params, callable $notify)
|
511 |
-
{
|
512 |
-
// Wrap the existing function if needed.
|
513 |
-
if (!isset($params['notification'])) {
|
514 |
-
$params['notification'] = $notify;
|
515 |
-
} else {
|
516 |
-
$params['notification'] = $this->callArray([
|
517 |
-
$params['notification'],
|
518 |
-
$notify
|
519 |
-
]);
|
520 |
-
}
|
521 |
-
}
|
522 |
-
|
523 |
-
private function callArray(array $functions)
|
524 |
-
{
|
525 |
-
return function () use ($functions) {
|
526 |
-
$args = func_get_args();
|
527 |
-
foreach ($functions as $fn) {
|
528 |
-
call_user_func_array($fn, $args);
|
529 |
-
}
|
530 |
-
};
|
531 |
-
}
|
532 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp\Handler;
|
3 |
+
|
4 |
+
use GuzzleHttp\Exception\RequestException;
|
5 |
+
use GuzzleHttp\Exception\ConnectException;
|
6 |
+
use GuzzleHttp\Promise\FulfilledPromise;
|
7 |
+
use GuzzleHttp\Promise\PromiseInterface;
|
8 |
+
use GuzzleHttp\Psr7;
|
9 |
+
use GuzzleHttp\TransferStats;
|
10 |
+
use Psr\Http\Message\RequestInterface;
|
11 |
+
use Psr\Http\Message\ResponseInterface;
|
12 |
+
use Psr\Http\Message\StreamInterface;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* HTTP handler that uses PHP's HTTP stream wrapper.
|
16 |
+
*/
|
17 |
+
class StreamHandler
|
18 |
+
{
|
19 |
+
private $lastHeaders = [];
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Sends an HTTP request.
|
23 |
+
*
|
24 |
+
* @param RequestInterface $request Request to send.
|
25 |
+
* @param array $options Request transfer options.
|
26 |
+
*
|
27 |
+
* @return PromiseInterface
|
28 |
+
*/
|
29 |
+
public function __invoke(RequestInterface $request, array $options)
|
30 |
+
{
|
31 |
+
// Sleep if there is a delay specified.
|
32 |
+
if (isset($options['delay'])) {
|
33 |
+
usleep($options['delay'] * 1000);
|
34 |
+
}
|
35 |
+
|
36 |
+
$startTime = isset($options['on_stats']) ? microtime(true) : null;
|
37 |
+
|
38 |
+
try {
|
39 |
+
// Does not support the expect header.
|
40 |
+
$request = $request->withoutHeader('Expect');
|
41 |
+
|
42 |
+
// Append a content-length header if body size is zero to match
|
43 |
+
// cURL's behavior.
|
44 |
+
if (0 === $request->getBody()->getSize()) {
|
45 |
+
$request = $request->withHeader('Content-Length', 0);
|
46 |
+
}
|
47 |
+
|
48 |
+
return $this->createResponse(
|
49 |
+
$request,
|
50 |
+
$options,
|
51 |
+
$this->createStream($request, $options),
|
52 |
+
$startTime
|
53 |
+
);
|
54 |
+
} catch (\InvalidArgumentException $e) {
|
55 |
+
throw $e;
|
56 |
+
} catch (\Exception $e) {
|
57 |
+
// Determine if the error was a networking error.
|
58 |
+
$message = $e->getMessage();
|
59 |
+
// This list can probably get more comprehensive.
|
60 |
+
if (strpos($message, 'getaddrinfo') // DNS lookup failed
|
61 |
+
|| strpos($message, 'Connection refused')
|
62 |
+
|| strpos($message, "couldn't connect to host") // error on HHVM
|
63 |
+
|| strpos($message, "connection attempt failed")
|
64 |
+
) {
|
65 |
+
$e = new ConnectException($e->getMessage(), $request, $e);
|
66 |
+
}
|
67 |
+
$e = RequestException::wrapException($request, $e);
|
68 |
+
$this->invokeStats($options, $request, $startTime, null, $e);
|
69 |
+
|
70 |
+
return \GuzzleHttp\Promise\rejection_for($e);
|
71 |
+
}
|
72 |
+
}
|
73 |
+
|
74 |
+
private function invokeStats(
|
75 |
+
array $options,
|
76 |
+
RequestInterface $request,
|
77 |
+
$startTime,
|
78 |
+
ResponseInterface $response = null,
|
79 |
+
$error = null
|
80 |
+
) {
|
81 |
+
if (isset($options['on_stats'])) {
|
82 |
+
$stats = new TransferStats(
|
83 |
+
$request,
|
84 |
+
$response,
|
85 |
+
microtime(true) - $startTime,
|
86 |
+
$error,
|
87 |
+
[]
|
88 |
+
);
|
89 |
+
call_user_func($options['on_stats'], $stats);
|
90 |
+
}
|
91 |
+
}
|
92 |
+
|
93 |
+
private function createResponse(
|
94 |
+
RequestInterface $request,
|
95 |
+
array $options,
|
96 |
+
$stream,
|
97 |
+
$startTime
|
98 |
+
) {
|
99 |
+
$hdrs = $this->lastHeaders;
|
100 |
+
$this->lastHeaders = [];
|
101 |
+
$parts = explode(' ', array_shift($hdrs), 3);
|
102 |
+
$ver = explode('/', $parts[0])[1];
|
103 |
+
$status = $parts[1];
|
104 |
+
$reason = isset($parts[2]) ? $parts[2] : null;
|
105 |
+
$headers = \GuzzleHttp\headers_from_lines($hdrs);
|
106 |
+
list($stream, $headers) = $this->checkDecode($options, $headers, $stream);
|
107 |
+
$stream = Psr7\stream_for($stream);
|
108 |
+
$sink = $stream;
|
109 |
+
|
110 |
+
if (strcasecmp('HEAD', $request->getMethod())) {
|
111 |
+
$sink = $this->createSink($stream, $options);
|
112 |
+
}
|
113 |
+
|
114 |
+
$response = new Psr7\Response($status, $headers, $sink, $ver, $reason);
|
115 |
+
|
116 |
+
if (isset($options['on_headers'])) {
|
117 |
+
try {
|
118 |
+
$options['on_headers']($response);
|
119 |
+
} catch (\Exception $e) {
|
120 |
+
$msg = 'An error was encountered during the on_headers event';
|
121 |
+
$ex = new RequestException($msg, $request, $response, $e);
|
122 |
+
return \GuzzleHttp\Promise\rejection_for($ex);
|
123 |
+
}
|
124 |
+
}
|
125 |
+
|
126 |
+
// Do not drain when the request is a HEAD request because they have
|
127 |
+
// no body.
|
128 |
+
if ($sink !== $stream) {
|
129 |
+
$this->drain(
|
130 |
+
$stream,
|
131 |
+
$sink,
|
132 |
+
$response->getHeaderLine('Content-Length')
|
133 |
+
);
|
134 |
+
}
|
135 |
+
|
136 |
+
$this->invokeStats($options, $request, $startTime, $response, null);
|
137 |
+
|
138 |
+
return new FulfilledPromise($response);
|
139 |
+
}
|
140 |
+
|
141 |
+
private function createSink(StreamInterface $stream, array $options)
|
142 |
+
{
|
143 |
+
if (!empty($options['stream'])) {
|
144 |
+
return $stream;
|
145 |
+
}
|
146 |
+
|
147 |
+
$sink = isset($options['sink'])
|
148 |
+
? $options['sink']
|
149 |
+
: fopen('php://temp', 'r+');
|
150 |
+
|
151 |
+
return is_string($sink)
|
152 |
+
? new Psr7\LazyOpenStream($sink, 'w+')
|
153 |
+
: Psr7\stream_for($sink);
|
154 |
+
}
|
155 |
+
|
156 |
+
private function checkDecode(array $options, array $headers, $stream)
|
157 |
+
{
|
158 |
+
// Automatically decode responses when instructed.
|
159 |
+
if (!empty($options['decode_content'])) {
|
160 |
+
$normalizedKeys = \GuzzleHttp\normalize_header_keys($headers);
|
161 |
+
if (isset($normalizedKeys['content-encoding'])) {
|
162 |
+
$encoding = $headers[$normalizedKeys['content-encoding']];
|
163 |
+
if ($encoding[0] === 'gzip' || $encoding[0] === 'deflate') {
|
164 |
+
$stream = new Psr7\InflateStream(
|
165 |
+
Psr7\stream_for($stream)
|
166 |
+
);
|
167 |
+
$headers['x-encoded-content-encoding']
|
168 |
+
= $headers[$normalizedKeys['content-encoding']];
|
169 |
+
// Remove content-encoding header
|
170 |
+
unset($headers[$normalizedKeys['content-encoding']]);
|
171 |
+
// Fix content-length header
|
172 |
+
if (isset($normalizedKeys['content-length'])) {
|
173 |
+
$headers['x-encoded-content-length']
|
174 |
+
= $headers[$normalizedKeys['content-length']];
|
175 |
+
|
176 |
+
$length = (int) $stream->getSize();
|
177 |
+
if ($length === 0) {
|
178 |
+
unset($headers[$normalizedKeys['content-length']]);
|
179 |
+
} else {
|
180 |
+
$headers[$normalizedKeys['content-length']] = [$length];
|
181 |
+
}
|
182 |
+
}
|
183 |
+
}
|
184 |
+
}
|
185 |
+
}
|
186 |
+
|
187 |
+
return [$stream, $headers];
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* Drains the source stream into the "sink" client option.
|
192 |
+
*
|
193 |
+
* @param StreamInterface $source
|
194 |
+
* @param StreamInterface $sink
|
195 |
+
* @param string $contentLength Header specifying the amount of
|
196 |
+
* data to read.
|
197 |
+
*
|
198 |
+
* @return StreamInterface
|
199 |
+
* @throws \RuntimeException when the sink option is invalid.
|
200 |
+
*/
|
201 |
+
private function drain(
|
202 |
+
StreamInterface $source,
|
203 |
+
StreamInterface $sink,
|
204 |
+
$contentLength
|
205 |
+
) {
|
206 |
+
// If a content-length header is provided, then stop reading once
|
207 |
+
// that number of bytes has been read. This can prevent infinitely
|
208 |
+
// reading from a stream when dealing with servers that do not honor
|
209 |
+
// Connection: Close headers.
|
210 |
+
Psr7\copy_to_stream(
|
211 |
+
$source,
|
212 |
+
$sink,
|
213 |
+
(strlen($contentLength) > 0 && (int) $contentLength > 0) ? (int) $contentLength : -1
|
214 |
+
);
|
215 |
+
|
216 |
+
$sink->seek(0);
|
217 |
+
$source->close();
|
218 |
+
|
219 |
+
return $sink;
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Create a resource and check to ensure it was created successfully
|
224 |
+
*
|
225 |
+
* @param callable $callback Callable that returns stream resource
|
226 |
+
*
|
227 |
+
* @return resource
|
228 |
+
* @throws \RuntimeException on error
|
229 |
+
*/
|
230 |
+
private function createResource(callable $callback)
|
231 |
+
{
|
232 |
+
$errors = null;
|
233 |
+
set_error_handler(function ($_, $msg, $file, $line) use (&$errors) {
|
234 |
+
$errors[] = [
|
235 |
+
'message' => $msg,
|
236 |
+
'file' => $file,
|
237 |
+
'line' => $line
|
238 |
+
];
|
239 |
+
return true;
|
240 |
+
});
|
241 |
+
|
242 |
+
$resource = $callback();
|
243 |
+
restore_error_handler();
|
244 |
+
|
245 |
+
if (!$resource) {
|
246 |
+
$message = 'Error creating resource: ';
|
247 |
+
foreach ($errors as $err) {
|
248 |
+
foreach ($err as $key => $value) {
|
249 |
+
$message .= "[$key] $value" . PHP_EOL;
|
250 |
+
}
|
251 |
+
}
|
252 |
+
throw new \RuntimeException(trim($message));
|
253 |
+
}
|
254 |
+
|
255 |
+
return $resource;
|
256 |
+
}
|
257 |
+
|
258 |
+
private function createStream(RequestInterface $request, array $options)
|
259 |
+
{
|
260 |
+
static $methods;
|
261 |
+
if (!$methods) {
|
262 |
+
$methods = array_flip(get_class_methods(__CLASS__));
|
263 |
+
}
|
264 |
+
|
265 |
+
// HTTP/1.1 streams using the PHP stream wrapper require a
|
266 |
+
// Connection: close header
|
267 |
+
if ($request->getProtocolVersion() == '1.1'
|
268 |
+
&& !$request->hasHeader('Connection')
|
269 |
+
) {
|
270 |
+
$request = $request->withHeader('Connection', 'close');
|
271 |
+
}
|
272 |
+
|
273 |
+
// Ensure SSL is verified by default
|
274 |
+
if (!isset($options['verify'])) {
|
275 |
+
$options['verify'] = true;
|
276 |
+
}
|
277 |
+
|
278 |
+
$params = [];
|
279 |
+
$context = $this->getDefaultContext($request);
|
280 |
+
|
281 |
+
if (isset($options['on_headers']) && !is_callable($options['on_headers'])) {
|
282 |
+
throw new \InvalidArgumentException('on_headers must be callable');
|
283 |
+
}
|
284 |
+
|
285 |
+
if (!empty($options)) {
|
286 |
+
foreach ($options as $key => $value) {
|
287 |
+
$method = "add_{$key}";
|
288 |
+
if (isset($methods[$method])) {
|
289 |
+
$this->{$method}($request, $context, $value, $params);
|
290 |
+
}
|
291 |
+
}
|
292 |
+
}
|
293 |
+
|
294 |
+
if (isset($options['stream_context'])) {
|
295 |
+
if (!is_array($options['stream_context'])) {
|
296 |
+
throw new \InvalidArgumentException('stream_context must be an array');
|
297 |
+
}
|
298 |
+
$context = array_replace_recursive(
|
299 |
+
$context,
|
300 |
+
$options['stream_context']
|
301 |
+
);
|
302 |
+
}
|
303 |
+
|
304 |
+
// Microsoft NTLM authentication only supported with curl handler
|
305 |
+
if (isset($options['auth'])
|
306 |
+
&& is_array($options['auth'])
|
307 |
+
&& isset($options['auth'][2])
|
308 |
+
&& 'ntlm' == $options['auth'][2]
|
309 |
+
) {
|
310 |
+
throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler');
|
311 |
+
}
|
312 |
+
|
313 |
+
$uri = $this->resolveHost($request, $options);
|
314 |
+
|
315 |
+
$context = $this->createResource(
|
316 |
+
function () use ($context, $params) {
|
317 |
+
return stream_context_create($context, $params);
|
318 |
+
}
|
319 |
+
);
|
320 |
+
|
321 |
+
return $this->createResource(
|
322 |
+
function () use ($uri, &$http_response_header, $context, $options) {
|
323 |
+
$resource = fopen((string) $uri, 'r', null, $context);
|
324 |
+
$this->lastHeaders = $http_response_header;
|
325 |
+
|
326 |
+
if (isset($options['read_timeout'])) {
|
327 |
+
$readTimeout = $options['read_timeout'];
|
328 |
+
$sec = (int) $readTimeout;
|
329 |
+
$usec = ($readTimeout - $sec) * 100000;
|
330 |
+
stream_set_timeout($resource, $sec, $usec);
|
331 |
+
}
|
332 |
+
|
333 |
+
return $resource;
|
334 |
+
}
|
335 |
+
);
|
336 |
+
}
|
337 |
+
|
338 |
+
private function resolveHost(RequestInterface $request, array $options)
|
339 |
+
{
|
340 |
+
$uri = $request->getUri();
|
341 |
+
|
342 |
+
if (isset($options['force_ip_resolve']) && !filter_var($uri->getHost(), FILTER_VALIDATE_IP)) {
|
343 |
+
if ('v4' === $options['force_ip_resolve']) {
|
344 |
+
$records = dns_get_record($uri->getHost(), DNS_A);
|
345 |
+
if (!isset($records[0]['ip'])) {
|
346 |
+
throw new ConnectException(sprintf("Could not resolve IPv4 address for host '%s'", $uri->getHost()), $request);
|
347 |
+
}
|
348 |
+
$uri = $uri->withHost($records[0]['ip']);
|
349 |
+
} elseif ('v6' === $options['force_ip_resolve']) {
|
350 |
+
$records = dns_get_record($uri->getHost(), DNS_AAAA);
|
351 |
+
if (!isset($records[0]['ipv6'])) {
|
352 |
+
throw new ConnectException(sprintf("Could not resolve IPv6 address for host '%s'", $uri->getHost()), $request);
|
353 |
+
}
|
354 |
+
$uri = $uri->withHost('[' . $records[0]['ipv6'] . ']');
|
355 |
+
}
|
356 |
+
}
|
357 |
+
|
358 |
+
return $uri;
|
359 |
+
}
|
360 |
+
|
361 |
+
private function getDefaultContext(RequestInterface $request)
|
362 |
+
{
|
363 |
+
$headers = '';
|
364 |
+
foreach ($request->getHeaders() as $name => $value) {
|
365 |
+
foreach ($value as $val) {
|
366 |
+
$headers .= "$name: $val\r\n";
|
367 |
+
}
|
368 |
+
}
|
369 |
+
|
370 |
+
$context = [
|
371 |
+
'http' => [
|
372 |
+
'method' => $request->getMethod(),
|
373 |
+
'header' => $headers,
|
374 |
+
'protocol_version' => $request->getProtocolVersion(),
|
375 |
+
'ignore_errors' => true,
|
376 |
+
'follow_location' => 0,
|
377 |
+
],
|
378 |
+
];
|
379 |
+
|
380 |
+
$body = (string) $request->getBody();
|
381 |
+
|
382 |
+
if (!empty($body)) {
|
383 |
+
$context['http']['content'] = $body;
|
384 |
+
// Prevent the HTTP handler from adding a Content-Type header.
|
385 |
+
if (!$request->hasHeader('Content-Type')) {
|
386 |
+
$context['http']['header'] .= "Content-Type:\r\n";
|
387 |
+
}
|
388 |
+
}
|
389 |
+
|
390 |
+
$context['http']['header'] = rtrim($context['http']['header']);
|
391 |
+
|
392 |
+
return $context;
|
393 |
+
}
|
394 |
+
|
395 |
+
private function add_proxy(RequestInterface $request, &$options, $value, &$params)
|
396 |
+
{
|
397 |
+
if (!is_array($value)) {
|
398 |
+
$options['http']['proxy'] = $value;
|
399 |
+
} else {
|
400 |
+
$scheme = $request->getUri()->getScheme();
|
401 |
+
if (isset($value[$scheme])) {
|
402 |
+
if (!isset($value['no'])
|
403 |
+
|| !\GuzzleHttp\is_host_in_noproxy(
|
404 |
+
$request->getUri()->getHost(),
|
405 |
+
$value['no']
|
406 |
+
)
|
407 |
+
) {
|
408 |
+
$options['http']['proxy'] = $value[$scheme];
|
409 |
+
}
|
410 |
+
}
|
411 |
+
}
|
412 |
+
}
|
413 |
+
|
414 |
+
private function add_timeout(RequestInterface $request, &$options, $value, &$params)
|
415 |
+
{
|
416 |
+
if ($value > 0) {
|
417 |
+
$options['http']['timeout'] = $value;
|
418 |
+
}
|
419 |
+
}
|
420 |
+
|
421 |
+
private function add_verify(RequestInterface $request, &$options, $value, &$params)
|
422 |
+
{
|
423 |
+
if ($value === true) {
|
424 |
+
// PHP 5.6 or greater will find the system cert by default. When
|
425 |
+
// < 5.6, use the Guzzle bundled cacert.
|
426 |
+
if (PHP_VERSION_ID < 50600) {
|
427 |
+
$options['ssl']['cafile'] = \GuzzleHttp\default_ca_bundle();
|
428 |
+
}
|
429 |
+
} elseif (is_string($value)) {
|
430 |
+
$options['ssl']['cafile'] = $value;
|
431 |
+
if (!file_exists($value)) {
|
432 |
+
throw new \RuntimeException("SSL CA bundle not found: $value");
|
433 |
+
}
|
434 |
+
} elseif ($value === false) {
|
435 |
+
$options['ssl']['verify_peer'] = false;
|
436 |
+
$options['ssl']['verify_peer_name'] = false;
|
437 |
+
return;
|
438 |
+
} else {
|
439 |
+
throw new \InvalidArgumentException('Invalid verify request option');
|
440 |
+
}
|
441 |
+
|
442 |
+
$options['ssl']['verify_peer'] = true;
|
443 |
+
$options['ssl']['verify_peer_name'] = true;
|
444 |
+
$options['ssl']['allow_self_signed'] = false;
|
445 |
+
}
|
446 |
+
|
447 |
+
private function add_cert(RequestInterface $request, &$options, $value, &$params)
|
448 |
+
{
|
449 |
+
if (is_array($value)) {
|
450 |
+
$options['ssl']['passphrase'] = $value[1];
|
451 |
+
$value = $value[0];
|
452 |
+
}
|
453 |
+
|
454 |
+
if (!file_exists($value)) {
|
455 |
+
throw new \RuntimeException("SSL certificate not found: {$value}");
|
456 |
+
}
|
457 |
+
|
458 |
+
$options['ssl']['local_cert'] = $value;
|
459 |
+
}
|
460 |
+
|
461 |
+
private function add_progress(RequestInterface $request, &$options, $value, &$params)
|
462 |
+
{
|
463 |
+
$this->addNotification(
|
464 |
+
$params,
|
465 |
+
function ($code, $a, $b, $c, $transferred, $total) use ($value) {
|
466 |
+
if ($code == STREAM_NOTIFY_PROGRESS) {
|
467 |
+
$value($total, $transferred, null, null);
|
468 |
+
}
|
469 |
+
}
|
470 |
+
);
|
471 |
+
}
|
472 |
+
|
473 |
+
private function add_debug(RequestInterface $request, &$options, $value, &$params)
|
474 |
+
{
|
475 |
+
if ($value === false) {
|
476 |
+
return;
|
477 |
+
}
|
478 |
+
|
479 |
+
static $map = [
|
480 |
+
STREAM_NOTIFY_CONNECT => 'CONNECT',
|
481 |
+
STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED',
|
482 |
+
STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT',
|
483 |
+
STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS',
|
484 |
+
STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS',
|
485 |
+
STREAM_NOTIFY_REDIRECTED => 'REDIRECTED',
|
486 |
+
STREAM_NOTIFY_PROGRESS => 'PROGRESS',
|
487 |
+
STREAM_NOTIFY_FAILURE => 'FAILURE',
|
488 |
+
STREAM_NOTIFY_COMPLETED => 'COMPLETED',
|
489 |
+
STREAM_NOTIFY_RESOLVE => 'RESOLVE',
|
490 |
+
];
|
491 |
+
static $args = ['severity', 'message', 'message_code',
|
492 |
+
'bytes_transferred', 'bytes_max'];
|
493 |
+
|
494 |
+
$value = \GuzzleHttp\debug_resource($value);
|
495 |
+
$ident = $request->getMethod() . ' ' . $request->getUri()->withFragment('');
|
496 |
+
$this->addNotification(
|
497 |
+
$params,
|
498 |
+
function () use ($ident, $value, $map, $args) {
|
499 |
+
$passed = func_get_args();
|
500 |
+
$code = array_shift($passed);
|
501 |
+
fprintf($value, '<%s> [%s] ', $ident, $map[$code]);
|
502 |
+
foreach (array_filter($passed) as $i => $v) {
|
503 |
+
fwrite($value, $args[$i] . ': "' . $v . '" ');
|
504 |
+
}
|
505 |
+
fwrite($value, "\n");
|
506 |
+
}
|
507 |
+
);
|
508 |
+
}
|
509 |
+
|
510 |
+
private function addNotification(array &$params, callable $notify)
|
511 |
+
{
|
512 |
+
// Wrap the existing function if needed.
|
513 |
+
if (!isset($params['notification'])) {
|
514 |
+
$params['notification'] = $notify;
|
515 |
+
} else {
|
516 |
+
$params['notification'] = $this->callArray([
|
517 |
+
$params['notification'],
|
518 |
+
$notify
|
519 |
+
]);
|
520 |
+
}
|
521 |
+
}
|
522 |
+
|
523 |
+
private function callArray(array $functions)
|
524 |
+
{
|
525 |
+
return function () use ($functions) {
|
526 |
+
$args = func_get_args();
|
527 |
+
foreach ($functions as $fn) {
|
528 |
+
call_user_func_array($fn, $args);
|
529 |
+
}
|
530 |
+
};
|
531 |
+
}
|
532 |
+
}
|
vendor/guzzlehttp/guzzle/src/HandlerStack.php
CHANGED
@@ -1,273 +1,273 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp;
|
3 |
-
|
4 |
-
use Psr\Http\Message\RequestInterface;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* Creates a composed Guzzle handler function by stacking middlewares on top of
|
8 |
-
* an HTTP handler function.
|
9 |
-
*/
|
10 |
-
class HandlerStack
|
11 |
-
{
|
12 |
-
/** @var callable */
|
13 |
-
private $handler;
|
14 |
-
|
15 |
-
/** @var array */
|
16 |
-
private $stack = [];
|
17 |
-
|
18 |
-
/** @var callable|null */
|
19 |
-
private $cached;
|
20 |
-
|
21 |
-
/**
|
22 |
-
* Creates a default handler stack that can be used by clients.
|
23 |
-
*
|
24 |
-
* The returned handler will wrap the provided handler or use the most
|
25 |
-
* appropriate default handler for your system. The returned HandlerStack has
|
26 |
-
* support for cookies, redirects, HTTP error exceptions, and preparing a body
|
27 |
-
* before sending.
|
28 |
-
*
|
29 |
-
* The returned handler stack can be passed to a client in the "handler"
|
30 |
-
* option.
|
31 |
-
*
|
32 |
-
* @param callable $handler HTTP handler function to use with the stack. If no
|
33 |
-
* handler is provided, the best handler for your
|
34 |
-
* system will be utilized.
|
35 |
-
*
|
36 |
-
* @return HandlerStack
|
37 |
-
*/
|
38 |
-
public static function create(callable $handler = null)
|
39 |
-
{
|
40 |
-
$stack = new self($handler ?: choose_handler());
|
41 |
-
$stack->push(Middleware::httpErrors(), 'http_errors');
|
42 |
-
$stack->push(Middleware::redirect(), 'allow_redirects');
|
43 |
-
$stack->push(Middleware::cookies(), 'cookies');
|
44 |
-
$stack->push(Middleware::prepareBody(), 'prepare_body');
|
45 |
-
|
46 |
-
return $stack;
|
47 |
-
}
|
48 |
-
|
49 |
-
/**
|
50 |
-
* @param callable $handler Underlying HTTP handler.
|
51 |
-
*/
|
52 |
-
public function __construct(callable $handler = null)
|
53 |
-
{
|
54 |
-
$this->handler = $handler;
|
55 |
-
}
|
56 |
-
|
57 |
-
/**
|
58 |
-
* Invokes the handler stack as a composed handler
|
59 |
-
*
|
60 |
-
* @param RequestInterface $request
|
61 |
-
* @param array $options
|
62 |
-
*/
|
63 |
-
public function __invoke(RequestInterface $request, array $options)
|
64 |
-
{
|
65 |
-
$handler = $this->resolve();
|
66 |
-
|
67 |
-
return $handler($request, $options);
|
68 |
-
}
|
69 |
-
|
70 |
-
/**
|
71 |
-
* Dumps a string representation of the stack.
|
72 |
-
*
|
73 |
-
* @return string
|
74 |
-
*/
|
75 |
-
public function __toString()
|
76 |
-
{
|
77 |
-
$depth = 0;
|
78 |
-
$stack = [];
|
79 |
-
if ($this->handler) {
|
80 |
-
$stack[] = "0) Handler: " . $this->debugCallable($this->handler);
|
81 |
-
}
|
82 |
-
|
83 |
-
$result = '';
|
84 |
-
foreach (array_reverse($this->stack) as $tuple) {
|
85 |
-
$depth++;
|
86 |
-
$str = "{$depth}) Name: '{$tuple[1]}', ";
|
87 |
-
$str .= "Function: " . $this->debugCallable($tuple[0]);
|
88 |
-
$result = "> {$str}\n{$result}";
|
89 |
-
$stack[] = $str;
|
90 |
-
}
|
91 |
-
|
92 |
-
foreach (array_keys($stack) as $k) {
|
93 |
-
$result .= "< {$stack[$k]}\n";
|
94 |
-
}
|
95 |
-
|
96 |
-
return $result;
|
97 |
-
}
|
98 |
-
|
99 |
-
/**
|
100 |
-
* Set the HTTP handler that actually returns a promise.
|
101 |
-
*
|
102 |
-
* @param callable $handler Accepts a request and array of options and
|
103 |
-
* returns a Promise.
|
104 |
-
*/
|
105 |
-
public function setHandler(callable $handler)
|
106 |
-
{
|
107 |
-
$this->handler = $handler;
|
108 |
-
$this->cached = null;
|
109 |
-
}
|
110 |
-
|
111 |
-
/**
|
112 |
-
* Returns true if the builder has a handler.
|
113 |
-
*
|
114 |
-
* @return bool
|
115 |
-
*/
|
116 |
-
public function hasHandler()
|
117 |
-
{
|
118 |
-
return (bool) $this->handler;
|
119 |
-
}
|
120 |
-
|
121 |
-
/**
|
122 |
-
* Unshift a middleware to the bottom of the stack.
|
123 |
-
*
|
124 |
-
* @param callable $middleware Middleware function
|
125 |
-
* @param string $name Name to register for this middleware.
|
126 |
-
*/
|
127 |
-
public function unshift(callable $middleware, $name = null)
|
128 |
-
{
|
129 |
-
array_unshift($this->stack, [$middleware, $name]);
|
130 |
-
$this->cached = null;
|
131 |
-
}
|
132 |
-
|
133 |
-
/**
|
134 |
-
* Push a middleware to the top of the stack.
|
135 |
-
*
|
136 |
-
* @param callable $middleware Middleware function
|
137 |
-
* @param string $name Name to register for this middleware.
|
138 |
-
*/
|
139 |
-
public function push(callable $middleware, $name = '')
|
140 |
-
{
|
141 |
-
$this->stack[] = [$middleware, $name];
|
142 |
-
$this->cached = null;
|
143 |
-
}
|
144 |
-
|
145 |
-
/**
|
146 |
-
* Add a middleware before another middleware by name.
|
147 |
-
*
|
148 |
-
* @param string $findName Middleware to find
|
149 |
-
* @param callable $middleware Middleware function
|
150 |
-
* @param string $withName Name to register for this middleware.
|
151 |
-
*/
|
152 |
-
public function before($findName, callable $middleware, $withName = '')
|
153 |
-
{
|
154 |
-
$this->splice($findName, $withName, $middleware, true);
|
155 |
-
}
|
156 |
-
|
157 |
-
/**
|
158 |
-
* Add a middleware after another middleware by name.
|
159 |
-
*
|
160 |
-
* @param string $findName Middleware to find
|
161 |
-
* @param callable $middleware Middleware function
|
162 |
-
* @param string $withName Name to register for this middleware.
|
163 |
-
*/
|
164 |
-
public function after($findName, callable $middleware, $withName = '')
|
165 |
-
{
|
166 |
-
$this->splice($findName, $withName, $middleware, false);
|
167 |
-
}
|
168 |
-
|
169 |
-
/**
|
170 |
-
* Remove a middleware by instance or name from the stack.
|
171 |
-
*
|
172 |
-
* @param callable|string $remove Middleware to remove by instance or name.
|
173 |
-
*/
|
174 |
-
public function remove($remove)
|
175 |
-
{
|
176 |
-
$this->cached = null;
|
177 |
-
$idx = is_callable($remove) ? 0 : 1;
|
178 |
-
$this->stack = array_values(array_filter(
|
179 |
-
$this->stack,
|
180 |
-
function ($tuple) use ($idx, $remove) {
|
181 |
-
return $tuple[$idx] !== $remove;
|
182 |
-
}
|
183 |
-
));
|
184 |
-
}
|
185 |
-
|
186 |
-
/**
|
187 |
-
* Compose the middleware and handler into a single callable function.
|
188 |
-
*
|
189 |
-
* @return callable
|
190 |
-
*/
|
191 |
-
public function resolve()
|
192 |
-
{
|
193 |
-
if (!$this->cached) {
|
194 |
-
if (!($prev = $this->handler)) {
|
195 |
-
throw new \LogicException('No handler has been specified');
|
196 |
-
}
|
197 |
-
|
198 |
-
foreach (array_reverse($this->stack) as $fn) {
|
199 |
-
$prev = $fn[0]($prev);
|
200 |
-
}
|
201 |
-
|
202 |
-
$this->cached = $prev;
|
203 |
-
}
|
204 |
-
|
205 |
-
return $this->cached;
|
206 |
-
}
|
207 |
-
|
208 |
-
/**
|
209 |
-
* @param $name
|
210 |
-
* @return int
|
211 |
-
*/
|
212 |
-
private function findByName($name)
|
213 |
-
{
|
214 |
-
foreach ($this->stack as $k => $v) {
|
215 |
-
if ($v[1] === $name) {
|
216 |
-
return $k;
|
217 |
-
}
|
218 |
-
}
|
219 |
-
|
220 |
-
throw new \InvalidArgumentException("Middleware not found: $name");
|
221 |
-
}
|
222 |
-
|
223 |
-
/**
|
224 |
-
* Splices a function into the middleware list at a specific position.
|
225 |
-
*
|
226 |
-
* @param $findName
|
227 |
-
* @param $withName
|
228 |
-
* @param callable $middleware
|
229 |
-
* @param $before
|
230 |
-
*/
|
231 |
-
private function splice($findName, $withName, callable $middleware, $before)
|
232 |
-
{
|
233 |
-
$this->cached = null;
|
234 |
-
$idx = $this->findByName($findName);
|
235 |
-
$tuple = [$middleware, $withName];
|
236 |
-
|
237 |
-
if ($before) {
|
238 |
-
if ($idx === 0) {
|
239 |
-
array_unshift($this->stack, $tuple);
|
240 |
-
} else {
|
241 |
-
$replacement = [$tuple, $this->stack[$idx]];
|
242 |
-
array_splice($this->stack, $idx, 1, $replacement);
|
243 |
-
}
|
244 |
-
} elseif ($idx === count($this->stack) - 1) {
|
245 |
-
$this->stack[] = $tuple;
|
246 |
-
} else {
|
247 |
-
$replacement = [$this->stack[$idx], $tuple];
|
248 |
-
array_splice($this->stack, $idx, 1, $replacement);
|
249 |
-
}
|
250 |
-
}
|
251 |
-
|
252 |
-
/**
|
253 |
-
* Provides a debug string for a given callable.
|
254 |
-
*
|
255 |
-
* @param array|callable $fn Function to write as a string.
|
256 |
-
*
|
257 |
-
* @return string
|
258 |
-
*/
|
259 |
-
private function debugCallable($fn)
|
260 |
-
{
|
261 |
-
if (is_string($fn)) {
|
262 |
-
return "callable({$fn})";
|
263 |
-
}
|
264 |
-
|
265 |
-
if (is_array($fn)) {
|
266 |
-
return is_string($fn[0])
|
267 |
-
? "callable({$fn[0]}::{$fn[1]})"
|
268 |
-
: "callable(['" . get_class($fn[0]) . "', '{$fn[1]}'])";
|
269 |
-
}
|
270 |
-
|
271 |
-
return 'callable(' . spl_object_hash($fn) . ')';
|
272 |
-
}
|
273 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp;
|
3 |
+
|
4 |
+
use Psr\Http\Message\RequestInterface;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Creates a composed Guzzle handler function by stacking middlewares on top of
|
8 |
+
* an HTTP handler function.
|
9 |
+
*/
|
10 |
+
class HandlerStack
|
11 |
+
{
|
12 |
+
/** @var callable */
|
13 |
+
private $handler;
|
14 |
+
|
15 |
+
/** @var array */
|
16 |
+
private $stack = [];
|
17 |
+
|
18 |
+
/** @var callable|null */
|
19 |
+
private $cached;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Creates a default handler stack that can be used by clients.
|
23 |
+
*
|
24 |
+
* The returned handler will wrap the provided handler or use the most
|
25 |
+
* appropriate default handler for your system. The returned HandlerStack has
|
26 |
+
* support for cookies, redirects, HTTP error exceptions, and preparing a body
|
27 |
+
* before sending.
|
28 |
+
*
|
29 |
+
* The returned handler stack can be passed to a client in the "handler"
|
30 |
+
* option.
|
31 |
+
*
|
32 |
+
* @param callable $handler HTTP handler function to use with the stack. If no
|
33 |
+
* handler is provided, the best handler for your
|
34 |
+
* system will be utilized.
|
35 |
+
*
|
36 |
+
* @return HandlerStack
|
37 |
+
*/
|
38 |
+
public static function create(callable $handler = null)
|
39 |
+
{
|
40 |
+
$stack = new self($handler ?: choose_handler());
|
41 |
+
$stack->push(Middleware::httpErrors(), 'http_errors');
|
42 |
+
$stack->push(Middleware::redirect(), 'allow_redirects');
|
43 |
+
$stack->push(Middleware::cookies(), 'cookies');
|
44 |
+
$stack->push(Middleware::prepareBody(), 'prepare_body');
|
45 |
+
|
46 |
+
return $stack;
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* @param callable $handler Underlying HTTP handler.
|
51 |
+
*/
|
52 |
+
public function __construct(callable $handler = null)
|
53 |
+
{
|
54 |
+
$this->handler = $handler;
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Invokes the handler stack as a composed handler
|
59 |
+
*
|
60 |
+
* @param RequestInterface $request
|
61 |
+
* @param array $options
|
62 |
+
*/
|
63 |
+
public function __invoke(RequestInterface $request, array $options)
|
64 |
+
{
|
65 |
+
$handler = $this->resolve();
|
66 |
+
|
67 |
+
return $handler($request, $options);
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Dumps a string representation of the stack.
|
72 |
+
*
|
73 |
+
* @return string
|
74 |
+
*/
|
75 |
+
public function __toString()
|
76 |
+
{
|
77 |
+
$depth = 0;
|
78 |
+
$stack = [];
|
79 |
+
if ($this->handler) {
|
80 |
+
$stack[] = "0) Handler: " . $this->debugCallable($this->handler);
|
81 |
+
}
|
82 |
+
|
83 |
+
$result = '';
|
84 |
+
foreach (array_reverse($this->stack) as $tuple) {
|
85 |
+
$depth++;
|
86 |
+
$str = "{$depth}) Name: '{$tuple[1]}', ";
|
87 |
+
$str .= "Function: " . $this->debugCallable($tuple[0]);
|
88 |
+
$result = "> {$str}\n{$result}";
|
89 |
+
$stack[] = $str;
|
90 |
+
}
|
91 |
+
|
92 |
+
foreach (array_keys($stack) as $k) {
|
93 |
+
$result .= "< {$stack[$k]}\n";
|
94 |
+
}
|
95 |
+
|
96 |
+
return $result;
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Set the HTTP handler that actually returns a promise.
|
101 |
+
*
|
102 |
+
* @param callable $handler Accepts a request and array of options and
|
103 |
+
* returns a Promise.
|
104 |
+
*/
|
105 |
+
public function setHandler(callable $handler)
|
106 |
+
{
|
107 |
+
$this->handler = $handler;
|
108 |
+
$this->cached = null;
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Returns true if the builder has a handler.
|
113 |
+
*
|
114 |
+
* @return bool
|
115 |
+
*/
|
116 |
+
public function hasHandler()
|
117 |
+
{
|
118 |
+
return (bool) $this->handler;
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* Unshift a middleware to the bottom of the stack.
|
123 |
+
*
|
124 |
+
* @param callable $middleware Middleware function
|
125 |
+
* @param string $name Name to register for this middleware.
|
126 |
+
*/
|
127 |
+
public function unshift(callable $middleware, $name = null)
|
128 |
+
{
|
129 |
+
array_unshift($this->stack, [$middleware, $name]);
|
130 |
+
$this->cached = null;
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Push a middleware to the top of the stack.
|
135 |
+
*
|
136 |
+
* @param callable $middleware Middleware function
|
137 |
+
* @param string $name Name to register for this middleware.
|
138 |
+
*/
|
139 |
+
public function push(callable $middleware, $name = '')
|
140 |
+
{
|
141 |
+
$this->stack[] = [$middleware, $name];
|
142 |
+
$this->cached = null;
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Add a middleware before another middleware by name.
|
147 |
+
*
|
148 |
+
* @param string $findName Middleware to find
|
149 |
+
* @param callable $middleware Middleware function
|
150 |
+
* @param string $withName Name to register for this middleware.
|
151 |
+
*/
|
152 |
+
public function before($findName, callable $middleware, $withName = '')
|
153 |
+
{
|
154 |
+
$this->splice($findName, $withName, $middleware, true);
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Add a middleware after another middleware by name.
|
159 |
+
*
|
160 |
+
* @param string $findName Middleware to find
|
161 |
+
* @param callable $middleware Middleware function
|
162 |
+
* @param string $withName Name to register for this middleware.
|
163 |
+
*/
|
164 |
+
public function after($findName, callable $middleware, $withName = '')
|
165 |
+
{
|
166 |
+
$this->splice($findName, $withName, $middleware, false);
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* Remove a middleware by instance or name from the stack.
|
171 |
+
*
|
172 |
+
* @param callable|string $remove Middleware to remove by instance or name.
|
173 |
+
*/
|
174 |
+
public function remove($remove)
|
175 |
+
{
|
176 |
+
$this->cached = null;
|
177 |
+
$idx = is_callable($remove) ? 0 : 1;
|
178 |
+
$this->stack = array_values(array_filter(
|
179 |
+
$this->stack,
|
180 |
+
function ($tuple) use ($idx, $remove) {
|
181 |
+
return $tuple[$idx] !== $remove;
|
182 |
+
}
|
183 |
+
));
|
184 |
+
}
|
185 |
+
|
186 |
+
/**
|
187 |
+
* Compose the middleware and handler into a single callable function.
|
188 |
+
*
|
189 |
+
* @return callable
|
190 |
+
*/
|
191 |
+
public function resolve()
|
192 |
+
{
|
193 |
+
if (!$this->cached) {
|
194 |
+
if (!($prev = $this->handler)) {
|
195 |
+
throw new \LogicException('No handler has been specified');
|
196 |
+
}
|
197 |
+
|
198 |
+
foreach (array_reverse($this->stack) as $fn) {
|
199 |
+
$prev = $fn[0]($prev);
|
200 |
+
}
|
201 |
+
|
202 |
+
$this->cached = $prev;
|
203 |
+
}
|
204 |
+
|
205 |
+
return $this->cached;
|
206 |
+
}
|
207 |
+
|
208 |
+
/**
|
209 |
+
* @param $name
|
210 |
+
* @return int
|
211 |
+
*/
|
212 |
+
private function findByName($name)
|
213 |
+
{
|
214 |
+
foreach ($this->stack as $k => $v) {
|
215 |
+
if ($v[1] === $name) {
|
216 |
+
return $k;
|
217 |
+
}
|
218 |
+
}
|
219 |
+
|
220 |
+
throw new \InvalidArgumentException("Middleware not found: $name");
|
221 |
+
}
|
222 |
+
|
223 |
+
/**
|
224 |
+
* Splices a function into the middleware list at a specific position.
|
225 |
+
*
|
226 |
+
* @param $findName
|
227 |
+
* @param $withName
|
228 |
+
* @param callable $middleware
|
229 |
+
* @param $before
|
230 |
+
*/
|
231 |
+
private function splice($findName, $withName, callable $middleware, $before)
|
232 |
+
{
|
233 |
+
$this->cached = null;
|
234 |
+
$idx = $this->findByName($findName);
|
235 |
+
$tuple = [$middleware, $withName];
|
236 |
+
|
237 |
+
if ($before) {
|
238 |
+
if ($idx === 0) {
|
239 |
+
array_unshift($this->stack, $tuple);
|
240 |
+
} else {
|
241 |
+
$replacement = [$tuple, $this->stack[$idx]];
|
242 |
+
array_splice($this->stack, $idx, 1, $replacement);
|
243 |
+
}
|
244 |
+
} elseif ($idx === count($this->stack) - 1) {
|
245 |
+
$this->stack[] = $tuple;
|
246 |
+
} else {
|
247 |
+
$replacement = [$this->stack[$idx], $tuple];
|
248 |
+
array_splice($this->stack, $idx, 1, $replacement);
|
249 |
+
}
|
250 |
+
}
|
251 |
+
|
252 |
+
/**
|
253 |
+
* Provides a debug string for a given callable.
|
254 |
+
*
|
255 |
+
* @param array|callable $fn Function to write as a string.
|
256 |
+
*
|
257 |
+
* @return string
|
258 |
+
*/
|
259 |
+
private function debugCallable($fn)
|
260 |
+
{
|
261 |
+
if (is_string($fn)) {
|
262 |
+
return "callable({$fn})";
|
263 |
+
}
|
264 |
+
|
265 |
+
if (is_array($fn)) {
|
266 |
+
return is_string($fn[0])
|
267 |
+
? "callable({$fn[0]}::{$fn[1]})"
|
268 |
+
: "callable(['" . get_class($fn[0]) . "', '{$fn[1]}'])";
|
269 |
+
}
|
270 |
+
|
271 |
+
return 'callable(' . spl_object_hash($fn) . ')';
|
272 |
+
}
|
273 |
+
}
|
vendor/guzzlehttp/guzzle/src/MessageFormatter.php
CHANGED
@@ -1,180 +1,180 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp;
|
3 |
-
|
4 |
-
use Psr\Http\Message\MessageInterface;
|
5 |
-
use Psr\Http\Message\RequestInterface;
|
6 |
-
use Psr\Http\Message\ResponseInterface;
|
7 |
-
|
8 |
-
/**
|
9 |
-
* Formats log messages using variable substitutions for requests, responses,
|
10 |
-
* and other transactional data.
|
11 |
-
*
|
12 |
-
* The following variable substitutions are supported:
|
13 |
-
*
|
14 |
-
* - {request}: Full HTTP request message
|
15 |
-
* - {response}: Full HTTP response message
|
16 |
-
* - {ts}: ISO 8601 date in GMT
|
17 |
-
* - {date_iso_8601} ISO 8601 date in GMT
|
18 |
-
* - {date_common_log} Apache common log date using the configured timezone.
|
19 |
-
* - {host}: Host of the request
|
20 |
-
* - {method}: Method of the request
|
21 |
-
* - {uri}: URI of the request
|
22 |
-
* - {version}: Protocol version
|
23 |
-
* - {target}: Request target of the request (path + query + fragment)
|
24 |
-
* - {hostname}: Hostname of the machine that sent the request
|
25 |
-
* - {code}: Status code of the response (if available)
|
26 |
-
* - {phrase}: Reason phrase of the response (if available)
|
27 |
-
* - {error}: Any error messages (if available)
|
28 |
-
* - {req_header_*}: Replace `*` with the lowercased name of a request header to add to the message
|
29 |
-
* - {res_header_*}: Replace `*` with the lowercased name of a response header to add to the message
|
30 |
-
* - {req_headers}: Request headers
|
31 |
-
* - {res_headers}: Response headers
|
32 |
-
* - {req_body}: Request body
|
33 |
-
* - {res_body}: Response body
|
34 |
-
*/
|
35 |
-
class MessageFormatter
|
36 |
-
{
|
37 |
-
/**
|
38 |
-
* Apache Common Log Format.
|
39 |
-
* @link http://httpd.apache.org/docs/2.4/logs.html#common
|
40 |
-
* @var string
|
41 |
-
*/
|
42 |
-
const CLF = "{hostname} {req_header_User-Agent} - [{date_common_log}] \"{method} {target} HTTP/{version}\" {code} {res_header_Content-Length}";
|
43 |
-
const DEBUG = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}";
|
44 |
-
const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}';
|
45 |
-
|
46 |
-
/** @var string Template used to format log messages */
|
47 |
-
private $template;
|
48 |
-
|
49 |
-
/**
|
50 |
-
* @param string $template Log message template
|
51 |
-
*/
|
52 |
-
public function __construct($template = self::CLF)
|
53 |
-
{
|
54 |
-
$this->template = $template ?: self::CLF;
|
55 |
-
}
|
56 |
-
|
57 |
-
/**
|
58 |
-
* Returns a formatted message string.
|
59 |
-
*
|
60 |
-
* @param RequestInterface $request Request that was sent
|
61 |
-
* @param ResponseInterface $response Response that was received
|
62 |
-
* @param \Exception $error Exception that was received
|
63 |
-
*
|
64 |
-
* @return string
|
65 |
-
*/
|
66 |
-
public function format(
|
67 |
-
RequestInterface $request,
|
68 |
-
ResponseInterface $response = null,
|
69 |
-
\Exception $error = null
|
70 |
-
) {
|
71 |
-
$cache = [];
|
72 |
-
|
73 |
-
return preg_replace_callback(
|
74 |
-
'/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
|
75 |
-
function (array $matches) use ($request, $response, $error, &$cache) {
|
76 |
-
if (isset($cache[$matches[1]])) {
|
77 |
-
return $cache[$matches[1]];
|
78 |
-
}
|
79 |
-
|
80 |
-
$result = '';
|
81 |
-
switch ($matches[1]) {
|
82 |
-
case 'request':
|
83 |
-
$result = Psr7\str($request);
|
84 |
-
break;
|
85 |
-
case 'response':
|
86 |
-
$result = $response ? Psr7\str($response) : '';
|
87 |
-
break;
|
88 |
-
case 'req_headers':
|
89 |
-
$result = trim($request->getMethod()
|
90 |
-
. ' ' . $request->getRequestTarget())
|
91 |
-
. ' HTTP/' . $request->getProtocolVersion() . "\r\n"
|
92 |
-
. $this->headers($request);
|
93 |
-
break;
|
94 |
-
case 'res_headers':
|
95 |
-
$result = $response ?
|
96 |
-
sprintf(
|
97 |
-
'HTTP/%s %d %s',
|
98 |
-
$response->getProtocolVersion(),
|
99 |
-
$response->getStatusCode(),
|
100 |
-
$response->getReasonPhrase()
|
101 |
-
) . "\r\n" . $this->headers($response)
|
102 |
-
: 'NULL';
|
103 |
-
break;
|
104 |
-
case 'req_body':
|
105 |
-
$result = $request->getBody();
|
106 |
-
break;
|
107 |
-
case 'res_body':
|
108 |
-
$result = $response ? $response->getBody() : 'NULL';
|
109 |
-
break;
|
110 |
-
case 'ts':
|
111 |
-
case 'date_iso_8601':
|
112 |
-
$result = gmdate('c');
|
113 |
-
break;
|
114 |
-
case 'date_common_log':
|
115 |
-
$result = date('d/M/Y:H:i:s O');
|
116 |
-
break;
|
117 |
-
case 'method':
|
118 |
-
$result = $request->getMethod();
|
119 |
-
break;
|
120 |
-
case 'version':
|
121 |
-
$result = $request->getProtocolVersion();
|
122 |
-
break;
|
123 |
-
case 'uri':
|
124 |
-
case 'url':
|
125 |
-
$result = $request->getUri();
|
126 |
-
break;
|
127 |
-
case 'target':
|
128 |
-
$result = $request->getRequestTarget();
|
129 |
-
break;
|
130 |
-
case 'req_version':
|
131 |
-
$result = $request->getProtocolVersion();
|
132 |
-
break;
|
133 |
-
case 'res_version':
|
134 |
-
$result = $response
|
135 |
-
? $response->getProtocolVersion()
|
136 |
-
: 'NULL';
|
137 |
-
break;
|
138 |
-
case 'host':
|
139 |
-
$result = $request->getHeaderLine('Host');
|
140 |
-
break;
|
141 |
-
case 'hostname':
|
142 |
-
$result = gethostname();
|
143 |
-
break;
|
144 |
-
case 'code':
|
145 |
-
$result = $response ? $response->getStatusCode() : 'NULL';
|
146 |
-
break;
|
147 |
-
case 'phrase':
|
148 |
-
$result = $response ? $response->getReasonPhrase() : 'NULL';
|
149 |
-
break;
|
150 |
-
case 'error':
|
151 |
-
$result = $error ? $error->getMessage() : 'NULL';
|
152 |
-
break;
|
153 |
-
default:
|
154 |
-
// handle prefixed dynamic headers
|
155 |
-
if (strpos($matches[1], 'req_header_') === 0) {
|
156 |
-
$result = $request->getHeaderLine(substr($matches[1], 11));
|
157 |
-
} elseif (strpos($matches[1], 'res_header_') === 0) {
|
158 |
-
$result = $response
|
159 |
-
? $response->getHeaderLine(substr($matches[1], 11))
|
160 |
-
: 'NULL';
|
161 |
-
}
|
162 |
-
}
|
163 |
-
|
164 |
-
$cache[$matches[1]] = $result;
|
165 |
-
return $result;
|
166 |
-
},
|
167 |
-
$this->template
|
168 |
-
);
|
169 |
-
}
|
170 |
-
|
171 |
-
private function headers(MessageInterface $message)
|
172 |
-
{
|
173 |
-
$result = '';
|
174 |
-
foreach ($message->getHeaders() as $name => $values) {
|
175 |
-
$result .= $name . ': ' . implode(', ', $values) . "\r\n";
|
176 |
-
}
|
177 |
-
|
178 |
-
return trim($result);
|
179 |
-
}
|
180 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp;
|
3 |
+
|
4 |
+
use Psr\Http\Message\MessageInterface;
|
5 |
+
use Psr\Http\Message\RequestInterface;
|
6 |
+
use Psr\Http\Message\ResponseInterface;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Formats log messages using variable substitutions for requests, responses,
|
10 |
+
* and other transactional data.
|
11 |
+
*
|
12 |
+
* The following variable substitutions are supported:
|
13 |
+
*
|
14 |
+
* - {request}: Full HTTP request message
|
15 |
+
* - {response}: Full HTTP response message
|
16 |
+
* - {ts}: ISO 8601 date in GMT
|
17 |
+
* - {date_iso_8601} ISO 8601 date in GMT
|
18 |
+
* - {date_common_log} Apache common log date using the configured timezone.
|
19 |
+
* - {host}: Host of the request
|
20 |
+
* - {method}: Method of the request
|
21 |
+
* - {uri}: URI of the request
|
22 |
+
* - {version}: Protocol version
|
23 |
+
* - {target}: Request target of the request (path + query + fragment)
|
24 |
+
* - {hostname}: Hostname of the machine that sent the request
|
25 |
+
* - {code}: Status code of the response (if available)
|
26 |
+
* - {phrase}: Reason phrase of the response (if available)
|
27 |
+
* - {error}: Any error messages (if available)
|
28 |
+
* - {req_header_*}: Replace `*` with the lowercased name of a request header to add to the message
|
29 |
+
* - {res_header_*}: Replace `*` with the lowercased name of a response header to add to the message
|
30 |
+
* - {req_headers}: Request headers
|
31 |
+
* - {res_headers}: Response headers
|
32 |
+
* - {req_body}: Request body
|
33 |
+
* - {res_body}: Response body
|
34 |
+
*/
|
35 |
+
class MessageFormatter
|
36 |
+
{
|
37 |
+
/**
|
38 |
+
* Apache Common Log Format.
|
39 |
+
* @link http://httpd.apache.org/docs/2.4/logs.html#common
|
40 |
+
* @var string
|
41 |
+
*/
|
42 |
+
const CLF = "{hostname} {req_header_User-Agent} - [{date_common_log}] \"{method} {target} HTTP/{version}\" {code} {res_header_Content-Length}";
|
43 |
+
const DEBUG = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}";
|
44 |
+
const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}';
|
45 |
+
|
46 |
+
/** @var string Template used to format log messages */
|
47 |
+
private $template;
|
48 |
+
|
49 |
+
/**
|
50 |
+
* @param string $template Log message template
|
51 |
+
*/
|
52 |
+
public function __construct($template = self::CLF)
|
53 |
+
{
|
54 |
+
$this->template = $template ?: self::CLF;
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Returns a formatted message string.
|
59 |
+
*
|
60 |
+
* @param RequestInterface $request Request that was sent
|
61 |
+
* @param ResponseInterface $response Response that was received
|
62 |
+
* @param \Exception $error Exception that was received
|
63 |
+
*
|
64 |
+
* @return string
|
65 |
+
*/
|
66 |
+
public function format(
|
67 |
+
RequestInterface $request,
|
68 |
+
ResponseInterface $response = null,
|
69 |
+
\Exception $error = null
|
70 |
+
) {
|
71 |
+
$cache = [];
|
72 |
+
|
73 |
+
return preg_replace_callback(
|
74 |
+
'/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
|
75 |
+
function (array $matches) use ($request, $response, $error, &$cache) {
|
76 |
+
if (isset($cache[$matches[1]])) {
|
77 |
+
return $cache[$matches[1]];
|
78 |
+
}
|
79 |
+
|
80 |
+
$result = '';
|
81 |
+
switch ($matches[1]) {
|
82 |
+
case 'request':
|
83 |
+
$result = Psr7\str($request);
|
84 |
+
break;
|
85 |
+
case 'response':
|
86 |
+
$result = $response ? Psr7\str($response) : '';
|
87 |
+
break;
|
88 |
+
case 'req_headers':
|
89 |
+
$result = trim($request->getMethod()
|
90 |
+
. ' ' . $request->getRequestTarget())
|
91 |
+
. ' HTTP/' . $request->getProtocolVersion() . "\r\n"
|
92 |
+
. $this->headers($request);
|
93 |
+
break;
|
94 |
+
case 'res_headers':
|
95 |
+
$result = $response ?
|
96 |
+
sprintf(
|
97 |
+
'HTTP/%s %d %s',
|
98 |
+
$response->getProtocolVersion(),
|
99 |
+
$response->getStatusCode(),
|
100 |
+
$response->getReasonPhrase()
|
101 |
+
) . "\r\n" . $this->headers($response)
|
102 |
+
: 'NULL';
|
103 |
+
break;
|
104 |
+
case 'req_body':
|
105 |
+
$result = $request->getBody();
|
106 |
+
break;
|
107 |
+
case 'res_body':
|
108 |
+
$result = $response ? $response->getBody() : 'NULL';
|
109 |
+
break;
|
110 |
+
case 'ts':
|
111 |
+
case 'date_iso_8601':
|
112 |
+
$result = gmdate('c');
|
113 |
+
break;
|
114 |
+
case 'date_common_log':
|
115 |
+
$result = date('d/M/Y:H:i:s O');
|
116 |
+
break;
|
117 |
+
case 'method':
|
118 |
+
$result = $request->getMethod();
|
119 |
+
break;
|
120 |
+
case 'version':
|
121 |
+
$result = $request->getProtocolVersion();
|
122 |
+
break;
|
123 |
+
case 'uri':
|
124 |
+
case 'url':
|
125 |
+
$result = $request->getUri();
|
126 |
+
break;
|
127 |
+
case 'target':
|
128 |
+
$result = $request->getRequestTarget();
|
129 |
+
break;
|
130 |
+
case 'req_version':
|
131 |
+
$result = $request->getProtocolVersion();
|
132 |
+
break;
|
133 |
+
case 'res_version':
|
134 |
+
$result = $response
|
135 |
+
? $response->getProtocolVersion()
|
136 |
+
: 'NULL';
|
137 |
+
break;
|
138 |
+
case 'host':
|
139 |
+
$result = $request->getHeaderLine('Host');
|
140 |
+
break;
|
141 |
+
case 'hostname':
|
142 |
+
$result = gethostname();
|
143 |
+
break;
|
144 |
+
case 'code':
|
145 |
+
$result = $response ? $response->getStatusCode() : 'NULL';
|
146 |
+
break;
|
147 |
+
case 'phrase':
|
148 |
+
$result = $response ? $response->getReasonPhrase() : 'NULL';
|
149 |
+
break;
|
150 |
+
case 'error':
|
151 |
+
$result = $error ? $error->getMessage() : 'NULL';
|
152 |
+
break;
|
153 |
+
default:
|
154 |
+
// handle prefixed dynamic headers
|
155 |
+
if (strpos($matches[1], 'req_header_') === 0) {
|
156 |
+
$result = $request->getHeaderLine(substr($matches[1], 11));
|
157 |
+
} elseif (strpos($matches[1], 'res_header_') === 0) {
|
158 |
+
$result = $response
|
159 |
+
? $response->getHeaderLine(substr($matches[1], 11))
|
160 |
+
: 'NULL';
|
161 |
+
}
|
162 |
+
}
|
163 |
+
|
164 |
+
$cache[$matches[1]] = $result;
|
165 |
+
return $result;
|
166 |
+
},
|
167 |
+
$this->template
|
168 |
+
);
|
169 |
+
}
|
170 |
+
|
171 |
+
private function headers(MessageInterface $message)
|
172 |
+
{
|
173 |
+
$result = '';
|
174 |
+
foreach ($message->getHeaders() as $name => $values) {
|
175 |
+
$result .= $name . ': ' . implode(', ', $values) . "\r\n";
|
176 |
+
}
|
177 |
+
|
178 |
+
return trim($result);
|
179 |
+
}
|
180 |
+
}
|
vendor/guzzlehttp/guzzle/src/Middleware.php
CHANGED
@@ -1,255 +1,255 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp;
|
3 |
-
|
4 |
-
use GuzzleHttp\Cookie\CookieJarInterface;
|
5 |
-
use GuzzleHttp\Exception\RequestException;
|
6 |
-
use GuzzleHttp\Promise\RejectedPromise;
|
7 |
-
use GuzzleHttp\Psr7;
|
8 |
-
use Psr\Http\Message\ResponseInterface;
|
9 |
-
use Psr\Log\LoggerInterface;
|
10 |
-
use Psr\Log\LogLevel;
|
11 |
-
|
12 |
-
/**
|
13 |
-
* Functions used to create and wrap handlers with handler middleware.
|
14 |
-
*/
|
15 |
-
final class Middleware
|
16 |
-
{
|
17 |
-
/**
|
18 |
-
* Middleware that adds cookies to requests.
|
19 |
-
*
|
20 |
-
* The options array must be set to a CookieJarInterface in order to use
|
21 |
-
* cookies. This is typically handled for you by a client.
|
22 |
-
*
|
23 |
-
* @return callable Returns a function that accepts the next handler.
|
24 |
-
*/
|
25 |
-
public static function cookies()
|
26 |
-
{
|
27 |
-
return function (callable $handler) {
|
28 |
-
return function ($request, array $options) use ($handler) {
|
29 |
-
if (empty($options['cookies'])) {
|
30 |
-
return $handler($request, $options);
|
31 |
-
} elseif (!($options['cookies'] instanceof CookieJarInterface)) {
|
32 |
-
throw new \InvalidArgumentException('cookies must be an instance of GuzzleHttp\Cookie\CookieJarInterface');
|
33 |
-
}
|
34 |
-
$cookieJar = $options['cookies'];
|
35 |
-
$request = $cookieJar->withCookieHeader($request);
|
36 |
-
return $handler($request, $options)
|
37 |
-
->then(
|
38 |
-
function ($response) use ($cookieJar, $request) {
|
39 |
-
$cookieJar->extractCookies($request, $response);
|
40 |
-
return $response;
|
41 |
-
}
|
42 |
-
);
|
43 |
-
};
|
44 |
-
};
|
45 |
-
}
|
46 |
-
|
47 |
-
/**
|
48 |
-
* Middleware that throws exceptions for 4xx or 5xx responses when the
|
49 |
-
* "http_error" request option is set to true.
|
50 |
-
*
|
51 |
-
* @return callable Returns a function that accepts the next handler.
|
52 |
-
*/
|
53 |
-
public static function httpErrors()
|
54 |
-
{
|
55 |
-
return function (callable $handler) {
|
56 |
-
return function ($request, array $options) use ($handler) {
|
57 |
-
if (empty($options['http_errors'])) {
|
58 |
-
return $handler($request, $options);
|
59 |
-
}
|
60 |
-
return $handler($request, $options)->then(
|
61 |
-
function (ResponseInterface $response) use ($request, $handler) {
|
62 |
-
$code = $response->getStatusCode();
|
63 |
-
if ($code < 400) {
|
64 |
-
return $response;
|
65 |
-
}
|
66 |
-
throw RequestException::create($request, $response);
|
67 |
-
}
|
68 |
-
);
|
69 |
-
};
|
70 |
-
};
|
71 |
-
}
|
72 |
-
|
73 |
-
/**
|
74 |
-
* Middleware that pushes history data to an ArrayAccess container.
|
75 |
-
*
|
76 |
-
* @param array|\ArrayAccess $container Container to hold the history (by reference).
|
77 |
-
*
|
78 |
-
* @return callable Returns a function that accepts the next handler.
|
79 |
-
* @throws \InvalidArgumentException if container is not an array or ArrayAccess.
|
80 |
-
*/
|
81 |
-
public static function history(&$container)
|
82 |
-
{
|
83 |
-
if (!is_array($container) && !$container instanceof \ArrayAccess) {
|
84 |
-
throw new \InvalidArgumentException('history container must be an array or object implementing ArrayAccess');
|
85 |
-
}
|
86 |
-
|
87 |
-
return function (callable $handler) use (&$container) {
|
88 |
-
return function ($request, array $options) use ($handler, &$container) {
|
89 |
-
return $handler($request, $options)->then(
|
90 |
-
function ($value) use ($request, &$container, $options) {
|
91 |
-
$container[] = [
|
92 |
-
'request' => $request,
|
93 |
-
'response' => $value,
|
94 |
-
'error' => null,
|
95 |
-
'options' => $options
|
96 |
-
];
|
97 |
-
return $value;
|
98 |
-
},
|
99 |
-
function ($reason) use ($request, &$container, $options) {
|
100 |
-
$container[] = [
|
101 |
-
'request' => $request,
|
102 |
-
'response' => null,
|
103 |
-
'error' => $reason,
|
104 |
-
'options' => $options
|
105 |
-
];
|
106 |
-
return \GuzzleHttp\Promise\rejection_for($reason);
|
107 |
-
}
|
108 |
-
);
|
109 |
-
};
|
110 |
-
};
|
111 |
-
}
|
112 |
-
|
113 |
-
/**
|
114 |
-
* Middleware that invokes a callback before and after sending a request.
|
115 |
-
*
|
116 |
-
* The provided listener cannot modify or alter the response. It simply
|
117 |
-
* "taps" into the chain to be notified before returning the promise. The
|
118 |
-
* before listener accepts a request and options array, and the after
|
119 |
-
* listener accepts a request, options array, and response promise.
|
120 |
-
*
|
121 |
-
* @param callable $before Function to invoke before forwarding the request.
|
122 |
-
* @param callable $after Function invoked after forwarding.
|
123 |
-
*
|
124 |
-
* @return callable Returns a function that accepts the next handler.
|
125 |
-
*/
|
126 |
-
public static function tap(callable $before = null, callable $after = null)
|
127 |
-
{
|
128 |
-
return function (callable $handler) use ($before, $after) {
|
129 |
-
return function ($request, array $options) use ($handler, $before, $after) {
|
130 |
-
if ($before) {
|
131 |
-
$before($request, $options);
|
132 |
-
}
|
133 |
-
$response = $handler($request, $options);
|
134 |
-
if ($after) {
|
135 |
-
$after($request, $options, $response);
|
136 |
-
}
|
137 |
-
return $response;
|
138 |
-
};
|
139 |
-
};
|
140 |
-
}
|
141 |
-
|
142 |
-
/**
|
143 |
-
* Middleware that handles request redirects.
|
144 |
-
*
|
145 |
-
* @return callable Returns a function that accepts the next handler.
|
146 |
-
*/
|
147 |
-
public static function redirect()
|
148 |
-
{
|
149 |
-
return function (callable $handler) {
|
150 |
-
return new RedirectMiddleware($handler);
|
151 |
-
};
|
152 |
-
}
|
153 |
-
|
154 |
-
/**
|
155 |
-
* Middleware that retries requests based on the boolean result of
|
156 |
-
* invoking the provided "decider" function.
|
157 |
-
*
|
158 |
-
* If no delay function is provided, a simple implementation of exponential
|
159 |
-
* backoff will be utilized.
|
160 |
-
*
|
161 |
-
* @param callable $decider Function that accepts the number of retries,
|
162 |
-
* a request, [response], and [exception] and
|
163 |
-
* returns true if the request is to be retried.
|
164 |
-
* @param callable $delay Function that accepts the number of retries and
|
165 |
-
* returns the number of milliseconds to delay.
|
166 |
-
*
|
167 |
-
* @return callable Returns a function that accepts the next handler.
|
168 |
-
*/
|
169 |
-
public static function retry(callable $decider, callable $delay = null)
|
170 |
-
{
|
171 |
-
return function (callable $handler) use ($decider, $delay) {
|
172 |
-
return new RetryMiddleware($decider, $handler, $delay);
|
173 |
-
};
|
174 |
-
}
|
175 |
-
|
176 |
-
/**
|
177 |
-
* Middleware that logs requests, responses, and errors using a message
|
178 |
-
* formatter.
|
179 |
-
*
|
180 |
-
* @param LoggerInterface $logger Logs messages.
|
181 |
-
* @param MessageFormatter $formatter Formatter used to create message strings.
|
182 |
-
* @param string $logLevel Level at which to log requests.
|
183 |
-
*
|
184 |
-
* @return callable Returns a function that accepts the next handler.
|
185 |
-
*/
|
186 |
-
public static function log(LoggerInterface $logger, MessageFormatter $formatter, $logLevel = LogLevel::INFO)
|
187 |
-
{
|
188 |
-
return function (callable $handler) use ($logger, $formatter, $logLevel) {
|
189 |
-
return function ($request, array $options) use ($handler, $logger, $formatter, $logLevel) {
|
190 |
-
return $handler($request, $options)->then(
|
191 |
-
function ($response) use ($logger, $request, $formatter, $logLevel) {
|
192 |
-
$message = $formatter->format($request, $response);
|
193 |
-
$logger->log($logLevel, $message);
|
194 |
-
return $response;
|
195 |
-
},
|
196 |
-
function ($reason) use ($logger, $request, $formatter) {
|
197 |
-
$response = $reason instanceof RequestException
|
198 |
-
? $reason->getResponse()
|
199 |
-
: null;
|
200 |
-
$message = $formatter->format($request, $response, $reason);
|
201 |
-
$logger->notice($message);
|
202 |
-
return \GuzzleHttp\Promise\rejection_for($reason);
|
203 |
-
}
|
204 |
-
);
|
205 |
-
};
|
206 |
-
};
|
207 |
-
}
|
208 |
-
|
209 |
-
/**
|
210 |
-
* This middleware adds a default content-type if possible, a default
|
211 |
-
* content-length or transfer-encoding header, and the expect header.
|
212 |
-
*
|
213 |
-
* @return callable
|
214 |
-
*/
|
215 |
-
public static function prepareBody()
|
216 |
-
{
|
217 |
-
return function (callable $handler) {
|
218 |
-
return new PrepareBodyMiddleware($handler);
|
219 |
-
};
|
220 |
-
}
|
221 |
-
|
222 |
-
/**
|
223 |
-
* Middleware that applies a map function to the request before passing to
|
224 |
-
* the next handler.
|
225 |
-
*
|
226 |
-
* @param callable $fn Function that accepts a RequestInterface and returns
|
227 |
-
* a RequestInterface.
|
228 |
-
* @return callable
|
229 |
-
*/
|
230 |
-
public static function mapRequest(callable $fn)
|
231 |
-
{
|
232 |
-
return function (callable $handler) use ($fn) {
|
233 |
-
return function ($request, array $options) use ($handler, $fn) {
|
234 |
-
return $handler($fn($request), $options);
|
235 |
-
};
|
236 |
-
};
|
237 |
-
}
|
238 |
-
|
239 |
-
/**
|
240 |
-
* Middleware that applies a map function to the resolved promise's
|
241 |
-
* response.
|
242 |
-
*
|
243 |
-
* @param callable $fn Function that accepts a ResponseInterface and
|
244 |
-
* returns a ResponseInterface.
|
245 |
-
* @return callable
|
246 |
-
*/
|
247 |
-
public static function mapResponse(callable $fn)
|
248 |
-
{
|
249 |
-
return function (callable $handler) use ($fn) {
|
250 |
-
return function ($request, array $options) use ($handler, $fn) {
|
251 |
-
return $handler($request, $options)->then($fn);
|
252 |
-
};
|
253 |
-
};
|
254 |
-
}
|
255 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp;
|
3 |
+
|
4 |
+
use GuzzleHttp\Cookie\CookieJarInterface;
|
5 |
+
use GuzzleHttp\Exception\RequestException;
|
6 |
+
use GuzzleHttp\Promise\RejectedPromise;
|
7 |
+
use GuzzleHttp\Psr7;
|
8 |
+
use Psr\Http\Message\ResponseInterface;
|
9 |
+
use Psr\Log\LoggerInterface;
|
10 |
+
use Psr\Log\LogLevel;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Functions used to create and wrap handlers with handler middleware.
|
14 |
+
*/
|
15 |
+
final class Middleware
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* Middleware that adds cookies to requests.
|
19 |
+
*
|
20 |
+
* The options array must be set to a CookieJarInterface in order to use
|
21 |
+
* cookies. This is typically handled for you by a client.
|
22 |
+
*
|
23 |
+
* @return callable Returns a function that accepts the next handler.
|
24 |
+
*/
|
25 |
+
public static function cookies()
|
26 |
+
{
|
27 |
+
return function (callable $handler) {
|
28 |
+
return function ($request, array $options) use ($handler) {
|
29 |
+
if (empty($options['cookies'])) {
|
30 |
+
return $handler($request, $options);
|
31 |
+
} elseif (!($options['cookies'] instanceof CookieJarInterface)) {
|
32 |
+
throw new \InvalidArgumentException('cookies must be an instance of GuzzleHttp\Cookie\CookieJarInterface');
|
33 |
+
}
|
34 |
+
$cookieJar = $options['cookies'];
|
35 |
+
$request = $cookieJar->withCookieHeader($request);
|
36 |
+
return $handler($request, $options)
|
37 |
+
->then(
|
38 |
+
function ($response) use ($cookieJar, $request) {
|
39 |
+
$cookieJar->extractCookies($request, $response);
|
40 |
+
return $response;
|
41 |
+
}
|
42 |
+
);
|
43 |
+
};
|
44 |
+
};
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Middleware that throws exceptions for 4xx or 5xx responses when the
|
49 |
+
* "http_error" request option is set to true.
|
50 |
+
*
|
51 |
+
* @return callable Returns a function that accepts the next handler.
|
52 |
+
*/
|
53 |
+
public static function httpErrors()
|
54 |
+
{
|
55 |
+
return function (callable $handler) {
|
56 |
+
return function ($request, array $options) use ($handler) {
|
57 |
+
if (empty($options['http_errors'])) {
|
58 |
+
return $handler($request, $options);
|
59 |
+
}
|
60 |
+
return $handler($request, $options)->then(
|
61 |
+
function (ResponseInterface $response) use ($request, $handler) {
|
62 |
+
$code = $response->getStatusCode();
|
63 |
+
if ($code < 400) {
|
64 |
+
return $response;
|
65 |
+
}
|
66 |
+
throw RequestException::create($request, $response);
|
67 |
+
}
|
68 |
+
);
|
69 |
+
};
|
70 |
+
};
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Middleware that pushes history data to an ArrayAccess container.
|
75 |
+
*
|
76 |
+
* @param array|\ArrayAccess $container Container to hold the history (by reference).
|
77 |
+
*
|
78 |
+
* @return callable Returns a function that accepts the next handler.
|
79 |
+
* @throws \InvalidArgumentException if container is not an array or ArrayAccess.
|
80 |
+
*/
|
81 |
+
public static function history(&$container)
|
82 |
+
{
|
83 |
+
if (!is_array($container) && !$container instanceof \ArrayAccess) {
|
84 |
+
throw new \InvalidArgumentException('history container must be an array or object implementing ArrayAccess');
|
85 |
+
}
|
86 |
+
|
87 |
+
return function (callable $handler) use (&$container) {
|
88 |
+
return function ($request, array $options) use ($handler, &$container) {
|
89 |
+
return $handler($request, $options)->then(
|
90 |
+
function ($value) use ($request, &$container, $options) {
|
91 |
+
$container[] = [
|
92 |
+
'request' => $request,
|
93 |
+
'response' => $value,
|
94 |
+
'error' => null,
|
95 |
+
'options' => $options
|
96 |
+
];
|
97 |
+
return $value;
|
98 |
+
},
|
99 |
+
function ($reason) use ($request, &$container, $options) {
|
100 |
+
$container[] = [
|
101 |
+
'request' => $request,
|
102 |
+
'response' => null,
|
103 |
+
'error' => $reason,
|
104 |
+
'options' => $options
|
105 |
+
];
|
106 |
+
return \GuzzleHttp\Promise\rejection_for($reason);
|
107 |
+
}
|
108 |
+
);
|
109 |
+
};
|
110 |
+
};
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Middleware that invokes a callback before and after sending a request.
|
115 |
+
*
|
116 |
+
* The provided listener cannot modify or alter the response. It simply
|
117 |
+
* "taps" into the chain to be notified before returning the promise. The
|
118 |
+
* before listener accepts a request and options array, and the after
|
119 |
+
* listener accepts a request, options array, and response promise.
|
120 |
+
*
|
121 |
+
* @param callable $before Function to invoke before forwarding the request.
|
122 |
+
* @param callable $after Function invoked after forwarding.
|
123 |
+
*
|
124 |
+
* @return callable Returns a function that accepts the next handler.
|
125 |
+
*/
|
126 |
+
public static function tap(callable $before = null, callable $after = null)
|
127 |
+
{
|
128 |
+
return function (callable $handler) use ($before, $after) {
|
129 |
+
return function ($request, array $options) use ($handler, $before, $after) {
|
130 |
+
if ($before) {
|
131 |
+
$before($request, $options);
|
132 |
+
}
|
133 |
+
$response = $handler($request, $options);
|
134 |
+
if ($after) {
|
135 |
+
$after($request, $options, $response);
|
136 |
+
}
|
137 |
+
return $response;
|
138 |
+
};
|
139 |
+
};
|
140 |
+
}
|
141 |
+
|
142 |
+
/**
|
143 |
+
* Middleware that handles request redirects.
|
144 |
+
*
|
145 |
+
* @return callable Returns a function that accepts the next handler.
|
146 |
+
*/
|
147 |
+
public static function redirect()
|
148 |
+
{
|
149 |
+
return function (callable $handler) {
|
150 |
+
return new RedirectMiddleware($handler);
|
151 |
+
};
|
152 |
+
}
|
153 |
+
|
154 |
+
/**
|
155 |
+
* Middleware that retries requests based on the boolean result of
|
156 |
+
* invoking the provided "decider" function.
|
157 |
+
*
|
158 |
+
* If no delay function is provided, a simple implementation of exponential
|
159 |
+
* backoff will be utilized.
|
160 |
+
*
|
161 |
+
* @param callable $decider Function that accepts the number of retries,
|
162 |
+
* a request, [response], and [exception] and
|
163 |
+
* returns true if the request is to be retried.
|
164 |
+
* @param callable $delay Function that accepts the number of retries and
|
165 |
+
* returns the number of milliseconds to delay.
|
166 |
+
*
|
167 |
+
* @return callable Returns a function that accepts the next handler.
|
168 |
+
*/
|
169 |
+
public static function retry(callable $decider, callable $delay = null)
|
170 |
+
{
|
171 |
+
return function (callable $handler) use ($decider, $delay) {
|
172 |
+
return new RetryMiddleware($decider, $handler, $delay);
|
173 |
+
};
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* Middleware that logs requests, responses, and errors using a message
|
178 |
+
* formatter.
|
179 |
+
*
|
180 |
+
* @param LoggerInterface $logger Logs messages.
|
181 |
+
* @param MessageFormatter $formatter Formatter used to create message strings.
|
182 |
+
* @param string $logLevel Level at which to log requests.
|
183 |
+
*
|
184 |
+
* @return callable Returns a function that accepts the next handler.
|
185 |
+
*/
|
186 |
+
public static function log(LoggerInterface $logger, MessageFormatter $formatter, $logLevel = LogLevel::INFO)
|
187 |
+
{
|
188 |
+
return function (callable $handler) use ($logger, $formatter, $logLevel) {
|
189 |
+
return function ($request, array $options) use ($handler, $logger, $formatter, $logLevel) {
|
190 |
+
return $handler($request, $options)->then(
|
191 |
+
function ($response) use ($logger, $request, $formatter, $logLevel) {
|
192 |
+
$message = $formatter->format($request, $response);
|
193 |
+
$logger->log($logLevel, $message);
|
194 |
+
return $response;
|
195 |
+
},
|
196 |
+
function ($reason) use ($logger, $request, $formatter) {
|
197 |
+
$response = $reason instanceof RequestException
|
198 |
+
? $reason->getResponse()
|
199 |
+
: null;
|
200 |
+
$message = $formatter->format($request, $response, $reason);
|
201 |
+
$logger->notice($message);
|
202 |
+
return \GuzzleHttp\Promise\rejection_for($reason);
|
203 |
+
}
|
204 |
+
);
|
205 |
+
};
|
206 |
+
};
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* This middleware adds a default content-type if possible, a default
|
211 |
+
* content-length or transfer-encoding header, and the expect header.
|
212 |
+
*
|
213 |
+
* @return callable
|
214 |
+
*/
|
215 |
+
public static function prepareBody()
|
216 |
+
{
|
217 |
+
return function (callable $handler) {
|
218 |
+
return new PrepareBodyMiddleware($handler);
|
219 |
+
};
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Middleware that applies a map function to the request before passing to
|
224 |
+
* the next handler.
|
225 |
+
*
|
226 |
+
* @param callable $fn Function that accepts a RequestInterface and returns
|
227 |
+
* a RequestInterface.
|
228 |
+
* @return callable
|
229 |
+
*/
|
230 |
+
public static function mapRequest(callable $fn)
|
231 |
+
{
|
232 |
+
return function (callable $handler) use ($fn) {
|
233 |
+
return function ($request, array $options) use ($handler, $fn) {
|
234 |
+
return $handler($fn($request), $options);
|
235 |
+
};
|
236 |
+
};
|
237 |
+
}
|
238 |
+
|
239 |
+
/**
|
240 |
+
* Middleware that applies a map function to the resolved promise's
|
241 |
+
* response.
|
242 |
+
*
|
243 |
+
* @param callable $fn Function that accepts a ResponseInterface and
|
244 |
+
* returns a ResponseInterface.
|
245 |
+
* @return callable
|
246 |
+
*/
|
247 |
+
public static function mapResponse(callable $fn)
|
248 |
+
{
|
249 |
+
return function (callable $handler) use ($fn) {
|
250 |
+
return function ($request, array $options) use ($handler, $fn) {
|
251 |
+
return $handler($request, $options)->then($fn);
|
252 |
+
};
|
253 |
+
};
|
254 |
+
}
|
255 |
+
}
|
vendor/guzzlehttp/guzzle/src/Pool.php
CHANGED
@@ -1,123 +1,123 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp;
|
3 |
-
|
4 |
-
use GuzzleHttp\Promise\PromisorInterface;
|
5 |
-
use Psr\Http\Message\RequestInterface;
|
6 |
-
use GuzzleHttp\Promise\EachPromise;
|
7 |
-
|
8 |
-
/**
|
9 |
-
* Sends and iterator of requests concurrently using a capped pool size.
|
10 |
-
*
|
11 |
-
* The pool will read from an iterator until it is cancelled or until the
|
12 |
-
* iterator is consumed. When a request is yielded, the request is sent after
|
13 |
-
* applying the "request_options" request options (if provided in the ctor).
|
14 |
-
*
|
15 |
-
* When a function is yielded by the iterator, the function is provided the
|
16 |
-
* "request_options" array that should be merged on top of any existing
|
17 |
-
* options, and the function MUST then return a wait-able promise.
|
18 |
-
*/
|
19 |
-
class Pool implements PromisorInterface
|
20 |
-
{
|
21 |
-
/** @var EachPromise */
|
22 |
-
private $each;
|
23 |
-
|
24 |
-
/**
|
25 |
-
* @param ClientInterface $client Client used to send the requests.
|
26 |
-
* @param array|\Iterator $requests Requests or functions that return
|
27 |
-
* requests to send concurrently.
|
28 |
-
* @param array $config Associative array of options
|
29 |
-
* - concurrency: (int) Maximum number of requests to send concurrently
|
30 |
-
* - options: Array of request options to apply to each request.
|
31 |
-
* - fulfilled: (callable) Function to invoke when a request completes.
|
32 |
-
* - rejected: (callable) Function to invoke when a request is rejected.
|
33 |
-
*/
|
34 |
-
public function __construct(
|
35 |
-
ClientInterface $client,
|
36 |
-
$requests,
|
37 |
-
array $config = []
|
38 |
-
) {
|
39 |
-
// Backwards compatibility.
|
40 |
-
if (isset($config['pool_size'])) {
|
41 |
-
$config['concurrency'] = $config['pool_size'];
|
42 |
-
} elseif (!isset($config['concurrency'])) {
|
43 |
-
$config['concurrency'] = 25;
|
44 |
-
}
|
45 |
-
|
46 |
-
if (isset($config['options'])) {
|
47 |
-
$opts = $config['options'];
|
48 |
-
unset($config['options']);
|
49 |
-
} else {
|
50 |
-
$opts = [];
|
51 |
-
}
|
52 |
-
|
53 |
-
$iterable = \GuzzleHttp\Promise\iter_for($requests);
|
54 |
-
$requests = function () use ($iterable, $client, $opts) {
|
55 |
-
foreach ($iterable as $key => $rfn) {
|
56 |
-
if ($rfn instanceof RequestInterface) {
|
57 |
-
yield $key => $client->sendAsync($rfn, $opts);
|
58 |
-
} elseif (is_callable($rfn)) {
|
59 |
-
yield $key => $rfn($opts);
|
60 |
-
} else {
|
61 |
-
throw new \InvalidArgumentException('Each value yielded by '
|
62 |
-
. 'the iterator must be a Psr7\Http\Message\RequestInterface '
|
63 |
-
. 'or a callable that returns a promise that fulfills '
|
64 |
-
. 'with a Psr7\Message\Http\ResponseInterface object.');
|
65 |
-
}
|
66 |
-
}
|
67 |
-
};
|
68 |
-
|
69 |
-
$this->each = new EachPromise($requests(), $config);
|
70 |
-
}
|
71 |
-
|
72 |
-
public function promise()
|
73 |
-
{
|
74 |
-
return $this->each->promise();
|
75 |
-
}
|
76 |
-
|
77 |
-
/**
|
78 |
-
* Sends multiple requests concurrently and returns an array of responses
|
79 |
-
* and exceptions that uses the same ordering as the provided requests.
|
80 |
-
*
|
81 |
-
* IMPORTANT: This method keeps every request and response in memory, and
|
82 |
-
* as such, is NOT recommended when sending a large number or an
|
83 |
-
* indeterminate number of requests concurrently.
|
84 |
-
*
|
85 |
-
* @param ClientInterface $client Client used to send the requests
|
86 |
-
* @param array|\Iterator $requests Requests to send concurrently.
|
87 |
-
* @param array $options Passes through the options available in
|
88 |
-
* {@see GuzzleHttp\Pool::__construct}
|
89 |
-
*
|
90 |
-
* @return array Returns an array containing the response or an exception
|
91 |
-
* in the same order that the requests were sent.
|
92 |
-
* @throws \InvalidArgumentException if the event format is incorrect.
|
93 |
-
*/
|
94 |
-
public static function batch(
|
95 |
-
ClientInterface $client,
|
96 |
-
$requests,
|
97 |
-
array $options = []
|
98 |
-
) {
|
99 |
-
$res = [];
|
100 |
-
self::cmpCallback($options, 'fulfilled', $res);
|
101 |
-
self::cmpCallback($options, 'rejected', $res);
|
102 |
-
$pool = new static($client, $requests, $options);
|
103 |
-
$pool->promise()->wait();
|
104 |
-
ksort($res);
|
105 |
-
|
106 |
-
return $res;
|
107 |
-
}
|
108 |
-
|
109 |
-
private static function cmpCallback(array &$options, $name, array &$results)
|
110 |
-
{
|
111 |
-
if (!isset($options[$name])) {
|
112 |
-
$options[$name] = function ($v, $k) use (&$results) {
|
113 |
-
$results[$k] = $v;
|
114 |
-
};
|
115 |
-
} else {
|
116 |
-
$currentFn = $options[$name];
|
117 |
-
$options[$name] = function ($v, $k) use (&$results, $currentFn) {
|
118 |
-
$currentFn($v, $k);
|
119 |
-
$results[$k] = $v;
|
120 |
-
};
|
121 |
-
}
|
122 |
-
}
|
123 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp;
|
3 |
+
|
4 |
+
use GuzzleHttp\Promise\PromisorInterface;
|
5 |
+
use Psr\Http\Message\RequestInterface;
|
6 |
+
use GuzzleHttp\Promise\EachPromise;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Sends and iterator of requests concurrently using a capped pool size.
|
10 |
+
*
|
11 |
+
* The pool will read from an iterator until it is cancelled or until the
|
12 |
+
* iterator is consumed. When a request is yielded, the request is sent after
|
13 |
+
* applying the "request_options" request options (if provided in the ctor).
|
14 |
+
*
|
15 |
+
* When a function is yielded by the iterator, the function is provided the
|
16 |
+
* "request_options" array that should be merged on top of any existing
|
17 |
+
* options, and the function MUST then return a wait-able promise.
|
18 |
+
*/
|
19 |
+
class Pool implements PromisorInterface
|
20 |
+
{
|
21 |
+
/** @var EachPromise */
|
22 |
+
private $each;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @param ClientInterface $client Client used to send the requests.
|
26 |
+
* @param array|\Iterator $requests Requests or functions that return
|
27 |
+
* requests to send concurrently.
|
28 |
+
* @param array $config Associative array of options
|
29 |
+
* - concurrency: (int) Maximum number of requests to send concurrently
|
30 |
+
* - options: Array of request options to apply to each request.
|
31 |
+
* - fulfilled: (callable) Function to invoke when a request completes.
|
32 |
+
* - rejected: (callable) Function to invoke when a request is rejected.
|
33 |
+
*/
|
34 |
+
public function __construct(
|
35 |
+
ClientInterface $client,
|
36 |
+
$requests,
|
37 |
+
array $config = []
|
38 |
+
) {
|
39 |
+
// Backwards compatibility.
|
40 |
+
if (isset($config['pool_size'])) {
|
41 |
+
$config['concurrency'] = $config['pool_size'];
|
42 |
+
} elseif (!isset($config['concurrency'])) {
|
43 |
+
$config['concurrency'] = 25;
|
44 |
+
}
|
45 |
+
|
46 |
+
if (isset($config['options'])) {
|
47 |
+
$opts = $config['options'];
|
48 |
+
unset($config['options']);
|
49 |
+
} else {
|
50 |
+
$opts = [];
|
51 |
+
}
|
52 |
+
|
53 |
+
$iterable = \GuzzleHttp\Promise\iter_for($requests);
|
54 |
+
$requests = function () use ($iterable, $client, $opts) {
|
55 |
+
foreach ($iterable as $key => $rfn) {
|
56 |
+
if ($rfn instanceof RequestInterface) {
|
57 |
+
yield $key => $client->sendAsync($rfn, $opts);
|
58 |
+
} elseif (is_callable($rfn)) {
|
59 |
+
yield $key => $rfn($opts);
|
60 |
+
} else {
|
61 |
+
throw new \InvalidArgumentException('Each value yielded by '
|
62 |
+
. 'the iterator must be a Psr7\Http\Message\RequestInterface '
|
63 |
+
. 'or a callable that returns a promise that fulfills '
|
64 |
+
. 'with a Psr7\Message\Http\ResponseInterface object.');
|
65 |
+
}
|
66 |
+
}
|
67 |
+
};
|
68 |
+
|
69 |
+
$this->each = new EachPromise($requests(), $config);
|
70 |
+
}
|
71 |
+
|
72 |
+
public function promise()
|
73 |
+
{
|
74 |
+
return $this->each->promise();
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Sends multiple requests concurrently and returns an array of responses
|
79 |
+
* and exceptions that uses the same ordering as the provided requests.
|
80 |
+
*
|
81 |
+
* IMPORTANT: This method keeps every request and response in memory, and
|
82 |
+
* as such, is NOT recommended when sending a large number or an
|
83 |
+
* indeterminate number of requests concurrently.
|
84 |
+
*
|
85 |
+
* @param ClientInterface $client Client used to send the requests
|
86 |
+
* @param array|\Iterator $requests Requests to send concurrently.
|
87 |
+
* @param array $options Passes through the options available in
|
88 |
+
* {@see GuzzleHttp\Pool::__construct}
|
89 |
+
*
|
90 |
+
* @return array Returns an array containing the response or an exception
|
91 |
+
* in the same order that the requests were sent.
|
92 |
+
* @throws \InvalidArgumentException if the event format is incorrect.
|
93 |
+
*/
|
94 |
+
public static function batch(
|
95 |
+
ClientInterface $client,
|
96 |
+
$requests,
|
97 |
+
array $options = []
|
98 |
+
) {
|
99 |
+
$res = [];
|
100 |
+
self::cmpCallback($options, 'fulfilled', $res);
|
101 |
+
self::cmpCallback($options, 'rejected', $res);
|
102 |
+
$pool = new static($client, $requests, $options);
|
103 |
+
$pool->promise()->wait();
|
104 |
+
ksort($res);
|
105 |
+
|
106 |
+
return $res;
|
107 |
+
}
|
108 |
+
|
109 |
+
private static function cmpCallback(array &$options, $name, array &$results)
|
110 |
+
{
|
111 |
+
if (!isset($options[$name])) {
|
112 |
+
$options[$name] = function ($v, $k) use (&$results) {
|
113 |
+
$results[$k] = $v;
|
114 |
+
};
|
115 |
+
} else {
|
116 |
+
$currentFn = $options[$name];
|
117 |
+
$options[$name] = function ($v, $k) use (&$results, $currentFn) {
|
118 |
+
$currentFn($v, $k);
|
119 |
+
$results[$k] = $v;
|
120 |
+
};
|
121 |
+
}
|
122 |
+
}
|
123 |
+
}
|
vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php
CHANGED
@@ -1,106 +1,106 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp;
|
3 |
-
|
4 |
-
use GuzzleHttp\Promise\PromiseInterface;
|
5 |
-
use GuzzleHttp\Psr7;
|
6 |
-
use Psr\Http\Message\RequestInterface;
|
7 |
-
|
8 |
-
/**
|
9 |
-
* Prepares requests that contain a body, adding the Content-Length,
|
10 |
-
* Content-Type, and Expect headers.
|
11 |
-
*/
|
12 |
-
class PrepareBodyMiddleware
|
13 |
-
{
|
14 |
-
/** @var callable */
|
15 |
-
private $nextHandler;
|
16 |
-
|
17 |
-
/**
|
18 |
-
* @param callable $nextHandler Next handler to invoke.
|
19 |
-
*/
|
20 |
-
public function __construct(callable $nextHandler)
|
21 |
-
{
|
22 |
-
$this->nextHandler = $nextHandler;
|
23 |
-
}
|
24 |
-
|
25 |
-
/**
|
26 |
-
* @param RequestInterface $request
|
27 |
-
* @param array $options
|
28 |
-
*
|
29 |
-
* @return PromiseInterface
|
30 |
-
*/
|
31 |
-
public function __invoke(RequestInterface $request, array $options)
|
32 |
-
{
|
33 |
-
$fn = $this->nextHandler;
|
34 |
-
|
35 |
-
// Don't do anything if the request has no body.
|
36 |
-
if ($request->getBody()->getSize() === 0) {
|
37 |
-
return $fn($request, $options);
|
38 |
-
}
|
39 |
-
|
40 |
-
$modify = [];
|
41 |
-
|
42 |
-
// Add a default content-type if possible.
|
43 |
-
if (!$request->hasHeader('Content-Type')) {
|
44 |
-
if ($uri = $request->getBody()->getMetadata('uri')) {
|
45 |
-
if ($type = Psr7\mimetype_from_filename($uri)) {
|
46 |
-
$modify['set_headers']['Content-Type'] = $type;
|
47 |
-
}
|
48 |
-
}
|
49 |
-
}
|
50 |
-
|
51 |
-
// Add a default content-length or transfer-encoding header.
|
52 |
-
if (!$request->hasHeader('Content-Length')
|
53 |
-
&& !$request->hasHeader('Transfer-Encoding')
|
54 |
-
) {
|
55 |
-
$size = $request->getBody()->getSize();
|
56 |
-
if ($size !== null) {
|
57 |
-
$modify['set_headers']['Content-Length'] = $size;
|
58 |
-
} else {
|
59 |
-
$modify['set_headers']['Transfer-Encoding'] = 'chunked';
|
60 |
-
}
|
61 |
-
}
|
62 |
-
|
63 |
-
// Add the expect header if needed.
|
64 |
-
$this->addExpectHeader($request, $options, $modify);
|
65 |
-
|
66 |
-
return $fn(Psr7\modify_request($request, $modify), $options);
|
67 |
-
}
|
68 |
-
|
69 |
-
private function addExpectHeader(
|
70 |
-
RequestInterface $request,
|
71 |
-
array $options,
|
72 |
-
array &$modify
|
73 |
-
) {
|
74 |
-
// Determine if the Expect header should be used
|
75 |
-
if ($request->hasHeader('Expect')) {
|
76 |
-
return;
|
77 |
-
}
|
78 |
-
|
79 |
-
$expect = isset($options['expect']) ? $options['expect'] : null;
|
80 |
-
|
81 |
-
// Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0
|
82 |
-
if ($expect === false || $request->getProtocolVersion() < 1.1) {
|
83 |
-
return;
|
84 |
-
}
|
85 |
-
|
86 |
-
// The expect header is unconditionally enabled
|
87 |
-
if ($expect === true) {
|
88 |
-
$modify['set_headers']['Expect'] = '100-Continue';
|
89 |
-
return;
|
90 |
-
}
|
91 |
-
|
92 |
-
// By default, send the expect header when the payload is > 1mb
|
93 |
-
if ($expect === null) {
|
94 |
-
$expect = 1048576;
|
95 |
-
}
|
96 |
-
|
97 |
-
// Always add if the body cannot be rewound, the size cannot be
|
98 |
-
// determined, or the size is greater than the cutoff threshold
|
99 |
-
$body = $request->getBody();
|
100 |
-
$size = $body->getSize();
|
101 |
-
|
102 |
-
if ($size === null || $size >= (int) $expect || !$body->isSeekable()) {
|
103 |
-
$modify['set_headers']['Expect'] = '100-Continue';
|
104 |
-
}
|
105 |
-
}
|
106 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp;
|
3 |
+
|
4 |
+
use GuzzleHttp\Promise\PromiseInterface;
|
5 |
+
use GuzzleHttp\Psr7;
|
6 |
+
use Psr\Http\Message\RequestInterface;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Prepares requests that contain a body, adding the Content-Length,
|
10 |
+
* Content-Type, and Expect headers.
|
11 |
+
*/
|
12 |
+
class PrepareBodyMiddleware
|
13 |
+
{
|
14 |
+
/** @var callable */
|
15 |
+
private $nextHandler;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @param callable $nextHandler Next handler to invoke.
|
19 |
+
*/
|
20 |
+
public function __construct(callable $nextHandler)
|
21 |
+
{
|
22 |
+
$this->nextHandler = $nextHandler;
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @param RequestInterface $request
|
27 |
+
* @param array $options
|
28 |
+
*
|
29 |
+
* @return PromiseInterface
|
30 |
+
*/
|
31 |
+
public function __invoke(RequestInterface $request, array $options)
|
32 |
+
{
|
33 |
+
$fn = $this->nextHandler;
|
34 |
+
|
35 |
+
// Don't do anything if the request has no body.
|
36 |
+
if ($request->getBody()->getSize() === 0) {
|
37 |
+
return $fn($request, $options);
|
38 |
+
}
|
39 |
+
|
40 |
+
$modify = [];
|
41 |
+
|
42 |
+
// Add a default content-type if possible.
|
43 |
+
if (!$request->hasHeader('Content-Type')) {
|
44 |
+
if ($uri = $request->getBody()->getMetadata('uri')) {
|
45 |
+
if ($type = Psr7\mimetype_from_filename($uri)) {
|
46 |
+
$modify['set_headers']['Content-Type'] = $type;
|
47 |
+
}
|
48 |
+
}
|
49 |
+
}
|
50 |
+
|
51 |
+
// Add a default content-length or transfer-encoding header.
|
52 |
+
if (!$request->hasHeader('Content-Length')
|
53 |
+
&& !$request->hasHeader('Transfer-Encoding')
|
54 |
+
) {
|
55 |
+
$size = $request->getBody()->getSize();
|
56 |
+
if ($size !== null) {
|
57 |
+
$modify['set_headers']['Content-Length'] = $size;
|
58 |
+
} else {
|
59 |
+
$modify['set_headers']['Transfer-Encoding'] = 'chunked';
|
60 |
+
}
|
61 |
+
}
|
62 |
+
|
63 |
+
// Add the expect header if needed.
|
64 |
+
$this->addExpectHeader($request, $options, $modify);
|
65 |
+
|
66 |
+
return $fn(Psr7\modify_request($request, $modify), $options);
|
67 |
+
}
|
68 |
+
|
69 |
+
private function addExpectHeader(
|
70 |
+
RequestInterface $request,
|
71 |
+
array $options,
|
72 |
+
array &$modify
|
73 |
+
) {
|
74 |
+
// Determine if the Expect header should be used
|
75 |
+
if ($request->hasHeader('Expect')) {
|
76 |
+
return;
|
77 |
+
}
|
78 |
+
|
79 |
+
$expect = isset($options['expect']) ? $options['expect'] : null;
|
80 |
+
|
81 |
+
// Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0
|
82 |
+
if ($expect === false || $request->getProtocolVersion() < 1.1) {
|
83 |
+
return;
|
84 |
+
}
|
85 |
+
|
86 |
+
// The expect header is unconditionally enabled
|
87 |
+
if ($expect === true) {
|
88 |
+
$modify['set_headers']['Expect'] = '100-Continue';
|
89 |
+
return;
|
90 |
+
}
|
91 |
+
|
92 |
+
// By default, send the expect header when the payload is > 1mb
|
93 |
+
if ($expect === null) {
|
94 |
+
$expect = 1048576;
|
95 |
+
}
|
96 |
+
|
97 |
+
// Always add if the body cannot be rewound, the size cannot be
|
98 |
+
// determined, or the size is greater than the cutoff threshold
|
99 |
+
$body = $request->getBody();
|
100 |
+
$size = $body->getSize();
|
101 |
+
|
102 |
+
if ($size === null || $size >= (int) $expect || !$body->isSeekable()) {
|
103 |
+
$modify['set_headers']['Expect'] = '100-Continue';
|
104 |
+
}
|
105 |
+
}
|
106 |
+
}
|
vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php
CHANGED
@@ -1,237 +1,237 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp;
|
3 |
-
|
4 |
-
use GuzzleHttp\Exception\BadResponseException;
|
5 |
-
use GuzzleHttp\Exception\TooManyRedirectsException;
|
6 |
-
use GuzzleHttp\Promise\PromiseInterface;
|
7 |
-
use GuzzleHttp\Psr7;
|
8 |
-
use Psr\Http\Message\RequestInterface;
|
9 |
-
use Psr\Http\Message\ResponseInterface;
|
10 |
-
use Psr\Http\Message\UriInterface;
|
11 |
-
|
12 |
-
/**
|
13 |
-
* Request redirect middleware.
|
14 |
-
*
|
15 |
-
* Apply this middleware like other middleware using
|
16 |
-
* {@see GuzzleHttp\Middleware::redirect()}.
|
17 |
-
*/
|
18 |
-
class RedirectMiddleware
|
19 |
-
{
|
20 |
-
const HISTORY_HEADER = 'X-Guzzle-Redirect-History';
|
21 |
-
|
22 |
-
const STATUS_HISTORY_HEADER = 'X-Guzzle-Redirect-Status-History';
|
23 |
-
|
24 |
-
public static $defaultSettings = [
|
25 |
-
'max' => 5,
|
26 |
-
'protocols' => ['http', 'https'],
|
27 |
-
'strict' => false,
|
28 |
-
'referer' => false,
|
29 |
-
'track_redirects' => false,
|
30 |
-
];
|
31 |
-
|
32 |
-
/** @var callable */
|
33 |
-
private $nextHandler;
|
34 |
-
|
35 |
-
/**
|
36 |
-
* @param callable $nextHandler Next handler to invoke.
|
37 |
-
*/
|
38 |
-
public function __construct(callable $nextHandler)
|
39 |
-
{
|
40 |
-
$this->nextHandler = $nextHandler;
|
41 |
-
}
|
42 |
-
|
43 |
-
/**
|
44 |
-
* @param RequestInterface $request
|
45 |
-
* @param array $options
|
46 |
-
*
|
47 |
-
* @return PromiseInterface
|
48 |
-
*/
|
49 |
-
public function __invoke(RequestInterface $request, array $options)
|
50 |
-
{
|
51 |
-
$fn = $this->nextHandler;
|
52 |
-
|
53 |
-
if (empty($options['allow_redirects'])) {
|
54 |
-
return $fn($request, $options);
|
55 |
-
}
|
56 |
-
|
57 |
-
if ($options['allow_redirects'] === true) {
|
58 |
-
$options['allow_redirects'] = self::$defaultSettings;
|
59 |
-
} elseif (!is_array($options['allow_redirects'])) {
|
60 |
-
throw new \InvalidArgumentException('allow_redirects must be true, false, or array');
|
61 |
-
} else {
|
62 |
-
// Merge the default settings with the provided settings
|
63 |
-
$options['allow_redirects'] += self::$defaultSettings;
|
64 |
-
}
|
65 |
-
|
66 |
-
if (empty($options['allow_redirects']['max'])) {
|
67 |
-
return $fn($request, $options);
|
68 |
-
}
|
69 |
-
|
70 |
-
return $fn($request, $options)
|
71 |
-
->then(function (ResponseInterface $response) use ($request, $options) {
|
72 |
-
return $this->checkRedirect($request, $options, $response);
|
73 |
-
});
|
74 |
-
}
|
75 |
-
|
76 |
-
/**
|
77 |
-
* @param RequestInterface $request
|
78 |
-
* @param array $options
|
79 |
-
* @param ResponseInterface|PromiseInterface $response
|
80 |
-
*
|
81 |
-
* @return ResponseInterface|PromiseInterface
|
82 |
-
*/
|
83 |
-
public function checkRedirect(
|
84 |
-
RequestInterface $request,
|
85 |
-
array $options,
|
86 |
-
ResponseInterface $response
|
87 |
-
) {
|
88 |
-
if (substr($response->getStatusCode(), 0, 1) != '3'
|
89 |
-
|| !$response->hasHeader('Location')
|
90 |
-
) {
|
91 |
-
return $response;
|
92 |
-
}
|
93 |
-
|
94 |
-
$this->guardMax($request, $options);
|
95 |
-
$nextRequest = $this->modifyRequest($request, $options, $response);
|
96 |
-
|
97 |
-
if (isset($options['allow_redirects']['on_redirect'])) {
|
98 |
-
call_user_func(
|
99 |
-
$options['allow_redirects']['on_redirect'],
|
100 |
-
$request,
|
101 |
-
$response,
|
102 |
-
$nextRequest->getUri()
|
103 |
-
);
|
104 |
-
}
|
105 |
-
|
106 |
-
/** @var PromiseInterface|ResponseInterface $promise */
|
107 |
-
$promise = $this($nextRequest, $options);
|
108 |
-
|
109 |
-
// Add headers to be able to track history of redirects.
|
110 |
-
if (!empty($options['allow_redirects']['track_redirects'])) {
|
111 |
-
return $this->withTracking(
|
112 |
-
$promise,
|
113 |
-
(string) $nextRequest->getUri(),
|
114 |
-
$response->getStatusCode()
|
115 |
-
);
|
116 |
-
}
|
117 |
-
|
118 |
-
return $promise;
|
119 |
-
}
|
120 |
-
|
121 |
-
private function withTracking(PromiseInterface $promise, $uri, $statusCode)
|
122 |
-
{
|
123 |
-
return $promise->then(
|
124 |
-
function (ResponseInterface $response) use ($uri, $statusCode) {
|
125 |
-
// Note that we are pushing to the front of the list as this
|
126 |
-
// would be an earlier response than what is currently present
|
127 |
-
// in the history header.
|
128 |
-
$historyHeader = $response->getHeader(self::HISTORY_HEADER);
|
129 |
-
$statusHeader = $response->getHeader(self::STATUS_HISTORY_HEADER);
|
130 |
-
array_unshift($historyHeader, $uri);
|
131 |
-
array_unshift($statusHeader, $statusCode);
|
132 |
-
return $response->withHeader(self::HISTORY_HEADER, $historyHeader)
|
133 |
-
->withHeader(self::STATUS_HISTORY_HEADER, $statusHeader);
|
134 |
-
}
|
135 |
-
);
|
136 |
-
}
|
137 |
-
|
138 |
-
private function guardMax(RequestInterface $request, array &$options)
|
139 |
-
{
|
140 |
-
$current = isset($options['__redirect_count'])
|
141 |
-
? $options['__redirect_count']
|
142 |
-
: 0;
|
143 |
-
$options['__redirect_count'] = $current + 1;
|
144 |
-
$max = $options['allow_redirects']['max'];
|
145 |
-
|
146 |
-
if ($options['__redirect_count'] > $max) {
|
147 |
-
throw new TooManyRedirectsException(
|
148 |
-
"Will not follow more than {$max} redirects",
|
149 |
-
$request
|
150 |
-
);
|
151 |
-
}
|
152 |
-
}
|
153 |
-
|
154 |
-
/**
|
155 |
-
* @param RequestInterface $request
|
156 |
-
* @param array $options
|
157 |
-
* @param ResponseInterface $response
|
158 |
-
*
|
159 |
-
* @return RequestInterface
|
160 |
-
*/
|
161 |
-
public function modifyRequest(
|
162 |
-
RequestInterface $request,
|
163 |
-
array $options,
|
164 |
-
ResponseInterface $response
|
165 |
-
) {
|
166 |
-
// Request modifications to apply.
|
167 |
-
$modify = [];
|
168 |
-
$protocols = $options['allow_redirects']['protocols'];
|
169 |
-
|
170 |
-
// Use a GET request if this is an entity enclosing request and we are
|
171 |
-
// not forcing RFC compliance, but rather emulating what all browsers
|
172 |
-
// would do.
|
173 |
-
$statusCode = $response->getStatusCode();
|
174 |
-
if ($statusCode == 303 ||
|
175 |
-
($statusCode <= 302 && $request->getBody() && !$options['allow_redirects']['strict'])
|
176 |
-
) {
|
177 |
-
$modify['method'] = 'GET';
|
178 |
-
$modify['body'] = '';
|
179 |
-
}
|
180 |
-
|
181 |
-
$modify['uri'] = $this->redirectUri($request, $response, $protocols);
|
182 |
-
Psr7\rewind_body($request);
|
183 |
-
|
184 |
-
// Add the Referer header if it is told to do so and only
|
185 |
-
// add the header if we are not redirecting from https to http.
|
186 |
-
if ($options['allow_redirects']['referer']
|
187 |
-
&& $modify['uri']->getScheme() === $request->getUri()->getScheme()
|
188 |
-
) {
|
189 |
-
$uri = $request->getUri()->withUserInfo('', '');
|
190 |
-
$modify['set_headers']['Referer'] = (string) $uri;
|
191 |
-
} else {
|
192 |
-
$modify['remove_headers'][] = 'Referer';
|
193 |
-
}
|
194 |
-
|
195 |
-
// Remove Authorization header if host is different.
|
196 |
-
if ($request->getUri()->getHost() !== $modify['uri']->getHost()) {
|
197 |
-
$modify['remove_headers'][] = 'Authorization';
|
198 |
-
}
|
199 |
-
|
200 |
-
return Psr7\modify_request($request, $modify);
|
201 |
-
}
|
202 |
-
|
203 |
-
/**
|
204 |
-
* Set the appropriate URL on the request based on the location header
|
205 |
-
*
|
206 |
-
* @param RequestInterface $request
|
207 |
-
* @param ResponseInterface $response
|
208 |
-
* @param array $protocols
|
209 |
-
*
|
210 |
-
* @return UriInterface
|
211 |
-
*/
|
212 |
-
private function redirectUri(
|
213 |
-
RequestInterface $request,
|
214 |
-
ResponseInterface $response,
|
215 |
-
array $protocols
|
216 |
-
) {
|
217 |
-
$location = Psr7\UriResolver::resolve(
|
218 |
-
$request->getUri(),
|
219 |
-
new Psr7\Uri($response->getHeaderLine('Location'))
|
220 |
-
);
|
221 |
-
|
222 |
-
// Ensure that the redirect URI is allowed based on the protocols.
|
223 |
-
if (!in_array($location->getScheme(), $protocols)) {
|
224 |
-
throw new BadResponseException(
|
225 |
-
sprintf(
|
226 |
-
'Redirect URI, %s, does not use one of the allowed redirect protocols: %s',
|
227 |
-
$location,
|
228 |
-
implode(', ', $protocols)
|
229 |
-
),
|
230 |
-
$request,
|
231 |
-
$response
|
232 |
-
);
|
233 |
-
}
|
234 |
-
|
235 |
-
return $location;
|
236 |
-
}
|
237 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp;
|
3 |
+
|
4 |
+
use GuzzleHttp\Exception\BadResponseException;
|
5 |
+
use GuzzleHttp\Exception\TooManyRedirectsException;
|
6 |
+
use GuzzleHttp\Promise\PromiseInterface;
|
7 |
+
use GuzzleHttp\Psr7;
|
8 |
+
use Psr\Http\Message\RequestInterface;
|
9 |
+
use Psr\Http\Message\ResponseInterface;
|
10 |
+
use Psr\Http\Message\UriInterface;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Request redirect middleware.
|
14 |
+
*
|
15 |
+
* Apply this middleware like other middleware using
|
16 |
+
* {@see GuzzleHttp\Middleware::redirect()}.
|
17 |
+
*/
|
18 |
+
class RedirectMiddleware
|
19 |
+
{
|
20 |
+
const HISTORY_HEADER = 'X-Guzzle-Redirect-History';
|
21 |
+
|
22 |
+
const STATUS_HISTORY_HEADER = 'X-Guzzle-Redirect-Status-History';
|
23 |
+
|
24 |
+
public static $defaultSettings = [
|
25 |
+
'max' => 5,
|
26 |
+
'protocols' => ['http', 'https'],
|
27 |
+
'strict' => false,
|
28 |
+
'referer' => false,
|
29 |
+
'track_redirects' => false,
|
30 |
+
];
|
31 |
+
|
32 |
+
/** @var callable */
|
33 |
+
private $nextHandler;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @param callable $nextHandler Next handler to invoke.
|
37 |
+
*/
|
38 |
+
public function __construct(callable $nextHandler)
|
39 |
+
{
|
40 |
+
$this->nextHandler = $nextHandler;
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* @param RequestInterface $request
|
45 |
+
* @param array $options
|
46 |
+
*
|
47 |
+
* @return PromiseInterface
|
48 |
+
*/
|
49 |
+
public function __invoke(RequestInterface $request, array $options)
|
50 |
+
{
|
51 |
+
$fn = $this->nextHandler;
|
52 |
+
|
53 |
+
if (empty($options['allow_redirects'])) {
|
54 |
+
return $fn($request, $options);
|
55 |
+
}
|
56 |
+
|
57 |
+
if ($options['allow_redirects'] === true) {
|
58 |
+
$options['allow_redirects'] = self::$defaultSettings;
|
59 |
+
} elseif (!is_array($options['allow_redirects'])) {
|
60 |
+
throw new \InvalidArgumentException('allow_redirects must be true, false, or array');
|
61 |
+
} else {
|
62 |
+
// Merge the default settings with the provided settings
|
63 |
+
$options['allow_redirects'] += self::$defaultSettings;
|
64 |
+
}
|
65 |
+
|
66 |
+
if (empty($options['allow_redirects']['max'])) {
|
67 |
+
return $fn($request, $options);
|
68 |
+
}
|
69 |
+
|
70 |
+
return $fn($request, $options)
|
71 |
+
->then(function (ResponseInterface $response) use ($request, $options) {
|
72 |
+
return $this->checkRedirect($request, $options, $response);
|
73 |
+
});
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* @param RequestInterface $request
|
78 |
+
* @param array $options
|
79 |
+
* @param ResponseInterface|PromiseInterface $response
|
80 |
+
*
|
81 |
+
* @return ResponseInterface|PromiseInterface
|
82 |
+
*/
|
83 |
+
public function checkRedirect(
|
84 |
+
RequestInterface $request,
|
85 |
+
array $options,
|
86 |
+
ResponseInterface $response
|
87 |
+
) {
|
88 |
+
if (substr($response->getStatusCode(), 0, 1) != '3'
|
89 |
+
|| !$response->hasHeader('Location')
|
90 |
+
) {
|
91 |
+
return $response;
|
92 |
+
}
|
93 |
+
|
94 |
+
$this->guardMax($request, $options);
|
95 |
+
$nextRequest = $this->modifyRequest($request, $options, $response);
|
96 |
+
|
97 |
+
if (isset($options['allow_redirects']['on_redirect'])) {
|
98 |
+
call_user_func(
|
99 |
+
$options['allow_redirects']['on_redirect'],
|
100 |
+
$request,
|
101 |
+
$response,
|
102 |
+
$nextRequest->getUri()
|
103 |
+
);
|
104 |
+
}
|
105 |
+
|
106 |
+
/** @var PromiseInterface|ResponseInterface $promise */
|
107 |
+
$promise = $this($nextRequest, $options);
|
108 |
+
|
109 |
+
// Add headers to be able to track history of redirects.
|
110 |
+
if (!empty($options['allow_redirects']['track_redirects'])) {
|
111 |
+
return $this->withTracking(
|
112 |
+
$promise,
|
113 |
+
(string) $nextRequest->getUri(),
|
114 |
+
$response->getStatusCode()
|
115 |
+
);
|
116 |
+
}
|
117 |
+
|
118 |
+
return $promise;
|
119 |
+
}
|
120 |
+
|
121 |
+
private function withTracking(PromiseInterface $promise, $uri, $statusCode)
|
122 |
+
{
|
123 |
+
return $promise->then(
|
124 |
+
function (ResponseInterface $response) use ($uri, $statusCode) {
|
125 |
+
// Note that we are pushing to the front of the list as this
|
126 |
+
// would be an earlier response than what is currently present
|
127 |
+
// in the history header.
|
128 |
+
$historyHeader = $response->getHeader(self::HISTORY_HEADER);
|
129 |
+
$statusHeader = $response->getHeader(self::STATUS_HISTORY_HEADER);
|
130 |
+
array_unshift($historyHeader, $uri);
|
131 |
+
array_unshift($statusHeader, $statusCode);
|
132 |
+
return $response->withHeader(self::HISTORY_HEADER, $historyHeader)
|
133 |
+
->withHeader(self::STATUS_HISTORY_HEADER, $statusHeader);
|
134 |
+
}
|
135 |
+
);
|
136 |
+
}
|
137 |
+
|
138 |
+
private function guardMax(RequestInterface $request, array &$options)
|
139 |
+
{
|
140 |
+
$current = isset($options['__redirect_count'])
|
141 |
+
? $options['__redirect_count']
|
142 |
+
: 0;
|
143 |
+
$options['__redirect_count'] = $current + 1;
|
144 |
+
$max = $options['allow_redirects']['max'];
|
145 |
+
|
146 |
+
if ($options['__redirect_count'] > $max) {
|
147 |
+
throw new TooManyRedirectsException(
|
148 |
+
"Will not follow more than {$max} redirects",
|
149 |
+
$request
|
150 |
+
);
|
151 |
+
}
|
152 |
+
}
|
153 |
+
|
154 |
+
/**
|
155 |
+
* @param RequestInterface $request
|
156 |
+
* @param array $options
|
157 |
+
* @param ResponseInterface $response
|
158 |
+
*
|
159 |
+
* @return RequestInterface
|
160 |
+
*/
|
161 |
+
public function modifyRequest(
|
162 |
+
RequestInterface $request,
|
163 |
+
array $options,
|
164 |
+
ResponseInterface $response
|
165 |
+
) {
|
166 |
+
// Request modifications to apply.
|
167 |
+
$modify = [];
|
168 |
+
$protocols = $options['allow_redirects']['protocols'];
|
169 |
+
|
170 |
+
// Use a GET request if this is an entity enclosing request and we are
|
171 |
+
// not forcing RFC compliance, but rather emulating what all browsers
|
172 |
+
// would do.
|
173 |
+
$statusCode = $response->getStatusCode();
|
174 |
+
if ($statusCode == 303 ||
|
175 |
+
($statusCode <= 302 && $request->getBody() && !$options['allow_redirects']['strict'])
|
176 |
+
) {
|
177 |
+
$modify['method'] = 'GET';
|
178 |
+
$modify['body'] = '';
|
179 |
+
}
|
180 |
+
|
181 |
+
$modify['uri'] = $this->redirectUri($request, $response, $protocols);
|
182 |
+
Psr7\rewind_body($request);
|
183 |
+
|
184 |
+
// Add the Referer header if it is told to do so and only
|
185 |
+
// add the header if we are not redirecting from https to http.
|
186 |
+
if ($options['allow_redirects']['referer']
|
187 |
+
&& $modify['uri']->getScheme() === $request->getUri()->getScheme()
|
188 |
+
) {
|
189 |
+
$uri = $request->getUri()->withUserInfo('', '');
|
190 |
+
$modify['set_headers']['Referer'] = (string) $uri;
|
191 |
+
} else {
|
192 |
+
$modify['remove_headers'][] = 'Referer';
|
193 |
+
}
|
194 |
+
|
195 |
+
// Remove Authorization header if host is different.
|
196 |
+
if ($request->getUri()->getHost() !== $modify['uri']->getHost()) {
|
197 |
+
$modify['remove_headers'][] = 'Authorization';
|
198 |
+
}
|
199 |
+
|
200 |
+
return Psr7\modify_request($request, $modify);
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* Set the appropriate URL on the request based on the location header
|
205 |
+
*
|
206 |
+
* @param RequestInterface $request
|
207 |
+
* @param ResponseInterface $response
|
208 |
+
* @param array $protocols
|
209 |
+
*
|
210 |
+
* @return UriInterface
|
211 |
+
*/
|
212 |
+
private function redirectUri(
|
213 |
+
RequestInterface $request,
|
214 |
+
ResponseInterface $response,
|
215 |
+
array $protocols
|
216 |
+
) {
|
217 |
+
$location = Psr7\UriResolver::resolve(
|
218 |
+
$request->getUri(),
|
219 |
+
new Psr7\Uri($response->getHeaderLine('Location'))
|
220 |
+
);
|
221 |
+
|
222 |
+
// Ensure that the redirect URI is allowed based on the protocols.
|
223 |
+
if (!in_array($location->getScheme(), $protocols)) {
|
224 |
+
throw new BadResponseException(
|
225 |
+
sprintf(
|
226 |
+
'Redirect URI, %s, does not use one of the allowed redirect protocols: %s',
|
227 |
+
$location,
|
228 |
+
implode(', ', $protocols)
|
229 |
+
),
|
230 |
+
$request,
|
231 |
+
$response
|
232 |
+
);
|
233 |
+
}
|
234 |
+
|
235 |
+
return $location;
|
236 |
+
}
|
237 |
+
}
|
vendor/guzzlehttp/guzzle/src/RequestOptions.php
CHANGED
@@ -1,255 +1,255 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp;
|
3 |
-
|
4 |
-
/**
|
5 |
-
* This class contains a list of built-in Guzzle request options.
|
6 |
-
*
|
7 |
-
* More documentation for each option can be found at http://guzzlephp.org/.
|
8 |
-
*
|
9 |
-
* @link http://docs.guzzlephp.org/en/v6/request-options.html
|
10 |
-
*/
|
11 |
-
final class RequestOptions
|
12 |
-
{
|
13 |
-
/**
|
14 |
-
* allow_redirects: (bool|array) Controls redirect behavior. Pass false
|
15 |
-
* to disable redirects, pass true to enable redirects, pass an
|
16 |
-
* associative to provide custom redirect settings. Defaults to "false".
|
17 |
-
* This option only works if your handler has the RedirectMiddleware. When
|
18 |
-
* passing an associative array, you can provide the following key value
|
19 |
-
* pairs:
|
20 |
-
*
|
21 |
-
* - max: (int, default=5) maximum number of allowed redirects.
|
22 |
-
* - strict: (bool, default=false) Set to true to use strict redirects
|
23 |
-
* meaning redirect POST requests with POST requests vs. doing what most
|
24 |
-
* browsers do which is redirect POST requests with GET requests
|
25 |
-
* - referer: (bool, default=true) Set to false to disable the Referer
|
26 |
-
* header.
|
27 |
-
* - protocols: (array, default=['http', 'https']) Allowed redirect
|
28 |
-
* protocols.
|
29 |
-
* - on_redirect: (callable) PHP callable that is invoked when a redirect
|
30 |
-
* is encountered. The callable is invoked with the request, the redirect
|
31 |
-
* response that was received, and the effective URI. Any return value
|
32 |
-
* from the on_redirect function is ignored.
|
33 |
-
*/
|
34 |
-
const ALLOW_REDIRECTS = 'allow_redirects';
|
35 |
-
|
36 |
-
/**
|
37 |
-
* auth: (array) Pass an array of HTTP authentication parameters to use
|
38 |
-
* with the request. The array must contain the username in index [0],
|
39 |
-
* the password in index [1], and you can optionally provide a built-in
|
40 |
-
* authentication type in index [2]. Pass null to disable authentication
|
41 |
-
* for a request.
|
42 |
-
*/
|
43 |
-
const AUTH = 'auth';
|
44 |
-
|
45 |
-
/**
|
46 |
-
* body: (resource|string|null|int|float|StreamInterface|callable|\Iterator)
|
47 |
-
* Body to send in the request.
|
48 |
-
*/
|
49 |
-
const BODY = 'body';
|
50 |
-
|
51 |
-
/**
|
52 |
-
* cert: (string|array) Set to a string to specify the path to a file
|
53 |
-
* containing a PEM formatted SSL client side certificate. If a password
|
54 |
-
* is required, then set cert to an array containing the path to the PEM
|
55 |
-
* file in the first array element followed by the certificate password
|
56 |
-
* in the second array element.
|
57 |
-
*/
|
58 |
-
const CERT = 'cert';
|
59 |
-
|
60 |
-
/**
|
61 |
-
* cookies: (bool|GuzzleHttp\Cookie\CookieJarInterface, default=false)
|
62 |
-
* Specifies whether or not cookies are used in a request or what cookie
|
63 |
-
* jar to use or what cookies to send. This option only works if your
|
64 |
-
* handler has the `cookie` middleware. Valid values are `false` and
|
65 |
-
* an instance of {@see GuzzleHttp\Cookie\CookieJarInterface}.
|
66 |
-
*/
|
67 |
-
const COOKIES = 'cookies';
|
68 |
-
|
69 |
-
/**
|
70 |
-
* connect_timeout: (float, default=0) Float describing the number of
|
71 |
-
* seconds to wait while trying to connect to a server. Use 0 to wait
|
72 |
-
* indefinitely (the default behavior).
|
73 |
-
*/
|
74 |
-
const CONNECT_TIMEOUT = 'connect_timeout';
|
75 |
-
|
76 |
-
/**
|
77 |
-
* debug: (bool|resource) Set to true or set to a PHP stream returned by
|
78 |
-
* fopen() enable debug output with the HTTP handler used to send a
|
79 |
-
* request.
|
80 |
-
*/
|
81 |
-
const DEBUG = 'debug';
|
82 |
-
|
83 |
-
/**
|
84 |
-
* decode_content: (bool, default=true) Specify whether or not
|
85 |
-
* Content-Encoding responses (gzip, deflate, etc.) are automatically
|
86 |
-
* decoded.
|
87 |
-
*/
|
88 |
-
const DECODE_CONTENT = 'decode_content';
|
89 |
-
|
90 |
-
/**
|
91 |
-
* delay: (int) The amount of time to delay before sending in milliseconds.
|
92 |
-
*/
|
93 |
-
const DELAY = 'delay';
|
94 |
-
|
95 |
-
/**
|
96 |
-
* expect: (bool|integer) Controls the behavior of the
|
97 |
-
* "Expect: 100-Continue" header.
|
98 |
-
*
|
99 |
-
* Set to `true` to enable the "Expect: 100-Continue" header for all
|
100 |
-
* requests that sends a body. Set to `false` to disable the
|
101 |
-
* "Expect: 100-Continue" header for all requests. Set to a number so that
|
102 |
-
* the size of the payload must be greater than the number in order to send
|
103 |
-
* the Expect header. Setting to a number will send the Expect header for
|
104 |
-
* all requests in which the size of the payload cannot be determined or
|
105 |
-
* where the body is not rewindable.
|
106 |
-
*
|
107 |
-
* By default, Guzzle will add the "Expect: 100-Continue" header when the
|
108 |
-
* size of the body of a request is greater than 1 MB and a request is
|
109 |
-
* using HTTP/1.1.
|
110 |
-
*/
|
111 |
-
const EXPECT = 'expect';
|
112 |
-
|
113 |
-
/**
|
114 |
-
* form_params: (array) Associative array of form field names to values
|
115 |
-
* where each value is a string or array of strings. Sets the Content-Type
|
116 |
-
* header to application/x-www-form-urlencoded when no Content-Type header
|
117 |
-
* is already present.
|
118 |
-
*/
|
119 |
-
const FORM_PARAMS = 'form_params';
|
120 |
-
|
121 |
-
/**
|
122 |
-
* headers: (array) Associative array of HTTP headers. Each value MUST be
|
123 |
-
* a string or array of strings.
|
124 |
-
*/
|
125 |
-
const HEADERS = 'headers';
|
126 |
-
|
127 |
-
/**
|
128 |
-
* http_errors: (bool, default=true) Set to false to disable exceptions
|
129 |
-
* when a non- successful HTTP response is received. By default,
|
130 |
-
* exceptions will be thrown for 4xx and 5xx responses. This option only
|
131 |
-
* works if your handler has the `httpErrors` middleware.
|
132 |
-
*/
|
133 |
-
const HTTP_ERRORS = 'http_errors';
|
134 |
-
|
135 |
-
/**
|
136 |
-
* json: (mixed) Adds JSON data to a request. The provided value is JSON
|
137 |
-
* encoded and a Content-Type header of application/json will be added to
|
138 |
-
* the request if no Content-Type header is already present.
|
139 |
-
*/
|
140 |
-
const JSON = 'json';
|
141 |
-
|
142 |
-
/**
|
143 |
-
* multipart: (array) Array of associative arrays, each containing a
|
144 |
-
* required "name" key mapping to the form field, name, a required
|
145 |
-
* "contents" key mapping to a StreamInterface|resource|string, an
|
146 |
-
* optional "headers" associative array of custom headers, and an
|
147 |
-
* optional "filename" key mapping to a string to send as the filename in
|
148 |
-
* the part. If no "filename" key is present, then no "filename" attribute
|
149 |
-
* will be added to the part.
|
150 |
-
*/
|
151 |
-
const MULTIPART = 'multipart';
|
152 |
-
|
153 |
-
/**
|
154 |
-
* on_headers: (callable) A callable that is invoked when the HTTP headers
|
155 |
-
* of the response have been received but the body has not yet begun to
|
156 |
-
* download.
|
157 |
-
*/
|
158 |
-
const ON_HEADERS = 'on_headers';
|
159 |
-
|
160 |
-
/**
|
161 |
-
* on_stats: (callable) allows you to get access to transfer statistics of
|
162 |
-
* a request and access the lower level transfer details of the handler
|
163 |
-
* associated with your client. ``on_stats`` is a callable that is invoked
|
164 |
-
* when a handler has finished sending a request. The callback is invoked
|
165 |
-
* with transfer statistics about the request, the response received, or
|
166 |
-
* the error encountered. Included in the data is the total amount of time
|
167 |
-
* taken to send the request.
|
168 |
-
*/
|
169 |
-
const ON_STATS = 'on_stats';
|
170 |
-
|
171 |
-
/**
|
172 |
-
* progress: (callable) Defines a function to invoke when transfer
|
173 |
-
* progress is made. The function accepts the following positional
|
174 |
-
* arguments: the total number of bytes expected to be downloaded, the
|
175 |
-
* number of bytes downloaded so far, the number of bytes expected to be
|
176 |
-
* uploaded, the number of bytes uploaded so far.
|
177 |
-
*/
|
178 |
-
const PROGRESS = 'progress';
|
179 |
-
|
180 |
-
/**
|
181 |
-
* proxy: (string|array) Pass a string to specify an HTTP proxy, or an
|
182 |
-
* array to specify different proxies for different protocols (where the
|
183 |
-
* key is the protocol and the value is a proxy string).
|
184 |
-
*/
|
185 |
-
const PROXY = 'proxy';
|
186 |
-
|
187 |
-
/**
|
188 |
-
* query: (array|string) Associative array of query string values to add
|
189 |
-
* to the request. This option uses PHP's http_build_query() to create
|
190 |
-
* the string representation. Pass a string value if you need more
|
191 |
-
* control than what this method provides
|
192 |
-
*/
|
193 |
-
const QUERY = 'query';
|
194 |
-
|
195 |
-
/**
|
196 |
-
* sink: (resource|string|StreamInterface) Where the data of the
|
197 |
-
* response is written to. Defaults to a PHP temp stream. Providing a
|
198 |
-
* string will write data to a file by the given name.
|
199 |
-
*/
|
200 |
-
const SINK = 'sink';
|
201 |
-
|
202 |
-
/**
|
203 |
-
* synchronous: (bool) Set to true to inform HTTP handlers that you intend
|
204 |
-
* on waiting on the response. This can be useful for optimizations. Note
|
205 |
-
* that a promise is still returned if you are using one of the async
|
206 |
-
* client methods.
|
207 |
-
*/
|
208 |
-
const SYNCHRONOUS = 'synchronous';
|
209 |
-
|
210 |
-
/**
|
211 |
-
* ssl_key: (array|string) Specify the path to a file containing a private
|
212 |
-
* SSL key in PEM format. If a password is required, then set to an array
|
213 |
-
* containing the path to the SSL key in the first array element followed
|
214 |
-
* by the password required for the certificate in the second element.
|
215 |
-
*/
|
216 |
-
const SSL_KEY = 'ssl_key';
|
217 |
-
|
218 |
-
/**
|
219 |
-
* stream: Set to true to attempt to stream a response rather than
|
220 |
-
* download it all up-front.
|
221 |
-
*/
|
222 |
-
const STREAM = 'stream';
|
223 |
-
|
224 |
-
/**
|
225 |
-
* verify: (bool|string, default=true) Describes the SSL certificate
|
226 |
-
* verification behavior of a request. Set to true to enable SSL
|
227 |
-
* certificate verification using the system CA bundle when available
|
228 |
-
* (the default). Set to false to disable certificate verification (this
|
229 |
-
* is insecure!). Set to a string to provide the path to a CA bundle on
|
230 |
-
* disk to enable verification using a custom certificate.
|
231 |
-
*/
|
232 |
-
const VERIFY = 'verify';
|
233 |
-
|
234 |
-
/**
|
235 |
-
* timeout: (float, default=0) Float describing the timeout of the
|
236 |
-
* request in seconds. Use 0 to wait indefinitely (the default behavior).
|
237 |
-
*/
|
238 |
-
const TIMEOUT = 'timeout';
|
239 |
-
|
240 |
-
/**
|
241 |
-
* read_timeout: (float, default=default_socket_timeout ini setting) Float describing
|
242 |
-
* the body read timeout, for stream requests.
|
243 |
-
*/
|
244 |
-
const READ_TIMEOUT = 'read_timeout';
|
245 |
-
|
246 |
-
/**
|
247 |
-
* version: (float) Specifies the HTTP protocol version to attempt to use.
|
248 |
-
*/
|
249 |
-
const VERSION = 'version';
|
250 |
-
|
251 |
-
/**
|
252 |
-
* force_ip_resolve: (bool) Force client to use only ipv4 or ipv6 protocol
|
253 |
-
*/
|
254 |
-
const FORCE_IP_RESOLVE = 'force_ip_resolve';
|
255 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* This class contains a list of built-in Guzzle request options.
|
6 |
+
*
|
7 |
+
* More documentation for each option can be found at http://guzzlephp.org/.
|
8 |
+
*
|
9 |
+
* @link http://docs.guzzlephp.org/en/v6/request-options.html
|
10 |
+
*/
|
11 |
+
final class RequestOptions
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* allow_redirects: (bool|array) Controls redirect behavior. Pass false
|
15 |
+
* to disable redirects, pass true to enable redirects, pass an
|
16 |
+
* associative to provide custom redirect settings. Defaults to "false".
|
17 |
+
* This option only works if your handler has the RedirectMiddleware. When
|
18 |
+
* passing an associative array, you can provide the following key value
|
19 |
+
* pairs:
|
20 |
+
*
|
21 |
+
* - max: (int, default=5) maximum number of allowed redirects.
|
22 |
+
* - strict: (bool, default=false) Set to true to use strict redirects
|
23 |
+
* meaning redirect POST requests with POST requests vs. doing what most
|
24 |
+
* browsers do which is redirect POST requests with GET requests
|
25 |
+
* - referer: (bool, default=true) Set to false to disable the Referer
|
26 |
+
* header.
|
27 |
+
* - protocols: (array, default=['http', 'https']) Allowed redirect
|
28 |
+
* protocols.
|
29 |
+
* - on_redirect: (callable) PHP callable that is invoked when a redirect
|
30 |
+
* is encountered. The callable is invoked with the request, the redirect
|
31 |
+
* response that was received, and the effective URI. Any return value
|
32 |
+
* from the on_redirect function is ignored.
|
33 |
+
*/
|
34 |
+
const ALLOW_REDIRECTS = 'allow_redirects';
|
35 |
+
|
36 |
+
/**
|
37 |
+
* auth: (array) Pass an array of HTTP authentication parameters to use
|
38 |
+
* with the request. The array must contain the username in index [0],
|
39 |
+
* the password in index [1], and you can optionally provide a built-in
|
40 |
+
* authentication type in index [2]. Pass null to disable authentication
|
41 |
+
* for a request.
|
42 |
+
*/
|
43 |
+
const AUTH = 'auth';
|
44 |
+
|
45 |
+
/**
|
46 |
+
* body: (resource|string|null|int|float|StreamInterface|callable|\Iterator)
|
47 |
+
* Body to send in the request.
|
48 |
+
*/
|
49 |
+
const BODY = 'body';
|
50 |
+
|
51 |
+
/**
|
52 |
+
* cert: (string|array) Set to a string to specify the path to a file
|
53 |
+
* containing a PEM formatted SSL client side certificate. If a password
|
54 |
+
* is required, then set cert to an array containing the path to the PEM
|
55 |
+
* file in the first array element followed by the certificate password
|
56 |
+
* in the second array element.
|
57 |
+
*/
|
58 |
+
const CERT = 'cert';
|
59 |
+
|
60 |
+
/**
|
61 |
+
* cookies: (bool|GuzzleHttp\Cookie\CookieJarInterface, default=false)
|
62 |
+
* Specifies whether or not cookies are used in a request or what cookie
|
63 |
+
* jar to use or what cookies to send. This option only works if your
|
64 |
+
* handler has the `cookie` middleware. Valid values are `false` and
|
65 |
+
* an instance of {@see GuzzleHttp\Cookie\CookieJarInterface}.
|
66 |
+
*/
|
67 |
+
const COOKIES = 'cookies';
|
68 |
+
|
69 |
+
/**
|
70 |
+
* connect_timeout: (float, default=0) Float describing the number of
|
71 |
+
* seconds to wait while trying to connect to a server. Use 0 to wait
|
72 |
+
* indefinitely (the default behavior).
|
73 |
+
*/
|
74 |
+
const CONNECT_TIMEOUT = 'connect_timeout';
|
75 |
+
|
76 |
+
/**
|
77 |
+
* debug: (bool|resource) Set to true or set to a PHP stream returned by
|
78 |
+
* fopen() enable debug output with the HTTP handler used to send a
|
79 |
+
* request.
|
80 |
+
*/
|
81 |
+
const DEBUG = 'debug';
|
82 |
+
|
83 |
+
/**
|
84 |
+
* decode_content: (bool, default=true) Specify whether or not
|
85 |
+
* Content-Encoding responses (gzip, deflate, etc.) are automatically
|
86 |
+
* decoded.
|
87 |
+
*/
|
88 |
+
const DECODE_CONTENT = 'decode_content';
|
89 |
+
|
90 |
+
/**
|
91 |
+
* delay: (int) The amount of time to delay before sending in milliseconds.
|
92 |
+
*/
|
93 |
+
const DELAY = 'delay';
|
94 |
+
|
95 |
+
/**
|
96 |
+
* expect: (bool|integer) Controls the behavior of the
|
97 |
+
* "Expect: 100-Continue" header.
|
98 |
+
*
|
99 |
+
* Set to `true` to enable the "Expect: 100-Continue" header for all
|
100 |
+
* requests that sends a body. Set to `false` to disable the
|
101 |
+
* "Expect: 100-Continue" header for all requests. Set to a number so that
|
102 |
+
* the size of the payload must be greater than the number in order to send
|
103 |
+
* the Expect header. Setting to a number will send the Expect header for
|
104 |
+
* all requests in which the size of the payload cannot be determined or
|
105 |
+
* where the body is not rewindable.
|
106 |
+
*
|
107 |
+
* By default, Guzzle will add the "Expect: 100-Continue" header when the
|
108 |
+
* size of the body of a request is greater than 1 MB and a request is
|
109 |
+
* using HTTP/1.1.
|
110 |
+
*/
|
111 |
+
const EXPECT = 'expect';
|
112 |
+
|
113 |
+
/**
|
114 |
+
* form_params: (array) Associative array of form field names to values
|
115 |
+
* where each value is a string or array of strings. Sets the Content-Type
|
116 |
+
* header to application/x-www-form-urlencoded when no Content-Type header
|
117 |
+
* is already present.
|
118 |
+
*/
|
119 |
+
const FORM_PARAMS = 'form_params';
|
120 |
+
|
121 |
+
/**
|
122 |
+
* headers: (array) Associative array of HTTP headers. Each value MUST be
|
123 |
+
* a string or array of strings.
|
124 |
+
*/
|
125 |
+
const HEADERS = 'headers';
|
126 |
+
|
127 |
+
/**
|
128 |
+
* http_errors: (bool, default=true) Set to false to disable exceptions
|
129 |
+
* when a non- successful HTTP response is received. By default,
|
130 |
+
* exceptions will be thrown for 4xx and 5xx responses. This option only
|
131 |
+
* works if your handler has the `httpErrors` middleware.
|
132 |
+
*/
|
133 |
+
const HTTP_ERRORS = 'http_errors';
|
134 |
+
|
135 |
+
/**
|
136 |
+
* json: (mixed) Adds JSON data to a request. The provided value is JSON
|
137 |
+
* encoded and a Content-Type header of application/json will be added to
|
138 |
+
* the request if no Content-Type header is already present.
|
139 |
+
*/
|
140 |
+
const JSON = 'json';
|
141 |
+
|
142 |
+
/**
|
143 |
+
* multipart: (array) Array of associative arrays, each containing a
|
144 |
+
* required "name" key mapping to the form field, name, a required
|
145 |
+
* "contents" key mapping to a StreamInterface|resource|string, an
|
146 |
+
* optional "headers" associative array of custom headers, and an
|
147 |
+
* optional "filename" key mapping to a string to send as the filename in
|
148 |
+
* the part. If no "filename" key is present, then no "filename" attribute
|
149 |
+
* will be added to the part.
|
150 |
+
*/
|
151 |
+
const MULTIPART = 'multipart';
|
152 |
+
|
153 |
+
/**
|
154 |
+
* on_headers: (callable) A callable that is invoked when the HTTP headers
|
155 |
+
* of the response have been received but the body has not yet begun to
|
156 |
+
* download.
|
157 |
+
*/
|
158 |
+
const ON_HEADERS = 'on_headers';
|
159 |
+
|
160 |
+
/**
|
161 |
+
* on_stats: (callable) allows you to get access to transfer statistics of
|
162 |
+
* a request and access the lower level transfer details of the handler
|
163 |
+
* associated with your client. ``on_stats`` is a callable that is invoked
|
164 |
+
* when a handler has finished sending a request. The callback is invoked
|
165 |
+
* with transfer statistics about the request, the response received, or
|
166 |
+
* the error encountered. Included in the data is the total amount of time
|
167 |
+
* taken to send the request.
|
168 |
+
*/
|
169 |
+
const ON_STATS = 'on_stats';
|
170 |
+
|
171 |
+
/**
|
172 |
+
* progress: (callable) Defines a function to invoke when transfer
|
173 |
+
* progress is made. The function accepts the following positional
|
174 |
+
* arguments: the total number of bytes expected to be downloaded, the
|
175 |
+
* number of bytes downloaded so far, the number of bytes expected to be
|
176 |
+
* uploaded, the number of bytes uploaded so far.
|
177 |
+
*/
|
178 |
+
const PROGRESS = 'progress';
|
179 |
+
|
180 |
+
/**
|
181 |
+
* proxy: (string|array) Pass a string to specify an HTTP proxy, or an
|
182 |
+
* array to specify different proxies for different protocols (where the
|
183 |
+
* key is the protocol and the value is a proxy string).
|
184 |
+
*/
|
185 |
+
const PROXY = 'proxy';
|
186 |
+
|
187 |
+
/**
|
188 |
+
* query: (array|string) Associative array of query string values to add
|
189 |
+
* to the request. This option uses PHP's http_build_query() to create
|
190 |
+
* the string representation. Pass a string value if you need more
|
191 |
+
* control than what this method provides
|
192 |
+
*/
|
193 |
+
const QUERY = 'query';
|
194 |
+
|
195 |
+
/**
|
196 |
+
* sink: (resource|string|StreamInterface) Where the data of the
|
197 |
+
* response is written to. Defaults to a PHP temp stream. Providing a
|
198 |
+
* string will write data to a file by the given name.
|
199 |
+
*/
|
200 |
+
const SINK = 'sink';
|
201 |
+
|
202 |
+
/**
|
203 |
+
* synchronous: (bool) Set to true to inform HTTP handlers that you intend
|
204 |
+
* on waiting on the response. This can be useful for optimizations. Note
|
205 |
+
* that a promise is still returned if you are using one of the async
|
206 |
+
* client methods.
|
207 |
+
*/
|
208 |
+
const SYNCHRONOUS = 'synchronous';
|
209 |
+
|
210 |
+
/**
|
211 |
+
* ssl_key: (array|string) Specify the path to a file containing a private
|
212 |
+
* SSL key in PEM format. If a password is required, then set to an array
|
213 |
+
* containing the path to the SSL key in the first array element followed
|
214 |
+
* by the password required for the certificate in the second element.
|
215 |
+
*/
|
216 |
+
const SSL_KEY = 'ssl_key';
|
217 |
+
|
218 |
+
/**
|
219 |
+
* stream: Set to true to attempt to stream a response rather than
|
220 |
+
* download it all up-front.
|
221 |
+
*/
|
222 |
+
const STREAM = 'stream';
|
223 |
+
|
224 |
+
/**
|
225 |
+
* verify: (bool|string, default=true) Describes the SSL certificate
|
226 |
+
* verification behavior of a request. Set to true to enable SSL
|
227 |
+
* certificate verification using the system CA bundle when available
|
228 |
+
* (the default). Set to false to disable certificate verification (this
|
229 |
+
* is insecure!). Set to a string to provide the path to a CA bundle on
|
230 |
+
* disk to enable verification using a custom certificate.
|
231 |
+
*/
|
232 |
+
const VERIFY = 'verify';
|
233 |
+
|
234 |
+
/**
|
235 |
+
* timeout: (float, default=0) Float describing the timeout of the
|
236 |
+
* request in seconds. Use 0 to wait indefinitely (the default behavior).
|
237 |
+
*/
|
238 |
+
const TIMEOUT = 'timeout';
|
239 |
+
|
240 |
+
/**
|
241 |
+
* read_timeout: (float, default=default_socket_timeout ini setting) Float describing
|
242 |
+
* the body read timeout, for stream requests.
|
243 |
+
*/
|
244 |
+
const READ_TIMEOUT = 'read_timeout';
|
245 |
+
|
246 |
+
/**
|
247 |
+
* version: (float) Specifies the HTTP protocol version to attempt to use.
|
248 |
+
*/
|
249 |
+
const VERSION = 'version';
|
250 |
+
|
251 |
+
/**
|
252 |
+
* force_ip_resolve: (bool) Force client to use only ipv4 or ipv6 protocol
|
253 |
+
*/
|
254 |
+
const FORCE_IP_RESOLVE = 'force_ip_resolve';
|
255 |
+
}
|
vendor/guzzlehttp/guzzle/src/RetryMiddleware.php
CHANGED
@@ -1,112 +1,112 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp;
|
3 |
-
|
4 |
-
use GuzzleHttp\Promise\PromiseInterface;
|
5 |
-
use GuzzleHttp\Promise\RejectedPromise;
|
6 |
-
use GuzzleHttp\Psr7;
|
7 |
-
use Psr\Http\Message\RequestInterface;
|
8 |
-
use Psr\Http\Message\ResponseInterface;
|
9 |
-
|
10 |
-
/**
|
11 |
-
* Middleware that retries requests based on the boolean result of
|
12 |
-
* invoking the provided "decider" function.
|
13 |
-
*/
|
14 |
-
class RetryMiddleware
|
15 |
-
{
|
16 |
-
/** @var callable */
|
17 |
-
private $nextHandler;
|
18 |
-
|
19 |
-
/** @var callable */
|
20 |
-
private $decider;
|
21 |
-
|
22 |
-
/**
|
23 |
-
* @param callable $decider Function that accepts the number of retries,
|
24 |
-
* a request, [response], and [exception] and
|
25 |
-
* returns true if the request is to be
|
26 |
-
* retried.
|
27 |
-
* @param callable $nextHandler Next handler to invoke.
|
28 |
-
* @param callable $delay Function that accepts the number of retries
|
29 |
-
* and [response] and returns the number of
|
30 |
-
* milliseconds to delay.
|
31 |
-
*/
|
32 |
-
public function __construct(
|
33 |
-
callable $decider,
|
34 |
-
callable $nextHandler,
|
35 |
-
callable $delay = null
|
36 |
-
) {
|
37 |
-
$this->decider = $decider;
|
38 |
-
$this->nextHandler = $nextHandler;
|
39 |
-
$this->delay = $delay ?: __CLASS__ . '::exponentialDelay';
|
40 |
-
}
|
41 |
-
|
42 |
-
/**
|
43 |
-
* Default exponential backoff delay function.
|
44 |
-
*
|
45 |
-
* @param $retries
|
46 |
-
*
|
47 |
-
* @return int
|
48 |
-
*/
|
49 |
-
public static function exponentialDelay($retries)
|
50 |
-
{
|
51 |
-
return (int) pow(2, $retries - 1);
|
52 |
-
}
|
53 |
-
|
54 |
-
/**
|
55 |
-
* @param RequestInterface $request
|
56 |
-
* @param array $options
|
57 |
-
*
|
58 |
-
* @return PromiseInterface
|
59 |
-
*/
|
60 |
-
public function __invoke(RequestInterface $request, array $options)
|
61 |
-
{
|
62 |
-
if (!isset($options['retries'])) {
|
63 |
-
$options['retries'] = 0;
|
64 |
-
}
|
65 |
-
|
66 |
-
$fn = $this->nextHandler;
|
67 |
-
return $fn($request, $options)
|
68 |
-
->then(
|
69 |
-
$this->onFulfilled($request, $options),
|
70 |
-
$this->onRejected($request, $options)
|
71 |
-
);
|
72 |
-
}
|
73 |
-
|
74 |
-
private function onFulfilled(RequestInterface $req, array $options)
|
75 |
-
{
|
76 |
-
return function ($value) use ($req, $options) {
|
77 |
-
if (!call_user_func(
|
78 |
-
$this->decider,
|
79 |
-
$options['retries'],
|
80 |
-
$req,
|
81 |
-
$value,
|
82 |
-
null
|
83 |
-
)) {
|
84 |
-
return $value;
|
85 |
-
}
|
86 |
-
return $this->doRetry($req, $options, $value);
|
87 |
-
};
|
88 |
-
}
|
89 |
-
|
90 |
-
private function onRejected(RequestInterface $req, array $options)
|
91 |
-
{
|
92 |
-
return function ($reason) use ($req, $options) {
|
93 |
-
if (!call_user_func(
|
94 |
-
$this->decider,
|
95 |
-
$options['retries'],
|
96 |
-
$req,
|
97 |
-
null,
|
98 |
-
$reason
|
99 |
-
)) {
|
100 |
-
return \GuzzleHttp\Promise\rejection_for($reason);
|
101 |
-
}
|
102 |
-
return $this->doRetry($req, $options);
|
103 |
-
};
|
104 |
-
}
|
105 |
-
|
106 |
-
private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null)
|
107 |
-
{
|
108 |
-
$options['delay'] = call_user_func($this->delay, ++$options['retries'], $response);
|
109 |
-
|
110 |
-
return $this($request, $options);
|
111 |
-
}
|
112 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp;
|
3 |
+
|
4 |
+
use GuzzleHttp\Promise\PromiseInterface;
|
5 |
+
use GuzzleHttp\Promise\RejectedPromise;
|
6 |
+
use GuzzleHttp\Psr7;
|
7 |
+
use Psr\Http\Message\RequestInterface;
|
8 |
+
use Psr\Http\Message\ResponseInterface;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Middleware that retries requests based on the boolean result of
|
12 |
+
* invoking the provided "decider" function.
|
13 |
+
*/
|
14 |
+
class RetryMiddleware
|
15 |
+
{
|
16 |
+
/** @var callable */
|
17 |
+
private $nextHandler;
|
18 |
+
|
19 |
+
/** @var callable */
|
20 |
+
private $decider;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @param callable $decider Function that accepts the number of retries,
|
24 |
+
* a request, [response], and [exception] and
|
25 |
+
* returns true if the request is to be
|
26 |
+
* retried.
|
27 |
+
* @param callable $nextHandler Next handler to invoke.
|
28 |
+
* @param callable $delay Function that accepts the number of retries
|
29 |
+
* and [response] and returns the number of
|
30 |
+
* milliseconds to delay.
|
31 |
+
*/
|
32 |
+
public function __construct(
|
33 |
+
callable $decider,
|
34 |
+
callable $nextHandler,
|
35 |
+
callable $delay = null
|
36 |
+
) {
|
37 |
+
$this->decider = $decider;
|
38 |
+
$this->nextHandler = $nextHandler;
|
39 |
+
$this->delay = $delay ?: __CLASS__ . '::exponentialDelay';
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Default exponential backoff delay function.
|
44 |
+
*
|
45 |
+
* @param $retries
|
46 |
+
*
|
47 |
+
* @return int
|
48 |
+
*/
|
49 |
+
public static function exponentialDelay($retries)
|
50 |
+
{
|
51 |
+
return (int) pow(2, $retries - 1);
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* @param RequestInterface $request
|
56 |
+
* @param array $options
|
57 |
+
*
|
58 |
+
* @return PromiseInterface
|
59 |
+
*/
|
60 |
+
public function __invoke(RequestInterface $request, array $options)
|
61 |
+
{
|
62 |
+
if (!isset($options['retries'])) {
|
63 |
+
$options['retries'] = 0;
|
64 |
+
}
|
65 |
+
|
66 |
+
$fn = $this->nextHandler;
|
67 |
+
return $fn($request, $options)
|
68 |
+
->then(
|
69 |
+
$this->onFulfilled($request, $options),
|
70 |
+
$this->onRejected($request, $options)
|
71 |
+
);
|
72 |
+
}
|
73 |
+
|
74 |
+
private function onFulfilled(RequestInterface $req, array $options)
|
75 |
+
{
|
76 |
+
return function ($value) use ($req, $options) {
|
77 |
+
if (!call_user_func(
|
78 |
+
$this->decider,
|
79 |
+
$options['retries'],
|
80 |
+
$req,
|
81 |
+
$value,
|
82 |
+
null
|
83 |
+
)) {
|
84 |
+
return $value;
|
85 |
+
}
|
86 |
+
return $this->doRetry($req, $options, $value);
|
87 |
+
};
|
88 |
+
}
|
89 |
+
|
90 |
+
private function onRejected(RequestInterface $req, array $options)
|
91 |
+
{
|
92 |
+
return function ($reason) use ($req, $options) {
|
93 |
+
if (!call_user_func(
|
94 |
+
$this->decider,
|
95 |
+
$options['retries'],
|
96 |
+
$req,
|
97 |
+
null,
|
98 |
+
$reason
|
99 |
+
)) {
|
100 |
+
return \GuzzleHttp\Promise\rejection_for($reason);
|
101 |
+
}
|
102 |
+
return $this->doRetry($req, $options);
|
103 |
+
};
|
104 |
+
}
|
105 |
+
|
106 |
+
private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null)
|
107 |
+
{
|
108 |
+
$options['delay'] = call_user_func($this->delay, ++$options['retries'], $response);
|
109 |
+
|
110 |
+
return $this($request, $options);
|
111 |
+
}
|
112 |
+
}
|
vendor/guzzlehttp/guzzle/src/TransferStats.php
CHANGED
@@ -1,126 +1,126 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp;
|
3 |
-
|
4 |
-
use Psr\Http\Message\RequestInterface;
|
5 |
-
use Psr\Http\Message\ResponseInterface;
|
6 |
-
use Psr\Http\Message\UriInterface;
|
7 |
-
|
8 |
-
/**
|
9 |
-
* Represents data at the point after it was transferred either successfully
|
10 |
-
* or after a network error.
|
11 |
-
*/
|
12 |
-
final class TransferStats
|
13 |
-
{
|
14 |
-
private $request;
|
15 |
-
private $response;
|
16 |
-
private $transferTime;
|
17 |
-
private $handlerStats;
|
18 |
-
private $handlerErrorData;
|
19 |
-
|
20 |
-
/**
|
21 |
-
* @param RequestInterface $request Request that was sent.
|
22 |
-
* @param ResponseInterface $response Response received (if any)
|
23 |
-
* @param null $transferTime Total handler transfer time.
|
24 |
-
* @param mixed $handlerErrorData Handler error data.
|
25 |
-
* @param array $handlerStats Handler specific stats.
|
26 |
-
*/
|
27 |
-
public function __construct(
|
28 |
-
RequestInterface $request,
|
29 |
-
ResponseInterface $response = null,
|
30 |
-
$transferTime = null,
|
31 |
-
$handlerErrorData = null,
|
32 |
-
$handlerStats = []
|
33 |
-
) {
|
34 |
-
$this->request = $request;
|
35 |
-
$this->response = $response;
|
36 |
-
$this->transferTime = $transferTime;
|
37 |
-
$this->handlerErrorData = $handlerErrorData;
|
38 |
-
$this->handlerStats = $handlerStats;
|
39 |
-
}
|
40 |
-
|
41 |
-
/**
|
42 |
-
* @return RequestInterface
|
43 |
-
*/
|
44 |
-
public function getRequest()
|
45 |
-
{
|
46 |
-
return $this->request;
|
47 |
-
}
|
48 |
-
|
49 |
-
/**
|
50 |
-
* Returns the response that was received (if any).
|
51 |
-
*
|
52 |
-
* @return ResponseInterface|null
|
53 |
-
*/
|
54 |
-
public function getResponse()
|
55 |
-
{
|
56 |
-
return $this->response;
|
57 |
-
}
|
58 |
-
|
59 |
-
/**
|
60 |
-
* Returns true if a response was received.
|
61 |
-
*
|
62 |
-
* @return bool
|
63 |
-
*/
|
64 |
-
public function hasResponse()
|
65 |
-
{
|
66 |
-
return $this->response !== null;
|
67 |
-
}
|
68 |
-
|
69 |
-
/**
|
70 |
-
* Gets handler specific error data.
|
71 |
-
*
|
72 |
-
* This might be an exception, a integer representing an error code, or
|
73 |
-
* anything else. Relying on this value assumes that you know what handler
|
74 |
-
* you are using.
|
75 |
-
*
|
76 |
-
* @return mixed
|
77 |
-
*/
|
78 |
-
public function getHandlerErrorData()
|
79 |
-
{
|
80 |
-
return $this->handlerErrorData;
|
81 |
-
}
|
82 |
-
|
83 |
-
/**
|
84 |
-
* Get the effective URI the request was sent to.
|
85 |
-
*
|
86 |
-
* @return UriInterface
|
87 |
-
*/
|
88 |
-
public function getEffectiveUri()
|
89 |
-
{
|
90 |
-
return $this->request->getUri();
|
91 |
-
}
|
92 |
-
|
93 |
-
/**
|
94 |
-
* Get the estimated time the request was being transferred by the handler.
|
95 |
-
*
|
96 |
-
* @return float Time in seconds.
|
97 |
-
*/
|
98 |
-
public function getTransferTime()
|
99 |
-
{
|
100 |
-
return $this->transferTime;
|
101 |
-
}
|
102 |
-
|
103 |
-
/**
|
104 |
-
* Gets an array of all of the handler specific transfer data.
|
105 |
-
*
|
106 |
-
* @return array
|
107 |
-
*/
|
108 |
-
public function getHandlerStats()
|
109 |
-
{
|
110 |
-
return $this->handlerStats;
|
111 |
-
}
|
112 |
-
|
113 |
-
/**
|
114 |
-
* Get a specific handler statistic from the handler by name.
|
115 |
-
*
|
116 |
-
* @param string $stat Handler specific transfer stat to retrieve.
|
117 |
-
*
|
118 |
-
* @return mixed|null
|
119 |
-
*/
|
120 |
-
public function getHandlerStat($stat)
|
121 |
-
{
|
122 |
-
return isset($this->handlerStats[$stat])
|
123 |
-
? $this->handlerStats[$stat]
|
124 |
-
: null;
|
125 |
-
}
|
126 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp;
|
3 |
+
|
4 |
+
use Psr\Http\Message\RequestInterface;
|
5 |
+
use Psr\Http\Message\ResponseInterface;
|
6 |
+
use Psr\Http\Message\UriInterface;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Represents data at the point after it was transferred either successfully
|
10 |
+
* or after a network error.
|
11 |
+
*/
|
12 |
+
final class TransferStats
|
13 |
+
{
|
14 |
+
private $request;
|
15 |
+
private $response;
|
16 |
+
private $transferTime;
|
17 |
+
private $handlerStats;
|
18 |
+
private $handlerErrorData;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @param RequestInterface $request Request that was sent.
|
22 |
+
* @param ResponseInterface $response Response received (if any)
|
23 |
+
* @param null $transferTime Total handler transfer time.
|
24 |
+
* @param mixed $handlerErrorData Handler error data.
|
25 |
+
* @param array $handlerStats Handler specific stats.
|
26 |
+
*/
|
27 |
+
public function __construct(
|
28 |
+
RequestInterface $request,
|
29 |
+
ResponseInterface $response = null,
|
30 |
+
$transferTime = null,
|
31 |
+
$handlerErrorData = null,
|
32 |
+
$handlerStats = []
|
33 |
+
) {
|
34 |
+
$this->request = $request;
|
35 |
+
$this->response = $response;
|
36 |
+
$this->transferTime = $transferTime;
|
37 |
+
$this->handlerErrorData = $handlerErrorData;
|
38 |
+
$this->handlerStats = $handlerStats;
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* @return RequestInterface
|
43 |
+
*/
|
44 |
+
public function getRequest()
|
45 |
+
{
|
46 |
+
return $this->request;
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Returns the response that was received (if any).
|
51 |
+
*
|
52 |
+
* @return ResponseInterface|null
|
53 |
+
*/
|
54 |
+
public function getResponse()
|
55 |
+
{
|
56 |
+
return $this->response;
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Returns true if a response was received.
|
61 |
+
*
|
62 |
+
* @return bool
|
63 |
+
*/
|
64 |
+
public function hasResponse()
|
65 |
+
{
|
66 |
+
return $this->response !== null;
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Gets handler specific error data.
|
71 |
+
*
|
72 |
+
* This might be an exception, a integer representing an error code, or
|
73 |
+
* anything else. Relying on this value assumes that you know what handler
|
74 |
+
* you are using.
|
75 |
+
*
|
76 |
+
* @return mixed
|
77 |
+
*/
|
78 |
+
public function getHandlerErrorData()
|
79 |
+
{
|
80 |
+
return $this->handlerErrorData;
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Get the effective URI the request was sent to.
|
85 |
+
*
|
86 |
+
* @return UriInterface
|
87 |
+
*/
|
88 |
+
public function getEffectiveUri()
|
89 |
+
{
|
90 |
+
return $this->request->getUri();
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Get the estimated time the request was being transferred by the handler.
|
95 |
+
*
|
96 |
+
* @return float Time in seconds.
|
97 |
+
*/
|
98 |
+
public function getTransferTime()
|
99 |
+
{
|
100 |
+
return $this->transferTime;
|
101 |
+
}
|
102 |
+
|
103 |
+
/**
|
104 |
+
* Gets an array of all of the handler specific transfer data.
|
105 |
+
*
|
106 |
+
* @return array
|
107 |
+
*/
|
108 |
+
public function getHandlerStats()
|
109 |
+
{
|
110 |
+
return $this->handlerStats;
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Get a specific handler statistic from the handler by name.
|
115 |
+
*
|
116 |
+
* @param string $stat Handler specific transfer stat to retrieve.
|
117 |
+
*
|
118 |
+
* @return mixed|null
|
119 |
+
*/
|
120 |
+
public function getHandlerStat($stat)
|
121 |
+
{
|
122 |
+
return isset($this->handlerStats[$stat])
|
123 |
+
? $this->handlerStats[$stat]
|
124 |
+
: null;
|
125 |
+
}
|
126 |
+
}
|
vendor/guzzlehttp/guzzle/src/UriTemplate.php
CHANGED
@@ -1,237 +1,237 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp;
|
3 |
-
|
4 |
-
/**
|
5 |
-
* Expands URI templates. Userland implementation of PECL uri_template.
|
6 |
-
*
|
7 |
-
* @link http://tools.ietf.org/html/rfc6570
|
8 |
-
*/
|
9 |
-
class UriTemplate
|
10 |
-
{
|
11 |
-
/** @var string URI template */
|
12 |
-
private $template;
|
13 |
-
|
14 |
-
/** @var array Variables to use in the template expansion */
|
15 |
-
private $variables;
|
16 |
-
|
17 |
-
/** @var array Hash for quick operator lookups */
|
18 |
-
private static $operatorHash = [
|
19 |
-
'' => ['prefix' => '', 'joiner' => ',', 'query' => false],
|
20 |
-
'+' => ['prefix' => '', 'joiner' => ',', 'query' => false],
|
21 |
-
'#' => ['prefix' => '#', 'joiner' => ',', 'query' => false],
|
22 |
-
'.' => ['prefix' => '.', 'joiner' => '.', 'query' => false],
|
23 |
-
'/' => ['prefix' => '/', 'joiner' => '/', 'query' => false],
|
24 |
-
';' => ['prefix' => ';', 'joiner' => ';', 'query' => true],
|
25 |
-
'?' => ['prefix' => '?', 'joiner' => '&', 'query' => true],
|
26 |
-
'&' => ['prefix' => '&', 'joiner' => '&', 'query' => true]
|
27 |
-
];
|
28 |
-
|
29 |
-
/** @var array Delimiters */
|
30 |
-
private static $delims = [':', '/', '?', '#', '[', ']', '@', '!', '$',
|
31 |
-
'&', '\'', '(', ')', '*', '+', ',', ';', '='];
|
32 |
-
|
33 |
-
/** @var array Percent encoded delimiters */
|
34 |
-
private static $delimsPct = ['%3A', '%2F', '%3F', '%23', '%5B', '%5D',
|
35 |
-
'%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C',
|
36 |
-
'%3B', '%3D'];
|
37 |
-
|
38 |
-
public function expand($template, array $variables)
|
39 |
-
{
|
40 |
-
if (false === strpos($template, '{')) {
|
41 |
-
return $template;
|
42 |
-
}
|
43 |
-
|
44 |
-
$this->template = $template;
|
45 |
-
$this->variables = $variables;
|
46 |
-
|
47 |
-
return preg_replace_callback(
|
48 |
-
'/\{([^\}]+)\}/',
|
49 |
-
[$this, 'expandMatch'],
|
50 |
-
$this->template
|
51 |
-
);
|
52 |
-
}
|
53 |
-
|
54 |
-
/**
|
55 |
-
* Parse an expression into parts
|
56 |
-
*
|
57 |
-
* @param string $expression Expression to parse
|
58 |
-
*
|
59 |
-
* @return array Returns an associative array of parts
|
60 |
-
*/
|
61 |
-
private function parseExpression($expression)
|
62 |
-
{
|
63 |
-
$result = [];
|
64 |
-
|
65 |
-
if (isset(self::$operatorHash[$expression[0]])) {
|
66 |
-
$result['operator'] = $expression[0];
|
67 |
-
$expression = substr($expression, 1);
|
68 |
-
} else {
|
69 |
-
$result['operator'] = '';
|
70 |
-
}
|
71 |
-
|
72 |
-
foreach (explode(',', $expression) as $value) {
|
73 |
-
$value = trim($value);
|
74 |
-
$varspec = [];
|
75 |
-
if ($colonPos = strpos($value, ':')) {
|
76 |
-
$varspec['value'] = substr($value, 0, $colonPos);
|
77 |
-
$varspec['modifier'] = ':';
|
78 |
-
$varspec['position'] = (int) substr($value, $colonPos + 1);
|
79 |
-
} elseif (substr($value, -1) === '*') {
|
80 |
-
$varspec['modifier'] = '*';
|
81 |
-
$varspec['value'] = substr($value, 0, -1);
|
82 |
-
} else {
|
83 |
-
$varspec['value'] = (string) $value;
|
84 |
-
$varspec['modifier'] = '';
|
85 |
-
}
|
86 |
-
$result['values'][] = $varspec;
|
87 |
-
}
|
88 |
-
|
89 |
-
return $result;
|
90 |
-
}
|
91 |
-
|
92 |
-
/**
|
93 |
-
* Process an expansion
|
94 |
-
*
|
95 |
-
* @param array $matches Matches met in the preg_replace_callback
|
96 |
-
*
|
97 |
-
* @return string Returns the replacement string
|
98 |
-
*/
|
99 |
-
private function expandMatch(array $matches)
|
100 |
-
{
|
101 |
-
static $rfc1738to3986 = ['+' => '%20', '%7e' => '~'];
|
102 |
-
|
103 |
-
$replacements = [];
|
104 |
-
$parsed = self::parseExpression($matches[1]);
|
105 |
-
$prefix = self::$operatorHash[$parsed['operator']]['prefix'];
|
106 |
-
$joiner = self::$operatorHash[$parsed['operator']]['joiner'];
|
107 |
-
$useQuery = self::$operatorHash[$parsed['operator']]['query'];
|
108 |
-
|
109 |
-
foreach ($parsed['values'] as $value) {
|
110 |
-
if (!isset($this->variables[$value['value']])) {
|
111 |
-
continue;
|
112 |
-
}
|
113 |
-
|
114 |
-
$variable = $this->variables[$value['value']];
|
115 |
-
$actuallyUseQuery = $useQuery;
|
116 |
-
$expanded = '';
|
117 |
-
|
118 |
-
if (is_array($variable)) {
|
119 |
-
$isAssoc = $this->isAssoc($variable);
|
120 |
-
$kvp = [];
|
121 |
-
foreach ($variable as $key => $var) {
|
122 |
-
if ($isAssoc) {
|
123 |
-
$key = rawurlencode($key);
|
124 |
-
$isNestedArray = is_array($var);
|
125 |
-
} else {
|
126 |
-
$isNestedArray = false;
|
127 |
-
}
|
128 |
-
|
129 |
-
if (!$isNestedArray) {
|
130 |
-
$var = rawurlencode($var);
|
131 |
-
if ($parsed['operator'] === '+' ||
|
132 |
-
$parsed['operator'] === '#'
|
133 |
-
) {
|
134 |
-
$var = $this->decodeReserved($var);
|
135 |
-
}
|
136 |
-
}
|
137 |
-
|
138 |
-
if ($value['modifier'] === '*') {
|
139 |
-
if ($isAssoc) {
|
140 |
-
if ($isNestedArray) {
|
141 |
-
// Nested arrays must allow for deeply nested
|
142 |
-
// structures.
|
143 |
-
$var = strtr(
|
144 |
-
http_build_query([$key => $var]),
|
145 |
-
$rfc1738to3986
|
146 |
-
);
|
147 |
-
} else {
|
148 |
-
$var = $key . '=' . $var;
|
149 |
-
}
|
150 |
-
} elseif ($key > 0 && $actuallyUseQuery) {
|
151 |
-
$var = $value['value'] . '=' . $var;
|
152 |
-
}
|
153 |
-
}
|
154 |
-
|
155 |
-
$kvp[$key] = $var;
|
156 |
-
}
|
157 |
-
|
158 |
-
if (empty($variable)) {
|
159 |
-
$actuallyUseQuery = false;
|
160 |
-
} elseif ($value['modifier'] === '*') {
|
161 |
-
$expanded = implode($joiner, $kvp);
|
162 |
-
if ($isAssoc) {
|
163 |
-
// Don't prepend the value name when using the explode
|
164 |
-
// modifier with an associative array.
|
165 |
-
$actuallyUseQuery = false;
|
166 |
-
}
|
167 |
-
} else {
|
168 |
-
if ($isAssoc) {
|
169 |
-
// When an associative array is encountered and the
|
170 |
-
// explode modifier is not set, then the result must be
|
171 |
-
// a comma separated list of keys followed by their
|
172 |
-
// respective values.
|
173 |
-
foreach ($kvp as $k => &$v) {
|
174 |
-
$v = $k . ',' . $v;
|
175 |
-
}
|
176 |
-
}
|
177 |
-
$expanded = implode(',', $kvp);
|
178 |
-
}
|
179 |
-
} else {
|
180 |
-
if ($value['modifier'] === ':') {
|
181 |
-
$variable = substr($variable, 0, $value['position']);
|
182 |
-
}
|
183 |
-
$expanded = rawurlencode($variable);
|
184 |
-
if ($parsed['operator'] === '+' || $parsed['operator'] === '#') {
|
185 |
-
$expanded = $this->decodeReserved($expanded);
|
186 |
-
}
|
187 |
-
}
|
188 |
-
|
189 |
-
if ($actuallyUseQuery) {
|
190 |
-
if (!$expanded && $joiner !== '&') {
|
191 |
-
$expanded = $value['value'];
|
192 |
-
} else {
|
193 |
-
$expanded = $value['value'] . '=' . $expanded;
|
194 |
-
}
|
195 |
-
}
|
196 |
-
|
197 |
-
$replacements[] = $expanded;
|
198 |
-
}
|
199 |
-
|
200 |
-
$ret = implode($joiner, $replacements);
|
201 |
-
if ($ret && $prefix) {
|
202 |
-
return $prefix . $ret;
|
203 |
-
}
|
204 |
-
|
205 |
-
return $ret;
|
206 |
-
}
|
207 |
-
|
208 |
-
/**
|
209 |
-
* Determines if an array is associative.
|
210 |
-
*
|
211 |
-
* This makes the assumption that input arrays are sequences or hashes.
|
212 |
-
* This assumption is a tradeoff for accuracy in favor of speed, but it
|
213 |
-
* should work in almost every case where input is supplied for a URI
|
214 |
-
* template.
|
215 |
-
*
|
216 |
-
* @param array $array Array to check
|
217 |
-
*
|
218 |
-
* @return bool
|
219 |
-
*/
|
220 |
-
private function isAssoc(array $array)
|
221 |
-
{
|
222 |
-
return $array && array_keys($array)[0] !== 0;
|
223 |
-
}
|
224 |
-
|
225 |
-
/**
|
226 |
-
* Removes percent encoding on reserved characters (used with + and #
|
227 |
-
* modifiers).
|
228 |
-
*
|
229 |
-
* @param string $string String to fix
|
230 |
-
*
|
231 |
-
* @return string
|
232 |
-
*/
|
233 |
-
private function decodeReserved($string)
|
234 |
-
{
|
235 |
-
return str_replace(self::$delimsPct, self::$delims, $string);
|
236 |
-
}
|
237 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Expands URI templates. Userland implementation of PECL uri_template.
|
6 |
+
*
|
7 |
+
* @link http://tools.ietf.org/html/rfc6570
|
8 |
+
*/
|
9 |
+
class UriTemplate
|
10 |
+
{
|
11 |
+
/** @var string URI template */
|
12 |
+
private $template;
|
13 |
+
|
14 |
+
/** @var array Variables to use in the template expansion */
|
15 |
+
private $variables;
|
16 |
+
|
17 |
+
/** @var array Hash for quick operator lookups */
|
18 |
+
private static $operatorHash = [
|
19 |
+
'' => ['prefix' => '', 'joiner' => ',', 'query' => false],
|
20 |
+
'+' => ['prefix' => '', 'joiner' => ',', 'query' => false],
|
21 |
+
'#' => ['prefix' => '#', 'joiner' => ',', 'query' => false],
|
22 |
+
'.' => ['prefix' => '.', 'joiner' => '.', 'query' => false],
|
23 |
+
'/' => ['prefix' => '/', 'joiner' => '/', 'query' => false],
|
24 |
+
';' => ['prefix' => ';', 'joiner' => ';', 'query' => true],
|
25 |
+
'?' => ['prefix' => '?', 'joiner' => '&', 'query' => true],
|
26 |
+
'&' => ['prefix' => '&', 'joiner' => '&', 'query' => true]
|
27 |
+
];
|
28 |
+
|
29 |
+
/** @var array Delimiters */
|
30 |
+
private static $delims = [':', '/', '?', '#', '[', ']', '@', '!', '$',
|
31 |
+
'&', '\'', '(', ')', '*', '+', ',', ';', '='];
|
32 |
+
|
33 |
+
/** @var array Percent encoded delimiters */
|
34 |
+
private static $delimsPct = ['%3A', '%2F', '%3F', '%23', '%5B', '%5D',
|
35 |
+
'%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C',
|
36 |
+
'%3B', '%3D'];
|
37 |
+
|
38 |
+
public function expand($template, array $variables)
|
39 |
+
{
|
40 |
+
if (false === strpos($template, '{')) {
|
41 |
+
return $template;
|
42 |
+
}
|
43 |
+
|
44 |
+
$this->template = $template;
|
45 |
+
$this->variables = $variables;
|
46 |
+
|
47 |
+
return preg_replace_callback(
|
48 |
+
'/\{([^\}]+)\}/',
|
49 |
+
[$this, 'expandMatch'],
|
50 |
+
$this->template
|
51 |
+
);
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Parse an expression into parts
|
56 |
+
*
|
57 |
+
* @param string $expression Expression to parse
|
58 |
+
*
|
59 |
+
* @return array Returns an associative array of parts
|
60 |
+
*/
|
61 |
+
private function parseExpression($expression)
|
62 |
+
{
|
63 |
+
$result = [];
|
64 |
+
|
65 |
+
if (isset(self::$operatorHash[$expression[0]])) {
|
66 |
+
$result['operator'] = $expression[0];
|
67 |
+
$expression = substr($expression, 1);
|
68 |
+
} else {
|
69 |
+
$result['operator'] = '';
|
70 |
+
}
|
71 |
+
|
72 |
+
foreach (explode(',', $expression) as $value) {
|
73 |
+
$value = trim($value);
|
74 |
+
$varspec = [];
|
75 |
+
if ($colonPos = strpos($value, ':')) {
|
76 |
+
$varspec['value'] = substr($value, 0, $colonPos);
|
77 |
+
$varspec['modifier'] = ':';
|
78 |
+
$varspec['position'] = (int) substr($value, $colonPos + 1);
|
79 |
+
} elseif (substr($value, -1) === '*') {
|
80 |
+
$varspec['modifier'] = '*';
|
81 |
+
$varspec['value'] = substr($value, 0, -1);
|
82 |
+
} else {
|
83 |
+
$varspec['value'] = (string) $value;
|
84 |
+
$varspec['modifier'] = '';
|
85 |
+
}
|
86 |
+
$result['values'][] = $varspec;
|
87 |
+
}
|
88 |
+
|
89 |
+
return $result;
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Process an expansion
|
94 |
+
*
|
95 |
+
* @param array $matches Matches met in the preg_replace_callback
|
96 |
+
*
|
97 |
+
* @return string Returns the replacement string
|
98 |
+
*/
|
99 |
+
private function expandMatch(array $matches)
|
100 |
+
{
|
101 |
+
static $rfc1738to3986 = ['+' => '%20', '%7e' => '~'];
|
102 |
+
|
103 |
+
$replacements = [];
|
104 |
+
$parsed = self::parseExpression($matches[1]);
|
105 |
+
$prefix = self::$operatorHash[$parsed['operator']]['prefix'];
|
106 |
+
$joiner = self::$operatorHash[$parsed['operator']]['joiner'];
|
107 |
+
$useQuery = self::$operatorHash[$parsed['operator']]['query'];
|
108 |
+
|
109 |
+
foreach ($parsed['values'] as $value) {
|
110 |
+
if (!isset($this->variables[$value['value']])) {
|
111 |
+
continue;
|
112 |
+
}
|
113 |
+
|
114 |
+
$variable = $this->variables[$value['value']];
|
115 |
+
$actuallyUseQuery = $useQuery;
|
116 |
+
$expanded = '';
|
117 |
+
|
118 |
+
if (is_array($variable)) {
|
119 |
+
$isAssoc = $this->isAssoc($variable);
|
120 |
+
$kvp = [];
|
121 |
+
foreach ($variable as $key => $var) {
|
122 |
+
if ($isAssoc) {
|
123 |
+
$key = rawurlencode($key);
|
124 |
+
$isNestedArray = is_array($var);
|
125 |
+
} else {
|
126 |
+
$isNestedArray = false;
|
127 |
+
}
|
128 |
+
|
129 |
+
if (!$isNestedArray) {
|
130 |
+
$var = rawurlencode($var);
|
131 |
+
if ($parsed['operator'] === '+' ||
|
132 |
+
$parsed['operator'] === '#'
|
133 |
+
) {
|
134 |
+
$var = $this->decodeReserved($var);
|
135 |
+
}
|
136 |
+
}
|
137 |
+
|
138 |
+
if ($value['modifier'] === '*') {
|
139 |
+
if ($isAssoc) {
|
140 |
+
if ($isNestedArray) {
|
141 |
+
// Nested arrays must allow for deeply nested
|
142 |
+
// structures.
|
143 |
+
$var = strtr(
|
144 |
+
http_build_query([$key => $var]),
|
145 |
+
$rfc1738to3986
|
146 |
+
);
|
147 |
+
} else {
|
148 |
+
$var = $key . '=' . $var;
|
149 |
+
}
|
150 |
+
} elseif ($key > 0 && $actuallyUseQuery) {
|
151 |
+
$var = $value['value'] . '=' . $var;
|
152 |
+
}
|
153 |
+
}
|
154 |
+
|
155 |
+
$kvp[$key] = $var;
|
156 |
+
}
|
157 |
+
|
158 |
+
if (empty($variable)) {
|
159 |
+
$actuallyUseQuery = false;
|
160 |
+
} elseif ($value['modifier'] === '*') {
|
161 |
+
$expanded = implode($joiner, $kvp);
|
162 |
+
if ($isAssoc) {
|
163 |
+
// Don't prepend the value name when using the explode
|
164 |
+
// modifier with an associative array.
|
165 |
+
$actuallyUseQuery = false;
|
166 |
+
}
|
167 |
+
} else {
|
168 |
+
if ($isAssoc) {
|
169 |
+
// When an associative array is encountered and the
|
170 |
+
// explode modifier is not set, then the result must be
|
171 |
+
// a comma separated list of keys followed by their
|
172 |
+
// respective values.
|
173 |
+
foreach ($kvp as $k => &$v) {
|
174 |
+
$v = $k . ',' . $v;
|
175 |
+
}
|
176 |
+
}
|
177 |
+
$expanded = implode(',', $kvp);
|
178 |
+
}
|
179 |
+
} else {
|
180 |
+
if ($value['modifier'] === ':') {
|
181 |
+
$variable = substr($variable, 0, $value['position']);
|
182 |
+
}
|
183 |
+
$expanded = rawurlencode($variable);
|
184 |
+
if ($parsed['operator'] === '+' || $parsed['operator'] === '#') {
|
185 |
+
$expanded = $this->decodeReserved($expanded);
|
186 |
+
}
|
187 |
+
}
|
188 |
+
|
189 |
+
if ($actuallyUseQuery) {
|
190 |
+
if (!$expanded && $joiner !== '&') {
|
191 |
+
$expanded = $value['value'];
|
192 |
+
} else {
|
193 |
+
$expanded = $value['value'] . '=' . $expanded;
|
194 |
+
}
|
195 |
+
}
|
196 |
+
|
197 |
+
$replacements[] = $expanded;
|
198 |
+
}
|
199 |
+
|
200 |
+
$ret = implode($joiner, $replacements);
|
201 |
+
if ($ret && $prefix) {
|
202 |
+
return $prefix . $ret;
|
203 |
+
}
|
204 |
+
|
205 |
+
return $ret;
|
206 |
+
}
|
207 |
+
|
208 |
+
/**
|
209 |
+
* Determines if an array is associative.
|
210 |
+
*
|
211 |
+
* This makes the assumption that input arrays are sequences or hashes.
|
212 |
+
* This assumption is a tradeoff for accuracy in favor of speed, but it
|
213 |
+
* should work in almost every case where input is supplied for a URI
|
214 |
+
* template.
|
215 |
+
*
|
216 |
+
* @param array $array Array to check
|
217 |
+
*
|
218 |
+
* @return bool
|
219 |
+
*/
|
220 |
+
private function isAssoc(array $array)
|
221 |
+
{
|
222 |
+
return $array && array_keys($array)[0] !== 0;
|
223 |
+
}
|
224 |
+
|
225 |
+
/**
|
226 |
+
* Removes percent encoding on reserved characters (used with + and #
|
227 |
+
* modifiers).
|
228 |
+
*
|
229 |
+
* @param string $string String to fix
|
230 |
+
*
|
231 |
+
* @return string
|
232 |
+
*/
|
233 |
+
private function decodeReserved($string)
|
234 |
+
{
|
235 |
+
return str_replace(self::$delimsPct, self::$delims, $string);
|
236 |
+
}
|
237 |
+
}
|
vendor/guzzlehttp/guzzle/src/functions.php
CHANGED
@@ -1,333 +1,333 @@
|
|
1 |
-
<?php
|
2 |
-
namespace GuzzleHttp;
|
3 |
-
|
4 |
-
use GuzzleHttp\Handler\CurlHandler;
|
5 |
-
use GuzzleHttp\Handler\CurlMultiHandler;
|
6 |
-
use GuzzleHttp\Handler\Proxy;
|
7 |
-
use GuzzleHttp\Handler\StreamHandler;
|
8 |
-
|
9 |
-
/**
|
10 |
-
* Expands a URI template
|
11 |
-
*
|
12 |
-
* @param string $template URI template
|
13 |
-
* @param array $variables Template variables
|
14 |
-
*
|
15 |
-
* @return string
|
16 |
-
*/
|
17 |
-
function uri_template($template, array $variables)
|
18 |
-
{
|
19 |
-
if (extension_loaded('uri_template')) {
|
20 |
-
// @codeCoverageIgnoreStart
|
21 |
-
return \uri_template($template, $variables);
|
22 |
-
// @codeCoverageIgnoreEnd
|
23 |
-
}
|
24 |
-
|
25 |
-
static $uriTemplate;
|
26 |
-
if (!$uriTemplate) {
|
27 |
-
$uriTemplate = new UriTemplate();
|
28 |
-
}
|
29 |
-
|
30 |
-
return $uriTemplate->expand($template, $variables);
|
31 |
-
}
|
32 |
-
|
33 |
-
/**
|
34 |
-
* Debug function used to describe the provided value type and class.
|
35 |
-
*
|
36 |
-
* @param mixed $input
|
37 |
-
*
|
38 |
-
* @return string Returns a string containing the type of the variable and
|
39 |
-
* if a class is provided, the class name.
|
40 |
-
*/
|
41 |
-
function describe_type($input)
|
42 |
-
{
|
43 |
-
switch (gettype($input)) {
|
44 |
-
case 'object':
|
45 |
-
return 'object(' . get_class($input) . ')';
|
46 |
-
case 'array':
|
47 |
-
return 'array(' . count($input) . ')';
|
48 |
-
default:
|
49 |
-
ob_start();
|
50 |
-
var_dump($input);
|
51 |
-
// normalize float vs double
|
52 |
-
return str_replace('double(', 'float(', rtrim(ob_get_clean()));
|
53 |
-
}
|
54 |
-
}
|
55 |
-
|
56 |
-
/**
|
57 |
-
* Parses an array of header lines into an associative array of headers.
|
58 |
-
*
|
59 |
-
* @param array $lines Header lines array of strings in the following
|
60 |
-
* format: "Name: Value"
|
61 |
-
* @return array
|
62 |
-
*/
|
63 |
-
function headers_from_lines($lines)
|
64 |
-
{
|
65 |
-
$headers = [];
|
66 |
-
|
67 |
-
foreach ($lines as $line) {
|
68 |
-
$parts = explode(':', $line, 2);
|
69 |
-
$headers[trim($parts[0])][] = isset($parts[1])
|
70 |
-
? trim($parts[1])
|
71 |
-
: null;
|
72 |
-
}
|
73 |
-
|
74 |
-
return $headers;
|
75 |
-
}
|
76 |
-
|
77 |
-
/**
|
78 |
-
* Returns a debug stream based on the provided variable.
|
79 |
-
*
|
80 |
-
* @param mixed $value Optional value
|
81 |
-
*
|
82 |
-
* @return resource
|
83 |
-
*/
|
84 |
-
function debug_resource($value = null)
|
85 |
-
{
|
86 |
-
if (is_resource($value)) {
|
87 |
-
return $value;
|
88 |
-
} elseif (defined('STDOUT')) {
|
89 |
-
return STDOUT;
|
90 |
-
}
|
91 |
-
|
92 |
-
return fopen('php://output', 'w');
|
93 |
-
}
|
94 |
-
|
95 |
-
/**
|
96 |
-
* Chooses and creates a default handler to use based on the environment.
|
97 |
-
*
|
98 |
-
* The returned handler is not wrapped by any default middlewares.
|
99 |
-
*
|
100 |
-
* @throws \RuntimeException if no viable Handler is available.
|
101 |
-
* @return callable Returns the best handler for the given system.
|
102 |
-
*/
|
103 |
-
function choose_handler()
|
104 |
-
{
|
105 |
-
$handler = null;
|
106 |
-
if (function_exists('curl_multi_exec') && function_exists('curl_exec')) {
|
107 |
-
$handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler());
|
108 |
-
} elseif (function_exists('curl_exec')) {
|
109 |
-
$handler = new CurlHandler();
|
110 |
-
} elseif (function_exists('curl_multi_exec')) {
|
111 |
-
$handler = new CurlMultiHandler();
|
112 |
-
}
|
113 |
-
|
114 |
-
if (ini_get('allow_url_fopen')) {
|
115 |
-
$handler = $handler
|
116 |
-
? Proxy::wrapStreaming($handler, new StreamHandler())
|
117 |
-
: new StreamHandler();
|
118 |
-
} elseif (!$handler) {
|
119 |
-
throw new \RuntimeException('GuzzleHttp requires cURL, the '
|
120 |
-
. 'allow_url_fopen ini setting, or a custom HTTP handler.');
|
121 |
-
}
|
122 |
-
|
123 |
-
return $handler;
|
124 |
-
}
|
125 |
-
|
126 |
-
/**
|
127 |
-
* Get the default User-Agent string to use with Guzzle
|
128 |
-
*
|
129 |
-
* @return string
|
130 |
-
*/
|
131 |
-
function default_user_agent()
|
132 |
-
{
|
133 |
-
static $defaultAgent = '';
|
134 |
-
|
135 |
-
if (!$defaultAgent) {
|
136 |
-
$defaultAgent = 'GuzzleHttp/' . Client::VERSION;
|
137 |
-
if (extension_loaded('curl') && function_exists('curl_version')) {
|
138 |
-
$defaultAgent .= ' curl/' . \curl_version()['version'];
|
139 |
-
}
|
140 |
-
$defaultAgent .= ' PHP/' . PHP_VERSION;
|
141 |
-
}
|
142 |
-
|
143 |
-
return $defaultAgent;
|
144 |
-
}
|
145 |
-
|
146 |
-
/**
|
147 |
-
* Returns the default cacert bundle for the current system.
|
148 |
-
*
|
149 |
-
* First, the openssl.cafile and curl.cainfo php.ini settings are checked.
|
150 |
-
* If those settings are not configured, then the common locations for
|
151 |
-
* bundles found on Red Hat, CentOS, Fedora, Ubuntu, Debian, FreeBSD, OS X
|
152 |
-
* and Windows are checked. If any of these file locations are found on
|
153 |
-
* disk, they will be utilized.
|
154 |
-
*
|
155 |
-
* Note: the result of this function is cached for subsequent calls.
|
156 |
-
*
|
157 |
-
* @return string
|
158 |
-
* @throws \RuntimeException if no bundle can be found.
|
159 |
-
*/
|
160 |
-
function default_ca_bundle()
|
161 |
-
{
|
162 |
-
static $cached = null;
|
163 |
-
static $cafiles = [
|
164 |
-
// Red Hat, CentOS, Fedora (provided by the ca-certificates package)
|
165 |
-
'/etc/pki/tls/certs/ca-bundle.crt',
|
166 |
-
// Ubuntu, Debian (provided by the ca-certificates package)
|
167 |
-
'/etc/ssl/certs/ca-certificates.crt',
|
168 |
-
// FreeBSD (provided by the ca_root_nss package)
|
169 |
-
'/usr/local/share/certs/ca-root-nss.crt',
|
170 |
-
// SLES 12 (provided by the ca-certificates package)
|
171 |
-
'/var/lib/ca-certificates/ca-bundle.pem',
|
172 |
-
// OS X provided by homebrew (using the default path)
|
173 |
-
'/usr/local/etc/openssl/cert.pem',
|
174 |
-
// Google app engine
|
175 |
-
'/etc/ca-certificates.crt',
|
176 |
-
// Windows?
|
177 |
-
'C:\\windows\\system32\\curl-ca-bundle.crt',
|
178 |
-
'C:\\windows\\curl-ca-bundle.crt',
|
179 |
-
];
|
180 |
-
|
181 |
-
if ($cached) {
|
182 |
-
return $cached;
|
183 |
-
}
|
184 |
-
|
185 |
-
if ($ca = ini_get('openssl.cafile')) {
|
186 |
-
return $cached = $ca;
|
187 |
-
}
|
188 |
-
|
189 |
-
if ($ca = ini_get('curl.cainfo')) {
|
190 |
-
return $cached = $ca;
|
191 |
-
}
|
192 |
-
|
193 |
-
foreach ($cafiles as $filename) {
|
194 |
-
if (file_exists($filename)) {
|
195 |
-
return $cached = $filename;
|
196 |
-
}
|
197 |
-
}
|
198 |
-
|
199 |
-
throw new \RuntimeException(<<< EOT
|
200 |
-
No system CA bundle could be found in any of the the common system locations.
|
201 |
-
PHP versions earlier than 5.6 are not properly configured to use the system's
|
202 |
-
CA bundle by default. In order to verify peer certificates, you will need to
|
203 |
-
supply the path on disk to a certificate bundle to the 'verify' request
|
204 |
-
option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not
|
205 |
-
need a specific certificate bundle, then Mozilla provides a commonly used CA
|
206 |
-
bundle which can be downloaded here (provided by the maintainer of cURL):
|
207 |
-
https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt. Once
|
208 |
-
you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP
|
209 |
-
ini setting to point to the path to the file, allowing you to omit the 'verify'
|
210 |
-
request option. See http://curl.haxx.se/docs/sslcerts.html for more
|
211 |
-
information.
|
212 |
-
EOT
|
213 |
-
);
|
214 |
-
}
|
215 |
-
|
216 |
-
/**
|
217 |
-
* Creates an associative array of lowercase header names to the actual
|
218 |
-
* header casing.
|
219 |
-
*
|
220 |
-
* @param array $headers
|
221 |
-
*
|
222 |
-
* @return array
|
223 |
-
*/
|
224 |
-
function normalize_header_keys(array $headers)
|
225 |
-
{
|
226 |
-
$result = [];
|
227 |
-
foreach (array_keys($headers) as $key) {
|
228 |
-
$result[strtolower($key)] = $key;
|
229 |
-
}
|
230 |
-
|
231 |
-
return $result;
|
232 |
-
}
|
233 |
-
|
234 |
-
/**
|
235 |
-
* Returns true if the provided host matches any of the no proxy areas.
|
236 |
-
*
|
237 |
-
* This method will strip a port from the host if it is present. Each pattern
|
238 |
-
* can be matched with an exact match (e.g., "foo.com" == "foo.com") or a
|
239 |
-
* partial match: (e.g., "foo.com" == "baz.foo.com" and ".foo.com" ==
|
240 |
-
* "baz.foo.com", but ".foo.com" != "foo.com").
|
241 |
-
*
|
242 |
-
* Areas are matched in the following cases:
|
243 |
-
* 1. "*" (without quotes) always matches any hosts.
|
244 |
-
* 2. An exact match.
|
245 |
-
* 3. The area starts with "." and the area is the last part of the host. e.g.
|
246 |
-
* '.mit.edu' will match any host that ends with '.mit.edu'.
|
247 |
-
*
|
248 |
-
* @param string $host Host to check against the patterns.
|
249 |
-
* @param array $noProxyArray An array of host patterns.
|
250 |
-
*
|
251 |
-
* @return bool
|
252 |
-
*/
|
253 |
-
function is_host_in_noproxy($host, array $noProxyArray)
|
254 |
-
{
|
255 |
-
if (strlen($host) === 0) {
|
256 |
-
throw new \InvalidArgumentException('Empty host provided');
|
257 |
-
}
|
258 |
-
|
259 |
-
// Strip port if present.
|
260 |
-
if (strpos($host, ':')) {
|
261 |
-
$host = explode($host, ':', 2)[0];
|
262 |
-
}
|
263 |
-
|
264 |
-
foreach ($noProxyArray as $area) {
|
265 |
-
// Always match on wildcards.
|
266 |
-
if ($area === '*') {
|
267 |
-
return true;
|
268 |
-
} elseif (empty($area)) {
|
269 |
-
// Don't match on empty values.
|
270 |
-
continue;
|
271 |
-
} elseif ($area === $host) {
|
272 |
-
// Exact matches.
|
273 |
-
return true;
|
274 |
-
} else {
|
275 |
-
// Special match if the area when prefixed with ".". Remove any
|
276 |
-
// existing leading "." and add a new leading ".".
|
277 |
-
$area = '.' . ltrim($area, '.');
|
278 |
-
if (substr($host, -(strlen($area))) === $area) {
|
279 |
-
return true;
|
280 |
-
}
|
281 |
-
}
|
282 |
-
}
|
283 |
-
|
284 |
-
return false;
|
285 |
-
}
|
286 |
-
|
287 |
-
/**
|
288 |
-
* Wrapper for json_decode that throws when an error occurs.
|
289 |
-
*
|
290 |
-
* @param string $json JSON data to parse
|
291 |
-
* @param bool $assoc When true, returned objects will be converted
|
292 |
-
* into associative arrays.
|
293 |
-
* @param int $depth User specified recursion depth.
|
294 |
-
* @param int $options Bitmask of JSON decode options.
|
295 |
-
*
|
296 |
-
* @return mixed
|
297 |
-
* @throws \InvalidArgumentException if the JSON cannot be decoded.
|
298 |
-
* @link http://www.php.net/manual/en/function.json-decode.php
|
299 |
-
*/
|
300 |
-
function json_decode($json, $assoc = false, $depth = 512, $options = 0)
|
301 |
-
{
|
302 |
-
$data = \json_decode($json, $assoc, $depth, $options);
|
303 |
-
if (JSON_ERROR_NONE !== json_last_error()) {
|
304 |
-
throw new \InvalidArgumentException(
|
305 |
-
'json_decode error: ' . json_last_error_msg()
|
306 |
-
);
|
307 |
-
}
|
308 |
-
|
309 |
-
return $data;
|
310 |
-
}
|
311 |
-
|
312 |
-
/**
|
313 |
-
* Wrapper for JSON encoding that throws when an error occurs.
|
314 |
-
*
|
315 |
-
* @param mixed $value The value being encoded
|
316 |
-
* @param int $options JSON encode option bitmask
|
317 |
-
* @param int $depth Set the maximum depth. Must be greater than zero.
|
318 |
-
*
|
319 |
-
* @return string
|
320 |
-
* @throws \InvalidArgumentException if the JSON cannot be encoded.
|
321 |
-
* @link http://www.php.net/manual/en/function.json-encode.php
|
322 |
-
*/
|
323 |
-
function json_encode($value, $options = 0, $depth = 512)
|
324 |
-
{
|
325 |
-
$json = \json_encode($value, $options, $depth);
|
326 |
-
if (JSON_ERROR_NONE !== json_last_error()) {
|
327 |
-
throw new \InvalidArgumentException(
|
328 |
-
'json_encode error: ' . json_last_error_msg()
|
329 |
-
);
|
330 |
-
}
|
331 |
-
|
332 |
-
return $json;
|
333 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace GuzzleHttp;
|
3 |
+
|
4 |
+
use GuzzleHttp\Handler\CurlHandler;
|
5 |
+
use GuzzleHttp\Handler\CurlMultiHandler;
|
6 |
+
use GuzzleHttp\Handler\Proxy;
|
7 |
+
use GuzzleHttp\Handler\StreamHandler;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Expands a URI template
|
11 |
+
*
|
12 |
+
* @param string $template URI template
|
13 |
+
* @param array $variables Template variables
|
14 |
+
*
|
15 |
+
* @return string
|
16 |
+
*/
|
17 |
+
function uri_template($template, array $variables)
|
18 |
+
{
|
19 |
+
if (extension_loaded('uri_template')) {
|
20 |
+
// @codeCoverageIgnoreStart
|
21 |
+
return \uri_template($template, $variables);
|
22 |
+
// @codeCoverageIgnoreEnd
|
23 |
+
}
|
24 |
+
|
25 |
+
static $uriTemplate;
|
26 |
+
if (!$uriTemplate) {
|
27 |
+
$uriTemplate = new UriTemplate();
|
28 |
+
}
|
29 |
+
|
30 |
+
return $uriTemplate->expand($template, $variables);
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Debug function used to describe the provided value type and class.
|
35 |
+
*
|
36 |
+
* @param mixed $input
|
37 |
+
*
|
38 |
+
* @return string Returns a string containing the type of the variable and
|
39 |
+
* if a class is provided, the class name.
|
40 |
+
*/
|
41 |
+
function describe_type($input)
|
42 |
+
{
|
43 |
+
switch (gettype($input)) {
|
44 |
+
case 'object':
|
45 |
+
return 'object(' . get_class($input) . ')';
|
46 |
+
case 'array':
|
47 |
+
return 'array(' . count($input) . ')';
|
48 |
+
default:
|
49 |
+
ob_start();
|
50 |
+
var_dump($input);
|
51 |
+
// normalize float vs double
|
52 |
+
return str_replace('double(', 'float(', rtrim(ob_get_clean()));
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Parses an array of header lines into an associative array of headers.
|
58 |
+
*
|
59 |
+
* @param array $lines Header lines array of strings in the following
|
60 |
+
* format: "Name: Value"
|
61 |
+
* @return array
|
62 |
+
*/
|
63 |
+
function headers_from_lines($lines)
|
64 |
+
{
|
65 |
+
$headers = [];
|
66 |
+
|
67 |
+
foreach ($lines as $line) {
|
68 |
+
$parts = explode(':', $line, 2);
|
69 |
+
$headers[trim($parts[0])][] = isset($parts[1])
|
70 |
+
? trim($parts[1])
|
71 |
+
: null;
|
72 |
+
}
|
73 |
+
|
74 |
+
return $headers;
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Returns a debug stream based on the provided variable.
|
79 |
+
*
|
80 |
+
* @param mixed $value Optional value
|
81 |
+
*
|
82 |
+
* @return resource
|
83 |
+
*/
|
84 |
+
function debug_resource($value = null)
|
85 |
+
{
|
86 |
+
if (is_resource($value)) {
|
87 |
+
return $value;
|
88 |
+
} elseif (defined('STDOUT')) {
|
89 |
+
return STDOUT;
|
90 |
+
}
|
91 |
+
|
92 |
+
return fopen('php://output', 'w');
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Chooses and creates a default handler to use based on the environment.
|
97 |
+
*
|
98 |
+
* The returned handler is not wrapped by any default middlewares.
|
99 |
+
*
|
100 |
+
* @throws \RuntimeException if no viable Handler is available.
|
101 |
+
* @return callable Returns the best handler for the given system.
|
102 |
+
*/
|
103 |
+
function choose_handler()
|
104 |
+
{
|
105 |
+
$handler = null;
|
106 |
+
if (function_exists('curl_multi_exec') && function_exists('curl_exec')) {
|
107 |
+
$handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler());
|
108 |
+
} elseif (function_exists('curl_exec')) {
|
109 |
+
$handler = new CurlHandler();
|
110 |
+
} elseif (function_exists('curl_multi_exec')) {
|
111 |
+
$handler = new CurlMultiHandler();
|
112 |
+
}
|
113 |
+
|
114 |
+
if (ini_get('allow_url_fopen')) {
|
115 |
+
$handler = $handler
|
116 |
+
? Proxy::wrapStreaming($handler, new StreamHandler())
|
117 |
+
: new StreamHandler();
|
118 |
+
} elseif (!$handler) {
|
119 |
+
throw new \RuntimeException('GuzzleHttp requires cURL, the '
|
120 |
+
. 'allow_url_fopen ini setting, or a custom HTTP handler.');
|
121 |
+
}
|
122 |
+
|
123 |
+
return $handler;
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Get the default User-Agent string to use with Guzzle
|
128 |
+
*
|
129 |
+
* @return string
|
130 |
+
*/
|
131 |
+
function default_user_agent()
|
132 |
+
{
|
133 |
+
static $defaultAgent = '';
|
134 |
+
|
135 |
+
if (!$defaultAgent) {
|
136 |
+
$defaultAgent = 'GuzzleHttp/' . Client::VERSION;
|
137 |
+
if (extension_loaded('curl') && function_exists('curl_version')) {
|
138 |
+
$defaultAgent .= ' curl/' . \curl_version()['version'];
|
139 |
+
}
|
140 |
+
$defaultAgent .= ' PHP/' . PHP_VERSION;
|
141 |
+
}
|
142 |
+
|
143 |
+
return $defaultAgent;
|
144 |
+
}
|
145 |
+
|
146 |
+
/**
|
147 |
+
* Returns the default cacert bundle for the current system.
|
148 |
+
*
|
149 |
+
* First, the openssl.cafile and curl.cainfo php.ini settings are checked.
|
150 |
+
* If those settings are not configured, then the common locations for
|
151 |
+
* bundles found on Red Hat, CentOS, Fedora, Ubuntu, Debian, FreeBSD, OS X
|
152 |
+
* and Windows are checked. If any of these file locations are found on
|
153 |
+
* disk, they will be utilized.
|
154 |
+
*
|
155 |
+
* Note: the result of this function is cached for subsequent calls.
|
156 |
+
*
|
157 |
+
* @return string
|
158 |
+
* @throws \RuntimeException if no bundle can be found.
|
159 |
+
*/
|
160 |
+
function default_ca_bundle()
|
161 |
+
{
|
162 |
+
static $cached = null;
|
163 |
+
static $cafiles = [
|
164 |
+
// Red Hat, CentOS, Fedora (provided by the ca-certificates package)
|
165 |
+
'/etc/pki/tls/certs/ca-bundle.crt',
|
166 |
+
// Ubuntu, Debian (provided by the ca-certificates package)
|
167 |
+
'/etc/ssl/certs/ca-certificates.crt',
|
168 |
+
// FreeBSD (provided by the ca_root_nss package)
|
169 |
+
'/usr/local/share/certs/ca-root-nss.crt',
|
170 |
+
// SLES 12 (provided by the ca-certificates package)
|
171 |
+
'/var/lib/ca-certificates/ca-bundle.pem',
|
172 |
+
// OS X provided by homebrew (using the default path)
|
173 |
+
'/usr/local/etc/openssl/cert.pem',
|
174 |
+
// Google app engine
|
175 |
+
'/etc/ca-certificates.crt',
|
176 |
+
// Windows?
|
177 |
+
'C:\\windows\\system32\\curl-ca-bundle.crt',
|
178 |
+
'C:\\windows\\curl-ca-bundle.crt',
|
179 |
+
];
|
180 |
+
|
181 |
+
if ($cached) {
|
182 |
+
return $cached;
|
183 |
+
}
|
184 |
+
|
185 |
+
if ($ca = ini_get('openssl.cafile')) {
|
186 |
+
return $cached = $ca;
|
187 |
+
}
|
188 |
+
|
189 |
+
if ($ca = ini_get('curl.cainfo')) {
|
190 |
+
return $cached = $ca;
|
191 |
+
}
|
192 |
+
|
193 |
+
foreach ($cafiles as $filename) {
|
194 |
+
if (file_exists($filename)) {
|
195 |
+
return $cached = $filename;
|
196 |
+
}
|
197 |
+
}
|
198 |
+
|
199 |
+
throw new \RuntimeException(<<< EOT
|
200 |
+
No system CA bundle could be found in any of the the common system locations.
|
201 |
+
PHP versions earlier than 5.6 are not properly configured to use the system's
|
202 |
+
CA bundle by default. In order to verify peer certificates, you will need to
|
203 |
+
supply the path on disk to a certificate bundle to the 'verify' request
|
204 |
+
option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not
|
205 |
+
need a specific certificate bundle, then Mozilla provides a commonly used CA
|
206 |
+
bundle which can be downloaded here (provided by the maintainer of cURL):
|
207 |
+
https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt. Once
|
208 |
+
you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP
|
209 |
+
ini setting to point to the path to the file, allowing you to omit the 'verify'
|
210 |
+
request option. See http://curl.haxx.se/docs/sslcerts.html for more
|
211 |
+
information.
|
212 |
+
EOT
|
213 |
+
);
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* Creates an associative array of lowercase header names to the actual
|
218 |
+
* header casing.
|
219 |
+
*
|
220 |
+
* @param array $headers
|
221 |
+
*
|
222 |
+
* @return array
|
223 |
+
*/
|
224 |
+
function normalize_header_keys(array $headers)
|
225 |
+
{
|
226 |
+
$result = [];
|
227 |
+
foreach (array_keys($headers) as $key) {
|
228 |
+
$result[strtolower($key)] = $key;
|
229 |
+
}
|
230 |
+
|
231 |
+
return $result;
|
232 |
+
}
|
233 |
+
|
234 |
+
/**
|
235 |
+
* Returns true if the provided host matches any of the no proxy areas.
|
236 |
+
*
|
237 |
+
* This method will strip a port from the host if it is present. Each pattern
|
238 |
+
* can be matched with an exact match (e.g., "foo.com" == "foo.com") or a
|
239 |
+
* partial match: (e.g., "foo.com" == "baz.foo.com" and ".foo.com" ==
|
240 |
+
* "baz.foo.com", but ".foo.com" != "foo.com").
|
241 |
+
*
|
242 |
+
* Areas are matched in the following cases:
|
243 |
+
* 1. "*" (without quotes) always matches any hosts.
|
244 |
+
* 2. An exact match.
|
245 |
+
* 3. The area starts with "." and the area is the last part of the host. e.g.
|
246 |
+
* '.mit.edu' will match any host that ends with '.mit.edu'.
|
247 |
+
*
|
248 |
+
* @param string $host Host to check against the patterns.
|
249 |
+
* @param array $noProxyArray An array of host patterns.
|
250 |
+
*
|
251 |
+
* @return bool
|
252 |
+
*/
|
253 |
+
function is_host_in_noproxy($host, array $noProxyArray)
|
254 |
+
{
|
255 |
+
if (strlen($host) === 0) {
|
256 |
+
throw new \InvalidArgumentException('Empty host provided');
|
257 |
+
}
|
258 |
+
|
259 |
+
// Strip port if present.
|
260 |
+
if (strpos($host, ':')) {
|
261 |
+
$host = explode($host, ':', 2)[0];
|
262 |
+
}
|
263 |
+
|
264 |
+
foreach ($noProxyArray as $area) {
|
265 |
+
// Always match on wildcards.
|
266 |
+
if ($area === '*') {
|
267 |
+
return true;
|
268 |
+
} elseif (empty($area)) {
|
269 |
+
// Don't match on empty values.
|
270 |
+
continue;
|
271 |
+
} elseif ($area === $host) {
|
272 |
+
// Exact matches.
|
273 |
+
return true;
|
274 |
+
} else {
|
275 |
+
// Special match if the area when prefixed with ".". Remove any
|
276 |
+
// existing leading "." and add a new leading ".".
|
277 |
+
$area = '.' . ltrim($area, '.');
|
278 |
+
if (substr($host, -(strlen($area))) === $area) {
|
279 |
+
return true;
|
280 |
+
}
|
281 |
+
}
|
282 |
+
}
|
283 |
+
|
284 |
+
return false;
|
285 |
+
}
|
286 |
+
|
287 |
+
/**
|
288 |
+
* Wrapper for json_decode that throws when an error occurs.
|
289 |
+
*
|
290 |
+
* @param string $json JSON data to parse
|
291 |
+
* @param bool $assoc When true, returned objects will be converted
|
292 |
+
* into associative arrays.
|
293 |
+
* @param int $depth User specified recursion depth.
|
294 |
+
* @param int $options Bitmask of JSON decode options.
|
295 |
+
*
|
296 |
+
* @return mixed
|
297 |
+
* @throws \InvalidArgumentException if the JSON cannot be decoded.
|
298 |
+
* @link http://www.php.net/manual/en/function.json-decode.php
|
299 |
+
*/
|
300 |
+
function json_decode($json, $assoc = false, $depth = 512, $options = 0)
|
301 |
+
{
|
302 |
+
$data = \json_decode($json, $assoc, $depth, $options);
|
303 |
+
if (JSON_ERROR_NONE !== json_last_error()) {
|
304 |
+
throw new \InvalidArgumentException(
|
305 |
+
'json_decode error: ' . json_last_error_msg()
|
306 |
+
);
|
307 |
+
}
|
308 |
+
|
309 |
+
return $data;
|
310 |
+
}
|
311 |
+
|
312 |
+
/**
|
313 |
+
* Wrapper for JSON encoding that throws when an error occurs.
|
314 |
+
*
|
315 |
+
* @param mixed $value The value being encoded
|
316 |
+
* @param int $options JSON encode option bitmask
|
317 |
+
* @param int $depth Set the maximum depth. Must be greater than zero.
|
318 |
+
*
|
319 |
+
* @return string
|
320 |
+
* @throws \InvalidArgumentException if the JSON cannot be encoded.
|
321 |
+
* @link http://www.php.net/manual/en/function.json-encode.php
|
322 |
+
*/
|
323 |
+
function json_encode($value, $options = 0, $depth = 512)
|
324 |
+
{
|
325 |
+
$json = \json_encode($value, $options, $depth);
|
326 |
+
if (JSON_ERROR_NONE !== json_last_error()) {
|
327 |
+
throw new \InvalidArgumentException(
|
328 |
+
'json_encode error: ' . json_last_error_msg()
|
329 |
+
);
|
330 |
+
}
|
331 |
+
|
332 |
+
return $json;
|
333 |
+
}
|
vendor/guzzlehttp/guzzle/src/functions_include.php
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
// Don't redefine the functions if included multiple times.
|
4 |
-
if (!function_exists('GuzzleHttp\uri_template')) {
|
5 |
-
require __DIR__ . '/functions.php';
|
6 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// Don't redefine the functions if included multiple times.
|
4 |
+
if (!function_exists('GuzzleHttp\uri_template')) {
|
5 |
+
require __DIR__ . '/functions.php';
|
6 |
+
}
|
vendor/phpseclib/phpseclib/LICENSE
CHANGED
@@ -1,21 +1,20 @@
|
|
1 |
-
Copyright
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
1 |
+
Copyright (c) 2011-2019 TerraFrost and other contributors
|
2 |
+
|
3 |
+
Permission is hereby granted, free of charge, to any person obtaining
|
4 |
+
a copy of this software and associated documentation files (the
|
5 |
+
"Software"), to deal in the Software without restriction, including
|
6 |
+
without limitation the rights to use, copy, modify, merge, publish,
|
7 |
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8 |
+
permit persons to whom the Software is furnished to do so, subject to
|
9 |
+
the following conditions:
|
10 |
+
|
11 |
+
The above copyright notice and this permission notice shall be
|
12 |
+
included in all copies or substantial portions of the Software.
|
13 |
+
|
14 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15 |
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16 |
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17 |
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18 |
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19 |
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20 |
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php
CHANGED
@@ -1,126 +1,126 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Pure-PHP implementation of AES.
|
5 |
-
*
|
6 |
-
* Uses mcrypt, if available/possible, and an internal implementation, otherwise.
|
7 |
-
*
|
8 |
-
* PHP version 5
|
9 |
-
*
|
10 |
-
* NOTE: Since AES.php is (for compatibility and phpseclib-historical reasons) virtually
|
11 |
-
* just a wrapper to Rijndael.php you may consider using Rijndael.php instead of
|
12 |
-
* to save one include_once().
|
13 |
-
*
|
14 |
-
* If {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
|
15 |
-
* {@link self::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's 136-bits
|
16 |
-
* it'll be null-padded to 192-bits and 192 bits will be the key length until {@link self::setKey() setKey()}
|
17 |
-
* is called, again, at which point, it'll be recalculated.
|
18 |
-
*
|
19 |
-
* Since \phpseclib\Crypt\AES extends \phpseclib\Crypt\Rijndael, some functions are available to be called that, in the context of AES, don't
|
20 |
-
* make a whole lot of sense. {@link self::setBlockLength() setBlockLength()}, for instance. Calling that function,
|
21 |
-
* however possible, won't do anything (AES has a fixed block length whereas Rijndael has a variable one).
|
22 |
-
*
|
23 |
-
* Here's a short example of how to use this library:
|
24 |
-
* <code>
|
25 |
-
* <?php
|
26 |
-
* include 'vendor/autoload.php';
|
27 |
-
*
|
28 |
-
* $aes = new \phpseclib\Crypt\AES();
|
29 |
-
*
|
30 |
-
* $aes->setKey('abcdefghijklmnop');
|
31 |
-
*
|
32 |
-
* $size = 10 * 1024;
|
33 |
-
* $plaintext = '';
|
34 |
-
* for ($i = 0; $i < $size; $i++) {
|
35 |
-
* $plaintext.= 'a';
|
36 |
-
* }
|
37 |
-
*
|
38 |
-
* echo $aes->decrypt($aes->encrypt($plaintext));
|
39 |
-
* ?>
|
40 |
-
* </code>
|
41 |
-
*
|
42 |
-
* @category Crypt
|
43 |
-
* @package AES
|
44 |
-
* @author Jim Wigginton <terrafrost@php.net>
|
45 |
-
* @copyright 2008 Jim Wigginton
|
46 |
-
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
47 |
-
* @link http://phpseclib.sourceforge.net
|
48 |
-
*/
|
49 |
-
|
50 |
-
namespace phpseclib\Crypt;
|
51 |
-
|
52 |
-
/**
|
53 |
-
* Pure-PHP implementation of AES.
|
54 |
-
*
|
55 |
-
* @package AES
|
56 |
-
* @author Jim Wigginton <terrafrost@php.net>
|
57 |
-
* @access public
|
58 |
-
*/
|
59 |
-
class AES extends Rijndael
|
60 |
-
{
|
61 |
-
/**
|
62 |
-
* Dummy function
|
63 |
-
*
|
64 |
-
* Since \phpseclib\Crypt\AES extends \phpseclib\Crypt\Rijndael, this function is, technically, available, but it doesn't do anything.
|
65 |
-
*
|
66 |
-
* @see \phpseclib\Crypt\Rijndael::setBlockLength()
|
67 |
-
* @access public
|
68 |
-
* @param int $length
|
69 |
-
*/
|
70 |
-
function setBlockLength($length)
|
71 |
-
{
|
72 |
-
return;
|
73 |
-
}
|
74 |
-
|
75 |
-
/**
|
76 |
-
* Sets the key length
|
77 |
-
*
|
78 |
-
* Valid key lengths are 128, 192, and 256. If the length is less than 128, it will be rounded up to
|
79 |
-
* 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
|
80 |
-
*
|
81 |
-
* @see \phpseclib\Crypt\Rijndael:setKeyLength()
|
82 |
-
* @access public
|
83 |
-
* @param int $length
|
84 |
-
*/
|
85 |
-
function setKeyLength($length)
|
86 |
-
{
|
87 |
-
switch ($length) {
|
88 |
-
case 160:
|
89 |
-
$length = 192;
|
90 |
-
break;
|
91 |
-
case 224:
|
92 |
-
$length = 256;
|
93 |
-
}
|
94 |
-
parent::setKeyLength($length);
|
95 |
-
}
|
96 |
-
|
97 |
-
/**
|
98 |
-
* Sets the key.
|
99 |
-
*
|
100 |
-
* Rijndael supports five different key lengths, AES only supports three.
|
101 |
-
*
|
102 |
-
* @see \phpseclib\Crypt\Rijndael:setKey()
|
103 |
-
* @see setKeyLength()
|
104 |
-
* @access public
|
105 |
-
* @param string $key
|
106 |
-
*/
|
107 |
-
function setKey($key)
|
108 |
-
{
|
109 |
-
parent::setKey($key);
|
110 |
-
|
111 |
-
if (!$this->explicit_key_length) {
|
112 |
-
$length = strlen($key);
|
113 |
-
switch (true) {
|
114 |
-
case $length <= 16:
|
115 |
-
$this->key_length = 16;
|
116 |
-
break;
|
117 |
-
case $length <= 24:
|
118 |
-
$this->key_length = 24;
|
119 |
-
break;
|
120 |
-
default:
|
121 |
-
$this->key_length = 32;
|
122 |
-
}
|
123 |
-
$this->_setEngine();
|
124 |
-
}
|
125 |
-
}
|
126 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Pure-PHP implementation of AES.
|
5 |
+
*
|
6 |
+
* Uses mcrypt, if available/possible, and an internal implementation, otherwise.
|
7 |
+
*
|
8 |
+
* PHP version 5
|
9 |
+
*
|
10 |
+
* NOTE: Since AES.php is (for compatibility and phpseclib-historical reasons) virtually
|
11 |
+
* just a wrapper to Rijndael.php you may consider using Rijndael.php instead of
|
12 |
+
* to save one include_once().
|
13 |
+
*
|
14 |
+
* If {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
|
15 |
+
* {@link self::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's 136-bits
|
16 |
+
* it'll be null-padded to 192-bits and 192 bits will be the key length until {@link self::setKey() setKey()}
|
17 |
+
* is called, again, at which point, it'll be recalculated.
|
18 |
+
*
|
19 |
+
* Since \phpseclib\Crypt\AES extends \phpseclib\Crypt\Rijndael, some functions are available to be called that, in the context of AES, don't
|
20 |
+
* make a whole lot of sense. {@link self::setBlockLength() setBlockLength()}, for instance. Calling that function,
|
21 |
+
* however possible, won't do anything (AES has a fixed block length whereas Rijndael has a variable one).
|
22 |
+
*
|
23 |
+
* Here's a short example of how to use this library:
|
24 |
+
* <code>
|
25 |
+
* <?php
|
26 |
+
* include 'vendor/autoload.php';
|
27 |
+
*
|
28 |
+
* $aes = new \phpseclib\Crypt\AES();
|
29 |
+
*
|
30 |
+
* $aes->setKey('abcdefghijklmnop');
|
31 |
+
*
|
32 |
+
* $size = 10 * 1024;
|
33 |
+
* $plaintext = '';
|
34 |
+
* for ($i = 0; $i < $size; $i++) {
|
35 |
+
* $plaintext.= 'a';
|
36 |
+
* }
|
37 |
+
*
|
38 |
+
* echo $aes->decrypt($aes->encrypt($plaintext));
|
39 |
+
* ?>
|
40 |
+
* </code>
|
41 |
+
*
|
42 |
+
* @category Crypt
|
43 |
+
* @package AES
|
44 |
+
* @author Jim Wigginton <terrafrost@php.net>
|
45 |
+
* @copyright 2008 Jim Wigginton
|
46 |
+
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
47 |
+
* @link http://phpseclib.sourceforge.net
|
48 |
+
*/
|
49 |
+
|
50 |
+
namespace phpseclib\Crypt;
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Pure-PHP implementation of AES.
|
54 |
+
*
|
55 |
+
* @package AES
|
56 |
+
* @author Jim Wigginton <terrafrost@php.net>
|
57 |
+
* @access public
|
58 |
+
*/
|
59 |
+
class AES extends Rijndael
|
60 |
+
{
|
61 |
+
/**
|
62 |
+
* Dummy function
|
63 |
+
*
|
64 |
+
* Since \phpseclib\Crypt\AES extends \phpseclib\Crypt\Rijndael, this function is, technically, available, but it doesn't do anything.
|
65 |
+
*
|
66 |
+
* @see \phpseclib\Crypt\Rijndael::setBlockLength()
|
67 |
+
* @access public
|
68 |
+
* @param int $length
|
69 |
+
*/
|
70 |
+
function setBlockLength($length)
|
71 |
+
{
|
72 |
+
return;
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Sets the key length
|
77 |
+
*
|
78 |
+
* Valid key lengths are 128, 192, and 256. If the length is less than 128, it will be rounded up to
|
79 |
+
* 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
|
80 |
+
*
|
81 |
+
* @see \phpseclib\Crypt\Rijndael:setKeyLength()
|
82 |
+
* @access public
|
83 |
+
* @param int $length
|
84 |
+
*/
|
85 |
+
function setKeyLength($length)
|
86 |
+
{
|
87 |
+
switch ($length) {
|
88 |
+
case 160:
|
89 |
+
$length = 192;
|
90 |
+
break;
|
91 |
+
case 224:
|
92 |
+
$length = 256;
|
93 |
+
}
|
94 |
+
parent::setKeyLength($length);
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Sets the key.
|
99 |
+
*
|
100 |
+
* Rijndael supports five different key lengths, AES only supports three.
|
101 |
+
*
|
102 |
+
* @see \phpseclib\Crypt\Rijndael:setKey()
|
103 |
+
* @see setKeyLength()
|
104 |
+
* @access public
|
105 |
+
* @param string $key
|
106 |
+
*/
|
107 |
+
function setKey($key)
|
108 |
+
{
|
109 |
+
parent::setKey($key);
|
110 |
+
|
111 |
+
if (!$this->explicit_key_length) {
|
112 |
+
$length = strlen($key);
|
113 |
+
switch (true) {
|
114 |
+
case $length <= 16:
|
115 |
+
$this->key_length = 16;
|
116 |
+
break;
|
117 |
+
case $length <= 24:
|
118 |
+
$this->key_length = 24;
|
119 |
+
break;
|
120 |
+
default:
|
121 |
+
$this->key_length = 32;
|
122 |
+
}
|
123 |
+
$this->_setEngine();
|
124 |
+
}
|
125 |
+
}
|
126 |
+
}
|
vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php
CHANGED
@@ -1,3201 +1,3201 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
|
5 |
-
*
|
6 |
-
* PHP version 5
|
7 |
-
*
|
8 |
-
* Here's an example of how to encrypt and decrypt text with this library:
|
9 |
-
* <code>
|
10 |
-
* <?php
|
11 |
-
* include 'vendor/autoload.php';
|
12 |
-
*
|
13 |
-
* $rsa = new \phpseclib\Crypt\RSA();
|
14 |
-
* extract($rsa->createKey());
|
15 |
-
*
|
16 |
-
* $plaintext = 'terrafrost';
|
17 |
-
*
|
18 |
-
* $rsa->loadKey($privatekey);
|
19 |
-
* $ciphertext = $rsa->encrypt($plaintext);
|
20 |
-
*
|
21 |
-
* $rsa->loadKey($publickey);
|
22 |
-
* echo $rsa->decrypt($ciphertext);
|
23 |
-
* ?>
|
24 |
-
* </code>
|
25 |
-
*
|
26 |
-
* Here's an example of how to create signatures and verify signatures with this library:
|
27 |
-
* <code>
|
28 |
-
* <?php
|
29 |
-
* include 'vendor/autoload.php';
|
30 |
-
*
|
31 |
-
* $rsa = new \phpseclib\Crypt\RSA();
|
32 |
-
* extract($rsa->createKey());
|
33 |
-
*
|
34 |
-
* $plaintext = 'terrafrost';
|
35 |
-
*
|
36 |
-
* $rsa->loadKey($privatekey);
|
37 |
-
* $signature = $rsa->sign($plaintext);
|
38 |
-
*
|
39 |
-
* $rsa->loadKey($publickey);
|
40 |
-
* echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
|
41 |
-
* ?>
|
42 |
-
* </code>
|
43 |
-
*
|
44 |
-
* @category Crypt
|
45 |
-
* @package RSA
|
46 |
-
* @author Jim Wigginton <terrafrost@php.net>
|
47 |
-
* @copyright 2009 Jim Wigginton
|
48 |
-
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
49 |
-
* @link http://phpseclib.sourceforge.net
|
50 |
-
*/
|
51 |
-
|
52 |
-
namespace phpseclib\Crypt;
|
53 |
-
|
54 |
-
use phpseclib\Math\BigInteger;
|
55 |
-
|
56 |
-
/**
|
57 |
-
* Pure-PHP PKCS#1 compliant implementation of RSA.
|
58 |
-
*
|
59 |
-
* @package RSA
|
60 |
-
* @author Jim Wigginton <terrafrost@php.net>
|
61 |
-
* @access public
|
62 |
-
*/
|
63 |
-
class RSA
|
64 |
-
{
|
65 |
-
/**#@+
|
66 |
-
* @access public
|
67 |
-
* @see self::encrypt()
|
68 |
-
* @see self::decrypt()
|
69 |
-
*/
|
70 |
-
/**
|
71 |
-
* Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
|
72 |
-
* (OAEP) for encryption / decryption.
|
73 |
-
*
|
74 |
-
* Uses sha1 by default.
|
75 |
-
*
|
76 |
-
* @see self::setHash()
|
77 |
-
* @see self::setMGFHash()
|
78 |
-
*/
|
79 |
-
const ENCRYPTION_OAEP = 1;
|
80 |
-
/**
|
81 |
-
* Use PKCS#1 padding.
|
82 |
-
*
|
83 |
-
* Although self::ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
|
84 |
-
* compatibility with protocols (like SSH-1) written before OAEP's introduction.
|
85 |
-
*/
|
86 |
-
const ENCRYPTION_PKCS1 = 2;
|
87 |
-
/**
|
88 |
-
* Do not use any padding
|
89 |
-
*
|
90 |
-
* Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy
|
91 |
-
* stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc.
|
92 |
-
*/
|
93 |
-
const ENCRYPTION_NONE = 3;
|
94 |
-
/**#@-*/
|
95 |
-
|
96 |
-
/**#@+
|
97 |
-
* @access public
|
98 |
-
* @see self::sign()
|
99 |
-
* @see self::verify()
|
100 |
-
* @see self::setHash()
|
101 |
-
*/
|
102 |
-
/**
|
103 |
-
* Use the Probabilistic Signature Scheme for signing
|
104 |
-
*
|
105 |
-
* Uses sha1 by default.
|
106 |
-
*
|
107 |
-
* @see self::setSaltLength()
|
108 |
-
* @see self::setMGFHash()
|
109 |
-
*/
|
110 |
-
const SIGNATURE_PSS = 1;
|
111 |
-
/**
|
112 |
-
* Use the PKCS#1 scheme by default.
|
113 |
-
*
|
114 |
-
* Although self::SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
|
115 |
-
* compatibility with protocols (like SSH-2) written before PSS's introduction.
|
116 |
-
*/
|
117 |
-
const SIGNATURE_PKCS1 = 2;
|
118 |
-
/**#@-*/
|
119 |
-
|
120 |
-
/**#@+
|
121 |
-
* @access private
|
122 |
-
* @see \phpseclib\Crypt\RSA::createKey()
|
123 |
-
*/
|
124 |
-
/**
|
125 |
-
* ASN1 Integer
|
126 |
-
*/
|
127 |
-
const ASN1_INTEGER = 2;
|
128 |
-
/**
|
129 |
-
* ASN1 Bit String
|
130 |
-
*/
|
131 |
-
const ASN1_BITSTRING = 3;
|
132 |
-
/**
|
133 |
-
* ASN1 Octet String
|
134 |
-
*/
|
135 |
-
const ASN1_OCTETSTRING = 4;
|
136 |
-
/**
|
137 |
-
* ASN1 Object Identifier
|
138 |
-
*/
|
139 |
-
const ASN1_OBJECT = 6;
|
140 |
-
/**
|
141 |
-
* ASN1 Sequence (with the constucted bit set)
|
142 |
-
*/
|
143 |
-
const ASN1_SEQUENCE = 48;
|
144 |
-
/**#@-*/
|
145 |
-
|
146 |
-
/**#@+
|
147 |
-
* @access private
|
148 |
-
* @see \phpseclib\Crypt\RSA::__construct()
|
149 |
-
*/
|
150 |
-
/**
|
151 |
-
* To use the pure-PHP implementation
|
152 |
-
*/
|
153 |
-
const MODE_INTERNAL = 1;
|
154 |
-
/**
|
155 |
-
* To use the OpenSSL library
|
156 |
-
*
|
157 |
-
* (if enabled; otherwise, the internal implementation will be used)
|
158 |
-
*/
|
159 |
-
const MODE_OPENSSL = 2;
|
160 |
-
/**#@-*/
|
161 |
-
|
162 |
-
/**#@+
|
163 |
-
* @access public
|
164 |
-
* @see \phpseclib\Crypt\RSA::createKey()
|
165 |
-
* @see \phpseclib\Crypt\RSA::setPrivateKeyFormat()
|
166 |
-
*/
|
167 |
-
/**
|
168 |
-
* PKCS#1 formatted private key
|
169 |
-
*
|
170 |
-
* Used by OpenSSH
|
171 |
-
*/
|
172 |
-
const PRIVATE_FORMAT_PKCS1 = 0;
|
173 |
-
/**
|
174 |
-
* PuTTY formatted private key
|
175 |
-
*/
|
176 |
-
const PRIVATE_FORMAT_PUTTY = 1;
|
177 |
-
/**
|
178 |
-
* XML formatted private key
|
179 |
-
*/
|
180 |
-
const PRIVATE_FORMAT_XML = 2;
|
181 |
-
/**
|
182 |
-
* PKCS#8 formatted private key
|
183 |
-
*/
|
184 |
-
const PRIVATE_FORMAT_PKCS8 = 8;
|
185 |
-
/**
|
186 |
-
* OpenSSH formatted private key
|
187 |
-
*/
|
188 |
-
const PRIVATE_FORMAT_OPENSSH = 9;
|
189 |
-
/**#@-*/
|
190 |
-
|
191 |
-
/**#@+
|
192 |
-
* @access public
|
193 |
-
* @see \phpseclib\Crypt\RSA::createKey()
|
194 |
-
* @see \phpseclib\Crypt\RSA::setPublicKeyFormat()
|
195 |
-
*/
|
196 |
-
/**
|
197 |
-
* Raw public key
|
198 |
-
*
|
199 |
-
* An array containing two \phpseclib\Math\BigInteger objects.
|
200 |
-
*
|
201 |
-
* The exponent can be indexed with any of the following:
|
202 |
-
*
|
203 |
-
* 0, e, exponent, publicExponent
|
204 |
-
*
|
205 |
-
* The modulus can be indexed with any of the following:
|
206 |
-
*
|
207 |
-
* 1, n, modulo, modulus
|
208 |
-
*/
|
209 |
-
const PUBLIC_FORMAT_RAW = 3;
|
210 |
-
/**
|
211 |
-
* PKCS#1 formatted public key (raw)
|
212 |
-
*
|
213 |
-
* Used by File/X509.php
|
214 |
-
*
|
215 |
-
* Has the following header:
|
216 |
-
*
|
217 |
-
* -----BEGIN RSA PUBLIC KEY-----
|
218 |
-
*
|
219 |
-
* Analogous to ssh-keygen's pem format (as specified by -m)
|
220 |
-
*/
|
221 |
-
const PUBLIC_FORMAT_PKCS1 = 4;
|
222 |
-
const PUBLIC_FORMAT_PKCS1_RAW = 4;
|
223 |
-
/**
|
224 |
-
* XML formatted public key
|
225 |
-
*/
|
226 |
-
const PUBLIC_FORMAT_XML = 5;
|
227 |
-
/**
|
228 |
-
* OpenSSH formatted public key
|
229 |
-
*
|
230 |
-
* Place in $HOME/.ssh/authorized_keys
|
231 |
-
*/
|
232 |
-
const PUBLIC_FORMAT_OPENSSH = 6;
|
233 |
-
/**
|
234 |
-
* PKCS#1 formatted public key (encapsulated)
|
235 |
-
*
|
236 |
-
* Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
|
237 |
-
*
|
238 |
-
* Has the following header:
|
239 |
-
*
|
240 |
-
* -----BEGIN PUBLIC KEY-----
|
241 |
-
*
|
242 |
-
* Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
|
243 |
-
* is specific to private keys it's basically creating a DER-encoded wrapper
|
244 |
-
* for keys. This just extends that same concept to public keys (much like ssh-keygen)
|
245 |
-
*/
|
246 |
-
const PUBLIC_FORMAT_PKCS8 = 7;
|
247 |
-
/**#@-*/
|
248 |
-
|
249 |
-
/**
|
250 |
-
* Precomputed Zero
|
251 |
-
*
|
252 |
-
* @var \phpseclib\Math\BigInteger
|
253 |
-
* @access private
|
254 |
-
*/
|
255 |
-
var $zero;
|
256 |
-
|
257 |
-
/**
|
258 |
-
* Precomputed One
|
259 |
-
*
|
260 |
-
* @var \phpseclib\Math\BigInteger
|
261 |
-
* @access private
|
262 |
-
*/
|
263 |
-
var $one;
|
264 |
-
|
265 |
-
/**
|
266 |
-
* Private Key Format
|
267 |
-
*
|
268 |
-
* @var int
|
269 |
-
* @access private
|
270 |
-
*/
|
271 |
-
var $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1;
|
272 |
-
|
273 |
-
/**
|
274 |
-
* Public Key Format
|
275 |
-
*
|
276 |
-
* @var int
|
277 |
-
* @access public
|
278 |
-
*/
|
279 |
-
var $publicKeyFormat = self::PUBLIC_FORMAT_PKCS8;
|
280 |
-
|
281 |
-
/**
|
282 |
-
* Modulus (ie. n)
|
283 |
-
*
|
284 |
-
* @var \phpseclib\Math\BigInteger
|
285 |
-
* @access private
|
286 |
-
*/
|
287 |
-
var $modulus;
|
288 |
-
|
289 |
-
/**
|
290 |
-
* Modulus length
|
291 |
-
*
|
292 |
-
* @var \phpseclib\Math\BigInteger
|
293 |
-
* @access private
|
294 |
-
*/
|
295 |
-
var $k;
|
296 |
-
|
297 |
-
/**
|
298 |
-
* Exponent (ie. e or d)
|
299 |
-
*
|
300 |
-
* @var \phpseclib\Math\BigInteger
|
301 |
-
* @access private
|
302 |
-
*/
|
303 |
-
var $exponent;
|
304 |
-
|
305 |
-
/**
|
306 |
-
* Primes for Chinese Remainder Theorem (ie. p and q)
|
307 |
-
*
|
308 |
-
* @var array
|
309 |
-
* @access private
|
310 |
-
*/
|
311 |
-
var $primes;
|
312 |
-
|
313 |
-
/**
|
314 |
-
* Exponents for Chinese Remainder Theorem (ie. dP and dQ)
|
315 |
-
*
|
316 |
-
* @var array
|
317 |
-
* @access private
|
318 |
-
*/
|
319 |
-
var $exponents;
|
320 |
-
|
321 |
-
/**
|
322 |
-
* Coefficients for Chinese Remainder Theorem (ie. qInv)
|
323 |
-
*
|
324 |
-
* @var array
|
325 |
-
* @access private
|
326 |
-
*/
|
327 |
-
var $coefficients;
|
328 |
-
|
329 |
-
/**
|
330 |
-
* Hash name
|
331 |
-
*
|
332 |
-
* @var string
|
333 |
-
* @access private
|
334 |
-
*/
|
335 |
-
var $hashName;
|
336 |
-
|
337 |
-
/**
|
338 |
-
* Hash function
|
339 |
-
*
|
340 |
-
* @var \phpseclib\Crypt\Hash
|
341 |
-
* @access private
|
342 |
-
*/
|
343 |
-
var $hash;
|
344 |
-
|
345 |
-
/**
|
346 |
-
* Length of hash function output
|
347 |
-
*
|
348 |
-
* @var int
|
349 |
-
* @access private
|
350 |
-
*/
|
351 |
-
var $hLen;
|
352 |
-
|
353 |
-
/**
|
354 |
-
* Length of salt
|
355 |
-
*
|
356 |
-
* @var int
|
357 |
-
* @access private
|
358 |
-
*/
|
359 |
-
var $sLen;
|
360 |
-
|
361 |
-
/**
|
362 |
-
* Hash function for the Mask Generation Function
|
363 |
-
*
|
364 |
-
* @var \phpseclib\Crypt\Hash
|
365 |
-
* @access private
|
366 |
-
*/
|
367 |
-
var $mgfHash;
|
368 |
-
|
369 |
-
/**
|
370 |
-
* Length of MGF hash function output
|
371 |
-
*
|
372 |
-
* @var int
|
373 |
-
* @access private
|
374 |
-
*/
|
375 |
-
var $mgfHLen;
|
376 |
-
|
377 |
-
/**
|
378 |
-
* Encryption mode
|
379 |
-
*
|
380 |
-
* @var int
|
381 |
-
* @access private
|
382 |
-
*/
|
383 |
-
var $encryptionMode = self::ENCRYPTION_OAEP;
|
384 |
-
|
385 |
-
/**
|
386 |
-
* Signature mode
|
387 |
-
*
|
388 |
-
* @var int
|
389 |
-
* @access private
|
390 |
-
*/
|
391 |
-
var $signatureMode = self::SIGNATURE_PSS;
|
392 |
-
|
393 |
-
/**
|
394 |
-
* Public Exponent
|
395 |
-
*
|
396 |
-
* @var mixed
|
397 |
-
* @access private
|
398 |
-
*/
|
399 |
-
var $publicExponent = false;
|
400 |
-
|
401 |
-
/**
|
402 |
-
* Password
|
403 |
-
*
|
404 |
-
* @var string
|
405 |
-
* @access private
|
406 |
-
*/
|
407 |
-
var $password = false;
|
408 |
-
|
409 |
-
/**
|
410 |
-
* Components
|
411 |
-
*
|
412 |
-
* For use with parsing XML formatted keys. PHP's XML Parser functions use utilized - instead of PHP's DOM functions -
|
413 |
-
* because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't.
|
414 |
-
*
|
415 |
-
* @see self::_start_element_handler()
|
416 |
-
* @var array
|
417 |
-
* @access private
|
418 |
-
*/
|
419 |
-
var $components = array();
|
420 |
-
|
421 |
-
/**
|
422 |
-
* Current String
|
423 |
-
*
|
424 |
-
* For use with parsing XML formatted keys.
|
425 |
-
*
|
426 |
-
* @see self::_character_handler()
|
427 |
-
* @see self::_stop_element_handler()
|
428 |
-
* @var mixed
|
429 |
-
* @access private
|
430 |
-
*/
|
431 |
-
var $current;
|
432 |
-
|
433 |
-
/**
|
434 |
-
* OpenSSL configuration file name.
|
435 |
-
*
|
436 |
-
* Set to null to use system configuration file.
|
437 |
-
* @see self::createKey()
|
438 |
-
* @var mixed
|
439 |
-
* @Access public
|
440 |
-
*/
|
441 |
-
var $configFile;
|
442 |
-
|
443 |
-
/**
|
444 |
-
* Public key comment field.
|
445 |
-
*
|
446 |
-
* @var string
|
447 |
-
* @access private
|
448 |
-
*/
|
449 |
-
var $comment = 'phpseclib-generated-key';
|
450 |
-
|
451 |
-
/**
|
452 |
-
* The constructor
|
453 |
-
*
|
454 |
-
* If you want to make use of the openssl extension, you'll need to set the mode manually, yourself. The reason
|
455 |
-
* \phpseclib\Crypt\RSA doesn't do it is because OpenSSL doesn't fail gracefully. openssl_pkey_new(), in particular, requires
|
456 |
-
* openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late.
|
457 |
-
*
|
458 |
-
* @return \phpseclib\Crypt\RSA
|
459 |
-
* @access public
|
460 |
-
*/
|
461 |
-
function __construct()
|
462 |
-
{
|
463 |
-
$this->configFile = dirname(__FILE__) . '/../openssl.cnf';
|
464 |
-
|
465 |
-
if (!defined('CRYPT_RSA_MODE')) {
|
466 |
-
switch (true) {
|
467 |
-
// Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular,
|
468 |
-
// Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger
|
469 |
-
// can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either.
|
470 |
-
case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
|
471 |
-
define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
|
472 |
-
break;
|
473 |
-
case extension_loaded('openssl') && file_exists($this->configFile):
|
474 |
-
// some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
|
475 |
-
$versions = array();
|
476 |
-
|
477 |
-
// avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems)
|
478 |
-
if (strpos(ini_get('disable_functions'), 'phpinfo') === false) {
|
479 |
-
ob_start();
|
480 |
-
@phpinfo();
|
481 |
-
$content = ob_get_contents();
|
482 |
-
ob_end_clean();
|
483 |
-
|
484 |
-
preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
|
485 |
-
|
486 |
-
if (!empty($matches[1])) {
|
487 |
-
for ($i = 0; $i < count($matches[1]); $i++) {
|
488 |
-
$fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
|
489 |
-
|
490 |
-
// Remove letter part in OpenSSL version
|
491 |
-
if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
|
492 |
-
$versions[$matches[1][$i]] = $fullVersion;
|
493 |
-
} else {
|
494 |
-
$versions[$matches[1][$i]] = $m[0];
|
495 |
-
}
|
496 |
-
}
|
497 |
-
}
|
498 |
-
}
|
499 |
-
|
500 |
-
// it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
|
501 |
-
switch (true) {
|
502 |
-
case !isset($versions['Header']):
|
503 |
-
case !isset($versions['Library']):
|
504 |
-
case $versions['Header'] == $versions['Library']:
|
505 |
-
case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0:
|
506 |
-
define('CRYPT_RSA_MODE', self::MODE_OPENSSL);
|
507 |
-
break;
|
508 |
-
default:
|
509 |
-
define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
|
510 |
-
define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
|
511 |
-
}
|
512 |
-
break;
|
513 |
-
default:
|
514 |
-
define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
|
515 |
-
}
|
516 |
-
}
|
517 |
-
|
518 |
-
$this->zero = new BigInteger();
|
519 |
-
$this->one = new BigInteger(1);
|
520 |
-
|
521 |
-
$this->hash = new Hash('sha1');
|
522 |
-
$this->hLen = $this->hash->getLength();
|
523 |
-
$this->hashName = 'sha1';
|
524 |
-
$this->mgfHash = new Hash('sha1');
|
525 |
-
$this->mgfHLen = $this->mgfHash->getLength();
|
526 |
-
}
|
527 |
-
|
528 |
-
/**
|
529 |
-
* Create public / private key pair
|
530 |
-
*
|
531 |
-
* Returns an array with the following three elements:
|
532 |
-
* - 'privatekey': The private key.
|
533 |
-
* - 'publickey': The public key.
|
534 |
-
* - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
|
535 |
-
* Will need to be passed back to \phpseclib\Crypt\RSA::createKey() as the third parameter for further processing.
|
536 |
-
*
|
537 |
-
* @access public
|
538 |
-
* @param int $bits
|
539 |
-
* @param int $timeout
|
540 |
-
* @param array $p
|
541 |
-
*/
|
542 |
-
function createKey($bits = 1024, $timeout = false, $partial = array())
|
543 |
-
{
|
544 |
-
if (!defined('CRYPT_RSA_EXPONENT')) {
|
545 |
-
// http://en.wikipedia.org/wiki/65537_%28number%29
|
546 |
-
define('CRYPT_RSA_EXPONENT', '65537');
|
547 |
-
}
|
548 |
-
// per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
|
549 |
-
// than 256 bits. as a consequence if the key you're trying to create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME
|
550 |
-
// to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if
|
551 |
-
// CRYPT_RSA_MODE is set to self::MODE_INTERNAL. if CRYPT_RSA_MODE is set to self::MODE_OPENSSL then
|
552 |
-
// CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key
|
553 |
-
// generation when there's a chance neither gmp nor OpenSSL are installed)
|
554 |
-
if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
|
555 |
-
define('CRYPT_RSA_SMALLEST_PRIME', 4096);
|
556 |
-
}
|
557 |
-
|
558 |
-
// OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
|
559 |
-
if (CRYPT_RSA_MODE == self::MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
|
560 |
-
$config = array();
|
561 |
-
if (isset($this->configFile)) {
|
562 |
-
$config['config'] = $this->configFile;
|
563 |
-
}
|
564 |
-
$rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config);
|
565 |
-
openssl_pkey_export($rsa, $privatekey, null, $config);
|
566 |
-
$publickey = openssl_pkey_get_details($rsa);
|
567 |
-
$publickey = $publickey['key'];
|
568 |
-
|
569 |
-
$privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, self::PRIVATE_FORMAT_PKCS1)));
|
570 |
-
$publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, self::PUBLIC_FORMAT_PKCS1)));
|
571 |
-
|
572 |
-
// clear the buffer of error strings stemming from a minimalistic openssl.cnf
|
573 |
-
while (openssl_error_string() !== false) {
|
574 |
-
}
|
575 |
-
|
576 |
-
return array(
|
577 |
-
'privatekey' => $privatekey,
|
578 |
-
'publickey' => $publickey,
|
579 |
-
'partialkey' => false
|
580 |
-
);
|
581 |
-
}
|
582 |
-
|
583 |
-
static $e;
|
584 |
-
if (!isset($e)) {
|
585 |
-
$e = new BigInteger(CRYPT_RSA_EXPONENT);
|
586 |
-
}
|
587 |
-
|
588 |
-
extract($this->_generateMinMax($bits));
|
589 |
-
$absoluteMin = $min;
|
590 |
-
$temp = $bits >> 1; // divide by two to see how many bits P and Q would be
|
591 |
-
if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
|
592 |
-
$num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
|
593 |
-
$temp = CRYPT_RSA_SMALLEST_PRIME;
|
594 |
-
} else {
|
595 |
-
$num_primes = 2;
|
596 |
-
}
|
597 |
-
extract($this->_generateMinMax($temp + $bits % $temp));
|
598 |
-
$finalMax = $max;
|
599 |
-
extract($this->_generateMinMax($temp));
|
600 |
-
|
601 |
-
$generator = new BigInteger();
|
602 |
-
|
603 |
-
$n = $this->one->copy();
|
604 |
-
if (!empty($partial)) {
|
605 |
-
extract(unserialize($partial));
|
606 |
-
} else {
|
607 |
-
$exponents = $coefficients = $primes = array();
|
608 |
-
$lcm = array(
|
609 |
-
'top' => $this->one->copy(),
|
610 |
-
'bottom' => false
|
611 |
-
);
|
612 |
-
}
|
613 |
-
|
614 |
-
$start = time();
|
615 |
-
$i0 = count($primes) + 1;
|
616 |
-
|
617 |
-
do {
|
618 |
-
for ($i = $i0; $i <= $num_primes; $i++) {
|
619 |
-
if ($timeout !== false) {
|
620 |
-
$timeout-= time() - $start;
|
621 |
-
$start = time();
|
622 |
-
if ($timeout <= 0) {
|
623 |
-
return array(
|
624 |
-
'privatekey' => '',
|
625 |
-
'publickey' => '',
|
626 |
-
'partialkey' => serialize(array(
|
627 |
-
'primes' => $primes,
|
628 |
-
'coefficients' => $coefficients,
|
629 |
-
'lcm' => $lcm,
|
630 |
-
'exponents' => $exponents
|
631 |
-
))
|
632 |
-
);
|
633 |
-
}
|
634 |
-
}
|
635 |
-
|
636 |
-
if ($i == $num_primes) {
|
637 |
-
list($min, $temp) = $absoluteMin->divide($n);
|
638 |
-
if (!$temp->equals($this->zero)) {
|
639 |
-
$min = $min->add($this->one); // ie. ceil()
|
640 |
-
}
|
641 |
-
$primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
|
642 |
-
} else {
|
643 |
-
$primes[$i] = $generator->randomPrime($min, $max, $timeout);
|
644 |
-
}
|
645 |
-
|
646 |
-
if ($primes[$i] === false) { // if we've reached the timeout
|
647 |
-
if (count($primes) > 1) {
|
648 |
-
$partialkey = '';
|
649 |
-
} else {
|
650 |
-
array_pop($primes);
|
651 |
-
$partialkey = serialize(array(
|
652 |
-
'primes' => $primes,
|
653 |
-
'coefficients' => $coefficients,
|
654 |
-
'lcm' => $lcm,
|
655 |
-
'exponents' => $exponents
|
656 |
-
));
|
657 |
-
}
|
658 |
-
|
659 |
-
return array(
|
660 |
-
'privatekey' => '',
|
661 |
-
'publickey' => '',
|
662 |
-
'partialkey' => $partialkey
|
663 |
-
);
|
664 |
-
}
|
665 |
-
|
666 |
-
// the first coefficient is calculated differently from the rest
|
667 |
-
// ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
|
668 |
-
if ($i > 2) {
|
669 |
-
$coefficients[$i] = $n->modInverse($primes[$i]);
|
670 |
-
}
|
671 |
-
|
672 |
-
$n = $n->multiply($primes[$i]);
|
673 |
-
|
674 |
-
$temp = $primes[$i]->subtract($this->one);
|
675 |
-
|
676 |
-
// textbook RSA implementations use Euler's totient function instead of the least common multiple.
|
677 |
-
// see http://en.wikipedia.org/wiki/Euler%27s_totient_function
|
678 |
-
$lcm['top'] = $lcm['top']->multiply($temp);
|
679 |
-
$lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
|
680 |
-
|
681 |
-
$exponents[$i] = $e->modInverse($temp);
|
682 |
-
}
|
683 |
-
|
684 |
-
list($temp) = $lcm['top']->divide($lcm['bottom']);
|
685 |
-
$gcd = $temp->gcd($e);
|
686 |
-
$i0 = 1;
|
687 |
-
} while (!$gcd->equals($this->one));
|
688 |
-
|
689 |
-
$d = $e->modInverse($temp);
|
690 |
-
|
691 |
-
$coefficients[2] = $primes[2]->modInverse($primes[1]);
|
692 |
-
|
693 |
-
// from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
|
694 |
-
// RSAPrivateKey ::= SEQUENCE {
|
695 |
-
// version Version,
|
696 |
-
// modulus INTEGER, -- n
|
697 |
-
// publicExponent INTEGER, -- e
|
698 |
-
// privateExponent INTEGER, -- d
|
699 |
-
// prime1 INTEGER, -- p
|
700 |
-
// prime2 INTEGER, -- q
|
701 |
-
// exponent1 INTEGER, -- d mod (p-1)
|
702 |
-
// exponent2 INTEGER, -- d mod (q-1)
|
703 |
-
// coefficient INTEGER, -- (inverse of q) mod p
|
704 |
-
// otherPrimeInfos OtherPrimeInfos OPTIONAL
|
705 |
-
// }
|
706 |
-
|
707 |
-
return array(
|
708 |
-
'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
|
709 |
-
'publickey' => $this->_convertPublicKey($n, $e),
|
710 |
-
'partialkey' => false
|
711 |
-
);
|
712 |
-
}
|
713 |
-
|
714 |
-
/**
|
715 |
-
* Convert a private key to the appropriate format.
|
716 |
-
*
|
717 |
-
* @access private
|
718 |
-
* @see self::setPrivateKeyFormat()
|
719 |
-
* @param string $RSAPrivateKey
|
720 |
-
* @return string
|
721 |
-
*/
|
722 |
-
function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
|
723 |
-
{
|
724 |
-
$signed = $this->privateKeyFormat != self::PRIVATE_FORMAT_XML;
|
725 |
-
$num_primes = count($primes);
|
726 |
-
$raw = array(
|
727 |
-
'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
|
728 |
-
'modulus' => $n->toBytes($signed),
|
729 |
-
'publicExponent' => $e->toBytes($signed),
|
730 |
-
'privateExponent' => $d->toBytes($signed),
|
731 |
-
'prime1' => $primes[1]->toBytes($signed),
|
732 |
-
'prime2' => $primes[2]->toBytes($signed),
|
733 |
-
'exponent1' => $exponents[1]->toBytes($signed),
|
734 |
-
'exponent2' => $exponents[2]->toBytes($signed),
|
735 |
-
'coefficient' => $coefficients[2]->toBytes($signed)
|
736 |
-
);
|
737 |
-
|
738 |
-
// if the format in question does not support multi-prime rsa and multi-prime rsa was used,
|
739 |
-
// call _convertPublicKey() instead.
|
740 |
-
switch ($this->privateKeyFormat) {
|
741 |
-
case self::PRIVATE_FORMAT_XML:
|
742 |
-
if ($num_primes != 2) {
|
743 |
-
return false;
|
744 |
-
}
|
745 |
-
return "<RSAKeyValue>\r\n" .
|
746 |
-
' <Modulus>' . base64_encode($raw['modulus']) . "</Modulus>\r\n" .
|
747 |
-
' <Exponent>' . base64_encode($raw['publicExponent']) . "</Exponent>\r\n" .
|
748 |
-
' <P>' . base64_encode($raw['prime1']) . "</P>\r\n" .
|
749 |
-
' <Q>' . base64_encode($raw['prime2']) . "</Q>\r\n" .
|
750 |
-
' <DP>' . base64_encode($raw['exponent1']) . "</DP>\r\n" .
|
751 |
-
' <DQ>' . base64_encode($raw['exponent2']) . "</DQ>\r\n" .
|
752 |
-
' <InverseQ>' . base64_encode($raw['coefficient']) . "</InverseQ>\r\n" .
|
753 |
-
' <D>' . base64_encode($raw['privateExponent']) . "</D>\r\n" .
|
754 |
-
'</RSAKeyValue>';
|
755 |
-
break;
|
756 |
-
case self::PRIVATE_FORMAT_PUTTY:
|
757 |
-
if ($num_primes != 2) {
|
758 |
-
return false;
|
759 |
-
}
|
760 |
-
$key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
|
761 |
-
$encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none';
|
762 |
-
$key.= $encryption;
|
763 |
-
$key.= "\r\nComment: " . $this->comment . "\r\n";
|
764 |
-
$public = pack(
|
765 |
-
'Na*Na*Na*',
|
766 |
-
strlen('ssh-rsa'),
|
767 |
-
'ssh-rsa',
|
768 |
-
strlen($raw['publicExponent']),
|
769 |
-
$raw['publicExponent'],
|
770 |
-
strlen($raw['modulus']),
|
771 |
-
$raw['modulus']
|
772 |
-
);
|
773 |
-
$source = pack(
|
774 |
-
'Na*Na*Na*Na*',
|
775 |
-
strlen('ssh-rsa'),
|
776 |
-
'ssh-rsa',
|
777 |
-
strlen($encryption),
|
778 |
-
$encryption,
|
779 |
-
strlen($this->comment),
|
780 |
-
$this->comment,
|
781 |
-
strlen($public),
|
782 |
-
$public
|
783 |
-
);
|
784 |
-
$public = base64_encode($public);
|
785 |
-
$key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n";
|
786 |
-
$key.= chunk_split($public, 64);
|
787 |
-
$private = pack(
|
788 |
-
'Na*Na*Na*Na*',
|
789 |
-
strlen($raw['privateExponent']),
|
790 |
-
$raw['privateExponent'],
|
791 |
-
strlen($raw['prime1']),
|
792 |
-
$raw['prime1'],
|
793 |
-
strlen($raw['prime2']),
|
794 |
-
$raw['prime2'],
|
795 |
-
strlen($raw['coefficient']),
|
796 |
-
$raw['coefficient']
|
797 |
-
);
|
798 |
-
if (empty($this->password) && !is_string($this->password)) {
|
799 |
-
$source.= pack('Na*', strlen($private), $private);
|
800 |
-
$hashkey = 'putty-private-key-file-mac-key';
|
801 |
-
} else {
|
802 |
-
$private.= Random::string(16 - (strlen($private) & 15));
|
803 |
-
$source.= pack('Na*', strlen($private), $private);
|
804 |
-
$sequence = 0;
|
805 |
-
$symkey = '';
|
806 |
-
while (strlen($symkey) < 32) {
|
807 |
-
$temp = pack('Na*', $sequence++, $this->password);
|
808 |
-
$symkey.= pack('H*', sha1($temp));
|
809 |
-
}
|
810 |
-
$symkey = substr($symkey, 0, 32);
|
811 |
-
$crypto = new AES();
|
812 |
-
|
813 |
-
$crypto->setKey($symkey);
|
814 |
-
$crypto->disablePadding();
|
815 |
-
$private = $crypto->encrypt($private);
|
816 |
-
$hashkey = 'putty-private-key-file-mac-key' . $this->password;
|
817 |
-
}
|
818 |
-
|
819 |
-
$private = base64_encode($private);
|
820 |
-
$key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n";
|
821 |
-
$key.= chunk_split($private, 64);
|
822 |
-
$hash = new Hash('sha1');
|
823 |
-
$hash->setKey(pack('H*', sha1($hashkey)));
|
824 |
-
$key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n";
|
825 |
-
|
826 |
-
return $key;
|
827 |
-
case self::PRIVATE_FORMAT_OPENSSH:
|
828 |
-
if ($num_primes != 2) {
|
829 |
-
return false;
|
830 |
-
}
|
831 |
-
$publicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']);
|
832 |
-
$privateKey = pack(
|
833 |
-
'Na*Na*Na*Na*Na*Na*Na*',
|
834 |
-
strlen('ssh-rsa'),
|
835 |
-
'ssh-rsa',
|
836 |
-
strlen($raw['modulus']),
|
837 |
-
$raw['modulus'],
|
838 |
-
strlen($raw['publicExponent']),
|
839 |
-
$raw['publicExponent'],
|
840 |
-
strlen($raw['privateExponent']),
|
841 |
-
$raw['privateExponent'],
|
842 |
-
strlen($raw['coefficient']),
|
843 |
-
$raw['coefficient'],
|
844 |
-
strlen($raw['prime1']),
|
845 |
-
$raw['prime1'],
|
846 |
-
strlen($raw['prime2']),
|
847 |
-
$raw['prime2']
|
848 |
-
);
|
849 |
-
$checkint = Random::string(4);
|
850 |
-
$paddedKey = pack(
|
851 |
-
'a*Na*',
|
852 |
-
$checkint . $checkint . $privateKey,
|
853 |
-
strlen($this->comment),
|
854 |
-
$this->comment
|
855 |
-
);
|
856 |
-
$paddingLength = (7 * strlen($paddedKey)) % 8;
|
857 |
-
for ($i = 1; $i <= $paddingLength; $i++) {
|
858 |
-
$paddedKey.= chr($i);
|
859 |
-
}
|
860 |
-
$key = pack(
|
861 |
-
'Na*Na*Na*NNa*Na*',
|
862 |
-
strlen('none'),
|
863 |
-
'none',
|
864 |
-
strlen('none'),
|
865 |
-
'none',
|
866 |
-
0,
|
867 |
-
'',
|
868 |
-
1,
|
869 |
-
strlen($publicKey),
|
870 |
-
$publicKey,
|
871 |
-
strlen($paddedKey),
|
872 |
-
$paddedKey
|
873 |
-
);
|
874 |
-
$key = "openssh-key-v1\0$key";
|
875 |
-
|
876 |
-
return "-----BEGIN OPENSSH PRIVATE KEY-----\r\n" .
|
877 |
-
chunk_split(base64_encode($key), 70) .
|
878 |
-
"-----END OPENSSH PRIVATE KEY-----";
|
879 |
-
default: // eg. self::PRIVATE_FORMAT_PKCS1
|
880 |
-
$components = array();
|
881 |
-
foreach ($raw as $name => $value) {
|
882 |
-
$components[$name] = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
|
883 |
-
}
|
884 |
-
|
885 |
-
$RSAPrivateKey = implode('', $components);
|
886 |
-
|
887 |
-
if ($num_primes > 2) {
|
888 |
-
$OtherPrimeInfos = '';
|
889 |
-
for ($i = 3; $i <= $num_primes; $i++) {
|
890 |
-
// OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
|
891 |
-
//
|
892 |
-
// OtherPrimeInfo ::= SEQUENCE {
|
893 |
-
// prime INTEGER, -- ri
|
894 |
-
// exponent INTEGER, -- di
|
895 |
-
// coefficient INTEGER -- ti
|
896 |
-
// }
|
897 |
-
$OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
|
898 |
-
$OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
|
899 |
-
$OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
|
900 |
-
$OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
|
901 |
-
}
|
902 |
-
$RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
|
903 |
-
}
|
904 |
-
|
905 |
-
$RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
|
906 |
-
|
907 |
-
if ($this->privateKeyFormat == self::PRIVATE_FORMAT_PKCS8) {
|
908 |
-
$rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
|
909 |
-
$RSAPrivateKey = pack(
|
910 |
-
'Ca*a*Ca*a*',
|
911 |
-
self::ASN1_INTEGER,
|
912 |
-
"\01\00",
|
913 |
-
$rsaOID,
|
914 |
-
4,
|
915 |
-
$this->_encodeLength(strlen($RSAPrivateKey)),
|
916 |
-
$RSAPrivateKey
|
917 |
-
);
|
918 |
-
$RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
|
919 |
-
if (!empty($this->password) || is_string($this->password)) {
|
920 |
-
$salt = Random::string(8);
|
921 |
-
$iterationCount = 2048;
|
922 |
-
|
923 |
-
$crypto = new DES();
|
924 |
-
$crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
|
925 |
-
$RSAPrivateKey = $crypto->encrypt($RSAPrivateKey);
|
926 |
-
|
927 |
-
$parameters = pack(
|
928 |
-
'Ca*a*Ca*N',
|
929 |
-
self::ASN1_OCTETSTRING,
|
930 |
-
$this->_encodeLength(strlen($salt)),
|
931 |
-
$salt,
|
932 |
-
self::ASN1_INTEGER,
|
933 |
-
$this->_encodeLength(4),
|
934 |
-
$iterationCount
|
935 |
-
);
|
936 |
-
$pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";
|
937 |
-
|
938 |
-
$encryptionAlgorithm = pack(
|
939 |
-
'Ca*a*Ca*a*',
|
940 |
-
self::ASN1_OBJECT,
|
941 |
-
$this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)),
|
942 |
-
$pbeWithMD5AndDES_CBC,
|
943 |
-
self::ASN1_SEQUENCE,
|
944 |
-
$this->_encodeLength(strlen($parameters)),
|
945 |
-
$parameters
|
946 |
-
);
|
947 |
-
|
948 |
-
$RSAPrivateKey = pack(
|
949 |
-
'Ca*a*Ca*a*',
|
950 |
-
self::ASN1_SEQUENCE,
|
951 |
-
$this->_encodeLength(strlen($encryptionAlgorithm)),
|
952 |
-
$encryptionAlgorithm,
|
953 |
-
self::ASN1_OCTETSTRING,
|
954 |
-
$this->_encodeLength(strlen($RSAPrivateKey)),
|
955 |
-
$RSAPrivateKey
|
956 |
-
);
|
957 |
-
|
958 |
-
$RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
|
959 |
-
|
960 |
-
$RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
|
961 |
-
chunk_split(base64_encode($RSAPrivateKey), 64) .
|
962 |
-
'-----END ENCRYPTED PRIVATE KEY-----';
|
963 |
-
} else {
|
964 |
-
$RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" .
|
965 |
-
chunk_split(base64_encode($RSAPrivateKey), 64) .
|
966 |
-
'-----END PRIVATE KEY-----';
|
967 |
-
}
|
968 |
-
return $RSAPrivateKey;
|
969 |
-
}
|
970 |
-
|
971 |
-
if (!empty($this->password) || is_string($this->password)) {
|
972 |
-
$iv = Random::string(8);
|
973 |
-
$symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
|
974 |
-
$symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
|
975 |
-
$des = new TripleDES();
|
976 |
-
$des->setKey($symkey);
|
977 |
-
$des->setIV($iv);
|
978 |
-
$iv = strtoupper(bin2hex($iv));
|
979 |
-
$RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
|
980 |
-
"Proc-Type: 4,ENCRYPTED\r\n" .
|
981 |
-
"DEK-Info: DES-EDE3-CBC,$iv\r\n" .
|
982 |
-
"\r\n" .
|
983 |
-
chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
|
984 |
-
'-----END RSA PRIVATE KEY-----';
|
985 |
-
} else {
|
986 |
-
$RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
|
987 |
-
chunk_split(base64_encode($RSAPrivateKey), 64) .
|
988 |
-
'-----END RSA PRIVATE KEY-----';
|
989 |
-
}
|
990 |
-
|
991 |
-
return $RSAPrivateKey;
|
992 |
-
}
|
993 |
-
}
|
994 |
-
|
995 |
-
/**
|
996 |
-
* Convert a public key to the appropriate format
|
997 |
-
*
|
998 |
-
* @access private
|
999 |
-
* @see self::setPublicKeyFormat()
|
1000 |
-
* @param string $RSAPrivateKey
|
1001 |
-
* @return string
|
1002 |
-
*/
|
1003 |
-
function _convertPublicKey($n, $e)
|
1004 |
-
{
|
1005 |
-
$signed = $this->publicKeyFormat != self::PUBLIC_FORMAT_XML;
|
1006 |
-
|
1007 |
-
$modulus = $n->toBytes($signed);
|
1008 |
-
$publicExponent = $e->toBytes($signed);
|
1009 |
-
|
1010 |
-
switch ($this->publicKeyFormat) {
|
1011 |
-
case self::PUBLIC_FORMAT_RAW:
|
1012 |
-
return array('e' => $e->copy(), 'n' => $n->copy());
|
1013 |
-
case self::PUBLIC_FORMAT_XML:
|
1014 |
-
return "<RSAKeyValue>\r\n" .
|
1015 |
-
' <Modulus>' . base64_encode($modulus) . "</Modulus>\r\n" .
|
1016 |
-
' <Exponent>' . base64_encode($publicExponent) . "</Exponent>\r\n" .
|
1017 |
-
'</RSAKeyValue>';
|
1018 |
-
break;
|
1019 |
-
case self::PUBLIC_FORMAT_OPENSSH:
|
1020 |
-
// from <http://tools.ietf.org/html/rfc4253#page-15>:
|
1021 |
-
// string "ssh-rsa"
|
1022 |
-
// mpint e
|
1023 |
-
// mpint n
|
1024 |
-
$RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
|
1025 |
-
$RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment;
|
1026 |
-
|
1027 |
-
return $RSAPublicKey;
|
1028 |
-
default: // eg. self::PUBLIC_FORMAT_PKCS1_RAW or self::PUBLIC_FORMAT_PKCS1
|
1029 |
-
// from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
|
1030 |
-
// RSAPublicKey ::= SEQUENCE {
|
1031 |
-
// modulus INTEGER, -- n
|
1032 |
-
// publicExponent INTEGER -- e
|
1033 |
-
// }
|
1034 |
-
$components = array(
|
1035 |
-
'modulus' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
|
1036 |
-
'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
|
1037 |
-
);
|
1038 |
-
|
1039 |
-
$RSAPublicKey = pack(
|
1040 |
-
'Ca*a*a*',
|
1041 |
-
self::ASN1_SEQUENCE,
|
1042 |
-
$this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
|
1043 |
-
$components['modulus'],
|
1044 |
-
$components['publicExponent']
|
1045 |
-
);
|
1046 |
-
|
1047 |
-
if ($this->publicKeyFormat == self::PUBLIC_FORMAT_PKCS1_RAW) {
|
1048 |
-
$RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" .
|
1049 |
-
chunk_split(base64_encode($RSAPublicKey), 64) .
|
1050 |
-
'-----END RSA PUBLIC KEY-----';
|
1051 |
-
} else {
|
1052 |
-
// sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
|
1053 |
-
$rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
|
1054 |
-
$RSAPublicKey = chr(0) . $RSAPublicKey;
|
1055 |
-
$RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
|
1056 |
-
|
1057 |
-
$RSAPublicKey = pack(
|
1058 |
-
'Ca*a*',
|
1059 |
-
self::ASN1_SEQUENCE,
|
1060 |
-
$this->_encodeLength(strlen($rsaOID . $RSAPublicKey)),
|
1061 |
-
$rsaOID . $RSAPublicKey
|
1062 |
-
);
|
1063 |
-
|
1064 |
-
$RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
|
1065 |
-
chunk_split(base64_encode($RSAPublicKey), 64) .
|
1066 |
-
'-----END PUBLIC KEY-----';
|
1067 |
-
}
|
1068 |
-
|
1069 |
-
return $RSAPublicKey;
|
1070 |
-
}
|
1071 |
-
}
|
1072 |
-
|
1073 |
-
/**
|
1074 |
-
* Break a public or private key down into its constituant components
|
1075 |
-
*
|
1076 |
-
* @access private
|
1077 |
-
* @see self::_convertPublicKey()
|
1078 |
-
* @see self::_convertPrivateKey()
|
1079 |
-
* @param string|array $key
|
1080 |
-
* @param int $type
|
1081 |
-
* @return array|bool
|
1082 |
-
*/
|
1083 |
-
function _parseKey($key, $type)
|
1084 |
-
{
|
1085 |
-
if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) {
|
1086 |
-
return false;
|
1087 |
-
}
|
1088 |
-
|
1089 |
-
switch ($type) {
|
1090 |
-
case self::PUBLIC_FORMAT_RAW:
|
1091 |
-
if (!is_array($key)) {
|
1092 |
-
return false;
|
1093 |
-
}
|
1094 |
-
$components = array();
|
1095 |
-
switch (true) {
|
1096 |
-
case isset($key['e']):
|
1097 |
-
$components['publicExponent'] = $key['e']->copy();
|
1098 |
-
break;
|
1099 |
-
case isset($key['exponent']):
|
1100 |
-
$components['publicExponent'] = $key['exponent']->copy();
|
1101 |
-
break;
|
1102 |
-
case isset($key['publicExponent']):
|
1103 |
-
$components['publicExponent'] = $key['publicExponent']->copy();
|
1104 |
-
break;
|
1105 |
-
case isset($key[0]):
|
1106 |
-
$components['publicExponent'] = $key[0]->copy();
|
1107 |
-
}
|
1108 |
-
switch (true) {
|
1109 |
-
case isset($key['n']):
|
1110 |
-
$components['modulus'] = $key['n']->copy();
|
1111 |
-
break;
|
1112 |
-
case isset($key['modulo']):
|
1113 |
-
$components['modulus'] = $key['modulo']->copy();
|
1114 |
-
break;
|
1115 |
-
case isset($key['modulus']):
|
1116 |
-
$components['modulus'] = $key['modulus']->copy();
|
1117 |
-
break;
|
1118 |
-
case isset($key[1]):
|
1119 |
-
$components['modulus'] = $key[1]->copy();
|
1120 |
-
}
|
1121 |
-
return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
|
1122 |
-
case self::PRIVATE_FORMAT_PKCS1:
|
1123 |
-
case self::PRIVATE_FORMAT_PKCS8:
|
1124 |
-
case self::PUBLIC_FORMAT_PKCS1:
|
1125 |
-
/* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
|
1126 |
-
"outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
|
1127 |
-
protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding
|
1128 |
-
two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here:
|
1129 |
-
|
1130 |
-
http://tools.ietf.org/html/rfc1421#section-4.6.1.1
|
1131 |
-
http://tools.ietf.org/html/rfc1421#section-4.6.1.3
|
1132 |
-
|
1133 |
-
DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
|
1134 |
-
DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
|
1135 |
-
function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
|
1136 |
-
own implementation. ie. the implementation *is* the standard and any bugs that may exist in that
|
1137 |
-
implementation are part of the standard, as well.
|
1138 |
-
|
1139 |
-
* OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */
|
1140 |
-
if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
|
1141 |
-
$iv = pack('H*', trim($matches[2]));
|
1142 |
-
$symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
|
1143 |
-
$symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8)));
|
1144 |
-
// remove the Proc-Type / DEK-Info sections as they're no longer needed
|
1145 |
-
$key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
|
1146 |
-
$ciphertext = $this->_extractBER($key);
|
1147 |
-
if ($ciphertext === false) {
|
1148 |
-
$ciphertext = $key;
|
1149 |
-
}
|
1150 |
-
switch ($matches[1]) {
|
1151 |
-
case 'AES-256-CBC':
|
1152 |
-
$crypto = new AES();
|
1153 |
-
break;
|
1154 |
-
case 'AES-128-CBC':
|
1155 |
-
$symkey = substr($symkey, 0, 16);
|
1156 |
-
$crypto = new AES();
|
1157 |
-
break;
|
1158 |
-
case 'DES-EDE3-CFB':
|
1159 |
-
$crypto = new TripleDES(Base::MODE_CFB);
|
1160 |
-
break;
|
1161 |
-
case 'DES-EDE3-CBC':
|
1162 |
-
$symkey = substr($symkey, 0, 24);
|
1163 |
-
$crypto = new TripleDES();
|
1164 |
-
break;
|
1165 |
-
case 'DES-CBC':
|
1166 |
-
$crypto = new DES();
|
1167 |
-
break;
|
1168 |
-
default:
|
1169 |
-
return false;
|
1170 |
-
}
|
1171 |
-
$crypto->setKey($symkey);
|
1172 |
-
$crypto->setIV($iv);
|
1173 |
-
$decoded = $crypto->decrypt($ciphertext);
|
1174 |
-
} else {
|
1175 |
-
$decoded = $this->_extractBER($key);
|
1176 |
-
}
|
1177 |
-
|
1178 |
-
if ($decoded !== false) {
|
1179 |
-
$key = $decoded;
|
1180 |
-
}
|
1181 |
-
|
1182 |
-
$components = array();
|
1183 |
-
|
1184 |
-
if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
|
1185 |
-
return false;
|
1186 |
-
}
|
1187 |
-
if ($this->_decodeLength($key) != strlen($key)) {
|
1188 |
-
return false;
|
1189 |
-
}
|
1190 |
-
|
1191 |
-
$tag = ord($this->_string_shift($key));
|
1192 |
-
/* intended for keys for which OpenSSL's asn1parse returns the following:
|
1193 |
-
|
1194 |
-
0:d=0 hl=4 l= 631 cons: SEQUENCE
|
1195 |
-
4:d=1 hl=2 l= 1 prim: INTEGER :00
|
1196 |
-
7:d=1 hl=2 l= 13 cons: SEQUENCE
|
1197 |
-
9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
|
1198 |
-
20:d=2 hl=2 l= 0 prim: NULL
|
1199 |
-
22:d=1 hl=4 l= 609 prim: OCTET STRING
|
1200 |
-
|
1201 |
-
ie. PKCS8 keys*/
|
1202 |
-
|
1203 |
-
if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
|
1204 |
-
$this->_string_shift($key, 3);
|
1205 |
-
$tag = self::ASN1_SEQUENCE;
|
1206 |
-
}
|
1207 |
-
|
1208 |
-
if ($tag == self::ASN1_SEQUENCE) {
|
1209 |
-
$temp = $this->_string_shift($key, $this->_decodeLength($key));
|
1210 |
-
if (ord($this->_string_shift($temp)) != self::ASN1_OBJECT) {
|
1211 |
-
return false;
|
1212 |
-
}
|
1213 |
-
$length = $this->_decodeLength($temp);
|
1214 |
-
switch ($this->_string_shift($temp, $length)) {
|
1215 |
-
case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
|
1216 |
-
break;
|
1217 |
-
case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
|
1218 |
-
/*
|
1219 |
-
PBEParameter ::= SEQUENCE {
|
1220 |
-
salt OCTET STRING (SIZE(8)),
|
1221 |
-
iterationCount INTEGER }
|
1222 |
-
*/
|
1223 |
-
if (ord($this->_string_shift($temp)) != self::ASN1_SEQUENCE) {
|
1224 |
-
return false;
|
1225 |
-
}
|
1226 |
-
if ($this->_decodeLength($temp) != strlen($temp)) {
|
1227 |
-
return false;
|
1228 |
-
}
|
1229 |
-
$this->_string_shift($temp); // assume it's an octet string
|
1230 |
-
$salt = $this->_string_shift($temp, $this->_decodeLength($temp));
|
1231 |
-
if (ord($this->_string_shift($temp)) != self::ASN1_INTEGER) {
|
1232 |
-
return false;
|
1233 |
-
}
|
1234 |
-
$this->_decodeLength($temp);
|
1235 |
-
list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
|
1236 |
-
$this->_string_shift($key); // assume it's an octet string
|
1237 |
-
$length = $this->_decodeLength($key);
|
1238 |
-
if (strlen($key) != $length) {
|
1239 |
-
return false;
|
1240 |
-
}
|
1241 |
-
|
1242 |
-
$crypto = new DES();
|
1243 |
-
$crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
|
1244 |
-
$key = $crypto->decrypt($key);
|
1245 |
-
if ($key === false) {
|
1246 |
-
return false;
|
1247 |
-
}
|
1248 |
-
return $this->_parseKey($key, self::PRIVATE_FORMAT_PKCS1);
|
1249 |
-
default:
|
1250 |
-
return false;
|
1251 |
-
}
|
1252 |
-
/* intended for keys for which OpenSSL's asn1parse returns the following:
|
1253 |
-
|
1254 |
-
0:d=0 hl=4 l= 290 cons: SEQUENCE
|
1255 |
-
4:d=1 hl=2 l= 13 cons: SEQUENCE
|
1256 |
-
6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
|
1257 |
-
17:d=2 hl=2 l= 0 prim: NULL
|
1258 |
-
19:d=1 hl=4 l= 271 prim: BIT STRING */
|
1259 |
-
$tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
|
1260 |
-
$this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
|
1261 |
-
// "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
|
1262 |
-
// unused bits in the final subsequent octet. The number shall be in the range zero to seven."
|
1263 |
-
// -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
|
1264 |
-
if ($tag == self::ASN1_BITSTRING) {
|
1265 |
-
$this->_string_shift($key);
|
1266 |
-
}
|
1267 |
-
if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
|
1268 |
-
return false;
|
1269 |
-
}
|
1270 |
-
if ($this->_decodeLength($key) != strlen($key)) {
|
1271 |
-
return false;
|
1272 |
-
}
|
1273 |
-
$tag = ord($this->_string_shift($key));
|
1274 |
-
}
|
1275 |
-
if ($tag != self::ASN1_INTEGER) {
|
1276 |
-
return false;
|
1277 |
-
}
|
1278 |
-
|
1279 |
-
$length = $this->_decodeLength($key);
|
1280 |
-
$temp = $this->_string_shift($key, $length);
|
1281 |
-
if (strlen($temp) != 1 || ord($temp) > 2) {
|
1282 |
-
$components['modulus'] = new BigInteger($temp, 256);
|
1283 |
-
$this->_string_shift($key); // skip over self::ASN1_INTEGER
|
1284 |
-
$length = $this->_decodeLength($key);
|
1285 |
-
$components[$type == self::PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
|
1286 |
-
|
1287 |
-
return $components;
|
1288 |
-
}
|
1289 |
-
if (ord($this->_string_shift($key)) != self::ASN1_INTEGER) {
|
1290 |
-
return false;
|
1291 |
-
}
|
1292 |
-
$length = $this->_decodeLength($key);
|
1293 |
-
$components['modulus'] = new BigInteger($this->_string_shift($key, $length), 256);
|
1294 |
-
$this->_string_shift($key);
|
1295 |
-
$length = $this->_decodeLength($key);
|
1296 |
-
$components['publicExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
|
1297 |
-
$this->_string_shift($key);
|
1298 |
-
$length = $this->_decodeLength($key);
|
1299 |
-
$components['privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
|
1300 |
-
$this->_string_shift($key);
|
1301 |
-
$length = $this->_decodeLength($key);
|
1302 |
-
$components['primes'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256));
|
1303 |
-
$this->_string_shift($key);
|
1304 |
-
$length = $this->_decodeLength($key);
|
1305 |
-
$components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256);
|
1306 |
-
$this->_string_shift($key);
|
1307 |
-
$length = $this->_decodeLength($key);
|
1308 |
-
$components['exponents'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256));
|
1309 |
-
$this->_string_shift($key);
|
1310 |
-
$length = $this->_decodeLength($key);
|
1311 |
-
$components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256);
|
1312 |
-
$this->_string_shift($key);
|
1313 |
-
$length = $this->_decodeLength($key);
|
1314 |
-
$components['coefficients'] = array(2 => new BigInteger($this->_string_shift($key, $length), 256));
|
1315 |
-
|
1316 |
-
if (!empty($key)) {
|
1317 |
-
if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
|
1318 |
-
return false;
|
1319 |
-
}
|
1320 |
-
$this->_decodeLength($key);
|
1321 |
-
while (!empty($key)) {
|
1322 |
-
if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
|
1323 |
-
return false;
|
1324 |
-
}
|
1325 |
-
$this->_decodeLength($key);
|
1326 |
-
$key = substr($key, 1);
|
1327 |
-
$length = $this->_decodeLength($key);
|
1328 |
-
$components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256);
|
1329 |
-
$this->_string_shift($key);
|
1330 |
-
$length = $this->_decodeLength($key);
|
1331 |
-
$components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256);
|
1332 |
-
$this->_string_shift($key);
|
1333 |
-
$length = $this->_decodeLength($key);
|
1334 |
-
$components['coefficients'][] = new BigInteger($this->_string_shift($key, $length), 256);
|
1335 |
-
}
|
1336 |
-
}
|
1337 |
-
|
1338 |
-
return $components;
|
1339 |
-
case self::PUBLIC_FORMAT_OPENSSH:
|
1340 |
-
$parts = explode(' ', $key, 3);
|
1341 |
-
|
1342 |
-
$key = isset($parts[1]) ? base64_decode($parts[1]) : false;
|
1343 |
-
if ($key === false) {
|
1344 |
-
return false;
|
1345 |
-
}
|
1346 |
-
|
1347 |
-
$comment = isset($parts[2]) ? $parts[2] : false;
|
1348 |
-
|
1349 |
-
$cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
|
1350 |
-
|
1351 |
-
if (strlen($key) <= 4) {
|
1352 |
-
return false;
|
1353 |
-
}
|
1354 |
-
extract(unpack('Nlength', $this->_string_shift($key, 4)));
|
1355 |
-
$publicExponent = new BigInteger($this->_string_shift($key, $length), -256);
|
1356 |
-
if (strlen($key) <= 4) {
|
1357 |
-
return false;
|
1358 |
-
}
|
1359 |
-
extract(unpack('Nlength', $this->_string_shift($key, 4)));
|
1360 |
-
$modulus = new BigInteger($this->_string_shift($key, $length), -256);
|
1361 |
-
|
1362 |
-
if ($cleanup && strlen($key)) {
|
1363 |
-
if (strlen($key) <= 4) {
|
1364 |
-
return false;
|
1365 |
-
}
|
1366 |
-
extract(unpack('Nlength', $this->_string_shift($key, 4)));
|
1367 |
-
$realModulus = new BigInteger($this->_string_shift($key, $length), -256);
|
1368 |
-
return strlen($key) ? false : array(
|
1369 |
-
'modulus' => $realModulus,
|
1370 |
-
'publicExponent' => $modulus,
|
1371 |
-
'comment' => $comment
|
1372 |
-
);
|
1373 |
-
} else {
|
1374 |
-
return strlen($key) ? false : array(
|
1375 |
-
'modulus' => $modulus,
|
1376 |
-
'publicExponent' => $publicExponent,
|
1377 |
-
'comment' => $comment
|
1378 |
-
);
|
1379 |
-
}
|
1380 |
-
// http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
|
1381 |
-
// http://en.wikipedia.org/wiki/XML_Signature
|
1382 |
-
case self::PRIVATE_FORMAT_XML:
|
1383 |
-
case self::PUBLIC_FORMAT_XML:
|
1384 |
-
$this->components = array();
|
1385 |
-
|
1386 |
-
$xml = xml_parser_create('UTF-8');
|
1387 |
-
xml_set_object($xml, $this);
|
1388 |
-
xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler');
|
1389 |
-
xml_set_character_data_handler($xml, '_data_handler');
|
1390 |
-
// add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
|
1391 |
-
if (!xml_parse($xml, '<xml>' . $key . '</xml>')) {
|
1392 |
-
xml_parser_free($xml);
|
1393 |
-
unset($xml);
|
1394 |
-
return false;
|
1395 |
-
}
|
1396 |
-
|
1397 |
-
xml_parser_free($xml);
|
1398 |
-
unset($xml);
|
1399 |
-
|
1400 |
-
return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
|
1401 |
-
// from PuTTY's SSHPUBK.C
|
1402 |
-
case self::PRIVATE_FORMAT_PUTTY:
|
1403 |
-
$components = array();
|
1404 |
-
$key = preg_split('#\r\n|\r|\n#', $key);
|
1405 |
-
$type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
|
1406 |
-
if ($type != 'ssh-rsa') {
|
1407 |
-
return false;
|
1408 |
-
}
|
1409 |
-
$encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
|
1410 |
-
$comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2]));
|
1411 |
-
|
1412 |
-
$publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
|
1413 |
-
$public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
|
1414 |
-
$public = substr($public, 11);
|
1415 |
-
extract(unpack('Nlength', $this->_string_shift($public, 4)));
|
1416 |
-
$components['publicExponent'] = new BigInteger($this->_string_shift($public, $length), -256);
|
1417 |
-
extract(unpack('Nlength', $this->_string_shift($public, 4)));
|
1418 |
-
$components['modulus'] = new BigInteger($this->_string_shift($public, $length), -256);
|
1419 |
-
|
1420 |
-
$privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4]));
|
1421 |
-
$private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength))));
|
1422 |
-
|
1423 |
-
switch ($encryption) {
|
1424 |
-
case 'aes256-cbc':
|
1425 |
-
$symkey = '';
|
1426 |
-
$sequence = 0;
|
1427 |
-
while (strlen($symkey) < 32) {
|
1428 |
-
$temp = pack('Na*', $sequence++, $this->password);
|
1429 |
-
$symkey.= pack('H*', sha1($temp));
|
1430 |
-
}
|
1431 |
-
$symkey = substr($symkey, 0, 32);
|
1432 |
-
$crypto = new AES();
|
1433 |
-
}
|
1434 |
-
|
1435 |
-
if ($encryption != 'none') {
|
1436 |
-
$crypto->setKey($symkey);
|
1437 |
-
$crypto->disablePadding();
|
1438 |
-
$private = $crypto->decrypt($private);
|
1439 |
-
if ($private === false) {
|
1440 |
-
return false;
|
1441 |
-
}
|
1442 |
-
}
|
1443 |
-
|
1444 |
-
extract(unpack('Nlength', $this->_string_shift($private, 4)));
|
1445 |
-
if (strlen($private) < $length) {
|
1446 |
-
return false;
|
1447 |
-
}
|
1448 |
-
$components['privateExponent'] = new BigInteger($this->_string_shift($private, $length), -256);
|
1449 |
-
extract(unpack('Nlength', $this->_string_shift($private, 4)));
|
1450 |
-
if (strlen($private) < $length) {
|
1451 |
-
return false;
|
1452 |
-
}
|
1453 |
-
$components['primes'] = array(1 => new BigInteger($this->_string_shift($private, $length), -256));
|
1454 |
-
extract(unpack('Nlength', $this->_string_shift($private, 4)));
|
1455 |
-
if (strlen($private) < $length) {
|
1456 |
-
return false;
|
1457 |
-
}
|
1458 |
-
$components['primes'][] = new BigInteger($this->_string_shift($private, $length), -256);
|
1459 |
-
|
1460 |
-
$temp = $components['primes'][1]->subtract($this->one);
|
1461 |
-
$components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
|
1462 |
-
$temp = $components['primes'][2]->subtract($this->one);
|
1463 |
-
$components['exponents'][] = $components['publicExponent']->modInverse($temp);
|
1464 |
-
|
1465 |
-
extract(unpack('Nlength', $this->_string_shift($private, 4)));
|
1466 |
-
if (strlen($private) < $length) {
|
1467 |
-
return false;
|
1468 |
-
}
|
1469 |
-
$components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256));
|
1470 |
-
|
1471 |
-
return $components;
|
1472 |
-
case self::PRIVATE_FORMAT_OPENSSH:
|
1473 |
-
$components = array();
|
1474 |
-
$decoded = $this->_extractBER($key);
|
1475 |
-
$magic = $this->_string_shift($decoded, 15);
|
1476 |
-
if ($magic !== "openssh-key-v1\0") {
|
1477 |
-
return false;
|
1478 |
-
}
|
1479 |
-
$options = $this->_string_shift($decoded, 24);
|
1480 |
-
// \0\0\0\4none = ciphername
|
1481 |
-
// \0\0\0\4none = kdfname
|
1482 |
-
// \0\0\0\0 = kdfoptions
|
1483 |
-
// \0\0\0\1 = numkeys
|
1484 |
-
if ($options != "\0\0\0\4none\0\0\0\4none\0\0\0\0\0\0\0\1") {
|
1485 |
-
return false;
|
1486 |
-
}
|
1487 |
-
extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
|
1488 |
-
if (strlen($decoded) < $length) {
|
1489 |
-
return false;
|
1490 |
-
}
|
1491 |
-
$publicKey = $this->_string_shift($decoded, $length);
|
1492 |
-
extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
|
1493 |
-
if (strlen($decoded) < $length) {
|
1494 |
-
return false;
|
1495 |
-
}
|
1496 |
-
$paddedKey = $this->_string_shift($decoded, $length);
|
1497 |
-
|
1498 |
-
if ($this->_string_shift($publicKey, 11) !== "\0\0\0\7ssh-rsa") {
|
1499 |
-
return false;
|
1500 |
-
}
|
1501 |
-
|
1502 |
-
$checkint1 = $this->_string_shift($paddedKey, 4);
|
1503 |
-
$checkint2 = $this->_string_shift($paddedKey, 4);
|
1504 |
-
if (strlen($checkint1) != 4 || $checkint1 !== $checkint2) {
|
1505 |
-
return false;
|
1506 |
-
}
|
1507 |
-
|
1508 |
-
if ($this->_string_shift($paddedKey, 11) !== "\0\0\0\7ssh-rsa") {
|
1509 |
-
return false;
|
1510 |
-
}
|
1511 |
-
|
1512 |
-
$values = array(
|
1513 |
-
&$components['modulus'],
|
1514 |
-
&$components['publicExponent'],
|
1515 |
-
&$components['privateExponent'],
|
1516 |
-
&$components['coefficients'][2],
|
1517 |
-
&$components['primes'][1],
|
1518 |
-
&$components['primes'][2]
|
1519 |
-
);
|
1520 |
-
|
1521 |
-
foreach ($values as &$value) {
|
1522 |
-
extract(unpack('Nlength', $this->_string_shift($paddedKey, 4)));
|
1523 |
-
if (strlen($paddedKey) < $length) {
|
1524 |
-
return false;
|
1525 |
-
}
|
1526 |
-
$value = new BigInteger($this->_string_shift($paddedKey, $length), -256);
|
1527 |
-
}
|
1528 |
-
|
1529 |
-
extract(unpack('Nlength', $this->_string_shift($paddedKey, 4)));
|
1530 |
-
if (strlen($paddedKey) < $length) {
|
1531 |
-
return false;
|
1532 |
-
}
|
1533 |
-
$components['comment'] = $this->_string_shift($decoded, $length);
|
1534 |
-
|
1535 |
-
$temp = $components['primes'][1]->subtract($this->one);
|
1536 |
-
$components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
|
1537 |
-
$temp = $components['primes'][2]->subtract($this->one);
|
1538 |
-
$components['exponents'][] = $components['publicExponent']->modInverse($temp);
|
1539 |
-
|
1540 |
-
return $components;
|
1541 |
-
}
|
1542 |
-
}
|
1543 |
-
|
1544 |
-
/**
|
1545 |
-
* Returns the key size
|
1546 |
-
*
|
1547 |
-
* More specifically, this returns the size of the modulo in bits.
|
1548 |
-
*
|
1549 |
-
* @access public
|
1550 |
-
* @return int
|
1551 |
-
*/
|
1552 |
-
function getSize()
|
1553 |
-
{
|
1554 |
-
return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits());
|
1555 |
-
}
|
1556 |
-
|
1557 |
-
/**
|
1558 |
-
* Start Element Handler
|
1559 |
-
*
|
1560 |
-
* Called by xml_set_element_handler()
|
1561 |
-
*
|
1562 |
-
* @access private
|
1563 |
-
* @param resource $parser
|
1564 |
-
* @param string $name
|
1565 |
-
* @param array $attribs
|
1566 |
-
*/
|
1567 |
-
function _start_element_handler($parser, $name, $attribs)
|
1568 |
-
{
|
1569 |
-
//$name = strtoupper($name);
|
1570 |
-
switch ($name) {
|
1571 |
-
case 'MODULUS':
|
1572 |
-
$this->current = &$this->components['modulus'];
|
1573 |
-
break;
|
1574 |
-
case 'EXPONENT':
|
1575 |
-
$this->current = &$this->components['publicExponent'];
|
1576 |
-
break;
|
1577 |
-
case 'P':
|
1578 |
-
$this->current = &$this->components['primes'][1];
|
1579 |
-
break;
|
1580 |
-
case 'Q':
|
1581 |
-
$this->current = &$this->components['primes'][2];
|
1582 |
-
break;
|
1583 |
-
case 'DP':
|
1584 |
-
$this->current = &$this->components['exponents'][1];
|
1585 |
-
break;
|
1586 |
-
case 'DQ':
|
1587 |
-
$this->current = &$this->components['exponents'][2];
|
1588 |
-
break;
|
1589 |
-
case 'INVERSEQ':
|
1590 |
-
$this->current = &$this->components['coefficients'][2];
|
1591 |
-
break;
|
1592 |
-
case 'D':
|
1593 |
-
$this->current = &$this->components['privateExponent'];
|
1594 |
-
}
|
1595 |
-
$this->current = '';
|
1596 |
-
}
|
1597 |
-
|
1598 |
-
/**
|
1599 |
-
* Stop Element Handler
|
1600 |
-
*
|
1601 |
-
* Called by xml_set_element_handler()
|
1602 |
-
*
|
1603 |
-
* @access private
|
1604 |
-
* @param resource $parser
|
1605 |
-
* @param string $name
|
1606 |
-
*/
|
1607 |
-
function _stop_element_handler($parser, $name)
|
1608 |
-
{
|
1609 |
-
if (isset($this->current)) {
|
1610 |
-
$this->current = new BigInteger(base64_decode($this->current), 256);
|
1611 |
-
unset($this->current);
|
1612 |
-
}
|
1613 |
-
}
|
1614 |
-
|
1615 |
-
/**
|
1616 |
-
* Data Handler
|
1617 |
-
*
|
1618 |
-
* Called by xml_set_character_data_handler()
|
1619 |
-
*
|
1620 |
-
* @access private
|
1621 |
-
* @param resource $parser
|
1622 |
-
* @param string $data
|
1623 |
-
*/
|
1624 |
-
function _data_handler($parser, $data)
|
1625 |
-
{
|
1626 |
-
if (!isset($this->current) || is_object($this->current)) {
|
1627 |
-
return;
|
1628 |
-
}
|
1629 |
-
$this->current.= trim($data);
|
1630 |
-
}
|
1631 |
-
|
1632 |
-
/**
|
1633 |
-
* Loads a public or private key
|
1634 |
-
*
|
1635 |
-
* Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
|
1636 |
-
*
|
1637 |
-
* @access public
|
1638 |
-
* @param string|RSA|array $key
|
1639 |
-
* @param bool|int $type optional
|
1640 |
-
* @return bool
|
1641 |
-
*/
|
1642 |
-
function loadKey($key, $type = false)
|
1643 |
-
{
|
1644 |
-
if ($key instanceof RSA) {
|
1645 |
-
$this->privateKeyFormat = $key->privateKeyFormat;
|
1646 |
-
$this->publicKeyFormat = $key->publicKeyFormat;
|
1647 |
-
$this->k = $key->k;
|
1648 |
-
$this->hLen = $key->hLen;
|
1649 |
-
$this->sLen = $key->sLen;
|
1650 |
-
$this->mgfHLen = $key->mgfHLen;
|
1651 |
-
$this->encryptionMode = $key->encryptionMode;
|
1652 |
-
$this->signatureMode = $key->signatureMode;
|
1653 |
-
$this->password = $key->password;
|
1654 |
-
$this->configFile = $key->configFile;
|
1655 |
-
$this->comment = $key->comment;
|
1656 |
-
|
1657 |
-
if (is_object($key->hash)) {
|
1658 |
-
$this->hash = new Hash($key->hash->getHash());
|
1659 |
-
}
|
1660 |
-
if (is_object($key->mgfHash)) {
|
1661 |
-
$this->mgfHash = new Hash($key->mgfHash->getHash());
|
1662 |
-
}
|
1663 |
-
|
1664 |
-
if (is_object($key->modulus)) {
|
1665 |
-
$this->modulus = $key->modulus->copy();
|
1666 |
-
}
|
1667 |
-
if (is_object($key->exponent)) {
|
1668 |
-
$this->exponent = $key->exponent->copy();
|
1669 |
-
}
|
1670 |
-
if (is_object($key->publicExponent)) {
|
1671 |
-
$this->publicExponent = $key->publicExponent->copy();
|
1672 |
-
}
|
1673 |
-
|
1674 |
-
$this->primes = array();
|
1675 |
-
$this->exponents = array();
|
1676 |
-
$this->coefficients = array();
|
1677 |
-
|
1678 |
-
foreach ($this->primes as $prime) {
|
1679 |
-
$this->primes[] = $prime->copy();
|
1680 |
-
}
|
1681 |
-
foreach ($this->exponents as $exponent) {
|
1682 |
-
$this->exponents[] = $exponent->copy();
|
1683 |
-
}
|
1684 |
-
foreach ($this->coefficients as $coefficient) {
|
1685 |
-
$this->coefficients[] = $coefficient->copy();
|
1686 |
-
}
|
1687 |
-
|
1688 |
-
return true;
|
1689 |
-
}
|
1690 |
-
|
1691 |
-
if ($type === false) {
|
1692 |
-
$types = array(
|
1693 |
-
self::PUBLIC_FORMAT_RAW,
|
1694 |
-
self::PRIVATE_FORMAT_PKCS1,
|
1695 |
-
self::PRIVATE_FORMAT_XML,
|
1696 |
-
self::PRIVATE_FORMAT_PUTTY,
|
1697 |
-
self::PUBLIC_FORMAT_OPENSSH,
|
1698 |
-
self::PRIVATE_FORMAT_OPENSSH
|
1699 |
-
);
|
1700 |
-
foreach ($types as $type) {
|
1701 |
-
$components = $this->_parseKey($key, $type);
|
1702 |
-
if ($components !== false) {
|
1703 |
-
break;
|
1704 |
-
}
|
1705 |
-
}
|
1706 |
-
} else {
|
1707 |
-
$components = $this->_parseKey($key, $type);
|
1708 |
-
}
|
1709 |
-
|
1710 |
-
if ($components === false) {
|
1711 |
-
$this->comment = null;
|
1712 |
-
$this->modulus = null;
|
1713 |
-
$this->k = null;
|
1714 |
-
$this->exponent = null;
|
1715 |
-
$this->primes = null;
|
1716 |
-
$this->exponents = null;
|
1717 |
-
$this->coefficients = null;
|
1718 |
-
$this->publicExponent = null;
|
1719 |
-
|
1720 |
-
return false;
|
1721 |
-
}
|
1722 |
-
|
1723 |
-
if (isset($components['comment']) && $components['comment'] !== false) {
|
1724 |
-
$this->comment = $components['comment'];
|
1725 |
-
}
|
1726 |
-
$this->modulus = $components['modulus'];
|
1727 |
-
$this->k = strlen($this->modulus->toBytes());
|
1728 |
-
$this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
|
1729 |
-
if (isset($components['primes'])) {
|
1730 |
-
$this->primes = $components['primes'];
|
1731 |
-
$this->exponents = $components['exponents'];
|
1732 |
-
$this->coefficients = $components['coefficients'];
|
1733 |
-
$this->publicExponent = $components['publicExponent'];
|
1734 |
-
} else {
|
1735 |
-
$this->primes = array();
|
1736 |
-
$this->exponents = array();
|
1737 |
-
$this->coefficients = array();
|
1738 |
-
$this->publicExponent = false;
|
1739 |
-
}
|
1740 |
-
|
1741 |
-
switch ($type) {
|
1742 |
-
case self::PUBLIC_FORMAT_OPENSSH:
|
1743 |
-
case self::PUBLIC_FORMAT_RAW:
|
1744 |
-
$this->setPublicKey();
|
1745 |
-
break;
|
1746 |
-
case self::PRIVATE_FORMAT_PKCS1:
|
1747 |
-
switch (true) {
|
1748 |
-
case strpos($key, '-BEGIN PUBLIC KEY-') !== false:
|
1749 |
-
case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false:
|
1750 |
-
$this->setPublicKey();
|
1751 |
-
}
|
1752 |
-
}
|
1753 |
-
|
1754 |
-
return true;
|
1755 |
-
}
|
1756 |
-
|
1757 |
-
/**
|
1758 |
-
* Sets the password
|
1759 |
-
*
|
1760 |
-
* Private keys can be encrypted with a password. To unset the password, pass in the empty string or false.
|
1761 |
-
* Or rather, pass in $password such that empty($password) && !is_string($password) is true.
|
1762 |
-
*
|
1763 |
-
* @see self::createKey()
|
1764 |
-
* @see self::loadKey()
|
1765 |
-
* @access public
|
1766 |
-
* @param string $password
|
1767 |
-
*/
|
1768 |
-
function setPassword($password = false)
|
1769 |
-
{
|
1770 |
-
$this->password = $password;
|
1771 |
-
}
|
1772 |
-
|
1773 |
-
/**
|
1774 |
-
* Defines the public key
|
1775 |
-
*
|
1776 |
-
* Some private key formats define the public exponent and some don't. Those that don't define it are problematic when
|
1777 |
-
* used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a
|
1778 |
-
* message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys
|
1779 |
-
* and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public
|
1780 |
-
* exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used
|
1781 |
-
* is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being
|
1782 |
-
* public.
|
1783 |
-
*
|
1784 |
-
* Do note that when a new key is loaded the index will be cleared.
|
1785 |
-
*
|
1786 |
-
* Returns true on success, false on failure
|
1787 |
-
*
|
1788 |
-
* @see self::getPublicKey()
|
1789 |
-
* @access public
|
1790 |
-
* @param string $key optional
|
1791 |
-
* @param int $type optional
|
1792 |
-
* @return bool
|
1793 |
-
*/
|
1794 |
-
function setPublicKey($key = false, $type = false)
|
1795 |
-
{
|
1796 |
-
// if a public key has already been loaded return false
|
1797 |
-
if (!empty($this->publicExponent)) {
|
1798 |
-
return false;
|
1799 |
-
}
|
1800 |
-
|
1801 |
-
if ($key === false && !empty($this->modulus)) {
|
1802 |
-
$this->publicExponent = $this->exponent;
|
1803 |
-
return true;
|
1804 |
-
}
|
1805 |
-
|
1806 |
-
if ($type === false) {
|
1807 |
-
$types = array(
|
1808 |
-
self::PUBLIC_FORMAT_RAW,
|
1809 |
-
self::PUBLIC_FORMAT_PKCS1,
|
1810 |
-
self::PUBLIC_FORMAT_XML,
|
1811 |
-
self::PUBLIC_FORMAT_OPENSSH
|
1812 |
-
);
|
1813 |
-
foreach ($types as $type) {
|
1814 |
-
$components = $this->_parseKey($key, $type);
|
1815 |
-
if ($components !== false) {
|
1816 |
-
break;
|
1817 |
-
}
|
1818 |
-
}
|
1819 |
-
} else {
|
1820 |
-
$components = $this->_parseKey($key, $type);
|
1821 |
-
}
|
1822 |
-
|
1823 |
-
if ($components === false) {
|
1824 |
-
return false;
|
1825 |
-
}
|
1826 |
-
|
1827 |
-
if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
|
1828 |
-
$this->modulus = $components['modulus'];
|
1829 |
-
$this->exponent = $this->publicExponent = $components['publicExponent'];
|
1830 |
-
return true;
|
1831 |
-
}
|
1832 |
-
|
1833 |
-
$this->publicExponent = $components['publicExponent'];
|
1834 |
-
|
1835 |
-
return true;
|
1836 |
-
}
|
1837 |
-
|
1838 |
-
/**
|
1839 |
-
* Defines the private key
|
1840 |
-
*
|
1841 |
-
* If phpseclib guessed a private key was a public key and loaded it as such it might be desirable to force
|
1842 |
-
* phpseclib to treat the key as a private key. This function will do that.
|
1843 |
-
*
|
1844 |
-
* Do note that when a new key is loaded the index will be cleared.
|
1845 |
-
*
|
1846 |
-
* Returns true on success, false on failure
|
1847 |
-
*
|
1848 |
-
* @see self::getPublicKey()
|
1849 |
-
* @access public
|
1850 |
-
* @param string $key optional
|
1851 |
-
* @param int $type optional
|
1852 |
-
* @return bool
|
1853 |
-
*/
|
1854 |
-
function setPrivateKey($key = false, $type = false)
|
1855 |
-
{
|
1856 |
-
if ($key === false && !empty($this->publicExponent)) {
|
1857 |
-
$this->publicExponent = false;
|
1858 |
-
return true;
|
1859 |
-
}
|
1860 |
-
|
1861 |
-
$rsa = new RSA();
|
1862 |
-
if (!$rsa->loadKey($key, $type)) {
|
1863 |
-
return false;
|
1864 |
-
}
|
1865 |
-
$rsa->publicExponent = false;
|
1866 |
-
|
1867 |
-
// don't overwrite the old key if the new key is invalid
|
1868 |
-
$this->loadKey($rsa);
|
1869 |
-
return true;
|
1870 |
-
}
|
1871 |
-
|
1872 |
-
/**
|
1873 |
-
* Returns the public key
|
1874 |
-
*
|
1875 |
-
* The public key is only returned under two circumstances - if the private key had the public key embedded within it
|
1876 |
-
* or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this
|
1877 |
-
* function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
|
1878 |
-
*
|
1879 |
-
* @see self::getPublicKey()
|
1880 |
-
* @access public
|
1881 |
-
* @param string $key
|
1882 |
-
* @param int $type optional
|
1883 |
-
*/
|
1884 |
-
function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8)
|
1885 |
-
{
|
1886 |
-
if (empty($this->modulus) || empty($this->publicExponent)) {
|
1887 |
-
return false;
|
1888 |
-
}
|
1889 |
-
|
1890 |
-
$oldFormat = $this->publicKeyFormat;
|
1891 |
-
$this->publicKeyFormat = $type;
|
1892 |
-
$temp = $this->_convertPublicKey($this->modulus, $this->publicExponent);
|
1893 |
-
$this->publicKeyFormat = $oldFormat;
|
1894 |
-
return $temp;
|
1895 |
-
}
|
1896 |
-
|
1897 |
-
/**
|
1898 |
-
* Returns the public key's fingerprint
|
1899 |
-
*
|
1900 |
-
* The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is
|
1901 |
-
* no public key currently loaded, false is returned.
|
1902 |
-
* Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716)
|
1903 |
-
*
|
1904 |
-
* @access public
|
1905 |
-
* @param string $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned
|
1906 |
-
* for invalid values.
|
1907 |
-
* @return mixed
|
1908 |
-
*/
|
1909 |
-
function getPublicKeyFingerprint($algorithm = 'md5')
|
1910 |
-
{
|
1911 |
-
if (empty($this->modulus) || empty($this->publicExponent)) {
|
1912 |
-
return false;
|
1913 |
-
}
|
1914 |
-
|
1915 |
-
$modulus = $this->modulus->toBytes(true);
|
1916 |
-
$publicExponent = $this->publicExponent->toBytes(true);
|
1917 |
-
|
1918 |
-
$RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
|
1919 |
-
|
1920 |
-
switch ($algorithm) {
|
1921 |
-
case 'sha256':
|
1922 |
-
$hash = new Hash('sha256');
|
1923 |
-
$base = base64_encode($hash->hash($RSAPublicKey));
|
1924 |
-
return substr($base, 0, strlen($base) - 1);
|
1925 |
-
case 'md5':
|
1926 |
-
return substr(chunk_split(md5($RSAPublicKey), 2, ':'), 0, -1);
|
1927 |
-
default:
|
1928 |
-
return false;
|
1929 |
-
}
|
1930 |
-
}
|
1931 |
-
|
1932 |
-
/**
|
1933 |
-
* Returns the private key
|
1934 |
-
*
|
1935 |
-
* The private key is only returned if the currently loaded key contains the constituent prime numbers.
|
1936 |
-
*
|
1937 |
-
* @see self::getPublicKey()
|
1938 |
-
* @access public
|
1939 |
-
* @param string $key
|
1940 |
-
* @param int $type optional
|
1941 |
-
* @return mixed
|
1942 |
-
*/
|
1943 |
-
function getPrivateKey($type = self::PUBLIC_FORMAT_PKCS1)
|
1944 |
-
{
|
1945 |
-
if (empty($this->primes)) {
|
1946 |
-
return false;
|
1947 |
-
}
|
1948 |
-
|
1949 |
-
$oldFormat = $this->privateKeyFormat;
|
1950 |
-
$this->privateKeyFormat = $type;
|
1951 |
-
$temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients);
|
1952 |
-
$this->privateKeyFormat = $oldFormat;
|
1953 |
-
return $temp;
|
1954 |
-
}
|
1955 |
-
|
1956 |
-
/**
|
1957 |
-
* Returns a minimalistic private key
|
1958 |
-
*
|
1959 |
-
* Returns the private key without the prime number constituants. Structurally identical to a public key that
|
1960 |
-
* hasn't been set as the public key
|
1961 |
-
*
|
1962 |
-
* @see self::getPrivateKey()
|
1963 |
-
* @access private
|
1964 |
-
* @param string $key
|
1965 |
-
* @param int $type optional
|
1966 |
-
*/
|
1967 |
-
function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8)
|
1968 |
-
{
|
1969 |
-
if (empty($this->modulus) || empty($this->exponent)) {
|
1970 |
-
return false;
|
1971 |
-
}
|
1972 |
-
|
1973 |
-
$oldFormat = $this->publicKeyFormat;
|
1974 |
-
$this->publicKeyFormat = $mode;
|
1975 |
-
$temp = $this->_convertPublicKey($this->modulus, $this->exponent);
|
1976 |
-
$this->publicKeyFormat = $oldFormat;
|
1977 |
-
return $temp;
|
1978 |
-
}
|
1979 |
-
|
1980 |
-
/**
|
1981 |
-
* __toString() magic method
|
1982 |
-
*
|
1983 |
-
* @access public
|
1984 |
-
* @return string
|
1985 |
-
*/
|
1986 |
-
function __toString()
|
1987 |
-
{
|
1988 |
-
$key = $this->getPrivateKey($this->privateKeyFormat);
|
1989 |
-
if ($key !== false) {
|
1990 |
-
return $key;
|
1991 |
-
}
|
1992 |
-
$key = $this->_getPrivatePublicKey($this->publicKeyFormat);
|
1993 |
-
return $key !== false ? $key : '';
|
1994 |
-
}
|
1995 |
-
|
1996 |
-
/**
|
1997 |
-
* __clone() magic method
|
1998 |
-
*
|
1999 |
-
* @access public
|
2000 |
-
* @return Crypt_RSA
|
2001 |
-
*/
|
2002 |
-
function __clone()
|
2003 |
-
{
|
2004 |
-
$key = new RSA();
|
2005 |
-
$key->loadKey($this);
|
2006 |
-
return $key;
|
2007 |
-
}
|
2008 |
-
|
2009 |
-
/**
|
2010 |
-
* Generates the smallest and largest numbers requiring $bits bits
|
2011 |
-
*
|
2012 |
-
* @access private
|
2013 |
-
* @param int $bits
|
2014 |
-
* @return array
|
2015 |
-
*/
|
2016 |
-
function _generateMinMax($bits)
|
2017 |
-
{
|
2018 |
-
$bytes = $bits >> 3;
|
2019 |
-
$min = str_repeat(chr(0), $bytes);
|
2020 |
-
$max = str_repeat(chr(0xFF), $bytes);
|
2021 |
-
$msb = $bits & 7;
|
2022 |
-
if ($msb) {
|
2023 |
-
$min = chr(1 << ($msb - 1)) . $min;
|
2024 |
-
$max = chr((1 << $msb) - 1) . $max;
|
2025 |
-
} else {
|
2026 |
-
$min[0] = chr(0x80);
|
2027 |
-
}
|
2028 |
-
|
2029 |
-
return array(
|
2030 |
-
'min' => new BigInteger($min, 256),
|
2031 |
-
'max' => new BigInteger($max, 256)
|
2032 |
-
);
|
2033 |
-
}
|
2034 |
-
|
2035 |
-
/**
|
2036 |
-
* DER-decode the length
|
2037 |
-
*
|
2038 |
-
* DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
|
2039 |
-
* {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
|
2040 |
-
*
|
2041 |
-
* @access private
|
2042 |
-
* @param string $string
|
2043 |
-
* @return int
|
2044 |
-
*/
|
2045 |
-
function _decodeLength(&$string)
|
2046 |
-
{
|
2047 |
-
$length = ord($this->_string_shift($string));
|
2048 |
-
if ($length & 0x80) { // definite length, long form
|
2049 |
-
$length&= 0x7F;
|
2050 |
-
$temp = $this->_string_shift($string, $length);
|
2051 |
-
list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
|
2052 |
-
}
|
2053 |
-
return $length;
|
2054 |
-
}
|
2055 |
-
|
2056 |
-
/**
|
2057 |
-
* DER-encode the length
|
2058 |
-
*
|
2059 |
-
* DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
|
2060 |
-
* {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
|
2061 |
-
*
|
2062 |
-
* @access private
|
2063 |
-
* @param int $length
|
2064 |
-
* @return string
|
2065 |
-
*/
|
2066 |
-
function _encodeLength($length)
|
2067 |
-
{
|
2068 |
-
if ($length <= 0x7F) {
|
2069 |
-
return chr($length);
|
2070 |
-
}
|
2071 |
-
|
2072 |
-
$temp = ltrim(pack('N', $length), chr(0));
|
2073 |
-
return pack('Ca*', 0x80 | strlen($temp), $temp);
|
2074 |
-
}
|
2075 |
-
|
2076 |
-
/**
|
2077 |
-
* String Shift
|
2078 |
-
*
|
2079 |
-
* Inspired by array_shift
|
2080 |
-
*
|
2081 |
-
* @param string $string
|
2082 |
-
* @param int $index
|
2083 |
-
* @return string
|
2084 |
-
* @access private
|
2085 |
-
*/
|
2086 |
-
function _string_shift(&$string, $index = 1)
|
2087 |
-
{
|
2088 |
-
$substr = substr($string, 0, $index);
|
2089 |
-
$string = substr($string, $index);
|
2090 |
-
return $substr;
|
2091 |
-
}
|
2092 |
-
|
2093 |
-
/**
|
2094 |
-
* Determines the private key format
|
2095 |
-
*
|
2096 |
-
* @see self::createKey()
|
2097 |
-
* @access public
|
2098 |
-
* @param int $format
|
2099 |
-
*/
|
2100 |
-
function setPrivateKeyFormat($format)
|
2101 |
-
{
|
2102 |
-
$this->privateKeyFormat = $format;
|
2103 |
-
}
|
2104 |
-
|
2105 |
-
/**
|
2106 |
-
* Determines the public key format
|
2107 |
-
*
|
2108 |
-
* @see self::createKey()
|
2109 |
-
* @access public
|
2110 |
-
* @param int $format
|
2111 |
-
*/
|
2112 |
-
function setPublicKeyFormat($format)
|
2113 |
-
{
|
2114 |
-
$this->publicKeyFormat = $format;
|
2115 |
-
}
|
2116 |
-
|
2117 |
-
/**
|
2118 |
-
* Determines which hashing function should be used
|
2119 |
-
*
|
2120 |
-
* Used with signature production / verification and (if the encryption mode is self::ENCRYPTION_OAEP) encryption and
|
2121 |
-
* decryption. If $hash isn't supported, sha1 is used.
|
2122 |
-
*
|
2123 |
-
* @access public
|
2124 |
-
* @param string $hash
|
2125 |
-
*/
|
2126 |
-
function setHash($hash)
|
2127 |
-
{
|
2128 |
-
// \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
|
2129 |
-
switch ($hash) {
|
2130 |
-
case 'md2':
|
2131 |
-
case 'md5':
|
2132 |
-
case 'sha1':
|
2133 |
-
case 'sha256':
|
2134 |
-
case 'sha384':
|
2135 |
-
case 'sha512':
|
2136 |
-
$this->hash = new Hash($hash);
|
2137 |
-
$this->hashName = $hash;
|
2138 |
-
break;
|
2139 |
-
default:
|
2140 |
-
$this->hash = new Hash('sha1');
|
2141 |
-
$this->hashName = 'sha1';
|
2142 |
-
}
|
2143 |
-
$this->hLen = $this->hash->getLength();
|
2144 |
-
}
|
2145 |
-
|
2146 |
-
/**
|
2147 |
-
* Determines which hashing function should be used for the mask generation function
|
2148 |
-
*
|
2149 |
-
* The mask generation function is used by self::ENCRYPTION_OAEP and self::SIGNATURE_PSS and although it's
|
2150 |
-
* best if Hash and MGFHash are set to the same thing this is not a requirement.
|
2151 |
-
*
|
2152 |
-
* @access public
|
2153 |
-
* @param string $hash
|
2154 |
-
*/
|
2155 |
-
function setMGFHash($hash)
|
2156 |
-
{
|
2157 |
-
// \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
|
2158 |
-
switch ($hash) {
|
2159 |
-
case 'md2':
|
2160 |
-
case 'md5':
|
2161 |
-
case 'sha1':
|
2162 |
-
case 'sha256':
|
2163 |
-
case 'sha384':
|
2164 |
-
case 'sha512':
|
2165 |
-
$this->mgfHash = new Hash($hash);
|
2166 |
-
break;
|
2167 |
-
default:
|
2168 |
-
$this->mgfHash = new Hash('sha1');
|
2169 |
-
}
|
2170 |
-
$this->mgfHLen = $this->mgfHash->getLength();
|
2171 |
-
}
|
2172 |
-
|
2173 |
-
/**
|
2174 |
-
* Determines the salt length
|
2175 |
-
*
|
2176 |
-
* To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
|
2177 |
-
*
|
2178 |
-
* Typical salt lengths in octets are hLen (the length of the output
|
2179 |
-
* of the hash function Hash) and 0.
|
2180 |
-
*
|
2181 |
-
* @access public
|
2182 |
-
* @param int $format
|
2183 |
-
*/
|
2184 |
-
function setSaltLength($sLen)
|
2185 |
-
{
|
2186 |
-
$this->sLen = $sLen;
|
2187 |
-
}
|
2188 |
-
|
2189 |
-
/**
|
2190 |
-
* Integer-to-Octet-String primitive
|
2191 |
-
*
|
2192 |
-
* See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
|
2193 |
-
*
|
2194 |
-
* @access private
|
2195 |
-
* @param \phpseclib\Math\BigInteger $x
|
2196 |
-
* @param int $xLen
|
2197 |
-
* @return string
|
2198 |
-
*/
|
2199 |
-
function _i2osp($x, $xLen)
|
2200 |
-
{
|
2201 |
-
$x = $x->toBytes();
|
2202 |
-
if (strlen($x) > $xLen) {
|
2203 |
-
user_error('Integer too large');
|
2204 |
-
return false;
|
2205 |
-
}
|
2206 |
-
return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
|
2207 |
-
}
|
2208 |
-
|
2209 |
-
/**
|
2210 |
-
* Octet-String-to-Integer primitive
|
2211 |
-
*
|
2212 |
-
* See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
|
2213 |
-
*
|
2214 |
-
* @access private
|
2215 |
-
* @param string $x
|
2216 |
-
* @return \phpseclib\Math\BigInteger
|
2217 |
-
*/
|
2218 |
-
function _os2ip($x)
|
2219 |
-
{
|
2220 |
-
return new BigInteger($x, 256);
|
2221 |
-
}
|
2222 |
-
|
2223 |
-
/**
|
2224 |
-
* Exponentiate with or without Chinese Remainder Theorem
|
2225 |
-
*
|
2226 |
-
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}.
|
2227 |
-
*
|
2228 |
-
* @access private
|
2229 |
-
* @param \phpseclib\Math\BigInteger $x
|
2230 |
-
* @return \phpseclib\Math\BigInteger
|
2231 |
-
*/
|
2232 |
-
function _exponentiate($x)
|
2233 |
-
{
|
2234 |
-
switch (true) {
|
2235 |
-
case empty($this->primes):
|
2236 |
-
case $this->primes[1]->equals($this->zero):
|
2237 |
-
case empty($this->coefficients):
|
2238 |
-
case $this->coefficients[2]->equals($this->zero):
|
2239 |
-
case empty($this->exponents):
|
2240 |
-
case $this->exponents[1]->equals($this->zero):
|
2241 |
-
return $x->modPow($this->exponent, $this->modulus);
|
2242 |
-
}
|
2243 |
-
|
2244 |
-
$num_primes = count($this->primes);
|
2245 |
-
|
2246 |
-
if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
|
2247 |
-
$m_i = array(
|
2248 |
-
1 => $x->modPow($this->exponents[1], $this->primes[1]),
|
2249 |
-
2 => $x->modPow($this->exponents[2], $this->primes[2])
|
2250 |
-
);
|
2251 |
-
$h = $m_i[1]->subtract($m_i[2]);
|
2252 |
-
$h = $h->multiply($this->coefficients[2]);
|
2253 |
-
list(, $h) = $h->divide($this->primes[1]);
|
2254 |
-
$m = $m_i[2]->add($h->multiply($this->primes[2]));
|
2255 |
-
|
2256 |
-
$r = $this->primes[1];
|
2257 |
-
for ($i = 3; $i <= $num_primes; $i++) {
|
2258 |
-
$m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
|
2259 |
-
|
2260 |
-
$r = $r->multiply($this->primes[$i - 1]);
|
2261 |
-
|
2262 |
-
$h = $m_i->subtract($m);
|
2263 |
-
$h = $h->multiply($this->coefficients[$i]);
|
2264 |
-
list(, $h) = $h->divide($this->primes[$i]);
|
2265 |
-
|
2266 |
-
$m = $m->add($r->multiply($h));
|
2267 |
-
}
|
2268 |
-
} else {
|
2269 |
-
$smallest = $this->primes[1];
|
2270 |
-
for ($i = 2; $i <= $num_primes; $i++) {
|
2271 |
-
if ($smallest->compare($this->primes[$i]) > 0) {
|
2272 |
-
$smallest = $this->primes[$i];
|
2273 |
-
}
|
2274 |
-
}
|
2275 |
-
|
2276 |
-
$one = new BigInteger(1);
|
2277 |
-
|
2278 |
-
$r = $one->random($one, $smallest->subtract($one));
|
2279 |
-
|
2280 |
-
$m_i = array(
|
2281 |
-
1 => $this->_blind($x, $r, 1),
|
2282 |
-
2 => $this->_blind($x, $r, 2)
|
2283 |
-
);
|
2284 |
-
$h = $m_i[1]->subtract($m_i[2]);
|
2285 |
-
$h = $h->multiply($this->coefficients[2]);
|
2286 |
-
list(, $h) = $h->divide($this->primes[1]);
|
2287 |
-
$m = $m_i[2]->add($h->multiply($this->primes[2]));
|
2288 |
-
|
2289 |
-
$r = $this->primes[1];
|
2290 |
-
for ($i = 3; $i <= $num_primes; $i++) {
|
2291 |
-
$m_i = $this->_blind($x, $r, $i);
|
2292 |
-
|
2293 |
-
$r = $r->multiply($this->primes[$i - 1]);
|
2294 |
-
|
2295 |
-
$h = $m_i->subtract($m);
|
2296 |
-
$h = $h->multiply($this->coefficients[$i]);
|
2297 |
-
list(, $h) = $h->divide($this->primes[$i]);
|
2298 |
-
|
2299 |
-
$m = $m->add($r->multiply($h));
|
2300 |
-
}
|
2301 |
-
}
|
2302 |
-
|
2303 |
-
return $m;
|
2304 |
-
}
|
2305 |
-
|
2306 |
-
/**
|
2307 |
-
* Performs RSA Blinding
|
2308 |
-
*
|
2309 |
-
* Protects against timing attacks by employing RSA Blinding.
|
2310 |
-
* Returns $x->modPow($this->exponents[$i], $this->primes[$i])
|
2311 |
-
*
|
2312 |
-
* @access private
|
2313 |
-
* @param \phpseclib\Math\BigInteger $x
|
2314 |
-
* @param \phpseclib\Math\BigInteger $r
|
2315 |
-
* @param int $i
|
2316 |
-
* @return \phpseclib\Math\BigInteger
|
2317 |
-
*/
|
2318 |
-
function _blind($x, $r, $i)
|
2319 |
-
{
|
2320 |
-
$x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
|
2321 |
-
$x = $x->modPow($this->exponents[$i], $this->primes[$i]);
|
2322 |
-
|
2323 |
-
$r = $r->modInverse($this->primes[$i]);
|
2324 |
-
$x = $x->multiply($r);
|
2325 |
-
list(, $x) = $x->divide($this->primes[$i]);
|
2326 |
-
|
2327 |
-
return $x;
|
2328 |
-
}
|
2329 |
-
|
2330 |
-
/**
|
2331 |
-
* Performs blinded RSA equality testing
|
2332 |
-
*
|
2333 |
-
* Protects against a particular type of timing attack described.
|
2334 |
-
*
|
2335 |
-
* See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)}
|
2336 |
-
*
|
2337 |
-
* Thanks for the heads up singpolyma!
|
2338 |
-
*
|
2339 |
-
* @access private
|
2340 |
-
* @param string $x
|
2341 |
-
* @param string $y
|
2342 |
-
* @return bool
|
2343 |
-
*/
|
2344 |
-
function _equals($x, $y)
|
2345 |
-
{
|
2346 |
-
if (function_exists('hash_equals')) {
|
2347 |
-
return hash_equals($x, $y);
|
2348 |
-
}
|
2349 |
-
|
2350 |
-
if (strlen($x) != strlen($y)) {
|
2351 |
-
return false;
|
2352 |
-
}
|
2353 |
-
|
2354 |
-
$result = "\0";
|
2355 |
-
$x^= $y;
|
2356 |
-
for ($i = 0; $i < strlen($x); $i++) {
|
2357 |
-
$result|= $x[$i];
|
2358 |
-
}
|
2359 |
-
|
2360 |
-
return $result === "\0";
|
2361 |
-
}
|
2362 |
-
|
2363 |
-
/**
|
2364 |
-
* RSAEP
|
2365 |
-
*
|
2366 |
-
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
|
2367 |
-
*
|
2368 |
-
* @access private
|
2369 |
-
* @param \phpseclib\Math\BigInteger $m
|
2370 |
-
* @return \phpseclib\Math\BigInteger
|
2371 |
-
*/
|
2372 |
-
function _rsaep($m)
|
2373 |
-
{
|
2374 |
-
if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
|
2375 |
-
user_error('Message representative out of range');
|
2376 |
-
return false;
|
2377 |
-
}
|
2378 |
-
return $this->_exponentiate($m);
|
2379 |
-
}
|
2380 |
-
|
2381 |
-
/**
|
2382 |
-
* RSADP
|
2383 |
-
*
|
2384 |
-
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
|
2385 |
-
*
|
2386 |
-
* @access private
|
2387 |
-
* @param \phpseclib\Math\BigInteger $c
|
2388 |
-
* @return \phpseclib\Math\BigInteger
|
2389 |
-
*/
|
2390 |
-
function _rsadp($c)
|
2391 |
-
{
|
2392 |
-
if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
|
2393 |
-
user_error('Ciphertext representative out of range');
|
2394 |
-
return false;
|
2395 |
-
}
|
2396 |
-
return $this->_exponentiate($c);
|
2397 |
-
}
|
2398 |
-
|
2399 |
-
/**
|
2400 |
-
* RSASP1
|
2401 |
-
*
|
2402 |
-
* See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
|
2403 |
-
*
|
2404 |
-
* @access private
|
2405 |
-
* @param \phpseclib\Math\BigInteger $m
|
2406 |
-
* @return \phpseclib\Math\BigInteger
|
2407 |
-
*/
|
2408 |
-
function _rsasp1($m)
|
2409 |
-
{
|
2410 |
-
if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
|
2411 |
-
user_error('Message representative out of range');
|
2412 |
-
return false;
|
2413 |
-
}
|
2414 |
-
return $this->_exponentiate($m);
|
2415 |
-
}
|
2416 |
-
|
2417 |
-
/**
|
2418 |
-
* RSAVP1
|
2419 |
-
*
|
2420 |
-
* See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
|
2421 |
-
*
|
2422 |
-
* @access private
|
2423 |
-
* @param \phpseclib\Math\BigInteger $s
|
2424 |
-
* @return \phpseclib\Math\BigInteger
|
2425 |
-
*/
|
2426 |
-
function _rsavp1($s)
|
2427 |
-
{
|
2428 |
-
if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
|
2429 |
-
user_error('Signature representative out of range');
|
2430 |
-
return false;
|
2431 |
-
}
|
2432 |
-
return $this->_exponentiate($s);
|
2433 |
-
}
|
2434 |
-
|
2435 |
-
/**
|
2436 |
-
* MGF1
|
2437 |
-
*
|
2438 |
-
* See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
|
2439 |
-
*
|
2440 |
-
* @access private
|
2441 |
-
* @param string $mgfSeed
|
2442 |
-
* @param int $mgfLen
|
2443 |
-
* @return string
|
2444 |
-
*/
|
2445 |
-
function _mgf1($mgfSeed, $maskLen)
|
2446 |
-
{
|
2447 |
-
// if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
|
2448 |
-
|
2449 |
-
$t = '';
|
2450 |
-
$count = ceil($maskLen / $this->mgfHLen);
|
2451 |
-
for ($i = 0; $i < $count; $i++) {
|
2452 |
-
$c = pack('N', $i);
|
2453 |
-
$t.= $this->mgfHash->hash($mgfSeed . $c);
|
2454 |
-
}
|
2455 |
-
|
2456 |
-
return substr($t, 0, $maskLen);
|
2457 |
-
}
|
2458 |
-
|
2459 |
-
/**
|
2460 |
-
* RSAES-OAEP-ENCRYPT
|
2461 |
-
*
|
2462 |
-
* See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
|
2463 |
-
* {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
|
2464 |
-
*
|
2465 |
-
* @access private
|
2466 |
-
* @param string $m
|
2467 |
-
* @param string $l
|
2468 |
-
* @return string
|
2469 |
-
*/
|
2470 |
-
function _rsaes_oaep_encrypt($m, $l = '')
|
2471 |
-
{
|
2472 |
-
$mLen = strlen($m);
|
2473 |
-
|
2474 |
-
// Length checking
|
2475 |
-
|
2476 |
-
// if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
|
2477 |
-
// be output.
|
2478 |
-
|
2479 |
-
if ($mLen > $this->k - 2 * $this->hLen - 2) {
|
2480 |
-
user_error('Message too long');
|
2481 |
-
return false;
|
2482 |
-
}
|
2483 |
-
|
2484 |
-
// EME-OAEP encoding
|
2485 |
-
|
2486 |
-
$lHash = $this->hash->hash($l);
|
2487 |
-
$ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
|
2488 |
-
$db = $lHash . $ps . chr(1) . $m;
|
2489 |
-
$seed = Random::string($this->hLen);
|
2490 |
-
$dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
|
2491 |
-
$maskedDB = $db ^ $dbMask;
|
2492 |
-
$seedMask = $this->_mgf1($maskedDB, $this->hLen);
|
2493 |
-
$maskedSeed = $seed ^ $seedMask;
|
2494 |
-
$em = chr(0) . $maskedSeed . $maskedDB;
|
2495 |
-
|
2496 |
-
// RSA encryption
|
2497 |
-
|
2498 |
-
$m = $this->_os2ip($em);
|
2499 |
-
$c = $this->_rsaep($m);
|
2500 |
-
$c = $this->_i2osp($c, $this->k);
|
2501 |
-
|
2502 |
-
// Output the ciphertext C
|
2503 |
-
|
2504 |
-
return $c;
|
2505 |
-
}
|
2506 |
-
|
2507 |
-
/**
|
2508 |
-
* RSAES-OAEP-DECRYPT
|
2509 |
-
*
|
2510 |
-
* See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error
|
2511 |
-
* messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
|
2512 |
-
*
|
2513 |
-
* Note. Care must be taken to ensure that an opponent cannot
|
2514 |
-
* distinguish the different error conditions in Step 3.g, whether by
|
2515 |
-
* error message or timing, or, more generally, learn partial
|
2516 |
-
* information about the encoded message EM. Otherwise an opponent may
|
2517 |
-
* be able to obtain useful information about the decryption of the
|
2518 |
-
* ciphertext C, leading to a chosen-ciphertext attack such as the one
|
2519 |
-
* observed by Manger [36].
|
2520 |
-
*
|
2521 |
-
* As for $l... to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
|
2522 |
-
*
|
2523 |
-
* Both the encryption and the decryption operations of RSAES-OAEP take
|
2524 |
-
* the value of a label L as input. In this version of PKCS #1, L is
|
2525 |
-
* the empty string; other uses of the label are outside the scope of
|
2526 |
-
* this document.
|
2527 |
-
*
|
2528 |
-
* @access private
|
2529 |
-
* @param string $c
|
2530 |
-
* @param string $l
|
2531 |
-
* @return string
|
2532 |
-
*/
|
2533 |
-
function _rsaes_oaep_decrypt($c, $l = '')
|
2534 |
-
{
|
2535 |
-
// Length checking
|
2536 |
-
|
2537 |
-
// if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
|
2538 |
-
// be output.
|
2539 |
-
|
2540 |
-
if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
|
2541 |
-
user_error('Decryption error');
|
2542 |
-
return false;
|
2543 |
-
}
|
2544 |
-
|
2545 |
-
// RSA decryption
|
2546 |
-
|
2547 |
-
$c = $this->_os2ip($c);
|
2548 |
-
$m = $this->_rsadp($c);
|
2549 |
-
if ($m === false) {
|
2550 |
-
user_error('Decryption error');
|
2551 |
-
return false;
|
2552 |
-
}
|
2553 |
-
$em = $this->_i2osp($m, $this->k);
|
2554 |
-
|
2555 |
-
// EME-OAEP decoding
|
2556 |
-
|
2557 |
-
$lHash = $this->hash->hash($l);
|
2558 |
-
$y = ord($em[0]);
|
2559 |
-
$maskedSeed = substr($em, 1, $this->hLen);
|
2560 |
-
$maskedDB = substr($em, $this->hLen + 1);
|
2561 |
-
$seedMask = $this->_mgf1($maskedDB, $this->hLen);
|
2562 |
-
$seed = $maskedSeed ^ $seedMask;
|
2563 |
-
$dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
|
2564 |
-
$db = $maskedDB ^ $dbMask;
|
2565 |
-
$lHash2 = substr($db, 0, $this->hLen);
|
2566 |
-
$m = substr($db, $this->hLen);
|
2567 |
-
$hashesMatch = $this->_equals($lHash, $lHash2);
|
2568 |
-
$leadingZeros = 1;
|
2569 |
-
$patternMatch = 0;
|
2570 |
-
$offset = 0;
|
2571 |
-
for ($i = 0; $i < strlen($m); $i++) {
|
2572 |
-
$patternMatch|= $leadingZeros & ($m[$i] === "\1");
|
2573 |
-
$leadingZeros&= $m[$i] === "\0";
|
2574 |
-
$offset+= $patternMatch ? 0 : 1;
|
2575 |
-
}
|
2576 |
-
|
2577 |
-
// we do & instead of && to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation
|
2578 |
-
// to protect against timing attacks
|
2579 |
-
if (!$hashesMatch & !$patternMatch) {
|
2580 |
-
user_error('Decryption error');
|
2581 |
-
return false;
|
2582 |
-
}
|
2583 |
-
|
2584 |
-
// Output the message M
|
2585 |
-
|
2586 |
-
return substr($m, $offset + 1);
|
2587 |
-
}
|
2588 |
-
|
2589 |
-
/**
|
2590 |
-
* Raw Encryption / Decryption
|
2591 |
-
*
|
2592 |
-
* Doesn't use padding and is not recommended.
|
2593 |
-
*
|
2594 |
-
* @access private
|
2595 |
-
* @param string $m
|
2596 |
-
* @return string
|
2597 |
-
*/
|
2598 |
-
function _raw_encrypt($m)
|
2599 |
-
{
|
2600 |
-
$temp = $this->_os2ip($m);
|
2601 |
-
$temp = $this->_rsaep($temp);
|
2602 |
-
return $this->_i2osp($temp, $this->k);
|
2603 |
-
}
|
2604 |
-
|
2605 |
-
/**
|
2606 |
-
* RSAES-PKCS1-V1_5-ENCRYPT
|
2607 |
-
*
|
2608 |
-
* See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
|
2609 |
-
*
|
2610 |
-
* @access private
|
2611 |
-
* @param string $m
|
2612 |
-
* @return string
|
2613 |
-
*/
|
2614 |
-
function _rsaes_pkcs1_v1_5_encrypt($m)
|
2615 |
-
{
|
2616 |
-
$mLen = strlen($m);
|
2617 |
-
|
2618 |
-
// Length checking
|
2619 |
-
|
2620 |
-
if ($mLen > $this->k - 11) {
|
2621 |
-
user_error('Message too long');
|
2622 |
-
return false;
|
2623 |
-
}
|
2624 |
-
|
2625 |
-
// EME-PKCS1-v1_5 encoding
|
2626 |
-
|
2627 |
-
$psLen = $this->k - $mLen - 3;
|
2628 |
-
$ps = '';
|
2629 |
-
while (strlen($ps) != $psLen) {
|
2630 |
-
$temp = Random::string($psLen - strlen($ps));
|
2631 |
-
$temp = str_replace("\x00", '', $temp);
|
2632 |
-
$ps.= $temp;
|
2633 |
-
}
|
2634 |
-
$type = 2;
|
2635 |
-
// see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done
|
2636 |
-
if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
|
2637 |
-
$type = 1;
|
2638 |
-
// "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF"
|
2639 |
-
$ps = str_repeat("\xFF", $psLen);
|
2640 |
-
}
|
2641 |
-
$em = chr(0) . chr($type) . $ps . chr(0) . $m;
|
2642 |
-
|
2643 |
-
// RSA encryption
|
2644 |
-
$m = $this->_os2ip($em);
|
2645 |
-
$c = $this->_rsaep($m);
|
2646 |
-
$c = $this->_i2osp($c, $this->k);
|
2647 |
-
|
2648 |
-
// Output the ciphertext C
|
2649 |
-
|
2650 |
-
return $c;
|
2651 |
-
}
|
2652 |
-
|
2653 |
-
/**
|
2654 |
-
* RSAES-PKCS1-V1_5-DECRYPT
|
2655 |
-
*
|
2656 |
-
* See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
|
2657 |
-
*
|
2658 |
-
* For compatibility purposes, this function departs slightly from the description given in RFC3447.
|
2659 |
-
* The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
|
2660 |
-
* private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
|
2661 |
-
* public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed
|
2662 |
-
* to be 2 regardless of which key is used. For compatibility purposes, we'll just check to make sure the
|
2663 |
-
* second byte is 2 or less. If it is, we'll accept the decrypted string as valid.
|
2664 |
-
*
|
2665 |
-
* As a consequence of this, a private key encrypted ciphertext produced with \phpseclib\Crypt\RSA may not decrypt
|
2666 |
-
* with a strictly PKCS#1 v1.5 compliant RSA implementation. Public key encrypted ciphertext's should but
|
2667 |
-
* not private key encrypted ciphertext's.
|
2668 |
-
*
|
2669 |
-
* @access private
|
2670 |
-
* @param string $c
|
2671 |
-
* @return string
|
2672 |
-
*/
|
2673 |
-
function _rsaes_pkcs1_v1_5_decrypt($c)
|
2674 |
-
{
|
2675 |
-
// Length checking
|
2676 |
-
|
2677 |
-
if (strlen($c) != $this->k) { // or if k < 11
|
2678 |
-
user_error('Decryption error');
|
2679 |
-
return false;
|
2680 |
-
}
|
2681 |
-
|
2682 |
-
// RSA decryption
|
2683 |
-
|
2684 |
-
$c = $this->_os2ip($c);
|
2685 |
-
$m = $this->_rsadp($c);
|
2686 |
-
|
2687 |
-
if ($m === false) {
|
2688 |
-
user_error('Decryption error');
|
2689 |
-
return false;
|
2690 |
-
}
|
2691 |
-
$em = $this->_i2osp($m, $this->k);
|
2692 |
-
|
2693 |
-
// EME-PKCS1-v1_5 decoding
|
2694 |
-
|
2695 |
-
if (ord($em[0]) != 0 || ord($em[1]) > 2) {
|
2696 |
-
user_error('Decryption error');
|
2697 |
-
return false;
|
2698 |
-
}
|
2699 |
-
|
2700 |
-
$ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
|
2701 |
-
$m = substr($em, strlen($ps) + 3);
|
2702 |
-
|
2703 |
-
if (strlen($ps) < 8) {
|
2704 |
-
user_error('Decryption error');
|
2705 |
-
return false;
|
2706 |
-
}
|
2707 |
-
|
2708 |
-
// Output M
|
2709 |
-
|
2710 |
-
return $m;
|
2711 |
-
}
|
2712 |
-
|
2713 |
-
/**
|
2714 |
-
* EMSA-PSS-ENCODE
|
2715 |
-
*
|
2716 |
-
* See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
|
2717 |
-
*
|
2718 |
-
* @access private
|
2719 |
-
* @param string $m
|
2720 |
-
* @param int $emBits
|
2721 |
-
*/
|
2722 |
-
function _emsa_pss_encode($m, $emBits)
|
2723 |
-
{
|
2724 |
-
// if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
|
2725 |
-
// be output.
|
2726 |
-
|
2727 |
-
$emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
|
2728 |
-
$sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
|
2729 |
-
|
2730 |
-
$mHash = $this->hash->hash($m);
|
2731 |
-
if ($emLen < $this->hLen + $sLen + 2) {
|
2732 |
-
user_error('Encoding error');
|
2733 |
-
return false;
|
2734 |
-
}
|
2735 |
-
|
2736 |
-
$salt = Random::string($sLen);
|
2737 |
-
$m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
|
2738 |
-
$h = $this->hash->hash($m2);
|
2739 |
-
$ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
|
2740 |
-
$db = $ps . chr(1) . $salt;
|
2741 |
-
$dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
|
2742 |
-
$maskedDB = $db ^ $dbMask;
|
2743 |
-
$maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
|
2744 |
-
$em = $maskedDB . $h . chr(0xBC);
|
2745 |
-
|
2746 |
-
return $em;
|
2747 |
-
}
|
2748 |
-
|
2749 |
-
/**
|
2750 |
-
* EMSA-PSS-VERIFY
|
2751 |
-
*
|
2752 |
-
* See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
|
2753 |
-
*
|
2754 |
-
* @access private
|
2755 |
-
* @param string $m
|
2756 |
-
* @param string $em
|
2757 |
-
* @param int $emBits
|
2758 |
-
* @return string
|
2759 |
-
*/
|
2760 |
-
function _emsa_pss_verify($m, $em, $emBits)
|
2761 |
-
{
|
2762 |
-
// if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
|
2763 |
-
// be output.
|
2764 |
-
|
2765 |
-
$emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
|
2766 |
-
$sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
|
2767 |
-
|
2768 |
-
$mHash = $this->hash->hash($m);
|
2769 |
-
if ($emLen < $this->hLen + $sLen + 2) {
|
2770 |
-
return false;
|
2771 |
-
}
|
2772 |
-
|
2773 |
-
if ($em[strlen($em) - 1] != chr(0xBC)) {
|
2774 |
-
return false;
|
2775 |
-
}
|
2776 |
-
|
2777 |
-
$maskedDB = substr($em, 0, -$this->hLen - 1);
|
2778 |
-
$h = substr($em, -$this->hLen - 1, $this->hLen);
|
2779 |
-
$temp = chr(0xFF << ($emBits & 7));
|
2780 |
-
if ((~$maskedDB[0] & $temp) != $temp) {
|
2781 |
-
return false;
|
2782 |
-
}
|
2783 |
-
$dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
|
2784 |
-
$db = $maskedDB ^ $dbMask;
|
2785 |
-
$db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
|
2786 |
-
$temp = $emLen - $this->hLen - $sLen - 2;
|
2787 |
-
if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
|
2788 |
-
return false;
|
2789 |
-
}
|
2790 |
-
$salt = substr($db, $temp + 1); // should be $sLen long
|
2791 |
-
$m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
|
2792 |
-
$h2 = $this->hash->hash($m2);
|
2793 |
-
return $this->_equals($h, $h2);
|
2794 |
-
}
|
2795 |
-
|
2796 |
-
/**
|
2797 |
-
* RSASSA-PSS-SIGN
|
2798 |
-
*
|
2799 |
-
* See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
|
2800 |
-
*
|
2801 |
-
* @access private
|
2802 |
-
* @param string $m
|
2803 |
-
* @return string
|
2804 |
-
*/
|
2805 |
-
function _rsassa_pss_sign($m)
|
2806 |
-
{
|
2807 |
-
// EMSA-PSS encoding
|
2808 |
-
|
2809 |
-
$em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
|
2810 |
-
|
2811 |
-
// RSA signature
|
2812 |
-
|
2813 |
-
$m = $this->_os2ip($em);
|
2814 |
-
$s = $this->_rsasp1($m);
|
2815 |
-
$s = $this->_i2osp($s, $this->k);
|
2816 |
-
|
2817 |
-
// Output the signature S
|
2818 |
-
|
2819 |
-
return $s;
|
2820 |
-
}
|
2821 |
-
|
2822 |
-
/**
|
2823 |
-
* RSASSA-PSS-VERIFY
|
2824 |
-
*
|
2825 |
-
* See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
|
2826 |
-
*
|
2827 |
-
* @access private
|
2828 |
-
* @param string $m
|
2829 |
-
* @param string $s
|
2830 |
-
* @return string
|
2831 |
-
*/
|
2832 |
-
function _rsassa_pss_verify($m, $s)
|
2833 |
-
{
|
2834 |
-
// Length checking
|
2835 |
-
|
2836 |
-
if (strlen($s) != $this->k) {
|
2837 |
-
user_error('Invalid signature');
|
2838 |
-
return false;
|
2839 |
-
}
|
2840 |
-
|
2841 |
-
// RSA verification
|
2842 |
-
|
2843 |
-
$modBits = 8 * $this->k;
|
2844 |
-
|
2845 |
-
$s2 = $this->_os2ip($s);
|
2846 |
-
$m2 = $this->_rsavp1($s2);
|
2847 |
-
if ($m2 === false) {
|
2848 |
-
user_error('Invalid signature');
|
2849 |
-
return false;
|
2850 |
-
}
|
2851 |
-
$em = $this->_i2osp($m2, $modBits >> 3);
|
2852 |
-
if ($em === false) {
|
2853 |
-
user_error('Invalid signature');
|
2854 |
-
return false;
|
2855 |
-
}
|
2856 |
-
|
2857 |
-
// EMSA-PSS verification
|
2858 |
-
|
2859 |
-
return $this->_emsa_pss_verify($m, $em, $modBits - 1);
|
2860 |
-
}
|
2861 |
-
|
2862 |
-
/**
|
2863 |
-
* EMSA-PKCS1-V1_5-ENCODE
|
2864 |
-
*
|
2865 |
-
* See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
|
2866 |
-
*
|
2867 |
-
* @access private
|
2868 |
-
* @param string $m
|
2869 |
-
* @param int $emLen
|
2870 |
-
* @return string
|
2871 |
-
*/
|
2872 |
-
function _emsa_pkcs1_v1_5_encode($m, $emLen)
|
2873 |
-
{
|
2874 |
-
$h = $this->hash->hash($m);
|
2875 |
-
if ($h === false) {
|
2876 |
-
return false;
|
2877 |
-
}
|
2878 |
-
|
2879 |
-
// see http://tools.ietf.org/html/rfc3447#page-43
|
2880 |
-
switch ($this->hashName) {
|
2881 |
-
case 'md2':
|
2882 |
-
$t = pack('H*', '3020300c06082a864886f70d020205000410');
|
2883 |
-
break;
|
2884 |
-
case 'md5':
|
2885 |
-
$t = pack('H*', '3020300c06082a864886f70d020505000410');
|
2886 |
-
break;
|
2887 |
-
case 'sha1':
|
2888 |
-
$t = pack('H*', '3021300906052b0e03021a05000414');
|
2889 |
-
break;
|
2890 |
-
case 'sha256':
|
2891 |
-
$t = pack('H*', '3031300d060960864801650304020105000420');
|
2892 |
-
break;
|
2893 |
-
case 'sha384':
|
2894 |
-
$t = pack('H*', '3041300d060960864801650304020205000430');
|
2895 |
-
break;
|
2896 |
-
case 'sha512':
|
2897 |
-
$t = pack('H*', '3051300d060960864801650304020305000440');
|
2898 |
-
}
|
2899 |
-
$t.= $h;
|
2900 |
-
$tLen = strlen($t);
|
2901 |
-
|
2902 |
-
if ($emLen < $tLen + 11) {
|
2903 |
-
user_error('Intended encoded message length too short');
|
2904 |
-
return false;
|
2905 |
-
}
|
2906 |
-
|
2907 |
-
$ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
|
2908 |
-
|
2909 |
-
$em = "\0\1$ps\0$t";
|
2910 |
-
|
2911 |
-
return $em;
|
2912 |
-
}
|
2913 |
-
|
2914 |
-
/**
|
2915 |
-
* RSASSA-PKCS1-V1_5-SIGN
|
2916 |
-
*
|
2917 |
-
* See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
|
2918 |
-
*
|
2919 |
-
* @access private
|
2920 |
-
* @param string $m
|
2921 |
-
* @return string
|
2922 |
-
*/
|
2923 |
-
function _rsassa_pkcs1_v1_5_sign($m)
|
2924 |
-
{
|
2925 |
-
// EMSA-PKCS1-v1_5 encoding
|
2926 |
-
|
2927 |
-
$em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
|
2928 |
-
if ($em === false) {
|
2929 |
-
user_error('RSA modulus too short');
|
2930 |
-
return false;
|
2931 |
-
}
|
2932 |
-
|
2933 |
-
// RSA signature
|
2934 |
-
|
2935 |
-
$m = $this->_os2ip($em);
|
2936 |
-
$s = $this->_rsasp1($m);
|
2937 |
-
$s = $this->_i2osp($s, $this->k);
|
2938 |
-
|
2939 |
-
// Output the signature S
|
2940 |
-
|
2941 |
-
return $s;
|
2942 |
-
}
|
2943 |
-
|
2944 |
-
/**
|
2945 |
-
* RSASSA-PKCS1-V1_5-VERIFY
|
2946 |
-
*
|
2947 |
-
* See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
|
2948 |
-
*
|
2949 |
-
* @access private
|
2950 |
-
* @param string $m
|
2951 |
-
* @return string
|
2952 |
-
*/
|
2953 |
-
function _rsassa_pkcs1_v1_5_verify($m, $s)
|
2954 |
-
{
|
2955 |
-
// Length checking
|
2956 |
-
|
2957 |
-
if (strlen($s) != $this->k) {
|
2958 |
-
user_error('Invalid signature');
|
2959 |
-
return false;
|
2960 |
-
}
|
2961 |
-
|
2962 |
-
// RSA verification
|
2963 |
-
|
2964 |
-
$s = $this->_os2ip($s);
|
2965 |
-
$m2 = $this->_rsavp1($s);
|
2966 |
-
if ($m2 === false) {
|
2967 |
-
user_error('Invalid signature');
|
2968 |
-
return false;
|
2969 |
-
}
|
2970 |
-
$em = $this->_i2osp($m2, $this->k);
|
2971 |
-
if ($em === false) {
|
2972 |
-
user_error('Invalid signature');
|
2973 |
-
return false;
|
2974 |
-
}
|
2975 |
-
|
2976 |
-
// EMSA-PKCS1-v1_5 encoding
|
2977 |
-
|
2978 |
-
$em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
|
2979 |
-
if ($em2 === false) {
|
2980 |
-
user_error('RSA modulus too short');
|
2981 |
-
return false;
|
2982 |
-
}
|
2983 |
-
|
2984 |
-
// Compare
|
2985 |
-
return $this->_equals($em, $em2);
|
2986 |
-
}
|
2987 |
-
|
2988 |
-
/**
|
2989 |
-
* Set Encryption Mode
|
2990 |
-
*
|
2991 |
-
* Valid values include self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1.
|
2992 |
-
*
|
2993 |
-
* @access public
|
2994 |
-
* @param int $mode
|
2995 |
-
*/
|
2996 |
-
function setEncryptionMode($mode)
|
2997 |
-
{
|
2998 |
-
$this->encryptionMode = $mode;
|
2999 |
-
}
|
3000 |
-
|
3001 |
-
/**
|
3002 |
-
* Set Signature Mode
|
3003 |
-
*
|
3004 |
-
* Valid values include self::SIGNATURE_PSS and self::SIGNATURE_PKCS1
|
3005 |
-
*
|
3006 |
-
* @access public
|
3007 |
-
* @param int $mode
|
3008 |
-
*/
|
3009 |
-
function setSignatureMode($mode)
|
3010 |
-
{
|
3011 |
-
$this->signatureMode = $mode;
|
3012 |
-
}
|
3013 |
-
|
3014 |
-
/**
|
3015 |
-
* Set public key comment.
|
3016 |
-
*
|
3017 |
-
* @access public
|
3018 |
-
* @param string $comment
|
3019 |
-
*/
|
3020 |
-
function setComment($comment)
|
3021 |
-
{
|
3022 |
-
$this->comment = $comment;
|
3023 |
-
}
|
3024 |
-
|
3025 |
-
/**
|
3026 |
-
* Get public key comment.
|
3027 |
-
*
|
3028 |
-
* @access public
|
3029 |
-
* @return string
|
3030 |
-
*/
|
3031 |
-
function getComment()
|
3032 |
-
{
|
3033 |
-
return $this->comment;
|
3034 |
-
}
|
3035 |
-
|
3036 |
-
/**
|
3037 |
-
* Encryption
|
3038 |
-
*
|
3039 |
-
* Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
|
3040 |
-
* If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
|
3041 |
-
* be concatenated together.
|
3042 |
-
*
|
3043 |
-
* @see self::decrypt()
|
3044 |
-
* @access public
|
3045 |
-
* @param string $plaintext
|
3046 |
-
* @return string
|
3047 |
-
*/
|
3048 |
-
function encrypt($plaintext)
|
3049 |
-
{
|
3050 |
-
switch ($this->encryptionMode) {
|
3051 |
-
case self::ENCRYPTION_NONE:
|
3052 |
-
$plaintext = str_split($plaintext, $this->k);
|
3053 |
-
$ciphertext = '';
|
3054 |
-
foreach ($plaintext as $m) {
|
3055 |
-
$ciphertext.= $this->_raw_encrypt($m);
|
3056 |
-
}
|
3057 |
-
return $ciphertext;
|
3058 |
-
case self::ENCRYPTION_PKCS1:
|
3059 |
-
$length = $this->k - 11;
|
3060 |
-
if ($length <= 0) {
|
3061 |
-
return false;
|
3062 |
-
}
|
3063 |
-
|
3064 |
-
$plaintext = str_split($plaintext, $length);
|
3065 |
-
$ciphertext = '';
|
3066 |
-
foreach ($plaintext as $m) {
|
3067 |
-
$ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
|
3068 |
-
}
|
3069 |
-
return $ciphertext;
|
3070 |
-
//case self::ENCRYPTION_OAEP:
|
3071 |
-
default:
|
3072 |
-
$length = $this->k - 2 * $this->hLen - 2;
|
3073 |
-
if ($length <= 0) {
|
3074 |
-
return false;
|
3075 |
-
}
|
3076 |
-
|
3077 |
-
$plaintext = str_split($plaintext, $length);
|
3078 |
-
$ciphertext = '';
|
3079 |
-
foreach ($plaintext as $m) {
|
3080 |
-
$ciphertext.= $this->_rsaes_oaep_encrypt($m);
|
3081 |
-
}
|
3082 |
-
return $ciphertext;
|
3083 |
-
}
|
3084 |
-
}
|
3085 |
-
|
3086 |
-
/**
|
3087 |
-
* Decryption
|
3088 |
-
*
|
3089 |
-
* @see self::encrypt()
|
3090 |
-
* @access public
|
3091 |
-
* @param string $plaintext
|
3092 |
-
* @return string
|
3093 |
-
*/
|
3094 |
-
function decrypt($ciphertext)
|
3095 |
-
{
|
3096 |
-
if ($this->k <= 0) {
|
3097 |
-
return false;
|
3098 |
-
}
|
3099 |
-
|
3100 |
-
$ciphertext = str_split($ciphertext, $this->k);
|
3101 |
-
$ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);
|
3102 |
-
|
3103 |
-
$plaintext = '';
|
3104 |
-
|
3105 |
-
switch ($this->encryptionMode) {
|
3106 |
-
case self::ENCRYPTION_NONE:
|
3107 |
-
$decrypt = '_raw_encrypt';
|
3108 |
-
break;
|
3109 |
-
case self::ENCRYPTION_PKCS1:
|
3110 |
-
$decrypt = '_rsaes_pkcs1_v1_5_decrypt';
|
3111 |
-
break;
|
3112 |
-
//case self::ENCRYPTION_OAEP:
|
3113 |
-
default:
|
3114 |
-
$decrypt = '_rsaes_oaep_decrypt';
|
3115 |
-
}
|
3116 |
-
|
3117 |
-
foreach ($ciphertext as $c) {
|
3118 |
-
$temp = $this->$decrypt($c);
|
3119 |
-
if ($temp === false) {
|
3120 |
-
return false;
|
3121 |
-
}
|
3122 |
-
$plaintext.= $temp;
|
3123 |
-
}
|
3124 |
-
|
3125 |
-
return $plaintext;
|
3126 |
-
}
|
3127 |
-
|
3128 |
-
/**
|
3129 |
-
* Create a signature
|
3130 |
-
*
|
3131 |
-
* @see self::verify()
|
3132 |
-
* @access public
|
3133 |
-
* @param string $message
|
3134 |
-
* @return string
|
3135 |
-
*/
|
3136 |
-
function sign($message)
|
3137 |
-
{
|
3138 |
-
if (empty($this->modulus) || empty($this->exponent)) {
|
3139 |
-
return false;
|
3140 |
-
}
|
3141 |
-
|
3142 |
-
switch ($this->signatureMode) {
|
3143 |
-
case self::SIGNATURE_PKCS1:
|
3144 |
-
return $this->_rsassa_pkcs1_v1_5_sign($message);
|
3145 |
-
//case self::SIGNATURE_PSS:
|
3146 |
-
default:
|
3147 |
-
return $this->_rsassa_pss_sign($message);
|
3148 |
-
}
|
3149 |
-
}
|
3150 |
-
|
3151 |
-
/**
|
3152 |
-
* Verifies a signature
|
3153 |
-
*
|
3154 |
-
* @see self::sign()
|
3155 |
-
* @access public
|
3156 |
-
* @param string $message
|
3157 |
-
* @param string $signature
|
3158 |
-
* @return bool
|
3159 |
-
*/
|
3160 |
-
function verify($message, $signature)
|
3161 |
-
{
|
3162 |
-
if (empty($this->modulus) || empty($this->exponent)) {
|
3163 |
-
return false;
|
3164 |
-
}
|
3165 |
-
|
3166 |
-
switch ($this->signatureMode) {
|
3167 |
-
case self::SIGNATURE_PKCS1:
|
3168 |
-
return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
|
3169 |
-
//case self::SIGNATURE_PSS:
|
3170 |
-
default:
|
3171 |
-
return $this->_rsassa_pss_verify($message, $signature);
|
3172 |
-
}
|
3173 |
-
}
|
3174 |
-
|
3175 |
-
/**
|
3176 |
-
* Extract raw BER from Base64 encoding
|
3177 |
-
*
|
3178 |
-
* @access private
|
3179 |
-
* @param string $str
|
3180 |
-
* @return string
|
3181 |
-
*/
|
3182 |
-
function _extractBER($str)
|
3183 |
-
{
|
3184 |
-
/* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
|
3185 |
-
* above and beyond the ceritificate.
|
3186 |
-
* ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
|
3187 |
-
*
|
3188 |
-
* Bag Attributes
|
3189 |
-
* localKeyID: 01 00 00 00
|
3190 |
-
* subject=/O=organization/OU=org unit/CN=common name
|
3191 |
-
* issuer=/O=organization/CN=common name
|
3192 |
-
*/
|
3193 |
-
$temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
|
3194 |
-
// remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
|
3195 |
-
$temp = preg_replace('#-+[^-]+-+#', '', $temp);
|
3196 |
-
// remove new lines
|
3197 |
-
$temp = str_replace(array("\r", "\n", ' '), '', $temp);
|
3198 |
-
$temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
|
3199 |
-
return $temp != false ? $temp : $str;
|
3200 |
-
}
|
3201 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
|
5 |
+
*
|
6 |
+
* PHP version 5
|
7 |
+
*
|
8 |
+
* Here's an example of how to encrypt and decrypt text with this library:
|
9 |
+
* <code>
|
10 |
+
* <?php
|
11 |
+
* include 'vendor/autoload.php';
|
12 |
+
*
|
13 |
+
* $rsa = new \phpseclib\Crypt\RSA();
|
14 |
+
* extract($rsa->createKey());
|
15 |
+
*
|
16 |
+
* $plaintext = 'terrafrost';
|
17 |
+
*
|
18 |
+
* $rsa->loadKey($privatekey);
|
19 |
+
* $ciphertext = $rsa->encrypt($plaintext);
|
20 |
+
*
|
21 |
+
* $rsa->loadKey($publickey);
|
22 |
+
* echo $rsa->decrypt($ciphertext);
|
23 |
+
* ?>
|
24 |
+
* </code>
|
25 |
+
*
|
26 |
+
* Here's an example of how to create signatures and verify signatures with this library:
|
27 |
+
* <code>
|
28 |
+
* <?php
|
29 |
+
* include 'vendor/autoload.php';
|
30 |
+
*
|
31 |
+
* $rsa = new \phpseclib\Crypt\RSA();
|
32 |
+
* extract($rsa->createKey());
|
33 |
+
*
|
34 |
+
* $plaintext = 'terrafrost';
|
35 |
+
*
|
36 |
+
* $rsa->loadKey($privatekey);
|
37 |
+
* $signature = $rsa->sign($plaintext);
|
38 |
+
*
|
39 |
+
* $rsa->loadKey($publickey);
|
40 |
+
* echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
|
41 |
+
* ?>
|
42 |
+
* </code>
|
43 |
+
*
|
44 |
+
* @category Crypt
|
45 |
+
* @package RSA
|
46 |
+
* @author Jim Wigginton <terrafrost@php.net>
|
47 |
+
* @copyright 2009 Jim Wigginton
|
48 |
+
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
49 |
+
* @link http://phpseclib.sourceforge.net
|
50 |
+
*/
|
51 |
+
|
52 |
+
namespace phpseclib\Crypt;
|
53 |
+
|
54 |
+
use phpseclib\Math\BigInteger;
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Pure-PHP PKCS#1 compliant implementation of RSA.
|
58 |
+
*
|
59 |
+
* @package RSA
|
60 |
+
* @author Jim Wigginton <terrafrost@php.net>
|
61 |
+
* @access public
|
62 |
+
*/
|
63 |
+
class RSA
|
64 |
+
{
|
65 |
+
/**#@+
|
66 |
+
* @access public
|
67 |
+
* @see self::encrypt()
|
68 |
+
* @see self::decrypt()
|
69 |
+
*/
|
70 |
+
/**
|
71 |
+
* Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
|
72 |
+
* (OAEP) for encryption / decryption.
|
73 |
+
*
|
74 |
+
* Uses sha1 by default.
|
75 |
+
*
|
76 |
+
* @see self::setHash()
|
77 |
+
* @see self::setMGFHash()
|
78 |
+
*/
|
79 |
+
const ENCRYPTION_OAEP = 1;
|
80 |
+
/**
|
81 |
+
* Use PKCS#1 padding.
|
82 |
+
*
|
83 |
+
* Although self::ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
|
84 |
+
* compatibility with protocols (like SSH-1) written before OAEP's introduction.
|
85 |
+
*/
|
86 |
+
const ENCRYPTION_PKCS1 = 2;
|
87 |
+
/**
|
88 |
+
* Do not use any padding
|
89 |
+
*
|
90 |
+
* Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy
|
91 |
+
* stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc.
|
92 |
+
*/
|
93 |
+
const ENCRYPTION_NONE = 3;
|
94 |
+
/**#@-*/
|
95 |
+
|
96 |
+
/**#@+
|
97 |
+
* @access public
|
98 |
+
* @see self::sign()
|
99 |
+
* @see self::verify()
|
100 |
+
* @see self::setHash()
|
101 |
+
*/
|
102 |
+
/**
|
103 |
+
* Use the Probabilistic Signature Scheme for signing
|
104 |
+
*
|
105 |
+
* Uses sha1 by default.
|
106 |
+
*
|
107 |
+
* @see self::setSaltLength()
|
108 |
+
* @see self::setMGFHash()
|
109 |
+
*/
|
110 |
+
const SIGNATURE_PSS = 1;
|
111 |
+
/**
|
112 |
+
* Use the PKCS#1 scheme by default.
|
113 |
+
*
|
114 |
+
* Although self::SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
|
115 |
+
* compatibility with protocols (like SSH-2) written before PSS's introduction.
|
116 |
+
*/
|
117 |
+
const SIGNATURE_PKCS1 = 2;
|
118 |
+
/**#@-*/
|
119 |
+
|
120 |
+
/**#@+
|
121 |
+
* @access private
|
122 |
+
* @see \phpseclib\Crypt\RSA::createKey()
|
123 |
+
*/
|
124 |
+
/**
|
125 |
+
* ASN1 Integer
|
126 |
+
*/
|
127 |
+
const ASN1_INTEGER = 2;
|
128 |
+
/**
|
129 |
+
* ASN1 Bit String
|
130 |
+
*/
|
131 |
+
const ASN1_BITSTRING = 3;
|
132 |
+
/**
|
133 |
+
* ASN1 Octet String
|
134 |
+
*/
|
135 |
+
const ASN1_OCTETSTRING = 4;
|
136 |
+
/**
|
137 |
+
* ASN1 Object Identifier
|
138 |
+
*/
|
139 |
+
const ASN1_OBJECT = 6;
|
140 |
+
/**
|
141 |
+
* ASN1 Sequence (with the constucted bit set)
|
142 |
+
*/
|
143 |
+
const ASN1_SEQUENCE = 48;
|
144 |
+
/**#@-*/
|
145 |
+
|
146 |
+
/**#@+
|
147 |
+
* @access private
|
148 |
+
* @see \phpseclib\Crypt\RSA::__construct()
|
149 |
+
*/
|
150 |
+
/**
|
151 |
+
* To use the pure-PHP implementation
|
152 |
+
*/
|
153 |
+
const MODE_INTERNAL = 1;
|
154 |
+
/**
|
155 |
+
* To use the OpenSSL library
|
156 |
+
*
|
157 |
+
* (if enabled; otherwise, the internal implementation will be used)
|
158 |
+
*/
|
159 |
+
const MODE_OPENSSL = 2;
|
160 |
+
/**#@-*/
|
161 |
+
|
162 |
+
/**#@+
|
163 |
+
* @access public
|
164 |
+
* @see \phpseclib\Crypt\RSA::createKey()
|
165 |
+
* @see \phpseclib\Crypt\RSA::setPrivateKeyFormat()
|
166 |
+
*/
|
167 |
+
/**
|
168 |
+
* PKCS#1 formatted private key
|
169 |
+
*
|
170 |
+
* Used by OpenSSH
|
171 |
+
*/
|
172 |
+
const PRIVATE_FORMAT_PKCS1 = 0;
|
173 |
+
/**
|
174 |
+
* PuTTY formatted private key
|
175 |
+
*/
|
176 |
+
const PRIVATE_FORMAT_PUTTY = 1;
|
177 |
+
/**
|
178 |
+
* XML formatted private key
|
179 |
+
*/
|
180 |
+
const PRIVATE_FORMAT_XML = 2;
|
181 |
+
/**
|
182 |
+
* PKCS#8 formatted private key
|
183 |
+
*/
|
184 |
+
const PRIVATE_FORMAT_PKCS8 = 8;
|
185 |
+
/**
|
186 |
+
* OpenSSH formatted private key
|
187 |
+
*/
|
188 |
+
const PRIVATE_FORMAT_OPENSSH = 9;
|
189 |
+
/**#@-*/
|
190 |
+
|
191 |
+
/**#@+
|
192 |
+
* @access public
|
193 |
+
* @see \phpseclib\Crypt\RSA::createKey()
|
194 |
+
* @see \phpseclib\Crypt\RSA::setPublicKeyFormat()
|
195 |
+
*/
|
196 |
+
/**
|
197 |
+
* Raw public key
|
198 |
+
*
|
199 |
+
* An array containing two \phpseclib\Math\BigInteger objects.
|
200 |
+
*
|
201 |
+
* The exponent can be indexed with any of the following:
|
202 |
+
*
|
203 |
+
* 0, e, exponent, publicExponent
|
204 |
+
*
|
205 |
+
* The modulus can be indexed with any of the following:
|
206 |
+
*
|
207 |
+
* 1, n, modulo, modulus
|
208 |
+
*/
|
209 |
+
const PUBLIC_FORMAT_RAW = 3;
|
210 |
+
/**
|
211 |
+
* PKCS#1 formatted public key (raw)
|
212 |
+
*
|
213 |
+
* Used by File/X509.php
|
214 |
+
*
|
215 |
+
* Has the following header:
|
216 |
+
*
|
217 |
+
* -----BEGIN RSA PUBLIC KEY-----
|
218 |
+
*
|
219 |
+
* Analogous to ssh-keygen's pem format (as specified by -m)
|
220 |
+
*/
|
221 |
+
const PUBLIC_FORMAT_PKCS1 = 4;
|
222 |
+
const PUBLIC_FORMAT_PKCS1_RAW = 4;
|
223 |
+
/**
|
224 |
+
* XML formatted public key
|
225 |
+
*/
|
226 |
+
const PUBLIC_FORMAT_XML = 5;
|
227 |
+
/**
|
228 |
+
* OpenSSH formatted public key
|
229 |
+
*
|
230 |
+
* Place in $HOME/.ssh/authorized_keys
|
231 |
+
*/
|
232 |
+
const PUBLIC_FORMAT_OPENSSH = 6;
|
233 |
+
/**
|
234 |
+
* PKCS#1 formatted public key (encapsulated)
|
235 |
+
*
|
236 |
+
* Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
|
237 |
+
*
|
238 |
+
* Has the following header:
|
239 |
+
*
|
240 |
+
* -----BEGIN PUBLIC KEY-----
|
241 |
+
*
|
242 |
+
* Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
|
243 |
+
* is specific to private keys it's basically creating a DER-encoded wrapper
|
244 |
+
* for keys. This just extends that same concept to public keys (much like ssh-keygen)
|
245 |
+
*/
|
246 |
+
const PUBLIC_FORMAT_PKCS8 = 7;
|
247 |
+
/**#@-*/
|
248 |
+
|
249 |
+
/**
|
250 |
+
* Precomputed Zero
|
251 |
+
*
|
252 |
+
* @var \phpseclib\Math\BigInteger
|
253 |
+
* @access private
|
254 |
+
*/
|
255 |
+
var $zero;
|
256 |
+
|
257 |
+
/**
|
258 |
+
* Precomputed One
|
259 |
+
*
|
260 |
+
* @var \phpseclib\Math\BigInteger
|
261 |
+
* @access private
|
262 |
+
*/
|
263 |
+
var $one;
|
264 |
+
|
265 |
+
/**
|
266 |
+
* Private Key Format
|
267 |
+
*
|
268 |
+
* @var int
|
269 |
+
* @access private
|
270 |
+
*/
|
271 |
+
var $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1;
|
272 |
+
|
273 |
+
/**
|
274 |
+
* Public Key Format
|
275 |
+
*
|
276 |
+
* @var int
|
277 |
+
* @access public
|
278 |
+
*/
|
279 |
+
var $publicKeyFormat = self::PUBLIC_FORMAT_PKCS8;
|
280 |
+
|
281 |
+
/**
|
282 |
+
* Modulus (ie. n)
|
283 |
+
*
|
284 |
+
* @var \phpseclib\Math\BigInteger
|
285 |
+
* @access private
|
286 |
+
*/
|
287 |
+
var $modulus;
|
288 |
+
|
289 |
+
/**
|
290 |
+
* Modulus length
|
291 |
+
*
|
292 |
+
* @var \phpseclib\Math\BigInteger
|
293 |
+
* @access private
|
294 |
+
*/
|
295 |
+
var $k;
|
296 |
+
|
297 |
+
/**
|
298 |
+
* Exponent (ie. e or d)
|
299 |
+
*
|
300 |
+
* @var \phpseclib\Math\BigInteger
|
301 |
+
* @access private
|
302 |
+
*/
|
303 |
+
var $exponent;
|
304 |
+
|
305 |
+
/**
|
306 |
+
* Primes for Chinese Remainder Theorem (ie. p and q)
|
307 |
+
*
|
308 |
+
* @var array
|
309 |
+
* @access private
|
310 |
+
*/
|
311 |
+
var $primes;
|
312 |
+
|
313 |
+
/**
|
314 |
+
* Exponents for Chinese Remainder Theorem (ie. dP and dQ)
|
315 |
+
*
|
316 |
+
* @var array
|
317 |
+
* @access private
|
318 |
+
*/
|
319 |
+
var $exponents;
|
320 |
+
|
321 |
+
/**
|
322 |
+
* Coefficients for Chinese Remainder Theorem (ie. qInv)
|
323 |
+
*
|
324 |
+
* @var array
|
325 |
+
* @access private
|
326 |
+
*/
|
327 |
+
var $coefficients;
|
328 |
+
|
329 |
+
/**
|
330 |
+
* Hash name
|
331 |
+
*
|
332 |
+
* @var string
|
333 |
+
* @access private
|
334 |
+
*/
|
335 |
+
var $hashName;
|
336 |
+
|
337 |
+
/**
|
338 |
+
* Hash function
|
339 |
+
*
|
340 |
+
* @var \phpseclib\Crypt\Hash
|
341 |
+
* @access private
|
342 |
+
*/
|
343 |
+
var $hash;
|
344 |
+
|
345 |
+
/**
|
346 |
+
* Length of hash function output
|
347 |
+
*
|
348 |
+
* @var int
|
349 |
+
* @access private
|
350 |
+
*/
|
351 |
+
var $hLen;
|
352 |
+
|
353 |
+
/**
|
354 |
+
* Length of salt
|
355 |
+
*
|
356 |
+
* @var int
|
357 |
+
* @access private
|
358 |
+
*/
|
359 |
+
var $sLen;
|
360 |
+
|
361 |
+
/**
|
362 |
+
* Hash function for the Mask Generation Function
|
363 |
+
*
|
364 |
+
* @var \phpseclib\Crypt\Hash
|
365 |
+
* @access private
|
366 |
+
*/
|
367 |
+
var $mgfHash;
|
368 |
+
|
369 |
+
/**
|
370 |
+
* Length of MGF hash function output
|
371 |
+
*
|
372 |
+
* @var int
|
373 |
+
* @access private
|
374 |
+
*/
|
375 |
+
var $mgfHLen;
|
376 |
+
|
377 |
+
/**
|
378 |
+
* Encryption mode
|
379 |
+
*
|
380 |
+
* @var int
|
381 |
+
* @access private
|
382 |
+
*/
|
383 |
+
var $encryptionMode = self::ENCRYPTION_OAEP;
|
384 |
+
|
385 |
+
/**
|
386 |
+
* Signature mode
|
387 |
+
*
|
388 |
+
* @var int
|
389 |
+
* @access private
|
390 |
+
*/
|
391 |
+
var $signatureMode = self::SIGNATURE_PSS;
|
392 |
+
|
393 |
+
/**
|
394 |
+
* Public Exponent
|
395 |
+
*
|
396 |
+
* @var mixed
|
397 |
+
* @access private
|
398 |
+
*/
|
399 |
+
var $publicExponent = false;
|
400 |
+
|
401 |
+
/**
|
402 |
+
* Password
|
403 |
+
*
|
404 |
+
* @var string
|
405 |
+
* @access private
|
406 |
+
*/
|
407 |
+
var $password = false;
|
408 |
+
|
409 |
+
/**
|
410 |
+
* Components
|
411 |
+
*
|
412 |
+
* For use with parsing XML formatted keys. PHP's XML Parser functions use utilized - instead of PHP's DOM functions -
|
413 |
+
* because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't.
|
414 |
+
*
|
415 |
+
* @see self::_start_element_handler()
|
416 |
+
* @var array
|
417 |
+
* @access private
|
418 |
+
*/
|
419 |
+
var $components = array();
|
420 |
+
|
421 |
+
/**
|
422 |
+
* Current String
|
423 |
+
*
|
424 |
+
* For use with parsing XML formatted keys.
|
425 |
+
*
|
426 |
+
* @see self::_character_handler()
|
427 |
+
* @see self::_stop_element_handler()
|
428 |
+
* @var mixed
|
429 |
+
* @access private
|
430 |
+
*/
|
431 |
+
var $current;
|
432 |
+
|
433 |
+
/**
|
434 |
+
* OpenSSL configuration file name.
|
435 |
+
*
|
436 |
+
* Set to null to use system configuration file.
|
437 |
+
* @see self::createKey()
|
438 |
+
* @var mixed
|
439 |
+
* @Access public
|
440 |
+
*/
|
441 |
+
var $configFile;
|
442 |
+
|
443 |
+
/**
|
444 |
+
* Public key comment field.
|
445 |
+
*
|
446 |
+
* @var string
|
447 |
+
* @access private
|
448 |
+
*/
|
449 |
+
var $comment = 'phpseclib-generated-key';
|
450 |
+
|
451 |
+
/**
|
452 |
+
* The constructor
|
453 |
+
*
|
454 |
+
* If you want to make use of the openssl extension, you'll need to set the mode manually, yourself. The reason
|
455 |
+
* \phpseclib\Crypt\RSA doesn't do it is because OpenSSL doesn't fail gracefully. openssl_pkey_new(), in particular, requires
|
456 |
+
* openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late.
|
457 |
+
*
|
458 |
+
* @return \phpseclib\Crypt\RSA
|
459 |
+
* @access public
|
460 |
+
*/
|
461 |
+
function __construct()
|
462 |
+
{
|
463 |
+
$this->configFile = dirname(__FILE__) . '/../openssl.cnf';
|
464 |
+
|
465 |
+
if (!defined('CRYPT_RSA_MODE')) {
|
466 |
+
switch (true) {
|
467 |
+
// Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular,
|
468 |
+
// Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger
|
469 |
+
// can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either.
|
470 |
+
case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
|
471 |
+
define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
|
472 |
+
break;
|
473 |
+
case extension_loaded('openssl') && file_exists($this->configFile):
|
474 |
+
// some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
|
475 |
+
$versions = array();
|
476 |
+
|
477 |
+
// avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems)
|
478 |
+
if (strpos(ini_get('disable_functions'), 'phpinfo') === false) {
|
479 |
+
ob_start();
|
480 |
+
@phpinfo();
|
481 |
+
$content = ob_get_contents();
|
482 |
+
ob_end_clean();
|
483 |
+
|
484 |
+
preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
|
485 |
+
|
486 |
+
if (!empty($matches[1])) {
|
487 |
+
for ($i = 0; $i < count($matches[1]); $i++) {
|
488 |
+
$fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
|
489 |
+
|
490 |
+
// Remove letter part in OpenSSL version
|
491 |
+
if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
|
492 |
+
$versions[$matches[1][$i]] = $fullVersion;
|
493 |
+
} else {
|
494 |
+
$versions[$matches[1][$i]] = $m[0];
|
495 |
+
}
|
496 |
+
}
|
497 |
+
}
|
498 |
+
}
|
499 |
+
|
500 |
+
// it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
|
501 |
+
switch (true) {
|
502 |
+
case !isset($versions['Header']):
|
503 |
+
case !isset($versions['Library']):
|
504 |
+
case $versions['Header'] == $versions['Library']:
|
505 |
+
case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0:
|
506 |
+
define('CRYPT_RSA_MODE', self::MODE_OPENSSL);
|
507 |
+
break;
|
508 |
+
default:
|
509 |
+
define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
|
510 |
+
define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
|
511 |
+
}
|
512 |
+
break;
|
513 |
+
default:
|
514 |
+
define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
|
515 |
+
}
|
516 |
+
}
|
517 |
+
|
518 |
+
$this->zero = new BigInteger();
|
519 |
+
$this->one = new BigInteger(1);
|
520 |
+
|
521 |
+
$this->hash = new Hash('sha1');
|
522 |
+
$this->hLen = $this->hash->getLength();
|
523 |
+
$this->hashName = 'sha1';
|
524 |
+
$this->mgfHash = new Hash('sha1');
|
525 |
+
$this->mgfHLen = $this->mgfHash->getLength();
|
526 |
+
}
|
527 |
+
|
528 |
+
/**
|
529 |
+
* Create public / private key pair
|
530 |
+
*
|
531 |
+
* Returns an array with the following three elements:
|
532 |
+
* - 'privatekey': The private key.
|
533 |
+
* - 'publickey': The public key.
|
534 |
+
* - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
|
535 |
+
* Will need to be passed back to \phpseclib\Crypt\RSA::createKey() as the third parameter for further processing.
|
536 |
+
*
|
537 |
+
* @access public
|
538 |
+
* @param int $bits
|
539 |
+
* @param int $timeout
|
540 |
+
* @param array $p
|
541 |
+
*/
|
542 |
+
function createKey($bits = 1024, $timeout = false, $partial = array())
|
543 |
+
{
|
544 |
+
if (!defined('CRYPT_RSA_EXPONENT')) {
|
545 |
+
// http://en.wikipedia.org/wiki/65537_%28number%29
|
546 |
+
define('CRYPT_RSA_EXPONENT', '65537');
|
547 |
+
}
|
548 |
+
// per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
|
549 |
+
// than 256 bits. as a consequence if the key you're trying to create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME
|
550 |
+
// to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if
|
551 |
+
// CRYPT_RSA_MODE is set to self::MODE_INTERNAL. if CRYPT_RSA_MODE is set to self::MODE_OPENSSL then
|
552 |
+
// CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key
|
553 |
+
// generation when there's a chance neither gmp nor OpenSSL are installed)
|
554 |
+
if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
|
555 |
+
define('CRYPT_RSA_SMALLEST_PRIME', 4096);
|
556 |
+
}
|
557 |
+
|
558 |
+
// OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
|
559 |
+
if (CRYPT_RSA_MODE == self::MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
|
560 |
+
$config = array();
|
561 |
+
if (isset($this->configFile)) {
|
562 |
+
$config['config'] = $this->configFile;
|
563 |
+
}
|
564 |
+
$rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config);
|
565 |
+
openssl_pkey_export($rsa, $privatekey, null, $config);
|
566 |
+
$publickey = openssl_pkey_get_details($rsa);
|
567 |
+
$publickey = $publickey['key'];
|
568 |
+
|
569 |
+
$privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, self::PRIVATE_FORMAT_PKCS1)));
|
570 |
+
$publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, self::PUBLIC_FORMAT_PKCS1)));
|
571 |
+
|
572 |
+
// clear the buffer of error strings stemming from a minimalistic openssl.cnf
|
573 |
+
while (openssl_error_string() !== false) {
|
574 |
+
}
|
575 |
+
|
576 |
+
return array(
|
577 |
+
'privatekey' => $privatekey,
|
578 |
+
'publickey' => $publickey,
|
579 |
+
'partialkey' => false
|
580 |
+
);
|
581 |
+
}
|
582 |
+
|
583 |
+
static $e;
|
584 |
+
if (!isset($e)) {
|
585 |
+
$e = new BigInteger(CRYPT_RSA_EXPONENT);
|
586 |
+
}
|
587 |
+
|
588 |
+
extract($this->_generateMinMax($bits));
|
589 |
+
$absoluteMin = $min;
|
590 |
+
$temp = $bits >> 1; // divide by two to see how many bits P and Q would be
|
591 |
+
if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
|
592 |
+
$num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
|
593 |
+
$temp = CRYPT_RSA_SMALLEST_PRIME;
|
594 |
+
} else {
|
595 |
+
$num_primes = 2;
|
596 |
+
}
|
597 |
+
extract($this->_generateMinMax($temp + $bits % $temp));
|
598 |
+
$finalMax = $max;
|
599 |
+
extract($this->_generateMinMax($temp));
|
600 |
+
|
601 |
+
$generator = new BigInteger();
|
602 |
+
|
603 |
+
$n = $this->one->copy();
|
604 |
+
if (!empty($partial)) {
|
605 |
+
extract(unserialize($partial));
|
606 |
+
} else {
|
607 |
+
$exponents = $coefficients = $primes = array();
|
608 |
+
$lcm = array(
|
609 |
+
'top' => $this->one->copy(),
|
610 |
+
'bottom' => false
|
611 |
+
);
|
612 |
+
}
|
613 |
+
|
614 |
+
$start = time();
|
615 |
+
$i0 = count($primes) + 1;
|
616 |
+
|
617 |
+
do {
|
618 |
+
for ($i = $i0; $i <= $num_primes; $i++) {
|
619 |
+
if ($timeout !== false) {
|
620 |
+
$timeout-= time() - $start;
|
621 |
+
$start = time();
|
622 |
+
if ($timeout <= 0) {
|
623 |
+
return array(
|
624 |
+
'privatekey' => '',
|
625 |
+
'publickey' => '',
|
626 |
+
'partialkey' => serialize(array(
|
627 |
+
'primes' => $primes,
|
628 |
+
'coefficients' => $coefficients,
|
629 |
+
'lcm' => $lcm,
|
630 |
+
'exponents' => $exponents
|
631 |
+
))
|
632 |
+
);
|
633 |
+
}
|
634 |
+
}
|
635 |
+
|
636 |
+
if ($i == $num_primes) {
|
637 |
+
list($min, $temp) = $absoluteMin->divide($n);
|
638 |
+
if (!$temp->equals($this->zero)) {
|
639 |
+
$min = $min->add($this->one); // ie. ceil()
|
640 |
+
}
|
641 |
+
$primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
|
642 |
+
} else {
|
643 |
+
$primes[$i] = $generator->randomPrime($min, $max, $timeout);
|
644 |
+
}
|
645 |
+
|
646 |
+
if ($primes[$i] === false) { // if we've reached the timeout
|
647 |
+
if (count($primes) > 1) {
|
648 |
+
$partialkey = '';
|
649 |
+
} else {
|
650 |
+
array_pop($primes);
|
651 |
+
$partialkey = serialize(array(
|
652 |
+
'primes' => $primes,
|
653 |
+
'coefficients' => $coefficients,
|
654 |
+
'lcm' => $lcm,
|
655 |
+
'exponents' => $exponents
|
656 |
+
));
|
657 |
+
}
|
658 |
+
|
659 |
+
return array(
|
660 |
+
'privatekey' => '',
|
661 |
+
'publickey' => '',
|
662 |
+
'partialkey' => $partialkey
|
663 |
+
);
|
664 |
+
}
|
665 |
+
|
666 |
+
// the first coefficient is calculated differently from the rest
|
667 |
+
// ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
|
668 |
+
if ($i > 2) {
|
669 |
+
$coefficients[$i] = $n->modInverse($primes[$i]);
|
670 |
+
}
|
671 |
+
|
672 |
+
$n = $n->multiply($primes[$i]);
|
673 |
+
|
674 |
+
$temp = $primes[$i]->subtract($this->one);
|
675 |
+
|
676 |
+
// textbook RSA implementations use Euler's totient function instead of the least common multiple.
|
677 |
+
// see http://en.wikipedia.org/wiki/Euler%27s_totient_function
|
678 |
+
$lcm['top'] = $lcm['top']->multiply($temp);
|
679 |
+
$lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
|
680 |
+
|
681 |
+
$exponents[$i] = $e->modInverse($temp);
|
682 |
+
}
|
683 |
+
|
684 |
+
list($temp) = $lcm['top']->divide($lcm['bottom']);
|
685 |
+
$gcd = $temp->gcd($e);
|
686 |
+
$i0 = 1;
|
687 |
+
} while (!$gcd->equals($this->one));
|
688 |
+
|
689 |
+
$d = $e->modInverse($temp);
|
690 |
+
|
691 |
+
$coefficients[2] = $primes[2]->modInverse($primes[1]);
|
692 |
+
|
693 |
+
// from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
|
694 |
+
// RSAPrivateKey ::= SEQUENCE {
|
695 |
+
// version Version,
|
696 |
+
// modulus INTEGER, -- n
|
697 |
+
// publicExponent INTEGER, -- e
|
698 |
+
// privateExponent INTEGER, -- d
|
699 |
+
// prime1 INTEGER, -- p
|
700 |
+
// prime2 INTEGER, -- q
|
701 |
+
// exponent1 INTEGER, -- d mod (p-1)
|
702 |
+
// exponent2 INTEGER, -- d mod (q-1)
|
703 |
+
// coefficient INTEGER, -- (inverse of q) mod p
|
704 |
+
// otherPrimeInfos OtherPrimeInfos OPTIONAL
|
705 |
+
// }
|
706 |
+
|
707 |
+
return array(
|
708 |
+
'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
|
709 |
+
'publickey' => $this->_convertPublicKey($n, $e),
|
710 |
+
'partialkey' => false
|
711 |
+
);
|
712 |
+
}
|
713 |
+
|
714 |
+
/**
|
715 |
+
* Convert a private key to the appropriate format.
|
716 |
+
*
|
717 |
+
* @access private
|
718 |
+
* @see self::setPrivateKeyFormat()
|
719 |
+
* @param string $RSAPrivateKey
|
720 |
+
* @return string
|
721 |
+
*/
|
722 |
+
function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
|
723 |
+
{
|
724 |
+
$signed = $this->privateKeyFormat != self::PRIVATE_FORMAT_XML;
|
725 |
+
$num_primes = count($primes);
|
726 |
+
$raw = array(
|
727 |
+
'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
|
728 |
+
'modulus' => $n->toBytes($signed),
|
729 |
+
'publicExponent' => $e->toBytes($signed),
|
730 |
+
'privateExponent' => $d->toBytes($signed),
|
731 |
+
'prime1' => $primes[1]->toBytes($signed),
|
732 |
+
'prime2' => $primes[2]->toBytes($signed),
|
733 |
+
'exponent1' => $exponents[1]->toBytes($signed),
|
734 |
+
'exponent2' => $exponents[2]->toBytes($signed),
|
735 |
+
'coefficient' => $coefficients[2]->toBytes($signed)
|
736 |
+
);
|
737 |
+
|
738 |
+
// if the format in question does not support multi-prime rsa and multi-prime rsa was used,
|
739 |
+
// call _convertPublicKey() instead.
|
740 |
+
switch ($this->privateKeyFormat) {
|
741 |
+
case self::PRIVATE_FORMAT_XML:
|
742 |
+
if ($num_primes != 2) {
|
743 |
+
return false;
|
744 |
+
}
|
745 |
+
return "<RSAKeyValue>\r\n" .
|
746 |
+
' <Modulus>' . base64_encode($raw['modulus']) . "</Modulus>\r\n" .
|
747 |
+
' <Exponent>' . base64_encode($raw['publicExponent']) . "</Exponent>\r\n" .
|
748 |
+
' <P>' . base64_encode($raw['prime1']) . "</P>\r\n" .
|
749 |
+
' <Q>' . base64_encode($raw['prime2']) . "</Q>\r\n" .
|
750 |
+
' <DP>' . base64_encode($raw['exponent1']) . "</DP>\r\n" .
|
751 |
+
' <DQ>' . base64_encode($raw['exponent2']) . "</DQ>\r\n" .
|
752 |
+
' <InverseQ>' . base64_encode($raw['coefficient']) . "</InverseQ>\r\n" .
|
753 |
+
' <D>' . base64_encode($raw['privateExponent']) . "</D>\r\n" .
|
754 |
+
'</RSAKeyValue>';
|
755 |
+
break;
|
756 |
+
case self::PRIVATE_FORMAT_PUTTY:
|
757 |
+
if ($num_primes != 2) {
|
758 |
+
return false;
|
759 |
+
}
|
760 |
+
$key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
|
761 |
+
$encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none';
|
762 |
+
$key.= $encryption;
|
763 |
+
$key.= "\r\nComment: " . $this->comment . "\r\n";
|
764 |
+
$public = pack(
|
765 |
+
'Na*Na*Na*',
|
766 |
+
strlen('ssh-rsa'),
|
767 |
+
'ssh-rsa',
|
768 |
+
strlen($raw['publicExponent']),
|
769 |
+
$raw['publicExponent'],
|
770 |
+
strlen($raw['modulus']),
|
771 |
+
$raw['modulus']
|
772 |
+
);
|
773 |
+
$source = pack(
|
774 |
+
'Na*Na*Na*Na*',
|
775 |
+
strlen('ssh-rsa'),
|
776 |
+
'ssh-rsa',
|
777 |
+
strlen($encryption),
|
778 |
+
$encryption,
|
779 |
+
strlen($this->comment),
|
780 |
+
$this->comment,
|
781 |
+
strlen($public),
|
782 |
+
$public
|
783 |
+
);
|
784 |
+
$public = base64_encode($public);
|
785 |
+
$key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n";
|
786 |
+
$key.= chunk_split($public, 64);
|
787 |
+
$private = pack(
|
788 |
+
'Na*Na*Na*Na*',
|
789 |
+
strlen($raw['privateExponent']),
|
790 |
+
$raw['privateExponent'],
|
791 |
+
strlen($raw['prime1']),
|
792 |
+
$raw['prime1'],
|
793 |
+
strlen($raw['prime2']),
|
794 |
+
$raw['prime2'],
|
795 |
+
strlen($raw['coefficient']),
|
796 |
+
$raw['coefficient']
|
797 |
+
);
|
798 |
+
if (empty($this->password) && !is_string($this->password)) {
|
799 |
+
$source.= pack('Na*', strlen($private), $private);
|
800 |
+
$hashkey = 'putty-private-key-file-mac-key';
|
801 |
+
} else {
|
802 |
+
$private.= Random::string(16 - (strlen($private) & 15));
|
803 |
+
$source.= pack('Na*', strlen($private), $private);
|
804 |
+
$sequence = 0;
|
805 |
+
$symkey = '';
|
806 |
+
while (strlen($symkey) < 32) {
|
807 |
+
$temp = pack('Na*', $sequence++, $this->password);
|
808 |
+
$symkey.= pack('H*', sha1($temp));
|
809 |
+
}
|
810 |
+
$symkey = substr($symkey, 0, 32);
|
811 |
+
$crypto = new AES();
|
812 |
+
|
813 |
+
$crypto->setKey($symkey);
|
814 |
+
$crypto->disablePadding();
|
815 |
+
$private = $crypto->encrypt($private);
|
816 |
+
$hashkey = 'putty-private-key-file-mac-key' . $this->password;
|
817 |
+
}
|
818 |
+
|
819 |
+
$private = base64_encode($private);
|
820 |
+
$key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n";
|
821 |
+
$key.= chunk_split($private, 64);
|
822 |
+
$hash = new Hash('sha1');
|
823 |
+
$hash->setKey(pack('H*', sha1($hashkey)));
|
824 |
+
$key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n";
|
825 |
+
|
826 |
+
return $key;
|
827 |
+
case self::PRIVATE_FORMAT_OPENSSH:
|
828 |
+
if ($num_primes != 2) {
|
829 |
+
return false;
|
830 |
+
}
|
831 |
+
$publicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']);
|
832 |
+
$privateKey = pack(
|
833 |
+
'Na*Na*Na*Na*Na*Na*Na*',
|
834 |
+
strlen('ssh-rsa'),
|
835 |
+
'ssh-rsa',
|
836 |
+
strlen($raw['modulus']),
|
837 |
+
$raw['modulus'],
|
838 |
+
strlen($raw['publicExponent']),
|
839 |
+
$raw['publicExponent'],
|
840 |
+
strlen($raw['privateExponent']),
|
841 |
+
$raw['privateExponent'],
|
842 |
+
strlen($raw['coefficient']),
|
843 |
+
$raw['coefficient'],
|
844 |
+
strlen($raw['prime1']),
|
845 |
+
$raw['prime1'],
|
846 |
+
strlen($raw['prime2']),
|
847 |
+
$raw['prime2']
|
848 |
+
);
|
849 |
+
$checkint = Random::string(4);
|
850 |
+
$paddedKey = pack(
|
851 |
+
'a*Na*',
|
852 |
+
$checkint . $checkint . $privateKey,
|
853 |
+
strlen($this->comment),
|
854 |
+
$this->comment
|
855 |
+
);
|
856 |
+
$paddingLength = (7 * strlen($paddedKey)) % 8;
|
857 |
+
for ($i = 1; $i <= $paddingLength; $i++) {
|
858 |
+
$paddedKey.= chr($i);
|
859 |
+
}
|
860 |
+
$key = pack(
|
861 |
+
'Na*Na*Na*NNa*Na*',
|
862 |
+
strlen('none'),
|
863 |
+
'none',
|
864 |
+
strlen('none'),
|
865 |
+
'none',
|
866 |
+
0,
|
867 |
+
'',
|
868 |
+
1,
|
869 |
+
strlen($publicKey),
|
870 |
+
$publicKey,
|
871 |
+
strlen($paddedKey),
|
872 |
+
$paddedKey
|
873 |
+
);
|
874 |
+
$key = "openssh-key-v1\0$key";
|
875 |
+
|
876 |
+
return "-----BEGIN OPENSSH PRIVATE KEY-----\r\n" .
|
877 |
+
chunk_split(base64_encode($key), 70) .
|
878 |
+
"-----END OPENSSH PRIVATE KEY-----";
|
879 |
+
default: // eg. self::PRIVATE_FORMAT_PKCS1
|
880 |
+
$components = array();
|
881 |
+
foreach ($raw as $name => $value) {
|
882 |
+
$components[$name] = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
|
883 |
+
}
|
884 |
+
|
885 |
+
$RSAPrivateKey = implode('', $components);
|
886 |
+
|
887 |
+
if ($num_primes > 2) {
|
888 |
+
$OtherPrimeInfos = '';
|
889 |
+
for ($i = 3; $i <= $num_primes; $i++) {
|
890 |
+
// OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
|
891 |
+
//
|
892 |
+
// OtherPrimeInfo ::= SEQUENCE {
|
893 |
+
// prime INTEGER, -- ri
|
894 |
+
// exponent INTEGER, -- di
|
895 |
+
// coefficient INTEGER -- ti
|
896 |
+
// }
|
897 |
+
$OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
|
898 |
+
$OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
|
899 |
+
$OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
|
900 |
+
$OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
|
901 |
+
}
|
902 |
+
$RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
|
903 |
+
}
|
904 |
+
|
905 |
+
$RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
|
906 |
+
|
907 |
+
if ($this->privateKeyFormat == self::PRIVATE_FORMAT_PKCS8) {
|
908 |
+
$rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
|
909 |
+
$RSAPrivateKey = pack(
|
910 |
+
'Ca*a*Ca*a*',
|
911 |
+
self::ASN1_INTEGER,
|
912 |
+
"\01\00",
|
913 |
+
$rsaOID,
|
914 |
+
4,
|
915 |
+
$this->_encodeLength(strlen($RSAPrivateKey)),
|
916 |
+
$RSAPrivateKey
|
917 |
+
);
|
918 |
+
$RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
|
919 |
+
if (!empty($this->password) || is_string($this->password)) {
|
920 |
+
$salt = Random::string(8);
|
921 |
+
$iterationCount = 2048;
|
922 |
+
|
923 |
+
$crypto = new DES();
|
924 |
+
$crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
|
925 |
+
$RSAPrivateKey = $crypto->encrypt($RSAPrivateKey);
|
926 |
+
|
927 |
+
$parameters = pack(
|
928 |
+
'Ca*a*Ca*N',
|
929 |
+
self::ASN1_OCTETSTRING,
|
930 |
+
$this->_encodeLength(strlen($salt)),
|
931 |
+
$salt,
|
932 |
+
self::ASN1_INTEGER,
|
933 |
+
$this->_encodeLength(4),
|
934 |
+
$iterationCount
|
935 |
+
);
|
936 |
+
$pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";
|
937 |
+
|
938 |
+
$encryptionAlgorithm = pack(
|
939 |
+
'Ca*a*Ca*a*',
|
940 |
+
self::ASN1_OBJECT,
|
941 |
+
$this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)),
|
942 |
+
$pbeWithMD5AndDES_CBC,
|
943 |
+
self::ASN1_SEQUENCE,
|
944 |
+
$this->_encodeLength(strlen($parameters)),
|
945 |
+
$parameters
|
946 |
+
);
|
947 |
+
|
948 |
+
$RSAPrivateKey = pack(
|
949 |
+
'Ca*a*Ca*a*',
|
950 |
+
self::ASN1_SEQUENCE,
|
951 |
+
$this->_encodeLength(strlen($encryptionAlgorithm)),
|
952 |
+
$encryptionAlgorithm,
|
953 |
+
self::ASN1_OCTETSTRING,
|
954 |
+
$this->_encodeLength(strlen($RSAPrivateKey)),
|
955 |
+
$RSAPrivateKey
|
956 |
+
);
|
957 |
+
|
958 |
+
$RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
|
959 |
+
|
960 |
+
$RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
|
961 |
+
chunk_split(base64_encode($RSAPrivateKey), 64) .
|
962 |
+
'-----END ENCRYPTED PRIVATE KEY-----';
|
963 |
+
} else {
|
964 |
+
$RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" .
|
965 |
+
chunk_split(base64_encode($RSAPrivateKey), 64) .
|
966 |
+
'-----END PRIVATE KEY-----';
|
967 |
+
}
|
968 |
+
return $RSAPrivateKey;
|
969 |
+
}
|
970 |
+
|
971 |
+
if (!empty($this->password) || is_string($this->password)) {
|
972 |
+
$iv = Random::string(8);
|
973 |
+
$symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
|
974 |
+
$symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
|
975 |
+
$des = new TripleDES();
|
976 |
+
$des->setKey($symkey);
|
977 |
+
$des->setIV($iv);
|
978 |
+
$iv = strtoupper(bin2hex($iv));
|
979 |
+
$RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
|
980 |
+
"Proc-Type: 4,ENCRYPTED\r\n" .
|
981 |
+
"DEK-Info: DES-EDE3-CBC,$iv\r\n" .
|
982 |
+
"\r\n" .
|
983 |
+
chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
|
984 |
+
'-----END RSA PRIVATE KEY-----';
|
985 |
+
} else {
|
986 |
+
$RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
|
987 |
+
chunk_split(base64_encode($RSAPrivateKey), 64) .
|
988 |
+
'-----END RSA PRIVATE KEY-----';
|
989 |
+
}
|
990 |
+
|
991 |
+
return $RSAPrivateKey;
|
992 |
+
}
|
993 |
+
}
|
994 |
+
|
995 |
+
/**
|
996 |
+
* Convert a public key to the appropriate format
|
997 |
+
*
|
998 |
+
* @access private
|
999 |
+
* @see self::setPublicKeyFormat()
|
1000 |
+
* @param string $RSAPrivateKey
|
1001 |
+
* @return string
|
1002 |
+
*/
|
1003 |
+
function _convertPublicKey($n, $e)
|
1004 |
+
{
|
1005 |
+
$signed = $this->publicKeyFormat != self::PUBLIC_FORMAT_XML;
|
1006 |
+
|
1007 |
+
$modulus = $n->toBytes($signed);
|
1008 |
+
$publicExponent = $e->toBytes($signed);
|
1009 |
+
|
1010 |
+
switch ($this->publicKeyFormat) {
|
1011 |
+
case self::PUBLIC_FORMAT_RAW:
|
1012 |
+
return array('e' => $e->copy(), 'n' => $n->copy());
|
1013 |
+
case self::PUBLIC_FORMAT_XML:
|
1014 |
+
return "<RSAKeyValue>\r\n" .
|
1015 |
+
' <Modulus>' . base64_encode($modulus) . "</Modulus>\r\n" .
|
1016 |
+
' <Exponent>' . base64_encode($publicExponent) . "</Exponent>\r\n" .
|
1017 |
+
'</RSAKeyValue>';
|
1018 |
+
break;
|
1019 |
+
case self::PUBLIC_FORMAT_OPENSSH:
|
1020 |
+
// from <http://tools.ietf.org/html/rfc4253#page-15>:
|
1021 |
+
// string "ssh-rsa"
|
1022 |
+
// mpint e
|
1023 |
+
// mpint n
|
1024 |
+
$RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
|
1025 |
+
$RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment;
|
1026 |
+
|
1027 |
+
return $RSAPublicKey;
|
1028 |
+
default: // eg. self::PUBLIC_FORMAT_PKCS1_RAW or self::PUBLIC_FORMAT_PKCS1
|
1029 |
+
// from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
|
1030 |
+
// RSAPublicKey ::= SEQUENCE {
|
1031 |
+
// modulus INTEGER, -- n
|
1032 |
+
// publicExponent INTEGER -- e
|
1033 |
+
// }
|
1034 |
+
$components = array(
|
1035 |
+
'modulus' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
|
1036 |
+
'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
|
1037 |
+
);
|
1038 |
+
|
1039 |
+
$RSAPublicKey = pack(
|
1040 |
+
'Ca*a*a*',
|
1041 |
+
self::ASN1_SEQUENCE,
|
1042 |
+
$this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
|
1043 |
+
$components['modulus'],
|
1044 |
+
$components['publicExponent']
|
1045 |
+
);
|
1046 |
+
|
1047 |
+
if ($this->publicKeyFormat == self::PUBLIC_FORMAT_PKCS1_RAW) {
|
1048 |
+
$RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" .
|
1049 |
+
chunk_split(base64_encode($RSAPublicKey), 64) .
|
1050 |
+
'-----END RSA PUBLIC KEY-----';
|
1051 |
+
} else {
|
1052 |
+
// sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
|
1053 |
+
$rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
|
1054 |
+
$RSAPublicKey = chr(0) . $RSAPublicKey;
|
1055 |
+
$RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
|
1056 |
+
|
1057 |
+
$RSAPublicKey = pack(
|
1058 |
+
'Ca*a*',
|
1059 |
+
self::ASN1_SEQUENCE,
|
1060 |
+
$this->_encodeLength(strlen($rsaOID . $RSAPublicKey)),
|
1061 |
+
$rsaOID . $RSAPublicKey
|
1062 |
+
);
|
1063 |
+
|
1064 |
+
$RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
|
1065 |
+
chunk_split(base64_encode($RSAPublicKey), 64) .
|
1066 |
+
'-----END PUBLIC KEY-----';
|
1067 |
+
}
|
1068 |
+
|
1069 |
+
return $RSAPublicKey;
|
1070 |
+
}
|
1071 |
+
}
|
1072 |
+
|
1073 |
+
/**
|
1074 |
+
* Break a public or private key down into its constituant components
|
1075 |
+
*
|
1076 |
+
* @access private
|
1077 |
+
* @see self::_convertPublicKey()
|
1078 |
+
* @see self::_convertPrivateKey()
|
1079 |
+
* @param string|array $key
|
1080 |
+
* @param int $type
|
1081 |
+
* @return array|bool
|
1082 |
+
*/
|
1083 |
+
function _parseKey($key, $type)
|
1084 |
+
{
|
1085 |
+
if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) {
|
1086 |
+
return false;
|
1087 |
+
}
|
1088 |
+
|
1089 |
+
switch ($type) {
|
1090 |
+
case self::PUBLIC_FORMAT_RAW:
|
1091 |
+
if (!is_array($key)) {
|
1092 |
+
return false;
|
1093 |
+
}
|
1094 |
+
$components = array();
|
1095 |
+
switch (true) {
|
1096 |
+
case isset($key['e']):
|
1097 |
+
$components['publicExponent'] = $key['e']->copy();
|
1098 |
+
break;
|
1099 |
+
case isset($key['exponent']):
|
1100 |
+
$components['publicExponent'] = $key['exponent']->copy();
|
1101 |
+
break;
|
1102 |
+
case isset($key['publicExponent']):
|
1103 |
+
$components['publicExponent'] = $key['publicExponent']->copy();
|
1104 |
+
break;
|
1105 |
+
case isset($key[0]):
|
1106 |
+
$components['publicExponent'] = $key[0]->copy();
|
1107 |
+
}
|
1108 |
+
switch (true) {
|
1109 |
+
case isset($key['n']):
|
1110 |
+
$components['modulus'] = $key['n']->copy();
|
1111 |
+
break;
|
1112 |
+
case isset($key['modulo']):
|
1113 |
+
$components['modulus'] = $key['modulo']->copy();
|
1114 |
+
break;
|
1115 |
+
case isset($key['modulus']):
|
1116 |
+
$components['modulus'] = $key['modulus']->copy();
|
1117 |
+
break;
|
1118 |
+
case isset($key[1]):
|
1119 |
+
$components['modulus'] = $key[1]->copy();
|
1120 |
+
}
|
1121 |
+
return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
|
1122 |
+
case self::PRIVATE_FORMAT_PKCS1:
|
1123 |
+
case self::PRIVATE_FORMAT_PKCS8:
|
1124 |
+
case self::PUBLIC_FORMAT_PKCS1:
|
1125 |
+
/* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
|
1126 |
+
"outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
|
1127 |
+
protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding
|
1128 |
+
two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here:
|
1129 |
+
|
1130 |
+
http://tools.ietf.org/html/rfc1421#section-4.6.1.1
|
1131 |
+
http://tools.ietf.org/html/rfc1421#section-4.6.1.3
|
1132 |
+
|
1133 |
+
DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
|
1134 |
+
DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
|
1135 |
+
function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
|
1136 |
+
own implementation. ie. the implementation *is* the standard and any bugs that may exist in that
|
1137 |
+
implementation are part of the standard, as well.
|
1138 |
+
|
1139 |
+
* OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */
|
1140 |
+
if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
|
1141 |
+
$iv = pack('H*', trim($matches[2]));
|
1142 |
+
$symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
|
1143 |
+
$symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8)));
|
1144 |
+
// remove the Proc-Type / DEK-Info sections as they're no longer needed
|
1145 |
+
$key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
|
1146 |
+
$ciphertext = $this->_extractBER($key);
|
1147 |
+
if ($ciphertext === false) {
|
1148 |
+
$ciphertext = $key;
|
1149 |
+
}
|
1150 |
+
switch ($matches[1]) {
|
1151 |
+
case 'AES-256-CBC':
|
1152 |
+
$crypto = new AES();
|
1153 |
+
break;
|
1154 |
+
case 'AES-128-CBC':
|
1155 |
+
$symkey = substr($symkey, 0, 16);
|
1156 |
+
$crypto = new AES();
|
1157 |
+
break;
|
1158 |
+
case 'DES-EDE3-CFB':
|
1159 |
+
$crypto = new TripleDES(Base::MODE_CFB);
|
1160 |
+
break;
|
1161 |
+
case 'DES-EDE3-CBC':
|
1162 |
+
$symkey = substr($symkey, 0, 24);
|
1163 |
+
$crypto = new TripleDES();
|
1164 |
+
break;
|
1165 |
+
case 'DES-CBC':
|
1166 |
+
$crypto = new DES();
|
1167 |
+
break;
|
1168 |
+
default:
|
1169 |
+
return false;
|
1170 |
+
}
|
1171 |
+
$crypto->setKey($symkey);
|
1172 |
+
$crypto->setIV($iv);
|
1173 |
+
$decoded = $crypto->decrypt($ciphertext);
|
1174 |
+
} else {
|
1175 |
+
$decoded = $this->_extractBER($key);
|
1176 |
+
}
|
1177 |
+
|
1178 |
+
if ($decoded !== false) {
|
1179 |
+
$key = $decoded;
|
1180 |
+
}
|
1181 |
+
|
1182 |
+
$components = array();
|
1183 |
+
|
1184 |
+
if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
|
1185 |
+
return false;
|
1186 |
+
}
|
1187 |
+
if ($this->_decodeLength($key) != strlen($key)) {
|
1188 |
+
return false;
|
1189 |
+
}
|
1190 |
+
|
1191 |
+
$tag = ord($this->_string_shift($key));
|
1192 |
+
/* intended for keys for which OpenSSL's asn1parse returns the following:
|
1193 |
+
|
1194 |
+
0:d=0 hl=4 l= 631 cons: SEQUENCE
|
1195 |
+
4:d=1 hl=2 l= 1 prim: INTEGER :00
|
1196 |
+
7:d=1 hl=2 l= 13 cons: SEQUENCE
|
1197 |
+
9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
|
1198 |
+
20:d=2 hl=2 l= 0 prim: NULL
|
1199 |
+
22:d=1 hl=4 l= 609 prim: OCTET STRING
|
1200 |
+
|
1201 |
+
ie. PKCS8 keys*/
|
1202 |
+
|
1203 |
+
if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
|
1204 |
+
$this->_string_shift($key, 3);
|
1205 |
+
$tag = self::ASN1_SEQUENCE;
|
1206 |
+
}
|
1207 |
+
|
1208 |
+
if ($tag == self::ASN1_SEQUENCE) {
|
1209 |
+
$temp = $this->_string_shift($key, $this->_decodeLength($key));
|
1210 |
+
if (ord($this->_string_shift($temp)) != self::ASN1_OBJECT) {
|
1211 |
+
return false;
|
1212 |
+
}
|
1213 |
+
$length = $this->_decodeLength($temp);
|
1214 |
+
switch ($this->_string_shift($temp, $length)) {
|
1215 |
+
case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
|
1216 |
+
break;
|
1217 |
+
case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
|
1218 |
+
/*
|
1219 |
+
PBEParameter ::= SEQUENCE {
|
1220 |
+
salt OCTET STRING (SIZE(8)),
|
1221 |
+
iterationCount INTEGER }
|
1222 |
+
*/
|
1223 |
+
if (ord($this->_string_shift($temp)) != self::ASN1_SEQUENCE) {
|
1224 |
+
return false;
|
1225 |
+
}
|
1226 |
+
if ($this->_decodeLength($temp) != strlen($temp)) {
|
1227 |
+
return false;
|
1228 |
+
}
|
1229 |
+
$this->_string_shift($temp); // assume it's an octet string
|
1230 |
+
$salt = $this->_string_shift($temp, $this->_decodeLength($temp));
|
1231 |
+
if (ord($this->_string_shift($temp)) != self::ASN1_INTEGER) {
|
1232 |
+
return false;
|
1233 |
+
}
|
1234 |
+
$this->_decodeLength($temp);
|
1235 |
+
list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
|
1236 |
+
$this->_string_shift($key); // assume it's an octet string
|
1237 |
+
$length = $this->_decodeLength($key);
|
1238 |
+
if (strlen($key) != $length) {
|
1239 |
+
return false;
|
1240 |
+
}
|
1241 |
+
|
1242 |
+
$crypto = new DES();
|
1243 |
+
$crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
|
1244 |
+
$key = $crypto->decrypt($key);
|
1245 |
+
if ($key === false) {
|
1246 |
+
return false;
|
1247 |
+
}
|
1248 |
+
return $this->_parseKey($key, self::PRIVATE_FORMAT_PKCS1);
|
1249 |
+
default:
|
1250 |
+
return false;
|
1251 |
+
}
|
1252 |
+
/* intended for keys for which OpenSSL's asn1parse returns the following:
|
1253 |
+
|
1254 |
+
0:d=0 hl=4 l= 290 cons: SEQUENCE
|
1255 |
+
4:d=1 hl=2 l= 13 cons: SEQUENCE
|
1256 |
+
6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
|
1257 |
+
17:d=2 hl=2 l= 0 prim: NULL
|
1258 |
+
19:d=1 hl=4 l= 271 prim: BIT STRING */
|
1259 |
+
$tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
|
1260 |
+
$this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
|
1261 |
+
// "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
|
1262 |
+
// unused bits in the final subsequent octet. The number shall be in the range zero to seven."
|
1263 |
+
// -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
|
1264 |
+
if ($tag == self::ASN1_BITSTRING) {
|
1265 |
+
$this->_string_shift($key);
|
1266 |
+
}
|
1267 |
+
if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
|
1268 |
+
return false;
|
1269 |
+
}
|
1270 |
+
if ($this->_decodeLength($key) != strlen($key)) {
|
1271 |
+
return false;
|
1272 |
+
}
|
1273 |
+
$tag = ord($this->_string_shift($key));
|
1274 |
+
}
|
1275 |
+
if ($tag != self::ASN1_INTEGER) {
|
1276 |
+
return false;
|
1277 |
+
}
|
1278 |
+
|
1279 |
+
$length = $this->_decodeLength($key);
|
1280 |
+
$temp = $this->_string_shift($key, $length);
|
1281 |
+
if (strlen($temp) != 1 || ord($temp) > 2) {
|
1282 |
+
$components['modulus'] = new BigInteger($temp, 256);
|
1283 |
+
$this->_string_shift($key); // skip over self::ASN1_INTEGER
|
1284 |
+
$length = $this->_decodeLength($key);
|
1285 |
+
$components[$type == self::PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
|
1286 |
+
|
1287 |
+
return $components;
|
1288 |
+
}
|
1289 |
+
if (ord($this->_string_shift($key)) != self::ASN1_INTEGER) {
|
1290 |
+
return false;
|
1291 |
+
}
|
1292 |
+
$length = $this->_decodeLength($key);
|
1293 |
+
$components['modulus'] = new BigInteger($this->_string_shift($key, $length), 256);
|
1294 |
+
$this->_string_shift($key);
|
1295 |
+
$length = $this->_decodeLength($key);
|
1296 |
+
$components['publicExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
|
1297 |
+
$this->_string_shift($key);
|
1298 |
+
$length = $this->_decodeLength($key);
|
1299 |
+
$components['privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
|
1300 |
+
$this->_string_shift($key);
|
1301 |
+
$length = $this->_decodeLength($key);
|
1302 |
+
$components['primes'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256));
|
1303 |
+
$this->_string_shift($key);
|
1304 |
+
$length = $this->_decodeLength($key);
|
1305 |
+
$components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256);
|
1306 |
+
$this->_string_shift($key);
|
1307 |
+
$length = $this->_decodeLength($key);
|
1308 |
+
$components['exponents'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256));
|
1309 |
+
$this->_string_shift($key);
|
1310 |
+
$length = $this->_decodeLength($key);
|
1311 |
+
$components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256);
|
1312 |
+
$this->_string_shift($key);
|
1313 |
+
$length = $this->_decodeLength($key);
|
1314 |
+
$components['coefficients'] = array(2 => new BigInteger($this->_string_shift($key, $length), 256));
|
1315 |
+
|
1316 |
+
if (!empty($key)) {
|
1317 |
+
if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
|
1318 |
+
return false;
|
1319 |
+
}
|
1320 |
+
$this->_decodeLength($key);
|
1321 |
+
while (!empty($key)) {
|
1322 |
+
if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
|
1323 |
+
return false;
|
1324 |
+
}
|
1325 |
+
$this->_decodeLength($key);
|
1326 |
+
$key = substr($key, 1);
|
1327 |
+
$length = $this->_decodeLength($key);
|
1328 |
+
$components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256);
|
1329 |
+
$this->_string_shift($key);
|
1330 |
+
$length = $this->_decodeLength($key);
|
1331 |
+
$components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256);
|
1332 |
+
$this->_string_shift($key);
|
1333 |
+
$length = $this->_decodeLength($key);
|
1334 |
+
$components['coefficients'][] = new BigInteger($this->_string_shift($key, $length), 256);
|
1335 |
+
}
|
1336 |
+
}
|
1337 |
+
|
1338 |
+
return $components;
|
1339 |
+
case self::PUBLIC_FORMAT_OPENSSH:
|
1340 |
+
$parts = explode(' ', $key, 3);
|
1341 |
+
|
1342 |
+
$key = isset($parts[1]) ? base64_decode($parts[1]) : false;
|
1343 |
+
if ($key === false) {
|
1344 |
+
return false;
|
1345 |
+
}
|
1346 |
+
|
1347 |
+
$comment = isset($parts[2]) ? $parts[2] : false;
|
1348 |
+
|
1349 |
+
$cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
|
1350 |
+
|
1351 |
+
if (strlen($key) <= 4) {
|
1352 |
+
return false;
|
1353 |
+
}
|
1354 |
+
extract(unpack('Nlength', $this->_string_shift($key, 4)));
|
1355 |
+
$publicExponent = new BigInteger($this->_string_shift($key, $length), -256);
|
1356 |
+
if (strlen($key) <= 4) {
|
1357 |
+
return false;
|
1358 |
+
}
|
1359 |
+
extract(unpack('Nlength', $this->_string_shift($key, 4)));
|
1360 |
+
$modulus = new BigInteger($this->_string_shift($key, $length), -256);
|
1361 |
+
|
1362 |
+
if ($cleanup && strlen($key)) {
|
1363 |
+
if (strlen($key) <= 4) {
|
1364 |
+
return false;
|
1365 |
+
}
|
1366 |
+
extract(unpack('Nlength', $this->_string_shift($key, 4)));
|
1367 |
+
$realModulus = new BigInteger($this->_string_shift($key, $length), -256);
|
1368 |
+
return strlen($key) ? false : array(
|
1369 |
+
'modulus' => $realModulus,
|
1370 |
+
'publicExponent' => $modulus,
|
1371 |
+
'comment' => $comment
|
1372 |
+
);
|
1373 |
+
} else {
|
1374 |
+
return strlen($key) ? false : array(
|
1375 |
+
'modulus' => $modulus,
|
1376 |
+
'publicExponent' => $publicExponent,
|
1377 |
+
'comment' => $comment
|
1378 |
+
);
|
1379 |
+
}
|
1380 |
+
// http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
|
1381 |
+
// http://en.wikipedia.org/wiki/XML_Signature
|
1382 |
+
case self::PRIVATE_FORMAT_XML:
|
1383 |
+
case self::PUBLIC_FORMAT_XML:
|
1384 |
+
$this->components = array();
|
1385 |
+
|
1386 |
+
$xml = xml_parser_create('UTF-8');
|
1387 |
+
xml_set_object($xml, $this);
|
1388 |
+
xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler');
|
1389 |
+
xml_set_character_data_handler($xml, '_data_handler');
|
1390 |
+
// add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
|
1391 |
+
if (!xml_parse($xml, '<xml>' . $key . '</xml>')) {
|
1392 |
+
xml_parser_free($xml);
|
1393 |
+
unset($xml);
|
1394 |
+
return false;
|
1395 |
+
}
|
1396 |
+
|
1397 |
+
xml_parser_free($xml);
|
1398 |
+
unset($xml);
|
1399 |
+
|
1400 |
+
return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
|
1401 |
+
// from PuTTY's SSHPUBK.C
|
1402 |
+
case self::PRIVATE_FORMAT_PUTTY:
|
1403 |
+
$components = array();
|
1404 |
+
$key = preg_split('#\r\n|\r|\n#', $key);
|
1405 |
+
$type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
|
1406 |
+
if ($type != 'ssh-rsa') {
|
1407 |
+
return false;
|
1408 |
+
}
|
1409 |
+
$encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
|
1410 |
+
$comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2]));
|
1411 |
+
|
1412 |
+
$publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
|
1413 |
+
$public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
|
1414 |
+
$public = substr($public, 11);
|
1415 |
+
extract(unpack('Nlength', $this->_string_shift($public, 4)));
|
1416 |
+
$components['publicExponent'] = new BigInteger($this->_string_shift($public, $length), -256);
|
1417 |
+
extract(unpack('Nlength', $this->_string_shift($public, 4)));
|
1418 |
+
$components['modulus'] = new BigInteger($this->_string_shift($public, $length), -256);
|
1419 |
+
|
1420 |
+
$privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4]));
|
1421 |
+
$private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength))));
|
1422 |
+
|
1423 |
+
switch ($encryption) {
|
1424 |
+
case 'aes256-cbc':
|
1425 |
+
$symkey = '';
|
1426 |
+
$sequence = 0;
|
1427 |
+
while (strlen($symkey) < 32) {
|
1428 |
+
$temp = pack('Na*', $sequence++, $this->password);
|
1429 |
+
$symkey.= pack('H*', sha1($temp));
|
1430 |
+
}
|
1431 |
+
$symkey = substr($symkey, 0, 32);
|
1432 |
+
$crypto = new AES();
|
1433 |
+
}
|
1434 |
+
|
1435 |
+
if ($encryption != 'none') {
|
1436 |
+
$crypto->setKey($symkey);
|
1437 |
+
$crypto->disablePadding();
|
1438 |
+
$private = $crypto->decrypt($private);
|
1439 |
+
if ($private === false) {
|
1440 |
+
return false;
|
1441 |
+
}
|
1442 |
+
}
|
1443 |
+
|
1444 |
+
extract(unpack('Nlength', $this->_string_shift($private, 4)));
|
1445 |
+
if (strlen($private) < $length) {
|
1446 |
+
return false;
|
1447 |
+
}
|
1448 |
+
$components['privateExponent'] = new BigInteger($this->_string_shift($private, $length), -256);
|
1449 |
+
extract(unpack('Nlength', $this->_string_shift($private, 4)));
|
1450 |
+
if (strlen($private) < $length) {
|
1451 |
+
return false;
|
1452 |
+
}
|
1453 |
+
$components['primes'] = array(1 => new BigInteger($this->_string_shift($private, $length), -256));
|
1454 |
+
extract(unpack('Nlength', $this->_string_shift($private, 4)));
|
1455 |
+
if (strlen($private) < $length) {
|
1456 |
+
return false;
|
1457 |
+
}
|
1458 |
+
$components['primes'][] = new BigInteger($this->_string_shift($private, $length), -256);
|
1459 |
+
|
1460 |
+
$temp = $components['primes'][1]->subtract($this->one);
|
1461 |
+
$components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
|
1462 |
+
$temp = $components['primes'][2]->subtract($this->one);
|
1463 |
+
$components['exponents'][] = $components['publicExponent']->modInverse($temp);
|
1464 |
+
|
1465 |
+
extract(unpack('Nlength', $this->_string_shift($private, 4)));
|
1466 |
+
if (strlen($private) < $length) {
|
1467 |
+
return false;
|
1468 |
+
}
|
1469 |
+
$components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256));
|
1470 |
+
|
1471 |
+
return $components;
|
1472 |
+
case self::PRIVATE_FORMAT_OPENSSH:
|
1473 |
+
$components = array();
|
1474 |
+
$decoded = $this->_extractBER($key);
|
1475 |
+
$magic = $this->_string_shift($decoded, 15);
|
1476 |
+
if ($magic !== "openssh-key-v1\0") {
|
1477 |
+
return false;
|
1478 |
+
}
|
1479 |
+
$options = $this->_string_shift($decoded, 24);
|
1480 |
+
// \0\0\0\4none = ciphername
|
1481 |
+
// \0\0\0\4none = kdfname
|
1482 |
+
// \0\0\0\0 = kdfoptions
|
1483 |
+
// \0\0\0\1 = numkeys
|
1484 |
+
if ($options != "\0\0\0\4none\0\0\0\4none\0\0\0\0\0\0\0\1") {
|
1485 |
+
return false;
|
1486 |
+
}
|
1487 |
+
extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
|
1488 |
+
if (strlen($decoded) < $length) {
|
1489 |
+
return false;
|
1490 |
+
}
|
1491 |
+
$publicKey = $this->_string_shift($decoded, $length);
|
1492 |
+
extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
|
1493 |
+
if (strlen($decoded) < $length) {
|
1494 |
+
return false;
|
1495 |
+
}
|
1496 |
+
$paddedKey = $this->_string_shift($decoded, $length);
|
1497 |
+
|
1498 |
+
if ($this->_string_shift($publicKey, 11) !== "\0\0\0\7ssh-rsa") {
|
1499 |
+
return false;
|
1500 |
+
}
|
1501 |
+
|
1502 |
+
$checkint1 = $this->_string_shift($paddedKey, 4);
|
1503 |
+
$checkint2 = $this->_string_shift($paddedKey, 4);
|
1504 |
+
if (strlen($checkint1) != 4 || $checkint1 !== $checkint2) {
|
1505 |
+
return false;
|
1506 |
+
}
|
1507 |
+
|
1508 |
+
if ($this->_string_shift($paddedKey, 11) !== "\0\0\0\7ssh-rsa") {
|
1509 |
+
return false;
|
1510 |
+
}
|
1511 |
+
|
1512 |
+
$values = array(
|
1513 |
+
&$components['modulus'],
|
1514 |
+
&$components['publicExponent'],
|
1515 |
+
&$components['privateExponent'],
|
1516 |
+
&$components['coefficients'][2],
|
1517 |
+
&$components['primes'][1],
|
1518 |
+
&$components['primes'][2]
|
1519 |
+
);
|
1520 |
+
|
1521 |
+
foreach ($values as &$value) {
|
1522 |
+
extract(unpack('Nlength', $this->_string_shift($paddedKey, 4)));
|
1523 |
+
if (strlen($paddedKey) < $length) {
|
1524 |
+
return false;
|
1525 |
+
}
|
1526 |
+
$value = new BigInteger($this->_string_shift($paddedKey, $length), -256);
|
1527 |
+
}
|
1528 |
+
|
1529 |
+
extract(unpack('Nlength', $this->_string_shift($paddedKey, 4)));
|
1530 |
+
if (strlen($paddedKey) < $length) {
|
1531 |
+
return false;
|
1532 |
+
}
|
1533 |
+
$components['comment'] = $this->_string_shift($decoded, $length);
|
1534 |
+
|
1535 |
+
$temp = $components['primes'][1]->subtract($this->one);
|
1536 |
+
$components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
|
1537 |
+
$temp = $components['primes'][2]->subtract($this->one);
|
1538 |
+
$components['exponents'][] = $components['publicExponent']->modInverse($temp);
|
1539 |
+
|
1540 |
+
return $components;
|
1541 |
+
}
|
1542 |
+
}
|
1543 |
+
|
1544 |
+
/**
|
1545 |
+
* Returns the key size
|
1546 |
+
*
|
1547 |
+
* More specifically, this returns the size of the modulo in bits.
|
1548 |
+
*
|
1549 |
+
* @access public
|
1550 |
+
* @return int
|
1551 |
+
*/
|
1552 |
+
function getSize()
|
1553 |
+
{
|
1554 |
+
return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits());
|
1555 |
+
}
|
1556 |
+
|
1557 |
+
/**
|
1558 |
+
* Start Element Handler
|
1559 |
+
*
|
1560 |
+
* Called by xml_set_element_handler()
|
1561 |
+
*
|
1562 |
+
* @access private
|
1563 |
+
* @param resource $parser
|
1564 |
+
* @param string $name
|
1565 |
+
* @param array $attribs
|
1566 |
+
*/
|
1567 |
+
function _start_element_handler($parser, $name, $attribs)
|
1568 |
+
{
|
1569 |
+
//$name = strtoupper($name);
|
1570 |
+
switch ($name) {
|
1571 |
+
case 'MODULUS':
|
1572 |
+
$this->current = &$this->components['modulus'];
|
1573 |
+
break;
|
1574 |
+
case 'EXPONENT':
|
1575 |
+
$this->current = &$this->components['publicExponent'];
|
1576 |
+
break;
|
1577 |
+
case 'P':
|
1578 |
+
$this->current = &$this->components['primes'][1];
|
1579 |
+
break;
|
1580 |
+
case 'Q':
|
1581 |
+
$this->current = &$this->components['primes'][2];
|
1582 |
+
break;
|
1583 |
+
case 'DP':
|
1584 |
+
$this->current = &$this->components['exponents'][1];
|
1585 |
+
break;
|
1586 |
+
case 'DQ':
|
1587 |
+
$this->current = &$this->components['exponents'][2];
|
1588 |
+
break;
|
1589 |
+
case 'INVERSEQ':
|
1590 |
+
$this->current = &$this->components['coefficients'][2];
|
1591 |
+
break;
|
1592 |
+
case 'D':
|
1593 |
+
$this->current = &$this->components['privateExponent'];
|
1594 |
+
}
|
1595 |
+
$this->current = '';
|
1596 |
+
}
|
1597 |
+
|
1598 |
+
/**
|
1599 |
+
* Stop Element Handler
|
1600 |
+
*
|
1601 |
+
* Called by xml_set_element_handler()
|
1602 |
+
*
|
1603 |
+
* @access private
|
1604 |
+
* @param resource $parser
|
1605 |
+
* @param string $name
|
1606 |
+
*/
|
1607 |
+
function _stop_element_handler($parser, $name)
|
1608 |
+
{
|
1609 |
+
if (isset($this->current)) {
|
1610 |
+
$this->current = new BigInteger(base64_decode($this->current), 256);
|
1611 |
+
unset($this->current);
|
1612 |
+
}
|
1613 |
+
}
|
1614 |
+
|
1615 |
+
/**
|
1616 |
+
* Data Handler
|
1617 |
+
*
|
1618 |
+
* Called by xml_set_character_data_handler()
|
1619 |
+
*
|
1620 |
+
* @access private
|
1621 |
+
* @param resource $parser
|
1622 |
+
* @param string $data
|
1623 |
+
*/
|
1624 |
+
function _data_handler($parser, $data)
|
1625 |
+
{
|
1626 |
+
if (!isset($this->current) || is_object($this->current)) {
|
1627 |
+
return;
|
1628 |
+
}
|
1629 |
+
$this->current.= trim($data);
|
1630 |
+
}
|
1631 |
+
|
1632 |
+
/**
|
1633 |
+
* Loads a public or private key
|
1634 |
+
*
|
1635 |
+
* Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
|
1636 |
+
*
|
1637 |
+
* @access public
|
1638 |
+
* @param string|RSA|array $key
|
1639 |
+
* @param bool|int $type optional
|
1640 |
+
* @return bool
|
1641 |
+
*/
|
1642 |
+
function loadKey($key, $type = false)
|
1643 |
+
{
|
1644 |
+
if ($key instanceof RSA) {
|
1645 |
+
$this->privateKeyFormat = $key->privateKeyFormat;
|
1646 |
+
$this->publicKeyFormat = $key->publicKeyFormat;
|
1647 |
+
$this->k = $key->k;
|
1648 |
+
$this->hLen = $key->hLen;
|
1649 |
+
$this->sLen = $key->sLen;
|
1650 |
+
$this->mgfHLen = $key->mgfHLen;
|
1651 |
+
$this->encryptionMode = $key->encryptionMode;
|
1652 |
+
$this->signatureMode = $key->signatureMode;
|
1653 |
+
$this->password = $key->password;
|
1654 |
+
$this->configFile = $key->configFile;
|
1655 |
+
$this->comment = $key->comment;
|
1656 |
+
|
1657 |
+
if (is_object($key->hash)) {
|
1658 |
+
$this->hash = new Hash($key->hash->getHash());
|
1659 |
+
}
|
1660 |
+
if (is_object($key->mgfHash)) {
|
1661 |
+
$this->mgfHash = new Hash($key->mgfHash->getHash());
|
1662 |
+
}
|
1663 |
+
|
1664 |
+
if (is_object($key->modulus)) {
|
1665 |
+
$this->modulus = $key->modulus->copy();
|
1666 |
+
}
|
1667 |
+
if (is_object($key->exponent)) {
|
1668 |
+
$this->exponent = $key->exponent->copy();
|
1669 |
+
}
|
1670 |
+
if (is_object($key->publicExponent)) {
|
1671 |
+
$this->publicExponent = $key->publicExponent->copy();
|
1672 |
+
}
|
1673 |
+
|
1674 |
+
$this->primes = array();
|
1675 |
+
$this->exponents = array();
|
1676 |
+
$this->coefficients = array();
|
1677 |
+
|
1678 |
+
foreach ($this->primes as $prime) {
|
1679 |
+
$this->primes[] = $prime->copy();
|
1680 |
+
}
|
1681 |
+
foreach ($this->exponents as $exponent) {
|
1682 |
+
$this->exponents[] = $exponent->copy();
|
1683 |
+
}
|
1684 |
+
foreach ($this->coefficients as $coefficient) {
|
1685 |
+
$this->coefficients[] = $coefficient->copy();
|
1686 |
+
}
|
1687 |
+
|
1688 |
+
return true;
|
1689 |
+
}
|
1690 |
+
|
1691 |
+
if ($type === false) {
|
1692 |
+
$types = array(
|
1693 |
+
self::PUBLIC_FORMAT_RAW,
|
1694 |
+
self::PRIVATE_FORMAT_PKCS1,
|
1695 |
+
self::PRIVATE_FORMAT_XML,
|
1696 |
+
self::PRIVATE_FORMAT_PUTTY,
|
1697 |
+
self::PUBLIC_FORMAT_OPENSSH,
|
1698 |
+
self::PRIVATE_FORMAT_OPENSSH
|
1699 |
+
);
|
1700 |
+
foreach ($types as $type) {
|
1701 |
+
$components = $this->_parseKey($key, $type);
|
1702 |
+
if ($components !== false) {
|
1703 |
+
break;
|
1704 |
+
}
|
1705 |
+
}
|
1706 |
+
} else {
|
1707 |
+
$components = $this->_parseKey($key, $type);
|
1708 |
+
}
|
1709 |
+
|
1710 |
+
if ($components === false) {
|
1711 |
+
$this->comment = null;
|
1712 |
+
$this->modulus = null;
|
1713 |
+
$this->k = null;
|
1714 |
+
$this->exponent = null;
|
1715 |
+
$this->primes = null;
|
1716 |
+
$this->exponents = null;
|
1717 |
+
$this->coefficients = null;
|
1718 |
+
$this->publicExponent = null;
|
1719 |
+
|
1720 |
+
return false;
|
1721 |
+
}
|
1722 |
+
|
1723 |
+
if (isset($components['comment']) && $components['comment'] !== false) {
|
1724 |
+
$this->comment = $components['comment'];
|
1725 |
+
}
|
1726 |
+
$this->modulus = $components['modulus'];
|
1727 |
+
$this->k = strlen($this->modulus->toBytes());
|
1728 |
+
$this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
|
1729 |
+
if (isset($components['primes'])) {
|
1730 |
+
$this->primes = $components['primes'];
|
1731 |
+
$this->exponents = $components['exponents'];
|
1732 |
+
$this->coefficients = $components['coefficients'];
|
1733 |
+
$this->publicExponent = $components['publicExponent'];
|
1734 |
+
} else {
|
1735 |
+
$this->primes = array();
|
1736 |
+
$this->exponents = array();
|
1737 |
+
$this->coefficients = array();
|
1738 |
+
$this->publicExponent = false;
|
1739 |
+
}
|
1740 |
+
|
1741 |
+
switch ($type) {
|
1742 |
+
case self::PUBLIC_FORMAT_OPENSSH:
|
1743 |
+
case self::PUBLIC_FORMAT_RAW:
|
1744 |
+
$this->setPublicKey();
|
1745 |
+
break;
|
1746 |
+
case self::PRIVATE_FORMAT_PKCS1:
|
1747 |
+
switch (true) {
|
1748 |
+
case strpos($key, '-BEGIN PUBLIC KEY-') !== false:
|
1749 |
+
case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false:
|
1750 |
+
$this->setPublicKey();
|
1751 |
+
}
|
1752 |
+
}
|
1753 |
+
|
1754 |
+
return true;
|
1755 |
+
}
|
1756 |
+
|
1757 |
+
/**
|
1758 |
+
* Sets the password
|
1759 |
+
*
|
1760 |
+
* Private keys can be encrypted with a password. To unset the password, pass in the empty string or false.
|
1761 |
+
* Or rather, pass in $password such that empty($password) && !is_string($password) is true.
|
1762 |
+
*
|
1763 |
+
* @see self::createKey()
|
1764 |
+
* @see self::loadKey()
|
1765 |
+
* @access public
|
1766 |
+
* @param string $password
|
1767 |
+
*/
|
1768 |
+
function setPassword($password = false)
|
1769 |
+
{
|
1770 |
+
$this->password = $password;
|
1771 |
+
}
|
1772 |
+
|
1773 |
+
/**
|
1774 |
+
* Defines the public key
|
1775 |
+
*
|
1776 |
+
* Some private key formats define the public exponent and some don't. Those that don't define it are problematic when
|
1777 |
+
* used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a
|
1778 |
+
* message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys
|
1779 |
+
* and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public
|
1780 |
+
* exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used
|
1781 |
+
* is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being
|
1782 |
+
* public.
|
1783 |
+
*
|
1784 |
+
* Do note that when a new key is loaded the index will be cleared.
|
1785 |
+
*
|
1786 |
+
* Returns true on success, false on failure
|
1787 |
+
*
|
1788 |
+
* @see self::getPublicKey()
|
1789 |
+
* @access public
|
1790 |
+
* @param string $key optional
|
1791 |
+
* @param int $type optional
|
1792 |
+
* @return bool
|
1793 |
+
*/
|
1794 |
+
function setPublicKey($key = false, $type = false)
|
1795 |
+
{
|
1796 |
+
// if a public key has already been loaded return false
|
1797 |
+
if (!empty($this->publicExponent)) {
|
1798 |
+
return false;
|
1799 |
+
}
|
1800 |
+
|
1801 |
+
if ($key === false && !empty($this->modulus)) {
|
1802 |
+
$this->publicExponent = $this->exponent;
|
1803 |
+
return true;
|
1804 |
+
}
|
1805 |
+
|
1806 |
+
if ($type === false) {
|
1807 |
+
$types = array(
|
1808 |
+
self::PUBLIC_FORMAT_RAW,
|
1809 |
+
self::PUBLIC_FORMAT_PKCS1,
|
1810 |
+
self::PUBLIC_FORMAT_XML,
|
1811 |
+
self::PUBLIC_FORMAT_OPENSSH
|
1812 |
+
);
|
1813 |
+
foreach ($types as $type) {
|
1814 |
+
$components = $this->_parseKey($key, $type);
|
1815 |
+
if ($components !== false) {
|
1816 |
+
break;
|
1817 |
+
}
|
1818 |
+
}
|
1819 |
+
} else {
|
1820 |
+
$components = $this->_parseKey($key, $type);
|
1821 |
+
}
|
1822 |
+
|
1823 |
+
if ($components === false) {
|
1824 |
+
return false;
|
1825 |
+
}
|
1826 |
+
|
1827 |
+
if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
|
1828 |
+
$this->modulus = $components['modulus'];
|
1829 |
+
$this->exponent = $this->publicExponent = $components['publicExponent'];
|
1830 |
+
return true;
|
1831 |
+
}
|
1832 |
+
|
1833 |
+
$this->publicExponent = $components['publicExponent'];
|
1834 |
+
|
1835 |
+
return true;
|
1836 |
+
}
|
1837 |
+
|
1838 |
+
/**
|
1839 |
+
* Defines the private key
|
1840 |
+
*
|
1841 |
+
* If phpseclib guessed a private key was a public key and loaded it as such it might be desirable to force
|
1842 |
+
* phpseclib to treat the key as a private key. This function will do that.
|
1843 |
+
*
|
1844 |
+
* Do note that when a new key is loaded the index will be cleared.
|
1845 |
+
*
|
1846 |
+
* Returns true on success, false on failure
|
1847 |
+
*
|
1848 |
+
* @see self::getPublicKey()
|
1849 |
+
* @access public
|
1850 |
+
* @param string $key optional
|
1851 |
+
* @param int $type optional
|
1852 |
+
* @return bool
|
1853 |
+
*/
|
1854 |
+
function setPrivateKey($key = false, $type = false)
|
1855 |
+
{
|
1856 |
+
if ($key === false && !empty($this->publicExponent)) {
|
1857 |
+
$this->publicExponent = false;
|
1858 |
+
return true;
|
1859 |
+
}
|
1860 |
+
|
1861 |
+
$rsa = new RSA();
|
1862 |
+
if (!$rsa->loadKey($key, $type)) {
|
1863 |
+
return false;
|
1864 |
+
}
|
1865 |
+
$rsa->publicExponent = false;
|
1866 |
+
|
1867 |
+
// don't overwrite the old key if the new key is invalid
|
1868 |
+
$this->loadKey($rsa);
|
1869 |
+
return true;
|
1870 |
+
}
|
1871 |
+
|
1872 |
+
/**
|
1873 |
+
* Returns the public key
|
1874 |
+
*
|
1875 |
+
* The public key is only returned under two circumstances - if the private key had the public key embedded within it
|
1876 |
+
* or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this
|
1877 |
+
* function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
|
1878 |
+
*
|
1879 |
+
* @see self::getPublicKey()
|
1880 |
+
* @access public
|
1881 |
+
* @param string $key
|
1882 |
+
* @param int $type optional
|
1883 |
+
*/
|
1884 |
+
function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8)
|
1885 |
+
{
|
1886 |
+
if (empty($this->modulus) || empty($this->publicExponent)) {
|
1887 |
+
return false;
|
1888 |
+
}
|
1889 |
+
|
1890 |
+
$oldFormat = $this->publicKeyFormat;
|
1891 |
+
$this->publicKeyFormat = $type;
|
1892 |
+
$temp = $this->_convertPublicKey($this->modulus, $this->publicExponent);
|
1893 |
+
$this->publicKeyFormat = $oldFormat;
|
1894 |
+
return $temp;
|
1895 |
+
}
|
1896 |
+
|
1897 |
+
/**
|
1898 |
+
* Returns the public key's fingerprint
|
1899 |
+
*
|
1900 |
+
* The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is
|
1901 |
+
* no public key currently loaded, false is returned.
|
1902 |
+
* Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716)
|
1903 |
+
*
|
1904 |
+
* @access public
|
1905 |
+
* @param string $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned
|
1906 |
+
* for invalid values.
|
1907 |
+
* @return mixed
|
1908 |
+
*/
|
1909 |
+
function getPublicKeyFingerprint($algorithm = 'md5')
|
1910 |
+
{
|
1911 |
+
if (empty($this->modulus) || empty($this->publicExponent)) {
|
1912 |
+
return false;
|
1913 |
+
}
|
1914 |
+
|
1915 |
+
$modulus = $this->modulus->toBytes(true);
|
1916 |
+
$publicExponent = $this->publicExponent->toBytes(true);
|
1917 |
+
|
1918 |
+
$RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
|
1919 |
+
|
1920 |
+
switch ($algorithm) {
|
1921 |
+
case 'sha256':
|
1922 |
+
$hash = new Hash('sha256');
|
1923 |
+
$base = base64_encode($hash->hash($RSAPublicKey));
|
1924 |
+
return substr($base, 0, strlen($base) - 1);
|
1925 |
+
case 'md5':
|
1926 |
+
return substr(chunk_split(md5($RSAPublicKey), 2, ':'), 0, -1);
|
1927 |
+
default:
|
1928 |
+
return false;
|
1929 |
+
}
|
1930 |
+
}
|
1931 |
+
|
1932 |
+
/**
|
1933 |
+
* Returns the private key
|
1934 |
+
*
|
1935 |
+
* The private key is only returned if the currently loaded key contains the constituent prime numbers.
|
1936 |
+
*
|
1937 |
+
* @see self::getPublicKey()
|
1938 |
+
* @access public
|
1939 |
+
* @param string $key
|
1940 |
+
* @param int $type optional
|
1941 |
+
* @return mixed
|
1942 |
+
*/
|
1943 |
+
function getPrivateKey($type = self::PUBLIC_FORMAT_PKCS1)
|
1944 |
+
{
|
1945 |
+
if (empty($this->primes)) {
|
1946 |
+
return false;
|
1947 |
+
}
|
1948 |
+
|
1949 |
+
$oldFormat = $this->privateKeyFormat;
|
1950 |
+
$this->privateKeyFormat = $type;
|
1951 |
+
$temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients);
|
1952 |
+
$this->privateKeyFormat = $oldFormat;
|
1953 |
+
return $temp;
|
1954 |
+
}
|
1955 |
+
|
1956 |
+
/**
|
1957 |
+
* Returns a minimalistic private key
|
1958 |
+
*
|
1959 |
+
* Returns the private key without the prime number constituants. Structurally identical to a public key that
|
1960 |
+
* hasn't been set as the public key
|
1961 |
+
*
|
1962 |
+
* @see self::getPrivateKey()
|
1963 |
+
* @access private
|
1964 |
+
* @param string $key
|
1965 |
+
* @param int $type optional
|
1966 |
+
*/
|
1967 |
+
function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8)
|
1968 |
+
{
|
1969 |
+
if (empty($this->modulus) || empty($this->exponent)) {
|
1970 |
+
return false;
|
1971 |
+
}
|
1972 |
+
|
1973 |
+
$oldFormat = $this->publicKeyFormat;
|
1974 |
+
$this->publicKeyFormat = $mode;
|
1975 |
+
$temp = $this->_convertPublicKey($this->modulus, $this->exponent);
|
1976 |
+
$this->publicKeyFormat = $oldFormat;
|
1977 |
+
return $temp;
|
1978 |
+
}
|
1979 |
+
|
1980 |
+
/**
|
1981 |
+
* __toString() magic method
|
1982 |
+
*
|
1983 |
+
* @access public
|
1984 |
+
* @return string
|
1985 |
+
*/
|
1986 |
+
function __toString()
|
1987 |
+
{
|
1988 |
+
$key = $this->getPrivateKey($this->privateKeyFormat);
|
1989 |
+
if ($key !== false) {
|
1990 |
+
return $key;
|
1991 |
+
}
|
1992 |
+
$key = $this->_getPrivatePublicKey($this->publicKeyFormat);
|
1993 |
+
return $key !== false ? $key : '';
|
1994 |
+
}
|
1995 |
+
|
1996 |
+
/**
|
1997 |
+
* __clone() magic method
|
1998 |
+
*
|
1999 |
+
* @access public
|
2000 |
+
* @return Crypt_RSA
|
2001 |
+
*/
|
2002 |
+
function __clone()
|
2003 |
+
{
|
2004 |
+
$key = new RSA();
|
2005 |
+
$key->loadKey($this);
|
2006 |
+
return $key;
|
2007 |
+
}
|
2008 |
+
|
2009 |
+
/**
|
2010 |
+
* Generates the smallest and largest numbers requiring $bits bits
|
2011 |
+
*
|
2012 |
+
* @access private
|
2013 |
+
* @param int $bits
|
2014 |
+
* @return array
|
2015 |
+
*/
|
2016 |
+
function _generateMinMax($bits)
|
2017 |
+
{
|
2018 |
+
$bytes = $bits >> 3;
|
2019 |
+
$min = str_repeat(chr(0), $bytes);
|
2020 |
+
$max = str_repeat(chr(0xFF), $bytes);
|
2021 |
+
$msb = $bits & 7;
|
2022 |
+
if ($msb) {
|
2023 |
+
$min = chr(1 << ($msb - 1)) . $min;
|
2024 |
+
$max = chr((1 << $msb) - 1) . $max;
|
2025 |
+
} else {
|
2026 |
+
$min[0] = chr(0x80);
|
2027 |
+
}
|
2028 |
+
|
2029 |
+
return array(
|
2030 |
+
'min' => new BigInteger($min, 256),
|
2031 |
+
'max' => new BigInteger($max, 256)
|
2032 |
+
);
|
2033 |
+
}
|
2034 |
+
|
2035 |
+
/**
|
2036 |
+
* DER-decode the length
|
2037 |
+
*
|
2038 |
+
* DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
|
2039 |
+
* {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
|
2040 |
+
*
|
2041 |
+
* @access private
|
2042 |
+
* @param string $string
|
2043 |
+
* @return int
|
2044 |
+
*/
|
2045 |
+
function _decodeLength(&$string)
|
2046 |
+
{
|
2047 |
+
$length = ord($this->_string_shift($string));
|
2048 |
+
if ($length & 0x80) { // definite length, long form
|
2049 |
+
$length&= 0x7F;
|
2050 |
+
$temp = $this->_string_shift($string, $length);
|
2051 |
+
list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
|
2052 |
+
}
|
2053 |
+
return $length;
|
2054 |
+
}
|
2055 |
+
|
2056 |
+
/**
|
2057 |
+
* DER-encode the length
|
2058 |
+
*
|
2059 |
+
* DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
|
2060 |
+
* {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
|
2061 |
+
*
|
2062 |
+
* @access private
|
2063 |
+
* @param int $length
|
2064 |
+
* @return string
|
2065 |
+
*/
|
2066 |
+
function _encodeLength($length)
|
2067 |
+
{
|
2068 |
+
if ($length <= 0x7F) {
|
2069 |
+
return chr($length);
|
2070 |
+
}
|
2071 |
+
|
2072 |
+
$temp = ltrim(pack('N', $length), chr(0));
|
2073 |
+
return pack('Ca*', 0x80 | strlen($temp), $temp);
|
2074 |
+
}
|
2075 |
+
|
2076 |
+
/**
|
2077 |
+
* String Shift
|
2078 |
+
*
|
2079 |
+
* Inspired by array_shift
|
2080 |
+
*
|
2081 |
+
* @param string $string
|
2082 |
+
* @param int $index
|
2083 |
+
* @return string
|
2084 |
+
* @access private
|
2085 |
+
*/
|
2086 |
+
function _string_shift(&$string, $index = 1)
|
2087 |
+
{
|
2088 |
+
$substr = substr($string, 0, $index);
|
2089 |
+
$string = substr($string, $index);
|
2090 |
+
return $substr;
|
2091 |
+
}
|
2092 |
+
|
2093 |
+
/**
|
2094 |
+
* Determines the private key format
|
2095 |
+
*
|
2096 |
+
* @see self::createKey()
|
2097 |
+
* @access public
|
2098 |
+
* @param int $format
|
2099 |
+
*/
|
2100 |
+
function setPrivateKeyFormat($format)
|
2101 |
+
{
|
2102 |
+
$this->privateKeyFormat = $format;
|
2103 |
+
}
|
2104 |
+
|
2105 |
+
/**
|
2106 |
+
* Determines the public key format
|
2107 |
+
*
|
2108 |
+
* @see self::createKey()
|
2109 |
+
* @access public
|
2110 |
+
* @param int $format
|
2111 |
+
*/
|
2112 |
+
function setPublicKeyFormat($format)
|
2113 |
+
{
|
2114 |
+
$this->publicKeyFormat = $format;
|
2115 |
+
}
|
2116 |
+
|
2117 |
+
/**
|
2118 |
+
* Determines which hashing function should be used
|
2119 |
+
*
|
2120 |
+
* Used with signature production / verification and (if the encryption mode is self::ENCRYPTION_OAEP) encryption and
|
2121 |
+
* decryption. If $hash isn't supported, sha1 is used.
|
2122 |
+
*
|
2123 |
+
* @access public
|
2124 |
+
* @param string $hash
|
2125 |
+
*/
|
2126 |
+
function setHash($hash)
|
2127 |
+
{
|
2128 |
+
// \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
|
2129 |
+
switch ($hash) {
|
2130 |
+
case 'md2':
|
2131 |
+
case 'md5':
|
2132 |
+
case 'sha1':
|
2133 |
+
case 'sha256':
|
2134 |
+
case 'sha384':
|
2135 |
+
case 'sha512':
|
2136 |
+
$this->hash = new Hash($hash);
|
2137 |
+
$this->hashName = $hash;
|
2138 |
+
break;
|
2139 |
+
default:
|
2140 |
+
$this->hash = new Hash('sha1');
|
2141 |
+
$this->hashName = 'sha1';
|
2142 |
+
}
|
2143 |
+
$this->hLen = $this->hash->getLength();
|
2144 |
+
}
|
2145 |
+
|
2146 |
+
/**
|
2147 |
+
* Determines which hashing function should be used for the mask generation function
|
2148 |
+
*
|
2149 |
+
* The mask generation function is used by self::ENCRYPTION_OAEP and self::SIGNATURE_PSS and although it's
|
2150 |
+
* best if Hash and MGFHash are set to the same thing this is not a requirement.
|
2151 |
+
*
|
2152 |
+
* @access public
|
2153 |
+
* @param string $hash
|
2154 |
+
*/
|
2155 |
+
function setMGFHash($hash)
|
2156 |
+
{
|
2157 |
+
// \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
|
2158 |
+
switch ($hash) {
|
2159 |
+
case 'md2':
|
2160 |
+
case 'md5':
|
2161 |
+
case 'sha1':
|
2162 |
+
case 'sha256':
|
2163 |
+
case 'sha384':
|
2164 |
+
case 'sha512':
|
2165 |
+
$this->mgfHash = new Hash($hash);
|
2166 |
+
break;
|
2167 |
+
default:
|
2168 |
+
$this->mgfHash = new Hash('sha1');
|
2169 |
+
}
|
2170 |
+
$this->mgfHLen = $this->mgfHash->getLength();
|
2171 |
+
}
|
2172 |
+
|
2173 |
+
/**
|
2174 |
+
* Determines the salt length
|
2175 |
+
*
|
2176 |
+
* To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
|
2177 |
+
*
|
2178 |
+
* Typical salt lengths in octets are hLen (the length of the output
|
2179 |
+
* of the hash function Hash) and 0.
|
2180 |
+
*
|
2181 |
+
* @access public
|
2182 |
+
* @param int $format
|
2183 |
+
*/
|
2184 |
+
function setSaltLength($sLen)
|
2185 |
+
{
|
2186 |
+
$this->sLen = $sLen;
|
2187 |
+
}
|
2188 |
+
|
2189 |
+
/**
|
2190 |
+
* Integer-to-Octet-String primitive
|
2191 |
+
*
|
2192 |
+
* See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
|
2193 |
+
*
|
2194 |
+
* @access private
|
2195 |
+
* @param \phpseclib\Math\BigInteger $x
|
2196 |
+
* @param int $xLen
|
2197 |
+
* @return string
|
2198 |
+
*/
|
2199 |
+
function _i2osp($x, $xLen)
|
2200 |
+
{
|
2201 |
+
$x = $x->toBytes();
|
2202 |
+
if (strlen($x) > $xLen) {
|
2203 |
+
user_error('Integer too large');
|
2204 |
+
return false;
|
2205 |
+
}
|
2206 |
+
return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
|
2207 |
+
}
|
2208 |
+
|
2209 |
+
/**
|
2210 |
+
* Octet-String-to-Integer primitive
|
2211 |
+
*
|
2212 |
+
* See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
|
2213 |
+
*
|
2214 |
+
* @access private
|
2215 |
+
* @param string $x
|
2216 |
+
* @return \phpseclib\Math\BigInteger
|
2217 |
+
*/
|
2218 |
+
function _os2ip($x)
|
2219 |
+
{
|
2220 |
+
return new BigInteger($x, 256);
|
2221 |
+
}
|
2222 |
+
|
2223 |
+
/**
|
2224 |
+
* Exponentiate with or without Chinese Remainder Theorem
|
2225 |
+
*
|
2226 |
+
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}.
|
2227 |
+
*
|
2228 |
+
* @access private
|
2229 |
+
* @param \phpseclib\Math\BigInteger $x
|
2230 |
+
* @return \phpseclib\Math\BigInteger
|
2231 |
+
*/
|
2232 |
+
function _exponentiate($x)
|
2233 |
+
{
|
2234 |
+
switch (true) {
|
2235 |
+
case empty($this->primes):
|
2236 |
+
case $this->primes[1]->equals($this->zero):
|
2237 |
+
case empty($this->coefficients):
|
2238 |
+
case $this->coefficients[2]->equals($this->zero):
|
2239 |
+
case empty($this->exponents):
|
2240 |
+
case $this->exponents[1]->equals($this->zero):
|
2241 |
+
return $x->modPow($this->exponent, $this->modulus);
|
2242 |
+
}
|
2243 |
+
|
2244 |
+
$num_primes = count($this->primes);
|
2245 |
+
|
2246 |
+
if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
|
2247 |
+
$m_i = array(
|
2248 |
+
1 => $x->modPow($this->exponents[1], $this->primes[1]),
|
2249 |
+
2 => $x->modPow($this->exponents[2], $this->primes[2])
|
2250 |
+
);
|
2251 |
+
$h = $m_i[1]->subtract($m_i[2]);
|
2252 |
+
$h = $h->multiply($this->coefficients[2]);
|
2253 |
+
list(, $h) = $h->divide($this->primes[1]);
|
2254 |
+
$m = $m_i[2]->add($h->multiply($this->primes[2]));
|
2255 |
+
|
2256 |
+
$r = $this->primes[1];
|
2257 |
+
for ($i = 3; $i <= $num_primes; $i++) {
|
2258 |
+
$m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
|
2259 |
+
|
2260 |
+
$r = $r->multiply($this->primes[$i - 1]);
|
2261 |
+
|
2262 |
+
$h = $m_i->subtract($m);
|
2263 |
+
$h = $h->multiply($this->coefficients[$i]);
|
2264 |
+
list(, $h) = $h->divide($this->primes[$i]);
|
2265 |
+
|
2266 |
+
$m = $m->add($r->multiply($h));
|
2267 |
+
}
|
2268 |
+
} else {
|
2269 |
+
$smallest = $this->primes[1];
|
2270 |
+
for ($i = 2; $i <= $num_primes; $i++) {
|
2271 |
+
if ($smallest->compare($this->primes[$i]) > 0) {
|
2272 |
+
$smallest = $this->primes[$i];
|
2273 |
+
}
|
2274 |
+
}
|
2275 |
+
|
2276 |
+
$one = new BigInteger(1);
|
2277 |
+
|
2278 |
+
$r = $one->random($one, $smallest->subtract($one));
|
2279 |
+
|
2280 |
+
$m_i = array(
|
2281 |
+
1 => $this->_blind($x, $r, 1),
|
2282 |
+
2 => $this->_blind($x, $r, 2)
|
2283 |
+
);
|
2284 |
+
$h = $m_i[1]->subtract($m_i[2]);
|
2285 |
+
$h = $h->multiply($this->coefficients[2]);
|
2286 |
+
list(, $h) = $h->divide($this->primes[1]);
|
2287 |
+
$m = $m_i[2]->add($h->multiply($this->primes[2]));
|
2288 |
+
|
2289 |
+
$r = $this->primes[1];
|
2290 |
+
for ($i = 3; $i <= $num_primes; $i++) {
|
2291 |
+
$m_i = $this->_blind($x, $r, $i);
|
2292 |
+
|
2293 |
+
$r = $r->multiply($this->primes[$i - 1]);
|
2294 |
+
|
2295 |
+
$h = $m_i->subtract($m);
|
2296 |
+
$h = $h->multiply($this->coefficients[$i]);
|
2297 |
+
list(, $h) = $h->divide($this->primes[$i]);
|
2298 |
+
|
2299 |
+
$m = $m->add($r->multiply($h));
|
2300 |
+
}
|
2301 |
+
}
|
2302 |
+
|
2303 |
+
return $m;
|
2304 |
+
}
|
2305 |
+
|
2306 |
+
/**
|
2307 |
+
* Performs RSA Blinding
|
2308 |
+
*
|
2309 |
+
* Protects against timing attacks by employing RSA Blinding.
|
2310 |
+
* Returns $x->modPow($this->exponents[$i], $this->primes[$i])
|
2311 |
+
*
|
2312 |
+
* @access private
|
2313 |
+
* @param \phpseclib\Math\BigInteger $x
|
2314 |
+
* @param \phpseclib\Math\BigInteger $r
|
2315 |
+
* @param int $i
|
2316 |
+
* @return \phpseclib\Math\BigInteger
|
2317 |
+
*/
|
2318 |
+
function _blind($x, $r, $i)
|
2319 |
+
{
|
2320 |
+
$x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
|
2321 |
+
$x = $x->modPow($this->exponents[$i], $this->primes[$i]);
|
2322 |
+
|
2323 |
+
$r = $r->modInverse($this->primes[$i]);
|
2324 |
+
$x = $x->multiply($r);
|
2325 |
+
list(, $x) = $x->divide($this->primes[$i]);
|
2326 |
+
|
2327 |
+
return $x;
|
2328 |
+
}
|
2329 |
+
|
2330 |
+
/**
|
2331 |
+
* Performs blinded RSA equality testing
|
2332 |
+
*
|
2333 |
+
* Protects against a particular type of timing attack described.
|
2334 |
+
*
|
2335 |
+
* See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)}
|
2336 |
+
*
|
2337 |
+
* Thanks for the heads up singpolyma!
|
2338 |
+
*
|
2339 |
+
* @access private
|
2340 |
+
* @param string $x
|
2341 |
+
* @param string $y
|
2342 |
+
* @return bool
|
2343 |
+
*/
|
2344 |
+
function _equals($x, $y)
|
2345 |
+
{
|
2346 |
+
if (function_exists('hash_equals')) {
|
2347 |
+
return hash_equals($x, $y);
|
2348 |
+
}
|
2349 |
+
|
2350 |
+
if (strlen($x) != strlen($y)) {
|
2351 |
+
return false;
|
2352 |
+
}
|
2353 |
+
|
2354 |
+
$result = "\0";
|
2355 |
+
$x^= $y;
|
2356 |
+
for ($i = 0; $i < strlen($x); $i++) {
|
2357 |
+
$result|= $x[$i];
|
2358 |
+
}
|
2359 |
+
|
2360 |
+
return $result === "\0";
|
2361 |
+
}
|
2362 |
+
|
2363 |
+
/**
|
2364 |
+
* RSAEP
|
2365 |
+
*
|
2366 |
+
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
|
2367 |
+
*
|
2368 |
+
* @access private
|
2369 |
+
* @param \phpseclib\Math\BigInteger $m
|
2370 |
+
* @return \phpseclib\Math\BigInteger
|
2371 |
+
*/
|
2372 |
+
function _rsaep($m)
|
2373 |
+
{
|
2374 |
+
if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
|
2375 |
+
user_error('Message representative out of range');
|
2376 |
+
return false;
|
2377 |
+
}
|
2378 |
+
return $this->_exponentiate($m);
|
2379 |
+
}
|
2380 |
+
|
2381 |
+
/**
|
2382 |
+
* RSADP
|
2383 |
+
*
|
2384 |
+
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
|
2385 |
+
*
|
2386 |
+
* @access private
|
2387 |
+
* @param \phpseclib\Math\BigInteger $c
|
2388 |
+
* @return \phpseclib\Math\BigInteger
|
2389 |
+
*/
|
2390 |
+
function _rsadp($c)
|
2391 |
+
{
|
2392 |
+
if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
|
2393 |
+
user_error('Ciphertext representative out of range');
|
2394 |
+
return false;
|
2395 |
+
}
|
2396 |
+
return $this->_exponentiate($c);
|
2397 |
+
}
|
2398 |
+
|
2399 |
+
/**
|
2400 |
+
* RSASP1
|
2401 |
+
*
|
2402 |
+
* See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
|
2403 |
+
*
|
2404 |
+
* @access private
|
2405 |
+
* @param \phpseclib\Math\BigInteger $m
|
2406 |
+
* @return \phpseclib\Math\BigInteger
|
2407 |
+
*/
|
2408 |
+
function _rsasp1($m)
|
2409 |
+
{
|
2410 |
+
if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
|
2411 |
+
user_error('Message representative out of range');
|
2412 |
+
return false;
|
2413 |
+
}
|
2414 |
+
return $this->_exponentiate($m);
|
2415 |
+
}
|
2416 |
+
|
2417 |
+
/**
|
2418 |
+
* RSAVP1
|
2419 |
+
*
|
2420 |
+
* See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
|
2421 |
+
*
|
2422 |
+
* @access private
|
2423 |
+
* @param \phpseclib\Math\BigInteger $s
|
2424 |
+
* @return \phpseclib\Math\BigInteger
|
2425 |
+
*/
|
2426 |
+
function _rsavp1($s)
|
2427 |
+
{
|
2428 |
+
if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
|
2429 |
+
user_error('Signature representative out of range');
|
2430 |
+
return false;
|
2431 |
+
}
|
2432 |
+
return $this->_exponentiate($s);
|
2433 |
+
}
|
2434 |
+
|
2435 |
+
/**
|
2436 |
+
* MGF1
|
2437 |
+
*
|
2438 |
+
* See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
|
2439 |
+
*
|
2440 |
+
* @access private
|
2441 |
+
* @param string $mgfSeed
|
2442 |
+
* @param int $mgfLen
|
2443 |
+
* @return string
|
2444 |
+
*/
|
2445 |
+
function _mgf1($mgfSeed, $maskLen)
|
2446 |
+
{
|
2447 |
+
// if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
|
2448 |
+
|
2449 |
+
$t = '';
|
2450 |
+
$count = ceil($maskLen / $this->mgfHLen);
|
2451 |
+
for ($i = 0; $i < $count; $i++) {
|
2452 |
+
$c = pack('N', $i);
|
2453 |
+
$t.= $this->mgfHash->hash($mgfSeed . $c);
|
2454 |
+
}
|
2455 |
+
|
2456 |
+
return substr($t, 0, $maskLen);
|
2457 |
+
}
|
2458 |
+
|
2459 |
+
/**
|
2460 |
+
* RSAES-OAEP-ENCRYPT
|
2461 |
+
*
|
2462 |
+
* See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
|
2463 |
+
* {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
|
2464 |
+
*
|
2465 |
+
* @access private
|
2466 |
+
* @param string $m
|
2467 |
+
* @param string $l
|
2468 |
+
* @return string
|
2469 |
+
*/
|
2470 |
+
function _rsaes_oaep_encrypt($m, $l = '')
|
2471 |
+
{
|
2472 |
+
$mLen = strlen($m);
|
2473 |
+
|
2474 |
+
// Length checking
|
2475 |
+
|
2476 |
+
// if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
|
2477 |
+
// be output.
|
2478 |
+
|
2479 |
+
if ($mLen > $this->k - 2 * $this->hLen - 2) {
|
2480 |
+
user_error('Message too long');
|
2481 |
+
return false;
|
2482 |
+
}
|
2483 |
+
|
2484 |
+
// EME-OAEP encoding
|
2485 |
+
|
2486 |
+
$lHash = $this->hash->hash($l);
|
2487 |
+
$ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
|
2488 |
+
$db = $lHash . $ps . chr(1) . $m;
|
2489 |
+
$seed = Random::string($this->hLen);
|
2490 |
+
$dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
|
2491 |
+
$maskedDB = $db ^ $dbMask;
|
2492 |
+
$seedMask = $this->_mgf1($maskedDB, $this->hLen);
|
2493 |
+
$maskedSeed = $seed ^ $seedMask;
|
2494 |
+
$em = chr(0) . $maskedSeed . $maskedDB;
|
2495 |
+
|
2496 |
+
// RSA encryption
|
2497 |
+
|
2498 |
+
$m = $this->_os2ip($em);
|
2499 |
+
$c = $this->_rsaep($m);
|
2500 |
+
$c = $this->_i2osp($c, $this->k);
|
2501 |
+
|
2502 |
+
// Output the ciphertext C
|
2503 |
+
|
2504 |
+
return $c;
|
2505 |
+
}
|
2506 |
+
|
2507 |
+
/**
|
2508 |
+
* RSAES-OAEP-DECRYPT
|
2509 |
+
*
|
2510 |
+
* See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error
|
2511 |
+
* messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
|
2512 |
+
*
|
2513 |
+
* Note. Care must be taken to ensure that an opponent cannot
|
2514 |
+
* distinguish the different error conditions in Step 3.g, whether by
|
2515 |
+
* error message or timing, or, more generally, learn partial
|
2516 |
+
* information about the encoded message EM. Otherwise an opponent may
|
2517 |
+
* be able to obtain useful information about the decryption of the
|
2518 |
+
* ciphertext C, leading to a chosen-ciphertext attack such as the one
|
2519 |
+
* observed by Manger [36].
|
2520 |
+
*
|
2521 |
+
* As for $l... to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
|
2522 |
+
*
|
2523 |
+
* Both the encryption and the decryption operations of RSAES-OAEP take
|
2524 |
+
* the value of a label L as input. In this version of PKCS #1, L is
|
2525 |
+
* the empty string; other uses of the label are outside the scope of
|
2526 |
+
* this document.
|
2527 |
+
*
|
2528 |
+
* @access private
|
2529 |
+
* @param string $c
|
2530 |
+
* @param string $l
|
2531 |
+
* @return string
|
2532 |
+
*/
|
2533 |
+
function _rsaes_oaep_decrypt($c, $l = '')
|
2534 |
+
{
|
2535 |
+
// Length checking
|
2536 |
+
|
2537 |
+
// if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
|
2538 |
+
// be output.
|
2539 |
+
|
2540 |
+
if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
|
2541 |
+
user_error('Decryption error');
|
2542 |
+
return false;
|
2543 |
+
}
|
2544 |
+
|
2545 |
+
// RSA decryption
|
2546 |
+
|
2547 |
+
$c = $this->_os2ip($c);
|
2548 |
+
$m = $this->_rsadp($c);
|
2549 |
+
if ($m === false) {
|
2550 |
+
user_error('Decryption error');
|
2551 |
+
return false;
|
2552 |
+
}
|
2553 |
+
$em = $this->_i2osp($m, $this->k);
|
2554 |
+
|
2555 |
+
// EME-OAEP decoding
|
2556 |
+
|
2557 |
+
$lHash = $this->hash->hash($l);
|
2558 |
+
$y = ord($em[0]);
|
2559 |
+
$maskedSeed = substr($em, 1, $this->hLen);
|
2560 |
+
$maskedDB = substr($em, $this->hLen + 1);
|
2561 |
+
$seedMask = $this->_mgf1($maskedDB, $this->hLen);
|
2562 |
+
$seed = $maskedSeed ^ $seedMask;
|
2563 |
+
$dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
|
2564 |
+
$db = $maskedDB ^ $dbMask;
|
2565 |
+
$lHash2 = substr($db, 0, $this->hLen);
|
2566 |
+
$m = substr($db, $this->hLen);
|
2567 |
+
$hashesMatch = $this->_equals($lHash, $lHash2);
|
2568 |
+
$leadingZeros = 1;
|
2569 |
+
$patternMatch = 0;
|
2570 |
+
$offset = 0;
|
2571 |
+
for ($i = 0; $i < strlen($m); $i++) {
|
2572 |
+
$patternMatch|= $leadingZeros & ($m[$i] === "\1");
|
2573 |
+
$leadingZeros&= $m[$i] === "\0";
|
2574 |
+
$offset+= $patternMatch ? 0 : 1;
|
2575 |
+
}
|
2576 |
+
|
2577 |
+
// we do & instead of && to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation
|
2578 |
+
// to protect against timing attacks
|
2579 |
+
if (!$hashesMatch & !$patternMatch) {
|
2580 |
+
user_error('Decryption error');
|
2581 |
+
return false;
|
2582 |
+
}
|
2583 |
+
|
2584 |
+
// Output the message M
|
2585 |
+
|
2586 |
+
return substr($m, $offset + 1);
|
2587 |
+
}
|
2588 |
+
|
2589 |
+
/**
|
2590 |
+
* Raw Encryption / Decryption
|
2591 |
+
*
|
2592 |
+
* Doesn't use padding and is not recommended.
|
2593 |
+
*
|
2594 |
+
* @access private
|
2595 |
+
* @param string $m
|
2596 |
+
* @return string
|
2597 |
+
*/
|
2598 |
+
function _raw_encrypt($m)
|
2599 |
+
{
|
2600 |
+
$temp = $this->_os2ip($m);
|
2601 |
+
$temp = $this->_rsaep($temp);
|
2602 |
+
return $this->_i2osp($temp, $this->k);
|
2603 |
+
}
|
2604 |
+
|
2605 |
+
/**
|
2606 |
+
* RSAES-PKCS1-V1_5-ENCRYPT
|
2607 |
+
*
|
2608 |
+
* See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
|
2609 |
+
*
|
2610 |
+
* @access private
|
2611 |
+
* @param string $m
|
2612 |
+
* @return string
|
2613 |
+
*/
|
2614 |
+
function _rsaes_pkcs1_v1_5_encrypt($m)
|
2615 |
+
{
|
2616 |
+
$mLen = strlen($m);
|
2617 |
+
|
2618 |
+
// Length checking
|
2619 |
+
|
2620 |
+
if ($mLen > $this->k - 11) {
|
2621 |
+
user_error('Message too long');
|
2622 |
+
return false;
|
2623 |
+
}
|
2624 |
+
|
2625 |
+
// EME-PKCS1-v1_5 encoding
|
2626 |
+
|
2627 |
+
$psLen = $this->k - $mLen - 3;
|
2628 |
+
$ps = '';
|
2629 |
+
while (strlen($ps) != $psLen) {
|
2630 |
+
$temp = Random::string($psLen - strlen($ps));
|
2631 |
+
$temp = str_replace("\x00", '', $temp);
|
2632 |
+
$ps.= $temp;
|
2633 |
+
}
|
2634 |
+
$type = 2;
|
2635 |
+
// see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done
|
2636 |
+
if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
|
2637 |
+
$type = 1;
|
2638 |
+
// "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF"
|
2639 |
+
$ps = str_repeat("\xFF", $psLen);
|
2640 |
+
}
|
2641 |
+
$em = chr(0) . chr($type) . $ps . chr(0) . $m;
|
2642 |
+
|
2643 |
+
// RSA encryption
|
2644 |
+
$m = $this->_os2ip($em);
|
2645 |
+
$c = $this->_rsaep($m);
|
2646 |
+
$c = $this->_i2osp($c, $this->k);
|
2647 |
+
|
2648 |
+
// Output the ciphertext C
|
2649 |
+
|
2650 |
+
return $c;
|
2651 |
+
}
|
2652 |
+
|
2653 |
+
/**
|
2654 |
+
* RSAES-PKCS1-V1_5-DECRYPT
|
2655 |
+
*
|
2656 |
+
* See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
|
2657 |
+
*
|
2658 |
+
* For compatibility purposes, this function departs slightly from the description given in RFC3447.
|
2659 |
+
* The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
|
2660 |
+
* private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
|
2661 |
+
* public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed
|
2662 |
+
* to be 2 regardless of which key is used. For compatibility purposes, we'll just check to make sure the
|
2663 |
+
* second byte is 2 or less. If it is, we'll accept the decrypted string as valid.
|
2664 |
+
*
|
2665 |
+
* As a consequence of this, a private key encrypted ciphertext produced with \phpseclib\Crypt\RSA may not decrypt
|
2666 |
+
* with a strictly PKCS#1 v1.5 compliant RSA implementation. Public key encrypted ciphertext's should but
|
2667 |
+
* not private key encrypted ciphertext's.
|
2668 |
+
*
|
2669 |
+
* @access private
|
2670 |
+
* @param string $c
|
2671 |
+
* @return string
|
2672 |
+
*/
|
2673 |
+
function _rsaes_pkcs1_v1_5_decrypt($c)
|
2674 |
+
{
|
2675 |
+
// Length checking
|
2676 |
+
|
2677 |
+
if (strlen($c) != $this->k) { // or if k < 11
|
2678 |
+
user_error('Decryption error');
|
2679 |
+
return false;
|
2680 |
+
}
|
2681 |
+
|
2682 |
+
// RSA decryption
|
2683 |
+
|
2684 |
+
$c = $this->_os2ip($c);
|
2685 |
+
$m = $this->_rsadp($c);
|
2686 |
+
|
2687 |
+
if ($m === false) {
|
2688 |
+
user_error('Decryption error');
|
2689 |
+
return false;
|
2690 |
+
}
|
2691 |
+
$em = $this->_i2osp($m, $this->k);
|
2692 |
+
|
2693 |
+
// EME-PKCS1-v1_5 decoding
|
2694 |
+
|
2695 |
+
if (ord($em[0]) != 0 || ord($em[1]) > 2) {
|
2696 |
+
user_error('Decryption error');
|
2697 |
+
return false;
|
2698 |
+
}
|
2699 |
+
|
2700 |
+
$ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
|
2701 |
+
$m = substr($em, strlen($ps) + 3);
|
2702 |
+
|
2703 |
+
if (strlen($ps) < 8) {
|
2704 |
+
user_error('Decryption error');
|
2705 |
+
return false;
|
2706 |
+
}
|
2707 |
+
|
2708 |
+
// Output M
|
2709 |
+
|
2710 |
+
return $m;
|
2711 |
+
}
|
2712 |
+
|
2713 |
+
/**
|
2714 |
+
* EMSA-PSS-ENCODE
|
2715 |
+
*
|
2716 |
+
* See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
|
2717 |
+
*
|
2718 |
+
* @access private
|
2719 |
+
* @param string $m
|
2720 |
+
* @param int $emBits
|
2721 |
+
*/
|
2722 |
+
function _emsa_pss_encode($m, $emBits)
|
2723 |
+
{
|
2724 |
+
// if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
|
2725 |
+
// be output.
|
2726 |
+
|
2727 |
+
$emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
|
2728 |
+
$sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
|
2729 |
+
|
2730 |
+
$mHash = $this->hash->hash($m);
|
2731 |
+
if ($emLen < $this->hLen + $sLen + 2) {
|
2732 |
+
user_error('Encoding error');
|
2733 |
+
return false;
|
2734 |
+
}
|
2735 |
+
|
2736 |
+
$salt = Random::string($sLen);
|
2737 |
+
$m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
|
2738 |
+
$h = $this->hash->hash($m2);
|
2739 |
+
$ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
|
2740 |
+
$db = $ps . chr(1) . $salt;
|
2741 |
+
$dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
|
2742 |
+
$maskedDB = $db ^ $dbMask;
|
2743 |
+
$maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
|
2744 |
+
$em = $maskedDB . $h . chr(0xBC);
|
2745 |
+
|
2746 |
+
return $em;
|
2747 |
+
}
|
2748 |
+
|
2749 |
+
/**
|
2750 |
+
* EMSA-PSS-VERIFY
|
2751 |
+
*
|
2752 |
+
* See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
|
2753 |
+
*
|
2754 |
+
* @access private
|
2755 |
+
* @param string $m
|
2756 |
+
* @param string $em
|
2757 |
+
* @param int $emBits
|
2758 |
+
* @return string
|
2759 |
+
*/
|
2760 |
+
function _emsa_pss_verify($m, $em, $emBits)
|
2761 |
+
{
|
2762 |
+
// if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
|
2763 |
+
// be output.
|
2764 |
+
|
2765 |
+
$emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
|
2766 |
+
$sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
|
2767 |
+
|
2768 |
+
$mHash = $this->hash->hash($m);
|
2769 |
+
if ($emLen < $this->hLen + $sLen + 2) {
|
2770 |
+
return false;
|
2771 |
+
}
|
2772 |
+
|
2773 |
+
if ($em[strlen($em) - 1] != chr(0xBC)) {
|
2774 |
+
return false;
|
2775 |
+
}
|
2776 |
+
|
2777 |
+
$maskedDB = substr($em, 0, -$this->hLen - 1);
|
2778 |
+
$h = substr($em, -$this->hLen - 1, $this->hLen);
|
2779 |
+
$temp = chr(0xFF << ($emBits & 7));
|
2780 |
+
if ((~$maskedDB[0] & $temp) != $temp) {
|
2781 |
+
return false;
|
2782 |
+
}
|
2783 |
+
$dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
|
2784 |
+
$db = $maskedDB ^ $dbMask;
|
2785 |
+
$db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
|
2786 |
+
$temp = $emLen - $this->hLen - $sLen - 2;
|
2787 |
+
if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
|
2788 |
+
return false;
|
2789 |
+
}
|
2790 |
+
$salt = substr($db, $temp + 1); // should be $sLen long
|
2791 |
+
$m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
|
2792 |
+
$h2 = $this->hash->hash($m2);
|
2793 |
+
return $this->_equals($h, $h2);
|
2794 |
+
}
|
2795 |
+
|
2796 |
+
/**
|
2797 |
+
* RSASSA-PSS-SIGN
|
2798 |
+
*
|
2799 |
+
* See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
|
2800 |
+
*
|
2801 |
+
* @access private
|
2802 |
+
* @param string $m
|
2803 |
+
* @return string
|
2804 |
+
*/
|
2805 |
+
function _rsassa_pss_sign($m)
|
2806 |
+
{
|
2807 |
+
// EMSA-PSS encoding
|
2808 |
+
|
2809 |
+
$em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
|
2810 |
+
|
2811 |
+
// RSA signature
|
2812 |
+
|
2813 |
+
$m = $this->_os2ip($em);
|
2814 |
+
$s = $this->_rsasp1($m);
|
2815 |
+
$s = $this->_i2osp($s, $this->k);
|
2816 |
+
|
2817 |
+
// Output the signature S
|
2818 |
+
|
2819 |
+
return $s;
|
2820 |
+
}
|
2821 |
+
|
2822 |
+
/**
|
2823 |
+
* RSASSA-PSS-VERIFY
|
2824 |
+
*
|
2825 |
+
* See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
|
2826 |
+
*
|
2827 |
+
* @access private
|
2828 |
+
* @param string $m
|
2829 |
+
* @param string $s
|
2830 |
+
* @return string
|
2831 |
+
*/
|
2832 |
+
function _rsassa_pss_verify($m, $s)
|
2833 |
+
{
|
2834 |
+
// Length checking
|
2835 |
+
|
2836 |
+
if (strlen($s) != $this->k) {
|
2837 |
+
user_error('Invalid signature');
|
2838 |
+
return false;
|
2839 |
+
}
|
2840 |
+
|
2841 |
+
// RSA verification
|
2842 |
+
|
2843 |
+
$modBits = 8 * $this->k;
|
2844 |
+
|
2845 |
+
$s2 = $this->_os2ip($s);
|
2846 |
+
$m2 = $this->_rsavp1($s2);
|
2847 |
+
if ($m2 === false) {
|
2848 |
+
user_error('Invalid signature');
|
2849 |
+
return false;
|
2850 |
+
}
|
2851 |
+
$em = $this->_i2osp($m2, $modBits >> 3);
|
2852 |
+
if ($em === false) {
|
2853 |
+
user_error('Invalid signature');
|
2854 |
+
return false;
|
2855 |
+
}
|
2856 |
+
|
2857 |
+
// EMSA-PSS verification
|
2858 |
+
|
2859 |
+
return $this->_emsa_pss_verify($m, $em, $modBits - 1);
|
2860 |
+
}
|
2861 |
+
|
2862 |
+
/**
|
2863 |
+
* EMSA-PKCS1-V1_5-ENCODE
|
2864 |
+
*
|
2865 |
+
* See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
|
2866 |
+
*
|
2867 |
+
* @access private
|
2868 |
+
* @param string $m
|
2869 |
+
* @param int $emLen
|
2870 |
+
* @return string
|
2871 |
+
*/
|
2872 |
+
function _emsa_pkcs1_v1_5_encode($m, $emLen)
|
2873 |
+
{
|
2874 |
+
$h = $this->hash->hash($m);
|
2875 |
+
if ($h === false) {
|
2876 |
+
return false;
|
2877 |
+
}
|
2878 |
+
|
2879 |
+
// see http://tools.ietf.org/html/rfc3447#page-43
|
2880 |
+
switch ($this->hashName) {
|
2881 |
+
case 'md2':
|
2882 |
+
$t = pack('H*', '3020300c06082a864886f70d020205000410');
|
2883 |
+
break;
|
2884 |
+
case 'md5':
|
2885 |
+
$t = pack('H*', '3020300c06082a864886f70d020505000410');
|
2886 |
+
break;
|
2887 |
+
case 'sha1':
|
2888 |
+
$t = pack('H*', '3021300906052b0e03021a05000414');
|
2889 |
+
break;
|
2890 |
+
case 'sha256':
|
2891 |
+
$t = pack('H*', '3031300d060960864801650304020105000420');
|
2892 |
+
break;
|
2893 |
+
case 'sha384':
|
2894 |
+
$t = pack('H*', '3041300d060960864801650304020205000430');
|
2895 |
+
break;
|
2896 |
+
case 'sha512':
|
2897 |
+
$t = pack('H*', '3051300d060960864801650304020305000440');
|
2898 |
+
}
|
2899 |
+
$t.= $h;
|
2900 |
+
$tLen = strlen($t);
|
2901 |
+
|
2902 |
+
if ($emLen < $tLen + 11) {
|
2903 |
+
user_error('Intended encoded message length too short');
|
2904 |
+
return false;
|
2905 |
+
}
|
2906 |
+
|
2907 |
+
$ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
|
2908 |
+
|
2909 |
+
$em = "\0\1$ps\0$t";
|
2910 |
+
|
2911 |
+
return $em;
|
2912 |
+
}
|
2913 |
+
|
2914 |
+
/**
|
2915 |
+
* RSASSA-PKCS1-V1_5-SIGN
|
2916 |
+
*
|
2917 |
+
* See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
|
2918 |
+
*
|
2919 |
+
* @access private
|
2920 |
+
* @param string $m
|
2921 |
+
* @return string
|
2922 |
+
*/
|
2923 |
+
function _rsassa_pkcs1_v1_5_sign($m)
|
2924 |
+
{
|
2925 |
+
// EMSA-PKCS1-v1_5 encoding
|
2926 |
+
|
2927 |
+
$em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
|
2928 |
+
if ($em === false) {
|
2929 |
+
user_error('RSA modulus too short');
|
2930 |
+
return false;
|
2931 |
+
}
|
2932 |
+
|
2933 |
+
// RSA signature
|
2934 |
+
|
2935 |
+
$m = $this->_os2ip($em);
|
2936 |
+
$s = $this->_rsasp1($m);
|
2937 |
+
$s = $this->_i2osp($s, $this->k);
|
2938 |
+
|
2939 |
+
// Output the signature S
|
2940 |
+
|
2941 |
+
return $s;
|
2942 |
+
}
|
2943 |
+
|
2944 |
+
/**
|
2945 |
+
* RSASSA-PKCS1-V1_5-VERIFY
|
2946 |
+
*
|
2947 |
+
* See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
|
2948 |
+
*
|
2949 |
+
* @access private
|
2950 |
+
* @param string $m
|
2951 |
+
* @return string
|
2952 |
+
*/
|
2953 |
+
function _rsassa_pkcs1_v1_5_verify($m, $s)
|
2954 |
+
{
|
2955 |
+
// Length checking
|
2956 |
+
|
2957 |
+
if (strlen($s) != $this->k) {
|
2958 |
+
user_error('Invalid signature');
|
2959 |
+
return false;
|
2960 |
+
}
|
2961 |
+
|
2962 |
+
// RSA verification
|
2963 |
+
|
2964 |
+
$s = $this->_os2ip($s);
|
2965 |
+
$m2 = $this->_rsavp1($s);
|
2966 |
+
if ($m2 === false) {
|
2967 |
+
user_error('Invalid signature');
|
2968 |
+
return false;
|
2969 |
+
}
|
2970 |
+
$em = $this->_i2osp($m2, $this->k);
|
2971 |
+
if ($em === false) {
|
2972 |
+
user_error('Invalid signature');
|
2973 |
+
return false;
|
2974 |
+
}
|
2975 |
+
|
2976 |
+
// EMSA-PKCS1-v1_5 encoding
|
2977 |
+
|
2978 |
+
$em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
|
2979 |
+
if ($em2 === false) {
|
2980 |
+
user_error('RSA modulus too short');
|
2981 |
+
return false;
|
2982 |
+
}
|
2983 |
+
|
2984 |
+
// Compare
|
2985 |
+
return $this->_equals($em, $em2);
|
2986 |
+
}
|
2987 |
+
|
2988 |
+
/**
|
2989 |
+
* Set Encryption Mode
|
2990 |
+
*
|
2991 |
+
* Valid values include self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1.
|
2992 |
+
*
|
2993 |
+
* @access public
|
2994 |
+
* @param int $mode
|
2995 |
+
*/
|
2996 |
+
function setEncryptionMode($mode)
|
2997 |
+
{
|
2998 |
+
$this->encryptionMode = $mode;
|
2999 |
+
}
|
3000 |
+
|
3001 |
+
/**
|
3002 |
+
* Set Signature Mode
|
3003 |
+
*
|
3004 |
+
* Valid values include self::SIGNATURE_PSS and self::SIGNATURE_PKCS1
|
3005 |
+
*
|
3006 |
+
* @access public
|
3007 |
+
* @param int $mode
|
3008 |
+
*/
|
3009 |
+
function setSignatureMode($mode)
|
3010 |
+
{
|
3011 |
+
$this->signatureMode = $mode;
|
3012 |
+
}
|
3013 |
+
|
3014 |
+
/**
|
3015 |
+
* Set public key comment.
|
3016 |
+
*
|
3017 |
+
* @access public
|
3018 |
+
* @param string $comment
|
3019 |
+
*/
|
3020 |
+
function setComment($comment)
|
3021 |
+
{
|
3022 |
+
$this->comment = $comment;
|
3023 |
+
}
|
3024 |
+
|
3025 |
+
/**
|
3026 |
+
* Get public key comment.
|
3027 |
+
*
|
3028 |
+
* @access public
|
3029 |
+
* @return string
|
3030 |
+
*/
|
3031 |
+
function getComment()
|
3032 |
+
{
|
3033 |
+
return $this->comment;
|
3034 |
+
}
|
3035 |
+
|
3036 |
+
/**
|
3037 |
+
* Encryption
|
3038 |
+
*
|
3039 |
+
* Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
|
3040 |
+
* If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
|
3041 |
+
* be concatenated together.
|
3042 |
+
*
|
3043 |
+
* @see self::decrypt()
|
3044 |
+
* @access public
|
3045 |
+
* @param string $plaintext
|
3046 |
+
* @return string
|
3047 |
+
*/
|
3048 |
+
function encrypt($plaintext)
|
3049 |
+
{
|
3050 |
+
switch ($this->encryptionMode) {
|
3051 |
+
case self::ENCRYPTION_NONE:
|
3052 |
+
$plaintext = str_split($plaintext, $this->k);
|
3053 |
+
$ciphertext = '';
|
3054 |
+
foreach ($plaintext as $m) {
|
3055 |
+
$ciphertext.= $this->_raw_encrypt($m);
|
3056 |
+
}
|
3057 |
+
return $ciphertext;
|
3058 |
+
case self::ENCRYPTION_PKCS1:
|
3059 |
+
$length = $this->k - 11;
|
3060 |
+
if ($length <= 0) {
|
3061 |
+
return false;
|
3062 |
+
}
|
3063 |
+
|
3064 |
+
$plaintext = str_split($plaintext, $length);
|
3065 |
+
$ciphertext = '';
|
3066 |
+
foreach ($plaintext as $m) {
|
3067 |
+
$ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
|
3068 |
+
}
|
3069 |
+
return $ciphertext;
|
3070 |
+
//case self::ENCRYPTION_OAEP:
|
3071 |
+
default:
|
3072 |
+
$length = $this->k - 2 * $this->hLen - 2;
|
3073 |
+
if ($length <= 0) {
|
3074 |
+
return false;
|
3075 |
+
}
|
3076 |
+
|
3077 |
+
$plaintext = str_split($plaintext, $length);
|
3078 |
+
$ciphertext = '';
|
3079 |
+
foreach ($plaintext as $m) {
|
3080 |
+
$ciphertext.= $this->_rsaes_oaep_encrypt($m);
|
3081 |
+
}
|
3082 |
+
return $ciphertext;
|
3083 |
+
}
|
3084 |
+
}
|
3085 |
+
|
3086 |
+
/**
|
3087 |
+
* Decryption
|
3088 |
+
*
|
3089 |
+
* @see self::encrypt()
|
3090 |
+
* @access public
|
3091 |
+
* @param string $plaintext
|
3092 |
+
* @return string
|
3093 |
+
*/
|
3094 |
+
function decrypt($ciphertext)
|
3095 |
+
{
|
3096 |
+
if ($this->k <= 0) {
|
3097 |
+
return false;
|
3098 |
+
}
|
3099 |
+
|
3100 |
+
$ciphertext = str_split($ciphertext, $this->k);
|
3101 |
+
$ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);
|
3102 |
+
|
3103 |
+
$plaintext = '';
|
3104 |
+
|
3105 |
+
switch ($this->encryptionMode) {
|
3106 |
+
case self::ENCRYPTION_NONE:
|
3107 |
+
$decrypt = '_raw_encrypt';
|
3108 |
+
break;
|
3109 |
+
case self::ENCRYPTION_PKCS1:
|
3110 |
+
$decrypt = '_rsaes_pkcs1_v1_5_decrypt';
|
3111 |
+
break;
|
3112 |
+
//case self::ENCRYPTION_OAEP:
|
3113 |
+
default:
|
3114 |
+
$decrypt = '_rsaes_oaep_decrypt';
|
3115 |
+
}
|
3116 |
+
|
3117 |
+
foreach ($ciphertext as $c) {
|
3118 |
+
$temp = $this->$decrypt($c);
|
3119 |
+
if ($temp === false) {
|
3120 |
+
return false;
|
3121 |
+
}
|
3122 |
+
$plaintext.= $temp;
|
3123 |
+
}
|
3124 |
+
|
3125 |
+
return $plaintext;
|
3126 |
+
}
|
3127 |
+
|
3128 |
+
/**
|
3129 |
+
* Create a signature
|
3130 |
+
*
|
3131 |
+
* @see self::verify()
|
3132 |
+
* @access public
|
3133 |
+
* @param string $message
|
3134 |
+
* @return string
|
3135 |
+
*/
|
3136 |
+
function sign($message)
|
3137 |
+
{
|
3138 |
+
if (empty($this->modulus) || empty($this->exponent)) {
|
3139 |
+
return false;
|
3140 |
+
}
|
3141 |
+
|
3142 |
+
switch ($this->signatureMode) {
|
3143 |
+
case self::SIGNATURE_PKCS1:
|
3144 |
+
return $this->_rsassa_pkcs1_v1_5_sign($message);
|
3145 |
+
//case self::SIGNATURE_PSS:
|
3146 |
+
default:
|
3147 |
+
return $this->_rsassa_pss_sign($message);
|
3148 |
+
}
|
3149 |
+
}
|
3150 |
+
|
3151 |
+
/**
|
3152 |
+
* Verifies a signature
|
3153 |
+
*
|
3154 |
+
* @see self::sign()
|
3155 |
+
* @access public
|
3156 |
+
* @param string $message
|
3157 |
+
* @param string $signature
|
3158 |
+
* @return bool
|
3159 |
+
*/
|
3160 |
+
function verify($message, $signature)
|
3161 |
+
{
|
3162 |
+
if (empty($this->modulus) || empty($this->exponent)) {
|
3163 |
+
return false;
|
3164 |
+
}
|
3165 |
+
|
3166 |
+
switch ($this->signatureMode) {
|
3167 |
+
case self::SIGNATURE_PKCS1:
|
3168 |
+
return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
|
3169 |
+
//case self::SIGNATURE_PSS:
|
3170 |
+
default:
|
3171 |
+
return $this->_rsassa_pss_verify($message, $signature);
|
3172 |
+
}
|
3173 |
+
}
|
3174 |
+
|
3175 |
+
/**
|
3176 |
+
* Extract raw BER from Base64 encoding
|
3177 |
+
*
|
3178 |
+
* @access private
|
3179 |
+
* @param string $str
|
3180 |
+
* @return string
|
3181 |
+
*/
|
3182 |
+
function _extractBER($str)
|
3183 |
+
{
|
3184 |
+
/* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
|
3185 |
+
* above and beyond the ceritificate.
|
3186 |
+
* ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
|
3187 |
+
*
|
3188 |
+
* Bag Attributes
|
3189 |
+
* localKeyID: 01 00 00 00
|
3190 |
+
* subject=/O=organization/OU=org unit/CN=common name
|
3191 |
+
* issuer=/O=organization/CN=common name
|
3192 |
+
*/
|
3193 |
+
$temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
|
3194 |
+
// remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
|
3195 |
+
$temp = preg_replace('#-+[^-]+-+#', '', $temp);
|
3196 |
+
// remove new lines
|
3197 |
+
$temp = str_replace(array("\r", "\n", ' '), '', $temp);
|
3198 |
+
$temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
|
3199 |
+
return $temp != false ? $temp : $str;
|
3200 |
+
}
|
3201 |
+
}
|
vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php
CHANGED
@@ -1,274 +1,274 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Random Number Generator
|
5 |
-
*
|
6 |
-
* PHP version 5
|
7 |
-
*
|
8 |
-
* Here's a short example of how to use this library:
|
9 |
-
* <code>
|
10 |
-
* <?php
|
11 |
-
* include 'vendor/autoload.php';
|
12 |
-
*
|
13 |
-
* echo bin2hex(\phpseclib\Crypt\Random::string(8));
|
14 |
-
* ?>
|
15 |
-
* </code>
|
16 |
-
*
|
17 |
-
* @category Crypt
|
18 |
-
* @package Random
|
19 |
-
* @author Jim Wigginton <terrafrost@php.net>
|
20 |
-
* @copyright 2007 Jim Wigginton
|
21 |
-
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
22 |
-
* @link http://phpseclib.sourceforge.net
|
23 |
-
*/
|
24 |
-
|
25 |
-
namespace phpseclib\Crypt;
|
26 |
-
|
27 |
-
/**
|
28 |
-
* Pure-PHP Random Number Generator
|
29 |
-
*
|
30 |
-
* @package Random
|
31 |
-
* @author Jim Wigginton <terrafrost@php.net>
|
32 |
-
* @access public
|
33 |
-
*/
|
34 |
-
class Random
|
35 |
-
{
|
36 |
-
/**
|
37 |
-
* Generate a random string.
|
38 |
-
*
|
39 |
-
* Although microoptimizations are generally discouraged as they impair readability this function is ripe with
|
40 |
-
* microoptimizations because this function has the potential of being called a huge number of times.
|
41 |
-
* eg. for RSA key generation.
|
42 |
-
*
|
43 |
-
* @param int $length
|
44 |
-
* @return string
|
45 |
-
*/
|
46 |
-
static function string($length)
|
47 |
-
{
|
48 |
-
if (!$length) {
|
49 |
-
return '';
|
50 |
-
}
|
51 |
-
|
52 |
-
if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
|
53 |
-
try {
|
54 |
-
return \random_bytes($length);
|
55 |
-
} catch (\Throwable $e) {
|
56 |
-
// If a sufficient source of randomness is unavailable, random_bytes() will throw an
|
57 |
-
// object that implements the Throwable interface (Exception, TypeError, Error).
|
58 |
-
// We don't actually need to do anything here. The string() method should just continue
|
59 |
-
// as normal. Note, however, that if we don't have a sufficient source of randomness for
|
60 |
-
// random_bytes(), most of the other calls here will fail too, so we'll end up using
|
61 |
-
// the PHP implementation.
|
62 |
-
}
|
63 |
-
}
|
64 |
-
|
65 |
-
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
|
66 |
-
// method 1. prior to PHP 5.3 this would call rand() on windows hence the function_exists('class_alias') call.
|
67 |
-
// ie. class_alias is a function that was introduced in PHP 5.3
|
68 |
-
if (extension_loaded('mcrypt') && function_exists('class_alias')) {
|
69 |
-
return @mcrypt_create_iv($length);
|
70 |
-
}
|
71 |
-
// method 2. openssl_random_pseudo_bytes was introduced in PHP 5.3.0 but prior to PHP 5.3.4 there was,
|
72 |
-
// to quote <http://php.net/ChangeLog-5.php#5.3.4>, "possible blocking behavior". as of 5.3.4
|
73 |
-
// openssl_random_pseudo_bytes and mcrypt_create_iv do the exact same thing on Windows. ie. they both
|
74 |
-
// call php_win32_get_random_bytes():
|
75 |
-
//
|
76 |
-
// https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/openssl/openssl.c#L5008
|
77 |
-
// https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1392
|
78 |
-
//
|
79 |
-
// php_win32_get_random_bytes() is defined thusly:
|
80 |
-
//
|
81 |
-
// https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/win32/winutil.c#L80
|
82 |
-
//
|
83 |
-
// we're calling it, all the same, in the off chance that the mcrypt extension is not available
|
84 |
-
if (extension_loaded('openssl') && version_compare(PHP_VERSION, '5.3.4', '>=')) {
|
85 |
-
return openssl_random_pseudo_bytes($length);
|
86 |
-
}
|
87 |
-
} else {
|
88 |
-
// method 1. the fastest
|
89 |
-
if (extension_loaded('openssl')) {
|
90 |
-
return openssl_random_pseudo_bytes($length);
|
91 |
-
}
|
92 |
-
// method 2
|
93 |
-
static $fp = true;
|
94 |
-
if ($fp === true) {
|
95 |
-
// warning's will be output unles the error suppression operator is used. errors such as
|
96 |
-
// "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc.
|
97 |
-
$fp = @fopen('/dev/urandom', 'rb');
|
98 |
-
}
|
99 |
-
if ($fp !== true && $fp !== false) { // surprisingly faster than !is_bool() or is_resource()
|
100 |
-
return fread($fp, $length);
|
101 |
-
}
|
102 |
-
// method 3. pretty much does the same thing as method 2 per the following url:
|
103 |
-
// https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1391
|
104 |
-
// surprisingly slower than method 2. maybe that's because mcrypt_create_iv does a bunch of error checking that we're
|
105 |
-
// not doing. regardless, this'll only be called if this PHP script couldn't open /dev/urandom due to open_basedir
|
106 |
-
// restrictions or some such
|
107 |
-
if (extension_loaded('mcrypt')) {
|
108 |
-
return @mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
|
109 |
-
}
|
110 |
-
}
|
111 |
-
// at this point we have no choice but to use a pure-PHP CSPRNG
|
112 |
-
|
113 |
-
// cascade entropy across multiple PHP instances by fixing the session and collecting all
|
114 |
-
// environmental variables, including the previous session data and the current session
|
115 |
-
// data.
|
116 |
-
//
|
117 |
-
// mt_rand seeds itself by looking at the PID and the time, both of which are (relatively)
|
118 |
-
// easy to guess at. linux uses mouse clicks, keyboard timings, etc, as entropy sources, but
|
119 |
-
// PHP isn't low level to be able to use those as sources and on a web server there's not likely
|
120 |
-
// going to be a ton of keyboard or mouse action. web servers do have one thing that we can use
|
121 |
-
// however, a ton of people visiting the website. obviously you don't want to base your seeding
|
122 |
-
// soley on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled
|
123 |
-
// by the user and (2) this isn't just looking at the data sent by the current user - it's based
|
124 |
-
// on the data sent by all users. one user requests the page and a hash of their info is saved.
|
125 |
-
// another user visits the page and the serialization of their data is utilized along with the
|
126 |
-
// server envirnment stuff and a hash of the previous http request data (which itself utilizes
|
127 |
-
// a hash of the session data before that). certainly an attacker should be assumed to have
|
128 |
-
// full control over his own http requests. he, however, is not going to have control over
|
129 |
-
// everyone's http requests.
|
130 |
-
static $crypto = false, $v;
|
131 |
-
if ($crypto === false) {
|
132 |
-
// save old session data
|
133 |
-
$old_session_id = session_id();
|
134 |
-
$old_use_cookies = ini_get('session.use_cookies');
|
135 |
-
$old_session_cache_limiter = session_cache_limiter();
|
136 |
-
$_OLD_SESSION = isset($_SESSION) ? $_SESSION : false;
|
137 |
-
if ($old_session_id != '') {
|
138 |
-
session_write_close();
|
139 |
-
}
|
140 |
-
|
141 |
-
session_id(1);
|
142 |
-
ini_set('session.use_cookies', 0);
|
143 |
-
session_cache_limiter('');
|
144 |
-
session_start();
|
145 |
-
|
146 |
-
$v = $seed = $_SESSION['seed'] = pack('H*', sha1(
|
147 |
-
(isset($_SERVER) ? phpseclib_safe_serialize($_SERVER) : '') .
|
148 |
-
(isset($_POST) ? phpseclib_safe_serialize($_POST) : '') .
|
149 |
-
(isset($_GET) ? phpseclib_safe_serialize($_GET) : '') .
|
150 |
-
(isset($_COOKIE) ? phpseclib_safe_serialize($_COOKIE) : '') .
|
151 |
-
phpseclib_safe_serialize($GLOBALS) .
|
152 |
-
phpseclib_safe_serialize($_SESSION) .
|
153 |
-
phpseclib_safe_serialize($_OLD_SESSION)
|
154 |
-
));
|
155 |
-
if (!isset($_SESSION['count'])) {
|
156 |
-
$_SESSION['count'] = 0;
|
157 |
-
}
|
158 |
-
$_SESSION['count']++;
|
159 |
-
|
160 |
-
session_write_close();
|
161 |
-
|
162 |
-
// restore old session data
|
163 |
-
if ($old_session_id != '') {
|
164 |
-
session_id($old_session_id);
|
165 |
-
session_start();
|
166 |
-
ini_set('session.use_cookies', $old_use_cookies);
|
167 |
-
session_cache_limiter($old_session_cache_limiter);
|
168 |
-
} else {
|
169 |
-
if ($_OLD_SESSION !== false) {
|
170 |
-
$_SESSION = $_OLD_SESSION;
|
171 |
-
unset($_OLD_SESSION);
|
172 |
-
} else {
|
173 |
-
unset($_SESSION);
|
174 |
-
}
|
175 |
-
}
|
176 |
-
|
177 |
-
// in SSH2 a shared secret and an exchange hash are generated through the key exchange process.
|
178 |
-
// the IV client to server is the hash of that "nonce" with the letter A and for the encryption key it's the letter C.
|
179 |
-
// if the hash doesn't produce enough a key or an IV that's long enough concat successive hashes of the
|
180 |
-
// original hash and the current hash. we'll be emulating that. for more info see the following URL:
|
181 |
-
//
|
182 |
-
// http://tools.ietf.org/html/rfc4253#section-7.2
|
183 |
-
//
|
184 |
-
// see the is_string($crypto) part for an example of how to expand the keys
|
185 |
-
$key = pack('H*', sha1($seed . 'A'));
|
186 |
-
$iv = pack('H*', sha1($seed . 'C'));
|
187 |
-
|
188 |
-
// ciphers are used as per the nist.gov link below. also, see this link:
|
189 |
-
//
|
190 |
-
// http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives
|
191 |
-
switch (true) {
|
192 |
-
case class_exists('\phpseclib\Crypt\AES'):
|
193 |
-
$crypto = new AES(Base::MODE_CTR);
|
194 |
-
break;
|
195 |
-
case class_exists('\phpseclib\Crypt\Twofish'):
|
196 |
-
$crypto = new Twofish(Base::MODE_CTR);
|
197 |
-
break;
|
198 |
-
case class_exists('\phpseclib\Crypt\Blowfish'):
|
199 |
-
$crypto = new Blowfish(Base::MODE_CTR);
|
200 |
-
break;
|
201 |
-
case class_exists('\phpseclib\Crypt\TripleDES'):
|
202 |
-
$crypto = new TripleDES(Base::MODE_CTR);
|
203 |
-
break;
|
204 |
-
case class_exists('\phpseclib\Crypt\DES'):
|
205 |
-
$crypto = new DES(Base::MODE_CTR);
|
206 |
-
break;
|
207 |
-
case class_exists('\phpseclib\Crypt\RC4'):
|
208 |
-
$crypto = new RC4();
|
209 |
-
break;
|
210 |
-
default:
|
211 |
-
user_error(__CLASS__ . ' requires at least one symmetric cipher be loaded');
|
212 |
-
return false;
|
213 |
-
}
|
214 |
-
|
215 |
-
$crypto->setKey($key);
|
216 |
-
$crypto->setIV($iv);
|
217 |
-
$crypto->enableContinuousBuffer();
|
218 |
-
}
|
219 |
-
|
220 |
-
//return $crypto->encrypt(str_repeat("\0", $length));
|
221 |
-
|
222 |
-
// the following is based off of ANSI X9.31:
|
223 |
-
//
|
224 |
-
// http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf
|
225 |
-
//
|
226 |
-
// OpenSSL uses that same standard for it's random numbers:
|
227 |
-
//
|
228 |
-
// http://www.opensource.apple.com/source/OpenSSL/OpenSSL-38/openssl/fips-1.0/rand/fips_rand.c
|
229 |
-
// (do a search for "ANS X9.31 A.2.4")
|
230 |
-
$result = '';
|
231 |
-
while (strlen($result) < $length) {
|
232 |
-
$i = $crypto->encrypt(microtime()); // strlen(microtime()) == 21
|
233 |
-
$r = $crypto->encrypt($i ^ $v); // strlen($v) == 20
|
234 |
-
$v = $crypto->encrypt($r ^ $i); // strlen($r) == 20
|
235 |
-
$result.= $r;
|
236 |
-
}
|
237 |
-
return substr($result, 0, $length);
|
238 |
-
}
|
239 |
-
}
|
240 |
-
|
241 |
-
if (!function_exists('phpseclib_safe_serialize')) {
|
242 |
-
/**
|
243 |
-
* Safely serialize variables
|
244 |
-
*
|
245 |
-
* If a class has a private __sleep() method it'll give a fatal error on PHP 5.2 and earlier.
|
246 |
-
* PHP 5.3 will emit a warning.
|
247 |
-
*
|
248 |
-
* @param mixed $arr
|
249 |
-
* @access public
|
250 |
-
*/
|
251 |
-
function phpseclib_safe_serialize(&$arr)
|
252 |
-
{
|
253 |
-
if (is_object($arr)) {
|
254 |
-
return '';
|
255 |
-
}
|
256 |
-
if (!is_array($arr)) {
|
257 |
-
return serialize($arr);
|
258 |
-
}
|
259 |
-
// prevent circular array recursion
|
260 |
-
if (isset($arr['__phpseclib_marker'])) {
|
261 |
-
return '';
|
262 |
-
}
|
263 |
-
$safearr = array();
|
264 |
-
$arr['__phpseclib_marker'] = true;
|
265 |
-
foreach (array_keys($arr) as $key) {
|
266 |
-
// do not recurse on the '__phpseclib_marker' key itself, for smaller memory usage
|
267 |
-
if ($key !== '__phpseclib_marker') {
|
268 |
-
$safearr[$key] = phpseclib_safe_serialize($arr[$key]);
|
269 |
-
}
|
270 |
-
}
|
271 |
-
unset($arr['__phpseclib_marker']);
|
272 |
-
return serialize($safearr);
|
273 |
-
}
|
274 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Random Number Generator
|
5 |
+
*
|
6 |
+
* PHP version 5
|
7 |
+
*
|
8 |
+
* Here's a short example of how to use this library:
|
9 |
+
* <code>
|
10 |
+
* <?php
|
11 |
+
* include 'vendor/autoload.php';
|
12 |
+
*
|
13 |
+
* echo bin2hex(\phpseclib\Crypt\Random::string(8));
|
14 |
+
* ?>
|
15 |
+
* </code>
|
16 |
+
*
|
17 |
+
* @category Crypt
|
18 |
+
* @package Random
|
19 |
+
* @author Jim Wigginton <terrafrost@php.net>
|
20 |
+
* @copyright 2007 Jim Wigginton
|
21 |
+
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
22 |
+
* @link http://phpseclib.sourceforge.net
|
23 |
+
*/
|
24 |
+
|
25 |
+
namespace phpseclib\Crypt;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Pure-PHP Random Number Generator
|
29 |
+
*
|
30 |
+
* @package Random
|
31 |
+
* @author Jim Wigginton <terrafrost@php.net>
|
32 |
+
* @access public
|
33 |
+
*/
|
34 |
+
class Random
|
35 |
+
{
|
36 |
+
/**
|
37 |
+
* Generate a random string.
|
38 |
+
*
|
39 |
+
* Although microoptimizations are generally discouraged as they impair readability this function is ripe with
|
40 |
+
* microoptimizations because this function has the potential of being called a huge number of times.
|
41 |
+
* eg. for RSA key generation.
|
42 |
+
*
|
43 |
+
* @param int $length
|
44 |
+
* @return string
|
45 |
+
*/
|
46 |
+
static function string($length)
|
47 |
+
{
|
48 |
+
if (!$length) {
|
49 |
+
return '';
|
50 |
+
}
|
51 |
+
|
52 |
+
if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
|
53 |
+
try {
|
54 |
+
return \random_bytes($length);
|
55 |
+
} catch (\Throwable $e) {
|
56 |
+
// If a sufficient source of randomness is unavailable, random_bytes() will throw an
|
57 |
+
// object that implements the Throwable interface (Exception, TypeError, Error).
|
58 |
+
// We don't actually need to do anything here. The string() method should just continue
|
59 |
+
// as normal. Note, however, that if we don't have a sufficient source of randomness for
|
60 |
+
// random_bytes(), most of the other calls here will fail too, so we'll end up using
|
61 |
+
// the PHP implementation.
|
62 |
+
}
|
63 |
+
}
|
64 |
+
|
65 |
+
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
|
66 |
+
// method 1. prior to PHP 5.3 this would call rand() on windows hence the function_exists('class_alias') call.
|
67 |
+
// ie. class_alias is a function that was introduced in PHP 5.3
|
68 |
+
if (extension_loaded('mcrypt') && function_exists('class_alias')) {
|
69 |
+
return @mcrypt_create_iv($length);
|
70 |
+
}
|
71 |
+
// method 2. openssl_random_pseudo_bytes was introduced in PHP 5.3.0 but prior to PHP 5.3.4 there was,
|
72 |
+
// to quote <http://php.net/ChangeLog-5.php#5.3.4>, "possible blocking behavior". as of 5.3.4
|
73 |
+
// openssl_random_pseudo_bytes and mcrypt_create_iv do the exact same thing on Windows. ie. they both
|
74 |
+
// call php_win32_get_random_bytes():
|
75 |
+
//
|
76 |
+
// https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/openssl/openssl.c#L5008
|
77 |
+
// https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1392
|
78 |
+
//
|
79 |
+
// php_win32_get_random_bytes() is defined thusly:
|
80 |
+
//
|
81 |
+
// https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/win32/winutil.c#L80
|
82 |
+
//
|
83 |
+
// we're calling it, all the same, in the off chance that the mcrypt extension is not available
|
84 |
+
if (extension_loaded('openssl') && version_compare(PHP_VERSION, '5.3.4', '>=')) {
|
85 |
+
return openssl_random_pseudo_bytes($length);
|
86 |
+
}
|
87 |
+
} else {
|
88 |
+
// method 1. the fastest
|
89 |
+
if (extension_loaded('openssl')) {
|
90 |
+
return openssl_random_pseudo_bytes($length);
|
91 |
+
}
|
92 |
+
// method 2
|
93 |
+
static $fp = true;
|
94 |
+
if ($fp === true) {
|
95 |
+
// warning's will be output unles the error suppression operator is used. errors such as
|
96 |
+
// "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc.
|
97 |
+
$fp = @fopen('/dev/urandom', 'rb');
|
98 |
+
}
|
99 |
+
if ($fp !== true && $fp !== false) { // surprisingly faster than !is_bool() or is_resource()
|
100 |
+
return fread($fp, $length);
|
101 |
+
}
|
102 |
+
// method 3. pretty much does the same thing as method 2 per the following url:
|
103 |
+
// https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1391
|
104 |
+
// surprisingly slower than method 2. maybe that's because mcrypt_create_iv does a bunch of error checking that we're
|
105 |
+
// not doing. regardless, this'll only be called if this PHP script couldn't open /dev/urandom due to open_basedir
|
106 |
+
// restrictions or some such
|
107 |
+
if (extension_loaded('mcrypt')) {
|
108 |
+
return @mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
|
109 |
+
}
|
110 |
+
}
|
111 |
+
// at this point we have no choice but to use a pure-PHP CSPRNG
|
112 |
+
|
113 |
+
// cascade entropy across multiple PHP instances by fixing the session and collecting all
|
114 |
+
// environmental variables, including the previous session data and the current session
|
115 |
+
// data.
|
116 |
+
//
|
117 |
+
// mt_rand seeds itself by looking at the PID and the time, both of which are (relatively)
|
118 |
+
// easy to guess at. linux uses mouse clicks, keyboard timings, etc, as entropy sources, but
|
119 |
+
// PHP isn't low level to be able to use those as sources and on a web server there's not likely
|
120 |
+
// going to be a ton of keyboard or mouse action. web servers do have one thing that we can use
|
121 |
+
// however, a ton of people visiting the website. obviously you don't want to base your seeding
|
122 |
+
// soley on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled
|
123 |
+
// by the user and (2) this isn't just looking at the data sent by the current user - it's based
|
124 |
+
// on the data sent by all users. one user requests the page and a hash of their info is saved.
|
125 |
+
// another user visits the page and the serialization of their data is utilized along with the
|
126 |
+
// server envirnment stuff and a hash of the previous http request data (which itself utilizes
|
127 |
+
// a hash of the session data before that). certainly an attacker should be assumed to have
|
128 |
+
// full control over his own http requests. he, however, is not going to have control over
|
129 |
+
// everyone's http requests.
|
130 |
+
static $crypto = false, $v;
|
131 |
+
if ($crypto === false) {
|
132 |
+
// save old session data
|
133 |
+
$old_session_id = session_id();
|
134 |
+
$old_use_cookies = ini_get('session.use_cookies');
|
135 |
+
$old_session_cache_limiter = session_cache_limiter();
|
136 |
+
$_OLD_SESSION = isset($_SESSION) ? $_SESSION : false;
|
137 |
+
if ($old_session_id != '') {
|
138 |
+
session_write_close();
|
139 |
+
}
|
140 |
+
|
141 |
+
session_id(1);
|
142 |
+
ini_set('session.use_cookies', 0);
|
143 |
+
session_cache_limiter('');
|
144 |
+
session_start();
|
145 |
+
|
146 |
+
$v = $seed = $_SESSION['seed'] = pack('H*', sha1(
|
147 |
+
(isset($_SERVER) ? phpseclib_safe_serialize($_SERVER) : '') .
|
148 |
+
(isset($_POST) ? phpseclib_safe_serialize($_POST) : '') .
|
149 |
+
(isset($_GET) ? phpseclib_safe_serialize($_GET) : '') .
|
150 |
+
(isset($_COOKIE) ? phpseclib_safe_serialize($_COOKIE) : '') .
|
151 |
+
phpseclib_safe_serialize($GLOBALS) .
|
152 |
+
phpseclib_safe_serialize($_SESSION) .
|
153 |
+
phpseclib_safe_serialize($_OLD_SESSION)
|
154 |
+
));
|
155 |
+
if (!isset($_SESSION['count'])) {
|
156 |
+
$_SESSION['count'] = 0;
|
157 |
+
}
|
158 |
+
$_SESSION['count']++;
|
159 |
+
|
160 |
+
session_write_close();
|
161 |
+
|
162 |
+
// restore old session data
|
163 |
+
if ($old_session_id != '') {
|
164 |
+
session_id($old_session_id);
|
165 |
+
session_start();
|
166 |
+
ini_set('session.use_cookies', $old_use_cookies);
|
167 |
+
session_cache_limiter($old_session_cache_limiter);
|
168 |
+
} else {
|
169 |
+
if ($_OLD_SESSION !== false) {
|
170 |
+
$_SESSION = $_OLD_SESSION;
|
171 |
+
unset($_OLD_SESSION);
|
172 |
+
} else {
|
173 |
+
unset($_SESSION);
|
174 |
+
}
|
175 |
+
}
|
176 |
+
|
177 |
+
// in SSH2 a shared secret and an exchange hash are generated through the key exchange process.
|
178 |
+
// the IV client to server is the hash of that "nonce" with the letter A and for the encryption key it's the letter C.
|
179 |
+
// if the hash doesn't produce enough a key or an IV that's long enough concat successive hashes of the
|
180 |
+
// original hash and the current hash. we'll be emulating that. for more info see the following URL:
|
181 |
+
//
|
182 |
+
// http://tools.ietf.org/html/rfc4253#section-7.2
|
183 |
+
//
|
184 |
+
// see the is_string($crypto) part for an example of how to expand the keys
|
185 |
+
$key = pack('H*', sha1($seed . 'A'));
|
186 |
+
$iv = pack('H*', sha1($seed . 'C'));
|
187 |
+
|
188 |
+
// ciphers are used as per the nist.gov link below. also, see this link:
|
189 |
+
//
|
190 |
+
// http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives
|
191 |
+
switch (true) {
|
192 |
+
case class_exists('\phpseclib\Crypt\AES'):
|
193 |
+
$crypto = new AES(Base::MODE_CTR);
|
194 |
+
break;
|
195 |
+
case class_exists('\phpseclib\Crypt\Twofish'):
|
196 |
+
$crypto = new Twofish(Base::MODE_CTR);
|
197 |
+
break;
|
198 |
+
case class_exists('\phpseclib\Crypt\Blowfish'):
|
199 |
+
$crypto = new Blowfish(Base::MODE_CTR);
|
200 |
+
break;
|
201 |
+
case class_exists('\phpseclib\Crypt\TripleDES'):
|
202 |
+
$crypto = new TripleDES(Base::MODE_CTR);
|
203 |
+
break;
|
204 |
+
case class_exists('\phpseclib\Crypt\DES'):
|
205 |
+
$crypto = new DES(Base::MODE_CTR);
|
206 |
+
break;
|
207 |
+
case class_exists('\phpseclib\Crypt\RC4'):
|
208 |
+
$crypto = new RC4();
|
209 |
+
break;
|
210 |
+
default:
|
211 |
+
user_error(__CLASS__ . ' requires at least one symmetric cipher be loaded');
|
212 |
+
return false;
|
213 |
+
}
|
214 |
+
|
215 |
+
$crypto->setKey($key);
|
216 |
+
$crypto->setIV($iv);
|
217 |
+
$crypto->enableContinuousBuffer();
|
218 |
+
}
|
219 |
+
|
220 |
+
//return $crypto->encrypt(str_repeat("\0", $length));
|
221 |
+
|
222 |
+
// the following is based off of ANSI X9.31:
|
223 |
+
//
|
224 |
+
// http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf
|
225 |
+
//
|
226 |
+
// OpenSSL uses that same standard for it's random numbers:
|
227 |
+
//
|
228 |
+
// http://www.opensource.apple.com/source/OpenSSL/OpenSSL-38/openssl/fips-1.0/rand/fips_rand.c
|
229 |
+
// (do a search for "ANS X9.31 A.2.4")
|
230 |
+
$result = '';
|
231 |
+
while (strlen($result) < $length) {
|
232 |
+
$i = $crypto->encrypt(microtime()); // strlen(microtime()) == 21
|
233 |
+
$r = $crypto->encrypt($i ^ $v); // strlen($v) == 20
|
234 |
+
$v = $crypto->encrypt($r ^ $i); // strlen($r) == 20
|
235 |
+
$result.= $r;
|
236 |
+
}
|
237 |
+
return substr($result, 0, $length);
|
238 |
+
}
|
239 |
+
}
|
240 |
+
|
241 |
+
if (!function_exists('phpseclib_safe_serialize')) {
|
242 |
+
/**
|
243 |
+
* Safely serialize variables
|
244 |
+
*
|
245 |
+
* If a class has a private __sleep() method it'll give a fatal error on PHP 5.2 and earlier.
|
246 |
+
* PHP 5.3 will emit a warning.
|
247 |
+
*
|
248 |
+
* @param mixed $arr
|
249 |
+
* @access public
|
250 |
+
*/
|
251 |
+
function phpseclib_safe_serialize(&$arr)
|
252 |
+
{
|
253 |
+
if (is_object($arr)) {
|
254 |
+
return '';
|
255 |
+
}
|
256 |
+
if (!is_array($arr)) {
|
257 |
+
return serialize($arr);
|
258 |
+
}
|
259 |
+
// prevent circular array recursion
|
260 |
+
if (isset($arr['__phpseclib_marker'])) {
|
261 |
+
return '';
|
262 |
+
}
|
263 |
+
$safearr = array();
|
264 |
+
$arr['__phpseclib_marker'] = true;
|
265 |
+
foreach (array_keys($arr) as $key) {
|
266 |
+
// do not recurse on the '__phpseclib_marker' key itself, for smaller memory usage
|
267 |
+
if ($key !== '__phpseclib_marker') {
|
268 |
+
$safearr[$key] = phpseclib_safe_serialize($arr[$key]);
|
269 |
+
}
|
270 |
+
}
|
271 |
+
unset($arr['__phpseclib_marker']);
|
272 |
+
return serialize($safearr);
|
273 |
+
}
|
274 |
+
}
|
vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php
CHANGED
@@ -1,936 +1,936 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Pure-PHP implementation of Rijndael.
|
5 |
-
*
|
6 |
-
* Uses mcrypt, if available/possible, and an internal implementation, otherwise.
|
7 |
-
*
|
8 |
-
* PHP version 5
|
9 |
-
*
|
10 |
-
* If {@link self::setBlockLength() setBlockLength()} isn't called, it'll be assumed to be 128 bits. If
|
11 |
-
* {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
|
12 |
-
* {@link self::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's
|
13 |
-
* 136-bits it'll be null-padded to 192-bits and 192 bits will be the key length until
|
14 |
-
* {@link self::setKey() setKey()} is called, again, at which point, it'll be recalculated.
|
15 |
-
*
|
16 |
-
* Not all Rijndael implementations may support 160-bits or 224-bits as the block length / key length. mcrypt, for example,
|
17 |
-
* does not. AES, itself, only supports block lengths of 128 and key lengths of 128, 192, and 256.
|
18 |
-
* {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=10 Rijndael-ammended.pdf#page=10} defines the
|
19 |
-
* algorithm for block lengths of 192 and 256 but not for block lengths / key lengths of 160 and 224. Indeed, 160 and 224
|
20 |
-
* are first defined as valid key / block lengths in
|
21 |
-
* {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=44 Rijndael-ammended.pdf#page=44}:
|
22 |
-
* Extensions: Other block and Cipher Key lengths.
|
23 |
-
* Note: Use of 160/224-bit Keys must be explicitly set by setKeyLength(160) respectively setKeyLength(224).
|
24 |
-
*
|
25 |
-
* {@internal The variable names are the same as those in
|
26 |
-
* {@link http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf#page=10 fips-197.pdf#page=10}.}}
|
27 |
-
*
|
28 |
-
* Here's a short example of how to use this library:
|
29 |
-
* <code>
|
30 |
-
* <?php
|
31 |
-
* include 'vendor/autoload.php';
|
32 |
-
*
|
33 |
-
* $rijndael = new \phpseclib\Crypt\Rijndael();
|
34 |
-
*
|
35 |
-
* $rijndael->setKey('abcdefghijklmnop');
|
36 |
-
*
|
37 |
-
* $size = 10 * 1024;
|
38 |
-
* $plaintext = '';
|
39 |
-
* for ($i = 0; $i < $size; $i++) {
|
40 |
-
* $plaintext.= 'a';
|
41 |
-
* }
|
42 |
-
*
|
43 |
-
* echo $rijndael->decrypt($rijndael->encrypt($plaintext));
|
44 |
-
* ?>
|
45 |
-
* </code>
|
46 |
-
*
|
47 |
-
* @category Crypt
|
48 |
-
* @package Rijndael
|
49 |
-
* @author Jim Wigginton <terrafrost@php.net>
|
50 |
-
* @copyright 2008 Jim Wigginton
|
51 |
-
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
52 |
-
* @link http://phpseclib.sourceforge.net
|
53 |
-
*/
|
54 |
-
|
55 |
-
namespace phpseclib\Crypt;
|
56 |
-
|
57 |
-
/**
|
58 |
-
* Pure-PHP implementation of Rijndael.
|
59 |
-
*
|
60 |
-
* @package Rijndael
|
61 |
-
* @author Jim Wigginton <terrafrost@php.net>
|
62 |
-
* @access public
|
63 |
-
*/
|
64 |
-
class Rijndael extends Base
|
65 |
-
{
|
66 |
-
/**
|
67 |
-
* The mcrypt specific name of the cipher
|
68 |
-
*
|
69 |
-
* Mcrypt is useable for 128/192/256-bit $block_size/$key_length. For 160/224 not.
|
70 |
-
* \phpseclib\Crypt\Rijndael determines automatically whether mcrypt is useable
|
71 |
-
* or not for the current $block_size/$key_length.
|
72 |
-
* In case of, $cipher_name_mcrypt will be set dynamically at run time accordingly.
|
73 |
-
*
|
74 |
-
* @see \phpseclib\Crypt\Base::cipher_name_mcrypt
|
75 |
-
* @see \phpseclib\Crypt\Base::engine
|
76 |
-
* @see self::isValidEngine()
|
77 |
-
* @var string
|
78 |
-
* @access private
|
79 |
-
*/
|
80 |
-
var $cipher_name_mcrypt = 'rijndael-128';
|
81 |
-
|
82 |
-
/**
|
83 |
-
* The default salt used by setPassword()
|
84 |
-
*
|
85 |
-
* @see \phpseclib\Crypt\Base::password_default_salt
|
86 |
-
* @see \phpseclib\Crypt\Base::setPassword()
|
87 |
-
* @var string
|
88 |
-
* @access private
|
89 |
-
*/
|
90 |
-
var $password_default_salt = 'phpseclib';
|
91 |
-
|
92 |
-
/**
|
93 |
-
* The Key Schedule
|
94 |
-
*
|
95 |
-
* @see self::_setup()
|
96 |
-
* @var array
|
97 |
-
* @access private
|
98 |
-
*/
|
99 |
-
var $w;
|
100 |
-
|
101 |
-
/**
|
102 |
-
* The Inverse Key Schedule
|
103 |
-
*
|
104 |
-
* @see self::_setup()
|
105 |
-
* @var array
|
106 |
-
* @access private
|
107 |
-
*/
|
108 |
-
var $dw;
|
109 |
-
|
110 |
-
/**
|
111 |
-
* The Block Length divided by 32
|
112 |
-
*
|
113 |
-
* @see self::setBlockLength()
|
114 |
-
* @var int
|
115 |
-
* @access private
|
116 |
-
* @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4. Exists in conjunction with $block_size
|
117 |
-
* because the encryption / decryption / key schedule creation requires this number and not $block_size. We could
|
118 |
-
* derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
|
119 |
-
* of that, we'll just precompute it once.
|
120 |
-
*/
|
121 |
-
var $Nb = 4;
|
122 |
-
|
123 |
-
/**
|
124 |
-
* The Key Length (in bytes)
|
125 |
-
*
|
126 |
-
* @see self::setKeyLength()
|
127 |
-
* @var int
|
128 |
-
* @access private
|
129 |
-
* @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk
|
130 |
-
* because the encryption / decryption / key schedule creation requires this number and not $key_length. We could
|
131 |
-
* derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
|
132 |
-
* of that, we'll just precompute it once.
|
133 |
-
*/
|
134 |
-
var $key_length = 16;
|
135 |
-
|
136 |
-
/**
|
137 |
-
* The Key Length divided by 32
|
138 |
-
*
|
139 |
-
* @see self::setKeyLength()
|
140 |
-
* @var int
|
141 |
-
* @access private
|
142 |
-
* @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4
|
143 |
-
*/
|
144 |
-
var $Nk = 4;
|
145 |
-
|
146 |
-
/**
|
147 |
-
* The Number of Rounds
|
148 |
-
*
|
149 |
-
* @var int
|
150 |
-
* @access private
|
151 |
-
* @internal The max value is 14, the min value is 10.
|
152 |
-
*/
|
153 |
-
var $Nr;
|
154 |
-
|
155 |
-
/**
|
156 |
-
* Shift offsets
|
157 |
-
*
|
158 |
-
* @var array
|
159 |
-
* @access private
|
160 |
-
*/
|
161 |
-
var $c;
|
162 |
-
|
163 |
-
/**
|
164 |
-
* Holds the last used key- and block_size information
|
165 |
-
*
|
166 |
-
* @var array
|
167 |
-
* @access private
|
168 |
-
*/
|
169 |
-
var $kl;
|
170 |
-
|
171 |
-
/**
|
172 |
-
* Sets the key length.
|
173 |
-
*
|
174 |
-
* Valid key lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to
|
175 |
-
* 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
|
176 |
-
*
|
177 |
-
* Note: phpseclib extends Rijndael (and AES) for using 160- and 224-bit keys but they are officially not defined
|
178 |
-
* and the most (if not all) implementations are not able using 160/224-bit keys but round/pad them up to
|
179 |
-
* 192/256 bits as, for example, mcrypt will do.
|
180 |
-
*
|
181 |
-
* That said, if you want be compatible with other Rijndael and AES implementations,
|
182 |
-
* you should not setKeyLength(160) or setKeyLength(224).
|
183 |
-
*
|
184 |
-
* Additional: In case of 160- and 224-bit keys, phpseclib will/can, for that reason, not use
|
185 |
-
* the mcrypt php extension, even if available.
|
186 |
-
* This results then in slower encryption.
|
187 |
-
*
|
188 |
-
* @access public
|
189 |
-
* @param int $length
|
190 |
-
*/
|
191 |
-
function setKeyLength($length)
|
192 |
-
{
|
193 |
-
switch (true) {
|
194 |
-
case $length <= 128:
|
195 |
-
$this->key_length = 16;
|
196 |
-
break;
|
197 |
-
case $length <= 160:
|
198 |
-
$this->key_length = 20;
|
199 |
-
break;
|
200 |
-
case $length <= 192:
|
201 |
-
$this->key_length = 24;
|
202 |
-
break;
|
203 |
-
case $length <= 224:
|
204 |
-
$this->key_length = 28;
|
205 |
-
break;
|
206 |
-
default:
|
207 |
-
$this->key_length = 32;
|
208 |
-
}
|
209 |
-
|
210 |
-
parent::setKeyLength($length);
|
211 |
-
}
|
212 |
-
|
213 |
-
/**
|
214 |
-
* Sets the block length
|
215 |
-
*
|
216 |
-
* Valid block lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to
|
217 |
-
* 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
|
218 |
-
*
|
219 |
-
* @access public
|
220 |
-
* @param int $length
|
221 |
-
*/
|
222 |
-
function setBlockLength($length)
|
223 |
-
{
|
224 |
-
$length >>= 5;
|
225 |
-
if ($length > 8) {
|
226 |
-
$length = 8;
|
227 |
-
} elseif ($length < 4) {
|
228 |
-
$length = 4;
|
229 |
-
}
|
230 |
-
$this->Nb = $length;
|
231 |
-
$this->block_size = $length << 2;
|
232 |
-
$this->changed = true;
|
233 |
-
$this->_setEngine();
|
234 |
-
}
|
235 |
-
|
236 |
-
/**
|
237 |
-
* Test for engine validity
|
238 |
-
*
|
239 |
-
* This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine()
|
240 |
-
*
|
241 |
-
* @see \phpseclib\Crypt\Base::__construct()
|
242 |
-
* @param int $engine
|
243 |
-
* @access public
|
244 |
-
* @return bool
|
245 |
-
*/
|
246 |
-
function isValidEngine($engine)
|
247 |
-
{
|
248 |
-
switch ($engine) {
|
249 |
-
case self::ENGINE_OPENSSL:
|
250 |
-
if ($this->block_size != 16) {
|
251 |
-
return false;
|
252 |
-
}
|
253 |
-
$this->cipher_name_openssl_ecb = 'aes-' . ($this->key_length << 3) . '-ecb';
|
254 |
-
$this->cipher_name_openssl = 'aes-' . ($this->key_length << 3) . '-' . $this->_openssl_translate_mode();
|
255 |
-
break;
|
256 |
-
case self::ENGINE_MCRYPT:
|
257 |
-
$this->cipher_name_mcrypt = 'rijndael-' . ($this->block_size << 3);
|
258 |
-
if ($this->key_length % 8) { // is it a 160/224-bit key?
|
259 |
-
// mcrypt is not usable for them, only for 128/192/256-bit keys
|
260 |
-
return false;
|
261 |
-
}
|
262 |
-
}
|
263 |
-
|
264 |
-
return parent::isValidEngine($engine);
|
265 |
-
}
|
266 |
-
|
267 |
-
/**
|
268 |
-
* Encrypts a block
|
269 |
-
*
|
270 |
-
* @access private
|
271 |
-
* @param string $in
|
272 |
-
* @return string
|
273 |
-
*/
|
274 |
-
function _encryptBlock($in)
|
275 |
-
{
|
276 |
-
static $tables;
|
277 |
-
if (empty($tables)) {
|
278 |
-
$tables = &$this->_getTables();
|
279 |
-
}
|
280 |
-
$t0 = $tables[0];
|
281 |
-
$t1 = $tables[1];
|
282 |
-
$t2 = $tables[2];
|
283 |
-
$t3 = $tables[3];
|
284 |
-
$sbox = $tables[4];
|
285 |
-
|
286 |
-
$state = array();
|
287 |
-
$words = unpack('N*', $in);
|
288 |
-
|
289 |
-
$c = $this->c;
|
290 |
-
$w = $this->w;
|
291 |
-
$Nb = $this->Nb;
|
292 |
-
$Nr = $this->Nr;
|
293 |
-
|
294 |
-
// addRoundKey
|
295 |
-
$wc = $Nb - 1;
|
296 |
-
foreach ($words as $word) {
|
297 |
-
$state[] = $word ^ $w[++$wc];
|
298 |
-
}
|
299 |
-
|
300 |
-
// fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components -
|
301 |
-
// subBytes, shiftRows, mixColumns, and addRoundKey. fips-197.pdf#page=30, "Implementation Suggestions Regarding
|
302 |
-
// Various Platforms" suggests that performs enhanced implementations are described in Rijndael-ammended.pdf.
|
303 |
-
// Rijndael-ammended.pdf#page=20, "Implementation aspects / 32-bit processor", discusses such an optimization.
|
304 |
-
// Unfortunately, the description given there is not quite correct. Per aes.spec.v316.pdf#page=19 [1],
|
305 |
-
// equation (7.4.7) is supposed to use addition instead of subtraction, so we'll do that here, as well.
|
306 |
-
|
307 |
-
// [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf
|
308 |
-
$temp = array();
|
309 |
-
for ($round = 1; $round < $Nr; ++$round) {
|
310 |
-
$i = 0; // $c[0] == 0
|
311 |
-
$j = $c[1];
|
312 |
-
$k = $c[2];
|
313 |
-
$l = $c[3];
|
314 |
-
|
315 |
-
while ($i < $Nb) {
|
316 |
-
$temp[$i] = $t0[$state[$i] >> 24 & 0x000000FF] ^
|
317 |
-
$t1[$state[$j] >> 16 & 0x000000FF] ^
|
318 |
-
$t2[$state[$k] >> 8 & 0x000000FF] ^
|
319 |
-
$t3[$state[$l] & 0x000000FF] ^
|
320 |
-
$w[++$wc];
|
321 |
-
++$i;
|
322 |
-
$j = ($j + 1) % $Nb;
|
323 |
-
$k = ($k + 1) % $Nb;
|
324 |
-
$l = ($l + 1) % $Nb;
|
325 |
-
}
|
326 |
-
$state = $temp;
|
327 |
-
}
|
328 |
-
|
329 |
-
// subWord
|
330 |
-
for ($i = 0; $i < $Nb; ++$i) {
|
331 |
-
$state[$i] = $sbox[$state[$i] & 0x000000FF] |
|
332 |
-
($sbox[$state[$i] >> 8 & 0x000000FF] << 8) |
|
333 |
-
($sbox[$state[$i] >> 16 & 0x000000FF] << 16) |
|
334 |
-
($sbox[$state[$i] >> 24 & 0x000000FF] << 24);
|
335 |
-
}
|
336 |
-
|
337 |
-
// shiftRows + addRoundKey
|
338 |
-
$i = 0; // $c[0] == 0
|
339 |
-
$j = $c[1];
|
340 |
-
$k = $c[2];
|
341 |
-
$l = $c[3];
|
342 |
-
while ($i < $Nb) {
|
343 |
-
$temp[$i] = ($state[$i] & 0xFF000000) ^
|
344 |
-
($state[$j] & 0x00FF0000) ^
|
345 |
-
($state[$k] & 0x0000FF00) ^
|
346 |
-
($state[$l] & 0x000000FF) ^
|
347 |
-
$w[$i];
|
348 |
-
++$i;
|
349 |
-
$j = ($j + 1) % $Nb;
|
350 |
-
$k = ($k + 1) % $Nb;
|
351 |
-
$l = ($l + 1) % $Nb;
|
352 |
-
}
|
353 |
-
|
354 |
-
switch ($Nb) {
|
355 |
-
case 8:
|
356 |
-
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]);
|
357 |
-
case 7:
|
358 |
-
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]);
|
359 |
-
case 6:
|
360 |
-
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]);
|
361 |
-
case 5:
|
362 |
-
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]);
|
363 |
-
default:
|
364 |
-
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]);
|
365 |
-
}
|
366 |
-
}
|
367 |
-
|
368 |
-
/**
|
369 |
-
* Decrypts a block
|
370 |
-
*
|
371 |
-
* @access private
|
372 |
-
* @param string $in
|
373 |
-
* @return string
|
374 |
-
*/
|
375 |
-
function _decryptBlock($in)
|
376 |
-
{
|
377 |
-
static $invtables;
|
378 |
-
if (empty($invtables)) {
|
379 |
-
$invtables = &$this->_getInvTables();
|
380 |
-
}
|
381 |
-
$dt0 = $invtables[0];
|
382 |
-
$dt1 = $invtables[1];
|
383 |
-
$dt2 = $invtables[2];
|
384 |
-
$dt3 = $invtables[3];
|
385 |
-
$isbox = $invtables[4];
|
386 |
-
|
387 |
-
$state = array();
|
388 |
-
$words = unpack('N*', $in);
|
389 |
-
|
390 |
-
$c = $this->c;
|
391 |
-
$dw = $this->dw;
|
392 |
-
$Nb = $this->Nb;
|
393 |
-
$Nr = $this->Nr;
|
394 |
-
|
395 |
-
// addRoundKey
|
396 |
-
$wc = $Nb - 1;
|
397 |
-
foreach ($words as $word) {
|
398 |
-
$state[] = $word ^ $dw[++$wc];
|
399 |
-
}
|
400 |
-
|
401 |
-
$temp = array();
|
402 |
-
for ($round = $Nr - 1; $round > 0; --$round) {
|
403 |
-
$i = 0; // $c[0] == 0
|
404 |
-
$j = $Nb - $c[1];
|
405 |
-
$k = $Nb - $c[2];
|
406 |
-
$l = $Nb - $c[3];
|
407 |
-
|
408 |
-
while ($i < $Nb) {
|
409 |
-
$temp[$i] = $dt0[$state[$i] >> 24 & 0x000000FF] ^
|
410 |
-
$dt1[$state[$j] >> 16 & 0x000000FF] ^
|
411 |
-
$dt2[$state[$k] >> 8 & 0x000000FF] ^
|
412 |
-
$dt3[$state[$l] & 0x000000FF] ^
|
413 |
-
$dw[++$wc];
|
414 |
-
++$i;
|
415 |
-
$j = ($j + 1) % $Nb;
|
416 |
-
$k = ($k + 1) % $Nb;
|
417 |
-
$l = ($l + 1) % $Nb;
|
418 |
-
}
|
419 |
-
$state = $temp;
|
420 |
-
}
|
421 |
-
|
422 |
-
// invShiftRows + invSubWord + addRoundKey
|
423 |
-
$i = 0; // $c[0] == 0
|
424 |
-
$j = $Nb - $c[1];
|
425 |
-
$k = $Nb - $c[2];
|
426 |
-
$l = $Nb - $c[3];
|
427 |
-
|
428 |
-
while ($i < $Nb) {
|
429 |
-
$word = ($state[$i] & 0xFF000000) |
|
430 |
-
($state[$j] & 0x00FF0000) |
|
431 |
-
($state[$k] & 0x0000FF00) |
|
432 |
-
($state[$l] & 0x000000FF);
|
433 |
-
|
434 |
-
$temp[$i] = $dw[$i] ^ ($isbox[$word & 0x000000FF] |
|
435 |
-
($isbox[$word >> 8 & 0x000000FF] << 8) |
|
436 |
-
($isbox[$word >> 16 & 0x000000FF] << 16) |
|
437 |
-
($isbox[$word >> 24 & 0x000000FF] << 24));
|
438 |
-
++$i;
|
439 |
-
$j = ($j + 1) % $Nb;
|
440 |
-
$k = ($k + 1) % $Nb;
|
441 |
-
$l = ($l + 1) % $Nb;
|
442 |
-
}
|
443 |
-
|
444 |
-
switch ($Nb) {
|
445 |
-
case 8:
|
446 |
-
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]);
|
447 |
-
case 7:
|
448 |
-
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]);
|
449 |
-
case 6:
|
450 |
-
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]);
|
451 |
-
case 5:
|
452 |
-
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]);
|
453 |
-
default:
|
454 |
-
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]);
|
455 |
-
}
|
456 |
-
}
|
457 |
-
|
458 |
-
/**
|
459 |
-
* Setup the key (expansion)
|
460 |
-
*
|
461 |
-
* @see \phpseclib\Crypt\Base::_setupKey()
|
462 |
-
* @access private
|
463 |
-
*/
|
464 |
-
function _setupKey()
|
465 |
-
{
|
466 |
-
// Each number in $rcon is equal to the previous number multiplied by two in Rijndael's finite field.
|
467 |
-
// See http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse
|
468 |
-
static $rcon = array(0,
|
469 |
-
0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
|
470 |
-
0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
|
471 |
-
0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000,
|
472 |
-
0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000,
|
473 |
-
0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000,
|
474 |
-
0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000
|
475 |
-
);
|
476 |
-
|
477 |
-
if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_length === $this->kl['key_length'] && $this->block_size === $this->kl['block_size']) {
|
478 |
-
// already expanded
|
479 |
-
return;
|
480 |
-
}
|
481 |
-
$this->kl = array('key' => $this->key, 'key_length' => $this->key_length, 'block_size' => $this->block_size);
|
482 |
-
|
483 |
-
$this->Nk = $this->key_length >> 2;
|
484 |
-
// see Rijndael-ammended.pdf#page=44
|
485 |
-
$this->Nr = max($this->Nk, $this->Nb) + 6;
|
486 |
-
|
487 |
-
// shift offsets for Nb = 5, 7 are defined in Rijndael-ammended.pdf#page=44,
|
488 |
-
// "Table 8: Shift offsets in Shiftrow for the alternative block lengths"
|
489 |
-
// shift offsets for Nb = 4, 6, 8 are defined in Rijndael-ammended.pdf#page=14,
|
490 |
-
// "Table 2: Shift offsets for different block lengths"
|
491 |
-
switch ($this->Nb) {
|
492 |
-
case 4:
|
493 |
-
case 5:
|
494 |
-
case 6:
|
495 |
-
$this->c = array(0, 1, 2, 3);
|
496 |
-
break;
|
497 |
-
case 7:
|
498 |
-
$this->c = array(0, 1, 2, 4);
|
499 |
-
break;
|
500 |
-
case 8:
|
501 |
-
$this->c = array(0, 1, 3, 4);
|
502 |
-
}
|
503 |
-
|
504 |
-
$w = array_values(unpack('N*words', $this->key));
|
505 |
-
|
506 |
-
$length = $this->Nb * ($this->Nr + 1);
|
507 |
-
for ($i = $this->Nk; $i < $length; $i++) {
|
508 |
-
$temp = $w[$i - 1];
|
509 |
-
if ($i % $this->Nk == 0) {
|
510 |
-
// according to <http://php.net/language.types.integer>, "the size of an integer is platform-dependent".
|
511 |
-
// on a 32-bit machine, it's 32-bits, and on a 64-bit machine, it's 64-bits. on a 32-bit machine,
|
512 |
-
// 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit machine, it equals 0xFFFFFFFF00. as such, doing 'and'
|
513 |
-
// with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is.
|
514 |
-
$temp = (($temp << 8) & 0xFFFFFF00) | (($temp >> 24) & 0x000000FF); // rotWord
|
515 |
-
$temp = $this->_subWord($temp) ^ $rcon[$i / $this->Nk];
|
516 |
-
} elseif ($this->Nk > 6 && $i % $this->Nk == 4) {
|
517 |
-
$temp = $this->_subWord($temp);
|
518 |
-
}
|
519 |
-
$w[$i] = $w[$i - $this->Nk] ^ $temp;
|
520 |
-
}
|
521 |
-
|
522 |
-
// convert the key schedule from a vector of $Nb * ($Nr + 1) length to a matrix with $Nr + 1 rows and $Nb columns
|
523 |
-
// and generate the inverse key schedule. more specifically,
|
524 |
-
// according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=23> (section 5.3.3),
|
525 |
-
// "The key expansion for the Inverse Cipher is defined as follows:
|
526 |
-
// 1. Apply the Key Expansion.
|
527 |
-
// 2. Apply InvMixColumn to all Round Keys except the first and the last one."
|
528 |
-
// also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher"
|
529 |
-
list($dt0, $dt1, $dt2, $dt3) = $this->_getInvTables();
|
530 |
-
$temp = $this->w = $this->dw = array();
|
531 |
-
for ($i = $row = $col = 0; $i < $length; $i++, $col++) {
|
532 |
-
if ($col == $this->Nb) {
|
533 |
-
if ($row == 0) {
|
534 |
-
$this->dw[0] = $this->w[0];
|
535 |
-
} else {
|
536 |
-
// subWord + invMixColumn + invSubWord = invMixColumn
|
537 |
-
$j = 0;
|
538 |
-
while ($j < $this->Nb) {
|
539 |
-
$dw = $this->_subWord($this->w[$row][$j]);
|
540 |
-
$temp[$j] = $dt0[$dw >> 24 & 0x000000FF] ^
|
541 |
-
$dt1[$dw >> 16 & 0x000000FF] ^
|
542 |
-
$dt2[$dw >> 8 & 0x000000FF] ^
|
543 |
-
$dt3[$dw & 0x000000FF];
|
544 |
-
$j++;
|
545 |
-
}
|
546 |
-
$this->dw[$row] = $temp;
|
547 |
-
}
|
548 |
-
|
549 |
-
$col = 0;
|
550 |
-
$row++;
|
551 |
-
}
|
552 |
-
$this->w[$row][$col] = $w[$i];
|
553 |
-
}
|
554 |
-
|
555 |
-
$this->dw[$row] = $this->w[$row];
|
556 |
-
|
557 |
-
// Converting to 1-dim key arrays (both ascending)
|
558 |
-
$this->dw = array_reverse($this->dw);
|
559 |
-
$w = array_pop($this->w);
|
560 |
-
$dw = array_pop($this->dw);
|
561 |
-
foreach ($this->w as $r => $wr) {
|
562 |
-
foreach ($wr as $c => $wc) {
|
563 |
-
$w[] = $wc;
|
564 |
-
$dw[] = $this->dw[$r][$c];
|
565 |
-
}
|
566 |
-
}
|
567 |
-
$this->w = $w;
|
568 |
-
$this->dw = $dw;
|
569 |
-
}
|
570 |
-
|
571 |
-
/**
|
572 |
-
* Performs S-Box substitutions
|
573 |
-
*
|
574 |
-
* @access private
|
575 |
-
* @param int $word
|
576 |
-
*/
|
577 |
-
function _subWord($word)
|
578 |
-
{
|
579 |
-
static $sbox;
|
580 |
-
if (empty($sbox)) {
|
581 |
-
list(, , , , $sbox) = $this->_getTables();
|
582 |
-
}
|
583 |
-
|
584 |
-
return $sbox[$word & 0x000000FF] |
|
585 |
-
($sbox[$word >> 8 & 0x000000FF] << 8) |
|
586 |
-
($sbox[$word >> 16 & 0x000000FF] << 16) |
|
587 |
-
($sbox[$word >> 24 & 0x000000FF] << 24);
|
588 |
-
}
|
589 |
-
|
590 |
-
/**
|
591 |
-
* Provides the mixColumns and sboxes tables
|
592 |
-
*
|
593 |
-
* @see self::_encryptBlock()
|
594 |
-
* @see self::_setupInlineCrypt()
|
595 |
-
* @see self::_subWord()
|
596 |
-
* @access private
|
597 |
-
* @return array &$tables
|
598 |
-
*/
|
599 |
-
function &_getTables()
|
600 |
-
{
|
601 |
-
static $tables;
|
602 |
-
if (empty($tables)) {
|
603 |
-
// according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=19> (section 5.2.1),
|
604 |
-
// precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so
|
605 |
-
// those are the names we'll use.
|
606 |
-
$t3 = array_map('intval', array(
|
607 |
-
// with array_map('intval', ...) we ensure we have only int's and not
|
608 |
-
// some slower floats converted by php automatically on high values
|
609 |
-
0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
|
610 |
-
0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC,
|
611 |
-
0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB,
|
612 |
-
0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B,
|
613 |
-
0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83,
|
614 |
-
0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A,
|
615 |
-
0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F,
|
616 |
-
0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA,
|
617 |
-
0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B,
|
618 |
-
0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713,
|
619 |
-
0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6,
|
620 |
-
0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85,
|
621 |
-
0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411,
|
622 |
-
0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B,
|
623 |
-
0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1,
|
624 |
-
0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF,
|
625 |
-
0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E,
|
626 |
-
0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6,
|
627 |
-
0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B,
|
628 |
-
0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD,
|
629 |
-
0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8,
|
630 |
-
0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2,
|
631 |
-
0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049,
|
632 |
-
0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810,
|
633 |
-
0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197,
|
634 |
-
0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F,
|
635 |
-
0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C,
|
636 |
-
0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927,
|
637 |
-
0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733,
|
638 |
-
0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5,
|
639 |
-
0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0,
|
640 |
-
0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C
|
641 |
-
));
|
642 |
-
|
643 |
-
foreach ($t3 as $t3i) {
|
644 |
-
$t0[] = (($t3i << 24) & 0xFF000000) | (($t3i >> 8) & 0x00FFFFFF);
|
645 |
-
$t1[] = (($t3i << 16) & 0xFFFF0000) | (($t3i >> 16) & 0x0000FFFF);
|
646 |
-
$t2[] = (($t3i << 8) & 0xFFFFFF00) | (($t3i >> 24) & 0x000000FF);
|
647 |
-
}
|
648 |
-
|
649 |
-
$tables = array(
|
650 |
-
// The Precomputed mixColumns tables t0 - t3
|
651 |
-
$t0,
|
652 |
-
$t1,
|
653 |
-
$t2,
|
654 |
-
$t3,
|
655 |
-
// The SubByte S-Box
|
656 |
-
array(
|
657 |
-
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
|
658 |
-
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
|
659 |
-
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
|
660 |
-
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
|
661 |
-
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
|
662 |
-
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
|
663 |
-
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
|
664 |
-
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
|
665 |
-
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
|
666 |
-
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
|
667 |
-
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
|
668 |
-
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
|
669 |
-
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
|
670 |
-
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
|
671 |
-
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
|
672 |
-
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
|
673 |
-
)
|
674 |
-
);
|
675 |
-
}
|
676 |
-
return $tables;
|
677 |
-
}
|
678 |
-
|
679 |
-
/**
|
680 |
-
* Provides the inverse mixColumns and inverse sboxes tables
|
681 |
-
*
|
682 |
-
* @see self::_decryptBlock()
|
683 |
-
* @see self::_setupInlineCrypt()
|
684 |
-
* @see self::_setupKey()
|
685 |
-
* @access private
|
686 |
-
* @return array &$tables
|
687 |
-
*/
|
688 |
-
function &_getInvTables()
|
689 |
-
{
|
690 |
-
static $tables;
|
691 |
-
if (empty($tables)) {
|
692 |
-
$dt3 = array_map('intval', array(
|
693 |
-
0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B,
|
694 |
-
0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5,
|
695 |
-
0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B,
|
696 |
-
0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E,
|
697 |
-
0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D,
|
698 |
-
0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9,
|
699 |
-
0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66,
|
700 |
-
0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED,
|
701 |
-
0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4,
|
702 |
-
0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD,
|
703 |
-
0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60,
|
704 |
-
0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79,
|
705 |
-
0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C,
|
706 |
-
0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24,
|
707 |
-
0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C,
|
708 |
-
0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814,
|
709 |
-
0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B,
|
710 |
-
0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084,
|
711 |
-
0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077,
|
712 |
-
0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22,
|
713 |
-
0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F,
|
714 |
-
0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582,
|
715 |
-
0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB,
|
716 |
-
0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF,
|
717 |
-
0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035,
|
718 |
-
0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17,
|
719 |
-
0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46,
|
720 |
-
0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D,
|
721 |
-
0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A,
|
722 |
-
0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678,
|
723 |
-
0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF,
|
724 |
-
0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0
|
725 |
-
));
|
726 |
-
|
727 |
-
foreach ($dt3 as $dt3i) {
|
728 |
-
$dt0[] = (($dt3i << 24) & 0xFF000000) | (($dt3i >> 8) & 0x00FFFFFF);
|
729 |
-
$dt1[] = (($dt3i << 16) & 0xFFFF0000) | (($dt3i >> 16) & 0x0000FFFF);
|
730 |
-
$dt2[] = (($dt3i << 8) & 0xFFFFFF00) | (($dt3i >> 24) & 0x000000FF);
|
731 |
-
};
|
732 |
-
|
733 |
-
$tables = array(
|
734 |
-
// The Precomputed inverse mixColumns tables dt0 - dt3
|
735 |
-
$dt0,
|
736 |
-
$dt1,
|
737 |
-
$dt2,
|
738 |
-
$dt3,
|
739 |
-
// The inverse SubByte S-Box
|
740 |
-
array(
|
741 |
-
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
|
742 |
-
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
|
743 |
-
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
|
744 |
-
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
|
745 |
-
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
|
746 |
-
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
|
747 |
-
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
|
748 |
-
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
|
749 |
-
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
|
750 |
-
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
|
751 |
-
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
|
752 |
-
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
|
753 |
-
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
|
754 |
-
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
|
755 |
-
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
|
756 |
-
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
|
757 |
-
)
|
758 |
-
);
|
759 |
-
}
|
760 |
-
return $tables;
|
761 |
-
}
|
762 |
-
|
763 |
-
/**
|
764 |
-
* Setup the performance-optimized function for de/encrypt()
|
765 |
-
*
|
766 |
-
* @see \phpseclib\Crypt\Base::_setupInlineCrypt()
|
767 |
-
* @access private
|
768 |
-
*/
|
769 |
-
function _setupInlineCrypt()
|
770 |
-
{
|
771 |
-
// Note: _setupInlineCrypt() will be called only if $this->changed === true
|
772 |
-
// So here we are'nt under the same heavy timing-stress as we are in _de/encryptBlock() or de/encrypt().
|
773 |
-
// However...the here generated function- $code, stored as php callback in $this->inline_crypt, must work as fast as even possible.
|
774 |
-
|
775 |
-
$lambda_functions =& self::_getLambdaFunctions();
|
776 |
-
|
777 |
-
// We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function.
|
778 |
-
// (Currently, for Crypt_Rijndael/AES, one generated $lambda_function cost on php5.5@32bit ~80kb unfreeable mem and ~130kb on php5.5@64bit)
|
779 |
-
// After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one.
|
780 |
-
$gen_hi_opt_code = (bool)(count($lambda_functions) < 10);
|
781 |
-
|
782 |
-
// Generation of a uniqe hash for our generated code
|
783 |
-
$code_hash = "Crypt_Rijndael, {$this->mode}, {$this->Nr}, {$this->Nb}";
|
784 |
-
if ($gen_hi_opt_code) {
|
785 |
-
$code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
|
786 |
-
}
|
787 |
-
|
788 |
-
if (!isset($lambda_functions[$code_hash])) {
|
789 |
-
switch (true) {
|
790 |
-
case $gen_hi_opt_code:
|
791 |
-
// The hi-optimized $lambda_functions will use the key-words hardcoded for better performance.
|
792 |
-
$w = $this->w;
|
793 |
-
$dw = $this->dw;
|
794 |
-
$init_encrypt = '';
|
795 |
-
$init_decrypt = '';
|
796 |
-
break;
|
797 |
-
default:
|
798 |
-
for ($i = 0, $cw = count($this->w); $i < $cw; ++$i) {
|
799 |
-
$w[] = '$w[' . $i . ']';
|
800 |
-
$dw[] = '$dw[' . $i . ']';
|
801 |
-
}
|
802 |
-
$init_encrypt = '$w = $self->w;';
|
803 |
-
$init_decrypt = '$dw = $self->dw;';
|
804 |
-
}
|
805 |
-
|
806 |
-
$Nr = $this->Nr;
|
807 |
-
$Nb = $this->Nb;
|
808 |
-
$c = $this->c;
|
809 |
-
|
810 |
-
// Generating encrypt code:
|
811 |
-
$init_encrypt.= '
|
812 |
-
static $tables;
|
813 |
-
if (empty($tables)) {
|
814 |
-
$tables = &$self->_getTables();
|
815 |
-
}
|
816 |
-
$t0 = $tables[0];
|
817 |
-
$t1 = $tables[1];
|
818 |
-
$t2 = $tables[2];
|
819 |
-
$t3 = $tables[3];
|
820 |
-
$sbox = $tables[4];
|
821 |
-
';
|
822 |
-
|
823 |
-
$s = 'e';
|
824 |
-
$e = 's';
|
825 |
-
$wc = $Nb - 1;
|
826 |
-
|
827 |
-
// Preround: addRoundKey
|
828 |
-
$encrypt_block = '$in = unpack("N*", $in);'."\n";
|
829 |
-
for ($i = 0; $i < $Nb; ++$i) {
|
830 |
-
$encrypt_block .= '$s'.$i.' = $in['.($i + 1).'] ^ '.$w[++$wc].";\n";
|
831 |
-
}
|
832 |
-
|
833 |
-
// Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
|
834 |
-
for ($round = 1; $round < $Nr; ++$round) {
|
835 |
-
list($s, $e) = array($e, $s);
|
836 |
-
for ($i = 0; $i < $Nb; ++$i) {
|
837 |
-
$encrypt_block.=
|
838 |
-
'$'.$e.$i.' =
|
839 |
-
$t0[($'.$s.$i .' >> 24) & 0xff] ^
|
840 |
-
$t1[($'.$s.(($i + $c[1]) % $Nb).' >> 16) & 0xff] ^
|
841 |
-
$t2[($'.$s.(($i + $c[2]) % $Nb).' >> 8) & 0xff] ^
|
842 |
-
$t3[ $'.$s.(($i + $c[3]) % $Nb).' & 0xff] ^
|
843 |
-
'.$w[++$wc].";\n";
|
844 |
-
}
|
845 |
-
}
|
846 |
-
|
847 |
-
// Finalround: subWord + shiftRows + addRoundKey
|
848 |
-
for ($i = 0; $i < $Nb; ++$i) {
|
849 |
-
$encrypt_block.=
|
850 |
-
'$'.$e.$i.' =
|
851 |
-
$sbox[ $'.$e.$i.' & 0xff] |
|
852 |
-
($sbox[($'.$e.$i.' >> 8) & 0xff] << 8) |
|
853 |
-
($sbox[($'.$e.$i.' >> 16) & 0xff] << 16) |
|
854 |
-
($sbox[($'.$e.$i.' >> 24) & 0xff] << 24);'."\n";
|
855 |
-
}
|
856 |
-
$encrypt_block .= '$in = pack("N*"'."\n";
|
857 |
-
for ($i = 0; $i < $Nb; ++$i) {
|
858 |
-
$encrypt_block.= ',
|
859 |
-
($'.$e.$i .' & '.((int)0xFF000000).') ^
|
860 |
-
($'.$e.(($i + $c[1]) % $Nb).' & 0x00FF0000 ) ^
|
861 |
-
($'.$e.(($i + $c[2]) % $Nb).' & 0x0000FF00 ) ^
|
862 |
-
($'.$e.(($i + $c[3]) % $Nb).' & 0x000000FF ) ^
|
863 |
-
'.$w[$i]."\n";
|
864 |
-
}
|
865 |
-
$encrypt_block .= ');';
|
866 |
-
|
867 |
-
// Generating decrypt code:
|
868 |
-
$init_decrypt.= '
|
869 |
-
static $invtables;
|
870 |
-
if (empty($invtables)) {
|
871 |
-
$invtables = &$self->_getInvTables();
|
872 |
-
}
|
873 |
-
$dt0 = $invtables[0];
|
874 |
-
$dt1 = $invtables[1];
|
875 |
-
$dt2 = $invtables[2];
|
876 |
-
$dt3 = $invtables[3];
|
877 |
-
$isbox = $invtables[4];
|
878 |
-
';
|
879 |
-
|
880 |
-
$s = 'e';
|
881 |
-
$e = 's';
|
882 |
-
$wc = $Nb - 1;
|
883 |
-
|
884 |
-
// Preround: addRoundKey
|
885 |
-
$decrypt_block = '$in = unpack("N*", $in);'."\n";
|
886 |
-
for ($i = 0; $i < $Nb; ++$i) {
|
887 |
-
$decrypt_block .= '$s'.$i.' = $in['.($i + 1).'] ^ '.$dw[++$wc].';'."\n";
|
888 |
-
}
|
889 |
-
|
890 |
-
// Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
|
891 |
-
for ($round = 1; $round < $Nr; ++$round) {
|
892 |
-
list($s, $e) = array($e, $s);
|
893 |
-
for ($i = 0; $i < $Nb; ++$i) {
|
894 |
-
$decrypt_block.=
|
895 |
-
'$'.$e.$i.' =
|
896 |
-
$dt0[($'.$s.$i .' >> 24) & 0xff] ^
|
897 |
-
$dt1[($'.$s.(($Nb + $i - $c[1]) % $Nb).' >> 16) & 0xff] ^
|
898 |
-
$dt2[($'.$s.(($Nb + $i - $c[2]) % $Nb).' >> 8) & 0xff] ^
|
899 |
-
$dt3[ $'.$s.(($Nb + $i - $c[3]) % $Nb).' & 0xff] ^
|
900 |
-
'.$dw[++$wc].";\n";
|
901 |
-
}
|
902 |
-
}
|
903 |
-
|
904 |
-
// Finalround: subWord + shiftRows + addRoundKey
|
905 |
-
for ($i = 0; $i < $Nb; ++$i) {
|
906 |
-
$decrypt_block.=
|
907 |
-
'$'.$e.$i.' =
|
908 |
-
$isbox[ $'.$e.$i.' & 0xff] |
|
909 |
-
($isbox[($'.$e.$i.' >> 8) & 0xff] << 8) |
|
910 |
-
($isbox[($'.$e.$i.' >> 16) & 0xff] << 16) |
|
911 |
-
($isbox[($'.$e.$i.' >> 24) & 0xff] << 24);'."\n";
|
912 |
-
}
|
913 |
-
$decrypt_block .= '$in = pack("N*"'."\n";
|
914 |
-
for ($i = 0; $i < $Nb; ++$i) {
|
915 |
-
$decrypt_block.= ',
|
916 |
-
($'.$e.$i. ' & '.((int)0xFF000000).') ^
|
917 |
-
($'.$e.(($Nb + $i - $c[1]) % $Nb).' & 0x00FF0000 ) ^
|
918 |
-
($'.$e.(($Nb + $i - $c[2]) % $Nb).' & 0x0000FF00 ) ^
|
919 |
-
($'.$e.(($Nb + $i - $c[3]) % $Nb).' & 0x000000FF ) ^
|
920 |
-
'.$dw[$i]."\n";
|
921 |
-
}
|
922 |
-
$decrypt_block .= ');';
|
923 |
-
|
924 |
-
$lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
|
925 |
-
array(
|
926 |
-
'init_crypt' => '',
|
927 |
-
'init_encrypt' => $init_encrypt,
|
928 |
-
'init_decrypt' => $init_decrypt,
|
929 |
-
'encrypt_block' => $encrypt_block,
|
930 |
-
'decrypt_block' => $decrypt_block
|
931 |
-
)
|
932 |
-
);
|
933 |
-
}
|
934 |
-
$this->inline_crypt = $lambda_functions[$code_hash];
|
935 |
-
}
|
936 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Pure-PHP implementation of Rijndael.
|
5 |
+
*
|
6 |
+
* Uses mcrypt, if available/possible, and an internal implementation, otherwise.
|
7 |
+
*
|
8 |
+
* PHP version 5
|
9 |
+
*
|
10 |
+
* If {@link self::setBlockLength() setBlockLength()} isn't called, it'll be assumed to be 128 bits. If
|
11 |
+
* {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
|
12 |
+
* {@link self::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's
|
13 |
+
* 136-bits it'll be null-padded to 192-bits and 192 bits will be the key length until
|
14 |
+
* {@link self::setKey() setKey()} is called, again, at which point, it'll be recalculated.
|
15 |
+
*
|
16 |
+
* Not all Rijndael implementations may support 160-bits or 224-bits as the block length / key length. mcrypt, for example,
|
17 |
+
* does not. AES, itself, only supports block lengths of 128 and key lengths of 128, 192, and 256.
|
18 |
+
* {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=10 Rijndael-ammended.pdf#page=10} defines the
|
19 |
+
* algorithm for block lengths of 192 and 256 but not for block lengths / key lengths of 160 and 224. Indeed, 160 and 224
|
20 |
+
* are first defined as valid key / block lengths in
|
21 |
+
* {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=44 Rijndael-ammended.pdf#page=44}:
|
22 |
+
* Extensions: Other block and Cipher Key lengths.
|
23 |
+
* Note: Use of 160/224-bit Keys must be explicitly set by setKeyLength(160) respectively setKeyLength(224).
|
24 |
+
*
|
25 |
+
* {@internal The variable names are the same as those in
|
26 |
+
* {@link http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf#page=10 fips-197.pdf#page=10}.}}
|
27 |
+
*
|
28 |
+
* Here's a short example of how to use this library:
|
29 |
+
* <code>
|
30 |
+
* <?php
|
31 |
+
* include 'vendor/autoload.php';
|
32 |
+
*
|
33 |
+
* $rijndael = new \phpseclib\Crypt\Rijndael();
|
34 |
+
*
|
35 |
+
* $rijndael->setKey('abcdefghijklmnop');
|
36 |
+
*
|
37 |
+
* $size = 10 * 1024;
|
38 |
+
* $plaintext = '';
|
39 |
+
* for ($i = 0; $i < $size; $i++) {
|
40 |
+
* $plaintext.= 'a';
|
41 |
+
* }
|
42 |
+
*
|
43 |
+
* echo $rijndael->decrypt($rijndael->encrypt($plaintext));
|
44 |
+
* ?>
|
45 |
+
* </code>
|
46 |
+
*
|
47 |
+
* @category Crypt
|
48 |
+
* @package Rijndael
|
49 |
+
* @author Jim Wigginton <terrafrost@php.net>
|
50 |
+
* @copyright 2008 Jim Wigginton
|
51 |
+
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
52 |
+
* @link http://phpseclib.sourceforge.net
|
53 |
+
*/
|
54 |
+
|
55 |
+
namespace phpseclib\Crypt;
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Pure-PHP implementation of Rijndael.
|
59 |
+
*
|
60 |
+
* @package Rijndael
|
61 |
+
* @author Jim Wigginton <terrafrost@php.net>
|
62 |
+
* @access public
|
63 |
+
*/
|
64 |
+
class Rijndael extends Base
|
65 |
+
{
|
66 |
+
/**
|
67 |
+
* The mcrypt specific name of the cipher
|
68 |
+
*
|
69 |
+
* Mcrypt is useable for 128/192/256-bit $block_size/$key_length. For 160/224 not.
|
70 |
+
* \phpseclib\Crypt\Rijndael determines automatically whether mcrypt is useable
|
71 |
+
* or not for the current $block_size/$key_length.
|
72 |
+
* In case of, $cipher_name_mcrypt will be set dynamically at run time accordingly.
|
73 |
+
*
|
74 |
+
* @see \phpseclib\Crypt\Base::cipher_name_mcrypt
|
75 |
+
* @see \phpseclib\Crypt\Base::engine
|
76 |
+
* @see self::isValidEngine()
|
77 |
+
* @var string
|
78 |
+
* @access private
|
79 |
+
*/
|
80 |
+
var $cipher_name_mcrypt = 'rijndael-128';
|
81 |
+
|
82 |
+
/**
|
83 |
+
* The default salt used by setPassword()
|
84 |
+
*
|
85 |
+
* @see \phpseclib\Crypt\Base::password_default_salt
|
86 |
+
* @see \phpseclib\Crypt\Base::setPassword()
|
87 |
+
* @var string
|
88 |
+
* @access private
|
89 |
+
*/
|
90 |
+
var $password_default_salt = 'phpseclib';
|
91 |
+
|
92 |
+
/**
|
93 |
+
* The Key Schedule
|
94 |
+
*
|
95 |
+
* @see self::_setup()
|
96 |
+
* @var array
|
97 |
+
* @access private
|
98 |
+
*/
|
99 |
+
var $w;
|
100 |
+
|
101 |
+
/**
|
102 |
+
* The Inverse Key Schedule
|
103 |
+
*
|
104 |
+
* @see self::_setup()
|
105 |
+
* @var array
|
106 |
+
* @access private
|
107 |
+
*/
|
108 |
+
var $dw;
|
109 |
+
|
110 |
+
/**
|
111 |
+
* The Block Length divided by 32
|
112 |
+
*
|
113 |
+
* @see self::setBlockLength()
|
114 |
+
* @var int
|
115 |
+
* @access private
|
116 |
+
* @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4. Exists in conjunction with $block_size
|
117 |
+
* because the encryption / decryption / key schedule creation requires this number and not $block_size. We could
|
118 |
+
* derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
|
119 |
+
* of that, we'll just precompute it once.
|
120 |
+
*/
|
121 |
+
var $Nb = 4;
|
122 |
+
|
123 |
+
/**
|
124 |
+
* The Key Length (in bytes)
|
125 |
+
*
|
126 |
+
* @see self::setKeyLength()
|
127 |
+
* @var int
|
128 |
+
* @access private
|
129 |
+
* @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk
|
130 |
+
* because the encryption / decryption / key schedule creation requires this number and not $key_length. We could
|
131 |
+
* derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
|
132 |
+
* of that, we'll just precompute it once.
|
133 |
+
*/
|
134 |
+
var $key_length = 16;
|
135 |
+
|
136 |
+
/**
|
137 |
+
* The Key Length divided by 32
|
138 |
+
*
|
139 |
+
* @see self::setKeyLength()
|
140 |
+
* @var int
|
141 |
+
* @access private
|
142 |
+
* @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4
|
143 |
+
*/
|
144 |
+
var $Nk = 4;
|
145 |
+
|
146 |
+
/**
|
147 |
+
* The Number of Rounds
|
148 |
+
*
|
149 |
+
* @var int
|
150 |
+
* @access private
|
151 |
+
* @internal The max value is 14, the min value is 10.
|
152 |
+
*/
|
153 |
+
var $Nr;
|
154 |
+
|
155 |
+
/**
|
156 |
+
* Shift offsets
|
157 |
+
*
|
158 |
+
* @var array
|
159 |
+
* @access private
|
160 |
+
*/
|
161 |
+
var $c;
|
162 |
+
|
163 |
+
/**
|
164 |
+
* Holds the last used key- and block_size information
|
165 |
+
*
|
166 |
+
* @var array
|
167 |
+
* @access private
|
168 |
+
*/
|
169 |
+
var $kl;
|
170 |
+
|
171 |
+
/**
|
172 |
+
* Sets the key length.
|
173 |
+
*
|
174 |
+
* Valid key lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to
|
175 |
+
* 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
|
176 |
+
*
|
177 |
+
* Note: phpseclib extends Rijndael (and AES) for using 160- and 224-bit keys but they are officially not defined
|
178 |
+
* and the most (if not all) implementations are not able using 160/224-bit keys but round/pad them up to
|
179 |
+
* 192/256 bits as, for example, mcrypt will do.
|
180 |
+
*
|
181 |
+
* That said, if you want be compatible with other Rijndael and AES implementations,
|
182 |
+
* you should not setKeyLength(160) or setKeyLength(224).
|
183 |
+
*
|
184 |
+
* Additional: In case of 160- and 224-bit keys, phpseclib will/can, for that reason, not use
|
185 |
+
* the mcrypt php extension, even if available.
|
186 |
+
* This results then in slower encryption.
|
187 |
+
*
|
188 |
+
* @access public
|
189 |
+
* @param int $length
|
190 |
+
*/
|
191 |
+
function setKeyLength($length)
|
192 |
+
{
|
193 |
+
switch (true) {
|
194 |
+
case $length <= 128:
|
195 |
+
$this->key_length = 16;
|
196 |
+
break;
|
197 |
+
case $length <= 160:
|
198 |
+
$this->key_length = 20;
|
199 |
+
break;
|
200 |
+
case $length <= 192:
|
201 |
+
$this->key_length = 24;
|
202 |
+
break;
|
203 |
+
case $length <= 224:
|
204 |
+
$this->key_length = 28;
|
205 |
+
break;
|
206 |
+
default:
|
207 |
+
$this->key_length = 32;
|
208 |
+
}
|
209 |
+
|
210 |
+
parent::setKeyLength($length);
|
211 |
+
}
|
212 |
+
|
213 |
+
/**
|
214 |
+
* Sets the block length
|
215 |
+
*
|
216 |
+
* Valid block lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to
|
217 |
+
* 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
|
218 |
+
*
|
219 |
+
* @access public
|
220 |
+
* @param int $length
|
221 |
+
*/
|
222 |
+
function setBlockLength($length)
|
223 |
+
{
|
224 |
+
$length >>= 5;
|
225 |
+
if ($length > 8) {
|
226 |
+
$length = 8;
|
227 |
+
} elseif ($length < 4) {
|
228 |
+
$length = 4;
|
229 |
+
}
|
230 |
+
$this->Nb = $length;
|
231 |
+
$this->block_size = $length << 2;
|
232 |
+
$this->changed = true;
|
233 |
+
$this->_setEngine();
|
234 |
+
}
|
235 |
+
|
236 |
+
/**
|
237 |
+
* Test for engine validity
|
238 |
+
*
|
239 |
+
* This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine()
|
240 |
+
*
|
241 |
+
* @see \phpseclib\Crypt\Base::__construct()
|
242 |
+
* @param int $engine
|
243 |
+
* @access public
|
244 |
+
* @return bool
|
245 |
+
*/
|
246 |
+
function isValidEngine($engine)
|
247 |
+
{
|
248 |
+
switch ($engine) {
|
249 |
+
case self::ENGINE_OPENSSL:
|
250 |
+
if ($this->block_size != 16) {
|
251 |
+
return false;
|
252 |
+
}
|
253 |
+
$this->cipher_name_openssl_ecb = 'aes-' . ($this->key_length << 3) . '-ecb';
|
254 |
+
$this->cipher_name_openssl = 'aes-' . ($this->key_length << 3) . '-' . $this->_openssl_translate_mode();
|
255 |
+
break;
|
256 |
+
case self::ENGINE_MCRYPT:
|
257 |
+
$this->cipher_name_mcrypt = 'rijndael-' . ($this->block_size << 3);
|
258 |
+
if ($this->key_length % 8) { // is it a 160/224-bit key?
|
259 |
+
// mcrypt is not usable for them, only for 128/192/256-bit keys
|
260 |
+
return false;
|
261 |
+
}
|
262 |
+
}
|
263 |
+
|
264 |
+
return parent::isValidEngine($engine);
|
265 |
+
}
|
266 |
+
|
267 |
+
/**
|
268 |
+
* Encrypts a block
|
269 |
+
*
|
270 |
+
* @access private
|
271 |
+
* @param string $in
|
272 |
+
* @return string
|
273 |
+
*/
|
274 |
+
function _encryptBlock($in)
|
275 |
+
{
|
276 |
+
static $tables;
|
277 |
+
if (empty($tables)) {
|
278 |
+
$tables = &$this->_getTables();
|
279 |
+
}
|
280 |
+
$t0 = $tables[0];
|
281 |
+
$t1 = $tables[1];
|
282 |
+
$t2 = $tables[2];
|
283 |
+
$t3 = $tables[3];
|
284 |
+
$sbox = $tables[4];
|
285 |
+
|
286 |
+
$state = array();
|
287 |
+
$words = unpack('N*', $in);
|
288 |
+
|
289 |
+
$c = $this->c;
|
290 |
+
$w = $this->w;
|
291 |
+
$Nb = $this->Nb;
|
292 |
+
$Nr = $this->Nr;
|
293 |
+
|
294 |
+
// addRoundKey
|
295 |
+
$wc = $Nb - 1;
|
296 |
+
foreach ($words as $word) {
|
297 |
+
$state[] = $word ^ $w[++$wc];
|
298 |
+
}
|
299 |
+
|
300 |
+
// fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components -
|
301 |
+
// subBytes, shiftRows, mixColumns, and addRoundKey. fips-197.pdf#page=30, "Implementation Suggestions Regarding
|
302 |
+
// Various Platforms" suggests that performs enhanced implementations are described in Rijndael-ammended.pdf.
|
303 |
+
// Rijndael-ammended.pdf#page=20, "Implementation aspects / 32-bit processor", discusses such an optimization.
|
304 |
+
// Unfortunately, the description given there is not quite correct. Per aes.spec.v316.pdf#page=19 [1],
|
305 |
+
// equation (7.4.7) is supposed to use addition instead of subtraction, so we'll do that here, as well.
|
306 |
+
|
307 |
+
// [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf
|
308 |
+
$temp = array();
|
309 |
+
for ($round = 1; $round < $Nr; ++$round) {
|
310 |
+
$i = 0; // $c[0] == 0
|
311 |
+
$j = $c[1];
|
312 |
+
$k = $c[2];
|
313 |
+
$l = $c[3];
|
314 |
+
|
315 |
+
while ($i < $Nb) {
|
316 |
+
$temp[$i] = $t0[$state[$i] >> 24 & 0x000000FF] ^
|
317 |
+
$t1[$state[$j] >> 16 & 0x000000FF] ^
|
318 |
+
$t2[$state[$k] >> 8 & 0x000000FF] ^
|
319 |
+
$t3[$state[$l] & 0x000000FF] ^
|
320 |
+
$w[++$wc];
|
321 |
+
++$i;
|
322 |
+
$j = ($j + 1) % $Nb;
|
323 |
+
$k = ($k + 1) % $Nb;
|
324 |
+
$l = ($l + 1) % $Nb;
|
325 |
+
}
|
326 |
+
$state = $temp;
|
327 |
+
}
|
328 |
+
|
329 |
+
// subWord
|
330 |
+
for ($i = 0; $i < $Nb; ++$i) {
|
331 |
+
$state[$i] = $sbox[$state[$i] & 0x000000FF] |
|
332 |
+
($sbox[$state[$i] >> 8 & 0x000000FF] << 8) |
|
333 |
+
($sbox[$state[$i] >> 16 & 0x000000FF] << 16) |
|
334 |
+
($sbox[$state[$i] >> 24 & 0x000000FF] << 24);
|
335 |
+
}
|
336 |
+
|
337 |
+
// shiftRows + addRoundKey
|
338 |
+
$i = 0; // $c[0] == 0
|
339 |
+
$j = $c[1];
|
340 |
+
$k = $c[2];
|
341 |
+
$l = $c[3];
|
342 |
+
while ($i < $Nb) {
|
343 |
+
$temp[$i] = ($state[$i] & 0xFF000000) ^
|
344 |
+
($state[$j] & 0x00FF0000) ^
|
345 |
+
($state[$k] & 0x0000FF00) ^
|
346 |
+
($state[$l] & 0x000000FF) ^
|
347 |
+
$w[$i];
|
348 |
+
++$i;
|
349 |
+
$j = ($j + 1) % $Nb;
|
350 |
+
$k = ($k + 1) % $Nb;
|
351 |
+
$l = ($l + 1) % $Nb;
|
352 |
+
}
|
353 |
+
|
354 |
+
switch ($Nb) {
|
355 |
+
case 8:
|
356 |
+
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]);
|
357 |
+
case 7:
|
358 |
+
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]);
|
359 |
+
case 6:
|
360 |
+
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]);
|
361 |
+
case 5:
|
362 |
+
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]);
|
363 |
+
default:
|
364 |
+
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]);
|
365 |
+
}
|
366 |
+
}
|
367 |
+
|
368 |
+
/**
|
369 |
+
* Decrypts a block
|
370 |
+
*
|
371 |
+
* @access private
|
372 |
+
* @param string $in
|
373 |
+
* @return string
|
374 |
+
*/
|
375 |
+
function _decryptBlock($in)
|
376 |
+
{
|
377 |
+
static $invtables;
|
378 |
+
if (empty($invtables)) {
|
379 |
+
$invtables = &$this->_getInvTables();
|
380 |
+
}
|
381 |
+
$dt0 = $invtables[0];
|
382 |
+
$dt1 = $invtables[1];
|
383 |
+
$dt2 = $invtables[2];
|
384 |
+
$dt3 = $invtables[3];
|
385 |
+
$isbox = $invtables[4];
|
386 |
+
|
387 |
+
$state = array();
|
388 |
+
$words = unpack('N*', $in);
|
389 |
+
|
390 |
+
$c = $this->c;
|
391 |
+
$dw = $this->dw;
|
392 |
+
$Nb = $this->Nb;
|
393 |
+
$Nr = $this->Nr;
|
394 |
+
|
395 |
+
// addRoundKey
|
396 |
+
$wc = $Nb - 1;
|
397 |
+
foreach ($words as $word) {
|
398 |
+
$state[] = $word ^ $dw[++$wc];
|
399 |
+
}
|
400 |
+
|
401 |
+
$temp = array();
|
402 |
+
for ($round = $Nr - 1; $round > 0; --$round) {
|
403 |
+
$i = 0; // $c[0] == 0
|
404 |
+
$j = $Nb - $c[1];
|
405 |
+
$k = $Nb - $c[2];
|
406 |
+
$l = $Nb - $c[3];
|
407 |
+
|
408 |
+
while ($i < $Nb) {
|
409 |
+
$temp[$i] = $dt0[$state[$i] >> 24 & 0x000000FF] ^
|
410 |
+
$dt1[$state[$j] >> 16 & 0x000000FF] ^
|
411 |
+
$dt2[$state[$k] >> 8 & 0x000000FF] ^
|
412 |
+
$dt3[$state[$l] & 0x000000FF] ^
|
413 |
+
$dw[++$wc];
|
414 |
+
++$i;
|
415 |
+
$j = ($j + 1) % $Nb;
|
416 |
+
$k = ($k + 1) % $Nb;
|
417 |
+
$l = ($l + 1) % $Nb;
|
418 |
+
}
|
419 |
+
$state = $temp;
|
420 |
+
}
|
421 |
+
|
422 |
+
// invShiftRows + invSubWord + addRoundKey
|
423 |
+
$i = 0; // $c[0] == 0
|
424 |
+
$j = $Nb - $c[1];
|
425 |
+
$k = $Nb - $c[2];
|
426 |
+
$l = $Nb - $c[3];
|
427 |
+
|
428 |
+
while ($i < $Nb) {
|
429 |
+
$word = ($state[$i] & 0xFF000000) |
|
430 |
+
($state[$j] & 0x00FF0000) |
|
431 |
+
($state[$k] & 0x0000FF00) |
|
432 |
+
($state[$l] & 0x000000FF);
|
433 |
+
|
434 |
+
$temp[$i] = $dw[$i] ^ ($isbox[$word & 0x000000FF] |
|
435 |
+
($isbox[$word >> 8 & 0x000000FF] << 8) |
|
436 |
+
($isbox[$word >> 16 & 0x000000FF] << 16) |
|
437 |
+
($isbox[$word >> 24 & 0x000000FF] << 24));
|
438 |
+
++$i;
|
439 |
+
$j = ($j + 1) % $Nb;
|
440 |
+
$k = ($k + 1) % $Nb;
|
441 |
+
$l = ($l + 1) % $Nb;
|
442 |
+
}
|
443 |
+
|
444 |
+
switch ($Nb) {
|
445 |
+
case 8:
|
446 |
+
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]);
|
447 |
+
case 7:
|
448 |
+
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]);
|
449 |
+
case 6:
|
450 |
+
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]);
|
451 |
+
case 5:
|
452 |
+
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]);
|
453 |
+
default:
|
454 |
+
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]);
|
455 |
+
}
|
456 |
+
}
|
457 |
+
|
458 |
+
/**
|
459 |
+
* Setup the key (expansion)
|
460 |
+
*
|
461 |
+
* @see \phpseclib\Crypt\Base::_setupKey()
|
462 |
+
* @access private
|
463 |
+
*/
|
464 |
+
function _setupKey()
|
465 |
+
{
|
466 |
+
// Each number in $rcon is equal to the previous number multiplied by two in Rijndael's finite field.
|
467 |
+
// See http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse
|
468 |
+
static $rcon = array(0,
|
469 |
+
0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
|
470 |
+
0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
|
471 |
+
0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000,
|
472 |
+
0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000,
|
473 |
+
0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000,
|
474 |
+
0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000
|
475 |
+
);
|
476 |
+
|
477 |
+
if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_length === $this->kl['key_length'] && $this->block_size === $this->kl['block_size']) {
|
478 |
+
// already expanded
|
479 |
+
return;
|
480 |
+
}
|
481 |
+
$this->kl = array('key' => $this->key, 'key_length' => $this->key_length, 'block_size' => $this->block_size);
|
482 |
+
|
483 |
+
$this->Nk = $this->key_length >> 2;
|
484 |
+
// see Rijndael-ammended.pdf#page=44
|
485 |
+
$this->Nr = max($this->Nk, $this->Nb) + 6;
|
486 |
+
|
487 |
+
// shift offsets for Nb = 5, 7 are defined in Rijndael-ammended.pdf#page=44,
|
488 |
+
// "Table 8: Shift offsets in Shiftrow for the alternative block lengths"
|
489 |
+
// shift offsets for Nb = 4, 6, 8 are defined in Rijndael-ammended.pdf#page=14,
|
490 |
+
// "Table 2: Shift offsets for different block lengths"
|
491 |
+
switch ($this->Nb) {
|
492 |
+
case 4:
|
493 |
+
case 5:
|
494 |
+
case 6:
|
495 |
+
$this->c = array(0, 1, 2, 3);
|
496 |
+
break;
|
497 |
+
case 7:
|
498 |
+
$this->c = array(0, 1, 2, 4);
|
499 |
+
break;
|
500 |
+
case 8:
|
501 |
+
$this->c = array(0, 1, 3, 4);
|
502 |
+
}
|
503 |
+
|
504 |
+
$w = array_values(unpack('N*words', $this->key));
|
505 |
+
|
506 |
+
$length = $this->Nb * ($this->Nr + 1);
|
507 |
+
for ($i = $this->Nk; $i < $length; $i++) {
|
508 |
+
$temp = $w[$i - 1];
|
509 |
+
if ($i % $this->Nk == 0) {
|
510 |
+
// according to <http://php.net/language.types.integer>, "the size of an integer is platform-dependent".
|
511 |
+
// on a 32-bit machine, it's 32-bits, and on a 64-bit machine, it's 64-bits. on a 32-bit machine,
|
512 |
+
// 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit machine, it equals 0xFFFFFFFF00. as such, doing 'and'
|
513 |
+
// with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is.
|
514 |
+
$temp = (($temp << 8) & 0xFFFFFF00) | (($temp >> 24) & 0x000000FF); // rotWord
|
515 |
+
$temp = $this->_subWord($temp) ^ $rcon[$i / $this->Nk];
|
516 |
+
} elseif ($this->Nk > 6 && $i % $this->Nk == 4) {
|
517 |
+
$temp = $this->_subWord($temp);
|
518 |
+
}
|
519 |
+
$w[$i] = $w[$i - $this->Nk] ^ $temp;
|
520 |
+
}
|
521 |
+
|
522 |
+
// convert the key schedule from a vector of $Nb * ($Nr + 1) length to a matrix with $Nr + 1 rows and $Nb columns
|
523 |
+
// and generate the inverse key schedule. more specifically,
|
524 |
+
// according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=23> (section 5.3.3),
|
525 |
+
// "The key expansion for the Inverse Cipher is defined as follows:
|
526 |
+
// 1. Apply the Key Expansion.
|
527 |
+
// 2. Apply InvMixColumn to all Round Keys except the first and the last one."
|
528 |
+
// also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher"
|
529 |
+
list($dt0, $dt1, $dt2, $dt3) = $this->_getInvTables();
|
530 |
+
$temp = $this->w = $this->dw = array();
|
531 |
+
for ($i = $row = $col = 0; $i < $length; $i++, $col++) {
|
532 |
+
if ($col == $this->Nb) {
|
533 |
+
if ($row == 0) {
|
534 |
+
$this->dw[0] = $this->w[0];
|
535 |
+
} else {
|
536 |
+
// subWord + invMixColumn + invSubWord = invMixColumn
|
537 |
+
$j = 0;
|
538 |
+
while ($j < $this->Nb) {
|
539 |
+
$dw = $this->_subWord($this->w[$row][$j]);
|
540 |
+
$temp[$j] = $dt0[$dw >> 24 & 0x000000FF] ^
|
541 |
+
$dt1[$dw >> 16 & 0x000000FF] ^
|
542 |
+
$dt2[$dw >> 8 & 0x000000FF] ^
|
543 |
+
$dt3[$dw & 0x000000FF];
|
544 |
+
$j++;
|
545 |
+
}
|
546 |
+
$this->dw[$row] = $temp;
|
547 |
+
}
|
548 |
+
|
549 |
+
$col = 0;
|
550 |
+
$row++;
|
551 |
+
}
|
552 |
+
$this->w[$row][$col] = $w[$i];
|
553 |
+
}
|
554 |
+
|
555 |
+
$this->dw[$row] = $this->w[$row];
|
556 |
+
|
557 |
+
// Converting to 1-dim key arrays (both ascending)
|
558 |
+
$this->dw = array_reverse($this->dw);
|
559 |
+
$w = array_pop($this->w);
|
560 |
+
$dw = array_pop($this->dw);
|
561 |
+
foreach ($this->w as $r => $wr) {
|
562 |
+
foreach ($wr as $c => $wc) {
|
563 |
+
$w[] = $wc;
|
564 |
+
$dw[] = $this->dw[$r][$c];
|
565 |
+
}
|
566 |
+
}
|
567 |
+
$this->w = $w;
|
568 |
+
$this->dw = $dw;
|
569 |
+
}
|
570 |
+
|
571 |
+
/**
|
572 |
+
* Performs S-Box substitutions
|
573 |
+
*
|
574 |
+
* @access private
|
575 |
+
* @param int $word
|
576 |
+
*/
|
577 |
+
function _subWord($word)
|
578 |
+
{
|
579 |
+
static $sbox;
|
580 |
+
if (empty($sbox)) {
|
581 |
+
list(, , , , $sbox) = $this->_getTables();
|
582 |
+
}
|
583 |
+
|
584 |
+
return $sbox[$word & 0x000000FF] |
|
585 |
+
($sbox[$word >> 8 & 0x000000FF] << 8) |
|
586 |
+
($sbox[$word >> 16 & 0x000000FF] << 16) |
|
587 |
+
($sbox[$word >> 24 & 0x000000FF] << 24);
|
588 |
+
}
|
589 |
+
|
590 |
+
/**
|
591 |
+
* Provides the mixColumns and sboxes tables
|
592 |
+
*
|
593 |
+
* @see self::_encryptBlock()
|
594 |
+
* @see self::_setupInlineCrypt()
|
595 |
+
* @see self::_subWord()
|
596 |
+
* @access private
|
597 |
+
* @return array &$tables
|
598 |
+
*/
|
599 |
+
function &_getTables()
|
600 |
+
{
|
601 |
+
static $tables;
|
602 |
+
if (empty($tables)) {
|
603 |
+
// according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=19> (section 5.2.1),
|
604 |
+
// precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so
|
605 |
+
// those are the names we'll use.
|
606 |
+
$t3 = array_map('intval', array(
|
607 |
+
// with array_map('intval', ...) we ensure we have only int's and not
|
608 |
+
// some slower floats converted by php automatically on high values
|
609 |
+
0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
|
610 |
+
0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC,
|
611 |
+
0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB,
|
612 |
+
0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B,
|
613 |
+
0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83,
|
614 |
+
0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A,
|
615 |
+
0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F,
|
616 |
+
0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA,
|
617 |
+
0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B,
|
618 |
+
0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713,
|
619 |
+
0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6,
|
620 |
+
0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85,
|
621 |
+
0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411,
|
622 |
+
0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B,
|
623 |
+
0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1,
|
624 |
+
0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF,
|
625 |
+
0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E,
|
626 |
+
0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6,
|
627 |
+
0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B,
|
628 |
+
0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD,
|
629 |
+
0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8,
|
630 |
+
0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2,
|
631 |
+
0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049,
|
632 |
+
0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810,
|
633 |
+
0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197,
|
634 |
+
0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F,
|
635 |
+
0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C,
|
636 |
+
0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927,
|
637 |
+
0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733,
|
638 |
+
0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5,
|
639 |
+
0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0,
|
640 |
+
0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C
|
641 |
+
));
|
642 |
+
|
643 |
+
foreach ($t3 as $t3i) {
|
644 |
+
$t0[] = (($t3i << 24) & 0xFF000000) | (($t3i >> 8) & 0x00FFFFFF);
|
645 |
+
$t1[] = (($t3i << 16) & 0xFFFF0000) | (($t3i >> 16) & 0x0000FFFF);
|
646 |
+
$t2[] = (($t3i << 8) & 0xFFFFFF00) | (($t3i >> 24) & 0x000000FF);
|
647 |
+
}
|
648 |
+
|
649 |
+
$tables = array(
|
650 |
+
// The Precomputed mixColumns tables t0 - t3
|
651 |
+
$t0,
|
652 |
+
$t1,
|
653 |
+
$t2,
|
654 |
+
$t3,
|
655 |
+
// The SubByte S-Box
|
656 |
+
array(
|
657 |
+
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
|
658 |
+
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
|
659 |
+
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
|
660 |
+
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
|
661 |
+
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
|
662 |
+
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
|
663 |
+
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
|
664 |
+
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
|
665 |
+
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
|
666 |
+
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
|
667 |
+
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
|
668 |
+
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
|
669 |
+
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
|
670 |
+
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
|
671 |
+
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
|
672 |
+
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
|
673 |
+
)
|
674 |
+
);
|
675 |
+
}
|
676 |
+
return $tables;
|
677 |
+
}
|
678 |
+
|
679 |
+
/**
|
680 |
+
* Provides the inverse mixColumns and inverse sboxes tables
|
681 |
+
*
|
682 |
+
* @see self::_decryptBlock()
|
683 |
+
* @see self::_setupInlineCrypt()
|
684 |
+
* @see self::_setupKey()
|
685 |
+
* @access private
|
686 |
+
* @return array &$tables
|
687 |
+
*/
|
688 |
+
function &_getInvTables()
|
689 |
+
{
|
690 |
+
static $tables;
|
691 |
+
if (empty($tables)) {
|
692 |
+
$dt3 = array_map('intval', array(
|
693 |
+
0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B,
|
694 |
+
0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5,
|
695 |
+
0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B,
|
696 |
+
0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E,
|
697 |
+
0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D,
|
698 |
+
0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9,
|
699 |
+
0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66,
|
700 |
+
0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED,
|
701 |
+
0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4,
|
702 |
+
0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD,
|
703 |
+
0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60,
|
704 |
+
0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79,
|
705 |
+
0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C,
|
706 |
+
0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24,
|
707 |
+
0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C,
|
708 |
+
0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814,
|
709 |
+
0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B,
|
710 |
+
0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084,
|
711 |
+
0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077,
|
712 |
+
0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22,
|
713 |
+
0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F,
|
714 |
+
0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582,
|
715 |
+
0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB,
|
716 |
+
0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF,
|
717 |
+
0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035,
|
718 |
+
0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17,
|
719 |
+
0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46,
|
720 |
+
0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D,
|
721 |
+
0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A,
|
722 |
+
0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678,
|
723 |
+
0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF,
|
724 |
+
0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0
|
725 |
+
));
|
726 |
+
|
727 |
+
foreach ($dt3 as $dt3i) {
|
728 |
+
$dt0[] = (($dt3i << 24) & 0xFF000000) | (($dt3i >> 8) & 0x00FFFFFF);
|
729 |
+
$dt1[] = (($dt3i << 16) & 0xFFFF0000) | (($dt3i >> 16) & 0x0000FFFF);
|
730 |
+
$dt2[] = (($dt3i << 8) & 0xFFFFFF00) | (($dt3i >> 24) & 0x000000FF);
|
731 |
+
};
|
732 |
+
|
733 |
+
$tables = array(
|
734 |
+
// The Precomputed inverse mixColumns tables dt0 - dt3
|
735 |
+
$dt0,
|
736 |
+
$dt1,
|
737 |
+
$dt2,
|
738 |
+
$dt3,
|
739 |
+
// The inverse SubByte S-Box
|
740 |
+
array(
|
741 |
+
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
|
742 |
+
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
|
743 |
+
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
|
744 |
+
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
|
745 |
+
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
|
746 |
+
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
|
747 |
+
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
|
748 |
+
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
|
749 |
+
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
|
750 |
+
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
|
751 |
+
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
|
752 |
+
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
|
753 |
+
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
|
754 |
+
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
|
755 |
+
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
|
756 |
+
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
|
757 |
+
)
|
758 |
+
);
|
759 |
+
}
|
760 |
+
return $tables;
|
761 |
+
}
|
762 |
+
|
763 |
+
/**
|
764 |
+
* Setup the performance-optimized function for de/encrypt()
|
765 |
+
*
|
766 |
+
* @see \phpseclib\Crypt\Base::_setupInlineCrypt()
|
767 |
+
* @access private
|
768 |
+
*/
|
769 |
+
function _setupInlineCrypt()
|
770 |
+
{
|
771 |
+
// Note: _setupInlineCrypt() will be called only if $this->changed === true
|
772 |
+
// So here we are'nt under the same heavy timing-stress as we are in _de/encryptBlock() or de/encrypt().
|
773 |
+
// However...the here generated function- $code, stored as php callback in $this->inline_crypt, must work as fast as even possible.
|
774 |
+
|
775 |
+
$lambda_functions =& self::_getLambdaFunctions();
|
776 |
+
|
777 |
+
// We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function.
|
778 |
+
// (Currently, for Crypt_Rijndael/AES, one generated $lambda_function cost on php5.5@32bit ~80kb unfreeable mem and ~130kb on php5.5@64bit)
|
779 |
+
// After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one.
|
780 |
+
$gen_hi_opt_code = (bool)(count($lambda_functions) < 10);
|
781 |
+
|
782 |
+
// Generation of a uniqe hash for our generated code
|
783 |
+
$code_hash = "Crypt_Rijndael, {$this->mode}, {$this->Nr}, {$this->Nb}";
|
784 |
+
if ($gen_hi_opt_code) {
|
785 |
+
$code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
|
786 |
+
}
|
787 |
+
|
788 |
+
if (!isset($lambda_functions[$code_hash])) {
|
789 |
+
switch (true) {
|
790 |
+
case $gen_hi_opt_code:
|
791 |
+
// The hi-optimized $lambda_functions will use the key-words hardcoded for better performance.
|
792 |
+
$w = $this->w;
|
793 |
+
$dw = $this->dw;
|
794 |
+
$init_encrypt = '';
|
795 |
+
$init_decrypt = '';
|
796 |
+
break;
|
797 |
+
default:
|
798 |
+
for ($i = 0, $cw = count($this->w); $i < $cw; ++$i) {
|
799 |
+
$w[] = '$w[' . $i . ']';
|
800 |
+
$dw[] = '$dw[' . $i . ']';
|
801 |
+
}
|
802 |
+
$init_encrypt = '$w = $self->w;';
|
803 |
+
$init_decrypt = '$dw = $self->dw;';
|
804 |
+
}
|
805 |
+
|
806 |
+
$Nr = $this->Nr;
|
807 |
+
$Nb = $this->Nb;
|
808 |
+
$c = $this->c;
|
809 |
+
|
810 |
+
// Generating encrypt code:
|
811 |
+
$init_encrypt.= '
|
812 |
+
static $tables;
|
813 |
+
if (empty($tables)) {
|
814 |
+
$tables = &$self->_getTables();
|
815 |
+
}
|
816 |
+
$t0 = $tables[0];
|
817 |
+
$t1 = $tables[1];
|
818 |
+
$t2 = $tables[2];
|
819 |
+
$t3 = $tables[3];
|
820 |
+
$sbox = $tables[4];
|
821 |
+
';
|
822 |
+
|
823 |
+
$s = 'e';
|
824 |
+
$e = 's';
|
825 |
+
$wc = $Nb - 1;
|
826 |
+
|
827 |
+
// Preround: addRoundKey
|
828 |
+
$encrypt_block = '$in = unpack("N*", $in);'."\n";
|
829 |
+
for ($i = 0; $i < $Nb; ++$i) {
|
830 |
+
$encrypt_block .= '$s'.$i.' = $in['.($i + 1).'] ^ '.$w[++$wc].";\n";
|
831 |
+
}
|
832 |
+
|
833 |
+
// Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
|
834 |
+
for ($round = 1; $round < $Nr; ++$round) {
|
835 |
+
list($s, $e) = array($e, $s);
|
836 |
+
for ($i = 0; $i < $Nb; ++$i) {
|
837 |
+
$encrypt_block.=
|
838 |
+
'$'.$e.$i.' =
|
839 |
+
$t0[($'.$s.$i .' >> 24) & 0xff] ^
|
840 |
+
$t1[($'.$s.(($i + $c[1]) % $Nb).' >> 16) & 0xff] ^
|
841 |
+
$t2[($'.$s.(($i + $c[2]) % $Nb).' >> 8) & 0xff] ^
|
842 |
+
$t3[ $'.$s.(($i + $c[3]) % $Nb).' & 0xff] ^
|
843 |
+
'.$w[++$wc].";\n";
|
844 |
+
}
|
845 |
+
}
|
846 |
+
|
847 |
+
// Finalround: subWord + shiftRows + addRoundKey
|
848 |
+
for ($i = 0; $i < $Nb; ++$i) {
|
849 |
+
$encrypt_block.=
|
850 |
+
'$'.$e.$i.' =
|
851 |
+
$sbox[ $'.$e.$i.' & 0xff] |
|
852 |
+
($sbox[($'.$e.$i.' >> 8) & 0xff] << 8) |
|
853 |
+
($sbox[($'.$e.$i.' >> 16) & 0xff] << 16) |
|
854 |
+
($sbox[($'.$e.$i.' >> 24) & 0xff] << 24);'."\n";
|
855 |
+
}
|
856 |
+
$encrypt_block .= '$in = pack("N*"'."\n";
|
857 |
+
for ($i = 0; $i < $Nb; ++$i) {
|
858 |
+
$encrypt_block.= ',
|
859 |
+
($'.$e.$i .' & '.((int)0xFF000000).') ^
|
860 |
+
($'.$e.(($i + $c[1]) % $Nb).' & 0x00FF0000 ) ^
|
861 |
+
($'.$e.(($i + $c[2]) % $Nb).' & 0x0000FF00 ) ^
|
862 |
+
($'.$e.(($i + $c[3]) % $Nb).' & 0x000000FF ) ^
|
863 |
+
'.$w[$i]."\n";
|
864 |
+
}
|
865 |
+
$encrypt_block .= ');';
|
866 |
+
|
867 |
+
// Generating decrypt code:
|
868 |
+
$init_decrypt.= '
|
869 |
+
static $invtables;
|
870 |
+
if (empty($invtables)) {
|
871 |
+
$invtables = &$self->_getInvTables();
|
872 |
+
}
|
873 |
+
$dt0 = $invtables[0];
|
874 |
+
$dt1 = $invtables[1];
|
875 |
+
$dt2 = $invtables[2];
|
876 |
+
$dt3 = $invtables[3];
|
877 |
+
$isbox = $invtables[4];
|
878 |
+
';
|
879 |
+
|
880 |
+
$s = 'e';
|
881 |
+
$e = 's';
|
882 |
+
$wc = $Nb - 1;
|
883 |
+
|
884 |
+
// Preround: addRoundKey
|
885 |
+
$decrypt_block = '$in = unpack("N*", $in);'."\n";
|
886 |
+
for ($i = 0; $i < $Nb; ++$i) {
|
887 |
+
$decrypt_block .= '$s'.$i.' = $in['.($i + 1).'] ^ '.$dw[++$wc].';'."\n";
|
888 |
+
}
|
889 |
+
|
890 |
+
// Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
|
891 |
+
for ($round = 1; $round < $Nr; ++$round) {
|
892 |
+
list($s, $e) = array($e, $s);
|
893 |
+
for ($i = 0; $i < $Nb; ++$i) {
|
894 |
+
$decrypt_block.=
|
895 |
+
'$'.$e.$i.' =
|
896 |
+
$dt0[($'.$s.$i .' >> 24) & 0xff] ^
|
897 |
+
$dt1[($'.$s.(($Nb + $i - $c[1]) % $Nb).' >> 16) & 0xff] ^
|
898 |
+
$dt2[($'.$s.(($Nb + $i - $c[2]) % $Nb).' >> 8) & 0xff] ^
|
899 |
+
$dt3[ $'.$s.(($Nb + $i - $c[3]) % $Nb).' & 0xff] ^
|
900 |
+
'.$dw[++$wc].";\n";
|
901 |
+
}
|
902 |
+
}
|
903 |
+
|
904 |
+
// Finalround: subWord + shiftRows + addRoundKey
|
905 |
+
for ($i = 0; $i < $Nb; ++$i) {
|
906 |
+
$decrypt_block.=
|
907 |
+
'$'.$e.$i.' =
|
908 |
+
$isbox[ $'.$e.$i.' & 0xff] |
|
909 |
+
($isbox[($'.$e.$i.' >> 8) & 0xff] << 8) |
|
910 |
+
($isbox[($'.$e.$i.' >> 16) & 0xff] << 16) |
|
911 |
+
($isbox[($'.$e.$i.' >> 24) & 0xff] << 24);'."\n";
|
912 |
+
}
|
913 |
+
$decrypt_block .= '$in = pack("N*"'."\n";
|
914 |
+
for ($i = 0; $i < $Nb; ++$i) {
|
915 |
+
$decrypt_block.= ',
|
916 |
+
($'.$e.$i. ' & '.((int)0xFF000000).') ^
|
917 |
+
($'.$e.(($Nb + $i - $c[1]) % $Nb).' & 0x00FF0000 ) ^
|
918 |
+
($'.$e.(($Nb + $i - $c[2]) % $Nb).' & 0x0000FF00 ) ^
|
919 |
+
($'.$e.(($Nb + $i - $c[3]) % $Nb).' & 0x000000FF ) ^
|
920 |
+
'.$dw[$i]."\n";
|
921 |
+
}
|
922 |
+
$decrypt_block .= ');';
|
923 |
+
|
924 |
+
$lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
|
925 |
+
array(
|
926 |
+
'init_crypt' => '',
|
927 |
+
'init_encrypt' => $init_encrypt,
|
928 |
+
'init_decrypt' => $init_decrypt,
|
929 |
+
'encrypt_block' => $encrypt_block,
|
930 |
+
'decrypt_block' => $decrypt_block
|
931 |
+
)
|
932 |
+
);
|
933 |
+
}
|
934 |
+
$this->inline_crypt = $lambda_functions[$code_hash];
|
935 |
+
}
|
936 |
+
}
|
vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php
CHANGED
@@ -1,3779 +1,3779 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Pure-PHP arbitrary precision integer arithmetic library.
|
5 |
-
*
|
6 |
-
* Supports base-2, base-10, base-16, and base-256 numbers. Uses the GMP or BCMath extensions, if available,
|
7 |
-
* and an internal implementation, otherwise.
|
8 |
-
*
|
9 |
-
* PHP version 5
|
10 |
-
*
|
11 |
-
* {@internal (all DocBlock comments regarding implementation - such as the one that follows - refer to the
|
12 |
-
* {@link self::MODE_INTERNAL self::MODE_INTERNAL} mode)
|
13 |
-
*
|
14 |
-
* BigInteger uses base-2**26 to perform operations such as multiplication and division and
|
15 |
-
* base-2**52 (ie. two base 2**26 digits) to perform addition and subtraction. Because the largest possible
|
16 |
-
* value when multiplying two base-2**26 numbers together is a base-2**52 number, double precision floating
|
17 |
-
* point numbers - numbers that should be supported on most hardware and whose significand is 53 bits - are
|
18 |
-
* used. As a consequence, bitwise operators such as >> and << cannot be used, nor can the modulo operator %,
|
19 |
-
* which only supports integers. Although this fact will slow this library down, the fact that such a high
|
20 |
-
* base is being used should more than compensate.
|
21 |
-
*
|
22 |
-
* Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness little endian} format. ie.
|
23 |
-
* (new \phpseclib\Math\BigInteger(pow(2, 26)))->value = array(0, 1)
|
24 |
-
*
|
25 |
-
* Useful resources are as follows:
|
26 |
-
*
|
27 |
-
* - {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf Handbook of Applied Cryptography (HAC)}
|
28 |
-
* - {@link http://math.libtomcrypt.com/files/tommath.pdf Multi-Precision Math (MPM)}
|
29 |
-
* - Java's BigInteger classes. See /j2se/src/share/classes/java/math in jdk-1_5_0-src-jrl.zip
|
30 |
-
*
|
31 |
-
* Here's an example of how to use this library:
|
32 |
-
* <code>
|
33 |
-
* <?php
|
34 |
-
* $a = new \phpseclib\Math\BigInteger(2);
|
35 |
-
* $b = new \phpseclib\Math\BigInteger(3);
|
36 |
-
*
|
37 |
-
* $c = $a->add($b);
|
38 |
-
*
|
39 |
-
* echo $c->toString(); // outputs 5
|
40 |
-
* ?>
|
41 |
-
* </code>
|
42 |
-
*
|
43 |
-
* @category Math
|
44 |
-
* @package BigInteger
|
45 |
-
* @author Jim Wigginton <terrafrost@php.net>
|
46 |
-
* @copyright 2006 Jim Wigginton
|
47 |
-
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
48 |
-
*/
|
49 |
-
|
50 |
-
namespace phpseclib\Math;
|
51 |
-
|
52 |
-
use phpseclib\Crypt\Random;
|
53 |
-
|
54 |
-
/**
|
55 |
-
* Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256
|
56 |
-
* numbers.
|
57 |
-
*
|
58 |
-
* @package BigInteger
|
59 |
-
* @author Jim Wigginton <terrafrost@php.net>
|
60 |
-
* @access public
|
61 |
-
*/
|
62 |
-
class BigInteger
|
63 |
-
{
|
64 |
-
/**#@+
|
65 |
-
* Reduction constants
|
66 |
-
*
|
67 |
-
* @access private
|
68 |
-
* @see BigInteger::_reduce()
|
69 |
-
*/
|
70 |
-
/**
|
71 |
-
* @see BigInteger::_montgomery()
|
72 |
-
* @see BigInteger::_prepMontgomery()
|
73 |
-
*/
|
74 |
-
const MONTGOMERY = 0;
|
75 |
-
/**
|
76 |
-
* @see BigInteger::_barrett()
|
77 |
-
*/
|
78 |
-
const BARRETT = 1;
|
79 |
-
/**
|
80 |
-
* @see BigInteger::_mod2()
|
81 |
-
*/
|
82 |
-
const POWEROF2 = 2;
|
83 |
-
/**
|
84 |
-
* @see BigInteger::_remainder()
|
85 |
-
*/
|
86 |
-
const CLASSIC = 3;
|
87 |
-
/**
|
88 |
-
* @see BigInteger::__clone()
|
89 |
-
*/
|
90 |
-
const NONE = 4;
|
91 |
-
/**#@-*/
|
92 |
-
|
93 |
-
/**#@+
|
94 |
-
* Array constants
|
95 |
-
*
|
96 |
-
* Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and
|
97 |
-
* multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them.
|
98 |
-
*
|
99 |
-
* @access private
|
100 |
-
*/
|
101 |
-
/**
|
102 |
-
* $result[self::VALUE] contains the value.
|
103 |
-
*/
|
104 |
-
const VALUE = 0;
|
105 |
-
/**
|
106 |
-
* $result[self::SIGN] contains the sign.
|
107 |
-
*/
|
108 |
-
const SIGN = 1;
|
109 |
-
/**#@-*/
|
110 |
-
|
111 |
-
/**#@+
|
112 |
-
* @access private
|
113 |
-
* @see BigInteger::_montgomery()
|
114 |
-
* @see BigInteger::_barrett()
|
115 |
-
*/
|
116 |
-
/**
|
117 |
-
* Cache constants
|
118 |
-
*
|
119 |
-
* $cache[self::VARIABLE] tells us whether or not the cached data is still valid.
|
120 |
-
*/
|
121 |
-
const VARIABLE = 0;
|
122 |
-
/**
|
123 |
-
* $cache[self::DATA] contains the cached data.
|
124 |
-
*/
|
125 |
-
const DATA = 1;
|
126 |
-
/**#@-*/
|
127 |
-
|
128 |
-
/**#@+
|
129 |
-
* Mode constants.
|
130 |
-
*
|
131 |
-
* @access private
|
132 |
-
* @see BigInteger::__construct()
|
133 |
-
*/
|
134 |
-
/**
|
135 |
-
* To use the pure-PHP implementation
|
136 |
-
*/
|
137 |
-
const MODE_INTERNAL = 1;
|
138 |
-
/**
|
139 |
-
* To use the BCMath library
|
140 |
-
*
|
141 |
-
* (if enabled; otherwise, the internal implementation will be used)
|
142 |
-
*/
|
143 |
-
const MODE_BCMATH = 2;
|
144 |
-
/**
|
145 |
-
* To use the GMP library
|
146 |
-
*
|
147 |
-
* (if present; otherwise, either the BCMath or the internal implementation will be used)
|
148 |
-
*/
|
149 |
-
const MODE_GMP = 3;
|
150 |
-
/**#@-*/
|
151 |
-
|
152 |
-
/**
|
153 |
-
* Karatsuba Cutoff
|
154 |
-
*
|
155 |
-
* At what point do we switch between Karatsuba multiplication and schoolbook long multiplication?
|
156 |
-
*
|
157 |
-
* @access private
|
158 |
-
*/
|
159 |
-
const KARATSUBA_CUTOFF = 25;
|
160 |
-
|
161 |
-
/**#@+
|
162 |
-
* Static properties used by the pure-PHP implementation.
|
163 |
-
*
|
164 |
-
* @see __construct()
|
165 |
-
*/
|
166 |
-
protected static $base;
|
167 |
-
protected static $baseFull;
|
168 |
-
protected static $maxDigit;
|
169 |
-
protected static $msb;
|
170 |
-
|
171 |
-
/**
|
172 |
-
* $max10 in greatest $max10Len satisfying
|
173 |
-
* $max10 = 10**$max10Len <= 2**$base.
|
174 |
-
*/
|
175 |
-
protected static $max10;
|
176 |
-
|
177 |
-
/**
|
178 |
-
* $max10Len in greatest $max10Len satisfying
|
179 |
-
* $max10 = 10**$max10Len <= 2**$base.
|
180 |
-
*/
|
181 |
-
protected static $max10Len;
|
182 |
-
protected static $maxDigit2;
|
183 |
-
/**#@-*/
|
184 |
-
|
185 |
-
/**
|
186 |
-
* Holds the BigInteger's value.
|
187 |
-
*
|
188 |
-
* @var array
|
189 |
-
* @access private
|
190 |
-
*/
|
191 |
-
var $value;
|
192 |
-
|
193 |
-
/**
|
194 |
-
* Holds the BigInteger's magnitude.
|
195 |
-
*
|
196 |
-
* @var bool
|
197 |
-
* @access private
|
198 |
-
*/
|
199 |
-
var $is_negative = false;
|
200 |
-
|
201 |
-
/**
|
202 |
-
* Precision
|
203 |
-
*
|
204 |
-
* @see self::setPrecision()
|
205 |
-
* @access private
|
206 |
-
*/
|
207 |
-
var $precision = -1;
|
208 |
-
|
209 |
-
/**
|
210 |
-
* Precision Bitmask
|
211 |
-
*
|
212 |
-
* @see self::setPrecision()
|
213 |
-
* @access private
|
214 |
-
*/
|
215 |
-
var $bitmask = false;
|
216 |
-
|
217 |
-
/**
|
218 |
-
* Mode independent value used for serialization.
|
219 |
-
*
|
220 |
-
* If the bcmath or gmp extensions are installed $this->value will be a non-serializable resource, hence the need for
|
221 |
-
* a variable that'll be serializable regardless of whether or not extensions are being used. Unlike $this->value,
|
222 |
-
* however, $this->hex is only calculated when $this->__sleep() is called.
|
223 |
-
*
|
224 |
-
* @see self::__sleep()
|
225 |
-
* @see self::__wakeup()
|
226 |
-
* @var string
|
227 |
-
* @access private
|
228 |
-
*/
|
229 |
-
var $hex;
|
230 |
-
|
231 |
-
/**
|
232 |
-
* Converts base-2, base-10, base-16, and binary strings (base-256) to BigIntegers.
|
233 |
-
*
|
234 |
-
* If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using
|
235 |
-
* two's compliment. The sole exception to this is -10, which is treated the same as 10 is.
|
236 |
-
*
|
237 |
-
* Here's an example:
|
238 |
-
* <code>
|
239 |
-
* <?php
|
240 |
-
* $a = new \phpseclib\Math\BigInteger('0x32', 16); // 50 in base-16
|
241 |
-
*
|
242 |
-
* echo $a->toString(); // outputs 50
|
243 |
-
* ?>
|
244 |
-
* </code>
|
245 |
-
*
|
246 |
-
* @param $x base-10 number or base-$base number if $base set.
|
247 |
-
* @param int $base
|
248 |
-
* @return \phpseclib\Math\BigInteger
|
249 |
-
* @access public
|
250 |
-
*/
|
251 |
-
function __construct($x = 0, $base = 10)
|
252 |
-
{
|
253 |
-
if (!defined('MATH_BIGINTEGER_MODE')) {
|
254 |
-
switch (true) {
|
255 |
-
case extension_loaded('gmp'):
|
256 |
-
define('MATH_BIGINTEGER_MODE', self::MODE_GMP);
|
257 |
-
break;
|
258 |
-
case extension_loaded('bcmath'):
|
259 |
-
define('MATH_BIGINTEGER_MODE', self::MODE_BCMATH);
|
260 |
-
break;
|
261 |
-
default:
|
262 |
-
define('MATH_BIGINTEGER_MODE', self::MODE_INTERNAL);
|
263 |
-
}
|
264 |
-
}
|
265 |
-
|
266 |
-
if (extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
|
267 |
-
// some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
|
268 |
-
$versions = array();
|
269 |
-
|
270 |
-
// avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems)
|
271 |
-
if (strpos(ini_get('disable_functions'), 'phpinfo') === false) {
|
272 |
-
ob_start();
|
273 |
-
@phpinfo();
|
274 |
-
$content = ob_get_contents();
|
275 |
-
ob_end_clean();
|
276 |
-
|
277 |
-
preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
|
278 |
-
|
279 |
-
if (!empty($matches[1])) {
|
280 |
-
for ($i = 0; $i < count($matches[1]); $i++) {
|
281 |
-
$fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
|
282 |
-
|
283 |
-
// Remove letter part in OpenSSL version
|
284 |
-
if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
|
285 |
-
$versions[$matches[1][$i]] = $fullVersion;
|
286 |
-
} else {
|
287 |
-
$versions[$matches[1][$i]] = $m[0];
|
288 |
-
}
|
289 |
-
}
|
290 |
-
}
|
291 |
-
}
|
292 |
-
|
293 |
-
// it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
|
294 |
-
switch (true) {
|
295 |
-
case !isset($versions['Header']):
|
296 |
-
case !isset($versions['Library']):
|
297 |
-
case $versions['Header'] == $versions['Library']:
|
298 |
-
case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0:
|
299 |
-
define('MATH_BIGINTEGER_OPENSSL_ENABLED', true);
|
300 |
-
break;
|
301 |
-
default:
|
302 |
-
define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
|
303 |
-
}
|
304 |
-
}
|
305 |
-
|
306 |
-
if (!defined('PHP_INT_SIZE')) {
|
307 |
-
define('PHP_INT_SIZE', 4);
|
308 |
-
}
|
309 |
-
|
310 |
-
if (empty(self::$base) && MATH_BIGINTEGER_MODE == self::MODE_INTERNAL) {
|
311 |
-
switch (PHP_INT_SIZE) {
|
312 |
-
case 8: // use 64-bit integers if int size is 8 bytes
|
313 |
-
self::$base = 31;
|
314 |
-
self::$baseFull = 0x80000000;
|
315 |
-
self::$maxDigit = 0x7FFFFFFF;
|
316 |
-
self::$msb = 0x40000000;
|
317 |
-
self::$max10 = 1000000000;
|
318 |
-
self::$max10Len = 9;
|
319 |
-
self::$maxDigit2 = pow(2, 62);
|
320 |
-
break;
|
321 |
-
//case 4: // use 64-bit floats if int size is 4 bytes
|
322 |
-
default:
|
323 |
-
self::$base = 26;
|
324 |
-
self::$baseFull = 0x4000000;
|
325 |
-
self::$maxDigit = 0x3FFFFFF;
|
326 |
-
self::$msb = 0x2000000;
|
327 |
-
self::$max10 = 10000000;
|
328 |
-
self::$max10Len = 7;
|
329 |
-
self::$maxDigit2 = pow(2, 52); // pow() prevents truncation
|
330 |
-
}
|
331 |
-
}
|
332 |
-
|
333 |
-
switch (MATH_BIGINTEGER_MODE) {
|
334 |
-
case self::MODE_GMP:
|
335 |
-
switch (true) {
|
336 |
-
case is_resource($x) && get_resource_type($x) == 'GMP integer':
|
337 |
-
// PHP 5.6 switched GMP from using resources to objects
|
338 |
-
case $x instanceof \GMP:
|
339 |
-
$this->value = $x;
|
340 |
-
return;
|
341 |
-
}
|
342 |
-
$this->value = gmp_init(0);
|
343 |
-
break;
|
344 |
-
case self::MODE_BCMATH:
|
345 |
-
$this->value = '0';
|
346 |
-
break;
|
347 |
-
default:
|
348 |
-
$this->value = array();
|
349 |
-
}
|
350 |
-
|
351 |
-
// '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48
|
352 |
-
// '0' is the only value like this per http://php.net/empty
|
353 |
-
if (empty($x) && (abs($base) != 256 || $x !== '0')) {
|
354 |
-
return;
|
355 |
-
}
|
356 |
-
|
357 |
-
switch ($base) {
|
358 |
-
case -256:
|
359 |
-
if (ord($x[0]) & 0x80) {
|
360 |
-
$x = ~$x;
|
361 |
-
$this->is_negative = true;
|
362 |
-
}
|
363 |
-
case 256:
|
364 |
-
switch (MATH_BIGINTEGER_MODE) {
|
365 |
-
case self::MODE_GMP:
|
366 |
-
$this->value = function_exists('gmp_import') ?
|
367 |
-
gmp_import($x) :
|
368 |
-
gmp_init('0x' . bin2hex($x));
|
369 |
-
if ($this->is_negative) {
|
370 |
-
$this->value = gmp_neg($this->value);
|
371 |
-
}
|
372 |
-
break;
|
373 |
-
case self::MODE_BCMATH:
|
374 |
-
// round $len to the nearest 4 (thanks, DavidMJ!)
|
375 |
-
$len = (strlen($x) + 3) & 0xFFFFFFFC;
|
376 |
-
|
377 |
-
$x = str_pad($x, $len, chr(0), STR_PAD_LEFT);
|
378 |
-
|
379 |
-
for ($i = 0; $i < $len; $i+= 4) {
|
380 |
-
$this->value = bcmul($this->value, '4294967296', 0); // 4294967296 == 2**32
|
381 |
-
$this->value = bcadd($this->value, 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2]) << 8) | ord($x[$i + 3])), 0);
|
382 |
-
}
|
383 |
-
|
384 |
-
if ($this->is_negative) {
|
385 |
-
$this->value = '-' . $this->value;
|
386 |
-
}
|
387 |
-
|
388 |
-
break;
|
389 |
-
// converts a base-2**8 (big endian / msb) number to base-2**26 (little endian / lsb)
|
390 |
-
default:
|
391 |
-
while (strlen($x)) {
|
392 |
-
$this->value[] = $this->_bytes2int($this->_base256_rshift($x, self::$base));
|
393 |
-
}
|
394 |
-
}
|
395 |
-
|
396 |
-
if ($this->is_negative) {
|
397 |
-
if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
|
398 |
-
$this->is_negative = false;
|
399 |
-
}
|
400 |
-
$temp = $this->add(new static('-1'));
|
401 |
-
$this->value = $temp->value;
|
402 |
-
}
|
403 |
-
break;
|
404 |
-
case 16:
|
405 |
-
case -16:
|
406 |
-
if ($base > 0 && $x[0] == '-') {
|
407 |
-
$this->is_negative = true;
|
408 |
-
$x = substr($x, 1);
|
409 |
-
}
|
410 |
-
|
411 |
-
$x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#', '$1', $x);
|
412 |
-
|
413 |
-
$is_negative = false;
|
414 |
-
if ($base < 0 && hexdec($x[0]) >= 8) {
|
415 |
-
$this->is_negative = $is_negative = true;
|
416 |
-
$x = bin2hex(~pack('H*', $x));
|
417 |
-
}
|
418 |
-
|
419 |
-
switch (MATH_BIGINTEGER_MODE) {
|
420 |
-
case self::MODE_GMP:
|
421 |
-
$temp = $this->is_negative ? '-0x' . $x : '0x' . $x;
|
422 |
-
$this->value = gmp_init($temp);
|
423 |
-
$this->is_negative = false;
|
424 |
-
break;
|
425 |
-
case self::MODE_BCMATH:
|
426 |
-
$x = (strlen($x) & 1) ? '0' . $x : $x;
|
427 |
-
$temp = new static(pack('H*', $x), 256);
|
428 |
-
$this->value = $this->is_negative ? '-' . $temp->value : $temp->value;
|
429 |
-
$this->is_negative = false;
|
430 |
-
break;
|
431 |
-
default:
|
432 |
-
$x = (strlen($x) & 1) ? '0' . $x : $x;
|
433 |
-
$temp = new static(pack('H*', $x), 256);
|
434 |
-
$this->value = $temp->value;
|
435 |
-
}
|
436 |
-
|
437 |
-
if ($is_negative) {
|
438 |
-
$temp = $this->add(new static('-1'));
|
439 |
-
$this->value = $temp->value;
|
440 |
-
}
|
441 |
-
break;
|
442 |
-
case 10:
|
443 |
-
case -10:
|
444 |
-
// (?<!^)(?:-).*: find any -'s that aren't at the beginning and then any characters that follow that
|
445 |
-
// (?<=^|-)0*: find any 0's that are preceded by the start of the string or by a - (ie. octals)
|
446 |
-
// [^-0-9].*: find any non-numeric characters and then any characters that follow that
|
447 |
-
$x = preg_replace('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#', '', $x);
|
448 |
-
if (!strlen($x) || $x == '-') {
|
449 |
-
$x = '0';
|
450 |
-
}
|
451 |
-
|
452 |
-
switch (MATH_BIGINTEGER_MODE) {
|
453 |
-
case self::MODE_GMP:
|
454 |
-
$this->value = gmp_init($x);
|
455 |
-
break;
|
456 |
-
case self::MODE_BCMATH:
|
457 |
-
// explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different
|
458 |
-
// results then doing it on '-1' does (modInverse does $x[0])
|
459 |
-
$this->value = $x === '-' ? '0' : (string) $x;
|
460 |
-
break;
|
461 |
-
default:
|
462 |
-
$temp = new static();
|
463 |
-
|
464 |
-
$multiplier = new static();
|
465 |
-
$multiplier->value = array(self::$max10);
|
466 |
-
|
467 |
-
if ($x[0] == '-') {
|
468 |
-
$this->is_negative = true;
|
469 |
-
$x = substr($x, 1);
|
470 |
-
}
|
471 |
-
|
472 |
-
$x = str_pad($x, strlen($x) + ((self::$max10Len - 1) * strlen($x)) % self::$max10Len, 0, STR_PAD_LEFT);
|
473 |
-
while (strlen($x)) {
|
474 |
-
$temp = $temp->multiply($multiplier);
|
475 |
-
$temp = $temp->add(new static($this->_int2bytes(substr($x, 0, self::$max10Len)), 256));
|
476 |
-
$x = substr($x, self::$max10Len);
|
477 |
-
}
|
478 |
-
|
479 |
-
$this->value = $temp->value;
|
480 |
-
}
|
481 |
-
break;
|
482 |
-
case 2: // base-2 support originally implemented by Lluis Pamies - thanks!
|
483 |
-
case -2:
|
484 |
-
if ($base > 0 && $x[0] == '-') {
|
485 |
-
$this->is_negative = true;
|
486 |
-
$x = substr($x, 1);
|
487 |
-
}
|
488 |
-
|
489 |
-
$x = preg_replace('#^([01]*).*#', '$1', $x);
|
490 |
-
$x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT);
|
491 |
-
|
492 |
-
$str = '0x';
|
493 |
-
while (strlen($x)) {
|
494 |
-
$part = substr($x, 0, 4);
|
495 |
-
$str.= dechex(bindec($part));
|
496 |
-
$x = substr($x, 4);
|
497 |
-
}
|
498 |
-
|
499 |
-
if ($this->is_negative) {
|
500 |
-
$str = '-' . $str;
|
501 |
-
}
|
502 |
-
|
503 |
-
$temp = new static($str, 8 * $base); // ie. either -16 or +16
|
504 |
-
$this->value = $temp->value;
|
505 |
-
$this->is_negative = $temp->is_negative;
|
506 |
-
|
507 |
-
break;
|
508 |
-
default:
|
509 |
-
// base not supported, so we'll let $this == 0
|
510 |
-
}
|
511 |
-
}
|
512 |
-
|
513 |
-
/**
|
514 |
-
* Converts a BigInteger to a byte string (eg. base-256).
|
515 |
-
*
|
516 |
-
* Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
|
517 |
-
* saved as two's compliment.
|
518 |
-
*
|
519 |
-
* Here's an example:
|
520 |
-
* <code>
|
521 |
-
* <?php
|
522 |
-
* $a = new \phpseclib\Math\BigInteger('65');
|
523 |
-
*
|
524 |
-
* echo $a->toBytes(); // outputs chr(65)
|
525 |
-
* ?>
|
526 |
-
* </code>
|
527 |
-
*
|
528 |
-
* @param bool $twos_compliment
|
529 |
-
* @return string
|
530 |
-
* @access public
|
531 |
-
* @internal Converts a base-2**26 number to base-2**8
|
532 |
-
*/
|
533 |
-
function toBytes($twos_compliment = false)
|
534 |
-
{
|
535 |
-
if ($twos_compliment) {
|
536 |
-
$comparison = $this->compare(new static());
|
537 |
-
if ($comparison == 0) {
|
538 |
-
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
|
539 |
-
}
|
540 |
-
|
541 |
-
$temp = $comparison < 0 ? $this->add(new static(1)) : $this->copy();
|
542 |
-
$bytes = $temp->toBytes();
|
543 |
-
|
544 |
-
if (!strlen($bytes)) { // eg. if the number we're trying to convert is -1
|
545 |
-
$bytes = chr(0);
|
546 |
-
}
|
547 |
-
|
548 |
-
if (ord($bytes[0]) & 0x80) {
|
549 |
-
$bytes = chr(0) . $bytes;
|
550 |
-
}
|
551 |
-
|
552 |
-
return $comparison < 0 ? ~$bytes : $bytes;
|
553 |
-
}
|
554 |
-
|
555 |
-
switch (MATH_BIGINTEGER_MODE) {
|
556 |
-
case self::MODE_GMP:
|
557 |
-
if (gmp_cmp($this->value, gmp_init(0)) == 0) {
|
558 |
-
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
|
559 |
-
}
|
560 |
-
|
561 |
-
if (function_exists('gmp_export')) {
|
562 |
-
$temp = gmp_export($this->value);
|
563 |
-
} else {
|
564 |
-
$temp = gmp_strval(gmp_abs($this->value), 16);
|
565 |
-
$temp = (strlen($temp) & 1) ? '0' . $temp : $temp;
|
566 |
-
$temp = pack('H*', $temp);
|
567 |
-
}
|
568 |
-
|
569 |
-
return $this->precision > 0 ?
|
570 |
-
substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
|
571 |
-
ltrim($temp, chr(0));
|
572 |
-
case self::MODE_BCMATH:
|
573 |
-
if ($this->value === '0') {
|
574 |
-
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
|
575 |
-
}
|
576 |
-
|
577 |
-
$value = '';
|
578 |
-
$current = $this->value;
|
579 |
-
|
580 |
-
if ($current[0] == '-') {
|
581 |
-
$current = substr($current, 1);
|
582 |
-
}
|
583 |
-
|
584 |
-
while (bccomp($current, '0', 0) > 0) {
|
585 |
-
$temp = bcmod($current, '16777216');
|
586 |
-
$value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value;
|
587 |
-
$current = bcdiv($current, '16777216', 0);
|
588 |
-
}
|
589 |
-
|
590 |
-
return $this->precision > 0 ?
|
591 |
-
substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
|
592 |
-
ltrim($value, chr(0));
|
593 |
-
}
|
594 |
-
|
595 |
-
if (!count($this->value)) {
|
596 |
-
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
|
597 |
-
}
|
598 |
-
$result = $this->_int2bytes($this->value[count($this->value) - 1]);
|
599 |
-
|
600 |
-
$temp = $this->copy();
|
601 |
-
|
602 |
-
for ($i = count($temp->value) - 2; $i >= 0; --$i) {
|
603 |
-
$temp->_base256_lshift($result, self::$base);
|
604 |
-
$result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT);
|
605 |
-
}
|
606 |
-
|
607 |
-
return $this->precision > 0 ?
|
608 |
-
str_pad(substr($result, -(($this->precision + 7) >> 3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) :
|
609 |
-
$result;
|
610 |
-
}
|
611 |
-
|
612 |
-
/**
|
613 |
-
* Converts a BigInteger to a hex string (eg. base-16)).
|
614 |
-
*
|
615 |
-
* Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
|
616 |
-
* saved as two's compliment.
|
617 |
-
*
|
618 |
-
* Here's an example:
|
619 |
-
* <code>
|
620 |
-
* <?php
|
621 |
-
* $a = new \phpseclib\Math\BigInteger('65');
|
622 |
-
*
|
623 |
-
* echo $a->toHex(); // outputs '41'
|
624 |
-
* ?>
|
625 |
-
* </code>
|
626 |
-
*
|
627 |
-
* @param bool $twos_compliment
|
628 |
-
* @return string
|
629 |
-
* @access public
|
630 |
-
* @internal Converts a base-2**26 number to base-2**8
|
631 |
-
*/
|
632 |
-
function toHex($twos_compliment = false)
|
633 |
-
{
|
634 |
-
return bin2hex($this->toBytes($twos_compliment));
|
635 |
-
}
|
636 |
-
|
637 |
-
/**
|
638 |
-
* Converts a BigInteger to a bit string (eg. base-2).
|
639 |
-
*
|
640 |
-
* Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
|
641 |
-
* saved as two's compliment.
|
642 |
-
*
|
643 |
-
* Here's an example:
|
644 |
-
* <code>
|
645 |
-
* <?php
|
646 |
-
* $a = new \phpseclib\Math\BigInteger('65');
|
647 |
-
*
|
648 |
-
* echo $a->toBits(); // outputs '1000001'
|
649 |
-
* ?>
|
650 |
-
* </code>
|
651 |
-
*
|
652 |
-
* @param bool $twos_compliment
|
653 |
-
* @return string
|
654 |
-
* @access public
|
655 |
-
* @internal Converts a base-2**26 number to base-2**2
|
656 |
-
*/
|
657 |
-
function toBits($twos_compliment = false)
|
658 |
-
{
|
659 |
-
$hex = $this->toHex($twos_compliment);
|
660 |
-
$bits = '';
|
661 |
-
for ($i = strlen($hex) - 8, $start = strlen($hex) & 7; $i >= $start; $i-=8) {
|
662 |
-
$bits = str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT) . $bits;
|
663 |
-
}
|
664 |
-
if ($start) { // hexdec('') == 0
|
665 |
-
$bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8, '0', STR_PAD_LEFT) . $bits;
|
666 |
-
}
|
667 |
-
$result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');
|
668 |
-
|
669 |
-
if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) {
|
670 |
-
return '0' . $result;
|
671 |
-
}
|
672 |
-
|
673 |
-
return $result;
|
674 |
-
}
|
675 |
-
|
676 |
-
/**
|
677 |
-
* Converts a BigInteger to a base-10 number.
|
678 |
-
*
|
679 |
-
* Here's an example:
|
680 |
-
* <code>
|
681 |
-
* <?php
|
682 |
-
* $a = new \phpseclib\Math\BigInteger('50');
|
683 |
-
*
|
684 |
-
* echo $a->toString(); // outputs 50
|
685 |
-
* ?>
|
686 |
-
* </code>
|
687 |
-
*
|
688 |
-
* @return string
|
689 |
-
* @access public
|
690 |
-
* @internal Converts a base-2**26 number to base-10**7 (which is pretty much base-10)
|
691 |
-
*/
|
692 |
-
function toString()
|
693 |
-
{
|
694 |
-
switch (MATH_BIGINTEGER_MODE) {
|
695 |
-
case self::MODE_GMP:
|
696 |
-
return gmp_strval($this->value);
|
697 |
-
case self::MODE_BCMATH:
|
698 |
-
if ($this->value === '0') {
|
699 |
-
return '0';
|
700 |
-
}
|
701 |
-
|
702 |
-
return ltrim($this->value, '0');
|
703 |
-
}
|
704 |
-
|
705 |
-
if (!count($this->value)) {
|
706 |
-
return '0';
|
707 |
-
}
|
708 |
-
|
709 |
-
$temp = $this->copy();
|
710 |
-
$temp->is_negative = false;
|
711 |
-
|
712 |
-
$divisor = new static();
|
713 |
-
$divisor->value = array(self::$max10);
|
714 |
-
$result = '';
|
715 |
-
while (count($temp->value)) {
|
716 |
-
list($temp, $mod) = $temp->divide($divisor);
|
717 |
-
$result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', self::$max10Len, '0', STR_PAD_LEFT) . $result;
|
718 |
-
}
|
719 |
-
$result = ltrim($result, '0');
|
720 |
-
if (empty($result)) {
|
721 |
-
$result = '0';
|
722 |
-
}
|
723 |
-
|
724 |
-
if ($this->is_negative) {
|
725 |
-
$result = '-' . $result;
|
726 |
-
}
|
727 |
-
|
728 |
-
return $result;
|
729 |
-
}
|
730 |
-
|
731 |
-
/**
|
732 |
-
* Copy an object
|
733 |
-
*
|
734 |
-
* PHP5 passes objects by reference while PHP4 passes by value. As such, we need a function to guarantee
|
735 |
-
* that all objects are passed by value, when appropriate. More information can be found here:
|
736 |
-
*
|
737 |
-
* {@link http://php.net/language.oop5.basic#51624}
|
738 |
-
*
|
739 |
-
* @access public
|
740 |
-
* @see self::__clone()
|
741 |
-
* @return \phpseclib\Math\BigInteger
|
742 |
-
*/
|
743 |
-
function copy()
|
744 |
-
{
|
745 |
-
$temp = new static();
|
746 |
-
$temp->value = $this->value;
|
747 |
-
$temp->is_negative = $this->is_negative;
|
748 |
-
$temp->precision = $this->precision;
|
749 |
-
$temp->bitmask = $this->bitmask;
|
750 |
-
return $temp;
|
751 |
-
}
|
752 |
-
|
753 |
-
/**
|
754 |
-
* __toString() magic method
|
755 |
-
*
|
756 |
-
* Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
|
757 |
-
* toString().
|
758 |
-
*
|
759 |
-
* @access public
|
760 |
-
* @internal Implemented per a suggestion by Techie-Michael - thanks!
|
761 |
-
*/
|
762 |
-
function __toString()
|
763 |
-
{
|
764 |
-
return $this->toString();
|
765 |
-
}
|
766 |
-
|
767 |
-
/**
|
768 |
-
* __clone() magic method
|
769 |
-
*
|
770 |
-
* Although you can call BigInteger::__toString() directly in PHP5, you cannot call BigInteger::__clone() directly
|
771 |
-
* in PHP5. You can in PHP4 since it's not a magic method, but in PHP5, you have to call it by using the PHP5
|
772 |
-
* only syntax of $y = clone $x. As such, if you're trying to write an application that works on both PHP4 and
|
773 |
-
* PHP5, call BigInteger::copy(), instead.
|
774 |
-
*
|
775 |
-
* @access public
|
776 |
-
* @see self::copy()
|
777 |
-
* @return \phpseclib\Math\BigInteger
|
778 |
-
*/
|
779 |
-
function __clone()
|
780 |
-
{
|
781 |
-
return $this->copy();
|
782 |
-
}
|
783 |
-
|
784 |
-
/**
|
785 |
-
* __sleep() magic method
|
786 |
-
*
|
787 |
-
* Will be called, automatically, when serialize() is called on a BigInteger object.
|
788 |
-
*
|
789 |
-
* @see self::__wakeup()
|
790 |
-
* @access public
|
791 |
-
*/
|
792 |
-
function __sleep()
|
793 |
-
{
|
794 |
-
$this->hex = $this->toHex(true);
|
795 |
-
$vars = array('hex');
|
796 |
-
if ($this->precision > 0) {
|
797 |
-
$vars[] = 'precision';
|
798 |
-
}
|
799 |
-
return $vars;
|
800 |
-
}
|
801 |
-
|
802 |
-
/**
|
803 |
-
* __wakeup() magic method
|
804 |
-
*
|
805 |
-
* Will be called, automatically, when unserialize() is called on a BigInteger object.
|
806 |
-
*
|
807 |
-
* @see self::__sleep()
|
808 |
-
* @access public
|
809 |
-
*/
|
810 |
-
function __wakeup()
|
811 |
-
{
|
812 |
-
$temp = new static($this->hex, -16);
|
813 |
-
$this->value = $temp->value;
|
814 |
-
$this->is_negative = $temp->is_negative;
|
815 |
-
if ($this->precision > 0) {
|
816 |
-
// recalculate $this->bitmask
|
817 |
-
$this->setPrecision($this->precision);
|
818 |
-
}
|
819 |
-
}
|
820 |
-
|
821 |
-
/**
|
822 |
-
* __debugInfo() magic method
|
823 |
-
*
|
824 |
-
* Will be called, automatically, when print_r() or var_dump() are called
|
825 |
-
*
|
826 |
-
* @access public
|
827 |
-
*/
|
828 |
-
function __debugInfo()
|
829 |
-
{
|
830 |
-
$opts = array();
|
831 |
-
switch (MATH_BIGINTEGER_MODE) {
|
832 |
-
case self::MODE_GMP:
|
833 |
-
$engine = 'gmp';
|
834 |
-
break;
|
835 |
-
case self::MODE_BCMATH:
|
836 |
-
$engine = 'bcmath';
|
837 |
-
break;
|
838 |
-
case self::MODE_INTERNAL:
|
839 |
-
$engine = 'internal';
|
840 |
-
$opts[] = PHP_INT_SIZE == 8 ? '64-bit' : '32-bit';
|
841 |
-
}
|
842 |
-
if (MATH_BIGINTEGER_MODE != self::MODE_GMP && defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
|
843 |
-
$opts[] = 'OpenSSL';
|
844 |
-
}
|
845 |
-
if (!empty($opts)) {
|
846 |
-
$engine.= ' (' . implode($opts, ', ') . ')';
|
847 |
-
}
|
848 |
-
return array(
|
849 |
-
'value' => '0x' . $this->toHex(true),
|
850 |
-
'engine' => $engine
|
851 |
-
);
|
852 |
-
}
|
853 |
-
|
854 |
-
/**
|
855 |
-
* Adds two BigIntegers.
|
856 |
-
*
|
857 |
-
* Here's an example:
|
858 |
-
* <code>
|
859 |
-
* <?php
|
860 |
-
* $a = new \phpseclib\Math\BigInteger('10');
|
861 |
-
* $b = new \phpseclib\Math\BigInteger('20');
|
862 |
-
*
|
863 |
-
* $c = $a->add($b);
|
864 |
-
*
|
865 |
-
* echo $c->toString(); // outputs 30
|
866 |
-
* ?>
|
867 |
-
* </code>
|
868 |
-
*
|
869 |
-
* @param \phpseclib\Math\BigInteger $y
|
870 |
-
* @return \phpseclib\Math\BigInteger
|
871 |
-
* @access public
|
872 |
-
* @internal Performs base-2**52 addition
|
873 |
-
*/
|
874 |
-
function add($y)
|
875 |
-
{
|
876 |
-
switch (MATH_BIGINTEGER_MODE) {
|
877 |
-
case self::MODE_GMP:
|
878 |
-
$temp = new static();
|
879 |
-
$temp->value = gmp_add($this->value, $y->value);
|
880 |
-
|
881 |
-
return $this->_normalize($temp);
|
882 |
-
case self::MODE_BCMATH:
|
883 |
-
$temp = new static();
|
884 |
-
$temp->value = bcadd($this->value, $y->value, 0);
|
885 |
-
|
886 |
-
return $this->_normalize($temp);
|
887 |
-
}
|
888 |
-
|
889 |
-
$temp = $this->_add($this->value, $this->is_negative, $y->value, $y->is_negative);
|
890 |
-
|
891 |
-
$result = new static();
|
892 |
-
$result->value = $temp[self::VALUE];
|
893 |
-
$result->is_negative = $temp[self::SIGN];
|
894 |
-
|
895 |
-
return $this->_normalize($result);
|
896 |
-
}
|
897 |
-
|
898 |
-
/**
|
899 |
-
* Performs addition.
|
900 |
-
*
|
901 |
-
* @param array $x_value
|
902 |
-
* @param bool $x_negative
|
903 |
-
* @param array $y_value
|
904 |
-
* @param bool $y_negative
|
905 |
-
* @return array
|
906 |
-
* @access private
|
907 |
-
*/
|
908 |
-
function _add($x_value, $x_negative, $y_value, $y_negative)
|
909 |
-
{
|
910 |
-
$x_size = count($x_value);
|
911 |
-
$y_size = count($y_value);
|
912 |
-
|
913 |
-
if ($x_size == 0) {
|
914 |
-
return array(
|
915 |
-
self::VALUE => $y_value,
|
916 |
-
self::SIGN => $y_negative
|
917 |
-
);
|
918 |
-
} elseif ($y_size == 0) {
|
919 |
-
return array(
|
920 |
-
self::VALUE => $x_value,
|
921 |
-
self::SIGN => $x_negative
|
922 |
-
);
|
923 |
-
}
|
924 |
-
|
925 |
-
// subtract, if appropriate
|
926 |
-
if ($x_negative != $y_negative) {
|
927 |
-
if ($x_value == $y_value) {
|
928 |
-
return array(
|
929 |
-
self::VALUE => array(),
|
930 |
-
self::SIGN => false
|
931 |
-
);
|
932 |
-
}
|
933 |
-
|
934 |
-
$temp = $this->_subtract($x_value, false, $y_value, false);
|
935 |
-
$temp[self::SIGN] = $this->_compare($x_value, false, $y_value, false) > 0 ?
|
936 |
-
$x_negative : $y_negative;
|
937 |
-
|
938 |
-
return $temp;
|
939 |
-
}
|
940 |
-
|
941 |
-
if ($x_size < $y_size) {
|
942 |
-
$size = $x_size;
|
943 |
-
$value = $y_value;
|
944 |
-
} else {
|
945 |
-
$size = $y_size;
|
946 |
-
$value = $x_value;
|
947 |
-
}
|
948 |
-
|
949 |
-
$value[count($value)] = 0; // just in case the carry adds an extra digit
|
950 |
-
|
951 |
-
$carry = 0;
|
952 |
-
for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) {
|
953 |
-
$sum = $x_value[$j] * self::$baseFull + $x_value[$i] + $y_value[$j] * self::$baseFull + $y_value[$i] + $carry;
|
954 |
-
$carry = $sum >= self::$maxDigit2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
|
955 |
-
$sum = $carry ? $sum - self::$maxDigit2 : $sum;
|
956 |
-
|
957 |
-
$temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31);
|
958 |
-
|
959 |
-
$value[$i] = (int) ($sum - self::$baseFull * $temp); // eg. a faster alternative to fmod($sum, 0x4000000)
|
960 |
-
$value[$j] = $temp;
|
961 |
-
}
|
962 |
-
|
963 |
-
if ($j == $size) { // ie. if $y_size is odd
|
964 |
-
$sum = $x_value[$i] + $y_value[$i] + $carry;
|
965 |
-
$carry = $sum >= self::$baseFull;
|
966 |
-
$value[$i] = $carry ? $sum - self::$baseFull : $sum;
|
967 |
-
++$i; // ie. let $i = $j since we've just done $value[$i]
|
968 |
-
}
|
969 |
-
|
970 |
-
if ($carry) {
|
971 |
-
for (; $value[$i] == self::$maxDigit; ++$i) {
|
972 |
-
$value[$i] = 0;
|
973 |
-
}
|
974 |
-
++$value[$i];
|
975 |
-
}
|
976 |
-
|
977 |
-
return array(
|
978 |
-
self::VALUE => $this->_trim($value),
|
979 |
-
self::SIGN => $x_negative
|
980 |
-
);
|
981 |
-
}
|
982 |
-
|
983 |
-
/**
|
984 |
-
* Subtracts two BigIntegers.
|
985 |
-
*
|
986 |
-
* Here's an example:
|
987 |
-
* <code>
|
988 |
-
* <?php
|
989 |
-
* $a = new \phpseclib\Math\BigInteger('10');
|
990 |
-
* $b = new \phpseclib\Math\BigInteger('20');
|
991 |
-
*
|
992 |
-
* $c = $a->subtract($b);
|
993 |
-
*
|
994 |
-
* echo $c->toString(); // outputs -10
|
995 |
-
* ?>
|
996 |
-
* </code>
|
997 |
-
*
|
998 |
-
* @param \phpseclib\Math\BigInteger $y
|
999 |
-
* @return \phpseclib\Math\BigInteger
|
1000 |
-
* @access public
|
1001 |
-
* @internal Performs base-2**52 subtraction
|
1002 |
-
*/
|
1003 |
-
function subtract($y)
|
1004 |
-
{
|
1005 |
-
switch (MATH_BIGINTEGER_MODE) {
|
1006 |
-
case self::MODE_GMP:
|
1007 |
-
$temp = new static();
|
1008 |
-
$temp->value = gmp_sub($this->value, $y->value);
|
1009 |
-
|
1010 |
-
return $this->_normalize($temp);
|
1011 |
-
case self::MODE_BCMATH:
|
1012 |
-
$temp = new static();
|
1013 |
-
$temp->value = bcsub($this->value, $y->value, 0);
|
1014 |
-
|
1015 |
-
return $this->_normalize($temp);
|
1016 |
-
}
|
1017 |
-
|
1018 |
-
$temp = $this->_subtract($this->value, $this->is_negative, $y->value, $y->is_negative);
|
1019 |
-
|
1020 |
-
$result = new static();
|
1021 |
-
$result->value = $temp[self::VALUE];
|
1022 |
-
$result->is_negative = $temp[self::SIGN];
|
1023 |
-
|
1024 |
-
return $this->_normalize($result);
|
1025 |
-
}
|
1026 |
-
|
1027 |
-
/**
|
1028 |
-
* Performs subtraction.
|
1029 |
-
*
|
1030 |
-
* @param array $x_value
|
1031 |
-
* @param bool $x_negative
|
1032 |
-
* @param array $y_value
|
1033 |
-
* @param bool $y_negative
|
1034 |
-
* @return array
|
1035 |
-
* @access private
|
1036 |
-
*/
|
1037 |
-
function _subtract($x_value, $x_negative, $y_value, $y_negative)
|
1038 |
-
{
|
1039 |
-
$x_size = count($x_value);
|
1040 |
-
$y_size = count($y_value);
|
1041 |
-
|
1042 |
-
if ($x_size == 0) {
|
1043 |
-
return array(
|
1044 |
-
self::VALUE => $y_value,
|
1045 |
-
self::SIGN => !$y_negative
|
1046 |
-
);
|
1047 |
-
} elseif ($y_size == 0) {
|
1048 |
-
return array(
|
1049 |
-
self::VALUE => $x_value,
|
1050 |
-
self::SIGN => $x_negative
|
1051 |
-
);
|
1052 |
-
}
|
1053 |
-
|
1054 |
-
// add, if appropriate (ie. -$x - +$y or +$x - -$y)
|
1055 |
-
if ($x_negative != $y_negative) {
|
1056 |
-
$temp = $this->_add($x_value, false, $y_value, false);
|
1057 |
-
$temp[self::SIGN] = $x_negative;
|
1058 |
-
|
1059 |
-
return $temp;
|
1060 |
-
}
|
1061 |
-
|
1062 |
-
$diff = $this->_compare($x_value, $x_negative, $y_value, $y_negative);
|
1063 |
-
|
1064 |
-
if (!$diff) {
|
1065 |
-
return array(
|
1066 |
-
self::VALUE => array(),
|
1067 |
-
self::SIGN => false
|
1068 |
-
);
|
1069 |
-
}
|
1070 |
-
|
1071 |
-
// switch $x and $y around, if appropriate.
|
1072 |
-
if ((!$x_negative && $diff < 0) || ($x_negative && $diff > 0)) {
|
1073 |
-
$temp = $x_value;
|
1074 |
-
$x_value = $y_value;
|
1075 |
-
$y_value = $temp;
|
1076 |
-
|
1077 |
-
$x_negative = !$x_negative;
|
1078 |
-
|
1079 |
-
$x_size = count($x_value);
|
1080 |
-
$y_size = count($y_value);
|
1081 |
-
}
|
1082 |
-
|
1083 |
-
// at this point, $x_value should be at least as big as - if not bigger than - $y_value
|
1084 |
-
|
1085 |
-
$carry = 0;
|
1086 |
-
for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) {
|
1087 |
-
$sum = $x_value[$j] * self::$baseFull + $x_value[$i] - $y_value[$j] * self::$baseFull - $y_value[$i] - $carry;
|
1088 |
-
$carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
|
1089 |
-
$sum = $carry ? $sum + self::$maxDigit2 : $sum;
|
1090 |
-
|
1091 |
-
$temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31);
|
1092 |
-
|
1093 |
-
$x_value[$i] = (int) ($sum - self::$baseFull * $temp);
|
1094 |
-
$x_value[$j] = $temp;
|
1095 |
-
}
|
1096 |
-
|
1097 |
-
if ($j == $y_size) { // ie. if $y_size is odd
|
1098 |
-
$sum = $x_value[$i] - $y_value[$i] - $carry;
|
1099 |
-
$carry = $sum < 0;
|
1100 |
-
$x_value[$i] = $carry ? $sum + self::$baseFull : $sum;
|
1101 |
-
++$i;
|
1102 |
-
}
|
1103 |
-
|
1104 |
-
if ($carry) {
|
1105 |
-
for (; !$x_value[$i]; ++$i) {
|
1106 |
-
$x_value[$i] = self::$maxDigit;
|
1107 |
-
}
|
1108 |
-
--$x_value[$i];
|
1109 |
-
}
|
1110 |
-
|
1111 |
-
return array(
|
1112 |
-
self::VALUE => $this->_trim($x_value),
|
1113 |
-
self::SIGN => $x_negative
|
1114 |
-
);
|
1115 |
-
}
|
1116 |
-
|
1117 |
-
/**
|
1118 |
-
* Multiplies two BigIntegers
|
1119 |
-
*
|
1120 |
-
* Here's an example:
|
1121 |
-
* <code>
|
1122 |
-
* <?php
|
1123 |
-
* $a = new \phpseclib\Math\BigInteger('10');
|
1124 |
-
* $b = new \phpseclib\Math\BigInteger('20');
|
1125 |
-
*
|
1126 |
-
* $c = $a->multiply($b);
|
1127 |
-
*
|
1128 |
-
* echo $c->toString(); // outputs 200
|
1129 |
-
* ?>
|
1130 |
-
* </code>
|
1131 |
-
*
|
1132 |
-
* @param \phpseclib\Math\BigInteger $x
|
1133 |
-
* @return \phpseclib\Math\BigInteger
|
1134 |
-
* @access public
|
1135 |
-
*/
|
1136 |
-
function multiply($x)
|
1137 |
-
{
|
1138 |
-
switch (MATH_BIGINTEGER_MODE) {
|
1139 |
-
case self::MODE_GMP:
|
1140 |
-
$temp = new static();
|
1141 |
-
$temp->value = gmp_mul($this->value, $x->value);
|
1142 |
-
|
1143 |
-
return $this->_normalize($temp);
|
1144 |
-
case self::MODE_BCMATH:
|
1145 |
-
$temp = new static();
|
1146 |
-
$temp->value = bcmul($this->value, $x->value, 0);
|
1147 |
-
|
1148 |
-
return $this->_normalize($temp);
|
1149 |
-
}
|
1150 |
-
|
1151 |
-
$temp = $this->_multiply($this->value, $this->is_negative, $x->value, $x->is_negative);
|
1152 |
-
|
1153 |
-
$product = new static();
|
1154 |
-
$product->value = $temp[self::VALUE];
|
1155 |
-
$product->is_negative = $temp[self::SIGN];
|
1156 |
-
|
1157 |
-
return $this->_normalize($product);
|
1158 |
-
}
|
1159 |
-
|
1160 |
-
/**
|
1161 |
-
* Performs multiplication.
|
1162 |
-
*
|
1163 |
-
* @param array $x_value
|
1164 |
-
* @param bool $x_negative
|
1165 |
-
* @param array $y_value
|
1166 |
-
* @param bool $y_negative
|
1167 |
-
* @return array
|
1168 |
-
* @access private
|
1169 |
-
*/
|
1170 |
-
function _multiply($x_value, $x_negative, $y_value, $y_negative)
|
1171 |
-
{
|
1172 |
-
//if ( $x_value == $y_value ) {
|
1173 |
-
// return array(
|
1174 |
-
// self::VALUE => $this->_square($x_value),
|
1175 |
-
// self::SIGN => $x_sign != $y_value
|
1176 |
-
// );
|
1177 |
-
//}
|
1178 |
-
|
1179 |
-
$x_length = count($x_value);
|
1180 |
-
$y_length = count($y_value);
|
1181 |
-
|
1182 |
-
if (!$x_length || !$y_length) { // a 0 is being multiplied
|
1183 |
-
return array(
|
1184 |
-
self::VALUE => array(),
|
1185 |
-
self::SIGN => false
|
1186 |
-
);
|
1187 |
-
}
|
1188 |
-
|
1189 |
-
return array(
|
1190 |
-
self::VALUE => min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ?
|
1191 |
-
$this->_trim($this->_regularMultiply($x_value, $y_value)) :
|
1192 |
-
$this->_trim($this->_karatsuba($x_value, $y_value)),
|
1193 |
-
self::SIGN => $x_negative != $y_negative
|
1194 |
-
);
|
1195 |
-
}
|
1196 |
-
|
1197 |
-
/**
|
1198 |
-
* Performs long multiplication on two BigIntegers
|
1199 |
-
*
|
1200 |
-
* Modeled after 'multiply' in MutableBigInteger.java.
|
1201 |
-
*
|
1202 |
-
* @param array $x_value
|
1203 |
-
* @param array $y_value
|
1204 |
-
* @return array
|
1205 |
-
* @access private
|
1206 |
-
*/
|
1207 |
-
function _regularMultiply($x_value, $y_value)
|
1208 |
-
{
|
1209 |
-
$x_length = count($x_value);
|
1210 |
-
$y_length = count($y_value);
|
1211 |
-
|
1212 |
-
if (!$x_length || !$y_length) { // a 0 is being multiplied
|
1213 |
-
return array();
|
1214 |
-
}
|
1215 |
-
|
1216 |
-
if ($x_length < $y_length) {
|
1217 |
-
$temp = $x_value;
|
1218 |
-
$x_value = $y_value;
|
1219 |
-
$y_value = $temp;
|
1220 |
-
|
1221 |
-
$x_length = count($x_value);
|
1222 |
-
$y_length = count($y_value);
|
1223 |
-
}
|
1224 |
-
|
1225 |
-
$product_value = $this->_array_repeat(0, $x_length + $y_length);
|
1226 |
-
|
1227 |
-
// the following for loop could be removed if the for loop following it
|
1228 |
-
// (the one with nested for loops) initially set $i to 0, but
|
1229 |
-
// doing so would also make the result in one set of unnecessary adds,
|
1230 |
-
// since on the outermost loops first pass, $product->value[$k] is going
|
1231 |
-
// to always be 0
|
1232 |
-
|
1233 |
-
$carry = 0;
|
1234 |
-
|
1235 |
-
for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0
|
1236 |
-
$temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
|
1237 |
-
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
|
1238 |
-
$product_value[$j] = (int) ($temp - self::$baseFull * $carry);
|
1239 |
-
}
|
1240 |
-
|
1241 |
-
$product_value[$j] = $carry;
|
1242 |
-
|
1243 |
-
// the above for loop is what the previous comment was talking about. the
|
1244 |
-
// following for loop is the "one with nested for loops"
|
1245 |
-
for ($i = 1; $i < $y_length; ++$i) {
|
1246 |
-
$carry = 0;
|
1247 |
-
|
1248 |
-
for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) {
|
1249 |
-
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
|
1250 |
-
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
|
1251 |
-
$product_value[$k] = (int) ($temp - self::$baseFull * $carry);
|
1252 |
-
}
|
1253 |
-
|
1254 |
-
$product_value[$k] = $carry;
|
1255 |
-
}
|
1256 |
-
|
1257 |
-
return $product_value;
|
1258 |
-
}
|
1259 |
-
|
1260 |
-
/**
|
1261 |
-
* Performs Karatsuba multiplication on two BigIntegers
|
1262 |
-
*
|
1263 |
-
* See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
|
1264 |
-
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}.
|
1265 |
-
*
|
1266 |
-
* @param array $x_value
|
1267 |
-
* @param array $y_value
|
1268 |
-
* @return array
|
1269 |
-
* @access private
|
1270 |
-
*/
|
1271 |
-
function _karatsuba($x_value, $y_value)
|
1272 |
-
{
|
1273 |
-
$m = min(count($x_value) >> 1, count($y_value) >> 1);
|
1274 |
-
|
1275 |
-
if ($m < self::KARATSUBA_CUTOFF) {
|
1276 |
-
return $this->_regularMultiply($x_value, $y_value);
|
1277 |
-
}
|
1278 |
-
|
1279 |
-
$x1 = array_slice($x_value, $m);
|
1280 |
-
$x0 = array_slice($x_value, 0, $m);
|
1281 |
-
$y1 = array_slice($y_value, $m);
|
1282 |
-
$y0 = array_slice($y_value, 0, $m);
|
1283 |
-
|
1284 |
-
$z2 = $this->_karatsuba($x1, $y1);
|
1285 |
-
$z0 = $this->_karatsuba($x0, $y0);
|
1286 |
-
|
1287 |
-
$z1 = $this->_add($x1, false, $x0, false);
|
1288 |
-
$temp = $this->_add($y1, false, $y0, false);
|
1289 |
-
$z1 = $this->_karatsuba($z1[self::VALUE], $temp[self::VALUE]);
|
1290 |
-
$temp = $this->_add($z2, false, $z0, false);
|
1291 |
-
$z1 = $this->_subtract($z1, false, $temp[self::VALUE], false);
|
1292 |
-
|
1293 |
-
$z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
|
1294 |
-
$z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]);
|
1295 |
-
|
1296 |
-
$xy = $this->_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]);
|
1297 |
-
$xy = $this->_add($xy[self::VALUE], $xy[self::SIGN], $z0, false);
|
1298 |
-
|
1299 |
-
return $xy[self::VALUE];
|
1300 |
-
}
|
1301 |
-
|
1302 |
-
/**
|
1303 |
-
* Performs squaring
|
1304 |
-
*
|
1305 |
-
* @param array $x
|
1306 |
-
* @return array
|
1307 |
-
* @access private
|
1308 |
-
*/
|
1309 |
-
function _square($x = false)
|
1310 |
-
{
|
1311 |
-
return count($x) < 2 * self::KARATSUBA_CUTOFF ?
|
1312 |
-
$this->_trim($this->_baseSquare($x)) :
|
1313 |
-
$this->_trim($this->_karatsubaSquare($x));
|
1314 |
-
}
|
1315 |
-
|
1316 |
-
/**
|
1317 |
-
* Performs traditional squaring on two BigIntegers
|
1318 |
-
*
|
1319 |
-
* Squaring can be done faster than multiplying a number by itself can be. See
|
1320 |
-
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} /
|
1321 |
-
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information.
|
1322 |
-
*
|
1323 |
-
* @param array $value
|
1324 |
-
* @return array
|
1325 |
-
* @access private
|
1326 |
-
*/
|
1327 |
-
function _baseSquare($value)
|
1328 |
-
{
|
1329 |
-
if (empty($value)) {
|
1330 |
-
return array();
|
1331 |
-
}
|
1332 |
-
$square_value = $this->_array_repeat(0, 2 * count($value));
|
1333 |
-
|
1334 |
-
for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) {
|
1335 |
-
$i2 = $i << 1;
|
1336 |
-
|
1337 |
-
$temp = $square_value[$i2] + $value[$i] * $value[$i];
|
1338 |
-
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
|
1339 |
-
$square_value[$i2] = (int) ($temp - self::$baseFull * $carry);
|
1340 |
-
|
1341 |
-
// note how we start from $i+1 instead of 0 as we do in multiplication.
|
1342 |
-
for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) {
|
1343 |
-
$temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry;
|
1344 |
-
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
|
1345 |
-
$square_value[$k] = (int) ($temp - self::$baseFull * $carry);
|
1346 |
-
}
|
1347 |
-
|
1348 |
-
// the following line can yield values larger 2**15. at this point, PHP should switch
|
1349 |
-
// over to floats.
|
1350 |
-
$square_value[$i + $max_index + 1] = $carry;
|
1351 |
-
}
|
1352 |
-
|
1353 |
-
return $square_value;
|
1354 |
-
}
|
1355 |
-
|
1356 |
-
/**
|
1357 |
-
* Performs Karatsuba "squaring" on two BigIntegers
|
1358 |
-
*
|
1359 |
-
* See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
|
1360 |
-
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}.
|
1361 |
-
*
|
1362 |
-
* @param array $value
|
1363 |
-
* @return array
|
1364 |
-
* @access private
|
1365 |
-
*/
|
1366 |
-
function _karatsubaSquare($value)
|
1367 |
-
{
|
1368 |
-
$m = count($value) >> 1;
|
1369 |
-
|
1370 |
-
if ($m < self::KARATSUBA_CUTOFF) {
|
1371 |
-
return $this->_baseSquare($value);
|
1372 |
-
}
|
1373 |
-
|
1374 |
-
$x1 = array_slice($value, $m);
|
1375 |
-
$x0 = array_slice($value, 0, $m);
|
1376 |
-
|
1377 |
-
$z2 = $this->_karatsubaSquare($x1);
|
1378 |
-
$z0 = $this->_karatsubaSquare($x0);
|
1379 |
-
|
1380 |
-
$z1 = $this->_add($x1, false, $x0, false);
|
1381 |
-
$z1 = $this->_karatsubaSquare($z1[self::VALUE]);
|
1382 |
-
$temp = $this->_add($z2, false, $z0, false);
|
1383 |
-
$z1 = $this->_subtract($z1, false, $temp[self::VALUE], false);
|
1384 |
-
|
1385 |
-
$z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
|
1386 |
-
$z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]);
|
1387 |
-
|
1388 |
-
$xx = $this->_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]);
|
1389 |
-
$xx = $this->_add($xx[self::VALUE], $xx[self::SIGN], $z0, false);
|
1390 |
-
|
1391 |
-
return $xx[self::VALUE];
|
1392 |
-
}
|
1393 |
-
|
1394 |
-
/**
|
1395 |
-
* Divides two BigIntegers.
|
1396 |
-
*
|
1397 |
-
* Returns an array whose first element contains the quotient and whose second element contains the
|
1398 |
-
* "common residue". If the remainder would be positive, the "common residue" and the remainder are the
|
1399 |
-
* same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder
|
1400 |
-
* and the divisor (basically, the "common residue" is the first positive modulo).
|
1401 |
-
*
|
1402 |
-
* Here's an example:
|
1403 |
-
* <code>
|
1404 |
-
* <?php
|
1405 |
-
* $a = new \phpseclib\Math\BigInteger('10');
|
1406 |
-
* $b = new \phpseclib\Math\BigInteger('20');
|
1407 |
-
*
|
1408 |
-
* list($quotient, $remainder) = $a->divide($b);
|
1409 |
-
*
|
1410 |
-
* echo $quotient->toString(); // outputs 0
|
1411 |
-
* echo "\r\n";
|
1412 |
-
* echo $remainder->toString(); // outputs 10
|
1413 |
-
* ?>
|
1414 |
-
* </code>
|
1415 |
-
*
|
1416 |
-
* @param \phpseclib\Math\BigInteger $y
|
1417 |
-
* @return array
|
1418 |
-
* @access public
|
1419 |
-
* @internal This function is based off of {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}.
|
1420 |
-
*/
|
1421 |
-
function divide($y)
|
1422 |
-
{
|
1423 |
-
switch (MATH_BIGINTEGER_MODE) {
|
1424 |
-
case self::MODE_GMP:
|
1425 |
-
$quotient = new static();
|
1426 |
-
$remainder = new static();
|
1427 |
-
|
1428 |
-
list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value);
|
1429 |
-
|
1430 |
-
if (gmp_sign($remainder->value) < 0) {
|
1431 |
-
$remainder->value = gmp_add($remainder->value, gmp_abs($y->value));
|
1432 |
-
}
|
1433 |
-
|
1434 |
-
return array($this->_normalize($quotient), $this->_normalize($remainder));
|
1435 |
-
case self::MODE_BCMATH:
|
1436 |
-
$quotient = new static();
|
1437 |
-
$remainder = new static();
|
1438 |
-
|
1439 |
-
$quotient->value = bcdiv($this->value, $y->value, 0);
|
1440 |
-
$remainder->value = bcmod($this->value, $y->value);
|
1441 |
-
|
1442 |
-
if ($remainder->value[0] == '-') {
|
1443 |
-
$remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0);
|
1444 |
-
}
|
1445 |
-
|
1446 |
-
return array($this->_normalize($quotient), $this->_normalize($remainder));
|
1447 |
-
}
|
1448 |
-
|
1449 |
-
if (count($y->value) == 1) {
|
1450 |
-
list($q, $r) = $this->_divide_digit($this->value, $y->value[0]);
|
1451 |
-
$quotient = new static();
|
1452 |
-
$remainder = new static();
|
1453 |
-
$quotient->value = $q;
|
1454 |
-
$remainder->value = array($r);
|
1455 |
-
$quotient->is_negative = $this->is_negative != $y->is_negative;
|
1456 |
-
return array($this->_normalize($quotient), $this->_normalize($remainder));
|
1457 |
-
}
|
1458 |
-
|
1459 |
-
static $zero;
|
1460 |
-
if (!isset($zero)) {
|
1461 |
-
$zero = new static();
|
1462 |
-
}
|
1463 |
-
|
1464 |
-
$x = $this->copy();
|
1465 |
-
$y = $y->copy();
|
1466 |
-
|
1467 |
-
$x_sign = $x->is_negative;
|
1468 |
-
$y_sign = $y->is_negative;
|
1469 |
-
|
1470 |
-
$x->is_negative = $y->is_negative = false;
|
1471 |
-
|
1472 |
-
$diff = $x->compare($y);
|
1473 |
-
|
1474 |
-
if (!$diff) {
|
1475 |
-
$temp = new static();
|
1476 |
-
$temp->value = array(1);
|
1477 |
-
$temp->is_negative = $x_sign != $y_sign;
|
1478 |
-
return array($this->_normalize($temp), $this->_normalize(new static()));
|
1479 |
-
}
|
1480 |
-
|
1481 |
-
if ($diff < 0) {
|
1482 |
-
// if $x is negative, "add" $y.
|
1483 |
-
if ($x_sign) {
|
1484 |
-
$x = $y->subtract($x);
|
1485 |
-
}
|
1486 |
-
return array($this->_normalize(new static()), $this->_normalize($x));
|
1487 |
-
}
|
1488 |
-
|
1489 |
-
// normalize $x and $y as described in HAC 14.23 / 14.24
|
1490 |
-
$msb = $y->value[count($y->value) - 1];
|
1491 |
-
for ($shift = 0; !($msb & self::$msb); ++$shift) {
|
1492 |
-
$msb <<= 1;
|
1493 |
-
}
|
1494 |
-
$x->_lshift($shift);
|
1495 |
-
$y->_lshift($shift);
|
1496 |
-
$y_value = &$y->value;
|
1497 |
-
|
1498 |
-
$x_max = count($x->value) - 1;
|
1499 |
-
$y_max = count($y->value) - 1;
|
1500 |
-
|
1501 |
-
$quotient = new static();
|
1502 |
-
$quotient_value = &$quotient->value;
|
1503 |
-
$quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1);
|
1504 |
-
|
1505 |
-
static $temp, $lhs, $rhs;
|
1506 |
-
if (!isset($temp)) {
|
1507 |
-
$temp = new static();
|
1508 |
-
$lhs = new static();
|
1509 |
-
$rhs = new static();
|
1510 |
-
}
|
1511 |
-
$temp_value = &$temp->value;
|
1512 |
-
$rhs_value = &$rhs->value;
|
1513 |
-
|
1514 |
-
// $temp = $y << ($x_max - $y_max-1) in base 2**26
|
1515 |
-
$temp_value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y_value);
|
1516 |
-
|
1517 |
-
while ($x->compare($temp) >= 0) {
|
1518 |
-
// calculate the "common residue"
|
1519 |
-
++$quotient_value[$x_max - $y_max];
|
1520 |
-
$x = $x->subtract($temp);
|
1521 |
-
$x_max = count($x->value) - 1;
|
1522 |
-
}
|
1523 |
-
|
1524 |
-
for ($i = $x_max; $i >= $y_max + 1; --$i) {
|
1525 |
-
$x_value = &$x->value;
|
1526 |
-
$x_window = array(
|
1527 |
-
isset($x_value[$i]) ? $x_value[$i] : 0,
|
1528 |
-
isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0,
|
1529 |
-
isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0
|
1530 |
-
);
|
1531 |
-
$y_window = array(
|
1532 |
-
$y_value[$y_max],
|
1533 |
-
($y_max > 0) ? $y_value[$y_max - 1] : 0
|
1534 |
-
);
|
1535 |
-
|
1536 |
-
$q_index = $i - $y_max - 1;
|
1537 |
-
if ($x_window[0] == $y_window[0]) {
|
1538 |
-
$quotient_value[$q_index] = self::$maxDigit;
|
1539 |
-
} else {
|
1540 |
-
$quotient_value[$q_index] = $this->_safe_divide(
|
1541 |
-
$x_window[0] * self::$baseFull + $x_window[1],
|
1542 |
-
$y_window[0]
|
1543 |
-
);
|
1544 |
-
}
|
1545 |
-
|
1546 |
-
$temp_value = array($y_window[1], $y_window[0]);
|
1547 |
-
|
1548 |
-
$lhs->value = array($quotient_value[$q_index]);
|
1549 |
-
$lhs = $lhs->multiply($temp);
|
1550 |
-
|
1551 |
-
$rhs_value = array($x_window[2], $x_window[1], $x_window[0]);
|
1552 |
-
|
1553 |
-
while ($lhs->compare($rhs) > 0) {
|
1554 |
-
--$quotient_value[$q_index];
|
1555 |
-
|
1556 |
-
$lhs->value = array($quotient_value[$q_index]);
|
1557 |
-
$lhs = $lhs->multiply($temp);
|
1558 |
-
}
|
1559 |
-
|
1560 |
-
$adjust = $this->_array_repeat(0, $q_index);
|
1561 |
-
$temp_value = array($quotient_value[$q_index]);
|
1562 |
-
$temp = $temp->multiply($y);
|
1563 |
-
$temp_value = &$temp->value;
|
1564 |
-
if (count($temp_value)) {
|
1565 |
-
$temp_value = array_merge($adjust, $temp_value);
|
1566 |
-
}
|
1567 |
-
|
1568 |
-
$x = $x->subtract($temp);
|
1569 |
-
|
1570 |
-
if ($x->compare($zero) < 0) {
|
1571 |
-
$temp_value = array_merge($adjust, $y_value);
|
1572 |
-
$x = $x->add($temp);
|
1573 |
-
|
1574 |
-
--$quotient_value[$q_index];
|
1575 |
-
}
|
1576 |
-
|
1577 |
-
$x_max = count($x_value) - 1;
|
1578 |
-
}
|
1579 |
-
|
1580 |
-
// unnormalize the remainder
|
1581 |
-
$x->_rshift($shift);
|
1582 |
-
|
1583 |
-
$quotient->is_negative = $x_sign != $y_sign;
|
1584 |
-
|
1585 |
-
// calculate the "common residue", if appropriate
|
1586 |
-
if ($x_sign) {
|
1587 |
-
$y->_rshift($shift);
|
1588 |
-
$x = $y->subtract($x);
|
1589 |
-
}
|
1590 |
-
|
1591 |
-
return array($this->_normalize($quotient), $this->_normalize($x));
|
1592 |
-
}
|
1593 |
-
|
1594 |
-
/**
|
1595 |
-
* Divides a BigInteger by a regular integer
|
1596 |
-
*
|
1597 |
-
* abc / x = a00 / x + b0 / x + c / x
|
1598 |
-
*
|
1599 |
-
* @param array $dividend
|
1600 |
-
* @param array $divisor
|
1601 |
-
* @return array
|
1602 |
-
* @access private
|
1603 |
-
*/
|
1604 |
-
function _divide_digit($dividend, $divisor)
|
1605 |
-
{
|
1606 |
-
$carry = 0;
|
1607 |
-
$result = array();
|
1608 |
-
|
1609 |
-
for ($i = count($dividend) - 1; $i >= 0; --$i) {
|
1610 |
-
$temp = self::$baseFull * $carry + $dividend[$i];
|
1611 |
-
$result[$i] = $this->_safe_divide($temp, $divisor);
|
1612 |
-
$carry = (int) ($temp - $divisor * $result[$i]);
|
1613 |
-
}
|
1614 |
-
|
1615 |
-
return array($result, $carry);
|
1616 |
-
}
|
1617 |
-
|
1618 |
-
/**
|
1619 |
-
* Performs modular exponentiation.
|
1620 |
-
*
|
1621 |
-
* Here's an example:
|
1622 |
-
* <code>
|
1623 |
-
* <?php
|
1624 |
-
* $a = new \phpseclib\Math\BigInteger('10');
|
1625 |
-
* $b = new \phpseclib\Math\BigInteger('20');
|
1626 |
-
* $c = new \phpseclib\Math\BigInteger('30');
|
1627 |
-
*
|
1628 |
-
* $c = $a->modPow($b, $c);
|
1629 |
-
*
|
1630 |
-
* echo $c->toString(); // outputs 10
|
1631 |
-
* ?>
|
1632 |
-
* </code>
|
1633 |
-
*
|
1634 |
-
* @param \phpseclib\Math\BigInteger $e
|
1635 |
-
* @param \phpseclib\Math\BigInteger $n
|
1636 |
-
* @return \phpseclib\Math\BigInteger
|
1637 |
-
* @access public
|
1638 |
-
* @internal The most naive approach to modular exponentiation has very unreasonable requirements, and
|
1639 |
-
* and although the approach involving repeated squaring does vastly better, it, too, is impractical
|
1640 |
-
* for our purposes. The reason being that division - by far the most complicated and time-consuming
|
1641 |
-
* of the basic operations (eg. +,-,*,/) - occurs multiple times within it.
|
1642 |
-
*
|
1643 |
-
* Modular reductions resolve this issue. Although an individual modular reduction takes more time
|
1644 |
-
* then an individual division, when performed in succession (with the same modulo), they're a lot faster.
|
1645 |
-
*
|
1646 |
-
* The two most commonly used modular reductions are Barrett and Montgomery reduction. Montgomery reduction,
|
1647 |
-
* although faster, only works when the gcd of the modulo and of the base being used is 1. In RSA, when the
|
1648 |
-
* base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because
|
1649 |
-
* the product of two odd numbers is odd), but what about when RSA isn't used?
|
1650 |
-
*
|
1651 |
-
* In contrast, Barrett reduction has no such constraint. As such, some bigint implementations perform a
|
1652 |
-
* Barrett reduction after every operation in the modpow function. Others perform Barrett reductions when the
|
1653 |
-
* modulo is even and Montgomery reductions when the modulo is odd. BigInteger.java's modPow method, however,
|
1654 |
-
* uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and
|
1655 |
-
* the other, a power of two - and recombine them, later. This is the method that this modPow function uses.
|
1656 |
-
* {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates.
|
1657 |
-
*/
|
1658 |
-
function modPow($e, $n)
|
1659 |
-
{
|
1660 |
-
$n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs();
|
1661 |
-
|
1662 |
-
if ($e->compare(new static()) < 0) {
|
1663 |
-
$e = $e->abs();
|
1664 |
-
|
1665 |
-
$temp = $this->modInverse($n);
|
1666 |
-
if ($temp === false) {
|
1667 |
-
return false;
|
1668 |
-
}
|
1669 |
-
|
1670 |
-
return $this->_normalize($temp->modPow($e, $n));
|
1671 |
-
}
|
1672 |
-
|
1673 |
-
if (MATH_BIGINTEGER_MODE == self::MODE_GMP) {
|
1674 |
-
$temp = new static();
|
1675 |
-
$temp->value = gmp_powm($this->value, $e->value, $n->value);
|
1676 |
-
|
1677 |
-
return $this->_normalize($temp);
|
1678 |
-
}
|
1679 |
-
|
1680 |
-
if ($this->compare(new static()) < 0 || $this->compare($n) > 0) {
|
1681 |
-
list(, $temp) = $this->divide($n);
|
1682 |
-
return $temp->modPow($e, $n);
|
1683 |
-
}
|
1684 |
-
|
1685 |
-
if (defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
|
1686 |
-
$components = array(
|
1687 |
-
'modulus' => $n->toBytes(true),
|
1688 |
-
'publicExponent' => $e->toBytes(true)
|
1689 |
-
);
|
1690 |
-
|
1691 |
-
$components = array(
|
1692 |
-
'modulus' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['modulus'])), $components['modulus']),
|
1693 |
-
'publicExponent' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent'])
|
1694 |
-
);
|
1695 |
-
|
1696 |
-
$RSAPublicKey = pack(
|
1697 |
-
'Ca*a*a*',
|
1698 |
-
48,
|
1699 |
-
$this->_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])),
|
1700 |
-
$components['modulus'],
|
1701 |
-
$components['publicExponent']
|
1702 |
-
);
|
1703 |
-
|
1704 |
-
$rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
|
1705 |
-
$RSAPublicKey = chr(0) . $RSAPublicKey;
|
1706 |
-
$RSAPublicKey = chr(3) . $this->_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey;
|
1707 |
-
|
1708 |
-
$encapsulated = pack(
|
1709 |
-
'Ca*a*',
|
1710 |
-
48,
|
1711 |
-
$this->_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)),
|
1712 |
-
$rsaOID . $RSAPublicKey
|
1713 |
-
);
|
1714 |
-
|
1715 |
-
$RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
|
1716 |
-
chunk_split(base64_encode($encapsulated)) .
|
1717 |
-
'-----END PUBLIC KEY-----';
|
1718 |
-
|
1719 |
-
$plaintext = str_pad($this->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT);
|
1720 |
-
|
1721 |
-
if (openssl_public_encrypt($plaintext, $result, $RSAPublicKey, OPENSSL_NO_PADDING)) {
|
1722 |
-
return new static($result, 256);
|
1723 |
-
}
|
1724 |
-
}
|
1725 |
-
|
1726 |
-
if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
|
1727 |
-
$temp = new static();
|
1728 |
-
$temp->value = bcpowmod($this->value, $e->value, $n->value, 0);
|
1729 |
-
|
1730 |
-
return $this->_normalize($temp);
|
1731 |
-
}
|
1732 |
-
|
1733 |
-
if (empty($e->value)) {
|
1734 |
-
$temp = new static();
|
1735 |
-
$temp->value = array(1);
|
1736 |
-
return $this->_normalize($temp);
|
1737 |
-
}
|
1738 |
-
|
1739 |
-
if ($e->value == array(1)) {
|
1740 |
-
list(, $temp) = $this->divide($n);
|
1741 |
-
return $this->_normalize($temp);
|
1742 |
-
}
|
1743 |
-
|
1744 |
-
if ($e->value == array(2)) {
|
1745 |
-
$temp = new static();
|
1746 |
-
$temp->value = $this->_square($this->value);
|
1747 |
-
list(, $temp) = $temp->divide($n);
|
1748 |
-
return $this->_normalize($temp);
|
1749 |
-
}
|
1750 |
-
|
1751 |
-
return $this->_normalize($this->_slidingWindow($e, $n, self::BARRETT));
|
1752 |
-
|
1753 |
-
// the following code, although not callable, can be run independently of the above code
|
1754 |
-
// although the above code performed better in my benchmarks the following could might
|
1755 |
-
// perform better under different circumstances. in lieu of deleting it it's just been
|
1756 |
-
// made uncallable
|
1757 |
-
|
1758 |
-
// is the modulo odd?
|
1759 |
-
if ($n->value[0] & 1) {
|
1760 |
-
return $this->_normalize($this->_slidingWindow($e, $n, self::MONTGOMERY));
|
1761 |
-
}
|
1762 |
-
// if it's not, it's even
|
1763 |
-
|
1764 |
-
// find the lowest set bit (eg. the max pow of 2 that divides $n)
|
1765 |
-
for ($i = 0; $i < count($n->value); ++$i) {
|
1766 |
-
if ($n->value[$i]) {
|
1767 |
-
$temp = decbin($n->value[$i]);
|
1768 |
-
$j = strlen($temp) - strrpos($temp, '1') - 1;
|
1769 |
-
$j+= 26 * $i;
|
1770 |
-
break;
|
1771 |
-
}
|
1772 |
-
}
|
1773 |
-
// at this point, 2^$j * $n/(2^$j) == $n
|
1774 |
-
|
1775 |
-
$mod1 = $n->copy();
|
1776 |
-
$mod1->_rshift($j);
|
1777 |
-
$mod2 = new static();
|
1778 |
-
$mod2->value = array(1);
|
1779 |
-
$mod2->_lshift($j);
|
1780 |
-
|
1781 |
-
$part1 = ($mod1->value != array(1)) ? $this->_slidingWindow($e, $mod1, self::MONTGOMERY) : new static();
|
1782 |
-
$part2 = $this->_slidingWindow($e, $mod2, self::POWEROF2);
|
1783 |
-
|
1784 |
-
$y1 = $mod2->modInverse($mod1);
|
1785 |
-
$y2 = $mod1->modInverse($mod2);
|
1786 |
-
|
1787 |
-
$result = $part1->multiply($mod2);
|
1788 |
-
$result = $result->multiply($y1);
|
1789 |
-
|
1790 |
-
$temp = $part2->multiply($mod1);
|
1791 |
-
$temp = $temp->multiply($y2);
|
1792 |
-
|
1793 |
-
$result = $result->add($temp);
|
1794 |
-
list(, $result) = $result->divide($n);
|
1795 |
-
|
1796 |
-
return $this->_normalize($result);
|
1797 |
-
}
|
1798 |
-
|
1799 |
-
/**
|
1800 |
-
* Performs modular exponentiation.
|
1801 |
-
*
|
1802 |
-
* Alias for modPow().
|
1803 |
-
*
|
1804 |
-
* @param \phpseclib\Math\BigInteger $e
|
1805 |
-
* @param \phpseclib\Math\BigInteger $n
|
1806 |
-
* @return \phpseclib\Math\BigInteger
|
1807 |
-
* @access public
|
1808 |
-
*/
|
1809 |
-
function powMod($e, $n)
|
1810 |
-
{
|
1811 |
-
return $this->modPow($e, $n);
|
1812 |
-
}
|
1813 |
-
|
1814 |
-
/**
|
1815 |
-
* Sliding Window k-ary Modular Exponentiation
|
1816 |
-
*
|
1817 |
-
* Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} /
|
1818 |
-
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}. In a departure from those algorithims,
|
1819 |
-
* however, this function performs a modular reduction after every multiplication and squaring operation.
|
1820 |
-
* As such, this function has the same preconditions that the reductions being used do.
|
1821 |
-
*
|
1822 |
-
* @param \phpseclib\Math\BigInteger $e
|
1823 |
-
* @param \phpseclib\Math\BigInteger $n
|
1824 |
-
* @param int $mode
|
1825 |
-
* @return \phpseclib\Math\BigInteger
|
1826 |
-
* @access private
|
1827 |
-
*/
|
1828 |
-
function _slidingWindow($e, $n, $mode)
|
1829 |
-
{
|
1830 |
-
static $window_ranges = array(7, 25, 81, 241, 673, 1793); // from BigInteger.java's oddModPow function
|
1831 |
-
//static $window_ranges = array(0, 7, 36, 140, 450, 1303, 3529); // from MPM 7.3.1
|
1832 |
-
|
1833 |
-
$e_value = $e->value;
|
1834 |
-
$e_length = count($e_value) - 1;
|
1835 |
-
$e_bits = decbin($e_value[$e_length]);
|
1836 |
-
for ($i = $e_length - 1; $i >= 0; --$i) {
|
1837 |
-
$e_bits.= str_pad(decbin($e_value[$i]), self::$base, '0', STR_PAD_LEFT);
|
1838 |
-
}
|
1839 |
-
|
1840 |
-
$e_length = strlen($e_bits);
|
1841 |
-
|
1842 |
-
// calculate the appropriate window size.
|
1843 |
-
// $window_size == 3 if $window_ranges is between 25 and 81, for example.
|
1844 |
-
for ($i = 0, $window_size = 1; $i < count($window_ranges) && $e_length > $window_ranges[$i]; ++$window_size, ++$i) {
|
1845 |
-
}
|
1846 |
-
|
1847 |
-
$n_value = $n->value;
|
1848 |
-
|
1849 |
-
// precompute $this^0 through $this^$window_size
|
1850 |
-
$powers = array();
|
1851 |
-
$powers[1] = $this->_prepareReduce($this->value, $n_value, $mode);
|
1852 |
-
$powers[2] = $this->_squareReduce($powers[1], $n_value, $mode);
|
1853 |
-
|
1854 |
-
// we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end
|
1855 |
-
// in a 1. ie. it's supposed to be odd.
|
1856 |
-
$temp = 1 << ($window_size - 1);
|
1857 |
-
for ($i = 1; $i < $temp; ++$i) {
|
1858 |
-
$i2 = $i << 1;
|
1859 |
-
$powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode);
|
1860 |
-
}
|
1861 |
-
|
1862 |
-
$result = array(1);
|
1863 |
-
$result = $this->_prepareReduce($result, $n_value, $mode);
|
1864 |
-
|
1865 |
-
for ($i = 0; $i < $e_length;) {
|
1866 |
-
if (!$e_bits[$i]) {
|
1867 |
-
$result = $this->_squareReduce($result, $n_value, $mode);
|
1868 |
-
++$i;
|
1869 |
-
} else {
|
1870 |
-
for ($j = $window_size - 1; $j > 0; --$j) {
|
1871 |
-
if (!empty($e_bits[$i + $j])) {
|
1872 |
-
break;
|
1873 |
-
}
|
1874 |
-
}
|
1875 |
-
|
1876 |
-
// eg. the length of substr($e_bits, $i, $j + 1)
|
1877 |
-
for ($k = 0; $k <= $j; ++$k) {
|
1878 |
-
$result = $this->_squareReduce($result, $n_value, $mode);
|
1879 |
-
}
|
1880 |
-
|
1881 |
-
$result = $this->_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode);
|
1882 |
-
|
1883 |
-
$i += $j + 1;
|
1884 |
-
}
|
1885 |
-
}
|
1886 |
-
|
1887 |
-
$temp = new static();
|
1888 |
-
$temp->value = $this->_reduce($result, $n_value, $mode);
|
1889 |
-
|
1890 |
-
return $temp;
|
1891 |
-
}
|
1892 |
-
|
1893 |
-
/**
|
1894 |
-
* Modular reduction
|
1895 |
-
*
|
1896 |
-
* For most $modes this will return the remainder.
|
1897 |
-
*
|
1898 |
-
* @see self::_slidingWindow()
|
1899 |
-
* @access private
|
1900 |
-
* @param array $x
|
1901 |
-
* @param array $n
|
1902 |
-
* @param int $mode
|
1903 |
-
* @return array
|
1904 |
-
*/
|
1905 |
-
function _reduce($x, $n, $mode)
|
1906 |
-
{
|
1907 |
-
switch ($mode) {
|
1908 |
-
case self::MONTGOMERY:
|
1909 |
-
return $this->_montgomery($x, $n);
|
1910 |
-
case self::BARRETT:
|
1911 |
-
return $this->_barrett($x, $n);
|
1912 |
-
case self::POWEROF2:
|
1913 |
-
$lhs = new static();
|
1914 |
-
$lhs->value = $x;
|
1915 |
-
$rhs = new static();
|
1916 |
-
$rhs->value = $n;
|
1917 |
-
return $x->_mod2($n);
|
1918 |
-
case self::CLASSIC:
|
1919 |
-
$lhs = new static();
|
1920 |
-
$lhs->value = $x;
|
1921 |
-
$rhs = new static();
|
1922 |
-
$rhs->value = $n;
|
1923 |
-
list(, $temp) = $lhs->divide($rhs);
|
1924 |
-
return $temp->value;
|
1925 |
-
case self::NONE:
|
1926 |
-
return $x;
|
1927 |
-
default:
|
1928 |
-
// an invalid $mode was provided
|
1929 |
-
}
|
1930 |
-
}
|
1931 |
-
|
1932 |
-
/**
|
1933 |
-
* Modular reduction preperation
|
1934 |
-
*
|
1935 |
-
* @see self::_slidingWindow()
|
1936 |
-
* @access private
|
1937 |
-
* @param array $x
|
1938 |
-
* @param array $n
|
1939 |
-
* @param int $mode
|
1940 |
-
* @return array
|
1941 |
-
*/
|
1942 |
-
function _prepareReduce($x, $n, $mode)
|
1943 |
-
{
|
1944 |
-
if ($mode == self::MONTGOMERY) {
|
1945 |
-
return $this->_prepMontgomery($x, $n);
|
1946 |
-
}
|
1947 |
-
return $this->_reduce($x, $n, $mode);
|
1948 |
-
}
|
1949 |
-
|
1950 |
-
/**
|
1951 |
-
* Modular multiply
|
1952 |
-
*
|
1953 |
-
* @see self::_slidingWindow()
|
1954 |
-
* @access private
|
1955 |
-
* @param array $x
|
1956 |
-
* @param array $y
|
1957 |
-
* @param array $n
|
1958 |
-
* @param int $mode
|
1959 |
-
* @return array
|
1960 |
-
*/
|
1961 |
-
function _multiplyReduce($x, $y, $n, $mode)
|
1962 |
-
{
|
1963 |
-
if ($mode == self::MONTGOMERY) {
|
1964 |
-
return $this->_montgomeryMultiply($x, $y, $n);
|
1965 |
-
}
|
1966 |
-
$temp = $this->_multiply($x, false, $y, false);
|
1967 |
-
return $this->_reduce($temp[self::VALUE], $n, $mode);
|
1968 |
-
}
|
1969 |
-
|
1970 |
-
/**
|
1971 |
-
* Modular square
|
1972 |
-
*
|
1973 |
-
* @see self::_slidingWindow()
|
1974 |
-
* @access private
|
1975 |
-
* @param array $x
|
1976 |
-
* @param array $n
|
1977 |
-
* @param int $mode
|
1978 |
-
* @return array
|
1979 |
-
*/
|
1980 |
-
function _squareReduce($x, $n, $mode)
|
1981 |
-
{
|
1982 |
-
if ($mode == self::MONTGOMERY) {
|
1983 |
-
return $this->_montgomeryMultiply($x, $x, $n);
|
1984 |
-
}
|
1985 |
-
return $this->_reduce($this->_square($x), $n, $mode);
|
1986 |
-
}
|
1987 |
-
|
1988 |
-
/**
|
1989 |
-
* Modulos for Powers of Two
|
1990 |
-
*
|
1991 |
-
* Calculates $x%$n, where $n = 2**$e, for some $e. Since this is basically the same as doing $x & ($n-1),
|
1992 |
-
* we'll just use this function as a wrapper for doing that.
|
1993 |
-
*
|
1994 |
-
* @see self::_slidingWindow()
|
1995 |
-
* @access private
|
1996 |
-
* @param \phpseclib\Math\BigInteger
|
1997 |
-
* @return \phpseclib\Math\BigInteger
|
1998 |
-
*/
|
1999 |
-
function _mod2($n)
|
2000 |
-
{
|
2001 |
-
$temp = new static();
|
2002 |
-
$temp->value = array(1);
|
2003 |
-
return $this->bitwise_and($n->subtract($temp));
|
2004 |
-
}
|
2005 |
-
|
2006 |
-
/**
|
2007 |
-
* Barrett Modular Reduction
|
2008 |
-
*
|
2009 |
-
* See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} /
|
2010 |
-
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information. Modified slightly,
|
2011 |
-
* so as not to require negative numbers (initially, this script didn't support negative numbers).
|
2012 |
-
*
|
2013 |
-
* Employs "folding", as described at
|
2014 |
-
* {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}. To quote from
|
2015 |
-
* it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x."
|
2016 |
-
*
|
2017 |
-
* Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that
|
2018 |
-
* usable on account of (1) its not using reasonable radix points as discussed in
|
2019 |
-
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable
|
2020 |
-
* radix points, it only works when there are an even number of digits in the denominator. The reason for (2) is that
|
2021 |
-
* (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line
|
2022 |
-
* comments for details.
|
2023 |
-
*
|
2024 |
-
* @see self::_slidingWindow()
|
2025 |
-
* @access private
|
2026 |
-
* @param array $n
|
2027 |
-
* @param array $m
|
2028 |
-
* @return array
|
2029 |
-
*/
|
2030 |
-
function _barrett($n, $m)
|
2031 |
-
{
|
2032 |
-
static $cache = array(
|
2033 |
-
self::VARIABLE => array(),
|
2034 |
-
self::DATA => array()
|
2035 |
-
);
|
2036 |
-
|
2037 |
-
$m_length = count($m);
|
2038 |
-
|
2039 |
-
// if ($this->_compare($n, $this->_square($m)) >= 0) {
|
2040 |
-
if (count($n) > 2 * $m_length) {
|
2041 |
-
$lhs = new static();
|
2042 |
-
$rhs = new static();
|
2043 |
-
$lhs->value = $n;
|
2044 |
-
$rhs->value = $m;
|
2045 |
-
list(, $temp) = $lhs->divide($rhs);
|
2046 |
-
return $temp->value;
|
2047 |
-
}
|
2048 |
-
|
2049 |
-
// if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced
|
2050 |
-
if ($m_length < 5) {
|
2051 |
-
return $this->_regularBarrett($n, $m);
|
2052 |
-
}
|
2053 |
-
|
2054 |
-
// n = 2 * m.length
|
2055 |
-
|
2056 |
-
if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
|
2057 |
-
$key = count($cache[self::VARIABLE]);
|
2058 |
-
$cache[self::VARIABLE][] = $m;
|
2059 |
-
|
2060 |
-
$lhs = new static();
|
2061 |
-
$lhs_value = &$lhs->value;
|
2062 |
-
$lhs_value = $this->_array_repeat(0, $m_length + ($m_length >> 1));
|
2063 |
-
$lhs_value[] = 1;
|
2064 |
-
$rhs = new static();
|
2065 |
-
$rhs->value = $m;
|
2066 |
-
|
2067 |
-
list($u, $m1) = $lhs->divide($rhs);
|
2068 |
-
$u = $u->value;
|
2069 |
-
$m1 = $m1->value;
|
2070 |
-
|
2071 |
-
$cache[self::DATA][] = array(
|
2072 |
-
'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1)
|
2073 |
-
'm1'=> $m1 // m.length
|
2074 |
-
);
|
2075 |
-
} else {
|
2076 |
-
extract($cache[self::DATA][$key]);
|
2077 |
-
}
|
2078 |
-
|
2079 |
-
$cutoff = $m_length + ($m_length >> 1);
|
2080 |
-
$lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1)
|
2081 |
-
$msd = array_slice($n, $cutoff); // m.length >> 1
|
2082 |
-
$lsd = $this->_trim($lsd);
|
2083 |
-
$temp = $this->_multiply($msd, false, $m1, false);
|
2084 |
-
$n = $this->_add($lsd, false, $temp[self::VALUE], false); // m.length + (m.length >> 1) + 1
|
2085 |
-
|
2086 |
-
if ($m_length & 1) {
|
2087 |
-
return $this->_regularBarrett($n[self::VALUE], $m);
|
2088 |
-
}
|
2089 |
-
|
2090 |
-
// (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2
|
2091 |
-
$temp = array_slice($n[self::VALUE], $m_length - 1);
|
2092 |
-
// if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2
|
2093 |
-
// if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1
|
2094 |
-
$temp = $this->_multiply($temp, false, $u, false);
|
2095 |
-
// if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1
|
2096 |
-
// if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1)
|
2097 |
-
$temp = array_slice($temp[self::VALUE], ($m_length >> 1) + 1);
|
2098 |
-
// if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1
|
2099 |
-
// if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1)
|
2100 |
-
$temp = $this->_multiply($temp, false, $m, false);
|
2101 |
-
|
2102 |
-
// at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit
|
2103 |
-
// number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop
|
2104 |
-
// following this comment would loop a lot (hence our calling _regularBarrett() in that situation).
|
2105 |
-
|
2106 |
-
$result = $this->_subtract($n[self::VALUE], false, $temp[self::VALUE], false);
|
2107 |
-
|
2108 |
-
while ($this->_compare($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) {
|
2109 |
-
$result = $this->_subtract($result[self::VALUE], $result[self::SIGN], $m, false);
|
2110 |
-
}
|
2111 |
-
|
2112 |
-
return $result[self::VALUE];
|
2113 |
-
}
|
2114 |
-
|
2115 |
-
/**
|
2116 |
-
* (Regular) Barrett Modular Reduction
|
2117 |
-
*
|
2118 |
-
* For numbers with more than four digits BigInteger::_barrett() is faster. The difference between that and this
|
2119 |
-
* is that this function does not fold the denominator into a smaller form.
|
2120 |
-
*
|
2121 |
-
* @see self::_slidingWindow()
|
2122 |
-
* @access private
|
2123 |
-
* @param array $x
|
2124 |
-
* @param array $n
|
2125 |
-
* @return array
|
2126 |
-
*/
|
2127 |
-
function _regularBarrett($x, $n)
|
2128 |
-
{
|
2129 |
-
static $cache = array(
|
2130 |
-
self::VARIABLE => array(),
|
2131 |
-
self::DATA => array()
|
2132 |
-
);
|
2133 |
-
|
2134 |
-
$n_length = count($n);
|
2135 |
-
|
2136 |
-
if (count($x) > 2 * $n_length) {
|
2137 |
-
$lhs = new static();
|
2138 |
-
$rhs = new static();
|
2139 |
-
$lhs->value = $x;
|
2140 |
-
$rhs->value = $n;
|
2141 |
-
list(, $temp) = $lhs->divide($rhs);
|
2142 |
-
return $temp->value;
|
2143 |
-
}
|
2144 |
-
|
2145 |
-
if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
|
2146 |
-
$key = count($cache[self::VARIABLE]);
|
2147 |
-
$cache[self::VARIABLE][] = $n;
|
2148 |
-
$lhs = new static();
|
2149 |
-
$lhs_value = &$lhs->value;
|
2150 |
-
$lhs_value = $this->_array_repeat(0, 2 * $n_length);
|
2151 |
-
$lhs_value[] = 1;
|
2152 |
-
$rhs = new static();
|
2153 |
-
$rhs->value = $n;
|
2154 |
-
list($temp, ) = $lhs->divide($rhs); // m.length
|
2155 |
-
$cache[self::DATA][] = $temp->value;
|
2156 |
-
}
|
2157 |
-
|
2158 |
-
// 2 * m.length - (m.length - 1) = m.length + 1
|
2159 |
-
$temp = array_slice($x, $n_length - 1);
|
2160 |
-
// (m.length + 1) + m.length = 2 * m.length + 1
|
2161 |
-
$temp = $this->_multiply($temp, false, $cache[self::DATA][$key], false);
|
2162 |
-
// (2 * m.length + 1) - (m.length - 1) = m.length + 2
|
2163 |
-
$temp = array_slice($temp[self::VALUE], $n_length + 1);
|
2164 |
-
|
2165 |
-
// m.length + 1
|
2166 |
-
$result = array_slice($x, 0, $n_length + 1);
|
2167 |
-
// m.length + 1
|
2168 |
-
$temp = $this->_multiplyLower($temp, false, $n, false, $n_length + 1);
|
2169 |
-
// $temp == array_slice($temp->_multiply($temp, false, $n, false)->value, 0, $n_length + 1)
|
2170 |
-
|
2171 |
-
if ($this->_compare($result, false, $temp[self::VALUE], $temp[self::SIGN]) < 0) {
|
2172 |
-
$corrector_value = $this->_array_repeat(0, $n_length + 1);
|
2173 |
-
$corrector_value[count($corrector_value)] = 1;
|
2174 |
-
$result = $this->_add($result, false, $corrector_value, false);
|
2175 |
-
$result = $result[self::VALUE];
|
2176 |
-
}
|
2177 |
-
|
2178 |
-
// at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits
|
2179 |
-
$result = $this->_subtract($result, false, $temp[self::VALUE], $temp[self::SIGN]);
|
2180 |
-
while ($this->_compare($result[self::VALUE], $result[self::SIGN], $n, false) > 0) {
|
2181 |
-
$result = $this->_subtract($result[self::VALUE], $result[self::SIGN], $n, false);
|
2182 |
-
}
|
2183 |
-
|
2184 |
-
return $result[self::VALUE];
|
2185 |
-
}
|
2186 |
-
|
2187 |
-
/**
|
2188 |
-
* Performs long multiplication up to $stop digits
|
2189 |
-
*
|
2190 |
-
* If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved.
|
2191 |
-
*
|
2192 |
-
* @see self::_regularBarrett()
|
2193 |
-
* @param array $x_value
|
2194 |
-
* @param bool $x_negative
|
2195 |
-
* @param array $y_value
|
2196 |
-
* @param bool $y_negative
|
2197 |
-
* @param int $stop
|
2198 |
-
* @return array
|
2199 |
-
* @access private
|
2200 |
-
*/
|
2201 |
-
function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop)
|
2202 |
-
{
|
2203 |
-
$x_length = count($x_value);
|
2204 |
-
$y_length = count($y_value);
|
2205 |
-
|
2206 |
-
if (!$x_length || !$y_length) { // a 0 is being multiplied
|
2207 |
-
return array(
|
2208 |
-
self::VALUE => array(),
|
2209 |
-
self::SIGN => false
|
2210 |
-
);
|
2211 |
-
}
|
2212 |
-
|
2213 |
-
if ($x_length < $y_length) {
|
2214 |
-
$temp = $x_value;
|
2215 |
-
$x_value = $y_value;
|
2216 |
-
$y_value = $temp;
|
2217 |
-
|
2218 |
-
$x_length = count($x_value);
|
2219 |
-
$y_length = count($y_value);
|
2220 |
-
}
|
2221 |
-
|
2222 |
-
$product_value = $this->_array_repeat(0, $x_length + $y_length);
|
2223 |
-
|
2224 |
-
// the following for loop could be removed if the for loop following it
|
2225 |
-
// (the one with nested for loops) initially set $i to 0, but
|
2226 |
-
// doing so would also make the result in one set of unnecessary adds,
|
2227 |
-
// since on the outermost loops first pass, $product->value[$k] is going
|
2228 |
-
// to always be 0
|
2229 |
-
|
2230 |
-
$carry = 0;
|
2231 |
-
|
2232 |
-
for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i
|
2233 |
-
$temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
|
2234 |
-
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
|
2235 |
-
$product_value[$j] = (int) ($temp - self::$baseFull * $carry);
|
2236 |
-
}
|
2237 |
-
|
2238 |
-
if ($j < $stop) {
|
2239 |
-
$product_value[$j] = $carry;
|
2240 |
-
}
|
2241 |
-
|
2242 |
-
// the above for loop is what the previous comment was talking about. the
|
2243 |
-
// following for loop is the "one with nested for loops"
|
2244 |
-
|
2245 |
-
for ($i = 1; $i < $y_length; ++$i) {
|
2246 |
-
$carry = 0;
|
2247 |
-
|
2248 |
-
for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) {
|
2249 |
-
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
|
2250 |
-
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
|
2251 |
-
$product_value[$k] = (int) ($temp - self::$baseFull * $carry);
|
2252 |
-
}
|
2253 |
-
|
2254 |
-
if ($k < $stop) {
|
2255 |
-
$product_value[$k] = $carry;
|
2256 |
-
}
|
2257 |
-
}
|
2258 |
-
|
2259 |
-
return array(
|
2260 |
-
self::VALUE => $this->_trim($product_value),
|
2261 |
-
self::SIGN => $x_negative != $y_negative
|
2262 |
-
);
|
2263 |
-
}
|
2264 |
-
|
2265 |
-
/**
|
2266 |
-
* Montgomery Modular Reduction
|
2267 |
-
*
|
2268 |
-
* ($x->_prepMontgomery($n))->_montgomery($n) yields $x % $n.
|
2269 |
-
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=170 MPM 6.3} provides insights on how this can be
|
2270 |
-
* improved upon (basically, by using the comba method). gcd($n, 2) must be equal to one for this function
|
2271 |
-
* to work correctly.
|
2272 |
-
*
|
2273 |
-
* @see self::_prepMontgomery()
|
2274 |
-
* @see self::_slidingWindow()
|
2275 |
-
* @access private
|
2276 |
-
* @param array $x
|
2277 |
-
* @param array $n
|
2278 |
-
* @return array
|
2279 |
-
*/
|
2280 |
-
function _montgomery($x, $n)
|
2281 |
-
{
|
2282 |
-
static $cache = array(
|
2283 |
-
self::VARIABLE => array(),
|
2284 |
-
self::DATA => array()
|
2285 |
-
);
|
2286 |
-
|
2287 |
-
if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
|
2288 |
-
$key = count($cache[self::VARIABLE]);
|
2289 |
-
$cache[self::VARIABLE][] = $x;
|
2290 |
-
$cache[self::DATA][] = $this->_modInverse67108864($n);
|
2291 |
-
}
|
2292 |
-
|
2293 |
-
$k = count($n);
|
2294 |
-
|
2295 |
-
$result = array(self::VALUE => $x);
|
2296 |
-
|
2297 |
-
for ($i = 0; $i < $k; ++$i) {
|
2298 |
-
$temp = $result[self::VALUE][$i] * $cache[self::DATA][$key];
|
2299 |
-
$temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
|
2300 |
-
$temp = $this->_regularMultiply(array($temp), $n);
|
2301 |
-
$temp = array_merge($this->_array_repeat(0, $i), $temp);
|
2302 |
-
$result = $this->_add($result[self::VALUE], false, $temp, false);
|
2303 |
-
}
|
2304 |
-
|
2305 |
-
$result[self::VALUE] = array_slice($result[self::VALUE], $k);
|
2306 |
-
|
2307 |
-
if ($this->_compare($result, false, $n, false) >= 0) {
|
2308 |
-
$result = $this->_subtract($result[self::VALUE], false, $n, false);
|
2309 |
-
}
|
2310 |
-
|
2311 |
-
return $result[self::VALUE];
|
2312 |
-
}
|
2313 |
-
|
2314 |
-
/**
|
2315 |
-
* Montgomery Multiply
|
2316 |
-
*
|
2317 |
-
* Interleaves the montgomery reduction and long multiplication algorithms together as described in
|
2318 |
-
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
|
2319 |
-
*
|
2320 |
-
* @see self::_prepMontgomery()
|
2321 |
-
* @see self::_montgomery()
|
2322 |
-
* @access private
|
2323 |
-
* @param array $x
|
2324 |
-
* @param array $y
|
2325 |
-
* @param array $m
|
2326 |
-
* @return array
|
2327 |
-
*/
|
2328 |
-
function _montgomeryMultiply($x, $y, $m)
|
2329 |
-
{
|
2330 |
-
$temp = $this->_multiply($x, false, $y, false);
|
2331 |
-
return $this->_montgomery($temp[self::VALUE], $m);
|
2332 |
-
|
2333 |
-
// the following code, although not callable, can be run independently of the above code
|
2334 |
-
// although the above code performed better in my benchmarks the following could might
|
2335 |
-
// perform better under different circumstances. in lieu of deleting it it's just been
|
2336 |
-
// made uncallable
|
2337 |
-
|
2338 |
-
static $cache = array(
|
2339 |
-
self::VARIABLE => array(),
|
2340 |
-
self::DATA => array()
|
2341 |
-
);
|
2342 |
-
|
2343 |
-
if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
|
2344 |
-
$key = count($cache[self::VARIABLE]);
|
2345 |
-
$cache[self::VARIABLE][] = $m;
|
2346 |
-
$cache[self::DATA][] = $this->_modInverse67108864($m);
|
2347 |
-
}
|
2348 |
-
|
2349 |
-
$n = max(count($x), count($y), count($m));
|
2350 |
-
$x = array_pad($x, $n, 0);
|
2351 |
-
$y = array_pad($y, $n, 0);
|
2352 |
-
$m = array_pad($m, $n, 0);
|
2353 |
-
$a = array(self::VALUE => $this->_array_repeat(0, $n + 1));
|
2354 |
-
for ($i = 0; $i < $n; ++$i) {
|
2355 |
-
$temp = $a[self::VALUE][0] + $x[$i] * $y[0];
|
2356 |
-
$temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
|
2357 |
-
$temp = $temp * $cache[self::DATA][$key];
|
2358 |
-
$temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
|
2359 |
-
$temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false);
|
2360 |
-
$a = $this->_add($a[self::VALUE], false, $temp[self::VALUE], false);
|
2361 |
-
$a[self::VALUE] = array_slice($a[self::VALUE], 1);
|
2362 |
-
}
|
2363 |
-
if ($this->_compare($a[self::VALUE], false, $m, false) >= 0) {
|
2364 |
-
$a = $this->_subtract($a[self::VALUE], false, $m, false);
|
2365 |
-
}
|
2366 |
-
return $a[self::VALUE];
|
2367 |
-
}
|
2368 |
-
|
2369 |
-
/**
|
2370 |
-
* Prepare a number for use in Montgomery Modular Reductions
|
2371 |
-
*
|
2372 |
-
* @see self::_montgomery()
|
2373 |
-
* @see self::_slidingWindow()
|
2374 |
-
* @access private
|
2375 |
-
* @param array $x
|
2376 |
-
* @param array $n
|
2377 |
-
* @return array
|
2378 |
-
*/
|
2379 |
-
function _prepMontgomery($x, $n)
|
2380 |
-
{
|
2381 |
-
$lhs = new static();
|
2382 |
-
$lhs->value = array_merge($this->_array_repeat(0, count($n)), $x);
|
2383 |
-
$rhs = new static();
|
2384 |
-
$rhs->value = $n;
|
2385 |
-
|
2386 |
-
list(, $temp) = $lhs->divide($rhs);
|
2387 |
-
return $temp->value;
|
2388 |
-
}
|
2389 |
-
|
2390 |
-
/**
|
2391 |
-
* Modular Inverse of a number mod 2**26 (eg. 67108864)
|
2392 |
-
*
|
2393 |
-
* Based off of the bnpInvDigit function implemented and justified in the following URL:
|
2394 |
-
*
|
2395 |
-
* {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js}
|
2396 |
-
*
|
2397 |
-
* The following URL provides more info:
|
2398 |
-
*
|
2399 |
-
* {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85}
|
2400 |
-
*
|
2401 |
-
* As for why we do all the bitmasking... strange things can happen when converting from floats to ints. For
|
2402 |
-
* instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields
|
2403 |
-
* int(-2147483648). To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't
|
2404 |
-
* auto-converted to floats. The outermost bitmask is present because without it, there's no guarantee that
|
2405 |
-
* the "residue" returned would be the so-called "common residue". We use fmod, in the last step, because the
|
2406 |
-
* maximum possible $x is 26 bits and the maximum $result is 16 bits. Thus, we have to be able to handle up to
|
2407 |
-
* 40 bits, which only 64-bit floating points will support.
|
2408 |
-
*
|
2409 |
-
* Thanks to Pedro Gimeno Fortea for input!
|
2410 |
-
*
|
2411 |
-
* @see self::_montgomery()
|
2412 |
-
* @access private
|
2413 |
-
* @param array $x
|
2414 |
-
* @return int
|
2415 |
-
*/
|
2416 |
-
function _modInverse67108864($x) // 2**26 == 67,108,864
|
2417 |
-
{
|
2418 |
-
$x = -$x[0];
|
2419 |
-
$result = $x & 0x3; // x**-1 mod 2**2
|
2420 |
-
$result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4
|
2421 |
-
$result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; // x**-1 mod 2**8
|
2422 |
-
$result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16
|
2423 |
-
$result = fmod($result * (2 - fmod($x * $result, self::$baseFull)), self::$baseFull); // x**-1 mod 2**26
|
2424 |
-
return $result & self::$maxDigit;
|
2425 |
-
}
|
2426 |
-
|
2427 |
-
/**
|
2428 |
-
* Calculates modular inverses.
|
2429 |
-
*
|
2430 |
-
* Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
|
2431 |
-
*
|
2432 |
-
* Here's an example:
|
2433 |
-
* <code>
|
2434 |
-
* <?php
|
2435 |
-
* $a = new \phpseclib\Math\BigInteger(30);
|
2436 |
-
* $b = new \phpseclib\Math\BigInteger(17);
|
2437 |
-
*
|
2438 |
-
* $c = $a->modInverse($b);
|
2439 |
-
* echo $c->toString(); // outputs 4
|
2440 |
-
*
|
2441 |
-
* echo "\r\n";
|
2442 |
-
*
|
2443 |
-
* $d = $a->multiply($c);
|
2444 |
-
* list(, $d) = $d->divide($b);
|
2445 |
-
* echo $d; // outputs 1 (as per the definition of modular inverse)
|
2446 |
-
* ?>
|
2447 |
-
* </code>
|
2448 |
-
*
|
2449 |
-
* @param \phpseclib\Math\BigInteger $n
|
2450 |
-
* @return \phpseclib\Math\BigInteger|false
|
2451 |
-
* @access public
|
2452 |
-
* @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information.
|
2453 |
-
*/
|
2454 |
-
function modInverse($n)
|
2455 |
-
{
|
2456 |
-
switch (MATH_BIGINTEGER_MODE) {
|
2457 |
-
case self::MODE_GMP:
|
2458 |
-
$temp = new static();
|
2459 |
-
$temp->value = gmp_invert($this->value, $n->value);
|
2460 |
-
|
2461 |
-
return ($temp->value === false) ? false : $this->_normalize($temp);
|
2462 |
-
}
|
2463 |
-
|
2464 |
-
static $zero, $one;
|
2465 |
-
if (!isset($zero)) {
|
2466 |
-
$zero = new static();
|
2467 |
-
$one = new static(1);
|
2468 |
-
}
|
2469 |
-
|
2470 |
-
// $x mod -$n == $x mod $n.
|
2471 |
-
$n = $n->abs();
|
2472 |
-
|
2473 |
-
if ($this->compare($zero) < 0) {
|
2474 |
-
$temp = $this->abs();
|
2475 |
-
$temp = $temp->modInverse($n);
|
2476 |
-
return $this->_normalize($n->subtract($temp));
|
2477 |
-
}
|
2478 |
-
|
2479 |
-
extract($this->extendedGCD($n));
|
2480 |
-
|
2481 |
-
if (!$gcd->equals($one)) {
|
2482 |
-
return false;
|
2483 |
-
}
|
2484 |
-
|
2485 |
-
$x = $x->compare($zero) < 0 ? $x->add($n) : $x;
|
2486 |
-
|
2487 |
-
return $this->compare($zero) < 0 ? $this->_normalize($n->subtract($x)) : $this->_normalize($x);
|
2488 |
-
}
|
2489 |
-
|
2490 |
-
/**
|
2491 |
-
* Calculates the greatest common divisor and Bezout's identity.
|
2492 |
-
*
|
2493 |
-
* Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that
|
2494 |
-
* 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which
|
2495 |
-
* combination is returned is dependent upon which mode is in use. See
|
2496 |
-
* {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information.
|
2497 |
-
*
|
2498 |
-
* Here's an example:
|
2499 |
-
* <code>
|
2500 |
-
* <?php
|
2501 |
-
* $a = new \phpseclib\Math\BigInteger(693);
|
2502 |
-
* $b = new \phpseclib\Math\BigInteger(609);
|
2503 |
-
*
|
2504 |
-
* extract($a->extendedGCD($b));
|
2505 |
-
*
|
2506 |
-
* echo $gcd->toString() . "\r\n"; // outputs 21
|
2507 |
-
* echo $a->toString() * $x->toString() + $b->toString() * $y->toString(); // outputs 21
|
2508 |
-
* ?>
|
2509 |
-
* </code>
|
2510 |
-
*
|
2511 |
-
* @param \phpseclib\Math\BigInteger $n
|
2512 |
-
* @return \phpseclib\Math\BigInteger
|
2513 |
-
* @access public
|
2514 |
-
* @internal Calculates the GCD using the binary xGCD algorithim described in
|
2515 |
-
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes,
|
2516 |
-
* the more traditional algorithim requires "relatively costly multiple-precision divisions".
|
2517 |
-
*/
|
2518 |
-
function extendedGCD($n)
|
2519 |
-
{
|
2520 |
-
switch (MATH_BIGINTEGER_MODE) {
|
2521 |
-
case self::MODE_GMP:
|
2522 |
-
extract(gmp_gcdext($this->value, $n->value));
|
2523 |
-
|
2524 |
-
return array(
|
2525 |
-
'gcd' => $this->_normalize(new static($g)),
|
2526 |
-
'x' => $this->_normalize(new static($s)),
|
2527 |
-
'y' => $this->_normalize(new static($t))
|
2528 |
-
);
|
2529 |
-
case self::MODE_BCMATH:
|
2530 |
-
// it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works
|
2531 |
-
// best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is,
|
2532 |
-
// the basic extended euclidean algorithim is what we're using.
|
2533 |
-
|
2534 |
-
$u = $this->value;
|
2535 |
-
$v = $n->value;
|
2536 |
-
|
2537 |
-
$a = '1';
|
2538 |
-
$b = '0';
|
2539 |
-
$c = '0';
|
2540 |
-
$d = '1';
|
2541 |
-
|
2542 |
-
while (bccomp($v, '0', 0) != 0) {
|
2543 |
-
$q = bcdiv($u, $v, 0);
|
2544 |
-
|
2545 |
-
$temp = $u;
|
2546 |
-
$u = $v;
|
2547 |
-
$v = bcsub($temp, bcmul($v, $q, 0), 0);
|
2548 |
-
|
2549 |
-
$temp = $a;
|
2550 |
-
$a = $c;
|
2551 |
-
$c = bcsub($temp, bcmul($a, $q, 0), 0);
|
2552 |
-
|
2553 |
-
$temp = $b;
|
2554 |
-
$b = $d;
|
2555 |
-
$d = bcsub($temp, bcmul($b, $q, 0), 0);
|
2556 |
-
}
|
2557 |
-
|
2558 |
-
return array(
|
2559 |
-
'gcd' => $this->_normalize(new static($u)),
|
2560 |
-
'x' => $this->_normalize(new static($a)),
|
2561 |
-
'y' => $this->_normalize(new static($b))
|
2562 |
-
);
|
2563 |
-
}
|
2564 |
-
|
2565 |
-
$y = $n->copy();
|
2566 |
-
$x = $this->copy();
|
2567 |
-
$g = new static();
|
2568 |
-
$g->value = array(1);
|
2569 |
-
|
2570 |
-
while (!(($x->value[0] & 1)|| ($y->value[0] & 1))) {
|
2571 |
-
$x->_rshift(1);
|
2572 |
-
$y->_rshift(1);
|
2573 |
-
$g->_lshift(1);
|
2574 |
-
}
|
2575 |
-
|
2576 |
-
$u = $x->copy();
|
2577 |
-
$v = $y->copy();
|
2578 |
-
|
2579 |
-
$a = new static();
|
2580 |
-
$b = new static();
|
2581 |
-
$c = new static();
|
2582 |
-
$d = new static();
|
2583 |
-
|
2584 |
-
$a->value = $d->value = $g->value = array(1);
|
2585 |
-
$b->value = $c->value = array();
|
2586 |
-
|
2587 |
-
while (!empty($u->value)) {
|
2588 |
-
while (!($u->value[0] & 1)) {
|
2589 |
-
$u->_rshift(1);
|
2590 |
-
if ((!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1))) {
|
2591 |
-
$a = $a->add($y);
|
2592 |
-
$b = $b->subtract($x);
|
2593 |
-
}
|
2594 |
-
$a->_rshift(1);
|
2595 |
-
$b->_rshift(1);
|
2596 |
-
}
|
2597 |
-
|
2598 |
-
while (!($v->value[0] & 1)) {
|
2599 |
-
$v->_rshift(1);
|
2600 |
-
if ((!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1))) {
|
2601 |
-
$c = $c->add($y);
|
2602 |
-
$d = $d->subtract($x);
|
2603 |
-
}
|
2604 |
-
$c->_rshift(1);
|
2605 |
-
$d->_rshift(1);
|
2606 |
-
}
|
2607 |
-
|
2608 |
-
if ($u->compare($v) >= 0) {
|
2609 |
-
$u = $u->subtract($v);
|
2610 |
-
$a = $a->subtract($c);
|
2611 |
-
$b = $b->subtract($d);
|
2612 |
-
} else {
|
2613 |
-
$v = $v->subtract($u);
|
2614 |
-
$c = $c->subtract($a);
|
2615 |
-
$d = $d->subtract($b);
|
2616 |
-
}
|
2617 |
-
}
|
2618 |
-
|
2619 |
-
return array(
|
2620 |
-
'gcd' => $this->_normalize($g->multiply($v)),
|
2621 |
-
'x' => $this->_normalize($c),
|
2622 |
-
'y' => $this->_normalize($d)
|
2623 |
-
);
|
2624 |
-
}
|
2625 |
-
|
2626 |
-
/**
|
2627 |
-
* Calculates the greatest common divisor
|
2628 |
-
*
|
2629 |
-
* Say you have 693 and 609. The GCD is 21.
|
2630 |
-
*
|
2631 |
-
* Here's an example:
|
2632 |
-
* <code>
|
2633 |
-
* <?php
|
2634 |
-
* $a = new \phpseclib\Math\BigInteger(693);
|
2635 |
-
* $b = new \phpseclib\Math\BigInteger(609);
|
2636 |
-
*
|
2637 |
-
* $gcd = a->extendedGCD($b);
|
2638 |
-
*
|
2639 |
-
* echo $gcd->toString() . "\r\n"; // outputs 21
|
2640 |
-
* ?>
|
2641 |
-
* </code>
|
2642 |
-
*
|
2643 |
-
* @param \phpseclib\Math\BigInteger $n
|
2644 |
-
* @return \phpseclib\Math\BigInteger
|
2645 |
-
* @access public
|
2646 |
-
*/
|
2647 |
-
function gcd($n)
|
2648 |
-
{
|
2649 |
-
extract($this->extendedGCD($n));
|
2650 |
-
return $gcd;
|
2651 |
-
}
|
2652 |
-
|
2653 |
-
/**
|
2654 |
-
* Absolute value.
|
2655 |
-
*
|
2656 |
-
* @return \phpseclib\Math\BigInteger
|
2657 |
-
* @access public
|
2658 |
-
*/
|
2659 |
-
function abs()
|
2660 |
-
{
|
2661 |
-
$temp = new static();
|
2662 |
-
|
2663 |
-
switch (MATH_BIGINTEGER_MODE) {
|
2664 |
-
case self::MODE_GMP:
|
2665 |
-
$temp->value = gmp_abs($this->value);
|
2666 |
-
break;
|
2667 |
-
case self::MODE_BCMATH:
|
2668 |
-
$temp->value = (bccomp($this->value, '0', 0) < 0) ? substr($this->value, 1) : $this->value;
|
2669 |
-
break;
|
2670 |
-
default:
|
2671 |
-
$temp->value = $this->value;
|
2672 |
-
}
|
2673 |
-
|
2674 |
-
return $temp;
|
2675 |
-
}
|
2676 |
-
|
2677 |
-
/**
|
2678 |
-
* Compares two numbers.
|
2679 |
-
*
|
2680 |
-
* Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is
|
2681 |
-
* demonstrated thusly:
|
2682 |
-
*
|
2683 |
-
* $x > $y: $x->compare($y) > 0
|
2684 |
-
* $x < $y: $x->compare($y) < 0
|
2685 |
-
* $x == $y: $x->compare($y) == 0
|
2686 |
-
*
|
2687 |
-
* Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y).
|
2688 |
-
*
|
2689 |
-
* @param \phpseclib\Math\BigInteger $y
|
2690 |
-
* @return int < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
|
2691 |
-
* @access public
|
2692 |
-
* @see self::equals()
|
2693 |
-
* @internal Could return $this->subtract($x), but that's not as fast as what we do do.
|
2694 |
-
*/
|
2695 |
-
function compare($y)
|
2696 |
-
{
|
2697 |
-
switch (MATH_BIGINTEGER_MODE) {
|
2698 |
-
case self::MODE_GMP:
|
2699 |
-
$r = gmp_cmp($this->value, $y->value);
|
2700 |
-
if ($r < -1) {
|
2701 |
-
$r = -1;
|
2702 |
-
}
|
2703 |
-
if ($r > 1) {
|
2704 |
-
$r = 1;
|
2705 |
-
}
|
2706 |
-
return $r;
|
2707 |
-
case self::MODE_BCMATH:
|
2708 |
-
return bccomp($this->value, $y->value, 0);
|
2709 |
-
}
|
2710 |
-
|
2711 |
-
return $this->_compare($this->value, $this->is_negative, $y->value, $y->is_negative);
|
2712 |
-
}
|
2713 |
-
|
2714 |
-
/**
|
2715 |
-
* Compares two numbers.
|
2716 |
-
*
|
2717 |
-
* @param array $x_value
|
2718 |
-
* @param bool $x_negative
|
2719 |
-
* @param array $y_value
|
2720 |
-
* @param bool $y_negative
|
2721 |
-
* @return int
|
2722 |
-
* @see self::compare()
|
2723 |
-
* @access private
|
2724 |
-
*/
|
2725 |
-
function _compare($x_value, $x_negative, $y_value, $y_negative)
|
2726 |
-
{
|
2727 |
-
if ($x_negative != $y_negative) {
|
2728 |
-
return (!$x_negative && $y_negative) ? 1 : -1;
|
2729 |
-
}
|
2730 |
-
|
2731 |
-
$result = $x_negative ? -1 : 1;
|
2732 |
-
|
2733 |
-
if (count($x_value) != count($y_value)) {
|
2734 |
-
return (count($x_value) > count($y_value)) ? $result : -$result;
|
2735 |
-
}
|
2736 |
-
$size = max(count($x_value), count($y_value));
|
2737 |
-
|
2738 |
-
$x_value = array_pad($x_value, $size, 0);
|
2739 |
-
$y_value = array_pad($y_value, $size, 0);
|
2740 |
-
|
2741 |
-
for ($i = count($x_value) - 1; $i >= 0; --$i) {
|
2742 |
-
if ($x_value[$i] != $y_value[$i]) {
|
2743 |
-
return ($x_value[$i] > $y_value[$i]) ? $result : -$result;
|
2744 |
-
}
|
2745 |
-
}
|
2746 |
-
|
2747 |
-
return 0;
|
2748 |
-
}
|
2749 |
-
|
2750 |
-
/**
|
2751 |
-
* Tests the equality of two numbers.
|
2752 |
-
*
|
2753 |
-
* If you need to see if one number is greater than or less than another number, use BigInteger::compare()
|
2754 |
-
*
|
2755 |
-
* @param \phpseclib\Math\BigInteger $x
|
2756 |
-
* @return bool
|
2757 |
-
* @access public
|
2758 |
-
* @see self::compare()
|
2759 |
-
*/
|
2760 |
-
function equals($x)
|
2761 |
-
{
|
2762 |
-
switch (MATH_BIGINTEGER_MODE) {
|
2763 |
-
case self::MODE_GMP:
|
2764 |
-
return gmp_cmp($this->value, $x->value) == 0;
|
2765 |
-
default:
|
2766 |
-
return $this->value === $x->value && $this->is_negative == $x->is_negative;
|
2767 |
-
}
|
2768 |
-
}
|
2769 |
-
|
2770 |
-
/**
|
2771 |
-
* Set Precision
|
2772 |
-
*
|
2773 |
-
* Some bitwise operations give different results depending on the precision being used. Examples include left
|
2774 |
-
* shift, not, and rotates.
|
2775 |
-
*
|
2776 |
-
* @param int $bits
|
2777 |
-
* @access public
|
2778 |
-
*/
|
2779 |
-
function setPrecision($bits)
|
2780 |
-
{
|
2781 |
-
$this->precision = $bits;
|
2782 |
-
if (MATH_BIGINTEGER_MODE != self::MODE_BCMATH) {
|
2783 |
-
$this->bitmask = new static(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256);
|
2784 |
-
} else {
|
2785 |
-
$this->bitmask = new static(bcpow('2', $bits, 0));
|
2786 |
-
}
|
2787 |
-
|
2788 |
-
$temp = $this->_normalize($this);
|
2789 |
-
$this->value = $temp->value;
|
2790 |
-
}
|
2791 |
-
|
2792 |
-
/**
|
2793 |
-
* Logical And
|
2794 |
-
*
|
2795 |
-
* @param \phpseclib\Math\BigInteger $x
|
2796 |
-
* @access public
|
2797 |
-
* @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
|
2798 |
-
* @return \phpseclib\Math\BigInteger
|
2799 |
-
*/
|
2800 |
-
function bitwise_and($x)
|
2801 |
-
{
|
2802 |
-
switch (MATH_BIGINTEGER_MODE) {
|
2803 |
-
case self::MODE_GMP:
|
2804 |
-
$temp = new static();
|
2805 |
-
$temp->value = gmp_and($this->value, $x->value);
|
2806 |
-
|
2807 |
-
return $this->_normalize($temp);
|
2808 |
-
case self::MODE_BCMATH:
|
2809 |
-
$left = $this->toBytes();
|
2810 |
-
$right = $x->toBytes();
|
2811 |
-
|
2812 |
-
$length = max(strlen($left), strlen($right));
|
2813 |
-
|
2814 |
-
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
|
2815 |
-
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
|
2816 |
-
|
2817 |
-
return $this->_normalize(new static($left & $right, 256));
|
2818 |
-
}
|
2819 |
-
|
2820 |
-
$result = $this->copy();
|
2821 |
-
|
2822 |
-
$length = min(count($x->value), count($this->value));
|
2823 |
-
|
2824 |
-
$result->value = array_slice($result->value, 0, $length);
|
2825 |
-
|
2826 |
-
for ($i = 0; $i < $length; ++$i) {
|
2827 |
-
$result->value[$i]&= $x->value[$i];
|
2828 |
-
}
|
2829 |
-
|
2830 |
-
return $this->_normalize($result);
|
2831 |
-
}
|
2832 |
-
|
2833 |
-
/**
|
2834 |
-
* Logical Or
|
2835 |
-
*
|
2836 |
-
* @param \phpseclib\Math\BigInteger $x
|
2837 |
-
* @access public
|
2838 |
-
* @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
|
2839 |
-
* @return \phpseclib\Math\BigInteger
|
2840 |
-
*/
|
2841 |
-
function bitwise_or($x)
|
2842 |
-
{
|
2843 |
-
switch (MATH_BIGINTEGER_MODE) {
|
2844 |
-
case self::MODE_GMP:
|
2845 |
-
$temp = new static();
|
2846 |
-
$temp->value = gmp_or($this->value, $x->value);
|
2847 |
-
|
2848 |
-
return $this->_normalize($temp);
|
2849 |
-
case self::MODE_BCMATH:
|
2850 |
-
$left = $this->toBytes();
|
2851 |
-
$right = $x->toBytes();
|
2852 |
-
|
2853 |
-
$length = max(strlen($left), strlen($right));
|
2854 |
-
|
2855 |
-
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
|
2856 |
-
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
|
2857 |
-
|
2858 |
-
return $this->_normalize(new static($left | $right, 256));
|
2859 |
-
}
|
2860 |
-
|
2861 |
-
$length = max(count($this->value), count($x->value));
|
2862 |
-
$result = $this->copy();
|
2863 |
-
$result->value = array_pad($result->value, $length, 0);
|
2864 |
-
$x->value = array_pad($x->value, $length, 0);
|
2865 |
-
|
2866 |
-
for ($i = 0; $i < $length; ++$i) {
|
2867 |
-
$result->value[$i]|= $x->value[$i];
|
2868 |
-
}
|
2869 |
-
|
2870 |
-
return $this->_normalize($result);
|
2871 |
-
}
|
2872 |
-
|
2873 |
-
/**
|
2874 |
-
* Logical Exclusive-Or
|
2875 |
-
*
|
2876 |
-
* @param \phpseclib\Math\BigInteger $x
|
2877 |
-
* @access public
|
2878 |
-
* @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
|
2879 |
-
* @return \phpseclib\Math\BigInteger
|
2880 |
-
*/
|
2881 |
-
function bitwise_xor($x)
|
2882 |
-
{
|
2883 |
-
switch (MATH_BIGINTEGER_MODE) {
|
2884 |
-
case self::MODE_GMP:
|
2885 |
-
$temp = new static();
|
2886 |
-
$temp->value = gmp_xor(gmp_abs($this->value), gmp_abs($x->value));
|
2887 |
-
return $this->_normalize($temp);
|
2888 |
-
case self::MODE_BCMATH:
|
2889 |
-
$left = $this->toBytes();
|
2890 |
-
$right = $x->toBytes();
|
2891 |
-
|
2892 |
-
$length = max(strlen($left), strlen($right));
|
2893 |
-
|
2894 |
-
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
|
2895 |
-
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
|
2896 |
-
|
2897 |
-
return $this->_normalize(new static($left ^ $right, 256));
|
2898 |
-
}
|
2899 |
-
|
2900 |
-
$length = max(count($this->value), count($x->value));
|
2901 |
-
$result = $this->copy();
|
2902 |
-
$result->is_negative = false;
|
2903 |
-
$result->value = array_pad($result->value, $length, 0);
|
2904 |
-
$x->value = array_pad($x->value, $length, 0);
|
2905 |
-
|
2906 |
-
for ($i = 0; $i < $length; ++$i) {
|
2907 |
-
$result->value[$i]^= $x->value[$i];
|
2908 |
-
}
|
2909 |
-
|
2910 |
-
return $this->_normalize($result);
|
2911 |
-
}
|
2912 |
-
|
2913 |
-
/**
|
2914 |
-
* Logical Not
|
2915 |
-
*
|
2916 |
-
* @access public
|
2917 |
-
* @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
|
2918 |
-
* @return \phpseclib\Math\BigInteger
|
2919 |
-
*/
|
2920 |
-
function bitwise_not()
|
2921 |
-
{
|
2922 |
-
// calculuate "not" without regard to $this->precision
|
2923 |
-
// (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0)
|
2924 |
-
$temp = $this->toBytes();
|
2925 |
-
if ($temp == '') {
|
2926 |
-
return $this->_normalize(new static());
|
2927 |
-
}
|
2928 |
-
$pre_msb = decbin(ord($temp[0]));
|
2929 |
-
$temp = ~$temp;
|
2930 |
-
$msb = decbin(ord($temp[0]));
|
2931 |
-
if (strlen($msb) == 8) {
|
2932 |
-
$msb = substr($msb, strpos($msb, '0'));
|
2933 |
-
}
|
2934 |
-
$temp[0] = chr(bindec($msb));
|
2935 |
-
|
2936 |
-
// see if we need to add extra leading 1's
|
2937 |
-
$current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8;
|
2938 |
-
$new_bits = $this->precision - $current_bits;
|
2939 |
-
if ($new_bits <= 0) {
|
2940 |
-
return $this->_normalize(new static($temp, 256));
|
2941 |
-
}
|
2942 |
-
|
2943 |
-
// generate as many leading 1's as we need to.
|
2944 |
-
$leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3);
|
2945 |
-
$this->_base256_lshift($leading_ones, $current_bits);
|
2946 |
-
|
2947 |
-
$temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT);
|
2948 |
-
|
2949 |
-
return $this->_normalize(new static($leading_ones | $temp, 256));
|
2950 |
-
}
|
2951 |
-
|
2952 |
-
/**
|
2953 |
-
* Logical Right Shift
|
2954 |
-
*
|
2955 |
-
* Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
|
2956 |
-
*
|
2957 |
-
* @param int $shift
|
2958 |
-
* @return \phpseclib\Math\BigInteger
|
2959 |
-
* @access public
|
2960 |
-
* @internal The only version that yields any speed increases is the internal version.
|
2961 |
-
*/
|
2962 |
-
function bitwise_rightShift($shift)
|
2963 |
-
{
|
2964 |
-
$temp = new static();
|
2965 |
-
|
2966 |
-
switch (MATH_BIGINTEGER_MODE) {
|
2967 |
-
case self::MODE_GMP:
|
2968 |
-
static $two;
|
2969 |
-
|
2970 |
-
if (!isset($two)) {
|
2971 |
-
$two = gmp_init('2');
|
2972 |
-
}
|
2973 |
-
|
2974 |
-
$temp->value = gmp_div_q($this->value, gmp_pow($two, $shift));
|
2975 |
-
|
2976 |
-
break;
|
2977 |
-
case self::MODE_BCMATH:
|
2978 |
-
$temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0);
|
2979 |
-
|
2980 |
-
break;
|
2981 |
-
default: // could just replace _lshift with this, but then all _lshift() calls would need to be rewritten
|
2982 |
-
// and I don't want to do that...
|
2983 |
-
$temp->value = $this->value;
|
2984 |
-
$temp->_rshift($shift);
|
2985 |
-
}
|
2986 |
-
|
2987 |
-
return $this->_normalize($temp);
|
2988 |
-
}
|
2989 |
-
|
2990 |
-
/**
|
2991 |
-
* Logical Left Shift
|
2992 |
-
*
|
2993 |
-
* Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
|
2994 |
-
*
|
2995 |
-
* @param int $shift
|
2996 |
-
* @return \phpseclib\Math\BigInteger
|
2997 |
-
* @access public
|
2998 |
-
* @internal The only version that yields any speed increases is the internal version.
|
2999 |
-
*/
|
3000 |
-
function bitwise_leftShift($shift)
|
3001 |
-
{
|
3002 |
-
$temp = new static();
|
3003 |
-
|
3004 |
-
switch (MATH_BIGINTEGER_MODE) {
|
3005 |
-
case self::MODE_GMP:
|
3006 |
-
static $two;
|
3007 |
-
|
3008 |
-
if (!isset($two)) {
|
3009 |
-
$two = gmp_init('2');
|
3010 |
-
}
|
3011 |
-
|
3012 |
-
$temp->value = gmp_mul($this->value, gmp_pow($two, $shift));
|
3013 |
-
|
3014 |
-
break;
|
3015 |
-
case self::MODE_BCMATH:
|
3016 |
-
$temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0);
|
3017 |
-
|
3018 |
-
break;
|
3019 |
-
default: // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten
|
3020 |
-
// and I don't want to do that...
|
3021 |
-
$temp->value = $this->value;
|
3022 |
-
$temp->_lshift($shift);
|
3023 |
-
}
|
3024 |
-
|
3025 |
-
return $this->_normalize($temp);
|
3026 |
-
}
|
3027 |
-
|
3028 |
-
/**
|
3029 |
-
* Logical Left Rotate
|
3030 |
-
*
|
3031 |
-
* Instead of the top x bits being dropped they're appended to the shifted bit string.
|
3032 |
-
*
|
3033 |
-
* @param int $shift
|
3034 |
-
* @return \phpseclib\Math\BigInteger
|
3035 |
-
* @access public
|
3036 |
-
*/
|
3037 |
-
function bitwise_leftRotate($shift)
|
3038 |
-
{
|
3039 |
-
$bits = $this->toBytes();
|
3040 |
-
|
3041 |
-
if ($this->precision > 0) {
|
3042 |
-
$precision = $this->precision;
|
3043 |
-
if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
|
3044 |
-
$mask = $this->bitmask->subtract(new static(1));
|
3045 |
-
$mask = $mask->toBytes();
|
3046 |
-
} else {
|
3047 |
-
$mask = $this->bitmask->toBytes();
|
3048 |
-
}
|
3049 |
-
} else {
|
3050 |
-
$temp = ord($bits[0]);
|
3051 |
-
for ($i = 0; $temp >> $i; ++$i) {
|
3052 |
-
}
|
3053 |
-
$precision = 8 * strlen($bits) - 8 + $i;
|
3054 |
-
$mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3);
|
3055 |
-
}
|
3056 |
-
|
3057 |
-
if ($shift < 0) {
|
3058 |
-
$shift+= $precision;
|
3059 |
-
}
|
3060 |
-
$shift%= $precision;
|
3061 |
-
|
3062 |
-
if (!$shift) {
|
3063 |
-
return $this->copy();
|
3064 |
-
}
|
3065 |
-
|
3066 |
-
$left = $this->bitwise_leftShift($shift);
|
3067 |
-
$left = $left->bitwise_and(new static($mask, 256));
|
3068 |
-
$right = $this->bitwise_rightShift($precision - $shift);
|
3069 |
-
$result = MATH_BIGINTEGER_MODE != self::MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right);
|
3070 |
-
return $this->_normalize($result);
|
3071 |
-
}
|
3072 |
-
|
3073 |
-
/**
|
3074 |
-
* Logical Right Rotate
|
3075 |
-
*
|
3076 |
-
* Instead of the bottom x bits being dropped they're prepended to the shifted bit string.
|
3077 |
-
*
|
3078 |
-
* @param int $shift
|
3079 |
-
* @return \phpseclib\Math\BigInteger
|
3080 |
-
* @access public
|
3081 |
-
*/
|
3082 |
-
function bitwise_rightRotate($shift)
|
3083 |
-
{
|
3084 |
-
return $this->bitwise_leftRotate(-$shift);
|
3085 |
-
}
|
3086 |
-
|
3087 |
-
/**
|
3088 |
-
* Generates a random BigInteger
|
3089 |
-
*
|
3090 |
-
* Byte length is equal to $length. Uses \phpseclib\Crypt\Random if it's loaded and mt_rand if it's not.
|
3091 |
-
*
|
3092 |
-
* @param int $length
|
3093 |
-
* @return \phpseclib\Math\BigInteger
|
3094 |
-
* @access private
|
3095 |
-
*/
|
3096 |
-
function _random_number_helper($size)
|
3097 |
-
{
|
3098 |
-
if (class_exists('\phpseclib\Crypt\Random')) {
|
3099 |
-
$random = Random::string($size);
|
3100 |
-
} else {
|
3101 |
-
$random = '';
|
3102 |
-
|
3103 |
-
if ($size & 1) {
|
3104 |
-
$random.= chr(mt_rand(0, 255));
|
3105 |
-
}
|
3106 |
-
|
3107 |
-
$blocks = $size >> 1;
|
3108 |
-
for ($i = 0; $i < $blocks; ++$i) {
|
3109 |
-
// mt_rand(-2147483648, 0x7FFFFFFF) always produces -2147483648 on some systems
|
3110 |
-
$random.= pack('n', mt_rand(0, 0xFFFF));
|
3111 |
-
}
|
3112 |
-
}
|
3113 |
-
|
3114 |
-
return new static($random, 256);
|
3115 |
-
}
|
3116 |
-
|
3117 |
-
/**
|
3118 |
-
* Generate a random number
|
3119 |
-
*
|
3120 |
-
* Returns a random number between $min and $max where $min and $max
|
3121 |
-
* can be defined using one of the two methods:
|
3122 |
-
*
|
3123 |
-
* $min->random($max)
|
3124 |
-
* $max->random($min)
|
3125 |
-
*
|
3126 |
-
* @param \phpseclib\Math\BigInteger $arg1
|
3127 |
-
* @param \phpseclib\Math\BigInteger $arg2
|
3128 |
-
* @return \phpseclib\Math\BigInteger
|
3129 |
-
* @access public
|
3130 |
-
* @internal The API for creating random numbers used to be $a->random($min, $max), where $a was a BigInteger object.
|
3131 |
-
* That method is still supported for BC purposes.
|
3132 |
-
*/
|
3133 |
-
function random($arg1, $arg2 = false)
|
3134 |
-
{
|
3135 |
-
if ($arg1 === false) {
|
3136 |
-
return false;
|
3137 |
-
}
|
3138 |
-
|
3139 |
-
if ($arg2 === false) {
|
3140 |
-
$max = $arg1;
|
3141 |
-
$min = $this;
|
3142 |
-
} else {
|
3143 |
-
$min = $arg1;
|
3144 |
-
$max = $arg2;
|
3145 |
-
}
|
3146 |
-
|
3147 |
-
$compare = $max->compare($min);
|
3148 |
-
|
3149 |
-
if (!$compare) {
|
3150 |
-
return $this->_normalize($min);
|
3151 |
-
} elseif ($compare < 0) {
|
3152 |
-
// if $min is bigger then $max, swap $min and $max
|
3153 |
-
$temp = $max;
|
3154 |
-
$max = $min;
|
3155 |
-
$min = $temp;
|
3156 |
-
}
|
3157 |
-
|
3158 |
-
static $one;
|
3159 |
-
if (!isset($one)) {
|
3160 |
-
$one = new static(1);
|
3161 |
-
}
|
3162 |
-
|
3163 |
-
$max = $max->subtract($min->subtract($one));
|
3164 |
-
$size = strlen(ltrim($max->toBytes(), chr(0)));
|
3165 |
-
|
3166 |
-
/*
|
3167 |
-
doing $random % $max doesn't work because some numbers will be more likely to occur than others.
|
3168 |
-
eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145
|
3169 |
-
would produce 5 whereas the only value of random that could produce 139 would be 139. ie.
|
3170 |
-
not all numbers would be equally likely. some would be more likely than others.
|
3171 |
-
|
3172 |
-
creating a whole new random number until you find one that is within the range doesn't work
|
3173 |
-
because, for sufficiently small ranges, the likelihood that you'd get a number within that range
|
3174 |
-
would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability
|
3175 |
-
would be pretty high that $random would be greater than $max.
|
3176 |
-
|
3177 |
-
phpseclib works around this using the technique described here:
|
3178 |
-
|
3179 |
-
http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string
|
3180 |
-
*/
|
3181 |
-
$random_max = new static(chr(1) . str_repeat("\0", $size), 256);
|
3182 |
-
$random = $this->_random_number_helper($size);
|
3183 |
-
|
3184 |
-
list($max_multiple) = $random_max->divide($max);
|
3185 |
-
$max_multiple = $max_multiple->multiply($max);
|
3186 |
-
|
3187 |
-
while ($random->compare($max_multiple) >= 0) {
|
3188 |
-
$random = $random->subtract($max_multiple);
|
3189 |
-
$random_max = $random_max->subtract($max_multiple);
|
3190 |
-
$random = $random->bitwise_leftShift(8);
|
3191 |
-
$random = $random->add($this->_random_number_helper(1));
|
3192 |
-
$random_max = $random_max->bitwise_leftShift(8);
|
3193 |
-
list($max_multiple) = $random_max->divide($max);
|
3194 |
-
$max_multiple = $max_multiple->multiply($max);
|
3195 |
-
}
|
3196 |
-
list(, $random) = $random->divide($max);
|
3197 |
-
|
3198 |
-
return $this->_normalize($random->add($min));
|
3199 |
-
}
|
3200 |
-
|
3201 |
-
/**
|
3202 |
-
* Generate a random prime number.
|
3203 |
-
*
|
3204 |
-
* If there's not a prime within the given range, false will be returned.
|
3205 |
-
* If more than $timeout seconds have elapsed, give up and return false.
|
3206 |
-
*
|
3207 |
-
* @param \phpseclib\Math\BigInteger $arg1
|
3208 |
-
* @param \phpseclib\Math\BigInteger $arg2
|
3209 |
-
* @param int $timeout
|
3210 |
-
* @return Math_BigInteger|false
|
3211 |
-
* @access public
|
3212 |
-
* @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}.
|
3213 |
-
*/
|
3214 |
-
function randomPrime($arg1, $arg2 = false, $timeout = false)
|
3215 |
-
{
|
3216 |
-
if ($arg1 === false) {
|
3217 |
-
return false;
|
3218 |
-
}
|
3219 |
-
|
3220 |
-
if ($arg2 === false) {
|
3221 |
-
$max = $arg1;
|
3222 |
-
$min = $this;
|
3223 |
-
} else {
|
3224 |
-
$min = $arg1;
|
3225 |
-
$max = $arg2;
|
3226 |
-
}
|
3227 |
-
|
3228 |
-
$compare = $max->compare($min);
|
3229 |
-
|
3230 |
-
if (!$compare) {
|
3231 |
-
return $min->isPrime() ? $min : false;
|
3232 |
-
} elseif ($compare < 0) {
|
3233 |
-
// if $min is bigger then $max, swap $min and $max
|
3234 |
-
$temp = $max;
|
3235 |
-
$max = $min;
|
3236 |
-
$min = $temp;
|
3237 |
-
}
|
3238 |
-
|
3239 |
-
static $one, $two;
|
3240 |
-
if (!isset($one)) {
|
3241 |
-
$one = new static(1);
|
3242 |
-
$two = new static(2);
|
3243 |
-
}
|
3244 |
-
|
3245 |
-
$start = time();
|
3246 |
-
|
3247 |
-
$x = $this->random($min, $max);
|
3248 |
-
|
3249 |
-
// gmp_nextprime() requires PHP 5 >= 5.2.0 per <http://php.net/gmp-nextprime>.
|
3250 |
-
if (MATH_BIGINTEGER_MODE == self::MODE_GMP && extension_loaded('gmp')) {
|
3251 |
-
$p = new static();
|
3252 |
-
$p->value = gmp_nextprime($x->value);
|
3253 |
-
|
3254 |
-
if ($p->compare($max) <= 0) {
|
3255 |
-
return $p;
|
3256 |
-
}
|
3257 |
-
|
3258 |
-
if (!$min->equals($x)) {
|
3259 |
-
$x = $x->subtract($one);
|
3260 |
-
}
|
3261 |
-
|
3262 |
-
return $x->randomPrime($min, $x);
|
3263 |
-
}
|
3264 |
-
|
3265 |
-
if ($x->equals($two)) {
|
3266 |
-
return $x;
|
3267 |
-
}
|
3268 |
-
|
3269 |
-
$x->_make_odd();
|
3270 |
-
if ($x->compare($max) > 0) {
|
3271 |
-
// if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range
|
3272 |
-
if ($min->equals($max)) {
|
3273 |
-
return false;
|
3274 |
-
}
|
3275 |
-
$x = $min->copy();
|
3276 |
-
$x->_make_odd();
|
3277 |
-
}
|
3278 |
-
|
3279 |
-
$initial_x = $x->copy();
|
3280 |
-
|
3281 |
-
while (true) {
|
3282 |
-
if ($timeout !== false && time() - $start > $timeout) {
|
3283 |
-
return false;
|
3284 |
-
}
|
3285 |
-
|
3286 |
-
if ($x->isPrime()) {
|
3287 |
-
return $x;
|
3288 |
-
}
|
3289 |
-
|
3290 |
-
$x = $x->add($two);
|
3291 |
-
|
3292 |
-
if ($x->compare($max) > 0) {
|
3293 |
-
$x = $min->copy();
|
3294 |
-
if ($x->equals($two)) {
|
3295 |
-
return $x;
|
3296 |
-
}
|
3297 |
-
$x->_make_odd();
|
3298 |
-
}
|
3299 |
-
|
3300 |
-
if ($x->equals($initial_x)) {
|
3301 |
-
return false;
|
3302 |
-
}
|
3303 |
-
}
|
3304 |
-
}
|
3305 |
-
|
3306 |
-
/**
|
3307 |
-
* Make the current number odd
|
3308 |
-
*
|
3309 |
-
* If the current number is odd it'll be unchanged. If it's even, one will be added to it.
|
3310 |
-
*
|
3311 |
-
* @see self::randomPrime()
|
3312 |
-
* @access private
|
3313 |
-
*/
|
3314 |
-
function _make_odd()
|
3315 |
-
{
|
3316 |
-
switch (MATH_BIGINTEGER_MODE) {
|
3317 |
-
case self::MODE_GMP:
|
3318 |
-
gmp_setbit($this->value, 0);
|
3319 |
-
break;
|
3320 |
-
case self::MODE_BCMATH:
|
3321 |
-
if ($this->value[strlen($this->value) - 1] % 2 == 0) {
|
3322 |
-
$this->value = bcadd($this->value, '1');
|
3323 |
-
}
|
3324 |
-
break;
|
3325 |
-
default:
|
3326 |
-
$this->value[0] |= 1;
|
3327 |
-
}
|
3328 |
-
}
|
3329 |
-
|
3330 |
-
/**
|
3331 |
-
* Checks a numer to see if it's prime
|
3332 |
-
*
|
3333 |
-
* Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the
|
3334 |
-
* $t parameter is distributability. BigInteger::randomPrime() can be distributed across multiple pageloads
|
3335 |
-
* on a website instead of just one.
|
3336 |
-
*
|
3337 |
-
* @param \phpseclib\Math\BigInteger $t
|
3338 |
-
* @return bool
|
3339 |
-
* @access public
|
3340 |
-
* @internal Uses the
|
3341 |
-
* {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}. See
|
3342 |
-
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24}.
|
3343 |
-
*/
|
3344 |
-
function isPrime($t = false)
|
3345 |
-
{
|
3346 |
-
$length = strlen($this->toBytes());
|
3347 |
-
|
3348 |
-
if (!$t) {
|
3349 |
-
// see HAC 4.49 "Note (controlling the error probability)"
|
3350 |
-
// @codingStandardsIgnoreStart
|
3351 |
-
if ($length >= 163) { $t = 2; } // floor(1300 / 8)
|
3352 |
-
else if ($length >= 106) { $t = 3; } // floor( 850 / 8)
|
3353 |
-
else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8)
|
3354 |
-
else if ($length >= 68 ) { $t = 5; } // floor( 550 / 8)
|
3355 |
-
else if ($length >= 56 ) { $t = 6; } // floor( 450 / 8)
|
3356 |
-
else if ($length >= 50 ) { $t = 7; } // floor( 400 / 8)
|
3357 |
-
else if ($length >= 43 ) { $t = 8; } // floor( 350 / 8)
|
3358 |
-
else if ($length >= 37 ) { $t = 9; } // floor( 300 / 8)
|
3359 |
-
else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8)
|
3360 |
-
else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8)
|
3361 |
-
else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8)
|
3362 |
-
else { $t = 27; }
|
3363 |
-
// @codingStandardsIgnoreEnd
|
3364 |
-
}
|
3365 |
-
|
3366 |
-
// ie. gmp_testbit($this, 0)
|
3367 |
-
// ie. isEven() or !isOdd()
|
3368 |
-
switch (MATH_BIGINTEGER_MODE) {
|
3369 |
-
case self::MODE_GMP:
|
3370 |
-
return gmp_prob_prime($this->value, $t) != 0;
|
3371 |
-
case self::MODE_BCMATH:
|
3372 |
-
if ($this->value === '2') {
|
3373 |
-
return true;
|
3374 |
-
}
|
3375 |
-
if ($this->value[strlen($this->value) - 1] % 2 == 0) {
|
3376 |
-
return false;
|
3377 |
-
}
|
3378 |
-
break;
|
3379 |
-
default:
|
3380 |
-
if ($this->value == array(2)) {
|
3381 |
-
return true;
|
3382 |
-
}
|
3383 |
-
if (~$this->value[0] & 1) {
|
3384 |
-
return false;
|
3385 |
-
}
|
3386 |
-
}
|
3387 |
-
|
3388 |
-
static $primes, $zero, $one, $two;
|
3389 |
-
|
3390 |
-
if (!isset($primes)) {
|
3391 |
-
$primes = array(
|
3392 |
-
3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59,
|
3393 |
-
61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137,
|
3394 |
-
139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
|
3395 |
-
229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
|
3396 |
-
317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
|
3397 |
-
421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
|
3398 |
-
521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617,
|
3399 |
-
619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727,
|
3400 |
-
733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829,
|
3401 |
-
839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947,
|
3402 |
-
953, 967, 971, 977, 983, 991, 997
|
3403 |
-
);
|
3404 |
-
|
3405 |
-
if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
|
3406 |
-
for ($i = 0; $i < count($primes); ++$i) {
|
3407 |
-
$primes[$i] = new static($primes[$i]);
|
3408 |
-
}
|
3409 |
-
}
|
3410 |
-
|
3411 |
-
$zero = new static();
|
3412 |
-
$one = new static(1);
|
3413 |
-
$two = new static(2);
|
3414 |
-
}
|
3415 |
-
|
3416 |
-
if ($this->equals($one)) {
|
3417 |
-
return false;
|
3418 |
-
}
|
3419 |
-
|
3420 |
-
// see HAC 4.4.1 "Random search for probable primes"
|
3421 |
-
if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
|
3422 |
-
foreach ($primes as $prime) {
|
3423 |
-
list(, $r) = $this->divide($prime);
|
3424 |
-
if ($r->equals($zero)) {
|
3425 |
-
return $this->equals($prime);
|
3426 |
-
}
|
3427 |
-
}
|
3428 |
-
} else {
|
3429 |
-
$value = $this->value;
|
3430 |
-
foreach ($primes as $prime) {
|
3431 |
-
list(, $r) = $this->_divide_digit($value, $prime);
|
3432 |
-
if (!$r) {
|
3433 |
-
return count($value) == 1 && $value[0] == $prime;
|
3434 |
-
}
|
3435 |
-
}
|
3436 |
-
}
|
3437 |
-
|
3438 |
-
$n = $this->copy();
|
3439 |
-
$n_1 = $n->subtract($one);
|
3440 |
-
$n_2 = $n->subtract($two);
|
3441 |
-
|
3442 |
-
$r = $n_1->copy();
|
3443 |
-
$r_value = $r->value;
|
3444 |
-
// ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
|
3445 |
-
if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
|
3446 |
-
$s = 0;
|
3447 |
-
// if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals($one) check earlier
|
3448 |
-
while ($r->value[strlen($r->value) - 1] % 2 == 0) {
|
3449 |
-
$r->value = bcdiv($r->value, '2', 0);
|
3450 |
-
++$s;
|
3451 |
-
}
|
3452 |
-
} else {
|
3453 |
-
for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) {
|
3454 |
-
$temp = ~$r_value[$i] & 0xFFFFFF;
|
3455 |
-
for ($j = 1; ($temp >> $j) & 1; ++$j) {
|
3456 |
-
}
|
3457 |
-
if ($j != 25) {
|
3458 |
-
break;
|
3459 |
-
}
|
3460 |
-
}
|
3461 |
-
$s = 26 * $i + $j;
|
3462 |
-
$r->_rshift($s);
|
3463 |
-
}
|
3464 |
-
|
3465 |
-
for ($i = 0; $i < $t; ++$i) {
|
3466 |
-
$a = $this->random($two, $n_2);
|
3467 |
-
$y = $a->modPow($r, $n);
|
3468 |
-
|
3469 |
-
if (!$y->equals($one) && !$y->equals($n_1)) {
|
3470 |
-
for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) {
|
3471 |
-
$y = $y->modPow($two, $n);
|
3472 |
-
if ($y->equals($one)) {
|
3473 |
-
return false;
|
3474 |
-
}
|
3475 |
-
}
|
3476 |
-
|
3477 |
-
if (!$y->equals($n_1)) {
|
3478 |
-
return false;
|
3479 |
-
}
|
3480 |
-
}
|
3481 |
-
}
|
3482 |
-
return true;
|
3483 |
-
}
|
3484 |
-
|
3485 |
-
/**
|
3486 |
-
* Logical Left Shift
|
3487 |
-
*
|
3488 |
-
* Shifts BigInteger's by $shift bits.
|
3489 |
-
*
|
3490 |
-
* @param int $shift
|
3491 |
-
* @access private
|
3492 |
-
*/
|
3493 |
-
function _lshift($shift)
|
3494 |
-
{
|
3495 |
-
if ($shift == 0) {
|
3496 |
-
return;
|
3497 |
-
}
|
3498 |
-
|
3499 |
-
$num_digits = (int) ($shift / self::$base);
|
3500 |
-
$shift %= self::$base;
|
3501 |
-
$shift = 1 << $shift;
|
3502 |
-
|
3503 |
-
$carry = 0;
|
3504 |
-
|
3505 |
-
for ($i = 0; $i < count($this->value); ++$i) {
|
3506 |
-
$temp = $this->value[$i] * $shift + $carry;
|
3507 |
-
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
|
3508 |
-
$this->value[$i] = (int) ($temp - $carry * self::$baseFull);
|
3509 |
-
}
|
3510 |
-
|
3511 |
-
if ($carry) {
|
3512 |
-
$this->value[count($this->value)] = $carry;
|
3513 |
-
}
|
3514 |
-
|
3515 |
-
while ($num_digits--) {
|
3516 |
-
array_unshift($this->value, 0);
|
3517 |
-
}
|
3518 |
-
}
|
3519 |
-
|
3520 |
-
/**
|
3521 |
-
* Logical Right Shift
|
3522 |
-
*
|
3523 |
-
* Shifts BigInteger's by $shift bits.
|
3524 |
-
*
|
3525 |
-
* @param int $shift
|
3526 |
-
* @access private
|
3527 |
-
*/
|
3528 |
-
function _rshift($shift)
|
3529 |
-
{
|
3530 |
-
if ($shift == 0) {
|
3531 |
-
return;
|
3532 |
-
}
|
3533 |
-
|
3534 |
-
$num_digits = (int) ($shift / self::$base);
|
3535 |
-
$shift %= self::$base;
|
3536 |
-
$carry_shift = self::$base - $shift;
|
3537 |
-
$carry_mask = (1 << $shift) - 1;
|
3538 |
-
|
3539 |
-
if ($num_digits) {
|
3540 |
-
$this->value = array_slice($this->value, $num_digits);
|
3541 |
-
}
|
3542 |
-
|
3543 |
-
$carry = 0;
|
3544 |
-
|
3545 |
-
for ($i = count($this->value) - 1; $i >= 0; --$i) {
|
3546 |
-
$temp = $this->value[$i] >> $shift | $carry;
|
3547 |
-
$carry = ($this->value[$i] & $carry_mask) << $carry_shift;
|
3548 |
-
$this->value[$i] = $temp;
|
3549 |
-
}
|
3550 |
-
|
3551 |
-
$this->value = $this->_trim($this->value);
|
3552 |
-
}
|
3553 |
-
|
3554 |
-
/**
|
3555 |
-
* Normalize
|
3556 |
-
*
|
3557 |
-
* Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
|
3558 |
-
*
|
3559 |
-
* @param \phpseclib\Math\BigInteger
|
3560 |
-
* @return \phpseclib\Math\BigInteger
|
3561 |
-
* @see self::_trim()
|
3562 |
-
* @access private
|
3563 |
-
*/
|
3564 |
-
function _normalize($result)
|
3565 |
-
{
|
3566 |
-
$result->precision = $this->precision;
|
3567 |
-
$result->bitmask = $this->bitmask;
|
3568 |
-
|
3569 |
-
switch (MATH_BIGINTEGER_MODE) {
|
3570 |
-
case self::MODE_GMP:
|
3571 |
-
if ($this->bitmask !== false) {
|
3572 |
-
$result->value = gmp_and($result->value, $result->bitmask->value);
|
3573 |
-
}
|
3574 |
-
|
3575 |
-
return $result;
|
3576 |
-
case self::MODE_BCMATH:
|
3577 |
-
if (!empty($result->bitmask->value)) {
|
3578 |
-
$result->value = bcmod($result->value, $result->bitmask->value);
|
3579 |
-
}
|
3580 |
-
|
3581 |
-
return $result;
|
3582 |
-
}
|
3583 |
-
|
3584 |
-
$value = &$result->value;
|
3585 |
-
|
3586 |
-
if (!count($value)) {
|
3587 |
-
$result->is_negative = false;
|
3588 |
-
return $result;
|
3589 |
-
}
|
3590 |
-
|
3591 |
-
$value = $this->_trim($value);
|
3592 |
-
|
3593 |
-
if (!empty($result->bitmask->value)) {
|
3594 |
-
$length = min(count($value), count($this->bitmask->value));
|
3595 |
-
$value = array_slice($value, 0, $length);
|
3596 |
-
|
3597 |
-
for ($i = 0; $i < $length; ++$i) {
|
3598 |
-
$value[$i] = $value[$i] & $this->bitmask->value[$i];
|
3599 |
-
}
|
3600 |
-
}
|
3601 |
-
|
3602 |
-
return $result;
|
3603 |
-
}
|
3604 |
-
|
3605 |
-
/**
|
3606 |
-
* Trim
|
3607 |
-
*
|
3608 |
-
* Removes leading zeros
|
3609 |
-
*
|
3610 |
-
* @param array $value
|
3611 |
-
* @return \phpseclib\Math\BigInteger
|
3612 |
-
* @access private
|
3613 |
-
*/
|
3614 |
-
function _trim($value)
|
3615 |
-
{
|
3616 |
-
for ($i = count($value) - 1; $i >= 0; --$i) {
|
3617 |
-
if ($value[$i]) {
|
3618 |
-
break;
|
3619 |
-
}
|
3620 |
-
unset($value[$i]);
|
3621 |
-
}
|
3622 |
-
|
3623 |
-
return $value;
|
3624 |
-
}
|
3625 |
-
|
3626 |
-
/**
|
3627 |
-
* Array Repeat
|
3628 |
-
*
|
3629 |
-
* @param $input Array
|
3630 |
-
* @param $multiplier mixed
|
3631 |
-
* @return array
|
3632 |
-
* @access private
|
3633 |
-
*/
|
3634 |
-
function _array_repeat($input, $multiplier)
|
3635 |
-
{
|
3636 |
-
return ($multiplier) ? array_fill(0, $multiplier, $input) : array();
|
3637 |
-
}
|
3638 |
-
|
3639 |
-
/**
|
3640 |
-
* Logical Left Shift
|
3641 |
-
*
|
3642 |
-
* Shifts binary strings $shift bits, essentially multiplying by 2**$shift.
|
3643 |
-
*
|
3644 |
-
* @param $x String
|
3645 |
-
* @param $shift Integer
|
3646 |
-
* @return string
|
3647 |
-
* @access private
|
3648 |
-
*/
|
3649 |
-
function _base256_lshift(&$x, $shift)
|
3650 |
-
{
|
3651 |
-
if ($shift == 0) {
|
3652 |
-
return;
|
3653 |
-
}
|
3654 |
-
|
3655 |
-
$num_bytes = $shift >> 3; // eg. floor($shift/8)
|
3656 |
-
$shift &= 7; // eg. $shift % 8
|
3657 |
-
|
3658 |
-
$carry = 0;
|
3659 |
-
for ($i = strlen($x) - 1; $i >= 0; --$i) {
|
3660 |
-
$temp = ord($x[$i]) << $shift | $carry;
|
3661 |
-
$x[$i] = chr($temp);
|
3662 |
-
$carry = $temp >> 8;
|
3663 |
-
}
|
3664 |
-
$carry = ($carry != 0) ? chr($carry) : '';
|
3665 |
-
$x = $carry . $x . str_repeat(chr(0), $num_bytes);
|
3666 |
-
}
|
3667 |
-
|
3668 |
-
/**
|
3669 |
-
* Logical Right Shift
|
3670 |
-
*
|
3671 |
-
* Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder.
|
3672 |
-
*
|
3673 |
-
* @param $x String
|
3674 |
-
* @param $shift Integer
|
3675 |
-
* @return string
|
3676 |
-
* @access private
|
3677 |
-
*/
|
3678 |
-
function _base256_rshift(&$x, $shift)
|
3679 |
-
{
|
3680 |
-
if ($shift == 0) {
|
3681 |
-
$x = ltrim($x, chr(0));
|
3682 |
-
return '';
|
3683 |
-
}
|
3684 |
-
|
3685 |
-
$num_bytes = $shift >> 3; // eg. floor($shift/8)
|
3686 |
-
$shift &= 7; // eg. $shift % 8
|
3687 |
-
|
3688 |
-
$remainder = '';
|
3689 |
-
if ($num_bytes) {
|
3690 |
-
$start = $num_bytes > strlen($x) ? -strlen($x) : -$num_bytes;
|
3691 |
-
$remainder = substr($x, $start);
|
3692 |
-
$x = substr($x, 0, -$num_bytes);
|
3693 |
-
}
|
3694 |
-
|
3695 |
-
$carry = 0;
|
3696 |
-
$carry_shift = 8 - $shift;
|
3697 |
-
for ($i = 0; $i < strlen($x); ++$i) {
|
3698 |
-
$temp = (ord($x[$i]) >> $shift) | $carry;
|
3699 |
-
$carry = (ord($x[$i]) << $carry_shift) & 0xFF;
|
3700 |
-
$x[$i] = chr($temp);
|
3701 |
-
}
|
3702 |
-
$x = ltrim($x, chr(0));
|
3703 |
-
|
3704 |
-
$remainder = chr($carry >> $carry_shift) . $remainder;
|
3705 |
-
|
3706 |
-
return ltrim($remainder, chr(0));
|
3707 |
-
}
|
3708 |
-
|
3709 |
-
// one quirk about how the following functions are implemented is that PHP defines N to be an unsigned long
|
3710 |
-
// at 32-bits, while java's longs are 64-bits.
|
3711 |
-
|
3712 |
-
/**
|
3713 |
-
* Converts 32-bit integers to bytes.
|
3714 |
-
*
|
3715 |
-
* @param int $x
|
3716 |
-
* @return string
|
3717 |
-
* @access private
|
3718 |
-
*/
|
3719 |
-
function _int2bytes($x)
|
3720 |
-
{
|
3721 |
-
return ltrim(pack('N', $x), chr(0));
|
3722 |
-
}
|
3723 |
-
|
3724 |
-
/**
|
3725 |
-
* Converts bytes to 32-bit integers
|
3726 |
-
*
|
3727 |
-
* @param string $x
|
3728 |
-
* @return int
|
3729 |
-
* @access private
|
3730 |
-
*/
|
3731 |
-
function _bytes2int($x)
|
3732 |
-
{
|
3733 |
-
$temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT));
|
3734 |
-
return $temp['int'];
|
3735 |
-
}
|
3736 |
-
|
3737 |
-
/**
|
3738 |
-
* DER-encode an integer
|
3739 |
-
*
|
3740 |
-
* The ability to DER-encode integers is needed to create RSA public keys for use with OpenSSL
|
3741 |
-
*
|
3742 |
-
* @see self::modPow()
|
3743 |
-
* @access private
|
3744 |
-
* @param int $length
|
3745 |
-
* @return string
|
3746 |
-
*/
|
3747 |
-
function _encodeASN1Length($length)
|
3748 |
-
{
|
3749 |
-
if ($length <= 0x7F) {
|
3750 |
-
return chr($length);
|
3751 |
-
}
|
3752 |
-
|
3753 |
-
$temp = ltrim(pack('N', $length), chr(0));
|
3754 |
-
return pack('Ca*', 0x80 | strlen($temp), $temp);
|
3755 |
-
}
|
3756 |
-
|
3757 |
-
/**
|
3758 |
-
* Single digit division
|
3759 |
-
*
|
3760 |
-
* Even if int64 is being used the division operator will return a float64 value
|
3761 |
-
* if the dividend is not evenly divisible by the divisor. Since a float64 doesn't
|
3762 |
-
* have the precision of int64 this is a problem so, when int64 is being used,
|
3763 |
-
* we'll guarantee that the dividend is divisible by first subtracting the remainder.
|
3764 |
-
*
|
3765 |
-
* @access private
|
3766 |
-
* @param int $x
|
3767 |
-
* @param int $y
|
3768 |
-
* @return int
|
3769 |
-
*/
|
3770 |
-
function _safe_divide($x, $y)
|
3771 |
-
{
|
3772 |
-
if (self::$base === 26) {
|
3773 |
-
return (int) ($x / $y);
|
3774 |
-
}
|
3775 |
-
|
3776 |
-
// self::$base === 31
|
3777 |
-
return ($x - ($x % $y)) / $y;
|
3778 |
-
}
|
3779 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Pure-PHP arbitrary precision integer arithmetic library.
|
5 |
+
*
|
6 |
+
* Supports base-2, base-10, base-16, and base-256 numbers. Uses the GMP or BCMath extensions, if available,
|
7 |
+
* and an internal implementation, otherwise.
|
8 |
+
*
|
9 |
+
* PHP version 5
|
10 |
+
*
|
11 |
+
* {@internal (all DocBlock comments regarding implementation - such as the one that follows - refer to the
|
12 |
+
* {@link self::MODE_INTERNAL self::MODE_INTERNAL} mode)
|
13 |
+
*
|
14 |
+
* BigInteger uses base-2**26 to perform operations such as multiplication and division and
|
15 |
+
* base-2**52 (ie. two base 2**26 digits) to perform addition and subtraction. Because the largest possible
|
16 |
+
* value when multiplying two base-2**26 numbers together is a base-2**52 number, double precision floating
|
17 |
+
* point numbers - numbers that should be supported on most hardware and whose significand is 53 bits - are
|
18 |
+
* used. As a consequence, bitwise operators such as >> and << cannot be used, nor can the modulo operator %,
|
19 |
+
* which only supports integers. Although this fact will slow this library down, the fact that such a high
|
20 |
+
* base is being used should more than compensate.
|
21 |
+
*
|
22 |
+
* Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness little endian} format. ie.
|
23 |
+
* (new \phpseclib\Math\BigInteger(pow(2, 26)))->value = array(0, 1)
|
24 |
+
*
|
25 |
+
* Useful resources are as follows:
|
26 |
+
*
|
27 |
+
* - {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf Handbook of Applied Cryptography (HAC)}
|
28 |
+
* - {@link http://math.libtomcrypt.com/files/tommath.pdf Multi-Precision Math (MPM)}
|
29 |
+
* - Java's BigInteger classes. See /j2se/src/share/classes/java/math in jdk-1_5_0-src-jrl.zip
|
30 |
+
*
|
31 |
+
* Here's an example of how to use this library:
|
32 |
+
* <code>
|
33 |
+
* <?php
|
34 |
+
* $a = new \phpseclib\Math\BigInteger(2);
|
35 |
+
* $b = new \phpseclib\Math\BigInteger(3);
|
36 |
+
*
|
37 |
+
* $c = $a->add($b);
|
38 |
+
*
|
39 |
+
* echo $c->toString(); // outputs 5
|
40 |
+
* ?>
|
41 |
+
* </code>
|
42 |
+
*
|
43 |
+
* @category Math
|
44 |
+
* @package BigInteger
|
45 |
+
* @author Jim Wigginton <terrafrost@php.net>
|
46 |
+
* @copyright 2006 Jim Wigginton
|
47 |
+
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
48 |
+
*/
|
49 |
+
|
50 |
+
namespace phpseclib\Math;
|
51 |
+
|
52 |
+
use phpseclib\Crypt\Random;
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256
|
56 |
+
* numbers.
|
57 |
+
*
|
58 |
+
* @package BigInteger
|
59 |
+
* @author Jim Wigginton <terrafrost@php.net>
|
60 |
+
* @access public
|
61 |
+
*/
|
62 |
+
class BigInteger
|
63 |
+
{
|
64 |
+
/**#@+
|
65 |
+
* Reduction constants
|
66 |
+
*
|
67 |
+
* @access private
|
68 |
+
* @see BigInteger::_reduce()
|
69 |
+
*/
|
70 |
+
/**
|
71 |
+
* @see BigInteger::_montgomery()
|
72 |
+
* @see BigInteger::_prepMontgomery()
|
73 |
+
*/
|
74 |
+
const MONTGOMERY = 0;
|
75 |
+
/**
|
76 |
+
* @see BigInteger::_barrett()
|
77 |
+
*/
|
78 |
+
const BARRETT = 1;
|
79 |
+
/**
|
80 |
+
* @see BigInteger::_mod2()
|
81 |
+
*/
|
82 |
+
const POWEROF2 = 2;
|
83 |
+
/**
|
84 |
+
* @see BigInteger::_remainder()
|
85 |
+
*/
|
86 |
+
const CLASSIC = 3;
|
87 |
+
/**
|
88 |
+
* @see BigInteger::__clone()
|
89 |
+
*/
|
90 |
+
const NONE = 4;
|
91 |
+
/**#@-*/
|
92 |
+
|
93 |
+
/**#@+
|
94 |
+
* Array constants
|
95 |
+
*
|
96 |
+
* Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and
|
97 |
+
* multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them.
|
98 |
+
*
|
99 |
+
* @access private
|
100 |
+
*/
|
101 |
+
/**
|
102 |
+
* $result[self::VALUE] contains the value.
|
103 |
+
*/
|
104 |
+
const VALUE = 0;
|
105 |
+
/**
|
106 |
+
* $result[self::SIGN] contains the sign.
|
107 |
+
*/
|
108 |
+
const SIGN = 1;
|
109 |
+
/**#@-*/
|
110 |
+
|
111 |
+
/**#@+
|
112 |
+
* @access private
|
113 |
+
* @see BigInteger::_montgomery()
|
114 |
+
* @see BigInteger::_barrett()
|
115 |
+
*/
|
116 |
+
/**
|
117 |
+
* Cache constants
|
118 |
+
*
|
119 |
+
* $cache[self::VARIABLE] tells us whether or not the cached data is still valid.
|
120 |
+
*/
|
121 |
+
const VARIABLE = 0;
|
122 |
+
/**
|
123 |
+
* $cache[self::DATA] contains the cached data.
|
124 |
+
*/
|
125 |
+
const DATA = 1;
|
126 |
+
/**#@-*/
|
127 |
+
|
128 |
+
/**#@+
|
129 |
+
* Mode constants.
|
130 |
+
*
|
131 |
+
* @access private
|
132 |
+
* @see BigInteger::__construct()
|
133 |
+
*/
|
134 |
+
/**
|
135 |
+
* To use the pure-PHP implementation
|
136 |
+
*/
|
137 |
+
const MODE_INTERNAL = 1;
|
138 |
+
/**
|
139 |
+
* To use the BCMath library
|
140 |
+
*
|
141 |
+
* (if enabled; otherwise, the internal implementation will be used)
|
142 |
+
*/
|
143 |
+
const MODE_BCMATH = 2;
|
144 |
+
/**
|
145 |
+
* To use the GMP library
|
146 |
+
*
|
147 |
+
* (if present; otherwise, either the BCMath or the internal implementation will be used)
|
148 |
+
*/
|
149 |
+
const MODE_GMP = 3;
|
150 |
+
/**#@-*/
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Karatsuba Cutoff
|
154 |
+
*
|
155 |
+
* At what point do we switch between Karatsuba multiplication and schoolbook long multiplication?
|
156 |
+
*
|
157 |
+
* @access private
|
158 |
+
*/
|
159 |
+
const KARATSUBA_CUTOFF = 25;
|
160 |
+
|
161 |
+
/**#@+
|
162 |
+
* Static properties used by the pure-PHP implementation.
|
163 |
+
*
|
164 |
+
* @see __construct()
|
165 |
+
*/
|
166 |
+
protected static $base;
|
167 |
+
protected static $baseFull;
|
168 |
+
protected static $maxDigit;
|
169 |
+
protected static $msb;
|
170 |
+
|
171 |
+
/**
|
172 |
+
* $max10 in greatest $max10Len satisfying
|
173 |
+
* $max10 = 10**$max10Len <= 2**$base.
|
174 |
+
*/
|
175 |
+
protected static $max10;
|
176 |
+
|
177 |
+
/**
|
178 |
+
* $max10Len in greatest $max10Len satisfying
|
179 |
+
* $max10 = 10**$max10Len <= 2**$base.
|
180 |
+
*/
|
181 |
+
protected static $max10Len;
|
182 |
+
protected static $maxDigit2;
|
183 |
+
/**#@-*/
|
184 |
+
|
185 |
+
/**
|
186 |
+
* Holds the BigInteger's value.
|
187 |
+
*
|
188 |
+
* @var array
|
189 |
+
* @access private
|
190 |
+
*/
|
191 |
+
var $value;
|
192 |
+
|
193 |
+
/**
|
194 |
+
* Holds the BigInteger's magnitude.
|
195 |
+
*
|
196 |
+
* @var bool
|
197 |
+
* @access private
|
198 |
+
*/
|
199 |
+
var $is_negative = false;
|
200 |
+
|
201 |
+
/**
|
202 |
+
* Precision
|
203 |
+
*
|
204 |
+
* @see self::setPrecision()
|
205 |
+
* @access private
|
206 |
+
*/
|
207 |
+
var $precision = -1;
|
208 |
+
|
209 |
+
/**
|
210 |
+
* Precision Bitmask
|
211 |
+
*
|
212 |
+
* @see self::setPrecision()
|
213 |
+
* @access private
|
214 |
+
*/
|
215 |
+
var $bitmask = false;
|
216 |
+
|
217 |
+
/**
|
218 |
+
* Mode independent value used for serialization.
|
219 |
+
*
|
220 |
+
* If the bcmath or gmp extensions are installed $this->value will be a non-serializable resource, hence the need for
|
221 |
+
* a variable that'll be serializable regardless of whether or not extensions are being used. Unlike $this->value,
|
222 |
+
* however, $this->hex is only calculated when $this->__sleep() is called.
|
223 |
+
*
|
224 |
+
* @see self::__sleep()
|
225 |
+
* @see self::__wakeup()
|
226 |
+
* @var string
|
227 |
+
* @access private
|
228 |
+
*/
|
229 |
+
var $hex;
|
230 |
+
|
231 |
+
/**
|
232 |
+
* Converts base-2, base-10, base-16, and binary strings (base-256) to BigIntegers.
|
233 |
+
*
|
234 |
+
* If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using
|
235 |
+
* two's compliment. The sole exception to this is -10, which is treated the same as 10 is.
|
236 |
+
*
|
237 |
+
* Here's an example:
|
238 |
+
* <code>
|
239 |
+
* <?php
|
240 |
+
* $a = new \phpseclib\Math\BigInteger('0x32', 16); // 50 in base-16
|
241 |
+
*
|
242 |
+
* echo $a->toString(); // outputs 50
|
243 |
+
* ?>
|
244 |
+
* </code>
|
245 |
+
*
|
246 |
+
* @param $x base-10 number or base-$base number if $base set.
|
247 |
+
* @param int $base
|
248 |
+
* @return \phpseclib\Math\BigInteger
|
249 |
+
* @access public
|
250 |
+
*/
|
251 |
+
function __construct($x = 0, $base = 10)
|
252 |
+
{
|
253 |
+
if (!defined('MATH_BIGINTEGER_MODE')) {
|
254 |
+
switch (true) {
|
255 |
+
case extension_loaded('gmp'):
|
256 |
+
define('MATH_BIGINTEGER_MODE', self::MODE_GMP);
|
257 |
+
break;
|
258 |
+
case extension_loaded('bcmath'):
|
259 |
+
define('MATH_BIGINTEGER_MODE', self::MODE_BCMATH);
|
260 |
+
break;
|
261 |
+
default:
|
262 |
+
define('MATH_BIGINTEGER_MODE', self::MODE_INTERNAL);
|
263 |
+
}
|
264 |
+
}
|
265 |
+
|
266 |
+
if (extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
|
267 |
+
// some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
|
268 |
+
$versions = array();
|
269 |
+
|
270 |
+
// avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems)
|
271 |
+
if (strpos(ini_get('disable_functions'), 'phpinfo') === false) {
|
272 |
+
ob_start();
|
273 |
+
@phpinfo();
|
274 |
+
$content = ob_get_contents();
|
275 |
+
ob_end_clean();
|
276 |
+
|
277 |
+
preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
|
278 |
+
|
279 |
+
if (!empty($matches[1])) {
|
280 |
+
for ($i = 0; $i < count($matches[1]); $i++) {
|
281 |
+
$fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
|
282 |
+
|
283 |
+
// Remove letter part in OpenSSL version
|
284 |
+
if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
|
285 |
+
$versions[$matches[1][$i]] = $fullVersion;
|
286 |
+
} else {
|
287 |
+
$versions[$matches[1][$i]] = $m[0];
|
288 |
+
}
|
289 |
+
}
|
290 |
+
}
|
291 |
+
}
|
292 |
+
|
293 |
+
// it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
|
294 |
+
switch (true) {
|
295 |
+
case !isset($versions['Header']):
|
296 |
+
case !isset($versions['Library']):
|
297 |
+
case $versions['Header'] == $versions['Library']:
|
298 |
+
case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0:
|
299 |
+
define('MATH_BIGINTEGER_OPENSSL_ENABLED', true);
|
300 |
+
break;
|
301 |
+
default:
|
302 |
+
define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
|
303 |
+
}
|
304 |
+
}
|
305 |
+
|
306 |
+
if (!defined('PHP_INT_SIZE')) {
|
307 |
+
define('PHP_INT_SIZE', 4);
|
308 |
+
}
|
309 |
+
|
310 |
+
if (empty(self::$base) && MATH_BIGINTEGER_MODE == self::MODE_INTERNAL) {
|
311 |
+
switch (PHP_INT_SIZE) {
|
312 |
+
case 8: // use 64-bit integers if int size is 8 bytes
|
313 |
+
self::$base = 31;
|
314 |
+
self::$baseFull = 0x80000000;
|
315 |
+
self::$maxDigit = 0x7FFFFFFF;
|
316 |
+
self::$msb = 0x40000000;
|
317 |
+
self::$max10 = 1000000000;
|
318 |
+
self::$max10Len = 9;
|
319 |
+
self::$maxDigit2 = pow(2, 62);
|
320 |
+
break;
|
321 |
+
//case 4: // use 64-bit floats if int size is 4 bytes
|
322 |
+
default:
|
323 |
+
self::$base = 26;
|
324 |
+
self::$baseFull = 0x4000000;
|
325 |
+
self::$maxDigit = 0x3FFFFFF;
|
326 |
+
self::$msb = 0x2000000;
|
327 |
+
self::$max10 = 10000000;
|
328 |
+
self::$max10Len = 7;
|
329 |
+
self::$maxDigit2 = pow(2, 52); // pow() prevents truncation
|
330 |
+
}
|
331 |
+
}
|
332 |
+
|
333 |
+
switch (MATH_BIGINTEGER_MODE) {
|
334 |
+
case self::MODE_GMP:
|
335 |
+
switch (true) {
|
336 |
+
case is_resource($x) && get_resource_type($x) == 'GMP integer':
|
337 |
+
// PHP 5.6 switched GMP from using resources to objects
|
338 |
+
case $x instanceof \GMP:
|
339 |
+
$this->value = $x;
|
340 |
+
return;
|
341 |
+
}
|
342 |
+
$this->value = gmp_init(0);
|
343 |
+
break;
|
344 |
+
case self::MODE_BCMATH:
|
345 |
+
$this->value = '0';
|
346 |
+
break;
|
347 |
+
default:
|
348 |
+
$this->value = array();
|
349 |
+
}
|
350 |
+
|
351 |
+
// '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48
|
352 |
+
// '0' is the only value like this per http://php.net/empty
|
353 |
+
if (empty($x) && (abs($base) != 256 || $x !== '0')) {
|
354 |
+
return;
|
355 |
+
}
|
356 |
+
|
357 |
+
switch ($base) {
|
358 |
+
case -256:
|
359 |
+
if (ord($x[0]) & 0x80) {
|
360 |
+
$x = ~$x;
|
361 |
+
$this->is_negative = true;
|
362 |
+
}
|
363 |
+
case 256:
|
364 |
+
switch (MATH_BIGINTEGER_MODE) {
|
365 |
+
case self::MODE_GMP:
|
366 |
+
$this->value = function_exists('gmp_import') ?
|
367 |
+
gmp_import($x) :
|
368 |
+
gmp_init('0x' . bin2hex($x));
|
369 |
+
if ($this->is_negative) {
|
370 |
+
$this->value = gmp_neg($this->value);
|
371 |
+
}
|
372 |
+
break;
|
373 |
+
case self::MODE_BCMATH:
|
374 |
+
// round $len to the nearest 4 (thanks, DavidMJ!)
|
375 |
+
$len = (strlen($x) + 3) & 0xFFFFFFFC;
|
376 |
+
|
377 |
+
$x = str_pad($x, $len, chr(0), STR_PAD_LEFT);
|
378 |
+
|
379 |
+
for ($i = 0; $i < $len; $i+= 4) {
|
380 |
+
$this->value = bcmul($this->value, '4294967296', 0); // 4294967296 == 2**32
|
381 |
+
$this->value = bcadd($this->value, 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2]) << 8) | ord($x[$i + 3])), 0);
|
382 |
+
}
|
383 |
+
|
384 |
+
if ($this->is_negative) {
|
385 |
+
$this->value = '-' . $this->value;
|
386 |
+
}
|
387 |
+
|
388 |
+
break;
|
389 |
+
// converts a base-2**8 (big endian / msb) number to base-2**26 (little endian / lsb)
|
390 |
+
default:
|
391 |
+
while (strlen($x)) {
|
392 |
+
$this->value[] = $this->_bytes2int($this->_base256_rshift($x, self::$base));
|
393 |
+
}
|
394 |
+
}
|
395 |
+
|
396 |
+
if ($this->is_negative) {
|
397 |
+
if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
|
398 |
+
$this->is_negative = false;
|
399 |
+
}
|
400 |
+
$temp = $this->add(new static('-1'));
|
401 |
+
$this->value = $temp->value;
|
402 |
+
}
|
403 |
+
break;
|
404 |
+
case 16:
|
405 |
+
case -16:
|
406 |
+
if ($base > 0 && $x[0] == '-') {
|
407 |
+
$this->is_negative = true;
|
408 |
+
$x = substr($x, 1);
|
409 |
+
}
|
410 |
+
|
411 |
+
$x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#', '$1', $x);
|
412 |
+
|
413 |
+
$is_negative = false;
|
414 |
+
if ($base < 0 && hexdec($x[0]) >= 8) {
|
415 |
+
$this->is_negative = $is_negative = true;
|
416 |
+
$x = bin2hex(~pack('H*', $x));
|
417 |
+
}
|
418 |
+
|
419 |
+
switch (MATH_BIGINTEGER_MODE) {
|
420 |
+
case self::MODE_GMP:
|
421 |
+
$temp = $this->is_negative ? '-0x' . $x : '0x' . $x;
|
422 |
+
$this->value = gmp_init($temp);
|
423 |
+
$this->is_negative = false;
|
424 |
+
break;
|
425 |
+
case self::MODE_BCMATH:
|
426 |
+
$x = (strlen($x) & 1) ? '0' . $x : $x;
|
427 |
+
$temp = new static(pack('H*', $x), 256);
|
428 |
+
$this->value = $this->is_negative ? '-' . $temp->value : $temp->value;
|
429 |
+
$this->is_negative = false;
|
430 |
+
break;
|
431 |
+
default:
|
432 |
+
$x = (strlen($x) & 1) ? '0' . $x : $x;
|
433 |
+
$temp = new static(pack('H*', $x), 256);
|
434 |
+
$this->value = $temp->value;
|
435 |
+
}
|
436 |
+
|
437 |
+
if ($is_negative) {
|
438 |
+
$temp = $this->add(new static('-1'));
|
439 |
+
$this->value = $temp->value;
|
440 |
+
}
|
441 |
+
break;
|
442 |
+
case 10:
|
443 |
+
case -10:
|
444 |
+
// (?<!^)(?:-).*: find any -'s that aren't at the beginning and then any characters that follow that
|
445 |
+
// (?<=^|-)0*: find any 0's that are preceded by the start of the string or by a - (ie. octals)
|
446 |
+
// [^-0-9].*: find any non-numeric characters and then any characters that follow that
|
447 |
+
$x = preg_replace('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#', '', $x);
|
448 |
+
if (!strlen($x) || $x == '-') {
|
449 |
+
$x = '0';
|
450 |
+
}
|
451 |
+
|
452 |
+
switch (MATH_BIGINTEGER_MODE) {
|
453 |
+
case self::MODE_GMP:
|
454 |
+
$this->value = gmp_init($x);
|
455 |
+
break;
|
456 |
+
case self::MODE_BCMATH:
|
457 |
+
// explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different
|
458 |
+
// results then doing it on '-1' does (modInverse does $x[0])
|
459 |
+
$this->value = $x === '-' ? '0' : (string) $x;
|
460 |
+
break;
|
461 |
+
default:
|
462 |
+
$temp = new static();
|
463 |
+
|
464 |
+
$multiplier = new static();
|
465 |
+
$multiplier->value = array(self::$max10);
|
466 |
+
|
467 |
+
if ($x[0] == '-') {
|
468 |
+
$this->is_negative = true;
|
469 |
+
$x = substr($x, 1);
|
470 |
+
}
|
471 |
+
|
472 |
+
$x = str_pad($x, strlen($x) + ((self::$max10Len - 1) * strlen($x)) % self::$max10Len, 0, STR_PAD_LEFT);
|
473 |
+
while (strlen($x)) {
|
474 |
+
$temp = $temp->multiply($multiplier);
|
475 |
+
$temp = $temp->add(new static($this->_int2bytes(substr($x, 0, self::$max10Len)), 256));
|
476 |
+
$x = substr($x, self::$max10Len);
|
477 |
+
}
|
478 |
+
|
479 |
+
$this->value = $temp->value;
|
480 |
+
}
|
481 |
+
break;
|
482 |
+
case 2: // base-2 support originally implemented by Lluis Pamies - thanks!
|
483 |
+
case -2:
|
484 |
+
if ($base > 0 && $x[0] == '-') {
|
485 |
+
$this->is_negative = true;
|
486 |
+
$x = substr($x, 1);
|
487 |
+
}
|
488 |
+
|
489 |
+
$x = preg_replace('#^([01]*).*#', '$1', $x);
|
490 |
+
$x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT);
|
491 |
+
|
492 |
+
$str = '0x';
|
493 |
+
while (strlen($x)) {
|
494 |
+
$part = substr($x, 0, 4);
|
495 |
+
$str.= dechex(bindec($part));
|
496 |
+
$x = substr($x, 4);
|
497 |
+
}
|
498 |
+
|
499 |
+
if ($this->is_negative) {
|
500 |
+
$str = '-' . $str;
|
501 |
+
}
|
502 |
+
|
503 |
+
$temp = new static($str, 8 * $base); // ie. either -16 or +16
|
504 |
+
$this->value = $temp->value;
|
505 |
+
$this->is_negative = $temp->is_negative;
|
506 |
+
|
507 |
+
break;
|
508 |
+
default:
|
509 |
+
// base not supported, so we'll let $this == 0
|
510 |
+
}
|
511 |
+
}
|
512 |
+
|
513 |
+
/**
|
514 |
+
* Converts a BigInteger to a byte string (eg. base-256).
|
515 |
+
*
|
516 |
+
* Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
|
517 |
+
* saved as two's compliment.
|
518 |
+
*
|
519 |
+
* Here's an example:
|
520 |
+
* <code>
|
521 |
+
* <?php
|
522 |
+
* $a = new \phpseclib\Math\BigInteger('65');
|
523 |
+
*
|
524 |
+
* echo $a->toBytes(); // outputs chr(65)
|
525 |
+
* ?>
|
526 |
+
* </code>
|
527 |
+
*
|
528 |
+
* @param bool $twos_compliment
|
529 |
+
* @return string
|
530 |
+
* @access public
|
531 |
+
* @internal Converts a base-2**26 number to base-2**8
|
532 |
+
*/
|
533 |
+
function toBytes($twos_compliment = false)
|
534 |
+
{
|
535 |
+
if ($twos_compliment) {
|
536 |
+
$comparison = $this->compare(new static());
|
537 |
+
if ($comparison == 0) {
|
538 |
+
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
|
539 |
+
}
|
540 |
+
|
541 |
+
$temp = $comparison < 0 ? $this->add(new static(1)) : $this->copy();
|
542 |
+
$bytes = $temp->toBytes();
|
543 |
+
|
544 |
+
if (!strlen($bytes)) { // eg. if the number we're trying to convert is -1
|
545 |
+
$bytes = chr(0);
|
546 |
+
}
|
547 |
+
|
548 |
+
if (ord($bytes[0]) & 0x80) {
|
549 |
+
$bytes = chr(0) . $bytes;
|
550 |
+
}
|
551 |
+
|
552 |
+
return $comparison < 0 ? ~$bytes : $bytes;
|
553 |
+
}
|
554 |
+
|
555 |
+
switch (MATH_BIGINTEGER_MODE) {
|
556 |
+
case self::MODE_GMP:
|
557 |
+
if (gmp_cmp($this->value, gmp_init(0)) == 0) {
|
558 |
+
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
|
559 |
+
}
|
560 |
+
|
561 |
+
if (function_exists('gmp_export')) {
|
562 |
+
$temp = gmp_export($this->value);
|
563 |
+
} else {
|
564 |
+
$temp = gmp_strval(gmp_abs($this->value), 16);
|
565 |
+
$temp = (strlen($temp) & 1) ? '0' . $temp : $temp;
|
566 |
+
$temp = pack('H*', $temp);
|
567 |
+
}
|
568 |
+
|
569 |
+
return $this->precision > 0 ?
|
570 |
+
substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
|
571 |
+
ltrim($temp, chr(0));
|
572 |
+
case self::MODE_BCMATH:
|
573 |
+
if ($this->value === '0') {
|
574 |
+
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
|
575 |
+
}
|
576 |
+
|
577 |
+
$value = '';
|
578 |
+
$current = $this->value;
|
579 |
+
|
580 |
+
if ($current[0] == '-') {
|
581 |
+
$current = substr($current, 1);
|
582 |
+
}
|
583 |
+
|
584 |
+
while (bccomp($current, '0', 0) > 0) {
|
585 |
+
$temp = bcmod($current, '16777216');
|
586 |
+
$value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value;
|
587 |
+
$current = bcdiv($current, '16777216', 0);
|
588 |
+
}
|
589 |
+
|
590 |
+
return $this->precision > 0 ?
|
591 |
+
substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
|
592 |
+
ltrim($value, chr(0));
|
593 |
+
}
|
594 |
+
|
595 |
+
if (!count($this->value)) {
|
596 |
+
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
|
597 |
+
}
|
598 |
+
$result = $this->_int2bytes($this->value[count($this->value) - 1]);
|
599 |
+
|
600 |
+
$temp = $this->copy();
|
601 |
+
|
602 |
+
for ($i = count($temp->value) - 2; $i >= 0; --$i) {
|
603 |
+
$temp->_base256_lshift($result, self::$base);
|
604 |
+
$result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT);
|
605 |
+
}
|
606 |
+
|
607 |
+
return $this->precision > 0 ?
|
608 |
+
str_pad(substr($result, -(($this->precision + 7) >> 3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) :
|
609 |
+
$result;
|
610 |
+
}
|
611 |
+
|
612 |
+
/**
|
613 |
+
* Converts a BigInteger to a hex string (eg. base-16)).
|
614 |
+
*
|
615 |
+
* Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
|
616 |
+
* saved as two's compliment.
|
617 |
+
*
|
618 |
+
* Here's an example:
|
619 |
+
* <code>
|
620 |
+
* <?php
|
621 |
+
* $a = new \phpseclib\Math\BigInteger('65');
|
622 |
+
*
|
623 |
+
* echo $a->toHex(); // outputs '41'
|
624 |
+
* ?>
|
625 |
+
* </code>
|
626 |
+
*
|
627 |
+
* @param bool $twos_compliment
|
628 |
+
* @return string
|
629 |
+
* @access public
|
630 |
+
* @internal Converts a base-2**26 number to base-2**8
|
631 |
+
*/
|
632 |
+
function toHex($twos_compliment = false)
|
633 |
+
{
|
634 |
+
return bin2hex($this->toBytes($twos_compliment));
|
635 |
+
}
|
636 |
+
|
637 |
+
/**
|
638 |
+
* Converts a BigInteger to a bit string (eg. base-2).
|
639 |
+
*
|
640 |
+
* Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
|
641 |
+
* saved as two's compliment.
|
642 |
+
*
|
643 |
+
* Here's an example:
|
644 |
+
* <code>
|
645 |
+
* <?php
|
646 |
+
* $a = new \phpseclib\Math\BigInteger('65');
|
647 |
+
*
|
648 |
+
* echo $a->toBits(); // outputs '1000001'
|
649 |
+
* ?>
|
650 |
+
* </code>
|
651 |
+
*
|
652 |
+
* @param bool $twos_compliment
|
653 |
+
* @return string
|
654 |
+
* @access public
|
655 |
+
* @internal Converts a base-2**26 number to base-2**2
|
656 |
+
*/
|
657 |
+
function toBits($twos_compliment = false)
|
658 |
+
{
|
659 |
+
$hex = $this->toHex($twos_compliment);
|
660 |
+
$bits = '';
|
661 |
+
for ($i = strlen($hex) - 8, $start = strlen($hex) & 7; $i >= $start; $i-=8) {
|
662 |
+
$bits = str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT) . $bits;
|
663 |
+
}
|
664 |
+
if ($start) { // hexdec('') == 0
|
665 |
+
$bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8, '0', STR_PAD_LEFT) . $bits;
|
666 |
+
}
|
667 |
+
$result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');
|
668 |
+
|
669 |
+
if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) {
|
670 |
+
return '0' . $result;
|
671 |
+
}
|
672 |
+
|
673 |
+
return $result;
|
674 |
+
}
|
675 |
+
|
676 |
+
/**
|
677 |
+
* Converts a BigInteger to a base-10 number.
|
678 |
+
*
|
679 |
+
* Here's an example:
|
680 |
+
* <code>
|
681 |
+
* <?php
|
682 |
+
* $a = new \phpseclib\Math\BigInteger('50');
|
683 |
+
*
|
684 |
+
* echo $a->toString(); // outputs 50
|
685 |
+
* ?>
|
686 |
+
* </code>
|
687 |
+
*
|
688 |
+
* @return string
|
689 |
+
* @access public
|
690 |
+
* @internal Converts a base-2**26 number to base-10**7 (which is pretty much base-10)
|
691 |
+
*/
|
692 |
+
function toString()
|
693 |
+
{
|
694 |
+
switch (MATH_BIGINTEGER_MODE) {
|
695 |
+
case self::MODE_GMP:
|
696 |
+
return gmp_strval($this->value);
|
697 |
+
case self::MODE_BCMATH:
|
698 |
+
if ($this->value === '0') {
|
699 |
+
return '0';
|
700 |
+
}
|
701 |
+
|
702 |
+
return ltrim($this->value, '0');
|
703 |
+
}
|
704 |
+
|
705 |
+
if (!count($this->value)) {
|
706 |
+
return '0';
|
707 |
+
}
|
708 |
+
|
709 |
+
$temp = $this->copy();
|
710 |
+
$temp->is_negative = false;
|
711 |
+
|
712 |
+
$divisor = new static();
|
713 |
+
$divisor->value = array(self::$max10);
|
714 |
+
$result = '';
|
715 |
+
while (count($temp->value)) {
|
716 |
+
list($temp, $mod) = $temp->divide($divisor);
|
717 |
+
$result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', self::$max10Len, '0', STR_PAD_LEFT) . $result;
|
718 |
+
}
|
719 |
+
$result = ltrim($result, '0');
|
720 |
+
if (empty($result)) {
|
721 |
+
$result = '0';
|
722 |
+
}
|
723 |
+
|
724 |
+
if ($this->is_negative) {
|
725 |
+
$result = '-' . $result;
|
726 |
+
}
|
727 |
+
|
728 |
+
return $result;
|
729 |
+
}
|
730 |
+
|
731 |
+
/**
|
732 |
+
* Copy an object
|
733 |
+
*
|
734 |
+
* PHP5 passes objects by reference while PHP4 passes by value. As such, we need a function to guarantee
|
735 |
+
* that all objects are passed by value, when appropriate. More information can be found here:
|
736 |
+
*
|
737 |
+
* {@link http://php.net/language.oop5.basic#51624}
|
738 |
+
*
|
739 |
+
* @access public
|
740 |
+
* @see self::__clone()
|
741 |
+
* @return \phpseclib\Math\BigInteger
|
742 |
+
*/
|
743 |
+
function copy()
|
744 |
+
{
|
745 |
+
$temp = new static();
|
746 |
+
$temp->value = $this->value;
|
747 |
+
$temp->is_negative = $this->is_negative;
|
748 |
+
$temp->precision = $this->precision;
|
749 |
+
$temp->bitmask = $this->bitmask;
|
750 |
+
return $temp;
|
751 |
+
}
|
752 |
+
|
753 |
+
/**
|
754 |
+
* __toString() magic method
|
755 |
+
*
|
756 |
+
* Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
|
757 |
+
* toString().
|
758 |
+
*
|
759 |
+
* @access public
|
760 |
+
* @internal Implemented per a suggestion by Techie-Michael - thanks!
|
761 |
+
*/
|
762 |
+
function __toString()
|
763 |
+
{
|
764 |
+
return $this->toString();
|
765 |
+
}
|
766 |
+
|
767 |
+
/**
|
768 |
+
* __clone() magic method
|
769 |
+
*
|
770 |
+
* Although you can call BigInteger::__toString() directly in PHP5, you cannot call BigInteger::__clone() directly
|
771 |
+
* in PHP5. You can in PHP4 since it's not a magic method, but in PHP5, you have to call it by using the PHP5
|
772 |
+
* only syntax of $y = clone $x. As such, if you're trying to write an application that works on both PHP4 and
|
773 |
+
* PHP5, call BigInteger::copy(), instead.
|
774 |
+
*
|
775 |
+
* @access public
|
776 |
+
* @see self::copy()
|
777 |
+
* @return \phpseclib\Math\BigInteger
|
778 |
+
*/
|
779 |
+
function __clone()
|
780 |
+
{
|
781 |
+
return $this->copy();
|
782 |
+
}
|
783 |
+
|
784 |
+
/**
|
785 |
+
* __sleep() magic method
|
786 |
+
*
|
787 |
+
* Will be called, automatically, when serialize() is called on a BigInteger object.
|
788 |
+
*
|
789 |
+
* @see self::__wakeup()
|
790 |
+
* @access public
|
791 |
+
*/
|
792 |
+
function __sleep()
|
793 |
+
{
|
794 |
+
$this->hex = $this->toHex(true);
|
795 |
+
$vars = array('hex');
|
796 |
+
if ($this->precision > 0) {
|
797 |
+
$vars[] = 'precision';
|
798 |
+
}
|
799 |
+
return $vars;
|
800 |
+
}
|
801 |
+
|
802 |
+
/**
|
803 |
+
* __wakeup() magic method
|
804 |
+
*
|
805 |
+
* Will be called, automatically, when unserialize() is called on a BigInteger object.
|
806 |
+
*
|
807 |
+
* @see self::__sleep()
|
808 |
+
* @access public
|
809 |
+
*/
|
810 |
+
function __wakeup()
|
811 |
+
{
|
812 |
+
$temp = new static($this->hex, -16);
|
813 |
+
$this->value = $temp->value;
|
814 |
+
$this->is_negative = $temp->is_negative;
|
815 |
+
if ($this->precision > 0) {
|
816 |
+
// recalculate $this->bitmask
|
817 |
+
$this->setPrecision($this->precision);
|
818 |
+
}
|
819 |
+
}
|
820 |
+
|
821 |
+
/**
|
822 |
+
* __debugInfo() magic method
|
823 |
+
*
|
824 |
+
* Will be called, automatically, when print_r() or var_dump() are called
|
825 |
+
*
|
826 |
+
* @access public
|
827 |
+
*/
|
828 |
+
function __debugInfo()
|
829 |
+
{
|
830 |
+
$opts = array();
|
831 |
+
switch (MATH_BIGINTEGER_MODE) {
|
832 |
+
case self::MODE_GMP:
|
833 |
+
$engine = 'gmp';
|
834 |
+
break;
|
835 |
+
case self::MODE_BCMATH:
|
836 |
+
$engine = 'bcmath';
|
837 |
+
break;
|
838 |
+
case self::MODE_INTERNAL:
|
839 |
+
$engine = 'internal';
|
840 |
+
$opts[] = PHP_INT_SIZE == 8 ? '64-bit' : '32-bit';
|
841 |
+
}
|
842 |
+
if (MATH_BIGINTEGER_MODE != self::MODE_GMP && defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
|
843 |
+
$opts[] = 'OpenSSL';
|
844 |
+
}
|
845 |
+
if (!empty($opts)) {
|
846 |
+
$engine.= ' (' . implode($opts, ', ') . ')';
|
847 |
+
}
|
848 |
+
return array(
|
849 |
+
'value' => '0x' . $this->toHex(true),
|
850 |
+
'engine' => $engine
|
851 |
+
);
|
852 |
+
}
|
853 |
+
|
854 |
+
/**
|
855 |
+
* Adds two BigIntegers.
|
856 |
+
*
|
857 |
+
* Here's an example:
|
858 |
+
* <code>
|
859 |
+
* <?php
|
860 |
+
* $a = new \phpseclib\Math\BigInteger('10');
|
861 |
+
* $b = new \phpseclib\Math\BigInteger('20');
|
862 |
+
*
|
863 |
+
* $c = $a->add($b);
|
864 |
+
*
|
865 |
+
* echo $c->toString(); // outputs 30
|
866 |
+
* ?>
|
867 |
+
* </code>
|
868 |
+
*
|
869 |
+
* @param \phpseclib\Math\BigInteger $y
|
870 |
+
* @return \phpseclib\Math\BigInteger
|
871 |
+
* @access public
|
872 |
+
* @internal Performs base-2**52 addition
|
873 |
+
*/
|
874 |
+
function add($y)
|
875 |
+
{
|
876 |
+
switch (MATH_BIGINTEGER_MODE) {
|
877 |
+
case self::MODE_GMP:
|
878 |
+
$temp = new static();
|
879 |
+
$temp->value = gmp_add($this->value, $y->value);
|
880 |
+
|
881 |
+
return $this->_normalize($temp);
|
882 |
+
case self::MODE_BCMATH:
|
883 |
+
$temp = new static();
|
884 |
+
$temp->value = bcadd($this->value, $y->value, 0);
|
885 |
+
|
886 |
+
return $this->_normalize($temp);
|
887 |
+
}
|
888 |
+
|
889 |
+
$temp = $this->_add($this->value, $this->is_negative, $y->value, $y->is_negative);
|
890 |
+
|
891 |
+
$result = new static();
|
892 |
+
$result->value = $temp[self::VALUE];
|
893 |
+
$result->is_negative = $temp[self::SIGN];
|
894 |
+
|
895 |
+
return $this->_normalize($result);
|
896 |
+
}
|
897 |
+
|
898 |
+
/**
|
899 |
+
* Performs addition.
|
900 |
+
*
|
901 |
+
* @param array $x_value
|
902 |
+
* @param bool $x_negative
|
903 |
+
* @param array $y_value
|
904 |
+
* @param bool $y_negative
|
905 |
+
* @return array
|
906 |
+
* @access private
|
907 |
+
*/
|
908 |
+
function _add($x_value, $x_negative, $y_value, $y_negative)
|
909 |
+
{
|
910 |
+
$x_size = count($x_value);
|
911 |
+
$y_size = count($y_value);
|
912 |
+
|
913 |
+
if ($x_size == 0) {
|
914 |
+
return array(
|
915 |
+
self::VALUE => $y_value,
|
916 |
+
self::SIGN => $y_negative
|
917 |
+
);
|
918 |
+
} elseif ($y_size == 0) {
|
919 |
+
return array(
|
920 |
+
self::VALUE => $x_value,
|
921 |
+
self::SIGN => $x_negative
|
922 |
+
);
|
923 |
+
}
|
924 |
+
|
925 |
+
// subtract, if appropriate
|
926 |
+
if ($x_negative != $y_negative) {
|
927 |
+
if ($x_value == $y_value) {
|
928 |
+
return array(
|
929 |
+
self::VALUE => array(),
|
930 |
+
self::SIGN => false
|
931 |
+
);
|
932 |
+
}
|
933 |
+
|
934 |
+
$temp = $this->_subtract($x_value, false, $y_value, false);
|
935 |
+
$temp[self::SIGN] = $this->_compare($x_value, false, $y_value, false) > 0 ?
|
936 |
+
$x_negative : $y_negative;
|
937 |
+
|
938 |
+
return $temp;
|
939 |
+
}
|
940 |
+
|
941 |
+
if ($x_size < $y_size) {
|
942 |
+
$size = $x_size;
|
943 |
+
$value = $y_value;
|
944 |
+
} else {
|
945 |
+
$size = $y_size;
|
946 |
+
$value = $x_value;
|
947 |
+
}
|
948 |
+
|
949 |
+
$value[count($value)] = 0; // just in case the carry adds an extra digit
|
950 |
+
|
951 |
+
$carry = 0;
|
952 |
+
for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) {
|
953 |
+
$sum = $x_value[$j] * self::$baseFull + $x_value[$i] + $y_value[$j] * self::$baseFull + $y_value[$i] + $carry;
|
954 |
+
$carry = $sum >= self::$maxDigit2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
|
955 |
+
$sum = $carry ? $sum - self::$maxDigit2 : $sum;
|
956 |
+
|
957 |
+
$temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31);
|
958 |
+
|
959 |
+
$value[$i] = (int) ($sum - self::$baseFull * $temp); // eg. a faster alternative to fmod($sum, 0x4000000)
|
960 |
+
$value[$j] = $temp;
|
961 |
+
}
|
962 |
+
|
963 |
+
if ($j == $size) { // ie. if $y_size is odd
|
964 |
+
$sum = $x_value[$i] + $y_value[$i] + $carry;
|
965 |
+
$carry = $sum >= self::$baseFull;
|
966 |
+
$value[$i] = $carry ? $sum - self::$baseFull : $sum;
|
967 |
+
++$i; // ie. let $i = $j since we've just done $value[$i]
|
968 |
+
}
|
969 |
+
|
970 |
+
if ($carry) {
|
971 |
+
for (; $value[$i] == self::$maxDigit; ++$i) {
|
972 |
+
$value[$i] = 0;
|
973 |
+
}
|
974 |
+
++$value[$i];
|
975 |
+
}
|
976 |
+
|
977 |
+
return array(
|
978 |
+
self::VALUE => $this->_trim($value),
|
979 |
+
self::SIGN => $x_negative
|
980 |
+
);
|
981 |
+
}
|
982 |
+
|
983 |
+
/**
|
984 |
+
* Subtracts two BigIntegers.
|
985 |
+
*
|
986 |
+
* Here's an example:
|
987 |
+
* <code>
|
988 |
+
* <?php
|
989 |
+
* $a = new \phpseclib\Math\BigInteger('10');
|
990 |
+
* $b = new \phpseclib\Math\BigInteger('20');
|
991 |
+
*
|
992 |
+
* $c = $a->subtract($b);
|
993 |
+
*
|
994 |
+
* echo $c->toString(); // outputs -10
|
995 |
+
* ?>
|
996 |
+
* </code>
|
997 |
+
*
|
998 |
+
* @param \phpseclib\Math\BigInteger $y
|
999 |
+
* @return \phpseclib\Math\BigInteger
|
1000 |
+
* @access public
|
1001 |
+
* @internal Performs base-2**52 subtraction
|
1002 |
+
*/
|
1003 |
+
function subtract($y)
|
1004 |
+
{
|
1005 |
+
switch (MATH_BIGINTEGER_MODE) {
|
1006 |
+
case self::MODE_GMP:
|
1007 |
+
$temp = new static();
|
1008 |
+
$temp->value = gmp_sub($this->value, $y->value);
|
1009 |
+
|
1010 |
+
return $this->_normalize($temp);
|
1011 |
+
case self::MODE_BCMATH:
|
1012 |
+
$temp = new static();
|
1013 |
+
$temp->value = bcsub($this->value, $y->value, 0);
|
1014 |
+
|
1015 |
+
return $this->_normalize($temp);
|
1016 |
+
}
|
1017 |
+
|
1018 |
+
$temp = $this->_subtract($this->value, $this->is_negative, $y->value, $y->is_negative);
|
1019 |
+
|
1020 |
+
$result = new static();
|
1021 |
+
$result->value = $temp[self::VALUE];
|
1022 |
+
$result->is_negative = $temp[self::SIGN];
|
1023 |
+
|
1024 |
+
return $this->_normalize($result);
|
1025 |
+
}
|
1026 |
+
|
1027 |
+
/**
|
1028 |
+
* Performs subtraction.
|
1029 |
+
*
|
1030 |
+
* @param array $x_value
|
1031 |
+
* @param bool $x_negative
|
1032 |
+
* @param array $y_value
|
1033 |
+
* @param bool $y_negative
|
1034 |
+
* @return array
|
1035 |
+
* @access private
|
1036 |
+
*/
|
1037 |
+
function _subtract($x_value, $x_negative, $y_value, $y_negative)
|
1038 |
+
{
|
1039 |
+
$x_size = count($x_value);
|
1040 |
+
$y_size = count($y_value);
|
1041 |
+
|
1042 |
+
if ($x_size == 0) {
|
1043 |
+
return array(
|
1044 |
+
self::VALUE => $y_value,
|
1045 |
+
self::SIGN => !$y_negative
|
1046 |
+
);
|
1047 |
+
} elseif ($y_size == 0) {
|
1048 |
+
return array(
|
1049 |
+
self::VALUE => $x_value,
|
1050 |
+
self::SIGN => $x_negative
|
1051 |
+
);
|
1052 |
+
}
|
1053 |
+
|
1054 |
+
// add, if appropriate (ie. -$x - +$y or +$x - -$y)
|
1055 |
+
if ($x_negative != $y_negative) {
|
1056 |
+
$temp = $this->_add($x_value, false, $y_value, false);
|
1057 |
+
$temp[self::SIGN] = $x_negative;
|
1058 |
+
|
1059 |
+
return $temp;
|
1060 |
+
}
|
1061 |
+
|
1062 |
+
$diff = $this->_compare($x_value, $x_negative, $y_value, $y_negative);
|
1063 |
+
|
1064 |
+
if (!$diff) {
|
1065 |
+
return array(
|
1066 |
+
self::VALUE => array(),
|
1067 |
+
self::SIGN => false
|
1068 |
+
);
|
1069 |
+
}
|
1070 |
+
|
1071 |
+
// switch $x and $y around, if appropriate.
|
1072 |
+
if ((!$x_negative && $diff < 0) || ($x_negative && $diff > 0)) {
|
1073 |
+
$temp = $x_value;
|
1074 |
+
$x_value = $y_value;
|
1075 |
+
$y_value = $temp;
|
1076 |
+
|
1077 |
+
$x_negative = !$x_negative;
|
1078 |
+
|
1079 |
+
$x_size = count($x_value);
|
1080 |
+
$y_size = count($y_value);
|
1081 |
+
}
|
1082 |
+
|
1083 |
+
// at this point, $x_value should be at least as big as - if not bigger than - $y_value
|
1084 |
+
|
1085 |
+
$carry = 0;
|
1086 |
+
for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) {
|
1087 |
+
$sum = $x_value[$j] * self::$baseFull + $x_value[$i] - $y_value[$j] * self::$baseFull - $y_value[$i] - $carry;
|
1088 |
+
$carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
|
1089 |
+
$sum = $carry ? $sum + self::$maxDigit2 : $sum;
|
1090 |
+
|
1091 |
+
$temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31);
|
1092 |
+
|
1093 |
+
$x_value[$i] = (int) ($sum - self::$baseFull * $temp);
|
1094 |
+
$x_value[$j] = $temp;
|
1095 |
+
}
|
1096 |
+
|
1097 |
+
if ($j == $y_size) { // ie. if $y_size is odd
|
1098 |
+
$sum = $x_value[$i] - $y_value[$i] - $carry;
|
1099 |
+
$carry = $sum < 0;
|
1100 |
+
$x_value[$i] = $carry ? $sum + self::$baseFull : $sum;
|
1101 |
+
++$i;
|
1102 |
+
}
|
1103 |
+
|
1104 |
+
if ($carry) {
|
1105 |
+
for (; !$x_value[$i]; ++$i) {
|
1106 |
+
$x_value[$i] = self::$maxDigit;
|
1107 |
+
}
|
1108 |
+
--$x_value[$i];
|
1109 |
+
}
|
1110 |
+
|
1111 |
+
return array(
|
1112 |
+
self::VALUE => $this->_trim($x_value),
|
1113 |
+
self::SIGN => $x_negative
|
1114 |
+
);
|
1115 |
+
}
|
1116 |
+
|
1117 |
+
/**
|
1118 |
+
* Multiplies two BigIntegers
|
1119 |
+
*
|
1120 |
+
* Here's an example:
|
1121 |
+
* <code>
|
1122 |
+
* <?php
|
1123 |
+
* $a = new \phpseclib\Math\BigInteger('10');
|
1124 |
+
* $b = new \phpseclib\Math\BigInteger('20');
|
1125 |
+
*
|
1126 |
+
* $c = $a->multiply($b);
|
1127 |
+
*
|
1128 |
+
* echo $c->toString(); // outputs 200
|
1129 |
+
* ?>
|
1130 |
+
* </code>
|
1131 |
+
*
|
1132 |
+
* @param \phpseclib\Math\BigInteger $x
|
1133 |
+
* @return \phpseclib\Math\BigInteger
|
1134 |
+
* @access public
|
1135 |
+
*/
|
1136 |
+
function multiply($x)
|
1137 |
+
{
|
1138 |
+
switch (MATH_BIGINTEGER_MODE) {
|
1139 |
+
case self::MODE_GMP:
|
1140 |
+
$temp = new static();
|
1141 |
+
$temp->value = gmp_mul($this->value, $x->value);
|
1142 |
+
|
1143 |
+
return $this->_normalize($temp);
|
1144 |
+
case self::MODE_BCMATH:
|
1145 |
+
$temp = new static();
|
1146 |
+
$temp->value = bcmul($this->value, $x->value, 0);
|
1147 |
+
|
1148 |
+
return $this->_normalize($temp);
|
1149 |
+
}
|
1150 |
+
|
1151 |
+
$temp = $this->_multiply($this->value, $this->is_negative, $x->value, $x->is_negative);
|
1152 |
+
|
1153 |
+
$product = new static();
|
1154 |
+
$product->value = $temp[self::VALUE];
|
1155 |
+
$product->is_negative = $temp[self::SIGN];
|
1156 |
+
|
1157 |
+
return $this->_normalize($product);
|
1158 |
+
}
|
1159 |
+
|
1160 |
+
/**
|
1161 |
+
* Performs multiplication.
|
1162 |
+
*
|
1163 |
+
* @param array $x_value
|
1164 |
+
* @param bool $x_negative
|
1165 |
+
* @param array $y_value
|
1166 |
+
* @param bool $y_negative
|
1167 |
+
* @return array
|
1168 |
+
* @access private
|
1169 |
+
*/
|
1170 |
+
function _multiply($x_value, $x_negative, $y_value, $y_negative)
|
1171 |
+
{
|
1172 |
+
//if ( $x_value == $y_value ) {
|
1173 |
+
// return array(
|
1174 |
+
// self::VALUE => $this->_square($x_value),
|
1175 |
+
// self::SIGN => $x_sign != $y_value
|
1176 |
+
// );
|
1177 |
+
//}
|
1178 |
+
|
1179 |
+
$x_length = count($x_value);
|
1180 |
+
$y_length = count($y_value);
|
1181 |
+
|
1182 |
+
if (!$x_length || !$y_length) { // a 0 is being multiplied
|
1183 |
+
return array(
|
1184 |
+
self::VALUE => array(),
|
1185 |
+
self::SIGN => false
|
1186 |
+
);
|
1187 |
+
}
|
1188 |
+
|
1189 |
+
return array(
|
1190 |
+
self::VALUE => min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ?
|
1191 |
+
$this->_trim($this->_regularMultiply($x_value, $y_value)) :
|
1192 |
+
$this->_trim($this->_karatsuba($x_value, $y_value)),
|
1193 |
+
self::SIGN => $x_negative != $y_negative
|
1194 |
+
);
|
1195 |
+
}
|
1196 |
+
|
1197 |
+
/**
|
1198 |
+
* Performs long multiplication on two BigIntegers
|
1199 |
+
*
|
1200 |
+
* Modeled after 'multiply' in MutableBigInteger.java.
|
1201 |
+
*
|
1202 |
+
* @param array $x_value
|
1203 |
+
* @param array $y_value
|
1204 |
+
* @return array
|
1205 |
+
* @access private
|
1206 |
+
*/
|
1207 |
+
function _regularMultiply($x_value, $y_value)
|
1208 |
+
{
|
1209 |
+
$x_length = count($x_value);
|
1210 |
+
$y_length = count($y_value);
|
1211 |
+
|
1212 |
+
if (!$x_length || !$y_length) { // a 0 is being multiplied
|
1213 |
+
return array();
|
1214 |
+
}
|
1215 |
+
|
1216 |
+
if ($x_length < $y_length) {
|
1217 |
+
$temp = $x_value;
|
1218 |
+
$x_value = $y_value;
|
1219 |
+
$y_value = $temp;
|
1220 |
+
|
1221 |
+
$x_length = count($x_value);
|
1222 |
+
$y_length = count($y_value);
|
1223 |
+
}
|
1224 |
+
|
1225 |
+
$product_value = $this->_array_repeat(0, $x_length + $y_length);
|
1226 |
+
|
1227 |
+
// the following for loop could be removed if the for loop following it
|
1228 |
+
// (the one with nested for loops) initially set $i to 0, but
|
1229 |
+
// doing so would also make the result in one set of unnecessary adds,
|
1230 |
+
// since on the outermost loops first pass, $product->value[$k] is going
|
1231 |
+
// to always be 0
|
1232 |
+
|
1233 |
+
$carry = 0;
|
1234 |
+
|
1235 |
+
for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0
|
1236 |
+
$temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
|
1237 |
+
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
|
1238 |
+
$product_value[$j] = (int) ($temp - self::$baseFull * $carry);
|
1239 |
+
}
|
1240 |
+
|
1241 |
+
$product_value[$j] = $carry;
|
1242 |
+
|
1243 |
+
// the above for loop is what the previous comment was talking about. the
|
1244 |
+
// following for loop is the "one with nested for loops"
|
1245 |
+
for ($i = 1; $i < $y_length; ++$i) {
|
1246 |
+
$carry = 0;
|
1247 |
+
|
1248 |
+
for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) {
|
1249 |
+
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
|
1250 |
+
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
|
1251 |
+
$product_value[$k] = (int) ($temp - self::$baseFull * $carry);
|
1252 |
+
}
|
1253 |
+
|
1254 |
+
$product_value[$k] = $carry;
|
1255 |
+
}
|
1256 |
+
|
1257 |
+
return $product_value;
|
1258 |
+
}
|
1259 |
+
|
1260 |
+
/**
|
1261 |
+
* Performs Karatsuba multiplication on two BigIntegers
|
1262 |
+
*
|
1263 |
+
* See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
|
1264 |
+
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}.
|
1265 |
+
*
|
1266 |
+
* @param array $x_value
|
1267 |
+
* @param array $y_value
|
1268 |
+
* @return array
|
1269 |
+
* @access private
|
1270 |
+
*/
|
1271 |
+
function _karatsuba($x_value, $y_value)
|
1272 |
+
{
|
1273 |
+
$m = min(count($x_value) >> 1, count($y_value) >> 1);
|
1274 |
+
|
1275 |
+
if ($m < self::KARATSUBA_CUTOFF) {
|
1276 |
+
return $this->_regularMultiply($x_value, $y_value);
|
1277 |
+
}
|
1278 |
+
|
1279 |
+
$x1 = array_slice($x_value, $m);
|
1280 |
+
$x0 = array_slice($x_value, 0, $m);
|
1281 |
+
$y1 = array_slice($y_value, $m);
|
1282 |
+
$y0 = array_slice($y_value, 0, $m);
|
1283 |
+
|
1284 |
+
$z2 = $this->_karatsuba($x1, $y1);
|
1285 |
+
$z0 = $this->_karatsuba($x0, $y0);
|
1286 |
+
|
1287 |
+
$z1 = $this->_add($x1, false, $x0, false);
|
1288 |
+
$temp = $this->_add($y1, false, $y0, false);
|
1289 |
+
$z1 = $this->_karatsuba($z1[self::VALUE], $temp[self::VALUE]);
|
1290 |
+
$temp = $this->_add($z2, false, $z0, false);
|
1291 |
+
$z1 = $this->_subtract($z1, false, $temp[self::VALUE], false);
|
1292 |
+
|
1293 |
+
$z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
|
1294 |
+
$z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]);
|
1295 |
+
|
1296 |
+
$xy = $this->_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]);
|
1297 |
+
$xy = $this->_add($xy[self::VALUE], $xy[self::SIGN], $z0, false);
|
1298 |
+
|
1299 |
+
return $xy[self::VALUE];
|
1300 |
+
}
|
1301 |
+
|
1302 |
+
/**
|
1303 |
+
* Performs squaring
|
1304 |
+
*
|
1305 |
+
* @param array $x
|
1306 |
+
* @return array
|
1307 |
+
* @access private
|
1308 |
+
*/
|
1309 |
+
function _square($x = false)
|
1310 |
+
{
|
1311 |
+
return count($x) < 2 * self::KARATSUBA_CUTOFF ?
|
1312 |
+
$this->_trim($this->_baseSquare($x)) :
|
1313 |
+
$this->_trim($this->_karatsubaSquare($x));
|
1314 |
+
}
|
1315 |
+
|
1316 |
+
/**
|
1317 |
+
* Performs traditional squaring on two BigIntegers
|
1318 |
+
*
|
1319 |
+
* Squaring can be done faster than multiplying a number by itself can be. See
|
1320 |
+
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} /
|
1321 |
+
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information.
|
1322 |
+
*
|
1323 |
+
* @param array $value
|
1324 |
+
* @return array
|
1325 |
+
* @access private
|
1326 |
+
*/
|
1327 |
+
function _baseSquare($value)
|
1328 |
+
{
|
1329 |
+
if (empty($value)) {
|
1330 |
+
return array();
|
1331 |
+
}
|
1332 |
+
$square_value = $this->_array_repeat(0, 2 * count($value));
|
1333 |
+
|
1334 |
+
for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) {
|
1335 |
+
$i2 = $i << 1;
|
1336 |
+
|
1337 |
+
$temp = $square_value[$i2] + $value[$i] * $value[$i];
|
1338 |
+
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
|
1339 |
+
$square_value[$i2] = (int) ($temp - self::$baseFull * $carry);
|
1340 |
+
|
1341 |
+
// note how we start from $i+1 instead of 0 as we do in multiplication.
|
1342 |
+
for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) {
|
1343 |
+
$temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry;
|
1344 |
+
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
|
1345 |
+
$square_value[$k] = (int) ($temp - self::$baseFull * $carry);
|
1346 |
+
}
|
1347 |
+
|
1348 |
+
// the following line can yield values larger 2**15. at this point, PHP should switch
|
1349 |
+
// over to floats.
|
1350 |
+
$square_value[$i + $max_index + 1] = $carry;
|
1351 |
+
}
|
1352 |
+
|
1353 |
+
return $square_value;
|
1354 |
+
}
|
1355 |
+
|
1356 |
+
/**
|
1357 |
+
* Performs Karatsuba "squaring" on two BigIntegers
|
1358 |
+
*
|
1359 |
+
* See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
|
1360 |
+
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}.
|
1361 |
+
*
|
1362 |
+
* @param array $value
|
1363 |
+
* @return array
|
1364 |
+
* @access private
|
1365 |
+
*/
|
1366 |
+
function _karatsubaSquare($value)
|
1367 |
+
{
|
1368 |
+
$m = count($value) >> 1;
|
1369 |
+
|
1370 |
+
if ($m < self::KARATSUBA_CUTOFF) {
|
1371 |
+
return $this->_baseSquare($value);
|
1372 |
+
}
|
1373 |
+
|
1374 |
+
$x1 = array_slice($value, $m);
|
1375 |
+
$x0 = array_slice($value, 0, $m);
|
1376 |
+
|
1377 |
+
$z2 = $this->_karatsubaSquare($x1);
|
1378 |
+
$z0 = $this->_karatsubaSquare($x0);
|
1379 |
+
|
1380 |
+
$z1 = $this->_add($x1, false, $x0, false);
|
1381 |
+
$z1 = $this->_karatsubaSquare($z1[self::VALUE]);
|
1382 |
+
$temp = $this->_add($z2, false, $z0, false);
|
1383 |
+
$z1 = $this->_subtract($z1, false, $temp[self::VALUE], false);
|
1384 |
+
|
1385 |
+
$z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
|
1386 |
+
$z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]);
|
1387 |
+
|
1388 |
+
$xx = $this->_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]);
|
1389 |
+
$xx = $this->_add($xx[self::VALUE], $xx[self::SIGN], $z0, false);
|
1390 |
+
|
1391 |
+
return $xx[self::VALUE];
|
1392 |
+
}
|
1393 |
+
|
1394 |
+
/**
|
1395 |
+
* Divides two BigIntegers.
|
1396 |
+
*
|
1397 |
+
* Returns an array whose first element contains the quotient and whose second element contains the
|
1398 |
+
* "common residue". If the remainder would be positive, the "common residue" and the remainder are the
|
1399 |
+
* same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder
|
1400 |
+
* and the divisor (basically, the "common residue" is the first positive modulo).
|
1401 |
+
*
|
1402 |
+
* Here's an example:
|
1403 |
+
* <code>
|
1404 |
+
* <?php
|
1405 |
+
* $a = new \phpseclib\Math\BigInteger('10');
|
1406 |
+
* $b = new \phpseclib\Math\BigInteger('20');
|
1407 |
+
*
|
1408 |
+
* list($quotient, $remainder) = $a->divide($b);
|
1409 |
+
*
|
1410 |
+
* echo $quotient->toString(); // outputs 0
|
1411 |
+
* echo "\r\n";
|
1412 |
+
* echo $remainder->toString(); // outputs 10
|
1413 |
+
* ?>
|
1414 |
+
* </code>
|
1415 |
+
*
|
1416 |
+
* @param \phpseclib\Math\BigInteger $y
|
1417 |
+
* @return array
|
1418 |
+
* @access public
|
1419 |
+
* @internal This function is based off of {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}.
|
1420 |
+
*/
|
1421 |
+
function divide($y)
|
1422 |
+
{
|
1423 |
+
switch (MATH_BIGINTEGER_MODE) {
|
1424 |
+
case self::MODE_GMP:
|
1425 |
+
$quotient = new static();
|
1426 |
+
$remainder = new static();
|
1427 |
+
|
1428 |
+
list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value);
|
1429 |
+
|
1430 |
+
if (gmp_sign($remainder->value) < 0) {
|
1431 |
+
$remainder->value = gmp_add($remainder->value, gmp_abs($y->value));
|
1432 |
+
}
|
1433 |
+
|
1434 |
+
return array($this->_normalize($quotient), $this->_normalize($remainder));
|
1435 |
+
case self::MODE_BCMATH:
|
1436 |
+
$quotient = new static();
|
1437 |
+
$remainder = new static();
|
1438 |
+
|
1439 |
+
$quotient->value = bcdiv($this->value, $y->value, 0);
|
1440 |
+
$remainder->value = bcmod($this->value, $y->value);
|
1441 |
+
|
1442 |
+
if ($remainder->value[0] == '-') {
|
1443 |
+
$remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0);
|
1444 |
+
}
|
1445 |
+
|
1446 |
+
return array($this->_normalize($quotient), $this->_normalize($remainder));
|
1447 |
+
}
|
1448 |
+
|
1449 |
+
if (count($y->value) == 1) {
|
1450 |
+
list($q, $r) = $this->_divide_digit($this->value, $y->value[0]);
|
1451 |
+
$quotient = new static();
|
1452 |
+
$remainder = new static();
|
1453 |
+
$quotient->value = $q;
|
1454 |
+
$remainder->value = array($r);
|
1455 |
+
$quotient->is_negative = $this->is_negative != $y->is_negative;
|
1456 |
+
return array($this->_normalize($quotient), $this->_normalize($remainder));
|
1457 |
+
}
|
1458 |
+
|
1459 |
+
static $zero;
|
1460 |
+
if (!isset($zero)) {
|
1461 |
+
$zero = new static();
|
1462 |
+
}
|
1463 |
+
|
1464 |
+
$x = $this->copy();
|
1465 |
+
$y = $y->copy();
|
1466 |
+
|
1467 |
+
$x_sign = $x->is_negative;
|
1468 |
+
$y_sign = $y->is_negative;
|
1469 |
+
|
1470 |
+
$x->is_negative = $y->is_negative = false;
|
1471 |
+
|
1472 |
+
$diff = $x->compare($y);
|
1473 |
+
|
1474 |
+
if (!$diff) {
|
1475 |
+
$temp = new static();
|
1476 |
+
$temp->value = array(1);
|
1477 |
+
$temp->is_negative = $x_sign != $y_sign;
|
1478 |
+
return array($this->_normalize($temp), $this->_normalize(new static()));
|
1479 |
+
}
|
1480 |
+
|
1481 |
+
if ($diff < 0) {
|
1482 |
+
// if $x is negative, "add" $y.
|
1483 |
+
if ($x_sign) {
|
1484 |
+
$x = $y->subtract($x);
|
1485 |
+
}
|
1486 |
+
return array($this->_normalize(new static()), $this->_normalize($x));
|
1487 |
+
}
|
1488 |
+
|
1489 |
+
// normalize $x and $y as described in HAC 14.23 / 14.24
|
1490 |
+
$msb = $y->value[count($y->value) - 1];
|
1491 |
+
for ($shift = 0; !($msb & self::$msb); ++$shift) {
|
1492 |
+
$msb <<= 1;
|
1493 |
+
}
|
1494 |
+
$x->_lshift($shift);
|
1495 |
+
$y->_lshift($shift);
|
1496 |
+
$y_value = &$y->value;
|
1497 |
+
|
1498 |
+
$x_max = count($x->value) - 1;
|
1499 |
+
$y_max = count($y->value) - 1;
|
1500 |
+
|
1501 |
+
$quotient = new static();
|
1502 |
+
$quotient_value = &$quotient->value;
|
1503 |
+
$quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1);
|
1504 |
+
|
1505 |
+
static $temp, $lhs, $rhs;
|
1506 |
+
if (!isset($temp)) {
|
1507 |
+
$temp = new static();
|
1508 |
+
$lhs = new static();
|
1509 |
+
$rhs = new static();
|
1510 |
+
}
|
1511 |
+
$temp_value = &$temp->value;
|
1512 |
+
$rhs_value = &$rhs->value;
|
1513 |
+
|
1514 |
+
// $temp = $y << ($x_max - $y_max-1) in base 2**26
|
1515 |
+
$temp_value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y_value);
|
1516 |
+
|
1517 |
+
while ($x->compare($temp) >= 0) {
|
1518 |
+
// calculate the "common residue"
|
1519 |
+
++$quotient_value[$x_max - $y_max];
|
1520 |
+
$x = $x->subtract($temp);
|
1521 |
+
$x_max = count($x->value) - 1;
|
1522 |
+
}
|
1523 |
+
|
1524 |
+
for ($i = $x_max; $i >= $y_max + 1; --$i) {
|
1525 |
+
$x_value = &$x->value;
|
1526 |
+
$x_window = array(
|
1527 |
+
isset($x_value[$i]) ? $x_value[$i] : 0,
|
1528 |
+
isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0,
|
1529 |
+
isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0
|
1530 |
+
);
|
1531 |
+
$y_window = array(
|
1532 |
+
$y_value[$y_max],
|
1533 |
+
($y_max > 0) ? $y_value[$y_max - 1] : 0
|
1534 |
+
);
|
1535 |
+
|
1536 |
+
$q_index = $i - $y_max - 1;
|
1537 |
+
if ($x_window[0] == $y_window[0]) {
|
1538 |
+
$quotient_value[$q_index] = self::$maxDigit;
|
1539 |
+
} else {
|
1540 |
+
$quotient_value[$q_index] = $this->_safe_divide(
|
1541 |
+
$x_window[0] * self::$baseFull + $x_window[1],
|
1542 |
+
$y_window[0]
|
1543 |
+
);
|
1544 |
+
}
|
1545 |
+
|
1546 |
+
$temp_value = array($y_window[1], $y_window[0]);
|
1547 |
+
|
1548 |
+
$lhs->value = array($quotient_value[$q_index]);
|
1549 |
+
$lhs = $lhs->multiply($temp);
|
1550 |
+
|
1551 |
+
$rhs_value = array($x_window[2], $x_window[1], $x_window[0]);
|
1552 |
+
|
1553 |
+
while ($lhs->compare($rhs) > 0) {
|
1554 |
+
--$quotient_value[$q_index];
|
1555 |
+
|
1556 |
+
$lhs->value = array($quotient_value[$q_index]);
|
1557 |
+
$lhs = $lhs->multiply($temp);
|
1558 |
+
}
|
1559 |
+
|
1560 |
+
$adjust = $this->_array_repeat(0, $q_index);
|
1561 |
+
$temp_value = array($quotient_value[$q_index]);
|
1562 |
+
$temp = $temp->multiply($y);
|
1563 |
+
$temp_value = &$temp->value;
|
1564 |
+
if (count($temp_value)) {
|
1565 |
+
$temp_value = array_merge($adjust, $temp_value);
|
1566 |
+
}
|
1567 |
+
|
1568 |
+
$x = $x->subtract($temp);
|
1569 |
+
|
1570 |
+
if ($x->compare($zero) < 0) {
|
1571 |
+
$temp_value = array_merge($adjust, $y_value);
|
1572 |
+
$x = $x->add($temp);
|
1573 |
+
|
1574 |
+
--$quotient_value[$q_index];
|
1575 |
+
}
|
1576 |
+
|
1577 |
+
$x_max = count($x_value) - 1;
|
1578 |
+
}
|
1579 |
+
|
1580 |
+
// unnormalize the remainder
|
1581 |
+
$x->_rshift($shift);
|
1582 |
+
|
1583 |
+
$quotient->is_negative = $x_sign != $y_sign;
|
1584 |
+
|
1585 |
+
// calculate the "common residue", if appropriate
|
1586 |
+
if ($x_sign) {
|
1587 |
+
$y->_rshift($shift);
|
1588 |
+
$x = $y->subtract($x);
|
1589 |
+
}
|
1590 |
+
|
1591 |
+
return array($this->_normalize($quotient), $this->_normalize($x));
|
1592 |
+
}
|
1593 |
+
|
1594 |
+
/**
|
1595 |
+
* Divides a BigInteger by a regular integer
|
1596 |
+
*
|
1597 |
+
* abc / x = a00 / x + b0 / x + c / x
|
1598 |
+
*
|
1599 |
+
* @param array $dividend
|
1600 |
+
* @param array $divisor
|
1601 |
+
* @return array
|
1602 |
+
* @access private
|
1603 |
+
*/
|
1604 |
+
function _divide_digit($dividend, $divisor)
|
1605 |
+
{
|
1606 |
+
$carry = 0;
|
1607 |
+
$result = array();
|
1608 |
+
|
1609 |
+
for ($i = count($dividend) - 1; $i >= 0; --$i) {
|
1610 |
+
$temp = self::$baseFull * $carry + $dividend[$i];
|
1611 |
+
$result[$i] = $this->_safe_divide($temp, $divisor);
|
1612 |
+
$carry = (int) ($temp - $divisor * $result[$i]);
|
1613 |
+
}
|
1614 |
+
|
1615 |
+
return array($result, $carry);
|
1616 |
+
}
|
1617 |
+
|
1618 |
+
/**
|
1619 |
+
* Performs modular exponentiation.
|
1620 |
+
*
|
1621 |
+
* Here's an example:
|
1622 |
+
* <code>
|
1623 |
+
* <?php
|
1624 |
+
* $a = new \phpseclib\Math\BigInteger('10');
|
1625 |
+
* $b = new \phpseclib\Math\BigInteger('20');
|
1626 |
+
* $c = new \phpseclib\Math\BigInteger('30');
|
1627 |
+
*
|
1628 |
+
* $c = $a->modPow($b, $c);
|
1629 |
+
*
|
1630 |
+
* echo $c->toString(); // outputs 10
|
1631 |
+
* ?>
|
1632 |
+
* </code>
|
1633 |
+
*
|
1634 |
+
* @param \phpseclib\Math\BigInteger $e
|
1635 |
+
* @param \phpseclib\Math\BigInteger $n
|
1636 |
+
* @return \phpseclib\Math\BigInteger
|
1637 |
+
* @access public
|
1638 |
+
* @internal The most naive approach to modular exponentiation has very unreasonable requirements, and
|
1639 |
+
* and although the approach involving repeated squaring does vastly better, it, too, is impractical
|
1640 |
+
* for our purposes. The reason being that division - by far the most complicated and time-consuming
|
1641 |
+
* of the basic operations (eg. +,-,*,/) - occurs multiple times within it.
|
1642 |
+
*
|
1643 |
+
* Modular reductions resolve this issue. Although an individual modular reduction takes more time
|
1644 |
+
* then an individual division, when performed in succession (with the same modulo), they're a lot faster.
|
1645 |
+
*
|
1646 |
+
* The two most commonly used modular reductions are Barrett and Montgomery reduction. Montgomery reduction,
|
1647 |
+
* although faster, only works when the gcd of the modulo and of the base being used is 1. In RSA, when the
|
1648 |
+
* base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because
|
1649 |
+
* the product of two odd numbers is odd), but what about when RSA isn't used?
|
1650 |
+
*
|
1651 |
+
* In contrast, Barrett reduction has no such constraint. As such, some bigint implementations perform a
|
1652 |
+
* Barrett reduction after every operation in the modpow function. Others perform Barrett reductions when the
|
1653 |
+
* modulo is even and Montgomery reductions when the modulo is odd. BigInteger.java's modPow method, however,
|
1654 |
+
* uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and
|
1655 |
+
* the other, a power of two - and recombine them, later. This is the method that this modPow function uses.
|
1656 |
+
* {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates.
|
1657 |
+
*/
|
1658 |
+
function modPow($e, $n)
|
1659 |
+
{
|
1660 |
+
$n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs();
|
1661 |
+
|
1662 |
+
if ($e->compare(new static()) < 0) {
|
1663 |
+
$e = $e->abs();
|
1664 |
+
|
1665 |
+
$temp = $this->modInverse($n);
|
1666 |
+
if ($temp === false) {
|
1667 |
+
return false;
|
1668 |
+
}
|
1669 |
+
|
1670 |
+
return $this->_normalize($temp->modPow($e, $n));
|
1671 |
+
}
|
1672 |
+
|
1673 |
+
if (MATH_BIGINTEGER_MODE == self::MODE_GMP) {
|
1674 |
+
$temp = new static();
|
1675 |
+
$temp->value = gmp_powm($this->value, $e->value, $n->value);
|
1676 |
+
|
1677 |
+
return $this->_normalize($temp);
|
1678 |
+
}
|
1679 |
+
|
1680 |
+
if ($this->compare(new static()) < 0 || $this->compare($n) > 0) {
|
1681 |
+
list(, $temp) = $this->divide($n);
|
1682 |
+
return $temp->modPow($e, $n);
|
1683 |
+
}
|
1684 |
+
|
1685 |
+
if (defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
|
1686 |
+
$components = array(
|
1687 |
+
'modulus' => $n->toBytes(true),
|
1688 |
+
'publicExponent' => $e->toBytes(true)
|
1689 |
+
);
|
1690 |
+
|
1691 |
+
$components = array(
|
1692 |
+
'modulus' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['modulus'])), $components['modulus']),
|
1693 |
+
'publicExponent' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent'])
|
1694 |
+
);
|
1695 |
+
|
1696 |
+
$RSAPublicKey = pack(
|
1697 |
+
'Ca*a*a*',
|
1698 |
+
48,
|
1699 |
+
$this->_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])),
|
1700 |
+
$components['modulus'],
|
1701 |
+
$components['publicExponent']
|
1702 |
+
);
|
1703 |
+
|
1704 |
+
$rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
|
1705 |
+
$RSAPublicKey = chr(0) . $RSAPublicKey;
|
1706 |
+
$RSAPublicKey = chr(3) . $this->_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey;
|
1707 |
+
|
1708 |
+
$encapsulated = pack(
|
1709 |
+
'Ca*a*',
|
1710 |
+
48,
|
1711 |
+
$this->_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)),
|
1712 |
+
$rsaOID . $RSAPublicKey
|
1713 |
+
);
|
1714 |
+
|
1715 |
+
$RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
|
1716 |
+
chunk_split(base64_encode($encapsulated)) .
|
1717 |
+
'-----END PUBLIC KEY-----';
|
1718 |
+
|
1719 |
+
$plaintext = str_pad($this->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT);
|
1720 |
+
|
1721 |
+
if (openssl_public_encrypt($plaintext, $result, $RSAPublicKey, OPENSSL_NO_PADDING)) {
|
1722 |
+
return new static($result, 256);
|
1723 |
+
}
|
1724 |
+
}
|
1725 |
+
|
1726 |
+
if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
|
1727 |
+
$temp = new static();
|
1728 |
+
$temp->value = bcpowmod($this->value, $e->value, $n->value, 0);
|
1729 |
+
|
1730 |
+
return $this->_normalize($temp);
|
1731 |
+
}
|
1732 |
+
|
1733 |
+
if (empty($e->value)) {
|
1734 |
+
$temp = new static();
|
1735 |
+
$temp->value = array(1);
|
1736 |
+
return $this->_normalize($temp);
|
1737 |
+
}
|
1738 |
+
|
1739 |
+
if ($e->value == array(1)) {
|
1740 |
+
list(, $temp) = $this->divide($n);
|
1741 |
+
return $this->_normalize($temp);
|
1742 |
+
}
|
1743 |
+
|
1744 |
+
if ($e->value == array(2)) {
|
1745 |
+
$temp = new static();
|
1746 |
+
$temp->value = $this->_square($this->value);
|
1747 |
+
list(, $temp) = $temp->divide($n);
|
1748 |
+
return $this->_normalize($temp);
|
1749 |
+
}
|
1750 |
+
|
1751 |
+
return $this->_normalize($this->_slidingWindow($e, $n, self::BARRETT));
|
1752 |
+
|
1753 |
+
// the following code, although not callable, can be run independently of the above code
|
1754 |
+
// although the above code performed better in my benchmarks the following could might
|
1755 |
+
// perform better under different circumstances. in lieu of deleting it it's just been
|
1756 |
+
// made uncallable
|
1757 |
+
|
1758 |
+
// is the modulo odd?
|
1759 |
+
if ($n->value[0] & 1) {
|
1760 |
+
return $this->_normalize($this->_slidingWindow($e, $n, self::MONTGOMERY));
|
1761 |
+
}
|
1762 |
+
// if it's not, it's even
|
1763 |
+
|
1764 |
+
// find the lowest set bit (eg. the max pow of 2 that divides $n)
|
1765 |
+
for ($i = 0; $i < count($n->value); ++$i) {
|
1766 |
+
if ($n->value[$i]) {
|
1767 |
+
$temp = decbin($n->value[$i]);
|
1768 |
+
$j = strlen($temp) - strrpos($temp, '1') - 1;
|
1769 |
+
$j+= 26 * $i;
|
1770 |
+
break;
|
1771 |
+
}
|
1772 |
+
}
|
1773 |
+
// at this point, 2^$j * $n/(2^$j) == $n
|
1774 |
+
|
1775 |
+
$mod1 = $n->copy();
|
1776 |
+
$mod1->_rshift($j);
|
1777 |
+
$mod2 = new static();
|
1778 |
+
$mod2->value = array(1);
|
1779 |
+
$mod2->_lshift($j);
|
1780 |
+
|
1781 |
+
$part1 = ($mod1->value != array(1)) ? $this->_slidingWindow($e, $mod1, self::MONTGOMERY) : new static();
|
1782 |
+
$part2 = $this->_slidingWindow($e, $mod2, self::POWEROF2);
|
1783 |
+
|
1784 |
+
$y1 = $mod2->modInverse($mod1);
|
1785 |
+
$y2 = $mod1->modInverse($mod2);
|
1786 |
+
|
1787 |
+
$result = $part1->multiply($mod2);
|
1788 |
+
$result = $result->multiply($y1);
|
1789 |
+
|
1790 |
+
$temp = $part2->multiply($mod1);
|
1791 |
+
$temp = $temp->multiply($y2);
|
1792 |
+
|
1793 |
+
$result = $result->add($temp);
|
1794 |
+
list(, $result) = $result->divide($n);
|
1795 |
+
|
1796 |
+
return $this->_normalize($result);
|
1797 |
+
}
|
1798 |
+
|
1799 |
+
/**
|
1800 |
+
* Performs modular exponentiation.
|
1801 |
+
*
|
1802 |
+
* Alias for modPow().
|
1803 |
+
*
|
1804 |
+
* @param \phpseclib\Math\BigInteger $e
|
1805 |
+
* @param \phpseclib\Math\BigInteger $n
|
1806 |
+
* @return \phpseclib\Math\BigInteger
|
1807 |
+
* @access public
|
1808 |
+
*/
|
1809 |
+
function powMod($e, $n)
|
1810 |
+
{
|
1811 |
+
return $this->modPow($e, $n);
|
1812 |
+
}
|
1813 |
+
|
1814 |
+
/**
|
1815 |
+
* Sliding Window k-ary Modular Exponentiation
|
1816 |
+
*
|
1817 |
+
* Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} /
|
1818 |
+
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}. In a departure from those algorithims,
|
1819 |
+
* however, this function performs a modular reduction after every multiplication and squaring operation.
|
1820 |
+
* As such, this function has the same preconditions that the reductions being used do.
|
1821 |
+
*
|
1822 |
+
* @param \phpseclib\Math\BigInteger $e
|
1823 |
+
* @param \phpseclib\Math\BigInteger $n
|
1824 |
+
* @param int $mode
|
1825 |
+
* @return \phpseclib\Math\BigInteger
|
1826 |
+
* @access private
|
1827 |
+
*/
|
1828 |
+
function _slidingWindow($e, $n, $mode)
|
1829 |
+
{
|
1830 |
+
static $window_ranges = array(7, 25, 81, 241, 673, 1793); // from BigInteger.java's oddModPow function
|
1831 |
+
//static $window_ranges = array(0, 7, 36, 140, 450, 1303, 3529); // from MPM 7.3.1
|
1832 |
+
|
1833 |
+
$e_value = $e->value;
|
1834 |
+
$e_length = count($e_value) - 1;
|
1835 |
+
$e_bits = decbin($e_value[$e_length]);
|
1836 |
+
for ($i = $e_length - 1; $i >= 0; --$i) {
|
1837 |
+
$e_bits.= str_pad(decbin($e_value[$i]), self::$base, '0', STR_PAD_LEFT);
|
1838 |
+
}
|
1839 |
+
|
1840 |
+
$e_length = strlen($e_bits);
|
1841 |
+
|
1842 |
+
// calculate the appropriate window size.
|
1843 |
+
// $window_size == 3 if $window_ranges is between 25 and 81, for example.
|
1844 |
+
for ($i = 0, $window_size = 1; $i < count($window_ranges) && $e_length > $window_ranges[$i]; ++$window_size, ++$i) {
|
1845 |
+
}
|
1846 |
+
|
1847 |
+
$n_value = $n->value;
|
1848 |
+
|
1849 |
+
// precompute $this^0 through $this^$window_size
|
1850 |
+
$powers = array();
|
1851 |
+
$powers[1] = $this->_prepareReduce($this->value, $n_value, $mode);
|
1852 |
+
$powers[2] = $this->_squareReduce($powers[1], $n_value, $mode);
|
1853 |
+
|
1854 |
+
// we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end
|
1855 |
+
// in a 1. ie. it's supposed to be odd.
|
1856 |
+
$temp = 1 << ($window_size - 1);
|
1857 |
+
for ($i = 1; $i < $temp; ++$i) {
|
1858 |
+
$i2 = $i << 1;
|
1859 |
+
$powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode);
|
1860 |
+
}
|
1861 |
+
|
1862 |
+
$result = array(1);
|
1863 |
+
$result = $this->_prepareReduce($result, $n_value, $mode);
|
1864 |
+
|
1865 |
+
for ($i = 0; $i < $e_length;) {
|
1866 |
+
if (!$e_bits[$i]) {
|
1867 |
+
$result = $this->_squareReduce($result, $n_value, $mode);
|
1868 |
+
++$i;
|
1869 |
+
} else {
|
1870 |
+
for ($j = $window_size - 1; $j > 0; --$j) {
|
1871 |
+
if (!empty($e_bits[$i + $j])) {
|
1872 |
+
break;
|
1873 |
+
}
|
1874 |
+
}
|
1875 |
+
|
1876 |
+
// eg. the length of substr($e_bits, $i, $j + 1)
|
1877 |
+
for ($k = 0; $k <= $j; ++$k) {
|
1878 |
+
$result = $this->_squareReduce($result, $n_value, $mode);
|
1879 |
+
}
|
1880 |
+
|
1881 |
+
$result = $this->_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode);
|
1882 |
+
|
1883 |
+
$i += $j + 1;
|
1884 |
+
}
|
1885 |
+
}
|
1886 |
+
|
1887 |
+
$temp = new static();
|
1888 |
+
$temp->value = $this->_reduce($result, $n_value, $mode);
|
1889 |
+
|
1890 |
+
return $temp;
|
1891 |
+
}
|
1892 |
+
|
1893 |
+
/**
|
1894 |
+
* Modular reduction
|
1895 |
+
*
|
1896 |
+
* For most $modes this will return the remainder.
|
1897 |
+
*
|
1898 |
+
* @see self::_slidingWindow()
|
1899 |
+
* @access private
|
1900 |
+
* @param array $x
|
1901 |
+
* @param array $n
|
1902 |
+
* @param int $mode
|
1903 |
+
* @return array
|
1904 |
+
*/
|
1905 |
+
function _reduce($x, $n, $mode)
|
1906 |
+
{
|
1907 |
+
switch ($mode) {
|
1908 |
+
case self::MONTGOMERY:
|
1909 |
+
return $this->_montgomery($x, $n);
|
1910 |
+
case self::BARRETT:
|
1911 |
+
return $this->_barrett($x, $n);
|
1912 |
+
case self::POWEROF2:
|
1913 |
+
$lhs = new static();
|
1914 |
+
$lhs->value = $x;
|
1915 |
+
$rhs = new static();
|
1916 |
+
$rhs->value = $n;
|
1917 |
+
return $x->_mod2($n);
|
1918 |
+
case self::CLASSIC:
|
1919 |
+
$lhs = new static();
|
1920 |
+
$lhs->value = $x;
|
1921 |
+
$rhs = new static();
|
1922 |
+
$rhs->value = $n;
|
1923 |
+
list(, $temp) = $lhs->divide($rhs);
|
1924 |
+
return $temp->value;
|
1925 |
+
case self::NONE:
|
1926 |
+
return $x;
|
1927 |
+
default:
|
1928 |
+
// an invalid $mode was provided
|
1929 |
+
}
|
1930 |
+
}
|
1931 |
+
|
1932 |
+
/**
|
1933 |
+
* Modular reduction preperation
|
1934 |
+
*
|
1935 |
+
* @see self::_slidingWindow()
|
1936 |
+
* @access private
|
1937 |
+
* @param array $x
|
1938 |
+
* @param array $n
|
1939 |
+
* @param int $mode
|
1940 |
+
* @return array
|
1941 |
+
*/
|
1942 |
+
function _prepareReduce($x, $n, $mode)
|
1943 |
+
{
|
1944 |
+
if ($mode == self::MONTGOMERY) {
|
1945 |
+
return $this->_prepMontgomery($x, $n);
|
1946 |
+
}
|
1947 |
+
return $this->_reduce($x, $n, $mode);
|
1948 |
+
}
|
1949 |
+
|
1950 |
+
/**
|
1951 |
+
* Modular multiply
|
1952 |
+
*
|
1953 |
+
* @see self::_slidingWindow()
|
1954 |
+
* @access private
|
1955 |
+
* @param array $x
|
1956 |
+
* @param array $y
|
1957 |
+
* @param array $n
|
1958 |
+
* @param int $mode
|
1959 |
+
* @return array
|
1960 |
+
*/
|
1961 |
+
function _multiplyReduce($x, $y, $n, $mode)
|
1962 |
+
{
|
1963 |
+
if ($mode == self::MONTGOMERY) {
|
1964 |
+
return $this->_montgomeryMultiply($x, $y, $n);
|
1965 |
+
}
|
1966 |
+
$temp = $this->_multiply($x, false, $y, false);
|
1967 |
+
return $this->_reduce($temp[self::VALUE], $n, $mode);
|
1968 |
+
}
|
1969 |
+
|
1970 |
+
/**
|
1971 |
+
* Modular square
|
1972 |
+
*
|
1973 |
+
* @see self::_slidingWindow()
|
1974 |
+
* @access private
|
1975 |
+
* @param array $x
|
1976 |
+
* @param array $n
|
1977 |
+
* @param int $mode
|
1978 |
+
* @return array
|
1979 |
+
*/
|
1980 |
+
function _squareReduce($x, $n, $mode)
|
1981 |
+
{
|
1982 |
+
if ($mode == self::MONTGOMERY) {
|
1983 |
+
return $this->_montgomeryMultiply($x, $x, $n);
|
1984 |
+
}
|
1985 |
+
return $this->_reduce($this->_square($x), $n, $mode);
|
1986 |
+
}
|
1987 |
+
|
1988 |
+
/**
|
1989 |
+
* Modulos for Powers of Two
|
1990 |
+
*
|
1991 |
+
* Calculates $x%$n, where $n = 2**$e, for some $e. Since this is basically the same as doing $x & ($n-1),
|
1992 |
+
* we'll just use this function as a wrapper for doing that.
|
1993 |
+
*
|
1994 |
+
* @see self::_slidingWindow()
|
1995 |
+
* @access private
|
1996 |
+
* @param \phpseclib\Math\BigInteger
|
1997 |
+
* @return \phpseclib\Math\BigInteger
|
1998 |
+
*/
|
1999 |
+
function _mod2($n)
|
2000 |
+
{
|
2001 |
+
$temp = new static();
|
2002 |
+
$temp->value = array(1);
|
2003 |
+
return $this->bitwise_and($n->subtract($temp));
|
2004 |
+
}
|
2005 |
+
|
2006 |
+
/**
|
2007 |
+
* Barrett Modular Reduction
|
2008 |
+
*
|
2009 |
+
* See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} /
|
2010 |
+
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information. Modified slightly,
|
2011 |
+
* so as not to require negative numbers (initially, this script didn't support negative numbers).
|
2012 |
+
*
|
2013 |
+
* Employs "folding", as described at
|
2014 |
+
* {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}. To quote from
|
2015 |
+
* it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x."
|
2016 |
+
*
|
2017 |
+
* Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that
|
2018 |
+
* usable on account of (1) its not using reasonable radix points as discussed in
|
2019 |
+
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable
|
2020 |
+
* radix points, it only works when there are an even number of digits in the denominator. The reason for (2) is that
|
2021 |
+
* (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line
|
2022 |
+
* comments for details.
|
2023 |
+
*
|
2024 |
+
* @see self::_slidingWindow()
|
2025 |
+
* @access private
|
2026 |
+
* @param array $n
|
2027 |
+
* @param array $m
|
2028 |
+
* @return array
|
2029 |
+
*/
|
2030 |
+
function _barrett($n, $m)
|
2031 |
+
{
|
2032 |
+
static $cache = array(
|
2033 |
+
self::VARIABLE => array(),
|
2034 |
+
self::DATA => array()
|
2035 |
+
);
|
2036 |
+
|
2037 |
+
$m_length = count($m);
|
2038 |
+
|
2039 |
+
// if ($this->_compare($n, $this->_square($m)) >= 0) {
|
2040 |
+
if (count($n) > 2 * $m_length) {
|
2041 |
+
$lhs = new static();
|
2042 |
+
$rhs = new static();
|
2043 |
+
$lhs->value = $n;
|
2044 |
+
$rhs->value = $m;
|
2045 |
+
list(, $temp) = $lhs->divide($rhs);
|
2046 |
+
return $temp->value;
|
2047 |
+
}
|
2048 |
+
|
2049 |
+
// if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced
|
2050 |
+
if ($m_length < 5) {
|
2051 |
+
return $this->_regularBarrett($n, $m);
|
2052 |
+
}
|
2053 |
+
|
2054 |
+
// n = 2 * m.length
|
2055 |
+
|
2056 |
+
if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
|
2057 |
+
$key = count($cache[self::VARIABLE]);
|
2058 |
+
$cache[self::VARIABLE][] = $m;
|
2059 |
+
|
2060 |
+
$lhs = new static();
|
2061 |
+
$lhs_value = &$lhs->value;
|
2062 |
+
$lhs_value = $this->_array_repeat(0, $m_length + ($m_length >> 1));
|
2063 |
+
$lhs_value[] = 1;
|
2064 |
+
$rhs = new static();
|
2065 |
+
$rhs->value = $m;
|
2066 |
+
|
2067 |
+
list($u, $m1) = $lhs->divide($rhs);
|
2068 |
+
$u = $u->value;
|
2069 |
+
$m1 = $m1->value;
|
2070 |
+
|
2071 |
+
$cache[self::DATA][] = array(
|
2072 |
+
'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1)
|
2073 |
+
'm1'=> $m1 // m.length
|
2074 |
+
);
|
2075 |
+
} else {
|
2076 |
+
extract($cache[self::DATA][$key]);
|
2077 |
+
}
|
2078 |
+
|
2079 |
+
$cutoff = $m_length + ($m_length >> 1);
|
2080 |
+
$lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1)
|
2081 |
+
$msd = array_slice($n, $cutoff); // m.length >> 1
|
2082 |
+
$lsd = $this->_trim($lsd);
|
2083 |
+
$temp = $this->_multiply($msd, false, $m1, false);
|
2084 |
+
$n = $this->_add($lsd, false, $temp[self::VALUE], false); // m.length + (m.length >> 1) + 1
|
2085 |
+
|
2086 |
+
if ($m_length & 1) {
|
2087 |
+
return $this->_regularBarrett($n[self::VALUE], $m);
|
2088 |
+
}
|
2089 |
+
|
2090 |
+
// (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2
|
2091 |
+
$temp = array_slice($n[self::VALUE], $m_length - 1);
|
2092 |
+
// if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2
|
2093 |
+
// if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1
|
2094 |
+
$temp = $this->_multiply($temp, false, $u, false);
|
2095 |
+
// if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1
|
2096 |
+
// if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1)
|
2097 |
+
$temp = array_slice($temp[self::VALUE], ($m_length >> 1) + 1);
|
2098 |
+
// if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1
|
2099 |
+
// if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1)
|
2100 |
+
$temp = $this->_multiply($temp, false, $m, false);
|
2101 |
+
|
2102 |
+
// at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit
|
2103 |
+
// number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop
|
2104 |
+
// following this comment would loop a lot (hence our calling _regularBarrett() in that situation).
|
2105 |
+
|
2106 |
+
$result = $this->_subtract($n[self::VALUE], false, $temp[self::VALUE], false);
|
2107 |
+
|
2108 |
+
while ($this->_compare($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) {
|
2109 |
+
$result = $this->_subtract($result[self::VALUE], $result[self::SIGN], $m, false);
|
2110 |
+
}
|
2111 |
+
|
2112 |
+
return $result[self::VALUE];
|
2113 |
+
}
|
2114 |
+
|
2115 |
+
/**
|
2116 |
+
* (Regular) Barrett Modular Reduction
|
2117 |
+
*
|
2118 |
+
* For numbers with more than four digits BigInteger::_barrett() is faster. The difference between that and this
|
2119 |
+
* is that this function does not fold the denominator into a smaller form.
|
2120 |
+
*
|
2121 |
+
* @see self::_slidingWindow()
|
2122 |
+
* @access private
|
2123 |
+
* @param array $x
|
2124 |
+
* @param array $n
|
2125 |
+
* @return array
|
2126 |
+
*/
|
2127 |
+
function _regularBarrett($x, $n)
|
2128 |
+
{
|
2129 |
+
static $cache = array(
|
2130 |
+
self::VARIABLE => array(),
|
2131 |
+
self::DATA => array()
|
2132 |
+
);
|
2133 |
+
|
2134 |
+
$n_length = count($n);
|
2135 |
+
|
2136 |
+
if (count($x) > 2 * $n_length) {
|
2137 |
+
$lhs = new static();
|
2138 |
+
$rhs = new static();
|
2139 |
+
$lhs->value = $x;
|
2140 |
+
$rhs->value = $n;
|
2141 |
+
list(, $temp) = $lhs->divide($rhs);
|
2142 |
+
return $temp->value;
|
2143 |
+
}
|
2144 |
+
|
2145 |
+
if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
|
2146 |
+
$key = count($cache[self::VARIABLE]);
|
2147 |
+
$cache[self::VARIABLE][] = $n;
|
2148 |
+
$lhs = new static();
|
2149 |
+
$lhs_value = &$lhs->value;
|
2150 |
+
$lhs_value = $this->_array_repeat(0, 2 * $n_length);
|
2151 |
+
$lhs_value[] = 1;
|
2152 |
+
$rhs = new static();
|
2153 |
+
$rhs->value = $n;
|
2154 |
+
list($temp, ) = $lhs->divide($rhs); // m.length
|
2155 |
+
$cache[self::DATA][] = $temp->value;
|
2156 |
+
}
|
2157 |
+
|
2158 |
+
// 2 * m.length - (m.length - 1) = m.length + 1
|
2159 |
+
$temp = array_slice($x, $n_length - 1);
|
2160 |
+
// (m.length + 1) + m.length = 2 * m.length + 1
|
2161 |
+
$temp = $this->_multiply($temp, false, $cache[self::DATA][$key], false);
|
2162 |
+
// (2 * m.length + 1) - (m.length - 1) = m.length + 2
|
2163 |
+
$temp = array_slice($temp[self::VALUE], $n_length + 1);
|
2164 |
+
|
2165 |
+
// m.length + 1
|
2166 |
+
$result = array_slice($x, 0, $n_length + 1);
|
2167 |
+
// m.length + 1
|
2168 |
+
$temp = $this->_multiplyLower($temp, false, $n, false, $n_length + 1);
|
2169 |
+
// $temp == array_slice($temp->_multiply($temp, false, $n, false)->value, 0, $n_length + 1)
|
2170 |
+
|
2171 |
+
if ($this->_compare($result, false, $temp[self::VALUE], $temp[self::SIGN]) < 0) {
|
2172 |
+
$corrector_value = $this->_array_repeat(0, $n_length + 1);
|
2173 |
+
$corrector_value[count($corrector_value)] = 1;
|
2174 |
+
$result = $this->_add($result, false, $corrector_value, false);
|
2175 |
+
$result = $result[self::VALUE];
|
2176 |
+
}
|
2177 |
+
|
2178 |
+
// at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits
|
2179 |
+
$result = $this->_subtract($result, false, $temp[self::VALUE], $temp[self::SIGN]);
|
2180 |
+
while ($this->_compare($result[self::VALUE], $result[self::SIGN], $n, false) > 0) {
|
2181 |
+
$result = $this->_subtract($result[self::VALUE], $result[self::SIGN], $n, false);
|
2182 |
+
}
|
2183 |
+
|
2184 |
+
return $result[self::VALUE];
|
2185 |
+
}
|
2186 |
+
|
2187 |
+
/**
|
2188 |
+
* Performs long multiplication up to $stop digits
|
2189 |
+
*
|
2190 |
+
* If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved.
|
2191 |
+
*
|
2192 |
+
* @see self::_regularBarrett()
|
2193 |
+
* @param array $x_value
|
2194 |
+
* @param bool $x_negative
|
2195 |
+
* @param array $y_value
|
2196 |
+
* @param bool $y_negative
|
2197 |
+
* @param int $stop
|
2198 |
+
* @return array
|
2199 |
+
* @access private
|
2200 |
+
*/
|
2201 |
+
function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop)
|
2202 |
+
{
|
2203 |
+
$x_length = count($x_value);
|
2204 |
+
$y_length = count($y_value);
|
2205 |
+
|
2206 |
+
if (!$x_length || !$y_length) { // a 0 is being multiplied
|
2207 |
+
return array(
|
2208 |
+
self::VALUE => array(),
|
2209 |
+
self::SIGN => false
|
2210 |
+
);
|
2211 |
+
}
|
2212 |
+
|
2213 |
+
if ($x_length < $y_length) {
|
2214 |
+
$temp = $x_value;
|
2215 |
+
$x_value = $y_value;
|
2216 |
+
$y_value = $temp;
|
2217 |
+
|
2218 |
+
$x_length = count($x_value);
|
2219 |
+
$y_length = count($y_value);
|
2220 |
+
}
|
2221 |
+
|
2222 |
+
$product_value = $this->_array_repeat(0, $x_length + $y_length);
|
2223 |
+
|
2224 |
+
// the following for loop could be removed if the for loop following it
|
2225 |
+
// (the one with nested for loops) initially set $i to 0, but
|
2226 |
+
// doing so would also make the result in one set of unnecessary adds,
|
2227 |
+
// since on the outermost loops first pass, $product->value[$k] is going
|
2228 |
+
// to always be 0
|
2229 |
+
|
2230 |
+
$carry = 0;
|
2231 |
+
|
2232 |
+
for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i
|
2233 |
+
$temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
|
2234 |
+
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
|
2235 |
+
$product_value[$j] = (int) ($temp - self::$baseFull * $carry);
|
2236 |
+
}
|
2237 |
+
|
2238 |
+
if ($j < $stop) {
|
2239 |
+
$product_value[$j] = $carry;
|
2240 |
+
}
|
2241 |
+
|
2242 |
+
// the above for loop is what the previous comment was talking about. the
|
2243 |
+
// following for loop is the "one with nested for loops"
|
2244 |
+
|
2245 |
+
for ($i = 1; $i < $y_length; ++$i) {
|
2246 |
+
$carry = 0;
|
2247 |
+
|
2248 |
+
for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) {
|
2249 |
+
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
|
2250 |
+
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
|
2251 |
+
$product_value[$k] = (int) ($temp - self::$baseFull * $carry);
|
2252 |
+
}
|
2253 |
+
|
2254 |
+
if ($k < $stop) {
|
2255 |
+
$product_value[$k] = $carry;
|
2256 |
+
}
|
2257 |
+
}
|
2258 |
+
|
2259 |
+
return array(
|
2260 |
+
self::VALUE => $this->_trim($product_value),
|
2261 |
+
self::SIGN => $x_negative != $y_negative
|
2262 |
+
);
|
2263 |
+
}
|
2264 |
+
|
2265 |
+
/**
|
2266 |
+
* Montgomery Modular Reduction
|
2267 |
+
*
|
2268 |
+
* ($x->_prepMontgomery($n))->_montgomery($n) yields $x % $n.
|
2269 |
+
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=170 MPM 6.3} provides insights on how this can be
|
2270 |
+
* improved upon (basically, by using the comba method). gcd($n, 2) must be equal to one for this function
|
2271 |
+
* to work correctly.
|
2272 |
+
*
|
2273 |
+
* @see self::_prepMontgomery()
|
2274 |
+
* @see self::_slidingWindow()
|
2275 |
+
* @access private
|
2276 |
+
* @param array $x
|
2277 |
+
* @param array $n
|
2278 |
+
* @return array
|
2279 |
+
*/
|
2280 |
+
function _montgomery($x, $n)
|
2281 |
+
{
|
2282 |
+
static $cache = array(
|
2283 |
+
self::VARIABLE => array(),
|
2284 |
+
self::DATA => array()
|
2285 |
+
);
|
2286 |
+
|
2287 |
+
if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
|
2288 |
+
$key = count($cache[self::VARIABLE]);
|
2289 |
+
$cache[self::VARIABLE][] = $x;
|
2290 |
+
$cache[self::DATA][] = $this->_modInverse67108864($n);
|
2291 |
+
}
|
2292 |
+
|
2293 |
+
$k = count($n);
|
2294 |
+
|
2295 |
+
$result = array(self::VALUE => $x);
|
2296 |
+
|
2297 |
+
for ($i = 0; $i < $k; ++$i) {
|
2298 |
+
$temp = $result[self::VALUE][$i] * $cache[self::DATA][$key];
|
2299 |
+
$temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
|
2300 |
+
$temp = $this->_regularMultiply(array($temp), $n);
|
2301 |
+
$temp = array_merge($this->_array_repeat(0, $i), $temp);
|
2302 |
+
$result = $this->_add($result[self::VALUE], false, $temp, false);
|
2303 |
+
}
|
2304 |
+
|
2305 |
+
$result[self::VALUE] = array_slice($result[self::VALUE], $k);
|
2306 |
+
|
2307 |
+
if ($this->_compare($result, false, $n, false) >= 0) {
|
2308 |
+
$result = $this->_subtract($result[self::VALUE], false, $n, false);
|
2309 |
+
}
|
2310 |
+
|
2311 |
+
return $result[self::VALUE];
|
2312 |
+
}
|
2313 |
+
|
2314 |
+
/**
|
2315 |
+
* Montgomery Multiply
|
2316 |
+
*
|
2317 |
+
* Interleaves the montgomery reduction and long multiplication algorithms together as described in
|
2318 |
+
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
|
2319 |
+
*
|
2320 |
+
* @see self::_prepMontgomery()
|
2321 |
+
* @see self::_montgomery()
|
2322 |
+
* @access private
|
2323 |
+
* @param array $x
|
2324 |
+
* @param array $y
|
2325 |
+
* @param array $m
|
2326 |
+
* @return array
|
2327 |
+
*/
|
2328 |
+
function _montgomeryMultiply($x, $y, $m)
|
2329 |
+
{
|
2330 |
+
$temp = $this->_multiply($x, false, $y, false);
|
2331 |
+
return $this->_montgomery($temp[self::VALUE], $m);
|
2332 |
+
|
2333 |
+
// the following code, although not callable, can be run independently of the above code
|
2334 |
+
// although the above code performed better in my benchmarks the following could might
|
2335 |
+
// perform better under different circumstances. in lieu of deleting it it's just been
|
2336 |
+
// made uncallable
|
2337 |
+
|
2338 |
+
static $cache = array(
|
2339 |
+
self::VARIABLE => array(),
|
2340 |
+
self::DATA => array()
|
2341 |
+
);
|
2342 |
+
|
2343 |
+
if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
|
2344 |
+
$key = count($cache[self::VARIABLE]);
|
2345 |
+
$cache[self::VARIABLE][] = $m;
|
2346 |
+
$cache[self::DATA][] = $this->_modInverse67108864($m);
|
2347 |
+
}
|
2348 |
+
|
2349 |
+
$n = max(count($x), count($y), count($m));
|
2350 |
+
$x = array_pad($x, $n, 0);
|
2351 |
+
$y = array_pad($y, $n, 0);
|
2352 |
+
$m = array_pad($m, $n, 0);
|
2353 |
+
$a = array(self::VALUE => $this->_array_repeat(0, $n + 1));
|
2354 |
+
for ($i = 0; $i < $n; ++$i) {
|
2355 |
+
$temp = $a[self::VALUE][0] + $x[$i] * $y[0];
|
2356 |
+
$temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
|
2357 |
+
$temp = $temp * $cache[self::DATA][$key];
|
2358 |
+
$temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
|
2359 |
+
$temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false);
|
2360 |
+
$a = $this->_add($a[self::VALUE], false, $temp[self::VALUE], false);
|
2361 |
+
$a[self::VALUE] = array_slice($a[self::VALUE], 1);
|
2362 |
+
}
|
2363 |
+
if ($this->_compare($a[self::VALUE], false, $m, false) >= 0) {
|
2364 |
+
$a = $this->_subtract($a[self::VALUE], false, $m, false);
|
2365 |
+
}
|
2366 |
+
return $a[self::VALUE];
|
2367 |
+
}
|
2368 |
+
|
2369 |
+
/**
|
2370 |
+
* Prepare a number for use in Montgomery Modular Reductions
|
2371 |
+
*
|
2372 |
+
* @see self::_montgomery()
|
2373 |
+
* @see self::_slidingWindow()
|
2374 |
+
* @access private
|
2375 |
+
* @param array $x
|
2376 |
+
* @param array $n
|
2377 |
+
* @return array
|
2378 |
+
*/
|
2379 |
+
function _prepMontgomery($x, $n)
|
2380 |
+
{
|
2381 |
+
$lhs = new static();
|
2382 |
+
$lhs->value = array_merge($this->_array_repeat(0, count($n)), $x);
|
2383 |
+
$rhs = new static();
|
2384 |
+
$rhs->value = $n;
|
2385 |
+
|
2386 |
+
list(, $temp) = $lhs->divide($rhs);
|
2387 |
+
return $temp->value;
|
2388 |
+
}
|
2389 |
+
|
2390 |
+
/**
|
2391 |
+
* Modular Inverse of a number mod 2**26 (eg. 67108864)
|
2392 |
+
*
|
2393 |
+
* Based off of the bnpInvDigit function implemented and justified in the following URL:
|
2394 |
+
*
|
2395 |
+
* {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js}
|
2396 |
+
*
|
2397 |
+
* The following URL provides more info:
|
2398 |
+
*
|
2399 |
+
* {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85}
|
2400 |
+
*
|
2401 |
+
* As for why we do all the bitmasking... strange things can happen when converting from floats to ints. For
|
2402 |
+
* instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields
|
2403 |
+
* int(-2147483648). To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't
|
2404 |
+
* auto-converted to floats. The outermost bitmask is present because without it, there's no guarantee that
|
2405 |
+
* the "residue" returned would be the so-called "common residue". We use fmod, in the last step, because the
|
2406 |
+
* maximum possible $x is 26 bits and the maximum $result is 16 bits. Thus, we have to be able to handle up to
|
2407 |
+
* 40 bits, which only 64-bit floating points will support.
|
2408 |
+
*
|
2409 |
+
* Thanks to Pedro Gimeno Fortea for input!
|
2410 |
+
*
|
2411 |
+
* @see self::_montgomery()
|
2412 |
+
* @access private
|
2413 |
+
* @param array $x
|
2414 |
+
* @return int
|
2415 |
+
*/
|
2416 |
+
function _modInverse67108864($x) // 2**26 == 67,108,864
|
2417 |
+
{
|
2418 |
+
$x = -$x[0];
|
2419 |
+
$result = $x & 0x3; // x**-1 mod 2**2
|
2420 |
+
$result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4
|
2421 |
+
$result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; // x**-1 mod 2**8
|
2422 |
+
$result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16
|
2423 |
+
$result = fmod($result * (2 - fmod($x * $result, self::$baseFull)), self::$baseFull); // x**-1 mod 2**26
|
2424 |
+
return $result & self::$maxDigit;
|
2425 |
+
}
|
2426 |
+
|
2427 |
+
/**
|
2428 |
+
* Calculates modular inverses.
|
2429 |
+
*
|
2430 |
+
* Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
|
2431 |
+
*
|
2432 |
+
* Here's an example:
|
2433 |
+
* <code>
|
2434 |
+
* <?php
|
2435 |
+
* $a = new \phpseclib\Math\BigInteger(30);
|
2436 |
+
* $b = new \phpseclib\Math\BigInteger(17);
|
2437 |
+
*
|
2438 |
+
* $c = $a->modInverse($b);
|
2439 |
+
* echo $c->toString(); // outputs 4
|
2440 |
+
*
|
2441 |
+
* echo "\r\n";
|
2442 |
+
*
|
2443 |
+
* $d = $a->multiply($c);
|
2444 |
+
* list(, $d) = $d->divide($b);
|
2445 |
+
* echo $d; // outputs 1 (as per the definition of modular inverse)
|
2446 |
+
* ?>
|
2447 |
+
* </code>
|
2448 |
+
*
|
2449 |
+
* @param \phpseclib\Math\BigInteger $n
|
2450 |
+
* @return \phpseclib\Math\BigInteger|false
|
2451 |
+
* @access public
|
2452 |
+
* @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information.
|
2453 |
+
*/
|
2454 |
+
function modInverse($n)
|
2455 |
+
{
|
2456 |
+
switch (MATH_BIGINTEGER_MODE) {
|
2457 |
+
case self::MODE_GMP:
|
2458 |
+
$temp = new static();
|
2459 |
+
$temp->value = gmp_invert($this->value, $n->value);
|
2460 |
+
|
2461 |
+
return ($temp->value === false) ? false : $this->_normalize($temp);
|
2462 |
+
}
|
2463 |
+
|
2464 |
+
static $zero, $one;
|
2465 |
+
if (!isset($zero)) {
|
2466 |
+
$zero = new static();
|
2467 |
+
$one = new static(1);
|
2468 |
+
}
|
2469 |
+
|
2470 |
+
// $x mod -$n == $x mod $n.
|
2471 |
+
$n = $n->abs();
|
2472 |
+
|
2473 |
+
if ($this->compare($zero) < 0) {
|
2474 |
+
$temp = $this->abs();
|
2475 |
+
$temp = $temp->modInverse($n);
|
2476 |
+
return $this->_normalize($n->subtract($temp));
|
2477 |
+
}
|
2478 |
+
|
2479 |
+
extract($this->extendedGCD($n));
|
2480 |
+
|
2481 |
+
if (!$gcd->equals($one)) {
|
2482 |
+
return false;
|
2483 |
+
}
|
2484 |
+
|
2485 |
+
$x = $x->compare($zero) < 0 ? $x->add($n) : $x;
|
2486 |
+
|
2487 |
+
return $this->compare($zero) < 0 ? $this->_normalize($n->subtract($x)) : $this->_normalize($x);
|
2488 |
+
}
|
2489 |
+
|
2490 |
+
/**
|
2491 |
+
* Calculates the greatest common divisor and Bezout's identity.
|
2492 |
+
*
|
2493 |
+
* Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that
|
2494 |
+
* 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which
|
2495 |
+
* combination is returned is dependent upon which mode is in use. See
|
2496 |
+
* {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information.
|
2497 |
+
*
|
2498 |
+
* Here's an example:
|
2499 |
+
* <code>
|
2500 |
+
* <?php
|
2501 |
+
* $a = new \phpseclib\Math\BigInteger(693);
|
2502 |
+
* $b = new \phpseclib\Math\BigInteger(609);
|
2503 |
+
*
|
2504 |
+
* extract($a->extendedGCD($b));
|
2505 |
+
*
|
2506 |
+
* echo $gcd->toString() . "\r\n"; // outputs 21
|
2507 |
+
* echo $a->toString() * $x->toString() + $b->toString() * $y->toString(); // outputs 21
|
2508 |
+
* ?>
|
2509 |
+
* </code>
|
2510 |
+
*
|
2511 |
+
* @param \phpseclib\Math\BigInteger $n
|
2512 |
+
* @return \phpseclib\Math\BigInteger
|
2513 |
+
* @access public
|
2514 |
+
* @internal Calculates the GCD using the binary xGCD algorithim described in
|
2515 |
+
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes,
|
2516 |
+
* the more traditional algorithim requires "relatively costly multiple-precision divisions".
|
2517 |
+
*/
|
2518 |
+
function extendedGCD($n)
|
2519 |
+
{
|
2520 |
+
switch (MATH_BIGINTEGER_MODE) {
|
2521 |
+
case self::MODE_GMP:
|
2522 |
+
extract(gmp_gcdext($this->value, $n->value));
|
2523 |
+
|
2524 |
+
return array(
|
2525 |
+
'gcd' => $this->_normalize(new static($g)),
|
2526 |
+
'x' => $this->_normalize(new static($s)),
|
2527 |
+
'y' => $this->_normalize(new static($t))
|
2528 |
+
);
|
2529 |
+
case self::MODE_BCMATH:
|
2530 |
+
// it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works
|
2531 |
+
// best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is,
|
2532 |
+
// the basic extended euclidean algorithim is what we're using.
|
2533 |
+
|
2534 |
+
$u = $this->value;
|
2535 |
+
$v = $n->value;
|
2536 |
+
|
2537 |
+
$a = '1';
|
2538 |
+
$b = '0';
|
2539 |
+
$c = '0';
|
2540 |
+
$d = '1';
|
2541 |
+
|
2542 |
+
while (bccomp($v, '0', 0) != 0) {
|
2543 |
+
$q = bcdiv($u, $v, 0);
|
2544 |
+
|
2545 |
+
$temp = $u;
|
2546 |
+
$u = $v;
|
2547 |
+
$v = bcsub($temp, bcmul($v, $q, 0), 0);
|
2548 |
+
|
2549 |
+
$temp = $a;
|
2550 |
+
$a = $c;
|
2551 |
+
$c = bcsub($temp, bcmul($a, $q, 0), 0);
|
2552 |
+
|
2553 |
+
$temp = $b;
|
2554 |
+
$b = $d;
|
2555 |
+
$d = bcsub($temp, bcmul($b, $q, 0), 0);
|
2556 |
+
}
|
2557 |
+
|
2558 |
+
return array(
|
2559 |
+
'gcd' => $this->_normalize(new static($u)),
|
2560 |
+
'x' => $this->_normalize(new static($a)),
|
2561 |
+
'y' => $this->_normalize(new static($b))
|
2562 |
+
);
|
2563 |
+
}
|
2564 |
+
|
2565 |
+
$y = $n->copy();
|
2566 |
+
$x = $this->copy();
|
2567 |
+
$g = new static();
|
2568 |
+
$g->value = array(1);
|
2569 |
+
|
2570 |
+
while (!(($x->value[0] & 1)|| ($y->value[0] & 1))) {
|
2571 |
+
$x->_rshift(1);
|
2572 |
+
$y->_rshift(1);
|
2573 |
+
$g->_lshift(1);
|
2574 |
+
}
|
2575 |
+
|
2576 |
+
$u = $x->copy();
|
2577 |
+
$v = $y->copy();
|
2578 |
+
|
2579 |
+
$a = new static();
|
2580 |
+
$b = new static();
|
2581 |
+
$c = new static();
|
2582 |
+
$d = new static();
|
2583 |
+
|
2584 |
+
$a->value = $d->value = $g->value = array(1);
|
2585 |
+
$b->value = $c->value = array();
|
2586 |
+
|
2587 |
+
while (!empty($u->value)) {
|
2588 |
+
while (!($u->value[0] & 1)) {
|
2589 |
+
$u->_rshift(1);
|
2590 |
+
if ((!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1))) {
|
2591 |
+
$a = $a->add($y);
|
2592 |
+
$b = $b->subtract($x);
|
2593 |
+
}
|
2594 |
+
$a->_rshift(1);
|
2595 |
+
$b->_rshift(1);
|
2596 |
+
}
|
2597 |
+
|
2598 |
+
while (!($v->value[0] & 1)) {
|
2599 |
+
$v->_rshift(1);
|
2600 |
+
if ((!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1))) {
|
2601 |
+
$c = $c->add($y);
|
2602 |
+
$d = $d->subtract($x);
|
2603 |
+
}
|
2604 |
+
$c->_rshift(1);
|
2605 |
+
$d->_rshift(1);
|
2606 |
+
}
|
2607 |
+
|
2608 |
+
if ($u->compare($v) >= 0) {
|
2609 |
+
$u = $u->subtract($v);
|
2610 |
+
$a = $a->subtract($c);
|
2611 |
+
$b = $b->subtract($d);
|
2612 |
+
} else {
|
2613 |
+
$v = $v->subtract($u);
|
2614 |
+
$c = $c->subtract($a);
|
2615 |
+
$d = $d->subtract($b);
|
2616 |
+
}
|
2617 |
+
}
|
2618 |
+
|
2619 |
+
return array(
|
2620 |
+
'gcd' => $this->_normalize($g->multiply($v)),
|
2621 |
+
'x' => $this->_normalize($c),
|
2622 |
+
'y' => $this->_normalize($d)
|
2623 |
+
);
|
2624 |
+
}
|
2625 |
+
|
2626 |
+
/**
|
2627 |
+
* Calculates the greatest common divisor
|
2628 |
+
*
|
2629 |
+
* Say you have 693 and 609. The GCD is 21.
|
2630 |
+
*
|
2631 |
+
* Here's an example:
|
2632 |
+
* <code>
|
2633 |
+
* <?php
|
2634 |
+
* $a = new \phpseclib\Math\BigInteger(693);
|
2635 |
+
* $b = new \phpseclib\Math\BigInteger(609);
|
2636 |
+
*
|
2637 |
+
* $gcd = a->extendedGCD($b);
|
2638 |
+
*
|
2639 |
+
* echo $gcd->toString() . "\r\n"; // outputs 21
|
2640 |
+
* ?>
|
2641 |
+
* </code>
|
2642 |
+
*
|
2643 |
+
* @param \phpseclib\Math\BigInteger $n
|
2644 |
+
* @return \phpseclib\Math\BigInteger
|
2645 |
+
* @access public
|
2646 |
+
*/
|
2647 |
+
function gcd($n)
|
2648 |
+
{
|
2649 |
+
extract($this->extendedGCD($n));
|
2650 |
+
return $gcd;
|
2651 |
+
}
|
2652 |
+
|
2653 |
+
/**
|
2654 |
+
* Absolute value.
|
2655 |
+
*
|
2656 |
+
* @return \phpseclib\Math\BigInteger
|
2657 |
+
* @access public
|
2658 |
+
*/
|
2659 |
+
function abs()
|
2660 |
+
{
|
2661 |
+
$temp = new static();
|
2662 |
+
|
2663 |
+
switch (MATH_BIGINTEGER_MODE) {
|
2664 |
+
case self::MODE_GMP:
|
2665 |
+
$temp->value = gmp_abs($this->value);
|
2666 |
+
break;
|
2667 |
+
case self::MODE_BCMATH:
|
2668 |
+
$temp->value = (bccomp($this->value, '0', 0) < 0) ? substr($this->value, 1) : $this->value;
|
2669 |
+
break;
|
2670 |
+
default:
|
2671 |
+
$temp->value = $this->value;
|
2672 |
+
}
|
2673 |
+
|
2674 |
+
return $temp;
|
2675 |
+
}
|
2676 |
+
|
2677 |
+
/**
|
2678 |
+
* Compares two numbers.
|
2679 |
+
*
|
2680 |
+
* Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is
|
2681 |
+
* demonstrated thusly:
|
2682 |
+
*
|
2683 |
+
* $x > $y: $x->compare($y) > 0
|
2684 |
+
* $x < $y: $x->compare($y) < 0
|
2685 |
+
* $x == $y: $x->compare($y) == 0
|
2686 |
+
*
|
2687 |
+
* Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y).
|
2688 |
+
*
|
2689 |
+
* @param \phpseclib\Math\BigInteger $y
|
2690 |
+
* @return int < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
|
2691 |
+
* @access public
|
2692 |
+
* @see self::equals()
|
2693 |
+
* @internal Could return $this->subtract($x), but that's not as fast as what we do do.
|
2694 |
+
*/
|
2695 |
+
function compare($y)
|
2696 |
+
{
|
2697 |
+
switch (MATH_BIGINTEGER_MODE) {
|
2698 |
+
case self::MODE_GMP:
|
2699 |
+
$r = gmp_cmp($this->value, $y->value);
|
2700 |
+
if ($r < -1) {
|
2701 |
+
$r = -1;
|
2702 |
+
}
|
2703 |
+
if ($r > 1) {
|
2704 |
+
$r = 1;
|
2705 |
+
}
|
2706 |
+
return $r;
|
2707 |
+
case self::MODE_BCMATH:
|
2708 |
+
return bccomp($this->value, $y->value, 0);
|
2709 |
+
}
|
2710 |
+
|
2711 |
+
return $this->_compare($this->value, $this->is_negative, $y->value, $y->is_negative);
|
2712 |
+
}
|
2713 |
+
|
2714 |
+
/**
|
2715 |
+
* Compares two numbers.
|
2716 |
+
*
|
2717 |
+
* @param array $x_value
|
2718 |
+
* @param bool $x_negative
|
2719 |
+
* @param array $y_value
|
2720 |
+
* @param bool $y_negative
|
2721 |
+
* @return int
|
2722 |
+
* @see self::compare()
|
2723 |
+
* @access private
|
2724 |
+
*/
|
2725 |
+
function _compare($x_value, $x_negative, $y_value, $y_negative)
|
2726 |
+
{
|
2727 |
+
if ($x_negative != $y_negative) {
|
2728 |
+
return (!$x_negative && $y_negative) ? 1 : -1;
|
2729 |
+
}
|
2730 |
+
|
2731 |
+
$result = $x_negative ? -1 : 1;
|
2732 |
+
|
2733 |
+
if (count($x_value) != count($y_value)) {
|
2734 |
+
return (count($x_value) > count($y_value)) ? $result : -$result;
|
2735 |
+
}
|
2736 |
+
$size = max(count($x_value), count($y_value));
|
2737 |
+
|
2738 |
+
$x_value = array_pad($x_value, $size, 0);
|
2739 |
+
$y_value = array_pad($y_value, $size, 0);
|
2740 |
+
|
2741 |
+
for ($i = count($x_value) - 1; $i >= 0; --$i) {
|
2742 |
+
if ($x_value[$i] != $y_value[$i]) {
|
2743 |
+
return ($x_value[$i] > $y_value[$i]) ? $result : -$result;
|
2744 |
+
}
|
2745 |
+
}
|
2746 |
+
|
2747 |
+
return 0;
|
2748 |
+
}
|
2749 |
+
|
2750 |
+
/**
|
2751 |
+
* Tests the equality of two numbers.
|
2752 |
+
*
|
2753 |
+
* If you need to see if one number is greater than or less than another number, use BigInteger::compare()
|
2754 |
+
*
|
2755 |
+
* @param \phpseclib\Math\BigInteger $x
|
2756 |
+
* @return bool
|
2757 |
+
* @access public
|
2758 |
+
* @see self::compare()
|
2759 |
+
*/
|
2760 |
+
function equals($x)
|
2761 |
+
{
|
2762 |
+
switch (MATH_BIGINTEGER_MODE) {
|
2763 |
+
case self::MODE_GMP:
|
2764 |
+
return gmp_cmp($this->value, $x->value) == 0;
|
2765 |
+
default:
|
2766 |
+
return $this->value === $x->value && $this->is_negative == $x->is_negative;
|
2767 |
+
}
|
2768 |
+
}
|
2769 |
+
|
2770 |
+
/**
|
2771 |
+
* Set Precision
|
2772 |
+
*
|
2773 |
+
* Some bitwise operations give different results depending on the precision being used. Examples include left
|
2774 |
+
* shift, not, and rotates.
|
2775 |
+
*
|
2776 |
+
* @param int $bits
|
2777 |
+
* @access public
|
2778 |
+
*/
|
2779 |
+
function setPrecision($bits)
|
2780 |
+
{
|
2781 |
+
$this->precision = $bits;
|
2782 |
+
if (MATH_BIGINTEGER_MODE != self::MODE_BCMATH) {
|
2783 |
+
$this->bitmask = new static(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256);
|
2784 |
+
} else {
|
2785 |
+
$this->bitmask = new static(bcpow('2', $bits, 0));
|
2786 |
+
}
|
2787 |
+
|
2788 |
+
$temp = $this->_normalize($this);
|
2789 |
+
$this->value = $temp->value;
|
2790 |
+
}
|
2791 |
+
|
2792 |
+
/**
|
2793 |
+
* Logical And
|
2794 |
+
*
|
2795 |
+
* @param \phpseclib\Math\BigInteger $x
|
2796 |
+
* @access public
|
2797 |
+
* @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
|
2798 |
+
* @return \phpseclib\Math\BigInteger
|
2799 |
+
*/
|
2800 |
+
function bitwise_and($x)
|
2801 |
+
{
|
2802 |
+
switch (MATH_BIGINTEGER_MODE) {
|
2803 |
+
case self::MODE_GMP:
|
2804 |
+
$temp = new static();
|
2805 |
+
$temp->value = gmp_and($this->value, $x->value);
|
2806 |
+
|
2807 |
+
return $this->_normalize($temp);
|
2808 |
+
case self::MODE_BCMATH:
|
2809 |
+
$left = $this->toBytes();
|
2810 |
+
$right = $x->toBytes();
|
2811 |
+
|
2812 |
+
$length = max(strlen($left), strlen($right));
|
2813 |
+
|
2814 |
+
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
|
2815 |
+
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
|
2816 |
+
|
2817 |
+
return $this->_normalize(new static($left & $right, 256));
|
2818 |
+
}
|
2819 |
+
|
2820 |
+
$result = $this->copy();
|
2821 |
+
|
2822 |
+
$length = min(count($x->value), count($this->value));
|
2823 |
+
|
2824 |
+
$result->value = array_slice($result->value, 0, $length);
|
2825 |
+
|
2826 |
+
for ($i = 0; $i < $length; ++$i) {
|
2827 |
+
$result->value[$i]&= $x->value[$i];
|
2828 |
+
}
|
2829 |
+
|
2830 |
+
return $this->_normalize($result);
|
2831 |
+
}
|
2832 |
+
|
2833 |
+
/**
|
2834 |
+
* Logical Or
|
2835 |
+
*
|
2836 |
+
* @param \phpseclib\Math\BigInteger $x
|
2837 |
+
* @access public
|
2838 |
+
* @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
|
2839 |
+
* @return \phpseclib\Math\BigInteger
|
2840 |
+
*/
|
2841 |
+
function bitwise_or($x)
|
2842 |
+
{
|
2843 |
+
switch (MATH_BIGINTEGER_MODE) {
|
2844 |
+
case self::MODE_GMP:
|
2845 |
+
$temp = new static();
|
2846 |
+
$temp->value = gmp_or($this->value, $x->value);
|
2847 |
+
|
2848 |
+
return $this->_normalize($temp);
|
2849 |
+
case self::MODE_BCMATH:
|
2850 |
+
$left = $this->toBytes();
|
2851 |
+
$right = $x->toBytes();
|
2852 |
+
|
2853 |
+
$length = max(strlen($left), strlen($right));
|
2854 |
+
|
2855 |
+
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
|
2856 |
+
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
|
2857 |
+
|
2858 |
+
return $this->_normalize(new static($left | $right, 256));
|
2859 |
+
}
|
2860 |
+
|
2861 |
+
$length = max(count($this->value), count($x->value));
|
2862 |
+
$result = $this->copy();
|
2863 |
+
$result->value = array_pad($result->value, $length, 0);
|
2864 |
+
$x->value = array_pad($x->value, $length, 0);
|
2865 |
+
|
2866 |
+
for ($i = 0; $i < $length; ++$i) {
|
2867 |
+
$result->value[$i]|= $x->value[$i];
|
2868 |
+
}
|
2869 |
+
|
2870 |
+
return $this->_normalize($result);
|
2871 |
+
}
|
2872 |
+
|
2873 |
+
/**
|
2874 |
+
* Logical Exclusive-Or
|
2875 |
+
*
|
2876 |
+
* @param \phpseclib\Math\BigInteger $x
|
2877 |
+
* @access public
|
2878 |
+
* @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
|
2879 |
+
* @return \phpseclib\Math\BigInteger
|
2880 |
+
*/
|
2881 |
+
function bitwise_xor($x)
|
2882 |
+
{
|
2883 |
+
switch (MATH_BIGINTEGER_MODE) {
|
2884 |
+
case self::MODE_GMP:
|
2885 |
+
$temp = new static();
|
2886 |
+
$temp->value = gmp_xor(gmp_abs($this->value), gmp_abs($x->value));
|
2887 |
+
return $this->_normalize($temp);
|
2888 |
+
case self::MODE_BCMATH:
|
2889 |
+
$left = $this->toBytes();
|
2890 |
+
$right = $x->toBytes();
|
2891 |
+
|
2892 |
+
$length = max(strlen($left), strlen($right));
|
2893 |
+
|
2894 |
+
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
|
2895 |
+
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
|
2896 |
+
|
2897 |
+
return $this->_normalize(new static($left ^ $right, 256));
|
2898 |
+
}
|
2899 |
+
|
2900 |
+
$length = max(count($this->value), count($x->value));
|
2901 |
+
$result = $this->copy();
|
2902 |
+
$result->is_negative = false;
|
2903 |
+
$result->value = array_pad($result->value, $length, 0);
|
2904 |
+
$x->value = array_pad($x->value, $length, 0);
|
2905 |
+
|
2906 |
+
for ($i = 0; $i < $length; ++$i) {
|
2907 |
+
$result->value[$i]^= $x->value[$i];
|
2908 |
+
}
|
2909 |
+
|
2910 |
+
return $this->_normalize($result);
|
2911 |
+
}
|
2912 |
+
|
2913 |
+
/**
|
2914 |
+
* Logical Not
|
2915 |
+
*
|
2916 |
+
* @access public
|
2917 |
+
* @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
|
2918 |
+
* @return \phpseclib\Math\BigInteger
|
2919 |
+
*/
|
2920 |
+
function bitwise_not()
|
2921 |
+
{
|
2922 |
+
// calculuate "not" without regard to $this->precision
|
2923 |
+
// (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0)
|
2924 |
+
$temp = $this->toBytes();
|
2925 |
+
if ($temp == '') {
|
2926 |
+
return $this->_normalize(new static());
|
2927 |
+
}
|
2928 |
+
$pre_msb = decbin(ord($temp[0]));
|
2929 |
+
$temp = ~$temp;
|
2930 |
+
$msb = decbin(ord($temp[0]));
|
2931 |
+
if (strlen($msb) == 8) {
|
2932 |
+
$msb = substr($msb, strpos($msb, '0'));
|
2933 |
+
}
|
2934 |
+
$temp[0] = chr(bindec($msb));
|
2935 |
+
|
2936 |
+
// see if we need to add extra leading 1's
|
2937 |
+
$current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8;
|
2938 |
+
$new_bits = $this->precision - $current_bits;
|
2939 |
+
if ($new_bits <= 0) {
|
2940 |
+
return $this->_normalize(new static($temp, 256));
|
2941 |
+
}
|
2942 |
+
|
2943 |
+
// generate as many leading 1's as we need to.
|
2944 |
+
$leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3);
|
2945 |
+
$this->_base256_lshift($leading_ones, $current_bits);
|
2946 |
+
|
2947 |
+
$temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT);
|
2948 |
+
|
2949 |
+
return $this->_normalize(new static($leading_ones | $temp, 256));
|
2950 |
+
}
|
2951 |
+
|
2952 |
+
/**
|
2953 |
+
* Logical Right Shift
|
2954 |
+
*
|
2955 |
+
* Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
|
2956 |
+
*
|
2957 |
+
* @param int $shift
|
2958 |
+
* @return \phpseclib\Math\BigInteger
|
2959 |
+
* @access public
|
2960 |
+
* @internal The only version that yields any speed increases is the internal version.
|
2961 |
+
*/
|
2962 |
+
function bitwise_rightShift($shift)
|
2963 |
+
{
|
2964 |
+
$temp = new static();
|
2965 |
+
|
2966 |
+
switch (MATH_BIGINTEGER_MODE) {
|
2967 |
+
case self::MODE_GMP:
|
2968 |
+
static $two;
|
2969 |
+
|
2970 |
+
if (!isset($two)) {
|
2971 |
+
$two = gmp_init('2');
|
2972 |
+
}
|
2973 |
+
|
2974 |
+
$temp->value = gmp_div_q($this->value, gmp_pow($two, $shift));
|
2975 |
+
|
2976 |
+
break;
|
2977 |
+
case self::MODE_BCMATH:
|
2978 |
+
$temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0);
|
2979 |
+
|
2980 |
+
break;
|
2981 |
+
default: // could just replace _lshift with this, but then all _lshift() calls would need to be rewritten
|
2982 |
+
// and I don't want to do that...
|
2983 |
+
$temp->value = $this->value;
|
2984 |
+
$temp->_rshift($shift);
|
2985 |
+
}
|
2986 |
+
|
2987 |
+
return $this->_normalize($temp);
|
2988 |
+
}
|
2989 |
+
|
2990 |
+
/**
|
2991 |
+
* Logical Left Shift
|
2992 |
+
*
|
2993 |
+
* Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
|
2994 |
+
*
|
2995 |
+
* @param int $shift
|
2996 |
+
* @return \phpseclib\Math\BigInteger
|
2997 |
+
* @access public
|
2998 |
+
* @internal The only version that yields any speed increases is the internal version.
|
2999 |
+
*/
|
3000 |
+
function bitwise_leftShift($shift)
|
3001 |
+
{
|
3002 |
+
$temp = new static();
|
3003 |
+
|
3004 |
+
switch (MATH_BIGINTEGER_MODE) {
|
3005 |
+
case self::MODE_GMP:
|
3006 |
+
static $two;
|
3007 |
+
|
3008 |
+
if (!isset($two)) {
|
3009 |
+
$two = gmp_init('2');
|
3010 |
+
}
|
3011 |
+
|
3012 |
+
$temp->value = gmp_mul($this->value, gmp_pow($two, $shift));
|
3013 |
+
|
3014 |
+
break;
|
3015 |
+
case self::MODE_BCMATH:
|
3016 |
+
$temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0);
|
3017 |
+
|
3018 |
+
break;
|
3019 |
+
default: // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten
|
3020 |
+
// and I don't want to do that...
|
3021 |
+
$temp->value = $this->value;
|
3022 |
+
$temp->_lshift($shift);
|
3023 |
+
}
|
3024 |
+
|
3025 |
+
return $this->_normalize($temp);
|
3026 |
+
}
|
3027 |
+
|
3028 |
+
/**
|
3029 |
+
* Logical Left Rotate
|
3030 |
+
*
|
3031 |
+
* Instead of the top x bits being dropped they're appended to the shifted bit string.
|
3032 |
+
*
|
3033 |
+
* @param int $shift
|
3034 |
+
* @return \phpseclib\Math\BigInteger
|
3035 |
+
* @access public
|
3036 |
+
*/
|
3037 |
+
function bitwise_leftRotate($shift)
|
3038 |
+
{
|
3039 |
+
$bits = $this->toBytes();
|
3040 |
+
|
3041 |
+
if ($this->precision > 0) {
|
3042 |
+
$precision = $this->precision;
|
3043 |
+
if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
|
3044 |
+
$mask = $this->bitmask->subtract(new static(1));
|
3045 |
+
$mask = $mask->toBytes();
|
3046 |
+
} else {
|
3047 |
+
$mask = $this->bitmask->toBytes();
|
3048 |
+
}
|
3049 |
+
} else {
|
3050 |
+
$temp = ord($bits[0]);
|
3051 |
+
for ($i = 0; $temp >> $i; ++$i) {
|
3052 |
+
}
|
3053 |
+
$precision = 8 * strlen($bits) - 8 + $i;
|
3054 |
+
$mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3);
|
3055 |
+
}
|
3056 |
+
|
3057 |
+
if ($shift < 0) {
|
3058 |
+
$shift+= $precision;
|
3059 |
+
}
|
3060 |
+
$shift%= $precision;
|
3061 |
+
|
3062 |
+
if (!$shift) {
|
3063 |
+
return $this->copy();
|
3064 |
+
}
|
3065 |
+
|
3066 |
+
$left = $this->bitwise_leftShift($shift);
|
3067 |
+
$left = $left->bitwise_and(new static($mask, 256));
|
3068 |
+
$right = $this->bitwise_rightShift($precision - $shift);
|
3069 |
+
$result = MATH_BIGINTEGER_MODE != self::MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right);
|
3070 |
+
return $this->_normalize($result);
|
3071 |
+
}
|
3072 |
+
|
3073 |
+
/**
|
3074 |
+
* Logical Right Rotate
|
3075 |
+
*
|
3076 |
+
* Instead of the bottom x bits being dropped they're prepended to the shifted bit string.
|
3077 |
+
*
|
3078 |
+
* @param int $shift
|
3079 |
+
* @return \phpseclib\Math\BigInteger
|
3080 |
+
* @access public
|
3081 |
+
*/
|
3082 |
+
function bitwise_rightRotate($shift)
|
3083 |
+
{
|
3084 |
+
return $this->bitwise_leftRotate(-$shift);
|
3085 |
+
}
|
3086 |
+
|
3087 |
+
/**
|
3088 |
+
* Generates a random BigInteger
|
3089 |
+
*
|
3090 |
+
* Byte length is equal to $length. Uses \phpseclib\Crypt\Random if it's loaded and mt_rand if it's not.
|
3091 |
+
*
|
3092 |
+
* @param int $length
|
3093 |
+
* @return \phpseclib\Math\BigInteger
|
3094 |
+
* @access private
|
3095 |
+
*/
|
3096 |
+
function _random_number_helper($size)
|
3097 |
+
{
|
3098 |
+
if (class_exists('\phpseclib\Crypt\Random')) {
|
3099 |
+
$random = Random::string($size);
|
3100 |
+
} else {
|
3101 |
+
$random = '';
|
3102 |
+
|
3103 |
+
if ($size & 1) {
|
3104 |
+
$random.= chr(mt_rand(0, 255));
|
3105 |
+
}
|
3106 |
+
|
3107 |
+
$blocks = $size >> 1;
|
3108 |
+
for ($i = 0; $i < $blocks; ++$i) {
|
3109 |
+
// mt_rand(-2147483648, 0x7FFFFFFF) always produces -2147483648 on some systems
|
3110 |
+
$random.= pack('n', mt_rand(0, 0xFFFF));
|
3111 |
+
}
|
3112 |
+
}
|
3113 |
+
|
3114 |
+
return new static($random, 256);
|
3115 |
+
}
|
3116 |
+
|
3117 |
+
/**
|
3118 |
+
* Generate a random number
|
3119 |
+
*
|
3120 |
+
* Returns a random number between $min and $max where $min and $max
|
3121 |
+
* can be defined using one of the two methods:
|
3122 |
+
*
|
3123 |
+
* $min->random($max)
|
3124 |
+
* $max->random($min)
|
3125 |
+
*
|
3126 |
+
* @param \phpseclib\Math\BigInteger $arg1
|
3127 |
+
* @param \phpseclib\Math\BigInteger $arg2
|
3128 |
+
* @return \phpseclib\Math\BigInteger
|
3129 |
+
* @access public
|
3130 |
+
* @internal The API for creating random numbers used to be $a->random($min, $max), where $a was a BigInteger object.
|
3131 |
+
* That method is still supported for BC purposes.
|
3132 |
+
*/
|
3133 |
+
function random($arg1, $arg2 = false)
|
3134 |
+
{
|
3135 |
+
if ($arg1 === false) {
|
3136 |
+
return false;
|
3137 |
+
}
|
3138 |
+
|
3139 |
+
if ($arg2 === false) {
|
3140 |
+
$max = $arg1;
|
3141 |
+
$min = $this;
|
3142 |
+
} else {
|
3143 |
+
$min = $arg1;
|
3144 |
+
$max = $arg2;
|
3145 |
+
}
|
3146 |
+
|
3147 |
+
$compare = $max->compare($min);
|
3148 |
+
|
3149 |
+
if (!$compare) {
|
3150 |
+
return $this->_normalize($min);
|
3151 |
+
} elseif ($compare < 0) {
|
3152 |
+
// if $min is bigger then $max, swap $min and $max
|
3153 |
+
$temp = $max;
|
3154 |
+
$max = $min;
|
3155 |
+
$min = $temp;
|
3156 |
+
}
|
3157 |
+
|
3158 |
+
static $one;
|
3159 |
+
if (!isset($one)) {
|
3160 |
+
$one = new static(1);
|
3161 |
+
}
|
3162 |
+
|
3163 |
+
$max = $max->subtract($min->subtract($one));
|
3164 |
+
$size = strlen(ltrim($max->toBytes(), chr(0)));
|
3165 |
+
|
3166 |
+
/*
|
3167 |
+
doing $random % $max doesn't work because some numbers will be more likely to occur than others.
|
3168 |
+
eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145
|
3169 |
+
would produce 5 whereas the only value of random that could produce 139 would be 139. ie.
|
3170 |
+
not all numbers would be equally likely. some would be more likely than others.
|
3171 |
+
|
3172 |
+
creating a whole new random number until you find one that is within the range doesn't work
|
3173 |
+
because, for sufficiently small ranges, the likelihood that you'd get a number within that range
|
3174 |
+
would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability
|
3175 |
+
would be pretty high that $random would be greater than $max.
|
3176 |
+
|
3177 |
+
phpseclib works around this using the technique described here:
|
3178 |
+
|
3179 |
+
http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string
|
3180 |
+
*/
|
3181 |
+
$random_max = new static(chr(1) . str_repeat("\0", $size), 256);
|
3182 |
+
$random = $this->_random_number_helper($size);
|
3183 |
+
|
3184 |
+
list($max_multiple) = $random_max->divide($max);
|
3185 |
+
$max_multiple = $max_multiple->multiply($max);
|
3186 |
+
|
3187 |
+
while ($random->compare($max_multiple) >= 0) {
|
3188 |
+
$random = $random->subtract($max_multiple);
|
3189 |
+
$random_max = $random_max->subtract($max_multiple);
|
3190 |
+
$random = $random->bitwise_leftShift(8);
|
3191 |
+
$random = $random->add($this->_random_number_helper(1));
|
3192 |
+
$random_max = $random_max->bitwise_leftShift(8);
|
3193 |
+
list($max_multiple) = $random_max->divide($max);
|
3194 |
+
$max_multiple = $max_multiple->multiply($max);
|
3195 |
+
}
|
3196 |
+
list(, $random) = $random->divide($max);
|
3197 |
+
|
3198 |
+
return $this->_normalize($random->add($min));
|
3199 |
+
}
|
3200 |
+
|
3201 |
+
/**
|
3202 |
+
* Generate a random prime number.
|
3203 |
+
*
|
3204 |
+
* If there's not a prime within the given range, false will be returned.
|
3205 |
+
* If more than $timeout seconds have elapsed, give up and return false.
|
3206 |
+
*
|
3207 |
+
* @param \phpseclib\Math\BigInteger $arg1
|
3208 |
+
* @param \phpseclib\Math\BigInteger $arg2
|
3209 |
+
* @param int $timeout
|
3210 |
+
* @return Math_BigInteger|false
|
3211 |
+
* @access public
|
3212 |
+
* @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}.
|
3213 |
+
*/
|
3214 |
+
function randomPrime($arg1, $arg2 = false, $timeout = false)
|
3215 |
+
{
|
3216 |
+
if ($arg1 === false) {
|
3217 |
+
return false;
|
3218 |
+
}
|
3219 |
+
|
3220 |
+
if ($arg2 === false) {
|
3221 |
+
$max = $arg1;
|
3222 |
+
$min = $this;
|
3223 |
+
} else {
|
3224 |
+
$min = $arg1;
|
3225 |
+
$max = $arg2;
|
3226 |
+
}
|
3227 |
+
|
3228 |
+
$compare = $max->compare($min);
|
3229 |
+
|
3230 |
+
if (!$compare) {
|
3231 |
+
return $min->isPrime() ? $min : false;
|
3232 |
+
} elseif ($compare < 0) {
|
3233 |
+
// if $min is bigger then $max, swap $min and $max
|
3234 |
+
$temp = $max;
|
3235 |
+
$max = $min;
|
3236 |
+
$min = $temp;
|
3237 |
+
}
|
3238 |
+
|
3239 |
+
static $one, $two;
|
3240 |
+
if (!isset($one)) {
|
3241 |
+
$one = new static(1);
|
3242 |
+
$two = new static(2);
|
3243 |
+
}
|
3244 |
+
|
3245 |
+
$start = time();
|
3246 |
+
|
3247 |
+
$x = $this->random($min, $max);
|
3248 |
+
|
3249 |
+
// gmp_nextprime() requires PHP 5 >= 5.2.0 per <http://php.net/gmp-nextprime>.
|
3250 |
+
if (MATH_BIGINTEGER_MODE == self::MODE_GMP && extension_loaded('gmp')) {
|
3251 |
+
$p = new static();
|
3252 |
+
$p->value = gmp_nextprime($x->value);
|
3253 |
+
|
3254 |
+
if ($p->compare($max) <= 0) {
|
3255 |
+
return $p;
|
3256 |
+
}
|
3257 |
+
|
3258 |
+
if (!$min->equals($x)) {
|
3259 |
+
$x = $x->subtract($one);
|
3260 |
+
}
|
3261 |
+
|
3262 |
+
return $x->randomPrime($min, $x);
|
3263 |
+
}
|
3264 |
+
|
3265 |
+
if ($x->equals($two)) {
|
3266 |
+
return $x;
|
3267 |
+
}
|
3268 |
+
|
3269 |
+
$x->_make_odd();
|
3270 |
+
if ($x->compare($max) > 0) {
|
3271 |
+
// if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range
|
3272 |
+
if ($min->equals($max)) {
|
3273 |
+
return false;
|
3274 |
+
}
|
3275 |
+
$x = $min->copy();
|
3276 |
+
$x->_make_odd();
|
3277 |
+
}
|
3278 |
+
|
3279 |
+
$initial_x = $x->copy();
|
3280 |
+
|
3281 |
+
while (true) {
|
3282 |
+
if ($timeout !== false && time() - $start > $timeout) {
|
3283 |
+
return false;
|
3284 |
+
}
|
3285 |
+
|
3286 |
+
if ($x->isPrime()) {
|
3287 |
+
return $x;
|
3288 |
+
}
|
3289 |
+
|
3290 |
+
$x = $x->add($two);
|
3291 |
+
|
3292 |
+
if ($x->compare($max) > 0) {
|
3293 |
+
$x = $min->copy();
|
3294 |
+
if ($x->equals($two)) {
|
3295 |
+
return $x;
|
3296 |
+
}
|
3297 |
+
$x->_make_odd();
|
3298 |
+
}
|
3299 |
+
|
3300 |
+
if ($x->equals($initial_x)) {
|
3301 |
+
return false;
|
3302 |
+
}
|
3303 |
+
}
|
3304 |
+
}
|
3305 |
+
|
3306 |
+
/**
|
3307 |
+
* Make the current number odd
|
3308 |
+
*
|
3309 |
+
* If the current number is odd it'll be unchanged. If it's even, one will be added to it.
|
3310 |
+
*
|
3311 |
+
* @see self::randomPrime()
|
3312 |
+
* @access private
|
3313 |
+
*/
|
3314 |
+
function _make_odd()
|
3315 |
+
{
|
3316 |
+
switch (MATH_BIGINTEGER_MODE) {
|
3317 |
+
case self::MODE_GMP:
|
3318 |
+
gmp_setbit($this->value, 0);
|
3319 |
+
break;
|
3320 |
+
case self::MODE_BCMATH:
|
3321 |
+
if ($this->value[strlen($this->value) - 1] % 2 == 0) {
|
3322 |
+
$this->value = bcadd($this->value, '1');
|
3323 |
+
}
|
3324 |
+
break;
|
3325 |
+
default:
|
3326 |
+
$this->value[0] |= 1;
|
3327 |
+
}
|
3328 |
+
}
|
3329 |
+
|
3330 |
+
/**
|
3331 |
+
* Checks a numer to see if it's prime
|
3332 |
+
*
|
3333 |
+
* Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the
|
3334 |
+
* $t parameter is distributability. BigInteger::randomPrime() can be distributed across multiple pageloads
|
3335 |
+
* on a website instead of just one.
|
3336 |
+
*
|
3337 |
+
* @param \phpseclib\Math\BigInteger $t
|
3338 |
+
* @return bool
|
3339 |
+
* @access public
|
3340 |
+
* @internal Uses the
|
3341 |
+
* {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}. See
|
3342 |
+
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24}.
|
3343 |
+
*/
|
3344 |
+
function isPrime($t = false)
|
3345 |
+
{
|
3346 |
+
$length = strlen($this->toBytes());
|
3347 |
+
|
3348 |
+
if (!$t) {
|
3349 |
+
// see HAC 4.49 "Note (controlling the error probability)"
|
3350 |
+
// @codingStandardsIgnoreStart
|
3351 |
+
if ($length >= 163) { $t = 2; } // floor(1300 / 8)
|
3352 |
+
else if ($length >= 106) { $t = 3; } // floor( 850 / 8)
|
3353 |
+
else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8)
|
3354 |
+
else if ($length >= 68 ) { $t = 5; } // floor( 550 / 8)
|
3355 |
+
else if ($length >= 56 ) { $t = 6; } // floor( 450 / 8)
|
3356 |
+
else if ($length >= 50 ) { $t = 7; } // floor( 400 / 8)
|
3357 |
+
else if ($length >= 43 ) { $t = 8; } // floor( 350 / 8)
|
3358 |
+
else if ($length >= 37 ) { $t = 9; } // floor( 300 / 8)
|
3359 |
+
else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8)
|
3360 |
+
else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8)
|
3361 |
+
else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8)
|
3362 |
+
else { $t = 27; }
|
3363 |
+
// @codingStandardsIgnoreEnd
|
3364 |
+
}
|
3365 |
+
|
3366 |
+
// ie. gmp_testbit($this, 0)
|
3367 |
+
// ie. isEven() or !isOdd()
|
3368 |
+
switch (MATH_BIGINTEGER_MODE) {
|
3369 |
+
case self::MODE_GMP:
|
3370 |
+
return gmp_prob_prime($this->value, $t) != 0;
|
3371 |
+
case self::MODE_BCMATH:
|
3372 |
+
if ($this->value === '2') {
|
3373 |
+
return true;
|
3374 |
+
}
|
3375 |
+
if ($this->value[strlen($this->value) - 1] % 2 == 0) {
|
3376 |
+
return false;
|
3377 |
+
}
|
3378 |
+
break;
|
3379 |
+
default:
|
3380 |
+
if ($this->value == array(2)) {
|
3381 |
+
return true;
|
3382 |
+
}
|
3383 |
+
if (~$this->value[0] & 1) {
|
3384 |
+
return false;
|
3385 |
+
}
|
3386 |
+
}
|
3387 |
+
|
3388 |
+
static $primes, $zero, $one, $two;
|
3389 |
+
|
3390 |
+
if (!isset($primes)) {
|
3391 |
+
$primes = array(
|
3392 |
+
3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59,
|
3393 |
+
61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137,
|
3394 |
+
139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
|
3395 |
+
229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
|
3396 |
+
317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
|
3397 |
+
421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
|
3398 |
+
521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617,
|
3399 |
+
619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727,
|
3400 |
+
733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829,
|
3401 |
+
839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947,
|
3402 |
+
953, 967, 971, 977, 983, 991, 997
|
3403 |
+
);
|
3404 |
+
|
3405 |
+
if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
|
3406 |
+
for ($i = 0; $i < count($primes); ++$i) {
|
3407 |
+
$primes[$i] = new static($primes[$i]);
|
3408 |
+
}
|
3409 |
+
}
|
3410 |
+
|
3411 |
+
$zero = new static();
|
3412 |
+
$one = new static(1);
|
3413 |
+
$two = new static(2);
|
3414 |
+
}
|
3415 |
+
|
3416 |
+
if ($this->equals($one)) {
|
3417 |
+
return false;
|
3418 |
+
}
|
3419 |
+
|
3420 |
+
// see HAC 4.4.1 "Random search for probable primes"
|
3421 |
+
if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
|
3422 |
+
foreach ($primes as $prime) {
|
3423 |
+
list(, $r) = $this->divide($prime);
|
3424 |
+
if ($r->equals($zero)) {
|
3425 |
+
return $this->equals($prime);
|
3426 |
+
}
|
3427 |
+
}
|
3428 |
+
} else {
|
3429 |
+
$value = $this->value;
|
3430 |
+
foreach ($primes as $prime) {
|
3431 |
+
list(, $r) = $this->_divide_digit($value, $prime);
|
3432 |
+
if (!$r) {
|
3433 |
+
return count($value) == 1 && $value[0] == $prime;
|
3434 |
+
}
|
3435 |
+
}
|
3436 |
+
}
|
3437 |
+
|
3438 |
+
$n = $this->copy();
|
3439 |
+
$n_1 = $n->subtract($one);
|
3440 |
+
$n_2 = $n->subtract($two);
|
3441 |
+
|
3442 |
+
$r = $n_1->copy();
|
3443 |
+
$r_value = $r->value;
|
3444 |
+
// ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
|
3445 |
+
if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
|
3446 |
+
$s = 0;
|
3447 |
+
// if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals($one) check earlier
|
3448 |
+
while ($r->value[strlen($r->value) - 1] % 2 == 0) {
|
3449 |
+
$r->value = bcdiv($r->value, '2', 0);
|
3450 |
+
++$s;
|
3451 |
+
}
|
3452 |
+
} else {
|
3453 |
+
for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) {
|
3454 |
+
$temp = ~$r_value[$i] & 0xFFFFFF;
|
3455 |
+
for ($j = 1; ($temp >> $j) & 1; ++$j) {
|
3456 |
+
}
|
3457 |
+
if ($j != 25) {
|
3458 |
+
break;
|
3459 |
+
}
|
3460 |
+
}
|
3461 |
+
$s = 26 * $i + $j;
|
3462 |
+
$r->_rshift($s);
|
3463 |
+
}
|
3464 |
+
|
3465 |
+
for ($i = 0; $i < $t; ++$i) {
|
3466 |
+
$a = $this->random($two, $n_2);
|
3467 |
+
$y = $a->modPow($r, $n);
|
3468 |
+
|
3469 |
+
if (!$y->equals($one) && !$y->equals($n_1)) {
|
3470 |
+
for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) {
|
3471 |
+
$y = $y->modPow($two, $n);
|
3472 |
+
if ($y->equals($one)) {
|
3473 |
+
return false;
|
3474 |
+
}
|
3475 |
+
}
|
3476 |
+
|
3477 |
+
if (!$y->equals($n_1)) {
|
3478 |
+
return false;
|
3479 |
+
}
|
3480 |
+
}
|
3481 |
+
}
|
3482 |
+
return true;
|
3483 |
+
}
|
3484 |
+
|
3485 |
+
/**
|
3486 |
+
* Logical Left Shift
|
3487 |
+
*
|
3488 |
+
* Shifts BigInteger's by $shift bits.
|
3489 |
+
*
|
3490 |
+
* @param int $shift
|
3491 |
+
* @access private
|
3492 |
+
*/
|
3493 |
+
function _lshift($shift)
|
3494 |
+
{
|
3495 |
+
if ($shift == 0) {
|
3496 |
+
return;
|
3497 |
+
}
|
3498 |
+
|
3499 |
+
$num_digits = (int) ($shift / self::$base);
|
3500 |
+
$shift %= self::$base;
|
3501 |
+
$shift = 1 << $shift;
|
3502 |
+
|
3503 |
+
$carry = 0;
|
3504 |
+
|
3505 |
+
for ($i = 0; $i < count($this->value); ++$i) {
|
3506 |
+
$temp = $this->value[$i] * $shift + $carry;
|
3507 |
+
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
|
3508 |
+
$this->value[$i] = (int) ($temp - $carry * self::$baseFull);
|
3509 |
+
}
|
3510 |
+
|
3511 |
+
if ($carry) {
|
3512 |
+
$this->value[count($this->value)] = $carry;
|
3513 |
+
}
|
3514 |
+
|
3515 |
+
while ($num_digits--) {
|
3516 |
+
array_unshift($this->value, 0);
|
3517 |
+
}
|
3518 |
+
}
|
3519 |
+
|
3520 |
+
/**
|
3521 |
+
* Logical Right Shift
|
3522 |
+
*
|
3523 |
+
* Shifts BigInteger's by $shift bits.
|
3524 |
+
*
|
3525 |
+
* @param int $shift
|
3526 |
+
* @access private
|
3527 |
+
*/
|
3528 |
+
function _rshift($shift)
|
3529 |
+
{
|
3530 |
+
if ($shift == 0) {
|
3531 |
+
return;
|
3532 |
+
}
|
3533 |
+
|
3534 |
+
$num_digits = (int) ($shift / self::$base);
|
3535 |
+
$shift %= self::$base;
|
3536 |
+
$carry_shift = self::$base - $shift;
|
3537 |
+
$carry_mask = (1 << $shift) - 1;
|
3538 |
+
|
3539 |
+
if ($num_digits) {
|
3540 |
+
$this->value = array_slice($this->value, $num_digits);
|
3541 |
+
}
|
3542 |
+
|
3543 |
+
$carry = 0;
|
3544 |
+
|
3545 |
+
for ($i = count($this->value) - 1; $i >= 0; --$i) {
|
3546 |
+
$temp = $this->value[$i] >> $shift | $carry;
|
3547 |
+
$carry = ($this->value[$i] & $carry_mask) << $carry_shift;
|
3548 |
+
$this->value[$i] = $temp;
|
3549 |
+
}
|
3550 |
+
|
3551 |
+
$this->value = $this->_trim($this->value);
|
3552 |
+
}
|
3553 |
+
|
3554 |
+
/**
|
3555 |
+
* Normalize
|
3556 |
+
*
|
3557 |
+
* Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
|
3558 |
+
*
|
3559 |
+
* @param \phpseclib\Math\BigInteger
|
3560 |
+
* @return \phpseclib\Math\BigInteger
|
3561 |
+
* @see self::_trim()
|
3562 |
+
* @access private
|
3563 |
+
*/
|
3564 |
+
function _normalize($result)
|
3565 |
+
{
|
3566 |
+
$result->precision = $this->precision;
|
3567 |
+
$result->bitmask = $this->bitmask;
|
3568 |
+
|
3569 |
+
switch (MATH_BIGINTEGER_MODE) {
|
3570 |
+
case self::MODE_GMP:
|
3571 |
+
if ($this->bitmask !== false) {
|
3572 |
+
$result->value = gmp_and($result->value, $result->bitmask->value);
|
3573 |
+
}
|
3574 |
+
|
3575 |
+
return $result;
|
3576 |
+
case self::MODE_BCMATH:
|
3577 |
+
if (!empty($result->bitmask->value)) {
|
3578 |
+
$result->value = bcmod($result->value, $result->bitmask->value);
|
3579 |
+
}
|
3580 |
+
|
3581 |
+
return $result;
|
3582 |
+
}
|
3583 |
+
|
3584 |
+
$value = &$result->value;
|
3585 |
+
|
3586 |
+
if (!count($value)) {
|
3587 |
+
$result->is_negative = false;
|
3588 |
+
return $result;
|
3589 |
+
}
|
3590 |
+
|
3591 |
+
$value = $this->_trim($value);
|
3592 |
+
|
3593 |
+
if (!empty($result->bitmask->value)) {
|
3594 |
+
$length = min(count($value), count($this->bitmask->value));
|
3595 |
+
$value = array_slice($value, 0, $length);
|
3596 |
+
|
3597 |
+
for ($i = 0; $i < $length; ++$i) {
|
3598 |
+
$value[$i] = $value[$i] & $this->bitmask->value[$i];
|
3599 |
+
}
|
3600 |
+
}
|
3601 |
+
|
3602 |
+
return $result;
|
3603 |
+
}
|
3604 |
+
|
3605 |
+
/**
|
3606 |
+
* Trim
|
3607 |
+
*
|
3608 |
+
* Removes leading zeros
|
3609 |
+
*
|
3610 |
+
* @param array $value
|
3611 |
+
* @return \phpseclib\Math\BigInteger
|
3612 |
+
* @access private
|
3613 |
+
*/
|
3614 |
+
function _trim($value)
|
3615 |
+
{
|
3616 |
+
for ($i = count($value) - 1; $i >= 0; --$i) {
|
3617 |
+
if ($value[$i]) {
|
3618 |
+
break;
|
3619 |
+
}
|
3620 |
+
unset($value[$i]);
|
3621 |
+
}
|
3622 |
+
|
3623 |
+
return $value;
|
3624 |
+
}
|
3625 |
+
|
3626 |
+
/**
|
3627 |
+
* Array Repeat
|
3628 |
+
*
|
3629 |
+
* @param $input Array
|
3630 |
+
* @param $multiplier mixed
|
3631 |
+
* @return array
|
3632 |
+
* @access private
|
3633 |
+
*/
|
3634 |
+
function _array_repeat($input, $multiplier)
|
3635 |
+
{
|
3636 |
+
return ($multiplier) ? array_fill(0, $multiplier, $input) : array();
|
3637 |
+
}
|
3638 |
+
|
3639 |
+
/**
|
3640 |
+
* Logical Left Shift
|
3641 |
+
*
|
3642 |
+
* Shifts binary strings $shift bits, essentially multiplying by 2**$shift.
|
3643 |
+
*
|
3644 |
+
* @param $x String
|
3645 |
+
* @param $shift Integer
|
3646 |
+
* @return string
|
3647 |
+
* @access private
|
3648 |
+
*/
|
3649 |
+
function _base256_lshift(&$x, $shift)
|
3650 |
+
{
|
3651 |
+
if ($shift == 0) {
|
3652 |
+
return;
|
3653 |
+
}
|
3654 |
+
|
3655 |
+
$num_bytes = $shift >> 3; // eg. floor($shift/8)
|
3656 |
+
$shift &= 7; // eg. $shift % 8
|
3657 |
+
|
3658 |
+
$carry = 0;
|
3659 |
+
for ($i = strlen($x) - 1; $i >= 0; --$i) {
|
3660 |
+
$temp = ord($x[$i]) << $shift | $carry;
|
3661 |
+
$x[$i] = chr($temp);
|
3662 |
+
$carry = $temp >> 8;
|
3663 |
+
}
|
3664 |
+
$carry = ($carry != 0) ? chr($carry) : '';
|
3665 |
+
$x = $carry . $x . str_repeat(chr(0), $num_bytes);
|
3666 |
+
}
|
3667 |
+
|
3668 |
+
/**
|
3669 |
+
* Logical Right Shift
|
3670 |
+
*
|
3671 |
+
* Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder.
|
3672 |
+
*
|
3673 |
+
* @param $x String
|
3674 |
+
* @param $shift Integer
|
3675 |
+
* @return string
|
3676 |
+
* @access private
|
3677 |
+
*/
|
3678 |
+
function _base256_rshift(&$x, $shift)
|
3679 |
+
{
|
3680 |
+
if ($shift == 0) {
|
3681 |
+
$x = ltrim($x, chr(0));
|
3682 |
+
return '';
|
3683 |
+
}
|
3684 |
+
|
3685 |
+
$num_bytes = $shift >> 3; // eg. floor($shift/8)
|
3686 |
+
$shift &= 7; // eg. $shift % 8
|
3687 |
+
|
3688 |
+
$remainder = '';
|
3689 |
+
if ($num_bytes) {
|
3690 |
+
$start = $num_bytes > strlen($x) ? -strlen($x) : -$num_bytes;
|
3691 |
+
$remainder = substr($x, $start);
|
3692 |
+
$x = substr($x, 0, -$num_bytes);
|
3693 |
+
}
|
3694 |
+
|
3695 |
+
$carry = 0;
|
3696 |
+
$carry_shift = 8 - $shift;
|
3697 |
+
for ($i = 0; $i < strlen($x); ++$i) {
|
3698 |
+
$temp = (ord($x[$i]) >> $shift) | $carry;
|
3699 |
+
$carry = (ord($x[$i]) << $carry_shift) & 0xFF;
|
3700 |
+
$x[$i] = chr($temp);
|
3701 |
+
}
|
3702 |
+
$x = ltrim($x, chr(0));
|
3703 |
+
|
3704 |
+
$remainder = chr($carry >> $carry_shift) . $remainder;
|
3705 |
+
|
3706 |
+
return ltrim($remainder, chr(0));
|
3707 |
+
}
|
3708 |
+
|
3709 |
+
// one quirk about how the following functions are implemented is that PHP defines N to be an unsigned long
|
3710 |
+
// at 32-bits, while java's longs are 64-bits.
|
3711 |
+
|
3712 |
+
/**
|
3713 |
+
* Converts 32-bit integers to bytes.
|
3714 |
+
*
|
3715 |
+
* @param int $x
|
3716 |
+
* @return string
|
3717 |
+
* @access private
|
3718 |
+
*/
|
3719 |
+
function _int2bytes($x)
|
3720 |
+
{
|
3721 |
+
return ltrim(pack('N', $x), chr(0));
|
3722 |
+
}
|
3723 |
+
|
3724 |
+
/**
|
3725 |
+
* Converts bytes to 32-bit integers
|
3726 |
+
*
|
3727 |
+
* @param string $x
|
3728 |
+
* @return int
|
3729 |
+
* @access private
|
3730 |
+
*/
|
3731 |
+
function _bytes2int($x)
|
3732 |
+
{
|
3733 |
+
$temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT));
|
3734 |
+
return $temp['int'];
|
3735 |
+
}
|
3736 |
+
|
3737 |
+
/**
|
3738 |
+
* DER-encode an integer
|
3739 |
+
*
|
3740 |
+
* The ability to DER-encode integers is needed to create RSA public keys for use with OpenSSL
|
3741 |
+
*
|
3742 |
+
* @see self::modPow()
|
3743 |
+
* @access private
|
3744 |
+
* @param int $length
|
3745 |
+
* @return string
|
3746 |
+
*/
|
3747 |
+
function _encodeASN1Length($length)
|
3748 |
+
{
|
3749 |
+
if ($length <= 0x7F) {
|
3750 |
+
return chr($length);
|
3751 |
+
}
|
3752 |
+
|
3753 |
+
$temp = ltrim(pack('N', $length), chr(0));
|
3754 |
+
return pack('Ca*', 0x80 | strlen($temp), $temp);
|
3755 |
+
}
|
3756 |
+
|
3757 |
+
/**
|
3758 |
+
* Single digit division
|
3759 |
+
*
|
3760 |
+
* Even if int64 is being used the division operator will return a float64 value
|
3761 |
+
* if the dividend is not evenly divisible by the divisor. Since a float64 doesn't
|
3762 |
+
* have the precision of int64 this is a problem so, when int64 is being used,
|
3763 |
+
* we'll guarantee that the dividend is divisible by first subtracting the remainder.
|
3764 |
+
*
|
3765 |
+
* @access private
|
3766 |
+
* @param int $x
|
3767 |
+
* @param int $y
|
3768 |
+
* @return int
|
3769 |
+
*/
|
3770 |
+
function _safe_divide($x, $y)
|
3771 |
+
{
|
3772 |
+
if (self::$base === 26) {
|
3773 |
+
return (int) ($x / $y);
|
3774 |
+
}
|
3775 |
+
|
3776 |
+
// self::$base === 31
|
3777 |
+
return ($x - ($x % $y)) / $y;
|
3778 |
+
}
|
3779 |
+
}
|
vendor/phpseclib/phpseclib/phpseclib/bootstrap.php
CHANGED
@@ -1,16 +1,16 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* Bootstrapping File for phpseclib
|
4 |
-
*
|
5 |
-
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
6 |
-
*/
|
7 |
-
|
8 |
-
if (extension_loaded('mbstring')) {
|
9 |
-
// 2 - MB_OVERLOAD_STRING
|
10 |
-
if (ini_get('mbstring.func_overload') & 2) {
|
11 |
-
throw new \UnexpectedValueException(
|
12 |
-
'Overloading of string functions using mbstring.func_overload ' .
|
13 |
-
'is not supported by phpseclib.'
|
14 |
-
);
|
15 |
-
}
|
16 |
-
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Bootstrapping File for phpseclib
|
4 |
+
*
|
5 |
+
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
6 |
+
*/
|
7 |
+
|
8 |
+
if (extension_loaded('mbstring')) {
|
9 |
+
// 2 - MB_OVERLOAD_STRING
|
10 |
+
if (ini_get('mbstring.func_overload') & 2) {
|
11 |
+
throw new \UnexpectedValueException(
|
12 |
+
'Overloading of string functions using mbstring.func_overload ' .
|
13 |
+
'is not supported by phpseclib.'
|
14 |
+
);
|
15 |
+
}
|
16 |
+
}
|
vendor/phpseclib/phpseclib/phpseclib/openssl.cnf
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
-
# minimalist openssl.cnf file for use with phpseclib
|
2 |
-
|
3 |
-
HOME = .
|
4 |
-
RANDFILE = $ENV::HOME/.rnd
|
5 |
-
|
6 |
-
[ v3_ca ]
|
1 |
+
# minimalist openssl.cnf file for use with phpseclib
|
2 |
+
|
3 |
+
HOME = .
|
4 |
+
RANDFILE = $ENV::HOME/.rnd
|
5 |
+
|
6 |
+
[ v3_ca ]
|
vendor/phpseclib/phpseclib/travis/code_coverage_id_rsa
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
-----BEGIN RSA PRIVATE KEY-----
|
2 |
+
Proc-Type: 4,ENCRYPTED
|
3 |
+
DEK-Info: AES-128-CBC,2F15FCF0B21FCFB5A37D709322F9B9EB
|
4 |
+
|
5 |
+
JpGgJqRnr0+3mHQoeYXXUzSWeA1wGwMzm6KOPnQLEmA9ztPlBQulzZRh6QJckCwy
|
6 |
+
TC7BMo+XRnYVXF3e0rjji0k7cfEk5Gs8saNcxxOa0u0SUOCXelGnZeqzwiT0a6Fe
|
7 |
+
qAc7NLgTEo9zul9s+MHsplkVk71Oke+1dL7kksMRT0TdXIaqSvk/+nyAeLzAot04
|
8 |
+
wo61T3+Y7/v/8oVxlCbxI5YfYZkm/4jTy7AfbXZBvC0R+F0ZYvIDRCuBe6h6XvcH
|
9 |
+
AiYtw5+Qek6FwIa2CFVxsefvsEnZQYaiJpEFAq2xVHlTjQHHYrfd5cgu1koUNsAt
|
10 |
+
nX1zpsK7tIregXFa07KfDaBBPxfEBqVJQInzj0Rc8HUt0AZ+MrPldrZ28+YQ4RXk
|
11 |
+
/qk5UyKkdHMSKBb4va3mxcpDq1As9HREfeUeOjjduRh1LnNQCJaOhydXBqCFvhFy
|
12 |
+
+Q9utDXP6q4OUxeDHCPGQ7K1I7erwiwuTeSXB3BEDZyZywHXABvJpsidkDlD/aNo
|
13 |
+
QmM19V8y0IAxEAZvc8N0MIOO8hmd8R9U1RK4S24o9M8mgRrmuXjViJjZd5E3h6tX
|
14 |
+
Mgxm8dpOiT77i+NsJwyp2A+MhkAHg2ruwlCIrSCC81zvdphVTfuc/vx3JpXYvhTP
|
15 |
+
Xf9R8ppGnDUFauroN7E7odJKDhLVuAbmU1lWwue2iaNEKZ3L/o9dpRz4Td4NzzSf
|
16 |
+
HKvKbJR35FCsqZq2krmNVd6ynF5PzWfYmz850yn/qdU8zwnW0fV+iHKS2oXuH9X1
|
17 |
+
ZW02/MDdCylpRftNJMntR4Yxim7WEZ5Dif9ZLj8IGRQdWbbIn0WjtiVrFrcUbIfk
|
18 |
+
TCP/3GeEcUa/XbE9hO9APw+7rO3sOehGJ84n4tXxFTFSnOJ5ZxTKpLvxRrmC8DaD
|
19 |
+
cvqy8bbjPfn1EjZmRpCjanLZ1UJbLitFfpUT55aTrcozS4FMHmFm74X2xZGeBhxC
|
20 |
+
CpWQe/agxhWxG+ZXdxt9ExI78ftQCQoGE0Si1KZXH5KQ/xiGmebY9wbtEHWG+q25
|
21 |
+
sKqvjQHQsE3NZq7kne3mnyvjzMDDFYPLDlQLgcKInNrLZ3GszyemtzqcVf3YVur4
|
22 |
+
N+nN0gu6LCx0vtw3yNRqjjmGN1V6sKMCqmIAtFDh9zRTlDTs7ZUBGPgakmmJLLVM
|
23 |
+
ESme0JrRxCP+eEU8JNti9pKlKPGOFVpu4shLjmnmKuDpOytFNpcB/NylkGwZCxvE
|
24 |
+
1KI+EQMZOE5VROAfkvwBLE0SsVxMq7H87zSEOtOqr+QN8RoY1V6N/woBVWda9GFk
|
25 |
+
HI44PM2ZywQbLGthaQV/Kxwf9YZruJdunNoTfEufZgv2Vp+3VAs+gCTGIsbblTnH
|
26 |
+
J8QGfs/lHRBqK1pMZrYy0ubFqifA+b9Xa6VJWToCgcQuWbOVWn1zKQTTXZPksVbB
|
27 |
+
qyE8BJkQCDGgoeq4kdxm0XUR5p3UZWQv0HoykQt33U06TCzU9HOcp0z+Glhrzsvd
|
28 |
+
rbNBhRK0zXF/BFSNBHwKEg02TNlj7gSFJXmvZqvyCAFo5D6nSDbzqm6ARuscsrF0
|
29 |
+
6Zd3toOZTWmrVsKvTlsNPfHXyoGqP3+0NXcTY+kKXG58u4TbEtw8pVyPikuVdh/m
|
30 |
+
-----END RSA PRIVATE KEY-----
|
wp_mail_smtp.php
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
<?php
|
2 |
/**
|
3 |
* Plugin Name: WP Mail SMTP
|
4 |
-
* Version: 1.5.
|
5 |
* Plugin URI: https://wpforms.com/
|
6 |
* Description: Reconfigures the <code>wp_mail()</code> function to use Gmail/Mailgun/SendGrid/SMTP instead of the default <code>mail()</code> and creates an options page to manage the settings.
|
7 |
* Author: WPForms
|
@@ -134,7 +134,7 @@ if ( ! function_exists( 'wp_mail_smtp_check_pro_loading_allowed' ) ) {
|
|
134 |
}
|
135 |
|
136 |
if ( ! defined( 'WPMS_PLUGIN_VER' ) ) {
|
137 |
-
define( 'WPMS_PLUGIN_VER', '1.5.
|
138 |
}
|
139 |
if ( ! defined( 'WPMS_PHP_VER' ) ) {
|
140 |
define( 'WPMS_PHP_VER', '5.3.6' );
|
1 |
<?php
|
2 |
/**
|
3 |
* Plugin Name: WP Mail SMTP
|
4 |
+
* Version: 1.5.2
|
5 |
* Plugin URI: https://wpforms.com/
|
6 |
* Description: Reconfigures the <code>wp_mail()</code> function to use Gmail/Mailgun/SendGrid/SMTP instead of the default <code>mail()</code> and creates an options page to manage the settings.
|
7 |
* Author: WPForms
|
134 |
}
|
135 |
|
136 |
if ( ! defined( 'WPMS_PLUGIN_VER' ) ) {
|
137 |
+
define( 'WPMS_PLUGIN_VER', '1.5.2' );
|
138 |
}
|
139 |
if ( ! defined( 'WPMS_PHP_VER' ) ) {
|
140 |
define( 'WPMS_PHP_VER', '5.3.6' );
|