Version Description
- update phpseclib to latest SVN
- read file when FTP_PRIKEY is defined (thanks, lkraav!)
Download this release
Release Info
Developer | TerraFrost |
Plugin | SSH SFTP Updater Support |
Version | 0.3 |
Comparing to | |
See all releases |
Version 0.3
- class-wp-filesystem-ssh2.php +335 -0
- phpseclib/Crypt/AES.php +612 -0
- phpseclib/Crypt/DES.php +1298 -0
- phpseclib/Crypt/Hash.php +825 -0
- phpseclib/Crypt/RC4.php +564 -0
- phpseclib/Crypt/RSA.php +2520 -0
- phpseclib/Crypt/Random.php +143 -0
- phpseclib/Crypt/Rijndael.php +1478 -0
- phpseclib/Crypt/TripleDES.php +1061 -0
- phpseclib/Math/BigInteger.php +3551 -0
- phpseclib/Net/SFTP.php +1955 -0
- phpseclib/Net/SSH2.php +2762 -0
- readme.txt +34 -0
- sftp.php +220 -0
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 |
+
}
|