SSH SFTP Updater Support - Version 0.3

Version Description

  • update phpseclib to latest SVN
  • read file when FTP_PRIKEY is defined (thanks, lkraav!)
Download this release

Release Info

Developer TerraFrost
Plugin Icon wp plugin SSH SFTP Updater Support
Version 0.3
Comparing to
See all releases

Version 0.3

class-wp-filesystem-ssh2.php ADDED
@@ -0,0 +1,335 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WordPress SSH2 Filesystem.
4
+ *
5
+ * @package WordPress
6
+ * @subpackage Filesystem
7
+ */
8
+
9
+ set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . '/phpseclib/');
10
+
11
+ require_once('Net/SFTP.php');
12
+ require_once('Crypt/RSA.php');
13
+
14
+ /**
15
+ * WordPress Filesystem Class for implementing SSH2.
16
+ *
17
+ * @since 2.7
18
+ * @package WordPress
19
+ * @subpackage Filesystem
20
+ * @uses WP_Filesystem_Base Extends class
21
+ */
22
+
23
+ //define('NET_SFTP_LOGGING', NET_SFTP_LOG_REALTIME);
24
+
25
+ class WP_Filesystem_SSH2 extends WP_Filesystem_Base {
26
+
27
+ var $link = false;
28
+ var $sftp_link = false;
29
+ var $keys = false;
30
+ var $password = false;
31
+ var $errors = array();
32
+ var $options = array();
33
+
34
+ function WP_Filesystem_SSH2($opt='') {
35
+ $this->method = 'ssh2';
36
+ $this->errors = new WP_Error();
37
+
38
+ if ( !function_exists('stream_get_contents') ) {
39
+ $this->errors->add('ssh2_php_requirement', __('We require the PHP5 function <code>stream_get_contents()</code>'));
40
+ return false;
41
+ }
42
+
43
+ // Set defaults:
44
+ if ( empty($opt['port']) )
45
+ $this->options['port'] = 22;
46
+ else
47
+ $this->options['port'] = $opt['port'];
48
+
49
+ if ( empty($opt['hostname']) )
50
+ $this->errors->add('empty_hostname', __('SSH2 hostname is required'));
51
+ else
52
+ $this->options['hostname'] = $opt['hostname'];
53
+
54
+ if ( ! empty($opt['base']) )
55
+ $this->wp_base = $opt['base'];
56
+
57
+ if ( !empty ($opt['private_key']) ) {
58
+ $this->options['private_key'] = $opt['private_key'];
59
+
60
+ $this->keys = true;
61
+ } elseif ( empty ($opt['username']) ) {
62
+ $this->errors->add('empty_username', __('SSH2 username is required'));
63
+ }
64
+
65
+ if ( !empty($opt['username']) )
66
+ $this->options['username'] = $opt['username'];
67
+
68
+ if ( empty ($opt['password']) ) {
69
+ if ( !$this->keys ) //password can be blank if we are using keys
70
+ $this->errors->add('empty_password', __('SSH2 password is required'));
71
+ } else {
72
+ $this->options['password'] = $opt['password'];
73
+
74
+ $this->password = true;
75
+ }
76
+ }
77
+
78
+ function connect() {
79
+ $this->link = new Net_SFTP($this->options['hostname'], $this->options['port']);
80
+
81
+ if ( ! $this->link ) {
82
+ $this->errors->add('connect', sprintf(__('Failed to connect to SSH2 Server %1$s:%2$s'), $this->options['hostname'], $this->options['port']));
83
+ return false;
84
+ }
85
+
86
+ if ( !$this->keys ) {
87
+ if ( ! $this->link->login($this->options['username'], $this->options['password']) ) {
88
+ $this->errors->add('auth', sprintf(__('Username/Password incorrect for %s'), $this->options['username']));
89
+ return false;
90
+ }
91
+ } else {
92
+ $rsa = new Crypt_RSA();
93
+ if ( $this->password ) {
94
+ $rsa->setPassword($this->options['password']);
95
+ }
96
+ $rsa->loadKey($this->options['private_key']);
97
+ if ( ! $this->link->login($this->options['username'], $rsa ) ) {
98
+ $this->errors->add('auth', sprintf(__('Private key incorrect for %s'), $this->options['username']));
99
+ return false;
100
+ }
101
+ }
102
+
103
+ return true;
104
+ }
105
+
106
+ function run_command( $command, $returnbool = false) {
107
+
108
+ if ( ! $this->link )
109
+ return false;
110
+
111
+ $data = $this->link->exec($command);
112
+
113
+ if ( $returnbool )
114
+ return ( $data === false ) ? false : '' != trim($data);
115
+ else
116
+ return $data;
117
+ }
118
+
119
+ function get_contents($file, $type = '', $resumepos = 0 ) {
120
+ return $this->link->get($file);
121
+ }
122
+
123
+ function get_contents_array($file) {
124
+ $lines = preg_split('#(\r\n|\r|\n)#', $this->link->get($file), -1, PREG_SPLIT_DELIM_CAPTURE);
125
+ $newLines = array();
126
+ for ($i = 0; $i < count($lines); $i+= 2)
127
+ $newLines[] = $lines[$i] . $lines[$i + 1];
128
+ return $newLines;
129
+ }
130
+
131
+ function put_contents($file, $contents, $mode = false ) {
132
+ $ret = $this->link->put($file, $contents);
133
+
134
+ $this->chmod($file, $mode);
135
+
136
+ return false !== $ret;
137
+ }
138
+
139
+ function cwd() {
140
+ $cwd = $this->run_command('pwd');
141
+ if ( $cwd )
142
+ $cwd = trailingslashit($cwd);
143
+ return $cwd;
144
+ }
145
+
146
+ function chdir($dir) {
147
+ $this->list->chdir($dir);
148
+ return $this->run_command('cd ' . $dir, true);
149
+ }
150
+
151
+ function chgrp($file, $group, $recursive = false ) {
152
+ if ( ! $this->exists($file) )
153
+ return false;
154
+ if ( ! $recursive || ! $this->is_dir($file) )
155
+ return $this->run_command(sprintf('chgrp %o %s', $mode, escapeshellarg($file)), true);
156
+ return $this->run_command(sprintf('chgrp -R %o %s', $mode, escapeshellarg($file)), true);
157
+ }
158
+
159
+ function chmod($file, $mode = false, $recursive = false) {
160
+ return $mode === false ? false : $this->link->chmod($mode, $file, $recursive);
161
+ }
162
+
163
+ function chown($file, $owner, $recursive = false ) {
164
+ if ( ! $this->exists($file) )
165
+ return false;
166
+ if ( ! $recursive || ! $this->is_dir($file) )
167
+ return $this->run_command(sprintf('chown %o %s', $mode, escapeshellarg($file)), true);
168
+ return $this->run_command(sprintf('chown -R %o %s', $mode, escapeshellarg($file)), true);
169
+ }
170
+
171
+ function owner($file, $owneruid = false) {
172
+ if ($owneruid === false) {
173
+ $result = $this->link->stat($file);
174
+ $owneruid = $result['uid'];
175
+ }
176
+
177
+ if ( ! $owneruid )
178
+ return false;
179
+ if ( ! function_exists('posix_getpwuid') )
180
+ return $owneruid;
181
+ $ownerarray = posix_getpwuid($owneruid);
182
+ return $ownerarray['name'];
183
+ }
184
+
185
+ function getchmod($file) {
186
+ $result = $this->link->stat($file);
187
+
188
+ return substr(decoct($result['permissions']),3);
189
+ }
190
+
191
+ function group($file, $gid = false) {
192
+ if ($gid === false) {
193
+ $result = $this->link->stat($file);
194
+ $gid = $result['gid'];
195
+ }
196
+
197
+ if ( ! $gid )
198
+ return false;
199
+ if ( ! function_exists('posix_getgrgid') )
200
+ return $gid;
201
+ $grouparray = posix_getgrgid($gid);
202
+ return $grouparray['name'];
203
+ }
204
+
205
+ function copy($source, $destination, $overwrite = false, $mode = false) {
206
+ if ( ! $overwrite && $this->exists($destination) )
207
+ return false;
208
+ $content = $this->get_contents($source);
209
+ if ( false === $content)
210
+ return false;
211
+ return $this->put_contents($destination, $content, $mode);
212
+ }
213
+
214
+ function move($source, $destination, $overwrite = false) {
215
+ return $this->link->rename($source, $destination);
216
+ }
217
+
218
+ function delete($file, $recursive = false) {
219
+ return $this->link->delete($file, $recursive);
220
+ }
221
+
222
+ function exists($file) {
223
+ return $this->link->stat($file) !== false;
224
+ }
225
+
226
+ function is_file($file) {
227
+ $result = $this->link->stat($file);
228
+ return $result['type'] == NET_SFTP_TYPE_REGULAR;
229
+ }
230
+
231
+ function is_dir($path) {
232
+ $result = $this->link->stat($path);
233
+ return $result['type'] == NET_SFTP_TYPE_DIRECTORY;
234
+ }
235
+
236
+ function is_readable($file) {
237
+ return true;
238
+
239
+ return is_readable('ssh2.sftp://' . $this->sftp_link . '/' . $file);
240
+ }
241
+
242
+ function is_writable($file) {
243
+ return true;
244
+
245
+ return is_writable('ssh2.sftp://' . $this->sftp_link . '/' . $file);
246
+ }
247
+
248
+ function atime($file) {
249
+ $result = $this->link->stat($file);
250
+ return $result['atime'];
251
+ }
252
+
253
+ function mtime($file) {
254
+ $result = $this->link->stat($file);
255
+ return $result['mtime'];
256
+ }
257
+
258
+ function size($file) {
259
+ $result = $this->link->stat($file);
260
+ return $result['size'];
261
+ }
262
+
263
+ function touch($file, $time = 0, $atime = 0) {
264
+ //Not implmented.
265
+ }
266
+
267
+ function mkdir($path, $chmod = false, $chown = false, $chgrp = false) {
268
+ $path = untrailingslashit($path);
269
+ if ( ! $chmod )
270
+ $chmod = FS_CHMOD_DIR;
271
+ //if ( ! ssh2_sftp_mkdir($this->sftp_link, $path, $chmod, true) )
272
+ // return false;
273
+ if ( ! $this->link->mkdir($path) && $this->link->chmod($chmod, $path) )
274
+ return false;
275
+ if ( $chown )
276
+ $this->chown($path, $chown);
277
+ if ( $chgrp )
278
+ $this->chgrp($path, $chgrp);
279
+ return true;
280
+ }
281
+
282
+ function rmdir($path, $recursive = false) {
283
+ return $this->delete($path, $recursive);
284
+ }
285
+
286
+ function dirlist($path, $include_hidden = true, $recursive = false) {
287
+ if ( $this->is_file($path) ) {
288
+ $limit_file = basename($path);
289
+ $path = dirname($path);
290
+ } else {
291
+ $limit_file = false;
292
+ }
293
+
294
+ if ( ! $this->is_dir($path) )
295
+ return false;
296
+
297
+ $ret = array();
298
+ $entries = $this->link->rawlist($path);
299
+
300
+ foreach ($entries as $name => $entry) {
301
+ $struc = array();
302
+ $struc['name'] = $name;
303
+
304
+ if ( '.' == $struc['name'] || '..' == $struc['name'] )
305
+ continue; //Do not care about these folders.
306
+
307
+ if ( ! $include_hidden && '.' == $struc['name'][0] )
308
+ continue;
309
+
310
+ if ( $limit_file && $struc['name'] != $limit_file )
311
+ continue;
312
+
313
+ $struc['perms'] = $entry['permissions'];
314
+ $struc['permsn'] = $this->getnumchmodfromh($struc['perms']);
315
+ $struc['number'] = false;
316
+ $struc['owner'] = $this->owner($path.'/'.$entry, $entry['uid']);
317
+ $struc['group'] = $this->group($path.'/'.$entry, $entry['gid']);
318
+ $struc['size'] = $entry['size'];//$this->size($path.'/'.$entry);
319
+ $struc['lastmodunix']= $entry['mtime'];//$this->mtime($path.'/'.$entry);
320
+ $struc['lastmod'] = date('M j',$struc['lastmodunix']);
321
+ $struc['time'] = date('h:i:s',$struc['lastmodunix']);
322
+ $struc['type'] = $entry['type'] == NET_SFTP_TYPE_DIRECTORY ? 'd' : 'f';
323
+
324
+ if ( 'd' == $struc['type'] ) {
325
+ if ( $recursive )
326
+ $struc['files'] = $this->dirlist($path . '/' . $struc['name'], $include_hidden, $recursive);
327
+ else
328
+ $struc['files'] = array();
329
+ }
330
+
331
+ $ret[ $struc['name'] ] = $struc;
332
+ }
333
+ return $ret;
334
+ }
335
+ }
phpseclib/Crypt/AES.php ADDED
@@ -0,0 +1,612 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP implementation of AES.
6
+ *
7
+ * Uses mcrypt, if available, and an internal implementation, otherwise.
8
+ *
9
+ * PHP versions 4 and 5
10
+ *
11
+ * If {@link Crypt_AES::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
12
+ * {@link Crypt_AES::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's 136-bits
13
+ * it'll be null-padded to 160-bits and 160 bits will be the key length until {@link Crypt_Rijndael::setKey() setKey()}
14
+ * is called, again, at which point, it'll be recalculated.
15
+ *
16
+ * Since Crypt_AES extends Crypt_Rijndael, some functions are available to be called that, in the context of AES, don't
17
+ * make a whole lot of sense. {@link Crypt_AES::setBlockLength() setBlockLength()}, for instance. Calling that function,
18
+ * however possible, won't do anything (AES has a fixed block length whereas Rijndael has a variable one).
19
+ *
20
+ * Here's a short example of how to use this library:
21
+ * <code>
22
+ * <?php
23
+ * include('Crypt/AES.php');
24
+ *
25
+ * $aes = new Crypt_AES();
26
+ *
27
+ * $aes->setKey('abcdefghijklmnop');
28
+ *
29
+ * $size = 10 * 1024;
30
+ * $plaintext = '';
31
+ * for ($i = 0; $i < $size; $i++) {
32
+ * $plaintext.= 'a';
33
+ * }
34
+ *
35
+ * echo $aes->decrypt($aes->encrypt($plaintext));
36
+ * ?>
37
+ * </code>
38
+ *
39
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
40
+ * of this software and associated documentation files (the "Software"), to deal
41
+ * in the Software without restriction, including without limitation the rights
42
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
43
+ * copies of the Software, and to permit persons to whom the Software is
44
+ * furnished to do so, subject to the following conditions:
45
+ *
46
+ * The above copyright notice and this permission notice shall be included in
47
+ * all copies or substantial portions of the Software.
48
+ *
49
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
50
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
51
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
52
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
53
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
54
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
55
+ * THE SOFTWARE.
56
+ *
57
+ * @category Crypt
58
+ * @package Crypt_AES
59
+ * @author Jim Wigginton <terrafrost@php.net>
60
+ * @copyright MMVIII Jim Wigginton
61
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
62
+ * @version $Id: AES.php,v 1.7 2010/02/09 06:10:25 terrafrost Exp $
63
+ * @link http://phpseclib.sourceforge.net
64
+ */
65
+
66
+ /**
67
+ * Include Crypt_Rijndael
68
+ */
69
+ require_once 'Rijndael.php';
70
+
71
+ /**#@+
72
+ * @access public
73
+ * @see Crypt_AES::encrypt()
74
+ * @see Crypt_AES::decrypt()
75
+ */
76
+ /**
77
+ * Encrypt / decrypt using the Counter mode.
78
+ *
79
+ * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
80
+ *
81
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
82
+ */
83
+ define('CRYPT_AES_MODE_CTR', -1);
84
+ /**
85
+ * Encrypt / decrypt using the Electronic Code Book mode.
86
+ *
87
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
88
+ */
89
+ define('CRYPT_AES_MODE_ECB', 1);
90
+ /**
91
+ * Encrypt / decrypt using the Code Book Chaining mode.
92
+ *
93
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
94
+ */
95
+ define('CRYPT_AES_MODE_CBC', 2);
96
+ /**
97
+ * Encrypt / decrypt using the Cipher Feedback mode.
98
+ *
99
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
100
+ */
101
+ define('CRYPT_AES_MODE_CFB', 3);
102
+ /**
103
+ * Encrypt / decrypt using the Cipher Feedback mode.
104
+ *
105
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
106
+ */
107
+ define('CRYPT_AES_MODE_OFB', 4);
108
+ /**#@-*/
109
+
110
+ /**#@+
111
+ * @access private
112
+ * @see Crypt_AES::Crypt_AES()
113
+ */
114
+ /**
115
+ * Toggles the internal implementation
116
+ */
117
+ define('CRYPT_AES_MODE_INTERNAL', 1);
118
+ /**
119
+ * Toggles the mcrypt implementation
120
+ */
121
+ define('CRYPT_AES_MODE_MCRYPT', 2);
122
+ /**#@-*/
123
+
124
+ /**
125
+ * Pure-PHP implementation of AES.
126
+ *
127
+ * @author Jim Wigginton <terrafrost@php.net>
128
+ * @version 0.1.0
129
+ * @access public
130
+ * @package Crypt_AES
131
+ */
132
+ class Crypt_AES extends Crypt_Rijndael {
133
+ /**
134
+ * mcrypt resource for encryption
135
+ *
136
+ * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
137
+ * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
138
+ *
139
+ * @see Crypt_AES::encrypt()
140
+ * @var String
141
+ * @access private
142
+ */
143
+ var $enmcrypt;
144
+
145
+ /**
146
+ * mcrypt resource for decryption
147
+ *
148
+ * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
149
+ * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
150
+ *
151
+ * @see Crypt_AES::decrypt()
152
+ * @var String
153
+ * @access private
154
+ */
155
+ var $demcrypt;
156
+
157
+ /**
158
+ * mcrypt resource for CFB mode
159
+ *
160
+ * @see Crypt_AES::encrypt()
161
+ * @see Crypt_AES::decrypt()
162
+ * @var String
163
+ * @access private
164
+ */
165
+ var $ecb;
166
+
167
+ /**
168
+ * Default Constructor.
169
+ *
170
+ * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
171
+ * CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC. If not explictly set, CRYPT_AES_MODE_CBC will be used.
172
+ *
173
+ * @param optional Integer $mode
174
+ * @return Crypt_AES
175
+ * @access public
176
+ */
177
+ function Crypt_AES($mode = CRYPT_AES_MODE_CBC)
178
+ {
179
+ if ( !defined('CRYPT_AES_MODE') ) {
180
+ switch (true) {
181
+ case extension_loaded('mcrypt'):
182
+ // i'd check to see if aes was supported, by doing in_array('des', mcrypt_list_algorithms('')),
183
+ // but since that can be changed after the object has been created, there doesn't seem to be
184
+ // a lot of point...
185
+ define('CRYPT_AES_MODE', CRYPT_AES_MODE_MCRYPT);
186
+ break;
187
+ default:
188
+ define('CRYPT_AES_MODE', CRYPT_AES_MODE_INTERNAL);
189
+ }
190
+ }
191
+
192
+ switch ( CRYPT_AES_MODE ) {
193
+ case CRYPT_AES_MODE_MCRYPT:
194
+ switch ($mode) {
195
+ case CRYPT_AES_MODE_ECB:
196
+ $this->paddable = true;
197
+ $this->mode = MCRYPT_MODE_ECB;
198
+ break;
199
+ case CRYPT_AES_MODE_CTR:
200
+ // ctr doesn't have a constant associated with it even though it appears to be fairly widely
201
+ // supported. in lieu of knowing just how widely supported it is, i've, for now, opted not to
202
+ // include a compatibility layer. the layer has been implemented but, for now, is commented out.
203
+ $this->mode = 'ctr';
204
+ //$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_AES_MODE_CTR;
205
+ break;
206
+ case CRYPT_AES_MODE_CFB:
207
+ $this->mode = 'ncfb';
208
+ break;
209
+ case CRYPT_AES_MODE_OFB:
210
+ $this->mode = MCRYPT_MODE_NOFB;
211
+ break;
212
+ case CRYPT_AES_MODE_CBC:
213
+ default:
214
+ $this->paddable = true;
215
+ $this->mode = MCRYPT_MODE_CBC;
216
+ }
217
+
218
+ $this->debuffer = $this->enbuffer = '';
219
+
220
+ break;
221
+ default:
222
+ switch ($mode) {
223
+ case CRYPT_AES_MODE_ECB:
224
+ $this->paddable = true;
225
+ $this->mode = CRYPT_RIJNDAEL_MODE_ECB;
226
+ break;
227
+ case CRYPT_AES_MODE_CTR:
228
+ $this->mode = CRYPT_RIJNDAEL_MODE_CTR;
229
+ break;
230
+ case CRYPT_AES_MODE_CFB:
231
+ $this->mode = CRYPT_RIJNDAEL_MODE_CFB;
232
+ break;
233
+ case CRYPT_AES_MODE_OFB:
234
+ $this->mode = CRYPT_RIJNDAEL_MODE_OFB;
235
+ break;
236
+ case CRYPT_AES_MODE_CBC:
237
+ default:
238
+ $this->paddable = true;
239
+ $this->mode = CRYPT_RIJNDAEL_MODE_CBC;
240
+ }
241
+ }
242
+
243
+ if (CRYPT_AES_MODE == CRYPT_AES_MODE_INTERNAL) {
244
+ parent::Crypt_Rijndael($this->mode);
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Dummy function
250
+ *
251
+ * Since Crypt_AES extends Crypt_Rijndael, this function is, technically, available, but it doesn't do anything.
252
+ *
253
+ * @access public
254
+ * @param Integer $length
255
+ */
256
+ function setBlockLength($length)
257
+ {
258
+ return;
259
+ }
260
+
261
+
262
+ /**
263
+ * Sets the initialization vector. (optional)
264
+ *
265
+ * SetIV is not required when CRYPT_RIJNDAEL_MODE_ECB is being used. If not explictly set, it'll be assumed
266
+ * to be all zero's.
267
+ *
268
+ * @access public
269
+ * @param String $iv
270
+ */
271
+ function setIV($iv)
272
+ {
273
+ parent::setIV($iv);
274
+ if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
275
+ $this->changed = true;
276
+ }
277
+ }
278
+
279
+ /**
280
+ * Encrypts a message.
281
+ *
282
+ * $plaintext will be padded with up to 16 additional bytes. Other AES implementations may or may not pad in the
283
+ * same manner. Other common approaches to padding and the reasons why it's necessary are discussed in the following
284
+ * URL:
285
+ *
286
+ * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
287
+ *
288
+ * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does.
289
+ * strlen($plaintext) will still need to be a multiple of 16, however, arbitrary values can be added to make it that
290
+ * length.
291
+ *
292
+ * @see Crypt_AES::decrypt()
293
+ * @access public
294
+ * @param String $plaintext
295
+ */
296
+ function encrypt($plaintext)
297
+ {
298
+ if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
299
+ $changed = $this->changed;
300
+ $this->_mcryptSetup();
301
+ /*
302
+ if ($this->mode == CRYPT_AES_MODE_CTR) {
303
+ $iv = $this->encryptIV;
304
+ $xor = mcrypt_generic($this->enmcrypt, $this->_generate_xor(strlen($plaintext), $iv));
305
+ $ciphertext = $plaintext ^ $xor;
306
+ if ($this->continuousBuffer) {
307
+ $this->encryptIV = $iv;
308
+ }
309
+ return $ciphertext;
310
+ }
311
+ */
312
+ // re: http://phpseclib.sourceforge.net/cfb-demo.phps
313
+ // using mcrypt's default handing of CFB the above would output two different things. using phpseclib's
314
+ // rewritten CFB implementation the above outputs the same thing twice.
315
+ if ($this->mode == 'ncfb') {
316
+ if ($changed) {
317
+ $this->ecb = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
318
+ mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
319
+ }
320
+
321
+ if (strlen($this->enbuffer)) {
322
+ $ciphertext = $plaintext ^ substr($this->encryptIV, strlen($this->enbuffer));
323
+ $this->enbuffer.= $ciphertext;
324
+ if (strlen($this->enbuffer) == 16) {
325
+ $this->encryptIV = $this->enbuffer;
326
+ $this->enbuffer = '';
327
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
328
+ }
329
+ $plaintext = substr($plaintext, strlen($ciphertext));
330
+ } else {
331
+ $ciphertext = '';
332
+ }
333
+
334
+ $last_pos = strlen($plaintext) & 0xFFFFFFF0;
335
+ $ciphertext.= $last_pos ? mcrypt_generic($this->enmcrypt, substr($plaintext, 0, $last_pos)) : '';
336
+
337
+ if (strlen($plaintext) & 0xF) {
338
+ if (strlen($ciphertext)) {
339
+ $this->encryptIV = substr($ciphertext, -16);
340
+ }
341
+ $this->encryptIV = mcrypt_generic($this->ecb, $this->encryptIV);
342
+ $this->enbuffer = substr($plaintext, $last_pos) ^ $this->encryptIV;
343
+ $ciphertext.= $this->enbuffer;
344
+ }
345
+
346
+ return $ciphertext;
347
+ }
348
+
349
+ if ($this->paddable) {
350
+ $plaintext = $this->_pad($plaintext);
351
+ }
352
+
353
+ $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
354
+
355
+ if (!$this->continuousBuffer) {
356
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
357
+ }
358
+
359
+ return $ciphertext;
360
+ }
361
+
362
+ return parent::encrypt($plaintext);
363
+ }
364
+
365
+ /**
366
+ * Decrypts a message.
367
+ *
368
+ * If strlen($ciphertext) is not a multiple of 16, null bytes will be added to the end of the string until it is.
369
+ *
370
+ * @see Crypt_AES::encrypt()
371
+ * @access public
372
+ * @param String $ciphertext
373
+ */
374
+ function decrypt($ciphertext)
375
+ {
376
+ if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
377
+ $changed = $this->changed;
378
+ $this->_mcryptSetup();
379
+ /*
380
+ if ($this->mode == CRYPT_AES_MODE_CTR) {
381
+ $iv = $this->decryptIV;
382
+ $xor = mcrypt_generic($this->enmcrypt, $this->_generate_xor(strlen($ciphertext), $iv));
383
+ $plaintext = $ciphertext ^ $xor;
384
+ if ($this->continuousBuffer) {
385
+ $this->decryptIV = $iv;
386
+ }
387
+ return $plaintext;
388
+ }
389
+ */
390
+ if ($this->mode == 'ncfb') {
391
+ if ($changed) {
392
+ $this->ecb = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
393
+ mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
394
+ }
395
+
396
+ if (strlen($this->debuffer)) {
397
+ $plaintext = $ciphertext ^ substr($this->decryptIV, strlen($this->debuffer));
398
+
399
+ $this->debuffer.= substr($ciphertext, 0, strlen($plaintext));
400
+ if (strlen($this->debuffer) == 16) {
401
+ $this->decryptIV = $this->debuffer;
402
+ $this->debuffer = '';
403
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
404
+ }
405
+ $ciphertext = substr($ciphertext, strlen($plaintext));
406
+ } else {
407
+ $plaintext = '';
408
+ }
409
+
410
+ $last_pos = strlen($ciphertext) & 0xFFFFFFF0;
411
+ $plaintext.= $last_pos ? mdecrypt_generic($this->demcrypt, substr($ciphertext, 0, $last_pos)) : '';
412
+
413
+ if (strlen($ciphertext) & 0xF) {
414
+ if (strlen($plaintext)) {
415
+ $this->decryptIV = substr($ciphertext, $last_pos - 16, 16);
416
+ }
417
+ $this->decryptIV = mcrypt_generic($this->ecb, $this->decryptIV);
418
+ $this->debuffer = substr($ciphertext, $last_pos);
419
+ $plaintext.= $this->debuffer ^ $this->decryptIV;
420
+ }
421
+
422
+ return $plaintext;
423
+ }
424
+
425
+ if ($this->paddable) {
426
+ // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
427
+ // "The data is padded with "\0" to make sure the length of the data is n * blocksize."
428
+ $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 15) & 0xFFFFFFF0, chr(0));
429
+ }
430
+
431
+ $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
432
+
433
+ if (!$this->continuousBuffer) {
434
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
435
+ }
436
+
437
+ return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
438
+ }
439
+
440
+ return parent::decrypt($ciphertext);
441
+ }
442
+
443
+ /**
444
+ * Setup mcrypt
445
+ *
446
+ * Validates all the variables.
447
+ *
448
+ * @access private
449
+ */
450
+ function _mcryptSetup()
451
+ {
452
+ if (!$this->changed) {
453
+ return;
454
+ }
455
+
456
+ if (!$this->explicit_key_length) {
457
+ // this just copied from Crypt_Rijndael::_setup()
458
+ $length = strlen($this->key) >> 2;
459
+ if ($length > 8) {
460
+ $length = 8;
461
+ } else if ($length < 4) {
462
+ $length = 4;
463
+ }
464
+ $this->Nk = $length;
465
+ $this->key_size = $length << 2;
466
+ }
467
+
468
+ switch ($this->Nk) {
469
+ case 4: // 128
470
+ $this->key_size = 16;
471
+ break;
472
+ case 5: // 160
473
+ case 6: // 192
474
+ $this->key_size = 24;
475
+ break;
476
+ case 7: // 224
477
+ case 8: // 256
478
+ $this->key_size = 32;
479
+ }
480
+
481
+ $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, chr(0));
482
+ $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, 16), 16, chr(0));
483
+
484
+ if (!isset($this->enmcrypt)) {
485
+ $mode = $this->mode;
486
+ //$mode = $this->mode == CRYPT_AES_MODE_CTR ? MCRYPT_MODE_ECB : $this->mode;
487
+
488
+ $this->demcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');
489
+ $this->enmcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');
490
+ } // else should mcrypt_generic_deinit be called?
491
+
492
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
493
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
494
+
495
+ $this->changed = false;
496
+ }
497
+
498
+ /**
499
+ * Encrypts a block
500
+ *
501
+ * Optimized over Crypt_Rijndael's implementation by means of loop unrolling.
502
+ *
503
+ * @see Crypt_Rijndael::_encryptBlock()
504
+ * @access private
505
+ * @param String $in
506
+ * @return String
507
+ */
508
+ function _encryptBlock($in)
509
+ {
510
+ $state = unpack('N*word', $in);
511
+
512
+ $Nr = $this->Nr;
513
+ $w = $this->w;
514
+ $t0 = $this->t0;
515
+ $t1 = $this->t1;
516
+ $t2 = $this->t2;
517
+ $t3 = $this->t3;
518
+
519
+ // addRoundKey and reindex $state
520
+ $state = array(
521
+ $state['word1'] ^ $w[0][0],
522
+ $state['word2'] ^ $w[0][1],
523
+ $state['word3'] ^ $w[0][2],
524
+ $state['word4'] ^ $w[0][3]
525
+ );
526
+
527
+ // shiftRows + subWord + mixColumns + addRoundKey
528
+ // we could loop unroll this and use if statements to do more rounds as necessary, but, in my tests, that yields
529
+ // only a marginal improvement. since that also, imho, hinders the readability of the code, i've opted not to do it.
530
+ for ($round = 1; $round < $this->Nr; $round++) {
531
+ $state = array(
532
+ $t0[$state[0] & 0xFF000000] ^ $t1[$state[1] & 0x00FF0000] ^ $t2[$state[2] & 0x0000FF00] ^ $t3[$state[3] & 0x000000FF] ^ $w[$round][0],
533
+ $t0[$state[1] & 0xFF000000] ^ $t1[$state[2] & 0x00FF0000] ^ $t2[$state[3] & 0x0000FF00] ^ $t3[$state[0] & 0x000000FF] ^ $w[$round][1],
534
+ $t0[$state[2] & 0xFF000000] ^ $t1[$state[3] & 0x00FF0000] ^ $t2[$state[0] & 0x0000FF00] ^ $t3[$state[1] & 0x000000FF] ^ $w[$round][2],
535
+ $t0[$state[3] & 0xFF000000] ^ $t1[$state[0] & 0x00FF0000] ^ $t2[$state[1] & 0x0000FF00] ^ $t3[$state[2] & 0x000000FF] ^ $w[$round][3]
536
+ );
537
+
538
+ }
539
+
540
+ // subWord
541
+ $state = array(
542
+ $this->_subWord($state[0]),
543
+ $this->_subWord($state[1]),
544
+ $this->_subWord($state[2]),
545
+ $this->_subWord($state[3])
546
+ );
547
+
548
+ // shiftRows + addRoundKey
549
+ $state = array(
550
+ ($state[0] & 0xFF000000) ^ ($state[1] & 0x00FF0000) ^ ($state[2] & 0x0000FF00) ^ ($state[3] & 0x000000FF) ^ $this->w[$this->Nr][0],
551
+ ($state[1] & 0xFF000000) ^ ($state[2] & 0x00FF0000) ^ ($state[3] & 0x0000FF00) ^ ($state[0] & 0x000000FF) ^ $this->w[$this->Nr][1],
552
+ ($state[2] & 0xFF000000) ^ ($state[3] & 0x00FF0000) ^ ($state[0] & 0x0000FF00) ^ ($state[1] & 0x000000FF) ^ $this->w[$this->Nr][2],
553
+ ($state[3] & 0xFF000000) ^ ($state[0] & 0x00FF0000) ^ ($state[1] & 0x0000FF00) ^ ($state[2] & 0x000000FF) ^ $this->w[$this->Nr][3]
554
+ );
555
+
556
+ return pack('N*', $state[0], $state[1], $state[2], $state[3]);
557
+ }
558
+
559
+ /**
560
+ * Decrypts a block
561
+ *
562
+ * Optimized over Crypt_Rijndael's implementation by means of loop unrolling.
563
+ *
564
+ * @see Crypt_Rijndael::_decryptBlock()
565
+ * @access private
566
+ * @param String $in
567
+ * @return String
568
+ */
569
+ function _decryptBlock($in)
570
+ {
571
+ $state = unpack('N*word', $in);
572
+
573
+ $Nr = $this->Nr;
574
+ $dw = $this->dw;
575
+ $dt0 = $this->dt0;
576
+ $dt1 = $this->dt1;
577
+ $dt2 = $this->dt2;
578
+ $dt3 = $this->dt3;
579
+
580
+ // addRoundKey and reindex $state
581
+ $state = array(
582
+ $state['word1'] ^ $dw[$this->Nr][0],
583
+ $state['word2'] ^ $dw[$this->Nr][1],
584
+ $state['word3'] ^ $dw[$this->Nr][2],
585
+ $state['word4'] ^ $dw[$this->Nr][3]
586
+ );
587
+
588
+
589
+ // invShiftRows + invSubBytes + invMixColumns + addRoundKey
590
+ for ($round = $this->Nr - 1; $round > 0; $round--) {
591
+ $state = array(
592
+ $dt0[$state[0] & 0xFF000000] ^ $dt1[$state[3] & 0x00FF0000] ^ $dt2[$state[2] & 0x0000FF00] ^ $dt3[$state[1] & 0x000000FF] ^ $dw[$round][0],
593
+ $dt0[$state[1] & 0xFF000000] ^ $dt1[$state[0] & 0x00FF0000] ^ $dt2[$state[3] & 0x0000FF00] ^ $dt3[$state[2] & 0x000000FF] ^ $dw[$round][1],
594
+ $dt0[$state[2] & 0xFF000000] ^ $dt1[$state[1] & 0x00FF0000] ^ $dt2[$state[0] & 0x0000FF00] ^ $dt3[$state[3] & 0x000000FF] ^ $dw[$round][2],
595
+ $dt0[$state[3] & 0xFF000000] ^ $dt1[$state[2] & 0x00FF0000] ^ $dt2[$state[1] & 0x0000FF00] ^ $dt3[$state[0] & 0x000000FF] ^ $dw[$round][3]
596
+ );
597
+ }
598
+
599
+ // invShiftRows + invSubWord + addRoundKey
600
+ $state = array(
601
+ $this->_invSubWord(($state[0] & 0xFF000000) ^ ($state[3] & 0x00FF0000) ^ ($state[2] & 0x0000FF00) ^ ($state[1] & 0x000000FF)) ^ $dw[0][0],
602
+ $this->_invSubWord(($state[1] & 0xFF000000) ^ ($state[0] & 0x00FF0000) ^ ($state[3] & 0x0000FF00) ^ ($state[2] & 0x000000FF)) ^ $dw[0][1],
603
+ $this->_invSubWord(($state[2] & 0xFF000000) ^ ($state[1] & 0x00FF0000) ^ ($state[0] & 0x0000FF00) ^ ($state[3] & 0x000000FF)) ^ $dw[0][2],
604
+ $this->_invSubWord(($state[3] & 0xFF000000) ^ ($state[2] & 0x00FF0000) ^ ($state[1] & 0x0000FF00) ^ ($state[0] & 0x000000FF)) ^ $dw[0][3]
605
+ );
606
+
607
+ return pack('N*', $state[0], $state[1], $state[2], $state[3]);
608
+ }
609
+ }
610
+
611
+ // vim: ts=4:sw=4:et:
612
+ // vim6: fdl=1:
phpseclib/Crypt/DES.php ADDED
@@ -0,0 +1,1298 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP implementation of DES.
6
+ *
7
+ * Uses mcrypt, if available, and an internal implementation, otherwise.
8
+ *
9
+ * PHP versions 4 and 5
10
+ *
11
+ * Useful resources are as follows:
12
+ *
13
+ * - {@link http://en.wikipedia.org/wiki/DES_supplementary_material Wikipedia: DES supplementary material}
14
+ * - {@link http://www.itl.nist.gov/fipspubs/fip46-2.htm FIPS 46-2 - (DES), Data Encryption Standard}
15
+ * - {@link http://www.cs.eku.edu/faculty/styer/460/Encrypt/JS-DES.html JavaScript DES Example}
16
+ *
17
+ * Here's a short example of how to use this library:
18
+ * <code>
19
+ * <?php
20
+ * include('Crypt/DES.php');
21
+ *
22
+ * $des = new Crypt_DES();
23
+ *
24
+ * $des->setKey('abcdefgh');
25
+ *
26
+ * $size = 10 * 1024;
27
+ * $plaintext = '';
28
+ * for ($i = 0; $i < $size; $i++) {
29
+ * $plaintext.= 'a';
30
+ * }
31
+ *
32
+ * echo $des->decrypt($des->encrypt($plaintext));
33
+ * ?>
34
+ * </code>
35
+ *
36
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
37
+ * of this software and associated documentation files (the "Software"), to deal
38
+ * in the Software without restriction, including without limitation the rights
39
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
40
+ * copies of the Software, and to permit persons to whom the Software is
41
+ * furnished to do so, subject to the following conditions:
42
+ *
43
+ * The above copyright notice and this permission notice shall be included in
44
+ * all copies or substantial portions of the Software.
45
+ *
46
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
47
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
48
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
49
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
50
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
51
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
52
+ * THE SOFTWARE.
53
+ *
54
+ * @category Crypt
55
+ * @package Crypt_DES
56
+ * @author Jim Wigginton <terrafrost@php.net>
57
+ * @copyright MMVII Jim Wigginton
58
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
59
+ * @version $Id: DES.php,v 1.12 2010/02/09 06:10:26 terrafrost Exp $
60
+ * @link http://phpseclib.sourceforge.net
61
+ */
62
+
63
+ /**#@+
64
+ * @access private
65
+ * @see Crypt_DES::_prepareKey()
66
+ * @see Crypt_DES::_processBlock()
67
+ */
68
+ /**
69
+ * Contains array_reverse($keys[CRYPT_DES_DECRYPT])
70
+ */
71
+ define('CRYPT_DES_ENCRYPT', 0);
72
+ /**
73
+ * Contains array_reverse($keys[CRYPT_DES_ENCRYPT])
74
+ */
75
+ define('CRYPT_DES_DECRYPT', 1);
76
+ /**#@-*/
77
+
78
+ /**#@+
79
+ * @access public
80
+ * @see Crypt_DES::encrypt()
81
+ * @see Crypt_DES::decrypt()
82
+ */
83
+ /**
84
+ * Encrypt / decrypt using the Counter mode.
85
+ *
86
+ * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
87
+ *
88
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
89
+ */
90
+ define('CRYPT_DES_MODE_CTR', -1);
91
+ /**
92
+ * Encrypt / decrypt using the Electronic Code Book mode.
93
+ *
94
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
95
+ */
96
+ define('CRYPT_DES_MODE_ECB', 1);
97
+ /**
98
+ * Encrypt / decrypt using the Code Book Chaining mode.
99
+ *
100
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
101
+ */
102
+ define('CRYPT_DES_MODE_CBC', 2);
103
+ /**
104
+ * Encrypt / decrypt using the Cipher Feedback mode.
105
+ *
106
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
107
+ */
108
+ define('CRYPT_DES_MODE_CFB', 3);
109
+ /**
110
+ * Encrypt / decrypt using the Cipher Feedback mode.
111
+ *
112
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
113
+ */
114
+ define('CRYPT_DES_MODE_OFB', 4);
115
+ /**#@-*/
116
+
117
+ /**#@+
118
+ * @access private
119
+ * @see Crypt_DES::Crypt_DES()
120
+ */
121
+ /**
122
+ * Toggles the internal implementation
123
+ */
124
+ define('CRYPT_DES_MODE_INTERNAL', 1);
125
+ /**
126
+ * Toggles the mcrypt implementation
127
+ */
128
+ define('CRYPT_DES_MODE_MCRYPT', 2);
129
+ /**#@-*/
130
+
131
+ /**
132
+ * Pure-PHP implementation of DES.
133
+ *
134
+ * @author Jim Wigginton <terrafrost@php.net>
135
+ * @version 0.1.0
136
+ * @access public
137
+ * @package Crypt_DES
138
+ */
139
+ class Crypt_DES {
140
+ /**
141
+ * The Key Schedule
142
+ *
143
+ * @see Crypt_DES::setKey()
144
+ * @var Array
145
+ * @access private
146
+ */
147
+ var $keys = "\0\0\0\0\0\0\0\0";
148
+
149
+ /**
150
+ * The Encryption Mode
151
+ *
152
+ * @see Crypt_DES::Crypt_DES()
153
+ * @var Integer
154
+ * @access private
155
+ */
156
+ var $mode;
157
+
158
+ /**
159
+ * Continuous Buffer status
160
+ *
161
+ * @see Crypt_DES::enableContinuousBuffer()
162
+ * @var Boolean
163
+ * @access private
164
+ */
165
+ var $continuousBuffer = false;
166
+
167
+ /**
168
+ * Padding status
169
+ *
170
+ * @see Crypt_DES::enablePadding()
171
+ * @var Boolean
172
+ * @access private
173
+ */
174
+ var $padding = true;
175
+
176
+ /**
177
+ * The Initialization Vector
178
+ *
179
+ * @see Crypt_DES::setIV()
180
+ * @var String
181
+ * @access private
182
+ */
183
+ var $iv = "\0\0\0\0\0\0\0\0";
184
+
185
+ /**
186
+ * A "sliding" Initialization Vector
187
+ *
188
+ * @see Crypt_DES::enableContinuousBuffer()
189
+ * @var String
190
+ * @access private
191
+ */
192
+ var $encryptIV = "\0\0\0\0\0\0\0\0";
193
+
194
+ /**
195
+ * A "sliding" Initialization Vector
196
+ *
197
+ * @see Crypt_DES::enableContinuousBuffer()
198
+ * @var String
199
+ * @access private
200
+ */
201
+ var $decryptIV = "\0\0\0\0\0\0\0\0";
202
+
203
+ /**
204
+ * mcrypt resource for encryption
205
+ *
206
+ * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
207
+ * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
208
+ *
209
+ * @see Crypt_DES::encrypt()
210
+ * @var String
211
+ * @access private
212
+ */
213
+ var $enmcrypt;
214
+
215
+ /**
216
+ * mcrypt resource for decryption
217
+ *
218
+ * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
219
+ * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
220
+ *
221
+ * @see Crypt_DES::decrypt()
222
+ * @var String
223
+ * @access private
224
+ */
225
+ var $demcrypt;
226
+
227
+ /**
228
+ * Does the enmcrypt resource need to be (re)initialized?
229
+ *
230
+ * @see Crypt_DES::setKey()
231
+ * @see Crypt_DES::setIV()
232
+ * @var Boolean
233
+ * @access private
234
+ */
235
+ var $enchanged = true;
236
+
237
+ /**
238
+ * Does the demcrypt resource need to be (re)initialized?
239
+ *
240
+ * @see Crypt_DES::setKey()
241
+ * @see Crypt_DES::setIV()
242
+ * @var Boolean
243
+ * @access private
244
+ */
245
+ var $dechanged = true;
246
+
247
+ /**
248
+ * Is the mode one that is paddable?
249
+ *
250
+ * @see Crypt_DES::Crypt_DES()
251
+ * @var Boolean
252
+ * @access private
253
+ */
254
+ var $paddable = false;
255
+
256
+ /**
257
+ * Encryption buffer for CTR, OFB and CFB modes
258
+ *
259
+ * @see Crypt_DES::encrypt()
260
+ * @var String
261
+ * @access private
262
+ */
263
+ var $enbuffer = '';
264
+
265
+ /**
266
+ * Decryption buffer for CTR, OFB and CFB modes
267
+ *
268
+ * @see Crypt_DES::decrypt()
269
+ * @var String
270
+ * @access private
271
+ */
272
+ var $debuffer = '';
273
+
274
+ /**
275
+ * mcrypt resource for CFB mode
276
+ *
277
+ * @see Crypt_DES::encrypt()
278
+ * @see Crypt_DES::decrypt()
279
+ * @var String
280
+ * @access private
281
+ */
282
+ var $ecb;
283
+
284
+ /**
285
+ * Default Constructor.
286
+ *
287
+ * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
288
+ * CRYPT_DES_MODE_ECB or CRYPT_DES_MODE_CBC. If not explictly set, CRYPT_DES_MODE_CBC will be used.
289
+ *
290
+ * @param optional Integer $mode
291
+ * @return Crypt_DES
292
+ * @access public
293
+ */
294
+ function Crypt_DES($mode = CRYPT_MODE_DES_CBC)
295
+ {
296
+ if ( !defined('CRYPT_DES_MODE') ) {
297
+ switch (true) {
298
+ case extension_loaded('mcrypt'):
299
+ // i'd check to see if des was supported, by doing in_array('des', mcrypt_list_algorithms('')),
300
+ // but since that can be changed after the object has been created, there doesn't seem to be
301
+ // a lot of point...
302
+ define('CRYPT_DES_MODE', CRYPT_DES_MODE_MCRYPT);
303
+ break;
304
+ default:
305
+ define('CRYPT_DES_MODE', CRYPT_DES_MODE_INTERNAL);
306
+ }
307
+ }
308
+
309
+ switch ( CRYPT_DES_MODE ) {
310
+ case CRYPT_DES_MODE_MCRYPT:
311
+ switch ($mode) {
312
+ case CRYPT_DES_MODE_ECB:
313
+ $this->paddable = true;
314
+ $this->mode = MCRYPT_MODE_ECB;
315
+ break;
316
+ case CRYPT_DES_MODE_CTR:
317
+ $this->mode = 'ctr';
318
+ //$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_DES_MODE_CTR;
319
+ break;
320
+ case CRYPT_DES_MODE_CFB:
321
+ $this->mode = 'ncfb';
322
+ break;
323
+ case CRYPT_DES_MODE_OFB:
324
+ $this->mode = MCRYPT_MODE_NOFB;
325
+ break;
326
+ case CRYPT_DES_MODE_CBC:
327
+ default:
328
+ $this->paddable = true;
329
+ $this->mode = MCRYPT_MODE_CBC;
330
+ }
331
+
332
+ break;
333
+ default:
334
+ switch ($mode) {
335
+ case CRYPT_DES_MODE_ECB:
336
+ case CRYPT_DES_MODE_CBC:
337
+ $this->paddable = true;
338
+ $this->mode = $mode;
339
+ break;
340
+ case CRYPT_DES_MODE_CTR:
341
+ case CRYPT_DES_MODE_CFB:
342
+ case CRYPT_DES_MODE_OFB:
343
+ $this->mode = $mode;
344
+ break;
345
+ default:
346
+ $this->paddable = true;
347
+ $this->mode = CRYPT_DES_MODE_CBC;
348
+ }
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Sets the key.
354
+ *
355
+ * Keys can be of any length. DES, itself, uses 64-bit keys (eg. strlen($key) == 8), however, we
356
+ * only use the first eight, if $key has more then eight characters in it, and pad $key with the
357
+ * null byte if it is less then eight characters long.
358
+ *
359
+ * DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
360
+ *
361
+ * If the key is not explicitly set, it'll be assumed to be all zero's.
362
+ *
363
+ * @access public
364
+ * @param String $key
365
+ */
366
+ function setKey($key)
367
+ {
368
+ $this->keys = ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) ? str_pad(substr($key, 0, 8), 8, chr(0)) : $this->_prepareKey($key);
369
+ $this->changed = true;
370
+ }
371
+
372
+ /**
373
+ * Sets the password.
374
+ *
375
+ * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
376
+ * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}:
377
+ * $hash, $salt, $method
378
+ *
379
+ * @param String $password
380
+ * @param optional String $method
381
+ * @access public
382
+ */
383
+ function setPassword($password, $method = 'pbkdf2')
384
+ {
385
+ $key = '';
386
+
387
+ switch ($method) {
388
+ default: // 'pbkdf2'
389
+ list(, , $hash, $salt, $count) = func_get_args();
390
+ if (!isset($hash)) {
391
+ $hash = 'sha1';
392
+ }
393
+ // WPA and WPA use the SSID as the salt
394
+ if (!isset($salt)) {
395
+ $salt = 'phpseclib';
396
+ }
397
+ // RFC2898#section-4.2 uses 1,000 iterations by default
398
+ // WPA and WPA2 use 4,096.
399
+ if (!isset($count)) {
400
+ $count = 1000;
401
+ }
402
+
403
+ if (!class_exists('Crypt_Hash')) {
404
+ require_once('Crypt/Hash.php');
405
+ }
406
+
407
+ $i = 1;
408
+ while (strlen($key) < 8) { // $dkLen == 8
409
+ //$dk.= $this->_pbkdf($password, $salt, $count, $i++);
410
+ $hmac = new Crypt_Hash();
411
+ $hmac->setHash($hash);
412
+ $hmac->setKey($password);
413
+ $f = $u = $hmac->hash($salt . pack('N', $i++));
414
+ for ($j = 2; $j <= $count; $j++) {
415
+ $u = $hmac->hash($u);
416
+ $f^= $u;
417
+ }
418
+ $key.= $f;
419
+ }
420
+ }
421
+
422
+ $this->setKey($key);
423
+ }
424
+
425
+ /**
426
+ * Sets the initialization vector. (optional)
427
+ *
428
+ * SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explictly set, it'll be assumed
429
+ * to be all zero's.
430
+ *
431
+ * @access public
432
+ * @param String $iv
433
+ */
434
+ function setIV($iv)
435
+ {
436
+ $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0));
437
+ $this->changed = true;
438
+ }
439
+
440
+ /**
441
+ * Generate CTR XOR encryption key
442
+ *
443
+ * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
444
+ * plaintext / ciphertext in CTR mode.
445
+ *
446
+ * @see Crypt_DES::decrypt()
447
+ * @see Crypt_DES::encrypt()
448
+ * @access public
449
+ * @param Integer $length
450
+ * @param String $iv
451
+ */
452
+ function _generate_xor($length, &$iv)
453
+ {
454
+ $xor = '';
455
+ $num_blocks = ($length + 7) >> 3;
456
+ for ($i = 0; $i < $num_blocks; $i++) {
457
+ $xor.= $iv;
458
+ for ($j = 4; $j <= 8; $j+=4) {
459
+ $temp = substr($iv, -$j, 4);
460
+ switch ($temp) {
461
+ case "\xFF\xFF\xFF\xFF":
462
+ $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
463
+ break;
464
+ case "\x7F\xFF\xFF\xFF":
465
+ $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
466
+ break 2;
467
+ default:
468
+ extract(unpack('Ncount', $temp));
469
+ $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
470
+ break 2;
471
+ }
472
+ }
473
+ }
474
+
475
+ return $xor;
476
+ }
477
+
478
+ /**
479
+ * Encrypts a message.
480
+ *
481
+ * $plaintext will be padded with up to 8 additional bytes. Other DES implementations may or may not pad in the
482
+ * same manner. Other common approaches to padding and the reasons why it's necessary are discussed in the following
483
+ * URL:
484
+ *
485
+ * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
486
+ *
487
+ * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does.
488
+ * strlen($plaintext) will still need to be a multiple of 8, however, arbitrary values can be added to make it that
489
+ * length.
490
+ *
491
+ * @see Crypt_DES::decrypt()
492
+ * @access public
493
+ * @param String $plaintext
494
+ */
495
+ function encrypt($plaintext)
496
+ {
497
+ if ($this->paddable) {
498
+ $plaintext = $this->_pad($plaintext);
499
+ }
500
+
501
+ if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
502
+ if ($this->enchanged) {
503
+ if (!isset($this->enmcrypt)) {
504
+ $this->enmcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, '');
505
+ }
506
+ mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV);
507
+ if ($this->mode != 'ncfb') {
508
+ $this->enchanged = false;
509
+ }
510
+ }
511
+
512
+ if ($this->mode != 'ncfb') {
513
+ $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
514
+ } else {
515
+ if ($this->enchanged) {
516
+ $this->ecb = mcrypt_module_open(MCRYPT_DES, '', MCRYPT_MODE_ECB, '');
517
+ mcrypt_generic_init($this->ecb, $this->keys, "\0\0\0\0\0\0\0\0");
518
+ $this->enchanged = false;
519
+ }
520
+
521
+ if (strlen($this->enbuffer)) {
522
+ $ciphertext = $plaintext ^ substr($this->encryptIV, strlen($this->enbuffer));
523
+ $this->enbuffer.= $ciphertext;
524
+ if (strlen($this->enbuffer) == 8) {
525
+ $this->encryptIV = $this->enbuffer;
526
+ $this->enbuffer = '';
527
+ mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV);
528
+ }
529
+ $plaintext = substr($plaintext, strlen($ciphertext));
530
+ } else {
531
+ $ciphertext = '';
532
+ }
533
+
534
+ $last_pos = strlen($plaintext) & 0xFFFFFFF8;
535
+ $ciphertext.= $last_pos ? mcrypt_generic($this->enmcrypt, substr($plaintext, 0, $last_pos)) : '';
536
+
537
+ if (strlen($plaintext) & 0x7) {
538
+ if (strlen($ciphertext)) {
539
+ $this->encryptIV = substr($ciphertext, -8);
540
+ }
541
+ $this->encryptIV = mcrypt_generic($this->ecb, $this->encryptIV);
542
+ $this->enbuffer = substr($plaintext, $last_pos) ^ $this->encryptIV;
543
+ $ciphertext.= $this->enbuffer;
544
+ }
545
+ }
546
+
547
+ if (!$this->continuousBuffer) {
548
+ mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV);
549
+ }
550
+
551
+ return $ciphertext;
552
+ }
553
+
554
+ if (!is_array($this->keys)) {
555
+ $this->keys = $this->_prepareKey("\0\0\0\0\0\0\0\0");
556
+ }
557
+
558
+ $buffer = &$this->enbuffer;
559
+ $continuousBuffer = $this->continuousBuffer;
560
+ $ciphertext = '';
561
+ switch ($this->mode) {
562
+ case CRYPT_DES_MODE_ECB:
563
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
564
+ $ciphertext.= $this->_processBlock(substr($plaintext, $i, 8), CRYPT_DES_ENCRYPT);
565
+ }
566
+ break;
567
+ case CRYPT_DES_MODE_CBC:
568
+ $xor = $this->encryptIV;
569
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
570
+ $block = substr($plaintext, $i, 8);
571
+ $block = $this->_processBlock($block ^ $xor, CRYPT_DES_ENCRYPT);
572
+ $xor = $block;
573
+ $ciphertext.= $block;
574
+ }
575
+ if ($this->continuousBuffer) {
576
+ $this->encryptIV = $xor;
577
+ }
578
+ break;
579
+ case CRYPT_DES_MODE_CTR:
580
+ $xor = $this->encryptIV;
581
+ if (strlen($buffer['encrypted'])) {
582
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
583
+ $block = substr($plaintext, $i, 8);
584
+ $buffer['encrypted'].= $this->_processBlock($this->_generate_xor(8, $xor), CRYPT_DES_ENCRYPT);
585
+ $key = $this->_string_shift($buffer['encrypted'], 8);
586
+ $ciphertext.= $block ^ $key;
587
+ }
588
+ } else {
589
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
590
+ $block = substr($plaintext, $i, 8);
591
+ $key = $this->_processBlock($this->_generate_xor(8, $xor), CRYPT_DES_ENCRYPT);
592
+ $ciphertext.= $block ^ $key;
593
+ }
594
+ }
595
+ if ($this->continuousBuffer) {
596
+ $this->encryptIV = $xor;
597
+ if ($start = strlen($plaintext) & 7) {
598
+ $buffer['encrypted'] = substr($key, $start) . $buffer['encrypted'];
599
+ }
600
+ }
601
+ break;
602
+ case CRYPT_DES_MODE_CFB:
603
+ if (!empty($buffer['xor'])) {
604
+ $ciphertext = $plaintext ^ $buffer['xor'];
605
+ $iv = $buffer['encrypted'] . $ciphertext;
606
+ $start = strlen($ciphertext);
607
+ $buffer['encrypted'].= $ciphertext;
608
+ $buffer['xor'] = substr($buffer['xor'], strlen($ciphertext));
609
+ } else {
610
+ $ciphertext = '';
611
+ $iv = $this->encryptIV;
612
+ $start = 0;
613
+ }
614
+
615
+ for ($i = $start; $i < strlen($plaintext); $i+=8) {
616
+ $block = substr($plaintext, $i, 8);
617
+ $xor = $this->_processBlock($iv, CRYPT_DES_ENCRYPT);
618
+ $iv = $block ^ $xor;
619
+ if ($continuousBuffer && strlen($iv) != 8) {
620
+ $buffer = array(
621
+ 'encrypted' => $iv,
622
+ 'xor' => substr($xor, strlen($iv))
623
+ );
624
+ }
625
+ $ciphertext.= $iv;
626
+ }
627
+
628
+ if ($this->continuousBuffer) {
629
+ $this->encryptIV = $iv;
630
+ }
631
+ break;
632
+ case CRYPT_DES_MODE_OFB:
633
+ $xor = $this->encryptIV;
634
+ if (strlen($buffer)) {
635
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
636
+ $xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT);
637
+ $buffer.= $xor;
638
+ $key = $this->_string_shift($buffer, 8);
639
+ $ciphertext.= substr($plaintext, $i, 8) ^ $key;
640
+ }
641
+ } else {
642
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
643
+ $xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT);
644
+ $ciphertext.= substr($plaintext, $i, 8) ^ $xor;
645
+ }
646
+ $key = $xor;
647
+ }
648
+ if ($this->continuousBuffer) {
649
+ $this->encryptIV = $xor;
650
+ if ($start = strlen($plaintext) & 7) {
651
+ $buffer = substr($key, $start) . $buffer;
652
+ }
653
+ }
654
+ }
655
+
656
+ return $ciphertext;
657
+ }
658
+
659
+ /**
660
+ * Decrypts a message.
661
+ *
662
+ * If strlen($ciphertext) is not a multiple of 8, null bytes will be added to the end of the string until it is.
663
+ *
664
+ * @see Crypt_DES::encrypt()
665
+ * @access public
666
+ * @param String $ciphertext
667
+ */
668
+ function decrypt($ciphertext)
669
+ {
670
+ if ($this->paddable) {
671
+ // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
672
+ // "The data is padded with "\0" to make sure the length of the data is n * blocksize."
673
+ $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0));
674
+ }
675
+
676
+ if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
677
+ if ($this->dechanged) {
678
+ if (!isset($this->demcrypt)) {
679
+ $this->demcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, '');
680
+ }
681
+ mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV);
682
+ if ($this->mode != 'ncfb') {
683
+ $this->dechanged = false;
684
+ }
685
+ }
686
+
687
+ if ($this->mode != 'ncfb') {
688
+ $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
689
+ } else {
690
+ if ($this->dechanged) {
691
+ $this->ecb = mcrypt_module_open(MCRYPT_DES, '', MCRYPT_MODE_ECB, '');
692
+ mcrypt_generic_init($this->ecb, $this->keys, "\0\0\0\0\0\0\0\0");
693
+ $this->dechanged = false;
694
+ }
695
+
696
+ if (strlen($this->debuffer)) {
697
+ $plaintext = $ciphertext ^ substr($this->decryptIV, strlen($this->debuffer));
698
+
699
+ $this->debuffer.= substr($ciphertext, 0, strlen($plaintext));
700
+ if (strlen($this->debuffer) == 8) {
701
+ $this->decryptIV = $this->debuffer;
702
+ $this->debuffer = '';
703
+ mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV);
704
+ }
705
+ $ciphertext = substr($ciphertext, strlen($plaintext));
706
+ } else {
707
+ $plaintext = '';
708
+ }
709
+
710
+ $last_pos = strlen($ciphertext) & 0xFFFFFFF8;
711
+ $plaintext.= $last_pos ? mdecrypt_generic($this->demcrypt, substr($ciphertext, 0, $last_pos)) : '';
712
+
713
+ if (strlen($ciphertext) & 0x7) {
714
+ if (strlen($plaintext)) {
715
+ $this->decryptIV = substr($ciphertext, $last_pos - 8, 8);
716
+ }
717
+ $this->decryptIV = mcrypt_generic($this->ecb, $this->decryptIV);
718
+ $this->debuffer = substr($ciphertext, $last_pos);
719
+ $plaintext.= $this->debuffer ^ $this->decryptIV;
720
+ }
721
+
722
+ return $plaintext;
723
+ }
724
+
725
+ if (!$this->continuousBuffer) {
726
+ mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV);
727
+ }
728
+
729
+ return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext;
730
+ }
731
+
732
+ if (!is_array($this->keys)) {
733
+ $this->keys = $this->_prepareKey("\0\0\0\0\0\0\0\0");
734
+ }
735
+
736
+ $buffer = &$this->debuffer;
737
+ $continuousBuffer = $this->continuousBuffer;
738
+ $plaintext = '';
739
+ switch ($this->mode) {
740
+ case CRYPT_DES_MODE_ECB:
741
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
742
+ $plaintext.= $this->_processBlock(substr($ciphertext, $i, 8), CRYPT_DES_DECRYPT);
743
+ }
744
+ break;
745
+ case CRYPT_DES_MODE_CBC:
746
+ $xor = $this->decryptIV;
747
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
748
+ $block = substr($ciphertext, $i, 8);
749
+ $plaintext.= $this->_processBlock($block, CRYPT_DES_DECRYPT) ^ $xor;
750
+ $xor = $block;
751
+ }
752
+ if ($this->continuousBuffer) {
753
+ $this->decryptIV = $xor;
754
+ }
755
+ break;
756
+ case CRYPT_DES_MODE_CTR:
757
+ $xor = $this->decryptIV;
758
+ if (strlen($buffer['ciphertext'])) {
759
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
760
+ $block = substr($ciphertext, $i, 8);
761
+ $buffer['ciphertext'].= $this->_processBlock($this->_generate_xor(8, $xor), CRYPT_DES_ENCRYPT);
762
+ $key = $this->_string_shift($buffer['ciphertext'], 8);
763
+ $plaintext.= $block ^ $key;
764
+ }
765
+ } else {
766
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
767
+ $block = substr($ciphertext, $i, 8);
768
+ $key = $this->_processBlock($this->_generate_xor(8, $xor), CRYPT_DES_ENCRYPT);
769
+ $plaintext.= $block ^ $key;
770
+ }
771
+ }
772
+ if ($this->continuousBuffer) {
773
+ $this->decryptIV = $xor;
774
+ if ($start = strlen($ciphertext) % 8) {
775
+ $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
776
+ }
777
+ }
778
+ break;
779
+ case CRYPT_DES_MODE_CFB:
780
+ if (!empty($buffer['ciphertext'])) {
781
+ $plaintext = $ciphertext ^ substr($this->decryptIV, strlen($buffer['ciphertext']));
782
+ $buffer['ciphertext'].= substr($ciphertext, 0, strlen($plaintext));
783
+ if (strlen($buffer['ciphertext']) == 8) {
784
+ $xor = $this->_processBlock($buffer['ciphertext'], CRYPT_DES_ENCRYPT);
785
+ $buffer['ciphertext'] = '';
786
+ }
787
+ $start = strlen($plaintext);
788
+ $block = $this->decryptIV;
789
+ } else {
790
+ $plaintext = '';
791
+ $xor = $this->_processBlock($this->decryptIV, CRYPT_DES_ENCRYPT);
792
+ $start = 0;
793
+ }
794
+
795
+ for ($i = $start; $i < strlen($ciphertext); $i+=8) {
796
+ $block = substr($ciphertext, $i, 8);
797
+ $plaintext.= $block ^ $xor;
798
+ if ($continuousBuffer && strlen($block) != 8) {
799
+ $buffer['ciphertext'].= $block;
800
+ $block = $xor;
801
+ } else if (strlen($block) == 8) {
802
+ $xor = $this->_processBlock($block, CRYPT_DES_ENCRYPT);
803
+ }
804
+ }
805
+ if ($this->continuousBuffer) {
806
+ $this->decryptIV = $block;
807
+ }
808
+ break;
809
+ case CRYPT_DES_MODE_OFB:
810
+ $xor = $this->decryptIV;
811
+ if (strlen($buffer)) {
812
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
813
+ $xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT);
814
+ $buffer.= $xor;
815
+ $key = $this->_string_shift($buffer, 8);
816
+ $plaintext.= substr($ciphertext, $i, 8) ^ $key;
817
+ }
818
+ } else {
819
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
820
+ $xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT);
821
+ $plaintext.= substr($ciphertext, $i, 8) ^ $xor;
822
+ }
823
+ $key = $xor;
824
+ }
825
+ if ($this->continuousBuffer) {
826
+ $this->decryptIV = $xor;
827
+ if ($start = strlen($ciphertext) % 8) {
828
+ $buffer = substr($key, $start) . $buffer;
829
+ }
830
+ }
831
+ }
832
+
833
+ return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
834
+ }
835
+
836
+ /**
837
+ * Treat consecutive "packets" as if they are a continuous buffer.
838
+ *
839
+ * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
840
+ * will yield different outputs:
841
+ *
842
+ * <code>
843
+ * echo $des->encrypt(substr($plaintext, 0, 8));
844
+ * echo $des->encrypt(substr($plaintext, 8, 8));
845
+ * </code>
846
+ * <code>
847
+ * echo $des->encrypt($plaintext);
848
+ * </code>
849
+ *
850
+ * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
851
+ * another, as demonstrated with the following:
852
+ *
853
+ * <code>
854
+ * $des->encrypt(substr($plaintext, 0, 8));
855
+ * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
856
+ * </code>
857
+ * <code>
858
+ * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
859
+ * </code>
860
+ *
861
+ * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
862
+ * outputs. The reason is due to the fact that the initialization vector's change after every encryption /
863
+ * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
864
+ *
865
+ * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
866
+ * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
867
+ * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
868
+ * however, they are also less intuitive and more likely to cause you problems.
869
+ *
870
+ * @see Crypt_DES::disableContinuousBuffer()
871
+ * @access public
872
+ */
873
+ function enableContinuousBuffer()
874
+ {
875
+ $this->continuousBuffer = true;
876
+ }
877
+
878
+ /**
879
+ * Treat consecutive packets as if they are a discontinuous buffer.
880
+ *
881
+ * The default behavior.
882
+ *
883
+ * @see Crypt_DES::enableContinuousBuffer()
884
+ * @access public
885
+ */
886
+ function disableContinuousBuffer()
887
+ {
888
+ $this->continuousBuffer = false;
889
+ $this->encryptIV = $this->iv;
890
+ $this->decryptIV = $this->iv;
891
+ }
892
+
893
+ /**
894
+ * Pad "packets".
895
+ *
896
+ * DES works by encrypting eight bytes at a time. If you ever need to encrypt or decrypt something that's not
897
+ * a multiple of eight, it becomes necessary to pad the input so that it's length is a multiple of eight.
898
+ *
899
+ * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH1,
900
+ * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping
901
+ * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
902
+ * transmitted separately)
903
+ *
904
+ * @see Crypt_DES::disablePadding()
905
+ * @access public
906
+ */
907
+ function enablePadding()
908
+ {
909
+ $this->padding = true;
910
+ }
911
+
912
+ /**
913
+ * Do not pad packets.
914
+ *
915
+ * @see Crypt_DES::enablePadding()
916
+ * @access public
917
+ */
918
+ function disablePadding()
919
+ {
920
+ $this->padding = false;
921
+ }
922
+
923
+ /**
924
+ * Pads a string
925
+ *
926
+ * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize (8).
927
+ * 8 - (strlen($text) & 7) bytes are added, each of which is equal to chr(8 - (strlen($text) & 7)
928
+ *
929
+ * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
930
+ * and padding will, hence forth, be enabled.
931
+ *
932
+ * @see Crypt_DES::_unpad()
933
+ * @access private
934
+ */
935
+ function _pad($text)
936
+ {
937
+ $length = strlen($text);
938
+
939
+ if (!$this->padding) {
940
+ if (($length & 7) == 0) {
941
+ return $text;
942
+ } else {
943
+ user_error("The plaintext's length ($length) is not a multiple of the block size (8)", E_USER_NOTICE);
944
+ $this->padding = true;
945
+ }
946
+ }
947
+
948
+ $pad = 8 - ($length & 7);
949
+ return str_pad($text, $length + $pad, chr($pad));
950
+ }
951
+
952
+ /**
953
+ * Unpads a string
954
+ *
955
+ * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
956
+ * and false will be returned.
957
+ *
958
+ * @see Crypt_DES::_pad()
959
+ * @access private
960
+ */
961
+ function _unpad($text)
962
+ {
963
+ if (!$this->padding) {
964
+ return $text;
965
+ }
966
+
967
+ $length = ord($text[strlen($text) - 1]);
968
+
969
+ if (!$length || $length > 8) {
970
+ return false;
971
+ }
972
+
973
+ return substr($text, 0, -$length);
974
+ }
975
+
976
+ /**
977
+ * Encrypts or decrypts a 64-bit block
978
+ *
979
+ * $mode should be either CRYPT_DES_ENCRYPT or CRYPT_DES_DECRYPT. See
980
+ * {@link http://en.wikipedia.org/wiki/Image:Feistel.png Feistel.png} to get a general
981
+ * idea of what this function does.
982
+ *
983
+ * @access private
984
+ * @param String $block
985
+ * @param Integer $mode
986
+ * @return String
987
+ */
988
+ function _processBlock($block, $mode)
989
+ {
990
+ // s-boxes. in the official DES docs, they're described as being matrices that
991
+ // one accesses by using the first and last bits to determine the row and the
992
+ // middle four bits to determine the column. in this implementation, they've
993
+ // been converted to vectors
994
+ static $sbox = array(
995
+ array(
996
+ 14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1,
997
+ 3, 10 ,10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8,
998
+ 4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7,
999
+ 15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13
1000
+ ),
1001
+ array(
1002
+ 15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14,
1003
+ 9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5,
1004
+ 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2,
1005
+ 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9
1006
+ ),
1007
+ array(
1008
+ 10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10,
1009
+ 1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1,
1010
+ 13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7,
1011
+ 11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12
1012
+ ),
1013
+ array(
1014
+ 7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3,
1015
+ 1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9,
1016
+ 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8,
1017
+ 15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14
1018
+ ),
1019
+ array(
1020
+ 2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1,
1021
+ 8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6,
1022
+ 4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13,
1023
+ 15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3
1024
+ ),
1025
+ array(
1026
+ 12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5,
1027
+ 0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8,
1028
+ 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10,
1029
+ 7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13
1030
+ ),
1031
+ array(
1032
+ 4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10,
1033
+ 3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6,
1034
+ 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7,
1035
+ 10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12
1036
+ ),
1037
+ array(
1038
+ 13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4,
1039
+ 10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2,
1040
+ 7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13,
1041
+ 0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11
1042
+ )
1043
+ );
1044
+
1045
+ $keys = $this->keys;
1046
+
1047
+ $temp = unpack('Na/Nb', $block);
1048
+ $block = array($temp['a'], $temp['b']);
1049
+
1050
+ // because php does arithmetic right shifts, if the most significant bits are set, right
1051
+ // shifting those into the correct position will add 1's - not 0's. this will intefere
1052
+ // with the | operation unless a second & is done. so we isolate these bits and left shift
1053
+ // them into place. we then & each block with 0x7FFFFFFF to prevennt 1's from being added
1054
+ // for any other shifts.
1055
+ $msb = array(
1056
+ ($block[0] >> 31) & 1,
1057
+ ($block[1] >> 31) & 1
1058
+ );
1059
+ $block[0] &= 0x7FFFFFFF;
1060
+ $block[1] &= 0x7FFFFFFF;
1061
+
1062
+ // we isolate the appropriate bit in the appropriate integer and shift as appropriate. in
1063
+ // some cases, there are going to be multiple bits in the same integer that need to be shifted
1064
+ // in the same way. we combine those into one shift operation.
1065
+ $block = array(
1066
+ (($block[1] & 0x00000040) << 25) | (($block[1] & 0x00004000) << 16) |
1067
+ (($block[1] & 0x00400001) << 7) | (($block[1] & 0x40000100) >> 2) |
1068
+ (($block[0] & 0x00000040) << 21) | (($block[0] & 0x00004000) << 12) |
1069
+ (($block[0] & 0x00400001) << 3) | (($block[0] & 0x40000100) >> 6) |
1070
+ (($block[1] & 0x00000010) << 19) | (($block[1] & 0x00001000) << 10) |
1071
+ (($block[1] & 0x00100000) << 1) | (($block[1] & 0x10000000) >> 8) |
1072
+ (($block[0] & 0x00000010) << 15) | (($block[0] & 0x00001000) << 6) |
1073
+ (($block[0] & 0x00100000) >> 3) | (($block[0] & 0x10000000) >> 12) |
1074
+ (($block[1] & 0x00000004) << 13) | (($block[1] & 0x00000400) << 4) |
1075
+ (($block[1] & 0x00040000) >> 5) | (($block[1] & 0x04000000) >> 14) |
1076
+ (($block[0] & 0x00000004) << 9) | ( $block[0] & 0x00000400 ) |
1077
+ (($block[0] & 0x00040000) >> 9) | (($block[0] & 0x04000000) >> 18) |
1078
+ (($block[1] & 0x00010000) >> 11) | (($block[1] & 0x01000000) >> 20) |
1079
+ (($block[0] & 0x00010000) >> 15) | (($block[0] & 0x01000000) >> 24)
1080
+ ,
1081
+ (($block[1] & 0x00000080) << 24) | (($block[1] & 0x00008000) << 15) |
1082
+ (($block[1] & 0x00800002) << 6) | (($block[0] & 0x00000080) << 20) |
1083
+ (($block[0] & 0x00008000) << 11) | (($block[0] & 0x00800002) << 2) |
1084
+ (($block[1] & 0x00000020) << 18) | (($block[1] & 0x00002000) << 9) |
1085
+ ( $block[1] & 0x00200000 ) | (($block[1] & 0x20000000) >> 9) |
1086
+ (($block[0] & 0x00000020) << 14) | (($block[0] & 0x00002000) << 5) |
1087
+ (($block[0] & 0x00200000) >> 4) | (($block[0] & 0x20000000) >> 13) |
1088
+ (($block[1] & 0x00000008) << 12) | (($block[1] & 0x00000800) << 3) |
1089
+ (($block[1] & 0x00080000) >> 6) | (($block[1] & 0x08000000) >> 15) |
1090
+ (($block[0] & 0x00000008) << 8) | (($block[0] & 0x00000800) >> 1) |
1091
+ (($block[0] & 0x00080000) >> 10) | (($block[0] & 0x08000000) >> 19) |
1092
+ (($block[1] & 0x00000200) >> 3) | (($block[0] & 0x00000200) >> 7) |
1093
+ (($block[1] & 0x00020000) >> 12) | (($block[1] & 0x02000000) >> 21) |
1094
+ (($block[0] & 0x00020000) >> 16) | (($block[0] & 0x02000000) >> 25) |
1095
+ ($msb[1] << 28) | ($msb[0] << 24)
1096
+ );
1097
+
1098
+ for ($i = 0; $i < 16; $i++) {
1099
+ // start of "the Feistel (F) function" - see the following URL:
1100
+ // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png
1101
+ $temp = (($sbox[0][((($block[1] >> 27) & 0x1F) | (($block[1] & 1) << 5)) ^ $keys[$mode][$i][0]]) << 28)
1102
+ | (($sbox[1][(($block[1] & 0x1F800000) >> 23) ^ $keys[$mode][$i][1]]) << 24)
1103
+ | (($sbox[2][(($block[1] & 0x01F80000) >> 19) ^ $keys[$mode][$i][2]]) << 20)
1104
+ | (($sbox[3][(($block[1] & 0x001F8000) >> 15) ^ $keys[$mode][$i][3]]) << 16)
1105
+ | (($sbox[4][(($block[1] & 0x0001F800) >> 11) ^ $keys[$mode][$i][4]]) << 12)
1106
+ | (($sbox[5][(($block[1] & 0x00001F80) >> 7) ^ $keys[$mode][$i][5]]) << 8)
1107
+ | (($sbox[6][(($block[1] & 0x000001F8) >> 3) ^ $keys[$mode][$i][6]]) << 4)
1108
+ | ( $sbox[7][((($block[1] & 0x1F) << 1) | (($block[1] >> 31) & 1)) ^ $keys[$mode][$i][7]]);
1109
+
1110
+ $msb = ($temp >> 31) & 1;
1111
+ $temp &= 0x7FFFFFFF;
1112
+ $newBlock = (($temp & 0x00010000) << 15) | (($temp & 0x02020120) << 5)
1113
+ | (($temp & 0x00001800) << 17) | (($temp & 0x01000000) >> 10)
1114
+ | (($temp & 0x00000008) << 24) | (($temp & 0x00100000) << 6)
1115
+ | (($temp & 0x00000010) << 21) | (($temp & 0x00008000) << 9)
1116
+ | (($temp & 0x00000200) << 12) | (($temp & 0x10000000) >> 27)
1117
+ | (($temp & 0x00000040) << 14) | (($temp & 0x08000000) >> 8)
1118
+ | (($temp & 0x00004000) << 4) | (($temp & 0x00000002) << 16)
1119
+ | (($temp & 0x00442000) >> 6) | (($temp & 0x40800000) >> 15)
1120
+ | (($temp & 0x00000001) << 11) | (($temp & 0x20000000) >> 20)
1121
+ | (($temp & 0x00080000) >> 13) | (($temp & 0x00000004) << 3)
1122
+ | (($temp & 0x04000000) >> 22) | (($temp & 0x00000480) >> 7)
1123
+ | (($temp & 0x00200000) >> 19) | ($msb << 23);
1124
+ // end of "the Feistel (F) function" - $newBlock is F's output
1125
+
1126
+ $temp = $block[1];
1127
+ $block[1] = $block[0] ^ $newBlock;
1128
+ $block[0] = $temp;
1129
+ }
1130
+
1131
+ $msb = array(
1132
+ ($block[0] >> 31) & 1,
1133
+ ($block[1] >> 31) & 1
1134
+ );
1135
+ $block[0] &= 0x7FFFFFFF;
1136
+ $block[1] &= 0x7FFFFFFF;
1137
+
1138
+ $block = array(
1139
+ (($block[0] & 0x01000004) << 7) | (($block[1] & 0x01000004) << 6) |
1140
+ (($block[0] & 0x00010000) << 13) | (($block[1] & 0x00010000) << 12) |
1141
+ (($block[0] & 0x00000100) << 19) | (($block[1] & 0x00000100) << 18) |
1142
+ (($block[0] & 0x00000001) << 25) | (($block[1] & 0x00000001) << 24) |
1143
+ (($block[0] & 0x02000008) >> 2) | (($block[1] & 0x02000008) >> 3) |
1144
+ (($block[0] & 0x00020000) << 4) | (($block[1] & 0x00020000) << 3) |
1145
+ (($block[0] & 0x00000200) << 10) | (($block[1] & 0x00000200) << 9) |
1146
+ (($block[0] & 0x00000002) << 16) | (($block[1] & 0x00000002) << 15) |
1147
+ (($block[0] & 0x04000000) >> 11) | (($block[1] & 0x04000000) >> 12) |
1148
+ (($block[0] & 0x00040000) >> 5) | (($block[1] & 0x00040000) >> 6) |
1149
+ (($block[0] & 0x00000400) << 1) | ( $block[1] & 0x00000400 ) |
1150
+ (($block[0] & 0x08000000) >> 20) | (($block[1] & 0x08000000) >> 21) |
1151
+ (($block[0] & 0x00080000) >> 14) | (($block[1] & 0x00080000) >> 15) |
1152
+ (($block[0] & 0x00000800) >> 8) | (($block[1] & 0x00000800) >> 9)
1153
+ ,
1154
+ (($block[0] & 0x10000040) << 3) | (($block[1] & 0x10000040) << 2) |
1155
+ (($block[0] & 0x00100000) << 9) | (($block[1] & 0x00100000) << 8) |
1156
+ (($block[0] & 0x00001000) << 15) | (($block[1] & 0x00001000) << 14) |
1157
+ (($block[0] & 0x00000010) << 21) | (($block[1] & 0x00000010) << 20) |
1158
+ (($block[0] & 0x20000080) >> 6) | (($block[1] & 0x20000080) >> 7) |
1159
+ ( $block[0] & 0x00200000 ) | (($block[1] & 0x00200000) >> 1) |
1160
+ (($block[0] & 0x00002000) << 6) | (($block[1] & 0x00002000) << 5) |
1161
+ (($block[0] & 0x00000020) << 12) | (($block[1] & 0x00000020) << 11) |
1162
+ (($block[0] & 0x40000000) >> 15) | (($block[1] & 0x40000000) >> 16) |
1163
+ (($block[0] & 0x00400000) >> 9) | (($block[1] & 0x00400000) >> 10) |
1164
+ (($block[0] & 0x00004000) >> 3) | (($block[1] & 0x00004000) >> 4) |
1165
+ (($block[0] & 0x00800000) >> 18) | (($block[1] & 0x00800000) >> 19) |
1166
+ (($block[0] & 0x00008000) >> 12) | (($block[1] & 0x00008000) >> 13) |
1167
+ ($msb[0] << 7) | ($msb[1] << 6)
1168
+ );
1169
+
1170
+ return pack('NN', $block[0], $block[1]);
1171
+ }
1172
+
1173
+ /**
1174
+ * Creates the key schedule.
1175
+ *
1176
+ * @access private
1177
+ * @param String $key
1178
+ * @return Array
1179
+ */
1180
+ function _prepareKey($key)
1181
+ {
1182
+ static $shifts = array( // number of key bits shifted per round
1183
+ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
1184
+ );
1185
+
1186
+ // pad the key and remove extra characters as appropriate.
1187
+ $key = str_pad(substr($key, 0, 8), 8, chr(0));
1188
+
1189
+ $temp = unpack('Na/Nb', $key);
1190
+ $key = array($temp['a'], $temp['b']);
1191
+ $msb = array(
1192
+ ($key[0] >> 31) & 1,
1193
+ ($key[1] >> 31) & 1
1194
+ );
1195
+ $key[0] &= 0x7FFFFFFF;
1196
+ $key[1] &= 0x7FFFFFFF;
1197
+
1198
+ $key = array(
1199
+ (($key[1] & 0x00000002) << 26) | (($key[1] & 0x00000204) << 17) |
1200
+ (($key[1] & 0x00020408) << 8) | (($key[1] & 0x02040800) >> 1) |
1201
+ (($key[0] & 0x00000002) << 22) | (($key[0] & 0x00000204) << 13) |
1202
+ (($key[0] & 0x00020408) << 4) | (($key[0] & 0x02040800) >> 5) |
1203
+ (($key[1] & 0x04080000) >> 10) | (($key[0] & 0x04080000) >> 14) |
1204
+ (($key[1] & 0x08000000) >> 19) | (($key[0] & 0x08000000) >> 23) |
1205
+ (($key[0] & 0x00000010) >> 1) | (($key[0] & 0x00001000) >> 10) |
1206
+ (($key[0] & 0x00100000) >> 19) | (($key[0] & 0x10000000) >> 28)
1207
+ ,
1208
+ (($key[1] & 0x00000080) << 20) | (($key[1] & 0x00008000) << 11) |
1209
+ (($key[1] & 0x00800000) << 2) | (($key[0] & 0x00000080) << 16) |
1210
+ (($key[0] & 0x00008000) << 7) | (($key[0] & 0x00800000) >> 2) |
1211
+ (($key[1] & 0x00000040) << 13) | (($key[1] & 0x00004000) << 4) |
1212
+ (($key[1] & 0x00400000) >> 5) | (($key[1] & 0x40000000) >> 14) |
1213
+ (($key[0] & 0x00000040) << 9) | ( $key[0] & 0x00004000 ) |
1214
+ (($key[0] & 0x00400000) >> 9) | (($key[0] & 0x40000000) >> 18) |
1215
+ (($key[1] & 0x00000020) << 6) | (($key[1] & 0x00002000) >> 3) |
1216
+ (($key[1] & 0x00200000) >> 12) | (($key[1] & 0x20000000) >> 21) |
1217
+ (($key[0] & 0x00000020) << 2) | (($key[0] & 0x00002000) >> 7) |
1218
+ (($key[0] & 0x00200000) >> 16) | (($key[0] & 0x20000000) >> 25) |
1219
+ (($key[1] & 0x00000010) >> 1) | (($key[1] & 0x00001000) >> 10) |
1220
+ (($key[1] & 0x00100000) >> 19) | (($key[1] & 0x10000000) >> 28) |
1221
+ ($msb[1] << 24) | ($msb[0] << 20)
1222
+ );
1223
+
1224
+ $keys = array();
1225
+ for ($i = 0; $i < 16; $i++) {
1226
+ $key[0] <<= $shifts[$i];
1227
+ $temp = ($key[0] & 0xF0000000) >> 28;
1228
+ $key[0] = ($key[0] | $temp) & 0x0FFFFFFF;
1229
+
1230
+ $key[1] <<= $shifts[$i];
1231
+ $temp = ($key[1] & 0xF0000000) >> 28;
1232
+ $key[1] = ($key[1] | $temp) & 0x0FFFFFFF;
1233
+
1234
+ $temp = array(
1235
+ (($key[1] & 0x00004000) >> 9) | (($key[1] & 0x00000800) >> 7) |
1236
+ (($key[1] & 0x00020000) >> 14) | (($key[1] & 0x00000010) >> 2) |
1237
+ (($key[1] & 0x08000000) >> 26) | (($key[1] & 0x00800000) >> 23)
1238
+ ,
1239
+ (($key[1] & 0x02400000) >> 20) | (($key[1] & 0x00000001) << 4) |
1240
+ (($key[1] & 0x00002000) >> 10) | (($key[1] & 0x00040000) >> 18) |
1241
+ (($key[1] & 0x00000080) >> 6)
1242
+ ,
1243
+ ( $key[1] & 0x00000020 ) | (($key[1] & 0x00000200) >> 5) |
1244
+ (($key[1] & 0x00010000) >> 13) | (($key[1] & 0x01000000) >> 22) |
1245
+ (($key[1] & 0x00000004) >> 1) | (($key[1] & 0x00100000) >> 20)
1246
+ ,
1247
+ (($key[1] & 0x00001000) >> 7) | (($key[1] & 0x00200000) >> 17) |
1248
+ (($key[1] & 0x00000002) << 2) | (($key[1] & 0x00000100) >> 6) |
1249
+ (($key[1] & 0x00008000) >> 14) | (($key[1] & 0x04000000) >> 26)
1250
+ ,
1251
+ (($key[0] & 0x00008000) >> 10) | ( $key[0] & 0x00000010 ) |
1252
+ (($key[0] & 0x02000000) >> 22) | (($key[0] & 0x00080000) >> 17) |
1253
+ (($key[0] & 0x00000200) >> 8) | (($key[0] & 0x00000002) >> 1)
1254
+ ,
1255
+ (($key[0] & 0x04000000) >> 21) | (($key[0] & 0x00010000) >> 12) |
1256
+ (($key[0] & 0x00000020) >> 2) | (($key[0] & 0x00000800) >> 9) |
1257
+ (($key[0] & 0x00800000) >> 22) | (($key[0] & 0x00000100) >> 8)
1258
+ ,
1259
+ (($key[0] & 0x00001000) >> 7) | (($key[0] & 0x00000088) >> 3) |
1260
+ (($key[0] & 0x00020000) >> 14) | (($key[0] & 0x00000001) << 2) |
1261
+ (($key[0] & 0x00400000) >> 21)
1262
+ ,
1263
+ (($key[0] & 0x00000400) >> 5) | (($key[0] & 0x00004000) >> 10) |
1264
+ (($key[0] & 0x00000040) >> 3) | (($key[0] & 0x00100000) >> 18) |
1265
+ (($key[0] & 0x08000000) >> 26) | (($key[0] & 0x01000000) >> 24)
1266
+ );
1267
+
1268
+ $keys[] = $temp;
1269
+ }
1270
+
1271
+ $temp = array(
1272
+ CRYPT_DES_ENCRYPT => $keys,
1273
+ CRYPT_DES_DECRYPT => array_reverse($keys)
1274
+ );
1275
+
1276
+ return $temp;
1277
+ }
1278
+
1279
+ /**
1280
+ * String Shift
1281
+ *
1282
+ * Inspired by array_shift
1283
+ *
1284
+ * @param String $string
1285
+ * @param optional Integer $index
1286
+ * @return String
1287
+ * @access private
1288
+ */
1289
+ function _string_shift(&$string, $index = 1)
1290
+ {
1291
+ $substr = substr($string, 0, $index);
1292
+ $string = substr($string, $index);
1293
+ return $substr;
1294
+ }
1295
+ }
1296
+
1297
+ // vim: ts=4:sw=4:et:
1298
+ // vim6: fdl=1:
phpseclib/Crypt/Hash.php ADDED
@@ -0,0 +1,825 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
6
+ *
7
+ * Uses hash() or mhash() if available and an internal implementation, otherwise. Currently supports the following:
8
+ *
9
+ * md2, md5, md5-96, sha1, sha1-96, sha256, sha384, and sha512
10
+ *
11
+ * If {@link Crypt_Hash::setKey() setKey()} is called, {@link Crypt_Hash::hash() hash()} will return the HMAC as opposed to
12
+ * the hash. If no valid algorithm is provided, sha1 will be used.
13
+ *
14
+ * PHP versions 4 and 5
15
+ *
16
+ * {@internal The variable names are the same as those in
17
+ * {@link http://tools.ietf.org/html/rfc2104#section-2 RFC2104}.}}
18
+ *
19
+ * Here's a short example of how to use this library:
20
+ * <code>
21
+ * <?php
22
+ * include('Crypt/Hash.php');
23
+ *
24
+ * $hash = new Crypt_Hash('sha1');
25
+ *
26
+ * $hash->setKey('abcdefg');
27
+ *
28
+ * echo base64_encode($hash->hash('abcdefg'));
29
+ * ?>
30
+ * </code>
31
+ *
32
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
33
+ * of this software and associated documentation files (the "Software"), to deal
34
+ * in the Software without restriction, including without limitation the rights
35
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
36
+ * copies of the Software, and to permit persons to whom the Software is
37
+ * furnished to do so, subject to the following conditions:
38
+ *
39
+ * The above copyright notice and this permission notice shall be included in
40
+ * all copies or substantial portions of the Software.
41
+ *
42
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
43
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
44
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
45
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
46
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
47
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
48
+ * THE SOFTWARE.
49
+ *
50
+ * @category Crypt
51
+ * @package Crypt_Hash
52
+ * @author Jim Wigginton <terrafrost@php.net>
53
+ * @copyright MMVII Jim Wigginton
54
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
55
+ * @version $Id: Hash.php,v 1.6 2009/11/23 23:37:07 terrafrost Exp $
56
+ * @link http://phpseclib.sourceforge.net
57
+ */
58
+
59
+ /**#@+
60
+ * @access private
61
+ * @see Crypt_Hash::Crypt_Hash()
62
+ */
63
+ /**
64
+ * Toggles the internal implementation
65
+ */
66
+ define('CRYPT_HASH_MODE_INTERNAL', 1);
67
+ /**
68
+ * Toggles the mhash() implementation, which has been deprecated on PHP 5.3.0+.
69
+ */
70
+ define('CRYPT_HASH_MODE_MHASH', 2);
71
+ /**
72
+ * Toggles the hash() implementation, which works on PHP 5.1.2+.
73
+ */
74
+ define('CRYPT_HASH_MODE_HASH', 3);
75
+ /**#@-*/
76
+
77
+ /**
78
+ * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
79
+ *
80
+ * @author Jim Wigginton <terrafrost@php.net>
81
+ * @version 0.1.0
82
+ * @access public
83
+ * @package Crypt_Hash
84
+ */
85
+ class Crypt_Hash {
86
+ /**
87
+ * Byte-length of compression blocks / key (Internal HMAC)
88
+ *
89
+ * @see Crypt_Hash::setAlgorithm()
90
+ * @var Integer
91
+ * @access private
92
+ */
93
+ var $b;
94
+
95
+ /**
96
+ * Byte-length of hash output (Internal HMAC)
97
+ *
98
+ * @see Crypt_Hash::setHash()
99
+ * @var Integer
100
+ * @access private
101
+ */
102
+ var $l = false;
103
+
104
+ /**
105
+ * Hash Algorithm
106
+ *
107
+ * @see Crypt_Hash::setHash()
108
+ * @var String
109
+ * @access private
110
+ */
111
+ var $hash;
112
+
113
+ /**
114
+ * Key
115
+ *
116
+ * @see Crypt_Hash::setKey()
117
+ * @var String
118
+ * @access private
119
+ */
120
+ var $key = '';
121
+
122
+ /**
123
+ * Outer XOR (Internal HMAC)
124
+ *
125
+ * @see Crypt_Hash::setKey()
126
+ * @var String
127
+ * @access private
128
+ */
129
+ var $opad;
130
+
131
+ /**
132
+ * Inner XOR (Internal HMAC)
133
+ *
134
+ * @see Crypt_Hash::setKey()
135
+ * @var String
136
+ * @access private
137
+ */
138
+ var $ipad;
139
+
140
+ /**
141
+ * Default Constructor.
142
+ *
143
+ * @param optional String $hash
144
+ * @return Crypt_Hash
145
+ * @access public
146
+ */
147
+ function Crypt_Hash($hash = 'sha1')
148
+ {
149
+ if ( !defined('CRYPT_HASH_MODE') ) {
150
+ switch (true) {
151
+ case extension_loaded('hash'):
152
+ define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_HASH);
153
+ break;
154
+ case extension_loaded('mhash'):
155
+ define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_MHASH);
156
+ break;
157
+ default:
158
+ define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_INTERNAL);
159
+ }
160
+ }
161
+
162
+ $this->setHash($hash);
163
+ }
164
+
165
+ /**
166
+ * Sets the key for HMACs
167
+ *
168
+ * Keys can be of any length.
169
+ *
170
+ * @access public
171
+ * @param String $key
172
+ */
173
+ function setKey($key)
174
+ {
175
+ $this->key = $key;
176
+ }
177
+
178
+ /**
179
+ * Sets the hash function.
180
+ *
181
+ * @access public
182
+ * @param String $hash
183
+ */
184
+ function setHash($hash)
185
+ {
186
+ $hash = strtolower($hash);
187
+ switch ($hash) {
188
+ case 'md5-96':
189
+ case 'sha1-96':
190
+ $this->l = 12; // 96 / 8 = 12
191
+ break;
192
+ case 'md2':
193
+ case 'md5':
194
+ $this->l = 16;
195
+ break;
196
+ case 'sha1':
197
+ $this->l = 20;
198
+ break;
199
+ case 'sha256':
200
+ $this->l = 32;
201
+ break;
202
+ case 'sha384':
203
+ $this->l = 48;
204
+ break;
205
+ case 'sha512':
206
+ $this->l = 64;
207
+ }
208
+
209
+ switch ($hash) {
210
+ case 'md2':
211
+ $mode = CRYPT_HASH_MODE == CRYPT_HASH_MODE_HASH && in_array('md2', hash_algos()) ?
212
+ CRYPT_HASH_MODE_HASH : CRYPT_HASH_MODE_INTERNAL;
213
+ break;
214
+ case 'sha384':
215
+ case 'sha512':
216
+ $mode = CRYPT_HASH_MODE == CRYPT_HASH_MODE_MHASH ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE;
217
+ break;
218
+ default:
219
+ $mode = CRYPT_HASH_MODE;
220
+ }
221
+
222
+ switch ( $mode ) {
223
+ case CRYPT_HASH_MODE_MHASH:
224
+ switch ($hash) {
225
+ case 'md5':
226
+ case 'md5-96':
227
+ $this->hash = MHASH_MD5;
228
+ break;
229
+ case 'sha256':
230
+ $this->hash = MHASH_SHA256;
231
+ break;
232
+ case 'sha1':
233
+ case 'sha1-96':
234
+ default:
235
+ $this->hash = MHASH_SHA1;
236
+ }
237
+ return;
238
+ case CRYPT_HASH_MODE_HASH:
239
+ switch ($hash) {
240
+ case 'md5':
241
+ case 'md5-96':
242
+ $this->hash = 'md5';
243
+ return;
244
+ case 'md2':
245
+ case 'sha256':
246
+ case 'sha384':
247
+ case 'sha512':
248
+ $this->hash = $hash;
249
+ return;
250
+ case 'sha1':
251
+ case 'sha1-96':
252
+ default:
253
+ $this->hash = 'sha1';
254
+ }
255
+ return;
256
+ }
257
+
258
+ switch ($hash) {
259
+ case 'md2':
260
+ $this->b = 16;
261
+ $this->hash = array($this, '_md2');
262
+ break;
263
+ case 'md5':
264
+ case 'md5-96':
265
+ $this->b = 64;
266
+ $this->hash = array($this, '_md5');
267
+ break;
268
+ case 'sha256':
269
+ $this->b = 64;
270
+ $this->hash = array($this, '_sha256');
271
+ break;
272
+ case 'sha384':
273
+ case 'sha512':
274
+ $this->b = 128;
275
+ $this->hash = array($this, '_sha512');
276
+ break;
277
+ case 'sha1':
278
+ case 'sha1-96':
279
+ default:
280
+ $this->b = 64;
281
+ $this->hash = array($this, '_sha1');
282
+ }
283
+
284
+ $this->ipad = str_repeat(chr(0x36), $this->b);
285
+ $this->opad = str_repeat(chr(0x5C), $this->b);
286
+ }
287
+
288
+ /**
289
+ * Compute the HMAC.
290
+ *
291
+ * @access public
292
+ * @param String $text
293
+ * @return String
294
+ */
295
+ function hash($text)
296
+ {
297
+ $mode = is_array($this->hash) ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE;
298
+
299
+ if (!empty($this->key)) {
300
+ switch ( $mode ) {
301
+ case CRYPT_HASH_MODE_MHASH:
302
+ $output = mhash($this->hash, $text, $this->key);
303
+ break;
304
+ case CRYPT_HASH_MODE_HASH:
305
+ $output = hash_hmac($this->hash, $text, $this->key, true);
306
+ break;
307
+ case CRYPT_HASH_MODE_INTERNAL:
308
+ /* "Applications that use keys longer than B bytes will first hash the key using H and then use the
309
+ resultant L byte string as the actual key to HMAC."
310
+
311
+ -- http://tools.ietf.org/html/rfc2104#section-2 */
312
+ $key = strlen($this->key) > $this->b ? call_user_func($this->hash, $this->key) : $this->key;
313
+
314
+ $key = str_pad($key, $this->b, chr(0)); // step 1
315
+ $temp = $this->ipad ^ $key; // step 2
316
+ $temp .= $text; // step 3
317
+ $temp = call_user_func($this->hash, $temp); // step 4
318
+ $output = $this->opad ^ $key; // step 5
319
+ $output.= $temp; // step 6
320
+ $output = call_user_func($this->hash, $output); // step 7
321
+ }
322
+ } else {
323
+ switch ( $mode ) {
324
+ case CRYPT_HASH_MODE_MHASH:
325
+ $output = mhash($this->hash, $text);
326
+ break;
327
+ case CRYPT_HASH_MODE_HASH:
328
+ $output = hash($this->hash, $text, true);
329
+ break;
330
+ case CRYPT_HASH_MODE_INTERNAL:
331
+ $output = call_user_func($this->hash, $text);
332
+ }
333
+ }
334
+
335
+ return substr($output, 0, $this->l);
336
+ }
337
+
338
+ /**
339
+ * Returns the hash length (in bytes)
340
+ *
341
+ * @access public
342
+ * @return Integer
343
+ */
344
+ function getLength()
345
+ {
346
+ return $this->l;
347
+ }
348
+
349
+ /**
350
+ * Wrapper for MD5
351
+ *
352
+ * @access private
353
+ * @param String $text
354
+ */
355
+ function _md5($m)
356
+ {
357
+ return pack('H*', md5($m));
358
+ }
359
+
360
+ /**
361
+ * Wrapper for SHA1
362
+ *
363
+ * @access private
364
+ * @param String $text
365
+ */
366
+ function _sha1($m)
367
+ {
368
+ return pack('H*', sha1($m));
369
+ }
370
+
371
+ /**
372
+ * Pure-PHP implementation of MD2
373
+ *
374
+ * See {@link http://tools.ietf.org/html/rfc1319 RFC1319}.
375
+ *
376
+ * @access private
377
+ * @param String $text
378
+ */
379
+ function _md2($m)
380
+ {
381
+ static $s = array(
382
+ 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
383
+ 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188,
384
+ 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24,
385
+ 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251,
386
+ 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63,
387
+ 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50,
388
+ 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165,
389
+ 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210,
390
+ 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157,
391
+ 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27,
392
+ 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15,
393
+ 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197,
394
+ 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65,
395
+ 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123,
396
+ 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233,
397
+ 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228,
398
+ 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237,
399
+ 31, 26, 219, 153, 141, 51, 159, 17, 131, 20
400
+ );
401
+
402
+ // Step 1. Append Padding Bytes
403
+ $pad = 16 - (strlen($m) & 0xF);
404
+ $m.= str_repeat(chr($pad), $pad);
405
+
406
+ $length = strlen($m);
407
+
408
+ // Step 2. Append Checksum
409
+ $c = str_repeat(chr(0), 16);
410
+ $l = chr(0);
411
+ for ($i = 0; $i < $length; $i+= 16) {
412
+ for ($j = 0; $j < 16; $j++) {
413
+ // RFC1319 incorrectly states that C[j] should be set to S[c xor L]
414
+ //$c[$j] = chr($s[ord($m[$i + $j] ^ $l)]);
415
+ // per <http://www.rfc-editor.org/errata_search.php?rfc=1319>, however, C[j] should be set to S[c xor L] xor C[j]
416
+ $c[$j] = chr($s[ord($m[$i + $j] ^ $l)] ^ ord($c[$j]));
417
+ $l = $c[$j];
418
+ }
419
+ }
420
+ $m.= $c;
421
+
422
+ $length+= 16;
423
+
424
+ // Step 3. Initialize MD Buffer
425
+ $x = str_repeat(chr(0), 48);
426
+
427
+ // Step 4. Process Message in 16-Byte Blocks
428
+ for ($i = 0; $i < $length; $i+= 16) {
429
+ for ($j = 0; $j < 16; $j++) {
430
+ $x[$j + 16] = $m[$i + $j];
431
+ $x[$j + 32] = $x[$j + 16] ^ $x[$j];
432
+ }
433
+ $t = chr(0);
434
+ for ($j = 0; $j < 18; $j++) {
435
+ for ($k = 0; $k < 48; $k++) {
436
+ $x[$k] = $t = $x[$k] ^ chr($s[ord($t)]);
437
+ //$t = $x[$k] = $x[$k] ^ chr($s[ord($t)]);
438
+ }
439
+ $t = chr(ord($t) + $j);
440
+ }
441
+ }
442
+
443
+ // Step 5. Output
444
+ return substr($x, 0, 16);
445
+ }
446
+
447
+ /**
448
+ * Pure-PHP implementation of SHA256
449
+ *
450
+ * See {@link http://en.wikipedia.org/wiki/SHA_hash_functions#SHA-256_.28a_SHA-2_variant.29_pseudocode SHA-256 (a SHA-2 variant) pseudocode - Wikipedia}.
451
+ *
452
+ * @access private
453
+ * @param String $text
454
+ */
455
+ function _sha256($m)
456
+ {
457
+ if (extension_loaded('suhosin')) {
458
+ return pack('H*', sha256($m));
459
+ }
460
+
461
+ // Initialize variables
462
+ $hash = array(
463
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
464
+ );
465
+ // Initialize table of round constants
466
+ // (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311)
467
+ static $k = array(
468
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
469
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
470
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
471
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
472
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
473
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
474
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
475
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
476
+ );
477
+
478
+ // Pre-processing
479
+ $length = strlen($m);
480
+ // to round to nearest 56 mod 64, we'll add 64 - (length + (64 - 56)) % 64
481
+ $m.= str_repeat(chr(0), 64 - (($length + 8) & 0x3F));
482
+ $m[$length] = chr(0x80);
483
+ // we don't support hashing strings 512MB long
484
+ $m.= pack('N2', 0, $length << 3);
485
+
486
+ // Process the message in successive 512-bit chunks
487
+ $chunks = str_split($m, 64);
488
+ foreach ($chunks as $chunk) {
489
+ $w = array();
490
+ for ($i = 0; $i < 16; $i++) {
491
+ extract(unpack('Ntemp', $this->_string_shift($chunk, 4)));
492
+ $w[] = $temp;
493
+ }
494
+
495
+ // Extend the sixteen 32-bit words into sixty-four 32-bit words
496
+ for ($i = 16; $i < 64; $i++) {
497
+ $s0 = $this->_rightRotate($w[$i - 15], 7) ^
498
+ $this->_rightRotate($w[$i - 15], 18) ^
499
+ $this->_rightShift( $w[$i - 15], 3);
500
+ $s1 = $this->_rightRotate($w[$i - 2], 17) ^
501
+ $this->_rightRotate($w[$i - 2], 19) ^
502
+ $this->_rightShift( $w[$i - 2], 10);
503
+ $w[$i] = $this->_add($w[$i - 16], $s0, $w[$i - 7], $s1);
504
+
505
+ }
506
+
507
+ // Initialize hash value for this chunk
508
+ list($a, $b, $c, $d, $e, $f, $g, $h) = $hash;
509
+
510
+ // Main loop
511
+ for ($i = 0; $i < 64; $i++) {
512
+ $s0 = $this->_rightRotate($a, 2) ^
513
+ $this->_rightRotate($a, 13) ^
514
+ $this->_rightRotate($a, 22);
515
+ $maj = ($a & $b) ^
516
+ ($a & $c) ^
517
+ ($b & $c);
518
+ $t2 = $this->_add($s0, $maj);
519
+
520
+ $s1 = $this->_rightRotate($e, 6) ^
521
+ $this->_rightRotate($e, 11) ^
522
+ $this->_rightRotate($e, 25);
523
+ $ch = ($e & $f) ^
524
+ ($this->_not($e) & $g);
525
+ $t1 = $this->_add($h, $s1, $ch, $k[$i], $w[$i]);
526
+
527
+ $h = $g;
528
+ $g = $f;
529
+ $f = $e;
530
+ $e = $this->_add($d, $t1);
531
+ $d = $c;
532
+ $c = $b;
533
+ $b = $a;
534
+ $a = $this->_add($t1, $t2);
535
+ }
536
+
537
+ // Add this chunk's hash to result so far
538
+ $hash = array(
539
+ $this->_add($hash[0], $a),
540
+ $this->_add($hash[1], $b),
541
+ $this->_add($hash[2], $c),
542
+ $this->_add($hash[3], $d),
543
+ $this->_add($hash[4], $e),
544
+ $this->_add($hash[5], $f),
545
+ $this->_add($hash[6], $g),
546
+ $this->_add($hash[7], $h)
547
+ );
548
+ }
549
+
550
+ // Produce the final hash value (big-endian)
551
+ return pack('N8', $hash[0], $hash[1], $hash[2], $hash[3], $hash[4], $hash[5], $hash[6], $hash[7]);
552
+ }
553
+
554
+ /**
555
+ * Pure-PHP implementation of SHA384 and SHA512
556
+ *
557
+ * @access private
558
+ * @param String $text
559
+ */
560
+ function _sha512($m)
561
+ {
562
+ if (!class_exists('Math_BigInteger')) {
563
+ require_once('Math/BigInteger.php');
564
+ }
565
+
566
+ static $init384, $init512, $k;
567
+
568
+ if (!isset($k)) {
569
+ // Initialize variables
570
+ $init384 = array( // initial values for SHA384
571
+ 'cbbb9d5dc1059ed8', '629a292a367cd507', '9159015a3070dd17', '152fecd8f70e5939',
572
+ '67332667ffc00b31', '8eb44a8768581511', 'db0c2e0d64f98fa7', '47b5481dbefa4fa4'
573
+ );
574
+ $init512 = array( // initial values for SHA512
575
+ '6a09e667f3bcc908', 'bb67ae8584caa73b', '3c6ef372fe94f82b', 'a54ff53a5f1d36f1',
576
+ '510e527fade682d1', '9b05688c2b3e6c1f', '1f83d9abfb41bd6b', '5be0cd19137e2179'
577
+ );
578
+
579
+ for ($i = 0; $i < 8; $i++) {
580
+ $init384[$i] = new Math_BigInteger($init384[$i], 16);
581
+ $init384[$i]->setPrecision(64);
582
+ $init512[$i] = new Math_BigInteger($init512[$i], 16);
583
+ $init512[$i]->setPrecision(64);
584
+ }
585
+
586
+ // Initialize table of round constants
587
+ // (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409)
588
+ $k = array(
589
+ '428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc',
590
+ '3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118',
591
+ 'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2',
592
+ '72be5d74f27b896f', '80deb1fe3b1696b1', '9bdc06a725c71235', 'c19bf174cf692694',
593
+ 'e49b69c19ef14ad2', 'efbe4786384f25e3', '0fc19dc68b8cd5b5', '240ca1cc77ac9c65',
594
+ '2de92c6f592b0275', '4a7484aa6ea6e483', '5cb0a9dcbd41fbd4', '76f988da831153b5',
595
+ '983e5152ee66dfab', 'a831c66d2db43210', 'b00327c898fb213f', 'bf597fc7beef0ee4',
596
+ 'c6e00bf33da88fc2', 'd5a79147930aa725', '06ca6351e003826f', '142929670a0e6e70',
597
+ '27b70a8546d22ffc', '2e1b21385c26c926', '4d2c6dfc5ac42aed', '53380d139d95b3df',
598
+ '650a73548baf63de', '766a0abb3c77b2a8', '81c2c92e47edaee6', '92722c851482353b',
599
+ 'a2bfe8a14cf10364', 'a81a664bbc423001', 'c24b8b70d0f89791', 'c76c51a30654be30',
600
+ 'd192e819d6ef5218', 'd69906245565a910', 'f40e35855771202a', '106aa07032bbd1b8',
601
+ '19a4c116b8d2d0c8', '1e376c085141ab53', '2748774cdf8eeb99', '34b0bcb5e19b48a8',
602
+ '391c0cb3c5c95a63', '4ed8aa4ae3418acb', '5b9cca4f7763e373', '682e6ff3d6b2b8a3',
603
+ '748f82ee5defb2fc', '78a5636f43172f60', '84c87814a1f0ab72', '8cc702081a6439ec',
604
+ '90befffa23631e28', 'a4506cebde82bde9', 'bef9a3f7b2c67915', 'c67178f2e372532b',
605
+ 'ca273eceea26619c', 'd186b8c721c0c207', 'eada7dd6cde0eb1e', 'f57d4f7fee6ed178',
606
+ '06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b',
607
+ '28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c',
608
+ '4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817'
609
+ );
610
+
611
+ for ($i = 0; $i < 80; $i++) {
612
+ $k[$i] = new Math_BigInteger($k[$i], 16);
613
+ }
614
+ }
615
+
616
+ $hash = $this->l == 48 ? $init384 : $init512;
617
+
618
+ // Pre-processing
619
+ $length = strlen($m);
620
+ // to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128
621
+ $m.= str_repeat(chr(0), 128 - (($length + 16) & 0x7F));
622
+ $m[$length] = chr(0x80);
623
+ // we don't support hashing strings 512MB long
624
+ $m.= pack('N4', 0, 0, 0, $length << 3);
625
+
626
+ // Process the message in successive 1024-bit chunks
627
+ $chunks = str_split($m, 128);
628
+ foreach ($chunks as $chunk) {
629
+ $w = array();
630
+ for ($i = 0; $i < 16; $i++) {
631
+ $temp = new Math_BigInteger($this->_string_shift($chunk, 8), 256);
632
+ $temp->setPrecision(64);
633
+ $w[] = $temp;
634
+ }
635
+
636
+ // Extend the sixteen 32-bit words into eighty 32-bit words
637
+ for ($i = 16; $i < 80; $i++) {
638
+ $temp = array(
639
+ $w[$i - 15]->bitwise_rightRotate(1),
640
+ $w[$i - 15]->bitwise_rightRotate(8),
641
+ $w[$i - 15]->bitwise_rightShift(7)
642
+ );
643
+ $s0 = $temp[0]->bitwise_xor($temp[1]);
644
+ $s0 = $s0->bitwise_xor($temp[2]);
645
+ $temp = array(
646
+ $w[$i - 2]->bitwise_rightRotate(19),
647
+ $w[$i - 2]->bitwise_rightRotate(61),
648
+ $w[$i - 2]->bitwise_rightShift(6)
649
+ );
650
+ $s1 = $temp[0]->bitwise_xor($temp[1]);
651
+ $s1 = $s1->bitwise_xor($temp[2]);
652
+ $w[$i] = $w[$i - 16]->copy();
653
+ $w[$i] = $w[$i]->add($s0);
654
+ $w[$i] = $w[$i]->add($w[$i - 7]);
655
+ $w[$i] = $w[$i]->add($s1);
656
+ }
657
+
658
+ // Initialize hash value for this chunk
659
+ $a = $hash[0]->copy();
660
+ $b = $hash[1]->copy();
661
+ $c = $hash[2]->copy();
662
+ $d = $hash[3]->copy();
663
+ $e = $hash[4]->copy();
664
+ $f = $hash[5]->copy();
665
+ $g = $hash[6]->copy();
666
+ $h = $hash[7]->copy();
667
+
668
+ // Main loop
669
+ for ($i = 0; $i < 80; $i++) {
670
+ $temp = array(
671
+ $a->bitwise_rightRotate(28),
672
+ $a->bitwise_rightRotate(34),
673
+ $a->bitwise_rightRotate(39)
674
+ );
675
+ $s0 = $temp[0]->bitwise_xor($temp[1]);
676
+ $s0 = $s0->bitwise_xor($temp[2]);
677
+ $temp = array(
678
+ $a->bitwise_and($b),
679
+ $a->bitwise_and($c),
680
+ $b->bitwise_and($c)
681
+ );
682
+ $maj = $temp[0]->bitwise_xor($temp[1]);
683
+ $maj = $maj->bitwise_xor($temp[2]);
684
+ $t2 = $s0->add($maj);
685
+
686
+ $temp = array(
687
+ $e->bitwise_rightRotate(14),
688
+ $e->bitwise_rightRotate(18),
689
+ $e->bitwise_rightRotate(41)
690
+ );
691
+ $s1 = $temp[0]->bitwise_xor($temp[1]);
692
+ $s1 = $s1->bitwise_xor($temp[2]);
693
+ $temp = array(
694
+ $e->bitwise_and($f),
695
+ $g->bitwise_and($e->bitwise_not())
696
+ );
697
+ $ch = $temp[0]->bitwise_xor($temp[1]);
698
+ $t1 = $h->add($s1);
699
+ $t1 = $t1->add($ch);
700
+ $t1 = $t1->add($k[$i]);
701
+ $t1 = $t1->add($w[$i]);
702
+
703
+ $h = $g->copy();
704
+ $g = $f->copy();
705
+ $f = $e->copy();
706
+ $e = $d->add($t1);
707
+ $d = $c->copy();
708
+ $c = $b->copy();
709
+ $b = $a->copy();
710
+ $a = $t1->add($t2);
711
+ }
712
+
713
+ // Add this chunk's hash to result so far
714
+ $hash = array(
715
+ $hash[0]->add($a),
716
+ $hash[1]->add($b),
717
+ $hash[2]->add($c),
718
+ $hash[3]->add($d),
719
+ $hash[4]->add($e),
720
+ $hash[5]->add($f),
721
+ $hash[6]->add($g),
722
+ $hash[7]->add($h)
723
+ );
724
+ }
725
+
726
+ // Produce the final hash value (big-endian)
727
+ // (Crypt_Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here)
728
+ $temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() .
729
+ $hash[4]->toBytes() . $hash[5]->toBytes();
730
+ if ($this->l != 48) {
731
+ $temp.= $hash[6]->toBytes() . $hash[7]->toBytes();
732
+ }
733
+
734
+ return $temp;
735
+ }
736
+
737
+ /**
738
+ * Right Rotate
739
+ *
740
+ * @access private
741
+ * @param Integer $int
742
+ * @param Integer $amt
743
+ * @see _sha256()
744
+ * @return Integer
745
+ */
746
+ function _rightRotate($int, $amt)
747
+ {
748
+ $invamt = 32 - $amt;
749
+ $mask = (1 << $invamt) - 1;
750
+ return (($int << $invamt) & 0xFFFFFFFF) | (($int >> $amt) & $mask);
751
+ }
752
+
753
+ /**
754
+ * Right Shift
755
+ *
756
+ * @access private
757
+ * @param Integer $int
758
+ * @param Integer $amt
759
+ * @see _sha256()
760
+ * @return Integer
761
+ */
762
+ function _rightShift($int, $amt)
763
+ {
764
+ $mask = (1 << (32 - $amt)) - 1;
765
+ return ($int >> $amt) & $mask;
766
+ }
767
+
768
+ /**
769
+ * Not
770
+ *
771
+ * @access private
772
+ * @param Integer $int
773
+ * @see _sha256()
774
+ * @return Integer
775
+ */
776
+ function _not($int)
777
+ {
778
+ return ~$int & 0xFFFFFFFF;
779
+ }
780
+
781
+ /**
782
+ * Add
783
+ *
784
+ * _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the
785
+ * possibility of overflow exists, care has to be taken. Math_BigInteger() could be used but this should be faster.
786
+ *
787
+ * @param String $string
788
+ * @param optional Integer $index
789
+ * @return String
790
+ * @see _sha256()
791
+ * @access private
792
+ */
793
+ function _add()
794
+ {
795
+ static $mod;
796
+ if (!isset($mod)) {
797
+ $mod = pow(2, 32);
798
+ }
799
+
800
+ $result = 0;
801
+ $arguments = func_get_args();
802
+ foreach ($arguments as $argument) {
803
+ $result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument;
804
+ }
805
+
806
+ return fmod($result, $mod);
807
+ }
808
+
809
+ /**
810
+ * String Shift
811
+ *
812
+ * Inspired by array_shift
813
+ *
814
+ * @param String $string
815
+ * @param optional Integer $index
816
+ * @return String
817
+ * @access private
818
+ */
819
+ function _string_shift(&$string, $index = 1)
820
+ {
821
+ $substr = substr($string, 0, $index);
822
+ $string = substr($string, $index);
823
+ return $substr;
824
+ }
825
+ }
phpseclib/Crypt/RC4.php ADDED
@@ -0,0 +1,564 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP implementation of RC4.
6
+ *
7
+ * Uses mcrypt, if available, and an internal implementation, otherwise.
8
+ *
9
+ * PHP versions 4 and 5
10
+ *
11
+ * Useful resources are as follows:
12
+ *
13
+ * - {@link http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt ARCFOUR Algorithm}
14
+ * - {@link http://en.wikipedia.org/wiki/RC4 - Wikipedia: RC4}
15
+ *
16
+ * RC4 is also known as ARCFOUR or ARC4. The reason is elaborated upon at Wikipedia. This class is named RC4 and not
17
+ * ARCFOUR or ARC4 because RC4 is how it is refered to in the SSH1 specification.
18
+ *
19
+ * Here's a short example of how to use this library:
20
+ * <code>
21
+ * <?php
22
+ * include('Crypt/RC4.php');
23
+ *
24
+ * $rc4 = new Crypt_RC4();
25
+ *
26
+ * $rc4->setKey('abcdefgh');
27
+ *
28
+ * $size = 10 * 1024;
29
+ * $plaintext = '';
30
+ * for ($i = 0; $i < $size; $i++) {
31
+ * $plaintext.= 'a';
32
+ * }
33
+ *
34
+ * echo $rc4->decrypt($rc4->encrypt($plaintext));
35
+ * ?>
36
+ * </code>
37
+ *
38
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
39
+ * of this software and associated documentation files (the "Software"), to deal
40
+ * in the Software without restriction, including without limitation the rights
41
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
42
+ * copies of the Software, and to permit persons to whom the Software is
43
+ * furnished to do so, subject to the following conditions:
44
+ *
45
+ * The above copyright notice and this permission notice shall be included in
46
+ * all copies or substantial portions of the Software.
47
+ *
48
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
49
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
50
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
51
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
52
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
53
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
54
+ * THE SOFTWARE.
55
+ *
56
+ * @category Crypt
57
+ * @package Crypt_RC4
58
+ * @author Jim Wigginton <terrafrost@php.net>
59
+ * @copyright MMVII Jim Wigginton
60
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
61
+ * @version $Id: RC4.php,v 1.8 2009/06/09 04:00:38 terrafrost Exp $
62
+ * @link http://phpseclib.sourceforge.net
63
+ */
64
+
65
+ /**#@+
66
+ * @access private
67
+ * @see Crypt_RC4::Crypt_RC4()
68
+ */
69
+ /**
70
+ * Toggles the internal implementation
71
+ */
72
+ define('CRYPT_RC4_MODE_INTERNAL', 1);
73
+ /**
74
+ * Toggles the mcrypt implementation
75
+ */
76
+ define('CRYPT_RC4_MODE_MCRYPT', 2);
77
+ /**#@-*/
78
+
79
+ /**#@+
80
+ * @access private
81
+ * @see Crypt_RC4::_crypt()
82
+ */
83
+ define('CRYPT_RC4_ENCRYPT', 0);
84
+ define('CRYPT_RC4_DECRYPT', 1);
85
+ /**#@-*/
86
+
87
+ /**
88
+ * Pure-PHP implementation of RC4.
89
+ *
90
+ * @author Jim Wigginton <terrafrost@php.net>
91
+ * @version 0.1.0
92
+ * @access public
93
+ * @package Crypt_RC4
94
+ */
95
+ class Crypt_RC4 {
96
+ /**
97
+ * The Key
98
+ *
99
+ * @see Crypt_RC4::setKey()
100
+ * @var String
101
+ * @access private
102
+ */
103
+ var $key = "\0";
104
+
105
+ /**
106
+ * The Key Stream for encryption
107
+ *
108
+ * If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object
109
+ *
110
+ * @see Crypt_RC4::setKey()
111
+ * @var Array
112
+ * @access private
113
+ */
114
+ var $encryptStream = false;
115
+
116
+ /**
117
+ * The Key Stream for decryption
118
+ *
119
+ * If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object
120
+ *
121
+ * @see Crypt_RC4::setKey()
122
+ * @var Array
123
+ * @access private
124
+ */
125
+ var $decryptStream = false;
126
+
127
+ /**
128
+ * The $i and $j indexes for encryption
129
+ *
130
+ * @see Crypt_RC4::_crypt()
131
+ * @var Integer
132
+ * @access private
133
+ */
134
+ var $encryptIndex = 0;
135
+
136
+ /**
137
+ * The $i and $j indexes for decryption
138
+ *
139
+ * @see Crypt_RC4::_crypt()
140
+ * @var Integer
141
+ * @access private
142
+ */
143
+ var $decryptIndex = 0;
144
+
145
+ /**
146
+ * MCrypt parameters
147
+ *
148
+ * @see Crypt_RC4::setMCrypt()
149
+ * @var Array
150
+ * @access private
151
+ */
152
+ var $mcrypt = array('', '');
153
+
154
+ /**
155
+ * The Encryption Algorithm
156
+ *
157
+ * Only used if CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT. Only possible values are MCRYPT_RC4 or MCRYPT_ARCFOUR.
158
+ *
159
+ * @see Crypt_RC4::Crypt_RC4()
160
+ * @var Integer
161
+ * @access private
162
+ */
163
+ var $mode;
164
+
165
+ /**
166
+ * Continuous Buffer status
167
+ *
168
+ * @see Crypt_RC4::enableContinuousBuffer()
169
+ * @var Boolean
170
+ * @access private
171
+ */
172
+ var $continuousBuffer = false;
173
+
174
+ /**
175
+ * Default Constructor.
176
+ *
177
+ * Determines whether or not the mcrypt extension should be used.
178
+ *
179
+ * @param optional Integer $mode
180
+ * @return Crypt_RC4
181
+ * @access public
182
+ */
183
+ function Crypt_RC4()
184
+ {
185
+ if ( !defined('CRYPT_RC4_MODE') ) {
186
+ switch (true) {
187
+ case extension_loaded('mcrypt') && (defined('MCRYPT_ARCFOUR') || defined('MCRYPT_RC4')):
188
+ // i'd check to see if rc4 was supported, by doing in_array('arcfour', mcrypt_list_algorithms('')),
189
+ // but since that can be changed after the object has been created, there doesn't seem to be
190
+ // a lot of point...
191
+ define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_MCRYPT);
192
+ break;
193
+ default:
194
+ define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_INTERNAL);
195
+ }
196
+ }
197
+
198
+ switch ( CRYPT_RC4_MODE ) {
199
+ case CRYPT_RC4_MODE_MCRYPT:
200
+ switch (true) {
201
+ case defined('MCRYPT_ARCFOUR'):
202
+ $this->mode = MCRYPT_ARCFOUR;
203
+ break;
204
+ case defined('MCRYPT_RC4');
205
+ $this->mode = MCRYPT_RC4;
206
+ }
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Sets the key.
212
+ *
213
+ * Keys can be between 1 and 256 bytes long. If they are longer then 256 bytes, the first 256 bytes will
214
+ * be used. If no key is explicitly set, it'll be assumed to be a single null byte.
215
+ *
216
+ * @access public
217
+ * @param String $key
218
+ */
219
+ function setKey($key)
220
+ {
221
+ $this->key = $key;
222
+
223
+ if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
224
+ return;
225
+ }
226
+
227
+ $keyLength = strlen($key);
228
+ $keyStream = array();
229
+ for ($i = 0; $i < 256; $i++) {
230
+ $keyStream[$i] = $i;
231
+ }
232
+ $j = 0;
233
+ for ($i = 0; $i < 256; $i++) {
234
+ $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255;
235
+ $temp = $keyStream[$i];
236
+ $keyStream[$i] = $keyStream[$j];
237
+ $keyStream[$j] = $temp;
238
+ }
239
+
240
+ $this->encryptIndex = $this->decryptIndex = array(0, 0);
241
+ $this->encryptStream = $this->decryptStream = $keyStream;
242
+ }
243
+
244
+ /**
245
+ * Sets the password.
246
+ *
247
+ * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
248
+ * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}:
249
+ * $hash, $salt, $method, $dkLen
250
+ *
251
+ * @param String $password
252
+ * @param optional String $method
253
+ * @access public
254
+ */
255
+ function setPassword($password, $method = 'pbkdf2')
256
+ {
257
+ $key = '';
258
+
259
+ switch ($method) {
260
+ default: // 'pbkdf2'
261
+ list(, , $hash, $salt, $count) = func_get_args();
262
+ if (!isset($hash)) {
263
+ $hash = 'sha1';
264
+ }
265
+ // WPA and WPA use the SSID as the salt
266
+ if (!isset($salt)) {
267
+ $salt = 'phpseclib';
268
+ }
269
+ // RFC2898#section-4.2 uses 1,000 iterations by default
270
+ // WPA and WPA2 use 4,096.
271
+ if (!isset($count)) {
272
+ $count = 1000;
273
+ }
274
+ if (!isset($count)) {
275
+ $count = 1000;
276
+ }
277
+ if (!isset($dkLen)) {
278
+ $count = 1000;
279
+ }
280
+
281
+ if (!class_exists('Crypt_Hash')) {
282
+ require_once('Crypt/Hash.php');
283
+ }
284
+
285
+ $i = 1;
286
+ while (strlen($key) < $dkLen) {
287
+ //$dk.= $this->_pbkdf($password, $salt, $count, $i++);
288
+ $hmac = new Crypt_Hash();
289
+ $hmac->setHash($hash);
290
+ $hmac->setKey($password);
291
+ $f = $u = $hmac->hash($salt . pack('N', $i++));
292
+ for ($j = 2; $j <= $count; $j++) {
293
+ $u = $hmac->hash($u);
294
+ $f^= $u;
295
+ }
296
+ $key.= $f;
297
+ }
298
+ }
299
+
300
+ $this->setKey(substr($key, 0, $dkLen));
301
+ }
302
+
303
+ /**
304
+ * Dummy function.
305
+ *
306
+ * Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1].
307
+ * If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before
308
+ * calling setKey().
309
+ *
310
+ * [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way. Since, in that protocol,
311
+ * the IV's are relatively easy to predict, an attack described by
312
+ * {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir}
313
+ * can be used to quickly guess at the rest of the key. The following links elaborate:
314
+ *
315
+ * {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009}
316
+ * {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack}
317
+ *
318
+ * @param String $iv
319
+ * @see Crypt_RC4::setKey()
320
+ * @access public
321
+ */
322
+ function setIV($iv)
323
+ {
324
+ }
325
+
326
+ /**
327
+ * Sets MCrypt parameters. (optional)
328
+ *
329
+ * If MCrypt is being used, empty strings will be used, unless otherwise specified.
330
+ *
331
+ * @link http://php.net/function.mcrypt-module-open#function.mcrypt-module-open
332
+ * @access public
333
+ * @param optional Integer $algorithm_directory
334
+ * @param optional Integer $mode_directory
335
+ */
336
+ function setMCrypt($algorithm_directory = '', $mode_directory = '')
337
+ {
338
+ if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
339
+ $this->mcrypt = array($algorithm_directory, $mode_directory);
340
+ $this->_closeMCrypt();
341
+ }
342
+ }
343
+
344
+ /**
345
+ * Encrypts a message.
346
+ *
347
+ * @see Crypt_RC4::_crypt()
348
+ * @access public
349
+ * @param String $plaintext
350
+ */
351
+ function encrypt($plaintext)
352
+ {
353
+ return $this->_crypt($plaintext, CRYPT_RC4_ENCRYPT);
354
+ }
355
+
356
+ /**
357
+ * Decrypts a message.
358
+ *
359
+ * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
360
+ * Atleast if the continuous buffer is disabled.
361
+ *
362
+ * @see Crypt_RC4::_crypt()
363
+ * @access public
364
+ * @param String $ciphertext
365
+ */
366
+ function decrypt($ciphertext)
367
+ {
368
+ return $this->_crypt($ciphertext, CRYPT_RC4_DECRYPT);
369
+ }
370
+
371
+ /**
372
+ * Encrypts or decrypts a message.
373
+ *
374
+ * @see Crypt_RC4::encrypt()
375
+ * @see Crypt_RC4::decrypt()
376
+ * @access private
377
+ * @param String $text
378
+ * @param Integer $mode
379
+ */
380
+ function _crypt($text, $mode)
381
+ {
382
+ if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
383
+ $keyStream = $mode == CRYPT_RC4_ENCRYPT ? 'encryptStream' : 'decryptStream';
384
+
385
+ if ($this->$keyStream === false) {
386
+ $this->$keyStream = mcrypt_module_open($this->mode, $this->mcrypt[0], MCRYPT_MODE_STREAM, $this->mcrypt[1]);
387
+ mcrypt_generic_init($this->$keyStream, $this->key, '');
388
+ } else if (!$this->continuousBuffer) {
389
+ mcrypt_generic_init($this->$keyStream, $this->key, '');
390
+ }
391
+ $newText = mcrypt_generic($this->$keyStream, $text);
392
+ if (!$this->continuousBuffer) {
393
+ mcrypt_generic_deinit($this->$keyStream);
394
+ }
395
+
396
+ return $newText;
397
+ }
398
+
399
+ if ($this->encryptStream === false) {
400
+ $this->setKey($this->key);
401
+ }
402
+
403
+ switch ($mode) {
404
+ case CRYPT_RC4_ENCRYPT:
405
+ $keyStream = $this->encryptStream;
406
+ list($i, $j) = $this->encryptIndex;
407
+ break;
408
+ case CRYPT_RC4_DECRYPT:
409
+ $keyStream = $this->decryptStream;
410
+ list($i, $j) = $this->decryptIndex;
411
+ }
412
+
413
+ $newText = '';
414
+ for ($k = 0; $k < strlen($text); $k++) {
415
+ $i = ($i + 1) & 255;
416
+ $j = ($j + $keyStream[$i]) & 255;
417
+ $temp = $keyStream[$i];
418
+ $keyStream[$i] = $keyStream[$j];
419
+ $keyStream[$j] = $temp;
420
+ $temp = $keyStream[($keyStream[$i] + $keyStream[$j]) & 255];
421
+ $newText.= chr(ord($text[$k]) ^ $temp);
422
+ }
423
+
424
+ if ($this->continuousBuffer) {
425
+ switch ($mode) {
426
+ case CRYPT_RC4_ENCRYPT:
427
+ $this->encryptStream = $keyStream;
428
+ $this->encryptIndex = array($i, $j);
429
+ break;
430
+ case CRYPT_RC4_DECRYPT:
431
+ $this->decryptStream = $keyStream;
432
+ $this->decryptIndex = array($i, $j);
433
+ }
434
+ }
435
+
436
+ return $newText;
437
+ }
438
+
439
+ /**
440
+ * Treat consecutive "packets" as if they are a continuous buffer.
441
+ *
442
+ * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
443
+ * will yield different outputs:
444
+ *
445
+ * <code>
446
+ * echo $rc4->encrypt(substr($plaintext, 0, 8));
447
+ * echo $rc4->encrypt(substr($plaintext, 8, 8));
448
+ * </code>
449
+ * <code>
450
+ * echo $rc4->encrypt($plaintext);
451
+ * </code>
452
+ *
453
+ * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
454
+ * another, as demonstrated with the following:
455
+ *
456
+ * <code>
457
+ * $rc4->encrypt(substr($plaintext, 0, 8));
458
+ * echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));
459
+ * </code>
460
+ * <code>
461
+ * echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));
462
+ * </code>
463
+ *
464
+ * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
465
+ * outputs. The reason is due to the fact that the initialization vector's change after every encryption /
466
+ * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
467
+ *
468
+ * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
469
+ * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
470
+ * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
471
+ * however, they are also less intuitive and more likely to cause you problems.
472
+ *
473
+ * @see Crypt_RC4::disableContinuousBuffer()
474
+ * @access public
475
+ */
476
+ function enableContinuousBuffer()
477
+ {
478
+ $this->continuousBuffer = true;
479
+ }
480
+
481
+ /**
482
+ * Treat consecutive packets as if they are a discontinuous buffer.
483
+ *
484
+ * The default behavior.
485
+ *
486
+ * @see Crypt_RC4::enableContinuousBuffer()
487
+ * @access public
488
+ */
489
+ function disableContinuousBuffer()
490
+ {
491
+ if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_INTERNAL ) {
492
+ $this->encryptIndex = $this->decryptIndex = array(0, 0);
493
+ $this->setKey($this->key);
494
+ }
495
+
496
+ $this->continuousBuffer = false;
497
+ }
498
+
499
+ /**
500
+ * Dummy function.
501
+ *
502
+ * Since RC4 is a stream cipher and not a block cipher, no padding is necessary. The only reason this function is
503
+ * included is so that you can switch between a block cipher and a stream cipher transparently.
504
+ *
505
+ * @see Crypt_RC4::disablePadding()
506
+ * @access public
507
+ */
508
+ function enablePadding()
509
+ {
510
+ }
511
+
512
+ /**
513
+ * Dummy function.
514
+ *
515
+ * @see Crypt_RC4::enablePadding()
516
+ * @access public
517
+ */
518
+ function disablePadding()
519
+ {
520
+ }
521
+
522
+ /**
523
+ * Class destructor.
524
+ *
525
+ * Will be called, automatically, if you're using PHP5. If you're using PHP4, call it yourself. Only really
526
+ * needs to be called if mcrypt is being used.
527
+ *
528
+ * @access public
529
+ */
530
+ function __destruct()
531
+ {
532
+ if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
533
+ $this->_closeMCrypt();
534
+ }
535
+ }
536
+
537
+ /**
538
+ * Properly close the MCrypt objects.
539
+ *
540
+ * @access prviate
541
+ */
542
+ function _closeMCrypt()
543
+ {
544
+ if ( $this->encryptStream !== false ) {
545
+ if ( $this->continuousBuffer ) {
546
+ mcrypt_generic_deinit($this->encryptStream);
547
+ }
548
+
549
+ mcrypt_module_close($this->encryptStream);
550
+
551
+ $this->encryptStream = false;
552
+ }
553
+
554
+ if ( $this->decryptStream !== false ) {
555
+ if ( $this->continuousBuffer ) {
556
+ mcrypt_generic_deinit($this->decryptStream);
557
+ }
558
+
559
+ mcrypt_module_close($this->decryptStream);
560
+
561
+ $this->decryptStream = false;
562
+ }
563
+ }
564
+ }
phpseclib/Crypt/RSA.php ADDED
@@ -0,0 +1,2520 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
6
+ *
7
+ * PHP versions 4 and 5
8
+ *
9
+ * Here's an example of how to encrypt and decrypt text with this library:
10
+ * <code>
11
+ * <?php
12
+ * include('Crypt/RSA.php');
13
+ *
14
+ * $rsa = new Crypt_RSA();
15
+ * extract($rsa->createKey());
16
+ *
17
+ * $plaintext = 'terrafrost';
18
+ *
19
+ * $rsa->loadKey($privatekey);
20
+ * $ciphertext = $rsa->encrypt($plaintext);
21
+ *
22
+ * $rsa->loadKey($publickey);
23
+ * echo $rsa->decrypt($ciphertext);
24
+ * ?>
25
+ * </code>
26
+ *
27
+ * Here's an example of how to create signatures and verify signatures with this library:
28
+ * <code>
29
+ * <?php
30
+ * include('Crypt/RSA.php');
31
+ *
32
+ * $rsa = new Crypt_RSA();
33
+ * extract($rsa->createKey());
34
+ *
35
+ * $plaintext = 'terrafrost';
36
+ *
37
+ * $rsa->loadKey($privatekey);
38
+ * $signature = $rsa->sign($plaintext);
39
+ *
40
+ * $rsa->loadKey($publickey);
41
+ * echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
42
+ * ?>
43
+ * </code>
44
+ *
45
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
46
+ * of this software and associated documentation files (the "Software"), to deal
47
+ * in the Software without restriction, including without limitation the rights
48
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
49
+ * copies of the Software, and to permit persons to whom the Software is
50
+ * furnished to do so, subject to the following conditions:
51
+ *
52
+ * The above copyright notice and this permission notice shall be included in
53
+ * all copies or substantial portions of the Software.
54
+ *
55
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
56
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
57
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
58
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
59
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
60
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
61
+ * THE SOFTWARE.
62
+ *
63
+ * @category Crypt
64
+ * @package Crypt_RSA
65
+ * @author Jim Wigginton <terrafrost@php.net>
66
+ * @copyright MMIX Jim Wigginton
67
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
68
+ * @version $Id: RSA.php,v 1.19 2010/09/12 21:58:54 terrafrost Exp $
69
+ * @link http://phpseclib.sourceforge.net
70
+ */
71
+
72
+ /**
73
+ * Include Math_BigInteger
74
+ */
75
+ require_once('Math/BigInteger.php');
76
+
77
+ /**
78
+ * Include Crypt_Random
79
+ */
80
+ require_once('Crypt/Random.php');
81
+
82
+ /**
83
+ * Include Crypt_Hash
84
+ */
85
+ require_once('Crypt/Hash.php');
86
+
87
+ /**#@+
88
+ * @access public
89
+ * @see Crypt_RSA::encrypt()
90
+ * @see Crypt_RSA::decrypt()
91
+ */
92
+ /**
93
+ * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
94
+ * (OAEP) for encryption / decryption.
95
+ *
96
+ * Uses sha1 by default.
97
+ *
98
+ * @see Crypt_RSA::setHash()
99
+ * @see Crypt_RSA::setMGFHash()
100
+ */
101
+ define('CRYPT_RSA_ENCRYPTION_OAEP', 1);
102
+ /**
103
+ * Use PKCS#1 padding.
104
+ *
105
+ * Although CRYPT_RSA_ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
106
+ * compatability with protocols (like SSH-1) written before OAEP's introduction.
107
+ */
108
+ define('CRYPT_RSA_ENCRYPTION_PKCS1', 2);
109
+ /**#@-*/
110
+
111
+ /**#@+
112
+ * @access public
113
+ * @see Crypt_RSA::sign()
114
+ * @see Crypt_RSA::verify()
115
+ * @see Crypt_RSA::setHash()
116
+ */
117
+ /**
118
+ * Use the Probabilistic Signature Scheme for signing
119
+ *
120
+ * Uses sha1 by default.
121
+ *
122
+ * @see Crypt_RSA::setSaltLength()
123
+ * @see Crypt_RSA::setMGFHash()
124
+ */
125
+ define('CRYPT_RSA_SIGNATURE_PSS', 1);
126
+ /**
127
+ * Use the PKCS#1 scheme by default.
128
+ *
129
+ * Although CRYPT_RSA_SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
130
+ * compatability with protocols (like SSH-2) written before PSS's introduction.
131
+ */
132
+ define('CRYPT_RSA_SIGNATURE_PKCS1', 2);
133
+ /**#@-*/
134
+
135
+ /**#@+
136
+ * @access private
137
+ * @see Crypt_RSA::createKey()
138
+ */
139
+ /**
140
+ * ASN1 Integer
141
+ */
142
+ define('CRYPT_RSA_ASN1_INTEGER', 2);
143
+ /**
144
+ * ASN1 Bit String
145
+ */
146
+ define('CRYPT_RSA_ASN1_BITSTRING', 3);
147
+ /**
148
+ * ASN1 Sequence (with the constucted bit set)
149
+ */
150
+ define('CRYPT_RSA_ASN1_SEQUENCE', 48);
151
+ /**#@-*/
152
+
153
+ /**#@+
154
+ * @access private
155
+ * @see Crypt_RSA::Crypt_RSA()
156
+ */
157
+ /**
158
+ * To use the pure-PHP implementation
159
+ */
160
+ define('CRYPT_RSA_MODE_INTERNAL', 1);
161
+ /**
162
+ * To use the OpenSSL library
163
+ *
164
+ * (if enabled; otherwise, the internal implementation will be used)
165
+ */
166
+ define('CRYPT_RSA_MODE_OPENSSL', 2);
167
+ /**#@-*/
168
+
169
+ /**#@+
170
+ * @access public
171
+ * @see Crypt_RSA::createKey()
172
+ * @see Crypt_RSA::setPrivateKeyFormat()
173
+ */
174
+ /**
175
+ * PKCS#1 formatted private key
176
+ *
177
+ * Used by OpenSSH
178
+ */
179
+ define('CRYPT_RSA_PRIVATE_FORMAT_PKCS1', 0);
180
+ /**
181
+ * PuTTY formatted private key
182
+ */
183
+ define('CRYPT_RSA_PRIVATE_FORMAT_PUTTY', 1);
184
+ /**
185
+ * XML formatted private key
186
+ */
187
+ define('CRYPT_RSA_PRIVATE_FORMAT_XML', 2);
188
+ /**#@-*/
189
+
190
+ /**#@+
191
+ * @access public
192
+ * @see Crypt_RSA::createKey()
193
+ * @see Crypt_RSA::setPublicKeyFormat()
194
+ */
195
+ /**
196
+ * Raw public key
197
+ *
198
+ * An array containing two Math_BigInteger objects.
199
+ *
200
+ * The exponent can be indexed with any of the following:
201
+ *
202
+ * 0, e, exponent, publicExponent
203
+ *
204
+ * The modulus can be indexed with any of the following:
205
+ *
206
+ * 1, n, modulo, modulus
207
+ */
208
+ define('CRYPT_RSA_PUBLIC_FORMAT_RAW', 3);
209
+ /**
210
+ * PKCS#1 formatted public key
211
+ */
212
+ define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1', 4);
213
+ /**
214
+ * XML formatted public key
215
+ */
216
+ define('CRYPT_RSA_PUBLIC_FORMAT_XML', 5);
217
+ /**
218
+ * OpenSSH formatted public key
219
+ *
220
+ * Place in $HOME/.ssh/authorized_keys
221
+ */
222
+ define('CRYPT_RSA_PUBLIC_FORMAT_OPENSSH', 6);
223
+ /**#@-*/
224
+
225
+ /**
226
+ * Pure-PHP PKCS#1 compliant implementation of RSA.
227
+ *
228
+ * @author Jim Wigginton <terrafrost@php.net>
229
+ * @version 0.1.0
230
+ * @access public
231
+ * @package Crypt_RSA
232
+ */
233
+ class Crypt_RSA {
234
+ /**
235
+ * Precomputed Zero
236
+ *
237
+ * @var Array
238
+ * @access private
239
+ */
240
+ var $zero;
241
+
242
+ /**
243
+ * Precomputed One
244
+ *
245
+ * @var Array
246
+ * @access private
247
+ */
248
+ var $one;
249
+
250
+ /**
251
+ * Private Key Format
252
+ *
253
+ * @var Integer
254
+ * @access private
255
+ */
256
+ var $privateKeyFormat = CRYPT_RSA_PRIVATE_FORMAT_PKCS1;
257
+
258
+ /**
259
+ * Public Key Format
260
+ *
261
+ * @var Integer
262
+ * @access public
263
+ */
264
+ var $publicKeyFormat = CRYPT_RSA_PUBLIC_FORMAT_PKCS1;
265
+
266
+ /**
267
+ * Modulus (ie. n)
268
+ *
269
+ * @var Math_BigInteger
270
+ * @access private
271
+ */
272
+ var $modulus;
273
+
274
+ /**
275
+ * Modulus length
276
+ *
277
+ * @var Math_BigInteger
278
+ * @access private
279
+ */
280
+ var $k;
281
+
282
+ /**
283
+ * Exponent (ie. e or d)
284
+ *
285
+ * @var Math_BigInteger
286
+ * @access private
287
+ */
288
+ var $exponent;
289
+
290
+ /**
291
+ * Primes for Chinese Remainder Theorem (ie. p and q)
292
+ *
293
+ * @var Array
294
+ * @access private
295
+ */
296
+ var $primes;
297
+
298
+ /**
299
+ * Exponents for Chinese Remainder Theorem (ie. dP and dQ)
300
+ *
301
+ * @var Array
302
+ * @access private
303
+ */
304
+ var $exponents;
305
+
306
+ /**
307
+ * Coefficients for Chinese Remainder Theorem (ie. qInv)
308
+ *
309
+ * @var Array
310
+ * @access private
311
+ */
312
+ var $coefficients;
313
+
314
+ /**
315
+ * Hash name
316
+ *
317
+ * @var String
318
+ * @access private
319
+ */
320
+ var $hashName;
321
+
322
+ /**
323
+ * Hash function
324
+ *
325
+ * @var Crypt_Hash
326
+ * @access private
327
+ */
328
+ var $hash;
329
+
330
+ /**
331
+ * Length of hash function output
332
+ *
333
+ * @var Integer
334
+ * @access private
335
+ */
336
+ var $hLen;
337
+
338
+ /**
339
+ * Length of salt
340
+ *
341
+ * @var Integer
342
+ * @access private
343
+ */
344
+ var $sLen;
345
+
346
+ /**
347
+ * Hash function for the Mask Generation Function
348
+ *
349
+ * @var Crypt_Hash
350
+ * @access private
351
+ */
352
+ var $mgfHash;
353
+
354
+ /**
355
+ * Length of MGF hash function output
356
+ *
357
+ * @var Integer
358
+ * @access private
359
+ */
360
+ var $mgfHLen;
361
+
362
+ /**
363
+ * Encryption mode
364
+ *
365
+ * @var Integer
366
+ * @access private
367
+ */
368
+ var $encryptionMode = CRYPT_RSA_ENCRYPTION_OAEP;
369
+
370
+ /**
371
+ * Signature mode
372
+ *
373
+ * @var Integer
374
+ * @access private
375
+ */
376
+ var $signatureMode = CRYPT_RSA_SIGNATURE_PSS;
377
+
378
+ /**
379
+ * Public Exponent
380
+ *
381
+ * @var Mixed
382
+ * @access private
383
+ */
384
+ var $publicExponent = false;
385
+
386
+ /**
387
+ * Password
388
+ *
389
+ * @var String
390
+ * @access private
391
+ */
392
+ var $password = '';
393
+
394
+ /**
395
+ * Components
396
+ *
397
+ * For use with parsing XML formatted keys. PHP's XML Parser functions use utilized - instead of PHP's DOM functions -
398
+ * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't.
399
+ *
400
+ * @see Crypt_RSA::_start_element_handler()
401
+ * @var Array
402
+ * @access private
403
+ */
404
+ var $components = array();
405
+
406
+ /**
407
+ * Current String
408
+ *
409
+ * For use with parsing XML formatted keys.
410
+ *
411
+ * @see Crypt_RSA::_character_handler()
412
+ * @see Crypt_RSA::_stop_element_handler()
413
+ * @var Mixed
414
+ * @access private
415
+ */
416
+ var $current;
417
+
418
+ /**
419
+ * The constructor
420
+ *
421
+ * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself. The reason
422
+ * Crypt_RSA doesn't do it is because OpenSSL doesn't fail gracefully. openssl_pkey_new(), in particular, requires
423
+ * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late.
424
+ *
425
+ * @return Crypt_RSA
426
+ * @access public
427
+ */
428
+ function Crypt_RSA()
429
+ {
430
+ if ( !defined('CRYPT_RSA_MODE') ) {
431
+ switch (true) {
432
+ //case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>='):
433
+ // define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL);
434
+ // break;
435
+ default:
436
+ define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
437
+ }
438
+ }
439
+
440
+ if (!defined('CRYPT_RSA_COMMENT')) {
441
+ define('CRYPT_RSA_COMMENT', 'phpseclib-generated-key');
442
+ }
443
+
444
+ $this->zero = new Math_BigInteger();
445
+ $this->one = new Math_BigInteger(1);
446
+
447
+ $this->hash = new Crypt_Hash('sha1');
448
+ $this->hLen = $this->hash->getLength();
449
+ $this->hashName = 'sha1';
450
+ $this->mgfHash = new Crypt_Hash('sha1');
451
+ $this->mgfHLen = $this->mgfHash->getLength();
452
+ }
453
+
454
+ /**
455
+ * Create public / private key pair
456
+ *
457
+ * Returns an array with the following three elements:
458
+ * - 'privatekey': The private key.
459
+ * - 'publickey': The public key.
460
+ * - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
461
+ * Will need to be passed back to Crypt_RSA::createKey() as the third parameter for further processing.
462
+ *
463
+ * @access public
464
+ * @param optional Integer $bits
465
+ * @param optional Integer $timeout
466
+ * @param optional Math_BigInteger $p
467
+ */
468
+ function createKey($bits = 1024, $timeout = false, $partial = array())
469
+ {
470
+ if ( CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL ) {
471
+ $rsa = openssl_pkey_new(array('private_key_bits' => $bits));
472
+ openssl_pkey_export($rsa, $privatekey);
473
+ $publickey = openssl_pkey_get_details($rsa);
474
+ $publickey = $publickey['key'];
475
+
476
+ if ($this->privateKeyFormat != CRYPT_RSA_PRIVATE_FORMAT_PKCS1) {
477
+ $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, CRYPT_RSA_PRIVATE_FORMAT_PKCS1)));
478
+ $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, CRYPT_RSA_PUBLIC_FORMAT_PKCS1)));
479
+ }
480
+
481
+ return array(
482
+ 'privatekey' => $privatekey,
483
+ 'publickey' => $publickey,
484
+ 'partialkey' => false
485
+ );
486
+ }
487
+
488
+ static $e;
489
+ if (!isset($e)) {
490
+ if (!defined('CRYPT_RSA_EXPONENT')) {
491
+ // http://en.wikipedia.org/wiki/65537_%28number%29
492
+ define('CRYPT_RSA_EXPONENT', '65537');
493
+ }
494
+ // per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
495
+ // than 256 bits.
496
+ if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
497
+ define('CRYPT_RSA_SMALLEST_PRIME', 4096);
498
+ }
499
+
500
+ $e = new Math_BigInteger(CRYPT_RSA_EXPONENT);
501
+ }
502
+
503
+ extract($this->_generateMinMax($bits));
504
+ $absoluteMin = $min;
505
+ $temp = $bits >> 1;
506
+ if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
507
+ $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
508
+ $temp = CRYPT_RSA_SMALLEST_PRIME;
509
+ } else {
510
+ $num_primes = 2;
511
+ }
512
+ extract($this->_generateMinMax($temp + $bits % $temp));
513
+ $finalMax = $max;
514
+ extract($this->_generateMinMax($temp));
515
+
516
+ $generator = new Math_BigInteger();
517
+ $generator->setRandomGenerator('crypt_random');
518
+
519
+ $n = $this->one->copy();
520
+ if (!empty($partial)) {
521
+ extract(unserialize($partial));
522
+ } else {
523
+ $exponents = $coefficients = $primes = array();
524
+ $lcm = array(
525
+ 'top' => $this->one->copy(),
526
+ 'bottom' => false
527
+ );
528
+ }
529
+
530
+ $start = time();
531
+ $i0 = count($primes) + 1;
532
+
533
+ do {
534
+ for ($i = $i0; $i <= $num_primes; $i++) {
535
+ if ($timeout !== false) {
536
+ $timeout-= time() - $start;
537
+ $start = time();
538
+ if ($timeout <= 0) {
539
+ return array(
540
+ 'privatekey' => '',
541
+ 'publickey' => '',
542
+ 'partialkey' => serialize(array(
543
+ 'primes' => $primes,
544
+ 'coefficients' => $coefficients,
545
+ 'lcm' => $lcm,
546
+ 'exponents' => $exponents
547
+ ))
548
+ );
549
+ }
550
+ }
551
+
552
+ if ($i == $num_primes) {
553
+ list($min, $temp) = $absoluteMin->divide($n);
554
+ if (!$temp->equals($this->zero)) {
555
+ $min = $min->add($this->one); // ie. ceil()
556
+ }
557
+ $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
558
+ } else {
559
+ $primes[$i] = $generator->randomPrime($min, $max, $timeout);
560
+ }
561
+
562
+ if ($primes[$i] === false) { // if we've reached the timeout
563
+ if (count($primes) > 1) {
564
+ $partialkey = '';
565
+ } else {
566
+ array_pop($primes);
567
+ $partialkey = serialize(array(
568
+ 'primes' => $primes,
569
+ 'coefficients' => $coefficients,
570
+ 'lcm' => $lcm,
571
+ 'exponents' => $exponents
572
+ ));
573
+ }
574
+
575
+ return array(
576
+ 'privatekey' => '',
577
+ 'publickey' => '',
578
+ 'partialkey' => $partialkey
579
+ );
580
+ }
581
+
582
+ // the first coefficient is calculated differently from the rest
583
+ // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
584
+ if ($i > 2) {
585
+ $coefficients[$i] = $n->modInverse($primes[$i]);
586
+ }
587
+
588
+ $n = $n->multiply($primes[$i]);
589
+
590
+ $temp = $primes[$i]->subtract($this->one);
591
+
592
+ // textbook RSA implementations use Euler's totient function instead of the least common multiple.
593
+ // see http://en.wikipedia.org/wiki/Euler%27s_totient_function
594
+ $lcm['top'] = $lcm['top']->multiply($temp);
595
+ $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
596
+
597
+ $exponents[$i] = $e->modInverse($temp);
598
+ }
599
+
600
+ list($lcm) = $lcm['top']->divide($lcm['bottom']);
601
+ $gcd = $lcm->gcd($e);
602
+ $i0 = 1;
603
+ } while (!$gcd->equals($this->one));
604
+
605
+ $d = $e->modInverse($lcm);
606
+
607
+ $coefficients[2] = $primes[2]->modInverse($primes[1]);
608
+
609
+ // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
610
+ // RSAPrivateKey ::= SEQUENCE {
611
+ // version Version,
612
+ // modulus INTEGER, -- n
613
+ // publicExponent INTEGER, -- e
614
+ // privateExponent INTEGER, -- d
615
+ // prime1 INTEGER, -- p
616
+ // prime2 INTEGER, -- q
617
+ // exponent1 INTEGER, -- d mod (p-1)
618
+ // exponent2 INTEGER, -- d mod (q-1)
619
+ // coefficient INTEGER, -- (inverse of q) mod p
620
+ // otherPrimeInfos OtherPrimeInfos OPTIONAL
621
+ // }
622
+
623
+ return array(
624
+ 'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
625
+ 'publickey' => $this->_convertPublicKey($n, $e),
626
+ 'partialkey' => false
627
+ );
628
+ }
629
+
630
+ /**
631
+ * Convert a private key to the appropriate format.
632
+ *
633
+ * @access private
634
+ * @see setPrivateKeyFormat()
635
+ * @param String $RSAPrivateKey
636
+ * @return String
637
+ */
638
+ function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
639
+ {
640
+ $num_primes = count($primes);
641
+ $raw = array(
642
+ 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
643
+ 'modulus' => $n->toBytes(true),
644
+ 'publicExponent' => $e->toBytes(true),
645
+ 'privateExponent' => $d->toBytes(true),
646
+ 'prime1' => $primes[1]->toBytes(true),
647
+ 'prime2' => $primes[2]->toBytes(true),
648
+ 'exponent1' => $exponents[1]->toBytes(true),
649
+ 'exponent2' => $exponents[2]->toBytes(true),
650
+ 'coefficient' => $coefficients[2]->toBytes(true)
651
+ );
652
+
653
+ // if the format in question does not support multi-prime rsa and multi-prime rsa was used,
654
+ // call _convertPublicKey() instead.
655
+ switch ($this->privateKeyFormat) {
656
+ case CRYPT_RSA_PRIVATE_FORMAT_XML:
657
+ if ($num_primes != 2) {
658
+ return false;
659
+ }
660
+ return "<RSAKeyValue>\r\n" .
661
+ ' <Modulus>' . base64_encode($raw['modulus']) . "</Modulus>\r\n" .
662
+ ' <Exponent>' . base64_encode($raw['publicExponent']) . "</Exponent>\r\n" .
663
+ ' <P>' . base64_encode($raw['prime1']) . "</P>\r\n" .
664
+ ' <Q>' . base64_encode($raw['prime2']) . "</Q>\r\n" .
665
+ ' <DP>' . base64_encode($raw['exponent1']) . "</DP>\r\n" .
666
+ ' <DQ>' . base64_encode($raw['exponent2']) . "</DQ>\r\n" .
667
+ ' <InverseQ>' . base64_encode($raw['coefficient']) . "</InverseQ>\r\n" .
668
+ ' <D>' . base64_encode($raw['privateExponent']) . "</D>\r\n" .
669
+ '</RSAKeyValue>';
670
+ break;
671
+ case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
672
+ if ($num_primes != 2) {
673
+ return false;
674
+ }
675
+ $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
676
+ $encryption = (!empty($this->password)) ? 'aes256-cbc' : 'none';
677
+ $key.= $encryption;
678
+ $key.= "\r\nComment: " . CRYPT_RSA_COMMENT . "\r\n";
679
+ $public = pack('Na*Na*Na*',
680
+ strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']
681
+ );
682
+ $source = pack('Na*Na*Na*Na*',
683
+ strlen('ssh-rsa'), 'ssh-rsa', strlen($encryption), $encryption,
684
+ strlen(CRYPT_RSA_COMMENT), CRYPT_RSA_COMMENT, strlen($public), $public
685
+ );
686
+ $public = base64_encode($public);
687
+ $key.= "Public-Lines: " . ((strlen($public) + 32) >> 6) . "\r\n";
688
+ $key.= chunk_split($public, 64);
689
+ $private = pack('Na*Na*Na*Na*',
690
+ strlen($raw['privateExponent']), $raw['privateExponent'], strlen($raw['prime1']), $raw['prime1'],
691
+ strlen($raw['prime2']), $raw['prime2'], strlen($raw['coefficient']), $raw['coefficient']
692
+ );
693
+ if (empty($this->password)) {
694
+ $source.= pack('Na*', strlen($private), $private);
695
+ $hashkey = 'putty-private-key-file-mac-key';
696
+ } else {
697
+ $private.= $this->_random(16 - (strlen($private) & 15));
698
+ $source.= pack('Na*', strlen($private), $private);
699
+ if (!class_exists('Crypt_AES')) {
700
+ require_once('Crypt/AES.php');
701
+ }
702
+ $sequence = 0;
703
+ $symkey = '';
704
+ while (strlen($symkey) < 32) {
705
+ $temp = pack('Na*', $sequence++, $this->password);
706
+ $symkey.= pack('H*', sha1($temp));
707
+ }
708
+ $symkey = substr($symkey, 0, 32);
709
+ $crypto = new Crypt_AES();
710
+
711
+ $crypto->setKey($symkey);
712
+ $crypto->disablePadding();
713
+ $private = $crypto->encrypt($private);
714
+ $hashkey = 'putty-private-key-file-mac-key' . $this->password;
715
+ }
716
+
717
+ $private = base64_encode($private);
718
+ $key.= 'Private-Lines: ' . ((strlen($private) + 32) >> 6) . "\r\n";
719
+ $key.= chunk_split($private, 64);
720
+ if (!class_exists('Crypt_Hash')) {
721
+ require_once('Crypt/Hash.php');
722
+ }
723
+ $hash = new Crypt_Hash('sha1');
724
+ $hash->setKey(pack('H*', sha1($hashkey)));
725
+ $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n";
726
+
727
+ return $key;
728
+ default: // eg. CRYPT_RSA_PRIVATE_FORMAT_PKCS1
729
+ $components = array();
730
+ foreach ($raw as $name => $value) {
731
+ $components[$name] = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
732
+ }
733
+
734
+ $RSAPrivateKey = implode('', $components);
735
+
736
+ if ($num_primes > 2) {
737
+ $OtherPrimeInfos = '';
738
+ for ($i = 3; $i <= $num_primes; $i++) {
739
+ // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
740
+ //
741
+ // OtherPrimeInfo ::= SEQUENCE {
742
+ // prime INTEGER, -- ri
743
+ // exponent INTEGER, -- di
744
+ // coefficient INTEGER -- ti
745
+ // }
746
+ $OtherPrimeInfo = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
747
+ $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
748
+ $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
749
+ $OtherPrimeInfos.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
750
+ }
751
+ $RSAPrivateKey.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
752
+ }
753
+
754
+ $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
755
+
756
+ if (!empty($this->password)) {
757
+ $iv = $this->_random(8);
758
+ $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
759
+ $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
760
+ if (!class_exists('Crypt_TripleDES')) {
761
+ require_once('Crypt/TripleDES.php');
762
+ }
763
+ $des = new Crypt_TripleDES();
764
+ $des->setKey($symkey);
765
+ $des->setIV($iv);
766
+ $iv = strtoupper(bin2hex($iv));
767
+ $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
768
+ "Proc-Type: 4,ENCRYPTED\r\n" .
769
+ "DEK-Info: DES-EDE3-CBC,$iv\r\n" .
770
+ "\r\n" .
771
+ chunk_split(base64_encode($des->encrypt($RSAPrivateKey))) .
772
+ '-----END RSA PRIVATE KEY-----';
773
+ } else {
774
+ $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
775
+ chunk_split(base64_encode($RSAPrivateKey)) .
776
+ '-----END RSA PRIVATE KEY-----';
777
+ }
778
+
779
+ return $RSAPrivateKey;
780
+ }
781
+ }
782
+
783
+ /**
784
+ * Convert a public key to the appropriate format
785
+ *
786
+ * @access private
787
+ * @see setPublicKeyFormat()
788
+ * @param String $RSAPrivateKey
789
+ * @return String
790
+ */
791
+ function _convertPublicKey($n, $e)
792
+ {
793
+ $modulus = $n->toBytes(true);
794
+ $publicExponent = $e->toBytes(true);
795
+
796
+ switch ($this->publicKeyFormat) {
797
+ case CRYPT_RSA_PUBLIC_FORMAT_RAW:
798
+ return array('e' => $e->copy(), 'n' => $n->copy());
799
+ case CRYPT_RSA_PUBLIC_FORMAT_XML:
800
+ return "<RSAKeyValue>\r\n" .
801
+ ' <Modulus>' . base64_encode($modulus) . "</Modulus>\r\n" .
802
+ ' <Exponent>' . base64_encode($publicExponent) . "</Exponent>\r\n" .
803
+ '</RSAKeyValue>';
804
+ break;
805
+ case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
806
+ // from <http://tools.ietf.org/html/rfc4253#page-15>:
807
+ // string "ssh-rsa"
808
+ // mpint e
809
+ // mpint n
810
+ $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
811
+ $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . CRYPT_RSA_COMMENT;
812
+
813
+ return $RSAPublicKey;
814
+ default: // eg. CRYPT_RSA_PUBLIC_FORMAT_PKCS1
815
+ // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
816
+ // RSAPublicKey ::= SEQUENCE {
817
+ // modulus INTEGER, -- n
818
+ // publicExponent INTEGER -- e
819
+ // }
820
+ $components = array(
821
+ 'modulus' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
822
+ 'publicExponent' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
823
+ );
824
+
825
+ $RSAPublicKey = pack('Ca*a*a*',
826
+ CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
827
+ $components['modulus'], $components['publicExponent']
828
+ );
829
+
830
+ $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
831
+ chunk_split(base64_encode($RSAPublicKey)) .
832
+ '-----END PUBLIC KEY-----';
833
+
834
+ return $RSAPublicKey;
835
+ }
836
+ }
837
+
838
+ /**
839
+ * Break a public or private key down into its constituant components
840
+ *
841
+ * @access private
842
+ * @see _convertPublicKey()
843
+ * @see _convertPrivateKey()
844
+ * @param String $key
845
+ * @param Integer $type
846
+ * @return Array
847
+ */
848
+ function _parseKey($key, $type)
849
+ {
850
+ if ($type != CRYPT_RSA_PUBLIC_FORMAT_RAW && !is_string($key)) {
851
+ return false;
852
+ }
853
+
854
+ switch ($type) {
855
+ case CRYPT_RSA_PUBLIC_FORMAT_RAW:
856
+ if (!is_array($key)) {
857
+ return false;
858
+ }
859
+ $components = array();
860
+ switch (true) {
861
+ case isset($key['e']):
862
+ $components['publicExponent'] = $key['e']->copy();
863
+ break;
864
+ case isset($key['exponent']):
865
+ $components['publicExponent'] = $key['exponent']->copy();
866
+ break;
867
+ case isset($key['publicExponent']):
868
+ $components['publicExponent'] = $key['publicExponent']->copy();
869
+ break;
870
+ case isset($key[0]):
871
+ $components['publicExponent'] = $key[0]->copy();
872
+ }
873
+ switch (true) {
874
+ case isset($key['n']):
875
+ $components['modulus'] = $key['n']->copy();
876
+ break;
877
+ case isset($key['modulo']):
878
+ $components['modulus'] = $key['modulo']->copy();
879
+ break;
880
+ case isset($key['modulus']):
881
+ $components['modulus'] = $key['modulus']->copy();
882
+ break;
883
+ case isset($key[1]):
884
+ $components['modulus'] = $key[1]->copy();
885
+ }
886
+ return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
887
+ case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
888
+ case CRYPT_RSA_PUBLIC_FORMAT_PKCS1:
889
+ /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
890
+ "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
891
+ protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding
892
+ two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here:
893
+
894
+ http://tools.ietf.org/html/rfc1421#section-4.6.1.1
895
+ http://tools.ietf.org/html/rfc1421#section-4.6.1.3
896
+
897
+ DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
898
+ DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
899
+ function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
900
+ own implementation. ie. the implementation *is* the standard and any bugs that may exist in that
901
+ implementation are part of the standard, as well.
902
+
903
+ * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */
904
+ if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
905
+ $iv = pack('H*', trim($matches[2]));
906
+ $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
907
+ $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
908
+ $ciphertext = preg_replace('#.+(\r|\n|\r\n)\1|[\r\n]|-.+-#s', '', $key);
909
+ $ciphertext = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $ciphertext) ? base64_decode($ciphertext) : false;
910
+ if ($ciphertext === false) {
911
+ $ciphertext = $key;
912
+ }
913
+ switch ($matches[1]) {
914
+ case 'AES-128-CBC':
915
+ if (!class_exists('Crypt_AES')) {
916
+ require_once('Crypt/AES.php');
917
+ }
918
+ $symkey = substr($symkey, 0, 16);
919
+ $crypto = new Crypt_AES();
920
+ break;
921
+ case 'DES-EDE3-CFB':
922
+ if (!class_exists('Crypt_TripleDES')) {
923
+ require_once('Crypt/TripleDES.php');
924
+ }
925
+ $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CFB);
926
+ break;
927
+ case 'DES-EDE3-CBC':
928
+ if (!class_exists('Crypt_TripleDES')) {
929
+ require_once('Crypt/TripleDES.php');
930
+ }
931
+ $crypto = new Crypt_TripleDES();
932
+ break;
933
+ case 'DES-CBC':
934
+ if (!class_exists('Crypt_DES')) {
935
+ require_once('Crypt/DES.php');
936
+ }
937
+ $crypto = new Crypt_DES();
938
+ break;
939
+ default:
940
+ return false;
941
+ }
942
+ $crypto->setKey($symkey);
943
+ $crypto->setIV($iv);
944
+ $decoded = $crypto->decrypt($ciphertext);
945
+ } else {
946
+ $decoded = preg_replace('#-.+-|[\r\n]#', '', $key);
947
+ $decoded = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $decoded) ? base64_decode($decoded) : false;
948
+ }
949
+
950
+ if ($decoded !== false) {
951
+ $key = $decoded;
952
+ }
953
+
954
+ $components = array();
955
+
956
+ if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
957
+ return false;
958
+ }
959
+ if ($this->_decodeLength($key) != strlen($key)) {
960
+ return false;
961
+ }
962
+
963
+ $tag = ord($this->_string_shift($key));
964
+ /* intended for keys for which OpenSSL's asn1parse returns the following:
965
+
966
+ 0:d=0 hl=4 l= 631 cons: SEQUENCE
967
+ 4:d=1 hl=2 l= 1 prim: INTEGER :00
968
+ 7:d=1 hl=2 l= 13 cons: SEQUENCE
969
+ 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
970
+ 20:d=2 hl=2 l= 0 prim: NULL
971
+ 22:d=1 hl=4 l= 609 prim: OCTET STRING */
972
+
973
+ if ($tag == CRYPT_RSA_ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
974
+ $this->_string_shift($key, 3);
975
+ $tag = CRYPT_RSA_ASN1_SEQUENCE;
976
+ }
977
+
978
+ if ($tag == CRYPT_RSA_ASN1_SEQUENCE) {
979
+ /* intended for keys for which OpenSSL's asn1parse returns the following:
980
+
981
+ 0:d=0 hl=4 l= 290 cons: SEQUENCE
982
+ 4:d=1 hl=2 l= 13 cons: SEQUENCE
983
+ 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
984
+ 17:d=2 hl=2 l= 0 prim: NULL
985
+ 19:d=1 hl=4 l= 271 prim: BIT STRING */
986
+ $this->_string_shift($key, $this->_decodeLength($key));
987
+ $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
988
+ $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
989
+ // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
990
+ // unused bits in the final subsequent octet. The number shall be in the range zero to seven."
991
+ // -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
992
+ if ($tag == CRYPT_RSA_ASN1_BITSTRING) {
993
+ $this->_string_shift($key);
994
+ }
995
+ if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
996
+ return false;
997
+ }
998
+ if ($this->_decodeLength($key) != strlen($key)) {
999
+ return false;
1000
+ }
1001
+ $tag = ord($this->_string_shift($key));
1002
+ }
1003
+ if ($tag != CRYPT_RSA_ASN1_INTEGER) {
1004
+ return false;
1005
+ }
1006
+
1007
+ $length = $this->_decodeLength($key);
1008
+ $temp = $this->_string_shift($key, $length);
1009
+ if (strlen($temp) != 1 || ord($temp) > 2) {
1010
+ $components['modulus'] = new Math_BigInteger($temp, -256);
1011
+ $this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER
1012
+ $length = $this->_decodeLength($key);
1013
+ $components[$type == CRYPT_RSA_PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
1014
+
1015
+ return $components;
1016
+ }
1017
+ if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_INTEGER) {
1018
+ return false;
1019
+ }
1020
+ $length = $this->_decodeLength($key);
1021
+ $components['modulus'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
1022
+ $this->_string_shift($key);
1023
+ $length = $this->_decodeLength($key);
1024
+ $components['publicExponent'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
1025
+ $this->_string_shift($key);
1026
+ $length = $this->_decodeLength($key);
1027
+ $components['privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
1028
+ $this->_string_shift($key);
1029
+ $length = $this->_decodeLength($key);
1030
+ $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), -256));
1031
+ $this->_string_shift($key);
1032
+ $length = $this->_decodeLength($key);
1033
+ $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
1034
+ $this->_string_shift($key);
1035
+ $length = $this->_decodeLength($key);
1036
+ $components['exponents'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), -256));
1037
+ $this->_string_shift($key);
1038
+ $length = $this->_decodeLength($key);
1039
+ $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
1040
+ $this->_string_shift($key);
1041
+ $length = $this->_decodeLength($key);
1042
+ $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($key, $length), -256));
1043
+
1044
+ if (!empty($key)) {
1045
+ if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
1046
+ return false;
1047
+ }
1048
+ $this->_decodeLength($key);
1049
+ while (!empty($key)) {
1050
+ if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
1051
+ return false;
1052
+ }
1053
+ $this->_decodeLength($key);
1054
+ $key = substr($key, 1);
1055
+ $length = $this->_decodeLength($key);
1056
+ $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
1057
+ $this->_string_shift($key);
1058
+ $length = $this->_decodeLength($key);
1059
+ $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
1060
+ $this->_string_shift($key);
1061
+ $length = $this->_decodeLength($key);
1062
+ $components['coefficients'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
1063
+ }
1064
+ }
1065
+
1066
+ return $components;
1067
+ case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
1068
+ $key = base64_decode(preg_replace('#^ssh-rsa | .+$#', '', $key));
1069
+ if ($key === false) {
1070
+ return false;
1071
+ }
1072
+
1073
+ $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
1074
+
1075
+ if (strlen($key) <= 4) {
1076
+ return false;
1077
+ }
1078
+ extract(unpack('Nlength', $this->_string_shift($key, 4)));
1079
+ $publicExponent = new Math_BigInteger($this->_string_shift($key, $length), -256);
1080
+ if (strlen($key) <= 4) {
1081
+ return false;
1082
+ }
1083
+ extract(unpack('Nlength', $this->_string_shift($key, 4)));
1084
+ $modulus = new Math_BigInteger($this->_string_shift($key, $length), -256);
1085
+
1086
+ if ($cleanup && strlen($key)) {
1087
+ if (strlen($key) <= 4) {
1088
+ return false;
1089
+ }
1090
+ extract(unpack('Nlength', $this->_string_shift($key, 4)));
1091
+ return strlen($key) ? false : array(
1092
+ 'modulus' => new Math_BigInteger($this->_string_shift($key, $length), -256),
1093
+ 'publicExponent' => $modulus
1094
+ );
1095
+ } else {
1096
+ return strlen($key) ? false : array(
1097
+ 'modulus' => $modulus,
1098
+ 'publicExponent' => $publicExponent
1099
+ );
1100
+ }
1101
+ // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
1102
+ // http://en.wikipedia.org/wiki/XML_Signature
1103
+ case CRYPT_RSA_PRIVATE_FORMAT_XML:
1104
+ case CRYPT_RSA_PUBLIC_FORMAT_XML:
1105
+ $this->components = array();
1106
+
1107
+ $xml = xml_parser_create('UTF-8');
1108
+ xml_set_object($xml, $this);
1109
+ xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler');
1110
+ xml_set_character_data_handler($xml, '_data_handler');
1111
+ if (!xml_parse($xml, $key)) {
1112
+ return false;
1113
+ }
1114
+
1115
+ return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
1116
+ // from PuTTY's SSHPUBK.C
1117
+ case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
1118
+ $components = array();
1119
+ $key = preg_split('#\r\n|\r|\n#', $key);
1120
+ $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
1121
+ if ($type != 'ssh-rsa') {
1122
+ return false;
1123
+ }
1124
+ $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
1125
+
1126
+ $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
1127
+ $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
1128
+ $public = substr($public, 11);
1129
+ extract(unpack('Nlength', $this->_string_shift($public, 4)));
1130
+ $components['publicExponent'] = new Math_BigInteger($this->_string_shift($public, $length), -256);
1131
+ extract(unpack('Nlength', $this->_string_shift($public, 4)));
1132
+ $components['modulus'] = new Math_BigInteger($this->_string_shift($public, $length), -256);
1133
+
1134
+ $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4]));
1135
+ $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength))));
1136
+
1137
+ switch ($encryption) {
1138
+ case 'aes256-cbc':
1139
+ if (!class_exists('Crypt_AES')) {
1140
+ require_once('Crypt/AES.php');
1141
+ }
1142
+ $symkey = '';
1143
+ $sequence = 0;
1144
+ while (strlen($symkey) < 32) {
1145
+ $temp = pack('Na*', $sequence++, $this->password);
1146
+ $symkey.= pack('H*', sha1($temp));
1147
+ }
1148
+ $symkey = substr($symkey, 0, 32);
1149
+ $crypto = new Crypt_AES();
1150
+ }
1151
+
1152
+ if ($encryption != 'none') {
1153
+ $crypto->setKey($symkey);
1154
+ $crypto->disablePadding();
1155
+ $private = $crypto->decrypt($private);
1156
+ if ($private === false) {
1157
+ return false;
1158
+ }
1159
+ }
1160
+
1161
+ extract(unpack('Nlength', $this->_string_shift($private, 4)));
1162
+ if (strlen($private) < $length) {
1163
+ return false;
1164
+ }
1165
+ $components['privateExponent'] = new Math_BigInteger($this->_string_shift($private, $length), -256);
1166
+ extract(unpack('Nlength', $this->_string_shift($private, 4)));
1167
+ if (strlen($private) < $length) {
1168
+ return false;
1169
+ }
1170
+ $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($private, $length), -256));
1171
+ extract(unpack('Nlength', $this->_string_shift($private, 4)));
1172
+ if (strlen($private) < $length) {
1173
+ return false;
1174
+ }
1175
+ $components['primes'][] = new Math_BigInteger($this->_string_shift($private, $length), -256);
1176
+
1177
+ $temp = $components['primes'][1]->subtract($this->one);
1178
+ $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
1179
+ $temp = $components['primes'][2]->subtract($this->one);
1180
+ $components['exponents'][] = $components['publicExponent']->modInverse($temp);
1181
+
1182
+ extract(unpack('Nlength', $this->_string_shift($private, 4)));
1183
+ if (strlen($private) < $length) {
1184
+ return false;
1185
+ }
1186
+ $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($private, $length), -256));
1187
+
1188
+ return $components;
1189
+ }
1190
+ }
1191
+
1192
+ /**
1193
+ * Start Element Handler
1194
+ *
1195
+ * Called by xml_set_element_handler()
1196
+ *
1197
+ * @access private
1198
+ * @param Resource $parser
1199
+ * @param String $name
1200
+ * @param Array $attribs
1201
+ */
1202
+ function _start_element_handler($parser, $name, $attribs)
1203
+ {
1204
+ //$name = strtoupper($name);
1205
+ switch ($name) {
1206
+ case 'MODULUS':
1207
+ $this->current = &$this->components['modulus'];
1208
+ break;
1209
+ case 'EXPONENT':
1210
+ $this->current = &$this->components['publicExponent'];
1211
+ break;
1212
+ case 'P':
1213
+ $this->current = &$this->components['primes'][1];
1214
+ break;
1215
+ case 'Q':
1216
+ $this->current = &$this->components['primes'][2];
1217
+ break;
1218
+ case 'DP':
1219
+ $this->current = &$this->components['exponents'][1];
1220
+ break;
1221
+ case 'DQ':
1222
+ $this->current = &$this->components['exponents'][2];
1223
+ break;
1224
+ case 'INVERSEQ':
1225
+ $this->current = &$this->components['coefficients'][2];
1226
+ break;
1227
+ case 'D':
1228
+ $this->current = &$this->components['privateExponent'];
1229
+ break;
1230
+ default:
1231
+ unset($this->current);
1232
+ }
1233
+ $this->current = '';
1234
+ }
1235
+
1236
+ /**
1237
+ * Stop Element Handler
1238
+ *
1239
+ * Called by xml_set_element_handler()
1240
+ *
1241
+ * @access private
1242
+ * @param Resource $parser
1243
+ * @param String $name
1244
+ */
1245
+ function _stop_element_handler($parser, $name)
1246
+ {
1247
+ //$name = strtoupper($name);
1248
+ if ($name == 'RSAKEYVALUE') {
1249
+ return;
1250
+ }
1251
+ $this->current = new Math_BigInteger(base64_decode($this->current), 256);
1252
+ }
1253
+
1254
+ /**
1255
+ * Data Handler
1256
+ *
1257
+ * Called by xml_set_character_data_handler()
1258
+ *
1259
+ * @access private
1260
+ * @param Resource $parser
1261
+ * @param String $data
1262
+ */
1263
+ function _data_handler($parser, $data)
1264
+ {
1265
+ if (!isset($this->current) || is_object($this->current)) {
1266
+ return;
1267
+ }
1268
+ $this->current.= trim($data);
1269
+ }
1270
+
1271
+ /**
1272
+ * Loads a public or private key
1273
+ *
1274
+ * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
1275
+ *
1276
+ * @access public
1277
+ * @param String $key
1278
+ * @param Integer $type optional
1279
+ */
1280
+ function loadKey($key, $type = false)
1281
+ {
1282
+ if ($type === false) {
1283
+ $types = array(
1284
+ CRYPT_RSA_PUBLIC_FORMAT_RAW,
1285
+ CRYPT_RSA_PRIVATE_FORMAT_PKCS1,
1286
+ CRYPT_RSA_PRIVATE_FORMAT_XML,
1287
+ CRYPT_RSA_PRIVATE_FORMAT_PUTTY,
1288
+ CRYPT_RSA_PUBLIC_FORMAT_OPENSSH
1289
+ );
1290
+ foreach ($types as $type) {
1291
+ $components = $this->_parseKey($key, $type);
1292
+ if ($components !== false) {
1293
+ break;
1294
+ }
1295
+ }
1296
+
1297
+ } else {
1298
+ $components = $this->_parseKey($key, $type);
1299
+ }
1300
+
1301
+ if ($components === false) {
1302
+ return false;
1303
+ }
1304
+
1305
+ $this->modulus = $components['modulus'];
1306
+ $this->k = strlen($this->modulus->toBytes());
1307
+ $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
1308
+ if (isset($components['primes'])) {
1309
+ $this->primes = $components['primes'];
1310
+ $this->exponents = $components['exponents'];
1311
+ $this->coefficients = $components['coefficients'];
1312
+ $this->publicExponent = $components['publicExponent'];
1313
+ } else {
1314
+ $this->primes = array();
1315
+ $this->exponents = array();
1316
+ $this->coefficients = array();
1317
+ $this->publicExponent = false;
1318
+ }
1319
+
1320
+ return true;
1321
+ }
1322
+
1323
+ /**
1324
+ * Sets the password
1325
+ *
1326
+ * Private keys can be encrypted with a password. To unset the password, pass in the empty string or false.
1327
+ * Or rather, pass in $password such that empty($password) is true.
1328
+ *
1329
+ * @see createKey()
1330
+ * @see loadKey()
1331
+ * @access public
1332
+ * @param String $password
1333
+ */
1334
+ function setPassword($password)
1335
+ {
1336
+ $this->password = $password;
1337
+ }
1338
+
1339
+ /**
1340
+ * Defines the public key
1341
+ *
1342
+ * Some private key formats define the public exponent and some don't. Those that don't define it are problematic when
1343
+ * used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a
1344
+ * message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys
1345
+ * and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public
1346
+ * exponent this won't work unless you manually add the public exponent.
1347
+ *
1348
+ * Do note that when a new key is loaded the index will be cleared.
1349
+ *
1350
+ * Returns true on success, false on failure
1351
+ *
1352
+ * @see getPublicKey()
1353
+ * @access public
1354
+ * @param String $key optional
1355
+ * @param Integer $type optional
1356
+ * @return Boolean
1357
+ */
1358
+ function setPublicKey($key = false, $type = false)
1359
+ {
1360
+ if ($key === false && !empty($this->modulus)) {
1361
+ $this->publicExponent = $this->exponent;
1362
+ return true;
1363
+ }
1364
+
1365
+ if ($type === false) {
1366
+ $types = array(
1367
+ CRYPT_RSA_PUBLIC_FORMAT_RAW,
1368
+ CRYPT_RSA_PUBLIC_FORMAT_PKCS1,
1369
+ CRYPT_RSA_PUBLIC_FORMAT_XML,
1370
+ CRYPT_RSA_PUBLIC_FORMAT_OPENSSH
1371
+ );
1372
+ foreach ($types as $type) {
1373
+ $components = $this->_parseKey($key, $type);
1374
+ if ($components !== false) {
1375
+ break;
1376
+ }
1377
+ }
1378
+ } else {
1379
+ $components = $this->_parseKey($key, $type);
1380
+ }
1381
+
1382
+ if ($components === false) {
1383
+ return false;
1384
+ }
1385
+
1386
+ if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
1387
+ $this->modulus = $components['modulus'];
1388
+ $this->exponent = $this->publicExponent = $components['publicExponent'];
1389
+ return true;
1390
+ }
1391
+
1392
+ $this->publicExponent = $components['publicExponent'];
1393
+
1394
+ return true;
1395
+ }
1396
+
1397
+ /**
1398
+ * Returns the public key
1399
+ *
1400
+ * The public key is only returned under two circumstances - if the private key had the public key embedded within it
1401
+ * or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this
1402
+ * function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
1403
+ *
1404
+ * @see getPublicKey()
1405
+ * @access public
1406
+ * @param String $key
1407
+ * @param Integer $type optional
1408
+ */
1409
+ function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
1410
+ {
1411
+ if (empty($this->modulus) || empty($this->publicExponent)) {
1412
+ return false;
1413
+ }
1414
+
1415
+ $oldFormat = $this->publicKeyFormat;
1416
+ $this->publicKeyFormat = $type;
1417
+ $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent);
1418
+ $this->publicKeyFormat = $oldFormat;
1419
+ return $temp;
1420
+ }
1421
+
1422
+ /**
1423
+ * Returns the private key
1424
+ *
1425
+ * The private key is only returned if the currently loaded key contains the constituent prime numbers.
1426
+ *
1427
+ * @see getPublicKey()
1428
+ * @access public
1429
+ * @param String $key
1430
+ * @param Integer $type optional
1431
+ */
1432
+ function getPrivateKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
1433
+ {
1434
+ if (empty($this->primes)) {
1435
+ return false;
1436
+ }
1437
+
1438
+ $oldFormat = $this->privateKeyFormat;
1439
+ $this->privateKeyFormat = $type;
1440
+ $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients);
1441
+ $this->privateKeyFormat = $oldFormat;
1442
+ return $temp;
1443
+ }
1444
+
1445
+ /**
1446
+ * Generates the smallest and largest numbers requiring $bits bits
1447
+ *
1448
+ * @access private
1449
+ * @param Integer $bits
1450
+ * @return Array
1451
+ */
1452
+ function _generateMinMax($bits)
1453
+ {
1454
+ $bytes = $bits >> 3;
1455
+ $min = str_repeat(chr(0), $bytes);
1456
+ $max = str_repeat(chr(0xFF), $bytes);
1457
+ $msb = $bits & 7;
1458
+ if ($msb) {
1459
+ $min = chr(1 << ($msb - 1)) . $min;
1460
+ $max = chr((1 << $msb) - 1) . $max;
1461
+ } else {
1462
+ $min[0] = chr(0x80);
1463
+ }
1464
+
1465
+ return array(
1466
+ 'min' => new Math_BigInteger($min, 256),
1467
+ 'max' => new Math_BigInteger($max, 256)
1468
+ );
1469
+ }
1470
+
1471
+ /**
1472
+ * DER-decode the length
1473
+ *
1474
+ * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
1475
+ * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 � 8.1.3} for more information.
1476
+ *
1477
+ * @access private
1478
+ * @param String $string
1479
+ * @return Integer
1480
+ */
1481
+ function _decodeLength(&$string)
1482
+ {
1483
+ $length = ord($this->_string_shift($string));
1484
+ if ( $length & 0x80 ) { // definite length, long form
1485
+ $length&= 0x7F;
1486
+ $temp = $this->_string_shift($string, $length);
1487
+ list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
1488
+ }
1489
+ return $length;
1490
+ }
1491
+
1492
+ /**
1493
+ * DER-encode the length
1494
+ *
1495
+ * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
1496
+ * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 � 8.1.3} for more information.
1497
+ *
1498
+ * @access private
1499
+ * @param Integer $length
1500
+ * @return String
1501
+ */
1502
+ function _encodeLength($length)
1503
+ {
1504
+ if ($length <= 0x7F) {
1505
+ return chr($length);
1506
+ }
1507
+
1508
+ $temp = ltrim(pack('N', $length), chr(0));
1509
+ return pack('Ca*', 0x80 | strlen($temp), $temp);
1510
+ }
1511
+
1512
+ /**
1513
+ * String Shift
1514
+ *
1515
+ * Inspired by array_shift
1516
+ *
1517
+ * @param String $string
1518
+ * @param optional Integer $index
1519
+ * @return String
1520
+ * @access private
1521
+ */
1522
+ function _string_shift(&$string, $index = 1)
1523
+ {
1524
+ $substr = substr($string, 0, $index);
1525
+ $string = substr($string, $index);
1526
+ return $substr;
1527
+ }
1528
+
1529
+ /**
1530
+ * Determines the private key format
1531
+ *
1532
+ * @see createKey()
1533
+ * @access public
1534
+ * @param Integer $format
1535
+ */
1536
+ function setPrivateKeyFormat($format)
1537
+ {
1538
+ $this->privateKeyFormat = $format;
1539
+ }
1540
+
1541
+ /**
1542
+ * Determines the public key format
1543
+ *
1544
+ * @see createKey()
1545
+ * @access public
1546
+ * @param Integer $format
1547
+ */
1548
+ function setPublicKeyFormat($format)
1549
+ {
1550
+ $this->publicKeyFormat = $format;
1551
+ }
1552
+
1553
+ /**
1554
+ * Determines which hashing function should be used
1555
+ *
1556
+ * Used with signature production / verification and (if the encryption mode is CRYPT_RSA_ENCRYPTION_OAEP) encryption and
1557
+ * decryption. If $hash isn't supported, sha1 is used.
1558
+ *
1559
+ * @access public
1560
+ * @param String $hash
1561
+ */
1562
+ function setHash($hash)
1563
+ {
1564
+ // Crypt_Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
1565
+ switch ($hash) {
1566
+ case 'md2':
1567
+ case 'md5':
1568
+ case 'sha1':
1569
+ case 'sha256':
1570
+ case 'sha384':
1571
+ case 'sha512':
1572
+ $this->hash = new Crypt_Hash($hash);
1573
+ $this->hashName = $hash;
1574
+ break;
1575
+ default:
1576
+ $this->hash = new Crypt_Hash('sha1');
1577
+ $this->hashName = 'sha1';
1578
+ }
1579
+ $this->hLen = $this->hash->getLength();
1580
+ }
1581
+
1582
+ /**
1583
+ * Determines which hashing function should be used for the mask generation function
1584
+ *
1585
+ * The mask generation function is used by CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_SIGNATURE_PSS and although it's
1586
+ * best if Hash and MGFHash are set to the same thing this is not a requirement.
1587
+ *
1588
+ * @access public
1589
+ * @param String $hash
1590
+ */
1591
+ function setMGFHash($hash)
1592
+ {
1593
+ // Crypt_Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
1594
+ switch ($hash) {
1595
+ case 'md2':
1596
+ case 'md5':
1597
+ case 'sha1':
1598
+ case 'sha256':
1599
+ case 'sha384':
1600
+ case 'sha512':
1601
+ $this->mgfHash = new Crypt_Hash($hash);
1602
+ break;
1603
+ default:
1604
+ $this->mgfHash = new Crypt_Hash('sha1');
1605
+ }
1606
+ $this->mgfHLen = $this->mgfHash->getLength();
1607
+ }
1608
+
1609
+ /**
1610
+ * Determines the salt length
1611
+ *
1612
+ * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
1613
+ *
1614
+ * Typical salt lengths in octets are hLen (the length of the output
1615
+ * of the hash function Hash) and 0.
1616
+ *
1617
+ * @access public
1618
+ * @param Integer $format
1619
+ */
1620
+ function setSaltLength($sLen)
1621
+ {
1622
+ $this->sLen = $sLen;
1623
+ }
1624
+
1625
+ /**
1626
+ * Generates a random string x bytes long
1627
+ *
1628
+ * @access public
1629
+ * @param Integer $bytes
1630
+ * @param optional Integer $nonzero
1631
+ * @return String
1632
+ */
1633
+ function _random($bytes, $nonzero = false)
1634
+ {
1635
+ $temp = '';
1636
+ for ($i = 0; $i < $bytes; $i++) {
1637
+ $temp.= chr(crypt_random($nonzero, 255));
1638
+ }
1639
+ return $temp;
1640
+ }
1641
+
1642
+ /**
1643
+ * Integer-to-Octet-String primitive
1644
+ *
1645
+ * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
1646
+ *
1647
+ * @access private
1648
+ * @param Math_BigInteger $x
1649
+ * @param Integer $xLen
1650
+ * @return String
1651
+ */
1652
+ function _i2osp($x, $xLen)
1653
+ {
1654
+ $x = $x->toBytes();
1655
+ if (strlen($x) > $xLen) {
1656
+ user_error('Integer too large', E_USER_NOTICE);
1657
+ return false;
1658
+ }
1659
+ return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
1660
+ }
1661
+
1662
+ /**
1663
+ * Octet-String-to-Integer primitive
1664
+ *
1665
+ * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
1666
+ *
1667
+ * @access private
1668
+ * @param String $x
1669
+ * @return Math_BigInteger
1670
+ */
1671
+ function _os2ip($x)
1672
+ {
1673
+ return new Math_BigInteger($x, 256);
1674
+ }
1675
+
1676
+ /**
1677
+ * Exponentiate with or without Chinese Remainder Theorem
1678
+ *
1679
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}.
1680
+ *
1681
+ * @access private
1682
+ * @param Math_BigInteger $x
1683
+ * @return Math_BigInteger
1684
+ */
1685
+ function _exponentiate($x)
1686
+ {
1687
+ if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) {
1688
+ return $x->modPow($this->exponent, $this->modulus);
1689
+ }
1690
+
1691
+ $num_primes = count($this->primes);
1692
+
1693
+ if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
1694
+ $m_i = array(
1695
+ 1 => $x->modPow($this->exponents[1], $this->primes[1]),
1696
+ 2 => $x->modPow($this->exponents[2], $this->primes[2])
1697
+ );
1698
+ $h = $m_i[1]->subtract($m_i[2]);
1699
+ $h = $h->multiply($this->coefficients[2]);
1700
+ list(, $h) = $h->divide($this->primes[1]);
1701
+ $m = $m_i[2]->add($h->multiply($this->primes[2]));
1702
+
1703
+ $r = $this->primes[1];
1704
+ for ($i = 3; $i <= $num_primes; $i++) {
1705
+ $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
1706
+
1707
+ $r = $r->multiply($this->primes[$i - 1]);
1708
+
1709
+ $h = $m_i->subtract($m);
1710
+ $h = $h->multiply($this->coefficients[$i]);
1711
+ list(, $h) = $h->divide($this->primes[$i]);
1712
+
1713
+ $m = $m->add($r->multiply($h));
1714
+ }
1715
+ } else {
1716
+ $smallest = $this->primes[1];
1717
+ for ($i = 2; $i <= $num_primes; $i++) {
1718
+ if ($smallest->compare($this->primes[$i]) > 0) {
1719
+ $smallest = $this->primes[$i];
1720
+ }
1721
+ }
1722
+
1723
+ $one = new Math_BigInteger(1);
1724
+ $one->setRandomGenerator('crypt_random');
1725
+
1726
+ $r = $one->random($one, $smallest->subtract($one));
1727
+
1728
+ $m_i = array(
1729
+ 1 => $this->_blind($x, $r, 1),
1730
+ 2 => $this->_blind($x, $r, 2)
1731
+ );
1732
+ $h = $m_i[1]->subtract($m_i[2]);
1733
+ $h = $h->multiply($this->coefficients[2]);
1734
+ list(, $h) = $h->divide($this->primes[1]);
1735
+ $m = $m_i[2]->add($h->multiply($this->primes[2]));
1736
+
1737
+ $r = $this->primes[1];
1738
+ for ($i = 3; $i <= $num_primes; $i++) {
1739
+ $m_i = $this->_blind($x, $r, $i);
1740
+
1741
+ $r = $r->multiply($this->primes[$i - 1]);
1742
+
1743
+ $h = $m_i->subtract($m);
1744
+ $h = $h->multiply($this->coefficients[$i]);
1745
+ list(, $h) = $h->divide($this->primes[$i]);
1746
+
1747
+ $m = $m->add($r->multiply($h));
1748
+ }
1749
+ }
1750
+
1751
+ return $m;
1752
+ }
1753
+
1754
+ /**
1755
+ * Performs RSA Blinding
1756
+ *
1757
+ * Protects against timing attacks by employing RSA Blinding.
1758
+ * Returns $x->modPow($this->exponents[$i], $this->primes[$i])
1759
+ *
1760
+ * @access private
1761
+ * @param Math_BigInteger $x
1762
+ * @param Math_BigInteger $r
1763
+ * @param Integer $i
1764
+ * @return Math_BigInteger
1765
+ */
1766
+ function _blind($x, $r, $i)
1767
+ {
1768
+ $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
1769
+ $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
1770
+
1771
+ $r = $r->modInverse($this->primes[$i]);
1772
+ $x = $x->multiply($r);
1773
+ list(, $x) = $x->divide($this->primes[$i]);
1774
+
1775
+ return $x;
1776
+ }
1777
+
1778
+ /**
1779
+ * RSAEP
1780
+ *
1781
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
1782
+ *
1783
+ * @access private
1784
+ * @param Math_BigInteger $m
1785
+ * @return Math_BigInteger
1786
+ */
1787
+ function _rsaep($m)
1788
+ {
1789
+ if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
1790
+ user_error('Message representative out of range', E_USER_NOTICE);
1791
+ return false;
1792
+ }
1793
+ return $this->_exponentiate($m);
1794
+ }
1795
+
1796
+ /**
1797
+ * RSADP
1798
+ *
1799
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
1800
+ *
1801
+ * @access private
1802
+ * @param Math_BigInteger $c
1803
+ * @return Math_BigInteger
1804
+ */
1805
+ function _rsadp($c)
1806
+ {
1807
+ if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
1808
+ user_error('Ciphertext representative out of range', E_USER_NOTICE);
1809
+ return false;
1810
+ }
1811
+ return $this->_exponentiate($c);
1812
+ }
1813
+
1814
+ /**
1815
+ * RSASP1
1816
+ *
1817
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
1818
+ *
1819
+ * @access private
1820
+ * @param Math_BigInteger $m
1821
+ * @return Math_BigInteger
1822
+ */
1823
+ function _rsasp1($m)
1824
+ {
1825
+ if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
1826
+ user_error('Message representative out of range', E_USER_NOTICE);
1827
+ return false;
1828
+ }
1829
+ return $this->_exponentiate($m);
1830
+ }
1831
+
1832
+ /**
1833
+ * RSAVP1
1834
+ *
1835
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
1836
+ *
1837
+ * @access private
1838
+ * @param Math_BigInteger $s
1839
+ * @return Math_BigInteger
1840
+ */
1841
+ function _rsavp1($s)
1842
+ {
1843
+ if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
1844
+ user_error('Signature representative out of range', E_USER_NOTICE);
1845
+ return false;
1846
+ }
1847
+ return $this->_exponentiate($s);
1848
+ }
1849
+
1850
+ /**
1851
+ * MGF1
1852
+ *
1853
+ * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
1854
+ *
1855
+ * @access private
1856
+ * @param String $mgfSeed
1857
+ * @param Integer $mgfLen
1858
+ * @return String
1859
+ */
1860
+ function _mgf1($mgfSeed, $maskLen)
1861
+ {
1862
+ // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
1863
+
1864
+ $t = '';
1865
+ $count = ceil($maskLen / $this->mgfHLen);
1866
+ for ($i = 0; $i < $count; $i++) {
1867
+ $c = pack('N', $i);
1868
+ $t.= $this->mgfHash->hash($mgfSeed . $c);
1869
+ }
1870
+
1871
+ return substr($t, 0, $maskLen);
1872
+ }
1873
+
1874
+ /**
1875
+ * RSAES-OAEP-ENCRYPT
1876
+ *
1877
+ * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
1878
+ * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
1879
+ *
1880
+ * @access private
1881
+ * @param String $m
1882
+ * @param String $l
1883
+ * @return String
1884
+ */
1885
+ function _rsaes_oaep_encrypt($m, $l = '')
1886
+ {
1887
+ $mLen = strlen($m);
1888
+
1889
+ // Length checking
1890
+
1891
+ // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1892
+ // be output.
1893
+
1894
+ if ($mLen > $this->k - 2 * $this->hLen - 2) {
1895
+ user_error('Message too long', E_USER_NOTICE);
1896
+ return false;
1897
+ }
1898
+
1899
+ // EME-OAEP encoding
1900
+
1901
+ $lHash = $this->hash->hash($l);
1902
+ $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
1903
+ $db = $lHash . $ps . chr(1) . $m;
1904
+ $seed = $this->_random($this->hLen);
1905
+ $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
1906
+ $maskedDB = $db ^ $dbMask;
1907
+ $seedMask = $this->_mgf1($maskedDB, $this->hLen);
1908
+ $maskedSeed = $seed ^ $seedMask;
1909
+ $em = chr(0) . $maskedSeed . $maskedDB;
1910
+
1911
+ // RSA encryption
1912
+
1913
+ $m = $this->_os2ip($em);
1914
+ $c = $this->_rsaep($m);
1915
+ $c = $this->_i2osp($c, $this->k);
1916
+
1917
+ // Output the ciphertext C
1918
+
1919
+ return $c;
1920
+ }
1921
+
1922
+ /**
1923
+ * RSAES-OAEP-DECRYPT
1924
+ *
1925
+ * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error
1926
+ * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
1927
+ *
1928
+ * Note. Care must be taken to ensure that an opponent cannot
1929
+ * distinguish the different error conditions in Step 3.g, whether by
1930
+ * error message or timing, or, more generally, learn partial
1931
+ * information about the encoded message EM. Otherwise an opponent may
1932
+ * be able to obtain useful information about the decryption of the
1933
+ * ciphertext C, leading to a chosen-ciphertext attack such as the one
1934
+ * observed by Manger [36].
1935
+ *
1936
+ * As for $l... to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
1937
+ *
1938
+ * Both the encryption and the decryption operations of RSAES-OAEP take
1939
+ * the value of a label L as input. In this version of PKCS #1, L is
1940
+ * the empty string; other uses of the label are outside the scope of
1941
+ * this document.
1942
+ *
1943
+ * @access private
1944
+ * @param String $c
1945
+ * @param String $l
1946
+ * @return String
1947
+ */
1948
+ function _rsaes_oaep_decrypt($c, $l = '')
1949
+ {
1950
+ // Length checking
1951
+
1952
+ // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1953
+ // be output.
1954
+
1955
+ if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
1956
+ user_error('Decryption error', E_USER_NOTICE);
1957
+ return false;
1958
+ }
1959
+
1960
+ // RSA decryption
1961
+
1962
+ $c = $this->_os2ip($c);
1963
+ $m = $this->_rsadp($c);
1964
+ if ($m === false) {
1965
+ user_error('Decryption error', E_USER_NOTICE);
1966
+ return false;
1967
+ }
1968
+ $em = $this->_i2osp($m, $this->k);
1969
+
1970
+ // EME-OAEP decoding
1971
+
1972
+ $lHash = $this->hash->hash($l);
1973
+ $y = ord($em[0]);
1974
+ $maskedSeed = substr($em, 1, $this->hLen);
1975
+ $maskedDB = substr($em, $this->hLen + 1);
1976
+ $seedMask = $this->_mgf1($maskedDB, $this->hLen);
1977
+ $seed = $maskedSeed ^ $seedMask;
1978
+ $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
1979
+ $db = $maskedDB ^ $dbMask;
1980
+ $lHash2 = substr($db, 0, $this->hLen);
1981
+ $m = substr($db, $this->hLen);
1982
+ if ($lHash != $lHash2) {
1983
+ user_error('Decryption error', E_USER_NOTICE);
1984
+ return false;
1985
+ }
1986
+ $m = ltrim($m, chr(0));
1987
+ if (ord($m[0]) != 1) {
1988
+ user_error('Decryption error', E_USER_NOTICE);
1989
+ return false;
1990
+ }
1991
+
1992
+ // Output the message M
1993
+
1994
+ return substr($m, 1);
1995
+ }
1996
+
1997
+ /**
1998
+ * RSAES-PKCS1-V1_5-ENCRYPT
1999
+ *
2000
+ * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
2001
+ *
2002
+ * @access private
2003
+ * @param String $m
2004
+ * @return String
2005
+ */
2006
+ function _rsaes_pkcs1_v1_5_encrypt($m)
2007
+ {
2008
+ $mLen = strlen($m);
2009
+
2010
+ // Length checking
2011
+
2012
+ if ($mLen > $this->k - 11) {
2013
+ user_error('Message too long', E_USER_NOTICE);
2014
+ return false;
2015
+ }
2016
+
2017
+ // EME-PKCS1-v1_5 encoding
2018
+
2019
+ $ps = $this->_random($this->k - $mLen - 3, true);
2020
+ $em = chr(0) . chr(2) . $ps . chr(0) . $m;
2021
+
2022
+ // RSA encryption
2023
+ $m = $this->_os2ip($em);
2024
+ $c = $this->_rsaep($m);
2025
+ $c = $this->_i2osp($c, $this->k);
2026
+
2027
+ // Output the ciphertext C
2028
+
2029
+ return $c;
2030
+ }
2031
+
2032
+ /**
2033
+ * RSAES-PKCS1-V1_5-DECRYPT
2034
+ *
2035
+ * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
2036
+ *
2037
+ * For compatability purposes, this function departs slightly from the description given in RFC3447.
2038
+ * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
2039
+ * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
2040
+ * public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed
2041
+ * to be 2 regardless of which key is used. for compatability purposes, we'll just check to make sure the
2042
+ * second byte is 2 or less. If it is, we'll accept the decrypted string as valid.
2043
+ *
2044
+ * As a consequence of this, a private key encrypted ciphertext produced with Crypt_RSA may not decrypt
2045
+ * with a strictly PKCS#1 v1.5 compliant RSA implementation. Public key encrypted ciphertext's should but
2046
+ * not private key encrypted ciphertext's.
2047
+ *
2048
+ * @access private
2049
+ * @param String $c
2050
+ * @return String
2051
+ */
2052
+ function _rsaes_pkcs1_v1_5_decrypt($c)
2053
+ {
2054
+ // Length checking
2055
+
2056
+ if (strlen($c) != $this->k) { // or if k < 11
2057
+ user_error('Decryption error', E_USER_NOTICE);
2058
+ return false;
2059
+ }
2060
+
2061
+ // RSA decryption
2062
+
2063
+ $c = $this->_os2ip($c);
2064
+ $m = $this->_rsadp($c);
2065
+
2066
+ if ($m === false) {
2067
+ user_error('Decryption error', E_USER_NOTICE);
2068
+ return false;
2069
+ }
2070
+ $em = $this->_i2osp($m, $this->k);
2071
+
2072
+ // EME-PKCS1-v1_5 decoding
2073
+
2074
+ if (ord($em[0]) != 0 || ord($em[1]) > 2) {
2075
+ user_error('Decryption error', E_USER_NOTICE);
2076
+ return false;
2077
+ }
2078
+
2079
+ $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
2080
+ $m = substr($em, strlen($ps) + 3);
2081
+
2082
+ if (strlen($ps) < 8) {
2083
+ user_error('Decryption error', E_USER_NOTICE);
2084
+ return false;
2085
+ }
2086
+
2087
+ // Output M
2088
+
2089
+ return $m;
2090
+ }
2091
+
2092
+ /**
2093
+ * EMSA-PSS-ENCODE
2094
+ *
2095
+ * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
2096
+ *
2097
+ * @access private
2098
+ * @param String $m
2099
+ * @param Integer $emBits
2100
+ */
2101
+ function _emsa_pss_encode($m, $emBits)
2102
+ {
2103
+ // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2104
+ // be output.
2105
+
2106
+ $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
2107
+ $sLen = $this->sLen == false ? $this->hLen : $this->sLen;
2108
+
2109
+ $mHash = $this->hash->hash($m);
2110
+ if ($emLen < $this->hLen + $sLen + 2) {
2111
+ user_error('Encoding error', E_USER_NOTICE);
2112
+ return false;
2113
+ }
2114
+
2115
+ $salt = $this->_random($sLen);
2116
+ $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
2117
+ $h = $this->hash->hash($m2);
2118
+ $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
2119
+ $db = $ps . chr(1) . $salt;
2120
+ $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
2121
+ $maskedDB = $db ^ $dbMask;
2122
+ $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
2123
+ $em = $maskedDB . $h . chr(0xBC);
2124
+
2125
+ return $em;
2126
+ }
2127
+
2128
+ /**
2129
+ * EMSA-PSS-VERIFY
2130
+ *
2131
+ * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
2132
+ *
2133
+ * @access private
2134
+ * @param String $m
2135
+ * @param String $em
2136
+ * @param Integer $emBits
2137
+ * @return String
2138
+ */
2139
+ function _emsa_pss_verify($m, $em, $emBits)
2140
+ {
2141
+ // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2142
+ // be output.
2143
+
2144
+ $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
2145
+ $sLen = $this->sLen == false ? $this->hLen : $this->sLen;
2146
+
2147
+ $mHash = $this->hash->hash($m);
2148
+ if ($emLen < $this->hLen + $sLen + 2) {
2149
+ return false;
2150
+ }
2151
+
2152
+ if ($em[strlen($em) - 1] != chr(0xBC)) {
2153
+ return false;
2154
+ }
2155
+
2156
+ $maskedDB = substr($em, 0, -$this->hLen - 1);
2157
+ $h = substr($em, -$this->hLen - 1, $this->hLen);
2158
+ $temp = chr(0xFF << ($emBits & 7));
2159
+ if ((~$maskedDB[0] & $temp) != $temp) {
2160
+ return false;
2161
+ }
2162
+ $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
2163
+ $db = $maskedDB ^ $dbMask;
2164
+ $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
2165
+ $temp = $emLen - $this->hLen - $sLen - 2;
2166
+ if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
2167
+ return false;
2168
+ }
2169
+ $salt = substr($db, $temp + 1); // should be $sLen long
2170
+ $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
2171
+ $h2 = $this->hash->hash($m2);
2172
+ return $h == $h2;
2173
+ }
2174
+
2175
+ /**
2176
+ * RSASSA-PSS-SIGN
2177
+ *
2178
+ * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
2179
+ *
2180
+ * @access private
2181
+ * @param String $m
2182
+ * @return String
2183
+ */
2184
+ function _rsassa_pss_sign($m)
2185
+ {
2186
+ // EMSA-PSS encoding
2187
+
2188
+ $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
2189
+
2190
+ // RSA signature
2191
+
2192
+ $m = $this->_os2ip($em);
2193
+ $s = $this->_rsasp1($m);
2194
+ $s = $this->_i2osp($s, $this->k);
2195
+
2196
+ // Output the signature S
2197
+
2198
+ return $s;
2199
+ }
2200
+
2201
+ /**
2202
+ * RSASSA-PSS-VERIFY
2203
+ *
2204
+ * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
2205
+ *
2206
+ * @access private
2207
+ * @param String $m
2208
+ * @param String $s
2209
+ * @return String
2210
+ */
2211
+ function _rsassa_pss_verify($m, $s)
2212
+ {
2213
+ // Length checking
2214
+
2215
+ if (strlen($s) != $this->k) {
2216
+ user_error('Invalid signature', E_USER_NOTICE);
2217
+ return false;
2218
+ }
2219
+
2220
+ // RSA verification
2221
+
2222
+ $modBits = 8 * $this->k;
2223
+
2224
+ $s2 = $this->_os2ip($s);
2225
+ $m2 = $this->_rsavp1($s2);
2226
+ if ($m2 === false) {
2227
+ user_error('Invalid signature', E_USER_NOTICE);
2228
+ return false;
2229
+ }
2230
+ $em = $this->_i2osp($m2, $modBits >> 3);
2231
+ if ($em === false) {
2232
+ user_error('Invalid signature', E_USER_NOTICE);
2233
+ return false;
2234
+ }
2235
+
2236
+ // EMSA-PSS verification
2237
+
2238
+ return $this->_emsa_pss_verify($m, $em, $modBits - 1);
2239
+ }
2240
+
2241
+ /**
2242
+ * EMSA-PKCS1-V1_5-ENCODE
2243
+ *
2244
+ * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
2245
+ *
2246
+ * @access private
2247
+ * @param String $m
2248
+ * @param Integer $emLen
2249
+ * @return String
2250
+ */
2251
+ function _emsa_pkcs1_v1_5_encode($m, $emLen)
2252
+ {
2253
+ $h = $this->hash->hash($m);
2254
+ if ($h === false) {
2255
+ return false;
2256
+ }
2257
+
2258
+ // see http://tools.ietf.org/html/rfc3447#page-43
2259
+ switch ($this->hashName) {
2260
+ case 'md2':
2261
+ $t = pack('H*', '3020300c06082a864886f70d020205000410');
2262
+ break;
2263
+ case 'md5':
2264
+ $t = pack('H*', '3020300c06082a864886f70d020505000410');
2265
+ break;
2266
+ case 'sha1':
2267
+ $t = pack('H*', '3021300906052b0e03021a05000414');
2268
+ break;
2269
+ case 'sha256':
2270
+ $t = pack('H*', '3031300d060960864801650304020105000420');
2271
+ break;
2272
+ case 'sha384':
2273
+ $t = pack('H*', '3041300d060960864801650304020205000430');
2274
+ break;
2275
+ case 'sha512':
2276
+ $t = pack('H*', '3051300d060960864801650304020305000440');
2277
+ }
2278
+ $t.= $h;
2279
+ $tLen = strlen($t);
2280
+
2281
+ if ($emLen < $tLen + 11) {
2282
+ user_error('Intended encoded message length too short', E_USER_NOTICE);
2283
+ return false;
2284
+ }
2285
+
2286
+ $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
2287
+
2288
+ $em = "\0\1$ps\0$t";
2289
+
2290
+ return $em;
2291
+ }
2292
+
2293
+ /**
2294
+ * RSASSA-PKCS1-V1_5-SIGN
2295
+ *
2296
+ * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
2297
+ *
2298
+ * @access private
2299
+ * @param String $m
2300
+ * @return String
2301
+ */
2302
+ function _rsassa_pkcs1_v1_5_sign($m)
2303
+ {
2304
+ // EMSA-PKCS1-v1_5 encoding
2305
+
2306
+ $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
2307
+ if ($em === false) {
2308
+ user_error('RSA modulus too short', E_USER_NOTICE);
2309
+ return false;
2310
+ }
2311
+
2312
+ // RSA signature
2313
+
2314
+ $m = $this->_os2ip($em);
2315
+ $s = $this->_rsasp1($m);
2316
+ $s = $this->_i2osp($s, $this->k);
2317
+
2318
+ // Output the signature S
2319
+
2320
+ return $s;
2321
+ }
2322
+
2323
+ /**
2324
+ * RSASSA-PKCS1-V1_5-VERIFY
2325
+ *
2326
+ * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
2327
+ *
2328
+ * @access private
2329
+ * @param String $m
2330
+ * @return String
2331
+ */
2332
+ function _rsassa_pkcs1_v1_5_verify($m, $s)
2333
+ {
2334
+ // Length checking
2335
+
2336
+ if (strlen($s) != $this->k) {
2337
+ user_error('Invalid signature', E_USER_NOTICE);
2338
+ return false;
2339
+ }
2340
+
2341
+ // RSA verification
2342
+
2343
+ $s = $this->_os2ip($s);
2344
+ $m2 = $this->_rsavp1($s);
2345
+ if ($m2 === false) {
2346
+ user_error('Invalid signature', E_USER_NOTICE);
2347
+ return false;
2348
+ }
2349
+ $em = $this->_i2osp($m2, $this->k);
2350
+ if ($em === false) {
2351
+ user_error('Invalid signature', E_USER_NOTICE);
2352
+ return false;
2353
+ }
2354
+
2355
+ // EMSA-PKCS1-v1_5 encoding
2356
+
2357
+ $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
2358
+ if ($em2 === false) {
2359
+ user_error('RSA modulus too short', E_USER_NOTICE);
2360
+ return false;
2361
+ }
2362
+
2363
+ // Compare
2364
+
2365
+ return $em === $em2;
2366
+ }
2367
+
2368
+ /**
2369
+ * Set Encryption Mode
2370
+ *
2371
+ * Valid values include CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1.
2372
+ *
2373
+ * @access public
2374
+ * @param Integer $mode
2375
+ */
2376
+ function setEncryptionMode($mode)
2377
+ {
2378
+ $this->encryptionMode = $mode;
2379
+ }
2380
+
2381
+ /**
2382
+ * Set Signature Mode
2383
+ *
2384
+ * Valid values include CRYPT_RSA_SIGNATURE_PSS and CRYPT_RSA_SIGNATURE_PKCS1
2385
+ *
2386
+ * @access public
2387
+ * @param Integer $mode
2388
+ */
2389
+ function setSignatureMode($mode)
2390
+ {
2391
+ $this->signatureMode = $mode;
2392
+ }
2393
+
2394
+ /**
2395
+ * Encryption
2396
+ *
2397
+ * Both CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
2398
+ * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
2399
+ * be concatenated together.
2400
+ *
2401
+ * @see decrypt()
2402
+ * @access public
2403
+ * @param String $plaintext
2404
+ * @return String
2405
+ */
2406
+ function encrypt($plaintext)
2407
+ {
2408
+ switch ($this->encryptionMode) {
2409
+ case CRYPT_RSA_ENCRYPTION_PKCS1:
2410
+ $length = $this->k - 11;
2411
+ if ($length <= 0) {
2412
+ return false;
2413
+ }
2414
+
2415
+ $plaintext = str_split($plaintext, $length);
2416
+ $ciphertext = '';
2417
+ foreach ($plaintext as $m) {
2418
+ $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
2419
+ }
2420
+ return $ciphertext;
2421
+ //case CRYPT_RSA_ENCRYPTION_OAEP:
2422
+ default:
2423
+ $length = $this->k - 2 * $this->hLen - 2;
2424
+ if ($length <= 0) {
2425
+ return false;
2426
+ }
2427
+
2428
+ $plaintext = str_split($plaintext, $length);
2429
+ $ciphertext = '';
2430
+ foreach ($plaintext as $m) {
2431
+ $ciphertext.= $this->_rsaes_oaep_encrypt($m);
2432
+ }
2433
+ return $ciphertext;
2434
+ }
2435
+ }
2436
+
2437
+ /**
2438
+ * Decryption
2439
+ *
2440
+ * @see encrypt()
2441
+ * @access public
2442
+ * @param String $plaintext
2443
+ * @return String
2444
+ */
2445
+ function decrypt($ciphertext)
2446
+ {
2447
+ if ($this->k <= 0) {
2448
+ return false;
2449
+ }
2450
+
2451
+ $ciphertext = str_split($ciphertext, $this->k);
2452
+ $plaintext = '';
2453
+
2454
+ switch ($this->encryptionMode) {
2455
+ case CRYPT_RSA_ENCRYPTION_PKCS1:
2456
+ $decrypt = '_rsaes_pkcs1_v1_5_decrypt';
2457
+ break;
2458
+ //case CRYPT_RSA_ENCRYPTION_OAEP:
2459
+ default:
2460
+ $decrypt = '_rsaes_oaep_decrypt';
2461
+ }
2462
+
2463
+ foreach ($ciphertext as $c) {
2464
+ $temp = $this->$decrypt($c);
2465
+ if ($temp === false) {
2466
+ return false;
2467
+ }
2468
+ $plaintext.= $temp;
2469
+ }
2470
+
2471
+ return $plaintext;
2472
+ }
2473
+
2474
+ /**
2475
+ * Create a signature
2476
+ *
2477
+ * @see verify()
2478
+ * @access public
2479
+ * @param String $message
2480
+ * @return String
2481
+ */
2482
+ function sign($message)
2483
+ {
2484
+ if (empty($this->modulus) || empty($this->exponent)) {
2485
+ return false;
2486
+ }
2487
+
2488
+ switch ($this->signatureMode) {
2489
+ case CRYPT_RSA_SIGNATURE_PKCS1:
2490
+ return $this->_rsassa_pkcs1_v1_5_sign($message);
2491
+ //case CRYPT_RSA_SIGNATURE_PSS:
2492
+ default:
2493
+ return $this->_rsassa_pss_sign($message);
2494
+ }
2495
+ }
2496
+
2497
+ /**
2498
+ * Verifies a signature
2499
+ *
2500
+ * @see sign()
2501
+ * @access public
2502
+ * @param String $message
2503
+ * @param String $signature
2504
+ * @return Boolean
2505
+ */
2506
+ function verify($message, $signature)
2507
+ {
2508
+ if (empty($this->modulus) || empty($this->exponent)) {
2509
+ return false;
2510
+ }
2511
+
2512
+ switch ($this->signatureMode) {
2513
+ case CRYPT_RSA_SIGNATURE_PKCS1:
2514
+ return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
2515
+ //case CRYPT_RSA_SIGNATURE_PSS:
2516
+ default:
2517
+ return $this->_rsassa_pss_verify($message, $signature);
2518
+ }
2519
+ }
2520
+ }
phpseclib/Crypt/Random.php ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Random Number Generator
6
+ *
7
+ * PHP versions 4 and 5
8
+ *
9
+ * Here's a short example of how to use this library:
10
+ * <code>
11
+ * <?php
12
+ * include('Crypt/Random.php');
13
+ *
14
+ * echo crypt_random();
15
+ * ?>
16
+ * </code>
17
+ *
18
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
19
+ * of this software and associated documentation files (the "Software"), to deal
20
+ * in the Software without restriction, including without limitation the rights
21
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
22
+ * copies of the Software, and to permit persons to whom the Software is
23
+ * furnished to do so, subject to the following conditions:
24
+ *
25
+ * The above copyright notice and this permission notice shall be included in
26
+ * all copies or substantial portions of the Software.
27
+ *
28
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
31
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
33
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
34
+ * THE SOFTWARE.
35
+ *
36
+ * @category Crypt
37
+ * @package Crypt_Random
38
+ * @author Jim Wigginton <terrafrost@php.net>
39
+ * @copyright MMVII Jim Wigginton
40
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
41
+ * @version $Id: Random.php,v 1.9 2010/04/24 06:40:48 terrafrost Exp $
42
+ * @link http://phpseclib.sourceforge.net
43
+ */
44
+
45
+ /**
46
+ * Generate a random value.
47
+ *
48
+ * On 32-bit machines, the largest distance that can exist between $min and $max is 2**31.
49
+ * If $min and $max are farther apart than that then the last ($max - range) numbers.
50
+ *
51
+ * Depending on how this is being used, it may be worth while to write a replacement. For example,
52
+ * a PHP-based web app that stores its data in an SQL database can collect more entropy than this function
53
+ * can.
54
+ *
55
+ * @param optional Integer $min
56
+ * @param optional Integer $max
57
+ * @return Integer
58
+ * @access public
59
+ */
60
+ function crypt_random($min = 0, $max = 0x7FFFFFFF)
61
+ {
62
+ if ($min == $max) {
63
+ return $min;
64
+ }
65
+
66
+ if (function_exists('openssl_random_pseudo_bytes')) {
67
+ // openssl_random_pseudo_bytes() is slow on windows per the following:
68
+ // http://stackoverflow.com/questions/1940168/openssl-random-pseudo-bytes-is-slow-php
69
+ if ((PHP_OS & "\xDF\xDF\xDF") !== 'WIN') { // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster
70
+ extract(unpack('Nrandom', openssl_random_pseudo_bytes(4)));
71
+
72
+ return abs($random) % ($max - $min) + $min;
73
+ }
74
+ }
75
+
76
+ // see http://en.wikipedia.org/wiki//dev/random
77
+ static $urandom = true;
78
+ if ($urandom === true) {
79
+ // Warning's will be output unles the error suppression operator is used. Errors such as
80
+ // "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc.
81
+ $urandom = @fopen('/dev/urandom', 'rb');
82
+ }
83
+ if (!is_bool($urandom)) {
84
+ extract(unpack('Nrandom', fread($urandom, 4)));
85
+
86
+ // say $min = 0 and $max = 3. if we didn't do abs() then we could have stuff like this:
87
+ // -4 % 3 + 0 = -1, even though -1 < $min
88
+ return abs($random) % ($max - $min) + $min;
89
+ }
90
+
91
+ /* Prior to PHP 4.2.0, mt_srand() had to be called before mt_rand() could be called.
92
+ Prior to PHP 5.2.6, mt_rand()'s automatic seeding was subpar, as elaborated here:
93
+
94
+ http://www.suspekt.org/2008/08/17/mt_srand-and-not-so-random-numbers/
95
+
96
+ The seeding routine is pretty much ripped from PHP's own internal GENERATE_SEED() macro:
97
+
98
+ http://svn.php.net/viewvc/php/php-src/tags/php_5_3_2/ext/standard/php_rand.h?view=markup */
99
+ if (version_compare(PHP_VERSION, '5.2.5', '<=')) {
100
+ static $seeded;
101
+ if (!isset($seeded)) {
102
+ $seeded = true;
103
+ mt_srand(fmod(time() * getmypid(), 0x7FFFFFFF) ^ fmod(1000000 * lcg_value(), 0x7FFFFFFF));
104
+ }
105
+ }
106
+
107
+ static $crypto;
108
+
109
+ // The CSPRNG's Yarrow and Fortuna periodically reseed. This function can be reseeded by hitting F5
110
+ // in the browser and reloading the page.
111
+
112
+ if (!isset($crypto)) {
113
+ $key = $iv = '';
114
+ for ($i = 0; $i < 8; $i++) {
115
+ $key.= pack('n', mt_rand(0, 0xFFFF));
116
+ $iv .= pack('n', mt_rand(0, 0xFFFF));
117
+ }
118
+ switch (true) {
119
+ case class_exists('Crypt_AES'):
120
+ $crypto = new Crypt_AES(CRYPT_AES_MODE_CTR);
121
+ break;
122
+ case class_exists('Crypt_TripleDES'):
123
+ $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
124
+ break;
125
+ case class_exists('Crypt_DES'):
126
+ $crypto = new Crypt_DES(CRYPT_DES_MODE_CTR);
127
+ break;
128
+ case class_exists('Crypt_RC4'):
129
+ $crypto = new Crypt_RC4();
130
+ break;
131
+ default:
132
+ extract(unpack('Nrandom', pack('H*', sha1(mt_rand(0, 0x7FFFFFFF)))));
133
+ return abs($random) % ($max - $min) + $min;
134
+ }
135
+ $crypto->setKey($key);
136
+ $crypto->setIV($iv);
137
+ $crypto->enableContinuousBuffer();
138
+ }
139
+
140
+ extract(unpack('Nrandom', $crypto->encrypt("\0\0\0\0")));
141
+ return abs($random) % ($max - $min) + $min;
142
+ }
143
+ ?>
phpseclib/Crypt/Rijndael.php ADDED
@@ -0,0 +1,1478 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP implementation of Rijndael.
6
+ *
7
+ * Does not use mcrypt, even when available, for reasons that are explained below.
8
+ *
9
+ * PHP versions 4 and 5
10
+ *
11
+ * If {@link Crypt_Rijndael::setBlockLength() setBlockLength()} isn't called, it'll be assumed to be 128 bits. If
12
+ * {@link Crypt_Rijndael::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
13
+ * {@link Crypt_Rijndael::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's
14
+ * 136-bits it'll be null-padded to 160-bits and 160 bits will be the key length until
15
+ * {@link Crypt_Rijndael::setKey() setKey()} is called, again, at which point, it'll be recalculated.
16
+ *
17
+ * Not all Rijndael implementations may support 160-bits or 224-bits as the block length / key length. mcrypt, for example,
18
+ * does not. AES, itself, only supports block lengths of 128 and key lengths of 128, 192, and 256.
19
+ * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=10 Rijndael-ammended.pdf#page=10} defines the
20
+ * algorithm for block lengths of 192 and 256 but not for block lengths / key lengths of 160 and 224. Indeed, 160 and 224
21
+ * are first defined as valid key / block lengths in
22
+ * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=44 Rijndael-ammended.pdf#page=44}:
23
+ * Extensions: Other block and Cipher Key lengths.
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('Crypt/Rijndael.php');
32
+ *
33
+ * $rijndael = new 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
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
48
+ * of this software and associated documentation files (the "Software"), to deal
49
+ * in the Software without restriction, including without limitation the rights
50
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
51
+ * copies of the Software, and to permit persons to whom the Software is
52
+ * furnished to do so, subject to the following conditions:
53
+ *
54
+ * The above copyright notice and this permission notice shall be included in
55
+ * all copies or substantial portions of the Software.
56
+ *
57
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
58
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
59
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
60
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
61
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
62
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
63
+ * THE SOFTWARE.
64
+ *
65
+ * @category Crypt
66
+ * @package Crypt_Rijndael
67
+ * @author Jim Wigginton <terrafrost@php.net>
68
+ * @copyright MMVIII Jim Wigginton
69
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
70
+ * @version $Id: Rijndael.php,v 1.12 2010/02/09 06:10:26 terrafrost Exp $
71
+ * @link http://phpseclib.sourceforge.net
72
+ */
73
+
74
+ /**#@+
75
+ * @access public
76
+ * @see Crypt_Rijndael::encrypt()
77
+ * @see Crypt_Rijndael::decrypt()
78
+ */
79
+ /**
80
+ * Encrypt / decrypt using the Counter mode.
81
+ *
82
+ * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
83
+ *
84
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
85
+ */
86
+ define('CRYPT_RIJNDAEL_MODE_CTR', -1);
87
+ /**
88
+ * Encrypt / decrypt using the Electronic Code Book mode.
89
+ *
90
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
91
+ */
92
+ define('CRYPT_RIJNDAEL_MODE_ECB', 1);
93
+ /**
94
+ * Encrypt / decrypt using the Code Book Chaining mode.
95
+ *
96
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
97
+ */
98
+ define('CRYPT_RIJNDAEL_MODE_CBC', 2);
99
+ /**
100
+ * Encrypt / decrypt using the Cipher Feedback mode.
101
+ *
102
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
103
+ */
104
+ define('CRYPT_RIJNDAEL_MODE_CFB', 3);
105
+ /**
106
+ * Encrypt / decrypt using the Cipher Feedback mode.
107
+ *
108
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
109
+ */
110
+ define('CRYPT_RIJNDAEL_MODE_OFB', 4);
111
+ /**#@-*/
112
+
113
+ /**#@+
114
+ * @access private
115
+ * @see Crypt_Rijndael::Crypt_Rijndael()
116
+ */
117
+ /**
118
+ * Toggles the internal implementation
119
+ */
120
+ define('CRYPT_RIJNDAEL_MODE_INTERNAL', 1);
121
+ /**
122
+ * Toggles the mcrypt implementation
123
+ */
124
+ define('CRYPT_RIJNDAEL_MODE_MCRYPT', 2);
125
+ /**#@-*/
126
+
127
+ /**
128
+ * Pure-PHP implementation of Rijndael.
129
+ *
130
+ * @author Jim Wigginton <terrafrost@php.net>
131
+ * @version 0.1.0
132
+ * @access public
133
+ * @package Crypt_Rijndael
134
+ */
135
+ class Crypt_Rijndael {
136
+ /**
137
+ * The Encryption Mode
138
+ *
139
+ * @see Crypt_Rijndael::Crypt_Rijndael()
140
+ * @var Integer
141
+ * @access private
142
+ */
143
+ var $mode;
144
+
145
+ /**
146
+ * The Key
147
+ *
148
+ * @see Crypt_Rijndael::setKey()
149
+ * @var String
150
+ * @access private
151
+ */
152
+ var $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
153
+
154
+ /**
155
+ * The Initialization Vector
156
+ *
157
+ * @see Crypt_Rijndael::setIV()
158
+ * @var String
159
+ * @access private
160
+ */
161
+ var $iv = '';
162
+
163
+ /**
164
+ * A "sliding" Initialization Vector
165
+ *
166
+ * @see Crypt_Rijndael::enableContinuousBuffer()
167
+ * @var String
168
+ * @access private
169
+ */
170
+ var $encryptIV = '';
171
+
172
+ /**
173
+ * A "sliding" Initialization Vector
174
+ *
175
+ * @see Crypt_Rijndael::enableContinuousBuffer()
176
+ * @var String
177
+ * @access private
178
+ */
179
+ var $decryptIV = '';
180
+
181
+ /**
182
+ * Continuous Buffer status
183
+ *
184
+ * @see Crypt_Rijndael::enableContinuousBuffer()
185
+ * @var Boolean
186
+ * @access private
187
+ */
188
+ var $continuousBuffer = false;
189
+
190
+ /**
191
+ * Padding status
192
+ *
193
+ * @see Crypt_Rijndael::enablePadding()
194
+ * @var Boolean
195
+ * @access private
196
+ */
197
+ var $padding = true;
198
+
199
+ /**
200
+ * Does the key schedule need to be (re)calculated?
201
+ *
202
+ * @see setKey()
203
+ * @see setBlockLength()
204
+ * @see setKeyLength()
205
+ * @var Boolean
206
+ * @access private
207
+ */
208
+ var $changed = true;
209
+
210
+ /**
211
+ * Has the key length explicitly been set or should it be derived from the key, itself?
212
+ *
213
+ * @see setKeyLength()
214
+ * @var Boolean
215
+ * @access private
216
+ */
217
+ var $explicit_key_length = false;
218
+
219
+ /**
220
+ * The Key Schedule
221
+ *
222
+ * @see _setup()
223
+ * @var Array
224
+ * @access private
225
+ */
226
+ var $w;
227
+
228
+ /**
229
+ * The Inverse Key Schedule
230
+ *
231
+ * @see _setup()
232
+ * @var Array
233
+ * @access private
234
+ */
235
+ var $dw;
236
+
237
+ /**
238
+ * The Block Length
239
+ *
240
+ * @see setBlockLength()
241
+ * @var Integer
242
+ * @access private
243
+ * @internal The max value is 32, the min value is 16. All valid values are multiples of 4. Exists in conjunction with
244
+ * $Nb because we need this value and not $Nb to pad strings appropriately.
245
+ */
246
+ var $block_size = 16;
247
+
248
+ /**
249
+ * The Block Length divided by 32
250
+ *
251
+ * @see setBlockLength()
252
+ * @var Integer
253
+ * @access private
254
+ * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4. Exists in conjunction with $block_size
255
+ * because the encryption / decryption / key schedule creation requires this number and not $block_size. We could
256
+ * derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
257
+ * of that, we'll just precompute it once.
258
+ *
259
+ */
260
+ var $Nb = 4;
261
+
262
+ /**
263
+ * The Key Length
264
+ *
265
+ * @see setKeyLength()
266
+ * @var Integer
267
+ * @access private
268
+ * @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $key_size
269
+ * because the encryption / decryption / key schedule creation requires this number and not $key_size. We could
270
+ * derive this from $key_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
271
+ * of that, we'll just precompute it once.
272
+ */
273
+ var $key_size = 16;
274
+
275
+ /**
276
+ * The Key Length divided by 32
277
+ *
278
+ * @see setKeyLength()
279
+ * @var Integer
280
+ * @access private
281
+ * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4
282
+ */
283
+ var $Nk = 4;
284
+
285
+ /**
286
+ * The Number of Rounds
287
+ *
288
+ * @var Integer
289
+ * @access private
290
+ * @internal The max value is 14, the min value is 10.
291
+ */
292
+ var $Nr;
293
+
294
+ /**
295
+ * Shift offsets
296
+ *
297
+ * @var Array
298
+ * @access private
299
+ */
300
+ var $c;
301
+
302
+ /**
303
+ * Precomputed mixColumns table
304
+ *
305
+ * @see Crypt_Rijndael()
306
+ * @var Array
307
+ * @access private
308
+ */
309
+ var $t0;
310
+
311
+ /**
312
+ * Precomputed mixColumns table
313
+ *
314
+ * @see Crypt_Rijndael()
315
+ * @var Array
316
+ * @access private
317
+ */
318
+ var $t1;
319
+
320
+ /**
321
+ * Precomputed mixColumns table
322
+ *
323
+ * @see Crypt_Rijndael()
324
+ * @var Array
325
+ * @access private
326
+ */
327
+ var $t2;
328
+
329
+ /**
330
+ * Precomputed mixColumns table
331
+ *
332
+ * @see Crypt_Rijndael()
333
+ * @var Array
334
+ * @access private
335
+ */
336
+ var $t3;
337
+
338
+ /**
339
+ * Precomputed invMixColumns table
340
+ *
341
+ * @see Crypt_Rijndael()
342
+ * @var Array
343
+ * @access private
344
+ */
345
+ var $dt0;
346
+
347
+ /**
348
+ * Precomputed invMixColumns table
349
+ *
350
+ * @see Crypt_Rijndael()
351
+ * @var Array
352
+ * @access private
353
+ */
354
+ var $dt1;
355
+
356
+ /**
357
+ * Precomputed invMixColumns table
358
+ *
359
+ * @see Crypt_Rijndael()
360
+ * @var Array
361
+ * @access private
362
+ */
363
+ var $dt2;
364
+
365
+ /**
366
+ * Precomputed invMixColumns table
367
+ *
368
+ * @see Crypt_Rijndael()
369
+ * @var Array
370
+ * @access private
371
+ */
372
+ var $dt3;
373
+
374
+ /**
375
+ * Is the mode one that is paddable?
376
+ *
377
+ * @see Crypt_Rijndael::Crypt_Rijndael()
378
+ * @var Boolean
379
+ * @access private
380
+ */
381
+ var $paddable = false;
382
+
383
+ /**
384
+ * Encryption buffer for CTR, OFB and CFB modes
385
+ *
386
+ * @see Crypt_Rijndael::encrypt()
387
+ * @var String
388
+ * @access private
389
+ */
390
+ var $enbuffer = array('encrypted' => '', 'xor' => '');
391
+
392
+ /**
393
+ * Decryption buffer for CTR, OFB and CFB modes
394
+ *
395
+ * @see Crypt_Rijndael::decrypt()
396
+ * @var String
397
+ * @access private
398
+ */
399
+ var $debuffer = array('ciphertext' => '');
400
+
401
+ /**
402
+ * Default Constructor.
403
+ *
404
+ * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
405
+ * CRYPT_RIJNDAEL_MODE_ECB or CRYPT_RIJNDAEL_MODE_CBC. If not explictly set, CRYPT_RIJNDAEL_MODE_CBC will be used.
406
+ *
407
+ * @param optional Integer $mode
408
+ * @return Crypt_Rijndael
409
+ * @access public
410
+ */
411
+ function Crypt_Rijndael($mode = CRYPT_RIJNDAEL_MODE_CBC)
412
+ {
413
+ switch ($mode) {
414
+ case CRYPT_RIJNDAEL_MODE_ECB:
415
+ case CRYPT_RIJNDAEL_MODE_CBC:
416
+ $this->paddable = true;
417
+ $this->mode = $mode;
418
+ break;
419
+ case CRYPT_RIJNDAEL_MODE_CTR:
420
+ case CRYPT_RIJNDAEL_MODE_CFB:
421
+ case CRYPT_RIJNDAEL_MODE_OFB:
422
+ $this->mode = $mode;
423
+ break;
424
+ default:
425
+ $this->paddable = true;
426
+ $this->mode = CRYPT_RIJNDAEL_MODE_CBC;
427
+ }
428
+
429
+ $t3 = &$this->t3;
430
+ $t2 = &$this->t2;
431
+ $t1 = &$this->t1;
432
+ $t0 = &$this->t0;
433
+
434
+ $dt3 = &$this->dt3;
435
+ $dt2 = &$this->dt2;
436
+ $dt1 = &$this->dt1;
437
+ $dt0 = &$this->dt0;
438
+
439
+ // according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=19> (section 5.2.1),
440
+ // precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so
441
+ // those are the names we'll use.
442
+ $t3 = array(
443
+ 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
444
+ 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC,
445
+ 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB,
446
+ 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B,
447
+ 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83,
448
+ 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A,
449
+ 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F,
450
+ 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA,
451
+ 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B,
452
+ 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713,
453
+ 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6,
454
+ 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85,
455
+ 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411,
456
+ 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B,
457
+ 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1,
458
+ 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF,
459
+ 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E,
460
+ 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6,
461
+ 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B,
462
+ 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD,
463
+ 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8,
464
+ 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2,
465
+ 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049,
466
+ 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810,
467
+ 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197,
468
+ 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F,
469
+ 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C,
470
+ 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927,
471
+ 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733,
472
+ 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5,
473
+ 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0,
474
+ 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C
475
+ );
476
+
477
+ $dt3 = array(
478
+ 0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B,
479
+ 0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5,
480
+ 0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B,
481
+ 0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E,
482
+ 0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D,
483
+ 0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9,
484
+ 0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66,
485
+ 0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED,
486
+ 0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4,
487
+ 0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD,
488
+ 0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60,
489
+ 0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79,
490
+ 0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C,
491
+ 0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24,
492
+ 0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C,
493
+ 0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814,
494
+ 0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B,
495
+ 0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084,
496
+ 0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077,
497
+ 0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22,
498
+ 0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F,
499
+ 0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582,
500
+ 0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB,
501
+ 0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF,
502
+ 0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035,
503
+ 0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17,
504
+ 0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46,
505
+ 0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D,
506
+ 0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A,
507
+ 0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678,
508
+ 0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF,
509
+ 0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0
510
+ );
511
+
512
+ for ($i = 0; $i < 256; $i++) {
513
+ $t2[$i << 8] = (($t3[$i] << 8) & 0xFFFFFF00) | (($t3[$i] >> 24) & 0x000000FF);
514
+ $t1[$i << 16] = (($t3[$i] << 16) & 0xFFFF0000) | (($t3[$i] >> 16) & 0x0000FFFF);
515
+ $t0[$i << 24] = (($t3[$i] << 24) & 0xFF000000) | (($t3[$i] >> 8) & 0x00FFFFFF);
516
+
517
+ $dt2[$i << 8] = (($this->dt3[$i] << 8) & 0xFFFFFF00) | (($dt3[$i] >> 24) & 0x000000FF);
518
+ $dt1[$i << 16] = (($this->dt3[$i] << 16) & 0xFFFF0000) | (($dt3[$i] >> 16) & 0x0000FFFF);
519
+ $dt0[$i << 24] = (($this->dt3[$i] << 24) & 0xFF000000) | (($dt3[$i] >> 8) & 0x00FFFFFF);
520
+ }
521
+ }
522
+
523
+ /**
524
+ * Sets the key.
525
+ *
526
+ * Keys can be of any length. Rijndael, itself, requires the use of a key that's between 128-bits and 256-bits long and
527
+ * whose length is a multiple of 32. If the key is less than 256-bits and the key length isn't set, we round the length
528
+ * up to the closest valid key length, padding $key with null bytes. If the key is more than 256-bits, we trim the
529
+ * excess bits.
530
+ *
531
+ * If the key is not explicitly set, it'll be assumed to be all null bytes.
532
+ *
533
+ * @access public
534
+ * @param String $key
535
+ */
536
+ function setKey($key)
537
+ {
538
+ $this->key = $key;
539
+ $this->changed = true;
540
+ }
541
+
542
+ /**
543
+ * Sets the initialization vector. (optional)
544
+ *
545
+ * SetIV is not required when CRYPT_RIJNDAEL_MODE_ECB is being used. If not explictly set, it'll be assumed
546
+ * to be all zero's.
547
+ *
548
+ * @access public
549
+ * @param String $iv
550
+ */
551
+ function setIV($iv)
552
+ {
553
+ $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, $this->block_size), $this->block_size, chr(0));
554
+ }
555
+
556
+ /**
557
+ * Sets the key length
558
+ *
559
+ * Valid key lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to
560
+ * 128. If the length is greater then 128 and invalid, it will be rounded down to the closest valid amount.
561
+ *
562
+ * @access public
563
+ * @param Integer $length
564
+ */
565
+ function setKeyLength($length)
566
+ {
567
+ $length >>= 5;
568
+ if ($length > 8) {
569
+ $length = 8;
570
+ } else if ($length < 4) {
571
+ $length = 4;
572
+ }
573
+ $this->Nk = $length;
574
+ $this->key_size = $length << 2;
575
+
576
+ $this->explicit_key_length = true;
577
+ $this->changed = true;
578
+ }
579
+
580
+ /**
581
+ * Sets the password.
582
+ *
583
+ * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
584
+ * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}:
585
+ * $hash, $salt, $method
586
+ * Set $dkLen by calling setKeyLength()
587
+ *
588
+ * @param String $password
589
+ * @param optional String $method
590
+ * @access public
591
+ */
592
+ function setPassword($password, $method = 'pbkdf2')
593
+ {
594
+ $key = '';
595
+
596
+ switch ($method) {
597
+ default: // 'pbkdf2'
598
+ list(, , $hash, $salt, $count) = func_get_args();
599
+ if (!isset($hash)) {
600
+ $hash = 'sha1';
601
+ }
602
+ // WPA and WPA use the SSID as the salt
603
+ if (!isset($salt)) {
604
+ $salt = 'phpseclib';
605
+ }
606
+ // RFC2898#section-4.2 uses 1,000 iterations by default
607
+ // WPA and WPA2 use 4,096.
608
+ if (!isset($count)) {
609
+ $count = 1000;
610
+ }
611
+
612
+ if (!class_exists('Crypt_Hash')) {
613
+ require_once('Crypt/Hash.php');
614
+ }
615
+
616
+ $i = 1;
617
+ while (strlen($key) < $this->key_size) { // $dkLen == $this->key_size
618
+ //$dk.= $this->_pbkdf($password, $salt, $count, $i++);
619
+ $hmac = new Crypt_Hash();
620
+ $hmac->setHash($hash);
621
+ $hmac->setKey($password);
622
+ $f = $u = $hmac->hash($salt . pack('N', $i++));
623
+ for ($j = 2; $j <= $count; $j++) {
624
+ $u = $hmac->hash($u);
625
+ $f^= $u;
626
+ }
627
+ $key.= $f;
628
+ }
629
+ }
630
+
631
+ $this->setKey(substr($key, 0, $this->key_size));
632
+ }
633
+
634
+ /**
635
+ * Sets the block length
636
+ *
637
+ * Valid block lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to
638
+ * 128. If the length is greater then 128 and invalid, it will be rounded down to the closest valid amount.
639
+ *
640
+ * @access public
641
+ * @param Integer $length
642
+ */
643
+ function setBlockLength($length)
644
+ {
645
+ $length >>= 5;
646
+ if ($length > 8) {
647
+ $length = 8;
648
+ } else if ($length < 4) {
649
+ $length = 4;
650
+ }
651
+ $this->Nb = $length;
652
+ $this->block_size = $length << 2;
653
+ $this->changed = true;
654
+ }
655
+
656
+ /**
657
+ * Generate CTR XOR encryption key
658
+ *
659
+ * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
660
+ * plaintext / ciphertext in CTR mode.
661
+ *
662
+ * @see Crypt_Rijndael::decrypt()
663
+ * @see Crypt_Rijndael::encrypt()
664
+ * @access public
665
+ * @param Integer $length
666
+ * @param String $iv
667
+ */
668
+ function _generate_xor($length, &$iv)
669
+ {
670
+ $xor = '';
671
+ $block_size = $this->block_size;
672
+ $num_blocks = floor(($length + ($block_size - 1)) / $block_size);
673
+ for ($i = 0; $i < $num_blocks; $i++) {
674
+ $xor.= $iv;
675
+ for ($j = 4; $j <= $block_size; $j+=4) {
676
+ $temp = substr($iv, -$j, 4);
677
+ switch ($temp) {
678
+ case "\xFF\xFF\xFF\xFF":
679
+ $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
680
+ break;
681
+ case "\x7F\xFF\xFF\xFF":
682
+ $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
683
+ break 2;
684
+ default:
685
+ extract(unpack('Ncount', $temp));
686
+ $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
687
+ break 2;
688
+ }
689
+ }
690
+ }
691
+
692
+ return $xor;
693
+ }
694
+
695
+ /**
696
+ * Encrypts a message.
697
+ *
698
+ * $plaintext will be padded with additional bytes such that it's length is a multiple of the block size. Other Rjindael
699
+ * implementations may or may not pad in the same manner. Other common approaches to padding and the reasons why it's
700
+ * necessary are discussed in the following
701
+ * URL:
702
+ *
703
+ * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
704
+ *
705
+ * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does.
706
+ * strlen($plaintext) will still need to be a multiple of 8, however, arbitrary values can be added to make it that
707
+ * length.
708
+ *
709
+ * @see Crypt_Rijndael::decrypt()
710
+ * @access public
711
+ * @param String $plaintext
712
+ */
713
+ function encrypt($plaintext)
714
+ {
715
+ $this->_setup();
716
+ if ($this->paddable) {
717
+ $plaintext = $this->_pad($plaintext);
718
+ }
719
+
720
+ $block_size = $this->block_size;
721
+ $buffer = &$this->enbuffer;
722
+ $continuousBuffer = $this->continuousBuffer;
723
+ $ciphertext = '';
724
+ switch ($this->mode) {
725
+ case CRYPT_RIJNDAEL_MODE_ECB:
726
+ for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
727
+ $ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $block_size));
728
+ }
729
+ break;
730
+ case CRYPT_RIJNDAEL_MODE_CBC:
731
+ $xor = $this->encryptIV;
732
+ for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
733
+ $block = substr($plaintext, $i, $block_size);
734
+ $block = $this->_encryptBlock($block ^ $xor);
735
+ $xor = $block;
736
+ $ciphertext.= $block;
737
+ }
738
+ if ($this->continuousBuffer) {
739
+ $this->encryptIV = $xor;
740
+ }
741
+ break;
742
+ case CRYPT_RIJNDAEL_MODE_CTR:
743
+ $xor = $this->encryptIV;
744
+ if (!empty($buffer['encrypted'])) {
745
+ for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
746
+ $block = substr($plaintext, $i, $block_size);
747
+ $buffer['encrypted'].= $this->_encryptBlock($this->_generate_xor($block_size, $xor));
748
+ $key = $this->_string_shift($buffer['encrypted'], $block_size);
749
+ $ciphertext.= $block ^ $key;
750
+ }
751
+ } else {
752
+ for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
753
+ $block = substr($plaintext, $i, $block_size);
754
+ $key = $this->_encryptBlock($this->_generate_xor($block_size, $xor));
755
+ $ciphertext.= $block ^ $key;
756
+ }
757
+ }
758
+ if ($this->continuousBuffer) {
759
+ $this->encryptIV = $xor;
760
+ if ($start = strlen($plaintext) % $block_size) {
761
+ $buffer['encrypted'] = substr($key, $start) . $buffer['encrypted'];
762
+ }
763
+ }
764
+ break;
765
+ case CRYPT_RIJNDAEL_MODE_CFB:
766
+ if (!empty($buffer['xor'])) {
767
+ $ciphertext = $plaintext ^ $buffer['xor'];
768
+ $iv = $buffer['encrypted'] . $ciphertext;
769
+ $start = strlen($ciphertext);
770
+ $buffer['encrypted'].= $ciphertext;
771
+ $buffer['xor'] = substr($buffer['xor'], strlen($ciphertext));
772
+ } else {
773
+ $ciphertext = '';
774
+ $iv = $this->encryptIV;
775
+ $start = 0;
776
+ }
777
+
778
+ for ($i = $start; $i < strlen($plaintext); $i+=$block_size) {
779
+ $block = substr($plaintext, $i, $block_size);
780
+ $xor = $this->_encryptBlock($iv);
781
+ $iv = $block ^ $xor;
782
+ if ($continuousBuffer && strlen($iv) != $block_size) {
783
+ $buffer = array(
784
+ 'encrypted' => $iv,
785
+ 'xor' => substr($xor, strlen($iv))
786
+ );
787
+ }
788
+ $ciphertext.= $iv;
789
+ }
790
+
791
+ if ($this->continuousBuffer) {
792
+ $this->encryptIV = $iv;
793
+ }
794
+ break;
795
+ case CRYPT_RIJNDAEL_MODE_OFB:
796
+ $xor = $this->encryptIV;
797
+ if (strlen($buffer)) {
798
+ for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
799
+ $xor = $this->_encryptBlock($xor);
800
+ $buffer.= $xor;
801
+ $key = $this->_string_shift($buffer, $block_size);
802
+ $ciphertext.= substr($plaintext, $i, $block_size) ^ $key;
803
+ }
804
+ } else {
805
+ for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
806
+ $xor = $this->_encryptBlock($xor);
807
+ $ciphertext.= substr($plaintext, $i, $block_size) ^ $xor;
808
+ }
809
+ $key = $xor;
810
+ }
811
+ if ($this->continuousBuffer) {
812
+ $this->encryptIV = $xor;
813
+ if ($start = strlen($plaintext) % $block_size) {
814
+ $buffer = substr($key, $start) . $buffer;
815
+ }
816
+ }
817
+ }
818
+
819
+ return $ciphertext;
820
+ }
821
+
822
+ /**
823
+ * Decrypts a message.
824
+ *
825
+ * If strlen($ciphertext) is not a multiple of the block size, null bytes will be added to the end of the string until
826
+ * it is.
827
+ *
828
+ * @see Crypt_Rijndael::encrypt()
829
+ * @access public
830
+ * @param String $ciphertext
831
+ */
832
+ function decrypt($ciphertext)
833
+ {
834
+ $this->_setup();
835
+
836
+ if ($this->paddable) {
837
+ // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
838
+ // "The data is padded with "\0" to make sure the length of the data is n * blocksize."
839
+ $ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($this->block_size - strlen($ciphertext) % $this->block_size) % $this->block_size, chr(0));
840
+ }
841
+
842
+ $block_size = $this->block_size;
843
+ $buffer = &$this->debuffer;
844
+ $continuousBuffer = $this->continuousBuffer;
845
+ $plaintext = '';
846
+ switch ($this->mode) {
847
+ case CRYPT_RIJNDAEL_MODE_ECB:
848
+ for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
849
+ $plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $block_size));
850
+ }
851
+ break;
852
+ case CRYPT_RIJNDAEL_MODE_CBC:
853
+ $xor = $this->decryptIV;
854
+ for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
855
+ $block = substr($ciphertext, $i, $block_size);
856
+ $plaintext.= $this->_decryptBlock($block) ^ $xor;
857
+ $xor = $block;
858
+ }
859
+ if ($this->continuousBuffer) {
860
+ $this->decryptIV = $xor;
861
+ }
862
+ break;
863
+ case CRYPT_RIJNDAEL_MODE_CTR:
864
+ $xor = $this->decryptIV;
865
+ if (!empty($buffer['ciphertext'])) {
866
+ for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
867
+ $block = substr($ciphertext, $i, $block_size);
868
+ $buffer['ciphertext'].= $this->_encryptBlock($this->_generate_xor($block_size, $xor));
869
+ $key = $this->_string_shift($buffer['ciphertext'], $block_size);
870
+ $plaintext.= $block ^ $key;
871
+ }
872
+ } else {
873
+ for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
874
+ $block = substr($ciphertext, $i, $block_size);
875
+ $key = $this->_encryptBlock($this->_generate_xor($block_size, $xor));
876
+ $plaintext.= $block ^ $key;
877
+ }
878
+ }
879
+ if ($this->continuousBuffer) {
880
+ $this->decryptIV = $xor;
881
+ if ($start = strlen($ciphertext) % $block_size) {
882
+ $buffer['ciphertext'] = substr($key, $start) . $buffer['encrypted'];
883
+ }
884
+ }
885
+ break;
886
+ case CRYPT_RIJNDAEL_MODE_CFB:
887
+ if (!empty($buffer['ciphertext'])) {
888
+ $plaintext = $ciphertext ^ substr($this->decryptIV, strlen($buffer['ciphertext']));
889
+ $buffer['ciphertext'].= substr($ciphertext, 0, strlen($plaintext));
890
+ if (strlen($buffer['ciphertext']) == $block_size) {
891
+ $xor = $this->_encryptBlock($buffer['ciphertext']);
892
+ $buffer['ciphertext'] = '';
893
+ }
894
+ $start = strlen($plaintext);
895
+ $block = $this->decryptIV;
896
+ } else {
897
+ $plaintext = '';
898
+ $xor = $this->_encryptBlock($this->decryptIV);
899
+ $start = 0;
900
+ }
901
+
902
+ for ($i = $start; $i < strlen($ciphertext); $i+=$block_size) {
903
+ $block = substr($ciphertext, $i, $block_size);
904
+ $plaintext.= $block ^ $xor;
905
+ if ($continuousBuffer && strlen($block) != $block_size) {
906
+ $buffer['ciphertext'].= $block;
907
+ $block = $xor;
908
+ } else if (strlen($block) == $block_size) {
909
+ $xor = $this->_encryptBlock($block);
910
+ }
911
+ }
912
+ if ($this->continuousBuffer) {
913
+ $this->decryptIV = $block;
914
+ }
915
+ break;
916
+ case CRYPT_RIJNDAEL_MODE_OFB:
917
+ $xor = $this->decryptIV;
918
+ if (strlen($buffer)) {
919
+ for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
920
+ $xor = $this->_encryptBlock($xor);
921
+ $buffer.= $xor;
922
+ $key = $this->_string_shift($buffer, $block_size);
923
+ $plaintext.= substr($ciphertext, $i, $block_size) ^ $key;
924
+ }
925
+ } else {
926
+ for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
927
+ $xor = $this->_encryptBlock($xor);
928
+ $plaintext.= substr($ciphertext, $i, $block_size) ^ $xor;
929
+ }
930
+ $key = $xor;
931
+ }
932
+ if ($this->continuousBuffer) {
933
+ $this->decryptIV = $xor;
934
+ if ($start = strlen($ciphertext) % $block_size) {
935
+ $buffer = substr($key, $start) . $buffer;
936
+ }
937
+ }
938
+ }
939
+
940
+ return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
941
+ }
942
+
943
+ /**
944
+ * Encrypts a block
945
+ *
946
+ * @access private
947
+ * @param String $in
948
+ * @return String
949
+ */
950
+ function _encryptBlock($in)
951
+ {
952
+ $state = array();
953
+ $words = unpack('N*word', $in);
954
+
955
+ $w = $this->w;
956
+ $t0 = $this->t0;
957
+ $t1 = $this->t1;
958
+ $t2 = $this->t2;
959
+ $t3 = $this->t3;
960
+ $Nb = $this->Nb;
961
+ $Nr = $this->Nr;
962
+ $c = $this->c;
963
+
964
+ // addRoundKey
965
+ $i = 0;
966
+ foreach ($words as $word) {
967
+ $state[] = $word ^ $w[0][$i++];
968
+ }
969
+
970
+ // fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components -
971
+ // subBytes, shiftRows, mixColumns, and addRoundKey. fips-197.pdf#page=30, "Implementation Suggestions Regarding
972
+ // Various Platforms" suggests that performs enhanced implementations are described in Rijndael-ammended.pdf.
973
+ // Rijndael-ammended.pdf#page=20, "Implementation aspects / 32-bit processor", discusses such an optimization.
974
+ // Unfortunately, the description given there is not quite correct. Per aes.spec.v316.pdf#page=19 [1],
975
+ // equation (7.4.7) is supposed to use addition instead of subtraction, so we'll do that here, as well.
976
+
977
+ // [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf
978
+ $temp = array();
979
+ for ($round = 1; $round < $Nr; $round++) {
980
+ $i = 0; // $c[0] == 0
981
+ $j = $c[1];
982
+ $k = $c[2];
983
+ $l = $c[3];
984
+
985
+ while ($i < $this->Nb) {
986
+ $temp[$i] = $t0[$state[$i] & 0xFF000000] ^
987
+ $t1[$state[$j] & 0x00FF0000] ^
988
+ $t2[$state[$k] & 0x0000FF00] ^
989
+ $t3[$state[$l] & 0x000000FF] ^
990
+ $w[$round][$i];
991
+ $i++;
992
+ $j = ($j + 1) % $Nb;
993
+ $k = ($k + 1) % $Nb;
994
+ $l = ($l + 1) % $Nb;
995
+ }
996
+
997
+ for ($i = 0; $i < $Nb; $i++) {
998
+ $state[$i] = $temp[$i];
999
+ }
1000
+ }
1001
+
1002
+ // subWord
1003
+ for ($i = 0; $i < $Nb; $i++) {
1004
+ $state[$i] = $this->_subWord($state[$i]);
1005
+ }
1006
+
1007
+ // shiftRows + addRoundKey
1008
+ $i = 0; // $c[0] == 0
1009
+ $j = $c[1];
1010
+ $k = $c[2];
1011
+ $l = $c[3];
1012
+ while ($i < $this->Nb) {
1013
+ $temp[$i] = ($state[$i] & 0xFF000000) ^
1014
+ ($state[$j] & 0x00FF0000) ^
1015
+ ($state[$k] & 0x0000FF00) ^
1016
+ ($state[$l] & 0x000000FF) ^
1017
+ $w[$Nr][$i];
1018
+ $i++;
1019
+ $j = ($j + 1) % $Nb;
1020
+ $k = ($k + 1) % $Nb;
1021
+ $l = ($l + 1) % $Nb;
1022
+ }
1023
+ $state = $temp;
1024
+
1025
+ array_unshift($state, 'N*');
1026
+
1027
+ return call_user_func_array('pack', $state);
1028
+ }
1029
+
1030
+ /**
1031
+ * Decrypts a block
1032
+ *
1033
+ * @access private
1034
+ * @param String $in
1035
+ * @return String
1036
+ */
1037
+ function _decryptBlock($in)
1038
+ {
1039
+ $state = array();
1040
+ $words = unpack('N*word', $in);
1041
+
1042
+ $num_states = count($state);
1043
+ $dw = $this->dw;
1044
+ $dt0 = $this->dt0;
1045
+ $dt1 = $this->dt1;
1046
+ $dt2 = $this->dt2;
1047
+ $dt3 = $this->dt3;
1048
+ $Nb = $this->Nb;
1049
+ $Nr = $this->Nr;
1050
+ $c = $this->c;
1051
+
1052
+ // addRoundKey
1053
+ $i = 0;
1054
+ foreach ($words as $word) {
1055
+ $state[] = $word ^ $dw[$Nr][$i++];
1056
+ }
1057
+
1058
+ $temp = array();
1059
+ for ($round = $Nr - 1; $round > 0; $round--) {
1060
+ $i = 0; // $c[0] == 0
1061
+ $j = $Nb - $c[1];
1062
+ $k = $Nb - $c[2];
1063
+ $l = $Nb - $c[3];
1064
+
1065
+ while ($i < $Nb) {
1066
+ $temp[$i] = $dt0[$state[$i] & 0xFF000000] ^
1067
+ $dt1[$state[$j] & 0x00FF0000] ^
1068
+ $dt2[$state[$k] & 0x0000FF00] ^
1069
+ $dt3[$state[$l] & 0x000000FF] ^
1070
+ $dw[$round][$i];
1071
+ $i++;
1072
+ $j = ($j + 1) % $Nb;
1073
+ $k = ($k + 1) % $Nb;
1074
+ $l = ($l + 1) % $Nb;
1075
+ }
1076
+
1077
+ for ($i = 0; $i < $Nb; $i++) {
1078
+ $state[$i] = $temp[$i];
1079
+ }
1080
+ }
1081
+
1082
+ // invShiftRows + invSubWord + addRoundKey
1083
+ $i = 0; // $c[0] == 0
1084
+ $j = $Nb - $c[1];
1085
+ $k = $Nb - $c[2];
1086
+ $l = $Nb - $c[3];
1087
+
1088
+ while ($i < $Nb) {
1089
+ $temp[$i] = $dw[0][$i] ^
1090
+ $this->_invSubWord(($state[$i] & 0xFF000000) |
1091
+ ($state[$j] & 0x00FF0000) |
1092
+ ($state[$k] & 0x0000FF00) |
1093
+ ($state[$l] & 0x000000FF));
1094
+ $i++;
1095
+ $j = ($j + 1) % $Nb;
1096
+ $k = ($k + 1) % $Nb;
1097
+ $l = ($l + 1) % $Nb;
1098
+ }
1099
+
1100
+ $state = $temp;
1101
+
1102
+ array_unshift($state, 'N*');
1103
+
1104
+ return call_user_func_array('pack', $state);
1105
+ }
1106
+
1107
+ /**
1108
+ * Setup Rijndael
1109
+ *
1110
+ * Validates all the variables and calculates $Nr - the number of rounds that need to be performed - and $w - the key
1111
+ * key schedule.
1112
+ *
1113
+ * @access private
1114
+ */
1115
+ function _setup()
1116
+ {
1117
+ // Each number in $rcon is equal to the previous number multiplied by two in Rijndael's finite field.
1118
+ // See http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse
1119
+ static $rcon = array(0,
1120
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
1121
+ 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
1122
+ 0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000,
1123
+ 0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000,
1124
+ 0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000,
1125
+ 0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000
1126
+ );
1127
+
1128
+ if (!$this->changed) {
1129
+ return;
1130
+ }
1131
+
1132
+ if (!$this->explicit_key_length) {
1133
+ // we do >> 2, here, and not >> 5, as we do above, since strlen($this->key) tells us the number of bytes - not bits
1134
+ $length = strlen($this->key) >> 2;
1135
+ if ($length > 8) {
1136
+ $length = 8;
1137
+ } else if ($length < 4) {
1138
+ $length = 4;
1139
+ }
1140
+ $this->Nk = $length;
1141
+ $this->key_size = $length << 2;
1142
+ }
1143
+
1144
+ $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, chr(0));
1145
+ $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, chr(0));
1146
+
1147
+ // see Rijndael-ammended.pdf#page=44
1148
+ $this->Nr = max($this->Nk, $this->Nb) + 6;
1149
+
1150
+ // shift offsets for Nb = 5, 7 are defined in Rijndael-ammended.pdf#page=44,
1151
+ // "Table 8: Shift offsets in Shiftrow for the alternative block lengths"
1152
+ // shift offsets for Nb = 4, 6, 8 are defined in Rijndael-ammended.pdf#page=14,
1153
+ // "Table 2: Shift offsets for different block lengths"
1154
+ switch ($this->Nb) {
1155
+ case 4:
1156
+ case 5:
1157
+ case 6:
1158
+ $this->c = array(0, 1, 2, 3);
1159
+ break;
1160
+ case 7:
1161
+ $this->c = array(0, 1, 2, 4);
1162
+ break;
1163
+ case 8:
1164
+ $this->c = array(0, 1, 3, 4);
1165
+ }
1166
+
1167
+ $key = $this->key;
1168
+
1169
+ $w = array_values(unpack('N*words', $key));
1170
+
1171
+ $length = $this->Nb * ($this->Nr + 1);
1172
+ for ($i = $this->Nk; $i < $length; $i++) {
1173
+ $temp = $w[$i - 1];
1174
+ if ($i % $this->Nk == 0) {
1175
+ // according to <http://php.net/language.types.integer>, "the size of an integer is platform-dependent".
1176
+ // on a 32-bit machine, it's 32-bits, and on a 64-bit machine, it's 64-bits. on a 32-bit machine,
1177
+ // 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit machine, it equals 0xFFFFFFFF00. as such, doing 'and'
1178
+ // with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is.
1179
+ $temp = (($temp << 8) & 0xFFFFFF00) | (($temp >> 24) & 0x000000FF); // rotWord
1180
+ $temp = $this->_subWord($temp) ^ $rcon[$i / $this->Nk];
1181
+ } else if ($this->Nk > 6 && $i % $this->Nk == 4) {
1182
+ $temp = $this->_subWord($temp);
1183
+ }
1184
+ $w[$i] = $w[$i - $this->Nk] ^ $temp;
1185
+ }
1186
+
1187
+ // convert the key schedule from a vector of $Nb * ($Nr + 1) length to a matrix with $Nr + 1 rows and $Nb columns
1188
+ // and generate the inverse key schedule. more specifically,
1189
+ // according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=23> (section 5.3.3),
1190
+ // "The key expansion for the Inverse Cipher is defined as follows:
1191
+ // 1. Apply the Key Expansion.
1192
+ // 2. Apply InvMixColumn to all Round Keys except the first and the last one."
1193
+ // also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher"
1194
+ $temp = array();
1195
+ for ($i = $row = $col = 0; $i < $length; $i++, $col++) {
1196
+ if ($col == $this->Nb) {
1197
+ if ($row == 0) {
1198
+ $this->dw[0] = $this->w[0];
1199
+ } else {
1200
+ // subWord + invMixColumn + invSubWord = invMixColumn
1201
+ $j = 0;
1202
+ while ($j < $this->Nb) {
1203
+ $dw = $this->_subWord($this->w[$row][$j]);
1204
+ $temp[$j] = $this->dt0[$dw & 0xFF000000] ^
1205
+ $this->dt1[$dw & 0x00FF0000] ^
1206
+ $this->dt2[$dw & 0x0000FF00] ^
1207
+ $this->dt3[$dw & 0x000000FF];
1208
+ $j++;
1209
+ }
1210
+ $this->dw[$row] = $temp;
1211
+ }
1212
+
1213
+ $col = 0;
1214
+ $row++;
1215
+ }
1216
+ $this->w[$row][$col] = $w[$i];
1217
+ }
1218
+
1219
+ $this->dw[$row] = $this->w[$row];
1220
+
1221
+ $this->changed = false;
1222
+ }
1223
+
1224
+ /**
1225
+ * Performs S-Box substitutions
1226
+ *
1227
+ * @access private
1228
+ */
1229
+ function _subWord($word)
1230
+ {
1231
+ static $sbox0, $sbox1, $sbox2, $sbox3;
1232
+
1233
+ if (empty($sbox0)) {
1234
+ $sbox0 = array(
1235
+ 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
1236
+ 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
1237
+ 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
1238
+ 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
1239
+ 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
1240
+ 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
1241
+ 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
1242
+ 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
1243
+ 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
1244
+ 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
1245
+ 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
1246
+ 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
1247
+ 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
1248
+ 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
1249
+ 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
1250
+ 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
1251
+ );
1252
+
1253
+ $sbox1 = array();
1254
+ $sbox2 = array();
1255
+ $sbox3 = array();
1256
+
1257
+ for ($i = 0; $i < 256; $i++) {
1258
+ $sbox1[$i << 8] = $sbox0[$i] << 8;
1259
+ $sbox2[$i << 16] = $sbox0[$i] << 16;
1260
+ $sbox3[$i << 24] = $sbox0[$i] << 24;
1261
+ }
1262
+ }
1263
+
1264
+ return $sbox0[$word & 0x000000FF] |
1265
+ $sbox1[$word & 0x0000FF00] |
1266
+ $sbox2[$word & 0x00FF0000] |
1267
+ $sbox3[$word & 0xFF000000];
1268
+ }
1269
+
1270
+ /**
1271
+ * Performs inverse S-Box substitutions
1272
+ *
1273
+ * @access private
1274
+ */
1275
+ function _invSubWord($word)
1276
+ {
1277
+ static $sbox0, $sbox1, $sbox2, $sbox3;
1278
+
1279
+ if (empty($sbox0)) {
1280
+ $sbox0 = array(
1281
+ 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
1282
+ 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
1283
+ 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
1284
+ 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
1285
+ 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
1286
+ 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
1287
+ 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
1288
+ 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
1289
+ 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
1290
+ 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
1291
+ 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
1292
+ 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
1293
+ 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
1294
+ 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
1295
+ 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
1296
+ 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
1297
+ );
1298
+
1299
+ $sbox1 = array();
1300
+ $sbox2 = array();
1301
+ $sbox3 = array();
1302
+
1303
+ for ($i = 0; $i < 256; $i++) {
1304
+ $sbox1[$i << 8] = $sbox0[$i] << 8;
1305
+ $sbox2[$i << 16] = $sbox0[$i] << 16;
1306
+ $sbox3[$i << 24] = $sbox0[$i] << 24;
1307
+ }
1308
+ }
1309
+
1310
+ return $sbox0[$word & 0x000000FF] |
1311
+ $sbox1[$word & 0x0000FF00] |
1312
+ $sbox2[$word & 0x00FF0000] |
1313
+ $sbox3[$word & 0xFF000000];
1314
+ }
1315
+
1316
+ /**
1317
+ * Pad "packets".
1318
+ *
1319
+ * Rijndael works by encrypting between sixteen and thirty-two bytes at a time, provided that number is also a multiple
1320
+ * of four. If you ever need to encrypt or decrypt something that isn't of the proper length, it becomes necessary to
1321
+ * pad the input so that it is of the proper length.
1322
+ *
1323
+ * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH,
1324
+ * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping
1325
+ * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
1326
+ * transmitted separately)
1327
+ *
1328
+ * @see Crypt_Rijndael::disablePadding()
1329
+ * @access public
1330
+ */
1331
+ function enablePadding()
1332
+ {
1333
+ $this->padding = true;
1334
+ }
1335
+
1336
+ /**
1337
+ * Do not pad packets.
1338
+ *
1339
+ * @see Crypt_Rijndael::enablePadding()
1340
+ * @access public
1341
+ */
1342
+ function disablePadding()
1343
+ {
1344
+ $this->padding = false;
1345
+ }
1346
+
1347
+ /**
1348
+ * Pads a string
1349
+ *
1350
+ * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize.
1351
+ * $block_size - (strlen($text) % $block_size) bytes are added, each of which is equal to
1352
+ * chr($block_size - (strlen($text) % $block_size)
1353
+ *
1354
+ * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
1355
+ * and padding will, hence forth, be enabled.
1356
+ *
1357
+ * @see Crypt_Rijndael::_unpad()
1358
+ * @access private
1359
+ */
1360
+ function _pad($text)
1361
+ {
1362
+ $length = strlen($text);
1363
+
1364
+ if (!$this->padding) {
1365
+ if ($length % $this->block_size == 0) {
1366
+ return $text;
1367
+ } else {
1368
+ user_error("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size})", E_USER_NOTICE);
1369
+ $this->padding = true;
1370
+ }
1371
+ }
1372
+
1373
+ $pad = $this->block_size - ($length % $this->block_size);
1374
+
1375
+ return str_pad($text, $length + $pad, chr($pad));
1376
+ }
1377
+
1378
+ /**
1379
+ * Unpads a string.
1380
+ *
1381
+ * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
1382
+ * and false will be returned.
1383
+ *
1384
+ * @see Crypt_Rijndael::_pad()
1385
+ * @access private
1386
+ */
1387
+ function _unpad($text)
1388
+ {
1389
+ if (!$this->padding) {
1390
+ return $text;
1391
+ }
1392
+
1393
+ $length = ord($text[strlen($text) - 1]);
1394
+
1395
+ if (!$length || $length > $this->block_size) {
1396
+ return false;
1397
+ }
1398
+
1399
+ return substr($text, 0, -$length);
1400
+ }
1401
+
1402
+ /**
1403
+ * Treat consecutive "packets" as if they are a continuous buffer.
1404
+ *
1405
+ * Say you have a 32-byte plaintext $plaintext. Using the default behavior, the two following code snippets
1406
+ * will yield different outputs:
1407
+ *
1408
+ * <code>
1409
+ * echo $rijndael->encrypt(substr($plaintext, 0, 16));
1410
+ * echo $rijndael->encrypt(substr($plaintext, 16, 16));
1411
+ * </code>
1412
+ * <code>
1413
+ * echo $rijndael->encrypt($plaintext);
1414
+ * </code>
1415
+ *
1416
+ * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
1417
+ * another, as demonstrated with the following:
1418
+ *
1419
+ * <code>
1420
+ * $rijndael->encrypt(substr($plaintext, 0, 16));
1421
+ * echo $rijndael->decrypt($des->encrypt(substr($plaintext, 16, 16)));
1422
+ * </code>
1423
+ * <code>
1424
+ * echo $rijndael->decrypt($des->encrypt(substr($plaintext, 16, 16)));
1425
+ * </code>
1426
+ *
1427
+ * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
1428
+ * outputs. The reason is due to the fact that the initialization vector's change after every encryption /
1429
+ * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
1430
+ *
1431
+ * Put another way, when the continuous buffer is enabled, the state of the Crypt_Rijndael() object changes after each
1432
+ * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
1433
+ * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
1434
+ * however, they are also less intuitive and more likely to cause you problems.
1435
+ *
1436
+ * @see Crypt_Rijndael::disableContinuousBuffer()
1437
+ * @access public
1438
+ */
1439
+ function enableContinuousBuffer()
1440
+ {
1441
+ $this->continuousBuffer = true;
1442
+ }
1443
+
1444
+ /**
1445
+ * Treat consecutive packets as if they are a discontinuous buffer.
1446
+ *
1447
+ * The default behavior.
1448
+ *
1449
+ * @see Crypt_Rijndael::enableContinuousBuffer()
1450
+ * @access public
1451
+ */
1452
+ function disableContinuousBuffer()
1453
+ {
1454
+ $this->continuousBuffer = false;
1455
+ $this->encryptIV = $this->iv;
1456
+ $this->decryptIV = $this->iv;
1457
+ }
1458
+
1459
+ /**
1460
+ * String Shift
1461
+ *
1462
+ * Inspired by array_shift
1463
+ *
1464
+ * @param String $string
1465
+ * @param optional Integer $index
1466
+ * @return String
1467
+ * @access private
1468
+ */
1469
+ function _string_shift(&$string, $index = 1)
1470
+ {
1471
+ $substr = substr($string, 0, $index);
1472
+ $string = substr($string, $index);
1473
+ return $substr;
1474
+ }
1475
+ }
1476
+
1477
+ // vim: ts=4:sw=4:et:
1478
+ // vim6: fdl=1:
phpseclib/Crypt/TripleDES.php ADDED
@@ -0,0 +1,1061 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP implementation of Triple DES.
6
+ *
7
+ * Uses mcrypt, if available, and an internal implementation, otherwise. Operates in the EDE3 mode (encrypt-decrypt-encrypt).
8
+ *
9
+ * PHP versions 4 and 5
10
+ *
11
+ * Here's a short example of how to use this library:
12
+ * <code>
13
+ * <?php
14
+ * include('Crypt/TripleDES.php');
15
+ *
16
+ * $des = new Crypt_TripleDES();
17
+ *
18
+ * $des->setKey('abcdefghijklmnopqrstuvwx');
19
+ *
20
+ * $size = 10 * 1024;
21
+ * $plaintext = '';
22
+ * for ($i = 0; $i < $size; $i++) {
23
+ * $plaintext.= 'a';
24
+ * }
25
+ *
26
+ * echo $des->decrypt($des->encrypt($plaintext));
27
+ * ?>
28
+ * </code>
29
+ *
30
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
31
+ * of this software and associated documentation files (the "Software"), to deal
32
+ * in the Software without restriction, including without limitation the rights
33
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
34
+ * copies of the Software, and to permit persons to whom the Software is
35
+ * furnished to do so, subject to the following conditions:
36
+ *
37
+ * The above copyright notice and this permission notice shall be included in
38
+ * all copies or substantial portions of the Software.
39
+ *
40
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
43
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
44
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
45
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
46
+ * THE SOFTWARE.
47
+ *
48
+ * @category Crypt
49
+ * @package Crypt_TripleDES
50
+ * @author Jim Wigginton <terrafrost@php.net>
51
+ * @copyright MMVII Jim Wigginton
52
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
53
+ * @version $Id: TripleDES.php,v 1.13 2010/02/26 03:40:25 terrafrost Exp $
54
+ * @link http://phpseclib.sourceforge.net
55
+ */
56
+
57
+ /**
58
+ * Include Crypt_DES
59
+ */
60
+ require_once('DES.php');
61
+
62
+ /**
63
+ * Encrypt / decrypt using inner chaining
64
+ *
65
+ * Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (CRYPT_DES_MODE_CBC3).
66
+ */
67
+ define('CRYPT_DES_MODE_3CBC', -2);
68
+
69
+ /**
70
+ * Encrypt / decrypt using outer chaining
71
+ *
72
+ * Outer chaining is used by SSH-2 and when the mode is set to CRYPT_DES_MODE_CBC.
73
+ */
74
+ define('CRYPT_DES_MODE_CBC3', CRYPT_DES_MODE_CBC);
75
+
76
+ /**
77
+ * Pure-PHP implementation of Triple DES.
78
+ *
79
+ * @author Jim Wigginton <terrafrost@php.net>
80
+ * @version 0.1.0
81
+ * @access public
82
+ * @package Crypt_TerraDES
83
+ */
84
+ class Crypt_TripleDES {
85
+ /**
86
+ * The Three Keys
87
+ *
88
+ * @see Crypt_TripleDES::setKey()
89
+ * @var String
90
+ * @access private
91
+ */
92
+ var $key = "\0\0\0\0\0\0\0\0";
93
+
94
+ /**
95
+ * The Encryption Mode
96
+ *
97
+ * @see Crypt_TripleDES::Crypt_TripleDES()
98
+ * @var Integer
99
+ * @access private
100
+ */
101
+ var $mode = CRYPT_DES_MODE_CBC;
102
+
103
+ /**
104
+ * Continuous Buffer status
105
+ *
106
+ * @see Crypt_TripleDES::enableContinuousBuffer()
107
+ * @var Boolean
108
+ * @access private
109
+ */
110
+ var $continuousBuffer = false;
111
+
112
+ /**
113
+ * Padding status
114
+ *
115
+ * @see Crypt_TripleDES::enablePadding()
116
+ * @var Boolean
117
+ * @access private
118
+ */
119
+ var $padding = true;
120
+
121
+ /**
122
+ * The Initialization Vector
123
+ *
124
+ * @see Crypt_TripleDES::setIV()
125
+ * @var String
126
+ * @access private
127
+ */
128
+ var $iv = "\0\0\0\0\0\0\0\0";
129
+
130
+ /**
131
+ * A "sliding" Initialization Vector
132
+ *
133
+ * @see Crypt_TripleDES::enableContinuousBuffer()
134
+ * @var String
135
+ * @access private
136
+ */
137
+ var $encryptIV = "\0\0\0\0\0\0\0\0";
138
+
139
+ /**
140
+ * A "sliding" Initialization Vector
141
+ *
142
+ * @see Crypt_TripleDES::enableContinuousBuffer()
143
+ * @var String
144
+ * @access private
145
+ */
146
+ var $decryptIV = "\0\0\0\0\0\0\0\0";
147
+
148
+ /**
149
+ * The Crypt_DES objects
150
+ *
151
+ * @var Array
152
+ * @access private
153
+ */
154
+ var $des;
155
+
156
+ /**
157
+ * mcrypt resource for encryption
158
+ *
159
+ * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
160
+ * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
161
+ *
162
+ * @see Crypt_TripleDES::encrypt()
163
+ * @var String
164
+ * @access private
165
+ */
166
+ var $enmcrypt;
167
+
168
+ /**
169
+ * mcrypt resource for decryption
170
+ *
171
+ * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
172
+ * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
173
+ *
174
+ * @see Crypt_TripleDES::decrypt()
175
+ * @var String
176
+ * @access private
177
+ */
178
+ var $demcrypt;
179
+
180
+ /**
181
+ * Does the enmcrypt resource need to be (re)initialized?
182
+ *
183
+ * @see Crypt_TripleDES::setKey()
184
+ * @see Crypt_TripleDES::setIV()
185
+ * @var Boolean
186
+ * @access private
187
+ */
188
+ var $enchanged = true;
189
+
190
+ /**
191
+ * Does the demcrypt resource need to be (re)initialized?
192
+ *
193
+ * @see Crypt_TripleDES::setKey()
194
+ * @see Crypt_TripleDES::setIV()
195
+ * @var Boolean
196
+ * @access private
197
+ */
198
+ var $dechanged = true;
199
+
200
+ /**
201
+ * Is the mode one that is paddable?
202
+ *
203
+ * @see Crypt_TripleDES::Crypt_TripleDES()
204
+ * @var Boolean
205
+ * @access private
206
+ */
207
+ var $paddable = false;
208
+
209
+ /**
210
+ * Encryption buffer for CTR, OFB and CFB modes
211
+ *
212
+ * @see Crypt_TripleDES::encrypt()
213
+ * @var String
214
+ * @access private
215
+ */
216
+ var $enbuffer = '';
217
+
218
+ /**
219
+ * Decryption buffer for CTR, OFB and CFB modes
220
+ *
221
+ * @see Crypt_TripleDES::decrypt()
222
+ * @var String
223
+ * @access private
224
+ */
225
+ var $debuffer = '';
226
+
227
+ /**
228
+ * mcrypt resource for CFB mode
229
+ *
230
+ * @see Crypt_TripleDES::encrypt()
231
+ * @see Crypt_TripleDES::decrypt()
232
+ * @var String
233
+ * @access private
234
+ */
235
+ var $ecb;
236
+
237
+ /**
238
+ * Default Constructor.
239
+ *
240
+ * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
241
+ * CRYPT_DES_MODE_ECB or CRYPT_DES_MODE_CBC. If not explictly set, CRYPT_DES_MODE_CBC will be used.
242
+ *
243
+ * @param optional Integer $mode
244
+ * @return Crypt_TripleDES
245
+ * @access public
246
+ */
247
+ function Crypt_TripleDES($mode = CRYPT_DES_MODE_CBC)
248
+ {
249
+ if ( !defined('CRYPT_DES_MODE') ) {
250
+ switch (true) {
251
+ case extension_loaded('mcrypt'):
252
+ // i'd check to see if des was supported, by doing in_array('des', mcrypt_list_algorithms('')),
253
+ // but since that can be changed after the object has been created, there doesn't seem to be
254
+ // a lot of point...
255
+ define('CRYPT_DES_MODE', CRYPT_DES_MODE_MCRYPT);
256
+ break;
257
+ default:
258
+ define('CRYPT_DES_MODE', CRYPT_DES_MODE_INTERNAL);
259
+ }
260
+ }
261
+
262
+ if ( $mode == CRYPT_DES_MODE_3CBC ) {
263
+ $this->mode = CRYPT_DES_MODE_3CBC;
264
+ $this->des = array(
265
+ new Crypt_DES(CRYPT_DES_MODE_CBC),
266
+ new Crypt_DES(CRYPT_DES_MODE_CBC),
267
+ new Crypt_DES(CRYPT_DES_MODE_CBC)
268
+ );
269
+
270
+ // we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects
271
+ $this->des[0]->disablePadding();
272
+ $this->des[1]->disablePadding();
273
+ $this->des[2]->disablePadding();
274
+
275
+ return;
276
+ }
277
+
278
+ switch ( CRYPT_DES_MODE ) {
279
+ case CRYPT_DES_MODE_MCRYPT:
280
+ switch ($mode) {
281
+ case CRYPT_DES_MODE_ECB:
282
+ $this->paddable = true;
283
+ $this->mode = MCRYPT_MODE_ECB;
284
+ break;
285
+ case CRYPT_DES_MODE_CTR:
286
+ $this->mode = 'ctr';
287
+ break;
288
+ case CRYPT_DES_MODE_CFB:
289
+ $this->mode = 'ncfb';
290
+ break;
291
+ case CRYPT_DES_MODE_OFB:
292
+ $this->mode = MCRYPT_MODE_NOFB;
293
+ break;
294
+ case CRYPT_DES_MODE_CBC:
295
+ default:
296
+ $this->paddable = true;
297
+ $this->mode = MCRYPT_MODE_CBC;
298
+ }
299
+
300
+ break;
301
+ default:
302
+ $this->des = array(
303
+ new Crypt_DES(CRYPT_DES_MODE_ECB),
304
+ new Crypt_DES(CRYPT_DES_MODE_ECB),
305
+ new Crypt_DES(CRYPT_DES_MODE_ECB)
306
+ );
307
+
308
+ // we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects
309
+ $this->des[0]->disablePadding();
310
+ $this->des[1]->disablePadding();
311
+ $this->des[2]->disablePadding();
312
+
313
+ switch ($mode) {
314
+ case CRYPT_DES_MODE_ECB:
315
+ case CRYPT_DES_MODE_CBC:
316
+ $this->paddable = true;
317
+ $this->mode = $mode;
318
+ break;
319
+ case CRYPT_DES_MODE_CTR:
320
+ case CRYPT_DES_MODE_CFB:
321
+ case CRYPT_DES_MODE_OFB:
322
+ $this->mode = $mode;
323
+ break;
324
+ default:
325
+ $this->paddable = true;
326
+ $this->mode = CRYPT_DES_MODE_CBC;
327
+ }
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Sets the key.
333
+ *
334
+ * Keys can be of any length. Triple DES, itself, can use 128-bit (eg. strlen($key) == 16) or
335
+ * 192-bit (eg. strlen($key) == 24) keys. This function pads and truncates $key as appropriate.
336
+ *
337
+ * DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
338
+ *
339
+ * If the key is not explicitly set, it'll be assumed to be all zero's.
340
+ *
341
+ * @access public
342
+ * @param String $key
343
+ */
344
+ function setKey($key)
345
+ {
346
+ $length = strlen($key);
347
+ if ($length > 8) {
348
+ $key = str_pad($key, 24, chr(0));
349
+ // if $key is between 64 and 128-bits, use the first 64-bits as the last, per this:
350
+ // http://php.net/function.mcrypt-encrypt#47973
351
+ //$key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24);
352
+ } else {
353
+ $key = str_pad($key, 8, chr(0));
354
+ }
355
+ $this->key = $key;
356
+ switch (true) {
357
+ case CRYPT_DES_MODE == CRYPT_DES_MODE_INTERNAL:
358
+ case $this->mode == CRYPT_DES_MODE_3CBC:
359
+ $this->des[0]->setKey(substr($key, 0, 8));
360
+ $this->des[1]->setKey(substr($key, 8, 8));
361
+ $this->des[2]->setKey(substr($key, 16, 8));
362
+ }
363
+ $this->enchanged = $this->dechanged = true;
364
+ }
365
+
366
+ /**
367
+ * Sets the password.
368
+ *
369
+ * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
370
+ * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}:
371
+ * $hash, $salt, $method
372
+ *
373
+ * @param String $password
374
+ * @param optional String $method
375
+ * @access public
376
+ */
377
+ function setPassword($password, $method = 'pbkdf2')
378
+ {
379
+ $key = '';
380
+
381
+ switch ($method) {
382
+ default: // 'pbkdf2'
383
+ list(, , $hash, $salt, $count) = func_get_args();
384
+ if (!isset($hash)) {
385
+ $hash = 'sha1';
386
+ }
387
+ // WPA and WPA use the SSID as the salt
388
+ if (!isset($salt)) {
389
+ $salt = 'phpseclib';
390
+ }
391
+ // RFC2898#section-4.2 uses 1,000 iterations by default
392
+ // WPA and WPA2 use 4,096.
393
+ if (!isset($count)) {
394
+ $count = 1000;
395
+ }
396
+
397
+ if (!class_exists('Crypt_Hash')) {
398
+ require_once('Crypt/Hash.php');
399
+ }
400
+
401
+ $i = 1;
402
+ while (strlen($key) < 24) { // $dkLen == 24
403
+ $hmac = new Crypt_Hash();
404
+ $hmac->setHash($hash);
405
+ $hmac->setKey($password);
406
+ $f = $u = $hmac->hash($salt . pack('N', $i++));
407
+ for ($j = 2; $j <= $count; $j++) {
408
+ $u = $hmac->hash($u);
409
+ $f^= $u;
410
+ }
411
+ $key.= $f;
412
+ }
413
+ }
414
+
415
+ $this->setKey($key);
416
+ }
417
+
418
+ /**
419
+ * Sets the initialization vector. (optional)
420
+ *
421
+ * SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explictly set, it'll be assumed
422
+ * to be all zero's.
423
+ *
424
+ * @access public
425
+ * @param String $iv
426
+ */
427
+ function setIV($iv)
428
+ {
429
+ $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0));
430
+ if ($this->mode == CRYPT_DES_MODE_3CBC) {
431
+ $this->des[0]->setIV($iv);
432
+ $this->des[1]->setIV($iv);
433
+ $this->des[2]->setIV($iv);
434
+ }
435
+ $this->enchanged = $this->dechanged = true;
436
+ }
437
+
438
+ /**
439
+ * Generate CTR XOR encryption key
440
+ *
441
+ * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
442
+ * plaintext / ciphertext in CTR mode.
443
+ *
444
+ * @see Crypt_TripleDES::decrypt()
445
+ * @see Crypt_TripleDES::encrypt()
446
+ * @access private
447
+ * @param Integer $length
448
+ * @param String $iv
449
+ */
450
+ function _generate_xor($length, &$iv)
451
+ {
452
+ $xor = '';
453
+ $num_blocks = ($length + 7) >> 3;
454
+ for ($i = 0; $i < $num_blocks; $i++) {
455
+ $xor.= $iv;
456
+ for ($j = 4; $j <= 8; $j+=4) {
457
+ $temp = substr($iv, -$j, 4);
458
+ switch ($temp) {
459
+ case "\xFF\xFF\xFF\xFF":
460
+ $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
461
+ break;
462
+ case "\x7F\xFF\xFF\xFF":
463
+ $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
464
+ break 2;
465
+ default:
466
+ extract(unpack('Ncount', $temp));
467
+ $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
468
+ break 2;
469
+ }
470
+ }
471
+ }
472
+
473
+ return $xor;
474
+ }
475
+
476
+ /**
477
+ * Encrypts a message.
478
+ *
479
+ * @access public
480
+ * @param String $plaintext
481
+ */
482
+ function encrypt($plaintext)
483
+ {
484
+ if ($this->paddable) {
485
+ $plaintext = $this->_pad($plaintext);
486
+ }
487
+
488
+ // if the key is smaller then 8, do what we'd normally do
489
+ if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) {
490
+ $ciphertext = $this->des[2]->encrypt($this->des[1]->decrypt($this->des[0]->encrypt($plaintext)));
491
+
492
+ return $ciphertext;
493
+ }
494
+
495
+ if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
496
+ if ($this->enchanged) {
497
+ if (!isset($this->enmcrypt)) {
498
+ $this->enmcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
499
+ }
500
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
501
+ if ($this->mode != 'ncfb') {
502
+ $this->enchanged = false;
503
+ }
504
+ }
505
+
506
+ if ($this->mode != 'ncfb') {
507
+ $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
508
+ } else {
509
+ if ($this->enchanged) {
510
+ $this->ecb = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, '');
511
+ mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0");
512
+ $this->enchanged = false;
513
+ }
514
+
515
+ if (strlen($this->enbuffer)) {
516
+ $ciphertext = $plaintext ^ substr($this->encryptIV, strlen($this->enbuffer));
517
+ $this->enbuffer.= $ciphertext;
518
+ if (strlen($this->enbuffer) == 8) {
519
+ $this->encryptIV = $this->enbuffer;
520
+ $this->enbuffer = '';
521
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
522
+ }
523
+ $plaintext = substr($plaintext, strlen($ciphertext));
524
+ } else {
525
+ $ciphertext = '';
526
+ }
527
+
528
+ $last_pos = strlen($plaintext) & 0xFFFFFFF8;
529
+ $ciphertext.= $last_pos ? mcrypt_generic($this->enmcrypt, substr($plaintext, 0, $last_pos)) : '';
530
+
531
+ if (strlen($plaintext) & 0x7) {
532
+ if (strlen($ciphertext)) {
533
+ $this->encryptIV = substr($ciphertext, -8);
534
+ }
535
+ $this->encryptIV = mcrypt_generic($this->ecb, $this->encryptIV);
536
+ $this->enbuffer = substr($plaintext, $last_pos) ^ $this->encryptIV;
537
+ $ciphertext.= $this->enbuffer;
538
+ }
539
+ }
540
+
541
+ if (!$this->continuousBuffer) {
542
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
543
+ }
544
+
545
+ return $ciphertext;
546
+ }
547
+
548
+ if (strlen($this->key) <= 8) {
549
+ $this->des[0]->mode = $this->mode;
550
+
551
+ return $this->des[0]->encrypt($plaintext);
552
+ }
553
+
554
+ $des = $this->des;
555
+
556
+ $buffer = &$this->enbuffer;
557
+ $continuousBuffer = $this->continuousBuffer;
558
+ $ciphertext = '';
559
+ switch ($this->mode) {
560
+ case CRYPT_DES_MODE_ECB:
561
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
562
+ $block = substr($plaintext, $i, 8);
563
+ // all of these _processBlock calls could, in theory, be put in a function - say Crypt_TripleDES::_ede_encrypt() or something.
564
+ // only problem with that: it would slow encryption and decryption down. $this->des would have to be called every time that
565
+ // function is called, instead of once for the whole string of text that's being encrypted, which would, in turn, make
566
+ // encryption and decryption take more time, per this:
567
+ //
568
+ // http://blog.libssh2.org/index.php?/archives/21-Compiled-Variables.html
569
+ $block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
570
+ $block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT);
571
+ $block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT);
572
+ $ciphertext.= $block;
573
+ }
574
+ break;
575
+ case CRYPT_DES_MODE_CBC:
576
+ $xor = $this->encryptIV;
577
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
578
+ $block = substr($plaintext, $i, 8) ^ $xor;
579
+ $block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
580
+ $block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT);
581
+ $block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT);
582
+ $xor = $block;
583
+ $ciphertext.= $block;
584
+ }
585
+ if ($this->continuousBuffer) {
586
+ $this->encryptIV = $xor;
587
+ }
588
+ break;
589
+ case CRYPT_DES_MODE_CTR:
590
+ $xor = $this->encryptIV;
591
+ if (strlen($buffer['encrypted'])) {
592
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
593
+ $block = substr($plaintext, $i, 8);
594
+ $key = $this->_generate_xor(8, $xor);
595
+ $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
596
+ $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
597
+ $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
598
+ $buffer['encrypted'].= $key;
599
+ $key = $this->_string_shift($buffer['encrypted'], 8);
600
+ $ciphertext.= $block ^ $key;
601
+ }
602
+ } else {
603
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
604
+ $block = substr($plaintext, $i, 8);
605
+ $key = $this->_generate_xor(8, $xor);
606
+ $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
607
+ $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
608
+ $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
609
+ $ciphertext.= $block ^ $key;
610
+ }
611
+ }
612
+ if ($this->continuousBuffer) {
613
+ $this->encryptIV = $xor;
614
+ if ($start = strlen($plaintext) & 7) {
615
+ $buffer['encrypted'] = substr($key, $start) . $buffer;
616
+ }
617
+ }
618
+ break;
619
+ case CRYPT_DES_MODE_CFB:
620
+ if (!empty($buffer['xor'])) {
621
+ $ciphertext = $plaintext ^ $buffer['xor'];
622
+ $iv = $buffer['encrypted'] . $ciphertext;
623
+ $start = strlen($ciphertext);
624
+ $buffer['encrypted'].= $ciphertext;
625
+ $buffer['xor'] = substr($buffer['xor'], strlen($ciphertext));
626
+ } else {
627
+ $ciphertext = '';
628
+ $iv = $this->encryptIV;
629
+ $start = 0;
630
+ }
631
+
632
+ for ($i = $start; $i < strlen($plaintext); $i+=8) {
633
+ $block = substr($plaintext, $i, 8);
634
+ $iv = $des[0]->_processBlock($iv, CRYPT_DES_ENCRYPT);
635
+ $iv = $des[1]->_processBlock($iv, CRYPT_DES_DECRYPT);
636
+ $xor= $des[2]->_processBlock($iv, CRYPT_DES_ENCRYPT);
637
+
638
+ $iv = $block ^ $xor;
639
+ if ($continuousBuffer && strlen($iv) != 8) {
640
+ $buffer = array(
641
+ 'encrypted' => $iv,
642
+ 'xor' => substr($xor, strlen($iv))
643
+ );
644
+ }
645
+ $ciphertext.= $iv;
646
+ }
647
+
648
+ if ($this->continuousBuffer) {
649
+ $this->encryptIV = $iv;
650
+ }
651
+ break;
652
+ case CRYPT_DES_MODE_OFB:
653
+ $xor = $this->encryptIV;
654
+ if (strlen($buffer)) {
655
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
656
+ $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);
657
+ $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
658
+ $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
659
+ $buffer.= $xor;
660
+ $key = $this->_string_shift($buffer, 8);
661
+ $ciphertext.= substr($plaintext, $i, 8) ^ $key;
662
+ }
663
+ } else {
664
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
665
+ $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);
666
+ $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
667
+ $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
668
+ $ciphertext.= substr($plaintext, $i, 8) ^ $xor;
669
+ }
670
+ $key = $xor;
671
+ }
672
+ if ($this->continuousBuffer) {
673
+ $this->encryptIV = $xor;
674
+ if ($start = strlen($plaintext) & 7) {
675
+ $buffer = substr($key, $start) . $buffer;
676
+ }
677
+ }
678
+ }
679
+
680
+ return $ciphertext;
681
+ }
682
+
683
+ /**
684
+ * Decrypts a message.
685
+ *
686
+ * @access public
687
+ * @param String $ciphertext
688
+ */
689
+ function decrypt($ciphertext)
690
+ {
691
+ if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) {
692
+ $plaintext = $this->des[0]->decrypt($this->des[1]->encrypt($this->des[2]->decrypt($ciphertext)));
693
+
694
+ return $this->_unpad($plaintext);
695
+ }
696
+
697
+ if ($this->paddable) {
698
+ // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
699
+ // "The data is padded with "\0" to make sure the length of the data is n * blocksize."
700
+ $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0));
701
+ }
702
+
703
+ if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
704
+ if ($this->dechanged) {
705
+ if (!isset($this->demcrypt)) {
706
+ $this->demcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
707
+ }
708
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
709
+ if ($this->mode != 'ncfb') {
710
+ $this->dechanged = false;
711
+ }
712
+ }
713
+
714
+ if ($this->mode != 'ncfb') {
715
+ $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
716
+ } else {
717
+ if ($this->dechanged) {
718
+ $this->ecb = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, '');
719
+ mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0");
720
+ $this->dechanged = false;
721
+ }
722
+
723
+ if (strlen($this->debuffer)) {
724
+ $plaintext = $ciphertext ^ substr($this->decryptIV, strlen($this->debuffer));
725
+
726
+ $this->debuffer.= substr($ciphertext, 0, strlen($plaintext));
727
+ if (strlen($this->debuffer) == 8) {
728
+ $this->decryptIV = $this->debuffer;
729
+ $this->debuffer = '';
730
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
731
+ }
732
+ $ciphertext = substr($ciphertext, strlen($plaintext));
733
+ } else {
734
+ $plaintext = '';
735
+ }
736
+
737
+ $last_pos = strlen($ciphertext) & 0xFFFFFFF8;
738
+ $plaintext.= $last_pos ? mdecrypt_generic($this->demcrypt, substr($ciphertext, 0, $last_pos)) : '';
739
+
740
+ if (strlen($ciphertext) & 0x7) {
741
+ if (strlen($plaintext)) {
742
+ $this->decryptIV = substr($ciphertext, $last_pos - 8, 8);
743
+ }
744
+ $this->decryptIV = mcrypt_generic($this->ecb, $this->decryptIV);
745
+ $this->debuffer = substr($ciphertext, $last_pos);
746
+ $plaintext.= $this->debuffer ^ $this->decryptIV;
747
+ }
748
+
749
+ return $plaintext;
750
+ }
751
+
752
+ if (!$this->continuousBuffer) {
753
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
754
+ }
755
+
756
+ return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
757
+ }
758
+
759
+ if (strlen($this->key) <= 8) {
760
+ $this->des[0]->mode = $this->mode;
761
+ $plaintext = $this->des[0]->decrypt($ciphertext);
762
+ return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
763
+ }
764
+
765
+ $des = $this->des;
766
+
767
+ $buffer = &$this->enbuffer;
768
+ $continuousBuffer = $this->continuousBuffer;
769
+ $plaintext = '';
770
+ switch ($this->mode) {
771
+ case CRYPT_DES_MODE_ECB:
772
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
773
+ $block = substr($ciphertext, $i, 8);
774
+ $block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT);
775
+ $block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT);
776
+ $block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT);
777
+ $plaintext.= $block;
778
+ }
779
+ break;
780
+ case CRYPT_DES_MODE_CBC:
781
+ $xor = $this->decryptIV;
782
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
783
+ $orig = $block = substr($ciphertext, $i, 8);
784
+ $block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT);
785
+ $block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT);
786
+ $block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT);
787
+ $plaintext.= $block ^ $xor;
788
+ $xor = $orig;
789
+ }
790
+ if ($this->continuousBuffer) {
791
+ $this->decryptIV = $xor;
792
+ }
793
+ break;
794
+ case CRYPT_DES_MODE_CTR:
795
+ $xor = $this->decryptIV;
796
+ if (strlen($buffer['ciphertext'])) {
797
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
798
+ $block = substr($ciphertext, $i, 8);
799
+ $key = $this->_generate_xor(8, $xor);
800
+ $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
801
+ $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
802
+ $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
803
+ $buffer['ciphertext'].= $key;
804
+ $key = $this->_string_shift($buffer['ciphertext'], 8);
805
+ $plaintext.= $block ^ $key;
806
+ }
807
+ } else {
808
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
809
+ $block = substr($ciphertext, $i, 8);
810
+ $key = $this->_generate_xor(8, $xor);
811
+ $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
812
+ $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
813
+ $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
814
+ $plaintext.= $block ^ $key;
815
+ }
816
+ }
817
+ if ($this->continuousBuffer) {
818
+ $this->decryptIV = $xor;
819
+ if ($start = strlen($plaintext) & 7) {
820
+ $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
821
+ }
822
+ }
823
+ break;
824
+ case CRYPT_DES_MODE_CFB:
825
+ if (!empty($buffer['ciphertext'])) {
826
+ $plaintext = $ciphertext ^ substr($this->decryptIV, strlen($buffer['ciphertext']));
827
+ $buffer['ciphertext'].= substr($ciphertext, 0, strlen($plaintext));
828
+ if (strlen($buffer['ciphertext']) == 8) {
829
+ $xor = $des[0]->_processBlock($buffer['ciphertext'], CRYPT_DES_ENCRYPT);
830
+ $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
831
+ $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
832
+ $buffer['ciphertext'] = '';
833
+ }
834
+ $start = strlen($plaintext);
835
+ $block = $this->decryptIV;
836
+ } else {
837
+ $plaintext = '';
838
+ $xor = $des[0]->_processBlock($this->decryptIV, CRYPT_DES_ENCRYPT);
839
+ $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
840
+ $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
841
+ $start = 0;
842
+ }
843
+
844
+ for ($i = $start; $i < strlen($ciphertext); $i+=8) {
845
+ $block = substr($ciphertext, $i, 8);
846
+ $plaintext.= $block ^ $xor;
847
+ if ($continuousBuffer && strlen($block) != 8) {
848
+ $buffer['ciphertext'].= $block;
849
+ $block = $xor;
850
+ } else if (strlen($block) == 8) {
851
+ $xor = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
852
+ $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
853
+ $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
854
+ }
855
+ }
856
+ if ($this->continuousBuffer) {
857
+ $this->decryptIV = $block;
858
+ }
859
+ break;
860
+ case CRYPT_DES_MODE_OFB:
861
+ $xor = $this->decryptIV;
862
+ if (strlen($buffer)) {
863
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
864
+ $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);
865
+ $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
866
+ $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
867
+ $buffer.= $xor;
868
+ $key = $this->_string_shift($buffer, 8);
869
+ $plaintext.= substr($ciphertext, $i, 8) ^ $key;
870
+ }
871
+ } else {
872
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
873
+ $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);
874
+ $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
875
+ $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
876
+ $plaintext.= substr($ciphertext, $i, 8) ^ $xor;
877
+ }
878
+ $key = $xor;
879
+ }
880
+ if ($this->continuousBuffer) {
881
+ $this->decryptIV = $xor;
882
+ if ($start = strlen($ciphertext) & 7) {
883
+ $buffer = substr($key, $start) . $buffer;
884
+ }
885
+ }
886
+ }
887
+
888
+ return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
889
+ }
890
+
891
+ /**
892
+ * Treat consecutive "packets" as if they are a continuous buffer.
893
+ *
894
+ * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
895
+ * will yield different outputs:
896
+ *
897
+ * <code>
898
+ * echo $des->encrypt(substr($plaintext, 0, 8));
899
+ * echo $des->encrypt(substr($plaintext, 8, 8));
900
+ * </code>
901
+ * <code>
902
+ * echo $des->encrypt($plaintext);
903
+ * </code>
904
+ *
905
+ * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
906
+ * another, as demonstrated with the following:
907
+ *
908
+ * <code>
909
+ * $des->encrypt(substr($plaintext, 0, 8));
910
+ * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
911
+ * </code>
912
+ * <code>
913
+ * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
914
+ * </code>
915
+ *
916
+ * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
917
+ * outputs. The reason is due to the fact that the initialization vector's change after every encryption /
918
+ * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
919
+ *
920
+ * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
921
+ * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
922
+ * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
923
+ * however, they are also less intuitive and more likely to cause you problems.
924
+ *
925
+ * @see Crypt_TripleDES::disableContinuousBuffer()
926
+ * @access public
927
+ */
928
+ function enableContinuousBuffer()
929
+ {
930
+ $this->continuousBuffer = true;
931
+ if ($this->mode == CRYPT_DES_MODE_3CBC) {
932
+ $this->des[0]->enableContinuousBuffer();
933
+ $this->des[1]->enableContinuousBuffer();
934
+ $this->des[2]->enableContinuousBuffer();
935
+ }
936
+ }
937
+
938
+ /**
939
+ * Treat consecutive packets as if they are a discontinuous buffer.
940
+ *
941
+ * The default behavior.
942
+ *
943
+ * @see Crypt_TripleDES::enableContinuousBuffer()
944
+ * @access public
945
+ */
946
+ function disableContinuousBuffer()
947
+ {
948
+ $this->continuousBuffer = false;
949
+ $this->encryptIV = $this->iv;
950
+ $this->decryptIV = $this->iv;
951
+
952
+ if ($this->mode == CRYPT_DES_MODE_3CBC) {
953
+ $this->des[0]->disableContinuousBuffer();
954
+ $this->des[1]->disableContinuousBuffer();
955
+ $this->des[2]->disableContinuousBuffer();
956
+ }
957
+ }
958
+
959
+ /**
960
+ * Pad "packets".
961
+ *
962
+ * DES works by encrypting eight bytes at a time. If you ever need to encrypt or decrypt something that's not
963
+ * a multiple of eight, it becomes necessary to pad the input so that it's length is a multiple of eight.
964
+ *
965
+ * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH1,
966
+ * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping
967
+ * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
968
+ * transmitted separately)
969
+ *
970
+ * @see Crypt_TripleDES::disablePadding()
971
+ * @access public
972
+ */
973
+ function enablePadding()
974
+ {
975
+ $this->padding = true;
976
+ }
977
+
978
+ /**
979
+ * Do not pad packets.
980
+ *
981
+ * @see Crypt_TripleDES::enablePadding()
982
+ * @access public
983
+ */
984
+ function disablePadding()
985
+ {
986
+ $this->padding = false;
987
+ }
988
+
989
+ /**
990
+ * Pads a string
991
+ *
992
+ * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize (8).
993
+ * 8 - (strlen($text) & 7) bytes are added, each of which is equal to chr(8 - (strlen($text) & 7)
994
+ *
995
+ * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
996
+ * and padding will, hence forth, be enabled.
997
+ *
998
+ * @see Crypt_TripleDES::_unpad()
999
+ * @access private
1000
+ */
1001
+ function _pad($text)
1002
+ {
1003
+ $length = strlen($text);
1004
+
1005
+ if (!$this->padding) {
1006
+ if (($length & 7) == 0) {
1007
+ return $text;
1008
+ } else {
1009
+ user_error("The plaintext's length ($length) is not a multiple of the block size (8)", E_USER_NOTICE);
1010
+ $this->padding = true;
1011
+ }
1012
+ }
1013
+
1014
+ $pad = 8 - ($length & 7);
1015
+ return str_pad($text, $length + $pad, chr($pad));
1016
+ }
1017
+
1018
+ /**
1019
+ * Unpads a string
1020
+ *
1021
+ * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
1022
+ * and false will be returned.
1023
+ *
1024
+ * @see Crypt_TripleDES::_pad()
1025
+ * @access private
1026
+ */
1027
+ function _unpad($text)
1028
+ {
1029
+ if (!$this->padding) {
1030
+ return $text;
1031
+ }
1032
+
1033
+ $length = ord($text[strlen($text) - 1]);
1034
+
1035
+ if (!$length || $length > 8) {
1036
+ return false;
1037
+ }
1038
+
1039
+ return substr($text, 0, -$length);
1040
+ }
1041
+
1042
+ /**
1043
+ * String Shift
1044
+ *
1045
+ * Inspired by array_shift
1046
+ *
1047
+ * @param String $string
1048
+ * @param optional Integer $index
1049
+ * @return String
1050
+ * @access private
1051
+ */
1052
+ function _string_shift(&$string, $index = 1)
1053
+ {
1054
+ $substr = substr($string, 0, $index);
1055
+ $string = substr($string, $index);
1056
+ return $substr;
1057
+ }
1058
+ }
1059
+
1060
+ // vim: ts=4:sw=4:et:
1061
+ // vim6: fdl=1:
phpseclib/Math/BigInteger.php ADDED
@@ -0,0 +1,3551 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP arbitrary precision integer arithmetic library.
6
+ *
7
+ * Supports base-2, base-10, base-16, and base-256 numbers. Uses the GMP or BCMath extensions, if available,
8
+ * and an internal implementation, otherwise.
9
+ *
10
+ * PHP versions 4 and 5
11
+ *
12
+ * {@internal (all DocBlock comments regarding implementation - such as the one that follows - refer to the
13
+ * {@link MATH_BIGINTEGER_MODE_INTERNAL MATH_BIGINTEGER_MODE_INTERNAL} mode)
14
+ *
15
+ * Math_BigInteger uses base-2**26 to perform operations such as multiplication and division and
16
+ * base-2**52 (ie. two base 2**26 digits) to perform addition and subtraction. Because the largest possible
17
+ * value when multiplying two base-2**26 numbers together is a base-2**52 number, double precision floating
18
+ * point numbers - numbers that should be supported on most hardware and whose significand is 53 bits - are
19
+ * used. As a consequence, bitwise operators such as >> and << cannot be used, nor can the modulo operator %,
20
+ * which only supports integers. Although this fact will slow this library down, the fact that such a high
21
+ * base is being used should more than compensate.
22
+ *
23
+ * When PHP version 6 is officially released, we'll be able to use 64-bit integers. This should, once again,
24
+ * allow bitwise operators, and will increase the maximum possible base to 2**31 (or 2**62 for addition /
25
+ * subtraction).
26
+ *
27
+ * Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness little endian} format. ie.
28
+ * (new Math_BigInteger(pow(2, 26)))->value = array(0, 1)
29
+ *
30
+ * Useful resources are as follows:
31
+ *
32
+ * - {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf Handbook of Applied Cryptography (HAC)}
33
+ * - {@link http://math.libtomcrypt.com/files/tommath.pdf Multi-Precision Math (MPM)}
34
+ * - Java's BigInteger classes. See /j2se/src/share/classes/java/math in jdk-1_5_0-src-jrl.zip
35
+ *
36
+ * Here's an example of how to use this library:
37
+ * <code>
38
+ * <?php
39
+ * include('Math/BigInteger.php');
40
+ *
41
+ * $a = new Math_BigInteger(2);
42
+ * $b = new Math_BigInteger(3);
43
+ *
44
+ * $c = $a->add($b);
45
+ *
46
+ * echo $c->toString(); // outputs 5
47
+ * ?>
48
+ * </code>
49
+ *
50
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
51
+ * of this software and associated documentation files (the "Software"), to deal
52
+ * in the Software without restriction, including without limitation the rights
53
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
54
+ * copies of the Software, and to permit persons to whom the Software is
55
+ * furnished to do so, subject to the following conditions:
56
+ *
57
+ * The above copyright notice and this permission notice shall be included in
58
+ * all copies or substantial portions of the Software.
59
+ *
60
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
61
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
62
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
63
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
64
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
65
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
66
+ * THE SOFTWARE.
67
+ *
68
+ * @category Math
69
+ * @package Math_BigInteger
70
+ * @author Jim Wigginton <terrafrost@php.net>
71
+ * @copyright MMVI Jim Wigginton
72
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
73
+ * @version $Id: BigInteger.php,v 1.33 2010/03/22 22:32:03 terrafrost Exp $
74
+ * @link http://pear.php.net/package/Math_BigInteger
75
+ */
76
+
77
+ /**#@+
78
+ * Reduction constants
79
+ *
80
+ * @access private
81
+ * @see Math_BigInteger::_reduce()
82
+ */
83
+ /**
84
+ * @see Math_BigInteger::_montgomery()
85
+ * @see Math_BigInteger::_prepMontgomery()
86
+ */
87
+ define('MATH_BIGINTEGER_MONTGOMERY', 0);
88
+ /**
89
+ * @see Math_BigInteger::_barrett()
90
+ */
91
+ define('MATH_BIGINTEGER_BARRETT', 1);
92
+ /**
93
+ * @see Math_BigInteger::_mod2()
94
+ */
95
+ define('MATH_BIGINTEGER_POWEROF2', 2);
96
+ /**
97
+ * @see Math_BigInteger::_remainder()
98
+ */
99
+ define('MATH_BIGINTEGER_CLASSIC', 3);
100
+ /**
101
+ * @see Math_BigInteger::__clone()
102
+ */
103
+ define('MATH_BIGINTEGER_NONE', 4);
104
+ /**#@-*/
105
+
106
+ /**#@+
107
+ * Array constants
108
+ *
109
+ * Rather than create a thousands and thousands of new Math_BigInteger objects in repeated function calls to add() and
110
+ * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them.
111
+ *
112
+ * @access private
113
+ */
114
+ /**
115
+ * $result[MATH_BIGINTEGER_VALUE] contains the value.
116
+ */
117
+ define('MATH_BIGINTEGER_VALUE', 0);
118
+ /**
119
+ * $result[MATH_BIGINTEGER_SIGN] contains the sign.
120
+ */
121
+ define('MATH_BIGINTEGER_SIGN', 1);
122
+ /**#@-*/
123
+
124
+ /**#@+
125
+ * @access private
126
+ * @see Math_BigInteger::_montgomery()
127
+ * @see Math_BigInteger::_barrett()
128
+ */
129
+ /**
130
+ * Cache constants
131
+ *
132
+ * $cache[MATH_BIGINTEGER_VARIABLE] tells us whether or not the cached data is still valid.
133
+ */
134
+ define('MATH_BIGINTEGER_VARIABLE', 0);
135
+ /**
136
+ * $cache[MATH_BIGINTEGER_DATA] contains the cached data.
137
+ */
138
+ define('MATH_BIGINTEGER_DATA', 1);
139
+ /**#@-*/
140
+
141
+ /**#@+
142
+ * Mode constants.
143
+ *
144
+ * @access private
145
+ * @see Math_BigInteger::Math_BigInteger()
146
+ */
147
+ /**
148
+ * To use the pure-PHP implementation
149
+ */
150
+ define('MATH_BIGINTEGER_MODE_INTERNAL', 1);
151
+ /**
152
+ * To use the BCMath library
153
+ *
154
+ * (if enabled; otherwise, the internal implementation will be used)
155
+ */
156
+ define('MATH_BIGINTEGER_MODE_BCMATH', 2);
157
+ /**
158
+ * To use the GMP library
159
+ *
160
+ * (if present; otherwise, either the BCMath or the internal implementation will be used)
161
+ */
162
+ define('MATH_BIGINTEGER_MODE_GMP', 3);
163
+ /**#@-*/
164
+
165
+ /**
166
+ * The largest digit that may be used in addition / subtraction
167
+ *
168
+ * (we do pow(2, 52) instead of using 4503599627370496, directly, because some PHP installations
169
+ * will truncate 4503599627370496)
170
+ *
171
+ * @access private
172
+ */
173
+ define('MATH_BIGINTEGER_MAX_DIGIT52', pow(2, 52));
174
+
175
+ /**
176
+ * Karatsuba Cutoff
177
+ *
178
+ * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication?
179
+ *
180
+ * @access private
181
+ */
182
+ define('MATH_BIGINTEGER_KARATSUBA_CUTOFF', 25);
183
+
184
+ /**
185
+ * Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256
186
+ * numbers.
187
+ *
188
+ * @author Jim Wigginton <terrafrost@php.net>
189
+ * @version 1.0.0RC4
190
+ * @access public
191
+ * @package Math_BigInteger
192
+ */
193
+ class Math_BigInteger {
194
+ /**
195
+ * Holds the BigInteger's value.
196
+ *
197
+ * @var Array
198
+ * @access private
199
+ */
200
+ var $value;
201
+
202
+ /**
203
+ * Holds the BigInteger's magnitude.
204
+ *
205
+ * @var Boolean
206
+ * @access private
207
+ */
208
+ var $is_negative = false;
209
+
210
+ /**
211
+ * Random number generator function
212
+ *
213
+ * @see setRandomGenerator()
214
+ * @access private
215
+ */
216
+ var $generator = 'mt_rand';
217
+
218
+ /**
219
+ * Precision
220
+ *
221
+ * @see setPrecision()
222
+ * @access private
223
+ */
224
+ var $precision = -1;
225
+
226
+ /**
227
+ * Precision Bitmask
228
+ *
229
+ * @see setPrecision()
230
+ * @access private
231
+ */
232
+ var $bitmask = false;
233
+
234
+ /**
235
+ * Mode independant value used for serialization.
236
+ *
237
+ * If the bcmath or gmp extensions are installed $this->value will be a non-serializable resource, hence the need for
238
+ * a variable that'll be serializable regardless of whether or not extensions are being used. Unlike $this->value,
239
+ * however, $this->hex is only calculated when $this->__sleep() is called.
240
+ *
241
+ * @see __sleep()
242
+ * @see __wakeup()
243
+ * @var String
244
+ * @access private
245
+ */
246
+ var $hex;
247
+
248
+ /**
249
+ * Converts base-2, base-10, base-16, and binary strings (eg. base-256) to BigIntegers.
250
+ *
251
+ * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using
252
+ * two's compliment. The sole exception to this is -10, which is treated the same as 10 is.
253
+ *
254
+ * Here's an example:
255
+ * <code>
256
+ * <?php
257
+ * include('Math/BigInteger.php');
258
+ *
259
+ * $a = new Math_BigInteger('0x32', 16); // 50 in base-16
260
+ *
261
+ * echo $a->toString(); // outputs 50
262
+ * ?>
263
+ * </code>
264
+ *
265
+ * @param optional $x base-10 number or base-$base number if $base set.
266
+ * @param optional integer $base
267
+ * @return Math_BigInteger
268
+ * @access public
269
+ */
270
+ function Math_BigInteger($x = 0, $base = 10)
271
+ {
272
+ if ( !defined('MATH_BIGINTEGER_MODE') ) {
273
+ switch (true) {
274
+ case extension_loaded('gmp'):
275
+ define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_GMP);
276
+ break;
277
+ case extension_loaded('bcmath'):
278
+ define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_BCMATH);
279
+ break;
280
+ default:
281
+ define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_INTERNAL);
282
+ }
283
+ }
284
+
285
+ switch ( MATH_BIGINTEGER_MODE ) {
286
+ case MATH_BIGINTEGER_MODE_GMP:
287
+ if (is_resource($x) && get_resource_type($x) == 'GMP integer') {
288
+ $this->value = $x;
289
+ return;
290
+ }
291
+ $this->value = gmp_init(0);
292
+ break;
293
+ case MATH_BIGINTEGER_MODE_BCMATH:
294
+ $this->value = '0';
295
+ break;
296
+ default:
297
+ $this->value = array();
298
+ }
299
+
300
+ if (empty($x)) {
301
+ return;
302
+ }
303
+
304
+ switch ($base) {
305
+ case -256:
306
+ if (ord($x[0]) & 0x80) {
307
+ $x = ~$x;
308
+ $this->is_negative = true;
309
+ }
310
+ case 256:
311
+ switch ( MATH_BIGINTEGER_MODE ) {
312
+ case MATH_BIGINTEGER_MODE_GMP:
313
+ $sign = $this->is_negative ? '-' : '';
314
+ $this->value = gmp_init($sign . '0x' . bin2hex($x));
315
+ break;
316
+ case MATH_BIGINTEGER_MODE_BCMATH:
317
+ // round $len to the nearest 4 (thanks, DavidMJ!)
318
+ $len = (strlen($x) + 3) & 0xFFFFFFFC;
319
+
320
+ $x = str_pad($x, $len, chr(0), STR_PAD_LEFT);
321
+
322
+ for ($i = 0; $i < $len; $i+= 4) {
323
+ $this->value = bcmul($this->value, '4294967296', 0); // 4294967296 == 2**32
324
+ $this->value = bcadd($this->value, 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2]) << 8) | ord($x[$i + 3])), 0);
325
+ }
326
+
327
+ if ($this->is_negative) {
328
+ $this->value = '-' . $this->value;
329
+ }
330
+
331
+ break;
332
+ // converts a base-2**8 (big endian / msb) number to base-2**26 (little endian / lsb)
333
+ default:
334
+ while (strlen($x)) {
335
+ $this->value[] = $this->_bytes2int($this->_base256_rshift($x, 26));
336
+ }
337
+ }
338
+
339
+ if ($this->is_negative) {
340
+ if (MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL) {
341
+ $this->is_negative = false;
342
+ }
343
+ $temp = $this->add(new Math_BigInteger('-1'));
344
+ $this->value = $temp->value;
345
+ }
346
+ break;
347
+ case 16:
348
+ case -16:
349
+ if ($base > 0 && $x[0] == '-') {
350
+ $this->is_negative = true;
351
+ $x = substr($x, 1);
352
+ }
353
+
354
+ $x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#', '$1', $x);
355
+
356
+ $is_negative = false;
357
+ if ($base < 0 && hexdec($x[0]) >= 8) {
358
+ $this->is_negative = $is_negative = true;
359
+ $x = bin2hex(~pack('H*', $x));
360
+ }
361
+
362
+ switch ( MATH_BIGINTEGER_MODE ) {
363
+ case MATH_BIGINTEGER_MODE_GMP:
364
+ $temp = $this->is_negative ? '-0x' . $x : '0x' . $x;
365
+ $this->value = gmp_init($temp);
366
+ $this->is_negative = false;
367
+ break;
368
+ case MATH_BIGINTEGER_MODE_BCMATH:
369
+ $x = ( strlen($x) & 1 ) ? '0' . $x : $x;
370
+ $temp = new Math_BigInteger(pack('H*', $x), 256);
371
+ $this->value = $this->is_negative ? '-' . $temp->value : $temp->value;
372
+ $this->is_negative = false;
373
+ break;
374
+ default:
375
+ $x = ( strlen($x) & 1 ) ? '0' . $x : $x;
376
+ $temp = new Math_BigInteger(pack('H*', $x), 256);
377
+ $this->value = $temp->value;
378
+ }
379
+
380
+ if ($is_negative) {
381
+ $temp = $this->add(new Math_BigInteger('-1'));
382
+ $this->value = $temp->value;
383
+ }
384
+ break;
385
+ case 10:
386
+ case -10:
387
+ $x = preg_replace('#^(-?[0-9]*).*#', '$1', $x);
388
+
389
+ switch ( MATH_BIGINTEGER_MODE ) {
390
+ case MATH_BIGINTEGER_MODE_GMP:
391
+ $this->value = gmp_init($x);
392
+ break;
393
+ case MATH_BIGINTEGER_MODE_BCMATH:
394
+ // explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different
395
+ // results then doing it on '-1' does (modInverse does $x[0])
396
+ $this->value = (string) $x;
397
+ break;
398
+ default:
399
+ $temp = new Math_BigInteger();
400
+
401
+ // array(10000000) is 10**7 in base-2**26. 10**7 is the closest to 2**26 we can get without passing it.
402
+ $multiplier = new Math_BigInteger();
403
+ $multiplier->value = array(10000000);
404
+
405
+ if ($x[0] == '-') {
406
+ $this->is_negative = true;
407
+ $x = substr($x, 1);
408
+ }
409
+
410
+ $x = str_pad($x, strlen($x) + (6 * strlen($x)) % 7, 0, STR_PAD_LEFT);
411
+
412
+ while (strlen($x)) {
413
+ $temp = $temp->multiply($multiplier);
414
+ $temp = $temp->add(new Math_BigInteger($this->_int2bytes(substr($x, 0, 7)), 256));
415
+ $x = substr($x, 7);
416
+ }
417
+
418
+ $this->value = $temp->value;
419
+ }
420
+ break;
421
+ case 2: // base-2 support originally implemented by Lluis Pamies - thanks!
422
+ case -2:
423
+ if ($base > 0 && $x[0] == '-') {
424
+ $this->is_negative = true;
425
+ $x = substr($x, 1);
426
+ }
427
+
428
+ $x = preg_replace('#^([01]*).*#', '$1', $x);
429
+ $x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT);
430
+
431
+ $str = '0x';
432
+ while (strlen($x)) {
433
+ $part = substr($x, 0, 4);
434
+ $str.= dechex(bindec($part));
435
+ $x = substr($x, 4);
436
+ }
437
+
438
+ if ($this->is_negative) {
439
+ $str = '-' . $str;
440
+ }
441
+
442
+ $temp = new Math_BigInteger($str, 8 * $base); // ie. either -16 or +16
443
+ $this->value = $temp->value;
444
+ $this->is_negative = $temp->is_negative;
445
+
446
+ break;
447
+ default:
448
+ // base not supported, so we'll let $this == 0
449
+ }
450
+ }
451
+
452
+ /**
453
+ * Converts a BigInteger to a byte string (eg. base-256).
454
+ *
455
+ * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
456
+ * saved as two's compliment.
457
+ *
458
+ * Here's an example:
459
+ * <code>
460
+ * <?php
461
+ * include('Math/BigInteger.php');
462
+ *
463
+ * $a = new Math_BigInteger('65');
464
+ *
465
+ * echo $a->toBytes(); // outputs chr(65)
466
+ * ?>
467
+ * </code>
468
+ *
469
+ * @param Boolean $twos_compliment
470
+ * @return String
471
+ * @access public
472
+ * @internal Converts a base-2**26 number to base-2**8
473
+ */
474
+ function toBytes($twos_compliment = false)
475
+ {
476
+ if ($twos_compliment) {
477
+ $comparison = $this->compare(new Math_BigInteger());
478
+ if ($comparison == 0) {
479
+ return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
480
+ }
481
+
482
+ $temp = $comparison < 0 ? $this->add(new Math_BigInteger(1)) : $this->copy();
483
+ $bytes = $temp->toBytes();
484
+
485
+ if (empty($bytes)) { // eg. if the number we're trying to convert is -1
486
+ $bytes = chr(0);
487
+ }
488
+
489
+ if (ord($bytes[0]) & 0x80) {
490
+ $bytes = chr(0) . $bytes;
491
+ }
492
+
493
+ return $comparison < 0 ? ~$bytes : $bytes;
494
+ }
495
+
496
+ switch ( MATH_BIGINTEGER_MODE ) {
497
+ case MATH_BIGINTEGER_MODE_GMP:
498
+ if (gmp_cmp($this->value, gmp_init(0)) == 0) {
499
+ return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
500
+ }
501
+
502
+ $temp = gmp_strval(gmp_abs($this->value), 16);
503
+ $temp = ( strlen($temp) & 1 ) ? '0' . $temp : $temp;
504
+ $temp = pack('H*', $temp);
505
+
506
+ return $this->precision > 0 ?
507
+ substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
508
+ ltrim($temp, chr(0));
509
+ case MATH_BIGINTEGER_MODE_BCMATH:
510
+ if ($this->value === '0') {
511
+ return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
512
+ }
513
+
514
+ $value = '';
515
+ $current = $this->value;
516
+
517
+ if ($current[0] == '-') {
518
+ $current = substr($current, 1);
519
+ }
520
+
521
+ while (bccomp($current, '0', 0) > 0) {
522
+ $temp = bcmod($current, '16777216');
523
+ $value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value;
524
+ $current = bcdiv($current, '16777216', 0);
525
+ }
526
+
527
+ return $this->precision > 0 ?
528
+ substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
529
+ ltrim($value, chr(0));
530
+ }
531
+
532
+ if (!count($this->value)) {
533
+ return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
534
+ }
535
+ $result = $this->_int2bytes($this->value[count($this->value) - 1]);
536
+
537
+ $temp = $this->copy();
538
+
539
+ for ($i = count($temp->value) - 2; $i >= 0; --$i) {
540
+ $temp->_base256_lshift($result, 26);
541
+ $result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT);
542
+ }
543
+
544
+ return $this->precision > 0 ?
545
+ str_pad(substr($result, -(($this->precision + 7) >> 3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) :
546
+ $result;
547
+ }
548
+
549
+ /**
550
+ * Converts a BigInteger to a hex string (eg. base-16)).
551
+ *
552
+ * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
553
+ * saved as two's compliment.
554
+ *
555
+ * Here's an example:
556
+ * <code>
557
+ * <?php
558
+ * include('Math/BigInteger.php');
559
+ *
560
+ * $a = new Math_BigInteger('65');
561
+ *
562
+ * echo $a->toHex(); // outputs '41'
563
+ * ?>
564
+ * </code>
565
+ *
566
+ * @param Boolean $twos_compliment
567
+ * @return String
568
+ * @access public
569
+ * @internal Converts a base-2**26 number to base-2**8
570
+ */
571
+ function toHex($twos_compliment = false)
572
+ {
573
+ return bin2hex($this->toBytes($twos_compliment));
574
+ }
575
+
576
+ /**
577
+ * Converts a BigInteger to a bit string (eg. base-2).
578
+ *
579
+ * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
580
+ * saved as two's compliment.
581
+ *
582
+ * Here's an example:
583
+ * <code>
584
+ * <?php
585
+ * include('Math/BigInteger.php');
586
+ *
587
+ * $a = new Math_BigInteger('65');
588
+ *
589
+ * echo $a->toBits(); // outputs '1000001'
590
+ * ?>
591
+ * </code>
592
+ *
593
+ * @param Boolean $twos_compliment
594
+ * @return String
595
+ * @access public
596
+ * @internal Converts a base-2**26 number to base-2**2
597
+ */
598
+ function toBits($twos_compliment = false)
599
+ {
600
+ $hex = $this->toHex($twos_compliment);
601
+ $bits = '';
602
+ for ($i = 0, $end = strlen($hex) & 0xFFFFFFF8; $i < $end; $i+=8) {
603
+ $bits.= str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT);
604
+ }
605
+ if ($end != strlen($hex)) { // hexdec('') == 0
606
+ $bits.= str_pad(decbin(hexdec(substr($hex, $end))), strlen($hex) & 7, '0', STR_PAD_LEFT);
607
+ }
608
+ return $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');
609
+ }
610
+
611
+ /**
612
+ * Converts a BigInteger to a base-10 number.
613
+ *
614
+ * Here's an example:
615
+ * <code>
616
+ * <?php
617
+ * include('Math/BigInteger.php');
618
+ *
619
+ * $a = new Math_BigInteger('50');
620
+ *
621
+ * echo $a->toString(); // outputs 50
622
+ * ?>
623
+ * </code>
624
+ *
625
+ * @return String
626
+ * @access public
627
+ * @internal Converts a base-2**26 number to base-10**7 (which is pretty much base-10)
628
+ */
629
+ function toString()
630
+ {
631
+ switch ( MATH_BIGINTEGER_MODE ) {
632
+ case MATH_BIGINTEGER_MODE_GMP:
633
+ return gmp_strval($this->value);
634
+ case MATH_BIGINTEGER_MODE_BCMATH:
635
+ if ($this->value === '0') {
636
+ return '0';
637
+ }
638
+
639
+ return ltrim($this->value, '0');
640
+ }
641
+
642
+ if (!count($this->value)) {
643
+ return '0';
644
+ }
645
+
646
+ $temp = $this->copy();
647
+ $temp->is_negative = false;
648
+
649
+ $divisor = new Math_BigInteger();
650
+ $divisor->value = array(10000000); // eg. 10**7
651
+ $result = '';
652
+ while (count($temp->value)) {
653
+ list($temp, $mod) = $temp->divide($divisor);
654
+ $result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', 7, '0', STR_PAD_LEFT) . $result;
655
+ }
656
+ $result = ltrim($result, '0');
657
+ if (empty($result)) {
658
+ $result = '0';
659
+ }
660
+
661
+ if ($this->is_negative) {
662
+ $result = '-' . $result;
663
+ }
664
+
665
+ return $result;
666
+ }
667
+
668
+ /**
669
+ * Copy an object
670
+ *
671
+ * PHP5 passes objects by reference while PHP4 passes by value. As such, we need a function to guarantee
672
+ * that all objects are passed by value, when appropriate. More information can be found here:
673
+ *
674
+ * {@link http://php.net/language.oop5.basic#51624}
675
+ *
676
+ * @access public
677
+ * @see __clone()
678
+ * @return Math_BigInteger
679
+ */
680
+ function copy()
681
+ {
682
+ $temp = new Math_BigInteger();
683
+ $temp->value = $this->value;
684
+ $temp->is_negative = $this->is_negative;
685
+ $temp->generator = $this->generator;
686
+ $temp->precision = $this->precision;
687
+ $temp->bitmask = $this->bitmask;
688
+ return $temp;
689
+ }
690
+
691
+ /**
692
+ * __toString() magic method
693
+ *
694
+ * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
695
+ * toString().
696
+ *
697
+ * @access public
698
+ * @internal Implemented per a suggestion by Techie-Michael - thanks!
699
+ */
700
+ function __toString()
701
+ {
702
+ return $this->toString();
703
+ }
704
+
705
+ /**
706
+ * __clone() magic method
707
+ *
708
+ * Although you can call Math_BigInteger::__toString() directly in PHP5, you cannot call Math_BigInteger::__clone()
709
+ * directly 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
710
+ * only syntax of $y = clone $x. As such, if you're trying to write an application that works on both PHP4 and PHP5,
711
+ * call Math_BigInteger::copy(), instead.
712
+ *
713
+ * @access public
714
+ * @see copy()
715
+ * @return Math_BigInteger
716
+ */
717
+ function __clone()
718
+ {
719
+ return $this->copy();
720
+ }
721
+
722
+ /**
723
+ * __sleep() magic method
724
+ *
725
+ * Will be called, automatically, when serialize() is called on a Math_BigInteger object.
726
+ *
727
+ * @see __wakeup()
728
+ * @access public
729
+ */
730
+ function __sleep()
731
+ {
732
+ $this->hex = $this->toHex(true);
733
+ $vars = array('hex');
734
+ if ($this->generator != 'mt_rand') {
735
+ $vars[] = 'generator';
736
+ }
737
+ if ($this->precision > 0) {
738
+ $vars[] = 'precision';
739
+ }
740
+ return $vars;
741
+
742
+ }
743
+
744
+ /**
745
+ * __wakeup() magic method
746
+ *
747
+ * Will be called, automatically, when unserialize() is called on a Math_BigInteger object.
748
+ *
749
+ * @see __sleep()
750
+ * @access public
751
+ */
752
+ function __wakeup()
753
+ {
754
+ $temp = new Math_BigInteger($this->hex, -16);
755
+ $this->value = $temp->value;
756
+ $this->is_negative = $temp->is_negative;
757
+ $this->setRandomGenerator($this->generator);
758
+ if ($this->precision > 0) {
759
+ // recalculate $this->bitmask
760
+ $this->setPrecision($this->precision);
761
+ }
762
+ }
763
+
764
+ /**
765
+ * Adds two BigIntegers.
766
+ *
767
+ * Here's an example:
768
+ * <code>
769
+ * <?php
770
+ * include('Math/BigInteger.php');
771
+ *
772
+ * $a = new Math_BigInteger('10');
773
+ * $b = new Math_BigInteger('20');
774
+ *
775
+ * $c = $a->add($b);
776
+ *
777
+ * echo $c->toString(); // outputs 30
778
+ * ?>
779
+ * </code>
780
+ *
781
+ * @param Math_BigInteger $y
782
+ * @return Math_BigInteger
783
+ * @access public
784
+ * @internal Performs base-2**52 addition
785
+ */
786
+ function add($y)
787
+ {
788
+ switch ( MATH_BIGINTEGER_MODE ) {
789
+ case MATH_BIGINTEGER_MODE_GMP:
790
+ $temp = new Math_BigInteger();
791
+ $temp->value = gmp_add($this->value, $y->value);
792
+
793
+ return $this->_normalize($temp);
794
+ case MATH_BIGINTEGER_MODE_BCMATH:
795
+ $temp = new Math_BigInteger();
796
+ $temp->value = bcadd($this->value, $y->value, 0);
797
+
798
+ return $this->_normalize($temp);
799
+ }
800
+
801
+ $temp = $this->_add($this->value, $this->is_negative, $y->value, $y->is_negative);
802
+
803
+ $result = new Math_BigInteger();
804
+ $result->value = $temp[MATH_BIGINTEGER_VALUE];
805
+ $result->is_negative = $temp[MATH_BIGINTEGER_SIGN];
806
+
807
+ return $this->_normalize($result);
808
+ }
809
+
810
+ /**
811
+ * Performs addition.
812
+ *
813
+ * @param Array $x_value
814
+ * @param Boolean $x_negative
815
+ * @param Array $y_value
816
+ * @param Boolean $y_negative
817
+ * @return Array
818
+ * @access private
819
+ */
820
+ function _add($x_value, $x_negative, $y_value, $y_negative)
821
+ {
822
+ $x_size = count($x_value);
823
+ $y_size = count($y_value);
824
+
825
+ if ($x_size == 0) {
826
+ return array(
827
+ MATH_BIGINTEGER_VALUE => $y_value,
828
+ MATH_BIGINTEGER_SIGN => $y_negative
829
+ );
830
+ } else if ($y_size == 0) {
831
+ return array(
832
+ MATH_BIGINTEGER_VALUE => $x_value,
833
+ MATH_BIGINTEGER_SIGN => $x_negative
834
+ );
835
+ }
836
+
837
+ // subtract, if appropriate
838
+ if ( $x_negative != $y_negative ) {
839
+ if ( $x_value == $y_value ) {
840
+ return array(
841
+ MATH_BIGINTEGER_VALUE => array(),
842
+ MATH_BIGINTEGER_SIGN => false
843
+ );
844
+ }
845
+
846
+ $temp = $this->_subtract($x_value, false, $y_value, false);
847
+ $temp[MATH_BIGINTEGER_SIGN] = $this->_compare($x_value, false, $y_value, false) > 0 ?
848
+ $x_negative : $y_negative;
849
+
850
+ return $temp;
851
+ }
852
+
853
+ if ($x_size < $y_size) {
854
+ $size = $x_size;
855
+ $value = $y_value;
856
+ } else {
857
+ $size = $y_size;
858
+ $value = $x_value;
859
+ }
860
+
861
+ $value[] = 0; // just in case the carry adds an extra digit
862
+
863
+ $carry = 0;
864
+ for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) {
865
+ $sum = $x_value[$j] * 0x4000000 + $x_value[$i] + $y_value[$j] * 0x4000000 + $y_value[$i] + $carry;
866
+ $carry = $sum >= MATH_BIGINTEGER_MAX_DIGIT52; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
867
+ $sum = $carry ? $sum - MATH_BIGINTEGER_MAX_DIGIT52 : $sum;
868
+
869
+ $temp = (int) ($sum / 0x4000000);
870
+
871
+ $value[$i] = (int) ($sum - 0x4000000 * $temp); // eg. a faster alternative to fmod($sum, 0x4000000)
872
+ $value[$j] = $temp;
873
+ }
874
+
875
+ if ($j == $size) { // ie. if $y_size is odd
876
+ $sum = $x_value[$i] + $y_value[$i] + $carry;
877
+ $carry = $sum >= 0x4000000;
878
+ $value[$i] = $carry ? $sum - 0x4000000 : $sum;
879
+ ++$i; // ie. let $i = $j since we've just done $value[$i]
880
+ }
881
+
882
+ if ($carry) {
883
+ for (; $value[$i] == 0x3FFFFFF; ++$i) {
884
+ $value[$i] = 0;
885
+ }
886
+ ++$value[$i];
887
+ }
888
+
889
+ return array(
890
+ MATH_BIGINTEGER_VALUE => $this->_trim($value),
891
+ MATH_BIGINTEGER_SIGN => $x_negative
892
+ );
893
+ }
894
+
895
+ /**
896
+ * Subtracts two BigIntegers.
897
+ *
898
+ * Here's an example:
899
+ * <code>
900
+ * <?php
901
+ * include('Math/BigInteger.php');
902
+ *
903
+ * $a = new Math_BigInteger('10');
904
+ * $b = new Math_BigInteger('20');
905
+ *
906
+ * $c = $a->subtract($b);
907
+ *
908
+ * echo $c->toString(); // outputs -10
909
+ * ?>
910
+ * </code>
911
+ *
912
+ * @param Math_BigInteger $y
913
+ * @return Math_BigInteger
914
+ * @access public
915
+ * @internal Performs base-2**52 subtraction
916
+ */
917
+ function subtract($y)
918
+ {
919
+ switch ( MATH_BIGINTEGER_MODE ) {
920
+ case MATH_BIGINTEGER_MODE_GMP:
921
+ $temp = new Math_BigInteger();
922
+ $temp->value = gmp_sub($this->value, $y->value);
923
+
924
+ return $this->_normalize($temp);
925
+ case MATH_BIGINTEGER_MODE_BCMATH:
926
+ $temp = new Math_BigInteger();
927
+ $temp->value = bcsub($this->value, $y->value, 0);
928
+
929
+ return $this->_normalize($temp);
930
+ }
931
+
932
+ $temp = $this->_subtract($this->value, $this->is_negative, $y->value, $y->is_negative);
933
+
934
+ $result = new Math_BigInteger();
935
+ $result->value = $temp[MATH_BIGINTEGER_VALUE];
936
+ $result->is_negative = $temp[MATH_BIGINTEGER_SIGN];
937
+
938
+ return $this->_normalize($result);
939
+ }
940
+
941
+ /**
942
+ * Performs subtraction.
943
+ *
944
+ * @param Array $x_value
945
+ * @param Boolean $x_negative
946
+ * @param Array $y_value
947
+ * @param Boolean $y_negative
948
+ * @return Array
949
+ * @access private
950
+ */
951
+ function _subtract($x_value, $x_negative, $y_value, $y_negative)
952
+ {
953
+ $x_size = count($x_value);
954
+ $y_size = count($y_value);
955
+
956
+ if ($x_size == 0) {
957
+ return array(
958
+ MATH_BIGINTEGER_VALUE => $y_value,
959
+ MATH_BIGINTEGER_SIGN => !$y_negative
960
+ );
961
+ } else if ($y_size == 0) {
962
+ return array(
963
+ MATH_BIGINTEGER_VALUE => $x_value,
964
+ MATH_BIGINTEGER_SIGN => $x_negative
965
+ );
966
+ }
967
+
968
+ // add, if appropriate (ie. -$x - +$y or +$x - -$y)
969
+ if ( $x_negative != $y_negative ) {
970
+ $temp = $this->_add($x_value, false, $y_value, false);
971
+ $temp[MATH_BIGINTEGER_SIGN] = $x_negative;
972
+
973
+ return $temp;
974
+ }
975
+
976
+ $diff = $this->_compare($x_value, $x_negative, $y_value, $y_negative);
977
+
978
+ if ( !$diff ) {
979
+ return array(
980
+ MATH_BIGINTEGER_VALUE => array(),
981
+ MATH_BIGINTEGER_SIGN => false
982
+ );
983
+ }
984
+
985
+ // switch $x and $y around, if appropriate.
986
+ if ( (!$x_negative && $diff < 0) || ($x_negative && $diff > 0) ) {
987
+ $temp = $x_value;
988
+ $x_value = $y_value;
989
+ $y_value = $temp;
990
+
991
+ $x_negative = !$x_negative;
992
+
993
+ $x_size = count($x_value);
994
+ $y_size = count($y_value);
995
+ }
996
+
997
+ // at this point, $x_value should be at least as big as - if not bigger than - $y_value
998
+
999
+ $carry = 0;
1000
+ for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) {
1001
+ $sum = $x_value[$j] * 0x4000000 + $x_value[$i] - $y_value[$j] * 0x4000000 - $y_value[$i] - $carry;
1002
+ $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
1003
+ $sum = $carry ? $sum + MATH_BIGINTEGER_MAX_DIGIT52 : $sum;
1004
+
1005
+ $temp = (int) ($sum / 0x4000000);
1006
+
1007
+ $x_value[$i] = (int) ($sum - 0x4000000 * $temp);
1008
+ $x_value[$j] = $temp;
1009
+ }
1010
+
1011
+ if ($j == $y_size) { // ie. if $y_size is odd
1012
+ $sum = $x_value[$i] - $y_value[$i] - $carry;
1013
+ $carry = $sum < 0;
1014
+ $x_value[$i] = $carry ? $sum + 0x4000000 : $sum;
1015
+ ++$i;
1016
+ }
1017
+
1018
+ if ($carry) {
1019
+ for (; !$x_value[$i]; ++$i) {
1020
+ $x_value[$i] = 0x3FFFFFF;
1021
+ }
1022
+ --$x_value[$i];
1023
+ }
1024
+
1025
+ return array(
1026
+ MATH_BIGINTEGER_VALUE => $this->_trim($x_value),
1027
+ MATH_BIGINTEGER_SIGN => $x_negative
1028
+ );
1029
+ }
1030
+
1031
+ /**
1032
+ * Multiplies two BigIntegers
1033
+ *
1034
+ * Here's an example:
1035
+ * <code>
1036
+ * <?php
1037
+ * include('Math/BigInteger.php');
1038
+ *
1039
+ * $a = new Math_BigInteger('10');
1040
+ * $b = new Math_BigInteger('20');
1041
+ *
1042
+ * $c = $a->multiply($b);
1043
+ *
1044
+ * echo $c->toString(); // outputs 200
1045
+ * ?>
1046
+ * </code>
1047
+ *
1048
+ * @param Math_BigInteger $x
1049
+ * @return Math_BigInteger
1050
+ * @access public
1051
+ */
1052
+ function multiply($x)
1053
+ {
1054
+ switch ( MATH_BIGINTEGER_MODE ) {
1055
+ case MATH_BIGINTEGER_MODE_GMP:
1056
+ $temp = new Math_BigInteger();
1057
+ $temp->value = gmp_mul($this->value, $x->value);
1058
+
1059
+ return $this->_normalize($temp);
1060
+ case MATH_BIGINTEGER_MODE_BCMATH:
1061
+ $temp = new Math_BigInteger();
1062
+ $temp->value = bcmul($this->value, $x->value, 0);
1063
+
1064
+ return $this->_normalize($temp);
1065
+ }
1066
+
1067
+ $temp = $this->_multiply($this->value, $this->is_negative, $x->value, $x->is_negative);
1068
+
1069
+ $product = new Math_BigInteger();
1070
+ $product->value = $temp[MATH_BIGINTEGER_VALUE];
1071
+ $product->is_negative = $temp[MATH_BIGINTEGER_SIGN];
1072
+
1073
+ return $this->_normalize($product);
1074
+ }
1075
+
1076
+ /**
1077
+ * Performs multiplication.
1078
+ *
1079
+ * @param Array $x_value
1080
+ * @param Boolean $x_negative
1081
+ * @param Array $y_value
1082
+ * @param Boolean $y_negative
1083
+ * @return Array
1084
+ * @access private
1085
+ */
1086
+ function _multiply($x_value, $x_negative, $y_value, $y_negative)
1087
+ {
1088
+ //if ( $x_value == $y_value ) {
1089
+ // return array(
1090
+ // MATH_BIGINTEGER_VALUE => $this->_square($x_value),
1091
+ // MATH_BIGINTEGER_SIGN => $x_sign != $y_value
1092
+ // );
1093
+ //}
1094
+
1095
+ $x_length = count($x_value);
1096
+ $y_length = count($y_value);
1097
+
1098
+ if ( !$x_length || !$y_length ) { // a 0 is being multiplied
1099
+ return array(
1100
+ MATH_BIGINTEGER_VALUE => array(),
1101
+ MATH_BIGINTEGER_SIGN => false
1102
+ );
1103
+ }
1104
+
1105
+ return array(
1106
+ MATH_BIGINTEGER_VALUE => min($x_length, $y_length) < 2 * MATH_BIGINTEGER_KARATSUBA_CUTOFF ?
1107
+ $this->_trim($this->_regularMultiply($x_value, $y_value)) :
1108
+ $this->_trim($this->_karatsuba($x_value, $y_value)),
1109
+ MATH_BIGINTEGER_SIGN => $x_negative != $y_negative
1110
+ );
1111
+ }
1112
+
1113
+ /**
1114
+ * Performs long multiplication on two BigIntegers
1115
+ *
1116
+ * Modeled after 'multiply' in MutableBigInteger.java.
1117
+ *
1118
+ * @param Array $x_value
1119
+ * @param Array $y_value
1120
+ * @return Array
1121
+ * @access private
1122
+ */
1123
+ function _regularMultiply($x_value, $y_value)
1124
+ {
1125
+ $x_length = count($x_value);
1126
+ $y_length = count($y_value);
1127
+
1128
+ if ( !$x_length || !$y_length ) { // a 0 is being multiplied
1129
+ return array();
1130
+ }
1131
+
1132
+ if ( $x_length < $y_length ) {
1133
+ $temp = $x_value;
1134
+ $x_value = $y_value;
1135
+ $y_value = $temp;
1136
+
1137
+ $x_length = count($x_value);
1138
+ $y_length = count($y_value);
1139
+ }
1140
+
1141
+ $product_value = $this->_array_repeat(0, $x_length + $y_length);
1142
+
1143
+ // the following for loop could be removed if the for loop following it
1144
+ // (the one with nested for loops) initially set $i to 0, but
1145
+ // doing so would also make the result in one set of unnecessary adds,
1146
+ // since on the outermost loops first pass, $product->value[$k] is going
1147
+ // to always be 0
1148
+
1149
+ $carry = 0;
1150
+
1151
+ for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0
1152
+ $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
1153
+ $carry = (int) ($temp / 0x4000000);
1154
+ $product_value[$j] = (int) ($temp - 0x4000000 * $carry);
1155
+ }
1156
+
1157
+ $product_value[$j] = $carry;
1158
+
1159
+ // the above for loop is what the previous comment was talking about. the
1160
+ // following for loop is the "one with nested for loops"
1161
+ for ($i = 1; $i < $y_length; ++$i) {
1162
+ $carry = 0;
1163
+
1164
+ for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) {
1165
+ $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
1166
+ $carry = (int) ($temp / 0x4000000);
1167
+ $product_value[$k] = (int) ($temp - 0x4000000 * $carry);
1168
+ }
1169
+
1170
+ $product_value[$k] = $carry;
1171
+ }
1172
+
1173
+ return $product_value;
1174
+ }
1175
+
1176
+ /**
1177
+ * Performs Karatsuba multiplication on two BigIntegers
1178
+ *
1179
+ * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
1180
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}.
1181
+ *
1182
+ * @param Array $x_value
1183
+ * @param Array $y_value
1184
+ * @return Array
1185
+ * @access private
1186
+ */
1187
+ function _karatsuba($x_value, $y_value)
1188
+ {
1189
+ $m = min(count($x_value) >> 1, count($y_value) >> 1);
1190
+
1191
+ if ($m < MATH_BIGINTEGER_KARATSUBA_CUTOFF) {
1192
+ return $this->_regularMultiply($x_value, $y_value);
1193
+ }
1194
+
1195
+ $x1 = array_slice($x_value, $m);
1196
+ $x0 = array_slice($x_value, 0, $m);
1197
+ $y1 = array_slice($y_value, $m);
1198
+ $y0 = array_slice($y_value, 0, $m);
1199
+
1200
+ $z2 = $this->_karatsuba($x1, $y1);
1201
+ $z0 = $this->_karatsuba($x0, $y0);
1202
+
1203
+ $z1 = $this->_add($x1, false, $x0, false);
1204
+ $temp = $this->_add($y1, false, $y0, false);
1205
+ $z1 = $this->_karatsuba($z1[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_VALUE]);
1206
+ $temp = $this->_add($z2, false, $z0, false);
1207
+ $z1 = $this->_subtract($z1, false, $temp[MATH_BIGINTEGER_VALUE], false);
1208
+
1209
+ $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
1210
+ $z1[MATH_BIGINTEGER_VALUE] = array_merge(array_fill(0, $m, 0), $z1[MATH_BIGINTEGER_VALUE]);
1211
+
1212
+ $xy = $this->_add($z2, false, $z1[MATH_BIGINTEGER_VALUE], $z1[MATH_BIGINTEGER_SIGN]);
1213
+ $xy = $this->_add($xy[MATH_BIGINTEGER_VALUE], $xy[MATH_BIGINTEGER_SIGN], $z0, false);
1214
+
1215
+ return $xy[MATH_BIGINTEGER_VALUE];
1216
+ }
1217
+
1218
+ /**
1219
+ * Performs squaring
1220
+ *
1221
+ * @param Array $x
1222
+ * @return Array
1223
+ * @access private
1224
+ */
1225
+ function _square($x = false)
1226
+ {
1227
+ return count($x) < 2 * MATH_BIGINTEGER_KARATSUBA_CUTOFF ?
1228
+ $this->_trim($this->_baseSquare($x)) :
1229
+ $this->_trim($this->_karatsubaSquare($x));
1230
+ }
1231
+
1232
+ /**
1233
+ * Performs traditional squaring on two BigIntegers
1234
+ *
1235
+ * Squaring can be done faster than multiplying a number by itself can be. See
1236
+ * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} /
1237
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information.
1238
+ *
1239
+ * @param Array $value
1240
+ * @return Array
1241
+ * @access private
1242
+ */
1243
+ function _baseSquare($value)
1244
+ {
1245
+ if ( empty($value) ) {
1246
+ return array();
1247
+ }
1248
+ $square_value = $this->_array_repeat(0, 2 * count($value));
1249
+
1250
+ for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) {
1251
+ $i2 = $i << 1;
1252
+
1253
+ $temp = $square_value[$i2] + $value[$i] * $value[$i];
1254
+ $carry = (int) ($temp / 0x4000000);
1255
+ $square_value[$i2] = (int) ($temp - 0x4000000 * $carry);
1256
+
1257
+ // note how we start from $i+1 instead of 0 as we do in multiplication.
1258
+ for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) {
1259
+ $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry;
1260
+ $carry = (int) ($temp / 0x4000000);
1261
+ $square_value[$k] = (int) ($temp - 0x4000000 * $carry);
1262
+ }
1263
+
1264
+ // the following line can yield values larger 2**15. at this point, PHP should switch
1265
+ // over to floats.
1266
+ $square_value[$i + $max_index + 1] = $carry;
1267
+ }
1268
+
1269
+ return $square_value;
1270
+ }
1271
+
1272
+ /**
1273
+ * Performs Karatsuba "squaring" on two BigIntegers
1274
+ *
1275
+ * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
1276
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}.
1277
+ *
1278
+ * @param Array $value
1279
+ * @return Array
1280
+ * @access private
1281
+ */
1282
+ function _karatsubaSquare($value)
1283
+ {
1284
+ $m = count($value) >> 1;
1285
+
1286
+ if ($m < MATH_BIGINTEGER_KARATSUBA_CUTOFF) {
1287
+ return $this->_baseSquare($value);
1288
+ }
1289
+
1290
+ $x1 = array_slice($value, $m);
1291
+ $x0 = array_slice($value, 0, $m);
1292
+
1293
+ $z2 = $this->_karatsubaSquare($x1);
1294
+ $z0 = $this->_karatsubaSquare($x0);
1295
+
1296
+ $z1 = $this->_add($x1, false, $x0, false);
1297
+ $z1 = $this->_karatsubaSquare($z1[MATH_BIGINTEGER_VALUE]);
1298
+ $temp = $this->_add($z2, false, $z0, false);
1299
+ $z1 = $this->_subtract($z1, false, $temp[MATH_BIGINTEGER_VALUE], false);
1300
+
1301
+ $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
1302
+ $z1[MATH_BIGINTEGER_VALUE] = array_merge(array_fill(0, $m, 0), $z1[MATH_BIGINTEGER_VALUE]);
1303
+
1304
+ $xx = $this->_add($z2, false, $z1[MATH_BIGINTEGER_VALUE], $z1[MATH_BIGINTEGER_SIGN]);
1305
+ $xx = $this->_add($xx[MATH_BIGINTEGER_VALUE], $xx[MATH_BIGINTEGER_SIGN], $z0, false);
1306
+
1307
+ return $xx[MATH_BIGINTEGER_VALUE];
1308
+ }
1309
+
1310
+ /**
1311
+ * Divides two BigIntegers.
1312
+ *
1313
+ * Returns an array whose first element contains the quotient and whose second element contains the
1314
+ * "common residue". If the remainder would be positive, the "common residue" and the remainder are the
1315
+ * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder
1316
+ * and the divisor (basically, the "common residue" is the first positive modulo).
1317
+ *
1318
+ * Here's an example:
1319
+ * <code>
1320
+ * <?php
1321
+ * include('Math/BigInteger.php');
1322
+ *
1323
+ * $a = new Math_BigInteger('10');
1324
+ * $b = new Math_BigInteger('20');
1325
+ *
1326
+ * list($quotient, $remainder) = $a->divide($b);
1327
+ *
1328
+ * echo $quotient->toString(); // outputs 0
1329
+ * echo "\r\n";
1330
+ * echo $remainder->toString(); // outputs 10
1331
+ * ?>
1332
+ * </code>
1333
+ *
1334
+ * @param Math_BigInteger $y
1335
+ * @return Array
1336
+ * @access public
1337
+ * @internal This function is based off of {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}.
1338
+ */
1339
+ function divide($y)
1340
+ {
1341
+ switch ( MATH_BIGINTEGER_MODE ) {
1342
+ case MATH_BIGINTEGER_MODE_GMP:
1343
+ $quotient = new Math_BigInteger();
1344
+ $remainder = new Math_BigInteger();
1345
+
1346
+ list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value);
1347
+
1348
+ if (gmp_sign($remainder->value) < 0) {
1349
+ $remainder->value = gmp_add($remainder->value, gmp_abs($y->value));
1350
+ }
1351
+
1352
+ return array($this->_normalize($quotient), $this->_normalize($remainder));
1353
+ case MATH_BIGINTEGER_MODE_BCMATH:
1354
+ $quotient = new Math_BigInteger();
1355
+ $remainder = new Math_BigInteger();
1356
+
1357
+ $quotient->value = bcdiv($this->value, $y->value, 0);
1358
+ $remainder->value = bcmod($this->value, $y->value);
1359
+
1360
+ if ($remainder->value[0] == '-') {
1361
+ $remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0);
1362
+ }
1363
+
1364
+ return array($this->_normalize($quotient), $this->_normalize($remainder));
1365
+ }
1366
+
1367
+ if (count($y->value) == 1) {
1368
+ list($q, $r) = $this->_divide_digit($this->value, $y->value[0]);
1369
+ $quotient = new Math_BigInteger();
1370
+ $remainder = new Math_BigInteger();
1371
+ $quotient->value = $q;
1372
+ $remainder->value = array($r);
1373
+ $quotient->is_negative = $this->is_negative != $y->is_negative;
1374
+ return array($this->_normalize($quotient), $this->_normalize($remainder));
1375
+ }
1376
+
1377
+ static $zero;
1378
+ if ( !isset($zero) ) {
1379
+ $zero = new Math_BigInteger();
1380
+ }
1381
+
1382
+ $x = $this->copy();
1383
+ $y = $y->copy();
1384
+
1385
+ $x_sign = $x->is_negative;
1386
+ $y_sign = $y->is_negative;
1387
+
1388
+ $x->is_negative = $y->is_negative = false;
1389
+
1390
+ $diff = $x->compare($y);
1391
+
1392
+ if ( !$diff ) {
1393
+ $temp = new Math_BigInteger();
1394
+ $temp->value = array(1);
1395
+ $temp->is_negative = $x_sign != $y_sign;
1396
+ return array($this->_normalize($temp), $this->_normalize(new Math_BigInteger()));
1397
+ }
1398
+
1399
+ if ( $diff < 0 ) {
1400
+ // if $x is negative, "add" $y.
1401
+ if ( $x_sign ) {
1402
+ $x = $y->subtract($x);
1403
+ }
1404
+ return array($this->_normalize(new Math_BigInteger()), $this->_normalize($x));
1405
+ }
1406
+
1407
+ // normalize $x and $y as described in HAC 14.23 / 14.24
1408
+ $msb = $y->value[count($y->value) - 1];
1409
+ for ($shift = 0; !($msb & 0x2000000); ++$shift) {
1410
+ $msb <<= 1;
1411
+ }
1412
+ $x->_lshift($shift);
1413
+ $y->_lshift($shift);
1414
+ $y_value = &$y->value;
1415
+
1416
+ $x_max = count($x->value) - 1;
1417
+ $y_max = count($y->value) - 1;
1418
+
1419
+ $quotient = new Math_BigInteger();
1420
+ $quotient_value = &$quotient->value;
1421
+ $quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1);
1422
+
1423
+ static $temp, $lhs, $rhs;
1424
+ if (!isset($temp)) {
1425
+ $temp = new Math_BigInteger();
1426
+ $lhs = new Math_BigInteger();
1427
+ $rhs = new Math_BigInteger();
1428
+ }
1429
+ $temp_value = &$temp->value;
1430
+ $rhs_value = &$rhs->value;
1431
+
1432
+ // $temp = $y << ($x_max - $y_max-1) in base 2**26
1433
+ $temp_value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y_value);
1434
+
1435
+ while ( $x->compare($temp) >= 0 ) {
1436
+ // calculate the "common residue"
1437
+ ++$quotient_value[$x_max - $y_max];
1438
+ $x = $x->subtract($temp);
1439
+ $x_max = count($x->value) - 1;
1440
+ }
1441
+
1442
+ for ($i = $x_max; $i >= $y_max + 1; --$i) {
1443
+ $x_value = &$x->value;
1444
+ $x_window = array(
1445
+ isset($x_value[$i]) ? $x_value[$i] : 0,
1446
+ isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0,
1447
+ isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0
1448
+ );
1449
+ $y_window = array(
1450
+ $y_value[$y_max],
1451
+ ( $y_max > 0 ) ? $y_value[$y_max - 1] : 0
1452
+ );
1453
+
1454
+ $q_index = $i - $y_max - 1;
1455
+ if ($x_window[0] == $y_window[0]) {
1456
+ $quotient_value[$q_index] = 0x3FFFFFF;
1457
+ } else {
1458
+ $quotient_value[$q_index] = (int) (
1459
+ ($x_window[0] * 0x4000000 + $x_window[1])
1460
+ /
1461
+ $y_window[0]
1462
+ );
1463
+ }
1464
+
1465
+ $temp_value = array($y_window[1], $y_window[0]);
1466
+
1467
+ $lhs->value = array($quotient_value[$q_index]);
1468
+ $lhs = $lhs->multiply($temp);
1469
+
1470
+ $rhs_value = array($x_window[2], $x_window[1], $x_window[0]);
1471
+
1472
+ while ( $lhs->compare($rhs) > 0 ) {
1473
+ --$quotient_value[$q_index];
1474
+
1475
+ $lhs->value = array($quotient_value[$q_index]);
1476
+ $lhs = $lhs->multiply($temp);
1477
+ }
1478
+
1479
+ $adjust = $this->_array_repeat(0, $q_index);
1480
+ $temp_value = array($quotient_value[$q_index]);
1481
+ $temp = $temp->multiply($y);
1482
+ $temp_value = &$temp->value;
1483
+ $temp_value = array_merge($adjust, $temp_value);
1484
+
1485
+ $x = $x->subtract($temp);
1486
+
1487
+ if ($x->compare($zero) < 0) {
1488
+ $temp_value = array_merge($adjust, $y_value);
1489
+ $x = $x->add($temp);
1490
+
1491
+ --$quotient_value[$q_index];
1492
+ }
1493
+
1494
+ $x_max = count($x_value) - 1;
1495
+ }
1496
+
1497
+ // unnormalize the remainder
1498
+ $x->_rshift($shift);
1499
+
1500
+ $quotient->is_negative = $x_sign != $y_sign;
1501
+
1502
+ // calculate the "common residue", if appropriate
1503
+ if ( $x_sign ) {
1504
+ $y->_rshift($shift);
1505
+ $x = $y->subtract($x);
1506
+ }
1507
+
1508
+ return array($this->_normalize($quotient), $this->_normalize($x));
1509
+ }
1510
+
1511
+ /**
1512
+ * Divides a BigInteger by a regular integer
1513
+ *
1514
+ * abc / x = a00 / x + b0 / x + c / x
1515
+ *
1516
+ * @param Array $dividend
1517
+ * @param Array $divisor
1518
+ * @return Array
1519
+ * @access private
1520
+ */
1521
+ function _divide_digit($dividend, $divisor)
1522
+ {
1523
+ $carry = 0;
1524
+ $result = array();
1525
+
1526
+ for ($i = count($dividend) - 1; $i >= 0; --$i) {
1527
+ $temp = 0x4000000 * $carry + $dividend[$i];
1528
+ $result[$i] = (int) ($temp / $divisor);
1529
+ $carry = (int) ($temp - $divisor * $result[$i]);
1530
+ }
1531
+
1532
+ return array($result, $carry);
1533
+ }
1534
+
1535
+ /**
1536
+ * Performs modular exponentiation.
1537
+ *
1538
+ * Here's an example:
1539
+ * <code>
1540
+ * <?php
1541
+ * include('Math/BigInteger.php');
1542
+ *
1543
+ * $a = new Math_BigInteger('10');
1544
+ * $b = new Math_BigInteger('20');
1545
+ * $c = new Math_BigInteger('30');
1546
+ *
1547
+ * $c = $a->modPow($b, $c);
1548
+ *
1549
+ * echo $c->toString(); // outputs 10
1550
+ * ?>
1551
+ * </code>
1552
+ *
1553
+ * @param Math_BigInteger $e
1554
+ * @param Math_BigInteger $n
1555
+ * @return Math_BigInteger
1556
+ * @access public
1557
+ * @internal The most naive approach to modular exponentiation has very unreasonable requirements, and
1558
+ * and although the approach involving repeated squaring does vastly better, it, too, is impractical
1559
+ * for our purposes. The reason being that division - by far the most complicated and time-consuming
1560
+ * of the basic operations (eg. +,-,*,/) - occurs multiple times within it.
1561
+ *
1562
+ * Modular reductions resolve this issue. Although an individual modular reduction takes more time
1563
+ * then an individual division, when performed in succession (with the same modulo), they're a lot faster.
1564
+ *
1565
+ * The two most commonly used modular reductions are Barrett and Montgomery reduction. Montgomery reduction,
1566
+ * although faster, only works when the gcd of the modulo and of the base being used is 1. In RSA, when the
1567
+ * base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because
1568
+ * the product of two odd numbers is odd), but what about when RSA isn't used?
1569
+ *
1570
+ * In contrast, Barrett reduction has no such constraint. As such, some bigint implementations perform a
1571
+ * Barrett reduction after every operation in the modpow function. Others perform Barrett reductions when the
1572
+ * modulo is even and Montgomery reductions when the modulo is odd. BigInteger.java's modPow method, however,
1573
+ * uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and
1574
+ * the other, a power of two - and recombine them, later. This is the method that this modPow function uses.
1575
+ * {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates.
1576
+ */
1577
+ function modPow($e, $n)
1578
+ {
1579
+ $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs();
1580
+
1581
+ if ($e->compare(new Math_BigInteger()) < 0) {
1582
+ $e = $e->abs();
1583
+
1584
+ $temp = $this->modInverse($n);
1585
+ if ($temp === false) {
1586
+ return false;
1587
+ }
1588
+
1589
+ return $this->_normalize($temp->modPow($e, $n));
1590
+ }
1591
+
1592
+ switch ( MATH_BIGINTEGER_MODE ) {
1593
+ case MATH_BIGINTEGER_MODE_GMP:
1594
+ $temp = new Math_BigInteger();
1595
+ $temp->value = gmp_powm($this->value, $e->value, $n->value);
1596
+
1597
+ return $this->_normalize($temp);
1598
+ case MATH_BIGINTEGER_MODE_BCMATH:
1599
+ $temp = new Math_BigInteger();
1600
+ $temp->value = bcpowmod($this->value, $e->value, $n->value, 0);
1601
+
1602
+ return $this->_normalize($temp);
1603
+ }
1604
+
1605
+ if ( empty($e->value) ) {
1606
+ $temp = new Math_BigInteger();
1607
+ $temp->value = array(1);
1608
+ return $this->_normalize($temp);
1609
+ }
1610
+
1611
+ if ( $e->value == array(1) ) {
1612
+ list(, $temp) = $this->divide($n);
1613
+ return $this->_normalize($temp);
1614
+ }
1615
+
1616
+ if ( $e->value == array(2) ) {
1617
+ $temp = new Math_BigInteger();
1618
+ $temp->value = $this->_square($this->value);
1619
+ list(, $temp) = $temp->divide($n);
1620
+ return $this->_normalize($temp);
1621
+ }
1622
+
1623
+ return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_BARRETT));
1624
+
1625
+ // is the modulo odd?
1626
+ if ( $n->value[0] & 1 ) {
1627
+ return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_MONTGOMERY));
1628
+ }
1629
+ // if it's not, it's even
1630
+
1631
+ // find the lowest set bit (eg. the max pow of 2 that divides $n)
1632
+ for ($i = 0; $i < count($n->value); ++$i) {
1633
+ if ( $n->value[$i] ) {
1634
+ $temp = decbin($n->value[$i]);
1635
+ $j = strlen($temp) - strrpos($temp, '1') - 1;
1636
+ $j+= 26 * $i;
1637
+ break;
1638
+ }
1639
+ }
1640
+ // at this point, 2^$j * $n/(2^$j) == $n
1641
+
1642
+ $mod1 = $n->copy();
1643
+ $mod1->_rshift($j);
1644
+ $mod2 = new Math_BigInteger();
1645
+ $mod2->value = array(1);
1646
+ $mod2->_lshift($j);
1647
+
1648
+ $part1 = ( $mod1->value != array(1) ) ? $this->_slidingWindow($e, $mod1, MATH_BIGINTEGER_MONTGOMERY) : new Math_BigInteger();
1649
+ $part2 = $this->_slidingWindow($e, $mod2, MATH_BIGINTEGER_POWEROF2);
1650
+
1651
+ $y1 = $mod2->modInverse($mod1);
1652
+ $y2 = $mod1->modInverse($mod2);
1653
+
1654
+ $result = $part1->multiply($mod2);
1655
+ $result = $result->multiply($y1);
1656
+
1657
+ $temp = $part2->multiply($mod1);
1658
+ $temp = $temp->multiply($y2);
1659
+
1660
+ $result = $result->add($temp);
1661
+ list(, $result) = $result->divide($n);
1662
+
1663
+ return $this->_normalize($result);
1664
+ }
1665
+
1666
+ /**
1667
+ * Performs modular exponentiation.
1668
+ *
1669
+ * Alias for Math_BigInteger::modPow()
1670
+ *
1671
+ * @param Math_BigInteger $e
1672
+ * @param Math_BigInteger $n
1673
+ * @return Math_BigInteger
1674
+ * @access public
1675
+ */
1676
+ function powMod($e, $n)
1677
+ {
1678
+ return $this->modPow($e, $n);
1679
+ }
1680
+
1681
+ /**
1682
+ * Sliding Window k-ary Modular Exponentiation
1683
+ *
1684
+ * Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} /
1685
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}. In a departure from those algorithims,
1686
+ * however, this function performs a modular reduction after every multiplication and squaring operation.
1687
+ * As such, this function has the same preconditions that the reductions being used do.
1688
+ *
1689
+ * @param Math_BigInteger $e
1690
+ * @param Math_BigInteger $n
1691
+ * @param Integer $mode
1692
+ * @return Math_BigInteger
1693
+ * @access private
1694
+ */
1695
+ function _slidingWindow($e, $n, $mode)
1696
+ {
1697
+ static $window_ranges = array(7, 25, 81, 241, 673, 1793); // from BigInteger.java's oddModPow function
1698
+ //static $window_ranges = array(0, 7, 36, 140, 450, 1303, 3529); // from MPM 7.3.1
1699
+
1700
+ $e_value = $e->value;
1701
+ $e_length = count($e_value) - 1;
1702
+ $e_bits = decbin($e_value[$e_length]);
1703
+ for ($i = $e_length - 1; $i >= 0; --$i) {
1704
+ $e_bits.= str_pad(decbin($e_value[$i]), 26, '0', STR_PAD_LEFT);
1705
+ }
1706
+
1707
+ $e_length = strlen($e_bits);
1708
+
1709
+ // calculate the appropriate window size.
1710
+ // $window_size == 3 if $window_ranges is between 25 and 81, for example.
1711
+ for ($i = 0, $window_size = 1; $e_length > $window_ranges[$i] && $i < count($window_ranges); ++$window_size, ++$i);
1712
+
1713
+ $n_value = $n->value;
1714
+
1715
+ // precompute $this^0 through $this^$window_size
1716
+ $powers = array();
1717
+ $powers[1] = $this->_prepareReduce($this->value, $n_value, $mode);
1718
+ $powers[2] = $this->_squareReduce($powers[1], $n_value, $mode);
1719
+
1720
+ // we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end
1721
+ // in a 1. ie. it's supposed to be odd.
1722
+ $temp = 1 << ($window_size - 1);
1723
+ for ($i = 1; $i < $temp; ++$i) {
1724
+ $i2 = $i << 1;
1725
+ $powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode);
1726
+ }
1727
+
1728
+ $result = array(1);
1729
+ $result = $this->_prepareReduce($result, $n_value, $mode);
1730
+
1731
+ for ($i = 0; $i < $e_length; ) {
1732
+ if ( !$e_bits[$i] ) {
1733
+ $result = $this->_squareReduce($result, $n_value, $mode);
1734
+ ++$i;
1735
+ } else {
1736
+ for ($j = $window_size - 1; $j > 0; --$j) {
1737
+ if ( !empty($e_bits[$i + $j]) ) {
1738
+ break;
1739
+ }
1740
+ }
1741
+
1742
+ for ($k = 0; $k <= $j; ++$k) {// eg. the length of substr($e_bits, $i, $j+1)
1743
+ $result = $this->_squareReduce($result, $n_value, $mode);
1744
+ }
1745
+
1746
+ $result = $this->_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode);
1747
+
1748
+ $i+=$j + 1;
1749
+ }
1750
+ }
1751
+
1752
+ $temp = new Math_BigInteger();
1753
+ $temp->value = $this->_reduce($result, $n_value, $mode);
1754
+
1755
+ return $temp;
1756
+ }
1757
+
1758
+ /**
1759
+ * Modular reduction
1760
+ *
1761
+ * For most $modes this will return the remainder.
1762
+ *
1763
+ * @see _slidingWindow()
1764
+ * @access private
1765
+ * @param Array $x
1766
+ * @param Array $n
1767
+ * @param Integer $mode
1768
+ * @return Array
1769
+ */
1770
+ function _reduce($x, $n, $mode)
1771
+ {
1772
+ switch ($mode) {
1773
+ case MATH_BIGINTEGER_MONTGOMERY:
1774
+ return $this->_montgomery($x, $n);
1775
+ case MATH_BIGINTEGER_BARRETT:
1776
+ return $this->_barrett($x, $n);
1777
+ case MATH_BIGINTEGER_POWEROF2:
1778
+ $lhs = new Math_BigInteger();
1779
+ $lhs->value = $x;
1780
+ $rhs = new Math_BigInteger();
1781
+ $rhs->value = $n;
1782
+ return $x->_mod2($n);
1783
+ case MATH_BIGINTEGER_CLASSIC:
1784
+ $lhs = new Math_BigInteger();
1785
+ $lhs->value = $x;
1786
+ $rhs = new Math_BigInteger();
1787
+ $rhs->value = $n;
1788
+ list(, $temp) = $lhs->divide($rhs);
1789
+ return $temp->value;
1790
+ case MATH_BIGINTEGER_NONE:
1791
+ return $x;
1792
+ default:
1793
+ // an invalid $mode was provided
1794
+ }
1795
+ }
1796
+
1797
+ /**
1798
+ * Modular reduction preperation
1799
+ *
1800
+ * @see _slidingWindow()
1801
+ * @access private
1802
+ * @param Array $x
1803
+ * @param Array $n
1804
+ * @param Integer $mode
1805
+ * @return Array
1806
+ */
1807
+ function _prepareReduce($x, $n, $mode)
1808
+ {
1809
+ if ($mode == MATH_BIGINTEGER_MONTGOMERY) {
1810
+ return $this->_prepMontgomery($x, $n);
1811
+ }
1812
+ return $this->_reduce($x, $n, $mode);
1813
+ }
1814
+
1815
+ /**
1816
+ * Modular multiply
1817
+ *
1818
+ * @see _slidingWindow()
1819
+ * @access private
1820
+ * @param Array $x
1821
+ * @param Array $y
1822
+ * @param Array $n
1823
+ * @param Integer $mode
1824
+ * @return Array
1825
+ */
1826
+ function _multiplyReduce($x, $y, $n, $mode)
1827
+ {
1828
+ if ($mode == MATH_BIGINTEGER_MONTGOMERY) {
1829
+ return $this->_montgomeryMultiply($x, $y, $n);
1830
+ }
1831
+ $temp = $this->_multiply($x, false, $y, false);
1832
+ return $this->_reduce($temp[MATH_BIGINTEGER_VALUE], $n, $mode);
1833
+ }
1834
+
1835
+ /**
1836
+ * Modular square
1837
+ *
1838
+ * @see _slidingWindow()
1839
+ * @access private
1840
+ * @param Array $x
1841
+ * @param Array $n
1842
+ * @param Integer $mode
1843
+ * @return Array
1844
+ */
1845
+ function _squareReduce($x, $n, $mode)
1846
+ {
1847
+ if ($mode == MATH_BIGINTEGER_MONTGOMERY) {
1848
+ return $this->_montgomeryMultiply($x, $x, $n);
1849
+ }
1850
+ return $this->_reduce($this->_square($x), $n, $mode);
1851
+ }
1852
+
1853
+ /**
1854
+ * Modulos for Powers of Two
1855
+ *
1856
+ * Calculates $x%$n, where $n = 2**$e, for some $e. Since this is basically the same as doing $x & ($n-1),
1857
+ * we'll just use this function as a wrapper for doing that.
1858
+ *
1859
+ * @see _slidingWindow()
1860
+ * @access private
1861
+ * @param Math_BigInteger
1862
+ * @return Math_BigInteger
1863
+ */
1864
+ function _mod2($n)
1865
+ {
1866
+ $temp = new Math_BigInteger();
1867
+ $temp->value = array(1);
1868
+ return $this->bitwise_and($n->subtract($temp));
1869
+ }
1870
+
1871
+ /**
1872
+ * Barrett Modular Reduction
1873
+ *
1874
+ * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} /
1875
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information. Modified slightly,
1876
+ * so as not to require negative numbers (initially, this script didn't support negative numbers).
1877
+ *
1878
+ * Employs "folding", as described at
1879
+ * {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}. To quote from
1880
+ * 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."
1881
+ *
1882
+ * Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that
1883
+ * usable on account of (1) its not using reasonable radix points as discussed in
1884
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable
1885
+ * radix points, it only works when there are an even number of digits in the denominator. The reason for (2) is that
1886
+ * (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
1887
+ * comments for details.
1888
+ *
1889
+ * @see _slidingWindow()
1890
+ * @access private
1891
+ * @param Array $n
1892
+ * @param Array $m
1893
+ * @return Array
1894
+ */
1895
+ function _barrett($n, $m)
1896
+ {
1897
+ static $cache = array(
1898
+ MATH_BIGINTEGER_VARIABLE => array(),
1899
+ MATH_BIGINTEGER_DATA => array()
1900
+ );
1901
+
1902
+ $m_length = count($m);
1903
+
1904
+ // if ($this->_compare($n, $this->_square($m)) >= 0) {
1905
+ if (count($n) > 2 * $m_length) {
1906
+ $lhs = new Math_BigInteger();
1907
+ $rhs = new Math_BigInteger();
1908
+ $lhs->value = $n;
1909
+ $rhs->value = $m;
1910
+ list(, $temp) = $lhs->divide($rhs);
1911
+ return $temp->value;
1912
+ }
1913
+
1914
+ // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced
1915
+ if ($m_length < 5) {
1916
+ return $this->_regularBarrett($n, $m);
1917
+ }
1918
+
1919
+ // n = 2 * m.length
1920
+
1921
+ if ( ($key = array_search($m, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
1922
+ $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
1923
+ $cache[MATH_BIGINTEGER_VARIABLE][] = $m;
1924
+
1925
+ $lhs = new Math_BigInteger();
1926
+ $lhs_value = &$lhs->value;
1927
+ $lhs_value = $this->_array_repeat(0, $m_length + ($m_length >> 1));
1928
+ $lhs_value[] = 1;
1929
+ $rhs = new Math_BigInteger();
1930
+ $rhs->value = $m;
1931
+
1932
+ list($u, $m1) = $lhs->divide($rhs);
1933
+ $u = $u->value;
1934
+ $m1 = $m1->value;
1935
+
1936
+ $cache[MATH_BIGINTEGER_DATA][] = array(
1937
+ 'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1)
1938
+ 'm1'=> $m1 // m.length
1939
+ );
1940
+ } else {
1941
+ extract($cache[MATH_BIGINTEGER_DATA][$key]);
1942
+ }
1943
+
1944
+ $cutoff = $m_length + ($m_length >> 1);
1945
+ $lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1)
1946
+ $msd = array_slice($n, $cutoff); // m.length >> 1
1947
+ $lsd = $this->_trim($lsd);
1948
+ $temp = $this->_multiply($msd, false, $m1, false);
1949
+ $n = $this->_add($lsd, false, $temp[MATH_BIGINTEGER_VALUE], false); // m.length + (m.length >> 1) + 1
1950
+
1951
+ if ($m_length & 1) {
1952
+ return $this->_regularBarrett($n[MATH_BIGINTEGER_VALUE], $m);
1953
+ }
1954
+
1955
+ // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2
1956
+ $temp = array_slice($n[MATH_BIGINTEGER_VALUE], $m_length - 1);
1957
+ // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2
1958
+ // if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1
1959
+ $temp = $this->_multiply($temp, false, $u, false);
1960
+ // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1
1961
+ // if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1)
1962
+ $temp = array_slice($temp[MATH_BIGINTEGER_VALUE], ($m_length >> 1) + 1);
1963
+ // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1
1964
+ // if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1)
1965
+ $temp = $this->_multiply($temp, false, $m, false);
1966
+
1967
+ // at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit
1968
+ // number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop
1969
+ // following this comment would loop a lot (hence our calling _regularBarrett() in that situation).
1970
+
1971
+ $result = $this->_subtract($n[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false);
1972
+
1973
+ while ($this->_compare($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $m, false) >= 0) {
1974
+ $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $m, false);
1975
+ }
1976
+
1977
+ return $result[MATH_BIGINTEGER_VALUE];
1978
+ }
1979
+
1980
+ /**
1981
+ * (Regular) Barrett Modular Reduction
1982
+ *
1983
+ * For numbers with more than four digits Math_BigInteger::_barrett() is faster. The difference between that and this
1984
+ * is that this function does not fold the denominator into a smaller form.
1985
+ *
1986
+ * @see _slidingWindow()
1987
+ * @access private
1988
+ * @param Array $x
1989
+ * @param Array $n
1990
+ * @return Array
1991
+ */
1992
+ function _regularBarrett($x, $n)
1993
+ {
1994
+ static $cache = array(
1995
+ MATH_BIGINTEGER_VARIABLE => array(),
1996
+ MATH_BIGINTEGER_DATA => array()
1997
+ );
1998
+
1999
+ $n_length = count($n);
2000
+
2001
+ if (count($x) > 2 * $n_length) {
2002
+ $lhs = new Math_BigInteger();
2003
+ $rhs = new Math_BigInteger();
2004
+ $lhs->value = $x;
2005
+ $rhs->value = $n;
2006
+ list(, $temp) = $lhs->divide($rhs);
2007
+ return $temp->value;
2008
+ }
2009
+
2010
+ if ( ($key = array_search($n, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
2011
+ $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
2012
+ $cache[MATH_BIGINTEGER_VARIABLE][] = $n;
2013
+ $lhs = new Math_BigInteger();
2014
+ $lhs_value = &$lhs->value;
2015
+ $lhs_value = $this->_array_repeat(0, 2 * $n_length);
2016
+ $lhs_value[] = 1;
2017
+ $rhs = new Math_BigInteger();
2018
+ $rhs->value = $n;
2019
+ list($temp, ) = $lhs->divide($rhs); // m.length
2020
+ $cache[MATH_BIGINTEGER_DATA][] = $temp->value;
2021
+ }
2022
+
2023
+ // 2 * m.length - (m.length - 1) = m.length + 1
2024
+ $temp = array_slice($x, $n_length - 1);
2025
+ // (m.length + 1) + m.length = 2 * m.length + 1
2026
+ $temp = $this->_multiply($temp, false, $cache[MATH_BIGINTEGER_DATA][$key], false);
2027
+ // (2 * m.length + 1) - (m.length - 1) = m.length + 2
2028
+ $temp = array_slice($temp[MATH_BIGINTEGER_VALUE], $n_length + 1);
2029
+
2030
+ // m.length + 1
2031
+ $result = array_slice($x, 0, $n_length + 1);
2032
+ // m.length + 1
2033
+ $temp = $this->_multiplyLower($temp, false, $n, false, $n_length + 1);
2034
+ // $temp == array_slice($temp->_multiply($temp, false, $n, false)->value, 0, $n_length + 1)
2035
+
2036
+ if ($this->_compare($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]) < 0) {
2037
+ $corrector_value = $this->_array_repeat(0, $n_length + 1);
2038
+ $corrector_value[] = 1;
2039
+ $result = $this->_add($result, false, $corrector, false);
2040
+ $result = $result[MATH_BIGINTEGER_VALUE];
2041
+ }
2042
+
2043
+ // at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits
2044
+ $result = $this->_subtract($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]);
2045
+ while ($this->_compare($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $n, false) > 0) {
2046
+ $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $n, false);
2047
+ }
2048
+
2049
+ return $result[MATH_BIGINTEGER_VALUE];
2050
+ }
2051
+
2052
+ /**
2053
+ * Performs long multiplication up to $stop digits
2054
+ *
2055
+ * If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved.
2056
+ *
2057
+ * @see _regularBarrett()
2058
+ * @param Array $x_value
2059
+ * @param Boolean $x_negative
2060
+ * @param Array $y_value
2061
+ * @param Boolean $y_negative
2062
+ * @return Array
2063
+ * @access private
2064
+ */
2065
+ function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop)
2066
+ {
2067
+ $x_length = count($x_value);
2068
+ $y_length = count($y_value);
2069
+
2070
+ if ( !$x_length || !$y_length ) { // a 0 is being multiplied
2071
+ return array(
2072
+ MATH_BIGINTEGER_VALUE => array(),
2073
+ MATH_BIGINTEGER_SIGN => false
2074
+ );
2075
+ }
2076
+
2077
+ if ( $x_length < $y_length ) {
2078
+ $temp = $x_value;
2079
+ $x_value = $y_value;
2080
+ $y_value = $temp;
2081
+
2082
+ $x_length = count($x_value);
2083
+ $y_length = count($y_value);
2084
+ }
2085
+
2086
+ $product_value = $this->_array_repeat(0, $x_length + $y_length);
2087
+
2088
+ // the following for loop could be removed if the for loop following it
2089
+ // (the one with nested for loops) initially set $i to 0, but
2090
+ // doing so would also make the result in one set of unnecessary adds,
2091
+ // since on the outermost loops first pass, $product->value[$k] is going
2092
+ // to always be 0
2093
+
2094
+ $carry = 0;
2095
+
2096
+ for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i
2097
+ $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
2098
+ $carry = (int) ($temp / 0x4000000);
2099
+ $product_value[$j] = (int) ($temp - 0x4000000 * $carry);
2100
+ }
2101
+
2102
+ if ($j < $stop) {
2103
+ $product_value[$j] = $carry;
2104
+ }
2105
+
2106
+ // the above for loop is what the previous comment was talking about. the
2107
+ // following for loop is the "one with nested for loops"
2108
+
2109
+ for ($i = 1; $i < $y_length; ++$i) {
2110
+ $carry = 0;
2111
+
2112
+ for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) {
2113
+ $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
2114
+ $carry = (int) ($temp / 0x4000000);
2115
+ $product_value[$k] = (int) ($temp - 0x4000000 * $carry);
2116
+ }
2117
+
2118
+ if ($k < $stop) {
2119
+ $product_value[$k] = $carry;
2120
+ }
2121
+ }
2122
+
2123
+ return array(
2124
+ MATH_BIGINTEGER_VALUE => $this->_trim($product_value),
2125
+ MATH_BIGINTEGER_SIGN => $x_negative != $y_negative
2126
+ );
2127
+ }
2128
+
2129
+ /**
2130
+ * Montgomery Modular Reduction
2131
+ *
2132
+ * ($x->_prepMontgomery($n))->_montgomery($n) yields $x % $n.
2133
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=170 MPM 6.3} provides insights on how this can be
2134
+ * improved upon (basically, by using the comba method). gcd($n, 2) must be equal to one for this function
2135
+ * to work correctly.
2136
+ *
2137
+ * @see _prepMontgomery()
2138
+ * @see _slidingWindow()
2139
+ * @access private
2140
+ * @param Array $x
2141
+ * @param Array $n
2142
+ * @return Array
2143
+ */
2144
+ function _montgomery($x, $n)
2145
+ {
2146
+ static $cache = array(
2147
+ MATH_BIGINTEGER_VARIABLE => array(),
2148
+ MATH_BIGINTEGER_DATA => array()
2149
+ );
2150
+
2151
+ if ( ($key = array_search($n, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
2152
+ $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
2153
+ $cache[MATH_BIGINTEGER_VARIABLE][] = $x;
2154
+ $cache[MATH_BIGINTEGER_DATA][] = $this->_modInverse67108864($n);
2155
+ }
2156
+
2157
+ $k = count($n);
2158
+
2159
+ $result = array(MATH_BIGINTEGER_VALUE => $x);
2160
+
2161
+ for ($i = 0; $i < $k; ++$i) {
2162
+ $temp = $result[MATH_BIGINTEGER_VALUE][$i] * $cache[MATH_BIGINTEGER_DATA][$key];
2163
+ $temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000)));
2164
+ $temp = $this->_regularMultiply(array($temp), $n);
2165
+ $temp = array_merge($this->_array_repeat(0, $i), $temp);
2166
+ $result = $this->_add($result[MATH_BIGINTEGER_VALUE], false, $temp, false);
2167
+ }
2168
+
2169
+ $result[MATH_BIGINTEGER_VALUE] = array_slice($result[MATH_BIGINTEGER_VALUE], $k);
2170
+
2171
+ if ($this->_compare($result, false, $n, false) >= 0) {
2172
+ $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], false, $n, false);
2173
+ }
2174
+
2175
+ return $result[MATH_BIGINTEGER_VALUE];
2176
+ }
2177
+
2178
+ /**
2179
+ * Montgomery Multiply
2180
+ *
2181
+ * Interleaves the montgomery reduction and long multiplication algorithms together as described in
2182
+ * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
2183
+ *
2184
+ * @see _prepMontgomery()
2185
+ * @see _montgomery()
2186
+ * @access private
2187
+ * @param Array $x
2188
+ * @param Array $y
2189
+ * @param Array $m
2190
+ * @return Array
2191
+ */
2192
+ function _montgomeryMultiply($x, $y, $m)
2193
+ {
2194
+ $temp = $this->_multiply($x, false, $y, false);
2195
+ return $this->_montgomery($temp[MATH_BIGINTEGER_VALUE], $m);
2196
+
2197
+ static $cache = array(
2198
+ MATH_BIGINTEGER_VARIABLE => array(),
2199
+ MATH_BIGINTEGER_DATA => array()
2200
+ );
2201
+
2202
+ if ( ($key = array_search($m, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
2203
+ $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
2204
+ $cache[MATH_BIGINTEGER_VARIABLE][] = $m;
2205
+ $cache[MATH_BIGINTEGER_DATA][] = $this->_modInverse67108864($m);
2206
+ }
2207
+
2208
+ $n = max(count($x), count($y), count($m));
2209
+ $x = array_pad($x, $n, 0);
2210
+ $y = array_pad($y, $n, 0);
2211
+ $m = array_pad($m, $n, 0);
2212
+ $a = array(MATH_BIGINTEGER_VALUE => $this->_array_repeat(0, $n + 1));
2213
+ for ($i = 0; $i < $n; ++$i) {
2214
+ $temp = $a[MATH_BIGINTEGER_VALUE][0] + $x[$i] * $y[0];
2215
+ $temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000)));
2216
+ $temp = $temp * $cache[MATH_BIGINTEGER_DATA][$key];
2217
+ $temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000)));
2218
+ $temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false);
2219
+ $a = $this->_add($a[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false);
2220
+ $a[MATH_BIGINTEGER_VALUE] = array_slice($a[MATH_BIGINTEGER_VALUE], 1);
2221
+ }
2222
+ if ($this->_compare($a[MATH_BIGINTEGER_VALUE], false, $m, false) >= 0) {
2223
+ $a = $this->_subtract($a[MATH_BIGINTEGER_VALUE], false, $m, false);
2224
+ }
2225
+ return $a[MATH_BIGINTEGER_VALUE];
2226
+ }
2227
+
2228
+ /**
2229
+ * Prepare a number for use in Montgomery Modular Reductions
2230
+ *
2231
+ * @see _montgomery()
2232
+ * @see _slidingWindow()
2233
+ * @access private
2234
+ * @param Array $x
2235
+ * @param Array $n
2236
+ * @return Array
2237
+ */
2238
+ function _prepMontgomery($x, $n)
2239
+ {
2240
+ $lhs = new Math_BigInteger();
2241
+ $lhs->value = array_merge($this->_array_repeat(0, count($n)), $x);
2242
+ $rhs = new Math_BigInteger();
2243
+ $rhs->value = $n;
2244
+
2245
+ list(, $temp) = $lhs->divide($rhs);
2246
+ return $temp->value;
2247
+ }
2248
+
2249
+ /**
2250
+ * Modular Inverse of a number mod 2**26 (eg. 67108864)
2251
+ *
2252
+ * Based off of the bnpInvDigit function implemented and justified in the following URL:
2253
+ *
2254
+ * {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js}
2255
+ *
2256
+ * The following URL provides more info:
2257
+ *
2258
+ * {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85}
2259
+ *
2260
+ * As for why we do all the bitmasking... strange things can happen when converting from floats to ints. For
2261
+ * instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields
2262
+ * int(-2147483648). To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't
2263
+ * auto-converted to floats. The outermost bitmask is present because without it, there's no guarantee that
2264
+ * the "residue" returned would be the so-called "common residue". We use fmod, in the last step, because the
2265
+ * maximum possible $x is 26 bits and the maximum $result is 16 bits. Thus, we have to be able to handle up to
2266
+ * 40 bits, which only 64-bit floating points will support.
2267
+ *
2268
+ * Thanks to Pedro Gimeno Fortea for input!
2269
+ *
2270
+ * @see _montgomery()
2271
+ * @access private
2272
+ * @param Array $x
2273
+ * @return Integer
2274
+ */
2275
+ function _modInverse67108864($x) // 2**26 == 67108864
2276
+ {
2277
+ $x = -$x[0];
2278
+ $result = $x & 0x3; // x**-1 mod 2**2
2279
+ $result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4
2280
+ $result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; // x**-1 mod 2**8
2281
+ $result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16
2282
+ $result = fmod($result * (2 - fmod($x * $result, 0x4000000)), 0x4000000); // x**-1 mod 2**26
2283
+ return $result & 0x3FFFFFF;
2284
+ }
2285
+
2286
+ /**
2287
+ * Calculates modular inverses.
2288
+ *
2289
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
2290
+ *
2291
+ * Here's an example:
2292
+ * <code>
2293
+ * <?php
2294
+ * include('Math/BigInteger.php');
2295
+ *
2296
+ * $a = new Math_BigInteger(30);
2297
+ * $b = new Math_BigInteger(17);
2298
+ *
2299
+ * $c = $a->modInverse($b);
2300
+ * echo $c->toString(); // outputs 4
2301
+ *
2302
+ * echo "\r\n";
2303
+ *
2304
+ * $d = $a->multiply($c);
2305
+ * list(, $d) = $d->divide($b);
2306
+ * echo $d; // outputs 1 (as per the definition of modular inverse)
2307
+ * ?>
2308
+ * </code>
2309
+ *
2310
+ * @param Math_BigInteger $n
2311
+ * @return mixed false, if no modular inverse exists, Math_BigInteger, otherwise.
2312
+ * @access public
2313
+ * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information.
2314
+ */
2315
+ function modInverse($n)
2316
+ {
2317
+ switch ( MATH_BIGINTEGER_MODE ) {
2318
+ case MATH_BIGINTEGER_MODE_GMP:
2319
+ $temp = new Math_BigInteger();
2320
+ $temp->value = gmp_invert($this->value, $n->value);
2321
+
2322
+ return ( $temp->value === false ) ? false : $this->_normalize($temp);
2323
+ }
2324
+
2325
+ static $zero, $one;
2326
+ if (!isset($zero)) {
2327
+ $zero = new Math_BigInteger();
2328
+ $one = new Math_BigInteger(1);
2329
+ }
2330
+
2331
+ // $x mod -$n == $x mod $n.
2332
+ $n = $n->abs();
2333
+
2334
+ if ($this->compare($zero) < 0) {
2335
+ $temp = $this->abs();
2336
+ $temp = $temp->modInverse($n);
2337
+ return $this->_normalize($n->subtract($temp));
2338
+ }
2339
+
2340
+ extract($this->extendedGCD($n));
2341
+
2342
+ if (!$gcd->equals($one)) {
2343
+ return false;
2344
+ }
2345
+
2346
+ $x = $x->compare($zero) < 0 ? $x->add($n) : $x;
2347
+
2348
+ return $this->compare($zero) < 0 ? $this->_normalize($n->subtract($x)) : $this->_normalize($x);
2349
+ }
2350
+
2351
+ /**
2352
+ * Calculates the greatest common divisor and B�zout's identity.
2353
+ *
2354
+ * Say you have 693 and 609. The GCD is 21. B�zout's identity states that there exist integers x and y such that
2355
+ * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which
2356
+ * combination is returned is dependant upon which mode is in use. See
2357
+ * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity B�zout's identity - Wikipedia} for more information.
2358
+ *
2359
+ * Here's an example:
2360
+ * <code>
2361
+ * <?php
2362
+ * include('Math/BigInteger.php');
2363
+ *
2364
+ * $a = new Math_BigInteger(693);
2365
+ * $b = new Math_BigInteger(609);
2366
+ *
2367
+ * extract($a->extendedGCD($b));
2368
+ *
2369
+ * echo $gcd->toString() . "\r\n"; // outputs 21
2370
+ * echo $a->toString() * $x->toString() + $b->toString() * $y->toString(); // outputs 21
2371
+ * ?>
2372
+ * </code>
2373
+ *
2374
+ * @param Math_BigInteger $n
2375
+ * @return Math_BigInteger
2376
+ * @access public
2377
+ * @internal Calculates the GCD using the binary xGCD algorithim described in
2378
+ * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes,
2379
+ * the more traditional algorithim requires "relatively costly multiple-precision divisions".
2380
+ */
2381
+ function extendedGCD($n)
2382
+ {
2383
+ switch ( MATH_BIGINTEGER_MODE ) {
2384
+ case MATH_BIGINTEGER_MODE_GMP:
2385
+ extract(gmp_gcdext($this->value, $n->value));
2386
+
2387
+ return array(
2388
+ 'gcd' => $this->_normalize(new Math_BigInteger($g)),
2389
+ 'x' => $this->_normalize(new Math_BigInteger($s)),
2390
+ 'y' => $this->_normalize(new Math_BigInteger($t))
2391
+ );
2392
+ case MATH_BIGINTEGER_MODE_BCMATH:
2393
+ // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works
2394
+ // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is,
2395
+ // the basic extended euclidean algorithim is what we're using.
2396
+
2397
+ $u = $this->value;
2398
+ $v = $n->value;
2399
+
2400
+ $a = '1';
2401
+ $b = '0';
2402
+ $c = '0';
2403
+ $d = '1';
2404
+
2405
+ while (bccomp($v, '0', 0) != 0) {
2406
+ $q = bcdiv($u, $v, 0);
2407
+
2408
+ $temp = $u;
2409
+ $u = $v;
2410
+ $v = bcsub($temp, bcmul($v, $q, 0), 0);
2411
+
2412
+ $temp = $a;
2413
+ $a = $c;
2414
+ $c = bcsub($temp, bcmul($a, $q, 0), 0);
2415
+
2416
+ $temp = $b;
2417
+ $b = $d;
2418
+ $d = bcsub($temp, bcmul($b, $q, 0), 0);
2419
+ }
2420
+
2421
+ return array(
2422
+ 'gcd' => $this->_normalize(new Math_BigInteger($u)),
2423
+ 'x' => $this->_normalize(new Math_BigInteger($a)),
2424
+ 'y' => $this->_normalize(new Math_BigInteger($b))
2425
+ );
2426
+ }
2427
+
2428
+ $y = $n->copy();
2429
+ $x = $this->copy();
2430
+ $g = new Math_BigInteger();
2431
+ $g->value = array(1);
2432
+
2433
+ while ( !(($x->value[0] & 1)|| ($y->value[0] & 1)) ) {
2434
+ $x->_rshift(1);
2435
+ $y->_rshift(1);
2436
+ $g->_lshift(1);
2437
+ }
2438
+
2439
+ $u = $x->copy();
2440
+ $v = $y->copy();
2441
+
2442
+ $a = new Math_BigInteger();
2443
+ $b = new Math_BigInteger();
2444
+ $c = new Math_BigInteger();
2445
+ $d = new Math_BigInteger();
2446
+
2447
+ $a->value = $d->value = $g->value = array(1);
2448
+ $b->value = $c->value = array();
2449
+
2450
+ while ( !empty($u->value) ) {
2451
+ while ( !($u->value[0] & 1) ) {
2452
+ $u->_rshift(1);
2453
+ if ( (!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1)) ) {
2454
+ $a = $a->add($y);
2455
+ $b = $b->subtract($x);
2456
+ }
2457
+ $a->_rshift(1);
2458
+ $b->_rshift(1);
2459
+ }
2460
+
2461
+ while ( !($v->value[0] & 1) ) {
2462
+ $v->_rshift(1);
2463
+ if ( (!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1)) ) {
2464
+ $c = $c->add($y);
2465
+ $d = $d->subtract($x);
2466
+ }
2467
+ $c->_rshift(1);
2468
+ $d->_rshift(1);
2469
+ }
2470
+
2471
+ if ($u->compare($v) >= 0) {
2472
+ $u = $u->subtract($v);
2473
+ $a = $a->subtract($c);
2474
+ $b = $b->subtract($d);
2475
+ } else {
2476
+ $v = $v->subtract($u);
2477
+ $c = $c->subtract($a);
2478
+ $d = $d->subtract($b);
2479
+ }
2480
+ }
2481
+
2482
+ return array(
2483
+ 'gcd' => $this->_normalize($g->multiply($v)),
2484
+ 'x' => $this->_normalize($c),
2485
+ 'y' => $this->_normalize($d)
2486
+ );
2487
+ }
2488
+
2489
+ /**
2490
+ * Calculates the greatest common divisor
2491
+ *
2492
+ * Say you have 693 and 609. The GCD is 21.
2493
+ *
2494
+ * Here's an example:
2495
+ * <code>
2496
+ * <?php
2497
+ * include('Math/BigInteger.php');
2498
+ *
2499
+ * $a = new Math_BigInteger(693);
2500
+ * $b = new Math_BigInteger(609);
2501
+ *
2502
+ * $gcd = a->extendedGCD($b);
2503
+ *
2504
+ * echo $gcd->toString() . "\r\n"; // outputs 21
2505
+ * ?>
2506
+ * </code>
2507
+ *
2508
+ * @param Math_BigInteger $n
2509
+ * @return Math_BigInteger
2510
+ * @access public
2511
+ */
2512
+ function gcd($n)
2513
+ {
2514
+ extract($this->extendedGCD($n));
2515
+ return $gcd;
2516
+ }
2517
+
2518
+ /**
2519
+ * Absolute value.
2520
+ *
2521
+ * @return Math_BigInteger
2522
+ * @access public
2523
+ */
2524
+ function abs()
2525
+ {
2526
+ $temp = new Math_BigInteger();
2527
+
2528
+ switch ( MATH_BIGINTEGER_MODE ) {
2529
+ case MATH_BIGINTEGER_MODE_GMP:
2530
+ $temp->value = gmp_abs($this->value);
2531
+ break;
2532
+ case MATH_BIGINTEGER_MODE_BCMATH:
2533
+ $temp->value = (bccomp($this->value, '0', 0) < 0) ? substr($this->value, 1) : $this->value;
2534
+ break;
2535
+ default:
2536
+ $temp->value = $this->value;
2537
+ }
2538
+
2539
+ return $temp;
2540
+ }
2541
+
2542
+ /**
2543
+ * Compares two numbers.
2544
+ *
2545
+ * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is
2546
+ * demonstrated thusly:
2547
+ *
2548
+ * $x > $y: $x->compare($y) > 0
2549
+ * $x < $y: $x->compare($y) < 0
2550
+ * $x == $y: $x->compare($y) == 0
2551
+ *
2552
+ * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y).
2553
+ *
2554
+ * @param Math_BigInteger $x
2555
+ * @return Integer < 0 if $this is less than $x; > 0 if $this is greater than $x, and 0 if they are equal.
2556
+ * @access public
2557
+ * @see equals()
2558
+ * @internal Could return $this->subtract($x), but that's not as fast as what we do do.
2559
+ */
2560
+ function compare($y)
2561
+ {
2562
+ switch ( MATH_BIGINTEGER_MODE ) {
2563
+ case MATH_BIGINTEGER_MODE_GMP:
2564
+ return gmp_cmp($this->value, $y->value);
2565
+ case MATH_BIGINTEGER_MODE_BCMATH:
2566
+ return bccomp($this->value, $y->value, 0);
2567
+ }
2568
+
2569
+ return $this->_compare($this->value, $this->is_negative, $y->value, $y->is_negative);
2570
+ }
2571
+
2572
+ /**
2573
+ * Compares two numbers.
2574
+ *
2575
+ * @param Array $x_value
2576
+ * @param Boolean $x_negative
2577
+ * @param Array $y_value
2578
+ * @param Boolean $y_negative
2579
+ * @return Integer
2580
+ * @see compare()
2581
+ * @access private
2582
+ */
2583
+ function _compare($x_value, $x_negative, $y_value, $y_negative)
2584
+ {
2585
+ if ( $x_negative != $y_negative ) {
2586
+ return ( !$x_negative && $y_negative ) ? 1 : -1;
2587
+ }
2588
+
2589
+ $result = $x_negative ? -1 : 1;
2590
+
2591
+ if ( count($x_value) != count($y_value) ) {
2592
+ return ( count($x_value) > count($y_value) ) ? $result : -$result;
2593
+ }
2594
+ $size = max(count($x_value), count($y_value));
2595
+
2596
+ $x_value = array_pad($x_value, $size, 0);
2597
+ $y_value = array_pad($y_value, $size, 0);
2598
+
2599
+ for ($i = count($x_value) - 1; $i >= 0; --$i) {
2600
+ if ($x_value[$i] != $y_value[$i]) {
2601
+ return ( $x_value[$i] > $y_value[$i] ) ? $result : -$result;
2602
+ }
2603
+ }
2604
+
2605
+ return 0;
2606
+ }
2607
+
2608
+ /**
2609
+ * Tests the equality of two numbers.
2610
+ *
2611
+ * If you need to see if one number is greater than or less than another number, use Math_BigInteger::compare()
2612
+ *
2613
+ * @param Math_BigInteger $x
2614
+ * @return Boolean
2615
+ * @access public
2616
+ * @see compare()
2617
+ */
2618
+ function equals($x)
2619
+ {
2620
+ switch ( MATH_BIGINTEGER_MODE ) {
2621
+ case MATH_BIGINTEGER_MODE_GMP:
2622
+ return gmp_cmp($this->value, $x->value) == 0;
2623
+ default:
2624
+ return $this->value === $x->value && $this->is_negative == $x->is_negative;
2625
+ }
2626
+ }
2627
+
2628
+ /**
2629
+ * Set Precision
2630
+ *
2631
+ * Some bitwise operations give different results depending on the precision being used. Examples include left
2632
+ * shift, not, and rotates.
2633
+ *
2634
+ * @param Math_BigInteger $x
2635
+ * @access public
2636
+ * @return Math_BigInteger
2637
+ */
2638
+ function setPrecision($bits)
2639
+ {
2640
+ $this->precision = $bits;
2641
+ if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ) {
2642
+ $this->bitmask = new Math_BigInteger(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256);
2643
+ } else {
2644
+ $this->bitmask = new Math_BigInteger(bcpow('2', $bits, 0));
2645
+ }
2646
+
2647
+ $temp = $this->_normalize($this);
2648
+ $this->value = $temp->value;
2649
+ }
2650
+
2651
+ /**
2652
+ * Logical And
2653
+ *
2654
+ * @param Math_BigInteger $x
2655
+ * @access public
2656
+ * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
2657
+ * @return Math_BigInteger
2658
+ */
2659
+ function bitwise_and($x)
2660
+ {
2661
+ switch ( MATH_BIGINTEGER_MODE ) {
2662
+ case MATH_BIGINTEGER_MODE_GMP:
2663
+ $temp = new Math_BigInteger();
2664
+ $temp->value = gmp_and($this->value, $x->value);
2665
+
2666
+ return $this->_normalize($temp);
2667
+ case MATH_BIGINTEGER_MODE_BCMATH:
2668
+ $left = $this->toBytes();
2669
+ $right = $x->toBytes();
2670
+
2671
+ $length = max(strlen($left), strlen($right));
2672
+
2673
+ $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
2674
+ $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
2675
+
2676
+ return $this->_normalize(new Math_BigInteger($left & $right, 256));
2677
+ }
2678
+
2679
+ $result = $this->copy();
2680
+
2681
+ $length = min(count($x->value), count($this->value));
2682
+
2683
+ $result->value = array_slice($result->value, 0, $length);
2684
+
2685
+ for ($i = 0; $i < $length; ++$i) {
2686
+ $result->value[$i] = $result->value[$i] & $x->value[$i];
2687
+ }
2688
+
2689
+ return $this->_normalize($result);
2690
+ }
2691
+
2692
+ /**
2693
+ * Logical Or
2694
+ *
2695
+ * @param Math_BigInteger $x
2696
+ * @access public
2697
+ * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
2698
+ * @return Math_BigInteger
2699
+ */
2700
+ function bitwise_or($x)
2701
+ {
2702
+ switch ( MATH_BIGINTEGER_MODE ) {
2703
+ case MATH_BIGINTEGER_MODE_GMP:
2704
+ $temp = new Math_BigInteger();
2705
+ $temp->value = gmp_or($this->value, $x->value);
2706
+
2707
+ return $this->_normalize($temp);
2708
+ case MATH_BIGINTEGER_MODE_BCMATH:
2709
+ $left = $this->toBytes();
2710
+ $right = $x->toBytes();
2711
+
2712
+ $length = max(strlen($left), strlen($right));
2713
+
2714
+ $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
2715
+ $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
2716
+
2717
+ return $this->_normalize(new Math_BigInteger($left | $right, 256));
2718
+ }
2719
+
2720
+ $length = max(count($this->value), count($x->value));
2721
+ $result = $this->copy();
2722
+ $result->value = array_pad($result->value, 0, $length);
2723
+ $x->value = array_pad($x->value, 0, $length);
2724
+
2725
+ for ($i = 0; $i < $length; ++$i) {
2726
+ $result->value[$i] = $this->value[$i] | $x->value[$i];
2727
+ }
2728
+
2729
+ return $this->_normalize($result);
2730
+ }
2731
+
2732
+ /**
2733
+ * Logical Exclusive-Or
2734
+ *
2735
+ * @param Math_BigInteger $x
2736
+ * @access public
2737
+ * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
2738
+ * @return Math_BigInteger
2739
+ */
2740
+ function bitwise_xor($x)
2741
+ {
2742
+ switch ( MATH_BIGINTEGER_MODE ) {
2743
+ case MATH_BIGINTEGER_MODE_GMP:
2744
+ $temp = new Math_BigInteger();
2745
+ $temp->value = gmp_xor($this->value, $x->value);
2746
+
2747
+ return $this->_normalize($temp);
2748
+ case MATH_BIGINTEGER_MODE_BCMATH:
2749
+ $left = $this->toBytes();
2750
+ $right = $x->toBytes();
2751
+
2752
+ $length = max(strlen($left), strlen($right));
2753
+
2754
+ $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
2755
+ $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
2756
+
2757
+ return $this->_normalize(new Math_BigInteger($left ^ $right, 256));
2758
+ }
2759
+
2760
+ $length = max(count($this->value), count($x->value));
2761
+ $result = $this->copy();
2762
+ $result->value = array_pad($result->value, 0, $length);
2763
+ $x->value = array_pad($x->value, 0, $length);
2764
+
2765
+ for ($i = 0; $i < $length; ++$i) {
2766
+ $result->value[$i] = $this->value[$i] ^ $x->value[$i];
2767
+ }
2768
+
2769
+ return $this->_normalize($result);
2770
+ }
2771
+
2772
+ /**
2773
+ * Logical Not
2774
+ *
2775
+ * @access public
2776
+ * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
2777
+ * @return Math_BigInteger
2778
+ */
2779
+ function bitwise_not()
2780
+ {
2781
+ // calculuate "not" without regard to $this->precision
2782
+ // (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0)
2783
+ $temp = $this->toBytes();
2784
+ $pre_msb = decbin(ord($temp[0]));
2785
+ $temp = ~$temp;
2786
+ $msb = decbin(ord($temp[0]));
2787
+ if (strlen($msb) == 8) {
2788
+ $msb = substr($msb, strpos($msb, '0'));
2789
+ }
2790
+ $temp[0] = chr(bindec($msb));
2791
+
2792
+ // see if we need to add extra leading 1's
2793
+ $current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8;
2794
+ $new_bits = $this->precision - $current_bits;
2795
+ if ($new_bits <= 0) {
2796
+ return $this->_normalize(new Math_BigInteger($temp, 256));
2797
+ }
2798
+
2799
+ // generate as many leading 1's as we need to.
2800
+ $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3);
2801
+ $this->_base256_lshift($leading_ones, $current_bits);
2802
+
2803
+ $temp = str_pad($temp, ceil($this->bits / 8), chr(0), STR_PAD_LEFT);
2804
+
2805
+ return $this->_normalize(new Math_BigInteger($leading_ones | $temp, 256));
2806
+ }
2807
+
2808
+ /**
2809
+ * Logical Right Shift
2810
+ *
2811
+ * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
2812
+ *
2813
+ * @param Integer $shift
2814
+ * @return Math_BigInteger
2815
+ * @access public
2816
+ * @internal The only version that yields any speed increases is the internal version.
2817
+ */
2818
+ function bitwise_rightShift($shift)
2819
+ {
2820
+ $temp = new Math_BigInteger();
2821
+
2822
+ switch ( MATH_BIGINTEGER_MODE ) {
2823
+ case MATH_BIGINTEGER_MODE_GMP:
2824
+ static $two;
2825
+
2826
+ if (!isset($two)) {
2827
+ $two = gmp_init('2');
2828
+ }
2829
+
2830
+ $temp->value = gmp_div_q($this->value, gmp_pow($two, $shift));
2831
+
2832
+ break;
2833
+ case MATH_BIGINTEGER_MODE_BCMATH:
2834
+ $temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0);
2835
+
2836
+ break;
2837
+ default: // could just replace _lshift with this, but then all _lshift() calls would need to be rewritten
2838
+ // and I don't want to do that...
2839
+ $temp->value = $this->value;
2840
+ $temp->_rshift($shift);
2841
+ }
2842
+
2843
+ return $this->_normalize($temp);
2844
+ }
2845
+
2846
+ /**
2847
+ * Logical Left Shift
2848
+ *
2849
+ * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
2850
+ *
2851
+ * @param Integer $shift
2852
+ * @return Math_BigInteger
2853
+ * @access public
2854
+ * @internal The only version that yields any speed increases is the internal version.
2855
+ */
2856
+ function bitwise_leftShift($shift)
2857
+ {
2858
+ $temp = new Math_BigInteger();
2859
+
2860
+ switch ( MATH_BIGINTEGER_MODE ) {
2861
+ case MATH_BIGINTEGER_MODE_GMP:
2862
+ static $two;
2863
+
2864
+ if (!isset($two)) {
2865
+ $two = gmp_init('2');
2866
+ }
2867
+
2868
+ $temp->value = gmp_mul($this->value, gmp_pow($two, $shift));
2869
+
2870
+ break;
2871
+ case MATH_BIGINTEGER_MODE_BCMATH:
2872
+ $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0);
2873
+
2874
+ break;
2875
+ default: // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten
2876
+ // and I don't want to do that...
2877
+ $temp->value = $this->value;
2878
+ $temp->_lshift($shift);
2879
+ }
2880
+
2881
+ return $this->_normalize($temp);
2882
+ }
2883
+
2884
+ /**
2885
+ * Logical Left Rotate
2886
+ *
2887
+ * Instead of the top x bits being dropped they're appended to the shifted bit string.
2888
+ *
2889
+ * @param Integer $shift
2890
+ * @return Math_BigInteger
2891
+ * @access public
2892
+ */
2893
+ function bitwise_leftRotate($shift)
2894
+ {
2895
+ $bits = $this->toBytes();
2896
+
2897
+ if ($this->precision > 0) {
2898
+ $precision = $this->precision;
2899
+ if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) {
2900
+ $mask = $this->bitmask->subtract(new Math_BigInteger(1));
2901
+ $mask = $mask->toBytes();
2902
+ } else {
2903
+ $mask = $this->bitmask->toBytes();
2904
+ }
2905
+ } else {
2906
+ $temp = ord($bits[0]);
2907
+ for ($i = 0; $temp >> $i; ++$i);
2908
+ $precision = 8 * strlen($bits) - 8 + $i;
2909
+ $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3);
2910
+ }
2911
+
2912
+ if ($shift < 0) {
2913
+ $shift+= $precision;
2914
+ }
2915
+ $shift%= $precision;
2916
+
2917
+ if (!$shift) {
2918
+ return $this->copy();
2919
+ }
2920
+
2921
+ $left = $this->bitwise_leftShift($shift);
2922
+ $left = $left->bitwise_and(new Math_BigInteger($mask, 256));
2923
+ $right = $this->bitwise_rightShift($precision - $shift);
2924
+ $result = MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right);
2925
+ return $this->_normalize($result);
2926
+ }
2927
+
2928
+ /**
2929
+ * Logical Right Rotate
2930
+ *
2931
+ * Instead of the bottom x bits being dropped they're prepended to the shifted bit string.
2932
+ *
2933
+ * @param Integer $shift
2934
+ * @return Math_BigInteger
2935
+ * @access public
2936
+ */
2937
+ function bitwise_rightRotate($shift)
2938
+ {
2939
+ return $this->bitwise_leftRotate(-$shift);
2940
+ }
2941
+
2942
+ /**
2943
+ * Set random number generator function
2944
+ *
2945
+ * $generator should be the name of a random generating function whose first parameter is the minimum
2946
+ * value and whose second parameter is the maximum value. If this function needs to be seeded, it should
2947
+ * be seeded prior to calling Math_BigInteger::random() or Math_BigInteger::randomPrime()
2948
+ *
2949
+ * If the random generating function is not explicitly set, it'll be assumed to be mt_rand().
2950
+ *
2951
+ * @see random()
2952
+ * @see randomPrime()
2953
+ * @param optional String $generator
2954
+ * @access public
2955
+ */
2956
+ function setRandomGenerator($generator)
2957
+ {
2958
+ $this->generator = $generator;
2959
+ }
2960
+
2961
+ /**
2962
+ * Generate a random number
2963
+ *
2964
+ * @param optional Integer $min
2965
+ * @param optional Integer $max
2966
+ * @return Math_BigInteger
2967
+ * @access public
2968
+ */
2969
+ function random($min = false, $max = false)
2970
+ {
2971
+ if ($min === false) {
2972
+ $min = new Math_BigInteger(0);
2973
+ }
2974
+
2975
+ if ($max === false) {
2976
+ $max = new Math_BigInteger(0x7FFFFFFF);
2977
+ }
2978
+
2979
+ $compare = $max->compare($min);
2980
+
2981
+ if (!$compare) {
2982
+ return $this->_normalize($min);
2983
+ } else if ($compare < 0) {
2984
+ // if $min is bigger then $max, swap $min and $max
2985
+ $temp = $max;
2986
+ $max = $min;
2987
+ $min = $temp;
2988
+ }
2989
+
2990
+ $generator = $this->generator;
2991
+
2992
+ $max = $max->subtract($min);
2993
+ $max = ltrim($max->toBytes(), chr(0));
2994
+ $size = strlen($max) - 1;
2995
+ $random = '';
2996
+
2997
+ $bytes = $size & 1;
2998
+ for ($i = 0; $i < $bytes; ++$i) {
2999
+ $random.= chr($generator(0, 255));
3000
+ }
3001
+
3002
+ $blocks = $size >> 1;
3003
+ for ($i = 0; $i < $blocks; ++$i) {
3004
+ // mt_rand(-2147483648, 0x7FFFFFFF) always produces -2147483648 on some systems
3005
+ $random.= pack('n', $generator(0, 0xFFFF));
3006
+ }
3007
+
3008
+ $temp = new Math_BigInteger($random, 256);
3009
+ if ($temp->compare(new Math_BigInteger(substr($max, 1), 256)) > 0) {
3010
+ $random = chr($generator(0, ord($max[0]) - 1)) . $random;
3011
+ } else {
3012
+ $random = chr($generator(0, ord($max[0]) )) . $random;
3013
+ }
3014
+
3015
+ $random = new Math_BigInteger($random, 256);
3016
+
3017
+ return $this->_normalize($random->add($min));
3018
+ }
3019
+
3020
+ /**
3021
+ * Generate a random prime number.
3022
+ *
3023
+ * If there's not a prime within the given range, false will be returned. If more than $timeout seconds have elapsed,
3024
+ * give up and return false.
3025
+ *
3026
+ * @param optional Integer $min
3027
+ * @param optional Integer $max
3028
+ * @param optional Integer $timeout
3029
+ * @return Math_BigInteger
3030
+ * @access public
3031
+ * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}.
3032
+ */
3033
+ function randomPrime($min = false, $max = false, $timeout = false)
3034
+ {
3035
+ $compare = $max->compare($min);
3036
+
3037
+ if (!$compare) {
3038
+ return $min;
3039
+ } else if ($compare < 0) {
3040
+ // if $min is bigger then $max, swap $min and $max
3041
+ $temp = $max;
3042
+ $max = $min;
3043
+ $min = $temp;
3044
+ }
3045
+
3046
+ // gmp_nextprime() requires PHP 5 >= 5.2.0 per <http://php.net/gmp-nextprime>.
3047
+ if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP && function_exists('gmp_nextprime') ) {
3048
+ // we don't rely on Math_BigInteger::random()'s min / max when gmp_nextprime() is being used since this function
3049
+ // does its own checks on $max / $min when gmp_nextprime() is used. When gmp_nextprime() is not used, however,
3050
+ // the same $max / $min checks are not performed.
3051
+ if ($min === false) {
3052
+ $min = new Math_BigInteger(0);
3053
+ }
3054
+
3055
+ if ($max === false) {
3056
+ $max = new Math_BigInteger(0x7FFFFFFF);
3057
+ }
3058
+
3059
+ $x = $this->random($min, $max);
3060
+
3061
+ $x->value = gmp_nextprime($x->value);
3062
+
3063
+ if ($x->compare($max) <= 0) {
3064
+ return $x;
3065
+ }
3066
+
3067
+ $x->value = gmp_nextprime($min->value);
3068
+
3069
+ if ($x->compare($max) <= 0) {
3070
+ return $x;
3071
+ }
3072
+
3073
+ return false;
3074
+ }
3075
+
3076
+ static $one, $two;
3077
+ if (!isset($one)) {
3078
+ $one = new Math_BigInteger(1);
3079
+ $two = new Math_BigInteger(2);
3080
+ }
3081
+
3082
+ $start = time();
3083
+
3084
+ $x = $this->random($min, $max);
3085
+ if ($x->equals($two)) {
3086
+ return $x;
3087
+ }
3088
+
3089
+ $x->_make_odd();
3090
+ if ($x->compare($max) > 0) {
3091
+ // if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range
3092
+ if ($min->equals($max)) {
3093
+ return false;
3094
+ }
3095
+ $x = $min->copy();
3096
+ $x->_make_odd();
3097
+ }
3098
+
3099
+ $initial_x = $x->copy();
3100
+
3101
+ while (true) {
3102
+ if ($timeout !== false && time() - $start > $timeout) {
3103
+ return false;
3104
+ }
3105
+
3106
+ if ($x->isPrime()) {
3107
+ return $x;
3108
+ }
3109
+
3110
+ $x = $x->add($two);
3111
+
3112
+ if ($x->compare($max) > 0) {
3113
+ $x = $min->copy();
3114
+ if ($x->equals($two)) {
3115
+ return $x;
3116
+ }
3117
+ $x->_make_odd();
3118
+ }
3119
+
3120
+ if ($x->equals($initial_x)) {
3121
+ return false;
3122
+ }
3123
+ }
3124
+ }
3125
+
3126
+ /**
3127
+ * Make the current number odd
3128
+ *
3129
+ * If the current number is odd it'll be unchanged. If it's even, one will be added to it.
3130
+ *
3131
+ * @see randomPrime()
3132
+ * @access private
3133
+ */
3134
+ function _make_odd()
3135
+ {
3136
+ switch ( MATH_BIGINTEGER_MODE ) {
3137
+ case MATH_BIGINTEGER_MODE_GMP:
3138
+ gmp_setbit($this->value, 0);
3139
+ break;
3140
+ case MATH_BIGINTEGER_MODE_BCMATH:
3141
+ if ($this->value[strlen($this->value) - 1] % 2 == 0) {
3142
+ $this->value = bcadd($this->value, '1');
3143
+ }
3144
+ break;
3145
+ default:
3146
+ $this->value[0] |= 1;
3147
+ }
3148
+ }
3149
+
3150
+ /**
3151
+ * Checks a numer to see if it's prime
3152
+ *
3153
+ * Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the
3154
+ * $t parameter is distributability. Math_BigInteger::randomPrime() can be distributed accross multiple pageloads
3155
+ * on a website instead of just one.
3156
+ *
3157
+ * @param optional Integer $t
3158
+ * @return Boolean
3159
+ * @access public
3160
+ * @internal Uses the
3161
+ * {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}. See
3162
+ * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24}.
3163
+ */
3164
+ function isPrime($t = false)
3165
+ {
3166
+ $length = strlen($this->toBytes());
3167
+
3168
+ if (!$t) {
3169
+ // see HAC 4.49 "Note (controlling the error probability)"
3170
+ if ($length >= 163) { $t = 2; } // floor(1300 / 8)
3171
+ else if ($length >= 106) { $t = 3; } // floor( 850 / 8)
3172
+ else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8)
3173
+ else if ($length >= 68 ) { $t = 5; } // floor( 550 / 8)
3174
+ else if ($length >= 56 ) { $t = 6; } // floor( 450 / 8)
3175
+ else if ($length >= 50 ) { $t = 7; } // floor( 400 / 8)
3176
+ else if ($length >= 43 ) { $t = 8; } // floor( 350 / 8)
3177
+ else if ($length >= 37 ) { $t = 9; } // floor( 300 / 8)
3178
+ else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8)
3179
+ else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8)
3180
+ else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8)
3181
+ else { $t = 27; }
3182
+ }
3183
+
3184
+ // ie. gmp_testbit($this, 0)
3185
+ // ie. isEven() or !isOdd()
3186
+ switch ( MATH_BIGINTEGER_MODE ) {
3187
+ case MATH_BIGINTEGER_MODE_GMP:
3188
+ return gmp_prob_prime($this->value, $t) != 0;
3189
+ case MATH_BIGINTEGER_MODE_BCMATH:
3190
+ if ($this->value === '2') {
3191
+ return true;
3192
+ }
3193
+ if ($this->value[strlen($this->value) - 1] % 2 == 0) {
3194
+ return false;
3195
+ }
3196
+ break;
3197
+ default:
3198
+ if ($this->value == array(2)) {
3199
+ return true;
3200
+ }
3201
+ if (~$this->value[0] & 1) {
3202
+ return false;
3203
+ }
3204
+ }
3205
+
3206
+ static $primes, $zero, $one, $two;
3207
+
3208
+ if (!isset($primes)) {
3209
+ $primes = array(
3210
+ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59,
3211
+ 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137,
3212
+ 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
3213
+ 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
3214
+ 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
3215
+ 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
3216
+ 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617,
3217
+ 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727,
3218
+ 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829,
3219
+ 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947,
3220
+ 953, 967, 971, 977, 983, 991, 997
3221
+ );
3222
+
3223
+ if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL ) {
3224
+ for ($i = 0; $i < count($primes); ++$i) {
3225
+ $primes[$i] = new Math_BigInteger($primes[$i]);
3226
+ }
3227
+ }
3228
+
3229
+ $zero = new Math_BigInteger();
3230
+ $one = new Math_BigInteger(1);
3231
+ $two = new Math_BigInteger(2);
3232
+ }
3233
+
3234
+ if ($this->equals($one)) {
3235
+ return false;
3236
+ }
3237
+
3238
+ // see HAC 4.4.1 "Random search for probable primes"
3239
+ if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL ) {
3240
+ foreach ($primes as $prime) {
3241
+ list(, $r) = $this->divide($prime);
3242
+ if ($r->equals($zero)) {
3243
+ return $this->equals($prime);
3244
+ }
3245
+ }
3246
+ } else {
3247
+ $value = $this->value;
3248
+ foreach ($primes as $prime) {
3249
+ list(, $r) = $this->_divide_digit($value, $prime);
3250
+ if (!$r) {
3251
+ return count($value) == 1 && $value[0] == $prime;
3252
+ }
3253
+ }
3254
+ }
3255
+
3256
+ $n = $this->copy();
3257
+ $n_1 = $n->subtract($one);
3258
+ $n_2 = $n->subtract($two);
3259
+
3260
+ $r = $n_1->copy();
3261
+ $r_value = $r->value;
3262
+ // ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
3263
+ if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) {
3264
+ $s = 0;
3265
+ // if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals($one) check earlier
3266
+ while ($r->value[strlen($r->value) - 1] % 2 == 0) {
3267
+ $r->value = bcdiv($r->value, '2', 0);
3268
+ ++$s;
3269
+ }
3270
+ } else {
3271
+ for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) {
3272
+ $temp = ~$r_value[$i] & 0xFFFFFF;
3273
+ for ($j = 1; ($temp >> $j) & 1; ++$j);
3274
+ if ($j != 25) {
3275
+ break;
3276
+ }
3277
+ }
3278
+ $s = 26 * $i + $j - 1;
3279
+ $r->_rshift($s);
3280
+ }
3281
+
3282
+ for ($i = 0; $i < $t; ++$i) {
3283
+ $a = $this->random($two, $n_2);
3284
+ $y = $a->modPow($r, $n);
3285
+
3286
+ if (!$y->equals($one) && !$y->equals($n_1)) {
3287
+ for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) {
3288
+ $y = $y->modPow($two, $n);
3289
+ if ($y->equals($one)) {
3290
+ return false;
3291
+ }
3292
+ }
3293
+
3294
+ if (!$y->equals($n_1)) {
3295
+ return false;
3296
+ }
3297
+ }
3298
+ }
3299
+ return true;
3300
+ }
3301
+
3302
+ /**
3303
+ * Logical Left Shift
3304
+ *
3305
+ * Shifts BigInteger's by $shift bits.
3306
+ *
3307
+ * @param Integer $shift
3308
+ * @access private
3309
+ */
3310
+ function _lshift($shift)
3311
+ {
3312
+ if ( $shift == 0 ) {
3313
+ return;
3314
+ }
3315
+
3316
+ $num_digits = (int) ($shift / 26);
3317
+ $shift %= 26;
3318
+ $shift = 1 << $shift;
3319
+
3320
+ $carry = 0;
3321
+
3322
+ for ($i = 0; $i < count($this->value); ++$i) {
3323
+ $temp = $this->value[$i] * $shift + $carry;
3324
+ $carry = (int) ($temp / 0x4000000);
3325
+ $this->value[$i] = (int) ($temp - $carry * 0x4000000);
3326
+ }
3327
+
3328
+ if ( $carry ) {
3329
+ $this->value[] = $carry;
3330
+ }
3331
+
3332
+ while ($num_digits--) {
3333
+ array_unshift($this->value, 0);
3334
+ }
3335
+ }
3336
+
3337
+ /**
3338
+ * Logical Right Shift
3339
+ *
3340
+ * Shifts BigInteger's by $shift bits.
3341
+ *
3342
+ * @param Integer $shift
3343
+ * @access private
3344
+ */
3345
+ function _rshift($shift)
3346
+ {
3347
+ if ($shift == 0) {
3348
+ return;
3349
+ }
3350
+
3351
+ $num_digits = (int) ($shift / 26);
3352
+ $shift %= 26;
3353
+ $carry_shift = 26 - $shift;
3354
+ $carry_mask = (1 << $shift) - 1;
3355
+
3356
+ if ( $num_digits ) {
3357
+ $this->value = array_slice($this->value, $num_digits);
3358
+ }
3359
+
3360
+ $carry = 0;
3361
+
3362
+ for ($i = count($this->value) - 1; $i >= 0; --$i) {
3363
+ $temp = $this->value[$i] >> $shift | $carry;
3364
+ $carry = ($this->value[$i] & $carry_mask) << $carry_shift;
3365
+ $this->value[$i] = $temp;
3366
+ }
3367
+
3368
+ $this->value = $this->_trim($this->value);
3369
+ }
3370
+
3371
+ /**
3372
+ * Normalize
3373
+ *
3374
+ * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
3375
+ *
3376
+ * @param Math_BigInteger
3377
+ * @return Math_BigInteger
3378
+ * @see _trim()
3379
+ * @access private
3380
+ */
3381
+ function _normalize($result)
3382
+ {
3383
+ $result->precision = $this->precision;
3384
+ $result->bitmask = $this->bitmask;
3385
+
3386
+ switch ( MATH_BIGINTEGER_MODE ) {
3387
+ case MATH_BIGINTEGER_MODE_GMP:
3388
+ if (!empty($result->bitmask->value)) {
3389
+ $result->value = gmp_and($result->value, $result->bitmask->value);
3390
+ }
3391
+
3392
+ return $result;
3393
+ case MATH_BIGINTEGER_MODE_BCMATH:
3394
+ if (!empty($result->bitmask->value)) {
3395
+ $result->value = bcmod($result->value, $result->bitmask->value);
3396
+ }
3397
+
3398
+ return $result;
3399
+ }
3400
+
3401
+ $value = &$result->value;
3402
+
3403
+ if ( !count($value) ) {
3404
+ return $result;
3405
+ }
3406
+
3407
+ $value = $this->_trim($value);
3408
+
3409
+ if (!empty($result->bitmask->value)) {
3410
+ $length = min(count($value), count($this->bitmask->value));
3411
+ $value = array_slice($value, 0, $length);
3412
+
3413
+ for ($i = 0; $i < $length; ++$i) {
3414
+ $value[$i] = $value[$i] & $this->bitmask->value[$i];
3415
+ }
3416
+ }
3417
+
3418
+ return $result;
3419
+ }
3420
+
3421
+ /**
3422
+ * Trim
3423
+ *
3424
+ * Removes leading zeros
3425
+ *
3426
+ * @return Math_BigInteger
3427
+ * @access private
3428
+ */
3429
+ function _trim($value)
3430
+ {
3431
+ for ($i = count($value) - 1; $i >= 0; --$i) {
3432
+ if ( $value[$i] ) {
3433
+ break;
3434
+ }
3435
+ unset($value[$i]);
3436
+ }
3437
+
3438
+ return $value;
3439
+ }
3440
+
3441
+ /**
3442
+ * Array Repeat
3443
+ *
3444
+ * @param $input Array
3445
+ * @param $multiplier mixed
3446
+ * @return Array
3447
+ * @access private
3448
+ */
3449
+ function _array_repeat($input, $multiplier)
3450
+ {
3451
+ return ($multiplier) ? array_fill(0, $multiplier, $input) : array();
3452
+ }
3453
+
3454
+ /**
3455
+ * Logical Left Shift
3456
+ *
3457
+ * Shifts binary strings $shift bits, essentially multiplying by 2**$shift.
3458
+ *
3459
+ * @param $x String
3460
+ * @param $shift Integer
3461
+ * @return String
3462
+ * @access private
3463
+ */
3464
+ function _base256_lshift(&$x, $shift)
3465
+ {
3466
+ if ($shift == 0) {
3467
+ return;
3468
+ }
3469
+
3470
+ $num_bytes = $shift >> 3; // eg. floor($shift/8)
3471
+ $shift &= 7; // eg. $shift % 8
3472
+
3473
+ $carry = 0;
3474
+ for ($i = strlen($x) - 1; $i >= 0; --$i) {
3475
+ $temp = ord($x[$i]) << $shift | $carry;
3476
+ $x[$i] = chr($temp);
3477
+ $carry = $temp >> 8;
3478
+ }
3479
+ $carry = ($carry != 0) ? chr($carry) : '';
3480
+ $x = $carry . $x . str_repeat(chr(0), $num_bytes);
3481
+ }
3482
+
3483
+ /**
3484
+ * Logical Right Shift
3485
+ *
3486
+ * Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder.
3487
+ *
3488
+ * @param $x String
3489
+ * @param $shift Integer
3490
+ * @return String
3491
+ * @access private
3492
+ */
3493
+ function _base256_rshift(&$x, $shift)
3494
+ {
3495
+ if ($shift == 0) {
3496
+ $x = ltrim($x, chr(0));
3497
+ return '';
3498
+ }
3499
+
3500
+ $num_bytes = $shift >> 3; // eg. floor($shift/8)
3501
+ $shift &= 7; // eg. $shift % 8
3502
+
3503
+ $remainder = '';
3504
+ if ($num_bytes) {
3505
+ $start = $num_bytes > strlen($x) ? -strlen($x) : -$num_bytes;
3506
+ $remainder = substr($x, $start);
3507
+ $x = substr($x, 0, -$num_bytes);
3508
+ }
3509
+
3510
+ $carry = 0;
3511
+ $carry_shift = 8 - $shift;
3512
+ for ($i = 0; $i < strlen($x); ++$i) {
3513
+ $temp = (ord($x[$i]) >> $shift) | $carry;
3514
+ $carry = (ord($x[$i]) << $carry_shift) & 0xFF;
3515
+ $x[$i] = chr($temp);
3516
+ }
3517
+ $x = ltrim($x, chr(0));
3518
+
3519
+ $remainder = chr($carry >> $carry_shift) . $remainder;
3520
+
3521
+ return ltrim($remainder, chr(0));
3522
+ }
3523
+
3524
+ // one quirk about how the following functions are implemented is that PHP defines N to be an unsigned long
3525
+ // at 32-bits, while java's longs are 64-bits.
3526
+
3527
+ /**
3528
+ * Converts 32-bit integers to bytes.
3529
+ *
3530
+ * @param Integer $x
3531
+ * @return String
3532
+ * @access private
3533
+ */
3534
+ function _int2bytes($x)
3535
+ {
3536
+ return ltrim(pack('N', $x), chr(0));
3537
+ }
3538
+
3539
+ /**
3540
+ * Converts bytes to 32-bit integers
3541
+ *
3542
+ * @param String $x
3543
+ * @return Integer
3544
+ * @access private
3545
+ */
3546
+ function _bytes2int($x)
3547
+ {
3548
+ $temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT));
3549
+ return $temp['int'];
3550
+ }
3551
+ }
phpseclib/Net/SFTP.php ADDED
@@ -0,0 +1,1955 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP implementation of SFTP.
6
+ *
7
+ * PHP versions 4 and 5
8
+ *
9
+ * Currently only supports SFTPv3, which, according to wikipedia.org, "is the most widely used version,
10
+ * implemented by the popular OpenSSH SFTP server". If you want SFTPv4/5/6 support, provide me with access
11
+ * to an SFTPv4/5/6 server.
12
+ *
13
+ * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}.
14
+ *
15
+ * Here's a short example of how to use this library:
16
+ * <code>
17
+ * <?php
18
+ * include('Net/SFTP.php');
19
+ *
20
+ * $sftp = new Net_SFTP('www.domain.tld');
21
+ * if (!$sftp->login('username', 'password')) {
22
+ * exit('Login Failed');
23
+ * }
24
+ *
25
+ * echo $sftp->pwd() . "\r\n";
26
+ * $sftp->put('filename.ext', 'hello, world!');
27
+ * print_r($sftp->nlist());
28
+ * ?>
29
+ * </code>
30
+ *
31
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
32
+ * of this software and associated documentation files (the "Software"), to deal
33
+ * in the Software without restriction, including without limitation the rights
34
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
35
+ * copies of the Software, and to permit persons to whom the Software is
36
+ * furnished to do so, subject to the following conditions:
37
+ *
38
+ * The above copyright notice and this permission notice shall be included in
39
+ * all copies or substantial portions of the Software.
40
+ *
41
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
44
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
46
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
47
+ * THE SOFTWARE.
48
+ *
49
+ * @category Net
50
+ * @package Net_SFTP
51
+ * @author Jim Wigginton <terrafrost@php.net>
52
+ * @copyright MMIX Jim Wigginton
53
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
54
+ * @link http://phpseclib.sourceforge.net
55
+ */
56
+
57
+ /**
58
+ * Include Net_SSH2
59
+ */
60
+ require_once('Net/SSH2.php');
61
+
62
+ /**#@+
63
+ * @access public
64
+ * @see Net_SFTP::getLog()
65
+ */
66
+ /**
67
+ * Returns the message numbers
68
+ */
69
+ define('NET_SFTP_LOG_SIMPLE', NET_SSH2_LOG_SIMPLE);
70
+ /**
71
+ * Returns the message content
72
+ */
73
+ define('NET_SFTP_LOG_COMPLEX', NET_SSH2_LOG_COMPLEX);
74
+ /**
75
+ * Outputs the message content in real-time.
76
+ */
77
+ define('NET_SFTP_LOG_REALTIME', 3);
78
+ /**#@-*/
79
+
80
+ /**
81
+ * SFTP channel constant
82
+ *
83
+ * Net_SSH2::exec() uses 0 and Net_SSH2::read() / Net_SSH2::write() use 1.
84
+ *
85
+ * @see Net_SSH2::_send_channel_packet()
86
+ * @see Net_SSH2::_get_channel_packet()
87
+ * @access private
88
+ */
89
+ define('NET_SFTP_CHANNEL', 2);
90
+
91
+ /**#@+
92
+ * @access public
93
+ * @see Net_SFTP::put()
94
+ */
95
+ /**
96
+ * Reads data from a local file.
97
+ */
98
+ define('NET_SFTP_LOCAL_FILE', 1);
99
+ /**
100
+ * Reads data from a string.
101
+ */
102
+ // this value isn't really used anymore but i'm keeping it reserved for historical reasons
103
+ define('NET_SFTP_STRING', 2);
104
+ /**
105
+ * Resumes an upload
106
+ */
107
+ define('NET_SFTP_RESUME', 4);
108
+ /**#@-*/
109
+
110
+ /**
111
+ * Pure-PHP implementations of SFTP.
112
+ *
113
+ * @author Jim Wigginton <terrafrost@php.net>
114
+ * @version 0.1.0
115
+ * @access public
116
+ * @package Net_SFTP
117
+ */
118
+ class Net_SFTP extends Net_SSH2 {
119
+ /**
120
+ * Packet Types
121
+ *
122
+ * @see Net_SFTP::Net_SFTP()
123
+ * @var Array
124
+ * @access private
125
+ */
126
+ var $packet_types = array();
127
+
128
+ /**
129
+ * Status Codes
130
+ *
131
+ * @see Net_SFTP::Net_SFTP()
132
+ * @var Array
133
+ * @access private
134
+ */
135
+ var $status_codes = array();
136
+
137
+ /**
138
+ * The Request ID
139
+ *
140
+ * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support
141
+ * concurrent actions, so it's somewhat academic, here.
142
+ *
143
+ * @var Integer
144
+ * @see Net_SFTP::_send_sftp_packet()
145
+ * @access private
146
+ */
147
+ var $request_id = false;
148
+
149
+ /**
150
+ * The Packet Type
151
+ *
152
+ * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support
153
+ * concurrent actions, so it's somewhat academic, here.
154
+ *
155
+ * @var Integer
156
+ * @see Net_SFTP::_get_sftp_packet()
157
+ * @access private
158
+ */
159
+ var $packet_type = -1;
160
+
161
+ /**
162
+ * Packet Buffer
163
+ *
164
+ * @var String
165
+ * @see Net_SFTP::_get_sftp_packet()
166
+ * @access private
167
+ */
168
+ var $packet_buffer = '';
169
+
170
+ /**
171
+ * Extensions supported by the server
172
+ *
173
+ * @var Array
174
+ * @see Net_SFTP::_initChannel()
175
+ * @access private
176
+ */
177
+ var $extensions = array();
178
+
179
+ /**
180
+ * Server SFTP version
181
+ *
182
+ * @var Integer
183
+ * @see Net_SFTP::_initChannel()
184
+ * @access private
185
+ */
186
+ var $version;
187
+
188
+ /**
189
+ * Current working directory
190
+ *
191
+ * @var String
192
+ * @see Net_SFTP::_realpath()
193
+ * @see Net_SFTP::chdir()
194
+ * @access private
195
+ */
196
+ var $pwd = false;
197
+
198
+ /**
199
+ * Packet Type Log
200
+ *
201
+ * @see Net_SFTP::getLog()
202
+ * @var Array
203
+ * @access private
204
+ */
205
+ var $packet_type_log = array();
206
+
207
+ /**
208
+ * Packet Log
209
+ *
210
+ * @see Net_SFTP::getLog()
211
+ * @var Array
212
+ * @access private
213
+ */
214
+ var $packet_log = array();
215
+
216
+ /**
217
+ * Error information
218
+ *
219
+ * @see Net_SFTP::getSFTPErrors()
220
+ * @see Net_SFTP::getLastSFTPError()
221
+ * @var String
222
+ * @access private
223
+ */
224
+ var $sftp_errors = array();
225
+
226
+ /**
227
+ * File Type
228
+ *
229
+ * @see Net_SFTP::_parseLongname()
230
+ * @var Integer
231
+ * @access private
232
+ */
233
+ var $fileType = 0;
234
+
235
+ /**
236
+ * Directory Cache
237
+ *
238
+ * Rather than always having to open a directory and close it immediately there after to see if a file is a directory or
239
+ * rather than always
240
+ *
241
+ * @see Net_SFTP::_save_dir()
242
+ * @see Net_SFTP::_remove_dir()
243
+ * @see Net_SFTP::_is_dir()
244
+ * @var Array
245
+ * @access private
246
+ */
247
+ var $dirs = array();
248
+
249
+ /**
250
+ * Default Constructor.
251
+ *
252
+ * Connects to an SFTP server
253
+ *
254
+ * @param String $host
255
+ * @param optional Integer $port
256
+ * @param optional Integer $timeout
257
+ * @return Net_SFTP
258
+ * @access public
259
+ */
260
+ function Net_SFTP($host, $port = 22, $timeout = 10)
261
+ {
262
+ parent::Net_SSH2($host, $port, $timeout);
263
+ $this->packet_types = array(
264
+ 1 => 'NET_SFTP_INIT',
265
+ 2 => 'NET_SFTP_VERSION',
266
+ /* the format of SSH_FXP_OPEN changed between SFTPv4 and SFTPv5+:
267
+ SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.1
268
+ pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 */
269
+ 3 => 'NET_SFTP_OPEN',
270
+ 4 => 'NET_SFTP_CLOSE',
271
+ 5 => 'NET_SFTP_READ',
272
+ 6 => 'NET_SFTP_WRITE',
273
+ 7 => 'NET_SFTP_LSTAT',
274
+ 9 => 'NET_SFTP_SETSTAT',
275
+ 11 => 'NET_SFTP_OPENDIR',
276
+ 12 => 'NET_SFTP_READDIR',
277
+ 13 => 'NET_SFTP_REMOVE',
278
+ 14 => 'NET_SFTP_MKDIR',
279
+ 15 => 'NET_SFTP_RMDIR',
280
+ 16 => 'NET_SFTP_REALPATH',
281
+ 17 => 'NET_SFTP_STAT',
282
+ /* the format of SSH_FXP_RENAME changed between SFTPv4 and SFTPv5+:
283
+ SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
284
+ pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.5 */
285
+ 18 => 'NET_SFTP_RENAME',
286
+
287
+ 101=> 'NET_SFTP_STATUS',
288
+ 102=> 'NET_SFTP_HANDLE',
289
+ /* the format of SSH_FXP_NAME changed between SFTPv3 and SFTPv4+:
290
+ SFTPv4+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4
291
+ pre-SFTPv4 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 */
292
+ 103=> 'NET_SFTP_DATA',
293
+ 104=> 'NET_SFTP_NAME',
294
+ 105=> 'NET_SFTP_ATTRS',
295
+
296
+ 200=> 'NET_SFTP_EXTENDED'
297
+ );
298
+ $this->status_codes = array(
299
+ 0 => 'NET_SFTP_STATUS_OK',
300
+ 1 => 'NET_SFTP_STATUS_EOF',
301
+ 2 => 'NET_SFTP_STATUS_NO_SUCH_FILE',
302
+ 3 => 'NET_SFTP_STATUS_PERMISSION_DENIED',
303
+ 4 => 'NET_SFTP_STATUS_FAILURE',
304
+ 5 => 'NET_SFTP_STATUS_BAD_MESSAGE',
305
+ 6 => 'NET_SFTP_STATUS_NO_CONNECTION',
306
+ 7 => 'NET_SFTP_STATUS_CONNECTION_LOST',
307
+ 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED'
308
+ );
309
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1
310
+ // the order, in this case, matters quite a lot - see Net_SFTP::_parseAttributes() to understand why
311
+ $this->attributes = array(
312
+ 0x00000001 => 'NET_SFTP_ATTR_SIZE',
313
+ 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+
314
+ 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
315
+ 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
316
+ // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers
317
+ // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in
318
+ // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000.
319
+ // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored.
320
+ -1 << 31 => 'NET_SFTP_ATTR_EXTENDED'
321
+ );
322
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3
323
+ // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name
324
+ // the array for that $this->open5_flags and similarily alter the constant names.
325
+ $this->open_flags = array(
326
+ 0x00000001 => 'NET_SFTP_OPEN_READ',
327
+ 0x00000002 => 'NET_SFTP_OPEN_WRITE',
328
+ 0x00000004 => 'NET_SFTP_OPEN_APPEND',
329
+ 0x00000008 => 'NET_SFTP_OPEN_CREATE',
330
+ 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE'
331
+ );
332
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
333
+ // see Net_SFTP::_parseLongname() for an explanation
334
+ $this->file_types = array(
335
+ 1 => 'NET_SFTP_TYPE_REGULAR',
336
+ 2 => 'NET_SFTP_TYPE_DIRECTORY',
337
+ 3 => 'NET_SFTP_TYPE_SYMLINK',
338
+ 4 => 'NET_SFTP_TYPE_SPECIAL'
339
+ );
340
+ $this->_define_array(
341
+ $this->packet_types,
342
+ $this->status_codes,
343
+ $this->attributes,
344
+ $this->open_flags,
345
+ $this->file_types
346
+ );
347
+ }
348
+
349
+ /**
350
+ * Login
351
+ *
352
+ * @param String $username
353
+ * @param optional String $password
354
+ * @return Boolean
355
+ * @access public
356
+ */
357
+ function login($username, $password = '')
358
+ {
359
+ if (!parent::login($username, $password)) {
360
+ return false;
361
+ }
362
+
363
+ $this->window_size_client_to_server[NET_SFTP_CHANNEL] = $this->window_size;
364
+
365
+ $packet = pack('CNa*N3',
366
+ NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SFTP_CHANNEL, $this->window_size, 0x4000);
367
+
368
+ if (!$this->_send_binary_packet($packet)) {
369
+ return false;
370
+ }
371
+
372
+ $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN;
373
+
374
+ $response = $this->_get_channel_packet(NET_SFTP_CHANNEL);
375
+ if ($response === false) {
376
+ return false;
377
+ }
378
+
379
+ $packet = pack('CNNa*CNa*',
380
+ NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SFTP_CHANNEL], strlen('subsystem'), 'subsystem', 1, strlen('sftp'), 'sftp');
381
+ if (!$this->_send_binary_packet($packet)) {
382
+ return false;
383
+ }
384
+
385
+ $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST;
386
+
387
+ $response = $this->_get_channel_packet(NET_SFTP_CHANNEL);
388
+ if ($response === false) {
389
+ return false;
390
+ }
391
+
392
+ $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA;
393
+
394
+ if (!$this->_send_sftp_packet(NET_SFTP_INIT, "\0\0\0\3")) {
395
+ return false;
396
+ }
397
+
398
+ $response = $this->_get_sftp_packet();
399
+ if ($this->packet_type != NET_SFTP_VERSION) {
400
+ user_error('Expected SSH_FXP_VERSION', E_USER_NOTICE);
401
+ return false;
402
+ }
403
+
404
+ extract(unpack('Nversion', $this->_string_shift($response, 4)));
405
+ $this->version = $version;
406
+ while (!empty($response)) {
407
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
408
+ $key = $this->_string_shift($response, $length);
409
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
410
+ $value = $this->_string_shift($response, $length);
411
+ $this->extensions[$key] = $value;
412
+ }
413
+
414
+ /*
415
+ SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com',
416
+ however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's
417
+ not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for
418
+ one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that
419
+ 'newline@vandyke.com' would.
420
+ */
421
+ /*
422
+ if (isset($this->extensions['newline@vandyke.com'])) {
423
+ $this->extensions['newline'] = $this->extensions['newline@vandyke.com'];
424
+ unset($this->extensions['newline@vandyke.com']);
425
+ }
426
+ */
427
+
428
+ $this->request_id = 1;
429
+
430
+ /*
431
+ A Note on SFTPv4/5/6 support:
432
+ <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.1> states the following:
433
+
434
+ "If the client wishes to interoperate with servers that support noncontiguous version
435
+ numbers it SHOULD send '3'"
436
+
437
+ Given that the server only sends its version number after the client has already done so, the above
438
+ seems to be suggesting that v3 should be the default version. This makes sense given that v3 is the
439
+ most popular.
440
+
441
+ <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.5> states the following;
442
+
443
+ "If the server did not send the "versions" extension, or the version-from-list was not included, the
444
+ server MAY send a status response describing the failure, but MUST then close the channel without
445
+ processing any further requests."
446
+
447
+ So what do you do if you have a client whose initial SSH_FXP_INIT packet says it implements v3 and
448
+ a server whose initial SSH_FXP_VERSION reply says it implements v4 and only v4? If it only implements
449
+ v4, the "versions" extension is likely not going to have been sent so version re-negotiation as discussed
450
+ in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what Net_SFTP would do is close the
451
+ channel and reopen it with a new and updated SSH_FXP_INIT packet.
452
+ */
453
+ if ($this->version != 3) {
454
+ return false;
455
+ }
456
+
457
+ $this->pwd = $this->_realpath('.', false);
458
+
459
+ $this->_save_dir($this->pwd);
460
+
461
+ return true;
462
+ }
463
+
464
+ /**
465
+ * Returns the current directory name
466
+ *
467
+ * @return Mixed
468
+ * @access public
469
+ */
470
+ function pwd()
471
+ {
472
+ return $this->pwd;
473
+ }
474
+
475
+ /**
476
+ * Canonicalize the Server-Side Path Name
477
+ *
478
+ * SFTP doesn't provide a mechanism by which the current working directory can be changed, so we'll emulate it. Returns
479
+ * the absolute (canonicalized) path. If $mode is set to NET_SFTP_CONFIRM_DIR (as opposed to NET_SFTP_CONFIRM_NONE,
480
+ * which is what it is set to by default), false is returned if $dir is not a valid directory.
481
+ *
482
+ * @see Net_SFTP::chdir()
483
+ * @param String $dir
484
+ * @param optional Integer $mode
485
+ * @return Mixed
486
+ * @access private
487
+ */
488
+ function _realpath($dir, $check_dir = true)
489
+ {
490
+ if ($check_dir && $this->_is_dir($dir)) {
491
+ return true;
492
+ }
493
+
494
+ /*
495
+ "This protocol represents file names as strings. File names are
496
+ assumed to use the slash ('/') character as a directory separator.
497
+
498
+ File names starting with a slash are "absolute", and are relative to
499
+ the root of the file system. Names starting with any other character
500
+ are relative to the user's default directory (home directory). Note
501
+ that identifying the user is assumed to take place outside of this
502
+ protocol."
503
+
504
+ -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-6
505
+ */
506
+ $file = '';
507
+ if ($this->pwd !== false) {
508
+ // if the SFTP server returned the canonicalized path even for non-existant files this wouldn't be necessary
509
+ // on OpenSSH it isn't necessary but on other SFTP servers it is. that and since the specs say nothing on
510
+ // the subject, we'll go ahead and work around it with the following.
511
+ if (empty($dir) || $dir[strlen($dir) - 1] != '/') {
512
+ $file = basename($dir);
513
+ $dir = dirname($dir);
514
+ }
515
+
516
+ $dir = $dir[0] == '/' ? '/' . rtrim(substr($dir, 1), '/') : rtrim($dir, '/');
517
+
518
+ if ($dir == '.' || $dir == $this->pwd) {
519
+ return $this->pwd . $file;
520
+ }
521
+
522
+ if ($dir[0] != '/') {
523
+ $dir = $this->pwd . '/' . $dir;
524
+ }
525
+ // on the surface it seems like maybe resolving a path beginning with / is unnecessary, but such paths
526
+ // can contain .'s and ..'s just like any other. we could parse those out as appropriate or we can let
527
+ // the server do it. we'll do the latter.
528
+ }
529
+
530
+ /*
531
+ that SSH_FXP_REALPATH returns SSH_FXP_NAME does not necessarily mean that anything actually exists at the
532
+ specified path. generally speaking, no attributes are returned with this particular SSH_FXP_NAME packet
533
+ regardless of whether or not a file actually exists. and in SFTPv3, the longname field and the filename
534
+ field match for this particular SSH_FXP_NAME packet. for other SSH_FXP_NAME packets, this will likely
535
+ not be the case, but for this one, it is.
536
+ */
537
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9
538
+ if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($dir), $dir))) {
539
+ return false;
540
+ }
541
+
542
+ $response = $this->_get_sftp_packet();
543
+ switch ($this->packet_type) {
544
+ case NET_SFTP_NAME:
545
+ // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following
546
+ // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks
547
+ // at is the first part and that part is defined the same in SFTP versions 3 through 6.
548
+ $this->_string_shift($response, 4); // skip over the count - it should be 1, anyway
549
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
550
+ $realpath = $this->_string_shift($response, $length);
551
+ // the following is SFTPv3 only code. see Net_SFTP::_parseLongname() for more information.
552
+ // per the above comment, this is a shot in the dark that, on most servers, won't help us in determining
553
+ // the file type for Net_SFTP::stat() and Net_SFTP::lstat() but it's worth a shot.
554
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
555
+ $this->fileType = $this->_parseLongname($this->_string_shift($response, $length));
556
+ break;
557
+ case NET_SFTP_STATUS:
558
+ extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
559
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
560
+ return false;
561
+ default:
562
+ user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS', E_USER_NOTICE);
563
+ return false;
564
+ }
565
+
566
+ // if $this->pwd isn't set than the only thing $realpath could be is for '.', which is pretty much guaranteed to
567
+ // be a bonafide directory
568
+ return $realpath . '/' . $file;
569
+ }
570
+
571
+ /**
572
+ * Changes the current directory
573
+ *
574
+ * @param String $dir
575
+ * @return Boolean
576
+ * @access public
577
+ */
578
+ function chdir($dir)
579
+ {
580
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
581
+ return false;
582
+ }
583
+
584
+ if ($dir[strlen($dir) - 1] != '/') {
585
+ $dir.= '/';
586
+ }
587
+
588
+ // confirm that $dir is, in fact, a valid directory
589
+ if ($this->_is_dir($dir)) {
590
+ $this->pwd = $dir;
591
+ return true;
592
+ }
593
+
594
+ $dir = $this->_realpath($dir, false);
595
+
596
+ if ($this->_is_dir($dir)) {
597
+ $this->pwd = $dir;
598
+ return true;
599
+ }
600
+
601
+ if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) {
602
+ return false;
603
+ }
604
+
605
+ // see Net_SFTP::nlist() for a more thorough explanation of the following
606
+ $response = $this->_get_sftp_packet();
607
+ switch ($this->packet_type) {
608
+ case NET_SFTP_HANDLE:
609
+ $handle = substr($response, 4);
610
+ break;
611
+ case NET_SFTP_STATUS:
612
+ extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
613
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
614
+ return false;
615
+ default:
616
+ user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
617
+ return false;
618
+ }
619
+
620
+ if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
621
+ return false;
622
+ }
623
+
624
+ $response = $this->_get_sftp_packet();
625
+ if ($this->packet_type != NET_SFTP_STATUS) {
626
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
627
+ return false;
628
+ }
629
+
630
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
631
+ if ($status != NET_SFTP_STATUS_OK) {
632
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
633
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
634
+ return false;
635
+ }
636
+
637
+ $this->_save_dir($dir);
638
+
639
+ $this->pwd = $dir;
640
+ return true;
641
+ }
642
+
643
+ /**
644
+ * Returns a list of files in the given directory
645
+ *
646
+ * @param optional String $dir
647
+ * @return Mixed
648
+ * @access public
649
+ */
650
+ function nlist($dir = '.')
651
+ {
652
+ return $this->_list($dir, false);
653
+ }
654
+
655
+ /**
656
+ * Returns a detailed list of files in the given directory
657
+ *
658
+ * @param optional String $dir
659
+ * @return Mixed
660
+ * @access public
661
+ */
662
+ function rawlist($dir = '.')
663
+ {
664
+ return $this->_list($dir, true);
665
+ }
666
+
667
+ /**
668
+ * Reads a list, be it detailed or not, of files in the given directory
669
+ *
670
+ * @param optional String $dir
671
+ * @return Mixed
672
+ * @access private
673
+ */
674
+ function _list($dir, $raw = true, $realpath = true)
675
+ {
676
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
677
+ return false;
678
+ }
679
+
680
+ $dir = $this->_realpath($dir . '/');
681
+ if ($dir === false) {
682
+ return false;
683
+ }
684
+
685
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2
686
+ if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) {
687
+ return false;
688
+ }
689
+
690
+ $response = $this->_get_sftp_packet();
691
+ switch ($this->packet_type) {
692
+ case NET_SFTP_HANDLE:
693
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2
694
+ // since 'handle' is the last field in the SSH_FXP_HANDLE packet, we'll just remove the first four bytes that
695
+ // represent the length of the string and leave it at that
696
+ $handle = substr($response, 4);
697
+ break;
698
+ case NET_SFTP_STATUS:
699
+ // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
700
+ extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
701
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
702
+ return false;
703
+ default:
704
+ user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
705
+ return false;
706
+ }
707
+
708
+ $this->_save_dir($dir);
709
+
710
+ $contents = array();
711
+ while (true) {
712
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2
713
+ // why multiple SSH_FXP_READDIR packets would be sent when the response to a single one can span arbitrarily many
714
+ // SSH_MSG_CHANNEL_DATA messages is not known to me.
715
+ if (!$this->_send_sftp_packet(NET_SFTP_READDIR, pack('Na*', strlen($handle), $handle))) {
716
+ return false;
717
+ }
718
+
719
+ $response = $this->_get_sftp_packet();
720
+ switch ($this->packet_type) {
721
+ case NET_SFTP_NAME:
722
+ extract(unpack('Ncount', $this->_string_shift($response, 4)));
723
+ for ($i = 0; $i < $count; $i++) {
724
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
725
+ $shortname = $this->_string_shift($response, $length);
726
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
727
+ $longname = $this->_string_shift($response, $length);
728
+ $attributes = $this->_parseAttributes($response); // we also don't care about the attributes
729
+ if (!$raw) {
730
+ $contents[] = $shortname;
731
+ } else {
732
+ $contents[$shortname] = $attributes;
733
+ $fileType = $this->_parseLongname($longname);
734
+ if ($fileType) {
735
+ if ($fileType == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) {
736
+ $this->_save_dir($dir . '/' . $shortname);
737
+ }
738
+ $contents[$shortname]['type'] = $fileType;
739
+ }
740
+ }
741
+ // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the
742
+ // final SSH_FXP_STATUS packet should tell us that, already.
743
+ }
744
+ break;
745
+ case NET_SFTP_STATUS:
746
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
747
+ if ($status != NET_SFTP_STATUS_EOF) {
748
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
749
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
750
+ return false;
751
+ }
752
+ break 2;
753
+ default:
754
+ user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS', E_USER_NOTICE);
755
+ return false;
756
+ }
757
+ }
758
+
759
+ if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
760
+ return false;
761
+ }
762
+
763
+ // "The client MUST release all resources associated with the handle regardless of the status."
764
+ // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3
765
+ $response = $this->_get_sftp_packet();
766
+ if ($this->packet_type != NET_SFTP_STATUS) {
767
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
768
+ return false;
769
+ }
770
+
771
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
772
+ if ($status != NET_SFTP_STATUS_OK) {
773
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
774
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
775
+ return false;
776
+ }
777
+
778
+ return $contents;
779
+ }
780
+
781
+ /**
782
+ * Returns the file size, in bytes, or false, on failure
783
+ *
784
+ * Files larger than 4GB will show up as being exactly 4GB.
785
+ *
786
+ * @param String $filename
787
+ * @return Mixed
788
+ * @access public
789
+ */
790
+ function size($filename)
791
+ {
792
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
793
+ return false;
794
+ }
795
+
796
+ $filename = $this->_realpath($filename);
797
+ if ($filename === false) {
798
+ return false;
799
+ }
800
+
801
+ return $this->_size($filename);
802
+ }
803
+
804
+ /**
805
+ * Save directories to cache
806
+ *
807
+ * @param String $dir
808
+ * @access private
809
+ */
810
+ function _save_dir($dir)
811
+ {
812
+ // preg_replace('#^/|/(?=/)|/$#', '', $dir) == str_replace('//', '/', trim($dir, '/'))
813
+ $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $dir));
814
+
815
+ $temp = &$this->dirs;
816
+ foreach ($dirs as $dir) {
817
+ if (!isset($temp[$dir])) {
818
+ $temp[$dir] = array();
819
+ }
820
+ $temp = &$temp[$dir];
821
+ }
822
+ }
823
+
824
+ /**
825
+ * Remove directories from cache
826
+ *
827
+ * @param String $dir
828
+ * @access private
829
+ */
830
+ function _remove_dir($dir)
831
+ {
832
+ $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $dir));
833
+
834
+ $temp = &$this->dirs;
835
+ foreach ($dirs as $dir) {
836
+ if ($dir == end($dirs)) {
837
+ unset($temp[$dir]);
838
+ break;
839
+ }
840
+ if (isset($new[$key])) {
841
+ $temp = &$temp[$dir];
842
+ }
843
+ }
844
+ }
845
+
846
+ /**
847
+ * Checks cache for directory
848
+ *
849
+ * @param String $dir
850
+ * @access private
851
+ */
852
+ function _is_dir($dir)
853
+ {
854
+ $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $dir));
855
+
856
+ $temp = &$this->dirs;
857
+ foreach ($dirs as $dir) {
858
+ if (!isset($temp[$dir])) {
859
+ return false;
860
+ }
861
+ $temp = &$temp[$dir];
862
+ }
863
+ }
864
+
865
+ /**
866
+ * Returns general information about a file.
867
+ *
868
+ * Returns an array on success and false otherwise.
869
+ *
870
+ * @param String $filename
871
+ * @return Mixed
872
+ * @access public
873
+ */
874
+ function stat($filename)
875
+ {
876
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
877
+ return false;
878
+ }
879
+
880
+ $filename = $this->_realpath($filename);
881
+ if ($filename === false) {
882
+ return false;
883
+ }
884
+
885
+ $stat = $this->_stat($filename, NET_SFTP_STAT);
886
+
887
+ $pwd = $this->pwd;
888
+ $stat['type'] = $this->chdir($filename) ?
889
+ NET_SFTP_TYPE_DIRECTORY :
890
+ NET_SFTP_TYPE_REGULAR;
891
+ $this->pwd = $pwd;
892
+
893
+ return $stat;
894
+ }
895
+
896
+ /**
897
+ * Returns general information about a file or symbolic link.
898
+ *
899
+ * Returns an array on success and false otherwise.
900
+ *
901
+ * @param String $filename
902
+ * @return Mixed
903
+ * @access public
904
+ */
905
+ function lstat($filename)
906
+ {
907
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
908
+ return false;
909
+ }
910
+
911
+ $filename = $this->_realpath($filename);
912
+ if ($filename === false) {
913
+ return false;
914
+ }
915
+
916
+ $lstat = $this->_stat($filename, NET_SFTP_LSTAT);
917
+ $stat = $this->_stat($filename, NET_SFTP_STAT);
918
+
919
+ if ($lstat != $stat) {
920
+ return array_merge($lstat, array('type' => NET_SFTP_TYPE_SYMLINK));
921
+ }
922
+
923
+ $pwd = $this->pwd;
924
+ $lstat['type'] = $this->chdir($filename) ?
925
+ NET_SFTP_TYPE_DIRECTORY :
926
+ NET_SFTP_TYPE_REGULAR;
927
+ $this->pwd = $pwd;
928
+
929
+ return $lstat;
930
+ }
931
+
932
+ /**
933
+ * Returns general information about a file or symbolic link
934
+ *
935
+ * Determines information without calling Net_SFTP::_realpath().
936
+ * The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT.
937
+ *
938
+ * @param String $filename
939
+ * @param Integer $type
940
+ * @return Mixed
941
+ * @access private
942
+ */
943
+ function _stat($filename, $type)
944
+ {
945
+ // SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
946
+ $packet = pack('Na*', strlen($filename), $filename);
947
+ if (!$this->_send_sftp_packet($type, $packet)) {
948
+ return false;
949
+ }
950
+
951
+ $response = $this->_get_sftp_packet();
952
+ switch ($this->packet_type) {
953
+ case NET_SFTP_ATTRS:
954
+ $attributes = $this->_parseAttributes($response);
955
+ if ($this->fileType) {
956
+ $attributes['type'] = $this->fileType;
957
+ }
958
+ return $attributes;
959
+ case NET_SFTP_STATUS:
960
+ extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
961
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
962
+ return false;
963
+ }
964
+
965
+ user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS', E_USER_NOTICE);
966
+ return false;
967
+ }
968
+
969
+ /**
970
+ * Attempt to identify the file type
971
+ *
972
+ * @param String $path
973
+ * @param Array $stat
974
+ * @param Array $lstat
975
+ * @return Integer
976
+ * @access private
977
+ */
978
+ function _identify_type($path, $stat1, $stat2)
979
+ {
980
+ $stat1 = $this->_stat($path, $stat1);
981
+ $stat2 = $this->_stat($path, $stat2);
982
+
983
+ if ($stat1 != $stat2) {
984
+ return array_merge($stat1, array('type' => NET_SFTP_TYPE_SYMLINK));
985
+ }
986
+
987
+ $pwd = $this->pwd;
988
+ $stat1['type'] = $this->chdir($path) ?
989
+ NET_SFTP_TYPE_DIRECTORY :
990
+ NET_SFTP_TYPE_REGULAR;
991
+ $this->pwd = $pwd;
992
+
993
+ return $stat1;
994
+ }
995
+
996
+ /**
997
+ * Returns the file size, in bytes, or false, on failure
998
+ *
999
+ * Determines the size without calling Net_SFTP::_realpath()
1000
+ *
1001
+ * @param String $filename
1002
+ * @return Mixed
1003
+ * @access private
1004
+ */
1005
+ function _size($filename)
1006
+ {
1007
+ $result = $this->_stat($filename, NET_SFTP_LSTAT);
1008
+ if ($result === false) {
1009
+ return false;
1010
+ }
1011
+ return isset($result['size']) ? $result['size'] : -1;
1012
+ }
1013
+
1014
+ /**
1015
+ * Set permissions on a file.
1016
+ *
1017
+ * Returns the new file permissions on success or FALSE on error.
1018
+ *
1019
+ * @param Integer $mode
1020
+ * @param String $filename
1021
+ * @return Mixed
1022
+ * @access public
1023
+ */
1024
+ function chmod($mode, $filename, $recursive = false)
1025
+ {
1026
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1027
+ return false;
1028
+ }
1029
+
1030
+ $filename = $this->_realpath($filename);
1031
+ if ($filename === false) {
1032
+ return false;
1033
+ }
1034
+
1035
+ if ($recursive) {
1036
+ $i = 0;
1037
+ $result = $this->_chmod_recursive($mode, $filename, $i);
1038
+ $this->_read_put_responses($i);
1039
+ return $result;
1040
+ }
1041
+
1042
+ // SFTPv4+ has an additional byte field - type - that would need to be sent, as well. setting it to
1043
+ // SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT.
1044
+ $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777);
1045
+ if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($filename), $filename, $attr))) {
1046
+ return false;
1047
+ }
1048
+
1049
+ /*
1050
+ "Because some systems must use separate system calls to set various attributes, it is possible that a failure
1051
+ response will be returned, but yet some of the attributes may be have been successfully modified. If possible,
1052
+ servers SHOULD avoid this situation; however, clients MUST be aware that this is possible."
1053
+
1054
+ -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6
1055
+ */
1056
+ $response = $this->_get_sftp_packet();
1057
+ if ($this->packet_type != NET_SFTP_STATUS) {
1058
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
1059
+ return false;
1060
+ }
1061
+
1062
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
1063
+ if ($status != NET_SFTP_STATUS_OK) {
1064
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1065
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1066
+ }
1067
+
1068
+ // rather than return what the permissions *should* be, we'll return what they actually are. this will also
1069
+ // tell us if the file actually exists.
1070
+ // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
1071
+ $packet = pack('Na*', strlen($filename), $filename);
1072
+ if (!$this->_send_sftp_packet(NET_SFTP_STAT, $packet)) {
1073
+ return false;
1074
+ }
1075
+
1076
+ $response = $this->_get_sftp_packet();
1077
+ switch ($this->packet_type) {
1078
+ case NET_SFTP_ATTRS:
1079
+ $attrs = $this->_parseAttributes($response);
1080
+ return $attrs['permissions'];
1081
+ case NET_SFTP_STATUS:
1082
+ extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
1083
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1084
+ return false;
1085
+ }
1086
+
1087
+ user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS', E_USER_NOTICE);
1088
+ return false;
1089
+ }
1090
+
1091
+ /**
1092
+ * Recursively chmods directories on the SFTP server
1093
+ *
1094
+ * Minimizes directory lookups and SSH_FXP_STATUS requests for speed.
1095
+ *
1096
+ * @param Integer $mode
1097
+ * @param String $filename
1098
+ * @return Boolean
1099
+ * @access private
1100
+ */
1101
+ function _chmod_recursive($mode, $path, &$i)
1102
+ {
1103
+ if (!$this->_read_put_responses($i)) {
1104
+ return false;
1105
+ }
1106
+ $i = 0;
1107
+ $entries = $this->_list($path, true, false);
1108
+
1109
+ if ($entries === false) {
1110
+ return $this->chmod($mode, $path);
1111
+ }
1112
+
1113
+ // presumably $entries will never be empty because it'll always have . and ..
1114
+
1115
+ foreach ($entries as $filename=>$props) {
1116
+ if ($filename == '.' || $filename == '..') {
1117
+ continue;
1118
+ }
1119
+
1120
+ if (!isset($props['type'])) {
1121
+ return false;
1122
+ }
1123
+
1124
+ $temp = $path . '/' . $filename;
1125
+ if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
1126
+ if (!$this->_chmod_recursive($mode, $temp, $i)) {
1127
+ return false;
1128
+ }
1129
+ } else {
1130
+ $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777);
1131
+ if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($temp), $temp, $attr))) {
1132
+ return false;
1133
+ }
1134
+
1135
+ $i++;
1136
+
1137
+ if ($i >= 50) {
1138
+ if (!$this->_read_put_responses($i)) {
1139
+ return false;
1140
+ }
1141
+ $i = 0;
1142
+ }
1143
+ }
1144
+ }
1145
+
1146
+ $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777);
1147
+ if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($path), $path, $attr))) {
1148
+ return false;
1149
+ }
1150
+
1151
+ $i++;
1152
+
1153
+ if ($i >= 50) {
1154
+ if (!$this->_read_put_responses($i)) {
1155
+ return false;
1156
+ }
1157
+ $i = 0;
1158
+ }
1159
+
1160
+ return true;
1161
+ }
1162
+
1163
+ /**
1164
+ * Creates a directory.
1165
+ *
1166
+ * @param String $dir
1167
+ * @return Boolean
1168
+ * @access public
1169
+ */
1170
+ function mkdir($dir)
1171
+ {
1172
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1173
+ return false;
1174
+ }
1175
+
1176
+ $dir = $this->_realpath(rtrim($dir, '/'));
1177
+ if ($dir === false) {
1178
+ return false;
1179
+ }
1180
+
1181
+ // by not providing any permissions, hopefully the server will use the logged in users umask - their
1182
+ // default permissions.
1183
+ if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*N', strlen($dir), $dir, 0))) {
1184
+ return false;
1185
+ }
1186
+
1187
+ $response = $this->_get_sftp_packet();
1188
+ if ($this->packet_type != NET_SFTP_STATUS) {
1189
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
1190
+ return false;
1191
+ }
1192
+
1193
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
1194
+ if ($status != NET_SFTP_STATUS_OK) {
1195
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1196
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1197
+ return false;
1198
+ }
1199
+
1200
+ $this->_save_dir($dir);
1201
+
1202
+ return true;
1203
+ }
1204
+
1205
+ /**
1206
+ * Removes a directory.
1207
+ *
1208
+ * @param String $dir
1209
+ * @return Boolean
1210
+ * @access public
1211
+ */
1212
+ function rmdir($dir)
1213
+ {
1214
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1215
+ return false;
1216
+ }
1217
+
1218
+ $dir = $this->_realpath($dir);
1219
+ if ($dir === false) {
1220
+ return false;
1221
+ }
1222
+
1223
+ if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($dir), $dir))) {
1224
+ return false;
1225
+ }
1226
+
1227
+ $response = $this->_get_sftp_packet();
1228
+ if ($this->packet_type != NET_SFTP_STATUS) {
1229
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
1230
+ return false;
1231
+ }
1232
+
1233
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
1234
+ if ($status != NET_SFTP_STATUS_OK) {
1235
+ // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED?
1236
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1237
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1238
+ return false;
1239
+ }
1240
+
1241
+ $this->_remove_dir($dir);
1242
+
1243
+ return true;
1244
+ }
1245
+
1246
+ /**
1247
+ * Uploads a file to the SFTP server.
1248
+ *
1249
+ * By default, Net_SFTP::put() does not read from the local filesystem. $data is dumped directly into $remote_file.
1250
+ * So, for example, if you set $data to 'filename.ext' and then do Net_SFTP::get(), you will get a file, twelve bytes
1251
+ * long, containing 'filename.ext' as its contents.
1252
+ *
1253
+ * Setting $mode to NET_SFTP_LOCAL_FILE will change the above behavior. With NET_SFTP_LOCAL_FILE, $remote_file will
1254
+ * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how
1255
+ * large $remote_file will be, as well.
1256
+ *
1257
+ * Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take
1258
+ * care of that, yourself.
1259
+ *
1260
+ * @param String $remote_file
1261
+ * @param String $data
1262
+ * @param optional Integer $mode
1263
+ * @return Boolean
1264
+ * @access public
1265
+ * @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - Net_SFTP::setMode().
1266
+ */
1267
+ function put($remote_file, $data, $mode = NET_SFTP_STRING)
1268
+ {
1269
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1270
+ return false;
1271
+ }
1272
+
1273
+ $remote_file = $this->_realpath($remote_file);
1274
+ if ($remote_file === false) {
1275
+ return false;
1276
+ }
1277
+
1278
+ $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE;
1279
+ // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file."
1280
+ // in practice, it doesn't seem to do that.
1281
+ //$flags|= ($mode & NET_SFTP_RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE;
1282
+
1283
+ // if NET_SFTP_OPEN_APPEND worked as it should the following (up until the -----------) wouldn't be necessary
1284
+ $offset = 0;
1285
+ if ($mode & NET_SFTP_RESUME) {
1286
+ $size = $this->_size($remote_file);
1287
+ $offset = $size !== false ? $size : 0;
1288
+ } else {
1289
+ $flags|= NET_SFTP_OPEN_TRUNCATE;
1290
+ }
1291
+ // --------------
1292
+
1293
+ $packet = pack('Na*N2', strlen($remote_file), $remote_file, $flags, 0);
1294
+ if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
1295
+ return false;
1296
+ }
1297
+
1298
+ $response = $this->_get_sftp_packet();
1299
+ switch ($this->packet_type) {
1300
+ case NET_SFTP_HANDLE:
1301
+ $handle = substr($response, 4);
1302
+ break;
1303
+ case NET_SFTP_STATUS:
1304
+ extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
1305
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1306
+ return false;
1307
+ default:
1308
+ user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
1309
+ return false;
1310
+ }
1311
+
1312
+ $initialize = true;
1313
+
1314
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3
1315
+ if ($mode & NET_SFTP_LOCAL_FILE) {
1316
+ if (!is_file($data)) {
1317
+ user_error("$data is not a valid file", E_USER_NOTICE);
1318
+ return false;
1319
+ }
1320
+ $fp = @fopen($data, 'rb');
1321
+ if (!$fp) {
1322
+ return false;
1323
+ }
1324
+ $size = filesize($data);
1325
+ } else {
1326
+ $size = strlen($data);
1327
+ }
1328
+
1329
+ $sent = 0;
1330
+ $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;
1331
+
1332
+ $sftp_packet_size = 4096; // PuTTY uses 4096
1333
+ $i = 0;
1334
+ while ($sent < $size) {
1335
+ $temp = $mode & NET_SFTP_LOCAL_FILE ? fread($fp, $sftp_packet_size) : $this->_string_shift($data, $sftp_packet_size);
1336
+ $packet = pack('Na*N3a*', strlen($handle), $handle, 0, $offset + $sent, strlen($temp), $temp);
1337
+ if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet)) {
1338
+ fclose($fp);
1339
+ return false;
1340
+ }
1341
+ $sent+= strlen($temp);
1342
+
1343
+ $i++;
1344
+
1345
+ if ($i == 50) {
1346
+ if (!$this->_read_put_responses($i)) {
1347
+ $i = 0;
1348
+ break;
1349
+ }
1350
+ $i = 0;
1351
+ }
1352
+ }
1353
+
1354
+ $this->_read_put_responses($i);
1355
+
1356
+ if ($mode & NET_SFTP_LOCAL_FILE) {
1357
+ fclose($fp);
1358
+ }
1359
+
1360
+ if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
1361
+ return false;
1362
+ }
1363
+
1364
+ $response = $this->_get_sftp_packet();
1365
+ if ($this->packet_type != NET_SFTP_STATUS) {
1366
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
1367
+ return false;
1368
+ }
1369
+
1370
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
1371
+ if ($status != NET_SFTP_STATUS_OK) {
1372
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1373
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1374
+ return false;
1375
+ }
1376
+
1377
+ return true;
1378
+ }
1379
+
1380
+ /**
1381
+ * Reads multiple successive SSH_FXP_WRITE responses
1382
+ *
1383
+ * Sending an SSH_FXP_WRITE packet and immediately reading its response isn't as efficient as blindly sending out $i
1384
+ * SSH_FXP_WRITEs, in succession, and then reading $i responses.
1385
+ *
1386
+ * @param Integer $i
1387
+ * @return Boolean
1388
+ * @access private
1389
+ */
1390
+ function _read_put_responses($i)
1391
+ {
1392
+ while ($i--) {
1393
+ $response = $this->_get_sftp_packet();
1394
+ if ($this->packet_type != NET_SFTP_STATUS) {
1395
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
1396
+ return false;
1397
+ }
1398
+
1399
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
1400
+ if ($status != NET_SFTP_STATUS_OK) {
1401
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1402
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1403
+ break;
1404
+ }
1405
+ }
1406
+
1407
+ return $i < 0;
1408
+ }
1409
+
1410
+ /**
1411
+ * Downloads a file from the SFTP server.
1412
+ *
1413
+ * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if
1414
+ * the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the
1415
+ * operation
1416
+ *
1417
+ * @param String $remote_file
1418
+ * @param optional String $local_file
1419
+ * @return Mixed
1420
+ * @access public
1421
+ */
1422
+ function get($remote_file, $local_file = false)
1423
+ {
1424
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1425
+ return false;
1426
+ }
1427
+
1428
+ $remote_file = $this->_realpath($remote_file);
1429
+ if ($remote_file === false) {
1430
+ return false;
1431
+ }
1432
+
1433
+ $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_READ, 0);
1434
+ if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
1435
+ return false;
1436
+ }
1437
+
1438
+ $response = $this->_get_sftp_packet();
1439
+ switch ($this->packet_type) {
1440
+ case NET_SFTP_HANDLE:
1441
+ $handle = substr($response, 4);
1442
+ break;
1443
+ case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
1444
+ extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
1445
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1446
+ return false;
1447
+ default:
1448
+ user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
1449
+ return false;
1450
+ }
1451
+
1452
+ if ($local_file !== false) {
1453
+ $fp = fopen($local_file, 'wb');
1454
+ if (!$fp) {
1455
+ return false;
1456
+ }
1457
+ } else {
1458
+ $content = '';
1459
+ }
1460
+
1461
+ $read = 0;
1462
+ while (true) {
1463
+ $packet = pack('Na*N3', strlen($handle), $handle, 0, $read, 1 << 20);
1464
+ if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) {
1465
+ if ($local_file !== false) {
1466
+ fclose($fp);
1467
+ }
1468
+ return false;
1469
+ }
1470
+
1471
+ $response = $this->_get_sftp_packet();
1472
+ switch ($this->packet_type) {
1473
+ case NET_SFTP_DATA:
1474
+ $temp = substr($response, 4);
1475
+ $read+= strlen($temp);
1476
+ if ($local_file === false) {
1477
+ $content.= $temp;
1478
+ } else {
1479
+ fputs($fp, $temp);
1480
+ }
1481
+ break;
1482
+ case NET_SFTP_STATUS:
1483
+ extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
1484
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1485
+ break 2;
1486
+ default:
1487
+ user_error('Expected SSH_FXP_DATA or SSH_FXP_STATUS', E_USER_NOTICE);
1488
+ if ($local_file !== false) {
1489
+ fclose($fp);
1490
+ }
1491
+ return false;
1492
+ }
1493
+ }
1494
+
1495
+ if ($local_file !== false) {
1496
+ fclose($fp);
1497
+ }
1498
+
1499
+ if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
1500
+ return false;
1501
+ }
1502
+
1503
+ $response = $this->_get_sftp_packet();
1504
+ if ($this->packet_type != NET_SFTP_STATUS) {
1505
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
1506
+ return false;
1507
+ }
1508
+
1509
+ extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
1510
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1511
+
1512
+ // check the status from the NET_SFTP_STATUS case in the above switch after the file has been closed
1513
+ if ($status != NET_SFTP_STATUS_OK) {
1514
+ return false;
1515
+ }
1516
+
1517
+ if (isset($content)) {
1518
+ return $content;
1519
+ }
1520
+
1521
+ return true;
1522
+ }
1523
+
1524
+ /**
1525
+ * Deletes a file on the SFTP server.
1526
+ *
1527
+ * @param String $path
1528
+ * @param Boolean $recursive
1529
+ * @return Boolean
1530
+ * @access public
1531
+ */
1532
+ function delete($path, $recursive = true)
1533
+ {
1534
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1535
+ return false;
1536
+ }
1537
+
1538
+ $path = $this->_realpath($path);
1539
+ if ($path === false) {
1540
+ return false;
1541
+ }
1542
+
1543
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
1544
+ if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($path), $path))) {
1545
+ return false;
1546
+ }
1547
+
1548
+ $response = $this->_get_sftp_packet();
1549
+ if ($this->packet_type != NET_SFTP_STATUS) {
1550
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
1551
+ return false;
1552
+ }
1553
+
1554
+ // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
1555
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
1556
+ if ($status != NET_SFTP_STATUS_OK) {
1557
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1558
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1559
+ if (!$recursive) {
1560
+ return false;
1561
+ }
1562
+ $i = 0;
1563
+ $result = $this->_delete_recursive($path, $i);
1564
+ $this->_read_put_responses($i);
1565
+ return $result;
1566
+ }
1567
+
1568
+ return true;
1569
+ }
1570
+
1571
+ /**
1572
+ * Recursively deletes directories on the SFTP server
1573
+ *
1574
+ * Minimizes directory lookups and SSH_FXP_STATUS requests for speed.
1575
+ *
1576
+ * @param String $path
1577
+ * @param Integer $i
1578
+ * @return Boolean
1579
+ * @access private
1580
+ */
1581
+ function _delete_recursive($path, &$i)
1582
+ {
1583
+ if (!$this->_read_put_responses($i)) {
1584
+ return false;
1585
+ }
1586
+ $i = 0;
1587
+ $entries = $this->_list($path, true, false);
1588
+
1589
+ // presumably $entries will never be empty because it'll always have . and ..
1590
+
1591
+ foreach ($entries as $filename=>$props) {
1592
+ if ($filename == '.' || $filename == '..') {
1593
+ continue;
1594
+ }
1595
+
1596
+ if (!isset($props['type'])) {
1597
+ return false;
1598
+ }
1599
+
1600
+ $temp = $path . '/' . $filename;
1601
+ if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
1602
+ if (!$this->_delete_recursive($temp, $i)) {
1603
+ return false;
1604
+ }
1605
+ } else {
1606
+ if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($temp), $temp))) {
1607
+ return false;
1608
+ }
1609
+
1610
+ $i++;
1611
+
1612
+ if ($i >= 50) {
1613
+ if (!$this->_read_put_responses($i)) {
1614
+ return false;
1615
+ }
1616
+ $i = 0;
1617
+ }
1618
+ }
1619
+ }
1620
+
1621
+ if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($path), $path))) {
1622
+ return false;
1623
+ }
1624
+
1625
+ $i++;
1626
+
1627
+ if ($i >= 50) {
1628
+ if (!$this->_read_put_responses($i)) {
1629
+ return false;
1630
+ }
1631
+ $i = 0;
1632
+ }
1633
+
1634
+ return true;
1635
+ }
1636
+
1637
+ /**
1638
+ * Renames a file or a directory on the SFTP server
1639
+ *
1640
+ * @param String $oldname
1641
+ * @param String $newname
1642
+ * @return Boolean
1643
+ * @access public
1644
+ */
1645
+ function rename($oldname, $newname)
1646
+ {
1647
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1648
+ return false;
1649
+ }
1650
+
1651
+ $oldname = $this->_realpath($oldname);
1652
+ $newname = $this->_realpath($newname);
1653
+ if ($oldname === false || $newname === false) {
1654
+ return false;
1655
+ }
1656
+
1657
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
1658
+ $packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname);
1659
+ if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) {
1660
+ return false;
1661
+ }
1662
+
1663
+ $response = $this->_get_sftp_packet();
1664
+ if ($this->packet_type != NET_SFTP_STATUS) {
1665
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
1666
+ return false;
1667
+ }
1668
+
1669
+ // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
1670
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
1671
+ if ($status != NET_SFTP_STATUS_OK) {
1672
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1673
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1674
+ return false;
1675
+ }
1676
+
1677
+ return true;
1678
+ }
1679
+
1680
+ /**
1681
+ * Parse Attributes
1682
+ *
1683
+ * See '7. File Attributes' of draft-ietf-secsh-filexfer-13 for more info.
1684
+ *
1685
+ * @param String $response
1686
+ * @return Array
1687
+ * @access private
1688
+ */
1689
+ function _parseAttributes(&$response)
1690
+ {
1691
+ $attr = array();
1692
+ extract(unpack('Nflags', $this->_string_shift($response, 4)));
1693
+ // SFTPv4+ have a type field (a byte) that follows the above flag field
1694
+ foreach ($this->attributes as $key => $value) {
1695
+ switch ($flags & $key) {
1696
+ case NET_SFTP_ATTR_SIZE: // 0x00000001
1697
+ // size is represented by a 64-bit integer, so we perhaps ought to be doing the following:
1698
+ // $attr['size'] = new Math_BigInteger($this->_string_shift($response, 8), 256);
1699
+ // of course, you shouldn't be using Net_SFTP to transfer files that are in excess of 4GB
1700
+ // (0xFFFFFFFF bytes), anyway. as such, we'll just represent all file sizes that are bigger than
1701
+ // 4GB as being 4GB.
1702
+ extract(unpack('Nupper/Nsize', $this->_string_shift($response, 8)));
1703
+ if ($upper) {
1704
+ $attr['size'] = 0xFFFFFFFF;
1705
+ } else {
1706
+ $attr['size'] = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;
1707
+ }
1708
+ break;
1709
+ case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only)
1710
+ $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8));
1711
+ break;
1712
+ case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004
1713
+ $attr+= unpack('Npermissions', $this->_string_shift($response, 4));
1714
+ break;
1715
+ case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008
1716
+ $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8));
1717
+ break;
1718
+ case NET_SFTP_ATTR_EXTENDED: // 0x80000000
1719
+ extract(unpack('Ncount', $this->_string_shift($response, 4)));
1720
+ for ($i = 0; $i < $count; $i++) {
1721
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1722
+ $key = $this->_string_shift($response, $length);
1723
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1724
+ $attr[$key] = $this->_string_shift($response, $length);
1725
+ }
1726
+ }
1727
+ }
1728
+ return $attr;
1729
+ }
1730
+
1731
+ /**
1732
+ * Parse Longname
1733
+ *
1734
+ * SFTPv3 doesn't provide any easy way of identifying a file type. You could try to open
1735
+ * a file as a directory and see if an error is returned or you could try to parse the
1736
+ * SFTPv3-specific longname field of the SSH_FXP_NAME packet. That's what this function does.
1737
+ * The result is returned using the
1738
+ * {@link http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 SFTPv4 type constants}.
1739
+ *
1740
+ * If the longname is in an unrecognized format bool(false) is returned.
1741
+ *
1742
+ * @param String $longname
1743
+ * @return Mixed
1744
+ * @access private
1745
+ */
1746
+ function _parseLongname($longname)
1747
+ {
1748
+ // http://en.wikipedia.org/wiki/Unix_file_types
1749
+ // http://en.wikipedia.org/wiki/Filesystem_permissions#Notation_of_traditional_Unix_permissions
1750
+ if (preg_match('#^[^/]([r-][w-][xstST-]){3}#', $longname)) {
1751
+ switch ($longname[0]) {
1752
+ case '-':
1753
+ return NET_SFTP_TYPE_REGULAR;
1754
+ case 'd':
1755
+ return NET_SFTP_TYPE_DIRECTORY;
1756
+ case 'l':
1757
+ return NET_SFTP_TYPE_SYMLINK;
1758
+ default:
1759
+ return NET_SFTP_TYPE_SPECIAL;
1760
+ }
1761
+ }
1762
+
1763
+ return false;
1764
+ }
1765
+
1766
+ /**
1767
+ * Sends SFTP Packets
1768
+ *
1769
+ * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info.
1770
+ *
1771
+ * @param Integer $type
1772
+ * @param String $data
1773
+ * @see Net_SFTP::_get_sftp_packet()
1774
+ * @see Net_SSH2::_send_channel_packet()
1775
+ * @return Boolean
1776
+ * @access private
1777
+ */
1778
+ function _send_sftp_packet($type, $data)
1779
+ {
1780
+ $packet = $this->request_id !== false ?
1781
+ pack('NCNa*', strlen($data) + 5, $type, $this->request_id, $data) :
1782
+ pack('NCa*', strlen($data) + 1, $type, $data);
1783
+
1784
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
1785
+ $result = $this->_send_channel_packet(NET_SFTP_CHANNEL, $packet);
1786
+ $stop = strtok(microtime(), ' ') + strtok('');
1787
+
1788
+ if (defined('NET_SFTP_LOGGING')) {
1789
+ $packet_type = '-> ' . $this->packet_types[$type] .
1790
+ ' (' . round($stop - $start, 4) . 's)';
1791
+ if (NET_SFTP_LOGGING == NET_SFTP_LOG_REALTIME) {
1792
+ echo "<pre>\r\n" . $this->_format_log(array($data), array($packet_type)) . "\r\n</pre>\r\n";
1793
+ flush();
1794
+ ob_flush();
1795
+ } else {
1796
+ $this->packet_type_log[] = $packet_type;
1797
+ if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) {
1798
+ $this->packet_log[] = $data;
1799
+ }
1800
+ }
1801
+ }
1802
+
1803
+ return $result;
1804
+ }
1805
+
1806
+ /**
1807
+ * Receives SFTP Packets
1808
+ *
1809
+ * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info.
1810
+ *
1811
+ * Incidentally, the number of SSH_MSG_CHANNEL_DATA messages has no bearing on the number of SFTP packets present.
1812
+ * There can be one SSH_MSG_CHANNEL_DATA messages containing two SFTP packets or there can be two SSH_MSG_CHANNEL_DATA
1813
+ * messages containing one SFTP packet.
1814
+ *
1815
+ * @see Net_SFTP::_send_sftp_packet()
1816
+ * @return String
1817
+ * @access private
1818
+ */
1819
+ function _get_sftp_packet()
1820
+ {
1821
+ $this->curTimeout = false;
1822
+
1823
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
1824
+
1825
+ // SFTP packet length
1826
+ while (strlen($this->packet_buffer) < 4) {
1827
+ $temp = $this->_get_channel_packet(NET_SFTP_CHANNEL);
1828
+ if (is_bool($temp)) {
1829
+ $this->packet_type = false;
1830
+ $this->packet_buffer = '';
1831
+ return false;
1832
+ }
1833
+ $this->packet_buffer.= $temp;
1834
+ }
1835
+ extract(unpack('Nlength', $this->_string_shift($this->packet_buffer, 4)));
1836
+ $tempLength = $length;
1837
+ $tempLength-= strlen($this->packet_buffer);
1838
+
1839
+ // SFTP packet type and data payload
1840
+ while ($tempLength > 0) {
1841
+ $temp = $this->_get_channel_packet(NET_SFTP_CHANNEL);
1842
+ if (is_bool($temp)) {
1843
+ $this->packet_type = false;
1844
+ $this->packet_buffer = '';
1845
+ return false;
1846
+ }
1847
+ $this->packet_buffer.= $temp;
1848
+ $tempLength-= strlen($temp);
1849
+ }
1850
+
1851
+ $stop = strtok(microtime(), ' ') + strtok('');
1852
+
1853
+ $this->packet_type = ord($this->_string_shift($this->packet_buffer));
1854
+
1855
+ if ($this->request_id !== false) {
1856
+ $this->_string_shift($this->packet_buffer, 4); // remove the request id
1857
+ $length-= 5; // account for the request id and the packet type
1858
+ } else {
1859
+ $length-= 1; // account for the packet type
1860
+ }
1861
+
1862
+ $packet = $this->_string_shift($this->packet_buffer, $length);
1863
+
1864
+ if (defined('NET_SFTP_LOGGING')) {
1865
+ $packet_type = '<- ' . $this->packet_types[$this->packet_type] .
1866
+ ' (' . round($stop - $start, 4) . 's)';
1867
+ if (NET_SFTP_LOGGING == NET_SFTP_LOG_REALTIME) {
1868
+ echo "<pre>\r\n" . $this->_format_log(array($packet), array($packet_type)) . "\r\n</pre>\r\n";
1869
+ flush();
1870
+ ob_flush();
1871
+ } else {
1872
+ $this->packet_type_log[] = $packet_type;
1873
+ if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) {
1874
+ $this->packet_log[] = $packet;
1875
+ }
1876
+ }
1877
+ }
1878
+
1879
+ return $packet;
1880
+ }
1881
+
1882
+ /**
1883
+ * Returns a log of the packets that have been sent and received.
1884
+ *
1885
+ * Returns a string if NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX, an array if NET_SFTP_LOGGING == NET_SFTP_LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING')
1886
+ *
1887
+ * @access public
1888
+ * @return String or Array
1889
+ */
1890
+ function getSFTPLog()
1891
+ {
1892
+ if (!defined('NET_SFTP_LOGGING')) {
1893
+ return false;
1894
+ }
1895
+
1896
+ switch (NET_SFTP_LOGGING) {
1897
+ case NET_SFTP_LOG_COMPLEX:
1898
+ return $this->_format_log($this->packet_log, $this->packet_type_log);
1899
+ break;
1900
+ //case NET_SFTP_LOG_SIMPLE:
1901
+ default:
1902
+ return $this->packet_type_log;
1903
+ }
1904
+ }
1905
+
1906
+ /**
1907
+ * Returns all errors
1908
+ *
1909
+ * @return String
1910
+ * @access public
1911
+ */
1912
+ function getSFTPErrors()
1913
+ {
1914
+ return $this->sftp_errors;
1915
+ }
1916
+
1917
+ /**
1918
+ * Returns the last error
1919
+ *
1920
+ * @return String
1921
+ * @access public
1922
+ */
1923
+ function getLastSFTPError()
1924
+ {
1925
+ return count($this->sftp_errors) ? $this->sftp_errors[count($this->sftp_errors) - 1] : '';
1926
+ }
1927
+
1928
+ /**
1929
+ * Get supported SFTP versions
1930
+ *
1931
+ * @return Array
1932
+ * @access public
1933
+ */
1934
+ function getSupportedVersions()
1935
+ {
1936
+ $temp = array('version' => $this->version);
1937
+ if (isset($this->extensions['versions'])) {
1938
+ $temp['extensions'] = $this->extensions['versions'];
1939
+ }
1940
+ return $temp;
1941
+ }
1942
+
1943
+ /**
1944
+ * Disconnect
1945
+ *
1946
+ * @param Integer $reason
1947
+ * @return Boolean
1948
+ * @access private
1949
+ */
1950
+ function _disconnect($reason)
1951
+ {
1952
+ $this->pwd = false;
1953
+ parent::_disconnect($reason);
1954
+ }
1955
+ }
phpseclib/Net/SSH2.php ADDED
@@ -0,0 +1,2762 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP implementation of SSHv2.
6
+ *
7
+ * PHP versions 4 and 5
8
+ *
9
+ * Here are some examples of how to use this library:
10
+ * <code>
11
+ * <?php
12
+ * include('Net/SSH2.php');
13
+ *
14
+ * $ssh = new Net_SSH2('www.domain.tld');
15
+ * if (!$ssh->login('username', 'password')) {
16
+ * exit('Login Failed');
17
+ * }
18
+ *
19
+ * echo $ssh->exec('pwd');
20
+ * echo $ssh->exec('ls -la');
21
+ * ?>
22
+ * </code>
23
+ *
24
+ * <code>
25
+ * <?php
26
+ * include('Crypt/RSA.php');
27
+ * include('Net/SSH2.php');
28
+ *
29
+ * $key = new Crypt_RSA();
30
+ * //$key->setPassword('whatever');
31
+ * $key->loadKey(file_get_contents('privatekey'));
32
+ *
33
+ * $ssh = new Net_SSH2('www.domain.tld');
34
+ * if (!$ssh->login('username', $key)) {
35
+ * exit('Login Failed');
36
+ * }
37
+ *
38
+ * echo $ssh->read('username@username:~$');
39
+ * $ssh->write("ls -la\n");
40
+ * echo $ssh->read('username@username:~$');
41
+ * ?>
42
+ * </code>
43
+ *
44
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
45
+ * of this software and associated documentation files (the "Software"), to deal
46
+ * in the Software without restriction, including without limitation the rights
47
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
48
+ * copies of the Software, and to permit persons to whom the Software is
49
+ * furnished to do so, subject to the following conditions:
50
+ *
51
+ * The above copyright notice and this permission notice shall be included in
52
+ * all copies or substantial portions of the Software.
53
+ *
54
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
55
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
56
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
57
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
58
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
59
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
60
+ * THE SOFTWARE.
61
+ *
62
+ * @category Net
63
+ * @package Net_SSH2
64
+ * @author Jim Wigginton <terrafrost@php.net>
65
+ * @copyright MMVII Jim Wigginton
66
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
67
+ * @version $Id: SSH2.php,v 1.53 2010-10-24 01:24:30 terrafrost Exp $
68
+ * @link http://phpseclib.sourceforge.net
69
+ */
70
+
71
+ /**
72
+ * Include Math_BigInteger
73
+ *
74
+ * Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
75
+ */
76
+ require_once('Math/BigInteger.php');
77
+
78
+ /**
79
+ * Include Crypt_Random
80
+ */
81
+ require_once('Crypt/Random.php');
82
+
83
+ /**
84
+ * Include Crypt_Hash
85
+ */
86
+ require_once('Crypt/Hash.php');
87
+
88
+ /**
89
+ * Include Crypt_TripleDES
90
+ */
91
+ require_once('Crypt/TripleDES.php');
92
+
93
+ /**
94
+ * Include Crypt_RC4
95
+ */
96
+ require_once('Crypt/RC4.php');
97
+
98
+ /**
99
+ * Include Crypt_AES
100
+ */
101
+ require_once('Crypt/AES.php');
102
+
103
+ /**#@+
104
+ * Execution Bitmap Masks
105
+ *
106
+ * @see Net_SSH2::bitmap
107
+ * @access private
108
+ */
109
+ define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001);
110
+ define('NET_SSH2_MASK_LOGIN', 0x00000002);
111
+ define('NET_SSH2_MASK_SHELL', 0x00000004);
112
+ /**#@-*/
113
+
114
+ /**#@+
115
+ * Channel constants
116
+ *
117
+ * RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer
118
+ * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
119
+ * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
120
+ * recepient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
121
+ * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet:
122
+ * The 'recipient channel' is the channel number given in the original
123
+ * open request, and 'sender channel' is the channel number allocated by
124
+ * the other side.
125
+ *
126
+ * @see Net_SSH2::_send_channel_packet()
127
+ * @see Net_SSH2::_get_channel_packet()
128
+ * @access private
129
+ */
130
+ define('NET_SSH2_CHANNEL_EXEC', 0); // PuTTy uses 0x100
131
+ define('NET_SSH2_CHANNEL_SHELL',1);
132
+ /**#@-*/
133
+
134
+ /**#@+
135
+ * @access public
136
+ * @see Net_SSH2::getLog()
137
+ */
138
+ /**
139
+ * Returns the message numbers
140
+ */
141
+ define('NET_SSH2_LOG_SIMPLE', 1);
142
+ /**
143
+ * Returns the message content
144
+ */
145
+ define('NET_SSH2_LOG_COMPLEX', 2);
146
+ /**#@-*/
147
+
148
+ /**#@+
149
+ * @access public
150
+ * @see Net_SSH2::read()
151
+ */
152
+ /**
153
+ * Returns when a string matching $expect exactly is found
154
+ */
155
+ define('NET_SSH2_READ_SIMPLE', 1);
156
+ /**
157
+ * Returns when a string matching the regular expression $expect is found
158
+ */
159
+ define('NET_SSH2_READ_REGEX', 2);
160
+ /**
161
+ * Make sure that the log never gets larger than this
162
+ */
163
+ define('NET_SSH2_LOG_MAX_SIZE', 1024 * 1024);
164
+ /**#@-*/
165
+
166
+ /**
167
+ * Pure-PHP implementation of SSHv2.
168
+ *
169
+ * @author Jim Wigginton <terrafrost@php.net>
170
+ * @version 0.1.0
171
+ * @access public
172
+ * @package Net_SSH2
173
+ */
174
+ class Net_SSH2 {
175
+ /**
176
+ * The SSH identifier
177
+ *
178
+ * @var String
179
+ * @access private
180
+ */
181
+ var $identifier = 'SSH-2.0-phpseclib_0.2';
182
+
183
+ /**
184
+ * The Socket Object
185
+ *
186
+ * @var Object
187
+ * @access private
188
+ */
189
+ var $fsock;
190
+
191
+ /**
192
+ * Execution Bitmap
193
+ *
194
+ * The bits that are set reprsent functions that have been called already. This is used to determine
195
+ * if a requisite function has been successfully executed. If not, an error should be thrown.
196
+ *
197
+ * @var Integer
198
+ * @access private
199
+ */
200
+ var $bitmap = 0;
201
+
202
+ /**
203
+ * Error information
204
+ *
205
+ * @see Net_SSH2::getErrors()
206
+ * @see Net_SSH2::getLastError()
207
+ * @var String
208
+ * @access private
209
+ */
210
+ var $errors = array();
211
+
212
+ /**
213
+ * Server Identifier
214
+ *
215
+ * @see Net_SSH2::getServerIdentification()
216
+ * @var String
217
+ * @access private
218
+ */
219
+ var $server_identifier = '';
220
+
221
+ /**
222
+ * Key Exchange Algorithms
223
+ *
224
+ * @see Net_SSH2::getKexAlgorithims()
225
+ * @var Array
226
+ * @access private
227
+ */
228
+ var $kex_algorithms;
229
+
230
+ /**
231
+ * Server Host Key Algorithms
232
+ *
233
+ * @see Net_SSH2::getServerHostKeyAlgorithms()
234
+ * @var Array
235
+ * @access private
236
+ */
237
+ var $server_host_key_algorithms;
238
+
239
+ /**
240
+ * Encryption Algorithms: Client to Server
241
+ *
242
+ * @see Net_SSH2::getEncryptionAlgorithmsClient2Server()
243
+ * @var Array
244
+ * @access private
245
+ */
246
+ var $encryption_algorithms_client_to_server;
247
+
248
+ /**
249
+ * Encryption Algorithms: Server to Client
250
+ *
251
+ * @see Net_SSH2::getEncryptionAlgorithmsServer2Client()
252
+ * @var Array
253
+ * @access private
254
+ */
255
+ var $encryption_algorithms_server_to_client;
256
+
257
+ /**
258
+ * MAC Algorithms: Client to Server
259
+ *
260
+ * @see Net_SSH2::getMACAlgorithmsClient2Server()
261
+ * @var Array
262
+ * @access private
263
+ */
264
+ var $mac_algorithms_client_to_server;
265
+
266
+ /**
267
+ * MAC Algorithms: Server to Client
268
+ *
269
+ * @see Net_SSH2::getMACAlgorithmsServer2Client()
270
+ * @var Array
271
+ * @access private
272
+ */
273
+ var $mac_algorithms_server_to_client;
274
+
275
+ /**
276
+ * Compression Algorithms: Client to Server
277
+ *
278
+ * @see Net_SSH2::getCompressionAlgorithmsClient2Server()
279
+ * @var Array
280
+ * @access private
281
+ */
282
+ var $compression_algorithms_client_to_server;
283
+
284
+ /**
285
+ * Compression Algorithms: Server to Client
286
+ *
287
+ * @see Net_SSH2::getCompressionAlgorithmsServer2Client()
288
+ * @var Array
289
+ * @access private
290
+ */
291
+ var $compression_algorithms_server_to_client;
292
+
293
+ /**
294
+ * Languages: Server to Client
295
+ *
296
+ * @see Net_SSH2::getLanguagesServer2Client()
297
+ * @var Array
298
+ * @access private
299
+ */
300
+ var $languages_server_to_client;
301
+
302
+ /**
303
+ * Languages: Client to Server
304
+ *
305
+ * @see Net_SSH2::getLanguagesClient2Server()
306
+ * @var Array
307
+ * @access private
308
+ */
309
+ var $languages_client_to_server;
310
+
311
+ /**
312
+ * Block Size for Server to Client Encryption
313
+ *
314
+ * "Note that the length of the concatenation of 'packet_length',
315
+ * 'padding_length', 'payload', and 'random padding' MUST be a multiple
316
+ * of the cipher block size or 8, whichever is larger. This constraint
317
+ * MUST be enforced, even when using stream ciphers."
318
+ *
319
+ * -- http://tools.ietf.org/html/rfc4253#section-6
320
+ *
321
+ * @see Net_SSH2::Net_SSH2()
322
+ * @see Net_SSH2::_send_binary_packet()
323
+ * @var Integer
324
+ * @access private
325
+ */
326
+ var $encrypt_block_size = 8;
327
+
328
+ /**
329
+ * Block Size for Client to Server Encryption
330
+ *
331
+ * @see Net_SSH2::Net_SSH2()
332
+ * @see Net_SSH2::_get_binary_packet()
333
+ * @var Integer
334
+ * @access private
335
+ */
336
+ var $decrypt_block_size = 8;
337
+
338
+ /**
339
+ * Server to Client Encryption Object
340
+ *
341
+ * @see Net_SSH2::_get_binary_packet()
342
+ * @var Object
343
+ * @access private
344
+ */
345
+ var $decrypt = false;
346
+
347
+ /**
348
+ * Client to Server Encryption Object
349
+ *
350
+ * @see Net_SSH2::_send_binary_packet()
351
+ * @var Object
352
+ * @access private
353
+ */
354
+ var $encrypt = false;
355
+
356
+ /**
357
+ * Client to Server HMAC Object
358
+ *
359
+ * @see Net_SSH2::_send_binary_packet()
360
+ * @var Object
361
+ * @access private
362
+ */
363
+ var $hmac_create = false;
364
+
365
+ /**
366
+ * Server to Client HMAC Object
367
+ *
368
+ * @see Net_SSH2::_get_binary_packet()
369
+ * @var Object
370
+ * @access private
371
+ */
372
+ var $hmac_check = false;
373
+
374
+ /**
375
+ * Size of server to client HMAC
376
+ *
377
+ * We need to know how big the HMAC will be for the server to client direction so that we know how many bytes to read.
378
+ * For the client to server side, the HMAC object will make the HMAC as long as it needs to be. All we need to do is
379
+ * append it.
380
+ *
381
+ * @see Net_SSH2::_get_binary_packet()
382
+ * @var Integer
383
+ * @access private
384
+ */
385
+ var $hmac_size = false;
386
+
387
+ /**
388
+ * Server Public Host Key
389
+ *
390
+ * @see Net_SSH2::getServerPublicHostKey()
391
+ * @var String
392
+ * @access private
393
+ */
394
+ var $server_public_host_key;
395
+
396
+ /**
397
+ * Session identifer
398
+ *
399
+ * "The exchange hash H from the first key exchange is additionally
400
+ * used as the session identifier, which is a unique identifier for
401
+ * this connection."
402
+ *
403
+ * -- http://tools.ietf.org/html/rfc4253#section-7.2
404
+ *
405
+ * @see Net_SSH2::_key_exchange()
406
+ * @var String
407
+ * @access private
408
+ */
409
+ var $session_id = false;
410
+
411
+ /**
412
+ * Exchange hash
413
+ *
414
+ * The current exchange hash
415
+ *
416
+ * @see Net_SSH2::_key_exchange()
417
+ * @var String
418
+ * @access private
419
+ */
420
+ var $exchange_hash = false;
421
+
422
+ /**
423
+ * Message Numbers
424
+ *
425
+ * @see Net_SSH2::Net_SSH2()
426
+ * @var Array
427
+ * @access private
428
+ */
429
+ var $message_numbers = array();
430
+
431
+ /**
432
+ * Disconnection Message 'reason codes' defined in RFC4253
433
+ *
434
+ * @see Net_SSH2::Net_SSH2()
435
+ * @var Array
436
+ * @access private
437
+ */
438
+ var $disconnect_reasons = array();
439
+
440
+ /**
441
+ * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254
442
+ *
443
+ * @see Net_SSH2::Net_SSH2()
444
+ * @var Array
445
+ * @access private
446
+ */
447
+ var $channel_open_failure_reasons = array();
448
+
449
+ /**
450
+ * Terminal Modes
451
+ *
452
+ * @link http://tools.ietf.org/html/rfc4254#section-8
453
+ * @see Net_SSH2::Net_SSH2()
454
+ * @var Array
455
+ * @access private
456
+ */
457
+ var $terminal_modes = array();
458
+
459
+ /**
460
+ * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
461
+ *
462
+ * @link http://tools.ietf.org/html/rfc4254#section-5.2
463
+ * @see Net_SSH2::Net_SSH2()
464
+ * @var Array
465
+ * @access private
466
+ */
467
+ var $channel_extended_data_type_codes = array();
468
+
469
+ /**
470
+ * Send Sequence Number
471
+ *
472
+ * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
473
+ *
474
+ * @see Net_SSH2::_send_binary_packet()
475
+ * @var Integer
476
+ * @access private
477
+ */
478
+ var $send_seq_no = 0;
479
+
480
+ /**
481
+ * Get Sequence Number
482
+ *
483
+ * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
484
+ *
485
+ * @see Net_SSH2::_get_binary_packet()
486
+ * @var Integer
487
+ * @access private
488
+ */
489
+ var $get_seq_no = 0;
490
+
491
+ /**
492
+ * Server Channels
493
+ *
494
+ * Maps client channels to server channels
495
+ *
496
+ * @see Net_SSH2::_get_channel_packet()
497
+ * @see Net_SSH2::exec()
498
+ * @var Array
499
+ * @access private
500
+ */
501
+ var $server_channels = array();
502
+
503
+ /**
504
+ * Channel Buffers
505
+ *
506
+ * If a client requests a packet from one channel but receives two packets from another those packets should
507
+ * be placed in a buffer
508
+ *
509
+ * @see Net_SSH2::_get_channel_packet()
510
+ * @see Net_SSH2::exec()
511
+ * @var Array
512
+ * @access private
513
+ */
514
+ var $channel_buffers = array();
515
+
516
+ /**
517
+ * Channel Status
518
+ *
519
+ * Contains the type of the last sent message
520
+ *
521
+ * @see Net_SSH2::_get_channel_packet()
522
+ * @var Array
523
+ * @access private
524
+ */
525
+ var $channel_status = array();
526
+
527
+ /**
528
+ * Packet Size
529
+ *
530
+ * Maximum packet size indexed by channel
531
+ *
532
+ * @see Net_SSH2::_send_channel_packet()
533
+ * @var Array
534
+ * @access private
535
+ */
536
+ var $packet_size_client_to_server = array();
537
+
538
+ /**
539
+ * Message Number Log
540
+ *
541
+ * @see Net_SSH2::getLog()
542
+ * @var Array
543
+ * @access private
544
+ */
545
+ var $message_number_log = array();
546
+
547
+ /**
548
+ * Message Log
549
+ *
550
+ * @see Net_SSH2::getLog()
551
+ * @var Array
552
+ * @access private
553
+ */
554
+ var $message_log = array();
555
+
556
+ /**
557
+ * The Window Size
558
+ *
559
+ * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 4GB)
560
+ *
561
+ * @var Integer
562
+ * @see Net_SSH2::_send_channel_packet()
563
+ * @see Net_SSH2::exec()
564
+ * @access private
565
+ */
566
+ var $window_size = 0x7FFFFFFF;
567
+
568
+ /**
569
+ * Window size
570
+ *
571
+ * Window size indexed by channel
572
+ *
573
+ * @see Net_SSH2::_send_channel_packet()
574
+ * @var Array
575
+ * @access private
576
+ */
577
+ var $window_size_client_to_server = array();
578
+
579
+ /**
580
+ * Server signature
581
+ *
582
+ * Verified against $this->session_id
583
+ *
584
+ * @see Net_SSH2::getServerPublicHostKey()
585
+ * @var String
586
+ * @access private
587
+ */
588
+ var $signature = '';
589
+
590
+ /**
591
+ * Server signature format
592
+ *
593
+ * ssh-rsa or ssh-dss.
594
+ *
595
+ * @see Net_SSH2::getServerPublicHostKey()
596
+ * @var String
597
+ * @access private
598
+ */
599
+ var $signature_format = '';
600
+
601
+ /**
602
+ * Interactive Buffer
603
+ *
604
+ * @see Net_SSH2::read()
605
+ * @var Array
606
+ * @access private
607
+ */
608
+ var $interactiveBuffer = '';
609
+
610
+ /**
611
+ * Current log size
612
+ *
613
+ * Should never exceed NET_SSH2_LOG_MAX_SIZE
614
+ *
615
+ * @see Net_SSH2::_send_binary_packet()
616
+ * @see Net_SSH2::_get_binary_packet()
617
+ * @var Integer
618
+ * @access private
619
+ */
620
+ var $log_size;
621
+
622
+ /**
623
+ * Timeout
624
+ *
625
+ * @see Net_SSH2::setTimeout()
626
+ * @access private
627
+ */
628
+ var $timeout;
629
+
630
+ /**
631
+ * Current Timeout
632
+ *
633
+ * @see Net_SSH2::_get_channel_packet()
634
+ * @access private
635
+ */
636
+ var $curTimeout;
637
+
638
+ /**
639
+ * Default Constructor.
640
+ *
641
+ * Connects to an SSHv2 server
642
+ *
643
+ * @param String $host
644
+ * @param optional Integer $port
645
+ * @param optional Integer $timeout
646
+ * @return Net_SSH2
647
+ * @access public
648
+ */
649
+ function Net_SSH2($host, $port = 22, $timeout = 10)
650
+ {
651
+ $this->message_numbers = array(
652
+ 1 => 'NET_SSH2_MSG_DISCONNECT',
653
+ 2 => 'NET_SSH2_MSG_IGNORE',
654
+ 3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
655
+ 4 => 'NET_SSH2_MSG_DEBUG',
656
+ 5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
657
+ 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
658
+ 20 => 'NET_SSH2_MSG_KEXINIT',
659
+ 21 => 'NET_SSH2_MSG_NEWKEYS',
660
+ 30 => 'NET_SSH2_MSG_KEXDH_INIT',
661
+ 31 => 'NET_SSH2_MSG_KEXDH_REPLY',
662
+ 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
663
+ 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
664
+ 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
665
+ 53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
666
+
667
+ 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
668
+ 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
669
+ 82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
670
+ 90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
671
+ 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
672
+ 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
673
+ 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
674
+ 94 => 'NET_SSH2_MSG_CHANNEL_DATA',
675
+ 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
676
+ 96 => 'NET_SSH2_MSG_CHANNEL_EOF',
677
+ 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
678
+ 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
679
+ 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
680
+ 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
681
+ );
682
+ $this->disconnect_reasons = array(
683
+ 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
684
+ 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
685
+ 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
686
+ 4 => 'NET_SSH2_DISCONNECT_RESERVED',
687
+ 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
688
+ 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
689
+ 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
690
+ 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
691
+ 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
692
+ 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
693
+ 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
694
+ 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
695
+ 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
696
+ 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
697
+ 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
698
+ );
699
+ $this->channel_open_failure_reasons = array(
700
+ 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
701
+ );
702
+ $this->terminal_modes = array(
703
+ 0 => 'NET_SSH2_TTY_OP_END'
704
+ );
705
+ $this->channel_extended_data_type_codes = array(
706
+ 1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
707
+ );
708
+
709
+ $this->_define_array(
710
+ $this->message_numbers,
711
+ $this->disconnect_reasons,
712
+ $this->channel_open_failure_reasons,
713
+ $this->terminal_modes,
714
+ $this->channel_extended_data_type_codes,
715
+ array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
716
+ array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'),
717
+ array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
718
+ 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE')
719
+ );
720
+
721
+ $this->fsock = @fsockopen($host, $port, $errno, $errstr, $timeout);
722
+ if (!$this->fsock) {
723
+ user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"), E_USER_NOTICE);
724
+ return;
725
+ }
726
+
727
+ /* According to the SSH2 specs,
728
+
729
+ "The server MAY send other lines of data before sending the version
730
+ string. Each line SHOULD be terminated by a Carriage Return and Line
731
+ Feed. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded
732
+ in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients
733
+ MUST be able to process such lines." */
734
+ $temp = '';
735
+ $extra = '';
736
+ while (!feof($this->fsock) && !preg_match('#^SSH-(\d\.\d+)#', $temp, $matches)) {
737
+ if (substr($temp, -2) == "\r\n") {
738
+ $extra.= $temp;
739
+ $temp = '';
740
+ }
741
+ $temp.= fgets($this->fsock, 255);
742
+ }
743
+
744
+ if (feof($this->fsock)) {
745
+ user_error('Connection closed by server', E_USER_NOTICE);
746
+ return false;
747
+ }
748
+
749
+ $ext = array();
750
+ if (extension_loaded('mcrypt')) {
751
+ $ext[] = 'mcrypt';
752
+ }
753
+ if (extension_loaded('gmp')) {
754
+ $ext[] = 'gmp';
755
+ } else if (extension_loaded('bcmath')) {
756
+ $ext[] = 'bcmath';
757
+ }
758
+
759
+ if (!empty($ext)) {
760
+ $this->identifier.= ' (' . implode(', ', $ext) . ')';
761
+ }
762
+
763
+ if (defined('NET_SSH2_LOGGING')) {
764
+ $this->message_number_log[] = '<-';
765
+ $this->message_number_log[] = '->';
766
+
767
+ if (NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
768
+ $this->message_log[] = $temp;
769
+ $this->message_log[] = $this->identifier . "\r\n";
770
+ }
771
+ }
772
+
773
+ $this->server_identifier = trim($temp, "\r\n");
774
+ if (!empty($extra)) {
775
+ $this->errors[] = utf8_decode($extra);
776
+ }
777
+
778
+ if ($matches[1] != '1.99' && $matches[1] != '2.0') {
779
+ user_error("Cannot connect to SSH $matches[1] servers", E_USER_NOTICE);
780
+ return;
781
+ }
782
+
783
+ fputs($this->fsock, $this->identifier . "\r\n");
784
+
785
+ $response = $this->_get_binary_packet();
786
+ if ($response === false) {
787
+ user_error('Connection closed by server', E_USER_NOTICE);
788
+ return;
789
+ }
790
+
791
+ if (ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
792
+ user_error('Expected SSH_MSG_KEXINIT', E_USER_NOTICE);
793
+ return;
794
+ }
795
+
796
+ if (!$this->_key_exchange($response)) {
797
+ return;
798
+ }
799
+
800
+ $this->bitmap = NET_SSH2_MASK_CONSTRUCTOR;
801
+ }
802
+
803
+ /**
804
+ * Key Exchange
805
+ *
806
+ * @param String $kexinit_payload_server
807
+ * @access private
808
+ */
809
+ function _key_exchange($kexinit_payload_server)
810
+ {
811
+ static $kex_algorithms = array(
812
+ 'diffie-hellman-group1-sha1', // REQUIRED
813
+ 'diffie-hellman-group14-sha1' // REQUIRED
814
+ );
815
+
816
+ static $server_host_key_algorithms = array(
817
+ 'ssh-rsa', // RECOMMENDED sign Raw RSA Key
818
+ 'ssh-dss' // REQUIRED sign Raw DSS Key
819
+ );
820
+
821
+ static $encryption_algorithms = array(
822
+ // from <http://tools.ietf.org/html/rfc4345#section-4>:
823
+ 'arcfour256',
824
+ 'arcfour128',
825
+
826
+ 'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key
827
+
828
+ 'aes128-cbc', // RECOMMENDED AES with a 128-bit key
829
+ 'aes192-cbc', // OPTIONAL AES with a 192-bit key
830
+ 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key
831
+
832
+ // from <http://tools.ietf.org/html/rfc4344#section-4>:
833
+ 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key
834
+ 'aes192-ctr', // RECOMMENDED AES with 192-bit key
835
+ 'aes256-ctr', // RECOMMENDED AES with 256-bit key
836
+ '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode
837
+
838
+ '3des-cbc', // REQUIRED three-key 3DES in CBC mode
839
+ 'none' // OPTIONAL no encryption; NOT RECOMMENDED
840
+ );
841
+
842
+ static $mac_algorithms = array(
843
+ 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
844
+ 'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20)
845
+ 'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
846
+ 'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16)
847
+ 'none' // OPTIONAL no MAC; NOT RECOMMENDED
848
+ );
849
+
850
+ static $compression_algorithms = array(
851
+ 'none' // REQUIRED no compression
852
+ //'zlib' // OPTIONAL ZLIB (LZ77) compression
853
+ );
854
+
855
+ static $str_kex_algorithms, $str_server_host_key_algorithms,
856
+ $encryption_algorithms_server_to_client, $mac_algorithms_server_to_client, $compression_algorithms_server_to_client,
857
+ $encryption_algorithms_client_to_server, $mac_algorithms_client_to_server, $compression_algorithms_client_to_server;
858
+
859
+ if (empty($str_kex_algorithms)) {
860
+ $str_kex_algorithms = implode(',', $kex_algorithms);
861
+ $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
862
+ $encryption_algorithms_server_to_client = $encryption_algorithms_client_to_server = implode(',', $encryption_algorithms);
863
+ $mac_algorithms_server_to_client = $mac_algorithms_client_to_server = implode(',', $mac_algorithms);
864
+ $compression_algorithms_server_to_client = $compression_algorithms_client_to_server = implode(',', $compression_algorithms);
865
+ }
866
+
867
+ $client_cookie = '';
868
+ for ($i = 0; $i < 16; $i++) {
869
+ $client_cookie.= chr(crypt_random(0, 255));
870
+ }
871
+
872
+ $response = $kexinit_payload_server;
873
+ $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
874
+ $server_cookie = $this->_string_shift($response, 16);
875
+
876
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
877
+ $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
878
+
879
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
880
+ $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
881
+
882
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
883
+ $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
884
+
885
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
886
+ $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
887
+
888
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
889
+ $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
890
+
891
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
892
+ $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
893
+
894
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
895
+ $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
896
+
897
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
898
+ $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
899
+
900
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
901
+ $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
902
+
903
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
904
+ $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
905
+
906
+ extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
907
+ $first_kex_packet_follows = $first_kex_packet_follows != 0;
908
+
909
+ // the sending of SSH2_MSG_KEXINIT could go in one of two places. this is the second place.
910
+ $kexinit_payload_client = pack('Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
911
+ NET_SSH2_MSG_KEXINIT, $client_cookie, strlen($str_kex_algorithms), $str_kex_algorithms,
912
+ strlen($str_server_host_key_algorithms), $str_server_host_key_algorithms, strlen($encryption_algorithms_client_to_server),
913
+ $encryption_algorithms_client_to_server, strlen($encryption_algorithms_server_to_client), $encryption_algorithms_server_to_client,
914
+ strlen($mac_algorithms_client_to_server), $mac_algorithms_client_to_server, strlen($mac_algorithms_server_to_client),
915
+ $mac_algorithms_server_to_client, strlen($compression_algorithms_client_to_server), $compression_algorithms_client_to_server,
916
+ strlen($compression_algorithms_server_to_client), $compression_algorithms_server_to_client, 0, '', 0, '',
917
+ 0, 0
918
+ );
919
+
920
+ if (!$this->_send_binary_packet($kexinit_payload_client)) {
921
+ return false;
922
+ }
923
+ // here ends the second place.
924
+
925
+ // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
926
+ for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_server_to_client); $i++);
927
+ if ($i == count($encryption_algorithms)) {
928
+ user_error('No compatible server to client encryption algorithms found', E_USER_NOTICE);
929
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
930
+ }
931
+
932
+ // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
933
+ // diffie-hellman key exchange as fast as possible
934
+ $decrypt = $encryption_algorithms[$i];
935
+ switch ($decrypt) {
936
+ case '3des-cbc':
937
+ case '3des-ctr':
938
+ $decryptKeyLength = 24; // eg. 192 / 8
939
+ break;
940
+ case 'aes256-cbc':
941
+ case 'aes256-ctr':
942
+ $decryptKeyLength = 32; // eg. 256 / 8
943
+ break;
944
+ case 'aes192-cbc':
945
+ case 'aes192-ctr':
946
+ $decryptKeyLength = 24; // eg. 192 / 8
947
+ break;
948
+ case 'aes128-cbc':
949
+ case 'aes128-ctr':
950
+ $decryptKeyLength = 16; // eg. 128 / 8
951
+ break;
952
+ case 'arcfour':
953
+ case 'arcfour128':
954
+ $decryptKeyLength = 16; // eg. 128 / 8
955
+ break;
956
+ case 'arcfour256':
957
+ $decryptKeyLength = 32; // eg. 128 / 8
958
+ break;
959
+ case 'none';
960
+ $decryptKeyLength = 0;
961
+ }
962
+
963
+ for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_client_to_server); $i++);
964
+ if ($i == count($encryption_algorithms)) {
965
+ user_error('No compatible client to server encryption algorithms found', E_USER_NOTICE);
966
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
967
+ }
968
+
969
+ $encrypt = $encryption_algorithms[$i];
970
+ switch ($encrypt) {
971
+ case '3des-cbc':
972
+ case '3des-ctr':
973
+ $encryptKeyLength = 24;
974
+ break;
975
+ case 'aes256-cbc':
976
+ case 'aes256-ctr':
977
+ $encryptKeyLength = 32;
978
+ break;
979
+ case 'aes192-cbc':
980
+ case 'aes192-ctr':
981
+ $encryptKeyLength = 24;
982
+ break;
983
+ case 'aes128-cbc':
984
+ case 'aes128-ctr':
985
+ $encryptKeyLength = 16;
986
+ break;
987
+ case 'arcfour':
988
+ case 'arcfour128':
989
+ $encryptKeyLength = 16;
990
+ break;
991
+ case 'arcfour256':
992
+ $encryptKeyLength = 32;
993
+ break;
994
+ case 'none';
995
+ $encryptKeyLength = 0;
996
+ }
997
+
998
+ $keyLength = $decryptKeyLength > $encryptKeyLength ? $decryptKeyLength : $encryptKeyLength;
999
+
1000
+ // through diffie-hellman key exchange a symmetric key is obtained
1001
+ for ($i = 0; $i < count($kex_algorithms) && !in_array($kex_algorithms[$i], $this->kex_algorithms); $i++);
1002
+ if ($i == count($kex_algorithms)) {
1003
+ user_error('No compatible key exchange algorithms found', E_USER_NOTICE);
1004
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1005
+ }
1006
+
1007
+ switch ($kex_algorithms[$i]) {
1008
+ // see http://tools.ietf.org/html/rfc2409#section-6.2 and
1009
+ // http://tools.ietf.org/html/rfc2412, appendex E
1010
+ case 'diffie-hellman-group1-sha1':
1011
+ $p = pack('H256', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
1012
+ '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
1013
+ '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
1014
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF');
1015
+ $keyLength = $keyLength < 160 ? $keyLength : 160;
1016
+ $hash = 'sha1';
1017
+ break;
1018
+ // see http://tools.ietf.org/html/rfc3526#section-3
1019
+ case 'diffie-hellman-group14-sha1':
1020
+ $p = pack('H512', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
1021
+ '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
1022
+ '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
1023
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
1024
+ '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
1025
+ '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
1026
+ 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
1027
+ '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF');
1028
+ $keyLength = $keyLength < 160 ? $keyLength : 160;
1029
+ $hash = 'sha1';
1030
+ }
1031
+
1032
+ $p = new Math_BigInteger($p, 256);
1033
+ //$q = $p->bitwise_rightShift(1);
1034
+
1035
+ /* To increase the speed of the key exchange, both client and server may
1036
+ reduce the size of their private exponents. It should be at least
1037
+ twice as long as the key material that is generated from the shared
1038
+ secret. For more details, see the paper by van Oorschot and Wiener
1039
+ [VAN-OORSCHOT].
1040
+
1041
+ -- http://tools.ietf.org/html/rfc4419#section-6.2 */
1042
+ $q = new Math_BigInteger(1);
1043
+ $q = $q->bitwise_leftShift(2 * $keyLength);
1044
+ $q = $q->subtract(new Math_BigInteger(1));
1045
+
1046
+ $g = new Math_BigInteger(2);
1047
+ $x = new Math_BigInteger();
1048
+ $x->setRandomGenerator('crypt_random');
1049
+ $x = $x->random(new Math_BigInteger(1), $q);
1050
+ $e = $g->modPow($x, $p);
1051
+
1052
+ $eBytes = $e->toBytes(true);
1053
+ $data = pack('CNa*', NET_SSH2_MSG_KEXDH_INIT, strlen($eBytes), $eBytes);
1054
+
1055
+ if (!$this->_send_binary_packet($data)) {
1056
+ user_error('Connection closed by server', E_USER_NOTICE);
1057
+ return false;
1058
+ }
1059
+
1060
+ $response = $this->_get_binary_packet();
1061
+ if ($response === false) {
1062
+ user_error('Connection closed by server', E_USER_NOTICE);
1063
+ return false;
1064
+ }
1065
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
1066
+
1067
+ if ($type != NET_SSH2_MSG_KEXDH_REPLY) {
1068
+ user_error('Expected SSH_MSG_KEXDH_REPLY', E_USER_NOTICE);
1069
+ return false;
1070
+ }
1071
+
1072
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
1073
+ $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']);
1074
+
1075
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
1076
+ $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']);
1077
+
1078
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
1079
+ $fBytes = $this->_string_shift($response, $temp['length']);
1080
+ $f = new Math_BigInteger($fBytes, -256);
1081
+
1082
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
1083
+ $this->signature = $this->_string_shift($response, $temp['length']);
1084
+
1085
+ $temp = unpack('Nlength', $this->_string_shift($this->signature, 4));
1086
+ $this->signature_format = $this->_string_shift($this->signature, $temp['length']);
1087
+
1088
+ $key = $f->modPow($x, $p);
1089
+ $keyBytes = $key->toBytes(true);
1090
+
1091
+ $this->exchange_hash = pack('Na*Na*Na*Na*Na*Na*Na*Na*',
1092
+ strlen($this->identifier), $this->identifier, strlen($this->server_identifier), $this->server_identifier,
1093
+ strlen($kexinit_payload_client), $kexinit_payload_client, strlen($kexinit_payload_server),
1094
+ $kexinit_payload_server, strlen($this->server_public_host_key), $this->server_public_host_key, strlen($eBytes),
1095
+ $eBytes, strlen($fBytes), $fBytes, strlen($keyBytes), $keyBytes
1096
+ );
1097
+
1098
+ $this->exchange_hash = pack('H*', $hash($this->exchange_hash));
1099
+
1100
+ if ($this->session_id === false) {
1101
+ $this->session_id = $this->exchange_hash;
1102
+ }
1103
+
1104
+ for ($i = 0; $i < count($server_host_key_algorithms) && !in_array($server_host_key_algorithms[$i], $this->server_host_key_algorithms); $i++);
1105
+ if ($i == count($server_host_key_algorithms)) {
1106
+ user_error('No compatible server host key algorithms found', E_USER_NOTICE);
1107
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1108
+ }
1109
+
1110
+ if ($public_key_format != $server_host_key_algorithms[$i] || $this->signature_format != $server_host_key_algorithms[$i]) {
1111
+ user_error('Sever Host Key Algorithm Mismatch', E_USER_NOTICE);
1112
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1113
+ }
1114
+
1115
+ $packet = pack('C',
1116
+ NET_SSH2_MSG_NEWKEYS
1117
+ );
1118
+
1119
+ if (!$this->_send_binary_packet($packet)) {
1120
+ return false;
1121
+ }
1122
+
1123
+ $response = $this->_get_binary_packet();
1124
+
1125
+ if ($response === false) {
1126
+ user_error('Connection closed by server', E_USER_NOTICE);
1127
+ return false;
1128
+ }
1129
+
1130
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
1131
+
1132
+ if ($type != NET_SSH2_MSG_NEWKEYS) {
1133
+ user_error('Expected SSH_MSG_NEWKEYS', E_USER_NOTICE);
1134
+ return false;
1135
+ }
1136
+
1137
+ switch ($encrypt) {
1138
+ case '3des-cbc':
1139
+ $this->encrypt = new Crypt_TripleDES();
1140
+ // $this->encrypt_block_size = 64 / 8 == the default
1141
+ break;
1142
+ case '3des-ctr':
1143
+ $this->encrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
1144
+ // $this->encrypt_block_size = 64 / 8 == the default
1145
+ break;
1146
+ case 'aes256-cbc':
1147
+ case 'aes192-cbc':
1148
+ case 'aes128-cbc':
1149
+ $this->encrypt = new Crypt_AES();
1150
+ $this->encrypt_block_size = 16; // eg. 128 / 8
1151
+ break;
1152
+ case 'aes256-ctr':
1153
+ case 'aes192-ctr':
1154
+ case 'aes128-ctr':
1155
+ $this->encrypt = new Crypt_AES(CRYPT_AES_MODE_CTR);
1156
+ $this->encrypt_block_size = 16; // eg. 128 / 8
1157
+ break;
1158
+ case 'arcfour':
1159
+ case 'arcfour128':
1160
+ case 'arcfour256':
1161
+ $this->encrypt = new Crypt_RC4();
1162
+ break;
1163
+ case 'none';
1164
+ //$this->encrypt = new Crypt_Null();
1165
+ }
1166
+
1167
+ switch ($decrypt) {
1168
+ case '3des-cbc':
1169
+ $this->decrypt = new Crypt_TripleDES();
1170
+ break;
1171
+ case '3des-ctr':
1172
+ $this->decrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
1173
+ break;
1174
+ case 'aes256-cbc':
1175
+ case 'aes192-cbc':
1176
+ case 'aes128-cbc':
1177
+ $this->decrypt = new Crypt_AES();
1178
+ $this->decrypt_block_size = 16;
1179
+ break;
1180
+ case 'aes256-ctr':
1181
+ case 'aes192-ctr':
1182
+ case 'aes128-ctr':
1183
+ $this->decrypt = new Crypt_AES(CRYPT_AES_MODE_CTR);
1184
+ $this->decrypt_block_size = 16;
1185
+ break;
1186
+ case 'arcfour':
1187
+ case 'arcfour128':
1188
+ case 'arcfour256':
1189
+ $this->decrypt = new Crypt_RC4();
1190
+ break;
1191
+ case 'none';
1192
+ //$this->decrypt = new Crypt_Null();
1193
+ }
1194
+
1195
+ $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
1196
+
1197
+ if ($this->encrypt) {
1198
+ $this->encrypt->enableContinuousBuffer();
1199
+ $this->encrypt->disablePadding();
1200
+
1201
+ $iv = pack('H*', $hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id));
1202
+ while ($this->encrypt_block_size > strlen($iv)) {
1203
+ $iv.= pack('H*', $hash($keyBytes . $this->exchange_hash . $iv));
1204
+ }
1205
+ $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
1206
+
1207
+ $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id));
1208
+ while ($encryptKeyLength > strlen($key)) {
1209
+ $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
1210
+ }
1211
+ $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
1212
+ }
1213
+
1214
+ if ($this->decrypt) {
1215
+ $this->decrypt->enableContinuousBuffer();
1216
+ $this->decrypt->disablePadding();
1217
+
1218
+ $iv = pack('H*', $hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id));
1219
+ while ($this->decrypt_block_size > strlen($iv)) {
1220
+ $iv.= pack('H*', $hash($keyBytes . $this->exchange_hash . $iv));
1221
+ }
1222
+ $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
1223
+
1224
+ $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id));
1225
+ while ($decryptKeyLength > strlen($key)) {
1226
+ $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
1227
+ }
1228
+ $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
1229
+ }
1230
+
1231
+ /* The "arcfour128" algorithm is the RC4 cipher, as described in
1232
+ [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream
1233
+ generated by the cipher MUST be discarded, and the first byte of the
1234
+ first encrypted packet MUST be encrypted using the 1537th byte of
1235
+ keystream.
1236
+
1237
+ -- http://tools.ietf.org/html/rfc4345#section-4 */
1238
+ if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') {
1239
+ $this->encrypt->encrypt(str_repeat("\0", 1536));
1240
+ }
1241
+ if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') {
1242
+ $this->decrypt->decrypt(str_repeat("\0", 1536));
1243
+ }
1244
+
1245
+ for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_client_to_server); $i++);
1246
+ if ($i == count($mac_algorithms)) {
1247
+ user_error('No compatible client to server message authentication algorithms found', E_USER_NOTICE);
1248
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1249
+ }
1250
+
1251
+ $createKeyLength = 0; // ie. $mac_algorithms[$i] == 'none'
1252
+ switch ($mac_algorithms[$i]) {
1253
+ case 'hmac-sha1':
1254
+ $this->hmac_create = new Crypt_Hash('sha1');
1255
+ $createKeyLength = 20;
1256
+ break;
1257
+ case 'hmac-sha1-96':
1258
+ $this->hmac_create = new Crypt_Hash('sha1-96');
1259
+ $createKeyLength = 20;
1260
+ break;
1261
+ case 'hmac-md5':
1262
+ $this->hmac_create = new Crypt_Hash('md5');
1263
+ $createKeyLength = 16;
1264
+ break;
1265
+ case 'hmac-md5-96':
1266
+ $this->hmac_create = new Crypt_Hash('md5-96');
1267
+ $createKeyLength = 16;
1268
+ }
1269
+
1270
+ for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_server_to_client); $i++);
1271
+ if ($i == count($mac_algorithms)) {
1272
+ user_error('No compatible server to client message authentication algorithms found', E_USER_NOTICE);
1273
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1274
+ }
1275
+
1276
+ $checkKeyLength = 0;
1277
+ $this->hmac_size = 0;
1278
+ switch ($mac_algorithms[$i]) {
1279
+ case 'hmac-sha1':
1280
+ $this->hmac_check = new Crypt_Hash('sha1');
1281
+ $checkKeyLength = 20;
1282
+ $this->hmac_size = 20;
1283
+ break;
1284
+ case 'hmac-sha1-96':
1285
+ $this->hmac_check = new Crypt_Hash('sha1-96');
1286
+ $checkKeyLength = 20;
1287
+ $this->hmac_size = 12;
1288
+ break;
1289
+ case 'hmac-md5':
1290
+ $this->hmac_check = new Crypt_Hash('md5');
1291
+ $checkKeyLength = 16;
1292
+ $this->hmac_size = 16;
1293
+ break;
1294
+ case 'hmac-md5-96':
1295
+ $this->hmac_check = new Crypt_Hash('md5-96');
1296
+ $checkKeyLength = 16;
1297
+ $this->hmac_size = 12;
1298
+ }
1299
+
1300
+ $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id));
1301
+ while ($createKeyLength > strlen($key)) {
1302
+ $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
1303
+ }
1304
+ $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
1305
+
1306
+ $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id));
1307
+ while ($checkKeyLength > strlen($key)) {
1308
+ $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
1309
+ }
1310
+ $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
1311
+
1312
+ for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_server_to_client); $i++);
1313
+ if ($i == count($compression_algorithms)) {
1314
+ user_error('No compatible server to client compression algorithms found', E_USER_NOTICE);
1315
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1316
+ }
1317
+ $this->decompress = $compression_algorithms[$i] == 'zlib';
1318
+
1319
+ for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_client_to_server); $i++);
1320
+ if ($i == count($compression_algorithms)) {
1321
+ user_error('No compatible client to server compression algorithms found', E_USER_NOTICE);
1322
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1323
+ }
1324
+ $this->compress = $compression_algorithms[$i] == 'zlib';
1325
+
1326
+ return true;
1327
+ }
1328
+
1329
+ /**
1330
+ * Login
1331
+ *
1332
+ * The $password parameter can be a plaintext password or a Crypt_RSA object.
1333
+ *
1334
+ * @param String $username
1335
+ * @param optional String $password
1336
+ * @return Boolean
1337
+ * @access public
1338
+ * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
1339
+ * by sending dummy SSH_MSG_IGNORE messages.
1340
+ */
1341
+ function login($username, $password = '')
1342
+ {
1343
+ if (!($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR)) {
1344
+ return false;
1345
+ }
1346
+
1347
+ $packet = pack('CNa*',
1348
+ NET_SSH2_MSG_SERVICE_REQUEST, strlen('ssh-userauth'), 'ssh-userauth'
1349
+ );
1350
+
1351
+ if (!$this->_send_binary_packet($packet)) {
1352
+ return false;
1353
+ }
1354
+
1355
+ $response = $this->_get_binary_packet();
1356
+ if ($response === false) {
1357
+ user_error('Connection closed by server', E_USER_NOTICE);
1358
+ return false;
1359
+ }
1360
+
1361
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
1362
+
1363
+ if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
1364
+ user_error('Expected SSH_MSG_SERVICE_ACCEPT', E_USER_NOTICE);
1365
+ return false;
1366
+ }
1367
+
1368
+ // although PHP5's get_class() preserves the case, PHP4's does not
1369
+ if (is_object($password) && strtolower(get_class($password)) == 'crypt_rsa') {
1370
+ return $this->_privatekey_login($username, $password);
1371
+ }
1372
+
1373
+ $packet = pack('CNa*Na*Na*CNa*',
1374
+ NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
1375
+ strlen('password'), 'password', 0, strlen($password), $password
1376
+ );
1377
+
1378
+ if (!$this->_send_binary_packet($packet)) {
1379
+ return false;
1380
+ }
1381
+
1382
+ // remove the username and password from the last logged packet
1383
+ if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
1384
+ $packet = pack('CNa*Na*Na*CNa*',
1385
+ NET_SSH2_MSG_USERAUTH_REQUEST, strlen('username'), 'username', strlen('ssh-connection'), 'ssh-connection',
1386
+ strlen('password'), 'password', 0, strlen('password'), 'password'
1387
+ );
1388
+ $this->message_log[count($this->message_log) - 1] = $packet;
1389
+ }
1390
+
1391
+ $response = $this->_get_binary_packet();
1392
+ if ($response === false) {
1393
+ user_error('Connection closed by server', E_USER_NOTICE);
1394
+ return false;
1395
+ }
1396
+
1397
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
1398
+
1399
+ switch ($type) {
1400
+ case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed
1401
+ if (defined('NET_SSH2_LOGGING')) {
1402
+ $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ';
1403
+ }
1404
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1405
+ $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . utf8_decode($this->_string_shift($response, $length));
1406
+ return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
1407
+ case NET_SSH2_MSG_USERAUTH_FAILURE:
1408
+ // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees
1409
+ // multi-factor authentication
1410
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1411
+ $auth_methods = explode(',', $this->_string_shift($response, $length));
1412
+ if (in_array('keyboard-interactive', $auth_methods)) {
1413
+ if ($this->_keyboard_interactive_login($username, $password)) {
1414
+ $this->bitmap |= NET_SSH2_MASK_LOGIN;
1415
+ return true;
1416
+ }
1417
+ return false;
1418
+ }
1419
+ return false;
1420
+ case NET_SSH2_MSG_USERAUTH_SUCCESS:
1421
+ $this->bitmap |= NET_SSH2_MASK_LOGIN;
1422
+ return true;
1423
+ }
1424
+
1425
+ return false;
1426
+ }
1427
+
1428
+ /**
1429
+ * Login via keyboard-interactive authentication
1430
+ *
1431
+ * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details. This is not a full-featured keyboard-interactive authenticator.
1432
+ *
1433
+ * @param String $username
1434
+ * @param String $password
1435
+ * @return Boolean
1436
+ * @access private
1437
+ */
1438
+ function _keyboard_interactive_login($username, $password)
1439
+ {
1440
+ $packet = pack('CNa*Na*Na*Na*Na*',
1441
+ NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
1442
+ strlen('keyboard-interactive'), 'keyboard-interactive', 0, '', 0, ''
1443
+ );
1444
+
1445
+ if (!$this->_send_binary_packet($packet)) {
1446
+ return false;
1447
+ }
1448
+
1449
+ return $this->_keyboard_interactive_process($password);
1450
+ }
1451
+
1452
+ /**
1453
+ * Handle the keyboard-interactive requests / responses.
1454
+ *
1455
+ * @param String $responses...
1456
+ * @return Boolean
1457
+ * @access private
1458
+ */
1459
+ function _keyboard_interactive_process()
1460
+ {
1461
+ $responses = func_get_args();
1462
+
1463
+ $response = $this->_get_binary_packet();
1464
+ if ($response === false) {
1465
+ user_error('Connection closed by server', E_USER_NOTICE);
1466
+ return false;
1467
+ }
1468
+
1469
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
1470
+
1471
+ switch ($type) {
1472
+ case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
1473
+ // see http://tools.ietf.org/html/rfc4256#section-3.2
1474
+ if (defined('NET_SSH2_LOGGING')) {
1475
+ $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
1476
+ 'UNKNOWN',
1477
+ 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
1478
+ $this->message_number_log[count($this->message_number_log) - 1]
1479
+ );
1480
+ }
1481
+
1482
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1483
+ $this->_string_shift($response, $length); // name; may be empty
1484
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1485
+ $this->_string_shift($response, $length); // instruction; may be empty
1486
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1487
+ $this->_string_shift($response, $length); // language tag; may be empty
1488
+ extract(unpack('Nnum_prompts', $this->_string_shift($response, 4)));
1489
+ /*
1490
+ for ($i = 0; $i < $num_prompts; $i++) {
1491
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1492
+ // prompt - ie. "Password: "; must not be empty
1493
+ $this->_string_shift($response, $length);
1494
+ $echo = $this->_string_shift($response) != chr(0);
1495
+ }
1496
+ */
1497
+
1498
+ /*
1499
+ After obtaining the requested information from the user, the client
1500
+ MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message.
1501
+ */
1502
+ // see http://tools.ietf.org/html/rfc4256#section-3.4
1503
+ $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses));
1504
+ for ($i = 0; $i < count($responses); $i++) {
1505
+ $packet.= pack('Na*', strlen($responses[$i]), $responses[$i]);
1506
+ $logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer');
1507
+ }
1508
+
1509
+ if (!$this->_send_binary_packet($packet)) {
1510
+ return false;
1511
+ }
1512
+
1513
+ if (defined('NET_SSH2_LOGGING')) {
1514
+ $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
1515
+ 'UNKNOWN',
1516
+ 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE',
1517
+ $this->message_number_log[count($this->message_number_log) - 1]
1518
+ );
1519
+ $this->message_log[count($this->message_log) - 1] = $logged;
1520
+ }
1521
+
1522
+ /*
1523
+ After receiving the response, the server MUST send either an
1524
+ SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another
1525
+ SSH_MSG_USERAUTH_INFO_REQUEST message.
1526
+ */
1527
+ // maybe phpseclib should force close the connection after x request / responses? unless something like that is done
1528
+ // there could be an infinite loop of request / responses.
1529
+ return $this->_keyboard_interactive_process();
1530
+ case NET_SSH2_MSG_USERAUTH_SUCCESS:
1531
+ return true;
1532
+ case NET_SSH2_MSG_USERAUTH_FAILURE:
1533
+ return false;
1534
+ }
1535
+
1536
+ return false;
1537
+ }
1538
+
1539
+ /**
1540
+ * Login with an RSA private key
1541
+ *
1542
+ * @param String $username
1543
+ * @param Crypt_RSA $password
1544
+ * @return Boolean
1545
+ * @access private
1546
+ * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
1547
+ * by sending dummy SSH_MSG_IGNORE messages.
1548
+ */
1549
+ function _privatekey_login($username, $privatekey)
1550
+ {
1551
+ // see http://tools.ietf.org/html/rfc4253#page-15
1552
+ $publickey = $privatekey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_RAW);
1553
+ if ($publickey === false) {
1554
+ return false;
1555
+ }
1556
+
1557
+ $publickey = array(
1558
+ 'e' => $publickey['e']->toBytes(true),
1559
+ 'n' => $publickey['n']->toBytes(true)
1560
+ );
1561
+ $publickey = pack('Na*Na*Na*',
1562
+ strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey['e']), $publickey['e'], strlen($publickey['n']), $publickey['n']
1563
+ );
1564
+
1565
+ $part1 = pack('CNa*Na*Na*',
1566
+ NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
1567
+ strlen('publickey'), 'publickey'
1568
+ );
1569
+ $part2 = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey), $publickey);
1570
+
1571
+ $packet = $part1 . chr(0) . $part2;
1572
+ if (!$this->_send_binary_packet($packet)) {
1573
+ return false;
1574
+ }
1575
+
1576
+ $response = $this->_get_binary_packet();
1577
+ if ($response === false) {
1578
+ user_error('Connection closed by server', E_USER_NOTICE);
1579
+ return false;
1580
+ }
1581
+
1582
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
1583
+
1584
+ switch ($type) {
1585
+ case NET_SSH2_MSG_USERAUTH_FAILURE:
1586
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1587
+ $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length);
1588
+ return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
1589
+ case NET_SSH2_MSG_USERAUTH_PK_OK:
1590
+ // we'll just take it on faith that the public key blob and the public key algorithm name are as
1591
+ // they should be
1592
+ if (defined('NET_SSH2_LOGGING')) {
1593
+ $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
1594
+ 'UNKNOWN',
1595
+ 'NET_SSH2_MSG_USERAUTH_PK_OK',
1596
+ $this->message_number_log[count($this->message_number_log) - 1]
1597
+ );
1598
+ }
1599
+ }
1600
+
1601
+ $packet = $part1 . chr(1) . $part2;
1602
+ $privatekey->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
1603
+ $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet));
1604
+ $signature = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($signature), $signature);
1605
+ $packet.= pack('Na*', strlen($signature), $signature);
1606
+
1607
+ if (!$this->_send_binary_packet($packet)) {
1608
+ return false;
1609
+ }
1610
+
1611
+ $response = $this->_get_binary_packet();
1612
+ if ($response === false) {
1613
+ user_error('Connection closed by server', E_USER_NOTICE);
1614
+ return false;
1615
+ }
1616
+
1617
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
1618
+
1619
+ switch ($type) {
1620
+ case NET_SSH2_MSG_USERAUTH_FAILURE:
1621
+ // either the login is bad or the server employees multi-factor authentication
1622
+ return false;
1623
+ case NET_SSH2_MSG_USERAUTH_SUCCESS:
1624
+ $this->bitmap |= NET_SSH2_MASK_LOGIN;
1625
+ return true;
1626
+ }
1627
+
1628
+ return false;
1629
+ }
1630
+
1631
+ /**
1632
+ * Set Timeout
1633
+ *
1634
+ * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout.
1635
+ * Setting $timeout to false or 0 will mean there is no timeout.
1636
+ *
1637
+ * @param Mixed $timeout
1638
+ */
1639
+ function setTimeout($timeout)
1640
+ {
1641
+ $this->timeout = $this->curTimeout = $timeout;
1642
+ }
1643
+
1644
+ /**
1645
+ * Execute Command
1646
+ *
1647
+ * If $block is set to false then Net_SSH2::_get_channel_packet(NET_SSH2_CHANNEL_EXEC) will need to be called manually.
1648
+ * In all likelihood, this is not a feature you want to be taking advantage of.
1649
+ *
1650
+ * @param String $command
1651
+ * @param optional Boolean $block
1652
+ * @return String
1653
+ * @access public
1654
+ */
1655
+ function exec($command, $block = true)
1656
+ {
1657
+ $this->curTimeout = $this->timeout;
1658
+
1659
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1660
+ return false;
1661
+ }
1662
+
1663
+ // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
1664
+ // be adjusted". 0x7FFFFFFF is, at 4GB, the max size. technically, it should probably be decremented, but,
1665
+ // honestly, if you're transfering more than 4GB, you probably shouldn't be using phpseclib, anyway.
1666
+ // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
1667
+ $this->window_size_client_to_server[NET_SSH2_CHANNEL_EXEC] = 0x7FFFFFFF;
1668
+ // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
1669
+ // uses 0x4000, that's what will be used here, as well.
1670
+ $packet_size = 0x4000;
1671
+
1672
+ $packet = pack('CNa*N3',
1673
+ NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_EXEC, $this->window_size_client_to_server[NET_SSH2_CHANNEL_EXEC], $packet_size);
1674
+
1675
+ if (!$this->_send_binary_packet($packet)) {
1676
+ return false;
1677
+ }
1678
+
1679
+ $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;
1680
+
1681
+ $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
1682
+ if ($response === false) {
1683
+ return false;
1684
+ }
1685
+
1686
+ // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things
1687
+ // down. the one place where it might be desirable is if you're doing something like Net_SSH2::exec('ping localhost &').
1688
+ // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then
1689
+ // then immediately terminate. without such a request exec() will loop indefinitely. the ping process won't end but
1690
+ // neither will your script.
1691
+
1692
+ // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
1693
+ // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
1694
+ // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates.
1695
+ $packet = pack('CNNa*CNa*',
1696
+ NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_EXEC], strlen('exec'), 'exec', 1, strlen($command), $command);
1697
+ if (!$this->_send_binary_packet($packet)) {
1698
+ return false;
1699
+ }
1700
+
1701
+ $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
1702
+
1703
+ $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
1704
+ if ($response === false) {
1705
+ return false;
1706
+ }
1707
+
1708
+ $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;
1709
+
1710
+ if (!$block) {
1711
+ return true;
1712
+ }
1713
+
1714
+ $output = '';
1715
+ while (true) {
1716
+ $temp = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
1717
+ switch (true) {
1718
+ case $temp === true:
1719
+ return $output;
1720
+ case $temp === false:
1721
+ return false;
1722
+ default:
1723
+ $output.= $temp;
1724
+ }
1725
+ }
1726
+ }
1727
+
1728
+ /**
1729
+ * Creates an interactive shell
1730
+ *
1731
+ * @see Net_SSH2::read()
1732
+ * @see Net_SSH2::write()
1733
+ * @return Boolean
1734
+ * @access private
1735
+ */
1736
+ function _initShell()
1737
+ {
1738
+ $this->window_size_client_to_server[NET_SSH2_CHANNEL_SHELL] = 0x7FFFFFFF;
1739
+ $packet_size = 0x4000;
1740
+
1741
+ $packet = pack('CNa*N3',
1742
+ NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_SHELL, $this->window_size_client_to_server[NET_SSH2_CHANNEL_SHELL], $packet_size);
1743
+
1744
+ if (!$this->_send_binary_packet($packet)) {
1745
+ return false;
1746
+ }
1747
+
1748
+ $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;
1749
+
1750
+ $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
1751
+ if ($response === false) {
1752
+ return false;
1753
+ }
1754
+
1755
+ $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
1756
+ $packet = pack('CNNa*CNa*N5a*',
1757
+ NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SHELL], strlen('pty-req'), 'pty-req', 1, strlen('vt100'), 'vt100',
1758
+ 80, 24, 0, 0, strlen($terminal_modes), $terminal_modes);
1759
+
1760
+ if (!$this->_send_binary_packet($packet)) {
1761
+ return false;
1762
+ }
1763
+
1764
+ $response = $this->_get_binary_packet();
1765
+ if ($response === false) {
1766
+ user_error('Connection closed by server', E_USER_NOTICE);
1767
+ return false;
1768
+ }
1769
+
1770
+ list(, $type) = unpack('C', $this->_string_shift($response, 1));
1771
+
1772
+ switch ($type) {
1773
+ case NET_SSH2_MSG_CHANNEL_SUCCESS:
1774
+ break;
1775
+ case NET_SSH2_MSG_CHANNEL_FAILURE:
1776
+ default:
1777
+ user_error('Unable to request pseudo-terminal', E_USER_NOTICE);
1778
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
1779
+ }
1780
+
1781
+ $packet = pack('CNNa*C',
1782
+ NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SHELL], strlen('shell'), 'shell', 1);
1783
+ if (!$this->_send_binary_packet($packet)) {
1784
+ return false;
1785
+ }
1786
+
1787
+ $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
1788
+
1789
+ $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
1790
+ if ($response === false) {
1791
+ return false;
1792
+ }
1793
+
1794
+ $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;
1795
+
1796
+ $this->bitmap |= NET_SSH2_MASK_SHELL;
1797
+
1798
+ return true;
1799
+ }
1800
+
1801
+ /**
1802
+ * Returns the output of an interactive shell
1803
+ *
1804
+ * Returns when there's a match for $expect, which can take the form of a string literal or,
1805
+ * if $mode == NET_SSH2_READ_REGEX, a regular expression.
1806
+ *
1807
+ * @see Net_SSH2::read()
1808
+ * @param String $expect
1809
+ * @param Integer $mode
1810
+ * @return String
1811
+ * @access public
1812
+ */
1813
+ function read($expect = '', $mode = NET_SSH2_READ_SIMPLE)
1814
+ {
1815
+ $this->curTimeout = $this->timeout;
1816
+
1817
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1818
+ user_error('Operation disallowed prior to login()', E_USER_NOTICE);
1819
+ return false;
1820
+ }
1821
+
1822
+ if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
1823
+ user_error('Unable to initiate an interactive shell session', E_USER_NOTICE);
1824
+ return false;
1825
+ }
1826
+
1827
+ $match = $expect;
1828
+ while (true) {
1829
+ if ($mode == NET_SSH2_READ_REGEX) {
1830
+ preg_match($expect, $this->interactiveBuffer, $matches);
1831
+ $match = $matches[0];
1832
+ }
1833
+ $pos = !empty($match) ? strpos($this->interactiveBuffer, $match) : false;
1834
+ if ($pos !== false) {
1835
+ return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
1836
+ }
1837
+ $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
1838
+ if (is_bool($response)) {
1839
+ return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false;
1840
+ }
1841
+
1842
+ $this->interactiveBuffer.= $response;
1843
+ }
1844
+ }
1845
+
1846
+ /**
1847
+ * Inputs a command into an interactive shell.
1848
+ *
1849
+ * @see Net_SSH1::interactiveWrite()
1850
+ * @param String $cmd
1851
+ * @return Boolean
1852
+ * @access public
1853
+ */
1854
+ function write($cmd)
1855
+ {
1856
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1857
+ user_error('Operation disallowed prior to login()', E_USER_NOTICE);
1858
+ return false;
1859
+ }
1860
+
1861
+ if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
1862
+ user_error('Unable to initiate an interactive shell session', E_USER_NOTICE);
1863
+ return false;
1864
+ }
1865
+
1866
+ return $this->_send_channel_packet(NET_SSH2_CHANNEL_SHELL, $cmd);
1867
+ }
1868
+
1869
+ /**
1870
+ * Disconnect
1871
+ *
1872
+ * @access public
1873
+ */
1874
+ function disconnect()
1875
+ {
1876
+ $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
1877
+ }
1878
+
1879
+ /**
1880
+ * Destructor.
1881
+ *
1882
+ * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
1883
+ * disconnect().
1884
+ *
1885
+ * @access public
1886
+ */
1887
+ function __destruct()
1888
+ {
1889
+ $this->disconnect();
1890
+ }
1891
+
1892
+ /**
1893
+ * Gets Binary Packets
1894
+ *
1895
+ * See '6. Binary Packet Protocol' of rfc4253 for more info.
1896
+ *
1897
+ * @see Net_SSH2::_send_binary_packet()
1898
+ * @return String
1899
+ * @access private
1900
+ */
1901
+ function _get_binary_packet()
1902
+ {
1903
+ if (!is_resource($this->fsock) || feof($this->fsock)) {
1904
+ user_error('Connection closed prematurely', E_USER_NOTICE);
1905
+ return false;
1906
+ }
1907
+
1908
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
1909
+ $raw = fread($this->fsock, $this->decrypt_block_size);
1910
+ $stop = strtok(microtime(), ' ') + strtok('');
1911
+
1912
+ if (empty($raw)) {
1913
+ return '';
1914
+ }
1915
+
1916
+ if ($this->decrypt !== false) {
1917
+ $raw = $this->decrypt->decrypt($raw);
1918
+ }
1919
+ if ($raw === false) {
1920
+ user_error('Unable to decrypt content', E_USER_NOTICE);
1921
+ return false;
1922
+ }
1923
+
1924
+ extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5)));
1925
+
1926
+ $remaining_length = $packet_length + 4 - $this->decrypt_block_size;
1927
+ $buffer = '';
1928
+ while ($remaining_length > 0) {
1929
+ $temp = fread($this->fsock, $remaining_length);
1930
+ $buffer.= $temp;
1931
+ $remaining_length-= strlen($temp);
1932
+ }
1933
+ if (!empty($buffer)) {
1934
+ $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer;
1935
+ $buffer = $temp = '';
1936
+ }
1937
+
1938
+ $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1);
1939
+ $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty
1940
+
1941
+ if ($this->hmac_check !== false) {
1942
+ $hmac = fread($this->fsock, $this->hmac_size);
1943
+ if ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) {
1944
+ user_error('Invalid HMAC', E_USER_NOTICE);
1945
+ return false;
1946
+ }
1947
+ }
1948
+
1949
+ //if ($this->decompress) {
1950
+ // $payload = gzinflate(substr($payload, 2));
1951
+ //}
1952
+
1953
+ $this->get_seq_no++;
1954
+
1955
+ if (defined('NET_SSH2_LOGGING')) {
1956
+ $temp = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';
1957
+ $this->message_number_log[] = '<- ' . $temp .
1958
+ ' (' . round($stop - $start, 4) . 's)';
1959
+ if (NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
1960
+ $this->_append_log($payload);
1961
+ }
1962
+ }
1963
+
1964
+ return $this->_filter($payload);
1965
+ }
1966
+
1967
+ /**
1968
+ * Filter Binary Packets
1969
+ *
1970
+ * Because some binary packets need to be ignored...
1971
+ *
1972
+ * @see Net_SSH2::_get_binary_packet()
1973
+ * @return String
1974
+ * @access private
1975
+ */
1976
+ function _filter($payload)
1977
+ {
1978
+ switch (ord($payload[0])) {
1979
+ case NET_SSH2_MSG_DISCONNECT:
1980
+ $this->_string_shift($payload, 1);
1981
+ extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8)));
1982
+ $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length));
1983
+ $this->bitmask = 0;
1984
+ return false;
1985
+ case NET_SSH2_MSG_IGNORE:
1986
+ $payload = $this->_get_binary_packet();
1987
+ break;
1988
+ case NET_SSH2_MSG_DEBUG:
1989
+ $this->_string_shift($payload, 2);
1990
+ extract(unpack('Nlength', $this->_string_shift($payload, 4)));
1991
+ $this->errors[] = 'SSH_MSG_DEBUG: ' . utf8_decode($this->_string_shift($payload, $length));
1992
+ $payload = $this->_get_binary_packet();
1993
+ break;
1994
+ case NET_SSH2_MSG_UNIMPLEMENTED:
1995
+ return false;
1996
+ case NET_SSH2_MSG_KEXINIT:
1997
+ if ($this->session_id !== false) {
1998
+ if (!$this->_key_exchange($payload)) {
1999
+ $this->bitmask = 0;
2000
+ return false;
2001
+ }
2002
+ $payload = $this->_get_binary_packet();
2003
+ }
2004
+ }
2005
+
2006
+ // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in
2007
+ if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && !($this->bitmap & NET_SSH2_MASK_LOGIN) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
2008
+ $this->_string_shift($payload, 1);
2009
+ extract(unpack('Nlength', $this->_string_shift($payload, 4)));
2010
+ $this->errors[] = 'SSH_MSG_USERAUTH_BANNER: ' . utf8_decode($this->_string_shift($payload, $length));
2011
+ $payload = $this->_get_binary_packet();
2012
+ }
2013
+
2014
+ // only called when we've already logged in
2015
+ if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && ($this->bitmap & NET_SSH2_MASK_LOGIN)) {
2016
+ switch (ord($payload[0])) {
2017
+ case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
2018
+ $this->_string_shift($payload, 1);
2019
+ extract(unpack('Nlength', $this->_string_shift($payload)));
2020
+ $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . utf8_decode($this->_string_shift($payload, $length));
2021
+
2022
+ if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) {
2023
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2024
+ }
2025
+
2026
+ $payload = $this->_get_binary_packet();
2027
+ break;
2028
+ case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
2029
+ $this->_string_shift($payload, 1);
2030
+ extract(unpack('N', $this->_string_shift($payload, 4)));
2031
+ $this->errors[] = 'SSH_MSG_CHANNEL_OPEN: ' . utf8_decode($this->_string_shift($payload, $length));
2032
+
2033
+ $this->_string_shift($payload, 4); // skip over client channel
2034
+ extract(unpack('Nserver_channel', $this->_string_shift($payload, 4)));
2035
+
2036
+ $packet = pack('CN3a*Na*',
2037
+ NET_SSH2_MSG_REQUEST_FAILURE, $server_channel, NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, 0, '', 0, '');
2038
+
2039
+ if (!$this->_send_binary_packet($packet)) {
2040
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2041
+ }
2042
+
2043
+ $payload = $this->_get_binary_packet();
2044
+ break;
2045
+ case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
2046
+ $payload = $this->_get_binary_packet();
2047
+ }
2048
+ }
2049
+
2050
+ return $payload;
2051
+ }
2052
+
2053
+ /**
2054
+ * Gets channel data
2055
+ *
2056
+ * Returns the data as a string if it's available and false if not.
2057
+ *
2058
+ * @param $client_channel
2059
+ * @return Mixed
2060
+ * @access private
2061
+ */
2062
+ function _get_channel_packet($client_channel, $skip_extended = false)
2063
+ {
2064
+ if (!empty($this->channel_buffers[$client_channel])) {
2065
+ return array_shift($this->channel_buffers[$client_channel]);
2066
+ }
2067
+
2068
+ while (true) {
2069
+ if ($this->curTimeout) {
2070
+ $read = array($this->fsock);
2071
+ $write = $except = NULL;
2072
+
2073
+ stream_set_blocking($this->fsock, false);
2074
+
2075
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
2076
+ // on windows this returns a "Warning: Invalid CRT parameters detected" error
2077
+ if (!@stream_select($read, $write, $except, $this->curTimeout)) {
2078
+ stream_set_blocking($this->fsock, true);
2079
+ $this->_close_channel($client_channel);
2080
+ return true;
2081
+ }
2082
+ $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
2083
+ $this->curTimeout-= $elapsed;
2084
+
2085
+ stream_set_blocking($this->fsock, true);
2086
+ }
2087
+
2088
+ $response = $this->_get_binary_packet();
2089
+ if ($response === false) {
2090
+ user_error('Connection closed by server', E_USER_NOTICE);
2091
+ return false;
2092
+ }
2093
+
2094
+ if (empty($response)) {
2095
+ return '';
2096
+ }
2097
+
2098
+ extract(unpack('Ctype/Nchannel', $this->_string_shift($response, 5)));
2099
+
2100
+ switch ($this->channel_status[$channel]) {
2101
+ case NET_SSH2_MSG_CHANNEL_OPEN:
2102
+ switch ($type) {
2103
+ case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
2104
+ extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
2105
+ $this->server_channels[$channel] = $server_channel;
2106
+ $this->_string_shift($response, 4); // skip over (server) window size
2107
+ $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4));
2108
+ $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server'];
2109
+ return $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended);
2110
+ //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
2111
+ default:
2112
+ user_error('Unable to open channel', E_USER_NOTICE);
2113
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2114
+ }
2115
+ break;
2116
+ case NET_SSH2_MSG_CHANNEL_REQUEST:
2117
+ switch ($type) {
2118
+ case NET_SSH2_MSG_CHANNEL_SUCCESS:
2119
+ return true;
2120
+ //case NET_SSH2_MSG_CHANNEL_FAILURE:
2121
+ default:
2122
+ user_error('Unable to request pseudo-terminal', E_USER_NOTICE);
2123
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2124
+ }
2125
+ case NET_SSH2_MSG_CHANNEL_CLOSE:
2126
+ return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended);
2127
+ }
2128
+
2129
+ switch ($type) {
2130
+ case NET_SSH2_MSG_CHANNEL_DATA:
2131
+ /*
2132
+ if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
2133
+ // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server
2134
+ // this actually seems to make things twice as fast. more to the point, the message right after
2135
+ // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
2136
+ // in OpenSSH it slows things down but only by a couple thousandths of a second.
2137
+ $this->_send_channel_packet($client_channel, chr(0));
2138
+ }
2139
+ */
2140
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
2141
+ $data = $this->_string_shift($response, $length);
2142
+ if ($client_channel == $channel) {
2143
+ return $data;
2144
+ }
2145
+ if (!isset($this->channel_buffers[$client_channel])) {
2146
+ $this->channel_buffers[$client_channel] = array();
2147
+ }
2148
+ $this->channel_buffers[$client_channel][] = $data;
2149
+ break;
2150
+ case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
2151
+ if ($skip_extended) {
2152
+ break;
2153
+ }
2154
+ /*
2155
+ if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
2156
+ $this->_send_channel_packet($client_channel, chr(0));
2157
+ }
2158
+ */
2159
+ // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
2160
+ extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
2161
+ $data = $this->_string_shift($response, $length);
2162
+ if ($client_channel == $channel) {
2163
+ return $data;
2164
+ }
2165
+ if (!isset($this->channel_buffers[$client_channel])) {
2166
+ $this->channel_buffers[$client_channel] = array();
2167
+ }
2168
+ $this->channel_buffers[$client_channel][] = $data;
2169
+ break;
2170
+ case NET_SSH2_MSG_CHANNEL_REQUEST:
2171
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
2172
+ $value = $this->_string_shift($response, $length);
2173
+ switch ($value) {
2174
+ case 'exit-signal':
2175
+ $this->_string_shift($response, 1);
2176
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
2177
+ $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length);
2178
+ $this->_string_shift($response, 1);
2179
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
2180
+ if ($length) {
2181
+ $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length);
2182
+ }
2183
+ case 'exit-status':
2184
+ // "The channel needs to be closed with SSH_MSG_CHANNEL_CLOSE after this message."
2185
+ // -- http://tools.ietf.org/html/rfc4254#section-6.10
2186
+ $this->_close_channel($client_channel);
2187
+ return true;
2188
+ default:
2189
+ // "Some systems may not implement signals, in which case they SHOULD ignore this message."
2190
+ // -- http://tools.ietf.org/html/rfc4254#section-6.9
2191
+ break;
2192
+ }
2193
+ break;
2194
+ case NET_SSH2_MSG_CHANNEL_CLOSE:
2195
+ $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
2196
+ return true;
2197
+ case NET_SSH2_MSG_CHANNEL_EOF:
2198
+ break;
2199
+ default:
2200
+ user_error('Error reading channel data', E_USER_NOTICE);
2201
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2202
+ }
2203
+ }
2204
+ }
2205
+
2206
+ /**
2207
+ * Sends Binary Packets
2208
+ *
2209
+ * See '6. Binary Packet Protocol' of rfc4253 for more info.
2210
+ *
2211
+ * @param String $data
2212
+ * @see Net_SSH2::_get_binary_packet()
2213
+ * @return Boolean
2214
+ * @access private
2215
+ */
2216
+ function _send_binary_packet($data)
2217
+ {
2218
+ if (!is_resource($this->fsock) || feof($this->fsock)) {
2219
+ user_error('Connection closed prematurely', E_USER_NOTICE);
2220
+ return false;
2221
+ }
2222
+
2223
+ //if ($this->compress) {
2224
+ // // the -4 removes the checksum:
2225
+ // // http://php.net/function.gzcompress#57710
2226
+ // $data = substr(gzcompress($data), 0, -4);
2227
+ //}
2228
+
2229
+ // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
2230
+ $packet_length = strlen($data) + 9;
2231
+ // round up to the nearest $this->encrypt_block_size
2232
+ $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;
2233
+ // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length
2234
+ $padding_length = $packet_length - strlen($data) - 5;
2235
+
2236
+ $padding = '';
2237
+ for ($i = 0; $i < $padding_length; $i++) {
2238
+ $padding.= chr(crypt_random(0, 255));
2239
+ }
2240
+
2241
+ // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself
2242
+ $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding);
2243
+
2244
+ $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : '';
2245
+ $this->send_seq_no++;
2246
+
2247
+ if ($this->encrypt !== false) {
2248
+ $packet = $this->encrypt->encrypt($packet);
2249
+ }
2250
+
2251
+ $packet.= $hmac;
2252
+
2253
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
2254
+ $result = strlen($packet) == fputs($this->fsock, $packet);
2255
+ $stop = strtok(microtime(), ' ') + strtok('');
2256
+
2257
+ if (defined('NET_SSH2_LOGGING')) {
2258
+ $temp = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')';
2259
+ $this->message_number_log[] = '-> ' . $temp .
2260
+ ' (' . round($stop - $start, 4) . 's)';
2261
+ if (NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
2262
+ $this->_append_log($data);
2263
+ }
2264
+ }
2265
+
2266
+ return $result;
2267
+ }
2268
+
2269
+ /**
2270
+ * Logs data packets
2271
+ *
2272
+ * Makes sure that only the last 1MB worth of packets will be logged
2273
+ *
2274
+ * @param String $data
2275
+ * @access private
2276
+ */
2277
+ function _append_log($data)
2278
+ {
2279
+ $this->_string_shift($data);
2280
+ $this->log_size+= strlen($data);
2281
+ $this->message_log[] = $data;
2282
+ while ($this->log_size > NET_SSH2_LOG_MAX_SIZE) {
2283
+ $this->log_size-= strlen(array_shift($this->message_log));
2284
+ array_shift($this->message_number_log);
2285
+ }
2286
+ }
2287
+
2288
+ /**
2289
+ * Sends channel data
2290
+ *
2291
+ * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
2292
+ *
2293
+ * @param Integer $client_channel
2294
+ * @param String $data
2295
+ * @return Boolean
2296
+ * @access private
2297
+ */
2298
+ function _send_channel_packet($client_channel, $data)
2299
+ {
2300
+ while (strlen($data) > $this->packet_size_client_to_server[$client_channel]) {
2301
+ // resize the window, if appropriate
2302
+ $this->window_size_client_to_server[$client_channel]-= $this->packet_size_client_to_server[$client_channel];
2303
+ if ($this->window_size_client_to_server[$client_channel] < 0) {
2304
+ $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$client_channel], $this->window_size);
2305
+ if (!$this->_send_binary_packet($packet)) {
2306
+ return false;
2307
+ }
2308
+ $this->window_size_client_to_server[$client_channel]+= $this->window_size;
2309
+ }
2310
+
2311
+ $packet = pack('CN2a*',
2312
+ NET_SSH2_MSG_CHANNEL_DATA,
2313
+ $this->server_channels[$client_channel],
2314
+ $this->packet_size_client_to_server[$client_channel],
2315
+ $this->_string_shift($data, $this->packet_size_client_to_server[$client_channel])
2316
+ );
2317
+
2318
+ if (!$this->_send_binary_packet($packet)) {
2319
+ return false;
2320
+ }
2321
+ }
2322
+
2323
+ // resize the window, if appropriate
2324
+ $this->window_size_client_to_server[$client_channel]-= strlen($data);
2325
+ if ($this->window_size_client_to_server[$client_channel] < 0) {
2326
+ $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$client_channel], $this->window_size);
2327
+ if (!$this->_send_binary_packet($packet)) {
2328
+ return false;
2329
+ }
2330
+ $this->window_size_client_to_server[$client_channel]+= $this->window_size;
2331
+ }
2332
+
2333
+ return $this->_send_binary_packet(pack('CN2a*',
2334
+ NET_SSH2_MSG_CHANNEL_DATA,
2335
+ $this->server_channels[$client_channel],
2336
+ strlen($data),
2337
+ $data));
2338
+ }
2339
+
2340
+ /**
2341
+ * Closes and flushes a channel
2342
+ *
2343
+ * Net_SSH2 doesn't properly close most channels. For exec() channels are normally closed by the server
2344
+ * and for SFTP channels are presumably closed when the client disconnects. This functions is intended
2345
+ * for SCP more than anything.
2346
+ *
2347
+ * @param Integer $client_channel
2348
+ * @return Boolean
2349
+ * @access private
2350
+ */
2351
+ function _close_channel($client_channel)
2352
+ {
2353
+ // see http://tools.ietf.org/html/rfc4254#section-5.3
2354
+
2355
+ $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
2356
+
2357
+ $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
2358
+
2359
+ $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
2360
+
2361
+ $this->curTimeout = 0;
2362
+
2363
+ while (!is_bool($this->_get_channel_packet($client_channel)));
2364
+
2365
+ if ($this->bitmap & NET_SSH2_MASK_SHELL) {
2366
+ $this->bitmap&= ~NET_SSH2_MASK_SHELL;
2367
+ }
2368
+ }
2369
+
2370
+ /**
2371
+ * Disconnect
2372
+ *
2373
+ * @param Integer $reason
2374
+ * @return Boolean
2375
+ * @access private
2376
+ */
2377
+ function _disconnect($reason)
2378
+ {
2379
+ if ($this->bitmap) {
2380
+ $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, '');
2381
+ $this->_send_binary_packet($data);
2382
+ $this->bitmap = 0;
2383
+ fclose($this->fsock);
2384
+ return false;
2385
+ }
2386
+ }
2387
+
2388
+ /**
2389
+ * String Shift
2390
+ *
2391
+ * Inspired by array_shift
2392
+ *
2393
+ * @param String $string
2394
+ * @param optional Integer $index
2395
+ * @return String
2396
+ * @access private
2397
+ */
2398
+ function _string_shift(&$string, $index = 1)
2399
+ {
2400
+ $substr = substr($string, 0, $index);
2401
+ $string = substr($string, $index);
2402
+ return $substr;
2403
+ }
2404
+
2405
+ /**
2406
+ * Define Array
2407
+ *
2408
+ * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
2409
+ * named constants from it, using the value as the name of the constant and the index as the value of the constant.
2410
+ * If any of the constants that would be defined already exists, none of the constants will be defined.
2411
+ *
2412
+ * @param Array $array
2413
+ * @access private
2414
+ */
2415
+ function _define_array()
2416
+ {
2417
+ $args = func_get_args();
2418
+ foreach ($args as $arg) {
2419
+ foreach ($arg as $key=>$value) {
2420
+ if (!defined($value)) {
2421
+ define($value, $key);
2422
+ } else {
2423
+ break 2;
2424
+ }
2425
+ }
2426
+ }
2427
+ }
2428
+
2429
+ /**
2430
+ * Returns a log of the packets that have been sent and received.
2431
+ *
2432
+ * Returns a string if NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX, an array if NET_SSH2_LOGGING == NET_SSH2_LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING')
2433
+ *
2434
+ * @access public
2435
+ * @return String or Array
2436
+ */
2437
+ function getLog()
2438
+ {
2439
+ if (!defined('NET_SSH2_LOGGING')) {
2440
+ return false;
2441
+ }
2442
+
2443
+ switch (NET_SSH2_LOGGING) {
2444
+ case NET_SSH2_LOG_SIMPLE:
2445
+ return $this->message_number_log;
2446
+ break;
2447
+ case NET_SSH2_LOG_COMPLEX:
2448
+ return $this->_format_log($this->message_log, $this->message_number_log);
2449
+ break;
2450
+ default:
2451
+ return false;
2452
+ }
2453
+ }
2454
+
2455
+ /**
2456
+ * Formats a log for printing
2457
+ *
2458
+ * @param Array $message_log
2459
+ * @param Array $message_number_log
2460
+ * @access private
2461
+ * @return String
2462
+ */
2463
+ function _format_log($message_log, $message_number_log)
2464
+ {
2465
+ static $boundary = ':', $long_width = 65, $short_width = 16;
2466
+
2467
+ $output = '';
2468
+ for ($i = 0; $i < count($message_log); $i++) {
2469
+ $output.= $message_number_log[$i] . "\r\n";
2470
+ $current_log = $message_log[$i];
2471
+ $j = 0;
2472
+ do {
2473
+ if (!empty($current_log)) {
2474
+ $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 ';
2475
+ }
2476
+ $fragment = $this->_string_shift($current_log, $short_width);
2477
+ $hex = substr(
2478
+ preg_replace(
2479
+ '#(.)#es',
2480
+ '"' . $boundary . '" . str_pad(dechex(ord(substr("\\1", -1))), 2, "0", STR_PAD_LEFT)',
2481
+ $fragment),
2482
+ strlen($boundary)
2483
+ );
2484
+ // replace non ASCII printable characters with dots
2485
+ // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
2486
+ // also replace < with a . since < messes up the output on web browsers
2487
+ $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
2488
+ $output.= str_pad($hex, $long_width - $short_width, ' ') . $raw . "\r\n";
2489
+ $j++;
2490
+ } while (!empty($current_log));
2491
+ $output.= "\r\n";
2492
+ }
2493
+
2494
+ return $output;
2495
+ }
2496
+
2497
+ /**
2498
+ * Returns all errors
2499
+ *
2500
+ * @return String
2501
+ * @access public
2502
+ */
2503
+ function getErrors()
2504
+ {
2505
+ return $this->errors;
2506
+ }
2507
+
2508
+ /**
2509
+ * Returns the last error
2510
+ *
2511
+ * @return String
2512
+ * @access public
2513
+ */
2514
+ function getLastError()
2515
+ {
2516
+ return $this->errors[count($this->errors) - 1];
2517
+ }
2518
+
2519
+ /**
2520
+ * Return the server identification.
2521
+ *
2522
+ * @return String
2523
+ * @access public
2524
+ */
2525
+ function getServerIdentification()
2526
+ {
2527
+ return $this->server_identifier;
2528
+ }
2529
+
2530
+ /**
2531
+ * Return a list of the key exchange algorithms the server supports.
2532
+ *
2533
+ * @return Array
2534
+ * @access public
2535
+ */
2536
+ function getKexAlgorithms()
2537
+ {
2538
+ return $this->kex_algorithms;
2539
+ }
2540
+
2541
+ /**
2542
+ * Return a list of the host key (public key) algorithms the server supports.
2543
+ *
2544
+ * @return Array
2545
+ * @access public
2546
+ */
2547
+ function getServerHostKeyAlgorithms()
2548
+ {
2549
+ return $this->server_host_key_algorithms;
2550
+ }
2551
+
2552
+ /**
2553
+ * Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client.
2554
+ *
2555
+ * @return Array
2556
+ * @access public
2557
+ */
2558
+ function getEncryptionAlgorithmsClient2Server()
2559
+ {
2560
+ return $this->encryption_algorithms_client_to_server;
2561
+ }
2562
+
2563
+ /**
2564
+ * Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client.
2565
+ *
2566
+ * @return Array
2567
+ * @access public
2568
+ */
2569
+ function getEncryptionAlgorithmsServer2Client()
2570
+ {
2571
+ return $this->encryption_algorithms_server_to_client;
2572
+ }
2573
+
2574
+ /**
2575
+ * Return a list of the MAC algorithms the server supports, when receiving stuff from the client.
2576
+ *
2577
+ * @return Array
2578
+ * @access public
2579
+ */
2580
+ function getMACAlgorithmsClient2Server()
2581
+ {
2582
+ return $this->mac_algorithms_client_to_server;
2583
+ }
2584
+
2585
+ /**
2586
+ * Return a list of the MAC algorithms the server supports, when sending stuff to the client.
2587
+ *
2588
+ * @return Array
2589
+ * @access public
2590
+ */
2591
+ function getMACAlgorithmsServer2Client()
2592
+ {
2593
+ return $this->mac_algorithms_server_to_client;
2594
+ }
2595
+
2596
+ /**
2597
+ * Return a list of the compression algorithms the server supports, when receiving stuff from the client.
2598
+ *
2599
+ * @return Array
2600
+ * @access public
2601
+ */
2602
+ function getCompressionAlgorithmsClient2Server()
2603
+ {
2604
+ return $this->compression_algorithms_client_to_server;
2605
+ }
2606
+
2607
+ /**
2608
+ * Return a list of the compression algorithms the server supports, when sending stuff to the client.
2609
+ *
2610
+ * @return Array
2611
+ * @access public
2612
+ */
2613
+ function getCompressionAlgorithmsServer2Client()
2614
+ {
2615
+ return $this->compression_algorithms_server_to_client;
2616
+ }
2617
+
2618
+ /**
2619
+ * Return a list of the languages the server supports, when sending stuff to the client.
2620
+ *
2621
+ * @return Array
2622
+ * @access public
2623
+ */
2624
+ function getLanguagesServer2Client()
2625
+ {
2626
+ return $this->languages_server_to_client;
2627
+ }
2628
+
2629
+ /**
2630
+ * Return a list of the languages the server supports, when receiving stuff from the client.
2631
+ *
2632
+ * @return Array
2633
+ * @access public
2634
+ */
2635
+ function getLanguagesClient2Server()
2636
+ {
2637
+ return $this->languages_client_to_server;
2638
+ }
2639
+
2640
+ /**
2641
+ * Returns the server public host key.
2642
+ *
2643
+ * Caching this the first time you connect to a server and checking the result on subsequent connections
2644
+ * is recommended. Returns false if the server signature is not signed correctly with the public host key.
2645
+ *
2646
+ * @return Mixed
2647
+ * @access public
2648
+ */
2649
+ function getServerPublicHostKey()
2650
+ {
2651
+ $signature = $this->signature;
2652
+ $server_public_host_key = $this->server_public_host_key;
2653
+
2654
+ extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4)));
2655
+ $this->_string_shift($server_public_host_key, $length);
2656
+
2657
+ switch ($this->signature_format) {
2658
+ case 'ssh-dss':
2659
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
2660
+ $p = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
2661
+
2662
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
2663
+ $q = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
2664
+
2665
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
2666
+ $g = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
2667
+
2668
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
2669
+ $y = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
2670
+
2671
+ /* The value for 'dss_signature_blob' is encoded as a string containing
2672
+ r, followed by s (which are 160-bit integers, without lengths or
2673
+ padding, unsigned, and in network byte order). */
2674
+ $temp = unpack('Nlength', $this->_string_shift($signature, 4));
2675
+ if ($temp['length'] != 40) {
2676
+ user_error('Invalid signature', E_USER_NOTICE);
2677
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
2678
+ }
2679
+
2680
+ $r = new Math_BigInteger($this->_string_shift($signature, 20), 256);
2681
+ $s = new Math_BigInteger($this->_string_shift($signature, 20), 256);
2682
+
2683
+ if ($r->compare($q) >= 0 || $s->compare($q) >= 0) {
2684
+ user_error('Invalid signature', E_USER_NOTICE);
2685
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
2686
+ }
2687
+
2688
+ $w = $s->modInverse($q);
2689
+
2690
+ $u1 = $w->multiply(new Math_BigInteger(sha1($this->exchange_hash), 16));
2691
+ list(, $u1) = $u1->divide($q);
2692
+
2693
+ $u2 = $w->multiply($r);
2694
+ list(, $u2) = $u2->divide($q);
2695
+
2696
+ $g = $g->modPow($u1, $p);
2697
+ $y = $y->modPow($u2, $p);
2698
+
2699
+ $v = $g->multiply($y);
2700
+ list(, $v) = $v->divide($p);
2701
+ list(, $v) = $v->divide($q);
2702
+
2703
+ if (!$v->equals($r)) {
2704
+ user_error('Bad server signature', E_USER_NOTICE);
2705
+ return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
2706
+ }
2707
+
2708
+ break;
2709
+ case 'ssh-rsa':
2710
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
2711
+ $e = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
2712
+
2713
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
2714
+ $n = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
2715
+ $nLength = $temp['length'];
2716
+
2717
+ /*
2718
+ $temp = unpack('Nlength', $this->_string_shift($signature, 4));
2719
+ $signature = $this->_string_shift($signature, $temp['length']);
2720
+
2721
+ if (!class_exists('Crypt_RSA')) {
2722
+ require_once('Crypt/RSA.php');
2723
+ }
2724
+
2725
+ $rsa = new Crypt_RSA();
2726
+ $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
2727
+ $rsa->loadKey(array('e' => $e, 'n' => $n), CRYPT_RSA_PUBLIC_FORMAT_RAW);
2728
+ if (!$rsa->verify($this->exchange_hash, $signature)) {
2729
+ user_error('Bad server signature', E_USER_NOTICE);
2730
+ return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
2731
+ }
2732
+ */
2733
+
2734
+ $temp = unpack('Nlength', $this->_string_shift($signature, 4));
2735
+ $s = new Math_BigInteger($this->_string_shift($signature, $temp['length']), 256);
2736
+
2737
+ // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the
2738
+ // following URL:
2739
+ // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
2740
+
2741
+ // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source.
2742
+
2743
+ if ($s->compare(new Math_BigInteger()) < 0 || $s->compare($n->subtract(new Math_BigInteger(1))) > 0) {
2744
+ user_error('Invalid signature', E_USER_NOTICE);
2745
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
2746
+ }
2747
+
2748
+ $s = $s->modPow($e, $n);
2749
+ $s = $s->toBytes();
2750
+
2751
+ $h = pack('N4H*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, sha1($this->exchange_hash));
2752
+ $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 3 - strlen($h)) . $h;
2753
+
2754
+ if ($s != $h) {
2755
+ user_error('Bad server signature', E_USER_NOTICE);
2756
+ return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
2757
+ }
2758
+ }
2759
+
2760
+ return $this->server_public_host_key;
2761
+ }
2762
+ }
readme.txt ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === SSH SFTP Updater Support ===
2
+ Contributors: TerraFrost
3
+ Donate link: http://sourceforge.net/donate/index.php?group_id=198487
4
+ Tags: ssh, sftp
5
+ Requires at least: 3.1
6
+ Tested up to: 3.3
7
+ Stable tag: 0.3
8
+
9
+ "SSH SFTP Updater Support" is the easiest way to keep your Wordpress installation up-to-date with SFTP.
10
+
11
+ == Description ==
12
+
13
+ Keeping your Wordpress install up-to-date and installing plugins in a hassle-free manner is not so easy if your server uses SFTP. "SSH SFTP Updater Support" for Wordpress uses phpseclib to remedy this deficiency.
14
+
15
+ == Installation ==
16
+
17
+ 1. Upload the files to the `/wp-content/plugins/ssh-sftp-updater-support` directory
18
+ 2. Activate the plugin through the 'Plugins' menu in WordPress
19
+
20
+ == Changelog ==
21
+
22
+ = 0.1 =
23
+ * Initial Release
24
+
25
+ = 0.2 =
26
+ * recursive deletes weren't working correctly (directories never got deleted - just files)
27
+ * use SFTP for recursive chmod instead of SSH / exec
28
+ * fix plugin for people using custom WP_CONTENT_DIR values (thanks, dd32!)
29
+ * plugin prevented non-SFTP install methods from being used
30
+ * make it so private keys can be uploaded in addition to being copy / pasted
31
+
32
+ = 0.3 =
33
+ * update phpseclib to latest SVN
34
+ * read file when FTP_PRIKEY is defined (thanks, lkraav!)
sftp.php ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: SSH SFTP Updater Support
4
+ Plugin URI: http://phpseclib.sourceforge.net/wordpress.htm
5
+ Description: Update your Wordpress blog / plugins via SFTP without libssh2
6
+ Version: 0.3
7
+ Author: TerraFrost
8
+ Author URI: http://phpseclib.sourceforge.net/
9
+ */
10
+
11
+ // see http://adambrown.info/p/wp_hooks/hook/<filter name>
12
+ add_filter('filesystem_method', 'phpseclib_filesystem_method', 10, 2); // since 2.6 - WordPress will ignore the ssh option if the php ssh extension is not loaded
13
+ add_filter('request_filesystem_credentials', 'phpseclib_request_filesystem_credentials', 10, 6); // since 2.5 - Alter some strings and don't ask for the public key
14
+ add_filter('fs_ftp_connection_types', 'phpseclib_fs_ftp_connection_types'); // since 2.9 - Add the SSH2 option to the connection options
15
+ add_filter('filesystem_method_file', 'phpseclib_filesystem_method_file', 10, 2); // since 2.6 - Direct WordPress to use our ssh2 class
16
+
17
+ function phpseclib_filesystem_method_file($path, $method) {
18
+ return $method == 'ssh2' ?
19
+ dirname(__FILE__) . '/class-wp-filesystem-ssh2.php' :
20
+ $path;
21
+ }
22
+
23
+ function phpseclib_filesystem_method($method, $args) {
24
+ return ( isset($args['connection_type']) && 'ssh' == $args['connection_type'] ) ? 'ssh2' : $method;
25
+ }
26
+
27
+ function phpseclib_fs_ftp_connection_types($types) {
28
+ $types['ssh'] = __('SSH2');
29
+ return $types;
30
+ }
31
+
32
+ // this has been pretty much copy / pasted from wp-admin/includes/file.php
33
+ function phpseclib_request_filesystem_credentials($value, $form_post, $type, $error, $context, $extra_fields) {
34
+ if ( empty($type) )
35
+ $type = get_filesystem_method(array(), $context);
36
+
37
+ if ( 'direct' == $type )
38
+ return true;
39
+
40
+ if ( is_null( $extra_fields ) )
41
+ $extra_fields = array( 'version', 'locale' );
42
+
43
+ $credentials = get_option('ftp_credentials', array( 'hostname' => '', 'username' => ''));
44
+
45
+ // If defined, set it to that, Else, If POST'd, set it to that, If not, Set it to whatever it previously was(saved details in option)
46
+ $credentials['hostname'] = defined('FTP_HOST') ? FTP_HOST : (!empty($_POST['hostname']) ? stripslashes($_POST['hostname']) : $credentials['hostname']);
47
+ $credentials['username'] = defined('FTP_USER') ? FTP_USER : (!empty($_POST['username']) ? stripslashes($_POST['username']) : $credentials['username']);
48
+ $credentials['password'] = defined('FTP_PASS') ? FTP_PASS : (!empty($_POST['password']) ? stripslashes($_POST['password']) : '');
49
+
50
+ // Check to see if we are setting the private key for ssh
51
+ if (defined('FTP_PRIKEY') && file_exists(FTP_PRIKEY)) {
52
+ $credentials['private_key'] = file_get_contents(FTP_PRIKEY);
53
+ } else {
54
+ $credentials['private_key'] = (!empty($_POST['private_key'])) ? stripslashes($_POST['private_key']) : '';
55
+ if (isset($_FILES['private_key_file']) && file_exists($_FILES['private_key_file']['tmp_name'])) {
56
+ $credentials['private_key'] = file_get_contents($_FILES['private_key_file']['tmp_name']);
57
+ }
58
+ }
59
+
60
+
61
+ //sanitize the hostname, Some people might pass in odd-data:
62
+ $credentials['hostname'] = preg_replace('|\w+://|', '', $credentials['hostname']); //Strip any schemes off
63
+
64
+ if ( strpos($credentials['hostname'], ':') ) {
65
+ list( $credentials['hostname'], $credentials['port'] ) = explode(':', $credentials['hostname'], 2);
66
+ if ( ! is_numeric($credentials['port']) )
67
+ unset($credentials['port']);
68
+ } else {
69
+ unset($credentials['port']);
70
+ }
71
+
72
+ if ( (defined('FTP_SSH') && FTP_SSH) || (defined('FS_METHOD') && 'ssh' == FS_METHOD) )
73
+ $credentials['connection_type'] = 'ssh';
74
+ else if ( (defined('FTP_SSL') && FTP_SSL) && 'ftpext' == $type ) //Only the FTP Extension understands SSL
75
+ $credentials['connection_type'] = 'ftps';
76
+ else if ( !empty($_POST['connection_type']) )
77
+ $credentials['connection_type'] = stripslashes($_POST['connection_type']);
78
+ else if ( !isset($credentials['connection_type']) ) //All else fails (And its not defaulted to something else saved), Default to FTP
79
+ $credentials['connection_type'] = 'ftp';
80
+
81
+ if ( ! $error &&
82
+ (
83
+ ( !empty($credentials['password']) && !empty($credentials['username']) && !empty($credentials['hostname']) ) ||
84
+ ( 'ssh' == $credentials['connection_type'] && !empty($credentials['private_key']) )
85
+ ) ) {
86
+ $stored_credentials = $credentials;
87
+ if ( !empty($stored_credentials['port']) ) //save port as part of hostname to simplify above code.
88
+ $stored_credentials['hostname'] .= ':' . $stored_credentials['port'];
89
+
90
+ unset($stored_credentials['password'], $stored_credentials['port'], $stored_credentials['private_key'], $stored_credentials['public_key']);
91
+ update_option('ftp_credentials', $stored_credentials);
92
+ return $credentials;
93
+ }
94
+ $hostname = '';
95
+ $username = '';
96
+ $password = '';
97
+ $connection_type = '';
98
+ if ( !empty($credentials) )
99
+ extract($credentials, EXTR_OVERWRITE);
100
+ if ( $error ) {
101
+ $error_string = __('<strong>Error:</strong> There was an error connecting to the server, Please verify the settings are correct.');
102
+ if ( is_wp_error($error) )
103
+ $error_string = esc_html( $error->get_error_message() );
104
+ echo '<div id="message" class="error"><p>' . $error_string . '</p></div>';
105
+ }
106
+
107
+ $types = array();
108
+ if ( extension_loaded('ftp') || extension_loaded('sockets') || function_exists('fsockopen') )
109
+ $types[ 'ftp' ] = __('FTP');
110
+ if ( extension_loaded('ftp') ) //Only this supports FTPS
111
+ $types[ 'ftps' ] = __('FTPS (SSL)');
112
+
113
+ $types = apply_filters('fs_ftp_connection_types', $types, $credentials, $type, $error, $context);
114
+
115
+ ?>
116
+ <script type="text/javascript">
117
+ <!--
118
+ jQuery(function($){
119
+ jQuery("#ssh").click(function () {
120
+ jQuery(".ssh_keys").show();
121
+ });
122
+ jQuery("#ftp, #ftps").click(function () {
123
+ jQuery(".ssh_keys").hide();
124
+ });
125
+ jQuery('form input[value=""]:first').focus();
126
+ });
127
+ -->
128
+ </script>
129
+ <form action="<?php echo $form_post ?>" method="post" enctype="multipart/form-data">
130
+ <div class="wrap">
131
+ <?php screen_icon(); ?>
132
+ <h2><?php _e('Connection Information') ?></h2>
133
+ <p><?php
134
+ $label_user = __('Username');
135
+ $label_pass = __('Password');
136
+ _e('To perform the requested action, WordPress needs to access your web server.');
137
+ echo ' ';
138
+ if ( ( isset( $types['ftp'] ) || isset( $types['ftps'] ) ) ) {
139
+ if ( isset( $types['ssh'] ) ) {
140
+ _e('Please enter your FTP or SSH credentials to proceed.');
141
+ $label_user = __('FTP/SSH Username');
142
+ $label_pass = __('FTP/SSH Password');
143
+ } else {
144
+ _e('Please enter your FTP credentials to proceed.');
145
+ $label_user = __('FTP Username');
146
+ $label_pass = __('FTP Password');
147
+ }
148
+ echo ' ';
149
+ }
150
+ _e('If you do not remember your credentials, you should contact your web host.');
151
+ ?></p>
152
+ <table class="form-table">
153
+ <tr valign="top">
154
+ <th scope="row"><label for="hostname"><?php _e('Hostname') ?></label></th>
155
+ <td><input name="hostname" type="text" id="hostname" value="<?php echo esc_attr($hostname); if ( !empty($port) ) echo ":$port"; ?>"<?php disabled( defined('FTP_HOST') ); ?> size="40" /></td>
156
+ </tr>
157
+
158
+ <tr valign="top">
159
+ <th scope="row"><label for="username"><?php echo $label_user; ?></label></th>
160
+ <td><input name="username" type="text" id="username" value="<?php echo esc_attr($username) ?>"<?php disabled( defined('FTP_USER') ); ?> size="40" /></td>
161
+ </tr>
162
+
163
+ <tr valign="top">
164
+ <th scope="row"><label for="password"><?php echo $label_pass; ?></label></th>
165
+ <td><input name="password" type="password" id="password" value="<?php if ( defined('FTP_PASS') ) echo '*****'; ?>"<?php disabled( defined('FTP_PASS') ); ?> size="40" /></td>
166
+ </tr>
167
+
168
+ <?php if ( isset($types['ssh']) ) : ?>
169
+ <tr class="ssh_keys" valign="top" style="<?php if ( 'ssh' != $connection_type ) echo 'display:none' ?>">
170
+ <th scope="row" colspan="2"><?php _e('SSH Authentication Keys') ?>
171
+ <div><?php _e('If a passphrase is needed, enter that in the password field above.') ?></div></th></tr>
172
+ <tr class="ssh_keys" valign="top" style="<?php if ( 'ssh' != $connection_type ) echo 'display:none' ?>">
173
+ <th scope="row">
174
+ <div class="key-labels textright">
175
+ <label for="private_key"><?php _e('Copy / Paste Private Key:') ?></label>
176
+ </div>
177
+ </th>
178
+ <td><textarea name="private_key" id="private_key" value="<?php echo esc_attr($private_key) ?>"<?php disabled( defined('FTP_PRIKEY') ); ?>></textarea>
179
+ </td>
180
+ </tr>
181
+ <tr class="ssh_keys" valign="top" style="<?php if ( 'ssh' != $connection_type ) echo 'display:none' ?>">
182
+ <th scope="row">
183
+ <div class="key-labels textright">
184
+ <label for="private_key_file"><?php _e('Upload Private Key:') ?></label>
185
+ </div>
186
+ </th>
187
+ <td><input name="private_key_file" id="private_key_file" type="file" <?php disabled( defined('FTP_PRIKEY') ); ?>/>
188
+ </td>
189
+ </tr>
190
+ <?php endif; ?>
191
+
192
+ <tr valign="top">
193
+ <th scope="row"><?php _e('Connection Type') ?></th>
194
+ <td>
195
+ <fieldset><legend class="screen-reader-text"><span><?php _e('Connection Type') ?></span></legend>
196
+ <?php
197
+ $disabled = disabled( (defined('FTP_SSL') && FTP_SSL) || (defined('FTP_SSH') && FTP_SSH), true, false );
198
+ foreach ( $types as $name => $text ) : ?>
199
+ <label for="<?php echo esc_attr($name) ?>">
200
+ <input type="radio" name="connection_type" id="<?php echo esc_attr($name) ?>" value="<?php echo esc_attr($name) ?>"<?php checked($name, $connection_type); echo $disabled; ?> />
201
+ <?php echo $text ?>
202
+ </label>
203
+ <?php endforeach; ?>
204
+ </fieldset>
205
+ </td>
206
+ </tr>
207
+ </table>
208
+
209
+ <?php
210
+ foreach ( (array) $extra_fields as $field ) {
211
+ if ( isset( $_POST[ $field ] ) )
212
+ echo '<input type="hidden" name="' . esc_attr( $field ) . '" value="' . esc_attr( stripslashes( $_POST[ $field ] ) ) . '" />';
213
+ }
214
+ submit_button( __( 'Proceed' ), 'button', 'upgrade' );
215
+ ?>
216
+ </div>
217
+ </form>
218
+ <?php
219
+ return false;
220
+ }