Update PHPseclib

This commit is contained in:
Michel Roux 2014-10-27 11:55:27 +01:00
parent f6857e6c0c
commit 8e7ec31de0
24 changed files with 8522 additions and 7752 deletions

View File

@ -1,16 +1,15 @@
<?php <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/** /**
* Pure-PHP implementation of AES. * Pure-PHP implementation of AES.
* *
* Uses mcrypt, if available, and an internal implementation, otherwise. * Uses mcrypt, if available/possible, and an internal implementation, otherwise.
* *
* PHP versions 4 and 5 * PHP versions 4 and 5
* *
* If {@link Crypt_AES::setKeyLength() setKeyLength()} isn't called, it'll be calculated from * If {@link Crypt_AES::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
* {@link Crypt_AES::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's 136-bits * {@link Crypt_AES::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's 136-bits
* it'll be null-padded to 160-bits and 160 bits will be the key length until {@link Crypt_Rijndael::setKey() setKey()} * it'll be null-padded to 192-bits and 192 bits will be the key length until {@link Crypt_AES::setKey() setKey()}
* is called, again, at which point, it'll be recalculated. * is called, again, at which point, it'll be recalculated.
* *
* Since Crypt_AES extends Crypt_Rijndael, some functions are available to be called that, in the context of AES, don't * Since Crypt_AES extends Crypt_Rijndael, some functions are available to be called that, in the context of AES, don't
@ -20,7 +19,7 @@
* Here's a short example of how to use this library: * Here's a short example of how to use this library:
* <code> * <code>
* <?php * <?php
* include('Crypt/AES.php'); * include 'Crypt/AES.php';
* *
* $aes = new Crypt_AES(); * $aes = new Crypt_AES();
* *
@ -66,7 +65,7 @@
* Include Crypt_Rijndael * Include Crypt_Rijndael
*/ */
if (!class_exists('Crypt_Rijndael')) { if (!class_exists('Crypt_Rijndael')) {
require_once 'Rijndael.php'; include_once 'Rijndael.php';
} }
/**#@+ /**#@+
@ -81,172 +80,71 @@ if (!class_exists('Crypt_Rijndael')) {
* *
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
*/ */
define('CRYPT_AES_MODE_CTR', -1); define('CRYPT_AES_MODE_CTR', CRYPT_MODE_CTR);
/** /**
* Encrypt / decrypt using the Electronic Code Book mode. * Encrypt / decrypt using the Electronic Code Book mode.
* *
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
*/ */
define('CRYPT_AES_MODE_ECB', 1); define('CRYPT_AES_MODE_ECB', CRYPT_MODE_ECB);
/** /**
* Encrypt / decrypt using the Code Book Chaining mode. * Encrypt / decrypt using the Code Book Chaining mode.
* *
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
*/ */
define('CRYPT_AES_MODE_CBC', 2); define('CRYPT_AES_MODE_CBC', CRYPT_MODE_CBC);
/** /**
* Encrypt / decrypt using the Cipher Feedback mode. * Encrypt / decrypt using the Cipher Feedback mode.
* *
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
*/ */
define('CRYPT_AES_MODE_CFB', 3); define('CRYPT_AES_MODE_CFB', CRYPT_MODE_CFB);
/** /**
* Encrypt / decrypt using the Cipher Feedback mode. * Encrypt / decrypt using the Cipher Feedback mode.
* *
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
*/ */
define('CRYPT_AES_MODE_OFB', 4); define('CRYPT_AES_MODE_OFB', CRYPT_MODE_OFB);
/**#@-*/ /**#@-*/
/**#@+ /**#@+
* @access private * @access private
* @see Crypt_AES::Crypt_AES() * @see Crypt_Base::Crypt_Base()
*/ */
/** /**
* Toggles the internal implementation * Toggles the internal implementation
*/ */
define('CRYPT_AES_MODE_INTERNAL', 1); define('CRYPT_AES_MODE_INTERNAL', CRYPT_MODE_INTERNAL);
/** /**
* Toggles the mcrypt implementation * Toggles the mcrypt implementation
*/ */
define('CRYPT_AES_MODE_MCRYPT', 2); define('CRYPT_AES_MODE_MCRYPT', CRYPT_MODE_MCRYPT);
/**#@-*/ /**#@-*/
/** /**
* Pure-PHP implementation of AES. * Pure-PHP implementation of AES.
* *
* @author Jim Wigginton <terrafrost@php.net>
* @version 0.1.0
* @access public
* @package Crypt_AES * @package Crypt_AES
*/ * @author Jim Wigginton <terrafrost@php.net>
class Crypt_AES extends Crypt_Rijndael {
/**
* mcrypt resource for encryption
*
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
* Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
*
* @see Crypt_AES::encrypt()
* @var String
* @access private
*/
var $enmcrypt;
/**
* mcrypt resource for decryption
*
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
* Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
*
* @see Crypt_AES::decrypt()
* @var String
* @access private
*/
var $demcrypt;
/**
* mcrypt resource for CFB mode
*
* @see Crypt_AES::encrypt()
* @see Crypt_AES::decrypt()
* @var String
* @access private
*/
var $ecb;
/**
* Default Constructor.
*
* Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
* CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC. If not explictly set, CRYPT_AES_MODE_CBC will be used.
*
* @param optional Integer $mode
* @return Crypt_AES
* @access public * @access public
*/ */
function Crypt_AES($mode = CRYPT_AES_MODE_CBC) class Crypt_AES extends Crypt_Rijndael
{ {
if ( !defined('CRYPT_AES_MODE') ) { /**
switch (true) { * The namespace used by the cipher for its constants.
case extension_loaded('mcrypt') && in_array('rijndael-128', mcrypt_list_algorithms()): *
define('CRYPT_AES_MODE', CRYPT_AES_MODE_MCRYPT); * @see Crypt_Base::const_namespace
break; * @var String
default: * @access private
define('CRYPT_AES_MODE', CRYPT_AES_MODE_INTERNAL); */
} var $const_namespace = 'AES';
}
switch ( CRYPT_AES_MODE ) {
case CRYPT_AES_MODE_MCRYPT:
switch ($mode) {
case CRYPT_AES_MODE_ECB:
$this->paddable = true;
$this->mode = MCRYPT_MODE_ECB;
break;
case CRYPT_AES_MODE_CTR:
// ctr doesn't have a constant associated with it even though it appears to be fairly widely
// supported. in lieu of knowing just how widely supported it is, i've, for now, opted not to
// include a compatibility layer. the layer has been implemented but, for now, is commented out.
$this->mode = 'ctr';
//$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_AES_MODE_CTR;
break;
case CRYPT_AES_MODE_CFB:
$this->mode = 'ncfb';
break;
case CRYPT_AES_MODE_OFB:
$this->mode = MCRYPT_MODE_NOFB;
break;
case CRYPT_AES_MODE_CBC:
default:
$this->paddable = true;
$this->mode = MCRYPT_MODE_CBC;
}
break;
default:
switch ($mode) {
case CRYPT_AES_MODE_ECB:
$this->paddable = true;
$this->mode = CRYPT_RIJNDAEL_MODE_ECB;
break;
case CRYPT_AES_MODE_CTR:
$this->mode = CRYPT_RIJNDAEL_MODE_CTR;
break;
case CRYPT_AES_MODE_CFB:
$this->mode = CRYPT_RIJNDAEL_MODE_CFB;
break;
case CRYPT_AES_MODE_OFB:
$this->mode = CRYPT_RIJNDAEL_MODE_OFB;
break;
case CRYPT_AES_MODE_CBC:
default:
$this->paddable = true;
$this->mode = CRYPT_RIJNDAEL_MODE_CBC;
}
}
if (CRYPT_AES_MODE == CRYPT_AES_MODE_INTERNAL) {
parent::Crypt_Rijndael($this->mode);
}
}
/** /**
* Dummy function * Dummy function
* *
* Since Crypt_AES extends Crypt_Rijndael, this function is, technically, available, but it doesn't do anything. * Since Crypt_AES extends Crypt_Rijndael, this function is, technically, available, but it doesn't do anything.
* *
* @see Crypt_Rijndael::setBlockLength()
* @access public * @access public
* @param Integer $length * @param Integer $length
*/ */
@ -256,285 +154,54 @@ class Crypt_AES extends Crypt_Rijndael {
} }
/** /**
* Sets the initialization vector. (optional) * Sets the key length
* *
* SetIV is not required when CRYPT_RIJNDAEL_MODE_ECB is being used. If not explictly set, it'll be assumed * Valid key lengths are 128, 192, and 256. If the length is less than 128, it will be rounded up to
* to be all zero's. * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
* *
* @see Crypt_Rijndael:setKeyLength()
* @access public * @access public
* @param String $iv * @param Integer $length
*/ */
function setIV($iv) function setKeyLength($length)
{ {
parent::setIV($iv); switch ($length) {
if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) { case 160:
$this->changed = true; $length = 192;
break;
case 224:
$length = 256;
} }
parent::setKeyLength($length);
} }
/** /**
* Encrypts a message. * Sets the key.
* *
* $plaintext will be padded with up to 16 additional bytes. Other AES implementations may or may not pad in the * Rijndael supports five different key lengths, AES only supports three.
* same manner. Other common approaches to padding and the reasons why it's necessary are discussed in the following
* URL:
* *
* {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html} * @see Crypt_Rijndael:setKey()
* * @see setKeyLength()
* An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does.
* strlen($plaintext) will still need to be a multiple of 16, however, arbitrary values can be added to make it that
* length.
*
* @see Crypt_AES::decrypt()
* @access public * @access public
* @param String $plaintext * @param String $key
*/ */
function encrypt($plaintext) function setKey($key)
{ {
if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) { parent::setKey($key);
$this->_mcryptSetup();
// re: http://phpseclib.sourceforge.net/cfb-demo.phps
// using mcrypt's default handing of CFB the above would output two different things. using phpseclib's
// rewritten CFB implementation the above outputs the same thing twice.
if ($this->mode == 'ncfb' && $this->continuousBuffer) {
$iv = &$this->encryptIV;
$pos = &$this->enbuffer['pos'];
$len = strlen($plaintext);
$ciphertext = '';
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = 16 - $pos;
if ($len >= $max) {
$i = $max;
$len-= $max;
$pos = 0;
} else {
$i = $len;
$pos+= $len;
$len = 0;
}
$ciphertext = substr($iv, $orig_pos) ^ $plaintext;
$iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
$this->enbuffer['enmcrypt_init'] = true;
}
if ($len >= 16) {
if ($this->enbuffer['enmcrypt_init'] === false || $len > 280) {
if ($this->enbuffer['enmcrypt_init'] === true) {
mcrypt_generic_init($this->enmcrypt, $this->key, $iv);
$this->enbuffer['enmcrypt_init'] = false;
}
$ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % 16));
$iv = substr($ciphertext, -16);
$len%= 16;
} else {
while ($len >= 16) {
$iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, 16);
$ciphertext.= $iv;
$len-= 16;
$i+= 16;
}
}
}
if ($len) {
$iv = mcrypt_generic($this->ecb, $iv);
$block = $iv ^ substr($plaintext, -$len);
$iv = substr_replace($iv, $block, 0, $len);
$ciphertext.= $block;
$pos = $len;
}
return $ciphertext;
}
if ($this->paddable) {
$plaintext = $this->_pad($plaintext);
}
$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
}
return $ciphertext;
}
return parent::encrypt($plaintext);
}
/**
* Decrypts a message.
*
* If strlen($ciphertext) is not a multiple of 16, null bytes will be added to the end of the string until it is.
*
* @see Crypt_AES::encrypt()
* @access public
* @param String $ciphertext
*/
function decrypt($ciphertext)
{
if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
$this->_mcryptSetup();
if ($this->mode == 'ncfb' && $this->continuousBuffer) {
$iv = &$this->decryptIV;
$pos = &$this->debuffer['pos'];
$len = strlen($ciphertext);
$plaintext = '';
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = 16 - $pos;
if ($len >= $max) {
$i = $max;
$len-= $max;
$pos = 0;
} else {
$i = $len;
$pos+= $len;
$len = 0;
}
// ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
$plaintext = substr($iv, $orig_pos) ^ $ciphertext;
$iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
}
if ($len >= 16) {
$cb = substr($ciphertext, $i, $len - $len % 16);
$plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb;
$iv = substr($cb, -16);
$len%= 16;
}
if ($len) {
$iv = mcrypt_generic($this->ecb, $iv);
$plaintext.= $iv ^ substr($ciphertext, -$len);
$iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len);
$pos = $len;
}
return $plaintext;
}
if ($this->paddable) {
// we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
// "The data is padded with "\0" to make sure the length of the data is n * blocksize."
$ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 15) & 0xFFFFFFF0, chr(0));
}
$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
}
return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
}
return parent::decrypt($ciphertext);
}
/**
* Setup mcrypt
*
* Validates all the variables.
*
* @access private
*/
function _mcryptSetup()
{
if (!$this->changed) {
return;
}
if (!$this->explicit_key_length) { if (!$this->explicit_key_length) {
// this just copied from Crypt_Rijndael::_setup() $length = strlen($key);
$length = strlen($this->key) >> 2; switch (true) {
if ($length > 8) { case $length <= 16:
$length = 8;
} else if ($length < 4) {
$length = 4;
}
$this->Nk = $length;
$this->key_size = $length << 2;
}
switch ($this->Nk) {
case 4: // 128
$this->key_size = 16; $this->key_size = 16;
break; break;
case 5: // 160 case $length <= 24:
case 6: // 192
$this->key_size = 24; $this->key_size = 24;
break; break;
case 7: // 224 default:
case 8: // 256
$this->key_size = 32; $this->key_size = 32;
} }
$this->_setupEngine();
$this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, chr(0));
$this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, 16), 16, chr(0));
if (!isset($this->enmcrypt)) {
$mode = $this->mode;
//$mode = $this->mode == CRYPT_AES_MODE_CTR ? MCRYPT_MODE_ECB : $this->mode;
$this->demcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');
$this->enmcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');
if ($mode == 'ncfb') {
$this->ecb = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
}
} // else should mcrypt_generic_deinit be called?
mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
if ($this->mode == 'ncfb') {
mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
}
$this->changed = false;
}
/**
* Treat consecutive "packets" as if they are a continuous buffer.
*
* The default behavior.
*
* @see Crypt_Rijndael::disableContinuousBuffer()
* @access public
*/
function enableContinuousBuffer()
{
parent::enableContinuousBuffer();
if (CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT) {
$this->enbuffer['enmcrypt_init'] = true;
$this->debuffer['demcrypt_init'] = true;
}
}
/**
* Treat consecutive packets as if they are a discontinuous buffer.
*
* The default behavior.
*
* @see Crypt_Rijndael::enableContinuousBuffer()
* @access public
*/
function disableContinuousBuffer()
{
parent::disableContinuousBuffer();
if (CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT) {
mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
} }
} }
} }
// vim: ts=4:sw=4:et:
// vim6: fdl=1:

2011
Crypt/Base.php Executable file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,11 @@
<?php <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/** /**
* Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions. * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
* *
* Uses hash() or mhash() if available and an internal implementation, otherwise. Currently supports the following: * Uses hash() or mhash() if available and an internal implementation, otherwise. Currently supports the following:
* *
* md2, md5, md5-96, sha1, sha1-96, sha256, sha384, and sha512 * md2, md5, md5-96, sha1, sha1-96, sha256, sha256-96, sha384, and sha512, sha512-96
* *
* If {@link Crypt_Hash::setKey() setKey()} is called, {@link Crypt_Hash::hash() hash()} will return the HMAC as opposed to * If {@link Crypt_Hash::setKey() setKey()} is called, {@link Crypt_Hash::hash() hash()} will return the HMAC as opposed to
* the hash. If no valid algorithm is provided, sha1 will be used. * the hash. If no valid algorithm is provided, sha1 will be used.
@ -19,7 +18,7 @@
* Here's a short example of how to use this library: * Here's a short example of how to use this library:
* <code> * <code>
* <?php * <?php
* include('Crypt/Hash.php'); * include 'Crypt/Hash.php';
* *
* $hash = new Crypt_Hash('sha1'); * $hash = new Crypt_Hash('sha1');
* *
@ -76,12 +75,21 @@ define('CRYPT_HASH_MODE_HASH', 3);
/** /**
* Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions. * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
* *
* @author Jim Wigginton <terrafrost@php.net>
* @version 0.1.0
* @access public
* @package Crypt_Hash * @package Crypt_Hash
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/ */
class Crypt_Hash { class Crypt_Hash
{
/**
* Hash Parameter
*
* @see Crypt_Hash::setHash()
* @var Integer
* @access private
*/
var $hashParam;
/** /**
* Byte-length of compression blocks / key (Internal HMAC) * Byte-length of compression blocks / key (Internal HMAC)
* *
@ -174,6 +182,19 @@ class Crypt_Hash {
$this->key = $key; $this->key = $key;
} }
/**
* Gets the hash function.
*
* As set by the constructor or by the setHash() method.
*
* @access public
* @return String
*/
function getHash()
{
return $this->hashParam;
}
/** /**
* Sets the hash function. * Sets the hash function.
* *
@ -182,10 +203,13 @@ class Crypt_Hash {
*/ */
function setHash($hash) function setHash($hash)
{ {
$hash = strtolower($hash); $this->hashParam = $hash = strtolower($hash);
switch ($hash) { switch ($hash) {
case 'md5-96': case 'md5-96':
case 'sha1-96': case 'sha1-96':
case 'sha256-96':
case 'sha512-96':
$hash = substr($hash, 0, -3);
$this->l = 12; // 96 / 8 = 12 $this->l = 12; // 96 / 8 = 12
break; break;
case 'md2': case 'md2':
@ -222,14 +246,12 @@ class Crypt_Hash {
case CRYPT_HASH_MODE_MHASH: case CRYPT_HASH_MODE_MHASH:
switch ($hash) { switch ($hash) {
case 'md5': case 'md5':
case 'md5-96':
$this->hash = MHASH_MD5; $this->hash = MHASH_MD5;
break; break;
case 'sha256': case 'sha256':
$this->hash = MHASH_SHA256; $this->hash = MHASH_SHA256;
break; break;
case 'sha1': case 'sha1':
case 'sha1-96':
default: default:
$this->hash = MHASH_SHA1; $this->hash = MHASH_SHA1;
} }
@ -237,7 +259,6 @@ class Crypt_Hash {
case CRYPT_HASH_MODE_HASH: case CRYPT_HASH_MODE_HASH:
switch ($hash) { switch ($hash) {
case 'md5': case 'md5':
case 'md5-96':
$this->hash = 'md5'; $this->hash = 'md5';
return; return;
case 'md2': case 'md2':
@ -247,7 +268,6 @@ class Crypt_Hash {
$this->hash = $hash; $this->hash = $hash;
return; return;
case 'sha1': case 'sha1':
case 'sha1-96':
default: default:
$this->hash = 'sha1'; $this->hash = 'sha1';
} }
@ -260,7 +280,6 @@ class Crypt_Hash {
$this->hash = array($this, '_md2'); $this->hash = array($this, '_md2');
break; break;
case 'md5': case 'md5':
case 'md5-96':
$this->b = 64; $this->b = 64;
$this->hash = array($this, '_md5'); $this->hash = array($this, '_md5');
break; break;
@ -274,7 +293,6 @@ class Crypt_Hash {
$this->hash = array($this, '_sha512'); $this->hash = array($this, '_sha512');
break; break;
case 'sha1': case 'sha1':
case 'sha1-96':
default: default:
$this->b = 64; $this->b = 64;
$this->hash = array($this, '_sha1'); $this->hash = array($this, '_sha1');
@ -559,7 +577,7 @@ class Crypt_Hash {
function _sha512($m) function _sha512($m)
{ {
if (!class_exists('Math_BigInteger')) { if (!class_exists('Math_BigInteger')) {
require_once('Math/BigInteger.php'); include_once 'Math/BigInteger.php';
} }
static $init384, $init512, $k; static $init384, $init512, $k;

652
Crypt/RC2.php Executable file
View File

@ -0,0 +1,652 @@
<?php
/**
* Pure-PHP implementation of RC2.
*
* Uses mcrypt, if available, and an internal implementation, otherwise.
*
* PHP versions 4 and 5
*
* Useful resources are as follows:
*
* - {@link http://tools.ietf.org/html/rfc2268}
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'Crypt/RC2.php';
*
* $rc2 = new Crypt_RC2();
*
* $rc2->setKey('abcdefgh');
*
* $plaintext = str_repeat('a', 1024);
*
* echo $rc2->decrypt($rc2->encrypt($plaintext));
* ?>
* </code>
*
* LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @category Crypt
* @package Crypt_RC2
* @author Patrick Monnerat <pm@datasphere.ch>
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
/**
* Include Crypt_Base
*
* Base cipher class
*/
if (!class_exists('Crypt_Base')) {
include_once 'Base.php';
}
/**#@+
* @access public
* @see Crypt_RC2::encrypt()
* @see Crypt_RC2::decrypt()
*/
/**
* Encrypt / decrypt using the Counter mode.
*
* Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
*/
define('CRYPT_RC2_MODE_CTR', CRYPT_MODE_CTR);
/**
* Encrypt / decrypt using the Electronic Code Book mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
*/
define('CRYPT_RC2_MODE_ECB', CRYPT_MODE_ECB);
/**
* Encrypt / decrypt using the Code Book Chaining mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
*/
define('CRYPT_RC2_MODE_CBC', CRYPT_MODE_CBC);
/**
* Encrypt / decrypt using the Cipher Feedback mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
*/
define('CRYPT_RC2_MODE_CFB', CRYPT_MODE_CFB);
/**
* Encrypt / decrypt using the Cipher Feedback mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
*/
define('CRYPT_RC2_MODE_OFB', CRYPT_MODE_OFB);
/**#@-*/
/**#@+
* @access private
* @see Crypt_RC2::Crypt_RC2()
*/
/**
* Toggles the internal implementation
*/
define('CRYPT_RC2_MODE_INTERNAL', CRYPT_MODE_INTERNAL);
/**
* Toggles the mcrypt implementation
*/
define('CRYPT_RC2_MODE_MCRYPT', CRYPT_MODE_MCRYPT);
/**#@-*/
/**
* Pure-PHP implementation of RC2.
*
* @package Crypt_RC2
* @access public
*/
class Crypt_RC2 extends Crypt_Base
{
/**
* Block Length of the cipher
*
* @see Crypt_Base::block_size
* @var Integer
* @access private
*/
var $block_size = 8;
/**
* The Key
*
* @see Crypt_Base::key
* @see setKey()
* @var String
* @access private
*/
var $key = "\0";
/**
* The default password key_size used by setPassword()
*
* @see Crypt_Base::password_key_size
* @see Crypt_Base::setPassword()
* @var Integer
* @access private
*/
var $password_key_size = 16; // = 128 bits
/**
* The namespace used by the cipher for its constants.
*
* @see Crypt_Base::const_namespace
* @var String
* @access private
*/
var $const_namespace = 'RC2';
/**
* The mcrypt specific name of the cipher
*
* @see Crypt_Base::cipher_name_mcrypt
* @var String
* @access private
*/
var $cipher_name_mcrypt = 'rc2';
/**
* Optimizing value while CFB-encrypting
*
* @see Crypt_Base::cfb_init_len
* @var Integer
* @access private
*/
var $cfb_init_len = 500;
/**
* The key length in bits.
*
* @see Crypt_RC2::setKeyLength()
* @see Crypt_RC2::setKey()
* @var Integer
* @access private
* @internal Should be in range [1..1024].
* @internal Changing this value after setting the key has no effect.
*/
var $default_key_length = 1024;
/**
* The Key Schedule
*
* @see Crypt_RC2::_setupKey()
* @var Array
* @access private
*/
var $keys;
/**
* Key expansion randomization table.
* Twice the same 256-value sequence to save a modulus in key expansion.
*
* @see Crypt_RC2::setKey()
* @var Array
* @access private
*/
var $pitable = array(
0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED,
0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D,
0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E,
0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2,
0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13,
0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32,
0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B,
0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82,
0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C,
0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC,
0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1,
0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26,
0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57,
0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03,
0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7,
0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7,
0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7,
0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A,
0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74,
0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC,
0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC,
0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39,
0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A,
0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31,
0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE,
0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9,
0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C,
0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9,
0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0,
0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E,
0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77,
0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD,
0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED,
0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D,
0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E,
0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2,
0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13,
0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32,
0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B,
0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82,
0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C,
0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC,
0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1,
0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26,
0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57,
0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03,
0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7,
0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7,
0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7,
0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A,
0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74,
0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC,
0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC,
0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39,
0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A,
0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31,
0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE,
0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9,
0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C,
0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9,
0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0,
0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E,
0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77,
0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD
);
/**
* Inverse key expansion randomization table.
*
* @see Crypt_RC2::setKey()
* @var Array
* @access private
*/
var $invpitable = array(
0xD1, 0xDA, 0xB9, 0x6F, 0x9C, 0xC8, 0x78, 0x66,
0x80, 0x2C, 0xF8, 0x37, 0xEA, 0xE0, 0x62, 0xA4,
0xCB, 0x71, 0x50, 0x27, 0x4B, 0x95, 0xD9, 0x20,
0x9D, 0x04, 0x91, 0xE3, 0x47, 0x6A, 0x7E, 0x53,
0xFA, 0x3A, 0x3B, 0xB4, 0xA8, 0xBC, 0x5F, 0x68,
0x08, 0xCA, 0x8F, 0x14, 0xD7, 0xC0, 0xEF, 0x7B,
0x5B, 0xBF, 0x2F, 0xE5, 0xE2, 0x8C, 0xBA, 0x12,
0xE1, 0xAF, 0xB2, 0x54, 0x5D, 0x59, 0x76, 0xDB,
0x32, 0xA2, 0x58, 0x6E, 0x1C, 0x29, 0x64, 0xF3,
0xE9, 0x96, 0x0C, 0x98, 0x19, 0x8D, 0x3E, 0x26,
0xAB, 0xA5, 0x85, 0x16, 0x40, 0xBD, 0x49, 0x67,
0xDC, 0x22, 0x94, 0xBB, 0x3C, 0xC1, 0x9B, 0xEB,
0x45, 0x28, 0x18, 0xD8, 0x1A, 0x42, 0x7D, 0xCC,
0xFB, 0x65, 0x8E, 0x3D, 0xCD, 0x2A, 0xA3, 0x60,
0xAE, 0x93, 0x8A, 0x48, 0x97, 0x51, 0x15, 0xF7,
0x01, 0x0B, 0xB7, 0x36, 0xB1, 0x2E, 0x11, 0xFD,
0x84, 0x2D, 0x3F, 0x13, 0x88, 0xB3, 0x34, 0x24,
0x1B, 0xDE, 0xC5, 0x1D, 0x4D, 0x2B, 0x17, 0x31,
0x74, 0xA9, 0xC6, 0x43, 0x6D, 0x39, 0x90, 0xBE,
0xC3, 0xB0, 0x21, 0x6B, 0xF6, 0x0F, 0xD5, 0x99,
0x0D, 0xAC, 0x1F, 0x5C, 0x9E, 0xF5, 0xF9, 0x4C,
0xD6, 0xDF, 0x89, 0xE4, 0x8B, 0xFF, 0xC7, 0xAA,
0xE7, 0xED, 0x46, 0x25, 0xB6, 0x06, 0x5E, 0x35,
0xB5, 0xEC, 0xCE, 0xE8, 0x6C, 0x30, 0x55, 0x61,
0x4A, 0xFE, 0xA0, 0x79, 0x03, 0xF0, 0x10, 0x72,
0x7C, 0xCF, 0x52, 0xA6, 0xA7, 0xEE, 0x44, 0xD3,
0x9A, 0x57, 0x92, 0xD0, 0x5A, 0x7A, 0x41, 0x7F,
0x0E, 0x00, 0x63, 0xF2, 0x4F, 0x05, 0x83, 0xC9,
0xA1, 0xD4, 0xDD, 0xC4, 0x56, 0xF4, 0xD2, 0x77,
0x81, 0x09, 0x82, 0x33, 0x9F, 0x07, 0x86, 0x75,
0x38, 0x4E, 0x69, 0xF1, 0xAD, 0x23, 0x73, 0x87,
0x70, 0x02, 0xC2, 0x1E, 0xB8, 0x0A, 0xFC, 0xE6
);
/**
* Default Constructor.
*
* Determines whether or not the mcrypt extension should be used.
*
* $mode could be:
*
* - CRYPT_RC2_MODE_ECB
*
* - CRYPT_RC2_MODE_CBC
*
* - CRYPT_RC2_MODE_CTR
*
* - CRYPT_RC2_MODE_CFB
*
* - CRYPT_RC2_MODE_OFB
*
* If not explicitly set, CRYPT_RC2_MODE_CBC will be used.
*
* @see Crypt_Base::Crypt_Base()
* @param optional Integer $mode
* @access public
*/
function Crypt_RC2($mode = CRYPT_RC2_MODE_CBC)
{
parent::Crypt_Base($mode);
$this->setKey('');
}
/**
* Sets the key length
*
* Valid key lengths are 1 to 1024.
* Calling this function after setting the key has no effect until the next
* Crypt_RC2::setKey() call.
*
* @access public
* @param Integer $length in bits
*/
function setKeyLength($length)
{
if ($length >= 1 && $length <= 1024) {
$this->default_key_length = $length;
}
}
/**
* Sets the key.
*
* Keys can be of any length. RC2, itself, uses 1 to 1024 bit keys (eg.
* strlen($key) <= 128), however, we only use the first 128 bytes if $key
* has more then 128 bytes in it, and set $key to a single null byte if
* it is empty.
*
* If the key is not explicitly set, it'll be assumed to be a single
* null byte.
*
* @see Crypt_Base::setKey()
* @access public
* @param String $key
* @param Integer $t1 optional Effective key length in bits.
*/
function setKey($key, $t1 = 0)
{
if ($t1 <= 0) {
$t1 = $this->default_key_length;
} else if ($t1 > 1024) {
$t1 = 1024;
}
// Key byte count should be 1..128.
$key = strlen($key) ? substr($key, 0, 128) : "\x00";
$t = strlen($key);
// The mcrypt RC2 implementation only supports effective key length
// of 1024 bits. It is however possible to handle effective key
// lengths in range 1..1024 by expanding the key and applying
// inverse pitable mapping to the first byte before submitting it
// to mcrypt.
// Key expansion.
$l = array_values(unpack('C*', $key));
$t8 = ($t1 + 7) >> 3;
$tm = 0xFF >> (8 * $t8 - $t1);
// Expand key.
$pitable = $this->pitable;
for ($i = $t; $i < 128; $i++) {
$l[$i] = $pitable[$l[$i - 1] + $l[$i - $t]];
}
$i = 128 - $t8;
$l[$i] = $pitable[$l[$i] & $tm];
while ($i--) {
$l[$i] = $pitable[$l[$i + 1] ^ $l[$i + $t8]];
}
// Prepare the key for mcrypt.
$l[0] = $this->invpitable[$l[0]];
array_unshift($l, 'C*');
parent::setKey(call_user_func_array('pack', $l));
}
/**
* Encrypts a block
*
* @see Crypt_Base::_encryptBlock()
* @see Crypt_Base::encrypt()
* @access private
* @param String $in
* @return String
*/
function _encryptBlock($in)
{
list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in));
$keys = $this->keys;
$limit = 20;
$actions = array($limit => 44, 44 => 64);
$j = 0;
for (;;) {
// Mixing round.
$r0 = (($r0 + $keys[$j++] + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1;
$r0 |= $r0 >> 16;
$r1 = (($r1 + $keys[$j++] + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2;
$r1 |= $r1 >> 16;
$r2 = (($r2 + $keys[$j++] + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3;
$r2 |= $r2 >> 16;
$r3 = (($r3 + $keys[$j++] + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5;
$r3 |= $r3 >> 16;
if ($j === $limit) {
if ($limit === 64) {
break;
}
// Mashing round.
$r0 += $keys[$r3 & 0x3F];
$r1 += $keys[$r0 & 0x3F];
$r2 += $keys[$r1 & 0x3F];
$r3 += $keys[$r2 & 0x3F];
$limit = $actions[$limit];
}
}
return pack('vvvv', $r0, $r1, $r2, $r3);
}
/**
* Decrypts a block
*
* @see Crypt_Base::_decryptBlock()
* @see Crypt_Base::decrypt()
* @access private
* @param String $in
* @return String
*/
function _decryptBlock($in)
{
list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in));
$keys = $this->keys;
$limit = 44;
$actions = array($limit => 20, 20 => 0);
$j = 64;
for (;;) {
// R-mixing round.
$r3 = ($r3 | ($r3 << 16)) >> 5;
$r3 = ($r3 - $keys[--$j] - ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF;
$r2 = ($r2 | ($r2 << 16)) >> 3;
$r2 = ($r2 - $keys[--$j] - ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF;
$r1 = ($r1 | ($r1 << 16)) >> 2;
$r1 = ($r1 - $keys[--$j] - ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF;
$r0 = ($r0 | ($r0 << 16)) >> 1;
$r0 = ($r0 - $keys[--$j] - ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;
if ($j === $limit) {
if ($limit === 0) {
break;
}
// R-mashing round.
$r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF;
$r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF;
$r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF;
$r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;
$limit = $actions[$limit];
}
}
return pack('vvvv', $r0, $r1, $r2, $r3);
}
/**
* Creates the key schedule
*
* @see Crypt_Base::_setupKey()
* @access private
*/
function _setupKey()
{
// Key has already been expanded in Crypt_RC2::setKey():
// Only the first value must be altered.
$l = unpack('Ca/Cb/v*', $this->key);
array_unshift($l, $this->pitable[$l['a']] | ($l['b'] << 8));
unset($l['a']);
unset($l['b']);
$this->keys = $l;
}
/**
* Setup the performance-optimized function for de/encrypt()
*
* @see Crypt_Base::_setupInlineCrypt()
* @access private
*/
function _setupInlineCrypt()
{
$lambda_functions = &Crypt_RC2::_getLambdaFunctions();
// The first 10 generated $lambda_functions will use the $keys hardcoded as integers
// for the mixing rounds, for better inline crypt performance [~20% faster].
// But for memory reason we have to limit those ultra-optimized $lambda_functions to an amount of 10.
$keys = $this->keys;
if (count($lambda_functions) >= 10) {
foreach ($this->keys as $k => $v) {
$keys[$k] = '$keys[' . $k . ']';
}
}
$code_hash = md5(str_pad("Crypt_RC2, {$this->mode}, ", 32, "\0") . implode(',', $keys));
// Is there a re-usable $lambda_functions in there?
// If not, we have to create it.
if (!isset($lambda_functions[$code_hash])) {
// Init code for both, encrypt and decrypt.
$init_crypt = '$keys = $self->keys;';
// $in is the current 8 bytes block which has to be en/decrypt
$encrypt_block = $decrypt_block = '
$in = unpack("v4", $in);
$r0 = $in[1];
$r1 = $in[2];
$r2 = $in[3];
$r3 = $in[4];
';
// Create code for encryption.
$limit = 20;
$actions = array($limit => 44, 44 => 64);
$j = 0;
for (;;) {
// Mixing round.
$encrypt_block .= '
$r0 = (($r0 + ' . $keys[$j++] . ' +
((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1;
$r0 |= $r0 >> 16;
$r1 = (($r1 + ' . $keys[$j++] . ' +
((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2;
$r1 |= $r1 >> 16;
$r2 = (($r2 + ' . $keys[$j++] . ' +
((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3;
$r2 |= $r2 >> 16;
$r3 = (($r3 + ' . $keys[$j++] . ' +
((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5;
$r3 |= $r3 >> 16;';
if ($j === $limit) {
if ($limit === 64) {
break;
}
// Mashing round.
$encrypt_block .= '
$r0 += $keys[$r3 & 0x3F];
$r1 += $keys[$r0 & 0x3F];
$r2 += $keys[$r1 & 0x3F];
$r3 += $keys[$r2 & 0x3F];';
$limit = $actions[$limit];
}
}
$encrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);';
// Create code for decryption.
$limit = 44;
$actions = array($limit => 20, 20 => 0);
$j = 64;
for (;;) {
// R-mixing round.
$decrypt_block .= '
$r3 = ($r3 | ($r3 << 16)) >> 5;
$r3 = ($r3 - ' . $keys[--$j] . ' -
((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF;
$r2 = ($r2 | ($r2 << 16)) >> 3;
$r2 = ($r2 - ' . $keys[--$j] . ' -
((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF;
$r1 = ($r1 | ($r1 << 16)) >> 2;
$r1 = ($r1 - ' . $keys[--$j] . ' -
((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF;
$r0 = ($r0 | ($r0 << 16)) >> 1;
$r0 = ($r0 - ' . $keys[--$j] . ' -
((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;';
if ($j === $limit) {
if ($limit === 0) {
break;
}
// R-mashing round.
$decrypt_block .= '
$r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF;
$r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF;
$r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF;
$r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;';
$limit = $actions[$limit];
}
}
$decrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);';
// Creates the inline-crypt function
$lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
array(
'init_crypt' => $init_crypt,
'encrypt_block' => $encrypt_block,
'decrypt_block' => $decrypt_block
)
);
}
// Set the inline-crypt function as callback in: $this->inline_crypt
$this->inline_crypt = $lambda_functions[$code_hash];
}
}

View File

@ -1,5 +1,4 @@
<?php <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/** /**
* Pure-PHP implementation of RC4. * Pure-PHP implementation of RC4.
@ -19,7 +18,7 @@
* Here's a short example of how to use this library: * Here's a short example of how to use this library:
* <code> * <code>
* <?php * <?php
* include('Crypt/RC4.php'); * include 'Crypt/RC4.php';
* *
* $rc4 = new Crypt_RC4(); * $rc4 = new Crypt_RC4();
* *
@ -61,6 +60,15 @@
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
/**
* Include Crypt_Base
*
* Base cipher class
*/
if (!class_exists('Crypt_Base')) {
include_once 'Base.php';
}
/**#@+ /**#@+
* @access private * @access private
* @see Crypt_RC4::Crypt_RC4() * @see Crypt_RC4::Crypt_RC4()
@ -68,11 +76,11 @@
/** /**
* Toggles the internal implementation * Toggles the internal implementation
*/ */
define('CRYPT_RC4_MODE_INTERNAL', 1); define('CRYPT_RC4_MODE_INTERNAL', CRYPT_MODE_INTERNAL);
/** /**
* Toggles the mcrypt implementation * Toggles the mcrypt implementation
*/ */
define('CRYPT_RC4_MODE_MCRYPT', 2); define('CRYPT_RC4_MODE_MCRYPT', CRYPT_MODE_MCRYPT);
/**#@-*/ /**#@-*/
/**#@+ /**#@+
@ -86,12 +94,61 @@ define('CRYPT_RC4_DECRYPT', 1);
/** /**
* Pure-PHP implementation of RC4. * Pure-PHP implementation of RC4.
* *
* @author Jim Wigginton <terrafrost@php.net>
* @version 0.1.0
* @access public
* @package Crypt_RC4 * @package Crypt_RC4
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/ */
class Crypt_RC4 { class Crypt_RC4 extends Crypt_Base
{
/**
* Block Length of the cipher
*
* RC4 is a stream cipher
* so we the block_size to 0
*
* @see Crypt_Base::block_size
* @var Integer
* @access private
*/
var $block_size = 0;
/**
* The default password key_size used by setPassword()
*
* @see Crypt_Base::password_key_size
* @see Crypt_Base::setPassword()
* @var Integer
* @access private
*/
var $password_key_size = 128; // = 1024 bits
/**
* The namespace used by the cipher for its constants.
*
* @see Crypt_Base::const_namespace
* @var String
* @access private
*/
var $const_namespace = 'RC4';
/**
* The mcrypt specific name of the cipher
*
* @see Crypt_Base::cipher_name_mcrypt
* @var String
* @access private
*/
var $cipher_name_mcrypt = 'arcfour';
/**
* Holds whether performance-optimized $inline_crypt() can/should be used.
*
* @see Crypt_Base::inline_crypt
* @var mixed
* @access private
*/
var $use_inline_crypt = false; // currently not available
/** /**
* The Key * The Key
* *
@ -102,190 +159,26 @@ class Crypt_RC4 {
var $key = "\0"; var $key = "\0";
/** /**
* The Key Stream for encryption * The Key Stream for decryption and encryption
*
* If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object
* *
* @see Crypt_RC4::setKey() * @see Crypt_RC4::setKey()
* @var Array * @var Array
* @access private * @access private
*/ */
var $encryptStream = false; var $stream;
/**
* The Key Stream for decryption
*
* If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object
*
* @see Crypt_RC4::setKey()
* @var Array
* @access private
*/
var $decryptStream = false;
/**
* The $i and $j indexes for encryption
*
* @see Crypt_RC4::_crypt()
* @var Integer
* @access private
*/
var $encryptIndex = 0;
/**
* The $i and $j indexes for decryption
*
* @see Crypt_RC4::_crypt()
* @var Integer
* @access private
*/
var $decryptIndex = 0;
/**
* The Encryption Algorithm
*
* Only used if CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT. Only possible values are MCRYPT_RC4 or MCRYPT_ARCFOUR.
*
* @see Crypt_RC4::Crypt_RC4()
* @var Integer
* @access private
*/
var $mode;
/**
* Continuous Buffer status
*
* @see Crypt_RC4::enableContinuousBuffer()
* @var Boolean
* @access private
*/
var $continuousBuffer = false;
/** /**
* Default Constructor. * Default Constructor.
* *
* Determines whether or not the mcrypt extension should be used. * Determines whether or not the mcrypt extension should be used.
* *
* @see Crypt_Base::Crypt_Base()
* @return Crypt_RC4 * @return Crypt_RC4
* @access public * @access public
*/ */
function Crypt_RC4() function Crypt_RC4()
{ {
if ( !defined('CRYPT_RC4_MODE') ) { parent::Crypt_Base(CRYPT_MODE_STREAM);
switch (true) {
case extension_loaded('mcrypt') && (defined('MCRYPT_ARCFOUR') || defined('MCRYPT_RC4')) && in_array('arcfour', mcrypt_list_algorithms()):
define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_MCRYPT);
break;
default:
define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_INTERNAL);
}
}
switch ( CRYPT_RC4_MODE ) {
case CRYPT_RC4_MODE_MCRYPT:
switch (true) {
case defined('MCRYPT_ARCFOUR'):
$this->mode = MCRYPT_ARCFOUR;
break;
case defined('MCRYPT_RC4');
$this->mode = MCRYPT_RC4;
}
$this->encryptStream = mcrypt_module_open($this->mode, '', MCRYPT_MODE_STREAM, '');
$this->decryptStream = mcrypt_module_open($this->mode, '', MCRYPT_MODE_STREAM, '');
}
}
/**
* Sets the key.
*
* Keys can be between 1 and 256 bytes long. If they are longer then 256 bytes, the first 256 bytes will
* be used. If no key is explicitly set, it'll be assumed to be a single null byte.
*
* @access public
* @param String $key
*/
function setKey($key)
{
$this->key = $key;
if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
mcrypt_generic_init($this->encryptStream, $this->key, '');
mcrypt_generic_init($this->decryptStream, $this->key, '');
return;
}
$keyLength = strlen($key);
$keyStream = array();
for ($i = 0; $i < 256; $i++) {
$keyStream[$i] = $i;
}
$j = 0;
for ($i = 0; $i < 256; $i++) {
$j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255;
$temp = $keyStream[$i];
$keyStream[$i] = $keyStream[$j];
$keyStream[$j] = $temp;
}
$this->encryptIndex = $this->decryptIndex = array(0, 0);
$this->encryptStream = $this->decryptStream = $keyStream;
}
/**
* Sets the password.
*
* Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
* {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}:
* $hash, $salt, $count, $dkLen
*
* @param String $password
* @param optional String $method
* @access public
*/
function setPassword($password, $method = 'pbkdf2')
{
$key = '';
switch ($method) {
default: // 'pbkdf2'
list(, , $hash, $salt, $count) = func_get_args();
if (!isset($hash)) {
$hash = 'sha1';
}
// WPA and WPA2 use the SSID as the salt
if (!isset($salt)) {
$salt = 'phpseclib/salt';
}
// RFC2898#section-4.2 uses 1,000 iterations by default
// WPA and WPA2 use 4,096.
if (!isset($count)) {
$count = 1000;
}
if (!isset($dkLen)) {
$dkLen = 128;
}
if (!class_exists('Crypt_Hash')) {
require_once('Crypt/Hash.php');
}
$i = 1;
while (strlen($key) < $dkLen) {
//$dk.= $this->_pbkdf($password, $salt, $count, $i++);
$hmac = new Crypt_Hash();
$hmac->setHash($hash);
$hmac->setKey($password);
$f = $u = $hmac->hash($salt . pack('N', $i++));
for ($j = 2; $j <= $count; $j++) {
$u = $hmac->hash($u);
$f^= $u;
}
$key.= $f;
}
}
$this->setKey(substr($key, 0, $dkLen));
} }
/** /**
@ -311,15 +204,35 @@ class Crypt_RC4 {
{ {
} }
/**
* Sets the key.
*
* Keys can be between 1 and 256 bytes long. If they are longer then 256 bytes, the first 256 bytes will
* be used. If no key is explicitly set, it'll be assumed to be a single null byte.
*
* @access public
* @see Crypt_Base::setKey()
* @param String $key
*/
function setKey($key)
{
parent::setKey(substr($key, 0, 256));
}
/** /**
* Encrypts a message. * Encrypts a message.
* *
* @see Crypt_Base::decrypt()
* @see Crypt_RC4::_crypt() * @see Crypt_RC4::_crypt()
* @access public * @access public
* @param String $plaintext * @param String $plaintext
* @return String $ciphertext
*/ */
function encrypt($plaintext) function encrypt($plaintext)
{ {
if ($this->engine == CRYPT_MODE_MCRYPT) {
return parent::encrypt($plaintext);
}
return $this->_crypt($plaintext, CRYPT_RC4_ENCRYPT); return $this->_crypt($plaintext, CRYPT_RC4_ENCRYPT);
} }
@ -329,15 +242,48 @@ class Crypt_RC4 {
* $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)). * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
* At least if the continuous buffer is disabled. * At least if the continuous buffer is disabled.
* *
* @see Crypt_Base::encrypt()
* @see Crypt_RC4::_crypt() * @see Crypt_RC4::_crypt()
* @access public * @access public
* @param String $ciphertext * @param String $ciphertext
* @return String $plaintext
*/ */
function decrypt($ciphertext) function decrypt($ciphertext)
{ {
if ($this->engine == CRYPT_MODE_MCRYPT) {
return parent::decrypt($ciphertext);
}
return $this->_crypt($ciphertext, CRYPT_RC4_DECRYPT); return $this->_crypt($ciphertext, CRYPT_RC4_DECRYPT);
} }
/**
* Setup the key (expansion)
*
* @see Crypt_Base::_setupKey()
* @access private
*/
function _setupKey()
{
$key = $this->key;
$keyLength = strlen($key);
$keyStream = range(0, 255);
$j = 0;
for ($i = 0; $i < 256; $i++) {
$j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255;
$temp = $keyStream[$i];
$keyStream[$i] = $keyStream[$j];
$keyStream[$j] = $temp;
}
$this->stream = array();
$this->stream[CRYPT_RC4_DECRYPT] = $this->stream[CRYPT_RC4_ENCRYPT] = array(
0, // index $i
0, // index $j
$keyStream
);
}
/** /**
* Encrypts or decrypts a message. * Encrypts or decrypts a message.
* *
@ -346,147 +292,38 @@ class Crypt_RC4 {
* @access private * @access private
* @param String $text * @param String $text
* @param Integer $mode * @param Integer $mode
* @return String $text
*/ */
function _crypt($text, $mode) function _crypt($text, $mode)
{ {
if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) { if ($this->changed) {
$keyStream = $mode == CRYPT_RC4_ENCRYPT ? 'encryptStream' : 'decryptStream'; $this->_setup();
$this->changed = false;
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->$keyStream, $this->key, '');
}
return mcrypt_generic($this->$keyStream, $text);
}
if ($this->encryptStream === false) {
$this->setKey($this->key);
}
switch ($mode) {
case CRYPT_RC4_ENCRYPT:
$keyStream = $this->encryptStream;
list($i, $j) = $this->encryptIndex;
break;
case CRYPT_RC4_DECRYPT:
$keyStream = $this->decryptStream;
list($i, $j) = $this->decryptIndex;
}
$newText = '';
for ($k = 0; $k < strlen($text); $k++) {
$i = ($i + 1) & 255;
$j = ($j + $keyStream[$i]) & 255;
$temp = $keyStream[$i];
$keyStream[$i] = $keyStream[$j];
$keyStream[$j] = $temp;
$temp = $keyStream[($keyStream[$i] + $keyStream[$j]) & 255];
$newText.= chr(ord($text[$k]) ^ $temp);
} }
$stream = &$this->stream[$mode];
if ($this->continuousBuffer) { if ($this->continuousBuffer) {
switch ($mode) { $i = &$stream[0];
case CRYPT_RC4_ENCRYPT: $j = &$stream[1];
$this->encryptStream = $keyStream; $keyStream = &$stream[2];
$this->encryptIndex = array($i, $j); } else {
break; $i = $stream[0];
case CRYPT_RC4_DECRYPT: $j = $stream[1];
$this->decryptStream = $keyStream; $keyStream = $stream[2];
$this->decryptIndex = array($i, $j);
}
} }
return $newText; $len = strlen($text);
for ($k = 0; $k < $len; ++$k) {
$i = ($i + 1) & 255;
$ksi = $keyStream[$i];
$j = ($j + $ksi) & 255;
$ksj = $keyStream[$j];
$keyStream[$i] = $ksj;
$keyStream[$j] = $ksi;
$text[$k] = $text[$k] ^ chr($keyStream[($ksj + $ksi) & 255]);
} }
/** return $text;
* Treat consecutive "packets" as if they are a continuous buffer.
*
* Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
* will yield different outputs:
*
* <code>
* echo $rc4->encrypt(substr($plaintext, 0, 8));
* echo $rc4->encrypt(substr($plaintext, 8, 8));
* </code>
* <code>
* echo $rc4->encrypt($plaintext);
* </code>
*
* The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
* another, as demonstrated with the following:
*
* <code>
* $rc4->encrypt(substr($plaintext, 0, 8));
* echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));
* </code>
* <code>
* echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));
* </code>
*
* With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
* outputs. The reason is due to the fact that the initialization vector's change after every encryption /
* decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
*
* Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
* encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
* continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
* however, they are also less intuitive and more likely to cause you problems.
*
* @see Crypt_RC4::disableContinuousBuffer()
* @access public
*/
function enableContinuousBuffer()
{
if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
mcrypt_generic_init($this->encryptStream, $this->key, '');
mcrypt_generic_init($this->decryptStream, $this->key, '');
}
$this->continuousBuffer = true;
}
/**
* Treat consecutive packets as if they are a discontinuous buffer.
*
* The default behavior.
*
* @see Crypt_RC4::enableContinuousBuffer()
* @access public
*/
function disableContinuousBuffer()
{
if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_INTERNAL ) {
$this->encryptIndex = $this->decryptIndex = array(0, 0);
$this->encryptStream = $this->decryptStream = false;
}
$this->continuousBuffer = false;
}
/**
* Dummy function.
*
* Since RC4 is a stream cipher and not a block cipher, no padding is necessary. The only reason this function is
* included is so that you can switch between a block cipher and a stream cipher transparently.
*
* @see Crypt_RC4::disablePadding()
* @access public
*/
function enablePadding()
{
}
/**
* Dummy function.
*
* @see Crypt_RC4::enablePadding()
* @access public
*/
function disablePadding()
{
} }
} }
// vim: ts=4:sw=4:et:
// vim6: fdl=1:

View File

@ -1,5 +1,4 @@
<?php <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/** /**
* Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA. * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
@ -9,7 +8,7 @@
* Here's an example of how to encrypt and decrypt text with this library: * Here's an example of how to encrypt and decrypt text with this library:
* <code> * <code>
* <?php * <?php
* include('Crypt/RSA.php'); * include 'Crypt/RSA.php';
* *
* $rsa = new Crypt_RSA(); * $rsa = new Crypt_RSA();
* extract($rsa->createKey()); * extract($rsa->createKey());
@ -27,7 +26,7 @@
* Here's an example of how to create signatures and verify signatures with this library: * Here's an example of how to create signatures and verify signatures with this library:
* <code> * <code>
* <?php * <?php
* include('Crypt/RSA.php'); * include 'Crypt/RSA.php';
* *
* $rsa = new Crypt_RSA(); * $rsa = new Crypt_RSA();
* extract($rsa->createKey()); * extract($rsa->createKey());
@ -73,17 +72,17 @@
*/ */
// the class_exists() will only be called if the crypt_random_string function hasn't been defined and // the class_exists() will only be called if the crypt_random_string function hasn't been defined and
// will trigger a call to __autoload() if you're wanting to auto-load classes // will trigger a call to __autoload() if you're wanting to auto-load classes
// call function_exists() a second time to stop the require_once from being called outside // call function_exists() a second time to stop the include_once from being called outside
// of the auto loader // of the auto loader
if (!function_exists('crypt_random_string')) { if (!function_exists('crypt_random_string')) {
require_once('Random.php'); include_once 'Random.php';
} }
/** /**
* Include Crypt_Hash * Include Crypt_Hash
*/ */
if (!class_exists('Crypt_Hash')) { if (!class_exists('Crypt_Hash')) {
require_once('Hash.php'); include_once 'Hash.php';
} }
/**#@+ /**#@+
@ -105,7 +104,7 @@ define('CRYPT_RSA_ENCRYPTION_OAEP', 1);
* Use PKCS#1 padding. * Use PKCS#1 padding.
* *
* Although CRYPT_RSA_ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards * Although CRYPT_RSA_ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
* compatability with protocols (like SSH-1) written before OAEP's introduction. * compatibility with protocols (like SSH-1) written before OAEP's introduction.
*/ */
define('CRYPT_RSA_ENCRYPTION_PKCS1', 2); define('CRYPT_RSA_ENCRYPTION_PKCS1', 2);
/**#@-*/ /**#@-*/
@ -129,7 +128,7 @@ define('CRYPT_RSA_SIGNATURE_PSS', 1);
* Use the PKCS#1 scheme by default. * Use the PKCS#1 scheme by default.
* *
* Although CRYPT_RSA_SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards * Although CRYPT_RSA_SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
* compatability with protocols (like SSH-2) written before PSS's introduction. * compatibility with protocols (like SSH-2) written before PSS's introduction.
*/ */
define('CRYPT_RSA_SIGNATURE_PKCS1', 2); define('CRYPT_RSA_SIGNATURE_PKCS1', 2);
/**#@-*/ /**#@-*/
@ -146,6 +145,14 @@ define('CRYPT_RSA_ASN1_INTEGER', 2);
* ASN1 Bit String * ASN1 Bit String
*/ */
define('CRYPT_RSA_ASN1_BITSTRING', 3); define('CRYPT_RSA_ASN1_BITSTRING', 3);
/**
* ASN1 Octet String
*/
define('CRYPT_RSA_ASN1_OCTETSTRING', 4);
/**
* ASN1 Object Identifier
*/
define('CRYPT_RSA_ASN1_OBJECT', 6);
/** /**
* ASN1 Sequence (with the constucted bit set) * ASN1 Sequence (with the constucted bit set)
*/ */
@ -173,7 +180,6 @@ define('CRYPT_RSA_MODE_OPENSSL', 2);
*/ */
define('CRYPT_RSA_OPENSSL_CONFIG', dirname(__FILE__) . '/../openssl.cnf'); define('CRYPT_RSA_OPENSSL_CONFIG', dirname(__FILE__) . '/../openssl.cnf');
/**#@+ /**#@+
* @access public * @access public
* @see Crypt_RSA::createKey() * @see Crypt_RSA::createKey()
@ -193,6 +199,10 @@ define('CRYPT_RSA_PRIVATE_FORMAT_PUTTY', 1);
* XML formatted private key * XML formatted private key
*/ */
define('CRYPT_RSA_PRIVATE_FORMAT_XML', 2); define('CRYPT_RSA_PRIVATE_FORMAT_XML', 2);
/**
* PKCS#8 formatted private key
*/
define('CRYPT_RSA_PRIVATE_FORMAT_PKCS8', 3);
/**#@-*/ /**#@-*/
/**#@+ /**#@+
@ -218,7 +228,14 @@ define('CRYPT_RSA_PUBLIC_FORMAT_RAW', 3);
* PKCS#1 formatted public key (raw) * PKCS#1 formatted public key (raw)
* *
* Used by File/X509.php * Used by File/X509.php
*
* Has the following header:
*
* -----BEGIN RSA PUBLIC KEY-----
*
* Analogous to ssh-keygen's pem format (as specified by -m)
*/ */
define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1', 4);
define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW', 4); define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW', 4);
/** /**
* XML formatted public key * XML formatted public key
@ -234,19 +251,27 @@ define('CRYPT_RSA_PUBLIC_FORMAT_OPENSSH', 6);
* PKCS#1 formatted public key (encapsulated) * PKCS#1 formatted public key (encapsulated)
* *
* Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set) * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
*
* Has the following header:
*
* -----BEGIN PUBLIC KEY-----
*
* Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
* is specific to private keys it's basically creating a DER-encoded wrapper
* for keys. This just extends that same concept to public keys (much like ssh-keygen)
*/ */
define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1', 7); define('CRYPT_RSA_PUBLIC_FORMAT_PKCS8', 7);
/**#@-*/ /**#@-*/
/** /**
* Pure-PHP PKCS#1 compliant implementation of RSA. * Pure-PHP PKCS#1 compliant implementation of RSA.
* *
* @author Jim Wigginton <terrafrost@php.net>
* @version 0.1.0
* @access public
* @package Crypt_RSA * @package Crypt_RSA
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/ */
class Crypt_RSA { class Crypt_RSA
{
/** /**
* Precomputed Zero * Precomputed Zero
* *
@ -277,7 +302,7 @@ class Crypt_RSA {
* @var Integer * @var Integer
* @access public * @access public
*/ */
var $publicKeyFormat = CRYPT_RSA_PUBLIC_FORMAT_PKCS1; var $publicKeyFormat = CRYPT_RSA_PUBLIC_FORMAT_PKCS8;
/** /**
* Modulus (ie. n) * Modulus (ie. n)
@ -434,7 +459,7 @@ class Crypt_RSA {
/** /**
* OpenSSL configuration file name. * OpenSSL configuration file name.
* *
* Set to NULL to use system configuration file. * Set to null to use system configuration file.
* @see Crypt_RSA::createKey() * @see Crypt_RSA::createKey()
* @var Mixed * @var Mixed
* @Access public * @Access public
@ -462,18 +487,53 @@ class Crypt_RSA {
function Crypt_RSA() function Crypt_RSA()
{ {
if (!class_exists('Math_BigInteger')) { if (!class_exists('Math_BigInteger')) {
require_once('Math/BigInteger.php'); include_once 'Math/BigInteger.php';
} }
$this->configFile = CRYPT_RSA_OPENSSL_CONFIG; $this->configFile = CRYPT_RSA_OPENSSL_CONFIG;
if ( !defined('CRYPT_RSA_MODE') ) { if ( !defined('CRYPT_RSA_MODE') ) {
switch (true) { switch (true) {
// Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular,
// Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger
// can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either.
case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
break;
// openssl_pkey_get_details - which is used in the only place Crypt/RSA.php uses OpenSSL - was introduced in PHP 5.2.0
case !function_exists('openssl_pkey_get_details'):
define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
break;
case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>=') && file_exists($this->configFile): case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>=') && file_exists($this->configFile):
// some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
ob_start();
@phpinfo();
$content = ob_get_contents();
ob_end_clean();
preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
$versions = array();
if (!empty($matches[1])) {
for ($i = 0; $i < count($matches[1]); $i++) {
$versions[$matches[1][$i]] = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
}
}
// it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
switch (true) {
case !isset($versions['Header']):
case !isset($versions['Library']):
case $versions['Header'] == $versions['Library']:
define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL); define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL);
break; break;
default: default:
define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL); define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
}
break;
default:
define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
} }
} }
@ -524,7 +584,7 @@ class Crypt_RSA {
$config['config'] = $this->configFile; $config['config'] = $this->configFile;
} }
$rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config); $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config);
openssl_pkey_export($rsa, $privatekey, NULL, $config); openssl_pkey_export($rsa, $privatekey, null, $config);
$publickey = openssl_pkey_get_details($rsa); $publickey = openssl_pkey_get_details($rsa);
$publickey = $publickey['key']; $publickey = $publickey['key'];
@ -642,12 +702,12 @@ class Crypt_RSA {
$exponents[$i] = $e->modInverse($temp); $exponents[$i] = $e->modInverse($temp);
} }
list($lcm) = $lcm['top']->divide($lcm['bottom']); list($temp) = $lcm['top']->divide($lcm['bottom']);
$gcd = $lcm->gcd($e); $gcd = $temp->gcd($e);
$i0 = 1; $i0 = 1;
} while (!$gcd->equals($this->one)); } while (!$gcd->equals($this->one));
$d = $e->modInverse($lcm); $d = $e->modInverse($temp);
$coefficients[2] = $primes[2]->modInverse($primes[1]); $coefficients[2] = $primes[2]->modInverse($primes[1]);
@ -682,17 +742,18 @@ class Crypt_RSA {
*/ */
function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients) function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
{ {
$signed = $this->privateKeyFormat != CRYPT_RSA_PRIVATE_FORMAT_XML;
$num_primes = count($primes); $num_primes = count($primes);
$raw = array( $raw = array(
'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
'modulus' => $n->toBytes(true), 'modulus' => $n->toBytes($signed),
'publicExponent' => $e->toBytes(true), 'publicExponent' => $e->toBytes($signed),
'privateExponent' => $d->toBytes(true), 'privateExponent' => $d->toBytes($signed),
'prime1' => $primes[1]->toBytes(true), 'prime1' => $primes[1]->toBytes($signed),
'prime2' => $primes[2]->toBytes(true), 'prime2' => $primes[2]->toBytes($signed),
'exponent1' => $exponents[1]->toBytes(true), 'exponent1' => $exponents[1]->toBytes($signed),
'exponent2' => $exponents[2]->toBytes(true), 'exponent2' => $exponents[2]->toBytes($signed),
'coefficient' => $coefficients[2]->toBytes(true) 'coefficient' => $coefficients[2]->toBytes($signed)
); );
// if the format in question does not support multi-prime rsa and multi-prime rsa was used, // if the format in question does not support multi-prime rsa and multi-prime rsa was used,
@ -729,7 +790,7 @@ class Crypt_RSA {
strlen($this->comment), $this->comment, strlen($public), $public strlen($this->comment), $this->comment, strlen($public), $public
); );
$public = base64_encode($public); $public = base64_encode($public);
$key.= "Public-Lines: " . ((strlen($public) + 32) >> 6) . "\r\n"; $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n";
$key.= chunk_split($public, 64); $key.= chunk_split($public, 64);
$private = pack('Na*Na*Na*Na*', $private = pack('Na*Na*Na*Na*',
strlen($raw['privateExponent']), $raw['privateExponent'], strlen($raw['prime1']), $raw['prime1'], strlen($raw['privateExponent']), $raw['privateExponent'], strlen($raw['prime1']), $raw['prime1'],
@ -742,7 +803,7 @@ class Crypt_RSA {
$private.= crypt_random_string(16 - (strlen($private) & 15)); $private.= crypt_random_string(16 - (strlen($private) & 15));
$source.= pack('Na*', strlen($private), $private); $source.= pack('Na*', strlen($private), $private);
if (!class_exists('Crypt_AES')) { if (!class_exists('Crypt_AES')) {
require_once('Crypt/AES.php'); include_once 'Crypt/AES.php';
} }
$sequence = 0; $sequence = 0;
$symkey = ''; $symkey = '';
@ -760,10 +821,10 @@ class Crypt_RSA {
} }
$private = base64_encode($private); $private = base64_encode($private);
$key.= 'Private-Lines: ' . ((strlen($private) + 32) >> 6) . "\r\n"; $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n";
$key.= chunk_split($private, 64); $key.= chunk_split($private, 64);
if (!class_exists('Crypt_Hash')) { if (!class_exists('Crypt_Hash')) {
require_once('Crypt/Hash.php'); include_once 'Crypt/Hash.php';
} }
$hash = new Crypt_Hash('sha1'); $hash = new Crypt_Hash('sha1');
$hash->setKey(pack('H*', sha1($hashkey))); $hash->setKey(pack('H*', sha1($hashkey)));
@ -798,12 +859,58 @@ class Crypt_RSA {
$RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
if ($this->privateKeyFormat == CRYPT_RSA_PRIVATE_FORMAT_PKCS8) {
$rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
$RSAPrivateKey = pack('Ca*a*Ca*a*',
CRYPT_RSA_ASN1_INTEGER, "\01\00", $rsaOID, 4, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey
);
$RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
if (!empty($this->password) || is_string($this->password)) {
$salt = crypt_random_string(8);
$iterationCount = 2048;
if (!class_exists('Crypt_DES')) {
include_once 'Crypt/DES.php';
}
$crypto = new Crypt_DES();
$crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
$RSAPrivateKey = $crypto->encrypt($RSAPrivateKey);
$parameters = pack('Ca*a*Ca*N',
CRYPT_RSA_ASN1_OCTETSTRING, $this->_encodeLength(strlen($salt)), $salt,
CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(4), $iterationCount
);
$pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";
$encryptionAlgorithm = pack('Ca*a*Ca*a*',
CRYPT_RSA_ASN1_OBJECT, $this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)), $pbeWithMD5AndDES_CBC,
CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($parameters)), $parameters
);
$RSAPrivateKey = pack('Ca*a*Ca*a*',
CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($encryptionAlgorithm)), $encryptionAlgorithm,
CRYPT_RSA_ASN1_OCTETSTRING, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey
);
$RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
$RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
chunk_split(base64_encode($RSAPrivateKey), 64) .
'-----END ENCRYPTED PRIVATE KEY-----';
} else {
$RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" .
chunk_split(base64_encode($RSAPrivateKey), 64) .
'-----END PRIVATE KEY-----';
}
return $RSAPrivateKey;
}
if (!empty($this->password) || is_string($this->password)) { if (!empty($this->password) || is_string($this->password)) {
$iv = crypt_random_string(8); $iv = crypt_random_string(8);
$symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
$symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8); $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
if (!class_exists('Crypt_TripleDES')) { if (!class_exists('Crypt_TripleDES')) {
require_once('Crypt/TripleDES.php'); include_once 'Crypt/TripleDES.php';
} }
$des = new Crypt_TripleDES(); $des = new Crypt_TripleDES();
$des->setKey($symkey); $des->setKey($symkey);
@ -835,8 +942,10 @@ class Crypt_RSA {
*/ */
function _convertPublicKey($n, $e) function _convertPublicKey($n, $e)
{ {
$modulus = $n->toBytes(true); $signed = $this->publicKeyFormat != CRYPT_RSA_PUBLIC_FORMAT_XML;
$publicExponent = $e->toBytes(true);
$modulus = $n->toBytes($signed);
$publicExponent = $e->toBytes($signed);
switch ($this->publicKeyFormat) { switch ($this->publicKeyFormat) {
case CRYPT_RSA_PUBLIC_FORMAT_RAW: case CRYPT_RSA_PUBLIC_FORMAT_RAW:
@ -872,7 +981,11 @@ class Crypt_RSA {
$components['modulus'], $components['publicExponent'] $components['modulus'], $components['publicExponent']
); );
if ($this->publicKeyFormat == CRYPT_RSA_PUBLIC_FORMAT_PKCS1) { if ($this->publicKeyFormat == CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW) {
$RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" .
chunk_split(base64_encode($RSAPublicKey), 64) .
'-----END RSA PUBLIC KEY-----';
} else {
// sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption. // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
$rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
$RSAPublicKey = chr(0) . $RSAPublicKey; $RSAPublicKey = chr(0) . $RSAPublicKey;
@ -881,11 +994,11 @@ class Crypt_RSA {
$RSAPublicKey = pack('Ca*a*', $RSAPublicKey = pack('Ca*a*',
CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey
); );
}
$RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
chunk_split(base64_encode($RSAPublicKey), 64) . chunk_split(base64_encode($RSAPublicKey), 64) .
'-----END PUBLIC KEY-----'; '-----END PUBLIC KEY-----';
}
return $RSAPublicKey; return $RSAPublicKey;
} }
@ -941,6 +1054,7 @@ class Crypt_RSA {
} }
return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false; return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
case CRYPT_RSA_PRIVATE_FORMAT_PKCS1: case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
case CRYPT_RSA_PRIVATE_FORMAT_PKCS8:
case CRYPT_RSA_PUBLIC_FORMAT_PKCS1: case CRYPT_RSA_PUBLIC_FORMAT_PKCS1:
/* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
"outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
@ -961,41 +1075,42 @@ class Crypt_RSA {
$iv = pack('H*', trim($matches[2])); $iv = pack('H*', trim($matches[2]));
$symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
$symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8))); $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8)));
$ciphertext = preg_replace('#.+(\r|\n|\r\n)\1|[\r\n]|-.+-| #s', '', $key); // remove the Proc-Type / DEK-Info sections as they're no longer needed
$ciphertext = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $ciphertext) ? base64_decode($ciphertext) : false; $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
$ciphertext = $this->_extractBER($key);
if ($ciphertext === false) { if ($ciphertext === false) {
$ciphertext = $key; $ciphertext = $key;
} }
switch ($matches[1]) { switch ($matches[1]) {
case 'AES-256-CBC': case 'AES-256-CBC':
if (!class_exists('Crypt_AES')) { if (!class_exists('Crypt_AES')) {
require_once('Crypt/AES.php'); include_once 'Crypt/AES.php';
} }
$crypto = new Crypt_AES(); $crypto = new Crypt_AES();
break; break;
case 'AES-128-CBC': case 'AES-128-CBC':
if (!class_exists('Crypt_AES')) { if (!class_exists('Crypt_AES')) {
require_once('Crypt/AES.php'); include_once 'Crypt/AES.php';
} }
$symkey = substr($symkey, 0, 16); $symkey = substr($symkey, 0, 16);
$crypto = new Crypt_AES(); $crypto = new Crypt_AES();
break; break;
case 'DES-EDE3-CFB': case 'DES-EDE3-CFB':
if (!class_exists('Crypt_TripleDES')) { if (!class_exists('Crypt_TripleDES')) {
require_once('Crypt/TripleDES.php'); include_once 'Crypt/TripleDES.php';
} }
$crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CFB); $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CFB);
break; break;
case 'DES-EDE3-CBC': case 'DES-EDE3-CBC':
if (!class_exists('Crypt_TripleDES')) { if (!class_exists('Crypt_TripleDES')) {
require_once('Crypt/TripleDES.php'); include_once 'Crypt/TripleDES.php';
} }
$symkey = substr($symkey, 0, 24); $symkey = substr($symkey, 0, 24);
$crypto = new Crypt_TripleDES(); $crypto = new Crypt_TripleDES();
break; break;
case 'DES-CBC': case 'DES-CBC':
if (!class_exists('Crypt_DES')) { if (!class_exists('Crypt_DES')) {
require_once('Crypt/DES.php'); include_once 'Crypt/DES.php';
} }
$crypto = new Crypt_DES(); $crypto = new Crypt_DES();
break; break;
@ -1006,8 +1121,7 @@ class Crypt_RSA {
$crypto->setIV($iv); $crypto->setIV($iv);
$decoded = $crypto->decrypt($ciphertext); $decoded = $crypto->decrypt($ciphertext);
} else { } else {
$decoded = preg_replace('#-.+-|[\r\n]| #', '', $key); $decoded = $this->_extractBER($key);
$decoded = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $decoded) ? base64_decode($decoded) : false;
} }
if ($decoded !== false) { if ($decoded !== false) {
@ -1031,7 +1145,9 @@ class Crypt_RSA {
7:d=1 hl=2 l= 13 cons: SEQUENCE 7:d=1 hl=2 l= 13 cons: SEQUENCE
9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
20:d=2 hl=2 l= 0 prim: NULL 20:d=2 hl=2 l= 0 prim: NULL
22:d=1 hl=4 l= 609 prim: OCTET STRING */ 22:d=1 hl=4 l= 609 prim: OCTET STRING
ie. PKCS8 keys*/
if ($tag == CRYPT_RSA_ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") { if ($tag == CRYPT_RSA_ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
$this->_string_shift($key, 3); $this->_string_shift($key, 3);
@ -1039,6 +1155,52 @@ class Crypt_RSA {
} }
if ($tag == CRYPT_RSA_ASN1_SEQUENCE) { if ($tag == CRYPT_RSA_ASN1_SEQUENCE) {
$temp = $this->_string_shift($key, $this->_decodeLength($key));
if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_OBJECT) {
return false;
}
$length = $this->_decodeLength($temp);
switch ($this->_string_shift($temp, $length)) {
case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
break;
case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
/*
PBEParameter ::= SEQUENCE {
salt OCTET STRING (SIZE(8)),
iterationCount INTEGER }
*/
if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_SEQUENCE) {
return false;
}
if ($this->_decodeLength($temp) != strlen($temp)) {
return false;
}
$this->_string_shift($temp); // assume it's an octet string
$salt = $this->_string_shift($temp, $this->_decodeLength($temp));
if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_INTEGER) {
return false;
}
$this->_decodeLength($temp);
list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
$this->_string_shift($key); // assume it's an octet string
$length = $this->_decodeLength($key);
if (strlen($key) != $length) {
return false;
}
if (!class_exists('Crypt_DES')) {
include_once 'Crypt/DES.php';
}
$crypto = new Crypt_DES();
$crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
$key = $crypto->decrypt($key);
if ($key === false) {
return false;
}
return $this->_parseKey($key, CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
default:
return false;
}
/* intended for keys for which OpenSSL's asn1parse returns the following: /* intended for keys for which OpenSSL's asn1parse returns the following:
0:d=0 hl=4 l= 290 cons: SEQUENCE 0:d=0 hl=4 l= 290 cons: SEQUENCE
@ -1046,7 +1208,6 @@ class Crypt_RSA {
6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
17:d=2 hl=2 l= 0 prim: NULL 17:d=2 hl=2 l= 0 prim: NULL
19:d=1 hl=4 l= 271 prim: BIT STRING */ 19:d=1 hl=4 l= 271 prim: BIT STRING */
$this->_string_shift($key, $this->_decodeLength($key));
$tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
$this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
// "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
@ -1209,7 +1370,7 @@ class Crypt_RSA {
switch ($encryption) { switch ($encryption) {
case 'aes256-cbc': case 'aes256-cbc':
if (!class_exists('Crypt_AES')) { if (!class_exists('Crypt_AES')) {
require_once('Crypt/AES.php'); include_once 'Crypt/AES.php';
} }
$symkey = ''; $symkey = '';
$sequence = 0; $sequence = 0;
@ -1311,9 +1472,6 @@ class Crypt_RSA {
break; break;
case 'D': case 'D':
$this->current = &$this->components['privateExponent']; $this->current = &$this->components['privateExponent'];
break;
default:
unset($this->current);
} }
$this->current = ''; $this->current = '';
} }
@ -1329,11 +1487,10 @@ class Crypt_RSA {
*/ */
function _stop_element_handler($parser, $name) function _stop_element_handler($parser, $name)
{ {
//$name = strtoupper($name); if (isset($this->current)) {
if ($name == 'RSAKEYVALUE') {
return;
}
$this->current = new Math_BigInteger(base64_decode($this->current), 256); $this->current = new Math_BigInteger(base64_decode($this->current), 256);
unset($this->current);
}
} }
/** /**
@ -1364,6 +1521,53 @@ class Crypt_RSA {
*/ */
function loadKey($key, $type = false) function loadKey($key, $type = false)
{ {
if (is_object($key) && strtolower(get_class($key)) == 'crypt_rsa') {
$this->privateKeyFormat = $key->privateKeyFormat;
$this->publicKeyFormat = $key->publicKeyFormat;
$this->k = $key->k;
$this->hLen = $key->hLen;
$this->sLen = $key->sLen;
$this->mgfHLen = $key->mgfHLen;
$this->encryptionMode = $key->encryptionMode;
$this->signatureMode = $key->signatureMode;
$this->password = $key->password;
$this->configFile = $key->configFile;
$this->comment = $key->comment;
if (is_object($key->hash)) {
$this->hash = new Crypt_Hash($key->hash->getHash());
}
if (is_object($key->mgfHash)) {
$this->mgfHash = new Crypt_Hash($key->mgfHash->getHash());
}
if (is_object($key->modulus)) {
$this->modulus = $key->modulus->copy();
}
if (is_object($key->exponent)) {
$this->exponent = $key->exponent->copy();
}
if (is_object($key->publicExponent)) {
$this->publicExponent = $key->publicExponent->copy();
}
$this->primes = array();
$this->exponents = array();
$this->coefficients = array();
foreach ($this->primes as $prime) {
$this->primes[] = $prime->copy();
}
foreach ($this->exponents as $exponent) {
$this->exponents[] = $exponent->copy();
}
foreach ($this->coefficients as $coefficient) {
$this->coefficients[] = $coefficient->copy();
}
return true;
}
if ($type === false) { if ($type === false) {
$types = array( $types = array(
CRYPT_RSA_PUBLIC_FORMAT_RAW, CRYPT_RSA_PUBLIC_FORMAT_RAW,
@ -1405,6 +1609,19 @@ class Crypt_RSA {
$this->publicExponent = false; $this->publicExponent = false;
} }
switch ($type) {
case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
case CRYPT_RSA_PUBLIC_FORMAT_RAW:
$this->setPublicKey();
break;
case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
switch (true) {
case strpos($key, '-BEGIN PUBLIC KEY-') !== false:
case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false:
$this->setPublicKey();
}
}
return true; return true;
} }
@ -1431,7 +1648,9 @@ class Crypt_RSA {
* used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a * used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a
* message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys * message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys
* and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public * and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public
* exponent this won't work unless you manually add the public exponent. * exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used
* is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being
* public.
* *
* Do note that when a new key is loaded the index will be cleared. * Do note that when a new key is loaded the index will be cleared.
* *
@ -1445,6 +1664,11 @@ class Crypt_RSA {
*/ */
function setPublicKey($key = false, $type = false) function setPublicKey($key = false, $type = false)
{ {
// if a public key has already been loaded return false
if (!empty($this->publicExponent)) {
return false;
}
if ($key === false && !empty($this->modulus)) { if ($key === false && !empty($this->modulus)) {
$this->publicExponent = $this->exponent; $this->publicExponent = $this->exponent;
return true; return true;
@ -1482,6 +1706,40 @@ class Crypt_RSA {
return true; return true;
} }
/**
* Defines the private key
*
* If phpseclib guessed a private key was a public key and loaded it as such it might be desirable to force
* phpseclib to treat the key as a private key. This function will do that.
*
* Do note that when a new key is loaded the index will be cleared.
*
* Returns true on success, false on failure
*
* @see getPublicKey()
* @access public
* @param String $key optional
* @param Integer $type optional
* @return Boolean
*/
function setPrivateKey($key = false, $type = false)
{
if ($key === false && !empty($this->publicExponent)) {
unset($this->publicExponent);
return true;
}
$rsa = new Crypt_RSA();
if (!$rsa->loadKey($key, $type)) {
return false;
}
unset($rsa->publicExponent);
// don't overwrite the old key if the new key is invalid
$this->loadKey($rsa);
return true;
}
/** /**
* Returns the public key * Returns the public key
* *
@ -1494,7 +1752,7 @@ class Crypt_RSA {
* @param String $key * @param String $key
* @param Integer $type optional * @param Integer $type optional
*/ */
function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1) function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS8)
{ {
if (empty($this->modulus) || empty($this->publicExponent)) { if (empty($this->modulus) || empty($this->publicExponent)) {
return false; return false;
@ -1541,7 +1799,7 @@ class Crypt_RSA {
* @param String $key * @param String $key
* @param Integer $type optional * @param Integer $type optional
*/ */
function _getPrivatePublicKey($mode = CRYPT_RSA_PUBLIC_FORMAT_PKCS1) function _getPrivatePublicKey($mode = CRYPT_RSA_PUBLIC_FORMAT_PKCS8)
{ {
if (empty($this->modulus) || empty($this->exponent)) { if (empty($this->modulus) || empty($this->exponent)) {
return false; return false;
@ -1569,6 +1827,18 @@ class Crypt_RSA {
return $key !== false ? $key : ''; return $key !== false ? $key : '';
} }
/**
* __clone() magic method
*
* @access public
*/
function __clone()
{
$key = new Crypt_RSA();
$key->loadKey($this);
return $key;
}
/** /**
* Generates the smallest and largest numbers requiring $bits bits * Generates the smallest and largest numbers requiring $bits bits
* *
@ -2184,11 +2454,11 @@ class Crypt_RSA {
* *
* See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}. * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
* *
* For compatability purposes, this function departs slightly from the description given in RFC3447. * For compatibility purposes, this function departs slightly from the description given in RFC3447.
* The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
* private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
* public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed * public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed
* to be 2 regardless of which key is used. For compatability purposes, we'll just check to make sure the * to be 2 regardless of which key is used. For compatibility purposes, we'll just check to make sure the
* second byte is 2 or less. If it is, we'll accept the decrypted string as valid. * second byte is 2 or less. If it is, we'll accept the decrypted string as valid.
* *
* As a consequence of this, a private key encrypted ciphertext produced with Crypt_RSA may not decrypt * As a consequence of this, a private key encrypted ciphertext produced with Crypt_RSA may not decrypt
@ -2690,4 +2960,31 @@ class Crypt_RSA {
return $this->_rsassa_pss_verify($message, $signature); return $this->_rsassa_pss_verify($message, $signature);
} }
} }
/**
* Extract raw BER from Base64 encoding
*
* @access private
* @param String $str
* @return String
*/
function _extractBER($str)
{
/* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
* above and beyond the ceritificate.
* ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
*
* Bag Attributes
* localKeyID: 01 00 00 00
* subject=/O=organization/OU=org unit/CN=common name
* issuer=/O=organization/CN=common name
*/
$temp = preg_replace('#.*?^-+[^-]+-+#ms', '', $str, 1);
// remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
$temp = preg_replace('#-+[^-]+-+#', '', $temp);
// remove new lines
$temp = str_replace(array("\r", "\n", ' '), '', $temp);
$temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
return $temp != false ? $temp : $str;
}
} }

View File

@ -1,15 +1,17 @@
<?php <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/** /**
* Random Number Generator * Random Number Generator
* *
* The idea behind this function is that it can be easily replaced with your own crypt_random_string()
* function. eg. maybe you have a better source of entropy for creating the initial states or whatever.
*
* PHP versions 4 and 5 * PHP versions 4 and 5
* *
* Here's a short example of how to use this library: * Here's a short example of how to use this library:
* <code> * <code>
* <?php * <?php
* include('Crypt/Random.php'); * include 'Crypt/Random.php';
* *
* echo bin2hex(crypt_random_string(8)); * echo bin2hex(crypt_random_string(8));
* ?> * ?>
@ -41,6 +43,10 @@
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
// laravel is a PHP framework that utilizes phpseclib. laravel workbenches may, independently,
// have phpseclib as a requirement as well. if you're developing such a program you may encounter
// a "Cannot redeclare crypt_random_string()" error.
if (!function_exists('crypt_random_string')) {
/** /**
* "Is Windows" test * "Is Windows" test
* *
@ -117,7 +123,7 @@ function crypt_random_string($length)
// easy to guess at. linux uses mouse clicks, keyboard timings, etc, as entropy sources, but // easy to guess at. linux uses mouse clicks, keyboard timings, etc, as entropy sources, but
// PHP isn't low level to be able to use those as sources and on a web server there's not likely // PHP isn't low level to be able to use those as sources and on a web server there's not likely
// going to be a ton of keyboard or mouse action. web servers do have one thing that we can use // going to be a ton of keyboard or mouse action. web servers do have one thing that we can use
// however. a ton of people visiting the website. obviously you don't want to base your seeding // however, a ton of people visiting the website. obviously you don't want to base your seeding
// soley on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled // soley on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled
// by the user and (2) this isn't just looking at the data sent by the current user - it's based // by the user and (2) this isn't just looking at the data sent by the current user - it's based
// on the data sent by all users. one user requests the page and a hash of their info is saved. // on the data sent by all users. one user requests the page and a hash of their info is saved.
@ -132,9 +138,7 @@ function crypt_random_string($length)
$old_session_id = session_id(); $old_session_id = session_id();
$old_use_cookies = ini_get('session.use_cookies'); $old_use_cookies = ini_get('session.use_cookies');
$old_session_cache_limiter = session_cache_limiter(); $old_session_cache_limiter = session_cache_limiter();
if (isset($_SESSION)) { $_OLD_SESSION = isset($_SESSION) ? $_SESSION : false;
$_OLD_SESSION = $_SESSION;
}
if ($old_session_id != '') { if ($old_session_id != '') {
session_write_close(); session_write_close();
} }
@ -167,7 +171,7 @@ function crypt_random_string($length)
ini_set('session.use_cookies', $old_use_cookies); ini_set('session.use_cookies', $old_use_cookies);
session_cache_limiter($old_session_cache_limiter); session_cache_limiter($old_session_cache_limiter);
} else { } else {
if (isset($_OLD_SESSION)) { if ($_OLD_SESSION !== false) {
$_SESSION = $_OLD_SESSION; $_SESSION = $_OLD_SESSION;
unset($_OLD_SESSION); unset($_OLD_SESSION);
} else { } else {
@ -190,21 +194,45 @@ function crypt_random_string($length)
// //
// http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives // http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives
switch (true) { switch (true) {
case class_exists('Crypt_AES'): case phpseclib_resolve_include_path('Crypt/AES.php'):
if (!class_exists('Crypt_AES')) {
include_once 'AES.php';
}
$crypto = new Crypt_AES(CRYPT_AES_MODE_CTR); $crypto = new Crypt_AES(CRYPT_AES_MODE_CTR);
break; break;
case class_exists('Crypt_TripleDES'): case phpseclib_resolve_include_path('Crypt/Twofish.php'):
if (!class_exists('Crypt_Twofish')) {
include_once 'Twofish.php';
}
$crypto = new Crypt_Twofish(CRYPT_TWOFISH_MODE_CTR);
break;
case phpseclib_resolve_include_path('Crypt/Blowfish.php'):
if (!class_exists('Crypt_Blowfish')) {
include_once 'Blowfish.php';
}
$crypto = new Crypt_Blowfish(CRYPT_BLOWFISH_MODE_CTR);
break;
case phpseclib_resolve_include_path('Crypt/TripleDES.php'):
if (!class_exists('Crypt_TripleDES')) {
include_once 'TripleDES.php';
}
$crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CTR); $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
break; break;
case class_exists('Crypt_DES'): case phpseclib_resolve_include_path('Crypt/DES.php'):
if (!class_exists('Crypt_DES')) {
include_once 'DES.php';
}
$crypto = new Crypt_DES(CRYPT_DES_MODE_CTR); $crypto = new Crypt_DES(CRYPT_DES_MODE_CTR);
break; break;
case class_exists('Crypt_RC4'): case phpseclib_resolve_include_path('Crypt/RC4.php'):
if (!class_exists('Crypt_RC4')) {
include_once 'RC4.php';
}
$crypto = new Crypt_RC4(); $crypto = new Crypt_RC4();
break; break;
default: default:
$crypto = $seed; user_error('crypt_random_string requires at least one symmetric cipher be loaded');
return crypt_random_string($length); return false;
} }
$crypto->setKey($key); $crypto->setKey($key);
@ -212,7 +240,8 @@ function crypt_random_string($length)
$crypto->enableContinuousBuffer(); $crypto->enableContinuousBuffer();
} }
if (is_string($crypto)) { //return $crypto->encrypt(str_repeat("\0", $length));
// the following is based off of ANSI X9.31: // the following is based off of ANSI X9.31:
// //
// http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf // http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf
@ -221,29 +250,51 @@ function crypt_random_string($length)
// //
// http://www.opensource.apple.com/source/OpenSSL/OpenSSL-38/openssl/fips-1.0/rand/fips_rand.c // http://www.opensource.apple.com/source/OpenSSL/OpenSSL-38/openssl/fips-1.0/rand/fips_rand.c
// (do a search for "ANS X9.31 A.2.4") // (do a search for "ANS X9.31 A.2.4")
//
// ANSI X9.31 recommends ciphers be used and phpseclib does use them if they're available (see
// later on in the code) but if they're not we'll use sha1
$result = '';
while (strlen($result) < $length) { // each loop adds 20 bytes
// microtime() isn't packed as "densely" as it could be but then neither is that the idea.
// the idea is simply to ensure that each "block" has a unique element to it.
$i = pack('H*', sha1(microtime()));
$r = pack('H*', sha1($i ^ $v));
$v = pack('H*', sha1($r ^ $i));
$result.= $r;
}
return substr($result, 0, $length);
}
//return $crypto->encrypt(str_repeat("\0", $length));
$result = ''; $result = '';
while (strlen($result) < $length) { while (strlen($result) < $length) {
$i = $crypto->encrypt(microtime()); $i = $crypto->encrypt(microtime()); // strlen(microtime()) == 21
$r = $crypto->encrypt($i ^ $v); $r = $crypto->encrypt($i ^ $v); // strlen($v) == 20
$v = $crypto->encrypt($r ^ $i); $v = $crypto->encrypt($r ^ $i); // strlen($r) == 20
$result.= $r; $result.= $r;
} }
return substr($result, 0, $length); return substr($result, 0, $length);
} }
}
if (!function_exists('phpseclib_resolve_include_path')) {
/**
* Resolve filename against the include path.
*
* Wrapper around stream_resolve_include_path() (which was introduced in
* PHP 5.3.2) with fallback implementation for earlier PHP versions.
*
* @param string $filename
* @return mixed Filename (string) on success, false otherwise.
* @access public
*/
function phpseclib_resolve_include_path($filename)
{
if (function_exists('stream_resolve_include_path')) {
return stream_resolve_include_path($filename);
}
// handle non-relative paths
if (file_exists($filename)) {
return realpath($filename);
}
$paths = PATH_SEPARATOR == ':' ?
preg_split('#(?<!phar):#', get_include_path()) :
explode(PATH_SEPARATOR, get_include_path());
foreach ($paths as $prefix) {
// path's specified in include_path don't always end in /
$ds = substr($prefix, -1) == DIRECTORY_SEPARATOR ? '' : DIRECTORY_SEPARATOR;
$file = $prefix . $ds . $filename;
if (file_exists($file)) {
return realpath($file);
}
}
return false;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,4 @@
<?php <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/** /**
* Pure-PHP implementation of Triple DES. * Pure-PHP implementation of Triple DES.
@ -11,7 +10,7 @@
* Here's a short example of how to use this library: * Here's a short example of how to use this library:
* <code> * <code>
* <?php * <?php
* include('Crypt/TripleDES.php'); * include 'Crypt/TripleDES.php';
* *
* $des = new Crypt_TripleDES(); * $des = new Crypt_TripleDES();
* *
@ -57,7 +56,7 @@
* Include Crypt_DES * Include Crypt_DES
*/ */
if (!class_exists('Crypt_DES')) { if (!class_exists('Crypt_DES')) {
require_once('DES.php'); include_once 'DES.php';
} }
/** /**
@ -77,15 +76,85 @@ define('CRYPT_DES_MODE_CBC3', CRYPT_DES_MODE_CBC);
/** /**
* Pure-PHP implementation of Triple DES. * Pure-PHP implementation of Triple DES.
* *
* @package Crypt_TripleDES
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @version 0.1.0
* @access public * @access public
* @package Crypt_TerraDES
*/ */
class Crypt_TripleDES extends Crypt_DES { class Crypt_TripleDES extends Crypt_DES
{
/**
* The default password key_size used by setPassword()
*
* @see Crypt_DES::password_key_size
* @see Crypt_Base::password_key_size
* @see Crypt_Base::setPassword()
* @var Integer
* @access private
*/
var $password_key_size = 24;
/**
* The default salt used by setPassword()
*
* @see Crypt_Base::password_default_salt
* @see Crypt_Base::setPassword()
* @var String
* @access private
*/
var $password_default_salt = 'phpseclib';
/**
* The namespace used by the cipher for its constants.
*
* @see Crypt_DES::const_namespace
* @see Crypt_Base::const_namespace
* @var String
* @access private
*/
var $const_namespace = 'DES';
/**
* The mcrypt specific name of the cipher
*
* @see Crypt_DES::cipher_name_mcrypt
* @see Crypt_Base::cipher_name_mcrypt
* @var String
* @access private
*/
var $cipher_name_mcrypt = 'tripledes';
/**
* Optimizing value while CFB-encrypting
*
* @see Crypt_Base::cfb_init_len
* @var Integer
* @access private
*/
var $cfb_init_len = 750;
/**
* max possible size of $key
*
* @see Crypt_TripleDES::setKey()
* @see Crypt_DES::setKey()
* @var String
* @access private
*/
var $key_size_max = 24;
/**
* Internal flag whether using CRYPT_DES_MODE_3CBC or not
*
* @var Boolean
* @access private
*/
var $mode_3cbc;
/** /**
* The Crypt_DES objects * The Crypt_DES objects
* *
* Used only if $mode_3cbc === true
*
* @var Array * @var Array
* @access private * @access private
*/ */
@ -94,99 +163,73 @@ class Crypt_TripleDES extends Crypt_DES {
/** /**
* Default Constructor. * Default Constructor.
* *
* Determines whether or not the mcrypt extension should be used. $mode should only, at present, be * Determines whether or not the mcrypt extension should be used.
* CRYPT_DES_MODE_ECB or CRYPT_DES_MODE_CBC. If not explictly set, CRYPT_DES_MODE_CBC will be used.
* *
* $mode could be:
*
* - CRYPT_DES_MODE_ECB
*
* - CRYPT_DES_MODE_CBC
*
* - CRYPT_DES_MODE_CTR
*
* - CRYPT_DES_MODE_CFB
*
* - CRYPT_DES_MODE_OFB
*
* - CRYPT_DES_MODE_3CBC
*
* If not explicitly set, CRYPT_DES_MODE_CBC will be used.
*
* @see Crypt_DES::Crypt_DES()
* @see Crypt_Base::Crypt_Base()
* @param optional Integer $mode * @param optional Integer $mode
* @return Crypt_TripleDES
* @access public * @access public
*/ */
function Crypt_TripleDES($mode = CRYPT_DES_MODE_CBC) function Crypt_TripleDES($mode = CRYPT_DES_MODE_CBC)
{ {
if ( !defined('CRYPT_DES_MODE') ) {
switch (true) {
case extension_loaded('mcrypt') && in_array('tripledes', mcrypt_list_algorithms()):
define('CRYPT_DES_MODE', CRYPT_DES_MODE_MCRYPT);
break;
default:
define('CRYPT_DES_MODE', CRYPT_DES_MODE_INTERNAL);
}
}
if ( $mode == CRYPT_DES_MODE_3CBC ) {
$this->mode = CRYPT_DES_MODE_3CBC;
$this->des = array(
new Crypt_DES(CRYPT_DES_MODE_CBC),
new Crypt_DES(CRYPT_DES_MODE_CBC),
new Crypt_DES(CRYPT_DES_MODE_CBC)
);
$this->paddable = true;
// we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects
$this->des[0]->disablePadding();
$this->des[1]->disablePadding();
$this->des[2]->disablePadding();
return;
}
switch ( CRYPT_DES_MODE ) {
case CRYPT_DES_MODE_MCRYPT:
switch ($mode) { switch ($mode) {
case CRYPT_DES_MODE_ECB: // In case of CRYPT_DES_MODE_3CBC, we init as CRYPT_DES_MODE_CBC
$this->paddable = true; // and additional flag us internally as 3CBC
$this->mode = MCRYPT_MODE_ECB; case CRYPT_DES_MODE_3CBC:
break; parent::Crypt_Base(CRYPT_DES_MODE_CBC);
case CRYPT_DES_MODE_CTR: $this->mode_3cbc = true;
$this->mode = 'ctr';
break;
case CRYPT_DES_MODE_CFB:
$this->mode = 'ncfb';
$this->ecb = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, '');
break;
case CRYPT_DES_MODE_OFB:
$this->mode = MCRYPT_MODE_NOFB;
break;
case CRYPT_DES_MODE_CBC:
default:
$this->paddable = true;
$this->mode = MCRYPT_MODE_CBC;
}
$this->enmcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
$this->demcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
break; // This three $des'es will do the 3CBC work (if $key > 64bits)
default:
$this->des = array( $this->des = array(
new Crypt_DES(CRYPT_DES_MODE_ECB), new Crypt_DES(CRYPT_DES_MODE_CBC),
new Crypt_DES(CRYPT_DES_MODE_ECB), new Crypt_DES(CRYPT_DES_MODE_CBC),
new Crypt_DES(CRYPT_DES_MODE_ECB) new Crypt_DES(CRYPT_DES_MODE_CBC),
); );
// we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects // we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects
$this->des[0]->disablePadding(); $this->des[0]->disablePadding();
$this->des[1]->disablePadding(); $this->des[1]->disablePadding();
$this->des[2]->disablePadding(); $this->des[2]->disablePadding();
switch ($mode) {
case CRYPT_DES_MODE_ECB:
case CRYPT_DES_MODE_CBC:
$this->paddable = true;
$this->mode = $mode;
break;
case CRYPT_DES_MODE_CTR:
case CRYPT_DES_MODE_CFB:
case CRYPT_DES_MODE_OFB:
$this->mode = $mode;
break; break;
// If not 3CBC, we init as usual
default: default:
$this->paddable = true; parent::Crypt_Base($mode);
$this->mode = CRYPT_DES_MODE_CBC;
} }
if (function_exists('create_function') && is_callable('create_function')) {
$this->inline_crypt_setup(3);
$this->use_inline_crypt = true;
} }
/**
* Sets the initialization vector. (optional)
*
* SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explicitly set, it'll be assumed
* to be all zero's.
*
* @see Crypt_Base::setIV()
* @access public
* @param String $iv
*/
function setIV($iv)
{
parent::setIV($iv);
if ($this->mode_3cbc) {
$this->des[0]->setIV($iv);
$this->des[1]->setIV($iv);
$this->des[2]->setIV($iv);
} }
} }
@ -198,571 +241,87 @@ class Crypt_TripleDES extends Crypt_DES {
* *
* DES also requires that every eighth bit be a parity bit, however, we'll ignore that. * DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
* *
* If the key is not explicitly set, it'll be assumed to be all zero's. * If the key is not explicitly set, it'll be assumed to be all null bytes.
* *
* @access public * @access public
* @see Crypt_DES::setKey()
* @see Crypt_Base::setKey()
* @param String $key * @param String $key
*/ */
function setKey($key) function setKey($key)
{ {
$length = strlen($key); $length = strlen($key);
if ($length > 8) { if ($length > 8) {
$key = str_pad($key, 24, chr(0)); $key = str_pad(substr($key, 0, 24), 24, chr(0));
// if $key is between 64 and 128-bits, use the first 64-bits as the last, per this: // if $key is between 64 and 128-bits, use the first 64-bits as the last, per this:
// http://php.net/function.mcrypt-encrypt#47973 // http://php.net/function.mcrypt-encrypt#47973
//$key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24); //$key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24);
} else { } else {
$key = str_pad($key, 8, chr(0)); $key = str_pad($key, 8, chr(0));
} }
$this->key = $key; parent::setKey($key);
switch (true) {
case CRYPT_DES_MODE == CRYPT_DES_MODE_INTERNAL: // And in case of CRYPT_DES_MODE_3CBC:
case $this->mode == CRYPT_DES_MODE_3CBC: // if key <= 64bits we not need the 3 $des to work,
// because we will then act as regular DES-CBC with just a <= 64bit key.
// So only if the key > 64bits (> 8 bytes) we will call setKey() for the 3 $des.
if ($this->mode_3cbc && $length > 8) {
$this->des[0]->setKey(substr($key, 0, 8)); $this->des[0]->setKey(substr($key, 0, 8));
$this->des[1]->setKey(substr($key, 8, 8)); $this->des[1]->setKey(substr($key, 8, 8));
$this->des[2]->setKey(substr($key, 16, 8)); $this->des[2]->setKey(substr($key, 16, 8));
// Merge the three DES-1-dim-key-arrays for 3DES-inline-en/decrypting
if ($this->use_inline_crypt && $this->mode != CRYPT_DES_MODE_3CBC) {
$this->keys = array(
CRYPT_DES_ENCRYPT_1DIM => array_merge(
$this->des[0]->keys[CRYPT_DES_ENCRYPT_1DIM],
$this->des[1]->keys[CRYPT_DES_DECRYPT_1DIM],
$this->des[2]->keys[CRYPT_DES_ENCRYPT_1DIM]
),
CRYPT_DES_DECRYPT_1DIM => array_merge(
$this->des[2]->keys[CRYPT_DES_DECRYPT_1DIM],
$this->des[1]->keys[CRYPT_DES_ENCRYPT_1DIM],
$this->des[0]->keys[CRYPT_DES_DECRYPT_1DIM]
),
);
} }
} }
$this->enchanged = $this->dechanged = true;
}
/**
* Sets the password.
*
* Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
* {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}:
* $hash, $salt, $method
*
* @param String $password
* @param optional String $method
* @access public
*/
function setPassword($password, $method = 'pbkdf2')
{
$key = '';
switch ($method) {
default: // 'pbkdf2'
list(, , $hash, $salt, $count) = func_get_args();
if (!isset($hash)) {
$hash = 'sha1';
}
// WPA and WPA2 use the SSID as the salt
if (!isset($salt)) {
$salt = 'phpseclib';
}
// RFC2898#section-4.2 uses 1,000 iterations by default
// WPA and WPA2 use 4,096.
if (!isset($count)) {
$count = 1000;
}
if (!class_exists('Crypt_Hash')) {
require_once('Crypt/Hash.php');
}
$i = 1;
while (strlen($key) < 24) { // $dkLen == 24
$hmac = new Crypt_Hash();
$hmac->setHash($hash);
$hmac->setKey($password);
$f = $u = $hmac->hash($salt . pack('N', $i++));
for ($j = 2; $j <= $count; $j++) {
$u = $hmac->hash($u);
$f^= $u;
}
$key.= $f;
}
}
$this->setKey($key);
}
/**
* Sets the initialization vector. (optional)
*
* SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explictly set, it'll be assumed
* to be all zero's.
*
* @access public
* @param String $iv
*/
function setIV($iv)
{
$this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0));
if ($this->mode == CRYPT_DES_MODE_3CBC) {
$this->des[0]->setIV($iv);
$this->des[1]->setIV($iv);
$this->des[2]->setIV($iv);
}
$this->enchanged = $this->dechanged = true;
}
/** /**
* Encrypts a message. * Encrypts a message.
* *
* @see Crypt_Base::encrypt()
* @access public * @access public
* @param String $plaintext * @param String $plaintext
* @return String $cipertext
*/ */
function encrypt($plaintext) function encrypt($plaintext)
{ {
if ($this->paddable) { // parent::en/decrypt() is able to do all the work for all modes and keylengths,
$plaintext = $this->_pad($plaintext); // except for: CRYPT_DES_MODE_3CBC (inner chaining CBC) with a key > 64bits
}
// if the key is smaller then 8, do what we'd normally do // if the key is smaller then 8, do what we'd normally do
if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) { if ($this->mode_3cbc && strlen($this->key) > 8) {
$ciphertext = $this->des[2]->encrypt($this->des[1]->decrypt($this->des[0]->encrypt($plaintext))); return $this->des[2]->encrypt(
$this->des[1]->decrypt(
return $ciphertext; $this->des[0]->encrypt(
} $this->_pad($plaintext)
)
if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { )
if ($this->enchanged) {
mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
if ($this->mode == 'ncfb') {
mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0");
}
$this->enchanged = false;
}
if ($this->mode != 'ncfb' || !$this->continuousBuffer) {
$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
} else {
$iv = &$this->encryptIV;
$pos = &$this->enbuffer['pos'];
$len = strlen($plaintext);
$ciphertext = '';
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = 8 - $pos;
if ($len >= $max) {
$i = $max;
$len-= $max;
$pos = 0;
} else {
$i = $len;
$pos+= $len;
$len = 0;
}
$ciphertext = substr($iv, $orig_pos) ^ $plaintext;
$iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
$this->enbuffer['enmcrypt_init'] = true;
}
if ($len >= 8) {
if ($this->enbuffer['enmcrypt_init'] === false || $len > 950) {
if ($this->enbuffer['enmcrypt_init'] === true) {
mcrypt_generic_init($this->enmcrypt, $this->key, $iv);
$this->enbuffer['enmcrypt_init'] = false;
}
$ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % 8));
$iv = substr($ciphertext, -8);
$i = strlen($ciphertext);
$len%= 8;
} else {
while ($len >= 8) {
$iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, 8);
$ciphertext.= $iv;
$len-= 8;
$i+= 8;
}
}
}
if ($len) {
$iv = mcrypt_generic($this->ecb, $iv);
$block = $iv ^ substr($plaintext, $i);
$iv = substr_replace($iv, $block, 0, $len);
$ciphertext.= $block;
$pos = $len;
}
return $ciphertext;
}
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
}
return $ciphertext;
}
if (strlen($this->key) <= 8) {
$this->des[0]->mode = $this->mode;
return $this->des[0]->encrypt($plaintext);
}
if ($this->use_inline_crypt) {
$inline = $this->inline_crypt;
return $inline('encrypt', $this, $plaintext);
}
$des = $this->des;
$buffer = &$this->enbuffer;
$continuousBuffer = $this->continuousBuffer;
$ciphertext = '';
switch ($this->mode) {
case CRYPT_DES_MODE_ECB:
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$block = substr($plaintext, $i, 8);
// all of these _processBlock calls could, in theory, be put in a function - say Crypt_TripleDES::_ede_encrypt() or something.
// only problem with that: it would slow encryption and decryption down. $this->des would have to be called every time that
// function is called, instead of once for the whole string of text that's being encrypted, which would, in turn, make
// encryption and decryption take more time, per this:
//
// http://blog.libssh2.org/index.php?/archives/21-Compiled-Variables.html
$block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
$block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT);
$block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT);
$ciphertext.= $block;
}
break;
case CRYPT_DES_MODE_CBC:
$xor = $this->encryptIV;
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$block = substr($plaintext, $i, 8) ^ $xor;
$block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
$block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT);
$block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT);
$xor = $block;
$ciphertext.= $block;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
}
break;
case CRYPT_DES_MODE_CTR:
$xor = $this->encryptIV;
if (strlen($buffer['encrypted'])) {
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$block = substr($plaintext, $i, 8);
if (strlen($block) > strlen($buffer['encrypted'])) {
$key = $this->_generate_xor($xor);
$key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
$key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
$key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
$buffer['encrypted'].= $key;
}
$key = $this->_string_shift($buffer['encrypted']);
$ciphertext.= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$block = substr($plaintext, $i, 8);
$key = $this->_generate_xor($xor);
$key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
$key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
$key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
$ciphertext.= $block ^ $key;
}
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
if ($start = strlen($plaintext) & 7) {
$buffer['encrypted'] = substr($key, $start) . $buffer['encrypted'];
}
}
break;
case CRYPT_DES_MODE_CFB:
if (strlen($buffer['xor'])) {
$ciphertext = $plaintext ^ $buffer['xor'];
$iv = $buffer['encrypted'] . $ciphertext;
$start = strlen($ciphertext);
$buffer['encrypted'].= $ciphertext;
$buffer['xor'] = substr($buffer['xor'], strlen($ciphertext));
} else {
$ciphertext = '';
$iv = $this->encryptIV;
$start = 0;
}
for ($i = $start; $i < strlen($plaintext); $i+=8) {
$block = substr($plaintext, $i, 8);
$iv = $des[0]->_processBlock($iv, CRYPT_DES_ENCRYPT);
$iv = $des[1]->_processBlock($iv, CRYPT_DES_DECRYPT);
$xor= $des[2]->_processBlock($iv, CRYPT_DES_ENCRYPT);
$iv = $block ^ $xor;
if ($continuousBuffer && strlen($iv) != 8) {
$buffer = array(
'encrypted' => $iv,
'xor' => substr($xor, strlen($iv))
); );
} }
$ciphertext.= $iv;
}
if ($this->continuousBuffer) { return parent::encrypt($plaintext);
$this->encryptIV = $iv;
}
break;
case CRYPT_DES_MODE_OFB:
$xor = $this->encryptIV;
if (strlen($buffer['xor'])) {
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$block = substr($plaintext, $i, 8);
if (strlen($block) > strlen($buffer['xor'])) {
$xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
$xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$buffer['xor'].= $xor;
}
$key = $this->_string_shift($buffer['xor']);
$ciphertext.= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
$xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$ciphertext.= substr($plaintext, $i, 8) ^ $xor;
}
$key = $xor;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
if ($start = strlen($plaintext) & 7) {
$buffer['xor'] = substr($key, $start) . $buffer['xor'];
}
}
}
return $ciphertext;
} }
/** /**
* Decrypts a message. * Decrypts a message.
* *
* @see Crypt_Base::decrypt()
* @access public * @access public
* @param String $ciphertext * @param String $ciphertext
* @return String $plaintext
*/ */
function decrypt($ciphertext) function decrypt($ciphertext)
{ {
if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) { if ($this->mode_3cbc && strlen($this->key) > 8) {
$plaintext = $this->des[0]->decrypt($this->des[1]->encrypt($this->des[2]->decrypt($ciphertext))); return $this->_unpad(
$this->des[0]->decrypt(
return $this->_unpad($plaintext); $this->des[1]->encrypt(
$this->des[2]->decrypt(
str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, "\0")
)
)
)
);
} }
if ($this->paddable) { return parent::decrypt($ciphertext);
// we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
// "The data is padded with "\0" to make sure the length of the data is n * blocksize."
$ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0));
}
if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
if ($this->dechanged) {
mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
if ($this->mode == 'ncfb') {
mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0");
}
$this->dechanged = false;
}
if ($this->mode != 'ncfb' || !$this->continuousBuffer) {
$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
} else {
$iv = &$this->decryptIV;
$pos = &$this->debuffer['pos'];
$len = strlen($ciphertext);
$plaintext = '';
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = 8 - $pos;
if ($len >= $max) {
$i = $max;
$len-= $max;
$pos = 0;
} else {
$i = $len;
$pos+= $len;
$len = 0;
}
$plaintext = substr($iv, $orig_pos) ^ $ciphertext;
$iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
}
if ($len >= 8) {
$cb = substr($ciphertext, $i, $len - $len % 8);
$plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb;
$iv = substr($cb, -8);
$len%= 8;
}
if ($len) {
$iv = mcrypt_generic($this->ecb, $iv);
$cb = substr($ciphertext, -$len);
$plaintext.= $iv ^ $cb;
$iv = substr_replace($iv, $cb, 0, $len);
$pos = $len;
}
return $plaintext;
}
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
}
return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
}
if (strlen($this->key) <= 8) {
$this->des[0]->mode = $this->mode;
$plaintext = $this->des[0]->decrypt($ciphertext);
return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
}
if ($this->use_inline_crypt) {
$inline = $this->inline_crypt;
return $inline('decrypt', $this, $ciphertext);
}
$des = $this->des;
$buffer = &$this->debuffer;
$continuousBuffer = $this->continuousBuffer;
$plaintext = '';
switch ($this->mode) {
case CRYPT_DES_MODE_ECB:
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$block = substr($ciphertext, $i, 8);
$block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT);
$block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT);
$block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT);
$plaintext.= $block;
}
break;
case CRYPT_DES_MODE_CBC:
$xor = $this->decryptIV;
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$orig = $block = substr($ciphertext, $i, 8);
$block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT);
$block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT);
$block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT);
$plaintext.= $block ^ $xor;
$xor = $orig;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
}
break;
case CRYPT_DES_MODE_CTR:
$xor = $this->decryptIV;
if (strlen($buffer['ciphertext'])) {
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$block = substr($ciphertext, $i, 8);
if (strlen($block) > strlen($buffer['ciphertext'])) {
$key = $this->_generate_xor($xor);
$key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
$key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
$key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
$buffer['ciphertext'].= $key;
}
$key = $this->_string_shift($buffer['ciphertext']);
$plaintext.= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$block = substr($ciphertext, $i, 8);
$key = $this->_generate_xor($xor);
$key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
$key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
$key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
$plaintext.= $block ^ $key;
}
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
if ($start = strlen($plaintext) & 7) {
$buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
}
}
break;
case CRYPT_DES_MODE_CFB:
if (strlen($buffer['ciphertext'])) {
$plaintext = $ciphertext ^ substr($this->decryptIV, strlen($buffer['ciphertext']));
$buffer['ciphertext'].= substr($ciphertext, 0, strlen($plaintext));
if (strlen($buffer['ciphertext']) != 8) {
$block = $this->decryptIV;
} else {
$block = $buffer['ciphertext'];
$xor = $des[0]->_processBlock($buffer['ciphertext'], CRYPT_DES_ENCRYPT);
$xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
$xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$buffer['ciphertext'] = '';
}
$start = strlen($plaintext);
} else {
$plaintext = '';
$xor = $des[0]->_processBlock($this->decryptIV, CRYPT_DES_ENCRYPT);
$xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
$xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$start = 0;
}
for ($i = $start; $i < strlen($ciphertext); $i+=8) {
$block = substr($ciphertext, $i, 8);
$plaintext.= $block ^ $xor;
if ($continuousBuffer && strlen($block) != 8) {
$buffer['ciphertext'].= $block;
$block = $xor;
} else if (strlen($block) == 8) {
$xor = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
$xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
$xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
}
}
if ($this->continuousBuffer) {
$this->decryptIV = $block;
}
break;
case CRYPT_DES_MODE_OFB:
$xor = $this->decryptIV;
if (strlen($buffer['xor'])) {
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$block = substr($ciphertext, $i, 8);
if (strlen($block) > strlen($buffer['xor'])) {
$xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
$xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$buffer['xor'].= $xor;
}
$key = $this->_string_shift($buffer['xor']);
$plaintext.= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
$xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$plaintext.= substr($ciphertext, $i, 8) ^ $xor;
}
$key = $xor;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
if ($start = strlen($ciphertext) & 7) {
$buffer['xor'] = substr($key, $start) . $buffer['xor'];
}
}
}
return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
} }
/** /**
@ -799,13 +358,14 @@ class Crypt_TripleDES extends Crypt_DES {
* continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
* however, they are also less intuitive and more likely to cause you problems. * however, they are also less intuitive and more likely to cause you problems.
* *
* @see Crypt_Base::enableContinuousBuffer()
* @see Crypt_TripleDES::disableContinuousBuffer() * @see Crypt_TripleDES::disableContinuousBuffer()
* @access public * @access public
*/ */
function enableContinuousBuffer() function enableContinuousBuffer()
{ {
$this->continuousBuffer = true; parent::enableContinuousBuffer();
if ($this->mode == CRYPT_DES_MODE_3CBC) { if ($this->mode_3cbc) {
$this->des[0]->enableContinuousBuffer(); $this->des[0]->enableContinuousBuffer();
$this->des[1]->enableContinuousBuffer(); $this->des[1]->enableContinuousBuffer();
$this->des[2]->enableContinuousBuffer(); $this->des[2]->enableContinuousBuffer();
@ -817,26 +377,52 @@ class Crypt_TripleDES extends Crypt_DES {
* *
* The default behavior. * The default behavior.
* *
* @see Crypt_Base::disableContinuousBuffer()
* @see Crypt_TripleDES::enableContinuousBuffer() * @see Crypt_TripleDES::enableContinuousBuffer()
* @access public * @access public
*/ */
function disableContinuousBuffer() function disableContinuousBuffer()
{ {
$this->continuousBuffer = false; parent::disableContinuousBuffer();
$this->encryptIV = $this->iv; if ($this->mode_3cbc) {
$this->decryptIV = $this->iv;
$this->enchanged = true;
$this->dechanged = true;
$this->enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true);
$this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true);
if ($this->mode == CRYPT_DES_MODE_3CBC) {
$this->des[0]->disableContinuousBuffer(); $this->des[0]->disableContinuousBuffer();
$this->des[1]->disableContinuousBuffer(); $this->des[1]->disableContinuousBuffer();
$this->des[2]->disableContinuousBuffer(); $this->des[2]->disableContinuousBuffer();
} }
} }
}
// vim: ts=4:sw=4:et: /**
// vim6: fdl=1: * Creates the key schedule
*
* @see Crypt_DES::_setupKey()
* @see Crypt_Base::_setupKey()
* @access private
*/
function _setupKey()
{
switch (true) {
// if $key <= 64bits we configure our internal pure-php cipher engine
// to act as regular [1]DES, not as 3DES. mcrypt.so::tripledes does the same.
case strlen($this->key) <= 8:
$this->des_rounds = 1;
break;
// otherwise, if $key > 64bits, we configure our engine to work as 3DES.
default:
$this->des_rounds = 3;
// (only) if 3CBC is used we have, of course, to setup the $des[0-2] keys also separately.
if ($this->mode_3cbc) {
$this->des[0]->_setupKey();
$this->des[1]->_setupKey();
$this->des[2]->_setupKey();
// because $des[0-2] will, now, do all the work we can return here
// not need unnecessary stress parent::_setupKey() with our, now unused, $key.
return;
}
}
// setup our key
parent::_setupKey();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,4 @@
<?php <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/** /**
* Pure-PHP ANSI Decoder * Pure-PHP ANSI Decoder
@ -40,12 +39,12 @@
/** /**
* Pure-PHP ANSI Decoder * Pure-PHP ANSI Decoder
* *
* @author Jim Wigginton <terrafrost@php.net>
* @version 0.3.0
* @access public
* @package File_ANSI * @package File_ANSI
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/ */
class File_ANSI { class File_ANSI
{
/** /**
* Max Width * Max Width
* *
@ -351,7 +350,7 @@ class File_ANSI {
$this->attrs[$this->y][$this->x] = ''; $this->attrs[$this->y][$this->x] = '';
if ($this->bold) $this->attrs[$this->y][$this->x].= '</b>'; if ($this->bold) $this->attrs[$this->y][$this->x].= '</b>';
if ($this->underline) $this->attrs[$this->y][$this->x].= '</underline>'; if ($this->underline) $this->attrs[$this->y][$this->x].= '</u>';
if ($this->blink) $this->attrs[$this->y][$this->x].= '</blink>'; if ($this->blink) $this->attrs[$this->y][$this->x].= '</blink>';
if ($this->color) $this->attrs[$this->y][$this->x].= '</span>'; if ($this->color) $this->attrs[$this->y][$this->x].= '</span>';

View File

@ -1,5 +1,4 @@
<?php <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/** /**
* Pure-PHP ASN.1 Parser * Pure-PHP ASN.1 Parser
@ -111,12 +110,12 @@ define('FILE_ASN1_TYPE_ANY', -2);
* *
* Bypass normal encoding rules in File_ASN1::encodeDER() * Bypass normal encoding rules in File_ASN1::encodeDER()
* *
* @author Jim Wigginton <terrafrost@php.net>
* @version 0.3.0
* @access public
* @package File_ASN1 * @package File_ASN1
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/ */
class File_ASN1_Element { class File_ASN1_Element
{
/** /**
* Raw element value * Raw element value
* *
@ -141,12 +140,12 @@ class File_ASN1_Element {
/** /**
* Pure-PHP ASN.1 Parser * Pure-PHP ASN.1 Parser
* *
* @author Jim Wigginton <terrafrost@php.net>
* @version 0.3.0
* @access public
* @package File_ASN1 * @package File_ASN1
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/ */
class File_ASN1 { class File_ASN1
{
/** /**
* ASN.1 object identifier * ASN.1 object identifier
* *
@ -163,7 +162,7 @@ class File_ASN1 {
* @access private * @access private
* @link http://php.net/class.datetime * @link http://php.net/class.datetime
*/ */
var $format = 'D, d M y H:i:s O'; var $format = 'D, d M Y H:i:s O';
/** /**
* Default date format * Default date format
@ -252,7 +251,7 @@ class File_ASN1 {
if (!$static_init) { if (!$static_init) {
$static_init = true; $static_init = true;
if (!class_exists('Math_BigInteger')) { if (!class_exists('Math_BigInteger')) {
require_once('Math/BigInteger.php'); include_once 'Math/BigInteger.php';
} }
} }
} }
@ -273,7 +272,8 @@ class File_ASN1 {
} }
$this->encoded = $encoded; $this->encoded = $encoded;
return $this->_decode_ber($encoded); // encapsulate in an array for BC with the old decodeBER
return array($this->_decode_ber($encoded));
} }
/** /**
@ -288,11 +288,8 @@ class File_ASN1 {
* @return Array * @return Array
* @access private * @access private
*/ */
function _decode_ber(&$encoded, $start = 0) function _decode_ber($encoded, $start = 0)
{ {
$decoded = array();
while ( strlen($encoded) ) {
$current = array('start' => $start); $current = array('start' => $start);
$type = ord($this->_string_shift($encoded)); $type = ord($this->_string_shift($encoded));
@ -318,16 +315,13 @@ class File_ASN1 {
if ( $length == 0x80 ) { // indefinite length if ( $length == 0x80 ) { // indefinite length
// "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all // "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all
// immediately available." -- paragraph 8.1.3.2.c // immediately available." -- paragraph 8.1.3.2.c
//if ( !$constructed ) {
// return false;
//}
$length = strlen($encoded); $length = strlen($encoded);
} elseif ( $length & 0x80 ) { // definite length, long form } elseif ( $length & 0x80 ) { // definite length, long form
// technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only // technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only
// support it up to four. // support it up to four.
$length&= 0x7F; $length&= 0x7F;
$temp = $this->_string_shift($encoded, $length); $temp = $this->_string_shift($encoded, $length);
// tags of indefinite length don't really have a header length; this length includes the tag // tags of indefinte length don't really have a header length; this length includes the tag
$current+= array('headerlength' => $length + 2); $current+= array('headerlength' => $length + 2);
$start+= $length; $start+= $length;
extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4))); extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)));
@ -335,12 +329,10 @@ class File_ASN1 {
$current+= array('headerlength' => 2); $current+= array('headerlength' => 2);
} }
// End-of-content, see paragraphs 8.1.1.3, 8.1.3.2, 8.1.3.6, 8.1.5, and (for an example) 8.6.4.2
if (!$type && !$length) {
return $decoded;
}
$content = $this->_string_shift($encoded, $length); $content = $this->_string_shift($encoded, $length);
// at this point $length can be overwritten. it's only accurate for definite length things as is
/* Class is UNIVERSAL, APPLICATION, PRIVATE, or CONTEXT-SPECIFIC. The UNIVERSAL class is restricted to the ASN.1 /* Class is UNIVERSAL, APPLICATION, PRIVATE, or CONTEXT-SPECIFIC. The UNIVERSAL class is restricted to the ASN.1
built-in types. It defines an application-independent data type that must be distinguishable from all other built-in types. It defines an application-independent data type that must be distinguishable from all other
data types. The other three classes are user defined. The APPLICATION class distinguishes data types that data types. The other three classes are user defined. The APPLICATION class distinguishes data types that
@ -355,14 +347,24 @@ class File_ASN1 {
case FILE_ASN1_CLASS_APPLICATION: case FILE_ASN1_CLASS_APPLICATION:
case FILE_ASN1_CLASS_PRIVATE: case FILE_ASN1_CLASS_PRIVATE:
case FILE_ASN1_CLASS_CONTEXT_SPECIFIC: case FILE_ASN1_CLASS_CONTEXT_SPECIFIC:
$decoded[] = array( $newcontent = $this->_decode_ber($content, $start);
$length = $newcontent['length'];
if (substr($content, $length, 2) == "\0\0") {
$length+= 2;
}
$start+= $length;
return array(
'type' => $class, 'type' => $class,
'constant' => $tag, 'constant' => $tag,
'content' => $constructed ? $this->_decode_ber($content, $start) : $content, // the array encapsulation is for BC with the old format
'length' => $length + $start - $current['start'] 'content' => array($newcontent),
// the only time when $content['headerlength'] isn't defined is when the length is indefinite.
// the absence of $content['headerlength'] is how we know if something is indefinite or not.
// technically, it could be defined to be 2 and then another indicator could be used but whatever.
'length' => $start - $current['start']
) + $current; ) + $current;
$start+= $length;
continue 2;
} }
$current+= array('type' => $tag); $current+= array('type' => $tag);
@ -410,16 +412,21 @@ class File_ASN1 {
if (!$constructed) { if (!$constructed) {
$current['content'] = $content; $current['content'] = $content;
} else { } else {
$temp = $this->_decode_ber($content, $start); $current['content'] = '';
$length-= strlen($content); $length = 0;
for ($i = 0, $size = count($temp); $i < $size; $i++) { while (substr($content, 0, 2) != "\0\0") {
$temp = $this->_decode_ber($content, $length + $start);
$this->_string_shift($content, $temp['length']);
// all subtags should be octet strings // all subtags should be octet strings
//if ($temp[$i]['type'] != FILE_ASN1_TYPE_OCTET_STRING) { //if ($temp['type'] != FILE_ASN1_TYPE_OCTET_STRING) {
// return false; // return false;
//} //}
$current['content'].= $temp[$i]['content']; $current['content'].= $temp['content'];
$length+= $temp['length'];
}
if (substr($content, 0, 2) == "\0\0") {
$length+= 2; // +2 for the EOC
} }
// $length =
} }
break; break;
case FILE_ASN1_TYPE_NULL: case FILE_ASN1_TYPE_NULL:
@ -430,7 +437,20 @@ class File_ASN1 {
break; break;
case FILE_ASN1_TYPE_SEQUENCE: case FILE_ASN1_TYPE_SEQUENCE:
case FILE_ASN1_TYPE_SET: case FILE_ASN1_TYPE_SET:
$current['content'] = $this->_decode_ber($content, $start); $offset = 0;
$current['content'] = array();
while (strlen($content)) {
// if indefinite length construction was used and we have an end-of-content string next
// see paragraphs 8.1.1.3, 8.1.3.2, 8.1.3.6, 8.1.5, and (for an example) 8.6.4.2
if (!isset($current['headerlength']) && substr($content, 0, 2) == "\0\0") {
$length = $offset + 2; // +2 for the EOC
break 2;
}
$temp = $this->_decode_ber($content, $start + $offset);
$this->_string_shift($content, $temp['length']);
$current['content'][] = $temp;
$offset+= $temp['length'];
}
break; break;
case FILE_ASN1_TYPE_OBJECT_IDENTIFIER: case FILE_ASN1_TYPE_OBJECT_IDENTIFIER:
$temp = ord($this->_string_shift($content)); $temp = ord($this->_string_shift($content));
@ -485,29 +505,30 @@ class File_ASN1 {
case FILE_ASN1_TYPE_GENERALIZED_TIME: case FILE_ASN1_TYPE_GENERALIZED_TIME:
$current['content'] = $this->_decodeTime($content, $tag); $current['content'] = $this->_decodeTime($content, $tag);
default: default:
} }
$start+= $length; $start+= $length;
$decoded[] = $current + array('length' => $start - $current['start']);
}
return $decoded; // ie. length is the length of the full TLV encoding - it's not just the length of the value
return $current + array('length' => $start - $current['start']);
} }
/** /**
* ASN.1 Decode * ASN.1 Map
* *
* Provides an ASN.1 semantic mapping ($mapping) from a parsed BER-encoding to a human readable format. * Provides an ASN.1 semantic mapping ($mapping) from a parsed BER-encoding to a human readable format.
* *
* "Special" mappings may be applied on a per tag-name basis via $special.
*
* @param Array $decoded * @param Array $decoded
* @param Array $mapping * @param Array $mapping
* @param Array $special
* @return Array * @return Array
* @access public * @access public
*/ */
function asn1map($decoded, $mapping) function asn1map($decoded, $mapping, $special = array())
{ {
if (isset($mapping['explicit'])) { if (isset($mapping['explicit']) && is_array($decoded['content'])) {
$decoded = $decoded['content'][0]; $decoded = $decoded['content'][0];
} }
@ -519,7 +540,7 @@ class File_ASN1 {
} }
$inmap = $this->ANYmap[$intype]; $inmap = $this->ANYmap[$intype];
if (is_string($inmap)) { if (is_string($inmap)) {
return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping)); return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping, $special));
} }
break; break;
case $mapping['type'] == FILE_ASN1_TYPE_CHOICE: case $mapping['type'] == FILE_ASN1_TYPE_CHOICE:
@ -527,25 +548,36 @@ class File_ASN1 {
switch (true) { switch (true) {
case isset($option['constant']) && $option['constant'] == $decoded['constant']: case isset($option['constant']) && $option['constant'] == $decoded['constant']:
case !isset($option['constant']) && $option['type'] == $decoded['type']: case !isset($option['constant']) && $option['type'] == $decoded['type']:
$value = $this->asn1map($decoded, $option); $value = $this->asn1map($decoded, $option, $special);
break; break;
case !isset($option['constant']) && $option['type'] == FILE_ASN1_TYPE_CHOICE: case !isset($option['constant']) && $option['type'] == FILE_ASN1_TYPE_CHOICE:
$v = $this->asn1map($decoded, $option); $v = $this->asn1map($decoded, $option, $special);
if (isset($v)) { if (isset($v)) {
$value = $v; $value = $v;
} }
} }
if (isset($value)) { if (isset($value)) {
if (isset($special[$key])) {
$value = call_user_func($special[$key], $value);
}
return array($key => $value); return array($key => $value);
} }
} }
return NULL; return null;
case isset($mapping['implicit']): case isset($mapping['implicit']):
case isset($mapping['explicit']): case isset($mapping['explicit']):
case $decoded['type'] == $mapping['type']: case $decoded['type'] == $mapping['type']:
break; break;
default: default:
return NULL; // if $decoded['type'] and $mapping['type'] are both strings, but different types of strings,
// let it through
switch (true) {
case $decoded['type'] < 18: // FILE_ASN1_TYPE_NUMERIC_STRING == 18
case $decoded['type'] > 30: // FILE_ASN1_TYPE_BMP_STRING == 30
case $mapping['type'] < 18:
case $mapping['type'] > 30:
return null;
}
} }
if (isset($mapping['implicit'])) { if (isset($mapping['implicit'])) {
@ -560,8 +592,8 @@ class File_ASN1 {
if (isset($mapping['min']) && isset($mapping['max'])) { if (isset($mapping['min']) && isset($mapping['max'])) {
$child = $mapping['children']; $child = $mapping['children'];
foreach ($decoded['content'] as $content) { foreach ($decoded['content'] as $content) {
if (($map[] = $this->asn1map($content, $child)) === NULL) { if (($map[] = $this->asn1map($content, $child, $special)) === null) {
return NULL; return null;
} }
} }
@ -579,15 +611,14 @@ class File_ASN1 {
if ($child['type'] != FILE_ASN1_TYPE_CHOICE) { if ($child['type'] != FILE_ASN1_TYPE_CHOICE) {
// Get the mapping and input class & constant. // Get the mapping and input class & constant.
$childClass = $tempClass = FILE_ASN1_CLASS_UNIVERSAL; $childClass = $tempClass = FILE_ASN1_CLASS_UNIVERSAL;
$constant = NULL; $constant = null;
if (isset($temp['constant'])) { if (isset($temp['constant'])) {
$tempClass = isset($temp['class']) ? $temp['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC; $tempClass = isset($temp['class']) ? $temp['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
} }
if (isset($child['class'])) { if (isset($child['class'])) {
$childClass = $child['class']; $childClass = $child['class'];
$constant = $child['cast']; $constant = $child['cast'];
} } elseif (isset($child['constant'])) {
elseif (isset($child['constant'])) {
$childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC; $childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
$constant = $child['constant']; $constant = $child['constant'];
} }
@ -604,23 +635,26 @@ class File_ASN1 {
if ($maymatch) { if ($maymatch) {
// Attempt submapping. // Attempt submapping.
$candidate = $this->asn1map($temp, $child); $candidate = $this->asn1map($temp, $child, $special);
$maymatch = $candidate !== NULL; $maymatch = $candidate !== null;
} }
if ($maymatch) { if ($maymatch) {
// Got the match: use it. // Got the match: use it.
if (isset($special[$key])) {
$candidate = call_user_func($special[$key], $candidate);
}
$map[$key] = $candidate; $map[$key] = $candidate;
$i++; $i++;
} elseif (isset($child['default'])) { } elseif (isset($child['default'])) {
$map[$key] = $child['default']; // Use default. $map[$key] = $child['default']; // Use default.
} elseif (!isset($child['optional'])) { } elseif (!isset($child['optional'])) {
return NULL; // Syntax error. return null; // Syntax error.
} }
} }
// Fail mapping if all input items have not been consumed. // Fail mapping if all input items have not been consumed.
return $i < $n? NULL: $map; return $i < $n? null: $map;
// the main diff between sets and sequences is the encapsulation of the foreach in another for loop // the main diff between sets and sequences is the encapsulation of the foreach in another for loop
case FILE_ASN1_TYPE_SET: case FILE_ASN1_TYPE_SET:
@ -630,8 +664,8 @@ class File_ASN1 {
if (isset($mapping['min']) && isset($mapping['max'])) { if (isset($mapping['min']) && isset($mapping['max'])) {
$child = $mapping['children']; $child = $mapping['children'];
foreach ($decoded['content'] as $content) { foreach ($decoded['content'] as $content) {
if (($map[] = $this->asn1map($content, $child)) === NULL) { if (($map[] = $this->asn1map($content, $child, $special)) === null) {
return NULL; return null;
} }
} }
@ -652,12 +686,11 @@ class File_ASN1 {
$maymatch = true; $maymatch = true;
if ($child['type'] != FILE_ASN1_TYPE_CHOICE) { if ($child['type'] != FILE_ASN1_TYPE_CHOICE) {
$childClass = FILE_ASN1_CLASS_UNIVERSAL; $childClass = FILE_ASN1_CLASS_UNIVERSAL;
$constant = NULL; $constant = null;
if (isset($child['class'])) { if (isset($child['class'])) {
$childClass = $child['class']; $childClass = $child['class'];
$constant = $child['cast']; $constant = $child['cast'];
} } elseif (isset($child['constant'])) {
elseif (isset($child['constant'])) {
$childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC; $childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
$constant = $child['constant']; $constant = $child['constant'];
} }
@ -673,8 +706,8 @@ class File_ASN1 {
if ($maymatch) { if ($maymatch) {
// Attempt submapping. // Attempt submapping.
$candidate = $this->asn1map($temp, $child); $candidate = $this->asn1map($temp, $child, $special);
$maymatch = $candidate !== NULL; $maymatch = $candidate !== null;
} }
if (!$maymatch) { if (!$maymatch) {
@ -682,6 +715,9 @@ class File_ASN1 {
} }
// Got the match: use it. // Got the match: use it.
if (isset($special[$key])) {
$candidate = call_user_func($special[$key], $candidate);
}
$map[$key] = $candidate; $map[$key] = $candidate;
break; break;
} }
@ -692,7 +728,7 @@ class File_ASN1 {
if (isset($child['default'])) { if (isset($child['default'])) {
$map[$key] = $child['default']; $map[$key] = $child['default'];
} elseif (!isset($child['optional'])) { } elseif (!isset($child['optional'])) {
return NULL; return null;
} }
} }
} }
@ -774,16 +810,18 @@ class File_ASN1 {
* DER-encodes an ASN.1 semantic mapping ($mapping). Some libraries would probably call this function * DER-encodes an ASN.1 semantic mapping ($mapping). Some libraries would probably call this function
* an ASN.1 compiler. * an ASN.1 compiler.
* *
* "Special" mappings can be applied via $special.
*
* @param String $source * @param String $source
* @param String $mapping * @param String $mapping
* @param Integer $idx * @param Integer $idx
* @return String * @return String
* @access public * @access public
*/ */
function encodeDER($source, $mapping) function encodeDER($source, $mapping, $special = array())
{ {
$this->location = array(); $this->location = array();
return $this->_encode_der($source, $mapping); return $this->_encode_der($source, $mapping, null, $special);
} }
/** /**
@ -795,7 +833,7 @@ class File_ASN1 {
* @return String * @return String
* @access private * @access private
*/ */
function _encode_der($source, $mapping, $idx = NULL) function _encode_der($source, $mapping, $idx = null, $special = array())
{ {
if (is_object($source) && strtolower(get_class($source)) == 'file_asn1_element') { if (is_object($source) && strtolower(get_class($source)) == 'file_asn1_element') {
return $source->element; return $source->element;
@ -807,6 +845,9 @@ class File_ASN1 {
} }
if (isset($idx)) { if (isset($idx)) {
if (isset($special[$idx])) {
$source = call_user_func($special[$idx], $source);
}
$this->location[] = $idx; $this->location[] = $idx;
} }
@ -823,7 +864,7 @@ class File_ASN1 {
$child = $mapping['children']; $child = $mapping['children'];
foreach ($source as $content) { foreach ($source as $content) {
$temp = $this->_encode_der($content, $child); $temp = $this->_encode_der($content, $child, null, $special);
if ($temp === false) { if ($temp === false) {
return false; return false;
} }
@ -840,7 +881,7 @@ class File_ASN1 {
continue; continue;
} }
$temp = $this->_encode_der($source[$key], $child, $key); $temp = $this->_encode_der($source[$key], $child, $key, $special);
if ($temp === false) { if ($temp === false) {
return false; return false;
} }
@ -881,7 +922,7 @@ class File_ASN1 {
continue; continue;
} }
$temp = $this->_encode_der($source[$key], $child, $key); $temp = $this->_encode_der($source[$key], $child, $key, $special);
if ($temp === false) { if ($temp === false) {
return false; return false;
} }
@ -918,6 +959,9 @@ class File_ASN1 {
case FILE_ASN1_TYPE_INTEGER: case FILE_ASN1_TYPE_INTEGER:
case FILE_ASN1_TYPE_ENUMERATED: case FILE_ASN1_TYPE_ENUMERATED:
if (!isset($mapping['mapping'])) { if (!isset($mapping['mapping'])) {
if (is_numeric($source)) {
$source = new Math_BigInteger($source);
}
$value = $source->toBytes(true); $value = $source->toBytes(true);
} else { } else {
$value = array_search($source, $mapping['mapping']); $value = array_search($source, $mapping['mapping']);
@ -926,10 +970,10 @@ class File_ASN1 {
} }
$value = new Math_BigInteger($value); $value = new Math_BigInteger($value);
$value = $value->toBytes(true); $value = $value->toBytes(true);
}
if (!strlen($value)) { if (!strlen($value)) {
$value = chr(0); $value = chr(0);
} }
}
break; break;
case FILE_ASN1_TYPE_UTC_TIME: case FILE_ASN1_TYPE_UTC_TIME:
case FILE_ASN1_TYPE_GENERALIZED_TIME: case FILE_ASN1_TYPE_GENERALIZED_TIME:
@ -948,6 +992,10 @@ class File_ASN1 {
} }
} }
if (isset($mapping['min']) && $mapping['min'] >= 1 && $size < $mapping['min']) {
$size = $mapping['min'] - 1;
}
$offset = 8 - (($size + 1) & 7); $offset = 8 - (($size + 1) & 7);
$offset = $offset !== 8 ? $offset : 0; $offset = $offset !== 8 ? $offset : 0;
@ -1003,19 +1051,19 @@ class File_ASN1 {
switch (true) { switch (true) {
case !isset($source): case !isset($source):
return $this->_encode_der(NULL, array('type' => FILE_ASN1_TYPE_NULL) + $mapping); return $this->_encode_der(null, array('type' => FILE_ASN1_TYPE_NULL) + $mapping, null, $special);
case is_int($source): case is_int($source):
case is_object($source) && strtolower(get_class($source)) == 'math_biginteger': case is_object($source) && strtolower(get_class($source)) == 'math_biginteger':
return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_INTEGER) + $mapping); return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_INTEGER) + $mapping, null, $special);
case is_float($source): case is_float($source):
return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_REAL) + $mapping); return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_REAL) + $mapping, null, $special);
case is_bool($source): case is_bool($source):
return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_BOOLEAN) + $mapping); return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_BOOLEAN) + $mapping, null, $special);
case is_array($source) && count($source) == 1: case is_array($source) && count($source) == 1:
$typename = implode('', array_keys($source)); $typename = implode('', array_keys($source));
$outtype = array_search($typename, $this->ANYmap, true); $outtype = array_search($typename, $this->ANYmap, true);
if ($outtype !== false) { if ($outtype !== false) {
return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping); return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping, null, $special);
} }
} }
@ -1031,7 +1079,7 @@ class File_ASN1 {
user_error('No filters defined for ' . implode('/', $loc)); user_error('No filters defined for ' . implode('/', $loc));
return false; return false;
} }
return $this->_encode_der($source, $filters + $mapping); return $this->_encode_der($source, $filters + $mapping, null, $special);
case FILE_ASN1_TYPE_NULL: case FILE_ASN1_TYPE_NULL:
$value = ''; $value = '';
break; break;
@ -1061,7 +1109,12 @@ class File_ASN1 {
} }
if (isset($mapping['cast'])) { if (isset($mapping['cast'])) {
$tag = ($mapping['class'] << 6) | ($tag & 0x20) | $mapping['cast']; if (isset($mapping['explicit']) || $mapping['type'] == FILE_ASN1_TYPE_CHOICE) {
$value = chr($tag) . $this->_encodeLength(strlen($value)) . $value;
$tag = ($mapping['class'] << 6) | 0x20 | $mapping['cast'];
} else {
$tag = ($mapping['class'] << 6) | (ord($temp[0]) & 0x20) | $mapping['cast'];
}
} }
return chr($tag) . $this->_encodeLength(strlen($value)) . $value; return chr($tag) . $this->_encodeLength(strlen($value)) . $value;

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,4 @@
<?php <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/** /**
* Pure-PHP arbitrary precision integer arithmetic library. * Pure-PHP arbitrary precision integer arithmetic library.
@ -20,10 +19,6 @@
* which only supports integers. Although this fact will slow this library down, the fact that such a high * which only supports integers. Although this fact will slow this library down, the fact that such a high
* base is being used should more than compensate. * base is being used should more than compensate.
* *
* When PHP version 6 is officially released, we'll be able to use 64-bit integers. This should, once again,
* allow bitwise operators, and will increase the maximum possible base to 2**31 (or 2**62 for addition /
* subtraction).
*
* Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness little endian} format. ie. * Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness little endian} format. ie.
* (new Math_BigInteger(pow(2, 26)))->value = array(0, 1) * (new Math_BigInteger(pow(2, 26)))->value = array(0, 1)
* *
@ -36,7 +31,7 @@
* Here's an example of how to use this library: * Here's an example of how to use this library:
* <code> * <code>
* <?php * <?php
* include('Math/BigInteger.php'); * include 'Math/BigInteger.php';
* *
* $a = new Math_BigInteger(2); * $a = new Math_BigInteger(2);
* $b = new Math_BigInteger(3); * $b = new Math_BigInteger(3);
@ -174,12 +169,12 @@ define('MATH_BIGINTEGER_KARATSUBA_CUTOFF', 25);
* Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256 * Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256
* numbers. * numbers.
* *
* @author Jim Wigginton <terrafrost@php.net>
* @version 1.0.0RC4
* @access public
* @package Math_BigInteger * @package Math_BigInteger
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/ */
class Math_BigInteger { class Math_BigInteger
{
/** /**
* Holds the BigInteger's value. * Holds the BigInteger's value.
* *
@ -242,13 +237,13 @@ class Math_BigInteger {
* *
* Here's an example: * Here's an example:
* <code> * <code>
* &lt;?php * <?php
* include('Math/BigInteger.php'); * include 'Math/BigInteger.php';
* *
* $a = new Math_BigInteger('0x32', 16); // 50 in base-16 * $a = new Math_BigInteger('0x32', 16); // 50 in base-16
* *
* echo $a->toString(); // outputs 50 * echo $a->toString(); // outputs 50
* ?&gt; * ?>
* </code> * </code>
* *
* @param optional $x base-10 number or base-$base number if $base set. * @param optional $x base-10 number or base-$base number if $base set.
@ -272,7 +267,31 @@ class Math_BigInteger {
} }
if (function_exists('openssl_public_encrypt') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { if (function_exists('openssl_public_encrypt') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
// some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
ob_start();
@phpinfo();
$content = ob_get_contents();
ob_end_clean();
preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
$versions = array();
if (!empty($matches[1])) {
for ($i = 0; $i < count($matches[1]); $i++) {
$versions[$matches[1][$i]] = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
}
}
// it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
switch (true) {
case !isset($versions['Header']):
case !isset($versions['Library']):
case $versions['Header'] == $versions['Library']:
define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); define('MATH_BIGINTEGER_OPENSSL_ENABLED', true);
break;
default:
define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
}
} }
if (!defined('PHP_INT_SIZE')) { if (!defined('PHP_INT_SIZE')) {
@ -438,7 +457,6 @@ class Math_BigInteger {
} }
$x = str_pad($x, strlen($x) + ((MATH_BIGINTEGER_MAX10_LEN - 1) * strlen($x)) % MATH_BIGINTEGER_MAX10_LEN, 0, STR_PAD_LEFT); $x = str_pad($x, strlen($x) + ((MATH_BIGINTEGER_MAX10_LEN - 1) * strlen($x)) % MATH_BIGINTEGER_MAX10_LEN, 0, STR_PAD_LEFT);
while (strlen($x)) { while (strlen($x)) {
$temp = $temp->multiply($multiplier); $temp = $temp->multiply($multiplier);
$temp = $temp->add(new Math_BigInteger($this->_int2bytes(substr($x, 0, MATH_BIGINTEGER_MAX10_LEN)), 256)); $temp = $temp->add(new Math_BigInteger($this->_int2bytes(substr($x, 0, MATH_BIGINTEGER_MAX10_LEN)), 256));
@ -488,7 +506,7 @@ class Math_BigInteger {
* Here's an example: * Here's an example:
* <code> * <code>
* <?php * <?php
* include('Math/BigInteger.php'); * include 'Math/BigInteger.php';
* *
* $a = new Math_BigInteger('65'); * $a = new Math_BigInteger('65');
* *
@ -585,7 +603,7 @@ class Math_BigInteger {
* Here's an example: * Here's an example:
* <code> * <code>
* <?php * <?php
* include('Math/BigInteger.php'); * include 'Math/BigInteger.php';
* *
* $a = new Math_BigInteger('65'); * $a = new Math_BigInteger('65');
* *
@ -612,7 +630,7 @@ class Math_BigInteger {
* Here's an example: * Here's an example:
* <code> * <code>
* <?php * <?php
* include('Math/BigInteger.php'); * include 'Math/BigInteger.php';
* *
* $a = new Math_BigInteger('65'); * $a = new Math_BigInteger('65');
* *
@ -650,7 +668,7 @@ class Math_BigInteger {
* Here's an example: * Here's an example:
* <code> * <code>
* <?php * <?php
* include('Math/BigInteger.php'); * include 'Math/BigInteger.php';
* *
* $a = new Math_BigInteger('50'); * $a = new Math_BigInteger('50');
* *
@ -803,7 +821,7 @@ class Math_BigInteger {
* Here's an example: * Here's an example:
* <code> * <code>
* <?php * <?php
* include('Math/BigInteger.php'); * include 'Math/BigInteger.php';
* *
* $a = new Math_BigInteger('10'); * $a = new Math_BigInteger('10');
* $b = new Math_BigInteger('20'); * $b = new Math_BigInteger('20');
@ -902,7 +920,7 @@ class Math_BigInteger {
$carry = $sum >= MATH_BIGINTEGER_MAX_DIGIT2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 $carry = $sum >= MATH_BIGINTEGER_MAX_DIGIT2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
$sum = $carry ? $sum - MATH_BIGINTEGER_MAX_DIGIT2 : $sum; $sum = $carry ? $sum - MATH_BIGINTEGER_MAX_DIGIT2 : $sum;
$temp = (int) ($sum / MATH_BIGINTEGER_BASE_FULL); $temp = MATH_BIGINTEGER_BASE === 26 ? intval($sum / 0x4000000) : ($sum >> 31);
$value[$i] = (int) ($sum - MATH_BIGINTEGER_BASE_FULL * $temp); // eg. a faster alternative to fmod($sum, 0x4000000) $value[$i] = (int) ($sum - MATH_BIGINTEGER_BASE_FULL * $temp); // eg. a faster alternative to fmod($sum, 0x4000000)
$value[$j] = $temp; $value[$j] = $temp;
@ -934,7 +952,7 @@ class Math_BigInteger {
* Here's an example: * Here's an example:
* <code> * <code>
* <?php * <?php
* include('Math/BigInteger.php'); * include 'Math/BigInteger.php';
* *
* $a = new Math_BigInteger('10'); * $a = new Math_BigInteger('10');
* $b = new Math_BigInteger('20'); * $b = new Math_BigInteger('20');
@ -1038,7 +1056,7 @@ class Math_BigInteger {
$carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
$sum = $carry ? $sum + MATH_BIGINTEGER_MAX_DIGIT2 : $sum; $sum = $carry ? $sum + MATH_BIGINTEGER_MAX_DIGIT2 : $sum;
$temp = (int) ($sum / MATH_BIGINTEGER_BASE_FULL); $temp = MATH_BIGINTEGER_BASE === 26 ? intval($sum / 0x4000000) : ($sum >> 31);
$x_value[$i] = (int) ($sum - MATH_BIGINTEGER_BASE_FULL * $temp); $x_value[$i] = (int) ($sum - MATH_BIGINTEGER_BASE_FULL * $temp);
$x_value[$j] = $temp; $x_value[$j] = $temp;
@ -1070,7 +1088,7 @@ class Math_BigInteger {
* Here's an example: * Here's an example:
* <code> * <code>
* <?php * <?php
* include('Math/BigInteger.php'); * include 'Math/BigInteger.php';
* *
* $a = new Math_BigInteger('10'); * $a = new Math_BigInteger('10');
* $b = new Math_BigInteger('20'); * $b = new Math_BigInteger('20');
@ -1186,7 +1204,7 @@ class Math_BigInteger {
for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0 for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0
$temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
$carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); $carry = MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
$product_value[$j] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); $product_value[$j] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry);
} }
@ -1199,7 +1217,7 @@ class Math_BigInteger {
for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) { for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) {
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
$carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); $carry = MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
$product_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); $product_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry);
} }
@ -1287,13 +1305,13 @@ class Math_BigInteger {
$i2 = $i << 1; $i2 = $i << 1;
$temp = $square_value[$i2] + $value[$i] * $value[$i]; $temp = $square_value[$i2] + $value[$i] * $value[$i];
$carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); $carry = MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
$square_value[$i2] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); $square_value[$i2] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry);
// note how we start from $i+1 instead of 0 as we do in multiplication. // note how we start from $i+1 instead of 0 as we do in multiplication.
for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) { for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) {
$temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry; $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry;
$carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); $carry = MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
$square_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); $square_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry);
} }
@ -1354,7 +1372,7 @@ class Math_BigInteger {
* Here's an example: * Here's an example:
* <code> * <code>
* <?php * <?php
* include('Math/BigInteger.php'); * include 'Math/BigInteger.php';
* *
* $a = new Math_BigInteger('10'); * $a = new Math_BigInteger('10');
* $b = new Math_BigInteger('20'); * $b = new Math_BigInteger('20');
@ -1491,9 +1509,8 @@ class Math_BigInteger {
if ($x_window[0] == $y_window[0]) { if ($x_window[0] == $y_window[0]) {
$quotient_value[$q_index] = MATH_BIGINTEGER_MAX_DIGIT; $quotient_value[$q_index] = MATH_BIGINTEGER_MAX_DIGIT;
} else { } else {
$quotient_value[$q_index] = (int) ( $quotient_value[$q_index] = $this->_safe_divide(
($x_window[0] * MATH_BIGINTEGER_BASE_FULL + $x_window[1]) $x_window[0] * MATH_BIGINTEGER_BASE_FULL + $x_window[1],
/
$y_window[0] $y_window[0]
); );
} }
@ -1561,7 +1578,7 @@ class Math_BigInteger {
for ($i = count($dividend) - 1; $i >= 0; --$i) { for ($i = count($dividend) - 1; $i >= 0; --$i) {
$temp = MATH_BIGINTEGER_BASE_FULL * $carry + $dividend[$i]; $temp = MATH_BIGINTEGER_BASE_FULL * $carry + $dividend[$i];
$result[$i] = (int) ($temp / $divisor); $result[$i] = $this->_safe_divide($temp, $divisor);
$carry = (int) ($temp - $divisor * $result[$i]); $carry = (int) ($temp - $divisor * $result[$i]);
} }
@ -1574,7 +1591,7 @@ class Math_BigInteger {
* Here's an example: * Here's an example:
* <code> * <code>
* <?php * <?php
* include('Math/BigInteger.php'); * include 'Math/BigInteger.php';
* *
* $a = new Math_BigInteger('10'); * $a = new Math_BigInteger('10');
* $b = new Math_BigInteger('20'); * $b = new Math_BigInteger('20');
@ -1699,6 +1716,11 @@ class Math_BigInteger {
return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_BARRETT)); return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_BARRETT));
// the following code, although not callable, can be run independently of the above code
// although the above code performed better in my benchmarks the following could might
// perform better under different circumstances. in lieu of deleting it it's just been
// made uncallable
// is the modulo odd? // is the modulo odd?
if ( $n->value[0] & 1 ) { if ( $n->value[0] & 1 ) {
return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_MONTGOMERY)); return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_MONTGOMERY));
@ -2173,7 +2195,7 @@ class Math_BigInteger {
for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i
$temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
$carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); $carry = MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
$product_value[$j] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); $product_value[$j] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry);
} }
@ -2189,7 +2211,7 @@ class Math_BigInteger {
for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) { for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) {
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
$carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); $carry = MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
$product_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); $product_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry);
} }
@ -2238,7 +2260,7 @@ class Math_BigInteger {
for ($i = 0; $i < $k; ++$i) { for ($i = 0; $i < $k; ++$i) {
$temp = $result[MATH_BIGINTEGER_VALUE][$i] * $cache[MATH_BIGINTEGER_DATA][$key]; $temp = $result[MATH_BIGINTEGER_VALUE][$i] * $cache[MATH_BIGINTEGER_DATA][$key];
$temp = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * ((int) ($temp / MATH_BIGINTEGER_BASE_FULL))); $temp = $temp - MATH_BIGINTEGER_BASE_FULL * (MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
$temp = $this->_regularMultiply(array($temp), $n); $temp = $this->_regularMultiply(array($temp), $n);
$temp = array_merge($this->_array_repeat(0, $i), $temp); $temp = array_merge($this->_array_repeat(0, $i), $temp);
$result = $this->_add($result[MATH_BIGINTEGER_VALUE], false, $temp, false); $result = $this->_add($result[MATH_BIGINTEGER_VALUE], false, $temp, false);
@ -2272,6 +2294,11 @@ class Math_BigInteger {
$temp = $this->_multiply($x, false, $y, false); $temp = $this->_multiply($x, false, $y, false);
return $this->_montgomery($temp[MATH_BIGINTEGER_VALUE], $m); return $this->_montgomery($temp[MATH_BIGINTEGER_VALUE], $m);
// the following code, although not callable, can be run independently of the above code
// although the above code performed better in my benchmarks the following could might
// perform better under different circumstances. in lieu of deleting it it's just been
// made uncallable
static $cache = array( static $cache = array(
MATH_BIGINTEGER_VARIABLE => array(), MATH_BIGINTEGER_VARIABLE => array(),
MATH_BIGINTEGER_DATA => array() MATH_BIGINTEGER_DATA => array()
@ -2290,9 +2317,9 @@ class Math_BigInteger {
$a = array(MATH_BIGINTEGER_VALUE => $this->_array_repeat(0, $n + 1)); $a = array(MATH_BIGINTEGER_VALUE => $this->_array_repeat(0, $n + 1));
for ($i = 0; $i < $n; ++$i) { for ($i = 0; $i < $n; ++$i) {
$temp = $a[MATH_BIGINTEGER_VALUE][0] + $x[$i] * $y[0]; $temp = $a[MATH_BIGINTEGER_VALUE][0] + $x[$i] * $y[0];
$temp = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * ((int) ($temp / MATH_BIGINTEGER_BASE_FULL))); $temp = $temp - MATH_BIGINTEGER_BASE_FULL * (MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
$temp = $temp * $cache[MATH_BIGINTEGER_DATA][$key]; $temp = $temp * $cache[MATH_BIGINTEGER_DATA][$key];
$temp = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * ((int) ($temp / MATH_BIGINTEGER_BASE_FULL))); $temp = $temp - MATH_BIGINTEGER_BASE_FULL * (MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
$temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false); $temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false);
$a = $this->_add($a[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false); $a = $this->_add($a[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false);
$a[MATH_BIGINTEGER_VALUE] = array_slice($a[MATH_BIGINTEGER_VALUE], 1); $a[MATH_BIGINTEGER_VALUE] = array_slice($a[MATH_BIGINTEGER_VALUE], 1);
@ -2369,7 +2396,7 @@ class Math_BigInteger {
* Here's an example: * Here's an example:
* <code> * <code>
* <?php * <?php
* include('Math/BigInteger.php'); * include 'Math/BigInteger.php';
* *
* $a = new Math_BigInteger(30); * $a = new Math_BigInteger(30);
* $b = new Math_BigInteger(17); * $b = new Math_BigInteger(17);
@ -2437,7 +2464,7 @@ class Math_BigInteger {
* Here's an example: * Here's an example:
* <code> * <code>
* <?php * <?php
* include('Math/BigInteger.php'); * include 'Math/BigInteger.php';
* *
* $a = new Math_BigInteger(693); * $a = new Math_BigInteger(693);
* $b = new Math_BigInteger(609); * $b = new Math_BigInteger(609);
@ -2572,7 +2599,7 @@ class Math_BigInteger {
* Here's an example: * Here's an example:
* <code> * <code>
* <?php * <?php
* include('Math/BigInteger.php'); * include 'Math/BigInteger.php';
* *
* $a = new Math_BigInteger(693); * $a = new Math_BigInteger(693);
* $b = new Math_BigInteger(609); * $b = new Math_BigInteger(609);
@ -2877,7 +2904,7 @@ class Math_BigInteger {
$leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3); $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3);
$this->_base256_lshift($leading_ones, $current_bits); $this->_base256_lshift($leading_ones, $current_bits);
$temp = str_pad($temp, ceil($this->bits / 8), chr(0), STR_PAD_LEFT); $temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT);
return $this->_normalize(new Math_BigInteger($leading_ones | $temp, 256)); return $this->_normalize(new Math_BigInteger($leading_ones | $temp, 256));
} }
@ -3029,40 +3056,17 @@ class Math_BigInteger {
} }
/** /**
* Generate a random number * Generates a random BigInteger
* *
* @param optional Integer $min * Byte length is equal to $length. Uses crypt_random if it's loaded and mt_rand if it's not.
* @param optional Integer $max *
* @param Integer $length
* @return Math_BigInteger * @return Math_BigInteger
* @access public * @access private
*/ */
function random($min = false, $max = false) function _random_number_helper($size)
{ {
if ($min === false) { if (function_exists('crypt_random_string')) {
$min = new Math_BigInteger(0);
}
if ($max === false) {
$max = new Math_BigInteger(0x7FFFFFFF);
}
$compare = $max->compare($min);
if (!$compare) {
return $this->_normalize($min);
} else if ($compare < 0) {
// if $min is bigger then $max, swap $min and $max
$temp = $max;
$max = $min;
$min = $temp;
}
$max = $max->subtract($min);
$max = ltrim($max->toBytes(), chr(0));
$size = strlen($max) - 1;
$crypt_random = function_exists('crypt_random_string') || (!class_exists('Crypt_Random') && function_exists('crypt_random_string'));
if ($crypt_random) {
$random = crypt_random_string($size); $random = crypt_random_string($size);
} else { } else {
$random = ''; $random = '';
@ -3078,25 +3082,89 @@ class Math_BigInteger {
} }
} }
$fragment = new Math_BigInteger($random, 256); return new Math_BigInteger($random, 256);
$leading = $fragment->compare(new Math_BigInteger(substr($max, 1), 256)) > 0 ? }
ord($max[0]) - 1 : ord($max[0]);
if (!$crypt_random) { /**
$msb = chr(mt_rand(0, $leading)); * Generate a random number
*
* Returns a random number between $min and $max where $min and $max
* can be defined using one of the two methods:
*
* $min->random($max)
* $max->random($min)
*
* @param Math_BigInteger $arg1
* @param optional Math_BigInteger $arg2
* @return Math_BigInteger
* @access public
* @internal The API for creating random numbers used to be $a->random($min, $max), where $a was a Math_BigInteger object.
* That method is still supported for BC purposes.
*/
function random($arg1, $arg2 = false)
{
if ($arg1 === false) {
return false;
}
if ($arg2 === false) {
$max = $arg1;
$min = $this;
} else { } else {
$cutoff = floor(0xFF / $leading) * $leading; $min = $arg1;
while (true) { $max = $arg2;
$msb = ord(crypt_random_string(1));
if ($msb <= $cutoff) {
$msb%= $leading;
break;
}
}
$msb = chr($msb);
} }
$random = new Math_BigInteger($msb . $random, 256); $compare = $max->compare($min);
if (!$compare) {
return $this->_normalize($min);
} else if ($compare < 0) {
// if $min is bigger then $max, swap $min and $max
$temp = $max;
$max = $min;
$min = $temp;
}
static $one;
if (!isset($one)) {
$one = new Math_BigInteger(1);
}
$max = $max->subtract($min->subtract($one));
$size = strlen(ltrim($max->toBytes(), chr(0)));
/*
doing $random % $max doesn't work because some numbers will be more likely to occur than others.
eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145
would produce 5 whereas the only value of random that could produce 139 would be 139. ie.
not all numbers would be equally likely. some would be more likely than others.
creating a whole new random number until you find one that is within the range doesn't work
because, for sufficiently small ranges, the likelihood that you'd get a number within that range
would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability
would be pretty high that $random would be greater than $max.
phpseclib works around this using the technique described here:
http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string
*/
$random_max = new Math_BigInteger(chr(1) . str_repeat("\0", $size), 256);
$random = $this->_random_number_helper($size);
list($max_multiple) = $random_max->divide($max);
$max_multiple = $max_multiple->multiply($max);
while ($random->compare($max_multiple) >= 0) {
$random = $random->subtract($max_multiple);
$random_max = $random_max->subtract($max_multiple);
$random = $random->bitwise_leftShift(8);
$random = $random->add($this->_random_number_helper(1));
$random_max = $random_max->bitwise_leftShift(8);
list($max_multiple) = $random_max->divide($max);
$max_multiple = $max_multiple->multiply($max);
}
list(, $random) = $random->divide($max);
return $this->_normalize($random->add($min)); return $this->_normalize($random->add($min));
} }
@ -3107,21 +3175,25 @@ class Math_BigInteger {
* If there's not a prime within the given range, false will be returned. If more than $timeout seconds have elapsed, * If there's not a prime within the given range, false will be returned. If more than $timeout seconds have elapsed,
* give up and return false. * give up and return false.
* *
* @param optional Integer $min * @param Math_BigInteger $arg1
* @param optional Integer $max * @param optional Math_BigInteger $arg2
* @param optional Integer $timeout * @param optional Integer $timeout
* @return Math_BigInteger * @return Mixed
* @access public * @access public
* @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}. * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}.
*/ */
function randomPrime($min = false, $max = false, $timeout = false) function randomPrime($arg1, $arg2 = false, $timeout = false)
{ {
if ($min === false) { if ($arg1 === false) {
$min = new Math_BigInteger(0); return false;
} }
if ($max === false) { if ($arg2 === false) {
$max = new Math_BigInteger(0x7FFFFFFF); $max = $arg1;
$min = $this;
} else {
$min = $arg1;
$max = $arg2;
} }
$compare = $max->compare($min); $compare = $max->compare($min);
@ -3147,6 +3219,7 @@ class Math_BigInteger {
// gmp_nextprime() requires PHP 5 >= 5.2.0 per <http://php.net/gmp-nextprime>. // gmp_nextprime() requires PHP 5 >= 5.2.0 per <http://php.net/gmp-nextprime>.
if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP && function_exists('gmp_nextprime') ) { if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP && function_exists('gmp_nextprime') ) {
$p = new Math_BigInteger();
$p->value = gmp_nextprime($x->value); $p->value = gmp_nextprime($x->value);
if ($p->compare($max) <= 0) { if ($p->compare($max) <= 0) {
@ -3229,10 +3302,10 @@ class Math_BigInteger {
* Checks a numer to see if it's prime * Checks a numer to see if it's prime
* *
* Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the * Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the
* $t parameter is distributability. Math_BigInteger::randomPrime() can be distributed accross multiple pageloads * $t parameter is distributability. Math_BigInteger::randomPrime() can be distributed across multiple pageloads
* on a website instead of just one. * on a website instead of just one.
* *
* @param optional Integer $t * @param optional Math_BigInteger $t
* @return Boolean * @return Boolean
* @access public * @access public
* @internal Uses the * @internal Uses the
@ -3245,6 +3318,7 @@ class Math_BigInteger {
if (!$t) { if (!$t) {
// see HAC 4.49 "Note (controlling the error probability)" // see HAC 4.49 "Note (controlling the error probability)"
// @codingStandardsIgnoreStart
if ($length >= 163) { $t = 2; } // floor(1300 / 8) if ($length >= 163) { $t = 2; } // floor(1300 / 8)
else if ($length >= 106) { $t = 3; } // floor( 850 / 8) else if ($length >= 106) { $t = 3; } // floor( 850 / 8)
else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8) else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8)
@ -3257,6 +3331,7 @@ class Math_BigInteger {
else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8) else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8)
else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8) else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8)
else { $t = 27; } else { $t = 27; }
// @codingStandardsIgnoreEnd
} }
// ie. gmp_testbit($this, 0) // ie. gmp_testbit($this, 0)
@ -3399,7 +3474,7 @@ class Math_BigInteger {
for ($i = 0; $i < count($this->value); ++$i) { for ($i = 0; $i < count($this->value); ++$i) {
$temp = $this->value[$i] * $shift + $carry; $temp = $this->value[$i] * $shift + $carry;
$carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); $carry = MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
$this->value[$i] = (int) ($temp - $carry * MATH_BIGINTEGER_BASE_FULL); $this->value[$i] = (int) ($temp - $carry * MATH_BIGINTEGER_BASE_FULL);
} }
@ -3647,4 +3722,27 @@ class Math_BigInteger {
$temp = ltrim(pack('N', $length), chr(0)); $temp = ltrim(pack('N', $length), chr(0));
return pack('Ca*', 0x80 | strlen($temp), $temp); return pack('Ca*', 0x80 | strlen($temp), $temp);
} }
/**
* Single digit division
*
* Even if int64 is being used the division operator will return a float64 value
* if the dividend is not evenly divisible by the divisor. Since a float64 doesn't
* have the precision of int64 this is a problem so, when int64 is being used,
* we'll guarantee that the dividend is divisible by first subtracting the remainder.
*
* @access private
* @param Integer $x
* @param Integer $y
* @return Integer
*/
function _safe_divide($x, $y)
{
if (MATH_BIGINTEGER_BASE === 26) {
return (int) ($x / $y);
}
// MATH_BIGINTEGER_BASE === 31
return ($x - ($x % $y)) / $y;
}
} }

View File

@ -1,5 +1,4 @@
<?php <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/** /**
* Pure-PHP implementation of SCP. * Pure-PHP implementation of SCP.
@ -11,8 +10,8 @@
* Here's a short example of how to use this library: * Here's a short example of how to use this library:
* <code> * <code>
* <?php * <?php
* include('Net/SCP.php'); * include 'Net/SCP.php';
* include('Net/SSH2.php'); * include 'Net/SSH2.php';
* *
* $ssh = new Net_SSH2('www.domain.tld'); * $ssh = new Net_SSH2('www.domain.tld');
* if (!$ssh->login('username', 'password')) { * if (!$ssh->login('username', 'password')) {
@ -82,12 +81,12 @@ define('NET_SCP_SSH2', 2);
/** /**
* Pure-PHP implementations of SCP. * Pure-PHP implementations of SCP.
* *
* @author Jim Wigginton <terrafrost@php.net>
* @version 0.1.0
* @access public
* @package Net_SCP * @package Net_SCP
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/ */
class Net_SCP { class Net_SCP
{
/** /**
* SSH Object * SSH Object
* *
@ -151,7 +150,7 @@ class Net_SCP {
* So, for example, if you set $data to 'filename.ext' and then do Net_SCP::get(), you will get a file, twelve bytes * So, for example, if you set $data to 'filename.ext' and then do Net_SCP::get(), you will get a file, twelve bytes
* long, containing 'filename.ext' as its contents. * long, containing 'filename.ext' as its contents.
* *
* Setting $mode to NET_SFTP_LOCAL_FILE will change the above behavior. With NET_SFTP_LOCAL_FILE, $remote_file will * Setting $mode to NET_SCP_LOCAL_FILE will change the above behavior. With NET_SCP_LOCAL_FILE, $remote_file will
* contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how
* large $remote_file will be, as well. * large $remote_file will be, as well.
* *
@ -161,16 +160,19 @@ class Net_SCP {
* @param String $remote_file * @param String $remote_file
* @param String $data * @param String $data
* @param optional Integer $mode * @param optional Integer $mode
* @param optional Callable $callback
* @return Boolean * @return Boolean
* @access public * @access public
*/ */
function put($remote_file, $data, $mode = NET_SCP_STRING) function put($remote_file, $data, $mode = NET_SCP_STRING, $callback = null)
{ {
if (!isset($this->ssh)) { if (!isset($this->ssh)) {
return false; return false;
} }
$this->ssh->exec('scp -t ' . $remote_file, false); // -t = to if (!$this->ssh->exec('scp -t "' . $remote_file . '"', false)) { // -t = to
return false;
}
$temp = $this->_receive(); $temp = $this->_receive();
if ($temp !== chr(0)) { if ($temp !== chr(0)) {
@ -178,35 +180,51 @@ class Net_SCP {
} }
if ($this->mode == NET_SCP_SSH2) { if ($this->mode == NET_SCP_SSH2) {
$this->packet_size = $this->ssh->packet_size_client_to_server[NET_SSH2_CHANNEL_EXEC]; $this->packet_size = $this->ssh->packet_size_client_to_server[NET_SSH2_CHANNEL_EXEC] - 4;
} }
$remote_file = basename($remote_file); $remote_file = basename($remote_file);
$this->_send('C0644 ' . strlen($data) . ' ' . $remote_file . "\n");
if ($mode == NET_SCP_STRING) {
$size = strlen($data);
} else {
if (!is_file($data)) {
user_error("$data is not a valid file", E_USER_NOTICE);
return false;
}
$fp = @fopen($data, 'rb');
if (!$fp) {
fclose($fp);
return false;
}
$size = filesize($data);
}
$this->_send('C0644 ' . $size . ' ' . $remote_file . "\n");
$temp = $this->_receive(); $temp = $this->_receive();
if ($temp !== chr(0)) { if ($temp !== chr(0)) {
return false; return false;
} }
if ($mode == NET_SCP_STRING) { $sent = 0;
$this->_send($data); while ($sent < $size) {
} else { $temp = $mode & NET_SCP_STRING ? substr($data, $sent, $this->packet_size) : fread($fp, $this->packet_size);
if (!is_file($data)) { $this->_send($temp);
user_error("$data is not a valid file", E_USER_NOTICE); $sent+= strlen($temp);
return false;
if (is_callable($callback)) {
call_user_func($callback, $sent);
} }
$fp = @fopen($data, 'rb');
if (!$fp) {
return false;
}
$size = filesize($data);
for ($i = 0; $i < $size; $i += $this->packet_size) {
$this->_send(fgets($fp, $this->packet_size));
}
fclose($fp);
} }
$this->_close(); $this->_close();
if ($mode != NET_SCP_STRING) {
fclose($fp);
}
return true;
} }
/** /**
@ -227,7 +245,9 @@ class Net_SCP {
return false; return false;
} }
$this->ssh->exec('scp -f ' . $remote_file, false); // -f = from if (!$this->ssh->exec('scp -f "' . $remote_file . '"', false)) { // -f = from
return false;
}
$this->_send("\0"); $this->_send("\0");
@ -332,7 +352,7 @@ class Net_SCP {
{ {
switch ($this->mode) { switch ($this->mode) {
case NET_SCP_SSH2: case NET_SCP_SSH2:
$this->ssh->_close_channel(NET_SSH2_CHANNEL_EXEC); $this->ssh->_close_channel(NET_SSH2_CHANNEL_EXEC, true);
break; break;
case NET_SCP_SSH1: case NET_SCP_SSH1:
$this->ssh->disconnect(); $this->ssh->disconnect();

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,4 @@
<?php <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/** /**
* SFTP Stream Wrapper * SFTP Stream Wrapper
@ -37,12 +36,12 @@
/** /**
* SFTP Stream Wrapper * SFTP Stream Wrapper
* *
* @author Jim Wigginton <terrafrost@php.net>
* @version 0.3.2
* @access public
* @package Net_SFTP_Stream * @package Net_SFTP_Stream
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/ */
class Net_SFTP_Stream { class Net_SFTP_Stream
{
/** /**
* SFTP instances * SFTP instances
* *
@ -127,6 +126,22 @@ class Net_SFTP_Stream {
*/ */
var $notification; var $notification;
/**
* Registers this class as a URL wrapper.
*
* @param optional String $protocol The wrapper name to be registered.
* @return Boolean True on success, false otherwise.
* @access public
*/
static function register($protocol = 'sftp')
{
if (in_array($protocol, stream_get_wrappers(), true)) {
return false;
}
$class = function_exists('get_called_class') ? get_called_class() : __CLASS__;
return stream_wrapper_register($protocol, $class);
}
/** /**
* The Constructor * The Constructor
* *
@ -134,8 +149,12 @@ class Net_SFTP_Stream {
*/ */
function Net_SFTP_Stream() function Net_SFTP_Stream()
{ {
if (defined('NET_SFTP_STREAM_LOGGING')) {
echo "__construct()\r\n";
}
if (!class_exists('Net_SFTP')) { if (!class_exists('Net_SFTP')) {
require_once('Net/SFTP.php'); include_once 'Net/SFTP.php';
} }
} }
@ -153,44 +172,48 @@ class Net_SFTP_Stream {
*/ */
function _parse_path($path) function _parse_path($path)
{ {
extract(parse_url($path)); extract(parse_url($path) + array('port' => 22));
if (!isset($host)) { if (!isset($host)) {
return false; return false;
} }
if (isset($this->context)) {
$context = stream_context_get_params($this->context); $context = stream_context_get_params($this->context);
if (isset($context['notification'])) { if (isset($context['notification'])) {
$this->notification = $context['notification']; $this->notification = $context['notification'];
} }
}
if ($host[0] == '$') { if ($host[0] == '$') {
$host = substr($host, 1); $host = substr($host, 1);
global $$host; global $$host;
if (!is_object($$host) || get_class($$host) != 'Net_sFTP') { if (!is_object($$host) || get_class($$host) != 'Net_SFTP') {
return false; return false;
} }
$this->sftp = $$host; $this->sftp = $$host;
} else { } else {
if (isset($this->context)) {
$context = stream_context_get_options($this->context); $context = stream_context_get_options($this->context);
if (isset($context['sftp']['session'])) {
$sftp = $context['sftp']['session'];
} }
if (isset($context['sftp']['sftp'])) { if (isset($context[$scheme]['session'])) {
$sftp = $context['sftp']['sftp']; $sftp = $context[$scheme]['session'];
}
if (isset($context[$scheme]['sftp'])) {
$sftp = $context[$scheme]['sftp'];
} }
if (isset($sftp) && is_object($sftp) && get_class($sftp) == 'Net_SFTP') { if (isset($sftp) && is_object($sftp) && get_class($sftp) == 'Net_SFTP') {
$this->sftp = $sftp; $this->sftp = $sftp;
return $path; return $path;
} }
if (isset($context['sftp']['username'])) { if (isset($context[$scheme]['username'])) {
$user = $context['sftp']['username']; $user = $context[$scheme]['username'];
} }
if (isset($context['sftp']['password'])) { if (isset($context[$scheme]['password'])) {
$pass = $context['sftp']['password']; $pass = $context[$scheme]['password'];
} }
if (isset($context['sftp']['privkey']) && is_object($context['sftp']['privkey']) && get_Class($context['sftp']['privkey']) == 'Crypt_RSA') { if (isset($context[$scheme]['privkey']) && is_object($context[$scheme]['privkey']) && get_Class($context[$scheme]['privkey']) == 'Crypt_RSA') {
$pass = $context['sftp']['privkey']; $pass = $context[$scheme]['privkey'];
} }
if (!isset($user) || !isset($pass)) { if (!isset($user) || !isset($pass)) {
@ -201,7 +224,8 @@ class Net_SFTP_Stream {
if (isset(self::$instances[$host][$port][$user][(string) $pass])) { if (isset(self::$instances[$host][$port][$user][(string) $pass])) {
$this->sftp = self::$instances[$host][$port][$user][(string) $pass]; $this->sftp = self::$instances[$host][$port][$user][(string) $pass];
} else { } else {
$this->sftp = new Net_SFTP($host, isset($port) ? $port : 22); $this->sftp = new Net_SFTP($host, $port);
$this->sftp->disableStatCache();
if (isset($this->notification) && is_callable($this->notification)) { if (isset($this->notification) && is_callable($this->notification)) {
/* if !is_callable($this->notification) we could do this: /* if !is_callable($this->notification) we could do this:
@ -252,6 +276,7 @@ class Net_SFTP_Stream {
$this->size = $this->sftp->size($path); $this->size = $this->sftp->size($path);
$this->mode = preg_replace('#[bt]$#', '', $mode); $this->mode = preg_replace('#[bt]$#', '', $mode);
$this->eof = false;
if ($this->size === false) { if ($this->size === false) {
if ($this->mode[0] == 'r') { if ($this->mode[0] == 'r') {
@ -508,7 +533,20 @@ class Net_SFTP_Stream {
* Open directory handle * Open directory handle
* *
* The only $options is "whether or not to enforce safe_mode (0x04)". Since safe mode was deprecated in 5.3 and * The only $options is "whether or not to enforce safe_mode (0x04)". Since safe mode was deprecated in 5.3 and
* removed in 5.4 I'm just going to ignore it * removed in 5.4 I'm just going to ignore it.
*
* Also, nlist() is the best that this function is realistically going to be able to do. When an SFTP client
* sends a SSH_FXP_READDIR packet you don't generally get info on just one file but on multiple files. Quoting
* the SFTP specs:
*
* The SSH_FXP_NAME response has the following format:
*
* uint32 id
* uint32 count
* repeats count times:
* string filename
* string longname
* ATTRS attrs
* *
* @param String $path * @param String $path
* @param Integer $options * @param Integer $options
@ -761,6 +799,4 @@ class Net_SFTP_Stream {
} }
} }
if (function_exists('stream_wrapper_register')) { Net_SFTP_Stream::register();
stream_wrapper_register('sftp', 'Net_SFTP_Stream');
}

View File

@ -1,5 +1,4 @@
<?php <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/** /**
* Pure-PHP implementation of SSHv1. * Pure-PHP implementation of SSHv1.
@ -9,7 +8,7 @@
* Here's a short example of how to use this library: * Here's a short example of how to use this library:
* <code> * <code>
* <?php * <?php
* include('Net/SSH1.php'); * include 'Net/SSH1.php';
* *
* $ssh = new Net_SSH1('www.domain.tld'); * $ssh = new Net_SSH1('www.domain.tld');
* if (!$ssh->login('username', 'password')) { * if (!$ssh->login('username', 'password')) {
@ -23,7 +22,7 @@
* Here's another short example: * Here's another short example:
* <code> * <code>
* <?php * <?php
* include('Net/SSH1.php'); * include 'Net/SSH1.php';
* *
* $ssh = new Net_SSH1('www.domain.tld'); * $ssh = new Net_SSH1('www.domain.tld');
* if (!$ssh->login('username', 'password')) { * if (!$ssh->login('username', 'password')) {
@ -183,8 +182,9 @@ define('NET_SSH1_RESPONSE_DATA', 2);
* @access private * @access private
*/ */
define('NET_SSH1_MASK_CONSTRUCTOR', 0x00000001); define('NET_SSH1_MASK_CONSTRUCTOR', 0x00000001);
define('NET_SSH1_MASK_LOGIN', 0x00000002); define('NET_SSH1_MASK_CONNECTED', 0x00000002);
define('NET_SSH1_MASK_SHELL', 0x00000004); define('NET_SSH1_MASK_LOGIN', 0x00000004);
define('NET_SSH1_MASK_SHELL', 0x00000008);
/**#@-*/ /**#@-*/
/**#@+ /**#@+
@ -202,11 +202,11 @@ define('NET_SSH1_LOG_COMPLEX', 2);
/** /**
* Outputs the content real-time * Outputs the content real-time
*/ */
define('NET_SSH2_LOG_REALTIME', 3); define('NET_SSH1_LOG_REALTIME', 3);
/** /**
* Dumps the content real-time to a file * Dumps the content real-time to a file
*/ */
define('NET_SSH2_LOG_REALTIME_FILE', 4); define('NET_SSH1_LOG_REALTIME_FILE', 4);
/**#@-*/ /**#@-*/
/**#@+ /**#@+
@ -226,12 +226,12 @@ define('NET_SSH1_READ_REGEX', 2);
/** /**
* Pure-PHP implementation of SSHv1. * Pure-PHP implementation of SSHv1.
* *
* @author Jim Wigginton <terrafrost@php.net>
* @version 0.1.0
* @access public
* @package Net_SSH1 * @package Net_SSH1
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/ */
class Net_SSH1 { class Net_SSH1
{
/** /**
* The SSH identifier * The SSH identifier
* *
@ -429,11 +429,80 @@ class Net_SSH1 {
/** /**
* Current Timeout * Current Timeout
* *
* @see Net_SSH2::_get_channel_packet() * @see Net_SSH1::_get_channel_packet()
* @access private * @access private
*/ */
var $curTimeout; var $curTimeout;
/**
* Log Boundary
*
* @see Net_SSH1::_format_log
* @access private
*/
var $log_boundary = ':';
/**
* Log Long Width
*
* @see Net_SSH1::_format_log
* @access private
*/
var $log_long_width = 65;
/**
* Log Short Width
*
* @see Net_SSH1::_format_log
* @access private
*/
var $log_short_width = 16;
/**
* Hostname
*
* @see Net_SSH1::Net_SSH1()
* @see Net_SSH1::_connect()
* @var String
* @access private
*/
var $host;
/**
* Port Number
*
* @see Net_SSH1::Net_SSH1()
* @see Net_SSH1::_connect()
* @var Integer
* @access private
*/
var $port;
/**
* Timeout for initial connection
*
* Set by the constructor call. Calling setTimeout() is optional. If it's not called functions like
* exec() won't timeout unless some PHP setting forces it too. The timeout specified in the constructor,
* however, is non-optional. There will be a timeout, whether or not you set it. If you don't it'll be
* 10 seconds. It is used by fsockopen() in that function.
*
* @see Net_SSH1::Net_SSH1()
* @see Net_SSH1::_connect()
* @var Integer
* @access private
*/
var $connectionTimeout;
/**
* Default cipher
*
* @see Net_SSH1::Net_SSH1()
* @see Net_SSH1::_connect()
* @var Integer
* @access private
*/
var $cipher;
/** /**
* Default Constructor. * Default Constructor.
* *
@ -449,16 +518,16 @@ class Net_SSH1 {
function Net_SSH1($host, $port = 22, $timeout = 10, $cipher = NET_SSH1_CIPHER_3DES) function Net_SSH1($host, $port = 22, $timeout = 10, $cipher = NET_SSH1_CIPHER_3DES)
{ {
if (!class_exists('Math_BigInteger')) { if (!class_exists('Math_BigInteger')) {
require_once('Math/BigInteger.php'); include_once 'Math/BigInteger.php';
} }
// Include Crypt_Random // Include Crypt_Random
// the class_exists() will only be called if the crypt_random_string function hasn't been defined and // the class_exists() will only be called if the crypt_random_string function hasn't been defined and
// will trigger a call to __autoload() if you're wanting to auto-load classes // will trigger a call to __autoload() if you're wanting to auto-load classes
// call function_exists() a second time to stop the require_once from being called outside // call function_exists() a second time to stop the include_once from being called outside
// of the auto loader // of the auto loader
if (!function_exists('crypt_random_string') && !class_exists('Crypt_Random') && !function_exists('crypt_random_string')) { if (!function_exists('crypt_random_string') && !class_exists('Crypt_Random') && !function_exists('crypt_random_string')) {
require_once('Crypt/Random.php'); include_once 'Crypt/Random.php';
} }
$this->protocol_flags = array( $this->protocol_flags = array(
@ -482,10 +551,24 @@ class Net_SSH1 {
$this->_define_array($this->protocol_flags); $this->_define_array($this->protocol_flags);
$this->fsock = @fsockopen($host, $port, $errno, $errstr, $timeout); $this->host = $host;
$this->port = $port;
$this->connectionTimeout = $timeout;
$this->cipher = $cipher;
}
/**
* Connect to an SSHv1 server
*
* @return Boolean
* @access private
*/
function _connect()
{
$this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->connectionTimeout);
if (!$this->fsock) { if (!$this->fsock) {
user_error(rtrim("Cannot connect to $host. Error $errno. $errstr")); user_error(rtrim("Cannot connect to {$this->host}:{$this->port}. Error $errno. $errstr"));
return; return false;
} }
$this->server_identification = $init_line = fgets($this->fsock, 255); $this->server_identification = $init_line = fgets($this->fsock, 255);
@ -497,11 +580,11 @@ class Net_SSH1 {
if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line, $parts)) { if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line, $parts)) {
user_error('Can only connect to SSH servers'); user_error('Can only connect to SSH servers');
return; return false;
} }
if ($parts[1][0] != 1) { if ($parts[1][0] != 1) {
user_error("Cannot connect to SSH $parts[1] servers"); user_error("Cannot connect to SSH $parts[1] servers");
return; return false;
} }
fputs($this->fsock, $this->identifier."\r\n"); fputs($this->fsock, $this->identifier."\r\n");
@ -509,7 +592,7 @@ class Net_SSH1 {
$response = $this->_get_binary_packet(); $response = $this->_get_binary_packet();
if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) { if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) {
user_error('Expected SSH_SMSG_PUBLIC_KEY'); user_error('Expected SSH_SMSG_PUBLIC_KEY');
return; return false;
} }
$anti_spoofing_cookie = $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 8); $anti_spoofing_cookie = $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 8);
@ -589,12 +672,12 @@ class Net_SSH1 {
); );
} }
$cipher = isset($this->supported_ciphers[$cipher]) ? $cipher : NET_SSH1_CIPHER_3DES; $cipher = isset($this->supported_ciphers[$this->cipher]) ? $this->cipher : NET_SSH1_CIPHER_3DES;
$data = pack('C2a*na*N', NET_SSH1_CMSG_SESSION_KEY, $cipher, $anti_spoofing_cookie, 8 * strlen($double_encrypted_session_key), $double_encrypted_session_key, 0); $data = pack('C2a*na*N', NET_SSH1_CMSG_SESSION_KEY, $cipher, $anti_spoofing_cookie, 8 * strlen($double_encrypted_session_key), $double_encrypted_session_key, 0);
if (!$this->_send_binary_packet($data)) { if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_SESSION_KEY'); user_error('Error sending SSH_CMSG_SESSION_KEY');
return; return false;
} }
switch ($cipher) { switch ($cipher) {
@ -603,7 +686,7 @@ class Net_SSH1 {
// break; // break;
case NET_SSH1_CIPHER_DES: case NET_SSH1_CIPHER_DES:
if (!class_exists('Crypt_DES')) { if (!class_exists('Crypt_DES')) {
require_once('Crypt/DES.php'); include_once 'Crypt/DES.php';
} }
$this->crypto = new Crypt_DES(); $this->crypto = new Crypt_DES();
$this->crypto->disablePadding(); $this->crypto->disablePadding();
@ -612,7 +695,7 @@ class Net_SSH1 {
break; break;
case NET_SSH1_CIPHER_3DES: case NET_SSH1_CIPHER_3DES:
if (!class_exists('Crypt_TripleDES')) { if (!class_exists('Crypt_TripleDES')) {
require_once('Crypt/TripleDES.php'); include_once 'Crypt/TripleDES.php';
} }
$this->crypto = new Crypt_TripleDES(CRYPT_DES_MODE_3CBC); $this->crypto = new Crypt_TripleDES(CRYPT_DES_MODE_3CBC);
$this->crypto->disablePadding(); $this->crypto->disablePadding();
@ -621,7 +704,7 @@ class Net_SSH1 {
break; break;
//case NET_SSH1_CIPHER_RC4: //case NET_SSH1_CIPHER_RC4:
// if (!class_exists('Crypt_RC4')) { // if (!class_exists('Crypt_RC4')) {
// require_once('Crypt/RC4.php'); // include_once 'Crypt/RC4.php';
// } // }
// $this->crypto = new Crypt_RC4(); // $this->crypto = new Crypt_RC4();
// $this->crypto->enableContinuousBuffer(); // $this->crypto->enableContinuousBuffer();
@ -633,10 +716,12 @@ class Net_SSH1 {
if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) { if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
user_error('Expected SSH_SMSG_SUCCESS'); user_error('Expected SSH_SMSG_SUCCESS');
return; return false;
} }
$this->bitmap = NET_SSH1_MASK_CONSTRUCTOR; $this->bitmap = NET_SSH1_MASK_CONNECTED;
return true;
} }
/** /**
@ -650,6 +735,13 @@ class Net_SSH1 {
function login($username, $password = '') function login($username, $password = '')
{ {
if (!($this->bitmap & NET_SSH1_MASK_CONSTRUCTOR)) { if (!($this->bitmap & NET_SSH1_MASK_CONSTRUCTOR)) {
$this->bitmap |= NET_SSH1_MASK_CONSTRUCTOR;
if (!$this->_connect()) {
return false;
}
}
if (!($this->bitmap & NET_SSH1_MASK_CONNECTED)) {
return false; return false;
} }
@ -1017,7 +1109,7 @@ class Net_SSH1 {
if ($this->curTimeout) { if ($this->curTimeout) {
$read = array($this->fsock); $read = array($this->fsock);
$write = $except = NULL; $write = $except = null;
$start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
$sec = floor($this->curTimeout); $sec = floor($this->curTimeout);
@ -1253,7 +1345,7 @@ class Net_SSH1 {
{ {
/* /*
if (!class_exists('Crypt_RSA')) { if (!class_exists('Crypt_RSA')) {
require_once('Crypt/RSA.php'); include_once 'Crypt/RSA.php';
} }
$rsa = new Crypt_RSA(); $rsa = new Crypt_RSA();
@ -1318,7 +1410,7 @@ class Net_SSH1 {
/** /**
* Returns a log of the packets that have been sent and received. * Returns a log of the packets that have been sent and received.
* *
* 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') * Returns a string if NET_SSH1_LOGGING == NET_SSH1_LOG_COMPLEX, an array if NET_SSH1_LOGGING == NET_SSH1_LOG_SIMPLE and false if !defined('NET_SSH1_LOGGING')
* *
* @access public * @access public
* @return String or Array * @return String or Array
@ -1351,8 +1443,6 @@ class Net_SSH1 {
*/ */
function _format_log($message_log, $message_number_log) function _format_log($message_log, $message_number_log)
{ {
static $boundary = ':', $long_width = 65, $short_width = 16;
$output = ''; $output = '';
for ($i = 0; $i < count($message_log); $i++) { for ($i = 0; $i < count($message_log); $i++) {
$output.= $message_number_log[$i] . "\r\n"; $output.= $message_number_log[$i] . "\r\n";
@ -1362,19 +1452,13 @@ class Net_SSH1 {
if (strlen($current_log)) { if (strlen($current_log)) {
$output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 '; $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 ';
} }
$fragment = $this->_string_shift($current_log, $short_width); $fragment = $this->_string_shift($current_log, $this->log_short_width);
$hex = substr( $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary));
preg_replace(
'#(.)#es',
'"' . $boundary . '" . str_pad(dechex(ord(substr("\\1", -1))), 2, "0", STR_PAD_LEFT)',
$fragment),
strlen($boundary)
);
// replace non ASCII printable characters with dots // replace non ASCII printable characters with dots
// http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
// also replace < with a . since < messes up the output on web browsers // also replace < with a . since < messes up the output on web browsers
$raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment); $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
$output.= str_pad($hex, $long_width - $short_width, ' ') . $raw . "\r\n"; $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n";
$j++; $j++;
} while (strlen($current_log)); } while (strlen($current_log));
$output.= "\r\n"; $output.= "\r\n";
@ -1383,6 +1467,20 @@ class Net_SSH1 {
return $output; return $output;
} }
/**
* Helper function for _format_log
*
* For use with preg_replace_callback()
*
* @param Array $matches
* @access private
* @return String
*/
function _format_log_helper($matches)
{
return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT);
}
/** /**
* Return the server key public exponent * Return the server key public exponent
* *
@ -1507,7 +1605,7 @@ class Net_SSH1 {
$this->_string_shift($message); $this->_string_shift($message);
$this->log_size+= strlen($message); $this->log_size+= strlen($message);
$this->message_log[] = $message; $this->message_log[] = $message;
while ($this->log_size > NET_SSH2_LOG_MAX_SIZE) { while ($this->log_size > NET_SSH1_LOG_MAX_SIZE) {
$this->log_size-= strlen(array_shift($this->message_log)); $this->log_size-= strlen(array_shift($this->message_log));
array_shift($this->protocol_flags_log); array_shift($this->protocol_flags_log);
} }
@ -1527,7 +1625,7 @@ class Net_SSH1 {
case NET_SSH1_LOG_REALTIME_FILE: case NET_SSH1_LOG_REALTIME_FILE:
if (!isset($this->realtime_log_file)) { if (!isset($this->realtime_log_file)) {
// PHP doesn't seem to like using constants in fopen() // PHP doesn't seem to like using constants in fopen()
$filename = NET_SSH2_LOG_REALTIME_FILE; $filename = NET_SSH1_LOG_REALTIME_FILE;
$fp = fopen($filename, 'w'); $fp = fopen($filename, 'w');
$this->realtime_log_file = $fp; $this->realtime_log_file = $fp;
} }

File diff suppressed because it is too large Load Diff

313
System/SSH/Agent.php Executable file
View File

@ -0,0 +1,313 @@
<?php
/**
* Pure-PHP ssh-agent client.
*
* PHP versions 4 and 5
*
* Here are some examples of how to use this library:
* <code>
* <?php
* include 'System/SSH/Agent.php';
* include 'Net/SSH2.php';
*
* $agent = new System_SSH_Agent();
*
* $ssh = new Net_SSH2('www.domain.tld');
* if (!$ssh->login('username', $agent)) {
* exit('Login Failed');
* }
*
* echo $ssh->exec('pwd');
* echo $ssh->exec('ls -la');
* ?>
* </code>
*
* LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @category System
* @package System_SSH_Agent
* @author Jim Wigginton <terrafrost@php.net>
* @copyright MMXIV Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
* @internal See http://api.libssh.org/rfc/PROTOCOL.agent
*/
/**#@+
* Message numbers
*
* @access private
*/
// to request SSH1 keys you have to use SSH_AGENTC_REQUEST_RSA_IDENTITIES (1)
define('SYSTEM_SSH_AGENTC_REQUEST_IDENTITIES', 11);
// this is the SSH2 response; the SSH1 response is SSH_AGENT_RSA_IDENTITIES_ANSWER (2).
define('SYSTEM_SSH_AGENT_IDENTITIES_ANSWER', 12);
define('SYSTEM_SSH_AGENT_FAILURE', 5);
// the SSH1 request is SSH_AGENTC_RSA_CHALLENGE (3)
define('SYSTEM_SSH_AGENTC_SIGN_REQUEST', 13);
// the SSH1 response is SSH_AGENT_RSA_RESPONSE (4)
define('SYSTEM_SSH_AGENT_SIGN_RESPONSE', 14);
/**#@-*/
/**
* Pure-PHP ssh-agent client identity object
*
* Instantiation should only be performed by System_SSH_Agent class.
* This could be thought of as implementing an interface that Crypt_RSA
* implements. ie. maybe a Net_SSH_Auth_PublicKey interface or something.
* The methods in this interface would be getPublicKey, setSignatureMode
* and sign since those are the methods phpseclib looks for to perform
* public key authentication.
*
* @package System_SSH_Agent
* @author Jim Wigginton <terrafrost@php.net>
* @access internal
*/
class System_SSH_Agent_Identity
{
/**
* Key Object
*
* @var Crypt_RSA
* @access private
* @see System_SSH_Agent_Identity::getPublicKey()
*/
var $key;
/**
* Key Blob
*
* @var String
* @access private
* @see System_SSH_Agent_Identity::sign()
*/
var $key_blob;
/**
* Socket Resource
*
* @var Resource
* @access private
* @see System_SSH_Agent_Identity::sign()
*/
var $fsock;
/**
* Default Constructor.
*
* @param Resource $fsock
* @return System_SSH_Agent_Identity
* @access private
*/
function System_SSH_Agent_Identity($fsock)
{
$this->fsock = $fsock;
}
/**
* Set Public Key
*
* Called by System_SSH_Agent::requestIdentities()
*
* @param Crypt_RSA $key
* @access private
*/
function setPublicKey($key)
{
$this->key = $key;
$this->key->setPublicKey();
}
/**
* Set Public Key
*
* Called by System_SSH_Agent::requestIdentities(). The key blob could be extracted from $this->key
* but this saves a small amount of computation.
*
* @param String $key_blob
* @access private
*/
function setPublicKeyBlob($key_blob)
{
$this->key_blob = $key_blob;
}
/**
* Get Public Key
*
* Wrapper for $this->key->getPublicKey()
*
* @param Integer $format optional
* @return Mixed
* @access public
*/
function getPublicKey($format = null)
{
return !isset($format) ? $this->key->getPublicKey() : $this->key->getPublicKey($format);
}
/**
* Set Signature Mode
*
* Doesn't do anything as ssh-agent doesn't let you pick and choose the signature mode. ie.
* ssh-agent's only supported mode is CRYPT_RSA_SIGNATURE_PKCS1
*
* @param Integer $mode
* @access public
*/
function setSignatureMode($mode)
{
}
/**
* Create a signature
*
* See "2.6.2 Protocol 2 private key signature request"
*
* @param String $message
* @return String
* @access public
*/
function sign($message)
{
// the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE
$packet = pack('CNa*Na*N', SYSTEM_SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, 0);
$packet = pack('Na*', strlen($packet), $packet);
if (strlen($packet) != fputs($this->fsock, $packet)) {
user_error('Connection closed during signing');
}
$length = current(unpack('N', fread($this->fsock, 4)));
$type = ord(fread($this->fsock, 1));
if ($type != SYSTEM_SSH_AGENT_SIGN_RESPONSE) {
user_error('Unable to retreive signature');
}
$signature_blob = fread($this->fsock, $length - 1);
// the only other signature format defined - ssh-dss - is the same length as ssh-rsa
// the + 12 is for the other various SSH added length fields
return substr($signature_blob, strlen('ssh-rsa') + 12);
}
}
/**
* Pure-PHP ssh-agent client identity factory
*
* requestIdentities() method pumps out System_SSH_Agent_Identity objects
*
* @package System_SSH_Agent
* @author Jim Wigginton <terrafrost@php.net>
* @access internal
*/
class System_SSH_Agent
{
/**
* Socket Resource
*
* @var Resource
* @access private
*/
var $fsock;
/**
* Default Constructor
*
* @return System_SSH_Agent
* @access public
*/
function System_SSH_Agent()
{
switch (true) {
case isset($_SERVER['SSH_AUTH_SOCK']):
$address = $_SERVER['SSH_AUTH_SOCK'];
break;
case isset($_ENV['SSH_AUTH_SOCK']):
$address = $_ENV['SSH_AUTH_SOCK'];
break;
default:
user_error('SSH_AUTH_SOCK not found');
return false;
}
$this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr);
if (!$this->fsock) {
user_error("Unable to connect to ssh-agent (Error $errno: $errstr)");
}
}
/**
* Request Identities
*
* See "2.5.2 Requesting a list of protocol 2 keys"
* Returns an array containing zero or more System_SSH_Agent_Identity objects
*
* @return Array
* @access public
*/
function requestIdentities()
{
if (!$this->fsock) {
return array();
}
$packet = pack('NC', 1, SYSTEM_SSH_AGENTC_REQUEST_IDENTITIES);
if (strlen($packet) != fputs($this->fsock, $packet)) {
user_error('Connection closed while requesting identities');
}
$length = current(unpack('N', fread($this->fsock, 4)));
$type = ord(fread($this->fsock, 1));
if ($type != SYSTEM_SSH_AGENT_IDENTITIES_ANSWER) {
user_error('Unable to request identities');
}
$identities = array();
$keyCount = current(unpack('N', fread($this->fsock, 4)));
for ($i = 0; $i < $keyCount; $i++) {
$length = current(unpack('N', fread($this->fsock, 4)));
$key_blob = fread($this->fsock, $length);
$length = current(unpack('N', fread($this->fsock, 4)));
$key_comment = fread($this->fsock, $length);
$length = current(unpack('N', substr($key_blob, 0, 4)));
$key_type = substr($key_blob, 4, $length);
switch ($key_type) {
case 'ssh-rsa':
if (!class_exists('Crypt_RSA')) {
include_once 'Crypt/RSA.php';
}
$key = new Crypt_RSA();
$key->loadKey('ssh-rsa ' . base64_encode($key_blob) . ' ' . $key_comment);
break;
case 'ssh-dss':
// not currently supported
break;
}
// resources are passed by reference by default
if (isset($key)) {
$identity = new System_SSH_Agent_Identity($this->fsock);
$identity->setPublicKey($key);
$identity->setPublicKeyBlob($key_blob);
$identities[] = $identity;
unset($key);
}
}
return $identities;
}
}

39
System/SSH_Agent.php Executable file
View File

@ -0,0 +1,39 @@
<?php
/**
* Pure-PHP ssh-agent client wrapper
*
* PHP versions 4 and 5
*
* Originally System_SSH_Agent was accessed as System/SSH_Agent.php instead of
* System/SSH/Agent.php. The problem with this is that PSR0 compatible autoloaders
* don't support that kind of directory layout hence the package being moved and
* this "alias" being created to maintain backwards compatibility.
*
* LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @category System
* @package System_SSH_Agent
* @author Jim Wigginton <terrafrost@php.net>
* @copyright MMXIV Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
* @internal See http://api.libssh.org/rfc/PROTOCOL.agent
*/
require_once 'SSH/Agent.php';