1
0
mirror of https://github.com/danog/phpseclib.git synced 2025-01-22 13:01:59 +01:00

Merge branch 'cipher-revamp'

This commit is contained in:
terrafrost 2016-03-14 22:42:51 -05:00
commit ea7d647612
18 changed files with 358 additions and 288 deletions

View File

@ -65,31 +65,32 @@ class AES extends Rijndael
* *
* @see \phpseclib\Crypt\Rijndael::setBlockLength() * @see \phpseclib\Crypt\Rijndael::setBlockLength()
* @access public * @access public
* @param int $length * @throws \BadMethodCallException anytime it's called
*/ */
function setBlockLength($length) function setBlockLength($length)
{ {
return; throw new \BadMethodCallException('The block length cannot be set for AES.');
} }
/** /**
* Sets the key length * Sets the key length
* *
* Valid key lengths are 128, 192, and 256. If the length is less than 128, it will be rounded up to * Valid key lengths are 128, 192, and 256. Set the link to bool(false) to disable a fixed key length
* 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
* *
* @see \phpseclib\Crypt\Rijndael:setKeyLength() * @see \phpseclib\Crypt\Rijndael:setKeyLength()
* @access public * @access public
* @param int $length * @param int $length
* @throws \LengthException if the key length isn't supported
*/ */
function setKeyLength($length) function setKeyLength($length)
{ {
switch ($length) { switch ($length) {
case 160: case 128:
$length = 192; case 192:
case 256:
break; break;
case 224: default:
$length = 256; throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported');
} }
parent::setKeyLength($length); parent::setKeyLength($length);
} }
@ -103,24 +104,19 @@ class AES extends Rijndael
* @see setKeyLength() * @see setKeyLength()
* @access public * @access public
* @param string $key * @param string $key
* @throws \LengthException if the key length isn't supported
*/ */
function setKey($key) function setKey($key)
{ {
parent::setKey($key); switch (strlen($key)) {
case 16:
if (!$this->explicit_key_length) { case 24:
$length = strlen($key); case 32:
switch (true) { break;
case $length <= 16: default:
$this->key_length = 16; throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported');
break;
case $length <= 24:
$this->key_length = 24;
break;
default:
$this->key_length = 32;
}
$this->_setEngine();
} }
parent::setKey($key);
} }
} }

View File

@ -139,7 +139,7 @@ abstract class Base
* @var string * @var string
* @access private * @access private
*/ */
var $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; var $key = false;
/** /**
* The Initialization Vector * The Initialization Vector
@ -429,15 +429,6 @@ abstract class Base
*/ */
var $openssl_options; var $openssl_options;
/**
* Has the key length explicitly been set or should it be derived from the key, itself?
*
* @see self::setKeyLength()
* @var bool
* @access private
*/
var $explicit_key_length = false;
/** /**
* Don't truncate / null pad key * Don't truncate / null pad key
* *
@ -448,9 +439,16 @@ abstract class Base
var $skip_key_adjustment = false; var $skip_key_adjustment = false;
/** /**
* Default Constructor. * Has the key length explicitly been set or should it be derived from the key, itself?
* *
* Determines whether or not the mcrypt extension should be used. * @see self::setKeyLength()
* @var bool
* @access private
*/
var $explicit_key_length = false;
/**
* Default Constructor.
* *
* $mode could be: * $mode could be:
* *
@ -464,32 +462,29 @@ abstract class Base
* *
* - self::MODE_OFB * - self::MODE_OFB
* *
* If not explicitly set, self::MODE_CBC will be used.
*
* @param int $mode * @param int $mode
* @access public * @access public
* @throws \InvalidArgumentException if an invalid / unsupported mode is provided
*/ */
function __construct($mode = self::MODE_CBC) function __construct($mode)
{ {
// $mode dependent settings // $mode dependent settings
switch ($mode) { switch ($mode) {
case self::MODE_ECB: case self::MODE_ECB:
case self::MODE_CBC:
$this->paddable = true; $this->paddable = true;
$this->mode = self::MODE_ECB;
break; break;
case self::MODE_CTR: case self::MODE_CTR:
case self::MODE_CFB: case self::MODE_CFB:
case self::MODE_OFB: case self::MODE_OFB:
case self::MODE_STREAM: case self::MODE_STREAM:
$this->mode = $mode; $this->paddable = false;
break; break;
case self::MODE_CBC:
default: default:
$this->paddable = true; throw new \InvalidArgumentException('No valid mode has been specified');
$this->mode = self::MODE_CBC;
} }
$this->_setEngine(); $this->mode = $mode;
// Determining whether inline crypting can be used by the cipher // Determining whether inline crypting can be used by the cipher
if ($this->use_inline_crypt !== false && function_exists('create_function')) { if ($this->use_inline_crypt !== false && function_exists('create_function')) {
@ -504,14 +499,22 @@ abstract class Base
* *
* @access public * @access public
* @param string $iv * @param string $iv
* @throws \LengthException if the IV length isn't equal to the block size
* @throws \InvalidArgumentException if an IV is provided when one shouldn't be
* @internal Can be overwritten by a sub class, but does not have to be * @internal Can be overwritten by a sub class, but does not have to be
*/ */
function setIV($iv) function setIV($iv)
{ {
switch ($this->mode) { if ($this->mode == self::MODE_ECB) {
case self::MODE_ECB: throw new \InvalidArgumentException('This mode does not require an IV.');
case self::MODE_STREAM: }
return;
if ($this->mode == self::MODE_STREAM && $this->usesIV()) {
throw new \InvalidArgumentException('This algorithm does not use an IV.');
}
if (strlen($iv) != $this->block_size) {
throw new \LengthException('Received initialization vector of size ' . strlen($iv) . ', but size ' . $this->block_size . ' is required');
} }
$this->iv = $iv; $this->iv = $iv;
@ -519,18 +522,14 @@ abstract class Base
} }
/** /**
* Sets the key length. * Returns whether or not the algorithm uses an IV
*
* Keys with explicitly set lengths need to be treated accordingly
* *
* @access public * @access public
* @param int $length * @return bool
*/ */
function setKeyLength($length) function usesIV()
{ {
$this->explicit_key_length = true; return true;
$this->changed = true;
$this->_setEngine();
} }
/** /**
@ -555,6 +554,24 @@ abstract class Base
return $this->block_size << 3; return $this->block_size << 3;
} }
/**
* Sets the key length.
*
* Keys with explicitly set lengths need to be treated accordingly
*
* @access public
* @param int $length
*/
function setKeyLength($length)
{
$this->explicit_key_length = $length >> 3;
if (is_string($this->key) && strlen($this->key) != $this->explicit_key_length) {
$this->key = false;
throw new \LengthException('Key has already been set and is not ' .$this->explicit_key_length . ' bytes long');
}
}
/** /**
* Sets the key. * Sets the key.
* *
@ -571,12 +588,12 @@ abstract class Base
*/ */
function setKey($key) function setKey($key)
{ {
if (!$this->explicit_key_length) { if ($this->explicit_key_length !== false && strlen($key) != $this->explicit_key_length) {
$this->setKeyLength(strlen($key) << 3); throw new \LengthException('Key length has already been set to ' . $this->explicit_key_length . ' bytes and this key is ' . strlen($key) . ' bytes');
$this->explicit_key_length = false;
} }
$this->key = $key; $this->key = $key;
$this->key_length = strlen($key);
$this->changed = true; $this->changed = true;
$this->_setEngine(); $this->_setEngine();
} }
@ -620,7 +637,8 @@ abstract class Base
if (isset($func_args[5])) { if (isset($func_args[5])) {
$dkLen = $func_args[5]; $dkLen = $func_args[5];
} else { } else {
$dkLen = $method == 'pbkdf1' ? 2 * $this->key_length : $this->key_length; $key_length = $this->explicit_key_length !== false ? $this->explicit_key_length : $this->key_length;
$dkLen = $method == 'pbkdf1' ? 2 * $key_length : $key_length;
} }
switch (true) { switch (true) {
@ -987,14 +1005,13 @@ abstract class Base
* @access public * @access public
* @param string $ciphertext * @param string $ciphertext
* @return string $plaintext * @return string $plaintext
* @throws \LengthException if we're inside a block cipher and the ciphertext length is not a multiple of the block size
* @internal Could, but not must, extend by the child Crypt_* class * @internal Could, but not must, extend by the child Crypt_* class
*/ */
function decrypt($ciphertext) function decrypt($ciphertext)
{ {
if ($this->paddable) { if ($this->paddable) {
// we pad with chr(0) since that's what mcrypt_generic does. to quote from {@link http://www.php.net/function.mcrypt-generic}: throw new \LengthException('The ciphertext length (' . strlen($ciphertext) . ') needs to be a multiple of the block size (' . $this->block_length . ')');
// "The data is padded with "\0" to make sure the length of the data is n * blocksize."
$ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($this->block_size - strlen($ciphertext) % $this->block_size) % $this->block_size, chr(0));
} }
if ($this->engine === self::ENGINE_OPENSSL) { if ($this->engine === self::ENGINE_OPENSSL) {
@ -1888,13 +1905,7 @@ abstract class Base
throw new \UnexpectedValueException('No IV has been defined'); throw new \UnexpectedValueException('No IV has been defined');
} }
// mcrypt's handling of invalid's $iv: $this->encryptIV = $this->decryptIV = $this->iv;
// $this->encryptIV = $this->decryptIV = strlen($this->iv) == $this->block_size ? $this->iv : str_repeat("\0", $this->block_size);
$this->encryptIV = $this->decryptIV = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, "\0");
if (!$this->skip_key_adjustment) {
$this->key = str_pad(substr($this->key, 0, $this->key_length), $this->key_length, "\0");
}
} }
/** /**

View File

@ -283,6 +283,22 @@ class Blowfish extends Base
*/ */
var $key_length = 16; var $key_length = 16;
/**
* Default Constructor.
*
* @param int $mode
* @access public
* @throws \InvalidArgumentException if an invalid / unsupported mode is provided
*/
function __construct($mode)
{
if ($mode == self::MODE_STREAM) {
throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode');
}
parent::__construct($mode);
}
/** /**
* Sets the key length. * Sets the key length.
* *
@ -293,14 +309,12 @@ class Blowfish extends Base
*/ */
function setKeyLength($length) function setKeyLength($length)
{ {
if ($length < 32) { if ($length < 32 || $length > 448) {
$this->key_length = 7; throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes between 32 and 448 bits are supported');
} elseif ($length > 448) {
$this->key_length = 56;
} else {
$this->key_length = $length >> 3;
} }
$this->key_length = $length >> 3;
parent::setKeyLength($length); parent::setKeyLength($length);
} }

View File

@ -578,6 +578,22 @@ class DES extends Base
0x00000820, 0x00020020, 0x08000000, 0x08020800 0x00000820, 0x00020020, 0x08000000, 0x08020800
); );
/**
* Default Constructor.
*
* @param int $mode
* @access public
* @throws \InvalidArgumentException if an invalid / unsupported mode is provided
*/
function __construct($mode)
{
if ($mode == self::MODE_STREAM) {
throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode');
}
parent::__construct($mode);
}
/** /**
* Test for engine validity * Test for engine validity
* *
@ -603,24 +619,18 @@ class DES extends Base
/** /**
* Sets the key. * Sets the key.
* *
* Keys can be of any length. DES, itself, uses 64-bit keys (eg. strlen($key) == 8), however, we * Keys must be 64-bits long or 8 bytes long.
* only use the first eight, if $key has more then eight characters in it, and pad $key with the
* null byte if it is less then eight characters long.
* *
* 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.
*
* @see \phpseclib\Crypt\Base::setKey() * @see \phpseclib\Crypt\Base::setKey()
* @access public * @access public
* @param string $key * @param string $key
*/ */
function setKey($key) function setKey($key)
{ {
// We check/cut here only up to max length of the key. if (!($this instanceof TripleDES) && strlen($key) != 8) {
// Key padding to the proper length will be done in _setupKey() throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of size 8 are supported');
if (strlen($key) > $this->key_length_max) {
$key = substr($key, 0, $this->key_length_max);
} }
// Sets the key // Sets the key

View File

@ -259,6 +259,22 @@ class RC2 extends Base
0x70, 0x02, 0xC2, 0x1E, 0xB8, 0x0A, 0xFC, 0xE6 0x70, 0x02, 0xC2, 0x1E, 0xB8, 0x0A, 0xFC, 0xE6
); );
/**
* Default Constructor.
*
* @param int $mode
* @access public
* @throws \InvalidArgumentException if an invalid / unsupported mode is provided
*/
function __construct($mode)
{
if ($mode == self::MODE_STREAM) {
throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode');
}
parent::__construct($mode);
}
/** /**
* Test for engine validity * Test for engine validity
* *
@ -292,12 +308,15 @@ class RC2 extends Base
* *
* @access public * @access public
* @param int $length in bits * @param int $length in bits
* @throws \LengthException if the key length isn't supported
*/ */
function setKeyLength($length) function setKeyLength($length)
{ {
if ($length >= 1 && $length <= 1024) { if ($length < 1 || $length > 1024) {
$this->default_key_length = $length; throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported');
} }
$this->default_key_length = $length;
} }
/** /**
@ -326,16 +345,20 @@ class RC2 extends Base
* @access public * @access public
* @param string $key * @param string $key
* @param int $t1 optional Effective key length in bits. * @param int $t1 optional Effective key length in bits.
* @throws \LengthException if the key length isn't supported
*/ */
function setKey($key, $t1 = 0) function setKey($key, $t1 = false)
{ {
$this->orig_key = $key; $this->orig_key = $key;
if ($t1 <= 0) { if ($t1 === false) {
$t1 = $this->default_key_length; $t1 = $this->default_key_length;
} elseif ($t1 > 1024) {
$t1 = 1024;
} }
if ($t1 < 1 || $t1 > 1024) {
throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported');
}
$this->current_key_length = $t1; $this->current_key_length = $t1;
// Key byte count should be 1..128. // Key byte count should be 1..128.
$key = strlen($key) ? substr($key, 0, 128) : "\x00"; $key = strlen($key) ? substr($key, 0, 128) : "\x00";

View File

@ -121,8 +121,6 @@ class RC4 extends Base
/** /**
* Default Constructor. * Default Constructor.
* *
* Determines whether or not the mcrypt extension should be used.
*
* @see \phpseclib\Crypt\Base::__construct() * @see \phpseclib\Crypt\Base::__construct()
* @return \phpseclib\Crypt\RC4 * @return \phpseclib\Crypt\RC4
* @access public * @access public
@ -165,26 +163,14 @@ class RC4 extends Base
} }
/** /**
* Dummy function. * RC4 does not use an IV
* *
* Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1].
* If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before
* calling setKey().
*
* [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way. Since, in that protocol,
* the IV's are relatively easy to predict, an attack described by
* {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir}
* can be used to quickly guess at the rest of the key. The following links elaborate:
*
* {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009}
* {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack}
*
* @param string $iv
* @see self::setKey()
* @access public * @access public
* @return bool
*/ */
function setIV($iv) function usesIV()
{ {
return false;
} }
/** /**
@ -194,20 +180,38 @@ class RC4 extends Base
* *
* @access public * @access public
* @param int $length * @param int $length
* @throws \LengthException if the key length is invalid
*/ */
function setKeyLength($length) function setKeyLength($length)
{ {
if ($length < 8) { if ($length < 8 || $length > 2048) {
$this->key_length = 1; throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 256 bytes are supported');
} elseif ($length > 2048) {
$this->key_length = 248;
} else {
$this->key_length = $length >> 3;
} }
$this->key_length = $length >> 3;
parent::setKeyLength($length); parent::setKeyLength($length);
} }
/**
* Sets the key length
*
* Keys can be between 1 and 256 bytes long.
*
* @access public
* @param int $length
* @throws \LengthException if the key length is invalid
*/
function setKey($key)
{
$length = strlen($key);
if ($length < 1 || $length > 256) {
throw new \LengthException('Key size of ' . $length . ' bytes is not supported by RC4. Keys must be between 1 and 256 bytes long');
}
parent::setKey($length);
}
/** /**
* Encrypts a message. * Encrypts a message.
* *

View File

@ -114,7 +114,7 @@ class PKCS1 extends PKCS
if (!empty($password) || is_string($password)) { if (!empty($password) || is_string($password)) {
$cipher = self::getEncryptionObject(self::$defaultEncryptionAlgorithm); $cipher = self::getEncryptionObject(self::$defaultEncryptionAlgorithm);
$iv = Random::string($cipher->getBlockLength() >> 3); $iv = Random::string($cipher->getBlockLength() >> 3);
$cipher->setKey(self::generateSymmetricKey($password, $iv, $cipher->getKeyLength())); $cipher->setKey(self::generateSymmetricKey($password, $iv, $cipher->getKeyLength() >> 3));
$cipher->setIV($iv); $cipher->setIV($iv);
$iv = strtoupper(bin2hex($iv)); $iv = strtoupper(bin2hex($iv));
$RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .

View File

@ -168,11 +168,26 @@ class Rijndael extends Base
*/ */
var $kl; var $kl;
/**
* Default Constructor.
*
* @param int $mode
* @access public
* @throws \InvalidArgumentException if an invalid / unsupported mode is provided
*/
function __construct($mode)
{
if ($mode == self::MODE_STREAM) {
throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode');
}
parent::__construct($mode);
}
/** /**
* Sets the key length. * Sets the key length.
* *
* Valid key lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to * Valid key lengths are 128, 160, 192, 224, and 256.
* 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
* *
* Note: phpseclib extends Rijndael (and AES) for using 160- and 224-bit keys but they are officially not defined * Note: phpseclib extends Rijndael (and AES) for using 160- and 224-bit keys but they are officially not defined
* and the most (if not all) implementations are not able using 160/224-bit keys but round/pad them up to * and the most (if not all) implementations are not able using 160/224-bit keys but round/pad them up to
@ -186,49 +201,75 @@ class Rijndael extends Base
* This results then in slower encryption. * This results then in slower encryption.
* *
* @access public * @access public
* @throws \LengthException if the key length is invalid
* @param int $length * @param int $length
*/ */
function setKeyLength($length) function setKeyLength($length)
{ {
switch (true) { switch ($length) {
case $length <= 128: case 128:
$this->key_length = 16; case 160:
break; case 192:
case $length <= 160: case 224:
$this->key_length = 20; case 256:
break; $this->key_length = $length >> 3;
case $length <= 192:
$this->key_length = 24;
break;
case $length <= 224:
$this->key_length = 28;
break; break;
default: default:
$this->key_length = 32; throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported');
} }
parent::setKeyLength($length); parent::setKeyLength($length);
} }
/**
* Sets the key.
*
* Rijndael supports five different key lengths
*
* @see setKeyLength()
* @access public
* @param string $key
* @throws \LengthException if the key length isn't supported
*/
function setKey($key)
{
switch (strlen($key)) {
case 16:
case 20:
case 24:
case 28:
case 32:
break;
default:
throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 20, 24, 28 or 32 are supported');
}
parent::setKey($key);
}
/** /**
* Sets the block length * Sets the block length
* *
* Valid block lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to * Valid block lengths are 128, 160, 192, 224, and 256.
* 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
* *
* @access public * @access public
* @param int $length * @param int $length
*/ */
function setBlockLength($length) function setBlockLength($length)
{ {
$length >>= 5; switch ($length) {
if ($length > 8) { case 128:
$length = 8; case 160:
} elseif ($length < 4) { case 192:
$length = 4; case 224:
case 256:
break;
default:
throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported');
} }
$this->Nb = $length;
$this->block_size = $length << 2; $this->Nb = $length >> 5;
$this->block_size = $length >> 3;
$this->changed = true; $this->changed = true;
$this->_setEngine(); $this->_setEngine();
} }

View File

@ -128,7 +128,7 @@ class TripleDES extends DES
/** /**
* Default Constructor. * Default Constructor.
* *
* Determines whether or not the mcrypt extension should be used. * Determines whether or not the mcrypt or OpenSSL extensions should be used.
* *
* $mode could be: * $mode could be:
* *
@ -142,16 +142,14 @@ class TripleDES extends DES
* *
* - \phpseclib\Crypt\Base::MODE_OFB * - \phpseclib\Crypt\Base::MODE_OFB
* *
* - \phpseclib\Crypt\TripleDES::MODE_3CBC * - \phpseclib\Crypt\TripleDES::MODE_3CB
*
* If not explicitly set, \phpseclib\Crypt\Base::MODE_CBC will be used.
* *
* @see \phpseclib\Crypt\DES::__construct() * @see \phpseclib\Crypt\DES::__construct()
* @see \phpseclib\Crypt\Base::__construct() * @see \phpseclib\Crypt\Base::__construct()
* @param int $mode * @param int $mode
* @access public * @access public
*/ */
function __construct($mode = Base::MODE_CBC) function __construct($mode)
{ {
switch ($mode) { switch ($mode) {
// In case of self::MODE_3CBC, we init as CRYPT_DES_MODE_CBC // In case of self::MODE_3CBC, we init as CRYPT_DES_MODE_CBC
@ -200,10 +198,9 @@ class TripleDES extends DES
} }
/** /**
* Sets the initialization vector. (optional) * Sets the initialization vector.
* *
* SetIV is not required when \phpseclib\Crypt\Base::MODE_ECB is being used. If not explicitly set, it'll be assumed * SetIV is not required when \phpseclib\Crypt\Base::MODE_ECB is being used.
* to be all zero's.
* *
* @see \phpseclib\Crypt\Base::setIV() * @see \phpseclib\Crypt\Base::setIV()
* @access public * @access public
@ -222,24 +219,23 @@ class TripleDES extends DES
/** /**
* Sets the key length. * Sets the key length.
* *
* Valid key lengths are 64, 128 and 192 * Valid key lengths are 128 and 192 bits.
*
* If you want to use a 64-bit key use DES.php
* *
* @see \phpseclib\Crypt\Base:setKeyLength() * @see \phpseclib\Crypt\Base:setKeyLength()
* @access public * @access public
* @throws \LengthException if the key length is invalid
* @param int $length * @param int $length
*/ */
function setKeyLength($length) function setKeyLength($length)
{ {
$length >>= 3; switch ($length) {
switch (true) { case 128:
case $length <= 8: case 192:
$this->key_length = 8;
break;
case $length <= 16:
$this->key_length = 16;
break; break;
default: default:
$this->key_length = 24; throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128 or 192 bits are supported');
} }
parent::setKeyLength($length); parent::setKeyLength($length);
@ -248,35 +244,37 @@ class TripleDES extends DES
/** /**
* Sets the key. * Sets the key.
* *
* Keys can be of any length. Triple DES, itself, can use 128-bit (eg. strlen($key) == 16) or * Triple DES can use 128-bit (eg. strlen($key) == 16) or 192-bit (eg. strlen($key) == 24) keys.
* 192-bit (eg. strlen($key) == 24) keys. This function pads and truncates $key as appropriate.
* *
* 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 null bytes.
*
* @access public * @access public
* @see \phpseclib\Crypt\DES::setKey() * @see \phpseclib\Crypt\DES::setKey()
* @see \phpseclib\Crypt\Base::setKey() * @see \phpseclib\Crypt\Base::setKey()
* @throws \LengthException if the key length is invalid
* @param string $key * @param string $key
*/ */
function setKey($key) function setKey($key)
{ {
$length = $this->explicit_key_length ? $this->key_length : strlen($key); if ($this->explicit_key_length !== false && strlen($key) != $this->explicit_key_length) {
if ($length > 8) { throw new \LengthException('Key length has already been set to ' . $this->explicit_key_length . ' bytes and this key is ' . strlen($key) . ' bytes');
$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:
// http://php.net/function.mcrypt-encrypt#47973
$key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24);
} else {
$key = str_pad($key, 8, chr(0));
} }
parent::setKey($key);
// And in case of self::MODE_3CBC: switch (strlen($key)) {
// if key <= 64bits we not need the 3 $des to work, case 16:
// because we will then act as regular DES-CBC with just a <= 64bit key. $key.= substr($key, 0, 8);
// So only if the key > 64bits (> 8 bytes) we will call setKey() for the 3 $des. case 24:
break;
default:
throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16 or 24 are supported');
}
// copied from Base::setKey()
$this->key = $key;
$this->key_length = strlen($key);
$this->changed = true;
$this->_setEngine();
if ($this->mode_3cbc && $length > 8) { 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));

View File

@ -368,6 +368,22 @@ class Twofish extends Base
*/ */
var $key_length = 16; var $key_length = 16;
/**
* Default Constructor.
*
* @param int $mode
* @access public
* @throws \InvalidArgumentException if an invalid / unsupported mode is provided
*/
function __construct($mode)
{
if ($mode == self::MODE_STREAM) {
throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode');
}
parent::__construct($mode);
}
/** /**
* Sets the key length. * Sets the key length.
* *
@ -378,20 +394,42 @@ class Twofish extends Base
*/ */
function setKeyLength($length) function setKeyLength($length)
{ {
switch (true) { switch ($length) {
case $length <= 128: case 128:
$this->key_length = 16; case 192:
break; case 256:
case $length <= 192:
$this->key_length = 24;
break; break;
default: default:
$this->key_length = 32; throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported');
} }
parent::setKeyLength($length); parent::setKeyLength($length);
} }
/**
* Sets the key.
*
* Rijndael supports five different key lengths
*
* @see setKeyLength()
* @access public
* @param string $key
* @throws \LengthException if the key length isn't supported
*/
function setKey($key)
{
switch (strlen($key)) {
case 16:
case 24:
case 32:
break;
default:
throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported');
}
parent::setKey($key);
}
/** /**
* Setup the key (expansion) * Setup the key (expansion)
* *

View File

@ -658,16 +658,18 @@ class SSH1
// $this->crypto = new \phpseclib\Crypt\Null(); // $this->crypto = new \phpseclib\Crypt\Null();
// break; // break;
case self::CIPHER_DES: case self::CIPHER_DES:
$this->crypto = new DES(); $this->crypto = new DES(DES::MODE_CBC);
$this->crypto->disablePadding(); $this->crypto->disablePadding();
$this->crypto->enableContinuousBuffer(); $this->crypto->enableContinuousBuffer();
$this->crypto->setKey(substr($session_key, 0, 8)); $this->crypto->setKey(substr($session_key, 0, 8));
$this->crypto->setIV(str_repeat("\0", 8));
break; break;
case self::CIPHER_3DES: case self::CIPHER_3DES:
$this->crypto = new TripleDES(TripleDES::MODE_3CBC); $this->crypto = new TripleDES(TripleDES::MODE_3CBC);
$this->crypto->disablePadding(); $this->crypto->disablePadding();
$this->crypto->enableContinuousBuffer(); $this->crypto->enableContinuousBuffer();
$this->crypto->setKey(substr($session_key, 0, 24)); $this->crypto->setKey(substr($session_key, 0, 24));
$this->crypto->setIV(str_repeat("\0", 8));
break; break;
//case self::CIPHER_RC4: //case self::CIPHER_RC4:
// $this->crypto = new RC4(); // $this->crypto = new RC4();

View File

@ -1616,11 +1616,13 @@ class SSH2
$this->encrypt->enableContinuousBuffer(); $this->encrypt->enableContinuousBuffer();
$this->encrypt->disablePadding(); $this->encrypt->disablePadding();
$iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id); if ($this->encrypt->usesIV()) {
while ($this->encrypt_block_size > strlen($iv)) { $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id);
$iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); while ($this->encrypt_block_size > strlen($iv)) {
$iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
}
$this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
} }
$this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
$key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id); $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id);
while ($encryptKeyLength > strlen($key)) { while ($encryptKeyLength > strlen($key)) {
@ -1640,11 +1642,13 @@ class SSH2
$this->decrypt->enableContinuousBuffer(); $this->decrypt->enableContinuousBuffer();
$this->decrypt->disablePadding(); $this->decrypt->disablePadding();
$iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id); if ($this->decrypt->usesIV()) {
while ($this->decrypt_block_size > strlen($iv)) { $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id);
$iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); while ($this->decrypt_block_size > strlen($iv)) {
$iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
}
$this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
} }
$this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
$key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id); $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id);
while ($decryptKeyLength > strlen($key)) { while ($decryptKeyLength > strlen($key)) {

View File

@ -47,13 +47,13 @@ abstract class Unit_Crypt_AES_TestCase extends PhpseclibTestCase
':-):-):-):-):-):-)', // https://github.com/phpseclib/phpseclib/pull/43 ':-):-):-):-):-):-)', // https://github.com/phpseclib/phpseclib/pull/43
); );
$ivs = array( $ivs = array(
'', str_repeat("\0", 16),
'test123', str_pad('test123', 16, "\0"),
); );
$keys = array( $keys = array(
'', str_repeat("\0", 16),
':-8', // https://github.com/phpseclib/phpseclib/pull/43 str_pad(':-8', 16, "\0"), // https://github.com/phpseclib/phpseclib/pull/43
'FOOBARZ', str_pad('FOOBARZ', 16, "\0"),
); );
$result = array(); $result = array();
@ -100,7 +100,7 @@ abstract class Unit_Crypt_AES_TestCase extends PhpseclibTestCase
// this test case is from the following URL: // this test case is from the following URL:
// https://web.archive.org/web/20070209120224/http://fp.gladman.plus.com/cryptography_technology/rijndael/aesdvec.zip // https://web.archive.org/web/20070209120224/http://fp.gladman.plus.com/cryptography_technology/rijndael/aesdvec.zip
$aes = new Rijndael(); $aes = new Rijndael(Base::MODE_CBC);
$aes->setPreferredEngine($this->engine); $aes->setPreferredEngine($this->engine);
$aes->disablePadding(); $aes->disablePadding();
$aes->setKey(pack('H*', '2b7e151628aed2a6abf7158809cf4f3c762e7160')); // 160-bit key. Valid in Rijndael. $aes->setKey(pack('H*', '2b7e151628aed2a6abf7158809cf4f3c762e7160')); // 160-bit key. Valid in Rijndael.
@ -112,15 +112,16 @@ abstract class Unit_Crypt_AES_TestCase extends PhpseclibTestCase
/** /**
* @group github451 * @group github451
* @expectedException \LengthException
*/ */
public function testKeyPaddingAES() public function testKeyPaddingAES()
{ {
// same as the above - just with a different ciphertext // same as the above - just with a different ciphertext
$aes = new AES(); $aes = new AES(Base::MODE_CBC);
$aes->setPreferredEngine($this->engine); $aes->setPreferredEngine($this->engine);
$aes->disablePadding(); $aes->disablePadding();
$aes->setKey(pack('H*', '2b7e151628aed2a6abf7158809cf4f3c762e7160')); // 160-bit key. AES should null pad to 192-bits $aes->setKey(pack('H*', '2b7e151628aed2a6abf7158809cf4f3c762e7160')); // 160-bit key. supported by Rijndael - not AES
$aes->setIV(str_repeat("\0", 16)); $aes->setIV(str_repeat("\0", 16));
$this->_checkEngine($aes); $this->_checkEngine($aes);
$ciphertext = $aes->encrypt(pack('H*', '3243f6a8885a308d313198a2e0370734')); $ciphertext = $aes->encrypt(pack('H*', '3243f6a8885a308d313198a2e0370734'));
@ -266,7 +267,7 @@ abstract class Unit_Crypt_AES_TestCase extends PhpseclibTestCase
// from http://csrc.nist.gov/groups/STM/cavp/documents/aes/AESAVS.pdf#page=16 // from http://csrc.nist.gov/groups/STM/cavp/documents/aes/AESAVS.pdf#page=16
public function testGFSBox128() public function testGFSBox128()
{ {
$aes = new AES(); $aes = new AES(Base::MODE_CBC);
$aes->setKey(pack('H*', '00000000000000000000000000000000')); $aes->setKey(pack('H*', '00000000000000000000000000000000'));
$aes->setIV(pack('H*', '00000000000000000000000000000000')); $aes->setIV(pack('H*', '00000000000000000000000000000000'));
@ -293,7 +294,7 @@ abstract class Unit_Crypt_AES_TestCase extends PhpseclibTestCase
public function testGFSBox192() public function testGFSBox192()
{ {
$aes = new AES(); $aes = new AES(Base::MODE_CBC);
$aes->setKey(pack('H*', '000000000000000000000000000000000000000000000000')); $aes->setKey(pack('H*', '000000000000000000000000000000000000000000000000'));
$aes->setIV(pack('H*', '00000000000000000000000000000000')); $aes->setIV(pack('H*', '00000000000000000000000000000000'));
@ -318,7 +319,7 @@ abstract class Unit_Crypt_AES_TestCase extends PhpseclibTestCase
public function testGFSBox256() public function testGFSBox256()
{ {
$aes = new AES(); $aes = new AES(Base::MODE_CBC);
$aes->setKey(pack('H*', '00000000000000000000000000000000' . '00000000000000000000000000000000')); $aes->setKey(pack('H*', '00000000000000000000000000000000' . '00000000000000000000000000000000'));
$aes->setIV(pack('H*', '00000000000000000000000000000000')); $aes->setIV(pack('H*', '00000000000000000000000000000000'));
@ -341,20 +342,23 @@ abstract class Unit_Crypt_AES_TestCase extends PhpseclibTestCase
public function testGetKeyLengthDefault() public function testGetKeyLengthDefault()
{ {
$aes = new AES(); $aes = new AES(Base::MODE_CBC);
$this->assertSame($aes->getKeyLength(), 128); $this->assertSame($aes->getKeyLength(), 128);
} }
public function testGetKeyLengthWith192BitKey() public function testGetKeyLengthWith192BitKey()
{ {
$aes = new AES(); $aes = new AES(Base::MODE_CBC);
$aes->setKey(str_repeat('a', 24)); $aes->setKey(str_repeat('a', 24));
$this->assertSame($aes->getKeyLength(), 192); $this->assertSame($aes->getKeyLength(), 192);
} }
/**
* @expectedException \LengthException
*/
public function testSetKeyLengthWithLargerKey() public function testSetKeyLengthWithLargerKey()
{ {
$aes = new AES(); $aes = new AES(Base::MODE_CBC);
$aes->setKeyLength(128); $aes->setKeyLength(128);
$aes->setKey(str_repeat('a', 24)); $aes->setKey(str_repeat('a', 24));
$aes->setIV(str_repeat("\0", 16)); $aes->setIV(str_repeat("\0", 16));
@ -364,9 +368,12 @@ abstract class Unit_Crypt_AES_TestCase extends PhpseclibTestCase
$this->assertSame($aes->getKeyLength(), 128); $this->assertSame($aes->getKeyLength(), 128);
} }
/**
* @expectedException \LengthException
*/
public function testSetKeyLengthWithSmallerKey() public function testSetKeyLengthWithSmallerKey()
{ {
$aes = new AES(); $aes = new AES(Base::MODE_CBC);
$aes->setKeyLength(256); $aes->setKeyLength(256);
$aes->setKey(str_repeat('a', 16)); $aes->setKey(str_repeat('a', 16));
$aes->setIV(str_repeat("\0", 16)); $aes->setIV(str_repeat("\0", 16));

View File

@ -73,7 +73,7 @@ class Unit_Crypt_BlowfishTest extends PhpseclibTestCase
*/ */
public function testVectors($engine, $engineName, $key, $plaintext, $expected) public function testVectors($engine, $engineName, $key, $plaintext, $expected)
{ {
$bf = new Blowfish(); $bf = new Blowfish(Blowfish::MODE_CBC);
$bf->setKey($key); $bf->setKey($key);
$bf->setIV(str_repeat("\0", $bf->getBlockLength() >> 3)); $bf->setIV(str_repeat("\0", $bf->getBlockLength() >> 3));
if (!$bf->isValidEngine($engine)) { if (!$bf->isValidEngine($engine)) {

View File

@ -1,78 +0,0 @@
<?php
/**
* @author Andreas Fischer <bantu@phpbb.com>
* @copyright MMXIII Andreas Fischer
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*/
use phpseclib\Crypt\Base;
use phpseclib\Crypt\DES;
// the AES tests establish the correctness of the modes of operation. this test is inteded to establish the consistency of
// key and iv padding between the multiple engines
class Unit_Crypt_DESTest extends PhpseclibTestCase
{
public function testEncryptPadding()
{
$des = new DES(Base::MODE_CBC);
$des->setKey('d');
$des->setIV('d');
$des->setPreferredEngine(Base::ENGINE_INTERNAL);
$result = pack('H*', '3e7613642049af1e');
$internal = $des->encrypt('d');
$this->assertEquals($result, $internal, 'Failed asserting that the internal engine produced the correct result');
$des->setPreferredEngine(Base::ENGINE_MCRYPT);
if ($des->getEngine() == Base::ENGINE_MCRYPT) {
$mcrypt = $des->encrypt('d');
$this->assertEquals($result, $mcrypt, 'Failed asserting that the mcrypt engine produced the correct result');
} else {
self::markTestSkipped('Unable to initialize mcrypt engine');
}
$des->setPreferredEngine(Base::ENGINE_OPENSSL);
if ($des->getEngine() == Base::ENGINE_OPENSSL) {
$openssl = $des->encrypt('d');
$this->assertEquals($result, $openssl, 'Failed asserting that the OpenSSL engine produced the correct result');
} else {
self::markTestSkipped('Unable to initialize OpenSSL engine');
}
}
// phpseclib null pads ciphertext's if they're not long enough and you're in ecb / cbc mode. this silent failure mode is consistent
// with mcrypt's behavior. maybe throwing an exception would be better but whatever. this test is more intended to establish consistent
// behavior between the various engine's
public function testDecryptPadding()
{
$des = new DES(Base::MODE_CBC);
$des->disablePadding();
// when the key and iv are not specified they should be null padded
//$des->setKey();
$des->setIV('');
$des->setPreferredEngine(Base::ENGINE_INTERNAL);
$internal = $des->decrypt('d');
$result = pack('H*', '79b305d1ce555221');
$this->assertEquals($result, $internal, 'Failed asserting that the internal engine produced the correct result');
$des->setPreferredEngine(Base::ENGINE_MCRYPT);
if ($des->getEngine() == Base::ENGINE_MCRYPT) {
$mcrypt = $des->decrypt('d');
$this->assertEquals($result, $mcrypt, 'Failed asserting that the mcrypt engine produced the correct result');
} else {
self::markTestSkipped('Unable to initialize mcrypt engine');
}
$des->setPreferredEngine(Base::ENGINE_OPENSSL);
if ($des->getEngine() == Base::ENGINE_OPENSSL) {
$openssl = $des->decrypt('d');
$this->assertEquals($result, $openssl, 'Failed asserting that the OpenSSL engine produced the correct result');
} else {
self::markTestSkipped('Unable to initialize OpenSSL engine');
}
}
}

View File

@ -110,7 +110,7 @@ class Unit_Crypt_RC2Test extends PhpseclibTestCase
*/ */
public function testVectors($engine, $engineName, $key, $keyLen, $plaintext, $ciphertext) public function testVectors($engine, $engineName, $key, $keyLen, $plaintext, $ciphertext)
{ {
$rc2 = new RC2(); $rc2 = new RC2(RC2::MODE_CBC);
$rc2->disablePadding(); $rc2->disablePadding();
$rc2->setKeyLength($keyLen); $rc2->setKeyLength($keyLen);
$rc2->setKey(pack('H*', $key)); // could also do $rc2->setKey(pack('H*', $key), $keyLen) $rc2->setKey(pack('H*', $key)); // could also do $rc2->setKey(pack('H*', $key), $keyLen)

View File

@ -104,7 +104,7 @@ class Unit_Crypt_TripleDESTest extends PhpseclibTestCase
*/ */
public function testVectors($engine, $engineName, $key, $plaintext, $expected) public function testVectors($engine, $engineName, $key, $plaintext, $expected)
{ {
$des = new TripleDES(); $des = new TripleDES(TripleDES::MODE_CBC);
if (!$des->isValidEngine($engine)) { if (!$des->isValidEngine($engine)) {
self::markTestSkipped('Unable to initialize ' . $engineName . ' engine'); self::markTestSkipped('Unable to initialize ' . $engineName . ' engine');
} }
@ -156,7 +156,7 @@ class Unit_Crypt_TripleDESTest extends PhpseclibTestCase
*/ */
public function testVectorsWithIV($engine, $engineName, $key, $iv, $plaintext, $expected) public function testVectorsWithIV($engine, $engineName, $key, $iv, $plaintext, $expected)
{ {
$des = new TripleDES(); $des = new TripleDES(TripleDES::MODE_CBC);
if (!$des->isValidEngine($engine)) { if (!$des->isValidEngine($engine)) {
self::markTestSkipped('Unable to initialize ' . $engineName . ' engine'); self::markTestSkipped('Unable to initialize ' . $engineName . ' engine');
} }

View File

@ -19,7 +19,7 @@ class Unit_Crypt_TwofishTest extends PhpseclibTestCase
); );
foreach ($engines as $engine => $name) { foreach ($engines as $engine => $name) {
$tf = new Twofish(); $tf = new Twofish(Twofish::MODE_CBC);
$tf->setIV(str_repeat("\0", $tf->getBlockLength() >> 3)); $tf->setIV(str_repeat("\0", $tf->getBlockLength() >> 3));
$tf->disablePadding(); $tf->disablePadding();