Spade
Mini Shell
| Directory:~$ /home/lmsyaran/public_html/joomla4/ |
| [Home] [System Details] [Kill Me] |
index.html000064400000000054151156520560006546 0ustar00<html><body
bgcolor="#FFFFFF"></body></html>vendor/autoload.php000064400000000262151156520560010370
0ustar00<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit10d22a526bd476954b93748a871e7ad4::getLoader();
vendor/composer/autoload_classmap.php000064400000000223151156520560014077
0ustar00<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);
vendor/composer/autoload_files.php000064400000000367151156520570013410
0ustar00<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'decc78cc4436b1292c6c0d151b19445c' => $vendorDir .
'/phpseclib/phpseclib/phpseclib/bootstrap.php',
);
vendor/composer/autoload_namespaces.php000064400000000225151156520570014416
0ustar00<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);
vendor/composer/autoload_psr4.php000064400000000332151156520570013166
0ustar00<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'phpseclib\\' => array($vendorDir .
'/phpseclib/phpseclib/phpseclib'),
);
vendor/composer/autoload_real.php000064400000004654151156520570013234
0ustar00<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit10d22a526bd476954b93748a871e7ad4
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit10d22a526bd476954b93748a871e7ad4',
'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit10d22a526bd476954b93748a871e7ad4',
'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 &&
!defined('HHVM_VERSION') &&
(!function_exists('zend_loader_file_encoded') ||
!zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit10d22a526bd476954b93748a871e7ad4::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ .
'/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
if ($useStaticLoader) {
$includeFiles =
Composer\Autoload\ComposerStaticInit10d22a526bd476954b93748a871e7ad4::$files;
} else {
$includeFiles = require __DIR__ .
'/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire10d22a526bd476954b93748a871e7ad4($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequire10d22a526bd476954b93748a871e7ad4($fileIdentifier,
$file)
{
if
(empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] =
true;
}
}
vendor/composer/autoload_static.php000064400000001745151156520570013576
0ustar00<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit10d22a526bd476954b93748a871e7ad4
{
public static $files = array (
'decc78cc4436b1292c6c0d151b19445c' => __DIR__ .
'/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
);
public static $prefixLengthsPsr4 = array (
'p' =>
array (
'phpseclib\\' => 10,
),
);
public static $prefixDirsPsr4 = array (
'phpseclib\\' =>
array (
0 => __DIR__ . '/..' .
'/phpseclib/phpseclib/phpseclib',
),
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 =
ComposerStaticInit10d22a526bd476954b93748a871e7ad4::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 =
ComposerStaticInit10d22a526bd476954b93748a871e7ad4::$prefixDirsPsr4;
}, null, ClassLoader::class);
}
}
vendor/composer/ClassLoader.php000064400000032223151156520570012606
0ustar00<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component',
__DIR__.'/component');
* $loader->add('Symfony',
__DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for
instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge',
$this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap,
$classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this
namespace.
*
* @param string $prefix The prefix/namespace, with trailing
'\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4
prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing
'\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4
prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to
check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the
extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch')
&& filter_var(ini_get('apc.enabled'),
FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true,
$prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative ||
isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class,
'.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\',
DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\'))
{
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR .
substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR .
$logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_',
DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_',
DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs)
{
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR
. $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR .
$logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file =
stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}
vendor/composer/installed.json000064400000007040151156520570012552
0ustar00[
{
"name": "phpseclib/phpseclib",
"version": "2.0.32",
"version_normalized": "2.0.32.0",
"source": {
"type": "git",
"url":
"https://github.com/phpseclib/phpseclib.git",
"reference":
"f5c4c19880d45d0be3e7d24ae8ac434844a898cd"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/phpseclib/phpseclib/zipball/f5c4c19880d45d0be3e7d24ae8ac434844a898cd",
"reference":
"f5c4c19880d45d0be3e7d24ae8ac434844a898cd",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phing/phing": "~2.7",
"phpunit/phpunit":
"^4.8.35|^5.7|^6.0|^9.4",
"squizlabs/php_codesniffer": "~2.0"
},
"suggest": {
"ext-gmp": "Install the GMP (GNU Multiple
Precision) extension in order to speed up arbitrary precision integer
arithmetic operations.",
"ext-libsodium": "SSH2/SFTP can make use of some
algorithms provided by the libsodium-php extension.",
"ext-mcrypt": "Install the Mcrypt extension in
order to speed up a few other cryptographic operations.",
"ext-openssl": "Install the OpenSSL extension in
order to speed up a wide variety of cryptographic operations."
},
"time": "2021-06-12T12:12:59+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"files": [
"phpseclib/bootstrap.php"
],
"psr-4": {
"phpseclib\\": "phpseclib/"
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jim Wigginton",
"email": "terrafrost@php.net",
"role": "Lead Developer"
},
{
"name": "Patrick Monnerat",
"email": "pm@datasphere.ch",
"role": "Developer"
},
{
"name": "Andreas Fischer",
"email": "bantu@phpbb.com",
"role": "Developer"
},
{
"name": "Hans-Jürgen Petrich",
"email": "petrich@tronic-media.com",
"role": "Developer"
},
{
"name": "Graham Campbell",
"email": "graham@alt-three.com",
"role": "Developer"
}
],
"description": "PHP Secure Communications Library -
Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
"homepage": "http://phpseclib.sourceforge.net",
"keywords": [
"BigInteger",
"aes",
"asn.1",
"asn1",
"blowfish",
"crypto",
"cryptography",
"encryption",
"rsa",
"security",
"sftp",
"signature",
"signing",
"ssh",
"twofish",
"x.509",
"x509"
],
"funding": [
{
"url": "https://github.com/terrafrost",
"type": "github"
},
{
"url":
"https://www.patreon.com/phpseclib",
"type": "patreon"
},
{
"url":
"https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
"type": "tidelift"
}
]
}
]
vendor/composer/LICENSE000064400000002054151156520570010705
0ustar00Copyright (c) Nils Adermann, Jordi Boggiano
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.
vendor/htaccess.txt000064400000000226151156520570010406 0ustar00# Apache
2.4+
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
# Apache 2.0-2.2
<IfModule !mod_authz_core.c>
Deny from all
</IfModule>
vendor/index.html000064400000000054151156520570010044
0ustar00<html><body
bgcolor="#FFFFFF"></body></html>vendor/phpseclib/phpseclib/appveyor.yml000064400000001427151156520620014362
0ustar00build: false
shallow_clone: false
platform:
- x86
- x64
clone_folder: C:\projects\phpseclib
install:
- cinst -y OpenSSL.Light
- SET PATH=C:\Program Files\OpenSSL;%PATH%
- sc config wuauserv start= auto
- net start wuauserv
- cinst -y php --version 5.6.30
- cd c:\tools\php56
- copy php.ini-production php.ini
- echo date.timezone="UTC" >> php.ini
- echo extension_dir=ext >> php.ini
- echo extension=php_openssl.dll >> php.ini
- echo extension=php_gmp.dll >> php.ini
- cd C:\projects\phpseclib
- SET PATH=C:\tools\php56;%PATH%
- php.exe -r
"readfile('http://getcomposer.org/installer');" |
php.exe
- php.exe composer.phar install --prefer-source --no-interaction
test_script:
- cd C:\projects\phpseclib
- vendor\bin\phpunit.bat
tests/Windows32Test.phpvendor/phpseclib/phpseclib/AUTHORS000064400000000427151156520620013041
0ustar00phpseclib Lead Developer: TerraFrost (Jim Wigginton)
phpseclib Developers: monnerat (Patrick Monnerat)
bantu (Andreas Fischer)
petrich (Hans-Jürgen Petrich)
GrahamCampbell (Graham Campbell)
vendor/phpseclib/phpseclib/BACKERS.md000064400000000523151156520620013362
0ustar00# Backers
phpseclib ongoing development is made possible by
[Tidelift](https://tidelift.com/subscription/pkg/packagist-phpseclib-phpseclib?utm_source=packagist-phpseclib-phpseclib&utm_medium=referral&utm_campaign=readme)
and by contributions by users like you. Thank you.
## Backers
- Zane Hooper
-
[Setasign](https://www.setasign.com/)vendor/phpseclib/phpseclib/composer.json000064400000004210151156520620014505
0ustar00{
"name": "phpseclib/phpseclib",
"type": "library",
"description": "PHP Secure Communications Library -
Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
"keywords": [
"security",
"crypto",
"cryptography",
"encryption",
"signature",
"signing",
"rsa",
"aes",
"blowfish",
"twofish",
"ssh",
"sftp",
"x509",
"x.509",
"asn1",
"asn.1",
"BigInteger"
],
"homepage": "http://phpseclib.sourceforge.net",
"license": "MIT",
"authors": [
{
"name": "Jim Wigginton",
"email": "terrafrost@php.net",
"role": "Lead Developer"
},
{
"name": "Patrick Monnerat",
"email": "pm@datasphere.ch",
"role": "Developer"
},
{
"name": "Andreas Fischer",
"email": "bantu@phpbb.com",
"role": "Developer"
},
{
"name": "Hans-Jürgen Petrich",
"email": "petrich@tronic-media.com",
"role": "Developer"
},
{
"name": "Graham Campbell",
"email": "graham@alt-three.com",
"role": "Developer"
}
],
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phing/phing": "~2.7",
"phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4",
"squizlabs/php_codesniffer": "~2.0"
},
"suggest": {
"ext-libsodium": "SSH2/SFTP can make use of some
algorithms provided by the libsodium-php extension.",
"ext-openssl": "Install the OpenSSL extension in
order to speed up a wide variety of cryptographic operations.",
"ext-mcrypt": "Install the Mcrypt extension in order
to speed up a few other cryptographic operations.",
"ext-gmp": "Install the GMP (GNU Multiple Precision)
extension in order to speed up arbitrary precision integer arithmetic
operations."
},
"autoload": {
"files": [
"phpseclib/bootstrap.php"
],
"psr-4": {
"phpseclib\\": "phpseclib/"
}
}
}
vendor/phpseclib/phpseclib/LICENSE000064400000002071151156520620012773
0ustar00Copyright (c) 2011-2019 TerraFrost and other contributors
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.vendor/phpseclib/phpseclib/phpseclib/bootstrap.php000064400000000660151156520620016467
0ustar00<?php
/**
* Bootstrapping File for phpseclib
*
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*/
if (extension_loaded('mbstring')) {
// 2 - MB_OVERLOAD_STRING
if (ini_get('mbstring.func_overload') & 2) {
throw new \UnexpectedValueException(
'Overloading of string functions using
mbstring.func_overload ' .
'is not supported by phpseclib.'
);
}
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php000064400000007165151156520620016172
0ustar00<?php
/**
* Pure-PHP implementation of AES.
*
* Uses mcrypt, if available/possible, and an internal implementation,
otherwise.
*
* PHP version 5
*
* NOTE: Since AES.php is (for compatibility and phpseclib-historical
reasons) virtually
* just a wrapper to Rijndael.php you may consider using Rijndael.php
instead of
* to save one include_once().
*
* If {@link self::setKeyLength() setKeyLength()} isn't called,
it'll be calculated from
* {@link self::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 192-bits and 192 bits will be the key
length until {@link self::setKey() setKey()}
* is called, again, at which point, it'll be recalculated.
*
* Since \phpseclib\Crypt\AES extends \phpseclib\Crypt\Rijndael, some
functions are available to be called that, in the context of AES,
don't
* make a whole lot of sense. {@link self::setBlockLength()
setBlockLength()}, for instance. Calling that function,
* however possible, won't do anything (AES has a fixed block length
whereas Rijndael has a variable one).
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $aes = new \phpseclib\Crypt\AES();
*
* $aes->setKey('abcdefghijklmnop');
*
* $size = 10 * 1024;
* $plaintext = '';
* for ($i = 0; $i < $size; $i++) {
* $plaintext.= 'a';
* }
*
* echo $aes->decrypt($aes->encrypt($plaintext));
* ?>
* </code>
*
* @category Crypt
* @package AES
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2008 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Pure-PHP implementation of AES.
*
* @package AES
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class AES extends Rijndael
{
/**
* Dummy function
*
* Since \phpseclib\Crypt\AES extends \phpseclib\Crypt\Rijndael, this
function is, technically, available, but it doesn't do anything.
*
* @see \phpseclib\Crypt\Rijndael::setBlockLength()
* @access public
* @param int $length
*/
function setBlockLength($length)
{
return;
}
/**
* 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
* 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()
* @access public
* @param int $length
*/
function setKeyLength($length)
{
switch ($length) {
case 160:
$length = 192;
break;
case 224:
$length = 256;
}
parent::setKeyLength($length);
}
/**
* Sets the key.
*
* Rijndael supports five different key lengths, AES only supports
three.
*
* @see \phpseclib\Crypt\Rijndael:setKey()
* @see setKeyLength()
* @access public
* @param string $key
*/
function setKey($key)
{
parent::setKey($key);
if (!$this->explicit_key_length) {
$length = strlen($key);
switch (true) {
case $length <= 16:
$this->key_length = 16;
break;
case $length <= 24:
$this->key_length = 24;
break;
default:
$this->key_length = 32;
}
$this->_setEngine();
}
}
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php000064400000312063151156520620016430
0ustar00<?php
/**
* Base Class for all \phpseclib\Crypt\* cipher classes
*
* PHP version 5
*
* Internally for phpseclib developers:
* If you plan to add a new cipher class, please note following rules:
*
* - The new \phpseclib\Crypt\* cipher class should extend
\phpseclib\Crypt\Base
*
* - Following methods are then required to be overridden/overloaded:
*
* - _encryptBlock()
*
* - _decryptBlock()
*
* - _setupKey()
*
* - All other methods are optional to be overridden/overloaded
*
* - Look at the source code of the current ciphers how they extend
\phpseclib\Crypt\Base
* and take one of them as a start up for the new cipher class.
*
* - Please read all the other comments/notes/hints here also for each
class var/method
*
* @category Crypt
* @package Base
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Base Class for all \phpseclib\Crypt\* cipher classes
*
* @package Base
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
*/
abstract class Base
{
/**#@+
* @access public
* @see \phpseclib\Crypt\Base::encrypt()
* @see \phpseclib\Crypt\Base::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
*/
const MODE_CTR = -1;
/**
* Encrypt / decrypt using the Electronic Code Book mode.
*
* @link
http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
*/
const MODE_ECB = 1;
/**
* Encrypt / decrypt using the Code Book Chaining mode.
*
* @link
http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
*/
const MODE_CBC = 2;
/**
* Encrypt / decrypt using the Cipher Feedback mode.
*
* @link
http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
*/
const MODE_CFB = 3;
/**
* Encrypt / decrypt using the Cipher Feedback mode (8bit)
*/
const MODE_CFB8 = 38;
/**
* Encrypt / decrypt using the Output Feedback mode.
*
* @link
http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
*/
const MODE_OFB = 4;
/**
* Encrypt / decrypt using streaming mode.
*/
const MODE_STREAM = 5;
/**#@-*/
/**
* Whirlpool available flag
*
* @see \phpseclib\Crypt\Base::_hashInlineCryptFunction()
* @var bool
* @access private
*/
static $WHIRLPOOL_AVAILABLE;
/**#@+
* @access private
* @see \phpseclib\Crypt\Base::__construct()
*/
/**
* Base value for the internal implementation $engine switch
*/
const ENGINE_INTERNAL = 1;
/**
* Base value for the mcrypt implementation $engine switch
*/
const ENGINE_MCRYPT = 2;
/**
* Base value for the mcrypt implementation $engine switch
*/
const ENGINE_OPENSSL = 3;
/**#@-*/
/**
* The Encryption Mode
*
* @see self::__construct()
* @var int
* @access private
*/
var $mode;
/**
* The Block Length of the block cipher
*
* @var int
* @access private
*/
var $block_size = 16;
/**
* The Key
*
* @see self::setKey()
* @var string
* @access private
*/
var $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
/**
* The Initialization Vector
*
* @see self::setIV()
* @var string
* @access private
*/
var $iv;
/**
* A "sliding" Initialization Vector
*
* @see self::enableContinuousBuffer()
* @see self::_clearBuffers()
* @var string
* @access private
*/
var $encryptIV;
/**
* A "sliding" Initialization Vector
*
* @see self::enableContinuousBuffer()
* @see self::_clearBuffers()
* @var string
* @access private
*/
var $decryptIV;
/**
* Continuous Buffer status
*
* @see self::enableContinuousBuffer()
* @var bool
* @access private
*/
var $continuousBuffer = false;
/**
* Encryption buffer for CTR, OFB and CFB modes
*
* @see self::encrypt()
* @see self::_clearBuffers()
* @var array
* @access private
*/
var $enbuffer;
/**
* Decryption buffer for CTR, OFB and CFB modes
*
* @see self::decrypt()
* @see self::_clearBuffers()
* @var array
* @access private
*/
var $debuffer;
/**
* 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 self::encrypt()
* @var resource
* @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 self::decrypt()
* @var resource
* @access private
*/
var $demcrypt;
/**
* Does the enmcrypt resource need to be (re)initialized?
*
* @see \phpseclib\Crypt\Twofish::setKey()
* @see \phpseclib\Crypt\Twofish::setIV()
* @var bool
* @access private
*/
var $enchanged = true;
/**
* Does the demcrypt resource need to be (re)initialized?
*
* @see \phpseclib\Crypt\Twofish::setKey()
* @see \phpseclib\Crypt\Twofish::setIV()
* @var bool
* @access private
*/
var $dechanged = true;
/**
* mcrypt resource for CFB mode
*
* mcrypt's CFB mode, in (and only in) buffered context,
* is broken, so phpseclib implements the CFB mode by it self,
* even when the mcrypt php extension is available.
*
* In order to do the CFB-mode work (fast) phpseclib
* use a separate ECB-mode mcrypt resource.
*
* @link http://phpseclib.sourceforge.net/cfb-demo.phps
* @see self::encrypt()
* @see self::decrypt()
* @see self::_setupMcrypt()
* @var resource
* @access private
*/
var $ecb;
/**
* Optimizing value while CFB-encrypting
*
* Only relevant if $continuousBuffer enabled
* and $engine == self::ENGINE_MCRYPT
*
* It's faster to re-init $enmcrypt if
* $buffer bytes > $cfb_init_len than
* using the $ecb resource furthermore.
*
* This value depends of the chosen cipher
* and the time it would be needed for it's
* initialization [by mcrypt_generic_init()]
* which, typically, depends on the complexity
* on its internaly Key-expanding algorithm.
*
* @see self::encrypt()
* @var int
* @access private
*/
var $cfb_init_len = 600;
/**
* Does internal cipher state need to be (re)initialized?
*
* @see self::setKey()
* @see self::setIV()
* @see self::disableContinuousBuffer()
* @var bool
* @access private
*/
var $changed = true;
/**
* Padding status
*
* @see self::enablePadding()
* @var bool
* @access private
*/
var $padding = true;
/**
* Is the mode one that is paddable?
*
* @see self::__construct()
* @var bool
* @access private
*/
var $paddable = false;
/**
* Holds which crypt engine internaly should be use,
* which will be determined automatically on __construct()
*
* Currently available $engines are:
* - self::ENGINE_OPENSSL (very fast, php-extension: openssl,
extension_loaded('openssl') required)
* - self::ENGINE_MCRYPT (fast, php-extension: mcrypt,
extension_loaded('mcrypt') required)
* - self::ENGINE_INTERNAL (slower, pure php-engine, no php-extension
required)
*
* @see self::_setEngine()
* @see self::encrypt()
* @see self::decrypt()
* @var int
* @access private
*/
var $engine;
/**
* Holds the preferred crypt engine
*
* @see self::_setEngine()
* @see self::setPreferredEngine()
* @var int
* @access private
*/
var $preferredEngine;
/**
* The mcrypt specific name of the cipher
*
* Only used if $engine == self::ENGINE_MCRYPT
*
* @link http://www.php.net/mcrypt_module_open
* @link http://www.php.net/mcrypt_list_algorithms
* @see self::_setupMcrypt()
* @var string
* @access private
*/
var $cipher_name_mcrypt;
/**
* The openssl specific name of the cipher
*
* Only used if $engine == self::ENGINE_OPENSSL
*
* @link http://www.php.net/openssl-get-cipher-methods
* @var string
* @access private
*/
var $cipher_name_openssl;
/**
* The openssl specific name of the cipher in ECB mode
*
* If OpenSSL does not support the mode we're trying to use (CTR)
* it can still be emulated with ECB mode.
*
* @link http://www.php.net/openssl-get-cipher-methods
* @var string
* @access private
*/
var $cipher_name_openssl_ecb;
/**
* The default salt used by setPassword()
*
* @see self::setPassword()
* @var string
* @access private
*/
var $password_default_salt = 'phpseclib/salt';
/**
* The name of the performance-optimized callback function
*
* Used by encrypt() / decrypt()
* only if $engine == self::ENGINE_INTERNAL
*
* @see self::encrypt()
* @see self::decrypt()
* @see self::_setupInlineCrypt()
* @see self::$use_inline_crypt
* @var Callback
* @access private
*/
var $inline_crypt;
/**
* Holds whether performance-optimized $inline_crypt() can/should be
used.
*
* @see self::encrypt()
* @see self::decrypt()
* @see self::inline_crypt
* @var mixed
* @access private
*/
var $use_inline_crypt = true;
/**
* If OpenSSL can be used in ECB but not in CTR we can emulate CTR
*
* @see self::_openssl_ctr_process()
* @var bool
* @access private
*/
var $openssl_emulate_ctr = false;
/**
* Determines what options are passed to openssl_encrypt/decrypt
*
* @see self::isValidEngine()
* @var mixed
* @access private
*/
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
*
* @see self::_clearBuffers()
* @var bool
* @access private
*/
var $skip_key_adjustment = false;
/**
* Default Constructor.
*
* Determines whether or not the mcrypt extension should be used.
*
* $mode could be:
*
* - self::MODE_ECB
*
* - self::MODE_CBC
*
* - self::MODE_CTR
*
* - self::MODE_CFB
*
* - self::MODE_OFB
*
* If not explicitly set, self::MODE_CBC will be used.
*
* @param int $mode
* @access public
*/
function __construct($mode = self::MODE_CBC)
{
// $mode dependent settings
switch ($mode) {
case self::MODE_ECB:
$this->paddable = true;
$this->mode = self::MODE_ECB;
break;
case self::MODE_CTR:
case self::MODE_CFB:
case self::MODE_CFB8:
case self::MODE_OFB:
case self::MODE_STREAM:
$this->mode = $mode;
break;
case self::MODE_CBC:
default:
$this->paddable = true;
$this->mode = self::MODE_CBC;
}
$this->_setEngine();
}
/**
* Sets the initialization vector. (optional)
*
* SetIV is not required when self::MODE_ECB (or ie for AES:
\phpseclib\Crypt\AES::MODE_ECB) is being used. If not explicitly set,
it'll be assumed
* to be all zero's.
*
* @access public
* @param string $iv
* @internal Can be overwritten by a sub class, but does not have to be
*/
function setIV($iv)
{
if ($this->mode == self::MODE_ECB) {
return;
}
$this->iv = $iv;
$this->changed = true;
}
/**
* 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 = true;
$this->changed = true;
$this->_setEngine();
}
/**
* Returns the current key length in bits
*
* @access public
* @return int
*/
function getKeyLength()
{
return $this->key_length << 3;
}
/**
* Returns the current block length in bits
*
* @access public
* @return int
*/
function getBlockLength()
{
return $this->block_size << 3;
}
/**
* Sets the key.
*
* The min/max length(s) of the key depends on the cipher which is
used.
* If the key not fits the length(s) of the cipher it will paded with
null bytes
* up to the closest valid key length. If the key is more than max
length,
* we trim the excess bits.
*
* If the key is not explicitly set, it'll be assumed to be all
null bytes.
*
* @access public
* @param string $key
* @internal Could, but not must, extend by the child Crypt_* class
*/
function setKey($key)
{
if (!$this->explicit_key_length) {
$this->setKeyLength(strlen($key) << 3);
$this->explicit_key_length = false;
}
$this->key = $key;
$this->changed = true;
$this->_setEngine();
}
/**
* 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} or pbkdf1:
* $hash, $salt, $count, $dkLen
*
* Where $hash (default = sha1) currently supports the
following hashes: see: Crypt/Hash.php
*
* @see Crypt/Hash.php
* @param string $password
* @param string $method
* @return bool
* @access public
* @internal Could, but not must, extend by the child Crypt_* class
*/
function setPassword($password, $method = 'pbkdf2')
{
$key = '';
switch ($method) {
default: // 'pbkdf2' or 'pbkdf1'
$func_args = func_get_args();
// Hash function
$hash = isset($func_args[2]) ? $func_args[2] :
'sha1';
// WPA and WPA2 use the SSID as the salt
$salt = isset($func_args[3]) ? $func_args[3] :
$this->password_default_salt;
// RFC2898#section-4.2 uses 1,000 iterations by default
// WPA and WPA2 use 4,096.
$count = isset($func_args[4]) ? $func_args[4] : 1000;
// Keylength
if (isset($func_args[5])) {
$dkLen = $func_args[5];
} else {
$dkLen = $method == 'pbkdf1' ? 2 *
$this->key_length : $this->key_length;
}
switch (true) {
case $method == 'pbkdf1':
$hashObj = new Hash();
$hashObj->setHash($hash);
if ($dkLen > $hashObj->getLength()) {
user_error('Derived key too long');
return false;
}
$t = $password . $salt;
for ($i = 0; $i < $count; ++$i) {
$t = $hashObj->hash($t);
}
$key = substr($t, 0, $dkLen);
$this->setKey(substr($key, 0, $dkLen >>
1));
$this->setIV(substr($key, $dkLen >> 1));
return true;
// Determining if php[>=5.5.0]'s hash_pbkdf2()
function avail- and useable
case !function_exists('hash_pbkdf2'):
case !function_exists('hash_algos'):
case !in_array($hash, hash_algos()):
$i = 1;
$hmac = new Hash();
$hmac->setHash($hash);
$hmac->setKey($password);
while (strlen($key) < $dkLen) {
$f = $u = $hmac->hash($salt .
pack('N', $i++));
for ($j = 2; $j <= $count; ++$j) {
$u = $hmac->hash($u);
$f^= $u;
}
$key.= $f;
}
$key = substr($key, 0, $dkLen);
break;
default:
$key = hash_pbkdf2($hash, $password, $salt, $count,
$dkLen, true);
}
}
$this->setKey($key);
return true;
}
/**
* Encrypts a message.
*
* $plaintext will be padded with additional bytes such that it's
length is a multiple of the block size. Other cipher
* implementations may or may not pad in the 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}
*
* 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 the block
size, however, arbitrary values can be added to make it that
* length.
*
* @see self::decrypt()
* @access public
* @param string $plaintext
* @return string $ciphertext
* @internal Could, but not must, extend by the child Crypt_* class
*/
function encrypt($plaintext)
{
if ($this->paddable) {
$plaintext = $this->_pad($plaintext);
}
if ($this->engine === self::ENGINE_OPENSSL) {
if ($this->changed) {
$this->_clearBuffers();
$this->changed = false;
}
switch ($this->mode) {
case self::MODE_STREAM:
return openssl_encrypt($plaintext,
$this->cipher_name_openssl, $this->key, $this->openssl_options);
case self::MODE_ECB:
$result = @openssl_encrypt($plaintext,
$this->cipher_name_openssl, $this->key, $this->openssl_options);
return !defined('OPENSSL_RAW_DATA') ?
substr($result, 0, -$this->block_size) : $result;
case self::MODE_CBC:
$result = openssl_encrypt($plaintext,
$this->cipher_name_openssl, $this->key, $this->openssl_options,
$this->encryptIV);
if (!defined('OPENSSL_RAW_DATA')) {
$result = substr($result, 0,
-$this->block_size);
}
if ($this->continuousBuffer) {
$this->encryptIV = substr($result,
-$this->block_size);
}
return $result;
case self::MODE_CTR:
return $this->_openssl_ctr_process($plaintext,
$this->encryptIV, $this->enbuffer);
case self::MODE_CFB:
// cfb loosely routines inspired by openssl's:
// {@link
http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
$ciphertext = '';
if ($this->continuousBuffer) {
$iv = &$this->encryptIV;
$pos = &$this->enbuffer['pos'];
} else {
$iv = $this->encryptIV;
$pos = 0;
}
$len = strlen($plaintext);
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $this->block_size - $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
$ciphertext = substr($iv, $orig_pos) ^ $plaintext;
$iv = substr_replace($iv, $ciphertext, $orig_pos,
$i);
$plaintext = substr($plaintext, $i);
}
$overflow = $len % $this->block_size;
if ($overflow) {
$ciphertext.= openssl_encrypt(substr($plaintext, 0,
-$overflow) . str_repeat("\0", $this->block_size),
$this->cipher_name_openssl, $this->key, $this->openssl_options,
$iv);
$iv = $this->_string_pop($ciphertext,
$this->block_size);
$size = $len - $overflow;
$block = $iv ^ substr($plaintext, -$overflow);
$iv = substr_replace($iv, $block, 0, $overflow);
$ciphertext.= $block;
$pos = $overflow;
} elseif ($len) {
$ciphertext = openssl_encrypt($plaintext,
$this->cipher_name_openssl, $this->key, $this->openssl_options,
$iv);
$iv = substr($ciphertext, -$this->block_size);
}
return $ciphertext;
case self::MODE_CFB8:
$ciphertext = openssl_encrypt($plaintext,
$this->cipher_name_openssl, $this->key, $this->openssl_options,
$this->encryptIV);
if ($this->continuousBuffer) {
if (($len = strlen($ciphertext)) >=
$this->block_size) {
$this->encryptIV = substr($ciphertext,
-$this->block_size);
} else {
$this->encryptIV =
substr($this->encryptIV, $len - $this->block_size) .
substr($ciphertext, -$len);
}
}
return $ciphertext;
case self::MODE_OFB:
return $this->_openssl_ofb_process($plaintext,
$this->encryptIV, $this->enbuffer);
}
}
if ($this->engine === self::ENGINE_MCRYPT) {
set_error_handler(array($this, 'do_nothing'));
if ($this->changed) {
$this->_setupMcrypt();
$this->changed = false;
}
if ($this->enchanged) {
mcrypt_generic_init($this->enmcrypt, $this->key,
$this->encryptIV);
$this->enchanged = false;
}
// re: {@link 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 == self::MODE_CFB &&
$this->continuousBuffer) {
$block_size = $this->block_size;
$iv = &$this->encryptIV;
$pos = &$this->enbuffer['pos'];
$len = strlen($plaintext);
$ciphertext = '';
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $block_size - $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 >= $block_size) {
if ($this->enbuffer['enmcrypt_init'] ===
false || $len > $this->cfb_init_len) {
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 % $block_size));
$iv = substr($ciphertext, -$block_size);
$len%= $block_size;
} else {
while ($len >= $block_size) {
$iv = mcrypt_generic($this->ecb, $iv) ^
substr($plaintext, $i, $block_size);
$ciphertext.= $iv;
$len-= $block_size;
$i+= $block_size;
}
}
}
if ($len) {
$iv = mcrypt_generic($this->ecb, $iv);
$block = $iv ^ substr($plaintext, -$len);
$iv = substr_replace($iv, $block, 0, $len);
$ciphertext.= $block;
$pos = $len;
}
restore_error_handler();
return $ciphertext;
}
$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->enmcrypt, $this->key,
$this->encryptIV);
}
restore_error_handler();
return $ciphertext;
}
if ($this->changed) {
$this->_setup();
$this->changed = false;
}
if ($this->use_inline_crypt) {
$inline = $this->inline_crypt;
return $inline('encrypt', $this, $plaintext);
}
$buffer = &$this->enbuffer;
$block_size = $this->block_size;
$ciphertext = '';
switch ($this->mode) {
case self::MODE_ECB:
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$ciphertext.=
$this->_encryptBlock(substr($plaintext, $i, $block_size));
}
break;
case self::MODE_CBC:
$xor = $this->encryptIV;
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
$block = $this->_encryptBlock($block ^ $xor);
$xor = $block;
$ciphertext.= $block;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
}
break;
case self::MODE_CTR:
$xor = $this->encryptIV;
if (strlen($buffer['ciphertext'])) {
for ($i = 0; $i < strlen($plaintext);
$i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
if (strlen($block) >
strlen($buffer['ciphertext'])) {
$buffer['ciphertext'].=
$this->_encryptBlock($xor);
}
$this->_increment_str($xor);
$key =
$this->_string_shift($buffer['ciphertext'], $block_size);
$ciphertext.= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($plaintext);
$i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
$key = $this->_encryptBlock($xor);
$this->_increment_str($xor);
$ciphertext.= $block ^ $key;
}
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
if ($start = strlen($plaintext) % $block_size) {
$buffer['ciphertext'] = substr($key,
$start) . $buffer['ciphertext'];
}
}
break;
case self::MODE_CFB:
// cfb loosely routines inspired by openssl's:
// {@link
http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
if ($this->continuousBuffer) {
$iv = &$this->encryptIV;
$pos = &$buffer['pos'];
} else {
$iv = $this->encryptIV;
$pos = 0;
}
$len = strlen($plaintext);
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $block_size - $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
$ciphertext = substr($iv, $orig_pos) ^ $plaintext;
$iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
}
while ($len >= $block_size) {
$iv = $this->_encryptBlock($iv) ^ substr($plaintext,
$i, $block_size);
$ciphertext.= $iv;
$len-= $block_size;
$i+= $block_size;
}
if ($len) {
$iv = $this->_encryptBlock($iv);
$block = $iv ^ substr($plaintext, $i);
$iv = substr_replace($iv, $block, 0, $len);
$ciphertext.= $block;
$pos = $len;
}
break;
case self::MODE_CFB8:
$ciphertext = '';
$len = strlen($plaintext);
$iv = $this->encryptIV;
for ($i = 0; $i < $len; ++$i) {
$ciphertext .= ($c = $plaintext[$i] ^
$this->_encryptBlock($iv));
$iv = substr($iv, 1) . $c;
}
if ($this->continuousBuffer) {
if ($len >= $block_size) {
$this->encryptIV = substr($ciphertext,
-$block_size);
} else {
$this->encryptIV = substr($this->encryptIV,
$len - $block_size) . substr($ciphertext, -$len);
}
}
break;
case self::MODE_OFB:
$xor = $this->encryptIV;
if (strlen($buffer['xor'])) {
for ($i = 0; $i < strlen($plaintext);
$i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
if (strlen($block) >
strlen($buffer['xor'])) {
$xor = $this->_encryptBlock($xor);
$buffer['xor'].= $xor;
}
$key =
$this->_string_shift($buffer['xor'], $block_size);
$ciphertext.= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($plaintext);
$i+=$block_size) {
$xor = $this->_encryptBlock($xor);
$ciphertext.= substr($plaintext, $i, $block_size) ^
$xor;
}
$key = $xor;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
if ($start = strlen($plaintext) % $block_size) {
$buffer['xor'] = substr($key, $start) .
$buffer['xor'];
}
}
break;
case self::MODE_STREAM:
$ciphertext = $this->_encryptBlock($plaintext);
break;
}
return $ciphertext;
}
/**
* Decrypts a message.
*
* If strlen($ciphertext) is not a multiple of the block size, null
bytes will be added to the end of the string until
* it is.
*
* @see self::encrypt()
* @access public
* @param string $ciphertext
* @return string $plaintext
* @internal Could, but not must, extend by the child Crypt_* class
*/
function decrypt($ciphertext)
{
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}:
// "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->changed) {
$this->_clearBuffers();
$this->changed = false;
}
switch ($this->mode) {
case self::MODE_STREAM:
$plaintext = openssl_decrypt($ciphertext,
$this->cipher_name_openssl, $this->key, $this->openssl_options);
break;
case self::MODE_ECB:
if (!defined('OPENSSL_RAW_DATA')) {
$ciphertext.= @openssl_encrypt('',
$this->cipher_name_openssl_ecb, $this->key, true);
}
$plaintext = openssl_decrypt($ciphertext,
$this->cipher_name_openssl, $this->key, $this->openssl_options);
break;
case self::MODE_CBC:
if (!defined('OPENSSL_RAW_DATA')) {
$padding = str_repeat(chr($this->block_size),
$this->block_size) ^ substr($ciphertext, -$this->block_size);
$ciphertext.= substr(@openssl_encrypt($padding,
$this->cipher_name_openssl_ecb, $this->key, true), 0,
$this->block_size);
$offset = 2 * $this->block_size;
} else {
$offset = $this->block_size;
}
$plaintext = openssl_decrypt($ciphertext,
$this->cipher_name_openssl, $this->key, $this->openssl_options,
$this->decryptIV);
if ($this->continuousBuffer) {
$this->decryptIV = substr($ciphertext, -$offset,
$this->block_size);
}
break;
case self::MODE_CTR:
$plaintext =
$this->_openssl_ctr_process($ciphertext, $this->decryptIV,
$this->debuffer);
break;
case self::MODE_CFB:
// cfb loosely routines inspired by openssl's:
// {@link
http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
$plaintext = '';
if ($this->continuousBuffer) {
$iv = &$this->decryptIV;
$pos = &$this->buffer['pos'];
} else {
$iv = $this->decryptIV;
$pos = 0;
}
$len = strlen($ciphertext);
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $this->block_size - $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%= $this->blocksize
$plaintext = substr($iv, $orig_pos) ^ $ciphertext;
$iv = substr_replace($iv, substr($ciphertext, 0,
$i), $orig_pos, $i);
$ciphertext = substr($ciphertext, $i);
}
$overflow = $len % $this->block_size;
if ($overflow) {
$plaintext.= openssl_decrypt(substr($ciphertext, 0,
-$overflow), $this->cipher_name_openssl, $this->key,
$this->openssl_options, $iv);
if ($len - $overflow) {
$iv = substr($ciphertext, -$overflow -
$this->block_size, -$overflow);
}
$iv = openssl_encrypt(str_repeat("\0",
$this->block_size), $this->cipher_name_openssl, $this->key,
$this->openssl_options, $iv);
$plaintext.= $iv ^ substr($ciphertext, -$overflow);
$iv = substr_replace($iv, substr($ciphertext,
-$overflow), 0, $overflow);
$pos = $overflow;
} elseif ($len) {
$plaintext.= openssl_decrypt($ciphertext,
$this->cipher_name_openssl, $this->key, $this->openssl_options,
$iv);
$iv = substr($ciphertext, -$this->block_size);
}
break;
case self::MODE_CFB8:
$plaintext = openssl_decrypt($ciphertext,
$this->cipher_name_openssl, $this->key, $this->openssl_options,
$this->decryptIV);
if ($this->continuousBuffer) {
if (($len = strlen($ciphertext)) >=
$this->block_size) {
$this->decryptIV = substr($ciphertext,
-$this->block_size);
} else {
$this->decryptIV =
substr($this->decryptIV, $len - $this->block_size) .
substr($ciphertext, -$len);
}
}
break;
case self::MODE_OFB:
$plaintext =
$this->_openssl_ofb_process($ciphertext, $this->decryptIV,
$this->debuffer);
}
return $this->paddable ? $this->_unpad($plaintext) :
$plaintext;
}
if ($this->engine === self::ENGINE_MCRYPT) {
set_error_handler(array($this, 'do_nothing'));
$block_size = $this->block_size;
if ($this->changed) {
$this->_setupMcrypt();
$this->changed = false;
}
if ($this->dechanged) {
mcrypt_generic_init($this->demcrypt, $this->key,
$this->decryptIV);
$this->dechanged = false;
}
if ($this->mode == self::MODE_CFB &&
$this->continuousBuffer) {
$iv = &$this->decryptIV;
$pos = &$this->debuffer['pos'];
$len = strlen($ciphertext);
$plaintext = '';
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $block_size - $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 >= $block_size) {
$cb = substr($ciphertext, $i, $len - $len %
$block_size);
$plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^
$cb;
$iv = substr($cb, -$block_size);
$len%= $block_size;
}
if ($len) {
$iv = mcrypt_generic($this->ecb, $iv);
$plaintext.= $iv ^ substr($ciphertext, -$len);
$iv = substr_replace($iv, substr($ciphertext, -$len),
0, $len);
$pos = $len;
}
restore_error_handler();
return $plaintext;
}
$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->demcrypt, $this->key,
$this->decryptIV);
}
restore_error_handler();
return $this->paddable ? $this->_unpad($plaintext) :
$plaintext;
}
if ($this->changed) {
$this->_setup();
$this->changed = false;
}
if ($this->use_inline_crypt) {
$inline = $this->inline_crypt;
return $inline('decrypt', $this, $ciphertext);
}
$block_size = $this->block_size;
$buffer = &$this->debuffer;
$plaintext = '';
switch ($this->mode) {
case self::MODE_ECB:
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size)
{
$plaintext.=
$this->_decryptBlock(substr($ciphertext, $i, $block_size));
}
break;
case self::MODE_CBC:
$xor = $this->decryptIV;
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size)
{
$block = substr($ciphertext, $i, $block_size);
$plaintext.= $this->_decryptBlock($block) ^ $xor;
$xor = $block;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
}
break;
case self::MODE_CTR:
$xor = $this->decryptIV;
if (strlen($buffer['ciphertext'])) {
for ($i = 0; $i < strlen($ciphertext);
$i+=$block_size) {
$block = substr($ciphertext, $i, $block_size);
if (strlen($block) >
strlen($buffer['ciphertext'])) {
$buffer['ciphertext'].=
$this->_encryptBlock($xor);
$this->_increment_str($xor);
}
$key =
$this->_string_shift($buffer['ciphertext'], $block_size);
$plaintext.= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($ciphertext);
$i+=$block_size) {
$block = substr($ciphertext, $i, $block_size);
$key = $this->_encryptBlock($xor);
$this->_increment_str($xor);
$plaintext.= $block ^ $key;
}
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
if ($start = strlen($ciphertext) % $block_size) {
$buffer['ciphertext'] = substr($key,
$start) . $buffer['ciphertext'];
}
}
break;
case self::MODE_CFB:
if ($this->continuousBuffer) {
$iv = &$this->decryptIV;
$pos = &$buffer['pos'];
} else {
$iv = $this->decryptIV;
$pos = 0;
}
$len = strlen($ciphertext);
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $block_size - $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);
}
while ($len >= $block_size) {
$iv = $this->_encryptBlock($iv);
$cb = substr($ciphertext, $i, $block_size);
$plaintext.= $iv ^ $cb;
$iv = $cb;
$len-= $block_size;
$i+= $block_size;
}
if ($len) {
$iv = $this->_encryptBlock($iv);
$plaintext.= $iv ^ substr($ciphertext, $i);
$iv = substr_replace($iv, substr($ciphertext, $i), 0,
$len);
$pos = $len;
}
break;
case self::MODE_CFB8:
$plaintext = '';
$len = strlen($ciphertext);
$iv = $this->decryptIV;
for ($i = 0; $i < $len; ++$i) {
$plaintext .= $ciphertext[$i] ^
$this->_encryptBlock($iv);
$iv = substr($iv, 1) . $ciphertext[$i];
}
if ($this->continuousBuffer) {
if ($len >= $block_size) {
$this->decryptIV = substr($ciphertext,
-$block_size);
} else {
$this->decryptIV = substr($this->decryptIV,
$len - $block_size) . substr($ciphertext, -$len);
}
}
break;
case self::MODE_OFB:
$xor = $this->decryptIV;
if (strlen($buffer['xor'])) {
for ($i = 0; $i < strlen($ciphertext);
$i+=$block_size) {
$block = substr($ciphertext, $i, $block_size);
if (strlen($block) >
strlen($buffer['xor'])) {
$xor = $this->_encryptBlock($xor);
$buffer['xor'].= $xor;
}
$key =
$this->_string_shift($buffer['xor'], $block_size);
$plaintext.= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($ciphertext);
$i+=$block_size) {
$xor = $this->_encryptBlock($xor);
$plaintext.= substr($ciphertext, $i, $block_size) ^
$xor;
}
$key = $xor;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
if ($start = strlen($ciphertext) % $block_size) {
$buffer['xor'] = substr($key, $start) .
$buffer['xor'];
}
}
break;
case self::MODE_STREAM:
$plaintext = $this->_decryptBlock($ciphertext);
break;
}
return $this->paddable ? $this->_unpad($plaintext) :
$plaintext;
}
/**
* OpenSSL CTR Processor
*
* PHP's OpenSSL bindings do not operate in continuous mode so
we'll wrap around it. Since the keystream
* for CTR is the same for both encrypting and decrypting this function
is re-used by both Base::encrypt()
* and Base::decrypt(). Also, OpenSSL doesn't implement CTR for
all of it's symmetric ciphers so this
* function will emulate CTR with ECB when necessary.
*
* @see self::encrypt()
* @see self::decrypt()
* @param string $plaintext
* @param string $encryptIV
* @param array $buffer
* @return string
* @access private
*/
function _openssl_ctr_process($plaintext, &$encryptIV,
&$buffer)
{
$ciphertext = '';
$block_size = $this->block_size;
$key = $this->key;
if ($this->openssl_emulate_ctr) {
$xor = $encryptIV;
if (strlen($buffer['ciphertext'])) {
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
if (strlen($block) >
strlen($buffer['ciphertext'])) {
$result = @openssl_encrypt($xor,
$this->cipher_name_openssl_ecb, $key, $this->openssl_options);
$result = !defined('OPENSSL_RAW_DATA') ?
substr($result, 0, -$this->block_size) : $result;
$buffer['ciphertext'].= $result;
}
$this->_increment_str($xor);
$otp =
$this->_string_shift($buffer['ciphertext'], $block_size);
$ciphertext.= $block ^ $otp;
}
} else {
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
$otp = @openssl_encrypt($xor,
$this->cipher_name_openssl_ecb, $key, $this->openssl_options);
$otp = !defined('OPENSSL_RAW_DATA') ?
substr($otp, 0, -$this->block_size) : $otp;
$this->_increment_str($xor);
$ciphertext.= $block ^ $otp;
}
}
if ($this->continuousBuffer) {
$encryptIV = $xor;
if ($start = strlen($plaintext) % $block_size) {
$buffer['ciphertext'] = substr($key, $start)
. $buffer['ciphertext'];
}
}
return $ciphertext;
}
if (strlen($buffer['ciphertext'])) {
$ciphertext = $plaintext ^
$this->_string_shift($buffer['ciphertext'],
strlen($plaintext));
$plaintext = substr($plaintext, strlen($ciphertext));
if (!strlen($plaintext)) {
return $ciphertext;
}
}
$overflow = strlen($plaintext) % $block_size;
if ($overflow) {
$plaintext2 = $this->_string_pop($plaintext, $overflow); //
ie. trim $plaintext to a multiple of $block_size and put rest of $plaintext
in $plaintext2
$encrypted = openssl_encrypt($plaintext .
str_repeat("\0", $block_size), $this->cipher_name_openssl,
$key, $this->openssl_options, $encryptIV);
$temp = $this->_string_pop($encrypted, $block_size);
$ciphertext.= $encrypted . ($plaintext2 ^ $temp);
if ($this->continuousBuffer) {
$buffer['ciphertext'] = substr($temp, $overflow);
$encryptIV = $temp;
}
} elseif (!strlen($buffer['ciphertext'])) {
$ciphertext.= openssl_encrypt($plaintext .
str_repeat("\0", $block_size), $this->cipher_name_openssl,
$key, $this->openssl_options, $encryptIV);
$temp = $this->_string_pop($ciphertext, $block_size);
if ($this->continuousBuffer) {
$encryptIV = $temp;
}
}
if ($this->continuousBuffer) {
if (!defined('OPENSSL_RAW_DATA')) {
$encryptIV.= @openssl_encrypt('',
$this->cipher_name_openssl_ecb, $key, $this->openssl_options);
}
$encryptIV = openssl_decrypt($encryptIV,
$this->cipher_name_openssl_ecb, $key, $this->openssl_options);
if ($overflow) {
$this->_increment_str($encryptIV);
}
}
return $ciphertext;
}
/**
* OpenSSL OFB Processor
*
* PHP's OpenSSL bindings do not operate in continuous mode so
we'll wrap around it. Since the keystream
* for OFB is the same for both encrypting and decrypting this function
is re-used by both Base::encrypt()
* and Base::decrypt().
*
* @see self::encrypt()
* @see self::decrypt()
* @param string $plaintext
* @param string $encryptIV
* @param array $buffer
* @return string
* @access private
*/
function _openssl_ofb_process($plaintext, &$encryptIV,
&$buffer)
{
if (strlen($buffer['xor'])) {
$ciphertext = $plaintext ^ $buffer['xor'];
$buffer['xor'] = substr($buffer['xor'],
strlen($ciphertext));
$plaintext = substr($plaintext, strlen($ciphertext));
} else {
$ciphertext = '';
}
$block_size = $this->block_size;
$len = strlen($plaintext);
$key = $this->key;
$overflow = $len % $block_size;
if (strlen($plaintext)) {
if ($overflow) {
$ciphertext.= openssl_encrypt(substr($plaintext, 0,
-$overflow) . str_repeat("\0", $block_size),
$this->cipher_name_openssl, $key, $this->openssl_options,
$encryptIV);
$xor = $this->_string_pop($ciphertext, $block_size);
if ($this->continuousBuffer) {
$encryptIV = $xor;
}
$ciphertext.= $this->_string_shift($xor, $overflow) ^
substr($plaintext, -$overflow);
if ($this->continuousBuffer) {
$buffer['xor'] = $xor;
}
} else {
$ciphertext = openssl_encrypt($plaintext,
$this->cipher_name_openssl, $key, $this->openssl_options,
$encryptIV);
if ($this->continuousBuffer) {
$encryptIV = substr($ciphertext, -$block_size) ^
substr($plaintext, -$block_size);
}
}
}
return $ciphertext;
}
/**
* phpseclib <-> OpenSSL Mode Mapper
*
* May need to be overwritten by classes extending this one in some
cases
*
* @return int
* @access private
*/
function _openssl_translate_mode()
{
switch ($this->mode) {
case self::MODE_ECB:
return 'ecb';
case self::MODE_CBC:
return 'cbc';
case self::MODE_CTR:
return 'ctr';
case self::MODE_CFB:
return 'cfb';
case self::MODE_CFB8:
return 'cfb8';
case self::MODE_OFB:
return 'ofb';
}
}
/**
* Pad "packets".
*
* Block ciphers working by encrypting between their specified
[$this->]block_size at a time
* If you ever need to encrypt or decrypt something that isn't of
the proper length, it becomes necessary to
* pad the input so that it is of the proper length.
*
* Padding is enabled by default. Sometimes, however, it is
undesirable to pad strings. Such is the case in SSH,
* where "packets" are padded with random bytes before being
encrypted. Unpad these packets and you risk stripping
* away characters that shouldn't be stripped away. (SSH knows how
many bytes are added because the length is
* transmitted separately)
*
* @see self::disablePadding()
* @access public
*/
function enablePadding()
{
$this->padding = true;
}
/**
* Do not pad packets.
*
* @see self::enablePadding()
* @access public
*/
function disablePadding()
{
$this->padding = false;
}
/**
* Treat consecutive "packets" as if they are a continuous
buffer.
*
* Say you have a 32-byte plaintext $plaintext. Using the default
behavior, the two following code snippets
* will yield different outputs:
*
* <code>
* echo $rijndael->encrypt(substr($plaintext, 0, 16));
* echo $rijndael->encrypt(substr($plaintext, 16, 16));
* </code>
* <code>
* echo $rijndael->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>
* $rijndael->encrypt(substr($plaintext, 0, 16));
* echo
$rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16)));
* </code>
* <code>
* echo
$rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16)));
* </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 \phpseclib\Crypt\*() 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 self::disableContinuousBuffer()
* @access public
* @internal Could, but not must, extend by the child Crypt_* class
*/
function enableContinuousBuffer()
{
if ($this->mode == self::MODE_ECB) {
return;
}
$this->continuousBuffer = true;
$this->_setEngine();
}
/**
* Treat consecutive packets as if they are a discontinuous buffer.
*
* The default behavior.
*
* @see self::enableContinuousBuffer()
* @access public
* @internal Could, but not must, extend by the child Crypt_* class
*/
function disableContinuousBuffer()
{
if ($this->mode == self::MODE_ECB) {
return;
}
if (!$this->continuousBuffer) {
return;
}
$this->continuousBuffer = false;
$this->changed = true;
$this->_setEngine();
}
/**
* Test for engine validity
*
* @see self::__construct()
* @param int $engine
* @access public
* @return bool
*/
function isValidEngine($engine)
{
switch ($engine) {
case self::ENGINE_OPENSSL:
if ($this->mode == self::MODE_STREAM &&
$this->continuousBuffer) {
return false;
}
$this->openssl_emulate_ctr = false;
$result = $this->cipher_name_openssl &&
extension_loaded('openssl') &&
// PHP 5.3.0 - 5.3.2 did not let you set
IV's
version_compare(PHP_VERSION, '5.3.3',
'>=');
if (!$result) {
return false;
}
// prior to PHP 5.4.0 OPENSSL_RAW_DATA and
OPENSSL_ZERO_PADDING were not defined. instead of expecting an integer
// $options openssl_encrypt expected a boolean $raw_data.
if (!defined('OPENSSL_RAW_DATA')) {
$this->openssl_options = true;
} else {
$this->openssl_options = OPENSSL_RAW_DATA |
OPENSSL_ZERO_PADDING;
}
$methods = openssl_get_cipher_methods();
if (in_array($this->cipher_name_openssl, $methods)) {
return true;
}
// not all of openssl's symmetric cipher's
support ctr. for those
// that don't we'll emulate it
switch ($this->mode) {
case self::MODE_CTR:
if (in_array($this->cipher_name_openssl_ecb,
$methods)) {
$this->openssl_emulate_ctr = true;
return true;
}
}
return false;
case self::ENGINE_MCRYPT:
set_error_handler(array($this, 'do_nothing'));
$result = $this->cipher_name_mcrypt &&
extension_loaded('mcrypt') &&
in_array($this->cipher_name_mcrypt,
mcrypt_list_algorithms());
restore_error_handler();
return $result;
case self::ENGINE_INTERNAL:
return true;
}
return false;
}
/**
* Sets the preferred crypt engine
*
* Currently, $engine could be:
*
* - \phpseclib\Crypt\Base::ENGINE_OPENSSL [very fast]
*
* - \phpseclib\Crypt\Base::ENGINE_MCRYPT [fast]
*
* - \phpseclib\Crypt\Base::ENGINE_INTERNAL [slow]
*
* If the preferred crypt engine is not available the fastest available
one will be used
*
* @see self::__construct()
* @param int $engine
* @access public
*/
function setPreferredEngine($engine)
{
switch ($engine) {
//case self::ENGINE_OPENSSL;
case self::ENGINE_MCRYPT:
case self::ENGINE_INTERNAL:
$this->preferredEngine = $engine;
break;
default:
$this->preferredEngine = self::ENGINE_OPENSSL;
}
$this->_setEngine();
}
/**
* Returns the engine currently being utilized
*
* @see self::_setEngine()
* @access public
*/
function getEngine()
{
return $this->engine;
}
/**
* Sets the engine as appropriate
*
* @see self::__construct()
* @access private
*/
function _setEngine()
{
$this->engine = null;
$candidateEngines = array(
$this->preferredEngine,
self::ENGINE_OPENSSL,
self::ENGINE_MCRYPT
);
foreach ($candidateEngines as $engine) {
if ($this->isValidEngine($engine)) {
$this->engine = $engine;
break;
}
}
if (!$this->engine) {
$this->engine = self::ENGINE_INTERNAL;
}
if ($this->engine != self::ENGINE_MCRYPT &&
$this->enmcrypt) {
set_error_handler(array($this, 'do_nothing'));
// Closing the current mcrypt resource(s). _mcryptSetup() will,
if needed,
// (re)open them with the module named in
$this->cipher_name_mcrypt
mcrypt_module_close($this->enmcrypt);
mcrypt_module_close($this->demcrypt);
$this->enmcrypt = null;
$this->demcrypt = null;
if ($this->ecb) {
mcrypt_module_close($this->ecb);
$this->ecb = null;
}
restore_error_handler();
}
$this->changed = true;
}
/**
* Encrypts a block
*
* Note: Must be extended by the child \phpseclib\Crypt\* class
*
* @access private
* @param string $in
* @return string
*/
abstract function _encryptBlock($in);
/**
* Decrypts a block
*
* Note: Must be extended by the child \phpseclib\Crypt\* class
*
* @access private
* @param string $in
* @return string
*/
abstract function _decryptBlock($in);
/**
* Setup the key (expansion)
*
* Only used if $engine == self::ENGINE_INTERNAL
*
* Note: Must extend by the child \phpseclib\Crypt\* class
*
* @see self::_setup()
* @access private
*/
abstract function _setupKey();
/**
* Setup the self::ENGINE_INTERNAL $engine
*
* (re)init, if necessary, the internal cipher $engine and flush all
$buffers
* Used (only) if $engine == self::ENGINE_INTERNAL
*
* _setup() will be called each time if $changed === true
* typically this happens when using one or more of following public
methods:
*
* - setKey()
*
* - setIV()
*
* - disableContinuousBuffer()
*
* - First run of encrypt() / decrypt() with no init-settings
*
* @see self::setKey()
* @see self::setIV()
* @see self::disableContinuousBuffer()
* @access private
* @internal _setup() is always called before en/decryption.
* @internal Could, but not must, extend by the child Crypt_* class
*/
function _setup()
{
$this->_clearBuffers();
$this->_setupKey();
if ($this->use_inline_crypt) {
$this->_setupInlineCrypt();
}
}
/**
* Setup the self::ENGINE_MCRYPT $engine
*
* (re)init, if necessary, the (ext)mcrypt resources and flush all
$buffers
* Used (only) if $engine = self::ENGINE_MCRYPT
*
* _setupMcrypt() will be called each time if $changed === true
* typically this happens when using one or more of following public
methods:
*
* - setKey()
*
* - setIV()
*
* - disableContinuousBuffer()
*
* - First run of encrypt() / decrypt()
*
* @see self::setKey()
* @see self::setIV()
* @see self::disableContinuousBuffer()
* @access private
* @internal Could, but not must, extend by the child Crypt_* class
*/
function _setupMcrypt()
{
$this->_clearBuffers();
$this->enchanged = $this->dechanged = true;
if (!isset($this->enmcrypt)) {
static $mcrypt_modes = array(
self::MODE_CTR => 'ctr',
self::MODE_ECB => MCRYPT_MODE_ECB,
self::MODE_CBC => MCRYPT_MODE_CBC,
self::MODE_CFB => 'ncfb',
self::MODE_CFB8 => MCRYPT_MODE_CFB,
self::MODE_OFB => MCRYPT_MODE_NOFB,
self::MODE_STREAM => MCRYPT_MODE_STREAM,
);
$this->demcrypt =
mcrypt_module_open($this->cipher_name_mcrypt, '',
$mcrypt_modes[$this->mode], '');
$this->enmcrypt =
mcrypt_module_open($this->cipher_name_mcrypt, '',
$mcrypt_modes[$this->mode], '');
// we need the $ecb mcrypt resource (only) in MODE_CFB with
enableContinuousBuffer()
// to workaround mcrypt's broken ncfb implementation in
buffered mode
// see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps}
if ($this->mode == self::MODE_CFB) {
$this->ecb =
mcrypt_module_open($this->cipher_name_mcrypt, '',
MCRYPT_MODE_ECB, '');
}
} // else should mcrypt_generic_deinit be called?
if ($this->mode == self::MODE_CFB) {
mcrypt_generic_init($this->ecb, $this->key,
str_repeat("\0", $this->block_size));
}
}
/**
* Pads a string
*
* Pads a string using the RSA PKCS padding standards so that its
length is a multiple of the blocksize.
* $this->block_size - (strlen($text) % $this->block_size) bytes
are added, each of which is equal to
* chr($this->block_size - (strlen($text) % $this->block_size)
*
* If padding is disabled and $text is not a multiple of the blocksize,
the string will be padded regardless
* and padding will, hence forth, be enabled.
*
* @see self::_unpad()
* @param string $text
* @access private
* @return string
*/
function _pad($text)
{
$length = strlen($text);
if (!$this->padding) {
if ($length % $this->block_size == 0) {
return $text;
} else {
user_error("The plaintext's length ($length) is
not a multiple of the block size ({$this->block_size})");
$this->padding = true;
}
}
$pad = $this->block_size - ($length % $this->block_size);
return str_pad($text, $length + $pad, chr($pad));
}
/**
* Unpads a string.
*
* If padding is enabled and the reported padding length is invalid the
encryption key will be assumed to be wrong
* and false will be returned.
*
* @see self::_pad()
* @param string $text
* @access private
* @return string
*/
function _unpad($text)
{
if (!$this->padding) {
return $text;
}
$length = ord($text[strlen($text) - 1]);
if (!$length || $length > $this->block_size) {
return false;
}
return substr($text, 0, -$length);
}
/**
* Clears internal buffers
*
* Clearing/resetting the internal buffers is done everytime
* after disableContinuousBuffer() or on cipher $engine (re)init
* ie after setKey() or setIV()
*
* @access public
* @internal Could, but not must, extend by the child Crypt_* class
*/
function _clearBuffers()
{
$this->enbuffer = $this->debuffer =
array('ciphertext' => '', 'xor' =>
'', 'pos' => 0, 'enmcrypt_init' =>
true);
// mcrypt's handling of invalid's $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");
}
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @access private
* @return string
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
/**
* String Pop
*
* Inspired by array_pop
*
* @param string $string
* @param int $index
* @access private
* @return string
*/
function _string_pop(&$string, $index = 1)
{
$substr = substr($string, -$index);
$string = substr($string, 0, -$index);
return $substr;
}
/**
* Increment the current string
*
* @see self::decrypt()
* @see self::encrypt()
* @param string $var
* @access private
*/
function _increment_str(&$var)
{
for ($i = 4; $i <= strlen($var); $i+= 4) {
$temp = substr($var, -$i, 4);
switch ($temp) {
case "\xFF\xFF\xFF\xFF":
$var = substr_replace($var,
"\x00\x00\x00\x00", -$i, 4);
break;
case "\x7F\xFF\xFF\xFF":
$var = substr_replace($var,
"\x80\x00\x00\x00", -$i, 4);
return;
default:
$temp = unpack('Nnum', $temp);
$var = substr_replace($var, pack('N',
$temp['num'] + 1), -$i, 4);
return;
}
}
$remainder = strlen($var) % 4;
if ($remainder == 0) {
return;
}
$temp = unpack('Nnum', str_pad(substr($var, 0,
$remainder), 4, "\0", STR_PAD_LEFT));
$temp = substr(pack('N', $temp['num'] + 1),
-$remainder);
$var = substr_replace($var, $temp, 0, $remainder);
}
/**
* Setup the performance-optimized function for de/encrypt()
*
* Stores the created (or existing) callback function-name
* in $this->inline_crypt
*
* Internally for phpseclib developers:
*
* _setupInlineCrypt() would be called only if:
*
* - $engine == self::ENGINE_INTERNAL and
*
* - $use_inline_crypt === true
*
* - each time on _setup(), after(!) _setupKey()
*
*
* This ensures that _setupInlineCrypt() has always a
* full ready2go initializated internal cipher $engine state
* where, for example, the keys allready expanded,
* keys/block_size calculated and such.
*
* It is, each time if called, the responsibility of
_setupInlineCrypt():
*
* - to set $this->inline_crypt to a valid and fully working
callback function
* as a (faster) replacement for encrypt() / decrypt()
*
* - NOT to create unlimited callback functions (for memory
reasons!)
* no matter how often _setupInlineCrypt() would be called. At
some
* point of amount they must be generic re-useable.
*
* - the code of _setupInlineCrypt() it self,
* and the generated callback code,
* must be, in following order:
* - 100% safe
* - 100% compatible to encrypt()/decrypt()
* - using only php5+ features/lang-constructs/php-extensions if
* compatibility (down to php4) or fallback is provided
* - readable/maintainable/understandable/commented and...
not-cryptic-styled-code :-)
* - >= 10% faster than encrypt()/decrypt() [which is, by the
way,
* the reason for the existence of _setupInlineCrypt() :-)]
* - memory-nice
* - short (as good as possible)
*
* Note: - _setupInlineCrypt() is using _createInlineCryptFunction() to
create the full callback function code.
* - In case of using inline crypting, _setupInlineCrypt() must
extend by the child \phpseclib\Crypt\* class.
* - The following variable names are reserved:
* - $_* (all variable names prefixed with an underscore)
* - $self (object reference to it self. Do not use $this, but
$self instead)
* - $in (the content of $in has to en/decrypt by the generated
code)
* - The callback function should not use the 'return'
statement, but en/decrypt'ing the content of $in only
*
*
* @see self::_setup()
* @see self::_createInlineCryptFunction()
* @see self::encrypt()
* @see self::decrypt()
* @access private
* @internal If a Crypt_* class providing inline crypting it must
extend _setupInlineCrypt()
*/
function _setupInlineCrypt()
{
// If, for any reason, an extending \phpseclib\Crypt\Base()
\phpseclib\Crypt\* class
// not using inline crypting then it must be ensured that:
$this->use_inline_crypt = false
// ie in the class var declaration of $use_inline_crypt in general
for the \phpseclib\Crypt\* class,
// in the constructor at object instance-time
// or, if it's runtime-specific, at runtime
$this->use_inline_crypt = false;
}
/**
* Creates the performance-optimized function for en/decrypt()
*
* Internally for phpseclib developers:
*
* _createInlineCryptFunction():
*
* - merge the $cipher_code [setup'ed by _setupInlineCrypt()]
* with the current [$this->]mode of operation code
*
* - create the $inline function, which called by encrypt() /
decrypt()
* as its replacement to speed up the en/decryption operations.
*
* - return the name of the created $inline callback function
*
* - used to speed up en/decryption
*
*
*
* The main reason why can speed up things [up to 50%] this way are:
*
* - using variables more effective then regular.
* (ie no use of expensive arrays but integers $k_0, $k_1 ...
* or even, for example, the pure $key[] values hardcoded)
*
* - avoiding 1000's of function calls of ie _encryptBlock()
* but inlining the crypt operations.
* in the mode of operation for() loop.
*
* - full loop unroll the (sometimes key-dependent) rounds
* avoiding this way ++$i counters and runtime-if's etc...
*
* The basic code architectur of the generated $inline en/decrypt()
* lambda function, in pseudo php, is:
*
* <code>
*
+----------------------------------------------------------------------------------------------+
* | callback $inline = create_function:
|
* | lambda_function_0001_crypt_ECB($action, $text)
|
* | {
|
* | INSERT PHP CODE OF:
|
* | $cipher_code['init_crypt']; //
general init code. |
* | // ie:
$sbox'es declarations used for |
* | // encrypt
and decrypt'ing. |
* |
|
* | switch ($action) {
|
* | case 'encrypt':
|
* | INSERT PHP CODE OF:
|
* | $cipher_code['init_encrypt']; //
encrypt sepcific init code. |
* | ie:
specified $key or $box |
* |
declarations for encrypt'ing. |
* |
|
* | foreach ($ciphertext) {
|
* | $in = $block_size of $ciphertext;
|
* |
|
* | INSERT PHP CODE OF:
|
* | $cipher_code['encrypt_block']; //
encrypt's (string) $in, which is always: |
* | // strlen($in)
== $this->block_size |
* | // here comes
the cipher algorithm in action |
* | // for
encryption. |
* | //
$cipher_code['encrypt_block'] has to |
* | // encrypt the
content of the $in variable |
* |
|
* | $plaintext .= $in;
|
* | }
|
* | return $plaintext;
|
* |
|
* | case 'decrypt':
|
* | INSERT PHP CODE OF:
|
* | $cipher_code['init_decrypt']; //
decrypt sepcific init code |
* | ie:
specified $key or $box |
* |
declarations for decrypt'ing. |
* | foreach ($plaintext) {
|
* | $in = $block_size of $plaintext;
|
* |
|
* | INSERT PHP CODE OF:
|
* | $cipher_code['decrypt_block']; //
decrypt's (string) $in, which is always |
* | // strlen($in)
== $this->block_size |
* | // here comes
the cipher algorithm in action |
* | // for
decryption. |
* | //
$cipher_code['decrypt_block'] has to |
* | // decrypt the
content of the $in variable |
* | $ciphertext .= $in;
|
* | }
|
* | return $ciphertext;
|
* | }
|
* | }
|
*
+----------------------------------------------------------------------------------------------+
* </code>
*
* See also the \phpseclib\Crypt\*::_setupInlineCrypt()'s for
* productive inline $cipher_code's how they works.
*
* Structure of:
* <code>
* $cipher_code = array(
* 'init_crypt' => (string) '', //
optional
* 'init_encrypt' => (string) '', //
optional
* 'init_decrypt' => (string) '', //
optional
* 'encrypt_block' => (string) '', //
required
* 'decrypt_block' => (string) '' //
required
* );
* </code>
*
* @see self::_setupInlineCrypt()
* @see self::encrypt()
* @see self::decrypt()
* @param array $cipher_code
* @access private
* @return string (the name of the created callback function)
*/
function _createInlineCryptFunction($cipher_code)
{
$block_size = $this->block_size;
// optional
$init_crypt = isset($cipher_code['init_crypt']) ?
$cipher_code['init_crypt'] : '';
$init_encrypt = isset($cipher_code['init_encrypt']) ?
$cipher_code['init_encrypt'] : '';
$init_decrypt = isset($cipher_code['init_decrypt']) ?
$cipher_code['init_decrypt'] : '';
// required
$encrypt_block = $cipher_code['encrypt_block'];
$decrypt_block = $cipher_code['decrypt_block'];
// Generating mode of operation inline code,
// merged with the $cipher_code algorithm
// for encrypt- and decryption.
switch ($this->mode) {
case self::MODE_ECB:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_plaintext_len = strlen($_text);
for ($_i = 0; $_i < $_plaintext_len; $_i+=
'.$block_size.') {
$in = substr($_text, $_i,
'.$block_size.');
'.$encrypt_block.'
$_ciphertext.= $in;
}
return $_ciphertext;
';
$decrypt = $init_decrypt . '
$_plaintext = "";
$_text = str_pad($_text, strlen($_text) +
('.$block_size.' - strlen($_text) % '.$block_size.') %
'.$block_size.', chr(0));
$_ciphertext_len = strlen($_text);
for ($_i = 0; $_i < $_ciphertext_len; $_i+=
'.$block_size.') {
$in = substr($_text, $_i,
'.$block_size.');
'.$decrypt_block.'
$_plaintext.= $in;
}
return $self->_unpad($_plaintext);
';
break;
case self::MODE_CTR:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_plaintext_len = strlen($_text);
$_xor = $self->encryptIV;
$_buffer = &$self->enbuffer;
if (strlen($_buffer["ciphertext"])) {
for ($_i = 0; $_i < $_plaintext_len; $_i+=
'.$block_size.') {
$_block = substr($_text, $_i,
'.$block_size.');
if (strlen($_block) >
strlen($_buffer["ciphertext"])) {
$in = $_xor;
'.$encrypt_block.'
$self->_increment_str($_xor);
$_buffer["ciphertext"].= $in;
}
$_key =
$self->_string_shift($_buffer["ciphertext"],
'.$block_size.');
$_ciphertext.= $_block ^ $_key;
}
} else {
for ($_i = 0; $_i < $_plaintext_len; $_i+=
'.$block_size.') {
$_block = substr($_text, $_i,
'.$block_size.');
$in = $_xor;
'.$encrypt_block.'
$self->_increment_str($_xor);
$_key = $in;
$_ciphertext.= $_block ^ $_key;
}
}
if ($self->continuousBuffer) {
$self->encryptIV = $_xor;
if ($_start = $_plaintext_len %
'.$block_size.') {
$_buffer["ciphertext"] =
substr($_key, $_start) . $_buffer["ciphertext"];
}
}
return $_ciphertext;
';
$decrypt = $init_encrypt . '
$_plaintext = "";
$_ciphertext_len = strlen($_text);
$_xor = $self->decryptIV;
$_buffer = &$self->debuffer;
if (strlen($_buffer["ciphertext"])) {
for ($_i = 0; $_i < $_ciphertext_len; $_i+=
'.$block_size.') {
$_block = substr($_text, $_i,
'.$block_size.');
if (strlen($_block) >
strlen($_buffer["ciphertext"])) {
$in = $_xor;
'.$encrypt_block.'
$self->_increment_str($_xor);
$_buffer["ciphertext"].= $in;
}
$_key =
$self->_string_shift($_buffer["ciphertext"],
'.$block_size.');
$_plaintext.= $_block ^ $_key;
}
} else {
for ($_i = 0; $_i < $_ciphertext_len; $_i+=
'.$block_size.') {
$_block = substr($_text, $_i,
'.$block_size.');
$in = $_xor;
'.$encrypt_block.'
$self->_increment_str($_xor);
$_key = $in;
$_plaintext.= $_block ^ $_key;
}
}
if ($self->continuousBuffer) {
$self->decryptIV = $_xor;
if ($_start = $_ciphertext_len %
'.$block_size.') {
$_buffer["ciphertext"] =
substr($_key, $_start) . $_buffer["ciphertext"];
}
}
return $_plaintext;
';
break;
case self::MODE_CFB:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_buffer = &$self->enbuffer;
if ($self->continuousBuffer) {
$_iv = &$self->encryptIV;
$_pos = &$_buffer["pos"];
} else {
$_iv = $self->encryptIV;
$_pos = 0;
}
$_len = strlen($_text);
$_i = 0;
if ($_pos) {
$_orig_pos = $_pos;
$_max = '.$block_size.' - $_pos;
if ($_len >= $_max) {
$_i = $_max;
$_len-= $_max;
$_pos = 0;
} else {
$_i = $_len;
$_pos+= $_len;
$_len = 0;
}
$_ciphertext = substr($_iv, $_orig_pos) ^ $_text;
$_iv = substr_replace($_iv, $_ciphertext,
$_orig_pos, $_i);
}
while ($_len >= '.$block_size.') {
$in = $_iv;
'.$encrypt_block.';
$_iv = $in ^ substr($_text, $_i,
'.$block_size.');
$_ciphertext.= $_iv;
$_len-= '.$block_size.';
$_i+= '.$block_size.';
}
if ($_len) {
$in = $_iv;
'.$encrypt_block.'
$_iv = $in;
$_block = $_iv ^ substr($_text, $_i);
$_iv = substr_replace($_iv, $_block, 0, $_len);
$_ciphertext.= $_block;
$_pos = $_len;
}
return $_ciphertext;
';
$decrypt = $init_encrypt . '
$_plaintext = "";
$_buffer = &$self->debuffer;
if ($self->continuousBuffer) {
$_iv = &$self->decryptIV;
$_pos = &$_buffer["pos"];
} else {
$_iv = $self->decryptIV;
$_pos = 0;
}
$_len = strlen($_text);
$_i = 0;
if ($_pos) {
$_orig_pos = $_pos;
$_max = '.$block_size.' - $_pos;
if ($_len >= $_max) {
$_i = $_max;
$_len-= $_max;
$_pos = 0;
} else {
$_i = $_len;
$_pos+= $_len;
$_len = 0;
}
$_plaintext = substr($_iv, $_orig_pos) ^ $_text;
$_iv = substr_replace($_iv, substr($_text, 0, $_i),
$_orig_pos, $_i);
}
while ($_len >= '.$block_size.') {
$in = $_iv;
'.$encrypt_block.'
$_iv = $in;
$cb = substr($_text, $_i,
'.$block_size.');
$_plaintext.= $_iv ^ $cb;
$_iv = $cb;
$_len-= '.$block_size.';
$_i+= '.$block_size.';
}
if ($_len) {
$in = $_iv;
'.$encrypt_block.'
$_iv = $in;
$_plaintext.= $_iv ^ substr($_text, $_i);
$_iv = substr_replace($_iv, substr($_text, $_i), 0,
$_len);
$_pos = $_len;
}
return $_plaintext;
';
break;
case self::MODE_CFB8:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_len = strlen($_text);
$_iv = $self->encryptIV;
for ($_i = 0; $_i < $_len; ++$_i) {
$in = $_iv;
'.$encrypt_block.'
$_ciphertext .= ($_c = $_text[$_i] ^ $in);
$_iv = substr($_iv, 1) . $_c;
}
if ($self->continuousBuffer) {
if ($_len >= '.$block_size.') {
$self->encryptIV = substr($_ciphertext,
-'.$block_size.');
} else {
$self->encryptIV =
substr($self->encryptIV, $_len - '.$block_size.') .
substr($_ciphertext, -$_len);
}
}
return $_ciphertext;
';
$decrypt = $init_encrypt . '
$_plaintext = "";
$_len = strlen($_text);
$_iv = $self->decryptIV;
for ($_i = 0; $_i < $_len; ++$_i) {
$in = $_iv;
'.$encrypt_block.'
$_plaintext .= $_text[$_i] ^ $in;
$_iv = substr($_iv, 1) . $_text[$_i];
}
if ($self->continuousBuffer) {
if ($_len >= '.$block_size.') {
$self->decryptIV = substr($_text,
-'.$block_size.');
} else {
$self->decryptIV =
substr($self->decryptIV, $_len - '.$block_size.') .
substr($_text, -$_len);
}
}
return $_plaintext;
';
break;
case self::MODE_OFB:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_plaintext_len = strlen($_text);
$_xor = $self->encryptIV;
$_buffer = &$self->enbuffer;
if (strlen($_buffer["xor"])) {
for ($_i = 0; $_i < $_plaintext_len; $_i+=
'.$block_size.') {
$_block = substr($_text, $_i,
'.$block_size.');
if (strlen($_block) >
strlen($_buffer["xor"])) {
$in = $_xor;
'.$encrypt_block.'
$_xor = $in;
$_buffer["xor"].= $_xor;
}
$_key =
$self->_string_shift($_buffer["xor"],
'.$block_size.');
$_ciphertext.= $_block ^ $_key;
}
} else {
for ($_i = 0; $_i < $_plaintext_len; $_i+=
'.$block_size.') {
$in = $_xor;
'.$encrypt_block.'
$_xor = $in;
$_ciphertext.= substr($_text, $_i,
'.$block_size.') ^ $_xor;
}
$_key = $_xor;
}
if ($self->continuousBuffer) {
$self->encryptIV = $_xor;
if ($_start = $_plaintext_len %
'.$block_size.') {
$_buffer["xor"] = substr($_key,
$_start) . $_buffer["xor"];
}
}
return $_ciphertext;
';
$decrypt = $init_encrypt . '
$_plaintext = "";
$_ciphertext_len = strlen($_text);
$_xor = $self->decryptIV;
$_buffer = &$self->debuffer;
if (strlen($_buffer["xor"])) {
for ($_i = 0; $_i < $_ciphertext_len; $_i+=
'.$block_size.') {
$_block = substr($_text, $_i,
'.$block_size.');
if (strlen($_block) >
strlen($_buffer["xor"])) {
$in = $_xor;
'.$encrypt_block.'
$_xor = $in;
$_buffer["xor"].= $_xor;
}
$_key =
$self->_string_shift($_buffer["xor"],
'.$block_size.');
$_plaintext.= $_block ^ $_key;
}
} else {
for ($_i = 0; $_i < $_ciphertext_len; $_i+=
'.$block_size.') {
$in = $_xor;
'.$encrypt_block.'
$_xor = $in;
$_plaintext.= substr($_text, $_i,
'.$block_size.') ^ $_xor;
}
$_key = $_xor;
}
if ($self->continuousBuffer) {
$self->decryptIV = $_xor;
if ($_start = $_ciphertext_len %
'.$block_size.') {
$_buffer["xor"] = substr($_key,
$_start) . $_buffer["xor"];
}
}
return $_plaintext;
';
break;
case self::MODE_STREAM:
$encrypt = $init_encrypt . '
$_ciphertext = "";
'.$encrypt_block.'
return $_ciphertext;
';
$decrypt = $init_decrypt . '
$_plaintext = "";
'.$decrypt_block.'
return $_plaintext;
';
break;
// case self::MODE_CBC:
default:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_plaintext_len = strlen($_text);
$in = $self->encryptIV;
for ($_i = 0; $_i < $_plaintext_len; $_i+=
'.$block_size.') {
$in = substr($_text, $_i,
'.$block_size.') ^ $in;
'.$encrypt_block.'
$_ciphertext.= $in;
}
if ($self->continuousBuffer) {
$self->encryptIV = $in;
}
return $_ciphertext;
';
$decrypt = $init_decrypt . '
$_plaintext = "";
$_text = str_pad($_text, strlen($_text) +
('.$block_size.' - strlen($_text) % '.$block_size.') %
'.$block_size.', chr(0));
$_ciphertext_len = strlen($_text);
$_iv = $self->decryptIV;
for ($_i = 0; $_i < $_ciphertext_len; $_i+=
'.$block_size.') {
$in = $_block = substr($_text, $_i,
'.$block_size.');
'.$decrypt_block.'
$_plaintext.= $in ^ $_iv;
$_iv = $_block;
}
if ($self->continuousBuffer) {
$self->decryptIV = $_iv;
}
return $self->_unpad($_plaintext);
';
break;
}
// Create the $inline function and return its name as string. Ready
to run!
eval('$func = function ($_action, &$self, $_text) { '
. $init_crypt . 'if ($_action == "encrypt") { ' .
$encrypt . ' } else { ' . $decrypt . ' } };');
return $func;
}
/**
* Holds the lambda_functions table (classwide)
*
* Each name of the lambda function, created from
* _setupInlineCrypt() && _createInlineCryptFunction()
* is stored, classwide (!), here for reusing.
*
* The string-based index of $function is a classwide
* unique value representing, at least, the $mode of
* operation (or more... depends of the optimizing level)
* for which $mode the lambda function was created.
*
* @access private
* @return array &$functions
*/
function &_getLambdaFunctions()
{
static $functions = array();
return $functions;
}
/**
* Generates a digest from $bytes
*
* @see self::_setupInlineCrypt()
* @access private
* @param string $bytes
* @return string
*/
function _hashInlineCryptFunction($bytes)
{
if (!isset(self::$WHIRLPOOL_AVAILABLE)) {
self::$WHIRLPOOL_AVAILABLE = extension_loaded('hash')
&& in_array('whirlpool', hash_algos());
}
$result = '';
$hash = $bytes;
switch (true) {
case self::$WHIRLPOOL_AVAILABLE:
foreach (str_split($bytes, 64) as $t) {
$hash = hash('whirlpool', $hash, true);
$result .= $t ^ $hash;
}
return $result . hash('whirlpool', $hash, true);
default:
$len = strlen($bytes);
for ($i = 0; $i < $len; $i+=20) {
$t = substr($bytes, $i, 20);
$hash = pack('H*', sha1($hash));
$result .= $t ^ $hash;
}
return $result . pack('H*', sha1($hash));
}
}
/**
* Convert float to int
*
* On ARM CPUs converting floats to ints doesn't always work
*
* @access private
* @param string $x
* @return int
*/
function safe_intval($x)
{
switch (true) {
case is_int($x):
// PHP 5.3, per http://php.net/releases/5_3_0.php, introduced
"more consistent float rounding"
case (php_uname('m') & "\xDF\xDF\xDF")
!= 'ARM':
return $x;
}
return (fmod($x, 0x80000000) & 0x7FFFFFFF) |
((fmod(floor($x / 0x80000000), 2) & 1) << 31);
}
/**
* eval()'able string for in-line float to int
*
* @access private
* @return string
*/
function safe_intval_inline()
{
switch (true) {
case defined('PHP_INT_SIZE') && PHP_INT_SIZE
== 8:
case (php_uname('m') & "\xDF\xDF\xDF")
!= 'ARM':
return '%s';
break;
default:
$safeint = '(is_int($temp = %s) ? $temp : (fmod($temp,
0x80000000) & 0x7FFFFFFF) | ';
return $safeint . '((fmod(floor($temp / 0x80000000),
2) & 1) << 31))';
}
}
/**
* Dummy error handler to suppress mcrypt errors
*
* @access private
*/
function do_nothing()
{
}
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php000064400000063744151156520620017344
0ustar00<?php
/**
* Pure-PHP implementation of Blowfish.
*
* Uses mcrypt, if available, and an internal implementation, otherwise.
*
* PHP version 5
*
* Useful resources are as follows:
*
* - {@link http://en.wikipedia.org/wiki/Blowfish_(cipher) Wikipedia
description of Blowfish}
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $blowfish = new \phpseclib\Crypt\Blowfish();
*
* $blowfish->setKey('12345678901234567890123456789012');
*
* $plaintext = str_repeat('a', 1024);
*
* echo $blowfish->decrypt($blowfish->encrypt($plaintext));
* ?>
* </code>
*
* @category Crypt
* @package Blowfish
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Pure-PHP implementation of Blowfish.
*
* @package Blowfish
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
* @access public
*/
class Blowfish extends Base
{
/**
* Block Length of the cipher
*
* @see \phpseclib\Crypt\Base::block_size
* @var int
* @access private
*/
var $block_size = 8;
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib\Crypt\Base::cipher_name_mcrypt
* @var string
* @access private
*/
var $cipher_name_mcrypt = 'blowfish';
/**
* Optimizing value while CFB-encrypting
*
* @see \phpseclib\Crypt\Base::cfb_init_len
* @var int
* @access private
*/
var $cfb_init_len = 500;
/**
* The fixed subkeys boxes ($sbox0 - $sbox3) with 256 entries each
*
* S-Box 0
*
* @access private
* @var array
*/
var $sbox0 = array(
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed,
0x6a267e96, 0xba7c9045, 0xf12c7f99,
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8,
0x71574e69, 0xa458fea3, 0xf4933d7e,
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d,
0xc25a59b5, 0x9c30d539, 0x2af26013,
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0,
0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3,
0xaa55ab94, 0x57489862, 0x63e81440,
0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af,
0x7c72e993, 0xb3ee1411, 0x636fbc2a,
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33,
0x6c24cf5c, 0x7a325381, 0x28958677,
0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc,
0xfb21a991, 0x487cac60, 0x5dec8032,
0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81,
0xd396acc5, 0x0f6d6ff3, 0x83f44239,
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842,
0xf6e96c9a, 0x670c9c61, 0xabd388f0,
0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c,
0x137a3be4, 0xba3bf050, 0x7efb2a98,
0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619,
0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62,
0x363f7706, 0x1bfedf72, 0x429b023d,
0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9,
0x80991b7b, 0x25d479d8, 0xf6e8def7,
0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6,
0x409f60c4, 0x5e5c9ec2, 0x196a2463,
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f,
0x9b30952c, 0xcc814544, 0xaf5ebd09,
0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857,
0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe,
0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa,
0xfd238760, 0x53317b48, 0x3e00df82,
0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6,
0x287effc3, 0xac6732c6, 0x8c4f5573,
0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98,
0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da,
0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d,
0xae909198, 0xeaad8e71, 0x6b93d5a0,
0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb,
0xf2122b64, 0x8888b812, 0x900df01c,
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218,
0xbe0e1777, 0xea752dfe, 0x8b021fa1,
0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0,
0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065,
0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d,
0x2071b35e, 0x226800bb, 0x57b8e0af,
0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389,
0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca,
0x7b14a94a, 0x1b510052, 0x9a532915,
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5,
0x571be91f, 0xf296ec6b, 0x2a0dd915,
0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d,
0xa99f8fa1, 0x08ba4799, 0x6e85076a
);
/**
* S-Box 1
*
* @access private
* @var array
*/
var $sbox1 = array(
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0,
0x49a7df7d, 0x9cee60b8, 0x8fedb266,
0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5,
0x75094c29, 0xa0591340, 0xe4183a3e,
0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07,
0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f,
0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c,
0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a,
0x3cb574b2, 0x25837a58, 0xdc0921bd,
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581,
0x37c2dadc, 0xc8b57634, 0x9af3dda7,
0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99,
0x3bea0e2f, 0x3280bba1, 0x183eb331,
0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290,
0x24977c79, 0x5679b072, 0xbcaf89af,
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f,
0x2e6b7124, 0x501adde6, 0x9f84cd87,
0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a,
0xdb851dfa, 0x63094366, 0xc464c3d2,
0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43,
0x2a65c451, 0x50940002, 0x133ae4dd,
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1,
0xd7a3c76b, 0x3c11183b, 0x5924a509,
0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570,
0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6,
0x5266c825, 0x2e4cc978, 0x9c10b36a,
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7,
0x361d2b3d, 0x1939260f, 0x19c27960,
0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595,
0xa67bc883, 0xb17f37d1, 0x018cff28,
0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f,
0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830,
0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc,
0x60622ca7, 0x9cab5cab, 0xb2f3846e,
0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32,
0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a,
0x97e32d77, 0x11ed935f, 0x16681281,
0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5,
0x1b227263, 0x9b83c3ff, 0x1ac24696,
0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef,
0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6,
0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e,
0x86854dc7, 0xe44b476a, 0x3d816250,
0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3,
0x69cb7492, 0x47848a0b, 0x5692b285,
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a,
0x0c55f5ea, 0x1dadf43e, 0x233f7061,
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759,
0xcbee7460, 0x4085f2a7, 0xce77326e,
0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa,
0xc50c06c2, 0x5a04abfc, 0x800bcadc,
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3,
0x105588cd, 0x675fda79, 0xe3674340,
0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7,
0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
);
/**
* S-Box 2
*
* @access private
* @var array
*/
var $sbox2 = array(
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7,
0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e,
0x97244546, 0x14214f74, 0xbf8b8840,
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec,
0x03bd9785, 0x7fac6dd0, 0x31cb8504,
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825,
0x530429f4, 0x0a2c86da, 0xe9b66dfb,
0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2,
0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35,
0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74,
0xdd5b4332, 0x6841e7f7, 0xca7820fb,
0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a,
0x20838d87, 0xfe6ba9b7, 0xd096954b,
0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56,
0x3f3125f9, 0x5ef47e1c, 0x9029317c,
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548,
0xe4c66d22, 0x48c1133f, 0xc70f86dc,
0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb,
0xd59bc0d1, 0xf2bcc18f, 0x41113564,
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2,
0x02e1329e, 0xaf664fd1, 0xcad18115,
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e,
0xe6ba0d99, 0xde720c8c, 0x2da2f728,
0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f,
0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60,
0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7,
0xcc00ffa3, 0xb5390f92, 0x690fed0b,
0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88,
0x515bad24, 0x7b9479bf, 0x763bd6eb,
0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7,
0xc66a2b3b, 0x12754ccc, 0x782ef11c,
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018,
0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e,
0xda86a85f, 0xbebfe988, 0x64e4c3fe,
0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346,
0xf6381fb0, 0x7745ae04, 0xd736fccc,
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be,
0xbde8ae24, 0x55464299, 0xbf582e61,
0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3,
0xc8b38e74, 0xb475f255, 0x46fcd9b9,
0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e,
0x20b45770, 0x8cd55591, 0xc902de4c,
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6,
0xe0a9dc09, 0x662d09a1, 0xc4324633,
0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d,
0x0ba5a4df, 0xa186f20f, 0x2868f169,
0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01,
0xa70683fa, 0xa002b5c4, 0x0de6d027,
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28,
0xc0f586e0, 0x006058aa, 0x30dc7d62,
0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56,
0x90bcb6de, 0xebfc7da1, 0xce591d76,
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f,
0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e,
0xb161e6f8, 0xa28514d9, 0x6c51133c,
0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234,
0x92638212, 0x670efa8e, 0x406000e0
);
/**
* S-Box 3
*
* @access private
* @var array
*/
var $sbox3 = array(
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e,
0x4fa33742, 0xd3822740, 0x99bc9bbe,
0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b,
0x21a19045, 0xb26eb1be, 0x6a366eb4,
0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee,
0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0,
0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba,
0x9be96a4d, 0x8fe51550, 0xba645bd6,
0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3,
0xf752f7da, 0x3f046f69, 0x77fa0a59,
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a,
0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf,
0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d,
0x283b57cc, 0xf8d56629, 0x79132e28,
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4,
0x88f46dba, 0x03a16125, 0x0564f0bd,
0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b,
0x1e6321f5, 0xf59c66fb, 0x26dcf319,
0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711,
0xc20ad9f8, 0xabcc5167, 0xccad925f,
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2,
0xfb3e7bce, 0x5121ce64, 0x774fbe32,
0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810,
0xdd6db224, 0x69852dfd, 0x09072166,
0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd,
0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8,
0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d,
0xe75b1357, 0xf8721671, 0xaf537d5d,
0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428,
0x95983a1d, 0x06b89fb4, 0xce6ea048,
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1,
0xe7933fdc, 0xbb3a792b, 0x344525bd,
0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e,
0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a,
0xc6913667, 0x8df9317c, 0xe0b12b4f,
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c,
0x15e6fc2a, 0x0f91fc71, 0x9b941525,
0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e,
0xe3056a0c, 0x10d25065, 0xcb03a442,
0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532,
0xe0d392df, 0xd3a0342b, 0x8971f21e,
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d,
0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166,
0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162,
0x5a75ebb5, 0x6e163697, 0x88d273cc,
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd,
0x327a140a, 0x45e1d006, 0xc3f27b9a,
0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905,
0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c,
0xf746ce76, 0x77afa1c5, 0x20756060,
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c,
0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132,
0xce77e25b, 0x578fdfe3, 0x3ac372e6
);
/**
* P-Array consists of 18 32-bit subkeys
*
* @var array
* @access private
*/
var $parray = array(
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822,
0x299f31d0,
0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf,
0x34e90c6c,
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9,
0x8979fb1b
);
/**
* The BCTX-working Array
*
* Holds the expanded key [p] and the key-depended s-boxes [sb]
*
* @var array
* @access private
*/
var $bctx;
/**
* Holds the last used key
*
* @var array
* @access private
*/
var $kl;
/**
* The Key Length (in bytes)
*
* @see \phpseclib\Crypt\Base::setKeyLength()
* @var int
* @access private
* @internal The max value is 256 / 8 = 32, the min value is 128 / 8 =
16. Exists in conjunction with $Nk
* because the encryption / decryption / key schedule creation
requires this number and not $key_length. We could
* derive this from $key_length or vice versa, but that'd mean
we'd have to do multiple shift operations, so in lieu
* of that, we'll just precompute it once.
*/
var $key_length = 16;
/**
* Sets the key length.
*
* Key lengths can be between 32 and 448 bits.
*
* @access public
* @param int $length
*/
function setKeyLength($length)
{
if ($length < 32) {
$this->key_length = 4;
} elseif ($length > 448) {
$this->key_length = 56;
} else {
$this->key_length = $length >> 3;
}
parent::setKeyLength($length);
}
/**
* Test for engine validity
*
* This is mainly just a wrapper to set things up for
\phpseclib\Crypt\Base::isValidEngine()
*
* @see \phpseclib\Crypt\Base::isValidEngine()
* @param int $engine
* @access public
* @return bool
*/
function isValidEngine($engine)
{
if ($engine == self::ENGINE_OPENSSL) {
if (version_compare(PHP_VERSION, '5.3.7') < 0
&& $this->key_length != 16) {
return false;
}
if ($this->key_length < 16) {
return false;
}
$this->cipher_name_openssl_ecb = 'bf-ecb';
$this->cipher_name_openssl = 'bf-' .
$this->_openssl_translate_mode();
}
return parent::isValidEngine($engine);
}
/**
* Setup the key (expansion)
*
* @see \phpseclib\Crypt\Base::_setupKey()
* @access private
*/
function _setupKey()
{
if (isset($this->kl['key']) && $this->key
=== $this->kl['key']) {
// already expanded
return;
}
$this->kl = array('key' => $this->key);
/* key-expanding p[] and S-Box building sb[] */
$this->bctx = array(
'p' => array(),
'sb' => array(
$this->sbox0,
$this->sbox1,
$this->sbox2,
$this->sbox3
)
);
// unpack binary string in unsigned chars
$key = array_values(unpack('C*', $this->key));
$keyl = count($key);
for ($j = 0, $i = 0; $i < 18; ++$i) {
// xor P1 with the first 32-bits of the key, xor P2 with the
second 32-bits ...
for ($data = 0, $k = 0; $k < 4; ++$k) {
$data = ($data << 8) | $key[$j];
if (++$j >= $keyl) {
$j = 0;
}
}
$this->bctx['p'][] = $this->parray[$i] ^ $data;
}
// encrypt the zero-string, replace P1 and P2 with the encrypted
data,
// encrypt P3 and P4 with the new P1 and P2, do it with all P-array
and subkeys
$data = "\0\0\0\0\0\0\0\0";
for ($i = 0; $i < 18; $i += 2) {
list($l, $r) = array_values(unpack('N*', $data =
$this->_encryptBlock($data)));
$this->bctx['p'][$i ] = $l;
$this->bctx['p'][$i + 1] = $r;
}
for ($i = 0; $i < 4; ++$i) {
for ($j = 0; $j < 256; $j += 2) {
list($l, $r) = array_values(unpack('N*', $data =
$this->_encryptBlock($data)));
$this->bctx['sb'][$i][$j ] = $l;
$this->bctx['sb'][$i][$j + 1] = $r;
}
}
}
/**
* Encrypts a block
*
* @access private
* @param string $in
* @return string
*/
function _encryptBlock($in)
{
$p = $this->bctx["p"];
// extract($this->bctx["sb"], EXTR_PREFIX_ALL,
"sb"); // slower
$sb_0 = $this->bctx["sb"][0];
$sb_1 = $this->bctx["sb"][1];
$sb_2 = $this->bctx["sb"][2];
$sb_3 = $this->bctx["sb"][3];
$in = unpack("N*", $in);
$l = $in[1];
$r = $in[2];
for ($i = 0; $i < 16; $i+= 2) {
$l^= $p[$i];
$r^= $this->safe_intval(($this->safe_intval($sb_0[$l
>> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^
$sb_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff]);
$r^= $p[$i + 1];
$l^= $this->safe_intval(($this->safe_intval($sb_0[$r
>> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^
$sb_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff]);
}
return pack("N*", $r ^ $p[17], $l ^ $p[16]);
}
/**
* Decrypts a block
*
* @access private
* @param string $in
* @return string
*/
function _decryptBlock($in)
{
$p = $this->bctx["p"];
$sb_0 = $this->bctx["sb"][0];
$sb_1 = $this->bctx["sb"][1];
$sb_2 = $this->bctx["sb"][2];
$sb_3 = $this->bctx["sb"][3];
$in = unpack("N*", $in);
$l = $in[1];
$r = $in[2];
for ($i = 17; $i > 2; $i-= 2) {
$l^= $p[$i];
$r^= $this->safe_intval(($this->safe_intval($sb_0[$l
>> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^
$sb_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff]);
$r^= $p[$i - 1];
$l^= $this->safe_intval(($this->safe_intval($sb_0[$r
>> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^
$sb_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff]);
}
return pack("N*", $r ^ $p[0], $l ^ $p[1]);
}
/**
* Setup the performance-optimized function for de/encrypt()
*
* @see \phpseclib\Crypt\Base::_setupInlineCrypt()
* @access private
*/
function _setupInlineCrypt()
{
$lambda_functions =& self::_getLambdaFunctions();
// We create max. 10 hi-optimized code for memory reason. Means:
For each $key one ultra fast inline-crypt function.
// (Currently, for Blowfish, one generated $lambda_function cost on
php5.5@32bit ~100kb unfreeable mem and ~180kb on php5.5@64bit)
// After that, we'll still create very fast optimized code but
not the hi-ultimative code, for each $mode one.
$gen_hi_opt_code = (bool)(count($lambda_functions) < 10);
// Generation of a unique hash for our generated code
$code_hash = "Crypt_Blowfish, {$this->mode}";
if ($gen_hi_opt_code) {
$code_hash = str_pad($code_hash, 32) .
$this->_hashInlineCryptFunction($this->key);
}
$safeint = $this->safe_intval_inline();
if (!isset($lambda_functions[$code_hash])) {
switch (true) {
case $gen_hi_opt_code:
$p = $this->bctx['p'];
$init_crypt = '
static $sb_0, $sb_1, $sb_2, $sb_3;
if (!$sb_0) {
$sb_0 = $self->bctx["sb"][0];
$sb_1 = $self->bctx["sb"][1];
$sb_2 = $self->bctx["sb"][2];
$sb_3 = $self->bctx["sb"][3];
}
';
break;
default:
$p = array();
for ($i = 0; $i < 18; ++$i) {
$p[] = '$p_' . $i;
}
$init_crypt = '
list($sb_0, $sb_1, $sb_2, $sb_3) =
$self->bctx["sb"];
list(' . implode(',', $p) . ')
= $self->bctx["p"];
';
}
// Generating encrypt code:
$encrypt_block = '
$in = unpack("N*", $in);
$l = $in[1];
$r = $in[2];
';
for ($i = 0; $i < 16; $i+= 2) {
$encrypt_block.= '
$l^= ' . $p[$i] . ';
$r^= ' . sprintf($safeint, '(' .
sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l
>> 16 & 0xff]') . ' ^
$sb_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff]') . ';
$r^= ' . $p[$i + 1] . ';
$l^= ' . sprintf($safeint, '(' .
sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r
>> 16 & 0xff]') . ' ^
$sb_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff]') . ';
';
}
$encrypt_block.= '
$in = pack("N*",
$r ^ ' . $p[17] . ',
$l ^ ' . $p[16] . '
);
';
// Generating decrypt code:
$decrypt_block = '
$in = unpack("N*", $in);
$l = $in[1];
$r = $in[2];
';
for ($i = 17; $i > 2; $i-= 2) {
$decrypt_block.= '
$l^= ' . $p[$i] . ';
$r^= ' . sprintf($safeint, '(' .
sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l
>> 16 & 0xff]') . ' ^
$sb_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff]') . ';
$r^= ' . $p[$i - 1] . ';
$l^= ' . sprintf($safeint, '(' .
sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r
>> 16 & 0xff]') . ' ^
$sb_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff]') . ';
';
}
$decrypt_block.= '
$in = pack("N*",
$r ^ ' . $p[0] . ',
$l ^ ' . $p[1] . '
);
';
$lambda_functions[$code_hash] =
$this->_createInlineCryptFunction(
array(
'init_crypt' => $init_crypt,
'init_encrypt' => '',
'init_decrypt' => '',
'encrypt_block' => $encrypt_block,
'decrypt_block' => $decrypt_block
)
);
}
$this->inline_crypt = $lambda_functions[$code_hash];
}
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php000064400000213503151156520630016171
0ustar00<?php
/**
* Pure-PHP implementation of DES.
*
* Uses mcrypt, if available, and an internal implementation, otherwise.
*
* PHP version 5
*
* Useful resources are as follows:
*
* - {@link http://en.wikipedia.org/wiki/DES_supplementary_material
Wikipedia: DES supplementary material}
* - {@link http://www.itl.nist.gov/fipspubs/fip46-2.htm FIPS 46-2 -
(DES), Data Encryption Standard}
* - {@link http://www.cs.eku.edu/faculty/styer/460/Encrypt/JS-DES.html
JavaScript DES Example}
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $des = new \phpseclib\Crypt\DES();
*
* $des->setKey('abcdefgh');
*
* $size = 10 * 1024;
* $plaintext = '';
* for ($i = 0; $i < $size; $i++) {
* $plaintext.= 'a';
* }
*
* echo $des->decrypt($des->encrypt($plaintext));
* ?>
* </code>
*
* @category Crypt
* @package DES
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Pure-PHP implementation of DES.
*
* @package DES
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class DES extends Base
{
/**#@+
* @access private
* @see \phpseclib\Crypt\DES::_setupKey()
* @see \phpseclib\Crypt\DES::_processBlock()
*/
/**
* Contains $keys[self::ENCRYPT]
*/
const ENCRYPT = 0;
/**
* Contains $keys[self::DECRYPT]
*/
const DECRYPT = 1;
/**#@-*/
/**
* Block Length of the cipher
*
* @see \phpseclib\Crypt\Base::block_size
* @var int
* @access private
*/
var $block_size = 8;
/**
* Key Length (in bytes)
*
* @see \phpseclib\Crypt\Base::setKeyLength()
* @var int
* @access private
*/
var $key_length = 8;
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib\Crypt\Base::cipher_name_mcrypt
* @var string
* @access private
*/
var $cipher_name_mcrypt = 'des';
/**
* The OpenSSL names of the cipher / modes
*
* @see \phpseclib\Crypt\Base::openssl_mode_names
* @var array
* @access private
*/
var $openssl_mode_names = array(
self::MODE_ECB => 'des-ecb',
self::MODE_CBC => 'des-cbc',
self::MODE_CFB => 'des-cfb',
self::MODE_OFB => 'des-ofb'
// self::MODE_CTR is undefined for DES
);
/**
* Optimizing value while CFB-encrypting
*
* @see \phpseclib\Crypt\Base::cfb_init_len
* @var int
* @access private
*/
var $cfb_init_len = 500;
/**
* Switch for DES/3DES encryption
*
* Used only if $engine == self::ENGINE_INTERNAL
*
* @see self::_setupKey()
* @see self::_processBlock()
* @var int
* @access private
*/
var $des_rounds = 1;
/**
* max possible size of $key
*
* @see self::setKey()
* @var string
* @access private
*/
var $key_length_max = 8;
/**
* The Key Schedule
*
* @see self::_setupKey()
* @var array
* @access private
*/
var $keys;
/**
* Shuffle table.
*
* For each byte value index, the entry holds an 8-byte string
* with each byte containing all bits in the same state as the
* corresponding bit in the index value.
*
* @see self::_processBlock()
* @see self::_setupKey()
* @var array
* @access private
*/
var $shuffle = array(
"\x00\x00\x00\x00\x00\x00\x00\x00",
"\x00\x00\x00\x00\x00\x00\x00\xFF",
"\x00\x00\x00\x00\x00\x00\xFF\x00",
"\x00\x00\x00\x00\x00\x00\xFF\xFF",
"\x00\x00\x00\x00\x00\xFF\x00\x00",
"\x00\x00\x00\x00\x00\xFF\x00\xFF",
"\x00\x00\x00\x00\x00\xFF\xFF\x00",
"\x00\x00\x00\x00\x00\xFF\xFF\xFF",
"\x00\x00\x00\x00\xFF\x00\x00\x00",
"\x00\x00\x00\x00\xFF\x00\x00\xFF",
"\x00\x00\x00\x00\xFF\x00\xFF\x00",
"\x00\x00\x00\x00\xFF\x00\xFF\xFF",
"\x00\x00\x00\x00\xFF\xFF\x00\x00",
"\x00\x00\x00\x00\xFF\xFF\x00\xFF",
"\x00\x00\x00\x00\xFF\xFF\xFF\x00",
"\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
"\x00\x00\x00\xFF\x00\x00\x00\x00",
"\x00\x00\x00\xFF\x00\x00\x00\xFF",
"\x00\x00\x00\xFF\x00\x00\xFF\x00",
"\x00\x00\x00\xFF\x00\x00\xFF\xFF",
"\x00\x00\x00\xFF\x00\xFF\x00\x00",
"\x00\x00\x00\xFF\x00\xFF\x00\xFF",
"\x00\x00\x00\xFF\x00\xFF\xFF\x00",
"\x00\x00\x00\xFF\x00\xFF\xFF\xFF",
"\x00\x00\x00\xFF\xFF\x00\x00\x00",
"\x00\x00\x00\xFF\xFF\x00\x00\xFF",
"\x00\x00\x00\xFF\xFF\x00\xFF\x00",
"\x00\x00\x00\xFF\xFF\x00\xFF\xFF",
"\x00\x00\x00\xFF\xFF\xFF\x00\x00",
"\x00\x00\x00\xFF\xFF\xFF\x00\xFF",
"\x00\x00\x00\xFF\xFF\xFF\xFF\x00",
"\x00\x00\x00\xFF\xFF\xFF\xFF\xFF",
"\x00\x00\xFF\x00\x00\x00\x00\x00",
"\x00\x00\xFF\x00\x00\x00\x00\xFF",
"\x00\x00\xFF\x00\x00\x00\xFF\x00",
"\x00\x00\xFF\x00\x00\x00\xFF\xFF",
"\x00\x00\xFF\x00\x00\xFF\x00\x00",
"\x00\x00\xFF\x00\x00\xFF\x00\xFF",
"\x00\x00\xFF\x00\x00\xFF\xFF\x00",
"\x00\x00\xFF\x00\x00\xFF\xFF\xFF",
"\x00\x00\xFF\x00\xFF\x00\x00\x00",
"\x00\x00\xFF\x00\xFF\x00\x00\xFF",
"\x00\x00\xFF\x00\xFF\x00\xFF\x00",
"\x00\x00\xFF\x00\xFF\x00\xFF\xFF",
"\x00\x00\xFF\x00\xFF\xFF\x00\x00",
"\x00\x00\xFF\x00\xFF\xFF\x00\xFF",
"\x00\x00\xFF\x00\xFF\xFF\xFF\x00",
"\x00\x00\xFF\x00\xFF\xFF\xFF\xFF",
"\x00\x00\xFF\xFF\x00\x00\x00\x00",
"\x00\x00\xFF\xFF\x00\x00\x00\xFF",
"\x00\x00\xFF\xFF\x00\x00\xFF\x00",
"\x00\x00\xFF\xFF\x00\x00\xFF\xFF",
"\x00\x00\xFF\xFF\x00\xFF\x00\x00",
"\x00\x00\xFF\xFF\x00\xFF\x00\xFF",
"\x00\x00\xFF\xFF\x00\xFF\xFF\x00",
"\x00\x00\xFF\xFF\x00\xFF\xFF\xFF",
"\x00\x00\xFF\xFF\xFF\x00\x00\x00",
"\x00\x00\xFF\xFF\xFF\x00\x00\xFF",
"\x00\x00\xFF\xFF\xFF\x00\xFF\x00",
"\x00\x00\xFF\xFF\xFF\x00\xFF\xFF",
"\x00\x00\xFF\xFF\xFF\xFF\x00\x00",
"\x00\x00\xFF\xFF\xFF\xFF\x00\xFF",
"\x00\x00\xFF\xFF\xFF\xFF\xFF\x00",
"\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF",
"\x00\xFF\x00\x00\x00\x00\x00\x00",
"\x00\xFF\x00\x00\x00\x00\x00\xFF",
"\x00\xFF\x00\x00\x00\x00\xFF\x00",
"\x00\xFF\x00\x00\x00\x00\xFF\xFF",
"\x00\xFF\x00\x00\x00\xFF\x00\x00",
"\x00\xFF\x00\x00\x00\xFF\x00\xFF",
"\x00\xFF\x00\x00\x00\xFF\xFF\x00",
"\x00\xFF\x00\x00\x00\xFF\xFF\xFF",
"\x00\xFF\x00\x00\xFF\x00\x00\x00",
"\x00\xFF\x00\x00\xFF\x00\x00\xFF",
"\x00\xFF\x00\x00\xFF\x00\xFF\x00",
"\x00\xFF\x00\x00\xFF\x00\xFF\xFF",
"\x00\xFF\x00\x00\xFF\xFF\x00\x00",
"\x00\xFF\x00\x00\xFF\xFF\x00\xFF",
"\x00\xFF\x00\x00\xFF\xFF\xFF\x00",
"\x00\xFF\x00\x00\xFF\xFF\xFF\xFF",
"\x00\xFF\x00\xFF\x00\x00\x00\x00",
"\x00\xFF\x00\xFF\x00\x00\x00\xFF",
"\x00\xFF\x00\xFF\x00\x00\xFF\x00",
"\x00\xFF\x00\xFF\x00\x00\xFF\xFF",
"\x00\xFF\x00\xFF\x00\xFF\x00\x00",
"\x00\xFF\x00\xFF\x00\xFF\x00\xFF",
"\x00\xFF\x00\xFF\x00\xFF\xFF\x00",
"\x00\xFF\x00\xFF\x00\xFF\xFF\xFF",
"\x00\xFF\x00\xFF\xFF\x00\x00\x00",
"\x00\xFF\x00\xFF\xFF\x00\x00\xFF",
"\x00\xFF\x00\xFF\xFF\x00\xFF\x00",
"\x00\xFF\x00\xFF\xFF\x00\xFF\xFF",
"\x00\xFF\x00\xFF\xFF\xFF\x00\x00",
"\x00\xFF\x00\xFF\xFF\xFF\x00\xFF",
"\x00\xFF\x00\xFF\xFF\xFF\xFF\x00",
"\x00\xFF\x00\xFF\xFF\xFF\xFF\xFF",
"\x00\xFF\xFF\x00\x00\x00\x00\x00",
"\x00\xFF\xFF\x00\x00\x00\x00\xFF",
"\x00\xFF\xFF\x00\x00\x00\xFF\x00",
"\x00\xFF\xFF\x00\x00\x00\xFF\xFF",
"\x00\xFF\xFF\x00\x00\xFF\x00\x00",
"\x00\xFF\xFF\x00\x00\xFF\x00\xFF",
"\x00\xFF\xFF\x00\x00\xFF\xFF\x00",
"\x00\xFF\xFF\x00\x00\xFF\xFF\xFF",
"\x00\xFF\xFF\x00\xFF\x00\x00\x00",
"\x00\xFF\xFF\x00\xFF\x00\x00\xFF",
"\x00\xFF\xFF\x00\xFF\x00\xFF\x00",
"\x00\xFF\xFF\x00\xFF\x00\xFF\xFF",
"\x00\xFF\xFF\x00\xFF\xFF\x00\x00",
"\x00\xFF\xFF\x00\xFF\xFF\x00\xFF",
"\x00\xFF\xFF\x00\xFF\xFF\xFF\x00",
"\x00\xFF\xFF\x00\xFF\xFF\xFF\xFF",
"\x00\xFF\xFF\xFF\x00\x00\x00\x00",
"\x00\xFF\xFF\xFF\x00\x00\x00\xFF",
"\x00\xFF\xFF\xFF\x00\x00\xFF\x00",
"\x00\xFF\xFF\xFF\x00\x00\xFF\xFF",
"\x00\xFF\xFF\xFF\x00\xFF\x00\x00",
"\x00\xFF\xFF\xFF\x00\xFF\x00\xFF",
"\x00\xFF\xFF\xFF\x00\xFF\xFF\x00",
"\x00\xFF\xFF\xFF\x00\xFF\xFF\xFF",
"\x00\xFF\xFF\xFF\xFF\x00\x00\x00",
"\x00\xFF\xFF\xFF\xFF\x00\x00\xFF",
"\x00\xFF\xFF\xFF\xFF\x00\xFF\x00",
"\x00\xFF\xFF\xFF\xFF\x00\xFF\xFF",
"\x00\xFF\xFF\xFF\xFF\xFF\x00\x00",
"\x00\xFF\xFF\xFF\xFF\xFF\x00\xFF",
"\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00",
"\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
"\xFF\x00\x00\x00\x00\x00\x00\x00",
"\xFF\x00\x00\x00\x00\x00\x00\xFF",
"\xFF\x00\x00\x00\x00\x00\xFF\x00",
"\xFF\x00\x00\x00\x00\x00\xFF\xFF",
"\xFF\x00\x00\x00\x00\xFF\x00\x00",
"\xFF\x00\x00\x00\x00\xFF\x00\xFF",
"\xFF\x00\x00\x00\x00\xFF\xFF\x00",
"\xFF\x00\x00\x00\x00\xFF\xFF\xFF",
"\xFF\x00\x00\x00\xFF\x00\x00\x00",
"\xFF\x00\x00\x00\xFF\x00\x00\xFF",
"\xFF\x00\x00\x00\xFF\x00\xFF\x00",
"\xFF\x00\x00\x00\xFF\x00\xFF\xFF",
"\xFF\x00\x00\x00\xFF\xFF\x00\x00",
"\xFF\x00\x00\x00\xFF\xFF\x00\xFF",
"\xFF\x00\x00\x00\xFF\xFF\xFF\x00",
"\xFF\x00\x00\x00\xFF\xFF\xFF\xFF",
"\xFF\x00\x00\xFF\x00\x00\x00\x00",
"\xFF\x00\x00\xFF\x00\x00\x00\xFF",
"\xFF\x00\x00\xFF\x00\x00\xFF\x00",
"\xFF\x00\x00\xFF\x00\x00\xFF\xFF",
"\xFF\x00\x00\xFF\x00\xFF\x00\x00",
"\xFF\x00\x00\xFF\x00\xFF\x00\xFF",
"\xFF\x00\x00\xFF\x00\xFF\xFF\x00",
"\xFF\x00\x00\xFF\x00\xFF\xFF\xFF",
"\xFF\x00\x00\xFF\xFF\x00\x00\x00",
"\xFF\x00\x00\xFF\xFF\x00\x00\xFF",
"\xFF\x00\x00\xFF\xFF\x00\xFF\x00",
"\xFF\x00\x00\xFF\xFF\x00\xFF\xFF",
"\xFF\x00\x00\xFF\xFF\xFF\x00\x00",
"\xFF\x00\x00\xFF\xFF\xFF\x00\xFF",
"\xFF\x00\x00\xFF\xFF\xFF\xFF\x00",
"\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF",
"\xFF\x00\xFF\x00\x00\x00\x00\x00",
"\xFF\x00\xFF\x00\x00\x00\x00\xFF",
"\xFF\x00\xFF\x00\x00\x00\xFF\x00",
"\xFF\x00\xFF\x00\x00\x00\xFF\xFF",
"\xFF\x00\xFF\x00\x00\xFF\x00\x00",
"\xFF\x00\xFF\x00\x00\xFF\x00\xFF",
"\xFF\x00\xFF\x00\x00\xFF\xFF\x00",
"\xFF\x00\xFF\x00\x00\xFF\xFF\xFF",
"\xFF\x00\xFF\x00\xFF\x00\x00\x00",
"\xFF\x00\xFF\x00\xFF\x00\x00\xFF",
"\xFF\x00\xFF\x00\xFF\x00\xFF\x00",
"\xFF\x00\xFF\x00\xFF\x00\xFF\xFF",
"\xFF\x00\xFF\x00\xFF\xFF\x00\x00",
"\xFF\x00\xFF\x00\xFF\xFF\x00\xFF",
"\xFF\x00\xFF\x00\xFF\xFF\xFF\x00",
"\xFF\x00\xFF\x00\xFF\xFF\xFF\xFF",
"\xFF\x00\xFF\xFF\x00\x00\x00\x00",
"\xFF\x00\xFF\xFF\x00\x00\x00\xFF",
"\xFF\x00\xFF\xFF\x00\x00\xFF\x00",
"\xFF\x00\xFF\xFF\x00\x00\xFF\xFF",
"\xFF\x00\xFF\xFF\x00\xFF\x00\x00",
"\xFF\x00\xFF\xFF\x00\xFF\x00\xFF",
"\xFF\x00\xFF\xFF\x00\xFF\xFF\x00",
"\xFF\x00\xFF\xFF\x00\xFF\xFF\xFF",
"\xFF\x00\xFF\xFF\xFF\x00\x00\x00",
"\xFF\x00\xFF\xFF\xFF\x00\x00\xFF",
"\xFF\x00\xFF\xFF\xFF\x00\xFF\x00",
"\xFF\x00\xFF\xFF\xFF\x00\xFF\xFF",
"\xFF\x00\xFF\xFF\xFF\xFF\x00\x00",
"\xFF\x00\xFF\xFF\xFF\xFF\x00\xFF",
"\xFF\x00\xFF\xFF\xFF\xFF\xFF\x00",
"\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF",
"\xFF\xFF\x00\x00\x00\x00\x00\x00",
"\xFF\xFF\x00\x00\x00\x00\x00\xFF",
"\xFF\xFF\x00\x00\x00\x00\xFF\x00",
"\xFF\xFF\x00\x00\x00\x00\xFF\xFF",
"\xFF\xFF\x00\x00\x00\xFF\x00\x00",
"\xFF\xFF\x00\x00\x00\xFF\x00\xFF",
"\xFF\xFF\x00\x00\x00\xFF\xFF\x00",
"\xFF\xFF\x00\x00\x00\xFF\xFF\xFF",
"\xFF\xFF\x00\x00\xFF\x00\x00\x00",
"\xFF\xFF\x00\x00\xFF\x00\x00\xFF",
"\xFF\xFF\x00\x00\xFF\x00\xFF\x00",
"\xFF\xFF\x00\x00\xFF\x00\xFF\xFF",
"\xFF\xFF\x00\x00\xFF\xFF\x00\x00",
"\xFF\xFF\x00\x00\xFF\xFF\x00\xFF",
"\xFF\xFF\x00\x00\xFF\xFF\xFF\x00",
"\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF",
"\xFF\xFF\x00\xFF\x00\x00\x00\x00",
"\xFF\xFF\x00\xFF\x00\x00\x00\xFF",
"\xFF\xFF\x00\xFF\x00\x00\xFF\x00",
"\xFF\xFF\x00\xFF\x00\x00\xFF\xFF",
"\xFF\xFF\x00\xFF\x00\xFF\x00\x00",
"\xFF\xFF\x00\xFF\x00\xFF\x00\xFF",
"\xFF\xFF\x00\xFF\x00\xFF\xFF\x00",
"\xFF\xFF\x00\xFF\x00\xFF\xFF\xFF",
"\xFF\xFF\x00\xFF\xFF\x00\x00\x00",
"\xFF\xFF\x00\xFF\xFF\x00\x00\xFF",
"\xFF\xFF\x00\xFF\xFF\x00\xFF\x00",
"\xFF\xFF\x00\xFF\xFF\x00\xFF\xFF",
"\xFF\xFF\x00\xFF\xFF\xFF\x00\x00",
"\xFF\xFF\x00\xFF\xFF\xFF\x00\xFF",
"\xFF\xFF\x00\xFF\xFF\xFF\xFF\x00",
"\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF",
"\xFF\xFF\xFF\x00\x00\x00\x00\x00",
"\xFF\xFF\xFF\x00\x00\x00\x00\xFF",
"\xFF\xFF\xFF\x00\x00\x00\xFF\x00",
"\xFF\xFF\xFF\x00\x00\x00\xFF\xFF",
"\xFF\xFF\xFF\x00\x00\xFF\x00\x00",
"\xFF\xFF\xFF\x00\x00\xFF\x00\xFF",
"\xFF\xFF\xFF\x00\x00\xFF\xFF\x00",
"\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF",
"\xFF\xFF\xFF\x00\xFF\x00\x00\x00",
"\xFF\xFF\xFF\x00\xFF\x00\x00\xFF",
"\xFF\xFF\xFF\x00\xFF\x00\xFF\x00",
"\xFF\xFF\xFF\x00\xFF\x00\xFF\xFF",
"\xFF\xFF\xFF\x00\xFF\xFF\x00\x00",
"\xFF\xFF\xFF\x00\xFF\xFF\x00\xFF",
"\xFF\xFF\xFF\x00\xFF\xFF\xFF\x00",
"\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF",
"\xFF\xFF\xFF\xFF\x00\x00\x00\x00",
"\xFF\xFF\xFF\xFF\x00\x00\x00\xFF",
"\xFF\xFF\xFF\xFF\x00\x00\xFF\x00",
"\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF",
"\xFF\xFF\xFF\xFF\x00\xFF\x00\x00",
"\xFF\xFF\xFF\xFF\x00\xFF\x00\xFF",
"\xFF\xFF\xFF\xFF\x00\xFF\xFF\x00",
"\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF",
"\xFF\xFF\xFF\xFF\xFF\x00\x00\x00",
"\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF",
"\xFF\xFF\xFF\xFF\xFF\x00\xFF\x00",
"\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF",
"\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00",
"\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF",
"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00",
"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
);
/**
* IP mapping helper table.
*
* Indexing this table with each source byte performs the initial bit
permutation.
*
* @var array
* @access private
*/
var $ipmap = array(
0x00, 0x10, 0x01, 0x11, 0x20, 0x30, 0x21, 0x31,
0x02, 0x12, 0x03, 0x13, 0x22, 0x32, 0x23, 0x33,
0x40, 0x50, 0x41, 0x51, 0x60, 0x70, 0x61, 0x71,
0x42, 0x52, 0x43, 0x53, 0x62, 0x72, 0x63, 0x73,
0x04, 0x14, 0x05, 0x15, 0x24, 0x34, 0x25, 0x35,
0x06, 0x16, 0x07, 0x17, 0x26, 0x36, 0x27, 0x37,
0x44, 0x54, 0x45, 0x55, 0x64, 0x74, 0x65, 0x75,
0x46, 0x56, 0x47, 0x57, 0x66, 0x76, 0x67, 0x77,
0x80, 0x90, 0x81, 0x91, 0xA0, 0xB0, 0xA1, 0xB1,
0x82, 0x92, 0x83, 0x93, 0xA2, 0xB2, 0xA3, 0xB3,
0xC0, 0xD0, 0xC1, 0xD1, 0xE0, 0xF0, 0xE1, 0xF1,
0xC2, 0xD2, 0xC3, 0xD3, 0xE2, 0xF2, 0xE3, 0xF3,
0x84, 0x94, 0x85, 0x95, 0xA4, 0xB4, 0xA5, 0xB5,
0x86, 0x96, 0x87, 0x97, 0xA6, 0xB6, 0xA7, 0xB7,
0xC4, 0xD4, 0xC5, 0xD5, 0xE4, 0xF4, 0xE5, 0xF5,
0xC6, 0xD6, 0xC7, 0xD7, 0xE6, 0xF6, 0xE7, 0xF7,
0x08, 0x18, 0x09, 0x19, 0x28, 0x38, 0x29, 0x39,
0x0A, 0x1A, 0x0B, 0x1B, 0x2A, 0x3A, 0x2B, 0x3B,
0x48, 0x58, 0x49, 0x59, 0x68, 0x78, 0x69, 0x79,
0x4A, 0x5A, 0x4B, 0x5B, 0x6A, 0x7A, 0x6B, 0x7B,
0x0C, 0x1C, 0x0D, 0x1D, 0x2C, 0x3C, 0x2D, 0x3D,
0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F,
0x4C, 0x5C, 0x4D, 0x5D, 0x6C, 0x7C, 0x6D, 0x7D,
0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F,
0x88, 0x98, 0x89, 0x99, 0xA8, 0xB8, 0xA9, 0xB9,
0x8A, 0x9A, 0x8B, 0x9B, 0xAA, 0xBA, 0xAB, 0xBB,
0xC8, 0xD8, 0xC9, 0xD9, 0xE8, 0xF8, 0xE9, 0xF9,
0xCA, 0xDA, 0xCB, 0xDB, 0xEA, 0xFA, 0xEB, 0xFB,
0x8C, 0x9C, 0x8D, 0x9D, 0xAC, 0xBC, 0xAD, 0xBD,
0x8E, 0x9E, 0x8F, 0x9F, 0xAE, 0xBE, 0xAF, 0xBF,
0xCC, 0xDC, 0xCD, 0xDD, 0xEC, 0xFC, 0xED, 0xFD,
0xCE, 0xDE, 0xCF, 0xDF, 0xEE, 0xFE, 0xEF, 0xFF
);
/**
* Inverse IP mapping helper table.
* Indexing this table with a byte value reverses the bit order.
*
* @var array
* @access private
*/
var $invipmap = array(
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
);
/**
* Pre-permuted S-box1
*
* Each box ($sbox1-$sbox8) has been vectorized, then each value
pre-permuted using the
* P table: concatenation can then be replaced by exclusive ORs.
*
* @var array
* @access private
*/
var $sbox1 = array(
0x00808200, 0x00000000, 0x00008000, 0x00808202,
0x00808002, 0x00008202, 0x00000002, 0x00008000,
0x00000200, 0x00808200, 0x00808202, 0x00000200,
0x00800202, 0x00808002, 0x00800000, 0x00000002,
0x00000202, 0x00800200, 0x00800200, 0x00008200,
0x00008200, 0x00808000, 0x00808000, 0x00800202,
0x00008002, 0x00800002, 0x00800002, 0x00008002,
0x00000000, 0x00000202, 0x00008202, 0x00800000,
0x00008000, 0x00808202, 0x00000002, 0x00808000,
0x00808200, 0x00800000, 0x00800000, 0x00000200,
0x00808002, 0x00008000, 0x00008200, 0x00800002,
0x00000200, 0x00000002, 0x00800202, 0x00008202,
0x00808202, 0x00008002, 0x00808000, 0x00800202,
0x00800002, 0x00000202, 0x00008202, 0x00808200,
0x00000202, 0x00800200, 0x00800200, 0x00000000,
0x00008002, 0x00008200, 0x00000000, 0x00808002
);
/**
* Pre-permuted S-box2
*
* @var array
* @access private
*/
var $sbox2 = array(
0x40084010, 0x40004000, 0x00004000, 0x00084010,
0x00080000, 0x00000010, 0x40080010, 0x40004010,
0x40000010, 0x40084010, 0x40084000, 0x40000000,
0x40004000, 0x00080000, 0x00000010, 0x40080010,
0x00084000, 0x00080010, 0x40004010, 0x00000000,
0x40000000, 0x00004000, 0x00084010, 0x40080000,
0x00080010, 0x40000010, 0x00000000, 0x00084000,
0x00004010, 0x40084000, 0x40080000, 0x00004010,
0x00000000, 0x00084010, 0x40080010, 0x00080000,
0x40004010, 0x40080000, 0x40084000, 0x00004000,
0x40080000, 0x40004000, 0x00000010, 0x40084010,
0x00084010, 0x00000010, 0x00004000, 0x40000000,
0x00004010, 0x40084000, 0x00080000, 0x40000010,
0x00080010, 0x40004010, 0x40000010, 0x00080010,
0x00084000, 0x00000000, 0x40004000, 0x00004010,
0x40000000, 0x40080010, 0x40084010, 0x00084000
);
/**
* Pre-permuted S-box3
*
* @var array
* @access private
*/
var $sbox3 = array(
0x00000104, 0x04010100, 0x00000000, 0x04010004,
0x04000100, 0x00000000, 0x00010104, 0x04000100,
0x00010004, 0x04000004, 0x04000004, 0x00010000,
0x04010104, 0x00010004, 0x04010000, 0x00000104,
0x04000000, 0x00000004, 0x04010100, 0x00000100,
0x00010100, 0x04010000, 0x04010004, 0x00010104,
0x04000104, 0x00010100, 0x00010000, 0x04000104,
0x00000004, 0x04010104, 0x00000100, 0x04000000,
0x04010100, 0x04000000, 0x00010004, 0x00000104,
0x00010000, 0x04010100, 0x04000100, 0x00000000,
0x00000100, 0x00010004, 0x04010104, 0x04000100,
0x04000004, 0x00000100, 0x00000000, 0x04010004,
0x04000104, 0x00010000, 0x04000000, 0x04010104,
0x00000004, 0x00010104, 0x00010100, 0x04000004,
0x04010000, 0x04000104, 0x00000104, 0x04010000,
0x00010104, 0x00000004, 0x04010004, 0x00010100
);
/**
* Pre-permuted S-box4
*
* @var array
* @access private
*/
var $sbox4 = array(
0x80401000, 0x80001040, 0x80001040, 0x00000040,
0x00401040, 0x80400040, 0x80400000, 0x80001000,
0x00000000, 0x00401000, 0x00401000, 0x80401040,
0x80000040, 0x00000000, 0x00400040, 0x80400000,
0x80000000, 0x00001000, 0x00400000, 0x80401000,
0x00000040, 0x00400000, 0x80001000, 0x00001040,
0x80400040, 0x80000000, 0x00001040, 0x00400040,
0x00001000, 0x00401040, 0x80401040, 0x80000040,
0x00400040, 0x80400000, 0x00401000, 0x80401040,
0x80000040, 0x00000000, 0x00000000, 0x00401000,
0x00001040, 0x00400040, 0x80400040, 0x80000000,
0x80401000, 0x80001040, 0x80001040, 0x00000040,
0x80401040, 0x80000040, 0x80000000, 0x00001000,
0x80400000, 0x80001000, 0x00401040, 0x80400040,
0x80001000, 0x00001040, 0x00400000, 0x80401000,
0x00000040, 0x00400000, 0x00001000, 0x00401040
);
/**
* Pre-permuted S-box5
*
* @var array
* @access private
*/
var $sbox5 = array(
0x00000080, 0x01040080, 0x01040000, 0x21000080,
0x00040000, 0x00000080, 0x20000000, 0x01040000,
0x20040080, 0x00040000, 0x01000080, 0x20040080,
0x21000080, 0x21040000, 0x00040080, 0x20000000,
0x01000000, 0x20040000, 0x20040000, 0x00000000,
0x20000080, 0x21040080, 0x21040080, 0x01000080,
0x21040000, 0x20000080, 0x00000000, 0x21000000,
0x01040080, 0x01000000, 0x21000000, 0x00040080,
0x00040000, 0x21000080, 0x00000080, 0x01000000,
0x20000000, 0x01040000, 0x21000080, 0x20040080,
0x01000080, 0x20000000, 0x21040000, 0x01040080,
0x20040080, 0x00000080, 0x01000000, 0x21040000,
0x21040080, 0x00040080, 0x21000000, 0x21040080,
0x01040000, 0x00000000, 0x20040000, 0x21000000,
0x00040080, 0x01000080, 0x20000080, 0x00040000,
0x00000000, 0x20040000, 0x01040080, 0x20000080
);
/**
* Pre-permuted S-box6
*
* @var array
* @access private
*/
var $sbox6 = array(
0x10000008, 0x10200000, 0x00002000, 0x10202008,
0x10200000, 0x00000008, 0x10202008, 0x00200000,
0x10002000, 0x00202008, 0x00200000, 0x10000008,
0x00200008, 0x10002000, 0x10000000, 0x00002008,
0x00000000, 0x00200008, 0x10002008, 0x00002000,
0x00202000, 0x10002008, 0x00000008, 0x10200008,
0x10200008, 0x00000000, 0x00202008, 0x10202000,
0x00002008, 0x00202000, 0x10202000, 0x10000000,
0x10002000, 0x00000008, 0x10200008, 0x00202000,
0x10202008, 0x00200000, 0x00002008, 0x10000008,
0x00200000, 0x10002000, 0x10000000, 0x00002008,
0x10000008, 0x10202008, 0x00202000, 0x10200000,
0x00202008, 0x10202000, 0x00000000, 0x10200008,
0x00000008, 0x00002000, 0x10200000, 0x00202008,
0x00002000, 0x00200008, 0x10002008, 0x00000000,
0x10202000, 0x10000000, 0x00200008, 0x10002008
);
/**
* Pre-permuted S-box7
*
* @var array
* @access private
*/
var $sbox7 = array(
0x00100000, 0x02100001, 0x02000401, 0x00000000,
0x00000400, 0x02000401, 0x00100401, 0x02100400,
0x02100401, 0x00100000, 0x00000000, 0x02000001,
0x00000001, 0x02000000, 0x02100001, 0x00000401,
0x02000400, 0x00100401, 0x00100001, 0x02000400,
0x02000001, 0x02100000, 0x02100400, 0x00100001,
0x02100000, 0x00000400, 0x00000401, 0x02100401,
0x00100400, 0x00000001, 0x02000000, 0x00100400,
0x02000000, 0x00100400, 0x00100000, 0x02000401,
0x02000401, 0x02100001, 0x02100001, 0x00000001,
0x00100001, 0x02000000, 0x02000400, 0x00100000,
0x02100400, 0x00000401, 0x00100401, 0x02100400,
0x00000401, 0x02000001, 0x02100401, 0x02100000,
0x00100400, 0x00000000, 0x00000001, 0x02100401,
0x00000000, 0x00100401, 0x02100000, 0x00000400,
0x02000001, 0x02000400, 0x00000400, 0x00100001
);
/**
* Pre-permuted S-box8
*
* @var array
* @access private
*/
var $sbox8 = array(
0x08000820, 0x00000800, 0x00020000, 0x08020820,
0x08000000, 0x08000820, 0x00000020, 0x08000000,
0x00020020, 0x08020000, 0x08020820, 0x00020800,
0x08020800, 0x00020820, 0x00000800, 0x00000020,
0x08020000, 0x08000020, 0x08000800, 0x00000820,
0x00020800, 0x00020020, 0x08020020, 0x08020800,
0x00000820, 0x00000000, 0x00000000, 0x08020020,
0x08000020, 0x08000800, 0x00020820, 0x00020000,
0x00020820, 0x00020000, 0x08020800, 0x00000800,
0x00000020, 0x08020020, 0x00000800, 0x00020820,
0x08000800, 0x00000020, 0x08000020, 0x08020000,
0x08020020, 0x08000000, 0x00020000, 0x08000820,
0x00000000, 0x08020820, 0x00020020, 0x08000020,
0x08020000, 0x08000800, 0x08000820, 0x00000000,
0x08020820, 0x00020800, 0x00020800, 0x00000820,
0x00000820, 0x00020020, 0x08000000, 0x08020800
);
/**
* Test for engine validity
*
* This is mainly just a wrapper to set things up for
\phpseclib\Crypt\Base::isValidEngine()
*
* @see \phpseclib\Crypt\Base::isValidEngine()
* @param int $engine
* @access public
* @return bool
*/
function isValidEngine($engine)
{
if ($this->key_length_max == 8) {
if ($engine == self::ENGINE_OPENSSL) {
$this->cipher_name_openssl_ecb = 'des-ecb';
$this->cipher_name_openssl = 'des-' .
$this->_openssl_translate_mode();
}
}
return parent::isValidEngine($engine);
}
/**
* Sets the key.
*
* Keys can be of any length. DES, itself, uses 64-bit keys (eg.
strlen($key) == 8), however, we
* 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.
*
* If the key is not explicitly set, it'll be assumed to be all
zero's.
*
* @see \phpseclib\Crypt\Base::setKey()
* @access public
* @param string $key
*/
function setKey($key)
{
// We check/cut here only up to max length of the key.
// Key padding to the proper length will be done in _setupKey()
if (strlen($key) > $this->key_length_max) {
$key = substr($key, 0, $this->key_length_max);
}
// Sets the key
parent::setKey($key);
}
/**
* Encrypts a block
*
* @see \phpseclib\Crypt\Base::_encryptBlock()
* @see \phpseclib\Crypt\Base::encrypt()
* @see self::encrypt()
* @access private
* @param string $in
* @return string
*/
function _encryptBlock($in)
{
return $this->_processBlock($in, self::ENCRYPT);
}
/**
* Decrypts a block
*
* @see \phpseclib\Crypt\Base::_decryptBlock()
* @see \phpseclib\Crypt\Base::decrypt()
* @see self::decrypt()
* @access private
* @param string $in
* @return string
*/
function _decryptBlock($in)
{
return $this->_processBlock($in, self::DECRYPT);
}
/**
* Encrypts or decrypts a 64-bit block
*
* $mode should be either self::ENCRYPT or self::DECRYPT. See
* {@link http://en.wikipedia.org/wiki/Image:Feistel.png Feistel.png}
to get a general
* idea of what this function does.
*
* @see self::_encryptBlock()
* @see self::_decryptBlock()
* @access private
* @param string $block
* @param int $mode
* @return string
*/
function _processBlock($block, $mode)
{
static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7,
$sbox8, $shuffleip, $shuffleinvip;
if (!$sbox1) {
$sbox1 = array_map("intval", $this->sbox1);
$sbox2 = array_map("intval", $this->sbox2);
$sbox3 = array_map("intval", $this->sbox3);
$sbox4 = array_map("intval", $this->sbox4);
$sbox5 = array_map("intval", $this->sbox5);
$sbox6 = array_map("intval", $this->sbox6);
$sbox7 = array_map("intval", $this->sbox7);
$sbox8 = array_map("intval", $this->sbox8);
/* Merge $shuffle with $[inv]ipmap */
for ($i = 0; $i < 256; ++$i) {
$shuffleip[] = $this->shuffle[$this->ipmap[$i]];
$shuffleinvip[] =
$this->shuffle[$this->invipmap[$i]];
}
}
$keys = $this->keys[$mode];
$ki = -1;
// Do the initial IP permutation.
$t = unpack('Nl/Nr', $block);
list($l, $r) = array($t['l'], $t['r']);
$block = ($shuffleip[ $r & 0xFF] &
"\x80\x80\x80\x80\x80\x80\x80\x80") |
($shuffleip[($r >> 8) & 0xFF] &
"\x40\x40\x40\x40\x40\x40\x40\x40") |
($shuffleip[($r >> 16) & 0xFF] &
"\x20\x20\x20\x20\x20\x20\x20\x20") |
($shuffleip[($r >> 24) & 0xFF] &
"\x10\x10\x10\x10\x10\x10\x10\x10") |
($shuffleip[ $l & 0xFF] &
"\x08\x08\x08\x08\x08\x08\x08\x08") |
($shuffleip[($l >> 8) & 0xFF] &
"\x04\x04\x04\x04\x04\x04\x04\x04") |
($shuffleip[($l >> 16) & 0xFF] &
"\x02\x02\x02\x02\x02\x02\x02\x02") |
($shuffleip[($l >> 24) & 0xFF] &
"\x01\x01\x01\x01\x01\x01\x01\x01");
// Extract L0 and R0.
$t = unpack('Nl/Nr', $block);
list($l, $r) = array($t['l'], $t['r']);
for ($des_round = 0; $des_round < $this->des_rounds;
++$des_round) {
// Perform the 16 steps.
for ($i = 0; $i < 16; $i++) {
// start of "the Feistel (F) function" - see the
following URL:
//
http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png
// Merge key schedule.
$b1 = (($r >> 3) & 0x1FFFFFFF) ^ ($r <<
29) ^ $keys[++$ki];
$b2 = (($r >> 31) & 0x00000001) ^ ($r <<
1) ^ $keys[++$ki];
// S-box indexing.
$t = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2
>> 24) & 0x3F] ^
$sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2
>> 16) & 0x3F] ^
$sbox5[($b1 >> 8) & 0x3F] ^ $sbox6[($b2
>> 8) & 0x3F] ^
$sbox7[ $b1 & 0x3F] ^ $sbox8[ $b2
& 0x3F] ^ $l;
// end of "the Feistel (F) function"
$l = $r;
$r = $t;
}
// Last step should not permute L & R.
$t = $l;
$l = $r;
$r = $t;
}
// Perform the inverse IP permutation.
return ($shuffleinvip[($r >> 24) & 0xFF] &
"\x80\x80\x80\x80\x80\x80\x80\x80") |
($shuffleinvip[($l >> 24) & 0xFF] &
"\x40\x40\x40\x40\x40\x40\x40\x40") |
($shuffleinvip[($r >> 16) & 0xFF] &
"\x20\x20\x20\x20\x20\x20\x20\x20") |
($shuffleinvip[($l >> 16) & 0xFF] &
"\x10\x10\x10\x10\x10\x10\x10\x10") |
($shuffleinvip[($r >> 8) & 0xFF] &
"\x08\x08\x08\x08\x08\x08\x08\x08") |
($shuffleinvip[($l >> 8) & 0xFF] &
"\x04\x04\x04\x04\x04\x04\x04\x04") |
($shuffleinvip[ $r & 0xFF] &
"\x02\x02\x02\x02\x02\x02\x02\x02") |
($shuffleinvip[ $l & 0xFF] &
"\x01\x01\x01\x01\x01\x01\x01\x01");
}
/**
* Creates the key schedule
*
* @see \phpseclib\Crypt\Base::_setupKey()
* @access private
*/
function _setupKey()
{
if (isset($this->kl['key']) && $this->key
=== $this->kl['key'] && $this->des_rounds ===
$this->kl['des_rounds']) {
// already expanded
return;
}
$this->kl = array('key' => $this->key,
'des_rounds' => $this->des_rounds);
static $shifts = array( // number of key bits shifted per round
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
);
static $pc1map = array(
0x00, 0x00, 0x08, 0x08, 0x04, 0x04, 0x0C, 0x0C,
0x02, 0x02, 0x0A, 0x0A, 0x06, 0x06, 0x0E, 0x0E,
0x10, 0x10, 0x18, 0x18, 0x14, 0x14, 0x1C, 0x1C,
0x12, 0x12, 0x1A, 0x1A, 0x16, 0x16, 0x1E, 0x1E,
0x20, 0x20, 0x28, 0x28, 0x24, 0x24, 0x2C, 0x2C,
0x22, 0x22, 0x2A, 0x2A, 0x26, 0x26, 0x2E, 0x2E,
0x30, 0x30, 0x38, 0x38, 0x34, 0x34, 0x3C, 0x3C,
0x32, 0x32, 0x3A, 0x3A, 0x36, 0x36, 0x3E, 0x3E,
0x40, 0x40, 0x48, 0x48, 0x44, 0x44, 0x4C, 0x4C,
0x42, 0x42, 0x4A, 0x4A, 0x46, 0x46, 0x4E, 0x4E,
0x50, 0x50, 0x58, 0x58, 0x54, 0x54, 0x5C, 0x5C,
0x52, 0x52, 0x5A, 0x5A, 0x56, 0x56, 0x5E, 0x5E,
0x60, 0x60, 0x68, 0x68, 0x64, 0x64, 0x6C, 0x6C,
0x62, 0x62, 0x6A, 0x6A, 0x66, 0x66, 0x6E, 0x6E,
0x70, 0x70, 0x78, 0x78, 0x74, 0x74, 0x7C, 0x7C,
0x72, 0x72, 0x7A, 0x7A, 0x76, 0x76, 0x7E, 0x7E,
0x80, 0x80, 0x88, 0x88, 0x84, 0x84, 0x8C, 0x8C,
0x82, 0x82, 0x8A, 0x8A, 0x86, 0x86, 0x8E, 0x8E,
0x90, 0x90, 0x98, 0x98, 0x94, 0x94, 0x9C, 0x9C,
0x92, 0x92, 0x9A, 0x9A, 0x96, 0x96, 0x9E, 0x9E,
0xA0, 0xA0, 0xA8, 0xA8, 0xA4, 0xA4, 0xAC, 0xAC,
0xA2, 0xA2, 0xAA, 0xAA, 0xA6, 0xA6, 0xAE, 0xAE,
0xB0, 0xB0, 0xB8, 0xB8, 0xB4, 0xB4, 0xBC, 0xBC,
0xB2, 0xB2, 0xBA, 0xBA, 0xB6, 0xB6, 0xBE, 0xBE,
0xC0, 0xC0, 0xC8, 0xC8, 0xC4, 0xC4, 0xCC, 0xCC,
0xC2, 0xC2, 0xCA, 0xCA, 0xC6, 0xC6, 0xCE, 0xCE,
0xD0, 0xD0, 0xD8, 0xD8, 0xD4, 0xD4, 0xDC, 0xDC,
0xD2, 0xD2, 0xDA, 0xDA, 0xD6, 0xD6, 0xDE, 0xDE,
0xE0, 0xE0, 0xE8, 0xE8, 0xE4, 0xE4, 0xEC, 0xEC,
0xE2, 0xE2, 0xEA, 0xEA, 0xE6, 0xE6, 0xEE, 0xEE,
0xF0, 0xF0, 0xF8, 0xF8, 0xF4, 0xF4, 0xFC, 0xFC,
0xF2, 0xF2, 0xFA, 0xFA, 0xF6, 0xF6, 0xFE, 0xFE
);
// Mapping tables for the PC-2 transformation.
static $pc2mapc1 = array(
0x00000000, 0x00000400, 0x00200000, 0x00200400,
0x00000001, 0x00000401, 0x00200001, 0x00200401,
0x02000000, 0x02000400, 0x02200000, 0x02200400,
0x02000001, 0x02000401, 0x02200001, 0x02200401
);
static $pc2mapc2 = array(
0x00000000, 0x00000800, 0x08000000, 0x08000800,
0x00010000, 0x00010800, 0x08010000, 0x08010800,
0x00000000, 0x00000800, 0x08000000, 0x08000800,
0x00010000, 0x00010800, 0x08010000, 0x08010800,
0x00000100, 0x00000900, 0x08000100, 0x08000900,
0x00010100, 0x00010900, 0x08010100, 0x08010900,
0x00000100, 0x00000900, 0x08000100, 0x08000900,
0x00010100, 0x00010900, 0x08010100, 0x08010900,
0x00000010, 0x00000810, 0x08000010, 0x08000810,
0x00010010, 0x00010810, 0x08010010, 0x08010810,
0x00000010, 0x00000810, 0x08000010, 0x08000810,
0x00010010, 0x00010810, 0x08010010, 0x08010810,
0x00000110, 0x00000910, 0x08000110, 0x08000910,
0x00010110, 0x00010910, 0x08010110, 0x08010910,
0x00000110, 0x00000910, 0x08000110, 0x08000910,
0x00010110, 0x00010910, 0x08010110, 0x08010910,
0x00040000, 0x00040800, 0x08040000, 0x08040800,
0x00050000, 0x00050800, 0x08050000, 0x08050800,
0x00040000, 0x00040800, 0x08040000, 0x08040800,
0x00050000, 0x00050800, 0x08050000, 0x08050800,
0x00040100, 0x00040900, 0x08040100, 0x08040900,
0x00050100, 0x00050900, 0x08050100, 0x08050900,
0x00040100, 0x00040900, 0x08040100, 0x08040900,
0x00050100, 0x00050900, 0x08050100, 0x08050900,
0x00040010, 0x00040810, 0x08040010, 0x08040810,
0x00050010, 0x00050810, 0x08050010, 0x08050810,
0x00040010, 0x00040810, 0x08040010, 0x08040810,
0x00050010, 0x00050810, 0x08050010, 0x08050810,
0x00040110, 0x00040910, 0x08040110, 0x08040910,
0x00050110, 0x00050910, 0x08050110, 0x08050910,
0x00040110, 0x00040910, 0x08040110, 0x08040910,
0x00050110, 0x00050910, 0x08050110, 0x08050910,
0x01000000, 0x01000800, 0x09000000, 0x09000800,
0x01010000, 0x01010800, 0x09010000, 0x09010800,
0x01000000, 0x01000800, 0x09000000, 0x09000800,
0x01010000, 0x01010800, 0x09010000, 0x09010800,
0x01000100, 0x01000900, 0x09000100, 0x09000900,
0x01010100, 0x01010900, 0x09010100, 0x09010900,
0x01000100, 0x01000900, 0x09000100, 0x09000900,
0x01010100, 0x01010900, 0x09010100, 0x09010900,
0x01000010, 0x01000810, 0x09000010, 0x09000810,
0x01010010, 0x01010810, 0x09010010, 0x09010810,
0x01000010, 0x01000810, 0x09000010, 0x09000810,
0x01010010, 0x01010810, 0x09010010, 0x09010810,
0x01000110, 0x01000910, 0x09000110, 0x09000910,
0x01010110, 0x01010910, 0x09010110, 0x09010910,
0x01000110, 0x01000910, 0x09000110, 0x09000910,
0x01010110, 0x01010910, 0x09010110, 0x09010910,
0x01040000, 0x01040800, 0x09040000, 0x09040800,
0x01050000, 0x01050800, 0x09050000, 0x09050800,
0x01040000, 0x01040800, 0x09040000, 0x09040800,
0x01050000, 0x01050800, 0x09050000, 0x09050800,
0x01040100, 0x01040900, 0x09040100, 0x09040900,
0x01050100, 0x01050900, 0x09050100, 0x09050900,
0x01040100, 0x01040900, 0x09040100, 0x09040900,
0x01050100, 0x01050900, 0x09050100, 0x09050900,
0x01040010, 0x01040810, 0x09040010, 0x09040810,
0x01050010, 0x01050810, 0x09050010, 0x09050810,
0x01040010, 0x01040810, 0x09040010, 0x09040810,
0x01050010, 0x01050810, 0x09050010, 0x09050810,
0x01040110, 0x01040910, 0x09040110, 0x09040910,
0x01050110, 0x01050910, 0x09050110, 0x09050910,
0x01040110, 0x01040910, 0x09040110, 0x09040910,
0x01050110, 0x01050910, 0x09050110, 0x09050910
);
static $pc2mapc3 = array(
0x00000000, 0x00000004, 0x00001000, 0x00001004,
0x00000000, 0x00000004, 0x00001000, 0x00001004,
0x10000000, 0x10000004, 0x10001000, 0x10001004,
0x10000000, 0x10000004, 0x10001000, 0x10001004,
0x00000020, 0x00000024, 0x00001020, 0x00001024,
0x00000020, 0x00000024, 0x00001020, 0x00001024,
0x10000020, 0x10000024, 0x10001020, 0x10001024,
0x10000020, 0x10000024, 0x10001020, 0x10001024,
0x00080000, 0x00080004, 0x00081000, 0x00081004,
0x00080000, 0x00080004, 0x00081000, 0x00081004,
0x10080000, 0x10080004, 0x10081000, 0x10081004,
0x10080000, 0x10080004, 0x10081000, 0x10081004,
0x00080020, 0x00080024, 0x00081020, 0x00081024,
0x00080020, 0x00080024, 0x00081020, 0x00081024,
0x10080020, 0x10080024, 0x10081020, 0x10081024,
0x10080020, 0x10080024, 0x10081020, 0x10081024,
0x20000000, 0x20000004, 0x20001000, 0x20001004,
0x20000000, 0x20000004, 0x20001000, 0x20001004,
0x30000000, 0x30000004, 0x30001000, 0x30001004,
0x30000000, 0x30000004, 0x30001000, 0x30001004,
0x20000020, 0x20000024, 0x20001020, 0x20001024,
0x20000020, 0x20000024, 0x20001020, 0x20001024,
0x30000020, 0x30000024, 0x30001020, 0x30001024,
0x30000020, 0x30000024, 0x30001020, 0x30001024,
0x20080000, 0x20080004, 0x20081000, 0x20081004,
0x20080000, 0x20080004, 0x20081000, 0x20081004,
0x30080000, 0x30080004, 0x30081000, 0x30081004,
0x30080000, 0x30080004, 0x30081000, 0x30081004,
0x20080020, 0x20080024, 0x20081020, 0x20081024,
0x20080020, 0x20080024, 0x20081020, 0x20081024,
0x30080020, 0x30080024, 0x30081020, 0x30081024,
0x30080020, 0x30080024, 0x30081020, 0x30081024,
0x00000002, 0x00000006, 0x00001002, 0x00001006,
0x00000002, 0x00000006, 0x00001002, 0x00001006,
0x10000002, 0x10000006, 0x10001002, 0x10001006,
0x10000002, 0x10000006, 0x10001002, 0x10001006,
0x00000022, 0x00000026, 0x00001022, 0x00001026,
0x00000022, 0x00000026, 0x00001022, 0x00001026,
0x10000022, 0x10000026, 0x10001022, 0x10001026,
0x10000022, 0x10000026, 0x10001022, 0x10001026,
0x00080002, 0x00080006, 0x00081002, 0x00081006,
0x00080002, 0x00080006, 0x00081002, 0x00081006,
0x10080002, 0x10080006, 0x10081002, 0x10081006,
0x10080002, 0x10080006, 0x10081002, 0x10081006,
0x00080022, 0x00080026, 0x00081022, 0x00081026,
0x00080022, 0x00080026, 0x00081022, 0x00081026,
0x10080022, 0x10080026, 0x10081022, 0x10081026,
0x10080022, 0x10080026, 0x10081022, 0x10081026,
0x20000002, 0x20000006, 0x20001002, 0x20001006,
0x20000002, 0x20000006, 0x20001002, 0x20001006,
0x30000002, 0x30000006, 0x30001002, 0x30001006,
0x30000002, 0x30000006, 0x30001002, 0x30001006,
0x20000022, 0x20000026, 0x20001022, 0x20001026,
0x20000022, 0x20000026, 0x20001022, 0x20001026,
0x30000022, 0x30000026, 0x30001022, 0x30001026,
0x30000022, 0x30000026, 0x30001022, 0x30001026,
0x20080002, 0x20080006, 0x20081002, 0x20081006,
0x20080002, 0x20080006, 0x20081002, 0x20081006,
0x30080002, 0x30080006, 0x30081002, 0x30081006,
0x30080002, 0x30080006, 0x30081002, 0x30081006,
0x20080022, 0x20080026, 0x20081022, 0x20081026,
0x20080022, 0x20080026, 0x20081022, 0x20081026,
0x30080022, 0x30080026, 0x30081022, 0x30081026,
0x30080022, 0x30080026, 0x30081022, 0x30081026
);
static $pc2mapc4 = array(
0x00000000, 0x00100000, 0x00000008, 0x00100008,
0x00000200, 0x00100200, 0x00000208, 0x00100208,
0x00000000, 0x00100000, 0x00000008, 0x00100008,
0x00000200, 0x00100200, 0x00000208, 0x00100208,
0x04000000, 0x04100000, 0x04000008, 0x04100008,
0x04000200, 0x04100200, 0x04000208, 0x04100208,
0x04000000, 0x04100000, 0x04000008, 0x04100008,
0x04000200, 0x04100200, 0x04000208, 0x04100208,
0x00002000, 0x00102000, 0x00002008, 0x00102008,
0x00002200, 0x00102200, 0x00002208, 0x00102208,
0x00002000, 0x00102000, 0x00002008, 0x00102008,
0x00002200, 0x00102200, 0x00002208, 0x00102208,
0x04002000, 0x04102000, 0x04002008, 0x04102008,
0x04002200, 0x04102200, 0x04002208, 0x04102208,
0x04002000, 0x04102000, 0x04002008, 0x04102008,
0x04002200, 0x04102200, 0x04002208, 0x04102208,
0x00000000, 0x00100000, 0x00000008, 0x00100008,
0x00000200, 0x00100200, 0x00000208, 0x00100208,
0x00000000, 0x00100000, 0x00000008, 0x00100008,
0x00000200, 0x00100200, 0x00000208, 0x00100208,
0x04000000, 0x04100000, 0x04000008, 0x04100008,
0x04000200, 0x04100200, 0x04000208, 0x04100208,
0x04000000, 0x04100000, 0x04000008, 0x04100008,
0x04000200, 0x04100200, 0x04000208, 0x04100208,
0x00002000, 0x00102000, 0x00002008, 0x00102008,
0x00002200, 0x00102200, 0x00002208, 0x00102208,
0x00002000, 0x00102000, 0x00002008, 0x00102008,
0x00002200, 0x00102200, 0x00002208, 0x00102208,
0x04002000, 0x04102000, 0x04002008, 0x04102008,
0x04002200, 0x04102200, 0x04002208, 0x04102208,
0x04002000, 0x04102000, 0x04002008, 0x04102008,
0x04002200, 0x04102200, 0x04002208, 0x04102208,
0x00020000, 0x00120000, 0x00020008, 0x00120008,
0x00020200, 0x00120200, 0x00020208, 0x00120208,
0x00020000, 0x00120000, 0x00020008, 0x00120008,
0x00020200, 0x00120200, 0x00020208, 0x00120208,
0x04020000, 0x04120000, 0x04020008, 0x04120008,
0x04020200, 0x04120200, 0x04020208, 0x04120208,
0x04020000, 0x04120000, 0x04020008, 0x04120008,
0x04020200, 0x04120200, 0x04020208, 0x04120208,
0x00022000, 0x00122000, 0x00022008, 0x00122008,
0x00022200, 0x00122200, 0x00022208, 0x00122208,
0x00022000, 0x00122000, 0x00022008, 0x00122008,
0x00022200, 0x00122200, 0x00022208, 0x00122208,
0x04022000, 0x04122000, 0x04022008, 0x04122008,
0x04022200, 0x04122200, 0x04022208, 0x04122208,
0x04022000, 0x04122000, 0x04022008, 0x04122008,
0x04022200, 0x04122200, 0x04022208, 0x04122208,
0x00020000, 0x00120000, 0x00020008, 0x00120008,
0x00020200, 0x00120200, 0x00020208, 0x00120208,
0x00020000, 0x00120000, 0x00020008, 0x00120008,
0x00020200, 0x00120200, 0x00020208, 0x00120208,
0x04020000, 0x04120000, 0x04020008, 0x04120008,
0x04020200, 0x04120200, 0x04020208, 0x04120208,
0x04020000, 0x04120000, 0x04020008, 0x04120008,
0x04020200, 0x04120200, 0x04020208, 0x04120208,
0x00022000, 0x00122000, 0x00022008, 0x00122008,
0x00022200, 0x00122200, 0x00022208, 0x00122208,
0x00022000, 0x00122000, 0x00022008, 0x00122008,
0x00022200, 0x00122200, 0x00022208, 0x00122208,
0x04022000, 0x04122000, 0x04022008, 0x04122008,
0x04022200, 0x04122200, 0x04022208, 0x04122208,
0x04022000, 0x04122000, 0x04022008, 0x04122008,
0x04022200, 0x04122200, 0x04022208, 0x04122208
);
static $pc2mapd1 = array(
0x00000000, 0x00000001, 0x08000000, 0x08000001,
0x00200000, 0x00200001, 0x08200000, 0x08200001,
0x00000002, 0x00000003, 0x08000002, 0x08000003,
0x00200002, 0x00200003, 0x08200002, 0x08200003
);
static $pc2mapd2 = array(
0x00000000, 0x00100000, 0x00000800, 0x00100800,
0x00000000, 0x00100000, 0x00000800, 0x00100800,
0x04000000, 0x04100000, 0x04000800, 0x04100800,
0x04000000, 0x04100000, 0x04000800, 0x04100800,
0x00000004, 0x00100004, 0x00000804, 0x00100804,
0x00000004, 0x00100004, 0x00000804, 0x00100804,
0x04000004, 0x04100004, 0x04000804, 0x04100804,
0x04000004, 0x04100004, 0x04000804, 0x04100804,
0x00000000, 0x00100000, 0x00000800, 0x00100800,
0x00000000, 0x00100000, 0x00000800, 0x00100800,
0x04000000, 0x04100000, 0x04000800, 0x04100800,
0x04000000, 0x04100000, 0x04000800, 0x04100800,
0x00000004, 0x00100004, 0x00000804, 0x00100804,
0x00000004, 0x00100004, 0x00000804, 0x00100804,
0x04000004, 0x04100004, 0x04000804, 0x04100804,
0x04000004, 0x04100004, 0x04000804, 0x04100804,
0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
0x00020000, 0x00120000, 0x00020800, 0x00120800,
0x00020000, 0x00120000, 0x00020800, 0x00120800,
0x04020000, 0x04120000, 0x04020800, 0x04120800,
0x04020000, 0x04120000, 0x04020800, 0x04120800,
0x00020004, 0x00120004, 0x00020804, 0x00120804,
0x00020004, 0x00120004, 0x00020804, 0x00120804,
0x04020004, 0x04120004, 0x04020804, 0x04120804,
0x04020004, 0x04120004, 0x04020804, 0x04120804,
0x00020000, 0x00120000, 0x00020800, 0x00120800,
0x00020000, 0x00120000, 0x00020800, 0x00120800,
0x04020000, 0x04120000, 0x04020800, 0x04120800,
0x04020000, 0x04120000, 0x04020800, 0x04120800,
0x00020004, 0x00120004, 0x00020804, 0x00120804,
0x00020004, 0x00120004, 0x00020804, 0x00120804,
0x04020004, 0x04120004, 0x04020804, 0x04120804,
0x04020004, 0x04120004, 0x04020804, 0x04120804,
0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
0x04020204, 0x04120204, 0x04020A04, 0x04120A04,
0x04020204, 0x04120204, 0x04020A04, 0x04120A04,
0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
0x04020204, 0x04120204, 0x04020A04, 0x04120A04,
0x04020204, 0x04120204, 0x04020A04, 0x04120A04
);
static $pc2mapd3 = array(
0x00000000, 0x00010000, 0x02000000, 0x02010000,
0x00000020, 0x00010020, 0x02000020, 0x02010020,
0x00040000, 0x00050000, 0x02040000, 0x02050000,
0x00040020, 0x00050020, 0x02040020, 0x02050020,
0x00002000, 0x00012000, 0x02002000, 0x02012000,
0x00002020, 0x00012020, 0x02002020, 0x02012020,
0x00042000, 0x00052000, 0x02042000, 0x02052000,
0x00042020, 0x00052020, 0x02042020, 0x02052020,
0x00000000, 0x00010000, 0x02000000, 0x02010000,
0x00000020, 0x00010020, 0x02000020, 0x02010020,
0x00040000, 0x00050000, 0x02040000, 0x02050000,
0x00040020, 0x00050020, 0x02040020, 0x02050020,
0x00002000, 0x00012000, 0x02002000, 0x02012000,
0x00002020, 0x00012020, 0x02002020, 0x02012020,
0x00042000, 0x00052000, 0x02042000, 0x02052000,
0x00042020, 0x00052020, 0x02042020, 0x02052020,
0x00000010, 0x00010010, 0x02000010, 0x02010010,
0x00000030, 0x00010030, 0x02000030, 0x02010030,
0x00040010, 0x00050010, 0x02040010, 0x02050010,
0x00040030, 0x00050030, 0x02040030, 0x02050030,
0x00002010, 0x00012010, 0x02002010, 0x02012010,
0x00002030, 0x00012030, 0x02002030, 0x02012030,
0x00042010, 0x00052010, 0x02042010, 0x02052010,
0x00042030, 0x00052030, 0x02042030, 0x02052030,
0x00000010, 0x00010010, 0x02000010, 0x02010010,
0x00000030, 0x00010030, 0x02000030, 0x02010030,
0x00040010, 0x00050010, 0x02040010, 0x02050010,
0x00040030, 0x00050030, 0x02040030, 0x02050030,
0x00002010, 0x00012010, 0x02002010, 0x02012010,
0x00002030, 0x00012030, 0x02002030, 0x02012030,
0x00042010, 0x00052010, 0x02042010, 0x02052010,
0x00042030, 0x00052030, 0x02042030, 0x02052030,
0x20000000, 0x20010000, 0x22000000, 0x22010000,
0x20000020, 0x20010020, 0x22000020, 0x22010020,
0x20040000, 0x20050000, 0x22040000, 0x22050000,
0x20040020, 0x20050020, 0x22040020, 0x22050020,
0x20002000, 0x20012000, 0x22002000, 0x22012000,
0x20002020, 0x20012020, 0x22002020, 0x22012020,
0x20042000, 0x20052000, 0x22042000, 0x22052000,
0x20042020, 0x20052020, 0x22042020, 0x22052020,
0x20000000, 0x20010000, 0x22000000, 0x22010000,
0x20000020, 0x20010020, 0x22000020, 0x22010020,
0x20040000, 0x20050000, 0x22040000, 0x22050000,
0x20040020, 0x20050020, 0x22040020, 0x22050020,
0x20002000, 0x20012000, 0x22002000, 0x22012000,
0x20002020, 0x20012020, 0x22002020, 0x22012020,
0x20042000, 0x20052000, 0x22042000, 0x22052000,
0x20042020, 0x20052020, 0x22042020, 0x22052020,
0x20000010, 0x20010010, 0x22000010, 0x22010010,
0x20000030, 0x20010030, 0x22000030, 0x22010030,
0x20040010, 0x20050010, 0x22040010, 0x22050010,
0x20040030, 0x20050030, 0x22040030, 0x22050030,
0x20002010, 0x20012010, 0x22002010, 0x22012010,
0x20002030, 0x20012030, 0x22002030, 0x22012030,
0x20042010, 0x20052010, 0x22042010, 0x22052010,
0x20042030, 0x20052030, 0x22042030, 0x22052030,
0x20000010, 0x20010010, 0x22000010, 0x22010010,
0x20000030, 0x20010030, 0x22000030, 0x22010030,
0x20040010, 0x20050010, 0x22040010, 0x22050010,
0x20040030, 0x20050030, 0x22040030, 0x22050030,
0x20002010, 0x20012010, 0x22002010, 0x22012010,
0x20002030, 0x20012030, 0x22002030, 0x22012030,
0x20042010, 0x20052010, 0x22042010, 0x22052010,
0x20042030, 0x20052030, 0x22042030, 0x22052030
);
static $pc2mapd4 = array(
0x00000000, 0x00000400, 0x01000000, 0x01000400,
0x00000000, 0x00000400, 0x01000000, 0x01000400,
0x00000100, 0x00000500, 0x01000100, 0x01000500,
0x00000100, 0x00000500, 0x01000100, 0x01000500,
0x10000000, 0x10000400, 0x11000000, 0x11000400,
0x10000000, 0x10000400, 0x11000000, 0x11000400,
0x10000100, 0x10000500, 0x11000100, 0x11000500,
0x10000100, 0x10000500, 0x11000100, 0x11000500,
0x00080000, 0x00080400, 0x01080000, 0x01080400,
0x00080000, 0x00080400, 0x01080000, 0x01080400,
0x00080100, 0x00080500, 0x01080100, 0x01080500,
0x00080100, 0x00080500, 0x01080100, 0x01080500,
0x10080000, 0x10080400, 0x11080000, 0x11080400,
0x10080000, 0x10080400, 0x11080000, 0x11080400,
0x10080100, 0x10080500, 0x11080100, 0x11080500,
0x10080100, 0x10080500, 0x11080100, 0x11080500,
0x00000008, 0x00000408, 0x01000008, 0x01000408,
0x00000008, 0x00000408, 0x01000008, 0x01000408,
0x00000108, 0x00000508, 0x01000108, 0x01000508,
0x00000108, 0x00000508, 0x01000108, 0x01000508,
0x10000008, 0x10000408, 0x11000008, 0x11000408,
0x10000008, 0x10000408, 0x11000008, 0x11000408,
0x10000108, 0x10000508, 0x11000108, 0x11000508,
0x10000108, 0x10000508, 0x11000108, 0x11000508,
0x00080008, 0x00080408, 0x01080008, 0x01080408,
0x00080008, 0x00080408, 0x01080008, 0x01080408,
0x00080108, 0x00080508, 0x01080108, 0x01080508,
0x00080108, 0x00080508, 0x01080108, 0x01080508,
0x10080008, 0x10080408, 0x11080008, 0x11080408,
0x10080008, 0x10080408, 0x11080008, 0x11080408,
0x10080108, 0x10080508, 0x11080108, 0x11080508,
0x10080108, 0x10080508, 0x11080108, 0x11080508,
0x00001000, 0x00001400, 0x01001000, 0x01001400,
0x00001000, 0x00001400, 0x01001000, 0x01001400,
0x00001100, 0x00001500, 0x01001100, 0x01001500,
0x00001100, 0x00001500, 0x01001100, 0x01001500,
0x10001000, 0x10001400, 0x11001000, 0x11001400,
0x10001000, 0x10001400, 0x11001000, 0x11001400,
0x10001100, 0x10001500, 0x11001100, 0x11001500,
0x10001100, 0x10001500, 0x11001100, 0x11001500,
0x00081000, 0x00081400, 0x01081000, 0x01081400,
0x00081000, 0x00081400, 0x01081000, 0x01081400,
0x00081100, 0x00081500, 0x01081100, 0x01081500,
0x00081100, 0x00081500, 0x01081100, 0x01081500,
0x10081000, 0x10081400, 0x11081000, 0x11081400,
0x10081000, 0x10081400, 0x11081000, 0x11081400,
0x10081100, 0x10081500, 0x11081100, 0x11081500,
0x10081100, 0x10081500, 0x11081100, 0x11081500,
0x00001008, 0x00001408, 0x01001008, 0x01001408,
0x00001008, 0x00001408, 0x01001008, 0x01001408,
0x00001108, 0x00001508, 0x01001108, 0x01001508,
0x00001108, 0x00001508, 0x01001108, 0x01001508,
0x10001008, 0x10001408, 0x11001008, 0x11001408,
0x10001008, 0x10001408, 0x11001008, 0x11001408,
0x10001108, 0x10001508, 0x11001108, 0x11001508,
0x10001108, 0x10001508, 0x11001108, 0x11001508,
0x00081008, 0x00081408, 0x01081008, 0x01081408,
0x00081008, 0x00081408, 0x01081008, 0x01081408,
0x00081108, 0x00081508, 0x01081108, 0x01081508,
0x00081108, 0x00081508, 0x01081108, 0x01081508,
0x10081008, 0x10081408, 0x11081008, 0x11081408,
0x10081008, 0x10081408, 0x11081008, 0x11081408,
0x10081108, 0x10081508, 0x11081108, 0x11081508,
0x10081108, 0x10081508, 0x11081108, 0x11081508
);
$keys = array();
for ($des_round = 0; $des_round < $this->des_rounds;
++$des_round) {
// pad the key and remove extra characters as appropriate.
$key = str_pad(substr($this->key, $des_round * 8, 8), 8,
"\0");
// Perform the PC/1 transformation and compute C and D.
$t = unpack('Nl/Nr', $key);
list($l, $r) = array($t['l'], $t['r']);
$key = ($this->shuffle[$pc1map[ $r & 0xFF]] &
"\x80\x80\x80\x80\x80\x80\x80\x00") |
($this->shuffle[$pc1map[($r >> 8) & 0xFF]]
& "\x40\x40\x40\x40\x40\x40\x40\x00") |
($this->shuffle[$pc1map[($r >> 16) & 0xFF]]
& "\x20\x20\x20\x20\x20\x20\x20\x00") |
($this->shuffle[$pc1map[($r >> 24) & 0xFF]]
& "\x10\x10\x10\x10\x10\x10\x10\x00") |
($this->shuffle[$pc1map[ $l & 0xFF]] &
"\x08\x08\x08\x08\x08\x08\x08\x00") |
($this->shuffle[$pc1map[($l >> 8) & 0xFF]]
& "\x04\x04\x04\x04\x04\x04\x04\x00") |
($this->shuffle[$pc1map[($l >> 16) & 0xFF]]
& "\x02\x02\x02\x02\x02\x02\x02\x00") |
($this->shuffle[$pc1map[($l >> 24) & 0xFF]]
& "\x01\x01\x01\x01\x01\x01\x01\x00");
$key = unpack('Nc/Nd', $key);
$c = ( $key['c'] >> 4) & 0x0FFFFFFF;
$d = (($key['d'] >> 4) & 0x0FFFFFF0) |
($key['c'] & 0x0F);
$keys[$des_round] = array(
self::ENCRYPT => array(),
self::DECRYPT => array_fill(0, 32, 0)
);
for ($i = 0, $ki = 31; $i < 16; ++$i, $ki-= 2) {
$c <<= $shifts[$i];
$c = ($c | ($c >> 28)) & 0x0FFFFFFF;
$d <<= $shifts[$i];
$d = ($d | ($d >> 28)) & 0x0FFFFFFF;
// Perform the PC-2 transformation.
$cp = $pc2mapc1[ $c >> 24 ] | $pc2mapc2[($c
>> 16) & 0xFF] |
$pc2mapc3[($c >> 8) & 0xFF] | $pc2mapc4[
$c & 0xFF];
$dp = $pc2mapd1[ $d >> 24 ] | $pc2mapd2[($d
>> 16) & 0xFF] |
$pc2mapd3[($d >> 8) & 0xFF] | $pc2mapd4[
$d & 0xFF];
// Reorder: odd bytes/even bytes. Push the result in key
schedule.
$val1 = ( $cp & 0xFF000000) | (($cp << 8)
& 0x00FF0000) |
(($dp >> 16) & 0x0000FF00) | (($dp
>> 8) & 0x000000FF);
$val2 = (($cp << 8) & 0xFF000000) | (($cp
<< 16) & 0x00FF0000) |
(($dp >> 8) & 0x0000FF00) | ( $dp
& 0x000000FF);
$keys[$des_round][self::ENCRYPT][ ] = $val1;
$keys[$des_round][self::DECRYPT][$ki - 1] = $val1;
$keys[$des_round][self::ENCRYPT][ ] = $val2;
$keys[$des_round][self::DECRYPT][$ki ] = $val2;
}
}
switch ($this->des_rounds) {
case 3: // 3DES keys
$this->keys = array(
self::ENCRYPT => array_merge(
$keys[0][self::ENCRYPT],
$keys[1][self::DECRYPT],
$keys[2][self::ENCRYPT]
),
self::DECRYPT => array_merge(
$keys[2][self::DECRYPT],
$keys[1][self::ENCRYPT],
$keys[0][self::DECRYPT]
)
);
break;
// case 1: // DES keys
default:
$this->keys = array(
self::ENCRYPT => $keys[0][self::ENCRYPT],
self::DECRYPT => $keys[0][self::DECRYPT]
);
}
}
/**
* Setup the performance-optimized function for de/encrypt()
*
* @see \phpseclib\Crypt\Base::_setupInlineCrypt()
* @access private
*/
function _setupInlineCrypt()
{
$lambda_functions =& self::_getLambdaFunctions();
// Engine configuration for:
// - DES ($des_rounds == 1) or
// - 3DES ($des_rounds == 3)
$des_rounds = $this->des_rounds;
// We create max. 10 hi-optimized code for memory reason. Means:
For each $key one ultra fast inline-crypt function.
// (Currently, for DES, one generated $lambda_function cost on
php5.5@32bit ~135kb unfreeable mem and ~230kb on php5.5@64bit)
// (Currently, for TripleDES, one generated $lambda_function cost
on php5.5@32bit ~240kb unfreeable mem and ~340kb on php5.5@64bit)
// After that, we'll still create very fast optimized code but
not the hi-ultimative code, for each $mode one
$gen_hi_opt_code = (bool)( count($lambda_functions) < 10 );
// Generation of a unique hash for our generated code
$code_hash = "Crypt_DES, $des_rounds, {$this->mode}";
if ($gen_hi_opt_code) {
// For hi-optimized code, we create for each combination of
// $mode, $des_rounds and $this->key its own encrypt/decrypt
function.
// After max 10 hi-optimized functions, we create generic
// (still very fast.. but not ultra) functions for each
$mode/$des_rounds
// Currently 2 * 5 generic functions will be then max.
possible.
$code_hash = str_pad($code_hash, 32) .
$this->_hashInlineCryptFunction($this->key);
}
// 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 = 'static $sbox1, $sbox2, $sbox3, $sbox4,
$sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip;
if (!$sbox1) {
$sbox1 = array_map("intval",
$self->sbox1);
$sbox2 = array_map("intval",
$self->sbox2);
$sbox3 = array_map("intval",
$self->sbox3);
$sbox4 = array_map("intval",
$self->sbox4);
$sbox5 = array_map("intval",
$self->sbox5);
$sbox6 = array_map("intval",
$self->sbox6);
$sbox7 = array_map("intval",
$self->sbox7);
$sbox8 = array_map("intval",
$self->sbox8);'
/* Merge $shuffle with $[inv]ipmap */ . '
for ($i = 0; $i < 256; ++$i) {
$shuffleip[] =
$self->shuffle[$self->ipmap[$i]];
$shuffleinvip[] =
$self->shuffle[$self->invipmap[$i]];
}
}
';
switch (true) {
case $gen_hi_opt_code:
// In Hi-optimized code mode, we use our [3]DES key
schedule as hardcoded integers.
// No futher initialisation of the $keys schedule is
necessary.
// That is the extra performance boost.
$k = array(
self::ENCRYPT => $this->keys[self::ENCRYPT],
self::DECRYPT => $this->keys[self::DECRYPT]
);
$init_encrypt = '';
$init_decrypt = '';
break;
default:
// In generic optimized code mode, we have to use, as
the best compromise [currently],
// our key schedule as $ke/$kd arrays. (with hardcoded
indexes...)
$k = array(
self::ENCRYPT => array(),
self::DECRYPT => array()
);
for ($i = 0, $c = count($this->keys[self::ENCRYPT]);
$i < $c; ++$i) {
$k[self::ENCRYPT][$i] = '$ke[' . $i .
']';
$k[self::DECRYPT][$i] = '$kd[' . $i .
']';
}
$init_encrypt = '$ke =
$self->keys[$self::ENCRYPT];';
$init_decrypt = '$kd =
$self->keys[$self::DECRYPT];';
break;
}
// Creating code for en- and decryption.
$crypt_block = array();
foreach (array(self::ENCRYPT, self::DECRYPT) as $c) {
/* Do the initial IP permutation. */
$crypt_block[$c] = '
$in = unpack("N*", $in);
$l = $in[1];
$r = $in[2];
$in = unpack("N*",
($shuffleip[ $r & 0xFF] &
"\x80\x80\x80\x80\x80\x80\x80\x80") |
($shuffleip[($r >> 8) & 0xFF] &
"\x40\x40\x40\x40\x40\x40\x40\x40") |
($shuffleip[($r >> 16) & 0xFF] &
"\x20\x20\x20\x20\x20\x20\x20\x20") |
($shuffleip[($r >> 24) & 0xFF] &
"\x10\x10\x10\x10\x10\x10\x10\x10") |
($shuffleip[ $l & 0xFF] &
"\x08\x08\x08\x08\x08\x08\x08\x08") |
($shuffleip[($l >> 8) & 0xFF] &
"\x04\x04\x04\x04\x04\x04\x04\x04") |
($shuffleip[($l >> 16) & 0xFF] &
"\x02\x02\x02\x02\x02\x02\x02\x02") |
($shuffleip[($l >> 24) & 0xFF] &
"\x01\x01\x01\x01\x01\x01\x01\x01")
);
' . /* Extract L0 and R0 */ '
$l = $in[1];
$r = $in[2];
';
$l = '$l';
$r = '$r';
// Perform DES or 3DES.
for ($ki = -1, $des_round = 0; $des_round < $des_rounds;
++$des_round) {
// Perform the 16 steps.
for ($i = 0; $i < 16; ++$i) {
// start of "the Feistel (F) function" -
see the following URL:
//
http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png
// Merge key schedule.
$crypt_block[$c].= '
$b1 = ((' . $r . ' >> 3) &
0x1FFFFFFF) ^ (' . $r . ' << 29) ^ ' . $k[$c][++$ki]
. ';
$b2 = ((' . $r . ' >> 31) &
0x00000001) ^ (' . $r . ' << 1) ^ ' . $k[$c][++$ki]
. ';' .
/* S-box indexing. */
$l . ' = $sbox1[($b1 >> 24) &
0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^
$sbox3[($b1 >> 16) & 0x3F] ^
$sbox4[($b2 >> 16) & 0x3F] ^
$sbox5[($b1 >> 8) & 0x3F] ^
$sbox6[($b2 >> 8) & 0x3F] ^
$sbox7[ $b1 & 0x3F] ^
$sbox8[ $b2 & 0x3F] ^ ' . $l . ';
';
// end of "the Feistel (F) function"
// swap L & R
list($l, $r) = array($r, $l);
}
list($l, $r) = array($r, $l);
}
// Perform the inverse IP permutation.
$crypt_block[$c].= '$in =
($shuffleinvip[($l >> 24) & 0xFF] &
"\x80\x80\x80\x80\x80\x80\x80\x80") |
($shuffleinvip[($r >> 24) & 0xFF] &
"\x40\x40\x40\x40\x40\x40\x40\x40") |
($shuffleinvip[($l >> 16) & 0xFF] &
"\x20\x20\x20\x20\x20\x20\x20\x20") |
($shuffleinvip[($r >> 16) & 0xFF] &
"\x10\x10\x10\x10\x10\x10\x10\x10") |
($shuffleinvip[($l >> 8) & 0xFF] &
"\x08\x08\x08\x08\x08\x08\x08\x08") |
($shuffleinvip[($r >> 8) & 0xFF] &
"\x04\x04\x04\x04\x04\x04\x04\x04") |
($shuffleinvip[ $l & 0xFF] &
"\x02\x02\x02\x02\x02\x02\x02\x02") |
($shuffleinvip[ $r & 0xFF] &
"\x01\x01\x01\x01\x01\x01\x01\x01");
';
}
// Creates the inline-crypt function
$lambda_functions[$code_hash] =
$this->_createInlineCryptFunction(
array(
'init_crypt' => $init_crypt,
'init_encrypt' => $init_encrypt,
'init_decrypt' => $init_decrypt,
'encrypt_block' =>
$crypt_block[self::ENCRYPT],
'decrypt_block' =>
$crypt_block[self::DECRYPT]
)
);
}
// Set the inline-crypt function as callback in:
$this->inline_crypt
$this->inline_crypt = $lambda_functions[$code_hash];
}
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php000064400000071171151156520630016444
0ustar00<?php
/**
* 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:
*
* md2, md5, md5-96, sha1, sha1-96, sha256, sha256-96, sha384, and sha512,
sha512-96
*
* If {@link self::setKey() setKey()} is called, {@link self::hash()
hash()} will return the HMAC as opposed to
* the hash. If no valid algorithm is provided, sha1 will be used.
*
* PHP version 5
*
* {@internal The variable names are the same as those in
* {@link http://tools.ietf.org/html/rfc2104#section-2 RFC2104}.}}
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $hash = new \phpseclib\Crypt\Hash('sha1');
*
* $hash->setKey('abcdefg');
*
* echo base64_encode($hash->hash('abcdefg'));
* ?>
* </code>
*
* @category Crypt
* @package Hash
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
use phpseclib\Math\BigInteger;
/**
* Pure-PHP implementations of keyed-hash message authentication codes
(HMACs) and various cryptographic hashing functions.
*
* @package Hash
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class Hash
{
/**#@+
* @access private
* @see \phpseclib\Crypt\Hash::__construct()
*/
/**
* Toggles the internal implementation
*/
const MODE_INTERNAL = 1;
/**
* Toggles the mhash() implementation, which has been deprecated on PHP
5.3.0+.
*/
const MODE_MHASH = 2;
/**
* Toggles the hash() implementation, which works on PHP 5.1.2+.
*/
const MODE_HASH = 3;
/**#@-*/
/**
* Hash Parameter
*
* @see self::setHash()
* @var int
* @access private
*/
var $hashParam;
/**
* Byte-length of compression blocks / key (Internal HMAC)
*
* @see self::setAlgorithm()
* @var int
* @access private
*/
var $b;
/**
* Byte-length of hash output (Internal HMAC)
*
* @see self::setHash()
* @var int
* @access private
*/
var $l = false;
/**
* Hash Algorithm
*
* @see self::setHash()
* @var string
* @access private
*/
var $hash;
/**
* Key
*
* @see self::setKey()
* @var string
* @access private
*/
var $key = false;
/**
* Computed Key
*
* @see self::_computeKey()
* @var string
* @access private
*/
var $computedKey = false;
/**
* Outer XOR (Internal HMAC)
*
* @see self::setKey()
* @var string
* @access private
*/
var $opad;
/**
* Inner XOR (Internal HMAC)
*
* @see self::setKey()
* @var string
* @access private
*/
var $ipad;
/**
* Engine
*
* @see self::setHash()
* @var string
* @access private
*/
var $engine;
/**
* Default Constructor.
*
* @param string $hash
* @return \phpseclib\Crypt\Hash
* @access public
*/
function __construct($hash = 'sha1')
{
if (!defined('CRYPT_HASH_MODE')) {
switch (true) {
case extension_loaded('hash'):
define('CRYPT_HASH_MODE', self::MODE_HASH);
break;
case extension_loaded('mhash'):
define('CRYPT_HASH_MODE', self::MODE_MHASH);
break;
default:
define('CRYPT_HASH_MODE',
self::MODE_INTERNAL);
}
}
$this->setHash($hash);
}
/**
* Sets the key for HMACs
*
* Keys can be of any length.
*
* @access public
* @param string $key
*/
function setKey($key = false)
{
$this->key = $key;
$this->_computeKey();
}
/**
* Pre-compute the key used by the HMAC
*
* Quoting http://tools.ietf.org/html/rfc2104#section-2,
"Applications that use keys longer than B bytes
* will first hash the key using H and then use the resultant L byte
string as the actual key to HMAC."
*
* As documented in
https://www.reddit.com/r/PHP/comments/9nct2l/symfonypolyfill_hash_pbkdf2_correct_fix_for/
* when doing an HMAC multiple times it's faster to compute the
hash once instead of computing it during
* every call
*
* @access private
*/
function _computeKey()
{
if ($this->key === false) {
$this->computedKey = false;
return;
}
if (strlen($this->key) <= $this->b) {
$this->computedKey = $this->key;
return;
}
switch ($this->engine) {
case self::MODE_MHASH:
$this->computedKey = mhash($this->hash,
$this->key);
break;
case self::MODE_HASH:
$this->computedKey = hash($this->hash, $this->key,
true);
break;
case self::MODE_INTERNAL:
$this->computedKey = call_user_func($this->hash,
$this->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.
*
* @access public
* @param string $hash
*/
function setHash($hash)
{
$this->hashParam = $hash = strtolower($hash);
switch ($hash) {
case 'md5-96':
case 'sha1-96':
case 'sha256-96':
case 'sha512-96':
$hash = substr($hash, 0, -3);
$this->l = 12; // 96 / 8 = 12
break;
case 'md2':
case 'md5':
$this->l = 16;
break;
case 'sha1':
$this->l = 20;
break;
case 'sha256':
$this->l = 32;
break;
case 'sha384':
$this->l = 48;
break;
case 'sha512':
$this->l = 64;
}
switch ($hash) {
case 'md2-96':
case 'md2':
$this->b = 16;
case 'md5-96':
case 'sha1-96':
case 'sha224-96':
case 'sha256-96':
case 'md2':
case 'md5':
case 'sha1':
case 'sha224':
case 'sha256':
$this->b = 64;
break;
default:
$this->b = 128;
}
switch ($hash) {
case 'md2':
$this->engine = CRYPT_HASH_MODE == self::MODE_HASH
&& in_array('md2', hash_algos()) ?
self::MODE_HASH : self::MODE_INTERNAL;
break;
case 'sha384':
case 'sha512':
$this->engine = CRYPT_HASH_MODE == self::MODE_MHASH ?
self::MODE_INTERNAL : CRYPT_HASH_MODE;
break;
default:
$this->engine = CRYPT_HASH_MODE;
}
switch ($this->engine) {
case self::MODE_MHASH:
switch ($hash) {
case 'md5':
$this->hash = MHASH_MD5;
break;
case 'sha256':
$this->hash = MHASH_SHA256;
break;
case 'sha1':
default:
$this->hash = MHASH_SHA1;
}
$this->_computeKey(self::MODE_MHASH);
return;
case self::MODE_HASH:
switch ($hash) {
case 'md5':
$this->hash = 'md5';
return;
case 'md2':
case 'sha256':
case 'sha384':
case 'sha512':
$this->hash = $hash;
return;
case 'sha1':
default:
$this->hash = 'sha1';
}
$this->_computeKey(self::MODE_HASH);
return;
}
switch ($hash) {
case 'md2':
$this->hash = array($this, '_md2');
break;
case 'md5':
$this->hash = array($this, '_md5');
break;
case 'sha256':
$this->hash = array($this, '_sha256');
break;
case 'sha384':
case 'sha512':
$this->hash = array($this, '_sha512');
break;
case 'sha1':
default:
$this->hash = array($this, '_sha1');
}
$this->ipad = str_repeat(chr(0x36), $this->b);
$this->opad = str_repeat(chr(0x5C), $this->b);
$this->_computeKey(self::MODE_INTERNAL);
}
/**
* Compute the HMAC.
*
* @access public
* @param string $text
* @return string
*/
function hash($text)
{
if (!empty($this->key) || is_string($this->key)) {
switch ($this->engine) {
case self::MODE_MHASH:
$output = mhash($this->hash, $text,
$this->computedKey);
break;
case self::MODE_HASH:
$output = hash_hmac($this->hash, $text,
$this->computedKey, true);
break;
case self::MODE_INTERNAL:
$key = str_pad($this->computedKey, $this->b,
chr(0)); // step 1
$temp = $this->ipad ^ $key;
// step 2
$temp .= $text;
// step 3
$temp = call_user_func($this->hash, $temp);
// step 4
$output = $this->opad ^ $key;
// step 5
$output.= $temp;
// step 6
$output = call_user_func($this->hash, $output);
// step 7
}
} else {
switch ($this->engine) {
case self::MODE_MHASH:
$output = mhash($this->hash, $text);
break;
case self::MODE_HASH:
$output = hash($this->hash, $text, true);
break;
case self::MODE_INTERNAL:
$output = call_user_func($this->hash, $text);
}
}
return substr($output, 0, $this->l);
}
/**
* Returns the hash length (in bytes)
*
* @access public
* @return int
*/
function getLength()
{
return $this->l;
}
/**
* Wrapper for MD5
*
* @access private
* @param string $m
*/
function _md5($m)
{
return pack('H*', md5($m));
}
/**
* Wrapper for SHA1
*
* @access private
* @param string $m
*/
function _sha1($m)
{
return pack('H*', sha1($m));
}
/**
* Pure-PHP implementation of MD2
*
* See {@link http://tools.ietf.org/html/rfc1319 RFC1319}.
*
* @access private
* @param string $m
*/
function _md2($m)
{
static $s = array(
41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161,
236, 240, 6,
19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43,
217, 188,
76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103,
66, 111, 24,
138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73,
160, 251,
245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178,
7, 63,
148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154,
90, 144, 50,
39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48,
179, 72, 165,
181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184,
56, 210,
150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241,
69, 157,
112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45,
168, 2, 27,
96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52,
64, 126, 15,
85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206,
186, 197,
234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223,
205, 244, 65,
129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36,
225, 123,
8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232,
109, 233,
203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102,
88, 208, 228,
166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180,
143, 237,
31, 26, 219, 153, 141, 51, 159, 17, 131, 20
);
// Step 1. Append Padding Bytes
$pad = 16 - (strlen($m) & 0xF);
$m.= str_repeat(chr($pad), $pad);
$length = strlen($m);
// Step 2. Append Checksum
$c = str_repeat(chr(0), 16);
$l = chr(0);
for ($i = 0; $i < $length; $i+= 16) {
for ($j = 0; $j < 16; $j++) {
// RFC1319 incorrectly states that C[j] should be set to
S[c xor L]
//$c[$j] = chr($s[ord($m[$i + $j] ^ $l)]);
// per
<http://www.rfc-editor.org/errata_search.php?rfc=1319>, however, C[j]
should be set to S[c xor L] xor C[j]
$c[$j] = chr($s[ord($m[$i + $j] ^ $l)] ^ ord($c[$j]));
$l = $c[$j];
}
}
$m.= $c;
$length+= 16;
// Step 3. Initialize MD Buffer
$x = str_repeat(chr(0), 48);
// Step 4. Process Message in 16-Byte Blocks
for ($i = 0; $i < $length; $i+= 16) {
for ($j = 0; $j < 16; $j++) {
$x[$j + 16] = $m[$i + $j];
$x[$j + 32] = $x[$j + 16] ^ $x[$j];
}
$t = chr(0);
for ($j = 0; $j < 18; $j++) {
for ($k = 0; $k < 48; $k++) {
$x[$k] = $t = $x[$k] ^ chr($s[ord($t)]);
//$t = $x[$k] = $x[$k] ^ chr($s[ord($t)]);
}
$t = chr(ord($t) + $j);
}
}
// Step 5. Output
return substr($x, 0, 16);
}
/**
* Pure-PHP implementation of SHA256
*
* See {@link
http://en.wikipedia.org/wiki/SHA_hash_functions#SHA-256_.28a_SHA-2_variant.29_pseudocode
SHA-256 (a SHA-2 variant) pseudocode - Wikipedia}.
*
* @access private
* @param string $m
*/
function _sha256($m)
{
if (extension_loaded('suhosin')) {
return pack('H*', sha256($m));
}
// Initialize variables
$hash = array(
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f,
0x9b05688c, 0x1f83d9ab, 0x5be0cd19
);
// Initialize table of round constants
// (first 32 bits of the fractional parts of the cube roots of the
first 64 primes 2..311)
static $k = array(
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74,
0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f,
0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3,
0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354,
0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3,
0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa,
0xa4506ceb, 0xbef9a3f7, 0xc67178f2
);
// Pre-processing
$length = strlen($m);
// to round to nearest 56 mod 64, we'll add 64 - (length + (64
- 56)) % 64
$m.= str_repeat(chr(0), 64 - (($length + 8) & 0x3F));
$m[$length] = chr(0x80);
// we don't support hashing strings 512MB long
$m.= pack('N2', 0, $length << 3);
// Process the message in successive 512-bit chunks
$chunks = str_split($m, 64);
foreach ($chunks as $chunk) {
$w = array();
for ($i = 0; $i < 16; $i++) {
extract(unpack('Ntemp',
$this->_string_shift($chunk, 4)));
$w[] = $temp;
}
// Extend the sixteen 32-bit words into sixty-four 32-bit words
for ($i = 16; $i < 64; $i++) {
// @codingStandardsIgnoreStart
$s0 = $this->_rightRotate($w[$i - 15], 7) ^
$this->_rightRotate($w[$i - 15], 18) ^
$this->_rightShift( $w[$i - 15], 3);
$s1 = $this->_rightRotate($w[$i - 2], 17) ^
$this->_rightRotate($w[$i - 2], 19) ^
$this->_rightShift( $w[$i - 2], 10);
// @codingStandardsIgnoreEnd
$w[$i] = $this->_add($w[$i - 16], $s0, $w[$i - 7], $s1);
}
// Initialize hash value for this chunk
list($a, $b, $c, $d, $e, $f, $g, $h) = $hash;
// Main loop
for ($i = 0; $i < 64; $i++) {
$s0 = $this->_rightRotate($a, 2) ^
$this->_rightRotate($a, 13) ^
$this->_rightRotate($a, 22);
$maj = ($a & $b) ^
($a & $c) ^
($b & $c);
$t2 = $this->_add($s0, $maj);
$s1 = $this->_rightRotate($e, 6) ^
$this->_rightRotate($e, 11) ^
$this->_rightRotate($e, 25);
$ch = ($e & $f) ^
($this->_not($e) & $g);
$t1 = $this->_add($h, $s1, $ch, $k[$i], $w[$i]);
$h = $g;
$g = $f;
$f = $e;
$e = $this->_add($d, $t1);
$d = $c;
$c = $b;
$b = $a;
$a = $this->_add($t1, $t2);
}
// Add this chunk's hash to result so far
$hash = array(
$this->_add($hash[0], $a),
$this->_add($hash[1], $b),
$this->_add($hash[2], $c),
$this->_add($hash[3], $d),
$this->_add($hash[4], $e),
$this->_add($hash[5], $f),
$this->_add($hash[6], $g),
$this->_add($hash[7], $h)
);
}
// Produce the final hash value (big-endian)
return pack('N8', $hash[0], $hash[1], $hash[2], $hash[3],
$hash[4], $hash[5], $hash[6], $hash[7]);
}
/**
* Pure-PHP implementation of SHA384 and SHA512
*
* @access private
* @param string $m
*/
function _sha512($m)
{
static $init384, $init512, $k;
if (!isset($k)) {
// Initialize variables
$init384 = array( // initial values for SHA384
'cbbb9d5dc1059ed8', '629a292a367cd507',
'9159015a3070dd17', '152fecd8f70e5939',
'67332667ffc00b31', '8eb44a8768581511',
'db0c2e0d64f98fa7', '47b5481dbefa4fa4'
);
$init512 = array( // initial values for SHA512
'6a09e667f3bcc908', 'bb67ae8584caa73b',
'3c6ef372fe94f82b', 'a54ff53a5f1d36f1',
'510e527fade682d1', '9b05688c2b3e6c1f',
'1f83d9abfb41bd6b', '5be0cd19137e2179'
);
for ($i = 0; $i < 8; $i++) {
$init384[$i] = new BigInteger($init384[$i], 16);
$init384[$i]->setPrecision(64);
$init512[$i] = new BigInteger($init512[$i], 16);
$init512[$i]->setPrecision(64);
}
// Initialize table of round constants
// (first 64 bits of the fractional parts of the cube roots of
the first 80 primes 2..409)
$k = array(
'428a2f98d728ae22', '7137449123ef65cd',
'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc',
'3956c25bf348b538', '59f111f1b605d019',
'923f82a4af194f9b', 'ab1c5ed5da6d8118',
'd807aa98a3030242', '12835b0145706fbe',
'243185be4ee4b28c', '550c7dc3d5ffb4e2',
'72be5d74f27b896f', '80deb1fe3b1696b1',
'9bdc06a725c71235', 'c19bf174cf692694',
'e49b69c19ef14ad2', 'efbe4786384f25e3',
'0fc19dc68b8cd5b5', '240ca1cc77ac9c65',
'2de92c6f592b0275', '4a7484aa6ea6e483',
'5cb0a9dcbd41fbd4', '76f988da831153b5',
'983e5152ee66dfab', 'a831c66d2db43210',
'b00327c898fb213f', 'bf597fc7beef0ee4',
'c6e00bf33da88fc2', 'd5a79147930aa725',
'06ca6351e003826f', '142929670a0e6e70',
'27b70a8546d22ffc', '2e1b21385c26c926',
'4d2c6dfc5ac42aed', '53380d139d95b3df',
'650a73548baf63de', '766a0abb3c77b2a8',
'81c2c92e47edaee6', '92722c851482353b',
'a2bfe8a14cf10364', 'a81a664bbc423001',
'c24b8b70d0f89791', 'c76c51a30654be30',
'd192e819d6ef5218', 'd69906245565a910',
'f40e35855771202a', '106aa07032bbd1b8',
'19a4c116b8d2d0c8', '1e376c085141ab53',
'2748774cdf8eeb99', '34b0bcb5e19b48a8',
'391c0cb3c5c95a63', '4ed8aa4ae3418acb',
'5b9cca4f7763e373', '682e6ff3d6b2b8a3',
'748f82ee5defb2fc', '78a5636f43172f60',
'84c87814a1f0ab72', '8cc702081a6439ec',
'90befffa23631e28', 'a4506cebde82bde9',
'bef9a3f7b2c67915', 'c67178f2e372532b',
'ca273eceea26619c', 'd186b8c721c0c207',
'eada7dd6cde0eb1e', 'f57d4f7fee6ed178',
'06f067aa72176fba', '0a637dc5a2c898a6',
'113f9804bef90dae', '1b710b35131c471b',
'28db77f523047d84', '32caab7b40c72493',
'3c9ebe0a15c9bebc', '431d67c49c100d4c',
'4cc5d4becb3e42b6', '597f299cfc657e2a',
'5fcb6fab3ad6faec', '6c44198c4a475817'
);
for ($i = 0; $i < 80; $i++) {
$k[$i] = new BigInteger($k[$i], 16);
}
}
$hash = $this->l == 48 ? $init384 : $init512;
// Pre-processing
$length = strlen($m);
// to round to nearest 112 mod 128, we'll add 128 - (length +
(128 - 112)) % 128
$m.= str_repeat(chr(0), 128 - (($length + 16) & 0x7F));
$m[$length] = chr(0x80);
// we don't support hashing strings 512MB long
$m.= pack('N4', 0, 0, 0, $length << 3);
// Process the message in successive 1024-bit chunks
$chunks = str_split($m, 128);
foreach ($chunks as $chunk) {
$w = array();
for ($i = 0; $i < 16; $i++) {
$temp = new BigInteger($this->_string_shift($chunk, 8),
256);
$temp->setPrecision(64);
$w[] = $temp;
}
// Extend the sixteen 32-bit words into eighty 32-bit words
for ($i = 16; $i < 80; $i++) {
$temp = array(
$w[$i - 15]->bitwise_rightRotate(1),
$w[$i - 15]->bitwise_rightRotate(8),
$w[$i - 15]->bitwise_rightShift(7)
);
$s0 = $temp[0]->bitwise_xor($temp[1]);
$s0 = $s0->bitwise_xor($temp[2]);
$temp = array(
$w[$i - 2]->bitwise_rightRotate(19),
$w[$i - 2]->bitwise_rightRotate(61),
$w[$i - 2]->bitwise_rightShift(6)
);
$s1 = $temp[0]->bitwise_xor($temp[1]);
$s1 = $s1->bitwise_xor($temp[2]);
$w[$i] = $w[$i - 16]->copy();
$w[$i] = $w[$i]->add($s0);
$w[$i] = $w[$i]->add($w[$i - 7]);
$w[$i] = $w[$i]->add($s1);
}
// Initialize hash value for this chunk
$a = $hash[0]->copy();
$b = $hash[1]->copy();
$c = $hash[2]->copy();
$d = $hash[3]->copy();
$e = $hash[4]->copy();
$f = $hash[5]->copy();
$g = $hash[6]->copy();
$h = $hash[7]->copy();
// Main loop
for ($i = 0; $i < 80; $i++) {
$temp = array(
$a->bitwise_rightRotate(28),
$a->bitwise_rightRotate(34),
$a->bitwise_rightRotate(39)
);
$s0 = $temp[0]->bitwise_xor($temp[1]);
$s0 = $s0->bitwise_xor($temp[2]);
$temp = array(
$a->bitwise_and($b),
$a->bitwise_and($c),
$b->bitwise_and($c)
);
$maj = $temp[0]->bitwise_xor($temp[1]);
$maj = $maj->bitwise_xor($temp[2]);
$t2 = $s0->add($maj);
$temp = array(
$e->bitwise_rightRotate(14),
$e->bitwise_rightRotate(18),
$e->bitwise_rightRotate(41)
);
$s1 = $temp[0]->bitwise_xor($temp[1]);
$s1 = $s1->bitwise_xor($temp[2]);
$temp = array(
$e->bitwise_and($f),
$g->bitwise_and($e->bitwise_not())
);
$ch = $temp[0]->bitwise_xor($temp[1]);
$t1 = $h->add($s1);
$t1 = $t1->add($ch);
$t1 = $t1->add($k[$i]);
$t1 = $t1->add($w[$i]);
$h = $g->copy();
$g = $f->copy();
$f = $e->copy();
$e = $d->add($t1);
$d = $c->copy();
$c = $b->copy();
$b = $a->copy();
$a = $t1->add($t2);
}
// Add this chunk's hash to result so far
$hash = array(
$hash[0]->add($a),
$hash[1]->add($b),
$hash[2]->add($c),
$hash[3]->add($d),
$hash[4]->add($e),
$hash[5]->add($f),
$hash[6]->add($g),
$hash[7]->add($h)
);
}
// Produce the final hash value (big-endian)
// (\phpseclib\Crypt\Hash::hash() trims the output for hashes but
not for HMACs. as such, we trim the output here)
$temp = $hash[0]->toBytes() . $hash[1]->toBytes() .
$hash[2]->toBytes() . $hash[3]->toBytes() .
$hash[4]->toBytes() . $hash[5]->toBytes();
if ($this->l != 48) {
$temp.= $hash[6]->toBytes() . $hash[7]->toBytes();
}
return $temp;
}
/**
* Right Rotate
*
* @access private
* @param int $int
* @param int $amt
* @see self::_sha256()
* @return int
*/
function _rightRotate($int, $amt)
{
$invamt = 32 - $amt;
$mask = (1 << $invamt) - 1;
return (($int << $invamt) & 0xFFFFFFFF) | (($int >>
$amt) & $mask);
}
/**
* Right Shift
*
* @access private
* @param int $int
* @param int $amt
* @see self::_sha256()
* @return int
*/
function _rightShift($int, $amt)
{
$mask = (1 << (32 - $amt)) - 1;
return ($int >> $amt) & $mask;
}
/**
* Not
*
* @access private
* @param int $int
* @see self::_sha256()
* @return int
*/
function _not($int)
{
return ~$int & 0xFFFFFFFF;
}
/**
* Add
*
* _sha256() adds multiple unsigned 32-bit integers. Since PHP
doesn't support unsigned integers and since the
* possibility of overflow exists, care has to be taken. BigInteger
could be used but this should be faster.
*
* @return int
* @see self::_sha256()
* @access private
*/
function _add()
{
static $mod;
if (!isset($mod)) {
$mod = pow(2, 32);
}
$result = 0;
$arguments = func_get_args();
foreach ($arguments as $argument) {
$result+= $argument < 0 ? ($argument & 0x7FFFFFFF) +
0x80000000 : $argument;
}
if ((php_uname('m') & "\xDF\xDF\xDF") !=
'ARM') {
return fmod($result, $mod);
}
return (fmod($result, 0x80000000) & 0x7FFFFFFF) |
((fmod(floor($result / 0x80000000), 2) & 1) << 31);
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @return string
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php000064400000030044151156520630016773
0ustar00<?php
/**
* Random Number Generator
*
* PHP version 5
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* echo bin2hex(\phpseclib\Crypt\Random::string(8));
* ?>
* </code>
*
* @category Crypt
* @package Random
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Pure-PHP Random Number Generator
*
* @package Random
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class Random
{
/**
* Generate a random string.
*
* Although microoptimizations are generally discouraged as they impair
readability this function is ripe with
* microoptimizations because this function has the potential of being
called a huge number of times.
* eg. for RSA key generation.
*
* @param int $length
* @return string
*/
static function string($length)
{
if (!$length) {
return '';
}
if (version_compare(PHP_VERSION, '7.0.0',
'>=')) {
try {
return \random_bytes($length);
} catch (\Throwable $e) {
// If a sufficient source of randomness is unavailable,
random_bytes() will throw an
// object that implements the Throwable interface
(Exception, TypeError, Error).
// We don't actually need to do anything here. The
string() method should just continue
// as normal. Note, however, that if we don't have a
sufficient source of randomness for
// random_bytes(), most of the other calls here will fail
too, so we'll end up using
// the PHP implementation.
}
}
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
// method 1. prior to PHP 5.3 this would call rand() on windows
hence the function_exists('class_alias') call.
// ie. class_alias is a function that was introduced in PHP 5.3
if (extension_loaded('mcrypt') &&
function_exists('class_alias')) {
return @mcrypt_create_iv($length);
}
// method 2. openssl_random_pseudo_bytes was introduced in PHP
5.3.0 but prior to PHP 5.3.4 there was,
// to quote <http://php.net/ChangeLog-5.php#5.3.4>,
"possible blocking behavior". as of 5.3.4
// openssl_random_pseudo_bytes and mcrypt_create_iv do the
exact same thing on Windows. ie. they both
// call php_win32_get_random_bytes():
//
//
https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/openssl/openssl.c#L5008
//
https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1392
//
// php_win32_get_random_bytes() is defined thusly:
//
//
https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/win32/winutil.c#L80
//
// we're calling it, all the same, in the off chance that
the mcrypt extension is not available
if (extension_loaded('openssl') &&
version_compare(PHP_VERSION, '5.3.4', '>=')) {
return openssl_random_pseudo_bytes($length);
}
} else {
// method 1. the fastest
if (extension_loaded('openssl')) {
return openssl_random_pseudo_bytes($length);
}
// method 2
static $fp = true;
if ($fp === true) {
// warning's will be output unles the error
suppression operator is used. errors such as
// "open_basedir restriction in effect",
"Permission denied", "No such file or directory", etc.
$fp = @fopen('/dev/urandom', 'rb');
}
if ($fp !== true && $fp !== false) { // surprisingly
faster than !is_bool() or is_resource()
$temp = fread($fp, $length);
if (strlen($temp) == $length) {
return $temp;
}
}
// method 3. pretty much does the same thing as method 2 per
the following url:
//
https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1391
// surprisingly slower than method 2. maybe that's because
mcrypt_create_iv does a bunch of error checking that we're
// not doing. regardless, this'll only be called if this
PHP script couldn't open /dev/urandom due to open_basedir
// restrictions or some such
if (extension_loaded('mcrypt')) {
return @mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
}
}
// at this point we have no choice but to use a pure-PHP CSPRNG
// cascade entropy across multiple PHP instances by fixing the
session and collecting all
// environmental variables, including the previous session data and
the current session
// data.
//
// mt_rand seeds itself by looking at the PID and the time, both of
which are (relatively)
// 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
// 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
// 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
// on the data sent by all users. one user requests the page and a
hash of their info is saved.
// another user visits the page and the serialization of their data
is utilized along with the
// server envirnment stuff and a hash of the previous http request
data (which itself utilizes
// a hash of the session data before that). certainly an attacker
should be assumed to have
// full control over his own http requests. he, however, is not
going to have control over
// everyone's http requests.
static $crypto = false, $v;
if ($crypto === false) {
// save old session data
$old_session_id = session_id();
$old_use_cookies = ini_get('session.use_cookies');
$old_session_cache_limiter = session_cache_limiter();
$_OLD_SESSION = isset($_SESSION) ? $_SESSION : false;
if ($old_session_id != '') {
session_write_close();
}
session_id(1);
ini_set('session.use_cookies', 0);
session_cache_limiter('');
session_start();
$v = $seed = $_SESSION['seed'] = pack('H*',
sha1(
(isset($_SERVER) ? phpseclib_safe_serialize($_SERVER) :
'') .
(isset($_POST) ? phpseclib_safe_serialize($_POST) :
'') .
(isset($_GET) ? phpseclib_safe_serialize($_GET) :
'') .
(isset($_COOKIE) ? phpseclib_safe_serialize($_COOKIE) :
'') .
phpseclib_safe_serialize($GLOBALS) .
phpseclib_safe_serialize($_SESSION) .
phpseclib_safe_serialize($_OLD_SESSION)
));
if (!isset($_SESSION['count'])) {
$_SESSION['count'] = 0;
}
$_SESSION['count']++;
session_write_close();
// restore old session data
if ($old_session_id != '') {
session_id($old_session_id);
session_start();
ini_set('session.use_cookies', $old_use_cookies);
session_cache_limiter($old_session_cache_limiter);
} else {
if ($_OLD_SESSION !== false) {
$_SESSION = $_OLD_SESSION;
unset($_OLD_SESSION);
} else {
unset($_SESSION);
}
}
// in SSH2 a shared secret and an exchange hash are generated
through the key exchange process.
// the IV client to server is the hash of that
"nonce" with the letter A and for the encryption key it's
the letter C.
// if the hash doesn't produce enough a key or an IV
that's long enough concat successive hashes of the
// original hash and the current hash. we'll be emulating
that. for more info see the following URL:
//
// http://tools.ietf.org/html/rfc4253#section-7.2
//
// see the is_string($crypto) part for an example of how to
expand the keys
$key = pack('H*', sha1($seed . 'A'));
$iv = pack('H*', sha1($seed . 'C'));
// ciphers are used as per the nist.gov link below. also, see
this link:
//
//
http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives
switch (true) {
case class_exists('\phpseclib\Crypt\AES'):
$crypto = new AES(Base::MODE_CTR);
break;
case class_exists('\phpseclib\Crypt\Twofish'):
$crypto = new Twofish(Base::MODE_CTR);
break;
case class_exists('\phpseclib\Crypt\Blowfish'):
$crypto = new Blowfish(Base::MODE_CTR);
break;
case class_exists('\phpseclib\Crypt\TripleDES'):
$crypto = new TripleDES(Base::MODE_CTR);
break;
case class_exists('\phpseclib\Crypt\DES'):
$crypto = new DES(Base::MODE_CTR);
break;
case class_exists('\phpseclib\Crypt\RC4'):
$crypto = new RC4();
break;
default:
user_error(__CLASS__ . ' requires at least one
symmetric cipher be loaded');
return false;
}
$crypto->setKey($key);
$crypto->setIV($iv);
$crypto->enableContinuousBuffer();
}
//return $crypto->encrypt(str_repeat("\0", $length));
// the following is based off of ANSI X9.31:
//
// http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf
//
// OpenSSL uses that same standard for it's random numbers:
//
//
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")
$result = '';
while (strlen($result) < $length) {
$i = $crypto->encrypt(microtime()); // strlen(microtime())
== 21
$r = $crypto->encrypt($i ^ $v); // strlen($v) == 20
$v = $crypto->encrypt($r ^ $i); // strlen($r) == 20
$result.= $r;
}
return substr($result, 0, $length);
}
}
if (!function_exists('phpseclib_safe_serialize')) {
/**
* Safely serialize variables
*
* If a class has a private __sleep() method it'll give a fatal
error on PHP 5.2 and earlier.
* PHP 5.3 will emit a warning.
*
* @param mixed $arr
* @access public
*/
function phpseclib_safe_serialize(&$arr)
{
if (is_object($arr)) {
return '';
}
if (!is_array($arr)) {
return serialize($arr);
}
// prevent circular array recursion
if (isset($arr['__phpseclib_marker'])) {
return '';
}
$safearr = array();
$arr['__phpseclib_marker'] = true;
foreach (array_keys($arr) as $key) {
// do not recurse on the '__phpseclib_marker' key
itself, for smaller memory usage
if ($key !== '__phpseclib_marker') {
$safearr[$key] = phpseclib_safe_serialize($arr[$key]);
}
}
unset($arr['__phpseclib_marker']);
return serialize($safearr);
}
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php000064400000054020151156520630016141
0ustar00<?php
/**
* Pure-PHP implementation of RC2.
*
* Uses mcrypt, if available, and an internal implementation, otherwise.
*
* PHP version 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 'vendor/autoload.php';
*
* $rc2 = new \phpseclib\Crypt\RC2();
*
* $rc2->setKey('abcdefgh');
*
* $plaintext = str_repeat('a', 1024);
*
* echo $rc2->decrypt($rc2->encrypt($plaintext));
* ?>
* </code>
*
* @category Crypt
* @package RC2
* @author Patrick Monnerat <pm@datasphere.ch>
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Pure-PHP implementation of RC2.
*
* @package RC2
* @access public
*/
class RC2 extends Base
{
/**
* Block Length of the cipher
*
* @see \phpseclib\Crypt\Base::block_size
* @var int
* @access private
*/
var $block_size = 8;
/**
* The Key
*
* @see \phpseclib\Crypt\Base::key
* @see self::setKey()
* @var string
* @access private
*/
var $key;
/**
* The Original (unpadded) Key
*
* @see \phpseclib\Crypt\Base::key
* @see self::setKey()
* @see self::encrypt()
* @see self::decrypt()
* @var string
* @access private
*/
var $orig_key;
/**
* Don't truncate / null pad key
*
* @see \phpseclib\Crypt\Base::_clearBuffers()
* @var bool
* @access private
*/
var $skip_key_adjustment = true;
/**
* Key Length (in bytes)
*
* @see \phpseclib\Crypt\RC2::setKeyLength()
* @var int
* @access private
*/
var $key_length = 16; // = 128 bits
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib\Crypt\Base::cipher_name_mcrypt
* @var string
* @access private
*/
var $cipher_name_mcrypt = 'rc2';
/**
* Optimizing value while CFB-encrypting
*
* @see \phpseclib\Crypt\Base::cfb_init_len
* @var int
* @access private
*/
var $cfb_init_len = 500;
/**
* The key length in bits.
*
* @see self::setKeyLength()
* @see self::setKey()
* @var int
* @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 length in bits.
*
* @see self::isValidEnine()
* @see self::setKey()
* @var int
* @access private
* @internal Should be in range [1..1024].
*/
var $current_key_length;
/**
* The Key Schedule
*
* @see self::_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 self::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 self::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
);
/**
* Test for engine validity
*
* This is mainly just a wrapper to set things up for
\phpseclib\Crypt\Base::isValidEngine()
*
* @see \phpseclib\Crypt\Base::__construct()
* @param int $engine
* @access public
* @return bool
*/
function isValidEngine($engine)
{
switch ($engine) {
case self::ENGINE_OPENSSL:
if ($this->current_key_length != 128 ||
strlen($this->orig_key) < 16) {
return false;
}
$this->cipher_name_openssl_ecb = 'rc2-ecb';
$this->cipher_name_openssl = 'rc2-' .
$this->_openssl_translate_mode();
}
return parent::isValidEngine($engine);
}
/**
* Sets the key length.
*
* Valid key lengths are 8 to 1024.
* Calling this function after setting the key has no effect until the
next
* \phpseclib\Crypt\RC2::setKey() call.
*
* @access public
* @param int $length in bits
*/
function setKeyLength($length)
{
if ($length < 8) {
$this->default_key_length = 1;
} elseif ($length > 1024) {
$this->default_key_length = 128;
} else {
$this->default_key_length = $length;
}
$this->current_key_length = $this->default_key_length;
parent::setKeyLength($length);
}
/**
* Returns the current key length
*
* @access public
* @return int
*/
function getKeyLength()
{
return $this->current_key_length;
}
/**
* Sets the key.
*
* Keys can be of any length. RC2, itself, uses 8 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 \phpseclib\Crypt\Base::setKey()
* @access public
* @param string $key
* @param int $t1 optional Effective key length in bits.
*/
function setKey($key, $t1 = 0)
{
$this->orig_key = $key;
if ($t1 <= 0) {
$t1 = $this->default_key_length;
} elseif ($t1 > 1024) {
$t1 = 1024;
}
$this->current_key_length = $t1;
// 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 message.
*
* Mostly a wrapper for \phpseclib\Crypt\Base::encrypt, with some
additional OpenSSL handling code
*
* @see self::decrypt()
* @access public
* @param string $plaintext
* @return string $ciphertext
*/
function encrypt($plaintext)
{
if ($this->engine == self::ENGINE_OPENSSL) {
$temp = $this->key;
$this->key = $this->orig_key;
$result = parent::encrypt($plaintext);
$this->key = $temp;
return $result;
}
return parent::encrypt($plaintext);
}
/**
* Decrypts a message.
*
* Mostly a wrapper for \phpseclib\Crypt\Base::decrypt, with some
additional OpenSSL handling code
*
* @see self::encrypt()
* @access public
* @param string $ciphertext
* @return string $plaintext
*/
function decrypt($ciphertext)
{
if ($this->engine == self::ENGINE_OPENSSL) {
$temp = $this->key;
$this->key = $this->orig_key;
$result = parent::decrypt($ciphertext);
$this->key = $temp;
return $result;
}
return parent::decrypt($ciphertext);
}
/**
* Encrypts a block
*
* @see \phpseclib\Crypt\Base::_encryptBlock()
* @see \phpseclib\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 \phpseclib\Crypt\Base::_decryptBlock()
* @see \phpseclib\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);
}
/**
* Setup the \phpseclib\Crypt\Base::ENGINE_MCRYPT $engine
*
* @see \phpseclib\Crypt\Base::_setupMcrypt()
* @access private
*/
function _setupMcrypt()
{
if (!isset($this->key)) {
$this->setKey('');
}
parent::_setupMcrypt();
}
/**
* Creates the key schedule
*
* @see \phpseclib\Crypt\Base::_setupKey()
* @access private
*/
function _setupKey()
{
if (!isset($this->key)) {
$this->setKey('');
}
// Key has already been expanded in \phpseclib\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 \phpseclib\Crypt\Base::_setupInlineCrypt()
* @access private
*/
function _setupInlineCrypt()
{
$lambda_functions =& self::_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.
// (Currently, for Crypt_RC2, one generated $lambda_function cost
on php5.5@32bit ~60kb unfreeable mem and ~100kb on php5.5@64bit)
$gen_hi_opt_code = (bool)(count($lambda_functions) < 10);
// Generation of a unique hash for our generated code
$code_hash = "Crypt_RC2, {$this->mode}";
if ($gen_hi_opt_code) {
$code_hash = str_pad($code_hash, 32) .
$this->_hashInlineCryptFunction($this->key);
}
// 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;';
switch (true) {
case $gen_hi_opt_code:
$keys = $this->keys;
default:
$keys = array();
foreach ($this->keys as $k => $v) {
$keys[$k] = '$keys[' . $k .
']';
}
}
// $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];
}
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php000064400000021062151156520630016143
0ustar00<?php
/**
* Pure-PHP implementation of RC4.
*
* Uses mcrypt, if available, and an internal implementation, otherwise.
*
* PHP version 5
*
* Useful resources are as follows:
*
* - {@link
http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt
ARCFOUR Algorithm}
* - {@link http://en.wikipedia.org/wiki/RC4 - Wikipedia: RC4}
*
* RC4 is also known as ARCFOUR or ARC4. The reason is elaborated upon at
Wikipedia. This class is named RC4 and not
* ARCFOUR or ARC4 because RC4 is how it is referred to in the SSH1
specification.
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $rc4 = new \phpseclib\Crypt\RC4();
*
* $rc4->setKey('abcdefgh');
*
* $size = 10 * 1024;
* $plaintext = '';
* for ($i = 0; $i < $size; $i++) {
* $plaintext.= 'a';
* }
*
* echo $rc4->decrypt($rc4->encrypt($plaintext));
* ?>
* </code>
*
* @category Crypt
* @package RC4
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Pure-PHP implementation of RC4.
*
* @package RC4
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class RC4 extends Base
{
/**#@+
* @access private
* @see \phpseclib\Crypt\RC4::_crypt()
*/
const ENCRYPT = 0;
const DECRYPT = 1;
/**#@-*/
/**
* Block Length of the cipher
*
* RC4 is a stream cipher
* so we the block_size to 0
*
* @see \phpseclib\Crypt\Base::block_size
* @var int
* @access private
*/
var $block_size = 0;
/**
* Key Length (in bytes)
*
* @see \phpseclib\Crypt\RC4::setKeyLength()
* @var int
* @access private
*/
var $key_length = 128; // = 1024 bits
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib\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 \phpseclib\Crypt\Base::inline_crypt
* @var mixed
* @access private
*/
var $use_inline_crypt = false; // currently not available
/**
* The Key
*
* @see self::setKey()
* @var string
* @access private
*/
var $key;
/**
* The Key Stream for decryption and encryption
*
* @see self::setKey()
* @var array
* @access private
*/
var $stream;
/**
* Default Constructor.
*
* Determines whether or not the mcrypt extension should be used.
*
* @see \phpseclib\Crypt\Base::__construct()
* @return \phpseclib\Crypt\RC4
* @access public
*/
function __construct()
{
parent::__construct(Base::MODE_STREAM);
}
/**
* Test for engine validity
*
* This is mainly just a wrapper to set things up for
\phpseclib\Crypt\Base::isValidEngine()
*
* @see \phpseclib\Crypt\Base::__construct()
* @param int $engine
* @access public
* @return bool
*/
function isValidEngine($engine)
{
if ($engine == Base::ENGINE_OPENSSL) {
if (version_compare(PHP_VERSION, '5.3.7') >= 0) {
$this->cipher_name_openssl = 'rc4-40';
} else {
switch (strlen($this->key)) {
case 5:
$this->cipher_name_openssl = 'rc4-40';
break;
case 8:
$this->cipher_name_openssl = 'rc4-64';
break;
case 16:
$this->cipher_name_openssl = 'rc4';
break;
default:
return false;
}
}
}
return parent::isValidEngine($engine);
}
/**
* Dummy function.
*
* 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
*/
function setIV($iv)
{
}
/**
* Sets the key length
*
* Keys can be between 1 and 256 bytes long.
*
* @access public
* @param int $length
*/
function setKeyLength($length)
{
if ($length < 8) {
$this->key_length = 1;
} elseif ($length > 2048) {
$this->key_length = 256;
} else {
$this->key_length = $length >> 3;
}
parent::setKeyLength($length);
}
/**
* Encrypts a message.
*
* @see \phpseclib\Crypt\Base::decrypt()
* @see self::_crypt()
* @access public
* @param string $plaintext
* @return string $ciphertext
*/
function encrypt($plaintext)
{
if ($this->engine != Base::ENGINE_INTERNAL) {
return parent::encrypt($plaintext);
}
return $this->_crypt($plaintext, self::ENCRYPT);
}
/**
* Decrypts a message.
*
* $this->decrypt($this->encrypt($plaintext)) ==
$this->encrypt($this->encrypt($plaintext)).
* At least if the continuous buffer is disabled.
*
* @see \phpseclib\Crypt\Base::encrypt()
* @see self::_crypt()
* @access public
* @param string $ciphertext
* @return string $plaintext
*/
function decrypt($ciphertext)
{
if ($this->engine != Base::ENGINE_INTERNAL) {
return parent::decrypt($ciphertext);
}
return $this->_crypt($ciphertext, self::DECRYPT);
}
/**
* Encrypts a block
*
* @access private
* @param string $in
*/
function _encryptBlock($in)
{
// RC4 does not utilize this method
}
/**
* Decrypts a block
*
* @access private
* @param string $in
*/
function _decryptBlock($in)
{
// RC4 does not utilize this method
}
/**
* Setup the key (expansion)
*
* @see \phpseclib\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[self::DECRYPT] = $this->stream[self::ENCRYPT] =
array(
0, // index $i
0, // index $j
$keyStream
);
}
/**
* Encrypts or decrypts a message.
*
* @see self::encrypt()
* @see self::decrypt()
* @access private
* @param string $text
* @param int $mode
* @return string $text
*/
function _crypt($text, $mode)
{
if ($this->changed) {
$this->_setup();
$this->changed = false;
}
$stream = &$this->stream[$mode];
if ($this->continuousBuffer) {
$i = &$stream[0];
$j = &$stream[1];
$keyStream = &$stream[2];
} else {
$i = $stream[0];
$j = $stream[1];
$keyStream = $stream[2];
}
$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;
}
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php000064400000121465151156520630017313
0ustar00<?php
/**
* Pure-PHP implementation of Rijndael.
*
* Uses mcrypt, if available/possible, and an internal implementation,
otherwise.
*
* PHP version 5
*
* If {@link self::setBlockLength() setBlockLength()} isn't called,
it'll be assumed to be 128 bits. If
* {@link self::setKeyLength() setKeyLength()} isn't called,
it'll be calculated from
* {@link self::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 192-bits and 192 bits will be the
key length until
* {@link self::setKey() setKey()} is called, again, at which point,
it'll be recalculated.
*
* Not all Rijndael implementations may support 160-bits or 224-bits as the
block length / key length. mcrypt, for example,
* does not. AES, itself, only supports block lengths of 128 and key
lengths of 128, 192, and 256.
* {@link
http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=10
Rijndael-ammended.pdf#page=10} defines the
* algorithm for block lengths of 192 and 256 but not for block lengths /
key lengths of 160 and 224. Indeed, 160 and 224
* are first defined as valid key / block lengths in
* {@link
http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=44
Rijndael-ammended.pdf#page=44}:
* Extensions: Other block and Cipher Key lengths.
* Note: Use of 160/224-bit Keys must be explicitly set by
setKeyLength(160) respectively setKeyLength(224).
*
* {@internal The variable names are the same as those in
* {@link
http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf#page=10
fips-197.pdf#page=10}.}}
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $rijndael = new \phpseclib\Crypt\Rijndael();
*
* $rijndael->setKey('abcdefghijklmnop');
*
* $size = 10 * 1024;
* $plaintext = '';
* for ($i = 0; $i < $size; $i++) {
* $plaintext.= 'a';
* }
*
* echo $rijndael->decrypt($rijndael->encrypt($plaintext));
* ?>
* </code>
*
* @category Crypt
* @package Rijndael
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2008 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Pure-PHP implementation of Rijndael.
*
* @package Rijndael
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class Rijndael extends Base
{
/**
* The mcrypt specific name of the cipher
*
* Mcrypt is useable for 128/192/256-bit $block_size/$key_length. For
160/224 not.
* \phpseclib\Crypt\Rijndael determines automatically whether mcrypt is
useable
* or not for the current $block_size/$key_length.
* In case of, $cipher_name_mcrypt will be set dynamically at run time
accordingly.
*
* @see \phpseclib\Crypt\Base::cipher_name_mcrypt
* @see \phpseclib\Crypt\Base::engine
* @see self::isValidEngine()
* @var string
* @access private
*/
var $cipher_name_mcrypt = 'rijndael-128';
/**
* The default salt used by setPassword()
*
* @see \phpseclib\Crypt\Base::password_default_salt
* @see \phpseclib\Crypt\Base::setPassword()
* @var string
* @access private
*/
var $password_default_salt = 'phpseclib';
/**
* The Key Schedule
*
* @see self::_setup()
* @var array
* @access private
*/
var $w;
/**
* The Inverse Key Schedule
*
* @see self::_setup()
* @var array
* @access private
*/
var $dw;
/**
* The Block Length divided by 32
*
* @see self::setBlockLength()
* @var int
* @access private
* @internal The max value is 256 / 32 = 8, the min value is 128 / 32 =
4. Exists in conjunction with $block_size
* because the encryption / decryption / key schedule creation
requires this number and not $block_size. We could
* derive this from $block_size or vice versa, but that'd mean
we'd have to do multiple shift operations, so in lieu
* of that, we'll just precompute it once.
*/
var $Nb = 4;
/**
* The Key Length (in bytes)
*
* @see self::setKeyLength()
* @var int
* @access private
* @internal The max value is 256 / 8 = 32, the min value is 128 / 8 =
16. Exists in conjunction with $Nk
* because the encryption / decryption / key schedule creation
requires this number and not $key_length. We could
* derive this from $key_length or vice versa, but that'd mean
we'd have to do multiple shift operations, so in lieu
* of that, we'll just precompute it once.
*/
var $key_length = 16;
/**
* The Key Length divided by 32
*
* @see self::setKeyLength()
* @var int
* @access private
* @internal The max value is 256 / 32 = 8, the min value is 128 / 32 =
4
*/
var $Nk = 4;
/**
* The Number of Rounds
*
* @var int
* @access private
* @internal The max value is 14, the min value is 10.
*/
var $Nr;
/**
* Shift offsets
*
* @var array
* @access private
*/
var $c;
/**
* Holds the last used key- and block_size information
*
* @var array
* @access private
*/
var $kl;
/**
* 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
* 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
* and the most (if not all) implementations are not able using
160/224-bit keys but round/pad them up to
* 192/256 bits as, for example, mcrypt will do.
*
* That said, if you want be compatible with other Rijndael and
AES implementations,
* you should not setKeyLength(160) or setKeyLength(224).
*
* Additional: In case of 160- and 224-bit keys, phpseclib will/can,
for that reason, not use
* the mcrypt php extension, even if available.
* This results then in slower encryption.
*
* @access public
* @param int $length
*/
function setKeyLength($length)
{
switch (true) {
case $length <= 128:
$this->key_length = 16;
break;
case $length <= 160:
$this->key_length = 20;
break;
case $length <= 192:
$this->key_length = 24;
break;
case $length <= 224:
$this->key_length = 28;
break;
default:
$this->key_length = 32;
}
parent::setKeyLength($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
* 128. If the length is greater than 128 and invalid, it will be
rounded down to the closest valid amount.
*
* @access public
* @param int $length
*/
function setBlockLength($length)
{
$length >>= 5;
if ($length > 8) {
$length = 8;
} elseif ($length < 4) {
$length = 4;
}
$this->Nb = $length;
$this->block_size = $length << 2;
$this->changed = true;
$this->_setEngine();
}
/**
* Test for engine validity
*
* This is mainly just a wrapper to set things up for
\phpseclib\Crypt\Base::isValidEngine()
*
* @see \phpseclib\Crypt\Base::__construct()
* @param int $engine
* @access public
* @return bool
*/
function isValidEngine($engine)
{
switch ($engine) {
case self::ENGINE_OPENSSL:
if ($this->block_size != 16) {
return false;
}
$this->cipher_name_openssl_ecb = 'aes-' .
($this->key_length << 3) . '-ecb';
$this->cipher_name_openssl = 'aes-' .
($this->key_length << 3) . '-' .
$this->_openssl_translate_mode();
break;
case self::ENGINE_MCRYPT:
$this->cipher_name_mcrypt = 'rijndael-' .
($this->block_size << 3);
if ($this->key_length % 8) { // is it a 160/224-bit key?
// mcrypt is not usable for them, only for
128/192/256-bit keys
return false;
}
}
return parent::isValidEngine($engine);
}
/**
* Encrypts a block
*
* @access private
* @param string $in
* @return string
*/
function _encryptBlock($in)
{
static $tables;
if (empty($tables)) {
$tables = &$this->_getTables();
}
$t0 = $tables[0];
$t1 = $tables[1];
$t2 = $tables[2];
$t3 = $tables[3];
$sbox = $tables[4];
$state = array();
$words = unpack('N*', $in);
$c = $this->c;
$w = $this->w;
$Nb = $this->Nb;
$Nr = $this->Nr;
// addRoundKey
$wc = $Nb - 1;
foreach ($words as $word) {
$state[] = $word ^ $w[++$wc];
}
// fips-197.pdf#page=19, "Figure 5. Pseudo Code for the
Cipher", states that this loop has four components -
// subBytes, shiftRows, mixColumns, and addRoundKey.
fips-197.pdf#page=30, "Implementation Suggestions Regarding
// Various Platforms" suggests that performs enhanced
implementations are described in Rijndael-ammended.pdf.
// Rijndael-ammended.pdf#page=20, "Implementation aspects /
32-bit processor", discusses such an optimization.
// Unfortunately, the description given there is not quite correct.
Per aes.spec.v316.pdf#page=19 [1],
// equation (7.4.7) is supposed to use addition instead of
subtraction, so we'll do that here, as well.
// [1]
http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf
$temp = array();
for ($round = 1; $round < $Nr; ++$round) {
$i = 0; // $c[0] == 0
$j = $c[1];
$k = $c[2];
$l = $c[3];
while ($i < $Nb) {
$temp[$i] = $t0[$state[$i] >> 24 & 0x000000FF] ^
$t1[$state[$j] >> 16 & 0x000000FF] ^
$t2[$state[$k] >> 8 & 0x000000FF] ^
$t3[$state[$l] & 0x000000FF] ^
$w[++$wc];
++$i;
$j = ($j + 1) % $Nb;
$k = ($k + 1) % $Nb;
$l = ($l + 1) % $Nb;
}
$state = $temp;
}
// subWord
for ($i = 0; $i < $Nb; ++$i) {
$state[$i] = $sbox[$state[$i] & 0x000000FF]
|
($sbox[$state[$i] >> 8 & 0x000000FF]
<< 8) |
($sbox[$state[$i] >> 16 & 0x000000FF]
<< 16) |
($sbox[$state[$i] >> 24 & 0x000000FF]
<< 24);
}
// shiftRows + addRoundKey
$i = 0; // $c[0] == 0
$j = $c[1];
$k = $c[2];
$l = $c[3];
while ($i < $Nb) {
$temp[$i] = ($state[$i] & 0xFF000000) ^
($state[$j] & 0x00FF0000) ^
($state[$k] & 0x0000FF00) ^
($state[$l] & 0x000000FF) ^
$w[$i];
++$i;
$j = ($j + 1) % $Nb;
$k = ($k + 1) % $Nb;
$l = ($l + 1) % $Nb;
}
switch ($Nb) {
case 8:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3], $temp[4], $temp[5], $temp[6], $temp[7]);
case 7:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3], $temp[4], $temp[5], $temp[6]);
case 6:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3], $temp[4], $temp[5]);
case 5:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3], $temp[4]);
default:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3]);
}
}
/**
* Decrypts a block
*
* @access private
* @param string $in
* @return string
*/
function _decryptBlock($in)
{
static $invtables;
if (empty($invtables)) {
$invtables = &$this->_getInvTables();
}
$dt0 = $invtables[0];
$dt1 = $invtables[1];
$dt2 = $invtables[2];
$dt3 = $invtables[3];
$isbox = $invtables[4];
$state = array();
$words = unpack('N*', $in);
$c = $this->c;
$dw = $this->dw;
$Nb = $this->Nb;
$Nr = $this->Nr;
// addRoundKey
$wc = $Nb - 1;
foreach ($words as $word) {
$state[] = $word ^ $dw[++$wc];
}
$temp = array();
for ($round = $Nr - 1; $round > 0; --$round) {
$i = 0; // $c[0] == 0
$j = $Nb - $c[1];
$k = $Nb - $c[2];
$l = $Nb - $c[3];
while ($i < $Nb) {
$temp[$i] = $dt0[$state[$i] >> 24 & 0x000000FF] ^
$dt1[$state[$j] >> 16 & 0x000000FF] ^
$dt2[$state[$k] >> 8 & 0x000000FF] ^
$dt3[$state[$l] & 0x000000FF] ^
$dw[++$wc];
++$i;
$j = ($j + 1) % $Nb;
$k = ($k + 1) % $Nb;
$l = ($l + 1) % $Nb;
}
$state = $temp;
}
// invShiftRows + invSubWord + addRoundKey
$i = 0; // $c[0] == 0
$j = $Nb - $c[1];
$k = $Nb - $c[2];
$l = $Nb - $c[3];
while ($i < $Nb) {
$word = ($state[$i] & 0xFF000000) |
($state[$j] & 0x00FF0000) |
($state[$k] & 0x0000FF00) |
($state[$l] & 0x000000FF);
$temp[$i] = $dw[$i] ^ ($isbox[$word & 0x000000FF]
|
($isbox[$word >> 8 &
0x000000FF] << 8) |
($isbox[$word >> 16 &
0x000000FF] << 16) |
($isbox[$word >> 24 &
0x000000FF] << 24));
++$i;
$j = ($j + 1) % $Nb;
$k = ($k + 1) % $Nb;
$l = ($l + 1) % $Nb;
}
switch ($Nb) {
case 8:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3], $temp[4], $temp[5], $temp[6], $temp[7]);
case 7:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3], $temp[4], $temp[5], $temp[6]);
case 6:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3], $temp[4], $temp[5]);
case 5:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3], $temp[4]);
default:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3]);
}
}
/**
* Setup the key (expansion)
*
* @see \phpseclib\Crypt\Base::_setupKey()
* @access private
*/
function _setupKey()
{
// Each number in $rcon is equal to the previous number multiplied
by two in Rijndael's finite field.
// See
http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse
static $rcon = array(0,
0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000,
0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000,
0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000,
0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000
);
if (isset($this->kl['key']) && $this->key
=== $this->kl['key'] && $this->key_length ===
$this->kl['key_length'] && $this->block_size ===
$this->kl['block_size']) {
// already expanded
return;
}
$this->kl = array('key' => $this->key,
'key_length' => $this->key_length, 'block_size'
=> $this->block_size);
$this->Nk = $this->key_length >> 2;
// see Rijndael-ammended.pdf#page=44
$this->Nr = max($this->Nk, $this->Nb) + 6;
// shift offsets for Nb = 5, 7 are defined in
Rijndael-ammended.pdf#page=44,
// "Table 8: Shift offsets in Shiftrow for the alternative
block lengths"
// shift offsets for Nb = 4, 6, 8 are defined in
Rijndael-ammended.pdf#page=14,
// "Table 2: Shift offsets for different block
lengths"
switch ($this->Nb) {
case 4:
case 5:
case 6:
$this->c = array(0, 1, 2, 3);
break;
case 7:
$this->c = array(0, 1, 2, 4);
break;
case 8:
$this->c = array(0, 1, 3, 4);
}
$w = array_values(unpack('N*words', $this->key));
$length = $this->Nb * ($this->Nr + 1);
for ($i = $this->Nk; $i < $length; $i++) {
$temp = $w[$i - 1];
if ($i % $this->Nk == 0) {
// according to
<http://php.net/language.types.integer>, "the size of an integer
is platform-dependent".
// on a 32-bit machine, it's 32-bits, and on a 64-bit
machine, it's 64-bits. on a 32-bit machine,
// 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit
machine, it equals 0xFFFFFFFF00. as such, doing 'and'
// with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is
unnecessary, but on a 64-bit machine, it is.
$temp = (($temp << 8) & 0xFFFFFF00) | (($temp
>> 24) & 0x000000FF); // rotWord
$temp = $this->_subWord($temp) ^ $rcon[$i /
$this->Nk];
} elseif ($this->Nk > 6 && $i % $this->Nk ==
4) {
$temp = $this->_subWord($temp);
}
$w[$i] = $w[$i - $this->Nk] ^ $temp;
}
// convert the key schedule from a vector of $Nb * ($Nr + 1) length
to a matrix with $Nr + 1 rows and $Nb columns
// and generate the inverse key schedule. more specifically,
// according to
<http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=23>
(section 5.3.3),
// "The key expansion for the Inverse Cipher is defined as
follows:
// 1. Apply the Key Expansion.
// 2. Apply InvMixColumn to all Round Keys except the first
and the last one."
// also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse
Cipher"
list($dt0, $dt1, $dt2, $dt3) = $this->_getInvTables();
$temp = $this->w = $this->dw = array();
for ($i = $row = $col = 0; $i < $length; $i++, $col++) {
if ($col == $this->Nb) {
if ($row == 0) {
$this->dw[0] = $this->w[0];
} else {
// subWord + invMixColumn + invSubWord = invMixColumn
$j = 0;
while ($j < $this->Nb) {
$dw = $this->_subWord($this->w[$row][$j]);
$temp[$j] = $dt0[$dw >> 24 & 0x000000FF]
^
$dt1[$dw >> 16 & 0x000000FF]
^
$dt2[$dw >> 8 & 0x000000FF]
^
$dt3[$dw & 0x000000FF];
$j++;
}
$this->dw[$row] = $temp;
}
$col = 0;
$row++;
}
$this->w[$row][$col] = $w[$i];
}
$this->dw[$row] = $this->w[$row];
// Converting to 1-dim key arrays (both ascending)
$this->dw = array_reverse($this->dw);
$w = array_pop($this->w);
$dw = array_pop($this->dw);
foreach ($this->w as $r => $wr) {
foreach ($wr as $c => $wc) {
$w[] = $wc;
$dw[] = $this->dw[$r][$c];
}
}
$this->w = $w;
$this->dw = $dw;
}
/**
* Performs S-Box substitutions
*
* @access private
* @param int $word
*/
function _subWord($word)
{
static $sbox;
if (empty($sbox)) {
list(, , , , $sbox) = $this->_getTables();
}
return $sbox[$word & 0x000000FF] |
($sbox[$word >> 8 & 0x000000FF] << 8) |
($sbox[$word >> 16 & 0x000000FF] << 16) |
($sbox[$word >> 24 & 0x000000FF] << 24);
}
/**
* Provides the mixColumns and sboxes tables
*
* @see self::_encryptBlock()
* @see self::_setupInlineCrypt()
* @see self::_subWord()
* @access private
* @return array &$tables
*/
function &_getTables()
{
static $tables;
if (empty($tables)) {
// according to
<http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=19>
(section 5.2.1),
// precomputed tables can be used in the mixColumns phase. in
that example, they're assigned t0...t3, so
// those are the names we'll use.
$t3 = array_map('intval', array(
// with array_map('intval', ...) we ensure we
have only int's and not
// some slower floats converted by php automatically on
high values
0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF,
0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7,
0xD7D762B5, 0xABABE64D, 0x76769AEC,
0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF,
0x5959EBB2, 0x4747C98E, 0xF0F00BFB,
0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23,
0xA4A4F753, 0x727296E4, 0xC0C05B9B,
0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C,
0x3F3F417E, 0xF7F702F5, 0xCCCC4F83,
0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2,
0xD8D873AB, 0x31315362, 0x15153F2A,
0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830,
0x9696A137, 0x05050F0A, 0x9A9AB52F,
0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD,
0x2727694E, 0xB2B2CD7F, 0x75759FEA,
0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36,
0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B,
0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52,
0xE3E33EDD, 0x2F2F715E, 0x84849713,
0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040,
0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6,
0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94,
0x4C4CD498, 0x5858E8B0, 0xCFCF4A85,
0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586,
0x4D4DD79A, 0x33335566, 0x85859411,
0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0,
0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B,
0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F,
0x9D9DBC21, 0x38384870, 0xF5F504F1,
0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020,
0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF,
0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE,
0x9797A235, 0x4444CC88, 0x1717392E,
0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8,
0x5D5DE7BA, 0x19192B32, 0x737395E6,
0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644,
0x2A2A7E54, 0x9090AB3B, 0x8888830B,
0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7,
0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD,
0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92,
0x06060A0C, 0x24246C48, 0x5C5CE4B8,
0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839,
0x9595A431, 0xE4E437D3, 0x79798BF2,
0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01,
0xD5D564B1, 0x4E4ED29C, 0xA9A9E049,
0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA,
0x7A7A8EF4, 0xAEAEE947, 0x08081810,
0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438,
0xA6A6F157, 0xB4B4C773, 0xC6C65197,
0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96,
0xBDBDDC61, 0x8B8B860D, 0x8A8A850F,
0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890,
0x03030506, 0xF6F601F7, 0x0E0E121C,
0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117,
0xC1C15899, 0x1D1D273A, 0x9E9EB927,
0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2,
0xD9D970A9, 0x8E8E8907, 0x9494A733,
0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987,
0x5555FFAA, 0x28287850, 0xDFDF7AA5,
0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65,
0xE6E631D7, 0x4242C684, 0x6868B8D0,
0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B,
0x5454FCA8, 0xBBBBD66D, 0x16163A2C
));
foreach ($t3 as $t3i) {
$t0[] = (($t3i << 24) & 0xFF000000) | (($t3i
>> 8) & 0x00FFFFFF);
$t1[] = (($t3i << 16) & 0xFFFF0000) | (($t3i
>> 16) & 0x0000FFFF);
$t2[] = (($t3i << 8) & 0xFFFFFF00) | (($t3i
>> 24) & 0x000000FF);
}
$tables = array(
// The Precomputed mixColumns tables t0 - t3
$t0,
$t1,
$t2,
$t3,
// The SubByte S-Box
array(
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30,
0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD,
0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34,
0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07,
0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52,
0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A,
0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45,
0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC,
0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4,
0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46,
0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2,
0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C,
0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8,
0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61,
0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B,
0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41,
0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
)
);
}
return $tables;
}
/**
* Provides the inverse mixColumns and inverse sboxes tables
*
* @see self::_decryptBlock()
* @see self::_setupInlineCrypt()
* @see self::_setupKey()
* @access private
* @return array &$tables
*/
function &_getInvTables()
{
static $tables;
if (empty($tables)) {
$dt3 = array_map('intval', array(
0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B,
0x9D45F11F, 0xFA58ABAC, 0xE303934B,
0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F,
0x2ACBD7C5, 0x35448026, 0x62A38FB5,
0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3,
0x4CF01281, 0x4697A38D, 0xD3F9C66B,
0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4,
0x7421D358, 0xE0692949, 0xC9C8448E,
0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE,
0x88AD17F0, 0x20AC66C9, 0xCE3AB47D,
0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1,
0x6BAE84BB, 0x81A01CFE, 0x082B94F9,
0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB,
0x4B02E272, 0x1F8F57E3, 0x55AB2A66,
0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230,
0xBFA5B223, 0x036ABA02, 0x16825CED,
0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65,
0x05BED506, 0x34621FD1, 0xA6FE8AC4,
0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B,
0x60EFAA40, 0x719F065E, 0x6E1051BD,
0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591,
0xC45D0571, 0x06D46F04, 0x5015FF60,
0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0,
0x898B8807, 0x195B38E7, 0xC8EEDB79,
0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309,
0x2BED4832, 0x1170AC1E, 0x5A724E6C,
0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A,
0x5CA62168, 0x5B54D19B, 0x362E3A24,
0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80,
0xDC20A261, 0x774B695A, 0x121A161C,
0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E,
0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814,
0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7,
0x72F5BC5C, 0x663BC544, 0xFB7E345B,
0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7,
0x63851042, 0x97224013, 0xC6112084,
0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D,
0xB230F3DC, 0x8652EC0D, 0xC1E3D077,
0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8,
0xF03F1AA0, 0x7D2CD856, 0x3390EF22,
0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6,
0x7ADE28A5, 0xB78E26DA, 0xADBFA43F,
0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6,
0xD8B8E890, 0x39F75E2E, 0xC3AFF582,
0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8,
0x187DA710, 0x9C636EE8, 0x3BBB7BDB,
0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6,
0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF,
0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31,
0x3F23312A, 0xA59430C6, 0xA266C035,
0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1,
0xECDAF741, 0xCD500E7F, 0x91F62F17,
0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E,
0x6A881B4C, 0x2C1FB8C1, 0x65517F46,
0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3,
0xDBD25292, 0x105633E9, 0xD647136D,
0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE,
0x61C935B7, 0x1CE5EDE1, 0x47B13C7A,
0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53,
0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678,
0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216,
0xE2250CBC, 0x3C498B28, 0x0D9541FF,
0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B,
0x32B670D5, 0x6C5C7448, 0xB85742D0
));
foreach ($dt3 as $dt3i) {
$dt0[] = (($dt3i << 24) & 0xFF000000) | (($dt3i
>> 8) & 0x00FFFFFF);
$dt1[] = (($dt3i << 16) & 0xFFFF0000) | (($dt3i
>> 16) & 0x0000FFFF);
$dt2[] = (($dt3i << 8) & 0xFFFFFF00) | (($dt3i
>> 24) & 0x000000FF);
};
$tables = array(
// The Precomputed inverse mixColumns tables dt0 - dt3
$dt0,
$dt1,
$dt2,
$dt3,
// The inverse SubByte S-Box
array(
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF,
0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34,
0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE,
0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76,
0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4,
0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E,
0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7,
0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1,
0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97,
0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2,
0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F,
0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A,
0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1,
0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D,
0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8,
0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1,
0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
)
);
}
return $tables;
}
/**
* Setup the performance-optimized function for de/encrypt()
*
* @see \phpseclib\Crypt\Base::_setupInlineCrypt()
* @access private
*/
function _setupInlineCrypt()
{
// Note: _setupInlineCrypt() will be called only if
$this->changed === true
// So here we are'nt under the same heavy timing-stress as we
are in _de/encryptBlock() or de/encrypt().
// However...the here generated function- $code, stored as php
callback in $this->inline_crypt, must work as fast as even possible.
$lambda_functions =& self::_getLambdaFunctions();
// We create max. 10 hi-optimized code for memory reason. Means:
For each $key one ultra fast inline-crypt function.
// (Currently, for Crypt_Rijndael/AES, one generated
$lambda_function cost on php5.5@32bit ~80kb unfreeable mem and ~130kb on
php5.5@64bit)
// After that, we'll still create very fast optimized code but
not the hi-ultimative code, for each $mode one.
$gen_hi_opt_code = (bool)(count($lambda_functions) < 10);
// Generation of a uniqe hash for our generated code
$code_hash = "Crypt_Rijndael, {$this->mode},
{$this->Nr}, {$this->Nb}";
if ($gen_hi_opt_code) {
$code_hash = str_pad($code_hash, 32) .
$this->_hashInlineCryptFunction($this->key);
}
if (!isset($lambda_functions[$code_hash])) {
switch (true) {
case $gen_hi_opt_code:
// The hi-optimized $lambda_functions will use the
key-words hardcoded for better performance.
$w = $this->w;
$dw = $this->dw;
$init_encrypt = '';
$init_decrypt = '';
break;
default:
for ($i = 0, $cw = count($this->w); $i < $cw;
++$i) {
$w[] = '$w[' . $i . ']';
$dw[] = '$dw[' . $i . ']';
}
$init_encrypt = '$w = $self->w;';
$init_decrypt = '$dw = $self->dw;';
}
$Nr = $this->Nr;
$Nb = $this->Nb;
$c = $this->c;
// Generating encrypt code:
$init_encrypt.= '
static $tables;
if (empty($tables)) {
$tables = &$self->_getTables();
}
$t0 = $tables[0];
$t1 = $tables[1];
$t2 = $tables[2];
$t3 = $tables[3];
$sbox = $tables[4];
';
$s = 'e';
$e = 's';
$wc = $Nb - 1;
// Preround: addRoundKey
$encrypt_block = '$in = unpack("N*",
$in);'."\n";
for ($i = 0; $i < $Nb; ++$i) {
$encrypt_block .= '$s'.$i.' = $in['.($i
+ 1).'] ^ '.$w[++$wc].";\n";
}
// Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
for ($round = 1; $round < $Nr; ++$round) {
list($s, $e) = array($e, $s);
for ($i = 0; $i < $Nb; ++$i) {
$encrypt_block.=
'$'.$e.$i.' =
$t0[($'.$s.$i .'
>> 24) & 0xff] ^
$t1[($'.$s.(($i + $c[1]) % $Nb).'
>> 16) & 0xff] ^
$t2[($'.$s.(($i + $c[2]) % $Nb).'
>> 8) & 0xff] ^
$t3[ $'.$s.(($i + $c[3]) % $Nb).'
& 0xff] ^
'.$w[++$wc].";\n";
}
}
// Finalround: subWord + shiftRows + addRoundKey
for ($i = 0; $i < $Nb; ++$i) {
$encrypt_block.=
'$'.$e.$i.' =
$sbox[ $'.$e.$i.' & 0xff]
|
($sbox[($'.$e.$i.' >> 8) & 0xff]
<< 8) |
($sbox[($'.$e.$i.' >> 16) & 0xff]
<< 16) |
($sbox[($'.$e.$i.' >> 24) & 0xff]
<< 24);'."\n";
}
$encrypt_block .= '$in =
pack("N*"'."\n";
for ($i = 0; $i < $Nb; ++$i) {
$encrypt_block.= ',
($'.$e.$i .' &
'.((int)0xFF000000).') ^
($'.$e.(($i + $c[1]) % $Nb).' &
0x00FF0000 ) ^
($'.$e.(($i + $c[2]) % $Nb).' &
0x0000FF00 ) ^
($'.$e.(($i + $c[3]) % $Nb).' &
0x000000FF ) ^
'.$w[$i]."\n";
}
$encrypt_block .= ');';
// Generating decrypt code:
$init_decrypt.= '
static $invtables;
if (empty($invtables)) {
$invtables = &$self->_getInvTables();
}
$dt0 = $invtables[0];
$dt1 = $invtables[1];
$dt2 = $invtables[2];
$dt3 = $invtables[3];
$isbox = $invtables[4];
';
$s = 'e';
$e = 's';
$wc = $Nb - 1;
// Preround: addRoundKey
$decrypt_block = '$in = unpack("N*",
$in);'."\n";
for ($i = 0; $i < $Nb; ++$i) {
$decrypt_block .= '$s'.$i.' = $in['.($i
+ 1).'] ^ '.$dw[++$wc].';'."\n";
}
// Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
for ($round = 1; $round < $Nr; ++$round) {
list($s, $e) = array($e, $s);
for ($i = 0; $i < $Nb; ++$i) {
$decrypt_block.=
'$'.$e.$i.' =
$dt0[($'.$s.$i .'
>> 24) & 0xff] ^
$dt1[($'.$s.(($Nb + $i - $c[1]) % $Nb).'
>> 16) & 0xff] ^
$dt2[($'.$s.(($Nb + $i - $c[2]) % $Nb).'
>> 8) & 0xff] ^
$dt3[ $'.$s.(($Nb + $i - $c[3]) % $Nb).'
& 0xff] ^
'.$dw[++$wc].";\n";
}
}
// Finalround: subWord + shiftRows + addRoundKey
for ($i = 0; $i < $Nb; ++$i) {
$decrypt_block.=
'$'.$e.$i.' =
$isbox[ $'.$e.$i.' & 0xff]
|
($isbox[($'.$e.$i.' >> 8) & 0xff]
<< 8) |
($isbox[($'.$e.$i.' >> 16) & 0xff]
<< 16) |
($isbox[($'.$e.$i.' >> 24) & 0xff]
<< 24);'."\n";
}
$decrypt_block .= '$in =
pack("N*"'."\n";
for ($i = 0; $i < $Nb; ++$i) {
$decrypt_block.= ',
($'.$e.$i. ' &
'.((int)0xFF000000).') ^
($'.$e.(($Nb + $i - $c[1]) % $Nb).' &
0x00FF0000 ) ^
($'.$e.(($Nb + $i - $c[2]) % $Nb).' &
0x0000FF00 ) ^
($'.$e.(($Nb + $i - $c[3]) % $Nb).' &
0x000000FF ) ^
'.$dw[$i]."\n";
}
$decrypt_block .= ');';
$lambda_functions[$code_hash] =
$this->_createInlineCryptFunction(
array(
'init_crypt' => '',
'init_encrypt' => $init_encrypt,
'init_decrypt' => $init_decrypt,
'encrypt_block' => $encrypt_block,
'decrypt_block' => $decrypt_block
)
);
}
$this->inline_crypt = $lambda_functions[$code_hash];
}
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php000064400000336717151156520650016222
0ustar00<?php
/**
* Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
*
* PHP version 5
*
* Here's an example of how to encrypt and decrypt text with this
library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $rsa = new \phpseclib\Crypt\RSA();
* extract($rsa->createKey());
*
* $plaintext = 'terrafrost';
*
* $rsa->loadKey($privatekey);
* $ciphertext = $rsa->encrypt($plaintext);
*
* $rsa->loadKey($publickey);
* echo $rsa->decrypt($ciphertext);
* ?>
* </code>
*
* Here's an example of how to create signatures and verify signatures
with this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $rsa = new \phpseclib\Crypt\RSA();
* extract($rsa->createKey());
*
* $plaintext = 'terrafrost';
*
* $rsa->loadKey($privatekey);
* $signature = $rsa->sign($plaintext);
*
* $rsa->loadKey($publickey);
* echo $rsa->verify($plaintext, $signature) ? 'verified' :
'unverified';
* ?>
* </code>
*
* @category Crypt
* @package RSA
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2009 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
use phpseclib\Math\BigInteger;
/**
* Pure-PHP PKCS#1 compliant implementation of RSA.
*
* @package RSA
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class RSA
{
/**#@+
* @access public
* @see self::encrypt()
* @see self::decrypt()
*/
/**
* Use {@link
http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal
Asymmetric Encryption Padding}
* (OAEP) for encryption / decryption.
*
* Uses sha1 by default.
*
* @see self::setHash()
* @see self::setMGFHash()
*/
const ENCRYPTION_OAEP = 1;
/**
* Use PKCS#1 padding.
*
* Although self::ENCRYPTION_OAEP offers more security, including
PKCS#1 padding is necessary for purposes of backwards
* compatibility with protocols (like SSH-1) written before OAEP's
introduction.
*/
const ENCRYPTION_PKCS1 = 2;
/**
* Do not use any padding
*
* Although this method is not recommended it can none-the-less
sometimes be useful if you're trying to decrypt some legacy
* stuff, if you're trying to diagnose why an encrypted message
isn't decrypting, etc.
*/
const ENCRYPTION_NONE = 3;
/**#@-*/
/**#@+
* @access public
* @see self::sign()
* @see self::verify()
* @see self::setHash()
*/
/**
* Use the Probabilistic Signature Scheme for signing
*
* Uses sha1 by default.
*
* @see self::setSaltLength()
* @see self::setMGFHash()
*/
const SIGNATURE_PSS = 1;
/**
* Use the PKCS#1 scheme by default.
*
* Although self::SIGNATURE_PSS offers more security, including PKCS#1
signing is necessary for purposes of backwards
* compatibility with protocols (like SSH-2) written before PSS's
introduction.
*/
const SIGNATURE_PKCS1 = 2;
/**#@-*/
/**#@+
* @access private
* @see \phpseclib\Crypt\RSA::createKey()
*/
/**
* ASN1 Integer
*/
const ASN1_INTEGER = 2;
/**
* ASN1 Bit String
*/
const ASN1_BITSTRING = 3;
/**
* ASN1 Octet String
*/
const ASN1_OCTETSTRING = 4;
/**
* ASN1 Object Identifier
*/
const ASN1_OBJECT = 6;
/**
* ASN1 Sequence (with the constucted bit set)
*/
const ASN1_SEQUENCE = 48;
/**#@-*/
/**#@+
* @access private
* @see \phpseclib\Crypt\RSA::__construct()
*/
/**
* To use the pure-PHP implementation
*/
const MODE_INTERNAL = 1;
/**
* To use the OpenSSL library
*
* (if enabled; otherwise, the internal implementation will be used)
*/
const MODE_OPENSSL = 2;
/**#@-*/
/**#@+
* @access public
* @see \phpseclib\Crypt\RSA::createKey()
* @see \phpseclib\Crypt\RSA::setPrivateKeyFormat()
*/
/**
* PKCS#1 formatted private key
*
* Used by OpenSSH
*/
const PRIVATE_FORMAT_PKCS1 = 0;
/**
* PuTTY formatted private key
*/
const PRIVATE_FORMAT_PUTTY = 1;
/**
* XML formatted private key
*/
const PRIVATE_FORMAT_XML = 2;
/**
* PKCS#8 formatted private key
*/
const PRIVATE_FORMAT_PKCS8 = 8;
/**
* OpenSSH formatted private key
*/
const PRIVATE_FORMAT_OPENSSH = 9;
/**#@-*/
/**#@+
* @access public
* @see \phpseclib\Crypt\RSA::createKey()
* @see \phpseclib\Crypt\RSA::setPublicKeyFormat()
*/
/**
* Raw public key
*
* An array containing two \phpseclib\Math\BigInteger objects.
*
* The exponent can be indexed with any of the following:
*
* 0, e, exponent, publicExponent
*
* The modulus can be indexed with any of the following:
*
* 1, n, modulo, modulus
*/
const PUBLIC_FORMAT_RAW = 3;
/**
* PKCS#1 formatted public key (raw)
*
* Used by File/X509.php
*
* Has the following header:
*
* -----BEGIN RSA PUBLIC KEY-----
*
* Analogous to ssh-keygen's pem format (as specified by -m)
*/
const PUBLIC_FORMAT_PKCS1 = 4;
const PUBLIC_FORMAT_PKCS1_RAW = 4;
/**
* XML formatted public key
*/
const PUBLIC_FORMAT_XML = 5;
/**
* OpenSSH formatted public key
*
* Place in $HOME/.ssh/authorized_keys
*/
const PUBLIC_FORMAT_OPENSSH = 6;
/**
* PKCS#1 formatted public key (encapsulated)
*
* 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)
*/
const PUBLIC_FORMAT_PKCS8 = 7;
/**#@-*/
/**
* Precomputed Zero
*
* @var \phpseclib\Math\BigInteger
* @access private
*/
var $zero;
/**
* Precomputed One
*
* @var \phpseclib\Math\BigInteger
* @access private
*/
var $one;
/**
* Private Key Format
*
* @var int
* @access private
*/
var $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1;
/**
* Public Key Format
*
* @var int
* @access public
*/
var $publicKeyFormat = self::PUBLIC_FORMAT_PKCS8;
/**
* Modulus (ie. n)
*
* @var \phpseclib\Math\BigInteger
* @access private
*/
var $modulus;
/**
* Modulus length
*
* @var \phpseclib\Math\BigInteger
* @access private
*/
var $k;
/**
* Exponent (ie. e or d)
*
* @var \phpseclib\Math\BigInteger
* @access private
*/
var $exponent;
/**
* Primes for Chinese Remainder Theorem (ie. p and q)
*
* @var array
* @access private
*/
var $primes;
/**
* Exponents for Chinese Remainder Theorem (ie. dP and dQ)
*
* @var array
* @access private
*/
var $exponents;
/**
* Coefficients for Chinese Remainder Theorem (ie. qInv)
*
* @var array
* @access private
*/
var $coefficients;
/**
* Hash name
*
* @var string
* @access private
*/
var $hashName;
/**
* Hash function
*
* @var \phpseclib\Crypt\Hash
* @access private
*/
var $hash;
/**
* Length of hash function output
*
* @var int
* @access private
*/
var $hLen;
/**
* Length of salt
*
* @var int
* @access private
*/
var $sLen;
/**
* Hash function for the Mask Generation Function
*
* @var \phpseclib\Crypt\Hash
* @access private
*/
var $mgfHash;
/**
* Length of MGF hash function output
*
* @var int
* @access private
*/
var $mgfHLen;
/**
* Encryption mode
*
* @var int
* @access private
*/
var $encryptionMode = self::ENCRYPTION_OAEP;
/**
* Signature mode
*
* @var int
* @access private
*/
var $signatureMode = self::SIGNATURE_PSS;
/**
* Public Exponent
*
* @var mixed
* @access private
*/
var $publicExponent = false;
/**
* Password
*
* @var string
* @access private
*/
var $password = false;
/**
* Components
*
* For use with parsing XML formatted keys. PHP's XML Parser
functions use utilized - instead of PHP's DOM functions -
* because PHP's XML Parser functions work on PHP4 whereas
PHP's DOM functions - although surperior - don't.
*
* @see self::_start_element_handler()
* @var array
* @access private
*/
var $components = array();
/**
* Current String
*
* For use with parsing XML formatted keys.
*
* @see self::_character_handler()
* @see self::_stop_element_handler()
* @var mixed
* @access private
*/
var $current;
/**
* OpenSSL configuration file name.
*
* Set to null to use system configuration file.
* @see self::createKey()
* @var mixed
* @Access public
*/
var $configFile;
/**
* Public key comment field.
*
* @var string
* @access private
*/
var $comment = 'phpseclib-generated-key';
/**
* The constructor
*
* If you want to make use of the openssl extension, you'll need
to set the mode manually, yourself. The reason
* \phpseclib\Crypt\RSA doesn't do it is because OpenSSL
doesn't fail gracefully. openssl_pkey_new(), in particular, requires
* openssl.cnf be present somewhere and, unfortunately, the only real
way to find out is too late.
*
* @return \phpseclib\Crypt\RSA
* @access public
*/
function __construct()
{
$this->configFile = dirname(__FILE__) .
'/../openssl.cnf';
if (!defined('CRYPT_RSA_MODE')) {
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',
self::MODE_INTERNAL);
break;
case extension_loaded('openssl') &&
file_exists($this->configFile):
// some versions of XAMPP have mismatched versions of
OpenSSL which causes it not to work
$versions = array();
// avoid generating errors (even with suppression) when
phpinfo() is disabled (common in production systems)
if (strpos(ini_get('disable_functions'),
'phpinfo') === false) {
ob_start();
@phpinfo();
$content = ob_get_contents();
ob_end_clean();
preg_match_all('#OpenSSL (Header|Library)
Version(.*)#im', $content, $matches);
if (!empty($matches[1])) {
for ($i = 0; $i < count($matches[1]); $i++)
{
$fullVersion =
trim(str_replace('=>', '',
strip_tags($matches[2][$i])));
// Remove letter part in OpenSSL version
if
(!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
$versions[$matches[1][$i]] =
$fullVersion;
} else {
$versions[$matches[1][$i]] = $m[0];
}
}
}
}
// 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']:
case version_compare($versions['Header'],
'1.0.0') >= 0 &&
version_compare($versions['Library'], '1.0.0') >= 0:
define('CRYPT_RSA_MODE',
self::MODE_OPENSSL);
break;
default:
define('CRYPT_RSA_MODE',
self::MODE_INTERNAL);
define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
}
break;
default:
define('CRYPT_RSA_MODE',
self::MODE_INTERNAL);
}
}
$this->zero = new BigInteger();
$this->one = new BigInteger(1);
$this->hash = new Hash('sha1');
$this->hLen = $this->hash->getLength();
$this->hashName = 'sha1';
$this->mgfHash = new Hash('sha1');
$this->mgfHLen = $this->mgfHash->getLength();
}
/**
* Create public / private key pair
*
* Returns an array with the following three elements:
* - 'privatekey': The private key.
* - 'publickey': The public key.
* - 'partialkey': A partially computed key (if the
execution time exceeded $timeout).
* Will need to be passed back to
\phpseclib\Crypt\RSA::createKey() as the third parameter for further
processing.
*
* @access public
* @param int $bits
* @param int $timeout
* @param array $partial
*/
function createKey($bits = 1024, $timeout = false, $partial = array())
{
if (!defined('CRYPT_RSA_EXPONENT')) {
// http://en.wikipedia.org/wiki/65537_%28number%29
define('CRYPT_RSA_EXPONENT', '65537');
}
// per
<http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number
ought not result in primes smaller
// than 256 bits. as a consequence if the key you're trying to
create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME
// to 384 bits then you're going to get a 384 bit prime and a
640 bit prime (384 + 1024 % 384). at least if
// CRYPT_RSA_MODE is set to self::MODE_INTERNAL. if CRYPT_RSA_MODE
is set to self::MODE_OPENSSL then
// CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support
is more intended as a way to speed up RSA key
// generation when there's a chance neither gmp nor OpenSSL
are installed)
if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
define('CRYPT_RSA_SMALLEST_PRIME', 4096);
}
// OpenSSL uses 65537 as the exponent and requires RSA keys be 384
bits minimum
if (CRYPT_RSA_MODE == self::MODE_OPENSSL && $bits >= 384
&& CRYPT_RSA_EXPONENT == 65537) {
$config = array();
if (isset($this->configFile)) {
$config['config'] = $this->configFile;
}
$rsa = openssl_pkey_new(array('private_key_bits'
=> $bits) + $config);
openssl_pkey_export($rsa, $privatekey, null, $config);
$publickey = openssl_pkey_get_details($rsa);
$publickey = $publickey['key'];
$privatekey = call_user_func_array(array($this,
'_convertPrivateKey'),
array_values($this->_parseKey($privatekey,
self::PRIVATE_FORMAT_PKCS1)));
$publickey = call_user_func_array(array($this,
'_convertPublicKey'),
array_values($this->_parseKey($publickey, self::PUBLIC_FORMAT_PKCS1)));
// clear the buffer of error strings stemming from a
minimalistic openssl.cnf
while (openssl_error_string() !== false) {
}
return array(
'privatekey' => $privatekey,
'publickey' => $publickey,
'partialkey' => false
);
}
static $e;
if (!isset($e)) {
$e = new BigInteger(CRYPT_RSA_EXPONENT);
}
extract($this->_generateMinMax($bits));
$absoluteMin = $min;
$temp = $bits >> 1; // divide by two to see how many bits P
and Q would be
if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
$num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
$temp = CRYPT_RSA_SMALLEST_PRIME;
} else {
$num_primes = 2;
}
extract($this->_generateMinMax($temp + $bits % $temp));
$finalMax = $max;
extract($this->_generateMinMax($temp));
$generator = new BigInteger();
$n = $this->one->copy();
if (!empty($partial)) {
extract(unserialize($partial));
} else {
$exponents = $coefficients = $primes = array();
$lcm = array(
'top' => $this->one->copy(),
'bottom' => false
);
}
$start = time();
$i0 = count($primes) + 1;
do {
for ($i = $i0; $i <= $num_primes; $i++) {
if ($timeout !== false) {
$timeout-= time() - $start;
$start = time();
if ($timeout <= 0) {
return array(
'privatekey' => '',
'publickey' => '',
'partialkey' => serialize(array(
'primes' => $primes,
'coefficients' =>
$coefficients,
'lcm' => $lcm,
'exponents' => $exponents
))
);
}
}
if ($i == $num_primes) {
list($min, $temp) = $absoluteMin->divide($n);
if (!$temp->equals($this->zero)) {
$min = $min->add($this->one); // ie. ceil()
}
$primes[$i] = $generator->randomPrime($min,
$finalMax, $timeout);
} else {
$primes[$i] = $generator->randomPrime($min, $max,
$timeout);
}
if ($primes[$i] === false) { // if we've reached the
timeout
if (count($primes) > 1) {
$partialkey = '';
} else {
array_pop($primes);
$partialkey = serialize(array(
'primes' => $primes,
'coefficients' => $coefficients,
'lcm' => $lcm,
'exponents' => $exponents
));
}
return array(
'privatekey' => '',
'publickey' => '',
'partialkey' => $partialkey
);
}
// the first coefficient is calculated differently from the
rest
// ie. instead of being
$primes[1]->modInverse($primes[2]), it's
$primes[2]->modInverse($primes[1])
if ($i > 2) {
$coefficients[$i] = $n->modInverse($primes[$i]);
}
$n = $n->multiply($primes[$i]);
$temp = $primes[$i]->subtract($this->one);
// textbook RSA implementations use Euler's totient
function instead of the least common multiple.
// see
http://en.wikipedia.org/wiki/Euler%27s_totient_function
$lcm['top'] =
$lcm['top']->multiply($temp);
$lcm['bottom'] = $lcm['bottom'] ===
false ? $temp : $lcm['bottom']->gcd($temp);
$exponents[$i] = $e->modInverse($temp);
}
list($temp) =
$lcm['top']->divide($lcm['bottom']);
$gcd = $temp->gcd($e);
$i0 = 1;
} while (!$gcd->equals($this->one));
$d = $e->modInverse($temp);
$coefficients[2] = $primes[2]->modInverse($primes[1]);
// from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
// RSAPrivateKey ::= SEQUENCE {
// version Version,
// modulus INTEGER, -- n
// publicExponent INTEGER, -- e
// privateExponent INTEGER, -- d
// prime1 INTEGER, -- p
// prime2 INTEGER, -- q
// exponent1 INTEGER, -- d mod (p-1)
// exponent2 INTEGER, -- d mod (q-1)
// coefficient INTEGER, -- (inverse of q) mod p
// otherPrimeInfos OtherPrimeInfos OPTIONAL
// }
return array(
'privatekey' => $this->_convertPrivateKey($n,
$e, $d, $primes, $exponents, $coefficients),
'publickey' => $this->_convertPublicKey($n,
$e),
'partialkey' => false
);
}
/**
* Convert a private key to the appropriate format.
*
* @access private
* @see self::setPrivateKeyFormat()
* @param Math_BigInteger $n
* @param Math_BigInteger $e
* @param Math_BigInteger $d
* @param array<int,Math_BigInteger> $primes
* @param array<int,Math_BigInteger> $exponents
* @param array<int,Math_BigInteger> $coefficients
* @return string
*/
function _convertPrivateKey($n, $e, $d, $primes, $exponents,
$coefficients)
{
$signed = $this->privateKeyFormat != self::PRIVATE_FORMAT_XML;
$num_primes = count($primes);
$raw = array(
'version' => $num_primes == 2 ? chr(0) : chr(1),
// two-prime vs. multi
'modulus' => $n->toBytes($signed),
'publicExponent' => $e->toBytes($signed),
'privateExponent' => $d->toBytes($signed),
'prime1' => $primes[1]->toBytes($signed),
'prime2' => $primes[2]->toBytes($signed),
'exponent1' => $exponents[1]->toBytes($signed),
'exponent2' => $exponents[2]->toBytes($signed),
'coefficient' =>
$coefficients[2]->toBytes($signed)
);
// if the format in question does not support multi-prime rsa and
multi-prime rsa was used,
// call _convertPublicKey() instead.
switch ($this->privateKeyFormat) {
case self::PRIVATE_FORMAT_XML:
if ($num_primes != 2) {
return false;
}
return "<RSAKeyValue>\r\n" .
' <Modulus>' .
base64_encode($raw['modulus']) . "</Modulus>\r\n"
.
' <Exponent>' .
base64_encode($raw['publicExponent']) .
"</Exponent>\r\n" .
' <P>' .
base64_encode($raw['prime1']) . "</P>\r\n" .
' <Q>' .
base64_encode($raw['prime2']) . "</Q>\r\n" .
' <DP>' .
base64_encode($raw['exponent1']) . "</DP>\r\n" .
' <DQ>' .
base64_encode($raw['exponent2']) . "</DQ>\r\n" .
' <InverseQ>' .
base64_encode($raw['coefficient']) .
"</InverseQ>\r\n" .
' <D>' .
base64_encode($raw['privateExponent']) .
"</D>\r\n" .
'</RSAKeyValue>';
break;
case self::PRIVATE_FORMAT_PUTTY:
if ($num_primes != 2) {
return false;
}
$key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption:
";
$encryption = (!empty($this->password) ||
is_string($this->password)) ? 'aes256-cbc' : 'none';
$key.= $encryption;
$key.= "\r\nComment: " . $this->comment .
"\r\n";
$public = pack(
'Na*Na*Na*',
strlen('ssh-rsa'),
'ssh-rsa',
strlen($raw['publicExponent']),
$raw['publicExponent'],
strlen($raw['modulus']),
$raw['modulus']
);
$source = pack(
'Na*Na*Na*Na*',
strlen('ssh-rsa'),
'ssh-rsa',
strlen($encryption),
$encryption,
strlen($this->comment),
$this->comment,
strlen($public),
$public
);
$public = base64_encode($public);
$key.= "Public-Lines: " . ((strlen($public) + 63)
>> 6) . "\r\n";
$key.= chunk_split($public, 64);
$private = pack(
'Na*Na*Na*Na*',
strlen($raw['privateExponent']),
$raw['privateExponent'],
strlen($raw['prime1']),
$raw['prime1'],
strlen($raw['prime2']),
$raw['prime2'],
strlen($raw['coefficient']),
$raw['coefficient']
);
if (empty($this->password) &&
!is_string($this->password)) {
$source.= pack('Na*', strlen($private),
$private);
$hashkey = 'putty-private-key-file-mac-key';
} else {
$private.= Random::string(16 - (strlen($private) &
15));
$source.= pack('Na*', strlen($private),
$private);
$sequence = 0;
$symkey = '';
while (strlen($symkey) < 32) {
$temp = pack('Na*', $sequence++,
$this->password);
$symkey.= pack('H*', sha1($temp));
}
$symkey = substr($symkey, 0, 32);
$crypto = new AES();
$crypto->setKey($symkey);
$crypto->disablePadding();
$private = $crypto->encrypt($private);
$hashkey = 'putty-private-key-file-mac-key' .
$this->password;
}
$private = base64_encode($private);
$key.= 'Private-Lines: ' . ((strlen($private) +
63) >> 6) . "\r\n";
$key.= chunk_split($private, 64);
$hash = new Hash('sha1');
$hash->setKey(pack('H*', sha1($hashkey)));
$key.= 'Private-MAC: ' .
bin2hex($hash->hash($source)) . "\r\n";
return $key;
case self::PRIVATE_FORMAT_OPENSSH:
if ($num_primes != 2) {
return false;
}
$publicKey = pack('Na*Na*Na*',
strlen('ssh-rsa'), 'ssh-rsa',
strlen($raw['publicExponent']), $raw['publicExponent'],
strlen($raw['modulus']), $raw['modulus']);
$privateKey = pack(
'Na*Na*Na*Na*Na*Na*Na*',
strlen('ssh-rsa'),
'ssh-rsa',
strlen($raw['modulus']),
$raw['modulus'],
strlen($raw['publicExponent']),
$raw['publicExponent'],
strlen($raw['privateExponent']),
$raw['privateExponent'],
strlen($raw['coefficient']),
$raw['coefficient'],
strlen($raw['prime1']),
$raw['prime1'],
strlen($raw['prime2']),
$raw['prime2']
);
$checkint = Random::string(4);
$paddedKey = pack(
'a*Na*',
$checkint . $checkint . $privateKey,
strlen($this->comment),
$this->comment
);
$paddingLength = (7 * strlen($paddedKey)) % 8;
for ($i = 1; $i <= $paddingLength; $i++) {
$paddedKey.= chr($i);
}
$key = pack(
'Na*Na*Na*NNa*Na*',
strlen('none'),
'none',
strlen('none'),
'none',
0,
'',
1,
strlen($publicKey),
$publicKey,
strlen($paddedKey),
$paddedKey
);
$key = "openssh-key-v1\0$key";
return "-----BEGIN OPENSSH PRIVATE KEY-----\r\n"
.
chunk_split(base64_encode($key), 70) .
"-----END OPENSSH PRIVATE KEY-----";
default: // eg. self::PRIVATE_FORMAT_PKCS1
$components = array();
foreach ($raw as $name => $value) {
$components[$name] = pack('Ca*a*',
self::ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
}
$RSAPrivateKey = implode('', $components);
if ($num_primes > 2) {
$OtherPrimeInfos = '';
for ($i = 3; $i <= $num_primes; $i++) {
// OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF
OtherPrimeInfo
//
// OtherPrimeInfo ::= SEQUENCE {
// prime INTEGER, -- ri
// exponent INTEGER, -- di
// coefficient INTEGER -- ti
// }
$OtherPrimeInfo = pack('Ca*a*',
self::ASN1_INTEGER,
$this->_encodeLength(strlen($primes[$i]->toBytes(true))),
$primes[$i]->toBytes(true));
$OtherPrimeInfo.= pack('Ca*a*',
self::ASN1_INTEGER,
$this->_encodeLength(strlen($exponents[$i]->toBytes(true))),
$exponents[$i]->toBytes(true));
$OtherPrimeInfo.= pack('Ca*a*',
self::ASN1_INTEGER,
$this->_encodeLength(strlen($coefficients[$i]->toBytes(true))),
$coefficients[$i]->toBytes(true));
$OtherPrimeInfos.= pack('Ca*a*',
self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)),
$OtherPrimeInfo);
}
$RSAPrivateKey.= pack('Ca*a*',
self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)),
$OtherPrimeInfos);
}
$RSAPrivateKey = pack('Ca*a*',
self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)),
$RSAPrivateKey);
if ($this->privateKeyFormat ==
self::PRIVATE_FORMAT_PKCS8) {
$rsaOID = pack('H*',
'300d06092a864886f70d0101010500'); // hex version of
MA0GCSqGSIb3DQEBAQUA
$RSAPrivateKey = pack(
'Ca*a*Ca*a*',
self::ASN1_INTEGER,
"\01\00",
$rsaOID,
4,
$this->_encodeLength(strlen($RSAPrivateKey)),
$RSAPrivateKey
);
$RSAPrivateKey = pack('Ca*a*',
self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)),
$RSAPrivateKey);
if (!empty($this->password) ||
is_string($this->password)) {
$salt = Random::string(8);
$iterationCount = 2048;
$crypto = new DES();
$crypto->setPassword($this->password,
'pbkdf1', 'md5', $salt, $iterationCount);
$RSAPrivateKey =
$crypto->encrypt($RSAPrivateKey);
$parameters = pack(
'Ca*a*Ca*N',
self::ASN1_OCTETSTRING,
$this->_encodeLength(strlen($salt)),
$salt,
self::ASN1_INTEGER,
$this->_encodeLength(4),
$iterationCount
);
$pbeWithMD5AndDES_CBC =
"\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";
$encryptionAlgorithm = pack(
'Ca*a*Ca*a*',
self::ASN1_OBJECT,
$this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)),
$pbeWithMD5AndDES_CBC,
self::ASN1_SEQUENCE,
$this->_encodeLength(strlen($parameters)),
$parameters
);
$RSAPrivateKey = pack(
'Ca*a*Ca*a*',
self::ASN1_SEQUENCE,
$this->_encodeLength(strlen($encryptionAlgorithm)),
$encryptionAlgorithm,
self::ASN1_OCTETSTRING,
$this->_encodeLength(strlen($RSAPrivateKey)),
$RSAPrivateKey
);
$RSAPrivateKey = pack('Ca*a*',
self::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)) {
$iv = Random::string(8);
$symkey = pack('H*', md5($this->password .
$iv)); // symkey is short for symmetric key
$symkey.= substr(pack('H*', md5($symkey .
$this->password . $iv)), 0, 8);
$des = new TripleDES();
$des->setKey($symkey);
$des->setIV($iv);
$iv = strtoupper(bin2hex($iv));
$RSAPrivateKey = "-----BEGIN RSA PRIVATE
KEY-----\r\n" .
"Proc-Type: 4,ENCRYPTED\r\n"
.
"DEK-Info:
DES-EDE3-CBC,$iv\r\n" .
"\r\n" .
chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
'-----END RSA PRIVATE
KEY-----';
} else {
$RSAPrivateKey = "-----BEGIN RSA PRIVATE
KEY-----\r\n" .
chunk_split(base64_encode($RSAPrivateKey), 64) .
'-----END RSA PRIVATE
KEY-----';
}
return $RSAPrivateKey;
}
}
/**
* Convert a public key to the appropriate format
*
* @access private
* @see self::setPublicKeyFormat()
* @param Math_BigInteger $n
* @param Math_BigInteger $e
* @return string|array<string,Math_BigInteger>
*/
function _convertPublicKey($n, $e)
{
$signed = $this->publicKeyFormat != self::PUBLIC_FORMAT_XML;
$modulus = $n->toBytes($signed);
$publicExponent = $e->toBytes($signed);
switch ($this->publicKeyFormat) {
case self::PUBLIC_FORMAT_RAW:
return array('e' => $e->copy(),
'n' => $n->copy());
case self::PUBLIC_FORMAT_XML:
return "<RSAKeyValue>\r\n" .
' <Modulus>' .
base64_encode($modulus) . "</Modulus>\r\n" .
' <Exponent>' .
base64_encode($publicExponent) . "</Exponent>\r\n" .
'</RSAKeyValue>';
break;
case self::PUBLIC_FORMAT_OPENSSH:
// from <http://tools.ietf.org/html/rfc4253#page-15>:
// string "ssh-rsa"
// mpint e
// mpint n
$RSAPublicKey = pack('Na*Na*Na*',
strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent),
$publicExponent, strlen($modulus), $modulus);
$RSAPublicKey = 'ssh-rsa ' .
base64_encode($RSAPublicKey) . ' ' . $this->comment;
return $RSAPublicKey;
default: // eg. self::PUBLIC_FORMAT_PKCS1_RAW or
self::PUBLIC_FORMAT_PKCS1
// from
<http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
// RSAPublicKey ::= SEQUENCE {
// modulus INTEGER, -- n
// publicExponent INTEGER -- e
// }
$components = array(
'modulus' => pack('Ca*a*',
self::ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
'publicExponent' =>
pack('Ca*a*', self::ASN1_INTEGER,
$this->_encodeLength(strlen($publicExponent)), $publicExponent)
);
$RSAPublicKey = pack(
'Ca*a*a*',
self::ASN1_SEQUENCE,
$this->_encodeLength(strlen($components['modulus']) +
strlen($components['publicExponent'])),
$components['modulus'],
$components['publicExponent']
);
if ($this->publicKeyFormat ==
self::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.
$rsaOID = pack('H*',
'300d06092a864886f70d0101010500'); // hex version of
MA0GCSqGSIb3DQEBAQUA
$RSAPublicKey = chr(0) . $RSAPublicKey;
$RSAPublicKey = chr(3) .
$this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
$RSAPublicKey = pack(
'Ca*a*',
self::ASN1_SEQUENCE,
$this->_encodeLength(strlen($rsaOID .
$RSAPublicKey)),
$rsaOID . $RSAPublicKey
);
$RSAPublicKey = "-----BEGIN PUBLIC
KEY-----\r\n" .
chunk_split(base64_encode($RSAPublicKey), 64) .
'-----END PUBLIC KEY-----';
}
return $RSAPublicKey;
}
}
/**
* Break a public or private key down into its constituant components
*
* @access private
* @see self::_convertPublicKey()
* @see self::_convertPrivateKey()
* @param string|array $key
* @param int $type
* @return array|bool
*/
function _parseKey($key, $type)
{
if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) {
return false;
}
switch ($type) {
case self::PUBLIC_FORMAT_RAW:
if (!is_array($key)) {
return false;
}
$components = array();
switch (true) {
case isset($key['e']):
$components['publicExponent'] =
$key['e']->copy();
break;
case isset($key['exponent']):
$components['publicExponent'] =
$key['exponent']->copy();
break;
case isset($key['publicExponent']):
$components['publicExponent'] =
$key['publicExponent']->copy();
break;
case isset($key[0]):
$components['publicExponent'] =
$key[0]->copy();
}
switch (true) {
case isset($key['n']):
$components['modulus'] =
$key['n']->copy();
break;
case isset($key['modulo']):
$components['modulus'] =
$key['modulo']->copy();
break;
case isset($key['modulus']):
$components['modulus'] =
$key['modulus']->copy();
break;
case isset($key[1]):
$components['modulus'] =
$key[1]->copy();
}
return isset($components['modulus']) &&
isset($components['publicExponent']) ? $components : false;
case self::PRIVATE_FORMAT_PKCS1:
case self::PRIVATE_FORMAT_PKCS8:
case self::PUBLIC_FORMAT_PKCS1:
/* 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
protect private keys, however, that's not what
OpenSSL* does. OpenSSL protects private keys by adding
two new "fields" to the key - DEK-Info and
Proc-Type. These fields are discussed here:
http://tools.ietf.org/html/rfc1421#section-4.6.1.1
http://tools.ietf.org/html/rfc1421#section-4.6.1.3
DES-EDE3-CBC as an algorithm, however, is not discussed
anywhere, near as I can tell.
DES-CBC and DES-EDE are discussed in RFC1423, however,
DES-EDE3-CBC isn't, nor is its key derivation
function. As is, the definitive authority on this
encoding scheme isn't the IETF but rather OpenSSL's
own implementation. ie. the implementation *is* the
standard and any bugs that may exist in that
implementation are part of the standard, as well.
* OpenSSL is the de facto standard. It's utilized
by OpenSSH and other projects */
if (preg_match('#DEK-Info: (.+),(.+)#', $key,
$matches)) {
$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($symkey .
$this->password . substr($iv, 0, 8)));
// remove the Proc-Type / DEK-Info sections as
they're no longer needed
$key = preg_replace('#^(?:Proc-Type|DEK-Info):
.*#m', '', $key);
$ciphertext = $this->_extractBER($key);
if ($ciphertext === false) {
$ciphertext = $key;
}
switch ($matches[1]) {
case 'AES-256-CBC':
$crypto = new AES();
break;
case 'AES-128-CBC':
$symkey = substr($symkey, 0, 16);
$crypto = new AES();
break;
case 'DES-EDE3-CFB':
$crypto = new TripleDES(Base::MODE_CFB);
break;
case 'DES-EDE3-CBC':
$symkey = substr($symkey, 0, 24);
$crypto = new TripleDES();
break;
case 'DES-CBC':
$crypto = new DES();
break;
default:
return false;
}
$crypto->setKey($symkey);
$crypto->setIV($iv);
$decoded = $crypto->decrypt($ciphertext);
} else {
$decoded = $this->_extractBER($key);
}
if ($decoded !== false) {
$key = $decoded;
}
$components = array();
if (ord($this->_string_shift($key)) !=
self::ASN1_SEQUENCE) {
return false;
}
if ($this->_decodeLength($key) != strlen($key)) {
return false;
}
$tag = ord($this->_string_shift($key));
/* intended for keys for which OpenSSL's asn1parse
returns the following:
0:d=0 hl=4 l= 631 cons: SEQUENCE
4:d=1 hl=2 l= 1 prim: INTEGER :00
7:d=1 hl=2 l= 13 cons: SEQUENCE
9:d=2 hl=2 l= 9 prim: OBJECT
:rsaEncryption
20:d=2 hl=2 l= 0 prim: NULL
22:d=1 hl=4 l= 609 prim: OCTET STRING
ie. PKCS8 keys*/
if ($tag == self::ASN1_INTEGER && substr($key, 0,
3) == "\x01\x00\x30") {
$this->_string_shift($key, 3);
$tag = self::ASN1_SEQUENCE;
}
if ($tag == self::ASN1_SEQUENCE) {
$temp = $this->_string_shift($key,
$this->_decodeLength($key));
if (ord($this->_string_shift($temp)) !=
self::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
case
"\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0A": // rsaPSS
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)) !=
self::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)) !=
self::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;
}
$crypto = new DES();
$crypto->setPassword($this->password,
'pbkdf1', 'md5', $salt, $iterationCount);
$key = $crypto->decrypt($key);
if ($key === false) {
return false;
}
return $this->_parseKey($key,
self::PRIVATE_FORMAT_PKCS1);
default:
return false;
}
/* intended for keys for which OpenSSL's asn1parse
returns the following:
0:d=0 hl=4 l= 290 cons: SEQUENCE
4:d=1 hl=2 l= 13 cons: SEQUENCE
6:d=2 hl=2 l= 9 prim: OBJECT
:rsaEncryption
17:d=2 hl=2 l= 0 prim: NULL
19:d=1 hl=4 l= 271 prim: BIT STRING */
$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
// "The initial octet shall encode, as an unsigned
binary integer wtih bit 1 as the least significant bit, the number of
// unused bits in the final subsequent octet. The
number shall be in the range zero to seven."
// --
http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
(section 8.6.2.2)
if ($tag == self::ASN1_BITSTRING) {
$this->_string_shift($key);
}
if (ord($this->_string_shift($key)) !=
self::ASN1_SEQUENCE) {
return false;
}
if ($this->_decodeLength($key) != strlen($key)) {
return false;
}
$tag = ord($this->_string_shift($key));
}
if ($tag != self::ASN1_INTEGER) {
return false;
}
$length = $this->_decodeLength($key);
$temp = $this->_string_shift($key, $length);
if (strlen($temp) != 1 || ord($temp) > 2) {
$components['modulus'] = new
BigInteger($temp, 256);
$this->_string_shift($key); // skip over
self::ASN1_INTEGER
$length = $this->_decodeLength($key);
$components[$type == self::PUBLIC_FORMAT_PKCS1 ?
'publicExponent' : 'privateExponent'] = new
BigInteger($this->_string_shift($key, $length), 256);
return $components;
}
if (ord($this->_string_shift($key)) !=
self::ASN1_INTEGER) {
return false;
}
$length = $this->_decodeLength($key);
$components['modulus'] = new
BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['publicExponent'] = new
BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['privateExponent'] = new
BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['primes'] = array(1 => new
BigInteger($this->_string_shift($key, $length), 256));
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['primes'][] = new
BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['exponents'] = array(1 => new
BigInteger($this->_string_shift($key, $length), 256));
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['exponents'][] = new
BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['coefficients'] = array(2 => new
BigInteger($this->_string_shift($key, $length), 256));
if (!empty($key)) {
if (ord($this->_string_shift($key)) !=
self::ASN1_SEQUENCE) {
return false;
}
$this->_decodeLength($key);
while (!empty($key)) {
if (ord($this->_string_shift($key)) !=
self::ASN1_SEQUENCE) {
return false;
}
$this->_decodeLength($key);
$key = substr($key, 1);
$length = $this->_decodeLength($key);
$components['primes'][] = new
BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['exponents'][] = new
BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['coefficients'][] = new
BigInteger($this->_string_shift($key, $length), 256);
}
}
return $components;
case self::PUBLIC_FORMAT_OPENSSH:
$parts = explode(' ', $key, 3);
$key = isset($parts[1]) ? base64_decode($parts[1]) : false;
if ($key === false) {
return false;
}
$comment = isset($parts[2]) ? $parts[2] : false;
$cleanup = substr($key, 0, 11) ==
"\0\0\0\7ssh-rsa";
if (strlen($key) <= 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($key, 4)));
$publicExponent = new
BigInteger($this->_string_shift($key, $length), -256);
if (strlen($key) <= 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($key, 4)));
$modulus = new BigInteger($this->_string_shift($key,
$length), -256);
if ($cleanup && strlen($key)) {
if (strlen($key) <= 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($key, 4)));
$realModulus = new
BigInteger($this->_string_shift($key, $length), -256);
return strlen($key) ? false : array(
'modulus' => $realModulus,
'publicExponent' => $modulus,
'comment' => $comment
);
} else {
return strlen($key) ? false : array(
'modulus' => $modulus,
'publicExponent' => $publicExponent,
'comment' => $comment
);
}
// http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
// http://en.wikipedia.org/wiki/XML_Signature
case self::PRIVATE_FORMAT_XML:
case self::PUBLIC_FORMAT_XML:
$this->components = array();
$xml = xml_parser_create('UTF-8');
xml_set_object($xml, $this);
xml_set_element_handler($xml,
'_start_element_handler', '_stop_element_handler');
xml_set_character_data_handler($xml,
'_data_handler');
// add <xml></xml> to account for
"dangling" tags like <BitStrength>...</BitStrength>
that are sometimes added
if (!xml_parse($xml, '<xml>' . $key .
'</xml>')) {
xml_parser_free($xml);
unset($xml);
return false;
}
xml_parser_free($xml);
unset($xml);
return isset($this->components['modulus'])
&& isset($this->components['publicExponent']) ?
$this->components : false;
// from PuTTY's SSHPUBK.C
case self::PRIVATE_FORMAT_PUTTY:
$components = array();
$key = preg_split('#\r\n|\r|\n#', $key);
$type = trim(preg_replace('#PuTTY-User-Key-File-2:
(.+)#', '$1', $key[0]));
if ($type != 'ssh-rsa') {
return false;
}
$encryption = trim(preg_replace('#Encryption:
(.+)#', '$1', $key[1]));
$comment = trim(preg_replace('#Comment: (.+)#',
'$1', $key[2]));
$publicLength = trim(preg_replace('#Public-Lines:
(\d+)#', '$1', $key[3]));
$public = base64_decode(implode('',
array_map('trim', array_slice($key, 4, $publicLength))));
$public = substr($public, 11);
extract(unpack('Nlength',
$this->_string_shift($public, 4)));
$components['publicExponent'] = new
BigInteger($this->_string_shift($public, $length), -256);
extract(unpack('Nlength',
$this->_string_shift($public, 4)));
$components['modulus'] = new
BigInteger($this->_string_shift($public, $length), -256);
$privateLength = trim(preg_replace('#Private-Lines:
(\d+)#', '$1', $key[$publicLength + 4]));
$private = base64_decode(implode('',
array_map('trim', array_slice($key, $publicLength + 5,
$privateLength))));
switch ($encryption) {
case 'aes256-cbc':
$symkey = '';
$sequence = 0;
while (strlen($symkey) < 32) {
$temp = pack('Na*', $sequence++,
$this->password);
$symkey.= pack('H*', sha1($temp));
}
$symkey = substr($symkey, 0, 32);
$crypto = new AES();
}
if ($encryption != 'none') {
$crypto->setKey($symkey);
$crypto->disablePadding();
$private = $crypto->decrypt($private);
if ($private === false) {
return false;
}
}
extract(unpack('Nlength',
$this->_string_shift($private, 4)));
if (strlen($private) < $length) {
return false;
}
$components['privateExponent'] = new
BigInteger($this->_string_shift($private, $length), -256);
extract(unpack('Nlength',
$this->_string_shift($private, 4)));
if (strlen($private) < $length) {
return false;
}
$components['primes'] = array(1 => new
BigInteger($this->_string_shift($private, $length), -256));
extract(unpack('Nlength',
$this->_string_shift($private, 4)));
if (strlen($private) < $length) {
return false;
}
$components['primes'][] = new
BigInteger($this->_string_shift($private, $length), -256);
$temp =
$components['primes'][1]->subtract($this->one);
$components['exponents'] = array(1 =>
$components['publicExponent']->modInverse($temp));
$temp =
$components['primes'][2]->subtract($this->one);
$components['exponents'][] =
$components['publicExponent']->modInverse($temp);
extract(unpack('Nlength',
$this->_string_shift($private, 4)));
if (strlen($private) < $length) {
return false;
}
$components['coefficients'] = array(2 => new
BigInteger($this->_string_shift($private, $length), -256));
return $components;
case self::PRIVATE_FORMAT_OPENSSH:
$components = array();
$decoded = $this->_extractBER($key);
$magic = $this->_string_shift($decoded, 15);
if ($magic !== "openssh-key-v1\0") {
return false;
}
$options = $this->_string_shift($decoded, 24);
// \0\0\0\4none = ciphername
// \0\0\0\4none = kdfname
// \0\0\0\0 = kdfoptions
// \0\0\0\1 = numkeys
if ($options !=
"\0\0\0\4none\0\0\0\4none\0\0\0\0\0\0\0\1") {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($decoded, 4)));
if (strlen($decoded) < $length) {
return false;
}
$publicKey = $this->_string_shift($decoded, $length);
extract(unpack('Nlength',
$this->_string_shift($decoded, 4)));
if (strlen($decoded) < $length) {
return false;
}
$paddedKey = $this->_string_shift($decoded, $length);
if ($this->_string_shift($publicKey, 11) !==
"\0\0\0\7ssh-rsa") {
return false;
}
$checkint1 = $this->_string_shift($paddedKey, 4);
$checkint2 = $this->_string_shift($paddedKey, 4);
if (strlen($checkint1) != 4 || $checkint1 !== $checkint2) {
return false;
}
if ($this->_string_shift($paddedKey, 11) !==
"\0\0\0\7ssh-rsa") {
return false;
}
$values = array(
&$components['modulus'],
&$components['publicExponent'],
&$components['privateExponent'],
&$components['coefficients'][2],
&$components['primes'][1],
&$components['primes'][2]
);
foreach ($values as &$value) {
extract(unpack('Nlength',
$this->_string_shift($paddedKey, 4)));
if (strlen($paddedKey) < $length) {
return false;
}
$value = new
BigInteger($this->_string_shift($paddedKey, $length), -256);
}
extract(unpack('Nlength',
$this->_string_shift($paddedKey, 4)));
if (strlen($paddedKey) < $length) {
return false;
}
$components['comment'] =
$this->_string_shift($decoded, $length);
$temp =
$components['primes'][1]->subtract($this->one);
$components['exponents'] = array(1 =>
$components['publicExponent']->modInverse($temp));
$temp =
$components['primes'][2]->subtract($this->one);
$components['exponents'][] =
$components['publicExponent']->modInverse($temp);
return $components;
}
return false;
}
/**
* Returns the key size
*
* More specifically, this returns the size of the modulo in bits.
*
* @access public
* @return int
*/
function getSize()
{
return !isset($this->modulus) ? 0 :
strlen($this->modulus->toBits());
}
/**
* Start Element Handler
*
* Called by xml_set_element_handler()
*
* @access private
* @param resource $parser
* @param string $name
* @param array $attribs
*/
function _start_element_handler($parser, $name, $attribs)
{
//$name = strtoupper($name);
switch ($name) {
case 'MODULUS':
$this->current =
&$this->components['modulus'];
break;
case 'EXPONENT':
$this->current =
&$this->components['publicExponent'];
break;
case 'P':
$this->current =
&$this->components['primes'][1];
break;
case 'Q':
$this->current =
&$this->components['primes'][2];
break;
case 'DP':
$this->current =
&$this->components['exponents'][1];
break;
case 'DQ':
$this->current =
&$this->components['exponents'][2];
break;
case 'INVERSEQ':
$this->current =
&$this->components['coefficients'][2];
break;
case 'D':
$this->current =
&$this->components['privateExponent'];
}
$this->current = '';
}
/**
* Stop Element Handler
*
* Called by xml_set_element_handler()
*
* @access private
* @param resource $parser
* @param string $name
*/
function _stop_element_handler($parser, $name)
{
if (isset($this->current)) {
$this->current = new
BigInteger(base64_decode($this->current), 256);
unset($this->current);
}
}
/**
* Data Handler
*
* Called by xml_set_character_data_handler()
*
* @access private
* @param resource $parser
* @param string $data
*/
function _data_handler($parser, $data)
{
if (!isset($this->current) || is_object($this->current)) {
return;
}
$this->current.= trim($data);
}
/**
* Loads a public or private key
*
* Returns true on success and false on failure (ie. an incorrect
password was provided or the key was malformed)
*
* @access public
* @param string|RSA|array $key
* @param bool|int $type optional
* @return bool
*/
function loadKey($key, $type = false)
{
if ($key instanceof 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 Hash($key->hash->getHash());
}
if (is_object($key->mgfHash)) {
$this->mgfHash = new
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) {
$types = array(
self::PUBLIC_FORMAT_RAW,
self::PRIVATE_FORMAT_PKCS1,
self::PRIVATE_FORMAT_XML,
self::PRIVATE_FORMAT_PUTTY,
self::PUBLIC_FORMAT_OPENSSH,
self::PRIVATE_FORMAT_OPENSSH
);
foreach ($types as $type) {
$components = $this->_parseKey($key, $type);
if ($components !== false) {
break;
}
}
} else {
$components = $this->_parseKey($key, $type);
}
if ($components === false) {
$this->comment = null;
$this->modulus = null;
$this->k = null;
$this->exponent = null;
$this->primes = null;
$this->exponents = null;
$this->coefficients = null;
$this->publicExponent = null;
return false;
}
if (isset($components['comment']) &&
$components['comment'] !== false) {
$this->comment = $components['comment'];
}
$this->modulus = $components['modulus'];
$this->k = strlen($this->modulus->toBytes());
$this->exponent =
isset($components['privateExponent']) ?
$components['privateExponent'] :
$components['publicExponent'];
if (isset($components['primes'])) {
$this->primes = $components['primes'];
$this->exponents = $components['exponents'];
$this->coefficients = $components['coefficients'];
$this->publicExponent =
$components['publicExponent'];
} else {
$this->primes = array();
$this->exponents = array();
$this->coefficients = array();
$this->publicExponent = false;
}
switch ($type) {
case self::PUBLIC_FORMAT_OPENSSH:
case self::PUBLIC_FORMAT_RAW:
$this->setPublicKey();
break;
case self::PRIVATE_FORMAT_PKCS1:
switch (true) {
case strpos($key, '-BEGIN PUBLIC KEY-') !==
false:
case strpos($key, '-BEGIN RSA PUBLIC KEY-')
!== false:
$this->setPublicKey();
}
}
return true;
}
/**
* Sets the password
*
* Private keys can be encrypted with a password. To unset the
password, pass in the empty string or false.
* Or rather, pass in $password such that empty($password) &&
!is_string($password) is true.
*
* @see self::createKey()
* @see self::loadKey()
* @access public
* @param string $password
*/
function setPassword($password = false)
{
$this->password = $password;
}
/**
* Defines the public key
*
* Some private key formats define the public exponent and some
don't. Those that don't define it are problematic when
* 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
* 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. 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.
*
* Returns true on success, false on failure
*
* @see self::getPublicKey()
* @access public
* @param string $key optional
* @param int $type optional
* @return bool
*/
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)) {
$this->publicExponent = $this->exponent;
return true;
}
if ($type === false) {
$types = array(
self::PUBLIC_FORMAT_RAW,
self::PUBLIC_FORMAT_PKCS1,
self::PUBLIC_FORMAT_XML,
self::PUBLIC_FORMAT_OPENSSH
);
foreach ($types as $type) {
$components = $this->_parseKey($key, $type);
if ($components !== false) {
break;
}
}
} else {
$components = $this->_parseKey($key, $type);
}
if ($components === false) {
return false;
}
if (empty($this->modulus) ||
!$this->modulus->equals($components['modulus'])) {
$this->modulus = $components['modulus'];
$this->exponent = $this->publicExponent =
$components['publicExponent'];
return true;
}
$this->publicExponent = $components['publicExponent'];
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 self::getPublicKey()
* @access public
* @param string $key optional
* @param int $type optional
* @return bool
*/
function setPrivateKey($key = false, $type = false)
{
if ($key === false && !empty($this->publicExponent)) {
$this->publicExponent = false;
return true;
}
$rsa = new RSA();
if (!$rsa->loadKey($key, $type)) {
return false;
}
$rsa->publicExponent = false;
// don't overwrite the old key if the new key is invalid
$this->loadKey($rsa);
return true;
}
/**
* Returns the public key
*
* The public key is only returned under two circumstances - if the
private key had the public key embedded within it
* or if the public key was set via setPublicKey(). If the currently
loaded key is supposed to be the public key this
* function won't return it since this library, for the most part,
doesn't distinguish between public and private keys.
*
* @see self::getPublicKey()
* @access public
* @param int $type optional
*/
function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8)
{
if (empty($this->modulus) || empty($this->publicExponent)) {
return false;
}
$oldFormat = $this->publicKeyFormat;
$this->publicKeyFormat = $type;
$temp = $this->_convertPublicKey($this->modulus,
$this->publicExponent);
$this->publicKeyFormat = $oldFormat;
return $temp;
}
/**
* Returns the public key's fingerprint
*
* The public key's fingerprint is returned, which is equivalent
to running `ssh-keygen -lf rsa.pub`. If there is
* no public key currently loaded, false is returned.
* Example output (md5):
"c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified
by RFC 4716)
*
* @access public
* @param string $algorithm The hashing algorithm to be used. Valid
options are 'md5' and 'sha256'. False is returned
* for invalid values.
* @return mixed
*/
function getPublicKeyFingerprint($algorithm = 'md5')
{
if (empty($this->modulus) || empty($this->publicExponent)) {
return false;
}
$modulus = $this->modulus->toBytes(true);
$publicExponent = $this->publicExponent->toBytes(true);
$RSAPublicKey = pack('Na*Na*Na*',
strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent),
$publicExponent, strlen($modulus), $modulus);
switch ($algorithm) {
case 'sha256':
$hash = new Hash('sha256');
$base = base64_encode($hash->hash($RSAPublicKey));
return substr($base, 0, strlen($base) - 1);
case 'md5':
return substr(chunk_split(md5($RSAPublicKey), 2,
':'), 0, -1);
default:
return false;
}
}
/**
* Returns the private key
*
* The private key is only returned if the currently loaded key
contains the constituent prime numbers.
*
* @see self::getPublicKey()
* @access public
* @param int $type optional
* @return mixed
*/
function getPrivateKey($type = self::PUBLIC_FORMAT_PKCS1)
{
if (empty($this->primes)) {
return false;
}
$oldFormat = $this->privateKeyFormat;
$this->privateKeyFormat = $type;
$temp = $this->_convertPrivateKey($this->modulus,
$this->publicExponent, $this->exponent, $this->primes,
$this->exponents, $this->coefficients);
$this->privateKeyFormat = $oldFormat;
return $temp;
}
/**
* Returns a minimalistic private key
*
* Returns the private key without the prime number constituants.
Structurally identical to a public key that
* hasn't been set as the public key
*
* @see self::getPrivateKey()
* @access private
* @param int $mode optional
*/
function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8)
{
if (empty($this->modulus) || empty($this->exponent)) {
return false;
}
$oldFormat = $this->publicKeyFormat;
$this->publicKeyFormat = $mode;
$temp = $this->_convertPublicKey($this->modulus,
$this->exponent);
$this->publicKeyFormat = $oldFormat;
return $temp;
}
/**
* __toString() magic method
*
* @access public
* @return string
*/
function __toString()
{
$key = $this->getPrivateKey($this->privateKeyFormat);
if ($key !== false) {
return $key;
}
$key = $this->_getPrivatePublicKey($this->publicKeyFormat);
return $key !== false ? $key : '';
}
/**
* __clone() magic method
*
* @access public
* @return Crypt_RSA
*/
function __clone()
{
$key = new RSA();
$key->loadKey($this);
return $key;
}
/**
* Generates the smallest and largest numbers requiring $bits bits
*
* @access private
* @param int $bits
* @return array
*/
function _generateMinMax($bits)
{
$bytes = $bits >> 3;
$min = str_repeat(chr(0), $bytes);
$max = str_repeat(chr(0xFF), $bytes);
$msb = $bits & 7;
if ($msb) {
$min = chr(1 << ($msb - 1)) . $min;
$max = chr((1 << $msb) - 1) . $max;
} else {
$min[0] = chr(0x80);
}
return array(
'min' => new BigInteger($min, 256),
'max' => new BigInteger($max, 256)
);
}
/**
* DER-decode the length
*
* DER supports lengths up to (2**8)**127, however, we'll only
support lengths up to (2**8)**4. See
* {@link
http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690
paragraph 8.1.3} for more information.
*
* @access private
* @param string $string
* @return int
*/
function _decodeLength(&$string)
{
$length = ord($this->_string_shift($string));
if ($length & 0x80) { // definite length, long form
$length&= 0x7F;
$temp = $this->_string_shift($string, $length);
list(, $length) = unpack('N', substr(str_pad($temp,
4, chr(0), STR_PAD_LEFT), -4));
}
return $length;
}
/**
* DER-encode the length
*
* DER supports lengths up to (2**8)**127, however, we'll only
support lengths up to (2**8)**4. See
* {@link
http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690
paragraph 8.1.3} for more information.
*
* @access private
* @param int $length
* @return string
*/
function _encodeLength($length)
{
if ($length <= 0x7F) {
return chr($length);
}
$temp = ltrim(pack('N', $length), chr(0));
return pack('Ca*', 0x80 | strlen($temp), $temp);
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @return string
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
/**
* Determines the private key format
*
* @see self::createKey()
* @access public
* @param int $format
*/
function setPrivateKeyFormat($format)
{
$this->privateKeyFormat = $format;
}
/**
* Determines the public key format
*
* @see self::createKey()
* @access public
* @param int $format
*/
function setPublicKeyFormat($format)
{
$this->publicKeyFormat = $format;
}
/**
* Determines which hashing function should be used
*
* Used with signature production / verification and (if the encryption
mode is self::ENCRYPTION_OAEP) encryption and
* decryption. If $hash isn't supported, sha1 is used.
*
* @access public
* @param string $hash
*/
function setHash($hash)
{
// \phpseclib\Crypt\Hash supports algorithms that PKCS#1
doesn't support. md5-96 and sha1-96, for example.
switch ($hash) {
case 'md2':
case 'md5':
case 'sha1':
case 'sha256':
case 'sha384':
case 'sha512':
$this->hash = new Hash($hash);
$this->hashName = $hash;
break;
default:
$this->hash = new Hash('sha1');
$this->hashName = 'sha1';
}
$this->hLen = $this->hash->getLength();
}
/**
* Determines which hashing function should be used for the mask
generation function
*
* The mask generation function is used by self::ENCRYPTION_OAEP and
self::SIGNATURE_PSS and although it's
* best if Hash and MGFHash are set to the same thing this is not a
requirement.
*
* @access public
* @param string $hash
*/
function setMGFHash($hash)
{
// \phpseclib\Crypt\Hash supports algorithms that PKCS#1
doesn't support. md5-96 and sha1-96, for example.
switch ($hash) {
case 'md2':
case 'md5':
case 'sha1':
case 'sha256':
case 'sha384':
case 'sha512':
$this->mgfHash = new Hash($hash);
break;
default:
$this->mgfHash = new Hash('sha1');
}
$this->mgfHLen = $this->mgfHash->getLength();
}
/**
* Determines the salt length
*
* To quote from {@link http://tools.ietf.org/html/rfc3447#page-38
RFC3447#page-38}:
*
* Typical salt lengths in octets are hLen (the length of the output
* of the hash function Hash) and 0.
*
* @access public
* @param int $sLen
*/
function setSaltLength($sLen)
{
$this->sLen = $sLen;
}
/**
* Integer-to-Octet-String primitive
*
* See {@link http://tools.ietf.org/html/rfc3447#section-4.1
RFC3447#section-4.1}.
*
* @access private
* @param \phpseclib\Math\BigInteger $x
* @param int $xLen
* @return string
*/
function _i2osp($x, $xLen)
{
$x = $x->toBytes();
if (strlen($x) > $xLen) {
user_error('Integer too large');
return false;
}
return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
}
/**
* Octet-String-to-Integer primitive
*
* See {@link http://tools.ietf.org/html/rfc3447#section-4.2
RFC3447#section-4.2}.
*
* @access private
* @param int|string|resource $x
* @return \phpseclib\Math\BigInteger
*/
function _os2ip($x)
{
return new BigInteger($x, 256);
}
/**
* Exponentiate with or without Chinese Remainder Theorem
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1
RFC3447#section-5.1.2}.
*
* @access private
* @param \phpseclib\Math\BigInteger $x
* @return \phpseclib\Math\BigInteger
*/
function _exponentiate($x)
{
switch (true) {
case empty($this->primes):
case $this->primes[1]->equals($this->zero):
case empty($this->coefficients):
case $this->coefficients[2]->equals($this->zero):
case empty($this->exponents):
case $this->exponents[1]->equals($this->zero):
return $x->modPow($this->exponent,
$this->modulus);
}
$num_primes = count($this->primes);
if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
$m_i = array(
1 => $x->modPow($this->exponents[1],
$this->primes[1]),
2 => $x->modPow($this->exponents[2],
$this->primes[2])
);
$h = $m_i[1]->subtract($m_i[2]);
$h = $h->multiply($this->coefficients[2]);
list(, $h) = $h->divide($this->primes[1]);
$m = $m_i[2]->add($h->multiply($this->primes[2]));
$r = $this->primes[1];
for ($i = 3; $i <= $num_primes; $i++) {
$m_i = $x->modPow($this->exponents[$i],
$this->primes[$i]);
$r = $r->multiply($this->primes[$i - 1]);
$h = $m_i->subtract($m);
$h = $h->multiply($this->coefficients[$i]);
list(, $h) = $h->divide($this->primes[$i]);
$m = $m->add($r->multiply($h));
}
} else {
$smallest = $this->primes[1];
for ($i = 2; $i <= $num_primes; $i++) {
if ($smallest->compare($this->primes[$i]) > 0) {
$smallest = $this->primes[$i];
}
}
$one = new BigInteger(1);
$r = $one->random($one, $smallest->subtract($one));
$m_i = array(
1 => $this->_blind($x, $r, 1),
2 => $this->_blind($x, $r, 2)
);
$h = $m_i[1]->subtract($m_i[2]);
$h = $h->multiply($this->coefficients[2]);
list(, $h) = $h->divide($this->primes[1]);
$m = $m_i[2]->add($h->multiply($this->primes[2]));
$r = $this->primes[1];
for ($i = 3; $i <= $num_primes; $i++) {
$m_i = $this->_blind($x, $r, $i);
$r = $r->multiply($this->primes[$i - 1]);
$h = $m_i->subtract($m);
$h = $h->multiply($this->coefficients[$i]);
list(, $h) = $h->divide($this->primes[$i]);
$m = $m->add($r->multiply($h));
}
}
return $m;
}
/**
* Performs RSA Blinding
*
* Protects against timing attacks by employing RSA Blinding.
* Returns $x->modPow($this->exponents[$i], $this->primes[$i])
*
* @access private
* @param \phpseclib\Math\BigInteger $x
* @param \phpseclib\Math\BigInteger $r
* @param int $i
* @return \phpseclib\Math\BigInteger
*/
function _blind($x, $r, $i)
{
$x = $x->multiply($r->modPow($this->publicExponent,
$this->primes[$i]));
$x = $x->modPow($this->exponents[$i], $this->primes[$i]);
$r = $r->modInverse($this->primes[$i]);
$x = $x->multiply($r);
list(, $x) = $x->divide($this->primes[$i]);
return $x;
}
/**
* Performs blinded RSA equality testing
*
* Protects against a particular type of timing attack described.
*
* See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson
In Timing Attacks (or, Don't use MessageDigest.isEquals)}
*
* Thanks for the heads up singpolyma!
*
* @access private
* @param string $x
* @param string $y
* @return bool
*/
function _equals($x, $y)
{
if (function_exists('hash_equals')) {
return hash_equals($x, $y);
}
if (strlen($x) != strlen($y)) {
return false;
}
$result = "\0";
$x^= $y;
for ($i = 0; $i < strlen($x); $i++) {
$result|= $x[$i];
}
return $result === "\0";
}
/**
* RSAEP
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1
RFC3447#section-5.1.1}.
*
* @access private
* @param \phpseclib\Math\BigInteger $m
* @return \phpseclib\Math\BigInteger
*/
function _rsaep($m)
{
if ($m->compare($this->zero) < 0 ||
$m->compare($this->modulus) > 0) {
user_error('Message representative out of range');
return false;
}
return $this->_exponentiate($m);
}
/**
* RSADP
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2
RFC3447#section-5.1.2}.
*
* @access private
* @param \phpseclib\Math\BigInteger $c
* @return \phpseclib\Math\BigInteger
*/
function _rsadp($c)
{
if ($c->compare($this->zero) < 0 ||
$c->compare($this->modulus) > 0) {
user_error('Ciphertext representative out of range');
return false;
}
return $this->_exponentiate($c);
}
/**
* RSASP1
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1
RFC3447#section-5.2.1}.
*
* @access private
* @param \phpseclib\Math\BigInteger $m
* @return \phpseclib\Math\BigInteger
*/
function _rsasp1($m)
{
if ($m->compare($this->zero) < 0 ||
$m->compare($this->modulus) > 0) {
user_error('Message representative out of range');
return false;
}
return $this->_exponentiate($m);
}
/**
* RSAVP1
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2
RFC3447#section-5.2.2}.
*
* @access private
* @param \phpseclib\Math\BigInteger $s
* @return \phpseclib\Math\BigInteger
*/
function _rsavp1($s)
{
if ($s->compare($this->zero) < 0 ||
$s->compare($this->modulus) > 0) {
user_error('Signature representative out of range');
return false;
}
return $this->_exponentiate($s);
}
/**
* MGF1
*
* See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1
RFC3447#appendix-B.2.1}.
*
* @access private
* @param string $mgfSeed
* @param int $maskLen
* @return string
*/
function _mgf1($mgfSeed, $maskLen)
{
// if $maskLen would yield strings larger than 4GB, PKCS#1 suggests
a "Mask too long" error be output.
$t = '';
$count = ceil($maskLen / $this->mgfHLen);
for ($i = 0; $i < $count; $i++) {
$c = pack('N', $i);
$t.= $this->mgfHash->hash($mgfSeed . $c);
}
return substr($t, 0, $maskLen);
}
/**
* RSAES-OAEP-ENCRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1
RFC3447#section-7.1.1} and
* {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding
OAES}.
*
* @access private
* @param string $m
* @param string $l
* @return string
*/
function _rsaes_oaep_encrypt($m, $l = '')
{
$mLen = strlen($m);
// Length checking
// if $l is larger than two million terrabytes and you're
using sha1, PKCS#1 suggests a "Label too long" error
// be output.
if ($mLen > $this->k - 2 * $this->hLen - 2) {
user_error('Message too long');
return false;
}
// EME-OAEP encoding
$lHash = $this->hash->hash($l);
$ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen -
2);
$db = $lHash . $ps . chr(1) . $m;
$seed = Random::string($this->hLen);
$dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
$maskedDB = $db ^ $dbMask;
$seedMask = $this->_mgf1($maskedDB, $this->hLen);
$maskedSeed = $seed ^ $seedMask;
$em = chr(0) . $maskedSeed . $maskedDB;
// RSA encryption
$m = $this->_os2ip($em);
$c = $this->_rsaep($m);
$c = $this->_i2osp($c, $this->k);
// Output the ciphertext C
return $c;
}
/**
* RSAES-OAEP-DECRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2
RFC3447#section-7.1.2}. The fact that the error
* messages aren't distinguishable from one another hinders
debugging, but, to quote from RFC3447#section-7.1.2:
*
* Note. Care must be taken to ensure that an opponent cannot
* distinguish the different error conditions in Step 3.g, whether
by
* error message or timing, or, more generally, learn partial
* information about the encoded message EM. Otherwise an opponent
may
* be able to obtain useful information about the decryption of the
* ciphertext C, leading to a chosen-ciphertext attack such as the
one
* observed by Manger [36].
*
* As for $l... to quote from {@link
http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
*
* Both the encryption and the decryption operations of RSAES-OAEP
take
* the value of a label L as input. In this version of PKCS #1, L
is
* the empty string; other uses of the label are outside the scope
of
* this document.
*
* @access private
* @param string $c
* @param string $l
* @return string
*/
function _rsaes_oaep_decrypt($c, $l = '')
{
// Length checking
// if $l is larger than two million terrabytes and you're
using sha1, PKCS#1 suggests a "Label too long" error
// be output.
if (strlen($c) != $this->k || $this->k < 2 *
$this->hLen + 2) {
user_error('Decryption error');
return false;
}
// RSA decryption
$c = $this->_os2ip($c);
$m = $this->_rsadp($c);
if ($m === false) {
user_error('Decryption error');
return false;
}
$em = $this->_i2osp($m, $this->k);
// EME-OAEP decoding
$lHash = $this->hash->hash($l);
$y = ord($em[0]);
$maskedSeed = substr($em, 1, $this->hLen);
$maskedDB = substr($em, $this->hLen + 1);
$seedMask = $this->_mgf1($maskedDB, $this->hLen);
$seed = $maskedSeed ^ $seedMask;
$dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
$db = $maskedDB ^ $dbMask;
$lHash2 = substr($db, 0, $this->hLen);
$m = substr($db, $this->hLen);
$hashesMatch = $this->_equals($lHash, $lHash2);
$leadingZeros = 1;
$patternMatch = 0;
$offset = 0;
for ($i = 0; $i < strlen($m); $i++) {
$patternMatch|= $leadingZeros & ($m[$i] ===
"\1");
$leadingZeros&= $m[$i] === "\0";
$offset+= $patternMatch ? 0 : 1;
}
// we do | instead of || to avoid
https://en.wikipedia.org/wiki/Short-circuit_evaluation
// to protect against timing attacks
if (!$hashesMatch | !$patternMatch) {
user_error('Decryption error');
return false;
}
// Output the message M
return substr($m, $offset + 1);
}
/**
* Raw Encryption / Decryption
*
* Doesn't use padding and is not recommended.
*
* @access private
* @param string $m
* @return string
*/
function _raw_encrypt($m)
{
$temp = $this->_os2ip($m);
$temp = $this->_rsaep($temp);
return $this->_i2osp($temp, $this->k);
}
/**
* RSAES-PKCS1-V1_5-ENCRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1
RFC3447#section-7.2.1}.
*
* @access private
* @param string $m
* @return string
*/
function _rsaes_pkcs1_v1_5_encrypt($m)
{
$mLen = strlen($m);
// Length checking
if ($mLen > $this->k - 11) {
user_error('Message too long');
return false;
}
// EME-PKCS1-v1_5 encoding
$psLen = $this->k - $mLen - 3;
$ps = '';
while (strlen($ps) != $psLen) {
$temp = Random::string($psLen - strlen($ps));
$temp = str_replace("\x00", '', $temp);
$ps.= $temp;
}
$type = 2;
// see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand
why this is being done
if (defined('CRYPT_RSA_PKCS15_COMPAT') &&
(!isset($this->publicExponent) || $this->exponent !==
$this->publicExponent)) {
$type = 1;
// "The padding string PS shall consist of k-3-||D||
octets. ... for block type 01, they shall have value FF"
$ps = str_repeat("\xFF", $psLen);
}
$em = chr(0) . chr($type) . $ps . chr(0) . $m;
// RSA encryption
$m = $this->_os2ip($em);
$c = $this->_rsaep($m);
$c = $this->_i2osp($c, $this->k);
// Output the ciphertext C
return $c;
}
/**
* RSAES-PKCS1-V1_5-DECRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2
RFC3447#section-7.2.2}.
*
* 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
* 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
* 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.
*
* As a consequence of this, a private key encrypted ciphertext
produced with \phpseclib\Crypt\RSA may not decrypt
* with a strictly PKCS#1 v1.5 compliant RSA implementation. Public
key encrypted ciphertext's should but
* not private key encrypted ciphertext's.
*
* @access private
* @param string $c
* @return string
*/
function _rsaes_pkcs1_v1_5_decrypt($c)
{
// Length checking
if (strlen($c) != $this->k) { // or if k < 11
user_error('Decryption error');
return false;
}
// RSA decryption
$c = $this->_os2ip($c);
$m = $this->_rsadp($c);
if ($m === false) {
user_error('Decryption error');
return false;
}
$em = $this->_i2osp($m, $this->k);
// EME-PKCS1-v1_5 decoding
if (ord($em[0]) != 0 || ord($em[1]) > 2) {
user_error('Decryption error');
return false;
}
$ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
$m = substr($em, strlen($ps) + 3);
if (strlen($ps) < 8) {
user_error('Decryption error');
return false;
}
// Output M
return $m;
}
/**
* EMSA-PSS-ENCODE
*
* See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1
RFC3447#section-9.1.1}.
*
* @access private
* @param string $m
* @param int $emBits
*/
function _emsa_pss_encode($m, $emBits)
{
// if $m is larger than two million terrabytes and you're
using sha1, PKCS#1 suggests a "Label too long" error
// be output.
$emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
$sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
$mHash = $this->hash->hash($m);
if ($emLen < $this->hLen + $sLen + 2) {
user_error('Encoding error');
return false;
}
$salt = Random::string($sLen);
$m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
$h = $this->hash->hash($m2);
$ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
$db = $ps . chr(1) . $salt;
$dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
$maskedDB = $db ^ $dbMask;
$maskedDB[0] = ~chr(0xFF << ($emBits & 7)) &
$maskedDB[0];
$em = $maskedDB . $h . chr(0xBC);
return $em;
}
/**
* EMSA-PSS-VERIFY
*
* See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2
RFC3447#section-9.1.2}.
*
* @access private
* @param string $m
* @param string $em
* @param int $emBits
* @return string
*/
function _emsa_pss_verify($m, $em, $emBits)
{
// if $m is larger than two million terrabytes and you're
using sha1, PKCS#1 suggests a "Label too long" error
// be output.
$emLen = ($emBits + 7) >> 3; // ie. ceil($emBits / 8);
$sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
$mHash = $this->hash->hash($m);
if ($emLen < $this->hLen + $sLen + 2) {
return false;
}
if ($em[strlen($em) - 1] != chr(0xBC)) {
return false;
}
$maskedDB = substr($em, 0, -$this->hLen - 1);
$h = substr($em, -$this->hLen - 1, $this->hLen);
$temp = chr(0xFF << ($emBits & 7));
if ((~$maskedDB[0] & $temp) != $temp) {
return false;
}
$dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
$db = $maskedDB ^ $dbMask;
$db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
$temp = $emLen - $this->hLen - $sLen - 2;
if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) ||
ord($db[$temp]) != 1) {
return false;
}
$salt = substr($db, $temp + 1); // should be $sLen long
$m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
$h2 = $this->hash->hash($m2);
return $this->_equals($h, $h2);
}
/**
* RSASSA-PSS-SIGN
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1
RFC3447#section-8.1.1}.
*
* @access private
* @param string $m
* @return string
*/
function _rsassa_pss_sign($m)
{
// EMSA-PSS encoding
$em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
// RSA signature
$m = $this->_os2ip($em);
$s = $this->_rsasp1($m);
$s = $this->_i2osp($s, $this->k);
// Output the signature S
return $s;
}
/**
* RSASSA-PSS-VERIFY
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2
RFC3447#section-8.1.2}.
*
* @access private
* @param string $m
* @param string $s
* @return string
*/
function _rsassa_pss_verify($m, $s)
{
// Length checking
if (strlen($s) != $this->k) {
user_error('Invalid signature');
return false;
}
// RSA verification
$modBits = strlen($this->modulus->toBits());
$s2 = $this->_os2ip($s);
$m2 = $this->_rsavp1($s2);
if ($m2 === false) {
user_error('Invalid signature');
return false;
}
$em = $this->_i2osp($m2, $this->k);
if ($em === false) {
user_error('Invalid signature');
return false;
}
// EMSA-PSS verification
return $this->_emsa_pss_verify($m, $em, $modBits - 1);
}
/**
* EMSA-PKCS1-V1_5-ENCODE
*
* See {@link http://tools.ietf.org/html/rfc3447#section-9.2
RFC3447#section-9.2}.
*
* @access private
* @param string $m
* @param int $emLen
* @return string
*/
function _emsa_pkcs1_v1_5_encode($m, $emLen)
{
$h = $this->hash->hash($m);
if ($h === false) {
return false;
}
// see http://tools.ietf.org/html/rfc3447#page-43
switch ($this->hashName) {
case 'md2':
$t = pack('H*',
'3020300c06082a864886f70d020205000410');
break;
case 'md5':
$t = pack('H*',
'3020300c06082a864886f70d020505000410');
break;
case 'sha1':
$t = pack('H*',
'3021300906052b0e03021a05000414');
break;
case 'sha256':
$t = pack('H*',
'3031300d060960864801650304020105000420');
break;
case 'sha384':
$t = pack('H*',
'3041300d060960864801650304020205000430');
break;
case 'sha512':
$t = pack('H*',
'3051300d060960864801650304020305000440');
}
$t.= $h;
$tLen = strlen($t);
if ($emLen < $tLen + 11) {
user_error('Intended encoded message length too
short');
return false;
}
$ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
$em = "\0\1$ps\0$t";
return $em;
}
/**
* EMSA-PKCS1-V1_5-ENCODE (without NULL)
*
* Quoting https://tools.ietf.org/html/rfc8017#page-65,
*
* "The parameters field associated with id-sha1, id-sha224,
id-sha256,
* id-sha384, id-sha512, id-sha512/224, and id-sha512/256 should
* generally be omitted, but if present, it shall have a value of type
* NULL"
*
* @access private
* @param string $m
* @param int $emLen
* @return string
*/
function _emsa_pkcs1_v1_5_encode_without_null($m, $emLen)
{
$h = $this->hash->hash($m);
if ($h === false) {
return false;
}
switch ($this->hashName) {
case 'sha1':
$t = pack('H*',
'301f300706052b0e03021a0414');
break;
case 'sha256':
$t = pack('H*',
'302f300b06096086480165030402010420');
break;
case 'sha384':
$t = pack('H*',
'303f300b06096086480165030402020430');
break;
case 'sha512':
$t = pack('H*',
'304f300b06096086480165030402030440');
break;
default:
return false;
}
$t.= $h;
$tLen = strlen($t);
if ($emLen < $tLen + 11) {
user_error('Intended encoded message length too
short');
return false;
}
$ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
$em = "\0\1$ps\0$t";
return $em;
}
/**
* RSASSA-PKCS1-V1_5-SIGN
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1
RFC3447#section-8.2.1}.
*
* @access private
* @param string $m
* @return string
*/
function _rsassa_pkcs1_v1_5_sign($m)
{
// EMSA-PKCS1-v1_5 encoding
$em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
if ($em === false) {
user_error('RSA modulus too short');
return false;
}
// RSA signature
$m = $this->_os2ip($em);
$s = $this->_rsasp1($m);
$s = $this->_i2osp($s, $this->k);
// Output the signature S
return $s;
}
/**
* RSASSA-PKCS1-V1_5-VERIFY
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2
RFC3447#section-8.2.2}.
*
* @access private
* @param string $m
* @param string $s
* @return string
*/
function _rsassa_pkcs1_v1_5_verify($m, $s)
{
// Length checking
if (strlen($s) != $this->k) {
user_error('Invalid signature');
return false;
}
// RSA verification
$s = $this->_os2ip($s);
$m2 = $this->_rsavp1($s);
if ($m2 === false) {
user_error('Invalid signature');
return false;
}
$em = $this->_i2osp($m2, $this->k);
if ($em === false) {
user_error('Invalid signature');
return false;
}
// EMSA-PKCS1-v1_5 encoding
$em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
$em3 = $this->_emsa_pkcs1_v1_5_encode_without_null($m,
$this->k);
if ($em2 === false && $em3 === false) {
user_error('RSA modulus too short');
return false;
}
// Compare
return ($em2 !== false && $this->_equals($em, $em2)) ||
($em3 !== false && $this->_equals($em, $em3));
}
/**
* Set Encryption Mode
*
* Valid values include self::ENCRYPTION_OAEP and
self::ENCRYPTION_PKCS1.
*
* @access public
* @param int $mode
*/
function setEncryptionMode($mode)
{
$this->encryptionMode = $mode;
}
/**
* Set Signature Mode
*
* Valid values include self::SIGNATURE_PSS and self::SIGNATURE_PKCS1
*
* @access public
* @param int $mode
*/
function setSignatureMode($mode)
{
$this->signatureMode = $mode;
}
/**
* Set public key comment.
*
* @access public
* @param string $comment
*/
function setComment($comment)
{
$this->comment = $comment;
}
/**
* Get public key comment.
*
* @access public
* @return string
*/
function getComment()
{
return $this->comment;
}
/**
* Encryption
*
* Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place
limits on how long $plaintext can be.
* If $plaintext exceeds those limits it will be broken up so that it
does and the resultant ciphertext's will
* be concatenated together.
*
* @see self::decrypt()
* @access public
* @param string $plaintext
* @return string
*/
function encrypt($plaintext)
{
switch ($this->encryptionMode) {
case self::ENCRYPTION_NONE:
$plaintext = str_split($plaintext, $this->k);
$ciphertext = '';
foreach ($plaintext as $m) {
$ciphertext.= $this->_raw_encrypt($m);
}
return $ciphertext;
case self::ENCRYPTION_PKCS1:
$length = $this->k - 11;
if ($length <= 0) {
return false;
}
$plaintext = str_split($plaintext, $length);
$ciphertext = '';
foreach ($plaintext as $m) {
$ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
}
return $ciphertext;
//case self::ENCRYPTION_OAEP:
default:
$length = $this->k - 2 * $this->hLen - 2;
if ($length <= 0) {
return false;
}
$plaintext = str_split($plaintext, $length);
$ciphertext = '';
foreach ($plaintext as $m) {
$ciphertext.= $this->_rsaes_oaep_encrypt($m);
}
return $ciphertext;
}
}
/**
* Decryption
*
* @see self::encrypt()
* @access public
* @param string $ciphertext
* @return string
*/
function decrypt($ciphertext)
{
if ($this->k <= 0) {
return false;
}
$ciphertext = str_split($ciphertext, $this->k);
$ciphertext[count($ciphertext) - 1] =
str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0),
STR_PAD_LEFT);
$plaintext = '';
switch ($this->encryptionMode) {
case self::ENCRYPTION_NONE:
$decrypt = '_raw_encrypt';
break;
case self::ENCRYPTION_PKCS1:
$decrypt = '_rsaes_pkcs1_v1_5_decrypt';
break;
//case self::ENCRYPTION_OAEP:
default:
$decrypt = '_rsaes_oaep_decrypt';
}
foreach ($ciphertext as $c) {
$temp = $this->$decrypt($c);
if ($temp === false) {
return false;
}
$plaintext.= $temp;
}
return $plaintext;
}
/**
* Create a signature
*
* @see self::verify()
* @access public
* @param string $message
* @return string
*/
function sign($message)
{
if (empty($this->modulus) || empty($this->exponent)) {
return false;
}
switch ($this->signatureMode) {
case self::SIGNATURE_PKCS1:
return $this->_rsassa_pkcs1_v1_5_sign($message);
//case self::SIGNATURE_PSS:
default:
return $this->_rsassa_pss_sign($message);
}
}
/**
* Verifies a signature
*
* @see self::sign()
* @access public
* @param string $message
* @param string $signature
* @return bool
*/
function verify($message, $signature)
{
if (empty($this->modulus) || empty($this->exponent)) {
return false;
}
switch ($this->signatureMode) {
case self::SIGNATURE_PKCS1:
return $this->_rsassa_pkcs1_v1_5_verify($message,
$signature);
//case self::SIGNATURE_PSS:
default:
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('#.*?^-+[^-]+-+[\r\n ]*$#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;
}
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php000064400000033232151156520650017352
0ustar00<?php
/**
* Pure-PHP implementation of Triple DES.
*
* Uses mcrypt, if available, and an internal implementation, otherwise.
Operates in the EDE3 mode (encrypt-decrypt-encrypt).
*
* PHP version 5
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $des = new \phpseclib\Crypt\TripleDES();
*
* $des->setKey('abcdefghijklmnopqrstuvwx');
*
* $size = 10 * 1024;
* $plaintext = '';
* for ($i = 0; $i < $size; $i++) {
* $plaintext.= 'a';
* }
*
* echo $des->decrypt($des->encrypt($plaintext));
* ?>
* </code>
*
* @category Crypt
* @package TripleDES
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Pure-PHP implementation of Triple DES.
*
* @package TripleDES
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class TripleDES extends DES
{
/**
* Encrypt / decrypt using inner chaining
*
* Inner chaining is used by SSH-1 and is generally considered to be
less secure then outer chaining (self::MODE_CBC3).
*/
const MODE_3CBC = -2;
/**
* Encrypt / decrypt using outer chaining
*
* Outer chaining is used by SSH-2 and when the mode is set to
\phpseclib\Crypt\Base::MODE_CBC.
*/
const MODE_CBC3 = Base::MODE_CBC;
/**
* Key Length (in bytes)
*
* @see \phpseclib\Crypt\TripleDES::setKeyLength()
* @var int
* @access private
*/
var $key_length = 24;
/**
* The default salt used by setPassword()
*
* @see \phpseclib\Crypt\Base::password_default_salt
* @see \phpseclib\Crypt\Base::setPassword()
* @var string
* @access private
*/
var $password_default_salt = 'phpseclib';
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib\Crypt\DES::cipher_name_mcrypt
* @see \phpseclib\Crypt\Base::cipher_name_mcrypt
* @var string
* @access private
*/
var $cipher_name_mcrypt = 'tripledes';
/**
* Optimizing value while CFB-encrypting
*
* @see \phpseclib\Crypt\Base::cfb_init_len
* @var int
* @access private
*/
var $cfb_init_len = 750;
/**
* max possible size of $key
*
* @see self::setKey()
* @see \phpseclib\Crypt\DES::setKey()
* @var string
* @access private
*/
var $key_length_max = 24;
/**
* Internal flag whether using self::MODE_3CBC or not
*
* @var bool
* @access private
*/
var $mode_3cbc;
/**
* The \phpseclib\Crypt\DES objects
*
* Used only if $mode_3cbc === true
*
* @var array
* @access private
*/
var $des;
/**
* Default Constructor.
*
* Determines whether or not the mcrypt extension should be used.
*
* $mode could be:
*
* - \phpseclib\Crypt\Base::MODE_ECB
*
* - \phpseclib\Crypt\Base::MODE_CBC
*
* - \phpseclib\Crypt\Base::MODE_CTR
*
* - \phpseclib\Crypt\Base::MODE_CFB
*
* - \phpseclib\Crypt\Base::MODE_OFB
*
* - \phpseclib\Crypt\TripleDES::MODE_3CBC
*
* If not explicitly set, \phpseclib\Crypt\Base::MODE_CBC will be used.
*
* @see \phpseclib\Crypt\DES::__construct()
* @see \phpseclib\Crypt\Base::__construct()
* @param int $mode
* @access public
*/
function __construct($mode = Base::MODE_CBC)
{
switch ($mode) {
// In case of self::MODE_3CBC, we init as CRYPT_DES_MODE_CBC
// and additional flag us internally as 3CBC
case self::MODE_3CBC:
parent::__construct(Base::MODE_CBC);
$this->mode_3cbc = true;
// This three $des'es will do the 3CBC work (if $key
> 64bits)
$this->des = array(
new DES(Base::MODE_CBC),
new DES(Base::MODE_CBC),
new DES(Base::MODE_CBC),
);
// we're going to be doing the padding, ourselves, so
disable it in the \phpseclib\Crypt\DES objects
$this->des[0]->disablePadding();
$this->des[1]->disablePadding();
$this->des[2]->disablePadding();
break;
// If not 3CBC, we init as usual
default:
parent::__construct($mode);
}
}
/**
* Test for engine validity
*
* This is mainly just a wrapper to set things up for
\phpseclib\Crypt\Base::isValidEngine()
*
* @see \phpseclib\Crypt\Base::__construct()
* @param int $engine
* @access public
* @return bool
*/
function isValidEngine($engine)
{
if ($engine == self::ENGINE_OPENSSL) {
$this->cipher_name_openssl_ecb = 'des-ede3';
$mode = $this->_openssl_translate_mode();
$this->cipher_name_openssl = $mode == 'ecb' ?
'des-ede3' : 'des-ede3-' . $mode;
}
return parent::isValidEngine($engine);
}
/**
* Sets the initialization vector. (optional)
*
* SetIV is not required when \phpseclib\Crypt\Base::MODE_ECB is being
used. If not explicitly set, it'll be assumed
* to be all zero's.
*
* @see \phpseclib\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);
}
}
/**
* Sets the key length.
*
* Valid key lengths are 64, 128 and 192
*
* @see \phpseclib\Crypt\Base:setKeyLength()
* @access public
* @param int $length
*/
function setKeyLength($length)
{
$length >>= 3;
switch (true) {
case $length <= 8:
$this->key_length = 8;
break;
case $length <= 16:
$this->key_length = 16;
break;
default:
$this->key_length = 24;
}
parent::setKeyLength($length);
}
/**
* Sets the key.
*
* Keys can be of any length. Triple DES, itself, can use 128-bit (eg.
strlen($key) == 16) or
* 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.
*
* If the key is not explicitly set, it'll be assumed to be all
null bytes.
*
* @access public
* @see \phpseclib\Crypt\DES::setKey()
* @see \phpseclib\Crypt\Base::setKey()
* @param string $key
*/
function setKey($key)
{
$length = $this->explicit_key_length ? $this->key_length :
strlen($key);
if ($length > 8) {
$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:
// 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[1]->setKey(substr($key, 8, 8));
$this->des[2]->setKey(substr($key, 16, 8));
}
}
/**
* Encrypts a message.
*
* @see \phpseclib\Crypt\Base::encrypt()
* @access public
* @param string $plaintext
* @return string $cipertext
*/
function encrypt($plaintext)
{
// parent::en/decrypt() is able to do all the work for all modes
and keylengths,
// except for: self::MODE_3CBC (inner chaining CBC) with a key >
64bits
// if the key is smaller then 8, do what we'd normally do
if ($this->mode_3cbc && strlen($this->key) > 8) {
return $this->des[2]->encrypt(
$this->des[1]->decrypt(
$this->des[0]->encrypt(
$this->_pad($plaintext)
)
)
);
}
return parent::encrypt($plaintext);
}
/**
* Decrypts a message.
*
* @see \phpseclib\Crypt\Base::decrypt()
* @access public
* @param string $ciphertext
* @return string $plaintext
*/
function decrypt($ciphertext)
{
if ($this->mode_3cbc && strlen($this->key) > 8) {
return $this->_unpad(
$this->des[0]->decrypt(
$this->des[1]->encrypt(
$this->des[2]->decrypt(
str_pad($ciphertext, (strlen($ciphertext) + 7)
& 0xFFFFFFF8, "\0")
)
)
)
);
}
return parent::decrypt($ciphertext);
}
/**
* 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 $des->encrypt(substr($plaintext, 0, 8));
* echo $des->encrypt(substr($plaintext, 8, 8));
* </code>
* <code>
* echo $des->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>
* $des->encrypt(substr($plaintext, 0, 8));
* echo $des->decrypt($des->encrypt(substr($plaintext, 8,
8)));
* </code>
* <code>
* echo $des->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 \phpseclib\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 \phpseclib\Crypt\Base::enableContinuousBuffer()
* @see self::disableContinuousBuffer()
* @access public
*/
function enableContinuousBuffer()
{
parent::enableContinuousBuffer();
if ($this->mode_3cbc) {
$this->des[0]->enableContinuousBuffer();
$this->des[1]->enableContinuousBuffer();
$this->des[2]->enableContinuousBuffer();
}
}
/**
* Treat consecutive packets as if they are a discontinuous buffer.
*
* The default behavior.
*
* @see \phpseclib\Crypt\Base::disableContinuousBuffer()
* @see self::enableContinuousBuffer()
* @access public
*/
function disableContinuousBuffer()
{
parent::disableContinuousBuffer();
if ($this->mode_3cbc) {
$this->des[0]->disableContinuousBuffer();
$this->des[1]->disableContinuousBuffer();
$this->des[2]->disableContinuousBuffer();
}
}
/**
* Creates the key schedule
*
* @see \phpseclib\Crypt\DES::_setupKey()
* @see \phpseclib\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();
}
/**
* Sets the internal crypt engine
*
* @see \phpseclib\Crypt\Base::__construct()
* @see \phpseclib\Crypt\Base::setPreferredEngine()
* @param int $engine
* @access public
* @return int
*/
function setPreferredEngine($engine)
{
if ($this->mode_3cbc) {
$this->des[0]->setPreferredEngine($engine);
$this->des[1]->setPreferredEngine($engine);
$this->des[2]->setPreferredEngine($engine);
}
return parent::setPreferredEngine($engine);
}
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php000064400000111700151156520650017177
0ustar00<?php
/**
* Pure-PHP implementation of Twofish.
*
* Uses mcrypt, if available, and an internal implementation, otherwise.
*
* PHP version 5
*
* Useful resources are as follows:
*
* - {@link http://en.wikipedia.org/wiki/Twofish Wikipedia description of
Twofish}
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $twofish = new \phpseclib\Crypt\Twofish();
*
* $twofish->setKey('12345678901234567890123456789012');
*
* $plaintext = str_repeat('a', 1024);
*
* echo $twofish->decrypt($twofish->encrypt($plaintext));
* ?>
* </code>
*
* @category Crypt
* @package Twofish
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Pure-PHP implementation of Twofish.
*
* @package Twofish
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
* @access public
*/
class Twofish extends Base
{
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib\Crypt\Base::cipher_name_mcrypt
* @var string
* @access private
*/
var $cipher_name_mcrypt = 'twofish';
/**
* Optimizing value while CFB-encrypting
*
* @see \phpseclib\Crypt\Base::cfb_init_len
* @var int
* @access private
*/
var $cfb_init_len = 800;
/**
* Q-Table
*
* @var array
* @access private
*/
var $q0 = array(
0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76,
0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38,
0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C,
0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48,
0xF2, 0xD0, 0x8B, 0x30, 0x84, 0x54, 0xDF, 0x23,
0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82,
0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C,
0xA6, 0xEB, 0xA5, 0xBE, 0x16, 0x0C, 0xE3, 0x61,
0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B,
0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1,
0xE1, 0xE6, 0xBD, 0x45, 0xE2, 0xF4, 0xB6, 0x66,
0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7,
0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA,
0xEA, 0x77, 0x39, 0xAF, 0x33, 0xC9, 0x62, 0x71,
0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8,
0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7,
0xA1, 0x1D, 0xAA, 0xED, 0x06, 0x70, 0xB2, 0xD2,
0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90,
0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB,
0x9E, 0x9C, 0x52, 0x1B, 0x5F, 0x93, 0x0A, 0xEF,
0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B,
0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64,
0x2A, 0xCE, 0xCB, 0x2F, 0xFC, 0x97, 0x05, 0x7A,
0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A,
0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02,
0xB8, 0xDA, 0xB0, 0x17, 0x55, 0x1F, 0x8A, 0x7D,
0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72,
0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34,
0x6E, 0x50, 0xDE, 0x68, 0x65, 0xBC, 0xDB, 0xF8,
0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4,
0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00,
0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0
);
/**
* Q-Table
*
* @var array
* @access private
*/
var $q1 = array(
0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8,
0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B,
0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1,
0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F,
0x5E, 0xBA, 0xAE, 0x5B, 0x8A, 0x00, 0xBC, 0x9D,
0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5,
0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3,
0xB2, 0x73, 0x4C, 0x54, 0x92, 0x74, 0x36, 0x51,
0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96,
0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C,
0x13, 0x95, 0x9C, 0xC7, 0x24, 0x46, 0x3B, 0x70,
0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8,
0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC,
0x03, 0x6F, 0x08, 0xBF, 0x40, 0xE7, 0x2B, 0xE2,
0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9,
0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17,
0x66, 0x94, 0xA1, 0x1D, 0x3D, 0xF0, 0xDE, 0xB3,
0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E,
0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49,
0x81, 0x88, 0xEE, 0x21, 0xC4, 0x1A, 0xEB, 0xD9,
0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01,
0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48,
0x4F, 0xF2, 0x65, 0x8E, 0x78, 0x5C, 0x58, 0x19,
0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64,
0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5,
0xCE, 0xE9, 0x68, 0x44, 0xE0, 0x4D, 0x43, 0x69,
0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E,
0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC,
0x22, 0xC9, 0xC0, 0x9B, 0x89, 0xD4, 0xED, 0xAB,
0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9,
0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2,
0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91
);
/**
* M-Table
*
* @var array
* @access private
*/
var $m0 = array(
0xBCBC3275, 0xECEC21F3, 0x202043C6, 0xB3B3C9F4, 0xDADA03DB,
0x02028B7B, 0xE2E22BFB, 0x9E9EFAC8,
0xC9C9EC4A, 0xD4D409D3, 0x18186BE6, 0x1E1E9F6B, 0x98980E45,
0xB2B2387D, 0xA6A6D2E8, 0x2626B74B,
0x3C3C57D6, 0x93938A32, 0x8282EED8, 0x525298FD, 0x7B7BD437,
0xBBBB3771, 0x5B5B97F1, 0x474783E1,
0x24243C30, 0x5151E20F, 0xBABAC6F8, 0x4A4AF31B, 0xBFBF4887,
0x0D0D70FA, 0xB0B0B306, 0x7575DE3F,
0xD2D2FD5E, 0x7D7D20BA, 0x666631AE, 0x3A3AA35B, 0x59591C8A,
0x00000000, 0xCDCD93BC, 0x1A1AE09D,
0xAEAE2C6D, 0x7F7FABC1, 0x2B2BC7B1, 0xBEBEB90E, 0xE0E0A080,
0x8A8A105D, 0x3B3B52D2, 0x6464BAD5,
0xD8D888A0, 0xE7E7A584, 0x5F5FE807, 0x1B1B1114, 0x2C2CC2B5,
0xFCFCB490, 0x3131272C, 0x808065A3,
0x73732AB2, 0x0C0C8173, 0x79795F4C, 0x6B6B4154, 0x4B4B0292,
0x53536974, 0x94948F36, 0x83831F51,
0x2A2A3638, 0xC4C49CB0, 0x2222C8BD, 0xD5D5F85A, 0xBDBDC3FC,
0x48487860, 0xFFFFCE62, 0x4C4C0796,
0x4141776C, 0xC7C7E642, 0xEBEB24F7, 0x1C1C1410, 0x5D5D637C,
0x36362228, 0x6767C027, 0xE9E9AF8C,
0x4444F913, 0x1414EA95, 0xF5F5BB9C, 0xCFCF18C7, 0x3F3F2D24,
0xC0C0E346, 0x7272DB3B, 0x54546C70,
0x29294CCA, 0xF0F035E3, 0x0808FE85, 0xC6C617CB, 0xF3F34F11,
0x8C8CE4D0, 0xA4A45993, 0xCACA96B8,
0x68683BA6, 0xB8B84D83, 0x38382820, 0xE5E52EFF, 0xADAD569F,
0x0B0B8477, 0xC8C81DC3, 0x9999FFCC,
0x5858ED03, 0x19199A6F, 0x0E0E0A08, 0x95957EBF, 0x70705040,
0xF7F730E7, 0x6E6ECF2B, 0x1F1F6EE2,
0xB5B53D79, 0x09090F0C, 0x616134AA, 0x57571682, 0x9F9F0B41,
0x9D9D803A, 0x111164EA, 0x2525CDB9,
0xAFAFDDE4, 0x4545089A, 0xDFDF8DA4, 0xA3A35C97, 0xEAEAD57E,
0x353558DA, 0xEDEDD07A, 0x4343FC17,
0xF8F8CB66, 0xFBFBB194, 0x3737D3A1, 0xFAFA401D, 0xC2C2683D,
0xB4B4CCF0, 0x32325DDE, 0x9C9C71B3,
0x5656E70B, 0xE3E3DA72, 0x878760A7, 0x15151B1C, 0xF9F93AEF,
0x6363BFD1, 0x3434A953, 0x9A9A853E,
0xB1B1428F, 0x7C7CD133, 0x88889B26, 0x3D3DA65F, 0xA1A1D7EC,
0xE4E4DF76, 0x8181942A, 0x91910149,
0x0F0FFB81, 0xEEEEAA88, 0x161661EE, 0xD7D77321, 0x9797F5C4,
0xA5A5A81A, 0xFEFE3FEB, 0x6D6DB5D9,
0x7878AEC5, 0xC5C56D39, 0x1D1DE599, 0x7676A4CD, 0x3E3EDCAD,
0xCBCB6731, 0xB6B6478B, 0xEFEF5B01,
0x12121E18, 0x6060C523, 0x6A6AB0DD, 0x4D4DF61F, 0xCECEE94E,
0xDEDE7C2D, 0x55559DF9, 0x7E7E5A48,
0x2121B24F, 0x03037AF2, 0xA0A02665, 0x5E5E198E, 0x5A5A6678,
0x65654B5C, 0x62624E58, 0xFDFD4519,
0x0606F48D, 0x404086E5, 0xF2F2BE98, 0x3333AC57, 0x17179067,
0x05058E7F, 0xE8E85E05, 0x4F4F7D64,
0x89896AAF, 0x10109563, 0x74742FB6, 0x0A0A75FE, 0x5C5C92F5,
0x9B9B74B7, 0x2D2D333C, 0x3030D6A5,
0x2E2E49CE, 0x494989E9, 0x46467268, 0x77775544, 0xA8A8D8E0,
0x9696044D, 0x2828BD43, 0xA9A92969,
0xD9D97929, 0x8686912E, 0xD1D187AC, 0xF4F44A15, 0x8D8D1559,
0xD6D682A8, 0xB9B9BC0A, 0x42420D9E,
0xF6F6C16E, 0x2F2FB847, 0xDDDD06DF, 0x23233934, 0xCCCC6235,
0xF1F1C46A, 0xC1C112CF, 0x8585EBDC,
0x8F8F9E22, 0x7171A1C9, 0x9090F0C0, 0xAAAA539B, 0x0101F189,
0x8B8BE1D4, 0x4E4E8CED, 0x8E8E6FAB,
0xABABA212, 0x6F6F3EA2, 0xE6E6540D, 0xDBDBF252, 0x92927BBB,
0xB7B7B602, 0x6969CA2F, 0x3939D9A9,
0xD3D30CD7, 0xA7A72361, 0xA2A2AD1E, 0xC3C399B4, 0x6C6C4450,
0x07070504, 0x04047FF6, 0x272746C2,
0xACACA716, 0xD0D07625, 0x50501386, 0xDCDCF756, 0x84841A55,
0xE1E15109, 0x7A7A25BE, 0x1313EF91
);
/**
* M-Table
*
* @var array
* @access private
*/
var $m1 = array(
0xA9D93939, 0x67901717, 0xB3719C9C, 0xE8D2A6A6, 0x04050707,
0xFD985252, 0xA3658080, 0x76DFE4E4,
0x9A084545, 0x92024B4B, 0x80A0E0E0, 0x78665A5A, 0xE4DDAFAF,
0xDDB06A6A, 0xD1BF6363, 0x38362A2A,
0x0D54E6E6, 0xC6432020, 0x3562CCCC, 0x98BEF2F2, 0x181E1212,
0xF724EBEB, 0xECD7A1A1, 0x6C774141,
0x43BD2828, 0x7532BCBC, 0x37D47B7B, 0x269B8888, 0xFA700D0D,
0x13F94444, 0x94B1FBFB, 0x485A7E7E,
0xF27A0303, 0xD0E48C8C, 0x8B47B6B6, 0x303C2424, 0x84A5E7E7,
0x54416B6B, 0xDF06DDDD, 0x23C56060,
0x1945FDFD, 0x5BA33A3A, 0x3D68C2C2, 0x59158D8D, 0xF321ECEC,
0xAE316666, 0xA23E6F6F, 0x82165757,
0x63951010, 0x015BEFEF, 0x834DB8B8, 0x2E918686, 0xD9B56D6D,
0x511F8383, 0x9B53AAAA, 0x7C635D5D,
0xA63B6868, 0xEB3FFEFE, 0xA5D63030, 0xBE257A7A, 0x16A7ACAC,
0x0C0F0909, 0xE335F0F0, 0x6123A7A7,
0xC0F09090, 0x8CAFE9E9, 0x3A809D9D, 0xF5925C5C, 0x73810C0C,
0x2C273131, 0x2576D0D0, 0x0BE75656,
0xBB7B9292, 0x4EE9CECE, 0x89F10101, 0x6B9F1E1E, 0x53A93434,
0x6AC4F1F1, 0xB499C3C3, 0xF1975B5B,
0xE1834747, 0xE66B1818, 0xBDC82222, 0x450E9898, 0xE26E1F1F,
0xF4C9B3B3, 0xB62F7474, 0x66CBF8F8,
0xCCFF9999, 0x95EA1414, 0x03ED5858, 0x56F7DCDC, 0xD4E18B8B,
0x1C1B1515, 0x1EADA2A2, 0xD70CD3D3,
0xFB2BE2E2, 0xC31DC8C8, 0x8E195E5E, 0xB5C22C2C, 0xE9894949,
0xCF12C1C1, 0xBF7E9595, 0xBA207D7D,
0xEA641111, 0x77840B0B, 0x396DC5C5, 0xAF6A8989, 0x33D17C7C,
0xC9A17171, 0x62CEFFFF, 0x7137BBBB,
0x81FB0F0F, 0x793DB5B5, 0x0951E1E1, 0xADDC3E3E, 0x242D3F3F,
0xCDA47676, 0xF99D5555, 0xD8EE8282,
0xE5864040, 0xC5AE7878, 0xB9CD2525, 0x4D049696, 0x44557777,
0x080A0E0E, 0x86135050, 0xE730F7F7,
0xA1D33737, 0x1D40FAFA, 0xAA346161, 0xED8C4E4E, 0x06B3B0B0,
0x706C5454, 0xB22A7373, 0xD2523B3B,
0x410B9F9F, 0x7B8B0202, 0xA088D8D8, 0x114FF3F3, 0x3167CBCB,
0xC2462727, 0x27C06767, 0x90B4FCFC,
0x20283838, 0xF67F0404, 0x60784848, 0xFF2EE5E5, 0x96074C4C,
0x5C4B6565, 0xB1C72B2B, 0xAB6F8E8E,
0x9E0D4242, 0x9CBBF5F5, 0x52F2DBDB, 0x1BF34A4A, 0x5FA63D3D,
0x9359A4A4, 0x0ABCB9B9, 0xEF3AF9F9,
0x91EF1313, 0x85FE0808, 0x49019191, 0xEE611616, 0x2D7CDEDE,
0x4FB22121, 0x8F42B1B1, 0x3BDB7272,
0x47B82F2F, 0x8748BFBF, 0x6D2CAEAE, 0x46E3C0C0, 0xD6573C3C,
0x3E859A9A, 0x6929A9A9, 0x647D4F4F,
0x2A948181, 0xCE492E2E, 0xCB17C6C6, 0x2FCA6969, 0xFCC3BDBD,
0x975CA3A3, 0x055EE8E8, 0x7AD0EDED,
0xAC87D1D1, 0x7F8E0505, 0xD5BA6464, 0x1AA8A5A5, 0x4BB72626,
0x0EB9BEBE, 0xA7608787, 0x5AF8D5D5,
0x28223636, 0x14111B1B, 0x3FDE7575, 0x2979D9D9, 0x88AAEEEE,
0x3C332D2D, 0x4C5F7979, 0x02B6B7B7,
0xB896CACA, 0xDA583535, 0xB09CC4C4, 0x17FC4343, 0x551A8484,
0x1FF64D4D, 0x8A1C5959, 0x7D38B2B2,
0x57AC3333, 0xC718CFCF, 0x8DF40606, 0x74695353, 0xB7749B9B,
0xC4F59797, 0x9F56ADAD, 0x72DAE3E3,
0x7ED5EAEA, 0x154AF4F4, 0x229E8F8F, 0x12A2ABAB, 0x584E6262,
0x07E85F5F, 0x99E51D1D, 0x34392323,
0x6EC1F6F6, 0x50446C6C, 0xDE5D3232, 0x68724646, 0x6526A0A0,
0xBC93CDCD, 0xDB03DADA, 0xF8C6BABA,
0xC8FA9E9E, 0xA882D6D6, 0x2BCF6E6E, 0x40507070, 0xDCEB8585,
0xFE750A0A, 0x328A9393, 0xA48DDFDF,
0xCA4C2929, 0x10141C1C, 0x2173D7D7, 0xF0CCB4B4, 0xD309D4D4,
0x5D108A8A, 0x0FE25151, 0x00000000,
0x6F9A1919, 0x9DE01A1A, 0x368F9494, 0x42E6C7C7, 0x4AECC9C9,
0x5EFDD2D2, 0xC1AB7F7F, 0xE0D8A8A8
);
/**
* M-Table
*
* @var array
* @access private
*/
var $m2 = array(
0xBC75BC32, 0xECF3EC21, 0x20C62043, 0xB3F4B3C9, 0xDADBDA03,
0x027B028B, 0xE2FBE22B, 0x9EC89EFA,
0xC94AC9EC, 0xD4D3D409, 0x18E6186B, 0x1E6B1E9F, 0x9845980E,
0xB27DB238, 0xA6E8A6D2, 0x264B26B7,
0x3CD63C57, 0x9332938A, 0x82D882EE, 0x52FD5298, 0x7B377BD4,
0xBB71BB37, 0x5BF15B97, 0x47E14783,
0x2430243C, 0x510F51E2, 0xBAF8BAC6, 0x4A1B4AF3, 0xBF87BF48,
0x0DFA0D70, 0xB006B0B3, 0x753F75DE,
0xD25ED2FD, 0x7DBA7D20, 0x66AE6631, 0x3A5B3AA3, 0x598A591C,
0x00000000, 0xCDBCCD93, 0x1A9D1AE0,
0xAE6DAE2C, 0x7FC17FAB, 0x2BB12BC7, 0xBE0EBEB9, 0xE080E0A0,
0x8A5D8A10, 0x3BD23B52, 0x64D564BA,
0xD8A0D888, 0xE784E7A5, 0x5F075FE8, 0x1B141B11, 0x2CB52CC2,
0xFC90FCB4, 0x312C3127, 0x80A38065,
0x73B2732A, 0x0C730C81, 0x794C795F, 0x6B546B41, 0x4B924B02,
0x53745369, 0x9436948F, 0x8351831F,
0x2A382A36, 0xC4B0C49C, 0x22BD22C8, 0xD55AD5F8, 0xBDFCBDC3,
0x48604878, 0xFF62FFCE, 0x4C964C07,
0x416C4177, 0xC742C7E6, 0xEBF7EB24, 0x1C101C14, 0x5D7C5D63,
0x36283622, 0x672767C0, 0xE98CE9AF,
0x441344F9, 0x149514EA, 0xF59CF5BB, 0xCFC7CF18, 0x3F243F2D,
0xC046C0E3, 0x723B72DB, 0x5470546C,
0x29CA294C, 0xF0E3F035, 0x088508FE, 0xC6CBC617, 0xF311F34F,
0x8CD08CE4, 0xA493A459, 0xCAB8CA96,
0x68A6683B, 0xB883B84D, 0x38203828, 0xE5FFE52E, 0xAD9FAD56,
0x0B770B84, 0xC8C3C81D, 0x99CC99FF,
0x580358ED, 0x196F199A, 0x0E080E0A, 0x95BF957E, 0x70407050,
0xF7E7F730, 0x6E2B6ECF, 0x1FE21F6E,
0xB579B53D, 0x090C090F, 0x61AA6134, 0x57825716, 0x9F419F0B,
0x9D3A9D80, 0x11EA1164, 0x25B925CD,
0xAFE4AFDD, 0x459A4508, 0xDFA4DF8D, 0xA397A35C, 0xEA7EEAD5,
0x35DA3558, 0xED7AEDD0, 0x431743FC,
0xF866F8CB, 0xFB94FBB1, 0x37A137D3, 0xFA1DFA40, 0xC23DC268,
0xB4F0B4CC, 0x32DE325D, 0x9CB39C71,
0x560B56E7, 0xE372E3DA, 0x87A78760, 0x151C151B, 0xF9EFF93A,
0x63D163BF, 0x345334A9, 0x9A3E9A85,
0xB18FB142, 0x7C337CD1, 0x8826889B, 0x3D5F3DA6, 0xA1ECA1D7,
0xE476E4DF, 0x812A8194, 0x91499101,
0x0F810FFB, 0xEE88EEAA, 0x16EE1661, 0xD721D773, 0x97C497F5,
0xA51AA5A8, 0xFEEBFE3F, 0x6DD96DB5,
0x78C578AE, 0xC539C56D, 0x1D991DE5, 0x76CD76A4, 0x3EAD3EDC,
0xCB31CB67, 0xB68BB647, 0xEF01EF5B,
0x1218121E, 0x602360C5, 0x6ADD6AB0, 0x4D1F4DF6, 0xCE4ECEE9,
0xDE2DDE7C, 0x55F9559D, 0x7E487E5A,
0x214F21B2, 0x03F2037A, 0xA065A026, 0x5E8E5E19, 0x5A785A66,
0x655C654B, 0x6258624E, 0xFD19FD45,
0x068D06F4, 0x40E54086, 0xF298F2BE, 0x335733AC, 0x17671790,
0x057F058E, 0xE805E85E, 0x4F644F7D,
0x89AF896A, 0x10631095, 0x74B6742F, 0x0AFE0A75, 0x5CF55C92,
0x9BB79B74, 0x2D3C2D33, 0x30A530D6,
0x2ECE2E49, 0x49E94989, 0x46684672, 0x77447755, 0xA8E0A8D8,
0x964D9604, 0x284328BD, 0xA969A929,
0xD929D979, 0x862E8691, 0xD1ACD187, 0xF415F44A, 0x8D598D15,
0xD6A8D682, 0xB90AB9BC, 0x429E420D,
0xF66EF6C1, 0x2F472FB8, 0xDDDFDD06, 0x23342339, 0xCC35CC62,
0xF16AF1C4, 0xC1CFC112, 0x85DC85EB,
0x8F228F9E, 0x71C971A1, 0x90C090F0, 0xAA9BAA53, 0x018901F1,
0x8BD48BE1, 0x4EED4E8C, 0x8EAB8E6F,
0xAB12ABA2, 0x6FA26F3E, 0xE60DE654, 0xDB52DBF2, 0x92BB927B,
0xB702B7B6, 0x692F69CA, 0x39A939D9,
0xD3D7D30C, 0xA761A723, 0xA21EA2AD, 0xC3B4C399, 0x6C506C44,
0x07040705, 0x04F6047F, 0x27C22746,
0xAC16ACA7, 0xD025D076, 0x50865013, 0xDC56DCF7, 0x8455841A,
0xE109E151, 0x7ABE7A25, 0x139113EF
);
/**
* M-Table
*
* @var array
* @access private
*/
var $m3 = array(
0xD939A9D9, 0x90176790, 0x719CB371, 0xD2A6E8D2, 0x05070405,
0x9852FD98, 0x6580A365, 0xDFE476DF,
0x08459A08, 0x024B9202, 0xA0E080A0, 0x665A7866, 0xDDAFE4DD,
0xB06ADDB0, 0xBF63D1BF, 0x362A3836,
0x54E60D54, 0x4320C643, 0x62CC3562, 0xBEF298BE, 0x1E12181E,
0x24EBF724, 0xD7A1ECD7, 0x77416C77,
0xBD2843BD, 0x32BC7532, 0xD47B37D4, 0x9B88269B, 0x700DFA70,
0xF94413F9, 0xB1FB94B1, 0x5A7E485A,
0x7A03F27A, 0xE48CD0E4, 0x47B68B47, 0x3C24303C, 0xA5E784A5,
0x416B5441, 0x06DDDF06, 0xC56023C5,
0x45FD1945, 0xA33A5BA3, 0x68C23D68, 0x158D5915, 0x21ECF321,
0x3166AE31, 0x3E6FA23E, 0x16578216,
0x95106395, 0x5BEF015B, 0x4DB8834D, 0x91862E91, 0xB56DD9B5,
0x1F83511F, 0x53AA9B53, 0x635D7C63,
0x3B68A63B, 0x3FFEEB3F, 0xD630A5D6, 0x257ABE25, 0xA7AC16A7,
0x0F090C0F, 0x35F0E335, 0x23A76123,
0xF090C0F0, 0xAFE98CAF, 0x809D3A80, 0x925CF592, 0x810C7381,
0x27312C27, 0x76D02576, 0xE7560BE7,
0x7B92BB7B, 0xE9CE4EE9, 0xF10189F1, 0x9F1E6B9F, 0xA93453A9,
0xC4F16AC4, 0x99C3B499, 0x975BF197,
0x8347E183, 0x6B18E66B, 0xC822BDC8, 0x0E98450E, 0x6E1FE26E,
0xC9B3F4C9, 0x2F74B62F, 0xCBF866CB,
0xFF99CCFF, 0xEA1495EA, 0xED5803ED, 0xF7DC56F7, 0xE18BD4E1,
0x1B151C1B, 0xADA21EAD, 0x0CD3D70C,
0x2BE2FB2B, 0x1DC8C31D, 0x195E8E19, 0xC22CB5C2, 0x8949E989,
0x12C1CF12, 0x7E95BF7E, 0x207DBA20,
0x6411EA64, 0x840B7784, 0x6DC5396D, 0x6A89AF6A, 0xD17C33D1,
0xA171C9A1, 0xCEFF62CE, 0x37BB7137,
0xFB0F81FB, 0x3DB5793D, 0x51E10951, 0xDC3EADDC, 0x2D3F242D,
0xA476CDA4, 0x9D55F99D, 0xEE82D8EE,
0x8640E586, 0xAE78C5AE, 0xCD25B9CD, 0x04964D04, 0x55774455,
0x0A0E080A, 0x13508613, 0x30F7E730,
0xD337A1D3, 0x40FA1D40, 0x3461AA34, 0x8C4EED8C, 0xB3B006B3,
0x6C54706C, 0x2A73B22A, 0x523BD252,
0x0B9F410B, 0x8B027B8B, 0x88D8A088, 0x4FF3114F, 0x67CB3167,
0x4627C246, 0xC06727C0, 0xB4FC90B4,
0x28382028, 0x7F04F67F, 0x78486078, 0x2EE5FF2E, 0x074C9607,
0x4B655C4B, 0xC72BB1C7, 0x6F8EAB6F,
0x0D429E0D, 0xBBF59CBB, 0xF2DB52F2, 0xF34A1BF3, 0xA63D5FA6,
0x59A49359, 0xBCB90ABC, 0x3AF9EF3A,
0xEF1391EF, 0xFE0885FE, 0x01914901, 0x6116EE61, 0x7CDE2D7C,
0xB2214FB2, 0x42B18F42, 0xDB723BDB,
0xB82F47B8, 0x48BF8748, 0x2CAE6D2C, 0xE3C046E3, 0x573CD657,
0x859A3E85, 0x29A96929, 0x7D4F647D,
0x94812A94, 0x492ECE49, 0x17C6CB17, 0xCA692FCA, 0xC3BDFCC3,
0x5CA3975C, 0x5EE8055E, 0xD0ED7AD0,
0x87D1AC87, 0x8E057F8E, 0xBA64D5BA, 0xA8A51AA8, 0xB7264BB7,
0xB9BE0EB9, 0x6087A760, 0xF8D55AF8,
0x22362822, 0x111B1411, 0xDE753FDE, 0x79D92979, 0xAAEE88AA,
0x332D3C33, 0x5F794C5F, 0xB6B702B6,
0x96CAB896, 0x5835DA58, 0x9CC4B09C, 0xFC4317FC, 0x1A84551A,
0xF64D1FF6, 0x1C598A1C, 0x38B27D38,
0xAC3357AC, 0x18CFC718, 0xF4068DF4, 0x69537469, 0x749BB774,
0xF597C4F5, 0x56AD9F56, 0xDAE372DA,
0xD5EA7ED5, 0x4AF4154A, 0x9E8F229E, 0xA2AB12A2, 0x4E62584E,
0xE85F07E8, 0xE51D99E5, 0x39233439,
0xC1F66EC1, 0x446C5044, 0x5D32DE5D, 0x72466872, 0x26A06526,
0x93CDBC93, 0x03DADB03, 0xC6BAF8C6,
0xFA9EC8FA, 0x82D6A882, 0xCF6E2BCF, 0x50704050, 0xEB85DCEB,
0x750AFE75, 0x8A93328A, 0x8DDFA48D,
0x4C29CA4C, 0x141C1014, 0x73D72173, 0xCCB4F0CC, 0x09D4D309,
0x108A5D10, 0xE2510FE2, 0x00000000,
0x9A196F9A, 0xE01A9DE0, 0x8F94368F, 0xE6C742E6, 0xECC94AEC,
0xFDD25EFD, 0xAB7FC1AB, 0xD8A8E0D8
);
/**
* The Key Schedule Array
*
* @var array
* @access private
*/
var $K = array();
/**
* The Key depended S-Table 0
*
* @var array
* @access private
*/
var $S0 = array();
/**
* The Key depended S-Table 1
*
* @var array
* @access private
*/
var $S1 = array();
/**
* The Key depended S-Table 2
*
* @var array
* @access private
*/
var $S2 = array();
/**
* The Key depended S-Table 3
*
* @var array
* @access private
*/
var $S3 = array();
/**
* Holds the last used key
*
* @var array
* @access private
*/
var $kl;
/**
* The Key Length (in bytes)
*
* @see Crypt_Twofish::setKeyLength()
* @var int
* @access private
*/
var $key_length = 16;
/**
* Sets the key length.
*
* Valid key lengths are 128, 192 or 256 bits
*
* @access public
* @param int $length
*/
function setKeyLength($length)
{
switch (true) {
case $length <= 128:
$this->key_length = 16;
break;
case $length <= 192:
$this->key_length = 24;
break;
default:
$this->key_length = 32;
}
parent::setKeyLength($length);
}
/**
* Setup the key (expansion)
*
* @see \phpseclib\Crypt\Base::_setupKey()
* @access private
*/
function _setupKey()
{
if (isset($this->kl['key']) && $this->key
=== $this->kl['key']) {
// already expanded
return;
}
$this->kl = array('key' => $this->key);
/* Key expanding and generating the key-depended s-boxes */
$le_longs = unpack('V*', $this->key);
$key = unpack('C*', $this->key);
$m0 = $this->m0;
$m1 = $this->m1;
$m2 = $this->m2;
$m3 = $this->m3;
$q0 = $this->q0;
$q1 = $this->q1;
$K = $S0 = $S1 = $S2 = $S3 = array();
switch (strlen($this->key)) {
case 16:
list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[1],
$le_longs[2]);
list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[3],
$le_longs[4]);
for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) {
$A = $m0[$q0[$q0[$i] ^ $key[ 9]] ^ $key[1]] ^
$m1[$q0[$q1[$i] ^ $key[10]] ^ $key[2]] ^
$m2[$q1[$q0[$i] ^ $key[11]] ^ $key[3]] ^
$m3[$q1[$q1[$i] ^ $key[12]] ^ $key[4]];
$B = $m0[$q0[$q0[$j] ^ $key[13]] ^ $key[5]] ^
$m1[$q0[$q1[$j] ^ $key[14]] ^ $key[6]] ^
$m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^
$m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff);
$A = $this->safe_intval($A + $B);
$K[] = $A;
$A = $this->safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
}
for ($i = 0; $i < 256; ++$i) {
$S0[$i] = $m0[$q0[$q0[$i] ^ $s4] ^ $s0];
$S1[$i] = $m1[$q0[$q1[$i] ^ $s5] ^ $s1];
$S2[$i] = $m2[$q1[$q0[$i] ^ $s6] ^ $s2];
$S3[$i] = $m3[$q1[$q1[$i] ^ $s7] ^ $s3];
}
break;
case 24:
list($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[1],
$le_longs[2]);
list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[3],
$le_longs[4]);
list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[5],
$le_longs[6]);
for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) {
$A = $m0[$q0[$q0[$q1[$i] ^ $key[17]] ^ $key[ 9]] ^
$key[1]] ^
$m1[$q0[$q1[$q1[$i] ^ $key[18]] ^ $key[10]] ^
$key[2]] ^
$m2[$q1[$q0[$q0[$i] ^ $key[19]] ^ $key[11]] ^
$key[3]] ^
$m3[$q1[$q1[$q0[$i] ^ $key[20]] ^ $key[12]] ^
$key[4]];
$B = $m0[$q0[$q0[$q1[$j] ^ $key[21]] ^ $key[13]] ^
$key[5]] ^
$m1[$q0[$q1[$q1[$j] ^ $key[22]] ^ $key[14]] ^
$key[6]] ^
$m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^
$key[7]] ^
$m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^
$key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff);
$A = $this->safe_intval($A + $B);
$K[] = $A;
$A = $this->safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
}
for ($i = 0; $i < 256; ++$i) {
$S0[$i] = $m0[$q0[$q0[$q1[$i] ^ $s8] ^ $s4] ^ $s0];
$S1[$i] = $m1[$q0[$q1[$q1[$i] ^ $s9] ^ $s5] ^ $s1];
$S2[$i] = $m2[$q1[$q0[$q0[$i] ^ $sa] ^ $s6] ^ $s2];
$S3[$i] = $m3[$q1[$q1[$q0[$i] ^ $sb] ^ $s7] ^ $s3];
}
break;
default: // 32
list($sf, $se, $sd, $sc) = $this->_mdsrem($le_longs[1],
$le_longs[2]);
list($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[3],
$le_longs[4]);
list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[5],
$le_longs[6]);
list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[7],
$le_longs[8]);
for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) {
$A = $m0[$q0[$q0[$q1[$q1[$i] ^ $key[25]] ^ $key[17]] ^
$key[ 9]] ^ $key[1]] ^
$m1[$q0[$q1[$q1[$q0[$i] ^ $key[26]] ^ $key[18]] ^
$key[10]] ^ $key[2]] ^
$m2[$q1[$q0[$q0[$q0[$i] ^ $key[27]] ^ $key[19]] ^
$key[11]] ^ $key[3]] ^
$m3[$q1[$q1[$q0[$q1[$i] ^ $key[28]] ^ $key[20]] ^
$key[12]] ^ $key[4]];
$B = $m0[$q0[$q0[$q1[$q1[$j] ^ $key[29]] ^ $key[21]] ^
$key[13]] ^ $key[5]] ^
$m1[$q0[$q1[$q1[$q0[$j] ^ $key[30]] ^ $key[22]] ^
$key[14]] ^ $key[6]] ^
$m2[$q1[$q0[$q0[$q0[$j] ^ $key[31]] ^ $key[23]] ^
$key[15]] ^ $key[7]] ^
$m3[$q1[$q1[$q0[$q1[$j] ^ $key[32]] ^ $key[24]] ^
$key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff);
$A = $this->safe_intval($A + $B);
$K[] = $A;
$A = $this->safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
}
for ($i = 0; $i < 256; ++$i) {
$S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4]
^ $s0];
$S1[$i] = $m1[$q0[$q1[$q1[$q0[$i] ^ $sd] ^ $s9] ^ $s5]
^ $s1];
$S2[$i] = $m2[$q1[$q0[$q0[$q0[$i] ^ $se] ^ $sa] ^ $s6]
^ $s2];
$S3[$i] = $m3[$q1[$q1[$q0[$q1[$i] ^ $sf] ^ $sb] ^ $s7]
^ $s3];
}
}
$this->K = $K;
$this->S0 = $S0;
$this->S1 = $S1;
$this->S2 = $S2;
$this->S3 = $S3;
}
/**
* _mdsrem function using by the twofish cipher algorithm
*
* @access private
* @param string $A
* @param string $B
* @return array
*/
function _mdsrem($A, $B)
{
// No gain by unrolling this loop.
for ($i = 0; $i < 8; ++$i) {
// Get most significant coefficient.
$t = 0xff & ($B >> 24);
// Shift the others up.
$B = ($B << 8) | (0xff & ($A >> 24));
$A<<= 8;
$u = $t << 1;
// Subtract the modular polynomial on overflow.
if ($t & 0x80) {
$u^= 0x14d;
}
// Remove t * (a * x^2 + 1).
$B ^= $t ^ ($u << 16);
// Form u = a*t + t/a = t*(a + 1/a).
$u^= 0x7fffffff & ($t >> 1);
// Add the modular polynomial on underflow.
if ($t & 0x01) {
$u^= 0xa6 ;
}
// Remove t * (a + 1/a) * (x^3 + x).
$B^= ($u << 24) | ($u << 8);
}
return array(
0xff & $B >> 24,
0xff & $B >> 16,
0xff & $B >> 8,
0xff & $B);
}
/**
* Encrypts a block
*
* @access private
* @param string $in
* @return string
*/
function _encryptBlock($in)
{
$S0 = $this->S0;
$S1 = $this->S1;
$S2 = $this->S2;
$S3 = $this->S3;
$K = $this->K;
$in = unpack("V4", $in);
$R0 = $K[0] ^ $in[1];
$R1 = $K[1] ^ $in[2];
$R2 = $K[2] ^ $in[3];
$R3 = $K[3] ^ $in[4];
$ki = 7;
while ($ki < 39) {
$t0 = $S0[ $R0 & 0xff] ^
$S1[($R0 >> 8) & 0xff] ^
$S2[($R0 >> 16) & 0xff] ^
$S3[($R0 >> 24) & 0xff];
$t1 = $S0[($R1 >> 24) & 0xff] ^
$S1[ $R1 & 0xff] ^
$S2[($R1 >> 8) & 0xff] ^
$S3[($R1 >> 16) & 0xff];
$R2^= $this->safe_intval($t0 + $t1 + $K[++$ki]);
$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
$R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^
$this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]);
$t0 = $S0[ $R2 & 0xff] ^
$S1[($R2 >> 8) & 0xff] ^
$S2[($R2 >> 16) & 0xff] ^
$S3[($R2 >> 24) & 0xff];
$t1 = $S0[($R3 >> 24) & 0xff] ^
$S1[ $R3 & 0xff] ^
$S2[($R3 >> 8) & 0xff] ^
$S3[($R3 >> 16) & 0xff];
$R0^= $this->safe_intval($t0 + $t1 + $K[++$ki]);
$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
$R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^
$this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]);
}
// @codingStandardsIgnoreStart
return pack("V4", $K[4] ^ $R2,
$K[5] ^ $R3,
$K[6] ^ $R0,
$K[7] ^ $R1);
// @codingStandardsIgnoreEnd
}
/**
* Decrypts a block
*
* @access private
* @param string $in
* @return string
*/
function _decryptBlock($in)
{
$S0 = $this->S0;
$S1 = $this->S1;
$S2 = $this->S2;
$S3 = $this->S3;
$K = $this->K;
$in = unpack("V4", $in);
$R0 = $K[4] ^ $in[1];
$R1 = $K[5] ^ $in[2];
$R2 = $K[6] ^ $in[3];
$R3 = $K[7] ^ $in[4];
$ki = 40;
while ($ki > 8) {
$t0 = $S0[$R0 & 0xff] ^
$S1[$R0 >> 8 & 0xff] ^
$S2[$R0 >> 16 & 0xff] ^
$S3[$R0 >> 24 & 0xff];
$t1 = $S0[$R1 >> 24 & 0xff] ^
$S1[$R1 & 0xff] ^
$S2[$R1 >> 8 & 0xff] ^
$S3[$R1 >> 16 & 0xff];
$R3^= $this->safe_intval($t0 + ($t1 << 1) +
$K[--$ki]);
$R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
$R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^
$this->safe_intval($t0 + $t1 + $K[--$ki]);
$t0 = $S0[$R2 & 0xff] ^
$S1[$R2 >> 8 & 0xff] ^
$S2[$R2 >> 16 & 0xff] ^
$S3[$R2 >> 24 & 0xff];
$t1 = $S0[$R3 >> 24 & 0xff] ^
$S1[$R3 & 0xff] ^
$S2[$R3 >> 8 & 0xff] ^
$S3[$R3 >> 16 & 0xff];
$R1^= $this->safe_intval($t0 + ($t1 << 1) +
$K[--$ki]);
$R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
$R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^
$this->safe_intval($t0 + $t1 + $K[--$ki]);
}
// @codingStandardsIgnoreStart
return pack("V4", $K[0] ^ $R2,
$K[1] ^ $R3,
$K[2] ^ $R0,
$K[3] ^ $R1);
// @codingStandardsIgnoreEnd
}
/**
* Setup the performance-optimized function for de/encrypt()
*
* @see \phpseclib\Crypt\Base::_setupInlineCrypt()
* @access private
*/
function _setupInlineCrypt()
{
$lambda_functions =& self::_getLambdaFunctions();
// Max. 10 Ultra-Hi-optimized inline-crypt functions. After that,
we'll (still) create very fast code, but not the ultimate fast one.
// (Currently, for Crypt_Twofish, one generated $lambda_function
cost on php5.5@32bit ~140kb unfreeable mem and ~240kb on php5.5@64bit)
$gen_hi_opt_code = (bool)(count($lambda_functions) < 10);
// Generation of a unique hash for our generated code
$code_hash = "Crypt_Twofish, {$this->mode}";
if ($gen_hi_opt_code) {
$code_hash = str_pad($code_hash, 32) .
$this->_hashInlineCryptFunction($this->key);
}
$safeint = $this->safe_intval_inline();
if (!isset($lambda_functions[$code_hash])) {
switch (true) {
case $gen_hi_opt_code:
$K = $this->K;
$init_crypt = '
static $S0, $S1, $S2, $S3;
if (!$S0) {
for ($i = 0; $i < 256; ++$i) {
$S0[] = (int)$self->S0[$i];
$S1[] = (int)$self->S1[$i];
$S2[] = (int)$self->S2[$i];
$S3[] = (int)$self->S3[$i];
}
}
';
break;
default:
$K = array();
for ($i = 0; $i < 40; ++$i) {
$K[] = '$K_' . $i;
}
$init_crypt = '
$S0 = $self->S0;
$S1 = $self->S1;
$S2 = $self->S2;
$S3 = $self->S3;
list(' . implode(',', $K) . ')
= $self->K;
';
}
// Generating encrypt code:
$encrypt_block = '
$in = unpack("V4", $in);
$R0 = '.$K[0].' ^ $in[1];
$R1 = '.$K[1].' ^ $in[2];
$R2 = '.$K[2].' ^ $in[3];
$R3 = '.$K[3].' ^ $in[4];
';
for ($ki = 7, $i = 0; $i < 8; ++$i) {
$encrypt_block.= '
$t0 = $S0[ $R0 & 0xff] ^
$S1[($R0 >> 8) & 0xff] ^
$S2[($R0 >> 16) & 0xff] ^
$S3[($R0 >> 24) & 0xff];
$t1 = $S0[($R1 >> 24) & 0xff] ^
$S1[ $R1 & 0xff] ^
$S2[($R1 >> 8) & 0xff] ^
$S3[($R1 >> 16) & 0xff];
$R2^= ' . sprintf($safeint, '$t0 + $t1 +
' . $K[++$ki]) . ';
$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 <<
31);
$R3 = ((($R3 >> 31) & 1) | ($R3 << 1))
^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' .
$K[++$ki] . ')') . ';
$t0 = $S0[ $R2 & 0xff] ^
$S1[($R2 >> 8) & 0xff] ^
$S2[($R2 >> 16) & 0xff] ^
$S3[($R2 >> 24) & 0xff];
$t1 = $S0[($R3 >> 24) & 0xff] ^
$S1[ $R3 & 0xff] ^
$S2[($R3 >> 8) & 0xff] ^
$S3[($R3 >> 16) & 0xff];
$R0^= ' . sprintf($safeint, '($t0 + $t1 +
' . $K[++$ki] . ')') . ';
$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 <<
31);
$R1 = ((($R1 >> 31) & 1) | ($R1 << 1))
^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' .
$K[++$ki] . ')') . ';
';
}
$encrypt_block.= '
$in = pack("V4", ' . $K[4] . ' ^ $R2,
' . $K[5] . ' ^ $R3,
' . $K[6] . ' ^ $R0,
' . $K[7] . ' ^ $R1);
';
// Generating decrypt code:
$decrypt_block = '
$in = unpack("V4", $in);
$R0 = '.$K[4].' ^ $in[1];
$R1 = '.$K[5].' ^ $in[2];
$R2 = '.$K[6].' ^ $in[3];
$R3 = '.$K[7].' ^ $in[4];
';
for ($ki = 40, $i = 0; $i < 8; ++$i) {
$decrypt_block.= '
$t0 = $S0[$R0 & 0xff] ^
$S1[$R0 >> 8 & 0xff] ^
$S2[$R0 >> 16 & 0xff] ^
$S3[$R0 >> 24 & 0xff];
$t1 = $S0[$R1 >> 24 & 0xff] ^
$S1[$R1 & 0xff] ^
$S2[$R1 >> 8 & 0xff] ^
$S3[$R1 >> 16 & 0xff];
$R3^= ' . sprintf($safeint, '$t0 + ($t1
<< 1) + ' . $K[--$ki]) . ';
$R3 = $R3 >> 1 & 0x7fffffff | $R3 <<
31;
$R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^
' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] .
')') . ';
$t0 = $S0[$R2 & 0xff] ^
$S1[$R2 >> 8 & 0xff] ^
$S2[$R2 >> 16 & 0xff] ^
$S3[$R2 >> 24 & 0xff];
$t1 = $S0[$R3 >> 24 & 0xff] ^
$S1[$R3 & 0xff] ^
$S2[$R3 >> 8 & 0xff] ^
$S3[$R3 >> 16 & 0xff];
$R1^= ' . sprintf($safeint, '$t0 + ($t1
<< 1) + ' . $K[--$ki]) . ';
$R1 = $R1 >> 1 & 0x7fffffff | $R1 <<
31;
$R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^
' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] .
')') . ';
';
}
$decrypt_block.= '
$in = pack("V4", ' . $K[0] . ' ^ $R2,
' . $K[1] . ' ^ $R3,
' . $K[2] . ' ^ $R0,
' . $K[3] . ' ^ $R1);
';
$lambda_functions[$code_hash] =
$this->_createInlineCryptFunction(
array(
'init_crypt' => $init_crypt,
'init_encrypt' => '',
'init_decrypt' => '',
'encrypt_block' => $encrypt_block,
'decrypt_block' => $decrypt_block
)
);
}
$this->inline_crypt = $lambda_functions[$code_hash];
}
}
vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php000064400000047570151156520650016101
0ustar00<?php
/**
* Pure-PHP ANSI Decoder
*
* PHP version 5
*
* If you call read() in \phpseclib\Net\SSH2 you may get {@link
http://en.wikipedia.org/wiki/ANSI_escape_code ANSI escape codes} back.
* They'd look like chr(0x1B) . '[00m' or whatever (0x1B =
ESC). They tell a
* {@link http://en.wikipedia.org/wiki/Terminal_emulator terminal emulator}
how to format the characters, what
* color to display them in, etc. \phpseclib\File\ANSI is a {@link
http://en.wikipedia.org/wiki/VT100 VT100} terminal emulator.
*
* @category File
* @package ANSI
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2012 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\File;
/**
* Pure-PHP ANSI Decoder
*
* @package ANSI
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class ANSI
{
/**
* Max Width
*
* @var int
* @access private
*/
var $max_x;
/**
* Max Height
*
* @var int
* @access private
*/
var $max_y;
/**
* Max History
*
* @var int
* @access private
*/
var $max_history;
/**
* History
*
* @var array
* @access private
*/
var $history;
/**
* History Attributes
*
* @var array
* @access private
*/
var $history_attrs;
/**
* Current Column
*
* @var int
* @access private
*/
var $x;
/**
* Current Row
*
* @var int
* @access private
*/
var $y;
/**
* Old Column
*
* @var int
* @access private
*/
var $old_x;
/**
* Old Row
*
* @var int
* @access private
*/
var $old_y;
/**
* An empty attribute cell
*
* @var object
* @access private
*/
var $base_attr_cell;
/**
* The current attribute cell
*
* @var object
* @access private
*/
var $attr_cell;
/**
* An empty attribute row
*
* @var array
* @access private
*/
var $attr_row;
/**
* The current screen text
*
* @var array
* @access private
*/
var $screen;
/**
* The current screen attributes
*
* @var array
* @access private
*/
var $attrs;
/**
* Current ANSI code
*
* @var string
* @access private
*/
var $ansi;
/**
* Tokenization
*
* @var array
* @access private
*/
var $tokenization;
/**
* Default Constructor.
*
* @return \phpseclib\File\ANSI
* @access public
*/
function __construct()
{
$attr_cell = new \stdClass();
$attr_cell->bold = false;
$attr_cell->underline = false;
$attr_cell->blink = false;
$attr_cell->background = 'black';
$attr_cell->foreground = 'white';
$attr_cell->reverse = false;
$this->base_attr_cell = clone $attr_cell;
$this->attr_cell = clone $attr_cell;
$this->setHistory(200);
$this->setDimensions(80, 24);
}
/**
* Set terminal width and height
*
* Resets the screen as well
*
* @param int $x
* @param int $y
* @access public
*/
function setDimensions($x, $y)
{
$this->max_x = $x - 1;
$this->max_y = $y - 1;
$this->x = $this->y = 0;
$this->history = $this->history_attrs = array();
$this->attr_row = array_fill(0, $this->max_x + 2,
$this->base_attr_cell);
$this->screen = array_fill(0, $this->max_y + 1,
'');
$this->attrs = array_fill(0, $this->max_y + 1,
$this->attr_row);
$this->ansi = '';
}
/**
* Set the number of lines that should be logged past the terminal
height
*
* @param int $history
* @access public
*/
function setHistory($history)
{
$this->max_history = $history;
}
/**
* Load a string
*
* @param string $source
* @access public
*/
function loadString($source)
{
$this->setDimensions($this->max_x + 1, $this->max_y + 1);
$this->appendString($source);
}
/**
* Appdend a string
*
* @param string $source
* @access public
*/
function appendString($source)
{
$this->tokenization = array('');
for ($i = 0; $i < strlen($source); $i++) {
if (strlen($this->ansi)) {
$this->ansi.= $source[$i];
$chr = ord($source[$i]);
//
http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements
// single character CSI's not currently supported
switch (true) {
case $this->ansi == "\x1B=":
$this->ansi = '';
continue 2;
case strlen($this->ansi) == 2 && $chr >=
64 && $chr <= 95 && $chr != ord('['):
case strlen($this->ansi) > 2 && $chr
>= 64 && $chr <= 126:
break;
default:
continue 2;
}
$this->tokenization[] = $this->ansi;
$this->tokenization[] = '';
// http://ascii-table.com/ansi-escape-sequences-vt-100.php
switch ($this->ansi) {
case "\x1B[H": // Move cursor to upper left
corner
$this->old_x = $this->x;
$this->old_y = $this->y;
$this->x = $this->y = 0;
break;
case "\x1B[J": // Clear screen from cursor
down
$this->history = array_merge($this->history,
array_slice(array_splice($this->screen, $this->y + 1), 0,
$this->old_y));
$this->screen = array_merge($this->screen,
array_fill($this->y, $this->max_y, ''));
$this->history_attrs =
array_merge($this->history_attrs,
array_slice(array_splice($this->attrs, $this->y + 1), 0,
$this->old_y));
$this->attrs = array_merge($this->attrs,
array_fill($this->y, $this->max_y, $this->attr_row));
if (count($this->history) ==
$this->max_history) {
array_shift($this->history);
array_shift($this->history_attrs);
}
case "\x1B[K": // Clear screen from cursor
right
$this->screen[$this->y] =
substr($this->screen[$this->y], 0, $this->x);
array_splice($this->attrs[$this->y],
$this->x + 1, $this->max_x - $this->x, array_fill($this->x,
$this->max_x - ($this->x - 1), $this->base_attr_cell));
break;
case "\x1B[2K": // Clear entire line
$this->screen[$this->y] = str_repeat('
', $this->x);
$this->attrs[$this->y] = $this->attr_row;
break;
case "\x1B[?1h": // set cursor key to
application
case "\x1B[?25h": // show the cursor
case "\x1B(B": // set united states g0
character set
break;
case "\x1BE": // Move to next line
$this->_newLine();
$this->x = 0;
break;
default:
switch (true) {
case preg_match('#\x1B\[(\d+)B#',
$this->ansi, $match): // Move cursor down n lines
$this->old_y = $this->y;
$this->y+= $match[1];
break;
case
preg_match('#\x1B\[(\d+);(\d+)H#', $this->ansi, $match): //
Move cursor to screen location v,h
$this->old_x = $this->x;
$this->old_y = $this->y;
$this->x = $match[2] - 1;
$this->y = $match[1] - 1;
break;
case preg_match('#\x1B\[(\d+)C#',
$this->ansi, $match): // Move cursor right n lines
$this->old_x = $this->x;
$this->x+= $match[1];
break;
case preg_match('#\x1B\[(\d+)D#',
$this->ansi, $match): // Move cursor left n lines
$this->old_x = $this->x;
$this->x-= $match[1];
if ($this->x < 0) {
$this->x = 0;
}
break;
case
preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): //
Set top and bottom lines of a window
break;
case
preg_match('#\x1B\[(\d*(?:;\d*)*)m#', $this->ansi, $match): //
character attributes
$attr_cell = &$this->attr_cell;
$mods = explode(';', $match[1]);
foreach ($mods as $mod) {
switch ($mod) {
case '':
case '0': // Turn off
character attributes
$attr_cell = clone
$this->base_attr_cell;
break;
case '1': // Turn bold
mode on
$attr_cell->bold = true;
break;
case '4': // Turn
underline mode on
$attr_cell->underline =
true;
break;
case '5': // Turn
blinking mode on
$attr_cell->blink = true;
break;
case '7': // Turn reverse
video on
$attr_cell->reverse =
!$attr_cell->reverse;
$temp =
$attr_cell->background;
$attr_cell->background =
$attr_cell->foreground;
$attr_cell->foreground =
$temp;
break;
default: // set colors
//$front =
$attr_cell->reverse ? &$attr_cell->background :
&$attr_cell->foreground;
$front = &$attr_cell->{
$attr_cell->reverse ? 'background' : 'foreground' };
//$back =
$attr_cell->reverse ? &$attr_cell->foreground :
&$attr_cell->background;
$back = &$attr_cell->{
$attr_cell->reverse ? 'foreground' : 'background' };
switch ($mod) {
//
@codingStandardsIgnoreStart
case '30': $front
= 'black'; break;
case '31': $front
= 'red'; break;
case '32': $front
= 'green'; break;
case '33': $front
= 'yellow'; break;
case '34': $front
= 'blue'; break;
case '35': $front
= 'magenta'; break;
case '36': $front
= 'cyan'; break;
case '37': $front
= 'white'; break;
case '40': $back
= 'black'; break;
case '41': $back
= 'red'; break;
case '42': $back
= 'green'; break;
case '43': $back
= 'yellow'; break;
case '44': $back
= 'blue'; break;
case '45': $back
= 'magenta'; break;
case '46': $back
= 'cyan'; break;
case '47': $back
= 'white'; break;
//
@codingStandardsIgnoreEnd
default:
//user_error('Unsupported attribute: ' . $mod);
$this->ansi =
'';
break 2;
}
}
}
break;
default:
//user_error("{$this->ansi} is
unsupported\r\n");
}
}
$this->ansi = '';
continue;
}
$this->tokenization[count($this->tokenization) - 1].=
$source[$i];
switch ($source[$i]) {
case "\r":
$this->x = 0;
break;
case "\n":
$this->_newLine();
break;
case "\x08": // backspace
if ($this->x) {
$this->x--;
$this->attrs[$this->y][$this->x] = clone
$this->base_attr_cell;
$this->screen[$this->y] = substr_replace(
$this->screen[$this->y],
$source[$i],
$this->x,
1
);
}
break;
case "\x0F": // shift
break;
case "\x1B": // start ANSI escape code
$this->tokenization[count($this->tokenization) -
1] = substr($this->tokenization[count($this->tokenization) - 1], 0,
-1);
//if
(!strlen($this->tokenization[count($this->tokenization) - 1])) {
// array_pop($this->tokenization);
//}
$this->ansi.= "\x1B";
break;
default:
$this->attrs[$this->y][$this->x] = clone
$this->attr_cell;
if ($this->x >
strlen($this->screen[$this->y])) {
$this->screen[$this->y] = str_repeat('
', $this->x);
}
$this->screen[$this->y] = substr_replace(
$this->screen[$this->y],
$source[$i],
$this->x,
1
);
if ($this->x > $this->max_x) {
$this->x = 0;
$this->_newLine();
} else {
$this->x++;
}
}
}
}
/**
* Add a new line
*
* Also update the $this->screen and $this->history buffers
*
* @access private
*/
function _newLine()
{
//if ($this->y < $this->max_y) {
// $this->y++;
//}
while ($this->y >= $this->max_y) {
$this->history = array_merge($this->history,
array(array_shift($this->screen)));
$this->screen[] = '';
$this->history_attrs = array_merge($this->history_attrs,
array(array_shift($this->attrs)));
$this->attrs[] = $this->attr_row;
if (count($this->history) >= $this->max_history) {
array_shift($this->history);
array_shift($this->history_attrs);
}
$this->y--;
}
$this->y++;
}
/**
* Returns the current coordinate without preformating
*
* @access private
* @return string
*/
function _processCoordinate($last_attr, $cur_attr, $char)
{
$output = '';
if ($last_attr != $cur_attr) {
$close = $open = '';
if ($last_attr->foreground != $cur_attr->foreground) {
if ($cur_attr->foreground != 'white') {
$open.= '<span style="color: ' .
$cur_attr->foreground . '">';
}
if ($last_attr->foreground != 'white') {
$close = '</span>' . $close;
}
}
if ($last_attr->background != $cur_attr->background) {
if ($cur_attr->background != 'black') {
$open.= '<span style="background: ' .
$cur_attr->background . '">';
}
if ($last_attr->background != 'black') {
$close = '</span>' . $close;
}
}
if ($last_attr->bold != $cur_attr->bold) {
if ($cur_attr->bold) {
$open.= '<b>';
} else {
$close = '</b>' . $close;
}
}
if ($last_attr->underline != $cur_attr->underline) {
if ($cur_attr->underline) {
$open.= '<u>';
} else {
$close = '</u>' . $close;
}
}
if ($last_attr->blink != $cur_attr->blink) {
if ($cur_attr->blink) {
$open.= '<blink>';
} else {
$close = '</blink>' . $close;
}
}
$output.= $close . $open;
}
$output.= htmlspecialchars($char);
return $output;
}
/**
* Returns the current screen without preformating
*
* @access private
* @return string
*/
function _getScreen()
{
$output = '';
$last_attr = $this->base_attr_cell;
for ($i = 0; $i <= $this->max_y; $i++) {
for ($j = 0; $j <= $this->max_x; $j++) {
$cur_attr = $this->attrs[$i][$j];
$output.= $this->_processCoordinate($last_attr,
$cur_attr, isset($this->screen[$i][$j]) ? $this->screen[$i][$j] :
'');
$last_attr = $this->attrs[$i][$j];
}
$output.= "\r\n";
}
$output = substr($output, 0, -2);
// close any remaining open tags
$output.= $this->_processCoordinate($last_attr,
$this->base_attr_cell, '');
return rtrim($output);
}
/**
* Returns the current screen
*
* @access public
* @return string
*/
function getScreen()
{
return '<pre width="' . ($this->max_x + 1) .
'" style="color: white; background: black">' .
$this->_getScreen() . '</pre>';
}
/**
* Returns the current screen and the x previous lines
*
* @access public
* @return string
*/
function getHistory()
{
$scrollback = '';
$last_attr = $this->base_attr_cell;
for ($i = 0; $i < count($this->history); $i++) {
for ($j = 0; $j <= $this->max_x + 1; $j++) {
$cur_attr = $this->history_attrs[$i][$j];
$scrollback.= $this->_processCoordinate($last_attr,
$cur_attr, isset($this->history[$i][$j]) ? $this->history[$i][$j] :
'');
$last_attr = $this->history_attrs[$i][$j];
}
$scrollback.= "\r\n";
}
$base_attr_cell = $this->base_attr_cell;
$this->base_attr_cell = $last_attr;
$scrollback.= $this->_getScreen();
$this->base_attr_cell = $base_attr_cell;
return '<pre width="' . ($this->max_x + 1) .
'" style="color: white; background: black">' .
$scrollback . '</span></pre>';
}
}
vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php000064400000001546151156520650017433
0ustar00<?php
/**
* Pure-PHP ASN.1 Parser
*
* PHP version 5
*
* @category File
* @package ASN1
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2012 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\File\ASN1;
/**
* ASN.1 Element
*
* Bypass normal encoding rules in phpseclib\File\ASN1::encodeDER()
*
* @package ASN1
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class Element
{
/**
* Raw element value
*
* @var string
* @access private
*/
var $element;
/**
* Constructor
*
* @param string $encoded
* @return \phpseclib\File\ASN1\Element
* @access public
*/
function __construct($encoded)
{
$this->element = $encoded;
}
}
vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php000064400000157126151156520650016050
0ustar00<?php
/**
* Pure-PHP ASN.1 Parser
*
* PHP version 5
*
* ASN.1 provides the semantics for data encoded using various schemes.
The most commonly
* utilized scheme is DER or the "Distinguished Encoding Rules".
PEM's are base64 encoded
* DER blobs.
*
* \phpseclib\File\ASN1 decodes and encodes DER formatted messages and
places them in a semantic context.
*
* Uses the 1988 ASN.1 syntax.
*
* @category File
* @package ASN1
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2012 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\File;
use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger;
use DateTime;
use DateTimeZone;
/**
* Pure-PHP ASN.1 Parser
*
* @package ASN1
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class ASN1
{
/**#@+
* Tag Classes
*
* @access private
* @link
http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=12
*/
const CLASS_UNIVERSAL = 0;
const CLASS_APPLICATION = 1;
const CLASS_CONTEXT_SPECIFIC = 2;
const CLASS_PRIVATE = 3;
/**#@-*/
/**#@+
* Tag Classes
*
* @access private
* @link http://www.obj-sys.com/asn1tutorial/node124.html
*/
const TYPE_BOOLEAN = 1;
const TYPE_INTEGER = 2;
const TYPE_BIT_STRING = 3;
const TYPE_OCTET_STRING = 4;
const TYPE_NULL = 5;
const TYPE_OBJECT_IDENTIFIER = 6;
//const TYPE_OBJECT_DESCRIPTOR = 7;
//const TYPE_INSTANCE_OF = 8; // EXTERNAL
const TYPE_REAL = 9;
const TYPE_ENUMERATED = 10;
//const TYPE_EMBEDDED = 11;
const TYPE_UTF8_STRING = 12;
//const TYPE_RELATIVE_OID = 13;
const TYPE_SEQUENCE = 16; // SEQUENCE OF
const TYPE_SET = 17; // SET OF
/**#@-*/
/**#@+
* More Tag Classes
*
* @access private
* @link http://www.obj-sys.com/asn1tutorial/node10.html
*/
const TYPE_NUMERIC_STRING = 18;
const TYPE_PRINTABLE_STRING = 19;
const TYPE_TELETEX_STRING = 20; // T61String
const TYPE_VIDEOTEX_STRING = 21;
const TYPE_IA5_STRING = 22;
const TYPE_UTC_TIME = 23;
const TYPE_GENERALIZED_TIME = 24;
const TYPE_GRAPHIC_STRING = 25;
const TYPE_VISIBLE_STRING = 26; // ISO646String
const TYPE_GENERAL_STRING = 27;
const TYPE_UNIVERSAL_STRING = 28;
//const TYPE_CHARACTER_STRING = 29;
const TYPE_BMP_STRING = 30;
/**#@-*/
/**#@+
* Tag Aliases
*
* These tags are kinda place holders for other tags.
*
* @access private
*/
const TYPE_CHOICE = -1;
const TYPE_ANY = -2;
/**#@-*/
/**
* ASN.1 object identifier
*
* @var array
* @access private
* @link http://en.wikipedia.org/wiki/Object_identifier
*/
var $oids = array();
/**
* Default date format
*
* @var string
* @access private
* @link http://php.net/class.datetime
*/
var $format = 'D, d M Y H:i:s O';
/**
* Default date format
*
* @var array
* @access private
* @see self::setTimeFormat()
* @see self::asn1map()
* @link http://php.net/class.datetime
*/
var $encoded;
/**
* Filters
*
* If the mapping type is self::TYPE_ANY what do we actually encode it
as?
*
* @var array
* @access private
* @see self::_encode_der()
*/
var $filters;
/**
* Type mapping table for the ANY type.
*
* Structured or unknown types are mapped to a
\phpseclib\File\ASN1\Element.
* Unambiguous types get the direct mapping (int/real/bool).
* Others are mapped as a choice, with an extra indexing level.
*
* @var array
* @access public
*/
var $ANYmap = array(
self::TYPE_BOOLEAN => true,
self::TYPE_INTEGER => true,
self::TYPE_BIT_STRING => 'bitString',
self::TYPE_OCTET_STRING => 'octetString',
self::TYPE_NULL => 'null',
self::TYPE_OBJECT_IDENTIFIER => 'objectIdentifier',
self::TYPE_REAL => true,
self::TYPE_ENUMERATED => 'enumerated',
self::TYPE_UTF8_STRING => 'utf8String',
self::TYPE_NUMERIC_STRING => 'numericString',
self::TYPE_PRINTABLE_STRING => 'printableString',
self::TYPE_TELETEX_STRING => 'teletexString',
self::TYPE_VIDEOTEX_STRING => 'videotexString',
self::TYPE_IA5_STRING => 'ia5String',
self::TYPE_UTC_TIME => 'utcTime',
self::TYPE_GENERALIZED_TIME => 'generalTime',
self::TYPE_GRAPHIC_STRING => 'graphicString',
self::TYPE_VISIBLE_STRING => 'visibleString',
self::TYPE_GENERAL_STRING => 'generalString',
self::TYPE_UNIVERSAL_STRING => 'universalString',
//self::TYPE_CHARACTER_STRING =>
'characterString',
self::TYPE_BMP_STRING => 'bmpString'
);
/**
* String type to character size mapping table.
*
* Non-convertable types are absent from this table.
* size == 0 indicates variable length encoding.
*
* @var array
* @access public
*/
var $stringTypeSize = array(
self::TYPE_UTF8_STRING => 0,
self::TYPE_BMP_STRING => 2,
self::TYPE_UNIVERSAL_STRING => 4,
self::TYPE_PRINTABLE_STRING => 1,
self::TYPE_TELETEX_STRING => 1,
self::TYPE_IA5_STRING => 1,
self::TYPE_VISIBLE_STRING => 1,
);
/**
* Parse BER-encoding
*
* Serves a similar purpose to openssl's asn1parse
*
* @param string $encoded
* @return array
* @access public
*/
function decodeBER($encoded)
{
if ($encoded instanceof Element) {
$encoded = $encoded->element;
}
$this->encoded = $encoded;
// encapsulate in an array for BC with the old decodeBER
return array($this->_decode_ber($encoded));
}
/**
* Parse BER-encoding (Helper function)
*
* Sometimes we want to get the BER encoding of a particular tag.
$start lets us do that without having to reencode.
* $encoded is passed by reference for the recursive calls done for
self::TYPE_BIT_STRING and
* self::TYPE_OCTET_STRING. In those cases, the indefinite length is
used.
*
* @param string $encoded
* @param int $start
* @param int $encoded_pos
* @return array
* @access private
*/
function _decode_ber($encoded, $start = 0, $encoded_pos = 0)
{
$current = array('start' => $start);
$type = ord($encoded[$encoded_pos++]);
$startOffset = 1;
$constructed = ($type >> 5) & 1;
$tag = $type & 0x1F;
if ($tag == 0x1F) {
$tag = 0;
// process septets (since the eighth bit is ignored, it's
not an octet)
do {
$temp = ord($encoded[$encoded_pos++]);
$startOffset++;
$loop = $temp >> 7;
$tag <<= 7;
$temp &= 0x7F;
// "bits 7 to 1 of the first subsequent octet shall
not all be zero"
if ($startOffset == 2 && $temp == 0) {
return false;
}
$tag |= $temp;
} while ($loop);
}
$start+= $startOffset;
// Length, as discussed in paragraph 8.1.3 of
X.690-0207.pdf#page=13
$length = ord($encoded[$encoded_pos++]);
$start++;
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
// immediately available." -- paragraph 8.1.3.2.c
$length = strlen($encoded) - $encoded_pos;
} 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
// support it up to four.
$length&= 0x7F;
$temp = substr($encoded, $encoded_pos, $length);
$encoded_pos += $length;
// tags of indefinte length don't really have a header
length; this length includes the tag
$current+= array('headerlength' => $length + 2);
$start+= $length;
extract(unpack('Nlength', substr(str_pad($temp, 4,
chr(0), STR_PAD_LEFT), -4)));
} else {
$current+= array('headerlength' => 2);
}
if ($length > (strlen($encoded) - $encoded_pos)) {
return false;
}
$content = substr($encoded, $encoded_pos, $length);
$content_pos = 0;
// 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
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
have a wide, scattered use within a particular presentation
context. PRIVATE distinguishes data types within
a particular organization or country. CONTEXT-SPECIFIC
distinguishes members of a sequence or set, the
alternatives of a CHOICE, or universally tagged set members.
Only the class number appears in braces for this
data type; the term CONTEXT-SPECIFIC does not appear.
-- http://www.obj-sys.com/asn1tutorial/node12.html */
$class = ($type >> 6) & 3;
switch ($class) {
case self::CLASS_APPLICATION:
case self::CLASS_PRIVATE:
case self::CLASS_CONTEXT_SPECIFIC:
if (!$constructed) {
return array(
'type' => $class,
'constant' => $tag,
'content' => $content,
'length' => $length + $start -
$current['start']
);
}
$newcontent = array();
$remainingLength = $length;
while ($remainingLength > 0) {
$temp = $this->_decode_ber($content, $start,
$content_pos);
if ($temp === false) {
break;
}
$length = $temp['length'];
// end-of-content octets - see paragraph 8.1.5
if (substr($content, $content_pos + $length, 2) ==
"\0\0") {
$length+= 2;
$start+= $length;
$newcontent[] = $temp;
break;
}
$start+= $length;
$remainingLength-= $length;
$newcontent[] = $temp;
$content_pos += $length;
}
return array(
'type' => $class,
'constant' => $tag,
// the array encapsulation is for BC with the old
format
'content' => $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+= array('type' => $tag);
// decode UNIVERSAL tags
switch ($tag) {
case self::TYPE_BOOLEAN:
// "The contents octets shall consist of a single
octet." -- paragraph 8.2.1
if ($constructed || strlen($content) != 1) {
return false;
}
$current['content'] = (bool)
ord($content[$content_pos]);
break;
case self::TYPE_INTEGER:
case self::TYPE_ENUMERATED:
if ($constructed) {
return false;
}
$current['content'] = new
BigInteger(substr($content, $content_pos), -256);
break;
case self::TYPE_REAL: // not currently supported
return false;
case self::TYPE_BIT_STRING:
// The initial octet shall encode, as an unsigned binary
integer with bit 1 as the least significant bit,
// the number of unused bits in the final subsequent octet.
The number shall be in the range zero to
// seven.
if (!$constructed) {
$current['content'] = substr($content,
$content_pos);
} else {
$temp = $this->_decode_ber($content, $start,
$content_pos);
if ($temp === false) {
return false;
}
$length-= (strlen($content) - $content_pos);
$last = count($temp) - 1;
for ($i = 0; $i < $last; $i++) {
// all subtags should be bit strings
if ($temp[$i]['type'] !=
self::TYPE_BIT_STRING) {
return false;
}
$current['content'].=
substr($temp[$i]['content'], 1);
}
// all subtags should be bit strings
if ($temp[$last]['type'] !=
self::TYPE_BIT_STRING) {
return false;
}
$current['content'] =
$temp[$last]['content'][0] . $current['content'] .
substr($temp[$i]['content'], 1);
}
break;
case self::TYPE_OCTET_STRING:
if (!$constructed) {
$current['content'] = substr($content,
$content_pos);
} else {
$current['content'] = '';
$length = 0;
while (substr($content, $content_pos, 2) !=
"\0\0") {
$temp = $this->_decode_ber($content, $length +
$start, $content_pos);
if ($temp === false) {
return false;
}
$content_pos += $temp['length'];
// all subtags should be octet strings
if ($temp['type'] !=
self::TYPE_OCTET_STRING) {
return false;
}
$current['content'].=
$temp['content'];
$length+= $temp['length'];
}
if (substr($content, $content_pos, 2) ==
"\0\0") {
$length+= 2; // +2 for the EOC
}
}
break;
case self::TYPE_NULL:
// "The contents octets shall not contain any
octets." -- paragraph 8.8.2
if ($constructed || strlen($content)) {
return false;
}
break;
case self::TYPE_SEQUENCE:
case self::TYPE_SET:
if (!$constructed) {
return false;
}
$offset = 0;
$current['content'] = array();
$content_len = strlen($content);
while ($content_pos < $content_len) {
// 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, $content_pos, 2) == "\0\0") {
$length = $offset + 2; // +2 for the EOC
break 2;
}
$temp = $this->_decode_ber($content, $start +
$offset, $content_pos);
if ($temp === false) {
return false;
}
$content_pos += $temp['length'];
$current['content'][] = $temp;
$offset+= $temp['length'];
}
break;
case self::TYPE_OBJECT_IDENTIFIER:
if ($constructed) {
return false;
}
$current['content'] =
$this->_decodeOID(substr($content, $content_pos));
if ($current['content'] === false) {
return false;
}
break;
/* Each character string type shall be encoded as if it had
been declared:
[UNIVERSAL x] IMPLICIT OCTET STRING
-- X.690-0207.pdf#page=23 (paragraph 8.21.3)
Per that, we're not going to do any validation. If
there are any illegal characters in the string,
we don't really care */
case self::TYPE_NUMERIC_STRING:
// 0,1,2,3,4,5,6,7,8,9, and space
case self::TYPE_PRINTABLE_STRING:
// Upper and lower case letters, digits, space, apostrophe,
left/right parenthesis, plus sign, comma,
// hyphen, full stop, solidus, colon, equal sign, question
mark
case self::TYPE_TELETEX_STRING:
// The Teletex character set in CCITT's T61, space,
and delete
// see http://en.wikipedia.org/wiki/Teletex#Character_sets
case self::TYPE_VIDEOTEX_STRING:
// The Videotex character set in CCITT's T.100 and
T.101, space, and delete
case self::TYPE_VISIBLE_STRING:
// Printing character sets of international ASCII, and
space
case self::TYPE_IA5_STRING:
// International Alphabet 5 (International ASCII)
case self::TYPE_GRAPHIC_STRING:
// All registered G sets, and space
case self::TYPE_GENERAL_STRING:
// All registered C and G sets, space and delete
case self::TYPE_UTF8_STRING:
// ????
case self::TYPE_BMP_STRING:
if ($constructed) {
return false;
}
$current['content'] = substr($content,
$content_pos);
break;
case self::TYPE_UTC_TIME:
case self::TYPE_GENERALIZED_TIME:
if ($constructed) {
return false;
}
$current['content'] =
$this->_decodeTime(substr($content, $content_pos), $tag);
break;
default:
return false;
}
$start+= $length;
// 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 Map
*
* 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 $mapping
* @param array $special
* @return array
* @access public
*/
function asn1map($decoded, $mapping, $special = array())
{
if (!is_array($decoded)) {
return false;
}
if (isset($mapping['explicit']) &&
is_array($decoded['content'])) {
$decoded = $decoded['content'][0];
}
switch (true) {
case $mapping['type'] == self::TYPE_ANY:
$intype = $decoded['type'];
if (isset($decoded['constant']) ||
!isset($this->ANYmap[$intype]) ||
(ord($this->encoded[$decoded['start']]) & 0x20)) {
return new Element(substr($this->encoded,
$decoded['start'], $decoded['length']));
}
$inmap = $this->ANYmap[$intype];
if (is_string($inmap)) {
return array($inmap => $this->asn1map($decoded,
array('type' => $intype) + $mapping, $special));
}
break;
case $mapping['type'] == self::TYPE_CHOICE:
foreach ($mapping['children'] as $key =>
$option) {
switch (true) {
case isset($option['constant'])
&& $option['constant'] == $decoded['constant']:
case !isset($option['constant'])
&& $option['type'] == $decoded['type']:
$value = $this->asn1map($decoded, $option,
$special);
break;
case !isset($option['constant'])
&& $option['type'] == self::TYPE_CHOICE:
$v = $this->asn1map($decoded, $option,
$special);
if (isset($v)) {
$value = $v;
}
}
if (isset($value)) {
if (isset($special[$key])) {
$value = call_user_func($special[$key],
$value);
}
return array($key => $value);
}
}
return null;
case isset($mapping['implicit']):
case isset($mapping['explicit']):
case $decoded['type'] == $mapping['type']:
break;
default:
// if $decoded['type'] and
$mapping['type'] are both strings, but different types of
strings,
// let it through
switch (true) {
case $decoded['type'] < 18: //
self::TYPE_NUMERIC_STRING == 18
case $decoded['type'] > 30: //
self::TYPE_BMP_STRING == 30
case $mapping['type'] < 18:
case $mapping['type'] > 30:
return null;
}
}
if (isset($mapping['implicit'])) {
$decoded['type'] = $mapping['type'];
}
switch ($decoded['type']) {
case self::TYPE_SEQUENCE:
$map = array();
// ignore the min and max
if (isset($mapping['min']) &&
isset($mapping['max'])) {
$child = $mapping['children'];
foreach ($decoded['content'] as $content) {
if (($map[] = $this->asn1map($content, $child,
$special)) === null) {
return null;
}
}
return $map;
}
$n = count($decoded['content']);
$i = 0;
foreach ($mapping['children'] as $key =>
$child) {
$maymatch = $i < $n; // Match only existing input.
if ($maymatch) {
$temp = $decoded['content'][$i];
if ($child['type'] != self::TYPE_CHOICE)
{
// Get the mapping and input class &
constant.
$childClass = $tempClass =
self::CLASS_UNIVERSAL;
$constant = null;
if (isset($temp['constant'])) {
$tempClass = $temp['type'];
}
if (isset($child['class'])) {
$childClass = $child['class'];
$constant = $child['cast'];
} elseif (isset($child['constant']))
{
$childClass = self::CLASS_CONTEXT_SPECIFIC;
$constant = $child['constant'];
}
if (isset($constant) &&
isset($temp['constant'])) {
// Can only match if constants and class
match.
$maymatch = $constant ==
$temp['constant'] && $childClass == $tempClass;
} else {
// Can only match if no constant expected
and type matches or is generic.
$maymatch =
!isset($child['constant']) &&
array_search($child['type'], array($temp['type'],
self::TYPE_ANY, self::TYPE_CHOICE)) !== false;
}
}
}
if ($maymatch) {
// Attempt submapping.
$candidate = $this->asn1map($temp, $child,
$special);
$maymatch = $candidate !== null;
}
if ($maymatch) {
// Got the match: use it.
if (isset($special[$key])) {
$candidate = call_user_func($special[$key],
$candidate);
}
$map[$key] = $candidate;
$i++;
} elseif (isset($child['default'])) {
$map[$key] = $child['default']; // Use
default.
} elseif (!isset($child['optional'])) {
return null; // Syntax error.
}
}
// Fail mapping if all input items have not been consumed.
return $i < $n ? null: $map;
// the main diff between sets and sequences is the
encapsulation of the foreach in another for loop
case self::TYPE_SET:
$map = array();
// ignore the min and max
if (isset($mapping['min']) &&
isset($mapping['max'])) {
$child = $mapping['children'];
foreach ($decoded['content'] as $content) {
if (($map[] = $this->asn1map($content, $child,
$special)) === null) {
return null;
}
}
return $map;
}
for ($i = 0; $i < count($decoded['content']);
$i++) {
$temp = $decoded['content'][$i];
$tempClass = self::CLASS_UNIVERSAL;
if (isset($temp['constant'])) {
$tempClass = $temp['type'];
}
foreach ($mapping['children'] as $key =>
$child) {
if (isset($map[$key])) {
continue;
}
$maymatch = true;
if ($child['type'] != self::TYPE_CHOICE)
{
$childClass = self::CLASS_UNIVERSAL;
$constant = null;
if (isset($child['class'])) {
$childClass = $child['class'];
$constant = $child['cast'];
} elseif (isset($child['constant']))
{
$childClass = self::CLASS_CONTEXT_SPECIFIC;
$constant = $child['constant'];
}
if (isset($constant) &&
isset($temp['constant'])) {
// Can only match if constants and class
match.
$maymatch = $constant ==
$temp['constant'] && $childClass == $tempClass;
} else {
// Can only match if no constant expected
and type matches or is generic.
$maymatch =
!isset($child['constant']) &&
array_search($child['type'], array($temp['type'],
self::TYPE_ANY, self::TYPE_CHOICE)) !== false;
}
}
if ($maymatch) {
// Attempt submapping.
$candidate = $this->asn1map($temp, $child,
$special);
$maymatch = $candidate !== null;
}
if (!$maymatch) {
break;
}
// Got the match: use it.
if (isset($special[$key])) {
$candidate = call_user_func($special[$key],
$candidate);
}
$map[$key] = $candidate;
break;
}
}
foreach ($mapping['children'] as $key =>
$child) {
if (!isset($map[$key])) {
if (isset($child['default'])) {
$map[$key] = $child['default'];
} elseif (!isset($child['optional'])) {
return null;
}
}
}
return $map;
case self::TYPE_OBJECT_IDENTIFIER:
return isset($this->oids[$decoded['content']])
? $this->oids[$decoded['content']] :
$decoded['content'];
case self::TYPE_UTC_TIME:
case self::TYPE_GENERALIZED_TIME:
// for explicitly tagged optional stuff
if (is_array($decoded['content'])) {
$decoded['content'] =
$decoded['content'][0]['content'];
}
// for implicitly tagged optional stuff
// in theory, doing isset($mapping['implicit'])
would work but malformed certs do exist
// in the wild that OpenSSL decodes without issue so
we'll support them as well
if (!is_object($decoded['content'])) {
$decoded['content'] =
$this->_decodeTime($decoded['content'],
$decoded['type']);
}
return $decoded['content'] ?
$decoded['content']->format($this->format) : false;
case self::TYPE_BIT_STRING:
if (isset($mapping['mapping'])) {
$offset = ord($decoded['content'][0]);
$size = (strlen($decoded['content']) - 1) * 8
- $offset;
/*
From X.680-0207.pdf#page=46 (21.7):
"When a "NamedBitList" is used in
defining a bitstring type ASN.1 encoding rules are free to add (or remove)
arbitrarily any trailing 0 bits to (or from) values
that are being encoded or decoded. Application designers should
therefore ensure that different semantics are not
associated with such values which differ only in the number of trailing
0 bits."
*/
$bits = count($mapping['mapping']) == $size ?
array() : array_fill(0, count($mapping['mapping']) - $size,
false);
for ($i = strlen($decoded['content']) - 1; $i
> 0; $i--) {
$current = ord($decoded['content'][$i]);
for ($j = $offset; $j < 8; $j++) {
$bits[] = (bool) ($current & (1 <<
$j));
}
$offset = 0;
}
$values = array();
$map = array_reverse($mapping['mapping']);
foreach ($map as $i => $value) {
if ($bits[$i]) {
$values[] = $value;
}
}
return $values;
}
case self::TYPE_OCTET_STRING:
return base64_encode($decoded['content']);
case self::TYPE_NULL:
return '';
case self::TYPE_BOOLEAN:
return $decoded['content'];
case self::TYPE_NUMERIC_STRING:
case self::TYPE_PRINTABLE_STRING:
case self::TYPE_TELETEX_STRING:
case self::TYPE_VIDEOTEX_STRING:
case self::TYPE_IA5_STRING:
case self::TYPE_GRAPHIC_STRING:
case self::TYPE_VISIBLE_STRING:
case self::TYPE_GENERAL_STRING:
case self::TYPE_UNIVERSAL_STRING:
case self::TYPE_UTF8_STRING:
case self::TYPE_BMP_STRING:
return $decoded['content'];
case self::TYPE_INTEGER:
case self::TYPE_ENUMERATED:
$temp = $decoded['content'];
if (isset($mapping['implicit'])) {
$temp = new BigInteger($decoded['content'],
-256);
}
if (isset($mapping['mapping'])) {
$temp = (int) $temp->toString();
return isset($mapping['mapping'][$temp]) ?
$mapping['mapping'][$temp] :
false;
}
return $temp;
}
}
/**
* ASN.1 Encode
*
* DER-encodes an ASN.1 semantic mapping ($mapping). Some libraries
would probably call this function
* an ASN.1 compiler.
*
* "Special" mappings can be applied via $special.
*
* @param string $source
* @param string $mapping
* @param array $special
* @return string
* @access public
*/
function encodeDER($source, $mapping, $special = array())
{
$this->location = array();
return $this->_encode_der($source, $mapping, null, $special);
}
/**
* ASN.1 Encode (Helper function)
*
* @param string $source
* @param string $mapping
* @param int $idx
* @param array $special
* @return string
* @access private
*/
function _encode_der($source, $mapping, $idx = null, $special =
array())
{
if ($source instanceof Element) {
return $source->element;
}
// do not encode (implicitly optional) fields with value set to
default
if (isset($mapping['default']) && $source ===
$mapping['default']) {
return '';
}
if (isset($idx)) {
if (isset($special[$idx])) {
$source = call_user_func($special[$idx], $source);
}
$this->location[] = $idx;
}
$tag = $mapping['type'];
switch ($tag) {
case self::TYPE_SET: // Children order is not important,
thus process in sequence.
case self::TYPE_SEQUENCE:
$tag|= 0x20; // set the constructed bit
// ignore the min and max
if (isset($mapping['min']) &&
isset($mapping['max'])) {
$value = array();
$child = $mapping['children'];
foreach ($source as $content) {
$temp = $this->_encode_der($content, $child,
null, $special);
if ($temp === false) {
return false;
}
$value[]= $temp;
}
/* "The encodings of the component values of a
set-of value shall appear in ascending order, the encodings being compared
as octet strings with the shorter components being
padded at their trailing end with 0-octets.
NOTE - The padding octets are for comparison
purposes only and do not appear in the encodings."
-- sec 11.6 of
http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf */
if ($mapping['type'] == self::TYPE_SET) {
sort($value);
}
$value = implode('', $value);
break;
}
$value = '';
foreach ($mapping['children'] as $key =>
$child) {
if (!array_key_exists($key, $source)) {
if (!isset($child['optional'])) {
return false;
}
continue;
}
$temp = $this->_encode_der($source[$key], $child,
$key, $special);
if ($temp === false) {
return false;
}
// An empty child encoding means it has been optimized
out.
// Else we should have at least one tag byte.
if ($temp === '') {
continue;
}
// if isset($child['constant']) is true then
isset($child['optional']) should be true as well
if (isset($child['constant'])) {
/*
From X.680-0207.pdf#page=58 (30.6):
"The tagging construction specifies
explicit tagging if any of the following holds:
...
c) the "Tag Type" alternative is used
and the value of "TagDefault" for the module is IMPLICIT TAGS or
AUTOMATIC TAGS, but the type defined by
"Type" is an untagged choice type, an untagged open type, or
an untagged "DummyReference" (see
ITU-T Rec. X.683 | ISO/IEC 8824-4, 8.3)."
*/
if (isset($child['explicit']) ||
$child['type'] == self::TYPE_CHOICE) {
$subtag = chr((self::CLASS_CONTEXT_SPECIFIC
<< 6) | 0x20 | $child['constant']);
$temp = $subtag .
$this->_encodeLength(strlen($temp)) . $temp;
} else {
$subtag = chr((self::CLASS_CONTEXT_SPECIFIC
<< 6) | (ord($temp[0]) & 0x20) | $child['constant']);
$temp = $subtag . substr($temp, 1);
}
}
$value.= $temp;
}
break;
case self::TYPE_CHOICE:
$temp = false;
foreach ($mapping['children'] as $key =>
$child) {
if (!isset($source[$key])) {
continue;
}
$temp = $this->_encode_der($source[$key], $child,
$key, $special);
if ($temp === false) {
return false;
}
// An empty child encoding means it has been optimized
out.
// Else we should have at least one tag byte.
if ($temp === '') {
continue;
}
$tag = ord($temp[0]);
// if isset($child['constant']) is true then
isset($child['optional']) should be true as well
if (isset($child['constant'])) {
if (isset($child['explicit']) ||
$child['type'] == self::TYPE_CHOICE) {
$subtag = chr((self::CLASS_CONTEXT_SPECIFIC
<< 6) | 0x20 | $child['constant']);
$temp = $subtag .
$this->_encodeLength(strlen($temp)) . $temp;
} else {
$subtag = chr((self::CLASS_CONTEXT_SPECIFIC
<< 6) | (ord($temp[0]) & 0x20) | $child['constant']);
$temp = $subtag . substr($temp, 1);
}
}
}
if (isset($idx)) {
array_pop($this->location);
}
if ($temp && isset($mapping['cast'])) {
$temp[0] = chr(($mapping['class'] << 6)
| ($tag & 0x20) | $mapping['cast']);
}
return $temp;
case self::TYPE_INTEGER:
case self::TYPE_ENUMERATED:
if (!isset($mapping['mapping'])) {
if (is_numeric($source)) {
$source = new BigInteger($source);
}
$value = $source->toBytes(true);
} else {
$value = array_search($source,
$mapping['mapping']);
if ($value === false) {
return false;
}
$value = new BigInteger($value);
$value = $value->toBytes(true);
}
if (!strlen($value)) {
$value = chr(0);
}
break;
case self::TYPE_UTC_TIME:
case self::TYPE_GENERALIZED_TIME:
$format = $mapping['type'] == self::TYPE_UTC_TIME
? 'y' : 'Y';
$format.= 'mdHis';
// if $source does _not_ include timezone information
within it then assume that the timezone is GMT
$date = new DateTime($source, new
DateTimeZone('GMT'));
// if $source _does_ include timezone information within it
then convert the time to GMT
$date->setTimezone(new DateTimeZone('GMT'));
$value = $date->format($format) . 'Z';
break;
case self::TYPE_BIT_STRING:
if (isset($mapping['mapping'])) {
$bits = array_fill(0,
count($mapping['mapping']), 0);
$size = 0;
for ($i = 0; $i <
count($mapping['mapping']); $i++) {
if (in_array($mapping['mapping'][$i],
$source)) {
$bits[$i] = 1;
$size = $i;
}
}
if (isset($mapping['min']) &&
$mapping['min'] >= 1 && $size <
$mapping['min']) {
$size = $mapping['min'] - 1;
}
$offset = 8 - (($size + 1) & 7);
$offset = $offset !== 8 ? $offset : 0;
$value = chr($offset);
for ($i = $size + 1; $i <
count($mapping['mapping']); $i++) {
unset($bits[$i]);
}
$bits = implode('', array_pad($bits, $size +
$offset + 1, 0));
$bytes = explode(' ',
rtrim(chunk_split($bits, 8, ' ')));
foreach ($bytes as $byte) {
$value.= chr(bindec($byte));
}
break;
}
case self::TYPE_OCTET_STRING:
/* The initial octet shall encode, as an unsigned binary
integer with bit 1 as the least significant bit,
the number of unused bits in the final subsequent octet.
The number shall be in the range zero to seven.
--
http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=16
*/
$value = base64_decode($source);
break;
case self::TYPE_OBJECT_IDENTIFIER:
$value = $this->_encodeOID($source);
break;
case self::TYPE_ANY:
$loc = $this->location;
if (isset($idx)) {
array_pop($this->location);
}
switch (true) {
case !isset($source):
return $this->_encode_der(null,
array('type' => self::TYPE_NULL) + $mapping, null, $special);
case is_int($source):
case $source instanceof BigInteger:
return $this->_encode_der($source,
array('type' => self::TYPE_INTEGER) + $mapping, null,
$special);
case is_float($source):
return $this->_encode_der($source,
array('type' => self::TYPE_REAL) + $mapping, null, $special);
case is_bool($source):
return $this->_encode_der($source,
array('type' => self::TYPE_BOOLEAN) + $mapping, null,
$special);
case is_array($source) && count($source) == 1:
$typename = implode('',
array_keys($source));
$outtype = array_search($typename,
$this->ANYmap, true);
if ($outtype !== false) {
return
$this->_encode_der($source[$typename], array('type' =>
$outtype) + $mapping, null, $special);
}
}
$filters = $this->filters;
foreach ($loc as $part) {
if (!isset($filters[$part])) {
$filters = false;
break;
}
$filters = $filters[$part];
}
if ($filters === false) {
user_error('No filters defined for ' .
implode('/', $loc));
return false;
}
return $this->_encode_der($source, $filters + $mapping,
null, $special);
case self::TYPE_NULL:
$value = '';
break;
case self::TYPE_NUMERIC_STRING:
case self::TYPE_TELETEX_STRING:
case self::TYPE_PRINTABLE_STRING:
case self::TYPE_UNIVERSAL_STRING:
case self::TYPE_UTF8_STRING:
case self::TYPE_BMP_STRING:
case self::TYPE_IA5_STRING:
case self::TYPE_VISIBLE_STRING:
case self::TYPE_VIDEOTEX_STRING:
case self::TYPE_GRAPHIC_STRING:
case self::TYPE_GENERAL_STRING:
$value = $source;
break;
case self::TYPE_BOOLEAN:
$value = $source ? "\xFF" : "\x00";
break;
default:
user_error('Mapping provides no type definition for
' . implode('/', $this->location));
return false;
}
if (isset($idx)) {
array_pop($this->location);
}
if (isset($mapping['cast'])) {
if (isset($mapping['explicit']) ||
$mapping['type'] == self::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;
}
/**
* DER-encode the length
*
* DER supports lengths up to (2**8)**127, however, we'll only
support lengths up to (2**8)**4. See
* {@link
http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690
paragraph 8.1.3} for more information.
*
* @access private
* @param int $length
* @return string
*/
function _encodeLength($length)
{
if ($length <= 0x7F) {
return chr($length);
}
$temp = ltrim(pack('N', $length), chr(0));
return pack('Ca*', 0x80 | strlen($temp), $temp);
}
/**
* BER-decode the OID
*
* Called by _decode_ber()
*
* @access private
* @param string $content
* @return string
*/
function _decodeOID($content)
{
static $eighty;
if (!$eighty) {
$eighty = new BigInteger(80);
}
$oid = array();
$pos = 0;
$len = strlen($content);
if (ord($content[$len - 1]) & 0x80) {
return false;
}
$n = new BigInteger();
while ($pos < $len) {
$temp = ord($content[$pos++]);
$n = $n->bitwise_leftShift(7);
$n = $n->bitwise_or(new BigInteger($temp & 0x7F));
if (~$temp & 0x80) {
$oid[] = $n;
$n = new BigInteger();
}
}
$part1 = array_shift($oid);
$first = floor(ord($content[0]) / 40);
/*
"This packing of the first two object identifier components
recognizes that only three values are allocated from the root
node, and at most 39 subsequent values from nodes reached by X =
0 and X = 1."
--
https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=22
*/
if ($first <= 2) { // ie. 0 <= ord($content[0]) < 120
(0x78)
array_unshift($oid, ord($content[0]) % 40);
array_unshift($oid, $first);
} else {
array_unshift($oid, $part1->subtract($eighty));
array_unshift($oid, 2);
}
return implode('.', $oid);
}
/**
* DER-encode the OID
*
* Called by _encode_der()
*
* @access private
* @param string $source
* @return string
*/
function _encodeOID($source)
{
static $mask, $zero, $forty;
if (!$mask) {
$mask = new BigInteger(0x7F);
$zero = new BigInteger();
$forty = new BigInteger(40);
}
$oid = preg_match('#(?:\d+\.)+#', $source) ? $source :
array_search($source, $this->oids);
if ($oid === false) {
user_error('Invalid OID');
return false;
}
$parts = explode('.', $oid);
$part1 = array_shift($parts);
$part2 = array_shift($parts);
$first = new BigInteger($part1);
$first = $first->multiply($forty);
$first = $first->add(new BigInteger($part2));
array_unshift($parts, $first->toString());
$value = '';
foreach ($parts as $part) {
if (!$part) {
$temp = "\0";
} else {
$temp = '';
$part = new BigInteger($part);
while (!$part->equals($zero)) {
$submask = $part->bitwise_and($mask);
$submask->setPrecision(8);
$temp = (chr(0x80) | $submask->toBytes()) . $temp;
$part = $part->bitwise_rightShift(7);
}
$temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] &
chr(0x7F);
}
$value.= $temp;
}
return $value;
}
/**
* BER-decode the time
*
* Called by _decode_ber() and in the case of implicit tags asn1map().
*
* @access private
* @param string $content
* @param int $tag
* @return string
*/
function _decodeTime($content, $tag)
{
/* UTCTime:
http://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
http://www.obj-sys.com/asn1tutorial/node15.html
GeneralizedTime:
http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2
http://www.obj-sys.com/asn1tutorial/node14.html */
$format = 'YmdHis';
if ($tag == self::TYPE_UTC_TIME) {
//
https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=28
says "the seconds
// element shall always be present" but none-the-less
I've seen X509 certs where it isn't and if the
// browsers parse it phpseclib ought to too
if (preg_match('#^(\d{10})(Z|[+-]\d{4})$#', $content,
$matches)) {
$content = $matches[1] . '00' . $matches[2];
}
$prefix = substr($content, 0, 2) >= 50 ? '19' :
'20';
$content = $prefix . $content;
} elseif (strpos($content, '.') !== false) {
$format.= '.u';
}
if ($content[strlen($content) - 1] == 'Z') {
$content = substr($content, 0, -1) . '+0000';
}
if (strpos($content, '-') !== false || strpos($content,
'+') !== false) {
$format.= 'O';
}
// error supression isn't necessary as of PHP 7.0:
// http://php.net/manual/en/migration70.other-changes.php
return @DateTime::createFromFormat($format, $content);
}
/**
* Set the time format
*
* Sets the time / date format for asn1map().
*
* @access public
* @param string $format
*/
function setTimeFormat($format)
{
$this->format = $format;
}
/**
* Load OIDs
*
* Load the relevant OIDs for a particular ASN.1 semantic mapping.
*
* @access public
* @param array $oids
*/
function loadOIDs($oids)
{
$this->oids = $oids;
}
/**
* Load filters
*
* See \phpseclib\File\X509, etc, for an example.
*
* @access public
* @param array $filters
*/
function loadFilters($filters)
{
$this->filters = $filters;
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @return string
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
/**
* String type conversion
*
* This is a lazy conversion, dealing only with character size.
* No real conversion table is used.
*
* @param string $in
* @param int $from
* @param int $to
* @return string
* @access public
*/
function convert($in, $from = self::TYPE_UTF8_STRING, $to =
self::TYPE_UTF8_STRING)
{
if (!isset($this->stringTypeSize[$from]) ||
!isset($this->stringTypeSize[$to])) {
return false;
}
$insize = $this->stringTypeSize[$from];
$outsize = $this->stringTypeSize[$to];
$inlength = strlen($in);
$out = '';
for ($i = 0; $i < $inlength;) {
if ($inlength - $i < $insize) {
return false;
}
// Get an input character as a 32-bit value.
$c = ord($in[$i++]);
switch (true) {
case $insize == 4:
$c = ($c << 8) | ord($in[$i++]);
$c = ($c << 8) | ord($in[$i++]);
case $insize == 2:
$c = ($c << 8) | ord($in[$i++]);
case $insize == 1:
break;
case ($c & 0x80) == 0x00:
break;
case ($c & 0x40) == 0x00:
return false;
default:
$bit = 6;
do {
if ($bit > 25 || $i >= $inlength ||
(ord($in[$i]) & 0xC0) != 0x80) {
return false;
}
$c = ($c << 6) | (ord($in[$i++]) & 0x3F);
$bit += 5;
$mask = 1 << $bit;
} while ($c & $bit);
$c &= $mask - 1;
break;
}
// Convert and append the character to output string.
$v = '';
switch (true) {
case $outsize == 4:
$v .= chr($c & 0xFF);
$c >>= 8;
$v .= chr($c & 0xFF);
$c >>= 8;
case $outsize == 2:
$v .= chr($c & 0xFF);
$c >>= 8;
case $outsize == 1:
$v .= chr($c & 0xFF);
$c >>= 8;
if ($c) {
return false;
}
break;
case ($c & 0x80000000) != 0:
return false;
case $c >= 0x04000000:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x04000000;
case $c >= 0x00200000:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x00200000;
case $c >= 0x00010000:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x00010000;
case $c >= 0x00000800:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x00000800;
case $c >= 0x00000080:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x000000C0;
default:
$v .= chr($c);
break;
}
$out .= strrev($v);
}
return $out;
}
}
vendor/phpseclib/phpseclib/phpseclib/File/X509.php000064400000557151151156520650016015
0ustar00<?php
/**
* Pure-PHP X.509 Parser
*
* PHP version 5
*
* Encode and decode X.509 certificates.
*
* The extensions are from {@link http://tools.ietf.org/html/rfc5280
RFC5280} and
* {@link
http://web.archive.org/web/19961027104704/http://www3.netscape.com/eng/security/cert-exts.html
Netscape Certificate Extensions}.
*
* Note that loading an X.509 certificate and resaving it may invalidate
the signature. The reason being that the signature is based on a
* portion of the certificate that contains optional parameters with
default values. ie. if the parameter isn't there the default value is
* used. Problem is, if the parameter is there and it just so happens to
have the default value there are two ways that that parameter can
* be encoded. It can be encoded explicitly or left out all together.
This would effect the signature value and thus may invalidate the
* the certificate all together unless the certificate is re-signed.
*
* @category File
* @package X509
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2012 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\File;
use phpseclib\Crypt\Hash;
use phpseclib\Crypt\Random;
use phpseclib\Crypt\RSA;
use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger;
use DateTime;
use DateTimeZone;
/**
* Pure-PHP X.509 Parser
*
* @package X509
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class X509
{
/**
* Flag to only accept signatures signed by certificate authorities
*
* Not really used anymore but retained all the same to suppress
E_NOTICEs from old installs
*
* @access public
*/
const VALIDATE_SIGNATURE_BY_CA = 1;
/**#@+
* @access public
* @see \phpseclib\File\X509::getDN()
*/
/**
* Return internal array representation
*/
const DN_ARRAY = 0;
/**
* Return string
*/
const DN_STRING = 1;
/**
* Return ASN.1 name string
*/
const DN_ASN1 = 2;
/**
* Return OpenSSL compatible array
*/
const DN_OPENSSL = 3;
/**
* Return canonical ASN.1 RDNs string
*/
const DN_CANON = 4;
/**
* Return name hash for file indexing
*/
const DN_HASH = 5;
/**#@-*/
/**#@+
* @access public
* @see \phpseclib\File\X509::saveX509()
* @see \phpseclib\File\X509::saveCSR()
* @see \phpseclib\File\X509::saveCRL()
*/
/**
* Save as PEM
*
* ie. a base64-encoded PEM with a header and a footer
*/
const FORMAT_PEM = 0;
/**
* Save as DER
*/
const FORMAT_DER = 1;
/**
* Save as a SPKAC
*
* Only works on CSRs. Not currently supported.
*/
const FORMAT_SPKAC = 2;
/**
* Auto-detect the format
*
* Used only by the load*() functions
*/
const FORMAT_AUTO_DETECT = 3;
/**#@-*/
/**
* Attribute value disposition.
* If disposition is >= 0, this is the index of the target value.
*/
const ATTR_ALL = -1; // All attribute values (array).
const ATTR_APPEND = -2; // Add a value.
const ATTR_REPLACE = -3; // Clear first, then add a value.
/**
* ASN.1 syntax for X.509 certificates
*
* @var array
* @access private
*/
var $Certificate;
/**#@+
* ASN.1 syntax for various extensions
*
* @access private
*/
var $DirectoryString;
var $PKCS9String;
var $AttributeValue;
var $Extensions;
var $KeyUsage;
var $ExtKeyUsageSyntax;
var $BasicConstraints;
var $KeyIdentifier;
var $CRLDistributionPoints;
var $AuthorityKeyIdentifier;
var $CertificatePolicies;
var $AuthorityInfoAccessSyntax;
var $SubjectAltName;
var $SubjectDirectoryAttributes;
var $PrivateKeyUsagePeriod;
var $IssuerAltName;
var $PolicyMappings;
var $NameConstraints;
var $CPSuri;
var $UserNotice;
var $netscape_cert_type;
var $netscape_comment;
var $netscape_ca_policy_url;
var $Name;
var $RelativeDistinguishedName;
var $CRLNumber;
var $CRLReason;
var $IssuingDistributionPoint;
var $InvalidityDate;
var $CertificateIssuer;
var $HoldInstructionCode;
var $SignedPublicKeyAndChallenge;
/**#@-*/
/**#@+
* ASN.1 syntax for various DN attributes
*
* @access private
*/
var $PostalAddress;
/**#@-*/
/**
* ASN.1 syntax for Certificate Signing Requests (RFC2986)
*
* @var array
* @access private
*/
var $CertificationRequest;
/**
* ASN.1 syntax for Certificate Revocation Lists (RFC5280)
*
* @var array
* @access private
*/
var $CertificateList;
/**
* Distinguished Name
*
* @var array
* @access private
*/
var $dn;
/**
* Public key
*
* @var string
* @access private
*/
var $publicKey;
/**
* Private key
*
* @var string
* @access private
*/
var $privateKey;
/**
* Object identifiers for X.509 certificates
*
* @var array
* @access private
* @link http://en.wikipedia.org/wiki/Object_identifier
*/
var $oids;
/**
* The certificate authorities
*
* @var array
* @access private
*/
var $CAs;
/**
* The currently loaded certificate
*
* @var array
* @access private
*/
var $currentCert;
/**
* The signature subject
*
* There's no guarantee \phpseclib\File\X509 is going to re-encode
an X.509 cert in the same way it was originally
* encoded so we take save the portion of the original cert that the
signature would have made for.
*
* @var string
* @access private
*/
var $signatureSubject;
/**
* Certificate Start Date
*
* @var string
* @access private
*/
var $startDate;
/**
* Certificate End Date
*
* @var string
* @access private
*/
var $endDate;
/**
* Serial Number
*
* @var string
* @access private
*/
var $serialNumber;
/**
* Key Identifier
*
* See {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.1
RFC5280#section-4.2.1.1} and
* {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.2
RFC5280#section-4.2.1.2}.
*
* @var string
* @access private
*/
var $currentKeyIdentifier;
/**
* CA Flag
*
* @var bool
* @access private
*/
var $caFlag = false;
/**
* SPKAC Challenge
*
* @var string
* @access private
*/
var $challenge;
/**
* Recursion Limit
*
* @var int
* @access private
*/
static $recur_limit = 5;
/**
* URL fetch flag
*
* @var bool
* @access private
*/
static $disable_url_fetch = false;
/**
* Default Constructor.
*
* @return \phpseclib\File\X509
* @access public
*/
function __construct()
{
// Explicitly Tagged Module, 1988 Syntax
// http://tools.ietf.org/html/rfc5280#appendix-A.1
$this->DirectoryString = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'teletexString' => array('type'
=> ASN1::TYPE_TELETEX_STRING),
'printableString' => array('type'
=> ASN1::TYPE_PRINTABLE_STRING),
'universalString' => array('type'
=> ASN1::TYPE_UNIVERSAL_STRING),
'utf8String' => array('type'
=> ASN1::TYPE_UTF8_STRING),
'bmpString' => array('type'
=> ASN1::TYPE_BMP_STRING)
)
);
$this->PKCS9String = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'ia5String' => array('type'
=> ASN1::TYPE_IA5_STRING),
'directoryString' => $this->DirectoryString
)
);
$this->AttributeValue = array('type' =>
ASN1::TYPE_ANY);
$AttributeType = array('type' =>
ASN1::TYPE_OBJECT_IDENTIFIER);
$AttributeTypeAndValue = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'type' => $AttributeType,
'value'=> $this->AttributeValue
)
);
/*
In practice, RDNs containing multiple name-value pairs (called
"multivalued RDNs") are rare,
but they can be useful at times when either there is no unique
attribute in the entry or you
want to ensure that the entry's DN contains some useful
identifying information.
-
https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName
*/
$this->RelativeDistinguishedName = array(
'type' => ASN1::TYPE_SET,
'min' => 1,
'max' => -1,
'children' => $AttributeTypeAndValue
);
// http://tools.ietf.org/html/rfc5280#section-4.1.2.4
$RDNSequence = array(
'type' => ASN1::TYPE_SEQUENCE,
// RDNSequence does not define a min or a max, which means it
doesn't have one
'min' => 0,
'max' => -1,
'children' => $this->RelativeDistinguishedName
);
$this->Name = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'rdnSequence' => $RDNSequence
)
);
// http://tools.ietf.org/html/rfc5280#section-4.1.1.2
$AlgorithmIdentifier = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'algorithm' => array('type' =>
ASN1::TYPE_OBJECT_IDENTIFIER),
'parameters' => array(
'type' =>
ASN1::TYPE_ANY,
'optional' => true
)
)
);
/*
A certificate using system MUST reject the certificate if it
encounters
a critical extension it does not recognize; however, a
non-critical
extension may be ignored if it is not recognized.
http://tools.ietf.org/html/rfc5280#section-4.2
*/
$Extension = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'extnId' => array('type' =>
ASN1::TYPE_OBJECT_IDENTIFIER),
'critical' => array(
'type' =>
ASN1::TYPE_BOOLEAN,
'optional' => true,
'default' => false
),
'extnValue' => array('type' =>
ASN1::TYPE_OCTET_STRING)
)
);
$this->Extensions = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
// technically, it's MAX, but we'll assume anything
< 0 is MAX
'max' => -1,
// if 'children' isn't an array then
'min' and 'max' must be defined
'children' => $Extension
);
$SubjectPublicKeyInfo = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'algorithm' => $AlgorithmIdentifier,
'subjectPublicKey' => array('type'
=> ASN1::TYPE_BIT_STRING)
)
);
$UniqueIdentifier = array('type' =>
ASN1::TYPE_BIT_STRING);
$Time = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'utcTime' => array('type' =>
ASN1::TYPE_UTC_TIME),
'generalTime' => array('type' =>
ASN1::TYPE_GENERALIZED_TIME)
)
);
// http://tools.ietf.org/html/rfc5280#section-4.1.2.5
$Validity = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'notBefore' => $Time,
'notAfter' => $Time
)
);
$CertificateSerialNumber = array('type' =>
ASN1::TYPE_INTEGER);
$Version = array(
'type' => ASN1::TYPE_INTEGER,
'mapping' => array('v1', 'v2',
'v3')
);
//
assert($TBSCertificate['children']['signature'] ==
$Certificate['children']['signatureAlgorithm'])
$TBSCertificate = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
// technically, default implies optional, but we'll
define it as being optional, none-the-less, just to
// reenforce that fact
'version' => array(
'constant' => 0,
'optional' =>
true,
'explicit' =>
true,
'default' =>
'v1'
) + $Version,
'serialNumber' =>
$CertificateSerialNumber,
'signature' =>
$AlgorithmIdentifier,
'issuer' => $this->Name,
'validity' => $Validity,
'subject' => $this->Name,
'subjectPublicKeyInfo' =>
$SubjectPublicKeyInfo,
// implicit means that the T in the TLV structure is to be
rewritten, regardless of the type
'issuerUniqueID' => array(
'constant' =>
1,
'optional' =>
true,
'implicit' =>
true
) + $UniqueIdentifier,
'subjectUniqueID' => array(
'constant' =>
2,
'optional' =>
true,
'implicit' =>
true
) + $UniqueIdentifier,
// <http://tools.ietf.org/html/rfc2459#page-74>
doesn't use the EXPLICIT keyword but if
// it's not IMPLICIT, it's EXPLICIT
'extensions' => array(
'constant' =>
3,
'optional' =>
true,
'explicit' =>
true
) + $this->Extensions
)
);
$this->Certificate = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'tbsCertificate' => $TBSCertificate,
'signatureAlgorithm' => $AlgorithmIdentifier,
'signature' =>
array('type' => ASN1::TYPE_BIT_STRING)
)
);
$this->KeyUsage = array(
'type' => ASN1::TYPE_BIT_STRING,
'mapping' => array(
'digitalSignature',
'nonRepudiation',
'keyEncipherment',
'dataEncipherment',
'keyAgreement',
'keyCertSign',
'cRLSign',
'encipherOnly',
'decipherOnly'
)
);
$this->BasicConstraints = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'cA' => array(
'type' =>
ASN1::TYPE_BOOLEAN,
'optional' =>
true,
'default' =>
false
),
'pathLenConstraint' => array(
'type' =>
ASN1::TYPE_INTEGER,
'optional' =>
true
)
)
);
$this->KeyIdentifier = array('type' =>
ASN1::TYPE_OCTET_STRING);
$OrganizationalUnitNames = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => 4, // ub-organizational-units
'children' => array('type' =>
ASN1::TYPE_PRINTABLE_STRING)
);
$PersonalName = array(
'type' => ASN1::TYPE_SET,
'children' => array(
'surname' => array(
'type' =>
ASN1::TYPE_PRINTABLE_STRING,
'constant' => 0,
'optional' => true,
'implicit' => true
),
'given-name' => array(
'type' =>
ASN1::TYPE_PRINTABLE_STRING,
'constant' => 1,
'optional' => true,
'implicit' => true
),
'initials' => array(
'type' =>
ASN1::TYPE_PRINTABLE_STRING,
'constant' => 2,
'optional' => true,
'implicit' => true
),
'generation-qualifier' => array(
'type' =>
ASN1::TYPE_PRINTABLE_STRING,
'constant' => 3,
'optional' => true,
'implicit' => true
)
)
);
$NumericUserIdentifier = array('type' =>
ASN1::TYPE_NUMERIC_STRING);
$OrganizationName = array('type' =>
ASN1::TYPE_PRINTABLE_STRING);
$PrivateDomainName = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'numeric' => array('type' =>
ASN1::TYPE_NUMERIC_STRING),
'printable' => array('type' =>
ASN1::TYPE_PRINTABLE_STRING)
)
);
$TerminalIdentifier = array('type' =>
ASN1::TYPE_PRINTABLE_STRING);
$NetworkAddress = array('type' =>
ASN1::TYPE_NUMERIC_STRING);
$AdministrationDomainName = array(
'type' => ASN1::TYPE_CHOICE,
// if class isn't present it's assumed to be
\phpseclib\File\ASN1::CLASS_UNIVERSAL or
// (if constant is present)
\phpseclib\File\ASN1::CLASS_CONTEXT_SPECIFIC
'class' => ASN1::CLASS_APPLICATION,
'cast' => 2,
'children' => array(
'numeric' => array('type' =>
ASN1::TYPE_NUMERIC_STRING),
'printable' => array('type' =>
ASN1::TYPE_PRINTABLE_STRING)
)
);
$CountryName = array(
'type' => ASN1::TYPE_CHOICE,
// if class isn't present it's assumed to be
\phpseclib\File\ASN1::CLASS_UNIVERSAL or
// (if constant is present)
\phpseclib\File\ASN1::CLASS_CONTEXT_SPECIFIC
'class' => ASN1::CLASS_APPLICATION,
'cast' => 1,
'children' => array(
'x121-dcc-code' =>
array('type' => ASN1::TYPE_NUMERIC_STRING),
'iso-3166-alpha2-code' =>
array('type' => ASN1::TYPE_PRINTABLE_STRING)
)
);
$AnotherName = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'type-id' => array('type' =>
ASN1::TYPE_OBJECT_IDENTIFIER),
'value' => array(
'type' => ASN1::TYPE_ANY,
'constant' => 0,
'optional' => true,
'explicit' => true
)
)
);
$ExtensionAttribute = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'extension-attribute-type' => array(
'type' =>
ASN1::TYPE_PRINTABLE_STRING,
'constant'
=> 0,
'optional'
=> true,
'implicit'
=> true
),
'extension-attribute-value' => array(
'type' =>
ASN1::TYPE_ANY,
'constant'
=> 1,
'optional'
=> true,
'explicit'
=> true
)
)
);
$ExtensionAttributes = array(
'type' => ASN1::TYPE_SET,
'min' => 1,
'max' => 256, // ub-extension-attributes
'children' => $ExtensionAttribute
);
$BuiltInDomainDefinedAttribute = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'type' => array('type' =>
ASN1::TYPE_PRINTABLE_STRING),
'value' => array('type' =>
ASN1::TYPE_PRINTABLE_STRING)
)
);
$BuiltInDomainDefinedAttributes = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => 4, // ub-domain-defined-attributes
'children' => $BuiltInDomainDefinedAttribute
);
$BuiltInStandardAttributes = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'country-name' =>
array('optional' => true) + $CountryName,
'administration-domain-name' =>
array('optional' => true) + $AdministrationDomainName,
'network-address' => array(
'constant' =>
0,
'optional' =>
true,
'implicit' =>
true
) + $NetworkAddress,
'terminal-identifier' => array(
'constant' =>
1,
'optional' =>
true,
'implicit' =>
true
) + $TerminalIdentifier,
'private-domain-name' => array(
'constant' =>
2,
'optional' =>
true,
'explicit' =>
true
) + $PrivateDomainName,
'organization-name' => array(
'constant' =>
3,
'optional' =>
true,
'implicit' =>
true
) + $OrganizationName,
'numeric-user-identifier' => array(
'constant' =>
4,
'optional' =>
true,
'implicit' =>
true
) + $NumericUserIdentifier,
'personal-name' => array(
'constant' =>
5,
'optional' =>
true,
'implicit' =>
true
) + $PersonalName,
'organizational-unit-names' => array(
'constant' =>
6,
'optional' =>
true,
'implicit' =>
true
) + $OrganizationalUnitNames
)
);
$ORAddress = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'built-in-standard-attributes' =>
$BuiltInStandardAttributes,
'built-in-domain-defined-attributes' =>
array('optional' => true) + $BuiltInDomainDefinedAttributes,
'extension-attributes' =>
array('optional' => true) + $ExtensionAttributes
)
);
$EDIPartyName = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'nameAssigner' => array(
'constant' => 0,
'optional' => true,
'implicit' => true
) + $this->DirectoryString,
// partyName is technically required but
\phpseclib\File\ASN1 doesn't currently support non-optional constants
and
// setting it to optional gets the job done in any event.
'partyName' => array(
'constant' => 1,
'optional' => true,
'implicit' => true
) + $this->DirectoryString
)
);
$GeneralName = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'otherName' => array(
'constant' =>
0,
'optional' =>
true,
'implicit' =>
true
) + $AnotherName,
'rfc822Name' => array(
'type' =>
ASN1::TYPE_IA5_STRING,
'constant' =>
1,
'optional' =>
true,
'implicit' =>
true
),
'dNSName' => array(
'type' =>
ASN1::TYPE_IA5_STRING,
'constant' =>
2,
'optional' =>
true,
'implicit' =>
true
),
'x400Address' => array(
'constant' =>
3,
'optional' =>
true,
'implicit' =>
true
) + $ORAddress,
'directoryName' => array(
'constant' =>
4,
'optional' =>
true,
'explicit' =>
true
) + $this->Name,
'ediPartyName' => array(
'constant' =>
5,
'optional' =>
true,
'implicit' =>
true
) + $EDIPartyName,
'uniformResourceIdentifier' => array(
'type' =>
ASN1::TYPE_IA5_STRING,
'constant' =>
6,
'optional' =>
true,
'implicit' =>
true
),
'iPAddress' => array(
'type' =>
ASN1::TYPE_OCTET_STRING,
'constant' =>
7,
'optional' =>
true,
'implicit' =>
true
),
'registeredID' => array(
'type' =>
ASN1::TYPE_OBJECT_IDENTIFIER,
'constant' =>
8,
'optional' =>
true,
'implicit' =>
true
)
)
);
$GeneralNames = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $GeneralName
);
$this->IssuerAltName = $GeneralNames;
$ReasonFlags = array(
'type' => ASN1::TYPE_BIT_STRING,
'mapping' => array(
'unused',
'keyCompromise',
'cACompromise',
'affiliationChanged',
'superseded',
'cessationOfOperation',
'certificateHold',
'privilegeWithdrawn',
'aACompromise'
)
);
$DistributionPointName = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'fullName' => array(
'constant' =>
0,
'optional' =>
true,
'implicit' =>
true
) + $GeneralNames,
'nameRelativeToCRLIssuer' => array(
'constant' =>
1,
'optional' =>
true,
'implicit' =>
true
) +
$this->RelativeDistinguishedName
)
);
$DistributionPoint = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'distributionPoint' => array(
'constant' =>
0,
'optional' =>
true,
'explicit' =>
true
) + $DistributionPointName,
'reasons' => array(
'constant' =>
1,
'optional' =>
true,
'implicit' =>
true
) + $ReasonFlags,
'cRLIssuer' => array(
'constant' =>
2,
'optional' =>
true,
'implicit' =>
true
) + $GeneralNames
)
);
$this->CRLDistributionPoints = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $DistributionPoint
);
$this->AuthorityKeyIdentifier = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'keyIdentifier' => array(
'constant' =>
0,
'optional' =>
true,
'implicit' =>
true
) + $this->KeyIdentifier,
'authorityCertIssuer' => array(
'constant' =>
1,
'optional' =>
true,
'implicit' =>
true
) + $GeneralNames,
'authorityCertSerialNumber' => array(
'constant' =>
2,
'optional' =>
true,
'implicit' =>
true
) + $CertificateSerialNumber
)
);
$PolicyQualifierId = array('type' =>
ASN1::TYPE_OBJECT_IDENTIFIER);
$PolicyQualifierInfo = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'policyQualifierId' => $PolicyQualifierId,
'qualifier' => array('type'
=> ASN1::TYPE_ANY)
)
);
$CertPolicyId = array('type' =>
ASN1::TYPE_OBJECT_IDENTIFIER);
$PolicyInformation = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'policyIdentifier' => $CertPolicyId,
'policyQualifiers' => array(
'type' =>
ASN1::TYPE_SEQUENCE,
'min' => 0,
'max' => -1,
'optional' => true,
'children' =>
$PolicyQualifierInfo
)
)
);
$this->CertificatePolicies = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $PolicyInformation
);
$this->PolicyMappings = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => array(
'type' =>
ASN1::TYPE_SEQUENCE,
'children' => array(
'issuerDomainPolicy' =>
$CertPolicyId,
'subjectDomainPolicy' =>
$CertPolicyId
)
)
);
$KeyPurposeId = array('type' =>
ASN1::TYPE_OBJECT_IDENTIFIER);
$this->ExtKeyUsageSyntax = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $KeyPurposeId
);
$AccessDescription = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'accessMethod' => array('type'
=> ASN1::TYPE_OBJECT_IDENTIFIER),
'accessLocation' => $GeneralName
)
);
$this->AuthorityInfoAccessSyntax = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $AccessDescription
);
$this->SubjectInfoAccessSyntax = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $AccessDescription
);
$this->SubjectAltName = $GeneralNames;
$this->PrivateKeyUsagePeriod = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'notBefore' => array(
'constant' =>
0,
'optional' =>
true,
'implicit' =>
true,
'type' =>
ASN1::TYPE_GENERALIZED_TIME),
'notAfter' => array(
'constant' =>
1,
'optional' =>
true,
'implicit' =>
true,
'type' =>
ASN1::TYPE_GENERALIZED_TIME)
)
);
$BaseDistance = array('type' => ASN1::TYPE_INTEGER);
$GeneralSubtree = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'base' => $GeneralName,
'minimum' => array(
'constant' => 0,
'optional' => true,
'implicit' => true,
'default' => new
BigInteger(0)
) + $BaseDistance,
'maximum' => array(
'constant' => 1,
'optional' => true,
'implicit' => true,
) + $BaseDistance
)
);
$GeneralSubtrees = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $GeneralSubtree
);
$this->NameConstraints = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'permittedSubtrees' => array(
'constant' => 0,
'optional' => true,
'implicit' => true
) + $GeneralSubtrees,
'excludedSubtrees' => array(
'constant' => 1,
'optional' => true,
'implicit' => true
) + $GeneralSubtrees
)
);
$this->CPSuri = array('type' =>
ASN1::TYPE_IA5_STRING);
$DisplayText = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'ia5String' => array('type'
=> ASN1::TYPE_IA5_STRING),
'visibleString' => array('type'
=> ASN1::TYPE_VISIBLE_STRING),
'bmpString' => array('type'
=> ASN1::TYPE_BMP_STRING),
'utf8String' => array('type'
=> ASN1::TYPE_UTF8_STRING)
)
);
$NoticeReference = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'organization' => $DisplayText,
'noticeNumbers' => array(
'type' =>
ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => 200,
'children' =>
array('type' => ASN1::TYPE_INTEGER)
)
)
);
$this->UserNotice = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'noticeRef' => array(
'optional' => true,
'implicit' => true
) + $NoticeReference,
'explicitText' => array(
'optional' => true,
'implicit' => true
) + $DisplayText
)
);
// mapping is from
<http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn3.html>
$this->netscape_cert_type = array(
'type' => ASN1::TYPE_BIT_STRING,
'mapping' => array(
'SSLClient',
'SSLServer',
'Email',
'ObjectSigning',
'Reserved',
'SSLCA',
'EmailCA',
'ObjectSigningCA'
)
);
$this->netscape_comment = array('type' =>
ASN1::TYPE_IA5_STRING);
$this->netscape_ca_policy_url = array('type' =>
ASN1::TYPE_IA5_STRING);
// attribute is used in RFC2986 but we're using the RFC5280
definition
$Attribute = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'type' => $AttributeType,
'value'=> array(
'type' => ASN1::TYPE_SET,
'min' => 1,
'max' => -1,
'children' =>
$this->AttributeValue
)
)
);
$this->SubjectDirectoryAttributes = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $Attribute
);
// adapted from <http://tools.ietf.org/html/rfc2986>
$Attributes = array(
'type' => ASN1::TYPE_SET,
'min' => 1,
'max' => -1,
'children' => $Attribute
);
$CertificationRequestInfo = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'version' => array(
'type' =>
ASN1::TYPE_INTEGER,
'mapping' =>
array('v1')
),
'subject' => $this->Name,
'subjectPKInfo' => $SubjectPublicKeyInfo,
'attributes' => array(
'constant' => 0,
'optional' => true,
'implicit' => true
) + $Attributes,
)
);
$this->CertificationRequest = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'certificationRequestInfo' =>
$CertificationRequestInfo,
'signatureAlgorithm' =>
$AlgorithmIdentifier,
'signature' =>
array('type' => ASN1::TYPE_BIT_STRING)
)
);
$RevokedCertificate = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'userCertificate' =>
$CertificateSerialNumber,
'revocationDate' => $Time,
'crlEntryExtensions' => array(
'optional' => true
) +
$this->Extensions
)
);
$TBSCertList = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'version' => array(
'optional' =>
true,
'default' =>
'v1'
) + $Version,
'signature' => $AlgorithmIdentifier,
'issuer' => $this->Name,
'thisUpdate' => $Time,
'nextUpdate' => array(
'optional' =>
true
) + $Time,
'revokedCertificates' => array(
'type' =>
ASN1::TYPE_SEQUENCE,
'optional' =>
true,
'min' => 0,
'max' => -1,
'children' =>
$RevokedCertificate
),
'crlExtensions' => array(
'constant' => 0,
'optional' =>
true,
'explicit' =>
true
) + $this->Extensions
)
);
$this->CertificateList = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'tbsCertList' => $TBSCertList,
'signatureAlgorithm' => $AlgorithmIdentifier,
'signature' => array('type'
=> ASN1::TYPE_BIT_STRING)
)
);
$this->CRLNumber = array('type' =>
ASN1::TYPE_INTEGER);
$this->CRLReason = array('type' =>
ASN1::TYPE_ENUMERATED,
'mapping' => array(
'unspecified',
'keyCompromise',
'cACompromise',
'affiliationChanged',
'superseded',
'cessationOfOperation',
'certificateHold',
// Value 7 is not used.
8 => 'removeFromCRL',
'privilegeWithdrawn',
'aACompromise'
)
);
$this->IssuingDistributionPoint = array('type' =>
ASN1::TYPE_SEQUENCE,
'children' => array(
'distributionPoint' => array(
'constant'
=> 0,
'optional'
=> true,
'explicit'
=> true
) + $DistributionPointName,
'onlyContainsUserCerts' => array(
'type'
=> ASN1::TYPE_BOOLEAN,
'constant'
=> 1,
'optional'
=> true,
'default'
=> false,
'implicit'
=> true
),
'onlyContainsCACerts' => array(
'type'
=> ASN1::TYPE_BOOLEAN,
'constant'
=> 2,
'optional'
=> true,
'default'
=> false,
'implicit'
=> true
),
'onlySomeReasons' => array(
'constant'
=> 3,
'optional'
=> true,
'implicit'
=> true
) + $ReasonFlags,
'indirectCRL' => array(
'type'
=> ASN1::TYPE_BOOLEAN,
'constant'
=> 4,
'optional'
=> true,
'default'
=> false,
'implicit'
=> true
),
'onlyContainsAttributeCerts' => array(
'type'
=> ASN1::TYPE_BOOLEAN,
'constant'
=> 5,
'optional'
=> true,
'default'
=> false,
'implicit'
=> true
)
)
);
$this->InvalidityDate = array('type' =>
ASN1::TYPE_GENERALIZED_TIME);
$this->CertificateIssuer = $GeneralNames;
$this->HoldInstructionCode = array('type' =>
ASN1::TYPE_OBJECT_IDENTIFIER);
$PublicKeyAndChallenge = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'spki' => $SubjectPublicKeyInfo,
'challenge' => array('type' =>
ASN1::TYPE_IA5_STRING)
)
);
$this->SignedPublicKeyAndChallenge = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'publicKeyAndChallenge' =>
$PublicKeyAndChallenge,
'signatureAlgorithm' =>
$AlgorithmIdentifier,
'signature' =>
array('type' => ASN1::TYPE_BIT_STRING)
)
);
$this->PostalAddress = array(
'type' => ASN1::TYPE_SEQUENCE,
'optional' => true,
'min' => 1,
'max' => -1,
'children' => $this->DirectoryString
);
// OIDs from RFC5280 and those RFCs mentioned in
RFC5280#section-4.1.1.2
$this->oids = array(
'1.3.6.1.5.5.7' => 'id-pkix',
'1.3.6.1.5.5.7.1' => 'id-pe',
'1.3.6.1.5.5.7.2' => 'id-qt',
'1.3.6.1.5.5.7.3' => 'id-kp',
'1.3.6.1.5.5.7.48' => 'id-ad',
'1.3.6.1.5.5.7.2.1' => 'id-qt-cps',
'1.3.6.1.5.5.7.2.2' => 'id-qt-unotice',
'1.3.6.1.5.5.7.48.1' =>'id-ad-ocsp',
'1.3.6.1.5.5.7.48.2' =>
'id-ad-caIssuers',
'1.3.6.1.5.5.7.48.3' =>
'id-ad-timeStamping',
'1.3.6.1.5.5.7.48.5' =>
'id-ad-caRepository',
'2.5.4' => 'id-at',
'2.5.4.41' => 'id-at-name',
'2.5.4.4' => 'id-at-surname',
'2.5.4.42' => 'id-at-givenName',
'2.5.4.43' => 'id-at-initials',
'2.5.4.44' =>
'id-at-generationQualifier',
'2.5.4.3' => 'id-at-commonName',
'2.5.4.7' => 'id-at-localityName',
'2.5.4.8' =>
'id-at-stateOrProvinceName',
'2.5.4.10' => 'id-at-organizationName',
'2.5.4.11' =>
'id-at-organizationalUnitName',
'2.5.4.12' => 'id-at-title',
'2.5.4.13' => 'id-at-description',
'2.5.4.46' => 'id-at-dnQualifier',
'2.5.4.6' => 'id-at-countryName',
'2.5.4.5' => 'id-at-serialNumber',
'2.5.4.65' => 'id-at-pseudonym',
'2.5.4.17' => 'id-at-postalCode',
'2.5.4.9' => 'id-at-streetAddress',
'2.5.4.45' => 'id-at-uniqueIdentifier',
'2.5.4.72' => 'id-at-role',
'2.5.4.16' => 'id-at-postalAddress',
'0.9.2342.19200300.100.1.25' =>
'id-domainComponent',
'1.2.840.113549.1.9' => 'pkcs-9',
'1.2.840.113549.1.9.1' =>
'pkcs-9-at-emailAddress',
'2.5.29' => 'id-ce',
'2.5.29.35' =>
'id-ce-authorityKeyIdentifier',
'2.5.29.14' =>
'id-ce-subjectKeyIdentifier',
'2.5.29.15' => 'id-ce-keyUsage',
'2.5.29.16' =>
'id-ce-privateKeyUsagePeriod',
'2.5.29.32' =>
'id-ce-certificatePolicies',
'2.5.29.32.0' => 'anyPolicy',
'2.5.29.33' => 'id-ce-policyMappings',
'2.5.29.17' => 'id-ce-subjectAltName',
'2.5.29.18' => 'id-ce-issuerAltName',
'2.5.29.9' =>
'id-ce-subjectDirectoryAttributes',
'2.5.29.19' => 'id-ce-basicConstraints',
'2.5.29.30' => 'id-ce-nameConstraints',
'2.5.29.36' =>
'id-ce-policyConstraints',
'2.5.29.31' =>
'id-ce-cRLDistributionPoints',
'2.5.29.37' => 'id-ce-extKeyUsage',
'2.5.29.37.0' => 'anyExtendedKeyUsage',
'1.3.6.1.5.5.7.3.1' =>
'id-kp-serverAuth',
'1.3.6.1.5.5.7.3.2' =>
'id-kp-clientAuth',
'1.3.6.1.5.5.7.3.3' =>
'id-kp-codeSigning',
'1.3.6.1.5.5.7.3.4' =>
'id-kp-emailProtection',
'1.3.6.1.5.5.7.3.8' =>
'id-kp-timeStamping',
'1.3.6.1.5.5.7.3.9' =>
'id-kp-OCSPSigning',
'2.5.29.54' => 'id-ce-inhibitAnyPolicy',
'2.5.29.46' => 'id-ce-freshestCRL',
'1.3.6.1.5.5.7.1.1' =>
'id-pe-authorityInfoAccess',
'1.3.6.1.5.5.7.1.11' =>
'id-pe-subjectInfoAccess',
'2.5.29.20' => 'id-ce-cRLNumber',
'2.5.29.28' =>
'id-ce-issuingDistributionPoint',
'2.5.29.27' =>
'id-ce-deltaCRLIndicator',
'2.5.29.21' => 'id-ce-cRLReasons',
'2.5.29.29' =>
'id-ce-certificateIssuer',
'2.5.29.23' =>
'id-ce-holdInstructionCode',
'1.2.840.10040.2' => 'holdInstruction',
'1.2.840.10040.2.1' =>
'id-holdinstruction-none',
'1.2.840.10040.2.2' =>
'id-holdinstruction-callissuer',
'1.2.840.10040.2.3' =>
'id-holdinstruction-reject',
'2.5.29.24' => 'id-ce-invalidityDate',
'1.2.840.113549.2.2' => 'md2',
'1.2.840.113549.2.5' => 'md5',
'1.3.14.3.2.26' => 'id-sha1',
'1.2.840.10040.4.1' => 'id-dsa',
'1.2.840.10040.4.3' =>
'id-dsa-with-sha1',
'1.2.840.113549.1.1' => 'pkcs-1',
'1.2.840.113549.1.1.1' =>
'rsaEncryption',
'1.2.840.113549.1.1.2' =>
'md2WithRSAEncryption',
'1.2.840.113549.1.1.4' =>
'md5WithRSAEncryption',
'1.2.840.113549.1.1.5' =>
'sha1WithRSAEncryption',
'1.2.840.10046.2.1' => 'dhpublicnumber',
'2.16.840.1.101.2.1.1.22' =>
'id-keyExchangeAlgorithm',
'1.2.840.10045' => 'ansi-X9-62',
'1.2.840.10045.4' => 'id-ecSigType',
'1.2.840.10045.4.1' =>
'ecdsa-with-SHA1',
'1.2.840.10045.1' => 'id-fieldType',
'1.2.840.10045.1.1' => 'prime-field',
'1.2.840.10045.1.2' =>
'characteristic-two-field',
'1.2.840.10045.1.2.3' =>
'id-characteristic-two-basis',
'1.2.840.10045.1.2.3.1' => 'gnBasis',
'1.2.840.10045.1.2.3.2' => 'tpBasis',
'1.2.840.10045.1.2.3.3' => 'ppBasis',
'1.2.840.10045.2' => 'id-publicKeyType',
'1.2.840.10045.2.1' => 'id-ecPublicKey',
'1.2.840.10045.3' => 'ellipticCurve',
'1.2.840.10045.3.0' => 'c-TwoCurve',
'1.2.840.10045.3.0.1' => 'c2pnb163v1',
'1.2.840.10045.3.0.2' => 'c2pnb163v2',
'1.2.840.10045.3.0.3' => 'c2pnb163v3',
'1.2.840.10045.3.0.4' => 'c2pnb176w1',
'1.2.840.10045.3.0.5' => 'c2pnb191v1',
'1.2.840.10045.3.0.6' => 'c2pnb191v2',
'1.2.840.10045.3.0.7' => 'c2pnb191v3',
'1.2.840.10045.3.0.8' => 'c2pnb191v4',
'1.2.840.10045.3.0.9' => 'c2pnb191v5',
'1.2.840.10045.3.0.10' => 'c2pnb208w1',
'1.2.840.10045.3.0.11' => 'c2pnb239v1',
'1.2.840.10045.3.0.12' => 'c2pnb239v2',
'1.2.840.10045.3.0.13' => 'c2pnb239v3',
'1.2.840.10045.3.0.14' => 'c2pnb239v4',
'1.2.840.10045.3.0.15' => 'c2pnb239v5',
'1.2.840.10045.3.0.16' => 'c2pnb272w1',
'1.2.840.10045.3.0.17' => 'c2pnb304w1',
'1.2.840.10045.3.0.18' => 'c2pnb359v1',
'1.2.840.10045.3.0.19' => 'c2pnb368w1',
'1.2.840.10045.3.0.20' => 'c2pnb431r1',
'1.2.840.10045.3.1' => 'primeCurve',
'1.2.840.10045.3.1.1' => 'prime192v1',
'1.2.840.10045.3.1.2' => 'prime192v2',
'1.2.840.10045.3.1.3' => 'prime192v3',
'1.2.840.10045.3.1.4' => 'prime239v1',
'1.2.840.10045.3.1.5' => 'prime239v2',
'1.2.840.10045.3.1.6' => 'prime239v3',
'1.2.840.10045.3.1.7' => 'prime256v1',
'1.2.840.113549.1.1.7' =>
'id-RSAES-OAEP',
'1.2.840.113549.1.1.9' =>
'id-pSpecified',
'1.2.840.113549.1.1.10' =>
'id-RSASSA-PSS',
'1.2.840.113549.1.1.8' => 'id-mgf1',
'1.2.840.113549.1.1.14' =>
'sha224WithRSAEncryption',
'1.2.840.113549.1.1.11' =>
'sha256WithRSAEncryption',
'1.2.840.113549.1.1.12' =>
'sha384WithRSAEncryption',
'1.2.840.113549.1.1.13' =>
'sha512WithRSAEncryption',
'2.16.840.1.101.3.4.2.4' => 'id-sha224',
'2.16.840.1.101.3.4.2.1' => 'id-sha256',
'2.16.840.1.101.3.4.2.2' => 'id-sha384',
'2.16.840.1.101.3.4.2.3' => 'id-sha512',
'1.2.643.2.2.4' =>
'id-GostR3411-94-with-GostR3410-94',
'1.2.643.2.2.3' =>
'id-GostR3411-94-with-GostR3410-2001',
'1.2.643.2.2.20' => 'id-GostR3410-2001',
'1.2.643.2.2.19' => 'id-GostR3410-94',
// Netscape Object Identifiers from "Netscape Certificate
Extensions"
'2.16.840.1.113730' => 'netscape',
'2.16.840.1.113730.1' =>
'netscape-cert-extension',
'2.16.840.1.113730.1.1' =>
'netscape-cert-type',
'2.16.840.1.113730.1.13' =>
'netscape-comment',
'2.16.840.1.113730.1.8' =>
'netscape-ca-policy-url',
// the following are X.509 extensions not supported by
phpseclib
'1.3.6.1.5.5.7.1.12' =>
'id-pe-logotype',
'1.2.840.113533.7.65.0' =>
'entrustVersInfo',
'2.16.840.1.113733.1.6.9' =>
'verisignPrivate',
// for Certificate Signing Requests
// see http://tools.ietf.org/html/rfc2985
'1.2.840.113549.1.9.2' =>
'pkcs-9-at-unstructuredName', // PKCS #9 unstructured name
'1.2.840.113549.1.9.7' =>
'pkcs-9-at-challengePassword', // Challenge password for
certificate revocations
'1.2.840.113549.1.9.14' =>
'pkcs-9-at-extensionRequest' // Certificate extension request
);
}
/**
* Load X.509 certificate
*
* Returns an associative array describing the X.509 cert or a false if
the cert failed to load
*
* @param string $cert
* @param int $mode
* @access public
* @return mixed
*/
function loadX509($cert, $mode = self::FORMAT_AUTO_DETECT)
{
if (is_array($cert) &&
isset($cert['tbsCertificate'])) {
unset($this->currentCert);
unset($this->currentKeyIdentifier);
$this->dn =
$cert['tbsCertificate']['subject'];
if (!isset($this->dn)) {
return false;
}
$this->currentCert = $cert;
$currentKeyIdentifier =
$this->getExtension('id-ce-subjectKeyIdentifier');
$this->currentKeyIdentifier =
is_string($currentKeyIdentifier) ? $currentKeyIdentifier : null;
unset($this->signatureSubject);
return $cert;
}
$asn1 = new ASN1();
if ($mode != self::FORMAT_DER) {
$newcert = $this->_extractBER($cert);
if ($mode == self::FORMAT_PEM && $cert == $newcert) {
return false;
}
$cert = $newcert;
}
if ($cert === false) {
$this->currentCert = false;
return false;
}
$asn1->loadOIDs($this->oids);
$decoded = $asn1->decodeBER($cert);
if (!empty($decoded)) {
$x509 = $asn1->asn1map($decoded[0], $this->Certificate);
}
if (!isset($x509) || $x509 === false) {
$this->currentCert = false;
return false;
}
$this->signatureSubject = substr($cert,
$decoded[0]['content'][0]['start'],
$decoded[0]['content'][0]['length']);
if ($this->_isSubArrayValid($x509,
'tbsCertificate/extensions')) {
$this->_mapInExtensions($x509,
'tbsCertificate/extensions', $asn1);
}
$this->_mapInDNs($x509,
'tbsCertificate/issuer/rdnSequence', $asn1);
$this->_mapInDNs($x509,
'tbsCertificate/subject/rdnSequence', $asn1);
$key =
&$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'];
$key =
$this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
$key);
$this->currentCert = $x509;
$this->dn =
$x509['tbsCertificate']['subject'];
$currentKeyIdentifier =
$this->getExtension('id-ce-subjectKeyIdentifier');
$this->currentKeyIdentifier = is_string($currentKeyIdentifier) ?
$currentKeyIdentifier : null;
return $x509;
}
/**
* Save X.509 certificate
*
* @param array $cert
* @param int $format optional
* @access public
* @return string
*/
function saveX509($cert, $format = self::FORMAT_PEM)
{
if (!is_array($cert) || !isset($cert['tbsCertificate']))
{
return false;
}
switch (true) {
// "case !$a: case !$b: break; default: whatever();"
is the same thing as "if ($a && $b) whatever()"
case !($algorithm = $this->_subArray($cert,
'tbsCertificate/subjectPublicKeyInfo/algorithm/algorithm')):
case
is_object($cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
break;
default:
switch ($algorithm) {
case 'rsaEncryption':
$cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']
= base64_encode("\0" .
base64_decode(preg_replace('#-.+-|[\r\n]#', '',
$cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'])));
/* "[For RSA keys] the parameters field MUST
have ASN.1 type NULL for this algorithm identifier."
--
https://tools.ietf.org/html/rfc3279#section-2.3.1
given that and the fact that RSA keys appear ot
be the only key type for which the parameters field can be blank,
it seems like perhaps the ASN.1 description
ought not say the parameters field is OPTIONAL, but whatever.
*/
$cert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters']
= null;
//
https://tools.ietf.org/html/rfc3279#section-2.2.1
$cert['signatureAlgorithm']['parameters'] = null;
$cert['tbsCertificate']['signature']['parameters']
= null;
}
}
$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$type_utf8_string = array('type' =>
ASN1::TYPE_UTF8_STRING);
$filters['tbsCertificate']['signature']['parameters']
= $type_utf8_string;
$filters['tbsCertificate']['signature']['issuer']['rdnSequence']['value']
= $type_utf8_string;
$filters['tbsCertificate']['issuer']['rdnSequence']['value']
= $type_utf8_string;
$filters['tbsCertificate']['subject']['rdnSequence']['value']
= $type_utf8_string;
$filters['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters']
= $type_utf8_string;
$filters['signatureAlgorithm']['parameters'] =
$type_utf8_string;
$filters['authorityCertIssuer']['directoryName']['rdnSequence']['value']
= $type_utf8_string;
//$filters['policyQualifiers']['qualifier'] =
$type_utf8_string;
$filters['distributionPoint']['fullName']['directoryName']['rdnSequence']['value']
= $type_utf8_string;
$filters['directoryName']['rdnSequence']['value']
= $type_utf8_string;
/* in the case of policyQualifiers/qualifier, the type has to be
\phpseclib\File\ASN1::TYPE_IA5_STRING.
\phpseclib\File\ASN1::TYPE_PRINTABLE_STRING will cause
OpenSSL's X.509 parser to spit out random
characters.
*/
$filters['policyQualifiers']['qualifier']
= array('type' => ASN1::TYPE_IA5_STRING);
$asn1->loadFilters($filters);
$this->_mapOutExtensions($cert,
'tbsCertificate/extensions', $asn1);
$this->_mapOutDNs($cert,
'tbsCertificate/issuer/rdnSequence', $asn1);
$this->_mapOutDNs($cert,
'tbsCertificate/subject/rdnSequence', $asn1);
$cert = $asn1->encodeDER($cert, $this->Certificate);
switch ($format) {
case self::FORMAT_DER:
return $cert;
// case self::FORMAT_PEM:
default:
return "-----BEGIN CERTIFICATE-----\r\n" .
chunk_split(base64_encode($cert), 64) . '-----END
CERTIFICATE-----';
}
}
/**
* Map extension values from octet string to extension-specific
internal
* format.
*
* @param array $root (by reference)
* @param string $path
* @param object $asn1
* @access private
*/
function _mapInExtensions(&$root, $path, $asn1)
{
$extensions = &$this->_subArrayUnchecked($root, $path);
if ($extensions) {
for ($i = 0; $i < count($extensions); $i++) {
$id = $extensions[$i]['extnId'];
$value = &$extensions[$i]['extnValue'];
$value = base64_decode($value);
$decoded = $asn1->decodeBER($value);
/* [extnValue] contains the DER encoding of an ASN.1 value
corresponding to the extension type identified by extnID
*/
$map = $this->_getMapping($id);
if (!is_bool($map)) {
$decoder = $id == 'id-ce-nameConstraints' ?
array($this, '_decodeNameConstraintIP') :
array($this, '_decodeIP');
$mapped = $asn1->asn1map($decoded[0], $map,
array('iPAddress' => $decoder));
$value = $mapped === false ? $decoded[0] : $mapped;
if ($id == 'id-ce-certificatePolicies') {
for ($j = 0; $j < count($value); $j++) {
if
(!isset($value[$j]['policyQualifiers'])) {
continue;
}
for ($k = 0; $k <
count($value[$j]['policyQualifiers']); $k++) {
$subid =
$value[$j]['policyQualifiers'][$k]['policyQualifierId'];
$map = $this->_getMapping($subid);
$subvalue =
&$value[$j]['policyQualifiers'][$k]['qualifier'];
if ($map !== false) {
$decoded =
$asn1->decodeBER($subvalue);
$mapped =
$asn1->asn1map($decoded[0], $map);
$subvalue = $mapped === false ?
$decoded[0] : $mapped;
}
}
}
}
} else {
$value = base64_encode($value);
}
}
}
}
/**
* Map extension values from extension-specific internal format to
* octet string.
*
* @param array $root (by reference)
* @param string $path
* @param object $asn1
* @access private
*/
function _mapOutExtensions(&$root, $path, $asn1)
{
$extensions = &$this->_subArray($root, $path);
if (is_array($extensions)) {
$size = count($extensions);
for ($i = 0; $i < $size; $i++) {
if ($extensions[$i] instanceof Element) {
continue;
}
$id = $extensions[$i]['extnId'];
$value = &$extensions[$i]['extnValue'];
switch ($id) {
case 'id-ce-certificatePolicies':
for ($j = 0; $j < count($value); $j++) {
if
(!isset($value[$j]['policyQualifiers'])) {
continue;
}
for ($k = 0; $k <
count($value[$j]['policyQualifiers']); $k++) {
$subid =
$value[$j]['policyQualifiers'][$k]['policyQualifierId'];
$map = $this->_getMapping($subid);
$subvalue =
&$value[$j]['policyQualifiers'][$k]['qualifier'];
if ($map !== false) {
// by default \phpseclib\File\ASN1 will
try to render qualifier as a \phpseclib\File\ASN1::TYPE_IA5_STRING since
it's
// actual type is
\phpseclib\File\ASN1::TYPE_ANY
$subvalue = new
Element($asn1->encodeDER($subvalue, $map));
}
}
}
break;
case 'id-ce-authorityKeyIdentifier': // use
00 as the serial number instead of an empty string
if
(isset($value['authorityCertSerialNumber'])) {
if
($value['authorityCertSerialNumber']->toBytes() ==
'') {
$temp = chr((ASN1::CLASS_CONTEXT_SPECIFIC
<< 6) | 2) . "\1\0";
$value['authorityCertSerialNumber'] = new Element($temp);
}
}
}
/* [extnValue] contains the DER encoding of an ASN.1 value
corresponding to the extension type identified by extnID
*/
$map = $this->_getMapping($id);
if (is_bool($map)) {
if (!$map) {
user_error($id . ' is not a currently
supported extension');
unset($extensions[$i]);
}
} else {
$temp = $asn1->encodeDER($value, $map,
array('iPAddress' => array($this, '_encodeIP')));
$value = base64_encode($temp);
}
}
}
}
/**
* Map attribute values from ANY type to attribute-specific internal
* format.
*
* @param array $root (by reference)
* @param string $path
* @param object $asn1
* @access private
*/
function _mapInAttributes(&$root, $path, $asn1)
{
$attributes = &$this->_subArray($root, $path);
if (is_array($attributes)) {
for ($i = 0; $i < count($attributes); $i++) {
$id = $attributes[$i]['type'];
/* $value contains the DER encoding of an ASN.1 value
corresponding to the attribute type identified by type
*/
$map = $this->_getMapping($id);
if (is_array($attributes[$i]['value'])) {
$values = &$attributes[$i]['value'];
for ($j = 0; $j < count($values); $j++) {
$value = $asn1->encodeDER($values[$j],
$this->AttributeValue);
$decoded = $asn1->decodeBER($value);
if (!is_bool($map)) {
$mapped = $asn1->asn1map($decoded[0], $map);
if ($mapped !== false) {
$values[$j] = $mapped;
}
if ($id ==
'pkcs-9-at-extensionRequest' &&
$this->_isSubArrayValid($values, $j)) {
$this->_mapInExtensions($values, $j,
$asn1);
}
} elseif ($map) {
$values[$j] = base64_encode($value);
}
}
}
}
}
}
/**
* Map attribute values from attribute-specific internal format to
* ANY type.
*
* @param array $root (by reference)
* @param string $path
* @param object $asn1
* @access private
*/
function _mapOutAttributes(&$root, $path, $asn1)
{
$attributes = &$this->_subArray($root, $path);
if (is_array($attributes)) {
$size = count($attributes);
for ($i = 0; $i < $size; $i++) {
/* [value] contains the DER encoding of an ASN.1 value
corresponding to the attribute type identified by type
*/
$id = $attributes[$i]['type'];
$map = $this->_getMapping($id);
if ($map === false) {
user_error($id . ' is not a currently supported
attribute', E_USER_NOTICE);
unset($attributes[$i]);
} elseif (is_array($attributes[$i]['value'])) {
$values = &$attributes[$i]['value'];
for ($j = 0; $j < count($values); $j++) {
switch ($id) {
case 'pkcs-9-at-extensionRequest':
$this->_mapOutExtensions($values, $j,
$asn1);
break;
}
if (!is_bool($map)) {
$temp = $asn1->encodeDER($values[$j], $map);
$decoded = $asn1->decodeBER($temp);
$values[$j] = $asn1->asn1map($decoded[0],
$this->AttributeValue);
}
}
}
}
}
}
/**
* Map DN values from ANY type to DN-specific internal
* format.
*
* @param array $root (by reference)
* @param string $path
* @param object $asn1
* @access private
*/
function _mapInDNs(&$root, $path, $asn1)
{
$dns = &$this->_subArray($root, $path);
if (is_array($dns)) {
for ($i = 0; $i < count($dns); $i++) {
for ($j = 0; $j < count($dns[$i]); $j++) {
$type = $dns[$i][$j]['type'];
$value = &$dns[$i][$j]['value'];
if (is_object($value) && $value instanceof
Element) {
$map = $this->_getMapping($type);
if (!is_bool($map)) {
$decoded = $asn1->decodeBER($value);
$value = $asn1->asn1map($decoded[0], $map);
}
}
}
}
}
}
/**
* Map DN values from DN-specific internal format to
* ANY type.
*
* @param array $root (by reference)
* @param string $path
* @param object $asn1
* @access private
*/
function _mapOutDNs(&$root, $path, $asn1)
{
$dns = &$this->_subArray($root, $path);
if (is_array($dns)) {
$size = count($dns);
for ($i = 0; $i < $size; $i++) {
for ($j = 0; $j < count($dns[$i]); $j++) {
$type = $dns[$i][$j]['type'];
$value = &$dns[$i][$j]['value'];
if (is_object($value) && $value instanceof
Element) {
continue;
}
$map = $this->_getMapping($type);
if (!is_bool($map)) {
$value = new Element($asn1->encodeDER($value,
$map));
}
}
}
}
}
/**
* Associate an extension ID to an extension mapping
*
* @param string $extnId
* @access private
* @return mixed
*/
function _getMapping($extnId)
{
if (!is_string($extnId)) { // eg. if it's a
\phpseclib\File\ASN1\Element object
return true;
}
switch ($extnId) {
case 'id-ce-keyUsage':
return $this->KeyUsage;
case 'id-ce-basicConstraints':
return $this->BasicConstraints;
case 'id-ce-subjectKeyIdentifier':
return $this->KeyIdentifier;
case 'id-ce-cRLDistributionPoints':
return $this->CRLDistributionPoints;
case 'id-ce-authorityKeyIdentifier':
return $this->AuthorityKeyIdentifier;
case 'id-ce-certificatePolicies':
return $this->CertificatePolicies;
case 'id-ce-extKeyUsage':
return $this->ExtKeyUsageSyntax;
case 'id-pe-authorityInfoAccess':
return $this->AuthorityInfoAccessSyntax;
case 'id-pe-subjectInfoAccess':
return $this->SubjectInfoAccessSyntax;
case 'id-ce-subjectAltName':
return $this->SubjectAltName;
case 'id-ce-subjectDirectoryAttributes':
return $this->SubjectDirectoryAttributes;
case 'id-ce-privateKeyUsagePeriod':
return $this->PrivateKeyUsagePeriod;
case 'id-ce-issuerAltName':
return $this->IssuerAltName;
case 'id-ce-policyMappings':
return $this->PolicyMappings;
case 'id-ce-nameConstraints':
return $this->NameConstraints;
case 'netscape-cert-type':
return $this->netscape_cert_type;
case 'netscape-comment':
return $this->netscape_comment;
case 'netscape-ca-policy-url':
return $this->netscape_ca_policy_url;
// since id-qt-cps isn't a constructed type it will have
already been decoded as a string by the time it gets
// back around to asn1map() and we don't want it decoded
again.
//case 'id-qt-cps':
// return $this->CPSuri;
case 'id-qt-unotice':
return $this->UserNotice;
// the following OIDs are unsupported but we don't want
them to give notices when calling saveX509().
case 'id-pe-logotype': //
http://www.ietf.org/rfc/rfc3709.txt
case 'entrustVersInfo':
// http://support.microsoft.com/kb/287547
case '1.3.6.1.4.1.311.20.2': //
szOID_ENROLL_CERTTYPE_EXTENSION
case '1.3.6.1.4.1.311.21.1': //
szOID_CERTSRV_CA_VERSION
// "SET Secure Electronic Transaction Specification"
// http://www.maithean.com/docs/set_bk3.pdf
case '2.23.42.7.0': // id-set-hashedRootKey
// "Certificate Transparency"
// https://tools.ietf.org/html/rfc6962
case '1.3.6.1.4.1.11129.2.4.2':
// "Qualified Certificate statements"
// https://tools.ietf.org/html/rfc3739#section-3.2.6
case '1.3.6.1.5.5.7.1.3':
return true;
// CSR attributes
case 'pkcs-9-at-unstructuredName':
return $this->PKCS9String;
case 'pkcs-9-at-challengePassword':
return $this->DirectoryString;
case 'pkcs-9-at-extensionRequest':
return $this->Extensions;
// CRL extensions.
case 'id-ce-cRLNumber':
return $this->CRLNumber;
case 'id-ce-deltaCRLIndicator':
return $this->CRLNumber;
case 'id-ce-issuingDistributionPoint':
return $this->IssuingDistributionPoint;
case 'id-ce-freshestCRL':
return $this->CRLDistributionPoints;
case 'id-ce-cRLReasons':
return $this->CRLReason;
case 'id-ce-invalidityDate':
return $this->InvalidityDate;
case 'id-ce-certificateIssuer':
return $this->CertificateIssuer;
case 'id-ce-holdInstructionCode':
return $this->HoldInstructionCode;
case 'id-at-postalAddress':
return $this->PostalAddress;
}
return false;
}
/**
* Load an X.509 certificate as a certificate authority
*
* @param string $cert
* @access public
* @return bool
*/
function loadCA($cert)
{
$olddn = $this->dn;
$oldcert = $this->currentCert;
$oldsigsubj = $this->signatureSubject;
$oldkeyid = $this->currentKeyIdentifier;
$cert = $this->loadX509($cert);
if (!$cert) {
$this->dn = $olddn;
$this->currentCert = $oldcert;
$this->signatureSubject = $oldsigsubj;
$this->currentKeyIdentifier = $oldkeyid;
return false;
}
/* From RFC5280 "PKIX Certificate and CRL Profile":
If the keyUsage extension is present, then the subject public
key
MUST NOT be used to verify signatures on certificates or CRLs
unless
the corresponding keyCertSign or cRLSign bit is set. */
//$keyUsage = $this->getExtension('id-ce-keyUsage');
//if ($keyUsage && !in_array('keyCertSign',
$keyUsage)) {
// return false;
//}
/* From RFC5280 "PKIX Certificate and CRL Profile":
The cA boolean indicates whether the certified public key may be
used
to verify certificate signatures. If the cA boolean is not
asserted,
then the keyCertSign bit in the key usage extension MUST NOT be
asserted. If the basic constraints extension is not present in
a
version 3 certificate, or the extension is present but the cA
boolean
is not asserted, then the certified public key MUST NOT be used
to
verify certificate signatures. */
//$basicConstraints =
$this->getExtension('id-ce-basicConstraints');
//if (!$basicConstraints || !$basicConstraints['cA']) {
// return false;
//}
$this->CAs[] = $cert;
$this->dn = $olddn;
$this->currentCert = $oldcert;
$this->signatureSubject = $oldsigsubj;
return true;
}
/**
* Validate an X.509 certificate against a URL
*
* From RFC2818 "HTTP over TLS":
*
* Matching is performed using the matching rules specified by
* [RFC2459]. If more than one identity of a given type is present in
* the certificate (e.g., more than one dNSName name, a match in any
one
* of the set is considered acceptable.) Names may contain the wildcard
* character * which is considered to match any single domain name
* component or component fragment. E.g., *.a.com matches foo.a.com but
* not bar.foo.a.com. f*.com matches foo.com but not bar.com.
*
* @param string $url
* @access public
* @return bool
*/
function validateURL($url)
{
if (!is_array($this->currentCert) ||
!isset($this->currentCert['tbsCertificate'])) {
return false;
}
$components = parse_url($url);
if (!isset($components['host'])) {
return false;
}
if ($names =
$this->getExtension('id-ce-subjectAltName')) {
foreach ($names as $name) {
foreach ($name as $key => $value) {
$value = str_replace(array('.',
'*'), array('\.', '[^.]*'), $value);
switch ($key) {
case 'dNSName':
/* From RFC2818 "HTTP over TLS":
If a subjectAltName extension of type
dNSName is present, that MUST
be used as the identity. Otherwise, the
(most specific) Common Name
field in the Subject field of the
certificate MUST be used. Although
the use of the Common Name is existing
practice, it is deprecated and
Certification Authorities are encouraged to
use the dNSName instead. */
if (preg_match('#^' . $value .
'$#', $components['host'])) {
return true;
}
break;
case 'iPAddress':
/* From RFC2818 "HTTP over TLS":
In some cases, the URI is specified as an IP
address rather than a
hostname. In this case, the iPAddress
subjectAltName must be present
in the certificate and must exactly match
the IP in the URI. */
if (preg_match('#(?:\d{1-3}\.){4}#',
$components['host'] . '.') &&
preg_match('#^' . $value . '$#',
$components['host'])) {
return true;
}
}
}
}
return false;
}
if ($value = $this->getDNProp('id-at-commonName')) {
$value = str_replace(array('.', '*'),
array('\.', '[^.]*'), $value[0]);
return preg_match('#^' . $value . '$#',
$components['host']);
}
return false;
}
/**
* Validate a date
*
* If $date isn't defined it is assumed to be the current date.
*
* @param \DateTime|string $date optional
* @access public
*/
function validateDate($date = null)
{
if (!is_array($this->currentCert) ||
!isset($this->currentCert['tbsCertificate'])) {
return false;
}
if (!isset($date)) {
$date = new DateTime(null, new
DateTimeZone(@date_default_timezone_get()));
}
$notBefore =
$this->currentCert['tbsCertificate']['validity']['notBefore'];
$notBefore = isset($notBefore['generalTime']) ?
$notBefore['generalTime'] : $notBefore['utcTime'];
$notAfter =
$this->currentCert['tbsCertificate']['validity']['notAfter'];
$notAfter = isset($notAfter['generalTime']) ?
$notAfter['generalTime'] : $notAfter['utcTime'];
if (is_string($date)) {
$date = new DateTime($date, new
DateTimeZone(@date_default_timezone_get()));
}
$notBefore = new DateTime($notBefore, new
DateTimeZone(@date_default_timezone_get()));
$notAfter = new DateTime($notAfter, new
DateTimeZone(@date_default_timezone_get()));
switch (true) {
case $date < $notBefore:
case $date > $notAfter:
return false;
}
return true;
}
/**
* Fetches a URL
*
* @param string $url
* @access private
* @return bool|string
*/
static function _fetchURL($url)
{
if (self::$disable_url_fetch) {
return false;
}
$parts = parse_url($url);
$data = '';
switch ($parts['scheme']) {
case 'http':
$fsock = @fsockopen($parts['host'],
isset($parts['port']) ? $parts['port'] : 80);
if (!$fsock) {
return false;
}
fputs($fsock, "GET $parts[path] HTTP/1.0\r\n");
fputs($fsock, "Host: $parts[host]\r\n\r\n");
$line = fgets($fsock, 1024);
if (strlen($line) < 3) {
return false;
}
preg_match('#HTTP/1.\d (\d{3})#', $line, $temp);
if ($temp[1] != '200') {
return false;
}
// skip the rest of the headers in the http response
while (!feof($fsock) && fgets($fsock, 1024) !=
"\r\n") {
}
while (!feof($fsock)) {
$temp = fread($fsock, 1024);
if ($temp === false) {
return false;
}
$data.= $temp;
}
break;
//case 'ftp':
//case 'ldap':
//default:
}
return $data;
}
/**
* Validates an intermediate cert as identified via authority info
access extension
*
* See https://tools.ietf.org/html/rfc4325 for more info
*
* @param bool $caonly
* @param int $count
* @access private
* @return bool
*/
function _testForIntermediate($caonly, $count)
{
$opts =
$this->getExtension('id-pe-authorityInfoAccess');
if (!is_array($opts)) {
return false;
}
foreach ($opts as $opt) {
if ($opt['accessMethod'] ==
'id-ad-caIssuers') {
// accessLocation is a GeneralName. GeneralName fields
support stuff like email addresses, IP addresses, LDAP,
// etc, but we're only supporting URI's.
URI's and LDAP are the only thing https://tools.ietf.org/html/rfc4325
// discusses
if
(isset($opt['accessLocation']['uniformResourceIdentifier']))
{
$url =
$opt['accessLocation']['uniformResourceIdentifier'];
break;
}
}
}
if (!isset($url)) {
return false;
}
$cert = static::_fetchURL($url);
if (!is_string($cert)) {
return false;
}
$parent = new static();
$parent->CAs = $this->CAs;
/*
"Conforming applications that support HTTP or FTP for
accessing
certificates MUST be able to accept .cer files and SHOULD be able
to accept .p7c files." --
https://tools.ietf.org/html/rfc4325
A .p7c file is 'a "certs-only" CMS message as
specified in RFC 2797"
These are currently unsupported
*/
if (!is_array($parent->loadX509($cert))) {
return false;
}
if (!$parent->_validateSignatureCountable($caonly, ++$count)) {
return false;
}
$this->CAs[] = $parent->currentCert;
//$this->loadCA($cert);
return true;
}
/**
* Validate a signature
*
* Works on X.509 certs, CSR's and CRL's.
* Returns true if the signature is verified, false if it is not
correct or null on error
*
* By default returns false for self-signed certs. Call
validateSignature(false) to make this support
* self-signed.
*
* The behavior of this function is inspired by {@link
http://php.net/openssl-verify openssl_verify}.
*
* @param bool $caonly optional
* @access public
* @return mixed
*/
function validateSignature($caonly = true)
{
return $this->_validateSignatureCountable($caonly, 0);
}
/**
* Validate a signature
*
* Performs said validation whilst keeping track of how many times
validation method is called
*
* @param bool $caonly
* @param int $count
* @access private
* @return mixed
*/
function _validateSignatureCountable($caonly, $count)
{
if (!is_array($this->currentCert) ||
!isset($this->signatureSubject)) {
return null;
}
if ($count == self::$recur_limit) {
return false;
}
/* TODO:
"emailAddress attribute values are not case-sensitive
(e.g., "subscriber@example.com" is the same as
"SUBSCRIBER@EXAMPLE.COM")."
-- http://tools.ietf.org/html/rfc5280#section-4.1.2.6
implement pathLenConstraint in the id-ce-basicConstraints
extension */
switch (true) {
case isset($this->currentCert['tbsCertificate']):
// self-signed cert
switch (true) {
case !defined('FILE_X509_IGNORE_TYPE')
&&
$this->currentCert['tbsCertificate']['issuer'] ===
$this->currentCert['tbsCertificate']['subject']:
case defined('FILE_X509_IGNORE_TYPE')
&& $this->getIssuerDN(self::DN_STRING) ===
$this->getDN(self::DN_STRING):
$authorityKey =
$this->getExtension('id-ce-authorityKeyIdentifier');
$subjectKeyID =
$this->getExtension('id-ce-subjectKeyIdentifier');
switch (true) {
case !is_array($authorityKey):
case !$subjectKeyID:
case
isset($authorityKey['keyIdentifier']) &&
$authorityKey['keyIdentifier'] === $subjectKeyID:
$signingCert = $this->currentCert; //
working cert
}
}
if (!empty($this->CAs)) {
for ($i = 0; $i < count($this->CAs); $i++) {
// even if the cert is a self-signed one we still
want to see if it's a CA;
// if not, we'll conditionally return an error
$ca = $this->CAs[$i];
switch (true) {
case
!defined('FILE_X509_IGNORE_TYPE') &&
$this->currentCert['tbsCertificate']['issuer'] ===
$ca['tbsCertificate']['subject']:
case defined('FILE_X509_IGNORE_TYPE')
&& $this->getDN(self::DN_STRING,
$this->currentCert['tbsCertificate']['issuer']) ===
$this->getDN(self::DN_STRING,
$ca['tbsCertificate']['subject']):
$authorityKey =
$this->getExtension('id-ce-authorityKeyIdentifier');
$subjectKeyID =
$this->getExtension('id-ce-subjectKeyIdentifier', $ca);
switch (true) {
case !is_array($authorityKey):
case !$subjectKeyID:
case
isset($authorityKey['keyIdentifier']) &&
$authorityKey['keyIdentifier'] === $subjectKeyID:
if (is_array($authorityKey)
&& isset($authorityKey['authorityCertSerialNumber'])
&&
!$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber']))
{
break 2; // serial mismatch -
check other ca
}
$signingCert = $ca; // working cert
break 3;
}
}
}
if (count($this->CAs) == $i && $caonly) {
return $this->_testForIntermediate($caonly,
$count) && $this->validateSignature($caonly);
}
} elseif (!isset($signingCert) || $caonly) {
return $this->_testForIntermediate($caonly, $count)
&& $this->validateSignature($caonly);
}
return $this->_validateSignature(
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'],
substr(base64_decode($this->currentCert['signature']), 1),
$this->signatureSubject
);
case
isset($this->currentCert['certificationRequestInfo']):
return $this->_validateSignature(
$this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'],
$this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'],
substr(base64_decode($this->currentCert['signature']), 1),
$this->signatureSubject
);
case
isset($this->currentCert['publicKeyAndChallenge']):
return $this->_validateSignature(
$this->currentCert['publicKeyAndChallenge']['spki']['algorithm']['algorithm'],
$this->currentCert['publicKeyAndChallenge']['spki']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'],
substr(base64_decode($this->currentCert['signature']), 1),
$this->signatureSubject
);
case isset($this->currentCert['tbsCertList']):
if (!empty($this->CAs)) {
for ($i = 0; $i < count($this->CAs); $i++) {
$ca = $this->CAs[$i];
switch (true) {
case
!defined('FILE_X509_IGNORE_TYPE') &&
$this->currentCert['tbsCertList']['issuer'] ===
$ca['tbsCertificate']['subject']:
case defined('FILE_X509_IGNORE_TYPE')
&& $this->getDN(self::DN_STRING,
$this->currentCert['tbsCertList']['issuer']) ===
$this->getDN(self::DN_STRING,
$ca['tbsCertificate']['subject']):
$authorityKey =
$this->getExtension('id-ce-authorityKeyIdentifier');
$subjectKeyID =
$this->getExtension('id-ce-subjectKeyIdentifier', $ca);
switch (true) {
case !is_array($authorityKey):
case !$subjectKeyID:
case
isset($authorityKey['keyIdentifier']) &&
$authorityKey['keyIdentifier'] === $subjectKeyID:
if (is_array($authorityKey)
&& isset($authorityKey['authorityCertSerialNumber'])
&&
!$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber']))
{
break 2; // serial mismatch -
check other ca
}
$signingCert = $ca; // working cert
break 3;
}
}
}
}
if (!isset($signingCert)) {
return false;
}
return $this->_validateSignature(
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'],
substr(base64_decode($this->currentCert['signature']), 1),
$this->signatureSubject
);
default:
return false;
}
}
/**
* Validates a signature
*
* Returns true if the signature is verified, false if it is not
correct or null on error
*
* @param string $publicKeyAlgorithm
* @param string $publicKey
* @param string $signatureAlgorithm
* @param string $signature
* @param string $signatureSubject
* @access private
* @return int
*/
function _validateSignature($publicKeyAlgorithm, $publicKey,
$signatureAlgorithm, $signature, $signatureSubject)
{
switch ($publicKeyAlgorithm) {
case 'rsaEncryption':
$rsa = new RSA();
$rsa->loadKey($publicKey);
switch ($signatureAlgorithm) {
case 'md2WithRSAEncryption':
case 'md5WithRSAEncryption':
case 'sha1WithRSAEncryption':
case 'sha224WithRSAEncryption':
case 'sha256WithRSAEncryption':
case 'sha384WithRSAEncryption':
case 'sha512WithRSAEncryption':
$rsa->setHash(preg_replace('#WithRSAEncryption$#',
'', $signatureAlgorithm));
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
if (!@$rsa->verify($signatureSubject,
$signature)) {
return false;
}
break;
default:
return null;
}
break;
default:
return null;
}
return true;
}
/**
* Sets the recursion limit
*
* When validating a signature it may be necessary to download
intermediate certs from URI's.
* An intermediate cert that linked to itself would result in an
infinite loop so to prevent
* that we set a recursion limit. A negative number means that there is
no recursion limit.
*
* @param int $count
* @access public
*/
static function setRecurLimit($count)
{
self::$recur_limit = $count;
}
/**
* Prevents URIs from being automatically retrieved
*
* @access public
*/
static function disableURLFetch()
{
self::$disable_url_fetch = true;
}
/**
* Allows URIs to be automatically retrieved
*
* @access public
*/
static function enableURLFetch()
{
self::$disable_url_fetch = false;
}
/**
* Reformat public keys
*
* Reformats a public key to a format supported by phpseclib (if
applicable)
*
* @param string $algorithm
* @param string $key
* @access private
* @return string
*/
function _reformatKey($algorithm, $key)
{
switch ($algorithm) {
case 'rsaEncryption':
return
"-----BEGIN RSA PUBLIC KEY-----\r\n" .
// subjectPublicKey is stored as a bit string in X.509
certs. the first byte of a bit string represents how many bits
// in the last byte should be ignored. the following
only supports non-zero stuff but as none of the X.509 certs Firefox
// uses as a cert authority actually use a non-zero bit
I think it's safe to assume that none do.
chunk_split(base64_encode(substr(base64_decode($key),
1)), 64) .
'-----END RSA PUBLIC KEY-----';
default:
return $key;
}
}
/**
* Decodes an IP address
*
* Takes in a base64 encoded "blob" and returns a human
readable IP address
*
* @param string $ip
* @access private
* @return string
*/
function _decodeIP($ip)
{
return inet_ntop(base64_decode($ip));
}
/**
* Decodes an IP address in a name constraints extension
*
* Takes in a base64 encoded "blob" and returns a human
readable IP address / mask
*
* @param string $ip
* @access private
* @return array
*/
function _decodeNameConstraintIP($ip)
{
$ip = base64_decode($ip);
$size = strlen($ip) >> 1;
$mask = substr($ip, $size);
$ip = substr($ip, 0, $size);
return array(inet_ntop($ip), inet_ntop($mask));
}
/**
* Encodes an IP address
*
* Takes a human readable IP address into a base64-encoded
"blob"
*
* @param string|array $ip
* @access private
* @return string
*/
function _encodeIP($ip)
{
return is_string($ip) ?
base64_encode(inet_pton($ip)) :
base64_encode(inet_pton($ip[0]) . inet_pton($ip[1]));
}
/**
* "Normalizes" a Distinguished Name property
*
* @param string $propName
* @access private
* @return mixed
*/
function _translateDNProp($propName)
{
switch (strtolower($propName)) {
case 'id-at-countryname':
case 'countryname':
case 'c':
return 'id-at-countryName';
case 'id-at-organizationname':
case 'organizationname':
case 'o':
return 'id-at-organizationName';
case 'id-at-dnqualifier':
case 'dnqualifier':
return 'id-at-dnQualifier';
case 'id-at-commonname':
case 'commonname':
case 'cn':
return 'id-at-commonName';
case 'id-at-stateorprovincename':
case 'stateorprovincename':
case 'state':
case 'province':
case 'provincename':
case 'st':
return 'id-at-stateOrProvinceName';
case 'id-at-localityname':
case 'localityname':
case 'l':
return 'id-at-localityName';
case 'id-emailaddress':
case 'emailaddress':
return 'pkcs-9-at-emailAddress';
case 'id-at-serialnumber':
case 'serialnumber':
return 'id-at-serialNumber';
case 'id-at-postalcode':
case 'postalcode':
return 'id-at-postalCode';
case 'id-at-streetaddress':
case 'streetaddress':
return 'id-at-streetAddress';
case 'id-at-name':
case 'name':
return 'id-at-name';
case 'id-at-givenname':
case 'givenname':
return 'id-at-givenName';
case 'id-at-surname':
case 'surname':
case 'sn':
return 'id-at-surname';
case 'id-at-initials':
case 'initials':
return 'id-at-initials';
case 'id-at-generationqualifier':
case 'generationqualifier':
return 'id-at-generationQualifier';
case 'id-at-organizationalunitname':
case 'organizationalunitname':
case 'ou':
return 'id-at-organizationalUnitName';
case 'id-at-pseudonym':
case 'pseudonym':
return 'id-at-pseudonym';
case 'id-at-title':
case 'title':
return 'id-at-title';
case 'id-at-description':
case 'description':
return 'id-at-description';
case 'id-at-role':
case 'role':
return 'id-at-role';
case 'id-at-uniqueidentifier':
case 'uniqueidentifier':
case 'x500uniqueidentifier':
return 'id-at-uniqueIdentifier';
case 'postaladdress':
case 'id-at-postaladdress':
return 'id-at-postalAddress';
default:
return false;
}
}
/**
* Set a Distinguished Name property
*
* @param string $propName
* @param mixed $propValue
* @param string $type optional
* @access public
* @return bool
*/
function setDNProp($propName, $propValue, $type =
'utf8String')
{
if (empty($this->dn)) {
$this->dn = array('rdnSequence' => array());
}
if (($propName = $this->_translateDNProp($propName)) === false)
{
return false;
}
foreach ((array) $propValue as $v) {
if (!is_array($v) && isset($type)) {
$v = array($type => $v);
}
$this->dn['rdnSequence'][] = array(
array(
'type' => $propName,
'value'=> $v
)
);
}
return true;
}
/**
* Remove Distinguished Name properties
*
* @param string $propName
* @access public
*/
function removeDNProp($propName)
{
if (empty($this->dn)) {
return;
}
if (($propName = $this->_translateDNProp($propName)) === false)
{
return;
}
$dn = &$this->dn['rdnSequence'];
$size = count($dn);
for ($i = 0; $i < $size; $i++) {
if ($dn[$i][0]['type'] == $propName) {
unset($dn[$i]);
}
}
$dn = array_values($dn);
// fix for https://bugs.php.net/75433 affecting PHP 7.2
if (!isset($dn[0])) {
$dn = array_splice($dn, 0, 0);
}
}
/**
* Get Distinguished Name properties
*
* @param string $propName
* @param array $dn optional
* @param bool $withType optional
* @return mixed
* @access public
*/
function getDNProp($propName, $dn = null, $withType = false)
{
if (!isset($dn)) {
$dn = $this->dn;
}
if (empty($dn)) {
return false;
}
if (($propName = $this->_translateDNProp($propName)) === false)
{
return false;
}
$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$filters['value'] = array('type' =>
ASN1::TYPE_UTF8_STRING);
$asn1->loadFilters($filters);
$this->_mapOutDNs($dn, 'rdnSequence', $asn1);
$dn = $dn['rdnSequence'];
$result = array();
for ($i = 0; $i < count($dn); $i++) {
if ($dn[$i][0]['type'] == $propName) {
$v = $dn[$i][0]['value'];
if (!$withType) {
if (is_array($v)) {
foreach ($v as $type => $s) {
$type = array_search($type, $asn1->ANYmap,
true);
if ($type !== false &&
isset($asn1->stringTypeSize[$type])) {
$s = $asn1->convert($s, $type);
if ($s !== false) {
$v = $s;
break;
}
}
}
if (is_array($v)) {
$v = array_pop($v); // Always strip data type.
}
} elseif (is_object($v) && $v instanceof
Element) {
$map = $this->_getMapping($propName);
if (!is_bool($map)) {
$decoded = $asn1->decodeBER($v);
$v = $asn1->asn1map($decoded[0], $map);
}
}
}
$result[] = $v;
}
}
return $result;
}
/**
* Set a Distinguished Name
*
* @param mixed $dn
* @param bool $merge optional
* @param string $type optional
* @access public
* @return bool
*/
function setDN($dn, $merge = false, $type = 'utf8String')
{
if (!$merge) {
$this->dn = null;
}
if (is_array($dn)) {
if (isset($dn['rdnSequence'])) {
$this->dn = $dn; // No merge here.
return true;
}
// handles stuff generated by openssl_x509_parse()
foreach ($dn as $prop => $value) {
if (!$this->setDNProp($prop, $value, $type)) {
return false;
}
}
return true;
}
// handles everything else
$results = preg_split('#((?:^|,
*|/)(?:C=|O=|OU=|CN=|L=|ST=|SN=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=|title=|description=|role=|x500UniqueIdentifier=|postalAddress=))#',
$dn, -1, PREG_SPLIT_DELIM_CAPTURE);
for ($i = 1; $i < count($results); $i+=2) {
$prop = trim($results[$i], ', =/');
$value = $results[$i + 1];
if (!$this->setDNProp($prop, $value, $type)) {
return false;
}
}
return true;
}
/**
* Get the Distinguished Name for a certificates subject
*
* @param mixed $format optional
* @param array $dn optional
* @access public
* @return bool
*/
function getDN($format = self::DN_ARRAY, $dn = null)
{
if (!isset($dn)) {
$dn = isset($this->currentCert['tbsCertList']) ?
$this->currentCert['tbsCertList']['issuer'] :
$this->dn;
}
switch ((int) $format) {
case self::DN_ARRAY:
return $dn;
case self::DN_ASN1:
$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$filters['rdnSequence']['value'] =
array('type' => ASN1::TYPE_UTF8_STRING);
$asn1->loadFilters($filters);
$this->_mapOutDNs($dn, 'rdnSequence', $asn1);
return $asn1->encodeDER($dn, $this->Name);
case self::DN_CANON:
// No SEQUENCE around RDNs and all string values
normalized as
// trimmed lowercase UTF-8 with all spacing as one blank.
// constructed RDNs will not be canonicalized
$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$filters['value'] = array('type' =>
ASN1::TYPE_UTF8_STRING);
$asn1->loadFilters($filters);
$result = '';
$this->_mapOutDNs($dn, 'rdnSequence', $asn1);
foreach ($dn['rdnSequence'] as $rdn) {
foreach ($rdn as $i => $attr) {
$attr = &$rdn[$i];
if (is_array($attr['value'])) {
foreach ($attr['value'] as $type
=> $v) {
$type = array_search($type,
$asn1->ANYmap, true);
if ($type !== false &&
isset($asn1->stringTypeSize[$type])) {
$v = $asn1->convert($v, $type);
if ($v !== false) {
$v =
preg_replace('/\s+/', ' ', $v);
$attr['value'] =
strtolower(trim($v));
break;
}
}
}
}
}
$result .= $asn1->encodeDER($rdn,
$this->RelativeDistinguishedName);
}
return $result;
case self::DN_HASH:
$dn = $this->getDN(self::DN_CANON, $dn);
$hash = new Hash('sha1');
$hash = $hash->hash($dn);
extract(unpack('Vhash', $hash));
return strtolower(bin2hex(pack('N', $hash)));
}
// Default is to return a string.
$start = true;
$output = '';
$result = array();
$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$filters['rdnSequence']['value'] =
array('type' => ASN1::TYPE_UTF8_STRING);
$asn1->loadFilters($filters);
$this->_mapOutDNs($dn, 'rdnSequence', $asn1);
foreach ($dn['rdnSequence'] as $field) {
$prop = $field[0]['type'];
$value = $field[0]['value'];
$delim = ', ';
switch ($prop) {
case 'id-at-countryName':
$desc = 'C';
break;
case 'id-at-stateOrProvinceName':
$desc = 'ST';
break;
case 'id-at-organizationName':
$desc = 'O';
break;
case 'id-at-organizationalUnitName':
$desc = 'OU';
break;
case 'id-at-commonName':
$desc = 'CN';
break;
case 'id-at-localityName':
$desc = 'L';
break;
case 'id-at-surname':
$desc = 'SN';
break;
case 'id-at-uniqueIdentifier':
$delim = '/';
$desc = 'x500UniqueIdentifier';
break;
case 'id-at-postalAddress':
$delim = '/';
$desc = 'postalAddress';
break;
default:
$delim = '/';
$desc = preg_replace('#.+-([^-]+)$#',
'$1', $prop);
}
if (!$start) {
$output.= $delim;
}
if (is_array($value)) {
foreach ($value as $type => $v) {
$type = array_search($type, $asn1->ANYmap, true);
if ($type !== false &&
isset($asn1->stringTypeSize[$type])) {
$v = $asn1->convert($v, $type);
if ($v !== false) {
$value = $v;
break;
}
}
}
if (is_array($value)) {
$value = array_pop($value); // Always strip data type.
}
} elseif (is_object($value) && $value instanceof
Element) {
$callback = function ($x) {
return "\x" . bin2hex($x[0]);
};
$value =
strtoupper(preg_replace_callback('#[^\x20-\x7E]#', $callback,
$value->element));
}
$output.= $desc . '=' . $value;
$result[$desc] = isset($result[$desc]) ?
array_merge((array) $result[$desc], array($value)) :
$value;
$start = false;
}
return $format == self::DN_OPENSSL ? $result : $output;
}
/**
* Get the Distinguished Name for a certificate/crl issuer
*
* @param int $format optional
* @access public
* @return mixed
*/
function getIssuerDN($format = self::DN_ARRAY)
{
switch (true) {
case !isset($this->currentCert) ||
!is_array($this->currentCert):
break;
case isset($this->currentCert['tbsCertificate']):
return $this->getDN($format,
$this->currentCert['tbsCertificate']['issuer']);
case isset($this->currentCert['tbsCertList']):
return $this->getDN($format,
$this->currentCert['tbsCertList']['issuer']);
}
return false;
}
/**
* Get the Distinguished Name for a certificate/csr subject
* Alias of getDN()
*
* @param int $format optional
* @access public
* @return mixed
*/
function getSubjectDN($format = self::DN_ARRAY)
{
switch (true) {
case !empty($this->dn):
return $this->getDN($format);
case !isset($this->currentCert) ||
!is_array($this->currentCert):
break;
case isset($this->currentCert['tbsCertificate']):
return $this->getDN($format,
$this->currentCert['tbsCertificate']['subject']);
case
isset($this->currentCert['certificationRequestInfo']):
return $this->getDN($format,
$this->currentCert['certificationRequestInfo']['subject']);
}
return false;
}
/**
* Get an individual Distinguished Name property for a certificate/crl
issuer
*
* @param string $propName
* @param bool $withType optional
* @access public
* @return mixed
*/
function getIssuerDNProp($propName, $withType = false)
{
switch (true) {
case !isset($this->currentCert) ||
!is_array($this->currentCert):
break;
case isset($this->currentCert['tbsCertificate']):
return $this->getDNProp($propName,
$this->currentCert['tbsCertificate']['issuer'],
$withType);
case isset($this->currentCert['tbsCertList']):
return $this->getDNProp($propName,
$this->currentCert['tbsCertList']['issuer'],
$withType);
}
return false;
}
/**
* Get an individual Distinguished Name property for a certificate/csr
subject
*
* @param string $propName
* @param bool $withType optional
* @access public
* @return mixed
*/
function getSubjectDNProp($propName, $withType = false)
{
switch (true) {
case !empty($this->dn):
return $this->getDNProp($propName, null, $withType);
case !isset($this->currentCert) ||
!is_array($this->currentCert):
break;
case isset($this->currentCert['tbsCertificate']):
return $this->getDNProp($propName,
$this->currentCert['tbsCertificate']['subject'],
$withType);
case
isset($this->currentCert['certificationRequestInfo']):
return $this->getDNProp($propName,
$this->currentCert['certificationRequestInfo']['subject'],
$withType);
}
return false;
}
/**
* Get the certificate chain for the current cert
*
* @access public
* @return mixed
*/
function getChain()
{
$chain = array($this->currentCert);
if (!is_array($this->currentCert) ||
!isset($this->currentCert['tbsCertificate'])) {
return false;
}
if (empty($this->CAs)) {
return $chain;
}
while (true) {
$currentCert = $chain[count($chain) - 1];
for ($i = 0; $i < count($this->CAs); $i++) {
$ca = $this->CAs[$i];
if
($currentCert['tbsCertificate']['issuer'] ===
$ca['tbsCertificate']['subject']) {
$authorityKey =
$this->getExtension('id-ce-authorityKeyIdentifier',
$currentCert);
$subjectKeyID =
$this->getExtension('id-ce-subjectKeyIdentifier', $ca);
switch (true) {
case !is_array($authorityKey):
case is_array($authorityKey) &&
isset($authorityKey['keyIdentifier']) &&
$authorityKey['keyIdentifier'] === $subjectKeyID:
if ($currentCert === $ca) {
break 3;
}
$chain[] = $ca;
break 2;
}
}
}
if ($i == count($this->CAs)) {
break;
}
}
foreach ($chain as $key => $value) {
$chain[$key] = new X509();
$chain[$key]->loadX509($value);
}
return $chain;
}
/**
* Set public key
*
* Key needs to be a \phpseclib\Crypt\RSA object
*
* @param object $key
* @access public
* @return bool
*/
function setPublicKey($key)
{
$key->setPublicKey();
$this->publicKey = $key;
}
/**
* Set private key
*
* Key needs to be a \phpseclib\Crypt\RSA object
*
* @param object $key
* @access public
*/
function setPrivateKey($key)
{
$this->privateKey = $key;
}
/**
* Set challenge
*
* Used for SPKAC CSR's
*
* @param string $challenge
* @access public
*/
function setChallenge($challenge)
{
$this->challenge = $challenge;
}
/**
* Gets the public key
*
* Returns a \phpseclib\Crypt\RSA object or a false.
*
* @access public
* @return mixed
*/
function getPublicKey()
{
if (isset($this->publicKey)) {
return $this->publicKey;
}
if (isset($this->currentCert) &&
is_array($this->currentCert)) {
foreach (array('tbsCertificate/subjectPublicKeyInfo',
'certificationRequestInfo/subjectPKInfo') as $path) {
$keyinfo = $this->_subArray($this->currentCert,
$path);
if (!empty($keyinfo)) {
break;
}
}
}
if (empty($keyinfo)) {
return false;
}
$key = $keyinfo['subjectPublicKey'];
switch ($keyinfo['algorithm']['algorithm']) {
case 'rsaEncryption':
$publicKey = new RSA();
$publicKey->loadKey($key);
$publicKey->setPublicKey();
break;
default:
return false;
}
return $publicKey;
}
/**
* Load a Certificate Signing Request
*
* @param string|array $csr
* @param int $mode
* @access public
* @return mixed
*/
function loadCSR($csr, $mode = self::FORMAT_AUTO_DETECT)
{
if (is_array($csr) &&
isset($csr['certificationRequestInfo'])) {
unset($this->currentCert);
unset($this->currentKeyIdentifier);
unset($this->signatureSubject);
$this->dn =
$csr['certificationRequestInfo']['subject'];
if (!isset($this->dn)) {
return false;
}
$this->currentCert = $csr;
return $csr;
}
// see http://tools.ietf.org/html/rfc2986
$asn1 = new ASN1();
if ($mode != self::FORMAT_DER) {
$newcsr = $this->_extractBER($csr);
if ($mode == self::FORMAT_PEM && $csr == $newcsr) {
return false;
}
$csr = $newcsr;
}
$orig = $csr;
if ($csr === false) {
$this->currentCert = false;
return false;
}
$asn1->loadOIDs($this->oids);
$decoded = $asn1->decodeBER($csr);
if (empty($decoded)) {
$this->currentCert = false;
return false;
}
$csr = $asn1->asn1map($decoded[0],
$this->CertificationRequest);
if (!isset($csr) || $csr === false) {
$this->currentCert = false;
return false;
}
$this->_mapInAttributes($csr,
'certificationRequestInfo/attributes', $asn1);
$this->_mapInDNs($csr,
'certificationRequestInfo/subject/rdnSequence', $asn1);
$this->dn =
$csr['certificationRequestInfo']['subject'];
$this->signatureSubject = substr($orig,
$decoded[0]['content'][0]['start'],
$decoded[0]['content'][0]['length']);
$algorithm =
&$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'];
$key =
&$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'];
$key = $this->_reformatKey($algorithm, $key);
switch ($algorithm) {
case 'rsaEncryption':
$this->publicKey = new RSA();
$this->publicKey->loadKey($key);
$this->publicKey->setPublicKey();
break;
default:
$this->publicKey = null;
}
$this->currentKeyIdentifier = null;
$this->currentCert = $csr;
return $csr;
}
/**
* Save CSR request
*
* @param array $csr
* @param int $format optional
* @access public
* @return string
*/
function saveCSR($csr, $format = self::FORMAT_PEM)
{
if (!is_array($csr) ||
!isset($csr['certificationRequestInfo'])) {
return false;
}
switch (true) {
case !($algorithm = $this->_subArray($csr,
'certificationRequestInfo/subjectPKInfo/algorithm/algorithm')):
case
is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']):
break;
default:
switch ($algorithm) {
case 'rsaEncryption':
$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']
= base64_encode("\0" .
base64_decode(preg_replace('#-.+-|[\r\n]#', '',
$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'])));
$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['parameters']
= null;
$csr['signatureAlgorithm']['parameters'] = null;
$csr['certificationRequestInfo']['signature']['parameters']
= null;
}
}
$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$filters['certificationRequestInfo']['subject']['rdnSequence']['value']
= array('type' => ASN1::TYPE_UTF8_STRING);
$asn1->loadFilters($filters);
$this->_mapOutDNs($csr,
'certificationRequestInfo/subject/rdnSequence', $asn1);
$this->_mapOutAttributes($csr,
'certificationRequestInfo/attributes', $asn1);
$csr = $asn1->encodeDER($csr, $this->CertificationRequest);
switch ($format) {
case self::FORMAT_DER:
return $csr;
// case self::FORMAT_PEM:
default:
return "-----BEGIN CERTIFICATE REQUEST-----\r\n"
. chunk_split(base64_encode($csr), 64) . '-----END CERTIFICATE
REQUEST-----';
}
}
/**
* Load a SPKAC CSR
*
* SPKAC's are produced by the HTML5 keygen element:
*
* https://developer.mozilla.org/en-US/docs/HTML/Element/keygen
*
* @param string|array $spkac
* @access public
* @return mixed
*/
function loadSPKAC($spkac)
{
if (is_array($spkac) &&
isset($spkac['publicKeyAndChallenge'])) {
unset($this->currentCert);
unset($this->currentKeyIdentifier);
unset($this->signatureSubject);
$this->currentCert = $spkac;
return $spkac;
}
// see
http://www.w3.org/html/wg/drafts/html/master/forms.html#signedpublickeyandchallenge
$asn1 = new ASN1();
// OpenSSL produces SPKAC's that are preceded by the string
SPKAC=
$temp = preg_replace('#(?:SPKAC=)|[ \r\n\\\]#',
'', $spkac);
$temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ?
base64_decode($temp) : false;
if ($temp != false) {
$spkac = $temp;
}
$orig = $spkac;
if ($spkac === false) {
$this->currentCert = false;
return false;
}
$asn1->loadOIDs($this->oids);
$decoded = $asn1->decodeBER($spkac);
if (empty($decoded)) {
$this->currentCert = false;
return false;
}
$spkac = $asn1->asn1map($decoded[0],
$this->SignedPublicKeyAndChallenge);
if (!isset($spkac) || $spkac === false) {
$this->currentCert = false;
return false;
}
$this->signatureSubject = substr($orig,
$decoded[0]['content'][0]['start'],
$decoded[0]['content'][0]['length']);
$algorithm =
&$spkac['publicKeyAndChallenge']['spki']['algorithm']['algorithm'];
$key =
&$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'];
$key = $this->_reformatKey($algorithm, $key);
switch ($algorithm) {
case 'rsaEncryption':
$this->publicKey = new RSA();
$this->publicKey->loadKey($key);
$this->publicKey->setPublicKey();
break;
default:
$this->publicKey = null;
}
$this->currentKeyIdentifier = null;
$this->currentCert = $spkac;
return $spkac;
}
/**
* Save a SPKAC CSR request
*
* @param string|array $spkac
* @param int $format optional
* @access public
* @return string
*/
function saveSPKAC($spkac, $format = self::FORMAT_PEM)
{
if (!is_array($spkac) ||
!isset($spkac['publicKeyAndChallenge'])) {
return false;
}
$algorithm = $this->_subArray($spkac,
'publicKeyAndChallenge/spki/algorithm/algorithm');
switch (true) {
case !$algorithm:
case
is_object($spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']):
break;
default:
switch ($algorithm) {
case 'rsaEncryption':
$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']
= base64_encode("\0" .
base64_decode(preg_replace('#-.+-|[\r\n]#', '',
$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'])));
}
}
$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$spkac = $asn1->encodeDER($spkac,
$this->SignedPublicKeyAndChallenge);
switch ($format) {
case self::FORMAT_DER:
return $spkac;
// case self::FORMAT_PEM:
default:
// OpenSSL's implementation of SPKAC requires the
SPKAC be preceded by SPKAC= and since there are pretty much
// no other SPKAC decoders phpseclib will use that same
format
return 'SPKAC=' . base64_encode($spkac);
}
}
/**
* Load a Certificate Revocation List
*
* @param string $crl
* @param int $mode
* @access public
* @return mixed
*/
function loadCRL($crl, $mode = self::FORMAT_AUTO_DETECT)
{
if (is_array($crl) && isset($crl['tbsCertList']))
{
$this->currentCert = $crl;
unset($this->signatureSubject);
return $crl;
}
$asn1 = new ASN1();
if ($mode != self::FORMAT_DER) {
$newcrl = $this->_extractBER($crl);
if ($mode == self::FORMAT_PEM && $crl == $newcrl) {
return false;
}
$crl = $newcrl;
}
$orig = $crl;
if ($crl === false) {
$this->currentCert = false;
return false;
}
$asn1->loadOIDs($this->oids);
$decoded = $asn1->decodeBER($crl);
if (empty($decoded)) {
$this->currentCert = false;
return false;
}
$crl = $asn1->asn1map($decoded[0], $this->CertificateList);
if (!isset($crl) || $crl === false) {
$this->currentCert = false;
return false;
}
$this->signatureSubject = substr($orig,
$decoded[0]['content'][0]['start'],
$decoded[0]['content'][0]['length']);
$this->_mapInDNs($crl,
'tbsCertList/issuer/rdnSequence', $asn1);
if ($this->_isSubArrayValid($crl,
'tbsCertList/crlExtensions')) {
$this->_mapInExtensions($crl,
'tbsCertList/crlExtensions', $asn1);
}
if ($this->_isSubArrayValid($crl,
'tbsCertList/revokedCertificates')) {
$rclist_ref = &$this->_subArrayUnchecked($crl,
'tbsCertList/revokedCertificates');
if ($rclist_ref) {
$rclist =
$crl['tbsCertList']['revokedCertificates'];
foreach ($rclist as $i => $extension) {
if ($this->_isSubArrayValid($rclist,
"$i/crlEntryExtensions", $asn1)) {
$this->_mapInExtensions($rclist_ref,
"$i/crlEntryExtensions", $asn1);
}
}
}
}
$this->currentKeyIdentifier = null;
$this->currentCert = $crl;
return $crl;
}
/**
* Save Certificate Revocation List.
*
* @param array $crl
* @param int $format optional
* @access public
* @return string
*/
function saveCRL($crl, $format = self::FORMAT_PEM)
{
if (!is_array($crl) || !isset($crl['tbsCertList'])) {
return false;
}
$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$filters['tbsCertList']['issuer']['rdnSequence']['value']
= array('type' => ASN1::TYPE_UTF8_STRING);
$filters['tbsCertList']['signature']['parameters']
= array('type' => ASN1::TYPE_UTF8_STRING);
$filters['signatureAlgorithm']['parameters']
= array('type' => ASN1::TYPE_UTF8_STRING);
if
(empty($crl['tbsCertList']['signature']['parameters']))
{
$filters['tbsCertList']['signature']['parameters']
= array('type' => ASN1::TYPE_NULL);
}
if
(empty($crl['signatureAlgorithm']['parameters'])) {
$filters['signatureAlgorithm']['parameters']
= array('type' => ASN1::TYPE_NULL);
}
$asn1->loadFilters($filters);
$this->_mapOutDNs($crl,
'tbsCertList/issuer/rdnSequence', $asn1);
$this->_mapOutExtensions($crl,
'tbsCertList/crlExtensions', $asn1);
$rclist = &$this->_subArray($crl,
'tbsCertList/revokedCertificates');
if (is_array($rclist)) {
foreach ($rclist as $i => $extension) {
$this->_mapOutExtensions($rclist,
"$i/crlEntryExtensions", $asn1);
}
}
$crl = $asn1->encodeDER($crl, $this->CertificateList);
switch ($format) {
case self::FORMAT_DER:
return $crl;
// case self::FORMAT_PEM:
default:
return "-----BEGIN X509 CRL-----\r\n" .
chunk_split(base64_encode($crl), 64) . '-----END X509 CRL-----';
}
}
/**
* Helper function to build a time field according to RFC 3280 section
* - 4.1.2.5 Validity
* - 5.1.2.4 This Update
* - 5.1.2.5 Next Update
* - 5.1.2.6 Revoked Certificates
* by choosing utcTime iff year of date given is before 2050 and
generalTime else.
*
* @param string $date in format date('D, d M Y H:i:s O')
* @access private
* @return array
*/
function _timeField($date)
{
if ($date instanceof Element) {
return $date;
}
$dateObj = new DateTime($date, new DateTimeZone('GMT'));
$year = $dateObj->format('Y'); // the same way
ASN1.php parses this
if ($year < 2050) {
return array('utcTime' => $date);
} else {
return array('generalTime' => $date);
}
}
/**
* Sign an X.509 certificate
*
* $issuer's private key needs to be loaded.
* $subject can be either an existing X.509 cert (if you want to resign
it),
* a CSR or something with the DN and public key explicitly set.
*
* @param \phpseclib\File\X509 $issuer
* @param \phpseclib\File\X509 $subject
* @param string $signatureAlgorithm optional
* @access public
* @return mixed
*/
function sign($issuer, $subject, $signatureAlgorithm =
'sha1WithRSAEncryption')
{
if (!is_object($issuer->privateKey) || empty($issuer->dn)) {
return false;
}
if (isset($subject->publicKey) && !($subjectPublicKey =
$subject->_formatSubjectPublicKey())) {
return false;
}
$currentCert = isset($this->currentCert) ? $this->currentCert
: null;
$signatureSubject = isset($this->signatureSubject) ?
$this->signatureSubject: null;
if (isset($subject->currentCert) &&
is_array($subject->currentCert) &&
isset($subject->currentCert['tbsCertificate'])) {
$this->currentCert = $subject->currentCert;
$this->currentCert['tbsCertificate']['signature']['algorithm']
= $signatureAlgorithm;
$this->currentCert['signatureAlgorithm']['algorithm']
= $signatureAlgorithm;
if (!empty($this->startDate)) {
$this->currentCert['tbsCertificate']['validity']['notBefore']
= $this->_timeField($this->startDate);
}
if (!empty($this->endDate)) {
$this->currentCert['tbsCertificate']['validity']['notAfter']
= $this->_timeField($this->endDate);
}
if (!empty($this->serialNumber)) {
$this->currentCert['tbsCertificate']['serialNumber']
= $this->serialNumber;
}
if (!empty($subject->dn)) {
$this->currentCert['tbsCertificate']['subject'] =
$subject->dn;
}
if (!empty($subject->publicKey)) {
$this->currentCert['tbsCertificate']['subjectPublicKeyInfo']
= $subjectPublicKey;
}
$this->removeExtension('id-ce-authorityKeyIdentifier');
if (isset($subject->domains)) {
$this->removeExtension('id-ce-subjectAltName');
}
} elseif (isset($subject->currentCert) &&
is_array($subject->currentCert) &&
isset($subject->currentCert['tbsCertList'])) {
return false;
} else {
if (!isset($subject->publicKey)) {
return false;
}
$startDate = new DateTime('now', new
DateTimeZone(@date_default_timezone_get()));
$startDate = !empty($this->startDate) ? $this->startDate
: $startDate->format('D, d M Y H:i:s O');
$endDate = new DateTime('+1 year', new
DateTimeZone(@date_default_timezone_get()));
$endDate = !empty($this->endDate) ? $this->endDate :
$endDate->format('D, d M Y H:i:s O');
/* "The serial number MUST be a positive integer"
"Conforming CAs MUST NOT use serialNumber values longer
than 20 octets."
-- https://tools.ietf.org/html/rfc5280#section-4.1.2.2
for the integer to be positive the leading bit needs to be 0
hence the
application of a bitmap
*/
$serialNumber = !empty($this->serialNumber) ?
$this->serialNumber :
new BigInteger(Random::string(20) & ("\x7F" .
str_repeat("\xFF", 19)), 256);
$this->currentCert = array(
'tbsCertificate' =>
array(
'version' => 'v3',
'serialNumber' => $serialNumber, //
$this->setSerialNumber()
'signature' =>
array('algorithm' => $signatureAlgorithm),
'issuer' => false, // this is going to
be overwritten later
'validity' => array(
'notBefore' =>
$this->_timeField($startDate), // $this->setStartDate()
'notAfter' =>
$this->_timeField($endDate) // $this->setEndDate()
),
'subject' => $subject->dn,
'subjectPublicKeyInfo' =>
$subjectPublicKey
),
'signatureAlgorithm' =>
array('algorithm' => $signatureAlgorithm),
'signature' => false // this is
going to be overwritten later
);
// Copy extensions from CSR.
$csrexts =
$subject->getAttribute('pkcs-9-at-extensionRequest', 0);
if (!empty($csrexts)) {
$this->currentCert['tbsCertificate']['extensions'] =
$csrexts;
}
}
$this->currentCert['tbsCertificate']['issuer'] =
$issuer->dn;
if (isset($issuer->currentKeyIdentifier)) {
$this->setExtension('id-ce-authorityKeyIdentifier', array(
//'authorityCertIssuer' => array(
// array(
// 'directoryName' =>
$issuer->dn
// )
//),
'keyIdentifier' =>
$issuer->currentKeyIdentifier
));
//$extensions =
&$this->currentCert['tbsCertificate']['extensions'];
//if (isset($issuer->serialNumber)) {
// $extensions[count($extensions) -
1]['authorityCertSerialNumber'] = $issuer->serialNumber;
//}
//unset($extensions);
}
if (isset($subject->currentKeyIdentifier)) {
$this->setExtension('id-ce-subjectKeyIdentifier',
$subject->currentKeyIdentifier);
}
$altName = array();
if (isset($subject->domains) &&
count($subject->domains)) {
$altName = array_map(array('\phpseclib\File\X509',
'_dnsName'), $subject->domains);
}
if (isset($subject->ipAddresses) &&
count($subject->ipAddresses)) {
// should an IP address appear as the CN if no domain name is
specified? idk
//$ips = count($subject->domains) ? $subject->ipAddresses
: array_slice($subject->ipAddresses, 1);
$ipAddresses = array();
foreach ($subject->ipAddresses as $ipAddress) {
$encoded = $subject->_ipAddress($ipAddress);
if ($encoded !== false) {
$ipAddresses[] = $encoded;
}
}
if (count($ipAddresses)) {
$altName = array_merge($altName, $ipAddresses);
}
}
if (!empty($altName)) {
$this->setExtension('id-ce-subjectAltName',
$altName);
}
if ($this->caFlag) {
$keyUsage = $this->getExtension('id-ce-keyUsage');
if (!$keyUsage) {
$keyUsage = array();
}
$this->setExtension(
'id-ce-keyUsage',
array_values(array_unique(array_merge($keyUsage,
array('cRLSign', 'keyCertSign'))))
);
$basicConstraints =
$this->getExtension('id-ce-basicConstraints');
if (!$basicConstraints) {
$basicConstraints = array();
}
$this->setExtension(
'id-ce-basicConstraints',
array_unique(array_merge(array('cA' => true),
$basicConstraints)),
true
);
if (!isset($subject->currentKeyIdentifier)) {
$this->setExtension('id-ce-subjectKeyIdentifier',
base64_encode($this->computeKeyIdentifier($this->currentCert)),
false, false);
}
}
// resync $this->signatureSubject
// save $tbsCertificate in case there are any
\phpseclib\File\ASN1\Element objects in it
$tbsCertificate =
$this->currentCert['tbsCertificate'];
$this->loadX509($this->saveX509($this->currentCert));
$result = $this->_sign($issuer->privateKey,
$signatureAlgorithm);
$result['tbsCertificate'] = $tbsCertificate;
$this->currentCert = $currentCert;
$this->signatureSubject = $signatureSubject;
return $result;
}
/**
* Sign a CSR
*
* @access public
* @return mixed
*/
function signCSR($signatureAlgorithm =
'sha1WithRSAEncryption')
{
if (!is_object($this->privateKey) || empty($this->dn)) {
return false;
}
$origPublicKey = $this->publicKey;
$class = get_class($this->privateKey);
$this->publicKey = new $class();
$this->publicKey->loadKey($this->privateKey->getPublicKey());
$this->publicKey->setPublicKey();
if (!($publicKey = $this->_formatSubjectPublicKey())) {
return false;
}
$this->publicKey = $origPublicKey;
$currentCert = isset($this->currentCert) ? $this->currentCert
: null;
$signatureSubject = isset($this->signatureSubject) ?
$this->signatureSubject: null;
if (isset($this->currentCert) &&
is_array($this->currentCert) &&
isset($this->currentCert['certificationRequestInfo'])) {
$this->currentCert['signatureAlgorithm']['algorithm']
= $signatureAlgorithm;
if (!empty($this->dn)) {
$this->currentCert['certificationRequestInfo']['subject']
= $this->dn;
}
$this->currentCert['certificationRequestInfo']['subjectPKInfo']
= $publicKey;
} else {
$this->currentCert = array(
'certificationRequestInfo' =>
array(
'version' => 'v1',
'subject' => $this->dn,
'subjectPKInfo' => $publicKey
),
'signatureAlgorithm' =>
array('algorithm' => $signatureAlgorithm),
'signature' => false // this is
going to be overwritten later
);
}
// resync $this->signatureSubject
// save $certificationRequestInfo in case there are any
\phpseclib\File\ASN1\Element objects in it
$certificationRequestInfo =
$this->currentCert['certificationRequestInfo'];
$this->loadCSR($this->saveCSR($this->currentCert));
$result = $this->_sign($this->privateKey,
$signatureAlgorithm);
$result['certificationRequestInfo'] =
$certificationRequestInfo;
$this->currentCert = $currentCert;
$this->signatureSubject = $signatureSubject;
return $result;
}
/**
* Sign a SPKAC
*
* @access public
* @return mixed
*/
function signSPKAC($signatureAlgorithm =
'sha1WithRSAEncryption')
{
if (!is_object($this->privateKey)) {
return false;
}
$origPublicKey = $this->publicKey;
$class = get_class($this->privateKey);
$this->publicKey = new $class();
$this->publicKey->loadKey($this->privateKey->getPublicKey());
$this->publicKey->setPublicKey();
$publicKey = $this->_formatSubjectPublicKey();
if (!$publicKey) {
return false;
}
$this->publicKey = $origPublicKey;
$currentCert = isset($this->currentCert) ? $this->currentCert
: null;
$signatureSubject = isset($this->signatureSubject) ?
$this->signatureSubject: null;
// re-signing a SPKAC seems silly but since everything else
supports re-signing why not?
if (isset($this->currentCert) &&
is_array($this->currentCert) &&
isset($this->currentCert['publicKeyAndChallenge'])) {
$this->currentCert['signatureAlgorithm']['algorithm']
= $signatureAlgorithm;
$this->currentCert['publicKeyAndChallenge']['spki']
= $publicKey;
if (!empty($this->challenge)) {
// the bitwise AND ensures that the output is a valid
IA5String
$this->currentCert['publicKeyAndChallenge']['challenge']
= $this->challenge & str_repeat("\x7F",
strlen($this->challenge));
}
} else {
$this->currentCert = array(
'publicKeyAndChallenge' =>
array(
'spki' => $publicKey,
// quoting
<https://developer.mozilla.org/en-US/docs/Web/HTML/Element/keygen>,
// "A challenge string that is submitted along
with the public key. Defaults to an empty string if not specified."
// both Firefox and OpenSSL ("openssl spkac
-key private.key") behave this way
// we could alternatively do this instead if we
ignored the specs:
// Random::string(8) &
str_repeat("\x7F", 8)
'challenge' =>
!empty($this->challenge) ? $this->challenge : ''
),
'signatureAlgorithm' =>
array('algorithm' => $signatureAlgorithm),
'signature' => false // this is
going to be overwritten later
);
}
// resync $this->signatureSubject
// save $publicKeyAndChallenge in case there are any
\phpseclib\File\ASN1\Element objects in it
$publicKeyAndChallenge =
$this->currentCert['publicKeyAndChallenge'];
$this->loadSPKAC($this->saveSPKAC($this->currentCert));
$result = $this->_sign($this->privateKey,
$signatureAlgorithm);
$result['publicKeyAndChallenge'] =
$publicKeyAndChallenge;
$this->currentCert = $currentCert;
$this->signatureSubject = $signatureSubject;
return $result;
}
/**
* Sign a CRL
*
* $issuer's private key needs to be loaded.
*
* @param \phpseclib\File\X509 $issuer
* @param \phpseclib\File\X509 $crl
* @param string $signatureAlgorithm optional
* @access public
* @return mixed
*/
function signCRL($issuer, $crl, $signatureAlgorithm =
'sha1WithRSAEncryption')
{
if (!is_object($issuer->privateKey) || empty($issuer->dn)) {
return false;
}
$currentCert = isset($this->currentCert) ? $this->currentCert
: null;
$signatureSubject = isset($this->signatureSubject) ?
$this->signatureSubject : null;
$thisUpdate = new DateTime('now', new
DateTimeZone(@date_default_timezone_get()));
$thisUpdate = !empty($this->startDate) ? $this->startDate :
$thisUpdate->format('D, d M Y H:i:s O');
if (isset($crl->currentCert) &&
is_array($crl->currentCert) &&
isset($crl->currentCert['tbsCertList'])) {
$this->currentCert = $crl->currentCert;
$this->currentCert['tbsCertList']['signature']['algorithm']
= $signatureAlgorithm;
$this->currentCert['signatureAlgorithm']['algorithm']
= $signatureAlgorithm;
} else {
$this->currentCert = array(
'tbsCertList' =>
array(
'version' => 'v2',
'signature' =>
array('algorithm' => $signatureAlgorithm),
'issuer' => false, // this is going to
be overwritten later
'thisUpdate' =>
$this->_timeField($thisUpdate) // $this->setStartDate()
),
'signatureAlgorithm' =>
array('algorithm' => $signatureAlgorithm),
'signature' => false // this is
going to be overwritten later
);
}
$tbsCertList = &$this->currentCert['tbsCertList'];
$tbsCertList['issuer'] = $issuer->dn;
$tbsCertList['thisUpdate'] =
$this->_timeField($thisUpdate);
if (!empty($this->endDate)) {
$tbsCertList['nextUpdate'] =
$this->_timeField($this->endDate); // $this->setEndDate()
} else {
unset($tbsCertList['nextUpdate']);
}
if (!empty($this->serialNumber)) {
$crlNumber = $this->serialNumber;
} else {
$crlNumber =
$this->getExtension('id-ce-cRLNumber');
// "The CRL number is a non-critical CRL extension that
conveys a
// monotonically increasing sequence number for a given CRL
scope and
// CRL issuer. This extension allows users to easily
determine when a
// particular CRL supersedes another CRL."
// -- https://tools.ietf.org/html/rfc5280#section-5.2.3
$crlNumber = $crlNumber !== false ? $crlNumber->add(new
BigInteger(1)) : null;
}
$this->removeExtension('id-ce-authorityKeyIdentifier');
$this->removeExtension('id-ce-issuerAltName');
// Be sure version >= v2 if some extension found.
$version = isset($tbsCertList['version']) ?
$tbsCertList['version'] : 0;
if (!$version) {
if (!empty($tbsCertList['crlExtensions'])) {
$version = 1; // v2.
} elseif
(!empty($tbsCertList['revokedCertificates'])) {
foreach ($tbsCertList['revokedCertificates'] as
$cert) {
if (!empty($cert['crlEntryExtensions'])) {
$version = 1; // v2.
}
}
}
if ($version) {
$tbsCertList['version'] = $version;
}
}
// Store additional extensions.
if (!empty($tbsCertList['version'])) { // At least v2.
if (!empty($crlNumber)) {
$this->setExtension('id-ce-cRLNumber',
$crlNumber);
}
if (isset($issuer->currentKeyIdentifier)) {
$this->setExtension('id-ce-authorityKeyIdentifier', array(
//'authorityCertIssuer' => array(
// array(
// 'directoryName' =>
$issuer->dn
// )
//),
'keyIdentifier' =>
$issuer->currentKeyIdentifier
));
//$extensions =
&$tbsCertList['crlExtensions'];
//if (isset($issuer->serialNumber)) {
// $extensions[count($extensions) -
1]['authorityCertSerialNumber'] = $issuer->serialNumber;
//}
//unset($extensions);
}
$issuerAltName =
$this->getExtension('id-ce-subjectAltName',
$issuer->currentCert);
if ($issuerAltName !== false) {
$this->setExtension('id-ce-issuerAltName',
$issuerAltName);
}
}
if (empty($tbsCertList['revokedCertificates'])) {
unset($tbsCertList['revokedCertificates']);
}
unset($tbsCertList);
// resync $this->signatureSubject
// save $tbsCertList in case there are any
\phpseclib\File\ASN1\Element objects in it
$tbsCertList = $this->currentCert['tbsCertList'];
$this->loadCRL($this->saveCRL($this->currentCert));
$result = $this->_sign($issuer->privateKey,
$signatureAlgorithm);
$result['tbsCertList'] = $tbsCertList;
$this->currentCert = $currentCert;
$this->signatureSubject = $signatureSubject;
return $result;
}
/**
* X.509 certificate signing helper function.
*
* @param \phpseclib\File\X509 $key
* @param string $signatureAlgorithm
* @access public
* @return mixed
*/
function _sign($key, $signatureAlgorithm)
{
if ($key instanceof RSA) {
switch ($signatureAlgorithm) {
case 'md2WithRSAEncryption':
case 'md5WithRSAEncryption':
case 'sha1WithRSAEncryption':
case 'sha224WithRSAEncryption':
case 'sha256WithRSAEncryption':
case 'sha384WithRSAEncryption':
case 'sha512WithRSAEncryption':
$key->setHash(preg_replace('#WithRSAEncryption$#',
'', $signatureAlgorithm));
$key->setSignatureMode(RSA::SIGNATURE_PKCS1);
$this->currentCert['signature'] =
base64_encode("\0" . $key->sign($this->signatureSubject));
return $this->currentCert;
}
}
return false;
}
/**
* Set certificate start date
*
* @param string $date
* @access public
*/
function setStartDate($date)
{
if (!is_object($date) || !is_a($date, 'DateTime')) {
$date = new DateTime($date, new
DateTimeZone(@date_default_timezone_get()));
}
$this->startDate = $date->format('D, d M Y H:i:s
O');
}
/**
* Set certificate end date
*
* @param string $date
* @access public
*/
function setEndDate($date)
{
/*
To indicate that a certificate has no well-defined expiration
date,
the notAfter SHOULD be assigned the GeneralizedTime value of
99991231235959Z.
-- http://tools.ietf.org/html/rfc5280#section-4.1.2.5
*/
if (strtolower($date) == 'lifetime') {
$temp = '99991231235959Z';
$asn1 = new ASN1();
$temp = chr(ASN1::TYPE_GENERALIZED_TIME) .
$asn1->_encodeLength(strlen($temp)) . $temp;
$this->endDate = new Element($temp);
} else {
if (!is_object($date) || !is_a($date, 'DateTime')) {
$date = new DateTime($date, new
DateTimeZone(@date_default_timezone_get()));
}
$this->endDate = $date->format('D, d M Y H:i:s
O');
}
}
/**
* Set Serial Number
*
* @param string $serial
* @param int $base optional
* @access public
*/
function setSerialNumber($serial, $base = -256)
{
$this->serialNumber = new BigInteger($serial, $base);
}
/**
* Turns the certificate into a certificate authority
*
* @access public
*/
function makeCA()
{
$this->caFlag = true;
}
/**
* Check for validity of subarray
*
* This is intended for use in conjunction with _subArrayUnchecked(),
* implementing the checks included in _subArray() but without copying
* a potentially large array by passing its reference by-value to
is_array().
*
* @param array $root
* @param string $path
* @return boolean
* @access private
*/
function _isSubArrayValid($root, $path)
{
if (!is_array($root)) {
return false;
}
foreach (explode('/', $path) as $i) {
if (!is_array($root)) {
return false;
}
if (!isset($root[$i])) {
return true;
}
$root = $root[$i];
}
return true;
}
/**
* Get a reference to a subarray
*
* This variant of _subArray() does no is_array() checking,
* so $root should be checked with _isSubArrayValid() first.
*
* This is here for performance reasons:
* Passing a reference (i.e. $root) by-value (i.e. to is_array())
* creates a copy. If $root is an especially large array, this is
expensive.
*
* @param array $root
* @param string $path absolute path with / as component separator
* @param bool $create optional
* @access private
* @return array|false
*/
function &_subArrayUnchecked(&$root, $path, $create = false)
{
$false = false;
foreach (explode('/', $path) as $i) {
if (!isset($root[$i])) {
if (!$create) {
return $false;
}
$root[$i] = array();
}
$root = &$root[$i];
}
return $root;
}
/**
* Get a reference to a subarray
*
* @param array $root
* @param string $path absolute path with / as component separator
* @param bool $create optional
* @access private
* @return array|false
*/
function &_subArray(&$root, $path, $create = false)
{
$false = false;
if (!is_array($root)) {
return $false;
}
foreach (explode('/', $path) as $i) {
if (!is_array($root)) {
return $false;
}
if (!isset($root[$i])) {
if (!$create) {
return $false;
}
$root[$i] = array();
}
$root = &$root[$i];
}
return $root;
}
/**
* Get a reference to an extension subarray
*
* @param array $root
* @param string $path optional absolute path with / as component
separator
* @param bool $create optional
* @access private
* @return array|false
*/
function &_extensions(&$root, $path = null, $create = false)
{
if (!isset($root)) {
$root = $this->currentCert;
}
switch (true) {
case !empty($path):
case !is_array($root):
break;
case isset($root['tbsCertificate']):
$path = 'tbsCertificate/extensions';
break;
case isset($root['tbsCertList']):
$path = 'tbsCertList/crlExtensions';
break;
case isset($root['certificationRequestInfo']):
$pth = 'certificationRequestInfo/attributes';
$attributes = &$this->_subArray($root, $pth,
$create);
if (is_array($attributes)) {
foreach ($attributes as $key => $value) {
if ($value['type'] ==
'pkcs-9-at-extensionRequest') {
$path = "$pth/$key/value/0";
break 2;
}
}
if ($create) {
$key = count($attributes);
$attributes[] = array('type' =>
'pkcs-9-at-extensionRequest', 'value' => array());
$path = "$pth/$key/value/0";
}
}
break;
}
$extensions = &$this->_subArray($root, $path, $create);
if (!is_array($extensions)) {
$false = false;
return $false;
}
return $extensions;
}
/**
* Remove an Extension
*
* @param string $id
* @param string $path optional
* @access private
* @return bool
*/
function _removeExtension($id, $path = null)
{
$extensions = &$this->_extensions($this->currentCert,
$path);
if (!is_array($extensions)) {
return false;
}
$result = false;
foreach ($extensions as $key => $value) {
if ($value['extnId'] == $id) {
unset($extensions[$key]);
$result = true;
}
}
$extensions = array_values($extensions);
// fix for https://bugs.php.net/75433 affecting PHP 7.2
if (!isset($extensions[0])) {
$extensions = array_splice($extensions, 0, 0);
}
return $result;
}
/**
* Get an Extension
*
* Returns the extension if it exists and false if not
*
* @param string $id
* @param array $cert optional
* @param string $path optional
* @access private
* @return mixed
*/
function _getExtension($id, $cert = null, $path = null)
{
$extensions = $this->_extensions($cert, $path);
if (!is_array($extensions)) {
return false;
}
foreach ($extensions as $key => $value) {
if ($value['extnId'] == $id) {
return $value['extnValue'];
}
}
return false;
}
/**
* Returns a list of all extensions in use
*
* @param array $cert optional
* @param string $path optional
* @access private
* @return array
*/
function _getExtensions($cert = null, $path = null)
{
$exts = $this->_extensions($cert, $path);
$extensions = array();
if (is_array($exts)) {
foreach ($exts as $extension) {
$extensions[] = $extension['extnId'];
}
}
return $extensions;
}
/**
* Set an Extension
*
* @param string $id
* @param mixed $value
* @param bool $critical optional
* @param bool $replace optional
* @param string $path optional
* @access private
* @return bool
*/
function _setExtension($id, $value, $critical = false, $replace = true,
$path = null)
{
$extensions = &$this->_extensions($this->currentCert,
$path, true);
if (!is_array($extensions)) {
return false;
}
$newext = array('extnId' => $id, 'critical'
=> $critical, 'extnValue' => $value);
foreach ($extensions as $key => $value) {
if ($value['extnId'] == $id) {
if (!$replace) {
return false;
}
$extensions[$key] = $newext;
return true;
}
}
$extensions[] = $newext;
return true;
}
/**
* Remove a certificate, CSR or CRL Extension
*
* @param string $id
* @access public
* @return bool
*/
function removeExtension($id)
{
return $this->_removeExtension($id);
}
/**
* Get a certificate, CSR or CRL Extension
*
* Returns the extension if it exists and false if not
*
* @param string $id
* @param array $cert optional
* @access public
* @return mixed
*/
function getExtension($id, $cert = null)
{
return $this->_getExtension($id, $cert);
}
/**
* Returns a list of all extensions in use in certificate, CSR or CRL
*
* @param array $cert optional
* @access public
* @return array
*/
function getExtensions($cert = null)
{
return $this->_getExtensions($cert);
}
/**
* Set a certificate, CSR or CRL Extension
*
* @param string $id
* @param mixed $value
* @param bool $critical optional
* @param bool $replace optional
* @access public
* @return bool
*/
function setExtension($id, $value, $critical = false, $replace = true)
{
return $this->_setExtension($id, $value, $critical, $replace);
}
/**
* Remove a CSR attribute.
*
* @param string $id
* @param int $disposition optional
* @access public
* @return bool
*/
function removeAttribute($id, $disposition = self::ATTR_ALL)
{
$attributes = &$this->_subArray($this->currentCert,
'certificationRequestInfo/attributes');
if (!is_array($attributes)) {
return false;
}
$result = false;
foreach ($attributes as $key => $attribute) {
if ($attribute['type'] == $id) {
$n = count($attribute['value']);
switch (true) {
case $disposition == self::ATTR_APPEND:
case $disposition == self::ATTR_REPLACE:
return false;
case $disposition >= $n:
$disposition -= $n;
break;
case $disposition == self::ATTR_ALL:
case $n == 1:
unset($attributes[$key]);
$result = true;
break;
default:
unset($attributes[$key]['value'][$disposition]);
$attributes[$key]['value'] =
array_values($attributes[$key]['value']);
$result = true;
break;
}
if ($result && $disposition != self::ATTR_ALL) {
break;
}
}
}
$attributes = array_values($attributes);
return $result;
}
/**
* Get a CSR attribute
*
* Returns the attribute if it exists and false if not
*
* @param string $id
* @param int $disposition optional
* @param array $csr optional
* @access public
* @return mixed
*/
function getAttribute($id, $disposition = self::ATTR_ALL, $csr = null)
{
if (empty($csr)) {
$csr = $this->currentCert;
}
$attributes = $this->_subArray($csr,
'certificationRequestInfo/attributes');
if (!is_array($attributes)) {
return false;
}
foreach ($attributes as $key => $attribute) {
if ($attribute['type'] == $id) {
$n = count($attribute['value']);
switch (true) {
case $disposition == self::ATTR_APPEND:
case $disposition == self::ATTR_REPLACE:
return false;
case $disposition == self::ATTR_ALL:
return $attribute['value'];
case $disposition >= $n:
$disposition -= $n;
break;
default:
return $attribute['value'][$disposition];
}
}
}
return false;
}
/**
* Returns a list of all CSR attributes in use
*
* @param array $csr optional
* @access public
* @return array
*/
function getAttributes($csr = null)
{
if (empty($csr)) {
$csr = $this->currentCert;
}
$attributes = $this->_subArray($csr,
'certificationRequestInfo/attributes');
$attrs = array();
if (is_array($attributes)) {
foreach ($attributes as $attribute) {
$attrs[] = $attribute['type'];
}
}
return $attrs;
}
/**
* Set a CSR attribute
*
* @param string $id
* @param mixed $value
* @param bool $disposition optional
* @access public
* @return bool
*/
function setAttribute($id, $value, $disposition = self::ATTR_ALL)
{
$attributes = &$this->_subArray($this->currentCert,
'certificationRequestInfo/attributes', true);
if (!is_array($attributes)) {
return false;
}
switch ($disposition) {
case self::ATTR_REPLACE:
$disposition = self::ATTR_APPEND;
case self::ATTR_ALL:
$this->removeAttribute($id);
break;
}
foreach ($attributes as $key => $attribute) {
if ($attribute['type'] == $id) {
$n = count($attribute['value']);
switch (true) {
case $disposition == self::ATTR_APPEND:
$last = $key;
break;
case $disposition >= $n:
$disposition -= $n;
break;
default:
$attributes[$key]['value'][$disposition]
= $value;
return true;
}
}
}
switch (true) {
case $disposition >= 0:
return false;
case isset($last):
$attributes[$last]['value'][] = $value;
break;
default:
$attributes[] = array('type' => $id,
'value' => $disposition == self::ATTR_ALL ? $value:
array($value));
break;
}
return true;
}
/**
* Sets the subject key identifier
*
* This is used by the id-ce-authorityKeyIdentifier and the
id-ce-subjectKeyIdentifier extensions.
*
* @param string $value
* @access public
*/
function setKeyIdentifier($value)
{
if (empty($value)) {
unset($this->currentKeyIdentifier);
} else {
$this->currentKeyIdentifier = base64_encode($value);
}
}
/**
* Compute a public key identifier.
*
* Although key identifiers may be set to any unique value, this
function
* computes key identifiers from public key according to the two
* recommended methods (4.2.1.2 RFC 3280).
* Highly polymorphic: try to accept all possible forms of key:
* - Key object
* - \phpseclib\File\X509 object with public or private key defined
* - Certificate or CSR array
* - \phpseclib\File\ASN1\Element object
* - PEM or DER string
*
* @param mixed $key optional
* @param int $method optional
* @access public
* @return string binary key identifier
*/
function computeKeyIdentifier($key = null, $method = 1)
{
if (is_null($key)) {
$key = $this;
}
switch (true) {
case is_string($key):
break;
case is_array($key) &&
isset($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
return
$this->computeKeyIdentifier($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
$method);
case is_array($key) &&
isset($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']):
return
$this->computeKeyIdentifier($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'],
$method);
case !is_object($key):
return false;
case $key instanceof Element:
// Assume the element is a bitstring-packed key.
$asn1 = new ASN1();
$decoded = $asn1->decodeBER($key->element);
if (empty($decoded)) {
return false;
}
$raw = $asn1->asn1map($decoded[0],
array('type' => ASN1::TYPE_BIT_STRING));
if (empty($raw)) {
return false;
}
$raw = base64_decode($raw);
// If the key is private, compute identifier from its
corresponding public key.
$key = new RSA();
if (!$key->loadKey($raw)) {
return false; // Not an unencrypted RSA key.
}
if ($key->getPrivateKey() !== false) { // If private.
return $this->computeKeyIdentifier($key, $method);
}
$key = $raw; // Is a public key.
break;
case $key instanceof X509:
if (isset($key->publicKey)) {
return
$this->computeKeyIdentifier($key->publicKey, $method);
}
if (isset($key->privateKey)) {
return
$this->computeKeyIdentifier($key->privateKey, $method);
}
if (isset($key->currentCert['tbsCertificate'])
|| isset($key->currentCert['certificationRequestInfo'])) {
return
$this->computeKeyIdentifier($key->currentCert, $method);
}
return false;
default: // Should be a key object (i.e.:
\phpseclib\Crypt\RSA).
$key = $key->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1);
break;
}
// If in PEM format, convert to binary.
$key = $this->_extractBER($key);
// Now we have the key string: compute its sha-1 sum.
$hash = new Hash('sha1');
$hash = $hash->hash($key);
if ($method == 2) {
$hash = substr($hash, -8);
$hash[0] = chr((ord($hash[0]) & 0x0F) | 0x40);
}
return $hash;
}
/**
* Format a public key as appropriate
*
* @access private
* @return array
*/
function _formatSubjectPublicKey()
{
if ($this->publicKey instanceof RSA) {
// the following two return statements do the same thing. i
dunno.. i just prefer the later for some reason.
// the former is a good example of how to do fuzzing on the
public key
//return new
Element(base64_decode(preg_replace('#-.+-|[\r\n]#', '',
$this->publicKey->getPublicKey())));
return array(
'algorithm' => array('algorithm'
=> 'rsaEncryption'),
'subjectPublicKey' =>
$this->publicKey->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1)
);
}
return false;
}
/**
* Set the domain name's which the cert is to be valid for
*
* @access public
* @return array
*/
function setDomain()
{
$this->domains = func_get_args();
$this->removeDNProp('id-at-commonName');
$this->setDNProp('id-at-commonName',
$this->domains[0]);
}
/**
* Set the IP Addresses's which the cert is to be valid for
*
* @access public
*/
function setIPAddress()
{
$this->ipAddresses = func_get_args();
/*
if (!isset($this->domains)) {
$this->removeDNProp('id-at-commonName');
$this->setDNProp('id-at-commonName',
$this->ipAddresses[0]);
}
*/
}
/**
* Helper function to build domain array
*
* @access private
* @param string $domain
* @return array
*/
function _dnsName($domain)
{
return array('dNSName' => $domain);
}
/**
* Helper function to build IP Address array
*
* (IPv6 is not currently supported)
*
* @access private
* @param string $address
* @return array
*/
function _iPAddress($address)
{
return array('iPAddress' => $address);
}
/**
* Get the index of a revoked certificate.
*
* @param array $rclist
* @param string $serial
* @param bool $create optional
* @access private
* @return int|false
*/
function _revokedCertificate(&$rclist, $serial, $create = false)
{
$serial = new BigInteger($serial);
foreach ($rclist as $i => $rc) {
if (!($serial->compare($rc['userCertificate']))) {
return $i;
}
}
if (!$create) {
return false;
}
$i = count($rclist);
$revocationDate = new DateTime('now', new
DateTimeZone(@date_default_timezone_get()));
$rclist[] = array('userCertificate' => $serial,
'revocationDate' =>
$this->_timeField($revocationDate->format('D, d M Y H:i:s
O')));
return $i;
}
/**
* Revoke a certificate.
*
* @param string $serial
* @param string $date optional
* @access public
* @return bool
*/
function revoke($serial, $date = null)
{
if (isset($this->currentCert['tbsCertList'])) {
if (is_array($rclist =
&$this->_subArray($this->currentCert,
'tbsCertList/revokedCertificates', true))) {
if ($this->_revokedCertificate($rclist, $serial) ===
false) { // If not yet revoked
if (($i = $this->_revokedCertificate($rclist,
$serial, true)) !== false) {
if (!empty($date)) {
$rclist[$i]['revocationDate'] =
$this->_timeField($date);
}
return true;
}
}
}
}
return false;
}
/**
* Unrevoke a certificate.
*
* @param string $serial
* @access public
* @return bool
*/
function unrevoke($serial)
{
if (is_array($rclist =
&$this->_subArray($this->currentCert,
'tbsCertList/revokedCertificates'))) {
if (($i = $this->_revokedCertificate($rclist, $serial)) !==
false) {
unset($rclist[$i]);
$rclist = array_values($rclist);
return true;
}
}
return false;
}
/**
* Get a revoked certificate.
*
* @param string $serial
* @access public
* @return mixed
*/
function getRevoked($serial)
{
if (is_array($rclist = $this->_subArray($this->currentCert,
'tbsCertList/revokedCertificates'))) {
if (($i = $this->_revokedCertificate($rclist, $serial)) !==
false) {
return $rclist[$i];
}
}
return false;
}
/**
* List revoked certificates
*
* @param array $crl optional
* @access public
* @return array
*/
function listRevoked($crl = null)
{
if (!isset($crl)) {
$crl = $this->currentCert;
}
if (!isset($crl['tbsCertList'])) {
return false;
}
$result = array();
if (is_array($rclist = $this->_subArray($crl,
'tbsCertList/revokedCertificates'))) {
foreach ($rclist as $rc) {
$result[] =
$rc['userCertificate']->toString();
}
}
return $result;
}
/**
* Remove a Revoked Certificate Extension
*
* @param string $serial
* @param string $id
* @access public
* @return bool
*/
function removeRevokedCertificateExtension($serial, $id)
{
if (is_array($rclist =
&$this->_subArray($this->currentCert,
'tbsCertList/revokedCertificates'))) {
if (($i = $this->_revokedCertificate($rclist, $serial)) !==
false) {
return $this->_removeExtension($id,
"tbsCertList/revokedCertificates/$i/crlEntryExtensions");
}
}
return false;
}
/**
* Get a Revoked Certificate Extension
*
* Returns the extension if it exists and false if not
*
* @param string $serial
* @param string $id
* @param array $crl optional
* @access public
* @return mixed
*/
function getRevokedCertificateExtension($serial, $id, $crl = null)
{
if (!isset($crl)) {
$crl = $this->currentCert;
}
if (is_array($rclist = $this->_subArray($crl,
'tbsCertList/revokedCertificates'))) {
if (($i = $this->_revokedCertificate($rclist, $serial)) !==
false) {
return $this->_getExtension($id, $crl,
"tbsCertList/revokedCertificates/$i/crlEntryExtensions");
}
}
return false;
}
/**
* Returns a list of all extensions in use for a given revoked
certificate
*
* @param string $serial
* @param array $crl optional
* @access public
* @return array
*/
function getRevokedCertificateExtensions($serial, $crl = null)
{
if (!isset($crl)) {
$crl = $this->currentCert;
}
if (is_array($rclist = $this->_subArray($crl,
'tbsCertList/revokedCertificates'))) {
if (($i = $this->_revokedCertificate($rclist, $serial)) !==
false) {
return $this->_getExtensions($crl,
"tbsCertList/revokedCertificates/$i/crlEntryExtensions");
}
}
return false;
}
/**
* Set a Revoked Certificate Extension
*
* @param string $serial
* @param string $id
* @param mixed $value
* @param bool $critical optional
* @param bool $replace optional
* @access public
* @return bool
*/
function setRevokedCertificateExtension($serial, $id, $value, $critical
= false, $replace = true)
{
if (isset($this->currentCert['tbsCertList'])) {
if (is_array($rclist =
&$this->_subArray($this->currentCert,
'tbsCertList/revokedCertificates', true))) {
if (($i = $this->_revokedCertificate($rclist, $serial,
true)) !== false) {
return $this->_setExtension($id, $value, $critical,
$replace,
"tbsCertList/revokedCertificates/$i/crlEntryExtensions");
}
}
}
return false;
}
/**
* 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
*/
if (strlen($str) > ini_get('pcre.backtrack_limit')) {
$temp = $str;
} else {
$temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms',
'', $str, 1);
$temp = preg_replace('#-+END.*[\r\n ]*.*#ms',
'', $temp, 1);
}
// remove new lines
$temp = str_replace(array("\r", "\n", '
'), '', $temp);
// remove the -----BEGIN CERTIFICATE----- and -----END
CERTIFICATE----- stuff
$temp = preg_replace('#^-+[^-]+-+|-+[^-]+-+$#',
'', $temp);
$temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ?
base64_decode($temp) : false;
return $temp != false ? $temp : $str;
}
/**
* Returns the OID corresponding to a name
*
* What's returned in the associative array returned by loadX509()
(or load*()) is either a name or an OID if
* no OID to name mapping is available. The problem with this is that
what may be an unmapped OID in one version
* of phpseclib may not be unmapped in the next version, so apps that
are looking at this OID may not be able
* to work from version to version.
*
* This method will return the OID if a name is passed to it and if no
mapping is avialable it'll assume that
* what's being passed to it already is an OID and return that
instead. A few examples.
*
* getOID('2.16.840.1.101.3.4.2.1') ==
'2.16.840.1.101.3.4.2.1'
* getOID('id-sha256') == '2.16.840.1.101.3.4.2.1'
* getOID('zzz') == 'zzz'
*
* @access public
* @return string
*/
function getOID($name)
{
static $reverseMap;
if (!isset($reverseMap)) {
$reverseMap = array_flip($this->oids);
}
return isset($reverseMap[$name]) ? $reverseMap[$name] : $name;
}
}
vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php000064400000366204151156520650017376
0ustar00<?php
/**
* Pure-PHP arbitrary precision integer arithmetic library.
*
* Supports base-2, base-10, base-16, and base-256 numbers. Uses the GMP
or BCMath extensions, if available,
* and an internal implementation, otherwise.
*
* PHP version 5
*
* {@internal (all DocBlock comments regarding implementation - such as the
one that follows - refer to the
* {@link self::MODE_INTERNAL self::MODE_INTERNAL} mode)
*
* BigInteger uses base-2**26 to perform operations such as multiplication
and division and
* base-2**52 (ie. two base 2**26 digits) to perform addition and
subtraction. Because the largest possible
* value when multiplying two base-2**26 numbers together is a base-2**52
number, double precision floating
* point numbers - numbers that should be supported on most hardware and
whose significand is 53 bits - are
* used. As a consequence, bitwise operators such as >> and <<
cannot be used, nor can the modulo operator %,
* 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.
*
* Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness
little endian} format. ie.
* (new \phpseclib\Math\BigInteger(pow(2, 26)))->value = array(0, 1)
*
* Useful resources are as follows:
*
* - {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf
Handbook of Applied Cryptography (HAC)}
* - {@link http://math.libtomcrypt.com/files/tommath.pdf Multi-Precision
Math (MPM)}
* - Java's BigInteger classes. See
/j2se/src/share/classes/java/math in jdk-1_5_0-src-jrl.zip
*
* Here's an example of how to use this library:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger(2);
* $b = new \phpseclib\Math\BigInteger(3);
*
* $c = $a->add($b);
*
* echo $c->toString(); // outputs 5
* ?>
* </code>
*
* @category Math
* @package BigInteger
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2006 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
*/
namespace phpseclib\Math;
use phpseclib\Crypt\Random;
/**
* Pure-PHP arbitrary precision integer arithmetic library. Supports
base-2, base-10, base-16, and base-256
* numbers.
*
* @package BigInteger
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class BigInteger
{
/**#@+
* Reduction constants
*
* @access private
* @see BigInteger::_reduce()
*/
/**
* @see BigInteger::_montgomery()
* @see BigInteger::_prepMontgomery()
*/
const MONTGOMERY = 0;
/**
* @see BigInteger::_barrett()
*/
const BARRETT = 1;
/**
* @see BigInteger::_mod2()
*/
const POWEROF2 = 2;
/**
* @see BigInteger::_remainder()
*/
const CLASSIC = 3;
/**
* @see BigInteger::__clone()
*/
const NONE = 4;
/**#@-*/
/**#@+
* Array constants
*
* Rather than create a thousands and thousands of new BigInteger
objects in repeated function calls to add() and
* multiply() or whatever, we'll just work directly on arrays,
taking them in as parameters and returning them.
*
* @access private
*/
/**
* $result[self::VALUE] contains the value.
*/
const VALUE = 0;
/**
* $result[self::SIGN] contains the sign.
*/
const SIGN = 1;
/**#@-*/
/**#@+
* @access private
* @see BigInteger::_montgomery()
* @see BigInteger::_barrett()
*/
/**
* Cache constants
*
* $cache[self::VARIABLE] tells us whether or not the cached data is
still valid.
*/
const VARIABLE = 0;
/**
* $cache[self::DATA] contains the cached data.
*/
const DATA = 1;
/**#@-*/
/**#@+
* Mode constants.
*
* @access private
* @see BigInteger::__construct()
*/
/**
* To use the pure-PHP implementation
*/
const MODE_INTERNAL = 1;
/**
* To use the BCMath library
*
* (if enabled; otherwise, the internal implementation will be used)
*/
const MODE_BCMATH = 2;
/**
* To use the GMP library
*
* (if present; otherwise, either the BCMath or the internal
implementation will be used)
*/
const MODE_GMP = 3;
/**#@-*/
/**
* Karatsuba Cutoff
*
* At what point do we switch between Karatsuba multiplication and
schoolbook long multiplication?
*
* @access private
*/
const KARATSUBA_CUTOFF = 25;
/**#@+
* Static properties used by the pure-PHP implementation.
*
* @see __construct()
*/
protected static $base;
protected static $baseFull;
protected static $maxDigit;
protected static $msb;
/**
* $max10 in greatest $max10Len satisfying
* $max10 = 10**$max10Len <= 2**$base.
*/
protected static $max10;
/**
* $max10Len in greatest $max10Len satisfying
* $max10 = 10**$max10Len <= 2**$base.
*/
protected static $max10Len;
protected static $maxDigit2;
/**#@-*/
/**
* Holds the BigInteger's value.
*
* @var array
* @access private
*/
var $value;
/**
* Holds the BigInteger's magnitude.
*
* @var bool
* @access private
*/
var $is_negative = false;
/**
* Precision
*
* @see self::setPrecision()
* @access private
*/
var $precision = -1;
/**
* Precision Bitmask
*
* @see self::setPrecision()
* @access private
*/
var $bitmask = false;
/**
* Mode independent value used for serialization.
*
* If the bcmath or gmp extensions are installed $this->value will
be a non-serializable resource, hence the need for
* a variable that'll be serializable regardless of whether or not
extensions are being used. Unlike $this->value,
* however, $this->hex is only calculated when $this->__sleep()
is called.
*
* @see self::__sleep()
* @see self::__wakeup()
* @var string
* @access private
*/
var $hex;
/**
* Converts base-2, base-10, base-16, and binary strings (base-256) to
BigIntegers.
*
* If the second parameter - $base - is negative, then it will be
assumed that the number's are encoded using
* two's compliment. The sole exception to this is -10, which is
treated the same as 10 is.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('0x32', 16); // 50
in base-16
*
* echo $a->toString(); // outputs 50
* ?>
* </code>
*
* @param int|string|resource $x base-10 number or base-$base number if
$base set.
* @param int $base
* @return \phpseclib\Math\BigInteger
* @access public
*/
function __construct($x = 0, $base = 10)
{
if (!defined('MATH_BIGINTEGER_MODE')) {
switch (true) {
case extension_loaded('gmp'):
define('MATH_BIGINTEGER_MODE',
self::MODE_GMP);
break;
case extension_loaded('bcmath'):
define('MATH_BIGINTEGER_MODE',
self::MODE_BCMATH);
break;
default:
define('MATH_BIGINTEGER_MODE',
self::MODE_INTERNAL);
}
}
if (extension_loaded('openssl') &&
!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
$versions = array();
// avoid generating errors (even with suppression) when
phpinfo() is disabled (common in production systems)
if (strpos(ini_get('disable_functions'),
'phpinfo') === false) {
ob_start();
@phpinfo();
$content = ob_get_contents();
ob_end_clean();
preg_match_all('#OpenSSL (Header|Library)
Version(.*)#im', $content, $matches);
if (!empty($matches[1])) {
for ($i = 0; $i < count($matches[1]); $i++) {
$fullVersion = trim(str_replace('=>',
'', strip_tags($matches[2][$i])));
// Remove letter part in OpenSSL version
if (!preg_match('/(\d+\.\d+\.\d+)/i',
$fullVersion, $m)) {
$versions[$matches[1][$i]] = $fullVersion;
} else {
$versions[$matches[1][$i]] = $m[0];
}
}
}
}
// 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']:
case version_compare($versions['Header'],
'1.0.0') >= 0 &&
version_compare($versions['Library'], '1.0.0') >= 0:
define('MATH_BIGINTEGER_OPENSSL_ENABLED',
true);
break;
default:
define('MATH_BIGINTEGER_OPENSSL_DISABLE',
true);
}
}
if (!defined('PHP_INT_SIZE')) {
define('PHP_INT_SIZE', 4);
}
if (empty(self::$base) && MATH_BIGINTEGER_MODE ==
self::MODE_INTERNAL) {
switch (PHP_INT_SIZE) {
case 8: // use 64-bit integers if int size is 8 bytes
self::$base = 31;
self::$baseFull = 0x80000000;
self::$maxDigit = 0x7FFFFFFF;
self::$msb = 0x40000000;
self::$max10 = 1000000000;
self::$max10Len = 9;
self::$maxDigit2 = pow(2, 62);
break;
//case 4: // use 64-bit floats if int size is 4 bytes
default:
self::$base = 26;
self::$baseFull = 0x4000000;
self::$maxDigit = 0x3FFFFFF;
self::$msb = 0x2000000;
self::$max10 = 10000000;
self::$max10Len = 7;
self::$maxDigit2 = pow(2, 52); // pow() prevents
truncation
}
}
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
switch (true) {
case is_resource($x) && get_resource_type($x)
== 'GMP integer':
// PHP 5.6 switched GMP from using resources to objects
case $x instanceof \GMP:
$this->value = $x;
return;
}
$this->value = gmp_init(0);
break;
case self::MODE_BCMATH:
$this->value = '0';
break;
default:
$this->value = array();
}
// '0' counts as empty() but when the base is 256
'0' is equal to ord('0') or 48
// '0' is the only value like this per
http://php.net/empty
if (empty($x) && (abs($base) != 256 || $x !==
'0')) {
return;
}
switch ($base) {
case -256:
if (ord($x[0]) & 0x80) {
$x = ~$x;
$this->is_negative = true;
}
case 256:
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$this->value =
function_exists('gmp_import') ?
gmp_import($x) :
gmp_init('0x' . bin2hex($x));
if ($this->is_negative) {
$this->value = gmp_neg($this->value);
}
break;
case self::MODE_BCMATH:
// round $len to the nearest 4 (thanks, DavidMJ!)
$len = (strlen($x) + 3) & 0xFFFFFFFC;
$x = str_pad($x, $len, chr(0), STR_PAD_LEFT);
for ($i = 0; $i < $len; $i+= 4) {
$this->value = bcmul($this->value,
'4294967296', 0); // 4294967296 == 2**32
$this->value = bcadd($this->value,
0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2])
<< 8) | ord($x[$i + 3])), 0);
}
if ($this->is_negative) {
$this->value = '-' .
$this->value;
}
break;
// converts a base-2**8 (big endian / msb) number to
base-2**26 (little endian / lsb)
default:
while (strlen($x)) {
$this->value[] =
$this->_bytes2int($this->_base256_rshift($x, self::$base));
}
}
if ($this->is_negative) {
if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
$this->is_negative = false;
}
$temp = $this->add(new static('-1'));
$this->value = $temp->value;
}
break;
case 16:
case -16:
if ($base > 0 && $x[0] == '-') {
$this->is_negative = true;
$x = substr($x, 1);
}
$x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#',
'$1', $x);
$is_negative = false;
if ($base < 0 && hexdec($x[0]) >= 8) {
$this->is_negative = $is_negative = true;
$x = bin2hex(~pack('H*', $x));
}
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = $this->is_negative ? '-0x' .
$x : '0x' . $x;
$this->value = gmp_init($temp);
$this->is_negative = false;
break;
case self::MODE_BCMATH:
$x = (strlen($x) & 1) ? '0' . $x :
$x;
$temp = new static(pack('H*', $x), 256);
$this->value = $this->is_negative ?
'-' . $temp->value : $temp->value;
$this->is_negative = false;
break;
default:
$x = (strlen($x) & 1) ? '0' . $x :
$x;
$temp = new static(pack('H*', $x), 256);
$this->value = $temp->value;
}
if ($is_negative) {
$temp = $this->add(new static('-1'));
$this->value = $temp->value;
}
break;
case 10:
case -10:
// (?<!^)(?:-).*: find any -'s that aren't at
the beginning and then any characters that follow that
// (?<=^|-)0*: find any 0's that are preceded by
the start of the string or by a - (ie. octals)
// [^-0-9].*: find any non-numeric characters and then any
characters that follow that
$x =
preg_replace('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#',
'', $x);
if (!strlen($x) || $x == '-') {
$x = '0';
}
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$this->value = gmp_init($x);
break;
case self::MODE_BCMATH:
// explicitly casting $x to a string is necessary,
here, since doing $x[0] on -1 yields different
// results then doing it on '-1' does
(modInverse does $x[0])
$this->value = $x === '-' ?
'0' : (string) $x;
break;
default:
$temp = new static();
$multiplier = new static();
$multiplier->value = array(self::$max10);
if ($x[0] == '-') {
$this->is_negative = true;
$x = substr($x, 1);
}
$x = str_pad($x, strlen($x) + ((self::$max10Len -
1) * strlen($x)) % self::$max10Len, 0, STR_PAD_LEFT);
while (strlen($x)) {
$temp = $temp->multiply($multiplier);
$temp = $temp->add(new
static($this->_int2bytes(substr($x, 0, self::$max10Len)), 256));
$x = substr($x, self::$max10Len);
}
$this->value = $temp->value;
}
break;
case 2: // base-2 support originally implemented by Lluis
Pamies - thanks!
case -2:
if ($base > 0 && $x[0] == '-') {
$this->is_negative = true;
$x = substr($x, 1);
}
$x = preg_replace('#^([01]*).*#', '$1',
$x);
$x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0,
STR_PAD_LEFT);
$str = '0x';
while (strlen($x)) {
$part = substr($x, 0, 4);
$str.= dechex(bindec($part));
$x = substr($x, 4);
}
if ($this->is_negative) {
$str = '-' . $str;
}
$temp = new static($str, 8 * $base); // ie. either -16 or
+16
$this->value = $temp->value;
$this->is_negative = $temp->is_negative;
break;
default:
// base not supported, so we'll let $this == 0
}
}
/**
* Converts a BigInteger to a byte string (eg. base-256).
*
* Negative numbers are saved as positive numbers, unless
$twos_compliment is set to true, at which point, they're
* saved as two's compliment.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('65');
*
* echo $a->toBytes(); // outputs chr(65)
* ?>
* </code>
*
* @param bool $twos_compliment
* @return string
* @access public
* @internal Converts a base-2**26 number to base-2**8
*/
function toBytes($twos_compliment = false)
{
if ($twos_compliment) {
$comparison = $this->compare(new static());
if ($comparison == 0) {
return $this->precision > 0 ? str_repeat(chr(0),
($this->precision + 1) >> 3) : '';
}
$temp = $comparison < 0 ? $this->add(new static(1)) :
$this->copy();
$bytes = $temp->toBytes();
if (!strlen($bytes)) { // eg. if the number we're trying
to convert is -1
$bytes = chr(0);
}
if ($this->precision <= 0 && (ord($bytes[0])
& 0x80)) {
$bytes = chr(0) . $bytes;
}
return $comparison < 0 ? ~$bytes : $bytes;
}
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
if (gmp_cmp($this->value, gmp_init(0)) == 0) {
return $this->precision > 0 ? str_repeat(chr(0),
($this->precision + 1) >> 3) : '';
}
if (function_exists('gmp_export')) {
$temp = gmp_export($this->value);
} else {
$temp = gmp_strval(gmp_abs($this->value), 16);
$temp = (strlen($temp) & 1) ? '0' . $temp
: $temp;
$temp = pack('H*', $temp);
}
return $this->precision > 0 ?
substr(str_pad($temp, $this->precision >> 3,
chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
ltrim($temp, chr(0));
case self::MODE_BCMATH:
if ($this->value === '0') {
return $this->precision > 0 ? str_repeat(chr(0),
($this->precision + 1) >> 3) : '';
}
$value = '';
$current = $this->value;
if ($current[0] == '-') {
$current = substr($current, 1);
}
while (bccomp($current, '0', 0) > 0) {
$temp = bcmod($current, '16777216');
$value = chr($temp >> 16) . chr($temp >> 8)
. chr($temp) . $value;
$current = bcdiv($current, '16777216', 0);
}
return $this->precision > 0 ?
substr(str_pad($value, $this->precision >> 3,
chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
ltrim($value, chr(0));
}
if (!count($this->value)) {
return $this->precision > 0 ? str_repeat(chr(0),
($this->precision + 1) >> 3) : '';
}
$result =
$this->_int2bytes($this->value[count($this->value) - 1]);
$temp = $this->copy();
for ($i = count($temp->value) - 2; $i >= 0; --$i) {
$temp->_base256_lshift($result, self::$base);
$result = $result |
str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0),
STR_PAD_LEFT);
}
return $this->precision > 0 ?
str_pad(substr($result, -(($this->precision + 7) >>
3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) :
$result;
}
/**
* Converts a BigInteger to a hex string (eg. base-16)).
*
* Negative numbers are saved as positive numbers, unless
$twos_compliment is set to true, at which point, they're
* saved as two's compliment.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('65');
*
* echo $a->toHex(); // outputs '41'
* ?>
* </code>
*
* @param bool $twos_compliment
* @return string
* @access public
* @internal Converts a base-2**26 number to base-2**8
*/
function toHex($twos_compliment = false)
{
return bin2hex($this->toBytes($twos_compliment));
}
/**
* Converts a BigInteger to a bit string (eg. base-2).
*
* Negative numbers are saved as positive numbers, unless
$twos_compliment is set to true, at which point, they're
* saved as two's compliment.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('65');
*
* echo $a->toBits(); // outputs '1000001'
* ?>
* </code>
*
* @param bool $twos_compliment
* @return string
* @access public
* @internal Converts a base-2**26 number to base-2**2
*/
function toBits($twos_compliment = false)
{
$hex = $this->toHex($twos_compliment);
$bits = '';
for ($i = strlen($hex) - 6, $start = strlen($hex) % 6; $i >=
$start; $i-=6) {
$bits = str_pad(decbin(hexdec(substr($hex, $i, 6))), 24,
'0', STR_PAD_LEFT) . $bits;
}
if ($start) { // hexdec('') == 0
$bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8 *
$start, '0', STR_PAD_LEFT) . $bits;
}
$result = $this->precision > 0 ? substr($bits,
-$this->precision) : ltrim($bits, '0');
if ($twos_compliment && $this->compare(new static())
> 0 && $this->precision <= 0) {
return '0' . $result;
}
return $result;
}
/**
* Converts a BigInteger to a base-10 number.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('50');
*
* echo $a->toString(); // outputs 50
* ?>
* </code>
*
* @return string
* @access public
* @internal Converts a base-2**26 number to base-10**7 (which is
pretty much base-10)
*/
function toString()
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
return gmp_strval($this->value);
case self::MODE_BCMATH:
if ($this->value === '0') {
return '0';
}
return ltrim($this->value, '0');
}
if (!count($this->value)) {
return '0';
}
$temp = $this->copy();
$temp->bitmask = false;
$temp->is_negative = false;
$divisor = new static();
$divisor->value = array(self::$max10);
$result = '';
while (count($temp->value)) {
list($temp, $mod) = $temp->divide($divisor);
$result = str_pad(isset($mod->value[0]) ? $mod->value[0]
: '', self::$max10Len, '0', STR_PAD_LEFT) . $result;
}
$result = ltrim($result, '0');
if (empty($result)) {
$result = '0';
}
if ($this->is_negative) {
$result = '-' . $result;
}
return $result;
}
/**
* Copy an object
*
* PHP5 passes objects by reference while PHP4 passes by value. As
such, we need a function to guarantee
* that all objects are passed by value, when appropriate. More
information can be found here:
*
* {@link http://php.net/language.oop5.basic#51624}
*
* @access public
* @see self::__clone()
* @return \phpseclib\Math\BigInteger
*/
function copy()
{
$temp = new static();
$temp->value = $this->value;
$temp->is_negative = $this->is_negative;
$temp->precision = $this->precision;
$temp->bitmask = $this->bitmask;
return $temp;
}
/**
* __toString() magic method
*
* Will be called, automatically, if you're supporting just PHP5.
If you're supporting PHP4, you'll need to call
* toString().
*
* @access public
* @internal Implemented per a suggestion by Techie-Michael - thanks!
*/
function __toString()
{
return $this->toString();
}
/**
* __clone() magic method
*
* Although you can call BigInteger::__toString() directly in PHP5, you
cannot call BigInteger::__clone() directly
* in PHP5. You can in PHP4 since it's not a magic method, but in
PHP5, you have to call it by using the PHP5
* only syntax of $y = clone $x. As such, if you're trying to
write an application that works on both PHP4 and
* PHP5, call BigInteger::copy(), instead.
*
* @access public
* @see self::copy()
* @return \phpseclib\Math\BigInteger
*/
function __clone()
{
return $this->copy();
}
/**
* __sleep() magic method
*
* Will be called, automatically, when serialize() is called on a
BigInteger object.
*
* @see self::__wakeup()
* @access public
*/
function __sleep()
{
$this->hex = $this->toHex(true);
$vars = array('hex');
if ($this->precision > 0) {
$vars[] = 'precision';
}
return $vars;
}
/**
* __wakeup() magic method
*
* Will be called, automatically, when unserialize() is called on a
BigInteger object.
*
* @see self::__sleep()
* @access public
*/
function __wakeup()
{
$temp = new static($this->hex, -16);
$this->value = $temp->value;
$this->is_negative = $temp->is_negative;
if ($this->precision > 0) {
// recalculate $this->bitmask
$this->setPrecision($this->precision);
}
}
/**
* __debugInfo() magic method
*
* Will be called, automatically, when print_r() or var_dump() are
called
*
* @access public
*/
function __debugInfo()
{
$opts = array();
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$engine = 'gmp';
break;
case self::MODE_BCMATH:
$engine = 'bcmath';
break;
case self::MODE_INTERNAL:
$engine = 'internal';
$opts[] = PHP_INT_SIZE == 8 ? '64-bit' :
'32-bit';
}
if (MATH_BIGINTEGER_MODE != self::MODE_GMP &&
defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
$opts[] = 'OpenSSL';
}
if (!empty($opts)) {
$engine.= ' (' . implode('.', $opts) .
')';
}
return array(
'value' => '0x' . $this->toHex(true),
'engine' => $engine
);
}
/**
* Adds two BigIntegers.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('10');
* $b = new \phpseclib\Math\BigInteger('20');
*
* $c = $a->add($b);
*
* echo $c->toString(); // outputs 30
* ?>
* </code>
*
* @param \phpseclib\Math\BigInteger $y
* @return \phpseclib\Math\BigInteger
* @access public
* @internal Performs base-2**52 addition
*/
function add($y)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_add($this->value, $y->value);
return $this->_normalize($temp);
case self::MODE_BCMATH:
$temp = new static();
$temp->value = bcadd($this->value, $y->value, 0);
return $this->_normalize($temp);
}
$temp = $this->_add($this->value, $this->is_negative,
$y->value, $y->is_negative);
$result = new static();
$result->value = $temp[self::VALUE];
$result->is_negative = $temp[self::SIGN];
return $this->_normalize($result);
}
/**
* Performs addition.
*
* @param array $x_value
* @param bool $x_negative
* @param array $y_value
* @param bool $y_negative
* @return array
* @access private
*/
function _add($x_value, $x_negative, $y_value, $y_negative)
{
$x_size = count($x_value);
$y_size = count($y_value);
if ($x_size == 0) {
return array(
self::VALUE => $y_value,
self::SIGN => $y_negative
);
} elseif ($y_size == 0) {
return array(
self::VALUE => $x_value,
self::SIGN => $x_negative
);
}
// subtract, if appropriate
if ($x_negative != $y_negative) {
if ($x_value == $y_value) {
return array(
self::VALUE => array(),
self::SIGN => false
);
}
$temp = $this->_subtract($x_value, false, $y_value, false);
$temp[self::SIGN] = $this->_compare($x_value, false,
$y_value, false) > 0 ?
$x_negative : $y_negative;
return $temp;
}
if ($x_size < $y_size) {
$size = $x_size;
$value = $y_value;
} else {
$size = $y_size;
$value = $x_value;
}
$value[count($value)] = 0; // just in case the carry adds an extra
digit
$carry = 0;
for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) {
$sum = $x_value[$j] * self::$baseFull + $x_value[$i] +
$y_value[$j] * self::$baseFull + $y_value[$i] + $carry;
$carry = $sum >= self::$maxDigit2; // eg. floor($sum /
2**52); only possible values (in any base) are 0 and 1
$sum = $carry ? $sum - self::$maxDigit2 : $sum;
$temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum
>> 31);
$value[$i] = (int) ($sum - self::$baseFull * $temp); // eg. a
faster alternative to fmod($sum, 0x4000000)
$value[$j] = $temp;
}
if ($j == $size) { // ie. if $y_size is odd
$sum = $x_value[$i] + $y_value[$i] + $carry;
$carry = $sum >= self::$baseFull;
$value[$i] = $carry ? $sum - self::$baseFull : $sum;
++$i; // ie. let $i = $j since we've just done $value[$i]
}
if ($carry) {
for (; $value[$i] == self::$maxDigit; ++$i) {
$value[$i] = 0;
}
++$value[$i];
}
return array(
self::VALUE => $this->_trim($value),
self::SIGN => $x_negative
);
}
/**
* Subtracts two BigIntegers.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('10');
* $b = new \phpseclib\Math\BigInteger('20');
*
* $c = $a->subtract($b);
*
* echo $c->toString(); // outputs -10
* ?>
* </code>
*
* @param \phpseclib\Math\BigInteger $y
* @return \phpseclib\Math\BigInteger
* @access public
* @internal Performs base-2**52 subtraction
*/
function subtract($y)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_sub($this->value, $y->value);
return $this->_normalize($temp);
case self::MODE_BCMATH:
$temp = new static();
$temp->value = bcsub($this->value, $y->value, 0);
return $this->_normalize($temp);
}
$temp = $this->_subtract($this->value, $this->is_negative,
$y->value, $y->is_negative);
$result = new static();
$result->value = $temp[self::VALUE];
$result->is_negative = $temp[self::SIGN];
return $this->_normalize($result);
}
/**
* Performs subtraction.
*
* @param array $x_value
* @param bool $x_negative
* @param array $y_value
* @param bool $y_negative
* @return array
* @access private
*/
function _subtract($x_value, $x_negative, $y_value, $y_negative)
{
$x_size = count($x_value);
$y_size = count($y_value);
if ($x_size == 0) {
return array(
self::VALUE => $y_value,
self::SIGN => !$y_negative
);
} elseif ($y_size == 0) {
return array(
self::VALUE => $x_value,
self::SIGN => $x_negative
);
}
// add, if appropriate (ie. -$x - +$y or +$x - -$y)
if ($x_negative != $y_negative) {
$temp = $this->_add($x_value, false, $y_value, false);
$temp[self::SIGN] = $x_negative;
return $temp;
}
$diff = $this->_compare($x_value, $x_negative, $y_value,
$y_negative);
if (!$diff) {
return array(
self::VALUE => array(),
self::SIGN => false
);
}
// switch $x and $y around, if appropriate.
if ((!$x_negative && $diff < 0) || ($x_negative
&& $diff > 0)) {
$temp = $x_value;
$x_value = $y_value;
$y_value = $temp;
$x_negative = !$x_negative;
$x_size = count($x_value);
$y_size = count($y_value);
}
// at this point, $x_value should be at least as big as - if not
bigger than - $y_value
$carry = 0;
for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) {
$sum = $x_value[$j] * self::$baseFull + $x_value[$i] -
$y_value[$j] * self::$baseFull - $y_value[$i] - $carry;
$carry = $sum < 0; // eg. floor($sum / 2**52); only possible
values (in any base) are 0 and 1
$sum = $carry ? $sum + self::$maxDigit2 : $sum;
$temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum
>> 31);
$x_value[$i] = (int) ($sum - self::$baseFull * $temp);
$x_value[$j] = $temp;
}
if ($j == $y_size) { // ie. if $y_size is odd
$sum = $x_value[$i] - $y_value[$i] - $carry;
$carry = $sum < 0;
$x_value[$i] = $carry ? $sum + self::$baseFull : $sum;
++$i;
}
if ($carry) {
for (; !$x_value[$i]; ++$i) {
$x_value[$i] = self::$maxDigit;
}
--$x_value[$i];
}
return array(
self::VALUE => $this->_trim($x_value),
self::SIGN => $x_negative
);
}
/**
* Multiplies two BigIntegers
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('10');
* $b = new \phpseclib\Math\BigInteger('20');
*
* $c = $a->multiply($b);
*
* echo $c->toString(); // outputs 200
* ?>
* </code>
*
* @param \phpseclib\Math\BigInteger $x
* @return \phpseclib\Math\BigInteger
* @access public
*/
function multiply($x)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_mul($this->value, $x->value);
return $this->_normalize($temp);
case self::MODE_BCMATH:
$temp = new static();
$temp->value = bcmul($this->value, $x->value, 0);
return $this->_normalize($temp);
}
$temp = $this->_multiply($this->value, $this->is_negative,
$x->value, $x->is_negative);
$product = new static();
$product->value = $temp[self::VALUE];
$product->is_negative = $temp[self::SIGN];
return $this->_normalize($product);
}
/**
* Performs multiplication.
*
* @param array $x_value
* @param bool $x_negative
* @param array $y_value
* @param bool $y_negative
* @return array
* @access private
*/
function _multiply($x_value, $x_negative, $y_value, $y_negative)
{
//if ( $x_value == $y_value ) {
// return array(
// self::VALUE => $this->_square($x_value),
// self::SIGN => $x_sign != $y_value
// );
//}
$x_length = count($x_value);
$y_length = count($y_value);
if (!$x_length || !$y_length) { // a 0 is being multiplied
return array(
self::VALUE => array(),
self::SIGN => false
);
}
return array(
self::VALUE => min($x_length, $y_length) < 2 *
self::KARATSUBA_CUTOFF ?
$this->_trim($this->_regularMultiply($x_value,
$y_value)) :
$this->_trim($this->_karatsuba($x_value, $y_value)),
self::SIGN => $x_negative != $y_negative
);
}
/**
* Performs long multiplication on two BigIntegers
*
* Modeled after 'multiply' in MutableBigInteger.java.
*
* @param array $x_value
* @param array $y_value
* @return array
* @access private
*/
function _regularMultiply($x_value, $y_value)
{
$x_length = count($x_value);
$y_length = count($y_value);
if (!$x_length || !$y_length) { // a 0 is being multiplied
return array();
}
if ($x_length < $y_length) {
$temp = $x_value;
$x_value = $y_value;
$y_value = $temp;
$x_length = count($x_value);
$y_length = count($y_value);
}
$product_value = $this->_array_repeat(0, $x_length + $y_length);
// the following for loop could be removed if the for loop
following it
// (the one with nested for loops) initially set $i to 0, but
// doing so would also make the result in one set of unnecessary
adds,
// since on the outermost loops first pass, $product->value[$k]
is going
// to always be 0
$carry = 0;
for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0
$temp = $x_value[$j] * $y_value[0] + $carry; //
$product_value[$k] == 0
$carry = self::$base === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$product_value[$j] = (int) ($temp - self::$baseFull * $carry);
}
$product_value[$j] = $carry;
// the above for loop is what the previous comment was talking
about. the
// following for loop is the "one with nested for loops"
for ($i = 1; $i < $y_length; ++$i) {
$carry = 0;
for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) {
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] +
$carry;
$carry = self::$base === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$product_value[$k] = (int) ($temp - self::$baseFull *
$carry);
}
$product_value[$k] = $carry;
}
return $product_value;
}
/**
* Performs Karatsuba multiplication on two BigIntegers
*
* See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm
Karatsuba algorithm} and
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM
5.2.3}.
*
* @param array $x_value
* @param array $y_value
* @return array
* @access private
*/
function _karatsuba($x_value, $y_value)
{
$m = min(count($x_value) >> 1, count($y_value) >> 1);
if ($m < self::KARATSUBA_CUTOFF) {
return $this->_regularMultiply($x_value, $y_value);
}
$x1 = array_slice($x_value, $m);
$x0 = array_slice($x_value, 0, $m);
$y1 = array_slice($y_value, $m);
$y0 = array_slice($y_value, 0, $m);
$z2 = $this->_karatsuba($x1, $y1);
$z0 = $this->_karatsuba($x0, $y0);
$z1 = $this->_add($x1, false, $x0, false);
$temp = $this->_add($y1, false, $y0, false);
$z1 = $this->_karatsuba($z1[self::VALUE], $temp[self::VALUE]);
$temp = $this->_add($z2, false, $z0, false);
$z1 = $this->_subtract($z1, false, $temp[self::VALUE], false);
$z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
$z1[self::VALUE] = array_merge(array_fill(0, $m, 0),
$z1[self::VALUE]);
$xy = $this->_add($z2, false, $z1[self::VALUE],
$z1[self::SIGN]);
$xy = $this->_add($xy[self::VALUE], $xy[self::SIGN], $z0,
false);
return $xy[self::VALUE];
}
/**
* Performs squaring
*
* @param array $x
* @return array
* @access private
*/
function _square($x = false)
{
return count($x) < 2 * self::KARATSUBA_CUTOFF ?
$this->_trim($this->_baseSquare($x)) :
$this->_trim($this->_karatsubaSquare($x));
}
/**
* Performs traditional squaring on two BigIntegers
*
* Squaring can be done faster than multiplying a number by itself can
be. See
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7
HAC 14.2.4} /
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM
5.3} for more information.
*
* @param array $value
* @return array
* @access private
*/
function _baseSquare($value)
{
if (empty($value)) {
return array();
}
$square_value = $this->_array_repeat(0, 2 * count($value));
for ($i = 0, $max_index = count($value) - 1; $i <= $max_index;
++$i) {
$i2 = $i << 1;
$temp = $square_value[$i2] + $value[$i] * $value[$i];
$carry = self::$base === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$square_value[$i2] = (int) ($temp - self::$baseFull * $carry);
// 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) {
$temp = $square_value[$k] + 2 * $value[$j] * $value[$i] +
$carry;
$carry = self::$base === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$square_value[$k] = (int) ($temp - self::$baseFull *
$carry);
}
// the following line can yield values larger 2**15. at this
point, PHP should switch
// over to floats.
$square_value[$i + $max_index + 1] = $carry;
}
return $square_value;
}
/**
* Performs Karatsuba "squaring" on two BigIntegers
*
* See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm
Karatsuba algorithm} and
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM
5.3.4}.
*
* @param array $value
* @return array
* @access private
*/
function _karatsubaSquare($value)
{
$m = count($value) >> 1;
if ($m < self::KARATSUBA_CUTOFF) {
return $this->_baseSquare($value);
}
$x1 = array_slice($value, $m);
$x0 = array_slice($value, 0, $m);
$z2 = $this->_karatsubaSquare($x1);
$z0 = $this->_karatsubaSquare($x0);
$z1 = $this->_add($x1, false, $x0, false);
$z1 = $this->_karatsubaSquare($z1[self::VALUE]);
$temp = $this->_add($z2, false, $z0, false);
$z1 = $this->_subtract($z1, false, $temp[self::VALUE], false);
$z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
$z1[self::VALUE] = array_merge(array_fill(0, $m, 0),
$z1[self::VALUE]);
$xx = $this->_add($z2, false, $z1[self::VALUE],
$z1[self::SIGN]);
$xx = $this->_add($xx[self::VALUE], $xx[self::SIGN], $z0,
false);
return $xx[self::VALUE];
}
/**
* Divides two BigIntegers.
*
* Returns an array whose first element contains the quotient and whose
second element contains the
* "common residue". If the remainder would be positive, the
"common residue" and the remainder are the
* same. If the remainder would be negative, the "common
residue" is equal to the sum of the remainder
* and the divisor (basically, the "common residue" is the
first positive modulo).
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('10');
* $b = new \phpseclib\Math\BigInteger('20');
*
* list($quotient, $remainder) = $a->divide($b);
*
* echo $quotient->toString(); // outputs 0
* echo "\r\n";
* echo $remainder->toString(); // outputs 10
* ?>
* </code>
*
* @param \phpseclib\Math\BigInteger $y
* @return array
* @access public
* @internal This function is based off of {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}.
*/
function divide($y)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$quotient = new static();
$remainder = new static();
list($quotient->value, $remainder->value) =
gmp_div_qr($this->value, $y->value);
if (gmp_sign($remainder->value) < 0) {
$remainder->value = gmp_add($remainder->value,
gmp_abs($y->value));
}
return array($this->_normalize($quotient),
$this->_normalize($remainder));
case self::MODE_BCMATH:
$quotient = new static();
$remainder = new static();
$quotient->value = bcdiv($this->value, $y->value,
0);
$remainder->value = bcmod($this->value,
$y->value);
if ($remainder->value[0] == '-') {
$remainder->value = bcadd($remainder->value,
$y->value[0] == '-' ? substr($y->value, 1) : $y->value,
0);
}
return array($this->_normalize($quotient),
$this->_normalize($remainder));
}
if (count($y->value) == 1) {
list($q, $r) = $this->_divide_digit($this->value,
$y->value[0]);
$quotient = new static();
$remainder = new static();
$quotient->value = $q;
$remainder->value = array($r);
$quotient->is_negative = $this->is_negative !=
$y->is_negative;
return array($this->_normalize($quotient),
$this->_normalize($remainder));
}
static $zero;
if (!isset($zero)) {
$zero = new static();
}
$x = $this->copy();
$y = $y->copy();
$x_sign = $x->is_negative;
$y_sign = $y->is_negative;
$x->is_negative = $y->is_negative = false;
$diff = $x->compare($y);
if (!$diff) {
$temp = new static();
$temp->value = array(1);
$temp->is_negative = $x_sign != $y_sign;
return array($this->_normalize($temp),
$this->_normalize(new static()));
}
if ($diff < 0) {
// if $x is negative, "add" $y.
if ($x_sign) {
$x = $y->subtract($x);
}
return array($this->_normalize(new static()),
$this->_normalize($x));
}
// normalize $x and $y as described in HAC 14.23 / 14.24
$msb = $y->value[count($y->value) - 1];
for ($shift = 0; !($msb & self::$msb); ++$shift) {
$msb <<= 1;
}
$x->_lshift($shift);
$y->_lshift($shift);
$y_value = &$y->value;
$x_max = count($x->value) - 1;
$y_max = count($y->value) - 1;
$quotient = new static();
$quotient_value = &$quotient->value;
$quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1);
static $temp, $lhs, $rhs;
if (!isset($temp)) {
$temp = new static();
$lhs = new static();
$rhs = new static();
}
$temp_value = &$temp->value;
$rhs_value = &$rhs->value;
// $temp = $y << ($x_max - $y_max-1) in base 2**26
$temp_value = array_merge($this->_array_repeat(0, $x_max -
$y_max), $y_value);
while ($x->compare($temp) >= 0) {
// calculate the "common residue"
++$quotient_value[$x_max - $y_max];
$x = $x->subtract($temp);
$x_max = count($x->value) - 1;
}
for ($i = $x_max; $i >= $y_max + 1; --$i) {
$x_value = &$x->value;
$x_window = array(
isset($x_value[$i]) ? $x_value[$i] : 0,
isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0,
isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0
);
$y_window = array(
$y_value[$y_max],
($y_max > 0) ? $y_value[$y_max - 1] : 0
);
$q_index = $i - $y_max - 1;
if ($x_window[0] == $y_window[0]) {
$quotient_value[$q_index] = self::$maxDigit;
} else {
$quotient_value[$q_index] = $this->_safe_divide(
$x_window[0] * self::$baseFull + $x_window[1],
$y_window[0]
);
}
$temp_value = array($y_window[1], $y_window[0]);
$lhs->value = array($quotient_value[$q_index]);
$lhs = $lhs->multiply($temp);
$rhs_value = array($x_window[2], $x_window[1], $x_window[0]);
while ($lhs->compare($rhs) > 0) {
--$quotient_value[$q_index];
$lhs->value = array($quotient_value[$q_index]);
$lhs = $lhs->multiply($temp);
}
$adjust = $this->_array_repeat(0, $q_index);
$temp_value = array($quotient_value[$q_index]);
$temp = $temp->multiply($y);
$temp_value = &$temp->value;
if (count($temp_value)) {
$temp_value = array_merge($adjust, $temp_value);
}
$x = $x->subtract($temp);
if ($x->compare($zero) < 0) {
$temp_value = array_merge($adjust, $y_value);
$x = $x->add($temp);
--$quotient_value[$q_index];
}
$x_max = count($x_value) - 1;
}
// unnormalize the remainder
$x->_rshift($shift);
$quotient->is_negative = $x_sign != $y_sign;
// calculate the "common residue", if appropriate
if ($x_sign) {
$y->_rshift($shift);
$x = $y->subtract($x);
}
return array($this->_normalize($quotient),
$this->_normalize($x));
}
/**
* Divides a BigInteger by a regular integer
*
* abc / x = a00 / x + b0 / x + c / x
*
* @param array $dividend
* @param array $divisor
* @return array
* @access private
*/
function _divide_digit($dividend, $divisor)
{
$carry = 0;
$result = array();
for ($i = count($dividend) - 1; $i >= 0; --$i) {
$temp = self::$baseFull * $carry + $dividend[$i];
$result[$i] = $this->_safe_divide($temp, $divisor);
$carry = (int) ($temp - $divisor * $result[$i]);
}
return array($result, $carry);
}
/**
* Performs modular exponentiation.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('10');
* $b = new \phpseclib\Math\BigInteger('20');
* $c = new \phpseclib\Math\BigInteger('30');
*
* $c = $a->modPow($b, $c);
*
* echo $c->toString(); // outputs 10
* ?>
* </code>
*
* @param \phpseclib\Math\BigInteger $e
* @param \phpseclib\Math\BigInteger $n
* @return \phpseclib\Math\BigInteger
* @access public
* @internal The most naive approach to modular exponentiation has very
unreasonable requirements, and
* and although the approach involving repeated squaring does vastly
better, it, too, is impractical
* for our purposes. The reason being that division - by far the
most complicated and time-consuming
* of the basic operations (eg. +,-,*,/) - occurs multiple times
within it.
*
* Modular reductions resolve this issue. Although an individual
modular reduction takes more time
* then an individual division, when performed in succession (with
the same modulo), they're a lot faster.
*
* The two most commonly used modular reductions are Barrett and
Montgomery reduction. Montgomery reduction,
* although faster, only works when the gcd of the modulo and of the
base being used is 1. In RSA, when the
* base is a power of two, the modulo - a product of two primes - is
always going to have a gcd of 1 (because
* the product of two odd numbers is odd), but what about when RSA
isn't used?
*
* In contrast, Barrett reduction has no such constraint. As such,
some bigint implementations perform a
* Barrett reduction after every operation in the modpow function.
Others perform Barrett reductions when the
* modulo is even and Montgomery reductions when the modulo is odd.
BigInteger.java's modPow method, however,
* uses a trick involving the Chinese Remainder Theorem to factor
the even modulo into two numbers - one odd and
* the other, a power of two - and recombine them, later. This is
the method that this modPow function uses.
* {@link http://islab.oregonstate.edu/papers/j34monex.pdf
Montgomery Reduction with Even Modulus} elaborates.
*/
function modPow($e, $n)
{
$n = $this->bitmask !== false &&
$this->bitmask->compare($n) < 0 ? $this->bitmask :
$n->abs();
if ($e->compare(new static()) < 0) {
$e = $e->abs();
$temp = $this->modInverse($n);
if ($temp === false) {
return false;
}
return $this->_normalize($temp->modPow($e, $n));
}
if (MATH_BIGINTEGER_MODE == self::MODE_GMP) {
$temp = new static();
$temp->value = gmp_powm($this->value, $e->value,
$n->value);
return $this->_normalize($temp);
}
if ($this->compare(new static()) < 0 || $this->compare($n)
> 0) {
list(, $temp) = $this->divide($n);
return $temp->modPow($e, $n);
}
if (defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
$components = array(
'modulus' => $n->toBytes(true),
'publicExponent' => $e->toBytes(true)
);
$components = array(
'modulus' => pack('Ca*a*', 2,
$this->_encodeASN1Length(strlen($components['modulus'])),
$components['modulus']),
'publicExponent' => pack('Ca*a*', 2,
$this->_encodeASN1Length(strlen($components['publicExponent'])),
$components['publicExponent'])
);
$RSAPublicKey = pack(
'Ca*a*a*',
48,
$this->_encodeASN1Length(strlen($components['modulus']) +
strlen($components['publicExponent'])),
$components['modulus'],
$components['publicExponent']
);
$rsaOID = pack('H*',
'300d06092a864886f70d0101010500'); // hex version of
MA0GCSqGSIb3DQEBAQUA
$RSAPublicKey = chr(0) . $RSAPublicKey;
$RSAPublicKey = chr(3) .
$this->_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey;
$encapsulated = pack(
'Ca*a*',
48,
$this->_encodeASN1Length(strlen($rsaOID .
$RSAPublicKey)),
$rsaOID . $RSAPublicKey
);
$RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
chunk_split(base64_encode($encapsulated)) .
'-----END PUBLIC KEY-----';
$plaintext = str_pad($this->toBytes(),
strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT);
if (openssl_public_encrypt($plaintext, $result, $RSAPublicKey,
OPENSSL_NO_PADDING)) {
return new static($result, 256);
}
}
if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
$temp = new static();
$temp->value = bcpowmod($this->value, $e->value,
$n->value, 0);
return $this->_normalize($temp);
}
if (empty($e->value)) {
$temp = new static();
$temp->value = array(1);
return $this->_normalize($temp);
}
if ($e->value == array(1)) {
list(, $temp) = $this->divide($n);
return $this->_normalize($temp);
}
if ($e->value == array(2)) {
$temp = new static();
$temp->value = $this->_square($this->value);
list(, $temp) = $temp->divide($n);
return $this->_normalize($temp);
}
return $this->_normalize($this->_slidingWindow($e, $n,
self::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?
if ($n->value[0] & 1) {
return $this->_normalize($this->_slidingWindow($e, $n,
self::MONTGOMERY));
}
// if it's not, it's even
// find the lowest set bit (eg. the max pow of 2 that divides $n)
for ($i = 0; $i < count($n->value); ++$i) {
if ($n->value[$i]) {
$temp = decbin($n->value[$i]);
$j = strlen($temp) - strrpos($temp, '1') - 1;
$j+= 26 * $i;
break;
}
}
// at this point, 2^$j * $n/(2^$j) == $n
$mod1 = $n->copy();
$mod1->_rshift($j);
$mod2 = new static();
$mod2->value = array(1);
$mod2->_lshift($j);
$part1 = ($mod1->value != array(1)) ?
$this->_slidingWindow($e, $mod1, self::MONTGOMERY) : new static();
$part2 = $this->_slidingWindow($e, $mod2, self::POWEROF2);
$y1 = $mod2->modInverse($mod1);
$y2 = $mod1->modInverse($mod2);
$result = $part1->multiply($mod2);
$result = $result->multiply($y1);
$temp = $part2->multiply($mod1);
$temp = $temp->multiply($y2);
$result = $result->add($temp);
list(, $result) = $result->divide($n);
return $this->_normalize($result);
}
/**
* Performs modular exponentiation.
*
* Alias for modPow().
*
* @param \phpseclib\Math\BigInteger $e
* @param \phpseclib\Math\BigInteger $n
* @return \phpseclib\Math\BigInteger
* @access public
*/
function powMod($e, $n)
{
return $this->modPow($e, $n);
}
/**
* Sliding Window k-ary Modular Exponentiation
*
* Based on {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} /
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM
7.7}. In a departure from those algorithims,
* however, this function performs a modular reduction after every
multiplication and squaring operation.
* As such, this function has the same preconditions that the
reductions being used do.
*
* @param \phpseclib\Math\BigInteger $e
* @param \phpseclib\Math\BigInteger $n
* @param int $mode
* @return \phpseclib\Math\BigInteger
* @access private
*/
function _slidingWindow($e, $n, $mode)
{
static $window_ranges = array(7, 25, 81, 241, 673, 1793); // from
BigInteger.java's oddModPow function
//static $window_ranges = array(0, 7, 36, 140, 450, 1303, 3529); //
from MPM 7.3.1
$e_value = $e->value;
$e_length = count($e_value) - 1;
$e_bits = decbin($e_value[$e_length]);
for ($i = $e_length - 1; $i >= 0; --$i) {
$e_bits.= str_pad(decbin($e_value[$i]), self::$base,
'0', STR_PAD_LEFT);
}
$e_length = strlen($e_bits);
// calculate the appropriate window size.
// $window_size == 3 if $window_ranges is between 25 and 81, for
example.
for ($i = 0, $window_size = 1; $i < count($window_ranges)
&& $e_length > $window_ranges[$i]; ++$window_size, ++$i) {
}
$n_value = $n->value;
// precompute $this^0 through $this^$window_size
$powers = array();
$powers[1] = $this->_prepareReduce($this->value, $n_value,
$mode);
$powers[2] = $this->_squareReduce($powers[1], $n_value, $mode);
// we do every other number since substr($e_bits, $i, $j+1) (see
below) is supposed to end
// in a 1. ie. it's supposed to be odd.
$temp = 1 << ($window_size - 1);
for ($i = 1; $i < $temp; ++$i) {
$i2 = $i << 1;
$powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1],
$powers[2], $n_value, $mode);
}
$result = array(1);
$result = $this->_prepareReduce($result, $n_value, $mode);
for ($i = 0; $i < $e_length;) {
if (!$e_bits[$i]) {
$result = $this->_squareReduce($result, $n_value,
$mode);
++$i;
} else {
for ($j = $window_size - 1; $j > 0; --$j) {
if (!empty($e_bits[$i + $j])) {
break;
}
}
// eg. the length of substr($e_bits, $i, $j + 1)
for ($k = 0; $k <= $j; ++$k) {
$result = $this->_squareReduce($result, $n_value,
$mode);
}
$result = $this->_multiplyReduce($result,
$powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode);
$i += $j + 1;
}
}
$temp = new static();
$temp->value = $this->_reduce($result, $n_value, $mode);
return $temp;
}
/**
* Modular reduction
*
* For most $modes this will return the remainder.
*
* @see self::_slidingWindow()
* @access private
* @param array $x
* @param array $n
* @param int $mode
* @return array
*/
function _reduce($x, $n, $mode)
{
switch ($mode) {
case self::MONTGOMERY:
return $this->_montgomery($x, $n);
case self::BARRETT:
return $this->_barrett($x, $n);
case self::POWEROF2:
$lhs = new static();
$lhs->value = $x;
$rhs = new static();
$rhs->value = $n;
return $x->_mod2($n);
case self::CLASSIC:
$lhs = new static();
$lhs->value = $x;
$rhs = new static();
$rhs->value = $n;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
case self::NONE:
return $x;
default:
// an invalid $mode was provided
}
}
/**
* Modular reduction preperation
*
* @see self::_slidingWindow()
* @access private
* @param array $x
* @param array $n
* @param int $mode
* @return array
*/
function _prepareReduce($x, $n, $mode)
{
if ($mode == self::MONTGOMERY) {
return $this->_prepMontgomery($x, $n);
}
return $this->_reduce($x, $n, $mode);
}
/**
* Modular multiply
*
* @see self::_slidingWindow()
* @access private
* @param array $x
* @param array $y
* @param array $n
* @param int $mode
* @return array
*/
function _multiplyReduce($x, $y, $n, $mode)
{
if ($mode == self::MONTGOMERY) {
return $this->_montgomeryMultiply($x, $y, $n);
}
$temp = $this->_multiply($x, false, $y, false);
return $this->_reduce($temp[self::VALUE], $n, $mode);
}
/**
* Modular square
*
* @see self::_slidingWindow()
* @access private
* @param array $x
* @param array $n
* @param int $mode
* @return array
*/
function _squareReduce($x, $n, $mode)
{
if ($mode == self::MONTGOMERY) {
return $this->_montgomeryMultiply($x, $x, $n);
}
return $this->_reduce($this->_square($x), $n, $mode);
}
/**
* Modulos for Powers of Two
*
* Calculates $x%$n, where $n = 2**$e, for some $e. Since this is
basically the same as doing $x & ($n-1),
* we'll just use this function as a wrapper for doing that.
*
* @see self::_slidingWindow()
* @access private
* @param \phpseclib\Math\BigInteger $n
* @return \phpseclib\Math\BigInteger
*/
function _mod2($n)
{
$temp = new static();
$temp->value = array(1);
return $this->bitwise_and($n->subtract($temp));
}
/**
* Barrett Modular Reduction
*
* See {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3}
/
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM
6.2.5} for more information. Modified slightly,
* so as not to require negative numbers (initially, this script
didn't support negative numbers).
*
* Employs "folding", as described at
* {@link
http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66
thesis-149.pdf#page=66}. To quote from
* it, "the idea [behind folding] is to find a value x' such
that x (mod m) = x' (mod m), with x' being smaller than x."
*
* Unfortunately, the "Barrett Reduction with Folding"
algorithm described in thesis-149.pdf is not, as written, all that
* usable on account of (1) its not using reasonable radix points as
discussed in
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM
6.2.2} and (2) the fact that, even with reasonable
* radix points, it only works when there are an even number of digits
in the denominator. The reason for (2) is that
* (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even,
they're the same, but if x is odd, they're not. See the in-line
* comments for details.
*
* @see self::_slidingWindow()
* @access private
* @param array $n
* @param array $m
* @return array
*/
function _barrett($n, $m)
{
static $cache = array(
self::VARIABLE => array(),
self::DATA => array()
);
$m_length = count($m);
// if ($this->_compare($n, $this->_square($m)) >= 0) {
if (count($n) > 2 * $m_length) {
$lhs = new static();
$rhs = new static();
$lhs->value = $n;
$rhs->value = $m;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
}
// if (m.length >> 1) + 2 <= m.length then m is too small
and n can't be reduced
if ($m_length < 5) {
return $this->_regularBarrett($n, $m);
}
// n = 2 * m.length
if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]);
$cache[self::VARIABLE][] = $m;
$lhs = new static();
$lhs_value = &$lhs->value;
$lhs_value = $this->_array_repeat(0, $m_length + ($m_length
>> 1));
$lhs_value[] = 1;
$rhs = new static();
$rhs->value = $m;
list($u, $m1) = $lhs->divide($rhs);
$u = $u->value;
$m1 = $m1->value;
$cache[self::DATA][] = array(
'u' => $u, // m.length >> 1 (technically
(m.length >> 1) + 1)
'm1'=> $m1 // m.length
);
} else {
extract($cache[self::DATA][$key]);
}
$cutoff = $m_length + ($m_length >> 1);
$lsd = array_slice($n, 0, $cutoff); // m.length + (m.length
>> 1)
$msd = array_slice($n, $cutoff); // m.length >> 1
$lsd = $this->_trim($lsd);
$temp = $this->_multiply($msd, false, $m1, false);
$n = $this->_add($lsd, false, $temp[self::VALUE], false); //
m.length + (m.length >> 1) + 1
if ($m_length & 1) {
return $this->_regularBarrett($n[self::VALUE], $m);
}
// (m.length + (m.length >> 1) + 1) - (m.length - 1) ==
(m.length >> 1) + 2
$temp = array_slice($n[self::VALUE], $m_length - 1);
// if even: ((m.length >> 1) + 2) + (m.length >> 1) ==
m.length + 2
// if odd: ((m.length >> 1) + 2) + (m.length >> 1) ==
(m.length - 1) + 2 == m.length + 1
$temp = $this->_multiply($temp, false, $u, false);
// if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length
- (m.length >> 1) + 1
// if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length
- (m.length >> 1)
$temp = array_slice($temp[self::VALUE], ($m_length >> 1) +
1);
// if even: (m.length - (m.length >> 1) + 1) + m.length = 2 *
m.length - (m.length >> 1) + 1
// if odd: (m.length - (m.length >> 1)) + m.length = 2 *
m.length - (m.length >> 1)
$temp = $this->_multiply($temp, false, $m, false);
// at this point, if m had an odd number of digits, we'd be
subtracting a 2 * m.length - (m.length >> 1) digit
// number from a m.length + (m.length >> 1) + 1 digit number.
ie. there'd be an extra digit and the while loop
// following this comment would loop a lot (hence our calling
_regularBarrett() in that situation).
$result = $this->_subtract($n[self::VALUE], false,
$temp[self::VALUE], false);
while ($this->_compare($result[self::VALUE],
$result[self::SIGN], $m, false) >= 0) {
$result = $this->_subtract($result[self::VALUE],
$result[self::SIGN], $m, false);
}
return $result[self::VALUE];
}
/**
* (Regular) Barrett Modular Reduction
*
* For numbers with more than four digits BigInteger::_barrett() is
faster. The difference between that and this
* is that this function does not fold the denominator into a smaller
form.
*
* @see self::_slidingWindow()
* @access private
* @param array $x
* @param array $n
* @return array
*/
function _regularBarrett($x, $n)
{
static $cache = array(
self::VARIABLE => array(),
self::DATA => array()
);
$n_length = count($n);
if (count($x) > 2 * $n_length) {
$lhs = new static();
$rhs = new static();
$lhs->value = $x;
$rhs->value = $n;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
}
if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]);
$cache[self::VARIABLE][] = $n;
$lhs = new static();
$lhs_value = &$lhs->value;
$lhs_value = $this->_array_repeat(0, 2 * $n_length);
$lhs_value[] = 1;
$rhs = new static();
$rhs->value = $n;
list($temp, ) = $lhs->divide($rhs); // m.length
$cache[self::DATA][] = $temp->value;
}
// 2 * m.length - (m.length - 1) = m.length + 1
$temp = array_slice($x, $n_length - 1);
// (m.length + 1) + m.length = 2 * m.length + 1
$temp = $this->_multiply($temp, false, $cache[self::DATA][$key],
false);
// (2 * m.length + 1) - (m.length - 1) = m.length + 2
$temp = array_slice($temp[self::VALUE], $n_length + 1);
// m.length + 1
$result = array_slice($x, 0, $n_length + 1);
// m.length + 1
$temp = $this->_multiplyLower($temp, false, $n, false, $n_length
+ 1);
// $temp == array_slice($temp->_multiply($temp, false, $n,
false)->value, 0, $n_length + 1)
if ($this->_compare($result, false, $temp[self::VALUE],
$temp[self::SIGN]) < 0) {
$corrector_value = $this->_array_repeat(0, $n_length + 1);
$corrector_value[count($corrector_value)] = 1;
$result = $this->_add($result, false, $corrector_value,
false);
$result = $result[self::VALUE];
}
// at this point, we're subtracting a number with m.length + 1
digits from another number with m.length + 1 digits
$result = $this->_subtract($result, false, $temp[self::VALUE],
$temp[self::SIGN]);
while ($this->_compare($result[self::VALUE],
$result[self::SIGN], $n, false) > 0) {
$result = $this->_subtract($result[self::VALUE],
$result[self::SIGN], $n, false);
}
return $result[self::VALUE];
}
/**
* Performs long multiplication up to $stop digits
*
* If you're going to be doing array_slice($product->value, 0,
$stop), some cycles can be saved.
*
* @see self::_regularBarrett()
* @param array $x_value
* @param bool $x_negative
* @param array $y_value
* @param bool $y_negative
* @param int $stop
* @return array
* @access private
*/
function _multiplyLower($x_value, $x_negative, $y_value, $y_negative,
$stop)
{
$x_length = count($x_value);
$y_length = count($y_value);
if (!$x_length || !$y_length) { // a 0 is being multiplied
return array(
self::VALUE => array(),
self::SIGN => false
);
}
if ($x_length < $y_length) {
$temp = $x_value;
$x_value = $y_value;
$y_value = $temp;
$x_length = count($x_value);
$y_length = count($y_value);
}
$product_value = $this->_array_repeat(0, $x_length + $y_length);
// the following for loop could be removed if the for loop
following it
// (the one with nested for loops) initially set $i to 0, but
// doing so would also make the result in one set of unnecessary
adds,
// since on the outermost loops first pass, $product->value[$k]
is going
// to always be 0
$carry = 0;
for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i
$temp = $x_value[$j] * $y_value[0] + $carry; //
$product_value[$k] == 0
$carry = self::$base === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$product_value[$j] = (int) ($temp - self::$baseFull * $carry);
}
if ($j < $stop) {
$product_value[$j] = $carry;
}
// the above for loop is what the previous comment was talking
about. the
// following for loop is the "one with nested for loops"
for ($i = 1; $i < $y_length; ++$i) {
$carry = 0;
for ($j = 0, $k = $i; $j < $x_length && $k <
$stop; ++$j, ++$k) {
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] +
$carry;
$carry = self::$base === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$product_value[$k] = (int) ($temp - self::$baseFull *
$carry);
}
if ($k < $stop) {
$product_value[$k] = $carry;
}
}
return array(
self::VALUE => $this->_trim($product_value),
self::SIGN => $x_negative != $y_negative
);
}
/**
* Montgomery Modular Reduction
*
* ($x->_prepMontgomery($n))->_montgomery($n) yields $x % $n.
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=170 MPM
6.3} provides insights on how this can be
* improved upon (basically, by using the comba method). gcd($n, 2)
must be equal to one for this function
* to work correctly.
*
* @see self::_prepMontgomery()
* @see self::_slidingWindow()
* @access private
* @param array $x
* @param array $n
* @return array
*/
function _montgomery($x, $n)
{
static $cache = array(
self::VARIABLE => array(),
self::DATA => array()
);
if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]);
$cache[self::VARIABLE][] = $x;
$cache[self::DATA][] = $this->_modInverse67108864($n);
}
$k = count($n);
$result = array(self::VALUE => $x);
for ($i = 0; $i < $k; ++$i) {
$temp = $result[self::VALUE][$i] * $cache[self::DATA][$key];
$temp = $temp - self::$baseFull * (self::$base === 26 ?
intval($temp / 0x4000000) : ($temp >> 31));
$temp = $this->_regularMultiply(array($temp), $n);
$temp = array_merge($this->_array_repeat(0, $i), $temp);
$result = $this->_add($result[self::VALUE], false, $temp,
false);
}
$result[self::VALUE] = array_slice($result[self::VALUE], $k);
if ($this->_compare($result, false, $n, false) >= 0) {
$result = $this->_subtract($result[self::VALUE], false, $n,
false);
}
return $result[self::VALUE];
}
/**
* Montgomery Multiply
*
* Interleaves the montgomery reduction and long multiplication
algorithms together as described in
* {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
*
* @see self::_prepMontgomery()
* @see self::_montgomery()
* @access private
* @param array $x
* @param array $y
* @param array $m
* @return array
*/
function _montgomeryMultiply($x, $y, $m)
{
$temp = $this->_multiply($x, false, $y, false);
return $this->_montgomery($temp[self::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(
self::VARIABLE => array(),
self::DATA => array()
);
if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]);
$cache[self::VARIABLE][] = $m;
$cache[self::DATA][] = $this->_modInverse67108864($m);
}
$n = max(count($x), count($y), count($m));
$x = array_pad($x, $n, 0);
$y = array_pad($y, $n, 0);
$m = array_pad($m, $n, 0);
$a = array(self::VALUE => $this->_array_repeat(0, $n + 1));
for ($i = 0; $i < $n; ++$i) {
$temp = $a[self::VALUE][0] + $x[$i] * $y[0];
$temp = $temp - self::$baseFull * (self::$base === 26 ?
intval($temp / 0x4000000) : ($temp >> 31));
$temp = $temp * $cache[self::DATA][$key];
$temp = $temp - self::$baseFull * (self::$base === 26 ?
intval($temp / 0x4000000) : ($temp >> 31));
$temp =
$this->_add($this->_regularMultiply(array($x[$i]), $y), false,
$this->_regularMultiply(array($temp), $m), false);
$a = $this->_add($a[self::VALUE], false, $temp[self::VALUE],
false);
$a[self::VALUE] = array_slice($a[self::VALUE], 1);
}
if ($this->_compare($a[self::VALUE], false, $m, false) >= 0)
{
$a = $this->_subtract($a[self::VALUE], false, $m, false);
}
return $a[self::VALUE];
}
/**
* Prepare a number for use in Montgomery Modular Reductions
*
* @see self::_montgomery()
* @see self::_slidingWindow()
* @access private
* @param array $x
* @param array $n
* @return array
*/
function _prepMontgomery($x, $n)
{
$lhs = new static();
$lhs->value = array_merge($this->_array_repeat(0, count($n)),
$x);
$rhs = new static();
$rhs->value = $n;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
}
/**
* Modular Inverse of a number mod 2**26 (eg. 67108864)
*
* Based off of the bnpInvDigit function implemented and justified in
the following URL:
*
* {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js}
*
* The following URL provides more info:
*
* {@link
http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85}
*
* As for why we do all the bitmasking... strange things can happen
when converting from floats to ints. For
* instance, on some computers, var_dump((int) -4294967297) yields
int(-1) and on others, it yields
* int(-2147483648). To avoid problems stemming from this, we use
bitmasks to guarantee that ints aren't
* auto-converted to floats. The outermost bitmask is present because
without it, there's no guarantee that
* the "residue" returned would be the so-called "common
residue". We use fmod, in the last step, because the
* maximum possible $x is 26 bits and the maximum $result is 16 bits.
Thus, we have to be able to handle up to
* 40 bits, which only 64-bit floating points will support.
*
* Thanks to Pedro Gimeno Fortea for input!
*
* @see self::_montgomery()
* @access private
* @param array $x
* @return int
*/
function _modInverse67108864($x) // 2**26 == 67,108,864
{
$x = -$x[0];
$result = $x & 0x3; // x**-1 mod 2**2
$result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod
2**4
$result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF;
// x**-1 mod 2**8
$result = ($result * ((2 - ($x & 0xFFFF) * $result) &
0xFFFF)) & 0xFFFF; // x**-1 mod 2**16
$result = fmod($result * (2 - fmod($x * $result, self::$baseFull)),
self::$baseFull); // x**-1 mod 2**26
return $result & self::$maxDigit;
}
/**
* Calculates modular inverses.
*
* Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found
using modular inverses.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger(30);
* $b = new \phpseclib\Math\BigInteger(17);
*
* $c = $a->modInverse($b);
* echo $c->toString(); // outputs 4
*
* echo "\r\n";
*
* $d = $a->multiply($c);
* list(, $d) = $d->divide($b);
* echo $d; // outputs 1 (as per the definition of modular inverse)
* ?>
* </code>
*
* @param \phpseclib\Math\BigInteger $n
* @return \phpseclib\Math\BigInteger|false
* @access public
* @internal See {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64}
for more information.
*/
function modInverse($n)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_invert($this->value,
$n->value);
return ($temp->value === false) ? false :
$this->_normalize($temp);
}
static $zero, $one;
if (!isset($zero)) {
$zero = new static();
$one = new static(1);
}
// $x mod -$n == $x mod $n.
$n = $n->abs();
if ($this->compare($zero) < 0) {
$temp = $this->abs();
$temp = $temp->modInverse($n);
return $this->_normalize($n->subtract($temp));
}
extract($this->extendedGCD($n));
if (!$gcd->equals($one)) {
return false;
}
$x = $x->compare($zero) < 0 ? $x->add($n) : $x;
return $this->compare($zero) < 0 ?
$this->_normalize($n->subtract($x)) : $this->_normalize($x);
}
/**
* Calculates the greatest common divisor and Bezout's identity.
*
* Say you have 693 and 609. The GCD is 21. Bezout's identity
states that there exist integers x and y such that
* 693*x + 609*y == 21. In point of fact, there are actually an
infinite number of x and y combinations and which
* combination is returned is dependent upon which mode is in use. See
* {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity
Bezout's identity - Wikipedia} for more information.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger(693);
* $b = new \phpseclib\Math\BigInteger(609);
*
* extract($a->extendedGCD($b));
*
* echo $gcd->toString() . "\r\n"; // outputs 21
* echo $a->toString() * $x->toString() + $b->toString() *
$y->toString(); // outputs 21
* ?>
* </code>
*
* @param \phpseclib\Math\BigInteger $n
* @return \phpseclib\Math\BigInteger
* @access public
* @internal Calculates the GCD using the binary xGCD algorithim
described in
* {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}.
As the text above 14.61 notes,
* the more traditional algorithim requires "relatively costly
multiple-precision divisions".
*/
function extendedGCD($n)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
extract(gmp_gcdext($this->value, $n->value));
return array(
'gcd' => $this->_normalize(new
static($g)),
'x' => $this->_normalize(new
static($s)),
'y' => $this->_normalize(new
static($t))
);
case self::MODE_BCMATH:
// it might be faster to use the binary xGCD algorithim
here, as well, but (1) that algorithim works
// best when the base is a power of 2 and (2) i don't
think it'd make much difference, anyway. as is,
// the basic extended euclidean algorithim is what
we're using.
$u = $this->value;
$v = $n->value;
$a = '1';
$b = '0';
$c = '0';
$d = '1';
while (bccomp($v, '0', 0) != 0) {
$q = bcdiv($u, $v, 0);
$temp = $u;
$u = $v;
$v = bcsub($temp, bcmul($v, $q, 0), 0);
$temp = $a;
$a = $c;
$c = bcsub($temp, bcmul($a, $q, 0), 0);
$temp = $b;
$b = $d;
$d = bcsub($temp, bcmul($b, $q, 0), 0);
}
return array(
'gcd' => $this->_normalize(new
static($u)),
'x' => $this->_normalize(new
static($a)),
'y' => $this->_normalize(new
static($b))
);
}
$y = $n->copy();
$x = $this->copy();
$g = new static();
$g->value = array(1);
while (!(($x->value[0] & 1)|| ($y->value[0] & 1))) {
$x->_rshift(1);
$y->_rshift(1);
$g->_lshift(1);
}
$u = $x->copy();
$v = $y->copy();
$a = new static();
$b = new static();
$c = new static();
$d = new static();
$a->value = $d->value = $g->value = array(1);
$b->value = $c->value = array();
while (!empty($u->value)) {
while (!($u->value[0] & 1)) {
$u->_rshift(1);
if ((!empty($a->value) && ($a->value[0] &
1)) || (!empty($b->value) && ($b->value[0] & 1))) {
$a = $a->add($y);
$b = $b->subtract($x);
}
$a->_rshift(1);
$b->_rshift(1);
}
while (!($v->value[0] & 1)) {
$v->_rshift(1);
if ((!empty($d->value) && ($d->value[0] &
1)) || (!empty($c->value) && ($c->value[0] & 1))) {
$c = $c->add($y);
$d = $d->subtract($x);
}
$c->_rshift(1);
$d->_rshift(1);
}
if ($u->compare($v) >= 0) {
$u = $u->subtract($v);
$a = $a->subtract($c);
$b = $b->subtract($d);
} else {
$v = $v->subtract($u);
$c = $c->subtract($a);
$d = $d->subtract($b);
}
}
return array(
'gcd' =>
$this->_normalize($g->multiply($v)),
'x' => $this->_normalize($c),
'y' => $this->_normalize($d)
);
}
/**
* Calculates the greatest common divisor
*
* Say you have 693 and 609. The GCD is 21.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger(693);
* $b = new \phpseclib\Math\BigInteger(609);
*
* $gcd = a->extendedGCD($b);
*
* echo $gcd->toString() . "\r\n"; // outputs 21
* ?>
* </code>
*
* @param \phpseclib\Math\BigInteger $n
* @return \phpseclib\Math\BigInteger
* @access public
*/
function gcd($n)
{
extract($this->extendedGCD($n));
return $gcd;
}
/**
* Absolute value.
*
* @return \phpseclib\Math\BigInteger
* @access public
*/
function abs()
{
$temp = new static();
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp->value = gmp_abs($this->value);
break;
case self::MODE_BCMATH:
$temp->value = (bccomp($this->value, '0',
0) < 0) ? substr($this->value, 1) : $this->value;
break;
default:
$temp->value = $this->value;
}
return $temp;
}
/**
* Compares two numbers.
*
* Although one might think !$x->compare($y) means $x != $y, it, in
fact, means the opposite. The reason for this is
* demonstrated thusly:
*
* $x > $y: $x->compare($y) > 0
* $x < $y: $x->compare($y) < 0
* $x == $y: $x->compare($y) == 0
*
* Note how the same comparison operator is used. If you want to test
for equality, use $x->equals($y).
*
* @param \phpseclib\Math\BigInteger $y
* @return int that is < 0 if $this is less than $y; > 0 if $this
is greater than $y, and 0 if they are equal.
* @access public
* @see self::equals()
* @internal Could return $this->subtract($x), but that's not
as fast as what we do do.
*/
function compare($y)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$r = gmp_cmp($this->value, $y->value);
if ($r < -1) {
$r = -1;
}
if ($r > 1) {
$r = 1;
}
return $r;
case self::MODE_BCMATH:
return bccomp($this->value, $y->value, 0);
}
return $this->_compare($this->value, $this->is_negative,
$y->value, $y->is_negative);
}
/**
* Compares two numbers.
*
* @param array $x_value
* @param bool $x_negative
* @param array $y_value
* @param bool $y_negative
* @return int
* @see self::compare()
* @access private
*/
function _compare($x_value, $x_negative, $y_value, $y_negative)
{
if ($x_negative != $y_negative) {
return (!$x_negative && $y_negative) ? 1 : -1;
}
$result = $x_negative ? -1 : 1;
if (count($x_value) != count($y_value)) {
return (count($x_value) > count($y_value)) ? $result :
-$result;
}
$size = max(count($x_value), count($y_value));
$x_value = array_pad($x_value, $size, 0);
$y_value = array_pad($y_value, $size, 0);
for ($i = count($x_value) - 1; $i >= 0; --$i) {
if ($x_value[$i] != $y_value[$i]) {
return ($x_value[$i] > $y_value[$i]) ? $result :
-$result;
}
}
return 0;
}
/**
* Tests the equality of two numbers.
*
* If you need to see if one number is greater than or less than
another number, use BigInteger::compare()
*
* @param \phpseclib\Math\BigInteger $x
* @return bool
* @access public
* @see self::compare()
*/
function equals($x)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
return gmp_cmp($this->value, $x->value) == 0;
default:
return $this->value === $x->value &&
$this->is_negative == $x->is_negative;
}
}
/**
* Set Precision
*
* Some bitwise operations give different results depending on the
precision being used. Examples include left
* shift, not, and rotates.
*
* @param int $bits
* @access public
*/
function setPrecision($bits)
{
$this->precision = $bits;
if (MATH_BIGINTEGER_MODE != self::MODE_BCMATH) {
$this->bitmask = new static(chr((1 << ($bits &
0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256);
} else {
$this->bitmask = new static(bcpow('2', $bits, 0));
}
$temp = $this->_normalize($this);
$this->value = $temp->value;
}
/**
* Logical And
*
* @param \phpseclib\Math\BigInteger $x
* @access public
* @internal Implemented per a request by Lluis Pamies i Juarez
<lluis _a_ pamies.cat>
* @return \phpseclib\Math\BigInteger
*/
function bitwise_and($x)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_and($this->value, $x->value);
return $this->_normalize($temp);
case self::MODE_BCMATH:
$left = $this->toBytes();
$right = $x->toBytes();
$length = max(strlen($left), strlen($right));
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
return $this->_normalize(new static($left & $right,
256));
}
$result = $this->copy();
$length = min(count($x->value), count($this->value));
$result->value = array_slice($result->value, 0, $length);
for ($i = 0; $i < $length; ++$i) {
$result->value[$i]&= $x->value[$i];
}
return $this->_normalize($result);
}
/**
* Logical Or
*
* @param \phpseclib\Math\BigInteger $x
* @access public
* @internal Implemented per a request by Lluis Pamies i Juarez
<lluis _a_ pamies.cat>
* @return \phpseclib\Math\BigInteger
*/
function bitwise_or($x)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_or($this->value, $x->value);
return $this->_normalize($temp);
case self::MODE_BCMATH:
$left = $this->toBytes();
$right = $x->toBytes();
$length = max(strlen($left), strlen($right));
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
return $this->_normalize(new static($left | $right,
256));
}
$length = max(count($this->value), count($x->value));
$result = $this->copy();
$result->value = array_pad($result->value, $length, 0);
$x->value = array_pad($x->value, $length, 0);
for ($i = 0; $i < $length; ++$i) {
$result->value[$i]|= $x->value[$i];
}
return $this->_normalize($result);
}
/**
* Logical Exclusive-Or
*
* @param \phpseclib\Math\BigInteger $x
* @access public
* @internal Implemented per a request by Lluis Pamies i Juarez
<lluis _a_ pamies.cat>
* @return \phpseclib\Math\BigInteger
*/
function bitwise_xor($x)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_xor(gmp_abs($this->value),
gmp_abs($x->value));
return $this->_normalize($temp);
case self::MODE_BCMATH:
$left = $this->toBytes();
$right = $x->toBytes();
$length = max(strlen($left), strlen($right));
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
return $this->_normalize(new static($left ^ $right,
256));
}
$length = max(count($this->value), count($x->value));
$result = $this->copy();
$result->is_negative = false;
$result->value = array_pad($result->value, $length, 0);
$x->value = array_pad($x->value, $length, 0);
for ($i = 0; $i < $length; ++$i) {
$result->value[$i]^= $x->value[$i];
}
return $this->_normalize($result);
}
/**
* Logical Not
*
* @access public
* @internal Implemented per a request by Lluis Pamies i Juarez
<lluis _a_ pamies.cat>
* @return \phpseclib\Math\BigInteger
*/
function bitwise_not()
{
// calculuate "not" without regard to $this->precision
// (will always result in a smaller number. ie. ~1 isn't 1111
1110 - it's 0)
$temp = $this->toBytes();
if ($temp == '') {
return $this->_normalize(new static());
}
$pre_msb = decbin(ord($temp[0]));
$temp = ~$temp;
$msb = decbin(ord($temp[0]));
if (strlen($msb) == 8) {
$msb = substr($msb, strpos($msb, '0'));
}
$temp[0] = chr(bindec($msb));
// see if we need to add extra leading 1's
$current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8;
$new_bits = $this->precision - $current_bits;
if ($new_bits <= 0) {
return $this->_normalize(new static($temp, 256));
}
// generate as many leading 1's as we need to.
$leading_ones = chr((1 << ($new_bits & 0x7)) - 1) .
str_repeat(chr(0xFF), $new_bits >> 3);
$this->_base256_lshift($leading_ones, $current_bits);
$temp = str_pad($temp, strlen($leading_ones), chr(0),
STR_PAD_LEFT);
return $this->_normalize(new static($leading_ones | $temp,
256));
}
/**
* Logical Right Shift
*
* Shifts BigInteger's by $shift bits, effectively dividing by
2**$shift.
*
* @param int $shift
* @return \phpseclib\Math\BigInteger
* @access public
* @internal The only version that yields any speed increases is the
internal version.
*/
function bitwise_rightShift($shift)
{
$temp = new static();
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
static $two;
if (!isset($two)) {
$two = gmp_init('2');
}
$temp->value = gmp_div_q($this->value, gmp_pow($two,
$shift));
break;
case self::MODE_BCMATH:
$temp->value = bcdiv($this->value,
bcpow('2', $shift, 0), 0);
break;
default: // could just replace _lshift with this, but then all
_lshift() calls would need to be rewritten
// and I don't want to do that...
$temp->value = $this->value;
$temp->_rshift($shift);
}
return $this->_normalize($temp);
}
/**
* Logical Left Shift
*
* Shifts BigInteger's by $shift bits, effectively multiplying by
2**$shift.
*
* @param int $shift
* @return \phpseclib\Math\BigInteger
* @access public
* @internal The only version that yields any speed increases is the
internal version.
*/
function bitwise_leftShift($shift)
{
$temp = new static();
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
static $two;
if (!isset($two)) {
$two = gmp_init('2');
}
$temp->value = gmp_mul($this->value, gmp_pow($two,
$shift));
break;
case self::MODE_BCMATH:
$temp->value = bcmul($this->value,
bcpow('2', $shift, 0), 0);
break;
default: // could just replace _rshift with this, but then all
_lshift() calls would need to be rewritten
// and I don't want to do that...
$temp->value = $this->value;
$temp->_lshift($shift);
}
return $this->_normalize($temp);
}
/**
* Logical Left Rotate
*
* Instead of the top x bits being dropped they're appended to the
shifted bit string.
*
* @param int $shift
* @return \phpseclib\Math\BigInteger
* @access public
*/
function bitwise_leftRotate($shift)
{
$bits = $this->toBytes();
if ($this->precision > 0) {
$precision = $this->precision;
if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
$mask = $this->bitmask->subtract(new static(1));
$mask = $mask->toBytes();
} else {
$mask = $this->bitmask->toBytes();
}
} else {
$temp = ord($bits[0]);
for ($i = 0; $temp >> $i; ++$i) {
}
$precision = 8 * strlen($bits) - 8 + $i;
$mask = chr((1 << ($precision & 0x7)) - 1) .
str_repeat(chr(0xFF), $precision >> 3);
}
if ($shift < 0) {
$shift+= $precision;
}
$shift%= $precision;
if (!$shift) {
return $this->copy();
}
$left = $this->bitwise_leftShift($shift);
$left = $left->bitwise_and(new static($mask, 256));
$right = $this->bitwise_rightShift($precision - $shift);
$result = MATH_BIGINTEGER_MODE != self::MODE_BCMATH ?
$left->bitwise_or($right) : $left->add($right);
return $this->_normalize($result);
}
/**
* Logical Right Rotate
*
* Instead of the bottom x bits being dropped they're prepended to
the shifted bit string.
*
* @param int $shift
* @return \phpseclib\Math\BigInteger
* @access public
*/
function bitwise_rightRotate($shift)
{
return $this->bitwise_leftRotate(-$shift);
}
/**
* Generates a random BigInteger
*
* Byte length is equal to $length. Uses \phpseclib\Crypt\Random if
it's loaded and mt_rand if it's not.
*
* @param int $size
* @return \phpseclib\Math\BigInteger
* @access private
*/
function _random_number_helper($size)
{
if (class_exists('\phpseclib\Crypt\Random')) {
$random = Random::string($size);
} else {
$random = '';
if ($size & 1) {
$random.= chr(mt_rand(0, 255));
}
$blocks = $size >> 1;
for ($i = 0; $i < $blocks; ++$i) {
// mt_rand(-2147483648, 0x7FFFFFFF) always produces
-2147483648 on some systems
$random.= pack('n', mt_rand(0, 0xFFFF));
}
}
return new static($random, 256);
}
/**
* 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 \phpseclib\Math\BigInteger $arg1
* @param \phpseclib\Math\BigInteger $arg2
* @return \phpseclib\Math\BigInteger
* @access public
* @internal The API for creating random numbers used to be
$a->random($min, $max), where $a was a 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 {
$min = $arg1;
$max = $arg2;
}
$compare = $max->compare($min);
if (!$compare) {
return $this->_normalize($min);
} elseif ($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 static(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 static(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));
}
/**
* Generate a random prime number.
*
* 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.
*
* @param \phpseclib\Math\BigInteger $arg1
* @param \phpseclib\Math\BigInteger $arg2
* @param int $timeout
* @return Math_BigInteger|false
* @access public
* @internal See {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}.
*/
function randomPrime($arg1, $arg2 = false, $timeout = false)
{
if ($arg1 === false) {
return false;
}
if ($arg2 === false) {
$max = $arg1;
$min = $this;
} else {
$min = $arg1;
$max = $arg2;
}
$compare = $max->compare($min);
if (!$compare) {
return $min->isPrime() ? $min : false;
} elseif ($compare < 0) {
// if $min is bigger then $max, swap $min and $max
$temp = $max;
$max = $min;
$min = $temp;
}
static $one, $two;
if (!isset($one)) {
$one = new static(1);
$two = new static(2);
}
$start = time();
$x = $this->random($min, $max);
// gmp_nextprime() requires PHP 5 >= 5.2.0 per
<http://php.net/gmp-nextprime>.
if (MATH_BIGINTEGER_MODE == self::MODE_GMP &&
extension_loaded('gmp')) {
$p = new static();
$p->value = gmp_nextprime($x->value);
if ($p->compare($max) <= 0) {
return $p;
}
if (!$min->equals($x)) {
$x = $x->subtract($one);
}
return $x->randomPrime($min, $x);
}
if ($x->equals($two)) {
return $x;
}
$x->_make_odd();
if ($x->compare($max) > 0) {
// if $x > $max then $max is even and if $min == $max then
no prime number exists between the specified range
if ($min->equals($max)) {
return false;
}
$x = $min->copy();
$x->_make_odd();
}
$initial_x = $x->copy();
while (true) {
if ($timeout !== false && time() - $start >
$timeout) {
return false;
}
if ($x->isPrime()) {
return $x;
}
$x = $x->add($two);
if ($x->compare($max) > 0) {
$x = $min->copy();
if ($x->equals($two)) {
return $x;
}
$x->_make_odd();
}
if ($x->equals($initial_x)) {
return false;
}
}
}
/**
* Make the current number odd
*
* If the current number is odd it'll be unchanged. If it's
even, one will be added to it.
*
* @see self::randomPrime()
* @access private
*/
function _make_odd()
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
gmp_setbit($this->value, 0);
break;
case self::MODE_BCMATH:
if ($this->value[strlen($this->value) - 1] % 2 == 0)
{
$this->value = bcadd($this->value,
'1');
}
break;
default:
$this->value[0] |= 1;
}
}
/**
* 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
* $t parameter is distributability. BigInteger::randomPrime() can be
distributed across multiple pageloads
* on a website instead of just one.
*
* @param \phpseclib\Math\BigInteger $t
* @return bool
* @access public
* @internal Uses the
* {@link
http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test
Miller-Rabin primality test}. See
* {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24}.
*/
function isPrime($t = false)
{
$length = strlen($this->toBytes());
if (!$t) {
// see HAC 4.49 "Note (controlling the error
probability)"
// @codingStandardsIgnoreStart
if ($length >= 163) { $t = 2; } // floor(1300 / 8)
else if ($length >= 106) { $t = 3; } // floor( 850 / 8)
else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8)
else if ($length >= 68 ) { $t = 5; } // floor( 550 / 8)
else if ($length >= 56 ) { $t = 6; } // floor( 450 / 8)
else if ($length >= 50 ) { $t = 7; } // floor( 400 / 8)
else if ($length >= 43 ) { $t = 8; } // floor( 350 / 8)
else if ($length >= 37 ) { $t = 9; } // floor( 300 / 8)
else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8)
else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8)
else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8)
else { $t = 27; }
// @codingStandardsIgnoreEnd
}
// ie. gmp_testbit($this, 0)
// ie. isEven() or !isOdd()
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
return gmp_prob_prime($this->value, $t) != 0;
case self::MODE_BCMATH:
if ($this->value === '2') {
return true;
}
if ($this->value[strlen($this->value) - 1] % 2 == 0)
{
return false;
}
break;
default:
if ($this->value == array(2)) {
return true;
}
if (~$this->value[0] & 1) {
return false;
}
}
static $primes, $zero, $one, $two;
if (!isset($primes)) {
$primes = array(
3, 5, 7, 11, 13, 17, 19, 23, 29, 31,
37, 41, 43, 47, 53, 59,
61, 67, 71, 73, 79, 83, 89, 97, 101, 103,
107, 109, 113, 127, 131, 137,
139, 149, 151, 157, 163, 167, 173, 179, 181, 191,
193, 197, 199, 211, 223, 227,
229, 233, 239, 241, 251, 257, 263, 269, 271, 277,
281, 283, 293, 307, 311, 313,
317, 331, 337, 347, 349, 353, 359, 367, 373, 379,
383, 389, 397, 401, 409, 419,
421, 431, 433, 439, 443, 449, 457, 461, 463, 467,
479, 487, 491, 499, 503, 509,
521, 523, 541, 547, 557, 563, 569, 571, 577, 587,
593, 599, 601, 607, 613, 617,
619, 631, 641, 643, 647, 653, 659, 661, 673, 677,
683, 691, 701, 709, 719, 727,
733, 739, 743, 751, 757, 761, 769, 773, 787, 797,
809, 811, 821, 823, 827, 829,
839, 853, 857, 859, 863, 877, 881, 883, 887, 907,
911, 919, 929, 937, 941, 947,
953, 967, 971, 977, 983, 991, 997
);
if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
for ($i = 0; $i < count($primes); ++$i) {
$primes[$i] = new static($primes[$i]);
}
}
$zero = new static();
$one = new static(1);
$two = new static(2);
}
if ($this->equals($one)) {
return false;
}
// see HAC 4.4.1 "Random search for probable primes"
if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
foreach ($primes as $prime) {
list(, $r) = $this->divide($prime);
if ($r->equals($zero)) {
return $this->equals($prime);
}
}
} else {
$value = $this->value;
foreach ($primes as $prime) {
list(, $r) = $this->_divide_digit($value, $prime);
if (!$r) {
return count($value) == 1 && $value[0] ==
$prime;
}
}
}
$n = $this->copy();
$n_1 = $n->subtract($one);
$n_2 = $n->subtract($two);
$r = $n_1->copy();
$r_value = $r->value;
// ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n,
gmp_pow(gmp_init('2'), $s));
if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
$s = 0;
// if $n was 1, $r would be 0 and this would be an infinite
loop, hence our $this->equals($one) check earlier
while ($r->value[strlen($r->value) - 1] % 2 == 0) {
$r->value = bcdiv($r->value, '2', 0);
++$s;
}
} else {
for ($i = 0, $r_length = count($r_value); $i < $r_length;
++$i) {
$temp = ~$r_value[$i] & 0xFFFFFF;
for ($j = 1; ($temp >> $j) & 1; ++$j) {
}
if ($j != 25) {
break;
}
}
$s = 26 * $i + $j;
$r->_rshift($s);
}
for ($i = 0; $i < $t; ++$i) {
$a = $this->random($two, $n_2);
$y = $a->modPow($r, $n);
if (!$y->equals($one) && !$y->equals($n_1)) {
for ($j = 1; $j < $s && !$y->equals($n_1);
++$j) {
$y = $y->modPow($two, $n);
if ($y->equals($one)) {
return false;
}
}
if (!$y->equals($n_1)) {
return false;
}
}
}
return true;
}
/**
* Logical Left Shift
*
* Shifts BigInteger's by $shift bits.
*
* @param int $shift
* @access private
*/
function _lshift($shift)
{
if ($shift == 0) {
return;
}
$num_digits = (int) ($shift / self::$base);
$shift %= self::$base;
$shift = 1 << $shift;
$carry = 0;
for ($i = 0; $i < count($this->value); ++$i) {
$temp = $this->value[$i] * $shift + $carry;
$carry = self::$base === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$this->value[$i] = (int) ($temp - $carry * self::$baseFull);
}
if ($carry) {
$this->value[count($this->value)] = $carry;
}
while ($num_digits--) {
array_unshift($this->value, 0);
}
}
/**
* Logical Right Shift
*
* Shifts BigInteger's by $shift bits.
*
* @param int $shift
* @access private
*/
function _rshift($shift)
{
if ($shift == 0) {
return;
}
$num_digits = (int) ($shift / self::$base);
$shift %= self::$base;
$carry_shift = self::$base - $shift;
$carry_mask = (1 << $shift) - 1;
if ($num_digits) {
$this->value = array_slice($this->value, $num_digits);
}
$carry = 0;
for ($i = count($this->value) - 1; $i >= 0; --$i) {
$temp = $this->value[$i] >> $shift | $carry;
$carry = ($this->value[$i] & $carry_mask) <<
$carry_shift;
$this->value[$i] = $temp;
}
$this->value = $this->_trim($this->value);
}
/**
* Normalize
*
* Removes leading zeros and truncates (if necessary) to maintain the
appropriate precision
*
* @param \phpseclib\Math\BigInteger $result
* @return \phpseclib\Math\BigInteger
* @see self::_trim()
* @access private
*/
function _normalize($result)
{
$result->precision = $this->precision;
$result->bitmask = $this->bitmask;
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
if ($this->bitmask !== false) {
$flip = gmp_cmp($result->value, gmp_init(0)) < 0;
if ($flip) {
$result->value = gmp_neg($result->value);
}
$result->value = gmp_and($result->value,
$result->bitmask->value);
if ($flip) {
$result->value = gmp_neg($result->value);
}
}
return $result;
case self::MODE_BCMATH:
if (!empty($result->bitmask->value)) {
$result->value = bcmod($result->value,
$result->bitmask->value);
}
return $result;
}
$value = &$result->value;
if (!count($value)) {
$result->is_negative = false;
return $result;
}
$value = $this->_trim($value);
if (!empty($result->bitmask->value)) {
$length = min(count($value),
count($this->bitmask->value));
$value = array_slice($value, 0, $length);
for ($i = 0; $i < $length; ++$i) {
$value[$i] = $value[$i] &
$this->bitmask->value[$i];
}
}
return $result;
}
/**
* Trim
*
* Removes leading zeros
*
* @param array $value
* @return \phpseclib\Math\BigInteger
* @access private
*/
function _trim($value)
{
for ($i = count($value) - 1; $i >= 0; --$i) {
if ($value[$i]) {
break;
}
unset($value[$i]);
}
return $value;
}
/**
* Array Repeat
*
* @param array $input
* @param mixed $multiplier
* @return array
* @access private
*/
function _array_repeat($input, $multiplier)
{
return ($multiplier) ? array_fill(0, $multiplier, $input) :
array();
}
/**
* Logical Left Shift
*
* Shifts binary strings $shift bits, essentially multiplying by
2**$shift.
*
* @param string $x (by reference)
* @param int $shift
* @return string
* @access private
*/
function _base256_lshift(&$x, $shift)
{
if ($shift == 0) {
return;
}
$num_bytes = $shift >> 3; // eg. floor($shift/8)
$shift &= 7; // eg. $shift % 8
$carry = 0;
for ($i = strlen($x) - 1; $i >= 0; --$i) {
$temp = ord($x[$i]) << $shift | $carry;
$x[$i] = chr($temp);
$carry = $temp >> 8;
}
$carry = ($carry != 0) ? chr($carry) : '';
$x = $carry . $x . str_repeat(chr(0), $num_bytes);
}
/**
* Logical Right Shift
*
* Shifts binary strings $shift bits, essentially dividing by 2**$shift
and returning the remainder.
*
* @param string $x (by referenc)
* @param int $shift
* @return string
* @access private
*/
function _base256_rshift(&$x, $shift)
{
if ($shift == 0) {
$x = ltrim($x, chr(0));
return '';
}
$num_bytes = $shift >> 3; // eg. floor($shift/8)
$shift &= 7; // eg. $shift % 8
$remainder = '';
if ($num_bytes) {
$start = $num_bytes > strlen($x) ? -strlen($x) :
-$num_bytes;
$remainder = substr($x, $start);
$x = substr($x, 0, -$num_bytes);
}
$carry = 0;
$carry_shift = 8 - $shift;
for ($i = 0; $i < strlen($x); ++$i) {
$temp = (ord($x[$i]) >> $shift) | $carry;
$carry = (ord($x[$i]) << $carry_shift) & 0xFF;
$x[$i] = chr($temp);
}
$x = ltrim($x, chr(0));
$remainder = chr($carry >> $carry_shift) . $remainder;
return ltrim($remainder, chr(0));
}
// one quirk about how the following functions are implemented is that
PHP defines N to be an unsigned long
// at 32-bits, while java's longs are 64-bits.
/**
* Converts 32-bit integers to bytes.
*
* @param int $x
* @return string
* @access private
*/
function _int2bytes($x)
{
return ltrim(pack('N', $x), chr(0));
}
/**
* Converts bytes to 32-bit integers
*
* @param string $x
* @return int
* @access private
*/
function _bytes2int($x)
{
$temp = unpack('Nint', str_pad($x, 4, chr(0),
STR_PAD_LEFT));
return $temp['int'];
}
/**
* DER-encode an integer
*
* The ability to DER-encode integers is needed to create RSA public
keys for use with OpenSSL
*
* @see self::modPow()
* @access private
* @param int $length
* @return string
*/
function _encodeASN1Length($length)
{
if ($length <= 0x7F) {
return chr($length);
}
$temp = ltrim(pack('N', $length), chr(0));
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 int $x
* @param int $y
* @return int
*/
function _safe_divide($x, $y)
{
if (self::$base === 26) {
return (int) ($x / $y);
}
// self::$base === 31
return ($x - ($x % $y)) / $y;
}
}
vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php000064400000021777151156520650015644
0ustar00<?php
/**
* Pure-PHP implementation of SCP.
*
* PHP version 5
*
* The API for this library is modeled after the API from PHP's {@link
http://php.net/book.ftp FTP extension}.
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $ssh = new \phpseclib\Net\SSH2('www.domain.tld');
* if (!$ssh->login('username', 'password')) {
* exit('bad login');
* }
* $scp = new \phpseclib\Net\SCP($ssh);
*
* $scp->put('abcd', str_repeat('x', 1024*1024));
* ?>
* </code>
*
* @category Net
* @package SCP
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2010 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Net;
/**
* Pure-PHP implementations of SCP.
*
* @package SCP
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class SCP
{
/**#@+
* @access public
* @see \phpseclib\Net\SCP::put()
*/
/**
* Reads data from a local file.
*/
const SOURCE_LOCAL_FILE = 1;
/**
* Reads data from a string.
*/
const SOURCE_STRING = 2;
/**#@-*/
/**#@+
* @access private
* @see \phpseclib\Net\SCP::_send()
* @see \phpseclib\Net\SCP::_receive()
*/
/**
* SSH1 is being used.
*/
const MODE_SSH1 = 1;
/**
* SSH2 is being used.
*/
const MODE_SSH2 = 2;
/**#@-*/
/**
* SSH Object
*
* @var object
* @access private
*/
var $ssh;
/**
* Packet Size
*
* @var int
* @access private
*/
var $packet_size;
/**
* Mode
*
* @var int
* @access private
*/
var $mode;
/**
* Default Constructor.
*
* Connects to an SSH server
*
* @param \phpseclib\Net\SSH1|\phpseclib\Net\SSH2 $ssh
* @return \phpseclib\Net\SCP
* @access public
*/
function __construct($ssh)
{
if ($ssh instanceof SSH2) {
$this->mode = self::MODE_SSH2;
} elseif ($ssh instanceof SSH1) {
$this->packet_size = 50000;
$this->mode = self::MODE_SSH1;
} else {
return;
}
$this->ssh = $ssh;
}
/**
* Uploads a file to the SCP server.
*
* By default, \phpseclib\Net\SCP::put() does not read from the local
filesystem. $data is dumped directly into $remote_file.
* So, for example, if you set $data to 'filename.ext' and
then do \phpseclib\Net\SCP::get(), you will get a file, twelve bytes
* long, containing 'filename.ext' as its contents.
*
* Setting $mode to self::SOURCE_LOCAL_FILE will change the above
behavior. With self::SOURCE_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
* large $remote_file will be, as well.
*
* Currently, only binary mode is supported. As such, if the line
endings need to be adjusted, you will need to take
* care of that, yourself.
*
* @param string $remote_file
* @param string $data
* @param int $mode
* @param callable $callback
* @return bool
* @access public
*/
function put($remote_file, $data, $mode = self::SOURCE_STRING,
$callback = null)
{
if (!isset($this->ssh)) {
return false;
}
if (empty($remote_file)) {
user_error('remote_file cannot be blank',
E_USER_NOTICE);
return false;
}
if (!$this->ssh->exec('scp -t ' .
escapeshellarg($remote_file), false)) { // -t = to
return false;
}
$temp = $this->_receive();
if ($temp !== chr(0)) {
return false;
}
if ($this->mode == self::MODE_SSH2) {
$this->packet_size =
$this->ssh->packet_size_client_to_server[SSH2::CHANNEL_EXEC] - 4;
}
$remote_file = basename($remote_file);
if ($mode == self::SOURCE_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) {
return false;
}
$size = filesize($data);
}
$this->_send('C0644 ' . $size . ' ' .
$remote_file . "\n");
$temp = $this->_receive();
if ($temp !== chr(0)) {
return false;
}
$sent = 0;
while ($sent < $size) {
$temp = $mode & self::SOURCE_STRING ? substr($data, $sent,
$this->packet_size) : fread($fp, $this->packet_size);
$this->_send($temp);
$sent+= strlen($temp);
if (is_callable($callback)) {
call_user_func($callback, $sent);
}
}
$this->_close();
if ($mode != self::SOURCE_STRING) {
fclose($fp);
}
return true;
}
/**
* Downloads a file from the SCP server.
*
* Returns a string containing the contents of $remote_file if
$local_file is left undefined or a boolean false if
* the operation was unsuccessful. If $local_file is defined, returns
true or false depending on the success of the
* operation
*
* @param string $remote_file
* @param string $local_file
* @return mixed
* @access public
*/
function get($remote_file, $local_file = false)
{
if (!isset($this->ssh)) {
return false;
}
if (!$this->ssh->exec('scp -f ' .
escapeshellarg($remote_file), false)) { // -f = from
return false;
}
$this->_send("\0");
if (!preg_match('#(?<perms>[^ ]+) (?<size>\d+)
(?<name>.+)#', rtrim($this->_receive()), $info)) {
return false;
}
$this->_send("\0");
$size = 0;
if ($local_file !== false) {
$fp = @fopen($local_file, 'wb');
if (!$fp) {
return false;
}
}
$content = '';
while ($size < $info['size']) {
$data = $this->_receive();
// SCP usually seems to split stuff out into 16k chunks
$size+= strlen($data);
if ($local_file === false) {
$content.= $data;
} else {
fputs($fp, $data);
}
}
$this->_close();
if ($local_file !== false) {
fclose($fp);
return true;
}
return $content;
}
/**
* Sends a packet to an SSH server
*
* @param string $data
* @access private
*/
function _send($data)
{
switch ($this->mode) {
case self::MODE_SSH2:
$this->ssh->_send_channel_packet(SSH2::CHANNEL_EXEC,
$data);
break;
case self::MODE_SSH1:
$data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA,
strlen($data), $data);
$this->ssh->_send_binary_packet($data);
}
}
/**
* Receives a packet from an SSH server
*
* @return string
* @access private
*/
function _receive()
{
switch ($this->mode) {
case self::MODE_SSH2:
return
$this->ssh->_get_channel_packet(SSH2::CHANNEL_EXEC, true);
case self::MODE_SSH1:
if (!$this->ssh->bitmap) {
return false;
}
while (true) {
$response = $this->ssh->_get_binary_packet();
switch ($response[SSH1::RESPONSE_TYPE]) {
case NET_SSH1_SMSG_STDOUT_DATA:
if (strlen($response[SSH1::RESPONSE_DATA]) <
4) {
return false;
}
extract(unpack('Nlength',
$response[SSH1::RESPONSE_DATA]));
return
$this->ssh->_string_shift($response[SSH1::RESPONSE_DATA], $length);
case NET_SSH1_SMSG_STDERR_DATA:
break;
case NET_SSH1_SMSG_EXITSTATUS:
$this->ssh->_send_binary_packet(chr(NET_SSH1_CMSG_EXIT_CONFIRMATION));
fclose($this->ssh->fsock);
$this->ssh->bitmap = 0;
return false;
default:
user_error('Unknown packet received',
E_USER_NOTICE);
return false;
}
}
}
}
/**
* Closes the connection to an SSH server
*
* @access private
*/
function _close()
{
switch ($this->mode) {
case self::MODE_SSH2:
$this->ssh->_close_channel(SSH2::CHANNEL_EXEC, true);
break;
case self::MODE_SSH1:
$this->ssh->disconnect();
}
}
}
vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php000064400000052560151156520650017220
0ustar00<?php
/**
* SFTP Stream Wrapper
*
* Creates an sftp:// protocol handler that can be used with, for example,
fopen(), dir(), etc.
*
* PHP version 5
*
* @category Net
* @package SFTP
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2013 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Net\SFTP;
use phpseclib\Crypt\RSA;
use phpseclib\Net\SFTP;
/**
* SFTP Stream Wrapper
*
* @package SFTP
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class Stream
{
/**
* SFTP instances
*
* Rather than re-create the connection we re-use instances if possible
*
* @var array
*/
static $instances;
/**
* SFTP instance
*
* @var object
* @access private
*/
var $sftp;
/**
* Path
*
* @var string
* @access private
*/
var $path;
/**
* Mode
*
* @var string
* @access private
*/
var $mode;
/**
* Position
*
* @var int
* @access private
*/
var $pos;
/**
* Size
*
* @var int
* @access private
*/
var $size;
/**
* Directory entries
*
* @var array
* @access private
*/
var $entries;
/**
* EOF flag
*
* @var bool
* @access private
*/
var $eof;
/**
* Context resource
*
* Technically this needs to be publically accessible so PHP can set it
directly
*
* @var resource
* @access public
*/
var $context;
/**
* Notification callback function
*
* @var callable
* @access public
*/
var $notification;
/**
* Registers this class as a URL wrapper.
*
* @param string $protocol The wrapper name to be registered.
* @return bool True on success, false otherwise.
* @access public
*/
static function register($protocol = 'sftp')
{
if (in_array($protocol, stream_get_wrappers(), true)) {
return false;
}
return stream_wrapper_register($protocol, get_called_class());
}
/**
* The Constructor
*
* @access public
*/
function __construct()
{
if (defined('NET_SFTP_STREAM_LOGGING')) {
echo "__construct()\r\n";
}
}
/**
* Path Parser
*
* Extract a path from a URI and actually connect to an SSH server if
appropriate
*
* If "notification" is set as a context parameter the
message code for successful login is
* NET_SSH2_MSG_USERAUTH_SUCCESS. For a failed login it's
NET_SSH2_MSG_USERAUTH_FAILURE.
*
* @param string $path
* @return string
* @access private
*/
function _parse_path($path)
{
$orig = $path;
extract(parse_url($path) + array('port' => 22));
if (isset($query)) {
$path.= '?' . $query;
} elseif (preg_match('/(\?|\?#)$/', $orig)) {
$path.= '?';
}
if (isset($fragment)) {
$path.= '#' . $fragment;
} elseif ($orig[strlen($orig) - 1] == '#') {
$path.= '#';
}
if (!isset($host)) {
return false;
}
if (isset($this->context)) {
$context = stream_context_get_params($this->context);
if (isset($context['notification'])) {
$this->notification =
$context['notification'];
}
}
if ($host[0] == '$') {
$host = substr($host, 1);
global ${$host};
if (($$host instanceof SFTP) === false) {
return false;
}
$this->sftp = $$host;
} else {
if (isset($this->context)) {
$context = stream_context_get_options($this->context);
}
if (isset($context[$scheme]['session'])) {
$sftp = $context[$scheme]['session'];
}
if (isset($context[$scheme]['sftp'])) {
$sftp = $context[$scheme]['sftp'];
}
if (isset($sftp) && $sftp instanceof SFTP) {
$this->sftp = $sftp;
return $path;
}
if (isset($context[$scheme]['username'])) {
$user = $context[$scheme]['username'];
}
if (isset($context[$scheme]['password'])) {
$pass = $context[$scheme]['password'];
}
if (isset($context[$scheme]['privkey']) &&
$context[$scheme]['privkey'] instanceof RSA) {
$pass = $context[$scheme]['privkey'];
}
if (!isset($user) || !isset($pass)) {
return false;
}
// casting $pass to a string is necessary in the event that
it's a \phpseclib\Crypt\RSA object
if (isset(self::$instances[$host][$port][$user][(string)
$pass])) {
$this->sftp =
self::$instances[$host][$port][$user][(string) $pass];
} else {
$this->sftp = new SFTP($host, $port);
$this->sftp->disableStatCache();
if (isset($this->notification) &&
is_callable($this->notification)) {
/* if !is_callable($this->notification) we could do
this:
user_error('fopen(): failed to call user
notifier', E_USER_WARNING);
the ftp wrapper gives errors like that when the
notifier isn't callable.
i've opted not to do that, however, since the
ftp wrapper gives the line
on which the fopen occurred as the line number - not
the line that the
user_error is on.
*/
call_user_func($this->notification,
STREAM_NOTIFY_CONNECT, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0);
call_user_func($this->notification,
STREAM_NOTIFY_AUTH_REQUIRED, STREAM_NOTIFY_SEVERITY_INFO, '', 0,
0, 0);
if (!$this->sftp->login($user, $pass)) {
call_user_func($this->notification,
STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_ERR, 'Login
Failure', NET_SSH2_MSG_USERAUTH_FAILURE, 0, 0);
return false;
}
call_user_func($this->notification,
STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_INFO, 'Login
Success', NET_SSH2_MSG_USERAUTH_SUCCESS, 0, 0);
} else {
if (!$this->sftp->login($user, $pass)) {
return false;
}
}
self::$instances[$host][$port][$user][(string) $pass] =
$this->sftp;
}
}
return $path;
}
/**
* Opens file or URL
*
* @param string $path
* @param string $mode
* @param int $options
* @param string $opened_path
* @return bool
* @access public
*/
function _stream_open($path, $mode, $options, &$opened_path)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}
$this->path = $path;
$this->size = $this->sftp->size($path);
$this->mode = preg_replace('#[bt]$#', '',
$mode);
$this->eof = false;
if ($this->size === false) {
if ($this->mode[0] == 'r') {
return false;
} else {
$this->sftp->touch($path);
$this->size = 0;
}
} else {
switch ($this->mode[0]) {
case 'x':
return false;
case 'w':
$this->sftp->truncate($path, 0);
$this->size = 0;
}
}
$this->pos = $this->mode[0] != 'a' ? 0 :
$this->size;
return true;
}
/**
* Read from stream
*
* @param int $count
* @return mixed
* @access public
*/
function _stream_read($count)
{
switch ($this->mode) {
case 'w':
case 'a':
case 'x':
case 'c':
return false;
}
// commented out because some files - eg. /dev/urandom - will say
their size is 0 when in fact it's kinda infinite
//if ($this->pos >= $this->size) {
// $this->eof = true;
// return false;
//}
$result = $this->sftp->get($this->path, false,
$this->pos, $count);
if (isset($this->notification) &&
is_callable($this->notification)) {
if ($result === false) {
call_user_func($this->notification,
STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR,
$this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0);
return 0;
}
// seems that PHP calls stream_read in 8k chunks
call_user_func($this->notification, STREAM_NOTIFY_PROGRESS,
STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($result),
$this->size);
}
if (empty($result)) { // ie. false or empty string
$this->eof = true;
return false;
}
$this->pos+= strlen($result);
return $result;
}
/**
* Write to stream
*
* @param string $data
* @return mixed
* @access public
*/
function _stream_write($data)
{
switch ($this->mode) {
case 'r':
return false;
}
$result = $this->sftp->put($this->path, $data,
SFTP::SOURCE_STRING, $this->pos);
if (isset($this->notification) &&
is_callable($this->notification)) {
if (!$result) {
call_user_func($this->notification,
STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR,
$this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0);
return 0;
}
// seems that PHP splits up strings into 8k blocks before
calling stream_write
call_user_func($this->notification, STREAM_NOTIFY_PROGRESS,
STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($data),
strlen($data));
}
if ($result === false) {
return false;
}
$this->pos+= strlen($data);
if ($this->pos > $this->size) {
$this->size = $this->pos;
}
$this->eof = false;
return strlen($data);
}
/**
* Retrieve the current position of a stream
*
* @return int
* @access public
*/
function _stream_tell()
{
return $this->pos;
}
/**
* Tests for end-of-file on a file pointer
*
* In my testing there are four classes functions that normally effect
the pointer:
* fseek, fputs / fwrite, fgets / fread and ftruncate.
*
* Only fgets / fread, however, results in feof() returning true. do
fputs($fp, 'aaa') on a blank file and feof()
* will return false. do fread($fp, 1) and feof() will then return
true. do fseek($fp, 10) on ablank file and feof()
* will return false. do fread($fp, 1) and feof() will then return
true.
*
* @return bool
* @access public
*/
function _stream_eof()
{
return $this->eof;
}
/**
* Seeks to specific location in a stream
*
* @param int $offset
* @param int $whence
* @return bool
* @access public
*/
function _stream_seek($offset, $whence)
{
switch ($whence) {
case SEEK_SET:
if ($offset < 0) {
return false;
}
break;
case SEEK_CUR:
$offset+= $this->pos;
break;
case SEEK_END:
$offset+= $this->size;
}
$this->pos = $offset;
$this->eof = false;
return true;
}
/**
* Change stream options
*
* @param string $path
* @param int $option
* @param mixed $var
* @return bool
* @access public
*/
function _stream_metadata($path, $option, $var)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}
// stream_metadata was introduced in PHP 5.4.0 but as of 5.4.11 the
constants haven't been defined
// see http://www.php.net/streamwrapper.stream-metadata and
https://bugs.php.net/64246
// and
https://github.com/php/php-src/blob/master/main/php_streams.h#L592
switch ($option) {
case 1: // PHP_STREAM_META_TOUCH
$time = isset($var[0]) ? $var[0] : null;
$atime = isset($var[1]) ? $var[1] : null;
return $this->sftp->touch($path, $time, $atime);
case 2: // PHP_STREAM_OWNER_NAME
case 3: // PHP_STREAM_GROUP_NAME
return false;
case 4: // PHP_STREAM_META_OWNER
return $this->sftp->chown($path, $var);
case 5: // PHP_STREAM_META_GROUP
return $this->sftp->chgrp($path, $var);
case 6: // PHP_STREAM_META_ACCESS
return $this->sftp->chmod($path, $var) !== false;
}
}
/**
* Retrieve the underlaying resource
*
* @param int $cast_as
* @return resource
* @access public
*/
function _stream_cast($cast_as)
{
return $this->sftp->fsock;
}
/**
* Advisory file locking
*
* @param int $operation
* @return bool
* @access public
*/
function _stream_lock($operation)
{
return false;
}
/**
* Renames a file or directory
*
* Attempts to rename oldname to newname, moving it between directories
if necessary.
* If newname exists, it will be overwritten. This is a departure from
what \phpseclib\Net\SFTP
* does.
*
* @param string $path_from
* @param string $path_to
* @return bool
* @access public
*/
function _rename($path_from, $path_to)
{
$path1 = parse_url($path_from);
$path2 = parse_url($path_to);
unset($path1['path'], $path2['path']);
if ($path1 != $path2) {
return false;
}
$path_from = $this->_parse_path($path_from);
$path_to = parse_url($path_to);
if ($path_from === false) {
return false;
}
$path_to = $path_to['path']; // the $component part of
parse_url() was added in PHP 5.1.2
// "It is an error if there already exists a file with the
name specified by newpath."
// --
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.5
if (!$this->sftp->rename($path_from, $path_to)) {
if ($this->sftp->stat($path_to)) {
return $this->sftp->delete($path_to, true) &&
$this->sftp->rename($path_from, $path_to);
}
return false;
}
return true;
}
/**
* Open directory handle
*
* 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.
*
* 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 int $options
* @return bool
* @access public
*/
function _dir_opendir($path, $options)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}
$this->pos = 0;
$this->entries = $this->sftp->nlist($path);
return $this->entries !== false;
}
/**
* Read entry from directory handle
*
* @return mixed
* @access public
*/
function _dir_readdir()
{
if (isset($this->entries[$this->pos])) {
return $this->entries[$this->pos++];
}
return false;
}
/**
* Rewind directory handle
*
* @return bool
* @access public
*/
function _dir_rewinddir()
{
$this->pos = 0;
return true;
}
/**
* Close directory handle
*
* @return bool
* @access public
*/
function _dir_closedir()
{
return true;
}
/**
* Create a directory
*
* Only valid $options is STREAM_MKDIR_RECURSIVE
*
* @param string $path
* @param int $mode
* @param int $options
* @return bool
* @access public
*/
function _mkdir($path, $mode, $options)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}
return $this->sftp->mkdir($path, $mode, $options &
STREAM_MKDIR_RECURSIVE);
}
/**
* Removes a directory
*
* Only valid $options is STREAM_MKDIR_RECURSIVE per
<http://php.net/streamwrapper.rmdir>, however,
* <http://php.net/rmdir> does not have a $recursive parameter
as mkdir() does so I don't know how
* STREAM_MKDIR_RECURSIVE is supposed to be set. Also, when I try it
out with rmdir() I get 8 as
* $options. What does 8 correspond to?
*
* @param string $path
* @param int $options
* @return bool
* @access public
*/
function _rmdir($path, $options)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}
return $this->sftp->rmdir($path);
}
/**
* Flushes the output
*
* See <http://php.net/fflush>. Always returns true because
\phpseclib\Net\SFTP doesn't cache stuff before writing
*
* @return bool
* @access public
*/
function _stream_flush()
{
return true;
}
/**
* Retrieve information about a file resource
*
* @return mixed
* @access public
*/
function _stream_stat()
{
$results = $this->sftp->stat($this->path);
if ($results === false) {
return false;
}
return $results;
}
/**
* Delete a file
*
* @param string $path
* @return bool
* @access public
*/
function _unlink($path)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}
return $this->sftp->delete($path, false);
}
/**
* Retrieve information about a file
*
* Ignores the STREAM_URL_STAT_QUIET flag because the entirety of
\phpseclib\Net\SFTP\Stream is quiet by default
* might be worthwhile to reconstruct bits 12-16 (ie. the file type) if
mode doesn't have them but we'll
* cross that bridge when and if it's reached
*
* @param string $path
* @param int $flags
* @return mixed
* @access public
*/
function _url_stat($path, $flags)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}
$results = $flags & STREAM_URL_STAT_LINK ?
$this->sftp->lstat($path) : $this->sftp->stat($path);
if ($results === false) {
return false;
}
return $results;
}
/**
* Truncate stream
*
* @param int $new_size
* @return bool
* @access public
*/
function _stream_truncate($new_size)
{
if (!$this->sftp->truncate($this->path, $new_size)) {
return false;
}
$this->eof = false;
$this->size = $new_size;
return true;
}
/**
* Change stream options
*
* STREAM_OPTION_WRITE_BUFFER isn't supported for the same reason
stream_flush isn't.
* The other two aren't supported because of limitations in
\phpseclib\Net\SFTP.
*
* @param int $option
* @param int $arg1
* @param int $arg2
* @return bool
* @access public
*/
function _stream_set_option($option, $arg1, $arg2)
{
return false;
}
/**
* Close an resource
*
* @access public
*/
function _stream_close()
{
}
/**
* __call Magic Method
*
* When you're utilizing an SFTP stream you're not calling
the methods in this class directly - PHP is calling them for you.
* Which kinda begs the question... what methods is PHP calling and
what parameters is it passing to them? This function
* lets you figure that out.
*
* If NET_SFTP_STREAM_LOGGING is defined all calls will be output on
the screen and then (regardless of whether or not
* NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed
through to the appropriate method.
*
* @param string $name
* @param array $arguments
* @return mixed
* @access public
*/
function __call($name, $arguments)
{
if (defined('NET_SFTP_STREAM_LOGGING')) {
echo $name . '(';
$last = count($arguments) - 1;
foreach ($arguments as $i => $argument) {
var_export($argument);
if ($i != $last) {
echo ',';
}
}
echo ")\r\n";
}
$name = '_' . $name;
if (!method_exists($this, $name)) {
return false;
}
return call_user_func_array(array($this, $name), $arguments);
}
}
vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php000064400000312300151156520660015755
0ustar00<?php
/**
* Pure-PHP implementation of SFTP.
*
* PHP version 5
*
* Currently only supports SFTPv2 and v3, which, according to
wikipedia.org, "is the most widely used version,
* implemented by the popular OpenSSH SFTP server". If you want
SFTPv4/5/6 support, provide me with access
* to an SFTPv4/5/6 server.
*
* The API for this library is modeled after the API from PHP's {@link
http://php.net/book.ftp FTP extension}.
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $sftp = new \phpseclib\Net\SFTP('www.domain.tld');
* if (!$sftp->login('username', 'password')) {
* exit('Login Failed');
* }
*
* echo $sftp->pwd() . "\r\n";
* $sftp->put('filename.ext', 'hello, world!');
* print_r($sftp->nlist());
* ?>
* </code>
*
* @category Net
* @package SFTP
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2009 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Net;
/**
* Pure-PHP implementations of SFTP.
*
* @package SFTP
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class SFTP extends SSH2
{
/**
* SFTP channel constant
*
* \phpseclib\Net\SSH2::exec() uses 0 and \phpseclib\Net\SSH2::read() /
\phpseclib\Net\SSH2::write() use 1.
*
* @see \phpseclib\Net\SSH2::_send_channel_packet()
* @see \phpseclib\Net\SSH2::_get_channel_packet()
* @access private
*/
const CHANNEL = 0x100;
/**#@+
* @access public
* @see \phpseclib\Net\SFTP::put()
*/
/**
* Reads data from a local file.
*/
const SOURCE_LOCAL_FILE = 1;
/**
* Reads data from a string.
*/
// this value isn't really used anymore but i'm keeping it
reserved for historical reasons
const SOURCE_STRING = 2;
/**
* Reads data from callback:
* function callback($length) returns string to proceed, null for EOF
*/
const SOURCE_CALLBACK = 16;
/**
* Resumes an upload
*/
const RESUME = 4;
/**
* Append a local file to an already existing remote file
*/
const RESUME_START = 8;
/**#@-*/
/**
* Packet Types
*
* @see self::__construct()
* @var array
* @access private
*/
var $packet_types = array();
/**
* Status Codes
*
* @see self::__construct()
* @var array
* @access private
*/
var $status_codes = array();
/**
* The Request ID
*
* The request ID exists in the off chance that a packet is sent
out-of-order. Of course, this library doesn't support
* concurrent actions, so it's somewhat academic, here.
*
* @var boolean
* @see self::_send_sftp_packet()
* @access private
*/
var $use_request_id = false;
/**
* The Packet Type
*
* The request ID exists in the off chance that a packet is sent
out-of-order. Of course, this library doesn't support
* concurrent actions, so it's somewhat academic, here.
*
* @var int
* @see self::_get_sftp_packet()
* @access private
*/
var $packet_type = -1;
/**
* Packet Buffer
*
* @var string
* @see self::_get_sftp_packet()
* @access private
*/
var $packet_buffer = '';
/**
* Extensions supported by the server
*
* @var array
* @see self::_initChannel()
* @access private
*/
var $extensions = array();
/**
* Server SFTP version
*
* @var int
* @see self::_initChannel()
* @access private
*/
var $version;
/**
* Current working directory
*
* @var string
* @see self::realpath()
* @see self::chdir()
* @access private
*/
var $pwd = false;
/**
* Packet Type Log
*
* @see self::getLog()
* @var array
* @access private
*/
var $packet_type_log = array();
/**
* Packet Log
*
* @see self::getLog()
* @var array
* @access private
*/
var $packet_log = array();
/**
* Error information
*
* @see self::getSFTPErrors()
* @see self::getLastSFTPError()
* @var array
* @access private
*/
var $sftp_errors = array();
/**
* Stat Cache
*
* Rather than always having to open a directory and close it
immediately there after to see if a file is a directory
* we'll cache the results.
*
* @see self::_update_stat_cache()
* @see self::_remove_from_stat_cache()
* @see self::_query_stat_cache()
* @var array
* @access private
*/
var $stat_cache = array();
/**
* Max SFTP Packet Size
*
* @see self::__construct()
* @see self::get()
* @var array
* @access private
*/
var $max_sftp_packet;
/**
* Stat Cache Flag
*
* @see self::disableStatCache()
* @see self::enableStatCache()
* @var bool
* @access private
*/
var $use_stat_cache = true;
/**
* Sort Options
*
* @see self::_comparator()
* @see self::setListOrder()
* @var array
* @access private
*/
var $sortOptions = array();
/**
* Canonicalization Flag
*
* Determines whether or not paths should be canonicalized before being
* passed on to the remote server.
*
* @see self::enablePathCanonicalization()
* @see self::disablePathCanonicalization()
* @see self::realpath()
* @var bool
* @access private
*/
var $canonicalize_paths = true;
/**
* Request Buffers
*
* @see self::_get_sftp_packet()
* @var array
* @access private
*/
var $requestBuffer = array();
/**
* Preserve timestamps on file downloads / uploads
*
* @see self::get()
* @see self::put()
* @var bool
* @access private
*/
var $preserveTime = false;
/**
* Was the last packet due to the channels being closed or not?
*
* @see self::get()
* @see self::get_sftp_packet()
* @var bool
* @access private
*/
var $channel_close = false;
/**
* Default Constructor.
*
* Connects to an SFTP server
*
* @param string $host
* @param int $port
* @param int $timeout
* @return \phpseclib\Net\SFTP
* @access public
*/
function __construct($host, $port = 22, $timeout = 10)
{
parent::__construct($host, $port, $timeout);
$this->max_sftp_packet = 1 << 15;
$this->packet_types = array(
1 => 'NET_SFTP_INIT',
2 => 'NET_SFTP_VERSION',
/* the format of SSH_FXP_OPEN changed between SFTPv4 and
SFTPv5+:
SFTPv5+:
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.1
pre-SFTPv5 :
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 */
3 => 'NET_SFTP_OPEN',
4 => 'NET_SFTP_CLOSE',
5 => 'NET_SFTP_READ',
6 => 'NET_SFTP_WRITE',
7 => 'NET_SFTP_LSTAT',
9 => 'NET_SFTP_SETSTAT',
11 => 'NET_SFTP_OPENDIR',
12 => 'NET_SFTP_READDIR',
13 => 'NET_SFTP_REMOVE',
14 => 'NET_SFTP_MKDIR',
15 => 'NET_SFTP_RMDIR',
16 => 'NET_SFTP_REALPATH',
17 => 'NET_SFTP_STAT',
/* the format of SSH_FXP_RENAME changed between SFTPv4 and
SFTPv5+:
SFTPv5+:
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
pre-SFTPv5 :
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.5 */
18 => 'NET_SFTP_RENAME',
19 => 'NET_SFTP_READLINK',
20 => 'NET_SFTP_SYMLINK',
101=> 'NET_SFTP_STATUS',
102=> 'NET_SFTP_HANDLE',
/* the format of SSH_FXP_NAME changed between SFTPv3 and
SFTPv4+:
SFTPv4+:
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4
pre-SFTPv4 :
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 */
103=> 'NET_SFTP_DATA',
104=> 'NET_SFTP_NAME',
105=> 'NET_SFTP_ATTRS',
200=> 'NET_SFTP_EXTENDED'
);
$this->status_codes = array(
0 => 'NET_SFTP_STATUS_OK',
1 => 'NET_SFTP_STATUS_EOF',
2 => 'NET_SFTP_STATUS_NO_SUCH_FILE',
3 => 'NET_SFTP_STATUS_PERMISSION_DENIED',
4 => 'NET_SFTP_STATUS_FAILURE',
5 => 'NET_SFTP_STATUS_BAD_MESSAGE',
6 => 'NET_SFTP_STATUS_NO_CONNECTION',
7 => 'NET_SFTP_STATUS_CONNECTION_LOST',
8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED',
9 => 'NET_SFTP_STATUS_INVALID_HANDLE',
10 => 'NET_SFTP_STATUS_NO_SUCH_PATH',
11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS',
12 => 'NET_SFTP_STATUS_WRITE_PROTECT',
13 => 'NET_SFTP_STATUS_NO_MEDIA',
14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM',
15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED',
16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL',
17 => 'NET_SFTP_STATUS_LOCK_CONFLICT',
18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY',
19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY',
20 => 'NET_SFTP_STATUS_INVALID_FILENAME',
21 => 'NET_SFTP_STATUS_LINK_LOOP',
22 => 'NET_SFTP_STATUS_CANNOT_DELETE',
23 => 'NET_SFTP_STATUS_INVALID_PARAMETER',
24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY',
25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT',
26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED',
27 => 'NET_SFTP_STATUS_DELETE_PENDING',
28 => 'NET_SFTP_STATUS_FILE_CORRUPT',
29 => 'NET_SFTP_STATUS_OWNER_INVALID',
30 => 'NET_SFTP_STATUS_GROUP_INVALID',
31 =>
'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK'
);
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1
// the order, in this case, matters quite a lot - see
\phpseclib\Net\SFTP::_parseAttributes() to understand why
$this->attributes = array(
0x00000001 => 'NET_SFTP_ATTR_SIZE',
0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined
in SFTPv3, removed in SFTPv4+
0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
// 0x80000000 will yield a floating point on 32-bit systems and
converting floating points to integers
// yields inconsistent behavior depending on how php is
compiled. so we left shift -1 (which, in
// two's compliment, consists of all 1 bits) by 31. on
64-bit systems this'll yield 0xFFFFFFFF80000000.
// that's not a problem, however, and 'anded'
and a 32-bit number, as all the leading 1 bits are ignored.
(-1 << 31) & 0xFFFFFFFF =>
'NET_SFTP_ATTR_EXTENDED'
);
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3
// the flag definitions change somewhat in SFTPv5+. if SFTPv5+
support is added to this library, maybe name
// the array for that $this->open5_flags and similarly alter the
constant names.
$this->open_flags = array(
0x00000001 => 'NET_SFTP_OPEN_READ',
0x00000002 => 'NET_SFTP_OPEN_WRITE',
0x00000004 => 'NET_SFTP_OPEN_APPEND',
0x00000008 => 'NET_SFTP_OPEN_CREATE',
0x00000010 => 'NET_SFTP_OPEN_TRUNCATE',
0x00000020 => 'NET_SFTP_OPEN_EXCL'
);
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
// see \phpseclib\Net\SFTP::_parseLongname() for an explanation
$this->file_types = array(
1 => 'NET_SFTP_TYPE_REGULAR',
2 => 'NET_SFTP_TYPE_DIRECTORY',
3 => 'NET_SFTP_TYPE_SYMLINK',
4 => 'NET_SFTP_TYPE_SPECIAL',
5 => 'NET_SFTP_TYPE_UNKNOWN',
// the followin types were first defined for use in SFTPv5+
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
6 => 'NET_SFTP_TYPE_SOCKET',
7 => 'NET_SFTP_TYPE_CHAR_DEVICE',
8 => 'NET_SFTP_TYPE_BLOCK_DEVICE',
9 => 'NET_SFTP_TYPE_FIFO'
);
$this->_define_array(
$this->packet_types,
$this->status_codes,
$this->attributes,
$this->open_flags,
$this->file_types
);
if (!defined('NET_SFTP_QUEUE_SIZE')) {
define('NET_SFTP_QUEUE_SIZE', 32);
}
if (!defined('NET_SFTP_UPLOAD_QUEUE_SIZE')) {
define('NET_SFTP_UPLOAD_QUEUE_SIZE', 1024);
}
}
/**
* Login
*
* @param string $username
* @return bool
* @access public
*/
function login($username)
{
if (!call_user_func_array('parent::login',
func_get_args())) {
return false;
}
return $this->_init_sftp_connection();
}
/**
* (Re)initializes the SFTP channel
*
* @return bool
* @access private
*/
function _init_sftp_connection()
{
$this->window_size_server_to_client[self::CHANNEL] =
$this->window_size;
$packet = pack(
'CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN,
strlen('session'),
'session',
self::CHANNEL,
$this->window_size,
0x4000
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[self::CHANNEL] =
NET_SSH2_MSG_CHANNEL_OPEN;
$response = $this->_get_channel_packet(self::CHANNEL, true);
if ($response === false) {
return false;
}
$packet = pack(
'CNNa*CNa*',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL],
strlen('subsystem'),
'subsystem',
1,
strlen('sftp'),
'sftp'
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[self::CHANNEL] =
NET_SSH2_MSG_CHANNEL_REQUEST;
$response = $this->_get_channel_packet(self::CHANNEL, true);
if ($response === false) {
// from PuTTY's psftp.exe
$command = "test -x /usr/lib/sftp-server && exec
/usr/lib/sftp-server\n" .
"test -x /usr/local/lib/sftp-server &&
exec /usr/local/lib/sftp-server\n" .
"exec sftp-server";
// we don't do $this->exec($command, false) because
exec() operates on a different channel and plus the SSH_MSG_CHANNEL_OPEN
that exec() does
// is redundant
$packet = pack(
'CNNa*CNa*',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL],
strlen('exec'),
'exec',
1,
strlen($command),
$command
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[self::CHANNEL] =
NET_SSH2_MSG_CHANNEL_REQUEST;
$response = $this->_get_channel_packet(self::CHANNEL, true);
if ($response === false) {
return false;
}
}
$this->channel_status[self::CHANNEL] =
NET_SSH2_MSG_CHANNEL_DATA;
if (!$this->_send_sftp_packet(NET_SFTP_INIT,
"\0\0\0\3")) {
return false;
}
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_VERSION) {
user_error('Expected SSH_FXP_VERSION');
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nversion',
$this->_string_shift($response, 4)));
$this->version = $version;
while (!empty($response)) {
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$key = $this->_string_shift($response, $length);
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$value = $this->_string_shift($response, $length);
$this->extensions[$key] = $value;
}
/*
SFTPv4+ defines a 'newline' extension. SFTPv3 seems to
have unofficial support for it via 'newline@vandyke.com',
however, I'm not sure what 'newline@vandyke.com' is
supposed to do (the fact that it's unofficial means that it's
not in the official SFTPv3 specs) and
'newline@vandyke.com' / 'newline' are likely not
drop-in substitutes for
one another due to the fact that 'newline' comes with a
SSH_FXF_TEXT bitmask whereas it seems unlikely that
'newline@vandyke.com' would.
*/
/*
if (isset($this->extensions['newline@vandyke.com'])) {
$this->extensions['newline'] =
$this->extensions['newline@vandyke.com'];
unset($this->extensions['newline@vandyke.com']);
}
*/
$this->use_request_id = true;
/*
A Note on SFTPv4/5/6 support:
<http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.1>
states the following:
"If the client wishes to interoperate with servers that
support noncontiguous version
numbers it SHOULD send '3'"
Given that the server only sends its version number after the
client has already done so, the above
seems to be suggesting that v3 should be the default version.
This makes sense given that v3 is the
most popular.
<http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.5>
states the following;
"If the server did not send the "versions"
extension, or the version-from-list was not included, the
server MAY send a status response describing the failure, but
MUST then close the channel without
processing any further requests."
So what do you do if you have a client whose initial SSH_FXP_INIT
packet says it implements v3 and
a server whose initial SSH_FXP_VERSION reply says it implements v4
and only v4? If it only implements
v4, the "versions" extension is likely not going to have
been sent so version re-negotiation as discussed
in draft-ietf-secsh-filexfer-13 would be quite impossible. As
such, what \phpseclib\Net\SFTP would do is close the
channel and reopen it with a new and updated SSH_FXP_INIT packet.
*/
switch ($this->version) {
case 2:
case 3:
break;
default:
return false;
}
$this->pwd = $this->_realpath('.');
$this->_update_stat_cache($this->pwd, array());
return true;
}
/**
* Disable the stat cache
*
* @access public
*/
function disableStatCache()
{
$this->use_stat_cache = false;
}
/**
* Enable the stat cache
*
* @access public
*/
function enableStatCache()
{
$this->use_stat_cache = true;
}
/**
* Clear the stat cache
*
* @access public
*/
function clearStatCache()
{
$this->stat_cache = array();
}
/**
* Enable path canonicalization
*
* @access public
*/
function enablePathCanonicalization()
{
$this->canonicalize_paths = true;
}
/**
* Enable path canonicalization
*
* @access public
*/
function disablePathCanonicalization()
{
$this->canonicalize_paths = false;
}
/**
* Returns the current directory name
*
* @return mixed
* @access public
*/
function pwd()
{
return $this->pwd;
}
/**
* Logs errors
*
* @param string $response
* @param int $status
* @access public
*/
function _logError($response, $status = -1)
{
if ($status == -1) {
if (strlen($response) < 4) {
return;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
}
$error = $this->status_codes[$status];
if ($this->version > 2 || strlen($response) < 4) {
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$this->sftp_errors[] = $error . ': ' .
$this->_string_shift($response, $length);
} else {
$this->sftp_errors[] = $error;
}
}
/**
* Returns canonicalized absolute pathname
*
* realpath() expands all symbolic links and resolves references to
'/./', '/../' and extra '/' characters in the
input
* path and returns the canonicalized absolute pathname.
*
* @param string $path
* @return mixed
* @access public
*/
function realpath($path)
{
return $this->_realpath($path);
}
/**
* Canonicalize the Server-Side Path Name
*
* SFTP doesn't provide a mechanism by which the current working
directory can be changed, so we'll emulate it. Returns
* the absolute (canonicalized) path.
*
* If canonicalize_paths has been disabled using
disablePathCanonicalization(), $path is returned as-is.
*
* @see self::chdir()
* @see self::disablePathCanonicalization()
* @param string $path
* @return mixed
* @access private
*/
function _realpath($path)
{
if (!$this->canonicalize_paths) {
return $path;
}
if ($this->pwd === false) {
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9
if (!$this->_send_sftp_packet(NET_SFTP_REALPATH,
pack('Na*', strlen($path), $path))) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_NAME:
// although SSH_FXP_NAME is implemented differently in
SFTPv3 than it is in SFTPv4+, the following
// should work on all SFTP versions since the only part
of the SSH_FXP_NAME packet the following looks
// at is the first part and that part is defined the
same in SFTP versions 3 through 6.
$this->_string_shift($response, 4); // skip over the
count - it should be 1, anyway
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
return $this->_string_shift($response, $length);
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_NAME or
SSH_FXP_STATUS');
return false;
}
}
if (!strlen($path) || $path[0] != '/') {
$path = $this->pwd . '/' . $path;
}
$path = explode('/', $path);
$new = array();
foreach ($path as $dir) {
if (!strlen($dir)) {
continue;
}
switch ($dir) {
case '..':
array_pop($new);
case '.':
break;
default:
$new[] = $dir;
}
}
return '/' . implode('/', $new);
}
/**
* Changes the current directory
*
* @param string $dir
* @return bool
* @access public
*/
function chdir($dir)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
// assume current dir if $dir is empty
if ($dir === '') {
$dir = './';
// suffix a slash if needed
} elseif ($dir[strlen($dir) - 1] != '/') {
$dir.= '/';
}
$dir = $this->_realpath($dir);
// confirm that $dir is, in fact, a valid directory
if ($this->use_stat_cache &&
is_array($this->_query_stat_cache($dir))) {
$this->pwd = $dir;
return true;
}
// we could do a stat on the alleged $dir to see if it's a
directory but that doesn't tell us
// the currently logged in user has the appropriate permissions or
not. maybe you could see if
// the file's uid / gid match the currently logged in
user's uid / gid but how there's no easy
// way to get those with SFTP
if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR,
pack('Na*', strlen($dir), $dir))) {
return false;
}
// see \phpseclib\Net\SFTP::nlist() for a more thorough explanation
of the following
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or
SSH_FXP_STATUS');
return false;
}
if (!$this->_close_handle($handle)) {
return false;
}
$this->_update_stat_cache($dir, array());
$this->pwd = $dir;
return true;
}
/**
* Returns a list of files in the given directory
*
* @param string $dir
* @param bool $recursive
* @return mixed
* @access public
*/
function nlist($dir = '.', $recursive = false)
{
return $this->_nlist_helper($dir, $recursive, '');
}
/**
* Helper method for nlist
*
* @param string $dir
* @param bool $recursive
* @param string $relativeDir
* @return mixed
* @access private
*/
function _nlist_helper($dir, $recursive, $relativeDir)
{
$files = $this->_list($dir, false);
if (!$recursive || $files === false) {
return $files;
}
$result = array();
foreach ($files as $value) {
if ($value == '.' || $value == '..') {
if ($relativeDir == '') {
$result[] = $value;
}
continue;
}
if
(is_array($this->_query_stat_cache($this->_realpath($dir .
'/' . $value)))) {
$temp = $this->_nlist_helper($dir . '/' .
$value, true, $relativeDir . $value . '/');
$temp = is_array($temp) ? $temp : array();
$result = array_merge($result, $temp);
} else {
$result[] = $relativeDir . $value;
}
}
return $result;
}
/**
* Returns a detailed list of files in the given directory
*
* @param string $dir
* @param bool $recursive
* @return mixed
* @access public
*/
function rawlist($dir = '.', $recursive = false)
{
$files = $this->_list($dir, true);
if (!$recursive || $files === false) {
return $files;
}
static $depth = 0;
foreach ($files as $key => $value) {
if ($depth != 0 && $key == '..') {
unset($files[$key]);
continue;
}
$is_directory = false;
if ($key != '.' && $key != '..') {
if ($this->use_stat_cache) {
$is_directory =
is_array($this->_query_stat_cache($this->_realpath($dir .
'/' . $key)));
} else {
$stat = $this->lstat($dir . '/' . $key);
$is_directory = $stat &&
$stat['type'] === NET_SFTP_TYPE_DIRECTORY;
}
}
if ($is_directory) {
$depth++;
$files[$key] = $this->rawlist($dir . '/' .
$key, true);
$depth--;
} else {
$files[$key] = (object) $value;
}
}
return $files;
}
/**
* Reads a list, be it detailed or not, of files in the given directory
*
* @param string $dir
* @param bool $raw
* @return mixed
* @access private
*/
function _list($dir, $raw = true)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$dir = $this->_realpath($dir . '/');
if ($dir === false) {
return false;
}
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2
if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR,
pack('Na*', strlen($dir), $dir))) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2
// since 'handle' is the last field in the
SSH_FXP_HANDLE packet, we'll just remove the first four bytes that
// represent the length of the string and leave it at that
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS:
// presumably SSH_FX_NO_SUCH_FILE or
SSH_FX_PERMISSION_DENIED
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or
SSH_FXP_STATUS');
return false;
}
$this->_update_stat_cache($dir, array());
$contents = array();
while (true) {
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2
// why multiple SSH_FXP_READDIR packets would be sent when the
response to a single one can span arbitrarily many
// SSH_MSG_CHANNEL_DATA messages is not known to me.
if (!$this->_send_sftp_packet(NET_SFTP_READDIR,
pack('Na*', strlen($handle), $handle))) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_NAME:
if (strlen($response) < 4) {
return false;
}
extract(unpack('Ncount',
$this->_string_shift($response, 4)));
for ($i = 0; $i < $count; $i++) {
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$shortname = $this->_string_shift($response,
$length);
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$longname = $this->_string_shift($response,
$length);
$attributes =
$this->_parseAttributes($response);
if (!isset($attributes['type'])) {
$fileType =
$this->_parseLongname($longname);
if ($fileType) {
$attributes['type'] = $fileType;
}
}
$contents[$shortname] = $attributes +
array('filename' => $shortname);
if (isset($attributes['type']) &&
$attributes['type'] == NET_SFTP_TYPE_DIRECTORY &&
($shortname != '.' && $shortname != '..')) {
$this->_update_stat_cache($dir .
'/' . $shortname, array());
} else {
if ($shortname == '..') {
$temp = $this->_realpath($dir .
'/..') . '/.';
} else {
$temp = $dir . '/' . $shortname;
}
$this->_update_stat_cache($temp, (object)
array('lstat' => $attributes));
}
// SFTPv6 has an optional boolean end-of-list
field, but we'll ignore that, since the
// final SSH_FXP_STATUS packet should tell us that,
already.
}
break;
case NET_SFTP_STATUS:
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_EOF) {
$this->_logError($response, $status);
return false;
}
break 2;
default:
user_error('Expected SSH_FXP_NAME or
SSH_FXP_STATUS');
return false;
}
}
if (!$this->_close_handle($handle)) {
return false;
}
if (count($this->sortOptions)) {
uasort($contents, array(&$this, '_comparator'));
}
return $raw ? $contents : array_map('strval',
array_keys($contents));
}
/**
* Compares two rawlist entries using parameters set by setListOrder()
*
* Intended for use with uasort()
*
* @param array $a
* @param array $b
* @return int
* @access private
*/
function _comparator($a, $b)
{
switch (true) {
case $a['filename'] === '.' ||
$b['filename'] === '.':
if ($a['filename'] === $b['filename'])
{
return 0;
}
return $a['filename'] === '.' ? -1 : 1;
case $a['filename'] === '..' ||
$b['filename'] === '..':
if ($a['filename'] === $b['filename'])
{
return 0;
}
return $a['filename'] === '..' ? -1 :
1;
case isset($a['type']) &&
$a['type'] === NET_SFTP_TYPE_DIRECTORY:
if (!isset($b['type'])) {
return 1;
}
if ($b['type'] !== $a['type']) {
return -1;
}
break;
case isset($b['type']) &&
$b['type'] === NET_SFTP_TYPE_DIRECTORY:
return 1;
}
foreach ($this->sortOptions as $sort => $order) {
if (!isset($a[$sort]) || !isset($b[$sort])) {
if (isset($a[$sort])) {
return -1;
}
if (isset($b[$sort])) {
return 1;
}
return 0;
}
switch ($sort) {
case 'filename':
$result = strcasecmp($a['filename'],
$b['filename']);
if ($result) {
return $order === SORT_DESC ? -$result : $result;
}
break;
case 'permissions':
case 'mode':
$a[$sort]&= 07777;
$b[$sort]&= 07777;
default:
if ($a[$sort] === $b[$sort]) {
break;
}
return $order === SORT_ASC ? $a[$sort] - $b[$sort] :
$b[$sort] - $a[$sort];
}
}
}
/**
* Defines how nlist() and rawlist() will be sorted - if at all.
*
* If sorting is enabled directories and files will be sorted
independently with
* directories appearing before files in the resultant array that is
returned.
*
* Any parameter returned by stat is a valid sort parameter for this
function.
* Filename comparisons are case insensitive.
*
* Examples:
*
* $sftp->setListOrder('filename', SORT_ASC);
* $sftp->setListOrder('size', SORT_DESC,
'filename', SORT_ASC);
* $sftp->setListOrder(true);
* Separates directories from files but doesn't do any sorting
beyond that
* $sftp->setListOrder();
* Don't do any sort of sorting
*
* @access public
*/
function setListOrder()
{
$this->sortOptions = array();
$args = func_get_args();
if (empty($args)) {
return;
}
$len = count($args) & 0x7FFFFFFE;
for ($i = 0; $i < $len; $i+=2) {
$this->sortOptions[$args[$i]] = $args[$i + 1];
}
if (!count($this->sortOptions)) {
$this->sortOptions = array('bogus' => true);
}
}
/**
* Returns the file size, in bytes, or false, on failure
*
* Files larger than 4GB will show up as being exactly 4GB.
*
* @param string $filename
* @return mixed
* @access public
*/
function size($filename)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$result = $this->stat($filename);
if ($result === false) {
return false;
}
return isset($result['size']) ? $result['size']
: -1;
}
/**
* Save files / directories to cache
*
* @param string $path
* @param mixed $value
* @access private
*/
function _update_stat_cache($path, $value)
{
if ($this->use_stat_cache === false) {
return;
}
// preg_replace('#^/|/(?=/)|/$#', '', $dir) ==
str_replace('//', '/', trim($path, '/'))
$dirs = explode('/',
preg_replace('#^/|/(?=/)|/$#', '', $path));
$temp = &$this->stat_cache;
$max = count($dirs) - 1;
foreach ($dirs as $i => $dir) {
// if $temp is an object that means one of two things.
// 1. a file was deleted and changed to a directory behind
phpseclib's back
// 2. it's a symlink. when lstat is done it's
unclear what it's a symlink to
if (is_object($temp)) {
$temp = array();
}
if (!isset($temp[$dir])) {
$temp[$dir] = array();
}
if ($i === $max) {
if (is_object($temp[$dir]) && is_object($value)) {
if (!isset($value->stat) &&
isset($temp[$dir]->stat)) {
$value->stat = $temp[$dir]->stat;
}
if (!isset($value->lstat) &&
isset($temp[$dir]->lstat)) {
$value->lstat = $temp[$dir]->lstat;
}
}
$temp[$dir] = $value;
break;
}
$temp = &$temp[$dir];
}
}
/**
* Remove files / directories from cache
*
* @param string $path
* @return bool
* @access private
*/
function _remove_from_stat_cache($path)
{
$dirs = explode('/',
preg_replace('#^/|/(?=/)|/$#', '', $path));
$temp = &$this->stat_cache;
$max = count($dirs) - 1;
foreach ($dirs as $i => $dir) {
if (!is_array($temp)) {
return false;
}
if ($i === $max) {
unset($temp[$dir]);
return true;
}
if (!isset($temp[$dir])) {
return false;
}
$temp = &$temp[$dir];
}
}
/**
* Checks cache for path
*
* Mainly used by file_exists
*
* @param string $path
* @return mixed
* @access private
*/
function _query_stat_cache($path)
{
$dirs = explode('/',
preg_replace('#^/|/(?=/)|/$#', '', $path));
$temp = &$this->stat_cache;
foreach ($dirs as $dir) {
if (!is_array($temp)) {
return null;
}
if (!isset($temp[$dir])) {
return null;
}
$temp = &$temp[$dir];
}
return $temp;
}
/**
* Returns general information about a file.
*
* Returns an array on success and false otherwise.
*
* @param string $filename
* @return mixed
* @access public
*/
function stat($filename)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$filename = $this->_realpath($filename);
if ($filename === false) {
return false;
}
if ($this->use_stat_cache) {
$result = $this->_query_stat_cache($filename);
if (is_array($result) && isset($result['.'])
&& isset($result['.']->stat)) {
return $result['.']->stat;
}
if (is_object($result) && isset($result->stat)) {
return $result->stat;
}
}
$stat = $this->_stat($filename, NET_SFTP_STAT);
if ($stat === false) {
$this->_remove_from_stat_cache($filename);
return false;
}
if (isset($stat['type'])) {
if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) {
$filename.= '/.';
}
$this->_update_stat_cache($filename, (object)
array('stat' => $stat));
return $stat;
}
$pwd = $this->pwd;
$stat['type'] = $this->chdir($filename) ?
NET_SFTP_TYPE_DIRECTORY :
NET_SFTP_TYPE_REGULAR;
$this->pwd = $pwd;
if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) {
$filename.= '/.';
}
$this->_update_stat_cache($filename, (object)
array('stat' => $stat));
return $stat;
}
/**
* Returns general information about a file or symbolic link.
*
* Returns an array on success and false otherwise.
*
* @param string $filename
* @return mixed
* @access public
*/
function lstat($filename)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$filename = $this->_realpath($filename);
if ($filename === false) {
return false;
}
if ($this->use_stat_cache) {
$result = $this->_query_stat_cache($filename);
if (is_array($result) && isset($result['.'])
&& isset($result['.']->lstat)) {
return $result['.']->lstat;
}
if (is_object($result) && isset($result->lstat)) {
return $result->lstat;
}
}
$lstat = $this->_stat($filename, NET_SFTP_LSTAT);
if ($lstat === false) {
$this->_remove_from_stat_cache($filename);
return false;
}
if (isset($lstat['type'])) {
if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) {
$filename.= '/.';
}
$this->_update_stat_cache($filename, (object)
array('lstat' => $lstat));
return $lstat;
}
$stat = $this->_stat($filename, NET_SFTP_STAT);
if ($lstat != $stat) {
$lstat = array_merge($lstat, array('type' =>
NET_SFTP_TYPE_SYMLINK));
$this->_update_stat_cache($filename, (object)
array('lstat' => $lstat));
return $stat;
}
$pwd = $this->pwd;
$lstat['type'] = $this->chdir($filename) ?
NET_SFTP_TYPE_DIRECTORY :
NET_SFTP_TYPE_REGULAR;
$this->pwd = $pwd;
if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) {
$filename.= '/.';
}
$this->_update_stat_cache($filename, (object)
array('lstat' => $lstat));
return $lstat;
}
/**
* Returns general information about a file or symbolic link
*
* Determines information without calling
\phpseclib\Net\SFTP::realpath().
* The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT.
*
* @param string $filename
* @param int $type
* @return mixed
* @access private
*/
function _stat($filename, $type)
{
// SFTPv4+ adds an additional 32-bit integer field - flags - to the
following:
$packet = pack('Na*', strlen($filename), $filename);
if (!$this->_send_sftp_packet($type, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_ATTRS:
return $this->_parseAttributes($response);
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
}
user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS');
return false;
}
/**
* Truncates a file to a given length
*
* @param string $filename
* @param int $new_size
* @return bool
* @access public
*/
function truncate($filename, $new_size)
{
$attr = pack('N3', NET_SFTP_ATTR_SIZE, $new_size /
4294967296, $new_size); // 4294967296 == 0x100000000 == 1<<32
return $this->_setstat($filename, $attr, false);
}
/**
* Sets access and modification time of file.
*
* If the file does not exist, it will be created.
*
* @param string $filename
* @param int $time
* @param int $atime
* @return bool
* @access public
*/
function touch($filename, $time = null, $atime = null)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$filename = $this->_realpath($filename);
if ($filename === false) {
return false;
}
if (!isset($time)) {
$time = time();
}
if (!isset($atime)) {
$atime = $time;
}
$flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE |
NET_SFTP_OPEN_EXCL;
$attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $time,
$atime);
$packet = pack('Na*Na*', strlen($filename), $filename,
$flags, $attr);
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
return $this->_close_handle(substr($response, 4));
case NET_SFTP_STATUS:
$this->_logError($response);
break;
default:
user_error('Expected SSH_FXP_HANDLE or
SSH_FXP_STATUS');
return false;
}
return $this->_setstat($filename, $attr, false);
}
/**
* Changes file or directory owner
*
* Returns true on success or false on error.
*
* @param string $filename
* @param int $uid
* @param bool $recursive
* @return bool
* @access public
*/
function chown($filename, $uid, $recursive = false)
{
// quoting from
<http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>,
// "if the owner or group is specified as -1, then that ID is
not changed"
$attr = pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1);
return $this->_setstat($filename, $attr, $recursive);
}
/**
* Changes file or directory group
*
* Returns true on success or false on error.
*
* @param string $filename
* @param int $gid
* @param bool $recursive
* @return bool
* @access public
*/
function chgrp($filename, $gid, $recursive = false)
{
$attr = pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid);
return $this->_setstat($filename, $attr, $recursive);
}
/**
* Set permissions on a file.
*
* Returns the new file permissions on success or false on error.
* If $recursive is true than this just returns true or false.
*
* @param int $mode
* @param string $filename
* @param bool $recursive
* @return mixed
* @access public
*/
function chmod($mode, $filename, $recursive = false)
{
if (is_string($mode) && is_int($filename)) {
$temp = $mode;
$mode = $filename;
$filename = $temp;
}
$attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode &
07777);
if (!$this->_setstat($filename, $attr, $recursive)) {
return false;
}
if ($recursive) {
return true;
}
$filename = $this->realpath($filename);
// rather than return what the permissions *should* be, we'll
return what they actually are. this will also
// tell us if the file actually exists.
// incidentally, SFTPv4+ adds an additional 32-bit integer field -
flags - to the following:
$packet = pack('Na*', strlen($filename), $filename);
if (!$this->_send_sftp_packet(NET_SFTP_STAT, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_ATTRS:
$attrs = $this->_parseAttributes($response);
return $attrs['permissions'];
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
}
user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS');
return false;
}
/**
* Sets information about a file
*
* @param string $filename
* @param string $attr
* @param bool $recursive
* @return bool
* @access private
*/
function _setstat($filename, $attr, $recursive)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$filename = $this->_realpath($filename);
if ($filename === false) {
return false;
}
$this->_remove_from_stat_cache($filename);
if ($recursive) {
$i = 0;
$result = $this->_setstat_recursive($filename, $attr, $i);
$this->_read_put_responses($i);
return $result;
}
// SFTPv4+ has an additional byte field - type - that would need to
be sent, as well. setting it to
// SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to
do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT.
if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT,
pack('Na*a*', strlen($filename), $filename, $attr))) {
return false;
}
/*
"Because some systems must use separate system calls to set
various attributes, it is possible that a failure
response will be returned, but yet some of the attributes may be
have been successfully modified. If possible,
servers SHOULD avoid this situation; however, clients MUST be
aware that this is possible."
--
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6
*/
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
return false;
}
return true;
}
/**
* Recursively sets information on directories on the SFTP server
*
* Minimizes directory lookups and SSH_FXP_STATUS requests for speed.
*
* @param string $path
* @param string $attr
* @param int $i
* @return bool
* @access private
*/
function _setstat_recursive($path, $attr, &$i)
{
if (!$this->_read_put_responses($i)) {
return false;
}
$i = 0;
$entries = $this->_list($path, true);
if ($entries === false) {
return $this->_setstat($path, $attr, false);
}
// normally $entries would have at least . and .. but it might not
if the directories
// permissions didn't allow reading
if (empty($entries)) {
return false;
}
unset($entries['.'], $entries['..']);
foreach ($entries as $filename => $props) {
if (!isset($props['type'])) {
return false;
}
$temp = $path . '/' . $filename;
if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
if (!$this->_setstat_recursive($temp, $attr, $i)) {
return false;
}
} else {
if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT,
pack('Na*a*', strlen($temp), $temp, $attr))) {
return false;
}
$i++;
if ($i >= NET_SFTP_QUEUE_SIZE) {
if (!$this->_read_put_responses($i)) {
return false;
}
$i = 0;
}
}
}
if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT,
pack('Na*a*', strlen($path), $path, $attr))) {
return false;
}
$i++;
if ($i >= NET_SFTP_QUEUE_SIZE) {
if (!$this->_read_put_responses($i)) {
return false;
}
$i = 0;
}
return true;
}
/**
* Return the target of a symbolic link
*
* @param string $link
* @return mixed
* @access public
*/
function readlink($link)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$link = $this->_realpath($link);
if (!$this->_send_sftp_packet(NET_SFTP_READLINK,
pack('Na*', strlen($link), $link))) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_NAME:
break;
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_NAME or
SSH_FXP_STATUS');
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Ncount',
$this->_string_shift($response, 4)));
// the file isn't a symlink
if (!$count) {
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
return $this->_string_shift($response, $length);
}
/**
* Create a symlink
*
* symlink() creates a symbolic link to the existing target with the
specified name link.
*
* @param string $target
* @param string $link
* @return bool
* @access public
*/
function symlink($target, $link)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
//$target = $this->_realpath($target);
$link = $this->_realpath($link);
$packet = pack('Na*Na*', strlen($target), $target,
strlen($link), $link);
if (!$this->_send_sftp_packet(NET_SFTP_SYMLINK, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
return false;
}
return true;
}
/**
* Creates a directory.
*
* @param string $dir
* @param int $mode
* @param bool $recursive
* @return bool
* @access public
*/
function mkdir($dir, $mode = -1, $recursive = false)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$dir = $this->_realpath($dir);
if ($recursive) {
$dirs = explode('/',
preg_replace('#/(?=/)|/$#', '', $dir));
if (empty($dirs[0])) {
array_shift($dirs);
$dirs[0] = '/' . $dirs[0];
}
for ($i = 0; $i < count($dirs); $i++) {
$temp = array_slice($dirs, 0, $i + 1);
$temp = implode('/', $temp);
$result = $this->_mkdir_helper($temp, $mode);
}
return $result;
}
return $this->_mkdir_helper($dir, $mode);
}
/**
* Helper function for directory creation
*
* @param string $dir
* @param int $mode
* @return bool
* @access private
*/
function _mkdir_helper($dir, $mode)
{
// send SSH_FXP_MKDIR without any attributes (that's what the
\0\0\0\0 is doing)
if (!$this->_send_sftp_packet(NET_SFTP_MKDIR,
pack('Na*a*', strlen($dir), $dir, "\0\0\0\0"))) {
return false;
}
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
return false;
}
if ($mode !== -1) {
$this->chmod($mode, $dir);
}
return true;
}
/**
* Removes a directory.
*
* @param string $dir
* @return bool
* @access public
*/
function rmdir($dir)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$dir = $this->_realpath($dir);
if ($dir === false) {
return false;
}
if (!$this->_send_sftp_packet(NET_SFTP_RMDIR,
pack('Na*', strlen($dir), $dir))) {
return false;
}
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
// presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED?
$this->_logError($response, $status);
return false;
}
$this->_remove_from_stat_cache($dir);
// the following will do a soft delete, which would be useful if
you deleted a file
// and then tried to do a stat on the deleted file. the above, in
contrast, does
// a hard delete
//$this->_update_stat_cache($dir, false);
return true;
}
/**
* Uploads a file to the SFTP server.
*
* By default, \phpseclib\Net\SFTP::put() does not read from the local
filesystem. $data is dumped directly into $remote_file.
* So, for example, if you set $data to 'filename.ext' and
then do \phpseclib\Net\SFTP::get(), you will get a file, twelve bytes
* long, containing 'filename.ext' as its contents.
*
* Setting $mode to self::SOURCE_LOCAL_FILE will change the above
behavior. With self::SOURCE_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
* large $remote_file will be, as well.
*
* Setting $mode to self::SOURCE_CALLBACK will use $data as callback
function, which gets only one parameter -- number of bytes to return, and
returns a string if there is some data or null if there is no more data
*
* If $data is a resource then it'll be used as a resource
instead.
*
* Currently, only binary mode is supported. As such, if the line
endings need to be adjusted, you will need to take
* care of that, yourself.
*
* $mode can take an additional two parameters - self::RESUME and
self::RESUME_START. These are bitwise AND'd with
* $mode. So if you want to resume upload of a 300mb file on the local
file system you'd set $mode to the following:
*
* self::SOURCE_LOCAL_FILE | self::RESUME
*
* If you wanted to simply append the full contents of a local file to
the full contents of a remote file you'd replace
* self::RESUME with self::RESUME_START.
*
* If $mode & (self::RESUME | self::RESUME_START) then
self::RESUME_START will be assumed.
*
* $start and $local_start give you more fine grained control over this
process and take precident over self::RESUME
* when they're non-negative. ie. $start could let you write at
the end of a file (like self::RESUME) or in the middle
* of one. $local_start could let you start your reading from the end
of a file (like self::RESUME_START) or in the
* middle of one.
*
* Setting $local_start to > 0 or $mode | self::RESUME_START
doesn't do anything unless $mode | self::SOURCE_LOCAL_FILE.
*
* @param string $remote_file
* @param string|resource $data
* @param int $mode
* @param int $start
* @param int $local_start
* @param callable|null $progressCallback
* @return bool
* @access public
* @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new
function - \phpseclib\Net\SFTP::setMode().
*/
function put($remote_file, $data, $mode = self::SOURCE_STRING, $start =
-1, $local_start = -1, $progressCallback = null)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$remote_file = $this->_realpath($remote_file);
if ($remote_file === false) {
return false;
}
$this->_remove_from_stat_cache($remote_file);
$flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE;
// according to the SFTP specs, NET_SFTP_OPEN_APPEND should
"force all writes to append data at the end of the file."
// in practice, it doesn't seem to do that.
//$flags|= ($mode & self::RESUME) ? NET_SFTP_OPEN_APPEND :
NET_SFTP_OPEN_TRUNCATE;
if ($start >= 0) {
$offset = $start;
} elseif ($mode & self::RESUME) {
// if NET_SFTP_OPEN_APPEND worked as it should _size()
wouldn't need to be called
$size = $this->size($remote_file);
$offset = $size !== false ? $size : 0;
} else {
$offset = 0;
$flags|= NET_SFTP_OPEN_TRUNCATE;
}
$packet = pack('Na*N2', strlen($remote_file),
$remote_file, $flags, 0);
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or
SSH_FXP_STATUS');
return false;
}
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3
$dataCallback = false;
switch (true) {
case $mode & self::SOURCE_CALLBACK:
if (!is_callable($data)) {
user_error("\$data should be is_callable() if you
specify SOURCE_CALLBACK flag");
}
$dataCallback = $data;
// do nothing
break;
case is_resource($data):
$mode = $mode & ~self::SOURCE_LOCAL_FILE;
$info = stream_get_meta_data($data);
if ($info['wrapper_type'] == 'PHP'
&& $info['stream_type'] == 'Input') {
$fp = fopen('php://memory', 'w+');
stream_copy_to_stream($data, $fp);
rewind($fp);
} else {
$fp = $data;
}
break;
case $mode & self::SOURCE_LOCAL_FILE:
if (!is_file($data)) {
user_error("$data is not a valid file");
return false;
}
$fp = @fopen($data, 'rb');
if (!$fp) {
return false;
}
}
if (isset($fp)) {
$stat = fstat($fp);
$size = !empty($stat) ? $stat['size'] : 0;
if ($local_start >= 0) {
fseek($fp, $local_start);
$size-= $local_start;
}
} elseif ($dataCallback) {
$size = 0;
} else {
$size = strlen($data);
}
$sent = 0;
$size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 :
$size;
$sftp_packet_size = $this->max_sftp_packet;
// make the SFTP packet be exactly the SFTP packet size by
including the bytes in the NET_SFTP_WRITE packets "header"
$sftp_packet_size-= strlen($handle) + 25;
$i = $j = 0;
while ($dataCallback || ($size === 0 || $sent < $size)) {
if ($dataCallback) {
$temp = call_user_func($dataCallback, $sftp_packet_size);
if (is_null($temp)) {
break;
}
} else {
$temp = isset($fp) ? fread($fp, $sftp_packet_size) :
substr($data, $sent, $sftp_packet_size);
if ($temp === false || $temp === '') {
break;
}
}
$subtemp = $offset + $sent;
$packet = pack('Na*N3a*', strlen($handle), $handle,
$subtemp / 4294967296, $subtemp, strlen($temp), $temp);
if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet, $j))
{
if ($mode & self::SOURCE_LOCAL_FILE) {
fclose($fp);
}
return false;
}
$sent+= strlen($temp);
if (is_callable($progressCallback)) {
call_user_func($progressCallback, $sent);
}
$i++;
$j++;
if ($i == NET_SFTP_UPLOAD_QUEUE_SIZE) {
if (!$this->_read_put_responses($i)) {
$i = 0;
break;
}
$i = 0;
}
}
if (!$this->_read_put_responses($i)) {
if ($mode & self::SOURCE_LOCAL_FILE) {
fclose($fp);
}
$this->_close_handle($handle);
return false;
}
if ($mode & self::SOURCE_LOCAL_FILE) {
if ($this->preserveTime) {
$stat = fstat($fp);
$this->touch($remote_file, $stat['mtime'],
$stat['atime']);
}
if (isset($fp) && is_resource($fp)) {
fclose($fp);
}
}
return $this->_close_handle($handle);
}
/**
* Reads multiple successive SSH_FXP_WRITE responses
*
* Sending an SSH_FXP_WRITE packet and immediately reading its response
isn't as efficient as blindly sending out $i
* SSH_FXP_WRITEs, in succession, and then reading $i responses.
*
* @param int $i
* @return bool
* @access private
*/
function _read_put_responses($i)
{
while ($i--) {
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
break;
}
}
return $i < 0;
}
/**
* Close handle
*
* @param string $handle
* @return bool
* @access private
*/
function _close_handle($handle)
{
if (!$this->_send_sftp_packet(NET_SFTP_CLOSE,
pack('Na*', strlen($handle), $handle))) {
return false;
}
// "The client MUST release all resources associated with the
handle regardless of the status."
// --
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
return false;
}
return true;
}
/**
* Downloads a file from the SFTP server.
*
* Returns a string containing the contents of $remote_file if
$local_file is left undefined or a boolean false if
* the operation was unsuccessful. If $local_file is defined, returns
true or false depending on the success of the
* operation.
*
* $offset and $length can be used to download files in chunks.
*
* @param string $remote_file
* @param string $local_file
* @param int $offset
* @param int $length
* @param callable|null $progressCallback
* @return mixed
* @access public
*/
function get($remote_file, $local_file = false, $offset = 0, $length =
-1, $progressCallback = null)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$remote_file = $this->_realpath($remote_file);
if ($remote_file === false) {
return false;
}
$packet = pack('Na*N2', strlen($remote_file),
$remote_file, NET_SFTP_OPEN_READ, 0);
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or
SSH_FX_PERMISSION_DENIED
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or
SSH_FXP_STATUS');
return false;
}
if (is_resource($local_file)) {
$fp = $local_file;
$stat = fstat($fp);
$res_offset = $stat['size'];
} else {
$res_offset = 0;
if ($local_file !== false && !is_callable($local_file))
{
$fp = fopen($local_file, 'wb');
if (!$fp) {
return false;
}
} else {
$content = '';
}
}
$fclose_check = $local_file !== false &&
!is_callable($local_file) && !is_resource($local_file);
$start = $offset;
$read = 0;
while (true) {
$i = 0;
while ($i < NET_SFTP_QUEUE_SIZE && ($length < 0
|| $read < $length)) {
$tempoffset = $start + $read;
$packet_size = $length > 0 ?
min($this->max_sftp_packet, $length - $read) :
$this->max_sftp_packet;
$packet = pack('Na*N3', strlen($handle), $handle,
$tempoffset / 4294967296, $tempoffset, $packet_size);
if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet,
$i)) {
if ($fclose_check) {
fclose($fp);
}
return false;
}
$packet = null;
$read+= $packet_size;
$i++;
}
if (!$i) {
break;
}
$packets_sent = $i - 1;
$clear_responses = false;
while ($i > 0) {
$i--;
if ($clear_responses) {
$this->_get_sftp_packet($packets_sent - $i);
continue;
} else {
$response = $this->_get_sftp_packet($packets_sent -
$i);
}
switch ($this->packet_type) {
case NET_SFTP_DATA:
$temp = substr($response, 4);
$offset+= strlen($temp);
if ($local_file === false) {
$content.= $temp;
} elseif (is_callable($local_file)) {
$local_file($temp);
} else {
fputs($fp, $temp);
}
if (is_callable($progressCallback)) {
call_user_func($progressCallback, $offset);
}
$temp = null;
break;
case NET_SFTP_STATUS:
// could, in theory, return false if
!strlen($content) but we'll hold off for the time being
$this->_logError($response);
$clear_responses = true; // don't break out of
the loop yet, so we can read the remaining responses
break;
default:
if ($fclose_check) {
fclose($fp);
}
// maybe the file was successfully transferred,
maybe it wasn't
if ($this->channel_close) {
$this->_init_sftp_connection();
return false;
} else {
user_error('Expected SSH_FX_DATA or
SSH_FXP_STATUS');
}
}
$response = null;
}
if ($clear_responses) {
break;
}
}
if ($length > 0 && $length <= $offset - $start) {
if ($local_file === false) {
$content = substr($content, 0, $length);
} else {
ftruncate($fp, $length + $res_offset);
}
}
if ($fclose_check) {
fclose($fp);
if ($this->preserveTime) {
$stat = $this->stat($remote_file);
touch($local_file, $stat['mtime'],
$stat['atime']);
}
}
if (!$this->_close_handle($handle)) {
return false;
}
// if $content isn't set that means a file was written to
return isset($content) ? $content : true;
}
/**
* Deletes a file on the SFTP server.
*
* @param string $path
* @param bool $recursive
* @return bool
* @access public
*/
function delete($path, $recursive = true)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
if (is_object($path)) {
// It's an object. Cast it as string before we check
anything else.
$path = (string) $path;
}
if (!is_string($path) || $path == '') {
return false;
}
$path = $this->_realpath($path);
if ($path === false) {
return false;
}
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
if (!$this->_send_sftp_packet(NET_SFTP_REMOVE,
pack('Na*', strlen($path), $path))) {
return false;
}
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}
// if $status isn't SSH_FX_OK it's probably
SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
if (!$recursive) {
return false;
}
$i = 0;
$result = $this->_delete_recursive($path, $i);
$this->_read_put_responses($i);
return $result;
}
$this->_remove_from_stat_cache($path);
return true;
}
/**
* Recursively deletes directories on the SFTP server
*
* Minimizes directory lookups and SSH_FXP_STATUS requests for speed.
*
* @param string $path
* @param int $i
* @return bool
* @access private
*/
function _delete_recursive($path, &$i)
{
if (!$this->_read_put_responses($i)) {
return false;
}
$i = 0;
$entries = $this->_list($path, true);
// normally $entries would have at least . and .. but it might not
if the directories
// permissions didn't allow reading
if (empty($entries)) {
return false;
}
unset($entries['.'], $entries['..']);
foreach ($entries as $filename => $props) {
if (!isset($props['type'])) {
return false;
}
$temp = $path . '/' . $filename;
if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
if (!$this->_delete_recursive($temp, $i)) {
return false;
}
} else {
if (!$this->_send_sftp_packet(NET_SFTP_REMOVE,
pack('Na*', strlen($temp), $temp))) {
return false;
}
$this->_remove_from_stat_cache($temp);
$i++;
if ($i >= NET_SFTP_QUEUE_SIZE) {
if (!$this->_read_put_responses($i)) {
return false;
}
$i = 0;
}
}
}
if (!$this->_send_sftp_packet(NET_SFTP_RMDIR,
pack('Na*', strlen($path), $path))) {
return false;
}
$this->_remove_from_stat_cache($path);
$i++;
if ($i >= NET_SFTP_QUEUE_SIZE) {
if (!$this->_read_put_responses($i)) {
return false;
}
$i = 0;
}
return true;
}
/**
* Checks whether a file or directory exists
*
* @param string $path
* @return bool
* @access public
*/
function file_exists($path)
{
if ($this->use_stat_cache) {
$path = $this->_realpath($path);
$result = $this->_query_stat_cache($path);
if (isset($result)) {
// return true if $result is an array or if it's an
stdClass object
return $result !== false;
}
}
return $this->stat($path) !== false;
}
/**
* Tells whether the filename is a directory
*
* @param string $path
* @return bool
* @access public
*/
function is_dir($path)
{
$result = $this->_get_stat_cache_prop($path, 'type');
if ($result === false) {
return false;
}
return $result === NET_SFTP_TYPE_DIRECTORY;
}
/**
* Tells whether the filename is a regular file
*
* @param string $path
* @return bool
* @access public
*/
function is_file($path)
{
$result = $this->_get_stat_cache_prop($path, 'type');
if ($result === false) {
return false;
}
return $result === NET_SFTP_TYPE_REGULAR;
}
/**
* Tells whether the filename is a symbolic link
*
* @param string $path
* @return bool
* @access public
*/
function is_link($path)
{
$result = $this->_get_lstat_cache_prop($path, 'type');
if ($result === false) {
return false;
}
return $result === NET_SFTP_TYPE_SYMLINK;
}
/**
* Tells whether a file exists and is readable
*
* @param string $path
* @return bool
* @access public
*/
function is_readable($path)
{
$path = $this->_realpath($path);
$packet = pack('Na*N2', strlen($path), $path,
NET_SFTP_OPEN_READ, 0);
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
return true;
case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or
SSH_FX_PERMISSION_DENIED
return false;
default:
user_error('Expected SSH_FXP_HANDLE or
SSH_FXP_STATUS');
return false;
}
}
/**
* Tells whether the filename is writable
*
* @param string $path
* @return bool
* @access public
*/
function is_writable($path)
{
$path = $this->_realpath($path);
$packet = pack('Na*N2', strlen($path), $path,
NET_SFTP_OPEN_WRITE, 0);
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
return true;
case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or
SSH_FX_PERMISSION_DENIED
return false;
default:
user_error('Expected SSH_FXP_HANDLE or
SSH_FXP_STATUS');
return false;
}
}
/**
* Tells whether the filename is writeable
*
* Alias of is_writable
*
* @param string $path
* @return bool
* @access public
*/
function is_writeable($path)
{
return $this->is_writable($path);
}
/**
* Gets last access time of file
*
* @param string $path
* @return mixed
* @access public
*/
function fileatime($path)
{
return $this->_get_stat_cache_prop($path, 'atime');
}
/**
* Gets file modification time
*
* @param string $path
* @return mixed
* @access public
*/
function filemtime($path)
{
return $this->_get_stat_cache_prop($path, 'mtime');
}
/**
* Gets file permissions
*
* @param string $path
* @return mixed
* @access public
*/
function fileperms($path)
{
return $this->_get_stat_cache_prop($path,
'permissions');
}
/**
* Gets file owner
*
* @param string $path
* @return mixed
* @access public
*/
function fileowner($path)
{
return $this->_get_stat_cache_prop($path, 'uid');
}
/**
* Gets file group
*
* @param string $path
* @return mixed
* @access public
*/
function filegroup($path)
{
return $this->_get_stat_cache_prop($path, 'gid');
}
/**
* Gets file size
*
* @param string $path
* @return mixed
* @access public
*/
function filesize($path)
{
return $this->_get_stat_cache_prop($path, 'size');
}
/**
* Gets file type
*
* @param string $path
* @return mixed
* @access public
*/
function filetype($path)
{
$type = $this->_get_stat_cache_prop($path, 'type');
if ($type === false) {
return false;
}
switch ($type) {
case NET_SFTP_TYPE_BLOCK_DEVICE:
return 'block';
case NET_SFTP_TYPE_CHAR_DEVICE:
return 'char';
case NET_SFTP_TYPE_DIRECTORY:
return 'dir';
case NET_SFTP_TYPE_FIFO:
return 'fifo';
case NET_SFTP_TYPE_REGULAR:
return 'file';
case NET_SFTP_TYPE_SYMLINK:
return 'link';
default:
return false;
}
}
/**
* Return a stat properity
*
* Uses cache if appropriate.
*
* @param string $path
* @param string $prop
* @return mixed
* @access private
*/
function _get_stat_cache_prop($path, $prop)
{
return $this->_get_xstat_cache_prop($path, $prop,
'stat');
}
/**
* Return an lstat properity
*
* Uses cache if appropriate.
*
* @param string $path
* @param string $prop
* @return mixed
* @access private
*/
function _get_lstat_cache_prop($path, $prop)
{
return $this->_get_xstat_cache_prop($path, $prop,
'lstat');
}
/**
* Return a stat or lstat properity
*
* Uses cache if appropriate.
*
* @param string $path
* @param string $prop
* @param mixed $type
* @return mixed
* @access private
*/
function _get_xstat_cache_prop($path, $prop, $type)
{
if ($this->use_stat_cache) {
$path = $this->_realpath($path);
$result = $this->_query_stat_cache($path);
if (is_object($result) && isset($result->$type)) {
return $result->{$type}[$prop];
}
}
$result = $this->$type($path);
if ($result === false || !isset($result[$prop])) {
return false;
}
return $result[$prop];
}
/**
* Renames a file or a directory on the SFTP server
*
* @param string $oldname
* @param string $newname
* @return bool
* @access public
*/
function rename($oldname, $newname)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$oldname = $this->_realpath($oldname);
$newname = $this->_realpath($newname);
if ($oldname === false || $newname === false) {
return false;
}
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
$packet = pack('Na*Na*', strlen($oldname), $oldname,
strlen($newname), $newname);
if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}
// if $status isn't SSH_FX_OK it's probably
SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
return false;
}
// don't move the stat cache entry over since this operation
could very well change the
// atime and mtime attributes
//$this->_update_stat_cache($newname,
$this->_query_stat_cache($oldname));
$this->_remove_from_stat_cache($oldname);
$this->_remove_from_stat_cache($newname);
return true;
}
/**
* Parse Attributes
*
* See '7. File Attributes' of draft-ietf-secsh-filexfer-13
for more info.
*
* @param string $response
* @return array
* @access private
*/
function _parseAttributes(&$response)
{
$attr = array();
if (strlen($response) < 4) {
user_error('Malformed file attributes');
return array();
}
extract(unpack('Nflags',
$this->_string_shift($response, 4)));
// SFTPv4+ have a type field (a byte) that follows the above flag
field
foreach ($this->attributes as $key => $value) {
switch ($flags & $key) {
case NET_SFTP_ATTR_SIZE: // 0x00000001
// The size attribute is defined as an unsigned 64-bit
integer.
// The following will use floats on 32-bit platforms,
if necessary.
// As can be seen in the BigInteger class, floats are
generally
// IEEE 754 binary64 "double precision" on
such platforms and
// as such can represent integers of at least 2^50
without loss
// of precision. Interpreted in filesize, 2^50 bytes =
1024 TiB.
$attr['size'] =
hexdec(bin2hex($this->_string_shift($response, 8)));
break;
case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only)
if (strlen($response) < 8) {
user_error('Malformed file attributes');
return $attr;
}
$attr+= unpack('Nuid/Ngid',
$this->_string_shift($response, 8));
break;
case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004
if (strlen($response) < 4) {
user_error('Malformed file attributes');
return $attr;
}
$attr+= unpack('Npermissions',
$this->_string_shift($response, 4));
// mode == permissions; permissions was the original
array key and is retained for bc purposes.
// mode was added because that's the more industry
standard terminology
$attr+= array('mode' =>
$attr['permissions']);
$fileType =
$this->_parseMode($attr['permissions']);
if ($fileType !== false) {
$attr+= array('type' => $fileType);
}
break;
case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008
if (strlen($response) < 8) {
user_error('Malformed file attributes');
return $attr;
}
$attr+= unpack('Natime/Nmtime',
$this->_string_shift($response, 8));
break;
case NET_SFTP_ATTR_EXTENDED: // 0x80000000
if (strlen($response) < 4) {
user_error('Malformed file attributes');
return $attr;
}
extract(unpack('Ncount',
$this->_string_shift($response, 4)));
for ($i = 0; $i < $count; $i++) {
if (strlen($response) < 4) {
user_error('Malformed file
attributes');
return $attr;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$key = $this->_string_shift($response, $length);
if (strlen($response) < 4) {
user_error('Malformed file
attributes');
return $attr;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$attr[$key] = $this->_string_shift($response,
$length);
}
}
}
return $attr;
}
/**
* Attempt to identify the file type
*
* Quoting the SFTP RFC, "Implementations MUST NOT send bits that
are not defined" but they seem to anyway
*
* @param int $mode
* @return int
* @access private
*/
function _parseMode($mode)
{
// values come from
http://lxr.free-electrons.com/source/include/uapi/linux/stat.h#L12
// see, also, http://linux.die.net/man/2/stat
switch ($mode & 0170000) {// ie. 1111 0000 0000 0000
case 0000000: // no file type specified - figure out the file
type using alternative means
return false;
case 0040000:
return NET_SFTP_TYPE_DIRECTORY;
case 0100000:
return NET_SFTP_TYPE_REGULAR;
case 0120000:
return NET_SFTP_TYPE_SYMLINK;
// new types introduced in SFTPv5+
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
case 0010000: // named pipe (fifo)
return NET_SFTP_TYPE_FIFO;
case 0020000: // character special
return NET_SFTP_TYPE_CHAR_DEVICE;
case 0060000: // block special
return NET_SFTP_TYPE_BLOCK_DEVICE;
case 0140000: // socket
return NET_SFTP_TYPE_SOCKET;
case 0160000: // whiteout
// "SPECIAL should be used for files that are of
// a known type which cannot be expressed in the
protocol"
return NET_SFTP_TYPE_SPECIAL;
default:
return NET_SFTP_TYPE_UNKNOWN;
}
}
/**
* Parse Longname
*
* SFTPv3 doesn't provide any easy way of identifying a file type.
You could try to open
* a file as a directory and see if an error is returned or you could
try to parse the
* SFTPv3-specific longname field of the SSH_FXP_NAME packet.
That's what this function does.
* The result is returned using the
* {@link
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 SFTPv4
type constants}.
*
* If the longname is in an unrecognized format bool(false) is
returned.
*
* @param string $longname
* @return mixed
* @access private
*/
function _parseLongname($longname)
{
// http://en.wikipedia.org/wiki/Unix_file_types
//
http://en.wikipedia.org/wiki/Filesystem_permissions#Notation_of_traditional_Unix_permissions
if (preg_match('#^[^/]([r-][w-][xstST-]){3}#',
$longname)) {
switch ($longname[0]) {
case '-':
return NET_SFTP_TYPE_REGULAR;
case 'd':
return NET_SFTP_TYPE_DIRECTORY;
case 'l':
return NET_SFTP_TYPE_SYMLINK;
default:
return NET_SFTP_TYPE_SPECIAL;
}
}
return false;
}
/**
* Sends SFTP Packets
*
* See '6. General Packet Format' of
draft-ietf-secsh-filexfer-13 for more info.
*
* @param int $type
* @param string $data
* @param int $request_id
* @see self::_get_sftp_packet()
* @see self::_send_channel_packet()
* @return bool
* @access private
*/
function _send_sftp_packet($type, $data, $request_id = 1)
{
// in SSH2.php the timeout is cumulative per function call. eg.
exec() will
// timeout after 10s. but for SFTP.php it's cumulative per
packet
$this->curTimeout = $this->timeout;
$packet = $this->use_request_id ?
pack('NCNa*', strlen($data) + 5, $type, $request_id,
$data) :
pack('NCa*', strlen($data) + 1, $type, $data);
$start = strtok(microtime(), ' ') + strtok('');
// http://php.net/microtime#61838
$result = $this->_send_channel_packet(self::CHANNEL, $packet);
$stop = strtok(microtime(), ' ') + strtok('');
if (defined('NET_SFTP_LOGGING')) {
$packet_type = '-> ' .
$this->packet_types[$type] .
' (' . round($stop - $start, 4) .
's)';
if (NET_SFTP_LOGGING == self::LOG_REALTIME) {
switch (PHP_SAPI) {
case 'cli':
$start = $stop = "\r\n";
break;
default:
$start = '<pre>';
$stop = '</pre>';
}
echo $start . $this->_format_log(array($data),
array($packet_type)) . $stop;
@flush();
@ob_flush();
} else {
$this->packet_type_log[] = $packet_type;
if (NET_SFTP_LOGGING == self::LOG_COMPLEX) {
$this->packet_log[] = $data;
}
}
}
return $result;
}
/**
* Resets a connection for re-use
*
* @param int $reason
* @access private
*/
function _reset_connection($reason)
{
parent::_reset_connection($reason);
$this->use_request_id = false;
$this->pwd = false;
$this->requestBuffer = array();
}
/**
* Receives SFTP Packets
*
* See '6. General Packet Format' of
draft-ietf-secsh-filexfer-13 for more info.
*
* Incidentally, the number of SSH_MSG_CHANNEL_DATA messages has no
bearing on the number of SFTP packets present.
* There can be one SSH_MSG_CHANNEL_DATA messages containing two SFTP
packets or there can be two SSH_MSG_CHANNEL_DATA
* messages containing one SFTP packet.
*
* @see self::_send_sftp_packet()
* @return string
* @access private
*/
function _get_sftp_packet($request_id = null)
{
$this->channel_close = false;
if (isset($request_id) &&
isset($this->requestBuffer[$request_id])) {
$this->packet_type =
$this->requestBuffer[$request_id]['packet_type'];
$temp =
$this->requestBuffer[$request_id]['packet'];
unset($this->requestBuffer[$request_id]);
return $temp;
}
// in SSH2.php the timeout is cumulative per function call. eg.
exec() will
// timeout after 10s. but for SFTP.php it's cumulative per
packet
$this->curTimeout = $this->timeout;
$start = strtok(microtime(), ' ') + strtok('');
// http://php.net/microtime#61838
// SFTP packet length
while (strlen($this->packet_buffer) < 4) {
$temp = $this->_get_channel_packet(self::CHANNEL, true);
if ($temp === true) {
if ($this->channel_status[self::CHANNEL] ===
NET_SSH2_MSG_CHANNEL_CLOSE) {
$this->channel_close = true;
}
$this->packet_type = false;
$this->packet_buffer = '';
return false;
}
$this->packet_buffer.= $temp;
}
if (strlen($this->packet_buffer) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($this->packet_buffer, 4)));
$tempLength = $length;
$tempLength-= strlen($this->packet_buffer);
// 256 * 1024 is what SFTP_MAX_MSG_LENGTH is set to in
OpenSSH's sftp-common.h
if ($tempLength > 256 * 1024) {
user_error('Invalid SFTP packet size');
return false;
}
// SFTP packet type and data payload
while ($tempLength > 0) {
$temp = $this->_get_channel_packet(self::CHANNEL, true);
if (is_bool($temp)) {
$this->packet_type = false;
$this->packet_buffer = '';
return false;
}
$this->packet_buffer.= $temp;
$tempLength-= strlen($temp);
}
$stop = strtok(microtime(), ' ') + strtok('');
$this->packet_type =
ord($this->_string_shift($this->packet_buffer));
if ($this->use_request_id) {
extract(unpack('Npacket_id',
$this->_string_shift($this->packet_buffer, 4))); // remove the
request id
$length-= 5; // account for the request id and the packet type
} else {
$length-= 1; // account for the packet type
}
$packet = $this->_string_shift($this->packet_buffer,
$length);
if (defined('NET_SFTP_LOGGING')) {
$packet_type = '<- ' .
$this->packet_types[$this->packet_type] .
' (' . round($stop - $start, 4) .
's)';
if (NET_SFTP_LOGGING == self::LOG_REALTIME) {
switch (PHP_SAPI) {
case 'cli':
$start = $stop = "\r\n";
break;
default:
$start = '<pre>';
$stop = '</pre>';
}
echo $start . $this->_format_log(array($packet),
array($packet_type)) . $stop;
@flush();
@ob_flush();
} else {
$this->packet_type_log[] = $packet_type;
if (NET_SFTP_LOGGING == self::LOG_COMPLEX) {
$this->packet_log[] = $packet;
}
}
}
if (isset($request_id) && $this->use_request_id
&& $packet_id != $request_id) {
$this->requestBuffer[$packet_id] = array(
'packet_type' => $this->packet_type,
'packet' => $packet
);
return $this->_get_sftp_packet($request_id);
}
return $packet;
}
/**
* Returns a log of the packets that have been sent and received.
*
* Returns a string if NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX, an
array if NET_SFTP_LOGGING == NET_SFTP_LOG_SIMPLE and false if
!defined('NET_SFTP_LOGGING')
*
* @access public
* @return string or Array
*/
function getSFTPLog()
{
if (!defined('NET_SFTP_LOGGING')) {
return false;
}
switch (NET_SFTP_LOGGING) {
case self::LOG_COMPLEX:
return $this->_format_log($this->packet_log,
$this->packet_type_log);
break;
//case self::LOG_SIMPLE:
default:
return $this->packet_type_log;
}
}
/**
* Returns all errors
*
* @return array
* @access public
*/
function getSFTPErrors()
{
return $this->sftp_errors;
}
/**
* Returns the last error
*
* @return string
* @access public
*/
function getLastSFTPError()
{
return count($this->sftp_errors) ?
$this->sftp_errors[count($this->sftp_errors) - 1] : '';
}
/**
* Get supported SFTP versions
*
* @return array
* @access public
*/
function getSupportedVersions()
{
$temp = array('version' => $this->version);
if (isset($this->extensions['versions'])) {
$temp['extensions'] =
$this->extensions['versions'];
}
return $temp;
}
/**
* Disconnect
*
* @param int $reason
* @return bool
* @access private
*/
function _disconnect($reason)
{
$this->pwd = false;
parent::_disconnect($reason);
}
/**
* Enable Date Preservation
*
* @access public
*/
function enableDatePreservation()
{
$this->preserveTime = true;
}
/**
* Disable Date Preservation
*
* @access public
*/
function disableDatePreservation()
{
$this->preserveTime = false;
}
}
vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php000064400000146730151156520660015733
0ustar00<?php
/**
* Pure-PHP implementation of SSHv1.
*
* PHP version 5
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $ssh = new \phpseclib\Net\SSH1('www.domain.tld');
* if (!$ssh->login('username', 'password')) {
* exit('Login Failed');
* }
*
* echo $ssh->exec('ls -la');
* ?>
* </code>
*
* Here's another short example:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $ssh = new \phpseclib\Net\SSH1('www.domain.tld');
* if (!$ssh->login('username', 'password')) {
* exit('Login Failed');
* }
*
* echo $ssh->read('username@username:~$');
* $ssh->write("ls -la\n");
* echo $ssh->read('username@username:~$');
* ?>
* </code>
*
* More information on the SSHv1 specification can be found by reading
* {@link http://www.snailbook.com/docs/protocol-1.5.txt protocol-1.5.txt}.
*
* @category Net
* @package SSH1
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Net;
use phpseclib\Crypt\DES;
use phpseclib\Crypt\Random;
use phpseclib\Crypt\TripleDES;
use phpseclib\Math\BigInteger;
/**
* Pure-PHP implementation of SSHv1.
*
* @package SSH1
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class SSH1
{
/**#@+
* Encryption Methods
*
* @see \phpseclib\Net\SSH1::getSupportedCiphers()
* @access public
*/
/**
* No encryption
*
* Not supported.
*/
const CIPHER_NONE = 0;
/**
* IDEA in CFB mode
*
* Not supported.
*/
const CIPHER_IDEA = 1;
/**
* DES in CBC mode
*/
const CIPHER_DES = 2;
/**
* Triple-DES in CBC mode
*
* All implementations are required to support this
*/
const CIPHER_3DES = 3;
/**
* TRI's Simple Stream encryption CBC
*
* Not supported nor is it defined in the official SSH1 specs.
OpenSSH, however, does define it (see cipher.h),
* although it doesn't use it (see cipher.c)
*/
const CIPHER_BROKEN_TSS = 4;
/**
* RC4
*
* Not supported.
*
* @internal According to the SSH1 specs:
*
* "The first 16 bytes of the session key are used as the
key for
* the server to client direction. The remaining 16 bytes are
used
* as the key for the client to server direction. This gives
* independent 128-bit keys for each direction."
*
* This library currently only supports encryption when the same
key is being used for both directions. This is
* because there's only one $crypto object. Two could be
added ($encrypt and $decrypt, perhaps).
*/
const CIPHER_RC4 = 5;
/**
* Blowfish
*
* Not supported nor is it defined in the official SSH1 specs.
OpenSSH, however, defines it (see cipher.h) and
* uses it (see cipher.c)
*/
const CIPHER_BLOWFISH = 6;
/**#@-*/
/**#@+
* Authentication Methods
*
* @see \phpseclib\Net\SSH1::getSupportedAuthentications()
* @access public
*/
/**
* .rhosts or /etc/hosts.equiv
*/
const AUTH_RHOSTS = 1;
/**
* pure RSA authentication
*/
const AUTH_RSA = 2;
/**
* password authentication
*
* This is the only method that is supported by this library.
*/
const AUTH_PASSWORD = 3;
/**
* .rhosts with RSA host authentication
*/
const AUTH_RHOSTS_RSA = 4;
/**#@-*/
/**#@+
* Terminal Modes
*
* @link
http://3sp.com/content/developer/maverick-net/docs/Maverick.SSH.PseudoTerminalModesMembers.html
* @access private
*/
const TTY_OP_END = 0;
/**#@-*/
/**
* The Response Type
*
* @see \phpseclib\Net\SSH1::_get_binary_packet()
* @access private
*/
const RESPONSE_TYPE = 1;
/**
* The Response Data
*
* @see \phpseclib\Net\SSH1::_get_binary_packet()
* @access private
*/
const RESPONSE_DATA = 2;
/**#@+
* Execution Bitmap Masks
*
* @see \phpseclib\Net\SSH1::bitmap
* @access private
*/
const MASK_CONSTRUCTOR = 0x00000001;
const MASK_CONNECTED = 0x00000002;
const MASK_LOGIN = 0x00000004;
const MASK_SHELL = 0x00000008;
/**#@-*/
/**#@+
* @access public
* @see \phpseclib\Net\SSH1::getLog()
*/
/**
* Returns the message numbers
*/
const LOG_SIMPLE = 1;
/**
* Returns the message content
*/
const LOG_COMPLEX = 2;
/**
* Outputs the content real-time
*/
const LOG_REALTIME = 3;
/**
* Dumps the content real-time to a file
*/
const LOG_REALTIME_FILE = 4;
/**#@-*/
/**#@+
* @access public
* @see \phpseclib\Net\SSH1::read()
*/
/**
* Returns when a string matching $expect exactly is found
*/
const READ_SIMPLE = 1;
/**
* Returns when a string matching the regular expression $expect is
found
*/
const READ_REGEX = 2;
/**#@-*/
/**
* The SSH identifier
*
* @var string
* @access private
*/
var $identifier = 'SSH-1.5-phpseclib';
/**
* The Socket Object
*
* @var object
* @access private
*/
var $fsock;
/**
* The cryptography object
*
* @var object
* @access private
*/
var $crypto = false;
/**
* Execution Bitmap
*
* The bits that are set represent functions that have been called
already. This is used to determine
* if a requisite function has been successfully executed. If not, an
error should be thrown.
*
* @var int
* @access private
*/
var $bitmap = 0;
/**
* The Server Key Public Exponent
*
* Logged for debug purposes
*
* @see self::getServerKeyPublicExponent()
* @var string
* @access private
*/
var $server_key_public_exponent;
/**
* The Server Key Public Modulus
*
* Logged for debug purposes
*
* @see self::getServerKeyPublicModulus()
* @var string
* @access private
*/
var $server_key_public_modulus;
/**
* The Host Key Public Exponent
*
* Logged for debug purposes
*
* @see self::getHostKeyPublicExponent()
* @var string
* @access private
*/
var $host_key_public_exponent;
/**
* The Host Key Public Modulus
*
* Logged for debug purposes
*
* @see self::getHostKeyPublicModulus()
* @var string
* @access private
*/
var $host_key_public_modulus;
/**
* Supported Ciphers
*
* Logged for debug purposes
*
* @see self::getSupportedCiphers()
* @var array
* @access private
*/
var $supported_ciphers = array(
self::CIPHER_NONE => 'No encryption',
self::CIPHER_IDEA => 'IDEA in CFB mode',
self::CIPHER_DES => 'DES in CBC mode',
self::CIPHER_3DES => 'Triple-DES in CBC mode',
self::CIPHER_BROKEN_TSS => 'TRI\'s Simple Stream
encryption CBC',
self::CIPHER_RC4 => 'RC4',
self::CIPHER_BLOWFISH => 'Blowfish'
);
/**
* Supported Authentications
*
* Logged for debug purposes
*
* @see self::getSupportedAuthentications()
* @var array
* @access private
*/
var $supported_authentications = array(
self::AUTH_RHOSTS => '.rhosts or
/etc/hosts.equiv',
self::AUTH_RSA => 'pure RSA authentication',
self::AUTH_PASSWORD => 'password authentication',
self::AUTH_RHOSTS_RSA => '.rhosts with RSA host
authentication'
);
/**
* Server Identification
*
* @see self::getServerIdentification()
* @var string
* @access private
*/
var $server_identification = '';
/**
* Protocol Flags
*
* @see self::__construct()
* @var array
* @access private
*/
var $protocol_flags = array();
/**
* Protocol Flag Log
*
* @see self::getLog()
* @var array
* @access private
*/
var $protocol_flag_log = array();
/**
* Message Log
*
* @see self::getLog()
* @var array
* @access private
*/
var $message_log = array();
/**
* Real-time log file pointer
*
* @see self::_append_log()
* @var resource
* @access private
*/
var $realtime_log_file;
/**
* Real-time log file size
*
* @see self::_append_log()
* @var int
* @access private
*/
var $realtime_log_size;
/**
* Real-time log file wrap boolean
*
* @see self::_append_log()
* @var bool
* @access private
*/
var $realtime_log_wrap;
/**
* Interactive Buffer
*
* @see self::read()
* @var array
* @access private
*/
var $interactiveBuffer = '';
/**
* Timeout
*
* @see self::setTimeout()
* @access private
*/
var $timeout;
/**
* Current Timeout
*
* @see self::_get_channel_packet()
* @access private
*/
var $curTimeout;
/**
* Log Boundary
*
* @see self::_format_log()
* @access private
*/
var $log_boundary = ':';
/**
* Log Long Width
*
* @see self::_format_log()
* @access private
*/
var $log_long_width = 65;
/**
* Log Short Width
*
* @see self::_format_log()
* @access private
*/
var $log_short_width = 16;
/**
* Hostname
*
* @see self::__construct()
* @see self::_connect()
* @var string
* @access private
*/
var $host;
/**
* Port Number
*
* @see self::__construct()
* @see self::_connect()
* @var int
* @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 self::__construct()
* @see self::_connect()
* @var int
* @access private
*/
var $connectionTimeout;
/**
* Default cipher
*
* @see self::__construct()
* @see self::_connect()
* @var int
* @access private
*/
var $cipher;
/**
* Default Constructor.
*
* Connects to an SSHv1 server
*
* @param string $host
* @param int $port
* @param int $timeout
* @param int $cipher
* @return \phpseclib\Net\SSH1
* @access public
*/
function __construct($host, $port = 22, $timeout = 10, $cipher =
self::CIPHER_3DES)
{
$this->protocol_flags = array(
1 => 'NET_SSH1_MSG_DISCONNECT',
2 => 'NET_SSH1_SMSG_PUBLIC_KEY',
3 => 'NET_SSH1_CMSG_SESSION_KEY',
4 => 'NET_SSH1_CMSG_USER',
9 => 'NET_SSH1_CMSG_AUTH_PASSWORD',
10 => 'NET_SSH1_CMSG_REQUEST_PTY',
12 => 'NET_SSH1_CMSG_EXEC_SHELL',
13 => 'NET_SSH1_CMSG_EXEC_CMD',
14 => 'NET_SSH1_SMSG_SUCCESS',
15 => 'NET_SSH1_SMSG_FAILURE',
16 => 'NET_SSH1_CMSG_STDIN_DATA',
17 => 'NET_SSH1_SMSG_STDOUT_DATA',
18 => 'NET_SSH1_SMSG_STDERR_DATA',
19 => 'NET_SSH1_CMSG_EOF',
20 => 'NET_SSH1_SMSG_EXITSTATUS',
33 => 'NET_SSH1_CMSG_EXIT_CONFIRMATION'
);
$this->_define_array($this->protocol_flags);
$this->host = $host;
$this->port = $port;
$this->connectionTimeout = $timeout;
$this->cipher = $cipher;
}
/**
* Connect to an SSHv1 server
*
* @return bool
* @access private
*/
function _connect()
{
$this->fsock = @fsockopen($this->host, $this->port,
$errno, $errstr, $this->connectionTimeout);
if (!$this->fsock) {
user_error(rtrim("Cannot connect to
{$this->host}:{$this->port}. Error $errno. $errstr"));
return false;
}
$this->server_identification = $init_line =
fgets($this->fsock, 255);
if (defined('NET_SSH1_LOGGING')) {
$this->_append_log('<-',
$this->server_identification);
$this->_append_log('->', $this->identifier .
"\r\n");
}
if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line,
$parts)) {
user_error('Can only connect to SSH servers');
return false;
}
if ($parts[1][0] != 1) {
user_error("Cannot connect to SSH $parts[1]
servers");
return false;
}
fputs($this->fsock, $this->identifier."\r\n");
$response = $this->_get_binary_packet();
if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) {
user_error('Expected SSH_SMSG_PUBLIC_KEY');
return false;
}
$anti_spoofing_cookie =
$this->_string_shift($response[self::RESPONSE_DATA], 8);
$this->_string_shift($response[self::RESPONSE_DATA], 4);
if (strlen($response[self::RESPONSE_DATA]) < 2) {
return false;
}
$temp = unpack('nlen',
$this->_string_shift($response[self::RESPONSE_DATA], 2));
$server_key_public_exponent = new
BigInteger($this->_string_shift($response[self::RESPONSE_DATA],
ceil($temp['len'] / 8)), 256);
$this->server_key_public_exponent = $server_key_public_exponent;
if (strlen($response[self::RESPONSE_DATA]) < 2) {
return false;
}
$temp = unpack('nlen',
$this->_string_shift($response[self::RESPONSE_DATA], 2));
$server_key_public_modulus = new
BigInteger($this->_string_shift($response[self::RESPONSE_DATA],
ceil($temp['len'] / 8)), 256);
$this->server_key_public_modulus = $server_key_public_modulus;
$this->_string_shift($response[self::RESPONSE_DATA], 4);
if (strlen($response[self::RESPONSE_DATA]) < 2) {
return false;
}
$temp = unpack('nlen',
$this->_string_shift($response[self::RESPONSE_DATA], 2));
$host_key_public_exponent = new
BigInteger($this->_string_shift($response[self::RESPONSE_DATA],
ceil($temp['len'] / 8)), 256);
$this->host_key_public_exponent = $host_key_public_exponent;
if (strlen($response[self::RESPONSE_DATA]) < 2) {
return false;
}
$temp = unpack('nlen',
$this->_string_shift($response[self::RESPONSE_DATA], 2));
$host_key_public_modulus = new
BigInteger($this->_string_shift($response[self::RESPONSE_DATA],
ceil($temp['len'] / 8)), 256);
$this->host_key_public_modulus = $host_key_public_modulus;
$this->_string_shift($response[self::RESPONSE_DATA], 4);
// get a list of the supported ciphers
if (strlen($response[self::RESPONSE_DATA]) < 4) {
return false;
}
extract(unpack('Nsupported_ciphers_mask',
$this->_string_shift($response[self::RESPONSE_DATA], 4)));
foreach ($this->supported_ciphers as $mask => $name) {
if (($supported_ciphers_mask & (1 << $mask)) == 0) {
unset($this->supported_ciphers[$mask]);
}
}
// get a list of the supported authentications
if (strlen($response[self::RESPONSE_DATA]) < 4) {
return false;
}
extract(unpack('Nsupported_authentications_mask',
$this->_string_shift($response[self::RESPONSE_DATA], 4)));
foreach ($this->supported_authentications as $mask => $name)
{
if (($supported_authentications_mask & (1 << $mask))
== 0) {
unset($this->supported_authentications[$mask]);
}
}
$session_id = pack('H*',
md5($host_key_public_modulus->toBytes() .
$server_key_public_modulus->toBytes() . $anti_spoofing_cookie));
$session_key = Random::string(32);
$double_encrypted_session_key = $session_key ^ str_pad($session_id,
32, chr(0));
if
($server_key_public_modulus->compare($host_key_public_modulus) < 0) {
$double_encrypted_session_key = $this->_rsa_crypt(
$double_encrypted_session_key,
array(
$server_key_public_exponent,
$server_key_public_modulus
)
);
$double_encrypted_session_key = $this->_rsa_crypt(
$double_encrypted_session_key,
array(
$host_key_public_exponent,
$host_key_public_modulus
)
);
} else {
$double_encrypted_session_key = $this->_rsa_crypt(
$double_encrypted_session_key,
array(
$host_key_public_exponent,
$host_key_public_modulus
)
);
$double_encrypted_session_key = $this->_rsa_crypt(
$double_encrypted_session_key,
array(
$server_key_public_exponent,
$server_key_public_modulus
)
);
}
$cipher = isset($this->supported_ciphers[$this->cipher]) ?
$this->cipher : self::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);
if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_SESSION_KEY');
return false;
}
switch ($cipher) {
//case self::CIPHER_NONE:
// $this->crypto = new \phpseclib\Crypt\Null();
// break;
case self::CIPHER_DES:
$this->crypto = new DES();
$this->crypto->disablePadding();
$this->crypto->enableContinuousBuffer();
$this->crypto->setKey(substr($session_key, 0, 8));
break;
case self::CIPHER_3DES:
$this->crypto = new TripleDES(TripleDES::MODE_3CBC);
$this->crypto->disablePadding();
$this->crypto->enableContinuousBuffer();
$this->crypto->setKey(substr($session_key, 0, 24));
break;
//case self::CIPHER_RC4:
// $this->crypto = new RC4();
// $this->crypto->enableContinuousBuffer();
// $this->crypto->setKey(substr($session_key, 0,
16));
// break;
}
$response = $this->_get_binary_packet();
if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
user_error('Expected SSH_SMSG_SUCCESS');
return false;
}
$this->bitmap = self::MASK_CONNECTED;
return true;
}
/**
* Login
*
* @param string $username
* @param string $password
* @return bool
* @access public
*/
function login($username, $password = '')
{
if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
$this->bitmap |= self::MASK_CONSTRUCTOR;
if (!$this->_connect()) {
return false;
}
}
if (!($this->bitmap & self::MASK_CONNECTED)) {
return false;
}
$data = pack('CNa*', NET_SSH1_CMSG_USER,
strlen($username), $username);
if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_USER');
return false;
}
$response = $this->_get_binary_packet();
if ($response === true) {
return false;
}
if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
$this->bitmap |= self::MASK_LOGIN;
return true;
} elseif ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE)
{
user_error('Expected SSH_SMSG_SUCCESS or
SSH_SMSG_FAILURE');
return false;
}
$data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD,
strlen($password), $password);
if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_AUTH_PASSWORD');
return false;
}
// remove the username and password from the last logged packet
if (defined('NET_SSH1_LOGGING') &&
NET_SSH1_LOGGING == self::LOG_COMPLEX) {
$data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD,
strlen('password'), 'password');
$this->message_log[count($this->message_log) - 1] =
$data;
}
$response = $this->_get_binary_packet();
if ($response === true) {
return false;
}
if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
$this->bitmap |= self::MASK_LOGIN;
return true;
} elseif ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_FAILURE)
{
return false;
} else {
user_error('Expected SSH_SMSG_SUCCESS or
SSH_SMSG_FAILURE');
return false;
}
}
/**
* Set Timeout
*
* $ssh->exec('ping 127.0.0.1'); on a Linux host will
never return and will run indefinitely. setTimeout() makes it so
it'll timeout.
* Setting $timeout to false or 0 will mean there is no timeout.
*
* @param mixed $timeout
*/
function setTimeout($timeout)
{
$this->timeout = $this->curTimeout = $timeout;
}
/**
* Executes a command on a non-interactive shell, returns the output,
and quits.
*
* An SSH1 server will close the connection after a command has been
executed on a non-interactive shell. SSH2
* servers don't, however, this isn't an SSH2 client. The
way this works, on the server, is by initiating a
* shell with the -s option, as discussed in the following links:
*
* {@link http://www.faqs.org/docs/bashman/bashref_65.html
http://www.faqs.org/docs/bashman/bashref_65.html}
* {@link http://www.faqs.org/docs/bashman/bashref_62.html
http://www.faqs.org/docs/bashman/bashref_62.html}
*
* To execute further commands, a new \phpseclib\Net\SSH1 object will
need to be created.
*
* Returns false on failure and the output, otherwise.
*
* @see self::interactiveRead()
* @see self::interactiveWrite()
* @param string $cmd
* @param bool $block
* @return mixed
* @access public
*/
function exec($cmd, $block = true)
{
if (!($this->bitmap & self::MASK_LOGIN)) {
user_error('Operation disallowed prior to login()');
return false;
}
$data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD,
strlen($cmd), $cmd);
if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_EXEC_CMD');
return false;
}
if (!$block) {
return true;
}
$output = '';
$response = $this->_get_binary_packet();
if ($response !== false) {
do {
$output.= substr($response[self::RESPONSE_DATA], 4);
$response = $this->_get_binary_packet();
} while (is_array($response) &&
$response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_EXITSTATUS);
}
$data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION);
// i don't think it's really all that important if this
packet gets sent or not.
$this->_send_binary_packet($data);
fclose($this->fsock);
// reset the execution bitmap - a new \phpseclib\Net\SSH1 object
needs to be created.
$this->bitmap = 0;
return $output;
}
/**
* Creates an interactive shell
*
* @see self::interactiveRead()
* @see self::interactiveWrite()
* @return bool
* @access private
*/
function _initShell()
{
// connect using the sample parameters in protocol-1.5.txt.
// according to wikipedia.org's entry on text terminals,
"the fundamental type of application running on a text
// terminal is a command line interpreter or shell". thus,
opening a terminal session to run the shell.
$data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY,
strlen('vt100'), 'vt100', 24, 80, 0, 0,
self::TTY_OP_END);
if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_REQUEST_PTY');
return false;
}
$response = $this->_get_binary_packet();
if ($response === true) {
return false;
}
if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
user_error('Expected SSH_SMSG_SUCCESS');
return false;
}
$data = pack('C', NET_SSH1_CMSG_EXEC_SHELL);
if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_EXEC_SHELL');
return false;
}
$this->bitmap |= self::MASK_SHELL;
//stream_set_blocking($this->fsock, 0);
return true;
}
/**
* Inputs a command into an interactive shell.
*
* @see self::interactiveWrite()
* @param string $cmd
* @return bool
* @access public
*/
function write($cmd)
{
return $this->interactiveWrite($cmd);
}
/**
* Returns the output of an interactive shell when there's a match
for $expect
*
* $expect can take the form of a string literal or, if $mode ==
self::READ_REGEX,
* a regular expression.
*
* @see self::write()
* @param string $expect
* @param int $mode
* @return bool
* @access public
*/
function read($expect, $mode = self::READ_SIMPLE)
{
if (!($this->bitmap & self::MASK_LOGIN)) {
user_error('Operation disallowed prior to login()');
return false;
}
if (!($this->bitmap & self::MASK_SHELL) &&
!$this->_initShell()) {
user_error('Unable to initiate an interactive shell
session');
return false;
}
$match = $expect;
while (true) {
if ($mode == self::READ_REGEX) {
preg_match($expect, $this->interactiveBuffer, $matches);
$match = isset($matches[0]) ? $matches[0] : '';
}
$pos = strlen($match) ? strpos($this->interactiveBuffer,
$match) : false;
if ($pos !== false) {
return $this->_string_shift($this->interactiveBuffer,
$pos + strlen($match));
}
$response = $this->_get_binary_packet();
if ($response === true) {
return $this->_string_shift($this->interactiveBuffer,
strlen($this->interactiveBuffer));
}
$this->interactiveBuffer.=
substr($response[self::RESPONSE_DATA], 4);
}
}
/**
* Inputs a command into an interactive shell.
*
* @see self::interactiveRead()
* @param string $cmd
* @return bool
* @access public
*/
function interactiveWrite($cmd)
{
if (!($this->bitmap & self::MASK_LOGIN)) {
user_error('Operation disallowed prior to login()');
return false;
}
if (!($this->bitmap & self::MASK_SHELL) &&
!$this->_initShell()) {
user_error('Unable to initiate an interactive shell
session');
return false;
}
$data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA,
strlen($cmd), $cmd);
if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_STDIN');
return false;
}
return true;
}
/**
* Returns the output of an interactive shell when no more output is
available.
*
* Requires PHP 4.3.0 or later due to the use of the stream_select()
function. If you see stuff like
* "^[[00m", you're seeing ANSI escape codes. According
to
* {@link http://support.microsoft.com/kb/101875 How to Enable ANSI.SYS
in a Command Window}, "Windows NT
* does not support ANSI escape sequences in Win32 Console
applications", so if you're a Windows user,
* there's not going to be much recourse.
*
* @see self::interactiveRead()
* @return string
* @access public
*/
function interactiveRead()
{
if (!($this->bitmap & self::MASK_LOGIN)) {
user_error('Operation disallowed prior to login()');
return false;
}
if (!($this->bitmap & self::MASK_SHELL) &&
!$this->_initShell()) {
user_error('Unable to initiate an interactive shell
session');
return false;
}
$read = array($this->fsock);
$write = $except = null;
if (stream_select($read, $write, $except, 0)) {
$response = $this->_get_binary_packet();
return substr($response[self::RESPONSE_DATA], 4);
} else {
return '';
}
}
/**
* Disconnect
*
* @access public
*/
function disconnect()
{
$this->_disconnect();
}
/**
* Destructor.
*
* Will be called, automatically, if you're supporting just PHP5.
If you're supporting PHP4, you'll need to call
* disconnect().
*
* @access public
*/
function __destruct()
{
$this->_disconnect();
}
/**
* Disconnect
*
* @param string $msg
* @access private
*/
function _disconnect($msg = 'Client Quit')
{
if ($this->bitmap) {
$data = pack('C', NET_SSH1_CMSG_EOF);
$this->_send_binary_packet($data);
/*
$response = $this->_get_binary_packet();
if ($response === true) {
$response = array(self::RESPONSE_TYPE => -1);
}
switch ($response[self::RESPONSE_TYPE]) {
case NET_SSH1_SMSG_EXITSTATUS:
$data = pack('C',
NET_SSH1_CMSG_EXIT_CONFIRMATION);
break;
default:
$data = pack('CNa*', NET_SSH1_MSG_DISCONNECT,
strlen($msg), $msg);
}
*/
$data = pack('CNa*', NET_SSH1_MSG_DISCONNECT,
strlen($msg), $msg);
$this->_send_binary_packet($data);
fclose($this->fsock);
$this->bitmap = 0;
}
}
/**
* Gets Binary Packets
*
* See 'The Binary Packet Protocol' of protocol-1.5.txt for
more info.
*
* Also, this function could be improved upon by adding detection for
the following exploit:
* http://www.securiteam.com/securitynews/5LP042K3FY.html
*
* @see self::_send_binary_packet()
* @return array
* @access private
*/
function _get_binary_packet()
{
if (feof($this->fsock)) {
//user_error('connection closed prematurely');
return false;
}
if ($this->curTimeout) {
$read = array($this->fsock);
$write = $except = null;
$start = strtok(microtime(), ' ') +
strtok(''); // http://php.net/microtime#61838
$sec = floor($this->curTimeout);
$usec = 1000000 * ($this->curTimeout - $sec);
// on windows this returns a "Warning: Invalid CRT
parameters detected" error
if (!@stream_select($read, $write, $except, $sec, $usec)
&& !count($read)) {
//$this->_disconnect('Timeout');
return true;
}
$elapsed = strtok(microtime(), ' ') +
strtok('') - $start;
$this->curTimeout-= $elapsed;
}
$start = strtok(microtime(), ' ') + strtok('');
// http://php.net/microtime#61838
$data = fread($this->fsock, 4);
if (strlen($data) < 4) {
return false;
}
$temp = unpack('Nlength', $data);
$padding_length = 8 - ($temp['length'] & 7);
$length = $temp['length'] + $padding_length;
$raw = '';
while ($length > 0) {
$temp = fread($this->fsock, $length);
if (strlen($temp) != $length) {
return false;
}
$raw.= $temp;
$length-= strlen($temp);
}
$stop = strtok(microtime(), ' ') + strtok('');
if (strlen($raw) && $this->crypto !== false) {
$raw = $this->crypto->decrypt($raw);
}
$padding = substr($raw, 0, $padding_length);
$type = $raw[$padding_length];
$data = substr($raw, $padding_length + 1, -4);
if (strlen($raw) < 4) {
return false;
}
$temp = unpack('Ncrc', substr($raw, -4));
//if ( $temp['crc'] != $this->_crc($padding . $type .
$data) ) {
// user_error('Bad CRC in packet from server');
// return false;
//}
$type = ord($type);
if (defined('NET_SSH1_LOGGING')) {
$temp = isset($this->protocol_flags[$type]) ?
$this->protocol_flags[$type] : 'UNKNOWN';
$temp = '<- ' . $temp .
' (' . round($stop - $start, 4) .
's)';
$this->_append_log($temp, $data);
}
return array(
self::RESPONSE_TYPE => $type,
self::RESPONSE_DATA => $data
);
}
/**
* Sends Binary Packets
*
* Returns true on success, false on failure.
*
* @see self::_get_binary_packet()
* @param string $data
* @return bool
* @access private
*/
function _send_binary_packet($data)
{
if (feof($this->fsock)) {
//user_error('connection closed prematurely');
return false;
}
$length = strlen($data) + 4;
$padding = Random::string(8 - ($length & 7));
$orig = $data;
$data = $padding . $data;
$data.= pack('N', $this->_crc($data));
if ($this->crypto !== false) {
$data = $this->crypto->encrypt($data);
}
$packet = pack('Na*', $length, $data);
$start = strtok(microtime(), ' ') + strtok('');
// http://php.net/microtime#61838
$result = strlen($packet) == fputs($this->fsock, $packet);
$stop = strtok(microtime(), ' ') + strtok('');
if (defined('NET_SSH1_LOGGING')) {
$temp = isset($this->protocol_flags[ord($orig[0])]) ?
$this->protocol_flags[ord($orig[0])] : 'UNKNOWN';
$temp = '-> ' . $temp .
' (' . round($stop - $start, 4) .
's)';
$this->_append_log($temp, $orig);
}
return $result;
}
/**
* Cyclic Redundancy Check (CRC)
*
* PHP's crc32 function is implemented slightly differently than
the one that SSH v1 uses, so
* we've reimplemented it. A more detailed discussion of the
differences can be found after
* $crc_lookup_table's initialization.
*
* @see self::_get_binary_packet()
* @see self::_send_binary_packet()
* @param string $data
* @return int
* @access private
*/
function _crc($data)
{
static $crc_lookup_table = array(
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
);
// For this function to yield the same output as PHP's crc32
function, $crc would have to be
// set to 0xFFFFFFFF, initially - not 0x00000000 as it currently
is.
$crc = 0x00000000;
$length = strlen($data);
for ($i=0; $i<$length; $i++) {
// We AND $crc >> 8 with 0x00FFFFFF because we want the
eight newly added bits to all
// be zero. PHP, unfortunately, doesn't always do this.
0x80000000 >> 8, as an example,
// yields 0xFF800000 - not 0x00800000. The following link
elaborates:
//
http://www.php.net/manual/en/language.operators.bitwise.php#57281
$crc = (($crc >> 8) & 0x00FFFFFF) ^
$crc_lookup_table[($crc & 0xFF) ^ ord($data[$i])];
}
// In addition to having to set $crc to 0xFFFFFFFF, initially, the
return value must be XOR'd with
// 0xFFFFFFFF for this function to return the same thing that
PHP's crc32 function would.
return $crc;
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @return string
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
/**
* RSA Encrypt
*
* Returns mod(pow($m, $e), $n), where $n should be the product of two
(large) primes $p and $q and where $e
* should be a number with the property that gcd($e, ($p - 1) * ($q -
1)) == 1. Could just make anything that
* calls this call modexp, instead, but I think this makes things
clearer, maybe...
*
* @see self::__construct()
* @param BigInteger $m
* @param array $key
* @return BigInteger
* @access private
*/
function _rsa_crypt($m, $key)
{
/*
$rsa = new RSA();
$rsa->loadKey($key, RSA::PUBLIC_FORMAT_RAW);
$rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1);
return $rsa->encrypt($m);
*/
// To quote from protocol-1.5.txt:
// The most significant byte (which is only partial as the value
must be
// less than the public modulus, which is never a power of two) is
zero.
//
// The next byte contains the value 2 (which stands for public-key
// encrypted data in the PKCS standard [PKCS#1]). Then, there are
non-
// zero random bytes to fill any unused space, a zero byte, and the
data
// to be encrypted in the least significant bytes, the last byte of
the
// data in the least significant byte.
// Presumably the part of PKCS#1 they're refering to is
"Section 7.2.1 Encryption Operation",
// under "7.2 RSAES-PKCS1-v1.5" and "7 Encryption
schemes" of the following URL:
// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
$modulus = $key[1]->toBytes();
$length = strlen($modulus) - strlen($m) - 3;
$random = '';
while (strlen($random) != $length) {
$block = Random::string($length - strlen($random));
$block = str_replace("\x00", '', $block);
$random.= $block;
}
$temp = chr(0) . chr(2) . $random . chr(0) . $m;
$m = new BigInteger($temp, 256);
$m = $m->modPow($key[0], $key[1]);
return $m->toBytes();
}
/**
* Define Array
*
* Takes any number of arrays whose indices are integers and whose
values are strings and defines a bunch of
* named constants from it, using the value as the name of the constant
and the index as the value of the constant.
* If any of the constants that would be defined already exists, none
of the constants will be defined.
*
* @access private
*/
function _define_array()
{
$args = func_get_args();
foreach ($args as $arg) {
foreach ($arg as $key => $value) {
if (!defined($value)) {
define($value, $key);
} else {
break 2;
}
}
}
}
/**
* Returns a log of the packets that have been sent and received.
*
* Returns a string if NET_SSH1_LOGGING == self::LOG_COMPLEX, an array
if NET_SSH1_LOGGING == self::LOG_SIMPLE and false if
!defined('NET_SSH1_LOGGING')
*
* @access public
* @return array|false|string
*/
function getLog()
{
if (!defined('NET_SSH1_LOGGING')) {
return false;
}
switch (NET_SSH1_LOGGING) {
case self::LOG_SIMPLE:
return $this->message_number_log;
break;
case self::LOG_COMPLEX:
return $this->_format_log($this->message_log,
$this->protocol_flags_log);
break;
default:
return false;
}
}
/**
* Formats a log for printing
*
* @param array $message_log
* @param array $message_number_log
* @access private
* @return string
*/
function _format_log($message_log, $message_number_log)
{
$output = '';
for ($i = 0; $i < count($message_log); $i++) {
$output.= $message_number_log[$i] . "\r\n";
$current_log = $message_log[$i];
$j = 0;
do {
if (strlen($current_log)) {
$output.= str_pad(dechex($j), 7, '0',
STR_PAD_LEFT) . '0 ';
}
$fragment = $this->_string_shift($current_log,
$this->log_short_width);
$hex = substr(preg_replace_callback('#.#s',
array($this, '_format_log_helper'), $fragment),
strlen($this->log_boundary));
// replace non ASCII printable characters with dots
//
http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
// also replace < with a . since < messes up the
output on web browsers
$raw = preg_replace('#[^\x20-\x7E]|<#',
'.', $fragment);
$output.= str_pad($hex, $this->log_long_width -
$this->log_short_width, ' ') . $raw . "\r\n";
$j++;
} while (strlen($current_log));
$output.= "\r\n";
}
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
*
* Returns, by default, the base-10 representation. If $raw_output is
set to true, returns, instead,
* the raw bytes. This behavior is similar to PHP's md5()
function.
*
* @param bool $raw_output
* @return string
* @access public
*/
function getServerKeyPublicExponent($raw_output = false)
{
return $raw_output ?
$this->server_key_public_exponent->toBytes() :
$this->server_key_public_exponent->toString();
}
/**
* Return the server key public modulus
*
* Returns, by default, the base-10 representation. If $raw_output is
set to true, returns, instead,
* the raw bytes. This behavior is similar to PHP's md5()
function.
*
* @param bool $raw_output
* @return string
* @access public
*/
function getServerKeyPublicModulus($raw_output = false)
{
return $raw_output ?
$this->server_key_public_modulus->toBytes() :
$this->server_key_public_modulus->toString();
}
/**
* Return the host key public exponent
*
* Returns, by default, the base-10 representation. If $raw_output is
set to true, returns, instead,
* the raw bytes. This behavior is similar to PHP's md5()
function.
*
* @param bool $raw_output
* @return string
* @access public
*/
function getHostKeyPublicExponent($raw_output = false)
{
return $raw_output ?
$this->host_key_public_exponent->toBytes() :
$this->host_key_public_exponent->toString();
}
/**
* Return the host key public modulus
*
* Returns, by default, the base-10 representation. If $raw_output is
set to true, returns, instead,
* the raw bytes. This behavior is similar to PHP's md5()
function.
*
* @param bool $raw_output
* @return string
* @access public
*/
function getHostKeyPublicModulus($raw_output = false)
{
return $raw_output ?
$this->host_key_public_modulus->toBytes() :
$this->host_key_public_modulus->toString();
}
/**
* Return a list of ciphers supported by SSH1 server.
*
* Just because a cipher is supported by an SSH1 server doesn't
mean it's supported by this library. If $raw_output
* is set to true, returns, instead, an array of constants. ie.
instead of array('Triple-DES in CBC mode'), you'll
* get array(self::CIPHER_3DES).
*
* @param bool $raw_output
* @return array
* @access public
*/
function getSupportedCiphers($raw_output = false)
{
return $raw_output ? array_keys($this->supported_ciphers) :
array_values($this->supported_ciphers);
}
/**
* Return a list of authentications supported by SSH1 server.
*
* Just because a cipher is supported by an SSH1 server doesn't
mean it's supported by this library. If $raw_output
* is set to true, returns, instead, an array of constants. ie.
instead of array('password authentication'), you'll
* get array(self::AUTH_PASSWORD).
*
* @param bool $raw_output
* @return array
* @access public
*/
function getSupportedAuthentications($raw_output = false)
{
return $raw_output ?
array_keys($this->supported_authentications) :
array_values($this->supported_authentications);
}
/**
* Return the server identification.
*
* @return string
* @access public
*/
function getServerIdentification()
{
return rtrim($this->server_identification);
}
/**
* Logs data packets
*
* Makes sure that only the last 1MB worth of packets will be logged
*
* @param int $protocol_flags
* @param string $message
* @access private
*/
function _append_log($protocol_flags, $message)
{
switch (NET_SSH1_LOGGING) {
// useful for benchmarks
case self::LOG_SIMPLE:
$this->protocol_flags_log[] = $protocol_flags;
break;
// the most useful log for SSH1
case self::LOG_COMPLEX:
$this->protocol_flags_log[] = $protocol_flags;
$this->_string_shift($message);
$this->log_size+= strlen($message);
$this->message_log[] = $message;
while ($this->log_size > self::LOG_MAX_SIZE) {
$this->log_size-=
strlen(array_shift($this->message_log));
array_shift($this->protocol_flags_log);
}
break;
// dump the output out realtime; packets may be interspersed
with non packets,
// passwords won't be filtered out and select other
packets may not be correctly
// identified
case self::LOG_REALTIME:
echo "<pre>\r\n" .
$this->_format_log(array($message), array($protocol_flags)) .
"\r\n</pre>\r\n";
@flush();
@ob_flush();
break;
// basically the same thing as self::LOG_REALTIME with the
caveat that self::LOG_REALTIME_FILE
// needs to be defined and that the resultant log file will be
capped out at self::LOG_MAX_SIZE.
// the earliest part of the log file is denoted by the first
<<< START >>> and is not going to necessarily
// at the beginning of the file
case self::LOG_REALTIME_FILE:
if (!isset($this->realtime_log_file)) {
// PHP doesn't seem to like using constants in
fopen()
$filename = self::LOG_REALTIME_FILE;
$fp = fopen($filename, 'w');
$this->realtime_log_file = $fp;
}
if (!is_resource($this->realtime_log_file)) {
break;
}
$entry = $this->_format_log(array($message),
array($protocol_flags));
if ($this->realtime_log_wrap) {
$temp = "<<< START
>>>\r\n";
$entry.= $temp;
fseek($this->realtime_log_file,
ftell($this->realtime_log_file) - strlen($temp));
}
$this->realtime_log_size+= strlen($entry);
if ($this->realtime_log_size > self::LOG_MAX_SIZE) {
fseek($this->realtime_log_file, 0);
$this->realtime_log_size = strlen($entry);
$this->realtime_log_wrap = true;
}
fputs($this->realtime_log_file, $entry);
}
}
}
vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php000064400000527204151156520660015733
0ustar00<?php
/**
* Pure-PHP implementation of SSHv2.
*
* PHP version 5
*
* Here are some examples of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $ssh = new \phpseclib\Net\SSH2('www.domain.tld');
* if (!$ssh->login('username', 'password')) {
* exit('Login Failed');
* }
*
* echo $ssh->exec('pwd');
* echo $ssh->exec('ls -la');
* ?>
* </code>
*
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $key = new \phpseclib\Crypt\RSA();
* //$key->setPassword('whatever');
* $key->loadKey(file_get_contents('privatekey'));
*
* $ssh = new \phpseclib\Net\SSH2('www.domain.tld');
* if (!$ssh->login('username', $key)) {
* exit('Login Failed');
* }
*
* echo $ssh->read('username@username:~$');
* $ssh->write("ls -la\n");
* echo $ssh->read('username@username:~$');
* ?>
* </code>
*
* @category Net
* @package SSH2
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Net;
use phpseclib\Crypt\Base;
use phpseclib\Crypt\Blowfish;
use phpseclib\Crypt\Hash;
use phpseclib\Crypt\Random;
use phpseclib\Crypt\RC4;
use phpseclib\Crypt\Rijndael;
use phpseclib\Crypt\RSA;
use phpseclib\Crypt\TripleDES;
use phpseclib\Crypt\Twofish;
use phpseclib\Math\BigInteger; // Used to do Diffie-Hellman key exchange
and DSA/RSA signature verification.
use phpseclib\System\SSH\Agent;
/**
* Pure-PHP implementation of SSHv2.
*
* @package SSH2
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class SSH2
{
/**#@+
* Execution Bitmap Masks
*
* @see \phpseclib\Net\SSH2::bitmap
* @access private
*/
const MASK_CONSTRUCTOR = 0x00000001;
const MASK_CONNECTED = 0x00000002;
const MASK_LOGIN_REQ = 0x00000004;
const MASK_LOGIN = 0x00000008;
const MASK_SHELL = 0x00000010;
const MASK_WINDOW_ADJUST = 0x00000020;
/**#@-*/
/**#@+
* Channel constants
*
* RFC4254 refers not to client and server channels but rather to
sender and recipient channels. we don't refer
* to them in that way because RFC4254 toggles the meaning. the client
sends a SSH_MSG_CHANNEL_OPEN message with
* a sender channel and the server sends a
SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
* recepient channel. at first glance, you might conclude that
SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
* would be the same thing as SSH_MSG_CHANNEL_OPEN's sender
channel, but it's not, per this snipet:
* The 'recipient channel' is the channel number given in
the original
* open request, and 'sender channel' is the channel
number allocated by
* the other side.
*
* @see \phpseclib\Net\SSH2::_send_channel_packet()
* @see \phpseclib\Net\SSH2::_get_channel_packet()
* @access private
*/
const CHANNEL_EXEC = 1; // PuTTy uses 0x100
const CHANNEL_SHELL = 2;
const CHANNEL_SUBSYSTEM = 3;
const CHANNEL_AGENT_FORWARD = 4;
const CHANNEL_KEEP_ALIVE = 5;
/**#@-*/
/**#@+
* @access public
* @see \phpseclib\Net\SSH2::getLog()
*/
/**
* Returns the message numbers
*/
const LOG_SIMPLE = 1;
/**
* Returns the message content
*/
const LOG_COMPLEX = 2;
/**
* Outputs the content real-time
*/
const LOG_REALTIME = 3;
/**
* Dumps the content real-time to a file
*/
const LOG_REALTIME_FILE = 4;
/**
* Make sure that the log never gets larger than this
*/
const LOG_MAX_SIZE = 1048576; // 1024 * 1024
/**#@-*/
/**#@+
* @access public
* @see \phpseclib\Net\SSH2::read()
*/
/**
* Returns when a string matching $expect exactly is found
*/
const READ_SIMPLE = 1;
/**
* Returns when a string matching the regular expression $expect is
found
*/
const READ_REGEX = 2;
/**
* Returns whenever a data packet is received.
*
* Some data packets may only contain a single character so it may be
necessary
* to call read() multiple times when using this option
*/
const READ_NEXT = 3;
/**#@-*/
/**
* The SSH identifier
*
* @var string
* @access private
*/
var $identifier;
/**
* The Socket Object
*
* @var object
* @access private
*/
var $fsock;
/**
* Execution Bitmap
*
* The bits that are set represent functions that have been called
already. This is used to determine
* if a requisite function has been successfully executed. If not, an
error should be thrown.
*
* @var int
* @access private
*/
var $bitmap = 0;
/**
* Error information
*
* @see self::getErrors()
* @see self::getLastError()
* @var string
* @access private
*/
var $errors = array();
/**
* Server Identifier
*
* @see self::getServerIdentification()
* @var array|false
* @access private
*/
var $server_identifier = false;
/**
* Key Exchange Algorithms
*
* @see self::getKexAlgorithims()
* @var array|false
* @access private
*/
var $kex_algorithms = false;
/**
* Key Exchange Algorithm
*
* @see self::getMethodsNegotiated()
* @var string|false
* @access private
*/
var $kex_algorithm = false;
/**
* Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange
Methods
*
* @see self::_key_exchange()
* @var int
* @access private
*/
var $kex_dh_group_size_min = 1536;
/**
* Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange
Methods
*
* @see self::_key_exchange()
* @var int
* @access private
*/
var $kex_dh_group_size_preferred = 2048;
/**
* Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange
Methods
*
* @see self::_key_exchange()
* @var int
* @access private
*/
var $kex_dh_group_size_max = 4096;
/**
* Server Host Key Algorithms
*
* @see self::getServerHostKeyAlgorithms()
* @var array|false
* @access private
*/
var $server_host_key_algorithms = false;
/**
* Encryption Algorithms: Client to Server
*
* @see self::getEncryptionAlgorithmsClient2Server()
* @var array|false
* @access private
*/
var $encryption_algorithms_client_to_server = false;
/**
* Encryption Algorithms: Server to Client
*
* @see self::getEncryptionAlgorithmsServer2Client()
* @var array|false
* @access private
*/
var $encryption_algorithms_server_to_client = false;
/**
* MAC Algorithms: Client to Server
*
* @see self::getMACAlgorithmsClient2Server()
* @var array|false
* @access private
*/
var $mac_algorithms_client_to_server = false;
/**
* MAC Algorithms: Server to Client
*
* @see self::getMACAlgorithmsServer2Client()
* @var array|false
* @access private
*/
var $mac_algorithms_server_to_client = false;
/**
* Compression Algorithms: Client to Server
*
* @see self::getCompressionAlgorithmsClient2Server()
* @var array|false
* @access private
*/
var $compression_algorithms_client_to_server = false;
/**
* Compression Algorithms: Server to Client
*
* @see self::getCompressionAlgorithmsServer2Client()
* @var array|false
* @access private
*/
var $compression_algorithms_server_to_client = false;
/**
* Languages: Server to Client
*
* @see self::getLanguagesServer2Client()
* @var array|false
* @access private
*/
var $languages_server_to_client = false;
/**
* Languages: Client to Server
*
* @see self::getLanguagesClient2Server()
* @var array|false
* @access private
*/
var $languages_client_to_server = false;
/**
* Preferred Algorithms
*
* @see self::setPreferredAlgorithms()
* @var array
* @access private
*/
var $preferred = array();
/**
* Block Size for Server to Client Encryption
*
* "Note that the length of the concatenation of
'packet_length',
* 'padding_length', 'payload', and 'random
padding' MUST be a multiple
* of the cipher block size or 8, whichever is larger. This
constraint
* MUST be enforced, even when using stream ciphers."
*
* -- http://tools.ietf.org/html/rfc4253#section-6
*
* @see self::__construct()
* @see self::_send_binary_packet()
* @var int
* @access private
*/
var $encrypt_block_size = 8;
/**
* Block Size for Client to Server Encryption
*
* @see self::__construct()
* @see self::_get_binary_packet()
* @var int
* @access private
*/
var $decrypt_block_size = 8;
/**
* Server to Client Encryption Object
*
* @see self::_get_binary_packet()
* @var object
* @access private
*/
var $decrypt = false;
/**
* Client to Server Encryption Object
*
* @see self::_send_binary_packet()
* @var object
* @access private
*/
var $encrypt = false;
/**
* Client to Server HMAC Object
*
* @see self::_send_binary_packet()
* @var object
* @access private
*/
var $hmac_create = false;
/**
* Server to Client HMAC Object
*
* @see self::_get_binary_packet()
* @var object
* @access private
*/
var $hmac_check = false;
/**
* Size of server to client HMAC
*
* We need to know how big the HMAC will be for the server to client
direction so that we know how many bytes to read.
* For the client to server side, the HMAC object will make the HMAC as
long as it needs to be. All we need to do is
* append it.
*
* @see self::_get_binary_packet()
* @var int
* @access private
*/
var $hmac_size = false;
/**
* Server Public Host Key
*
* @see self::getServerPublicHostKey()
* @var string
* @access private
*/
var $server_public_host_key;
/**
* Session identifier
*
* "The exchange hash H from the first key exchange is
additionally
* used as the session identifier, which is a unique identifier for
* this connection."
*
* -- http://tools.ietf.org/html/rfc4253#section-7.2
*
* @see self::_key_exchange()
* @var string
* @access private
*/
var $session_id = false;
/**
* Exchange hash
*
* The current exchange hash
*
* @see self::_key_exchange()
* @var string
* @access private
*/
var $exchange_hash = false;
/**
* Message Numbers
*
* @see self::__construct()
* @var array
* @access private
*/
var $message_numbers = array();
/**
* Disconnection Message 'reason codes' defined in RFC4253
*
* @see self::__construct()
* @var array
* @access private
*/
var $disconnect_reasons = array();
/**
* SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in
RFC4254
*
* @see self::__construct()
* @var array
* @access private
*/
var $channel_open_failure_reasons = array();
/**
* Terminal Modes
*
* @link http://tools.ietf.org/html/rfc4254#section-8
* @see self::__construct()
* @var array
* @access private
*/
var $terminal_modes = array();
/**
* SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
*
* @link http://tools.ietf.org/html/rfc4254#section-5.2
* @see self::__construct()
* @var array
* @access private
*/
var $channel_extended_data_type_codes = array();
/**
* Send Sequence Number
*
* See 'Section 6.4. Data Integrity' of rfc4253 for more
info.
*
* @see self::_send_binary_packet()
* @var int
* @access private
*/
var $send_seq_no = 0;
/**
* Get Sequence Number
*
* See 'Section 6.4. Data Integrity' of rfc4253 for more
info.
*
* @see self::_get_binary_packet()
* @var int
* @access private
*/
var $get_seq_no = 0;
/**
* Server Channels
*
* Maps client channels to server channels
*
* @see self::_get_channel_packet()
* @see self::exec()
* @var array
* @access private
*/
var $server_channels = array();
/**
* Channel Buffers
*
* If a client requests a packet from one channel but receives two
packets from another those packets should
* be placed in a buffer
*
* @see self::_get_channel_packet()
* @see self::exec()
* @var array
* @access private
*/
var $channel_buffers = array();
/**
* Channel Status
*
* Contains the type of the last sent message
*
* @see self::_get_channel_packet()
* @var array
* @access private
*/
var $channel_status = array();
/**
* Packet Size
*
* Maximum packet size indexed by channel
*
* @see self::_send_channel_packet()
* @var array
* @access private
*/
var $packet_size_client_to_server = array();
/**
* Message Number Log
*
* @see self::getLog()
* @var array
* @access private
*/
var $message_number_log = array();
/**
* Message Log
*
* @see self::getLog()
* @var array
* @access private
*/
var $message_log = array();
/**
* The Window Size
*
* Bytes the other party can send before it must wait for the window to
be adjusted (0x7FFFFFFF = 2GB)
*
* @var int
* @see self::_send_channel_packet()
* @see self::exec()
* @access private
*/
var $window_size = 0x7FFFFFFF;
/**
* What we resize the window to
*
* When PuTTY resizes the window it doesn't add an additional
0x7FFFFFFF bytes - it adds 0x40000000 bytes.
* Some SFTP clients (GoAnywhere) don't support adding 0x7FFFFFFF
to the window size after the fact so
* we'll just do what PuTTY does
*
* @var int
* @see self::_send_channel_packet()
* @see self::exec()
* @access private
*/
var $window_resize = 0x40000000;
/**
* Window size, server to client
*
* Window size indexed by channel
*
* @see self::_send_channel_packet()
* @var array
* @access private
*/
var $window_size_server_to_client = array();
/**
* Window size, client to server
*
* Window size indexed by channel
*
* @see self::_get_channel_packet()
* @var array
* @access private
*/
var $window_size_client_to_server = array();
/**
* Server signature
*
* Verified against $this->session_id
*
* @see self::getServerPublicHostKey()
* @var string
* @access private
*/
var $signature = '';
/**
* Server signature format
*
* ssh-rsa or ssh-dss.
*
* @see self::getServerPublicHostKey()
* @var string
* @access private
*/
var $signature_format = '';
/**
* Interactive Buffer
*
* @see self::read()
* @var array
* @access private
*/
var $interactiveBuffer = '';
/**
* Current log size
*
* Should never exceed self::LOG_MAX_SIZE
*
* @see self::_send_binary_packet()
* @see self::_get_binary_packet()
* @var int
* @access private
*/
var $log_size;
/**
* Timeout
*
* @see self::setTimeout()
* @access private
*/
var $timeout;
/**
* Current Timeout
*
* @see self::_get_channel_packet()
* @access private
*/
var $curTimeout;
/**
* Keep Alive Interval
*
* @see self::setKeepAlive()
* @access private
*/
var $keepAlive;
/**
* Real-time log file pointer
*
* @see self::_append_log()
* @var resource
* @access private
*/
var $realtime_log_file;
/**
* Real-time log file size
*
* @see self::_append_log()
* @var int
* @access private
*/
var $realtime_log_size;
/**
* Has the signature been validated?
*
* @see self::getServerPublicHostKey()
* @var bool
* @access private
*/
var $signature_validated = false;
/**
* Real-time log file wrap boolean
*
* @see self::_append_log()
* @access private
*/
var $realtime_log_wrap;
/**
* Flag to suppress stderr from output
*
* @see self::enableQuietMode()
* @access private
*/
var $quiet_mode = false;
/**
* Time of first network activity
*
* @var int
* @access private
*/
var $last_packet;
/**
* Exit status returned from ssh if any
*
* @var int
* @access private
*/
var $exit_status;
/**
* Flag to request a PTY when using exec()
*
* @var bool
* @see self::enablePTY()
* @access private
*/
var $request_pty = false;
/**
* Flag set while exec() is running when using enablePTY()
*
* @var bool
* @access private
*/
var $in_request_pty_exec = false;
/**
* Flag set after startSubsystem() is called
*
* @var bool
* @access private
*/
var $in_subsystem;
/**
* Contents of stdError
*
* @var string
* @access private
*/
var $stdErrorLog;
/**
* The Last Interactive Response
*
* @see self::_keyboard_interactive_process()
* @var string
* @access private
*/
var $last_interactive_response = '';
/**
* Keyboard Interactive Request / Responses
*
* @see self::_keyboard_interactive_process()
* @var array
* @access private
*/
var $keyboard_requests_responses = array();
/**
* Banner Message
*
* Quoting from the RFC, "in some jurisdictions, sending a warning
message before
* authentication may be relevant for getting legal protection."
*
* @see self::_filter()
* @see self::getBannerMessage()
* @var string
* @access private
*/
var $banner_message = '';
/**
* Did read() timeout or return normally?
*
* @see self::isTimeout()
* @var bool
* @access private
*/
var $is_timeout = false;
/**
* Log Boundary
*
* @see self::_format_log()
* @var string
* @access private
*/
var $log_boundary = ':';
/**
* Log Long Width
*
* @see self::_format_log()
* @var int
* @access private
*/
var $log_long_width = 65;
/**
* Log Short Width
*
* @see self::_format_log()
* @var int
* @access private
*/
var $log_short_width = 16;
/**
* Hostname
*
* @see self::__construct()
* @see self::_connect()
* @var string
* @access private
*/
var $host;
/**
* Port Number
*
* @see self::__construct()
* @see self::_connect()
* @var int
* @access private
*/
var $port;
/**
* Number of columns for terminal window size
*
* @see self::getWindowColumns()
* @see self::setWindowColumns()
* @see self::setWindowSize()
* @var int
* @access private
*/
var $windowColumns = 80;
/**
* Number of columns for terminal window size
*
* @see self::getWindowRows()
* @see self::setWindowRows()
* @see self::setWindowSize()
* @var int
* @access private
*/
var $windowRows = 24;
/**
* Crypto Engine
*
* @see self::setCryptoEngine()
* @see self::_key_exchange()
* @var int
* @access private
*/
var $crypto_engine = false;
/**
* A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario
*
* @var System_SSH_Agent
* @access private
*/
var $agent;
/**
* Send the identification string first?
*
* @var bool
* @access private
*/
var $send_id_string_first = true;
/**
* Send the key exchange initiation packet first?
*
* @var bool
* @access private
*/
var $send_kex_first = true;
/**
* Some versions of OpenSSH incorrectly calculate the key size
*
* @var bool
* @access private
*/
var $bad_key_size_fix = false;
/**
* Should we try to re-connect to re-establish keys?
*
* @var bool
* @access private
*/
var $retry_connect = false;
/**
* Binary Packet Buffer
*
* @var string|false
* @access private
*/
var $binary_packet_buffer = false;
/**
* Preferred Signature Format
*
* @var string|false
* @access private
*/
var $preferred_signature_format = false;
/**
* Authentication Credentials
*
* @var array
* @access private
*/
var $auth = array();
/**
* The authentication methods that may productively continue
authentication.
*
* @see https://tools.ietf.org/html/rfc4252#section-5.1
* @var array|null
*/
private $auth_methods_to_continue = null;
/**
* Default Constructor.
*
* $host can either be a string, representing the host, or a stream
resource.
*
* @param mixed $host
* @param int $port
* @param int $timeout
* @see self::login()
* @return \phpseclib\Net\SSH2
* @access public
*/
function __construct($host, $port = 22, $timeout = 10)
{
$this->message_numbers = array(
1 => 'NET_SSH2_MSG_DISCONNECT',
2 => 'NET_SSH2_MSG_IGNORE',
3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
4 => 'NET_SSH2_MSG_DEBUG',
5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
20 => 'NET_SSH2_MSG_KEXINIT',
21 => 'NET_SSH2_MSG_NEWKEYS',
30 => 'NET_SSH2_MSG_KEXDH_INIT',
31 => 'NET_SSH2_MSG_KEXDH_REPLY',
50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
94 => 'NET_SSH2_MSG_CHANNEL_DATA',
95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
96 => 'NET_SSH2_MSG_CHANNEL_EOF',
97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
);
$this->disconnect_reasons = array(
1 =>
'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
4 => 'NET_SSH2_DISCONNECT_RESERVED',
5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
8 =>
'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
9 =>
'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
13 =>
'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
14 =>
'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
);
$this->channel_open_failure_reasons = array(
1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
);
$this->terminal_modes = array(
0 => 'NET_SSH2_TTY_OP_END'
);
$this->channel_extended_data_type_codes = array(
1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
);
$this->_define_array(
$this->message_numbers,
$this->disconnect_reasons,
$this->channel_open_failure_reasons,
$this->terminal_modes,
$this->channel_extended_data_type_codes,
array(60 =>
'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'),
array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
61 =>
'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'),
// RFC 4419 - diffie-hellman-group-exchange-sha{1,256}
array(30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD',
31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP',
32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT',
33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY',
34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'),
// RFC 5656 - Elliptic Curves (for
curve25519-sha256@libssh.org)
array(30 => 'NET_SSH2_MSG_KEX_ECDH_INIT',
31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY')
);
if (is_resource($host)) {
$this->fsock = $host;
return;
}
if (is_string($host)) {
$this->host = $host;
$this->port = $port;
$this->timeout = $timeout;
}
}
/**
* Set Crypto Engine Mode
*
* Possible $engine values:
* CRYPT_MODE_INTERNAL, CRYPT_MODE_MCRYPT
*
* @param int $engine
* @access public
*/
function setCryptoEngine($engine)
{
$this->crypto_engine = $engine;
}
/**
* Send Identification String First
*
* https://tools.ietf.org/html/rfc4253#section-4.2 says "when the
connection has been established,
* both sides MUST send an identification string". It does not say
which side sends it first. In
* theory it shouldn't matter but it is a fact of life that some
SSH servers are simply buggy
*
* @access public
*/
function sendIdentificationStringFirst()
{
$this->send_id_string_first = true;
}
/**
* Send Identification String Last
*
* https://tools.ietf.org/html/rfc4253#section-4.2 says "when the
connection has been established,
* both sides MUST send an identification string". It does not say
which side sends it first. In
* theory it shouldn't matter but it is a fact of life that some
SSH servers are simply buggy
*
* @access public
*/
function sendIdentificationStringLast()
{
$this->send_id_string_first = false;
}
/**
* Send SSH_MSG_KEXINIT First
*
* https://tools.ietf.org/html/rfc4253#section-7.1 says "key
exchange begins by each sending
* sending the [SSH_MSG_KEXINIT] packet". It does not say which
side sends it first. In theory
* it shouldn't matter but it is a fact of life that some SSH
servers are simply buggy
*
* @access public
*/
function sendKEXINITFirst()
{
$this->send_kex_first = true;
}
/**
* Send SSH_MSG_KEXINIT Last
*
* https://tools.ietf.org/html/rfc4253#section-7.1 says "key
exchange begins by each sending
* sending the [SSH_MSG_KEXINIT] packet". It does not say which
side sends it first. In theory
* it shouldn't matter but it is a fact of life that some SSH
servers are simply buggy
*
* @access public
*/
function sendKEXINITLast()
{
$this->send_kex_first = false;
}
/**
* Connect to an SSHv2 server
*
* @return bool
* @access private
*/
function _connect()
{
if ($this->bitmap & self::MASK_CONSTRUCTOR) {
return false;
}
$this->bitmap |= self::MASK_CONSTRUCTOR;
$this->curTimeout = $this->timeout;
$this->last_packet = microtime(true);
if (!is_resource($this->fsock)) {
$start = microtime(true);
// with stream_select a timeout of 0 means that no timeout
takes place;
// with fsockopen a timeout of 0 means that you instantly
timeout
// to resolve this incompatibility a timeout of 100,000 will be
used for fsockopen if timeout is 0
$this->fsock = @fsockopen($this->host, $this->port,
$errno, $errstr, $this->curTimeout == 0 ? 100000 :
$this->curTimeout);
if (!$this->fsock) {
$host = $this->host . ':' . $this->port;
user_error(rtrim("Cannot connect to $host. Error
$errno. $errstr"));
return false;
}
$elapsed = microtime(true) - $start;
if ($this->curTimeout) {
$this->curTimeout-= $elapsed;
if ($this->curTimeout < 0) {
$this->is_timeout = true;
return false;
}
}
}
$this->identifier = $this->_generate_identifier();
if ($this->send_id_string_first) {
fputs($this->fsock, $this->identifier .
"\r\n");
}
/* According to the SSH2 specs,
"The server MAY send other lines of data before sending the
version
string. Each line SHOULD be terminated by a Carriage Return and
Line
Feed. Such lines MUST NOT begin with "SSH-", and
SHOULD be encoded
in ISO-10646 UTF-8 [RFC3629] (language is not specified).
Clients
MUST be able to process such lines." */
$data = '';
while (!feof($this->fsock) &&
!preg_match('#(.*)^(SSH-(\d\.\d+).*)#ms', $data, $matches)) {
$line = '';
while (true) {
if ($this->curTimeout) {
if ($this->curTimeout < 0) {
$this->is_timeout = true;
return false;
}
$read = array($this->fsock);
$write = $except = null;
$start = microtime(true);
$sec = floor($this->curTimeout);
$usec = 1000000 * ($this->curTimeout - $sec);
// on windows this returns a "Warning: Invalid CRT
parameters detected" error
// the !count() is done as a workaround for
<https://bugs.php.net/42682>
if (!@stream_select($read, $write, $except, $sec,
$usec) && !count($read)) {
$this->is_timeout = true;
return false;
}
$elapsed = microtime(true) - $start;
$this->curTimeout-= $elapsed;
}
$temp = stream_get_line($this->fsock, 255,
"\n");
if (strlen($temp) == 255) {
continue;
}
if ($temp === false) {
return false;
}
$line.= "$temp\n";
// quoting RFC4253, "Implementers who wish to maintain
// compatibility with older, undocumented versions of this
protocol may
// want to process the identification string without
expecting the
// presence of the carriage return character for reasons
described in
// Section 5 of this document."
//if (substr($line, -2) == "\r\n") {
// break;
//}
break;
}
$data.= $line;
}
if (feof($this->fsock)) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
$extra = $matches[1];
if (defined('NET_SSH2_LOGGING')) {
$this->_append_log('<-', $matches[0]);
$this->_append_log('->', $this->identifier .
"\r\n");
}
$this->server_identifier = trim($temp, "\r\n");
if (strlen($extra)) {
$this->errors[] = $data;
}
if (version_compare($matches[3], '1.99',
'<')) {
user_error("Cannot connect to SSH $matches[3]
servers");
return false;
}
if (!$this->send_id_string_first) {
fputs($this->fsock, $this->identifier .
"\r\n");
}
if (!$this->send_kex_first) {
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($response) || ord($response[0]) !=
NET_SSH2_MSG_KEXINIT) {
user_error('Expected SSH_MSG_KEXINIT');
return false;
}
if (!$this->_key_exchange($response)) {
return false;
}
}
if ($this->send_kex_first && !$this->_key_exchange())
{
return false;
}
$this->bitmap|= self::MASK_CONNECTED;
return true;
}
/**
* Generates the SSH identifier
*
* You should overwrite this method in your own class if you want to
use another identifier
*
* @access protected
* @return string
*/
function _generate_identifier()
{
$identifier = 'SSH-2.0-phpseclib_2.0';
$ext = array();
if
(function_exists('sodium_crypto_box_publickey_from_secretkey')) {
$ext[] = 'libsodium';
}
if (extension_loaded('openssl')) {
$ext[] = 'openssl';
} elseif (extension_loaded('mcrypt')) {
$ext[] = 'mcrypt';
}
if (extension_loaded('gmp')) {
$ext[] = 'gmp';
} elseif (extension_loaded('bcmath')) {
$ext[] = 'bcmath';
}
if (!empty($ext)) {
$identifier .= ' (' . implode(', ', $ext) .
')';
}
return $identifier;
}
/**
* Key Exchange
*
* @param string $kexinit_payload_server optional
* @access private
*/
function _key_exchange($kexinit_payload_server = false)
{
$preferred = $this->preferred;
$send_kex = true;
$kex_algorithms = isset($preferred['kex']) ?
$preferred['kex'] :
$this->getSupportedKEXAlgorithms();
$server_host_key_algorithms =
isset($preferred['hostkey']) ?
$preferred['hostkey'] :
$this->getSupportedHostKeyAlgorithms();
$s2c_encryption_algorithms =
isset($preferred['server_to_client']['crypt']) ?
$preferred['server_to_client']['crypt'] :
$this->getSupportedEncryptionAlgorithms();
$c2s_encryption_algorithms =
isset($preferred['client_to_server']['crypt']) ?
$preferred['client_to_server']['crypt'] :
$this->getSupportedEncryptionAlgorithms();
$s2c_mac_algorithms =
isset($preferred['server_to_client']['mac']) ?
$preferred['server_to_client']['mac'] :
$this->getSupportedMACAlgorithms();
$c2s_mac_algorithms =
isset($preferred['client_to_server']['mac']) ?
$preferred['client_to_server']['mac'] :
$this->getSupportedMACAlgorithms();
$s2c_compression_algorithms =
isset($preferred['server_to_client']['comp']) ?
$preferred['server_to_client']['comp'] :
$this->getSupportedCompressionAlgorithms();
$c2s_compression_algorithms =
isset($preferred['client_to_server']['comp']) ?
$preferred['client_to_server']['comp'] :
$this->getSupportedCompressionAlgorithms();
// some SSH servers have buggy implementations of some of the above
algorithms
switch (true) {
case $this->server_identifier == 'SSH-2.0-SSHD':
case substr($this->server_identifier, 0, 13) ==
'SSH-2.0-DLINK':
if
(!isset($preferred['server_to_client']['mac'])) {
$s2c_mac_algorithms = array_values(array_diff(
$s2c_mac_algorithms,
array('hmac-sha1-96',
'hmac-md5-96')
));
}
if
(!isset($preferred['client_to_server']['mac'])) {
$c2s_mac_algorithms = array_values(array_diff(
$c2s_mac_algorithms,
array('hmac-sha1-96',
'hmac-md5-96')
));
}
}
$str_kex_algorithms = implode(',', $kex_algorithms);
$str_server_host_key_algorithms = implode(',',
$server_host_key_algorithms);
$encryption_algorithms_server_to_client = implode(',',
$s2c_encryption_algorithms);
$encryption_algorithms_client_to_server = implode(',',
$c2s_encryption_algorithms);
$mac_algorithms_server_to_client = implode(',',
$s2c_mac_algorithms);
$mac_algorithms_client_to_server = implode(',',
$c2s_mac_algorithms);
$compression_algorithms_server_to_client = implode(',',
$s2c_compression_algorithms);
$compression_algorithms_client_to_server = implode(',',
$c2s_compression_algorithms);
$client_cookie = Random::string(16);
$kexinit_payload_client = pack(
'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
NET_SSH2_MSG_KEXINIT,
$client_cookie,
strlen($str_kex_algorithms),
$str_kex_algorithms,
strlen($str_server_host_key_algorithms),
$str_server_host_key_algorithms,
strlen($encryption_algorithms_client_to_server),
$encryption_algorithms_client_to_server,
strlen($encryption_algorithms_server_to_client),
$encryption_algorithms_server_to_client,
strlen($mac_algorithms_client_to_server),
$mac_algorithms_client_to_server,
strlen($mac_algorithms_server_to_client),
$mac_algorithms_server_to_client,
strlen($compression_algorithms_client_to_server),
$compression_algorithms_client_to_server,
strlen($compression_algorithms_server_to_client),
$compression_algorithms_server_to_client,
0,
'',
0,
'',
0,
0
);
if ($kexinit_payload_server === false) {
if (!$this->_send_binary_packet($kexinit_payload_client)) {
return false;
}
$kexinit_payload_server = $this->_get_binary_packet();
if ($kexinit_payload_server === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($kexinit_payload_server) ||
ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT) {
user_error('Expected SSH_MSG_KEXINIT');
return false;
}
$send_kex = false;
}
$response = $kexinit_payload_server;
$this->_string_shift($response, 1); // skip past the message
number (it should be SSH_MSG_KEXINIT)
$server_cookie = $this->_string_shift($response, 16);
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->kex_algorithms = explode(',',
$this->_string_shift($response, $temp['length']));
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->server_host_key_algorithms = explode(',',
$this->_string_shift($response, $temp['length']));
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->encryption_algorithms_client_to_server =
explode(',', $this->_string_shift($response,
$temp['length']));
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->encryption_algorithms_server_to_client =
explode(',', $this->_string_shift($response,
$temp['length']));
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->mac_algorithms_client_to_server = explode(',',
$this->_string_shift($response, $temp['length']));
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->mac_algorithms_server_to_client = explode(',',
$this->_string_shift($response, $temp['length']));
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->compression_algorithms_client_to_server =
explode(',', $this->_string_shift($response,
$temp['length']));
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->compression_algorithms_server_to_client =
explode(',', $this->_string_shift($response,
$temp['length']));
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->languages_client_to_server = explode(',',
$this->_string_shift($response, $temp['length']));
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->languages_server_to_client = explode(',',
$this->_string_shift($response, $temp['length']));
if (!strlen($response)) {
return false;
}
extract(unpack('Cfirst_kex_packet_follows',
$this->_string_shift($response, 1)));
$first_kex_packet_follows = $first_kex_packet_follows != 0;
if ($send_kex &&
!$this->_send_binary_packet($kexinit_payload_client)) {
return false;
}
// we need to decide upon the symmetric encryption algorithms
before we do the diffie-hellman key exchange
// we don't initialize any crypto-objects, yet - we do that,
later. for now, we need the lengths to make the
// diffie-hellman key exchange as fast as possible
$decrypt =
$this->_array_intersect_first($s2c_encryption_algorithms,
$this->encryption_algorithms_server_to_client);
$decryptKeyLength =
$this->_encryption_algorithm_to_key_size($decrypt);
if ($decryptKeyLength === null) {
user_error('No compatible server to client encryption
algorithms found');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$encrypt =
$this->_array_intersect_first($c2s_encryption_algorithms,
$this->encryption_algorithms_client_to_server);
$encryptKeyLength =
$this->_encryption_algorithm_to_key_size($encrypt);
if ($encryptKeyLength === null) {
user_error('No compatible client to server encryption
algorithms found');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
// through diffie-hellman key exchange a symmetric key is obtained
$this->kex_algorithm = $kex_algorithm =
$this->_array_intersect_first($kex_algorithms,
$this->kex_algorithms);
if ($kex_algorithm === false) {
user_error('No compatible key exchange algorithms
found');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$server_host_key_algorithm =
$this->_array_intersect_first($server_host_key_algorithms,
$this->server_host_key_algorithms);
if ($server_host_key_algorithm === false) {
user_error('No compatible server host key algorithms
found');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$mac_algorithm_in =
$this->_array_intersect_first($s2c_mac_algorithms,
$this->mac_algorithms_server_to_client);
if ($mac_algorithm_in === false) {
user_error('No compatible server to client message
authentication algorithms found');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$compression_algorithm_out =
$this->_array_intersect_first($c2s_compression_algorithms,
$this->compression_algorithms_client_to_server);
if ($compression_algorithm_out === false) {
user_error('No compatible client to server compression
algorithms found');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
//$this->decompress = $compression_algorithm_out ==
'zlib';
$compression_algorithm_in =
$this->_array_intersect_first($s2c_compression_algorithms,
$this->compression_algorithms_client_to_server);
if ($compression_algorithm_in === false) {
user_error('No compatible server to client compression
algorithms found');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
//$this->compress = $compression_algorithm_in ==
'zlib';
// Only relevant in diffie-hellman-group-exchange-sha{1,256},
otherwise empty.
$exchange_hash_rfc4419 = '';
if ($kex_algorithm === 'curve25519-sha256@libssh.org') {
$x = Random::string(32);
$eBytes = sodium_crypto_box_publickey_from_secretkey($x);
$clientKexInitMessage = 'NET_SSH2_MSG_KEX_ECDH_INIT';
$serverKexReplyMessage =
'NET_SSH2_MSG_KEX_ECDH_REPLY';
$kexHash = new Hash('sha256');
} else {
if (strpos($kex_algorithm,
'diffie-hellman-group-exchange') === 0) {
$dh_group_sizes_packed = pack(
'NNN',
$this->kex_dh_group_size_min,
$this->kex_dh_group_size_preferred,
$this->kex_dh_group_size_max
);
$packet = pack(
'Ca*',
NET_SSH2_MSG_KEXDH_GEX_REQUEST,
$dh_group_sizes_packed
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->_updateLogHistory('UNKNOWN (34)',
'NET_SSH2_MSG_KEXDH_GEX_REQUEST');
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) {
user_error('Expected
SSH_MSG_KEX_DH_GEX_GROUP');
return false;
}
$this->_updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY',
'NET_SSH2_MSG_KEXDH_GEX_GROUP');
if (strlen($response) < 4) {
return false;
}
extract(unpack('NprimeLength',
$this->_string_shift($response, 4)));
$primeBytes = $this->_string_shift($response,
$primeLength);
$prime = new BigInteger($primeBytes, -256);
if (strlen($response) < 4) {
return false;
}
extract(unpack('NgLength',
$this->_string_shift($response, 4)));
$gBytes = $this->_string_shift($response, $gLength);
$g = new BigInteger($gBytes, -256);
$exchange_hash_rfc4419 = pack(
'a*Na*Na*',
$dh_group_sizes_packed,
$primeLength,
$primeBytes,
$gLength,
$gBytes
);
$clientKexInitMessage =
'NET_SSH2_MSG_KEXDH_GEX_INIT';
$serverKexReplyMessage =
'NET_SSH2_MSG_KEXDH_GEX_REPLY';
} else {
switch ($kex_algorithm) {
// see http://tools.ietf.org/html/rfc2409#section-6.2
and
// http://tools.ietf.org/html/rfc2412, appendex E
case 'diffie-hellman-group1-sha1':
$prime =
'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74'
.
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437'
.
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED'
.
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
break;
// see http://tools.ietf.org/html/rfc3526#section-3
case 'diffie-hellman-group14-sha1':
$prime =
'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74'
.
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437'
.
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED'
.
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05'
.
'98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB'
.
'9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B'
.
'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718'
.
'3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF';
break;
}
// For both diffie-hellman-group1-sha1 and
diffie-hellman-group14-sha1
// the generator field element is 2 (decimal) and the hash
function is sha1.
$g = new BigInteger(2);
$prime = new BigInteger($prime, 16);
$clientKexInitMessage =
'NET_SSH2_MSG_KEXDH_INIT';
$serverKexReplyMessage =
'NET_SSH2_MSG_KEXDH_REPLY';
}
switch ($kex_algorithm) {
case 'diffie-hellman-group-exchange-sha256':
$kexHash = new Hash('sha256');
break;
default:
$kexHash = new Hash('sha1');
}
/* To increase the speed of the key exchange, both client and
server may
reduce the size of their private exponents. It should be at
least
twice as long as the key material that is generated from the
shared
secret. For more details, see the paper by van Oorschot and
Wiener
[VAN-OORSCHOT].
-- http://tools.ietf.org/html/rfc4419#section-6.2 */
$one = new BigInteger(1);
$keyLength = min($kexHash->getLength(),
max($encryptKeyLength, $decryptKeyLength));
$max = $one->bitwise_leftShift(16 * $keyLength); // 2 * 8 *
$keyLength
$max = $max->subtract($one);
$x = $one->random($one, $max);
$e = $g->modPow($x, $prime);
$eBytes = $e->toBytes(true);
}
$data = pack('CNa*', constant($clientKexInitMessage),
strlen($eBytes), $eBytes);
if (!$this->_send_binary_packet($data)) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
switch ($clientKexInitMessage) {
case 'NET_SSH2_MSG_KEX_ECDH_INIT':
$this->_updateLogHistory('NET_SSH2_MSG_KEXDH_INIT',
'NET_SSH2_MSG_KEX_ECDH_INIT');
break;
case 'NET_SSH2_MSG_KEXDH_GEX_INIT':
$this->_updateLogHistory('UNKNOWN (32)',
'NET_SSH2_MSG_KEXDH_GEX_INIT');
}
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($response)) {
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
if ($type != constant($serverKexReplyMessage)) {
user_error("Expected $serverKexReplyMessage");
return false;
}
switch ($serverKexReplyMessage) {
case 'NET_SSH2_MSG_KEX_ECDH_REPLY':
$this->_updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY',
'NET_SSH2_MSG_KEX_ECDH_REPLY');
break;
case 'NET_SSH2_MSG_KEXDH_GEX_REPLY':
$this->_updateLogHistory('UNKNOWN (33)',
'NET_SSH2_MSG_KEXDH_GEX_REPLY');
}
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->server_public_host_key = $server_public_host_key =
$this->_string_shift($response, $temp['length']);
if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($server_public_host_key, 4));
$public_key_format =
$this->_string_shift($server_public_host_key,
$temp['length']);
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$fBytes = $this->_string_shift($response,
$temp['length']);
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->signature = $this->_string_shift($response,
$temp['length']);
if (strlen($this->signature) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($this->signature, 4));
$this->signature_format =
$this->_string_shift($this->signature, $temp['length']);
if ($kex_algorithm === 'curve25519-sha256@libssh.org') {
if (strlen($fBytes) !== 32) {
user_error('Received curve25519 public key of invalid
length.');
return false;
}
$key = new BigInteger(sodium_crypto_scalarmult($x, $fBytes),
256);
// sodium_compat doesn't emulate sodium_memzero
// also, with v1 of libsodium API the extension identifies
itself as
// libsodium whereas v2 of the libsodium API (what PHP 7.2+
includes)
// identifies itself as sodium. sodium_compat uses the v1 API
to
// emulate the v2 API if it's the v1 API that's
available
if (extension_loaded('sodium') ||
extension_loaded('libsodium')) {
sodium_memzero($x);
}
} else {
$f = new BigInteger($fBytes, -256);
$key = $f->modPow($x, $prime);
}
$keyBytes = $key->toBytes(true);
$this->exchange_hash = pack(
'Na*Na*Na*Na*Na*a*Na*Na*Na*',
strlen($this->identifier),
$this->identifier,
strlen($this->server_identifier),
$this->server_identifier,
strlen($kexinit_payload_client),
$kexinit_payload_client,
strlen($kexinit_payload_server),
$kexinit_payload_server,
strlen($this->server_public_host_key),
$this->server_public_host_key,
$exchange_hash_rfc4419,
strlen($eBytes),
$eBytes,
strlen($fBytes),
$fBytes,
strlen($keyBytes),
$keyBytes
);
$this->exchange_hash =
$kexHash->hash($this->exchange_hash);
if ($this->session_id === false) {
$this->session_id = $this->exchange_hash;
}
switch ($server_host_key_algorithm) {
case 'ssh-dss':
$expected_key_format = 'ssh-dss';
break;
//case 'rsa-sha2-256':
//case 'rsa-sha2-512':
//case 'ssh-rsa':
default:
$expected_key_format = 'ssh-rsa';
}
if ($public_key_format != $expected_key_format ||
$this->signature_format != $server_host_key_algorithm) {
switch (true) {
case $this->signature_format ==
$server_host_key_algorithm:
case $server_host_key_algorithm != 'rsa-sha2-256'
&& $server_host_key_algorithm != 'rsa-sha2-512':
case $this->signature_format != 'ssh-rsa':
user_error('Server Host Key Algorithm
Mismatch');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
}
$packet = pack(
'C',
NET_SSH2_MSG_NEWKEYS
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($response)) {
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
if ($type != NET_SSH2_MSG_NEWKEYS) {
user_error('Expected SSH_MSG_NEWKEYS');
return false;
}
$keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
$this->encrypt =
$this->_encryption_algorithm_to_crypt_instance($encrypt);
if ($this->encrypt) {
if ($this->crypto_engine) {
$this->encrypt->setPreferredEngine($this->crypto_engine);
}
if ($this->encrypt->block_size) {
$this->encrypt_block_size =
$this->encrypt->block_size;
}
$this->encrypt->enableContinuousBuffer();
$this->encrypt->disablePadding();
if ($this->encrypt->getBlockLength()) {
$this->encrypt_block_size =
$this->encrypt->getBlockLength() >> 3;
}
$iv = $kexHash->hash($keyBytes . $this->exchange_hash .
'A' . $this->session_id);
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));
$key = $kexHash->hash($keyBytes . $this->exchange_hash .
'C' . $this->session_id);
while ($encryptKeyLength > strlen($key)) {
$key.= $kexHash->hash($keyBytes .
$this->exchange_hash . $key);
}
$this->encrypt->setKey(substr($key, 0,
$encryptKeyLength));
$this->encrypt->name = $decrypt;
}
$this->decrypt =
$this->_encryption_algorithm_to_crypt_instance($decrypt);
if ($this->decrypt) {
if ($this->crypto_engine) {
$this->decrypt->setPreferredEngine($this->crypto_engine);
}
if ($this->decrypt->block_size) {
$this->decrypt_block_size =
$this->decrypt->block_size;
}
$this->decrypt->enableContinuousBuffer();
$this->decrypt->disablePadding();
if ($this->decrypt->getBlockLength()) {
$this->decrypt_block_size =
$this->decrypt->getBlockLength() >> 3;
}
$iv = $kexHash->hash($keyBytes . $this->exchange_hash .
'B' . $this->session_id);
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));
$key = $kexHash->hash($keyBytes . $this->exchange_hash .
'D' . $this->session_id);
while ($decryptKeyLength > strlen($key)) {
$key.= $kexHash->hash($keyBytes .
$this->exchange_hash . $key);
}
$this->decrypt->setKey(substr($key, 0,
$decryptKeyLength));
$this->decrypt->name = $decrypt;
}
/* The "arcfour128" algorithm is the RC4 cipher, as
described in
[SCHNEIER], using a 128-bit key. The first 1536 bytes of
keystream
generated by the cipher MUST be discarded, and the first byte of
the
first encrypted packet MUST be encrypted using the 1537th byte
of
keystream.
-- http://tools.ietf.org/html/rfc4345#section-4 */
if ($encrypt == 'arcfour128' || $encrypt ==
'arcfour256') {
$this->encrypt->encrypt(str_repeat("\0",
1536));
}
if ($decrypt == 'arcfour128' || $decrypt ==
'arcfour256') {
$this->decrypt->decrypt(str_repeat("\0",
1536));
}
$mac_algorithm_out =
$this->_array_intersect_first($c2s_mac_algorithms,
$this->mac_algorithms_client_to_server);
if ($mac_algorithm_out === false) {
user_error('No compatible client to server message
authentication algorithms found');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$createKeyLength = 0; // ie. $mac_algorithm == 'none'
switch ($mac_algorithm_out) {
case 'hmac-sha2-256':
$this->hmac_create = new Hash('sha256');
$createKeyLength = 32;
break;
case 'hmac-sha1':
$this->hmac_create = new Hash('sha1');
$createKeyLength = 20;
break;
case 'hmac-sha1-96':
$this->hmac_create = new Hash('sha1-96');
$createKeyLength = 20;
break;
case 'hmac-md5':
$this->hmac_create = new Hash('md5');
$createKeyLength = 16;
break;
case 'hmac-md5-96':
$this->hmac_create = new Hash('md5-96');
$createKeyLength = 16;
}
$this->hmac_create->name = $mac_algorithm_out;
$checkKeyLength = 0;
$this->hmac_size = 0;
switch ($mac_algorithm_in) {
case 'hmac-sha2-256':
$this->hmac_check = new Hash('sha256');
$checkKeyLength = 32;
$this->hmac_size = 32;
break;
case 'hmac-sha1':
$this->hmac_check = new Hash('sha1');
$checkKeyLength = 20;
$this->hmac_size = 20;
break;
case 'hmac-sha1-96':
$this->hmac_check = new Hash('sha1-96');
$checkKeyLength = 20;
$this->hmac_size = 12;
break;
case 'hmac-md5':
$this->hmac_check = new Hash('md5');
$checkKeyLength = 16;
$this->hmac_size = 16;
break;
case 'hmac-md5-96':
$this->hmac_check = new Hash('md5-96');
$checkKeyLength = 16;
$this->hmac_size = 12;
}
$this->hmac_check->name = $mac_algorithm_in;
$key = $kexHash->hash($keyBytes . $this->exchange_hash .
'E' . $this->session_id);
while ($createKeyLength > strlen($key)) {
$key.= $kexHash->hash($keyBytes . $this->exchange_hash .
$key);
}
$this->hmac_create->setKey(substr($key, 0,
$createKeyLength));
$key = $kexHash->hash($keyBytes . $this->exchange_hash .
'F' . $this->session_id);
while ($checkKeyLength > strlen($key)) {
$key.= $kexHash->hash($keyBytes . $this->exchange_hash .
$key);
}
$this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
return true;
}
/**
* Maps an encryption algorithm name to the number of key bytes.
*
* @param string $algorithm Name of the encryption algorithm
* @return int|null Number of bytes as an integer or null for unknown
* @access private
*/
function _encryption_algorithm_to_key_size($algorithm)
{
if ($this->bad_key_size_fix &&
$this->_bad_algorithm_candidate($algorithm)) {
return 16;
}
switch ($algorithm) {
case 'none':
return 0;
case 'aes128-cbc':
case 'aes128-ctr':
case 'arcfour':
case 'arcfour128':
case 'blowfish-cbc':
case 'blowfish-ctr':
case 'twofish128-cbc':
case 'twofish128-ctr':
return 16;
case '3des-cbc':
case '3des-ctr':
case 'aes192-cbc':
case 'aes192-ctr':
case 'twofish192-cbc':
case 'twofish192-ctr':
return 24;
case 'aes256-cbc':
case 'aes256-ctr':
case 'arcfour256':
case 'twofish-cbc':
case 'twofish256-cbc':
case 'twofish256-ctr':
return 32;
}
return null;
}
/**
* Maps an encryption algorithm name to an instance of a subclass of
* \phpseclib\Crypt\Base.
*
* @param string $algorithm Name of the encryption algorithm
* @return mixed Instance of \phpseclib\Crypt\Base or null for unknown
* @access private
*/
function _encryption_algorithm_to_crypt_instance($algorithm)
{
switch ($algorithm) {
case '3des-cbc':
return new TripleDES();
case '3des-ctr':
return new TripleDES(Base::MODE_CTR);
case 'aes256-cbc':
case 'aes192-cbc':
case 'aes128-cbc':
return new Rijndael();
case 'aes256-ctr':
case 'aes192-ctr':
case 'aes128-ctr':
return new Rijndael(Base::MODE_CTR);
case 'blowfish-cbc':
return new Blowfish();
case 'blowfish-ctr':
return new Blowfish(Base::MODE_CTR);
case 'twofish128-cbc':
case 'twofish192-cbc':
case 'twofish256-cbc':
case 'twofish-cbc':
return new Twofish();
case 'twofish128-ctr':
case 'twofish192-ctr':
case 'twofish256-ctr':
return new Twofish(Base::MODE_CTR);
case 'arcfour':
case 'arcfour128':
case 'arcfour256':
return new RC4();
}
return null;
}
/**
* Tests whether or not proposed algorithm has a potential for issues
*
* @link
https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html
* @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291
* @param string $algorithm Name of the encryption algorithm
* @return bool
* @access private
*/
function _bad_algorithm_candidate($algorithm)
{
switch ($algorithm) {
case 'arcfour256':
case 'aes192-ctr':
case 'aes256-ctr':
return true;
}
return false;
}
/**
* Login
*
* The $password parameter can be a plaintext password, a
\phpseclib\Crypt\RSA object or an array
*
* @param string $username
* @return bool
* @see self::_login()
* @access public
*/
function login($username)
{
$args = func_get_args();
$this->auth[] = $args;
// try logging with 'none' as an authentication method
first since that's what
// PuTTY does
if (substr($this->server_identifier, 0, 13) !=
'SSH-2.0-CoreFTP' && $this->auth_methods_to_continue
=== null) {
if ($this->_login($username)) {
return true;
}
if (count($args) == 1) {
return false;
}
}
return call_user_func_array(array(&$this, '_login'),
$args);
}
/**
* Login Helper
*
* @param string $username
* @return bool
* @see self::_login_helper()
* @access private
*/
function _login($username)
{
if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
if (!$this->_connect()) {
return false;
}
}
$args = array_slice(func_get_args(), 1);
if (empty($args)) {
return $this->_login_helper($username);
}
foreach ($args as $arg) {
if ($this->_login_helper($username, $arg)) {
return true;
}
}
return false;
}
/**
* Login Helper
*
* @param string $username
* @param string $password
* @return bool
* @access private
* @internal It might be worthwhile, at some point, to protect against
{@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
* by sending dummy SSH_MSG_IGNORE messages.
*/
function _login_helper($username, $password = null)
{
if (!($this->bitmap & self::MASK_CONNECTED)) {
return false;
}
if (!($this->bitmap & self::MASK_LOGIN_REQ)) {
$packet = pack(
'CNa*',
NET_SSH2_MSG_SERVICE_REQUEST,
strlen('ssh-userauth'),
'ssh-userauth'
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$response = $this->_get_binary_packet();
if ($response === false) {
if ($this->retry_connect) {
$this->retry_connect = false;
if (!$this->_connect()) {
return false;
}
return $this->_login_helper($username, $password);
}
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
user_error('Expected SSH_MSG_SERVICE_ACCEPT');
return false;
}
$this->bitmap |= self::MASK_LOGIN_REQ;
}
if (strlen($this->last_interactive_response)) {
return !is_string($password) && !is_array($password) ?
false : $this->_keyboard_interactive_process($password);
}
if ($password instanceof RSA) {
return $this->_privatekey_login($username, $password);
} elseif ($password instanceof Agent) {
return $this->_ssh_agent_login($username, $password);
}
if (is_array($password)) {
if ($this->_keyboard_interactive_login($username,
$password)) {
$this->bitmap |= self::MASK_LOGIN;
return true;
}
return false;
}
if (!isset($password)) {
$packet = pack(
'CNa*Na*Na*',
NET_SSH2_MSG_USERAUTH_REQUEST,
strlen($username),
$username,
strlen('ssh-connection'),
'ssh-connection',
strlen('none'),
'none'
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($response)) {
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
switch ($type) {
case NET_SSH2_MSG_USERAUTH_SUCCESS:
$this->bitmap |= self::MASK_LOGIN;
return true;
case NET_SSH2_MSG_USERAUTH_FAILURE:
extract(unpack('Nmethodlistlen',
$this->_string_shift($response, 4)));
$this->auth_methods_to_continue =
explode(',', $this->_string_shift($response, $methodlistlen));
default:
return false;
}
}
$packet = pack(
'CNa*Na*Na*CNa*',
NET_SSH2_MSG_USERAUTH_REQUEST,
strlen($username),
$username,
strlen('ssh-connection'),
'ssh-connection',
strlen('password'),
'password',
0,
strlen($password),
$password
);
// remove the username and password from the logged packet
if (!defined('NET_SSH2_LOGGING')) {
$logged = null;
} else {
$logged = pack(
'CNa*Na*Na*CNa*',
NET_SSH2_MSG_USERAUTH_REQUEST,
strlen('username'),
'username',
strlen('ssh-connection'),
'ssh-connection',
strlen('password'),
'password',
0,
strlen('password'),
'password'
);
}
if (!$this->_send_binary_packet($packet, $logged)) {
return false;
}
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($response)) {
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
switch ($type) {
case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the
password can be changed
$this->_updateLogHistory('UNKNOWN (60)',
'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ');
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$this->errors[] =
'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' .
$this->_string_shift($response, $length);
return
$this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
case NET_SSH2_MSG_USERAUTH_FAILURE:
// can we use keyboard-interactive authentication? if not
then either the login is bad or the server employees
// multi-factor authentication
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$auth_methods = explode(',',
$this->_string_shift($response, $length));
$this->auth_methods_to_continue = $auth_methods;
if (!strlen($response)) {
return false;
}
extract(unpack('Cpartial_success',
$this->_string_shift($response, 1)));
$partial_success = $partial_success != 0;
if (!$partial_success &&
in_array('keyboard-interactive', $auth_methods)) {
if ($this->_keyboard_interactive_login($username,
$password)) {
$this->bitmap |= self::MASK_LOGIN;
return true;
}
return false;
}
return false;
case NET_SSH2_MSG_USERAUTH_SUCCESS:
$this->bitmap |= self::MASK_LOGIN;
return true;
}
return false;
}
/**
* Login via keyboard-interactive authentication
*
* See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details.
This is not a full-featured keyboard-interactive authenticator.
*
* @param string $username
* @param string $password
* @return bool
* @access private
*/
function _keyboard_interactive_login($username, $password)
{
$packet = pack(
'CNa*Na*Na*Na*Na*',
NET_SSH2_MSG_USERAUTH_REQUEST,
strlen($username),
$username,
strlen('ssh-connection'),
'ssh-connection',
strlen('keyboard-interactive'),
'keyboard-interactive',
0,
'',
0,
''
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
return $this->_keyboard_interactive_process($password);
}
/**
* Handle the keyboard-interactive requests / responses.
*
* @return bool
* @access private
*/
function _keyboard_interactive_process()
{
$responses = func_get_args();
if (strlen($this->last_interactive_response)) {
$response = $this->last_interactive_response;
} else {
$orig = $response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
}
if (!strlen($response)) {
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
switch ($type) {
case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$this->_string_shift($response, $length); // name; may
be empty
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$this->_string_shift($response, $length); //
instruction; may be empty
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$this->_string_shift($response, $length); // language
tag; may be empty
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nnum_prompts',
$this->_string_shift($response, 4)));
for ($i = 0; $i < count($responses); $i++) {
if (is_array($responses[$i])) {
foreach ($responses[$i] as $key => $value) {
$this->keyboard_requests_responses[$key] =
$value;
}
unset($responses[$i]);
}
}
$responses = array_values($responses);
if (isset($this->keyboard_requests_responses)) {
for ($i = 0; $i < $num_prompts; $i++) {
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
// prompt - ie. "Password: "; must not be
empty
$prompt = $this->_string_shift($response,
$length);
//$echo = $this->_string_shift($response) !=
chr(0);
foreach ($this->keyboard_requests_responses as
$key => $value) {
if (substr($prompt, 0, strlen($key)) == $key) {
$responses[] = $value;
break;
}
}
}
}
// see http://tools.ietf.org/html/rfc4256#section-3.2
if (strlen($this->last_interactive_response)) {
$this->last_interactive_response = '';
} else {
$this->_updateLogHistory('UNKNOWN (60)',
'NET_SSH2_MSG_USERAUTH_INFO_REQUEST');
}
if (!count($responses) && $num_prompts) {
$this->last_interactive_response = $orig;
return false;
}
/*
After obtaining the requested information from the user,
the client
MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE
message.
*/
// see http://tools.ietf.org/html/rfc4256#section-3.4
$packet = $logged = pack('CN',
NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses));
for ($i = 0; $i < count($responses); $i++) {
$packet.= pack('Na*', strlen($responses[$i]),
$responses[$i]);
$logged.= pack('Na*',
strlen('dummy-answer'), 'dummy-answer');
}
if (!$this->_send_binary_packet($packet, $logged)) {
return false;
}
$this->_updateLogHistory('UNKNOWN (61)',
'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE');
/*
After receiving the response, the server MUST send
either an
SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or
another
SSH_MSG_USERAUTH_INFO_REQUEST message.
*/
// maybe phpseclib should force close the connection after
x request / responses? unless something like that is done
// there could be an infinite loop of request / responses.
return $this->_keyboard_interactive_process();
case NET_SSH2_MSG_USERAUTH_SUCCESS:
return true;
case NET_SSH2_MSG_USERAUTH_FAILURE:
extract(unpack('Nmethodlistlen',
$this->_string_shift($response, 4)));
$this->auth_methods_to_continue = explode(',',
$this->_string_shift($response, $methodlistlen));
return false;
}
return false;
}
/**
* Login with an ssh-agent provided key
*
* @param string $username
* @param \phpseclib\System\SSH\Agent $agent
* @return bool
* @access private
*/
function _ssh_agent_login($username, $agent)
{
$this->agent = $agent;
$keys = $agent->requestIdentities();
foreach ($keys as $key) {
if ($this->_privatekey_login($username, $key)) {
return true;
}
}
return false;
}
/**
* Login with an RSA private key
*
* @param string $username
* @param \phpseclib\Crypt\RSA $privatekey
* @return bool
* @access private
* @internal It might be worthwhile, at some point, to protect against
{@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
* by sending dummy SSH_MSG_IGNORE messages.
*/
function _privatekey_login($username, $privatekey)
{
// see http://tools.ietf.org/html/rfc4253#page-15
$publickey = $privatekey->getPublicKey(RSA::PUBLIC_FORMAT_RAW);
if ($publickey === false) {
return false;
}
$publickey = array(
'e' =>
$publickey['e']->toBytes(true),
'n' => $publickey['n']->toBytes(true)
);
$publickey = pack(
'Na*Na*Na*',
strlen('ssh-rsa'),
'ssh-rsa',
strlen($publickey['e']),
$publickey['e'],
strlen($publickey['n']),
$publickey['n']
);
switch ($this->signature_format) {
case 'rsa-sha2-512':
$hash = 'sha512';
$signatureType = 'rsa-sha2-512';
break;
case 'rsa-sha2-256':
$hash = 'sha256';
$signatureType = 'rsa-sha2-256';
break;
//case 'ssh-rsa':
default:
$hash = 'sha1';
$signatureType = 'ssh-rsa';
}
$part1 = pack(
'CNa*Na*Na*',
NET_SSH2_MSG_USERAUTH_REQUEST,
strlen($username),
$username,
strlen('ssh-connection'),
'ssh-connection',
strlen('publickey'),
'publickey'
);
$part2 = pack('Na*Na*', strlen($signatureType),
$signatureType, strlen($publickey), $publickey);
$packet = $part1 . chr(0) . $part2;
if (!$this->_send_binary_packet($packet)) {
return false;
}
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($response)) {
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
switch ($type) {
case NET_SSH2_MSG_USERAUTH_FAILURE:
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nmethodlistlen',
$this->_string_shift($response, 4)));
$this->auth_methods_to_continue = explode(',',
$this->_string_shift($response, $methodlistlen));
$this->errors[] = 'SSH_MSG_USERAUTH_FAILURE';
return false;
case NET_SSH2_MSG_USERAUTH_PK_OK:
// we'll just take it on faith that the public key
blob and the public key algorithm name are as
// they should be
$this->_updateLogHistory('UNKNOWN (60)',
'NET_SSH2_MSG_USERAUTH_PK_OK');
break;
case NET_SSH2_MSG_USERAUTH_SUCCESS:
$this->bitmap |= self::MASK_LOGIN;
return true;
default:
user_error('Unexpected response to publickey
authentication pt 1');
return
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
$packet = $part1 . chr(1) . $part2;
$privatekey->setSignatureMode(RSA::SIGNATURE_PKCS1);
$privatekey->setHash($hash);
$signature = $privatekey->sign(pack('Na*a*',
strlen($this->session_id), $this->session_id, $packet));
$signature = pack('Na*Na*', strlen($signatureType),
$signatureType, strlen($signature), $signature);
$packet.= pack('Na*', strlen($signature), $signature);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($response)) {
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
switch ($type) {
case NET_SSH2_MSG_USERAUTH_FAILURE:
// either the login is bad or the server employs
multi-factor authentication
extract(unpack('Nmethodlistlen',
$this->_string_shift($response, 4)));
$this->auth_methods_to_continue = explode(',',
$this->_string_shift($response, $methodlistlen));
return false;
case NET_SSH2_MSG_USERAUTH_SUCCESS:
$this->bitmap |= self::MASK_LOGIN;
return true;
}
user_error('Unexpected response to publickey authentication pt
2');
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
/**
* Set Timeout
*
* $ssh->exec('ping 127.0.0.1'); on a Linux host will
never return and will run indefinitely. setTimeout() makes it so
it'll timeout.
* Setting $timeout to false or 0 will mean there is no timeout.
*
* @param mixed $timeout
* @access public
*/
function setTimeout($timeout)
{
$this->timeout = $this->curTimeout = $timeout;
}
/**
* Set Keep Alive
*
* Sends an SSH2_MSG_IGNORE message every x seconds, if x is a positive
non-zero number.
*
* @param int $interval
* @access public
*/
function setKeepAlive($interval)
{
$this->keepAlive = $interval;
}
/**
* Get the output from stdError
*
* @access public
*/
function getStdError()
{
return $this->stdErrorLog;
}
/**
* Execute Command
*
* If $callback is set to false then
\phpseclib\Net\SSH2::_get_channel_packet(self::CHANNEL_EXEC) will need to
be called manually.
* In all likelihood, this is not a feature you want to be taking
advantage of.
*
* @param string $command
* @param Callback $callback
* @return string
* @access public
*/
function exec($command, $callback = null)
{
$this->curTimeout = $this->timeout;
$this->is_timeout = false;
$this->stdErrorLog = '';
if (!$this->isAuthenticated()) {
return false;
}
if ($this->in_request_pty_exec) {
user_error('If you want to run multiple exec()\'s you
will need to disable (and re-enable if appropriate) a PTY for each
one.');
return false;
}
// RFC4254 defines the (client) window size as "bytes the
other party can send before it must wait for the window to
// be adjusted". 0x7FFFFFFF is, at 2GB, the max size.
technically, it should probably be decremented, but,
// honestly, if you're transferring more than 2GB, you
probably shouldn't be using phpseclib, anyway.
// see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
$this->window_size_server_to_client[self::CHANNEL_EXEC] =
$this->window_size;
// 0x8000 is the maximum max packet size, per
http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
// uses 0x4000, that's what will be used here, as well.
$packet_size = 0x4000;
$packet = pack(
'CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN,
strlen('session'),
'session',
self::CHANNEL_EXEC,
$this->window_size_server_to_client[self::CHANNEL_EXEC],
$packet_size
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[self::CHANNEL_EXEC] =
NET_SSH2_MSG_CHANNEL_OPEN;
$response = $this->_get_channel_packet(self::CHANNEL_EXEC);
if ($response === false) {
return false;
}
if ($this->request_pty === true) {
$terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
$packet = pack(
'CNNa*CNa*N5a*',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_EXEC],
strlen('pty-req'),
'pty-req',
1,
strlen('vt100'),
'vt100',
$this->windowColumns,
$this->windowRows,
0,
0,
strlen($terminal_modes),
$terminal_modes
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($response)) {
return false;
}
list(, $type) = unpack('C',
$this->_string_shift($response, 1));
switch ($type) {
case NET_SSH2_MSG_CHANNEL_SUCCESS:
break;
case NET_SSH2_MSG_CHANNEL_FAILURE:
default:
user_error('Unable to request
pseudo-terminal');
return
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
$this->in_request_pty_exec = true;
}
// sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary
and, in fact, in most cases, slows things
// down. the one place where it might be desirable is if
you're doing something like \phpseclib\Net\SSH2::exec('ping
localhost &').
// with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return
immediately and the ping process will then
// then immediately terminate. without such a request exec() will
loop indefinitely. the ping process won't end but
// neither will your script.
// although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could
exceed the maximum packet size established by
// SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states
that the "maximum packet size" refers to the
// "maximum size of an individual data packet". ie.
SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates.
$packet = pack(
'CNNa*CNa*',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_EXEC],
strlen('exec'),
'exec',
1,
strlen($command),
$command
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[self::CHANNEL_EXEC] =
NET_SSH2_MSG_CHANNEL_REQUEST;
$response = $this->_get_channel_packet(self::CHANNEL_EXEC);
if ($response === false) {
return false;
}
$this->channel_status[self::CHANNEL_EXEC] =
NET_SSH2_MSG_CHANNEL_DATA;
if ($callback === false || $this->in_request_pty_exec) {
return true;
}
$output = '';
while (true) {
$temp = $this->_get_channel_packet(self::CHANNEL_EXEC);
switch (true) {
case $temp === true:
return is_callable($callback) ? true : $output;
case $temp === false:
return false;
default:
if (is_callable($callback)) {
if (call_user_func($callback, $temp) === true) {
$this->_close_channel(self::CHANNEL_EXEC);
return true;
}
} else {
$output.= $temp;
}
}
}
}
/**
* Creates an interactive shell
*
* @see self::read()
* @see self::write()
* @return bool
* @access private
*/
function _initShell()
{
if ($this->in_request_pty_exec === true) {
return true;
}
$this->window_size_server_to_client[self::CHANNEL_SHELL] =
$this->window_size;
$packet_size = 0x4000;
$packet = pack(
'CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN,
strlen('session'),
'session',
self::CHANNEL_SHELL,
$this->window_size_server_to_client[self::CHANNEL_SHELL],
$packet_size
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[self::CHANNEL_SHELL] =
NET_SSH2_MSG_CHANNEL_OPEN;
$response = $this->_get_channel_packet(self::CHANNEL_SHELL);
if ($response === false) {
return false;
}
$terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
$packet = pack(
'CNNa*CNa*N5a*',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_SHELL],
strlen('pty-req'),
'pty-req',
1,
strlen('vt100'),
'vt100',
$this->windowColumns,
$this->windowRows,
0,
0,
strlen($terminal_modes),
$terminal_modes
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$packet = pack(
'CNNa*C',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_SHELL],
strlen('shell'),
'shell',
1
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[self::CHANNEL_SHELL] =
NET_SSH2_MSG_IGNORE;
$this->bitmap |= self::MASK_SHELL;
return true;
}
/**
* Return the channel to be used with read() / write()
*
* @see self::read()
* @see self::write()
* @return int
* @access public
*/
function _get_interactive_channel()
{
switch (true) {
case $this->in_subsystem:
return self::CHANNEL_SUBSYSTEM;
case $this->in_request_pty_exec:
return self::CHANNEL_EXEC;
default:
return self::CHANNEL_SHELL;
}
}
/**
* Return an available open channel
*
* @return int
* @access public
*/
function _get_open_channel()
{
$channel = self::CHANNEL_EXEC;
do {
if (isset($this->channel_status[$channel]) &&
$this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) {
return $channel;
}
} while ($channel++ < self::CHANNEL_SUBSYSTEM);
return false;
}
/**
* Returns the output of an interactive shell
*
* Returns when there's a match for $expect, which can take the
form of a string literal or,
* if $mode == self::READ_REGEX, a regular expression.
*
* @see self::write()
* @param string $expect
* @param int $mode
* @return string|bool
* @access public
*/
function read($expect = '', $mode = self::READ_SIMPLE)
{
$this->curTimeout = $this->timeout;
$this->is_timeout = false;
if (!$this->isAuthenticated()) {
user_error('Operation disallowed prior to login()');
return false;
}
if (!($this->bitmap & self::MASK_SHELL) &&
!$this->_initShell()) {
user_error('Unable to initiate an interactive shell
session');
return false;
}
$channel = $this->_get_interactive_channel();
if ($mode == self::READ_NEXT) {
return $this->_get_channel_packet($channel);
}
$match = $expect;
while (true) {
if ($mode == self::READ_REGEX) {
preg_match($expect, substr($this->interactiveBuffer,
-1024), $matches);
$match = isset($matches[0]) ? $matches[0] : '';
}
$pos = strlen($match) ? strpos($this->interactiveBuffer,
$match) : false;
if ($pos !== false) {
return $this->_string_shift($this->interactiveBuffer,
$pos + strlen($match));
}
$response = $this->_get_channel_packet($channel);
if (is_bool($response)) {
$this->in_request_pty_exec = false;
return $response ?
$this->_string_shift($this->interactiveBuffer,
strlen($this->interactiveBuffer)) : false;
}
$this->interactiveBuffer.= $response;
}
}
/**
* Inputs a command into an interactive shell.
*
* @see self::read()
* @param string $cmd
* @return bool
* @access public
*/
function write($cmd)
{
if (!$this->isAuthenticated()) {
user_error('Operation disallowed prior to login()');
return false;
}
if (!($this->bitmap & self::MASK_SHELL) &&
!$this->_initShell()) {
user_error('Unable to initiate an interactive shell
session');
return false;
}
return
$this->_send_channel_packet($this->_get_interactive_channel(), $cmd);
}
/**
* Start a subsystem.
*
* Right now only one subsystem at a time is supported. To support
multiple subsystem's stopSubsystem() could accept
* a string that contained the name of the subsystem, but at that
point, only one subsystem of each type could be opened.
* To support multiple subsystem's of the same name maybe
it'd be best if startSubsystem() generated a new channel id and
* returns that and then that that was passed into stopSubsystem() but
that'll be saved for a future date and implemented
* if there's sufficient demand for such a feature.
*
* @see self::stopSubsystem()
* @param string $subsystem
* @return bool
* @access public
*/
function startSubsystem($subsystem)
{
$this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] =
$this->window_size;
$packet = pack(
'CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN,
strlen('session'),
'session',
self::CHANNEL_SUBSYSTEM,
$this->window_size,
0x4000
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[self::CHANNEL_SUBSYSTEM] =
NET_SSH2_MSG_CHANNEL_OPEN;
$response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM);
if ($response === false) {
return false;
}
$packet = pack(
'CNNa*CNa*',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_SUBSYSTEM],
strlen('subsystem'),
'subsystem',
1,
strlen($subsystem),
$subsystem
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[self::CHANNEL_SUBSYSTEM] =
NET_SSH2_MSG_CHANNEL_REQUEST;
$response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM);
if ($response === false) {
return false;
}
$this->channel_status[self::CHANNEL_SUBSYSTEM] =
NET_SSH2_MSG_CHANNEL_DATA;
$this->bitmap |= self::MASK_SHELL;
$this->in_subsystem = true;
return true;
}
/**
* Stops a subsystem.
*
* @see self::startSubsystem()
* @return bool
* @access public
*/
function stopSubsystem()
{
$this->in_subsystem = false;
$this->_close_channel(self::CHANNEL_SUBSYSTEM);
return true;
}
/**
* Closes a channel
*
* If read() timed out you might want to just close the channel and
have it auto-restart on the next read() call
*
* @access public
*/
function reset()
{
$this->_close_channel($this->_get_interactive_channel());
}
/**
* Is timeout?
*
* Did exec() or read() return because they timed out or because they
encountered the end?
*
* @access public
*/
function isTimeout()
{
return $this->is_timeout;
}
/**
* Disconnect
*
* @access public
*/
function disconnect()
{
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
if (isset($this->realtime_log_file) &&
is_resource($this->realtime_log_file)) {
fclose($this->realtime_log_file);
}
}
/**
* Destructor.
*
* Will be called, automatically, if you're supporting just PHP5.
If you're supporting PHP4, you'll need to call
* disconnect().
*
* @access public
*/
function __destruct()
{
$this->disconnect();
}
/**
* Is the connection still active?
*
* @return bool
* @access public
*/
function isConnected()
{
return (bool) ($this->bitmap & self::MASK_CONNECTED);
}
/**
* Have you successfully been logged in?
*
* @return bool
* @access public
*/
function isAuthenticated()
{
return (bool) ($this->bitmap & self::MASK_LOGIN);
}
/**
* Pings a server connection, or tries to reconnect if the connection
has gone down
*
* Inspired by http://php.net/manual/en/mysqli.ping.php
*
* @return bool
* @access public
*/
function ping()
{
if (!$this->isAuthenticated()) {
if (!empty($this->auth)) {
return $this->_reconnect();
}
return false;
}
$this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] =
$this->window_size;
$packet_size = 0x4000;
$packet = pack(
'CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN,
strlen('session'),
'session',
self::CHANNEL_KEEP_ALIVE,
$this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE],
$packet_size
);
if (!@$this->_send_binary_packet($packet)) {
return $this->_reconnect();
}
$this->channel_status[self::CHANNEL_KEEP_ALIVE] =
NET_SSH2_MSG_CHANNEL_OPEN;
$response =
@$this->_get_channel_packet(self::CHANNEL_KEEP_ALIVE);
if ($response !== false) {
$this->_close_channel(self::CHANNEL_KEEP_ALIVE);
return true;
}
return $this->_reconnect();
}
/**
* In situ reconnect method
*
* @return boolean
* @access private
*/
function _reconnect()
{
$this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST);
$this->retry_connect = true;
if (!$this->_connect()) {
return false;
}
foreach ($this->auth as $auth) {
$result = call_user_func_array(array(&$this,
'login'), $auth);
}
return $result;
}
/**
* Resets a connection for re-use
*
* @param int $reason
* @access private
*/
function _reset_connection($reason)
{
$this->_disconnect($reason);
$this->decrypt = $this->encrypt = false;
$this->decrypt_block_size = $this->encrypt_block_size = 8;
$this->hmac_check = $this->hmac_create = false;
$this->hmac_size = false;
$this->session_id = false;
$this->retry_connect = true;
$this->get_seq_no = $this->send_seq_no = 0;
}
/**
* Gets Binary Packets
*
* See '6. Binary Packet Protocol' of rfc4253 for more info.
*
* @see self::_send_binary_packet()
* @return string
* @access private
*/
function _get_binary_packet($skip_channel_filter = false)
{
if ($skip_channel_filter) {
$read = array($this->fsock);
$write = $except = null;
if (!$this->curTimeout) {
if ($this->keepAlive <= 0) {
@stream_select($read, $write, $except, null);
} else {
if (!@stream_select($read, $write, $except,
$this->keepAlive) && !count($read)) {
$this->_send_binary_packet(pack('CN',
NET_SSH2_MSG_IGNORE, 0));
return $this->_get_binary_packet(true);
}
}
} else {
if ($this->curTimeout < 0) {
$this->is_timeout = true;
return true;
}
$read = array($this->fsock);
$write = $except = null;
$start = microtime(true);
if ($this->keepAlive > 0 &&
$this->keepAlive < $this->curTimeout) {
if (!@stream_select($read, $write, $except,
$this->keepAlive) && !count($read)) {
$this->_send_binary_packet(pack('CN',
NET_SSH2_MSG_IGNORE, 0));
$elapsed = microtime(true) - $start;
$this->curTimeout-= $elapsed;
return $this->_get_binary_packet(true);
}
$elapsed = microtime(true) - $start;
$this->curTimeout-= $elapsed;
}
$sec = floor($this->curTimeout);
$usec = 1000000 * ($this->curTimeout - $sec);
// on windows this returns a "Warning: Invalid CRT
parameters detected" error
if (!@stream_select($read, $write, $except, $sec, $usec)
&& !count($read)) {
$this->is_timeout = true;
return true;
}
$elapsed = microtime(true) - $start;
$this->curTimeout-= $elapsed;
}
}
if (!is_resource($this->fsock) || feof($this->fsock)) {
$this->bitmap = 0;
user_error('Connection closed prematurely');
return false;
}
$start = microtime(true);
$raw = stream_get_contents($this->fsock,
$this->decrypt_block_size);
if (!strlen($raw)) {
return '';
}
if ($this->decrypt !== false) {
$raw = $this->decrypt->decrypt($raw);
}
if ($raw === false) {
user_error('Unable to decrypt content');
return false;
}
if (strlen($raw) < 5) {
return false;
}
extract(unpack('Npacket_length/Cpadding_length',
$this->_string_shift($raw, 5)));
$remaining_length = $packet_length + 4 -
$this->decrypt_block_size;
// quoting <http://tools.ietf.org/html/rfc4253#section-6.1>,
// "implementations SHOULD check that the packet length is
reasonable"
// PuTTY uses 0x9000 as the actual max packet size and so to shall
we
if ($remaining_length < -$this->decrypt_block_size ||
$remaining_length > 0x9000 || $remaining_length %
$this->decrypt_block_size != 0) {
if (!$this->bad_key_size_fix &&
$this->_bad_algorithm_candidate($this->decrypt->name) &&
!($this->bitmap & SSH2::MASK_LOGIN)) {
$this->bad_key_size_fix = true;
$this->_reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
return false;
}
user_error('Invalid size');
return false;
}
$buffer = '';
while ($remaining_length > 0) {
$temp = stream_get_contents($this->fsock,
$remaining_length);
if ($temp === false || feof($this->fsock)) {
$this->bitmap = 0;
user_error('Error reading from socket');
return false;
}
$buffer.= $temp;
$remaining_length-= strlen($temp);
}
$stop = microtime(true);
if (strlen($buffer)) {
$raw.= $this->decrypt !== false ?
$this->decrypt->decrypt($buffer) : $buffer;
}
$payload = $this->_string_shift($raw, $packet_length -
$padding_length - 1);
$padding = $this->_string_shift($raw, $padding_length); //
should leave $raw empty
if ($this->hmac_check !== false) {
$hmac = stream_get_contents($this->fsock,
$this->hmac_size);
if ($hmac === false || strlen($hmac) != $this->hmac_size) {
$this->bitmap = 0;
user_error('Error reading socket');
return false;
} elseif ($hmac !=
$this->hmac_check->hash(pack('NNCa*', $this->get_seq_no,
$packet_length, $padding_length, $payload . $padding))) {
user_error('Invalid HMAC');
return false;
}
}
//if ($this->decompress) {
// $payload = gzinflate(substr($payload, 2));
//}
$this->get_seq_no++;
if (defined('NET_SSH2_LOGGING')) {
$current = microtime(true);
$message_number =
isset($this->message_numbers[ord($payload[0])]) ?
$this->message_numbers[ord($payload[0])] : 'UNKNOWN (' .
ord($payload[0]) . ')';
$message_number = '<- ' . $message_number .
' (since last: ' . round($current -
$this->last_packet, 4) . ', network: ' . round($stop - $start,
4) . 's)';
$this->_append_log($message_number, $payload);
$this->last_packet = $current;
}
return $this->_filter($payload, $skip_channel_filter);
}
/**
* Filter Binary Packets
*
* Because some binary packets need to be ignored...
*
* @see self::_get_binary_packet()
* @return string
* @access private
*/
function _filter($payload, $skip_channel_filter)
{
switch (ord($payload[0])) {
case NET_SSH2_MSG_DISCONNECT:
$this->_string_shift($payload, 1);
if (strlen($payload) < 8) {
return false;
}
extract(unpack('Nreason_code/Nlength',
$this->_string_shift($payload, 8)));
$this->errors[] = 'SSH_MSG_DISCONNECT: ' .
$this->disconnect_reasons[$reason_code] . "\r\n" .
$this->_string_shift($payload, $length);
$this->bitmap = 0;
return false;
case NET_SSH2_MSG_IGNORE:
$payload =
$this->_get_binary_packet($skip_channel_filter);
break;
case NET_SSH2_MSG_DEBUG:
$this->_string_shift($payload, 2);
if (strlen($payload) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($payload, 4)));
$this->errors[] = 'SSH_MSG_DEBUG: ' .
$this->_string_shift($payload, $length);
$payload =
$this->_get_binary_packet($skip_channel_filter);
break;
case NET_SSH2_MSG_UNIMPLEMENTED:
return false;
case NET_SSH2_MSG_KEXINIT:
if ($this->session_id !== false) {
$this->send_kex_first = false;
if (!$this->_key_exchange($payload)) {
$this->bitmap = 0;
return false;
}
$payload =
$this->_get_binary_packet($skip_channel_filter);
}
}
// see http://tools.ietf.org/html/rfc4252#section-5.4; only called
when the encryption has been activated and when we haven't already
logged in
if (($this->bitmap & self::MASK_CONNECTED) &&
!$this->isAuthenticated() && ord($payload[0]) ==
NET_SSH2_MSG_USERAUTH_BANNER) {
$this->_string_shift($payload, 1);
if (strlen($payload) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($payload, 4)));
$this->banner_message = $this->_string_shift($payload,
$length);
$payload = $this->_get_binary_packet();
}
// only called when we've already logged in
if (($this->bitmap & self::MASK_CONNECTED) &&
$this->isAuthenticated()) {
if (is_bool($payload)) {
return $payload;
}
switch (ord($payload[0])) {
case NET_SSH2_MSG_CHANNEL_REQUEST:
if (strlen($payload) == 31) {
extract(unpack('cpacket_type/Nchannel/Nlength', $payload));
if (substr($payload, 9, $length) ==
'keepalive@openssh.com' &&
isset($this->server_channels[$channel])) {
if (ord(substr($payload, 9 + $length))) { //
want reply
$this->_send_binary_packet(pack('CN',
NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel]));
}
$payload =
$this->_get_binary_packet($skip_channel_filter);
}
}
break;
case NET_SSH2_MSG_CHANNEL_DATA:
case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
case NET_SSH2_MSG_CHANNEL_CLOSE:
case NET_SSH2_MSG_CHANNEL_EOF:
if (!$skip_channel_filter &&
!empty($this->server_channels)) {
$this->binary_packet_buffer = $payload;
$this->_get_channel_packet(true);
$payload = $this->_get_binary_packet();
}
break;
case NET_SSH2_MSG_GLOBAL_REQUEST: // see
http://tools.ietf.org/html/rfc4254#section-4
if (strlen($payload) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($payload, 4)));
$this->errors[] = 'SSH_MSG_GLOBAL_REQUEST:
' . $this->_string_shift($payload, $length);
if (!$this->_send_binary_packet(pack('C',
NET_SSH2_MSG_REQUEST_FAILURE))) {
return
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
$payload =
$this->_get_binary_packet($skip_channel_filter);
break;
case NET_SSH2_MSG_CHANNEL_OPEN: // see
http://tools.ietf.org/html/rfc4254#section-5.1
$this->_string_shift($payload, 1);
if (strlen($payload) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($payload, 4)));
$data = $this->_string_shift($payload, $length);
if (strlen($payload) < 4) {
return false;
}
extract(unpack('Nserver_channel',
$this->_string_shift($payload, 4)));
switch ($data) {
case 'auth-agent':
case 'auth-agent@openssh.com':
if (isset($this->agent)) {
$new_channel = self::CHANNEL_AGENT_FORWARD;
if (strlen($payload) < 8) {
return false;
}
extract(unpack('Nremote_window_size',
$this->_string_shift($payload, 4)));
extract(unpack('Nremote_maximum_packet_size',
$this->_string_shift($payload, 4)));
$this->packet_size_client_to_server[$new_channel] = $remote_window_size;
$this->window_size_server_to_client[$new_channel] =
$remote_maximum_packet_size;
$this->window_size_client_to_server[$new_channel] =
$this->window_size;
$packet_size = 0x4000;
$packet = pack(
'CN4',
NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION,
$server_channel,
$new_channel,
$packet_size,
$packet_size
);
$this->server_channels[$new_channel] =
$server_channel;
$this->channel_status[$new_channel] =
NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION;
if
(!$this->_send_binary_packet($packet)) {
return false;
}
}
break;
default:
$packet = pack(
'CN3a*Na*',
NET_SSH2_MSG_REQUEST_FAILURE,
$server_channel,
NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
0,
'',
0,
''
);
if (!$this->_send_binary_packet($packet)) {
return
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
}
$payload =
$this->_get_binary_packet($skip_channel_filter);
break;
case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
$this->_string_shift($payload, 1);
if (strlen($payload) < 8) {
return false;
}
extract(unpack('Nchannel',
$this->_string_shift($payload, 4)));
extract(unpack('Nwindow_size',
$this->_string_shift($payload, 4)));
$this->window_size_client_to_server[$channel]+=
$window_size;
$payload = ($this->bitmap &
self::MASK_WINDOW_ADJUST) ? true :
$this->_get_binary_packet($skip_channel_filter);
}
}
return $payload;
}
/**
* Enable Quiet Mode
*
* Suppress stderr from output
*
* @access public
*/
function enableQuietMode()
{
$this->quiet_mode = true;
}
/**
* Disable Quiet Mode
*
* Show stderr in output
*
* @access public
*/
function disableQuietMode()
{
$this->quiet_mode = false;
}
/**
* Returns whether Quiet Mode is enabled or not
*
* @see self::enableQuietMode()
* @see self::disableQuietMode()
* @access public
* @return bool
*/
function isQuietModeEnabled()
{
return $this->quiet_mode;
}
/**
* Enable request-pty when using exec()
*
* @access public
*/
function enablePTY()
{
$this->request_pty = true;
}
/**
* Disable request-pty when using exec()
*
* @access public
*/
function disablePTY()
{
if ($this->in_request_pty_exec) {
$this->_close_channel(self::CHANNEL_EXEC);
$this->in_request_pty_exec = false;
}
$this->request_pty = false;
}
/**
* Returns whether request-pty is enabled or not
*
* @see self::enablePTY()
* @see self::disablePTY()
* @access public
* @return bool
*/
function isPTYEnabled()
{
return $this->request_pty;
}
/**
* Gets channel data
*
* Returns the data as a string if it's available and false if
not.
*
* @param int $client_channel
* @param bool $skip_extended
* @return mixed|bool
* @access private
*/
function _get_channel_packet($client_channel, $skip_extended = false)
{
if (!empty($this->channel_buffers[$client_channel])) {
return array_shift($this->channel_buffers[$client_channel]);
}
while (true) {
if ($this->binary_packet_buffer !== false) {
$response = $this->binary_packet_buffer;
$this->binary_packet_buffer = false;
} else {
$response = $this->_get_binary_packet(true);
if ($response === true && $this->is_timeout) {
if ($client_channel == self::CHANNEL_EXEC &&
!$this->request_pty) {
$this->_close_channel($client_channel);
}
return true;
}
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
}
if ($client_channel == -1 && $response === true) {
return true;
}
if (!strlen($response)) {
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
if (strlen($response) < 4) {
return false;
}
if ($type == NET_SSH2_MSG_CHANNEL_OPEN) {
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
} else {
extract(unpack('Nchannel',
$this->_string_shift($response, 4)));
}
// will not be setup yet on incoming channel open request
if (isset($channel) &&
isset($this->channel_status[$channel]) &&
isset($this->window_size_server_to_client[$channel])) {
$this->window_size_server_to_client[$channel]-=
strlen($response);
// resize the window, if appropriate
if ($this->window_size_server_to_client[$channel] <
0) {
// PuTTY does something more analogous to the following:
//if ($this->window_size_server_to_client[$channel] <
0x3FFFFFFF) {
$packet = pack('CNN',
NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel],
$this->window_resize);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->window_size_server_to_client[$channel]+=
$this->window_resize;
}
switch ($type) {
case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
/*
if ($client_channel == self::CHANNEL_EXEC) {
$this->_send_channel_packet($client_channel,
chr(0));
}
*/
// currently, there's only one possible value
for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
if (strlen($response) < 8) {
return false;
}
extract(unpack('Ndata_type_code/Nlength',
$this->_string_shift($response, 8)));
$data = $this->_string_shift($response,
$length);
$this->stdErrorLog.= $data;
if ($skip_extended || $this->quiet_mode) {
continue 2;
}
if ($client_channel == $channel &&
$this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) {
return $data;
}
if (!isset($this->channel_buffers[$channel])) {
$this->channel_buffers[$channel] = array();
}
$this->channel_buffers[$channel][] = $data;
continue 2;
case NET_SSH2_MSG_CHANNEL_REQUEST:
if ($this->channel_status[$channel] ==
NET_SSH2_MSG_CHANNEL_CLOSE) {
continue 2;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$value = $this->_string_shift($response,
$length);
switch ($value) {
case 'exit-signal':
$this->_string_shift($response, 1);
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$this->errors[] =
'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' .
$this->_string_shift($response, $length);
$this->_string_shift($response, 1);
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
if ($length) {
$this->errors[count($this->errors)].= "\r\n" .
$this->_string_shift($response, $length);
}
$this->_send_binary_packet(pack('CN',
NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
$this->_send_binary_packet(pack('CN',
NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
$this->channel_status[$channel] =
NET_SSH2_MSG_CHANNEL_EOF;
continue 3;
case 'exit-status':
if (strlen($response) < 5) {
return false;
}
extract(unpack('Cfalse/Nexit_status',
$this->_string_shift($response, 5)));
$this->exit_status = $exit_status;
// "The client MAY ignore these
messages."
// --
http://tools.ietf.org/html/rfc4254#section-6.10
continue 3;
default:
// "Some systems may not implement
signals, in which case they SHOULD ignore this message."
// --
http://tools.ietf.org/html/rfc4254#section-6.9
continue 3;
}
}
switch ($this->channel_status[$channel]) {
case NET_SSH2_MSG_CHANNEL_OPEN:
switch ($type) {
case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nserver_channel',
$this->_string_shift($response, 4)));
$this->server_channels[$channel] =
$server_channel;
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nwindow_size',
$this->_string_shift($response, 4)));
if ($window_size < 0) {
$window_size&= 0x7FFFFFFF;
$window_size+= 0x80000000;
}
$this->window_size_client_to_server[$channel] = $window_size;
if (strlen($response) < 4) {
return false;
}
$temp =
unpack('Npacket_size_client_to_server',
$this->_string_shift($response, 4));
$this->packet_size_client_to_server[$channel] =
$temp['packet_size_client_to_server'];
$result = $client_channel == $channel ?
true : $this->_get_channel_packet($client_channel, $skip_extended);
$this->_on_channel_open();
return $result;
//case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
default:
user_error('Unable to open
channel');
return
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
break;
case NET_SSH2_MSG_IGNORE:
switch ($type) {
case NET_SSH2_MSG_CHANNEL_SUCCESS:
//$this->channel_status[$channel] =
NET_SSH2_MSG_CHANNEL_DATA;
continue 3;
case NET_SSH2_MSG_CHANNEL_FAILURE:
user_error('Error opening
channel');
return
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
break;
case NET_SSH2_MSG_CHANNEL_REQUEST:
switch ($type) {
case NET_SSH2_MSG_CHANNEL_SUCCESS:
return true;
case NET_SSH2_MSG_CHANNEL_FAILURE:
return false;
default:
user_error('Unable to fulfill channel
request');
return
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
case NET_SSH2_MSG_CHANNEL_CLOSE:
return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true :
$this->_get_channel_packet($client_channel, $skip_extended);
}
}
// ie. $this->channel_status[$channel] ==
NET_SSH2_MSG_CHANNEL_DATA
switch ($type) {
case NET_SSH2_MSG_CHANNEL_DATA:
//if ($this->channel_status[$channel] ==
NET_SSH2_MSG_IGNORE) {
// $this->channel_status[$channel] =
NET_SSH2_MSG_CHANNEL_DATA;
//}
/*
if ($channel == self::CHANNEL_EXEC) {
// SCP requires null packets, such as this, be
sent. further, in the case of the ssh.com SSH server
// this actually seems to make things twice as
fast. more to the point, the message right after
// SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE)
won't block for as long as it would have otherwise.
// in OpenSSH it slows things down but only by a
couple thousandths of a second.
$this->_send_channel_packet($channel, chr(0));
}
*/
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$data = $this->_string_shift($response, $length);
if ($channel == self::CHANNEL_AGENT_FORWARD) {
$agent_response =
$this->agent->_forward_data($data);
if (!is_bool($agent_response)) {
$this->_send_channel_packet($channel,
$agent_response);
}
break;
}
if ($client_channel == $channel) {
return $data;
}
if (!isset($this->channel_buffers[$channel])) {
$this->channel_buffers[$channel] = array();
}
$this->channel_buffers[$channel][] = $data;
break;
case NET_SSH2_MSG_CHANNEL_CLOSE:
$this->curTimeout = 5;
if ($this->bitmap & self::MASK_SHELL) {
$this->bitmap&= ~self::MASK_SHELL;
}
if ($this->channel_status[$channel] !=
NET_SSH2_MSG_CHANNEL_EOF) {
$this->_send_binary_packet(pack('CN',
NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
}
$this->channel_status[$channel] =
NET_SSH2_MSG_CHANNEL_CLOSE;
if ($client_channel == $channel) {
return true;
}
case NET_SSH2_MSG_CHANNEL_EOF:
break;
default:
user_error('Error reading channel data');
return
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
}
}
/**
* Sends Binary Packets
*
* See '6. Binary Packet Protocol' of rfc4253 for more info.
*
* @param string $data
* @param string $logged
* @see self::_get_binary_packet()
* @return bool
* @access private
*/
function _send_binary_packet($data, $logged = null)
{
if (!is_resource($this->fsock) || feof($this->fsock)) {
$this->bitmap = 0;
user_error('Connection closed prematurely');
return false;
}
//if ($this->compress) {
// // the -4 removes the checksum:
// // http://php.net/function.gzcompress#57710
// $data = substr(gzcompress($data), 0, -4);
//}
// 4 (packet length) + 1 (padding length) + 4 (minimal padding
amount) == 9
$packet_length = strlen($data) + 9;
// round up to the nearest $this->encrypt_block_size
$packet_length+= (($this->encrypt_block_size - 1) *
$packet_length) % $this->encrypt_block_size;
// subtracting strlen($data) is obvious - subtracting 5 is
necessary because of packet_length and padding_length
$padding_length = $packet_length - strlen($data) - 5;
$padding = Random::string($padding_length);
// we subtract 4 from packet_length because the packet_length field
isn't supposed to include itself
$packet = pack('NCa*', $packet_length - 4,
$padding_length, $data . $padding);
$hmac = $this->hmac_create !== false ?
$this->hmac_create->hash(pack('Na*', $this->send_seq_no,
$packet)) : '';
$this->send_seq_no++;
if ($this->encrypt !== false) {
$packet = $this->encrypt->encrypt($packet);
}
$packet.= $hmac;
$start = microtime(true);
$result = strlen($packet) == @fputs($this->fsock, $packet);
$stop = microtime(true);
if (defined('NET_SSH2_LOGGING')) {
$current = microtime(true);
$message_number =
isset($this->message_numbers[ord($data[0])]) ?
$this->message_numbers[ord($data[0])] : 'UNKNOWN (' .
ord($data[0]) . ')';
$message_number = '-> ' . $message_number .
' (since last: ' . round($current -
$this->last_packet, 4) . ', network: ' . round($stop - $start,
4) . 's)';
$this->_append_log($message_number, isset($logged) ? $logged
: $data);
$this->last_packet = $current;
}
return $result;
}
/**
* Logs data packets
*
* Makes sure that only the last 1MB worth of packets will be logged
*
* @param string $message_number
* @param string $message
* @access private
*/
function _append_log($message_number, $message)
{
// remove the byte identifying the message type from all but the
first two messages (ie. the identification strings)
if (strlen($message_number) > 2) {
$this->_string_shift($message);
}
switch (NET_SSH2_LOGGING) {
// useful for benchmarks
case self::LOG_SIMPLE:
$this->message_number_log[] = $message_number;
break;
// the most useful log for SSH2
case self::LOG_COMPLEX:
$this->message_number_log[] = $message_number;
$this->log_size+= strlen($message);
$this->message_log[] = $message;
while ($this->log_size > self::LOG_MAX_SIZE) {
$this->log_size-=
strlen(array_shift($this->message_log));
array_shift($this->message_number_log);
}
break;
// dump the output out realtime; packets may be interspersed
with non packets,
// passwords won't be filtered out and select other
packets may not be correctly
// identified
case self::LOG_REALTIME:
switch (PHP_SAPI) {
case 'cli':
$start = $stop = "\r\n";
break;
default:
$start = '<pre>';
$stop = '</pre>';
}
echo $start . $this->_format_log(array($message),
array($message_number)) . $stop;
@flush();
@ob_flush();
break;
// basically the same thing as self::LOG_REALTIME with the
caveat that self::LOG_REALTIME_FILE
// needs to be defined and that the resultant log file will be
capped out at self::LOG_MAX_SIZE.
// the earliest part of the log file is denoted by the first
<<< START >>> and is not going to necessarily
// at the beginning of the file
case self::LOG_REALTIME_FILE:
if (!isset($this->realtime_log_file)) {
// PHP doesn't seem to like using constants in
fopen()
$filename = self::LOG_REALTIME_FILENAME;
$fp = fopen($filename, 'w');
$this->realtime_log_file = $fp;
}
if (!is_resource($this->realtime_log_file)) {
break;
}
$entry = $this->_format_log(array($message),
array($message_number));
if ($this->realtime_log_wrap) {
$temp = "<<< START
>>>\r\n";
$entry.= $temp;
fseek($this->realtime_log_file,
ftell($this->realtime_log_file) - strlen($temp));
}
$this->realtime_log_size+= strlen($entry);
if ($this->realtime_log_size > self::LOG_MAX_SIZE) {
fseek($this->realtime_log_file, 0);
$this->realtime_log_size = strlen($entry);
$this->realtime_log_wrap = true;
}
fputs($this->realtime_log_file, $entry);
}
}
/**
* Sends channel data
*
* Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
*
* @param int $client_channel
* @param string $data
* @return bool
* @access private
*/
function _send_channel_packet($client_channel, $data)
{
while (strlen($data)) {
if (!$this->window_size_client_to_server[$client_channel]) {
$this->bitmap^= self::MASK_WINDOW_ADJUST;
// using an invalid channel will let the buffers be built
up for the valid channels
$this->_get_channel_packet(-1);
$this->bitmap^= self::MASK_WINDOW_ADJUST;
}
/* The maximum amount of data allowed is determined by the
maximum
packet size for the channel, and the current window size,
whichever
is smaller.
-- http://tools.ietf.org/html/rfc4254#section-5.2 */
$max_size = min(
$this->packet_size_client_to_server[$client_channel],
$this->window_size_client_to_server[$client_channel]
);
$temp = $this->_string_shift($data, $max_size);
$packet = pack(
'CN2a*',
NET_SSH2_MSG_CHANNEL_DATA,
$this->server_channels[$client_channel],
strlen($temp),
$temp
);
$this->window_size_client_to_server[$client_channel]-=
strlen($temp);
if (!$this->_send_binary_packet($packet)) {
return false;
}
}
return true;
}
/**
* Closes and flushes a channel
*
* \phpseclib\Net\SSH2 doesn't properly close most channels. For
exec() channels are normally closed by the server
* and for SFTP channels are presumably closed when the client
disconnects. This functions is intended
* for SCP more than anything.
*
* @param int $client_channel
* @param bool $want_reply
* @return bool
* @access private
*/
function _close_channel($client_channel, $want_reply = false)
{
// see http://tools.ietf.org/html/rfc4254#section-5.3
$this->_send_binary_packet(pack('CN',
NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
if (!$want_reply) {
$this->_send_binary_packet(pack('CN',
NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
}
$this->channel_status[$client_channel] =
NET_SSH2_MSG_CHANNEL_CLOSE;
$this->curTimeout = 5;
while (!is_bool($this->_get_channel_packet($client_channel))) {
}
if ($this->is_timeout) {
$this->disconnect();
}
if ($want_reply) {
$this->_send_binary_packet(pack('CN',
NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
}
if ($this->bitmap & self::MASK_SHELL) {
$this->bitmap&= ~self::MASK_SHELL;
}
}
/**
* Disconnect
*
* @param int $reason
* @return bool
* @access private
*/
function _disconnect($reason)
{
if ($this->bitmap & self::MASK_CONNECTED) {
$data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT,
$reason, 0, '', 0, '');
$this->_send_binary_packet($data);
}
$this->bitmap = 0;
if (is_resource($this->fsock) &&
get_resource_type($this->fsock) == 'stream') {
fclose($this->fsock);
}
return false;
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @return string
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
/**
* Define Array
*
* Takes any number of arrays whose indices are integers and whose
values are strings and defines a bunch of
* named constants from it, using the value as the name of the constant
and the index as the value of the constant.
* If any of the constants that would be defined already exists, none
of the constants will be defined.
*
* @access private
*/
function _define_array()
{
$args = func_get_args();
foreach ($args as $arg) {
foreach ($arg as $key => $value) {
if (!defined($value)) {
define($value, $key);
} else {
break 2;
}
}
}
}
/**
* Returns a log of the packets that have been sent and received.
*
* Returns a string if NET_SSH2_LOGGING == self::LOG_COMPLEX, an array
if NET_SSH2_LOGGING == self::LOG_SIMPLE and false if
!defined('NET_SSH2_LOGGING')
*
* @access public
* @return array|false|string
*/
function getLog()
{
if (!defined('NET_SSH2_LOGGING')) {
return false;
}
switch (NET_SSH2_LOGGING) {
case self::LOG_SIMPLE:
return $this->message_number_log;
case self::LOG_COMPLEX:
$log = $this->_format_log($this->message_log,
$this->message_number_log);
return PHP_SAPI == 'cli' ? $log :
'<pre>' . $log . '</pre>';
default:
return false;
}
}
/**
* Formats a log for printing
*
* @param array $message_log
* @param array $message_number_log
* @access private
* @return string
*/
function _format_log($message_log, $message_number_log)
{
$output = '';
for ($i = 0; $i < count($message_log); $i++) {
$output.= $message_number_log[$i] . "\r\n";
$current_log = $message_log[$i];
$j = 0;
do {
if (strlen($current_log)) {
$output.= str_pad(dechex($j), 7, '0',
STR_PAD_LEFT) . '0 ';
}
$fragment = $this->_string_shift($current_log,
$this->log_short_width);
$hex = substr(preg_replace_callback('#.#s',
array($this, '_format_log_helper'), $fragment),
strlen($this->log_boundary));
// replace non ASCII printable characters with dots
//
http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
// also replace < with a . since < messes up the
output on web browsers
$raw = preg_replace('#[^\x20-\x7E]|<#',
'.', $fragment);
$output.= str_pad($hex, $this->log_long_width -
$this->log_short_width, ' ') . $raw . "\r\n";
$j++;
} while (strlen($current_log));
$output.= "\r\n";
}
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);
}
/**
* Helper function for agent->_on_channel_open()
*
* Used when channels are created to inform agent
* of said channel opening. Must be called after
* channel open confirmation received
*
* @access private
*/
function _on_channel_open()
{
if (isset($this->agent)) {
$this->agent->_on_channel_open($this);
}
}
/**
* Returns the first value of the intersection of two arrays or false
if
* the intersection is empty. The order is defined by the first
parameter.
*
* @param array $array1
* @param array $array2
* @return mixed False if intersection is empty, else intersected
value.
* @access private
*/
function _array_intersect_first($array1, $array2)
{
foreach ($array1 as $value) {
if (in_array($value, $array2)) {
return $value;
}
}
return false;
}
/**
* Returns all errors
*
* @return string[]
* @access public
*/
function getErrors()
{
return $this->errors;
}
/**
* Returns the last error
*
* @return string
* @access public
*/
function getLastError()
{
$count = count($this->errors);
if ($count > 0) {
return $this->errors[$count - 1];
}
}
/**
* Return the server identification.
*
* @return string
* @access public
*/
function getServerIdentification()
{
$this->_connect();
return $this->server_identifier;
}
/**
* Return a list of the key exchange algorithms the server supports.
*
* @return array
* @access public
*/
function getKexAlgorithms()
{
$this->_connect();
return $this->kex_algorithms;
}
/**
* Return a list of the host key (public key) algorithms the server
supports.
*
* @return array
* @access public
*/
function getServerHostKeyAlgorithms()
{
$this->_connect();
return $this->server_host_key_algorithms;
}
/**
* Return a list of the (symmetric key) encryption algorithms the
server supports, when receiving stuff from the client.
*
* @return array
* @access public
*/
function getEncryptionAlgorithmsClient2Server()
{
$this->_connect();
return $this->encryption_algorithms_client_to_server;
}
/**
* Return a list of the (symmetric key) encryption algorithms the
server supports, when sending stuff to the client.
*
* @return array
* @access public
*/
function getEncryptionAlgorithmsServer2Client()
{
$this->_connect();
return $this->encryption_algorithms_server_to_client;
}
/**
* Return a list of the MAC algorithms the server supports, when
receiving stuff from the client.
*
* @return array
* @access public
*/
function getMACAlgorithmsClient2Server()
{
$this->_connect();
return $this->mac_algorithms_client_to_server;
}
/**
* Return a list of the MAC algorithms the server supports, when
sending stuff to the client.
*
* @return array
* @access public
*/
function getMACAlgorithmsServer2Client()
{
$this->_connect();
return $this->mac_algorithms_server_to_client;
}
/**
* Return a list of the compression algorithms the server supports,
when receiving stuff from the client.
*
* @return array
* @access public
*/
function getCompressionAlgorithmsClient2Server()
{
$this->_connect();
return $this->compression_algorithms_client_to_server;
}
/**
* Return a list of the compression algorithms the server supports,
when sending stuff to the client.
*
* @return array
* @access public
*/
function getCompressionAlgorithmsServer2Client()
{
$this->_connect();
return $this->compression_algorithms_server_to_client;
}
/**
* Return a list of the languages the server supports, when sending
stuff to the client.
*
* @return array
* @access public
*/
function getLanguagesServer2Client()
{
$this->_connect();
return $this->languages_server_to_client;
}
/**
* Return a list of the languages the server supports, when receiving
stuff from the client.
*
* @return array
* @access public
*/
function getLanguagesClient2Server()
{
$this->_connect();
return $this->languages_client_to_server;
}
/**
* Returns a list of algorithms the server supports
*
* @return array
* @access public
*/
function getServerAlgorithms()
{
$this->_connect();
return array(
'kex' => $this->kex_algorithms,
'hostkey' => $this->server_host_key_algorithms,
'client_to_server' => array(
'crypt' =>
$this->encryption_algorithms_client_to_server,
'mac' =>
$this->mac_algorithms_client_to_server,
'comp' =>
$this->compression_algorithms_client_to_server,
'lang' => $this->languages_client_to_server
),
'server_to_client' => array(
'crypt' =>
$this->encryption_algorithms_server_to_client,
'mac' =>
$this->mac_algorithms_server_to_client,
'comp' =>
$this->compression_algorithms_server_to_client,
'lang' => $this->languages_server_to_client
)
);
}
/**
* Returns a list of KEX algorithms that phpseclib supports
*
* @return array
* @access public
*/
function getSupportedKEXAlgorithms()
{
$kex_algorithms = array(
// Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using
// Curve25519. See doc/curve25519-sha256@libssh.org.txt in the
// libssh repository for more information.
'curve25519-sha256@libssh.org',
'diffie-hellman-group-exchange-sha256',// RFC 4419
'diffie-hellman-group-exchange-sha1', // RFC 4419
// Diffie-Hellman Key Agreement (DH) using integer modulo prime
// groups.
'diffie-hellman-group14-sha1', // REQUIRED
'diffie-hellman-group1-sha1', // REQUIRED
);
if
(!function_exists('sodium_crypto_box_publickey_from_secretkey'))
{
$kex_algorithms = array_diff(
$kex_algorithms,
array('curve25519-sha256@libssh.org')
);
}
return $kex_algorithms;
}
/**
* Returns a list of host key algorithms that phpseclib supports
*
* @return array
* @access public
*/
function getSupportedHostKeyAlgorithms()
{
return array(
'rsa-sha2-256', // RFC 8332
'rsa-sha2-512', // RFC 8332
'ssh-rsa', // RECOMMENDED sign Raw RSA Key
'ssh-dss' // REQUIRED sign Raw DSS Key
);
}
/**
* Returns a list of symmetric key algorithms that phpseclib supports
*
* @return array
* @access public
*/
function getSupportedEncryptionAlgorithms()
{
$algos = array(
// from <http://tools.ietf.org/html/rfc4345#section-4>:
'arcfour256',
'arcfour128',
//'arcfour', // OPTIONAL the ARCFOUR
stream cipher with a 128-bit key
// CTR modes from
<http://tools.ietf.org/html/rfc4344#section-4>:
'aes128-ctr', // RECOMMENDED AES (Rijndael)
in SDCTR mode, with 128-bit key
'aes192-ctr', // RECOMMENDED AES with
192-bit key
'aes256-ctr', // RECOMMENDED AES with
256-bit key
'twofish128-ctr', // OPTIONAL Twofish in
SDCTR mode, with 128-bit key
'twofish192-ctr', // OPTIONAL Twofish with
192-bit key
'twofish256-ctr', // OPTIONAL Twofish with
256-bit key
'aes128-cbc', // RECOMMENDED AES with a
128-bit key
'aes192-cbc', // OPTIONAL AES with a
192-bit key
'aes256-cbc', // OPTIONAL AES in CBC
mode, with a 256-bit key
'twofish128-cbc', // OPTIONAL Twofish with a
128-bit key
'twofish192-cbc', // OPTIONAL Twofish with a
192-bit key
'twofish256-cbc',
'twofish-cbc', // OPTIONAL alias for
"twofish256-cbc"
// (this is being retained
for historical reasons)
'blowfish-ctr', // OPTIONAL Blowfish in
SDCTR mode
'blowfish-cbc', // OPTIONAL Blowfish in
CBC mode
'3des-ctr', // RECOMMENDED Three-key 3DES
in SDCTR mode
'3des-cbc', // REQUIRED three-key 3DES
in CBC mode
//'none' // OPTIONAL no
encryption; NOT RECOMMENDED
);
if ($this->crypto_engine) {
$engines = array($this->crypto_engine);
} else {
$engines = array(
Base::ENGINE_OPENSSL,
Base::ENGINE_MCRYPT,
Base::ENGINE_INTERNAL
);
}
$ciphers = array();
foreach ($engines as $engine) {
foreach ($algos as $algo) {
$obj =
$this->_encryption_algorithm_to_crypt_instance($algo);
if ($obj instanceof Rijndael) {
$obj->setKeyLength(preg_replace('#[^\d]#',
'', $algo));
}
switch ($algo) {
case 'arcfour128':
case 'arcfour256':
if ($engine != Base::ENGINE_INTERNAL) {
continue 2;
}
}
if ($obj->isValidEngine($engine)) {
$algos = array_diff($algos, array($algo));
$ciphers[] = $algo;
}
}
}
return $ciphers;
}
/**
* Returns a list of MAC algorithms that phpseclib supports
*
* @return array
* @access public
*/
function getSupportedMACAlgorithms()
{
return array(
// from <http://www.ietf.org/rfc/rfc6668.txt>:
'hmac-sha2-256',// RECOMMENDED HMAC-SHA256
(digest length = key length = 32)
'hmac-sha1-96', // RECOMMENDED first 96 bits of
HMAC-SHA1 (digest length = 12, key length = 20)
'hmac-sha1', // REQUIRED HMAC-SHA1 (digest
length = key length = 20)
'hmac-md5-96', // OPTIONAL first 96 bits of
HMAC-MD5 (digest length = 12, key length = 16)
'hmac-md5', // OPTIONAL HMAC-MD5 (digest
length = key length = 16)
//'none' // OPTIONAL no MAC; NOT
RECOMMENDED
);
}
/**
* Returns a list of compression algorithms that phpseclib supports
*
* @return array
* @access public
*/
function getSupportedCompressionAlgorithms()
{
return array(
'none' // REQUIRED no compression
//'zlib' // OPTIONAL ZLIB (LZ77) compression
);
}
/**
* Return list of negotiated algorithms
*
* Uses the same format as https://www.php.net/ssh2-methods-negotiated
*
* @return array
* @access public
*/
function getAlgorithmsNegotiated()
{
$this->_connect();
return array(
'kex' => $this->kex_algorithm,
'hostkey' => $this->signature_format,
'client_to_server' => array(
'crypt' => $this->encrypt->name,
'mac' => $this->hmac_create->name,
'comp' => 'none',
),
'server_to_client' => array(
'crypt' => $this->decrypt->name,
'mac' => $this->hmac_check->name,
'comp' => 'none',
)
);
}
/**
* Accepts an associative array with up to four parameters as described
at
* <https://www.php.net/manual/en/function.ssh2-connect.php>
*
* @param array $methods
* @access public
*/
function setPreferredAlgorithms($methods)
{
$preferred = $methods;
if (isset($preferred['kex'])) {
$preferred['kex'] = array_intersect(
$preferred['kex'],
$this->getSupportedKEXAlgorithms()
);
}
if (isset($preferred['hostkey'])) {
$preferred['hostkey'] = array_intersect(
$preferred['hostkey'],
$this->getSupportedHostKeyAlgorithms()
);
}
$keys = array('client_to_server',
'server_to_client');
foreach ($keys as $key) {
if (isset($preferred[$key])) {
$a = &$preferred[$key];
if (isset($a['crypt'])) {
$a['crypt'] = array_intersect(
$a['crypt'],
$this->getSupportedEncryptionAlgorithms()
);
}
if (isset($a['comp'])) {
$a['comp'] = array_intersect(
$a['comp'],
$this->getSupportedCompressionAlgorithms()
);
}
if (isset($a['mac'])) {
$a['mac'] = array_intersect(
$a['mac'],
$this->getSupportedMACAlgorithms()
);
}
}
}
$keys = array(
'kex',
'hostkey',
'client_to_server/crypt',
'client_to_server/comp',
'client_to_server/mac',
'server_to_client/crypt',
'server_to_client/comp',
'server_to_client/mac',
);
foreach ($keys as $key) {
$p = $preferred;
$m = $methods;
$subkeys = explode('/', $key);
foreach ($subkeys as $subkey) {
if (!isset($p[$subkey])) {
continue 2;
}
$p = $p[$subkey];
$m = $m[$subkey];
}
if (count($p) != count($m)) {
$diff = array_diff($m, $p);
$msg = count($diff) == 1 ?
' is not a supported algorithm' :
' are not supported algorithms';
user_error(implode(', ', $diff) . $msg);
return false;
}
}
$this->preferred = $preferred;
}
/**
* Returns the banner message.
*
* Quoting from the RFC, "in some jurisdictions, sending a warning
message before
* authentication may be relevant for getting legal protection."
*
* @return string
* @access public
*/
function getBannerMessage()
{
return $this->banner_message;
}
/**
* Returns the server public host key.
*
* Caching this the first time you connect to a server and checking the
result on subsequent connections
* is recommended. Returns false if the server signature is not signed
correctly with the public host key.
*
* @return mixed
* @access public
*/
function getServerPublicHostKey()
{
if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
if (!$this->_connect()) {
return false;
}
}
$signature = $this->signature;
$server_public_host_key = $this->server_public_host_key;
if (strlen($server_public_host_key) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($server_public_host_key, 4)));
$this->_string_shift($server_public_host_key, $length);
if ($this->signature_validated) {
return $this->bitmap ?
$this->signature_format . ' ' .
base64_encode($this->server_public_host_key) :
false;
}
$this->signature_validated = true;
switch ($this->signature_format) {
case 'ssh-dss':
$zero = new BigInteger();
if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($server_public_host_key, 4));
$p = new
BigInteger($this->_string_shift($server_public_host_key,
$temp['length']), -256);
if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($server_public_host_key, 4));
$q = new
BigInteger($this->_string_shift($server_public_host_key,
$temp['length']), -256);
if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($server_public_host_key, 4));
$g = new
BigInteger($this->_string_shift($server_public_host_key,
$temp['length']), -256);
if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($server_public_host_key, 4));
$y = new
BigInteger($this->_string_shift($server_public_host_key,
$temp['length']), -256);
/* The value for 'dss_signature_blob' is encoded
as a string containing
r, followed by s (which are 160-bit integers, without
lengths or
padding, unsigned, and in network byte order). */
$temp = unpack('Nlength',
$this->_string_shift($signature, 4));
if ($temp['length'] != 40) {
user_error('Invalid signature');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$r = new BigInteger($this->_string_shift($signature,
20), 256);
$s = new BigInteger($this->_string_shift($signature,
20), 256);
switch (true) {
case $r->equals($zero):
case $r->compare($q) >= 0:
case $s->equals($zero):
case $s->compare($q) >= 0:
user_error('Invalid signature');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$w = $s->modInverse($q);
$u1 = $w->multiply(new
BigInteger(sha1($this->exchange_hash), 16));
list(, $u1) = $u1->divide($q);
$u2 = $w->multiply($r);
list(, $u2) = $u2->divide($q);
$g = $g->modPow($u1, $p);
$y = $y->modPow($u2, $p);
$v = $g->multiply($y);
list(, $v) = $v->divide($p);
list(, $v) = $v->divide($q);
if (!$v->equals($r)) {
user_error('Bad server signature');
return
$this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
}
break;
case 'ssh-rsa':
case 'rsa-sha2-256':
case 'rsa-sha2-512':
if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($server_public_host_key, 4));
$e = new
BigInteger($this->_string_shift($server_public_host_key,
$temp['length']), -256);
if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($server_public_host_key, 4));
$rawN = $this->_string_shift($server_public_host_key,
$temp['length']);
$n = new BigInteger($rawN, -256);
$nLength = strlen(ltrim($rawN, "\0"));
/*
if (strlen($signature) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($signature, 4));
$signature = $this->_string_shift($signature,
$temp['length']);
$rsa = new RSA();
switch ($this->signature_format) {
case 'rsa-sha2-512':
$hash = 'sha512';
break;
case 'rsa-sha2-256':
$hash = 'sha256';
break;
//case 'ssh-rsa':
default:
$hash = 'sha1';
}
$rsa->setHash($hash);
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
$rsa->loadKey(array('e' => $e,
'n' => $n), RSA::PUBLIC_FORMAT_RAW);
if (!$rsa->verify($this->exchange_hash, $signature))
{
user_error('Bad server signature');
return
$this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
}
*/
if (strlen($signature) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($signature, 4));
$s = new BigInteger($this->_string_shift($signature,
$temp['length']), 256);
// validate an RSA signature per "8.2
RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1
EMSA-PSS" in the
// following URL:
// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
// also, see SSHRSA.c (rsa2_verifysig) in PuTTy's
source.
if ($s->compare(new BigInteger()) < 0 ||
$s->compare($n->subtract(new BigInteger(1))) > 0) {
user_error('Invalid signature');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$s = $s->modPow($e, $n);
$s = $s->toBytes();
switch ($this->signature_format) {
case 'rsa-sha2-512':
$hash = 'sha512';
break;
case 'rsa-sha2-256':
$hash = 'sha256';
break;
//case 'ssh-rsa':
default:
$hash = 'sha1';
}
$hashObj = new Hash($hash);
switch ($this->signature_format) {
case 'rsa-sha2-512':
$h = pack('N5a*', 0x00305130, 0x0D060960,
0x86480165, 0x03040203, 0x05000440,
$hashObj->hash($this->exchange_hash));
break;
case 'rsa-sha2-256':
$h = pack('N5a*', 0x00303130, 0x0D060960,
0x86480165, 0x03040201, 0x05000420,
$hashObj->hash($this->exchange_hash));
break;
//case 'ssh-rsa':
default:
$hash = 'sha1';
$h = pack('N4a*', 0x00302130, 0x0906052B,
0x0E03021A, 0x05000414, $hashObj->hash($this->exchange_hash));
}
$h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 2 -
strlen($h)) . $h;
if ($s != $h) {
user_error('Bad server signature');
return
$this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
}
break;
default:
user_error('Unsupported signature format');
return
$this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
}
return $this->signature_format . ' ' .
base64_encode($this->server_public_host_key);
}
/**
* Returns the exit status of an SSH command or false.
*
* @return false|int
* @access public
*/
function getExitStatus()
{
if (is_null($this->exit_status)) {
return false;
}
return $this->exit_status;
}
/**
* Returns the number of columns for the terminal window size.
*
* @return int
* @access public
*/
function getWindowColumns()
{
return $this->windowColumns;
}
/**
* Returns the number of rows for the terminal window size.
*
* @return int
* @access public
*/
function getWindowRows()
{
return $this->windowRows;
}
/**
* Sets the number of columns for the terminal window size.
*
* @param int $value
* @access public
*/
function setWindowColumns($value)
{
$this->windowColumns = $value;
}
/**
* Sets the number of rows for the terminal window size.
*
* @param int $value
* @access public
*/
function setWindowRows($value)
{
$this->windowRows = $value;
}
/**
* Sets the number of columns and rows for the terminal window size.
*
* @param int $columns
* @param int $rows
* @access public
*/
function setWindowSize($columns = 80, $rows = 24)
{
$this->windowColumns = $columns;
$this->windowRows = $rows;
}
/**
* Update packet types in log history
*
* @param string $old
* @param string $new
* @access private
*/
function _updateLogHistory($old, $new)
{
if (defined('NET_SSH2_LOGGING') &&
NET_SSH2_LOGGING == self::LOG_COMPLEX) {
$this->message_number_log[count($this->message_number_log) - 1] =
str_replace(
$old,
$new,
$this->message_number_log[count($this->message_number_log) - 1]
);
}
}
/**
* Return the list of authentication methods that may productively
continue authentication.
*
* @see https://tools.ietf.org/html/rfc4252#section-5.1
* @return array|null
*/
public function getAuthMethodsToContinue()
{
return $this->auth_methods_to_continue;
}
}
vendor/phpseclib/phpseclib/phpseclib/openssl.cnf000064400000000150151156520660016112
0ustar00# minimalist openssl.cnf file for use with phpseclib
HOME = .
RANDFILE = $ENV::HOME/.rnd
[ v3_ca ]
vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php000064400000014136151156520660021231
0ustar00<?php
/**
* Pure-PHP ssh-agent client.
*
* PHP version 5
*
* @category System
* @package SSH\Agent
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2009 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
*/
namespace phpseclib\System\SSH\Agent;
use phpseclib\System\SSH\Agent;
/**
* Pure-PHP ssh-agent client identity object
*
* Instantiation should only be performed by \phpseclib\System\SSH\Agent
class.
* This could be thought of as implementing an interface that
phpseclib\Crypt\RSA
* implements. ie. maybe a Net_SSH_Auth_PublicKey interface or something.
* The methods in this interface would be getPublicKey and sign since those
are the
* methods phpseclib looks for to perform public key authentication.
*
* @package SSH\Agent
* @author Jim Wigginton <terrafrost@php.net>
* @access internal
*/
class Identity
{
/**@+
* Signature Flags
*
* See
https://tools.ietf.org/html/draft-miller-ssh-agent-00#section-5.3
*
* @access private
*/
const SSH_AGENT_RSA2_256 = 2;
const SSH_AGENT_RSA2_512 = 4;
/**#@-*/
/**
* Key Object
*
* @var \phpseclib\Crypt\RSA
* @access private
* @see self::getPublicKey()
*/
var $key;
/**
* Key Blob
*
* @var string
* @access private
* @see self::sign()
*/
var $key_blob;
/**
* Socket Resource
*
* @var resource
* @access private
* @see self::sign()
*/
var $fsock;
/**
* Signature flags
*
* @var int
* @access private
* @see self::sign()
* @see self::setHash()
*/
var $flags = 0;
/**
* Default Constructor.
*
* @param resource $fsock
* @return \phpseclib\System\SSH\Agent\Identity
* @access private
*/
function __construct($fsock)
{
$this->fsock = $fsock;
}
/**
* Set Public Key
*
* Called by \phpseclib\System\SSH\Agent::requestIdentities()
*
* @param \phpseclib\Crypt\RSA $key
* @access private
*/
function setPublicKey($key)
{
$this->key = $key;
$this->key->setPublicKey();
}
/**
* Set Public Key
*
* Called by \phpseclib\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 int $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
\phpseclib\Crypt\RSA::SIGNATURE_PKCS1
*
* @param int $mode
* @access public
*/
function setSignatureMode($mode)
{
}
/**
* Set Hash
*
* ssh-agent doesn't support using hashes for RSA other than SHA1
*
* @param string $hash
* @access public
*/
function setHash($hash)
{
$this->flags = 0;
switch ($hash) {
case 'sha1':
break;
case 'sha256':
$this->flags = self::SSH_AGENT_RSA2_256;
break;
case 'sha512':
$this->flags = self::SSH_AGENT_RSA2_512;
break;
default:
user_error('The only supported hashes for RSA are
sha1, sha256 and sha512');
}
}
/**
* 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',
Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob),
$this->key_blob, strlen($message), $message, $this->flags);
$packet = pack('Na*', strlen($packet), $packet);
if (strlen($packet) != fputs($this->fsock, $packet)) {
user_error('Connection closed during signing');
return false;
}
$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed during signing');
return false;
}
$length = current(unpack('N', $temp));
$type = ord(fread($this->fsock, 1));
if ($type != Agent::SSH_AGENT_SIGN_RESPONSE) {
user_error('Unable to retrieve signature');
return false;
}
$signature_blob = fread($this->fsock, $length - 1);
if (strlen($signature_blob) != $length - 1) {
user_error('Connection closed during signing');
return false;
}
$length = current(unpack('N',
$this->_string_shift($signature_blob, 4)));
if ($length != strlen($signature_blob)) {
user_error('Malformed signature blob');
}
$length = current(unpack('N',
$this->_string_shift($signature_blob, 4)));
if ($length > strlen($signature_blob) + 4) {
user_error('Malformed signature blob');
}
$type = $this->_string_shift($signature_blob, $length);
$this->_string_shift($signature_blob, 4);
return $signature_blob;
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @return string
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
}
vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php000064400000023774151156520660017450
0ustar00<?php
/**
* Pure-PHP ssh-agent client.
*
* PHP version 5
*
* Here are some examples of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $agent = new \phpseclib\System\SSH\Agent();
*
* $ssh = new \phpseclib\Net\SSH2('www.domain.tld');
* if (!$ssh->login('username', $agent)) {
* exit('Login Failed');
* }
*
* echo $ssh->exec('pwd');
* echo $ssh->exec('ls -la');
* ?>
* </code>
*
* @category System
* @package SSH\Agent
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2014 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
*/
namespace phpseclib\System\SSH;
use phpseclib\Crypt\RSA;
use phpseclib\System\SSH\Agent\Identity;
/**
* Pure-PHP ssh-agent client identity factory
*
* requestIdentities() method pumps out
\phpseclib\System\SSH\Agent\Identity objects
*
* @package SSH\Agent
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class Agent
{
/**#@+
* Message numbers
*
* @access private
*/
// to request SSH1 keys you have to use
SSH_AGENTC_REQUEST_RSA_IDENTITIES (1)
const SSH_AGENTC_REQUEST_IDENTITIES = 11;
// this is the SSH2 response; the SSH1 response is
SSH_AGENT_RSA_IDENTITIES_ANSWER (2).
const SSH_AGENT_IDENTITIES_ANSWER = 12;
// the SSH1 request is SSH_AGENTC_RSA_CHALLENGE (3)
const SSH_AGENTC_SIGN_REQUEST = 13;
// the SSH1 response is SSH_AGENT_RSA_RESPONSE (4)
const SSH_AGENT_SIGN_RESPONSE = 14;
/**#@-*/
/**@+
* Agent forwarding status
*
* @access private
*/
// no forwarding requested and not active
const FORWARD_NONE = 0;
// request agent forwarding when opportune
const FORWARD_REQUEST = 1;
// forwarding has been request and is active
const FORWARD_ACTIVE = 2;
/**#@-*/
/**
* Unused
*/
const SSH_AGENT_FAILURE = 5;
/**
* Socket Resource
*
* @var resource
* @access private
*/
var $fsock;
/**
* Agent forwarding status
*
* @access private
*/
var $forward_status = self::FORWARD_NONE;
/**
* Buffer for accumulating forwarded authentication
* agent data arriving on SSH data channel destined
* for agent unix socket
*
* @access private
*/
var $socket_buffer = '';
/**
* Tracking the number of bytes we are expecting
* to arrive for the agent socket on the SSH data
* channel
*/
var $expected_bytes = 0;
/**
* Default Constructor
*
* @return \phpseclib\System\SSH\Agent
* @access public
*/
function __construct($address = null)
{
if (!$address) {
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
\phpseclib\System\SSH\Agent\Identity objects
*
* @return array
* @access public
*/
function requestIdentities()
{
if (!$this->fsock) {
return array();
}
$packet = pack('NC', 1,
self::SSH_AGENTC_REQUEST_IDENTITIES);
if (strlen($packet) != fputs($this->fsock, $packet)) {
user_error('Connection closed while requesting
identities');
return array();
}
$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed while requesting
identities');
return array();
}
$length = current(unpack('N', $temp));
$type = ord(fread($this->fsock, 1));
if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) {
user_error('Unable to request identities');
return array();
}
$identities = array();
$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed while requesting
identities');
return array();
}
$keyCount = current(unpack('N', $temp));
for ($i = 0; $i < $keyCount; $i++) {
$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed while requesting
identities');
return array();
}
$length = current(unpack('N', $temp));
$key_blob = fread($this->fsock, $length);
if (strlen($key_blob) != $length) {
user_error('Connection closed while requesting
identities');
return array();
}
$key_str = 'ssh-rsa ' . base64_encode($key_blob);
$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed while requesting
identities');
return array();
}
$length = current(unpack('N', $temp));
if ($length) {
$temp = fread($this->fsock, $length);
if (strlen($temp) != $length) {
user_error('Connection closed while requesting
identities');
return array();
}
$key_str.= ' ' . $temp;
}
$length = current(unpack('N', substr($key_blob, 0,
4)));
$key_type = substr($key_blob, 4, $length);
switch ($key_type) {
case 'ssh-rsa':
$key = new RSA();
$key->loadKey($key_str);
break;
case 'ssh-dss':
// not currently supported
break;
}
// resources are passed by reference by default
if (isset($key)) {
$identity = new Identity($this->fsock);
$identity->setPublicKey($key);
$identity->setPublicKeyBlob($key_blob);
$identities[] = $identity;
unset($key);
}
}
return $identities;
}
/**
* Signal that agent forwarding should
* be requested when a channel is opened
*
* @param Net_SSH2 $ssh
* @return bool
* @access public
*/
function startSSHForwarding($ssh)
{
if ($this->forward_status == self::FORWARD_NONE) {
$this->forward_status = self::FORWARD_REQUEST;
}
}
/**
* Request agent forwarding of remote server
*
* @param Net_SSH2 $ssh
* @return bool
* @access private
*/
function _request_forwarding($ssh)
{
$request_channel = $ssh->_get_open_channel();
if ($request_channel === false) {
return false;
}
$packet = pack(
'CNNa*C',
NET_SSH2_MSG_CHANNEL_REQUEST,
$ssh->server_channels[$request_channel],
strlen('auth-agent-req@openssh.com'),
'auth-agent-req@openssh.com',
1
);
$ssh->channel_status[$request_channel] =
NET_SSH2_MSG_CHANNEL_REQUEST;
if (!$ssh->_send_binary_packet($packet)) {
return false;
}
$response = $ssh->_get_channel_packet($request_channel);
if ($response === false) {
return false;
}
$ssh->channel_status[$request_channel] =
NET_SSH2_MSG_CHANNEL_OPEN;
$this->forward_status = self::FORWARD_ACTIVE;
return true;
}
/**
* On successful channel open
*
* This method is called upon successful channel
* open to give the SSH Agent an opportunity
* to take further action. i.e. request agent forwarding
*
* @param Net_SSH2 $ssh
* @access private
*/
function _on_channel_open($ssh)
{
if ($this->forward_status == self::FORWARD_REQUEST) {
$this->_request_forwarding($ssh);
}
}
/**
* Forward data to SSH Agent and return data reply
*
* @param string $data
* @return data from SSH Agent
* @access private
*/
function _forward_data($data)
{
if ($this->expected_bytes > 0) {
$this->socket_buffer.= $data;
$this->expected_bytes -= strlen($data);
} else {
$agent_data_bytes = current(unpack('N', $data));
$current_data_bytes = strlen($data);
$this->socket_buffer = $data;
if ($current_data_bytes != $agent_data_bytes + 4) {
$this->expected_bytes = ($agent_data_bytes + 4) -
$current_data_bytes;
return false;
}
}
if (strlen($this->socket_buffer) != fwrite($this->fsock,
$this->socket_buffer)) {
user_error('Connection closed attempting to forward data
to SSH agent');
return false;
}
$this->socket_buffer = '';
$this->expected_bytes = 0;
$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed while reading data
response');
return false;
}
$agent_reply_bytes = current(unpack('N', $temp));
$agent_reply_data = fread($this->fsock, $agent_reply_bytes);
if (strlen($agent_reply_data) != $agent_reply_bytes) {
user_error('Connection closed while reading data
response');
return false;
}
$agent_reply_data = current(unpack('a*',
$agent_reply_data));
return pack('Na*', $agent_reply_bytes,
$agent_reply_data);
}
}
vendor/phpseclib/phpseclib/README.md000064400000006035151156520660013255
0ustar00# phpseclib - PHP Secure Communications Library
[](https://travis-ci.com/phpseclib/phpseclib)
## Supporting phpseclib
- [Become a backer or sponsor on
Patreon](https://www.patreon.com/phpseclib)
- [One-time donation via PayPal or
crypto-currencies](http://sourceforge.net/donate/index.php?group_id=198487)
- [Subscribe to
Tidelift](https://tidelift.com/subscription/pkg/packagist-phpseclib-phpseclib?utm_source=packagist-phpseclib-phpseclib&utm_medium=referral&utm_campaign=readme)
## Introduction
MIT-licensed pure-PHP implementations of the following:
SSH-2, SFTP, X.509, an arbitrary-precision integer arithmetic library,
Ed25519 / Ed449 / Curve25519 / Curve449, ECDSA / ECDH (with support for 66
curves), RSA (PKCS#1 v2.2 compliant), DSA / DH, DES / 3DES / RC4 / Rijndael
/ AES / Blowfish / Twofish / Salsa20 / ChaCha20, GCM / Poly1305
* [Browse Git](https://github.com/phpseclib/phpseclib)
## Documentation
* [Documentation / Manual](https://phpseclib.com/)
* [API Documentation](https://api.phpseclib.com/2.0/) (generated by Doctum)
## Branches
### master
* Development Branch
* Unstable API
* Do not use in production
### 3.0
* Long term support (LTS) release
* Major expansion of cryptographic primitives
* Minimum PHP version: 5.6.1
* PSR-4 autoloading with namespace rooted at `\phpseclib3`
* Install via Composer: `composer require phpseclib/phpseclib:~3.0`
### 2.0
* Long term support (LTS) release
* Modernized version of 1.0
* Minimum PHP version: 5.3.3
* PSR-4 autoloading with namespace rooted at `\phpseclib`
* Install via Composer: `composer require phpseclib/phpseclib:~2.0`
### 1.0
* Long term support (LTS) release
* PHP4 compatible
* Composer compatible (PSR-0 autoloading)
* Install using Composer: `composer require phpseclib/phpseclib:~1.0`
* Install using PEAR: See [phpseclib PEAR Channel
Documentation](http://phpseclib.sourceforge.net/pear.htm)
* [Download 1.0.19 as
ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.19.zip/download)
## Security contact information
To report a security vulnerability, please use the [Tidelift security
contact](https://tidelift.com/security). Tidelift will coordinate the fix
and disclosure.
## Support
Need Support?
* [Checkout Questions and Answers on Stack
Overflow](http://stackoverflow.com/questions/tagged/phpseclib)
* [Create a Support Ticket on
GitHub](https://github.com/phpseclib/phpseclib/issues/new)
* [Browse the Support
Forum](http://www.frostjedi.com/phpbb/viewforum.php?f=46) (no longer in
use)
## Contributing
1. Fork the Project
2. Ensure you have Composer installed (see [Composer Download
Instructions](https://getcomposer.org/download/))
3. Install Development Dependencies
``` sh
composer install
```
4. Create a Feature Branch
5. (Recommended) Run the Test Suite
``` sh
vendor/bin/phpunit
```
6. (Recommended) Check whether your code conforms to our Coding Standards
by running
``` sh
vendor/bin/phing -f build/build.xml sniff
```
7. Send us a Pull Request
vendor/web.config000064400000000267151156520660010021 0ustar00<?xml
version="1.0"?>
<configuration>
<system.web>
<authorization>
<deny users="*" />
</authorization>
</system.web>
</configuration>vendor/composer/InstalledVersions.php000064400000035217151157062500014063
0ustar00<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available
to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*/
class InstalledVersions
{
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, version: string,
reference: string, pretty_version: string, aliases: string[], dev: bool,
install_path: string, type: string}, versions: array<string,
array{dev_requirement: bool, pretty_version?: string, version?: string,
aliases?: string[], reference?: string, replaced?: string[], provided?:
string[], install_path?: string, type?: string}>}|array{}|null
*/
private static $installed;
/**
* @var bool|null
*/
private static $canGetVendors;
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, version:
string, reference: string, pretty_version: string, aliases: string[], dev:
bool, install_path: string, type: string}, versions: array<string,
array{dev_requirement: bool, pretty_version?: string, version?: string,
aliases?: string[], reference?: string, replaced?: string[], provided?:
string[], install_path?: string, type?: string}>}>
*/
private static $installedByVendor = array();
/**
* Returns a list of all package names which are present, either by
being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return
array_keys(array_flip(\call_user_func_array('array_merge',
$packages)));
}
/**
* Returns a list of all package names with a specific type e.g.
'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name =>
$package) {
if (isset($package['type']) &&
$package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced
by another package
*
* @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName,
$includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements ||
empty($installed['versions'][$packageName]['dev_requirement']);
}
}
return false;
}
/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is
installed, you would call:
*
* Composer\InstalledVersions::satisfies(new VersionParser,
'foo/bar', '^2.3')
*
* @param VersionParser $parser Install composer/semver to have
access to this class and functionality
* @param string $packageName
* @param string|null $constraint A version constraint to check
for, if you pass one you have to make sure composer/semver is required by
your package
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName,
$constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided =
$parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
/**
* Returns a version constraint representing all the range(s) which are
installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint
argument if you need to check
* whether a given version of a package is installed, and not just
whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
if
(isset($installed['versions'][$packageName]['pretty_version']))
{
$ranges[] =
$installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases',
$installed['versions'][$packageName])) {
$ranges = array_merge($ranges,
$installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced',
$installed['versions'][$packageName])) {
$ranges = array_merge($ranges,
$installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided',
$installed['versions'][$packageName])) {
$ranges = array_merge($ranges,
$installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
throw new \OutOfBoundsException('Package "' .
$packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but
is not really installed, null will be returned as version, use satisfies or
getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if
(!isset($installed['versions'][$packageName]['version']))
{
return null;
}
return
$installed['versions'][$packageName]['version'];
}
throw new \OutOfBoundsException('Package "' .
$packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but
is not really installed, null will be returned as version, use satisfies or
getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if
(!isset($installed['versions'][$packageName]['pretty_version']))
{
return null;
}
return
$installed['versions'][$packageName]['pretty_version'];
}
throw new \OutOfBoundsException('Package "' .
$packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but
is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if
(!isset($installed['versions'][$packageName]['reference']))
{
return null;
}
return
$installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' .
$packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but
is not really installed, null will be returned as install path. Packages of
type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
return
isset($installed['versions'][$packageName]['install_path'])
? $installed['versions'][$packageName]['install_path']
: null;
}
throw new \OutOfBoundsException('Package "' .
$packageName . '" is not installed');
}
/**
* @return array
* @psalm-return array{name: string, version: string, reference:
string, pretty_version: string, aliases: string[], dev: bool, install_path:
string, type: string}
*/
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
/**
* Returns the raw installed.php data for custom implementations
*
* @deprecated Use getAllRawData() instead which returns all datasets
for all autoloaders present in the process. getRawData only returns the
first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, version: string,
reference: string, pretty_version: string, aliases: string[], dev: bool,
install_path: string, type: string}, versions: array<string,
array{dev_requirement: bool, pretty_version?: string, version?: string,
aliases?: string[], reference?: string, replaced?: string[], provided?:
string[], install_path?: string, type?: string}>}
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset
loaded, which may not be what you expect. Use getAllRawData() instead which
returns all datasets for all autoloaders present in the process.',
E_USER_DEPRECATED);
if (null === self::$installed) {
// only require the installed.php file if this file is loaded
from its dumped location,
// and not from its source location in the composer/composer
package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ .
'/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed;
}
/**
* Returns the raw data of all installed.php which are currently loaded
for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, version:
string, reference: string, pretty_version: string, aliases: string[], dev:
bool, install_path: string, type: string}, versions: array<string,
array{dev_requirement: bool, pretty_version?: string, version?: string,
aliases?: string[], reference?: string, replaced?: string[], provided?:
string[], install_path?: string, type?: string}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project
needs to use
* this class but then also needs to execute another project's
autoloader in process,
* and wants to ensure both projects have access to their version of
installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it
reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input
to make sure
* the project in which it runs can then also use this class safely,
without
* interference between PHPUnit's dependencies and the
project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, version: string,
reference: string, pretty_version: string, aliases: string[], dev: bool,
install_path: string, type: string}, versions: array<string,
array{dev_requirement: bool, pretty_version?: string, version?: string,
aliases?: string[], reference?: string, replaced?: string[], provided?:
string[], install_path?: string, type?: string}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, version:
string, reference: string, pretty_version: string, aliases: string[], dev:
bool, install_path: string, type: string}, versions: array<string,
array{dev_requirement: bool, pretty_version?: string, version?: string,
aliases?: string[], reference?: string, replaced?: string[], provided?:
string[], install_path?: string, type?: string}>}>
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors =
method_exists('Composer\Autoload\ClassLoader',
'getRegisteredLoaders');
}
$installed = array();
if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir
=> $loader) {
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif
(is_file($vendorDir.'/composer/installed.php')) {
$installed[] = self::$installedByVendor[$vendorDir] =
require $vendorDir.'/composer/installed.php';
if (null === self::$installed &&
strtr($vendorDir.'/composer', '\\', '/') ===
strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) -
1];
}
}
}
}
if (null === self::$installed) {
// only require the installed.php file if this file is loaded
from its dumped location,
// and not from its source location in the composer/composer
package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = require __DIR__ .
'/installed.php';
} else {
self::$installed = array();
}
}
$installed[] = self::$installed;
return $installed;
}
}
vendor/composer/installed.php000064400000002035151157062500012362
0ustar00<?php return array(
'root' => array(
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => NULL,
'name' => '__root__',
'dev' => true,
),
'versions' => array(
'__root__' => array(
'pretty_version' =>
'1.0.0+no-version-set',
'version' => '1.0.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => NULL,
'dev_requirement' => false,
),
'phpseclib/phpseclib' => array(
'pretty_version' => '2.0.35',
'version' => '2.0.35.0',
'type' => 'library',
'install_path' => __DIR__ .
'/../phpseclib/phpseclib',
'aliases' => array(),
'reference' =>
'4e16cf3f5f927a7d3f5317820af795c0366c0420',
'dev_requirement' => false,
),
),
);
vendor/composer/platform_check.php000064400000001635151157062500013371
0ustar00<?php
// platform_check.php @generated by Composer
$issues = array();
if (!(PHP_VERSION_ID >= 50303)) {
$issues[] = 'Your Composer dependencies require a PHP version
">= 5.3.3". You are running ' . PHP_VERSION .
'.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI ===
'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your
platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) .
PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' .
PHP_EOL.PHP_EOL . str_replace('You are running
'.PHP_VERSION.'.', '', implode(PHP_EOL, $issues))
. PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' .
implode(' ', $issues),
E_USER_ERROR
);
}
vendor/.htaccess000064400000000226151157062510007642 0ustar00# Apache 2.4+
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
# Apache 2.0-2.2
<IfModule !mod_authz_core.c>
Deny from all
</IfModule>
phpseclib/appveyor.yml000064400000001427151161207740011115 0ustar00build:
false
shallow_clone: false
platform:
- x86
- x64
clone_folder: C:\projects\phpseclib
install:
- cinst -y OpenSSL.Light
- SET PATH=C:\Program Files\OpenSSL;%PATH%
- sc config wuauserv start= auto
- net start wuauserv
- cinst -y php --version 5.6.30
- cd c:\tools\php56
- copy php.ini-production php.ini
- echo date.timezone="UTC" >> php.ini
- echo extension_dir=ext >> php.ini
- echo extension=php_openssl.dll >> php.ini
- echo extension=php_gmp.dll >> php.ini
- cd C:\projects\phpseclib
- SET PATH=C:\tools\php56;%PATH%
- php.exe -r
"readfile('http://getcomposer.org/installer');" |
php.exe
- php.exe composer.phar install --prefer-source --no-interaction
test_script:
- cd C:\projects\phpseclib
- vendor\bin\phpunit.bat
tests/Windows32Test.phpphpseclib/AUTHORS000064400000000427151161207740007574
0ustar00phpseclib Lead Developer: TerraFrost (Jim Wigginton)
phpseclib Developers: monnerat (Patrick Monnerat)
bantu (Andreas Fischer)
petrich (Hans-Jürgen Petrich)
GrahamCampbell (Graham Campbell)
phpseclib/BACKERS.md000064400000000523151161207740010115 0ustar00# Backers
phpseclib ongoing development is made possible by
[Tidelift](https://tidelift.com/subscription/pkg/packagist-phpseclib-phpseclib?utm_source=packagist-phpseclib-phpseclib&utm_medium=referral&utm_campaign=readme)
and by contributions by users like you. Thank you.
## Backers
- Zane Hooper
-
[Setasign](https://www.setasign.com/)phpseclib/composer.json000064400000004210151161207740011240
0ustar00{
"name": "phpseclib/phpseclib",
"type": "library",
"description": "PHP Secure Communications Library -
Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
"keywords": [
"security",
"crypto",
"cryptography",
"encryption",
"signature",
"signing",
"rsa",
"aes",
"blowfish",
"twofish",
"ssh",
"sftp",
"x509",
"x.509",
"asn1",
"asn.1",
"BigInteger"
],
"homepage": "http://phpseclib.sourceforge.net",
"license": "MIT",
"authors": [
{
"name": "Jim Wigginton",
"email": "terrafrost@php.net",
"role": "Lead Developer"
},
{
"name": "Patrick Monnerat",
"email": "pm@datasphere.ch",
"role": "Developer"
},
{
"name": "Andreas Fischer",
"email": "bantu@phpbb.com",
"role": "Developer"
},
{
"name": "Hans-Jürgen Petrich",
"email": "petrich@tronic-media.com",
"role": "Developer"
},
{
"name": "Graham Campbell",
"email": "graham@alt-three.com",
"role": "Developer"
}
],
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phing/phing": "~2.7",
"phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4",
"squizlabs/php_codesniffer": "~2.0"
},
"suggest": {
"ext-libsodium": "SSH2/SFTP can make use of some
algorithms provided by the libsodium-php extension.",
"ext-openssl": "Install the OpenSSL extension in
order to speed up a wide variety of cryptographic operations.",
"ext-mcrypt": "Install the Mcrypt extension in order
to speed up a few other cryptographic operations.",
"ext-gmp": "Install the GMP (GNU Multiple Precision)
extension in order to speed up arbitrary precision integer arithmetic
operations."
},
"autoload": {
"files": [
"phpseclib/bootstrap.php"
],
"psr-4": {
"phpseclib\\": "phpseclib/"
}
}
}
phpseclib/LICENSE000064400000002071151161207740007526 0ustar00Copyright (c)
2011-2019 TerraFrost and other contributors
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.phpseclib/phpseclib/bootstrap.php000064400000000660151161207740013222
0ustar00<?php
/**
* Bootstrapping File for phpseclib
*
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*/
if (extension_loaded('mbstring')) {
// 2 - MB_OVERLOAD_STRING
if (ini_get('mbstring.func_overload') & 2) {
throw new \UnexpectedValueException(
'Overloading of string functions using
mbstring.func_overload ' .
'is not supported by phpseclib.'
);
}
}
phpseclib/phpseclib/Crypt/AES.php000064400000007165151161207740012725
0ustar00<?php
/**
* Pure-PHP implementation of AES.
*
* Uses mcrypt, if available/possible, and an internal implementation,
otherwise.
*
* PHP version 5
*
* NOTE: Since AES.php is (for compatibility and phpseclib-historical
reasons) virtually
* just a wrapper to Rijndael.php you may consider using Rijndael.php
instead of
* to save one include_once().
*
* If {@link self::setKeyLength() setKeyLength()} isn't called,
it'll be calculated from
* {@link self::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 192-bits and 192 bits will be the key
length until {@link self::setKey() setKey()}
* is called, again, at which point, it'll be recalculated.
*
* Since \phpseclib\Crypt\AES extends \phpseclib\Crypt\Rijndael, some
functions are available to be called that, in the context of AES,
don't
* make a whole lot of sense. {@link self::setBlockLength()
setBlockLength()}, for instance. Calling that function,
* however possible, won't do anything (AES has a fixed block length
whereas Rijndael has a variable one).
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $aes = new \phpseclib\Crypt\AES();
*
* $aes->setKey('abcdefghijklmnop');
*
* $size = 10 * 1024;
* $plaintext = '';
* for ($i = 0; $i < $size; $i++) {
* $plaintext.= 'a';
* }
*
* echo $aes->decrypt($aes->encrypt($plaintext));
* ?>
* </code>
*
* @category Crypt
* @package AES
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2008 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Pure-PHP implementation of AES.
*
* @package AES
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class AES extends Rijndael
{
/**
* Dummy function
*
* Since \phpseclib\Crypt\AES extends \phpseclib\Crypt\Rijndael, this
function is, technically, available, but it doesn't do anything.
*
* @see \phpseclib\Crypt\Rijndael::setBlockLength()
* @access public
* @param int $length
*/
function setBlockLength($length)
{
return;
}
/**
* 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
* 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()
* @access public
* @param int $length
*/
function setKeyLength($length)
{
switch ($length) {
case 160:
$length = 192;
break;
case 224:
$length = 256;
}
parent::setKeyLength($length);
}
/**
* Sets the key.
*
* Rijndael supports five different key lengths, AES only supports
three.
*
* @see \phpseclib\Crypt\Rijndael:setKey()
* @see setKeyLength()
* @access public
* @param string $key
*/
function setKey($key)
{
parent::setKey($key);
if (!$this->explicit_key_length) {
$length = strlen($key);
switch (true) {
case $length <= 16:
$this->key_length = 16;
break;
case $length <= 24:
$this->key_length = 24;
break;
default:
$this->key_length = 32;
}
$this->_setEngine();
}
}
}
phpseclib/phpseclib/Crypt/Base.php000064400000312063151161207740013163
0ustar00<?php
/**
* Base Class for all \phpseclib\Crypt\* cipher classes
*
* PHP version 5
*
* Internally for phpseclib developers:
* If you plan to add a new cipher class, please note following rules:
*
* - The new \phpseclib\Crypt\* cipher class should extend
\phpseclib\Crypt\Base
*
* - Following methods are then required to be overridden/overloaded:
*
* - _encryptBlock()
*
* - _decryptBlock()
*
* - _setupKey()
*
* - All other methods are optional to be overridden/overloaded
*
* - Look at the source code of the current ciphers how they extend
\phpseclib\Crypt\Base
* and take one of them as a start up for the new cipher class.
*
* - Please read all the other comments/notes/hints here also for each
class var/method
*
* @category Crypt
* @package Base
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Base Class for all \phpseclib\Crypt\* cipher classes
*
* @package Base
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
*/
abstract class Base
{
/**#@+
* @access public
* @see \phpseclib\Crypt\Base::encrypt()
* @see \phpseclib\Crypt\Base::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
*/
const MODE_CTR = -1;
/**
* Encrypt / decrypt using the Electronic Code Book mode.
*
* @link
http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
*/
const MODE_ECB = 1;
/**
* Encrypt / decrypt using the Code Book Chaining mode.
*
* @link
http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
*/
const MODE_CBC = 2;
/**
* Encrypt / decrypt using the Cipher Feedback mode.
*
* @link
http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
*/
const MODE_CFB = 3;
/**
* Encrypt / decrypt using the Cipher Feedback mode (8bit)
*/
const MODE_CFB8 = 38;
/**
* Encrypt / decrypt using the Output Feedback mode.
*
* @link
http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
*/
const MODE_OFB = 4;
/**
* Encrypt / decrypt using streaming mode.
*/
const MODE_STREAM = 5;
/**#@-*/
/**
* Whirlpool available flag
*
* @see \phpseclib\Crypt\Base::_hashInlineCryptFunction()
* @var bool
* @access private
*/
static $WHIRLPOOL_AVAILABLE;
/**#@+
* @access private
* @see \phpseclib\Crypt\Base::__construct()
*/
/**
* Base value for the internal implementation $engine switch
*/
const ENGINE_INTERNAL = 1;
/**
* Base value for the mcrypt implementation $engine switch
*/
const ENGINE_MCRYPT = 2;
/**
* Base value for the mcrypt implementation $engine switch
*/
const ENGINE_OPENSSL = 3;
/**#@-*/
/**
* The Encryption Mode
*
* @see self::__construct()
* @var int
* @access private
*/
var $mode;
/**
* The Block Length of the block cipher
*
* @var int
* @access private
*/
var $block_size = 16;
/**
* The Key
*
* @see self::setKey()
* @var string
* @access private
*/
var $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
/**
* The Initialization Vector
*
* @see self::setIV()
* @var string
* @access private
*/
var $iv;
/**
* A "sliding" Initialization Vector
*
* @see self::enableContinuousBuffer()
* @see self::_clearBuffers()
* @var string
* @access private
*/
var $encryptIV;
/**
* A "sliding" Initialization Vector
*
* @see self::enableContinuousBuffer()
* @see self::_clearBuffers()
* @var string
* @access private
*/
var $decryptIV;
/**
* Continuous Buffer status
*
* @see self::enableContinuousBuffer()
* @var bool
* @access private
*/
var $continuousBuffer = false;
/**
* Encryption buffer for CTR, OFB and CFB modes
*
* @see self::encrypt()
* @see self::_clearBuffers()
* @var array
* @access private
*/
var $enbuffer;
/**
* Decryption buffer for CTR, OFB and CFB modes
*
* @see self::decrypt()
* @see self::_clearBuffers()
* @var array
* @access private
*/
var $debuffer;
/**
* 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 self::encrypt()
* @var resource
* @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 self::decrypt()
* @var resource
* @access private
*/
var $demcrypt;
/**
* Does the enmcrypt resource need to be (re)initialized?
*
* @see \phpseclib\Crypt\Twofish::setKey()
* @see \phpseclib\Crypt\Twofish::setIV()
* @var bool
* @access private
*/
var $enchanged = true;
/**
* Does the demcrypt resource need to be (re)initialized?
*
* @see \phpseclib\Crypt\Twofish::setKey()
* @see \phpseclib\Crypt\Twofish::setIV()
* @var bool
* @access private
*/
var $dechanged = true;
/**
* mcrypt resource for CFB mode
*
* mcrypt's CFB mode, in (and only in) buffered context,
* is broken, so phpseclib implements the CFB mode by it self,
* even when the mcrypt php extension is available.
*
* In order to do the CFB-mode work (fast) phpseclib
* use a separate ECB-mode mcrypt resource.
*
* @link http://phpseclib.sourceforge.net/cfb-demo.phps
* @see self::encrypt()
* @see self::decrypt()
* @see self::_setupMcrypt()
* @var resource
* @access private
*/
var $ecb;
/**
* Optimizing value while CFB-encrypting
*
* Only relevant if $continuousBuffer enabled
* and $engine == self::ENGINE_MCRYPT
*
* It's faster to re-init $enmcrypt if
* $buffer bytes > $cfb_init_len than
* using the $ecb resource furthermore.
*
* This value depends of the chosen cipher
* and the time it would be needed for it's
* initialization [by mcrypt_generic_init()]
* which, typically, depends on the complexity
* on its internaly Key-expanding algorithm.
*
* @see self::encrypt()
* @var int
* @access private
*/
var $cfb_init_len = 600;
/**
* Does internal cipher state need to be (re)initialized?
*
* @see self::setKey()
* @see self::setIV()
* @see self::disableContinuousBuffer()
* @var bool
* @access private
*/
var $changed = true;
/**
* Padding status
*
* @see self::enablePadding()
* @var bool
* @access private
*/
var $padding = true;
/**
* Is the mode one that is paddable?
*
* @see self::__construct()
* @var bool
* @access private
*/
var $paddable = false;
/**
* Holds which crypt engine internaly should be use,
* which will be determined automatically on __construct()
*
* Currently available $engines are:
* - self::ENGINE_OPENSSL (very fast, php-extension: openssl,
extension_loaded('openssl') required)
* - self::ENGINE_MCRYPT (fast, php-extension: mcrypt,
extension_loaded('mcrypt') required)
* - self::ENGINE_INTERNAL (slower, pure php-engine, no php-extension
required)
*
* @see self::_setEngine()
* @see self::encrypt()
* @see self::decrypt()
* @var int
* @access private
*/
var $engine;
/**
* Holds the preferred crypt engine
*
* @see self::_setEngine()
* @see self::setPreferredEngine()
* @var int
* @access private
*/
var $preferredEngine;
/**
* The mcrypt specific name of the cipher
*
* Only used if $engine == self::ENGINE_MCRYPT
*
* @link http://www.php.net/mcrypt_module_open
* @link http://www.php.net/mcrypt_list_algorithms
* @see self::_setupMcrypt()
* @var string
* @access private
*/
var $cipher_name_mcrypt;
/**
* The openssl specific name of the cipher
*
* Only used if $engine == self::ENGINE_OPENSSL
*
* @link http://www.php.net/openssl-get-cipher-methods
* @var string
* @access private
*/
var $cipher_name_openssl;
/**
* The openssl specific name of the cipher in ECB mode
*
* If OpenSSL does not support the mode we're trying to use (CTR)
* it can still be emulated with ECB mode.
*
* @link http://www.php.net/openssl-get-cipher-methods
* @var string
* @access private
*/
var $cipher_name_openssl_ecb;
/**
* The default salt used by setPassword()
*
* @see self::setPassword()
* @var string
* @access private
*/
var $password_default_salt = 'phpseclib/salt';
/**
* The name of the performance-optimized callback function
*
* Used by encrypt() / decrypt()
* only if $engine == self::ENGINE_INTERNAL
*
* @see self::encrypt()
* @see self::decrypt()
* @see self::_setupInlineCrypt()
* @see self::$use_inline_crypt
* @var Callback
* @access private
*/
var $inline_crypt;
/**
* Holds whether performance-optimized $inline_crypt() can/should be
used.
*
* @see self::encrypt()
* @see self::decrypt()
* @see self::inline_crypt
* @var mixed
* @access private
*/
var $use_inline_crypt = true;
/**
* If OpenSSL can be used in ECB but not in CTR we can emulate CTR
*
* @see self::_openssl_ctr_process()
* @var bool
* @access private
*/
var $openssl_emulate_ctr = false;
/**
* Determines what options are passed to openssl_encrypt/decrypt
*
* @see self::isValidEngine()
* @var mixed
* @access private
*/
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
*
* @see self::_clearBuffers()
* @var bool
* @access private
*/
var $skip_key_adjustment = false;
/**
* Default Constructor.
*
* Determines whether or not the mcrypt extension should be used.
*
* $mode could be:
*
* - self::MODE_ECB
*
* - self::MODE_CBC
*
* - self::MODE_CTR
*
* - self::MODE_CFB
*
* - self::MODE_OFB
*
* If not explicitly set, self::MODE_CBC will be used.
*
* @param int $mode
* @access public
*/
function __construct($mode = self::MODE_CBC)
{
// $mode dependent settings
switch ($mode) {
case self::MODE_ECB:
$this->paddable = true;
$this->mode = self::MODE_ECB;
break;
case self::MODE_CTR:
case self::MODE_CFB:
case self::MODE_CFB8:
case self::MODE_OFB:
case self::MODE_STREAM:
$this->mode = $mode;
break;
case self::MODE_CBC:
default:
$this->paddable = true;
$this->mode = self::MODE_CBC;
}
$this->_setEngine();
}
/**
* Sets the initialization vector. (optional)
*
* SetIV is not required when self::MODE_ECB (or ie for AES:
\phpseclib\Crypt\AES::MODE_ECB) is being used. If not explicitly set,
it'll be assumed
* to be all zero's.
*
* @access public
* @param string $iv
* @internal Can be overwritten by a sub class, but does not have to be
*/
function setIV($iv)
{
if ($this->mode == self::MODE_ECB) {
return;
}
$this->iv = $iv;
$this->changed = true;
}
/**
* 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 = true;
$this->changed = true;
$this->_setEngine();
}
/**
* Returns the current key length in bits
*
* @access public
* @return int
*/
function getKeyLength()
{
return $this->key_length << 3;
}
/**
* Returns the current block length in bits
*
* @access public
* @return int
*/
function getBlockLength()
{
return $this->block_size << 3;
}
/**
* Sets the key.
*
* The min/max length(s) of the key depends on the cipher which is
used.
* If the key not fits the length(s) of the cipher it will paded with
null bytes
* up to the closest valid key length. If the key is more than max
length,
* we trim the excess bits.
*
* If the key is not explicitly set, it'll be assumed to be all
null bytes.
*
* @access public
* @param string $key
* @internal Could, but not must, extend by the child Crypt_* class
*/
function setKey($key)
{
if (!$this->explicit_key_length) {
$this->setKeyLength(strlen($key) << 3);
$this->explicit_key_length = false;
}
$this->key = $key;
$this->changed = true;
$this->_setEngine();
}
/**
* 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} or pbkdf1:
* $hash, $salt, $count, $dkLen
*
* Where $hash (default = sha1) currently supports the
following hashes: see: Crypt/Hash.php
*
* @see Crypt/Hash.php
* @param string $password
* @param string $method
* @return bool
* @access public
* @internal Could, but not must, extend by the child Crypt_* class
*/
function setPassword($password, $method = 'pbkdf2')
{
$key = '';
switch ($method) {
default: // 'pbkdf2' or 'pbkdf1'
$func_args = func_get_args();
// Hash function
$hash = isset($func_args[2]) ? $func_args[2] :
'sha1';
// WPA and WPA2 use the SSID as the salt
$salt = isset($func_args[3]) ? $func_args[3] :
$this->password_default_salt;
// RFC2898#section-4.2 uses 1,000 iterations by default
// WPA and WPA2 use 4,096.
$count = isset($func_args[4]) ? $func_args[4] : 1000;
// Keylength
if (isset($func_args[5])) {
$dkLen = $func_args[5];
} else {
$dkLen = $method == 'pbkdf1' ? 2 *
$this->key_length : $this->key_length;
}
switch (true) {
case $method == 'pbkdf1':
$hashObj = new Hash();
$hashObj->setHash($hash);
if ($dkLen > $hashObj->getLength()) {
user_error('Derived key too long');
return false;
}
$t = $password . $salt;
for ($i = 0; $i < $count; ++$i) {
$t = $hashObj->hash($t);
}
$key = substr($t, 0, $dkLen);
$this->setKey(substr($key, 0, $dkLen >>
1));
$this->setIV(substr($key, $dkLen >> 1));
return true;
// Determining if php[>=5.5.0]'s hash_pbkdf2()
function avail- and useable
case !function_exists('hash_pbkdf2'):
case !function_exists('hash_algos'):
case !in_array($hash, hash_algos()):
$i = 1;
$hmac = new Hash();
$hmac->setHash($hash);
$hmac->setKey($password);
while (strlen($key) < $dkLen) {
$f = $u = $hmac->hash($salt .
pack('N', $i++));
for ($j = 2; $j <= $count; ++$j) {
$u = $hmac->hash($u);
$f^= $u;
}
$key.= $f;
}
$key = substr($key, 0, $dkLen);
break;
default:
$key = hash_pbkdf2($hash, $password, $salt, $count,
$dkLen, true);
}
}
$this->setKey($key);
return true;
}
/**
* Encrypts a message.
*
* $plaintext will be padded with additional bytes such that it's
length is a multiple of the block size. Other cipher
* implementations may or may not pad in the 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}
*
* 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 the block
size, however, arbitrary values can be added to make it that
* length.
*
* @see self::decrypt()
* @access public
* @param string $plaintext
* @return string $ciphertext
* @internal Could, but not must, extend by the child Crypt_* class
*/
function encrypt($plaintext)
{
if ($this->paddable) {
$plaintext = $this->_pad($plaintext);
}
if ($this->engine === self::ENGINE_OPENSSL) {
if ($this->changed) {
$this->_clearBuffers();
$this->changed = false;
}
switch ($this->mode) {
case self::MODE_STREAM:
return openssl_encrypt($plaintext,
$this->cipher_name_openssl, $this->key, $this->openssl_options);
case self::MODE_ECB:
$result = @openssl_encrypt($plaintext,
$this->cipher_name_openssl, $this->key, $this->openssl_options);
return !defined('OPENSSL_RAW_DATA') ?
substr($result, 0, -$this->block_size) : $result;
case self::MODE_CBC:
$result = openssl_encrypt($plaintext,
$this->cipher_name_openssl, $this->key, $this->openssl_options,
$this->encryptIV);
if (!defined('OPENSSL_RAW_DATA')) {
$result = substr($result, 0,
-$this->block_size);
}
if ($this->continuousBuffer) {
$this->encryptIV = substr($result,
-$this->block_size);
}
return $result;
case self::MODE_CTR:
return $this->_openssl_ctr_process($plaintext,
$this->encryptIV, $this->enbuffer);
case self::MODE_CFB:
// cfb loosely routines inspired by openssl's:
// {@link
http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
$ciphertext = '';
if ($this->continuousBuffer) {
$iv = &$this->encryptIV;
$pos = &$this->enbuffer['pos'];
} else {
$iv = $this->encryptIV;
$pos = 0;
}
$len = strlen($plaintext);
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $this->block_size - $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
$ciphertext = substr($iv, $orig_pos) ^ $plaintext;
$iv = substr_replace($iv, $ciphertext, $orig_pos,
$i);
$plaintext = substr($plaintext, $i);
}
$overflow = $len % $this->block_size;
if ($overflow) {
$ciphertext.= openssl_encrypt(substr($plaintext, 0,
-$overflow) . str_repeat("\0", $this->block_size),
$this->cipher_name_openssl, $this->key, $this->openssl_options,
$iv);
$iv = $this->_string_pop($ciphertext,
$this->block_size);
$size = $len - $overflow;
$block = $iv ^ substr($plaintext, -$overflow);
$iv = substr_replace($iv, $block, 0, $overflow);
$ciphertext.= $block;
$pos = $overflow;
} elseif ($len) {
$ciphertext = openssl_encrypt($plaintext,
$this->cipher_name_openssl, $this->key, $this->openssl_options,
$iv);
$iv = substr($ciphertext, -$this->block_size);
}
return $ciphertext;
case self::MODE_CFB8:
$ciphertext = openssl_encrypt($plaintext,
$this->cipher_name_openssl, $this->key, $this->openssl_options,
$this->encryptIV);
if ($this->continuousBuffer) {
if (($len = strlen($ciphertext)) >=
$this->block_size) {
$this->encryptIV = substr($ciphertext,
-$this->block_size);
} else {
$this->encryptIV =
substr($this->encryptIV, $len - $this->block_size) .
substr($ciphertext, -$len);
}
}
return $ciphertext;
case self::MODE_OFB:
return $this->_openssl_ofb_process($plaintext,
$this->encryptIV, $this->enbuffer);
}
}
if ($this->engine === self::ENGINE_MCRYPT) {
set_error_handler(array($this, 'do_nothing'));
if ($this->changed) {
$this->_setupMcrypt();
$this->changed = false;
}
if ($this->enchanged) {
mcrypt_generic_init($this->enmcrypt, $this->key,
$this->encryptIV);
$this->enchanged = false;
}
// re: {@link 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 == self::MODE_CFB &&
$this->continuousBuffer) {
$block_size = $this->block_size;
$iv = &$this->encryptIV;
$pos = &$this->enbuffer['pos'];
$len = strlen($plaintext);
$ciphertext = '';
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $block_size - $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 >= $block_size) {
if ($this->enbuffer['enmcrypt_init'] ===
false || $len > $this->cfb_init_len) {
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 % $block_size));
$iv = substr($ciphertext, -$block_size);
$len%= $block_size;
} else {
while ($len >= $block_size) {
$iv = mcrypt_generic($this->ecb, $iv) ^
substr($plaintext, $i, $block_size);
$ciphertext.= $iv;
$len-= $block_size;
$i+= $block_size;
}
}
}
if ($len) {
$iv = mcrypt_generic($this->ecb, $iv);
$block = $iv ^ substr($plaintext, -$len);
$iv = substr_replace($iv, $block, 0, $len);
$ciphertext.= $block;
$pos = $len;
}
restore_error_handler();
return $ciphertext;
}
$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->enmcrypt, $this->key,
$this->encryptIV);
}
restore_error_handler();
return $ciphertext;
}
if ($this->changed) {
$this->_setup();
$this->changed = false;
}
if ($this->use_inline_crypt) {
$inline = $this->inline_crypt;
return $inline('encrypt', $this, $plaintext);
}
$buffer = &$this->enbuffer;
$block_size = $this->block_size;
$ciphertext = '';
switch ($this->mode) {
case self::MODE_ECB:
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$ciphertext.=
$this->_encryptBlock(substr($plaintext, $i, $block_size));
}
break;
case self::MODE_CBC:
$xor = $this->encryptIV;
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
$block = $this->_encryptBlock($block ^ $xor);
$xor = $block;
$ciphertext.= $block;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
}
break;
case self::MODE_CTR:
$xor = $this->encryptIV;
if (strlen($buffer['ciphertext'])) {
for ($i = 0; $i < strlen($plaintext);
$i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
if (strlen($block) >
strlen($buffer['ciphertext'])) {
$buffer['ciphertext'].=
$this->_encryptBlock($xor);
}
$this->_increment_str($xor);
$key =
$this->_string_shift($buffer['ciphertext'], $block_size);
$ciphertext.= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($plaintext);
$i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
$key = $this->_encryptBlock($xor);
$this->_increment_str($xor);
$ciphertext.= $block ^ $key;
}
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
if ($start = strlen($plaintext) % $block_size) {
$buffer['ciphertext'] = substr($key,
$start) . $buffer['ciphertext'];
}
}
break;
case self::MODE_CFB:
// cfb loosely routines inspired by openssl's:
// {@link
http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
if ($this->continuousBuffer) {
$iv = &$this->encryptIV;
$pos = &$buffer['pos'];
} else {
$iv = $this->encryptIV;
$pos = 0;
}
$len = strlen($plaintext);
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $block_size - $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
$ciphertext = substr($iv, $orig_pos) ^ $plaintext;
$iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
}
while ($len >= $block_size) {
$iv = $this->_encryptBlock($iv) ^ substr($plaintext,
$i, $block_size);
$ciphertext.= $iv;
$len-= $block_size;
$i+= $block_size;
}
if ($len) {
$iv = $this->_encryptBlock($iv);
$block = $iv ^ substr($plaintext, $i);
$iv = substr_replace($iv, $block, 0, $len);
$ciphertext.= $block;
$pos = $len;
}
break;
case self::MODE_CFB8:
$ciphertext = '';
$len = strlen($plaintext);
$iv = $this->encryptIV;
for ($i = 0; $i < $len; ++$i) {
$ciphertext .= ($c = $plaintext[$i] ^
$this->_encryptBlock($iv));
$iv = substr($iv, 1) . $c;
}
if ($this->continuousBuffer) {
if ($len >= $block_size) {
$this->encryptIV = substr($ciphertext,
-$block_size);
} else {
$this->encryptIV = substr($this->encryptIV,
$len - $block_size) . substr($ciphertext, -$len);
}
}
break;
case self::MODE_OFB:
$xor = $this->encryptIV;
if (strlen($buffer['xor'])) {
for ($i = 0; $i < strlen($plaintext);
$i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
if (strlen($block) >
strlen($buffer['xor'])) {
$xor = $this->_encryptBlock($xor);
$buffer['xor'].= $xor;
}
$key =
$this->_string_shift($buffer['xor'], $block_size);
$ciphertext.= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($plaintext);
$i+=$block_size) {
$xor = $this->_encryptBlock($xor);
$ciphertext.= substr($plaintext, $i, $block_size) ^
$xor;
}
$key = $xor;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
if ($start = strlen($plaintext) % $block_size) {
$buffer['xor'] = substr($key, $start) .
$buffer['xor'];
}
}
break;
case self::MODE_STREAM:
$ciphertext = $this->_encryptBlock($plaintext);
break;
}
return $ciphertext;
}
/**
* Decrypts a message.
*
* If strlen($ciphertext) is not a multiple of the block size, null
bytes will be added to the end of the string until
* it is.
*
* @see self::encrypt()
* @access public
* @param string $ciphertext
* @return string $plaintext
* @internal Could, but not must, extend by the child Crypt_* class
*/
function decrypt($ciphertext)
{
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}:
// "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->changed) {
$this->_clearBuffers();
$this->changed = false;
}
switch ($this->mode) {
case self::MODE_STREAM:
$plaintext = openssl_decrypt($ciphertext,
$this->cipher_name_openssl, $this->key, $this->openssl_options);
break;
case self::MODE_ECB:
if (!defined('OPENSSL_RAW_DATA')) {
$ciphertext.= @openssl_encrypt('',
$this->cipher_name_openssl_ecb, $this->key, true);
}
$plaintext = openssl_decrypt($ciphertext,
$this->cipher_name_openssl, $this->key, $this->openssl_options);
break;
case self::MODE_CBC:
if (!defined('OPENSSL_RAW_DATA')) {
$padding = str_repeat(chr($this->block_size),
$this->block_size) ^ substr($ciphertext, -$this->block_size);
$ciphertext.= substr(@openssl_encrypt($padding,
$this->cipher_name_openssl_ecb, $this->key, true), 0,
$this->block_size);
$offset = 2 * $this->block_size;
} else {
$offset = $this->block_size;
}
$plaintext = openssl_decrypt($ciphertext,
$this->cipher_name_openssl, $this->key, $this->openssl_options,
$this->decryptIV);
if ($this->continuousBuffer) {
$this->decryptIV = substr($ciphertext, -$offset,
$this->block_size);
}
break;
case self::MODE_CTR:
$plaintext =
$this->_openssl_ctr_process($ciphertext, $this->decryptIV,
$this->debuffer);
break;
case self::MODE_CFB:
// cfb loosely routines inspired by openssl's:
// {@link
http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
$plaintext = '';
if ($this->continuousBuffer) {
$iv = &$this->decryptIV;
$pos = &$this->buffer['pos'];
} else {
$iv = $this->decryptIV;
$pos = 0;
}
$len = strlen($ciphertext);
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $this->block_size - $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%= $this->blocksize
$plaintext = substr($iv, $orig_pos) ^ $ciphertext;
$iv = substr_replace($iv, substr($ciphertext, 0,
$i), $orig_pos, $i);
$ciphertext = substr($ciphertext, $i);
}
$overflow = $len % $this->block_size;
if ($overflow) {
$plaintext.= openssl_decrypt(substr($ciphertext, 0,
-$overflow), $this->cipher_name_openssl, $this->key,
$this->openssl_options, $iv);
if ($len - $overflow) {
$iv = substr($ciphertext, -$overflow -
$this->block_size, -$overflow);
}
$iv = openssl_encrypt(str_repeat("\0",
$this->block_size), $this->cipher_name_openssl, $this->key,
$this->openssl_options, $iv);
$plaintext.= $iv ^ substr($ciphertext, -$overflow);
$iv = substr_replace($iv, substr($ciphertext,
-$overflow), 0, $overflow);
$pos = $overflow;
} elseif ($len) {
$plaintext.= openssl_decrypt($ciphertext,
$this->cipher_name_openssl, $this->key, $this->openssl_options,
$iv);
$iv = substr($ciphertext, -$this->block_size);
}
break;
case self::MODE_CFB8:
$plaintext = openssl_decrypt($ciphertext,
$this->cipher_name_openssl, $this->key, $this->openssl_options,
$this->decryptIV);
if ($this->continuousBuffer) {
if (($len = strlen($ciphertext)) >=
$this->block_size) {
$this->decryptIV = substr($ciphertext,
-$this->block_size);
} else {
$this->decryptIV =
substr($this->decryptIV, $len - $this->block_size) .
substr($ciphertext, -$len);
}
}
break;
case self::MODE_OFB:
$plaintext =
$this->_openssl_ofb_process($ciphertext, $this->decryptIV,
$this->debuffer);
}
return $this->paddable ? $this->_unpad($plaintext) :
$plaintext;
}
if ($this->engine === self::ENGINE_MCRYPT) {
set_error_handler(array($this, 'do_nothing'));
$block_size = $this->block_size;
if ($this->changed) {
$this->_setupMcrypt();
$this->changed = false;
}
if ($this->dechanged) {
mcrypt_generic_init($this->demcrypt, $this->key,
$this->decryptIV);
$this->dechanged = false;
}
if ($this->mode == self::MODE_CFB &&
$this->continuousBuffer) {
$iv = &$this->decryptIV;
$pos = &$this->debuffer['pos'];
$len = strlen($ciphertext);
$plaintext = '';
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $block_size - $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 >= $block_size) {
$cb = substr($ciphertext, $i, $len - $len %
$block_size);
$plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^
$cb;
$iv = substr($cb, -$block_size);
$len%= $block_size;
}
if ($len) {
$iv = mcrypt_generic($this->ecb, $iv);
$plaintext.= $iv ^ substr($ciphertext, -$len);
$iv = substr_replace($iv, substr($ciphertext, -$len),
0, $len);
$pos = $len;
}
restore_error_handler();
return $plaintext;
}
$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->demcrypt, $this->key,
$this->decryptIV);
}
restore_error_handler();
return $this->paddable ? $this->_unpad($plaintext) :
$plaintext;
}
if ($this->changed) {
$this->_setup();
$this->changed = false;
}
if ($this->use_inline_crypt) {
$inline = $this->inline_crypt;
return $inline('decrypt', $this, $ciphertext);
}
$block_size = $this->block_size;
$buffer = &$this->debuffer;
$plaintext = '';
switch ($this->mode) {
case self::MODE_ECB:
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size)
{
$plaintext.=
$this->_decryptBlock(substr($ciphertext, $i, $block_size));
}
break;
case self::MODE_CBC:
$xor = $this->decryptIV;
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size)
{
$block = substr($ciphertext, $i, $block_size);
$plaintext.= $this->_decryptBlock($block) ^ $xor;
$xor = $block;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
}
break;
case self::MODE_CTR:
$xor = $this->decryptIV;
if (strlen($buffer['ciphertext'])) {
for ($i = 0; $i < strlen($ciphertext);
$i+=$block_size) {
$block = substr($ciphertext, $i, $block_size);
if (strlen($block) >
strlen($buffer['ciphertext'])) {
$buffer['ciphertext'].=
$this->_encryptBlock($xor);
$this->_increment_str($xor);
}
$key =
$this->_string_shift($buffer['ciphertext'], $block_size);
$plaintext.= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($ciphertext);
$i+=$block_size) {
$block = substr($ciphertext, $i, $block_size);
$key = $this->_encryptBlock($xor);
$this->_increment_str($xor);
$plaintext.= $block ^ $key;
}
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
if ($start = strlen($ciphertext) % $block_size) {
$buffer['ciphertext'] = substr($key,
$start) . $buffer['ciphertext'];
}
}
break;
case self::MODE_CFB:
if ($this->continuousBuffer) {
$iv = &$this->decryptIV;
$pos = &$buffer['pos'];
} else {
$iv = $this->decryptIV;
$pos = 0;
}
$len = strlen($ciphertext);
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $block_size - $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);
}
while ($len >= $block_size) {
$iv = $this->_encryptBlock($iv);
$cb = substr($ciphertext, $i, $block_size);
$plaintext.= $iv ^ $cb;
$iv = $cb;
$len-= $block_size;
$i+= $block_size;
}
if ($len) {
$iv = $this->_encryptBlock($iv);
$plaintext.= $iv ^ substr($ciphertext, $i);
$iv = substr_replace($iv, substr($ciphertext, $i), 0,
$len);
$pos = $len;
}
break;
case self::MODE_CFB8:
$plaintext = '';
$len = strlen($ciphertext);
$iv = $this->decryptIV;
for ($i = 0; $i < $len; ++$i) {
$plaintext .= $ciphertext[$i] ^
$this->_encryptBlock($iv);
$iv = substr($iv, 1) . $ciphertext[$i];
}
if ($this->continuousBuffer) {
if ($len >= $block_size) {
$this->decryptIV = substr($ciphertext,
-$block_size);
} else {
$this->decryptIV = substr($this->decryptIV,
$len - $block_size) . substr($ciphertext, -$len);
}
}
break;
case self::MODE_OFB:
$xor = $this->decryptIV;
if (strlen($buffer['xor'])) {
for ($i = 0; $i < strlen($ciphertext);
$i+=$block_size) {
$block = substr($ciphertext, $i, $block_size);
if (strlen($block) >
strlen($buffer['xor'])) {
$xor = $this->_encryptBlock($xor);
$buffer['xor'].= $xor;
}
$key =
$this->_string_shift($buffer['xor'], $block_size);
$plaintext.= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($ciphertext);
$i+=$block_size) {
$xor = $this->_encryptBlock($xor);
$plaintext.= substr($ciphertext, $i, $block_size) ^
$xor;
}
$key = $xor;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
if ($start = strlen($ciphertext) % $block_size) {
$buffer['xor'] = substr($key, $start) .
$buffer['xor'];
}
}
break;
case self::MODE_STREAM:
$plaintext = $this->_decryptBlock($ciphertext);
break;
}
return $this->paddable ? $this->_unpad($plaintext) :
$plaintext;
}
/**
* OpenSSL CTR Processor
*
* PHP's OpenSSL bindings do not operate in continuous mode so
we'll wrap around it. Since the keystream
* for CTR is the same for both encrypting and decrypting this function
is re-used by both Base::encrypt()
* and Base::decrypt(). Also, OpenSSL doesn't implement CTR for
all of it's symmetric ciphers so this
* function will emulate CTR with ECB when necessary.
*
* @see self::encrypt()
* @see self::decrypt()
* @param string $plaintext
* @param string $encryptIV
* @param array $buffer
* @return string
* @access private
*/
function _openssl_ctr_process($plaintext, &$encryptIV,
&$buffer)
{
$ciphertext = '';
$block_size = $this->block_size;
$key = $this->key;
if ($this->openssl_emulate_ctr) {
$xor = $encryptIV;
if (strlen($buffer['ciphertext'])) {
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
if (strlen($block) >
strlen($buffer['ciphertext'])) {
$result = @openssl_encrypt($xor,
$this->cipher_name_openssl_ecb, $key, $this->openssl_options);
$result = !defined('OPENSSL_RAW_DATA') ?
substr($result, 0, -$this->block_size) : $result;
$buffer['ciphertext'].= $result;
}
$this->_increment_str($xor);
$otp =
$this->_string_shift($buffer['ciphertext'], $block_size);
$ciphertext.= $block ^ $otp;
}
} else {
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
$otp = @openssl_encrypt($xor,
$this->cipher_name_openssl_ecb, $key, $this->openssl_options);
$otp = !defined('OPENSSL_RAW_DATA') ?
substr($otp, 0, -$this->block_size) : $otp;
$this->_increment_str($xor);
$ciphertext.= $block ^ $otp;
}
}
if ($this->continuousBuffer) {
$encryptIV = $xor;
if ($start = strlen($plaintext) % $block_size) {
$buffer['ciphertext'] = substr($key, $start)
. $buffer['ciphertext'];
}
}
return $ciphertext;
}
if (strlen($buffer['ciphertext'])) {
$ciphertext = $plaintext ^
$this->_string_shift($buffer['ciphertext'],
strlen($plaintext));
$plaintext = substr($plaintext, strlen($ciphertext));
if (!strlen($plaintext)) {
return $ciphertext;
}
}
$overflow = strlen($plaintext) % $block_size;
if ($overflow) {
$plaintext2 = $this->_string_pop($plaintext, $overflow); //
ie. trim $plaintext to a multiple of $block_size and put rest of $plaintext
in $plaintext2
$encrypted = openssl_encrypt($plaintext .
str_repeat("\0", $block_size), $this->cipher_name_openssl,
$key, $this->openssl_options, $encryptIV);
$temp = $this->_string_pop($encrypted, $block_size);
$ciphertext.= $encrypted . ($plaintext2 ^ $temp);
if ($this->continuousBuffer) {
$buffer['ciphertext'] = substr($temp, $overflow);
$encryptIV = $temp;
}
} elseif (!strlen($buffer['ciphertext'])) {
$ciphertext.= openssl_encrypt($plaintext .
str_repeat("\0", $block_size), $this->cipher_name_openssl,
$key, $this->openssl_options, $encryptIV);
$temp = $this->_string_pop($ciphertext, $block_size);
if ($this->continuousBuffer) {
$encryptIV = $temp;
}
}
if ($this->continuousBuffer) {
if (!defined('OPENSSL_RAW_DATA')) {
$encryptIV.= @openssl_encrypt('',
$this->cipher_name_openssl_ecb, $key, $this->openssl_options);
}
$encryptIV = openssl_decrypt($encryptIV,
$this->cipher_name_openssl_ecb, $key, $this->openssl_options);
if ($overflow) {
$this->_increment_str($encryptIV);
}
}
return $ciphertext;
}
/**
* OpenSSL OFB Processor
*
* PHP's OpenSSL bindings do not operate in continuous mode so
we'll wrap around it. Since the keystream
* for OFB is the same for both encrypting and decrypting this function
is re-used by both Base::encrypt()
* and Base::decrypt().
*
* @see self::encrypt()
* @see self::decrypt()
* @param string $plaintext
* @param string $encryptIV
* @param array $buffer
* @return string
* @access private
*/
function _openssl_ofb_process($plaintext, &$encryptIV,
&$buffer)
{
if (strlen($buffer['xor'])) {
$ciphertext = $plaintext ^ $buffer['xor'];
$buffer['xor'] = substr($buffer['xor'],
strlen($ciphertext));
$plaintext = substr($plaintext, strlen($ciphertext));
} else {
$ciphertext = '';
}
$block_size = $this->block_size;
$len = strlen($plaintext);
$key = $this->key;
$overflow = $len % $block_size;
if (strlen($plaintext)) {
if ($overflow) {
$ciphertext.= openssl_encrypt(substr($plaintext, 0,
-$overflow) . str_repeat("\0", $block_size),
$this->cipher_name_openssl, $key, $this->openssl_options,
$encryptIV);
$xor = $this->_string_pop($ciphertext, $block_size);
if ($this->continuousBuffer) {
$encryptIV = $xor;
}
$ciphertext.= $this->_string_shift($xor, $overflow) ^
substr($plaintext, -$overflow);
if ($this->continuousBuffer) {
$buffer['xor'] = $xor;
}
} else {
$ciphertext = openssl_encrypt($plaintext,
$this->cipher_name_openssl, $key, $this->openssl_options,
$encryptIV);
if ($this->continuousBuffer) {
$encryptIV = substr($ciphertext, -$block_size) ^
substr($plaintext, -$block_size);
}
}
}
return $ciphertext;
}
/**
* phpseclib <-> OpenSSL Mode Mapper
*
* May need to be overwritten by classes extending this one in some
cases
*
* @return int
* @access private
*/
function _openssl_translate_mode()
{
switch ($this->mode) {
case self::MODE_ECB:
return 'ecb';
case self::MODE_CBC:
return 'cbc';
case self::MODE_CTR:
return 'ctr';
case self::MODE_CFB:
return 'cfb';
case self::MODE_CFB8:
return 'cfb8';
case self::MODE_OFB:
return 'ofb';
}
}
/**
* Pad "packets".
*
* Block ciphers working by encrypting between their specified
[$this->]block_size at a time
* If you ever need to encrypt or decrypt something that isn't of
the proper length, it becomes necessary to
* pad the input so that it is of the proper length.
*
* Padding is enabled by default. Sometimes, however, it is
undesirable to pad strings. Such is the case in SSH,
* where "packets" are padded with random bytes before being
encrypted. Unpad these packets and you risk stripping
* away characters that shouldn't be stripped away. (SSH knows how
many bytes are added because the length is
* transmitted separately)
*
* @see self::disablePadding()
* @access public
*/
function enablePadding()
{
$this->padding = true;
}
/**
* Do not pad packets.
*
* @see self::enablePadding()
* @access public
*/
function disablePadding()
{
$this->padding = false;
}
/**
* Treat consecutive "packets" as if they are a continuous
buffer.
*
* Say you have a 32-byte plaintext $plaintext. Using the default
behavior, the two following code snippets
* will yield different outputs:
*
* <code>
* echo $rijndael->encrypt(substr($plaintext, 0, 16));
* echo $rijndael->encrypt(substr($plaintext, 16, 16));
* </code>
* <code>
* echo $rijndael->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>
* $rijndael->encrypt(substr($plaintext, 0, 16));
* echo
$rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16)));
* </code>
* <code>
* echo
$rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16)));
* </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 \phpseclib\Crypt\*() 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 self::disableContinuousBuffer()
* @access public
* @internal Could, but not must, extend by the child Crypt_* class
*/
function enableContinuousBuffer()
{
if ($this->mode == self::MODE_ECB) {
return;
}
$this->continuousBuffer = true;
$this->_setEngine();
}
/**
* Treat consecutive packets as if they are a discontinuous buffer.
*
* The default behavior.
*
* @see self::enableContinuousBuffer()
* @access public
* @internal Could, but not must, extend by the child Crypt_* class
*/
function disableContinuousBuffer()
{
if ($this->mode == self::MODE_ECB) {
return;
}
if (!$this->continuousBuffer) {
return;
}
$this->continuousBuffer = false;
$this->changed = true;
$this->_setEngine();
}
/**
* Test for engine validity
*
* @see self::__construct()
* @param int $engine
* @access public
* @return bool
*/
function isValidEngine($engine)
{
switch ($engine) {
case self::ENGINE_OPENSSL:
if ($this->mode == self::MODE_STREAM &&
$this->continuousBuffer) {
return false;
}
$this->openssl_emulate_ctr = false;
$result = $this->cipher_name_openssl &&
extension_loaded('openssl') &&
// PHP 5.3.0 - 5.3.2 did not let you set
IV's
version_compare(PHP_VERSION, '5.3.3',
'>=');
if (!$result) {
return false;
}
// prior to PHP 5.4.0 OPENSSL_RAW_DATA and
OPENSSL_ZERO_PADDING were not defined. instead of expecting an integer
// $options openssl_encrypt expected a boolean $raw_data.
if (!defined('OPENSSL_RAW_DATA')) {
$this->openssl_options = true;
} else {
$this->openssl_options = OPENSSL_RAW_DATA |
OPENSSL_ZERO_PADDING;
}
$methods = openssl_get_cipher_methods();
if (in_array($this->cipher_name_openssl, $methods)) {
return true;
}
// not all of openssl's symmetric cipher's
support ctr. for those
// that don't we'll emulate it
switch ($this->mode) {
case self::MODE_CTR:
if (in_array($this->cipher_name_openssl_ecb,
$methods)) {
$this->openssl_emulate_ctr = true;
return true;
}
}
return false;
case self::ENGINE_MCRYPT:
set_error_handler(array($this, 'do_nothing'));
$result = $this->cipher_name_mcrypt &&
extension_loaded('mcrypt') &&
in_array($this->cipher_name_mcrypt,
mcrypt_list_algorithms());
restore_error_handler();
return $result;
case self::ENGINE_INTERNAL:
return true;
}
return false;
}
/**
* Sets the preferred crypt engine
*
* Currently, $engine could be:
*
* - \phpseclib\Crypt\Base::ENGINE_OPENSSL [very fast]
*
* - \phpseclib\Crypt\Base::ENGINE_MCRYPT [fast]
*
* - \phpseclib\Crypt\Base::ENGINE_INTERNAL [slow]
*
* If the preferred crypt engine is not available the fastest available
one will be used
*
* @see self::__construct()
* @param int $engine
* @access public
*/
function setPreferredEngine($engine)
{
switch ($engine) {
//case self::ENGINE_OPENSSL;
case self::ENGINE_MCRYPT:
case self::ENGINE_INTERNAL:
$this->preferredEngine = $engine;
break;
default:
$this->preferredEngine = self::ENGINE_OPENSSL;
}
$this->_setEngine();
}
/**
* Returns the engine currently being utilized
*
* @see self::_setEngine()
* @access public
*/
function getEngine()
{
return $this->engine;
}
/**
* Sets the engine as appropriate
*
* @see self::__construct()
* @access private
*/
function _setEngine()
{
$this->engine = null;
$candidateEngines = array(
$this->preferredEngine,
self::ENGINE_OPENSSL,
self::ENGINE_MCRYPT
);
foreach ($candidateEngines as $engine) {
if ($this->isValidEngine($engine)) {
$this->engine = $engine;
break;
}
}
if (!$this->engine) {
$this->engine = self::ENGINE_INTERNAL;
}
if ($this->engine != self::ENGINE_MCRYPT &&
$this->enmcrypt) {
set_error_handler(array($this, 'do_nothing'));
// Closing the current mcrypt resource(s). _mcryptSetup() will,
if needed,
// (re)open them with the module named in
$this->cipher_name_mcrypt
mcrypt_module_close($this->enmcrypt);
mcrypt_module_close($this->demcrypt);
$this->enmcrypt = null;
$this->demcrypt = null;
if ($this->ecb) {
mcrypt_module_close($this->ecb);
$this->ecb = null;
}
restore_error_handler();
}
$this->changed = true;
}
/**
* Encrypts a block
*
* Note: Must be extended by the child \phpseclib\Crypt\* class
*
* @access private
* @param string $in
* @return string
*/
abstract function _encryptBlock($in);
/**
* Decrypts a block
*
* Note: Must be extended by the child \phpseclib\Crypt\* class
*
* @access private
* @param string $in
* @return string
*/
abstract function _decryptBlock($in);
/**
* Setup the key (expansion)
*
* Only used if $engine == self::ENGINE_INTERNAL
*
* Note: Must extend by the child \phpseclib\Crypt\* class
*
* @see self::_setup()
* @access private
*/
abstract function _setupKey();
/**
* Setup the self::ENGINE_INTERNAL $engine
*
* (re)init, if necessary, the internal cipher $engine and flush all
$buffers
* Used (only) if $engine == self::ENGINE_INTERNAL
*
* _setup() will be called each time if $changed === true
* typically this happens when using one or more of following public
methods:
*
* - setKey()
*
* - setIV()
*
* - disableContinuousBuffer()
*
* - First run of encrypt() / decrypt() with no init-settings
*
* @see self::setKey()
* @see self::setIV()
* @see self::disableContinuousBuffer()
* @access private
* @internal _setup() is always called before en/decryption.
* @internal Could, but not must, extend by the child Crypt_* class
*/
function _setup()
{
$this->_clearBuffers();
$this->_setupKey();
if ($this->use_inline_crypt) {
$this->_setupInlineCrypt();
}
}
/**
* Setup the self::ENGINE_MCRYPT $engine
*
* (re)init, if necessary, the (ext)mcrypt resources and flush all
$buffers
* Used (only) if $engine = self::ENGINE_MCRYPT
*
* _setupMcrypt() will be called each time if $changed === true
* typically this happens when using one or more of following public
methods:
*
* - setKey()
*
* - setIV()
*
* - disableContinuousBuffer()
*
* - First run of encrypt() / decrypt()
*
* @see self::setKey()
* @see self::setIV()
* @see self::disableContinuousBuffer()
* @access private
* @internal Could, but not must, extend by the child Crypt_* class
*/
function _setupMcrypt()
{
$this->_clearBuffers();
$this->enchanged = $this->dechanged = true;
if (!isset($this->enmcrypt)) {
static $mcrypt_modes = array(
self::MODE_CTR => 'ctr',
self::MODE_ECB => MCRYPT_MODE_ECB,
self::MODE_CBC => MCRYPT_MODE_CBC,
self::MODE_CFB => 'ncfb',
self::MODE_CFB8 => MCRYPT_MODE_CFB,
self::MODE_OFB => MCRYPT_MODE_NOFB,
self::MODE_STREAM => MCRYPT_MODE_STREAM,
);
$this->demcrypt =
mcrypt_module_open($this->cipher_name_mcrypt, '',
$mcrypt_modes[$this->mode], '');
$this->enmcrypt =
mcrypt_module_open($this->cipher_name_mcrypt, '',
$mcrypt_modes[$this->mode], '');
// we need the $ecb mcrypt resource (only) in MODE_CFB with
enableContinuousBuffer()
// to workaround mcrypt's broken ncfb implementation in
buffered mode
// see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps}
if ($this->mode == self::MODE_CFB) {
$this->ecb =
mcrypt_module_open($this->cipher_name_mcrypt, '',
MCRYPT_MODE_ECB, '');
}
} // else should mcrypt_generic_deinit be called?
if ($this->mode == self::MODE_CFB) {
mcrypt_generic_init($this->ecb, $this->key,
str_repeat("\0", $this->block_size));
}
}
/**
* Pads a string
*
* Pads a string using the RSA PKCS padding standards so that its
length is a multiple of the blocksize.
* $this->block_size - (strlen($text) % $this->block_size) bytes
are added, each of which is equal to
* chr($this->block_size - (strlen($text) % $this->block_size)
*
* If padding is disabled and $text is not a multiple of the blocksize,
the string will be padded regardless
* and padding will, hence forth, be enabled.
*
* @see self::_unpad()
* @param string $text
* @access private
* @return string
*/
function _pad($text)
{
$length = strlen($text);
if (!$this->padding) {
if ($length % $this->block_size == 0) {
return $text;
} else {
user_error("The plaintext's length ($length) is
not a multiple of the block size ({$this->block_size})");
$this->padding = true;
}
}
$pad = $this->block_size - ($length % $this->block_size);
return str_pad($text, $length + $pad, chr($pad));
}
/**
* Unpads a string.
*
* If padding is enabled and the reported padding length is invalid the
encryption key will be assumed to be wrong
* and false will be returned.
*
* @see self::_pad()
* @param string $text
* @access private
* @return string
*/
function _unpad($text)
{
if (!$this->padding) {
return $text;
}
$length = ord($text[strlen($text) - 1]);
if (!$length || $length > $this->block_size) {
return false;
}
return substr($text, 0, -$length);
}
/**
* Clears internal buffers
*
* Clearing/resetting the internal buffers is done everytime
* after disableContinuousBuffer() or on cipher $engine (re)init
* ie after setKey() or setIV()
*
* @access public
* @internal Could, but not must, extend by the child Crypt_* class
*/
function _clearBuffers()
{
$this->enbuffer = $this->debuffer =
array('ciphertext' => '', 'xor' =>
'', 'pos' => 0, 'enmcrypt_init' =>
true);
// mcrypt's handling of invalid's $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");
}
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @access private
* @return string
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
/**
* String Pop
*
* Inspired by array_pop
*
* @param string $string
* @param int $index
* @access private
* @return string
*/
function _string_pop(&$string, $index = 1)
{
$substr = substr($string, -$index);
$string = substr($string, 0, -$index);
return $substr;
}
/**
* Increment the current string
*
* @see self::decrypt()
* @see self::encrypt()
* @param string $var
* @access private
*/
function _increment_str(&$var)
{
for ($i = 4; $i <= strlen($var); $i+= 4) {
$temp = substr($var, -$i, 4);
switch ($temp) {
case "\xFF\xFF\xFF\xFF":
$var = substr_replace($var,
"\x00\x00\x00\x00", -$i, 4);
break;
case "\x7F\xFF\xFF\xFF":
$var = substr_replace($var,
"\x80\x00\x00\x00", -$i, 4);
return;
default:
$temp = unpack('Nnum', $temp);
$var = substr_replace($var, pack('N',
$temp['num'] + 1), -$i, 4);
return;
}
}
$remainder = strlen($var) % 4;
if ($remainder == 0) {
return;
}
$temp = unpack('Nnum', str_pad(substr($var, 0,
$remainder), 4, "\0", STR_PAD_LEFT));
$temp = substr(pack('N', $temp['num'] + 1),
-$remainder);
$var = substr_replace($var, $temp, 0, $remainder);
}
/**
* Setup the performance-optimized function for de/encrypt()
*
* Stores the created (or existing) callback function-name
* in $this->inline_crypt
*
* Internally for phpseclib developers:
*
* _setupInlineCrypt() would be called only if:
*
* - $engine == self::ENGINE_INTERNAL and
*
* - $use_inline_crypt === true
*
* - each time on _setup(), after(!) _setupKey()
*
*
* This ensures that _setupInlineCrypt() has always a
* full ready2go initializated internal cipher $engine state
* where, for example, the keys allready expanded,
* keys/block_size calculated and such.
*
* It is, each time if called, the responsibility of
_setupInlineCrypt():
*
* - to set $this->inline_crypt to a valid and fully working
callback function
* as a (faster) replacement for encrypt() / decrypt()
*
* - NOT to create unlimited callback functions (for memory
reasons!)
* no matter how often _setupInlineCrypt() would be called. At
some
* point of amount they must be generic re-useable.
*
* - the code of _setupInlineCrypt() it self,
* and the generated callback code,
* must be, in following order:
* - 100% safe
* - 100% compatible to encrypt()/decrypt()
* - using only php5+ features/lang-constructs/php-extensions if
* compatibility (down to php4) or fallback is provided
* - readable/maintainable/understandable/commented and...
not-cryptic-styled-code :-)
* - >= 10% faster than encrypt()/decrypt() [which is, by the
way,
* the reason for the existence of _setupInlineCrypt() :-)]
* - memory-nice
* - short (as good as possible)
*
* Note: - _setupInlineCrypt() is using _createInlineCryptFunction() to
create the full callback function code.
* - In case of using inline crypting, _setupInlineCrypt() must
extend by the child \phpseclib\Crypt\* class.
* - The following variable names are reserved:
* - $_* (all variable names prefixed with an underscore)
* - $self (object reference to it self. Do not use $this, but
$self instead)
* - $in (the content of $in has to en/decrypt by the generated
code)
* - The callback function should not use the 'return'
statement, but en/decrypt'ing the content of $in only
*
*
* @see self::_setup()
* @see self::_createInlineCryptFunction()
* @see self::encrypt()
* @see self::decrypt()
* @access private
* @internal If a Crypt_* class providing inline crypting it must
extend _setupInlineCrypt()
*/
function _setupInlineCrypt()
{
// If, for any reason, an extending \phpseclib\Crypt\Base()
\phpseclib\Crypt\* class
// not using inline crypting then it must be ensured that:
$this->use_inline_crypt = false
// ie in the class var declaration of $use_inline_crypt in general
for the \phpseclib\Crypt\* class,
// in the constructor at object instance-time
// or, if it's runtime-specific, at runtime
$this->use_inline_crypt = false;
}
/**
* Creates the performance-optimized function for en/decrypt()
*
* Internally for phpseclib developers:
*
* _createInlineCryptFunction():
*
* - merge the $cipher_code [setup'ed by _setupInlineCrypt()]
* with the current [$this->]mode of operation code
*
* - create the $inline function, which called by encrypt() /
decrypt()
* as its replacement to speed up the en/decryption operations.
*
* - return the name of the created $inline callback function
*
* - used to speed up en/decryption
*
*
*
* The main reason why can speed up things [up to 50%] this way are:
*
* - using variables more effective then regular.
* (ie no use of expensive arrays but integers $k_0, $k_1 ...
* or even, for example, the pure $key[] values hardcoded)
*
* - avoiding 1000's of function calls of ie _encryptBlock()
* but inlining the crypt operations.
* in the mode of operation for() loop.
*
* - full loop unroll the (sometimes key-dependent) rounds
* avoiding this way ++$i counters and runtime-if's etc...
*
* The basic code architectur of the generated $inline en/decrypt()
* lambda function, in pseudo php, is:
*
* <code>
*
+----------------------------------------------------------------------------------------------+
* | callback $inline = create_function:
|
* | lambda_function_0001_crypt_ECB($action, $text)
|
* | {
|
* | INSERT PHP CODE OF:
|
* | $cipher_code['init_crypt']; //
general init code. |
* | // ie:
$sbox'es declarations used for |
* | // encrypt
and decrypt'ing. |
* |
|
* | switch ($action) {
|
* | case 'encrypt':
|
* | INSERT PHP CODE OF:
|
* | $cipher_code['init_encrypt']; //
encrypt sepcific init code. |
* | ie:
specified $key or $box |
* |
declarations for encrypt'ing. |
* |
|
* | foreach ($ciphertext) {
|
* | $in = $block_size of $ciphertext;
|
* |
|
* | INSERT PHP CODE OF:
|
* | $cipher_code['encrypt_block']; //
encrypt's (string) $in, which is always: |
* | // strlen($in)
== $this->block_size |
* | // here comes
the cipher algorithm in action |
* | // for
encryption. |
* | //
$cipher_code['encrypt_block'] has to |
* | // encrypt the
content of the $in variable |
* |
|
* | $plaintext .= $in;
|
* | }
|
* | return $plaintext;
|
* |
|
* | case 'decrypt':
|
* | INSERT PHP CODE OF:
|
* | $cipher_code['init_decrypt']; //
decrypt sepcific init code |
* | ie:
specified $key or $box |
* |
declarations for decrypt'ing. |
* | foreach ($plaintext) {
|
* | $in = $block_size of $plaintext;
|
* |
|
* | INSERT PHP CODE OF:
|
* | $cipher_code['decrypt_block']; //
decrypt's (string) $in, which is always |
* | // strlen($in)
== $this->block_size |
* | // here comes
the cipher algorithm in action |
* | // for
decryption. |
* | //
$cipher_code['decrypt_block'] has to |
* | // decrypt the
content of the $in variable |
* | $ciphertext .= $in;
|
* | }
|
* | return $ciphertext;
|
* | }
|
* | }
|
*
+----------------------------------------------------------------------------------------------+
* </code>
*
* See also the \phpseclib\Crypt\*::_setupInlineCrypt()'s for
* productive inline $cipher_code's how they works.
*
* Structure of:
* <code>
* $cipher_code = array(
* 'init_crypt' => (string) '', //
optional
* 'init_encrypt' => (string) '', //
optional
* 'init_decrypt' => (string) '', //
optional
* 'encrypt_block' => (string) '', //
required
* 'decrypt_block' => (string) '' //
required
* );
* </code>
*
* @see self::_setupInlineCrypt()
* @see self::encrypt()
* @see self::decrypt()
* @param array $cipher_code
* @access private
* @return string (the name of the created callback function)
*/
function _createInlineCryptFunction($cipher_code)
{
$block_size = $this->block_size;
// optional
$init_crypt = isset($cipher_code['init_crypt']) ?
$cipher_code['init_crypt'] : '';
$init_encrypt = isset($cipher_code['init_encrypt']) ?
$cipher_code['init_encrypt'] : '';
$init_decrypt = isset($cipher_code['init_decrypt']) ?
$cipher_code['init_decrypt'] : '';
// required
$encrypt_block = $cipher_code['encrypt_block'];
$decrypt_block = $cipher_code['decrypt_block'];
// Generating mode of operation inline code,
// merged with the $cipher_code algorithm
// for encrypt- and decryption.
switch ($this->mode) {
case self::MODE_ECB:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_plaintext_len = strlen($_text);
for ($_i = 0; $_i < $_plaintext_len; $_i+=
'.$block_size.') {
$in = substr($_text, $_i,
'.$block_size.');
'.$encrypt_block.'
$_ciphertext.= $in;
}
return $_ciphertext;
';
$decrypt = $init_decrypt . '
$_plaintext = "";
$_text = str_pad($_text, strlen($_text) +
('.$block_size.' - strlen($_text) % '.$block_size.') %
'.$block_size.', chr(0));
$_ciphertext_len = strlen($_text);
for ($_i = 0; $_i < $_ciphertext_len; $_i+=
'.$block_size.') {
$in = substr($_text, $_i,
'.$block_size.');
'.$decrypt_block.'
$_plaintext.= $in;
}
return $self->_unpad($_plaintext);
';
break;
case self::MODE_CTR:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_plaintext_len = strlen($_text);
$_xor = $self->encryptIV;
$_buffer = &$self->enbuffer;
if (strlen($_buffer["ciphertext"])) {
for ($_i = 0; $_i < $_plaintext_len; $_i+=
'.$block_size.') {
$_block = substr($_text, $_i,
'.$block_size.');
if (strlen($_block) >
strlen($_buffer["ciphertext"])) {
$in = $_xor;
'.$encrypt_block.'
$self->_increment_str($_xor);
$_buffer["ciphertext"].= $in;
}
$_key =
$self->_string_shift($_buffer["ciphertext"],
'.$block_size.');
$_ciphertext.= $_block ^ $_key;
}
} else {
for ($_i = 0; $_i < $_plaintext_len; $_i+=
'.$block_size.') {
$_block = substr($_text, $_i,
'.$block_size.');
$in = $_xor;
'.$encrypt_block.'
$self->_increment_str($_xor);
$_key = $in;
$_ciphertext.= $_block ^ $_key;
}
}
if ($self->continuousBuffer) {
$self->encryptIV = $_xor;
if ($_start = $_plaintext_len %
'.$block_size.') {
$_buffer["ciphertext"] =
substr($_key, $_start) . $_buffer["ciphertext"];
}
}
return $_ciphertext;
';
$decrypt = $init_encrypt . '
$_plaintext = "";
$_ciphertext_len = strlen($_text);
$_xor = $self->decryptIV;
$_buffer = &$self->debuffer;
if (strlen($_buffer["ciphertext"])) {
for ($_i = 0; $_i < $_ciphertext_len; $_i+=
'.$block_size.') {
$_block = substr($_text, $_i,
'.$block_size.');
if (strlen($_block) >
strlen($_buffer["ciphertext"])) {
$in = $_xor;
'.$encrypt_block.'
$self->_increment_str($_xor);
$_buffer["ciphertext"].= $in;
}
$_key =
$self->_string_shift($_buffer["ciphertext"],
'.$block_size.');
$_plaintext.= $_block ^ $_key;
}
} else {
for ($_i = 0; $_i < $_ciphertext_len; $_i+=
'.$block_size.') {
$_block = substr($_text, $_i,
'.$block_size.');
$in = $_xor;
'.$encrypt_block.'
$self->_increment_str($_xor);
$_key = $in;
$_plaintext.= $_block ^ $_key;
}
}
if ($self->continuousBuffer) {
$self->decryptIV = $_xor;
if ($_start = $_ciphertext_len %
'.$block_size.') {
$_buffer["ciphertext"] =
substr($_key, $_start) . $_buffer["ciphertext"];
}
}
return $_plaintext;
';
break;
case self::MODE_CFB:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_buffer = &$self->enbuffer;
if ($self->continuousBuffer) {
$_iv = &$self->encryptIV;
$_pos = &$_buffer["pos"];
} else {
$_iv = $self->encryptIV;
$_pos = 0;
}
$_len = strlen($_text);
$_i = 0;
if ($_pos) {
$_orig_pos = $_pos;
$_max = '.$block_size.' - $_pos;
if ($_len >= $_max) {
$_i = $_max;
$_len-= $_max;
$_pos = 0;
} else {
$_i = $_len;
$_pos+= $_len;
$_len = 0;
}
$_ciphertext = substr($_iv, $_orig_pos) ^ $_text;
$_iv = substr_replace($_iv, $_ciphertext,
$_orig_pos, $_i);
}
while ($_len >= '.$block_size.') {
$in = $_iv;
'.$encrypt_block.';
$_iv = $in ^ substr($_text, $_i,
'.$block_size.');
$_ciphertext.= $_iv;
$_len-= '.$block_size.';
$_i+= '.$block_size.';
}
if ($_len) {
$in = $_iv;
'.$encrypt_block.'
$_iv = $in;
$_block = $_iv ^ substr($_text, $_i);
$_iv = substr_replace($_iv, $_block, 0, $_len);
$_ciphertext.= $_block;
$_pos = $_len;
}
return $_ciphertext;
';
$decrypt = $init_encrypt . '
$_plaintext = "";
$_buffer = &$self->debuffer;
if ($self->continuousBuffer) {
$_iv = &$self->decryptIV;
$_pos = &$_buffer["pos"];
} else {
$_iv = $self->decryptIV;
$_pos = 0;
}
$_len = strlen($_text);
$_i = 0;
if ($_pos) {
$_orig_pos = $_pos;
$_max = '.$block_size.' - $_pos;
if ($_len >= $_max) {
$_i = $_max;
$_len-= $_max;
$_pos = 0;
} else {
$_i = $_len;
$_pos+= $_len;
$_len = 0;
}
$_plaintext = substr($_iv, $_orig_pos) ^ $_text;
$_iv = substr_replace($_iv, substr($_text, 0, $_i),
$_orig_pos, $_i);
}
while ($_len >= '.$block_size.') {
$in = $_iv;
'.$encrypt_block.'
$_iv = $in;
$cb = substr($_text, $_i,
'.$block_size.');
$_plaintext.= $_iv ^ $cb;
$_iv = $cb;
$_len-= '.$block_size.';
$_i+= '.$block_size.';
}
if ($_len) {
$in = $_iv;
'.$encrypt_block.'
$_iv = $in;
$_plaintext.= $_iv ^ substr($_text, $_i);
$_iv = substr_replace($_iv, substr($_text, $_i), 0,
$_len);
$_pos = $_len;
}
return $_plaintext;
';
break;
case self::MODE_CFB8:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_len = strlen($_text);
$_iv = $self->encryptIV;
for ($_i = 0; $_i < $_len; ++$_i) {
$in = $_iv;
'.$encrypt_block.'
$_ciphertext .= ($_c = $_text[$_i] ^ $in);
$_iv = substr($_iv, 1) . $_c;
}
if ($self->continuousBuffer) {
if ($_len >= '.$block_size.') {
$self->encryptIV = substr($_ciphertext,
-'.$block_size.');
} else {
$self->encryptIV =
substr($self->encryptIV, $_len - '.$block_size.') .
substr($_ciphertext, -$_len);
}
}
return $_ciphertext;
';
$decrypt = $init_encrypt . '
$_plaintext = "";
$_len = strlen($_text);
$_iv = $self->decryptIV;
for ($_i = 0; $_i < $_len; ++$_i) {
$in = $_iv;
'.$encrypt_block.'
$_plaintext .= $_text[$_i] ^ $in;
$_iv = substr($_iv, 1) . $_text[$_i];
}
if ($self->continuousBuffer) {
if ($_len >= '.$block_size.') {
$self->decryptIV = substr($_text,
-'.$block_size.');
} else {
$self->decryptIV =
substr($self->decryptIV, $_len - '.$block_size.') .
substr($_text, -$_len);
}
}
return $_plaintext;
';
break;
case self::MODE_OFB:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_plaintext_len = strlen($_text);
$_xor = $self->encryptIV;
$_buffer = &$self->enbuffer;
if (strlen($_buffer["xor"])) {
for ($_i = 0; $_i < $_plaintext_len; $_i+=
'.$block_size.') {
$_block = substr($_text, $_i,
'.$block_size.');
if (strlen($_block) >
strlen($_buffer["xor"])) {
$in = $_xor;
'.$encrypt_block.'
$_xor = $in;
$_buffer["xor"].= $_xor;
}
$_key =
$self->_string_shift($_buffer["xor"],
'.$block_size.');
$_ciphertext.= $_block ^ $_key;
}
} else {
for ($_i = 0; $_i < $_plaintext_len; $_i+=
'.$block_size.') {
$in = $_xor;
'.$encrypt_block.'
$_xor = $in;
$_ciphertext.= substr($_text, $_i,
'.$block_size.') ^ $_xor;
}
$_key = $_xor;
}
if ($self->continuousBuffer) {
$self->encryptIV = $_xor;
if ($_start = $_plaintext_len %
'.$block_size.') {
$_buffer["xor"] = substr($_key,
$_start) . $_buffer["xor"];
}
}
return $_ciphertext;
';
$decrypt = $init_encrypt . '
$_plaintext = "";
$_ciphertext_len = strlen($_text);
$_xor = $self->decryptIV;
$_buffer = &$self->debuffer;
if (strlen($_buffer["xor"])) {
for ($_i = 0; $_i < $_ciphertext_len; $_i+=
'.$block_size.') {
$_block = substr($_text, $_i,
'.$block_size.');
if (strlen($_block) >
strlen($_buffer["xor"])) {
$in = $_xor;
'.$encrypt_block.'
$_xor = $in;
$_buffer["xor"].= $_xor;
}
$_key =
$self->_string_shift($_buffer["xor"],
'.$block_size.');
$_plaintext.= $_block ^ $_key;
}
} else {
for ($_i = 0; $_i < $_ciphertext_len; $_i+=
'.$block_size.') {
$in = $_xor;
'.$encrypt_block.'
$_xor = $in;
$_plaintext.= substr($_text, $_i,
'.$block_size.') ^ $_xor;
}
$_key = $_xor;
}
if ($self->continuousBuffer) {
$self->decryptIV = $_xor;
if ($_start = $_ciphertext_len %
'.$block_size.') {
$_buffer["xor"] = substr($_key,
$_start) . $_buffer["xor"];
}
}
return $_plaintext;
';
break;
case self::MODE_STREAM:
$encrypt = $init_encrypt . '
$_ciphertext = "";
'.$encrypt_block.'
return $_ciphertext;
';
$decrypt = $init_decrypt . '
$_plaintext = "";
'.$decrypt_block.'
return $_plaintext;
';
break;
// case self::MODE_CBC:
default:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_plaintext_len = strlen($_text);
$in = $self->encryptIV;
for ($_i = 0; $_i < $_plaintext_len; $_i+=
'.$block_size.') {
$in = substr($_text, $_i,
'.$block_size.') ^ $in;
'.$encrypt_block.'
$_ciphertext.= $in;
}
if ($self->continuousBuffer) {
$self->encryptIV = $in;
}
return $_ciphertext;
';
$decrypt = $init_decrypt . '
$_plaintext = "";
$_text = str_pad($_text, strlen($_text) +
('.$block_size.' - strlen($_text) % '.$block_size.') %
'.$block_size.', chr(0));
$_ciphertext_len = strlen($_text);
$_iv = $self->decryptIV;
for ($_i = 0; $_i < $_ciphertext_len; $_i+=
'.$block_size.') {
$in = $_block = substr($_text, $_i,
'.$block_size.');
'.$decrypt_block.'
$_plaintext.= $in ^ $_iv;
$_iv = $_block;
}
if ($self->continuousBuffer) {
$self->decryptIV = $_iv;
}
return $self->_unpad($_plaintext);
';
break;
}
// Create the $inline function and return its name as string. Ready
to run!
eval('$func = function ($_action, &$self, $_text) { '
. $init_crypt . 'if ($_action == "encrypt") { ' .
$encrypt . ' } else { ' . $decrypt . ' } };');
return $func;
}
/**
* Holds the lambda_functions table (classwide)
*
* Each name of the lambda function, created from
* _setupInlineCrypt() && _createInlineCryptFunction()
* is stored, classwide (!), here for reusing.
*
* The string-based index of $function is a classwide
* unique value representing, at least, the $mode of
* operation (or more... depends of the optimizing level)
* for which $mode the lambda function was created.
*
* @access private
* @return array &$functions
*/
function &_getLambdaFunctions()
{
static $functions = array();
return $functions;
}
/**
* Generates a digest from $bytes
*
* @see self::_setupInlineCrypt()
* @access private
* @param string $bytes
* @return string
*/
function _hashInlineCryptFunction($bytes)
{
if (!isset(self::$WHIRLPOOL_AVAILABLE)) {
self::$WHIRLPOOL_AVAILABLE = extension_loaded('hash')
&& in_array('whirlpool', hash_algos());
}
$result = '';
$hash = $bytes;
switch (true) {
case self::$WHIRLPOOL_AVAILABLE:
foreach (str_split($bytes, 64) as $t) {
$hash = hash('whirlpool', $hash, true);
$result .= $t ^ $hash;
}
return $result . hash('whirlpool', $hash, true);
default:
$len = strlen($bytes);
for ($i = 0; $i < $len; $i+=20) {
$t = substr($bytes, $i, 20);
$hash = pack('H*', sha1($hash));
$result .= $t ^ $hash;
}
return $result . pack('H*', sha1($hash));
}
}
/**
* Convert float to int
*
* On ARM CPUs converting floats to ints doesn't always work
*
* @access private
* @param string $x
* @return int
*/
function safe_intval($x)
{
switch (true) {
case is_int($x):
// PHP 5.3, per http://php.net/releases/5_3_0.php, introduced
"more consistent float rounding"
case (php_uname('m') & "\xDF\xDF\xDF")
!= 'ARM':
return $x;
}
return (fmod($x, 0x80000000) & 0x7FFFFFFF) |
((fmod(floor($x / 0x80000000), 2) & 1) << 31);
}
/**
* eval()'able string for in-line float to int
*
* @access private
* @return string
*/
function safe_intval_inline()
{
switch (true) {
case defined('PHP_INT_SIZE') && PHP_INT_SIZE
== 8:
case (php_uname('m') & "\xDF\xDF\xDF")
!= 'ARM':
return '%s';
break;
default:
$safeint = '(is_int($temp = %s) ? $temp : (fmod($temp,
0x80000000) & 0x7FFFFFFF) | ';
return $safeint . '((fmod(floor($temp / 0x80000000),
2) & 1) << 31))';
}
}
/**
* Dummy error handler to suppress mcrypt errors
*
* @access private
*/
function do_nothing()
{
}
}
phpseclib/phpseclib/Crypt/Blowfish.php000064400000063744151161207740014077
0ustar00<?php
/**
* Pure-PHP implementation of Blowfish.
*
* Uses mcrypt, if available, and an internal implementation, otherwise.
*
* PHP version 5
*
* Useful resources are as follows:
*
* - {@link http://en.wikipedia.org/wiki/Blowfish_(cipher) Wikipedia
description of Blowfish}
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $blowfish = new \phpseclib\Crypt\Blowfish();
*
* $blowfish->setKey('12345678901234567890123456789012');
*
* $plaintext = str_repeat('a', 1024);
*
* echo $blowfish->decrypt($blowfish->encrypt($plaintext));
* ?>
* </code>
*
* @category Crypt
* @package Blowfish
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Pure-PHP implementation of Blowfish.
*
* @package Blowfish
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
* @access public
*/
class Blowfish extends Base
{
/**
* Block Length of the cipher
*
* @see \phpseclib\Crypt\Base::block_size
* @var int
* @access private
*/
var $block_size = 8;
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib\Crypt\Base::cipher_name_mcrypt
* @var string
* @access private
*/
var $cipher_name_mcrypt = 'blowfish';
/**
* Optimizing value while CFB-encrypting
*
* @see \phpseclib\Crypt\Base::cfb_init_len
* @var int
* @access private
*/
var $cfb_init_len = 500;
/**
* The fixed subkeys boxes ($sbox0 - $sbox3) with 256 entries each
*
* S-Box 0
*
* @access private
* @var array
*/
var $sbox0 = array(
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed,
0x6a267e96, 0xba7c9045, 0xf12c7f99,
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8,
0x71574e69, 0xa458fea3, 0xf4933d7e,
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d,
0xc25a59b5, 0x9c30d539, 0x2af26013,
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0,
0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3,
0xaa55ab94, 0x57489862, 0x63e81440,
0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af,
0x7c72e993, 0xb3ee1411, 0x636fbc2a,
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33,
0x6c24cf5c, 0x7a325381, 0x28958677,
0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc,
0xfb21a991, 0x487cac60, 0x5dec8032,
0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81,
0xd396acc5, 0x0f6d6ff3, 0x83f44239,
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842,
0xf6e96c9a, 0x670c9c61, 0xabd388f0,
0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c,
0x137a3be4, 0xba3bf050, 0x7efb2a98,
0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619,
0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62,
0x363f7706, 0x1bfedf72, 0x429b023d,
0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9,
0x80991b7b, 0x25d479d8, 0xf6e8def7,
0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6,
0x409f60c4, 0x5e5c9ec2, 0x196a2463,
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f,
0x9b30952c, 0xcc814544, 0xaf5ebd09,
0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857,
0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe,
0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa,
0xfd238760, 0x53317b48, 0x3e00df82,
0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6,
0x287effc3, 0xac6732c6, 0x8c4f5573,
0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98,
0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da,
0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d,
0xae909198, 0xeaad8e71, 0x6b93d5a0,
0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb,
0xf2122b64, 0x8888b812, 0x900df01c,
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218,
0xbe0e1777, 0xea752dfe, 0x8b021fa1,
0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0,
0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065,
0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d,
0x2071b35e, 0x226800bb, 0x57b8e0af,
0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389,
0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca,
0x7b14a94a, 0x1b510052, 0x9a532915,
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5,
0x571be91f, 0xf296ec6b, 0x2a0dd915,
0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d,
0xa99f8fa1, 0x08ba4799, 0x6e85076a
);
/**
* S-Box 1
*
* @access private
* @var array
*/
var $sbox1 = array(
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0,
0x49a7df7d, 0x9cee60b8, 0x8fedb266,
0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5,
0x75094c29, 0xa0591340, 0xe4183a3e,
0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07,
0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f,
0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c,
0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a,
0x3cb574b2, 0x25837a58, 0xdc0921bd,
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581,
0x37c2dadc, 0xc8b57634, 0x9af3dda7,
0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99,
0x3bea0e2f, 0x3280bba1, 0x183eb331,
0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290,
0x24977c79, 0x5679b072, 0xbcaf89af,
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f,
0x2e6b7124, 0x501adde6, 0x9f84cd87,
0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a,
0xdb851dfa, 0x63094366, 0xc464c3d2,
0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43,
0x2a65c451, 0x50940002, 0x133ae4dd,
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1,
0xd7a3c76b, 0x3c11183b, 0x5924a509,
0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570,
0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6,
0x5266c825, 0x2e4cc978, 0x9c10b36a,
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7,
0x361d2b3d, 0x1939260f, 0x19c27960,
0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595,
0xa67bc883, 0xb17f37d1, 0x018cff28,
0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f,
0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830,
0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc,
0x60622ca7, 0x9cab5cab, 0xb2f3846e,
0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32,
0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a,
0x97e32d77, 0x11ed935f, 0x16681281,
0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5,
0x1b227263, 0x9b83c3ff, 0x1ac24696,
0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef,
0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6,
0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e,
0x86854dc7, 0xe44b476a, 0x3d816250,
0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3,
0x69cb7492, 0x47848a0b, 0x5692b285,
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a,
0x0c55f5ea, 0x1dadf43e, 0x233f7061,
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759,
0xcbee7460, 0x4085f2a7, 0xce77326e,
0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa,
0xc50c06c2, 0x5a04abfc, 0x800bcadc,
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3,
0x105588cd, 0x675fda79, 0xe3674340,
0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7,
0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
);
/**
* S-Box 2
*
* @access private
* @var array
*/
var $sbox2 = array(
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7,
0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e,
0x97244546, 0x14214f74, 0xbf8b8840,
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec,
0x03bd9785, 0x7fac6dd0, 0x31cb8504,
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825,
0x530429f4, 0x0a2c86da, 0xe9b66dfb,
0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2,
0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35,
0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74,
0xdd5b4332, 0x6841e7f7, 0xca7820fb,
0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a,
0x20838d87, 0xfe6ba9b7, 0xd096954b,
0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56,
0x3f3125f9, 0x5ef47e1c, 0x9029317c,
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548,
0xe4c66d22, 0x48c1133f, 0xc70f86dc,
0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb,
0xd59bc0d1, 0xf2bcc18f, 0x41113564,
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2,
0x02e1329e, 0xaf664fd1, 0xcad18115,
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e,
0xe6ba0d99, 0xde720c8c, 0x2da2f728,
0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f,
0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60,
0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7,
0xcc00ffa3, 0xb5390f92, 0x690fed0b,
0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88,
0x515bad24, 0x7b9479bf, 0x763bd6eb,
0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7,
0xc66a2b3b, 0x12754ccc, 0x782ef11c,
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018,
0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e,
0xda86a85f, 0xbebfe988, 0x64e4c3fe,
0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346,
0xf6381fb0, 0x7745ae04, 0xd736fccc,
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be,
0xbde8ae24, 0x55464299, 0xbf582e61,
0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3,
0xc8b38e74, 0xb475f255, 0x46fcd9b9,
0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e,
0x20b45770, 0x8cd55591, 0xc902de4c,
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6,
0xe0a9dc09, 0x662d09a1, 0xc4324633,
0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d,
0x0ba5a4df, 0xa186f20f, 0x2868f169,
0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01,
0xa70683fa, 0xa002b5c4, 0x0de6d027,
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28,
0xc0f586e0, 0x006058aa, 0x30dc7d62,
0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56,
0x90bcb6de, 0xebfc7da1, 0xce591d76,
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f,
0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e,
0xb161e6f8, 0xa28514d9, 0x6c51133c,
0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234,
0x92638212, 0x670efa8e, 0x406000e0
);
/**
* S-Box 3
*
* @access private
* @var array
*/
var $sbox3 = array(
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e,
0x4fa33742, 0xd3822740, 0x99bc9bbe,
0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b,
0x21a19045, 0xb26eb1be, 0x6a366eb4,
0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee,
0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0,
0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba,
0x9be96a4d, 0x8fe51550, 0xba645bd6,
0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3,
0xf752f7da, 0x3f046f69, 0x77fa0a59,
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a,
0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf,
0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d,
0x283b57cc, 0xf8d56629, 0x79132e28,
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4,
0x88f46dba, 0x03a16125, 0x0564f0bd,
0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b,
0x1e6321f5, 0xf59c66fb, 0x26dcf319,
0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711,
0xc20ad9f8, 0xabcc5167, 0xccad925f,
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2,
0xfb3e7bce, 0x5121ce64, 0x774fbe32,
0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810,
0xdd6db224, 0x69852dfd, 0x09072166,
0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd,
0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8,
0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d,
0xe75b1357, 0xf8721671, 0xaf537d5d,
0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428,
0x95983a1d, 0x06b89fb4, 0xce6ea048,
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1,
0xe7933fdc, 0xbb3a792b, 0x344525bd,
0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e,
0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a,
0xc6913667, 0x8df9317c, 0xe0b12b4f,
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c,
0x15e6fc2a, 0x0f91fc71, 0x9b941525,
0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e,
0xe3056a0c, 0x10d25065, 0xcb03a442,
0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532,
0xe0d392df, 0xd3a0342b, 0x8971f21e,
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d,
0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166,
0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162,
0x5a75ebb5, 0x6e163697, 0x88d273cc,
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd,
0x327a140a, 0x45e1d006, 0xc3f27b9a,
0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905,
0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c,
0xf746ce76, 0x77afa1c5, 0x20756060,
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c,
0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132,
0xce77e25b, 0x578fdfe3, 0x3ac372e6
);
/**
* P-Array consists of 18 32-bit subkeys
*
* @var array
* @access private
*/
var $parray = array(
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822,
0x299f31d0,
0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf,
0x34e90c6c,
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9,
0x8979fb1b
);
/**
* The BCTX-working Array
*
* Holds the expanded key [p] and the key-depended s-boxes [sb]
*
* @var array
* @access private
*/
var $bctx;
/**
* Holds the last used key
*
* @var array
* @access private
*/
var $kl;
/**
* The Key Length (in bytes)
*
* @see \phpseclib\Crypt\Base::setKeyLength()
* @var int
* @access private
* @internal The max value is 256 / 8 = 32, the min value is 128 / 8 =
16. Exists in conjunction with $Nk
* because the encryption / decryption / key schedule creation
requires this number and not $key_length. We could
* derive this from $key_length or vice versa, but that'd mean
we'd have to do multiple shift operations, so in lieu
* of that, we'll just precompute it once.
*/
var $key_length = 16;
/**
* Sets the key length.
*
* Key lengths can be between 32 and 448 bits.
*
* @access public
* @param int $length
*/
function setKeyLength($length)
{
if ($length < 32) {
$this->key_length = 4;
} elseif ($length > 448) {
$this->key_length = 56;
} else {
$this->key_length = $length >> 3;
}
parent::setKeyLength($length);
}
/**
* Test for engine validity
*
* This is mainly just a wrapper to set things up for
\phpseclib\Crypt\Base::isValidEngine()
*
* @see \phpseclib\Crypt\Base::isValidEngine()
* @param int $engine
* @access public
* @return bool
*/
function isValidEngine($engine)
{
if ($engine == self::ENGINE_OPENSSL) {
if (version_compare(PHP_VERSION, '5.3.7') < 0
&& $this->key_length != 16) {
return false;
}
if ($this->key_length < 16) {
return false;
}
$this->cipher_name_openssl_ecb = 'bf-ecb';
$this->cipher_name_openssl = 'bf-' .
$this->_openssl_translate_mode();
}
return parent::isValidEngine($engine);
}
/**
* Setup the key (expansion)
*
* @see \phpseclib\Crypt\Base::_setupKey()
* @access private
*/
function _setupKey()
{
if (isset($this->kl['key']) && $this->key
=== $this->kl['key']) {
// already expanded
return;
}
$this->kl = array('key' => $this->key);
/* key-expanding p[] and S-Box building sb[] */
$this->bctx = array(
'p' => array(),
'sb' => array(
$this->sbox0,
$this->sbox1,
$this->sbox2,
$this->sbox3
)
);
// unpack binary string in unsigned chars
$key = array_values(unpack('C*', $this->key));
$keyl = count($key);
for ($j = 0, $i = 0; $i < 18; ++$i) {
// xor P1 with the first 32-bits of the key, xor P2 with the
second 32-bits ...
for ($data = 0, $k = 0; $k < 4; ++$k) {
$data = ($data << 8) | $key[$j];
if (++$j >= $keyl) {
$j = 0;
}
}
$this->bctx['p'][] = $this->parray[$i] ^ $data;
}
// encrypt the zero-string, replace P1 and P2 with the encrypted
data,
// encrypt P3 and P4 with the new P1 and P2, do it with all P-array
and subkeys
$data = "\0\0\0\0\0\0\0\0";
for ($i = 0; $i < 18; $i += 2) {
list($l, $r) = array_values(unpack('N*', $data =
$this->_encryptBlock($data)));
$this->bctx['p'][$i ] = $l;
$this->bctx['p'][$i + 1] = $r;
}
for ($i = 0; $i < 4; ++$i) {
for ($j = 0; $j < 256; $j += 2) {
list($l, $r) = array_values(unpack('N*', $data =
$this->_encryptBlock($data)));
$this->bctx['sb'][$i][$j ] = $l;
$this->bctx['sb'][$i][$j + 1] = $r;
}
}
}
/**
* Encrypts a block
*
* @access private
* @param string $in
* @return string
*/
function _encryptBlock($in)
{
$p = $this->bctx["p"];
// extract($this->bctx["sb"], EXTR_PREFIX_ALL,
"sb"); // slower
$sb_0 = $this->bctx["sb"][0];
$sb_1 = $this->bctx["sb"][1];
$sb_2 = $this->bctx["sb"][2];
$sb_3 = $this->bctx["sb"][3];
$in = unpack("N*", $in);
$l = $in[1];
$r = $in[2];
for ($i = 0; $i < 16; $i+= 2) {
$l^= $p[$i];
$r^= $this->safe_intval(($this->safe_intval($sb_0[$l
>> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^
$sb_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff]);
$r^= $p[$i + 1];
$l^= $this->safe_intval(($this->safe_intval($sb_0[$r
>> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^
$sb_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff]);
}
return pack("N*", $r ^ $p[17], $l ^ $p[16]);
}
/**
* Decrypts a block
*
* @access private
* @param string $in
* @return string
*/
function _decryptBlock($in)
{
$p = $this->bctx["p"];
$sb_0 = $this->bctx["sb"][0];
$sb_1 = $this->bctx["sb"][1];
$sb_2 = $this->bctx["sb"][2];
$sb_3 = $this->bctx["sb"][3];
$in = unpack("N*", $in);
$l = $in[1];
$r = $in[2];
for ($i = 17; $i > 2; $i-= 2) {
$l^= $p[$i];
$r^= $this->safe_intval(($this->safe_intval($sb_0[$l
>> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^
$sb_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff]);
$r^= $p[$i - 1];
$l^= $this->safe_intval(($this->safe_intval($sb_0[$r
>> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^
$sb_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff]);
}
return pack("N*", $r ^ $p[0], $l ^ $p[1]);
}
/**
* Setup the performance-optimized function for de/encrypt()
*
* @see \phpseclib\Crypt\Base::_setupInlineCrypt()
* @access private
*/
function _setupInlineCrypt()
{
$lambda_functions =& self::_getLambdaFunctions();
// We create max. 10 hi-optimized code for memory reason. Means:
For each $key one ultra fast inline-crypt function.
// (Currently, for Blowfish, one generated $lambda_function cost on
php5.5@32bit ~100kb unfreeable mem and ~180kb on php5.5@64bit)
// After that, we'll still create very fast optimized code but
not the hi-ultimative code, for each $mode one.
$gen_hi_opt_code = (bool)(count($lambda_functions) < 10);
// Generation of a unique hash for our generated code
$code_hash = "Crypt_Blowfish, {$this->mode}";
if ($gen_hi_opt_code) {
$code_hash = str_pad($code_hash, 32) .
$this->_hashInlineCryptFunction($this->key);
}
$safeint = $this->safe_intval_inline();
if (!isset($lambda_functions[$code_hash])) {
switch (true) {
case $gen_hi_opt_code:
$p = $this->bctx['p'];
$init_crypt = '
static $sb_0, $sb_1, $sb_2, $sb_3;
if (!$sb_0) {
$sb_0 = $self->bctx["sb"][0];
$sb_1 = $self->bctx["sb"][1];
$sb_2 = $self->bctx["sb"][2];
$sb_3 = $self->bctx["sb"][3];
}
';
break;
default:
$p = array();
for ($i = 0; $i < 18; ++$i) {
$p[] = '$p_' . $i;
}
$init_crypt = '
list($sb_0, $sb_1, $sb_2, $sb_3) =
$self->bctx["sb"];
list(' . implode(',', $p) . ')
= $self->bctx["p"];
';
}
// Generating encrypt code:
$encrypt_block = '
$in = unpack("N*", $in);
$l = $in[1];
$r = $in[2];
';
for ($i = 0; $i < 16; $i+= 2) {
$encrypt_block.= '
$l^= ' . $p[$i] . ';
$r^= ' . sprintf($safeint, '(' .
sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l
>> 16 & 0xff]') . ' ^
$sb_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff]') . ';
$r^= ' . $p[$i + 1] . ';
$l^= ' . sprintf($safeint, '(' .
sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r
>> 16 & 0xff]') . ' ^
$sb_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff]') . ';
';
}
$encrypt_block.= '
$in = pack("N*",
$r ^ ' . $p[17] . ',
$l ^ ' . $p[16] . '
);
';
// Generating decrypt code:
$decrypt_block = '
$in = unpack("N*", $in);
$l = $in[1];
$r = $in[2];
';
for ($i = 17; $i > 2; $i-= 2) {
$decrypt_block.= '
$l^= ' . $p[$i] . ';
$r^= ' . sprintf($safeint, '(' .
sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l
>> 16 & 0xff]') . ' ^
$sb_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff]') . ';
$r^= ' . $p[$i - 1] . ';
$l^= ' . sprintf($safeint, '(' .
sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r
>> 16 & 0xff]') . ' ^
$sb_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff]') . ';
';
}
$decrypt_block.= '
$in = pack("N*",
$r ^ ' . $p[0] . ',
$l ^ ' . $p[1] . '
);
';
$lambda_functions[$code_hash] =
$this->_createInlineCryptFunction(
array(
'init_crypt' => $init_crypt,
'init_encrypt' => '',
'init_decrypt' => '',
'encrypt_block' => $encrypt_block,
'decrypt_block' => $decrypt_block
)
);
}
$this->inline_crypt = $lambda_functions[$code_hash];
}
}
phpseclib/phpseclib/Crypt/DES.php000064400000213503151161207740012723
0ustar00<?php
/**
* Pure-PHP implementation of DES.
*
* Uses mcrypt, if available, and an internal implementation, otherwise.
*
* PHP version 5
*
* Useful resources are as follows:
*
* - {@link http://en.wikipedia.org/wiki/DES_supplementary_material
Wikipedia: DES supplementary material}
* - {@link http://www.itl.nist.gov/fipspubs/fip46-2.htm FIPS 46-2 -
(DES), Data Encryption Standard}
* - {@link http://www.cs.eku.edu/faculty/styer/460/Encrypt/JS-DES.html
JavaScript DES Example}
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $des = new \phpseclib\Crypt\DES();
*
* $des->setKey('abcdefgh');
*
* $size = 10 * 1024;
* $plaintext = '';
* for ($i = 0; $i < $size; $i++) {
* $plaintext.= 'a';
* }
*
* echo $des->decrypt($des->encrypt($plaintext));
* ?>
* </code>
*
* @category Crypt
* @package DES
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Pure-PHP implementation of DES.
*
* @package DES
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class DES extends Base
{
/**#@+
* @access private
* @see \phpseclib\Crypt\DES::_setupKey()
* @see \phpseclib\Crypt\DES::_processBlock()
*/
/**
* Contains $keys[self::ENCRYPT]
*/
const ENCRYPT = 0;
/**
* Contains $keys[self::DECRYPT]
*/
const DECRYPT = 1;
/**#@-*/
/**
* Block Length of the cipher
*
* @see \phpseclib\Crypt\Base::block_size
* @var int
* @access private
*/
var $block_size = 8;
/**
* Key Length (in bytes)
*
* @see \phpseclib\Crypt\Base::setKeyLength()
* @var int
* @access private
*/
var $key_length = 8;
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib\Crypt\Base::cipher_name_mcrypt
* @var string
* @access private
*/
var $cipher_name_mcrypt = 'des';
/**
* The OpenSSL names of the cipher / modes
*
* @see \phpseclib\Crypt\Base::openssl_mode_names
* @var array
* @access private
*/
var $openssl_mode_names = array(
self::MODE_ECB => 'des-ecb',
self::MODE_CBC => 'des-cbc',
self::MODE_CFB => 'des-cfb',
self::MODE_OFB => 'des-ofb'
// self::MODE_CTR is undefined for DES
);
/**
* Optimizing value while CFB-encrypting
*
* @see \phpseclib\Crypt\Base::cfb_init_len
* @var int
* @access private
*/
var $cfb_init_len = 500;
/**
* Switch for DES/3DES encryption
*
* Used only if $engine == self::ENGINE_INTERNAL
*
* @see self::_setupKey()
* @see self::_processBlock()
* @var int
* @access private
*/
var $des_rounds = 1;
/**
* max possible size of $key
*
* @see self::setKey()
* @var string
* @access private
*/
var $key_length_max = 8;
/**
* The Key Schedule
*
* @see self::_setupKey()
* @var array
* @access private
*/
var $keys;
/**
* Shuffle table.
*
* For each byte value index, the entry holds an 8-byte string
* with each byte containing all bits in the same state as the
* corresponding bit in the index value.
*
* @see self::_processBlock()
* @see self::_setupKey()
* @var array
* @access private
*/
var $shuffle = array(
"\x00\x00\x00\x00\x00\x00\x00\x00",
"\x00\x00\x00\x00\x00\x00\x00\xFF",
"\x00\x00\x00\x00\x00\x00\xFF\x00",
"\x00\x00\x00\x00\x00\x00\xFF\xFF",
"\x00\x00\x00\x00\x00\xFF\x00\x00",
"\x00\x00\x00\x00\x00\xFF\x00\xFF",
"\x00\x00\x00\x00\x00\xFF\xFF\x00",
"\x00\x00\x00\x00\x00\xFF\xFF\xFF",
"\x00\x00\x00\x00\xFF\x00\x00\x00",
"\x00\x00\x00\x00\xFF\x00\x00\xFF",
"\x00\x00\x00\x00\xFF\x00\xFF\x00",
"\x00\x00\x00\x00\xFF\x00\xFF\xFF",
"\x00\x00\x00\x00\xFF\xFF\x00\x00",
"\x00\x00\x00\x00\xFF\xFF\x00\xFF",
"\x00\x00\x00\x00\xFF\xFF\xFF\x00",
"\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
"\x00\x00\x00\xFF\x00\x00\x00\x00",
"\x00\x00\x00\xFF\x00\x00\x00\xFF",
"\x00\x00\x00\xFF\x00\x00\xFF\x00",
"\x00\x00\x00\xFF\x00\x00\xFF\xFF",
"\x00\x00\x00\xFF\x00\xFF\x00\x00",
"\x00\x00\x00\xFF\x00\xFF\x00\xFF",
"\x00\x00\x00\xFF\x00\xFF\xFF\x00",
"\x00\x00\x00\xFF\x00\xFF\xFF\xFF",
"\x00\x00\x00\xFF\xFF\x00\x00\x00",
"\x00\x00\x00\xFF\xFF\x00\x00\xFF",
"\x00\x00\x00\xFF\xFF\x00\xFF\x00",
"\x00\x00\x00\xFF\xFF\x00\xFF\xFF",
"\x00\x00\x00\xFF\xFF\xFF\x00\x00",
"\x00\x00\x00\xFF\xFF\xFF\x00\xFF",
"\x00\x00\x00\xFF\xFF\xFF\xFF\x00",
"\x00\x00\x00\xFF\xFF\xFF\xFF\xFF",
"\x00\x00\xFF\x00\x00\x00\x00\x00",
"\x00\x00\xFF\x00\x00\x00\x00\xFF",
"\x00\x00\xFF\x00\x00\x00\xFF\x00",
"\x00\x00\xFF\x00\x00\x00\xFF\xFF",
"\x00\x00\xFF\x00\x00\xFF\x00\x00",
"\x00\x00\xFF\x00\x00\xFF\x00\xFF",
"\x00\x00\xFF\x00\x00\xFF\xFF\x00",
"\x00\x00\xFF\x00\x00\xFF\xFF\xFF",
"\x00\x00\xFF\x00\xFF\x00\x00\x00",
"\x00\x00\xFF\x00\xFF\x00\x00\xFF",
"\x00\x00\xFF\x00\xFF\x00\xFF\x00",
"\x00\x00\xFF\x00\xFF\x00\xFF\xFF",
"\x00\x00\xFF\x00\xFF\xFF\x00\x00",
"\x00\x00\xFF\x00\xFF\xFF\x00\xFF",
"\x00\x00\xFF\x00\xFF\xFF\xFF\x00",
"\x00\x00\xFF\x00\xFF\xFF\xFF\xFF",
"\x00\x00\xFF\xFF\x00\x00\x00\x00",
"\x00\x00\xFF\xFF\x00\x00\x00\xFF",
"\x00\x00\xFF\xFF\x00\x00\xFF\x00",
"\x00\x00\xFF\xFF\x00\x00\xFF\xFF",
"\x00\x00\xFF\xFF\x00\xFF\x00\x00",
"\x00\x00\xFF\xFF\x00\xFF\x00\xFF",
"\x00\x00\xFF\xFF\x00\xFF\xFF\x00",
"\x00\x00\xFF\xFF\x00\xFF\xFF\xFF",
"\x00\x00\xFF\xFF\xFF\x00\x00\x00",
"\x00\x00\xFF\xFF\xFF\x00\x00\xFF",
"\x00\x00\xFF\xFF\xFF\x00\xFF\x00",
"\x00\x00\xFF\xFF\xFF\x00\xFF\xFF",
"\x00\x00\xFF\xFF\xFF\xFF\x00\x00",
"\x00\x00\xFF\xFF\xFF\xFF\x00\xFF",
"\x00\x00\xFF\xFF\xFF\xFF\xFF\x00",
"\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF",
"\x00\xFF\x00\x00\x00\x00\x00\x00",
"\x00\xFF\x00\x00\x00\x00\x00\xFF",
"\x00\xFF\x00\x00\x00\x00\xFF\x00",
"\x00\xFF\x00\x00\x00\x00\xFF\xFF",
"\x00\xFF\x00\x00\x00\xFF\x00\x00",
"\x00\xFF\x00\x00\x00\xFF\x00\xFF",
"\x00\xFF\x00\x00\x00\xFF\xFF\x00",
"\x00\xFF\x00\x00\x00\xFF\xFF\xFF",
"\x00\xFF\x00\x00\xFF\x00\x00\x00",
"\x00\xFF\x00\x00\xFF\x00\x00\xFF",
"\x00\xFF\x00\x00\xFF\x00\xFF\x00",
"\x00\xFF\x00\x00\xFF\x00\xFF\xFF",
"\x00\xFF\x00\x00\xFF\xFF\x00\x00",
"\x00\xFF\x00\x00\xFF\xFF\x00\xFF",
"\x00\xFF\x00\x00\xFF\xFF\xFF\x00",
"\x00\xFF\x00\x00\xFF\xFF\xFF\xFF",
"\x00\xFF\x00\xFF\x00\x00\x00\x00",
"\x00\xFF\x00\xFF\x00\x00\x00\xFF",
"\x00\xFF\x00\xFF\x00\x00\xFF\x00",
"\x00\xFF\x00\xFF\x00\x00\xFF\xFF",
"\x00\xFF\x00\xFF\x00\xFF\x00\x00",
"\x00\xFF\x00\xFF\x00\xFF\x00\xFF",
"\x00\xFF\x00\xFF\x00\xFF\xFF\x00",
"\x00\xFF\x00\xFF\x00\xFF\xFF\xFF",
"\x00\xFF\x00\xFF\xFF\x00\x00\x00",
"\x00\xFF\x00\xFF\xFF\x00\x00\xFF",
"\x00\xFF\x00\xFF\xFF\x00\xFF\x00",
"\x00\xFF\x00\xFF\xFF\x00\xFF\xFF",
"\x00\xFF\x00\xFF\xFF\xFF\x00\x00",
"\x00\xFF\x00\xFF\xFF\xFF\x00\xFF",
"\x00\xFF\x00\xFF\xFF\xFF\xFF\x00",
"\x00\xFF\x00\xFF\xFF\xFF\xFF\xFF",
"\x00\xFF\xFF\x00\x00\x00\x00\x00",
"\x00\xFF\xFF\x00\x00\x00\x00\xFF",
"\x00\xFF\xFF\x00\x00\x00\xFF\x00",
"\x00\xFF\xFF\x00\x00\x00\xFF\xFF",
"\x00\xFF\xFF\x00\x00\xFF\x00\x00",
"\x00\xFF\xFF\x00\x00\xFF\x00\xFF",
"\x00\xFF\xFF\x00\x00\xFF\xFF\x00",
"\x00\xFF\xFF\x00\x00\xFF\xFF\xFF",
"\x00\xFF\xFF\x00\xFF\x00\x00\x00",
"\x00\xFF\xFF\x00\xFF\x00\x00\xFF",
"\x00\xFF\xFF\x00\xFF\x00\xFF\x00",
"\x00\xFF\xFF\x00\xFF\x00\xFF\xFF",
"\x00\xFF\xFF\x00\xFF\xFF\x00\x00",
"\x00\xFF\xFF\x00\xFF\xFF\x00\xFF",
"\x00\xFF\xFF\x00\xFF\xFF\xFF\x00",
"\x00\xFF\xFF\x00\xFF\xFF\xFF\xFF",
"\x00\xFF\xFF\xFF\x00\x00\x00\x00",
"\x00\xFF\xFF\xFF\x00\x00\x00\xFF",
"\x00\xFF\xFF\xFF\x00\x00\xFF\x00",
"\x00\xFF\xFF\xFF\x00\x00\xFF\xFF",
"\x00\xFF\xFF\xFF\x00\xFF\x00\x00",
"\x00\xFF\xFF\xFF\x00\xFF\x00\xFF",
"\x00\xFF\xFF\xFF\x00\xFF\xFF\x00",
"\x00\xFF\xFF\xFF\x00\xFF\xFF\xFF",
"\x00\xFF\xFF\xFF\xFF\x00\x00\x00",
"\x00\xFF\xFF\xFF\xFF\x00\x00\xFF",
"\x00\xFF\xFF\xFF\xFF\x00\xFF\x00",
"\x00\xFF\xFF\xFF\xFF\x00\xFF\xFF",
"\x00\xFF\xFF\xFF\xFF\xFF\x00\x00",
"\x00\xFF\xFF\xFF\xFF\xFF\x00\xFF",
"\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00",
"\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
"\xFF\x00\x00\x00\x00\x00\x00\x00",
"\xFF\x00\x00\x00\x00\x00\x00\xFF",
"\xFF\x00\x00\x00\x00\x00\xFF\x00",
"\xFF\x00\x00\x00\x00\x00\xFF\xFF",
"\xFF\x00\x00\x00\x00\xFF\x00\x00",
"\xFF\x00\x00\x00\x00\xFF\x00\xFF",
"\xFF\x00\x00\x00\x00\xFF\xFF\x00",
"\xFF\x00\x00\x00\x00\xFF\xFF\xFF",
"\xFF\x00\x00\x00\xFF\x00\x00\x00",
"\xFF\x00\x00\x00\xFF\x00\x00\xFF",
"\xFF\x00\x00\x00\xFF\x00\xFF\x00",
"\xFF\x00\x00\x00\xFF\x00\xFF\xFF",
"\xFF\x00\x00\x00\xFF\xFF\x00\x00",
"\xFF\x00\x00\x00\xFF\xFF\x00\xFF",
"\xFF\x00\x00\x00\xFF\xFF\xFF\x00",
"\xFF\x00\x00\x00\xFF\xFF\xFF\xFF",
"\xFF\x00\x00\xFF\x00\x00\x00\x00",
"\xFF\x00\x00\xFF\x00\x00\x00\xFF",
"\xFF\x00\x00\xFF\x00\x00\xFF\x00",
"\xFF\x00\x00\xFF\x00\x00\xFF\xFF",
"\xFF\x00\x00\xFF\x00\xFF\x00\x00",
"\xFF\x00\x00\xFF\x00\xFF\x00\xFF",
"\xFF\x00\x00\xFF\x00\xFF\xFF\x00",
"\xFF\x00\x00\xFF\x00\xFF\xFF\xFF",
"\xFF\x00\x00\xFF\xFF\x00\x00\x00",
"\xFF\x00\x00\xFF\xFF\x00\x00\xFF",
"\xFF\x00\x00\xFF\xFF\x00\xFF\x00",
"\xFF\x00\x00\xFF\xFF\x00\xFF\xFF",
"\xFF\x00\x00\xFF\xFF\xFF\x00\x00",
"\xFF\x00\x00\xFF\xFF\xFF\x00\xFF",
"\xFF\x00\x00\xFF\xFF\xFF\xFF\x00",
"\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF",
"\xFF\x00\xFF\x00\x00\x00\x00\x00",
"\xFF\x00\xFF\x00\x00\x00\x00\xFF",
"\xFF\x00\xFF\x00\x00\x00\xFF\x00",
"\xFF\x00\xFF\x00\x00\x00\xFF\xFF",
"\xFF\x00\xFF\x00\x00\xFF\x00\x00",
"\xFF\x00\xFF\x00\x00\xFF\x00\xFF",
"\xFF\x00\xFF\x00\x00\xFF\xFF\x00",
"\xFF\x00\xFF\x00\x00\xFF\xFF\xFF",
"\xFF\x00\xFF\x00\xFF\x00\x00\x00",
"\xFF\x00\xFF\x00\xFF\x00\x00\xFF",
"\xFF\x00\xFF\x00\xFF\x00\xFF\x00",
"\xFF\x00\xFF\x00\xFF\x00\xFF\xFF",
"\xFF\x00\xFF\x00\xFF\xFF\x00\x00",
"\xFF\x00\xFF\x00\xFF\xFF\x00\xFF",
"\xFF\x00\xFF\x00\xFF\xFF\xFF\x00",
"\xFF\x00\xFF\x00\xFF\xFF\xFF\xFF",
"\xFF\x00\xFF\xFF\x00\x00\x00\x00",
"\xFF\x00\xFF\xFF\x00\x00\x00\xFF",
"\xFF\x00\xFF\xFF\x00\x00\xFF\x00",
"\xFF\x00\xFF\xFF\x00\x00\xFF\xFF",
"\xFF\x00\xFF\xFF\x00\xFF\x00\x00",
"\xFF\x00\xFF\xFF\x00\xFF\x00\xFF",
"\xFF\x00\xFF\xFF\x00\xFF\xFF\x00",
"\xFF\x00\xFF\xFF\x00\xFF\xFF\xFF",
"\xFF\x00\xFF\xFF\xFF\x00\x00\x00",
"\xFF\x00\xFF\xFF\xFF\x00\x00\xFF",
"\xFF\x00\xFF\xFF\xFF\x00\xFF\x00",
"\xFF\x00\xFF\xFF\xFF\x00\xFF\xFF",
"\xFF\x00\xFF\xFF\xFF\xFF\x00\x00",
"\xFF\x00\xFF\xFF\xFF\xFF\x00\xFF",
"\xFF\x00\xFF\xFF\xFF\xFF\xFF\x00",
"\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF",
"\xFF\xFF\x00\x00\x00\x00\x00\x00",
"\xFF\xFF\x00\x00\x00\x00\x00\xFF",
"\xFF\xFF\x00\x00\x00\x00\xFF\x00",
"\xFF\xFF\x00\x00\x00\x00\xFF\xFF",
"\xFF\xFF\x00\x00\x00\xFF\x00\x00",
"\xFF\xFF\x00\x00\x00\xFF\x00\xFF",
"\xFF\xFF\x00\x00\x00\xFF\xFF\x00",
"\xFF\xFF\x00\x00\x00\xFF\xFF\xFF",
"\xFF\xFF\x00\x00\xFF\x00\x00\x00",
"\xFF\xFF\x00\x00\xFF\x00\x00\xFF",
"\xFF\xFF\x00\x00\xFF\x00\xFF\x00",
"\xFF\xFF\x00\x00\xFF\x00\xFF\xFF",
"\xFF\xFF\x00\x00\xFF\xFF\x00\x00",
"\xFF\xFF\x00\x00\xFF\xFF\x00\xFF",
"\xFF\xFF\x00\x00\xFF\xFF\xFF\x00",
"\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF",
"\xFF\xFF\x00\xFF\x00\x00\x00\x00",
"\xFF\xFF\x00\xFF\x00\x00\x00\xFF",
"\xFF\xFF\x00\xFF\x00\x00\xFF\x00",
"\xFF\xFF\x00\xFF\x00\x00\xFF\xFF",
"\xFF\xFF\x00\xFF\x00\xFF\x00\x00",
"\xFF\xFF\x00\xFF\x00\xFF\x00\xFF",
"\xFF\xFF\x00\xFF\x00\xFF\xFF\x00",
"\xFF\xFF\x00\xFF\x00\xFF\xFF\xFF",
"\xFF\xFF\x00\xFF\xFF\x00\x00\x00",
"\xFF\xFF\x00\xFF\xFF\x00\x00\xFF",
"\xFF\xFF\x00\xFF\xFF\x00\xFF\x00",
"\xFF\xFF\x00\xFF\xFF\x00\xFF\xFF",
"\xFF\xFF\x00\xFF\xFF\xFF\x00\x00",
"\xFF\xFF\x00\xFF\xFF\xFF\x00\xFF",
"\xFF\xFF\x00\xFF\xFF\xFF\xFF\x00",
"\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF",
"\xFF\xFF\xFF\x00\x00\x00\x00\x00",
"\xFF\xFF\xFF\x00\x00\x00\x00\xFF",
"\xFF\xFF\xFF\x00\x00\x00\xFF\x00",
"\xFF\xFF\xFF\x00\x00\x00\xFF\xFF",
"\xFF\xFF\xFF\x00\x00\xFF\x00\x00",
"\xFF\xFF\xFF\x00\x00\xFF\x00\xFF",
"\xFF\xFF\xFF\x00\x00\xFF\xFF\x00",
"\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF",
"\xFF\xFF\xFF\x00\xFF\x00\x00\x00",
"\xFF\xFF\xFF\x00\xFF\x00\x00\xFF",
"\xFF\xFF\xFF\x00\xFF\x00\xFF\x00",
"\xFF\xFF\xFF\x00\xFF\x00\xFF\xFF",
"\xFF\xFF\xFF\x00\xFF\xFF\x00\x00",
"\xFF\xFF\xFF\x00\xFF\xFF\x00\xFF",
"\xFF\xFF\xFF\x00\xFF\xFF\xFF\x00",
"\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF",
"\xFF\xFF\xFF\xFF\x00\x00\x00\x00",
"\xFF\xFF\xFF\xFF\x00\x00\x00\xFF",
"\xFF\xFF\xFF\xFF\x00\x00\xFF\x00",
"\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF",
"\xFF\xFF\xFF\xFF\x00\xFF\x00\x00",
"\xFF\xFF\xFF\xFF\x00\xFF\x00\xFF",
"\xFF\xFF\xFF\xFF\x00\xFF\xFF\x00",
"\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF",
"\xFF\xFF\xFF\xFF\xFF\x00\x00\x00",
"\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF",
"\xFF\xFF\xFF\xFF\xFF\x00\xFF\x00",
"\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF",
"\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00",
"\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF",
"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00",
"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
);
/**
* IP mapping helper table.
*
* Indexing this table with each source byte performs the initial bit
permutation.
*
* @var array
* @access private
*/
var $ipmap = array(
0x00, 0x10, 0x01, 0x11, 0x20, 0x30, 0x21, 0x31,
0x02, 0x12, 0x03, 0x13, 0x22, 0x32, 0x23, 0x33,
0x40, 0x50, 0x41, 0x51, 0x60, 0x70, 0x61, 0x71,
0x42, 0x52, 0x43, 0x53, 0x62, 0x72, 0x63, 0x73,
0x04, 0x14, 0x05, 0x15, 0x24, 0x34, 0x25, 0x35,
0x06, 0x16, 0x07, 0x17, 0x26, 0x36, 0x27, 0x37,
0x44, 0x54, 0x45, 0x55, 0x64, 0x74, 0x65, 0x75,
0x46, 0x56, 0x47, 0x57, 0x66, 0x76, 0x67, 0x77,
0x80, 0x90, 0x81, 0x91, 0xA0, 0xB0, 0xA1, 0xB1,
0x82, 0x92, 0x83, 0x93, 0xA2, 0xB2, 0xA3, 0xB3,
0xC0, 0xD0, 0xC1, 0xD1, 0xE0, 0xF0, 0xE1, 0xF1,
0xC2, 0xD2, 0xC3, 0xD3, 0xE2, 0xF2, 0xE3, 0xF3,
0x84, 0x94, 0x85, 0x95, 0xA4, 0xB4, 0xA5, 0xB5,
0x86, 0x96, 0x87, 0x97, 0xA6, 0xB6, 0xA7, 0xB7,
0xC4, 0xD4, 0xC5, 0xD5, 0xE4, 0xF4, 0xE5, 0xF5,
0xC6, 0xD6, 0xC7, 0xD7, 0xE6, 0xF6, 0xE7, 0xF7,
0x08, 0x18, 0x09, 0x19, 0x28, 0x38, 0x29, 0x39,
0x0A, 0x1A, 0x0B, 0x1B, 0x2A, 0x3A, 0x2B, 0x3B,
0x48, 0x58, 0x49, 0x59, 0x68, 0x78, 0x69, 0x79,
0x4A, 0x5A, 0x4B, 0x5B, 0x6A, 0x7A, 0x6B, 0x7B,
0x0C, 0x1C, 0x0D, 0x1D, 0x2C, 0x3C, 0x2D, 0x3D,
0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F,
0x4C, 0x5C, 0x4D, 0x5D, 0x6C, 0x7C, 0x6D, 0x7D,
0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F,
0x88, 0x98, 0x89, 0x99, 0xA8, 0xB8, 0xA9, 0xB9,
0x8A, 0x9A, 0x8B, 0x9B, 0xAA, 0xBA, 0xAB, 0xBB,
0xC8, 0xD8, 0xC9, 0xD9, 0xE8, 0xF8, 0xE9, 0xF9,
0xCA, 0xDA, 0xCB, 0xDB, 0xEA, 0xFA, 0xEB, 0xFB,
0x8C, 0x9C, 0x8D, 0x9D, 0xAC, 0xBC, 0xAD, 0xBD,
0x8E, 0x9E, 0x8F, 0x9F, 0xAE, 0xBE, 0xAF, 0xBF,
0xCC, 0xDC, 0xCD, 0xDD, 0xEC, 0xFC, 0xED, 0xFD,
0xCE, 0xDE, 0xCF, 0xDF, 0xEE, 0xFE, 0xEF, 0xFF
);
/**
* Inverse IP mapping helper table.
* Indexing this table with a byte value reverses the bit order.
*
* @var array
* @access private
*/
var $invipmap = array(
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
);
/**
* Pre-permuted S-box1
*
* Each box ($sbox1-$sbox8) has been vectorized, then each value
pre-permuted using the
* P table: concatenation can then be replaced by exclusive ORs.
*
* @var array
* @access private
*/
var $sbox1 = array(
0x00808200, 0x00000000, 0x00008000, 0x00808202,
0x00808002, 0x00008202, 0x00000002, 0x00008000,
0x00000200, 0x00808200, 0x00808202, 0x00000200,
0x00800202, 0x00808002, 0x00800000, 0x00000002,
0x00000202, 0x00800200, 0x00800200, 0x00008200,
0x00008200, 0x00808000, 0x00808000, 0x00800202,
0x00008002, 0x00800002, 0x00800002, 0x00008002,
0x00000000, 0x00000202, 0x00008202, 0x00800000,
0x00008000, 0x00808202, 0x00000002, 0x00808000,
0x00808200, 0x00800000, 0x00800000, 0x00000200,
0x00808002, 0x00008000, 0x00008200, 0x00800002,
0x00000200, 0x00000002, 0x00800202, 0x00008202,
0x00808202, 0x00008002, 0x00808000, 0x00800202,
0x00800002, 0x00000202, 0x00008202, 0x00808200,
0x00000202, 0x00800200, 0x00800200, 0x00000000,
0x00008002, 0x00008200, 0x00000000, 0x00808002
);
/**
* Pre-permuted S-box2
*
* @var array
* @access private
*/
var $sbox2 = array(
0x40084010, 0x40004000, 0x00004000, 0x00084010,
0x00080000, 0x00000010, 0x40080010, 0x40004010,
0x40000010, 0x40084010, 0x40084000, 0x40000000,
0x40004000, 0x00080000, 0x00000010, 0x40080010,
0x00084000, 0x00080010, 0x40004010, 0x00000000,
0x40000000, 0x00004000, 0x00084010, 0x40080000,
0x00080010, 0x40000010, 0x00000000, 0x00084000,
0x00004010, 0x40084000, 0x40080000, 0x00004010,
0x00000000, 0x00084010, 0x40080010, 0x00080000,
0x40004010, 0x40080000, 0x40084000, 0x00004000,
0x40080000, 0x40004000, 0x00000010, 0x40084010,
0x00084010, 0x00000010, 0x00004000, 0x40000000,
0x00004010, 0x40084000, 0x00080000, 0x40000010,
0x00080010, 0x40004010, 0x40000010, 0x00080010,
0x00084000, 0x00000000, 0x40004000, 0x00004010,
0x40000000, 0x40080010, 0x40084010, 0x00084000
);
/**
* Pre-permuted S-box3
*
* @var array
* @access private
*/
var $sbox3 = array(
0x00000104, 0x04010100, 0x00000000, 0x04010004,
0x04000100, 0x00000000, 0x00010104, 0x04000100,
0x00010004, 0x04000004, 0x04000004, 0x00010000,
0x04010104, 0x00010004, 0x04010000, 0x00000104,
0x04000000, 0x00000004, 0x04010100, 0x00000100,
0x00010100, 0x04010000, 0x04010004, 0x00010104,
0x04000104, 0x00010100, 0x00010000, 0x04000104,
0x00000004, 0x04010104, 0x00000100, 0x04000000,
0x04010100, 0x04000000, 0x00010004, 0x00000104,
0x00010000, 0x04010100, 0x04000100, 0x00000000,
0x00000100, 0x00010004, 0x04010104, 0x04000100,
0x04000004, 0x00000100, 0x00000000, 0x04010004,
0x04000104, 0x00010000, 0x04000000, 0x04010104,
0x00000004, 0x00010104, 0x00010100, 0x04000004,
0x04010000, 0x04000104, 0x00000104, 0x04010000,
0x00010104, 0x00000004, 0x04010004, 0x00010100
);
/**
* Pre-permuted S-box4
*
* @var array
* @access private
*/
var $sbox4 = array(
0x80401000, 0x80001040, 0x80001040, 0x00000040,
0x00401040, 0x80400040, 0x80400000, 0x80001000,
0x00000000, 0x00401000, 0x00401000, 0x80401040,
0x80000040, 0x00000000, 0x00400040, 0x80400000,
0x80000000, 0x00001000, 0x00400000, 0x80401000,
0x00000040, 0x00400000, 0x80001000, 0x00001040,
0x80400040, 0x80000000, 0x00001040, 0x00400040,
0x00001000, 0x00401040, 0x80401040, 0x80000040,
0x00400040, 0x80400000, 0x00401000, 0x80401040,
0x80000040, 0x00000000, 0x00000000, 0x00401000,
0x00001040, 0x00400040, 0x80400040, 0x80000000,
0x80401000, 0x80001040, 0x80001040, 0x00000040,
0x80401040, 0x80000040, 0x80000000, 0x00001000,
0x80400000, 0x80001000, 0x00401040, 0x80400040,
0x80001000, 0x00001040, 0x00400000, 0x80401000,
0x00000040, 0x00400000, 0x00001000, 0x00401040
);
/**
* Pre-permuted S-box5
*
* @var array
* @access private
*/
var $sbox5 = array(
0x00000080, 0x01040080, 0x01040000, 0x21000080,
0x00040000, 0x00000080, 0x20000000, 0x01040000,
0x20040080, 0x00040000, 0x01000080, 0x20040080,
0x21000080, 0x21040000, 0x00040080, 0x20000000,
0x01000000, 0x20040000, 0x20040000, 0x00000000,
0x20000080, 0x21040080, 0x21040080, 0x01000080,
0x21040000, 0x20000080, 0x00000000, 0x21000000,
0x01040080, 0x01000000, 0x21000000, 0x00040080,
0x00040000, 0x21000080, 0x00000080, 0x01000000,
0x20000000, 0x01040000, 0x21000080, 0x20040080,
0x01000080, 0x20000000, 0x21040000, 0x01040080,
0x20040080, 0x00000080, 0x01000000, 0x21040000,
0x21040080, 0x00040080, 0x21000000, 0x21040080,
0x01040000, 0x00000000, 0x20040000, 0x21000000,
0x00040080, 0x01000080, 0x20000080, 0x00040000,
0x00000000, 0x20040000, 0x01040080, 0x20000080
);
/**
* Pre-permuted S-box6
*
* @var array
* @access private
*/
var $sbox6 = array(
0x10000008, 0x10200000, 0x00002000, 0x10202008,
0x10200000, 0x00000008, 0x10202008, 0x00200000,
0x10002000, 0x00202008, 0x00200000, 0x10000008,
0x00200008, 0x10002000, 0x10000000, 0x00002008,
0x00000000, 0x00200008, 0x10002008, 0x00002000,
0x00202000, 0x10002008, 0x00000008, 0x10200008,
0x10200008, 0x00000000, 0x00202008, 0x10202000,
0x00002008, 0x00202000, 0x10202000, 0x10000000,
0x10002000, 0x00000008, 0x10200008, 0x00202000,
0x10202008, 0x00200000, 0x00002008, 0x10000008,
0x00200000, 0x10002000, 0x10000000, 0x00002008,
0x10000008, 0x10202008, 0x00202000, 0x10200000,
0x00202008, 0x10202000, 0x00000000, 0x10200008,
0x00000008, 0x00002000, 0x10200000, 0x00202008,
0x00002000, 0x00200008, 0x10002008, 0x00000000,
0x10202000, 0x10000000, 0x00200008, 0x10002008
);
/**
* Pre-permuted S-box7
*
* @var array
* @access private
*/
var $sbox7 = array(
0x00100000, 0x02100001, 0x02000401, 0x00000000,
0x00000400, 0x02000401, 0x00100401, 0x02100400,
0x02100401, 0x00100000, 0x00000000, 0x02000001,
0x00000001, 0x02000000, 0x02100001, 0x00000401,
0x02000400, 0x00100401, 0x00100001, 0x02000400,
0x02000001, 0x02100000, 0x02100400, 0x00100001,
0x02100000, 0x00000400, 0x00000401, 0x02100401,
0x00100400, 0x00000001, 0x02000000, 0x00100400,
0x02000000, 0x00100400, 0x00100000, 0x02000401,
0x02000401, 0x02100001, 0x02100001, 0x00000001,
0x00100001, 0x02000000, 0x02000400, 0x00100000,
0x02100400, 0x00000401, 0x00100401, 0x02100400,
0x00000401, 0x02000001, 0x02100401, 0x02100000,
0x00100400, 0x00000000, 0x00000001, 0x02100401,
0x00000000, 0x00100401, 0x02100000, 0x00000400,
0x02000001, 0x02000400, 0x00000400, 0x00100001
);
/**
* Pre-permuted S-box8
*
* @var array
* @access private
*/
var $sbox8 = array(
0x08000820, 0x00000800, 0x00020000, 0x08020820,
0x08000000, 0x08000820, 0x00000020, 0x08000000,
0x00020020, 0x08020000, 0x08020820, 0x00020800,
0x08020800, 0x00020820, 0x00000800, 0x00000020,
0x08020000, 0x08000020, 0x08000800, 0x00000820,
0x00020800, 0x00020020, 0x08020020, 0x08020800,
0x00000820, 0x00000000, 0x00000000, 0x08020020,
0x08000020, 0x08000800, 0x00020820, 0x00020000,
0x00020820, 0x00020000, 0x08020800, 0x00000800,
0x00000020, 0x08020020, 0x00000800, 0x00020820,
0x08000800, 0x00000020, 0x08000020, 0x08020000,
0x08020020, 0x08000000, 0x00020000, 0x08000820,
0x00000000, 0x08020820, 0x00020020, 0x08000020,
0x08020000, 0x08000800, 0x08000820, 0x00000000,
0x08020820, 0x00020800, 0x00020800, 0x00000820,
0x00000820, 0x00020020, 0x08000000, 0x08020800
);
/**
* Test for engine validity
*
* This is mainly just a wrapper to set things up for
\phpseclib\Crypt\Base::isValidEngine()
*
* @see \phpseclib\Crypt\Base::isValidEngine()
* @param int $engine
* @access public
* @return bool
*/
function isValidEngine($engine)
{
if ($this->key_length_max == 8) {
if ($engine == self::ENGINE_OPENSSL) {
$this->cipher_name_openssl_ecb = 'des-ecb';
$this->cipher_name_openssl = 'des-' .
$this->_openssl_translate_mode();
}
}
return parent::isValidEngine($engine);
}
/**
* Sets the key.
*
* Keys can be of any length. DES, itself, uses 64-bit keys (eg.
strlen($key) == 8), however, we
* 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.
*
* If the key is not explicitly set, it'll be assumed to be all
zero's.
*
* @see \phpseclib\Crypt\Base::setKey()
* @access public
* @param string $key
*/
function setKey($key)
{
// We check/cut here only up to max length of the key.
// Key padding to the proper length will be done in _setupKey()
if (strlen($key) > $this->key_length_max) {
$key = substr($key, 0, $this->key_length_max);
}
// Sets the key
parent::setKey($key);
}
/**
* Encrypts a block
*
* @see \phpseclib\Crypt\Base::_encryptBlock()
* @see \phpseclib\Crypt\Base::encrypt()
* @see self::encrypt()
* @access private
* @param string $in
* @return string
*/
function _encryptBlock($in)
{
return $this->_processBlock($in, self::ENCRYPT);
}
/**
* Decrypts a block
*
* @see \phpseclib\Crypt\Base::_decryptBlock()
* @see \phpseclib\Crypt\Base::decrypt()
* @see self::decrypt()
* @access private
* @param string $in
* @return string
*/
function _decryptBlock($in)
{
return $this->_processBlock($in, self::DECRYPT);
}
/**
* Encrypts or decrypts a 64-bit block
*
* $mode should be either self::ENCRYPT or self::DECRYPT. See
* {@link http://en.wikipedia.org/wiki/Image:Feistel.png Feistel.png}
to get a general
* idea of what this function does.
*
* @see self::_encryptBlock()
* @see self::_decryptBlock()
* @access private
* @param string $block
* @param int $mode
* @return string
*/
function _processBlock($block, $mode)
{
static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7,
$sbox8, $shuffleip, $shuffleinvip;
if (!$sbox1) {
$sbox1 = array_map("intval", $this->sbox1);
$sbox2 = array_map("intval", $this->sbox2);
$sbox3 = array_map("intval", $this->sbox3);
$sbox4 = array_map("intval", $this->sbox4);
$sbox5 = array_map("intval", $this->sbox5);
$sbox6 = array_map("intval", $this->sbox6);
$sbox7 = array_map("intval", $this->sbox7);
$sbox8 = array_map("intval", $this->sbox8);
/* Merge $shuffle with $[inv]ipmap */
for ($i = 0; $i < 256; ++$i) {
$shuffleip[] = $this->shuffle[$this->ipmap[$i]];
$shuffleinvip[] =
$this->shuffle[$this->invipmap[$i]];
}
}
$keys = $this->keys[$mode];
$ki = -1;
// Do the initial IP permutation.
$t = unpack('Nl/Nr', $block);
list($l, $r) = array($t['l'], $t['r']);
$block = ($shuffleip[ $r & 0xFF] &
"\x80\x80\x80\x80\x80\x80\x80\x80") |
($shuffleip[($r >> 8) & 0xFF] &
"\x40\x40\x40\x40\x40\x40\x40\x40") |
($shuffleip[($r >> 16) & 0xFF] &
"\x20\x20\x20\x20\x20\x20\x20\x20") |
($shuffleip[($r >> 24) & 0xFF] &
"\x10\x10\x10\x10\x10\x10\x10\x10") |
($shuffleip[ $l & 0xFF] &
"\x08\x08\x08\x08\x08\x08\x08\x08") |
($shuffleip[($l >> 8) & 0xFF] &
"\x04\x04\x04\x04\x04\x04\x04\x04") |
($shuffleip[($l >> 16) & 0xFF] &
"\x02\x02\x02\x02\x02\x02\x02\x02") |
($shuffleip[($l >> 24) & 0xFF] &
"\x01\x01\x01\x01\x01\x01\x01\x01");
// Extract L0 and R0.
$t = unpack('Nl/Nr', $block);
list($l, $r) = array($t['l'], $t['r']);
for ($des_round = 0; $des_round < $this->des_rounds;
++$des_round) {
// Perform the 16 steps.
for ($i = 0; $i < 16; $i++) {
// start of "the Feistel (F) function" - see the
following URL:
//
http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png
// Merge key schedule.
$b1 = (($r >> 3) & 0x1FFFFFFF) ^ ($r <<
29) ^ $keys[++$ki];
$b2 = (($r >> 31) & 0x00000001) ^ ($r <<
1) ^ $keys[++$ki];
// S-box indexing.
$t = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2
>> 24) & 0x3F] ^
$sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2
>> 16) & 0x3F] ^
$sbox5[($b1 >> 8) & 0x3F] ^ $sbox6[($b2
>> 8) & 0x3F] ^
$sbox7[ $b1 & 0x3F] ^ $sbox8[ $b2
& 0x3F] ^ $l;
// end of "the Feistel (F) function"
$l = $r;
$r = $t;
}
// Last step should not permute L & R.
$t = $l;
$l = $r;
$r = $t;
}
// Perform the inverse IP permutation.
return ($shuffleinvip[($r >> 24) & 0xFF] &
"\x80\x80\x80\x80\x80\x80\x80\x80") |
($shuffleinvip[($l >> 24) & 0xFF] &
"\x40\x40\x40\x40\x40\x40\x40\x40") |
($shuffleinvip[($r >> 16) & 0xFF] &
"\x20\x20\x20\x20\x20\x20\x20\x20") |
($shuffleinvip[($l >> 16) & 0xFF] &
"\x10\x10\x10\x10\x10\x10\x10\x10") |
($shuffleinvip[($r >> 8) & 0xFF] &
"\x08\x08\x08\x08\x08\x08\x08\x08") |
($shuffleinvip[($l >> 8) & 0xFF] &
"\x04\x04\x04\x04\x04\x04\x04\x04") |
($shuffleinvip[ $r & 0xFF] &
"\x02\x02\x02\x02\x02\x02\x02\x02") |
($shuffleinvip[ $l & 0xFF] &
"\x01\x01\x01\x01\x01\x01\x01\x01");
}
/**
* Creates the key schedule
*
* @see \phpseclib\Crypt\Base::_setupKey()
* @access private
*/
function _setupKey()
{
if (isset($this->kl['key']) && $this->key
=== $this->kl['key'] && $this->des_rounds ===
$this->kl['des_rounds']) {
// already expanded
return;
}
$this->kl = array('key' => $this->key,
'des_rounds' => $this->des_rounds);
static $shifts = array( // number of key bits shifted per round
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
);
static $pc1map = array(
0x00, 0x00, 0x08, 0x08, 0x04, 0x04, 0x0C, 0x0C,
0x02, 0x02, 0x0A, 0x0A, 0x06, 0x06, 0x0E, 0x0E,
0x10, 0x10, 0x18, 0x18, 0x14, 0x14, 0x1C, 0x1C,
0x12, 0x12, 0x1A, 0x1A, 0x16, 0x16, 0x1E, 0x1E,
0x20, 0x20, 0x28, 0x28, 0x24, 0x24, 0x2C, 0x2C,
0x22, 0x22, 0x2A, 0x2A, 0x26, 0x26, 0x2E, 0x2E,
0x30, 0x30, 0x38, 0x38, 0x34, 0x34, 0x3C, 0x3C,
0x32, 0x32, 0x3A, 0x3A, 0x36, 0x36, 0x3E, 0x3E,
0x40, 0x40, 0x48, 0x48, 0x44, 0x44, 0x4C, 0x4C,
0x42, 0x42, 0x4A, 0x4A, 0x46, 0x46, 0x4E, 0x4E,
0x50, 0x50, 0x58, 0x58, 0x54, 0x54, 0x5C, 0x5C,
0x52, 0x52, 0x5A, 0x5A, 0x56, 0x56, 0x5E, 0x5E,
0x60, 0x60, 0x68, 0x68, 0x64, 0x64, 0x6C, 0x6C,
0x62, 0x62, 0x6A, 0x6A, 0x66, 0x66, 0x6E, 0x6E,
0x70, 0x70, 0x78, 0x78, 0x74, 0x74, 0x7C, 0x7C,
0x72, 0x72, 0x7A, 0x7A, 0x76, 0x76, 0x7E, 0x7E,
0x80, 0x80, 0x88, 0x88, 0x84, 0x84, 0x8C, 0x8C,
0x82, 0x82, 0x8A, 0x8A, 0x86, 0x86, 0x8E, 0x8E,
0x90, 0x90, 0x98, 0x98, 0x94, 0x94, 0x9C, 0x9C,
0x92, 0x92, 0x9A, 0x9A, 0x96, 0x96, 0x9E, 0x9E,
0xA0, 0xA0, 0xA8, 0xA8, 0xA4, 0xA4, 0xAC, 0xAC,
0xA2, 0xA2, 0xAA, 0xAA, 0xA6, 0xA6, 0xAE, 0xAE,
0xB0, 0xB0, 0xB8, 0xB8, 0xB4, 0xB4, 0xBC, 0xBC,
0xB2, 0xB2, 0xBA, 0xBA, 0xB6, 0xB6, 0xBE, 0xBE,
0xC0, 0xC0, 0xC8, 0xC8, 0xC4, 0xC4, 0xCC, 0xCC,
0xC2, 0xC2, 0xCA, 0xCA, 0xC6, 0xC6, 0xCE, 0xCE,
0xD0, 0xD0, 0xD8, 0xD8, 0xD4, 0xD4, 0xDC, 0xDC,
0xD2, 0xD2, 0xDA, 0xDA, 0xD6, 0xD6, 0xDE, 0xDE,
0xE0, 0xE0, 0xE8, 0xE8, 0xE4, 0xE4, 0xEC, 0xEC,
0xE2, 0xE2, 0xEA, 0xEA, 0xE6, 0xE6, 0xEE, 0xEE,
0xF0, 0xF0, 0xF8, 0xF8, 0xF4, 0xF4, 0xFC, 0xFC,
0xF2, 0xF2, 0xFA, 0xFA, 0xF6, 0xF6, 0xFE, 0xFE
);
// Mapping tables for the PC-2 transformation.
static $pc2mapc1 = array(
0x00000000, 0x00000400, 0x00200000, 0x00200400,
0x00000001, 0x00000401, 0x00200001, 0x00200401,
0x02000000, 0x02000400, 0x02200000, 0x02200400,
0x02000001, 0x02000401, 0x02200001, 0x02200401
);
static $pc2mapc2 = array(
0x00000000, 0x00000800, 0x08000000, 0x08000800,
0x00010000, 0x00010800, 0x08010000, 0x08010800,
0x00000000, 0x00000800, 0x08000000, 0x08000800,
0x00010000, 0x00010800, 0x08010000, 0x08010800,
0x00000100, 0x00000900, 0x08000100, 0x08000900,
0x00010100, 0x00010900, 0x08010100, 0x08010900,
0x00000100, 0x00000900, 0x08000100, 0x08000900,
0x00010100, 0x00010900, 0x08010100, 0x08010900,
0x00000010, 0x00000810, 0x08000010, 0x08000810,
0x00010010, 0x00010810, 0x08010010, 0x08010810,
0x00000010, 0x00000810, 0x08000010, 0x08000810,
0x00010010, 0x00010810, 0x08010010, 0x08010810,
0x00000110, 0x00000910, 0x08000110, 0x08000910,
0x00010110, 0x00010910, 0x08010110, 0x08010910,
0x00000110, 0x00000910, 0x08000110, 0x08000910,
0x00010110, 0x00010910, 0x08010110, 0x08010910,
0x00040000, 0x00040800, 0x08040000, 0x08040800,
0x00050000, 0x00050800, 0x08050000, 0x08050800,
0x00040000, 0x00040800, 0x08040000, 0x08040800,
0x00050000, 0x00050800, 0x08050000, 0x08050800,
0x00040100, 0x00040900, 0x08040100, 0x08040900,
0x00050100, 0x00050900, 0x08050100, 0x08050900,
0x00040100, 0x00040900, 0x08040100, 0x08040900,
0x00050100, 0x00050900, 0x08050100, 0x08050900,
0x00040010, 0x00040810, 0x08040010, 0x08040810,
0x00050010, 0x00050810, 0x08050010, 0x08050810,
0x00040010, 0x00040810, 0x08040010, 0x08040810,
0x00050010, 0x00050810, 0x08050010, 0x08050810,
0x00040110, 0x00040910, 0x08040110, 0x08040910,
0x00050110, 0x00050910, 0x08050110, 0x08050910,
0x00040110, 0x00040910, 0x08040110, 0x08040910,
0x00050110, 0x00050910, 0x08050110, 0x08050910,
0x01000000, 0x01000800, 0x09000000, 0x09000800,
0x01010000, 0x01010800, 0x09010000, 0x09010800,
0x01000000, 0x01000800, 0x09000000, 0x09000800,
0x01010000, 0x01010800, 0x09010000, 0x09010800,
0x01000100, 0x01000900, 0x09000100, 0x09000900,
0x01010100, 0x01010900, 0x09010100, 0x09010900,
0x01000100, 0x01000900, 0x09000100, 0x09000900,
0x01010100, 0x01010900, 0x09010100, 0x09010900,
0x01000010, 0x01000810, 0x09000010, 0x09000810,
0x01010010, 0x01010810, 0x09010010, 0x09010810,
0x01000010, 0x01000810, 0x09000010, 0x09000810,
0x01010010, 0x01010810, 0x09010010, 0x09010810,
0x01000110, 0x01000910, 0x09000110, 0x09000910,
0x01010110, 0x01010910, 0x09010110, 0x09010910,
0x01000110, 0x01000910, 0x09000110, 0x09000910,
0x01010110, 0x01010910, 0x09010110, 0x09010910,
0x01040000, 0x01040800, 0x09040000, 0x09040800,
0x01050000, 0x01050800, 0x09050000, 0x09050800,
0x01040000, 0x01040800, 0x09040000, 0x09040800,
0x01050000, 0x01050800, 0x09050000, 0x09050800,
0x01040100, 0x01040900, 0x09040100, 0x09040900,
0x01050100, 0x01050900, 0x09050100, 0x09050900,
0x01040100, 0x01040900, 0x09040100, 0x09040900,
0x01050100, 0x01050900, 0x09050100, 0x09050900,
0x01040010, 0x01040810, 0x09040010, 0x09040810,
0x01050010, 0x01050810, 0x09050010, 0x09050810,
0x01040010, 0x01040810, 0x09040010, 0x09040810,
0x01050010, 0x01050810, 0x09050010, 0x09050810,
0x01040110, 0x01040910, 0x09040110, 0x09040910,
0x01050110, 0x01050910, 0x09050110, 0x09050910,
0x01040110, 0x01040910, 0x09040110, 0x09040910,
0x01050110, 0x01050910, 0x09050110, 0x09050910
);
static $pc2mapc3 = array(
0x00000000, 0x00000004, 0x00001000, 0x00001004,
0x00000000, 0x00000004, 0x00001000, 0x00001004,
0x10000000, 0x10000004, 0x10001000, 0x10001004,
0x10000000, 0x10000004, 0x10001000, 0x10001004,
0x00000020, 0x00000024, 0x00001020, 0x00001024,
0x00000020, 0x00000024, 0x00001020, 0x00001024,
0x10000020, 0x10000024, 0x10001020, 0x10001024,
0x10000020, 0x10000024, 0x10001020, 0x10001024,
0x00080000, 0x00080004, 0x00081000, 0x00081004,
0x00080000, 0x00080004, 0x00081000, 0x00081004,
0x10080000, 0x10080004, 0x10081000, 0x10081004,
0x10080000, 0x10080004, 0x10081000, 0x10081004,
0x00080020, 0x00080024, 0x00081020, 0x00081024,
0x00080020, 0x00080024, 0x00081020, 0x00081024,
0x10080020, 0x10080024, 0x10081020, 0x10081024,
0x10080020, 0x10080024, 0x10081020, 0x10081024,
0x20000000, 0x20000004, 0x20001000, 0x20001004,
0x20000000, 0x20000004, 0x20001000, 0x20001004,
0x30000000, 0x30000004, 0x30001000, 0x30001004,
0x30000000, 0x30000004, 0x30001000, 0x30001004,
0x20000020, 0x20000024, 0x20001020, 0x20001024,
0x20000020, 0x20000024, 0x20001020, 0x20001024,
0x30000020, 0x30000024, 0x30001020, 0x30001024,
0x30000020, 0x30000024, 0x30001020, 0x30001024,
0x20080000, 0x20080004, 0x20081000, 0x20081004,
0x20080000, 0x20080004, 0x20081000, 0x20081004,
0x30080000, 0x30080004, 0x30081000, 0x30081004,
0x30080000, 0x30080004, 0x30081000, 0x30081004,
0x20080020, 0x20080024, 0x20081020, 0x20081024,
0x20080020, 0x20080024, 0x20081020, 0x20081024,
0x30080020, 0x30080024, 0x30081020, 0x30081024,
0x30080020, 0x30080024, 0x30081020, 0x30081024,
0x00000002, 0x00000006, 0x00001002, 0x00001006,
0x00000002, 0x00000006, 0x00001002, 0x00001006,
0x10000002, 0x10000006, 0x10001002, 0x10001006,
0x10000002, 0x10000006, 0x10001002, 0x10001006,
0x00000022, 0x00000026, 0x00001022, 0x00001026,
0x00000022, 0x00000026, 0x00001022, 0x00001026,
0x10000022, 0x10000026, 0x10001022, 0x10001026,
0x10000022, 0x10000026, 0x10001022, 0x10001026,
0x00080002, 0x00080006, 0x00081002, 0x00081006,
0x00080002, 0x00080006, 0x00081002, 0x00081006,
0x10080002, 0x10080006, 0x10081002, 0x10081006,
0x10080002, 0x10080006, 0x10081002, 0x10081006,
0x00080022, 0x00080026, 0x00081022, 0x00081026,
0x00080022, 0x00080026, 0x00081022, 0x00081026,
0x10080022, 0x10080026, 0x10081022, 0x10081026,
0x10080022, 0x10080026, 0x10081022, 0x10081026,
0x20000002, 0x20000006, 0x20001002, 0x20001006,
0x20000002, 0x20000006, 0x20001002, 0x20001006,
0x30000002, 0x30000006, 0x30001002, 0x30001006,
0x30000002, 0x30000006, 0x30001002, 0x30001006,
0x20000022, 0x20000026, 0x20001022, 0x20001026,
0x20000022, 0x20000026, 0x20001022, 0x20001026,
0x30000022, 0x30000026, 0x30001022, 0x30001026,
0x30000022, 0x30000026, 0x30001022, 0x30001026,
0x20080002, 0x20080006, 0x20081002, 0x20081006,
0x20080002, 0x20080006, 0x20081002, 0x20081006,
0x30080002, 0x30080006, 0x30081002, 0x30081006,
0x30080002, 0x30080006, 0x30081002, 0x30081006,
0x20080022, 0x20080026, 0x20081022, 0x20081026,
0x20080022, 0x20080026, 0x20081022, 0x20081026,
0x30080022, 0x30080026, 0x30081022, 0x30081026,
0x30080022, 0x30080026, 0x30081022, 0x30081026
);
static $pc2mapc4 = array(
0x00000000, 0x00100000, 0x00000008, 0x00100008,
0x00000200, 0x00100200, 0x00000208, 0x00100208,
0x00000000, 0x00100000, 0x00000008, 0x00100008,
0x00000200, 0x00100200, 0x00000208, 0x00100208,
0x04000000, 0x04100000, 0x04000008, 0x04100008,
0x04000200, 0x04100200, 0x04000208, 0x04100208,
0x04000000, 0x04100000, 0x04000008, 0x04100008,
0x04000200, 0x04100200, 0x04000208, 0x04100208,
0x00002000, 0x00102000, 0x00002008, 0x00102008,
0x00002200, 0x00102200, 0x00002208, 0x00102208,
0x00002000, 0x00102000, 0x00002008, 0x00102008,
0x00002200, 0x00102200, 0x00002208, 0x00102208,
0x04002000, 0x04102000, 0x04002008, 0x04102008,
0x04002200, 0x04102200, 0x04002208, 0x04102208,
0x04002000, 0x04102000, 0x04002008, 0x04102008,
0x04002200, 0x04102200, 0x04002208, 0x04102208,
0x00000000, 0x00100000, 0x00000008, 0x00100008,
0x00000200, 0x00100200, 0x00000208, 0x00100208,
0x00000000, 0x00100000, 0x00000008, 0x00100008,
0x00000200, 0x00100200, 0x00000208, 0x00100208,
0x04000000, 0x04100000, 0x04000008, 0x04100008,
0x04000200, 0x04100200, 0x04000208, 0x04100208,
0x04000000, 0x04100000, 0x04000008, 0x04100008,
0x04000200, 0x04100200, 0x04000208, 0x04100208,
0x00002000, 0x00102000, 0x00002008, 0x00102008,
0x00002200, 0x00102200, 0x00002208, 0x00102208,
0x00002000, 0x00102000, 0x00002008, 0x00102008,
0x00002200, 0x00102200, 0x00002208, 0x00102208,
0x04002000, 0x04102000, 0x04002008, 0x04102008,
0x04002200, 0x04102200, 0x04002208, 0x04102208,
0x04002000, 0x04102000, 0x04002008, 0x04102008,
0x04002200, 0x04102200, 0x04002208, 0x04102208,
0x00020000, 0x00120000, 0x00020008, 0x00120008,
0x00020200, 0x00120200, 0x00020208, 0x00120208,
0x00020000, 0x00120000, 0x00020008, 0x00120008,
0x00020200, 0x00120200, 0x00020208, 0x00120208,
0x04020000, 0x04120000, 0x04020008, 0x04120008,
0x04020200, 0x04120200, 0x04020208, 0x04120208,
0x04020000, 0x04120000, 0x04020008, 0x04120008,
0x04020200, 0x04120200, 0x04020208, 0x04120208,
0x00022000, 0x00122000, 0x00022008, 0x00122008,
0x00022200, 0x00122200, 0x00022208, 0x00122208,
0x00022000, 0x00122000, 0x00022008, 0x00122008,
0x00022200, 0x00122200, 0x00022208, 0x00122208,
0x04022000, 0x04122000, 0x04022008, 0x04122008,
0x04022200, 0x04122200, 0x04022208, 0x04122208,
0x04022000, 0x04122000, 0x04022008, 0x04122008,
0x04022200, 0x04122200, 0x04022208, 0x04122208,
0x00020000, 0x00120000, 0x00020008, 0x00120008,
0x00020200, 0x00120200, 0x00020208, 0x00120208,
0x00020000, 0x00120000, 0x00020008, 0x00120008,
0x00020200, 0x00120200, 0x00020208, 0x00120208,
0x04020000, 0x04120000, 0x04020008, 0x04120008,
0x04020200, 0x04120200, 0x04020208, 0x04120208,
0x04020000, 0x04120000, 0x04020008, 0x04120008,
0x04020200, 0x04120200, 0x04020208, 0x04120208,
0x00022000, 0x00122000, 0x00022008, 0x00122008,
0x00022200, 0x00122200, 0x00022208, 0x00122208,
0x00022000, 0x00122000, 0x00022008, 0x00122008,
0x00022200, 0x00122200, 0x00022208, 0x00122208,
0x04022000, 0x04122000, 0x04022008, 0x04122008,
0x04022200, 0x04122200, 0x04022208, 0x04122208,
0x04022000, 0x04122000, 0x04022008, 0x04122008,
0x04022200, 0x04122200, 0x04022208, 0x04122208
);
static $pc2mapd1 = array(
0x00000000, 0x00000001, 0x08000000, 0x08000001,
0x00200000, 0x00200001, 0x08200000, 0x08200001,
0x00000002, 0x00000003, 0x08000002, 0x08000003,
0x00200002, 0x00200003, 0x08200002, 0x08200003
);
static $pc2mapd2 = array(
0x00000000, 0x00100000, 0x00000800, 0x00100800,
0x00000000, 0x00100000, 0x00000800, 0x00100800,
0x04000000, 0x04100000, 0x04000800, 0x04100800,
0x04000000, 0x04100000, 0x04000800, 0x04100800,
0x00000004, 0x00100004, 0x00000804, 0x00100804,
0x00000004, 0x00100004, 0x00000804, 0x00100804,
0x04000004, 0x04100004, 0x04000804, 0x04100804,
0x04000004, 0x04100004, 0x04000804, 0x04100804,
0x00000000, 0x00100000, 0x00000800, 0x00100800,
0x00000000, 0x00100000, 0x00000800, 0x00100800,
0x04000000, 0x04100000, 0x04000800, 0x04100800,
0x04000000, 0x04100000, 0x04000800, 0x04100800,
0x00000004, 0x00100004, 0x00000804, 0x00100804,
0x00000004, 0x00100004, 0x00000804, 0x00100804,
0x04000004, 0x04100004, 0x04000804, 0x04100804,
0x04000004, 0x04100004, 0x04000804, 0x04100804,
0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
0x00020000, 0x00120000, 0x00020800, 0x00120800,
0x00020000, 0x00120000, 0x00020800, 0x00120800,
0x04020000, 0x04120000, 0x04020800, 0x04120800,
0x04020000, 0x04120000, 0x04020800, 0x04120800,
0x00020004, 0x00120004, 0x00020804, 0x00120804,
0x00020004, 0x00120004, 0x00020804, 0x00120804,
0x04020004, 0x04120004, 0x04020804, 0x04120804,
0x04020004, 0x04120004, 0x04020804, 0x04120804,
0x00020000, 0x00120000, 0x00020800, 0x00120800,
0x00020000, 0x00120000, 0x00020800, 0x00120800,
0x04020000, 0x04120000, 0x04020800, 0x04120800,
0x04020000, 0x04120000, 0x04020800, 0x04120800,
0x00020004, 0x00120004, 0x00020804, 0x00120804,
0x00020004, 0x00120004, 0x00020804, 0x00120804,
0x04020004, 0x04120004, 0x04020804, 0x04120804,
0x04020004, 0x04120004, 0x04020804, 0x04120804,
0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
0x04020204, 0x04120204, 0x04020A04, 0x04120A04,
0x04020204, 0x04120204, 0x04020A04, 0x04120A04,
0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
0x04020204, 0x04120204, 0x04020A04, 0x04120A04,
0x04020204, 0x04120204, 0x04020A04, 0x04120A04
);
static $pc2mapd3 = array(
0x00000000, 0x00010000, 0x02000000, 0x02010000,
0x00000020, 0x00010020, 0x02000020, 0x02010020,
0x00040000, 0x00050000, 0x02040000, 0x02050000,
0x00040020, 0x00050020, 0x02040020, 0x02050020,
0x00002000, 0x00012000, 0x02002000, 0x02012000,
0x00002020, 0x00012020, 0x02002020, 0x02012020,
0x00042000, 0x00052000, 0x02042000, 0x02052000,
0x00042020, 0x00052020, 0x02042020, 0x02052020,
0x00000000, 0x00010000, 0x02000000, 0x02010000,
0x00000020, 0x00010020, 0x02000020, 0x02010020,
0x00040000, 0x00050000, 0x02040000, 0x02050000,
0x00040020, 0x00050020, 0x02040020, 0x02050020,
0x00002000, 0x00012000, 0x02002000, 0x02012000,
0x00002020, 0x00012020, 0x02002020, 0x02012020,
0x00042000, 0x00052000, 0x02042000, 0x02052000,
0x00042020, 0x00052020, 0x02042020, 0x02052020,
0x00000010, 0x00010010, 0x02000010, 0x02010010,
0x00000030, 0x00010030, 0x02000030, 0x02010030,
0x00040010, 0x00050010, 0x02040010, 0x02050010,
0x00040030, 0x00050030, 0x02040030, 0x02050030,
0x00002010, 0x00012010, 0x02002010, 0x02012010,
0x00002030, 0x00012030, 0x02002030, 0x02012030,
0x00042010, 0x00052010, 0x02042010, 0x02052010,
0x00042030, 0x00052030, 0x02042030, 0x02052030,
0x00000010, 0x00010010, 0x02000010, 0x02010010,
0x00000030, 0x00010030, 0x02000030, 0x02010030,
0x00040010, 0x00050010, 0x02040010, 0x02050010,
0x00040030, 0x00050030, 0x02040030, 0x02050030,
0x00002010, 0x00012010, 0x02002010, 0x02012010,
0x00002030, 0x00012030, 0x02002030, 0x02012030,
0x00042010, 0x00052010, 0x02042010, 0x02052010,
0x00042030, 0x00052030, 0x02042030, 0x02052030,
0x20000000, 0x20010000, 0x22000000, 0x22010000,
0x20000020, 0x20010020, 0x22000020, 0x22010020,
0x20040000, 0x20050000, 0x22040000, 0x22050000,
0x20040020, 0x20050020, 0x22040020, 0x22050020,
0x20002000, 0x20012000, 0x22002000, 0x22012000,
0x20002020, 0x20012020, 0x22002020, 0x22012020,
0x20042000, 0x20052000, 0x22042000, 0x22052000,
0x20042020, 0x20052020, 0x22042020, 0x22052020,
0x20000000, 0x20010000, 0x22000000, 0x22010000,
0x20000020, 0x20010020, 0x22000020, 0x22010020,
0x20040000, 0x20050000, 0x22040000, 0x22050000,
0x20040020, 0x20050020, 0x22040020, 0x22050020,
0x20002000, 0x20012000, 0x22002000, 0x22012000,
0x20002020, 0x20012020, 0x22002020, 0x22012020,
0x20042000, 0x20052000, 0x22042000, 0x22052000,
0x20042020, 0x20052020, 0x22042020, 0x22052020,
0x20000010, 0x20010010, 0x22000010, 0x22010010,
0x20000030, 0x20010030, 0x22000030, 0x22010030,
0x20040010, 0x20050010, 0x22040010, 0x22050010,
0x20040030, 0x20050030, 0x22040030, 0x22050030,
0x20002010, 0x20012010, 0x22002010, 0x22012010,
0x20002030, 0x20012030, 0x22002030, 0x22012030,
0x20042010, 0x20052010, 0x22042010, 0x22052010,
0x20042030, 0x20052030, 0x22042030, 0x22052030,
0x20000010, 0x20010010, 0x22000010, 0x22010010,
0x20000030, 0x20010030, 0x22000030, 0x22010030,
0x20040010, 0x20050010, 0x22040010, 0x22050010,
0x20040030, 0x20050030, 0x22040030, 0x22050030,
0x20002010, 0x20012010, 0x22002010, 0x22012010,
0x20002030, 0x20012030, 0x22002030, 0x22012030,
0x20042010, 0x20052010, 0x22042010, 0x22052010,
0x20042030, 0x20052030, 0x22042030, 0x22052030
);
static $pc2mapd4 = array(
0x00000000, 0x00000400, 0x01000000, 0x01000400,
0x00000000, 0x00000400, 0x01000000, 0x01000400,
0x00000100, 0x00000500, 0x01000100, 0x01000500,
0x00000100, 0x00000500, 0x01000100, 0x01000500,
0x10000000, 0x10000400, 0x11000000, 0x11000400,
0x10000000, 0x10000400, 0x11000000, 0x11000400,
0x10000100, 0x10000500, 0x11000100, 0x11000500,
0x10000100, 0x10000500, 0x11000100, 0x11000500,
0x00080000, 0x00080400, 0x01080000, 0x01080400,
0x00080000, 0x00080400, 0x01080000, 0x01080400,
0x00080100, 0x00080500, 0x01080100, 0x01080500,
0x00080100, 0x00080500, 0x01080100, 0x01080500,
0x10080000, 0x10080400, 0x11080000, 0x11080400,
0x10080000, 0x10080400, 0x11080000, 0x11080400,
0x10080100, 0x10080500, 0x11080100, 0x11080500,
0x10080100, 0x10080500, 0x11080100, 0x11080500,
0x00000008, 0x00000408, 0x01000008, 0x01000408,
0x00000008, 0x00000408, 0x01000008, 0x01000408,
0x00000108, 0x00000508, 0x01000108, 0x01000508,
0x00000108, 0x00000508, 0x01000108, 0x01000508,
0x10000008, 0x10000408, 0x11000008, 0x11000408,
0x10000008, 0x10000408, 0x11000008, 0x11000408,
0x10000108, 0x10000508, 0x11000108, 0x11000508,
0x10000108, 0x10000508, 0x11000108, 0x11000508,
0x00080008, 0x00080408, 0x01080008, 0x01080408,
0x00080008, 0x00080408, 0x01080008, 0x01080408,
0x00080108, 0x00080508, 0x01080108, 0x01080508,
0x00080108, 0x00080508, 0x01080108, 0x01080508,
0x10080008, 0x10080408, 0x11080008, 0x11080408,
0x10080008, 0x10080408, 0x11080008, 0x11080408,
0x10080108, 0x10080508, 0x11080108, 0x11080508,
0x10080108, 0x10080508, 0x11080108, 0x11080508,
0x00001000, 0x00001400, 0x01001000, 0x01001400,
0x00001000, 0x00001400, 0x01001000, 0x01001400,
0x00001100, 0x00001500, 0x01001100, 0x01001500,
0x00001100, 0x00001500, 0x01001100, 0x01001500,
0x10001000, 0x10001400, 0x11001000, 0x11001400,
0x10001000, 0x10001400, 0x11001000, 0x11001400,
0x10001100, 0x10001500, 0x11001100, 0x11001500,
0x10001100, 0x10001500, 0x11001100, 0x11001500,
0x00081000, 0x00081400, 0x01081000, 0x01081400,
0x00081000, 0x00081400, 0x01081000, 0x01081400,
0x00081100, 0x00081500, 0x01081100, 0x01081500,
0x00081100, 0x00081500, 0x01081100, 0x01081500,
0x10081000, 0x10081400, 0x11081000, 0x11081400,
0x10081000, 0x10081400, 0x11081000, 0x11081400,
0x10081100, 0x10081500, 0x11081100, 0x11081500,
0x10081100, 0x10081500, 0x11081100, 0x11081500,
0x00001008, 0x00001408, 0x01001008, 0x01001408,
0x00001008, 0x00001408, 0x01001008, 0x01001408,
0x00001108, 0x00001508, 0x01001108, 0x01001508,
0x00001108, 0x00001508, 0x01001108, 0x01001508,
0x10001008, 0x10001408, 0x11001008, 0x11001408,
0x10001008, 0x10001408, 0x11001008, 0x11001408,
0x10001108, 0x10001508, 0x11001108, 0x11001508,
0x10001108, 0x10001508, 0x11001108, 0x11001508,
0x00081008, 0x00081408, 0x01081008, 0x01081408,
0x00081008, 0x00081408, 0x01081008, 0x01081408,
0x00081108, 0x00081508, 0x01081108, 0x01081508,
0x00081108, 0x00081508, 0x01081108, 0x01081508,
0x10081008, 0x10081408, 0x11081008, 0x11081408,
0x10081008, 0x10081408, 0x11081008, 0x11081408,
0x10081108, 0x10081508, 0x11081108, 0x11081508,
0x10081108, 0x10081508, 0x11081108, 0x11081508
);
$keys = array();
for ($des_round = 0; $des_round < $this->des_rounds;
++$des_round) {
// pad the key and remove extra characters as appropriate.
$key = str_pad(substr($this->key, $des_round * 8, 8), 8,
"\0");
// Perform the PC/1 transformation and compute C and D.
$t = unpack('Nl/Nr', $key);
list($l, $r) = array($t['l'], $t['r']);
$key = ($this->shuffle[$pc1map[ $r & 0xFF]] &
"\x80\x80\x80\x80\x80\x80\x80\x00") |
($this->shuffle[$pc1map[($r >> 8) & 0xFF]]
& "\x40\x40\x40\x40\x40\x40\x40\x00") |
($this->shuffle[$pc1map[($r >> 16) & 0xFF]]
& "\x20\x20\x20\x20\x20\x20\x20\x00") |
($this->shuffle[$pc1map[($r >> 24) & 0xFF]]
& "\x10\x10\x10\x10\x10\x10\x10\x00") |
($this->shuffle[$pc1map[ $l & 0xFF]] &
"\x08\x08\x08\x08\x08\x08\x08\x00") |
($this->shuffle[$pc1map[($l >> 8) & 0xFF]]
& "\x04\x04\x04\x04\x04\x04\x04\x00") |
($this->shuffle[$pc1map[($l >> 16) & 0xFF]]
& "\x02\x02\x02\x02\x02\x02\x02\x00") |
($this->shuffle[$pc1map[($l >> 24) & 0xFF]]
& "\x01\x01\x01\x01\x01\x01\x01\x00");
$key = unpack('Nc/Nd', $key);
$c = ( $key['c'] >> 4) & 0x0FFFFFFF;
$d = (($key['d'] >> 4) & 0x0FFFFFF0) |
($key['c'] & 0x0F);
$keys[$des_round] = array(
self::ENCRYPT => array(),
self::DECRYPT => array_fill(0, 32, 0)
);
for ($i = 0, $ki = 31; $i < 16; ++$i, $ki-= 2) {
$c <<= $shifts[$i];
$c = ($c | ($c >> 28)) & 0x0FFFFFFF;
$d <<= $shifts[$i];
$d = ($d | ($d >> 28)) & 0x0FFFFFFF;
// Perform the PC-2 transformation.
$cp = $pc2mapc1[ $c >> 24 ] | $pc2mapc2[($c
>> 16) & 0xFF] |
$pc2mapc3[($c >> 8) & 0xFF] | $pc2mapc4[
$c & 0xFF];
$dp = $pc2mapd1[ $d >> 24 ] | $pc2mapd2[($d
>> 16) & 0xFF] |
$pc2mapd3[($d >> 8) & 0xFF] | $pc2mapd4[
$d & 0xFF];
// Reorder: odd bytes/even bytes. Push the result in key
schedule.
$val1 = ( $cp & 0xFF000000) | (($cp << 8)
& 0x00FF0000) |
(($dp >> 16) & 0x0000FF00) | (($dp
>> 8) & 0x000000FF);
$val2 = (($cp << 8) & 0xFF000000) | (($cp
<< 16) & 0x00FF0000) |
(($dp >> 8) & 0x0000FF00) | ( $dp
& 0x000000FF);
$keys[$des_round][self::ENCRYPT][ ] = $val1;
$keys[$des_round][self::DECRYPT][$ki - 1] = $val1;
$keys[$des_round][self::ENCRYPT][ ] = $val2;
$keys[$des_round][self::DECRYPT][$ki ] = $val2;
}
}
switch ($this->des_rounds) {
case 3: // 3DES keys
$this->keys = array(
self::ENCRYPT => array_merge(
$keys[0][self::ENCRYPT],
$keys[1][self::DECRYPT],
$keys[2][self::ENCRYPT]
),
self::DECRYPT => array_merge(
$keys[2][self::DECRYPT],
$keys[1][self::ENCRYPT],
$keys[0][self::DECRYPT]
)
);
break;
// case 1: // DES keys
default:
$this->keys = array(
self::ENCRYPT => $keys[0][self::ENCRYPT],
self::DECRYPT => $keys[0][self::DECRYPT]
);
}
}
/**
* Setup the performance-optimized function for de/encrypt()
*
* @see \phpseclib\Crypt\Base::_setupInlineCrypt()
* @access private
*/
function _setupInlineCrypt()
{
$lambda_functions =& self::_getLambdaFunctions();
// Engine configuration for:
// - DES ($des_rounds == 1) or
// - 3DES ($des_rounds == 3)
$des_rounds = $this->des_rounds;
// We create max. 10 hi-optimized code for memory reason. Means:
For each $key one ultra fast inline-crypt function.
// (Currently, for DES, one generated $lambda_function cost on
php5.5@32bit ~135kb unfreeable mem and ~230kb on php5.5@64bit)
// (Currently, for TripleDES, one generated $lambda_function cost
on php5.5@32bit ~240kb unfreeable mem and ~340kb on php5.5@64bit)
// After that, we'll still create very fast optimized code but
not the hi-ultimative code, for each $mode one
$gen_hi_opt_code = (bool)( count($lambda_functions) < 10 );
// Generation of a unique hash for our generated code
$code_hash = "Crypt_DES, $des_rounds, {$this->mode}";
if ($gen_hi_opt_code) {
// For hi-optimized code, we create for each combination of
// $mode, $des_rounds and $this->key its own encrypt/decrypt
function.
// After max 10 hi-optimized functions, we create generic
// (still very fast.. but not ultra) functions for each
$mode/$des_rounds
// Currently 2 * 5 generic functions will be then max.
possible.
$code_hash = str_pad($code_hash, 32) .
$this->_hashInlineCryptFunction($this->key);
}
// 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 = 'static $sbox1, $sbox2, $sbox3, $sbox4,
$sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip;
if (!$sbox1) {
$sbox1 = array_map("intval",
$self->sbox1);
$sbox2 = array_map("intval",
$self->sbox2);
$sbox3 = array_map("intval",
$self->sbox3);
$sbox4 = array_map("intval",
$self->sbox4);
$sbox5 = array_map("intval",
$self->sbox5);
$sbox6 = array_map("intval",
$self->sbox6);
$sbox7 = array_map("intval",
$self->sbox7);
$sbox8 = array_map("intval",
$self->sbox8);'
/* Merge $shuffle with $[inv]ipmap */ . '
for ($i = 0; $i < 256; ++$i) {
$shuffleip[] =
$self->shuffle[$self->ipmap[$i]];
$shuffleinvip[] =
$self->shuffle[$self->invipmap[$i]];
}
}
';
switch (true) {
case $gen_hi_opt_code:
// In Hi-optimized code mode, we use our [3]DES key
schedule as hardcoded integers.
// No futher initialisation of the $keys schedule is
necessary.
// That is the extra performance boost.
$k = array(
self::ENCRYPT => $this->keys[self::ENCRYPT],
self::DECRYPT => $this->keys[self::DECRYPT]
);
$init_encrypt = '';
$init_decrypt = '';
break;
default:
// In generic optimized code mode, we have to use, as
the best compromise [currently],
// our key schedule as $ke/$kd arrays. (with hardcoded
indexes...)
$k = array(
self::ENCRYPT => array(),
self::DECRYPT => array()
);
for ($i = 0, $c = count($this->keys[self::ENCRYPT]);
$i < $c; ++$i) {
$k[self::ENCRYPT][$i] = '$ke[' . $i .
']';
$k[self::DECRYPT][$i] = '$kd[' . $i .
']';
}
$init_encrypt = '$ke =
$self->keys[$self::ENCRYPT];';
$init_decrypt = '$kd =
$self->keys[$self::DECRYPT];';
break;
}
// Creating code for en- and decryption.
$crypt_block = array();
foreach (array(self::ENCRYPT, self::DECRYPT) as $c) {
/* Do the initial IP permutation. */
$crypt_block[$c] = '
$in = unpack("N*", $in);
$l = $in[1];
$r = $in[2];
$in = unpack("N*",
($shuffleip[ $r & 0xFF] &
"\x80\x80\x80\x80\x80\x80\x80\x80") |
($shuffleip[($r >> 8) & 0xFF] &
"\x40\x40\x40\x40\x40\x40\x40\x40") |
($shuffleip[($r >> 16) & 0xFF] &
"\x20\x20\x20\x20\x20\x20\x20\x20") |
($shuffleip[($r >> 24) & 0xFF] &
"\x10\x10\x10\x10\x10\x10\x10\x10") |
($shuffleip[ $l & 0xFF] &
"\x08\x08\x08\x08\x08\x08\x08\x08") |
($shuffleip[($l >> 8) & 0xFF] &
"\x04\x04\x04\x04\x04\x04\x04\x04") |
($shuffleip[($l >> 16) & 0xFF] &
"\x02\x02\x02\x02\x02\x02\x02\x02") |
($shuffleip[($l >> 24) & 0xFF] &
"\x01\x01\x01\x01\x01\x01\x01\x01")
);
' . /* Extract L0 and R0 */ '
$l = $in[1];
$r = $in[2];
';
$l = '$l';
$r = '$r';
// Perform DES or 3DES.
for ($ki = -1, $des_round = 0; $des_round < $des_rounds;
++$des_round) {
// Perform the 16 steps.
for ($i = 0; $i < 16; ++$i) {
// start of "the Feistel (F) function" -
see the following URL:
//
http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png
// Merge key schedule.
$crypt_block[$c].= '
$b1 = ((' . $r . ' >> 3) &
0x1FFFFFFF) ^ (' . $r . ' << 29) ^ ' . $k[$c][++$ki]
. ';
$b2 = ((' . $r . ' >> 31) &
0x00000001) ^ (' . $r . ' << 1) ^ ' . $k[$c][++$ki]
. ';' .
/* S-box indexing. */
$l . ' = $sbox1[($b1 >> 24) &
0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^
$sbox3[($b1 >> 16) & 0x3F] ^
$sbox4[($b2 >> 16) & 0x3F] ^
$sbox5[($b1 >> 8) & 0x3F] ^
$sbox6[($b2 >> 8) & 0x3F] ^
$sbox7[ $b1 & 0x3F] ^
$sbox8[ $b2 & 0x3F] ^ ' . $l . ';
';
// end of "the Feistel (F) function"
// swap L & R
list($l, $r) = array($r, $l);
}
list($l, $r) = array($r, $l);
}
// Perform the inverse IP permutation.
$crypt_block[$c].= '$in =
($shuffleinvip[($l >> 24) & 0xFF] &
"\x80\x80\x80\x80\x80\x80\x80\x80") |
($shuffleinvip[($r >> 24) & 0xFF] &
"\x40\x40\x40\x40\x40\x40\x40\x40") |
($shuffleinvip[($l >> 16) & 0xFF] &
"\x20\x20\x20\x20\x20\x20\x20\x20") |
($shuffleinvip[($r >> 16) & 0xFF] &
"\x10\x10\x10\x10\x10\x10\x10\x10") |
($shuffleinvip[($l >> 8) & 0xFF] &
"\x08\x08\x08\x08\x08\x08\x08\x08") |
($shuffleinvip[($r >> 8) & 0xFF] &
"\x04\x04\x04\x04\x04\x04\x04\x04") |
($shuffleinvip[ $l & 0xFF] &
"\x02\x02\x02\x02\x02\x02\x02\x02") |
($shuffleinvip[ $r & 0xFF] &
"\x01\x01\x01\x01\x01\x01\x01\x01");
';
}
// Creates the inline-crypt function
$lambda_functions[$code_hash] =
$this->_createInlineCryptFunction(
array(
'init_crypt' => $init_crypt,
'init_encrypt' => $init_encrypt,
'init_decrypt' => $init_decrypt,
'encrypt_block' =>
$crypt_block[self::ENCRYPT],
'decrypt_block' =>
$crypt_block[self::DECRYPT]
)
);
}
// Set the inline-crypt function as callback in:
$this->inline_crypt
$this->inline_crypt = $lambda_functions[$code_hash];
}
}
phpseclib/phpseclib/Crypt/Hash.php000064400000071171151161207740013176
0ustar00<?php
/**
* 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:
*
* md2, md5, md5-96, sha1, sha1-96, sha256, sha256-96, sha384, and sha512,
sha512-96
*
* If {@link self::setKey() setKey()} is called, {@link self::hash()
hash()} will return the HMAC as opposed to
* the hash. If no valid algorithm is provided, sha1 will be used.
*
* PHP version 5
*
* {@internal The variable names are the same as those in
* {@link http://tools.ietf.org/html/rfc2104#section-2 RFC2104}.}}
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $hash = new \phpseclib\Crypt\Hash('sha1');
*
* $hash->setKey('abcdefg');
*
* echo base64_encode($hash->hash('abcdefg'));
* ?>
* </code>
*
* @category Crypt
* @package Hash
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
use phpseclib\Math\BigInteger;
/**
* Pure-PHP implementations of keyed-hash message authentication codes
(HMACs) and various cryptographic hashing functions.
*
* @package Hash
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class Hash
{
/**#@+
* @access private
* @see \phpseclib\Crypt\Hash::__construct()
*/
/**
* Toggles the internal implementation
*/
const MODE_INTERNAL = 1;
/**
* Toggles the mhash() implementation, which has been deprecated on PHP
5.3.0+.
*/
const MODE_MHASH = 2;
/**
* Toggles the hash() implementation, which works on PHP 5.1.2+.
*/
const MODE_HASH = 3;
/**#@-*/
/**
* Hash Parameter
*
* @see self::setHash()
* @var int
* @access private
*/
var $hashParam;
/**
* Byte-length of compression blocks / key (Internal HMAC)
*
* @see self::setAlgorithm()
* @var int
* @access private
*/
var $b;
/**
* Byte-length of hash output (Internal HMAC)
*
* @see self::setHash()
* @var int
* @access private
*/
var $l = false;
/**
* Hash Algorithm
*
* @see self::setHash()
* @var string
* @access private
*/
var $hash;
/**
* Key
*
* @see self::setKey()
* @var string
* @access private
*/
var $key = false;
/**
* Computed Key
*
* @see self::_computeKey()
* @var string
* @access private
*/
var $computedKey = false;
/**
* Outer XOR (Internal HMAC)
*
* @see self::setKey()
* @var string
* @access private
*/
var $opad;
/**
* Inner XOR (Internal HMAC)
*
* @see self::setKey()
* @var string
* @access private
*/
var $ipad;
/**
* Engine
*
* @see self::setHash()
* @var string
* @access private
*/
var $engine;
/**
* Default Constructor.
*
* @param string $hash
* @return \phpseclib\Crypt\Hash
* @access public
*/
function __construct($hash = 'sha1')
{
if (!defined('CRYPT_HASH_MODE')) {
switch (true) {
case extension_loaded('hash'):
define('CRYPT_HASH_MODE', self::MODE_HASH);
break;
case extension_loaded('mhash'):
define('CRYPT_HASH_MODE', self::MODE_MHASH);
break;
default:
define('CRYPT_HASH_MODE',
self::MODE_INTERNAL);
}
}
$this->setHash($hash);
}
/**
* Sets the key for HMACs
*
* Keys can be of any length.
*
* @access public
* @param string $key
*/
function setKey($key = false)
{
$this->key = $key;
$this->_computeKey();
}
/**
* Pre-compute the key used by the HMAC
*
* Quoting http://tools.ietf.org/html/rfc2104#section-2,
"Applications that use keys longer than B bytes
* will first hash the key using H and then use the resultant L byte
string as the actual key to HMAC."
*
* As documented in
https://www.reddit.com/r/PHP/comments/9nct2l/symfonypolyfill_hash_pbkdf2_correct_fix_for/
* when doing an HMAC multiple times it's faster to compute the
hash once instead of computing it during
* every call
*
* @access private
*/
function _computeKey()
{
if ($this->key === false) {
$this->computedKey = false;
return;
}
if (strlen($this->key) <= $this->b) {
$this->computedKey = $this->key;
return;
}
switch ($this->engine) {
case self::MODE_MHASH:
$this->computedKey = mhash($this->hash,
$this->key);
break;
case self::MODE_HASH:
$this->computedKey = hash($this->hash, $this->key,
true);
break;
case self::MODE_INTERNAL:
$this->computedKey = call_user_func($this->hash,
$this->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.
*
* @access public
* @param string $hash
*/
function setHash($hash)
{
$this->hashParam = $hash = strtolower($hash);
switch ($hash) {
case 'md5-96':
case 'sha1-96':
case 'sha256-96':
case 'sha512-96':
$hash = substr($hash, 0, -3);
$this->l = 12; // 96 / 8 = 12
break;
case 'md2':
case 'md5':
$this->l = 16;
break;
case 'sha1':
$this->l = 20;
break;
case 'sha256':
$this->l = 32;
break;
case 'sha384':
$this->l = 48;
break;
case 'sha512':
$this->l = 64;
}
switch ($hash) {
case 'md2-96':
case 'md2':
$this->b = 16;
case 'md5-96':
case 'sha1-96':
case 'sha224-96':
case 'sha256-96':
case 'md2':
case 'md5':
case 'sha1':
case 'sha224':
case 'sha256':
$this->b = 64;
break;
default:
$this->b = 128;
}
switch ($hash) {
case 'md2':
$this->engine = CRYPT_HASH_MODE == self::MODE_HASH
&& in_array('md2', hash_algos()) ?
self::MODE_HASH : self::MODE_INTERNAL;
break;
case 'sha384':
case 'sha512':
$this->engine = CRYPT_HASH_MODE == self::MODE_MHASH ?
self::MODE_INTERNAL : CRYPT_HASH_MODE;
break;
default:
$this->engine = CRYPT_HASH_MODE;
}
switch ($this->engine) {
case self::MODE_MHASH:
switch ($hash) {
case 'md5':
$this->hash = MHASH_MD5;
break;
case 'sha256':
$this->hash = MHASH_SHA256;
break;
case 'sha1':
default:
$this->hash = MHASH_SHA1;
}
$this->_computeKey(self::MODE_MHASH);
return;
case self::MODE_HASH:
switch ($hash) {
case 'md5':
$this->hash = 'md5';
return;
case 'md2':
case 'sha256':
case 'sha384':
case 'sha512':
$this->hash = $hash;
return;
case 'sha1':
default:
$this->hash = 'sha1';
}
$this->_computeKey(self::MODE_HASH);
return;
}
switch ($hash) {
case 'md2':
$this->hash = array($this, '_md2');
break;
case 'md5':
$this->hash = array($this, '_md5');
break;
case 'sha256':
$this->hash = array($this, '_sha256');
break;
case 'sha384':
case 'sha512':
$this->hash = array($this, '_sha512');
break;
case 'sha1':
default:
$this->hash = array($this, '_sha1');
}
$this->ipad = str_repeat(chr(0x36), $this->b);
$this->opad = str_repeat(chr(0x5C), $this->b);
$this->_computeKey(self::MODE_INTERNAL);
}
/**
* Compute the HMAC.
*
* @access public
* @param string $text
* @return string
*/
function hash($text)
{
if (!empty($this->key) || is_string($this->key)) {
switch ($this->engine) {
case self::MODE_MHASH:
$output = mhash($this->hash, $text,
$this->computedKey);
break;
case self::MODE_HASH:
$output = hash_hmac($this->hash, $text,
$this->computedKey, true);
break;
case self::MODE_INTERNAL:
$key = str_pad($this->computedKey, $this->b,
chr(0)); // step 1
$temp = $this->ipad ^ $key;
// step 2
$temp .= $text;
// step 3
$temp = call_user_func($this->hash, $temp);
// step 4
$output = $this->opad ^ $key;
// step 5
$output.= $temp;
// step 6
$output = call_user_func($this->hash, $output);
// step 7
}
} else {
switch ($this->engine) {
case self::MODE_MHASH:
$output = mhash($this->hash, $text);
break;
case self::MODE_HASH:
$output = hash($this->hash, $text, true);
break;
case self::MODE_INTERNAL:
$output = call_user_func($this->hash, $text);
}
}
return substr($output, 0, $this->l);
}
/**
* Returns the hash length (in bytes)
*
* @access public
* @return int
*/
function getLength()
{
return $this->l;
}
/**
* Wrapper for MD5
*
* @access private
* @param string $m
*/
function _md5($m)
{
return pack('H*', md5($m));
}
/**
* Wrapper for SHA1
*
* @access private
* @param string $m
*/
function _sha1($m)
{
return pack('H*', sha1($m));
}
/**
* Pure-PHP implementation of MD2
*
* See {@link http://tools.ietf.org/html/rfc1319 RFC1319}.
*
* @access private
* @param string $m
*/
function _md2($m)
{
static $s = array(
41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161,
236, 240, 6,
19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43,
217, 188,
76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103,
66, 111, 24,
138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73,
160, 251,
245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178,
7, 63,
148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154,
90, 144, 50,
39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48,
179, 72, 165,
181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184,
56, 210,
150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241,
69, 157,
112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45,
168, 2, 27,
96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52,
64, 126, 15,
85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206,
186, 197,
234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223,
205, 244, 65,
129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36,
225, 123,
8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232,
109, 233,
203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102,
88, 208, 228,
166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180,
143, 237,
31, 26, 219, 153, 141, 51, 159, 17, 131, 20
);
// Step 1. Append Padding Bytes
$pad = 16 - (strlen($m) & 0xF);
$m.= str_repeat(chr($pad), $pad);
$length = strlen($m);
// Step 2. Append Checksum
$c = str_repeat(chr(0), 16);
$l = chr(0);
for ($i = 0; $i < $length; $i+= 16) {
for ($j = 0; $j < 16; $j++) {
// RFC1319 incorrectly states that C[j] should be set to
S[c xor L]
//$c[$j] = chr($s[ord($m[$i + $j] ^ $l)]);
// per
<http://www.rfc-editor.org/errata_search.php?rfc=1319>, however, C[j]
should be set to S[c xor L] xor C[j]
$c[$j] = chr($s[ord($m[$i + $j] ^ $l)] ^ ord($c[$j]));
$l = $c[$j];
}
}
$m.= $c;
$length+= 16;
// Step 3. Initialize MD Buffer
$x = str_repeat(chr(0), 48);
// Step 4. Process Message in 16-Byte Blocks
for ($i = 0; $i < $length; $i+= 16) {
for ($j = 0; $j < 16; $j++) {
$x[$j + 16] = $m[$i + $j];
$x[$j + 32] = $x[$j + 16] ^ $x[$j];
}
$t = chr(0);
for ($j = 0; $j < 18; $j++) {
for ($k = 0; $k < 48; $k++) {
$x[$k] = $t = $x[$k] ^ chr($s[ord($t)]);
//$t = $x[$k] = $x[$k] ^ chr($s[ord($t)]);
}
$t = chr(ord($t) + $j);
}
}
// Step 5. Output
return substr($x, 0, 16);
}
/**
* Pure-PHP implementation of SHA256
*
* See {@link
http://en.wikipedia.org/wiki/SHA_hash_functions#SHA-256_.28a_SHA-2_variant.29_pseudocode
SHA-256 (a SHA-2 variant) pseudocode - Wikipedia}.
*
* @access private
* @param string $m
*/
function _sha256($m)
{
if (extension_loaded('suhosin')) {
return pack('H*', sha256($m));
}
// Initialize variables
$hash = array(
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f,
0x9b05688c, 0x1f83d9ab, 0x5be0cd19
);
// Initialize table of round constants
// (first 32 bits of the fractional parts of the cube roots of the
first 64 primes 2..311)
static $k = array(
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74,
0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f,
0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3,
0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354,
0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3,
0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa,
0xa4506ceb, 0xbef9a3f7, 0xc67178f2
);
// Pre-processing
$length = strlen($m);
// to round to nearest 56 mod 64, we'll add 64 - (length + (64
- 56)) % 64
$m.= str_repeat(chr(0), 64 - (($length + 8) & 0x3F));
$m[$length] = chr(0x80);
// we don't support hashing strings 512MB long
$m.= pack('N2', 0, $length << 3);
// Process the message in successive 512-bit chunks
$chunks = str_split($m, 64);
foreach ($chunks as $chunk) {
$w = array();
for ($i = 0; $i < 16; $i++) {
extract(unpack('Ntemp',
$this->_string_shift($chunk, 4)));
$w[] = $temp;
}
// Extend the sixteen 32-bit words into sixty-four 32-bit words
for ($i = 16; $i < 64; $i++) {
// @codingStandardsIgnoreStart
$s0 = $this->_rightRotate($w[$i - 15], 7) ^
$this->_rightRotate($w[$i - 15], 18) ^
$this->_rightShift( $w[$i - 15], 3);
$s1 = $this->_rightRotate($w[$i - 2], 17) ^
$this->_rightRotate($w[$i - 2], 19) ^
$this->_rightShift( $w[$i - 2], 10);
// @codingStandardsIgnoreEnd
$w[$i] = $this->_add($w[$i - 16], $s0, $w[$i - 7], $s1);
}
// Initialize hash value for this chunk
list($a, $b, $c, $d, $e, $f, $g, $h) = $hash;
// Main loop
for ($i = 0; $i < 64; $i++) {
$s0 = $this->_rightRotate($a, 2) ^
$this->_rightRotate($a, 13) ^
$this->_rightRotate($a, 22);
$maj = ($a & $b) ^
($a & $c) ^
($b & $c);
$t2 = $this->_add($s0, $maj);
$s1 = $this->_rightRotate($e, 6) ^
$this->_rightRotate($e, 11) ^
$this->_rightRotate($e, 25);
$ch = ($e & $f) ^
($this->_not($e) & $g);
$t1 = $this->_add($h, $s1, $ch, $k[$i], $w[$i]);
$h = $g;
$g = $f;
$f = $e;
$e = $this->_add($d, $t1);
$d = $c;
$c = $b;
$b = $a;
$a = $this->_add($t1, $t2);
}
// Add this chunk's hash to result so far
$hash = array(
$this->_add($hash[0], $a),
$this->_add($hash[1], $b),
$this->_add($hash[2], $c),
$this->_add($hash[3], $d),
$this->_add($hash[4], $e),
$this->_add($hash[5], $f),
$this->_add($hash[6], $g),
$this->_add($hash[7], $h)
);
}
// Produce the final hash value (big-endian)
return pack('N8', $hash[0], $hash[1], $hash[2], $hash[3],
$hash[4], $hash[5], $hash[6], $hash[7]);
}
/**
* Pure-PHP implementation of SHA384 and SHA512
*
* @access private
* @param string $m
*/
function _sha512($m)
{
static $init384, $init512, $k;
if (!isset($k)) {
// Initialize variables
$init384 = array( // initial values for SHA384
'cbbb9d5dc1059ed8', '629a292a367cd507',
'9159015a3070dd17', '152fecd8f70e5939',
'67332667ffc00b31', '8eb44a8768581511',
'db0c2e0d64f98fa7', '47b5481dbefa4fa4'
);
$init512 = array( // initial values for SHA512
'6a09e667f3bcc908', 'bb67ae8584caa73b',
'3c6ef372fe94f82b', 'a54ff53a5f1d36f1',
'510e527fade682d1', '9b05688c2b3e6c1f',
'1f83d9abfb41bd6b', '5be0cd19137e2179'
);
for ($i = 0; $i < 8; $i++) {
$init384[$i] = new BigInteger($init384[$i], 16);
$init384[$i]->setPrecision(64);
$init512[$i] = new BigInteger($init512[$i], 16);
$init512[$i]->setPrecision(64);
}
// Initialize table of round constants
// (first 64 bits of the fractional parts of the cube roots of
the first 80 primes 2..409)
$k = array(
'428a2f98d728ae22', '7137449123ef65cd',
'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc',
'3956c25bf348b538', '59f111f1b605d019',
'923f82a4af194f9b', 'ab1c5ed5da6d8118',
'd807aa98a3030242', '12835b0145706fbe',
'243185be4ee4b28c', '550c7dc3d5ffb4e2',
'72be5d74f27b896f', '80deb1fe3b1696b1',
'9bdc06a725c71235', 'c19bf174cf692694',
'e49b69c19ef14ad2', 'efbe4786384f25e3',
'0fc19dc68b8cd5b5', '240ca1cc77ac9c65',
'2de92c6f592b0275', '4a7484aa6ea6e483',
'5cb0a9dcbd41fbd4', '76f988da831153b5',
'983e5152ee66dfab', 'a831c66d2db43210',
'b00327c898fb213f', 'bf597fc7beef0ee4',
'c6e00bf33da88fc2', 'd5a79147930aa725',
'06ca6351e003826f', '142929670a0e6e70',
'27b70a8546d22ffc', '2e1b21385c26c926',
'4d2c6dfc5ac42aed', '53380d139d95b3df',
'650a73548baf63de', '766a0abb3c77b2a8',
'81c2c92e47edaee6', '92722c851482353b',
'a2bfe8a14cf10364', 'a81a664bbc423001',
'c24b8b70d0f89791', 'c76c51a30654be30',
'd192e819d6ef5218', 'd69906245565a910',
'f40e35855771202a', '106aa07032bbd1b8',
'19a4c116b8d2d0c8', '1e376c085141ab53',
'2748774cdf8eeb99', '34b0bcb5e19b48a8',
'391c0cb3c5c95a63', '4ed8aa4ae3418acb',
'5b9cca4f7763e373', '682e6ff3d6b2b8a3',
'748f82ee5defb2fc', '78a5636f43172f60',
'84c87814a1f0ab72', '8cc702081a6439ec',
'90befffa23631e28', 'a4506cebde82bde9',
'bef9a3f7b2c67915', 'c67178f2e372532b',
'ca273eceea26619c', 'd186b8c721c0c207',
'eada7dd6cde0eb1e', 'f57d4f7fee6ed178',
'06f067aa72176fba', '0a637dc5a2c898a6',
'113f9804bef90dae', '1b710b35131c471b',
'28db77f523047d84', '32caab7b40c72493',
'3c9ebe0a15c9bebc', '431d67c49c100d4c',
'4cc5d4becb3e42b6', '597f299cfc657e2a',
'5fcb6fab3ad6faec', '6c44198c4a475817'
);
for ($i = 0; $i < 80; $i++) {
$k[$i] = new BigInteger($k[$i], 16);
}
}
$hash = $this->l == 48 ? $init384 : $init512;
// Pre-processing
$length = strlen($m);
// to round to nearest 112 mod 128, we'll add 128 - (length +
(128 - 112)) % 128
$m.= str_repeat(chr(0), 128 - (($length + 16) & 0x7F));
$m[$length] = chr(0x80);
// we don't support hashing strings 512MB long
$m.= pack('N4', 0, 0, 0, $length << 3);
// Process the message in successive 1024-bit chunks
$chunks = str_split($m, 128);
foreach ($chunks as $chunk) {
$w = array();
for ($i = 0; $i < 16; $i++) {
$temp = new BigInteger($this->_string_shift($chunk, 8),
256);
$temp->setPrecision(64);
$w[] = $temp;
}
// Extend the sixteen 32-bit words into eighty 32-bit words
for ($i = 16; $i < 80; $i++) {
$temp = array(
$w[$i - 15]->bitwise_rightRotate(1),
$w[$i - 15]->bitwise_rightRotate(8),
$w[$i - 15]->bitwise_rightShift(7)
);
$s0 = $temp[0]->bitwise_xor($temp[1]);
$s0 = $s0->bitwise_xor($temp[2]);
$temp = array(
$w[$i - 2]->bitwise_rightRotate(19),
$w[$i - 2]->bitwise_rightRotate(61),
$w[$i - 2]->bitwise_rightShift(6)
);
$s1 = $temp[0]->bitwise_xor($temp[1]);
$s1 = $s1->bitwise_xor($temp[2]);
$w[$i] = $w[$i - 16]->copy();
$w[$i] = $w[$i]->add($s0);
$w[$i] = $w[$i]->add($w[$i - 7]);
$w[$i] = $w[$i]->add($s1);
}
// Initialize hash value for this chunk
$a = $hash[0]->copy();
$b = $hash[1]->copy();
$c = $hash[2]->copy();
$d = $hash[3]->copy();
$e = $hash[4]->copy();
$f = $hash[5]->copy();
$g = $hash[6]->copy();
$h = $hash[7]->copy();
// Main loop
for ($i = 0; $i < 80; $i++) {
$temp = array(
$a->bitwise_rightRotate(28),
$a->bitwise_rightRotate(34),
$a->bitwise_rightRotate(39)
);
$s0 = $temp[0]->bitwise_xor($temp[1]);
$s0 = $s0->bitwise_xor($temp[2]);
$temp = array(
$a->bitwise_and($b),
$a->bitwise_and($c),
$b->bitwise_and($c)
);
$maj = $temp[0]->bitwise_xor($temp[1]);
$maj = $maj->bitwise_xor($temp[2]);
$t2 = $s0->add($maj);
$temp = array(
$e->bitwise_rightRotate(14),
$e->bitwise_rightRotate(18),
$e->bitwise_rightRotate(41)
);
$s1 = $temp[0]->bitwise_xor($temp[1]);
$s1 = $s1->bitwise_xor($temp[2]);
$temp = array(
$e->bitwise_and($f),
$g->bitwise_and($e->bitwise_not())
);
$ch = $temp[0]->bitwise_xor($temp[1]);
$t1 = $h->add($s1);
$t1 = $t1->add($ch);
$t1 = $t1->add($k[$i]);
$t1 = $t1->add($w[$i]);
$h = $g->copy();
$g = $f->copy();
$f = $e->copy();
$e = $d->add($t1);
$d = $c->copy();
$c = $b->copy();
$b = $a->copy();
$a = $t1->add($t2);
}
// Add this chunk's hash to result so far
$hash = array(
$hash[0]->add($a),
$hash[1]->add($b),
$hash[2]->add($c),
$hash[3]->add($d),
$hash[4]->add($e),
$hash[5]->add($f),
$hash[6]->add($g),
$hash[7]->add($h)
);
}
// Produce the final hash value (big-endian)
// (\phpseclib\Crypt\Hash::hash() trims the output for hashes but
not for HMACs. as such, we trim the output here)
$temp = $hash[0]->toBytes() . $hash[1]->toBytes() .
$hash[2]->toBytes() . $hash[3]->toBytes() .
$hash[4]->toBytes() . $hash[5]->toBytes();
if ($this->l != 48) {
$temp.= $hash[6]->toBytes() . $hash[7]->toBytes();
}
return $temp;
}
/**
* Right Rotate
*
* @access private
* @param int $int
* @param int $amt
* @see self::_sha256()
* @return int
*/
function _rightRotate($int, $amt)
{
$invamt = 32 - $amt;
$mask = (1 << $invamt) - 1;
return (($int << $invamt) & 0xFFFFFFFF) | (($int >>
$amt) & $mask);
}
/**
* Right Shift
*
* @access private
* @param int $int
* @param int $amt
* @see self::_sha256()
* @return int
*/
function _rightShift($int, $amt)
{
$mask = (1 << (32 - $amt)) - 1;
return ($int >> $amt) & $mask;
}
/**
* Not
*
* @access private
* @param int $int
* @see self::_sha256()
* @return int
*/
function _not($int)
{
return ~$int & 0xFFFFFFFF;
}
/**
* Add
*
* _sha256() adds multiple unsigned 32-bit integers. Since PHP
doesn't support unsigned integers and since the
* possibility of overflow exists, care has to be taken. BigInteger
could be used but this should be faster.
*
* @return int
* @see self::_sha256()
* @access private
*/
function _add()
{
static $mod;
if (!isset($mod)) {
$mod = pow(2, 32);
}
$result = 0;
$arguments = func_get_args();
foreach ($arguments as $argument) {
$result+= $argument < 0 ? ($argument & 0x7FFFFFFF) +
0x80000000 : $argument;
}
if ((php_uname('m') & "\xDF\xDF\xDF") !=
'ARM') {
return fmod($result, $mod);
}
return (fmod($result, 0x80000000) & 0x7FFFFFFF) |
((fmod(floor($result / 0x80000000), 2) & 1) << 31);
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @return string
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
}
phpseclib/phpseclib/Crypt/Random.php000064400000030044151161207740013525
0ustar00<?php
/**
* Random Number Generator
*
* PHP version 5
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* echo bin2hex(\phpseclib\Crypt\Random::string(8));
* ?>
* </code>
*
* @category Crypt
* @package Random
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Pure-PHP Random Number Generator
*
* @package Random
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class Random
{
/**
* Generate a random string.
*
* Although microoptimizations are generally discouraged as they impair
readability this function is ripe with
* microoptimizations because this function has the potential of being
called a huge number of times.
* eg. for RSA key generation.
*
* @param int $length
* @return string
*/
static function string($length)
{
if (!$length) {
return '';
}
if (version_compare(PHP_VERSION, '7.0.0',
'>=')) {
try {
return \random_bytes($length);
} catch (\Throwable $e) {
// If a sufficient source of randomness is unavailable,
random_bytes() will throw an
// object that implements the Throwable interface
(Exception, TypeError, Error).
// We don't actually need to do anything here. The
string() method should just continue
// as normal. Note, however, that if we don't have a
sufficient source of randomness for
// random_bytes(), most of the other calls here will fail
too, so we'll end up using
// the PHP implementation.
}
}
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
// method 1. prior to PHP 5.3 this would call rand() on windows
hence the function_exists('class_alias') call.
// ie. class_alias is a function that was introduced in PHP 5.3
if (extension_loaded('mcrypt') &&
function_exists('class_alias')) {
return @mcrypt_create_iv($length);
}
// method 2. openssl_random_pseudo_bytes was introduced in PHP
5.3.0 but prior to PHP 5.3.4 there was,
// to quote <http://php.net/ChangeLog-5.php#5.3.4>,
"possible blocking behavior". as of 5.3.4
// openssl_random_pseudo_bytes and mcrypt_create_iv do the
exact same thing on Windows. ie. they both
// call php_win32_get_random_bytes():
//
//
https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/openssl/openssl.c#L5008
//
https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1392
//
// php_win32_get_random_bytes() is defined thusly:
//
//
https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/win32/winutil.c#L80
//
// we're calling it, all the same, in the off chance that
the mcrypt extension is not available
if (extension_loaded('openssl') &&
version_compare(PHP_VERSION, '5.3.4', '>=')) {
return openssl_random_pseudo_bytes($length);
}
} else {
// method 1. the fastest
if (extension_loaded('openssl')) {
return openssl_random_pseudo_bytes($length);
}
// method 2
static $fp = true;
if ($fp === true) {
// warning's will be output unles the error
suppression operator is used. errors such as
// "open_basedir restriction in effect",
"Permission denied", "No such file or directory", etc.
$fp = @fopen('/dev/urandom', 'rb');
}
if ($fp !== true && $fp !== false) { // surprisingly
faster than !is_bool() or is_resource()
$temp = fread($fp, $length);
if (strlen($temp) == $length) {
return $temp;
}
}
// method 3. pretty much does the same thing as method 2 per
the following url:
//
https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1391
// surprisingly slower than method 2. maybe that's because
mcrypt_create_iv does a bunch of error checking that we're
// not doing. regardless, this'll only be called if this
PHP script couldn't open /dev/urandom due to open_basedir
// restrictions or some such
if (extension_loaded('mcrypt')) {
return @mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
}
}
// at this point we have no choice but to use a pure-PHP CSPRNG
// cascade entropy across multiple PHP instances by fixing the
session and collecting all
// environmental variables, including the previous session data and
the current session
// data.
//
// mt_rand seeds itself by looking at the PID and the time, both of
which are (relatively)
// 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
// 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
// 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
// on the data sent by all users. one user requests the page and a
hash of their info is saved.
// another user visits the page and the serialization of their data
is utilized along with the
// server envirnment stuff and a hash of the previous http request
data (which itself utilizes
// a hash of the session data before that). certainly an attacker
should be assumed to have
// full control over his own http requests. he, however, is not
going to have control over
// everyone's http requests.
static $crypto = false, $v;
if ($crypto === false) {
// save old session data
$old_session_id = session_id();
$old_use_cookies = ini_get('session.use_cookies');
$old_session_cache_limiter = session_cache_limiter();
$_OLD_SESSION = isset($_SESSION) ? $_SESSION : false;
if ($old_session_id != '') {
session_write_close();
}
session_id(1);
ini_set('session.use_cookies', 0);
session_cache_limiter('');
session_start();
$v = $seed = $_SESSION['seed'] = pack('H*',
sha1(
(isset($_SERVER) ? phpseclib_safe_serialize($_SERVER) :
'') .
(isset($_POST) ? phpseclib_safe_serialize($_POST) :
'') .
(isset($_GET) ? phpseclib_safe_serialize($_GET) :
'') .
(isset($_COOKIE) ? phpseclib_safe_serialize($_COOKIE) :
'') .
phpseclib_safe_serialize($GLOBALS) .
phpseclib_safe_serialize($_SESSION) .
phpseclib_safe_serialize($_OLD_SESSION)
));
if (!isset($_SESSION['count'])) {
$_SESSION['count'] = 0;
}
$_SESSION['count']++;
session_write_close();
// restore old session data
if ($old_session_id != '') {
session_id($old_session_id);
session_start();
ini_set('session.use_cookies', $old_use_cookies);
session_cache_limiter($old_session_cache_limiter);
} else {
if ($_OLD_SESSION !== false) {
$_SESSION = $_OLD_SESSION;
unset($_OLD_SESSION);
} else {
unset($_SESSION);
}
}
// in SSH2 a shared secret and an exchange hash are generated
through the key exchange process.
// the IV client to server is the hash of that
"nonce" with the letter A and for the encryption key it's
the letter C.
// if the hash doesn't produce enough a key or an IV
that's long enough concat successive hashes of the
// original hash and the current hash. we'll be emulating
that. for more info see the following URL:
//
// http://tools.ietf.org/html/rfc4253#section-7.2
//
// see the is_string($crypto) part for an example of how to
expand the keys
$key = pack('H*', sha1($seed . 'A'));
$iv = pack('H*', sha1($seed . 'C'));
// ciphers are used as per the nist.gov link below. also, see
this link:
//
//
http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives
switch (true) {
case class_exists('\phpseclib\Crypt\AES'):
$crypto = new AES(Base::MODE_CTR);
break;
case class_exists('\phpseclib\Crypt\Twofish'):
$crypto = new Twofish(Base::MODE_CTR);
break;
case class_exists('\phpseclib\Crypt\Blowfish'):
$crypto = new Blowfish(Base::MODE_CTR);
break;
case class_exists('\phpseclib\Crypt\TripleDES'):
$crypto = new TripleDES(Base::MODE_CTR);
break;
case class_exists('\phpseclib\Crypt\DES'):
$crypto = new DES(Base::MODE_CTR);
break;
case class_exists('\phpseclib\Crypt\RC4'):
$crypto = new RC4();
break;
default:
user_error(__CLASS__ . ' requires at least one
symmetric cipher be loaded');
return false;
}
$crypto->setKey($key);
$crypto->setIV($iv);
$crypto->enableContinuousBuffer();
}
//return $crypto->encrypt(str_repeat("\0", $length));
// the following is based off of ANSI X9.31:
//
// http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf
//
// OpenSSL uses that same standard for it's random numbers:
//
//
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")
$result = '';
while (strlen($result) < $length) {
$i = $crypto->encrypt(microtime()); // strlen(microtime())
== 21
$r = $crypto->encrypt($i ^ $v); // strlen($v) == 20
$v = $crypto->encrypt($r ^ $i); // strlen($r) == 20
$result.= $r;
}
return substr($result, 0, $length);
}
}
if (!function_exists('phpseclib_safe_serialize')) {
/**
* Safely serialize variables
*
* If a class has a private __sleep() method it'll give a fatal
error on PHP 5.2 and earlier.
* PHP 5.3 will emit a warning.
*
* @param mixed $arr
* @access public
*/
function phpseclib_safe_serialize(&$arr)
{
if (is_object($arr)) {
return '';
}
if (!is_array($arr)) {
return serialize($arr);
}
// prevent circular array recursion
if (isset($arr['__phpseclib_marker'])) {
return '';
}
$safearr = array();
$arr['__phpseclib_marker'] = true;
foreach (array_keys($arr) as $key) {
// do not recurse on the '__phpseclib_marker' key
itself, for smaller memory usage
if ($key !== '__phpseclib_marker') {
$safearr[$key] = phpseclib_safe_serialize($arr[$key]);
}
}
unset($arr['__phpseclib_marker']);
return serialize($safearr);
}
}
phpseclib/phpseclib/Crypt/RC2.php000064400000054020151161207740012673
0ustar00<?php
/**
* Pure-PHP implementation of RC2.
*
* Uses mcrypt, if available, and an internal implementation, otherwise.
*
* PHP version 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 'vendor/autoload.php';
*
* $rc2 = new \phpseclib\Crypt\RC2();
*
* $rc2->setKey('abcdefgh');
*
* $plaintext = str_repeat('a', 1024);
*
* echo $rc2->decrypt($rc2->encrypt($plaintext));
* ?>
* </code>
*
* @category Crypt
* @package RC2
* @author Patrick Monnerat <pm@datasphere.ch>
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Pure-PHP implementation of RC2.
*
* @package RC2
* @access public
*/
class RC2 extends Base
{
/**
* Block Length of the cipher
*
* @see \phpseclib\Crypt\Base::block_size
* @var int
* @access private
*/
var $block_size = 8;
/**
* The Key
*
* @see \phpseclib\Crypt\Base::key
* @see self::setKey()
* @var string
* @access private
*/
var $key;
/**
* The Original (unpadded) Key
*
* @see \phpseclib\Crypt\Base::key
* @see self::setKey()
* @see self::encrypt()
* @see self::decrypt()
* @var string
* @access private
*/
var $orig_key;
/**
* Don't truncate / null pad key
*
* @see \phpseclib\Crypt\Base::_clearBuffers()
* @var bool
* @access private
*/
var $skip_key_adjustment = true;
/**
* Key Length (in bytes)
*
* @see \phpseclib\Crypt\RC2::setKeyLength()
* @var int
* @access private
*/
var $key_length = 16; // = 128 bits
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib\Crypt\Base::cipher_name_mcrypt
* @var string
* @access private
*/
var $cipher_name_mcrypt = 'rc2';
/**
* Optimizing value while CFB-encrypting
*
* @see \phpseclib\Crypt\Base::cfb_init_len
* @var int
* @access private
*/
var $cfb_init_len = 500;
/**
* The key length in bits.
*
* @see self::setKeyLength()
* @see self::setKey()
* @var int
* @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 length in bits.
*
* @see self::isValidEnine()
* @see self::setKey()
* @var int
* @access private
* @internal Should be in range [1..1024].
*/
var $current_key_length;
/**
* The Key Schedule
*
* @see self::_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 self::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 self::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
);
/**
* Test for engine validity
*
* This is mainly just a wrapper to set things up for
\phpseclib\Crypt\Base::isValidEngine()
*
* @see \phpseclib\Crypt\Base::__construct()
* @param int $engine
* @access public
* @return bool
*/
function isValidEngine($engine)
{
switch ($engine) {
case self::ENGINE_OPENSSL:
if ($this->current_key_length != 128 ||
strlen($this->orig_key) < 16) {
return false;
}
$this->cipher_name_openssl_ecb = 'rc2-ecb';
$this->cipher_name_openssl = 'rc2-' .
$this->_openssl_translate_mode();
}
return parent::isValidEngine($engine);
}
/**
* Sets the key length.
*
* Valid key lengths are 8 to 1024.
* Calling this function after setting the key has no effect until the
next
* \phpseclib\Crypt\RC2::setKey() call.
*
* @access public
* @param int $length in bits
*/
function setKeyLength($length)
{
if ($length < 8) {
$this->default_key_length = 1;
} elseif ($length > 1024) {
$this->default_key_length = 128;
} else {
$this->default_key_length = $length;
}
$this->current_key_length = $this->default_key_length;
parent::setKeyLength($length);
}
/**
* Returns the current key length
*
* @access public
* @return int
*/
function getKeyLength()
{
return $this->current_key_length;
}
/**
* Sets the key.
*
* Keys can be of any length. RC2, itself, uses 8 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 \phpseclib\Crypt\Base::setKey()
* @access public
* @param string $key
* @param int $t1 optional Effective key length in bits.
*/
function setKey($key, $t1 = 0)
{
$this->orig_key = $key;
if ($t1 <= 0) {
$t1 = $this->default_key_length;
} elseif ($t1 > 1024) {
$t1 = 1024;
}
$this->current_key_length = $t1;
// 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 message.
*
* Mostly a wrapper for \phpseclib\Crypt\Base::encrypt, with some
additional OpenSSL handling code
*
* @see self::decrypt()
* @access public
* @param string $plaintext
* @return string $ciphertext
*/
function encrypt($plaintext)
{
if ($this->engine == self::ENGINE_OPENSSL) {
$temp = $this->key;
$this->key = $this->orig_key;
$result = parent::encrypt($plaintext);
$this->key = $temp;
return $result;
}
return parent::encrypt($plaintext);
}
/**
* Decrypts a message.
*
* Mostly a wrapper for \phpseclib\Crypt\Base::decrypt, with some
additional OpenSSL handling code
*
* @see self::encrypt()
* @access public
* @param string $ciphertext
* @return string $plaintext
*/
function decrypt($ciphertext)
{
if ($this->engine == self::ENGINE_OPENSSL) {
$temp = $this->key;
$this->key = $this->orig_key;
$result = parent::decrypt($ciphertext);
$this->key = $temp;
return $result;
}
return parent::decrypt($ciphertext);
}
/**
* Encrypts a block
*
* @see \phpseclib\Crypt\Base::_encryptBlock()
* @see \phpseclib\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 \phpseclib\Crypt\Base::_decryptBlock()
* @see \phpseclib\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);
}
/**
* Setup the \phpseclib\Crypt\Base::ENGINE_MCRYPT $engine
*
* @see \phpseclib\Crypt\Base::_setupMcrypt()
* @access private
*/
function _setupMcrypt()
{
if (!isset($this->key)) {
$this->setKey('');
}
parent::_setupMcrypt();
}
/**
* Creates the key schedule
*
* @see \phpseclib\Crypt\Base::_setupKey()
* @access private
*/
function _setupKey()
{
if (!isset($this->key)) {
$this->setKey('');
}
// Key has already been expanded in \phpseclib\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 \phpseclib\Crypt\Base::_setupInlineCrypt()
* @access private
*/
function _setupInlineCrypt()
{
$lambda_functions =& self::_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.
// (Currently, for Crypt_RC2, one generated $lambda_function cost
on php5.5@32bit ~60kb unfreeable mem and ~100kb on php5.5@64bit)
$gen_hi_opt_code = (bool)(count($lambda_functions) < 10);
// Generation of a unique hash for our generated code
$code_hash = "Crypt_RC2, {$this->mode}";
if ($gen_hi_opt_code) {
$code_hash = str_pad($code_hash, 32) .
$this->_hashInlineCryptFunction($this->key);
}
// 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;';
switch (true) {
case $gen_hi_opt_code:
$keys = $this->keys;
default:
$keys = array();
foreach ($this->keys as $k => $v) {
$keys[$k] = '$keys[' . $k .
']';
}
}
// $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];
}
}
phpseclib/phpseclib/Crypt/RC4.php000064400000021062151161207740012675
0ustar00<?php
/**
* Pure-PHP implementation of RC4.
*
* Uses mcrypt, if available, and an internal implementation, otherwise.
*
* PHP version 5
*
* Useful resources are as follows:
*
* - {@link
http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt
ARCFOUR Algorithm}
* - {@link http://en.wikipedia.org/wiki/RC4 - Wikipedia: RC4}
*
* RC4 is also known as ARCFOUR or ARC4. The reason is elaborated upon at
Wikipedia. This class is named RC4 and not
* ARCFOUR or ARC4 because RC4 is how it is referred to in the SSH1
specification.
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $rc4 = new \phpseclib\Crypt\RC4();
*
* $rc4->setKey('abcdefgh');
*
* $size = 10 * 1024;
* $plaintext = '';
* for ($i = 0; $i < $size; $i++) {
* $plaintext.= 'a';
* }
*
* echo $rc4->decrypt($rc4->encrypt($plaintext));
* ?>
* </code>
*
* @category Crypt
* @package RC4
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Pure-PHP implementation of RC4.
*
* @package RC4
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class RC4 extends Base
{
/**#@+
* @access private
* @see \phpseclib\Crypt\RC4::_crypt()
*/
const ENCRYPT = 0;
const DECRYPT = 1;
/**#@-*/
/**
* Block Length of the cipher
*
* RC4 is a stream cipher
* so we the block_size to 0
*
* @see \phpseclib\Crypt\Base::block_size
* @var int
* @access private
*/
var $block_size = 0;
/**
* Key Length (in bytes)
*
* @see \phpseclib\Crypt\RC4::setKeyLength()
* @var int
* @access private
*/
var $key_length = 128; // = 1024 bits
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib\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 \phpseclib\Crypt\Base::inline_crypt
* @var mixed
* @access private
*/
var $use_inline_crypt = false; // currently not available
/**
* The Key
*
* @see self::setKey()
* @var string
* @access private
*/
var $key;
/**
* The Key Stream for decryption and encryption
*
* @see self::setKey()
* @var array
* @access private
*/
var $stream;
/**
* Default Constructor.
*
* Determines whether or not the mcrypt extension should be used.
*
* @see \phpseclib\Crypt\Base::__construct()
* @return \phpseclib\Crypt\RC4
* @access public
*/
function __construct()
{
parent::__construct(Base::MODE_STREAM);
}
/**
* Test for engine validity
*
* This is mainly just a wrapper to set things up for
\phpseclib\Crypt\Base::isValidEngine()
*
* @see \phpseclib\Crypt\Base::__construct()
* @param int $engine
* @access public
* @return bool
*/
function isValidEngine($engine)
{
if ($engine == Base::ENGINE_OPENSSL) {
if (version_compare(PHP_VERSION, '5.3.7') >= 0) {
$this->cipher_name_openssl = 'rc4-40';
} else {
switch (strlen($this->key)) {
case 5:
$this->cipher_name_openssl = 'rc4-40';
break;
case 8:
$this->cipher_name_openssl = 'rc4-64';
break;
case 16:
$this->cipher_name_openssl = 'rc4';
break;
default:
return false;
}
}
}
return parent::isValidEngine($engine);
}
/**
* Dummy function.
*
* 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
*/
function setIV($iv)
{
}
/**
* Sets the key length
*
* Keys can be between 1 and 256 bytes long.
*
* @access public
* @param int $length
*/
function setKeyLength($length)
{
if ($length < 8) {
$this->key_length = 1;
} elseif ($length > 2048) {
$this->key_length = 256;
} else {
$this->key_length = $length >> 3;
}
parent::setKeyLength($length);
}
/**
* Encrypts a message.
*
* @see \phpseclib\Crypt\Base::decrypt()
* @see self::_crypt()
* @access public
* @param string $plaintext
* @return string $ciphertext
*/
function encrypt($plaintext)
{
if ($this->engine != Base::ENGINE_INTERNAL) {
return parent::encrypt($plaintext);
}
return $this->_crypt($plaintext, self::ENCRYPT);
}
/**
* Decrypts a message.
*
* $this->decrypt($this->encrypt($plaintext)) ==
$this->encrypt($this->encrypt($plaintext)).
* At least if the continuous buffer is disabled.
*
* @see \phpseclib\Crypt\Base::encrypt()
* @see self::_crypt()
* @access public
* @param string $ciphertext
* @return string $plaintext
*/
function decrypt($ciphertext)
{
if ($this->engine != Base::ENGINE_INTERNAL) {
return parent::decrypt($ciphertext);
}
return $this->_crypt($ciphertext, self::DECRYPT);
}
/**
* Encrypts a block
*
* @access private
* @param string $in
*/
function _encryptBlock($in)
{
// RC4 does not utilize this method
}
/**
* Decrypts a block
*
* @access private
* @param string $in
*/
function _decryptBlock($in)
{
// RC4 does not utilize this method
}
/**
* Setup the key (expansion)
*
* @see \phpseclib\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[self::DECRYPT] = $this->stream[self::ENCRYPT] =
array(
0, // index $i
0, // index $j
$keyStream
);
}
/**
* Encrypts or decrypts a message.
*
* @see self::encrypt()
* @see self::decrypt()
* @access private
* @param string $text
* @param int $mode
* @return string $text
*/
function _crypt($text, $mode)
{
if ($this->changed) {
$this->_setup();
$this->changed = false;
}
$stream = &$this->stream[$mode];
if ($this->continuousBuffer) {
$i = &$stream[0];
$j = &$stream[1];
$keyStream = &$stream[2];
} else {
$i = $stream[0];
$j = $stream[1];
$keyStream = $stream[2];
}
$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;
}
}
phpseclib/phpseclib/Crypt/Rijndael.php000064400000121465151161207740014045
0ustar00<?php
/**
* Pure-PHP implementation of Rijndael.
*
* Uses mcrypt, if available/possible, and an internal implementation,
otherwise.
*
* PHP version 5
*
* If {@link self::setBlockLength() setBlockLength()} isn't called,
it'll be assumed to be 128 bits. If
* {@link self::setKeyLength() setKeyLength()} isn't called,
it'll be calculated from
* {@link self::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 192-bits and 192 bits will be the
key length until
* {@link self::setKey() setKey()} is called, again, at which point,
it'll be recalculated.
*
* Not all Rijndael implementations may support 160-bits or 224-bits as the
block length / key length. mcrypt, for example,
* does not. AES, itself, only supports block lengths of 128 and key
lengths of 128, 192, and 256.
* {@link
http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=10
Rijndael-ammended.pdf#page=10} defines the
* algorithm for block lengths of 192 and 256 but not for block lengths /
key lengths of 160 and 224. Indeed, 160 and 224
* are first defined as valid key / block lengths in
* {@link
http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=44
Rijndael-ammended.pdf#page=44}:
* Extensions: Other block and Cipher Key lengths.
* Note: Use of 160/224-bit Keys must be explicitly set by
setKeyLength(160) respectively setKeyLength(224).
*
* {@internal The variable names are the same as those in
* {@link
http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf#page=10
fips-197.pdf#page=10}.}}
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $rijndael = new \phpseclib\Crypt\Rijndael();
*
* $rijndael->setKey('abcdefghijklmnop');
*
* $size = 10 * 1024;
* $plaintext = '';
* for ($i = 0; $i < $size; $i++) {
* $plaintext.= 'a';
* }
*
* echo $rijndael->decrypt($rijndael->encrypt($plaintext));
* ?>
* </code>
*
* @category Crypt
* @package Rijndael
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2008 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Pure-PHP implementation of Rijndael.
*
* @package Rijndael
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class Rijndael extends Base
{
/**
* The mcrypt specific name of the cipher
*
* Mcrypt is useable for 128/192/256-bit $block_size/$key_length. For
160/224 not.
* \phpseclib\Crypt\Rijndael determines automatically whether mcrypt is
useable
* or not for the current $block_size/$key_length.
* In case of, $cipher_name_mcrypt will be set dynamically at run time
accordingly.
*
* @see \phpseclib\Crypt\Base::cipher_name_mcrypt
* @see \phpseclib\Crypt\Base::engine
* @see self::isValidEngine()
* @var string
* @access private
*/
var $cipher_name_mcrypt = 'rijndael-128';
/**
* The default salt used by setPassword()
*
* @see \phpseclib\Crypt\Base::password_default_salt
* @see \phpseclib\Crypt\Base::setPassword()
* @var string
* @access private
*/
var $password_default_salt = 'phpseclib';
/**
* The Key Schedule
*
* @see self::_setup()
* @var array
* @access private
*/
var $w;
/**
* The Inverse Key Schedule
*
* @see self::_setup()
* @var array
* @access private
*/
var $dw;
/**
* The Block Length divided by 32
*
* @see self::setBlockLength()
* @var int
* @access private
* @internal The max value is 256 / 32 = 8, the min value is 128 / 32 =
4. Exists in conjunction with $block_size
* because the encryption / decryption / key schedule creation
requires this number and not $block_size. We could
* derive this from $block_size or vice versa, but that'd mean
we'd have to do multiple shift operations, so in lieu
* of that, we'll just precompute it once.
*/
var $Nb = 4;
/**
* The Key Length (in bytes)
*
* @see self::setKeyLength()
* @var int
* @access private
* @internal The max value is 256 / 8 = 32, the min value is 128 / 8 =
16. Exists in conjunction with $Nk
* because the encryption / decryption / key schedule creation
requires this number and not $key_length. We could
* derive this from $key_length or vice versa, but that'd mean
we'd have to do multiple shift operations, so in lieu
* of that, we'll just precompute it once.
*/
var $key_length = 16;
/**
* The Key Length divided by 32
*
* @see self::setKeyLength()
* @var int
* @access private
* @internal The max value is 256 / 32 = 8, the min value is 128 / 32 =
4
*/
var $Nk = 4;
/**
* The Number of Rounds
*
* @var int
* @access private
* @internal The max value is 14, the min value is 10.
*/
var $Nr;
/**
* Shift offsets
*
* @var array
* @access private
*/
var $c;
/**
* Holds the last used key- and block_size information
*
* @var array
* @access private
*/
var $kl;
/**
* 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
* 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
* and the most (if not all) implementations are not able using
160/224-bit keys but round/pad them up to
* 192/256 bits as, for example, mcrypt will do.
*
* That said, if you want be compatible with other Rijndael and
AES implementations,
* you should not setKeyLength(160) or setKeyLength(224).
*
* Additional: In case of 160- and 224-bit keys, phpseclib will/can,
for that reason, not use
* the mcrypt php extension, even if available.
* This results then in slower encryption.
*
* @access public
* @param int $length
*/
function setKeyLength($length)
{
switch (true) {
case $length <= 128:
$this->key_length = 16;
break;
case $length <= 160:
$this->key_length = 20;
break;
case $length <= 192:
$this->key_length = 24;
break;
case $length <= 224:
$this->key_length = 28;
break;
default:
$this->key_length = 32;
}
parent::setKeyLength($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
* 128. If the length is greater than 128 and invalid, it will be
rounded down to the closest valid amount.
*
* @access public
* @param int $length
*/
function setBlockLength($length)
{
$length >>= 5;
if ($length > 8) {
$length = 8;
} elseif ($length < 4) {
$length = 4;
}
$this->Nb = $length;
$this->block_size = $length << 2;
$this->changed = true;
$this->_setEngine();
}
/**
* Test for engine validity
*
* This is mainly just a wrapper to set things up for
\phpseclib\Crypt\Base::isValidEngine()
*
* @see \phpseclib\Crypt\Base::__construct()
* @param int $engine
* @access public
* @return bool
*/
function isValidEngine($engine)
{
switch ($engine) {
case self::ENGINE_OPENSSL:
if ($this->block_size != 16) {
return false;
}
$this->cipher_name_openssl_ecb = 'aes-' .
($this->key_length << 3) . '-ecb';
$this->cipher_name_openssl = 'aes-' .
($this->key_length << 3) . '-' .
$this->_openssl_translate_mode();
break;
case self::ENGINE_MCRYPT:
$this->cipher_name_mcrypt = 'rijndael-' .
($this->block_size << 3);
if ($this->key_length % 8) { // is it a 160/224-bit key?
// mcrypt is not usable for them, only for
128/192/256-bit keys
return false;
}
}
return parent::isValidEngine($engine);
}
/**
* Encrypts a block
*
* @access private
* @param string $in
* @return string
*/
function _encryptBlock($in)
{
static $tables;
if (empty($tables)) {
$tables = &$this->_getTables();
}
$t0 = $tables[0];
$t1 = $tables[1];
$t2 = $tables[2];
$t3 = $tables[3];
$sbox = $tables[4];
$state = array();
$words = unpack('N*', $in);
$c = $this->c;
$w = $this->w;
$Nb = $this->Nb;
$Nr = $this->Nr;
// addRoundKey
$wc = $Nb - 1;
foreach ($words as $word) {
$state[] = $word ^ $w[++$wc];
}
// fips-197.pdf#page=19, "Figure 5. Pseudo Code for the
Cipher", states that this loop has four components -
// subBytes, shiftRows, mixColumns, and addRoundKey.
fips-197.pdf#page=30, "Implementation Suggestions Regarding
// Various Platforms" suggests that performs enhanced
implementations are described in Rijndael-ammended.pdf.
// Rijndael-ammended.pdf#page=20, "Implementation aspects /
32-bit processor", discusses such an optimization.
// Unfortunately, the description given there is not quite correct.
Per aes.spec.v316.pdf#page=19 [1],
// equation (7.4.7) is supposed to use addition instead of
subtraction, so we'll do that here, as well.
// [1]
http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf
$temp = array();
for ($round = 1; $round < $Nr; ++$round) {
$i = 0; // $c[0] == 0
$j = $c[1];
$k = $c[2];
$l = $c[3];
while ($i < $Nb) {
$temp[$i] = $t0[$state[$i] >> 24 & 0x000000FF] ^
$t1[$state[$j] >> 16 & 0x000000FF] ^
$t2[$state[$k] >> 8 & 0x000000FF] ^
$t3[$state[$l] & 0x000000FF] ^
$w[++$wc];
++$i;
$j = ($j + 1) % $Nb;
$k = ($k + 1) % $Nb;
$l = ($l + 1) % $Nb;
}
$state = $temp;
}
// subWord
for ($i = 0; $i < $Nb; ++$i) {
$state[$i] = $sbox[$state[$i] & 0x000000FF]
|
($sbox[$state[$i] >> 8 & 0x000000FF]
<< 8) |
($sbox[$state[$i] >> 16 & 0x000000FF]
<< 16) |
($sbox[$state[$i] >> 24 & 0x000000FF]
<< 24);
}
// shiftRows + addRoundKey
$i = 0; // $c[0] == 0
$j = $c[1];
$k = $c[2];
$l = $c[3];
while ($i < $Nb) {
$temp[$i] = ($state[$i] & 0xFF000000) ^
($state[$j] & 0x00FF0000) ^
($state[$k] & 0x0000FF00) ^
($state[$l] & 0x000000FF) ^
$w[$i];
++$i;
$j = ($j + 1) % $Nb;
$k = ($k + 1) % $Nb;
$l = ($l + 1) % $Nb;
}
switch ($Nb) {
case 8:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3], $temp[4], $temp[5], $temp[6], $temp[7]);
case 7:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3], $temp[4], $temp[5], $temp[6]);
case 6:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3], $temp[4], $temp[5]);
case 5:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3], $temp[4]);
default:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3]);
}
}
/**
* Decrypts a block
*
* @access private
* @param string $in
* @return string
*/
function _decryptBlock($in)
{
static $invtables;
if (empty($invtables)) {
$invtables = &$this->_getInvTables();
}
$dt0 = $invtables[0];
$dt1 = $invtables[1];
$dt2 = $invtables[2];
$dt3 = $invtables[3];
$isbox = $invtables[4];
$state = array();
$words = unpack('N*', $in);
$c = $this->c;
$dw = $this->dw;
$Nb = $this->Nb;
$Nr = $this->Nr;
// addRoundKey
$wc = $Nb - 1;
foreach ($words as $word) {
$state[] = $word ^ $dw[++$wc];
}
$temp = array();
for ($round = $Nr - 1; $round > 0; --$round) {
$i = 0; // $c[0] == 0
$j = $Nb - $c[1];
$k = $Nb - $c[2];
$l = $Nb - $c[3];
while ($i < $Nb) {
$temp[$i] = $dt0[$state[$i] >> 24 & 0x000000FF] ^
$dt1[$state[$j] >> 16 & 0x000000FF] ^
$dt2[$state[$k] >> 8 & 0x000000FF] ^
$dt3[$state[$l] & 0x000000FF] ^
$dw[++$wc];
++$i;
$j = ($j + 1) % $Nb;
$k = ($k + 1) % $Nb;
$l = ($l + 1) % $Nb;
}
$state = $temp;
}
// invShiftRows + invSubWord + addRoundKey
$i = 0; // $c[0] == 0
$j = $Nb - $c[1];
$k = $Nb - $c[2];
$l = $Nb - $c[3];
while ($i < $Nb) {
$word = ($state[$i] & 0xFF000000) |
($state[$j] & 0x00FF0000) |
($state[$k] & 0x0000FF00) |
($state[$l] & 0x000000FF);
$temp[$i] = $dw[$i] ^ ($isbox[$word & 0x000000FF]
|
($isbox[$word >> 8 &
0x000000FF] << 8) |
($isbox[$word >> 16 &
0x000000FF] << 16) |
($isbox[$word >> 24 &
0x000000FF] << 24));
++$i;
$j = ($j + 1) % $Nb;
$k = ($k + 1) % $Nb;
$l = ($l + 1) % $Nb;
}
switch ($Nb) {
case 8:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3], $temp[4], $temp[5], $temp[6], $temp[7]);
case 7:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3], $temp[4], $temp[5], $temp[6]);
case 6:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3], $temp[4], $temp[5]);
case 5:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3], $temp[4]);
default:
return pack('N*', $temp[0], $temp[1], $temp[2],
$temp[3]);
}
}
/**
* Setup the key (expansion)
*
* @see \phpseclib\Crypt\Base::_setupKey()
* @access private
*/
function _setupKey()
{
// Each number in $rcon is equal to the previous number multiplied
by two in Rijndael's finite field.
// See
http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse
static $rcon = array(0,
0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000,
0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000,
0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000,
0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000
);
if (isset($this->kl['key']) && $this->key
=== $this->kl['key'] && $this->key_length ===
$this->kl['key_length'] && $this->block_size ===
$this->kl['block_size']) {
// already expanded
return;
}
$this->kl = array('key' => $this->key,
'key_length' => $this->key_length, 'block_size'
=> $this->block_size);
$this->Nk = $this->key_length >> 2;
// see Rijndael-ammended.pdf#page=44
$this->Nr = max($this->Nk, $this->Nb) + 6;
// shift offsets for Nb = 5, 7 are defined in
Rijndael-ammended.pdf#page=44,
// "Table 8: Shift offsets in Shiftrow for the alternative
block lengths"
// shift offsets for Nb = 4, 6, 8 are defined in
Rijndael-ammended.pdf#page=14,
// "Table 2: Shift offsets for different block
lengths"
switch ($this->Nb) {
case 4:
case 5:
case 6:
$this->c = array(0, 1, 2, 3);
break;
case 7:
$this->c = array(0, 1, 2, 4);
break;
case 8:
$this->c = array(0, 1, 3, 4);
}
$w = array_values(unpack('N*words', $this->key));
$length = $this->Nb * ($this->Nr + 1);
for ($i = $this->Nk; $i < $length; $i++) {
$temp = $w[$i - 1];
if ($i % $this->Nk == 0) {
// according to
<http://php.net/language.types.integer>, "the size of an integer
is platform-dependent".
// on a 32-bit machine, it's 32-bits, and on a 64-bit
machine, it's 64-bits. on a 32-bit machine,
// 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit
machine, it equals 0xFFFFFFFF00. as such, doing 'and'
// with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is
unnecessary, but on a 64-bit machine, it is.
$temp = (($temp << 8) & 0xFFFFFF00) | (($temp
>> 24) & 0x000000FF); // rotWord
$temp = $this->_subWord($temp) ^ $rcon[$i /
$this->Nk];
} elseif ($this->Nk > 6 && $i % $this->Nk ==
4) {
$temp = $this->_subWord($temp);
}
$w[$i] = $w[$i - $this->Nk] ^ $temp;
}
// convert the key schedule from a vector of $Nb * ($Nr + 1) length
to a matrix with $Nr + 1 rows and $Nb columns
// and generate the inverse key schedule. more specifically,
// according to
<http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=23>
(section 5.3.3),
// "The key expansion for the Inverse Cipher is defined as
follows:
// 1. Apply the Key Expansion.
// 2. Apply InvMixColumn to all Round Keys except the first
and the last one."
// also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse
Cipher"
list($dt0, $dt1, $dt2, $dt3) = $this->_getInvTables();
$temp = $this->w = $this->dw = array();
for ($i = $row = $col = 0; $i < $length; $i++, $col++) {
if ($col == $this->Nb) {
if ($row == 0) {
$this->dw[0] = $this->w[0];
} else {
// subWord + invMixColumn + invSubWord = invMixColumn
$j = 0;
while ($j < $this->Nb) {
$dw = $this->_subWord($this->w[$row][$j]);
$temp[$j] = $dt0[$dw >> 24 & 0x000000FF]
^
$dt1[$dw >> 16 & 0x000000FF]
^
$dt2[$dw >> 8 & 0x000000FF]
^
$dt3[$dw & 0x000000FF];
$j++;
}
$this->dw[$row] = $temp;
}
$col = 0;
$row++;
}
$this->w[$row][$col] = $w[$i];
}
$this->dw[$row] = $this->w[$row];
// Converting to 1-dim key arrays (both ascending)
$this->dw = array_reverse($this->dw);
$w = array_pop($this->w);
$dw = array_pop($this->dw);
foreach ($this->w as $r => $wr) {
foreach ($wr as $c => $wc) {
$w[] = $wc;
$dw[] = $this->dw[$r][$c];
}
}
$this->w = $w;
$this->dw = $dw;
}
/**
* Performs S-Box substitutions
*
* @access private
* @param int $word
*/
function _subWord($word)
{
static $sbox;
if (empty($sbox)) {
list(, , , , $sbox) = $this->_getTables();
}
return $sbox[$word & 0x000000FF] |
($sbox[$word >> 8 & 0x000000FF] << 8) |
($sbox[$word >> 16 & 0x000000FF] << 16) |
($sbox[$word >> 24 & 0x000000FF] << 24);
}
/**
* Provides the mixColumns and sboxes tables
*
* @see self::_encryptBlock()
* @see self::_setupInlineCrypt()
* @see self::_subWord()
* @access private
* @return array &$tables
*/
function &_getTables()
{
static $tables;
if (empty($tables)) {
// according to
<http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=19>
(section 5.2.1),
// precomputed tables can be used in the mixColumns phase. in
that example, they're assigned t0...t3, so
// those are the names we'll use.
$t3 = array_map('intval', array(
// with array_map('intval', ...) we ensure we
have only int's and not
// some slower floats converted by php automatically on
high values
0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF,
0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7,
0xD7D762B5, 0xABABE64D, 0x76769AEC,
0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF,
0x5959EBB2, 0x4747C98E, 0xF0F00BFB,
0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23,
0xA4A4F753, 0x727296E4, 0xC0C05B9B,
0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C,
0x3F3F417E, 0xF7F702F5, 0xCCCC4F83,
0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2,
0xD8D873AB, 0x31315362, 0x15153F2A,
0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830,
0x9696A137, 0x05050F0A, 0x9A9AB52F,
0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD,
0x2727694E, 0xB2B2CD7F, 0x75759FEA,
0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36,
0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B,
0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52,
0xE3E33EDD, 0x2F2F715E, 0x84849713,
0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040,
0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6,
0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94,
0x4C4CD498, 0x5858E8B0, 0xCFCF4A85,
0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586,
0x4D4DD79A, 0x33335566, 0x85859411,
0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0,
0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B,
0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F,
0x9D9DBC21, 0x38384870, 0xF5F504F1,
0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020,
0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF,
0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE,
0x9797A235, 0x4444CC88, 0x1717392E,
0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8,
0x5D5DE7BA, 0x19192B32, 0x737395E6,
0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644,
0x2A2A7E54, 0x9090AB3B, 0x8888830B,
0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7,
0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD,
0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92,
0x06060A0C, 0x24246C48, 0x5C5CE4B8,
0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839,
0x9595A431, 0xE4E437D3, 0x79798BF2,
0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01,
0xD5D564B1, 0x4E4ED29C, 0xA9A9E049,
0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA,
0x7A7A8EF4, 0xAEAEE947, 0x08081810,
0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438,
0xA6A6F157, 0xB4B4C773, 0xC6C65197,
0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96,
0xBDBDDC61, 0x8B8B860D, 0x8A8A850F,
0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890,
0x03030506, 0xF6F601F7, 0x0E0E121C,
0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117,
0xC1C15899, 0x1D1D273A, 0x9E9EB927,
0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2,
0xD9D970A9, 0x8E8E8907, 0x9494A733,
0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987,
0x5555FFAA, 0x28287850, 0xDFDF7AA5,
0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65,
0xE6E631D7, 0x4242C684, 0x6868B8D0,
0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B,
0x5454FCA8, 0xBBBBD66D, 0x16163A2C
));
foreach ($t3 as $t3i) {
$t0[] = (($t3i << 24) & 0xFF000000) | (($t3i
>> 8) & 0x00FFFFFF);
$t1[] = (($t3i << 16) & 0xFFFF0000) | (($t3i
>> 16) & 0x0000FFFF);
$t2[] = (($t3i << 8) & 0xFFFFFF00) | (($t3i
>> 24) & 0x000000FF);
}
$tables = array(
// The Precomputed mixColumns tables t0 - t3
$t0,
$t1,
$t2,
$t3,
// The SubByte S-Box
array(
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30,
0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD,
0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34,
0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07,
0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52,
0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A,
0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45,
0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC,
0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4,
0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46,
0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2,
0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C,
0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8,
0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61,
0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B,
0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41,
0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
)
);
}
return $tables;
}
/**
* Provides the inverse mixColumns and inverse sboxes tables
*
* @see self::_decryptBlock()
* @see self::_setupInlineCrypt()
* @see self::_setupKey()
* @access private
* @return array &$tables
*/
function &_getInvTables()
{
static $tables;
if (empty($tables)) {
$dt3 = array_map('intval', array(
0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B,
0x9D45F11F, 0xFA58ABAC, 0xE303934B,
0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F,
0x2ACBD7C5, 0x35448026, 0x62A38FB5,
0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3,
0x4CF01281, 0x4697A38D, 0xD3F9C66B,
0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4,
0x7421D358, 0xE0692949, 0xC9C8448E,
0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE,
0x88AD17F0, 0x20AC66C9, 0xCE3AB47D,
0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1,
0x6BAE84BB, 0x81A01CFE, 0x082B94F9,
0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB,
0x4B02E272, 0x1F8F57E3, 0x55AB2A66,
0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230,
0xBFA5B223, 0x036ABA02, 0x16825CED,
0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65,
0x05BED506, 0x34621FD1, 0xA6FE8AC4,
0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B,
0x60EFAA40, 0x719F065E, 0x6E1051BD,
0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591,
0xC45D0571, 0x06D46F04, 0x5015FF60,
0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0,
0x898B8807, 0x195B38E7, 0xC8EEDB79,
0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309,
0x2BED4832, 0x1170AC1E, 0x5A724E6C,
0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A,
0x5CA62168, 0x5B54D19B, 0x362E3A24,
0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80,
0xDC20A261, 0x774B695A, 0x121A161C,
0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E,
0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814,
0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7,
0x72F5BC5C, 0x663BC544, 0xFB7E345B,
0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7,
0x63851042, 0x97224013, 0xC6112084,
0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D,
0xB230F3DC, 0x8652EC0D, 0xC1E3D077,
0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8,
0xF03F1AA0, 0x7D2CD856, 0x3390EF22,
0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6,
0x7ADE28A5, 0xB78E26DA, 0xADBFA43F,
0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6,
0xD8B8E890, 0x39F75E2E, 0xC3AFF582,
0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8,
0x187DA710, 0x9C636EE8, 0x3BBB7BDB,
0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6,
0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF,
0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31,
0x3F23312A, 0xA59430C6, 0xA266C035,
0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1,
0xECDAF741, 0xCD500E7F, 0x91F62F17,
0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E,
0x6A881B4C, 0x2C1FB8C1, 0x65517F46,
0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3,
0xDBD25292, 0x105633E9, 0xD647136D,
0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE,
0x61C935B7, 0x1CE5EDE1, 0x47B13C7A,
0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53,
0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678,
0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216,
0xE2250CBC, 0x3C498B28, 0x0D9541FF,
0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B,
0x32B670D5, 0x6C5C7448, 0xB85742D0
));
foreach ($dt3 as $dt3i) {
$dt0[] = (($dt3i << 24) & 0xFF000000) | (($dt3i
>> 8) & 0x00FFFFFF);
$dt1[] = (($dt3i << 16) & 0xFFFF0000) | (($dt3i
>> 16) & 0x0000FFFF);
$dt2[] = (($dt3i << 8) & 0xFFFFFF00) | (($dt3i
>> 24) & 0x000000FF);
};
$tables = array(
// The Precomputed inverse mixColumns tables dt0 - dt3
$dt0,
$dt1,
$dt2,
$dt3,
// The inverse SubByte S-Box
array(
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF,
0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34,
0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE,
0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76,
0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4,
0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E,
0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7,
0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1,
0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97,
0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2,
0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F,
0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A,
0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1,
0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D,
0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8,
0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1,
0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
)
);
}
return $tables;
}
/**
* Setup the performance-optimized function for de/encrypt()
*
* @see \phpseclib\Crypt\Base::_setupInlineCrypt()
* @access private
*/
function _setupInlineCrypt()
{
// Note: _setupInlineCrypt() will be called only if
$this->changed === true
// So here we are'nt under the same heavy timing-stress as we
are in _de/encryptBlock() or de/encrypt().
// However...the here generated function- $code, stored as php
callback in $this->inline_crypt, must work as fast as even possible.
$lambda_functions =& self::_getLambdaFunctions();
// We create max. 10 hi-optimized code for memory reason. Means:
For each $key one ultra fast inline-crypt function.
// (Currently, for Crypt_Rijndael/AES, one generated
$lambda_function cost on php5.5@32bit ~80kb unfreeable mem and ~130kb on
php5.5@64bit)
// After that, we'll still create very fast optimized code but
not the hi-ultimative code, for each $mode one.
$gen_hi_opt_code = (bool)(count($lambda_functions) < 10);
// Generation of a uniqe hash for our generated code
$code_hash = "Crypt_Rijndael, {$this->mode},
{$this->Nr}, {$this->Nb}";
if ($gen_hi_opt_code) {
$code_hash = str_pad($code_hash, 32) .
$this->_hashInlineCryptFunction($this->key);
}
if (!isset($lambda_functions[$code_hash])) {
switch (true) {
case $gen_hi_opt_code:
// The hi-optimized $lambda_functions will use the
key-words hardcoded for better performance.
$w = $this->w;
$dw = $this->dw;
$init_encrypt = '';
$init_decrypt = '';
break;
default:
for ($i = 0, $cw = count($this->w); $i < $cw;
++$i) {
$w[] = '$w[' . $i . ']';
$dw[] = '$dw[' . $i . ']';
}
$init_encrypt = '$w = $self->w;';
$init_decrypt = '$dw = $self->dw;';
}
$Nr = $this->Nr;
$Nb = $this->Nb;
$c = $this->c;
// Generating encrypt code:
$init_encrypt.= '
static $tables;
if (empty($tables)) {
$tables = &$self->_getTables();
}
$t0 = $tables[0];
$t1 = $tables[1];
$t2 = $tables[2];
$t3 = $tables[3];
$sbox = $tables[4];
';
$s = 'e';
$e = 's';
$wc = $Nb - 1;
// Preround: addRoundKey
$encrypt_block = '$in = unpack("N*",
$in);'."\n";
for ($i = 0; $i < $Nb; ++$i) {
$encrypt_block .= '$s'.$i.' = $in['.($i
+ 1).'] ^ '.$w[++$wc].";\n";
}
// Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
for ($round = 1; $round < $Nr; ++$round) {
list($s, $e) = array($e, $s);
for ($i = 0; $i < $Nb; ++$i) {
$encrypt_block.=
'$'.$e.$i.' =
$t0[($'.$s.$i .'
>> 24) & 0xff] ^
$t1[($'.$s.(($i + $c[1]) % $Nb).'
>> 16) & 0xff] ^
$t2[($'.$s.(($i + $c[2]) % $Nb).'
>> 8) & 0xff] ^
$t3[ $'.$s.(($i + $c[3]) % $Nb).'
& 0xff] ^
'.$w[++$wc].";\n";
}
}
// Finalround: subWord + shiftRows + addRoundKey
for ($i = 0; $i < $Nb; ++$i) {
$encrypt_block.=
'$'.$e.$i.' =
$sbox[ $'.$e.$i.' & 0xff]
|
($sbox[($'.$e.$i.' >> 8) & 0xff]
<< 8) |
($sbox[($'.$e.$i.' >> 16) & 0xff]
<< 16) |
($sbox[($'.$e.$i.' >> 24) & 0xff]
<< 24);'."\n";
}
$encrypt_block .= '$in =
pack("N*"'."\n";
for ($i = 0; $i < $Nb; ++$i) {
$encrypt_block.= ',
($'.$e.$i .' &
'.((int)0xFF000000).') ^
($'.$e.(($i + $c[1]) % $Nb).' &
0x00FF0000 ) ^
($'.$e.(($i + $c[2]) % $Nb).' &
0x0000FF00 ) ^
($'.$e.(($i + $c[3]) % $Nb).' &
0x000000FF ) ^
'.$w[$i]."\n";
}
$encrypt_block .= ');';
// Generating decrypt code:
$init_decrypt.= '
static $invtables;
if (empty($invtables)) {
$invtables = &$self->_getInvTables();
}
$dt0 = $invtables[0];
$dt1 = $invtables[1];
$dt2 = $invtables[2];
$dt3 = $invtables[3];
$isbox = $invtables[4];
';
$s = 'e';
$e = 's';
$wc = $Nb - 1;
// Preround: addRoundKey
$decrypt_block = '$in = unpack("N*",
$in);'."\n";
for ($i = 0; $i < $Nb; ++$i) {
$decrypt_block .= '$s'.$i.' = $in['.($i
+ 1).'] ^ '.$dw[++$wc].';'."\n";
}
// Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
for ($round = 1; $round < $Nr; ++$round) {
list($s, $e) = array($e, $s);
for ($i = 0; $i < $Nb; ++$i) {
$decrypt_block.=
'$'.$e.$i.' =
$dt0[($'.$s.$i .'
>> 24) & 0xff] ^
$dt1[($'.$s.(($Nb + $i - $c[1]) % $Nb).'
>> 16) & 0xff] ^
$dt2[($'.$s.(($Nb + $i - $c[2]) % $Nb).'
>> 8) & 0xff] ^
$dt3[ $'.$s.(($Nb + $i - $c[3]) % $Nb).'
& 0xff] ^
'.$dw[++$wc].";\n";
}
}
// Finalround: subWord + shiftRows + addRoundKey
for ($i = 0; $i < $Nb; ++$i) {
$decrypt_block.=
'$'.$e.$i.' =
$isbox[ $'.$e.$i.' & 0xff]
|
($isbox[($'.$e.$i.' >> 8) & 0xff]
<< 8) |
($isbox[($'.$e.$i.' >> 16) & 0xff]
<< 16) |
($isbox[($'.$e.$i.' >> 24) & 0xff]
<< 24);'."\n";
}
$decrypt_block .= '$in =
pack("N*"'."\n";
for ($i = 0; $i < $Nb; ++$i) {
$decrypt_block.= ',
($'.$e.$i. ' &
'.((int)0xFF000000).') ^
($'.$e.(($Nb + $i - $c[1]) % $Nb).' &
0x00FF0000 ) ^
($'.$e.(($Nb + $i - $c[2]) % $Nb).' &
0x0000FF00 ) ^
($'.$e.(($Nb + $i - $c[3]) % $Nb).' &
0x000000FF ) ^
'.$dw[$i]."\n";
}
$decrypt_block .= ');';
$lambda_functions[$code_hash] =
$this->_createInlineCryptFunction(
array(
'init_crypt' => '',
'init_encrypt' => $init_encrypt,
'init_decrypt' => $init_decrypt,
'encrypt_block' => $encrypt_block,
'decrypt_block' => $decrypt_block
)
);
}
$this->inline_crypt = $lambda_functions[$code_hash];
}
}
phpseclib/phpseclib/Crypt/RSA.php000064400000336717151161207740012752
0ustar00<?php
/**
* Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
*
* PHP version 5
*
* Here's an example of how to encrypt and decrypt text with this
library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $rsa = new \phpseclib\Crypt\RSA();
* extract($rsa->createKey());
*
* $plaintext = 'terrafrost';
*
* $rsa->loadKey($privatekey);
* $ciphertext = $rsa->encrypt($plaintext);
*
* $rsa->loadKey($publickey);
* echo $rsa->decrypt($ciphertext);
* ?>
* </code>
*
* Here's an example of how to create signatures and verify signatures
with this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $rsa = new \phpseclib\Crypt\RSA();
* extract($rsa->createKey());
*
* $plaintext = 'terrafrost';
*
* $rsa->loadKey($privatekey);
* $signature = $rsa->sign($plaintext);
*
* $rsa->loadKey($publickey);
* echo $rsa->verify($plaintext, $signature) ? 'verified' :
'unverified';
* ?>
* </code>
*
* @category Crypt
* @package RSA
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2009 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
use phpseclib\Math\BigInteger;
/**
* Pure-PHP PKCS#1 compliant implementation of RSA.
*
* @package RSA
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class RSA
{
/**#@+
* @access public
* @see self::encrypt()
* @see self::decrypt()
*/
/**
* Use {@link
http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal
Asymmetric Encryption Padding}
* (OAEP) for encryption / decryption.
*
* Uses sha1 by default.
*
* @see self::setHash()
* @see self::setMGFHash()
*/
const ENCRYPTION_OAEP = 1;
/**
* Use PKCS#1 padding.
*
* Although self::ENCRYPTION_OAEP offers more security, including
PKCS#1 padding is necessary for purposes of backwards
* compatibility with protocols (like SSH-1) written before OAEP's
introduction.
*/
const ENCRYPTION_PKCS1 = 2;
/**
* Do not use any padding
*
* Although this method is not recommended it can none-the-less
sometimes be useful if you're trying to decrypt some legacy
* stuff, if you're trying to diagnose why an encrypted message
isn't decrypting, etc.
*/
const ENCRYPTION_NONE = 3;
/**#@-*/
/**#@+
* @access public
* @see self::sign()
* @see self::verify()
* @see self::setHash()
*/
/**
* Use the Probabilistic Signature Scheme for signing
*
* Uses sha1 by default.
*
* @see self::setSaltLength()
* @see self::setMGFHash()
*/
const SIGNATURE_PSS = 1;
/**
* Use the PKCS#1 scheme by default.
*
* Although self::SIGNATURE_PSS offers more security, including PKCS#1
signing is necessary for purposes of backwards
* compatibility with protocols (like SSH-2) written before PSS's
introduction.
*/
const SIGNATURE_PKCS1 = 2;
/**#@-*/
/**#@+
* @access private
* @see \phpseclib\Crypt\RSA::createKey()
*/
/**
* ASN1 Integer
*/
const ASN1_INTEGER = 2;
/**
* ASN1 Bit String
*/
const ASN1_BITSTRING = 3;
/**
* ASN1 Octet String
*/
const ASN1_OCTETSTRING = 4;
/**
* ASN1 Object Identifier
*/
const ASN1_OBJECT = 6;
/**
* ASN1 Sequence (with the constucted bit set)
*/
const ASN1_SEQUENCE = 48;
/**#@-*/
/**#@+
* @access private
* @see \phpseclib\Crypt\RSA::__construct()
*/
/**
* To use the pure-PHP implementation
*/
const MODE_INTERNAL = 1;
/**
* To use the OpenSSL library
*
* (if enabled; otherwise, the internal implementation will be used)
*/
const MODE_OPENSSL = 2;
/**#@-*/
/**#@+
* @access public
* @see \phpseclib\Crypt\RSA::createKey()
* @see \phpseclib\Crypt\RSA::setPrivateKeyFormat()
*/
/**
* PKCS#1 formatted private key
*
* Used by OpenSSH
*/
const PRIVATE_FORMAT_PKCS1 = 0;
/**
* PuTTY formatted private key
*/
const PRIVATE_FORMAT_PUTTY = 1;
/**
* XML formatted private key
*/
const PRIVATE_FORMAT_XML = 2;
/**
* PKCS#8 formatted private key
*/
const PRIVATE_FORMAT_PKCS8 = 8;
/**
* OpenSSH formatted private key
*/
const PRIVATE_FORMAT_OPENSSH = 9;
/**#@-*/
/**#@+
* @access public
* @see \phpseclib\Crypt\RSA::createKey()
* @see \phpseclib\Crypt\RSA::setPublicKeyFormat()
*/
/**
* Raw public key
*
* An array containing two \phpseclib\Math\BigInteger objects.
*
* The exponent can be indexed with any of the following:
*
* 0, e, exponent, publicExponent
*
* The modulus can be indexed with any of the following:
*
* 1, n, modulo, modulus
*/
const PUBLIC_FORMAT_RAW = 3;
/**
* PKCS#1 formatted public key (raw)
*
* Used by File/X509.php
*
* Has the following header:
*
* -----BEGIN RSA PUBLIC KEY-----
*
* Analogous to ssh-keygen's pem format (as specified by -m)
*/
const PUBLIC_FORMAT_PKCS1 = 4;
const PUBLIC_FORMAT_PKCS1_RAW = 4;
/**
* XML formatted public key
*/
const PUBLIC_FORMAT_XML = 5;
/**
* OpenSSH formatted public key
*
* Place in $HOME/.ssh/authorized_keys
*/
const PUBLIC_FORMAT_OPENSSH = 6;
/**
* PKCS#1 formatted public key (encapsulated)
*
* 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)
*/
const PUBLIC_FORMAT_PKCS8 = 7;
/**#@-*/
/**
* Precomputed Zero
*
* @var \phpseclib\Math\BigInteger
* @access private
*/
var $zero;
/**
* Precomputed One
*
* @var \phpseclib\Math\BigInteger
* @access private
*/
var $one;
/**
* Private Key Format
*
* @var int
* @access private
*/
var $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1;
/**
* Public Key Format
*
* @var int
* @access public
*/
var $publicKeyFormat = self::PUBLIC_FORMAT_PKCS8;
/**
* Modulus (ie. n)
*
* @var \phpseclib\Math\BigInteger
* @access private
*/
var $modulus;
/**
* Modulus length
*
* @var \phpseclib\Math\BigInteger
* @access private
*/
var $k;
/**
* Exponent (ie. e or d)
*
* @var \phpseclib\Math\BigInteger
* @access private
*/
var $exponent;
/**
* Primes for Chinese Remainder Theorem (ie. p and q)
*
* @var array
* @access private
*/
var $primes;
/**
* Exponents for Chinese Remainder Theorem (ie. dP and dQ)
*
* @var array
* @access private
*/
var $exponents;
/**
* Coefficients for Chinese Remainder Theorem (ie. qInv)
*
* @var array
* @access private
*/
var $coefficients;
/**
* Hash name
*
* @var string
* @access private
*/
var $hashName;
/**
* Hash function
*
* @var \phpseclib\Crypt\Hash
* @access private
*/
var $hash;
/**
* Length of hash function output
*
* @var int
* @access private
*/
var $hLen;
/**
* Length of salt
*
* @var int
* @access private
*/
var $sLen;
/**
* Hash function for the Mask Generation Function
*
* @var \phpseclib\Crypt\Hash
* @access private
*/
var $mgfHash;
/**
* Length of MGF hash function output
*
* @var int
* @access private
*/
var $mgfHLen;
/**
* Encryption mode
*
* @var int
* @access private
*/
var $encryptionMode = self::ENCRYPTION_OAEP;
/**
* Signature mode
*
* @var int
* @access private
*/
var $signatureMode = self::SIGNATURE_PSS;
/**
* Public Exponent
*
* @var mixed
* @access private
*/
var $publicExponent = false;
/**
* Password
*
* @var string
* @access private
*/
var $password = false;
/**
* Components
*
* For use with parsing XML formatted keys. PHP's XML Parser
functions use utilized - instead of PHP's DOM functions -
* because PHP's XML Parser functions work on PHP4 whereas
PHP's DOM functions - although surperior - don't.
*
* @see self::_start_element_handler()
* @var array
* @access private
*/
var $components = array();
/**
* Current String
*
* For use with parsing XML formatted keys.
*
* @see self::_character_handler()
* @see self::_stop_element_handler()
* @var mixed
* @access private
*/
var $current;
/**
* OpenSSL configuration file name.
*
* Set to null to use system configuration file.
* @see self::createKey()
* @var mixed
* @Access public
*/
var $configFile;
/**
* Public key comment field.
*
* @var string
* @access private
*/
var $comment = 'phpseclib-generated-key';
/**
* The constructor
*
* If you want to make use of the openssl extension, you'll need
to set the mode manually, yourself. The reason
* \phpseclib\Crypt\RSA doesn't do it is because OpenSSL
doesn't fail gracefully. openssl_pkey_new(), in particular, requires
* openssl.cnf be present somewhere and, unfortunately, the only real
way to find out is too late.
*
* @return \phpseclib\Crypt\RSA
* @access public
*/
function __construct()
{
$this->configFile = dirname(__FILE__) .
'/../openssl.cnf';
if (!defined('CRYPT_RSA_MODE')) {
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',
self::MODE_INTERNAL);
break;
case extension_loaded('openssl') &&
file_exists($this->configFile):
// some versions of XAMPP have mismatched versions of
OpenSSL which causes it not to work
$versions = array();
// avoid generating errors (even with suppression) when
phpinfo() is disabled (common in production systems)
if (strpos(ini_get('disable_functions'),
'phpinfo') === false) {
ob_start();
@phpinfo();
$content = ob_get_contents();
ob_end_clean();
preg_match_all('#OpenSSL (Header|Library)
Version(.*)#im', $content, $matches);
if (!empty($matches[1])) {
for ($i = 0; $i < count($matches[1]); $i++)
{
$fullVersion =
trim(str_replace('=>', '',
strip_tags($matches[2][$i])));
// Remove letter part in OpenSSL version
if
(!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
$versions[$matches[1][$i]] =
$fullVersion;
} else {
$versions[$matches[1][$i]] = $m[0];
}
}
}
}
// 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']:
case version_compare($versions['Header'],
'1.0.0') >= 0 &&
version_compare($versions['Library'], '1.0.0') >= 0:
define('CRYPT_RSA_MODE',
self::MODE_OPENSSL);
break;
default:
define('CRYPT_RSA_MODE',
self::MODE_INTERNAL);
define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
}
break;
default:
define('CRYPT_RSA_MODE',
self::MODE_INTERNAL);
}
}
$this->zero = new BigInteger();
$this->one = new BigInteger(1);
$this->hash = new Hash('sha1');
$this->hLen = $this->hash->getLength();
$this->hashName = 'sha1';
$this->mgfHash = new Hash('sha1');
$this->mgfHLen = $this->mgfHash->getLength();
}
/**
* Create public / private key pair
*
* Returns an array with the following three elements:
* - 'privatekey': The private key.
* - 'publickey': The public key.
* - 'partialkey': A partially computed key (if the
execution time exceeded $timeout).
* Will need to be passed back to
\phpseclib\Crypt\RSA::createKey() as the third parameter for further
processing.
*
* @access public
* @param int $bits
* @param int $timeout
* @param array $partial
*/
function createKey($bits = 1024, $timeout = false, $partial = array())
{
if (!defined('CRYPT_RSA_EXPONENT')) {
// http://en.wikipedia.org/wiki/65537_%28number%29
define('CRYPT_RSA_EXPONENT', '65537');
}
// per
<http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number
ought not result in primes smaller
// than 256 bits. as a consequence if the key you're trying to
create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME
// to 384 bits then you're going to get a 384 bit prime and a
640 bit prime (384 + 1024 % 384). at least if
// CRYPT_RSA_MODE is set to self::MODE_INTERNAL. if CRYPT_RSA_MODE
is set to self::MODE_OPENSSL then
// CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support
is more intended as a way to speed up RSA key
// generation when there's a chance neither gmp nor OpenSSL
are installed)
if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
define('CRYPT_RSA_SMALLEST_PRIME', 4096);
}
// OpenSSL uses 65537 as the exponent and requires RSA keys be 384
bits minimum
if (CRYPT_RSA_MODE == self::MODE_OPENSSL && $bits >= 384
&& CRYPT_RSA_EXPONENT == 65537) {
$config = array();
if (isset($this->configFile)) {
$config['config'] = $this->configFile;
}
$rsa = openssl_pkey_new(array('private_key_bits'
=> $bits) + $config);
openssl_pkey_export($rsa, $privatekey, null, $config);
$publickey = openssl_pkey_get_details($rsa);
$publickey = $publickey['key'];
$privatekey = call_user_func_array(array($this,
'_convertPrivateKey'),
array_values($this->_parseKey($privatekey,
self::PRIVATE_FORMAT_PKCS1)));
$publickey = call_user_func_array(array($this,
'_convertPublicKey'),
array_values($this->_parseKey($publickey, self::PUBLIC_FORMAT_PKCS1)));
// clear the buffer of error strings stemming from a
minimalistic openssl.cnf
while (openssl_error_string() !== false) {
}
return array(
'privatekey' => $privatekey,
'publickey' => $publickey,
'partialkey' => false
);
}
static $e;
if (!isset($e)) {
$e = new BigInteger(CRYPT_RSA_EXPONENT);
}
extract($this->_generateMinMax($bits));
$absoluteMin = $min;
$temp = $bits >> 1; // divide by two to see how many bits P
and Q would be
if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
$num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
$temp = CRYPT_RSA_SMALLEST_PRIME;
} else {
$num_primes = 2;
}
extract($this->_generateMinMax($temp + $bits % $temp));
$finalMax = $max;
extract($this->_generateMinMax($temp));
$generator = new BigInteger();
$n = $this->one->copy();
if (!empty($partial)) {
extract(unserialize($partial));
} else {
$exponents = $coefficients = $primes = array();
$lcm = array(
'top' => $this->one->copy(),
'bottom' => false
);
}
$start = time();
$i0 = count($primes) + 1;
do {
for ($i = $i0; $i <= $num_primes; $i++) {
if ($timeout !== false) {
$timeout-= time() - $start;
$start = time();
if ($timeout <= 0) {
return array(
'privatekey' => '',
'publickey' => '',
'partialkey' => serialize(array(
'primes' => $primes,
'coefficients' =>
$coefficients,
'lcm' => $lcm,
'exponents' => $exponents
))
);
}
}
if ($i == $num_primes) {
list($min, $temp) = $absoluteMin->divide($n);
if (!$temp->equals($this->zero)) {
$min = $min->add($this->one); // ie. ceil()
}
$primes[$i] = $generator->randomPrime($min,
$finalMax, $timeout);
} else {
$primes[$i] = $generator->randomPrime($min, $max,
$timeout);
}
if ($primes[$i] === false) { // if we've reached the
timeout
if (count($primes) > 1) {
$partialkey = '';
} else {
array_pop($primes);
$partialkey = serialize(array(
'primes' => $primes,
'coefficients' => $coefficients,
'lcm' => $lcm,
'exponents' => $exponents
));
}
return array(
'privatekey' => '',
'publickey' => '',
'partialkey' => $partialkey
);
}
// the first coefficient is calculated differently from the
rest
// ie. instead of being
$primes[1]->modInverse($primes[2]), it's
$primes[2]->modInverse($primes[1])
if ($i > 2) {
$coefficients[$i] = $n->modInverse($primes[$i]);
}
$n = $n->multiply($primes[$i]);
$temp = $primes[$i]->subtract($this->one);
// textbook RSA implementations use Euler's totient
function instead of the least common multiple.
// see
http://en.wikipedia.org/wiki/Euler%27s_totient_function
$lcm['top'] =
$lcm['top']->multiply($temp);
$lcm['bottom'] = $lcm['bottom'] ===
false ? $temp : $lcm['bottom']->gcd($temp);
$exponents[$i] = $e->modInverse($temp);
}
list($temp) =
$lcm['top']->divide($lcm['bottom']);
$gcd = $temp->gcd($e);
$i0 = 1;
} while (!$gcd->equals($this->one));
$d = $e->modInverse($temp);
$coefficients[2] = $primes[2]->modInverse($primes[1]);
// from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
// RSAPrivateKey ::= SEQUENCE {
// version Version,
// modulus INTEGER, -- n
// publicExponent INTEGER, -- e
// privateExponent INTEGER, -- d
// prime1 INTEGER, -- p
// prime2 INTEGER, -- q
// exponent1 INTEGER, -- d mod (p-1)
// exponent2 INTEGER, -- d mod (q-1)
// coefficient INTEGER, -- (inverse of q) mod p
// otherPrimeInfos OtherPrimeInfos OPTIONAL
// }
return array(
'privatekey' => $this->_convertPrivateKey($n,
$e, $d, $primes, $exponents, $coefficients),
'publickey' => $this->_convertPublicKey($n,
$e),
'partialkey' => false
);
}
/**
* Convert a private key to the appropriate format.
*
* @access private
* @see self::setPrivateKeyFormat()
* @param Math_BigInteger $n
* @param Math_BigInteger $e
* @param Math_BigInteger $d
* @param array<int,Math_BigInteger> $primes
* @param array<int,Math_BigInteger> $exponents
* @param array<int,Math_BigInteger> $coefficients
* @return string
*/
function _convertPrivateKey($n, $e, $d, $primes, $exponents,
$coefficients)
{
$signed = $this->privateKeyFormat != self::PRIVATE_FORMAT_XML;
$num_primes = count($primes);
$raw = array(
'version' => $num_primes == 2 ? chr(0) : chr(1),
// two-prime vs. multi
'modulus' => $n->toBytes($signed),
'publicExponent' => $e->toBytes($signed),
'privateExponent' => $d->toBytes($signed),
'prime1' => $primes[1]->toBytes($signed),
'prime2' => $primes[2]->toBytes($signed),
'exponent1' => $exponents[1]->toBytes($signed),
'exponent2' => $exponents[2]->toBytes($signed),
'coefficient' =>
$coefficients[2]->toBytes($signed)
);
// if the format in question does not support multi-prime rsa and
multi-prime rsa was used,
// call _convertPublicKey() instead.
switch ($this->privateKeyFormat) {
case self::PRIVATE_FORMAT_XML:
if ($num_primes != 2) {
return false;
}
return "<RSAKeyValue>\r\n" .
' <Modulus>' .
base64_encode($raw['modulus']) . "</Modulus>\r\n"
.
' <Exponent>' .
base64_encode($raw['publicExponent']) .
"</Exponent>\r\n" .
' <P>' .
base64_encode($raw['prime1']) . "</P>\r\n" .
' <Q>' .
base64_encode($raw['prime2']) . "</Q>\r\n" .
' <DP>' .
base64_encode($raw['exponent1']) . "</DP>\r\n" .
' <DQ>' .
base64_encode($raw['exponent2']) . "</DQ>\r\n" .
' <InverseQ>' .
base64_encode($raw['coefficient']) .
"</InverseQ>\r\n" .
' <D>' .
base64_encode($raw['privateExponent']) .
"</D>\r\n" .
'</RSAKeyValue>';
break;
case self::PRIVATE_FORMAT_PUTTY:
if ($num_primes != 2) {
return false;
}
$key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption:
";
$encryption = (!empty($this->password) ||
is_string($this->password)) ? 'aes256-cbc' : 'none';
$key.= $encryption;
$key.= "\r\nComment: " . $this->comment .
"\r\n";
$public = pack(
'Na*Na*Na*',
strlen('ssh-rsa'),
'ssh-rsa',
strlen($raw['publicExponent']),
$raw['publicExponent'],
strlen($raw['modulus']),
$raw['modulus']
);
$source = pack(
'Na*Na*Na*Na*',
strlen('ssh-rsa'),
'ssh-rsa',
strlen($encryption),
$encryption,
strlen($this->comment),
$this->comment,
strlen($public),
$public
);
$public = base64_encode($public);
$key.= "Public-Lines: " . ((strlen($public) + 63)
>> 6) . "\r\n";
$key.= chunk_split($public, 64);
$private = pack(
'Na*Na*Na*Na*',
strlen($raw['privateExponent']),
$raw['privateExponent'],
strlen($raw['prime1']),
$raw['prime1'],
strlen($raw['prime2']),
$raw['prime2'],
strlen($raw['coefficient']),
$raw['coefficient']
);
if (empty($this->password) &&
!is_string($this->password)) {
$source.= pack('Na*', strlen($private),
$private);
$hashkey = 'putty-private-key-file-mac-key';
} else {
$private.= Random::string(16 - (strlen($private) &
15));
$source.= pack('Na*', strlen($private),
$private);
$sequence = 0;
$symkey = '';
while (strlen($symkey) < 32) {
$temp = pack('Na*', $sequence++,
$this->password);
$symkey.= pack('H*', sha1($temp));
}
$symkey = substr($symkey, 0, 32);
$crypto = new AES();
$crypto->setKey($symkey);
$crypto->disablePadding();
$private = $crypto->encrypt($private);
$hashkey = 'putty-private-key-file-mac-key' .
$this->password;
}
$private = base64_encode($private);
$key.= 'Private-Lines: ' . ((strlen($private) +
63) >> 6) . "\r\n";
$key.= chunk_split($private, 64);
$hash = new Hash('sha1');
$hash->setKey(pack('H*', sha1($hashkey)));
$key.= 'Private-MAC: ' .
bin2hex($hash->hash($source)) . "\r\n";
return $key;
case self::PRIVATE_FORMAT_OPENSSH:
if ($num_primes != 2) {
return false;
}
$publicKey = pack('Na*Na*Na*',
strlen('ssh-rsa'), 'ssh-rsa',
strlen($raw['publicExponent']), $raw['publicExponent'],
strlen($raw['modulus']), $raw['modulus']);
$privateKey = pack(
'Na*Na*Na*Na*Na*Na*Na*',
strlen('ssh-rsa'),
'ssh-rsa',
strlen($raw['modulus']),
$raw['modulus'],
strlen($raw['publicExponent']),
$raw['publicExponent'],
strlen($raw['privateExponent']),
$raw['privateExponent'],
strlen($raw['coefficient']),
$raw['coefficient'],
strlen($raw['prime1']),
$raw['prime1'],
strlen($raw['prime2']),
$raw['prime2']
);
$checkint = Random::string(4);
$paddedKey = pack(
'a*Na*',
$checkint . $checkint . $privateKey,
strlen($this->comment),
$this->comment
);
$paddingLength = (7 * strlen($paddedKey)) % 8;
for ($i = 1; $i <= $paddingLength; $i++) {
$paddedKey.= chr($i);
}
$key = pack(
'Na*Na*Na*NNa*Na*',
strlen('none'),
'none',
strlen('none'),
'none',
0,
'',
1,
strlen($publicKey),
$publicKey,
strlen($paddedKey),
$paddedKey
);
$key = "openssh-key-v1\0$key";
return "-----BEGIN OPENSSH PRIVATE KEY-----\r\n"
.
chunk_split(base64_encode($key), 70) .
"-----END OPENSSH PRIVATE KEY-----";
default: // eg. self::PRIVATE_FORMAT_PKCS1
$components = array();
foreach ($raw as $name => $value) {
$components[$name] = pack('Ca*a*',
self::ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
}
$RSAPrivateKey = implode('', $components);
if ($num_primes > 2) {
$OtherPrimeInfos = '';
for ($i = 3; $i <= $num_primes; $i++) {
// OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF
OtherPrimeInfo
//
// OtherPrimeInfo ::= SEQUENCE {
// prime INTEGER, -- ri
// exponent INTEGER, -- di
// coefficient INTEGER -- ti
// }
$OtherPrimeInfo = pack('Ca*a*',
self::ASN1_INTEGER,
$this->_encodeLength(strlen($primes[$i]->toBytes(true))),
$primes[$i]->toBytes(true));
$OtherPrimeInfo.= pack('Ca*a*',
self::ASN1_INTEGER,
$this->_encodeLength(strlen($exponents[$i]->toBytes(true))),
$exponents[$i]->toBytes(true));
$OtherPrimeInfo.= pack('Ca*a*',
self::ASN1_INTEGER,
$this->_encodeLength(strlen($coefficients[$i]->toBytes(true))),
$coefficients[$i]->toBytes(true));
$OtherPrimeInfos.= pack('Ca*a*',
self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)),
$OtherPrimeInfo);
}
$RSAPrivateKey.= pack('Ca*a*',
self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)),
$OtherPrimeInfos);
}
$RSAPrivateKey = pack('Ca*a*',
self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)),
$RSAPrivateKey);
if ($this->privateKeyFormat ==
self::PRIVATE_FORMAT_PKCS8) {
$rsaOID = pack('H*',
'300d06092a864886f70d0101010500'); // hex version of
MA0GCSqGSIb3DQEBAQUA
$RSAPrivateKey = pack(
'Ca*a*Ca*a*',
self::ASN1_INTEGER,
"\01\00",
$rsaOID,
4,
$this->_encodeLength(strlen($RSAPrivateKey)),
$RSAPrivateKey
);
$RSAPrivateKey = pack('Ca*a*',
self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)),
$RSAPrivateKey);
if (!empty($this->password) ||
is_string($this->password)) {
$salt = Random::string(8);
$iterationCount = 2048;
$crypto = new DES();
$crypto->setPassword($this->password,
'pbkdf1', 'md5', $salt, $iterationCount);
$RSAPrivateKey =
$crypto->encrypt($RSAPrivateKey);
$parameters = pack(
'Ca*a*Ca*N',
self::ASN1_OCTETSTRING,
$this->_encodeLength(strlen($salt)),
$salt,
self::ASN1_INTEGER,
$this->_encodeLength(4),
$iterationCount
);
$pbeWithMD5AndDES_CBC =
"\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";
$encryptionAlgorithm = pack(
'Ca*a*Ca*a*',
self::ASN1_OBJECT,
$this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)),
$pbeWithMD5AndDES_CBC,
self::ASN1_SEQUENCE,
$this->_encodeLength(strlen($parameters)),
$parameters
);
$RSAPrivateKey = pack(
'Ca*a*Ca*a*',
self::ASN1_SEQUENCE,
$this->_encodeLength(strlen($encryptionAlgorithm)),
$encryptionAlgorithm,
self::ASN1_OCTETSTRING,
$this->_encodeLength(strlen($RSAPrivateKey)),
$RSAPrivateKey
);
$RSAPrivateKey = pack('Ca*a*',
self::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)) {
$iv = Random::string(8);
$symkey = pack('H*', md5($this->password .
$iv)); // symkey is short for symmetric key
$symkey.= substr(pack('H*', md5($symkey .
$this->password . $iv)), 0, 8);
$des = new TripleDES();
$des->setKey($symkey);
$des->setIV($iv);
$iv = strtoupper(bin2hex($iv));
$RSAPrivateKey = "-----BEGIN RSA PRIVATE
KEY-----\r\n" .
"Proc-Type: 4,ENCRYPTED\r\n"
.
"DEK-Info:
DES-EDE3-CBC,$iv\r\n" .
"\r\n" .
chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
'-----END RSA PRIVATE
KEY-----';
} else {
$RSAPrivateKey = "-----BEGIN RSA PRIVATE
KEY-----\r\n" .
chunk_split(base64_encode($RSAPrivateKey), 64) .
'-----END RSA PRIVATE
KEY-----';
}
return $RSAPrivateKey;
}
}
/**
* Convert a public key to the appropriate format
*
* @access private
* @see self::setPublicKeyFormat()
* @param Math_BigInteger $n
* @param Math_BigInteger $e
* @return string|array<string,Math_BigInteger>
*/
function _convertPublicKey($n, $e)
{
$signed = $this->publicKeyFormat != self::PUBLIC_FORMAT_XML;
$modulus = $n->toBytes($signed);
$publicExponent = $e->toBytes($signed);
switch ($this->publicKeyFormat) {
case self::PUBLIC_FORMAT_RAW:
return array('e' => $e->copy(),
'n' => $n->copy());
case self::PUBLIC_FORMAT_XML:
return "<RSAKeyValue>\r\n" .
' <Modulus>' .
base64_encode($modulus) . "</Modulus>\r\n" .
' <Exponent>' .
base64_encode($publicExponent) . "</Exponent>\r\n" .
'</RSAKeyValue>';
break;
case self::PUBLIC_FORMAT_OPENSSH:
// from <http://tools.ietf.org/html/rfc4253#page-15>:
// string "ssh-rsa"
// mpint e
// mpint n
$RSAPublicKey = pack('Na*Na*Na*',
strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent),
$publicExponent, strlen($modulus), $modulus);
$RSAPublicKey = 'ssh-rsa ' .
base64_encode($RSAPublicKey) . ' ' . $this->comment;
return $RSAPublicKey;
default: // eg. self::PUBLIC_FORMAT_PKCS1_RAW or
self::PUBLIC_FORMAT_PKCS1
// from
<http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
// RSAPublicKey ::= SEQUENCE {
// modulus INTEGER, -- n
// publicExponent INTEGER -- e
// }
$components = array(
'modulus' => pack('Ca*a*',
self::ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
'publicExponent' =>
pack('Ca*a*', self::ASN1_INTEGER,
$this->_encodeLength(strlen($publicExponent)), $publicExponent)
);
$RSAPublicKey = pack(
'Ca*a*a*',
self::ASN1_SEQUENCE,
$this->_encodeLength(strlen($components['modulus']) +
strlen($components['publicExponent'])),
$components['modulus'],
$components['publicExponent']
);
if ($this->publicKeyFormat ==
self::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.
$rsaOID = pack('H*',
'300d06092a864886f70d0101010500'); // hex version of
MA0GCSqGSIb3DQEBAQUA
$RSAPublicKey = chr(0) . $RSAPublicKey;
$RSAPublicKey = chr(3) .
$this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
$RSAPublicKey = pack(
'Ca*a*',
self::ASN1_SEQUENCE,
$this->_encodeLength(strlen($rsaOID .
$RSAPublicKey)),
$rsaOID . $RSAPublicKey
);
$RSAPublicKey = "-----BEGIN PUBLIC
KEY-----\r\n" .
chunk_split(base64_encode($RSAPublicKey), 64) .
'-----END PUBLIC KEY-----';
}
return $RSAPublicKey;
}
}
/**
* Break a public or private key down into its constituant components
*
* @access private
* @see self::_convertPublicKey()
* @see self::_convertPrivateKey()
* @param string|array $key
* @param int $type
* @return array|bool
*/
function _parseKey($key, $type)
{
if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) {
return false;
}
switch ($type) {
case self::PUBLIC_FORMAT_RAW:
if (!is_array($key)) {
return false;
}
$components = array();
switch (true) {
case isset($key['e']):
$components['publicExponent'] =
$key['e']->copy();
break;
case isset($key['exponent']):
$components['publicExponent'] =
$key['exponent']->copy();
break;
case isset($key['publicExponent']):
$components['publicExponent'] =
$key['publicExponent']->copy();
break;
case isset($key[0]):
$components['publicExponent'] =
$key[0]->copy();
}
switch (true) {
case isset($key['n']):
$components['modulus'] =
$key['n']->copy();
break;
case isset($key['modulo']):
$components['modulus'] =
$key['modulo']->copy();
break;
case isset($key['modulus']):
$components['modulus'] =
$key['modulus']->copy();
break;
case isset($key[1]):
$components['modulus'] =
$key[1]->copy();
}
return isset($components['modulus']) &&
isset($components['publicExponent']) ? $components : false;
case self::PRIVATE_FORMAT_PKCS1:
case self::PRIVATE_FORMAT_PKCS8:
case self::PUBLIC_FORMAT_PKCS1:
/* 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
protect private keys, however, that's not what
OpenSSL* does. OpenSSL protects private keys by adding
two new "fields" to the key - DEK-Info and
Proc-Type. These fields are discussed here:
http://tools.ietf.org/html/rfc1421#section-4.6.1.1
http://tools.ietf.org/html/rfc1421#section-4.6.1.3
DES-EDE3-CBC as an algorithm, however, is not discussed
anywhere, near as I can tell.
DES-CBC and DES-EDE are discussed in RFC1423, however,
DES-EDE3-CBC isn't, nor is its key derivation
function. As is, the definitive authority on this
encoding scheme isn't the IETF but rather OpenSSL's
own implementation. ie. the implementation *is* the
standard and any bugs that may exist in that
implementation are part of the standard, as well.
* OpenSSL is the de facto standard. It's utilized
by OpenSSH and other projects */
if (preg_match('#DEK-Info: (.+),(.+)#', $key,
$matches)) {
$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($symkey .
$this->password . substr($iv, 0, 8)));
// remove the Proc-Type / DEK-Info sections as
they're no longer needed
$key = preg_replace('#^(?:Proc-Type|DEK-Info):
.*#m', '', $key);
$ciphertext = $this->_extractBER($key);
if ($ciphertext === false) {
$ciphertext = $key;
}
switch ($matches[1]) {
case 'AES-256-CBC':
$crypto = new AES();
break;
case 'AES-128-CBC':
$symkey = substr($symkey, 0, 16);
$crypto = new AES();
break;
case 'DES-EDE3-CFB':
$crypto = new TripleDES(Base::MODE_CFB);
break;
case 'DES-EDE3-CBC':
$symkey = substr($symkey, 0, 24);
$crypto = new TripleDES();
break;
case 'DES-CBC':
$crypto = new DES();
break;
default:
return false;
}
$crypto->setKey($symkey);
$crypto->setIV($iv);
$decoded = $crypto->decrypt($ciphertext);
} else {
$decoded = $this->_extractBER($key);
}
if ($decoded !== false) {
$key = $decoded;
}
$components = array();
if (ord($this->_string_shift($key)) !=
self::ASN1_SEQUENCE) {
return false;
}
if ($this->_decodeLength($key) != strlen($key)) {
return false;
}
$tag = ord($this->_string_shift($key));
/* intended for keys for which OpenSSL's asn1parse
returns the following:
0:d=0 hl=4 l= 631 cons: SEQUENCE
4:d=1 hl=2 l= 1 prim: INTEGER :00
7:d=1 hl=2 l= 13 cons: SEQUENCE
9:d=2 hl=2 l= 9 prim: OBJECT
:rsaEncryption
20:d=2 hl=2 l= 0 prim: NULL
22:d=1 hl=4 l= 609 prim: OCTET STRING
ie. PKCS8 keys*/
if ($tag == self::ASN1_INTEGER && substr($key, 0,
3) == "\x01\x00\x30") {
$this->_string_shift($key, 3);
$tag = self::ASN1_SEQUENCE;
}
if ($tag == self::ASN1_SEQUENCE) {
$temp = $this->_string_shift($key,
$this->_decodeLength($key));
if (ord($this->_string_shift($temp)) !=
self::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
case
"\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0A": // rsaPSS
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)) !=
self::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)) !=
self::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;
}
$crypto = new DES();
$crypto->setPassword($this->password,
'pbkdf1', 'md5', $salt, $iterationCount);
$key = $crypto->decrypt($key);
if ($key === false) {
return false;
}
return $this->_parseKey($key,
self::PRIVATE_FORMAT_PKCS1);
default:
return false;
}
/* intended for keys for which OpenSSL's asn1parse
returns the following:
0:d=0 hl=4 l= 290 cons: SEQUENCE
4:d=1 hl=2 l= 13 cons: SEQUENCE
6:d=2 hl=2 l= 9 prim: OBJECT
:rsaEncryption
17:d=2 hl=2 l= 0 prim: NULL
19:d=1 hl=4 l= 271 prim: BIT STRING */
$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
// "The initial octet shall encode, as an unsigned
binary integer wtih bit 1 as the least significant bit, the number of
// unused bits in the final subsequent octet. The
number shall be in the range zero to seven."
// --
http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
(section 8.6.2.2)
if ($tag == self::ASN1_BITSTRING) {
$this->_string_shift($key);
}
if (ord($this->_string_shift($key)) !=
self::ASN1_SEQUENCE) {
return false;
}
if ($this->_decodeLength($key) != strlen($key)) {
return false;
}
$tag = ord($this->_string_shift($key));
}
if ($tag != self::ASN1_INTEGER) {
return false;
}
$length = $this->_decodeLength($key);
$temp = $this->_string_shift($key, $length);
if (strlen($temp) != 1 || ord($temp) > 2) {
$components['modulus'] = new
BigInteger($temp, 256);
$this->_string_shift($key); // skip over
self::ASN1_INTEGER
$length = $this->_decodeLength($key);
$components[$type == self::PUBLIC_FORMAT_PKCS1 ?
'publicExponent' : 'privateExponent'] = new
BigInteger($this->_string_shift($key, $length), 256);
return $components;
}
if (ord($this->_string_shift($key)) !=
self::ASN1_INTEGER) {
return false;
}
$length = $this->_decodeLength($key);
$components['modulus'] = new
BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['publicExponent'] = new
BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['privateExponent'] = new
BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['primes'] = array(1 => new
BigInteger($this->_string_shift($key, $length), 256));
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['primes'][] = new
BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['exponents'] = array(1 => new
BigInteger($this->_string_shift($key, $length), 256));
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['exponents'][] = new
BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['coefficients'] = array(2 => new
BigInteger($this->_string_shift($key, $length), 256));
if (!empty($key)) {
if (ord($this->_string_shift($key)) !=
self::ASN1_SEQUENCE) {
return false;
}
$this->_decodeLength($key);
while (!empty($key)) {
if (ord($this->_string_shift($key)) !=
self::ASN1_SEQUENCE) {
return false;
}
$this->_decodeLength($key);
$key = substr($key, 1);
$length = $this->_decodeLength($key);
$components['primes'][] = new
BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['exponents'][] = new
BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['coefficients'][] = new
BigInteger($this->_string_shift($key, $length), 256);
}
}
return $components;
case self::PUBLIC_FORMAT_OPENSSH:
$parts = explode(' ', $key, 3);
$key = isset($parts[1]) ? base64_decode($parts[1]) : false;
if ($key === false) {
return false;
}
$comment = isset($parts[2]) ? $parts[2] : false;
$cleanup = substr($key, 0, 11) ==
"\0\0\0\7ssh-rsa";
if (strlen($key) <= 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($key, 4)));
$publicExponent = new
BigInteger($this->_string_shift($key, $length), -256);
if (strlen($key) <= 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($key, 4)));
$modulus = new BigInteger($this->_string_shift($key,
$length), -256);
if ($cleanup && strlen($key)) {
if (strlen($key) <= 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($key, 4)));
$realModulus = new
BigInteger($this->_string_shift($key, $length), -256);
return strlen($key) ? false : array(
'modulus' => $realModulus,
'publicExponent' => $modulus,
'comment' => $comment
);
} else {
return strlen($key) ? false : array(
'modulus' => $modulus,
'publicExponent' => $publicExponent,
'comment' => $comment
);
}
// http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
// http://en.wikipedia.org/wiki/XML_Signature
case self::PRIVATE_FORMAT_XML:
case self::PUBLIC_FORMAT_XML:
$this->components = array();
$xml = xml_parser_create('UTF-8');
xml_set_object($xml, $this);
xml_set_element_handler($xml,
'_start_element_handler', '_stop_element_handler');
xml_set_character_data_handler($xml,
'_data_handler');
// add <xml></xml> to account for
"dangling" tags like <BitStrength>...</BitStrength>
that are sometimes added
if (!xml_parse($xml, '<xml>' . $key .
'</xml>')) {
xml_parser_free($xml);
unset($xml);
return false;
}
xml_parser_free($xml);
unset($xml);
return isset($this->components['modulus'])
&& isset($this->components['publicExponent']) ?
$this->components : false;
// from PuTTY's SSHPUBK.C
case self::PRIVATE_FORMAT_PUTTY:
$components = array();
$key = preg_split('#\r\n|\r|\n#', $key);
$type = trim(preg_replace('#PuTTY-User-Key-File-2:
(.+)#', '$1', $key[0]));
if ($type != 'ssh-rsa') {
return false;
}
$encryption = trim(preg_replace('#Encryption:
(.+)#', '$1', $key[1]));
$comment = trim(preg_replace('#Comment: (.+)#',
'$1', $key[2]));
$publicLength = trim(preg_replace('#Public-Lines:
(\d+)#', '$1', $key[3]));
$public = base64_decode(implode('',
array_map('trim', array_slice($key, 4, $publicLength))));
$public = substr($public, 11);
extract(unpack('Nlength',
$this->_string_shift($public, 4)));
$components['publicExponent'] = new
BigInteger($this->_string_shift($public, $length), -256);
extract(unpack('Nlength',
$this->_string_shift($public, 4)));
$components['modulus'] = new
BigInteger($this->_string_shift($public, $length), -256);
$privateLength = trim(preg_replace('#Private-Lines:
(\d+)#', '$1', $key[$publicLength + 4]));
$private = base64_decode(implode('',
array_map('trim', array_slice($key, $publicLength + 5,
$privateLength))));
switch ($encryption) {
case 'aes256-cbc':
$symkey = '';
$sequence = 0;
while (strlen($symkey) < 32) {
$temp = pack('Na*', $sequence++,
$this->password);
$symkey.= pack('H*', sha1($temp));
}
$symkey = substr($symkey, 0, 32);
$crypto = new AES();
}
if ($encryption != 'none') {
$crypto->setKey($symkey);
$crypto->disablePadding();
$private = $crypto->decrypt($private);
if ($private === false) {
return false;
}
}
extract(unpack('Nlength',
$this->_string_shift($private, 4)));
if (strlen($private) < $length) {
return false;
}
$components['privateExponent'] = new
BigInteger($this->_string_shift($private, $length), -256);
extract(unpack('Nlength',
$this->_string_shift($private, 4)));
if (strlen($private) < $length) {
return false;
}
$components['primes'] = array(1 => new
BigInteger($this->_string_shift($private, $length), -256));
extract(unpack('Nlength',
$this->_string_shift($private, 4)));
if (strlen($private) < $length) {
return false;
}
$components['primes'][] = new
BigInteger($this->_string_shift($private, $length), -256);
$temp =
$components['primes'][1]->subtract($this->one);
$components['exponents'] = array(1 =>
$components['publicExponent']->modInverse($temp));
$temp =
$components['primes'][2]->subtract($this->one);
$components['exponents'][] =
$components['publicExponent']->modInverse($temp);
extract(unpack('Nlength',
$this->_string_shift($private, 4)));
if (strlen($private) < $length) {
return false;
}
$components['coefficients'] = array(2 => new
BigInteger($this->_string_shift($private, $length), -256));
return $components;
case self::PRIVATE_FORMAT_OPENSSH:
$components = array();
$decoded = $this->_extractBER($key);
$magic = $this->_string_shift($decoded, 15);
if ($magic !== "openssh-key-v1\0") {
return false;
}
$options = $this->_string_shift($decoded, 24);
// \0\0\0\4none = ciphername
// \0\0\0\4none = kdfname
// \0\0\0\0 = kdfoptions
// \0\0\0\1 = numkeys
if ($options !=
"\0\0\0\4none\0\0\0\4none\0\0\0\0\0\0\0\1") {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($decoded, 4)));
if (strlen($decoded) < $length) {
return false;
}
$publicKey = $this->_string_shift($decoded, $length);
extract(unpack('Nlength',
$this->_string_shift($decoded, 4)));
if (strlen($decoded) < $length) {
return false;
}
$paddedKey = $this->_string_shift($decoded, $length);
if ($this->_string_shift($publicKey, 11) !==
"\0\0\0\7ssh-rsa") {
return false;
}
$checkint1 = $this->_string_shift($paddedKey, 4);
$checkint2 = $this->_string_shift($paddedKey, 4);
if (strlen($checkint1) != 4 || $checkint1 !== $checkint2) {
return false;
}
if ($this->_string_shift($paddedKey, 11) !==
"\0\0\0\7ssh-rsa") {
return false;
}
$values = array(
&$components['modulus'],
&$components['publicExponent'],
&$components['privateExponent'],
&$components['coefficients'][2],
&$components['primes'][1],
&$components['primes'][2]
);
foreach ($values as &$value) {
extract(unpack('Nlength',
$this->_string_shift($paddedKey, 4)));
if (strlen($paddedKey) < $length) {
return false;
}
$value = new
BigInteger($this->_string_shift($paddedKey, $length), -256);
}
extract(unpack('Nlength',
$this->_string_shift($paddedKey, 4)));
if (strlen($paddedKey) < $length) {
return false;
}
$components['comment'] =
$this->_string_shift($decoded, $length);
$temp =
$components['primes'][1]->subtract($this->one);
$components['exponents'] = array(1 =>
$components['publicExponent']->modInverse($temp));
$temp =
$components['primes'][2]->subtract($this->one);
$components['exponents'][] =
$components['publicExponent']->modInverse($temp);
return $components;
}
return false;
}
/**
* Returns the key size
*
* More specifically, this returns the size of the modulo in bits.
*
* @access public
* @return int
*/
function getSize()
{
return !isset($this->modulus) ? 0 :
strlen($this->modulus->toBits());
}
/**
* Start Element Handler
*
* Called by xml_set_element_handler()
*
* @access private
* @param resource $parser
* @param string $name
* @param array $attribs
*/
function _start_element_handler($parser, $name, $attribs)
{
//$name = strtoupper($name);
switch ($name) {
case 'MODULUS':
$this->current =
&$this->components['modulus'];
break;
case 'EXPONENT':
$this->current =
&$this->components['publicExponent'];
break;
case 'P':
$this->current =
&$this->components['primes'][1];
break;
case 'Q':
$this->current =
&$this->components['primes'][2];
break;
case 'DP':
$this->current =
&$this->components['exponents'][1];
break;
case 'DQ':
$this->current =
&$this->components['exponents'][2];
break;
case 'INVERSEQ':
$this->current =
&$this->components['coefficients'][2];
break;
case 'D':
$this->current =
&$this->components['privateExponent'];
}
$this->current = '';
}
/**
* Stop Element Handler
*
* Called by xml_set_element_handler()
*
* @access private
* @param resource $parser
* @param string $name
*/
function _stop_element_handler($parser, $name)
{
if (isset($this->current)) {
$this->current = new
BigInteger(base64_decode($this->current), 256);
unset($this->current);
}
}
/**
* Data Handler
*
* Called by xml_set_character_data_handler()
*
* @access private
* @param resource $parser
* @param string $data
*/
function _data_handler($parser, $data)
{
if (!isset($this->current) || is_object($this->current)) {
return;
}
$this->current.= trim($data);
}
/**
* Loads a public or private key
*
* Returns true on success and false on failure (ie. an incorrect
password was provided or the key was malformed)
*
* @access public
* @param string|RSA|array $key
* @param bool|int $type optional
* @return bool
*/
function loadKey($key, $type = false)
{
if ($key instanceof 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 Hash($key->hash->getHash());
}
if (is_object($key->mgfHash)) {
$this->mgfHash = new
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) {
$types = array(
self::PUBLIC_FORMAT_RAW,
self::PRIVATE_FORMAT_PKCS1,
self::PRIVATE_FORMAT_XML,
self::PRIVATE_FORMAT_PUTTY,
self::PUBLIC_FORMAT_OPENSSH,
self::PRIVATE_FORMAT_OPENSSH
);
foreach ($types as $type) {
$components = $this->_parseKey($key, $type);
if ($components !== false) {
break;
}
}
} else {
$components = $this->_parseKey($key, $type);
}
if ($components === false) {
$this->comment = null;
$this->modulus = null;
$this->k = null;
$this->exponent = null;
$this->primes = null;
$this->exponents = null;
$this->coefficients = null;
$this->publicExponent = null;
return false;
}
if (isset($components['comment']) &&
$components['comment'] !== false) {
$this->comment = $components['comment'];
}
$this->modulus = $components['modulus'];
$this->k = strlen($this->modulus->toBytes());
$this->exponent =
isset($components['privateExponent']) ?
$components['privateExponent'] :
$components['publicExponent'];
if (isset($components['primes'])) {
$this->primes = $components['primes'];
$this->exponents = $components['exponents'];
$this->coefficients = $components['coefficients'];
$this->publicExponent =
$components['publicExponent'];
} else {
$this->primes = array();
$this->exponents = array();
$this->coefficients = array();
$this->publicExponent = false;
}
switch ($type) {
case self::PUBLIC_FORMAT_OPENSSH:
case self::PUBLIC_FORMAT_RAW:
$this->setPublicKey();
break;
case self::PRIVATE_FORMAT_PKCS1:
switch (true) {
case strpos($key, '-BEGIN PUBLIC KEY-') !==
false:
case strpos($key, '-BEGIN RSA PUBLIC KEY-')
!== false:
$this->setPublicKey();
}
}
return true;
}
/**
* Sets the password
*
* Private keys can be encrypted with a password. To unset the
password, pass in the empty string or false.
* Or rather, pass in $password such that empty($password) &&
!is_string($password) is true.
*
* @see self::createKey()
* @see self::loadKey()
* @access public
* @param string $password
*/
function setPassword($password = false)
{
$this->password = $password;
}
/**
* Defines the public key
*
* Some private key formats define the public exponent and some
don't. Those that don't define it are problematic when
* 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
* 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. 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.
*
* Returns true on success, false on failure
*
* @see self::getPublicKey()
* @access public
* @param string $key optional
* @param int $type optional
* @return bool
*/
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)) {
$this->publicExponent = $this->exponent;
return true;
}
if ($type === false) {
$types = array(
self::PUBLIC_FORMAT_RAW,
self::PUBLIC_FORMAT_PKCS1,
self::PUBLIC_FORMAT_XML,
self::PUBLIC_FORMAT_OPENSSH
);
foreach ($types as $type) {
$components = $this->_parseKey($key, $type);
if ($components !== false) {
break;
}
}
} else {
$components = $this->_parseKey($key, $type);
}
if ($components === false) {
return false;
}
if (empty($this->modulus) ||
!$this->modulus->equals($components['modulus'])) {
$this->modulus = $components['modulus'];
$this->exponent = $this->publicExponent =
$components['publicExponent'];
return true;
}
$this->publicExponent = $components['publicExponent'];
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 self::getPublicKey()
* @access public
* @param string $key optional
* @param int $type optional
* @return bool
*/
function setPrivateKey($key = false, $type = false)
{
if ($key === false && !empty($this->publicExponent)) {
$this->publicExponent = false;
return true;
}
$rsa = new RSA();
if (!$rsa->loadKey($key, $type)) {
return false;
}
$rsa->publicExponent = false;
// don't overwrite the old key if the new key is invalid
$this->loadKey($rsa);
return true;
}
/**
* Returns the public key
*
* The public key is only returned under two circumstances - if the
private key had the public key embedded within it
* or if the public key was set via setPublicKey(). If the currently
loaded key is supposed to be the public key this
* function won't return it since this library, for the most part,
doesn't distinguish between public and private keys.
*
* @see self::getPublicKey()
* @access public
* @param int $type optional
*/
function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8)
{
if (empty($this->modulus) || empty($this->publicExponent)) {
return false;
}
$oldFormat = $this->publicKeyFormat;
$this->publicKeyFormat = $type;
$temp = $this->_convertPublicKey($this->modulus,
$this->publicExponent);
$this->publicKeyFormat = $oldFormat;
return $temp;
}
/**
* Returns the public key's fingerprint
*
* The public key's fingerprint is returned, which is equivalent
to running `ssh-keygen -lf rsa.pub`. If there is
* no public key currently loaded, false is returned.
* Example output (md5):
"c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified
by RFC 4716)
*
* @access public
* @param string $algorithm The hashing algorithm to be used. Valid
options are 'md5' and 'sha256'. False is returned
* for invalid values.
* @return mixed
*/
function getPublicKeyFingerprint($algorithm = 'md5')
{
if (empty($this->modulus) || empty($this->publicExponent)) {
return false;
}
$modulus = $this->modulus->toBytes(true);
$publicExponent = $this->publicExponent->toBytes(true);
$RSAPublicKey = pack('Na*Na*Na*',
strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent),
$publicExponent, strlen($modulus), $modulus);
switch ($algorithm) {
case 'sha256':
$hash = new Hash('sha256');
$base = base64_encode($hash->hash($RSAPublicKey));
return substr($base, 0, strlen($base) - 1);
case 'md5':
return substr(chunk_split(md5($RSAPublicKey), 2,
':'), 0, -1);
default:
return false;
}
}
/**
* Returns the private key
*
* The private key is only returned if the currently loaded key
contains the constituent prime numbers.
*
* @see self::getPublicKey()
* @access public
* @param int $type optional
* @return mixed
*/
function getPrivateKey($type = self::PUBLIC_FORMAT_PKCS1)
{
if (empty($this->primes)) {
return false;
}
$oldFormat = $this->privateKeyFormat;
$this->privateKeyFormat = $type;
$temp = $this->_convertPrivateKey($this->modulus,
$this->publicExponent, $this->exponent, $this->primes,
$this->exponents, $this->coefficients);
$this->privateKeyFormat = $oldFormat;
return $temp;
}
/**
* Returns a minimalistic private key
*
* Returns the private key without the prime number constituants.
Structurally identical to a public key that
* hasn't been set as the public key
*
* @see self::getPrivateKey()
* @access private
* @param int $mode optional
*/
function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8)
{
if (empty($this->modulus) || empty($this->exponent)) {
return false;
}
$oldFormat = $this->publicKeyFormat;
$this->publicKeyFormat = $mode;
$temp = $this->_convertPublicKey($this->modulus,
$this->exponent);
$this->publicKeyFormat = $oldFormat;
return $temp;
}
/**
* __toString() magic method
*
* @access public
* @return string
*/
function __toString()
{
$key = $this->getPrivateKey($this->privateKeyFormat);
if ($key !== false) {
return $key;
}
$key = $this->_getPrivatePublicKey($this->publicKeyFormat);
return $key !== false ? $key : '';
}
/**
* __clone() magic method
*
* @access public
* @return Crypt_RSA
*/
function __clone()
{
$key = new RSA();
$key->loadKey($this);
return $key;
}
/**
* Generates the smallest and largest numbers requiring $bits bits
*
* @access private
* @param int $bits
* @return array
*/
function _generateMinMax($bits)
{
$bytes = $bits >> 3;
$min = str_repeat(chr(0), $bytes);
$max = str_repeat(chr(0xFF), $bytes);
$msb = $bits & 7;
if ($msb) {
$min = chr(1 << ($msb - 1)) . $min;
$max = chr((1 << $msb) - 1) . $max;
} else {
$min[0] = chr(0x80);
}
return array(
'min' => new BigInteger($min, 256),
'max' => new BigInteger($max, 256)
);
}
/**
* DER-decode the length
*
* DER supports lengths up to (2**8)**127, however, we'll only
support lengths up to (2**8)**4. See
* {@link
http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690
paragraph 8.1.3} for more information.
*
* @access private
* @param string $string
* @return int
*/
function _decodeLength(&$string)
{
$length = ord($this->_string_shift($string));
if ($length & 0x80) { // definite length, long form
$length&= 0x7F;
$temp = $this->_string_shift($string, $length);
list(, $length) = unpack('N', substr(str_pad($temp,
4, chr(0), STR_PAD_LEFT), -4));
}
return $length;
}
/**
* DER-encode the length
*
* DER supports lengths up to (2**8)**127, however, we'll only
support lengths up to (2**8)**4. See
* {@link
http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690
paragraph 8.1.3} for more information.
*
* @access private
* @param int $length
* @return string
*/
function _encodeLength($length)
{
if ($length <= 0x7F) {
return chr($length);
}
$temp = ltrim(pack('N', $length), chr(0));
return pack('Ca*', 0x80 | strlen($temp), $temp);
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @return string
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
/**
* Determines the private key format
*
* @see self::createKey()
* @access public
* @param int $format
*/
function setPrivateKeyFormat($format)
{
$this->privateKeyFormat = $format;
}
/**
* Determines the public key format
*
* @see self::createKey()
* @access public
* @param int $format
*/
function setPublicKeyFormat($format)
{
$this->publicKeyFormat = $format;
}
/**
* Determines which hashing function should be used
*
* Used with signature production / verification and (if the encryption
mode is self::ENCRYPTION_OAEP) encryption and
* decryption. If $hash isn't supported, sha1 is used.
*
* @access public
* @param string $hash
*/
function setHash($hash)
{
// \phpseclib\Crypt\Hash supports algorithms that PKCS#1
doesn't support. md5-96 and sha1-96, for example.
switch ($hash) {
case 'md2':
case 'md5':
case 'sha1':
case 'sha256':
case 'sha384':
case 'sha512':
$this->hash = new Hash($hash);
$this->hashName = $hash;
break;
default:
$this->hash = new Hash('sha1');
$this->hashName = 'sha1';
}
$this->hLen = $this->hash->getLength();
}
/**
* Determines which hashing function should be used for the mask
generation function
*
* The mask generation function is used by self::ENCRYPTION_OAEP and
self::SIGNATURE_PSS and although it's
* best if Hash and MGFHash are set to the same thing this is not a
requirement.
*
* @access public
* @param string $hash
*/
function setMGFHash($hash)
{
// \phpseclib\Crypt\Hash supports algorithms that PKCS#1
doesn't support. md5-96 and sha1-96, for example.
switch ($hash) {
case 'md2':
case 'md5':
case 'sha1':
case 'sha256':
case 'sha384':
case 'sha512':
$this->mgfHash = new Hash($hash);
break;
default:
$this->mgfHash = new Hash('sha1');
}
$this->mgfHLen = $this->mgfHash->getLength();
}
/**
* Determines the salt length
*
* To quote from {@link http://tools.ietf.org/html/rfc3447#page-38
RFC3447#page-38}:
*
* Typical salt lengths in octets are hLen (the length of the output
* of the hash function Hash) and 0.
*
* @access public
* @param int $sLen
*/
function setSaltLength($sLen)
{
$this->sLen = $sLen;
}
/**
* Integer-to-Octet-String primitive
*
* See {@link http://tools.ietf.org/html/rfc3447#section-4.1
RFC3447#section-4.1}.
*
* @access private
* @param \phpseclib\Math\BigInteger $x
* @param int $xLen
* @return string
*/
function _i2osp($x, $xLen)
{
$x = $x->toBytes();
if (strlen($x) > $xLen) {
user_error('Integer too large');
return false;
}
return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
}
/**
* Octet-String-to-Integer primitive
*
* See {@link http://tools.ietf.org/html/rfc3447#section-4.2
RFC3447#section-4.2}.
*
* @access private
* @param int|string|resource $x
* @return \phpseclib\Math\BigInteger
*/
function _os2ip($x)
{
return new BigInteger($x, 256);
}
/**
* Exponentiate with or without Chinese Remainder Theorem
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1
RFC3447#section-5.1.2}.
*
* @access private
* @param \phpseclib\Math\BigInteger $x
* @return \phpseclib\Math\BigInteger
*/
function _exponentiate($x)
{
switch (true) {
case empty($this->primes):
case $this->primes[1]->equals($this->zero):
case empty($this->coefficients):
case $this->coefficients[2]->equals($this->zero):
case empty($this->exponents):
case $this->exponents[1]->equals($this->zero):
return $x->modPow($this->exponent,
$this->modulus);
}
$num_primes = count($this->primes);
if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
$m_i = array(
1 => $x->modPow($this->exponents[1],
$this->primes[1]),
2 => $x->modPow($this->exponents[2],
$this->primes[2])
);
$h = $m_i[1]->subtract($m_i[2]);
$h = $h->multiply($this->coefficients[2]);
list(, $h) = $h->divide($this->primes[1]);
$m = $m_i[2]->add($h->multiply($this->primes[2]));
$r = $this->primes[1];
for ($i = 3; $i <= $num_primes; $i++) {
$m_i = $x->modPow($this->exponents[$i],
$this->primes[$i]);
$r = $r->multiply($this->primes[$i - 1]);
$h = $m_i->subtract($m);
$h = $h->multiply($this->coefficients[$i]);
list(, $h) = $h->divide($this->primes[$i]);
$m = $m->add($r->multiply($h));
}
} else {
$smallest = $this->primes[1];
for ($i = 2; $i <= $num_primes; $i++) {
if ($smallest->compare($this->primes[$i]) > 0) {
$smallest = $this->primes[$i];
}
}
$one = new BigInteger(1);
$r = $one->random($one, $smallest->subtract($one));
$m_i = array(
1 => $this->_blind($x, $r, 1),
2 => $this->_blind($x, $r, 2)
);
$h = $m_i[1]->subtract($m_i[2]);
$h = $h->multiply($this->coefficients[2]);
list(, $h) = $h->divide($this->primes[1]);
$m = $m_i[2]->add($h->multiply($this->primes[2]));
$r = $this->primes[1];
for ($i = 3; $i <= $num_primes; $i++) {
$m_i = $this->_blind($x, $r, $i);
$r = $r->multiply($this->primes[$i - 1]);
$h = $m_i->subtract($m);
$h = $h->multiply($this->coefficients[$i]);
list(, $h) = $h->divide($this->primes[$i]);
$m = $m->add($r->multiply($h));
}
}
return $m;
}
/**
* Performs RSA Blinding
*
* Protects against timing attacks by employing RSA Blinding.
* Returns $x->modPow($this->exponents[$i], $this->primes[$i])
*
* @access private
* @param \phpseclib\Math\BigInteger $x
* @param \phpseclib\Math\BigInteger $r
* @param int $i
* @return \phpseclib\Math\BigInteger
*/
function _blind($x, $r, $i)
{
$x = $x->multiply($r->modPow($this->publicExponent,
$this->primes[$i]));
$x = $x->modPow($this->exponents[$i], $this->primes[$i]);
$r = $r->modInverse($this->primes[$i]);
$x = $x->multiply($r);
list(, $x) = $x->divide($this->primes[$i]);
return $x;
}
/**
* Performs blinded RSA equality testing
*
* Protects against a particular type of timing attack described.
*
* See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson
In Timing Attacks (or, Don't use MessageDigest.isEquals)}
*
* Thanks for the heads up singpolyma!
*
* @access private
* @param string $x
* @param string $y
* @return bool
*/
function _equals($x, $y)
{
if (function_exists('hash_equals')) {
return hash_equals($x, $y);
}
if (strlen($x) != strlen($y)) {
return false;
}
$result = "\0";
$x^= $y;
for ($i = 0; $i < strlen($x); $i++) {
$result|= $x[$i];
}
return $result === "\0";
}
/**
* RSAEP
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1
RFC3447#section-5.1.1}.
*
* @access private
* @param \phpseclib\Math\BigInteger $m
* @return \phpseclib\Math\BigInteger
*/
function _rsaep($m)
{
if ($m->compare($this->zero) < 0 ||
$m->compare($this->modulus) > 0) {
user_error('Message representative out of range');
return false;
}
return $this->_exponentiate($m);
}
/**
* RSADP
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2
RFC3447#section-5.1.2}.
*
* @access private
* @param \phpseclib\Math\BigInteger $c
* @return \phpseclib\Math\BigInteger
*/
function _rsadp($c)
{
if ($c->compare($this->zero) < 0 ||
$c->compare($this->modulus) > 0) {
user_error('Ciphertext representative out of range');
return false;
}
return $this->_exponentiate($c);
}
/**
* RSASP1
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1
RFC3447#section-5.2.1}.
*
* @access private
* @param \phpseclib\Math\BigInteger $m
* @return \phpseclib\Math\BigInteger
*/
function _rsasp1($m)
{
if ($m->compare($this->zero) < 0 ||
$m->compare($this->modulus) > 0) {
user_error('Message representative out of range');
return false;
}
return $this->_exponentiate($m);
}
/**
* RSAVP1
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2
RFC3447#section-5.2.2}.
*
* @access private
* @param \phpseclib\Math\BigInteger $s
* @return \phpseclib\Math\BigInteger
*/
function _rsavp1($s)
{
if ($s->compare($this->zero) < 0 ||
$s->compare($this->modulus) > 0) {
user_error('Signature representative out of range');
return false;
}
return $this->_exponentiate($s);
}
/**
* MGF1
*
* See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1
RFC3447#appendix-B.2.1}.
*
* @access private
* @param string $mgfSeed
* @param int $maskLen
* @return string
*/
function _mgf1($mgfSeed, $maskLen)
{
// if $maskLen would yield strings larger than 4GB, PKCS#1 suggests
a "Mask too long" error be output.
$t = '';
$count = ceil($maskLen / $this->mgfHLen);
for ($i = 0; $i < $count; $i++) {
$c = pack('N', $i);
$t.= $this->mgfHash->hash($mgfSeed . $c);
}
return substr($t, 0, $maskLen);
}
/**
* RSAES-OAEP-ENCRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1
RFC3447#section-7.1.1} and
* {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding
OAES}.
*
* @access private
* @param string $m
* @param string $l
* @return string
*/
function _rsaes_oaep_encrypt($m, $l = '')
{
$mLen = strlen($m);
// Length checking
// if $l is larger than two million terrabytes and you're
using sha1, PKCS#1 suggests a "Label too long" error
// be output.
if ($mLen > $this->k - 2 * $this->hLen - 2) {
user_error('Message too long');
return false;
}
// EME-OAEP encoding
$lHash = $this->hash->hash($l);
$ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen -
2);
$db = $lHash . $ps . chr(1) . $m;
$seed = Random::string($this->hLen);
$dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
$maskedDB = $db ^ $dbMask;
$seedMask = $this->_mgf1($maskedDB, $this->hLen);
$maskedSeed = $seed ^ $seedMask;
$em = chr(0) . $maskedSeed . $maskedDB;
// RSA encryption
$m = $this->_os2ip($em);
$c = $this->_rsaep($m);
$c = $this->_i2osp($c, $this->k);
// Output the ciphertext C
return $c;
}
/**
* RSAES-OAEP-DECRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2
RFC3447#section-7.1.2}. The fact that the error
* messages aren't distinguishable from one another hinders
debugging, but, to quote from RFC3447#section-7.1.2:
*
* Note. Care must be taken to ensure that an opponent cannot
* distinguish the different error conditions in Step 3.g, whether
by
* error message or timing, or, more generally, learn partial
* information about the encoded message EM. Otherwise an opponent
may
* be able to obtain useful information about the decryption of the
* ciphertext C, leading to a chosen-ciphertext attack such as the
one
* observed by Manger [36].
*
* As for $l... to quote from {@link
http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
*
* Both the encryption and the decryption operations of RSAES-OAEP
take
* the value of a label L as input. In this version of PKCS #1, L
is
* the empty string; other uses of the label are outside the scope
of
* this document.
*
* @access private
* @param string $c
* @param string $l
* @return string
*/
function _rsaes_oaep_decrypt($c, $l = '')
{
// Length checking
// if $l is larger than two million terrabytes and you're
using sha1, PKCS#1 suggests a "Label too long" error
// be output.
if (strlen($c) != $this->k || $this->k < 2 *
$this->hLen + 2) {
user_error('Decryption error');
return false;
}
// RSA decryption
$c = $this->_os2ip($c);
$m = $this->_rsadp($c);
if ($m === false) {
user_error('Decryption error');
return false;
}
$em = $this->_i2osp($m, $this->k);
// EME-OAEP decoding
$lHash = $this->hash->hash($l);
$y = ord($em[0]);
$maskedSeed = substr($em, 1, $this->hLen);
$maskedDB = substr($em, $this->hLen + 1);
$seedMask = $this->_mgf1($maskedDB, $this->hLen);
$seed = $maskedSeed ^ $seedMask;
$dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
$db = $maskedDB ^ $dbMask;
$lHash2 = substr($db, 0, $this->hLen);
$m = substr($db, $this->hLen);
$hashesMatch = $this->_equals($lHash, $lHash2);
$leadingZeros = 1;
$patternMatch = 0;
$offset = 0;
for ($i = 0; $i < strlen($m); $i++) {
$patternMatch|= $leadingZeros & ($m[$i] ===
"\1");
$leadingZeros&= $m[$i] === "\0";
$offset+= $patternMatch ? 0 : 1;
}
// we do | instead of || to avoid
https://en.wikipedia.org/wiki/Short-circuit_evaluation
// to protect against timing attacks
if (!$hashesMatch | !$patternMatch) {
user_error('Decryption error');
return false;
}
// Output the message M
return substr($m, $offset + 1);
}
/**
* Raw Encryption / Decryption
*
* Doesn't use padding and is not recommended.
*
* @access private
* @param string $m
* @return string
*/
function _raw_encrypt($m)
{
$temp = $this->_os2ip($m);
$temp = $this->_rsaep($temp);
return $this->_i2osp($temp, $this->k);
}
/**
* RSAES-PKCS1-V1_5-ENCRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1
RFC3447#section-7.2.1}.
*
* @access private
* @param string $m
* @return string
*/
function _rsaes_pkcs1_v1_5_encrypt($m)
{
$mLen = strlen($m);
// Length checking
if ($mLen > $this->k - 11) {
user_error('Message too long');
return false;
}
// EME-PKCS1-v1_5 encoding
$psLen = $this->k - $mLen - 3;
$ps = '';
while (strlen($ps) != $psLen) {
$temp = Random::string($psLen - strlen($ps));
$temp = str_replace("\x00", '', $temp);
$ps.= $temp;
}
$type = 2;
// see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand
why this is being done
if (defined('CRYPT_RSA_PKCS15_COMPAT') &&
(!isset($this->publicExponent) || $this->exponent !==
$this->publicExponent)) {
$type = 1;
// "The padding string PS shall consist of k-3-||D||
octets. ... for block type 01, they shall have value FF"
$ps = str_repeat("\xFF", $psLen);
}
$em = chr(0) . chr($type) . $ps . chr(0) . $m;
// RSA encryption
$m = $this->_os2ip($em);
$c = $this->_rsaep($m);
$c = $this->_i2osp($c, $this->k);
// Output the ciphertext C
return $c;
}
/**
* RSAES-PKCS1-V1_5-DECRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2
RFC3447#section-7.2.2}.
*
* 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
* 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
* 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.
*
* As a consequence of this, a private key encrypted ciphertext
produced with \phpseclib\Crypt\RSA may not decrypt
* with a strictly PKCS#1 v1.5 compliant RSA implementation. Public
key encrypted ciphertext's should but
* not private key encrypted ciphertext's.
*
* @access private
* @param string $c
* @return string
*/
function _rsaes_pkcs1_v1_5_decrypt($c)
{
// Length checking
if (strlen($c) != $this->k) { // or if k < 11
user_error('Decryption error');
return false;
}
// RSA decryption
$c = $this->_os2ip($c);
$m = $this->_rsadp($c);
if ($m === false) {
user_error('Decryption error');
return false;
}
$em = $this->_i2osp($m, $this->k);
// EME-PKCS1-v1_5 decoding
if (ord($em[0]) != 0 || ord($em[1]) > 2) {
user_error('Decryption error');
return false;
}
$ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
$m = substr($em, strlen($ps) + 3);
if (strlen($ps) < 8) {
user_error('Decryption error');
return false;
}
// Output M
return $m;
}
/**
* EMSA-PSS-ENCODE
*
* See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1
RFC3447#section-9.1.1}.
*
* @access private
* @param string $m
* @param int $emBits
*/
function _emsa_pss_encode($m, $emBits)
{
// if $m is larger than two million terrabytes and you're
using sha1, PKCS#1 suggests a "Label too long" error
// be output.
$emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
$sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
$mHash = $this->hash->hash($m);
if ($emLen < $this->hLen + $sLen + 2) {
user_error('Encoding error');
return false;
}
$salt = Random::string($sLen);
$m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
$h = $this->hash->hash($m2);
$ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
$db = $ps . chr(1) . $salt;
$dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
$maskedDB = $db ^ $dbMask;
$maskedDB[0] = ~chr(0xFF << ($emBits & 7)) &
$maskedDB[0];
$em = $maskedDB . $h . chr(0xBC);
return $em;
}
/**
* EMSA-PSS-VERIFY
*
* See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2
RFC3447#section-9.1.2}.
*
* @access private
* @param string $m
* @param string $em
* @param int $emBits
* @return string
*/
function _emsa_pss_verify($m, $em, $emBits)
{
// if $m is larger than two million terrabytes and you're
using sha1, PKCS#1 suggests a "Label too long" error
// be output.
$emLen = ($emBits + 7) >> 3; // ie. ceil($emBits / 8);
$sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
$mHash = $this->hash->hash($m);
if ($emLen < $this->hLen + $sLen + 2) {
return false;
}
if ($em[strlen($em) - 1] != chr(0xBC)) {
return false;
}
$maskedDB = substr($em, 0, -$this->hLen - 1);
$h = substr($em, -$this->hLen - 1, $this->hLen);
$temp = chr(0xFF << ($emBits & 7));
if ((~$maskedDB[0] & $temp) != $temp) {
return false;
}
$dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
$db = $maskedDB ^ $dbMask;
$db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
$temp = $emLen - $this->hLen - $sLen - 2;
if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) ||
ord($db[$temp]) != 1) {
return false;
}
$salt = substr($db, $temp + 1); // should be $sLen long
$m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
$h2 = $this->hash->hash($m2);
return $this->_equals($h, $h2);
}
/**
* RSASSA-PSS-SIGN
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1
RFC3447#section-8.1.1}.
*
* @access private
* @param string $m
* @return string
*/
function _rsassa_pss_sign($m)
{
// EMSA-PSS encoding
$em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
// RSA signature
$m = $this->_os2ip($em);
$s = $this->_rsasp1($m);
$s = $this->_i2osp($s, $this->k);
// Output the signature S
return $s;
}
/**
* RSASSA-PSS-VERIFY
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2
RFC3447#section-8.1.2}.
*
* @access private
* @param string $m
* @param string $s
* @return string
*/
function _rsassa_pss_verify($m, $s)
{
// Length checking
if (strlen($s) != $this->k) {
user_error('Invalid signature');
return false;
}
// RSA verification
$modBits = strlen($this->modulus->toBits());
$s2 = $this->_os2ip($s);
$m2 = $this->_rsavp1($s2);
if ($m2 === false) {
user_error('Invalid signature');
return false;
}
$em = $this->_i2osp($m2, $this->k);
if ($em === false) {
user_error('Invalid signature');
return false;
}
// EMSA-PSS verification
return $this->_emsa_pss_verify($m, $em, $modBits - 1);
}
/**
* EMSA-PKCS1-V1_5-ENCODE
*
* See {@link http://tools.ietf.org/html/rfc3447#section-9.2
RFC3447#section-9.2}.
*
* @access private
* @param string $m
* @param int $emLen
* @return string
*/
function _emsa_pkcs1_v1_5_encode($m, $emLen)
{
$h = $this->hash->hash($m);
if ($h === false) {
return false;
}
// see http://tools.ietf.org/html/rfc3447#page-43
switch ($this->hashName) {
case 'md2':
$t = pack('H*',
'3020300c06082a864886f70d020205000410');
break;
case 'md5':
$t = pack('H*',
'3020300c06082a864886f70d020505000410');
break;
case 'sha1':
$t = pack('H*',
'3021300906052b0e03021a05000414');
break;
case 'sha256':
$t = pack('H*',
'3031300d060960864801650304020105000420');
break;
case 'sha384':
$t = pack('H*',
'3041300d060960864801650304020205000430');
break;
case 'sha512':
$t = pack('H*',
'3051300d060960864801650304020305000440');
}
$t.= $h;
$tLen = strlen($t);
if ($emLen < $tLen + 11) {
user_error('Intended encoded message length too
short');
return false;
}
$ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
$em = "\0\1$ps\0$t";
return $em;
}
/**
* EMSA-PKCS1-V1_5-ENCODE (without NULL)
*
* Quoting https://tools.ietf.org/html/rfc8017#page-65,
*
* "The parameters field associated with id-sha1, id-sha224,
id-sha256,
* id-sha384, id-sha512, id-sha512/224, and id-sha512/256 should
* generally be omitted, but if present, it shall have a value of type
* NULL"
*
* @access private
* @param string $m
* @param int $emLen
* @return string
*/
function _emsa_pkcs1_v1_5_encode_without_null($m, $emLen)
{
$h = $this->hash->hash($m);
if ($h === false) {
return false;
}
switch ($this->hashName) {
case 'sha1':
$t = pack('H*',
'301f300706052b0e03021a0414');
break;
case 'sha256':
$t = pack('H*',
'302f300b06096086480165030402010420');
break;
case 'sha384':
$t = pack('H*',
'303f300b06096086480165030402020430');
break;
case 'sha512':
$t = pack('H*',
'304f300b06096086480165030402030440');
break;
default:
return false;
}
$t.= $h;
$tLen = strlen($t);
if ($emLen < $tLen + 11) {
user_error('Intended encoded message length too
short');
return false;
}
$ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
$em = "\0\1$ps\0$t";
return $em;
}
/**
* RSASSA-PKCS1-V1_5-SIGN
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1
RFC3447#section-8.2.1}.
*
* @access private
* @param string $m
* @return string
*/
function _rsassa_pkcs1_v1_5_sign($m)
{
// EMSA-PKCS1-v1_5 encoding
$em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
if ($em === false) {
user_error('RSA modulus too short');
return false;
}
// RSA signature
$m = $this->_os2ip($em);
$s = $this->_rsasp1($m);
$s = $this->_i2osp($s, $this->k);
// Output the signature S
return $s;
}
/**
* RSASSA-PKCS1-V1_5-VERIFY
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2
RFC3447#section-8.2.2}.
*
* @access private
* @param string $m
* @param string $s
* @return string
*/
function _rsassa_pkcs1_v1_5_verify($m, $s)
{
// Length checking
if (strlen($s) != $this->k) {
user_error('Invalid signature');
return false;
}
// RSA verification
$s = $this->_os2ip($s);
$m2 = $this->_rsavp1($s);
if ($m2 === false) {
user_error('Invalid signature');
return false;
}
$em = $this->_i2osp($m2, $this->k);
if ($em === false) {
user_error('Invalid signature');
return false;
}
// EMSA-PKCS1-v1_5 encoding
$em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
$em3 = $this->_emsa_pkcs1_v1_5_encode_without_null($m,
$this->k);
if ($em2 === false && $em3 === false) {
user_error('RSA modulus too short');
return false;
}
// Compare
return ($em2 !== false && $this->_equals($em, $em2)) ||
($em3 !== false && $this->_equals($em, $em3));
}
/**
* Set Encryption Mode
*
* Valid values include self::ENCRYPTION_OAEP and
self::ENCRYPTION_PKCS1.
*
* @access public
* @param int $mode
*/
function setEncryptionMode($mode)
{
$this->encryptionMode = $mode;
}
/**
* Set Signature Mode
*
* Valid values include self::SIGNATURE_PSS and self::SIGNATURE_PKCS1
*
* @access public
* @param int $mode
*/
function setSignatureMode($mode)
{
$this->signatureMode = $mode;
}
/**
* Set public key comment.
*
* @access public
* @param string $comment
*/
function setComment($comment)
{
$this->comment = $comment;
}
/**
* Get public key comment.
*
* @access public
* @return string
*/
function getComment()
{
return $this->comment;
}
/**
* Encryption
*
* Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place
limits on how long $plaintext can be.
* If $plaintext exceeds those limits it will be broken up so that it
does and the resultant ciphertext's will
* be concatenated together.
*
* @see self::decrypt()
* @access public
* @param string $plaintext
* @return string
*/
function encrypt($plaintext)
{
switch ($this->encryptionMode) {
case self::ENCRYPTION_NONE:
$plaintext = str_split($plaintext, $this->k);
$ciphertext = '';
foreach ($plaintext as $m) {
$ciphertext.= $this->_raw_encrypt($m);
}
return $ciphertext;
case self::ENCRYPTION_PKCS1:
$length = $this->k - 11;
if ($length <= 0) {
return false;
}
$plaintext = str_split($plaintext, $length);
$ciphertext = '';
foreach ($plaintext as $m) {
$ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
}
return $ciphertext;
//case self::ENCRYPTION_OAEP:
default:
$length = $this->k - 2 * $this->hLen - 2;
if ($length <= 0) {
return false;
}
$plaintext = str_split($plaintext, $length);
$ciphertext = '';
foreach ($plaintext as $m) {
$ciphertext.= $this->_rsaes_oaep_encrypt($m);
}
return $ciphertext;
}
}
/**
* Decryption
*
* @see self::encrypt()
* @access public
* @param string $ciphertext
* @return string
*/
function decrypt($ciphertext)
{
if ($this->k <= 0) {
return false;
}
$ciphertext = str_split($ciphertext, $this->k);
$ciphertext[count($ciphertext) - 1] =
str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0),
STR_PAD_LEFT);
$plaintext = '';
switch ($this->encryptionMode) {
case self::ENCRYPTION_NONE:
$decrypt = '_raw_encrypt';
break;
case self::ENCRYPTION_PKCS1:
$decrypt = '_rsaes_pkcs1_v1_5_decrypt';
break;
//case self::ENCRYPTION_OAEP:
default:
$decrypt = '_rsaes_oaep_decrypt';
}
foreach ($ciphertext as $c) {
$temp = $this->$decrypt($c);
if ($temp === false) {
return false;
}
$plaintext.= $temp;
}
return $plaintext;
}
/**
* Create a signature
*
* @see self::verify()
* @access public
* @param string $message
* @return string
*/
function sign($message)
{
if (empty($this->modulus) || empty($this->exponent)) {
return false;
}
switch ($this->signatureMode) {
case self::SIGNATURE_PKCS1:
return $this->_rsassa_pkcs1_v1_5_sign($message);
//case self::SIGNATURE_PSS:
default:
return $this->_rsassa_pss_sign($message);
}
}
/**
* Verifies a signature
*
* @see self::sign()
* @access public
* @param string $message
* @param string $signature
* @return bool
*/
function verify($message, $signature)
{
if (empty($this->modulus) || empty($this->exponent)) {
return false;
}
switch ($this->signatureMode) {
case self::SIGNATURE_PKCS1:
return $this->_rsassa_pkcs1_v1_5_verify($message,
$signature);
//case self::SIGNATURE_PSS:
default:
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('#.*?^-+[^-]+-+[\r\n ]*$#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;
}
}
phpseclib/phpseclib/Crypt/TripleDES.php000064400000033232151161207740014102
0ustar00<?php
/**
* Pure-PHP implementation of Triple DES.
*
* Uses mcrypt, if available, and an internal implementation, otherwise.
Operates in the EDE3 mode (encrypt-decrypt-encrypt).
*
* PHP version 5
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $des = new \phpseclib\Crypt\TripleDES();
*
* $des->setKey('abcdefghijklmnopqrstuvwx');
*
* $size = 10 * 1024;
* $plaintext = '';
* for ($i = 0; $i < $size; $i++) {
* $plaintext.= 'a';
* }
*
* echo $des->decrypt($des->encrypt($plaintext));
* ?>
* </code>
*
* @category Crypt
* @package TripleDES
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Pure-PHP implementation of Triple DES.
*
* @package TripleDES
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class TripleDES extends DES
{
/**
* Encrypt / decrypt using inner chaining
*
* Inner chaining is used by SSH-1 and is generally considered to be
less secure then outer chaining (self::MODE_CBC3).
*/
const MODE_3CBC = -2;
/**
* Encrypt / decrypt using outer chaining
*
* Outer chaining is used by SSH-2 and when the mode is set to
\phpseclib\Crypt\Base::MODE_CBC.
*/
const MODE_CBC3 = Base::MODE_CBC;
/**
* Key Length (in bytes)
*
* @see \phpseclib\Crypt\TripleDES::setKeyLength()
* @var int
* @access private
*/
var $key_length = 24;
/**
* The default salt used by setPassword()
*
* @see \phpseclib\Crypt\Base::password_default_salt
* @see \phpseclib\Crypt\Base::setPassword()
* @var string
* @access private
*/
var $password_default_salt = 'phpseclib';
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib\Crypt\DES::cipher_name_mcrypt
* @see \phpseclib\Crypt\Base::cipher_name_mcrypt
* @var string
* @access private
*/
var $cipher_name_mcrypt = 'tripledes';
/**
* Optimizing value while CFB-encrypting
*
* @see \phpseclib\Crypt\Base::cfb_init_len
* @var int
* @access private
*/
var $cfb_init_len = 750;
/**
* max possible size of $key
*
* @see self::setKey()
* @see \phpseclib\Crypt\DES::setKey()
* @var string
* @access private
*/
var $key_length_max = 24;
/**
* Internal flag whether using self::MODE_3CBC or not
*
* @var bool
* @access private
*/
var $mode_3cbc;
/**
* The \phpseclib\Crypt\DES objects
*
* Used only if $mode_3cbc === true
*
* @var array
* @access private
*/
var $des;
/**
* Default Constructor.
*
* Determines whether or not the mcrypt extension should be used.
*
* $mode could be:
*
* - \phpseclib\Crypt\Base::MODE_ECB
*
* - \phpseclib\Crypt\Base::MODE_CBC
*
* - \phpseclib\Crypt\Base::MODE_CTR
*
* - \phpseclib\Crypt\Base::MODE_CFB
*
* - \phpseclib\Crypt\Base::MODE_OFB
*
* - \phpseclib\Crypt\TripleDES::MODE_3CBC
*
* If not explicitly set, \phpseclib\Crypt\Base::MODE_CBC will be used.
*
* @see \phpseclib\Crypt\DES::__construct()
* @see \phpseclib\Crypt\Base::__construct()
* @param int $mode
* @access public
*/
function __construct($mode = Base::MODE_CBC)
{
switch ($mode) {
// In case of self::MODE_3CBC, we init as CRYPT_DES_MODE_CBC
// and additional flag us internally as 3CBC
case self::MODE_3CBC:
parent::__construct(Base::MODE_CBC);
$this->mode_3cbc = true;
// This three $des'es will do the 3CBC work (if $key
> 64bits)
$this->des = array(
new DES(Base::MODE_CBC),
new DES(Base::MODE_CBC),
new DES(Base::MODE_CBC),
);
// we're going to be doing the padding, ourselves, so
disable it in the \phpseclib\Crypt\DES objects
$this->des[0]->disablePadding();
$this->des[1]->disablePadding();
$this->des[2]->disablePadding();
break;
// If not 3CBC, we init as usual
default:
parent::__construct($mode);
}
}
/**
* Test for engine validity
*
* This is mainly just a wrapper to set things up for
\phpseclib\Crypt\Base::isValidEngine()
*
* @see \phpseclib\Crypt\Base::__construct()
* @param int $engine
* @access public
* @return bool
*/
function isValidEngine($engine)
{
if ($engine == self::ENGINE_OPENSSL) {
$this->cipher_name_openssl_ecb = 'des-ede3';
$mode = $this->_openssl_translate_mode();
$this->cipher_name_openssl = $mode == 'ecb' ?
'des-ede3' : 'des-ede3-' . $mode;
}
return parent::isValidEngine($engine);
}
/**
* Sets the initialization vector. (optional)
*
* SetIV is not required when \phpseclib\Crypt\Base::MODE_ECB is being
used. If not explicitly set, it'll be assumed
* to be all zero's.
*
* @see \phpseclib\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);
}
}
/**
* Sets the key length.
*
* Valid key lengths are 64, 128 and 192
*
* @see \phpseclib\Crypt\Base:setKeyLength()
* @access public
* @param int $length
*/
function setKeyLength($length)
{
$length >>= 3;
switch (true) {
case $length <= 8:
$this->key_length = 8;
break;
case $length <= 16:
$this->key_length = 16;
break;
default:
$this->key_length = 24;
}
parent::setKeyLength($length);
}
/**
* Sets the key.
*
* Keys can be of any length. Triple DES, itself, can use 128-bit (eg.
strlen($key) == 16) or
* 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.
*
* If the key is not explicitly set, it'll be assumed to be all
null bytes.
*
* @access public
* @see \phpseclib\Crypt\DES::setKey()
* @see \phpseclib\Crypt\Base::setKey()
* @param string $key
*/
function setKey($key)
{
$length = $this->explicit_key_length ? $this->key_length :
strlen($key);
if ($length > 8) {
$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:
// 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[1]->setKey(substr($key, 8, 8));
$this->des[2]->setKey(substr($key, 16, 8));
}
}
/**
* Encrypts a message.
*
* @see \phpseclib\Crypt\Base::encrypt()
* @access public
* @param string $plaintext
* @return string $cipertext
*/
function encrypt($plaintext)
{
// parent::en/decrypt() is able to do all the work for all modes
and keylengths,
// except for: self::MODE_3CBC (inner chaining CBC) with a key >
64bits
// if the key is smaller then 8, do what we'd normally do
if ($this->mode_3cbc && strlen($this->key) > 8) {
return $this->des[2]->encrypt(
$this->des[1]->decrypt(
$this->des[0]->encrypt(
$this->_pad($plaintext)
)
)
);
}
return parent::encrypt($plaintext);
}
/**
* Decrypts a message.
*
* @see \phpseclib\Crypt\Base::decrypt()
* @access public
* @param string $ciphertext
* @return string $plaintext
*/
function decrypt($ciphertext)
{
if ($this->mode_3cbc && strlen($this->key) > 8) {
return $this->_unpad(
$this->des[0]->decrypt(
$this->des[1]->encrypt(
$this->des[2]->decrypt(
str_pad($ciphertext, (strlen($ciphertext) + 7)
& 0xFFFFFFF8, "\0")
)
)
)
);
}
return parent::decrypt($ciphertext);
}
/**
* 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 $des->encrypt(substr($plaintext, 0, 8));
* echo $des->encrypt(substr($plaintext, 8, 8));
* </code>
* <code>
* echo $des->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>
* $des->encrypt(substr($plaintext, 0, 8));
* echo $des->decrypt($des->encrypt(substr($plaintext, 8,
8)));
* </code>
* <code>
* echo $des->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 \phpseclib\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 \phpseclib\Crypt\Base::enableContinuousBuffer()
* @see self::disableContinuousBuffer()
* @access public
*/
function enableContinuousBuffer()
{
parent::enableContinuousBuffer();
if ($this->mode_3cbc) {
$this->des[0]->enableContinuousBuffer();
$this->des[1]->enableContinuousBuffer();
$this->des[2]->enableContinuousBuffer();
}
}
/**
* Treat consecutive packets as if they are a discontinuous buffer.
*
* The default behavior.
*
* @see \phpseclib\Crypt\Base::disableContinuousBuffer()
* @see self::enableContinuousBuffer()
* @access public
*/
function disableContinuousBuffer()
{
parent::disableContinuousBuffer();
if ($this->mode_3cbc) {
$this->des[0]->disableContinuousBuffer();
$this->des[1]->disableContinuousBuffer();
$this->des[2]->disableContinuousBuffer();
}
}
/**
* Creates the key schedule
*
* @see \phpseclib\Crypt\DES::_setupKey()
* @see \phpseclib\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();
}
/**
* Sets the internal crypt engine
*
* @see \phpseclib\Crypt\Base::__construct()
* @see \phpseclib\Crypt\Base::setPreferredEngine()
* @param int $engine
* @access public
* @return int
*/
function setPreferredEngine($engine)
{
if ($this->mode_3cbc) {
$this->des[0]->setPreferredEngine($engine);
$this->des[1]->setPreferredEngine($engine);
$this->des[2]->setPreferredEngine($engine);
}
return parent::setPreferredEngine($engine);
}
}
phpseclib/phpseclib/Crypt/Twofish.php000064400000111700151161207740013727
0ustar00<?php
/**
* Pure-PHP implementation of Twofish.
*
* Uses mcrypt, if available, and an internal implementation, otherwise.
*
* PHP version 5
*
* Useful resources are as follows:
*
* - {@link http://en.wikipedia.org/wiki/Twofish Wikipedia description of
Twofish}
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $twofish = new \phpseclib\Crypt\Twofish();
*
* $twofish->setKey('12345678901234567890123456789012');
*
* $plaintext = str_repeat('a', 1024);
*
* echo $twofish->decrypt($twofish->encrypt($plaintext));
* ?>
* </code>
*
* @category Crypt
* @package Twofish
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
/**
* Pure-PHP implementation of Twofish.
*
* @package Twofish
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
* @access public
*/
class Twofish extends Base
{
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib\Crypt\Base::cipher_name_mcrypt
* @var string
* @access private
*/
var $cipher_name_mcrypt = 'twofish';
/**
* Optimizing value while CFB-encrypting
*
* @see \phpseclib\Crypt\Base::cfb_init_len
* @var int
* @access private
*/
var $cfb_init_len = 800;
/**
* Q-Table
*
* @var array
* @access private
*/
var $q0 = array(
0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76,
0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38,
0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C,
0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48,
0xF2, 0xD0, 0x8B, 0x30, 0x84, 0x54, 0xDF, 0x23,
0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82,
0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C,
0xA6, 0xEB, 0xA5, 0xBE, 0x16, 0x0C, 0xE3, 0x61,
0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B,
0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1,
0xE1, 0xE6, 0xBD, 0x45, 0xE2, 0xF4, 0xB6, 0x66,
0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7,
0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA,
0xEA, 0x77, 0x39, 0xAF, 0x33, 0xC9, 0x62, 0x71,
0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8,
0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7,
0xA1, 0x1D, 0xAA, 0xED, 0x06, 0x70, 0xB2, 0xD2,
0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90,
0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB,
0x9E, 0x9C, 0x52, 0x1B, 0x5F, 0x93, 0x0A, 0xEF,
0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B,
0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64,
0x2A, 0xCE, 0xCB, 0x2F, 0xFC, 0x97, 0x05, 0x7A,
0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A,
0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02,
0xB8, 0xDA, 0xB0, 0x17, 0x55, 0x1F, 0x8A, 0x7D,
0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72,
0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34,
0x6E, 0x50, 0xDE, 0x68, 0x65, 0xBC, 0xDB, 0xF8,
0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4,
0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00,
0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0
);
/**
* Q-Table
*
* @var array
* @access private
*/
var $q1 = array(
0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8,
0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B,
0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1,
0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F,
0x5E, 0xBA, 0xAE, 0x5B, 0x8A, 0x00, 0xBC, 0x9D,
0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5,
0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3,
0xB2, 0x73, 0x4C, 0x54, 0x92, 0x74, 0x36, 0x51,
0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96,
0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C,
0x13, 0x95, 0x9C, 0xC7, 0x24, 0x46, 0x3B, 0x70,
0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8,
0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC,
0x03, 0x6F, 0x08, 0xBF, 0x40, 0xE7, 0x2B, 0xE2,
0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9,
0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17,
0x66, 0x94, 0xA1, 0x1D, 0x3D, 0xF0, 0xDE, 0xB3,
0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E,
0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49,
0x81, 0x88, 0xEE, 0x21, 0xC4, 0x1A, 0xEB, 0xD9,
0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01,
0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48,
0x4F, 0xF2, 0x65, 0x8E, 0x78, 0x5C, 0x58, 0x19,
0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64,
0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5,
0xCE, 0xE9, 0x68, 0x44, 0xE0, 0x4D, 0x43, 0x69,
0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E,
0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC,
0x22, 0xC9, 0xC0, 0x9B, 0x89, 0xD4, 0xED, 0xAB,
0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9,
0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2,
0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91
);
/**
* M-Table
*
* @var array
* @access private
*/
var $m0 = array(
0xBCBC3275, 0xECEC21F3, 0x202043C6, 0xB3B3C9F4, 0xDADA03DB,
0x02028B7B, 0xE2E22BFB, 0x9E9EFAC8,
0xC9C9EC4A, 0xD4D409D3, 0x18186BE6, 0x1E1E9F6B, 0x98980E45,
0xB2B2387D, 0xA6A6D2E8, 0x2626B74B,
0x3C3C57D6, 0x93938A32, 0x8282EED8, 0x525298FD, 0x7B7BD437,
0xBBBB3771, 0x5B5B97F1, 0x474783E1,
0x24243C30, 0x5151E20F, 0xBABAC6F8, 0x4A4AF31B, 0xBFBF4887,
0x0D0D70FA, 0xB0B0B306, 0x7575DE3F,
0xD2D2FD5E, 0x7D7D20BA, 0x666631AE, 0x3A3AA35B, 0x59591C8A,
0x00000000, 0xCDCD93BC, 0x1A1AE09D,
0xAEAE2C6D, 0x7F7FABC1, 0x2B2BC7B1, 0xBEBEB90E, 0xE0E0A080,
0x8A8A105D, 0x3B3B52D2, 0x6464BAD5,
0xD8D888A0, 0xE7E7A584, 0x5F5FE807, 0x1B1B1114, 0x2C2CC2B5,
0xFCFCB490, 0x3131272C, 0x808065A3,
0x73732AB2, 0x0C0C8173, 0x79795F4C, 0x6B6B4154, 0x4B4B0292,
0x53536974, 0x94948F36, 0x83831F51,
0x2A2A3638, 0xC4C49CB0, 0x2222C8BD, 0xD5D5F85A, 0xBDBDC3FC,
0x48487860, 0xFFFFCE62, 0x4C4C0796,
0x4141776C, 0xC7C7E642, 0xEBEB24F7, 0x1C1C1410, 0x5D5D637C,
0x36362228, 0x6767C027, 0xE9E9AF8C,
0x4444F913, 0x1414EA95, 0xF5F5BB9C, 0xCFCF18C7, 0x3F3F2D24,
0xC0C0E346, 0x7272DB3B, 0x54546C70,
0x29294CCA, 0xF0F035E3, 0x0808FE85, 0xC6C617CB, 0xF3F34F11,
0x8C8CE4D0, 0xA4A45993, 0xCACA96B8,
0x68683BA6, 0xB8B84D83, 0x38382820, 0xE5E52EFF, 0xADAD569F,
0x0B0B8477, 0xC8C81DC3, 0x9999FFCC,
0x5858ED03, 0x19199A6F, 0x0E0E0A08, 0x95957EBF, 0x70705040,
0xF7F730E7, 0x6E6ECF2B, 0x1F1F6EE2,
0xB5B53D79, 0x09090F0C, 0x616134AA, 0x57571682, 0x9F9F0B41,
0x9D9D803A, 0x111164EA, 0x2525CDB9,
0xAFAFDDE4, 0x4545089A, 0xDFDF8DA4, 0xA3A35C97, 0xEAEAD57E,
0x353558DA, 0xEDEDD07A, 0x4343FC17,
0xF8F8CB66, 0xFBFBB194, 0x3737D3A1, 0xFAFA401D, 0xC2C2683D,
0xB4B4CCF0, 0x32325DDE, 0x9C9C71B3,
0x5656E70B, 0xE3E3DA72, 0x878760A7, 0x15151B1C, 0xF9F93AEF,
0x6363BFD1, 0x3434A953, 0x9A9A853E,
0xB1B1428F, 0x7C7CD133, 0x88889B26, 0x3D3DA65F, 0xA1A1D7EC,
0xE4E4DF76, 0x8181942A, 0x91910149,
0x0F0FFB81, 0xEEEEAA88, 0x161661EE, 0xD7D77321, 0x9797F5C4,
0xA5A5A81A, 0xFEFE3FEB, 0x6D6DB5D9,
0x7878AEC5, 0xC5C56D39, 0x1D1DE599, 0x7676A4CD, 0x3E3EDCAD,
0xCBCB6731, 0xB6B6478B, 0xEFEF5B01,
0x12121E18, 0x6060C523, 0x6A6AB0DD, 0x4D4DF61F, 0xCECEE94E,
0xDEDE7C2D, 0x55559DF9, 0x7E7E5A48,
0x2121B24F, 0x03037AF2, 0xA0A02665, 0x5E5E198E, 0x5A5A6678,
0x65654B5C, 0x62624E58, 0xFDFD4519,
0x0606F48D, 0x404086E5, 0xF2F2BE98, 0x3333AC57, 0x17179067,
0x05058E7F, 0xE8E85E05, 0x4F4F7D64,
0x89896AAF, 0x10109563, 0x74742FB6, 0x0A0A75FE, 0x5C5C92F5,
0x9B9B74B7, 0x2D2D333C, 0x3030D6A5,
0x2E2E49CE, 0x494989E9, 0x46467268, 0x77775544, 0xA8A8D8E0,
0x9696044D, 0x2828BD43, 0xA9A92969,
0xD9D97929, 0x8686912E, 0xD1D187AC, 0xF4F44A15, 0x8D8D1559,
0xD6D682A8, 0xB9B9BC0A, 0x42420D9E,
0xF6F6C16E, 0x2F2FB847, 0xDDDD06DF, 0x23233934, 0xCCCC6235,
0xF1F1C46A, 0xC1C112CF, 0x8585EBDC,
0x8F8F9E22, 0x7171A1C9, 0x9090F0C0, 0xAAAA539B, 0x0101F189,
0x8B8BE1D4, 0x4E4E8CED, 0x8E8E6FAB,
0xABABA212, 0x6F6F3EA2, 0xE6E6540D, 0xDBDBF252, 0x92927BBB,
0xB7B7B602, 0x6969CA2F, 0x3939D9A9,
0xD3D30CD7, 0xA7A72361, 0xA2A2AD1E, 0xC3C399B4, 0x6C6C4450,
0x07070504, 0x04047FF6, 0x272746C2,
0xACACA716, 0xD0D07625, 0x50501386, 0xDCDCF756, 0x84841A55,
0xE1E15109, 0x7A7A25BE, 0x1313EF91
);
/**
* M-Table
*
* @var array
* @access private
*/
var $m1 = array(
0xA9D93939, 0x67901717, 0xB3719C9C, 0xE8D2A6A6, 0x04050707,
0xFD985252, 0xA3658080, 0x76DFE4E4,
0x9A084545, 0x92024B4B, 0x80A0E0E0, 0x78665A5A, 0xE4DDAFAF,
0xDDB06A6A, 0xD1BF6363, 0x38362A2A,
0x0D54E6E6, 0xC6432020, 0x3562CCCC, 0x98BEF2F2, 0x181E1212,
0xF724EBEB, 0xECD7A1A1, 0x6C774141,
0x43BD2828, 0x7532BCBC, 0x37D47B7B, 0x269B8888, 0xFA700D0D,
0x13F94444, 0x94B1FBFB, 0x485A7E7E,
0xF27A0303, 0xD0E48C8C, 0x8B47B6B6, 0x303C2424, 0x84A5E7E7,
0x54416B6B, 0xDF06DDDD, 0x23C56060,
0x1945FDFD, 0x5BA33A3A, 0x3D68C2C2, 0x59158D8D, 0xF321ECEC,
0xAE316666, 0xA23E6F6F, 0x82165757,
0x63951010, 0x015BEFEF, 0x834DB8B8, 0x2E918686, 0xD9B56D6D,
0x511F8383, 0x9B53AAAA, 0x7C635D5D,
0xA63B6868, 0xEB3FFEFE, 0xA5D63030, 0xBE257A7A, 0x16A7ACAC,
0x0C0F0909, 0xE335F0F0, 0x6123A7A7,
0xC0F09090, 0x8CAFE9E9, 0x3A809D9D, 0xF5925C5C, 0x73810C0C,
0x2C273131, 0x2576D0D0, 0x0BE75656,
0xBB7B9292, 0x4EE9CECE, 0x89F10101, 0x6B9F1E1E, 0x53A93434,
0x6AC4F1F1, 0xB499C3C3, 0xF1975B5B,
0xE1834747, 0xE66B1818, 0xBDC82222, 0x450E9898, 0xE26E1F1F,
0xF4C9B3B3, 0xB62F7474, 0x66CBF8F8,
0xCCFF9999, 0x95EA1414, 0x03ED5858, 0x56F7DCDC, 0xD4E18B8B,
0x1C1B1515, 0x1EADA2A2, 0xD70CD3D3,
0xFB2BE2E2, 0xC31DC8C8, 0x8E195E5E, 0xB5C22C2C, 0xE9894949,
0xCF12C1C1, 0xBF7E9595, 0xBA207D7D,
0xEA641111, 0x77840B0B, 0x396DC5C5, 0xAF6A8989, 0x33D17C7C,
0xC9A17171, 0x62CEFFFF, 0x7137BBBB,
0x81FB0F0F, 0x793DB5B5, 0x0951E1E1, 0xADDC3E3E, 0x242D3F3F,
0xCDA47676, 0xF99D5555, 0xD8EE8282,
0xE5864040, 0xC5AE7878, 0xB9CD2525, 0x4D049696, 0x44557777,
0x080A0E0E, 0x86135050, 0xE730F7F7,
0xA1D33737, 0x1D40FAFA, 0xAA346161, 0xED8C4E4E, 0x06B3B0B0,
0x706C5454, 0xB22A7373, 0xD2523B3B,
0x410B9F9F, 0x7B8B0202, 0xA088D8D8, 0x114FF3F3, 0x3167CBCB,
0xC2462727, 0x27C06767, 0x90B4FCFC,
0x20283838, 0xF67F0404, 0x60784848, 0xFF2EE5E5, 0x96074C4C,
0x5C4B6565, 0xB1C72B2B, 0xAB6F8E8E,
0x9E0D4242, 0x9CBBF5F5, 0x52F2DBDB, 0x1BF34A4A, 0x5FA63D3D,
0x9359A4A4, 0x0ABCB9B9, 0xEF3AF9F9,
0x91EF1313, 0x85FE0808, 0x49019191, 0xEE611616, 0x2D7CDEDE,
0x4FB22121, 0x8F42B1B1, 0x3BDB7272,
0x47B82F2F, 0x8748BFBF, 0x6D2CAEAE, 0x46E3C0C0, 0xD6573C3C,
0x3E859A9A, 0x6929A9A9, 0x647D4F4F,
0x2A948181, 0xCE492E2E, 0xCB17C6C6, 0x2FCA6969, 0xFCC3BDBD,
0x975CA3A3, 0x055EE8E8, 0x7AD0EDED,
0xAC87D1D1, 0x7F8E0505, 0xD5BA6464, 0x1AA8A5A5, 0x4BB72626,
0x0EB9BEBE, 0xA7608787, 0x5AF8D5D5,
0x28223636, 0x14111B1B, 0x3FDE7575, 0x2979D9D9, 0x88AAEEEE,
0x3C332D2D, 0x4C5F7979, 0x02B6B7B7,
0xB896CACA, 0xDA583535, 0xB09CC4C4, 0x17FC4343, 0x551A8484,
0x1FF64D4D, 0x8A1C5959, 0x7D38B2B2,
0x57AC3333, 0xC718CFCF, 0x8DF40606, 0x74695353, 0xB7749B9B,
0xC4F59797, 0x9F56ADAD, 0x72DAE3E3,
0x7ED5EAEA, 0x154AF4F4, 0x229E8F8F, 0x12A2ABAB, 0x584E6262,
0x07E85F5F, 0x99E51D1D, 0x34392323,
0x6EC1F6F6, 0x50446C6C, 0xDE5D3232, 0x68724646, 0x6526A0A0,
0xBC93CDCD, 0xDB03DADA, 0xF8C6BABA,
0xC8FA9E9E, 0xA882D6D6, 0x2BCF6E6E, 0x40507070, 0xDCEB8585,
0xFE750A0A, 0x328A9393, 0xA48DDFDF,
0xCA4C2929, 0x10141C1C, 0x2173D7D7, 0xF0CCB4B4, 0xD309D4D4,
0x5D108A8A, 0x0FE25151, 0x00000000,
0x6F9A1919, 0x9DE01A1A, 0x368F9494, 0x42E6C7C7, 0x4AECC9C9,
0x5EFDD2D2, 0xC1AB7F7F, 0xE0D8A8A8
);
/**
* M-Table
*
* @var array
* @access private
*/
var $m2 = array(
0xBC75BC32, 0xECF3EC21, 0x20C62043, 0xB3F4B3C9, 0xDADBDA03,
0x027B028B, 0xE2FBE22B, 0x9EC89EFA,
0xC94AC9EC, 0xD4D3D409, 0x18E6186B, 0x1E6B1E9F, 0x9845980E,
0xB27DB238, 0xA6E8A6D2, 0x264B26B7,
0x3CD63C57, 0x9332938A, 0x82D882EE, 0x52FD5298, 0x7B377BD4,
0xBB71BB37, 0x5BF15B97, 0x47E14783,
0x2430243C, 0x510F51E2, 0xBAF8BAC6, 0x4A1B4AF3, 0xBF87BF48,
0x0DFA0D70, 0xB006B0B3, 0x753F75DE,
0xD25ED2FD, 0x7DBA7D20, 0x66AE6631, 0x3A5B3AA3, 0x598A591C,
0x00000000, 0xCDBCCD93, 0x1A9D1AE0,
0xAE6DAE2C, 0x7FC17FAB, 0x2BB12BC7, 0xBE0EBEB9, 0xE080E0A0,
0x8A5D8A10, 0x3BD23B52, 0x64D564BA,
0xD8A0D888, 0xE784E7A5, 0x5F075FE8, 0x1B141B11, 0x2CB52CC2,
0xFC90FCB4, 0x312C3127, 0x80A38065,
0x73B2732A, 0x0C730C81, 0x794C795F, 0x6B546B41, 0x4B924B02,
0x53745369, 0x9436948F, 0x8351831F,
0x2A382A36, 0xC4B0C49C, 0x22BD22C8, 0xD55AD5F8, 0xBDFCBDC3,
0x48604878, 0xFF62FFCE, 0x4C964C07,
0x416C4177, 0xC742C7E6, 0xEBF7EB24, 0x1C101C14, 0x5D7C5D63,
0x36283622, 0x672767C0, 0xE98CE9AF,
0x441344F9, 0x149514EA, 0xF59CF5BB, 0xCFC7CF18, 0x3F243F2D,
0xC046C0E3, 0x723B72DB, 0x5470546C,
0x29CA294C, 0xF0E3F035, 0x088508FE, 0xC6CBC617, 0xF311F34F,
0x8CD08CE4, 0xA493A459, 0xCAB8CA96,
0x68A6683B, 0xB883B84D, 0x38203828, 0xE5FFE52E, 0xAD9FAD56,
0x0B770B84, 0xC8C3C81D, 0x99CC99FF,
0x580358ED, 0x196F199A, 0x0E080E0A, 0x95BF957E, 0x70407050,
0xF7E7F730, 0x6E2B6ECF, 0x1FE21F6E,
0xB579B53D, 0x090C090F, 0x61AA6134, 0x57825716, 0x9F419F0B,
0x9D3A9D80, 0x11EA1164, 0x25B925CD,
0xAFE4AFDD, 0x459A4508, 0xDFA4DF8D, 0xA397A35C, 0xEA7EEAD5,
0x35DA3558, 0xED7AEDD0, 0x431743FC,
0xF866F8CB, 0xFB94FBB1, 0x37A137D3, 0xFA1DFA40, 0xC23DC268,
0xB4F0B4CC, 0x32DE325D, 0x9CB39C71,
0x560B56E7, 0xE372E3DA, 0x87A78760, 0x151C151B, 0xF9EFF93A,
0x63D163BF, 0x345334A9, 0x9A3E9A85,
0xB18FB142, 0x7C337CD1, 0x8826889B, 0x3D5F3DA6, 0xA1ECA1D7,
0xE476E4DF, 0x812A8194, 0x91499101,
0x0F810FFB, 0xEE88EEAA, 0x16EE1661, 0xD721D773, 0x97C497F5,
0xA51AA5A8, 0xFEEBFE3F, 0x6DD96DB5,
0x78C578AE, 0xC539C56D, 0x1D991DE5, 0x76CD76A4, 0x3EAD3EDC,
0xCB31CB67, 0xB68BB647, 0xEF01EF5B,
0x1218121E, 0x602360C5, 0x6ADD6AB0, 0x4D1F4DF6, 0xCE4ECEE9,
0xDE2DDE7C, 0x55F9559D, 0x7E487E5A,
0x214F21B2, 0x03F2037A, 0xA065A026, 0x5E8E5E19, 0x5A785A66,
0x655C654B, 0x6258624E, 0xFD19FD45,
0x068D06F4, 0x40E54086, 0xF298F2BE, 0x335733AC, 0x17671790,
0x057F058E, 0xE805E85E, 0x4F644F7D,
0x89AF896A, 0x10631095, 0x74B6742F, 0x0AFE0A75, 0x5CF55C92,
0x9BB79B74, 0x2D3C2D33, 0x30A530D6,
0x2ECE2E49, 0x49E94989, 0x46684672, 0x77447755, 0xA8E0A8D8,
0x964D9604, 0x284328BD, 0xA969A929,
0xD929D979, 0x862E8691, 0xD1ACD187, 0xF415F44A, 0x8D598D15,
0xD6A8D682, 0xB90AB9BC, 0x429E420D,
0xF66EF6C1, 0x2F472FB8, 0xDDDFDD06, 0x23342339, 0xCC35CC62,
0xF16AF1C4, 0xC1CFC112, 0x85DC85EB,
0x8F228F9E, 0x71C971A1, 0x90C090F0, 0xAA9BAA53, 0x018901F1,
0x8BD48BE1, 0x4EED4E8C, 0x8EAB8E6F,
0xAB12ABA2, 0x6FA26F3E, 0xE60DE654, 0xDB52DBF2, 0x92BB927B,
0xB702B7B6, 0x692F69CA, 0x39A939D9,
0xD3D7D30C, 0xA761A723, 0xA21EA2AD, 0xC3B4C399, 0x6C506C44,
0x07040705, 0x04F6047F, 0x27C22746,
0xAC16ACA7, 0xD025D076, 0x50865013, 0xDC56DCF7, 0x8455841A,
0xE109E151, 0x7ABE7A25, 0x139113EF
);
/**
* M-Table
*
* @var array
* @access private
*/
var $m3 = array(
0xD939A9D9, 0x90176790, 0x719CB371, 0xD2A6E8D2, 0x05070405,
0x9852FD98, 0x6580A365, 0xDFE476DF,
0x08459A08, 0x024B9202, 0xA0E080A0, 0x665A7866, 0xDDAFE4DD,
0xB06ADDB0, 0xBF63D1BF, 0x362A3836,
0x54E60D54, 0x4320C643, 0x62CC3562, 0xBEF298BE, 0x1E12181E,
0x24EBF724, 0xD7A1ECD7, 0x77416C77,
0xBD2843BD, 0x32BC7532, 0xD47B37D4, 0x9B88269B, 0x700DFA70,
0xF94413F9, 0xB1FB94B1, 0x5A7E485A,
0x7A03F27A, 0xE48CD0E4, 0x47B68B47, 0x3C24303C, 0xA5E784A5,
0x416B5441, 0x06DDDF06, 0xC56023C5,
0x45FD1945, 0xA33A5BA3, 0x68C23D68, 0x158D5915, 0x21ECF321,
0x3166AE31, 0x3E6FA23E, 0x16578216,
0x95106395, 0x5BEF015B, 0x4DB8834D, 0x91862E91, 0xB56DD9B5,
0x1F83511F, 0x53AA9B53, 0x635D7C63,
0x3B68A63B, 0x3FFEEB3F, 0xD630A5D6, 0x257ABE25, 0xA7AC16A7,
0x0F090C0F, 0x35F0E335, 0x23A76123,
0xF090C0F0, 0xAFE98CAF, 0x809D3A80, 0x925CF592, 0x810C7381,
0x27312C27, 0x76D02576, 0xE7560BE7,
0x7B92BB7B, 0xE9CE4EE9, 0xF10189F1, 0x9F1E6B9F, 0xA93453A9,
0xC4F16AC4, 0x99C3B499, 0x975BF197,
0x8347E183, 0x6B18E66B, 0xC822BDC8, 0x0E98450E, 0x6E1FE26E,
0xC9B3F4C9, 0x2F74B62F, 0xCBF866CB,
0xFF99CCFF, 0xEA1495EA, 0xED5803ED, 0xF7DC56F7, 0xE18BD4E1,
0x1B151C1B, 0xADA21EAD, 0x0CD3D70C,
0x2BE2FB2B, 0x1DC8C31D, 0x195E8E19, 0xC22CB5C2, 0x8949E989,
0x12C1CF12, 0x7E95BF7E, 0x207DBA20,
0x6411EA64, 0x840B7784, 0x6DC5396D, 0x6A89AF6A, 0xD17C33D1,
0xA171C9A1, 0xCEFF62CE, 0x37BB7137,
0xFB0F81FB, 0x3DB5793D, 0x51E10951, 0xDC3EADDC, 0x2D3F242D,
0xA476CDA4, 0x9D55F99D, 0xEE82D8EE,
0x8640E586, 0xAE78C5AE, 0xCD25B9CD, 0x04964D04, 0x55774455,
0x0A0E080A, 0x13508613, 0x30F7E730,
0xD337A1D3, 0x40FA1D40, 0x3461AA34, 0x8C4EED8C, 0xB3B006B3,
0x6C54706C, 0x2A73B22A, 0x523BD252,
0x0B9F410B, 0x8B027B8B, 0x88D8A088, 0x4FF3114F, 0x67CB3167,
0x4627C246, 0xC06727C0, 0xB4FC90B4,
0x28382028, 0x7F04F67F, 0x78486078, 0x2EE5FF2E, 0x074C9607,
0x4B655C4B, 0xC72BB1C7, 0x6F8EAB6F,
0x0D429E0D, 0xBBF59CBB, 0xF2DB52F2, 0xF34A1BF3, 0xA63D5FA6,
0x59A49359, 0xBCB90ABC, 0x3AF9EF3A,
0xEF1391EF, 0xFE0885FE, 0x01914901, 0x6116EE61, 0x7CDE2D7C,
0xB2214FB2, 0x42B18F42, 0xDB723BDB,
0xB82F47B8, 0x48BF8748, 0x2CAE6D2C, 0xE3C046E3, 0x573CD657,
0x859A3E85, 0x29A96929, 0x7D4F647D,
0x94812A94, 0x492ECE49, 0x17C6CB17, 0xCA692FCA, 0xC3BDFCC3,
0x5CA3975C, 0x5EE8055E, 0xD0ED7AD0,
0x87D1AC87, 0x8E057F8E, 0xBA64D5BA, 0xA8A51AA8, 0xB7264BB7,
0xB9BE0EB9, 0x6087A760, 0xF8D55AF8,
0x22362822, 0x111B1411, 0xDE753FDE, 0x79D92979, 0xAAEE88AA,
0x332D3C33, 0x5F794C5F, 0xB6B702B6,
0x96CAB896, 0x5835DA58, 0x9CC4B09C, 0xFC4317FC, 0x1A84551A,
0xF64D1FF6, 0x1C598A1C, 0x38B27D38,
0xAC3357AC, 0x18CFC718, 0xF4068DF4, 0x69537469, 0x749BB774,
0xF597C4F5, 0x56AD9F56, 0xDAE372DA,
0xD5EA7ED5, 0x4AF4154A, 0x9E8F229E, 0xA2AB12A2, 0x4E62584E,
0xE85F07E8, 0xE51D99E5, 0x39233439,
0xC1F66EC1, 0x446C5044, 0x5D32DE5D, 0x72466872, 0x26A06526,
0x93CDBC93, 0x03DADB03, 0xC6BAF8C6,
0xFA9EC8FA, 0x82D6A882, 0xCF6E2BCF, 0x50704050, 0xEB85DCEB,
0x750AFE75, 0x8A93328A, 0x8DDFA48D,
0x4C29CA4C, 0x141C1014, 0x73D72173, 0xCCB4F0CC, 0x09D4D309,
0x108A5D10, 0xE2510FE2, 0x00000000,
0x9A196F9A, 0xE01A9DE0, 0x8F94368F, 0xE6C742E6, 0xECC94AEC,
0xFDD25EFD, 0xAB7FC1AB, 0xD8A8E0D8
);
/**
* The Key Schedule Array
*
* @var array
* @access private
*/
var $K = array();
/**
* The Key depended S-Table 0
*
* @var array
* @access private
*/
var $S0 = array();
/**
* The Key depended S-Table 1
*
* @var array
* @access private
*/
var $S1 = array();
/**
* The Key depended S-Table 2
*
* @var array
* @access private
*/
var $S2 = array();
/**
* The Key depended S-Table 3
*
* @var array
* @access private
*/
var $S3 = array();
/**
* Holds the last used key
*
* @var array
* @access private
*/
var $kl;
/**
* The Key Length (in bytes)
*
* @see Crypt_Twofish::setKeyLength()
* @var int
* @access private
*/
var $key_length = 16;
/**
* Sets the key length.
*
* Valid key lengths are 128, 192 or 256 bits
*
* @access public
* @param int $length
*/
function setKeyLength($length)
{
switch (true) {
case $length <= 128:
$this->key_length = 16;
break;
case $length <= 192:
$this->key_length = 24;
break;
default:
$this->key_length = 32;
}
parent::setKeyLength($length);
}
/**
* Setup the key (expansion)
*
* @see \phpseclib\Crypt\Base::_setupKey()
* @access private
*/
function _setupKey()
{
if (isset($this->kl['key']) && $this->key
=== $this->kl['key']) {
// already expanded
return;
}
$this->kl = array('key' => $this->key);
/* Key expanding and generating the key-depended s-boxes */
$le_longs = unpack('V*', $this->key);
$key = unpack('C*', $this->key);
$m0 = $this->m0;
$m1 = $this->m1;
$m2 = $this->m2;
$m3 = $this->m3;
$q0 = $this->q0;
$q1 = $this->q1;
$K = $S0 = $S1 = $S2 = $S3 = array();
switch (strlen($this->key)) {
case 16:
list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[1],
$le_longs[2]);
list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[3],
$le_longs[4]);
for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) {
$A = $m0[$q0[$q0[$i] ^ $key[ 9]] ^ $key[1]] ^
$m1[$q0[$q1[$i] ^ $key[10]] ^ $key[2]] ^
$m2[$q1[$q0[$i] ^ $key[11]] ^ $key[3]] ^
$m3[$q1[$q1[$i] ^ $key[12]] ^ $key[4]];
$B = $m0[$q0[$q0[$j] ^ $key[13]] ^ $key[5]] ^
$m1[$q0[$q1[$j] ^ $key[14]] ^ $key[6]] ^
$m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^
$m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff);
$A = $this->safe_intval($A + $B);
$K[] = $A;
$A = $this->safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
}
for ($i = 0; $i < 256; ++$i) {
$S0[$i] = $m0[$q0[$q0[$i] ^ $s4] ^ $s0];
$S1[$i] = $m1[$q0[$q1[$i] ^ $s5] ^ $s1];
$S2[$i] = $m2[$q1[$q0[$i] ^ $s6] ^ $s2];
$S3[$i] = $m3[$q1[$q1[$i] ^ $s7] ^ $s3];
}
break;
case 24:
list($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[1],
$le_longs[2]);
list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[3],
$le_longs[4]);
list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[5],
$le_longs[6]);
for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) {
$A = $m0[$q0[$q0[$q1[$i] ^ $key[17]] ^ $key[ 9]] ^
$key[1]] ^
$m1[$q0[$q1[$q1[$i] ^ $key[18]] ^ $key[10]] ^
$key[2]] ^
$m2[$q1[$q0[$q0[$i] ^ $key[19]] ^ $key[11]] ^
$key[3]] ^
$m3[$q1[$q1[$q0[$i] ^ $key[20]] ^ $key[12]] ^
$key[4]];
$B = $m0[$q0[$q0[$q1[$j] ^ $key[21]] ^ $key[13]] ^
$key[5]] ^
$m1[$q0[$q1[$q1[$j] ^ $key[22]] ^ $key[14]] ^
$key[6]] ^
$m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^
$key[7]] ^
$m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^
$key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff);
$A = $this->safe_intval($A + $B);
$K[] = $A;
$A = $this->safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
}
for ($i = 0; $i < 256; ++$i) {
$S0[$i] = $m0[$q0[$q0[$q1[$i] ^ $s8] ^ $s4] ^ $s0];
$S1[$i] = $m1[$q0[$q1[$q1[$i] ^ $s9] ^ $s5] ^ $s1];
$S2[$i] = $m2[$q1[$q0[$q0[$i] ^ $sa] ^ $s6] ^ $s2];
$S3[$i] = $m3[$q1[$q1[$q0[$i] ^ $sb] ^ $s7] ^ $s3];
}
break;
default: // 32
list($sf, $se, $sd, $sc) = $this->_mdsrem($le_longs[1],
$le_longs[2]);
list($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[3],
$le_longs[4]);
list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[5],
$le_longs[6]);
list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[7],
$le_longs[8]);
for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) {
$A = $m0[$q0[$q0[$q1[$q1[$i] ^ $key[25]] ^ $key[17]] ^
$key[ 9]] ^ $key[1]] ^
$m1[$q0[$q1[$q1[$q0[$i] ^ $key[26]] ^ $key[18]] ^
$key[10]] ^ $key[2]] ^
$m2[$q1[$q0[$q0[$q0[$i] ^ $key[27]] ^ $key[19]] ^
$key[11]] ^ $key[3]] ^
$m3[$q1[$q1[$q0[$q1[$i] ^ $key[28]] ^ $key[20]] ^
$key[12]] ^ $key[4]];
$B = $m0[$q0[$q0[$q1[$q1[$j] ^ $key[29]] ^ $key[21]] ^
$key[13]] ^ $key[5]] ^
$m1[$q0[$q1[$q1[$q0[$j] ^ $key[30]] ^ $key[22]] ^
$key[14]] ^ $key[6]] ^
$m2[$q1[$q0[$q0[$q0[$j] ^ $key[31]] ^ $key[23]] ^
$key[15]] ^ $key[7]] ^
$m3[$q1[$q1[$q0[$q1[$j] ^ $key[32]] ^ $key[24]] ^
$key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff);
$A = $this->safe_intval($A + $B);
$K[] = $A;
$A = $this->safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
}
for ($i = 0; $i < 256; ++$i) {
$S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4]
^ $s0];
$S1[$i] = $m1[$q0[$q1[$q1[$q0[$i] ^ $sd] ^ $s9] ^ $s5]
^ $s1];
$S2[$i] = $m2[$q1[$q0[$q0[$q0[$i] ^ $se] ^ $sa] ^ $s6]
^ $s2];
$S3[$i] = $m3[$q1[$q1[$q0[$q1[$i] ^ $sf] ^ $sb] ^ $s7]
^ $s3];
}
}
$this->K = $K;
$this->S0 = $S0;
$this->S1 = $S1;
$this->S2 = $S2;
$this->S3 = $S3;
}
/**
* _mdsrem function using by the twofish cipher algorithm
*
* @access private
* @param string $A
* @param string $B
* @return array
*/
function _mdsrem($A, $B)
{
// No gain by unrolling this loop.
for ($i = 0; $i < 8; ++$i) {
// Get most significant coefficient.
$t = 0xff & ($B >> 24);
// Shift the others up.
$B = ($B << 8) | (0xff & ($A >> 24));
$A<<= 8;
$u = $t << 1;
// Subtract the modular polynomial on overflow.
if ($t & 0x80) {
$u^= 0x14d;
}
// Remove t * (a * x^2 + 1).
$B ^= $t ^ ($u << 16);
// Form u = a*t + t/a = t*(a + 1/a).
$u^= 0x7fffffff & ($t >> 1);
// Add the modular polynomial on underflow.
if ($t & 0x01) {
$u^= 0xa6 ;
}
// Remove t * (a + 1/a) * (x^3 + x).
$B^= ($u << 24) | ($u << 8);
}
return array(
0xff & $B >> 24,
0xff & $B >> 16,
0xff & $B >> 8,
0xff & $B);
}
/**
* Encrypts a block
*
* @access private
* @param string $in
* @return string
*/
function _encryptBlock($in)
{
$S0 = $this->S0;
$S1 = $this->S1;
$S2 = $this->S2;
$S3 = $this->S3;
$K = $this->K;
$in = unpack("V4", $in);
$R0 = $K[0] ^ $in[1];
$R1 = $K[1] ^ $in[2];
$R2 = $K[2] ^ $in[3];
$R3 = $K[3] ^ $in[4];
$ki = 7;
while ($ki < 39) {
$t0 = $S0[ $R0 & 0xff] ^
$S1[($R0 >> 8) & 0xff] ^
$S2[($R0 >> 16) & 0xff] ^
$S3[($R0 >> 24) & 0xff];
$t1 = $S0[($R1 >> 24) & 0xff] ^
$S1[ $R1 & 0xff] ^
$S2[($R1 >> 8) & 0xff] ^
$S3[($R1 >> 16) & 0xff];
$R2^= $this->safe_intval($t0 + $t1 + $K[++$ki]);
$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
$R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^
$this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]);
$t0 = $S0[ $R2 & 0xff] ^
$S1[($R2 >> 8) & 0xff] ^
$S2[($R2 >> 16) & 0xff] ^
$S3[($R2 >> 24) & 0xff];
$t1 = $S0[($R3 >> 24) & 0xff] ^
$S1[ $R3 & 0xff] ^
$S2[($R3 >> 8) & 0xff] ^
$S3[($R3 >> 16) & 0xff];
$R0^= $this->safe_intval($t0 + $t1 + $K[++$ki]);
$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
$R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^
$this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]);
}
// @codingStandardsIgnoreStart
return pack("V4", $K[4] ^ $R2,
$K[5] ^ $R3,
$K[6] ^ $R0,
$K[7] ^ $R1);
// @codingStandardsIgnoreEnd
}
/**
* Decrypts a block
*
* @access private
* @param string $in
* @return string
*/
function _decryptBlock($in)
{
$S0 = $this->S0;
$S1 = $this->S1;
$S2 = $this->S2;
$S3 = $this->S3;
$K = $this->K;
$in = unpack("V4", $in);
$R0 = $K[4] ^ $in[1];
$R1 = $K[5] ^ $in[2];
$R2 = $K[6] ^ $in[3];
$R3 = $K[7] ^ $in[4];
$ki = 40;
while ($ki > 8) {
$t0 = $S0[$R0 & 0xff] ^
$S1[$R0 >> 8 & 0xff] ^
$S2[$R0 >> 16 & 0xff] ^
$S3[$R0 >> 24 & 0xff];
$t1 = $S0[$R1 >> 24 & 0xff] ^
$S1[$R1 & 0xff] ^
$S2[$R1 >> 8 & 0xff] ^
$S3[$R1 >> 16 & 0xff];
$R3^= $this->safe_intval($t0 + ($t1 << 1) +
$K[--$ki]);
$R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
$R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^
$this->safe_intval($t0 + $t1 + $K[--$ki]);
$t0 = $S0[$R2 & 0xff] ^
$S1[$R2 >> 8 & 0xff] ^
$S2[$R2 >> 16 & 0xff] ^
$S3[$R2 >> 24 & 0xff];
$t1 = $S0[$R3 >> 24 & 0xff] ^
$S1[$R3 & 0xff] ^
$S2[$R3 >> 8 & 0xff] ^
$S3[$R3 >> 16 & 0xff];
$R1^= $this->safe_intval($t0 + ($t1 << 1) +
$K[--$ki]);
$R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
$R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^
$this->safe_intval($t0 + $t1 + $K[--$ki]);
}
// @codingStandardsIgnoreStart
return pack("V4", $K[0] ^ $R2,
$K[1] ^ $R3,
$K[2] ^ $R0,
$K[3] ^ $R1);
// @codingStandardsIgnoreEnd
}
/**
* Setup the performance-optimized function for de/encrypt()
*
* @see \phpseclib\Crypt\Base::_setupInlineCrypt()
* @access private
*/
function _setupInlineCrypt()
{
$lambda_functions =& self::_getLambdaFunctions();
// Max. 10 Ultra-Hi-optimized inline-crypt functions. After that,
we'll (still) create very fast code, but not the ultimate fast one.
// (Currently, for Crypt_Twofish, one generated $lambda_function
cost on php5.5@32bit ~140kb unfreeable mem and ~240kb on php5.5@64bit)
$gen_hi_opt_code = (bool)(count($lambda_functions) < 10);
// Generation of a unique hash for our generated code
$code_hash = "Crypt_Twofish, {$this->mode}";
if ($gen_hi_opt_code) {
$code_hash = str_pad($code_hash, 32) .
$this->_hashInlineCryptFunction($this->key);
}
$safeint = $this->safe_intval_inline();
if (!isset($lambda_functions[$code_hash])) {
switch (true) {
case $gen_hi_opt_code:
$K = $this->K;
$init_crypt = '
static $S0, $S1, $S2, $S3;
if (!$S0) {
for ($i = 0; $i < 256; ++$i) {
$S0[] = (int)$self->S0[$i];
$S1[] = (int)$self->S1[$i];
$S2[] = (int)$self->S2[$i];
$S3[] = (int)$self->S3[$i];
}
}
';
break;
default:
$K = array();
for ($i = 0; $i < 40; ++$i) {
$K[] = '$K_' . $i;
}
$init_crypt = '
$S0 = $self->S0;
$S1 = $self->S1;
$S2 = $self->S2;
$S3 = $self->S3;
list(' . implode(',', $K) . ')
= $self->K;
';
}
// Generating encrypt code:
$encrypt_block = '
$in = unpack("V4", $in);
$R0 = '.$K[0].' ^ $in[1];
$R1 = '.$K[1].' ^ $in[2];
$R2 = '.$K[2].' ^ $in[3];
$R3 = '.$K[3].' ^ $in[4];
';
for ($ki = 7, $i = 0; $i < 8; ++$i) {
$encrypt_block.= '
$t0 = $S0[ $R0 & 0xff] ^
$S1[($R0 >> 8) & 0xff] ^
$S2[($R0 >> 16) & 0xff] ^
$S3[($R0 >> 24) & 0xff];
$t1 = $S0[($R1 >> 24) & 0xff] ^
$S1[ $R1 & 0xff] ^
$S2[($R1 >> 8) & 0xff] ^
$S3[($R1 >> 16) & 0xff];
$R2^= ' . sprintf($safeint, '$t0 + $t1 +
' . $K[++$ki]) . ';
$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 <<
31);
$R3 = ((($R3 >> 31) & 1) | ($R3 << 1))
^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' .
$K[++$ki] . ')') . ';
$t0 = $S0[ $R2 & 0xff] ^
$S1[($R2 >> 8) & 0xff] ^
$S2[($R2 >> 16) & 0xff] ^
$S3[($R2 >> 24) & 0xff];
$t1 = $S0[($R3 >> 24) & 0xff] ^
$S1[ $R3 & 0xff] ^
$S2[($R3 >> 8) & 0xff] ^
$S3[($R3 >> 16) & 0xff];
$R0^= ' . sprintf($safeint, '($t0 + $t1 +
' . $K[++$ki] . ')') . ';
$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 <<
31);
$R1 = ((($R1 >> 31) & 1) | ($R1 << 1))
^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' .
$K[++$ki] . ')') . ';
';
}
$encrypt_block.= '
$in = pack("V4", ' . $K[4] . ' ^ $R2,
' . $K[5] . ' ^ $R3,
' . $K[6] . ' ^ $R0,
' . $K[7] . ' ^ $R1);
';
// Generating decrypt code:
$decrypt_block = '
$in = unpack("V4", $in);
$R0 = '.$K[4].' ^ $in[1];
$R1 = '.$K[5].' ^ $in[2];
$R2 = '.$K[6].' ^ $in[3];
$R3 = '.$K[7].' ^ $in[4];
';
for ($ki = 40, $i = 0; $i < 8; ++$i) {
$decrypt_block.= '
$t0 = $S0[$R0 & 0xff] ^
$S1[$R0 >> 8 & 0xff] ^
$S2[$R0 >> 16 & 0xff] ^
$S3[$R0 >> 24 & 0xff];
$t1 = $S0[$R1 >> 24 & 0xff] ^
$S1[$R1 & 0xff] ^
$S2[$R1 >> 8 & 0xff] ^
$S3[$R1 >> 16 & 0xff];
$R3^= ' . sprintf($safeint, '$t0 + ($t1
<< 1) + ' . $K[--$ki]) . ';
$R3 = $R3 >> 1 & 0x7fffffff | $R3 <<
31;
$R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^
' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] .
')') . ';
$t0 = $S0[$R2 & 0xff] ^
$S1[$R2 >> 8 & 0xff] ^
$S2[$R2 >> 16 & 0xff] ^
$S3[$R2 >> 24 & 0xff];
$t1 = $S0[$R3 >> 24 & 0xff] ^
$S1[$R3 & 0xff] ^
$S2[$R3 >> 8 & 0xff] ^
$S3[$R3 >> 16 & 0xff];
$R1^= ' . sprintf($safeint, '$t0 + ($t1
<< 1) + ' . $K[--$ki]) . ';
$R1 = $R1 >> 1 & 0x7fffffff | $R1 <<
31;
$R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^
' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] .
')') . ';
';
}
$decrypt_block.= '
$in = pack("V4", ' . $K[0] . ' ^ $R2,
' . $K[1] . ' ^ $R3,
' . $K[2] . ' ^ $R0,
' . $K[3] . ' ^ $R1);
';
$lambda_functions[$code_hash] =
$this->_createInlineCryptFunction(
array(
'init_crypt' => $init_crypt,
'init_encrypt' => '',
'init_decrypt' => '',
'encrypt_block' => $encrypt_block,
'decrypt_block' => $decrypt_block
)
);
}
$this->inline_crypt = $lambda_functions[$code_hash];
}
}
phpseclib/phpseclib/File/ANSI.php000064400000047570151161207740012631
0ustar00<?php
/**
* Pure-PHP ANSI Decoder
*
* PHP version 5
*
* If you call read() in \phpseclib\Net\SSH2 you may get {@link
http://en.wikipedia.org/wiki/ANSI_escape_code ANSI escape codes} back.
* They'd look like chr(0x1B) . '[00m' or whatever (0x1B =
ESC). They tell a
* {@link http://en.wikipedia.org/wiki/Terminal_emulator terminal emulator}
how to format the characters, what
* color to display them in, etc. \phpseclib\File\ANSI is a {@link
http://en.wikipedia.org/wiki/VT100 VT100} terminal emulator.
*
* @category File
* @package ANSI
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2012 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\File;
/**
* Pure-PHP ANSI Decoder
*
* @package ANSI
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class ANSI
{
/**
* Max Width
*
* @var int
* @access private
*/
var $max_x;
/**
* Max Height
*
* @var int
* @access private
*/
var $max_y;
/**
* Max History
*
* @var int
* @access private
*/
var $max_history;
/**
* History
*
* @var array
* @access private
*/
var $history;
/**
* History Attributes
*
* @var array
* @access private
*/
var $history_attrs;
/**
* Current Column
*
* @var int
* @access private
*/
var $x;
/**
* Current Row
*
* @var int
* @access private
*/
var $y;
/**
* Old Column
*
* @var int
* @access private
*/
var $old_x;
/**
* Old Row
*
* @var int
* @access private
*/
var $old_y;
/**
* An empty attribute cell
*
* @var object
* @access private
*/
var $base_attr_cell;
/**
* The current attribute cell
*
* @var object
* @access private
*/
var $attr_cell;
/**
* An empty attribute row
*
* @var array
* @access private
*/
var $attr_row;
/**
* The current screen text
*
* @var array
* @access private
*/
var $screen;
/**
* The current screen attributes
*
* @var array
* @access private
*/
var $attrs;
/**
* Current ANSI code
*
* @var string
* @access private
*/
var $ansi;
/**
* Tokenization
*
* @var array
* @access private
*/
var $tokenization;
/**
* Default Constructor.
*
* @return \phpseclib\File\ANSI
* @access public
*/
function __construct()
{
$attr_cell = new \stdClass();
$attr_cell->bold = false;
$attr_cell->underline = false;
$attr_cell->blink = false;
$attr_cell->background = 'black';
$attr_cell->foreground = 'white';
$attr_cell->reverse = false;
$this->base_attr_cell = clone $attr_cell;
$this->attr_cell = clone $attr_cell;
$this->setHistory(200);
$this->setDimensions(80, 24);
}
/**
* Set terminal width and height
*
* Resets the screen as well
*
* @param int $x
* @param int $y
* @access public
*/
function setDimensions($x, $y)
{
$this->max_x = $x - 1;
$this->max_y = $y - 1;
$this->x = $this->y = 0;
$this->history = $this->history_attrs = array();
$this->attr_row = array_fill(0, $this->max_x + 2,
$this->base_attr_cell);
$this->screen = array_fill(0, $this->max_y + 1,
'');
$this->attrs = array_fill(0, $this->max_y + 1,
$this->attr_row);
$this->ansi = '';
}
/**
* Set the number of lines that should be logged past the terminal
height
*
* @param int $history
* @access public
*/
function setHistory($history)
{
$this->max_history = $history;
}
/**
* Load a string
*
* @param string $source
* @access public
*/
function loadString($source)
{
$this->setDimensions($this->max_x + 1, $this->max_y + 1);
$this->appendString($source);
}
/**
* Appdend a string
*
* @param string $source
* @access public
*/
function appendString($source)
{
$this->tokenization = array('');
for ($i = 0; $i < strlen($source); $i++) {
if (strlen($this->ansi)) {
$this->ansi.= $source[$i];
$chr = ord($source[$i]);
//
http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements
// single character CSI's not currently supported
switch (true) {
case $this->ansi == "\x1B=":
$this->ansi = '';
continue 2;
case strlen($this->ansi) == 2 && $chr >=
64 && $chr <= 95 && $chr != ord('['):
case strlen($this->ansi) > 2 && $chr
>= 64 && $chr <= 126:
break;
default:
continue 2;
}
$this->tokenization[] = $this->ansi;
$this->tokenization[] = '';
// http://ascii-table.com/ansi-escape-sequences-vt-100.php
switch ($this->ansi) {
case "\x1B[H": // Move cursor to upper left
corner
$this->old_x = $this->x;
$this->old_y = $this->y;
$this->x = $this->y = 0;
break;
case "\x1B[J": // Clear screen from cursor
down
$this->history = array_merge($this->history,
array_slice(array_splice($this->screen, $this->y + 1), 0,
$this->old_y));
$this->screen = array_merge($this->screen,
array_fill($this->y, $this->max_y, ''));
$this->history_attrs =
array_merge($this->history_attrs,
array_slice(array_splice($this->attrs, $this->y + 1), 0,
$this->old_y));
$this->attrs = array_merge($this->attrs,
array_fill($this->y, $this->max_y, $this->attr_row));
if (count($this->history) ==
$this->max_history) {
array_shift($this->history);
array_shift($this->history_attrs);
}
case "\x1B[K": // Clear screen from cursor
right
$this->screen[$this->y] =
substr($this->screen[$this->y], 0, $this->x);
array_splice($this->attrs[$this->y],
$this->x + 1, $this->max_x - $this->x, array_fill($this->x,
$this->max_x - ($this->x - 1), $this->base_attr_cell));
break;
case "\x1B[2K": // Clear entire line
$this->screen[$this->y] = str_repeat('
', $this->x);
$this->attrs[$this->y] = $this->attr_row;
break;
case "\x1B[?1h": // set cursor key to
application
case "\x1B[?25h": // show the cursor
case "\x1B(B": // set united states g0
character set
break;
case "\x1BE": // Move to next line
$this->_newLine();
$this->x = 0;
break;
default:
switch (true) {
case preg_match('#\x1B\[(\d+)B#',
$this->ansi, $match): // Move cursor down n lines
$this->old_y = $this->y;
$this->y+= $match[1];
break;
case
preg_match('#\x1B\[(\d+);(\d+)H#', $this->ansi, $match): //
Move cursor to screen location v,h
$this->old_x = $this->x;
$this->old_y = $this->y;
$this->x = $match[2] - 1;
$this->y = $match[1] - 1;
break;
case preg_match('#\x1B\[(\d+)C#',
$this->ansi, $match): // Move cursor right n lines
$this->old_x = $this->x;
$this->x+= $match[1];
break;
case preg_match('#\x1B\[(\d+)D#',
$this->ansi, $match): // Move cursor left n lines
$this->old_x = $this->x;
$this->x-= $match[1];
if ($this->x < 0) {
$this->x = 0;
}
break;
case
preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): //
Set top and bottom lines of a window
break;
case
preg_match('#\x1B\[(\d*(?:;\d*)*)m#', $this->ansi, $match): //
character attributes
$attr_cell = &$this->attr_cell;
$mods = explode(';', $match[1]);
foreach ($mods as $mod) {
switch ($mod) {
case '':
case '0': // Turn off
character attributes
$attr_cell = clone
$this->base_attr_cell;
break;
case '1': // Turn bold
mode on
$attr_cell->bold = true;
break;
case '4': // Turn
underline mode on
$attr_cell->underline =
true;
break;
case '5': // Turn
blinking mode on
$attr_cell->blink = true;
break;
case '7': // Turn reverse
video on
$attr_cell->reverse =
!$attr_cell->reverse;
$temp =
$attr_cell->background;
$attr_cell->background =
$attr_cell->foreground;
$attr_cell->foreground =
$temp;
break;
default: // set colors
//$front =
$attr_cell->reverse ? &$attr_cell->background :
&$attr_cell->foreground;
$front = &$attr_cell->{
$attr_cell->reverse ? 'background' : 'foreground' };
//$back =
$attr_cell->reverse ? &$attr_cell->foreground :
&$attr_cell->background;
$back = &$attr_cell->{
$attr_cell->reverse ? 'foreground' : 'background' };
switch ($mod) {
//
@codingStandardsIgnoreStart
case '30': $front
= 'black'; break;
case '31': $front
= 'red'; break;
case '32': $front
= 'green'; break;
case '33': $front
= 'yellow'; break;
case '34': $front
= 'blue'; break;
case '35': $front
= 'magenta'; break;
case '36': $front
= 'cyan'; break;
case '37': $front
= 'white'; break;
case '40': $back
= 'black'; break;
case '41': $back
= 'red'; break;
case '42': $back
= 'green'; break;
case '43': $back
= 'yellow'; break;
case '44': $back
= 'blue'; break;
case '45': $back
= 'magenta'; break;
case '46': $back
= 'cyan'; break;
case '47': $back
= 'white'; break;
//
@codingStandardsIgnoreEnd
default:
//user_error('Unsupported attribute: ' . $mod);
$this->ansi =
'';
break 2;
}
}
}
break;
default:
//user_error("{$this->ansi} is
unsupported\r\n");
}
}
$this->ansi = '';
continue;
}
$this->tokenization[count($this->tokenization) - 1].=
$source[$i];
switch ($source[$i]) {
case "\r":
$this->x = 0;
break;
case "\n":
$this->_newLine();
break;
case "\x08": // backspace
if ($this->x) {
$this->x--;
$this->attrs[$this->y][$this->x] = clone
$this->base_attr_cell;
$this->screen[$this->y] = substr_replace(
$this->screen[$this->y],
$source[$i],
$this->x,
1
);
}
break;
case "\x0F": // shift
break;
case "\x1B": // start ANSI escape code
$this->tokenization[count($this->tokenization) -
1] = substr($this->tokenization[count($this->tokenization) - 1], 0,
-1);
//if
(!strlen($this->tokenization[count($this->tokenization) - 1])) {
// array_pop($this->tokenization);
//}
$this->ansi.= "\x1B";
break;
default:
$this->attrs[$this->y][$this->x] = clone
$this->attr_cell;
if ($this->x >
strlen($this->screen[$this->y])) {
$this->screen[$this->y] = str_repeat('
', $this->x);
}
$this->screen[$this->y] = substr_replace(
$this->screen[$this->y],
$source[$i],
$this->x,
1
);
if ($this->x > $this->max_x) {
$this->x = 0;
$this->_newLine();
} else {
$this->x++;
}
}
}
}
/**
* Add a new line
*
* Also update the $this->screen and $this->history buffers
*
* @access private
*/
function _newLine()
{
//if ($this->y < $this->max_y) {
// $this->y++;
//}
while ($this->y >= $this->max_y) {
$this->history = array_merge($this->history,
array(array_shift($this->screen)));
$this->screen[] = '';
$this->history_attrs = array_merge($this->history_attrs,
array(array_shift($this->attrs)));
$this->attrs[] = $this->attr_row;
if (count($this->history) >= $this->max_history) {
array_shift($this->history);
array_shift($this->history_attrs);
}
$this->y--;
}
$this->y++;
}
/**
* Returns the current coordinate without preformating
*
* @access private
* @return string
*/
function _processCoordinate($last_attr, $cur_attr, $char)
{
$output = '';
if ($last_attr != $cur_attr) {
$close = $open = '';
if ($last_attr->foreground != $cur_attr->foreground) {
if ($cur_attr->foreground != 'white') {
$open.= '<span style="color: ' .
$cur_attr->foreground . '">';
}
if ($last_attr->foreground != 'white') {
$close = '</span>' . $close;
}
}
if ($last_attr->background != $cur_attr->background) {
if ($cur_attr->background != 'black') {
$open.= '<span style="background: ' .
$cur_attr->background . '">';
}
if ($last_attr->background != 'black') {
$close = '</span>' . $close;
}
}
if ($last_attr->bold != $cur_attr->bold) {
if ($cur_attr->bold) {
$open.= '<b>';
} else {
$close = '</b>' . $close;
}
}
if ($last_attr->underline != $cur_attr->underline) {
if ($cur_attr->underline) {
$open.= '<u>';
} else {
$close = '</u>' . $close;
}
}
if ($last_attr->blink != $cur_attr->blink) {
if ($cur_attr->blink) {
$open.= '<blink>';
} else {
$close = '</blink>' . $close;
}
}
$output.= $close . $open;
}
$output.= htmlspecialchars($char);
return $output;
}
/**
* Returns the current screen without preformating
*
* @access private
* @return string
*/
function _getScreen()
{
$output = '';
$last_attr = $this->base_attr_cell;
for ($i = 0; $i <= $this->max_y; $i++) {
for ($j = 0; $j <= $this->max_x; $j++) {
$cur_attr = $this->attrs[$i][$j];
$output.= $this->_processCoordinate($last_attr,
$cur_attr, isset($this->screen[$i][$j]) ? $this->screen[$i][$j] :
'');
$last_attr = $this->attrs[$i][$j];
}
$output.= "\r\n";
}
$output = substr($output, 0, -2);
// close any remaining open tags
$output.= $this->_processCoordinate($last_attr,
$this->base_attr_cell, '');
return rtrim($output);
}
/**
* Returns the current screen
*
* @access public
* @return string
*/
function getScreen()
{
return '<pre width="' . ($this->max_x + 1) .
'" style="color: white; background: black">' .
$this->_getScreen() . '</pre>';
}
/**
* Returns the current screen and the x previous lines
*
* @access public
* @return string
*/
function getHistory()
{
$scrollback = '';
$last_attr = $this->base_attr_cell;
for ($i = 0; $i < count($this->history); $i++) {
for ($j = 0; $j <= $this->max_x + 1; $j++) {
$cur_attr = $this->history_attrs[$i][$j];
$scrollback.= $this->_processCoordinate($last_attr,
$cur_attr, isset($this->history[$i][$j]) ? $this->history[$i][$j] :
'');
$last_attr = $this->history_attrs[$i][$j];
}
$scrollback.= "\r\n";
}
$base_attr_cell = $this->base_attr_cell;
$this->base_attr_cell = $last_attr;
$scrollback.= $this->_getScreen();
$this->base_attr_cell = $base_attr_cell;
return '<pre width="' . ($this->max_x + 1) .
'" style="color: white; background: black">' .
$scrollback . '</span></pre>';
}
}
phpseclib/phpseclib/File/ASN1/Element.php000064400000001546151161207740014163
0ustar00<?php
/**
* Pure-PHP ASN.1 Parser
*
* PHP version 5
*
* @category File
* @package ASN1
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2012 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\File\ASN1;
/**
* ASN.1 Element
*
* Bypass normal encoding rules in phpseclib\File\ASN1::encodeDER()
*
* @package ASN1
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class Element
{
/**
* Raw element value
*
* @var string
* @access private
*/
var $element;
/**
* Constructor
*
* @param string $encoded
* @return \phpseclib\File\ASN1\Element
* @access public
*/
function __construct($encoded)
{
$this->element = $encoded;
}
}
phpseclib/phpseclib/File/ASN1.php000064400000157126151161207740012600
0ustar00<?php
/**
* Pure-PHP ASN.1 Parser
*
* PHP version 5
*
* ASN.1 provides the semantics for data encoded using various schemes.
The most commonly
* utilized scheme is DER or the "Distinguished Encoding Rules".
PEM's are base64 encoded
* DER blobs.
*
* \phpseclib\File\ASN1 decodes and encodes DER formatted messages and
places them in a semantic context.
*
* Uses the 1988 ASN.1 syntax.
*
* @category File
* @package ASN1
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2012 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\File;
use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger;
use DateTime;
use DateTimeZone;
/**
* Pure-PHP ASN.1 Parser
*
* @package ASN1
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class ASN1
{
/**#@+
* Tag Classes
*
* @access private
* @link
http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=12
*/
const CLASS_UNIVERSAL = 0;
const CLASS_APPLICATION = 1;
const CLASS_CONTEXT_SPECIFIC = 2;
const CLASS_PRIVATE = 3;
/**#@-*/
/**#@+
* Tag Classes
*
* @access private
* @link http://www.obj-sys.com/asn1tutorial/node124.html
*/
const TYPE_BOOLEAN = 1;
const TYPE_INTEGER = 2;
const TYPE_BIT_STRING = 3;
const TYPE_OCTET_STRING = 4;
const TYPE_NULL = 5;
const TYPE_OBJECT_IDENTIFIER = 6;
//const TYPE_OBJECT_DESCRIPTOR = 7;
//const TYPE_INSTANCE_OF = 8; // EXTERNAL
const TYPE_REAL = 9;
const TYPE_ENUMERATED = 10;
//const TYPE_EMBEDDED = 11;
const TYPE_UTF8_STRING = 12;
//const TYPE_RELATIVE_OID = 13;
const TYPE_SEQUENCE = 16; // SEQUENCE OF
const TYPE_SET = 17; // SET OF
/**#@-*/
/**#@+
* More Tag Classes
*
* @access private
* @link http://www.obj-sys.com/asn1tutorial/node10.html
*/
const TYPE_NUMERIC_STRING = 18;
const TYPE_PRINTABLE_STRING = 19;
const TYPE_TELETEX_STRING = 20; // T61String
const TYPE_VIDEOTEX_STRING = 21;
const TYPE_IA5_STRING = 22;
const TYPE_UTC_TIME = 23;
const TYPE_GENERALIZED_TIME = 24;
const TYPE_GRAPHIC_STRING = 25;
const TYPE_VISIBLE_STRING = 26; // ISO646String
const TYPE_GENERAL_STRING = 27;
const TYPE_UNIVERSAL_STRING = 28;
//const TYPE_CHARACTER_STRING = 29;
const TYPE_BMP_STRING = 30;
/**#@-*/
/**#@+
* Tag Aliases
*
* These tags are kinda place holders for other tags.
*
* @access private
*/
const TYPE_CHOICE = -1;
const TYPE_ANY = -2;
/**#@-*/
/**
* ASN.1 object identifier
*
* @var array
* @access private
* @link http://en.wikipedia.org/wiki/Object_identifier
*/
var $oids = array();
/**
* Default date format
*
* @var string
* @access private
* @link http://php.net/class.datetime
*/
var $format = 'D, d M Y H:i:s O';
/**
* Default date format
*
* @var array
* @access private
* @see self::setTimeFormat()
* @see self::asn1map()
* @link http://php.net/class.datetime
*/
var $encoded;
/**
* Filters
*
* If the mapping type is self::TYPE_ANY what do we actually encode it
as?
*
* @var array
* @access private
* @see self::_encode_der()
*/
var $filters;
/**
* Type mapping table for the ANY type.
*
* Structured or unknown types are mapped to a
\phpseclib\File\ASN1\Element.
* Unambiguous types get the direct mapping (int/real/bool).
* Others are mapped as a choice, with an extra indexing level.
*
* @var array
* @access public
*/
var $ANYmap = array(
self::TYPE_BOOLEAN => true,
self::TYPE_INTEGER => true,
self::TYPE_BIT_STRING => 'bitString',
self::TYPE_OCTET_STRING => 'octetString',
self::TYPE_NULL => 'null',
self::TYPE_OBJECT_IDENTIFIER => 'objectIdentifier',
self::TYPE_REAL => true,
self::TYPE_ENUMERATED => 'enumerated',
self::TYPE_UTF8_STRING => 'utf8String',
self::TYPE_NUMERIC_STRING => 'numericString',
self::TYPE_PRINTABLE_STRING => 'printableString',
self::TYPE_TELETEX_STRING => 'teletexString',
self::TYPE_VIDEOTEX_STRING => 'videotexString',
self::TYPE_IA5_STRING => 'ia5String',
self::TYPE_UTC_TIME => 'utcTime',
self::TYPE_GENERALIZED_TIME => 'generalTime',
self::TYPE_GRAPHIC_STRING => 'graphicString',
self::TYPE_VISIBLE_STRING => 'visibleString',
self::TYPE_GENERAL_STRING => 'generalString',
self::TYPE_UNIVERSAL_STRING => 'universalString',
//self::TYPE_CHARACTER_STRING =>
'characterString',
self::TYPE_BMP_STRING => 'bmpString'
);
/**
* String type to character size mapping table.
*
* Non-convertable types are absent from this table.
* size == 0 indicates variable length encoding.
*
* @var array
* @access public
*/
var $stringTypeSize = array(
self::TYPE_UTF8_STRING => 0,
self::TYPE_BMP_STRING => 2,
self::TYPE_UNIVERSAL_STRING => 4,
self::TYPE_PRINTABLE_STRING => 1,
self::TYPE_TELETEX_STRING => 1,
self::TYPE_IA5_STRING => 1,
self::TYPE_VISIBLE_STRING => 1,
);
/**
* Parse BER-encoding
*
* Serves a similar purpose to openssl's asn1parse
*
* @param string $encoded
* @return array
* @access public
*/
function decodeBER($encoded)
{
if ($encoded instanceof Element) {
$encoded = $encoded->element;
}
$this->encoded = $encoded;
// encapsulate in an array for BC with the old decodeBER
return array($this->_decode_ber($encoded));
}
/**
* Parse BER-encoding (Helper function)
*
* Sometimes we want to get the BER encoding of a particular tag.
$start lets us do that without having to reencode.
* $encoded is passed by reference for the recursive calls done for
self::TYPE_BIT_STRING and
* self::TYPE_OCTET_STRING. In those cases, the indefinite length is
used.
*
* @param string $encoded
* @param int $start
* @param int $encoded_pos
* @return array
* @access private
*/
function _decode_ber($encoded, $start = 0, $encoded_pos = 0)
{
$current = array('start' => $start);
$type = ord($encoded[$encoded_pos++]);
$startOffset = 1;
$constructed = ($type >> 5) & 1;
$tag = $type & 0x1F;
if ($tag == 0x1F) {
$tag = 0;
// process septets (since the eighth bit is ignored, it's
not an octet)
do {
$temp = ord($encoded[$encoded_pos++]);
$startOffset++;
$loop = $temp >> 7;
$tag <<= 7;
$temp &= 0x7F;
// "bits 7 to 1 of the first subsequent octet shall
not all be zero"
if ($startOffset == 2 && $temp == 0) {
return false;
}
$tag |= $temp;
} while ($loop);
}
$start+= $startOffset;
// Length, as discussed in paragraph 8.1.3 of
X.690-0207.pdf#page=13
$length = ord($encoded[$encoded_pos++]);
$start++;
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
// immediately available." -- paragraph 8.1.3.2.c
$length = strlen($encoded) - $encoded_pos;
} 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
// support it up to four.
$length&= 0x7F;
$temp = substr($encoded, $encoded_pos, $length);
$encoded_pos += $length;
// tags of indefinte length don't really have a header
length; this length includes the tag
$current+= array('headerlength' => $length + 2);
$start+= $length;
extract(unpack('Nlength', substr(str_pad($temp, 4,
chr(0), STR_PAD_LEFT), -4)));
} else {
$current+= array('headerlength' => 2);
}
if ($length > (strlen($encoded) - $encoded_pos)) {
return false;
}
$content = substr($encoded, $encoded_pos, $length);
$content_pos = 0;
// 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
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
have a wide, scattered use within a particular presentation
context. PRIVATE distinguishes data types within
a particular organization or country. CONTEXT-SPECIFIC
distinguishes members of a sequence or set, the
alternatives of a CHOICE, or universally tagged set members.
Only the class number appears in braces for this
data type; the term CONTEXT-SPECIFIC does not appear.
-- http://www.obj-sys.com/asn1tutorial/node12.html */
$class = ($type >> 6) & 3;
switch ($class) {
case self::CLASS_APPLICATION:
case self::CLASS_PRIVATE:
case self::CLASS_CONTEXT_SPECIFIC:
if (!$constructed) {
return array(
'type' => $class,
'constant' => $tag,
'content' => $content,
'length' => $length + $start -
$current['start']
);
}
$newcontent = array();
$remainingLength = $length;
while ($remainingLength > 0) {
$temp = $this->_decode_ber($content, $start,
$content_pos);
if ($temp === false) {
break;
}
$length = $temp['length'];
// end-of-content octets - see paragraph 8.1.5
if (substr($content, $content_pos + $length, 2) ==
"\0\0") {
$length+= 2;
$start+= $length;
$newcontent[] = $temp;
break;
}
$start+= $length;
$remainingLength-= $length;
$newcontent[] = $temp;
$content_pos += $length;
}
return array(
'type' => $class,
'constant' => $tag,
// the array encapsulation is for BC with the old
format
'content' => $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+= array('type' => $tag);
// decode UNIVERSAL tags
switch ($tag) {
case self::TYPE_BOOLEAN:
// "The contents octets shall consist of a single
octet." -- paragraph 8.2.1
if ($constructed || strlen($content) != 1) {
return false;
}
$current['content'] = (bool)
ord($content[$content_pos]);
break;
case self::TYPE_INTEGER:
case self::TYPE_ENUMERATED:
if ($constructed) {
return false;
}
$current['content'] = new
BigInteger(substr($content, $content_pos), -256);
break;
case self::TYPE_REAL: // not currently supported
return false;
case self::TYPE_BIT_STRING:
// The initial octet shall encode, as an unsigned binary
integer with bit 1 as the least significant bit,
// the number of unused bits in the final subsequent octet.
The number shall be in the range zero to
// seven.
if (!$constructed) {
$current['content'] = substr($content,
$content_pos);
} else {
$temp = $this->_decode_ber($content, $start,
$content_pos);
if ($temp === false) {
return false;
}
$length-= (strlen($content) - $content_pos);
$last = count($temp) - 1;
for ($i = 0; $i < $last; $i++) {
// all subtags should be bit strings
if ($temp[$i]['type'] !=
self::TYPE_BIT_STRING) {
return false;
}
$current['content'].=
substr($temp[$i]['content'], 1);
}
// all subtags should be bit strings
if ($temp[$last]['type'] !=
self::TYPE_BIT_STRING) {
return false;
}
$current['content'] =
$temp[$last]['content'][0] . $current['content'] .
substr($temp[$i]['content'], 1);
}
break;
case self::TYPE_OCTET_STRING:
if (!$constructed) {
$current['content'] = substr($content,
$content_pos);
} else {
$current['content'] = '';
$length = 0;
while (substr($content, $content_pos, 2) !=
"\0\0") {
$temp = $this->_decode_ber($content, $length +
$start, $content_pos);
if ($temp === false) {
return false;
}
$content_pos += $temp['length'];
// all subtags should be octet strings
if ($temp['type'] !=
self::TYPE_OCTET_STRING) {
return false;
}
$current['content'].=
$temp['content'];
$length+= $temp['length'];
}
if (substr($content, $content_pos, 2) ==
"\0\0") {
$length+= 2; // +2 for the EOC
}
}
break;
case self::TYPE_NULL:
// "The contents octets shall not contain any
octets." -- paragraph 8.8.2
if ($constructed || strlen($content)) {
return false;
}
break;
case self::TYPE_SEQUENCE:
case self::TYPE_SET:
if (!$constructed) {
return false;
}
$offset = 0;
$current['content'] = array();
$content_len = strlen($content);
while ($content_pos < $content_len) {
// 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, $content_pos, 2) == "\0\0") {
$length = $offset + 2; // +2 for the EOC
break 2;
}
$temp = $this->_decode_ber($content, $start +
$offset, $content_pos);
if ($temp === false) {
return false;
}
$content_pos += $temp['length'];
$current['content'][] = $temp;
$offset+= $temp['length'];
}
break;
case self::TYPE_OBJECT_IDENTIFIER:
if ($constructed) {
return false;
}
$current['content'] =
$this->_decodeOID(substr($content, $content_pos));
if ($current['content'] === false) {
return false;
}
break;
/* Each character string type shall be encoded as if it had
been declared:
[UNIVERSAL x] IMPLICIT OCTET STRING
-- X.690-0207.pdf#page=23 (paragraph 8.21.3)
Per that, we're not going to do any validation. If
there are any illegal characters in the string,
we don't really care */
case self::TYPE_NUMERIC_STRING:
// 0,1,2,3,4,5,6,7,8,9, and space
case self::TYPE_PRINTABLE_STRING:
// Upper and lower case letters, digits, space, apostrophe,
left/right parenthesis, plus sign, comma,
// hyphen, full stop, solidus, colon, equal sign, question
mark
case self::TYPE_TELETEX_STRING:
// The Teletex character set in CCITT's T61, space,
and delete
// see http://en.wikipedia.org/wiki/Teletex#Character_sets
case self::TYPE_VIDEOTEX_STRING:
// The Videotex character set in CCITT's T.100 and
T.101, space, and delete
case self::TYPE_VISIBLE_STRING:
// Printing character sets of international ASCII, and
space
case self::TYPE_IA5_STRING:
// International Alphabet 5 (International ASCII)
case self::TYPE_GRAPHIC_STRING:
// All registered G sets, and space
case self::TYPE_GENERAL_STRING:
// All registered C and G sets, space and delete
case self::TYPE_UTF8_STRING:
// ????
case self::TYPE_BMP_STRING:
if ($constructed) {
return false;
}
$current['content'] = substr($content,
$content_pos);
break;
case self::TYPE_UTC_TIME:
case self::TYPE_GENERALIZED_TIME:
if ($constructed) {
return false;
}
$current['content'] =
$this->_decodeTime(substr($content, $content_pos), $tag);
break;
default:
return false;
}
$start+= $length;
// 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 Map
*
* 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 $mapping
* @param array $special
* @return array
* @access public
*/
function asn1map($decoded, $mapping, $special = array())
{
if (!is_array($decoded)) {
return false;
}
if (isset($mapping['explicit']) &&
is_array($decoded['content'])) {
$decoded = $decoded['content'][0];
}
switch (true) {
case $mapping['type'] == self::TYPE_ANY:
$intype = $decoded['type'];
if (isset($decoded['constant']) ||
!isset($this->ANYmap[$intype]) ||
(ord($this->encoded[$decoded['start']]) & 0x20)) {
return new Element(substr($this->encoded,
$decoded['start'], $decoded['length']));
}
$inmap = $this->ANYmap[$intype];
if (is_string($inmap)) {
return array($inmap => $this->asn1map($decoded,
array('type' => $intype) + $mapping, $special));
}
break;
case $mapping['type'] == self::TYPE_CHOICE:
foreach ($mapping['children'] as $key =>
$option) {
switch (true) {
case isset($option['constant'])
&& $option['constant'] == $decoded['constant']:
case !isset($option['constant'])
&& $option['type'] == $decoded['type']:
$value = $this->asn1map($decoded, $option,
$special);
break;
case !isset($option['constant'])
&& $option['type'] == self::TYPE_CHOICE:
$v = $this->asn1map($decoded, $option,
$special);
if (isset($v)) {
$value = $v;
}
}
if (isset($value)) {
if (isset($special[$key])) {
$value = call_user_func($special[$key],
$value);
}
return array($key => $value);
}
}
return null;
case isset($mapping['implicit']):
case isset($mapping['explicit']):
case $decoded['type'] == $mapping['type']:
break;
default:
// if $decoded['type'] and
$mapping['type'] are both strings, but different types of
strings,
// let it through
switch (true) {
case $decoded['type'] < 18: //
self::TYPE_NUMERIC_STRING == 18
case $decoded['type'] > 30: //
self::TYPE_BMP_STRING == 30
case $mapping['type'] < 18:
case $mapping['type'] > 30:
return null;
}
}
if (isset($mapping['implicit'])) {
$decoded['type'] = $mapping['type'];
}
switch ($decoded['type']) {
case self::TYPE_SEQUENCE:
$map = array();
// ignore the min and max
if (isset($mapping['min']) &&
isset($mapping['max'])) {
$child = $mapping['children'];
foreach ($decoded['content'] as $content) {
if (($map[] = $this->asn1map($content, $child,
$special)) === null) {
return null;
}
}
return $map;
}
$n = count($decoded['content']);
$i = 0;
foreach ($mapping['children'] as $key =>
$child) {
$maymatch = $i < $n; // Match only existing input.
if ($maymatch) {
$temp = $decoded['content'][$i];
if ($child['type'] != self::TYPE_CHOICE)
{
// Get the mapping and input class &
constant.
$childClass = $tempClass =
self::CLASS_UNIVERSAL;
$constant = null;
if (isset($temp['constant'])) {
$tempClass = $temp['type'];
}
if (isset($child['class'])) {
$childClass = $child['class'];
$constant = $child['cast'];
} elseif (isset($child['constant']))
{
$childClass = self::CLASS_CONTEXT_SPECIFIC;
$constant = $child['constant'];
}
if (isset($constant) &&
isset($temp['constant'])) {
// Can only match if constants and class
match.
$maymatch = $constant ==
$temp['constant'] && $childClass == $tempClass;
} else {
// Can only match if no constant expected
and type matches or is generic.
$maymatch =
!isset($child['constant']) &&
array_search($child['type'], array($temp['type'],
self::TYPE_ANY, self::TYPE_CHOICE)) !== false;
}
}
}
if ($maymatch) {
// Attempt submapping.
$candidate = $this->asn1map($temp, $child,
$special);
$maymatch = $candidate !== null;
}
if ($maymatch) {
// Got the match: use it.
if (isset($special[$key])) {
$candidate = call_user_func($special[$key],
$candidate);
}
$map[$key] = $candidate;
$i++;
} elseif (isset($child['default'])) {
$map[$key] = $child['default']; // Use
default.
} elseif (!isset($child['optional'])) {
return null; // Syntax error.
}
}
// Fail mapping if all input items have not been consumed.
return $i < $n ? null: $map;
// the main diff between sets and sequences is the
encapsulation of the foreach in another for loop
case self::TYPE_SET:
$map = array();
// ignore the min and max
if (isset($mapping['min']) &&
isset($mapping['max'])) {
$child = $mapping['children'];
foreach ($decoded['content'] as $content) {
if (($map[] = $this->asn1map($content, $child,
$special)) === null) {
return null;
}
}
return $map;
}
for ($i = 0; $i < count($decoded['content']);
$i++) {
$temp = $decoded['content'][$i];
$tempClass = self::CLASS_UNIVERSAL;
if (isset($temp['constant'])) {
$tempClass = $temp['type'];
}
foreach ($mapping['children'] as $key =>
$child) {
if (isset($map[$key])) {
continue;
}
$maymatch = true;
if ($child['type'] != self::TYPE_CHOICE)
{
$childClass = self::CLASS_UNIVERSAL;
$constant = null;
if (isset($child['class'])) {
$childClass = $child['class'];
$constant = $child['cast'];
} elseif (isset($child['constant']))
{
$childClass = self::CLASS_CONTEXT_SPECIFIC;
$constant = $child['constant'];
}
if (isset($constant) &&
isset($temp['constant'])) {
// Can only match if constants and class
match.
$maymatch = $constant ==
$temp['constant'] && $childClass == $tempClass;
} else {
// Can only match if no constant expected
and type matches or is generic.
$maymatch =
!isset($child['constant']) &&
array_search($child['type'], array($temp['type'],
self::TYPE_ANY, self::TYPE_CHOICE)) !== false;
}
}
if ($maymatch) {
// Attempt submapping.
$candidate = $this->asn1map($temp, $child,
$special);
$maymatch = $candidate !== null;
}
if (!$maymatch) {
break;
}
// Got the match: use it.
if (isset($special[$key])) {
$candidate = call_user_func($special[$key],
$candidate);
}
$map[$key] = $candidate;
break;
}
}
foreach ($mapping['children'] as $key =>
$child) {
if (!isset($map[$key])) {
if (isset($child['default'])) {
$map[$key] = $child['default'];
} elseif (!isset($child['optional'])) {
return null;
}
}
}
return $map;
case self::TYPE_OBJECT_IDENTIFIER:
return isset($this->oids[$decoded['content']])
? $this->oids[$decoded['content']] :
$decoded['content'];
case self::TYPE_UTC_TIME:
case self::TYPE_GENERALIZED_TIME:
// for explicitly tagged optional stuff
if (is_array($decoded['content'])) {
$decoded['content'] =
$decoded['content'][0]['content'];
}
// for implicitly tagged optional stuff
// in theory, doing isset($mapping['implicit'])
would work but malformed certs do exist
// in the wild that OpenSSL decodes without issue so
we'll support them as well
if (!is_object($decoded['content'])) {
$decoded['content'] =
$this->_decodeTime($decoded['content'],
$decoded['type']);
}
return $decoded['content'] ?
$decoded['content']->format($this->format) : false;
case self::TYPE_BIT_STRING:
if (isset($mapping['mapping'])) {
$offset = ord($decoded['content'][0]);
$size = (strlen($decoded['content']) - 1) * 8
- $offset;
/*
From X.680-0207.pdf#page=46 (21.7):
"When a "NamedBitList" is used in
defining a bitstring type ASN.1 encoding rules are free to add (or remove)
arbitrarily any trailing 0 bits to (or from) values
that are being encoded or decoded. Application designers should
therefore ensure that different semantics are not
associated with such values which differ only in the number of trailing
0 bits."
*/
$bits = count($mapping['mapping']) == $size ?
array() : array_fill(0, count($mapping['mapping']) - $size,
false);
for ($i = strlen($decoded['content']) - 1; $i
> 0; $i--) {
$current = ord($decoded['content'][$i]);
for ($j = $offset; $j < 8; $j++) {
$bits[] = (bool) ($current & (1 <<
$j));
}
$offset = 0;
}
$values = array();
$map = array_reverse($mapping['mapping']);
foreach ($map as $i => $value) {
if ($bits[$i]) {
$values[] = $value;
}
}
return $values;
}
case self::TYPE_OCTET_STRING:
return base64_encode($decoded['content']);
case self::TYPE_NULL:
return '';
case self::TYPE_BOOLEAN:
return $decoded['content'];
case self::TYPE_NUMERIC_STRING:
case self::TYPE_PRINTABLE_STRING:
case self::TYPE_TELETEX_STRING:
case self::TYPE_VIDEOTEX_STRING:
case self::TYPE_IA5_STRING:
case self::TYPE_GRAPHIC_STRING:
case self::TYPE_VISIBLE_STRING:
case self::TYPE_GENERAL_STRING:
case self::TYPE_UNIVERSAL_STRING:
case self::TYPE_UTF8_STRING:
case self::TYPE_BMP_STRING:
return $decoded['content'];
case self::TYPE_INTEGER:
case self::TYPE_ENUMERATED:
$temp = $decoded['content'];
if (isset($mapping['implicit'])) {
$temp = new BigInteger($decoded['content'],
-256);
}
if (isset($mapping['mapping'])) {
$temp = (int) $temp->toString();
return isset($mapping['mapping'][$temp]) ?
$mapping['mapping'][$temp] :
false;
}
return $temp;
}
}
/**
* ASN.1 Encode
*
* DER-encodes an ASN.1 semantic mapping ($mapping). Some libraries
would probably call this function
* an ASN.1 compiler.
*
* "Special" mappings can be applied via $special.
*
* @param string $source
* @param string $mapping
* @param array $special
* @return string
* @access public
*/
function encodeDER($source, $mapping, $special = array())
{
$this->location = array();
return $this->_encode_der($source, $mapping, null, $special);
}
/**
* ASN.1 Encode (Helper function)
*
* @param string $source
* @param string $mapping
* @param int $idx
* @param array $special
* @return string
* @access private
*/
function _encode_der($source, $mapping, $idx = null, $special =
array())
{
if ($source instanceof Element) {
return $source->element;
}
// do not encode (implicitly optional) fields with value set to
default
if (isset($mapping['default']) && $source ===
$mapping['default']) {
return '';
}
if (isset($idx)) {
if (isset($special[$idx])) {
$source = call_user_func($special[$idx], $source);
}
$this->location[] = $idx;
}
$tag = $mapping['type'];
switch ($tag) {
case self::TYPE_SET: // Children order is not important,
thus process in sequence.
case self::TYPE_SEQUENCE:
$tag|= 0x20; // set the constructed bit
// ignore the min and max
if (isset($mapping['min']) &&
isset($mapping['max'])) {
$value = array();
$child = $mapping['children'];
foreach ($source as $content) {
$temp = $this->_encode_der($content, $child,
null, $special);
if ($temp === false) {
return false;
}
$value[]= $temp;
}
/* "The encodings of the component values of a
set-of value shall appear in ascending order, the encodings being compared
as octet strings with the shorter components being
padded at their trailing end with 0-octets.
NOTE - The padding octets are for comparison
purposes only and do not appear in the encodings."
-- sec 11.6 of
http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf */
if ($mapping['type'] == self::TYPE_SET) {
sort($value);
}
$value = implode('', $value);
break;
}
$value = '';
foreach ($mapping['children'] as $key =>
$child) {
if (!array_key_exists($key, $source)) {
if (!isset($child['optional'])) {
return false;
}
continue;
}
$temp = $this->_encode_der($source[$key], $child,
$key, $special);
if ($temp === false) {
return false;
}
// An empty child encoding means it has been optimized
out.
// Else we should have at least one tag byte.
if ($temp === '') {
continue;
}
// if isset($child['constant']) is true then
isset($child['optional']) should be true as well
if (isset($child['constant'])) {
/*
From X.680-0207.pdf#page=58 (30.6):
"The tagging construction specifies
explicit tagging if any of the following holds:
...
c) the "Tag Type" alternative is used
and the value of "TagDefault" for the module is IMPLICIT TAGS or
AUTOMATIC TAGS, but the type defined by
"Type" is an untagged choice type, an untagged open type, or
an untagged "DummyReference" (see
ITU-T Rec. X.683 | ISO/IEC 8824-4, 8.3)."
*/
if (isset($child['explicit']) ||
$child['type'] == self::TYPE_CHOICE) {
$subtag = chr((self::CLASS_CONTEXT_SPECIFIC
<< 6) | 0x20 | $child['constant']);
$temp = $subtag .
$this->_encodeLength(strlen($temp)) . $temp;
} else {
$subtag = chr((self::CLASS_CONTEXT_SPECIFIC
<< 6) | (ord($temp[0]) & 0x20) | $child['constant']);
$temp = $subtag . substr($temp, 1);
}
}
$value.= $temp;
}
break;
case self::TYPE_CHOICE:
$temp = false;
foreach ($mapping['children'] as $key =>
$child) {
if (!isset($source[$key])) {
continue;
}
$temp = $this->_encode_der($source[$key], $child,
$key, $special);
if ($temp === false) {
return false;
}
// An empty child encoding means it has been optimized
out.
// Else we should have at least one tag byte.
if ($temp === '') {
continue;
}
$tag = ord($temp[0]);
// if isset($child['constant']) is true then
isset($child['optional']) should be true as well
if (isset($child['constant'])) {
if (isset($child['explicit']) ||
$child['type'] == self::TYPE_CHOICE) {
$subtag = chr((self::CLASS_CONTEXT_SPECIFIC
<< 6) | 0x20 | $child['constant']);
$temp = $subtag .
$this->_encodeLength(strlen($temp)) . $temp;
} else {
$subtag = chr((self::CLASS_CONTEXT_SPECIFIC
<< 6) | (ord($temp[0]) & 0x20) | $child['constant']);
$temp = $subtag . substr($temp, 1);
}
}
}
if (isset($idx)) {
array_pop($this->location);
}
if ($temp && isset($mapping['cast'])) {
$temp[0] = chr(($mapping['class'] << 6)
| ($tag & 0x20) | $mapping['cast']);
}
return $temp;
case self::TYPE_INTEGER:
case self::TYPE_ENUMERATED:
if (!isset($mapping['mapping'])) {
if (is_numeric($source)) {
$source = new BigInteger($source);
}
$value = $source->toBytes(true);
} else {
$value = array_search($source,
$mapping['mapping']);
if ($value === false) {
return false;
}
$value = new BigInteger($value);
$value = $value->toBytes(true);
}
if (!strlen($value)) {
$value = chr(0);
}
break;
case self::TYPE_UTC_TIME:
case self::TYPE_GENERALIZED_TIME:
$format = $mapping['type'] == self::TYPE_UTC_TIME
? 'y' : 'Y';
$format.= 'mdHis';
// if $source does _not_ include timezone information
within it then assume that the timezone is GMT
$date = new DateTime($source, new
DateTimeZone('GMT'));
// if $source _does_ include timezone information within it
then convert the time to GMT
$date->setTimezone(new DateTimeZone('GMT'));
$value = $date->format($format) . 'Z';
break;
case self::TYPE_BIT_STRING:
if (isset($mapping['mapping'])) {
$bits = array_fill(0,
count($mapping['mapping']), 0);
$size = 0;
for ($i = 0; $i <
count($mapping['mapping']); $i++) {
if (in_array($mapping['mapping'][$i],
$source)) {
$bits[$i] = 1;
$size = $i;
}
}
if (isset($mapping['min']) &&
$mapping['min'] >= 1 && $size <
$mapping['min']) {
$size = $mapping['min'] - 1;
}
$offset = 8 - (($size + 1) & 7);
$offset = $offset !== 8 ? $offset : 0;
$value = chr($offset);
for ($i = $size + 1; $i <
count($mapping['mapping']); $i++) {
unset($bits[$i]);
}
$bits = implode('', array_pad($bits, $size +
$offset + 1, 0));
$bytes = explode(' ',
rtrim(chunk_split($bits, 8, ' ')));
foreach ($bytes as $byte) {
$value.= chr(bindec($byte));
}
break;
}
case self::TYPE_OCTET_STRING:
/* The initial octet shall encode, as an unsigned binary
integer with bit 1 as the least significant bit,
the number of unused bits in the final subsequent octet.
The number shall be in the range zero to seven.
--
http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=16
*/
$value = base64_decode($source);
break;
case self::TYPE_OBJECT_IDENTIFIER:
$value = $this->_encodeOID($source);
break;
case self::TYPE_ANY:
$loc = $this->location;
if (isset($idx)) {
array_pop($this->location);
}
switch (true) {
case !isset($source):
return $this->_encode_der(null,
array('type' => self::TYPE_NULL) + $mapping, null, $special);
case is_int($source):
case $source instanceof BigInteger:
return $this->_encode_der($source,
array('type' => self::TYPE_INTEGER) + $mapping, null,
$special);
case is_float($source):
return $this->_encode_der($source,
array('type' => self::TYPE_REAL) + $mapping, null, $special);
case is_bool($source):
return $this->_encode_der($source,
array('type' => self::TYPE_BOOLEAN) + $mapping, null,
$special);
case is_array($source) && count($source) == 1:
$typename = implode('',
array_keys($source));
$outtype = array_search($typename,
$this->ANYmap, true);
if ($outtype !== false) {
return
$this->_encode_der($source[$typename], array('type' =>
$outtype) + $mapping, null, $special);
}
}
$filters = $this->filters;
foreach ($loc as $part) {
if (!isset($filters[$part])) {
$filters = false;
break;
}
$filters = $filters[$part];
}
if ($filters === false) {
user_error('No filters defined for ' .
implode('/', $loc));
return false;
}
return $this->_encode_der($source, $filters + $mapping,
null, $special);
case self::TYPE_NULL:
$value = '';
break;
case self::TYPE_NUMERIC_STRING:
case self::TYPE_TELETEX_STRING:
case self::TYPE_PRINTABLE_STRING:
case self::TYPE_UNIVERSAL_STRING:
case self::TYPE_UTF8_STRING:
case self::TYPE_BMP_STRING:
case self::TYPE_IA5_STRING:
case self::TYPE_VISIBLE_STRING:
case self::TYPE_VIDEOTEX_STRING:
case self::TYPE_GRAPHIC_STRING:
case self::TYPE_GENERAL_STRING:
$value = $source;
break;
case self::TYPE_BOOLEAN:
$value = $source ? "\xFF" : "\x00";
break;
default:
user_error('Mapping provides no type definition for
' . implode('/', $this->location));
return false;
}
if (isset($idx)) {
array_pop($this->location);
}
if (isset($mapping['cast'])) {
if (isset($mapping['explicit']) ||
$mapping['type'] == self::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;
}
/**
* DER-encode the length
*
* DER supports lengths up to (2**8)**127, however, we'll only
support lengths up to (2**8)**4. See
* {@link
http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690
paragraph 8.1.3} for more information.
*
* @access private
* @param int $length
* @return string
*/
function _encodeLength($length)
{
if ($length <= 0x7F) {
return chr($length);
}
$temp = ltrim(pack('N', $length), chr(0));
return pack('Ca*', 0x80 | strlen($temp), $temp);
}
/**
* BER-decode the OID
*
* Called by _decode_ber()
*
* @access private
* @param string $content
* @return string
*/
function _decodeOID($content)
{
static $eighty;
if (!$eighty) {
$eighty = new BigInteger(80);
}
$oid = array();
$pos = 0;
$len = strlen($content);
if (ord($content[$len - 1]) & 0x80) {
return false;
}
$n = new BigInteger();
while ($pos < $len) {
$temp = ord($content[$pos++]);
$n = $n->bitwise_leftShift(7);
$n = $n->bitwise_or(new BigInteger($temp & 0x7F));
if (~$temp & 0x80) {
$oid[] = $n;
$n = new BigInteger();
}
}
$part1 = array_shift($oid);
$first = floor(ord($content[0]) / 40);
/*
"This packing of the first two object identifier components
recognizes that only three values are allocated from the root
node, and at most 39 subsequent values from nodes reached by X =
0 and X = 1."
--
https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=22
*/
if ($first <= 2) { // ie. 0 <= ord($content[0]) < 120
(0x78)
array_unshift($oid, ord($content[0]) % 40);
array_unshift($oid, $first);
} else {
array_unshift($oid, $part1->subtract($eighty));
array_unshift($oid, 2);
}
return implode('.', $oid);
}
/**
* DER-encode the OID
*
* Called by _encode_der()
*
* @access private
* @param string $source
* @return string
*/
function _encodeOID($source)
{
static $mask, $zero, $forty;
if (!$mask) {
$mask = new BigInteger(0x7F);
$zero = new BigInteger();
$forty = new BigInteger(40);
}
$oid = preg_match('#(?:\d+\.)+#', $source) ? $source :
array_search($source, $this->oids);
if ($oid === false) {
user_error('Invalid OID');
return false;
}
$parts = explode('.', $oid);
$part1 = array_shift($parts);
$part2 = array_shift($parts);
$first = new BigInteger($part1);
$first = $first->multiply($forty);
$first = $first->add(new BigInteger($part2));
array_unshift($parts, $first->toString());
$value = '';
foreach ($parts as $part) {
if (!$part) {
$temp = "\0";
} else {
$temp = '';
$part = new BigInteger($part);
while (!$part->equals($zero)) {
$submask = $part->bitwise_and($mask);
$submask->setPrecision(8);
$temp = (chr(0x80) | $submask->toBytes()) . $temp;
$part = $part->bitwise_rightShift(7);
}
$temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] &
chr(0x7F);
}
$value.= $temp;
}
return $value;
}
/**
* BER-decode the time
*
* Called by _decode_ber() and in the case of implicit tags asn1map().
*
* @access private
* @param string $content
* @param int $tag
* @return string
*/
function _decodeTime($content, $tag)
{
/* UTCTime:
http://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
http://www.obj-sys.com/asn1tutorial/node15.html
GeneralizedTime:
http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2
http://www.obj-sys.com/asn1tutorial/node14.html */
$format = 'YmdHis';
if ($tag == self::TYPE_UTC_TIME) {
//
https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=28
says "the seconds
// element shall always be present" but none-the-less
I've seen X509 certs where it isn't and if the
// browsers parse it phpseclib ought to too
if (preg_match('#^(\d{10})(Z|[+-]\d{4})$#', $content,
$matches)) {
$content = $matches[1] . '00' . $matches[2];
}
$prefix = substr($content, 0, 2) >= 50 ? '19' :
'20';
$content = $prefix . $content;
} elseif (strpos($content, '.') !== false) {
$format.= '.u';
}
if ($content[strlen($content) - 1] == 'Z') {
$content = substr($content, 0, -1) . '+0000';
}
if (strpos($content, '-') !== false || strpos($content,
'+') !== false) {
$format.= 'O';
}
// error supression isn't necessary as of PHP 7.0:
// http://php.net/manual/en/migration70.other-changes.php
return @DateTime::createFromFormat($format, $content);
}
/**
* Set the time format
*
* Sets the time / date format for asn1map().
*
* @access public
* @param string $format
*/
function setTimeFormat($format)
{
$this->format = $format;
}
/**
* Load OIDs
*
* Load the relevant OIDs for a particular ASN.1 semantic mapping.
*
* @access public
* @param array $oids
*/
function loadOIDs($oids)
{
$this->oids = $oids;
}
/**
* Load filters
*
* See \phpseclib\File\X509, etc, for an example.
*
* @access public
* @param array $filters
*/
function loadFilters($filters)
{
$this->filters = $filters;
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @return string
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
/**
* String type conversion
*
* This is a lazy conversion, dealing only with character size.
* No real conversion table is used.
*
* @param string $in
* @param int $from
* @param int $to
* @return string
* @access public
*/
function convert($in, $from = self::TYPE_UTF8_STRING, $to =
self::TYPE_UTF8_STRING)
{
if (!isset($this->stringTypeSize[$from]) ||
!isset($this->stringTypeSize[$to])) {
return false;
}
$insize = $this->stringTypeSize[$from];
$outsize = $this->stringTypeSize[$to];
$inlength = strlen($in);
$out = '';
for ($i = 0; $i < $inlength;) {
if ($inlength - $i < $insize) {
return false;
}
// Get an input character as a 32-bit value.
$c = ord($in[$i++]);
switch (true) {
case $insize == 4:
$c = ($c << 8) | ord($in[$i++]);
$c = ($c << 8) | ord($in[$i++]);
case $insize == 2:
$c = ($c << 8) | ord($in[$i++]);
case $insize == 1:
break;
case ($c & 0x80) == 0x00:
break;
case ($c & 0x40) == 0x00:
return false;
default:
$bit = 6;
do {
if ($bit > 25 || $i >= $inlength ||
(ord($in[$i]) & 0xC0) != 0x80) {
return false;
}
$c = ($c << 6) | (ord($in[$i++]) & 0x3F);
$bit += 5;
$mask = 1 << $bit;
} while ($c & $bit);
$c &= $mask - 1;
break;
}
// Convert and append the character to output string.
$v = '';
switch (true) {
case $outsize == 4:
$v .= chr($c & 0xFF);
$c >>= 8;
$v .= chr($c & 0xFF);
$c >>= 8;
case $outsize == 2:
$v .= chr($c & 0xFF);
$c >>= 8;
case $outsize == 1:
$v .= chr($c & 0xFF);
$c >>= 8;
if ($c) {
return false;
}
break;
case ($c & 0x80000000) != 0:
return false;
case $c >= 0x04000000:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x04000000;
case $c >= 0x00200000:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x00200000;
case $c >= 0x00010000:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x00010000;
case $c >= 0x00000800:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x00000800;
case $c >= 0x00000080:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x000000C0;
default:
$v .= chr($c);
break;
}
$out .= strrev($v);
}
return $out;
}
}
phpseclib/phpseclib/File/X509.php000064400000557151151161207740012545
0ustar00<?php
/**
* Pure-PHP X.509 Parser
*
* PHP version 5
*
* Encode and decode X.509 certificates.
*
* The extensions are from {@link http://tools.ietf.org/html/rfc5280
RFC5280} and
* {@link
http://web.archive.org/web/19961027104704/http://www3.netscape.com/eng/security/cert-exts.html
Netscape Certificate Extensions}.
*
* Note that loading an X.509 certificate and resaving it may invalidate
the signature. The reason being that the signature is based on a
* portion of the certificate that contains optional parameters with
default values. ie. if the parameter isn't there the default value is
* used. Problem is, if the parameter is there and it just so happens to
have the default value there are two ways that that parameter can
* be encoded. It can be encoded explicitly or left out all together.
This would effect the signature value and thus may invalidate the
* the certificate all together unless the certificate is re-signed.
*
* @category File
* @package X509
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2012 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\File;
use phpseclib\Crypt\Hash;
use phpseclib\Crypt\Random;
use phpseclib\Crypt\RSA;
use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger;
use DateTime;
use DateTimeZone;
/**
* Pure-PHP X.509 Parser
*
* @package X509
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class X509
{
/**
* Flag to only accept signatures signed by certificate authorities
*
* Not really used anymore but retained all the same to suppress
E_NOTICEs from old installs
*
* @access public
*/
const VALIDATE_SIGNATURE_BY_CA = 1;
/**#@+
* @access public
* @see \phpseclib\File\X509::getDN()
*/
/**
* Return internal array representation
*/
const DN_ARRAY = 0;
/**
* Return string
*/
const DN_STRING = 1;
/**
* Return ASN.1 name string
*/
const DN_ASN1 = 2;
/**
* Return OpenSSL compatible array
*/
const DN_OPENSSL = 3;
/**
* Return canonical ASN.1 RDNs string
*/
const DN_CANON = 4;
/**
* Return name hash for file indexing
*/
const DN_HASH = 5;
/**#@-*/
/**#@+
* @access public
* @see \phpseclib\File\X509::saveX509()
* @see \phpseclib\File\X509::saveCSR()
* @see \phpseclib\File\X509::saveCRL()
*/
/**
* Save as PEM
*
* ie. a base64-encoded PEM with a header and a footer
*/
const FORMAT_PEM = 0;
/**
* Save as DER
*/
const FORMAT_DER = 1;
/**
* Save as a SPKAC
*
* Only works on CSRs. Not currently supported.
*/
const FORMAT_SPKAC = 2;
/**
* Auto-detect the format
*
* Used only by the load*() functions
*/
const FORMAT_AUTO_DETECT = 3;
/**#@-*/
/**
* Attribute value disposition.
* If disposition is >= 0, this is the index of the target value.
*/
const ATTR_ALL = -1; // All attribute values (array).
const ATTR_APPEND = -2; // Add a value.
const ATTR_REPLACE = -3; // Clear first, then add a value.
/**
* ASN.1 syntax for X.509 certificates
*
* @var array
* @access private
*/
var $Certificate;
/**#@+
* ASN.1 syntax for various extensions
*
* @access private
*/
var $DirectoryString;
var $PKCS9String;
var $AttributeValue;
var $Extensions;
var $KeyUsage;
var $ExtKeyUsageSyntax;
var $BasicConstraints;
var $KeyIdentifier;
var $CRLDistributionPoints;
var $AuthorityKeyIdentifier;
var $CertificatePolicies;
var $AuthorityInfoAccessSyntax;
var $SubjectAltName;
var $SubjectDirectoryAttributes;
var $PrivateKeyUsagePeriod;
var $IssuerAltName;
var $PolicyMappings;
var $NameConstraints;
var $CPSuri;
var $UserNotice;
var $netscape_cert_type;
var $netscape_comment;
var $netscape_ca_policy_url;
var $Name;
var $RelativeDistinguishedName;
var $CRLNumber;
var $CRLReason;
var $IssuingDistributionPoint;
var $InvalidityDate;
var $CertificateIssuer;
var $HoldInstructionCode;
var $SignedPublicKeyAndChallenge;
/**#@-*/
/**#@+
* ASN.1 syntax for various DN attributes
*
* @access private
*/
var $PostalAddress;
/**#@-*/
/**
* ASN.1 syntax for Certificate Signing Requests (RFC2986)
*
* @var array
* @access private
*/
var $CertificationRequest;
/**
* ASN.1 syntax for Certificate Revocation Lists (RFC5280)
*
* @var array
* @access private
*/
var $CertificateList;
/**
* Distinguished Name
*
* @var array
* @access private
*/
var $dn;
/**
* Public key
*
* @var string
* @access private
*/
var $publicKey;
/**
* Private key
*
* @var string
* @access private
*/
var $privateKey;
/**
* Object identifiers for X.509 certificates
*
* @var array
* @access private
* @link http://en.wikipedia.org/wiki/Object_identifier
*/
var $oids;
/**
* The certificate authorities
*
* @var array
* @access private
*/
var $CAs;
/**
* The currently loaded certificate
*
* @var array
* @access private
*/
var $currentCert;
/**
* The signature subject
*
* There's no guarantee \phpseclib\File\X509 is going to re-encode
an X.509 cert in the same way it was originally
* encoded so we take save the portion of the original cert that the
signature would have made for.
*
* @var string
* @access private
*/
var $signatureSubject;
/**
* Certificate Start Date
*
* @var string
* @access private
*/
var $startDate;
/**
* Certificate End Date
*
* @var string
* @access private
*/
var $endDate;
/**
* Serial Number
*
* @var string
* @access private
*/
var $serialNumber;
/**
* Key Identifier
*
* See {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.1
RFC5280#section-4.2.1.1} and
* {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.2
RFC5280#section-4.2.1.2}.
*
* @var string
* @access private
*/
var $currentKeyIdentifier;
/**
* CA Flag
*
* @var bool
* @access private
*/
var $caFlag = false;
/**
* SPKAC Challenge
*
* @var string
* @access private
*/
var $challenge;
/**
* Recursion Limit
*
* @var int
* @access private
*/
static $recur_limit = 5;
/**
* URL fetch flag
*
* @var bool
* @access private
*/
static $disable_url_fetch = false;
/**
* Default Constructor.
*
* @return \phpseclib\File\X509
* @access public
*/
function __construct()
{
// Explicitly Tagged Module, 1988 Syntax
// http://tools.ietf.org/html/rfc5280#appendix-A.1
$this->DirectoryString = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'teletexString' => array('type'
=> ASN1::TYPE_TELETEX_STRING),
'printableString' => array('type'
=> ASN1::TYPE_PRINTABLE_STRING),
'universalString' => array('type'
=> ASN1::TYPE_UNIVERSAL_STRING),
'utf8String' => array('type'
=> ASN1::TYPE_UTF8_STRING),
'bmpString' => array('type'
=> ASN1::TYPE_BMP_STRING)
)
);
$this->PKCS9String = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'ia5String' => array('type'
=> ASN1::TYPE_IA5_STRING),
'directoryString' => $this->DirectoryString
)
);
$this->AttributeValue = array('type' =>
ASN1::TYPE_ANY);
$AttributeType = array('type' =>
ASN1::TYPE_OBJECT_IDENTIFIER);
$AttributeTypeAndValue = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'type' => $AttributeType,
'value'=> $this->AttributeValue
)
);
/*
In practice, RDNs containing multiple name-value pairs (called
"multivalued RDNs") are rare,
but they can be useful at times when either there is no unique
attribute in the entry or you
want to ensure that the entry's DN contains some useful
identifying information.
-
https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName
*/
$this->RelativeDistinguishedName = array(
'type' => ASN1::TYPE_SET,
'min' => 1,
'max' => -1,
'children' => $AttributeTypeAndValue
);
// http://tools.ietf.org/html/rfc5280#section-4.1.2.4
$RDNSequence = array(
'type' => ASN1::TYPE_SEQUENCE,
// RDNSequence does not define a min or a max, which means it
doesn't have one
'min' => 0,
'max' => -1,
'children' => $this->RelativeDistinguishedName
);
$this->Name = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'rdnSequence' => $RDNSequence
)
);
// http://tools.ietf.org/html/rfc5280#section-4.1.1.2
$AlgorithmIdentifier = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'algorithm' => array('type' =>
ASN1::TYPE_OBJECT_IDENTIFIER),
'parameters' => array(
'type' =>
ASN1::TYPE_ANY,
'optional' => true
)
)
);
/*
A certificate using system MUST reject the certificate if it
encounters
a critical extension it does not recognize; however, a
non-critical
extension may be ignored if it is not recognized.
http://tools.ietf.org/html/rfc5280#section-4.2
*/
$Extension = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'extnId' => array('type' =>
ASN1::TYPE_OBJECT_IDENTIFIER),
'critical' => array(
'type' =>
ASN1::TYPE_BOOLEAN,
'optional' => true,
'default' => false
),
'extnValue' => array('type' =>
ASN1::TYPE_OCTET_STRING)
)
);
$this->Extensions = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
// technically, it's MAX, but we'll assume anything
< 0 is MAX
'max' => -1,
// if 'children' isn't an array then
'min' and 'max' must be defined
'children' => $Extension
);
$SubjectPublicKeyInfo = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'algorithm' => $AlgorithmIdentifier,
'subjectPublicKey' => array('type'
=> ASN1::TYPE_BIT_STRING)
)
);
$UniqueIdentifier = array('type' =>
ASN1::TYPE_BIT_STRING);
$Time = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'utcTime' => array('type' =>
ASN1::TYPE_UTC_TIME),
'generalTime' => array('type' =>
ASN1::TYPE_GENERALIZED_TIME)
)
);
// http://tools.ietf.org/html/rfc5280#section-4.1.2.5
$Validity = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'notBefore' => $Time,
'notAfter' => $Time
)
);
$CertificateSerialNumber = array('type' =>
ASN1::TYPE_INTEGER);
$Version = array(
'type' => ASN1::TYPE_INTEGER,
'mapping' => array('v1', 'v2',
'v3')
);
//
assert($TBSCertificate['children']['signature'] ==
$Certificate['children']['signatureAlgorithm'])
$TBSCertificate = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
// technically, default implies optional, but we'll
define it as being optional, none-the-less, just to
// reenforce that fact
'version' => array(
'constant' => 0,
'optional' =>
true,
'explicit' =>
true,
'default' =>
'v1'
) + $Version,
'serialNumber' =>
$CertificateSerialNumber,
'signature' =>
$AlgorithmIdentifier,
'issuer' => $this->Name,
'validity' => $Validity,
'subject' => $this->Name,
'subjectPublicKeyInfo' =>
$SubjectPublicKeyInfo,
// implicit means that the T in the TLV structure is to be
rewritten, regardless of the type
'issuerUniqueID' => array(
'constant' =>
1,
'optional' =>
true,
'implicit' =>
true
) + $UniqueIdentifier,
'subjectUniqueID' => array(
'constant' =>
2,
'optional' =>
true,
'implicit' =>
true
) + $UniqueIdentifier,
// <http://tools.ietf.org/html/rfc2459#page-74>
doesn't use the EXPLICIT keyword but if
// it's not IMPLICIT, it's EXPLICIT
'extensions' => array(
'constant' =>
3,
'optional' =>
true,
'explicit' =>
true
) + $this->Extensions
)
);
$this->Certificate = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'tbsCertificate' => $TBSCertificate,
'signatureAlgorithm' => $AlgorithmIdentifier,
'signature' =>
array('type' => ASN1::TYPE_BIT_STRING)
)
);
$this->KeyUsage = array(
'type' => ASN1::TYPE_BIT_STRING,
'mapping' => array(
'digitalSignature',
'nonRepudiation',
'keyEncipherment',
'dataEncipherment',
'keyAgreement',
'keyCertSign',
'cRLSign',
'encipherOnly',
'decipherOnly'
)
);
$this->BasicConstraints = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'cA' => array(
'type' =>
ASN1::TYPE_BOOLEAN,
'optional' =>
true,
'default' =>
false
),
'pathLenConstraint' => array(
'type' =>
ASN1::TYPE_INTEGER,
'optional' =>
true
)
)
);
$this->KeyIdentifier = array('type' =>
ASN1::TYPE_OCTET_STRING);
$OrganizationalUnitNames = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => 4, // ub-organizational-units
'children' => array('type' =>
ASN1::TYPE_PRINTABLE_STRING)
);
$PersonalName = array(
'type' => ASN1::TYPE_SET,
'children' => array(
'surname' => array(
'type' =>
ASN1::TYPE_PRINTABLE_STRING,
'constant' => 0,
'optional' => true,
'implicit' => true
),
'given-name' => array(
'type' =>
ASN1::TYPE_PRINTABLE_STRING,
'constant' => 1,
'optional' => true,
'implicit' => true
),
'initials' => array(
'type' =>
ASN1::TYPE_PRINTABLE_STRING,
'constant' => 2,
'optional' => true,
'implicit' => true
),
'generation-qualifier' => array(
'type' =>
ASN1::TYPE_PRINTABLE_STRING,
'constant' => 3,
'optional' => true,
'implicit' => true
)
)
);
$NumericUserIdentifier = array('type' =>
ASN1::TYPE_NUMERIC_STRING);
$OrganizationName = array('type' =>
ASN1::TYPE_PRINTABLE_STRING);
$PrivateDomainName = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'numeric' => array('type' =>
ASN1::TYPE_NUMERIC_STRING),
'printable' => array('type' =>
ASN1::TYPE_PRINTABLE_STRING)
)
);
$TerminalIdentifier = array('type' =>
ASN1::TYPE_PRINTABLE_STRING);
$NetworkAddress = array('type' =>
ASN1::TYPE_NUMERIC_STRING);
$AdministrationDomainName = array(
'type' => ASN1::TYPE_CHOICE,
// if class isn't present it's assumed to be
\phpseclib\File\ASN1::CLASS_UNIVERSAL or
// (if constant is present)
\phpseclib\File\ASN1::CLASS_CONTEXT_SPECIFIC
'class' => ASN1::CLASS_APPLICATION,
'cast' => 2,
'children' => array(
'numeric' => array('type' =>
ASN1::TYPE_NUMERIC_STRING),
'printable' => array('type' =>
ASN1::TYPE_PRINTABLE_STRING)
)
);
$CountryName = array(
'type' => ASN1::TYPE_CHOICE,
// if class isn't present it's assumed to be
\phpseclib\File\ASN1::CLASS_UNIVERSAL or
// (if constant is present)
\phpseclib\File\ASN1::CLASS_CONTEXT_SPECIFIC
'class' => ASN1::CLASS_APPLICATION,
'cast' => 1,
'children' => array(
'x121-dcc-code' =>
array('type' => ASN1::TYPE_NUMERIC_STRING),
'iso-3166-alpha2-code' =>
array('type' => ASN1::TYPE_PRINTABLE_STRING)
)
);
$AnotherName = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'type-id' => array('type' =>
ASN1::TYPE_OBJECT_IDENTIFIER),
'value' => array(
'type' => ASN1::TYPE_ANY,
'constant' => 0,
'optional' => true,
'explicit' => true
)
)
);
$ExtensionAttribute = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'extension-attribute-type' => array(
'type' =>
ASN1::TYPE_PRINTABLE_STRING,
'constant'
=> 0,
'optional'
=> true,
'implicit'
=> true
),
'extension-attribute-value' => array(
'type' =>
ASN1::TYPE_ANY,
'constant'
=> 1,
'optional'
=> true,
'explicit'
=> true
)
)
);
$ExtensionAttributes = array(
'type' => ASN1::TYPE_SET,
'min' => 1,
'max' => 256, // ub-extension-attributes
'children' => $ExtensionAttribute
);
$BuiltInDomainDefinedAttribute = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'type' => array('type' =>
ASN1::TYPE_PRINTABLE_STRING),
'value' => array('type' =>
ASN1::TYPE_PRINTABLE_STRING)
)
);
$BuiltInDomainDefinedAttributes = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => 4, // ub-domain-defined-attributes
'children' => $BuiltInDomainDefinedAttribute
);
$BuiltInStandardAttributes = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'country-name' =>
array('optional' => true) + $CountryName,
'administration-domain-name' =>
array('optional' => true) + $AdministrationDomainName,
'network-address' => array(
'constant' =>
0,
'optional' =>
true,
'implicit' =>
true
) + $NetworkAddress,
'terminal-identifier' => array(
'constant' =>
1,
'optional' =>
true,
'implicit' =>
true
) + $TerminalIdentifier,
'private-domain-name' => array(
'constant' =>
2,
'optional' =>
true,
'explicit' =>
true
) + $PrivateDomainName,
'organization-name' => array(
'constant' =>
3,
'optional' =>
true,
'implicit' =>
true
) + $OrganizationName,
'numeric-user-identifier' => array(
'constant' =>
4,
'optional' =>
true,
'implicit' =>
true
) + $NumericUserIdentifier,
'personal-name' => array(
'constant' =>
5,
'optional' =>
true,
'implicit' =>
true
) + $PersonalName,
'organizational-unit-names' => array(
'constant' =>
6,
'optional' =>
true,
'implicit' =>
true
) + $OrganizationalUnitNames
)
);
$ORAddress = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'built-in-standard-attributes' =>
$BuiltInStandardAttributes,
'built-in-domain-defined-attributes' =>
array('optional' => true) + $BuiltInDomainDefinedAttributes,
'extension-attributes' =>
array('optional' => true) + $ExtensionAttributes
)
);
$EDIPartyName = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'nameAssigner' => array(
'constant' => 0,
'optional' => true,
'implicit' => true
) + $this->DirectoryString,
// partyName is technically required but
\phpseclib\File\ASN1 doesn't currently support non-optional constants
and
// setting it to optional gets the job done in any event.
'partyName' => array(
'constant' => 1,
'optional' => true,
'implicit' => true
) + $this->DirectoryString
)
);
$GeneralName = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'otherName' => array(
'constant' =>
0,
'optional' =>
true,
'implicit' =>
true
) + $AnotherName,
'rfc822Name' => array(
'type' =>
ASN1::TYPE_IA5_STRING,
'constant' =>
1,
'optional' =>
true,
'implicit' =>
true
),
'dNSName' => array(
'type' =>
ASN1::TYPE_IA5_STRING,
'constant' =>
2,
'optional' =>
true,
'implicit' =>
true
),
'x400Address' => array(
'constant' =>
3,
'optional' =>
true,
'implicit' =>
true
) + $ORAddress,
'directoryName' => array(
'constant' =>
4,
'optional' =>
true,
'explicit' =>
true
) + $this->Name,
'ediPartyName' => array(
'constant' =>
5,
'optional' =>
true,
'implicit' =>
true
) + $EDIPartyName,
'uniformResourceIdentifier' => array(
'type' =>
ASN1::TYPE_IA5_STRING,
'constant' =>
6,
'optional' =>
true,
'implicit' =>
true
),
'iPAddress' => array(
'type' =>
ASN1::TYPE_OCTET_STRING,
'constant' =>
7,
'optional' =>
true,
'implicit' =>
true
),
'registeredID' => array(
'type' =>
ASN1::TYPE_OBJECT_IDENTIFIER,
'constant' =>
8,
'optional' =>
true,
'implicit' =>
true
)
)
);
$GeneralNames = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $GeneralName
);
$this->IssuerAltName = $GeneralNames;
$ReasonFlags = array(
'type' => ASN1::TYPE_BIT_STRING,
'mapping' => array(
'unused',
'keyCompromise',
'cACompromise',
'affiliationChanged',
'superseded',
'cessationOfOperation',
'certificateHold',
'privilegeWithdrawn',
'aACompromise'
)
);
$DistributionPointName = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'fullName' => array(
'constant' =>
0,
'optional' =>
true,
'implicit' =>
true
) + $GeneralNames,
'nameRelativeToCRLIssuer' => array(
'constant' =>
1,
'optional' =>
true,
'implicit' =>
true
) +
$this->RelativeDistinguishedName
)
);
$DistributionPoint = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'distributionPoint' => array(
'constant' =>
0,
'optional' =>
true,
'explicit' =>
true
) + $DistributionPointName,
'reasons' => array(
'constant' =>
1,
'optional' =>
true,
'implicit' =>
true
) + $ReasonFlags,
'cRLIssuer' => array(
'constant' =>
2,
'optional' =>
true,
'implicit' =>
true
) + $GeneralNames
)
);
$this->CRLDistributionPoints = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $DistributionPoint
);
$this->AuthorityKeyIdentifier = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'keyIdentifier' => array(
'constant' =>
0,
'optional' =>
true,
'implicit' =>
true
) + $this->KeyIdentifier,
'authorityCertIssuer' => array(
'constant' =>
1,
'optional' =>
true,
'implicit' =>
true
) + $GeneralNames,
'authorityCertSerialNumber' => array(
'constant' =>
2,
'optional' =>
true,
'implicit' =>
true
) + $CertificateSerialNumber
)
);
$PolicyQualifierId = array('type' =>
ASN1::TYPE_OBJECT_IDENTIFIER);
$PolicyQualifierInfo = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'policyQualifierId' => $PolicyQualifierId,
'qualifier' => array('type'
=> ASN1::TYPE_ANY)
)
);
$CertPolicyId = array('type' =>
ASN1::TYPE_OBJECT_IDENTIFIER);
$PolicyInformation = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'policyIdentifier' => $CertPolicyId,
'policyQualifiers' => array(
'type' =>
ASN1::TYPE_SEQUENCE,
'min' => 0,
'max' => -1,
'optional' => true,
'children' =>
$PolicyQualifierInfo
)
)
);
$this->CertificatePolicies = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $PolicyInformation
);
$this->PolicyMappings = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => array(
'type' =>
ASN1::TYPE_SEQUENCE,
'children' => array(
'issuerDomainPolicy' =>
$CertPolicyId,
'subjectDomainPolicy' =>
$CertPolicyId
)
)
);
$KeyPurposeId = array('type' =>
ASN1::TYPE_OBJECT_IDENTIFIER);
$this->ExtKeyUsageSyntax = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $KeyPurposeId
);
$AccessDescription = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'accessMethod' => array('type'
=> ASN1::TYPE_OBJECT_IDENTIFIER),
'accessLocation' => $GeneralName
)
);
$this->AuthorityInfoAccessSyntax = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $AccessDescription
);
$this->SubjectInfoAccessSyntax = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $AccessDescription
);
$this->SubjectAltName = $GeneralNames;
$this->PrivateKeyUsagePeriod = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'notBefore' => array(
'constant' =>
0,
'optional' =>
true,
'implicit' =>
true,
'type' =>
ASN1::TYPE_GENERALIZED_TIME),
'notAfter' => array(
'constant' =>
1,
'optional' =>
true,
'implicit' =>
true,
'type' =>
ASN1::TYPE_GENERALIZED_TIME)
)
);
$BaseDistance = array('type' => ASN1::TYPE_INTEGER);
$GeneralSubtree = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'base' => $GeneralName,
'minimum' => array(
'constant' => 0,
'optional' => true,
'implicit' => true,
'default' => new
BigInteger(0)
) + $BaseDistance,
'maximum' => array(
'constant' => 1,
'optional' => true,
'implicit' => true,
) + $BaseDistance
)
);
$GeneralSubtrees = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $GeneralSubtree
);
$this->NameConstraints = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'permittedSubtrees' => array(
'constant' => 0,
'optional' => true,
'implicit' => true
) + $GeneralSubtrees,
'excludedSubtrees' => array(
'constant' => 1,
'optional' => true,
'implicit' => true
) + $GeneralSubtrees
)
);
$this->CPSuri = array('type' =>
ASN1::TYPE_IA5_STRING);
$DisplayText = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'ia5String' => array('type'
=> ASN1::TYPE_IA5_STRING),
'visibleString' => array('type'
=> ASN1::TYPE_VISIBLE_STRING),
'bmpString' => array('type'
=> ASN1::TYPE_BMP_STRING),
'utf8String' => array('type'
=> ASN1::TYPE_UTF8_STRING)
)
);
$NoticeReference = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'organization' => $DisplayText,
'noticeNumbers' => array(
'type' =>
ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => 200,
'children' =>
array('type' => ASN1::TYPE_INTEGER)
)
)
);
$this->UserNotice = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'noticeRef' => array(
'optional' => true,
'implicit' => true
) + $NoticeReference,
'explicitText' => array(
'optional' => true,
'implicit' => true
) + $DisplayText
)
);
// mapping is from
<http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn3.html>
$this->netscape_cert_type = array(
'type' => ASN1::TYPE_BIT_STRING,
'mapping' => array(
'SSLClient',
'SSLServer',
'Email',
'ObjectSigning',
'Reserved',
'SSLCA',
'EmailCA',
'ObjectSigningCA'
)
);
$this->netscape_comment = array('type' =>
ASN1::TYPE_IA5_STRING);
$this->netscape_ca_policy_url = array('type' =>
ASN1::TYPE_IA5_STRING);
// attribute is used in RFC2986 but we're using the RFC5280
definition
$Attribute = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'type' => $AttributeType,
'value'=> array(
'type' => ASN1::TYPE_SET,
'min' => 1,
'max' => -1,
'children' =>
$this->AttributeValue
)
)
);
$this->SubjectDirectoryAttributes = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $Attribute
);
// adapted from <http://tools.ietf.org/html/rfc2986>
$Attributes = array(
'type' => ASN1::TYPE_SET,
'min' => 1,
'max' => -1,
'children' => $Attribute
);
$CertificationRequestInfo = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'version' => array(
'type' =>
ASN1::TYPE_INTEGER,
'mapping' =>
array('v1')
),
'subject' => $this->Name,
'subjectPKInfo' => $SubjectPublicKeyInfo,
'attributes' => array(
'constant' => 0,
'optional' => true,
'implicit' => true
) + $Attributes,
)
);
$this->CertificationRequest = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'certificationRequestInfo' =>
$CertificationRequestInfo,
'signatureAlgorithm' =>
$AlgorithmIdentifier,
'signature' =>
array('type' => ASN1::TYPE_BIT_STRING)
)
);
$RevokedCertificate = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'userCertificate' =>
$CertificateSerialNumber,
'revocationDate' => $Time,
'crlEntryExtensions' => array(
'optional' => true
) +
$this->Extensions
)
);
$TBSCertList = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'version' => array(
'optional' =>
true,
'default' =>
'v1'
) + $Version,
'signature' => $AlgorithmIdentifier,
'issuer' => $this->Name,
'thisUpdate' => $Time,
'nextUpdate' => array(
'optional' =>
true
) + $Time,
'revokedCertificates' => array(
'type' =>
ASN1::TYPE_SEQUENCE,
'optional' =>
true,
'min' => 0,
'max' => -1,
'children' =>
$RevokedCertificate
),
'crlExtensions' => array(
'constant' => 0,
'optional' =>
true,
'explicit' =>
true
) + $this->Extensions
)
);
$this->CertificateList = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'tbsCertList' => $TBSCertList,
'signatureAlgorithm' => $AlgorithmIdentifier,
'signature' => array('type'
=> ASN1::TYPE_BIT_STRING)
)
);
$this->CRLNumber = array('type' =>
ASN1::TYPE_INTEGER);
$this->CRLReason = array('type' =>
ASN1::TYPE_ENUMERATED,
'mapping' => array(
'unspecified',
'keyCompromise',
'cACompromise',
'affiliationChanged',
'superseded',
'cessationOfOperation',
'certificateHold',
// Value 7 is not used.
8 => 'removeFromCRL',
'privilegeWithdrawn',
'aACompromise'
)
);
$this->IssuingDistributionPoint = array('type' =>
ASN1::TYPE_SEQUENCE,
'children' => array(
'distributionPoint' => array(
'constant'
=> 0,
'optional'
=> true,
'explicit'
=> true
) + $DistributionPointName,
'onlyContainsUserCerts' => array(
'type'
=> ASN1::TYPE_BOOLEAN,
'constant'
=> 1,
'optional'
=> true,
'default'
=> false,
'implicit'
=> true
),
'onlyContainsCACerts' => array(
'type'
=> ASN1::TYPE_BOOLEAN,
'constant'
=> 2,
'optional'
=> true,
'default'
=> false,
'implicit'
=> true
),
'onlySomeReasons' => array(
'constant'
=> 3,
'optional'
=> true,
'implicit'
=> true
) + $ReasonFlags,
'indirectCRL' => array(
'type'
=> ASN1::TYPE_BOOLEAN,
'constant'
=> 4,
'optional'
=> true,
'default'
=> false,
'implicit'
=> true
),
'onlyContainsAttributeCerts' => array(
'type'
=> ASN1::TYPE_BOOLEAN,
'constant'
=> 5,
'optional'
=> true,
'default'
=> false,
'implicit'
=> true
)
)
);
$this->InvalidityDate = array('type' =>
ASN1::TYPE_GENERALIZED_TIME);
$this->CertificateIssuer = $GeneralNames;
$this->HoldInstructionCode = array('type' =>
ASN1::TYPE_OBJECT_IDENTIFIER);
$PublicKeyAndChallenge = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'spki' => $SubjectPublicKeyInfo,
'challenge' => array('type' =>
ASN1::TYPE_IA5_STRING)
)
);
$this->SignedPublicKeyAndChallenge = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'publicKeyAndChallenge' =>
$PublicKeyAndChallenge,
'signatureAlgorithm' =>
$AlgorithmIdentifier,
'signature' =>
array('type' => ASN1::TYPE_BIT_STRING)
)
);
$this->PostalAddress = array(
'type' => ASN1::TYPE_SEQUENCE,
'optional' => true,
'min' => 1,
'max' => -1,
'children' => $this->DirectoryString
);
// OIDs from RFC5280 and those RFCs mentioned in
RFC5280#section-4.1.1.2
$this->oids = array(
'1.3.6.1.5.5.7' => 'id-pkix',
'1.3.6.1.5.5.7.1' => 'id-pe',
'1.3.6.1.5.5.7.2' => 'id-qt',
'1.3.6.1.5.5.7.3' => 'id-kp',
'1.3.6.1.5.5.7.48' => 'id-ad',
'1.3.6.1.5.5.7.2.1' => 'id-qt-cps',
'1.3.6.1.5.5.7.2.2' => 'id-qt-unotice',
'1.3.6.1.5.5.7.48.1' =>'id-ad-ocsp',
'1.3.6.1.5.5.7.48.2' =>
'id-ad-caIssuers',
'1.3.6.1.5.5.7.48.3' =>
'id-ad-timeStamping',
'1.3.6.1.5.5.7.48.5' =>
'id-ad-caRepository',
'2.5.4' => 'id-at',
'2.5.4.41' => 'id-at-name',
'2.5.4.4' => 'id-at-surname',
'2.5.4.42' => 'id-at-givenName',
'2.5.4.43' => 'id-at-initials',
'2.5.4.44' =>
'id-at-generationQualifier',
'2.5.4.3' => 'id-at-commonName',
'2.5.4.7' => 'id-at-localityName',
'2.5.4.8' =>
'id-at-stateOrProvinceName',
'2.5.4.10' => 'id-at-organizationName',
'2.5.4.11' =>
'id-at-organizationalUnitName',
'2.5.4.12' => 'id-at-title',
'2.5.4.13' => 'id-at-description',
'2.5.4.46' => 'id-at-dnQualifier',
'2.5.4.6' => 'id-at-countryName',
'2.5.4.5' => 'id-at-serialNumber',
'2.5.4.65' => 'id-at-pseudonym',
'2.5.4.17' => 'id-at-postalCode',
'2.5.4.9' => 'id-at-streetAddress',
'2.5.4.45' => 'id-at-uniqueIdentifier',
'2.5.4.72' => 'id-at-role',
'2.5.4.16' => 'id-at-postalAddress',
'0.9.2342.19200300.100.1.25' =>
'id-domainComponent',
'1.2.840.113549.1.9' => 'pkcs-9',
'1.2.840.113549.1.9.1' =>
'pkcs-9-at-emailAddress',
'2.5.29' => 'id-ce',
'2.5.29.35' =>
'id-ce-authorityKeyIdentifier',
'2.5.29.14' =>
'id-ce-subjectKeyIdentifier',
'2.5.29.15' => 'id-ce-keyUsage',
'2.5.29.16' =>
'id-ce-privateKeyUsagePeriod',
'2.5.29.32' =>
'id-ce-certificatePolicies',
'2.5.29.32.0' => 'anyPolicy',
'2.5.29.33' => 'id-ce-policyMappings',
'2.5.29.17' => 'id-ce-subjectAltName',
'2.5.29.18' => 'id-ce-issuerAltName',
'2.5.29.9' =>
'id-ce-subjectDirectoryAttributes',
'2.5.29.19' => 'id-ce-basicConstraints',
'2.5.29.30' => 'id-ce-nameConstraints',
'2.5.29.36' =>
'id-ce-policyConstraints',
'2.5.29.31' =>
'id-ce-cRLDistributionPoints',
'2.5.29.37' => 'id-ce-extKeyUsage',
'2.5.29.37.0' => 'anyExtendedKeyUsage',
'1.3.6.1.5.5.7.3.1' =>
'id-kp-serverAuth',
'1.3.6.1.5.5.7.3.2' =>
'id-kp-clientAuth',
'1.3.6.1.5.5.7.3.3' =>
'id-kp-codeSigning',
'1.3.6.1.5.5.7.3.4' =>
'id-kp-emailProtection',
'1.3.6.1.5.5.7.3.8' =>
'id-kp-timeStamping',
'1.3.6.1.5.5.7.3.9' =>
'id-kp-OCSPSigning',
'2.5.29.54' => 'id-ce-inhibitAnyPolicy',
'2.5.29.46' => 'id-ce-freshestCRL',
'1.3.6.1.5.5.7.1.1' =>
'id-pe-authorityInfoAccess',
'1.3.6.1.5.5.7.1.11' =>
'id-pe-subjectInfoAccess',
'2.5.29.20' => 'id-ce-cRLNumber',
'2.5.29.28' =>
'id-ce-issuingDistributionPoint',
'2.5.29.27' =>
'id-ce-deltaCRLIndicator',
'2.5.29.21' => 'id-ce-cRLReasons',
'2.5.29.29' =>
'id-ce-certificateIssuer',
'2.5.29.23' =>
'id-ce-holdInstructionCode',
'1.2.840.10040.2' => 'holdInstruction',
'1.2.840.10040.2.1' =>
'id-holdinstruction-none',
'1.2.840.10040.2.2' =>
'id-holdinstruction-callissuer',
'1.2.840.10040.2.3' =>
'id-holdinstruction-reject',
'2.5.29.24' => 'id-ce-invalidityDate',
'1.2.840.113549.2.2' => 'md2',
'1.2.840.113549.2.5' => 'md5',
'1.3.14.3.2.26' => 'id-sha1',
'1.2.840.10040.4.1' => 'id-dsa',
'1.2.840.10040.4.3' =>
'id-dsa-with-sha1',
'1.2.840.113549.1.1' => 'pkcs-1',
'1.2.840.113549.1.1.1' =>
'rsaEncryption',
'1.2.840.113549.1.1.2' =>
'md2WithRSAEncryption',
'1.2.840.113549.1.1.4' =>
'md5WithRSAEncryption',
'1.2.840.113549.1.1.5' =>
'sha1WithRSAEncryption',
'1.2.840.10046.2.1' => 'dhpublicnumber',
'2.16.840.1.101.2.1.1.22' =>
'id-keyExchangeAlgorithm',
'1.2.840.10045' => 'ansi-X9-62',
'1.2.840.10045.4' => 'id-ecSigType',
'1.2.840.10045.4.1' =>
'ecdsa-with-SHA1',
'1.2.840.10045.1' => 'id-fieldType',
'1.2.840.10045.1.1' => 'prime-field',
'1.2.840.10045.1.2' =>
'characteristic-two-field',
'1.2.840.10045.1.2.3' =>
'id-characteristic-two-basis',
'1.2.840.10045.1.2.3.1' => 'gnBasis',
'1.2.840.10045.1.2.3.2' => 'tpBasis',
'1.2.840.10045.1.2.3.3' => 'ppBasis',
'1.2.840.10045.2' => 'id-publicKeyType',
'1.2.840.10045.2.1' => 'id-ecPublicKey',
'1.2.840.10045.3' => 'ellipticCurve',
'1.2.840.10045.3.0' => 'c-TwoCurve',
'1.2.840.10045.3.0.1' => 'c2pnb163v1',
'1.2.840.10045.3.0.2' => 'c2pnb163v2',
'1.2.840.10045.3.0.3' => 'c2pnb163v3',
'1.2.840.10045.3.0.4' => 'c2pnb176w1',
'1.2.840.10045.3.0.5' => 'c2pnb191v1',
'1.2.840.10045.3.0.6' => 'c2pnb191v2',
'1.2.840.10045.3.0.7' => 'c2pnb191v3',
'1.2.840.10045.3.0.8' => 'c2pnb191v4',
'1.2.840.10045.3.0.9' => 'c2pnb191v5',
'1.2.840.10045.3.0.10' => 'c2pnb208w1',
'1.2.840.10045.3.0.11' => 'c2pnb239v1',
'1.2.840.10045.3.0.12' => 'c2pnb239v2',
'1.2.840.10045.3.0.13' => 'c2pnb239v3',
'1.2.840.10045.3.0.14' => 'c2pnb239v4',
'1.2.840.10045.3.0.15' => 'c2pnb239v5',
'1.2.840.10045.3.0.16' => 'c2pnb272w1',
'1.2.840.10045.3.0.17' => 'c2pnb304w1',
'1.2.840.10045.3.0.18' => 'c2pnb359v1',
'1.2.840.10045.3.0.19' => 'c2pnb368w1',
'1.2.840.10045.3.0.20' => 'c2pnb431r1',
'1.2.840.10045.3.1' => 'primeCurve',
'1.2.840.10045.3.1.1' => 'prime192v1',
'1.2.840.10045.3.1.2' => 'prime192v2',
'1.2.840.10045.3.1.3' => 'prime192v3',
'1.2.840.10045.3.1.4' => 'prime239v1',
'1.2.840.10045.3.1.5' => 'prime239v2',
'1.2.840.10045.3.1.6' => 'prime239v3',
'1.2.840.10045.3.1.7' => 'prime256v1',
'1.2.840.113549.1.1.7' =>
'id-RSAES-OAEP',
'1.2.840.113549.1.1.9' =>
'id-pSpecified',
'1.2.840.113549.1.1.10' =>
'id-RSASSA-PSS',
'1.2.840.113549.1.1.8' => 'id-mgf1',
'1.2.840.113549.1.1.14' =>
'sha224WithRSAEncryption',
'1.2.840.113549.1.1.11' =>
'sha256WithRSAEncryption',
'1.2.840.113549.1.1.12' =>
'sha384WithRSAEncryption',
'1.2.840.113549.1.1.13' =>
'sha512WithRSAEncryption',
'2.16.840.1.101.3.4.2.4' => 'id-sha224',
'2.16.840.1.101.3.4.2.1' => 'id-sha256',
'2.16.840.1.101.3.4.2.2' => 'id-sha384',
'2.16.840.1.101.3.4.2.3' => 'id-sha512',
'1.2.643.2.2.4' =>
'id-GostR3411-94-with-GostR3410-94',
'1.2.643.2.2.3' =>
'id-GostR3411-94-with-GostR3410-2001',
'1.2.643.2.2.20' => 'id-GostR3410-2001',
'1.2.643.2.2.19' => 'id-GostR3410-94',
// Netscape Object Identifiers from "Netscape Certificate
Extensions"
'2.16.840.1.113730' => 'netscape',
'2.16.840.1.113730.1' =>
'netscape-cert-extension',
'2.16.840.1.113730.1.1' =>
'netscape-cert-type',
'2.16.840.1.113730.1.13' =>
'netscape-comment',
'2.16.840.1.113730.1.8' =>
'netscape-ca-policy-url',
// the following are X.509 extensions not supported by
phpseclib
'1.3.6.1.5.5.7.1.12' =>
'id-pe-logotype',
'1.2.840.113533.7.65.0' =>
'entrustVersInfo',
'2.16.840.1.113733.1.6.9' =>
'verisignPrivate',
// for Certificate Signing Requests
// see http://tools.ietf.org/html/rfc2985
'1.2.840.113549.1.9.2' =>
'pkcs-9-at-unstructuredName', // PKCS #9 unstructured name
'1.2.840.113549.1.9.7' =>
'pkcs-9-at-challengePassword', // Challenge password for
certificate revocations
'1.2.840.113549.1.9.14' =>
'pkcs-9-at-extensionRequest' // Certificate extension request
);
}
/**
* Load X.509 certificate
*
* Returns an associative array describing the X.509 cert or a false if
the cert failed to load
*
* @param string $cert
* @param int $mode
* @access public
* @return mixed
*/
function loadX509($cert, $mode = self::FORMAT_AUTO_DETECT)
{
if (is_array($cert) &&
isset($cert['tbsCertificate'])) {
unset($this->currentCert);
unset($this->currentKeyIdentifier);
$this->dn =
$cert['tbsCertificate']['subject'];
if (!isset($this->dn)) {
return false;
}
$this->currentCert = $cert;
$currentKeyIdentifier =
$this->getExtension('id-ce-subjectKeyIdentifier');
$this->currentKeyIdentifier =
is_string($currentKeyIdentifier) ? $currentKeyIdentifier : null;
unset($this->signatureSubject);
return $cert;
}
$asn1 = new ASN1();
if ($mode != self::FORMAT_DER) {
$newcert = $this->_extractBER($cert);
if ($mode == self::FORMAT_PEM && $cert == $newcert) {
return false;
}
$cert = $newcert;
}
if ($cert === false) {
$this->currentCert = false;
return false;
}
$asn1->loadOIDs($this->oids);
$decoded = $asn1->decodeBER($cert);
if (!empty($decoded)) {
$x509 = $asn1->asn1map($decoded[0], $this->Certificate);
}
if (!isset($x509) || $x509 === false) {
$this->currentCert = false;
return false;
}
$this->signatureSubject = substr($cert,
$decoded[0]['content'][0]['start'],
$decoded[0]['content'][0]['length']);
if ($this->_isSubArrayValid($x509,
'tbsCertificate/extensions')) {
$this->_mapInExtensions($x509,
'tbsCertificate/extensions', $asn1);
}
$this->_mapInDNs($x509,
'tbsCertificate/issuer/rdnSequence', $asn1);
$this->_mapInDNs($x509,
'tbsCertificate/subject/rdnSequence', $asn1);
$key =
&$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'];
$key =
$this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
$key);
$this->currentCert = $x509;
$this->dn =
$x509['tbsCertificate']['subject'];
$currentKeyIdentifier =
$this->getExtension('id-ce-subjectKeyIdentifier');
$this->currentKeyIdentifier = is_string($currentKeyIdentifier) ?
$currentKeyIdentifier : null;
return $x509;
}
/**
* Save X.509 certificate
*
* @param array $cert
* @param int $format optional
* @access public
* @return string
*/
function saveX509($cert, $format = self::FORMAT_PEM)
{
if (!is_array($cert) || !isset($cert['tbsCertificate']))
{
return false;
}
switch (true) {
// "case !$a: case !$b: break; default: whatever();"
is the same thing as "if ($a && $b) whatever()"
case !($algorithm = $this->_subArray($cert,
'tbsCertificate/subjectPublicKeyInfo/algorithm/algorithm')):
case
is_object($cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
break;
default:
switch ($algorithm) {
case 'rsaEncryption':
$cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']
= base64_encode("\0" .
base64_decode(preg_replace('#-.+-|[\r\n]#', '',
$cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'])));
/* "[For RSA keys] the parameters field MUST
have ASN.1 type NULL for this algorithm identifier."
--
https://tools.ietf.org/html/rfc3279#section-2.3.1
given that and the fact that RSA keys appear ot
be the only key type for which the parameters field can be blank,
it seems like perhaps the ASN.1 description
ought not say the parameters field is OPTIONAL, but whatever.
*/
$cert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters']
= null;
//
https://tools.ietf.org/html/rfc3279#section-2.2.1
$cert['signatureAlgorithm']['parameters'] = null;
$cert['tbsCertificate']['signature']['parameters']
= null;
}
}
$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$type_utf8_string = array('type' =>
ASN1::TYPE_UTF8_STRING);
$filters['tbsCertificate']['signature']['parameters']
= $type_utf8_string;
$filters['tbsCertificate']['signature']['issuer']['rdnSequence']['value']
= $type_utf8_string;
$filters['tbsCertificate']['issuer']['rdnSequence']['value']
= $type_utf8_string;
$filters['tbsCertificate']['subject']['rdnSequence']['value']
= $type_utf8_string;
$filters['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters']
= $type_utf8_string;
$filters['signatureAlgorithm']['parameters'] =
$type_utf8_string;
$filters['authorityCertIssuer']['directoryName']['rdnSequence']['value']
= $type_utf8_string;
//$filters['policyQualifiers']['qualifier'] =
$type_utf8_string;
$filters['distributionPoint']['fullName']['directoryName']['rdnSequence']['value']
= $type_utf8_string;
$filters['directoryName']['rdnSequence']['value']
= $type_utf8_string;
/* in the case of policyQualifiers/qualifier, the type has to be
\phpseclib\File\ASN1::TYPE_IA5_STRING.
\phpseclib\File\ASN1::TYPE_PRINTABLE_STRING will cause
OpenSSL's X.509 parser to spit out random
characters.
*/
$filters['policyQualifiers']['qualifier']
= array('type' => ASN1::TYPE_IA5_STRING);
$asn1->loadFilters($filters);
$this->_mapOutExtensions($cert,
'tbsCertificate/extensions', $asn1);
$this->_mapOutDNs($cert,
'tbsCertificate/issuer/rdnSequence', $asn1);
$this->_mapOutDNs($cert,
'tbsCertificate/subject/rdnSequence', $asn1);
$cert = $asn1->encodeDER($cert, $this->Certificate);
switch ($format) {
case self::FORMAT_DER:
return $cert;
// case self::FORMAT_PEM:
default:
return "-----BEGIN CERTIFICATE-----\r\n" .
chunk_split(base64_encode($cert), 64) . '-----END
CERTIFICATE-----';
}
}
/**
* Map extension values from octet string to extension-specific
internal
* format.
*
* @param array $root (by reference)
* @param string $path
* @param object $asn1
* @access private
*/
function _mapInExtensions(&$root, $path, $asn1)
{
$extensions = &$this->_subArrayUnchecked($root, $path);
if ($extensions) {
for ($i = 0; $i < count($extensions); $i++) {
$id = $extensions[$i]['extnId'];
$value = &$extensions[$i]['extnValue'];
$value = base64_decode($value);
$decoded = $asn1->decodeBER($value);
/* [extnValue] contains the DER encoding of an ASN.1 value
corresponding to the extension type identified by extnID
*/
$map = $this->_getMapping($id);
if (!is_bool($map)) {
$decoder = $id == 'id-ce-nameConstraints' ?
array($this, '_decodeNameConstraintIP') :
array($this, '_decodeIP');
$mapped = $asn1->asn1map($decoded[0], $map,
array('iPAddress' => $decoder));
$value = $mapped === false ? $decoded[0] : $mapped;
if ($id == 'id-ce-certificatePolicies') {
for ($j = 0; $j < count($value); $j++) {
if
(!isset($value[$j]['policyQualifiers'])) {
continue;
}
for ($k = 0; $k <
count($value[$j]['policyQualifiers']); $k++) {
$subid =
$value[$j]['policyQualifiers'][$k]['policyQualifierId'];
$map = $this->_getMapping($subid);
$subvalue =
&$value[$j]['policyQualifiers'][$k]['qualifier'];
if ($map !== false) {
$decoded =
$asn1->decodeBER($subvalue);
$mapped =
$asn1->asn1map($decoded[0], $map);
$subvalue = $mapped === false ?
$decoded[0] : $mapped;
}
}
}
}
} else {
$value = base64_encode($value);
}
}
}
}
/**
* Map extension values from extension-specific internal format to
* octet string.
*
* @param array $root (by reference)
* @param string $path
* @param object $asn1
* @access private
*/
function _mapOutExtensions(&$root, $path, $asn1)
{
$extensions = &$this->_subArray($root, $path);
if (is_array($extensions)) {
$size = count($extensions);
for ($i = 0; $i < $size; $i++) {
if ($extensions[$i] instanceof Element) {
continue;
}
$id = $extensions[$i]['extnId'];
$value = &$extensions[$i]['extnValue'];
switch ($id) {
case 'id-ce-certificatePolicies':
for ($j = 0; $j < count($value); $j++) {
if
(!isset($value[$j]['policyQualifiers'])) {
continue;
}
for ($k = 0; $k <
count($value[$j]['policyQualifiers']); $k++) {
$subid =
$value[$j]['policyQualifiers'][$k]['policyQualifierId'];
$map = $this->_getMapping($subid);
$subvalue =
&$value[$j]['policyQualifiers'][$k]['qualifier'];
if ($map !== false) {
// by default \phpseclib\File\ASN1 will
try to render qualifier as a \phpseclib\File\ASN1::TYPE_IA5_STRING since
it's
// actual type is
\phpseclib\File\ASN1::TYPE_ANY
$subvalue = new
Element($asn1->encodeDER($subvalue, $map));
}
}
}
break;
case 'id-ce-authorityKeyIdentifier': // use
00 as the serial number instead of an empty string
if
(isset($value['authorityCertSerialNumber'])) {
if
($value['authorityCertSerialNumber']->toBytes() ==
'') {
$temp = chr((ASN1::CLASS_CONTEXT_SPECIFIC
<< 6) | 2) . "\1\0";
$value['authorityCertSerialNumber'] = new Element($temp);
}
}
}
/* [extnValue] contains the DER encoding of an ASN.1 value
corresponding to the extension type identified by extnID
*/
$map = $this->_getMapping($id);
if (is_bool($map)) {
if (!$map) {
user_error($id . ' is not a currently
supported extension');
unset($extensions[$i]);
}
} else {
$temp = $asn1->encodeDER($value, $map,
array('iPAddress' => array($this, '_encodeIP')));
$value = base64_encode($temp);
}
}
}
}
/**
* Map attribute values from ANY type to attribute-specific internal
* format.
*
* @param array $root (by reference)
* @param string $path
* @param object $asn1
* @access private
*/
function _mapInAttributes(&$root, $path, $asn1)
{
$attributes = &$this->_subArray($root, $path);
if (is_array($attributes)) {
for ($i = 0; $i < count($attributes); $i++) {
$id = $attributes[$i]['type'];
/* $value contains the DER encoding of an ASN.1 value
corresponding to the attribute type identified by type
*/
$map = $this->_getMapping($id);
if (is_array($attributes[$i]['value'])) {
$values = &$attributes[$i]['value'];
for ($j = 0; $j < count($values); $j++) {
$value = $asn1->encodeDER($values[$j],
$this->AttributeValue);
$decoded = $asn1->decodeBER($value);
if (!is_bool($map)) {
$mapped = $asn1->asn1map($decoded[0], $map);
if ($mapped !== false) {
$values[$j] = $mapped;
}
if ($id ==
'pkcs-9-at-extensionRequest' &&
$this->_isSubArrayValid($values, $j)) {
$this->_mapInExtensions($values, $j,
$asn1);
}
} elseif ($map) {
$values[$j] = base64_encode($value);
}
}
}
}
}
}
/**
* Map attribute values from attribute-specific internal format to
* ANY type.
*
* @param array $root (by reference)
* @param string $path
* @param object $asn1
* @access private
*/
function _mapOutAttributes(&$root, $path, $asn1)
{
$attributes = &$this->_subArray($root, $path);
if (is_array($attributes)) {
$size = count($attributes);
for ($i = 0; $i < $size; $i++) {
/* [value] contains the DER encoding of an ASN.1 value
corresponding to the attribute type identified by type
*/
$id = $attributes[$i]['type'];
$map = $this->_getMapping($id);
if ($map === false) {
user_error($id . ' is not a currently supported
attribute', E_USER_NOTICE);
unset($attributes[$i]);
} elseif (is_array($attributes[$i]['value'])) {
$values = &$attributes[$i]['value'];
for ($j = 0; $j < count($values); $j++) {
switch ($id) {
case 'pkcs-9-at-extensionRequest':
$this->_mapOutExtensions($values, $j,
$asn1);
break;
}
if (!is_bool($map)) {
$temp = $asn1->encodeDER($values[$j], $map);
$decoded = $asn1->decodeBER($temp);
$values[$j] = $asn1->asn1map($decoded[0],
$this->AttributeValue);
}
}
}
}
}
}
/**
* Map DN values from ANY type to DN-specific internal
* format.
*
* @param array $root (by reference)
* @param string $path
* @param object $asn1
* @access private
*/
function _mapInDNs(&$root, $path, $asn1)
{
$dns = &$this->_subArray($root, $path);
if (is_array($dns)) {
for ($i = 0; $i < count($dns); $i++) {
for ($j = 0; $j < count($dns[$i]); $j++) {
$type = $dns[$i][$j]['type'];
$value = &$dns[$i][$j]['value'];
if (is_object($value) && $value instanceof
Element) {
$map = $this->_getMapping($type);
if (!is_bool($map)) {
$decoded = $asn1->decodeBER($value);
$value = $asn1->asn1map($decoded[0], $map);
}
}
}
}
}
}
/**
* Map DN values from DN-specific internal format to
* ANY type.
*
* @param array $root (by reference)
* @param string $path
* @param object $asn1
* @access private
*/
function _mapOutDNs(&$root, $path, $asn1)
{
$dns = &$this->_subArray($root, $path);
if (is_array($dns)) {
$size = count($dns);
for ($i = 0; $i < $size; $i++) {
for ($j = 0; $j < count($dns[$i]); $j++) {
$type = $dns[$i][$j]['type'];
$value = &$dns[$i][$j]['value'];
if (is_object($value) && $value instanceof
Element) {
continue;
}
$map = $this->_getMapping($type);
if (!is_bool($map)) {
$value = new Element($asn1->encodeDER($value,
$map));
}
}
}
}
}
/**
* Associate an extension ID to an extension mapping
*
* @param string $extnId
* @access private
* @return mixed
*/
function _getMapping($extnId)
{
if (!is_string($extnId)) { // eg. if it's a
\phpseclib\File\ASN1\Element object
return true;
}
switch ($extnId) {
case 'id-ce-keyUsage':
return $this->KeyUsage;
case 'id-ce-basicConstraints':
return $this->BasicConstraints;
case 'id-ce-subjectKeyIdentifier':
return $this->KeyIdentifier;
case 'id-ce-cRLDistributionPoints':
return $this->CRLDistributionPoints;
case 'id-ce-authorityKeyIdentifier':
return $this->AuthorityKeyIdentifier;
case 'id-ce-certificatePolicies':
return $this->CertificatePolicies;
case 'id-ce-extKeyUsage':
return $this->ExtKeyUsageSyntax;
case 'id-pe-authorityInfoAccess':
return $this->AuthorityInfoAccessSyntax;
case 'id-pe-subjectInfoAccess':
return $this->SubjectInfoAccessSyntax;
case 'id-ce-subjectAltName':
return $this->SubjectAltName;
case 'id-ce-subjectDirectoryAttributes':
return $this->SubjectDirectoryAttributes;
case 'id-ce-privateKeyUsagePeriod':
return $this->PrivateKeyUsagePeriod;
case 'id-ce-issuerAltName':
return $this->IssuerAltName;
case 'id-ce-policyMappings':
return $this->PolicyMappings;
case 'id-ce-nameConstraints':
return $this->NameConstraints;
case 'netscape-cert-type':
return $this->netscape_cert_type;
case 'netscape-comment':
return $this->netscape_comment;
case 'netscape-ca-policy-url':
return $this->netscape_ca_policy_url;
// since id-qt-cps isn't a constructed type it will have
already been decoded as a string by the time it gets
// back around to asn1map() and we don't want it decoded
again.
//case 'id-qt-cps':
// return $this->CPSuri;
case 'id-qt-unotice':
return $this->UserNotice;
// the following OIDs are unsupported but we don't want
them to give notices when calling saveX509().
case 'id-pe-logotype': //
http://www.ietf.org/rfc/rfc3709.txt
case 'entrustVersInfo':
// http://support.microsoft.com/kb/287547
case '1.3.6.1.4.1.311.20.2': //
szOID_ENROLL_CERTTYPE_EXTENSION
case '1.3.6.1.4.1.311.21.1': //
szOID_CERTSRV_CA_VERSION
// "SET Secure Electronic Transaction Specification"
// http://www.maithean.com/docs/set_bk3.pdf
case '2.23.42.7.0': // id-set-hashedRootKey
// "Certificate Transparency"
// https://tools.ietf.org/html/rfc6962
case '1.3.6.1.4.1.11129.2.4.2':
// "Qualified Certificate statements"
// https://tools.ietf.org/html/rfc3739#section-3.2.6
case '1.3.6.1.5.5.7.1.3':
return true;
// CSR attributes
case 'pkcs-9-at-unstructuredName':
return $this->PKCS9String;
case 'pkcs-9-at-challengePassword':
return $this->DirectoryString;
case 'pkcs-9-at-extensionRequest':
return $this->Extensions;
// CRL extensions.
case 'id-ce-cRLNumber':
return $this->CRLNumber;
case 'id-ce-deltaCRLIndicator':
return $this->CRLNumber;
case 'id-ce-issuingDistributionPoint':
return $this->IssuingDistributionPoint;
case 'id-ce-freshestCRL':
return $this->CRLDistributionPoints;
case 'id-ce-cRLReasons':
return $this->CRLReason;
case 'id-ce-invalidityDate':
return $this->InvalidityDate;
case 'id-ce-certificateIssuer':
return $this->CertificateIssuer;
case 'id-ce-holdInstructionCode':
return $this->HoldInstructionCode;
case 'id-at-postalAddress':
return $this->PostalAddress;
}
return false;
}
/**
* Load an X.509 certificate as a certificate authority
*
* @param string $cert
* @access public
* @return bool
*/
function loadCA($cert)
{
$olddn = $this->dn;
$oldcert = $this->currentCert;
$oldsigsubj = $this->signatureSubject;
$oldkeyid = $this->currentKeyIdentifier;
$cert = $this->loadX509($cert);
if (!$cert) {
$this->dn = $olddn;
$this->currentCert = $oldcert;
$this->signatureSubject = $oldsigsubj;
$this->currentKeyIdentifier = $oldkeyid;
return false;
}
/* From RFC5280 "PKIX Certificate and CRL Profile":
If the keyUsage extension is present, then the subject public
key
MUST NOT be used to verify signatures on certificates or CRLs
unless
the corresponding keyCertSign or cRLSign bit is set. */
//$keyUsage = $this->getExtension('id-ce-keyUsage');
//if ($keyUsage && !in_array('keyCertSign',
$keyUsage)) {
// return false;
//}
/* From RFC5280 "PKIX Certificate and CRL Profile":
The cA boolean indicates whether the certified public key may be
used
to verify certificate signatures. If the cA boolean is not
asserted,
then the keyCertSign bit in the key usage extension MUST NOT be
asserted. If the basic constraints extension is not present in
a
version 3 certificate, or the extension is present but the cA
boolean
is not asserted, then the certified public key MUST NOT be used
to
verify certificate signatures. */
//$basicConstraints =
$this->getExtension('id-ce-basicConstraints');
//if (!$basicConstraints || !$basicConstraints['cA']) {
// return false;
//}
$this->CAs[] = $cert;
$this->dn = $olddn;
$this->currentCert = $oldcert;
$this->signatureSubject = $oldsigsubj;
return true;
}
/**
* Validate an X.509 certificate against a URL
*
* From RFC2818 "HTTP over TLS":
*
* Matching is performed using the matching rules specified by
* [RFC2459]. If more than one identity of a given type is present in
* the certificate (e.g., more than one dNSName name, a match in any
one
* of the set is considered acceptable.) Names may contain the wildcard
* character * which is considered to match any single domain name
* component or component fragment. E.g., *.a.com matches foo.a.com but
* not bar.foo.a.com. f*.com matches foo.com but not bar.com.
*
* @param string $url
* @access public
* @return bool
*/
function validateURL($url)
{
if (!is_array($this->currentCert) ||
!isset($this->currentCert['tbsCertificate'])) {
return false;
}
$components = parse_url($url);
if (!isset($components['host'])) {
return false;
}
if ($names =
$this->getExtension('id-ce-subjectAltName')) {
foreach ($names as $name) {
foreach ($name as $key => $value) {
$value = str_replace(array('.',
'*'), array('\.', '[^.]*'), $value);
switch ($key) {
case 'dNSName':
/* From RFC2818 "HTTP over TLS":
If a subjectAltName extension of type
dNSName is present, that MUST
be used as the identity. Otherwise, the
(most specific) Common Name
field in the Subject field of the
certificate MUST be used. Although
the use of the Common Name is existing
practice, it is deprecated and
Certification Authorities are encouraged to
use the dNSName instead. */
if (preg_match('#^' . $value .
'$#', $components['host'])) {
return true;
}
break;
case 'iPAddress':
/* From RFC2818 "HTTP over TLS":
In some cases, the URI is specified as an IP
address rather than a
hostname. In this case, the iPAddress
subjectAltName must be present
in the certificate and must exactly match
the IP in the URI. */
if (preg_match('#(?:\d{1-3}\.){4}#',
$components['host'] . '.') &&
preg_match('#^' . $value . '$#',
$components['host'])) {
return true;
}
}
}
}
return false;
}
if ($value = $this->getDNProp('id-at-commonName')) {
$value = str_replace(array('.', '*'),
array('\.', '[^.]*'), $value[0]);
return preg_match('#^' . $value . '$#',
$components['host']);
}
return false;
}
/**
* Validate a date
*
* If $date isn't defined it is assumed to be the current date.
*
* @param \DateTime|string $date optional
* @access public
*/
function validateDate($date = null)
{
if (!is_array($this->currentCert) ||
!isset($this->currentCert['tbsCertificate'])) {
return false;
}
if (!isset($date)) {
$date = new DateTime(null, new
DateTimeZone(@date_default_timezone_get()));
}
$notBefore =
$this->currentCert['tbsCertificate']['validity']['notBefore'];
$notBefore = isset($notBefore['generalTime']) ?
$notBefore['generalTime'] : $notBefore['utcTime'];
$notAfter =
$this->currentCert['tbsCertificate']['validity']['notAfter'];
$notAfter = isset($notAfter['generalTime']) ?
$notAfter['generalTime'] : $notAfter['utcTime'];
if (is_string($date)) {
$date = new DateTime($date, new
DateTimeZone(@date_default_timezone_get()));
}
$notBefore = new DateTime($notBefore, new
DateTimeZone(@date_default_timezone_get()));
$notAfter = new DateTime($notAfter, new
DateTimeZone(@date_default_timezone_get()));
switch (true) {
case $date < $notBefore:
case $date > $notAfter:
return false;
}
return true;
}
/**
* Fetches a URL
*
* @param string $url
* @access private
* @return bool|string
*/
static function _fetchURL($url)
{
if (self::$disable_url_fetch) {
return false;
}
$parts = parse_url($url);
$data = '';
switch ($parts['scheme']) {
case 'http':
$fsock = @fsockopen($parts['host'],
isset($parts['port']) ? $parts['port'] : 80);
if (!$fsock) {
return false;
}
fputs($fsock, "GET $parts[path] HTTP/1.0\r\n");
fputs($fsock, "Host: $parts[host]\r\n\r\n");
$line = fgets($fsock, 1024);
if (strlen($line) < 3) {
return false;
}
preg_match('#HTTP/1.\d (\d{3})#', $line, $temp);
if ($temp[1] != '200') {
return false;
}
// skip the rest of the headers in the http response
while (!feof($fsock) && fgets($fsock, 1024) !=
"\r\n") {
}
while (!feof($fsock)) {
$temp = fread($fsock, 1024);
if ($temp === false) {
return false;
}
$data.= $temp;
}
break;
//case 'ftp':
//case 'ldap':
//default:
}
return $data;
}
/**
* Validates an intermediate cert as identified via authority info
access extension
*
* See https://tools.ietf.org/html/rfc4325 for more info
*
* @param bool $caonly
* @param int $count
* @access private
* @return bool
*/
function _testForIntermediate($caonly, $count)
{
$opts =
$this->getExtension('id-pe-authorityInfoAccess');
if (!is_array($opts)) {
return false;
}
foreach ($opts as $opt) {
if ($opt['accessMethod'] ==
'id-ad-caIssuers') {
// accessLocation is a GeneralName. GeneralName fields
support stuff like email addresses, IP addresses, LDAP,
// etc, but we're only supporting URI's.
URI's and LDAP are the only thing https://tools.ietf.org/html/rfc4325
// discusses
if
(isset($opt['accessLocation']['uniformResourceIdentifier']))
{
$url =
$opt['accessLocation']['uniformResourceIdentifier'];
break;
}
}
}
if (!isset($url)) {
return false;
}
$cert = static::_fetchURL($url);
if (!is_string($cert)) {
return false;
}
$parent = new static();
$parent->CAs = $this->CAs;
/*
"Conforming applications that support HTTP or FTP for
accessing
certificates MUST be able to accept .cer files and SHOULD be able
to accept .p7c files." --
https://tools.ietf.org/html/rfc4325
A .p7c file is 'a "certs-only" CMS message as
specified in RFC 2797"
These are currently unsupported
*/
if (!is_array($parent->loadX509($cert))) {
return false;
}
if (!$parent->_validateSignatureCountable($caonly, ++$count)) {
return false;
}
$this->CAs[] = $parent->currentCert;
//$this->loadCA($cert);
return true;
}
/**
* Validate a signature
*
* Works on X.509 certs, CSR's and CRL's.
* Returns true if the signature is verified, false if it is not
correct or null on error
*
* By default returns false for self-signed certs. Call
validateSignature(false) to make this support
* self-signed.
*
* The behavior of this function is inspired by {@link
http://php.net/openssl-verify openssl_verify}.
*
* @param bool $caonly optional
* @access public
* @return mixed
*/
function validateSignature($caonly = true)
{
return $this->_validateSignatureCountable($caonly, 0);
}
/**
* Validate a signature
*
* Performs said validation whilst keeping track of how many times
validation method is called
*
* @param bool $caonly
* @param int $count
* @access private
* @return mixed
*/
function _validateSignatureCountable($caonly, $count)
{
if (!is_array($this->currentCert) ||
!isset($this->signatureSubject)) {
return null;
}
if ($count == self::$recur_limit) {
return false;
}
/* TODO:
"emailAddress attribute values are not case-sensitive
(e.g., "subscriber@example.com" is the same as
"SUBSCRIBER@EXAMPLE.COM")."
-- http://tools.ietf.org/html/rfc5280#section-4.1.2.6
implement pathLenConstraint in the id-ce-basicConstraints
extension */
switch (true) {
case isset($this->currentCert['tbsCertificate']):
// self-signed cert
switch (true) {
case !defined('FILE_X509_IGNORE_TYPE')
&&
$this->currentCert['tbsCertificate']['issuer'] ===
$this->currentCert['tbsCertificate']['subject']:
case defined('FILE_X509_IGNORE_TYPE')
&& $this->getIssuerDN(self::DN_STRING) ===
$this->getDN(self::DN_STRING):
$authorityKey =
$this->getExtension('id-ce-authorityKeyIdentifier');
$subjectKeyID =
$this->getExtension('id-ce-subjectKeyIdentifier');
switch (true) {
case !is_array($authorityKey):
case !$subjectKeyID:
case
isset($authorityKey['keyIdentifier']) &&
$authorityKey['keyIdentifier'] === $subjectKeyID:
$signingCert = $this->currentCert; //
working cert
}
}
if (!empty($this->CAs)) {
for ($i = 0; $i < count($this->CAs); $i++) {
// even if the cert is a self-signed one we still
want to see if it's a CA;
// if not, we'll conditionally return an error
$ca = $this->CAs[$i];
switch (true) {
case
!defined('FILE_X509_IGNORE_TYPE') &&
$this->currentCert['tbsCertificate']['issuer'] ===
$ca['tbsCertificate']['subject']:
case defined('FILE_X509_IGNORE_TYPE')
&& $this->getDN(self::DN_STRING,
$this->currentCert['tbsCertificate']['issuer']) ===
$this->getDN(self::DN_STRING,
$ca['tbsCertificate']['subject']):
$authorityKey =
$this->getExtension('id-ce-authorityKeyIdentifier');
$subjectKeyID =
$this->getExtension('id-ce-subjectKeyIdentifier', $ca);
switch (true) {
case !is_array($authorityKey):
case !$subjectKeyID:
case
isset($authorityKey['keyIdentifier']) &&
$authorityKey['keyIdentifier'] === $subjectKeyID:
if (is_array($authorityKey)
&& isset($authorityKey['authorityCertSerialNumber'])
&&
!$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber']))
{
break 2; // serial mismatch -
check other ca
}
$signingCert = $ca; // working cert
break 3;
}
}
}
if (count($this->CAs) == $i && $caonly) {
return $this->_testForIntermediate($caonly,
$count) && $this->validateSignature($caonly);
}
} elseif (!isset($signingCert) || $caonly) {
return $this->_testForIntermediate($caonly, $count)
&& $this->validateSignature($caonly);
}
return $this->_validateSignature(
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'],
substr(base64_decode($this->currentCert['signature']), 1),
$this->signatureSubject
);
case
isset($this->currentCert['certificationRequestInfo']):
return $this->_validateSignature(
$this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'],
$this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'],
substr(base64_decode($this->currentCert['signature']), 1),
$this->signatureSubject
);
case
isset($this->currentCert['publicKeyAndChallenge']):
return $this->_validateSignature(
$this->currentCert['publicKeyAndChallenge']['spki']['algorithm']['algorithm'],
$this->currentCert['publicKeyAndChallenge']['spki']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'],
substr(base64_decode($this->currentCert['signature']), 1),
$this->signatureSubject
);
case isset($this->currentCert['tbsCertList']):
if (!empty($this->CAs)) {
for ($i = 0; $i < count($this->CAs); $i++) {
$ca = $this->CAs[$i];
switch (true) {
case
!defined('FILE_X509_IGNORE_TYPE') &&
$this->currentCert['tbsCertList']['issuer'] ===
$ca['tbsCertificate']['subject']:
case defined('FILE_X509_IGNORE_TYPE')
&& $this->getDN(self::DN_STRING,
$this->currentCert['tbsCertList']['issuer']) ===
$this->getDN(self::DN_STRING,
$ca['tbsCertificate']['subject']):
$authorityKey =
$this->getExtension('id-ce-authorityKeyIdentifier');
$subjectKeyID =
$this->getExtension('id-ce-subjectKeyIdentifier', $ca);
switch (true) {
case !is_array($authorityKey):
case !$subjectKeyID:
case
isset($authorityKey['keyIdentifier']) &&
$authorityKey['keyIdentifier'] === $subjectKeyID:
if (is_array($authorityKey)
&& isset($authorityKey['authorityCertSerialNumber'])
&&
!$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber']))
{
break 2; // serial mismatch -
check other ca
}
$signingCert = $ca; // working cert
break 3;
}
}
}
}
if (!isset($signingCert)) {
return false;
}
return $this->_validateSignature(
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'],
substr(base64_decode($this->currentCert['signature']), 1),
$this->signatureSubject
);
default:
return false;
}
}
/**
* Validates a signature
*
* Returns true if the signature is verified, false if it is not
correct or null on error
*
* @param string $publicKeyAlgorithm
* @param string $publicKey
* @param string $signatureAlgorithm
* @param string $signature
* @param string $signatureSubject
* @access private
* @return int
*/
function _validateSignature($publicKeyAlgorithm, $publicKey,
$signatureAlgorithm, $signature, $signatureSubject)
{
switch ($publicKeyAlgorithm) {
case 'rsaEncryption':
$rsa = new RSA();
$rsa->loadKey($publicKey);
switch ($signatureAlgorithm) {
case 'md2WithRSAEncryption':
case 'md5WithRSAEncryption':
case 'sha1WithRSAEncryption':
case 'sha224WithRSAEncryption':
case 'sha256WithRSAEncryption':
case 'sha384WithRSAEncryption':
case 'sha512WithRSAEncryption':
$rsa->setHash(preg_replace('#WithRSAEncryption$#',
'', $signatureAlgorithm));
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
if (!@$rsa->verify($signatureSubject,
$signature)) {
return false;
}
break;
default:
return null;
}
break;
default:
return null;
}
return true;
}
/**
* Sets the recursion limit
*
* When validating a signature it may be necessary to download
intermediate certs from URI's.
* An intermediate cert that linked to itself would result in an
infinite loop so to prevent
* that we set a recursion limit. A negative number means that there is
no recursion limit.
*
* @param int $count
* @access public
*/
static function setRecurLimit($count)
{
self::$recur_limit = $count;
}
/**
* Prevents URIs from being automatically retrieved
*
* @access public
*/
static function disableURLFetch()
{
self::$disable_url_fetch = true;
}
/**
* Allows URIs to be automatically retrieved
*
* @access public
*/
static function enableURLFetch()
{
self::$disable_url_fetch = false;
}
/**
* Reformat public keys
*
* Reformats a public key to a format supported by phpseclib (if
applicable)
*
* @param string $algorithm
* @param string $key
* @access private
* @return string
*/
function _reformatKey($algorithm, $key)
{
switch ($algorithm) {
case 'rsaEncryption':
return
"-----BEGIN RSA PUBLIC KEY-----\r\n" .
// subjectPublicKey is stored as a bit string in X.509
certs. the first byte of a bit string represents how many bits
// in the last byte should be ignored. the following
only supports non-zero stuff but as none of the X.509 certs Firefox
// uses as a cert authority actually use a non-zero bit
I think it's safe to assume that none do.
chunk_split(base64_encode(substr(base64_decode($key),
1)), 64) .
'-----END RSA PUBLIC KEY-----';
default:
return $key;
}
}
/**
* Decodes an IP address
*
* Takes in a base64 encoded "blob" and returns a human
readable IP address
*
* @param string $ip
* @access private
* @return string
*/
function _decodeIP($ip)
{
return inet_ntop(base64_decode($ip));
}
/**
* Decodes an IP address in a name constraints extension
*
* Takes in a base64 encoded "blob" and returns a human
readable IP address / mask
*
* @param string $ip
* @access private
* @return array
*/
function _decodeNameConstraintIP($ip)
{
$ip = base64_decode($ip);
$size = strlen($ip) >> 1;
$mask = substr($ip, $size);
$ip = substr($ip, 0, $size);
return array(inet_ntop($ip), inet_ntop($mask));
}
/**
* Encodes an IP address
*
* Takes a human readable IP address into a base64-encoded
"blob"
*
* @param string|array $ip
* @access private
* @return string
*/
function _encodeIP($ip)
{
return is_string($ip) ?
base64_encode(inet_pton($ip)) :
base64_encode(inet_pton($ip[0]) . inet_pton($ip[1]));
}
/**
* "Normalizes" a Distinguished Name property
*
* @param string $propName
* @access private
* @return mixed
*/
function _translateDNProp($propName)
{
switch (strtolower($propName)) {
case 'id-at-countryname':
case 'countryname':
case 'c':
return 'id-at-countryName';
case 'id-at-organizationname':
case 'organizationname':
case 'o':
return 'id-at-organizationName';
case 'id-at-dnqualifier':
case 'dnqualifier':
return 'id-at-dnQualifier';
case 'id-at-commonname':
case 'commonname':
case 'cn':
return 'id-at-commonName';
case 'id-at-stateorprovincename':
case 'stateorprovincename':
case 'state':
case 'province':
case 'provincename':
case 'st':
return 'id-at-stateOrProvinceName';
case 'id-at-localityname':
case 'localityname':
case 'l':
return 'id-at-localityName';
case 'id-emailaddress':
case 'emailaddress':
return 'pkcs-9-at-emailAddress';
case 'id-at-serialnumber':
case 'serialnumber':
return 'id-at-serialNumber';
case 'id-at-postalcode':
case 'postalcode':
return 'id-at-postalCode';
case 'id-at-streetaddress':
case 'streetaddress':
return 'id-at-streetAddress';
case 'id-at-name':
case 'name':
return 'id-at-name';
case 'id-at-givenname':
case 'givenname':
return 'id-at-givenName';
case 'id-at-surname':
case 'surname':
case 'sn':
return 'id-at-surname';
case 'id-at-initials':
case 'initials':
return 'id-at-initials';
case 'id-at-generationqualifier':
case 'generationqualifier':
return 'id-at-generationQualifier';
case 'id-at-organizationalunitname':
case 'organizationalunitname':
case 'ou':
return 'id-at-organizationalUnitName';
case 'id-at-pseudonym':
case 'pseudonym':
return 'id-at-pseudonym';
case 'id-at-title':
case 'title':
return 'id-at-title';
case 'id-at-description':
case 'description':
return 'id-at-description';
case 'id-at-role':
case 'role':
return 'id-at-role';
case 'id-at-uniqueidentifier':
case 'uniqueidentifier':
case 'x500uniqueidentifier':
return 'id-at-uniqueIdentifier';
case 'postaladdress':
case 'id-at-postaladdress':
return 'id-at-postalAddress';
default:
return false;
}
}
/**
* Set a Distinguished Name property
*
* @param string $propName
* @param mixed $propValue
* @param string $type optional
* @access public
* @return bool
*/
function setDNProp($propName, $propValue, $type =
'utf8String')
{
if (empty($this->dn)) {
$this->dn = array('rdnSequence' => array());
}
if (($propName = $this->_translateDNProp($propName)) === false)
{
return false;
}
foreach ((array) $propValue as $v) {
if (!is_array($v) && isset($type)) {
$v = array($type => $v);
}
$this->dn['rdnSequence'][] = array(
array(
'type' => $propName,
'value'=> $v
)
);
}
return true;
}
/**
* Remove Distinguished Name properties
*
* @param string $propName
* @access public
*/
function removeDNProp($propName)
{
if (empty($this->dn)) {
return;
}
if (($propName = $this->_translateDNProp($propName)) === false)
{
return;
}
$dn = &$this->dn['rdnSequence'];
$size = count($dn);
for ($i = 0; $i < $size; $i++) {
if ($dn[$i][0]['type'] == $propName) {
unset($dn[$i]);
}
}
$dn = array_values($dn);
// fix for https://bugs.php.net/75433 affecting PHP 7.2
if (!isset($dn[0])) {
$dn = array_splice($dn, 0, 0);
}
}
/**
* Get Distinguished Name properties
*
* @param string $propName
* @param array $dn optional
* @param bool $withType optional
* @return mixed
* @access public
*/
function getDNProp($propName, $dn = null, $withType = false)
{
if (!isset($dn)) {
$dn = $this->dn;
}
if (empty($dn)) {
return false;
}
if (($propName = $this->_translateDNProp($propName)) === false)
{
return false;
}
$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$filters['value'] = array('type' =>
ASN1::TYPE_UTF8_STRING);
$asn1->loadFilters($filters);
$this->_mapOutDNs($dn, 'rdnSequence', $asn1);
$dn = $dn['rdnSequence'];
$result = array();
for ($i = 0; $i < count($dn); $i++) {
if ($dn[$i][0]['type'] == $propName) {
$v = $dn[$i][0]['value'];
if (!$withType) {
if (is_array($v)) {
foreach ($v as $type => $s) {
$type = array_search($type, $asn1->ANYmap,
true);
if ($type !== false &&
isset($asn1->stringTypeSize[$type])) {
$s = $asn1->convert($s, $type);
if ($s !== false) {
$v = $s;
break;
}
}
}
if (is_array($v)) {
$v = array_pop($v); // Always strip data type.
}
} elseif (is_object($v) && $v instanceof
Element) {
$map = $this->_getMapping($propName);
if (!is_bool($map)) {
$decoded = $asn1->decodeBER($v);
$v = $asn1->asn1map($decoded[0], $map);
}
}
}
$result[] = $v;
}
}
return $result;
}
/**
* Set a Distinguished Name
*
* @param mixed $dn
* @param bool $merge optional
* @param string $type optional
* @access public
* @return bool
*/
function setDN($dn, $merge = false, $type = 'utf8String')
{
if (!$merge) {
$this->dn = null;
}
if (is_array($dn)) {
if (isset($dn['rdnSequence'])) {
$this->dn = $dn; // No merge here.
return true;
}
// handles stuff generated by openssl_x509_parse()
foreach ($dn as $prop => $value) {
if (!$this->setDNProp($prop, $value, $type)) {
return false;
}
}
return true;
}
// handles everything else
$results = preg_split('#((?:^|,
*|/)(?:C=|O=|OU=|CN=|L=|ST=|SN=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=|title=|description=|role=|x500UniqueIdentifier=|postalAddress=))#',
$dn, -1, PREG_SPLIT_DELIM_CAPTURE);
for ($i = 1; $i < count($results); $i+=2) {
$prop = trim($results[$i], ', =/');
$value = $results[$i + 1];
if (!$this->setDNProp($prop, $value, $type)) {
return false;
}
}
return true;
}
/**
* Get the Distinguished Name for a certificates subject
*
* @param mixed $format optional
* @param array $dn optional
* @access public
* @return bool
*/
function getDN($format = self::DN_ARRAY, $dn = null)
{
if (!isset($dn)) {
$dn = isset($this->currentCert['tbsCertList']) ?
$this->currentCert['tbsCertList']['issuer'] :
$this->dn;
}
switch ((int) $format) {
case self::DN_ARRAY:
return $dn;
case self::DN_ASN1:
$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$filters['rdnSequence']['value'] =
array('type' => ASN1::TYPE_UTF8_STRING);
$asn1->loadFilters($filters);
$this->_mapOutDNs($dn, 'rdnSequence', $asn1);
return $asn1->encodeDER($dn, $this->Name);
case self::DN_CANON:
// No SEQUENCE around RDNs and all string values
normalized as
// trimmed lowercase UTF-8 with all spacing as one blank.
// constructed RDNs will not be canonicalized
$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$filters['value'] = array('type' =>
ASN1::TYPE_UTF8_STRING);
$asn1->loadFilters($filters);
$result = '';
$this->_mapOutDNs($dn, 'rdnSequence', $asn1);
foreach ($dn['rdnSequence'] as $rdn) {
foreach ($rdn as $i => $attr) {
$attr = &$rdn[$i];
if (is_array($attr['value'])) {
foreach ($attr['value'] as $type
=> $v) {
$type = array_search($type,
$asn1->ANYmap, true);
if ($type !== false &&
isset($asn1->stringTypeSize[$type])) {
$v = $asn1->convert($v, $type);
if ($v !== false) {
$v =
preg_replace('/\s+/', ' ', $v);
$attr['value'] =
strtolower(trim($v));
break;
}
}
}
}
}
$result .= $asn1->encodeDER($rdn,
$this->RelativeDistinguishedName);
}
return $result;
case self::DN_HASH:
$dn = $this->getDN(self::DN_CANON, $dn);
$hash = new Hash('sha1');
$hash = $hash->hash($dn);
extract(unpack('Vhash', $hash));
return strtolower(bin2hex(pack('N', $hash)));
}
// Default is to return a string.
$start = true;
$output = '';
$result = array();
$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$filters['rdnSequence']['value'] =
array('type' => ASN1::TYPE_UTF8_STRING);
$asn1->loadFilters($filters);
$this->_mapOutDNs($dn, 'rdnSequence', $asn1);
foreach ($dn['rdnSequence'] as $field) {
$prop = $field[0]['type'];
$value = $field[0]['value'];
$delim = ', ';
switch ($prop) {
case 'id-at-countryName':
$desc = 'C';
break;
case 'id-at-stateOrProvinceName':
$desc = 'ST';
break;
case 'id-at-organizationName':
$desc = 'O';
break;
case 'id-at-organizationalUnitName':
$desc = 'OU';
break;
case 'id-at-commonName':
$desc = 'CN';
break;
case 'id-at-localityName':
$desc = 'L';
break;
case 'id-at-surname':
$desc = 'SN';
break;
case 'id-at-uniqueIdentifier':
$delim = '/';
$desc = 'x500UniqueIdentifier';
break;
case 'id-at-postalAddress':
$delim = '/';
$desc = 'postalAddress';
break;
default:
$delim = '/';
$desc = preg_replace('#.+-([^-]+)$#',
'$1', $prop);
}
if (!$start) {
$output.= $delim;
}
if (is_array($value)) {
foreach ($value as $type => $v) {
$type = array_search($type, $asn1->ANYmap, true);
if ($type !== false &&
isset($asn1->stringTypeSize[$type])) {
$v = $asn1->convert($v, $type);
if ($v !== false) {
$value = $v;
break;
}
}
}
if (is_array($value)) {
$value = array_pop($value); // Always strip data type.
}
} elseif (is_object($value) && $value instanceof
Element) {
$callback = function ($x) {
return "\x" . bin2hex($x[0]);
};
$value =
strtoupper(preg_replace_callback('#[^\x20-\x7E]#', $callback,
$value->element));
}
$output.= $desc . '=' . $value;
$result[$desc] = isset($result[$desc]) ?
array_merge((array) $result[$desc], array($value)) :
$value;
$start = false;
}
return $format == self::DN_OPENSSL ? $result : $output;
}
/**
* Get the Distinguished Name for a certificate/crl issuer
*
* @param int $format optional
* @access public
* @return mixed
*/
function getIssuerDN($format = self::DN_ARRAY)
{
switch (true) {
case !isset($this->currentCert) ||
!is_array($this->currentCert):
break;
case isset($this->currentCert['tbsCertificate']):
return $this->getDN($format,
$this->currentCert['tbsCertificate']['issuer']);
case isset($this->currentCert['tbsCertList']):
return $this->getDN($format,
$this->currentCert['tbsCertList']['issuer']);
}
return false;
}
/**
* Get the Distinguished Name for a certificate/csr subject
* Alias of getDN()
*
* @param int $format optional
* @access public
* @return mixed
*/
function getSubjectDN($format = self::DN_ARRAY)
{
switch (true) {
case !empty($this->dn):
return $this->getDN($format);
case !isset($this->currentCert) ||
!is_array($this->currentCert):
break;
case isset($this->currentCert['tbsCertificate']):
return $this->getDN($format,
$this->currentCert['tbsCertificate']['subject']);
case
isset($this->currentCert['certificationRequestInfo']):
return $this->getDN($format,
$this->currentCert['certificationRequestInfo']['subject']);
}
return false;
}
/**
* Get an individual Distinguished Name property for a certificate/crl
issuer
*
* @param string $propName
* @param bool $withType optional
* @access public
* @return mixed
*/
function getIssuerDNProp($propName, $withType = false)
{
switch (true) {
case !isset($this->currentCert) ||
!is_array($this->currentCert):
break;
case isset($this->currentCert['tbsCertificate']):
return $this->getDNProp($propName,
$this->currentCert['tbsCertificate']['issuer'],
$withType);
case isset($this->currentCert['tbsCertList']):
return $this->getDNProp($propName,
$this->currentCert['tbsCertList']['issuer'],
$withType);
}
return false;
}
/**
* Get an individual Distinguished Name property for a certificate/csr
subject
*
* @param string $propName
* @param bool $withType optional
* @access public
* @return mixed
*/
function getSubjectDNProp($propName, $withType = false)
{
switch (true) {
case !empty($this->dn):
return $this->getDNProp($propName, null, $withType);
case !isset($this->currentCert) ||
!is_array($this->currentCert):
break;
case isset($this->currentCert['tbsCertificate']):
return $this->getDNProp($propName,
$this->currentCert['tbsCertificate']['subject'],
$withType);
case
isset($this->currentCert['certificationRequestInfo']):
return $this->getDNProp($propName,
$this->currentCert['certificationRequestInfo']['subject'],
$withType);
}
return false;
}
/**
* Get the certificate chain for the current cert
*
* @access public
* @return mixed
*/
function getChain()
{
$chain = array($this->currentCert);
if (!is_array($this->currentCert) ||
!isset($this->currentCert['tbsCertificate'])) {
return false;
}
if (empty($this->CAs)) {
return $chain;
}
while (true) {
$currentCert = $chain[count($chain) - 1];
for ($i = 0; $i < count($this->CAs); $i++) {
$ca = $this->CAs[$i];
if
($currentCert['tbsCertificate']['issuer'] ===
$ca['tbsCertificate']['subject']) {
$authorityKey =
$this->getExtension('id-ce-authorityKeyIdentifier',
$currentCert);
$subjectKeyID =
$this->getExtension('id-ce-subjectKeyIdentifier', $ca);
switch (true) {
case !is_array($authorityKey):
case is_array($authorityKey) &&
isset($authorityKey['keyIdentifier']) &&
$authorityKey['keyIdentifier'] === $subjectKeyID:
if ($currentCert === $ca) {
break 3;
}
$chain[] = $ca;
break 2;
}
}
}
if ($i == count($this->CAs)) {
break;
}
}
foreach ($chain as $key => $value) {
$chain[$key] = new X509();
$chain[$key]->loadX509($value);
}
return $chain;
}
/**
* Set public key
*
* Key needs to be a \phpseclib\Crypt\RSA object
*
* @param object $key
* @access public
* @return bool
*/
function setPublicKey($key)
{
$key->setPublicKey();
$this->publicKey = $key;
}
/**
* Set private key
*
* Key needs to be a \phpseclib\Crypt\RSA object
*
* @param object $key
* @access public
*/
function setPrivateKey($key)
{
$this->privateKey = $key;
}
/**
* Set challenge
*
* Used for SPKAC CSR's
*
* @param string $challenge
* @access public
*/
function setChallenge($challenge)
{
$this->challenge = $challenge;
}
/**
* Gets the public key
*
* Returns a \phpseclib\Crypt\RSA object or a false.
*
* @access public
* @return mixed
*/
function getPublicKey()
{
if (isset($this->publicKey)) {
return $this->publicKey;
}
if (isset($this->currentCert) &&
is_array($this->currentCert)) {
foreach (array('tbsCertificate/subjectPublicKeyInfo',
'certificationRequestInfo/subjectPKInfo') as $path) {
$keyinfo = $this->_subArray($this->currentCert,
$path);
if (!empty($keyinfo)) {
break;
}
}
}
if (empty($keyinfo)) {
return false;
}
$key = $keyinfo['subjectPublicKey'];
switch ($keyinfo['algorithm']['algorithm']) {
case 'rsaEncryption':
$publicKey = new RSA();
$publicKey->loadKey($key);
$publicKey->setPublicKey();
break;
default:
return false;
}
return $publicKey;
}
/**
* Load a Certificate Signing Request
*
* @param string|array $csr
* @param int $mode
* @access public
* @return mixed
*/
function loadCSR($csr, $mode = self::FORMAT_AUTO_DETECT)
{
if (is_array($csr) &&
isset($csr['certificationRequestInfo'])) {
unset($this->currentCert);
unset($this->currentKeyIdentifier);
unset($this->signatureSubject);
$this->dn =
$csr['certificationRequestInfo']['subject'];
if (!isset($this->dn)) {
return false;
}
$this->currentCert = $csr;
return $csr;
}
// see http://tools.ietf.org/html/rfc2986
$asn1 = new ASN1();
if ($mode != self::FORMAT_DER) {
$newcsr = $this->_extractBER($csr);
if ($mode == self::FORMAT_PEM && $csr == $newcsr) {
return false;
}
$csr = $newcsr;
}
$orig = $csr;
if ($csr === false) {
$this->currentCert = false;
return false;
}
$asn1->loadOIDs($this->oids);
$decoded = $asn1->decodeBER($csr);
if (empty($decoded)) {
$this->currentCert = false;
return false;
}
$csr = $asn1->asn1map($decoded[0],
$this->CertificationRequest);
if (!isset($csr) || $csr === false) {
$this->currentCert = false;
return false;
}
$this->_mapInAttributes($csr,
'certificationRequestInfo/attributes', $asn1);
$this->_mapInDNs($csr,
'certificationRequestInfo/subject/rdnSequence', $asn1);
$this->dn =
$csr['certificationRequestInfo']['subject'];
$this->signatureSubject = substr($orig,
$decoded[0]['content'][0]['start'],
$decoded[0]['content'][0]['length']);
$algorithm =
&$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'];
$key =
&$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'];
$key = $this->_reformatKey($algorithm, $key);
switch ($algorithm) {
case 'rsaEncryption':
$this->publicKey = new RSA();
$this->publicKey->loadKey($key);
$this->publicKey->setPublicKey();
break;
default:
$this->publicKey = null;
}
$this->currentKeyIdentifier = null;
$this->currentCert = $csr;
return $csr;
}
/**
* Save CSR request
*
* @param array $csr
* @param int $format optional
* @access public
* @return string
*/
function saveCSR($csr, $format = self::FORMAT_PEM)
{
if (!is_array($csr) ||
!isset($csr['certificationRequestInfo'])) {
return false;
}
switch (true) {
case !($algorithm = $this->_subArray($csr,
'certificationRequestInfo/subjectPKInfo/algorithm/algorithm')):
case
is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']):
break;
default:
switch ($algorithm) {
case 'rsaEncryption':
$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']
= base64_encode("\0" .
base64_decode(preg_replace('#-.+-|[\r\n]#', '',
$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'])));
$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['parameters']
= null;
$csr['signatureAlgorithm']['parameters'] = null;
$csr['certificationRequestInfo']['signature']['parameters']
= null;
}
}
$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$filters['certificationRequestInfo']['subject']['rdnSequence']['value']
= array('type' => ASN1::TYPE_UTF8_STRING);
$asn1->loadFilters($filters);
$this->_mapOutDNs($csr,
'certificationRequestInfo/subject/rdnSequence', $asn1);
$this->_mapOutAttributes($csr,
'certificationRequestInfo/attributes', $asn1);
$csr = $asn1->encodeDER($csr, $this->CertificationRequest);
switch ($format) {
case self::FORMAT_DER:
return $csr;
// case self::FORMAT_PEM:
default:
return "-----BEGIN CERTIFICATE REQUEST-----\r\n"
. chunk_split(base64_encode($csr), 64) . '-----END CERTIFICATE
REQUEST-----';
}
}
/**
* Load a SPKAC CSR
*
* SPKAC's are produced by the HTML5 keygen element:
*
* https://developer.mozilla.org/en-US/docs/HTML/Element/keygen
*
* @param string|array $spkac
* @access public
* @return mixed
*/
function loadSPKAC($spkac)
{
if (is_array($spkac) &&
isset($spkac['publicKeyAndChallenge'])) {
unset($this->currentCert);
unset($this->currentKeyIdentifier);
unset($this->signatureSubject);
$this->currentCert = $spkac;
return $spkac;
}
// see
http://www.w3.org/html/wg/drafts/html/master/forms.html#signedpublickeyandchallenge
$asn1 = new ASN1();
// OpenSSL produces SPKAC's that are preceded by the string
SPKAC=
$temp = preg_replace('#(?:SPKAC=)|[ \r\n\\\]#',
'', $spkac);
$temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ?
base64_decode($temp) : false;
if ($temp != false) {
$spkac = $temp;
}
$orig = $spkac;
if ($spkac === false) {
$this->currentCert = false;
return false;
}
$asn1->loadOIDs($this->oids);
$decoded = $asn1->decodeBER($spkac);
if (empty($decoded)) {
$this->currentCert = false;
return false;
}
$spkac = $asn1->asn1map($decoded[0],
$this->SignedPublicKeyAndChallenge);
if (!isset($spkac) || $spkac === false) {
$this->currentCert = false;
return false;
}
$this->signatureSubject = substr($orig,
$decoded[0]['content'][0]['start'],
$decoded[0]['content'][0]['length']);
$algorithm =
&$spkac['publicKeyAndChallenge']['spki']['algorithm']['algorithm'];
$key =
&$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'];
$key = $this->_reformatKey($algorithm, $key);
switch ($algorithm) {
case 'rsaEncryption':
$this->publicKey = new RSA();
$this->publicKey->loadKey($key);
$this->publicKey->setPublicKey();
break;
default:
$this->publicKey = null;
}
$this->currentKeyIdentifier = null;
$this->currentCert = $spkac;
return $spkac;
}
/**
* Save a SPKAC CSR request
*
* @param string|array $spkac
* @param int $format optional
* @access public
* @return string
*/
function saveSPKAC($spkac, $format = self::FORMAT_PEM)
{
if (!is_array($spkac) ||
!isset($spkac['publicKeyAndChallenge'])) {
return false;
}
$algorithm = $this->_subArray($spkac,
'publicKeyAndChallenge/spki/algorithm/algorithm');
switch (true) {
case !$algorithm:
case
is_object($spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']):
break;
default:
switch ($algorithm) {
case 'rsaEncryption':
$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']
= base64_encode("\0" .
base64_decode(preg_replace('#-.+-|[\r\n]#', '',
$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'])));
}
}
$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$spkac = $asn1->encodeDER($spkac,
$this->SignedPublicKeyAndChallenge);
switch ($format) {
case self::FORMAT_DER:
return $spkac;
// case self::FORMAT_PEM:
default:
// OpenSSL's implementation of SPKAC requires the
SPKAC be preceded by SPKAC= and since there are pretty much
// no other SPKAC decoders phpseclib will use that same
format
return 'SPKAC=' . base64_encode($spkac);
}
}
/**
* Load a Certificate Revocation List
*
* @param string $crl
* @param int $mode
* @access public
* @return mixed
*/
function loadCRL($crl, $mode = self::FORMAT_AUTO_DETECT)
{
if (is_array($crl) && isset($crl['tbsCertList']))
{
$this->currentCert = $crl;
unset($this->signatureSubject);
return $crl;
}
$asn1 = new ASN1();
if ($mode != self::FORMAT_DER) {
$newcrl = $this->_extractBER($crl);
if ($mode == self::FORMAT_PEM && $crl == $newcrl) {
return false;
}
$crl = $newcrl;
}
$orig = $crl;
if ($crl === false) {
$this->currentCert = false;
return false;
}
$asn1->loadOIDs($this->oids);
$decoded = $asn1->decodeBER($crl);
if (empty($decoded)) {
$this->currentCert = false;
return false;
}
$crl = $asn1->asn1map($decoded[0], $this->CertificateList);
if (!isset($crl) || $crl === false) {
$this->currentCert = false;
return false;
}
$this->signatureSubject = substr($orig,
$decoded[0]['content'][0]['start'],
$decoded[0]['content'][0]['length']);
$this->_mapInDNs($crl,
'tbsCertList/issuer/rdnSequence', $asn1);
if ($this->_isSubArrayValid($crl,
'tbsCertList/crlExtensions')) {
$this->_mapInExtensions($crl,
'tbsCertList/crlExtensions', $asn1);
}
if ($this->_isSubArrayValid($crl,
'tbsCertList/revokedCertificates')) {
$rclist_ref = &$this->_subArrayUnchecked($crl,
'tbsCertList/revokedCertificates');
if ($rclist_ref) {
$rclist =
$crl['tbsCertList']['revokedCertificates'];
foreach ($rclist as $i => $extension) {
if ($this->_isSubArrayValid($rclist,
"$i/crlEntryExtensions", $asn1)) {
$this->_mapInExtensions($rclist_ref,
"$i/crlEntryExtensions", $asn1);
}
}
}
}
$this->currentKeyIdentifier = null;
$this->currentCert = $crl;
return $crl;
}
/**
* Save Certificate Revocation List.
*
* @param array $crl
* @param int $format optional
* @access public
* @return string
*/
function saveCRL($crl, $format = self::FORMAT_PEM)
{
if (!is_array($crl) || !isset($crl['tbsCertList'])) {
return false;
}
$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$filters['tbsCertList']['issuer']['rdnSequence']['value']
= array('type' => ASN1::TYPE_UTF8_STRING);
$filters['tbsCertList']['signature']['parameters']
= array('type' => ASN1::TYPE_UTF8_STRING);
$filters['signatureAlgorithm']['parameters']
= array('type' => ASN1::TYPE_UTF8_STRING);
if
(empty($crl['tbsCertList']['signature']['parameters']))
{
$filters['tbsCertList']['signature']['parameters']
= array('type' => ASN1::TYPE_NULL);
}
if
(empty($crl['signatureAlgorithm']['parameters'])) {
$filters['signatureAlgorithm']['parameters']
= array('type' => ASN1::TYPE_NULL);
}
$asn1->loadFilters($filters);
$this->_mapOutDNs($crl,
'tbsCertList/issuer/rdnSequence', $asn1);
$this->_mapOutExtensions($crl,
'tbsCertList/crlExtensions', $asn1);
$rclist = &$this->_subArray($crl,
'tbsCertList/revokedCertificates');
if (is_array($rclist)) {
foreach ($rclist as $i => $extension) {
$this->_mapOutExtensions($rclist,
"$i/crlEntryExtensions", $asn1);
}
}
$crl = $asn1->encodeDER($crl, $this->CertificateList);
switch ($format) {
case self::FORMAT_DER:
return $crl;
// case self::FORMAT_PEM:
default:
return "-----BEGIN X509 CRL-----\r\n" .
chunk_split(base64_encode($crl), 64) . '-----END X509 CRL-----';
}
}
/**
* Helper function to build a time field according to RFC 3280 section
* - 4.1.2.5 Validity
* - 5.1.2.4 This Update
* - 5.1.2.5 Next Update
* - 5.1.2.6 Revoked Certificates
* by choosing utcTime iff year of date given is before 2050 and
generalTime else.
*
* @param string $date in format date('D, d M Y H:i:s O')
* @access private
* @return array
*/
function _timeField($date)
{
if ($date instanceof Element) {
return $date;
}
$dateObj = new DateTime($date, new DateTimeZone('GMT'));
$year = $dateObj->format('Y'); // the same way
ASN1.php parses this
if ($year < 2050) {
return array('utcTime' => $date);
} else {
return array('generalTime' => $date);
}
}
/**
* Sign an X.509 certificate
*
* $issuer's private key needs to be loaded.
* $subject can be either an existing X.509 cert (if you want to resign
it),
* a CSR or something with the DN and public key explicitly set.
*
* @param \phpseclib\File\X509 $issuer
* @param \phpseclib\File\X509 $subject
* @param string $signatureAlgorithm optional
* @access public
* @return mixed
*/
function sign($issuer, $subject, $signatureAlgorithm =
'sha1WithRSAEncryption')
{
if (!is_object($issuer->privateKey) || empty($issuer->dn)) {
return false;
}
if (isset($subject->publicKey) && !($subjectPublicKey =
$subject->_formatSubjectPublicKey())) {
return false;
}
$currentCert = isset($this->currentCert) ? $this->currentCert
: null;
$signatureSubject = isset($this->signatureSubject) ?
$this->signatureSubject: null;
if (isset($subject->currentCert) &&
is_array($subject->currentCert) &&
isset($subject->currentCert['tbsCertificate'])) {
$this->currentCert = $subject->currentCert;
$this->currentCert['tbsCertificate']['signature']['algorithm']
= $signatureAlgorithm;
$this->currentCert['signatureAlgorithm']['algorithm']
= $signatureAlgorithm;
if (!empty($this->startDate)) {
$this->currentCert['tbsCertificate']['validity']['notBefore']
= $this->_timeField($this->startDate);
}
if (!empty($this->endDate)) {
$this->currentCert['tbsCertificate']['validity']['notAfter']
= $this->_timeField($this->endDate);
}
if (!empty($this->serialNumber)) {
$this->currentCert['tbsCertificate']['serialNumber']
= $this->serialNumber;
}
if (!empty($subject->dn)) {
$this->currentCert['tbsCertificate']['subject'] =
$subject->dn;
}
if (!empty($subject->publicKey)) {
$this->currentCert['tbsCertificate']['subjectPublicKeyInfo']
= $subjectPublicKey;
}
$this->removeExtension('id-ce-authorityKeyIdentifier');
if (isset($subject->domains)) {
$this->removeExtension('id-ce-subjectAltName');
}
} elseif (isset($subject->currentCert) &&
is_array($subject->currentCert) &&
isset($subject->currentCert['tbsCertList'])) {
return false;
} else {
if (!isset($subject->publicKey)) {
return false;
}
$startDate = new DateTime('now', new
DateTimeZone(@date_default_timezone_get()));
$startDate = !empty($this->startDate) ? $this->startDate
: $startDate->format('D, d M Y H:i:s O');
$endDate = new DateTime('+1 year', new
DateTimeZone(@date_default_timezone_get()));
$endDate = !empty($this->endDate) ? $this->endDate :
$endDate->format('D, d M Y H:i:s O');
/* "The serial number MUST be a positive integer"
"Conforming CAs MUST NOT use serialNumber values longer
than 20 octets."
-- https://tools.ietf.org/html/rfc5280#section-4.1.2.2
for the integer to be positive the leading bit needs to be 0
hence the
application of a bitmap
*/
$serialNumber = !empty($this->serialNumber) ?
$this->serialNumber :
new BigInteger(Random::string(20) & ("\x7F" .
str_repeat("\xFF", 19)), 256);
$this->currentCert = array(
'tbsCertificate' =>
array(
'version' => 'v3',
'serialNumber' => $serialNumber, //
$this->setSerialNumber()
'signature' =>
array('algorithm' => $signatureAlgorithm),
'issuer' => false, // this is going to
be overwritten later
'validity' => array(
'notBefore' =>
$this->_timeField($startDate), // $this->setStartDate()
'notAfter' =>
$this->_timeField($endDate) // $this->setEndDate()
),
'subject' => $subject->dn,
'subjectPublicKeyInfo' =>
$subjectPublicKey
),
'signatureAlgorithm' =>
array('algorithm' => $signatureAlgorithm),
'signature' => false // this is
going to be overwritten later
);
// Copy extensions from CSR.
$csrexts =
$subject->getAttribute('pkcs-9-at-extensionRequest', 0);
if (!empty($csrexts)) {
$this->currentCert['tbsCertificate']['extensions'] =
$csrexts;
}
}
$this->currentCert['tbsCertificate']['issuer'] =
$issuer->dn;
if (isset($issuer->currentKeyIdentifier)) {
$this->setExtension('id-ce-authorityKeyIdentifier', array(
//'authorityCertIssuer' => array(
// array(
// 'directoryName' =>
$issuer->dn
// )
//),
'keyIdentifier' =>
$issuer->currentKeyIdentifier
));
//$extensions =
&$this->currentCert['tbsCertificate']['extensions'];
//if (isset($issuer->serialNumber)) {
// $extensions[count($extensions) -
1]['authorityCertSerialNumber'] = $issuer->serialNumber;
//}
//unset($extensions);
}
if (isset($subject->currentKeyIdentifier)) {
$this->setExtension('id-ce-subjectKeyIdentifier',
$subject->currentKeyIdentifier);
}
$altName = array();
if (isset($subject->domains) &&
count($subject->domains)) {
$altName = array_map(array('\phpseclib\File\X509',
'_dnsName'), $subject->domains);
}
if (isset($subject->ipAddresses) &&
count($subject->ipAddresses)) {
// should an IP address appear as the CN if no domain name is
specified? idk
//$ips = count($subject->domains) ? $subject->ipAddresses
: array_slice($subject->ipAddresses, 1);
$ipAddresses = array();
foreach ($subject->ipAddresses as $ipAddress) {
$encoded = $subject->_ipAddress($ipAddress);
if ($encoded !== false) {
$ipAddresses[] = $encoded;
}
}
if (count($ipAddresses)) {
$altName = array_merge($altName, $ipAddresses);
}
}
if (!empty($altName)) {
$this->setExtension('id-ce-subjectAltName',
$altName);
}
if ($this->caFlag) {
$keyUsage = $this->getExtension('id-ce-keyUsage');
if (!$keyUsage) {
$keyUsage = array();
}
$this->setExtension(
'id-ce-keyUsage',
array_values(array_unique(array_merge($keyUsage,
array('cRLSign', 'keyCertSign'))))
);
$basicConstraints =
$this->getExtension('id-ce-basicConstraints');
if (!$basicConstraints) {
$basicConstraints = array();
}
$this->setExtension(
'id-ce-basicConstraints',
array_unique(array_merge(array('cA' => true),
$basicConstraints)),
true
);
if (!isset($subject->currentKeyIdentifier)) {
$this->setExtension('id-ce-subjectKeyIdentifier',
base64_encode($this->computeKeyIdentifier($this->currentCert)),
false, false);
}
}
// resync $this->signatureSubject
// save $tbsCertificate in case there are any
\phpseclib\File\ASN1\Element objects in it
$tbsCertificate =
$this->currentCert['tbsCertificate'];
$this->loadX509($this->saveX509($this->currentCert));
$result = $this->_sign($issuer->privateKey,
$signatureAlgorithm);
$result['tbsCertificate'] = $tbsCertificate;
$this->currentCert = $currentCert;
$this->signatureSubject = $signatureSubject;
return $result;
}
/**
* Sign a CSR
*
* @access public
* @return mixed
*/
function signCSR($signatureAlgorithm =
'sha1WithRSAEncryption')
{
if (!is_object($this->privateKey) || empty($this->dn)) {
return false;
}
$origPublicKey = $this->publicKey;
$class = get_class($this->privateKey);
$this->publicKey = new $class();
$this->publicKey->loadKey($this->privateKey->getPublicKey());
$this->publicKey->setPublicKey();
if (!($publicKey = $this->_formatSubjectPublicKey())) {
return false;
}
$this->publicKey = $origPublicKey;
$currentCert = isset($this->currentCert) ? $this->currentCert
: null;
$signatureSubject = isset($this->signatureSubject) ?
$this->signatureSubject: null;
if (isset($this->currentCert) &&
is_array($this->currentCert) &&
isset($this->currentCert['certificationRequestInfo'])) {
$this->currentCert['signatureAlgorithm']['algorithm']
= $signatureAlgorithm;
if (!empty($this->dn)) {
$this->currentCert['certificationRequestInfo']['subject']
= $this->dn;
}
$this->currentCert['certificationRequestInfo']['subjectPKInfo']
= $publicKey;
} else {
$this->currentCert = array(
'certificationRequestInfo' =>
array(
'version' => 'v1',
'subject' => $this->dn,
'subjectPKInfo' => $publicKey
),
'signatureAlgorithm' =>
array('algorithm' => $signatureAlgorithm),
'signature' => false // this is
going to be overwritten later
);
}
// resync $this->signatureSubject
// save $certificationRequestInfo in case there are any
\phpseclib\File\ASN1\Element objects in it
$certificationRequestInfo =
$this->currentCert['certificationRequestInfo'];
$this->loadCSR($this->saveCSR($this->currentCert));
$result = $this->_sign($this->privateKey,
$signatureAlgorithm);
$result['certificationRequestInfo'] =
$certificationRequestInfo;
$this->currentCert = $currentCert;
$this->signatureSubject = $signatureSubject;
return $result;
}
/**
* Sign a SPKAC
*
* @access public
* @return mixed
*/
function signSPKAC($signatureAlgorithm =
'sha1WithRSAEncryption')
{
if (!is_object($this->privateKey)) {
return false;
}
$origPublicKey = $this->publicKey;
$class = get_class($this->privateKey);
$this->publicKey = new $class();
$this->publicKey->loadKey($this->privateKey->getPublicKey());
$this->publicKey->setPublicKey();
$publicKey = $this->_formatSubjectPublicKey();
if (!$publicKey) {
return false;
}
$this->publicKey = $origPublicKey;
$currentCert = isset($this->currentCert) ? $this->currentCert
: null;
$signatureSubject = isset($this->signatureSubject) ?
$this->signatureSubject: null;
// re-signing a SPKAC seems silly but since everything else
supports re-signing why not?
if (isset($this->currentCert) &&
is_array($this->currentCert) &&
isset($this->currentCert['publicKeyAndChallenge'])) {
$this->currentCert['signatureAlgorithm']['algorithm']
= $signatureAlgorithm;
$this->currentCert['publicKeyAndChallenge']['spki']
= $publicKey;
if (!empty($this->challenge)) {
// the bitwise AND ensures that the output is a valid
IA5String
$this->currentCert['publicKeyAndChallenge']['challenge']
= $this->challenge & str_repeat("\x7F",
strlen($this->challenge));
}
} else {
$this->currentCert = array(
'publicKeyAndChallenge' =>
array(
'spki' => $publicKey,
// quoting
<https://developer.mozilla.org/en-US/docs/Web/HTML/Element/keygen>,
// "A challenge string that is submitted along
with the public key. Defaults to an empty string if not specified."
// both Firefox and OpenSSL ("openssl spkac
-key private.key") behave this way
// we could alternatively do this instead if we
ignored the specs:
// Random::string(8) &
str_repeat("\x7F", 8)
'challenge' =>
!empty($this->challenge) ? $this->challenge : ''
),
'signatureAlgorithm' =>
array('algorithm' => $signatureAlgorithm),
'signature' => false // this is
going to be overwritten later
);
}
// resync $this->signatureSubject
// save $publicKeyAndChallenge in case there are any
\phpseclib\File\ASN1\Element objects in it
$publicKeyAndChallenge =
$this->currentCert['publicKeyAndChallenge'];
$this->loadSPKAC($this->saveSPKAC($this->currentCert));
$result = $this->_sign($this->privateKey,
$signatureAlgorithm);
$result['publicKeyAndChallenge'] =
$publicKeyAndChallenge;
$this->currentCert = $currentCert;
$this->signatureSubject = $signatureSubject;
return $result;
}
/**
* Sign a CRL
*
* $issuer's private key needs to be loaded.
*
* @param \phpseclib\File\X509 $issuer
* @param \phpseclib\File\X509 $crl
* @param string $signatureAlgorithm optional
* @access public
* @return mixed
*/
function signCRL($issuer, $crl, $signatureAlgorithm =
'sha1WithRSAEncryption')
{
if (!is_object($issuer->privateKey) || empty($issuer->dn)) {
return false;
}
$currentCert = isset($this->currentCert) ? $this->currentCert
: null;
$signatureSubject = isset($this->signatureSubject) ?
$this->signatureSubject : null;
$thisUpdate = new DateTime('now', new
DateTimeZone(@date_default_timezone_get()));
$thisUpdate = !empty($this->startDate) ? $this->startDate :
$thisUpdate->format('D, d M Y H:i:s O');
if (isset($crl->currentCert) &&
is_array($crl->currentCert) &&
isset($crl->currentCert['tbsCertList'])) {
$this->currentCert = $crl->currentCert;
$this->currentCert['tbsCertList']['signature']['algorithm']
= $signatureAlgorithm;
$this->currentCert['signatureAlgorithm']['algorithm']
= $signatureAlgorithm;
} else {
$this->currentCert = array(
'tbsCertList' =>
array(
'version' => 'v2',
'signature' =>
array('algorithm' => $signatureAlgorithm),
'issuer' => false, // this is going to
be overwritten later
'thisUpdate' =>
$this->_timeField($thisUpdate) // $this->setStartDate()
),
'signatureAlgorithm' =>
array('algorithm' => $signatureAlgorithm),
'signature' => false // this is
going to be overwritten later
);
}
$tbsCertList = &$this->currentCert['tbsCertList'];
$tbsCertList['issuer'] = $issuer->dn;
$tbsCertList['thisUpdate'] =
$this->_timeField($thisUpdate);
if (!empty($this->endDate)) {
$tbsCertList['nextUpdate'] =
$this->_timeField($this->endDate); // $this->setEndDate()
} else {
unset($tbsCertList['nextUpdate']);
}
if (!empty($this->serialNumber)) {
$crlNumber = $this->serialNumber;
} else {
$crlNumber =
$this->getExtension('id-ce-cRLNumber');
// "The CRL number is a non-critical CRL extension that
conveys a
// monotonically increasing sequence number for a given CRL
scope and
// CRL issuer. This extension allows users to easily
determine when a
// particular CRL supersedes another CRL."
// -- https://tools.ietf.org/html/rfc5280#section-5.2.3
$crlNumber = $crlNumber !== false ? $crlNumber->add(new
BigInteger(1)) : null;
}
$this->removeExtension('id-ce-authorityKeyIdentifier');
$this->removeExtension('id-ce-issuerAltName');
// Be sure version >= v2 if some extension found.
$version = isset($tbsCertList['version']) ?
$tbsCertList['version'] : 0;
if (!$version) {
if (!empty($tbsCertList['crlExtensions'])) {
$version = 1; // v2.
} elseif
(!empty($tbsCertList['revokedCertificates'])) {
foreach ($tbsCertList['revokedCertificates'] as
$cert) {
if (!empty($cert['crlEntryExtensions'])) {
$version = 1; // v2.
}
}
}
if ($version) {
$tbsCertList['version'] = $version;
}
}
// Store additional extensions.
if (!empty($tbsCertList['version'])) { // At least v2.
if (!empty($crlNumber)) {
$this->setExtension('id-ce-cRLNumber',
$crlNumber);
}
if (isset($issuer->currentKeyIdentifier)) {
$this->setExtension('id-ce-authorityKeyIdentifier', array(
//'authorityCertIssuer' => array(
// array(
// 'directoryName' =>
$issuer->dn
// )
//),
'keyIdentifier' =>
$issuer->currentKeyIdentifier
));
//$extensions =
&$tbsCertList['crlExtensions'];
//if (isset($issuer->serialNumber)) {
// $extensions[count($extensions) -
1]['authorityCertSerialNumber'] = $issuer->serialNumber;
//}
//unset($extensions);
}
$issuerAltName =
$this->getExtension('id-ce-subjectAltName',
$issuer->currentCert);
if ($issuerAltName !== false) {
$this->setExtension('id-ce-issuerAltName',
$issuerAltName);
}
}
if (empty($tbsCertList['revokedCertificates'])) {
unset($tbsCertList['revokedCertificates']);
}
unset($tbsCertList);
// resync $this->signatureSubject
// save $tbsCertList in case there are any
\phpseclib\File\ASN1\Element objects in it
$tbsCertList = $this->currentCert['tbsCertList'];
$this->loadCRL($this->saveCRL($this->currentCert));
$result = $this->_sign($issuer->privateKey,
$signatureAlgorithm);
$result['tbsCertList'] = $tbsCertList;
$this->currentCert = $currentCert;
$this->signatureSubject = $signatureSubject;
return $result;
}
/**
* X.509 certificate signing helper function.
*
* @param \phpseclib\File\X509 $key
* @param string $signatureAlgorithm
* @access public
* @return mixed
*/
function _sign($key, $signatureAlgorithm)
{
if ($key instanceof RSA) {
switch ($signatureAlgorithm) {
case 'md2WithRSAEncryption':
case 'md5WithRSAEncryption':
case 'sha1WithRSAEncryption':
case 'sha224WithRSAEncryption':
case 'sha256WithRSAEncryption':
case 'sha384WithRSAEncryption':
case 'sha512WithRSAEncryption':
$key->setHash(preg_replace('#WithRSAEncryption$#',
'', $signatureAlgorithm));
$key->setSignatureMode(RSA::SIGNATURE_PKCS1);
$this->currentCert['signature'] =
base64_encode("\0" . $key->sign($this->signatureSubject));
return $this->currentCert;
}
}
return false;
}
/**
* Set certificate start date
*
* @param string $date
* @access public
*/
function setStartDate($date)
{
if (!is_object($date) || !is_a($date, 'DateTime')) {
$date = new DateTime($date, new
DateTimeZone(@date_default_timezone_get()));
}
$this->startDate = $date->format('D, d M Y H:i:s
O');
}
/**
* Set certificate end date
*
* @param string $date
* @access public
*/
function setEndDate($date)
{
/*
To indicate that a certificate has no well-defined expiration
date,
the notAfter SHOULD be assigned the GeneralizedTime value of
99991231235959Z.
-- http://tools.ietf.org/html/rfc5280#section-4.1.2.5
*/
if (strtolower($date) == 'lifetime') {
$temp = '99991231235959Z';
$asn1 = new ASN1();
$temp = chr(ASN1::TYPE_GENERALIZED_TIME) .
$asn1->_encodeLength(strlen($temp)) . $temp;
$this->endDate = new Element($temp);
} else {
if (!is_object($date) || !is_a($date, 'DateTime')) {
$date = new DateTime($date, new
DateTimeZone(@date_default_timezone_get()));
}
$this->endDate = $date->format('D, d M Y H:i:s
O');
}
}
/**
* Set Serial Number
*
* @param string $serial
* @param int $base optional
* @access public
*/
function setSerialNumber($serial, $base = -256)
{
$this->serialNumber = new BigInteger($serial, $base);
}
/**
* Turns the certificate into a certificate authority
*
* @access public
*/
function makeCA()
{
$this->caFlag = true;
}
/**
* Check for validity of subarray
*
* This is intended for use in conjunction with _subArrayUnchecked(),
* implementing the checks included in _subArray() but without copying
* a potentially large array by passing its reference by-value to
is_array().
*
* @param array $root
* @param string $path
* @return boolean
* @access private
*/
function _isSubArrayValid($root, $path)
{
if (!is_array($root)) {
return false;
}
foreach (explode('/', $path) as $i) {
if (!is_array($root)) {
return false;
}
if (!isset($root[$i])) {
return true;
}
$root = $root[$i];
}
return true;
}
/**
* Get a reference to a subarray
*
* This variant of _subArray() does no is_array() checking,
* so $root should be checked with _isSubArrayValid() first.
*
* This is here for performance reasons:
* Passing a reference (i.e. $root) by-value (i.e. to is_array())
* creates a copy. If $root is an especially large array, this is
expensive.
*
* @param array $root
* @param string $path absolute path with / as component separator
* @param bool $create optional
* @access private
* @return array|false
*/
function &_subArrayUnchecked(&$root, $path, $create = false)
{
$false = false;
foreach (explode('/', $path) as $i) {
if (!isset($root[$i])) {
if (!$create) {
return $false;
}
$root[$i] = array();
}
$root = &$root[$i];
}
return $root;
}
/**
* Get a reference to a subarray
*
* @param array $root
* @param string $path absolute path with / as component separator
* @param bool $create optional
* @access private
* @return array|false
*/
function &_subArray(&$root, $path, $create = false)
{
$false = false;
if (!is_array($root)) {
return $false;
}
foreach (explode('/', $path) as $i) {
if (!is_array($root)) {
return $false;
}
if (!isset($root[$i])) {
if (!$create) {
return $false;
}
$root[$i] = array();
}
$root = &$root[$i];
}
return $root;
}
/**
* Get a reference to an extension subarray
*
* @param array $root
* @param string $path optional absolute path with / as component
separator
* @param bool $create optional
* @access private
* @return array|false
*/
function &_extensions(&$root, $path = null, $create = false)
{
if (!isset($root)) {
$root = $this->currentCert;
}
switch (true) {
case !empty($path):
case !is_array($root):
break;
case isset($root['tbsCertificate']):
$path = 'tbsCertificate/extensions';
break;
case isset($root['tbsCertList']):
$path = 'tbsCertList/crlExtensions';
break;
case isset($root['certificationRequestInfo']):
$pth = 'certificationRequestInfo/attributes';
$attributes = &$this->_subArray($root, $pth,
$create);
if (is_array($attributes)) {
foreach ($attributes as $key => $value) {
if ($value['type'] ==
'pkcs-9-at-extensionRequest') {
$path = "$pth/$key/value/0";
break 2;
}
}
if ($create) {
$key = count($attributes);
$attributes[] = array('type' =>
'pkcs-9-at-extensionRequest', 'value' => array());
$path = "$pth/$key/value/0";
}
}
break;
}
$extensions = &$this->_subArray($root, $path, $create);
if (!is_array($extensions)) {
$false = false;
return $false;
}
return $extensions;
}
/**
* Remove an Extension
*
* @param string $id
* @param string $path optional
* @access private
* @return bool
*/
function _removeExtension($id, $path = null)
{
$extensions = &$this->_extensions($this->currentCert,
$path);
if (!is_array($extensions)) {
return false;
}
$result = false;
foreach ($extensions as $key => $value) {
if ($value['extnId'] == $id) {
unset($extensions[$key]);
$result = true;
}
}
$extensions = array_values($extensions);
// fix for https://bugs.php.net/75433 affecting PHP 7.2
if (!isset($extensions[0])) {
$extensions = array_splice($extensions, 0, 0);
}
return $result;
}
/**
* Get an Extension
*
* Returns the extension if it exists and false if not
*
* @param string $id
* @param array $cert optional
* @param string $path optional
* @access private
* @return mixed
*/
function _getExtension($id, $cert = null, $path = null)
{
$extensions = $this->_extensions($cert, $path);
if (!is_array($extensions)) {
return false;
}
foreach ($extensions as $key => $value) {
if ($value['extnId'] == $id) {
return $value['extnValue'];
}
}
return false;
}
/**
* Returns a list of all extensions in use
*
* @param array $cert optional
* @param string $path optional
* @access private
* @return array
*/
function _getExtensions($cert = null, $path = null)
{
$exts = $this->_extensions($cert, $path);
$extensions = array();
if (is_array($exts)) {
foreach ($exts as $extension) {
$extensions[] = $extension['extnId'];
}
}
return $extensions;
}
/**
* Set an Extension
*
* @param string $id
* @param mixed $value
* @param bool $critical optional
* @param bool $replace optional
* @param string $path optional
* @access private
* @return bool
*/
function _setExtension($id, $value, $critical = false, $replace = true,
$path = null)
{
$extensions = &$this->_extensions($this->currentCert,
$path, true);
if (!is_array($extensions)) {
return false;
}
$newext = array('extnId' => $id, 'critical'
=> $critical, 'extnValue' => $value);
foreach ($extensions as $key => $value) {
if ($value['extnId'] == $id) {
if (!$replace) {
return false;
}
$extensions[$key] = $newext;
return true;
}
}
$extensions[] = $newext;
return true;
}
/**
* Remove a certificate, CSR or CRL Extension
*
* @param string $id
* @access public
* @return bool
*/
function removeExtension($id)
{
return $this->_removeExtension($id);
}
/**
* Get a certificate, CSR or CRL Extension
*
* Returns the extension if it exists and false if not
*
* @param string $id
* @param array $cert optional
* @access public
* @return mixed
*/
function getExtension($id, $cert = null)
{
return $this->_getExtension($id, $cert);
}
/**
* Returns a list of all extensions in use in certificate, CSR or CRL
*
* @param array $cert optional
* @access public
* @return array
*/
function getExtensions($cert = null)
{
return $this->_getExtensions($cert);
}
/**
* Set a certificate, CSR or CRL Extension
*
* @param string $id
* @param mixed $value
* @param bool $critical optional
* @param bool $replace optional
* @access public
* @return bool
*/
function setExtension($id, $value, $critical = false, $replace = true)
{
return $this->_setExtension($id, $value, $critical, $replace);
}
/**
* Remove a CSR attribute.
*
* @param string $id
* @param int $disposition optional
* @access public
* @return bool
*/
function removeAttribute($id, $disposition = self::ATTR_ALL)
{
$attributes = &$this->_subArray($this->currentCert,
'certificationRequestInfo/attributes');
if (!is_array($attributes)) {
return false;
}
$result = false;
foreach ($attributes as $key => $attribute) {
if ($attribute['type'] == $id) {
$n = count($attribute['value']);
switch (true) {
case $disposition == self::ATTR_APPEND:
case $disposition == self::ATTR_REPLACE:
return false;
case $disposition >= $n:
$disposition -= $n;
break;
case $disposition == self::ATTR_ALL:
case $n == 1:
unset($attributes[$key]);
$result = true;
break;
default:
unset($attributes[$key]['value'][$disposition]);
$attributes[$key]['value'] =
array_values($attributes[$key]['value']);
$result = true;
break;
}
if ($result && $disposition != self::ATTR_ALL) {
break;
}
}
}
$attributes = array_values($attributes);
return $result;
}
/**
* Get a CSR attribute
*
* Returns the attribute if it exists and false if not
*
* @param string $id
* @param int $disposition optional
* @param array $csr optional
* @access public
* @return mixed
*/
function getAttribute($id, $disposition = self::ATTR_ALL, $csr = null)
{
if (empty($csr)) {
$csr = $this->currentCert;
}
$attributes = $this->_subArray($csr,
'certificationRequestInfo/attributes');
if (!is_array($attributes)) {
return false;
}
foreach ($attributes as $key => $attribute) {
if ($attribute['type'] == $id) {
$n = count($attribute['value']);
switch (true) {
case $disposition == self::ATTR_APPEND:
case $disposition == self::ATTR_REPLACE:
return false;
case $disposition == self::ATTR_ALL:
return $attribute['value'];
case $disposition >= $n:
$disposition -= $n;
break;
default:
return $attribute['value'][$disposition];
}
}
}
return false;
}
/**
* Returns a list of all CSR attributes in use
*
* @param array $csr optional
* @access public
* @return array
*/
function getAttributes($csr = null)
{
if (empty($csr)) {
$csr = $this->currentCert;
}
$attributes = $this->_subArray($csr,
'certificationRequestInfo/attributes');
$attrs = array();
if (is_array($attributes)) {
foreach ($attributes as $attribute) {
$attrs[] = $attribute['type'];
}
}
return $attrs;
}
/**
* Set a CSR attribute
*
* @param string $id
* @param mixed $value
* @param bool $disposition optional
* @access public
* @return bool
*/
function setAttribute($id, $value, $disposition = self::ATTR_ALL)
{
$attributes = &$this->_subArray($this->currentCert,
'certificationRequestInfo/attributes', true);
if (!is_array($attributes)) {
return false;
}
switch ($disposition) {
case self::ATTR_REPLACE:
$disposition = self::ATTR_APPEND;
case self::ATTR_ALL:
$this->removeAttribute($id);
break;
}
foreach ($attributes as $key => $attribute) {
if ($attribute['type'] == $id) {
$n = count($attribute['value']);
switch (true) {
case $disposition == self::ATTR_APPEND:
$last = $key;
break;
case $disposition >= $n:
$disposition -= $n;
break;
default:
$attributes[$key]['value'][$disposition]
= $value;
return true;
}
}
}
switch (true) {
case $disposition >= 0:
return false;
case isset($last):
$attributes[$last]['value'][] = $value;
break;
default:
$attributes[] = array('type' => $id,
'value' => $disposition == self::ATTR_ALL ? $value:
array($value));
break;
}
return true;
}
/**
* Sets the subject key identifier
*
* This is used by the id-ce-authorityKeyIdentifier and the
id-ce-subjectKeyIdentifier extensions.
*
* @param string $value
* @access public
*/
function setKeyIdentifier($value)
{
if (empty($value)) {
unset($this->currentKeyIdentifier);
} else {
$this->currentKeyIdentifier = base64_encode($value);
}
}
/**
* Compute a public key identifier.
*
* Although key identifiers may be set to any unique value, this
function
* computes key identifiers from public key according to the two
* recommended methods (4.2.1.2 RFC 3280).
* Highly polymorphic: try to accept all possible forms of key:
* - Key object
* - \phpseclib\File\X509 object with public or private key defined
* - Certificate or CSR array
* - \phpseclib\File\ASN1\Element object
* - PEM or DER string
*
* @param mixed $key optional
* @param int $method optional
* @access public
* @return string binary key identifier
*/
function computeKeyIdentifier($key = null, $method = 1)
{
if (is_null($key)) {
$key = $this;
}
switch (true) {
case is_string($key):
break;
case is_array($key) &&
isset($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
return
$this->computeKeyIdentifier($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
$method);
case is_array($key) &&
isset($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']):
return
$this->computeKeyIdentifier($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'],
$method);
case !is_object($key):
return false;
case $key instanceof Element:
// Assume the element is a bitstring-packed key.
$asn1 = new ASN1();
$decoded = $asn1->decodeBER($key->element);
if (empty($decoded)) {
return false;
}
$raw = $asn1->asn1map($decoded[0],
array('type' => ASN1::TYPE_BIT_STRING));
if (empty($raw)) {
return false;
}
$raw = base64_decode($raw);
// If the key is private, compute identifier from its
corresponding public key.
$key = new RSA();
if (!$key->loadKey($raw)) {
return false; // Not an unencrypted RSA key.
}
if ($key->getPrivateKey() !== false) { // If private.
return $this->computeKeyIdentifier($key, $method);
}
$key = $raw; // Is a public key.
break;
case $key instanceof X509:
if (isset($key->publicKey)) {
return
$this->computeKeyIdentifier($key->publicKey, $method);
}
if (isset($key->privateKey)) {
return
$this->computeKeyIdentifier($key->privateKey, $method);
}
if (isset($key->currentCert['tbsCertificate'])
|| isset($key->currentCert['certificationRequestInfo'])) {
return
$this->computeKeyIdentifier($key->currentCert, $method);
}
return false;
default: // Should be a key object (i.e.:
\phpseclib\Crypt\RSA).
$key = $key->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1);
break;
}
// If in PEM format, convert to binary.
$key = $this->_extractBER($key);
// Now we have the key string: compute its sha-1 sum.
$hash = new Hash('sha1');
$hash = $hash->hash($key);
if ($method == 2) {
$hash = substr($hash, -8);
$hash[0] = chr((ord($hash[0]) & 0x0F) | 0x40);
}
return $hash;
}
/**
* Format a public key as appropriate
*
* @access private
* @return array
*/
function _formatSubjectPublicKey()
{
if ($this->publicKey instanceof RSA) {
// the following two return statements do the same thing. i
dunno.. i just prefer the later for some reason.
// the former is a good example of how to do fuzzing on the
public key
//return new
Element(base64_decode(preg_replace('#-.+-|[\r\n]#', '',
$this->publicKey->getPublicKey())));
return array(
'algorithm' => array('algorithm'
=> 'rsaEncryption'),
'subjectPublicKey' =>
$this->publicKey->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1)
);
}
return false;
}
/**
* Set the domain name's which the cert is to be valid for
*
* @access public
* @return array
*/
function setDomain()
{
$this->domains = func_get_args();
$this->removeDNProp('id-at-commonName');
$this->setDNProp('id-at-commonName',
$this->domains[0]);
}
/**
* Set the IP Addresses's which the cert is to be valid for
*
* @access public
*/
function setIPAddress()
{
$this->ipAddresses = func_get_args();
/*
if (!isset($this->domains)) {
$this->removeDNProp('id-at-commonName');
$this->setDNProp('id-at-commonName',
$this->ipAddresses[0]);
}
*/
}
/**
* Helper function to build domain array
*
* @access private
* @param string $domain
* @return array
*/
function _dnsName($domain)
{
return array('dNSName' => $domain);
}
/**
* Helper function to build IP Address array
*
* (IPv6 is not currently supported)
*
* @access private
* @param string $address
* @return array
*/
function _iPAddress($address)
{
return array('iPAddress' => $address);
}
/**
* Get the index of a revoked certificate.
*
* @param array $rclist
* @param string $serial
* @param bool $create optional
* @access private
* @return int|false
*/
function _revokedCertificate(&$rclist, $serial, $create = false)
{
$serial = new BigInteger($serial);
foreach ($rclist as $i => $rc) {
if (!($serial->compare($rc['userCertificate']))) {
return $i;
}
}
if (!$create) {
return false;
}
$i = count($rclist);
$revocationDate = new DateTime('now', new
DateTimeZone(@date_default_timezone_get()));
$rclist[] = array('userCertificate' => $serial,
'revocationDate' =>
$this->_timeField($revocationDate->format('D, d M Y H:i:s
O')));
return $i;
}
/**
* Revoke a certificate.
*
* @param string $serial
* @param string $date optional
* @access public
* @return bool
*/
function revoke($serial, $date = null)
{
if (isset($this->currentCert['tbsCertList'])) {
if (is_array($rclist =
&$this->_subArray($this->currentCert,
'tbsCertList/revokedCertificates', true))) {
if ($this->_revokedCertificate($rclist, $serial) ===
false) { // If not yet revoked
if (($i = $this->_revokedCertificate($rclist,
$serial, true)) !== false) {
if (!empty($date)) {
$rclist[$i]['revocationDate'] =
$this->_timeField($date);
}
return true;
}
}
}
}
return false;
}
/**
* Unrevoke a certificate.
*
* @param string $serial
* @access public
* @return bool
*/
function unrevoke($serial)
{
if (is_array($rclist =
&$this->_subArray($this->currentCert,
'tbsCertList/revokedCertificates'))) {
if (($i = $this->_revokedCertificate($rclist, $serial)) !==
false) {
unset($rclist[$i]);
$rclist = array_values($rclist);
return true;
}
}
return false;
}
/**
* Get a revoked certificate.
*
* @param string $serial
* @access public
* @return mixed
*/
function getRevoked($serial)
{
if (is_array($rclist = $this->_subArray($this->currentCert,
'tbsCertList/revokedCertificates'))) {
if (($i = $this->_revokedCertificate($rclist, $serial)) !==
false) {
return $rclist[$i];
}
}
return false;
}
/**
* List revoked certificates
*
* @param array $crl optional
* @access public
* @return array
*/
function listRevoked($crl = null)
{
if (!isset($crl)) {
$crl = $this->currentCert;
}
if (!isset($crl['tbsCertList'])) {
return false;
}
$result = array();
if (is_array($rclist = $this->_subArray($crl,
'tbsCertList/revokedCertificates'))) {
foreach ($rclist as $rc) {
$result[] =
$rc['userCertificate']->toString();
}
}
return $result;
}
/**
* Remove a Revoked Certificate Extension
*
* @param string $serial
* @param string $id
* @access public
* @return bool
*/
function removeRevokedCertificateExtension($serial, $id)
{
if (is_array($rclist =
&$this->_subArray($this->currentCert,
'tbsCertList/revokedCertificates'))) {
if (($i = $this->_revokedCertificate($rclist, $serial)) !==
false) {
return $this->_removeExtension($id,
"tbsCertList/revokedCertificates/$i/crlEntryExtensions");
}
}
return false;
}
/**
* Get a Revoked Certificate Extension
*
* Returns the extension if it exists and false if not
*
* @param string $serial
* @param string $id
* @param array $crl optional
* @access public
* @return mixed
*/
function getRevokedCertificateExtension($serial, $id, $crl = null)
{
if (!isset($crl)) {
$crl = $this->currentCert;
}
if (is_array($rclist = $this->_subArray($crl,
'tbsCertList/revokedCertificates'))) {
if (($i = $this->_revokedCertificate($rclist, $serial)) !==
false) {
return $this->_getExtension($id, $crl,
"tbsCertList/revokedCertificates/$i/crlEntryExtensions");
}
}
return false;
}
/**
* Returns a list of all extensions in use for a given revoked
certificate
*
* @param string $serial
* @param array $crl optional
* @access public
* @return array
*/
function getRevokedCertificateExtensions($serial, $crl = null)
{
if (!isset($crl)) {
$crl = $this->currentCert;
}
if (is_array($rclist = $this->_subArray($crl,
'tbsCertList/revokedCertificates'))) {
if (($i = $this->_revokedCertificate($rclist, $serial)) !==
false) {
return $this->_getExtensions($crl,
"tbsCertList/revokedCertificates/$i/crlEntryExtensions");
}
}
return false;
}
/**
* Set a Revoked Certificate Extension
*
* @param string $serial
* @param string $id
* @param mixed $value
* @param bool $critical optional
* @param bool $replace optional
* @access public
* @return bool
*/
function setRevokedCertificateExtension($serial, $id, $value, $critical
= false, $replace = true)
{
if (isset($this->currentCert['tbsCertList'])) {
if (is_array($rclist =
&$this->_subArray($this->currentCert,
'tbsCertList/revokedCertificates', true))) {
if (($i = $this->_revokedCertificate($rclist, $serial,
true)) !== false) {
return $this->_setExtension($id, $value, $critical,
$replace,
"tbsCertList/revokedCertificates/$i/crlEntryExtensions");
}
}
}
return false;
}
/**
* 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
*/
if (strlen($str) > ini_get('pcre.backtrack_limit')) {
$temp = $str;
} else {
$temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms',
'', $str, 1);
$temp = preg_replace('#-+END.*[\r\n ]*.*#ms',
'', $temp, 1);
}
// remove new lines
$temp = str_replace(array("\r", "\n", '
'), '', $temp);
// remove the -----BEGIN CERTIFICATE----- and -----END
CERTIFICATE----- stuff
$temp = preg_replace('#^-+[^-]+-+|-+[^-]+-+$#',
'', $temp);
$temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ?
base64_decode($temp) : false;
return $temp != false ? $temp : $str;
}
/**
* Returns the OID corresponding to a name
*
* What's returned in the associative array returned by loadX509()
(or load*()) is either a name or an OID if
* no OID to name mapping is available. The problem with this is that
what may be an unmapped OID in one version
* of phpseclib may not be unmapped in the next version, so apps that
are looking at this OID may not be able
* to work from version to version.
*
* This method will return the OID if a name is passed to it and if no
mapping is avialable it'll assume that
* what's being passed to it already is an OID and return that
instead. A few examples.
*
* getOID('2.16.840.1.101.3.4.2.1') ==
'2.16.840.1.101.3.4.2.1'
* getOID('id-sha256') == '2.16.840.1.101.3.4.2.1'
* getOID('zzz') == 'zzz'
*
* @access public
* @return string
*/
function getOID($name)
{
static $reverseMap;
if (!isset($reverseMap)) {
$reverseMap = array_flip($this->oids);
}
return isset($reverseMap[$name]) ? $reverseMap[$name] : $name;
}
}
phpseclib/phpseclib/Math/BigInteger.php000064400000366204151161207740014126
0ustar00<?php
/**
* Pure-PHP arbitrary precision integer arithmetic library.
*
* Supports base-2, base-10, base-16, and base-256 numbers. Uses the GMP
or BCMath extensions, if available,
* and an internal implementation, otherwise.
*
* PHP version 5
*
* {@internal (all DocBlock comments regarding implementation - such as the
one that follows - refer to the
* {@link self::MODE_INTERNAL self::MODE_INTERNAL} mode)
*
* BigInteger uses base-2**26 to perform operations such as multiplication
and division and
* base-2**52 (ie. two base 2**26 digits) to perform addition and
subtraction. Because the largest possible
* value when multiplying two base-2**26 numbers together is a base-2**52
number, double precision floating
* point numbers - numbers that should be supported on most hardware and
whose significand is 53 bits - are
* used. As a consequence, bitwise operators such as >> and <<
cannot be used, nor can the modulo operator %,
* 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.
*
* Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness
little endian} format. ie.
* (new \phpseclib\Math\BigInteger(pow(2, 26)))->value = array(0, 1)
*
* Useful resources are as follows:
*
* - {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf
Handbook of Applied Cryptography (HAC)}
* - {@link http://math.libtomcrypt.com/files/tommath.pdf Multi-Precision
Math (MPM)}
* - Java's BigInteger classes. See
/j2se/src/share/classes/java/math in jdk-1_5_0-src-jrl.zip
*
* Here's an example of how to use this library:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger(2);
* $b = new \phpseclib\Math\BigInteger(3);
*
* $c = $a->add($b);
*
* echo $c->toString(); // outputs 5
* ?>
* </code>
*
* @category Math
* @package BigInteger
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2006 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
*/
namespace phpseclib\Math;
use phpseclib\Crypt\Random;
/**
* Pure-PHP arbitrary precision integer arithmetic library. Supports
base-2, base-10, base-16, and base-256
* numbers.
*
* @package BigInteger
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class BigInteger
{
/**#@+
* Reduction constants
*
* @access private
* @see BigInteger::_reduce()
*/
/**
* @see BigInteger::_montgomery()
* @see BigInteger::_prepMontgomery()
*/
const MONTGOMERY = 0;
/**
* @see BigInteger::_barrett()
*/
const BARRETT = 1;
/**
* @see BigInteger::_mod2()
*/
const POWEROF2 = 2;
/**
* @see BigInteger::_remainder()
*/
const CLASSIC = 3;
/**
* @see BigInteger::__clone()
*/
const NONE = 4;
/**#@-*/
/**#@+
* Array constants
*
* Rather than create a thousands and thousands of new BigInteger
objects in repeated function calls to add() and
* multiply() or whatever, we'll just work directly on arrays,
taking them in as parameters and returning them.
*
* @access private
*/
/**
* $result[self::VALUE] contains the value.
*/
const VALUE = 0;
/**
* $result[self::SIGN] contains the sign.
*/
const SIGN = 1;
/**#@-*/
/**#@+
* @access private
* @see BigInteger::_montgomery()
* @see BigInteger::_barrett()
*/
/**
* Cache constants
*
* $cache[self::VARIABLE] tells us whether or not the cached data is
still valid.
*/
const VARIABLE = 0;
/**
* $cache[self::DATA] contains the cached data.
*/
const DATA = 1;
/**#@-*/
/**#@+
* Mode constants.
*
* @access private
* @see BigInteger::__construct()
*/
/**
* To use the pure-PHP implementation
*/
const MODE_INTERNAL = 1;
/**
* To use the BCMath library
*
* (if enabled; otherwise, the internal implementation will be used)
*/
const MODE_BCMATH = 2;
/**
* To use the GMP library
*
* (if present; otherwise, either the BCMath or the internal
implementation will be used)
*/
const MODE_GMP = 3;
/**#@-*/
/**
* Karatsuba Cutoff
*
* At what point do we switch between Karatsuba multiplication and
schoolbook long multiplication?
*
* @access private
*/
const KARATSUBA_CUTOFF = 25;
/**#@+
* Static properties used by the pure-PHP implementation.
*
* @see __construct()
*/
protected static $base;
protected static $baseFull;
protected static $maxDigit;
protected static $msb;
/**
* $max10 in greatest $max10Len satisfying
* $max10 = 10**$max10Len <= 2**$base.
*/
protected static $max10;
/**
* $max10Len in greatest $max10Len satisfying
* $max10 = 10**$max10Len <= 2**$base.
*/
protected static $max10Len;
protected static $maxDigit2;
/**#@-*/
/**
* Holds the BigInteger's value.
*
* @var array
* @access private
*/
var $value;
/**
* Holds the BigInteger's magnitude.
*
* @var bool
* @access private
*/
var $is_negative = false;
/**
* Precision
*
* @see self::setPrecision()
* @access private
*/
var $precision = -1;
/**
* Precision Bitmask
*
* @see self::setPrecision()
* @access private
*/
var $bitmask = false;
/**
* Mode independent value used for serialization.
*
* If the bcmath or gmp extensions are installed $this->value will
be a non-serializable resource, hence the need for
* a variable that'll be serializable regardless of whether or not
extensions are being used. Unlike $this->value,
* however, $this->hex is only calculated when $this->__sleep()
is called.
*
* @see self::__sleep()
* @see self::__wakeup()
* @var string
* @access private
*/
var $hex;
/**
* Converts base-2, base-10, base-16, and binary strings (base-256) to
BigIntegers.
*
* If the second parameter - $base - is negative, then it will be
assumed that the number's are encoded using
* two's compliment. The sole exception to this is -10, which is
treated the same as 10 is.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('0x32', 16); // 50
in base-16
*
* echo $a->toString(); // outputs 50
* ?>
* </code>
*
* @param int|string|resource $x base-10 number or base-$base number if
$base set.
* @param int $base
* @return \phpseclib\Math\BigInteger
* @access public
*/
function __construct($x = 0, $base = 10)
{
if (!defined('MATH_BIGINTEGER_MODE')) {
switch (true) {
case extension_loaded('gmp'):
define('MATH_BIGINTEGER_MODE',
self::MODE_GMP);
break;
case extension_loaded('bcmath'):
define('MATH_BIGINTEGER_MODE',
self::MODE_BCMATH);
break;
default:
define('MATH_BIGINTEGER_MODE',
self::MODE_INTERNAL);
}
}
if (extension_loaded('openssl') &&
!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
$versions = array();
// avoid generating errors (even with suppression) when
phpinfo() is disabled (common in production systems)
if (strpos(ini_get('disable_functions'),
'phpinfo') === false) {
ob_start();
@phpinfo();
$content = ob_get_contents();
ob_end_clean();
preg_match_all('#OpenSSL (Header|Library)
Version(.*)#im', $content, $matches);
if (!empty($matches[1])) {
for ($i = 0; $i < count($matches[1]); $i++) {
$fullVersion = trim(str_replace('=>',
'', strip_tags($matches[2][$i])));
// Remove letter part in OpenSSL version
if (!preg_match('/(\d+\.\d+\.\d+)/i',
$fullVersion, $m)) {
$versions[$matches[1][$i]] = $fullVersion;
} else {
$versions[$matches[1][$i]] = $m[0];
}
}
}
}
// 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']:
case version_compare($versions['Header'],
'1.0.0') >= 0 &&
version_compare($versions['Library'], '1.0.0') >= 0:
define('MATH_BIGINTEGER_OPENSSL_ENABLED',
true);
break;
default:
define('MATH_BIGINTEGER_OPENSSL_DISABLE',
true);
}
}
if (!defined('PHP_INT_SIZE')) {
define('PHP_INT_SIZE', 4);
}
if (empty(self::$base) && MATH_BIGINTEGER_MODE ==
self::MODE_INTERNAL) {
switch (PHP_INT_SIZE) {
case 8: // use 64-bit integers if int size is 8 bytes
self::$base = 31;
self::$baseFull = 0x80000000;
self::$maxDigit = 0x7FFFFFFF;
self::$msb = 0x40000000;
self::$max10 = 1000000000;
self::$max10Len = 9;
self::$maxDigit2 = pow(2, 62);
break;
//case 4: // use 64-bit floats if int size is 4 bytes
default:
self::$base = 26;
self::$baseFull = 0x4000000;
self::$maxDigit = 0x3FFFFFF;
self::$msb = 0x2000000;
self::$max10 = 10000000;
self::$max10Len = 7;
self::$maxDigit2 = pow(2, 52); // pow() prevents
truncation
}
}
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
switch (true) {
case is_resource($x) && get_resource_type($x)
== 'GMP integer':
// PHP 5.6 switched GMP from using resources to objects
case $x instanceof \GMP:
$this->value = $x;
return;
}
$this->value = gmp_init(0);
break;
case self::MODE_BCMATH:
$this->value = '0';
break;
default:
$this->value = array();
}
// '0' counts as empty() but when the base is 256
'0' is equal to ord('0') or 48
// '0' is the only value like this per
http://php.net/empty
if (empty($x) && (abs($base) != 256 || $x !==
'0')) {
return;
}
switch ($base) {
case -256:
if (ord($x[0]) & 0x80) {
$x = ~$x;
$this->is_negative = true;
}
case 256:
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$this->value =
function_exists('gmp_import') ?
gmp_import($x) :
gmp_init('0x' . bin2hex($x));
if ($this->is_negative) {
$this->value = gmp_neg($this->value);
}
break;
case self::MODE_BCMATH:
// round $len to the nearest 4 (thanks, DavidMJ!)
$len = (strlen($x) + 3) & 0xFFFFFFFC;
$x = str_pad($x, $len, chr(0), STR_PAD_LEFT);
for ($i = 0; $i < $len; $i+= 4) {
$this->value = bcmul($this->value,
'4294967296', 0); // 4294967296 == 2**32
$this->value = bcadd($this->value,
0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2])
<< 8) | ord($x[$i + 3])), 0);
}
if ($this->is_negative) {
$this->value = '-' .
$this->value;
}
break;
// converts a base-2**8 (big endian / msb) number to
base-2**26 (little endian / lsb)
default:
while (strlen($x)) {
$this->value[] =
$this->_bytes2int($this->_base256_rshift($x, self::$base));
}
}
if ($this->is_negative) {
if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
$this->is_negative = false;
}
$temp = $this->add(new static('-1'));
$this->value = $temp->value;
}
break;
case 16:
case -16:
if ($base > 0 && $x[0] == '-') {
$this->is_negative = true;
$x = substr($x, 1);
}
$x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#',
'$1', $x);
$is_negative = false;
if ($base < 0 && hexdec($x[0]) >= 8) {
$this->is_negative = $is_negative = true;
$x = bin2hex(~pack('H*', $x));
}
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = $this->is_negative ? '-0x' .
$x : '0x' . $x;
$this->value = gmp_init($temp);
$this->is_negative = false;
break;
case self::MODE_BCMATH:
$x = (strlen($x) & 1) ? '0' . $x :
$x;
$temp = new static(pack('H*', $x), 256);
$this->value = $this->is_negative ?
'-' . $temp->value : $temp->value;
$this->is_negative = false;
break;
default:
$x = (strlen($x) & 1) ? '0' . $x :
$x;
$temp = new static(pack('H*', $x), 256);
$this->value = $temp->value;
}
if ($is_negative) {
$temp = $this->add(new static('-1'));
$this->value = $temp->value;
}
break;
case 10:
case -10:
// (?<!^)(?:-).*: find any -'s that aren't at
the beginning and then any characters that follow that
// (?<=^|-)0*: find any 0's that are preceded by
the start of the string or by a - (ie. octals)
// [^-0-9].*: find any non-numeric characters and then any
characters that follow that
$x =
preg_replace('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#',
'', $x);
if (!strlen($x) || $x == '-') {
$x = '0';
}
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$this->value = gmp_init($x);
break;
case self::MODE_BCMATH:
// explicitly casting $x to a string is necessary,
here, since doing $x[0] on -1 yields different
// results then doing it on '-1' does
(modInverse does $x[0])
$this->value = $x === '-' ?
'0' : (string) $x;
break;
default:
$temp = new static();
$multiplier = new static();
$multiplier->value = array(self::$max10);
if ($x[0] == '-') {
$this->is_negative = true;
$x = substr($x, 1);
}
$x = str_pad($x, strlen($x) + ((self::$max10Len -
1) * strlen($x)) % self::$max10Len, 0, STR_PAD_LEFT);
while (strlen($x)) {
$temp = $temp->multiply($multiplier);
$temp = $temp->add(new
static($this->_int2bytes(substr($x, 0, self::$max10Len)), 256));
$x = substr($x, self::$max10Len);
}
$this->value = $temp->value;
}
break;
case 2: // base-2 support originally implemented by Lluis
Pamies - thanks!
case -2:
if ($base > 0 && $x[0] == '-') {
$this->is_negative = true;
$x = substr($x, 1);
}
$x = preg_replace('#^([01]*).*#', '$1',
$x);
$x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0,
STR_PAD_LEFT);
$str = '0x';
while (strlen($x)) {
$part = substr($x, 0, 4);
$str.= dechex(bindec($part));
$x = substr($x, 4);
}
if ($this->is_negative) {
$str = '-' . $str;
}
$temp = new static($str, 8 * $base); // ie. either -16 or
+16
$this->value = $temp->value;
$this->is_negative = $temp->is_negative;
break;
default:
// base not supported, so we'll let $this == 0
}
}
/**
* Converts a BigInteger to a byte string (eg. base-256).
*
* Negative numbers are saved as positive numbers, unless
$twos_compliment is set to true, at which point, they're
* saved as two's compliment.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('65');
*
* echo $a->toBytes(); // outputs chr(65)
* ?>
* </code>
*
* @param bool $twos_compliment
* @return string
* @access public
* @internal Converts a base-2**26 number to base-2**8
*/
function toBytes($twos_compliment = false)
{
if ($twos_compliment) {
$comparison = $this->compare(new static());
if ($comparison == 0) {
return $this->precision > 0 ? str_repeat(chr(0),
($this->precision + 1) >> 3) : '';
}
$temp = $comparison < 0 ? $this->add(new static(1)) :
$this->copy();
$bytes = $temp->toBytes();
if (!strlen($bytes)) { // eg. if the number we're trying
to convert is -1
$bytes = chr(0);
}
if ($this->precision <= 0 && (ord($bytes[0])
& 0x80)) {
$bytes = chr(0) . $bytes;
}
return $comparison < 0 ? ~$bytes : $bytes;
}
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
if (gmp_cmp($this->value, gmp_init(0)) == 0) {
return $this->precision > 0 ? str_repeat(chr(0),
($this->precision + 1) >> 3) : '';
}
if (function_exists('gmp_export')) {
$temp = gmp_export($this->value);
} else {
$temp = gmp_strval(gmp_abs($this->value), 16);
$temp = (strlen($temp) & 1) ? '0' . $temp
: $temp;
$temp = pack('H*', $temp);
}
return $this->precision > 0 ?
substr(str_pad($temp, $this->precision >> 3,
chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
ltrim($temp, chr(0));
case self::MODE_BCMATH:
if ($this->value === '0') {
return $this->precision > 0 ? str_repeat(chr(0),
($this->precision + 1) >> 3) : '';
}
$value = '';
$current = $this->value;
if ($current[0] == '-') {
$current = substr($current, 1);
}
while (bccomp($current, '0', 0) > 0) {
$temp = bcmod($current, '16777216');
$value = chr($temp >> 16) . chr($temp >> 8)
. chr($temp) . $value;
$current = bcdiv($current, '16777216', 0);
}
return $this->precision > 0 ?
substr(str_pad($value, $this->precision >> 3,
chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
ltrim($value, chr(0));
}
if (!count($this->value)) {
return $this->precision > 0 ? str_repeat(chr(0),
($this->precision + 1) >> 3) : '';
}
$result =
$this->_int2bytes($this->value[count($this->value) - 1]);
$temp = $this->copy();
for ($i = count($temp->value) - 2; $i >= 0; --$i) {
$temp->_base256_lshift($result, self::$base);
$result = $result |
str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0),
STR_PAD_LEFT);
}
return $this->precision > 0 ?
str_pad(substr($result, -(($this->precision + 7) >>
3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) :
$result;
}
/**
* Converts a BigInteger to a hex string (eg. base-16)).
*
* Negative numbers are saved as positive numbers, unless
$twos_compliment is set to true, at which point, they're
* saved as two's compliment.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('65');
*
* echo $a->toHex(); // outputs '41'
* ?>
* </code>
*
* @param bool $twos_compliment
* @return string
* @access public
* @internal Converts a base-2**26 number to base-2**8
*/
function toHex($twos_compliment = false)
{
return bin2hex($this->toBytes($twos_compliment));
}
/**
* Converts a BigInteger to a bit string (eg. base-2).
*
* Negative numbers are saved as positive numbers, unless
$twos_compliment is set to true, at which point, they're
* saved as two's compliment.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('65');
*
* echo $a->toBits(); // outputs '1000001'
* ?>
* </code>
*
* @param bool $twos_compliment
* @return string
* @access public
* @internal Converts a base-2**26 number to base-2**2
*/
function toBits($twos_compliment = false)
{
$hex = $this->toHex($twos_compliment);
$bits = '';
for ($i = strlen($hex) - 6, $start = strlen($hex) % 6; $i >=
$start; $i-=6) {
$bits = str_pad(decbin(hexdec(substr($hex, $i, 6))), 24,
'0', STR_PAD_LEFT) . $bits;
}
if ($start) { // hexdec('') == 0
$bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8 *
$start, '0', STR_PAD_LEFT) . $bits;
}
$result = $this->precision > 0 ? substr($bits,
-$this->precision) : ltrim($bits, '0');
if ($twos_compliment && $this->compare(new static())
> 0 && $this->precision <= 0) {
return '0' . $result;
}
return $result;
}
/**
* Converts a BigInteger to a base-10 number.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('50');
*
* echo $a->toString(); // outputs 50
* ?>
* </code>
*
* @return string
* @access public
* @internal Converts a base-2**26 number to base-10**7 (which is
pretty much base-10)
*/
function toString()
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
return gmp_strval($this->value);
case self::MODE_BCMATH:
if ($this->value === '0') {
return '0';
}
return ltrim($this->value, '0');
}
if (!count($this->value)) {
return '0';
}
$temp = $this->copy();
$temp->bitmask = false;
$temp->is_negative = false;
$divisor = new static();
$divisor->value = array(self::$max10);
$result = '';
while (count($temp->value)) {
list($temp, $mod) = $temp->divide($divisor);
$result = str_pad(isset($mod->value[0]) ? $mod->value[0]
: '', self::$max10Len, '0', STR_PAD_LEFT) . $result;
}
$result = ltrim($result, '0');
if (empty($result)) {
$result = '0';
}
if ($this->is_negative) {
$result = '-' . $result;
}
return $result;
}
/**
* Copy an object
*
* PHP5 passes objects by reference while PHP4 passes by value. As
such, we need a function to guarantee
* that all objects are passed by value, when appropriate. More
information can be found here:
*
* {@link http://php.net/language.oop5.basic#51624}
*
* @access public
* @see self::__clone()
* @return \phpseclib\Math\BigInteger
*/
function copy()
{
$temp = new static();
$temp->value = $this->value;
$temp->is_negative = $this->is_negative;
$temp->precision = $this->precision;
$temp->bitmask = $this->bitmask;
return $temp;
}
/**
* __toString() magic method
*
* Will be called, automatically, if you're supporting just PHP5.
If you're supporting PHP4, you'll need to call
* toString().
*
* @access public
* @internal Implemented per a suggestion by Techie-Michael - thanks!
*/
function __toString()
{
return $this->toString();
}
/**
* __clone() magic method
*
* Although you can call BigInteger::__toString() directly in PHP5, you
cannot call BigInteger::__clone() directly
* in PHP5. You can in PHP4 since it's not a magic method, but in
PHP5, you have to call it by using the PHP5
* only syntax of $y = clone $x. As such, if you're trying to
write an application that works on both PHP4 and
* PHP5, call BigInteger::copy(), instead.
*
* @access public
* @see self::copy()
* @return \phpseclib\Math\BigInteger
*/
function __clone()
{
return $this->copy();
}
/**
* __sleep() magic method
*
* Will be called, automatically, when serialize() is called on a
BigInteger object.
*
* @see self::__wakeup()
* @access public
*/
function __sleep()
{
$this->hex = $this->toHex(true);
$vars = array('hex');
if ($this->precision > 0) {
$vars[] = 'precision';
}
return $vars;
}
/**
* __wakeup() magic method
*
* Will be called, automatically, when unserialize() is called on a
BigInteger object.
*
* @see self::__sleep()
* @access public
*/
function __wakeup()
{
$temp = new static($this->hex, -16);
$this->value = $temp->value;
$this->is_negative = $temp->is_negative;
if ($this->precision > 0) {
// recalculate $this->bitmask
$this->setPrecision($this->precision);
}
}
/**
* __debugInfo() magic method
*
* Will be called, automatically, when print_r() or var_dump() are
called
*
* @access public
*/
function __debugInfo()
{
$opts = array();
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$engine = 'gmp';
break;
case self::MODE_BCMATH:
$engine = 'bcmath';
break;
case self::MODE_INTERNAL:
$engine = 'internal';
$opts[] = PHP_INT_SIZE == 8 ? '64-bit' :
'32-bit';
}
if (MATH_BIGINTEGER_MODE != self::MODE_GMP &&
defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
$opts[] = 'OpenSSL';
}
if (!empty($opts)) {
$engine.= ' (' . implode('.', $opts) .
')';
}
return array(
'value' => '0x' . $this->toHex(true),
'engine' => $engine
);
}
/**
* Adds two BigIntegers.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('10');
* $b = new \phpseclib\Math\BigInteger('20');
*
* $c = $a->add($b);
*
* echo $c->toString(); // outputs 30
* ?>
* </code>
*
* @param \phpseclib\Math\BigInteger $y
* @return \phpseclib\Math\BigInteger
* @access public
* @internal Performs base-2**52 addition
*/
function add($y)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_add($this->value, $y->value);
return $this->_normalize($temp);
case self::MODE_BCMATH:
$temp = new static();
$temp->value = bcadd($this->value, $y->value, 0);
return $this->_normalize($temp);
}
$temp = $this->_add($this->value, $this->is_negative,
$y->value, $y->is_negative);
$result = new static();
$result->value = $temp[self::VALUE];
$result->is_negative = $temp[self::SIGN];
return $this->_normalize($result);
}
/**
* Performs addition.
*
* @param array $x_value
* @param bool $x_negative
* @param array $y_value
* @param bool $y_negative
* @return array
* @access private
*/
function _add($x_value, $x_negative, $y_value, $y_negative)
{
$x_size = count($x_value);
$y_size = count($y_value);
if ($x_size == 0) {
return array(
self::VALUE => $y_value,
self::SIGN => $y_negative
);
} elseif ($y_size == 0) {
return array(
self::VALUE => $x_value,
self::SIGN => $x_negative
);
}
// subtract, if appropriate
if ($x_negative != $y_negative) {
if ($x_value == $y_value) {
return array(
self::VALUE => array(),
self::SIGN => false
);
}
$temp = $this->_subtract($x_value, false, $y_value, false);
$temp[self::SIGN] = $this->_compare($x_value, false,
$y_value, false) > 0 ?
$x_negative : $y_negative;
return $temp;
}
if ($x_size < $y_size) {
$size = $x_size;
$value = $y_value;
} else {
$size = $y_size;
$value = $x_value;
}
$value[count($value)] = 0; // just in case the carry adds an extra
digit
$carry = 0;
for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) {
$sum = $x_value[$j] * self::$baseFull + $x_value[$i] +
$y_value[$j] * self::$baseFull + $y_value[$i] + $carry;
$carry = $sum >= self::$maxDigit2; // eg. floor($sum /
2**52); only possible values (in any base) are 0 and 1
$sum = $carry ? $sum - self::$maxDigit2 : $sum;
$temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum
>> 31);
$value[$i] = (int) ($sum - self::$baseFull * $temp); // eg. a
faster alternative to fmod($sum, 0x4000000)
$value[$j] = $temp;
}
if ($j == $size) { // ie. if $y_size is odd
$sum = $x_value[$i] + $y_value[$i] + $carry;
$carry = $sum >= self::$baseFull;
$value[$i] = $carry ? $sum - self::$baseFull : $sum;
++$i; // ie. let $i = $j since we've just done $value[$i]
}
if ($carry) {
for (; $value[$i] == self::$maxDigit; ++$i) {
$value[$i] = 0;
}
++$value[$i];
}
return array(
self::VALUE => $this->_trim($value),
self::SIGN => $x_negative
);
}
/**
* Subtracts two BigIntegers.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('10');
* $b = new \phpseclib\Math\BigInteger('20');
*
* $c = $a->subtract($b);
*
* echo $c->toString(); // outputs -10
* ?>
* </code>
*
* @param \phpseclib\Math\BigInteger $y
* @return \phpseclib\Math\BigInteger
* @access public
* @internal Performs base-2**52 subtraction
*/
function subtract($y)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_sub($this->value, $y->value);
return $this->_normalize($temp);
case self::MODE_BCMATH:
$temp = new static();
$temp->value = bcsub($this->value, $y->value, 0);
return $this->_normalize($temp);
}
$temp = $this->_subtract($this->value, $this->is_negative,
$y->value, $y->is_negative);
$result = new static();
$result->value = $temp[self::VALUE];
$result->is_negative = $temp[self::SIGN];
return $this->_normalize($result);
}
/**
* Performs subtraction.
*
* @param array $x_value
* @param bool $x_negative
* @param array $y_value
* @param bool $y_negative
* @return array
* @access private
*/
function _subtract($x_value, $x_negative, $y_value, $y_negative)
{
$x_size = count($x_value);
$y_size = count($y_value);
if ($x_size == 0) {
return array(
self::VALUE => $y_value,
self::SIGN => !$y_negative
);
} elseif ($y_size == 0) {
return array(
self::VALUE => $x_value,
self::SIGN => $x_negative
);
}
// add, if appropriate (ie. -$x - +$y or +$x - -$y)
if ($x_negative != $y_negative) {
$temp = $this->_add($x_value, false, $y_value, false);
$temp[self::SIGN] = $x_negative;
return $temp;
}
$diff = $this->_compare($x_value, $x_negative, $y_value,
$y_negative);
if (!$diff) {
return array(
self::VALUE => array(),
self::SIGN => false
);
}
// switch $x and $y around, if appropriate.
if ((!$x_negative && $diff < 0) || ($x_negative
&& $diff > 0)) {
$temp = $x_value;
$x_value = $y_value;
$y_value = $temp;
$x_negative = !$x_negative;
$x_size = count($x_value);
$y_size = count($y_value);
}
// at this point, $x_value should be at least as big as - if not
bigger than - $y_value
$carry = 0;
for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) {
$sum = $x_value[$j] * self::$baseFull + $x_value[$i] -
$y_value[$j] * self::$baseFull - $y_value[$i] - $carry;
$carry = $sum < 0; // eg. floor($sum / 2**52); only possible
values (in any base) are 0 and 1
$sum = $carry ? $sum + self::$maxDigit2 : $sum;
$temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum
>> 31);
$x_value[$i] = (int) ($sum - self::$baseFull * $temp);
$x_value[$j] = $temp;
}
if ($j == $y_size) { // ie. if $y_size is odd
$sum = $x_value[$i] - $y_value[$i] - $carry;
$carry = $sum < 0;
$x_value[$i] = $carry ? $sum + self::$baseFull : $sum;
++$i;
}
if ($carry) {
for (; !$x_value[$i]; ++$i) {
$x_value[$i] = self::$maxDigit;
}
--$x_value[$i];
}
return array(
self::VALUE => $this->_trim($x_value),
self::SIGN => $x_negative
);
}
/**
* Multiplies two BigIntegers
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('10');
* $b = new \phpseclib\Math\BigInteger('20');
*
* $c = $a->multiply($b);
*
* echo $c->toString(); // outputs 200
* ?>
* </code>
*
* @param \phpseclib\Math\BigInteger $x
* @return \phpseclib\Math\BigInteger
* @access public
*/
function multiply($x)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_mul($this->value, $x->value);
return $this->_normalize($temp);
case self::MODE_BCMATH:
$temp = new static();
$temp->value = bcmul($this->value, $x->value, 0);
return $this->_normalize($temp);
}
$temp = $this->_multiply($this->value, $this->is_negative,
$x->value, $x->is_negative);
$product = new static();
$product->value = $temp[self::VALUE];
$product->is_negative = $temp[self::SIGN];
return $this->_normalize($product);
}
/**
* Performs multiplication.
*
* @param array $x_value
* @param bool $x_negative
* @param array $y_value
* @param bool $y_negative
* @return array
* @access private
*/
function _multiply($x_value, $x_negative, $y_value, $y_negative)
{
//if ( $x_value == $y_value ) {
// return array(
// self::VALUE => $this->_square($x_value),
// self::SIGN => $x_sign != $y_value
// );
//}
$x_length = count($x_value);
$y_length = count($y_value);
if (!$x_length || !$y_length) { // a 0 is being multiplied
return array(
self::VALUE => array(),
self::SIGN => false
);
}
return array(
self::VALUE => min($x_length, $y_length) < 2 *
self::KARATSUBA_CUTOFF ?
$this->_trim($this->_regularMultiply($x_value,
$y_value)) :
$this->_trim($this->_karatsuba($x_value, $y_value)),
self::SIGN => $x_negative != $y_negative
);
}
/**
* Performs long multiplication on two BigIntegers
*
* Modeled after 'multiply' in MutableBigInteger.java.
*
* @param array $x_value
* @param array $y_value
* @return array
* @access private
*/
function _regularMultiply($x_value, $y_value)
{
$x_length = count($x_value);
$y_length = count($y_value);
if (!$x_length || !$y_length) { // a 0 is being multiplied
return array();
}
if ($x_length < $y_length) {
$temp = $x_value;
$x_value = $y_value;
$y_value = $temp;
$x_length = count($x_value);
$y_length = count($y_value);
}
$product_value = $this->_array_repeat(0, $x_length + $y_length);
// the following for loop could be removed if the for loop
following it
// (the one with nested for loops) initially set $i to 0, but
// doing so would also make the result in one set of unnecessary
adds,
// since on the outermost loops first pass, $product->value[$k]
is going
// to always be 0
$carry = 0;
for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0
$temp = $x_value[$j] * $y_value[0] + $carry; //
$product_value[$k] == 0
$carry = self::$base === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$product_value[$j] = (int) ($temp - self::$baseFull * $carry);
}
$product_value[$j] = $carry;
// the above for loop is what the previous comment was talking
about. the
// following for loop is the "one with nested for loops"
for ($i = 1; $i < $y_length; ++$i) {
$carry = 0;
for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) {
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] +
$carry;
$carry = self::$base === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$product_value[$k] = (int) ($temp - self::$baseFull *
$carry);
}
$product_value[$k] = $carry;
}
return $product_value;
}
/**
* Performs Karatsuba multiplication on two BigIntegers
*
* See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm
Karatsuba algorithm} and
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM
5.2.3}.
*
* @param array $x_value
* @param array $y_value
* @return array
* @access private
*/
function _karatsuba($x_value, $y_value)
{
$m = min(count($x_value) >> 1, count($y_value) >> 1);
if ($m < self::KARATSUBA_CUTOFF) {
return $this->_regularMultiply($x_value, $y_value);
}
$x1 = array_slice($x_value, $m);
$x0 = array_slice($x_value, 0, $m);
$y1 = array_slice($y_value, $m);
$y0 = array_slice($y_value, 0, $m);
$z2 = $this->_karatsuba($x1, $y1);
$z0 = $this->_karatsuba($x0, $y0);
$z1 = $this->_add($x1, false, $x0, false);
$temp = $this->_add($y1, false, $y0, false);
$z1 = $this->_karatsuba($z1[self::VALUE], $temp[self::VALUE]);
$temp = $this->_add($z2, false, $z0, false);
$z1 = $this->_subtract($z1, false, $temp[self::VALUE], false);
$z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
$z1[self::VALUE] = array_merge(array_fill(0, $m, 0),
$z1[self::VALUE]);
$xy = $this->_add($z2, false, $z1[self::VALUE],
$z1[self::SIGN]);
$xy = $this->_add($xy[self::VALUE], $xy[self::SIGN], $z0,
false);
return $xy[self::VALUE];
}
/**
* Performs squaring
*
* @param array $x
* @return array
* @access private
*/
function _square($x = false)
{
return count($x) < 2 * self::KARATSUBA_CUTOFF ?
$this->_trim($this->_baseSquare($x)) :
$this->_trim($this->_karatsubaSquare($x));
}
/**
* Performs traditional squaring on two BigIntegers
*
* Squaring can be done faster than multiplying a number by itself can
be. See
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7
HAC 14.2.4} /
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM
5.3} for more information.
*
* @param array $value
* @return array
* @access private
*/
function _baseSquare($value)
{
if (empty($value)) {
return array();
}
$square_value = $this->_array_repeat(0, 2 * count($value));
for ($i = 0, $max_index = count($value) - 1; $i <= $max_index;
++$i) {
$i2 = $i << 1;
$temp = $square_value[$i2] + $value[$i] * $value[$i];
$carry = self::$base === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$square_value[$i2] = (int) ($temp - self::$baseFull * $carry);
// 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) {
$temp = $square_value[$k] + 2 * $value[$j] * $value[$i] +
$carry;
$carry = self::$base === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$square_value[$k] = (int) ($temp - self::$baseFull *
$carry);
}
// the following line can yield values larger 2**15. at this
point, PHP should switch
// over to floats.
$square_value[$i + $max_index + 1] = $carry;
}
return $square_value;
}
/**
* Performs Karatsuba "squaring" on two BigIntegers
*
* See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm
Karatsuba algorithm} and
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM
5.3.4}.
*
* @param array $value
* @return array
* @access private
*/
function _karatsubaSquare($value)
{
$m = count($value) >> 1;
if ($m < self::KARATSUBA_CUTOFF) {
return $this->_baseSquare($value);
}
$x1 = array_slice($value, $m);
$x0 = array_slice($value, 0, $m);
$z2 = $this->_karatsubaSquare($x1);
$z0 = $this->_karatsubaSquare($x0);
$z1 = $this->_add($x1, false, $x0, false);
$z1 = $this->_karatsubaSquare($z1[self::VALUE]);
$temp = $this->_add($z2, false, $z0, false);
$z1 = $this->_subtract($z1, false, $temp[self::VALUE], false);
$z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
$z1[self::VALUE] = array_merge(array_fill(0, $m, 0),
$z1[self::VALUE]);
$xx = $this->_add($z2, false, $z1[self::VALUE],
$z1[self::SIGN]);
$xx = $this->_add($xx[self::VALUE], $xx[self::SIGN], $z0,
false);
return $xx[self::VALUE];
}
/**
* Divides two BigIntegers.
*
* Returns an array whose first element contains the quotient and whose
second element contains the
* "common residue". If the remainder would be positive, the
"common residue" and the remainder are the
* same. If the remainder would be negative, the "common
residue" is equal to the sum of the remainder
* and the divisor (basically, the "common residue" is the
first positive modulo).
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('10');
* $b = new \phpseclib\Math\BigInteger('20');
*
* list($quotient, $remainder) = $a->divide($b);
*
* echo $quotient->toString(); // outputs 0
* echo "\r\n";
* echo $remainder->toString(); // outputs 10
* ?>
* </code>
*
* @param \phpseclib\Math\BigInteger $y
* @return array
* @access public
* @internal This function is based off of {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}.
*/
function divide($y)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$quotient = new static();
$remainder = new static();
list($quotient->value, $remainder->value) =
gmp_div_qr($this->value, $y->value);
if (gmp_sign($remainder->value) < 0) {
$remainder->value = gmp_add($remainder->value,
gmp_abs($y->value));
}
return array($this->_normalize($quotient),
$this->_normalize($remainder));
case self::MODE_BCMATH:
$quotient = new static();
$remainder = new static();
$quotient->value = bcdiv($this->value, $y->value,
0);
$remainder->value = bcmod($this->value,
$y->value);
if ($remainder->value[0] == '-') {
$remainder->value = bcadd($remainder->value,
$y->value[0] == '-' ? substr($y->value, 1) : $y->value,
0);
}
return array($this->_normalize($quotient),
$this->_normalize($remainder));
}
if (count($y->value) == 1) {
list($q, $r) = $this->_divide_digit($this->value,
$y->value[0]);
$quotient = new static();
$remainder = new static();
$quotient->value = $q;
$remainder->value = array($r);
$quotient->is_negative = $this->is_negative !=
$y->is_negative;
return array($this->_normalize($quotient),
$this->_normalize($remainder));
}
static $zero;
if (!isset($zero)) {
$zero = new static();
}
$x = $this->copy();
$y = $y->copy();
$x_sign = $x->is_negative;
$y_sign = $y->is_negative;
$x->is_negative = $y->is_negative = false;
$diff = $x->compare($y);
if (!$diff) {
$temp = new static();
$temp->value = array(1);
$temp->is_negative = $x_sign != $y_sign;
return array($this->_normalize($temp),
$this->_normalize(new static()));
}
if ($diff < 0) {
// if $x is negative, "add" $y.
if ($x_sign) {
$x = $y->subtract($x);
}
return array($this->_normalize(new static()),
$this->_normalize($x));
}
// normalize $x and $y as described in HAC 14.23 / 14.24
$msb = $y->value[count($y->value) - 1];
for ($shift = 0; !($msb & self::$msb); ++$shift) {
$msb <<= 1;
}
$x->_lshift($shift);
$y->_lshift($shift);
$y_value = &$y->value;
$x_max = count($x->value) - 1;
$y_max = count($y->value) - 1;
$quotient = new static();
$quotient_value = &$quotient->value;
$quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1);
static $temp, $lhs, $rhs;
if (!isset($temp)) {
$temp = new static();
$lhs = new static();
$rhs = new static();
}
$temp_value = &$temp->value;
$rhs_value = &$rhs->value;
// $temp = $y << ($x_max - $y_max-1) in base 2**26
$temp_value = array_merge($this->_array_repeat(0, $x_max -
$y_max), $y_value);
while ($x->compare($temp) >= 0) {
// calculate the "common residue"
++$quotient_value[$x_max - $y_max];
$x = $x->subtract($temp);
$x_max = count($x->value) - 1;
}
for ($i = $x_max; $i >= $y_max + 1; --$i) {
$x_value = &$x->value;
$x_window = array(
isset($x_value[$i]) ? $x_value[$i] : 0,
isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0,
isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0
);
$y_window = array(
$y_value[$y_max],
($y_max > 0) ? $y_value[$y_max - 1] : 0
);
$q_index = $i - $y_max - 1;
if ($x_window[0] == $y_window[0]) {
$quotient_value[$q_index] = self::$maxDigit;
} else {
$quotient_value[$q_index] = $this->_safe_divide(
$x_window[0] * self::$baseFull + $x_window[1],
$y_window[0]
);
}
$temp_value = array($y_window[1], $y_window[0]);
$lhs->value = array($quotient_value[$q_index]);
$lhs = $lhs->multiply($temp);
$rhs_value = array($x_window[2], $x_window[1], $x_window[0]);
while ($lhs->compare($rhs) > 0) {
--$quotient_value[$q_index];
$lhs->value = array($quotient_value[$q_index]);
$lhs = $lhs->multiply($temp);
}
$adjust = $this->_array_repeat(0, $q_index);
$temp_value = array($quotient_value[$q_index]);
$temp = $temp->multiply($y);
$temp_value = &$temp->value;
if (count($temp_value)) {
$temp_value = array_merge($adjust, $temp_value);
}
$x = $x->subtract($temp);
if ($x->compare($zero) < 0) {
$temp_value = array_merge($adjust, $y_value);
$x = $x->add($temp);
--$quotient_value[$q_index];
}
$x_max = count($x_value) - 1;
}
// unnormalize the remainder
$x->_rshift($shift);
$quotient->is_negative = $x_sign != $y_sign;
// calculate the "common residue", if appropriate
if ($x_sign) {
$y->_rshift($shift);
$x = $y->subtract($x);
}
return array($this->_normalize($quotient),
$this->_normalize($x));
}
/**
* Divides a BigInteger by a regular integer
*
* abc / x = a00 / x + b0 / x + c / x
*
* @param array $dividend
* @param array $divisor
* @return array
* @access private
*/
function _divide_digit($dividend, $divisor)
{
$carry = 0;
$result = array();
for ($i = count($dividend) - 1; $i >= 0; --$i) {
$temp = self::$baseFull * $carry + $dividend[$i];
$result[$i] = $this->_safe_divide($temp, $divisor);
$carry = (int) ($temp - $divisor * $result[$i]);
}
return array($result, $carry);
}
/**
* Performs modular exponentiation.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger('10');
* $b = new \phpseclib\Math\BigInteger('20');
* $c = new \phpseclib\Math\BigInteger('30');
*
* $c = $a->modPow($b, $c);
*
* echo $c->toString(); // outputs 10
* ?>
* </code>
*
* @param \phpseclib\Math\BigInteger $e
* @param \phpseclib\Math\BigInteger $n
* @return \phpseclib\Math\BigInteger
* @access public
* @internal The most naive approach to modular exponentiation has very
unreasonable requirements, and
* and although the approach involving repeated squaring does vastly
better, it, too, is impractical
* for our purposes. The reason being that division - by far the
most complicated and time-consuming
* of the basic operations (eg. +,-,*,/) - occurs multiple times
within it.
*
* Modular reductions resolve this issue. Although an individual
modular reduction takes more time
* then an individual division, when performed in succession (with
the same modulo), they're a lot faster.
*
* The two most commonly used modular reductions are Barrett and
Montgomery reduction. Montgomery reduction,
* although faster, only works when the gcd of the modulo and of the
base being used is 1. In RSA, when the
* base is a power of two, the modulo - a product of two primes - is
always going to have a gcd of 1 (because
* the product of two odd numbers is odd), but what about when RSA
isn't used?
*
* In contrast, Barrett reduction has no such constraint. As such,
some bigint implementations perform a
* Barrett reduction after every operation in the modpow function.
Others perform Barrett reductions when the
* modulo is even and Montgomery reductions when the modulo is odd.
BigInteger.java's modPow method, however,
* uses a trick involving the Chinese Remainder Theorem to factor
the even modulo into two numbers - one odd and
* the other, a power of two - and recombine them, later. This is
the method that this modPow function uses.
* {@link http://islab.oregonstate.edu/papers/j34monex.pdf
Montgomery Reduction with Even Modulus} elaborates.
*/
function modPow($e, $n)
{
$n = $this->bitmask !== false &&
$this->bitmask->compare($n) < 0 ? $this->bitmask :
$n->abs();
if ($e->compare(new static()) < 0) {
$e = $e->abs();
$temp = $this->modInverse($n);
if ($temp === false) {
return false;
}
return $this->_normalize($temp->modPow($e, $n));
}
if (MATH_BIGINTEGER_MODE == self::MODE_GMP) {
$temp = new static();
$temp->value = gmp_powm($this->value, $e->value,
$n->value);
return $this->_normalize($temp);
}
if ($this->compare(new static()) < 0 || $this->compare($n)
> 0) {
list(, $temp) = $this->divide($n);
return $temp->modPow($e, $n);
}
if (defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
$components = array(
'modulus' => $n->toBytes(true),
'publicExponent' => $e->toBytes(true)
);
$components = array(
'modulus' => pack('Ca*a*', 2,
$this->_encodeASN1Length(strlen($components['modulus'])),
$components['modulus']),
'publicExponent' => pack('Ca*a*', 2,
$this->_encodeASN1Length(strlen($components['publicExponent'])),
$components['publicExponent'])
);
$RSAPublicKey = pack(
'Ca*a*a*',
48,
$this->_encodeASN1Length(strlen($components['modulus']) +
strlen($components['publicExponent'])),
$components['modulus'],
$components['publicExponent']
);
$rsaOID = pack('H*',
'300d06092a864886f70d0101010500'); // hex version of
MA0GCSqGSIb3DQEBAQUA
$RSAPublicKey = chr(0) . $RSAPublicKey;
$RSAPublicKey = chr(3) .
$this->_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey;
$encapsulated = pack(
'Ca*a*',
48,
$this->_encodeASN1Length(strlen($rsaOID .
$RSAPublicKey)),
$rsaOID . $RSAPublicKey
);
$RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
chunk_split(base64_encode($encapsulated)) .
'-----END PUBLIC KEY-----';
$plaintext = str_pad($this->toBytes(),
strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT);
if (openssl_public_encrypt($plaintext, $result, $RSAPublicKey,
OPENSSL_NO_PADDING)) {
return new static($result, 256);
}
}
if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
$temp = new static();
$temp->value = bcpowmod($this->value, $e->value,
$n->value, 0);
return $this->_normalize($temp);
}
if (empty($e->value)) {
$temp = new static();
$temp->value = array(1);
return $this->_normalize($temp);
}
if ($e->value == array(1)) {
list(, $temp) = $this->divide($n);
return $this->_normalize($temp);
}
if ($e->value == array(2)) {
$temp = new static();
$temp->value = $this->_square($this->value);
list(, $temp) = $temp->divide($n);
return $this->_normalize($temp);
}
return $this->_normalize($this->_slidingWindow($e, $n,
self::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?
if ($n->value[0] & 1) {
return $this->_normalize($this->_slidingWindow($e, $n,
self::MONTGOMERY));
}
// if it's not, it's even
// find the lowest set bit (eg. the max pow of 2 that divides $n)
for ($i = 0; $i < count($n->value); ++$i) {
if ($n->value[$i]) {
$temp = decbin($n->value[$i]);
$j = strlen($temp) - strrpos($temp, '1') - 1;
$j+= 26 * $i;
break;
}
}
// at this point, 2^$j * $n/(2^$j) == $n
$mod1 = $n->copy();
$mod1->_rshift($j);
$mod2 = new static();
$mod2->value = array(1);
$mod2->_lshift($j);
$part1 = ($mod1->value != array(1)) ?
$this->_slidingWindow($e, $mod1, self::MONTGOMERY) : new static();
$part2 = $this->_slidingWindow($e, $mod2, self::POWEROF2);
$y1 = $mod2->modInverse($mod1);
$y2 = $mod1->modInverse($mod2);
$result = $part1->multiply($mod2);
$result = $result->multiply($y1);
$temp = $part2->multiply($mod1);
$temp = $temp->multiply($y2);
$result = $result->add($temp);
list(, $result) = $result->divide($n);
return $this->_normalize($result);
}
/**
* Performs modular exponentiation.
*
* Alias for modPow().
*
* @param \phpseclib\Math\BigInteger $e
* @param \phpseclib\Math\BigInteger $n
* @return \phpseclib\Math\BigInteger
* @access public
*/
function powMod($e, $n)
{
return $this->modPow($e, $n);
}
/**
* Sliding Window k-ary Modular Exponentiation
*
* Based on {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} /
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM
7.7}. In a departure from those algorithims,
* however, this function performs a modular reduction after every
multiplication and squaring operation.
* As such, this function has the same preconditions that the
reductions being used do.
*
* @param \phpseclib\Math\BigInteger $e
* @param \phpseclib\Math\BigInteger $n
* @param int $mode
* @return \phpseclib\Math\BigInteger
* @access private
*/
function _slidingWindow($e, $n, $mode)
{
static $window_ranges = array(7, 25, 81, 241, 673, 1793); // from
BigInteger.java's oddModPow function
//static $window_ranges = array(0, 7, 36, 140, 450, 1303, 3529); //
from MPM 7.3.1
$e_value = $e->value;
$e_length = count($e_value) - 1;
$e_bits = decbin($e_value[$e_length]);
for ($i = $e_length - 1; $i >= 0; --$i) {
$e_bits.= str_pad(decbin($e_value[$i]), self::$base,
'0', STR_PAD_LEFT);
}
$e_length = strlen($e_bits);
// calculate the appropriate window size.
// $window_size == 3 if $window_ranges is between 25 and 81, for
example.
for ($i = 0, $window_size = 1; $i < count($window_ranges)
&& $e_length > $window_ranges[$i]; ++$window_size, ++$i) {
}
$n_value = $n->value;
// precompute $this^0 through $this^$window_size
$powers = array();
$powers[1] = $this->_prepareReduce($this->value, $n_value,
$mode);
$powers[2] = $this->_squareReduce($powers[1], $n_value, $mode);
// we do every other number since substr($e_bits, $i, $j+1) (see
below) is supposed to end
// in a 1. ie. it's supposed to be odd.
$temp = 1 << ($window_size - 1);
for ($i = 1; $i < $temp; ++$i) {
$i2 = $i << 1;
$powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1],
$powers[2], $n_value, $mode);
}
$result = array(1);
$result = $this->_prepareReduce($result, $n_value, $mode);
for ($i = 0; $i < $e_length;) {
if (!$e_bits[$i]) {
$result = $this->_squareReduce($result, $n_value,
$mode);
++$i;
} else {
for ($j = $window_size - 1; $j > 0; --$j) {
if (!empty($e_bits[$i + $j])) {
break;
}
}
// eg. the length of substr($e_bits, $i, $j + 1)
for ($k = 0; $k <= $j; ++$k) {
$result = $this->_squareReduce($result, $n_value,
$mode);
}
$result = $this->_multiplyReduce($result,
$powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode);
$i += $j + 1;
}
}
$temp = new static();
$temp->value = $this->_reduce($result, $n_value, $mode);
return $temp;
}
/**
* Modular reduction
*
* For most $modes this will return the remainder.
*
* @see self::_slidingWindow()
* @access private
* @param array $x
* @param array $n
* @param int $mode
* @return array
*/
function _reduce($x, $n, $mode)
{
switch ($mode) {
case self::MONTGOMERY:
return $this->_montgomery($x, $n);
case self::BARRETT:
return $this->_barrett($x, $n);
case self::POWEROF2:
$lhs = new static();
$lhs->value = $x;
$rhs = new static();
$rhs->value = $n;
return $x->_mod2($n);
case self::CLASSIC:
$lhs = new static();
$lhs->value = $x;
$rhs = new static();
$rhs->value = $n;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
case self::NONE:
return $x;
default:
// an invalid $mode was provided
}
}
/**
* Modular reduction preperation
*
* @see self::_slidingWindow()
* @access private
* @param array $x
* @param array $n
* @param int $mode
* @return array
*/
function _prepareReduce($x, $n, $mode)
{
if ($mode == self::MONTGOMERY) {
return $this->_prepMontgomery($x, $n);
}
return $this->_reduce($x, $n, $mode);
}
/**
* Modular multiply
*
* @see self::_slidingWindow()
* @access private
* @param array $x
* @param array $y
* @param array $n
* @param int $mode
* @return array
*/
function _multiplyReduce($x, $y, $n, $mode)
{
if ($mode == self::MONTGOMERY) {
return $this->_montgomeryMultiply($x, $y, $n);
}
$temp = $this->_multiply($x, false, $y, false);
return $this->_reduce($temp[self::VALUE], $n, $mode);
}
/**
* Modular square
*
* @see self::_slidingWindow()
* @access private
* @param array $x
* @param array $n
* @param int $mode
* @return array
*/
function _squareReduce($x, $n, $mode)
{
if ($mode == self::MONTGOMERY) {
return $this->_montgomeryMultiply($x, $x, $n);
}
return $this->_reduce($this->_square($x), $n, $mode);
}
/**
* Modulos for Powers of Two
*
* Calculates $x%$n, where $n = 2**$e, for some $e. Since this is
basically the same as doing $x & ($n-1),
* we'll just use this function as a wrapper for doing that.
*
* @see self::_slidingWindow()
* @access private
* @param \phpseclib\Math\BigInteger $n
* @return \phpseclib\Math\BigInteger
*/
function _mod2($n)
{
$temp = new static();
$temp->value = array(1);
return $this->bitwise_and($n->subtract($temp));
}
/**
* Barrett Modular Reduction
*
* See {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3}
/
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM
6.2.5} for more information. Modified slightly,
* so as not to require negative numbers (initially, this script
didn't support negative numbers).
*
* Employs "folding", as described at
* {@link
http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66
thesis-149.pdf#page=66}. To quote from
* it, "the idea [behind folding] is to find a value x' such
that x (mod m) = x' (mod m), with x' being smaller than x."
*
* Unfortunately, the "Barrett Reduction with Folding"
algorithm described in thesis-149.pdf is not, as written, all that
* usable on account of (1) its not using reasonable radix points as
discussed in
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM
6.2.2} and (2) the fact that, even with reasonable
* radix points, it only works when there are an even number of digits
in the denominator. The reason for (2) is that
* (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even,
they're the same, but if x is odd, they're not. See the in-line
* comments for details.
*
* @see self::_slidingWindow()
* @access private
* @param array $n
* @param array $m
* @return array
*/
function _barrett($n, $m)
{
static $cache = array(
self::VARIABLE => array(),
self::DATA => array()
);
$m_length = count($m);
// if ($this->_compare($n, $this->_square($m)) >= 0) {
if (count($n) > 2 * $m_length) {
$lhs = new static();
$rhs = new static();
$lhs->value = $n;
$rhs->value = $m;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
}
// if (m.length >> 1) + 2 <= m.length then m is too small
and n can't be reduced
if ($m_length < 5) {
return $this->_regularBarrett($n, $m);
}
// n = 2 * m.length
if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]);
$cache[self::VARIABLE][] = $m;
$lhs = new static();
$lhs_value = &$lhs->value;
$lhs_value = $this->_array_repeat(0, $m_length + ($m_length
>> 1));
$lhs_value[] = 1;
$rhs = new static();
$rhs->value = $m;
list($u, $m1) = $lhs->divide($rhs);
$u = $u->value;
$m1 = $m1->value;
$cache[self::DATA][] = array(
'u' => $u, // m.length >> 1 (technically
(m.length >> 1) + 1)
'm1'=> $m1 // m.length
);
} else {
extract($cache[self::DATA][$key]);
}
$cutoff = $m_length + ($m_length >> 1);
$lsd = array_slice($n, 0, $cutoff); // m.length + (m.length
>> 1)
$msd = array_slice($n, $cutoff); // m.length >> 1
$lsd = $this->_trim($lsd);
$temp = $this->_multiply($msd, false, $m1, false);
$n = $this->_add($lsd, false, $temp[self::VALUE], false); //
m.length + (m.length >> 1) + 1
if ($m_length & 1) {
return $this->_regularBarrett($n[self::VALUE], $m);
}
// (m.length + (m.length >> 1) + 1) - (m.length - 1) ==
(m.length >> 1) + 2
$temp = array_slice($n[self::VALUE], $m_length - 1);
// if even: ((m.length >> 1) + 2) + (m.length >> 1) ==
m.length + 2
// if odd: ((m.length >> 1) + 2) + (m.length >> 1) ==
(m.length - 1) + 2 == m.length + 1
$temp = $this->_multiply($temp, false, $u, false);
// if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length
- (m.length >> 1) + 1
// if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length
- (m.length >> 1)
$temp = array_slice($temp[self::VALUE], ($m_length >> 1) +
1);
// if even: (m.length - (m.length >> 1) + 1) + m.length = 2 *
m.length - (m.length >> 1) + 1
// if odd: (m.length - (m.length >> 1)) + m.length = 2 *
m.length - (m.length >> 1)
$temp = $this->_multiply($temp, false, $m, false);
// at this point, if m had an odd number of digits, we'd be
subtracting a 2 * m.length - (m.length >> 1) digit
// number from a m.length + (m.length >> 1) + 1 digit number.
ie. there'd be an extra digit and the while loop
// following this comment would loop a lot (hence our calling
_regularBarrett() in that situation).
$result = $this->_subtract($n[self::VALUE], false,
$temp[self::VALUE], false);
while ($this->_compare($result[self::VALUE],
$result[self::SIGN], $m, false) >= 0) {
$result = $this->_subtract($result[self::VALUE],
$result[self::SIGN], $m, false);
}
return $result[self::VALUE];
}
/**
* (Regular) Barrett Modular Reduction
*
* For numbers with more than four digits BigInteger::_barrett() is
faster. The difference between that and this
* is that this function does not fold the denominator into a smaller
form.
*
* @see self::_slidingWindow()
* @access private
* @param array $x
* @param array $n
* @return array
*/
function _regularBarrett($x, $n)
{
static $cache = array(
self::VARIABLE => array(),
self::DATA => array()
);
$n_length = count($n);
if (count($x) > 2 * $n_length) {
$lhs = new static();
$rhs = new static();
$lhs->value = $x;
$rhs->value = $n;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
}
if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]);
$cache[self::VARIABLE][] = $n;
$lhs = new static();
$lhs_value = &$lhs->value;
$lhs_value = $this->_array_repeat(0, 2 * $n_length);
$lhs_value[] = 1;
$rhs = new static();
$rhs->value = $n;
list($temp, ) = $lhs->divide($rhs); // m.length
$cache[self::DATA][] = $temp->value;
}
// 2 * m.length - (m.length - 1) = m.length + 1
$temp = array_slice($x, $n_length - 1);
// (m.length + 1) + m.length = 2 * m.length + 1
$temp = $this->_multiply($temp, false, $cache[self::DATA][$key],
false);
// (2 * m.length + 1) - (m.length - 1) = m.length + 2
$temp = array_slice($temp[self::VALUE], $n_length + 1);
// m.length + 1
$result = array_slice($x, 0, $n_length + 1);
// m.length + 1
$temp = $this->_multiplyLower($temp, false, $n, false, $n_length
+ 1);
// $temp == array_slice($temp->_multiply($temp, false, $n,
false)->value, 0, $n_length + 1)
if ($this->_compare($result, false, $temp[self::VALUE],
$temp[self::SIGN]) < 0) {
$corrector_value = $this->_array_repeat(0, $n_length + 1);
$corrector_value[count($corrector_value)] = 1;
$result = $this->_add($result, false, $corrector_value,
false);
$result = $result[self::VALUE];
}
// at this point, we're subtracting a number with m.length + 1
digits from another number with m.length + 1 digits
$result = $this->_subtract($result, false, $temp[self::VALUE],
$temp[self::SIGN]);
while ($this->_compare($result[self::VALUE],
$result[self::SIGN], $n, false) > 0) {
$result = $this->_subtract($result[self::VALUE],
$result[self::SIGN], $n, false);
}
return $result[self::VALUE];
}
/**
* Performs long multiplication up to $stop digits
*
* If you're going to be doing array_slice($product->value, 0,
$stop), some cycles can be saved.
*
* @see self::_regularBarrett()
* @param array $x_value
* @param bool $x_negative
* @param array $y_value
* @param bool $y_negative
* @param int $stop
* @return array
* @access private
*/
function _multiplyLower($x_value, $x_negative, $y_value, $y_negative,
$stop)
{
$x_length = count($x_value);
$y_length = count($y_value);
if (!$x_length || !$y_length) { // a 0 is being multiplied
return array(
self::VALUE => array(),
self::SIGN => false
);
}
if ($x_length < $y_length) {
$temp = $x_value;
$x_value = $y_value;
$y_value = $temp;
$x_length = count($x_value);
$y_length = count($y_value);
}
$product_value = $this->_array_repeat(0, $x_length + $y_length);
// the following for loop could be removed if the for loop
following it
// (the one with nested for loops) initially set $i to 0, but
// doing so would also make the result in one set of unnecessary
adds,
// since on the outermost loops first pass, $product->value[$k]
is going
// to always be 0
$carry = 0;
for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i
$temp = $x_value[$j] * $y_value[0] + $carry; //
$product_value[$k] == 0
$carry = self::$base === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$product_value[$j] = (int) ($temp - self::$baseFull * $carry);
}
if ($j < $stop) {
$product_value[$j] = $carry;
}
// the above for loop is what the previous comment was talking
about. the
// following for loop is the "one with nested for loops"
for ($i = 1; $i < $y_length; ++$i) {
$carry = 0;
for ($j = 0, $k = $i; $j < $x_length && $k <
$stop; ++$j, ++$k) {
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] +
$carry;
$carry = self::$base === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$product_value[$k] = (int) ($temp - self::$baseFull *
$carry);
}
if ($k < $stop) {
$product_value[$k] = $carry;
}
}
return array(
self::VALUE => $this->_trim($product_value),
self::SIGN => $x_negative != $y_negative
);
}
/**
* Montgomery Modular Reduction
*
* ($x->_prepMontgomery($n))->_montgomery($n) yields $x % $n.
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=170 MPM
6.3} provides insights on how this can be
* improved upon (basically, by using the comba method). gcd($n, 2)
must be equal to one for this function
* to work correctly.
*
* @see self::_prepMontgomery()
* @see self::_slidingWindow()
* @access private
* @param array $x
* @param array $n
* @return array
*/
function _montgomery($x, $n)
{
static $cache = array(
self::VARIABLE => array(),
self::DATA => array()
);
if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]);
$cache[self::VARIABLE][] = $x;
$cache[self::DATA][] = $this->_modInverse67108864($n);
}
$k = count($n);
$result = array(self::VALUE => $x);
for ($i = 0; $i < $k; ++$i) {
$temp = $result[self::VALUE][$i] * $cache[self::DATA][$key];
$temp = $temp - self::$baseFull * (self::$base === 26 ?
intval($temp / 0x4000000) : ($temp >> 31));
$temp = $this->_regularMultiply(array($temp), $n);
$temp = array_merge($this->_array_repeat(0, $i), $temp);
$result = $this->_add($result[self::VALUE], false, $temp,
false);
}
$result[self::VALUE] = array_slice($result[self::VALUE], $k);
if ($this->_compare($result, false, $n, false) >= 0) {
$result = $this->_subtract($result[self::VALUE], false, $n,
false);
}
return $result[self::VALUE];
}
/**
* Montgomery Multiply
*
* Interleaves the montgomery reduction and long multiplication
algorithms together as described in
* {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
*
* @see self::_prepMontgomery()
* @see self::_montgomery()
* @access private
* @param array $x
* @param array $y
* @param array $m
* @return array
*/
function _montgomeryMultiply($x, $y, $m)
{
$temp = $this->_multiply($x, false, $y, false);
return $this->_montgomery($temp[self::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(
self::VARIABLE => array(),
self::DATA => array()
);
if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]);
$cache[self::VARIABLE][] = $m;
$cache[self::DATA][] = $this->_modInverse67108864($m);
}
$n = max(count($x), count($y), count($m));
$x = array_pad($x, $n, 0);
$y = array_pad($y, $n, 0);
$m = array_pad($m, $n, 0);
$a = array(self::VALUE => $this->_array_repeat(0, $n + 1));
for ($i = 0; $i < $n; ++$i) {
$temp = $a[self::VALUE][0] + $x[$i] * $y[0];
$temp = $temp - self::$baseFull * (self::$base === 26 ?
intval($temp / 0x4000000) : ($temp >> 31));
$temp = $temp * $cache[self::DATA][$key];
$temp = $temp - self::$baseFull * (self::$base === 26 ?
intval($temp / 0x4000000) : ($temp >> 31));
$temp =
$this->_add($this->_regularMultiply(array($x[$i]), $y), false,
$this->_regularMultiply(array($temp), $m), false);
$a = $this->_add($a[self::VALUE], false, $temp[self::VALUE],
false);
$a[self::VALUE] = array_slice($a[self::VALUE], 1);
}
if ($this->_compare($a[self::VALUE], false, $m, false) >= 0)
{
$a = $this->_subtract($a[self::VALUE], false, $m, false);
}
return $a[self::VALUE];
}
/**
* Prepare a number for use in Montgomery Modular Reductions
*
* @see self::_montgomery()
* @see self::_slidingWindow()
* @access private
* @param array $x
* @param array $n
* @return array
*/
function _prepMontgomery($x, $n)
{
$lhs = new static();
$lhs->value = array_merge($this->_array_repeat(0, count($n)),
$x);
$rhs = new static();
$rhs->value = $n;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
}
/**
* Modular Inverse of a number mod 2**26 (eg. 67108864)
*
* Based off of the bnpInvDigit function implemented and justified in
the following URL:
*
* {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js}
*
* The following URL provides more info:
*
* {@link
http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85}
*
* As for why we do all the bitmasking... strange things can happen
when converting from floats to ints. For
* instance, on some computers, var_dump((int) -4294967297) yields
int(-1) and on others, it yields
* int(-2147483648). To avoid problems stemming from this, we use
bitmasks to guarantee that ints aren't
* auto-converted to floats. The outermost bitmask is present because
without it, there's no guarantee that
* the "residue" returned would be the so-called "common
residue". We use fmod, in the last step, because the
* maximum possible $x is 26 bits and the maximum $result is 16 bits.
Thus, we have to be able to handle up to
* 40 bits, which only 64-bit floating points will support.
*
* Thanks to Pedro Gimeno Fortea for input!
*
* @see self::_montgomery()
* @access private
* @param array $x
* @return int
*/
function _modInverse67108864($x) // 2**26 == 67,108,864
{
$x = -$x[0];
$result = $x & 0x3; // x**-1 mod 2**2
$result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod
2**4
$result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF;
// x**-1 mod 2**8
$result = ($result * ((2 - ($x & 0xFFFF) * $result) &
0xFFFF)) & 0xFFFF; // x**-1 mod 2**16
$result = fmod($result * (2 - fmod($x * $result, self::$baseFull)),
self::$baseFull); // x**-1 mod 2**26
return $result & self::$maxDigit;
}
/**
* Calculates modular inverses.
*
* Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found
using modular inverses.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger(30);
* $b = new \phpseclib\Math\BigInteger(17);
*
* $c = $a->modInverse($b);
* echo $c->toString(); // outputs 4
*
* echo "\r\n";
*
* $d = $a->multiply($c);
* list(, $d) = $d->divide($b);
* echo $d; // outputs 1 (as per the definition of modular inverse)
* ?>
* </code>
*
* @param \phpseclib\Math\BigInteger $n
* @return \phpseclib\Math\BigInteger|false
* @access public
* @internal See {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64}
for more information.
*/
function modInverse($n)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_invert($this->value,
$n->value);
return ($temp->value === false) ? false :
$this->_normalize($temp);
}
static $zero, $one;
if (!isset($zero)) {
$zero = new static();
$one = new static(1);
}
// $x mod -$n == $x mod $n.
$n = $n->abs();
if ($this->compare($zero) < 0) {
$temp = $this->abs();
$temp = $temp->modInverse($n);
return $this->_normalize($n->subtract($temp));
}
extract($this->extendedGCD($n));
if (!$gcd->equals($one)) {
return false;
}
$x = $x->compare($zero) < 0 ? $x->add($n) : $x;
return $this->compare($zero) < 0 ?
$this->_normalize($n->subtract($x)) : $this->_normalize($x);
}
/**
* Calculates the greatest common divisor and Bezout's identity.
*
* Say you have 693 and 609. The GCD is 21. Bezout's identity
states that there exist integers x and y such that
* 693*x + 609*y == 21. In point of fact, there are actually an
infinite number of x and y combinations and which
* combination is returned is dependent upon which mode is in use. See
* {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity
Bezout's identity - Wikipedia} for more information.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger(693);
* $b = new \phpseclib\Math\BigInteger(609);
*
* extract($a->extendedGCD($b));
*
* echo $gcd->toString() . "\r\n"; // outputs 21
* echo $a->toString() * $x->toString() + $b->toString() *
$y->toString(); // outputs 21
* ?>
* </code>
*
* @param \phpseclib\Math\BigInteger $n
* @return \phpseclib\Math\BigInteger
* @access public
* @internal Calculates the GCD using the binary xGCD algorithim
described in
* {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}.
As the text above 14.61 notes,
* the more traditional algorithim requires "relatively costly
multiple-precision divisions".
*/
function extendedGCD($n)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
extract(gmp_gcdext($this->value, $n->value));
return array(
'gcd' => $this->_normalize(new
static($g)),
'x' => $this->_normalize(new
static($s)),
'y' => $this->_normalize(new
static($t))
);
case self::MODE_BCMATH:
// it might be faster to use the binary xGCD algorithim
here, as well, but (1) that algorithim works
// best when the base is a power of 2 and (2) i don't
think it'd make much difference, anyway. as is,
// the basic extended euclidean algorithim is what
we're using.
$u = $this->value;
$v = $n->value;
$a = '1';
$b = '0';
$c = '0';
$d = '1';
while (bccomp($v, '0', 0) != 0) {
$q = bcdiv($u, $v, 0);
$temp = $u;
$u = $v;
$v = bcsub($temp, bcmul($v, $q, 0), 0);
$temp = $a;
$a = $c;
$c = bcsub($temp, bcmul($a, $q, 0), 0);
$temp = $b;
$b = $d;
$d = bcsub($temp, bcmul($b, $q, 0), 0);
}
return array(
'gcd' => $this->_normalize(new
static($u)),
'x' => $this->_normalize(new
static($a)),
'y' => $this->_normalize(new
static($b))
);
}
$y = $n->copy();
$x = $this->copy();
$g = new static();
$g->value = array(1);
while (!(($x->value[0] & 1)|| ($y->value[0] & 1))) {
$x->_rshift(1);
$y->_rshift(1);
$g->_lshift(1);
}
$u = $x->copy();
$v = $y->copy();
$a = new static();
$b = new static();
$c = new static();
$d = new static();
$a->value = $d->value = $g->value = array(1);
$b->value = $c->value = array();
while (!empty($u->value)) {
while (!($u->value[0] & 1)) {
$u->_rshift(1);
if ((!empty($a->value) && ($a->value[0] &
1)) || (!empty($b->value) && ($b->value[0] & 1))) {
$a = $a->add($y);
$b = $b->subtract($x);
}
$a->_rshift(1);
$b->_rshift(1);
}
while (!($v->value[0] & 1)) {
$v->_rshift(1);
if ((!empty($d->value) && ($d->value[0] &
1)) || (!empty($c->value) && ($c->value[0] & 1))) {
$c = $c->add($y);
$d = $d->subtract($x);
}
$c->_rshift(1);
$d->_rshift(1);
}
if ($u->compare($v) >= 0) {
$u = $u->subtract($v);
$a = $a->subtract($c);
$b = $b->subtract($d);
} else {
$v = $v->subtract($u);
$c = $c->subtract($a);
$d = $d->subtract($b);
}
}
return array(
'gcd' =>
$this->_normalize($g->multiply($v)),
'x' => $this->_normalize($c),
'y' => $this->_normalize($d)
);
}
/**
* Calculates the greatest common divisor
*
* Say you have 693 and 609. The GCD is 21.
*
* Here's an example:
* <code>
* <?php
* $a = new \phpseclib\Math\BigInteger(693);
* $b = new \phpseclib\Math\BigInteger(609);
*
* $gcd = a->extendedGCD($b);
*
* echo $gcd->toString() . "\r\n"; // outputs 21
* ?>
* </code>
*
* @param \phpseclib\Math\BigInteger $n
* @return \phpseclib\Math\BigInteger
* @access public
*/
function gcd($n)
{
extract($this->extendedGCD($n));
return $gcd;
}
/**
* Absolute value.
*
* @return \phpseclib\Math\BigInteger
* @access public
*/
function abs()
{
$temp = new static();
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp->value = gmp_abs($this->value);
break;
case self::MODE_BCMATH:
$temp->value = (bccomp($this->value, '0',
0) < 0) ? substr($this->value, 1) : $this->value;
break;
default:
$temp->value = $this->value;
}
return $temp;
}
/**
* Compares two numbers.
*
* Although one might think !$x->compare($y) means $x != $y, it, in
fact, means the opposite. The reason for this is
* demonstrated thusly:
*
* $x > $y: $x->compare($y) > 0
* $x < $y: $x->compare($y) < 0
* $x == $y: $x->compare($y) == 0
*
* Note how the same comparison operator is used. If you want to test
for equality, use $x->equals($y).
*
* @param \phpseclib\Math\BigInteger $y
* @return int that is < 0 if $this is less than $y; > 0 if $this
is greater than $y, and 0 if they are equal.
* @access public
* @see self::equals()
* @internal Could return $this->subtract($x), but that's not
as fast as what we do do.
*/
function compare($y)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$r = gmp_cmp($this->value, $y->value);
if ($r < -1) {
$r = -1;
}
if ($r > 1) {
$r = 1;
}
return $r;
case self::MODE_BCMATH:
return bccomp($this->value, $y->value, 0);
}
return $this->_compare($this->value, $this->is_negative,
$y->value, $y->is_negative);
}
/**
* Compares two numbers.
*
* @param array $x_value
* @param bool $x_negative
* @param array $y_value
* @param bool $y_negative
* @return int
* @see self::compare()
* @access private
*/
function _compare($x_value, $x_negative, $y_value, $y_negative)
{
if ($x_negative != $y_negative) {
return (!$x_negative && $y_negative) ? 1 : -1;
}
$result = $x_negative ? -1 : 1;
if (count($x_value) != count($y_value)) {
return (count($x_value) > count($y_value)) ? $result :
-$result;
}
$size = max(count($x_value), count($y_value));
$x_value = array_pad($x_value, $size, 0);
$y_value = array_pad($y_value, $size, 0);
for ($i = count($x_value) - 1; $i >= 0; --$i) {
if ($x_value[$i] != $y_value[$i]) {
return ($x_value[$i] > $y_value[$i]) ? $result :
-$result;
}
}
return 0;
}
/**
* Tests the equality of two numbers.
*
* If you need to see if one number is greater than or less than
another number, use BigInteger::compare()
*
* @param \phpseclib\Math\BigInteger $x
* @return bool
* @access public
* @see self::compare()
*/
function equals($x)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
return gmp_cmp($this->value, $x->value) == 0;
default:
return $this->value === $x->value &&
$this->is_negative == $x->is_negative;
}
}
/**
* Set Precision
*
* Some bitwise operations give different results depending on the
precision being used. Examples include left
* shift, not, and rotates.
*
* @param int $bits
* @access public
*/
function setPrecision($bits)
{
$this->precision = $bits;
if (MATH_BIGINTEGER_MODE != self::MODE_BCMATH) {
$this->bitmask = new static(chr((1 << ($bits &
0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256);
} else {
$this->bitmask = new static(bcpow('2', $bits, 0));
}
$temp = $this->_normalize($this);
$this->value = $temp->value;
}
/**
* Logical And
*
* @param \phpseclib\Math\BigInteger $x
* @access public
* @internal Implemented per a request by Lluis Pamies i Juarez
<lluis _a_ pamies.cat>
* @return \phpseclib\Math\BigInteger
*/
function bitwise_and($x)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_and($this->value, $x->value);
return $this->_normalize($temp);
case self::MODE_BCMATH:
$left = $this->toBytes();
$right = $x->toBytes();
$length = max(strlen($left), strlen($right));
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
return $this->_normalize(new static($left & $right,
256));
}
$result = $this->copy();
$length = min(count($x->value), count($this->value));
$result->value = array_slice($result->value, 0, $length);
for ($i = 0; $i < $length; ++$i) {
$result->value[$i]&= $x->value[$i];
}
return $this->_normalize($result);
}
/**
* Logical Or
*
* @param \phpseclib\Math\BigInteger $x
* @access public
* @internal Implemented per a request by Lluis Pamies i Juarez
<lluis _a_ pamies.cat>
* @return \phpseclib\Math\BigInteger
*/
function bitwise_or($x)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_or($this->value, $x->value);
return $this->_normalize($temp);
case self::MODE_BCMATH:
$left = $this->toBytes();
$right = $x->toBytes();
$length = max(strlen($left), strlen($right));
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
return $this->_normalize(new static($left | $right,
256));
}
$length = max(count($this->value), count($x->value));
$result = $this->copy();
$result->value = array_pad($result->value, $length, 0);
$x->value = array_pad($x->value, $length, 0);
for ($i = 0; $i < $length; ++$i) {
$result->value[$i]|= $x->value[$i];
}
return $this->_normalize($result);
}
/**
* Logical Exclusive-Or
*
* @param \phpseclib\Math\BigInteger $x
* @access public
* @internal Implemented per a request by Lluis Pamies i Juarez
<lluis _a_ pamies.cat>
* @return \phpseclib\Math\BigInteger
*/
function bitwise_xor($x)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_xor(gmp_abs($this->value),
gmp_abs($x->value));
return $this->_normalize($temp);
case self::MODE_BCMATH:
$left = $this->toBytes();
$right = $x->toBytes();
$length = max(strlen($left), strlen($right));
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
return $this->_normalize(new static($left ^ $right,
256));
}
$length = max(count($this->value), count($x->value));
$result = $this->copy();
$result->is_negative = false;
$result->value = array_pad($result->value, $length, 0);
$x->value = array_pad($x->value, $length, 0);
for ($i = 0; $i < $length; ++$i) {
$result->value[$i]^= $x->value[$i];
}
return $this->_normalize($result);
}
/**
* Logical Not
*
* @access public
* @internal Implemented per a request by Lluis Pamies i Juarez
<lluis _a_ pamies.cat>
* @return \phpseclib\Math\BigInteger
*/
function bitwise_not()
{
// calculuate "not" without regard to $this->precision
// (will always result in a smaller number. ie. ~1 isn't 1111
1110 - it's 0)
$temp = $this->toBytes();
if ($temp == '') {
return $this->_normalize(new static());
}
$pre_msb = decbin(ord($temp[0]));
$temp = ~$temp;
$msb = decbin(ord($temp[0]));
if (strlen($msb) == 8) {
$msb = substr($msb, strpos($msb, '0'));
}
$temp[0] = chr(bindec($msb));
// see if we need to add extra leading 1's
$current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8;
$new_bits = $this->precision - $current_bits;
if ($new_bits <= 0) {
return $this->_normalize(new static($temp, 256));
}
// generate as many leading 1's as we need to.
$leading_ones = chr((1 << ($new_bits & 0x7)) - 1) .
str_repeat(chr(0xFF), $new_bits >> 3);
$this->_base256_lshift($leading_ones, $current_bits);
$temp = str_pad($temp, strlen($leading_ones), chr(0),
STR_PAD_LEFT);
return $this->_normalize(new static($leading_ones | $temp,
256));
}
/**
* Logical Right Shift
*
* Shifts BigInteger's by $shift bits, effectively dividing by
2**$shift.
*
* @param int $shift
* @return \phpseclib\Math\BigInteger
* @access public
* @internal The only version that yields any speed increases is the
internal version.
*/
function bitwise_rightShift($shift)
{
$temp = new static();
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
static $two;
if (!isset($two)) {
$two = gmp_init('2');
}
$temp->value = gmp_div_q($this->value, gmp_pow($two,
$shift));
break;
case self::MODE_BCMATH:
$temp->value = bcdiv($this->value,
bcpow('2', $shift, 0), 0);
break;
default: // could just replace _lshift with this, but then all
_lshift() calls would need to be rewritten
// and I don't want to do that...
$temp->value = $this->value;
$temp->_rshift($shift);
}
return $this->_normalize($temp);
}
/**
* Logical Left Shift
*
* Shifts BigInteger's by $shift bits, effectively multiplying by
2**$shift.
*
* @param int $shift
* @return \phpseclib\Math\BigInteger
* @access public
* @internal The only version that yields any speed increases is the
internal version.
*/
function bitwise_leftShift($shift)
{
$temp = new static();
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
static $two;
if (!isset($two)) {
$two = gmp_init('2');
}
$temp->value = gmp_mul($this->value, gmp_pow($two,
$shift));
break;
case self::MODE_BCMATH:
$temp->value = bcmul($this->value,
bcpow('2', $shift, 0), 0);
break;
default: // could just replace _rshift with this, but then all
_lshift() calls would need to be rewritten
// and I don't want to do that...
$temp->value = $this->value;
$temp->_lshift($shift);
}
return $this->_normalize($temp);
}
/**
* Logical Left Rotate
*
* Instead of the top x bits being dropped they're appended to the
shifted bit string.
*
* @param int $shift
* @return \phpseclib\Math\BigInteger
* @access public
*/
function bitwise_leftRotate($shift)
{
$bits = $this->toBytes();
if ($this->precision > 0) {
$precision = $this->precision;
if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
$mask = $this->bitmask->subtract(new static(1));
$mask = $mask->toBytes();
} else {
$mask = $this->bitmask->toBytes();
}
} else {
$temp = ord($bits[0]);
for ($i = 0; $temp >> $i; ++$i) {
}
$precision = 8 * strlen($bits) - 8 + $i;
$mask = chr((1 << ($precision & 0x7)) - 1) .
str_repeat(chr(0xFF), $precision >> 3);
}
if ($shift < 0) {
$shift+= $precision;
}
$shift%= $precision;
if (!$shift) {
return $this->copy();
}
$left = $this->bitwise_leftShift($shift);
$left = $left->bitwise_and(new static($mask, 256));
$right = $this->bitwise_rightShift($precision - $shift);
$result = MATH_BIGINTEGER_MODE != self::MODE_BCMATH ?
$left->bitwise_or($right) : $left->add($right);
return $this->_normalize($result);
}
/**
* Logical Right Rotate
*
* Instead of the bottom x bits being dropped they're prepended to
the shifted bit string.
*
* @param int $shift
* @return \phpseclib\Math\BigInteger
* @access public
*/
function bitwise_rightRotate($shift)
{
return $this->bitwise_leftRotate(-$shift);
}
/**
* Generates a random BigInteger
*
* Byte length is equal to $length. Uses \phpseclib\Crypt\Random if
it's loaded and mt_rand if it's not.
*
* @param int $size
* @return \phpseclib\Math\BigInteger
* @access private
*/
function _random_number_helper($size)
{
if (class_exists('\phpseclib\Crypt\Random')) {
$random = Random::string($size);
} else {
$random = '';
if ($size & 1) {
$random.= chr(mt_rand(0, 255));
}
$blocks = $size >> 1;
for ($i = 0; $i < $blocks; ++$i) {
// mt_rand(-2147483648, 0x7FFFFFFF) always produces
-2147483648 on some systems
$random.= pack('n', mt_rand(0, 0xFFFF));
}
}
return new static($random, 256);
}
/**
* 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 \phpseclib\Math\BigInteger $arg1
* @param \phpseclib\Math\BigInteger $arg2
* @return \phpseclib\Math\BigInteger
* @access public
* @internal The API for creating random numbers used to be
$a->random($min, $max), where $a was a 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 {
$min = $arg1;
$max = $arg2;
}
$compare = $max->compare($min);
if (!$compare) {
return $this->_normalize($min);
} elseif ($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 static(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 static(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));
}
/**
* Generate a random prime number.
*
* 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.
*
* @param \phpseclib\Math\BigInteger $arg1
* @param \phpseclib\Math\BigInteger $arg2
* @param int $timeout
* @return Math_BigInteger|false
* @access public
* @internal See {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}.
*/
function randomPrime($arg1, $arg2 = false, $timeout = false)
{
if ($arg1 === false) {
return false;
}
if ($arg2 === false) {
$max = $arg1;
$min = $this;
} else {
$min = $arg1;
$max = $arg2;
}
$compare = $max->compare($min);
if (!$compare) {
return $min->isPrime() ? $min : false;
} elseif ($compare < 0) {
// if $min is bigger then $max, swap $min and $max
$temp = $max;
$max = $min;
$min = $temp;
}
static $one, $two;
if (!isset($one)) {
$one = new static(1);
$two = new static(2);
}
$start = time();
$x = $this->random($min, $max);
// gmp_nextprime() requires PHP 5 >= 5.2.0 per
<http://php.net/gmp-nextprime>.
if (MATH_BIGINTEGER_MODE == self::MODE_GMP &&
extension_loaded('gmp')) {
$p = new static();
$p->value = gmp_nextprime($x->value);
if ($p->compare($max) <= 0) {
return $p;
}
if (!$min->equals($x)) {
$x = $x->subtract($one);
}
return $x->randomPrime($min, $x);
}
if ($x->equals($two)) {
return $x;
}
$x->_make_odd();
if ($x->compare($max) > 0) {
// if $x > $max then $max is even and if $min == $max then
no prime number exists between the specified range
if ($min->equals($max)) {
return false;
}
$x = $min->copy();
$x->_make_odd();
}
$initial_x = $x->copy();
while (true) {
if ($timeout !== false && time() - $start >
$timeout) {
return false;
}
if ($x->isPrime()) {
return $x;
}
$x = $x->add($two);
if ($x->compare($max) > 0) {
$x = $min->copy();
if ($x->equals($two)) {
return $x;
}
$x->_make_odd();
}
if ($x->equals($initial_x)) {
return false;
}
}
}
/**
* Make the current number odd
*
* If the current number is odd it'll be unchanged. If it's
even, one will be added to it.
*
* @see self::randomPrime()
* @access private
*/
function _make_odd()
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
gmp_setbit($this->value, 0);
break;
case self::MODE_BCMATH:
if ($this->value[strlen($this->value) - 1] % 2 == 0)
{
$this->value = bcadd($this->value,
'1');
}
break;
default:
$this->value[0] |= 1;
}
}
/**
* 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
* $t parameter is distributability. BigInteger::randomPrime() can be
distributed across multiple pageloads
* on a website instead of just one.
*
* @param \phpseclib\Math\BigInteger $t
* @return bool
* @access public
* @internal Uses the
* {@link
http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test
Miller-Rabin primality test}. See
* {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24}.
*/
function isPrime($t = false)
{
$length = strlen($this->toBytes());
if (!$t) {
// see HAC 4.49 "Note (controlling the error
probability)"
// @codingStandardsIgnoreStart
if ($length >= 163) { $t = 2; } // floor(1300 / 8)
else if ($length >= 106) { $t = 3; } // floor( 850 / 8)
else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8)
else if ($length >= 68 ) { $t = 5; } // floor( 550 / 8)
else if ($length >= 56 ) { $t = 6; } // floor( 450 / 8)
else if ($length >= 50 ) { $t = 7; } // floor( 400 / 8)
else if ($length >= 43 ) { $t = 8; } // floor( 350 / 8)
else if ($length >= 37 ) { $t = 9; } // floor( 300 / 8)
else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8)
else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8)
else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8)
else { $t = 27; }
// @codingStandardsIgnoreEnd
}
// ie. gmp_testbit($this, 0)
// ie. isEven() or !isOdd()
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
return gmp_prob_prime($this->value, $t) != 0;
case self::MODE_BCMATH:
if ($this->value === '2') {
return true;
}
if ($this->value[strlen($this->value) - 1] % 2 == 0)
{
return false;
}
break;
default:
if ($this->value == array(2)) {
return true;
}
if (~$this->value[0] & 1) {
return false;
}
}
static $primes, $zero, $one, $two;
if (!isset($primes)) {
$primes = array(
3, 5, 7, 11, 13, 17, 19, 23, 29, 31,
37, 41, 43, 47, 53, 59,
61, 67, 71, 73, 79, 83, 89, 97, 101, 103,
107, 109, 113, 127, 131, 137,
139, 149, 151, 157, 163, 167, 173, 179, 181, 191,
193, 197, 199, 211, 223, 227,
229, 233, 239, 241, 251, 257, 263, 269, 271, 277,
281, 283, 293, 307, 311, 313,
317, 331, 337, 347, 349, 353, 359, 367, 373, 379,
383, 389, 397, 401, 409, 419,
421, 431, 433, 439, 443, 449, 457, 461, 463, 467,
479, 487, 491, 499, 503, 509,
521, 523, 541, 547, 557, 563, 569, 571, 577, 587,
593, 599, 601, 607, 613, 617,
619, 631, 641, 643, 647, 653, 659, 661, 673, 677,
683, 691, 701, 709, 719, 727,
733, 739, 743, 751, 757, 761, 769, 773, 787, 797,
809, 811, 821, 823, 827, 829,
839, 853, 857, 859, 863, 877, 881, 883, 887, 907,
911, 919, 929, 937, 941, 947,
953, 967, 971, 977, 983, 991, 997
);
if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
for ($i = 0; $i < count($primes); ++$i) {
$primes[$i] = new static($primes[$i]);
}
}
$zero = new static();
$one = new static(1);
$two = new static(2);
}
if ($this->equals($one)) {
return false;
}
// see HAC 4.4.1 "Random search for probable primes"
if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
foreach ($primes as $prime) {
list(, $r) = $this->divide($prime);
if ($r->equals($zero)) {
return $this->equals($prime);
}
}
} else {
$value = $this->value;
foreach ($primes as $prime) {
list(, $r) = $this->_divide_digit($value, $prime);
if (!$r) {
return count($value) == 1 && $value[0] ==
$prime;
}
}
}
$n = $this->copy();
$n_1 = $n->subtract($one);
$n_2 = $n->subtract($two);
$r = $n_1->copy();
$r_value = $r->value;
// ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n,
gmp_pow(gmp_init('2'), $s));
if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
$s = 0;
// if $n was 1, $r would be 0 and this would be an infinite
loop, hence our $this->equals($one) check earlier
while ($r->value[strlen($r->value) - 1] % 2 == 0) {
$r->value = bcdiv($r->value, '2', 0);
++$s;
}
} else {
for ($i = 0, $r_length = count($r_value); $i < $r_length;
++$i) {
$temp = ~$r_value[$i] & 0xFFFFFF;
for ($j = 1; ($temp >> $j) & 1; ++$j) {
}
if ($j != 25) {
break;
}
}
$s = 26 * $i + $j;
$r->_rshift($s);
}
for ($i = 0; $i < $t; ++$i) {
$a = $this->random($two, $n_2);
$y = $a->modPow($r, $n);
if (!$y->equals($one) && !$y->equals($n_1)) {
for ($j = 1; $j < $s && !$y->equals($n_1);
++$j) {
$y = $y->modPow($two, $n);
if ($y->equals($one)) {
return false;
}
}
if (!$y->equals($n_1)) {
return false;
}
}
}
return true;
}
/**
* Logical Left Shift
*
* Shifts BigInteger's by $shift bits.
*
* @param int $shift
* @access private
*/
function _lshift($shift)
{
if ($shift == 0) {
return;
}
$num_digits = (int) ($shift / self::$base);
$shift %= self::$base;
$shift = 1 << $shift;
$carry = 0;
for ($i = 0; $i < count($this->value); ++$i) {
$temp = $this->value[$i] * $shift + $carry;
$carry = self::$base === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$this->value[$i] = (int) ($temp - $carry * self::$baseFull);
}
if ($carry) {
$this->value[count($this->value)] = $carry;
}
while ($num_digits--) {
array_unshift($this->value, 0);
}
}
/**
* Logical Right Shift
*
* Shifts BigInteger's by $shift bits.
*
* @param int $shift
* @access private
*/
function _rshift($shift)
{
if ($shift == 0) {
return;
}
$num_digits = (int) ($shift / self::$base);
$shift %= self::$base;
$carry_shift = self::$base - $shift;
$carry_mask = (1 << $shift) - 1;
if ($num_digits) {
$this->value = array_slice($this->value, $num_digits);
}
$carry = 0;
for ($i = count($this->value) - 1; $i >= 0; --$i) {
$temp = $this->value[$i] >> $shift | $carry;
$carry = ($this->value[$i] & $carry_mask) <<
$carry_shift;
$this->value[$i] = $temp;
}
$this->value = $this->_trim($this->value);
}
/**
* Normalize
*
* Removes leading zeros and truncates (if necessary) to maintain the
appropriate precision
*
* @param \phpseclib\Math\BigInteger $result
* @return \phpseclib\Math\BigInteger
* @see self::_trim()
* @access private
*/
function _normalize($result)
{
$result->precision = $this->precision;
$result->bitmask = $this->bitmask;
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
if ($this->bitmask !== false) {
$flip = gmp_cmp($result->value, gmp_init(0)) < 0;
if ($flip) {
$result->value = gmp_neg($result->value);
}
$result->value = gmp_and($result->value,
$result->bitmask->value);
if ($flip) {
$result->value = gmp_neg($result->value);
}
}
return $result;
case self::MODE_BCMATH:
if (!empty($result->bitmask->value)) {
$result->value = bcmod($result->value,
$result->bitmask->value);
}
return $result;
}
$value = &$result->value;
if (!count($value)) {
$result->is_negative = false;
return $result;
}
$value = $this->_trim($value);
if (!empty($result->bitmask->value)) {
$length = min(count($value),
count($this->bitmask->value));
$value = array_slice($value, 0, $length);
for ($i = 0; $i < $length; ++$i) {
$value[$i] = $value[$i] &
$this->bitmask->value[$i];
}
}
return $result;
}
/**
* Trim
*
* Removes leading zeros
*
* @param array $value
* @return \phpseclib\Math\BigInteger
* @access private
*/
function _trim($value)
{
for ($i = count($value) - 1; $i >= 0; --$i) {
if ($value[$i]) {
break;
}
unset($value[$i]);
}
return $value;
}
/**
* Array Repeat
*
* @param array $input
* @param mixed $multiplier
* @return array
* @access private
*/
function _array_repeat($input, $multiplier)
{
return ($multiplier) ? array_fill(0, $multiplier, $input) :
array();
}
/**
* Logical Left Shift
*
* Shifts binary strings $shift bits, essentially multiplying by
2**$shift.
*
* @param string $x (by reference)
* @param int $shift
* @return string
* @access private
*/
function _base256_lshift(&$x, $shift)
{
if ($shift == 0) {
return;
}
$num_bytes = $shift >> 3; // eg. floor($shift/8)
$shift &= 7; // eg. $shift % 8
$carry = 0;
for ($i = strlen($x) - 1; $i >= 0; --$i) {
$temp = ord($x[$i]) << $shift | $carry;
$x[$i] = chr($temp);
$carry = $temp >> 8;
}
$carry = ($carry != 0) ? chr($carry) : '';
$x = $carry . $x . str_repeat(chr(0), $num_bytes);
}
/**
* Logical Right Shift
*
* Shifts binary strings $shift bits, essentially dividing by 2**$shift
and returning the remainder.
*
* @param string $x (by referenc)
* @param int $shift
* @return string
* @access private
*/
function _base256_rshift(&$x, $shift)
{
if ($shift == 0) {
$x = ltrim($x, chr(0));
return '';
}
$num_bytes = $shift >> 3; // eg. floor($shift/8)
$shift &= 7; // eg. $shift % 8
$remainder = '';
if ($num_bytes) {
$start = $num_bytes > strlen($x) ? -strlen($x) :
-$num_bytes;
$remainder = substr($x, $start);
$x = substr($x, 0, -$num_bytes);
}
$carry = 0;
$carry_shift = 8 - $shift;
for ($i = 0; $i < strlen($x); ++$i) {
$temp = (ord($x[$i]) >> $shift) | $carry;
$carry = (ord($x[$i]) << $carry_shift) & 0xFF;
$x[$i] = chr($temp);
}
$x = ltrim($x, chr(0));
$remainder = chr($carry >> $carry_shift) . $remainder;
return ltrim($remainder, chr(0));
}
// one quirk about how the following functions are implemented is that
PHP defines N to be an unsigned long
// at 32-bits, while java's longs are 64-bits.
/**
* Converts 32-bit integers to bytes.
*
* @param int $x
* @return string
* @access private
*/
function _int2bytes($x)
{
return ltrim(pack('N', $x), chr(0));
}
/**
* Converts bytes to 32-bit integers
*
* @param string $x
* @return int
* @access private
*/
function _bytes2int($x)
{
$temp = unpack('Nint', str_pad($x, 4, chr(0),
STR_PAD_LEFT));
return $temp['int'];
}
/**
* DER-encode an integer
*
* The ability to DER-encode integers is needed to create RSA public
keys for use with OpenSSL
*
* @see self::modPow()
* @access private
* @param int $length
* @return string
*/
function _encodeASN1Length($length)
{
if ($length <= 0x7F) {
return chr($length);
}
$temp = ltrim(pack('N', $length), chr(0));
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 int $x
* @param int $y
* @return int
*/
function _safe_divide($x, $y)
{
if (self::$base === 26) {
return (int) ($x / $y);
}
// self::$base === 31
return ($x - ($x % $y)) / $y;
}
}
phpseclib/phpseclib/Net/SCP.php000064400000021777151161207740012374
0ustar00<?php
/**
* Pure-PHP implementation of SCP.
*
* PHP version 5
*
* The API for this library is modeled after the API from PHP's {@link
http://php.net/book.ftp FTP extension}.
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $ssh = new \phpseclib\Net\SSH2('www.domain.tld');
* if (!$ssh->login('username', 'password')) {
* exit('bad login');
* }
* $scp = new \phpseclib\Net\SCP($ssh);
*
* $scp->put('abcd', str_repeat('x', 1024*1024));
* ?>
* </code>
*
* @category Net
* @package SCP
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2010 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Net;
/**
* Pure-PHP implementations of SCP.
*
* @package SCP
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class SCP
{
/**#@+
* @access public
* @see \phpseclib\Net\SCP::put()
*/
/**
* Reads data from a local file.
*/
const SOURCE_LOCAL_FILE = 1;
/**
* Reads data from a string.
*/
const SOURCE_STRING = 2;
/**#@-*/
/**#@+
* @access private
* @see \phpseclib\Net\SCP::_send()
* @see \phpseclib\Net\SCP::_receive()
*/
/**
* SSH1 is being used.
*/
const MODE_SSH1 = 1;
/**
* SSH2 is being used.
*/
const MODE_SSH2 = 2;
/**#@-*/
/**
* SSH Object
*
* @var object
* @access private
*/
var $ssh;
/**
* Packet Size
*
* @var int
* @access private
*/
var $packet_size;
/**
* Mode
*
* @var int
* @access private
*/
var $mode;
/**
* Default Constructor.
*
* Connects to an SSH server
*
* @param \phpseclib\Net\SSH1|\phpseclib\Net\SSH2 $ssh
* @return \phpseclib\Net\SCP
* @access public
*/
function __construct($ssh)
{
if ($ssh instanceof SSH2) {
$this->mode = self::MODE_SSH2;
} elseif ($ssh instanceof SSH1) {
$this->packet_size = 50000;
$this->mode = self::MODE_SSH1;
} else {
return;
}
$this->ssh = $ssh;
}
/**
* Uploads a file to the SCP server.
*
* By default, \phpseclib\Net\SCP::put() does not read from the local
filesystem. $data is dumped directly into $remote_file.
* So, for example, if you set $data to 'filename.ext' and
then do \phpseclib\Net\SCP::get(), you will get a file, twelve bytes
* long, containing 'filename.ext' as its contents.
*
* Setting $mode to self::SOURCE_LOCAL_FILE will change the above
behavior. With self::SOURCE_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
* large $remote_file will be, as well.
*
* Currently, only binary mode is supported. As such, if the line
endings need to be adjusted, you will need to take
* care of that, yourself.
*
* @param string $remote_file
* @param string $data
* @param int $mode
* @param callable $callback
* @return bool
* @access public
*/
function put($remote_file, $data, $mode = self::SOURCE_STRING,
$callback = null)
{
if (!isset($this->ssh)) {
return false;
}
if (empty($remote_file)) {
user_error('remote_file cannot be blank',
E_USER_NOTICE);
return false;
}
if (!$this->ssh->exec('scp -t ' .
escapeshellarg($remote_file), false)) { // -t = to
return false;
}
$temp = $this->_receive();
if ($temp !== chr(0)) {
return false;
}
if ($this->mode == self::MODE_SSH2) {
$this->packet_size =
$this->ssh->packet_size_client_to_server[SSH2::CHANNEL_EXEC] - 4;
}
$remote_file = basename($remote_file);
if ($mode == self::SOURCE_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) {
return false;
}
$size = filesize($data);
}
$this->_send('C0644 ' . $size . ' ' .
$remote_file . "\n");
$temp = $this->_receive();
if ($temp !== chr(0)) {
return false;
}
$sent = 0;
while ($sent < $size) {
$temp = $mode & self::SOURCE_STRING ? substr($data, $sent,
$this->packet_size) : fread($fp, $this->packet_size);
$this->_send($temp);
$sent+= strlen($temp);
if (is_callable($callback)) {
call_user_func($callback, $sent);
}
}
$this->_close();
if ($mode != self::SOURCE_STRING) {
fclose($fp);
}
return true;
}
/**
* Downloads a file from the SCP server.
*
* Returns a string containing the contents of $remote_file if
$local_file is left undefined or a boolean false if
* the operation was unsuccessful. If $local_file is defined, returns
true or false depending on the success of the
* operation
*
* @param string $remote_file
* @param string $local_file
* @return mixed
* @access public
*/
function get($remote_file, $local_file = false)
{
if (!isset($this->ssh)) {
return false;
}
if (!$this->ssh->exec('scp -f ' .
escapeshellarg($remote_file), false)) { // -f = from
return false;
}
$this->_send("\0");
if (!preg_match('#(?<perms>[^ ]+) (?<size>\d+)
(?<name>.+)#', rtrim($this->_receive()), $info)) {
return false;
}
$this->_send("\0");
$size = 0;
if ($local_file !== false) {
$fp = @fopen($local_file, 'wb');
if (!$fp) {
return false;
}
}
$content = '';
while ($size < $info['size']) {
$data = $this->_receive();
// SCP usually seems to split stuff out into 16k chunks
$size+= strlen($data);
if ($local_file === false) {
$content.= $data;
} else {
fputs($fp, $data);
}
}
$this->_close();
if ($local_file !== false) {
fclose($fp);
return true;
}
return $content;
}
/**
* Sends a packet to an SSH server
*
* @param string $data
* @access private
*/
function _send($data)
{
switch ($this->mode) {
case self::MODE_SSH2:
$this->ssh->_send_channel_packet(SSH2::CHANNEL_EXEC,
$data);
break;
case self::MODE_SSH1:
$data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA,
strlen($data), $data);
$this->ssh->_send_binary_packet($data);
}
}
/**
* Receives a packet from an SSH server
*
* @return string
* @access private
*/
function _receive()
{
switch ($this->mode) {
case self::MODE_SSH2:
return
$this->ssh->_get_channel_packet(SSH2::CHANNEL_EXEC, true);
case self::MODE_SSH1:
if (!$this->ssh->bitmap) {
return false;
}
while (true) {
$response = $this->ssh->_get_binary_packet();
switch ($response[SSH1::RESPONSE_TYPE]) {
case NET_SSH1_SMSG_STDOUT_DATA:
if (strlen($response[SSH1::RESPONSE_DATA]) <
4) {
return false;
}
extract(unpack('Nlength',
$response[SSH1::RESPONSE_DATA]));
return
$this->ssh->_string_shift($response[SSH1::RESPONSE_DATA], $length);
case NET_SSH1_SMSG_STDERR_DATA:
break;
case NET_SSH1_SMSG_EXITSTATUS:
$this->ssh->_send_binary_packet(chr(NET_SSH1_CMSG_EXIT_CONFIRMATION));
fclose($this->ssh->fsock);
$this->ssh->bitmap = 0;
return false;
default:
user_error('Unknown packet received',
E_USER_NOTICE);
return false;
}
}
}
}
/**
* Closes the connection to an SSH server
*
* @access private
*/
function _close()
{
switch ($this->mode) {
case self::MODE_SSH2:
$this->ssh->_close_channel(SSH2::CHANNEL_EXEC, true);
break;
case self::MODE_SSH1:
$this->ssh->disconnect();
}
}
}
phpseclib/phpseclib/Net/SFTP/Stream.php000064400000052560151161207740013750
0ustar00<?php
/**
* SFTP Stream Wrapper
*
* Creates an sftp:// protocol handler that can be used with, for example,
fopen(), dir(), etc.
*
* PHP version 5
*
* @category Net
* @package SFTP
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2013 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Net\SFTP;
use phpseclib\Crypt\RSA;
use phpseclib\Net\SFTP;
/**
* SFTP Stream Wrapper
*
* @package SFTP
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class Stream
{
/**
* SFTP instances
*
* Rather than re-create the connection we re-use instances if possible
*
* @var array
*/
static $instances;
/**
* SFTP instance
*
* @var object
* @access private
*/
var $sftp;
/**
* Path
*
* @var string
* @access private
*/
var $path;
/**
* Mode
*
* @var string
* @access private
*/
var $mode;
/**
* Position
*
* @var int
* @access private
*/
var $pos;
/**
* Size
*
* @var int
* @access private
*/
var $size;
/**
* Directory entries
*
* @var array
* @access private
*/
var $entries;
/**
* EOF flag
*
* @var bool
* @access private
*/
var $eof;
/**
* Context resource
*
* Technically this needs to be publically accessible so PHP can set it
directly
*
* @var resource
* @access public
*/
var $context;
/**
* Notification callback function
*
* @var callable
* @access public
*/
var $notification;
/**
* Registers this class as a URL wrapper.
*
* @param string $protocol The wrapper name to be registered.
* @return bool True on success, false otherwise.
* @access public
*/
static function register($protocol = 'sftp')
{
if (in_array($protocol, stream_get_wrappers(), true)) {
return false;
}
return stream_wrapper_register($protocol, get_called_class());
}
/**
* The Constructor
*
* @access public
*/
function __construct()
{
if (defined('NET_SFTP_STREAM_LOGGING')) {
echo "__construct()\r\n";
}
}
/**
* Path Parser
*
* Extract a path from a URI and actually connect to an SSH server if
appropriate
*
* If "notification" is set as a context parameter the
message code for successful login is
* NET_SSH2_MSG_USERAUTH_SUCCESS. For a failed login it's
NET_SSH2_MSG_USERAUTH_FAILURE.
*
* @param string $path
* @return string
* @access private
*/
function _parse_path($path)
{
$orig = $path;
extract(parse_url($path) + array('port' => 22));
if (isset($query)) {
$path.= '?' . $query;
} elseif (preg_match('/(\?|\?#)$/', $orig)) {
$path.= '?';
}
if (isset($fragment)) {
$path.= '#' . $fragment;
} elseif ($orig[strlen($orig) - 1] == '#') {
$path.= '#';
}
if (!isset($host)) {
return false;
}
if (isset($this->context)) {
$context = stream_context_get_params($this->context);
if (isset($context['notification'])) {
$this->notification =
$context['notification'];
}
}
if ($host[0] == '$') {
$host = substr($host, 1);
global ${$host};
if (($$host instanceof SFTP) === false) {
return false;
}
$this->sftp = $$host;
} else {
if (isset($this->context)) {
$context = stream_context_get_options($this->context);
}
if (isset($context[$scheme]['session'])) {
$sftp = $context[$scheme]['session'];
}
if (isset($context[$scheme]['sftp'])) {
$sftp = $context[$scheme]['sftp'];
}
if (isset($sftp) && $sftp instanceof SFTP) {
$this->sftp = $sftp;
return $path;
}
if (isset($context[$scheme]['username'])) {
$user = $context[$scheme]['username'];
}
if (isset($context[$scheme]['password'])) {
$pass = $context[$scheme]['password'];
}
if (isset($context[$scheme]['privkey']) &&
$context[$scheme]['privkey'] instanceof RSA) {
$pass = $context[$scheme]['privkey'];
}
if (!isset($user) || !isset($pass)) {
return false;
}
// casting $pass to a string is necessary in the event that
it's a \phpseclib\Crypt\RSA object
if (isset(self::$instances[$host][$port][$user][(string)
$pass])) {
$this->sftp =
self::$instances[$host][$port][$user][(string) $pass];
} else {
$this->sftp = new SFTP($host, $port);
$this->sftp->disableStatCache();
if (isset($this->notification) &&
is_callable($this->notification)) {
/* if !is_callable($this->notification) we could do
this:
user_error('fopen(): failed to call user
notifier', E_USER_WARNING);
the ftp wrapper gives errors like that when the
notifier isn't callable.
i've opted not to do that, however, since the
ftp wrapper gives the line
on which the fopen occurred as the line number - not
the line that the
user_error is on.
*/
call_user_func($this->notification,
STREAM_NOTIFY_CONNECT, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0);
call_user_func($this->notification,
STREAM_NOTIFY_AUTH_REQUIRED, STREAM_NOTIFY_SEVERITY_INFO, '', 0,
0, 0);
if (!$this->sftp->login($user, $pass)) {
call_user_func($this->notification,
STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_ERR, 'Login
Failure', NET_SSH2_MSG_USERAUTH_FAILURE, 0, 0);
return false;
}
call_user_func($this->notification,
STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_INFO, 'Login
Success', NET_SSH2_MSG_USERAUTH_SUCCESS, 0, 0);
} else {
if (!$this->sftp->login($user, $pass)) {
return false;
}
}
self::$instances[$host][$port][$user][(string) $pass] =
$this->sftp;
}
}
return $path;
}
/**
* Opens file or URL
*
* @param string $path
* @param string $mode
* @param int $options
* @param string $opened_path
* @return bool
* @access public
*/
function _stream_open($path, $mode, $options, &$opened_path)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}
$this->path = $path;
$this->size = $this->sftp->size($path);
$this->mode = preg_replace('#[bt]$#', '',
$mode);
$this->eof = false;
if ($this->size === false) {
if ($this->mode[0] == 'r') {
return false;
} else {
$this->sftp->touch($path);
$this->size = 0;
}
} else {
switch ($this->mode[0]) {
case 'x':
return false;
case 'w':
$this->sftp->truncate($path, 0);
$this->size = 0;
}
}
$this->pos = $this->mode[0] != 'a' ? 0 :
$this->size;
return true;
}
/**
* Read from stream
*
* @param int $count
* @return mixed
* @access public
*/
function _stream_read($count)
{
switch ($this->mode) {
case 'w':
case 'a':
case 'x':
case 'c':
return false;
}
// commented out because some files - eg. /dev/urandom - will say
their size is 0 when in fact it's kinda infinite
//if ($this->pos >= $this->size) {
// $this->eof = true;
// return false;
//}
$result = $this->sftp->get($this->path, false,
$this->pos, $count);
if (isset($this->notification) &&
is_callable($this->notification)) {
if ($result === false) {
call_user_func($this->notification,
STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR,
$this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0);
return 0;
}
// seems that PHP calls stream_read in 8k chunks
call_user_func($this->notification, STREAM_NOTIFY_PROGRESS,
STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($result),
$this->size);
}
if (empty($result)) { // ie. false or empty string
$this->eof = true;
return false;
}
$this->pos+= strlen($result);
return $result;
}
/**
* Write to stream
*
* @param string $data
* @return mixed
* @access public
*/
function _stream_write($data)
{
switch ($this->mode) {
case 'r':
return false;
}
$result = $this->sftp->put($this->path, $data,
SFTP::SOURCE_STRING, $this->pos);
if (isset($this->notification) &&
is_callable($this->notification)) {
if (!$result) {
call_user_func($this->notification,
STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR,
$this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0);
return 0;
}
// seems that PHP splits up strings into 8k blocks before
calling stream_write
call_user_func($this->notification, STREAM_NOTIFY_PROGRESS,
STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($data),
strlen($data));
}
if ($result === false) {
return false;
}
$this->pos+= strlen($data);
if ($this->pos > $this->size) {
$this->size = $this->pos;
}
$this->eof = false;
return strlen($data);
}
/**
* Retrieve the current position of a stream
*
* @return int
* @access public
*/
function _stream_tell()
{
return $this->pos;
}
/**
* Tests for end-of-file on a file pointer
*
* In my testing there are four classes functions that normally effect
the pointer:
* fseek, fputs / fwrite, fgets / fread and ftruncate.
*
* Only fgets / fread, however, results in feof() returning true. do
fputs($fp, 'aaa') on a blank file and feof()
* will return false. do fread($fp, 1) and feof() will then return
true. do fseek($fp, 10) on ablank file and feof()
* will return false. do fread($fp, 1) and feof() will then return
true.
*
* @return bool
* @access public
*/
function _stream_eof()
{
return $this->eof;
}
/**
* Seeks to specific location in a stream
*
* @param int $offset
* @param int $whence
* @return bool
* @access public
*/
function _stream_seek($offset, $whence)
{
switch ($whence) {
case SEEK_SET:
if ($offset < 0) {
return false;
}
break;
case SEEK_CUR:
$offset+= $this->pos;
break;
case SEEK_END:
$offset+= $this->size;
}
$this->pos = $offset;
$this->eof = false;
return true;
}
/**
* Change stream options
*
* @param string $path
* @param int $option
* @param mixed $var
* @return bool
* @access public
*/
function _stream_metadata($path, $option, $var)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}
// stream_metadata was introduced in PHP 5.4.0 but as of 5.4.11 the
constants haven't been defined
// see http://www.php.net/streamwrapper.stream-metadata and
https://bugs.php.net/64246
// and
https://github.com/php/php-src/blob/master/main/php_streams.h#L592
switch ($option) {
case 1: // PHP_STREAM_META_TOUCH
$time = isset($var[0]) ? $var[0] : null;
$atime = isset($var[1]) ? $var[1] : null;
return $this->sftp->touch($path, $time, $atime);
case 2: // PHP_STREAM_OWNER_NAME
case 3: // PHP_STREAM_GROUP_NAME
return false;
case 4: // PHP_STREAM_META_OWNER
return $this->sftp->chown($path, $var);
case 5: // PHP_STREAM_META_GROUP
return $this->sftp->chgrp($path, $var);
case 6: // PHP_STREAM_META_ACCESS
return $this->sftp->chmod($path, $var) !== false;
}
}
/**
* Retrieve the underlaying resource
*
* @param int $cast_as
* @return resource
* @access public
*/
function _stream_cast($cast_as)
{
return $this->sftp->fsock;
}
/**
* Advisory file locking
*
* @param int $operation
* @return bool
* @access public
*/
function _stream_lock($operation)
{
return false;
}
/**
* Renames a file or directory
*
* Attempts to rename oldname to newname, moving it between directories
if necessary.
* If newname exists, it will be overwritten. This is a departure from
what \phpseclib\Net\SFTP
* does.
*
* @param string $path_from
* @param string $path_to
* @return bool
* @access public
*/
function _rename($path_from, $path_to)
{
$path1 = parse_url($path_from);
$path2 = parse_url($path_to);
unset($path1['path'], $path2['path']);
if ($path1 != $path2) {
return false;
}
$path_from = $this->_parse_path($path_from);
$path_to = parse_url($path_to);
if ($path_from === false) {
return false;
}
$path_to = $path_to['path']; // the $component part of
parse_url() was added in PHP 5.1.2
// "It is an error if there already exists a file with the
name specified by newpath."
// --
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.5
if (!$this->sftp->rename($path_from, $path_to)) {
if ($this->sftp->stat($path_to)) {
return $this->sftp->delete($path_to, true) &&
$this->sftp->rename($path_from, $path_to);
}
return false;
}
return true;
}
/**
* Open directory handle
*
* 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.
*
* 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 int $options
* @return bool
* @access public
*/
function _dir_opendir($path, $options)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}
$this->pos = 0;
$this->entries = $this->sftp->nlist($path);
return $this->entries !== false;
}
/**
* Read entry from directory handle
*
* @return mixed
* @access public
*/
function _dir_readdir()
{
if (isset($this->entries[$this->pos])) {
return $this->entries[$this->pos++];
}
return false;
}
/**
* Rewind directory handle
*
* @return bool
* @access public
*/
function _dir_rewinddir()
{
$this->pos = 0;
return true;
}
/**
* Close directory handle
*
* @return bool
* @access public
*/
function _dir_closedir()
{
return true;
}
/**
* Create a directory
*
* Only valid $options is STREAM_MKDIR_RECURSIVE
*
* @param string $path
* @param int $mode
* @param int $options
* @return bool
* @access public
*/
function _mkdir($path, $mode, $options)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}
return $this->sftp->mkdir($path, $mode, $options &
STREAM_MKDIR_RECURSIVE);
}
/**
* Removes a directory
*
* Only valid $options is STREAM_MKDIR_RECURSIVE per
<http://php.net/streamwrapper.rmdir>, however,
* <http://php.net/rmdir> does not have a $recursive parameter
as mkdir() does so I don't know how
* STREAM_MKDIR_RECURSIVE is supposed to be set. Also, when I try it
out with rmdir() I get 8 as
* $options. What does 8 correspond to?
*
* @param string $path
* @param int $options
* @return bool
* @access public
*/
function _rmdir($path, $options)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}
return $this->sftp->rmdir($path);
}
/**
* Flushes the output
*
* See <http://php.net/fflush>. Always returns true because
\phpseclib\Net\SFTP doesn't cache stuff before writing
*
* @return bool
* @access public
*/
function _stream_flush()
{
return true;
}
/**
* Retrieve information about a file resource
*
* @return mixed
* @access public
*/
function _stream_stat()
{
$results = $this->sftp->stat($this->path);
if ($results === false) {
return false;
}
return $results;
}
/**
* Delete a file
*
* @param string $path
* @return bool
* @access public
*/
function _unlink($path)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}
return $this->sftp->delete($path, false);
}
/**
* Retrieve information about a file
*
* Ignores the STREAM_URL_STAT_QUIET flag because the entirety of
\phpseclib\Net\SFTP\Stream is quiet by default
* might be worthwhile to reconstruct bits 12-16 (ie. the file type) if
mode doesn't have them but we'll
* cross that bridge when and if it's reached
*
* @param string $path
* @param int $flags
* @return mixed
* @access public
*/
function _url_stat($path, $flags)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}
$results = $flags & STREAM_URL_STAT_LINK ?
$this->sftp->lstat($path) : $this->sftp->stat($path);
if ($results === false) {
return false;
}
return $results;
}
/**
* Truncate stream
*
* @param int $new_size
* @return bool
* @access public
*/
function _stream_truncate($new_size)
{
if (!$this->sftp->truncate($this->path, $new_size)) {
return false;
}
$this->eof = false;
$this->size = $new_size;
return true;
}
/**
* Change stream options
*
* STREAM_OPTION_WRITE_BUFFER isn't supported for the same reason
stream_flush isn't.
* The other two aren't supported because of limitations in
\phpseclib\Net\SFTP.
*
* @param int $option
* @param int $arg1
* @param int $arg2
* @return bool
* @access public
*/
function _stream_set_option($option, $arg1, $arg2)
{
return false;
}
/**
* Close an resource
*
* @access public
*/
function _stream_close()
{
}
/**
* __call Magic Method
*
* When you're utilizing an SFTP stream you're not calling
the methods in this class directly - PHP is calling them for you.
* Which kinda begs the question... what methods is PHP calling and
what parameters is it passing to them? This function
* lets you figure that out.
*
* If NET_SFTP_STREAM_LOGGING is defined all calls will be output on
the screen and then (regardless of whether or not
* NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed
through to the appropriate method.
*
* @param string $name
* @param array $arguments
* @return mixed
* @access public
*/
function __call($name, $arguments)
{
if (defined('NET_SFTP_STREAM_LOGGING')) {
echo $name . '(';
$last = count($arguments) - 1;
foreach ($arguments as $i => $argument) {
var_export($argument);
if ($i != $last) {
echo ',';
}
}
echo ")\r\n";
}
$name = '_' . $name;
if (!method_exists($this, $name)) {
return false;
}
return call_user_func_array(array($this, $name), $arguments);
}
}
phpseclib/phpseclib/Net/SFTP.php000064400000312300151161207740012504
0ustar00<?php
/**
* Pure-PHP implementation of SFTP.
*
* PHP version 5
*
* Currently only supports SFTPv2 and v3, which, according to
wikipedia.org, "is the most widely used version,
* implemented by the popular OpenSSH SFTP server". If you want
SFTPv4/5/6 support, provide me with access
* to an SFTPv4/5/6 server.
*
* The API for this library is modeled after the API from PHP's {@link
http://php.net/book.ftp FTP extension}.
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $sftp = new \phpseclib\Net\SFTP('www.domain.tld');
* if (!$sftp->login('username', 'password')) {
* exit('Login Failed');
* }
*
* echo $sftp->pwd() . "\r\n";
* $sftp->put('filename.ext', 'hello, world!');
* print_r($sftp->nlist());
* ?>
* </code>
*
* @category Net
* @package SFTP
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2009 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Net;
/**
* Pure-PHP implementations of SFTP.
*
* @package SFTP
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class SFTP extends SSH2
{
/**
* SFTP channel constant
*
* \phpseclib\Net\SSH2::exec() uses 0 and \phpseclib\Net\SSH2::read() /
\phpseclib\Net\SSH2::write() use 1.
*
* @see \phpseclib\Net\SSH2::_send_channel_packet()
* @see \phpseclib\Net\SSH2::_get_channel_packet()
* @access private
*/
const CHANNEL = 0x100;
/**#@+
* @access public
* @see \phpseclib\Net\SFTP::put()
*/
/**
* Reads data from a local file.
*/
const SOURCE_LOCAL_FILE = 1;
/**
* Reads data from a string.
*/
// this value isn't really used anymore but i'm keeping it
reserved for historical reasons
const SOURCE_STRING = 2;
/**
* Reads data from callback:
* function callback($length) returns string to proceed, null for EOF
*/
const SOURCE_CALLBACK = 16;
/**
* Resumes an upload
*/
const RESUME = 4;
/**
* Append a local file to an already existing remote file
*/
const RESUME_START = 8;
/**#@-*/
/**
* Packet Types
*
* @see self::__construct()
* @var array
* @access private
*/
var $packet_types = array();
/**
* Status Codes
*
* @see self::__construct()
* @var array
* @access private
*/
var $status_codes = array();
/**
* The Request ID
*
* The request ID exists in the off chance that a packet is sent
out-of-order. Of course, this library doesn't support
* concurrent actions, so it's somewhat academic, here.
*
* @var boolean
* @see self::_send_sftp_packet()
* @access private
*/
var $use_request_id = false;
/**
* The Packet Type
*
* The request ID exists in the off chance that a packet is sent
out-of-order. Of course, this library doesn't support
* concurrent actions, so it's somewhat academic, here.
*
* @var int
* @see self::_get_sftp_packet()
* @access private
*/
var $packet_type = -1;
/**
* Packet Buffer
*
* @var string
* @see self::_get_sftp_packet()
* @access private
*/
var $packet_buffer = '';
/**
* Extensions supported by the server
*
* @var array
* @see self::_initChannel()
* @access private
*/
var $extensions = array();
/**
* Server SFTP version
*
* @var int
* @see self::_initChannel()
* @access private
*/
var $version;
/**
* Current working directory
*
* @var string
* @see self::realpath()
* @see self::chdir()
* @access private
*/
var $pwd = false;
/**
* Packet Type Log
*
* @see self::getLog()
* @var array
* @access private
*/
var $packet_type_log = array();
/**
* Packet Log
*
* @see self::getLog()
* @var array
* @access private
*/
var $packet_log = array();
/**
* Error information
*
* @see self::getSFTPErrors()
* @see self::getLastSFTPError()
* @var array
* @access private
*/
var $sftp_errors = array();
/**
* Stat Cache
*
* Rather than always having to open a directory and close it
immediately there after to see if a file is a directory
* we'll cache the results.
*
* @see self::_update_stat_cache()
* @see self::_remove_from_stat_cache()
* @see self::_query_stat_cache()
* @var array
* @access private
*/
var $stat_cache = array();
/**
* Max SFTP Packet Size
*
* @see self::__construct()
* @see self::get()
* @var array
* @access private
*/
var $max_sftp_packet;
/**
* Stat Cache Flag
*
* @see self::disableStatCache()
* @see self::enableStatCache()
* @var bool
* @access private
*/
var $use_stat_cache = true;
/**
* Sort Options
*
* @see self::_comparator()
* @see self::setListOrder()
* @var array
* @access private
*/
var $sortOptions = array();
/**
* Canonicalization Flag
*
* Determines whether or not paths should be canonicalized before being
* passed on to the remote server.
*
* @see self::enablePathCanonicalization()
* @see self::disablePathCanonicalization()
* @see self::realpath()
* @var bool
* @access private
*/
var $canonicalize_paths = true;
/**
* Request Buffers
*
* @see self::_get_sftp_packet()
* @var array
* @access private
*/
var $requestBuffer = array();
/**
* Preserve timestamps on file downloads / uploads
*
* @see self::get()
* @see self::put()
* @var bool
* @access private
*/
var $preserveTime = false;
/**
* Was the last packet due to the channels being closed or not?
*
* @see self::get()
* @see self::get_sftp_packet()
* @var bool
* @access private
*/
var $channel_close = false;
/**
* Default Constructor.
*
* Connects to an SFTP server
*
* @param string $host
* @param int $port
* @param int $timeout
* @return \phpseclib\Net\SFTP
* @access public
*/
function __construct($host, $port = 22, $timeout = 10)
{
parent::__construct($host, $port, $timeout);
$this->max_sftp_packet = 1 << 15;
$this->packet_types = array(
1 => 'NET_SFTP_INIT',
2 => 'NET_SFTP_VERSION',
/* the format of SSH_FXP_OPEN changed between SFTPv4 and
SFTPv5+:
SFTPv5+:
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.1
pre-SFTPv5 :
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 */
3 => 'NET_SFTP_OPEN',
4 => 'NET_SFTP_CLOSE',
5 => 'NET_SFTP_READ',
6 => 'NET_SFTP_WRITE',
7 => 'NET_SFTP_LSTAT',
9 => 'NET_SFTP_SETSTAT',
11 => 'NET_SFTP_OPENDIR',
12 => 'NET_SFTP_READDIR',
13 => 'NET_SFTP_REMOVE',
14 => 'NET_SFTP_MKDIR',
15 => 'NET_SFTP_RMDIR',
16 => 'NET_SFTP_REALPATH',
17 => 'NET_SFTP_STAT',
/* the format of SSH_FXP_RENAME changed between SFTPv4 and
SFTPv5+:
SFTPv5+:
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
pre-SFTPv5 :
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.5 */
18 => 'NET_SFTP_RENAME',
19 => 'NET_SFTP_READLINK',
20 => 'NET_SFTP_SYMLINK',
101=> 'NET_SFTP_STATUS',
102=> 'NET_SFTP_HANDLE',
/* the format of SSH_FXP_NAME changed between SFTPv3 and
SFTPv4+:
SFTPv4+:
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4
pre-SFTPv4 :
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 */
103=> 'NET_SFTP_DATA',
104=> 'NET_SFTP_NAME',
105=> 'NET_SFTP_ATTRS',
200=> 'NET_SFTP_EXTENDED'
);
$this->status_codes = array(
0 => 'NET_SFTP_STATUS_OK',
1 => 'NET_SFTP_STATUS_EOF',
2 => 'NET_SFTP_STATUS_NO_SUCH_FILE',
3 => 'NET_SFTP_STATUS_PERMISSION_DENIED',
4 => 'NET_SFTP_STATUS_FAILURE',
5 => 'NET_SFTP_STATUS_BAD_MESSAGE',
6 => 'NET_SFTP_STATUS_NO_CONNECTION',
7 => 'NET_SFTP_STATUS_CONNECTION_LOST',
8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED',
9 => 'NET_SFTP_STATUS_INVALID_HANDLE',
10 => 'NET_SFTP_STATUS_NO_SUCH_PATH',
11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS',
12 => 'NET_SFTP_STATUS_WRITE_PROTECT',
13 => 'NET_SFTP_STATUS_NO_MEDIA',
14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM',
15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED',
16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL',
17 => 'NET_SFTP_STATUS_LOCK_CONFLICT',
18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY',
19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY',
20 => 'NET_SFTP_STATUS_INVALID_FILENAME',
21 => 'NET_SFTP_STATUS_LINK_LOOP',
22 => 'NET_SFTP_STATUS_CANNOT_DELETE',
23 => 'NET_SFTP_STATUS_INVALID_PARAMETER',
24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY',
25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT',
26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED',
27 => 'NET_SFTP_STATUS_DELETE_PENDING',
28 => 'NET_SFTP_STATUS_FILE_CORRUPT',
29 => 'NET_SFTP_STATUS_OWNER_INVALID',
30 => 'NET_SFTP_STATUS_GROUP_INVALID',
31 =>
'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK'
);
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1
// the order, in this case, matters quite a lot - see
\phpseclib\Net\SFTP::_parseAttributes() to understand why
$this->attributes = array(
0x00000001 => 'NET_SFTP_ATTR_SIZE',
0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined
in SFTPv3, removed in SFTPv4+
0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
// 0x80000000 will yield a floating point on 32-bit systems and
converting floating points to integers
// yields inconsistent behavior depending on how php is
compiled. so we left shift -1 (which, in
// two's compliment, consists of all 1 bits) by 31. on
64-bit systems this'll yield 0xFFFFFFFF80000000.
// that's not a problem, however, and 'anded'
and a 32-bit number, as all the leading 1 bits are ignored.
(-1 << 31) & 0xFFFFFFFF =>
'NET_SFTP_ATTR_EXTENDED'
);
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3
// the flag definitions change somewhat in SFTPv5+. if SFTPv5+
support is added to this library, maybe name
// the array for that $this->open5_flags and similarly alter the
constant names.
$this->open_flags = array(
0x00000001 => 'NET_SFTP_OPEN_READ',
0x00000002 => 'NET_SFTP_OPEN_WRITE',
0x00000004 => 'NET_SFTP_OPEN_APPEND',
0x00000008 => 'NET_SFTP_OPEN_CREATE',
0x00000010 => 'NET_SFTP_OPEN_TRUNCATE',
0x00000020 => 'NET_SFTP_OPEN_EXCL'
);
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
// see \phpseclib\Net\SFTP::_parseLongname() for an explanation
$this->file_types = array(
1 => 'NET_SFTP_TYPE_REGULAR',
2 => 'NET_SFTP_TYPE_DIRECTORY',
3 => 'NET_SFTP_TYPE_SYMLINK',
4 => 'NET_SFTP_TYPE_SPECIAL',
5 => 'NET_SFTP_TYPE_UNKNOWN',
// the followin types were first defined for use in SFTPv5+
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
6 => 'NET_SFTP_TYPE_SOCKET',
7 => 'NET_SFTP_TYPE_CHAR_DEVICE',
8 => 'NET_SFTP_TYPE_BLOCK_DEVICE',
9 => 'NET_SFTP_TYPE_FIFO'
);
$this->_define_array(
$this->packet_types,
$this->status_codes,
$this->attributes,
$this->open_flags,
$this->file_types
);
if (!defined('NET_SFTP_QUEUE_SIZE')) {
define('NET_SFTP_QUEUE_SIZE', 32);
}
if (!defined('NET_SFTP_UPLOAD_QUEUE_SIZE')) {
define('NET_SFTP_UPLOAD_QUEUE_SIZE', 1024);
}
}
/**
* Login
*
* @param string $username
* @return bool
* @access public
*/
function login($username)
{
if (!call_user_func_array('parent::login',
func_get_args())) {
return false;
}
return $this->_init_sftp_connection();
}
/**
* (Re)initializes the SFTP channel
*
* @return bool
* @access private
*/
function _init_sftp_connection()
{
$this->window_size_server_to_client[self::CHANNEL] =
$this->window_size;
$packet = pack(
'CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN,
strlen('session'),
'session',
self::CHANNEL,
$this->window_size,
0x4000
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[self::CHANNEL] =
NET_SSH2_MSG_CHANNEL_OPEN;
$response = $this->_get_channel_packet(self::CHANNEL, true);
if ($response === false) {
return false;
}
$packet = pack(
'CNNa*CNa*',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL],
strlen('subsystem'),
'subsystem',
1,
strlen('sftp'),
'sftp'
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[self::CHANNEL] =
NET_SSH2_MSG_CHANNEL_REQUEST;
$response = $this->_get_channel_packet(self::CHANNEL, true);
if ($response === false) {
// from PuTTY's psftp.exe
$command = "test -x /usr/lib/sftp-server && exec
/usr/lib/sftp-server\n" .
"test -x /usr/local/lib/sftp-server &&
exec /usr/local/lib/sftp-server\n" .
"exec sftp-server";
// we don't do $this->exec($command, false) because
exec() operates on a different channel and plus the SSH_MSG_CHANNEL_OPEN
that exec() does
// is redundant
$packet = pack(
'CNNa*CNa*',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL],
strlen('exec'),
'exec',
1,
strlen($command),
$command
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[self::CHANNEL] =
NET_SSH2_MSG_CHANNEL_REQUEST;
$response = $this->_get_channel_packet(self::CHANNEL, true);
if ($response === false) {
return false;
}
}
$this->channel_status[self::CHANNEL] =
NET_SSH2_MSG_CHANNEL_DATA;
if (!$this->_send_sftp_packet(NET_SFTP_INIT,
"\0\0\0\3")) {
return false;
}
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_VERSION) {
user_error('Expected SSH_FXP_VERSION');
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nversion',
$this->_string_shift($response, 4)));
$this->version = $version;
while (!empty($response)) {
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$key = $this->_string_shift($response, $length);
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$value = $this->_string_shift($response, $length);
$this->extensions[$key] = $value;
}
/*
SFTPv4+ defines a 'newline' extension. SFTPv3 seems to
have unofficial support for it via 'newline@vandyke.com',
however, I'm not sure what 'newline@vandyke.com' is
supposed to do (the fact that it's unofficial means that it's
not in the official SFTPv3 specs) and
'newline@vandyke.com' / 'newline' are likely not
drop-in substitutes for
one another due to the fact that 'newline' comes with a
SSH_FXF_TEXT bitmask whereas it seems unlikely that
'newline@vandyke.com' would.
*/
/*
if (isset($this->extensions['newline@vandyke.com'])) {
$this->extensions['newline'] =
$this->extensions['newline@vandyke.com'];
unset($this->extensions['newline@vandyke.com']);
}
*/
$this->use_request_id = true;
/*
A Note on SFTPv4/5/6 support:
<http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.1>
states the following:
"If the client wishes to interoperate with servers that
support noncontiguous version
numbers it SHOULD send '3'"
Given that the server only sends its version number after the
client has already done so, the above
seems to be suggesting that v3 should be the default version.
This makes sense given that v3 is the
most popular.
<http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.5>
states the following;
"If the server did not send the "versions"
extension, or the version-from-list was not included, the
server MAY send a status response describing the failure, but
MUST then close the channel without
processing any further requests."
So what do you do if you have a client whose initial SSH_FXP_INIT
packet says it implements v3 and
a server whose initial SSH_FXP_VERSION reply says it implements v4
and only v4? If it only implements
v4, the "versions" extension is likely not going to have
been sent so version re-negotiation as discussed
in draft-ietf-secsh-filexfer-13 would be quite impossible. As
such, what \phpseclib\Net\SFTP would do is close the
channel and reopen it with a new and updated SSH_FXP_INIT packet.
*/
switch ($this->version) {
case 2:
case 3:
break;
default:
return false;
}
$this->pwd = $this->_realpath('.');
$this->_update_stat_cache($this->pwd, array());
return true;
}
/**
* Disable the stat cache
*
* @access public
*/
function disableStatCache()
{
$this->use_stat_cache = false;
}
/**
* Enable the stat cache
*
* @access public
*/
function enableStatCache()
{
$this->use_stat_cache = true;
}
/**
* Clear the stat cache
*
* @access public
*/
function clearStatCache()
{
$this->stat_cache = array();
}
/**
* Enable path canonicalization
*
* @access public
*/
function enablePathCanonicalization()
{
$this->canonicalize_paths = true;
}
/**
* Enable path canonicalization
*
* @access public
*/
function disablePathCanonicalization()
{
$this->canonicalize_paths = false;
}
/**
* Returns the current directory name
*
* @return mixed
* @access public
*/
function pwd()
{
return $this->pwd;
}
/**
* Logs errors
*
* @param string $response
* @param int $status
* @access public
*/
function _logError($response, $status = -1)
{
if ($status == -1) {
if (strlen($response) < 4) {
return;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
}
$error = $this->status_codes[$status];
if ($this->version > 2 || strlen($response) < 4) {
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$this->sftp_errors[] = $error . ': ' .
$this->_string_shift($response, $length);
} else {
$this->sftp_errors[] = $error;
}
}
/**
* Returns canonicalized absolute pathname
*
* realpath() expands all symbolic links and resolves references to
'/./', '/../' and extra '/' characters in the
input
* path and returns the canonicalized absolute pathname.
*
* @param string $path
* @return mixed
* @access public
*/
function realpath($path)
{
return $this->_realpath($path);
}
/**
* Canonicalize the Server-Side Path Name
*
* SFTP doesn't provide a mechanism by which the current working
directory can be changed, so we'll emulate it. Returns
* the absolute (canonicalized) path.
*
* If canonicalize_paths has been disabled using
disablePathCanonicalization(), $path is returned as-is.
*
* @see self::chdir()
* @see self::disablePathCanonicalization()
* @param string $path
* @return mixed
* @access private
*/
function _realpath($path)
{
if (!$this->canonicalize_paths) {
return $path;
}
if ($this->pwd === false) {
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9
if (!$this->_send_sftp_packet(NET_SFTP_REALPATH,
pack('Na*', strlen($path), $path))) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_NAME:
// although SSH_FXP_NAME is implemented differently in
SFTPv3 than it is in SFTPv4+, the following
// should work on all SFTP versions since the only part
of the SSH_FXP_NAME packet the following looks
// at is the first part and that part is defined the
same in SFTP versions 3 through 6.
$this->_string_shift($response, 4); // skip over the
count - it should be 1, anyway
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
return $this->_string_shift($response, $length);
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_NAME or
SSH_FXP_STATUS');
return false;
}
}
if (!strlen($path) || $path[0] != '/') {
$path = $this->pwd . '/' . $path;
}
$path = explode('/', $path);
$new = array();
foreach ($path as $dir) {
if (!strlen($dir)) {
continue;
}
switch ($dir) {
case '..':
array_pop($new);
case '.':
break;
default:
$new[] = $dir;
}
}
return '/' . implode('/', $new);
}
/**
* Changes the current directory
*
* @param string $dir
* @return bool
* @access public
*/
function chdir($dir)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
// assume current dir if $dir is empty
if ($dir === '') {
$dir = './';
// suffix a slash if needed
} elseif ($dir[strlen($dir) - 1] != '/') {
$dir.= '/';
}
$dir = $this->_realpath($dir);
// confirm that $dir is, in fact, a valid directory
if ($this->use_stat_cache &&
is_array($this->_query_stat_cache($dir))) {
$this->pwd = $dir;
return true;
}
// we could do a stat on the alleged $dir to see if it's a
directory but that doesn't tell us
// the currently logged in user has the appropriate permissions or
not. maybe you could see if
// the file's uid / gid match the currently logged in
user's uid / gid but how there's no easy
// way to get those with SFTP
if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR,
pack('Na*', strlen($dir), $dir))) {
return false;
}
// see \phpseclib\Net\SFTP::nlist() for a more thorough explanation
of the following
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or
SSH_FXP_STATUS');
return false;
}
if (!$this->_close_handle($handle)) {
return false;
}
$this->_update_stat_cache($dir, array());
$this->pwd = $dir;
return true;
}
/**
* Returns a list of files in the given directory
*
* @param string $dir
* @param bool $recursive
* @return mixed
* @access public
*/
function nlist($dir = '.', $recursive = false)
{
return $this->_nlist_helper($dir, $recursive, '');
}
/**
* Helper method for nlist
*
* @param string $dir
* @param bool $recursive
* @param string $relativeDir
* @return mixed
* @access private
*/
function _nlist_helper($dir, $recursive, $relativeDir)
{
$files = $this->_list($dir, false);
if (!$recursive || $files === false) {
return $files;
}
$result = array();
foreach ($files as $value) {
if ($value == '.' || $value == '..') {
if ($relativeDir == '') {
$result[] = $value;
}
continue;
}
if
(is_array($this->_query_stat_cache($this->_realpath($dir .
'/' . $value)))) {
$temp = $this->_nlist_helper($dir . '/' .
$value, true, $relativeDir . $value . '/');
$temp = is_array($temp) ? $temp : array();
$result = array_merge($result, $temp);
} else {
$result[] = $relativeDir . $value;
}
}
return $result;
}
/**
* Returns a detailed list of files in the given directory
*
* @param string $dir
* @param bool $recursive
* @return mixed
* @access public
*/
function rawlist($dir = '.', $recursive = false)
{
$files = $this->_list($dir, true);
if (!$recursive || $files === false) {
return $files;
}
static $depth = 0;
foreach ($files as $key => $value) {
if ($depth != 0 && $key == '..') {
unset($files[$key]);
continue;
}
$is_directory = false;
if ($key != '.' && $key != '..') {
if ($this->use_stat_cache) {
$is_directory =
is_array($this->_query_stat_cache($this->_realpath($dir .
'/' . $key)));
} else {
$stat = $this->lstat($dir . '/' . $key);
$is_directory = $stat &&
$stat['type'] === NET_SFTP_TYPE_DIRECTORY;
}
}
if ($is_directory) {
$depth++;
$files[$key] = $this->rawlist($dir . '/' .
$key, true);
$depth--;
} else {
$files[$key] = (object) $value;
}
}
return $files;
}
/**
* Reads a list, be it detailed or not, of files in the given directory
*
* @param string $dir
* @param bool $raw
* @return mixed
* @access private
*/
function _list($dir, $raw = true)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$dir = $this->_realpath($dir . '/');
if ($dir === false) {
return false;
}
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2
if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR,
pack('Na*', strlen($dir), $dir))) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2
// since 'handle' is the last field in the
SSH_FXP_HANDLE packet, we'll just remove the first four bytes that
// represent the length of the string and leave it at that
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS:
// presumably SSH_FX_NO_SUCH_FILE or
SSH_FX_PERMISSION_DENIED
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or
SSH_FXP_STATUS');
return false;
}
$this->_update_stat_cache($dir, array());
$contents = array();
while (true) {
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2
// why multiple SSH_FXP_READDIR packets would be sent when the
response to a single one can span arbitrarily many
// SSH_MSG_CHANNEL_DATA messages is not known to me.
if (!$this->_send_sftp_packet(NET_SFTP_READDIR,
pack('Na*', strlen($handle), $handle))) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_NAME:
if (strlen($response) < 4) {
return false;
}
extract(unpack('Ncount',
$this->_string_shift($response, 4)));
for ($i = 0; $i < $count; $i++) {
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$shortname = $this->_string_shift($response,
$length);
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$longname = $this->_string_shift($response,
$length);
$attributes =
$this->_parseAttributes($response);
if (!isset($attributes['type'])) {
$fileType =
$this->_parseLongname($longname);
if ($fileType) {
$attributes['type'] = $fileType;
}
}
$contents[$shortname] = $attributes +
array('filename' => $shortname);
if (isset($attributes['type']) &&
$attributes['type'] == NET_SFTP_TYPE_DIRECTORY &&
($shortname != '.' && $shortname != '..')) {
$this->_update_stat_cache($dir .
'/' . $shortname, array());
} else {
if ($shortname == '..') {
$temp = $this->_realpath($dir .
'/..') . '/.';
} else {
$temp = $dir . '/' . $shortname;
}
$this->_update_stat_cache($temp, (object)
array('lstat' => $attributes));
}
// SFTPv6 has an optional boolean end-of-list
field, but we'll ignore that, since the
// final SSH_FXP_STATUS packet should tell us that,
already.
}
break;
case NET_SFTP_STATUS:
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_EOF) {
$this->_logError($response, $status);
return false;
}
break 2;
default:
user_error('Expected SSH_FXP_NAME or
SSH_FXP_STATUS');
return false;
}
}
if (!$this->_close_handle($handle)) {
return false;
}
if (count($this->sortOptions)) {
uasort($contents, array(&$this, '_comparator'));
}
return $raw ? $contents : array_map('strval',
array_keys($contents));
}
/**
* Compares two rawlist entries using parameters set by setListOrder()
*
* Intended for use with uasort()
*
* @param array $a
* @param array $b
* @return int
* @access private
*/
function _comparator($a, $b)
{
switch (true) {
case $a['filename'] === '.' ||
$b['filename'] === '.':
if ($a['filename'] === $b['filename'])
{
return 0;
}
return $a['filename'] === '.' ? -1 : 1;
case $a['filename'] === '..' ||
$b['filename'] === '..':
if ($a['filename'] === $b['filename'])
{
return 0;
}
return $a['filename'] === '..' ? -1 :
1;
case isset($a['type']) &&
$a['type'] === NET_SFTP_TYPE_DIRECTORY:
if (!isset($b['type'])) {
return 1;
}
if ($b['type'] !== $a['type']) {
return -1;
}
break;
case isset($b['type']) &&
$b['type'] === NET_SFTP_TYPE_DIRECTORY:
return 1;
}
foreach ($this->sortOptions as $sort => $order) {
if (!isset($a[$sort]) || !isset($b[$sort])) {
if (isset($a[$sort])) {
return -1;
}
if (isset($b[$sort])) {
return 1;
}
return 0;
}
switch ($sort) {
case 'filename':
$result = strcasecmp($a['filename'],
$b['filename']);
if ($result) {
return $order === SORT_DESC ? -$result : $result;
}
break;
case 'permissions':
case 'mode':
$a[$sort]&= 07777;
$b[$sort]&= 07777;
default:
if ($a[$sort] === $b[$sort]) {
break;
}
return $order === SORT_ASC ? $a[$sort] - $b[$sort] :
$b[$sort] - $a[$sort];
}
}
}
/**
* Defines how nlist() and rawlist() will be sorted - if at all.
*
* If sorting is enabled directories and files will be sorted
independently with
* directories appearing before files in the resultant array that is
returned.
*
* Any parameter returned by stat is a valid sort parameter for this
function.
* Filename comparisons are case insensitive.
*
* Examples:
*
* $sftp->setListOrder('filename', SORT_ASC);
* $sftp->setListOrder('size', SORT_DESC,
'filename', SORT_ASC);
* $sftp->setListOrder(true);
* Separates directories from files but doesn't do any sorting
beyond that
* $sftp->setListOrder();
* Don't do any sort of sorting
*
* @access public
*/
function setListOrder()
{
$this->sortOptions = array();
$args = func_get_args();
if (empty($args)) {
return;
}
$len = count($args) & 0x7FFFFFFE;
for ($i = 0; $i < $len; $i+=2) {
$this->sortOptions[$args[$i]] = $args[$i + 1];
}
if (!count($this->sortOptions)) {
$this->sortOptions = array('bogus' => true);
}
}
/**
* Returns the file size, in bytes, or false, on failure
*
* Files larger than 4GB will show up as being exactly 4GB.
*
* @param string $filename
* @return mixed
* @access public
*/
function size($filename)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$result = $this->stat($filename);
if ($result === false) {
return false;
}
return isset($result['size']) ? $result['size']
: -1;
}
/**
* Save files / directories to cache
*
* @param string $path
* @param mixed $value
* @access private
*/
function _update_stat_cache($path, $value)
{
if ($this->use_stat_cache === false) {
return;
}
// preg_replace('#^/|/(?=/)|/$#', '', $dir) ==
str_replace('//', '/', trim($path, '/'))
$dirs = explode('/',
preg_replace('#^/|/(?=/)|/$#', '', $path));
$temp = &$this->stat_cache;
$max = count($dirs) - 1;
foreach ($dirs as $i => $dir) {
// if $temp is an object that means one of two things.
// 1. a file was deleted and changed to a directory behind
phpseclib's back
// 2. it's a symlink. when lstat is done it's
unclear what it's a symlink to
if (is_object($temp)) {
$temp = array();
}
if (!isset($temp[$dir])) {
$temp[$dir] = array();
}
if ($i === $max) {
if (is_object($temp[$dir]) && is_object($value)) {
if (!isset($value->stat) &&
isset($temp[$dir]->stat)) {
$value->stat = $temp[$dir]->stat;
}
if (!isset($value->lstat) &&
isset($temp[$dir]->lstat)) {
$value->lstat = $temp[$dir]->lstat;
}
}
$temp[$dir] = $value;
break;
}
$temp = &$temp[$dir];
}
}
/**
* Remove files / directories from cache
*
* @param string $path
* @return bool
* @access private
*/
function _remove_from_stat_cache($path)
{
$dirs = explode('/',
preg_replace('#^/|/(?=/)|/$#', '', $path));
$temp = &$this->stat_cache;
$max = count($dirs) - 1;
foreach ($dirs as $i => $dir) {
if (!is_array($temp)) {
return false;
}
if ($i === $max) {
unset($temp[$dir]);
return true;
}
if (!isset($temp[$dir])) {
return false;
}
$temp = &$temp[$dir];
}
}
/**
* Checks cache for path
*
* Mainly used by file_exists
*
* @param string $path
* @return mixed
* @access private
*/
function _query_stat_cache($path)
{
$dirs = explode('/',
preg_replace('#^/|/(?=/)|/$#', '', $path));
$temp = &$this->stat_cache;
foreach ($dirs as $dir) {
if (!is_array($temp)) {
return null;
}
if (!isset($temp[$dir])) {
return null;
}
$temp = &$temp[$dir];
}
return $temp;
}
/**
* Returns general information about a file.
*
* Returns an array on success and false otherwise.
*
* @param string $filename
* @return mixed
* @access public
*/
function stat($filename)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$filename = $this->_realpath($filename);
if ($filename === false) {
return false;
}
if ($this->use_stat_cache) {
$result = $this->_query_stat_cache($filename);
if (is_array($result) && isset($result['.'])
&& isset($result['.']->stat)) {
return $result['.']->stat;
}
if (is_object($result) && isset($result->stat)) {
return $result->stat;
}
}
$stat = $this->_stat($filename, NET_SFTP_STAT);
if ($stat === false) {
$this->_remove_from_stat_cache($filename);
return false;
}
if (isset($stat['type'])) {
if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) {
$filename.= '/.';
}
$this->_update_stat_cache($filename, (object)
array('stat' => $stat));
return $stat;
}
$pwd = $this->pwd;
$stat['type'] = $this->chdir($filename) ?
NET_SFTP_TYPE_DIRECTORY :
NET_SFTP_TYPE_REGULAR;
$this->pwd = $pwd;
if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) {
$filename.= '/.';
}
$this->_update_stat_cache($filename, (object)
array('stat' => $stat));
return $stat;
}
/**
* Returns general information about a file or symbolic link.
*
* Returns an array on success and false otherwise.
*
* @param string $filename
* @return mixed
* @access public
*/
function lstat($filename)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$filename = $this->_realpath($filename);
if ($filename === false) {
return false;
}
if ($this->use_stat_cache) {
$result = $this->_query_stat_cache($filename);
if (is_array($result) && isset($result['.'])
&& isset($result['.']->lstat)) {
return $result['.']->lstat;
}
if (is_object($result) && isset($result->lstat)) {
return $result->lstat;
}
}
$lstat = $this->_stat($filename, NET_SFTP_LSTAT);
if ($lstat === false) {
$this->_remove_from_stat_cache($filename);
return false;
}
if (isset($lstat['type'])) {
if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) {
$filename.= '/.';
}
$this->_update_stat_cache($filename, (object)
array('lstat' => $lstat));
return $lstat;
}
$stat = $this->_stat($filename, NET_SFTP_STAT);
if ($lstat != $stat) {
$lstat = array_merge($lstat, array('type' =>
NET_SFTP_TYPE_SYMLINK));
$this->_update_stat_cache($filename, (object)
array('lstat' => $lstat));
return $stat;
}
$pwd = $this->pwd;
$lstat['type'] = $this->chdir($filename) ?
NET_SFTP_TYPE_DIRECTORY :
NET_SFTP_TYPE_REGULAR;
$this->pwd = $pwd;
if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) {
$filename.= '/.';
}
$this->_update_stat_cache($filename, (object)
array('lstat' => $lstat));
return $lstat;
}
/**
* Returns general information about a file or symbolic link
*
* Determines information without calling
\phpseclib\Net\SFTP::realpath().
* The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT.
*
* @param string $filename
* @param int $type
* @return mixed
* @access private
*/
function _stat($filename, $type)
{
// SFTPv4+ adds an additional 32-bit integer field - flags - to the
following:
$packet = pack('Na*', strlen($filename), $filename);
if (!$this->_send_sftp_packet($type, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_ATTRS:
return $this->_parseAttributes($response);
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
}
user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS');
return false;
}
/**
* Truncates a file to a given length
*
* @param string $filename
* @param int $new_size
* @return bool
* @access public
*/
function truncate($filename, $new_size)
{
$attr = pack('N3', NET_SFTP_ATTR_SIZE, $new_size /
4294967296, $new_size); // 4294967296 == 0x100000000 == 1<<32
return $this->_setstat($filename, $attr, false);
}
/**
* Sets access and modification time of file.
*
* If the file does not exist, it will be created.
*
* @param string $filename
* @param int $time
* @param int $atime
* @return bool
* @access public
*/
function touch($filename, $time = null, $atime = null)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$filename = $this->_realpath($filename);
if ($filename === false) {
return false;
}
if (!isset($time)) {
$time = time();
}
if (!isset($atime)) {
$atime = $time;
}
$flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE |
NET_SFTP_OPEN_EXCL;
$attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $time,
$atime);
$packet = pack('Na*Na*', strlen($filename), $filename,
$flags, $attr);
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
return $this->_close_handle(substr($response, 4));
case NET_SFTP_STATUS:
$this->_logError($response);
break;
default:
user_error('Expected SSH_FXP_HANDLE or
SSH_FXP_STATUS');
return false;
}
return $this->_setstat($filename, $attr, false);
}
/**
* Changes file or directory owner
*
* Returns true on success or false on error.
*
* @param string $filename
* @param int $uid
* @param bool $recursive
* @return bool
* @access public
*/
function chown($filename, $uid, $recursive = false)
{
// quoting from
<http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>,
// "if the owner or group is specified as -1, then that ID is
not changed"
$attr = pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1);
return $this->_setstat($filename, $attr, $recursive);
}
/**
* Changes file or directory group
*
* Returns true on success or false on error.
*
* @param string $filename
* @param int $gid
* @param bool $recursive
* @return bool
* @access public
*/
function chgrp($filename, $gid, $recursive = false)
{
$attr = pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid);
return $this->_setstat($filename, $attr, $recursive);
}
/**
* Set permissions on a file.
*
* Returns the new file permissions on success or false on error.
* If $recursive is true than this just returns true or false.
*
* @param int $mode
* @param string $filename
* @param bool $recursive
* @return mixed
* @access public
*/
function chmod($mode, $filename, $recursive = false)
{
if (is_string($mode) && is_int($filename)) {
$temp = $mode;
$mode = $filename;
$filename = $temp;
}
$attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode &
07777);
if (!$this->_setstat($filename, $attr, $recursive)) {
return false;
}
if ($recursive) {
return true;
}
$filename = $this->realpath($filename);
// rather than return what the permissions *should* be, we'll
return what they actually are. this will also
// tell us if the file actually exists.
// incidentally, SFTPv4+ adds an additional 32-bit integer field -
flags - to the following:
$packet = pack('Na*', strlen($filename), $filename);
if (!$this->_send_sftp_packet(NET_SFTP_STAT, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_ATTRS:
$attrs = $this->_parseAttributes($response);
return $attrs['permissions'];
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
}
user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS');
return false;
}
/**
* Sets information about a file
*
* @param string $filename
* @param string $attr
* @param bool $recursive
* @return bool
* @access private
*/
function _setstat($filename, $attr, $recursive)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$filename = $this->_realpath($filename);
if ($filename === false) {
return false;
}
$this->_remove_from_stat_cache($filename);
if ($recursive) {
$i = 0;
$result = $this->_setstat_recursive($filename, $attr, $i);
$this->_read_put_responses($i);
return $result;
}
// SFTPv4+ has an additional byte field - type - that would need to
be sent, as well. setting it to
// SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to
do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT.
if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT,
pack('Na*a*', strlen($filename), $filename, $attr))) {
return false;
}
/*
"Because some systems must use separate system calls to set
various attributes, it is possible that a failure
response will be returned, but yet some of the attributes may be
have been successfully modified. If possible,
servers SHOULD avoid this situation; however, clients MUST be
aware that this is possible."
--
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6
*/
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
return false;
}
return true;
}
/**
* Recursively sets information on directories on the SFTP server
*
* Minimizes directory lookups and SSH_FXP_STATUS requests for speed.
*
* @param string $path
* @param string $attr
* @param int $i
* @return bool
* @access private
*/
function _setstat_recursive($path, $attr, &$i)
{
if (!$this->_read_put_responses($i)) {
return false;
}
$i = 0;
$entries = $this->_list($path, true);
if ($entries === false) {
return $this->_setstat($path, $attr, false);
}
// normally $entries would have at least . and .. but it might not
if the directories
// permissions didn't allow reading
if (empty($entries)) {
return false;
}
unset($entries['.'], $entries['..']);
foreach ($entries as $filename => $props) {
if (!isset($props['type'])) {
return false;
}
$temp = $path . '/' . $filename;
if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
if (!$this->_setstat_recursive($temp, $attr, $i)) {
return false;
}
} else {
if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT,
pack('Na*a*', strlen($temp), $temp, $attr))) {
return false;
}
$i++;
if ($i >= NET_SFTP_QUEUE_SIZE) {
if (!$this->_read_put_responses($i)) {
return false;
}
$i = 0;
}
}
}
if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT,
pack('Na*a*', strlen($path), $path, $attr))) {
return false;
}
$i++;
if ($i >= NET_SFTP_QUEUE_SIZE) {
if (!$this->_read_put_responses($i)) {
return false;
}
$i = 0;
}
return true;
}
/**
* Return the target of a symbolic link
*
* @param string $link
* @return mixed
* @access public
*/
function readlink($link)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$link = $this->_realpath($link);
if (!$this->_send_sftp_packet(NET_SFTP_READLINK,
pack('Na*', strlen($link), $link))) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_NAME:
break;
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_NAME or
SSH_FXP_STATUS');
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Ncount',
$this->_string_shift($response, 4)));
// the file isn't a symlink
if (!$count) {
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
return $this->_string_shift($response, $length);
}
/**
* Create a symlink
*
* symlink() creates a symbolic link to the existing target with the
specified name link.
*
* @param string $target
* @param string $link
* @return bool
* @access public
*/
function symlink($target, $link)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
//$target = $this->_realpath($target);
$link = $this->_realpath($link);
$packet = pack('Na*Na*', strlen($target), $target,
strlen($link), $link);
if (!$this->_send_sftp_packet(NET_SFTP_SYMLINK, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
return false;
}
return true;
}
/**
* Creates a directory.
*
* @param string $dir
* @param int $mode
* @param bool $recursive
* @return bool
* @access public
*/
function mkdir($dir, $mode = -1, $recursive = false)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$dir = $this->_realpath($dir);
if ($recursive) {
$dirs = explode('/',
preg_replace('#/(?=/)|/$#', '', $dir));
if (empty($dirs[0])) {
array_shift($dirs);
$dirs[0] = '/' . $dirs[0];
}
for ($i = 0; $i < count($dirs); $i++) {
$temp = array_slice($dirs, 0, $i + 1);
$temp = implode('/', $temp);
$result = $this->_mkdir_helper($temp, $mode);
}
return $result;
}
return $this->_mkdir_helper($dir, $mode);
}
/**
* Helper function for directory creation
*
* @param string $dir
* @param int $mode
* @return bool
* @access private
*/
function _mkdir_helper($dir, $mode)
{
// send SSH_FXP_MKDIR without any attributes (that's what the
\0\0\0\0 is doing)
if (!$this->_send_sftp_packet(NET_SFTP_MKDIR,
pack('Na*a*', strlen($dir), $dir, "\0\0\0\0"))) {
return false;
}
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
return false;
}
if ($mode !== -1) {
$this->chmod($mode, $dir);
}
return true;
}
/**
* Removes a directory.
*
* @param string $dir
* @return bool
* @access public
*/
function rmdir($dir)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$dir = $this->_realpath($dir);
if ($dir === false) {
return false;
}
if (!$this->_send_sftp_packet(NET_SFTP_RMDIR,
pack('Na*', strlen($dir), $dir))) {
return false;
}
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
// presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED?
$this->_logError($response, $status);
return false;
}
$this->_remove_from_stat_cache($dir);
// the following will do a soft delete, which would be useful if
you deleted a file
// and then tried to do a stat on the deleted file. the above, in
contrast, does
// a hard delete
//$this->_update_stat_cache($dir, false);
return true;
}
/**
* Uploads a file to the SFTP server.
*
* By default, \phpseclib\Net\SFTP::put() does not read from the local
filesystem. $data is dumped directly into $remote_file.
* So, for example, if you set $data to 'filename.ext' and
then do \phpseclib\Net\SFTP::get(), you will get a file, twelve bytes
* long, containing 'filename.ext' as its contents.
*
* Setting $mode to self::SOURCE_LOCAL_FILE will change the above
behavior. With self::SOURCE_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
* large $remote_file will be, as well.
*
* Setting $mode to self::SOURCE_CALLBACK will use $data as callback
function, which gets only one parameter -- number of bytes to return, and
returns a string if there is some data or null if there is no more data
*
* If $data is a resource then it'll be used as a resource
instead.
*
* Currently, only binary mode is supported. As such, if the line
endings need to be adjusted, you will need to take
* care of that, yourself.
*
* $mode can take an additional two parameters - self::RESUME and
self::RESUME_START. These are bitwise AND'd with
* $mode. So if you want to resume upload of a 300mb file on the local
file system you'd set $mode to the following:
*
* self::SOURCE_LOCAL_FILE | self::RESUME
*
* If you wanted to simply append the full contents of a local file to
the full contents of a remote file you'd replace
* self::RESUME with self::RESUME_START.
*
* If $mode & (self::RESUME | self::RESUME_START) then
self::RESUME_START will be assumed.
*
* $start and $local_start give you more fine grained control over this
process and take precident over self::RESUME
* when they're non-negative. ie. $start could let you write at
the end of a file (like self::RESUME) or in the middle
* of one. $local_start could let you start your reading from the end
of a file (like self::RESUME_START) or in the
* middle of one.
*
* Setting $local_start to > 0 or $mode | self::RESUME_START
doesn't do anything unless $mode | self::SOURCE_LOCAL_FILE.
*
* @param string $remote_file
* @param string|resource $data
* @param int $mode
* @param int $start
* @param int $local_start
* @param callable|null $progressCallback
* @return bool
* @access public
* @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new
function - \phpseclib\Net\SFTP::setMode().
*/
function put($remote_file, $data, $mode = self::SOURCE_STRING, $start =
-1, $local_start = -1, $progressCallback = null)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$remote_file = $this->_realpath($remote_file);
if ($remote_file === false) {
return false;
}
$this->_remove_from_stat_cache($remote_file);
$flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE;
// according to the SFTP specs, NET_SFTP_OPEN_APPEND should
"force all writes to append data at the end of the file."
// in practice, it doesn't seem to do that.
//$flags|= ($mode & self::RESUME) ? NET_SFTP_OPEN_APPEND :
NET_SFTP_OPEN_TRUNCATE;
if ($start >= 0) {
$offset = $start;
} elseif ($mode & self::RESUME) {
// if NET_SFTP_OPEN_APPEND worked as it should _size()
wouldn't need to be called
$size = $this->size($remote_file);
$offset = $size !== false ? $size : 0;
} else {
$offset = 0;
$flags|= NET_SFTP_OPEN_TRUNCATE;
}
$packet = pack('Na*N2', strlen($remote_file),
$remote_file, $flags, 0);
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or
SSH_FXP_STATUS');
return false;
}
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3
$dataCallback = false;
switch (true) {
case $mode & self::SOURCE_CALLBACK:
if (!is_callable($data)) {
user_error("\$data should be is_callable() if you
specify SOURCE_CALLBACK flag");
}
$dataCallback = $data;
// do nothing
break;
case is_resource($data):
$mode = $mode & ~self::SOURCE_LOCAL_FILE;
$info = stream_get_meta_data($data);
if ($info['wrapper_type'] == 'PHP'
&& $info['stream_type'] == 'Input') {
$fp = fopen('php://memory', 'w+');
stream_copy_to_stream($data, $fp);
rewind($fp);
} else {
$fp = $data;
}
break;
case $mode & self::SOURCE_LOCAL_FILE:
if (!is_file($data)) {
user_error("$data is not a valid file");
return false;
}
$fp = @fopen($data, 'rb');
if (!$fp) {
return false;
}
}
if (isset($fp)) {
$stat = fstat($fp);
$size = !empty($stat) ? $stat['size'] : 0;
if ($local_start >= 0) {
fseek($fp, $local_start);
$size-= $local_start;
}
} elseif ($dataCallback) {
$size = 0;
} else {
$size = strlen($data);
}
$sent = 0;
$size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 :
$size;
$sftp_packet_size = $this->max_sftp_packet;
// make the SFTP packet be exactly the SFTP packet size by
including the bytes in the NET_SFTP_WRITE packets "header"
$sftp_packet_size-= strlen($handle) + 25;
$i = $j = 0;
while ($dataCallback || ($size === 0 || $sent < $size)) {
if ($dataCallback) {
$temp = call_user_func($dataCallback, $sftp_packet_size);
if (is_null($temp)) {
break;
}
} else {
$temp = isset($fp) ? fread($fp, $sftp_packet_size) :
substr($data, $sent, $sftp_packet_size);
if ($temp === false || $temp === '') {
break;
}
}
$subtemp = $offset + $sent;
$packet = pack('Na*N3a*', strlen($handle), $handle,
$subtemp / 4294967296, $subtemp, strlen($temp), $temp);
if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet, $j))
{
if ($mode & self::SOURCE_LOCAL_FILE) {
fclose($fp);
}
return false;
}
$sent+= strlen($temp);
if (is_callable($progressCallback)) {
call_user_func($progressCallback, $sent);
}
$i++;
$j++;
if ($i == NET_SFTP_UPLOAD_QUEUE_SIZE) {
if (!$this->_read_put_responses($i)) {
$i = 0;
break;
}
$i = 0;
}
}
if (!$this->_read_put_responses($i)) {
if ($mode & self::SOURCE_LOCAL_FILE) {
fclose($fp);
}
$this->_close_handle($handle);
return false;
}
if ($mode & self::SOURCE_LOCAL_FILE) {
if ($this->preserveTime) {
$stat = fstat($fp);
$this->touch($remote_file, $stat['mtime'],
$stat['atime']);
}
if (isset($fp) && is_resource($fp)) {
fclose($fp);
}
}
return $this->_close_handle($handle);
}
/**
* Reads multiple successive SSH_FXP_WRITE responses
*
* Sending an SSH_FXP_WRITE packet and immediately reading its response
isn't as efficient as blindly sending out $i
* SSH_FXP_WRITEs, in succession, and then reading $i responses.
*
* @param int $i
* @return bool
* @access private
*/
function _read_put_responses($i)
{
while ($i--) {
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
break;
}
}
return $i < 0;
}
/**
* Close handle
*
* @param string $handle
* @return bool
* @access private
*/
function _close_handle($handle)
{
if (!$this->_send_sftp_packet(NET_SFTP_CLOSE,
pack('Na*', strlen($handle), $handle))) {
return false;
}
// "The client MUST release all resources associated with the
handle regardless of the status."
// --
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
return false;
}
return true;
}
/**
* Downloads a file from the SFTP server.
*
* Returns a string containing the contents of $remote_file if
$local_file is left undefined or a boolean false if
* the operation was unsuccessful. If $local_file is defined, returns
true or false depending on the success of the
* operation.
*
* $offset and $length can be used to download files in chunks.
*
* @param string $remote_file
* @param string $local_file
* @param int $offset
* @param int $length
* @param callable|null $progressCallback
* @return mixed
* @access public
*/
function get($remote_file, $local_file = false, $offset = 0, $length =
-1, $progressCallback = null)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$remote_file = $this->_realpath($remote_file);
if ($remote_file === false) {
return false;
}
$packet = pack('Na*N2', strlen($remote_file),
$remote_file, NET_SFTP_OPEN_READ, 0);
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or
SSH_FX_PERMISSION_DENIED
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or
SSH_FXP_STATUS');
return false;
}
if (is_resource($local_file)) {
$fp = $local_file;
$stat = fstat($fp);
$res_offset = $stat['size'];
} else {
$res_offset = 0;
if ($local_file !== false && !is_callable($local_file))
{
$fp = fopen($local_file, 'wb');
if (!$fp) {
return false;
}
} else {
$content = '';
}
}
$fclose_check = $local_file !== false &&
!is_callable($local_file) && !is_resource($local_file);
$start = $offset;
$read = 0;
while (true) {
$i = 0;
while ($i < NET_SFTP_QUEUE_SIZE && ($length < 0
|| $read < $length)) {
$tempoffset = $start + $read;
$packet_size = $length > 0 ?
min($this->max_sftp_packet, $length - $read) :
$this->max_sftp_packet;
$packet = pack('Na*N3', strlen($handle), $handle,
$tempoffset / 4294967296, $tempoffset, $packet_size);
if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet,
$i)) {
if ($fclose_check) {
fclose($fp);
}
return false;
}
$packet = null;
$read+= $packet_size;
$i++;
}
if (!$i) {
break;
}
$packets_sent = $i - 1;
$clear_responses = false;
while ($i > 0) {
$i--;
if ($clear_responses) {
$this->_get_sftp_packet($packets_sent - $i);
continue;
} else {
$response = $this->_get_sftp_packet($packets_sent -
$i);
}
switch ($this->packet_type) {
case NET_SFTP_DATA:
$temp = substr($response, 4);
$offset+= strlen($temp);
if ($local_file === false) {
$content.= $temp;
} elseif (is_callable($local_file)) {
$local_file($temp);
} else {
fputs($fp, $temp);
}
if (is_callable($progressCallback)) {
call_user_func($progressCallback, $offset);
}
$temp = null;
break;
case NET_SFTP_STATUS:
// could, in theory, return false if
!strlen($content) but we'll hold off for the time being
$this->_logError($response);
$clear_responses = true; // don't break out of
the loop yet, so we can read the remaining responses
break;
default:
if ($fclose_check) {
fclose($fp);
}
// maybe the file was successfully transferred,
maybe it wasn't
if ($this->channel_close) {
$this->_init_sftp_connection();
return false;
} else {
user_error('Expected SSH_FX_DATA or
SSH_FXP_STATUS');
}
}
$response = null;
}
if ($clear_responses) {
break;
}
}
if ($length > 0 && $length <= $offset - $start) {
if ($local_file === false) {
$content = substr($content, 0, $length);
} else {
ftruncate($fp, $length + $res_offset);
}
}
if ($fclose_check) {
fclose($fp);
if ($this->preserveTime) {
$stat = $this->stat($remote_file);
touch($local_file, $stat['mtime'],
$stat['atime']);
}
}
if (!$this->_close_handle($handle)) {
return false;
}
// if $content isn't set that means a file was written to
return isset($content) ? $content : true;
}
/**
* Deletes a file on the SFTP server.
*
* @param string $path
* @param bool $recursive
* @return bool
* @access public
*/
function delete($path, $recursive = true)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
if (is_object($path)) {
// It's an object. Cast it as string before we check
anything else.
$path = (string) $path;
}
if (!is_string($path) || $path == '') {
return false;
}
$path = $this->_realpath($path);
if ($path === false) {
return false;
}
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
if (!$this->_send_sftp_packet(NET_SFTP_REMOVE,
pack('Na*', strlen($path), $path))) {
return false;
}
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}
// if $status isn't SSH_FX_OK it's probably
SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
if (!$recursive) {
return false;
}
$i = 0;
$result = $this->_delete_recursive($path, $i);
$this->_read_put_responses($i);
return $result;
}
$this->_remove_from_stat_cache($path);
return true;
}
/**
* Recursively deletes directories on the SFTP server
*
* Minimizes directory lookups and SSH_FXP_STATUS requests for speed.
*
* @param string $path
* @param int $i
* @return bool
* @access private
*/
function _delete_recursive($path, &$i)
{
if (!$this->_read_put_responses($i)) {
return false;
}
$i = 0;
$entries = $this->_list($path, true);
// normally $entries would have at least . and .. but it might not
if the directories
// permissions didn't allow reading
if (empty($entries)) {
return false;
}
unset($entries['.'], $entries['..']);
foreach ($entries as $filename => $props) {
if (!isset($props['type'])) {
return false;
}
$temp = $path . '/' . $filename;
if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
if (!$this->_delete_recursive($temp, $i)) {
return false;
}
} else {
if (!$this->_send_sftp_packet(NET_SFTP_REMOVE,
pack('Na*', strlen($temp), $temp))) {
return false;
}
$this->_remove_from_stat_cache($temp);
$i++;
if ($i >= NET_SFTP_QUEUE_SIZE) {
if (!$this->_read_put_responses($i)) {
return false;
}
$i = 0;
}
}
}
if (!$this->_send_sftp_packet(NET_SFTP_RMDIR,
pack('Na*', strlen($path), $path))) {
return false;
}
$this->_remove_from_stat_cache($path);
$i++;
if ($i >= NET_SFTP_QUEUE_SIZE) {
if (!$this->_read_put_responses($i)) {
return false;
}
$i = 0;
}
return true;
}
/**
* Checks whether a file or directory exists
*
* @param string $path
* @return bool
* @access public
*/
function file_exists($path)
{
if ($this->use_stat_cache) {
$path = $this->_realpath($path);
$result = $this->_query_stat_cache($path);
if (isset($result)) {
// return true if $result is an array or if it's an
stdClass object
return $result !== false;
}
}
return $this->stat($path) !== false;
}
/**
* Tells whether the filename is a directory
*
* @param string $path
* @return bool
* @access public
*/
function is_dir($path)
{
$result = $this->_get_stat_cache_prop($path, 'type');
if ($result === false) {
return false;
}
return $result === NET_SFTP_TYPE_DIRECTORY;
}
/**
* Tells whether the filename is a regular file
*
* @param string $path
* @return bool
* @access public
*/
function is_file($path)
{
$result = $this->_get_stat_cache_prop($path, 'type');
if ($result === false) {
return false;
}
return $result === NET_SFTP_TYPE_REGULAR;
}
/**
* Tells whether the filename is a symbolic link
*
* @param string $path
* @return bool
* @access public
*/
function is_link($path)
{
$result = $this->_get_lstat_cache_prop($path, 'type');
if ($result === false) {
return false;
}
return $result === NET_SFTP_TYPE_SYMLINK;
}
/**
* Tells whether a file exists and is readable
*
* @param string $path
* @return bool
* @access public
*/
function is_readable($path)
{
$path = $this->_realpath($path);
$packet = pack('Na*N2', strlen($path), $path,
NET_SFTP_OPEN_READ, 0);
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
return true;
case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or
SSH_FX_PERMISSION_DENIED
return false;
default:
user_error('Expected SSH_FXP_HANDLE or
SSH_FXP_STATUS');
return false;
}
}
/**
* Tells whether the filename is writable
*
* @param string $path
* @return bool
* @access public
*/
function is_writable($path)
{
$path = $this->_realpath($path);
$packet = pack('Na*N2', strlen($path), $path,
NET_SFTP_OPEN_WRITE, 0);
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
return true;
case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or
SSH_FX_PERMISSION_DENIED
return false;
default:
user_error('Expected SSH_FXP_HANDLE or
SSH_FXP_STATUS');
return false;
}
}
/**
* Tells whether the filename is writeable
*
* Alias of is_writable
*
* @param string $path
* @return bool
* @access public
*/
function is_writeable($path)
{
return $this->is_writable($path);
}
/**
* Gets last access time of file
*
* @param string $path
* @return mixed
* @access public
*/
function fileatime($path)
{
return $this->_get_stat_cache_prop($path, 'atime');
}
/**
* Gets file modification time
*
* @param string $path
* @return mixed
* @access public
*/
function filemtime($path)
{
return $this->_get_stat_cache_prop($path, 'mtime');
}
/**
* Gets file permissions
*
* @param string $path
* @return mixed
* @access public
*/
function fileperms($path)
{
return $this->_get_stat_cache_prop($path,
'permissions');
}
/**
* Gets file owner
*
* @param string $path
* @return mixed
* @access public
*/
function fileowner($path)
{
return $this->_get_stat_cache_prop($path, 'uid');
}
/**
* Gets file group
*
* @param string $path
* @return mixed
* @access public
*/
function filegroup($path)
{
return $this->_get_stat_cache_prop($path, 'gid');
}
/**
* Gets file size
*
* @param string $path
* @return mixed
* @access public
*/
function filesize($path)
{
return $this->_get_stat_cache_prop($path, 'size');
}
/**
* Gets file type
*
* @param string $path
* @return mixed
* @access public
*/
function filetype($path)
{
$type = $this->_get_stat_cache_prop($path, 'type');
if ($type === false) {
return false;
}
switch ($type) {
case NET_SFTP_TYPE_BLOCK_DEVICE:
return 'block';
case NET_SFTP_TYPE_CHAR_DEVICE:
return 'char';
case NET_SFTP_TYPE_DIRECTORY:
return 'dir';
case NET_SFTP_TYPE_FIFO:
return 'fifo';
case NET_SFTP_TYPE_REGULAR:
return 'file';
case NET_SFTP_TYPE_SYMLINK:
return 'link';
default:
return false;
}
}
/**
* Return a stat properity
*
* Uses cache if appropriate.
*
* @param string $path
* @param string $prop
* @return mixed
* @access private
*/
function _get_stat_cache_prop($path, $prop)
{
return $this->_get_xstat_cache_prop($path, $prop,
'stat');
}
/**
* Return an lstat properity
*
* Uses cache if appropriate.
*
* @param string $path
* @param string $prop
* @return mixed
* @access private
*/
function _get_lstat_cache_prop($path, $prop)
{
return $this->_get_xstat_cache_prop($path, $prop,
'lstat');
}
/**
* Return a stat or lstat properity
*
* Uses cache if appropriate.
*
* @param string $path
* @param string $prop
* @param mixed $type
* @return mixed
* @access private
*/
function _get_xstat_cache_prop($path, $prop, $type)
{
if ($this->use_stat_cache) {
$path = $this->_realpath($path);
$result = $this->_query_stat_cache($path);
if (is_object($result) && isset($result->$type)) {
return $result->{$type}[$prop];
}
}
$result = $this->$type($path);
if ($result === false || !isset($result[$prop])) {
return false;
}
return $result[$prop];
}
/**
* Renames a file or a directory on the SFTP server
*
* @param string $oldname
* @param string $newname
* @return bool
* @access public
*/
function rename($oldname, $newname)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$oldname = $this->_realpath($oldname);
$newname = $this->_realpath($newname);
if ($oldname === false || $newname === false) {
return false;
}
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
$packet = pack('Na*Na*', strlen($oldname), $oldname,
strlen($newname), $newname);
if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}
// if $status isn't SSH_FX_OK it's probably
SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus',
$this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
return false;
}
// don't move the stat cache entry over since this operation
could very well change the
// atime and mtime attributes
//$this->_update_stat_cache($newname,
$this->_query_stat_cache($oldname));
$this->_remove_from_stat_cache($oldname);
$this->_remove_from_stat_cache($newname);
return true;
}
/**
* Parse Attributes
*
* See '7. File Attributes' of draft-ietf-secsh-filexfer-13
for more info.
*
* @param string $response
* @return array
* @access private
*/
function _parseAttributes(&$response)
{
$attr = array();
if (strlen($response) < 4) {
user_error('Malformed file attributes');
return array();
}
extract(unpack('Nflags',
$this->_string_shift($response, 4)));
// SFTPv4+ have a type field (a byte) that follows the above flag
field
foreach ($this->attributes as $key => $value) {
switch ($flags & $key) {
case NET_SFTP_ATTR_SIZE: // 0x00000001
// The size attribute is defined as an unsigned 64-bit
integer.
// The following will use floats on 32-bit platforms,
if necessary.
// As can be seen in the BigInteger class, floats are
generally
// IEEE 754 binary64 "double precision" on
such platforms and
// as such can represent integers of at least 2^50
without loss
// of precision. Interpreted in filesize, 2^50 bytes =
1024 TiB.
$attr['size'] =
hexdec(bin2hex($this->_string_shift($response, 8)));
break;
case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only)
if (strlen($response) < 8) {
user_error('Malformed file attributes');
return $attr;
}
$attr+= unpack('Nuid/Ngid',
$this->_string_shift($response, 8));
break;
case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004
if (strlen($response) < 4) {
user_error('Malformed file attributes');
return $attr;
}
$attr+= unpack('Npermissions',
$this->_string_shift($response, 4));
// mode == permissions; permissions was the original
array key and is retained for bc purposes.
// mode was added because that's the more industry
standard terminology
$attr+= array('mode' =>
$attr['permissions']);
$fileType =
$this->_parseMode($attr['permissions']);
if ($fileType !== false) {
$attr+= array('type' => $fileType);
}
break;
case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008
if (strlen($response) < 8) {
user_error('Malformed file attributes');
return $attr;
}
$attr+= unpack('Natime/Nmtime',
$this->_string_shift($response, 8));
break;
case NET_SFTP_ATTR_EXTENDED: // 0x80000000
if (strlen($response) < 4) {
user_error('Malformed file attributes');
return $attr;
}
extract(unpack('Ncount',
$this->_string_shift($response, 4)));
for ($i = 0; $i < $count; $i++) {
if (strlen($response) < 4) {
user_error('Malformed file
attributes');
return $attr;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$key = $this->_string_shift($response, $length);
if (strlen($response) < 4) {
user_error('Malformed file
attributes');
return $attr;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$attr[$key] = $this->_string_shift($response,
$length);
}
}
}
return $attr;
}
/**
* Attempt to identify the file type
*
* Quoting the SFTP RFC, "Implementations MUST NOT send bits that
are not defined" but they seem to anyway
*
* @param int $mode
* @return int
* @access private
*/
function _parseMode($mode)
{
// values come from
http://lxr.free-electrons.com/source/include/uapi/linux/stat.h#L12
// see, also, http://linux.die.net/man/2/stat
switch ($mode & 0170000) {// ie. 1111 0000 0000 0000
case 0000000: // no file type specified - figure out the file
type using alternative means
return false;
case 0040000:
return NET_SFTP_TYPE_DIRECTORY;
case 0100000:
return NET_SFTP_TYPE_REGULAR;
case 0120000:
return NET_SFTP_TYPE_SYMLINK;
// new types introduced in SFTPv5+
//
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
case 0010000: // named pipe (fifo)
return NET_SFTP_TYPE_FIFO;
case 0020000: // character special
return NET_SFTP_TYPE_CHAR_DEVICE;
case 0060000: // block special
return NET_SFTP_TYPE_BLOCK_DEVICE;
case 0140000: // socket
return NET_SFTP_TYPE_SOCKET;
case 0160000: // whiteout
// "SPECIAL should be used for files that are of
// a known type which cannot be expressed in the
protocol"
return NET_SFTP_TYPE_SPECIAL;
default:
return NET_SFTP_TYPE_UNKNOWN;
}
}
/**
* Parse Longname
*
* SFTPv3 doesn't provide any easy way of identifying a file type.
You could try to open
* a file as a directory and see if an error is returned or you could
try to parse the
* SFTPv3-specific longname field of the SSH_FXP_NAME packet.
That's what this function does.
* The result is returned using the
* {@link
http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 SFTPv4
type constants}.
*
* If the longname is in an unrecognized format bool(false) is
returned.
*
* @param string $longname
* @return mixed
* @access private
*/
function _parseLongname($longname)
{
// http://en.wikipedia.org/wiki/Unix_file_types
//
http://en.wikipedia.org/wiki/Filesystem_permissions#Notation_of_traditional_Unix_permissions
if (preg_match('#^[^/]([r-][w-][xstST-]){3}#',
$longname)) {
switch ($longname[0]) {
case '-':
return NET_SFTP_TYPE_REGULAR;
case 'd':
return NET_SFTP_TYPE_DIRECTORY;
case 'l':
return NET_SFTP_TYPE_SYMLINK;
default:
return NET_SFTP_TYPE_SPECIAL;
}
}
return false;
}
/**
* Sends SFTP Packets
*
* See '6. General Packet Format' of
draft-ietf-secsh-filexfer-13 for more info.
*
* @param int $type
* @param string $data
* @param int $request_id
* @see self::_get_sftp_packet()
* @see self::_send_channel_packet()
* @return bool
* @access private
*/
function _send_sftp_packet($type, $data, $request_id = 1)
{
// in SSH2.php the timeout is cumulative per function call. eg.
exec() will
// timeout after 10s. but for SFTP.php it's cumulative per
packet
$this->curTimeout = $this->timeout;
$packet = $this->use_request_id ?
pack('NCNa*', strlen($data) + 5, $type, $request_id,
$data) :
pack('NCa*', strlen($data) + 1, $type, $data);
$start = strtok(microtime(), ' ') + strtok('');
// http://php.net/microtime#61838
$result = $this->_send_channel_packet(self::CHANNEL, $packet);
$stop = strtok(microtime(), ' ') + strtok('');
if (defined('NET_SFTP_LOGGING')) {
$packet_type = '-> ' .
$this->packet_types[$type] .
' (' . round($stop - $start, 4) .
's)';
if (NET_SFTP_LOGGING == self::LOG_REALTIME) {
switch (PHP_SAPI) {
case 'cli':
$start = $stop = "\r\n";
break;
default:
$start = '<pre>';
$stop = '</pre>';
}
echo $start . $this->_format_log(array($data),
array($packet_type)) . $stop;
@flush();
@ob_flush();
} else {
$this->packet_type_log[] = $packet_type;
if (NET_SFTP_LOGGING == self::LOG_COMPLEX) {
$this->packet_log[] = $data;
}
}
}
return $result;
}
/**
* Resets a connection for re-use
*
* @param int $reason
* @access private
*/
function _reset_connection($reason)
{
parent::_reset_connection($reason);
$this->use_request_id = false;
$this->pwd = false;
$this->requestBuffer = array();
}
/**
* Receives SFTP Packets
*
* See '6. General Packet Format' of
draft-ietf-secsh-filexfer-13 for more info.
*
* Incidentally, the number of SSH_MSG_CHANNEL_DATA messages has no
bearing on the number of SFTP packets present.
* There can be one SSH_MSG_CHANNEL_DATA messages containing two SFTP
packets or there can be two SSH_MSG_CHANNEL_DATA
* messages containing one SFTP packet.
*
* @see self::_send_sftp_packet()
* @return string
* @access private
*/
function _get_sftp_packet($request_id = null)
{
$this->channel_close = false;
if (isset($request_id) &&
isset($this->requestBuffer[$request_id])) {
$this->packet_type =
$this->requestBuffer[$request_id]['packet_type'];
$temp =
$this->requestBuffer[$request_id]['packet'];
unset($this->requestBuffer[$request_id]);
return $temp;
}
// in SSH2.php the timeout is cumulative per function call. eg.
exec() will
// timeout after 10s. but for SFTP.php it's cumulative per
packet
$this->curTimeout = $this->timeout;
$start = strtok(microtime(), ' ') + strtok('');
// http://php.net/microtime#61838
// SFTP packet length
while (strlen($this->packet_buffer) < 4) {
$temp = $this->_get_channel_packet(self::CHANNEL, true);
if ($temp === true) {
if ($this->channel_status[self::CHANNEL] ===
NET_SSH2_MSG_CHANNEL_CLOSE) {
$this->channel_close = true;
}
$this->packet_type = false;
$this->packet_buffer = '';
return false;
}
$this->packet_buffer.= $temp;
}
if (strlen($this->packet_buffer) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($this->packet_buffer, 4)));
$tempLength = $length;
$tempLength-= strlen($this->packet_buffer);
// 256 * 1024 is what SFTP_MAX_MSG_LENGTH is set to in
OpenSSH's sftp-common.h
if ($tempLength > 256 * 1024) {
user_error('Invalid SFTP packet size');
return false;
}
// SFTP packet type and data payload
while ($tempLength > 0) {
$temp = $this->_get_channel_packet(self::CHANNEL, true);
if (is_bool($temp)) {
$this->packet_type = false;
$this->packet_buffer = '';
return false;
}
$this->packet_buffer.= $temp;
$tempLength-= strlen($temp);
}
$stop = strtok(microtime(), ' ') + strtok('');
$this->packet_type =
ord($this->_string_shift($this->packet_buffer));
if ($this->use_request_id) {
extract(unpack('Npacket_id',
$this->_string_shift($this->packet_buffer, 4))); // remove the
request id
$length-= 5; // account for the request id and the packet type
} else {
$length-= 1; // account for the packet type
}
$packet = $this->_string_shift($this->packet_buffer,
$length);
if (defined('NET_SFTP_LOGGING')) {
$packet_type = '<- ' .
$this->packet_types[$this->packet_type] .
' (' . round($stop - $start, 4) .
's)';
if (NET_SFTP_LOGGING == self::LOG_REALTIME) {
switch (PHP_SAPI) {
case 'cli':
$start = $stop = "\r\n";
break;
default:
$start = '<pre>';
$stop = '</pre>';
}
echo $start . $this->_format_log(array($packet),
array($packet_type)) . $stop;
@flush();
@ob_flush();
} else {
$this->packet_type_log[] = $packet_type;
if (NET_SFTP_LOGGING == self::LOG_COMPLEX) {
$this->packet_log[] = $packet;
}
}
}
if (isset($request_id) && $this->use_request_id
&& $packet_id != $request_id) {
$this->requestBuffer[$packet_id] = array(
'packet_type' => $this->packet_type,
'packet' => $packet
);
return $this->_get_sftp_packet($request_id);
}
return $packet;
}
/**
* Returns a log of the packets that have been sent and received.
*
* Returns a string if NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX, an
array if NET_SFTP_LOGGING == NET_SFTP_LOG_SIMPLE and false if
!defined('NET_SFTP_LOGGING')
*
* @access public
* @return string or Array
*/
function getSFTPLog()
{
if (!defined('NET_SFTP_LOGGING')) {
return false;
}
switch (NET_SFTP_LOGGING) {
case self::LOG_COMPLEX:
return $this->_format_log($this->packet_log,
$this->packet_type_log);
break;
//case self::LOG_SIMPLE:
default:
return $this->packet_type_log;
}
}
/**
* Returns all errors
*
* @return array
* @access public
*/
function getSFTPErrors()
{
return $this->sftp_errors;
}
/**
* Returns the last error
*
* @return string
* @access public
*/
function getLastSFTPError()
{
return count($this->sftp_errors) ?
$this->sftp_errors[count($this->sftp_errors) - 1] : '';
}
/**
* Get supported SFTP versions
*
* @return array
* @access public
*/
function getSupportedVersions()
{
$temp = array('version' => $this->version);
if (isset($this->extensions['versions'])) {
$temp['extensions'] =
$this->extensions['versions'];
}
return $temp;
}
/**
* Disconnect
*
* @param int $reason
* @return bool
* @access private
*/
function _disconnect($reason)
{
$this->pwd = false;
parent::_disconnect($reason);
}
/**
* Enable Date Preservation
*
* @access public
*/
function enableDatePreservation()
{
$this->preserveTime = true;
}
/**
* Disable Date Preservation
*
* @access public
*/
function disableDatePreservation()
{
$this->preserveTime = false;
}
}
phpseclib/phpseclib/Net/SSH1.php000064400000146730151161207740012462
0ustar00<?php
/**
* Pure-PHP implementation of SSHv1.
*
* PHP version 5
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $ssh = new \phpseclib\Net\SSH1('www.domain.tld');
* if (!$ssh->login('username', 'password')) {
* exit('Login Failed');
* }
*
* echo $ssh->exec('ls -la');
* ?>
* </code>
*
* Here's another short example:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $ssh = new \phpseclib\Net\SSH1('www.domain.tld');
* if (!$ssh->login('username', 'password')) {
* exit('Login Failed');
* }
*
* echo $ssh->read('username@username:~$');
* $ssh->write("ls -la\n");
* echo $ssh->read('username@username:~$');
* ?>
* </code>
*
* More information on the SSHv1 specification can be found by reading
* {@link http://www.snailbook.com/docs/protocol-1.5.txt protocol-1.5.txt}.
*
* @category Net
* @package SSH1
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Net;
use phpseclib\Crypt\DES;
use phpseclib\Crypt\Random;
use phpseclib\Crypt\TripleDES;
use phpseclib\Math\BigInteger;
/**
* Pure-PHP implementation of SSHv1.
*
* @package SSH1
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class SSH1
{
/**#@+
* Encryption Methods
*
* @see \phpseclib\Net\SSH1::getSupportedCiphers()
* @access public
*/
/**
* No encryption
*
* Not supported.
*/
const CIPHER_NONE = 0;
/**
* IDEA in CFB mode
*
* Not supported.
*/
const CIPHER_IDEA = 1;
/**
* DES in CBC mode
*/
const CIPHER_DES = 2;
/**
* Triple-DES in CBC mode
*
* All implementations are required to support this
*/
const CIPHER_3DES = 3;
/**
* TRI's Simple Stream encryption CBC
*
* Not supported nor is it defined in the official SSH1 specs.
OpenSSH, however, does define it (see cipher.h),
* although it doesn't use it (see cipher.c)
*/
const CIPHER_BROKEN_TSS = 4;
/**
* RC4
*
* Not supported.
*
* @internal According to the SSH1 specs:
*
* "The first 16 bytes of the session key are used as the
key for
* the server to client direction. The remaining 16 bytes are
used
* as the key for the client to server direction. This gives
* independent 128-bit keys for each direction."
*
* This library currently only supports encryption when the same
key is being used for both directions. This is
* because there's only one $crypto object. Two could be
added ($encrypt and $decrypt, perhaps).
*/
const CIPHER_RC4 = 5;
/**
* Blowfish
*
* Not supported nor is it defined in the official SSH1 specs.
OpenSSH, however, defines it (see cipher.h) and
* uses it (see cipher.c)
*/
const CIPHER_BLOWFISH = 6;
/**#@-*/
/**#@+
* Authentication Methods
*
* @see \phpseclib\Net\SSH1::getSupportedAuthentications()
* @access public
*/
/**
* .rhosts or /etc/hosts.equiv
*/
const AUTH_RHOSTS = 1;
/**
* pure RSA authentication
*/
const AUTH_RSA = 2;
/**
* password authentication
*
* This is the only method that is supported by this library.
*/
const AUTH_PASSWORD = 3;
/**
* .rhosts with RSA host authentication
*/
const AUTH_RHOSTS_RSA = 4;
/**#@-*/
/**#@+
* Terminal Modes
*
* @link
http://3sp.com/content/developer/maverick-net/docs/Maverick.SSH.PseudoTerminalModesMembers.html
* @access private
*/
const TTY_OP_END = 0;
/**#@-*/
/**
* The Response Type
*
* @see \phpseclib\Net\SSH1::_get_binary_packet()
* @access private
*/
const RESPONSE_TYPE = 1;
/**
* The Response Data
*
* @see \phpseclib\Net\SSH1::_get_binary_packet()
* @access private
*/
const RESPONSE_DATA = 2;
/**#@+
* Execution Bitmap Masks
*
* @see \phpseclib\Net\SSH1::bitmap
* @access private
*/
const MASK_CONSTRUCTOR = 0x00000001;
const MASK_CONNECTED = 0x00000002;
const MASK_LOGIN = 0x00000004;
const MASK_SHELL = 0x00000008;
/**#@-*/
/**#@+
* @access public
* @see \phpseclib\Net\SSH1::getLog()
*/
/**
* Returns the message numbers
*/
const LOG_SIMPLE = 1;
/**
* Returns the message content
*/
const LOG_COMPLEX = 2;
/**
* Outputs the content real-time
*/
const LOG_REALTIME = 3;
/**
* Dumps the content real-time to a file
*/
const LOG_REALTIME_FILE = 4;
/**#@-*/
/**#@+
* @access public
* @see \phpseclib\Net\SSH1::read()
*/
/**
* Returns when a string matching $expect exactly is found
*/
const READ_SIMPLE = 1;
/**
* Returns when a string matching the regular expression $expect is
found
*/
const READ_REGEX = 2;
/**#@-*/
/**
* The SSH identifier
*
* @var string
* @access private
*/
var $identifier = 'SSH-1.5-phpseclib';
/**
* The Socket Object
*
* @var object
* @access private
*/
var $fsock;
/**
* The cryptography object
*
* @var object
* @access private
*/
var $crypto = false;
/**
* Execution Bitmap
*
* The bits that are set represent functions that have been called
already. This is used to determine
* if a requisite function has been successfully executed. If not, an
error should be thrown.
*
* @var int
* @access private
*/
var $bitmap = 0;
/**
* The Server Key Public Exponent
*
* Logged for debug purposes
*
* @see self::getServerKeyPublicExponent()
* @var string
* @access private
*/
var $server_key_public_exponent;
/**
* The Server Key Public Modulus
*
* Logged for debug purposes
*
* @see self::getServerKeyPublicModulus()
* @var string
* @access private
*/
var $server_key_public_modulus;
/**
* The Host Key Public Exponent
*
* Logged for debug purposes
*
* @see self::getHostKeyPublicExponent()
* @var string
* @access private
*/
var $host_key_public_exponent;
/**
* The Host Key Public Modulus
*
* Logged for debug purposes
*
* @see self::getHostKeyPublicModulus()
* @var string
* @access private
*/
var $host_key_public_modulus;
/**
* Supported Ciphers
*
* Logged for debug purposes
*
* @see self::getSupportedCiphers()
* @var array
* @access private
*/
var $supported_ciphers = array(
self::CIPHER_NONE => 'No encryption',
self::CIPHER_IDEA => 'IDEA in CFB mode',
self::CIPHER_DES => 'DES in CBC mode',
self::CIPHER_3DES => 'Triple-DES in CBC mode',
self::CIPHER_BROKEN_TSS => 'TRI\'s Simple Stream
encryption CBC',
self::CIPHER_RC4 => 'RC4',
self::CIPHER_BLOWFISH => 'Blowfish'
);
/**
* Supported Authentications
*
* Logged for debug purposes
*
* @see self::getSupportedAuthentications()
* @var array
* @access private
*/
var $supported_authentications = array(
self::AUTH_RHOSTS => '.rhosts or
/etc/hosts.equiv',
self::AUTH_RSA => 'pure RSA authentication',
self::AUTH_PASSWORD => 'password authentication',
self::AUTH_RHOSTS_RSA => '.rhosts with RSA host
authentication'
);
/**
* Server Identification
*
* @see self::getServerIdentification()
* @var string
* @access private
*/
var $server_identification = '';
/**
* Protocol Flags
*
* @see self::__construct()
* @var array
* @access private
*/
var $protocol_flags = array();
/**
* Protocol Flag Log
*
* @see self::getLog()
* @var array
* @access private
*/
var $protocol_flag_log = array();
/**
* Message Log
*
* @see self::getLog()
* @var array
* @access private
*/
var $message_log = array();
/**
* Real-time log file pointer
*
* @see self::_append_log()
* @var resource
* @access private
*/
var $realtime_log_file;
/**
* Real-time log file size
*
* @see self::_append_log()
* @var int
* @access private
*/
var $realtime_log_size;
/**
* Real-time log file wrap boolean
*
* @see self::_append_log()
* @var bool
* @access private
*/
var $realtime_log_wrap;
/**
* Interactive Buffer
*
* @see self::read()
* @var array
* @access private
*/
var $interactiveBuffer = '';
/**
* Timeout
*
* @see self::setTimeout()
* @access private
*/
var $timeout;
/**
* Current Timeout
*
* @see self::_get_channel_packet()
* @access private
*/
var $curTimeout;
/**
* Log Boundary
*
* @see self::_format_log()
* @access private
*/
var $log_boundary = ':';
/**
* Log Long Width
*
* @see self::_format_log()
* @access private
*/
var $log_long_width = 65;
/**
* Log Short Width
*
* @see self::_format_log()
* @access private
*/
var $log_short_width = 16;
/**
* Hostname
*
* @see self::__construct()
* @see self::_connect()
* @var string
* @access private
*/
var $host;
/**
* Port Number
*
* @see self::__construct()
* @see self::_connect()
* @var int
* @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 self::__construct()
* @see self::_connect()
* @var int
* @access private
*/
var $connectionTimeout;
/**
* Default cipher
*
* @see self::__construct()
* @see self::_connect()
* @var int
* @access private
*/
var $cipher;
/**
* Default Constructor.
*
* Connects to an SSHv1 server
*
* @param string $host
* @param int $port
* @param int $timeout
* @param int $cipher
* @return \phpseclib\Net\SSH1
* @access public
*/
function __construct($host, $port = 22, $timeout = 10, $cipher =
self::CIPHER_3DES)
{
$this->protocol_flags = array(
1 => 'NET_SSH1_MSG_DISCONNECT',
2 => 'NET_SSH1_SMSG_PUBLIC_KEY',
3 => 'NET_SSH1_CMSG_SESSION_KEY',
4 => 'NET_SSH1_CMSG_USER',
9 => 'NET_SSH1_CMSG_AUTH_PASSWORD',
10 => 'NET_SSH1_CMSG_REQUEST_PTY',
12 => 'NET_SSH1_CMSG_EXEC_SHELL',
13 => 'NET_SSH1_CMSG_EXEC_CMD',
14 => 'NET_SSH1_SMSG_SUCCESS',
15 => 'NET_SSH1_SMSG_FAILURE',
16 => 'NET_SSH1_CMSG_STDIN_DATA',
17 => 'NET_SSH1_SMSG_STDOUT_DATA',
18 => 'NET_SSH1_SMSG_STDERR_DATA',
19 => 'NET_SSH1_CMSG_EOF',
20 => 'NET_SSH1_SMSG_EXITSTATUS',
33 => 'NET_SSH1_CMSG_EXIT_CONFIRMATION'
);
$this->_define_array($this->protocol_flags);
$this->host = $host;
$this->port = $port;
$this->connectionTimeout = $timeout;
$this->cipher = $cipher;
}
/**
* Connect to an SSHv1 server
*
* @return bool
* @access private
*/
function _connect()
{
$this->fsock = @fsockopen($this->host, $this->port,
$errno, $errstr, $this->connectionTimeout);
if (!$this->fsock) {
user_error(rtrim("Cannot connect to
{$this->host}:{$this->port}. Error $errno. $errstr"));
return false;
}
$this->server_identification = $init_line =
fgets($this->fsock, 255);
if (defined('NET_SSH1_LOGGING')) {
$this->_append_log('<-',
$this->server_identification);
$this->_append_log('->', $this->identifier .
"\r\n");
}
if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line,
$parts)) {
user_error('Can only connect to SSH servers');
return false;
}
if ($parts[1][0] != 1) {
user_error("Cannot connect to SSH $parts[1]
servers");
return false;
}
fputs($this->fsock, $this->identifier."\r\n");
$response = $this->_get_binary_packet();
if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) {
user_error('Expected SSH_SMSG_PUBLIC_KEY');
return false;
}
$anti_spoofing_cookie =
$this->_string_shift($response[self::RESPONSE_DATA], 8);
$this->_string_shift($response[self::RESPONSE_DATA], 4);
if (strlen($response[self::RESPONSE_DATA]) < 2) {
return false;
}
$temp = unpack('nlen',
$this->_string_shift($response[self::RESPONSE_DATA], 2));
$server_key_public_exponent = new
BigInteger($this->_string_shift($response[self::RESPONSE_DATA],
ceil($temp['len'] / 8)), 256);
$this->server_key_public_exponent = $server_key_public_exponent;
if (strlen($response[self::RESPONSE_DATA]) < 2) {
return false;
}
$temp = unpack('nlen',
$this->_string_shift($response[self::RESPONSE_DATA], 2));
$server_key_public_modulus = new
BigInteger($this->_string_shift($response[self::RESPONSE_DATA],
ceil($temp['len'] / 8)), 256);
$this->server_key_public_modulus = $server_key_public_modulus;
$this->_string_shift($response[self::RESPONSE_DATA], 4);
if (strlen($response[self::RESPONSE_DATA]) < 2) {
return false;
}
$temp = unpack('nlen',
$this->_string_shift($response[self::RESPONSE_DATA], 2));
$host_key_public_exponent = new
BigInteger($this->_string_shift($response[self::RESPONSE_DATA],
ceil($temp['len'] / 8)), 256);
$this->host_key_public_exponent = $host_key_public_exponent;
if (strlen($response[self::RESPONSE_DATA]) < 2) {
return false;
}
$temp = unpack('nlen',
$this->_string_shift($response[self::RESPONSE_DATA], 2));
$host_key_public_modulus = new
BigInteger($this->_string_shift($response[self::RESPONSE_DATA],
ceil($temp['len'] / 8)), 256);
$this->host_key_public_modulus = $host_key_public_modulus;
$this->_string_shift($response[self::RESPONSE_DATA], 4);
// get a list of the supported ciphers
if (strlen($response[self::RESPONSE_DATA]) < 4) {
return false;
}
extract(unpack('Nsupported_ciphers_mask',
$this->_string_shift($response[self::RESPONSE_DATA], 4)));
foreach ($this->supported_ciphers as $mask => $name) {
if (($supported_ciphers_mask & (1 << $mask)) == 0) {
unset($this->supported_ciphers[$mask]);
}
}
// get a list of the supported authentications
if (strlen($response[self::RESPONSE_DATA]) < 4) {
return false;
}
extract(unpack('Nsupported_authentications_mask',
$this->_string_shift($response[self::RESPONSE_DATA], 4)));
foreach ($this->supported_authentications as $mask => $name)
{
if (($supported_authentications_mask & (1 << $mask))
== 0) {
unset($this->supported_authentications[$mask]);
}
}
$session_id = pack('H*',
md5($host_key_public_modulus->toBytes() .
$server_key_public_modulus->toBytes() . $anti_spoofing_cookie));
$session_key = Random::string(32);
$double_encrypted_session_key = $session_key ^ str_pad($session_id,
32, chr(0));
if
($server_key_public_modulus->compare($host_key_public_modulus) < 0) {
$double_encrypted_session_key = $this->_rsa_crypt(
$double_encrypted_session_key,
array(
$server_key_public_exponent,
$server_key_public_modulus
)
);
$double_encrypted_session_key = $this->_rsa_crypt(
$double_encrypted_session_key,
array(
$host_key_public_exponent,
$host_key_public_modulus
)
);
} else {
$double_encrypted_session_key = $this->_rsa_crypt(
$double_encrypted_session_key,
array(
$host_key_public_exponent,
$host_key_public_modulus
)
);
$double_encrypted_session_key = $this->_rsa_crypt(
$double_encrypted_session_key,
array(
$server_key_public_exponent,
$server_key_public_modulus
)
);
}
$cipher = isset($this->supported_ciphers[$this->cipher]) ?
$this->cipher : self::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);
if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_SESSION_KEY');
return false;
}
switch ($cipher) {
//case self::CIPHER_NONE:
// $this->crypto = new \phpseclib\Crypt\Null();
// break;
case self::CIPHER_DES:
$this->crypto = new DES();
$this->crypto->disablePadding();
$this->crypto->enableContinuousBuffer();
$this->crypto->setKey(substr($session_key, 0, 8));
break;
case self::CIPHER_3DES:
$this->crypto = new TripleDES(TripleDES::MODE_3CBC);
$this->crypto->disablePadding();
$this->crypto->enableContinuousBuffer();
$this->crypto->setKey(substr($session_key, 0, 24));
break;
//case self::CIPHER_RC4:
// $this->crypto = new RC4();
// $this->crypto->enableContinuousBuffer();
// $this->crypto->setKey(substr($session_key, 0,
16));
// break;
}
$response = $this->_get_binary_packet();
if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
user_error('Expected SSH_SMSG_SUCCESS');
return false;
}
$this->bitmap = self::MASK_CONNECTED;
return true;
}
/**
* Login
*
* @param string $username
* @param string $password
* @return bool
* @access public
*/
function login($username, $password = '')
{
if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
$this->bitmap |= self::MASK_CONSTRUCTOR;
if (!$this->_connect()) {
return false;
}
}
if (!($this->bitmap & self::MASK_CONNECTED)) {
return false;
}
$data = pack('CNa*', NET_SSH1_CMSG_USER,
strlen($username), $username);
if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_USER');
return false;
}
$response = $this->_get_binary_packet();
if ($response === true) {
return false;
}
if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
$this->bitmap |= self::MASK_LOGIN;
return true;
} elseif ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE)
{
user_error('Expected SSH_SMSG_SUCCESS or
SSH_SMSG_FAILURE');
return false;
}
$data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD,
strlen($password), $password);
if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_AUTH_PASSWORD');
return false;
}
// remove the username and password from the last logged packet
if (defined('NET_SSH1_LOGGING') &&
NET_SSH1_LOGGING == self::LOG_COMPLEX) {
$data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD,
strlen('password'), 'password');
$this->message_log[count($this->message_log) - 1] =
$data;
}
$response = $this->_get_binary_packet();
if ($response === true) {
return false;
}
if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
$this->bitmap |= self::MASK_LOGIN;
return true;
} elseif ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_FAILURE)
{
return false;
} else {
user_error('Expected SSH_SMSG_SUCCESS or
SSH_SMSG_FAILURE');
return false;
}
}
/**
* Set Timeout
*
* $ssh->exec('ping 127.0.0.1'); on a Linux host will
never return and will run indefinitely. setTimeout() makes it so
it'll timeout.
* Setting $timeout to false or 0 will mean there is no timeout.
*
* @param mixed $timeout
*/
function setTimeout($timeout)
{
$this->timeout = $this->curTimeout = $timeout;
}
/**
* Executes a command on a non-interactive shell, returns the output,
and quits.
*
* An SSH1 server will close the connection after a command has been
executed on a non-interactive shell. SSH2
* servers don't, however, this isn't an SSH2 client. The
way this works, on the server, is by initiating a
* shell with the -s option, as discussed in the following links:
*
* {@link http://www.faqs.org/docs/bashman/bashref_65.html
http://www.faqs.org/docs/bashman/bashref_65.html}
* {@link http://www.faqs.org/docs/bashman/bashref_62.html
http://www.faqs.org/docs/bashman/bashref_62.html}
*
* To execute further commands, a new \phpseclib\Net\SSH1 object will
need to be created.
*
* Returns false on failure and the output, otherwise.
*
* @see self::interactiveRead()
* @see self::interactiveWrite()
* @param string $cmd
* @param bool $block
* @return mixed
* @access public
*/
function exec($cmd, $block = true)
{
if (!($this->bitmap & self::MASK_LOGIN)) {
user_error('Operation disallowed prior to login()');
return false;
}
$data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD,
strlen($cmd), $cmd);
if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_EXEC_CMD');
return false;
}
if (!$block) {
return true;
}
$output = '';
$response = $this->_get_binary_packet();
if ($response !== false) {
do {
$output.= substr($response[self::RESPONSE_DATA], 4);
$response = $this->_get_binary_packet();
} while (is_array($response) &&
$response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_EXITSTATUS);
}
$data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION);
// i don't think it's really all that important if this
packet gets sent or not.
$this->_send_binary_packet($data);
fclose($this->fsock);
// reset the execution bitmap - a new \phpseclib\Net\SSH1 object
needs to be created.
$this->bitmap = 0;
return $output;
}
/**
* Creates an interactive shell
*
* @see self::interactiveRead()
* @see self::interactiveWrite()
* @return bool
* @access private
*/
function _initShell()
{
// connect using the sample parameters in protocol-1.5.txt.
// according to wikipedia.org's entry on text terminals,
"the fundamental type of application running on a text
// terminal is a command line interpreter or shell". thus,
opening a terminal session to run the shell.
$data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY,
strlen('vt100'), 'vt100', 24, 80, 0, 0,
self::TTY_OP_END);
if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_REQUEST_PTY');
return false;
}
$response = $this->_get_binary_packet();
if ($response === true) {
return false;
}
if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
user_error('Expected SSH_SMSG_SUCCESS');
return false;
}
$data = pack('C', NET_SSH1_CMSG_EXEC_SHELL);
if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_EXEC_SHELL');
return false;
}
$this->bitmap |= self::MASK_SHELL;
//stream_set_blocking($this->fsock, 0);
return true;
}
/**
* Inputs a command into an interactive shell.
*
* @see self::interactiveWrite()
* @param string $cmd
* @return bool
* @access public
*/
function write($cmd)
{
return $this->interactiveWrite($cmd);
}
/**
* Returns the output of an interactive shell when there's a match
for $expect
*
* $expect can take the form of a string literal or, if $mode ==
self::READ_REGEX,
* a regular expression.
*
* @see self::write()
* @param string $expect
* @param int $mode
* @return bool
* @access public
*/
function read($expect, $mode = self::READ_SIMPLE)
{
if (!($this->bitmap & self::MASK_LOGIN)) {
user_error('Operation disallowed prior to login()');
return false;
}
if (!($this->bitmap & self::MASK_SHELL) &&
!$this->_initShell()) {
user_error('Unable to initiate an interactive shell
session');
return false;
}
$match = $expect;
while (true) {
if ($mode == self::READ_REGEX) {
preg_match($expect, $this->interactiveBuffer, $matches);
$match = isset($matches[0]) ? $matches[0] : '';
}
$pos = strlen($match) ? strpos($this->interactiveBuffer,
$match) : false;
if ($pos !== false) {
return $this->_string_shift($this->interactiveBuffer,
$pos + strlen($match));
}
$response = $this->_get_binary_packet();
if ($response === true) {
return $this->_string_shift($this->interactiveBuffer,
strlen($this->interactiveBuffer));
}
$this->interactiveBuffer.=
substr($response[self::RESPONSE_DATA], 4);
}
}
/**
* Inputs a command into an interactive shell.
*
* @see self::interactiveRead()
* @param string $cmd
* @return bool
* @access public
*/
function interactiveWrite($cmd)
{
if (!($this->bitmap & self::MASK_LOGIN)) {
user_error('Operation disallowed prior to login()');
return false;
}
if (!($this->bitmap & self::MASK_SHELL) &&
!$this->_initShell()) {
user_error('Unable to initiate an interactive shell
session');
return false;
}
$data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA,
strlen($cmd), $cmd);
if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_STDIN');
return false;
}
return true;
}
/**
* Returns the output of an interactive shell when no more output is
available.
*
* Requires PHP 4.3.0 or later due to the use of the stream_select()
function. If you see stuff like
* "^[[00m", you're seeing ANSI escape codes. According
to
* {@link http://support.microsoft.com/kb/101875 How to Enable ANSI.SYS
in a Command Window}, "Windows NT
* does not support ANSI escape sequences in Win32 Console
applications", so if you're a Windows user,
* there's not going to be much recourse.
*
* @see self::interactiveRead()
* @return string
* @access public
*/
function interactiveRead()
{
if (!($this->bitmap & self::MASK_LOGIN)) {
user_error('Operation disallowed prior to login()');
return false;
}
if (!($this->bitmap & self::MASK_SHELL) &&
!$this->_initShell()) {
user_error('Unable to initiate an interactive shell
session');
return false;
}
$read = array($this->fsock);
$write = $except = null;
if (stream_select($read, $write, $except, 0)) {
$response = $this->_get_binary_packet();
return substr($response[self::RESPONSE_DATA], 4);
} else {
return '';
}
}
/**
* Disconnect
*
* @access public
*/
function disconnect()
{
$this->_disconnect();
}
/**
* Destructor.
*
* Will be called, automatically, if you're supporting just PHP5.
If you're supporting PHP4, you'll need to call
* disconnect().
*
* @access public
*/
function __destruct()
{
$this->_disconnect();
}
/**
* Disconnect
*
* @param string $msg
* @access private
*/
function _disconnect($msg = 'Client Quit')
{
if ($this->bitmap) {
$data = pack('C', NET_SSH1_CMSG_EOF);
$this->_send_binary_packet($data);
/*
$response = $this->_get_binary_packet();
if ($response === true) {
$response = array(self::RESPONSE_TYPE => -1);
}
switch ($response[self::RESPONSE_TYPE]) {
case NET_SSH1_SMSG_EXITSTATUS:
$data = pack('C',
NET_SSH1_CMSG_EXIT_CONFIRMATION);
break;
default:
$data = pack('CNa*', NET_SSH1_MSG_DISCONNECT,
strlen($msg), $msg);
}
*/
$data = pack('CNa*', NET_SSH1_MSG_DISCONNECT,
strlen($msg), $msg);
$this->_send_binary_packet($data);
fclose($this->fsock);
$this->bitmap = 0;
}
}
/**
* Gets Binary Packets
*
* See 'The Binary Packet Protocol' of protocol-1.5.txt for
more info.
*
* Also, this function could be improved upon by adding detection for
the following exploit:
* http://www.securiteam.com/securitynews/5LP042K3FY.html
*
* @see self::_send_binary_packet()
* @return array
* @access private
*/
function _get_binary_packet()
{
if (feof($this->fsock)) {
//user_error('connection closed prematurely');
return false;
}
if ($this->curTimeout) {
$read = array($this->fsock);
$write = $except = null;
$start = strtok(microtime(), ' ') +
strtok(''); // http://php.net/microtime#61838
$sec = floor($this->curTimeout);
$usec = 1000000 * ($this->curTimeout - $sec);
// on windows this returns a "Warning: Invalid CRT
parameters detected" error
if (!@stream_select($read, $write, $except, $sec, $usec)
&& !count($read)) {
//$this->_disconnect('Timeout');
return true;
}
$elapsed = strtok(microtime(), ' ') +
strtok('') - $start;
$this->curTimeout-= $elapsed;
}
$start = strtok(microtime(), ' ') + strtok('');
// http://php.net/microtime#61838
$data = fread($this->fsock, 4);
if (strlen($data) < 4) {
return false;
}
$temp = unpack('Nlength', $data);
$padding_length = 8 - ($temp['length'] & 7);
$length = $temp['length'] + $padding_length;
$raw = '';
while ($length > 0) {
$temp = fread($this->fsock, $length);
if (strlen($temp) != $length) {
return false;
}
$raw.= $temp;
$length-= strlen($temp);
}
$stop = strtok(microtime(), ' ') + strtok('');
if (strlen($raw) && $this->crypto !== false) {
$raw = $this->crypto->decrypt($raw);
}
$padding = substr($raw, 0, $padding_length);
$type = $raw[$padding_length];
$data = substr($raw, $padding_length + 1, -4);
if (strlen($raw) < 4) {
return false;
}
$temp = unpack('Ncrc', substr($raw, -4));
//if ( $temp['crc'] != $this->_crc($padding . $type .
$data) ) {
// user_error('Bad CRC in packet from server');
// return false;
//}
$type = ord($type);
if (defined('NET_SSH1_LOGGING')) {
$temp = isset($this->protocol_flags[$type]) ?
$this->protocol_flags[$type] : 'UNKNOWN';
$temp = '<- ' . $temp .
' (' . round($stop - $start, 4) .
's)';
$this->_append_log($temp, $data);
}
return array(
self::RESPONSE_TYPE => $type,
self::RESPONSE_DATA => $data
);
}
/**
* Sends Binary Packets
*
* Returns true on success, false on failure.
*
* @see self::_get_binary_packet()
* @param string $data
* @return bool
* @access private
*/
function _send_binary_packet($data)
{
if (feof($this->fsock)) {
//user_error('connection closed prematurely');
return false;
}
$length = strlen($data) + 4;
$padding = Random::string(8 - ($length & 7));
$orig = $data;
$data = $padding . $data;
$data.= pack('N', $this->_crc($data));
if ($this->crypto !== false) {
$data = $this->crypto->encrypt($data);
}
$packet = pack('Na*', $length, $data);
$start = strtok(microtime(), ' ') + strtok('');
// http://php.net/microtime#61838
$result = strlen($packet) == fputs($this->fsock, $packet);
$stop = strtok(microtime(), ' ') + strtok('');
if (defined('NET_SSH1_LOGGING')) {
$temp = isset($this->protocol_flags[ord($orig[0])]) ?
$this->protocol_flags[ord($orig[0])] : 'UNKNOWN';
$temp = '-> ' . $temp .
' (' . round($stop - $start, 4) .
's)';
$this->_append_log($temp, $orig);
}
return $result;
}
/**
* Cyclic Redundancy Check (CRC)
*
* PHP's crc32 function is implemented slightly differently than
the one that SSH v1 uses, so
* we've reimplemented it. A more detailed discussion of the
differences can be found after
* $crc_lookup_table's initialization.
*
* @see self::_get_binary_packet()
* @see self::_send_binary_packet()
* @param string $data
* @return int
* @access private
*/
function _crc($data)
{
static $crc_lookup_table = array(
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
);
// For this function to yield the same output as PHP's crc32
function, $crc would have to be
// set to 0xFFFFFFFF, initially - not 0x00000000 as it currently
is.
$crc = 0x00000000;
$length = strlen($data);
for ($i=0; $i<$length; $i++) {
// We AND $crc >> 8 with 0x00FFFFFF because we want the
eight newly added bits to all
// be zero. PHP, unfortunately, doesn't always do this.
0x80000000 >> 8, as an example,
// yields 0xFF800000 - not 0x00800000. The following link
elaborates:
//
http://www.php.net/manual/en/language.operators.bitwise.php#57281
$crc = (($crc >> 8) & 0x00FFFFFF) ^
$crc_lookup_table[($crc & 0xFF) ^ ord($data[$i])];
}
// In addition to having to set $crc to 0xFFFFFFFF, initially, the
return value must be XOR'd with
// 0xFFFFFFFF for this function to return the same thing that
PHP's crc32 function would.
return $crc;
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @return string
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
/**
* RSA Encrypt
*
* Returns mod(pow($m, $e), $n), where $n should be the product of two
(large) primes $p and $q and where $e
* should be a number with the property that gcd($e, ($p - 1) * ($q -
1)) == 1. Could just make anything that
* calls this call modexp, instead, but I think this makes things
clearer, maybe...
*
* @see self::__construct()
* @param BigInteger $m
* @param array $key
* @return BigInteger
* @access private
*/
function _rsa_crypt($m, $key)
{
/*
$rsa = new RSA();
$rsa->loadKey($key, RSA::PUBLIC_FORMAT_RAW);
$rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1);
return $rsa->encrypt($m);
*/
// To quote from protocol-1.5.txt:
// The most significant byte (which is only partial as the value
must be
// less than the public modulus, which is never a power of two) is
zero.
//
// The next byte contains the value 2 (which stands for public-key
// encrypted data in the PKCS standard [PKCS#1]). Then, there are
non-
// zero random bytes to fill any unused space, a zero byte, and the
data
// to be encrypted in the least significant bytes, the last byte of
the
// data in the least significant byte.
// Presumably the part of PKCS#1 they're refering to is
"Section 7.2.1 Encryption Operation",
// under "7.2 RSAES-PKCS1-v1.5" and "7 Encryption
schemes" of the following URL:
// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
$modulus = $key[1]->toBytes();
$length = strlen($modulus) - strlen($m) - 3;
$random = '';
while (strlen($random) != $length) {
$block = Random::string($length - strlen($random));
$block = str_replace("\x00", '', $block);
$random.= $block;
}
$temp = chr(0) . chr(2) . $random . chr(0) . $m;
$m = new BigInteger($temp, 256);
$m = $m->modPow($key[0], $key[1]);
return $m->toBytes();
}
/**
* Define Array
*
* Takes any number of arrays whose indices are integers and whose
values are strings and defines a bunch of
* named constants from it, using the value as the name of the constant
and the index as the value of the constant.
* If any of the constants that would be defined already exists, none
of the constants will be defined.
*
* @access private
*/
function _define_array()
{
$args = func_get_args();
foreach ($args as $arg) {
foreach ($arg as $key => $value) {
if (!defined($value)) {
define($value, $key);
} else {
break 2;
}
}
}
}
/**
* Returns a log of the packets that have been sent and received.
*
* Returns a string if NET_SSH1_LOGGING == self::LOG_COMPLEX, an array
if NET_SSH1_LOGGING == self::LOG_SIMPLE and false if
!defined('NET_SSH1_LOGGING')
*
* @access public
* @return array|false|string
*/
function getLog()
{
if (!defined('NET_SSH1_LOGGING')) {
return false;
}
switch (NET_SSH1_LOGGING) {
case self::LOG_SIMPLE:
return $this->message_number_log;
break;
case self::LOG_COMPLEX:
return $this->_format_log($this->message_log,
$this->protocol_flags_log);
break;
default:
return false;
}
}
/**
* Formats a log for printing
*
* @param array $message_log
* @param array $message_number_log
* @access private
* @return string
*/
function _format_log($message_log, $message_number_log)
{
$output = '';
for ($i = 0; $i < count($message_log); $i++) {
$output.= $message_number_log[$i] . "\r\n";
$current_log = $message_log[$i];
$j = 0;
do {
if (strlen($current_log)) {
$output.= str_pad(dechex($j), 7, '0',
STR_PAD_LEFT) . '0 ';
}
$fragment = $this->_string_shift($current_log,
$this->log_short_width);
$hex = substr(preg_replace_callback('#.#s',
array($this, '_format_log_helper'), $fragment),
strlen($this->log_boundary));
// replace non ASCII printable characters with dots
//
http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
// also replace < with a . since < messes up the
output on web browsers
$raw = preg_replace('#[^\x20-\x7E]|<#',
'.', $fragment);
$output.= str_pad($hex, $this->log_long_width -
$this->log_short_width, ' ') . $raw . "\r\n";
$j++;
} while (strlen($current_log));
$output.= "\r\n";
}
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
*
* Returns, by default, the base-10 representation. If $raw_output is
set to true, returns, instead,
* the raw bytes. This behavior is similar to PHP's md5()
function.
*
* @param bool $raw_output
* @return string
* @access public
*/
function getServerKeyPublicExponent($raw_output = false)
{
return $raw_output ?
$this->server_key_public_exponent->toBytes() :
$this->server_key_public_exponent->toString();
}
/**
* Return the server key public modulus
*
* Returns, by default, the base-10 representation. If $raw_output is
set to true, returns, instead,
* the raw bytes. This behavior is similar to PHP's md5()
function.
*
* @param bool $raw_output
* @return string
* @access public
*/
function getServerKeyPublicModulus($raw_output = false)
{
return $raw_output ?
$this->server_key_public_modulus->toBytes() :
$this->server_key_public_modulus->toString();
}
/**
* Return the host key public exponent
*
* Returns, by default, the base-10 representation. If $raw_output is
set to true, returns, instead,
* the raw bytes. This behavior is similar to PHP's md5()
function.
*
* @param bool $raw_output
* @return string
* @access public
*/
function getHostKeyPublicExponent($raw_output = false)
{
return $raw_output ?
$this->host_key_public_exponent->toBytes() :
$this->host_key_public_exponent->toString();
}
/**
* Return the host key public modulus
*
* Returns, by default, the base-10 representation. If $raw_output is
set to true, returns, instead,
* the raw bytes. This behavior is similar to PHP's md5()
function.
*
* @param bool $raw_output
* @return string
* @access public
*/
function getHostKeyPublicModulus($raw_output = false)
{
return $raw_output ?
$this->host_key_public_modulus->toBytes() :
$this->host_key_public_modulus->toString();
}
/**
* Return a list of ciphers supported by SSH1 server.
*
* Just because a cipher is supported by an SSH1 server doesn't
mean it's supported by this library. If $raw_output
* is set to true, returns, instead, an array of constants. ie.
instead of array('Triple-DES in CBC mode'), you'll
* get array(self::CIPHER_3DES).
*
* @param bool $raw_output
* @return array
* @access public
*/
function getSupportedCiphers($raw_output = false)
{
return $raw_output ? array_keys($this->supported_ciphers) :
array_values($this->supported_ciphers);
}
/**
* Return a list of authentications supported by SSH1 server.
*
* Just because a cipher is supported by an SSH1 server doesn't
mean it's supported by this library. If $raw_output
* is set to true, returns, instead, an array of constants. ie.
instead of array('password authentication'), you'll
* get array(self::AUTH_PASSWORD).
*
* @param bool $raw_output
* @return array
* @access public
*/
function getSupportedAuthentications($raw_output = false)
{
return $raw_output ?
array_keys($this->supported_authentications) :
array_values($this->supported_authentications);
}
/**
* Return the server identification.
*
* @return string
* @access public
*/
function getServerIdentification()
{
return rtrim($this->server_identification);
}
/**
* Logs data packets
*
* Makes sure that only the last 1MB worth of packets will be logged
*
* @param int $protocol_flags
* @param string $message
* @access private
*/
function _append_log($protocol_flags, $message)
{
switch (NET_SSH1_LOGGING) {
// useful for benchmarks
case self::LOG_SIMPLE:
$this->protocol_flags_log[] = $protocol_flags;
break;
// the most useful log for SSH1
case self::LOG_COMPLEX:
$this->protocol_flags_log[] = $protocol_flags;
$this->_string_shift($message);
$this->log_size+= strlen($message);
$this->message_log[] = $message;
while ($this->log_size > self::LOG_MAX_SIZE) {
$this->log_size-=
strlen(array_shift($this->message_log));
array_shift($this->protocol_flags_log);
}
break;
// dump the output out realtime; packets may be interspersed
with non packets,
// passwords won't be filtered out and select other
packets may not be correctly
// identified
case self::LOG_REALTIME:
echo "<pre>\r\n" .
$this->_format_log(array($message), array($protocol_flags)) .
"\r\n</pre>\r\n";
@flush();
@ob_flush();
break;
// basically the same thing as self::LOG_REALTIME with the
caveat that self::LOG_REALTIME_FILE
// needs to be defined and that the resultant log file will be
capped out at self::LOG_MAX_SIZE.
// the earliest part of the log file is denoted by the first
<<< START >>> and is not going to necessarily
// at the beginning of the file
case self::LOG_REALTIME_FILE:
if (!isset($this->realtime_log_file)) {
// PHP doesn't seem to like using constants in
fopen()
$filename = self::LOG_REALTIME_FILE;
$fp = fopen($filename, 'w');
$this->realtime_log_file = $fp;
}
if (!is_resource($this->realtime_log_file)) {
break;
}
$entry = $this->_format_log(array($message),
array($protocol_flags));
if ($this->realtime_log_wrap) {
$temp = "<<< START
>>>\r\n";
$entry.= $temp;
fseek($this->realtime_log_file,
ftell($this->realtime_log_file) - strlen($temp));
}
$this->realtime_log_size+= strlen($entry);
if ($this->realtime_log_size > self::LOG_MAX_SIZE) {
fseek($this->realtime_log_file, 0);
$this->realtime_log_size = strlen($entry);
$this->realtime_log_wrap = true;
}
fputs($this->realtime_log_file, $entry);
}
}
}
phpseclib/phpseclib/Net/SSH2.php000064400000527204151161207740012462
0ustar00<?php
/**
* Pure-PHP implementation of SSHv2.
*
* PHP version 5
*
* Here are some examples of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $ssh = new \phpseclib\Net\SSH2('www.domain.tld');
* if (!$ssh->login('username', 'password')) {
* exit('Login Failed');
* }
*
* echo $ssh->exec('pwd');
* echo $ssh->exec('ls -la');
* ?>
* </code>
*
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $key = new \phpseclib\Crypt\RSA();
* //$key->setPassword('whatever');
* $key->loadKey(file_get_contents('privatekey'));
*
* $ssh = new \phpseclib\Net\SSH2('www.domain.tld');
* if (!$ssh->login('username', $key)) {
* exit('Login Failed');
* }
*
* echo $ssh->read('username@username:~$');
* $ssh->write("ls -la\n");
* echo $ssh->read('username@username:~$');
* ?>
* </code>
*
* @category Net
* @package SSH2
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Net;
use phpseclib\Crypt\Base;
use phpseclib\Crypt\Blowfish;
use phpseclib\Crypt\Hash;
use phpseclib\Crypt\Random;
use phpseclib\Crypt\RC4;
use phpseclib\Crypt\Rijndael;
use phpseclib\Crypt\RSA;
use phpseclib\Crypt\TripleDES;
use phpseclib\Crypt\Twofish;
use phpseclib\Math\BigInteger; // Used to do Diffie-Hellman key exchange
and DSA/RSA signature verification.
use phpseclib\System\SSH\Agent;
/**
* Pure-PHP implementation of SSHv2.
*
* @package SSH2
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class SSH2
{
/**#@+
* Execution Bitmap Masks
*
* @see \phpseclib\Net\SSH2::bitmap
* @access private
*/
const MASK_CONSTRUCTOR = 0x00000001;
const MASK_CONNECTED = 0x00000002;
const MASK_LOGIN_REQ = 0x00000004;
const MASK_LOGIN = 0x00000008;
const MASK_SHELL = 0x00000010;
const MASK_WINDOW_ADJUST = 0x00000020;
/**#@-*/
/**#@+
* Channel constants
*
* RFC4254 refers not to client and server channels but rather to
sender and recipient channels. we don't refer
* to them in that way because RFC4254 toggles the meaning. the client
sends a SSH_MSG_CHANNEL_OPEN message with
* a sender channel and the server sends a
SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
* recepient channel. at first glance, you might conclude that
SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
* would be the same thing as SSH_MSG_CHANNEL_OPEN's sender
channel, but it's not, per this snipet:
* The 'recipient channel' is the channel number given in
the original
* open request, and 'sender channel' is the channel
number allocated by
* the other side.
*
* @see \phpseclib\Net\SSH2::_send_channel_packet()
* @see \phpseclib\Net\SSH2::_get_channel_packet()
* @access private
*/
const CHANNEL_EXEC = 1; // PuTTy uses 0x100
const CHANNEL_SHELL = 2;
const CHANNEL_SUBSYSTEM = 3;
const CHANNEL_AGENT_FORWARD = 4;
const CHANNEL_KEEP_ALIVE = 5;
/**#@-*/
/**#@+
* @access public
* @see \phpseclib\Net\SSH2::getLog()
*/
/**
* Returns the message numbers
*/
const LOG_SIMPLE = 1;
/**
* Returns the message content
*/
const LOG_COMPLEX = 2;
/**
* Outputs the content real-time
*/
const LOG_REALTIME = 3;
/**
* Dumps the content real-time to a file
*/
const LOG_REALTIME_FILE = 4;
/**
* Make sure that the log never gets larger than this
*/
const LOG_MAX_SIZE = 1048576; // 1024 * 1024
/**#@-*/
/**#@+
* @access public
* @see \phpseclib\Net\SSH2::read()
*/
/**
* Returns when a string matching $expect exactly is found
*/
const READ_SIMPLE = 1;
/**
* Returns when a string matching the regular expression $expect is
found
*/
const READ_REGEX = 2;
/**
* Returns whenever a data packet is received.
*
* Some data packets may only contain a single character so it may be
necessary
* to call read() multiple times when using this option
*/
const READ_NEXT = 3;
/**#@-*/
/**
* The SSH identifier
*
* @var string
* @access private
*/
var $identifier;
/**
* The Socket Object
*
* @var object
* @access private
*/
var $fsock;
/**
* Execution Bitmap
*
* The bits that are set represent functions that have been called
already. This is used to determine
* if a requisite function has been successfully executed. If not, an
error should be thrown.
*
* @var int
* @access private
*/
var $bitmap = 0;
/**
* Error information
*
* @see self::getErrors()
* @see self::getLastError()
* @var string
* @access private
*/
var $errors = array();
/**
* Server Identifier
*
* @see self::getServerIdentification()
* @var array|false
* @access private
*/
var $server_identifier = false;
/**
* Key Exchange Algorithms
*
* @see self::getKexAlgorithims()
* @var array|false
* @access private
*/
var $kex_algorithms = false;
/**
* Key Exchange Algorithm
*
* @see self::getMethodsNegotiated()
* @var string|false
* @access private
*/
var $kex_algorithm = false;
/**
* Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange
Methods
*
* @see self::_key_exchange()
* @var int
* @access private
*/
var $kex_dh_group_size_min = 1536;
/**
* Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange
Methods
*
* @see self::_key_exchange()
* @var int
* @access private
*/
var $kex_dh_group_size_preferred = 2048;
/**
* Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange
Methods
*
* @see self::_key_exchange()
* @var int
* @access private
*/
var $kex_dh_group_size_max = 4096;
/**
* Server Host Key Algorithms
*
* @see self::getServerHostKeyAlgorithms()
* @var array|false
* @access private
*/
var $server_host_key_algorithms = false;
/**
* Encryption Algorithms: Client to Server
*
* @see self::getEncryptionAlgorithmsClient2Server()
* @var array|false
* @access private
*/
var $encryption_algorithms_client_to_server = false;
/**
* Encryption Algorithms: Server to Client
*
* @see self::getEncryptionAlgorithmsServer2Client()
* @var array|false
* @access private
*/
var $encryption_algorithms_server_to_client = false;
/**
* MAC Algorithms: Client to Server
*
* @see self::getMACAlgorithmsClient2Server()
* @var array|false
* @access private
*/
var $mac_algorithms_client_to_server = false;
/**
* MAC Algorithms: Server to Client
*
* @see self::getMACAlgorithmsServer2Client()
* @var array|false
* @access private
*/
var $mac_algorithms_server_to_client = false;
/**
* Compression Algorithms: Client to Server
*
* @see self::getCompressionAlgorithmsClient2Server()
* @var array|false
* @access private
*/
var $compression_algorithms_client_to_server = false;
/**
* Compression Algorithms: Server to Client
*
* @see self::getCompressionAlgorithmsServer2Client()
* @var array|false
* @access private
*/
var $compression_algorithms_server_to_client = false;
/**
* Languages: Server to Client
*
* @see self::getLanguagesServer2Client()
* @var array|false
* @access private
*/
var $languages_server_to_client = false;
/**
* Languages: Client to Server
*
* @see self::getLanguagesClient2Server()
* @var array|false
* @access private
*/
var $languages_client_to_server = false;
/**
* Preferred Algorithms
*
* @see self::setPreferredAlgorithms()
* @var array
* @access private
*/
var $preferred = array();
/**
* Block Size for Server to Client Encryption
*
* "Note that the length of the concatenation of
'packet_length',
* 'padding_length', 'payload', and 'random
padding' MUST be a multiple
* of the cipher block size or 8, whichever is larger. This
constraint
* MUST be enforced, even when using stream ciphers."
*
* -- http://tools.ietf.org/html/rfc4253#section-6
*
* @see self::__construct()
* @see self::_send_binary_packet()
* @var int
* @access private
*/
var $encrypt_block_size = 8;
/**
* Block Size for Client to Server Encryption
*
* @see self::__construct()
* @see self::_get_binary_packet()
* @var int
* @access private
*/
var $decrypt_block_size = 8;
/**
* Server to Client Encryption Object
*
* @see self::_get_binary_packet()
* @var object
* @access private
*/
var $decrypt = false;
/**
* Client to Server Encryption Object
*
* @see self::_send_binary_packet()
* @var object
* @access private
*/
var $encrypt = false;
/**
* Client to Server HMAC Object
*
* @see self::_send_binary_packet()
* @var object
* @access private
*/
var $hmac_create = false;
/**
* Server to Client HMAC Object
*
* @see self::_get_binary_packet()
* @var object
* @access private
*/
var $hmac_check = false;
/**
* Size of server to client HMAC
*
* We need to know how big the HMAC will be for the server to client
direction so that we know how many bytes to read.
* For the client to server side, the HMAC object will make the HMAC as
long as it needs to be. All we need to do is
* append it.
*
* @see self::_get_binary_packet()
* @var int
* @access private
*/
var $hmac_size = false;
/**
* Server Public Host Key
*
* @see self::getServerPublicHostKey()
* @var string
* @access private
*/
var $server_public_host_key;
/**
* Session identifier
*
* "The exchange hash H from the first key exchange is
additionally
* used as the session identifier, which is a unique identifier for
* this connection."
*
* -- http://tools.ietf.org/html/rfc4253#section-7.2
*
* @see self::_key_exchange()
* @var string
* @access private
*/
var $session_id = false;
/**
* Exchange hash
*
* The current exchange hash
*
* @see self::_key_exchange()
* @var string
* @access private
*/
var $exchange_hash = false;
/**
* Message Numbers
*
* @see self::__construct()
* @var array
* @access private
*/
var $message_numbers = array();
/**
* Disconnection Message 'reason codes' defined in RFC4253
*
* @see self::__construct()
* @var array
* @access private
*/
var $disconnect_reasons = array();
/**
* SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in
RFC4254
*
* @see self::__construct()
* @var array
* @access private
*/
var $channel_open_failure_reasons = array();
/**
* Terminal Modes
*
* @link http://tools.ietf.org/html/rfc4254#section-8
* @see self::__construct()
* @var array
* @access private
*/
var $terminal_modes = array();
/**
* SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
*
* @link http://tools.ietf.org/html/rfc4254#section-5.2
* @see self::__construct()
* @var array
* @access private
*/
var $channel_extended_data_type_codes = array();
/**
* Send Sequence Number
*
* See 'Section 6.4. Data Integrity' of rfc4253 for more
info.
*
* @see self::_send_binary_packet()
* @var int
* @access private
*/
var $send_seq_no = 0;
/**
* Get Sequence Number
*
* See 'Section 6.4. Data Integrity' of rfc4253 for more
info.
*
* @see self::_get_binary_packet()
* @var int
* @access private
*/
var $get_seq_no = 0;
/**
* Server Channels
*
* Maps client channels to server channels
*
* @see self::_get_channel_packet()
* @see self::exec()
* @var array
* @access private
*/
var $server_channels = array();
/**
* Channel Buffers
*
* If a client requests a packet from one channel but receives two
packets from another those packets should
* be placed in a buffer
*
* @see self::_get_channel_packet()
* @see self::exec()
* @var array
* @access private
*/
var $channel_buffers = array();
/**
* Channel Status
*
* Contains the type of the last sent message
*
* @see self::_get_channel_packet()
* @var array
* @access private
*/
var $channel_status = array();
/**
* Packet Size
*
* Maximum packet size indexed by channel
*
* @see self::_send_channel_packet()
* @var array
* @access private
*/
var $packet_size_client_to_server = array();
/**
* Message Number Log
*
* @see self::getLog()
* @var array
* @access private
*/
var $message_number_log = array();
/**
* Message Log
*
* @see self::getLog()
* @var array
* @access private
*/
var $message_log = array();
/**
* The Window Size
*
* Bytes the other party can send before it must wait for the window to
be adjusted (0x7FFFFFFF = 2GB)
*
* @var int
* @see self::_send_channel_packet()
* @see self::exec()
* @access private
*/
var $window_size = 0x7FFFFFFF;
/**
* What we resize the window to
*
* When PuTTY resizes the window it doesn't add an additional
0x7FFFFFFF bytes - it adds 0x40000000 bytes.
* Some SFTP clients (GoAnywhere) don't support adding 0x7FFFFFFF
to the window size after the fact so
* we'll just do what PuTTY does
*
* @var int
* @see self::_send_channel_packet()
* @see self::exec()
* @access private
*/
var $window_resize = 0x40000000;
/**
* Window size, server to client
*
* Window size indexed by channel
*
* @see self::_send_channel_packet()
* @var array
* @access private
*/
var $window_size_server_to_client = array();
/**
* Window size, client to server
*
* Window size indexed by channel
*
* @see self::_get_channel_packet()
* @var array
* @access private
*/
var $window_size_client_to_server = array();
/**
* Server signature
*
* Verified against $this->session_id
*
* @see self::getServerPublicHostKey()
* @var string
* @access private
*/
var $signature = '';
/**
* Server signature format
*
* ssh-rsa or ssh-dss.
*
* @see self::getServerPublicHostKey()
* @var string
* @access private
*/
var $signature_format = '';
/**
* Interactive Buffer
*
* @see self::read()
* @var array
* @access private
*/
var $interactiveBuffer = '';
/**
* Current log size
*
* Should never exceed self::LOG_MAX_SIZE
*
* @see self::_send_binary_packet()
* @see self::_get_binary_packet()
* @var int
* @access private
*/
var $log_size;
/**
* Timeout
*
* @see self::setTimeout()
* @access private
*/
var $timeout;
/**
* Current Timeout
*
* @see self::_get_channel_packet()
* @access private
*/
var $curTimeout;
/**
* Keep Alive Interval
*
* @see self::setKeepAlive()
* @access private
*/
var $keepAlive;
/**
* Real-time log file pointer
*
* @see self::_append_log()
* @var resource
* @access private
*/
var $realtime_log_file;
/**
* Real-time log file size
*
* @see self::_append_log()
* @var int
* @access private
*/
var $realtime_log_size;
/**
* Has the signature been validated?
*
* @see self::getServerPublicHostKey()
* @var bool
* @access private
*/
var $signature_validated = false;
/**
* Real-time log file wrap boolean
*
* @see self::_append_log()
* @access private
*/
var $realtime_log_wrap;
/**
* Flag to suppress stderr from output
*
* @see self::enableQuietMode()
* @access private
*/
var $quiet_mode = false;
/**
* Time of first network activity
*
* @var int
* @access private
*/
var $last_packet;
/**
* Exit status returned from ssh if any
*
* @var int
* @access private
*/
var $exit_status;
/**
* Flag to request a PTY when using exec()
*
* @var bool
* @see self::enablePTY()
* @access private
*/
var $request_pty = false;
/**
* Flag set while exec() is running when using enablePTY()
*
* @var bool
* @access private
*/
var $in_request_pty_exec = false;
/**
* Flag set after startSubsystem() is called
*
* @var bool
* @access private
*/
var $in_subsystem;
/**
* Contents of stdError
*
* @var string
* @access private
*/
var $stdErrorLog;
/**
* The Last Interactive Response
*
* @see self::_keyboard_interactive_process()
* @var string
* @access private
*/
var $last_interactive_response = '';
/**
* Keyboard Interactive Request / Responses
*
* @see self::_keyboard_interactive_process()
* @var array
* @access private
*/
var $keyboard_requests_responses = array();
/**
* Banner Message
*
* Quoting from the RFC, "in some jurisdictions, sending a warning
message before
* authentication may be relevant for getting legal protection."
*
* @see self::_filter()
* @see self::getBannerMessage()
* @var string
* @access private
*/
var $banner_message = '';
/**
* Did read() timeout or return normally?
*
* @see self::isTimeout()
* @var bool
* @access private
*/
var $is_timeout = false;
/**
* Log Boundary
*
* @see self::_format_log()
* @var string
* @access private
*/
var $log_boundary = ':';
/**
* Log Long Width
*
* @see self::_format_log()
* @var int
* @access private
*/
var $log_long_width = 65;
/**
* Log Short Width
*
* @see self::_format_log()
* @var int
* @access private
*/
var $log_short_width = 16;
/**
* Hostname
*
* @see self::__construct()
* @see self::_connect()
* @var string
* @access private
*/
var $host;
/**
* Port Number
*
* @see self::__construct()
* @see self::_connect()
* @var int
* @access private
*/
var $port;
/**
* Number of columns for terminal window size
*
* @see self::getWindowColumns()
* @see self::setWindowColumns()
* @see self::setWindowSize()
* @var int
* @access private
*/
var $windowColumns = 80;
/**
* Number of columns for terminal window size
*
* @see self::getWindowRows()
* @see self::setWindowRows()
* @see self::setWindowSize()
* @var int
* @access private
*/
var $windowRows = 24;
/**
* Crypto Engine
*
* @see self::setCryptoEngine()
* @see self::_key_exchange()
* @var int
* @access private
*/
var $crypto_engine = false;
/**
* A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario
*
* @var System_SSH_Agent
* @access private
*/
var $agent;
/**
* Send the identification string first?
*
* @var bool
* @access private
*/
var $send_id_string_first = true;
/**
* Send the key exchange initiation packet first?
*
* @var bool
* @access private
*/
var $send_kex_first = true;
/**
* Some versions of OpenSSH incorrectly calculate the key size
*
* @var bool
* @access private
*/
var $bad_key_size_fix = false;
/**
* Should we try to re-connect to re-establish keys?
*
* @var bool
* @access private
*/
var $retry_connect = false;
/**
* Binary Packet Buffer
*
* @var string|false
* @access private
*/
var $binary_packet_buffer = false;
/**
* Preferred Signature Format
*
* @var string|false
* @access private
*/
var $preferred_signature_format = false;
/**
* Authentication Credentials
*
* @var array
* @access private
*/
var $auth = array();
/**
* The authentication methods that may productively continue
authentication.
*
* @see https://tools.ietf.org/html/rfc4252#section-5.1
* @var array|null
*/
private $auth_methods_to_continue = null;
/**
* Default Constructor.
*
* $host can either be a string, representing the host, or a stream
resource.
*
* @param mixed $host
* @param int $port
* @param int $timeout
* @see self::login()
* @return \phpseclib\Net\SSH2
* @access public
*/
function __construct($host, $port = 22, $timeout = 10)
{
$this->message_numbers = array(
1 => 'NET_SSH2_MSG_DISCONNECT',
2 => 'NET_SSH2_MSG_IGNORE',
3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
4 => 'NET_SSH2_MSG_DEBUG',
5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
20 => 'NET_SSH2_MSG_KEXINIT',
21 => 'NET_SSH2_MSG_NEWKEYS',
30 => 'NET_SSH2_MSG_KEXDH_INIT',
31 => 'NET_SSH2_MSG_KEXDH_REPLY',
50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
94 => 'NET_SSH2_MSG_CHANNEL_DATA',
95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
96 => 'NET_SSH2_MSG_CHANNEL_EOF',
97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
);
$this->disconnect_reasons = array(
1 =>
'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
4 => 'NET_SSH2_DISCONNECT_RESERVED',
5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
8 =>
'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
9 =>
'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
13 =>
'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
14 =>
'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
);
$this->channel_open_failure_reasons = array(
1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
);
$this->terminal_modes = array(
0 => 'NET_SSH2_TTY_OP_END'
);
$this->channel_extended_data_type_codes = array(
1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
);
$this->_define_array(
$this->message_numbers,
$this->disconnect_reasons,
$this->channel_open_failure_reasons,
$this->terminal_modes,
$this->channel_extended_data_type_codes,
array(60 =>
'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'),
array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
61 =>
'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'),
// RFC 4419 - diffie-hellman-group-exchange-sha{1,256}
array(30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD',
31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP',
32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT',
33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY',
34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'),
// RFC 5656 - Elliptic Curves (for
curve25519-sha256@libssh.org)
array(30 => 'NET_SSH2_MSG_KEX_ECDH_INIT',
31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY')
);
if (is_resource($host)) {
$this->fsock = $host;
return;
}
if (is_string($host)) {
$this->host = $host;
$this->port = $port;
$this->timeout = $timeout;
}
}
/**
* Set Crypto Engine Mode
*
* Possible $engine values:
* CRYPT_MODE_INTERNAL, CRYPT_MODE_MCRYPT
*
* @param int $engine
* @access public
*/
function setCryptoEngine($engine)
{
$this->crypto_engine = $engine;
}
/**
* Send Identification String First
*
* https://tools.ietf.org/html/rfc4253#section-4.2 says "when the
connection has been established,
* both sides MUST send an identification string". It does not say
which side sends it first. In
* theory it shouldn't matter but it is a fact of life that some
SSH servers are simply buggy
*
* @access public
*/
function sendIdentificationStringFirst()
{
$this->send_id_string_first = true;
}
/**
* Send Identification String Last
*
* https://tools.ietf.org/html/rfc4253#section-4.2 says "when the
connection has been established,
* both sides MUST send an identification string". It does not say
which side sends it first. In
* theory it shouldn't matter but it is a fact of life that some
SSH servers are simply buggy
*
* @access public
*/
function sendIdentificationStringLast()
{
$this->send_id_string_first = false;
}
/**
* Send SSH_MSG_KEXINIT First
*
* https://tools.ietf.org/html/rfc4253#section-7.1 says "key
exchange begins by each sending
* sending the [SSH_MSG_KEXINIT] packet". It does not say which
side sends it first. In theory
* it shouldn't matter but it is a fact of life that some SSH
servers are simply buggy
*
* @access public
*/
function sendKEXINITFirst()
{
$this->send_kex_first = true;
}
/**
* Send SSH_MSG_KEXINIT Last
*
* https://tools.ietf.org/html/rfc4253#section-7.1 says "key
exchange begins by each sending
* sending the [SSH_MSG_KEXINIT] packet". It does not say which
side sends it first. In theory
* it shouldn't matter but it is a fact of life that some SSH
servers are simply buggy
*
* @access public
*/
function sendKEXINITLast()
{
$this->send_kex_first = false;
}
/**
* Connect to an SSHv2 server
*
* @return bool
* @access private
*/
function _connect()
{
if ($this->bitmap & self::MASK_CONSTRUCTOR) {
return false;
}
$this->bitmap |= self::MASK_CONSTRUCTOR;
$this->curTimeout = $this->timeout;
$this->last_packet = microtime(true);
if (!is_resource($this->fsock)) {
$start = microtime(true);
// with stream_select a timeout of 0 means that no timeout
takes place;
// with fsockopen a timeout of 0 means that you instantly
timeout
// to resolve this incompatibility a timeout of 100,000 will be
used for fsockopen if timeout is 0
$this->fsock = @fsockopen($this->host, $this->port,
$errno, $errstr, $this->curTimeout == 0 ? 100000 :
$this->curTimeout);
if (!$this->fsock) {
$host = $this->host . ':' . $this->port;
user_error(rtrim("Cannot connect to $host. Error
$errno. $errstr"));
return false;
}
$elapsed = microtime(true) - $start;
if ($this->curTimeout) {
$this->curTimeout-= $elapsed;
if ($this->curTimeout < 0) {
$this->is_timeout = true;
return false;
}
}
}
$this->identifier = $this->_generate_identifier();
if ($this->send_id_string_first) {
fputs($this->fsock, $this->identifier .
"\r\n");
}
/* According to the SSH2 specs,
"The server MAY send other lines of data before sending the
version
string. Each line SHOULD be terminated by a Carriage Return and
Line
Feed. Such lines MUST NOT begin with "SSH-", and
SHOULD be encoded
in ISO-10646 UTF-8 [RFC3629] (language is not specified).
Clients
MUST be able to process such lines." */
$data = '';
while (!feof($this->fsock) &&
!preg_match('#(.*)^(SSH-(\d\.\d+).*)#ms', $data, $matches)) {
$line = '';
while (true) {
if ($this->curTimeout) {
if ($this->curTimeout < 0) {
$this->is_timeout = true;
return false;
}
$read = array($this->fsock);
$write = $except = null;
$start = microtime(true);
$sec = floor($this->curTimeout);
$usec = 1000000 * ($this->curTimeout - $sec);
// on windows this returns a "Warning: Invalid CRT
parameters detected" error
// the !count() is done as a workaround for
<https://bugs.php.net/42682>
if (!@stream_select($read, $write, $except, $sec,
$usec) && !count($read)) {
$this->is_timeout = true;
return false;
}
$elapsed = microtime(true) - $start;
$this->curTimeout-= $elapsed;
}
$temp = stream_get_line($this->fsock, 255,
"\n");
if (strlen($temp) == 255) {
continue;
}
if ($temp === false) {
return false;
}
$line.= "$temp\n";
// quoting RFC4253, "Implementers who wish to maintain
// compatibility with older, undocumented versions of this
protocol may
// want to process the identification string without
expecting the
// presence of the carriage return character for reasons
described in
// Section 5 of this document."
//if (substr($line, -2) == "\r\n") {
// break;
//}
break;
}
$data.= $line;
}
if (feof($this->fsock)) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
$extra = $matches[1];
if (defined('NET_SSH2_LOGGING')) {
$this->_append_log('<-', $matches[0]);
$this->_append_log('->', $this->identifier .
"\r\n");
}
$this->server_identifier = trim($temp, "\r\n");
if (strlen($extra)) {
$this->errors[] = $data;
}
if (version_compare($matches[3], '1.99',
'<')) {
user_error("Cannot connect to SSH $matches[3]
servers");
return false;
}
if (!$this->send_id_string_first) {
fputs($this->fsock, $this->identifier .
"\r\n");
}
if (!$this->send_kex_first) {
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($response) || ord($response[0]) !=
NET_SSH2_MSG_KEXINIT) {
user_error('Expected SSH_MSG_KEXINIT');
return false;
}
if (!$this->_key_exchange($response)) {
return false;
}
}
if ($this->send_kex_first && !$this->_key_exchange())
{
return false;
}
$this->bitmap|= self::MASK_CONNECTED;
return true;
}
/**
* Generates the SSH identifier
*
* You should overwrite this method in your own class if you want to
use another identifier
*
* @access protected
* @return string
*/
function _generate_identifier()
{
$identifier = 'SSH-2.0-phpseclib_2.0';
$ext = array();
if
(function_exists('sodium_crypto_box_publickey_from_secretkey')) {
$ext[] = 'libsodium';
}
if (extension_loaded('openssl')) {
$ext[] = 'openssl';
} elseif (extension_loaded('mcrypt')) {
$ext[] = 'mcrypt';
}
if (extension_loaded('gmp')) {
$ext[] = 'gmp';
} elseif (extension_loaded('bcmath')) {
$ext[] = 'bcmath';
}
if (!empty($ext)) {
$identifier .= ' (' . implode(', ', $ext) .
')';
}
return $identifier;
}
/**
* Key Exchange
*
* @param string $kexinit_payload_server optional
* @access private
*/
function _key_exchange($kexinit_payload_server = false)
{
$preferred = $this->preferred;
$send_kex = true;
$kex_algorithms = isset($preferred['kex']) ?
$preferred['kex'] :
$this->getSupportedKEXAlgorithms();
$server_host_key_algorithms =
isset($preferred['hostkey']) ?
$preferred['hostkey'] :
$this->getSupportedHostKeyAlgorithms();
$s2c_encryption_algorithms =
isset($preferred['server_to_client']['crypt']) ?
$preferred['server_to_client']['crypt'] :
$this->getSupportedEncryptionAlgorithms();
$c2s_encryption_algorithms =
isset($preferred['client_to_server']['crypt']) ?
$preferred['client_to_server']['crypt'] :
$this->getSupportedEncryptionAlgorithms();
$s2c_mac_algorithms =
isset($preferred['server_to_client']['mac']) ?
$preferred['server_to_client']['mac'] :
$this->getSupportedMACAlgorithms();
$c2s_mac_algorithms =
isset($preferred['client_to_server']['mac']) ?
$preferred['client_to_server']['mac'] :
$this->getSupportedMACAlgorithms();
$s2c_compression_algorithms =
isset($preferred['server_to_client']['comp']) ?
$preferred['server_to_client']['comp'] :
$this->getSupportedCompressionAlgorithms();
$c2s_compression_algorithms =
isset($preferred['client_to_server']['comp']) ?
$preferred['client_to_server']['comp'] :
$this->getSupportedCompressionAlgorithms();
// some SSH servers have buggy implementations of some of the above
algorithms
switch (true) {
case $this->server_identifier == 'SSH-2.0-SSHD':
case substr($this->server_identifier, 0, 13) ==
'SSH-2.0-DLINK':
if
(!isset($preferred['server_to_client']['mac'])) {
$s2c_mac_algorithms = array_values(array_diff(
$s2c_mac_algorithms,
array('hmac-sha1-96',
'hmac-md5-96')
));
}
if
(!isset($preferred['client_to_server']['mac'])) {
$c2s_mac_algorithms = array_values(array_diff(
$c2s_mac_algorithms,
array('hmac-sha1-96',
'hmac-md5-96')
));
}
}
$str_kex_algorithms = implode(',', $kex_algorithms);
$str_server_host_key_algorithms = implode(',',
$server_host_key_algorithms);
$encryption_algorithms_server_to_client = implode(',',
$s2c_encryption_algorithms);
$encryption_algorithms_client_to_server = implode(',',
$c2s_encryption_algorithms);
$mac_algorithms_server_to_client = implode(',',
$s2c_mac_algorithms);
$mac_algorithms_client_to_server = implode(',',
$c2s_mac_algorithms);
$compression_algorithms_server_to_client = implode(',',
$s2c_compression_algorithms);
$compression_algorithms_client_to_server = implode(',',
$c2s_compression_algorithms);
$client_cookie = Random::string(16);
$kexinit_payload_client = pack(
'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
NET_SSH2_MSG_KEXINIT,
$client_cookie,
strlen($str_kex_algorithms),
$str_kex_algorithms,
strlen($str_server_host_key_algorithms),
$str_server_host_key_algorithms,
strlen($encryption_algorithms_client_to_server),
$encryption_algorithms_client_to_server,
strlen($encryption_algorithms_server_to_client),
$encryption_algorithms_server_to_client,
strlen($mac_algorithms_client_to_server),
$mac_algorithms_client_to_server,
strlen($mac_algorithms_server_to_client),
$mac_algorithms_server_to_client,
strlen($compression_algorithms_client_to_server),
$compression_algorithms_client_to_server,
strlen($compression_algorithms_server_to_client),
$compression_algorithms_server_to_client,
0,
'',
0,
'',
0,
0
);
if ($kexinit_payload_server === false) {
if (!$this->_send_binary_packet($kexinit_payload_client)) {
return false;
}
$kexinit_payload_server = $this->_get_binary_packet();
if ($kexinit_payload_server === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($kexinit_payload_server) ||
ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT) {
user_error('Expected SSH_MSG_KEXINIT');
return false;
}
$send_kex = false;
}
$response = $kexinit_payload_server;
$this->_string_shift($response, 1); // skip past the message
number (it should be SSH_MSG_KEXINIT)
$server_cookie = $this->_string_shift($response, 16);
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->kex_algorithms = explode(',',
$this->_string_shift($response, $temp['length']));
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->server_host_key_algorithms = explode(',',
$this->_string_shift($response, $temp['length']));
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->encryption_algorithms_client_to_server =
explode(',', $this->_string_shift($response,
$temp['length']));
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->encryption_algorithms_server_to_client =
explode(',', $this->_string_shift($response,
$temp['length']));
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->mac_algorithms_client_to_server = explode(',',
$this->_string_shift($response, $temp['length']));
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->mac_algorithms_server_to_client = explode(',',
$this->_string_shift($response, $temp['length']));
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->compression_algorithms_client_to_server =
explode(',', $this->_string_shift($response,
$temp['length']));
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->compression_algorithms_server_to_client =
explode(',', $this->_string_shift($response,
$temp['length']));
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->languages_client_to_server = explode(',',
$this->_string_shift($response, $temp['length']));
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->languages_server_to_client = explode(',',
$this->_string_shift($response, $temp['length']));
if (!strlen($response)) {
return false;
}
extract(unpack('Cfirst_kex_packet_follows',
$this->_string_shift($response, 1)));
$first_kex_packet_follows = $first_kex_packet_follows != 0;
if ($send_kex &&
!$this->_send_binary_packet($kexinit_payload_client)) {
return false;
}
// we need to decide upon the symmetric encryption algorithms
before we do the diffie-hellman key exchange
// we don't initialize any crypto-objects, yet - we do that,
later. for now, we need the lengths to make the
// diffie-hellman key exchange as fast as possible
$decrypt =
$this->_array_intersect_first($s2c_encryption_algorithms,
$this->encryption_algorithms_server_to_client);
$decryptKeyLength =
$this->_encryption_algorithm_to_key_size($decrypt);
if ($decryptKeyLength === null) {
user_error('No compatible server to client encryption
algorithms found');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$encrypt =
$this->_array_intersect_first($c2s_encryption_algorithms,
$this->encryption_algorithms_client_to_server);
$encryptKeyLength =
$this->_encryption_algorithm_to_key_size($encrypt);
if ($encryptKeyLength === null) {
user_error('No compatible client to server encryption
algorithms found');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
// through diffie-hellman key exchange a symmetric key is obtained
$this->kex_algorithm = $kex_algorithm =
$this->_array_intersect_first($kex_algorithms,
$this->kex_algorithms);
if ($kex_algorithm === false) {
user_error('No compatible key exchange algorithms
found');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$server_host_key_algorithm =
$this->_array_intersect_first($server_host_key_algorithms,
$this->server_host_key_algorithms);
if ($server_host_key_algorithm === false) {
user_error('No compatible server host key algorithms
found');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$mac_algorithm_in =
$this->_array_intersect_first($s2c_mac_algorithms,
$this->mac_algorithms_server_to_client);
if ($mac_algorithm_in === false) {
user_error('No compatible server to client message
authentication algorithms found');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$compression_algorithm_out =
$this->_array_intersect_first($c2s_compression_algorithms,
$this->compression_algorithms_client_to_server);
if ($compression_algorithm_out === false) {
user_error('No compatible client to server compression
algorithms found');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
//$this->decompress = $compression_algorithm_out ==
'zlib';
$compression_algorithm_in =
$this->_array_intersect_first($s2c_compression_algorithms,
$this->compression_algorithms_client_to_server);
if ($compression_algorithm_in === false) {
user_error('No compatible server to client compression
algorithms found');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
//$this->compress = $compression_algorithm_in ==
'zlib';
// Only relevant in diffie-hellman-group-exchange-sha{1,256},
otherwise empty.
$exchange_hash_rfc4419 = '';
if ($kex_algorithm === 'curve25519-sha256@libssh.org') {
$x = Random::string(32);
$eBytes = sodium_crypto_box_publickey_from_secretkey($x);
$clientKexInitMessage = 'NET_SSH2_MSG_KEX_ECDH_INIT';
$serverKexReplyMessage =
'NET_SSH2_MSG_KEX_ECDH_REPLY';
$kexHash = new Hash('sha256');
} else {
if (strpos($kex_algorithm,
'diffie-hellman-group-exchange') === 0) {
$dh_group_sizes_packed = pack(
'NNN',
$this->kex_dh_group_size_min,
$this->kex_dh_group_size_preferred,
$this->kex_dh_group_size_max
);
$packet = pack(
'Ca*',
NET_SSH2_MSG_KEXDH_GEX_REQUEST,
$dh_group_sizes_packed
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->_updateLogHistory('UNKNOWN (34)',
'NET_SSH2_MSG_KEXDH_GEX_REQUEST');
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) {
user_error('Expected
SSH_MSG_KEX_DH_GEX_GROUP');
return false;
}
$this->_updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY',
'NET_SSH2_MSG_KEXDH_GEX_GROUP');
if (strlen($response) < 4) {
return false;
}
extract(unpack('NprimeLength',
$this->_string_shift($response, 4)));
$primeBytes = $this->_string_shift($response,
$primeLength);
$prime = new BigInteger($primeBytes, -256);
if (strlen($response) < 4) {
return false;
}
extract(unpack('NgLength',
$this->_string_shift($response, 4)));
$gBytes = $this->_string_shift($response, $gLength);
$g = new BigInteger($gBytes, -256);
$exchange_hash_rfc4419 = pack(
'a*Na*Na*',
$dh_group_sizes_packed,
$primeLength,
$primeBytes,
$gLength,
$gBytes
);
$clientKexInitMessage =
'NET_SSH2_MSG_KEXDH_GEX_INIT';
$serverKexReplyMessage =
'NET_SSH2_MSG_KEXDH_GEX_REPLY';
} else {
switch ($kex_algorithm) {
// see http://tools.ietf.org/html/rfc2409#section-6.2
and
// http://tools.ietf.org/html/rfc2412, appendex E
case 'diffie-hellman-group1-sha1':
$prime =
'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74'
.
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437'
.
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED'
.
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
break;
// see http://tools.ietf.org/html/rfc3526#section-3
case 'diffie-hellman-group14-sha1':
$prime =
'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74'
.
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437'
.
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED'
.
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05'
.
'98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB'
.
'9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B'
.
'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718'
.
'3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF';
break;
}
// For both diffie-hellman-group1-sha1 and
diffie-hellman-group14-sha1
// the generator field element is 2 (decimal) and the hash
function is sha1.
$g = new BigInteger(2);
$prime = new BigInteger($prime, 16);
$clientKexInitMessage =
'NET_SSH2_MSG_KEXDH_INIT';
$serverKexReplyMessage =
'NET_SSH2_MSG_KEXDH_REPLY';
}
switch ($kex_algorithm) {
case 'diffie-hellman-group-exchange-sha256':
$kexHash = new Hash('sha256');
break;
default:
$kexHash = new Hash('sha1');
}
/* To increase the speed of the key exchange, both client and
server may
reduce the size of their private exponents. It should be at
least
twice as long as the key material that is generated from the
shared
secret. For more details, see the paper by van Oorschot and
Wiener
[VAN-OORSCHOT].
-- http://tools.ietf.org/html/rfc4419#section-6.2 */
$one = new BigInteger(1);
$keyLength = min($kexHash->getLength(),
max($encryptKeyLength, $decryptKeyLength));
$max = $one->bitwise_leftShift(16 * $keyLength); // 2 * 8 *
$keyLength
$max = $max->subtract($one);
$x = $one->random($one, $max);
$e = $g->modPow($x, $prime);
$eBytes = $e->toBytes(true);
}
$data = pack('CNa*', constant($clientKexInitMessage),
strlen($eBytes), $eBytes);
if (!$this->_send_binary_packet($data)) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
switch ($clientKexInitMessage) {
case 'NET_SSH2_MSG_KEX_ECDH_INIT':
$this->_updateLogHistory('NET_SSH2_MSG_KEXDH_INIT',
'NET_SSH2_MSG_KEX_ECDH_INIT');
break;
case 'NET_SSH2_MSG_KEXDH_GEX_INIT':
$this->_updateLogHistory('UNKNOWN (32)',
'NET_SSH2_MSG_KEXDH_GEX_INIT');
}
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($response)) {
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
if ($type != constant($serverKexReplyMessage)) {
user_error("Expected $serverKexReplyMessage");
return false;
}
switch ($serverKexReplyMessage) {
case 'NET_SSH2_MSG_KEX_ECDH_REPLY':
$this->_updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY',
'NET_SSH2_MSG_KEX_ECDH_REPLY');
break;
case 'NET_SSH2_MSG_KEXDH_GEX_REPLY':
$this->_updateLogHistory('UNKNOWN (33)',
'NET_SSH2_MSG_KEXDH_GEX_REPLY');
}
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->server_public_host_key = $server_public_host_key =
$this->_string_shift($response, $temp['length']);
if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($server_public_host_key, 4));
$public_key_format =
$this->_string_shift($server_public_host_key,
$temp['length']);
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$fBytes = $this->_string_shift($response,
$temp['length']);
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($response, 4));
$this->signature = $this->_string_shift($response,
$temp['length']);
if (strlen($this->signature) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($this->signature, 4));
$this->signature_format =
$this->_string_shift($this->signature, $temp['length']);
if ($kex_algorithm === 'curve25519-sha256@libssh.org') {
if (strlen($fBytes) !== 32) {
user_error('Received curve25519 public key of invalid
length.');
return false;
}
$key = new BigInteger(sodium_crypto_scalarmult($x, $fBytes),
256);
// sodium_compat doesn't emulate sodium_memzero
// also, with v1 of libsodium API the extension identifies
itself as
// libsodium whereas v2 of the libsodium API (what PHP 7.2+
includes)
// identifies itself as sodium. sodium_compat uses the v1 API
to
// emulate the v2 API if it's the v1 API that's
available
if (extension_loaded('sodium') ||
extension_loaded('libsodium')) {
sodium_memzero($x);
}
} else {
$f = new BigInteger($fBytes, -256);
$key = $f->modPow($x, $prime);
}
$keyBytes = $key->toBytes(true);
$this->exchange_hash = pack(
'Na*Na*Na*Na*Na*a*Na*Na*Na*',
strlen($this->identifier),
$this->identifier,
strlen($this->server_identifier),
$this->server_identifier,
strlen($kexinit_payload_client),
$kexinit_payload_client,
strlen($kexinit_payload_server),
$kexinit_payload_server,
strlen($this->server_public_host_key),
$this->server_public_host_key,
$exchange_hash_rfc4419,
strlen($eBytes),
$eBytes,
strlen($fBytes),
$fBytes,
strlen($keyBytes),
$keyBytes
);
$this->exchange_hash =
$kexHash->hash($this->exchange_hash);
if ($this->session_id === false) {
$this->session_id = $this->exchange_hash;
}
switch ($server_host_key_algorithm) {
case 'ssh-dss':
$expected_key_format = 'ssh-dss';
break;
//case 'rsa-sha2-256':
//case 'rsa-sha2-512':
//case 'ssh-rsa':
default:
$expected_key_format = 'ssh-rsa';
}
if ($public_key_format != $expected_key_format ||
$this->signature_format != $server_host_key_algorithm) {
switch (true) {
case $this->signature_format ==
$server_host_key_algorithm:
case $server_host_key_algorithm != 'rsa-sha2-256'
&& $server_host_key_algorithm != 'rsa-sha2-512':
case $this->signature_format != 'ssh-rsa':
user_error('Server Host Key Algorithm
Mismatch');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
}
$packet = pack(
'C',
NET_SSH2_MSG_NEWKEYS
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($response)) {
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
if ($type != NET_SSH2_MSG_NEWKEYS) {
user_error('Expected SSH_MSG_NEWKEYS');
return false;
}
$keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
$this->encrypt =
$this->_encryption_algorithm_to_crypt_instance($encrypt);
if ($this->encrypt) {
if ($this->crypto_engine) {
$this->encrypt->setPreferredEngine($this->crypto_engine);
}
if ($this->encrypt->block_size) {
$this->encrypt_block_size =
$this->encrypt->block_size;
}
$this->encrypt->enableContinuousBuffer();
$this->encrypt->disablePadding();
if ($this->encrypt->getBlockLength()) {
$this->encrypt_block_size =
$this->encrypt->getBlockLength() >> 3;
}
$iv = $kexHash->hash($keyBytes . $this->exchange_hash .
'A' . $this->session_id);
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));
$key = $kexHash->hash($keyBytes . $this->exchange_hash .
'C' . $this->session_id);
while ($encryptKeyLength > strlen($key)) {
$key.= $kexHash->hash($keyBytes .
$this->exchange_hash . $key);
}
$this->encrypt->setKey(substr($key, 0,
$encryptKeyLength));
$this->encrypt->name = $decrypt;
}
$this->decrypt =
$this->_encryption_algorithm_to_crypt_instance($decrypt);
if ($this->decrypt) {
if ($this->crypto_engine) {
$this->decrypt->setPreferredEngine($this->crypto_engine);
}
if ($this->decrypt->block_size) {
$this->decrypt_block_size =
$this->decrypt->block_size;
}
$this->decrypt->enableContinuousBuffer();
$this->decrypt->disablePadding();
if ($this->decrypt->getBlockLength()) {
$this->decrypt_block_size =
$this->decrypt->getBlockLength() >> 3;
}
$iv = $kexHash->hash($keyBytes . $this->exchange_hash .
'B' . $this->session_id);
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));
$key = $kexHash->hash($keyBytes . $this->exchange_hash .
'D' . $this->session_id);
while ($decryptKeyLength > strlen($key)) {
$key.= $kexHash->hash($keyBytes .
$this->exchange_hash . $key);
}
$this->decrypt->setKey(substr($key, 0,
$decryptKeyLength));
$this->decrypt->name = $decrypt;
}
/* The "arcfour128" algorithm is the RC4 cipher, as
described in
[SCHNEIER], using a 128-bit key. The first 1536 bytes of
keystream
generated by the cipher MUST be discarded, and the first byte of
the
first encrypted packet MUST be encrypted using the 1537th byte
of
keystream.
-- http://tools.ietf.org/html/rfc4345#section-4 */
if ($encrypt == 'arcfour128' || $encrypt ==
'arcfour256') {
$this->encrypt->encrypt(str_repeat("\0",
1536));
}
if ($decrypt == 'arcfour128' || $decrypt ==
'arcfour256') {
$this->decrypt->decrypt(str_repeat("\0",
1536));
}
$mac_algorithm_out =
$this->_array_intersect_first($c2s_mac_algorithms,
$this->mac_algorithms_client_to_server);
if ($mac_algorithm_out === false) {
user_error('No compatible client to server message
authentication algorithms found');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$createKeyLength = 0; // ie. $mac_algorithm == 'none'
switch ($mac_algorithm_out) {
case 'hmac-sha2-256':
$this->hmac_create = new Hash('sha256');
$createKeyLength = 32;
break;
case 'hmac-sha1':
$this->hmac_create = new Hash('sha1');
$createKeyLength = 20;
break;
case 'hmac-sha1-96':
$this->hmac_create = new Hash('sha1-96');
$createKeyLength = 20;
break;
case 'hmac-md5':
$this->hmac_create = new Hash('md5');
$createKeyLength = 16;
break;
case 'hmac-md5-96':
$this->hmac_create = new Hash('md5-96');
$createKeyLength = 16;
}
$this->hmac_create->name = $mac_algorithm_out;
$checkKeyLength = 0;
$this->hmac_size = 0;
switch ($mac_algorithm_in) {
case 'hmac-sha2-256':
$this->hmac_check = new Hash('sha256');
$checkKeyLength = 32;
$this->hmac_size = 32;
break;
case 'hmac-sha1':
$this->hmac_check = new Hash('sha1');
$checkKeyLength = 20;
$this->hmac_size = 20;
break;
case 'hmac-sha1-96':
$this->hmac_check = new Hash('sha1-96');
$checkKeyLength = 20;
$this->hmac_size = 12;
break;
case 'hmac-md5':
$this->hmac_check = new Hash('md5');
$checkKeyLength = 16;
$this->hmac_size = 16;
break;
case 'hmac-md5-96':
$this->hmac_check = new Hash('md5-96');
$checkKeyLength = 16;
$this->hmac_size = 12;
}
$this->hmac_check->name = $mac_algorithm_in;
$key = $kexHash->hash($keyBytes . $this->exchange_hash .
'E' . $this->session_id);
while ($createKeyLength > strlen($key)) {
$key.= $kexHash->hash($keyBytes . $this->exchange_hash .
$key);
}
$this->hmac_create->setKey(substr($key, 0,
$createKeyLength));
$key = $kexHash->hash($keyBytes . $this->exchange_hash .
'F' . $this->session_id);
while ($checkKeyLength > strlen($key)) {
$key.= $kexHash->hash($keyBytes . $this->exchange_hash .
$key);
}
$this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
return true;
}
/**
* Maps an encryption algorithm name to the number of key bytes.
*
* @param string $algorithm Name of the encryption algorithm
* @return int|null Number of bytes as an integer or null for unknown
* @access private
*/
function _encryption_algorithm_to_key_size($algorithm)
{
if ($this->bad_key_size_fix &&
$this->_bad_algorithm_candidate($algorithm)) {
return 16;
}
switch ($algorithm) {
case 'none':
return 0;
case 'aes128-cbc':
case 'aes128-ctr':
case 'arcfour':
case 'arcfour128':
case 'blowfish-cbc':
case 'blowfish-ctr':
case 'twofish128-cbc':
case 'twofish128-ctr':
return 16;
case '3des-cbc':
case '3des-ctr':
case 'aes192-cbc':
case 'aes192-ctr':
case 'twofish192-cbc':
case 'twofish192-ctr':
return 24;
case 'aes256-cbc':
case 'aes256-ctr':
case 'arcfour256':
case 'twofish-cbc':
case 'twofish256-cbc':
case 'twofish256-ctr':
return 32;
}
return null;
}
/**
* Maps an encryption algorithm name to an instance of a subclass of
* \phpseclib\Crypt\Base.
*
* @param string $algorithm Name of the encryption algorithm
* @return mixed Instance of \phpseclib\Crypt\Base or null for unknown
* @access private
*/
function _encryption_algorithm_to_crypt_instance($algorithm)
{
switch ($algorithm) {
case '3des-cbc':
return new TripleDES();
case '3des-ctr':
return new TripleDES(Base::MODE_CTR);
case 'aes256-cbc':
case 'aes192-cbc':
case 'aes128-cbc':
return new Rijndael();
case 'aes256-ctr':
case 'aes192-ctr':
case 'aes128-ctr':
return new Rijndael(Base::MODE_CTR);
case 'blowfish-cbc':
return new Blowfish();
case 'blowfish-ctr':
return new Blowfish(Base::MODE_CTR);
case 'twofish128-cbc':
case 'twofish192-cbc':
case 'twofish256-cbc':
case 'twofish-cbc':
return new Twofish();
case 'twofish128-ctr':
case 'twofish192-ctr':
case 'twofish256-ctr':
return new Twofish(Base::MODE_CTR);
case 'arcfour':
case 'arcfour128':
case 'arcfour256':
return new RC4();
}
return null;
}
/**
* Tests whether or not proposed algorithm has a potential for issues
*
* @link
https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html
* @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291
* @param string $algorithm Name of the encryption algorithm
* @return bool
* @access private
*/
function _bad_algorithm_candidate($algorithm)
{
switch ($algorithm) {
case 'arcfour256':
case 'aes192-ctr':
case 'aes256-ctr':
return true;
}
return false;
}
/**
* Login
*
* The $password parameter can be a plaintext password, a
\phpseclib\Crypt\RSA object or an array
*
* @param string $username
* @return bool
* @see self::_login()
* @access public
*/
function login($username)
{
$args = func_get_args();
$this->auth[] = $args;
// try logging with 'none' as an authentication method
first since that's what
// PuTTY does
if (substr($this->server_identifier, 0, 13) !=
'SSH-2.0-CoreFTP' && $this->auth_methods_to_continue
=== null) {
if ($this->_login($username)) {
return true;
}
if (count($args) == 1) {
return false;
}
}
return call_user_func_array(array(&$this, '_login'),
$args);
}
/**
* Login Helper
*
* @param string $username
* @return bool
* @see self::_login_helper()
* @access private
*/
function _login($username)
{
if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
if (!$this->_connect()) {
return false;
}
}
$args = array_slice(func_get_args(), 1);
if (empty($args)) {
return $this->_login_helper($username);
}
foreach ($args as $arg) {
if ($this->_login_helper($username, $arg)) {
return true;
}
}
return false;
}
/**
* Login Helper
*
* @param string $username
* @param string $password
* @return bool
* @access private
* @internal It might be worthwhile, at some point, to protect against
{@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
* by sending dummy SSH_MSG_IGNORE messages.
*/
function _login_helper($username, $password = null)
{
if (!($this->bitmap & self::MASK_CONNECTED)) {
return false;
}
if (!($this->bitmap & self::MASK_LOGIN_REQ)) {
$packet = pack(
'CNa*',
NET_SSH2_MSG_SERVICE_REQUEST,
strlen('ssh-userauth'),
'ssh-userauth'
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$response = $this->_get_binary_packet();
if ($response === false) {
if ($this->retry_connect) {
$this->retry_connect = false;
if (!$this->_connect()) {
return false;
}
return $this->_login_helper($username, $password);
}
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
user_error('Expected SSH_MSG_SERVICE_ACCEPT');
return false;
}
$this->bitmap |= self::MASK_LOGIN_REQ;
}
if (strlen($this->last_interactive_response)) {
return !is_string($password) && !is_array($password) ?
false : $this->_keyboard_interactive_process($password);
}
if ($password instanceof RSA) {
return $this->_privatekey_login($username, $password);
} elseif ($password instanceof Agent) {
return $this->_ssh_agent_login($username, $password);
}
if (is_array($password)) {
if ($this->_keyboard_interactive_login($username,
$password)) {
$this->bitmap |= self::MASK_LOGIN;
return true;
}
return false;
}
if (!isset($password)) {
$packet = pack(
'CNa*Na*Na*',
NET_SSH2_MSG_USERAUTH_REQUEST,
strlen($username),
$username,
strlen('ssh-connection'),
'ssh-connection',
strlen('none'),
'none'
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($response)) {
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
switch ($type) {
case NET_SSH2_MSG_USERAUTH_SUCCESS:
$this->bitmap |= self::MASK_LOGIN;
return true;
case NET_SSH2_MSG_USERAUTH_FAILURE:
extract(unpack('Nmethodlistlen',
$this->_string_shift($response, 4)));
$this->auth_methods_to_continue =
explode(',', $this->_string_shift($response, $methodlistlen));
default:
return false;
}
}
$packet = pack(
'CNa*Na*Na*CNa*',
NET_SSH2_MSG_USERAUTH_REQUEST,
strlen($username),
$username,
strlen('ssh-connection'),
'ssh-connection',
strlen('password'),
'password',
0,
strlen($password),
$password
);
// remove the username and password from the logged packet
if (!defined('NET_SSH2_LOGGING')) {
$logged = null;
} else {
$logged = pack(
'CNa*Na*Na*CNa*',
NET_SSH2_MSG_USERAUTH_REQUEST,
strlen('username'),
'username',
strlen('ssh-connection'),
'ssh-connection',
strlen('password'),
'password',
0,
strlen('password'),
'password'
);
}
if (!$this->_send_binary_packet($packet, $logged)) {
return false;
}
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($response)) {
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
switch ($type) {
case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the
password can be changed
$this->_updateLogHistory('UNKNOWN (60)',
'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ');
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$this->errors[] =
'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' .
$this->_string_shift($response, $length);
return
$this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
case NET_SSH2_MSG_USERAUTH_FAILURE:
// can we use keyboard-interactive authentication? if not
then either the login is bad or the server employees
// multi-factor authentication
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$auth_methods = explode(',',
$this->_string_shift($response, $length));
$this->auth_methods_to_continue = $auth_methods;
if (!strlen($response)) {
return false;
}
extract(unpack('Cpartial_success',
$this->_string_shift($response, 1)));
$partial_success = $partial_success != 0;
if (!$partial_success &&
in_array('keyboard-interactive', $auth_methods)) {
if ($this->_keyboard_interactive_login($username,
$password)) {
$this->bitmap |= self::MASK_LOGIN;
return true;
}
return false;
}
return false;
case NET_SSH2_MSG_USERAUTH_SUCCESS:
$this->bitmap |= self::MASK_LOGIN;
return true;
}
return false;
}
/**
* Login via keyboard-interactive authentication
*
* See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details.
This is not a full-featured keyboard-interactive authenticator.
*
* @param string $username
* @param string $password
* @return bool
* @access private
*/
function _keyboard_interactive_login($username, $password)
{
$packet = pack(
'CNa*Na*Na*Na*Na*',
NET_SSH2_MSG_USERAUTH_REQUEST,
strlen($username),
$username,
strlen('ssh-connection'),
'ssh-connection',
strlen('keyboard-interactive'),
'keyboard-interactive',
0,
'',
0,
''
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
return $this->_keyboard_interactive_process($password);
}
/**
* Handle the keyboard-interactive requests / responses.
*
* @return bool
* @access private
*/
function _keyboard_interactive_process()
{
$responses = func_get_args();
if (strlen($this->last_interactive_response)) {
$response = $this->last_interactive_response;
} else {
$orig = $response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
}
if (!strlen($response)) {
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
switch ($type) {
case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$this->_string_shift($response, $length); // name; may
be empty
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$this->_string_shift($response, $length); //
instruction; may be empty
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$this->_string_shift($response, $length); // language
tag; may be empty
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nnum_prompts',
$this->_string_shift($response, 4)));
for ($i = 0; $i < count($responses); $i++) {
if (is_array($responses[$i])) {
foreach ($responses[$i] as $key => $value) {
$this->keyboard_requests_responses[$key] =
$value;
}
unset($responses[$i]);
}
}
$responses = array_values($responses);
if (isset($this->keyboard_requests_responses)) {
for ($i = 0; $i < $num_prompts; $i++) {
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
// prompt - ie. "Password: "; must not be
empty
$prompt = $this->_string_shift($response,
$length);
//$echo = $this->_string_shift($response) !=
chr(0);
foreach ($this->keyboard_requests_responses as
$key => $value) {
if (substr($prompt, 0, strlen($key)) == $key) {
$responses[] = $value;
break;
}
}
}
}
// see http://tools.ietf.org/html/rfc4256#section-3.2
if (strlen($this->last_interactive_response)) {
$this->last_interactive_response = '';
} else {
$this->_updateLogHistory('UNKNOWN (60)',
'NET_SSH2_MSG_USERAUTH_INFO_REQUEST');
}
if (!count($responses) && $num_prompts) {
$this->last_interactive_response = $orig;
return false;
}
/*
After obtaining the requested information from the user,
the client
MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE
message.
*/
// see http://tools.ietf.org/html/rfc4256#section-3.4
$packet = $logged = pack('CN',
NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses));
for ($i = 0; $i < count($responses); $i++) {
$packet.= pack('Na*', strlen($responses[$i]),
$responses[$i]);
$logged.= pack('Na*',
strlen('dummy-answer'), 'dummy-answer');
}
if (!$this->_send_binary_packet($packet, $logged)) {
return false;
}
$this->_updateLogHistory('UNKNOWN (61)',
'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE');
/*
After receiving the response, the server MUST send
either an
SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or
another
SSH_MSG_USERAUTH_INFO_REQUEST message.
*/
// maybe phpseclib should force close the connection after
x request / responses? unless something like that is done
// there could be an infinite loop of request / responses.
return $this->_keyboard_interactive_process();
case NET_SSH2_MSG_USERAUTH_SUCCESS:
return true;
case NET_SSH2_MSG_USERAUTH_FAILURE:
extract(unpack('Nmethodlistlen',
$this->_string_shift($response, 4)));
$this->auth_methods_to_continue = explode(',',
$this->_string_shift($response, $methodlistlen));
return false;
}
return false;
}
/**
* Login with an ssh-agent provided key
*
* @param string $username
* @param \phpseclib\System\SSH\Agent $agent
* @return bool
* @access private
*/
function _ssh_agent_login($username, $agent)
{
$this->agent = $agent;
$keys = $agent->requestIdentities();
foreach ($keys as $key) {
if ($this->_privatekey_login($username, $key)) {
return true;
}
}
return false;
}
/**
* Login with an RSA private key
*
* @param string $username
* @param \phpseclib\Crypt\RSA $privatekey
* @return bool
* @access private
* @internal It might be worthwhile, at some point, to protect against
{@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
* by sending dummy SSH_MSG_IGNORE messages.
*/
function _privatekey_login($username, $privatekey)
{
// see http://tools.ietf.org/html/rfc4253#page-15
$publickey = $privatekey->getPublicKey(RSA::PUBLIC_FORMAT_RAW);
if ($publickey === false) {
return false;
}
$publickey = array(
'e' =>
$publickey['e']->toBytes(true),
'n' => $publickey['n']->toBytes(true)
);
$publickey = pack(
'Na*Na*Na*',
strlen('ssh-rsa'),
'ssh-rsa',
strlen($publickey['e']),
$publickey['e'],
strlen($publickey['n']),
$publickey['n']
);
switch ($this->signature_format) {
case 'rsa-sha2-512':
$hash = 'sha512';
$signatureType = 'rsa-sha2-512';
break;
case 'rsa-sha2-256':
$hash = 'sha256';
$signatureType = 'rsa-sha2-256';
break;
//case 'ssh-rsa':
default:
$hash = 'sha1';
$signatureType = 'ssh-rsa';
}
$part1 = pack(
'CNa*Na*Na*',
NET_SSH2_MSG_USERAUTH_REQUEST,
strlen($username),
$username,
strlen('ssh-connection'),
'ssh-connection',
strlen('publickey'),
'publickey'
);
$part2 = pack('Na*Na*', strlen($signatureType),
$signatureType, strlen($publickey), $publickey);
$packet = $part1 . chr(0) . $part2;
if (!$this->_send_binary_packet($packet)) {
return false;
}
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($response)) {
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
switch ($type) {
case NET_SSH2_MSG_USERAUTH_FAILURE:
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nmethodlistlen',
$this->_string_shift($response, 4)));
$this->auth_methods_to_continue = explode(',',
$this->_string_shift($response, $methodlistlen));
$this->errors[] = 'SSH_MSG_USERAUTH_FAILURE';
return false;
case NET_SSH2_MSG_USERAUTH_PK_OK:
// we'll just take it on faith that the public key
blob and the public key algorithm name are as
// they should be
$this->_updateLogHistory('UNKNOWN (60)',
'NET_SSH2_MSG_USERAUTH_PK_OK');
break;
case NET_SSH2_MSG_USERAUTH_SUCCESS:
$this->bitmap |= self::MASK_LOGIN;
return true;
default:
user_error('Unexpected response to publickey
authentication pt 1');
return
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
$packet = $part1 . chr(1) . $part2;
$privatekey->setSignatureMode(RSA::SIGNATURE_PKCS1);
$privatekey->setHash($hash);
$signature = $privatekey->sign(pack('Na*a*',
strlen($this->session_id), $this->session_id, $packet));
$signature = pack('Na*Na*', strlen($signatureType),
$signatureType, strlen($signature), $signature);
$packet.= pack('Na*', strlen($signature), $signature);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($response)) {
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
switch ($type) {
case NET_SSH2_MSG_USERAUTH_FAILURE:
// either the login is bad or the server employs
multi-factor authentication
extract(unpack('Nmethodlistlen',
$this->_string_shift($response, 4)));
$this->auth_methods_to_continue = explode(',',
$this->_string_shift($response, $methodlistlen));
return false;
case NET_SSH2_MSG_USERAUTH_SUCCESS:
$this->bitmap |= self::MASK_LOGIN;
return true;
}
user_error('Unexpected response to publickey authentication pt
2');
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
/**
* Set Timeout
*
* $ssh->exec('ping 127.0.0.1'); on a Linux host will
never return and will run indefinitely. setTimeout() makes it so
it'll timeout.
* Setting $timeout to false or 0 will mean there is no timeout.
*
* @param mixed $timeout
* @access public
*/
function setTimeout($timeout)
{
$this->timeout = $this->curTimeout = $timeout;
}
/**
* Set Keep Alive
*
* Sends an SSH2_MSG_IGNORE message every x seconds, if x is a positive
non-zero number.
*
* @param int $interval
* @access public
*/
function setKeepAlive($interval)
{
$this->keepAlive = $interval;
}
/**
* Get the output from stdError
*
* @access public
*/
function getStdError()
{
return $this->stdErrorLog;
}
/**
* Execute Command
*
* If $callback is set to false then
\phpseclib\Net\SSH2::_get_channel_packet(self::CHANNEL_EXEC) will need to
be called manually.
* In all likelihood, this is not a feature you want to be taking
advantage of.
*
* @param string $command
* @param Callback $callback
* @return string
* @access public
*/
function exec($command, $callback = null)
{
$this->curTimeout = $this->timeout;
$this->is_timeout = false;
$this->stdErrorLog = '';
if (!$this->isAuthenticated()) {
return false;
}
if ($this->in_request_pty_exec) {
user_error('If you want to run multiple exec()\'s you
will need to disable (and re-enable if appropriate) a PTY for each
one.');
return false;
}
// RFC4254 defines the (client) window size as "bytes the
other party can send before it must wait for the window to
// be adjusted". 0x7FFFFFFF is, at 2GB, the max size.
technically, it should probably be decremented, but,
// honestly, if you're transferring more than 2GB, you
probably shouldn't be using phpseclib, anyway.
// see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
$this->window_size_server_to_client[self::CHANNEL_EXEC] =
$this->window_size;
// 0x8000 is the maximum max packet size, per
http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
// uses 0x4000, that's what will be used here, as well.
$packet_size = 0x4000;
$packet = pack(
'CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN,
strlen('session'),
'session',
self::CHANNEL_EXEC,
$this->window_size_server_to_client[self::CHANNEL_EXEC],
$packet_size
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[self::CHANNEL_EXEC] =
NET_SSH2_MSG_CHANNEL_OPEN;
$response = $this->_get_channel_packet(self::CHANNEL_EXEC);
if ($response === false) {
return false;
}
if ($this->request_pty === true) {
$terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
$packet = pack(
'CNNa*CNa*N5a*',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_EXEC],
strlen('pty-req'),
'pty-req',
1,
strlen('vt100'),
'vt100',
$this->windowColumns,
$this->windowRows,
0,
0,
strlen($terminal_modes),
$terminal_modes
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($response)) {
return false;
}
list(, $type) = unpack('C',
$this->_string_shift($response, 1));
switch ($type) {
case NET_SSH2_MSG_CHANNEL_SUCCESS:
break;
case NET_SSH2_MSG_CHANNEL_FAILURE:
default:
user_error('Unable to request
pseudo-terminal');
return
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
$this->in_request_pty_exec = true;
}
// sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary
and, in fact, in most cases, slows things
// down. the one place where it might be desirable is if
you're doing something like \phpseclib\Net\SSH2::exec('ping
localhost &').
// with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return
immediately and the ping process will then
// then immediately terminate. without such a request exec() will
loop indefinitely. the ping process won't end but
// neither will your script.
// although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could
exceed the maximum packet size established by
// SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states
that the "maximum packet size" refers to the
// "maximum size of an individual data packet". ie.
SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates.
$packet = pack(
'CNNa*CNa*',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_EXEC],
strlen('exec'),
'exec',
1,
strlen($command),
$command
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[self::CHANNEL_EXEC] =
NET_SSH2_MSG_CHANNEL_REQUEST;
$response = $this->_get_channel_packet(self::CHANNEL_EXEC);
if ($response === false) {
return false;
}
$this->channel_status[self::CHANNEL_EXEC] =
NET_SSH2_MSG_CHANNEL_DATA;
if ($callback === false || $this->in_request_pty_exec) {
return true;
}
$output = '';
while (true) {
$temp = $this->_get_channel_packet(self::CHANNEL_EXEC);
switch (true) {
case $temp === true:
return is_callable($callback) ? true : $output;
case $temp === false:
return false;
default:
if (is_callable($callback)) {
if (call_user_func($callback, $temp) === true) {
$this->_close_channel(self::CHANNEL_EXEC);
return true;
}
} else {
$output.= $temp;
}
}
}
}
/**
* Creates an interactive shell
*
* @see self::read()
* @see self::write()
* @return bool
* @access private
*/
function _initShell()
{
if ($this->in_request_pty_exec === true) {
return true;
}
$this->window_size_server_to_client[self::CHANNEL_SHELL] =
$this->window_size;
$packet_size = 0x4000;
$packet = pack(
'CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN,
strlen('session'),
'session',
self::CHANNEL_SHELL,
$this->window_size_server_to_client[self::CHANNEL_SHELL],
$packet_size
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[self::CHANNEL_SHELL] =
NET_SSH2_MSG_CHANNEL_OPEN;
$response = $this->_get_channel_packet(self::CHANNEL_SHELL);
if ($response === false) {
return false;
}
$terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
$packet = pack(
'CNNa*CNa*N5a*',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_SHELL],
strlen('pty-req'),
'pty-req',
1,
strlen('vt100'),
'vt100',
$this->windowColumns,
$this->windowRows,
0,
0,
strlen($terminal_modes),
$terminal_modes
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$packet = pack(
'CNNa*C',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_SHELL],
strlen('shell'),
'shell',
1
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[self::CHANNEL_SHELL] =
NET_SSH2_MSG_IGNORE;
$this->bitmap |= self::MASK_SHELL;
return true;
}
/**
* Return the channel to be used with read() / write()
*
* @see self::read()
* @see self::write()
* @return int
* @access public
*/
function _get_interactive_channel()
{
switch (true) {
case $this->in_subsystem:
return self::CHANNEL_SUBSYSTEM;
case $this->in_request_pty_exec:
return self::CHANNEL_EXEC;
default:
return self::CHANNEL_SHELL;
}
}
/**
* Return an available open channel
*
* @return int
* @access public
*/
function _get_open_channel()
{
$channel = self::CHANNEL_EXEC;
do {
if (isset($this->channel_status[$channel]) &&
$this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) {
return $channel;
}
} while ($channel++ < self::CHANNEL_SUBSYSTEM);
return false;
}
/**
* Returns the output of an interactive shell
*
* Returns when there's a match for $expect, which can take the
form of a string literal or,
* if $mode == self::READ_REGEX, a regular expression.
*
* @see self::write()
* @param string $expect
* @param int $mode
* @return string|bool
* @access public
*/
function read($expect = '', $mode = self::READ_SIMPLE)
{
$this->curTimeout = $this->timeout;
$this->is_timeout = false;
if (!$this->isAuthenticated()) {
user_error('Operation disallowed prior to login()');
return false;
}
if (!($this->bitmap & self::MASK_SHELL) &&
!$this->_initShell()) {
user_error('Unable to initiate an interactive shell
session');
return false;
}
$channel = $this->_get_interactive_channel();
if ($mode == self::READ_NEXT) {
return $this->_get_channel_packet($channel);
}
$match = $expect;
while (true) {
if ($mode == self::READ_REGEX) {
preg_match($expect, substr($this->interactiveBuffer,
-1024), $matches);
$match = isset($matches[0]) ? $matches[0] : '';
}
$pos = strlen($match) ? strpos($this->interactiveBuffer,
$match) : false;
if ($pos !== false) {
return $this->_string_shift($this->interactiveBuffer,
$pos + strlen($match));
}
$response = $this->_get_channel_packet($channel);
if (is_bool($response)) {
$this->in_request_pty_exec = false;
return $response ?
$this->_string_shift($this->interactiveBuffer,
strlen($this->interactiveBuffer)) : false;
}
$this->interactiveBuffer.= $response;
}
}
/**
* Inputs a command into an interactive shell.
*
* @see self::read()
* @param string $cmd
* @return bool
* @access public
*/
function write($cmd)
{
if (!$this->isAuthenticated()) {
user_error('Operation disallowed prior to login()');
return false;
}
if (!($this->bitmap & self::MASK_SHELL) &&
!$this->_initShell()) {
user_error('Unable to initiate an interactive shell
session');
return false;
}
return
$this->_send_channel_packet($this->_get_interactive_channel(), $cmd);
}
/**
* Start a subsystem.
*
* Right now only one subsystem at a time is supported. To support
multiple subsystem's stopSubsystem() could accept
* a string that contained the name of the subsystem, but at that
point, only one subsystem of each type could be opened.
* To support multiple subsystem's of the same name maybe
it'd be best if startSubsystem() generated a new channel id and
* returns that and then that that was passed into stopSubsystem() but
that'll be saved for a future date and implemented
* if there's sufficient demand for such a feature.
*
* @see self::stopSubsystem()
* @param string $subsystem
* @return bool
* @access public
*/
function startSubsystem($subsystem)
{
$this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] =
$this->window_size;
$packet = pack(
'CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN,
strlen('session'),
'session',
self::CHANNEL_SUBSYSTEM,
$this->window_size,
0x4000
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[self::CHANNEL_SUBSYSTEM] =
NET_SSH2_MSG_CHANNEL_OPEN;
$response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM);
if ($response === false) {
return false;
}
$packet = pack(
'CNNa*CNa*',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_SUBSYSTEM],
strlen('subsystem'),
'subsystem',
1,
strlen($subsystem),
$subsystem
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[self::CHANNEL_SUBSYSTEM] =
NET_SSH2_MSG_CHANNEL_REQUEST;
$response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM);
if ($response === false) {
return false;
}
$this->channel_status[self::CHANNEL_SUBSYSTEM] =
NET_SSH2_MSG_CHANNEL_DATA;
$this->bitmap |= self::MASK_SHELL;
$this->in_subsystem = true;
return true;
}
/**
* Stops a subsystem.
*
* @see self::startSubsystem()
* @return bool
* @access public
*/
function stopSubsystem()
{
$this->in_subsystem = false;
$this->_close_channel(self::CHANNEL_SUBSYSTEM);
return true;
}
/**
* Closes a channel
*
* If read() timed out you might want to just close the channel and
have it auto-restart on the next read() call
*
* @access public
*/
function reset()
{
$this->_close_channel($this->_get_interactive_channel());
}
/**
* Is timeout?
*
* Did exec() or read() return because they timed out or because they
encountered the end?
*
* @access public
*/
function isTimeout()
{
return $this->is_timeout;
}
/**
* Disconnect
*
* @access public
*/
function disconnect()
{
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
if (isset($this->realtime_log_file) &&
is_resource($this->realtime_log_file)) {
fclose($this->realtime_log_file);
}
}
/**
* Destructor.
*
* Will be called, automatically, if you're supporting just PHP5.
If you're supporting PHP4, you'll need to call
* disconnect().
*
* @access public
*/
function __destruct()
{
$this->disconnect();
}
/**
* Is the connection still active?
*
* @return bool
* @access public
*/
function isConnected()
{
return (bool) ($this->bitmap & self::MASK_CONNECTED);
}
/**
* Have you successfully been logged in?
*
* @return bool
* @access public
*/
function isAuthenticated()
{
return (bool) ($this->bitmap & self::MASK_LOGIN);
}
/**
* Pings a server connection, or tries to reconnect if the connection
has gone down
*
* Inspired by http://php.net/manual/en/mysqli.ping.php
*
* @return bool
* @access public
*/
function ping()
{
if (!$this->isAuthenticated()) {
if (!empty($this->auth)) {
return $this->_reconnect();
}
return false;
}
$this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] =
$this->window_size;
$packet_size = 0x4000;
$packet = pack(
'CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN,
strlen('session'),
'session',
self::CHANNEL_KEEP_ALIVE,
$this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE],
$packet_size
);
if (!@$this->_send_binary_packet($packet)) {
return $this->_reconnect();
}
$this->channel_status[self::CHANNEL_KEEP_ALIVE] =
NET_SSH2_MSG_CHANNEL_OPEN;
$response =
@$this->_get_channel_packet(self::CHANNEL_KEEP_ALIVE);
if ($response !== false) {
$this->_close_channel(self::CHANNEL_KEEP_ALIVE);
return true;
}
return $this->_reconnect();
}
/**
* In situ reconnect method
*
* @return boolean
* @access private
*/
function _reconnect()
{
$this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST);
$this->retry_connect = true;
if (!$this->_connect()) {
return false;
}
foreach ($this->auth as $auth) {
$result = call_user_func_array(array(&$this,
'login'), $auth);
}
return $result;
}
/**
* Resets a connection for re-use
*
* @param int $reason
* @access private
*/
function _reset_connection($reason)
{
$this->_disconnect($reason);
$this->decrypt = $this->encrypt = false;
$this->decrypt_block_size = $this->encrypt_block_size = 8;
$this->hmac_check = $this->hmac_create = false;
$this->hmac_size = false;
$this->session_id = false;
$this->retry_connect = true;
$this->get_seq_no = $this->send_seq_no = 0;
}
/**
* Gets Binary Packets
*
* See '6. Binary Packet Protocol' of rfc4253 for more info.
*
* @see self::_send_binary_packet()
* @return string
* @access private
*/
function _get_binary_packet($skip_channel_filter = false)
{
if ($skip_channel_filter) {
$read = array($this->fsock);
$write = $except = null;
if (!$this->curTimeout) {
if ($this->keepAlive <= 0) {
@stream_select($read, $write, $except, null);
} else {
if (!@stream_select($read, $write, $except,
$this->keepAlive) && !count($read)) {
$this->_send_binary_packet(pack('CN',
NET_SSH2_MSG_IGNORE, 0));
return $this->_get_binary_packet(true);
}
}
} else {
if ($this->curTimeout < 0) {
$this->is_timeout = true;
return true;
}
$read = array($this->fsock);
$write = $except = null;
$start = microtime(true);
if ($this->keepAlive > 0 &&
$this->keepAlive < $this->curTimeout) {
if (!@stream_select($read, $write, $except,
$this->keepAlive) && !count($read)) {
$this->_send_binary_packet(pack('CN',
NET_SSH2_MSG_IGNORE, 0));
$elapsed = microtime(true) - $start;
$this->curTimeout-= $elapsed;
return $this->_get_binary_packet(true);
}
$elapsed = microtime(true) - $start;
$this->curTimeout-= $elapsed;
}
$sec = floor($this->curTimeout);
$usec = 1000000 * ($this->curTimeout - $sec);
// on windows this returns a "Warning: Invalid CRT
parameters detected" error
if (!@stream_select($read, $write, $except, $sec, $usec)
&& !count($read)) {
$this->is_timeout = true;
return true;
}
$elapsed = microtime(true) - $start;
$this->curTimeout-= $elapsed;
}
}
if (!is_resource($this->fsock) || feof($this->fsock)) {
$this->bitmap = 0;
user_error('Connection closed prematurely');
return false;
}
$start = microtime(true);
$raw = stream_get_contents($this->fsock,
$this->decrypt_block_size);
if (!strlen($raw)) {
return '';
}
if ($this->decrypt !== false) {
$raw = $this->decrypt->decrypt($raw);
}
if ($raw === false) {
user_error('Unable to decrypt content');
return false;
}
if (strlen($raw) < 5) {
return false;
}
extract(unpack('Npacket_length/Cpadding_length',
$this->_string_shift($raw, 5)));
$remaining_length = $packet_length + 4 -
$this->decrypt_block_size;
// quoting <http://tools.ietf.org/html/rfc4253#section-6.1>,
// "implementations SHOULD check that the packet length is
reasonable"
// PuTTY uses 0x9000 as the actual max packet size and so to shall
we
if ($remaining_length < -$this->decrypt_block_size ||
$remaining_length > 0x9000 || $remaining_length %
$this->decrypt_block_size != 0) {
if (!$this->bad_key_size_fix &&
$this->_bad_algorithm_candidate($this->decrypt->name) &&
!($this->bitmap & SSH2::MASK_LOGIN)) {
$this->bad_key_size_fix = true;
$this->_reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
return false;
}
user_error('Invalid size');
return false;
}
$buffer = '';
while ($remaining_length > 0) {
$temp = stream_get_contents($this->fsock,
$remaining_length);
if ($temp === false || feof($this->fsock)) {
$this->bitmap = 0;
user_error('Error reading from socket');
return false;
}
$buffer.= $temp;
$remaining_length-= strlen($temp);
}
$stop = microtime(true);
if (strlen($buffer)) {
$raw.= $this->decrypt !== false ?
$this->decrypt->decrypt($buffer) : $buffer;
}
$payload = $this->_string_shift($raw, $packet_length -
$padding_length - 1);
$padding = $this->_string_shift($raw, $padding_length); //
should leave $raw empty
if ($this->hmac_check !== false) {
$hmac = stream_get_contents($this->fsock,
$this->hmac_size);
if ($hmac === false || strlen($hmac) != $this->hmac_size) {
$this->bitmap = 0;
user_error('Error reading socket');
return false;
} elseif ($hmac !=
$this->hmac_check->hash(pack('NNCa*', $this->get_seq_no,
$packet_length, $padding_length, $payload . $padding))) {
user_error('Invalid HMAC');
return false;
}
}
//if ($this->decompress) {
// $payload = gzinflate(substr($payload, 2));
//}
$this->get_seq_no++;
if (defined('NET_SSH2_LOGGING')) {
$current = microtime(true);
$message_number =
isset($this->message_numbers[ord($payload[0])]) ?
$this->message_numbers[ord($payload[0])] : 'UNKNOWN (' .
ord($payload[0]) . ')';
$message_number = '<- ' . $message_number .
' (since last: ' . round($current -
$this->last_packet, 4) . ', network: ' . round($stop - $start,
4) . 's)';
$this->_append_log($message_number, $payload);
$this->last_packet = $current;
}
return $this->_filter($payload, $skip_channel_filter);
}
/**
* Filter Binary Packets
*
* Because some binary packets need to be ignored...
*
* @see self::_get_binary_packet()
* @return string
* @access private
*/
function _filter($payload, $skip_channel_filter)
{
switch (ord($payload[0])) {
case NET_SSH2_MSG_DISCONNECT:
$this->_string_shift($payload, 1);
if (strlen($payload) < 8) {
return false;
}
extract(unpack('Nreason_code/Nlength',
$this->_string_shift($payload, 8)));
$this->errors[] = 'SSH_MSG_DISCONNECT: ' .
$this->disconnect_reasons[$reason_code] . "\r\n" .
$this->_string_shift($payload, $length);
$this->bitmap = 0;
return false;
case NET_SSH2_MSG_IGNORE:
$payload =
$this->_get_binary_packet($skip_channel_filter);
break;
case NET_SSH2_MSG_DEBUG:
$this->_string_shift($payload, 2);
if (strlen($payload) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($payload, 4)));
$this->errors[] = 'SSH_MSG_DEBUG: ' .
$this->_string_shift($payload, $length);
$payload =
$this->_get_binary_packet($skip_channel_filter);
break;
case NET_SSH2_MSG_UNIMPLEMENTED:
return false;
case NET_SSH2_MSG_KEXINIT:
if ($this->session_id !== false) {
$this->send_kex_first = false;
if (!$this->_key_exchange($payload)) {
$this->bitmap = 0;
return false;
}
$payload =
$this->_get_binary_packet($skip_channel_filter);
}
}
// see http://tools.ietf.org/html/rfc4252#section-5.4; only called
when the encryption has been activated and when we haven't already
logged in
if (($this->bitmap & self::MASK_CONNECTED) &&
!$this->isAuthenticated() && ord($payload[0]) ==
NET_SSH2_MSG_USERAUTH_BANNER) {
$this->_string_shift($payload, 1);
if (strlen($payload) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($payload, 4)));
$this->banner_message = $this->_string_shift($payload,
$length);
$payload = $this->_get_binary_packet();
}
// only called when we've already logged in
if (($this->bitmap & self::MASK_CONNECTED) &&
$this->isAuthenticated()) {
if (is_bool($payload)) {
return $payload;
}
switch (ord($payload[0])) {
case NET_SSH2_MSG_CHANNEL_REQUEST:
if (strlen($payload) == 31) {
extract(unpack('cpacket_type/Nchannel/Nlength', $payload));
if (substr($payload, 9, $length) ==
'keepalive@openssh.com' &&
isset($this->server_channels[$channel])) {
if (ord(substr($payload, 9 + $length))) { //
want reply
$this->_send_binary_packet(pack('CN',
NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel]));
}
$payload =
$this->_get_binary_packet($skip_channel_filter);
}
}
break;
case NET_SSH2_MSG_CHANNEL_DATA:
case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
case NET_SSH2_MSG_CHANNEL_CLOSE:
case NET_SSH2_MSG_CHANNEL_EOF:
if (!$skip_channel_filter &&
!empty($this->server_channels)) {
$this->binary_packet_buffer = $payload;
$this->_get_channel_packet(true);
$payload = $this->_get_binary_packet();
}
break;
case NET_SSH2_MSG_GLOBAL_REQUEST: // see
http://tools.ietf.org/html/rfc4254#section-4
if (strlen($payload) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($payload, 4)));
$this->errors[] = 'SSH_MSG_GLOBAL_REQUEST:
' . $this->_string_shift($payload, $length);
if (!$this->_send_binary_packet(pack('C',
NET_SSH2_MSG_REQUEST_FAILURE))) {
return
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
$payload =
$this->_get_binary_packet($skip_channel_filter);
break;
case NET_SSH2_MSG_CHANNEL_OPEN: // see
http://tools.ietf.org/html/rfc4254#section-5.1
$this->_string_shift($payload, 1);
if (strlen($payload) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($payload, 4)));
$data = $this->_string_shift($payload, $length);
if (strlen($payload) < 4) {
return false;
}
extract(unpack('Nserver_channel',
$this->_string_shift($payload, 4)));
switch ($data) {
case 'auth-agent':
case 'auth-agent@openssh.com':
if (isset($this->agent)) {
$new_channel = self::CHANNEL_AGENT_FORWARD;
if (strlen($payload) < 8) {
return false;
}
extract(unpack('Nremote_window_size',
$this->_string_shift($payload, 4)));
extract(unpack('Nremote_maximum_packet_size',
$this->_string_shift($payload, 4)));
$this->packet_size_client_to_server[$new_channel] = $remote_window_size;
$this->window_size_server_to_client[$new_channel] =
$remote_maximum_packet_size;
$this->window_size_client_to_server[$new_channel] =
$this->window_size;
$packet_size = 0x4000;
$packet = pack(
'CN4',
NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION,
$server_channel,
$new_channel,
$packet_size,
$packet_size
);
$this->server_channels[$new_channel] =
$server_channel;
$this->channel_status[$new_channel] =
NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION;
if
(!$this->_send_binary_packet($packet)) {
return false;
}
}
break;
default:
$packet = pack(
'CN3a*Na*',
NET_SSH2_MSG_REQUEST_FAILURE,
$server_channel,
NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
0,
'',
0,
''
);
if (!$this->_send_binary_packet($packet)) {
return
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
}
$payload =
$this->_get_binary_packet($skip_channel_filter);
break;
case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
$this->_string_shift($payload, 1);
if (strlen($payload) < 8) {
return false;
}
extract(unpack('Nchannel',
$this->_string_shift($payload, 4)));
extract(unpack('Nwindow_size',
$this->_string_shift($payload, 4)));
$this->window_size_client_to_server[$channel]+=
$window_size;
$payload = ($this->bitmap &
self::MASK_WINDOW_ADJUST) ? true :
$this->_get_binary_packet($skip_channel_filter);
}
}
return $payload;
}
/**
* Enable Quiet Mode
*
* Suppress stderr from output
*
* @access public
*/
function enableQuietMode()
{
$this->quiet_mode = true;
}
/**
* Disable Quiet Mode
*
* Show stderr in output
*
* @access public
*/
function disableQuietMode()
{
$this->quiet_mode = false;
}
/**
* Returns whether Quiet Mode is enabled or not
*
* @see self::enableQuietMode()
* @see self::disableQuietMode()
* @access public
* @return bool
*/
function isQuietModeEnabled()
{
return $this->quiet_mode;
}
/**
* Enable request-pty when using exec()
*
* @access public
*/
function enablePTY()
{
$this->request_pty = true;
}
/**
* Disable request-pty when using exec()
*
* @access public
*/
function disablePTY()
{
if ($this->in_request_pty_exec) {
$this->_close_channel(self::CHANNEL_EXEC);
$this->in_request_pty_exec = false;
}
$this->request_pty = false;
}
/**
* Returns whether request-pty is enabled or not
*
* @see self::enablePTY()
* @see self::disablePTY()
* @access public
* @return bool
*/
function isPTYEnabled()
{
return $this->request_pty;
}
/**
* Gets channel data
*
* Returns the data as a string if it's available and false if
not.
*
* @param int $client_channel
* @param bool $skip_extended
* @return mixed|bool
* @access private
*/
function _get_channel_packet($client_channel, $skip_extended = false)
{
if (!empty($this->channel_buffers[$client_channel])) {
return array_shift($this->channel_buffers[$client_channel]);
}
while (true) {
if ($this->binary_packet_buffer !== false) {
$response = $this->binary_packet_buffer;
$this->binary_packet_buffer = false;
} else {
$response = $this->_get_binary_packet(true);
if ($response === true && $this->is_timeout) {
if ($client_channel == self::CHANNEL_EXEC &&
!$this->request_pty) {
$this->_close_channel($client_channel);
}
return true;
}
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
}
if ($client_channel == -1 && $response === true) {
return true;
}
if (!strlen($response)) {
return false;
}
extract(unpack('Ctype',
$this->_string_shift($response, 1)));
if (strlen($response) < 4) {
return false;
}
if ($type == NET_SSH2_MSG_CHANNEL_OPEN) {
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
} else {
extract(unpack('Nchannel',
$this->_string_shift($response, 4)));
}
// will not be setup yet on incoming channel open request
if (isset($channel) &&
isset($this->channel_status[$channel]) &&
isset($this->window_size_server_to_client[$channel])) {
$this->window_size_server_to_client[$channel]-=
strlen($response);
// resize the window, if appropriate
if ($this->window_size_server_to_client[$channel] <
0) {
// PuTTY does something more analogous to the following:
//if ($this->window_size_server_to_client[$channel] <
0x3FFFFFFF) {
$packet = pack('CNN',
NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel],
$this->window_resize);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->window_size_server_to_client[$channel]+=
$this->window_resize;
}
switch ($type) {
case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
/*
if ($client_channel == self::CHANNEL_EXEC) {
$this->_send_channel_packet($client_channel,
chr(0));
}
*/
// currently, there's only one possible value
for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
if (strlen($response) < 8) {
return false;
}
extract(unpack('Ndata_type_code/Nlength',
$this->_string_shift($response, 8)));
$data = $this->_string_shift($response,
$length);
$this->stdErrorLog.= $data;
if ($skip_extended || $this->quiet_mode) {
continue 2;
}
if ($client_channel == $channel &&
$this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) {
return $data;
}
if (!isset($this->channel_buffers[$channel])) {
$this->channel_buffers[$channel] = array();
}
$this->channel_buffers[$channel][] = $data;
continue 2;
case NET_SSH2_MSG_CHANNEL_REQUEST:
if ($this->channel_status[$channel] ==
NET_SSH2_MSG_CHANNEL_CLOSE) {
continue 2;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$value = $this->_string_shift($response,
$length);
switch ($value) {
case 'exit-signal':
$this->_string_shift($response, 1);
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$this->errors[] =
'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' .
$this->_string_shift($response, $length);
$this->_string_shift($response, 1);
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
if ($length) {
$this->errors[count($this->errors)].= "\r\n" .
$this->_string_shift($response, $length);
}
$this->_send_binary_packet(pack('CN',
NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
$this->_send_binary_packet(pack('CN',
NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
$this->channel_status[$channel] =
NET_SSH2_MSG_CHANNEL_EOF;
continue 3;
case 'exit-status':
if (strlen($response) < 5) {
return false;
}
extract(unpack('Cfalse/Nexit_status',
$this->_string_shift($response, 5)));
$this->exit_status = $exit_status;
// "The client MAY ignore these
messages."
// --
http://tools.ietf.org/html/rfc4254#section-6.10
continue 3;
default:
// "Some systems may not implement
signals, in which case they SHOULD ignore this message."
// --
http://tools.ietf.org/html/rfc4254#section-6.9
continue 3;
}
}
switch ($this->channel_status[$channel]) {
case NET_SSH2_MSG_CHANNEL_OPEN:
switch ($type) {
case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nserver_channel',
$this->_string_shift($response, 4)));
$this->server_channels[$channel] =
$server_channel;
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nwindow_size',
$this->_string_shift($response, 4)));
if ($window_size < 0) {
$window_size&= 0x7FFFFFFF;
$window_size+= 0x80000000;
}
$this->window_size_client_to_server[$channel] = $window_size;
if (strlen($response) < 4) {
return false;
}
$temp =
unpack('Npacket_size_client_to_server',
$this->_string_shift($response, 4));
$this->packet_size_client_to_server[$channel] =
$temp['packet_size_client_to_server'];
$result = $client_channel == $channel ?
true : $this->_get_channel_packet($client_channel, $skip_extended);
$this->_on_channel_open();
return $result;
//case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
default:
user_error('Unable to open
channel');
return
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
break;
case NET_SSH2_MSG_IGNORE:
switch ($type) {
case NET_SSH2_MSG_CHANNEL_SUCCESS:
//$this->channel_status[$channel] =
NET_SSH2_MSG_CHANNEL_DATA;
continue 3;
case NET_SSH2_MSG_CHANNEL_FAILURE:
user_error('Error opening
channel');
return
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
break;
case NET_SSH2_MSG_CHANNEL_REQUEST:
switch ($type) {
case NET_SSH2_MSG_CHANNEL_SUCCESS:
return true;
case NET_SSH2_MSG_CHANNEL_FAILURE:
return false;
default:
user_error('Unable to fulfill channel
request');
return
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
case NET_SSH2_MSG_CHANNEL_CLOSE:
return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true :
$this->_get_channel_packet($client_channel, $skip_extended);
}
}
// ie. $this->channel_status[$channel] ==
NET_SSH2_MSG_CHANNEL_DATA
switch ($type) {
case NET_SSH2_MSG_CHANNEL_DATA:
//if ($this->channel_status[$channel] ==
NET_SSH2_MSG_IGNORE) {
// $this->channel_status[$channel] =
NET_SSH2_MSG_CHANNEL_DATA;
//}
/*
if ($channel == self::CHANNEL_EXEC) {
// SCP requires null packets, such as this, be
sent. further, in the case of the ssh.com SSH server
// this actually seems to make things twice as
fast. more to the point, the message right after
// SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE)
won't block for as long as it would have otherwise.
// in OpenSSH it slows things down but only by a
couple thousandths of a second.
$this->_send_channel_packet($channel, chr(0));
}
*/
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($response, 4)));
$data = $this->_string_shift($response, $length);
if ($channel == self::CHANNEL_AGENT_FORWARD) {
$agent_response =
$this->agent->_forward_data($data);
if (!is_bool($agent_response)) {
$this->_send_channel_packet($channel,
$agent_response);
}
break;
}
if ($client_channel == $channel) {
return $data;
}
if (!isset($this->channel_buffers[$channel])) {
$this->channel_buffers[$channel] = array();
}
$this->channel_buffers[$channel][] = $data;
break;
case NET_SSH2_MSG_CHANNEL_CLOSE:
$this->curTimeout = 5;
if ($this->bitmap & self::MASK_SHELL) {
$this->bitmap&= ~self::MASK_SHELL;
}
if ($this->channel_status[$channel] !=
NET_SSH2_MSG_CHANNEL_EOF) {
$this->_send_binary_packet(pack('CN',
NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
}
$this->channel_status[$channel] =
NET_SSH2_MSG_CHANNEL_CLOSE;
if ($client_channel == $channel) {
return true;
}
case NET_SSH2_MSG_CHANNEL_EOF:
break;
default:
user_error('Error reading channel data');
return
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
}
}
/**
* Sends Binary Packets
*
* See '6. Binary Packet Protocol' of rfc4253 for more info.
*
* @param string $data
* @param string $logged
* @see self::_get_binary_packet()
* @return bool
* @access private
*/
function _send_binary_packet($data, $logged = null)
{
if (!is_resource($this->fsock) || feof($this->fsock)) {
$this->bitmap = 0;
user_error('Connection closed prematurely');
return false;
}
//if ($this->compress) {
// // the -4 removes the checksum:
// // http://php.net/function.gzcompress#57710
// $data = substr(gzcompress($data), 0, -4);
//}
// 4 (packet length) + 1 (padding length) + 4 (minimal padding
amount) == 9
$packet_length = strlen($data) + 9;
// round up to the nearest $this->encrypt_block_size
$packet_length+= (($this->encrypt_block_size - 1) *
$packet_length) % $this->encrypt_block_size;
// subtracting strlen($data) is obvious - subtracting 5 is
necessary because of packet_length and padding_length
$padding_length = $packet_length - strlen($data) - 5;
$padding = Random::string($padding_length);
// we subtract 4 from packet_length because the packet_length field
isn't supposed to include itself
$packet = pack('NCa*', $packet_length - 4,
$padding_length, $data . $padding);
$hmac = $this->hmac_create !== false ?
$this->hmac_create->hash(pack('Na*', $this->send_seq_no,
$packet)) : '';
$this->send_seq_no++;
if ($this->encrypt !== false) {
$packet = $this->encrypt->encrypt($packet);
}
$packet.= $hmac;
$start = microtime(true);
$result = strlen($packet) == @fputs($this->fsock, $packet);
$stop = microtime(true);
if (defined('NET_SSH2_LOGGING')) {
$current = microtime(true);
$message_number =
isset($this->message_numbers[ord($data[0])]) ?
$this->message_numbers[ord($data[0])] : 'UNKNOWN (' .
ord($data[0]) . ')';
$message_number = '-> ' . $message_number .
' (since last: ' . round($current -
$this->last_packet, 4) . ', network: ' . round($stop - $start,
4) . 's)';
$this->_append_log($message_number, isset($logged) ? $logged
: $data);
$this->last_packet = $current;
}
return $result;
}
/**
* Logs data packets
*
* Makes sure that only the last 1MB worth of packets will be logged
*
* @param string $message_number
* @param string $message
* @access private
*/
function _append_log($message_number, $message)
{
// remove the byte identifying the message type from all but the
first two messages (ie. the identification strings)
if (strlen($message_number) > 2) {
$this->_string_shift($message);
}
switch (NET_SSH2_LOGGING) {
// useful for benchmarks
case self::LOG_SIMPLE:
$this->message_number_log[] = $message_number;
break;
// the most useful log for SSH2
case self::LOG_COMPLEX:
$this->message_number_log[] = $message_number;
$this->log_size+= strlen($message);
$this->message_log[] = $message;
while ($this->log_size > self::LOG_MAX_SIZE) {
$this->log_size-=
strlen(array_shift($this->message_log));
array_shift($this->message_number_log);
}
break;
// dump the output out realtime; packets may be interspersed
with non packets,
// passwords won't be filtered out and select other
packets may not be correctly
// identified
case self::LOG_REALTIME:
switch (PHP_SAPI) {
case 'cli':
$start = $stop = "\r\n";
break;
default:
$start = '<pre>';
$stop = '</pre>';
}
echo $start . $this->_format_log(array($message),
array($message_number)) . $stop;
@flush();
@ob_flush();
break;
// basically the same thing as self::LOG_REALTIME with the
caveat that self::LOG_REALTIME_FILE
// needs to be defined and that the resultant log file will be
capped out at self::LOG_MAX_SIZE.
// the earliest part of the log file is denoted by the first
<<< START >>> and is not going to necessarily
// at the beginning of the file
case self::LOG_REALTIME_FILE:
if (!isset($this->realtime_log_file)) {
// PHP doesn't seem to like using constants in
fopen()
$filename = self::LOG_REALTIME_FILENAME;
$fp = fopen($filename, 'w');
$this->realtime_log_file = $fp;
}
if (!is_resource($this->realtime_log_file)) {
break;
}
$entry = $this->_format_log(array($message),
array($message_number));
if ($this->realtime_log_wrap) {
$temp = "<<< START
>>>\r\n";
$entry.= $temp;
fseek($this->realtime_log_file,
ftell($this->realtime_log_file) - strlen($temp));
}
$this->realtime_log_size+= strlen($entry);
if ($this->realtime_log_size > self::LOG_MAX_SIZE) {
fseek($this->realtime_log_file, 0);
$this->realtime_log_size = strlen($entry);
$this->realtime_log_wrap = true;
}
fputs($this->realtime_log_file, $entry);
}
}
/**
* Sends channel data
*
* Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
*
* @param int $client_channel
* @param string $data
* @return bool
* @access private
*/
function _send_channel_packet($client_channel, $data)
{
while (strlen($data)) {
if (!$this->window_size_client_to_server[$client_channel]) {
$this->bitmap^= self::MASK_WINDOW_ADJUST;
// using an invalid channel will let the buffers be built
up for the valid channels
$this->_get_channel_packet(-1);
$this->bitmap^= self::MASK_WINDOW_ADJUST;
}
/* The maximum amount of data allowed is determined by the
maximum
packet size for the channel, and the current window size,
whichever
is smaller.
-- http://tools.ietf.org/html/rfc4254#section-5.2 */
$max_size = min(
$this->packet_size_client_to_server[$client_channel],
$this->window_size_client_to_server[$client_channel]
);
$temp = $this->_string_shift($data, $max_size);
$packet = pack(
'CN2a*',
NET_SSH2_MSG_CHANNEL_DATA,
$this->server_channels[$client_channel],
strlen($temp),
$temp
);
$this->window_size_client_to_server[$client_channel]-=
strlen($temp);
if (!$this->_send_binary_packet($packet)) {
return false;
}
}
return true;
}
/**
* Closes and flushes a channel
*
* \phpseclib\Net\SSH2 doesn't properly close most channels. For
exec() channels are normally closed by the server
* and for SFTP channels are presumably closed when the client
disconnects. This functions is intended
* for SCP more than anything.
*
* @param int $client_channel
* @param bool $want_reply
* @return bool
* @access private
*/
function _close_channel($client_channel, $want_reply = false)
{
// see http://tools.ietf.org/html/rfc4254#section-5.3
$this->_send_binary_packet(pack('CN',
NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
if (!$want_reply) {
$this->_send_binary_packet(pack('CN',
NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
}
$this->channel_status[$client_channel] =
NET_SSH2_MSG_CHANNEL_CLOSE;
$this->curTimeout = 5;
while (!is_bool($this->_get_channel_packet($client_channel))) {
}
if ($this->is_timeout) {
$this->disconnect();
}
if ($want_reply) {
$this->_send_binary_packet(pack('CN',
NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
}
if ($this->bitmap & self::MASK_SHELL) {
$this->bitmap&= ~self::MASK_SHELL;
}
}
/**
* Disconnect
*
* @param int $reason
* @return bool
* @access private
*/
function _disconnect($reason)
{
if ($this->bitmap & self::MASK_CONNECTED) {
$data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT,
$reason, 0, '', 0, '');
$this->_send_binary_packet($data);
}
$this->bitmap = 0;
if (is_resource($this->fsock) &&
get_resource_type($this->fsock) == 'stream') {
fclose($this->fsock);
}
return false;
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @return string
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
/**
* Define Array
*
* Takes any number of arrays whose indices are integers and whose
values are strings and defines a bunch of
* named constants from it, using the value as the name of the constant
and the index as the value of the constant.
* If any of the constants that would be defined already exists, none
of the constants will be defined.
*
* @access private
*/
function _define_array()
{
$args = func_get_args();
foreach ($args as $arg) {
foreach ($arg as $key => $value) {
if (!defined($value)) {
define($value, $key);
} else {
break 2;
}
}
}
}
/**
* Returns a log of the packets that have been sent and received.
*
* Returns a string if NET_SSH2_LOGGING == self::LOG_COMPLEX, an array
if NET_SSH2_LOGGING == self::LOG_SIMPLE and false if
!defined('NET_SSH2_LOGGING')
*
* @access public
* @return array|false|string
*/
function getLog()
{
if (!defined('NET_SSH2_LOGGING')) {
return false;
}
switch (NET_SSH2_LOGGING) {
case self::LOG_SIMPLE:
return $this->message_number_log;
case self::LOG_COMPLEX:
$log = $this->_format_log($this->message_log,
$this->message_number_log);
return PHP_SAPI == 'cli' ? $log :
'<pre>' . $log . '</pre>';
default:
return false;
}
}
/**
* Formats a log for printing
*
* @param array $message_log
* @param array $message_number_log
* @access private
* @return string
*/
function _format_log($message_log, $message_number_log)
{
$output = '';
for ($i = 0; $i < count($message_log); $i++) {
$output.= $message_number_log[$i] . "\r\n";
$current_log = $message_log[$i];
$j = 0;
do {
if (strlen($current_log)) {
$output.= str_pad(dechex($j), 7, '0',
STR_PAD_LEFT) . '0 ';
}
$fragment = $this->_string_shift($current_log,
$this->log_short_width);
$hex = substr(preg_replace_callback('#.#s',
array($this, '_format_log_helper'), $fragment),
strlen($this->log_boundary));
// replace non ASCII printable characters with dots
//
http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
// also replace < with a . since < messes up the
output on web browsers
$raw = preg_replace('#[^\x20-\x7E]|<#',
'.', $fragment);
$output.= str_pad($hex, $this->log_long_width -
$this->log_short_width, ' ') . $raw . "\r\n";
$j++;
} while (strlen($current_log));
$output.= "\r\n";
}
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);
}
/**
* Helper function for agent->_on_channel_open()
*
* Used when channels are created to inform agent
* of said channel opening. Must be called after
* channel open confirmation received
*
* @access private
*/
function _on_channel_open()
{
if (isset($this->agent)) {
$this->agent->_on_channel_open($this);
}
}
/**
* Returns the first value of the intersection of two arrays or false
if
* the intersection is empty. The order is defined by the first
parameter.
*
* @param array $array1
* @param array $array2
* @return mixed False if intersection is empty, else intersected
value.
* @access private
*/
function _array_intersect_first($array1, $array2)
{
foreach ($array1 as $value) {
if (in_array($value, $array2)) {
return $value;
}
}
return false;
}
/**
* Returns all errors
*
* @return string[]
* @access public
*/
function getErrors()
{
return $this->errors;
}
/**
* Returns the last error
*
* @return string
* @access public
*/
function getLastError()
{
$count = count($this->errors);
if ($count > 0) {
return $this->errors[$count - 1];
}
}
/**
* Return the server identification.
*
* @return string
* @access public
*/
function getServerIdentification()
{
$this->_connect();
return $this->server_identifier;
}
/**
* Return a list of the key exchange algorithms the server supports.
*
* @return array
* @access public
*/
function getKexAlgorithms()
{
$this->_connect();
return $this->kex_algorithms;
}
/**
* Return a list of the host key (public key) algorithms the server
supports.
*
* @return array
* @access public
*/
function getServerHostKeyAlgorithms()
{
$this->_connect();
return $this->server_host_key_algorithms;
}
/**
* Return a list of the (symmetric key) encryption algorithms the
server supports, when receiving stuff from the client.
*
* @return array
* @access public
*/
function getEncryptionAlgorithmsClient2Server()
{
$this->_connect();
return $this->encryption_algorithms_client_to_server;
}
/**
* Return a list of the (symmetric key) encryption algorithms the
server supports, when sending stuff to the client.
*
* @return array
* @access public
*/
function getEncryptionAlgorithmsServer2Client()
{
$this->_connect();
return $this->encryption_algorithms_server_to_client;
}
/**
* Return a list of the MAC algorithms the server supports, when
receiving stuff from the client.
*
* @return array
* @access public
*/
function getMACAlgorithmsClient2Server()
{
$this->_connect();
return $this->mac_algorithms_client_to_server;
}
/**
* Return a list of the MAC algorithms the server supports, when
sending stuff to the client.
*
* @return array
* @access public
*/
function getMACAlgorithmsServer2Client()
{
$this->_connect();
return $this->mac_algorithms_server_to_client;
}
/**
* Return a list of the compression algorithms the server supports,
when receiving stuff from the client.
*
* @return array
* @access public
*/
function getCompressionAlgorithmsClient2Server()
{
$this->_connect();
return $this->compression_algorithms_client_to_server;
}
/**
* Return a list of the compression algorithms the server supports,
when sending stuff to the client.
*
* @return array
* @access public
*/
function getCompressionAlgorithmsServer2Client()
{
$this->_connect();
return $this->compression_algorithms_server_to_client;
}
/**
* Return a list of the languages the server supports, when sending
stuff to the client.
*
* @return array
* @access public
*/
function getLanguagesServer2Client()
{
$this->_connect();
return $this->languages_server_to_client;
}
/**
* Return a list of the languages the server supports, when receiving
stuff from the client.
*
* @return array
* @access public
*/
function getLanguagesClient2Server()
{
$this->_connect();
return $this->languages_client_to_server;
}
/**
* Returns a list of algorithms the server supports
*
* @return array
* @access public
*/
function getServerAlgorithms()
{
$this->_connect();
return array(
'kex' => $this->kex_algorithms,
'hostkey' => $this->server_host_key_algorithms,
'client_to_server' => array(
'crypt' =>
$this->encryption_algorithms_client_to_server,
'mac' =>
$this->mac_algorithms_client_to_server,
'comp' =>
$this->compression_algorithms_client_to_server,
'lang' => $this->languages_client_to_server
),
'server_to_client' => array(
'crypt' =>
$this->encryption_algorithms_server_to_client,
'mac' =>
$this->mac_algorithms_server_to_client,
'comp' =>
$this->compression_algorithms_server_to_client,
'lang' => $this->languages_server_to_client
)
);
}
/**
* Returns a list of KEX algorithms that phpseclib supports
*
* @return array
* @access public
*/
function getSupportedKEXAlgorithms()
{
$kex_algorithms = array(
// Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using
// Curve25519. See doc/curve25519-sha256@libssh.org.txt in the
// libssh repository for more information.
'curve25519-sha256@libssh.org',
'diffie-hellman-group-exchange-sha256',// RFC 4419
'diffie-hellman-group-exchange-sha1', // RFC 4419
// Diffie-Hellman Key Agreement (DH) using integer modulo prime
// groups.
'diffie-hellman-group14-sha1', // REQUIRED
'diffie-hellman-group1-sha1', // REQUIRED
);
if
(!function_exists('sodium_crypto_box_publickey_from_secretkey'))
{
$kex_algorithms = array_diff(
$kex_algorithms,
array('curve25519-sha256@libssh.org')
);
}
return $kex_algorithms;
}
/**
* Returns a list of host key algorithms that phpseclib supports
*
* @return array
* @access public
*/
function getSupportedHostKeyAlgorithms()
{
return array(
'rsa-sha2-256', // RFC 8332
'rsa-sha2-512', // RFC 8332
'ssh-rsa', // RECOMMENDED sign Raw RSA Key
'ssh-dss' // REQUIRED sign Raw DSS Key
);
}
/**
* Returns a list of symmetric key algorithms that phpseclib supports
*
* @return array
* @access public
*/
function getSupportedEncryptionAlgorithms()
{
$algos = array(
// from <http://tools.ietf.org/html/rfc4345#section-4>:
'arcfour256',
'arcfour128',
//'arcfour', // OPTIONAL the ARCFOUR
stream cipher with a 128-bit key
// CTR modes from
<http://tools.ietf.org/html/rfc4344#section-4>:
'aes128-ctr', // RECOMMENDED AES (Rijndael)
in SDCTR mode, with 128-bit key
'aes192-ctr', // RECOMMENDED AES with
192-bit key
'aes256-ctr', // RECOMMENDED AES with
256-bit key
'twofish128-ctr', // OPTIONAL Twofish in
SDCTR mode, with 128-bit key
'twofish192-ctr', // OPTIONAL Twofish with
192-bit key
'twofish256-ctr', // OPTIONAL Twofish with
256-bit key
'aes128-cbc', // RECOMMENDED AES with a
128-bit key
'aes192-cbc', // OPTIONAL AES with a
192-bit key
'aes256-cbc', // OPTIONAL AES in CBC
mode, with a 256-bit key
'twofish128-cbc', // OPTIONAL Twofish with a
128-bit key
'twofish192-cbc', // OPTIONAL Twofish with a
192-bit key
'twofish256-cbc',
'twofish-cbc', // OPTIONAL alias for
"twofish256-cbc"
// (this is being retained
for historical reasons)
'blowfish-ctr', // OPTIONAL Blowfish in
SDCTR mode
'blowfish-cbc', // OPTIONAL Blowfish in
CBC mode
'3des-ctr', // RECOMMENDED Three-key 3DES
in SDCTR mode
'3des-cbc', // REQUIRED three-key 3DES
in CBC mode
//'none' // OPTIONAL no
encryption; NOT RECOMMENDED
);
if ($this->crypto_engine) {
$engines = array($this->crypto_engine);
} else {
$engines = array(
Base::ENGINE_OPENSSL,
Base::ENGINE_MCRYPT,
Base::ENGINE_INTERNAL
);
}
$ciphers = array();
foreach ($engines as $engine) {
foreach ($algos as $algo) {
$obj =
$this->_encryption_algorithm_to_crypt_instance($algo);
if ($obj instanceof Rijndael) {
$obj->setKeyLength(preg_replace('#[^\d]#',
'', $algo));
}
switch ($algo) {
case 'arcfour128':
case 'arcfour256':
if ($engine != Base::ENGINE_INTERNAL) {
continue 2;
}
}
if ($obj->isValidEngine($engine)) {
$algos = array_diff($algos, array($algo));
$ciphers[] = $algo;
}
}
}
return $ciphers;
}
/**
* Returns a list of MAC algorithms that phpseclib supports
*
* @return array
* @access public
*/
function getSupportedMACAlgorithms()
{
return array(
// from <http://www.ietf.org/rfc/rfc6668.txt>:
'hmac-sha2-256',// RECOMMENDED HMAC-SHA256
(digest length = key length = 32)
'hmac-sha1-96', // RECOMMENDED first 96 bits of
HMAC-SHA1 (digest length = 12, key length = 20)
'hmac-sha1', // REQUIRED HMAC-SHA1 (digest
length = key length = 20)
'hmac-md5-96', // OPTIONAL first 96 bits of
HMAC-MD5 (digest length = 12, key length = 16)
'hmac-md5', // OPTIONAL HMAC-MD5 (digest
length = key length = 16)
//'none' // OPTIONAL no MAC; NOT
RECOMMENDED
);
}
/**
* Returns a list of compression algorithms that phpseclib supports
*
* @return array
* @access public
*/
function getSupportedCompressionAlgorithms()
{
return array(
'none' // REQUIRED no compression
//'zlib' // OPTIONAL ZLIB (LZ77) compression
);
}
/**
* Return list of negotiated algorithms
*
* Uses the same format as https://www.php.net/ssh2-methods-negotiated
*
* @return array
* @access public
*/
function getAlgorithmsNegotiated()
{
$this->_connect();
return array(
'kex' => $this->kex_algorithm,
'hostkey' => $this->signature_format,
'client_to_server' => array(
'crypt' => $this->encrypt->name,
'mac' => $this->hmac_create->name,
'comp' => 'none',
),
'server_to_client' => array(
'crypt' => $this->decrypt->name,
'mac' => $this->hmac_check->name,
'comp' => 'none',
)
);
}
/**
* Accepts an associative array with up to four parameters as described
at
* <https://www.php.net/manual/en/function.ssh2-connect.php>
*
* @param array $methods
* @access public
*/
function setPreferredAlgorithms($methods)
{
$preferred = $methods;
if (isset($preferred['kex'])) {
$preferred['kex'] = array_intersect(
$preferred['kex'],
$this->getSupportedKEXAlgorithms()
);
}
if (isset($preferred['hostkey'])) {
$preferred['hostkey'] = array_intersect(
$preferred['hostkey'],
$this->getSupportedHostKeyAlgorithms()
);
}
$keys = array('client_to_server',
'server_to_client');
foreach ($keys as $key) {
if (isset($preferred[$key])) {
$a = &$preferred[$key];
if (isset($a['crypt'])) {
$a['crypt'] = array_intersect(
$a['crypt'],
$this->getSupportedEncryptionAlgorithms()
);
}
if (isset($a['comp'])) {
$a['comp'] = array_intersect(
$a['comp'],
$this->getSupportedCompressionAlgorithms()
);
}
if (isset($a['mac'])) {
$a['mac'] = array_intersect(
$a['mac'],
$this->getSupportedMACAlgorithms()
);
}
}
}
$keys = array(
'kex',
'hostkey',
'client_to_server/crypt',
'client_to_server/comp',
'client_to_server/mac',
'server_to_client/crypt',
'server_to_client/comp',
'server_to_client/mac',
);
foreach ($keys as $key) {
$p = $preferred;
$m = $methods;
$subkeys = explode('/', $key);
foreach ($subkeys as $subkey) {
if (!isset($p[$subkey])) {
continue 2;
}
$p = $p[$subkey];
$m = $m[$subkey];
}
if (count($p) != count($m)) {
$diff = array_diff($m, $p);
$msg = count($diff) == 1 ?
' is not a supported algorithm' :
' are not supported algorithms';
user_error(implode(', ', $diff) . $msg);
return false;
}
}
$this->preferred = $preferred;
}
/**
* Returns the banner message.
*
* Quoting from the RFC, "in some jurisdictions, sending a warning
message before
* authentication may be relevant for getting legal protection."
*
* @return string
* @access public
*/
function getBannerMessage()
{
return $this->banner_message;
}
/**
* Returns the server public host key.
*
* Caching this the first time you connect to a server and checking the
result on subsequent connections
* is recommended. Returns false if the server signature is not signed
correctly with the public host key.
*
* @return mixed
* @access public
*/
function getServerPublicHostKey()
{
if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
if (!$this->_connect()) {
return false;
}
}
$signature = $this->signature;
$server_public_host_key = $this->server_public_host_key;
if (strlen($server_public_host_key) < 4) {
return false;
}
extract(unpack('Nlength',
$this->_string_shift($server_public_host_key, 4)));
$this->_string_shift($server_public_host_key, $length);
if ($this->signature_validated) {
return $this->bitmap ?
$this->signature_format . ' ' .
base64_encode($this->server_public_host_key) :
false;
}
$this->signature_validated = true;
switch ($this->signature_format) {
case 'ssh-dss':
$zero = new BigInteger();
if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($server_public_host_key, 4));
$p = new
BigInteger($this->_string_shift($server_public_host_key,
$temp['length']), -256);
if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($server_public_host_key, 4));
$q = new
BigInteger($this->_string_shift($server_public_host_key,
$temp['length']), -256);
if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($server_public_host_key, 4));
$g = new
BigInteger($this->_string_shift($server_public_host_key,
$temp['length']), -256);
if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($server_public_host_key, 4));
$y = new
BigInteger($this->_string_shift($server_public_host_key,
$temp['length']), -256);
/* The value for 'dss_signature_blob' is encoded
as a string containing
r, followed by s (which are 160-bit integers, without
lengths or
padding, unsigned, and in network byte order). */
$temp = unpack('Nlength',
$this->_string_shift($signature, 4));
if ($temp['length'] != 40) {
user_error('Invalid signature');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$r = new BigInteger($this->_string_shift($signature,
20), 256);
$s = new BigInteger($this->_string_shift($signature,
20), 256);
switch (true) {
case $r->equals($zero):
case $r->compare($q) >= 0:
case $s->equals($zero):
case $s->compare($q) >= 0:
user_error('Invalid signature');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$w = $s->modInverse($q);
$u1 = $w->multiply(new
BigInteger(sha1($this->exchange_hash), 16));
list(, $u1) = $u1->divide($q);
$u2 = $w->multiply($r);
list(, $u2) = $u2->divide($q);
$g = $g->modPow($u1, $p);
$y = $y->modPow($u2, $p);
$v = $g->multiply($y);
list(, $v) = $v->divide($p);
list(, $v) = $v->divide($q);
if (!$v->equals($r)) {
user_error('Bad server signature');
return
$this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
}
break;
case 'ssh-rsa':
case 'rsa-sha2-256':
case 'rsa-sha2-512':
if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($server_public_host_key, 4));
$e = new
BigInteger($this->_string_shift($server_public_host_key,
$temp['length']), -256);
if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($server_public_host_key, 4));
$rawN = $this->_string_shift($server_public_host_key,
$temp['length']);
$n = new BigInteger($rawN, -256);
$nLength = strlen(ltrim($rawN, "\0"));
/*
if (strlen($signature) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($signature, 4));
$signature = $this->_string_shift($signature,
$temp['length']);
$rsa = new RSA();
switch ($this->signature_format) {
case 'rsa-sha2-512':
$hash = 'sha512';
break;
case 'rsa-sha2-256':
$hash = 'sha256';
break;
//case 'ssh-rsa':
default:
$hash = 'sha1';
}
$rsa->setHash($hash);
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
$rsa->loadKey(array('e' => $e,
'n' => $n), RSA::PUBLIC_FORMAT_RAW);
if (!$rsa->verify($this->exchange_hash, $signature))
{
user_error('Bad server signature');
return
$this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
}
*/
if (strlen($signature) < 4) {
return false;
}
$temp = unpack('Nlength',
$this->_string_shift($signature, 4));
$s = new BigInteger($this->_string_shift($signature,
$temp['length']), 256);
// validate an RSA signature per "8.2
RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1
EMSA-PSS" in the
// following URL:
// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
// also, see SSHRSA.c (rsa2_verifysig) in PuTTy's
source.
if ($s->compare(new BigInteger()) < 0 ||
$s->compare($n->subtract(new BigInteger(1))) > 0) {
user_error('Invalid signature');
return
$this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$s = $s->modPow($e, $n);
$s = $s->toBytes();
switch ($this->signature_format) {
case 'rsa-sha2-512':
$hash = 'sha512';
break;
case 'rsa-sha2-256':
$hash = 'sha256';
break;
//case 'ssh-rsa':
default:
$hash = 'sha1';
}
$hashObj = new Hash($hash);
switch ($this->signature_format) {
case 'rsa-sha2-512':
$h = pack('N5a*', 0x00305130, 0x0D060960,
0x86480165, 0x03040203, 0x05000440,
$hashObj->hash($this->exchange_hash));
break;
case 'rsa-sha2-256':
$h = pack('N5a*', 0x00303130, 0x0D060960,
0x86480165, 0x03040201, 0x05000420,
$hashObj->hash($this->exchange_hash));
break;
//case 'ssh-rsa':
default:
$hash = 'sha1';
$h = pack('N4a*', 0x00302130, 0x0906052B,
0x0E03021A, 0x05000414, $hashObj->hash($this->exchange_hash));
}
$h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 2 -
strlen($h)) . $h;
if ($s != $h) {
user_error('Bad server signature');
return
$this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
}
break;
default:
user_error('Unsupported signature format');
return
$this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
}
return $this->signature_format . ' ' .
base64_encode($this->server_public_host_key);
}
/**
* Returns the exit status of an SSH command or false.
*
* @return false|int
* @access public
*/
function getExitStatus()
{
if (is_null($this->exit_status)) {
return false;
}
return $this->exit_status;
}
/**
* Returns the number of columns for the terminal window size.
*
* @return int
* @access public
*/
function getWindowColumns()
{
return $this->windowColumns;
}
/**
* Returns the number of rows for the terminal window size.
*
* @return int
* @access public
*/
function getWindowRows()
{
return $this->windowRows;
}
/**
* Sets the number of columns for the terminal window size.
*
* @param int $value
* @access public
*/
function setWindowColumns($value)
{
$this->windowColumns = $value;
}
/**
* Sets the number of rows for the terminal window size.
*
* @param int $value
* @access public
*/
function setWindowRows($value)
{
$this->windowRows = $value;
}
/**
* Sets the number of columns and rows for the terminal window size.
*
* @param int $columns
* @param int $rows
* @access public
*/
function setWindowSize($columns = 80, $rows = 24)
{
$this->windowColumns = $columns;
$this->windowRows = $rows;
}
/**
* Update packet types in log history
*
* @param string $old
* @param string $new
* @access private
*/
function _updateLogHistory($old, $new)
{
if (defined('NET_SSH2_LOGGING') &&
NET_SSH2_LOGGING == self::LOG_COMPLEX) {
$this->message_number_log[count($this->message_number_log) - 1] =
str_replace(
$old,
$new,
$this->message_number_log[count($this->message_number_log) - 1]
);
}
}
/**
* Return the list of authentication methods that may productively
continue authentication.
*
* @see https://tools.ietf.org/html/rfc4252#section-5.1
* @return array|null
*/
public function getAuthMethodsToContinue()
{
return $this->auth_methods_to_continue;
}
}
phpseclib/phpseclib/openssl.cnf000064400000000150151161207740012641
0ustar00# minimalist openssl.cnf file for use with phpseclib
HOME = .
RANDFILE = $ENV::HOME/.rnd
[ v3_ca ]
phpseclib/phpseclib/System/SSH/Agent/Identity.php000064400000014136151161207740015760
0ustar00<?php
/**
* Pure-PHP ssh-agent client.
*
* PHP version 5
*
* @category System
* @package SSH\Agent
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2009 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
*/
namespace phpseclib\System\SSH\Agent;
use phpseclib\System\SSH\Agent;
/**
* Pure-PHP ssh-agent client identity object
*
* Instantiation should only be performed by \phpseclib\System\SSH\Agent
class.
* This could be thought of as implementing an interface that
phpseclib\Crypt\RSA
* implements. ie. maybe a Net_SSH_Auth_PublicKey interface or something.
* The methods in this interface would be getPublicKey and sign since those
are the
* methods phpseclib looks for to perform public key authentication.
*
* @package SSH\Agent
* @author Jim Wigginton <terrafrost@php.net>
* @access internal
*/
class Identity
{
/**@+
* Signature Flags
*
* See
https://tools.ietf.org/html/draft-miller-ssh-agent-00#section-5.3
*
* @access private
*/
const SSH_AGENT_RSA2_256 = 2;
const SSH_AGENT_RSA2_512 = 4;
/**#@-*/
/**
* Key Object
*
* @var \phpseclib\Crypt\RSA
* @access private
* @see self::getPublicKey()
*/
var $key;
/**
* Key Blob
*
* @var string
* @access private
* @see self::sign()
*/
var $key_blob;
/**
* Socket Resource
*
* @var resource
* @access private
* @see self::sign()
*/
var $fsock;
/**
* Signature flags
*
* @var int
* @access private
* @see self::sign()
* @see self::setHash()
*/
var $flags = 0;
/**
* Default Constructor.
*
* @param resource $fsock
* @return \phpseclib\System\SSH\Agent\Identity
* @access private
*/
function __construct($fsock)
{
$this->fsock = $fsock;
}
/**
* Set Public Key
*
* Called by \phpseclib\System\SSH\Agent::requestIdentities()
*
* @param \phpseclib\Crypt\RSA $key
* @access private
*/
function setPublicKey($key)
{
$this->key = $key;
$this->key->setPublicKey();
}
/**
* Set Public Key
*
* Called by \phpseclib\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 int $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
\phpseclib\Crypt\RSA::SIGNATURE_PKCS1
*
* @param int $mode
* @access public
*/
function setSignatureMode($mode)
{
}
/**
* Set Hash
*
* ssh-agent doesn't support using hashes for RSA other than SHA1
*
* @param string $hash
* @access public
*/
function setHash($hash)
{
$this->flags = 0;
switch ($hash) {
case 'sha1':
break;
case 'sha256':
$this->flags = self::SSH_AGENT_RSA2_256;
break;
case 'sha512':
$this->flags = self::SSH_AGENT_RSA2_512;
break;
default:
user_error('The only supported hashes for RSA are
sha1, sha256 and sha512');
}
}
/**
* 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',
Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob),
$this->key_blob, strlen($message), $message, $this->flags);
$packet = pack('Na*', strlen($packet), $packet);
if (strlen($packet) != fputs($this->fsock, $packet)) {
user_error('Connection closed during signing');
return false;
}
$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed during signing');
return false;
}
$length = current(unpack('N', $temp));
$type = ord(fread($this->fsock, 1));
if ($type != Agent::SSH_AGENT_SIGN_RESPONSE) {
user_error('Unable to retrieve signature');
return false;
}
$signature_blob = fread($this->fsock, $length - 1);
if (strlen($signature_blob) != $length - 1) {
user_error('Connection closed during signing');
return false;
}
$length = current(unpack('N',
$this->_string_shift($signature_blob, 4)));
if ($length != strlen($signature_blob)) {
user_error('Malformed signature blob');
}
$length = current(unpack('N',
$this->_string_shift($signature_blob, 4)));
if ($length > strlen($signature_blob) + 4) {
user_error('Malformed signature blob');
}
$type = $this->_string_shift($signature_blob, $length);
$this->_string_shift($signature_blob, 4);
return $signature_blob;
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @return string
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
}
phpseclib/phpseclib/System/SSH/Agent.php000064400000023774151161207740014177
0ustar00<?php
/**
* Pure-PHP ssh-agent client.
*
* PHP version 5
*
* Here are some examples of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $agent = new \phpseclib\System\SSH\Agent();
*
* $ssh = new \phpseclib\Net\SSH2('www.domain.tld');
* if (!$ssh->login('username', $agent)) {
* exit('Login Failed');
* }
*
* echo $ssh->exec('pwd');
* echo $ssh->exec('ls -la');
* ?>
* </code>
*
* @category System
* @package SSH\Agent
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2014 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
*/
namespace phpseclib\System\SSH;
use phpseclib\Crypt\RSA;
use phpseclib\System\SSH\Agent\Identity;
/**
* Pure-PHP ssh-agent client identity factory
*
* requestIdentities() method pumps out
\phpseclib\System\SSH\Agent\Identity objects
*
* @package SSH\Agent
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class Agent
{
/**#@+
* Message numbers
*
* @access private
*/
// to request SSH1 keys you have to use
SSH_AGENTC_REQUEST_RSA_IDENTITIES (1)
const SSH_AGENTC_REQUEST_IDENTITIES = 11;
// this is the SSH2 response; the SSH1 response is
SSH_AGENT_RSA_IDENTITIES_ANSWER (2).
const SSH_AGENT_IDENTITIES_ANSWER = 12;
// the SSH1 request is SSH_AGENTC_RSA_CHALLENGE (3)
const SSH_AGENTC_SIGN_REQUEST = 13;
// the SSH1 response is SSH_AGENT_RSA_RESPONSE (4)
const SSH_AGENT_SIGN_RESPONSE = 14;
/**#@-*/
/**@+
* Agent forwarding status
*
* @access private
*/
// no forwarding requested and not active
const FORWARD_NONE = 0;
// request agent forwarding when opportune
const FORWARD_REQUEST = 1;
// forwarding has been request and is active
const FORWARD_ACTIVE = 2;
/**#@-*/
/**
* Unused
*/
const SSH_AGENT_FAILURE = 5;
/**
* Socket Resource
*
* @var resource
* @access private
*/
var $fsock;
/**
* Agent forwarding status
*
* @access private
*/
var $forward_status = self::FORWARD_NONE;
/**
* Buffer for accumulating forwarded authentication
* agent data arriving on SSH data channel destined
* for agent unix socket
*
* @access private
*/
var $socket_buffer = '';
/**
* Tracking the number of bytes we are expecting
* to arrive for the agent socket on the SSH data
* channel
*/
var $expected_bytes = 0;
/**
* Default Constructor
*
* @return \phpseclib\System\SSH\Agent
* @access public
*/
function __construct($address = null)
{
if (!$address) {
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
\phpseclib\System\SSH\Agent\Identity objects
*
* @return array
* @access public
*/
function requestIdentities()
{
if (!$this->fsock) {
return array();
}
$packet = pack('NC', 1,
self::SSH_AGENTC_REQUEST_IDENTITIES);
if (strlen($packet) != fputs($this->fsock, $packet)) {
user_error('Connection closed while requesting
identities');
return array();
}
$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed while requesting
identities');
return array();
}
$length = current(unpack('N', $temp));
$type = ord(fread($this->fsock, 1));
if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) {
user_error('Unable to request identities');
return array();
}
$identities = array();
$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed while requesting
identities');
return array();
}
$keyCount = current(unpack('N', $temp));
for ($i = 0; $i < $keyCount; $i++) {
$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed while requesting
identities');
return array();
}
$length = current(unpack('N', $temp));
$key_blob = fread($this->fsock, $length);
if (strlen($key_blob) != $length) {
user_error('Connection closed while requesting
identities');
return array();
}
$key_str = 'ssh-rsa ' . base64_encode($key_blob);
$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed while requesting
identities');
return array();
}
$length = current(unpack('N', $temp));
if ($length) {
$temp = fread($this->fsock, $length);
if (strlen($temp) != $length) {
user_error('Connection closed while requesting
identities');
return array();
}
$key_str.= ' ' . $temp;
}
$length = current(unpack('N', substr($key_blob, 0,
4)));
$key_type = substr($key_blob, 4, $length);
switch ($key_type) {
case 'ssh-rsa':
$key = new RSA();
$key->loadKey($key_str);
break;
case 'ssh-dss':
// not currently supported
break;
}
// resources are passed by reference by default
if (isset($key)) {
$identity = new Identity($this->fsock);
$identity->setPublicKey($key);
$identity->setPublicKeyBlob($key_blob);
$identities[] = $identity;
unset($key);
}
}
return $identities;
}
/**
* Signal that agent forwarding should
* be requested when a channel is opened
*
* @param Net_SSH2 $ssh
* @return bool
* @access public
*/
function startSSHForwarding($ssh)
{
if ($this->forward_status == self::FORWARD_NONE) {
$this->forward_status = self::FORWARD_REQUEST;
}
}
/**
* Request agent forwarding of remote server
*
* @param Net_SSH2 $ssh
* @return bool
* @access private
*/
function _request_forwarding($ssh)
{
$request_channel = $ssh->_get_open_channel();
if ($request_channel === false) {
return false;
}
$packet = pack(
'CNNa*C',
NET_SSH2_MSG_CHANNEL_REQUEST,
$ssh->server_channels[$request_channel],
strlen('auth-agent-req@openssh.com'),
'auth-agent-req@openssh.com',
1
);
$ssh->channel_status[$request_channel] =
NET_SSH2_MSG_CHANNEL_REQUEST;
if (!$ssh->_send_binary_packet($packet)) {
return false;
}
$response = $ssh->_get_channel_packet($request_channel);
if ($response === false) {
return false;
}
$ssh->channel_status[$request_channel] =
NET_SSH2_MSG_CHANNEL_OPEN;
$this->forward_status = self::FORWARD_ACTIVE;
return true;
}
/**
* On successful channel open
*
* This method is called upon successful channel
* open to give the SSH Agent an opportunity
* to take further action. i.e. request agent forwarding
*
* @param Net_SSH2 $ssh
* @access private
*/
function _on_channel_open($ssh)
{
if ($this->forward_status == self::FORWARD_REQUEST) {
$this->_request_forwarding($ssh);
}
}
/**
* Forward data to SSH Agent and return data reply
*
* @param string $data
* @return data from SSH Agent
* @access private
*/
function _forward_data($data)
{
if ($this->expected_bytes > 0) {
$this->socket_buffer.= $data;
$this->expected_bytes -= strlen($data);
} else {
$agent_data_bytes = current(unpack('N', $data));
$current_data_bytes = strlen($data);
$this->socket_buffer = $data;
if ($current_data_bytes != $agent_data_bytes + 4) {
$this->expected_bytes = ($agent_data_bytes + 4) -
$current_data_bytes;
return false;
}
}
if (strlen($this->socket_buffer) != fwrite($this->fsock,
$this->socket_buffer)) {
user_error('Connection closed attempting to forward data
to SSH agent');
return false;
}
$this->socket_buffer = '';
$this->expected_bytes = 0;
$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed while reading data
response');
return false;
}
$agent_reply_bytes = current(unpack('N', $temp));
$agent_reply_data = fread($this->fsock, $agent_reply_bytes);
if (strlen($agent_reply_data) != $agent_reply_bytes) {
user_error('Connection closed while reading data
response');
return false;
}
$agent_reply_data = current(unpack('a*',
$agent_reply_data));
return pack('Na*', $agent_reply_bytes,
$agent_reply_data);
}
}
phpseclib/README.md000064400000006035151161207740010004 0ustar00# phpseclib
- PHP Secure Communications Library
[](https://travis-ci.com/phpseclib/phpseclib)
## Supporting phpseclib
- [Become a backer or sponsor on
Patreon](https://www.patreon.com/phpseclib)
- [One-time donation via PayPal or
crypto-currencies](http://sourceforge.net/donate/index.php?group_id=198487)
- [Subscribe to
Tidelift](https://tidelift.com/subscription/pkg/packagist-phpseclib-phpseclib?utm_source=packagist-phpseclib-phpseclib&utm_medium=referral&utm_campaign=readme)
## Introduction
MIT-licensed pure-PHP implementations of the following:
SSH-2, SFTP, X.509, an arbitrary-precision integer arithmetic library,
Ed25519 / Ed449 / Curve25519 / Curve449, ECDSA / ECDH (with support for 66
curves), RSA (PKCS#1 v2.2 compliant), DSA / DH, DES / 3DES / RC4 / Rijndael
/ AES / Blowfish / Twofish / Salsa20 / ChaCha20, GCM / Poly1305
* [Browse Git](https://github.com/phpseclib/phpseclib)
## Documentation
* [Documentation / Manual](https://phpseclib.com/)
* [API Documentation](https://api.phpseclib.com/2.0/) (generated by Doctum)
## Branches
### master
* Development Branch
* Unstable API
* Do not use in production
### 3.0
* Long term support (LTS) release
* Major expansion of cryptographic primitives
* Minimum PHP version: 5.6.1
* PSR-4 autoloading with namespace rooted at `\phpseclib3`
* Install via Composer: `composer require phpseclib/phpseclib:~3.0`
### 2.0
* Long term support (LTS) release
* Modernized version of 1.0
* Minimum PHP version: 5.3.3
* PSR-4 autoloading with namespace rooted at `\phpseclib`
* Install via Composer: `composer require phpseclib/phpseclib:~2.0`
### 1.0
* Long term support (LTS) release
* PHP4 compatible
* Composer compatible (PSR-0 autoloading)
* Install using Composer: `composer require phpseclib/phpseclib:~1.0`
* Install using PEAR: See [phpseclib PEAR Channel
Documentation](http://phpseclib.sourceforge.net/pear.htm)
* [Download 1.0.19 as
ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.19.zip/download)
## Security contact information
To report a security vulnerability, please use the [Tidelift security
contact](https://tidelift.com/security). Tidelift will coordinate the fix
and disclosure.
## Support
Need Support?
* [Checkout Questions and Answers on Stack
Overflow](http://stackoverflow.com/questions/tagged/phpseclib)
* [Create a Support Ticket on
GitHub](https://github.com/phpseclib/phpseclib/issues/new)
* [Browse the Support
Forum](http://www.frostjedi.com/phpbb/viewforum.php?f=46) (no longer in
use)
## Contributing
1. Fork the Project
2. Ensure you have Composer installed (see [Composer Download
Instructions](https://getcomposer.org/download/))
3. Install Development Dependencies
``` sh
composer install
```
4. Create a Feature Branch
5. (Recommended) Run the Test Suite
``` sh
vendor/bin/phpunit
```
6. (Recommended) Check whether your code conforms to our Coding Standards
by running
``` sh
vendor/bin/phing -f build/build.xml sniff
```
7. Send us a Pull Request
phpseclib/phpseclib/Common/Functions/Strings.php000064400000037562151161424240016045
0ustar00<?php
/**
* Common String Functions
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Common\Functions;
use ParagonIE\ConstantTime\Base64;
use ParagonIE\ConstantTime\Base64UrlSafe;
use ParagonIE\ConstantTime\Hex;
use phpseclib3\Math\BigInteger;
use phpseclib3\Math\Common\FiniteField;
/**
* Common String Functions
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Strings
{
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @return string
*/
public static function shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
/**
* String Pop
*
* Inspired by array_pop
*
* @param string $string
* @param int $index
* @return string
*/
public static function pop(&$string, $index = 1)
{
$substr = substr($string, -$index);
$string = substr($string, 0, -$index);
return $substr;
}
/**
* Parse SSH2-style string
*
* Returns either an array or a boolean if $data is malformed.
*
* Valid characters for $format are as follows:
*
* C = byte
* b = boolean (true/false)
* N = uint32
* Q = uint64
* s = string
* i = mpint
* L = name-list
*
* uint64 is not supported.
*
* @param string $format
* @param string $data
* @return mixed
*/
public static function unpackSSH2($format, &$data)
{
$format = self::formatPack($format);
$result = [];
for ($i = 0; $i < strlen($format); $i++) {
switch ($format[$i]) {
case 'C':
case 'b':
if (!strlen($data)) {
throw new \LengthException('At least one byte
needs to be present for successful C / b decodes');
}
break;
case 'N':
case 'i':
case 's':
case 'L':
if (strlen($data) < 4) {
throw new \LengthException('At least four byte
needs to be present for successful N / i / s / L decodes');
}
break;
case 'Q':
if (strlen($data) < 8) {
throw new \LengthException('At least eight
byte needs to be present for successful N / i / s / L decodes');
}
break;
default:
throw new \InvalidArgumentException('$format
contains an invalid character');
}
switch ($format[$i]) {
case 'C':
$result[] = ord(self::shift($data));
continue 2;
case 'b':
$result[] = ord(self::shift($data)) != 0;
continue 2;
case 'N':
list(, $temp) = unpack('N',
self::shift($data, 4));
$result[] = $temp;
continue 2;
case 'Q':
// pack() added support for Q in PHP 5.6.3 and PHP 5.6
is phpseclib 3's minimum version
// so in theory we could support this BUT, "64-bit
format codes are not available for
// 32-bit versions" and phpseclib works on 32-bit
installs. on 32-bit installs
// 64-bit floats can be used to get larger numbers then
32-bit signed ints would allow
// for. sure, you're not gonna get the full
precision of 64-bit numbers but just because
// you need > 32-bit precision doesn't mean you
need the full 64-bit precision
extract(unpack('Nupper/Nlower',
self::shift($data, 8)));
$temp = $upper ? 4294967296 * $upper : 0;
$temp += $lower < 0 ? ($lower & 0x7FFFFFFFF) +
0x80000000 : $lower;
// $temp = hexdec(bin2hex(self::shift($data, 8)));
$result[] = $temp;
continue 2;
}
list(, $length) = unpack('N', self::shift($data, 4));
if (strlen($data) < $length) {
throw new \LengthException("$length bytes needed;
" . strlen($data) . ' bytes available');
}
$temp = self::shift($data, $length);
switch ($format[$i]) {
case 'i':
$result[] = new BigInteger($temp, -256);
break;
case 's':
$result[] = $temp;
break;
case 'L':
$result[] = explode(',', $temp);
}
}
return $result;
}
/**
* Create SSH2-style string
*
* @param string $format
* @param string|int|float|array|bool ...$elements
* @return string
*/
public static function packSSH2($format, ...$elements)
{
$format = self::formatPack($format);
if (strlen($format) != count($elements)) {
throw new \InvalidArgumentException('There must be as many
arguments as there are characters in the $format string');
}
$result = '';
for ($i = 0; $i < strlen($format); $i++) {
$element = $elements[$i];
switch ($format[$i]) {
case 'C':
if (!is_int($element)) {
throw new \InvalidArgumentException('Bytes
must be represented as an integer between 0 and 255, inclusive.');
}
$result .= pack('C', $element);
break;
case 'b':
if (!is_bool($element)) {
throw new \InvalidArgumentException('A boolean
parameter was expected.');
}
$result .= $element ? "\1" : "\0";
break;
case 'Q':
if (!is_int($element) && !is_float($element)) {
throw new \InvalidArgumentException('An
integer was expected.');
}
// 4294967296 == 1 << 32
$result .= pack('NN', $element / 4294967296,
$element);
break;
case 'N':
if (is_float($element)) {
$element = (int) $element;
}
if (!is_int($element)) {
throw new \InvalidArgumentException('An
integer was expected.');
}
$result .= pack('N', $element);
break;
case 's':
if (!self::is_stringable($element)) {
throw new \InvalidArgumentException('A string
was expected.');
}
$result .= pack('Na*', strlen($element),
$element);
break;
case 'i':
if (!$element instanceof BigInteger &&
!$element instanceof FiniteField\Integer) {
throw new \InvalidArgumentException('A
phpseclib3\Math\BigInteger or phpseclib3\Math\Common\FiniteField\Integer
object was expected.');
}
$element = $element->toBytes(true);
$result .= pack('Na*', strlen($element),
$element);
break;
case 'L':
if (!is_array($element)) {
throw new \InvalidArgumentException('An array
was expected.');
}
$element = implode(',', $element);
$result .= pack('Na*', strlen($element),
$element);
break;
default:
throw new \InvalidArgumentException('$format
contains an invalid character');
}
}
return $result;
}
/**
* Expand a pack string
*
* Converts C5 to CCCCC, for example.
*
* @param string $format
* @return string
*/
private static function formatPack($format)
{
$parts = preg_split('#(\d+)#', $format, -1,
PREG_SPLIT_DELIM_CAPTURE);
$format = '';
for ($i = 1; $i < count($parts); $i += 2) {
$format .= substr($parts[$i - 1], 0, -1) .
str_repeat(substr($parts[$i - 1], -1), $parts[$i]);
}
$format .= $parts[$i - 1];
return $format;
}
/**
* Convert binary data into bits
*
* bin2hex / hex2bin refer to base-256 encoded data as binary, whilst
* decbin / bindec refer to base-2 encoded data as binary. For the
purposes
* of this function, bin refers to base-256 encoded data whilst bits
refers
* to base-2 encoded data
*
* @param string $x
* @return string
*/
public static function bits2bin($x)
{
/*
// the pure-PHP approach is faster than the GMP approach
if (function_exists('gmp_export')) {
return strlen($x) ? gmp_export(gmp_init($x, 2)) : gmp_init(0);
}
*/
if (preg_match('#[^01]#', $x)) {
throw new \RuntimeException('The only valid characters are
0 and 1');
}
if (!defined('PHP_INT_MIN')) {
define('PHP_INT_MIN', ~PHP_INT_MAX);
}
$length = strlen($x);
if (!$length) {
return '';
}
$block_size = PHP_INT_SIZE << 3;
$pad = $block_size - ($length % $block_size);
if ($pad != $block_size) {
$x = str_repeat('0', $pad) . $x;
}
$parts = str_split($x, $block_size);
$str = '';
foreach ($parts as $part) {
$xor = $part[0] == '1' ? PHP_INT_MIN : 0;
$part[0] = '0';
$str .= pack(
PHP_INT_SIZE == 4 ? 'N' : 'J',
$xor ^ eval('return 0b' . $part . ';')
);
}
return ltrim($str, "\0");
}
/**
* Convert bits to binary data
*
* @param string $x
* @return string
*/
public static function bin2bits($x, $trim = true)
{
/*
// the pure-PHP approach is slower than the GMP approach BUT
// i want to the pure-PHP version to be easily unit tested as well
if (function_exists('gmp_import')) {
return gmp_strval(gmp_import($x), 2);
}
*/
$len = strlen($x);
$mod = $len % PHP_INT_SIZE;
if ($mod) {
$x = str_pad($x, $len + PHP_INT_SIZE - $mod, "\0",
STR_PAD_LEFT);
}
$bits = '';
if (PHP_INT_SIZE == 4) {
$digits = unpack('N*', $x);
foreach ($digits as $digit) {
$bits .= sprintf('%032b', $digit);
}
} else {
$digits = unpack('J*', $x);
foreach ($digits as $digit) {
$bits .= sprintf('%064b', $digit);
}
}
return $trim ? ltrim($bits, '0') : $bits;
}
/**
* Switch Endianness Bit Order
*
* @param string $x
* @return string
*/
public static function switchEndianness($x)
{
$r = '';
for ($i = strlen($x) - 1; $i >= 0; $i--) {
$b = ord($x[$i]);
if (PHP_INT_SIZE === 8) {
// 3 operations
// from
http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64BitsDiv
$r .= chr((($b * 0x0202020202) & 0x010884422010) %
1023);
} else {
// 7 operations
// from
http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits
$p1 = ($b * 0x0802) & 0x22110;
$p2 = ($b * 0x8020) & 0x88440;
$r .= chr(
(($p1 | $p2) * 0x10101) >> 16
);
}
}
return $r;
}
/**
* Increment the current string
*
* @param string $var
* @return string
*/
public static function increment_str(&$var)
{
if (function_exists('sodium_increment')) {
$var = strrev($var);
sodium_increment($var);
$var = strrev($var);
return $var;
}
for ($i = 4; $i <= strlen($var); $i += 4) {
$temp = substr($var, -$i, 4);
switch ($temp) {
case "\xFF\xFF\xFF\xFF":
$var = substr_replace($var,
"\x00\x00\x00\x00", -$i, 4);
break;
case "\x7F\xFF\xFF\xFF":
$var = substr_replace($var,
"\x80\x00\x00\x00", -$i, 4);
return $var;
default:
$temp = unpack('Nnum', $temp);
$var = substr_replace($var, pack('N',
$temp['num'] + 1), -$i, 4);
return $var;
}
}
$remainder = strlen($var) % 4;
if ($remainder == 0) {
return $var;
}
$temp = unpack('Nnum', str_pad(substr($var, 0,
$remainder), 4, "\0", STR_PAD_LEFT));
$temp = substr(pack('N', $temp['num'] + 1),
-$remainder);
$var = substr_replace($var, $temp, 0, $remainder);
return $var;
}
/**
* Find whether the type of a variable is string (or could be converted
to one)
*
* @param mixed $var
* @return bool
* @psalm-assert-if-true string|\Stringable $var
*/
public static function is_stringable($var)
{
return is_string($var) || (is_object($var) &&
method_exists($var, '__toString'));
}
/**
* Constant Time Base64-decoding
*
* ParagoneIE\ConstantTime doesn't use libsodium if it's
available so we'll do so
* ourselves. see
https://github.com/paragonie/constant_time_encoding/issues/39
*
* @param string $data
* @return string
*/
public static function base64_decode($data)
{
return function_exists('sodium_base642bin') ?
sodium_base642bin($data,
SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING, '=') :
Base64::decode($data);
}
/**
* Constant Time Base64-decoding (URL safe)
*
* @param string $data
* @return string
*/
public static function base64url_decode($data)
{
// return self::base64_decode(str_replace(['-',
'_'], ['+', '/'], $data));
return function_exists('sodium_base642bin') ?
sodium_base642bin($data,
SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING, '=') :
Base64UrlSafe::decode($data);
}
/**
* Constant Time Base64-encoding
*
* @param string $data
* @return string
*/
public static function base64_encode($data)
{
return function_exists('sodium_bin2base64') ?
sodium_bin2base64($data, SODIUM_BASE64_VARIANT_ORIGINAL) :
Base64::encode($data);
}
/**
* Constant Time Base64-encoding (URL safe)
*
* @param string $data
* @return string
*/
public static function base64url_encode($data)
{
// return str_replace(['+', '/'],
['-', '_'], self::base64_encode($data));
return function_exists('sodium_bin2base64') ?
sodium_bin2base64($data, SODIUM_BASE64_VARIANT_URLSAFE) :
Base64UrlSafe::encode($data);
}
/**
* Constant Time Hex Decoder
*
* @param string $data
* @return string
*/
public static function hex2bin($data)
{
return function_exists('sodium_hex2bin') ?
sodium_hex2bin($data) :
Hex::decode($data);
}
/**
* Constant Time Hex Encoder
*
* @param string $data
* @return string
*/
public static function bin2hex($data)
{
return function_exists('sodium_bin2hex') ?
sodium_bin2hex($data) :
Hex::encode($data);
}
}
phpseclib/phpseclib/Crypt/Common/Formats/Keys/JWK.php000064400000003265151161424240016517
0ustar00<?php
/**
* JSON Web Key (RFC7517) Handler
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\Common\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
/**
* JSON Web Key Formatted Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class JWK
{
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password
* @return array
*/
public static function load($key, $password = '')
{
if (!Strings::is_stringable($key)) {
throw new \UnexpectedValueException('Key should be a
string - not a ' . gettype($key));
}
$key = preg_replace('#\s#', '', $key); //
remove whitespace
if (PHP_VERSION_ID >= 73000) {
$key = json_decode($key, null, 512, JSON_THROW_ON_ERROR);
} else {
$key = json_decode($key);
if (!$key) {
throw new \RuntimeException('Unable to decode
JSON');
}
}
if (isset($key->kty)) {
return $key;
}
if (count($key->keys) != 1) {
throw new \RuntimeException('Although the JWK key format
supports multiple keys phpseclib does not');
}
return $key->keys[0];
}
/**
* Wrap a key appropriately
*
* @return string
*/
protected static function wrapKey(array $key, array $options)
{
return json_encode(['keys' => [$key + $options]]);
}
}
phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php000064400000017170151161424240017343
0ustar00<?php
/**
* OpenSSH Key Handler
*
* PHP version 5
*
* Place in $HOME/.ssh/authorized_keys
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\Common\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\AES;
use phpseclib3\Crypt\Random;
/**
* OpenSSH Formatted RSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class OpenSSH
{
/**
* Default comment
*
* @var string
*/
protected static $comment = 'phpseclib-generated-key';
/**
* Binary key flag
*
* @var bool
*/
protected static $binary = false;
/**
* Sets the default comment
*
* @param string $comment
*/
public static function setComment($comment)
{
self::$comment = str_replace(["\r", "\n"],
'', $comment);
}
/**
* Break a public or private key down into its constituent components
*
* $type can be either ssh-dss or ssh-rsa
*
* @param string $key
* @param string $password
* @return array
*/
public static function load($key, $password = '')
{
if (!Strings::is_stringable($key)) {
throw new \UnexpectedValueException('Key should be a
string - not a ' . gettype($key));
}
// key format is described here:
//
https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?annotate=HEAD
if (strpos($key, 'BEGIN OPENSSH PRIVATE KEY') !== false)
{
$key = preg_replace('#(?:^-.*?-[\r\n]*$)|\s#ms',
'', $key);
$key = Strings::base64_decode($key);
$magic = Strings::shift($key, 15);
if ($magic != "openssh-key-v1\0") {
throw new \RuntimeException('Expected
openssh-key-v1');
}
list($ciphername, $kdfname, $kdfoptions, $numKeys) =
Strings::unpackSSH2('sssN', $key);
if ($numKeys != 1) {
// if we wanted to support multiple keys we could update
PublicKeyLoader to preview what the # of keys
// would be; it'd then call
Common\Keys\OpenSSH.php::load() and get the paddedKey. it'd then pass
// that to the appropriate key loading parser $numKey times
or something
throw new \RuntimeException('Although the OpenSSH
private key format supports multiple keys phpseclib does not');
}
switch ($ciphername) {
case 'none':
break;
case 'aes256-ctr':
if ($kdfname != 'bcrypt') {
throw new \RuntimeException('Only the bcrypt
kdf is supported (' . $kdfname . ' encountered)');
}
list($salt, $rounds) =
Strings::unpackSSH2('sN', $kdfoptions);
$crypto = new AES('ctr');
//$crypto->setKeyLength(256);
//$crypto->disablePadding();
$crypto->setPassword($password, 'bcrypt',
$salt, $rounds, 32);
break;
default:
throw new \RuntimeException('The only supported
cipherse are: none, aes256-ctr (' . $ciphername . ' is being
used)');
}
list($publicKey, $paddedKey) =
Strings::unpackSSH2('ss', $key);
list($type) = Strings::unpackSSH2('s', $publicKey);
if (isset($crypto)) {
$paddedKey = $crypto->decrypt($paddedKey);
}
list($checkint1, $checkint2) =
Strings::unpackSSH2('NN', $paddedKey);
// any leftover bytes in $paddedKey are for padding? but they
should be sequential bytes. eg. 1, 2, 3, etc.
if ($checkint1 != $checkint2) {
throw new \RuntimeException('The two checkints do not
match');
}
self::checkType($type);
return compact('type', 'publicKey',
'paddedKey');
}
$parts = explode(' ', $key, 3);
if (!isset($parts[1])) {
$key = base64_decode($parts[0]);
$comment = false;
} else {
$asciiType = $parts[0];
self::checkType($parts[0]);
$key = base64_decode($parts[1]);
$comment = isset($parts[2]) ? $parts[2] : false;
}
if ($key === false) {
throw new \UnexpectedValueException('Key should be a
string - not a ' . gettype($key));
}
list($type) = Strings::unpackSSH2('s', $key);
self::checkType($type);
if (isset($asciiType) && $asciiType != $type) {
throw new \RuntimeException('Two different types of keys
are claimed: ' . $asciiType . ' and ' . $type);
}
if (strlen($key) <= 4) {
throw new \UnexpectedValueException('Key appears to be
malformed');
}
$publicKey = $key;
return compact('type', 'publicKey',
'comment');
}
/**
* Toggle between binary and printable keys
*
* Printable keys are what are generated by default. These are the ones
that go in
* $HOME/.ssh/authorized_key.
*
* @param bool $enabled
*/
public static function setBinaryOutput($enabled)
{
self::$binary = $enabled;
}
/**
* Checks to see if the type is valid
*
* @param string $candidate
*/
private static function checkType($candidate)
{
if (!in_array($candidate, static::$types)) {
throw new \RuntimeException("The key type ($candidate) is
not equal to: " . implode(',', static::$types));
}
}
/**
* Wrap a private key appropriately
*
* @param string $publicKey
* @param string $privateKey
* @param string $password
* @param array $options
* @return string
*/
protected static function wrapPrivateKey($publicKey, $privateKey,
$password, $options)
{
list(, $checkint) = unpack('N', Random::string(4));
$comment = isset($options['comment']) ?
$options['comment'] : self::$comment;
$paddedKey = Strings::packSSH2('NN', $checkint,
$checkint) .
$privateKey .
Strings::packSSH2('s', $comment);
$usesEncryption = !empty($password) &&
is_string($password);
/*
from http://tools.ietf.org/html/rfc4253#section-6 :
Note that the length of the concatenation of
'packet_length',
'padding_length', 'payload', and
'random padding' MUST be a multiple
of the cipher block size or 8, whichever is larger.
*/
$blockSize = $usesEncryption ? 16 : 8;
$paddingLength = (($blockSize - 1) * strlen($paddedKey)) %
$blockSize;
for ($i = 1; $i <= $paddingLength; $i++) {
$paddedKey .= chr($i);
}
if (!$usesEncryption) {
$key = Strings::packSSH2('sssNss', 'none',
'none', '', 1, $publicKey, $paddedKey);
} else {
$rounds = isset($options['rounds']) ?
$options['rounds'] : 16;
$salt = Random::string(16);
$kdfoptions = Strings::packSSH2('sN', $salt,
$rounds);
$crypto = new AES('ctr');
$crypto->setPassword($password, 'bcrypt', $salt,
$rounds, 32);
$paddedKey = $crypto->encrypt($paddedKey);
$key = Strings::packSSH2('sssNss',
'aes256-ctr', 'bcrypt', $kdfoptions, 1, $publicKey,
$paddedKey);
}
$key = "openssh-key-v1\0$key";
return "-----BEGIN OPENSSH PRIVATE KEY-----\n" .
chunk_split(Strings::base64_encode($key), 70,
"\n") .
"-----END OPENSSH PRIVATE KEY-----\n";
}
}
phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS.php000064400000002541151161424240016620
0ustar00<?php
/**
* PKCS Formatted Key Handler
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\Common\Formats\Keys;
/**
* PKCS1 Formatted Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PKCS
{
/**
* Auto-detect the format
*/
const MODE_ANY = 0;
/**
* Require base64-encoded PEM's be supplied
*/
const MODE_PEM = 1;
/**
* Require raw DER's be supplied
*/
const MODE_DER = 2;
/**#@-*/
/**
* Is the key a base-64 encoded PEM, DER or should it be auto-detected?
*
* @var int
*/
protected static $format = self::MODE_ANY;
/**
* Require base64-encoded PEM's be supplied
*
*/
public static function requirePEM()
{
self::$format = self::MODE_PEM;
}
/**
* Require raw DER's be supplied
*
*/
public static function requireDER()
{
self::$format = self::MODE_DER;
}
/**
* Accept any format and auto detect the format
*
* This is the default setting
*
*/
public static function requireAny()
{
self::$format = self::MODE_ANY;
}
}
phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS1.php000064400000016434151161424240016707
0ustar00<?php
/**
* PKCS1 Formatted Key Handler
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\Common\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\AES;
use phpseclib3\Crypt\DES;
use phpseclib3\Crypt\Random;
use phpseclib3\Crypt\TripleDES;
use phpseclib3\Exception\UnsupportedAlgorithmException;
use phpseclib3\File\ASN1;
/**
* PKCS1 Formatted Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PKCS1 extends PKCS
{
/**
* Default encryption algorithm
*
* @var string
*/
private static $defaultEncryptionAlgorithm = 'AES-128-CBC';
/**
* Sets the default encryption algorithm
*
* @param string $algo
*/
public static function setEncryptionAlgorithm($algo)
{
self::$defaultEncryptionAlgorithm = $algo;
}
/**
* Returns the mode constant corresponding to the mode string
*
* @param string $mode
* @return int
* @throws \UnexpectedValueException if the block cipher mode is
unsupported
*/
private static function getEncryptionMode($mode)
{
switch ($mode) {
case 'CBC':
case 'ECB':
case 'CFB':
case 'OFB':
case 'CTR':
return $mode;
}
throw new \UnexpectedValueException('Unsupported block cipher
mode of operation');
}
/**
* Returns a cipher object corresponding to a string
*
* @param string $algo
* @return string
* @throws \UnexpectedValueException if the encryption algorithm is
unsupported
*/
private static function getEncryptionObject($algo)
{
$modes = '(CBC|ECB|CFB|OFB|CTR)';
switch (true) {
case preg_match("#^AES-(128|192|256)-$modes$#",
$algo, $matches):
$cipher = new AES(self::getEncryptionMode($matches[2]));
$cipher->setKeyLength($matches[1]);
return $cipher;
case preg_match("#^DES-EDE3-$modes$#", $algo,
$matches):
return new TripleDES(self::getEncryptionMode($matches[1]));
case preg_match("#^DES-$modes$#", $algo, $matches):
return new DES(self::getEncryptionMode($matches[1]));
default:
throw new UnsupportedAlgorithmException($algo . ' is
not a supported algorithm');
}
}
/**
* Generate a symmetric key for PKCS#1 keys
*
* @param string $password
* @param string $iv
* @param int $length
* @return string
*/
private static function generateSymmetricKey($password, $iv, $length)
{
$symkey = '';
$iv = substr($iv, 0, 8);
while (strlen($symkey) < $length) {
$symkey .= md5($symkey . $password . $iv, true);
}
return substr($symkey, 0, $length);
}
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
protected static function load($key, $password)
{
if (!Strings::is_stringable($key)) {
throw new \UnexpectedValueException('Key should be a
string - not a ' . gettype($key));
}
/* 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
protect private keys, however, that's not what OpenSSL*
does. OpenSSL protects private keys by adding
two new "fields" to the key - DEK-Info and Proc-Type.
These fields are discussed here:
http://tools.ietf.org/html/rfc1421#section-4.6.1.1
http://tools.ietf.org/html/rfc1421#section-4.6.1.3
DES-EDE3-CBC as an algorithm, however, is not discussed
anywhere, near as I can tell.
DES-CBC and DES-EDE are discussed in RFC1423, however,
DES-EDE3-CBC isn't, nor is its key derivation
function. As is, the definitive authority on this encoding
scheme isn't the IETF but rather OpenSSL's
own implementation. ie. the implementation *is* the standard
and any bugs that may exist in that
implementation are part of the standard, as well.
* OpenSSL is the de facto standard. It's utilized by
OpenSSH and other projects */
if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches))
{
$iv = Strings::hex2bin(trim($matches[2]));
// remove the Proc-Type / DEK-Info sections as they're no
longer needed
$key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m',
'', $key);
$ciphertext = ASN1::extractBER($key);
if ($ciphertext === false) {
$ciphertext = $key;
}
$crypto = self::getEncryptionObject($matches[1]);
$crypto->setKey(self::generateSymmetricKey($password, $iv,
$crypto->getKeyLength() >> 3));
$crypto->setIV($iv);
$key = $crypto->decrypt($ciphertext);
} else {
if (self::$format != self::MODE_DER) {
$decoded = ASN1::extractBER($key);
if ($decoded !== false) {
$key = $decoded;
} elseif (self::$format == self::MODE_PEM) {
throw new \UnexpectedValueException('Expected
base64-encoded PEM format but was unable to decode base64 text');
}
}
}
return $key;
}
/**
* Wrap a private key appropriately
*
* @param string $key
* @param string $type
* @param string $password
* @param array $options optional
* @return string
*/
protected static function wrapPrivateKey($key, $type, $password, array
$options = [])
{
if (empty($password) || !is_string($password)) {
return "-----BEGIN $type PRIVATE KEY-----\r\n" .
chunk_split(Strings::base64_encode($key), 64) .
"-----END $type PRIVATE KEY-----";
}
$encryptionAlgorithm =
isset($options['encryptionAlgorithm']) ?
$options['encryptionAlgorithm'] :
self::$defaultEncryptionAlgorithm;
$cipher = self::getEncryptionObject($encryptionAlgorithm);
$iv = Random::string($cipher->getBlockLength() >> 3);
$cipher->setKey(self::generateSymmetricKey($password, $iv,
$cipher->getKeyLength() >> 3));
$cipher->setIV($iv);
$iv = strtoupper(Strings::bin2hex($iv));
return "-----BEGIN $type PRIVATE KEY-----\r\n" .
"Proc-Type: 4,ENCRYPTED\r\n" .
"DEK-Info: " . $encryptionAlgorithm .
",$iv\r\n" .
"\r\n" .
chunk_split(Strings::base64_encode($cipher->encrypt($key)), 64) .
"-----END $type PRIVATE KEY-----";
}
/**
* Wrap a public key appropriately
*
* @param string $key
* @param string $type
* @return string
*/
protected static function wrapPublicKey($key, $type)
{
return "-----BEGIN $type PUBLIC KEY-----\r\n" .
chunk_split(Strings::base64_encode($key), 64) .
"-----END $type PUBLIC KEY-----";
}
}
phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS8.php000064400000066413151161424240016720
0ustar00<?php
/**
* PKCS#8 Formatted Key Handler
*
* PHP version 5
*
* Used by PHP's openssl_public_encrypt() and openssl's rsautl
(when -pubin is set)
*
* Processes keys with the following headers:
*
* -----BEGIN ENCRYPTED PRIVATE KEY-----
* -----BEGIN PRIVATE KEY-----
* -----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)
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\Common\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\AES;
use phpseclib3\Crypt\DES;
use phpseclib3\Crypt\Random;
use phpseclib3\Crypt\RC2;
use phpseclib3\Crypt\RC4;
use phpseclib3\Crypt\TripleDES;
use phpseclib3\Exception\InsufficientSetupException;
use phpseclib3\Exception\UnsupportedAlgorithmException;
use phpseclib3\File\ASN1;
use phpseclib3\File\ASN1\Maps;
/**
* PKCS#8 Formatted Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PKCS8 extends PKCS
{
/**
* Default encryption algorithm
*
* @var string
*/
private static $defaultEncryptionAlgorithm = 'id-PBES2';
/**
* Default encryption scheme
*
* Only used when defaultEncryptionAlgorithm is id-PBES2
*
* @var string
*/
private static $defaultEncryptionScheme = 'aes128-CBC-PAD';
/**
* Default PRF
*
* Only used when defaultEncryptionAlgorithm is id-PBES2
*
* @var string
*/
private static $defaultPRF = 'id-hmacWithSHA256';
/**
* Default Iteration Count
*
* @var int
*/
private static $defaultIterationCount = 2048;
/**
* OIDs loaded
*
* @var bool
*/
private static $oidsLoaded = false;
/**
* Sets the default encryption algorithm
*
* @param string $algo
*/
public static function setEncryptionAlgorithm($algo)
{
self::$defaultEncryptionAlgorithm = $algo;
}
/**
* Sets the default encryption algorithm for PBES2
*
* @param string $algo
*/
public static function setEncryptionScheme($algo)
{
self::$defaultEncryptionScheme = $algo;
}
/**
* Sets the iteration count
*
* @param int $count
*/
public static function setIterationCount($count)
{
self::$defaultIterationCount = $count;
}
/**
* Sets the PRF for PBES2
*
* @param string $algo
*/
public static function setPRF($algo)
{
self::$defaultPRF = $algo;
}
/**
* Returns a SymmetricKey object based on a PBES1 $algo
*
* @return \phpseclib3\Crypt\Common\SymmetricKey
* @param string $algo
*/
private static function getPBES1EncryptionObject($algo)
{
$algo =
preg_match('#^pbeWith(?:MD2|MD5|SHA1|SHA)And(.*?)-CBC$#', $algo,
$matches) ?
$matches[1] :
substr($algo, 13); // strlen('pbeWithSHAAnd') == 13
switch ($algo) {
case 'DES':
$cipher = new DES('cbc');
break;
case 'RC2':
$cipher = new RC2('cbc');
break;
case '3-KeyTripleDES':
$cipher = new TripleDES('cbc');
break;
case '2-KeyTripleDES':
$cipher = new TripleDES('cbc');
$cipher->setKeyLength(128);
break;
case '128BitRC2':
$cipher = new RC2('cbc');
$cipher->setKeyLength(128);
break;
case '40BitRC2':
$cipher = new RC2('cbc');
$cipher->setKeyLength(40);
break;
case '128BitRC4':
$cipher = new RC4();
$cipher->setKeyLength(128);
break;
case '40BitRC4':
$cipher = new RC4();
$cipher->setKeyLength(40);
break;
default:
throw new UnsupportedAlgorithmException("$algo is not
a supported algorithm");
}
return $cipher;
}
/**
* Returns a hash based on a PBES1 $algo
*
* @return string
* @param string $algo
*/
private static function getPBES1Hash($algo)
{
if (preg_match('#^pbeWith(MD2|MD5|SHA1|SHA)And.*?-CBC$#',
$algo, $matches)) {
return $matches[1] == 'SHA' ? 'sha1' :
$matches[1];
}
return 'sha1';
}
/**
* Returns a KDF baesd on a PBES1 $algo
*
* @return string
* @param string $algo
*/
private static function getPBES1KDF($algo)
{
switch ($algo) {
case 'pbeWithMD2AndDES-CBC':
case 'pbeWithMD2AndRC2-CBC':
case 'pbeWithMD5AndDES-CBC':
case 'pbeWithMD5AndRC2-CBC':
case 'pbeWithSHA1AndDES-CBC':
case 'pbeWithSHA1AndRC2-CBC':
return 'pbkdf1';
}
return 'pkcs12';
}
/**
* Returns a SymmetricKey object baesd on a PBES2 $algo
*
* @return SymmetricKey
* @param string $algo
*/
private static function getPBES2EncryptionObject($algo)
{
switch ($algo) {
case 'desCBC':
$cipher = new TripleDES('cbc');
break;
case 'des-EDE3-CBC':
$cipher = new TripleDES('cbc');
break;
case 'rc2CBC':
$cipher = new RC2('cbc');
// in theory this can be changed
$cipher->setKeyLength(128);
break;
case 'rc5-CBC-PAD':
throw new UnsupportedAlgorithmException('rc5-CBC-PAD
is not supported for PBES2 PKCS#8 keys');
case 'aes128-CBC-PAD':
case 'aes192-CBC-PAD':
case 'aes256-CBC-PAD':
$cipher = new AES('cbc');
$cipher->setKeyLength(substr($algo, 3, 3));
break;
default:
throw new UnsupportedAlgorithmException("$algo is not
supported");
}
return $cipher;
}
/**
* Initialize static variables
*
*/
private static function initialize_static_variables()
{
if (!isset(static::$childOIDsLoaded)) {
throw new InsufficientSetupException('This class should
not be called directly');
}
if (!static::$childOIDsLoaded) {
ASN1::loadOIDs(is_array(static::OID_NAME) ?
array_combine(static::OID_NAME, static::OID_VALUE) :
[static::OID_NAME => static::OID_VALUE]);
static::$childOIDsLoaded = true;
}
if (!self::$oidsLoaded) {
// from https://tools.ietf.org/html/rfc2898
ASN1::loadOIDs([
// PBES1 encryption schemes
'pbeWithMD2AndDES-CBC' =>
'1.2.840.113549.1.5.1',
'pbeWithMD2AndRC2-CBC' =>
'1.2.840.113549.1.5.4',
'pbeWithMD5AndDES-CBC' =>
'1.2.840.113549.1.5.3',
'pbeWithMD5AndRC2-CBC' =>
'1.2.840.113549.1.5.6',
'pbeWithSHA1AndDES-CBC' =>
'1.2.840.113549.1.5.10',
'pbeWithSHA1AndRC2-CBC' =>
'1.2.840.113549.1.5.11',
// from PKCS#12:
// https://tools.ietf.org/html/rfc7292
'pbeWithSHAAnd128BitRC4' =>
'1.2.840.113549.1.12.1.1',
'pbeWithSHAAnd40BitRC4' =>
'1.2.840.113549.1.12.1.2',
'pbeWithSHAAnd3-KeyTripleDES-CBC' =>
'1.2.840.113549.1.12.1.3',
'pbeWithSHAAnd2-KeyTripleDES-CBC' =>
'1.2.840.113549.1.12.1.4',
'pbeWithSHAAnd128BitRC2-CBC' =>
'1.2.840.113549.1.12.1.5',
'pbeWithSHAAnd40BitRC2-CBC' =>
'1.2.840.113549.1.12.1.6',
'id-PBKDF2' =>
'1.2.840.113549.1.5.12',
'id-PBES2' =>
'1.2.840.113549.1.5.13',
'id-PBMAC1' =>
'1.2.840.113549.1.5.14',
// from PKCS#5 v2.1:
//
http://www.rsa.com/rsalabs/pkcs/files/h11302-wp-pkcs5v2-1-password-based-cryptography-standard.pdf
'id-hmacWithSHA1' =>
'1.2.840.113549.2.7',
'id-hmacWithSHA224' =>
'1.2.840.113549.2.8',
'id-hmacWithSHA256' =>
'1.2.840.113549.2.9',
'id-hmacWithSHA384' =>
'1.2.840.113549.2.10',
'id-hmacWithSHA512' =>
'1.2.840.113549.2.11',
'id-hmacWithSHA512-224' =>
'1.2.840.113549.2.12',
'id-hmacWithSHA512-256' =>
'1.2.840.113549.2.13',
'desCBC' => '1.3.14.3.2.7',
'des-EDE3-CBC' =>
'1.2.840.113549.3.7',
'rc2CBC' => '1.2.840.113549.3.2',
'rc5-CBC-PAD' =>
'1.2.840.113549.3.9',
'aes128-CBC-PAD' =>
'2.16.840.1.101.3.4.1.2',
'aes192-CBC-PAD' =>
'2.16.840.1.101.3.4.1.22',
'aes256-CBC-PAD' =>
'2.16.840.1.101.3.4.1.42'
]);
self::$oidsLoaded = true;
}
}
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
protected static function load($key, $password = '')
{
if (!Strings::is_stringable($key)) {
throw new \UnexpectedValueException('Key should be a
string - not a ' . gettype($key));
}
$isPublic = strpos($key, 'PUBLIC') !== false;
$isPrivate = strpos($key, 'PRIVATE') !== false;
$decoded = self::preParse($key);
$meta = [];
$decrypted = ASN1::asn1map($decoded[0],
Maps\EncryptedPrivateKeyInfo::MAP);
if (strlen($password) && is_array($decrypted)) {
$algorithm =
$decrypted['encryptionAlgorithm']['algorithm'];
switch ($algorithm) {
// PBES1
case 'pbeWithMD2AndDES-CBC':
case 'pbeWithMD2AndRC2-CBC':
case 'pbeWithMD5AndDES-CBC':
case 'pbeWithMD5AndRC2-CBC':
case 'pbeWithSHA1AndDES-CBC':
case 'pbeWithSHA1AndRC2-CBC':
case 'pbeWithSHAAnd3-KeyTripleDES-CBC':
case 'pbeWithSHAAnd2-KeyTripleDES-CBC':
case 'pbeWithSHAAnd128BitRC2-CBC':
case 'pbeWithSHAAnd40BitRC2-CBC':
case 'pbeWithSHAAnd128BitRC4':
case 'pbeWithSHAAnd40BitRC4':
$cipher = self::getPBES1EncryptionObject($algorithm);
$hash = self::getPBES1Hash($algorithm);
$kdf = self::getPBES1KDF($algorithm);
$meta['meta']['algorithm'] =
$algorithm;
$temp =
ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']);
if (!$temp) {
throw new \RuntimeException('Unable to decode
BER');
}
extract(ASN1::asn1map($temp[0],
Maps\PBEParameter::MAP));
$iterationCount = (int) $iterationCount->toString();
$cipher->setPassword($password, $kdf, $hash, $salt,
$iterationCount);
$key =
$cipher->decrypt($decrypted['encryptedData']);
$decoded = ASN1::decodeBER($key);
if (!$decoded) {
throw new \RuntimeException('Unable to decode
BER 2');
}
break;
case 'id-PBES2':
$meta['meta']['algorithm'] =
$algorithm;
$temp =
ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']);
if (!$temp) {
throw new \RuntimeException('Unable to decode
BER');
}
$temp = ASN1::asn1map($temp[0], Maps\PBES2params::MAP);
extract($temp);
$cipher =
self::getPBES2EncryptionObject($encryptionScheme['algorithm']);
$meta['meta']['cipher'] =
$encryptionScheme['algorithm'];
$temp =
ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']);
if (!$temp) {
throw new \RuntimeException('Unable to decode
BER');
}
$temp = ASN1::asn1map($temp[0], Maps\PBES2params::MAP);
extract($temp);
if (!$cipher instanceof RC2) {
$cipher->setIV($encryptionScheme['parameters']['octetString']);
} else {
$temp =
ASN1::decodeBER($encryptionScheme['parameters']);
if (!$temp) {
throw new \RuntimeException('Unable to
decode BER');
}
extract(ASN1::asn1map($temp[0],
Maps\RC2CBCParameter::MAP));
$effectiveKeyLength = (int)
$rc2ParametersVersion->toString();
switch ($effectiveKeyLength) {
case 160:
$effectiveKeyLength = 40;
break;
case 120:
$effectiveKeyLength = 64;
break;
case 58:
$effectiveKeyLength = 128;
break;
//default: // should be >= 256
}
$cipher->setIV($iv);
$cipher->setKeyLength($effectiveKeyLength);
}
$meta['meta']['keyDerivationFunc']
= $keyDerivationFunc['algorithm'];
switch ($keyDerivationFunc['algorithm']) {
case 'id-PBKDF2':
$temp =
ASN1::decodeBER($keyDerivationFunc['parameters']);
if (!$temp) {
throw new \RuntimeException('Unable to
decode BER');
}
$prf = ['algorithm' =>
'id-hmacWithSHA1'];
$params = ASN1::asn1map($temp[0],
Maps\PBKDF2params::MAP);
extract($params);
$meta['meta']['prf'] =
$prf['algorithm'];
$hash = str_replace('-',
'/', substr($prf['algorithm'], 11));
$params = [
$password,
'pbkdf2',
$hash,
$salt,
(int) $iterationCount->toString()
];
if (isset($keyLength)) {
$params[] = (int)
$keyLength->toString();
}
$cipher->setPassword(...$params);
$key =
$cipher->decrypt($decrypted['encryptedData']);
$decoded = ASN1::decodeBER($key);
if (!$decoded) {
throw new \RuntimeException('Unable to
decode BER 3');
}
break;
default:
throw new
UnsupportedAlgorithmException('Only PBKDF2 is supported for PBES2
PKCS#8 keys');
}
break;
case 'id-PBMAC1':
//$temp =
ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']);
//$value = ASN1::asn1map($temp[0],
Maps\PBMAC1params::MAP);
// since i can't find any implementation that does
PBMAC1 it is unsupported
throw new UnsupportedAlgorithmException('Only
PBES1 and PBES2 PKCS#8 keys are supported.');
// at this point we'll assume that the key conforms to
PublicKeyInfo
}
}
$private = ASN1::asn1map($decoded[0], Maps\OneAsymmetricKey::MAP);
if (is_array($private)) {
if ($isPublic) {
throw new \UnexpectedValueException('Human readable
string claims public key but DER encoded string claims private key');
}
if
(isset($private['privateKeyAlgorithm']['parameters'])
&&
!$private['privateKeyAlgorithm']['parameters']
instanceof ASN1\Element &&
isset($decoded[0]['content'][1]['content'][1])) {
$temp =
$decoded[0]['content'][1]['content'][1];
$private['privateKeyAlgorithm']['parameters'] = new
ASN1\Element(substr($key, $temp['start'],
$temp['length']));
}
if (is_array(static::OID_NAME)) {
if
(!in_array($private['privateKeyAlgorithm']['algorithm'],
static::OID_NAME)) {
throw new
UnsupportedAlgorithmException($private['privateKeyAlgorithm']['algorithm']
. ' is not a supported key type');
}
} else {
if
($private['privateKeyAlgorithm']['algorithm'] !=
static::OID_NAME) {
throw new UnsupportedAlgorithmException('Only
' . static::OID_NAME . ' keys are supported; this is a ' .
$private['privateKeyAlgorithm']['algorithm'] . '
key');
}
}
if (isset($private['publicKey'])) {
if ($private['publicKey'][0] != "\0") {
throw new \UnexpectedValueException('The first
byte of the public key should be null - not ' .
bin2hex($private['publicKey'][0]));
}
$private['publicKey'] =
substr($private['publicKey'], 1);
}
return $private + $meta;
}
// EncryptedPrivateKeyInfo and PublicKeyInfo have largely identical
"signatures". the only difference
// is that the former has an octet string and the later has a bit
string. the first byte of a bit
// string represents the number of bits in the last byte that are
to be ignored but, currently,
// bit strings wanting a non-zero amount of bits trimmed are not
supported
$public = ASN1::asn1map($decoded[0], Maps\PublicKeyInfo::MAP);
if (is_array($public)) {
if ($isPrivate) {
throw new \UnexpectedValueException('Human readable
string claims private key but DER encoded string claims public key');
}
if ($public['publicKey'][0] != "\0") {
throw new \UnexpectedValueException('The first byte of
the public key should be null - not ' .
bin2hex($public['publicKey'][0]));
}
if (is_array(static::OID_NAME)) {
if
(!in_array($public['publicKeyAlgorithm']['algorithm'],
static::OID_NAME)) {
throw new
UnsupportedAlgorithmException($public['publicKeyAlgorithm']['algorithm']
. ' is not a supported key type');
}
} else {
if
($public['publicKeyAlgorithm']['algorithm'] !=
static::OID_NAME) {
throw new UnsupportedAlgorithmException('Only
' . static::OID_NAME . ' keys are supported; this is a ' .
$public['publicKeyAlgorithm']['algorithm'] . '
key');
}
}
if
(isset($public['publicKeyAlgorithm']['parameters'])
&& !$public['publicKeyAlgorithm']['parameters']
instanceof ASN1\Element &&
isset($decoded[0]['content'][0]['content'][1])) {
$temp =
$decoded[0]['content'][0]['content'][1];
$public['publicKeyAlgorithm']['parameters'] = new
ASN1\Element(substr($key, $temp['start'],
$temp['length']));
}
$public['publicKey'] =
substr($public['publicKey'], 1);
return $public;
}
throw new \RuntimeException('Unable to parse using either
OneAsymmetricKey or PublicKeyInfo ASN1 maps');
}
/**
* Wrap a private key appropriately
*
* @param string $key
* @param string $attr
* @param mixed $params
* @param string $password
* @param string $oid optional
* @param string $publicKey optional
* @param array $options optional
* @return string
*/
protected static function wrapPrivateKey($key, $attr, $params,
$password, $oid = null, $publicKey = '', array $options = [])
{
self::initialize_static_variables();
$key = [
'version' => 'v1',
'privateKeyAlgorithm' => [
'algorithm' => is_string(static::OID_NAME) ?
static::OID_NAME : $oid
],
'privateKey' => $key
];
if ($oid != 'id-Ed25519' && $oid !=
'id-Ed448') {
$key['privateKeyAlgorithm']['parameters'] =
$params;
}
if (!empty($attr)) {
$key['attributes'] = $attr;
}
if (!empty($publicKey)) {
$key['version'] = 'v2';
$key['publicKey'] = $publicKey;
}
$key = ASN1::encodeDER($key, Maps\OneAsymmetricKey::MAP);
if (!empty($password) && is_string($password)) {
$salt = Random::string(8);
$iterationCount = isset($options['iterationCount']) ?
$options['iterationCount'] : self::$defaultIterationCount;
$encryptionAlgorithm =
isset($options['encryptionAlgorithm']) ?
$options['encryptionAlgorithm'] :
self::$defaultEncryptionAlgorithm;
$encryptionScheme =
isset($options['encryptionScheme']) ?
$options['encryptionScheme'] : self::$defaultEncryptionScheme;
$prf = isset($options['PRF']) ?
$options['PRF'] : self::$defaultPRF;
if ($encryptionAlgorithm == 'id-PBES2') {
$crypto =
self::getPBES2EncryptionObject($encryptionScheme);
$hash = str_replace('-', '/',
substr($prf, 11));
$kdf = 'pbkdf2';
$iv = Random::string($crypto->getBlockLength() >>
3);
$PBKDF2params = [
'salt' => $salt,
'iterationCount' => $iterationCount,
'prf' => ['algorithm' =>
$prf, 'parameters' => null]
];
$PBKDF2params = ASN1::encodeDER($PBKDF2params,
Maps\PBKDF2params::MAP);
if (!$crypto instanceof RC2) {
$params = ['octetString' => $iv];
} else {
$params = [
'rc2ParametersVersion' => 58,
'iv' => $iv
];
$params = ASN1::encodeDER($params,
Maps\RC2CBCParameter::MAP);
$params = new ASN1\Element($params);
}
$params = [
'keyDerivationFunc' => [
'algorithm' => 'id-PBKDF2',
'parameters' => new
ASN1\Element($PBKDF2params)
],
'encryptionScheme' => [
'algorithm' => $encryptionScheme,
'parameters' => $params
]
];
$params = ASN1::encodeDER($params, Maps\PBES2params::MAP);
$crypto->setIV($iv);
} else {
$crypto =
self::getPBES1EncryptionObject($encryptionAlgorithm);
$hash = self::getPBES1Hash($encryptionAlgorithm);
$kdf = self::getPBES1KDF($encryptionAlgorithm);
$params = [
'salt' => $salt,
'iterationCount' => $iterationCount
];
$params = ASN1::encodeDER($params, Maps\PBEParameter::MAP);
}
$crypto->setPassword($password, $kdf, $hash, $salt,
$iterationCount);
$key = $crypto->encrypt($key);
$key = [
'encryptionAlgorithm' => [
'algorithm' => $encryptionAlgorithm,
'parameters' => new ASN1\Element($params)
],
'encryptedData' => $key
];
$key = ASN1::encodeDER($key,
Maps\EncryptedPrivateKeyInfo::MAP);
return "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
chunk_split(Strings::base64_encode($key), 64) .
"-----END ENCRYPTED PRIVATE KEY-----";
}
return "-----BEGIN PRIVATE KEY-----\r\n" .
chunk_split(Strings::base64_encode($key), 64) .
"-----END PRIVATE KEY-----";
}
/**
* Wrap a public key appropriately
*
* @param string $key
* @param mixed $params
* @param string $oid
* @return string
*/
protected static function wrapPublicKey($key, $params, $oid = null)
{
self::initialize_static_variables();
$key = [
'publicKeyAlgorithm' => [
'algorithm' => is_string(static::OID_NAME) ?
static::OID_NAME : $oid
],
'publicKey' => "\0" . $key
];
if ($oid != 'id-Ed25519' && $oid !=
'id-Ed448') {
$key['publicKeyAlgorithm']['parameters'] =
$params;
}
$key = ASN1::encodeDER($key, Maps\PublicKeyInfo::MAP);
return "-----BEGIN PUBLIC KEY-----\r\n" .
chunk_split(Strings::base64_encode($key), 64) .
"-----END PUBLIC KEY-----";
}
/**
* Perform some preliminary parsing of the key
*
* @param string $key
* @return array
*/
private static function preParse(&$key)
{
self::initialize_static_variables();
if (self::$format != self::MODE_DER) {
$decoded = ASN1::extractBER($key);
if ($decoded !== false) {
$key = $decoded;
} elseif (self::$format == self::MODE_PEM) {
throw new \UnexpectedValueException('Expected
base64-encoded PEM format but was unable to decode base64 text');
}
}
$decoded = ASN1::decodeBER($key);
if (!$decoded) {
throw new \RuntimeException('Unable to decode BER');
}
return $decoded;
}
/**
* Returns the encryption parameters used by the key
*
* @param string $key
* @return array
*/
public static function extractEncryptionAlgorithm($key)
{
if (!Strings::is_stringable($key)) {
throw new \UnexpectedValueException('Key should be a
string - not a ' . gettype($key));
}
$decoded = self::preParse($key);
$r = ASN1::asn1map($decoded[0],
ASN1\Maps\EncryptedPrivateKeyInfo::MAP);
if (!is_array($r)) {
throw new \RuntimeException('Unable to parse using
EncryptedPrivateKeyInfo map');
}
if ($r['encryptionAlgorithm']['algorithm'] ==
'id-PBES2') {
$decoded =
ASN1::decodeBER($r['encryptionAlgorithm']['parameters']->element);
if (!$decoded) {
throw new \RuntimeException('Unable to decode
BER');
}
$r['encryptionAlgorithm']['parameters'] =
ASN1::asn1map($decoded[0], ASN1\Maps\PBES2params::MAP);
$kdf =
&$r['encryptionAlgorithm']['parameters']['keyDerivationFunc'];
switch ($kdf['algorithm']) {
case 'id-PBKDF2':
$decoded =
ASN1::decodeBER($kdf['parameters']->element);
if (!$decoded) {
throw new \RuntimeException('Unable to decode
BER');
}
$kdf['parameters'] =
ASN1::asn1map($decoded[0], Maps\PBKDF2params::MAP);
}
}
return $r['encryptionAlgorithm'];
}
}
phpseclib/phpseclib/Crypt/Common/Formats/Keys/PuTTY.php000064400000032110151161424240017040
0ustar00<?php
/**
* PuTTY Formatted Key Handler
*
* See PuTTY's SSHPUBK.C and
https://tartarus.org/~simon/putty-snapshots/htmldoc/AppendixC.html
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\Common\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\AES;
use phpseclib3\Crypt\Hash;
use phpseclib3\Crypt\Random;
use phpseclib3\Exception\UnsupportedAlgorithmException;
/**
* PuTTY Formatted Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PuTTY
{
/**
* Default comment
*
* @var string
*/
private static $comment = 'phpseclib-generated-key';
/**
* Default version
*
* @var int
*/
private static $version = 2;
/**
* Sets the default comment
*
* @param string $comment
*/
public static function setComment($comment)
{
self::$comment = str_replace(["\r", "\n"],
'', $comment);
}
/**
* Sets the default version
*
* @param int $version
*/
public static function setVersion($version)
{
if ($version != 2 && $version != 3) {
throw new \RuntimeException('Only supported versions are 2
and 3');
}
self::$version = $version;
}
/**
* Generate a symmetric key for PuTTY v2 keys
*
* @param string $password
* @param int $length
* @return string
*/
private static function generateV2Key($password, $length)
{
$symkey = '';
$sequence = 0;
while (strlen($symkey) < $length) {
$temp = pack('Na*', $sequence++, $password);
$symkey .= Strings::hex2bin(sha1($temp));
}
return substr($symkey, 0, $length);
}
/**
* Generate a symmetric key for PuTTY v3 keys
*
* @param string $password
* @param string $flavour
* @param int $memory
* @param int $passes
* @param string $salt
* @return array
*/
private static function generateV3Key($password, $flavour, $memory,
$passes, $salt)
{
if (!function_exists('sodium_crypto_pwhash')) {
throw new \RuntimeException('sodium_crypto_pwhash needs to
exist for Argon2 password hasing');
}
switch ($flavour) {
case 'Argon2i':
$flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2I13;
break;
case 'Argon2id':
$flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13;
break;
default:
throw new UnsupportedAlgorithmException('Only Argon2i
and Argon2id are supported');
}
$length = 80; // keylen + ivlen + mac_keylen
$temp = sodium_crypto_pwhash($length, $password, $salt, $passes,
$memory << 10, $flavour);
$symkey = substr($temp, 0, 32);
$symiv = substr($temp, 32, 16);
$hashkey = substr($temp, -32);
return compact('symkey', 'symiv',
'hashkey');
}
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password
* @return array
*/
public static function load($key, $password)
{
if (!Strings::is_stringable($key)) {
throw new \UnexpectedValueException('Key should be a
string - not a ' . gettype($key));
}
if (strpos($key, 'BEGIN SSH2 PUBLIC KEY') !== false) {
$lines = preg_split('#[\r\n]+#', $key);
switch (true) {
case $lines[0] != '---- BEGIN SSH2 PUBLIC KEY
----':
throw new \UnexpectedValueException('Key
doesn\'t start with ---- BEGIN SSH2 PUBLIC KEY ----');
case $lines[count($lines) - 1] != '---- END SSH2
PUBLIC KEY ----':
throw new \UnexpectedValueException('Key
doesn\'t end with ---- END SSH2 PUBLIC KEY ----');
}
$lines = array_splice($lines, 1, -1);
$lines = array_map(function ($line) {
return rtrim($line, "\r\n");
}, $lines);
$data = $current = '';
$values = [];
$in_value = false;
foreach ($lines as $line) {
switch (true) {
case preg_match('#^(.*?): (.*)#', $line,
$match):
$in_value = $line[strlen($line) - 1] ==
'\\';
$current = strtolower($match[1]);
$values[$current] = $in_value ? substr($match[2],
0, -1) : $match[2];
break;
case $in_value:
$in_value = $line[strlen($line) - 1] ==
'\\';
$values[$current] .= $in_value ? substr($line, 0,
-1) : $line;
break;
default:
$data .= $line;
}
}
$components = call_user_func([static::PUBLIC_HANDLER,
'load'], $data);
if ($components === false) {
throw new \UnexpectedValueException('Unable to decode
public key');
}
$components += $values;
$components['comment'] =
str_replace(['\\\\', '\"'], ['\\',
'"'], $values['comment']);
return $components;
}
$components = [];
$key = preg_split('#\r\n|\r|\n#', trim($key));
if (Strings::shift($key[0],
strlen('PuTTY-User-Key-File-')) !=
'PuTTY-User-Key-File-') {
return false;
}
$version = (int) Strings::shift($key[0], 3); // should be either
"2: " or "3: 0" prior to int casting
if ($version != 2 && $version != 3) {
throw new \RuntimeException('Only v2 and v3 PuTTY private
keys are supported');
}
$components['type'] = $type = rtrim($key[0]);
if (!in_array($type, static::$types)) {
$error = count(static::$types) == 1 ?
'Only ' . static::$types[0] . ' keys are
supported. ' :
'';
throw new UnsupportedAlgorithmException($error . 'This is
an unsupported ' . $type . ' key');
}
$encryption = trim(preg_replace('#Encryption: (.+)#',
'$1', $key[1]));
$components['comment'] =
trim(preg_replace('#Comment: (.+)#', '$1', $key[2]));
$publicLength = trim(preg_replace('#Public-Lines:
(\d+)#', '$1', $key[3]));
$public = Strings::base64_decode(implode('',
array_map('trim', array_slice($key, 4, $publicLength))));
$source = Strings::packSSH2('ssss', $type, $encryption,
$components['comment'], $public);
extract(unpack('Nlength', Strings::shift($public, 4)));
$newtype = Strings::shift($public, $length);
if ($newtype != $type) {
throw new \RuntimeException('The binary type does not
match the human readable type field');
}
$components['public'] = $public;
switch ($version) {
case 3:
$hashkey = '';
break;
case 2:
$hashkey = 'putty-private-key-file-mac-key';
}
$offset = $publicLength + 4;
switch ($encryption) {
case 'aes256-cbc':
$crypto = new AES('cbc');
switch ($version) {
case 3:
$flavour = trim(preg_replace('#Key-Derivation:
(.*)#', '$1', $key[$offset++]));
$memory = trim(preg_replace('#Argon2-Memory:
(\d+)#', '$1', $key[$offset++]));
$passes = trim(preg_replace('#Argon2-Passes:
(\d+)#', '$1', $key[$offset++]));
$parallelism =
trim(preg_replace('#Argon2-Parallelism: (\d+)#', '$1',
$key[$offset++]));
$salt =
Strings::hex2bin(trim(preg_replace('#Argon2-Salt: ([0-9a-f]+)#',
'$1', $key[$offset++])));
extract(self::generateV3Key($password, $flavour,
$memory, $passes, $salt));
break;
case 2:
$symkey = self::generateV2Key($password, 32);
$symiv = str_repeat("\0",
$crypto->getBlockLength() >> 3);
$hashkey .= $password;
}
}
switch ($version) {
case 3:
$hash = new Hash('sha256');
$hash->setKey($hashkey);
break;
case 2:
$hash = new Hash('sha1');
$hash->setKey(sha1($hashkey, true));
}
$privateLength = trim(preg_replace('#Private-Lines:
(\d+)#', '$1', $key[$offset++]));
$private = Strings::base64_decode(implode('',
array_map('trim', array_slice($key, $offset, $privateLength))));
if ($encryption != 'none') {
$crypto->setKey($symkey);
$crypto->setIV($symiv);
$crypto->disablePadding();
$private = $crypto->decrypt($private);
}
$source .= Strings::packSSH2('s', $private);
$hmac = trim(preg_replace('#Private-MAC: (.+)#',
'$1', $key[$offset + $privateLength]));
$hmac = Strings::hex2bin($hmac);
if (!hash_equals($hash->hash($source), $hmac)) {
throw new \UnexpectedValueException('MAC validation
error');
}
$components['private'] = $private;
return $components;
}
/**
* Wrap a private key appropriately
*
* @param string $public
* @param string $private
* @param string $type
* @param string $password
* @param array $options optional
* @return string
*/
protected static function wrapPrivateKey($public, $private, $type,
$password, array $options = [])
{
$encryption = (!empty($password) || is_string($password)) ?
'aes256-cbc' : 'none';
$comment = isset($options['comment']) ?
$options['comment'] : self::$comment;
$version = isset($options['version']) ?
$options['version'] : self::$version;
$key = "PuTTY-User-Key-File-$version: $type\r\n";
$key .= "Encryption: $encryption\r\n";
$key .= "Comment: $comment\r\n";
$public = Strings::packSSH2('s', $type) . $public;
$source = Strings::packSSH2('ssss', $type, $encryption,
$comment, $public);
$public = Strings::base64_encode($public);
$key .= "Public-Lines: " . ((strlen($public) + 63)
>> 6) . "\r\n";
$key .= chunk_split($public, 64);
if (empty($password) && !is_string($password)) {
$source .= Strings::packSSH2('s', $private);
switch ($version) {
case 3:
$hash = new Hash('sha256');
$hash->setKey('');
break;
case 2:
$hash = new Hash('sha1');
$hash->setKey(sha1('putty-private-key-file-mac-key', true));
}
} else {
$private .= Random::string(16 - (strlen($private) & 15));
$source .= Strings::packSSH2('s', $private);
$crypto = new AES('cbc');
switch ($version) {
case 3:
$salt = Random::string(16);
$key .= "Key-Derivation: Argon2id\r\n";
$key .= "Argon2-Memory: 8192\r\n";
$key .= "Argon2-Passes: 13\r\n";
$key .= "Argon2-Parallelism: 1\r\n";
$key .= "Argon2-Salt: " .
Strings::bin2hex($salt) . "\r\n";
extract(self::generateV3Key($password,
'Argon2id', 8192, 13, $salt));
$hash = new Hash('sha256');
$hash->setKey($hashkey);
break;
case 2:
$symkey = self::generateV2Key($password, 32);
$symiv = str_repeat("\0",
$crypto->getBlockLength() >> 3);
$hashkey = 'putty-private-key-file-mac-key' .
$password;
$hash = new Hash('sha1');
$hash->setKey(sha1($hashkey, true));
}
$crypto->setKey($symkey);
$crypto->setIV($symiv);
$crypto->disablePadding();
$private = $crypto->encrypt($private);
$mac = $hash->hash($source);
}
$private = Strings::base64_encode($private);
$key .= 'Private-Lines: ' . ((strlen($private) + 63)
>> 6) . "\r\n";
$key .= chunk_split($private, 64);
$key .= 'Private-MAC: ' .
Strings::bin2hex($hash->hash($source)) . "\r\n";
return $key;
}
/**
* Wrap a public key appropriately
*
* This is basically the format described in RFC 4716
(https://tools.ietf.org/html/rfc4716)
*
* @param string $key
* @param string $type
* @return string
*/
protected static function wrapPublicKey($key, $type)
{
$key = pack('Na*a*', strlen($type), $type, $key);
$key = "---- BEGIN SSH2 PUBLIC KEY ----\r\n" .
'Comment: "' . str_replace(['\\',
'"'], ['\\\\', '\"'],
self::$comment) . "\"\r\n" .
chunk_split(Strings::base64_encode($key), 64) .
'---- END SSH2 PUBLIC KEY ----';
return $key;
}
}
phpseclib/phpseclib/Crypt/Common/Formats/Signature/Raw.php000064400000002413151161424240017635
0ustar00<?php
/**
* Raw Signature Handler
*
* PHP version 5
*
* Handles signatures as arrays
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\Common\Formats\Signature;
use phpseclib3\Math\BigInteger;
/**
* Raw Signature Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Raw
{
/**
* Loads a signature
*
* @param array $sig
* @return array|bool
*/
public static function load($sig)
{
switch (true) {
case !is_array($sig):
case !isset($sig['r']) ||
!isset($sig['s']):
case !$sig['r'] instanceof BigInteger:
case !$sig['s'] instanceof BigInteger:
return false;
}
return [
'r' => $sig['r'],
's' => $sig['s']
];
}
/**
* Returns a signature in the appropriate format
*
* @param \phpseclib3\Math\BigInteger $r
* @param \phpseclib3\Math\BigInteger $s
* @return string
*/
public static function save(BigInteger $r, BigInteger $s)
{
return compact('r', 's');
}
}
phpseclib/phpseclib/Crypt/Common/Traits/Fingerprint.php000064400000003256151161424240017273
0ustar00<?php
/**
* Fingerprint Trait for Public Keys
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\Common\Traits;
use phpseclib3\Crypt\Hash;
/**
* Fingerprint Trait for Private Keys
*
* @author Jim Wigginton <terrafrost@php.net>
*/
trait Fingerprint
{
/**
* Returns the public key's fingerprint
*
* The public key's fingerprint is returned, which is equivalent
to running `ssh-keygen -lf rsa.pub`. If there is
* no public key currently loaded, false is returned.
* Example output (md5):
"c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified
by RFC 4716)
*
* @param string $algorithm The hashing algorithm to be used. Valid
options are 'md5' and 'sha256'. False is returned
* for invalid values.
* @return mixed
*/
public function getFingerprint($algorithm = 'md5')
{
$type = self::validatePlugin('Keys', 'OpenSSH',
'savePublicKey');
if ($type === false) {
return false;
}
$key = $this->toString('OpenSSH', ['binary'
=> true]);
if ($key === false) {
return false;
}
switch ($algorithm) {
case 'sha256':
$hash = new Hash('sha256');
$base = base64_encode($hash->hash($key));
return substr($base, 0, strlen($base) - 1);
case 'md5':
return substr(chunk_split(md5($key), 2, ':'), 0,
-1);
default:
return false;
}
}
}
phpseclib/phpseclib/Crypt/Common/Traits/PasswordProtected.php000064400000002052151161424240020451
0ustar00<?php
/**
* Password Protected Trait for Private Keys
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\Common\Traits;
/**
* Password Protected Trait for Private Keys
*
* @author Jim Wigginton <terrafrost@php.net>
*/
trait PasswordProtected
{
/**
* Password
*
* @var string|bool
*/
private $password = false;
/**
* Sets the password
*
* Private keys can be encrypted with a password. To unset the
password, pass in the empty string or false.
* Or rather, pass in $password such that empty($password) &&
!is_string($password) is true.
*
* @see self::createKey()
* @see self::load()
* @param string|bool $password
*/
public function withPassword($password = false)
{
$new = clone $this;
$new->password = $password;
return $new;
}
}
phpseclib/phpseclib/Crypt/Common/AsymmetricKey.php000064400000035701151161424240016324
0ustar00<?php
/**
* Base Class for all asymmetric key ciphers
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\Common;
use phpseclib3\Crypt\DSA;
use phpseclib3\Crypt\Hash;
use phpseclib3\Crypt\RSA;
use phpseclib3\Exception\NoKeyLoadedException;
use phpseclib3\Exception\UnsupportedFormatException;
use phpseclib3\Math\BigInteger;
/**
* Base Class for all asymmetric cipher classes
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class AsymmetricKey
{
/**
* Precomputed Zero
*
* @var \phpseclib3\Math\BigInteger
*/
protected static $zero;
/**
* Precomputed One
*
* @var \phpseclib3\Math\BigInteger
*/
protected static $one;
/**
* Format of the loaded key
*
* @var string
*/
protected $format;
/**
* Hash function
*
* @var \phpseclib3\Crypt\Hash
*/
protected $hash;
/**
* HMAC function
*
* @var \phpseclib3\Crypt\Hash
*/
private $hmac;
/**
* Supported plugins (lower case)
*
* @see self::initialize_static_variables()
* @var array
*/
private static $plugins = [];
/**
* Invisible plugins
*
* @see self::initialize_static_variables()
* @var array
*/
private static $invisiblePlugins = [];
/**
* Available Engines
*
* @var boolean[]
*/
protected static $engines = [];
/**
* Key Comment
*
* @var null|string
*/
private $comment;
/**
* @param string $type
* @return string
*/
abstract public function toString($type, array $options = []);
/**
* The constructor
*/
protected function __construct()
{
self::initialize_static_variables();
$this->hash = new Hash('sha256');
$this->hmac = new Hash('sha256');
}
/**
* Initialize static variables
*/
protected static function initialize_static_variables()
{
if (!isset(self::$zero)) {
self::$zero = new BigInteger(0);
self::$one = new BigInteger(1);
}
self::loadPlugins('Keys');
if (static::ALGORITHM != 'RSA' &&
static::ALGORITHM != 'DH') {
self::loadPlugins('Signature');
}
}
/**
* Load the key
*
* @param string $key
* @param string $password optional
* @return
\phpseclib3\Crypt\Common\PublicKey|\phpseclib3\Crypt\Common\PrivateKey
*/
public static function load($key, $password = false)
{
self::initialize_static_variables();
$class = new \ReflectionClass(static::class);
if ($class->isFinal()) {
throw new \RuntimeException('load() should not be called
from final classes (' . static::class . ')');
}
$components = false;
foreach (self::$plugins[static::ALGORITHM]['Keys'] as
$format) {
if (isset(self::$invisiblePlugins[static::ALGORITHM])
&& in_array($format, self::$invisiblePlugins[static::ALGORITHM])) {
continue;
}
try {
$components = $format::load($key, $password);
} catch (\Exception $e) {
$components = false;
}
if ($components !== false) {
break;
}
}
if ($components === false) {
throw new NoKeyLoadedException('Unable to read key');
}
$components['format'] = $format;
$components['secret'] =
isset($components['secret']) ? $components['secret'] :
'';
$comment = isset($components['comment']) ?
$components['comment'] : null;
$new = static::onLoad($components);
$new->format = $format;
$new->comment = $comment;
return $new instanceof PrivateKey ?
$new->withPassword($password) :
$new;
}
/**
* Loads a private key
*
* @return PrivateKey
* @param string|array $key
* @param string $password optional
*/
public static function loadPrivateKey($key, $password = '')
{
$key = self::load($key, $password);
if (!$key instanceof PrivateKey) {
throw new NoKeyLoadedException('The key that was loaded
was not a private key');
}
return $key;
}
/**
* Loads a public key
*
* @return PublicKey
* @param string|array $key
*/
public static function loadPublicKey($key)
{
$key = self::load($key);
if (!$key instanceof PublicKey) {
throw new NoKeyLoadedException('The key that was loaded
was not a public key');
}
return $key;
}
/**
* Loads parameters
*
* @return AsymmetricKey
* @param string|array $key
*/
public static function loadParameters($key)
{
$key = self::load($key);
if (!$key instanceof PrivateKey && !$key instanceof
PublicKey) {
throw new NoKeyLoadedException('The key that was loaded
was not a parameter');
}
return $key;
}
/**
* Load the key, assuming a specific format
*
* @param string $type
* @param string $key
* @param string $password optional
* @return static
*/
public static function loadFormat($type, $key, $password = false)
{
self::initialize_static_variables();
$components = false;
$format = strtolower($type);
if
(isset(self::$plugins[static::ALGORITHM]['Keys'][$format])) {
$format =
self::$plugins[static::ALGORITHM]['Keys'][$format];
$components = $format::load($key, $password);
}
if ($components === false) {
throw new NoKeyLoadedException('Unable to read key');
}
$components['format'] = $format;
$components['secret'] =
isset($components['secret']) ? $components['secret'] :
'';
$new = static::onLoad($components);
$new->format = $format;
return $new instanceof PrivateKey ?
$new->withPassword($password) :
$new;
}
/**
* Loads a private key
*
* @return PrivateKey
* @param string $type
* @param string $key
* @param string $password optional
*/
public static function loadPrivateKeyFormat($type, $key, $password =
false)
{
$key = self::loadFormat($type, $key, $password);
if (!$key instanceof PrivateKey) {
throw new NoKeyLoadedException('The key that was loaded
was not a private key');
}
return $key;
}
/**
* Loads a public key
*
* @return PublicKey
* @param string $type
* @param string $key
*/
public static function loadPublicKeyFormat($type, $key)
{
$key = self::loadFormat($type, $key);
if (!$key instanceof PublicKey) {
throw new NoKeyLoadedException('The key that was loaded
was not a public key');
}
return $key;
}
/**
* Loads parameters
*
* @return AsymmetricKey
* @param string $type
* @param string|array $key
*/
public static function loadParametersFormat($type, $key)
{
$key = self::loadFormat($type, $key);
if (!$key instanceof PrivateKey && !$key instanceof
PublicKey) {
throw new NoKeyLoadedException('The key that was loaded
was not a parameter');
}
return $key;
}
/**
* Validate Plugin
*
* @param string $format
* @param string $type
* @param string $method optional
* @return mixed
*/
protected static function validatePlugin($format, $type, $method =
null)
{
$type = strtolower($type);
if (!isset(self::$plugins[static::ALGORITHM][$format][$type])) {
throw new UnsupportedFormatException("$type is not a
supported format");
}
$type = self::$plugins[static::ALGORITHM][$format][$type];
if (isset($method) && !method_exists($type, $method)) {
throw new UnsupportedFormatException("$type does not
implement $method");
}
return $type;
}
/**
* Load Plugins
*
* @param string $format
*/
private static function loadPlugins($format)
{
if (!isset(self::$plugins[static::ALGORITHM][$format])) {
self::$plugins[static::ALGORITHM][$format] = [];
foreach (new \DirectoryIterator(__DIR__ . '/../' .
static::ALGORITHM . '/Formats/' . $format . '/') as
$file) {
if ($file->getExtension() != 'php') {
continue;
}
$name = $file->getBasename('.php');
if ($name[0] == '.') {
continue;
}
$type = 'phpseclib3\Crypt\\' . static::ALGORITHM
. '\\Formats\\' . $format . '\\' . $name;
$reflect = new \ReflectionClass($type);
if ($reflect->isTrait()) {
continue;
}
self::$plugins[static::ALGORITHM][$format][strtolower($name)] = $type;
if ($reflect->hasConstant('IS_INVISIBLE')) {
self::$invisiblePlugins[static::ALGORITHM][] = $type;
}
}
}
}
/**
* Returns a list of supported formats.
*
* @return array
*/
public static function getSupportedKeyFormats()
{
self::initialize_static_variables();
return self::$plugins[static::ALGORITHM]['Keys'];
}
/**
* Add a fileformat plugin
*
* The plugin needs to either already be loaded or be auto-loadable.
* Loading a plugin whose shortname overwrite an existing shortname
will overwrite the old plugin.
*
* @see self::load()
* @param string $fullname
* @return bool
*/
public static function addFileFormat($fullname)
{
self::initialize_static_variables();
if (class_exists($fullname)) {
$meta = new \ReflectionClass($fullname);
$shortname = $meta->getShortName();
self::$plugins[static::ALGORITHM]['Keys'][strtolower($shortname)]
= $fullname;
if ($meta->hasConstant('IS_INVISIBLE')) {
self::$invisiblePlugins[static::ALGORITHM] =
strtolower($name);
}
}
}
/**
* Returns the format of the loaded key.
*
* If the key that was loaded wasn't in a valid or if the key was
auto-generated
* with RSA::createKey() then this will throw an exception.
*
* @see self::load()
* @return mixed
*/
public function getLoadedFormat()
{
if (empty($this->format)) {
throw new NoKeyLoadedException('This key was created with
createKey - it was not loaded with load. Therefore there is no "loaded
format"');
}
$meta = new \ReflectionClass($this->format);
return $meta->getShortName();
}
/**
* Returns the key's comment
*
* Not all key formats support comments. If you want to set a comment
use toString()
*
* @return null|string
*/
public function getComment()
{
return $this->comment;
}
/**
* Tests engine validity
*
*/
public static function useBestEngine()
{
static::$engines = [
'PHP' => true,
'OpenSSL' =>
extension_loaded('openssl'),
// this test can be satisfied by either of the following:
// http://php.net/manual/en/book.sodium.php
// https://github.com/paragonie/sodium_compat
'libsodium' =>
function_exists('sodium_crypto_sign_keypair')
];
return static::$engines;
}
/**
* Flag to use internal engine only (useful for unit testing)
*
*/
public static function useInternalEngine()
{
static::$engines = [
'PHP' => true,
'OpenSSL' => false,
'libsodium' => false
];
}
/**
* __toString() magic method
*
* @return string
*/
public function __toString()
{
return $this->toString('PKCS8');
}
/**
* Determines which hashing function should be used
*
* @param string $hash
*/
public function withHash($hash)
{
$new = clone $this;
$new->hash = new Hash($hash);
$new->hmac = new Hash($hash);
return $new;
}
/**
* Returns the hash algorithm currently being used
*
*/
public function getHash()
{
return clone $this->hash;
}
/**
* Compute the pseudorandom k for signature generation,
* using the process specified for deterministic DSA.
*
* @param string $h1
* @return string
*/
protected function computek($h1)
{
$v = str_repeat("\1", strlen($h1));
$k = str_repeat("\0", strlen($h1));
$x = $this->int2octets($this->x);
$h1 = $this->bits2octets($h1);
$this->hmac->setKey($k);
$k = $this->hmac->hash($v . "\0" . $x . $h1);
$this->hmac->setKey($k);
$v = $this->hmac->hash($v);
$k = $this->hmac->hash($v . "\1" . $x . $h1);
$this->hmac->setKey($k);
$v = $this->hmac->hash($v);
$qlen = $this->q->getLengthInBytes();
while (true) {
$t = '';
while (strlen($t) < $qlen) {
$v = $this->hmac->hash($v);
$t = $t . $v;
}
$k = $this->bits2int($t);
if (!$k->equals(self::$zero) &&
$k->compare($this->q) < 0) {
break;
}
$k = $this->hmac->hash($v . "\0");
$this->hmac->setKey($k);
$v = $this->hmac->hash($v);
}
return $k;
}
/**
* Integer to Octet String
*
* @param \phpseclib3\Math\BigInteger $v
* @return string
*/
private function int2octets($v)
{
$out = $v->toBytes();
$rolen = $this->q->getLengthInBytes();
if (strlen($out) < $rolen) {
return str_pad($out, $rolen, "\0", STR_PAD_LEFT);
} elseif (strlen($out) > $rolen) {
return substr($out, -$rolen);
} else {
return $out;
}
}
/**
* Bit String to Integer
*
* @param string $in
* @return \phpseclib3\Math\BigInteger
*/
protected function bits2int($in)
{
$v = new BigInteger($in, 256);
$vlen = strlen($in) << 3;
$qlen = $this->q->getLength();
if ($vlen > $qlen) {
return $v->bitwise_rightShift($vlen - $qlen);
}
return $v;
}
/**
* Bit String to Octet String
*
* @param string $in
* @return string
*/
private function bits2octets($in)
{
$z1 = $this->bits2int($in);
$z2 = $z1->subtract($this->q);
return $z2->compare(self::$zero) < 0 ?
$this->int2octets($z1) :
$this->int2octets($z2);
}
}
phpseclib/phpseclib/Crypt/Common/BlockCipher.php000064400000001026151161424240015714
0ustar00<?php
/**
* Base Class for all block ciphers
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\Common;
/**
* Base Class for all block cipher classes
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class BlockCipher extends SymmetricKey
{
}
phpseclib/phpseclib/Crypt/Common/PrivateKey.php000064400000001270151161424240015613
0ustar00<?php
/**
* PrivateKey interface
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2009 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\Common;
/**
* PrivateKey interface
*
* @author Jim Wigginton <terrafrost@php.net>
*/
interface PrivateKey
{
public function sign($message);
//public function decrypt($ciphertext);
public function getPublicKey();
public function toString($type, array $options = []);
/**
* @param string|false $password
* @return mixed
*/
public function withPassword($password = false);
}
phpseclib/phpseclib/Crypt/Common/PublicKey.php000064400000001116151161424240015416
0ustar00<?php
/**
* PublicKey interface
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2009 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\Common;
/**
* PublicKey interface
*
* @author Jim Wigginton <terrafrost@php.net>
*/
interface PublicKey
{
public function verify($message, $signature);
//public function encrypt($plaintext);
public function toString($type, array $options = []);
public function getFingerprint($algorithm);
}
phpseclib/phpseclib/Crypt/Common/StreamCipher.php000064400000002203151161424240016113
0ustar00<?php
/**
* Base Class for all stream ciphers
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\Common;
/**
* Base Class for all stream cipher classes
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class StreamCipher extends SymmetricKey
{
/**
* Block Length of the cipher
*
* Stream ciphers do not have a block size
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::block_size
* @var int
*/
protected $block_size = 0;
/**
* Default Constructor.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* @return \phpseclib3\Crypt\Common\StreamCipher
*/
public function __construct()
{
parent::__construct('stream');
}
/**
* Stream ciphers not use an IV
*
* @return bool
*/
public function usesIV()
{
return false;
}
}
phpseclib/phpseclib/Crypt/Common/SymmetricKey.php000064400000373727151161424240016200
0ustar00<?php
/**
* Base Class for all \phpseclib3\Crypt\* cipher classes
*
* PHP version 5
*
* Internally for phpseclib developers:
* If you plan to add a new cipher class, please note following rules:
*
* - The new \phpseclib3\Crypt\* cipher class should extend
\phpseclib3\Crypt\Common\SymmetricKey
*
* - Following methods are then required to be overridden/overloaded:
*
* - encryptBlock()
*
* - decryptBlock()
*
* - setupKey()
*
* - All other methods are optional to be overridden/overloaded
*
* - Look at the source code of the current ciphers how they extend
\phpseclib3\Crypt\Common\SymmetricKey
* and take one of them as a start up for the new cipher class.
*
* - Please read all the other comments/notes/hints here also for each
class var/method
*
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\Common;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Blowfish;
use phpseclib3\Crypt\Hash;
use phpseclib3\Exception\BadDecryptionException;
use phpseclib3\Exception\BadModeException;
use phpseclib3\Exception\InconsistentSetupException;
use phpseclib3\Exception\InsufficientSetupException;
use phpseclib3\Exception\UnsupportedAlgorithmException;
use phpseclib3\Math\BigInteger;
use phpseclib3\Math\BinaryField;
use phpseclib3\Math\PrimeField;
/**
* Base Class for all \phpseclib3\Crypt\* cipher classes
*
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
*/
abstract class SymmetricKey
{
/**
* 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
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
*/
const MODE_CTR = -1;
/**
* Encrypt / decrypt using the Electronic Code Book mode.
*
* @link
http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
*/
const MODE_ECB = 1;
/**
* Encrypt / decrypt using the Code Book Chaining mode.
*
* @link
http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
*/
const MODE_CBC = 2;
/**
* Encrypt / decrypt using the Cipher Feedback mode.
*
* @link
http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
*/
const MODE_CFB = 3;
/**
* Encrypt / decrypt using the Cipher Feedback mode (8bit)
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
*/
const MODE_CFB8 = 7;
/**
* Encrypt / decrypt using the Output Feedback mode (8bit)
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
*/
const MODE_OFB8 = 8;
/**
* Encrypt / decrypt using the Output Feedback mode.
*
* @link
http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
*/
const MODE_OFB = 4;
/**
* Encrypt / decrypt using Galois/Counter mode.
*
* @link https://en.wikipedia.org/wiki/Galois/Counter_Mode
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
*/
const MODE_GCM = 5;
/**
* Encrypt / decrypt using streaming mode.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
*/
const MODE_STREAM = 6;
/**
* Mode Map
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
*/
const MODE_MAP = [
'ctr' => self::MODE_CTR,
'ecb' => self::MODE_ECB,
'cbc' => self::MODE_CBC,
'cfb' => self::MODE_CFB,
'cfb8' => self::MODE_CFB8,
'ofb' => self::MODE_OFB,
'ofb8' => self::MODE_OFB8,
'gcm' => self::MODE_GCM,
'stream' => self::MODE_STREAM
];
/**
* Base value for the internal implementation $engine switch
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
*/
const ENGINE_INTERNAL = 1;
/**
* Base value for the eval() implementation $engine switch
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
*/
const ENGINE_EVAL = 2;
/**
* Base value for the mcrypt implementation $engine switch
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
*/
const ENGINE_MCRYPT = 3;
/**
* Base value for the openssl implementation $engine switch
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
*/
const ENGINE_OPENSSL = 4;
/**
* Base value for the libsodium implementation $engine switch
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
*/
const ENGINE_LIBSODIUM = 5;
/**
* Base value for the openssl / gcm implementation $engine switch
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
*/
const ENGINE_OPENSSL_GCM = 6;
/**
* Engine Reverse Map
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::getEngine()
*/
const ENGINE_MAP = [
self::ENGINE_INTERNAL => 'PHP',
self::ENGINE_EVAL => 'Eval',
self::ENGINE_MCRYPT => 'mcrypt',
self::ENGINE_OPENSSL => 'OpenSSL',
self::ENGINE_LIBSODIUM => 'libsodium',
self::ENGINE_OPENSSL_GCM => 'OpenSSL (GCM)'
];
/**
* The Encryption Mode
*
* @see self::__construct()
* @var int
*/
protected $mode;
/**
* The Block Length of the block cipher
*
* @var int
*/
protected $block_size = 16;
/**
* The Key
*
* @see self::setKey()
* @var string
*/
protected $key = false;
/**
* HMAC Key
*
* @see self::setupGCM()
* @var ?string
*/
protected $hKey = false;
/**
* The Initialization Vector
*
* @see self::setIV()
* @var string
*/
protected $iv = false;
/**
* A "sliding" Initialization Vector
*
* @see self::enableContinuousBuffer()
* @see self::clearBuffers()
* @var string
*/
protected $encryptIV;
/**
* A "sliding" Initialization Vector
*
* @see self::enableContinuousBuffer()
* @see self::clearBuffers()
* @var string
*/
protected $decryptIV;
/**
* Continuous Buffer status
*
* @see self::enableContinuousBuffer()
* @var bool
*/
protected $continuousBuffer = false;
/**
* Encryption buffer for CTR, OFB and CFB modes
*
* @see self::encrypt()
* @see self::clearBuffers()
* @var array
*/
protected $enbuffer;
/**
* Decryption buffer for CTR, OFB and CFB modes
*
* @see self::decrypt()
* @see self::clearBuffers()
* @var array
*/
protected $debuffer;
/**
* 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 self::encrypt()
* @var resource
*/
private $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 self::decrypt()
* @var resource
*/
private $demcrypt;
/**
* Does the enmcrypt resource need to be (re)initialized?
*
* @see \phpseclib3\Crypt\Twofish::setKey()
* @see \phpseclib3\Crypt\Twofish::setIV()
* @var bool
*/
private $enchanged = true;
/**
* Does the demcrypt resource need to be (re)initialized?
*
* @see \phpseclib3\Crypt\Twofish::setKey()
* @see \phpseclib3\Crypt\Twofish::setIV()
* @var bool
*/
private $dechanged = true;
/**
* mcrypt resource for CFB mode
*
* mcrypt's CFB mode, in (and only in) buffered context,
* is broken, so phpseclib implements the CFB mode by it self,
* even when the mcrypt php extension is available.
*
* In order to do the CFB-mode work (fast) phpseclib
* use a separate ECB-mode mcrypt resource.
*
* @link http://phpseclib.sourceforge.net/cfb-demo.phps
* @see self::encrypt()
* @see self::decrypt()
* @see self::setupMcrypt()
* @var resource
*/
private $ecb;
/**
* Optimizing value while CFB-encrypting
*
* Only relevant if $continuousBuffer enabled
* and $engine == self::ENGINE_MCRYPT
*
* It's faster to re-init $enmcrypt if
* $buffer bytes > $cfb_init_len than
* using the $ecb resource furthermore.
*
* This value depends of the chosen cipher
* and the time it would be needed for it's
* initialization [by mcrypt_generic_init()]
* which, typically, depends on the complexity
* on its internaly Key-expanding algorithm.
*
* @see self::encrypt()
* @var int
*/
protected $cfb_init_len = 600;
/**
* Does internal cipher state need to be (re)initialized?
*
* @see self::setKey()
* @see self::setIV()
* @see self::disableContinuousBuffer()
* @var bool
*/
protected $changed = true;
/**
* Does Eval engie need to be (re)initialized?
*
* @see self::setup()
* @var bool
*/
protected $nonIVChanged = true;
/**
* Padding status
*
* @see self::enablePadding()
* @var bool
*/
private $padding = true;
/**
* Is the mode one that is paddable?
*
* @see self::__construct()
* @var bool
*/
private $paddable = false;
/**
* Holds which crypt engine internaly should be use,
* which will be determined automatically on __construct()
*
* Currently available $engines are:
* - self::ENGINE_LIBSODIUM (very fast, php-extension: libsodium,
extension_loaded('libsodium') required)
* - self::ENGINE_OPENSSL_GCM (very fast, php-extension: openssl,
extension_loaded('openssl') required)
* - self::ENGINE_OPENSSL (very fast, php-extension: openssl,
extension_loaded('openssl') required)
* - self::ENGINE_MCRYPT (fast, php-extension: mcrypt,
extension_loaded('mcrypt') required)
* - self::ENGINE_EVAL (medium, pure php-engine, no
php-extension required)
* - self::ENGINE_INTERNAL (slower, pure php-engine, no
php-extension required)
*
* @see self::setEngine()
* @see self::encrypt()
* @see self::decrypt()
* @var int
*/
protected $engine;
/**
* Holds the preferred crypt engine
*
* @see self::setEngine()
* @see self::setPreferredEngine()
* @var int
*/
private $preferredEngine;
/**
* The mcrypt specific name of the cipher
*
* Only used if $engine == self::ENGINE_MCRYPT
*
* @link http://www.php.net/mcrypt_module_open
* @link http://www.php.net/mcrypt_list_algorithms
* @see self::setupMcrypt()
* @var string
*/
protected $cipher_name_mcrypt;
/**
* The openssl specific name of the cipher
*
* Only used if $engine == self::ENGINE_OPENSSL
*
* @link http://www.php.net/openssl-get-cipher-methods
* @var string
*/
protected $cipher_name_openssl;
/**
* The openssl specific name of the cipher in ECB mode
*
* If OpenSSL does not support the mode we're trying to use (CTR)
* it can still be emulated with ECB mode.
*
* @link http://www.php.net/openssl-get-cipher-methods
* @var string
*/
protected $cipher_name_openssl_ecb;
/**
* The default salt used by setPassword()
*
* @see self::setPassword()
* @var string
*/
private $password_default_salt = 'phpseclib/salt';
/**
* The name of the performance-optimized callback function
*
* Used by encrypt() / decrypt()
* only if $engine == self::ENGINE_INTERNAL
*
* @see self::encrypt()
* @see self::decrypt()
* @see self::setupInlineCrypt()
* @var Callback
*/
protected $inline_crypt;
/**
* If OpenSSL can be used in ECB but not in CTR we can emulate CTR
*
* @see self::openssl_ctr_process()
* @var bool
*/
private $openssl_emulate_ctr = false;
/**
* Don't truncate / null pad key
*
* @see self::clearBuffers()
* @var bool
*/
private $skip_key_adjustment = false;
/**
* Has the key length explicitly been set or should it be derived from
the key, itself?
*
* @see self::setKeyLength()
* @var bool
*/
protected $explicit_key_length = false;
/**
* Hash subkey for GHASH
*
* @see self::setupGCM()
* @see self::ghash()
* @var BinaryField\Integer
*/
private $h;
/**
* Additional authenticated data
*
* @var string
*/
protected $aad = '';
/**
* Authentication Tag produced after a round of encryption
*
* @var string
*/
protected $newtag = false;
/**
* Authentication Tag to be verified during decryption
*
* @var string
*/
protected $oldtag = false;
/**
* GCM Binary Field
*
* @see self::__construct()
* @see self::ghash()
* @var BinaryField
*/
private static $gcmField;
/**
* Poly1305 Prime Field
*
* @see self::enablePoly1305()
* @see self::poly1305()
* @var PrimeField
*/
private static $poly1305Field;
/**
* Flag for using regular vs "safe" intval
*
* @see self::initialize_static_variables()
* @var boolean
*/
protected static $use_reg_intval;
/**
* Poly1305 Key
*
* @see self::setPoly1305Key()
* @see self::poly1305()
* @var string
*/
protected $poly1305Key;
/**
* Poly1305 Flag
*
* @see self::setPoly1305Key()
* @see self::enablePoly1305()
* @var boolean
*/
protected $usePoly1305 = false;
/**
* The Original Initialization Vector
*
* GCM uses the nonce to build the IV but we want to be able to
distinguish between nonce-derived
* IV's and user-set IV's
*
* @see self::setIV()
* @var string
*/
private $origIV = false;
/**
* Nonce
*
* Only used with GCM. We could re-use setIV() but nonce's can be
of a different length and
* toggling between GCM and other modes could be more complicated if we
re-used setIV()
*
* @see self::setNonce()
* @var string
*/
protected $nonce = false;
/**
* Default Constructor.
*
* $mode could be:
*
* - ecb
*
* - cbc
*
* - ctr
*
* - cfb
*
* - cfb8
*
* - ofb
*
* - ofb8
*
* - gcm
*
* @param string $mode
* @throws BadModeException if an invalid / unsupported mode is
provided
*/
public function __construct($mode)
{
$mode = strtolower($mode);
// necessary because of 5.6 compatibility; we can't do
isset(self::MODE_MAP[$mode]) in 5.6
$map = self::MODE_MAP;
if (!isset($map[$mode])) {
throw new BadModeException('No valid mode has been
specified');
}
$mode = self::MODE_MAP[$mode];
// $mode dependent settings
switch ($mode) {
case self::MODE_ECB:
case self::MODE_CBC:
$this->paddable = true;
break;
case self::MODE_CTR:
case self::MODE_CFB:
case self::MODE_CFB8:
case self::MODE_OFB:
case self::MODE_OFB8:
case self::MODE_STREAM:
$this->paddable = false;
break;
case self::MODE_GCM:
if ($this->block_size != 16) {
throw new BadModeException('GCM is only valid for
block ciphers with a block size of 128 bits');
}
if (!isset(self::$gcmField)) {
self::$gcmField = new BinaryField(128, 7, 2, 1, 0);
}
$this->paddable = false;
break;
default:
throw new BadModeException('No valid mode has been
specified');
}
$this->mode = $mode;
static::initialize_static_variables();
}
/**
* Initialize static variables
*/
protected static function initialize_static_variables()
{
if (!isset(self::$use_reg_intval)) {
switch (true) {
// PHP_OS & "\xDF\xDF\xDF" ==
strtoupper(substr(PHP_OS, 0, 3)), but a lot faster
case (PHP_OS & "\xDF\xDF\xDF") ===
'WIN':
case !(is_string(php_uname('m')) &&
(php_uname('m') & "\xDF\xDF\xDF") ==
'ARM'):
case defined('PHP_INT_SIZE') &&
PHP_INT_SIZE == 8:
self::$use_reg_intval = true;
break;
case is_string(php_uname('m')) &&
(php_uname('m') & "\xDF\xDF\xDF") ==
'ARM':
switch (true) {
/* PHP 7.0.0 introduced a bug that affected 32-bit
ARM processors:
https://github.com/php/php-src/commit/716da71446ebbd40fa6cf2cea8a4b70f504cc3cd
altho the changelogs make no mention of it, this
bug was fixed with this commit:
https://github.com/php/php-src/commit/c1729272b17a1fe893d1a54e423d3b71470f3ee8
affected versions of PHP are: 7.0.x, 7.1.0 -
7.1.23 and 7.2.0 - 7.2.11 */
case PHP_VERSION_ID >= 70000 &&
PHP_VERSION_ID <= 70123:
case PHP_VERSION_ID >= 70200 &&
PHP_VERSION_ID <= 70211:
self::$use_reg_intval = false;
break;
default:
self::$use_reg_intval = true;
}
}
}
}
/**
* Sets the initialization vector.
*
* setIV() is not required when ecb or gcm modes are being used.
*
* {@internal Can be overwritten by a sub class, but does not have to
be}
*
* @param string $iv
* @throws \LengthException if the IV length isn't equal to the
block size
* @throws \BadMethodCallException if an IV is provided when one
shouldn't be
*/
public function setIV($iv)
{
if ($this->mode == self::MODE_ECB) {
throw new \BadMethodCallException('This mode does not
require an IV.');
}
if ($this->mode == self::MODE_GCM) {
throw new \BadMethodCallException('Use setNonce
instead');
}
if (!$this->usesIV()) {
throw new \BadMethodCallException('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 = $this->origIV = $iv;
$this->changed = true;
}
/**
* Enables Poly1305 mode.
*
* Once enabled Poly1305 cannot be disabled.
*
* @throws \BadMethodCallException if Poly1305 is enabled whilst in GCM
mode
*/
public function enablePoly1305()
{
if ($this->mode == self::MODE_GCM) {
throw new \BadMethodCallException('Poly1305 cannot be used
in GCM mode');
}
$this->usePoly1305 = true;
}
/**
* Enables Poly1305 mode.
*
* Once enabled Poly1305 cannot be disabled. If $key is not passed then
an attempt to call createPoly1305Key
* will be made.
*
* @param string $key optional
* @throws \LengthException if the key isn't long enough
* @throws \BadMethodCallException if Poly1305 is enabled whilst in GCM
mode
*/
public function setPoly1305Key($key = null)
{
if ($this->mode == self::MODE_GCM) {
throw new \BadMethodCallException('Poly1305 cannot be used
in GCM mode');
}
if (!is_string($key) || strlen($key) != 32) {
throw new \LengthException('The Poly1305 key must be 32
bytes long (256 bits)');
}
if (!isset(self::$poly1305Field)) {
// 2^130-5
self::$poly1305Field = new PrimeField(new
BigInteger('3fffffffffffffffffffffffffffffffb', 16));
}
$this->poly1305Key = $key;
$this->usePoly1305 = true;
}
/**
* Sets the nonce.
*
* setNonce() is only required when gcm is used
*
* @param string $nonce
* @throws \BadMethodCallException if an nonce is provided when one
shouldn't be
*/
public function setNonce($nonce)
{
if ($this->mode != self::MODE_GCM) {
throw new \BadMethodCallException('Nonces are only used in
GCM mode.');
}
$this->nonce = $nonce;
$this->setEngine();
}
/**
* Sets additional authenticated data
*
* setAAD() is only used by gcm or in poly1305 mode
*
* @param string $aad
* @throws \BadMethodCallException if mode isn't GCM or if
poly1305 isn't being utilized
*/
public function setAAD($aad)
{
if ($this->mode != self::MODE_GCM &&
!$this->usePoly1305) {
throw new \BadMethodCallException('Additional
authenticated data is only utilized in GCM mode or with Poly1305');
}
$this->aad = $aad;
}
/**
* Returns whether or not the algorithm uses an IV
*
* @return bool
*/
public function usesIV()
{
return $this->mode != self::MODE_GCM && $this->mode
!= self::MODE_ECB;
}
/**
* Returns whether or not the algorithm uses a nonce
*
* @return bool
*/
public function usesNonce()
{
return $this->mode == self::MODE_GCM;
}
/**
* Returns the current key length in bits
*
* @return int
*/
public function getKeyLength()
{
return $this->key_length << 3;
}
/**
* Returns the current block length in bits
*
* @return int
*/
public function getBlockLength()
{
return $this->block_size << 3;
}
/**
* Returns the current block length in bytes
*
* @return int
*/
public function getBlockLengthInBytes()
{
return $this->block_size;
}
/**
* Sets the key length.
*
* Keys with explicitly set lengths need to be treated accordingly
*
* @param int $length
*/
public 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 InconsistentSetupException('Key has already been
set and is not ' . $this->explicit_key_length . ' bytes
long');
}
}
/**
* Sets the key.
*
* The min/max length(s) of the key depends on the cipher which is
used.
* If the key not fits the length(s) of the cipher it will paded with
null bytes
* up to the closest valid key length. If the key is more than max
length,
* we trim the excess bits.
*
* If the key is not explicitly set, it'll be assumed to be all
null bytes.
*
* {@internal Could, but not must, extend by the child Crypt_* class}
*
* @param string $key
*/
public function setKey($key)
{
if ($this->explicit_key_length !== false && strlen($key)
!= $this->explicit_key_length) {
throw new InconsistentSetupException('Key length has
already been set to ' . $this->explicit_key_length . ' bytes
and this key is ' . strlen($key) . ' bytes');
}
$this->key = $key;
$this->key_length = strlen($key);
$this->setEngine();
}
/**
* 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} or pbkdf1:
* $hash, $salt, $count, $dkLen
*
* Where $hash (default = sha1) currently supports the
following hashes: see: Crypt/Hash.php
* {@link https://en.wikipedia.org/wiki/Bcrypt bcypt}:
* $salt, $rounds, $keylen
*
* This is a modified version of bcrypt used by OpenSSH.
*
* {@internal Could, but not must, extend by the child Crypt_* class}
*
* @see Crypt/Hash.php
* @param string $password
* @param string $method
* @param int|string ...$func_args
* @throws \LengthException if pbkdf1 is being used and the derived key
length exceeds the hash length
* @throws \RuntimeException if bcrypt is being used and a salt
isn't provided
* @return bool
*/
public function setPassword($password, $method = 'pbkdf2',
...$func_args)
{
$key = '';
$method = strtolower($method);
switch ($method) {
case 'bcrypt':
if (!isset($func_args[2])) {
throw new \RuntimeException('A salt must be
provided for bcrypt to work');
}
$salt = $func_args[0];
$rounds = isset($func_args[1]) ? $func_args[1] : 16;
$keylen = isset($func_args[2]) ? $func_args[2] :
$this->key_length;
$key = Blowfish::bcrypt_pbkdf($password, $salt, $keylen +
$this->block_size, $rounds);
$this->setKey(substr($key, 0, $keylen));
$this->setIV(substr($key, $keylen));
return true;
case 'pkcs12': // from
https://tools.ietf.org/html/rfc7292#appendix-B.2
case 'pbkdf1':
case 'pbkdf2':
// Hash function
$hash = isset($func_args[0]) ? strtolower($func_args[0]) :
'sha1';
$hashObj = new Hash();
$hashObj->setHash($hash);
// WPA and WPA2 use the SSID as the salt
$salt = isset($func_args[1]) ? $func_args[1] :
$this->password_default_salt;
// RFC2898#section-4.2 uses 1,000 iterations by default
// WPA and WPA2 use 4,096.
$count = isset($func_args[2]) ? $func_args[2] : 1000;
// Keylength
if (isset($func_args[3])) {
if ($func_args[3] <= 0) {
throw new \LengthException('Derived key length
cannot be longer 0 or less');
}
$dkLen = $func_args[3];
} else {
$key_length = $this->explicit_key_length !== false ?
$this->explicit_key_length : $this->key_length;
$dkLen = $method == 'pbkdf1' ? 2 *
$key_length : $key_length;
}
switch (true) {
case $method == 'pkcs12':
/*
In this specification, however, all passwords are
created from
BMPStrings with a NULL terminator. This means
that each character in
the original BMPString is encoded in 2 bytes in
big-endian format
(most-significant byte first). There are no
Unicode byte order
marks. The 2 bytes produced from the last
character in the BMPString
are followed by 2 additional bytes with the value
0x00.
--
https://tools.ietf.org/html/rfc7292#appendix-B.1
*/
$password = "\0" . chunk_split($password,
1, "\0") . "\0";
/*
This standard specifies 3 different values for the
ID byte mentioned
above:
1. If ID=1, then the pseudorandom bits being
produced are to be used
as key material for performing encryption or
decryption.
2. If ID=2, then the pseudorandom bits being
produced are to be used
as an IV (Initial Value) for encryption or
decryption.
3. If ID=3, then the pseudorandom bits being
produced are to be used
as an integrity key for MACing.
*/
// Construct a string, D (the
"diversifier"), by concatenating v/8
// copies of ID.
$blockLength =
$hashObj->getBlockLengthInBytes();
$d1 = str_repeat(chr(1), $blockLength);
$d2 = str_repeat(chr(2), $blockLength);
$s = '';
if (strlen($salt)) {
while (strlen($s) < $blockLength) {
$s .= $salt;
}
}
$s = substr($s, 0, $blockLength);
$p = '';
if (strlen($password)) {
while (strlen($p) < $blockLength) {
$p .= $password;
}
}
$p = substr($p, 0, $blockLength);
$i = $s . $p;
$this->setKey(self::pkcs12helper($dkLen,
$hashObj, $i, $d1, $count));
if ($this->usesIV()) {
$this->setIV(self::pkcs12helper($this->block_size, $hashObj, $i, $d2,
$count));
}
return true;
case $method == 'pbkdf1':
if ($dkLen > $hashObj->getLengthInBytes()) {
throw new \LengthException('Derived key
length cannot be longer than the hash length');
}
$t = $password . $salt;
for ($i = 0; $i < $count; ++$i) {
$t = $hashObj->hash($t);
}
$key = substr($t, 0, $dkLen);
$this->setKey(substr($key, 0, $dkLen >>
1));
if ($this->usesIV()) {
$this->setIV(substr($key, $dkLen >>
1));
}
return true;
case !in_array($hash, hash_algos()):
$i = 1;
$hashObj->setKey($password);
while (strlen($key) < $dkLen) {
$f = $u = $hashObj->hash($salt .
pack('N', $i++));
for ($j = 2; $j <= $count; ++$j) {
$u = $hashObj->hash($u);
$f ^= $u;
}
$key .= $f;
}
$key = substr($key, 0, $dkLen);
break;
default:
$key = hash_pbkdf2($hash, $password, $salt, $count,
$dkLen, true);
}
break;
default:
throw new UnsupportedAlgorithmException($method . ' is
not a supported password hashing method');
}
$this->setKey($key);
return true;
}
/**
* PKCS#12 KDF Helper Function
*
* As discussed here:
*
* {@link https://tools.ietf.org/html/rfc7292#appendix-B}
*
* @see self::setPassword()
* @param int $n
* @param \phpseclib3\Crypt\Hash $hashObj
* @param string $i
* @param string $d
* @param int $count
* @return string $a
*/
private static function pkcs12helper($n, $hashObj, $i, $d, $count)
{
static $one;
if (!isset($one)) {
$one = new BigInteger(1);
}
$blockLength = $hashObj->getBlockLength() >> 3;
$c = ceil($n / $hashObj->getLengthInBytes());
$a = '';
for ($j = 1; $j <= $c; $j++) {
$ai = $d . $i;
for ($k = 0; $k < $count; $k++) {
$ai = $hashObj->hash($ai);
}
$b = '';
while (strlen($b) < $blockLength) {
$b .= $ai;
}
$b = substr($b, 0, $blockLength);
$b = new BigInteger($b, 256);
$newi = '';
for ($k = 0; $k < strlen($i); $k += $blockLength) {
$temp = substr($i, $k, $blockLength);
$temp = new BigInteger($temp, 256);
$temp->setPrecision($blockLength << 3);
$temp = $temp->add($b);
$temp = $temp->add($one);
$newi .= $temp->toBytes(false);
}
$i = $newi;
$a .= $ai;
}
return substr($a, 0, $n);
}
/**
* Encrypts a message.
*
* $plaintext will be padded with additional bytes such that it's
length is a multiple of the block size. Other cipher
* implementations may or may not pad in the 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}
*
* 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 the block
size, however, arbitrary values can be added to make it that
* length.
*
* {@internal Could, but not must, extend by the child Crypt_* class}
*
* @see self::decrypt()
* @param string $plaintext
* @return string $ciphertext
*/
public function encrypt($plaintext)
{
if ($this->paddable) {
$plaintext = $this->pad($plaintext);
}
$this->setup();
if ($this->mode == self::MODE_GCM) {
$oldIV = $this->iv;
Strings::increment_str($this->iv);
$cipher = new static('ctr');
$cipher->setKey($this->key);
$cipher->setIV($this->iv);
$ciphertext = $cipher->encrypt($plaintext);
$s = $this->ghash(
self::nullPad128($this->aad) .
self::nullPad128($ciphertext) .
self::len64($this->aad) .
self::len64($ciphertext)
);
$cipher->encryptIV = $this->iv = $this->encryptIV =
$this->decryptIV = $oldIV;
$this->newtag = $cipher->encrypt($s);
return $ciphertext;
}
if (isset($this->poly1305Key)) {
$cipher = clone $this;
unset($cipher->poly1305Key);
$this->usePoly1305 = false;
$ciphertext = $cipher->encrypt($plaintext);
$this->newtag = $this->poly1305($ciphertext);
return $ciphertext;
}
if ($this->engine === self::ENGINE_OPENSSL) {
switch ($this->mode) {
case self::MODE_STREAM:
return openssl_encrypt($plaintext,
$this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA |
OPENSSL_ZERO_PADDING);
case self::MODE_ECB:
return openssl_encrypt($plaintext,
$this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA |
OPENSSL_ZERO_PADDING);
case self::MODE_CBC:
$result = openssl_encrypt($plaintext,
$this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA |
OPENSSL_ZERO_PADDING, $this->encryptIV);
if ($this->continuousBuffer) {
$this->encryptIV = substr($result,
-$this->block_size);
}
return $result;
case self::MODE_CTR:
return $this->openssl_ctr_process($plaintext,
$this->encryptIV, $this->enbuffer);
case self::MODE_CFB:
// cfb loosely routines inspired by openssl's:
// {@link
http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
$ciphertext = '';
if ($this->continuousBuffer) {
$iv = &$this->encryptIV;
$pos = &$this->enbuffer['pos'];
} else {
$iv = $this->encryptIV;
$pos = 0;
}
$len = strlen($plaintext);
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $this->block_size - $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
$ciphertext = substr($iv, $orig_pos) ^ $plaintext;
$iv = substr_replace($iv, $ciphertext, $orig_pos,
$i);
$plaintext = substr($plaintext, $i);
}
$overflow = $len % $this->block_size;
if ($overflow) {
$ciphertext .= openssl_encrypt(substr($plaintext,
0, -$overflow) . str_repeat("\0", $this->block_size),
$this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA |
OPENSSL_ZERO_PADDING, $iv);
$iv = Strings::pop($ciphertext,
$this->block_size);
$size = $len - $overflow;
$block = $iv ^ substr($plaintext, -$overflow);
$iv = substr_replace($iv, $block, 0, $overflow);
$ciphertext .= $block;
$pos = $overflow;
} elseif ($len) {
$ciphertext = openssl_encrypt($plaintext,
$this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA |
OPENSSL_ZERO_PADDING, $iv);
$iv = substr($ciphertext, -$this->block_size);
}
return $ciphertext;
case self::MODE_CFB8:
$ciphertext = openssl_encrypt($plaintext,
$this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA |
OPENSSL_ZERO_PADDING, $this->encryptIV);
if ($this->continuousBuffer) {
if (($len = strlen($ciphertext)) >=
$this->block_size) {
$this->encryptIV = substr($ciphertext,
-$this->block_size);
} else {
$this->encryptIV =
substr($this->encryptIV, $len - $this->block_size) .
substr($ciphertext, -$len);
}
}
return $ciphertext;
case self::MODE_OFB8:
$ciphertext = '';
$len = strlen($plaintext);
$iv = $this->encryptIV;
for ($i = 0; $i < $len; ++$i) {
$xor = openssl_encrypt($iv,
$this->cipher_name_openssl_ecb, $this->key,
$this->openssl_options, $this->decryptIV);
$ciphertext .= $plaintext[$i] ^ $xor;
$iv = substr($iv, 1) . $xor[0];
}
if ($this->continuousBuffer) {
$this->encryptIV = $iv;
}
break;
case self::MODE_OFB:
return $this->openssl_ofb_process($plaintext,
$this->encryptIV, $this->enbuffer);
}
}
if ($this->engine === self::ENGINE_MCRYPT) {
set_error_handler(function () {
});
if ($this->enchanged) {
mcrypt_generic_init($this->enmcrypt, $this->key,
$this->getIV($this->encryptIV));
$this->enchanged = false;
}
// re: {@link 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 == self::MODE_CFB &&
$this->continuousBuffer) {
$block_size = $this->block_size;
$iv = &$this->encryptIV;
$pos = &$this->enbuffer['pos'];
$len = strlen($plaintext);
$ciphertext = '';
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $block_size - $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 >= $block_size) {
if ($this->enbuffer['enmcrypt_init'] ===
false || $len > $this->cfb_init_len) {
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 % $block_size));
$iv = substr($ciphertext, -$block_size);
$len %= $block_size;
} else {
while ($len >= $block_size) {
$iv = mcrypt_generic($this->ecb, $iv) ^
substr($plaintext, $i, $block_size);
$ciphertext .= $iv;
$len -= $block_size;
$i += $block_size;
}
}
}
if ($len) {
$iv = mcrypt_generic($this->ecb, $iv);
$block = $iv ^ substr($plaintext, -$len);
$iv = substr_replace($iv, $block, 0, $len);
$ciphertext .= $block;
$pos = $len;
}
restore_error_handler();
return $ciphertext;
}
$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->enmcrypt, $this->key,
$this->getIV($this->encryptIV));
}
restore_error_handler();
return $ciphertext;
}
if ($this->engine === self::ENGINE_EVAL) {
$inline = $this->inline_crypt;
return $inline('encrypt', $plaintext);
}
$buffer = &$this->enbuffer;
$block_size = $this->block_size;
$ciphertext = '';
switch ($this->mode) {
case self::MODE_ECB:
for ($i = 0; $i < strlen($plaintext); $i += $block_size)
{
$ciphertext .=
$this->encryptBlock(substr($plaintext, $i, $block_size));
}
break;
case self::MODE_CBC:
$xor = $this->encryptIV;
for ($i = 0; $i < strlen($plaintext); $i += $block_size)
{
$block = substr($plaintext, $i, $block_size);
$block = $this->encryptBlock($block ^ $xor);
$xor = $block;
$ciphertext .= $block;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
}
break;
case self::MODE_CTR:
$xor = $this->encryptIV;
if (strlen($buffer['ciphertext'])) {
for ($i = 0; $i < strlen($plaintext); $i +=
$block_size) {
$block = substr($plaintext, $i, $block_size);
if (strlen($block) >
strlen($buffer['ciphertext'])) {
$buffer['ciphertext'] .=
$this->encryptBlock($xor);
Strings::increment_str($xor);
}
$key =
Strings::shift($buffer['ciphertext'], $block_size);
$ciphertext .= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($plaintext); $i +=
$block_size) {
$block = substr($plaintext, $i, $block_size);
$key = $this->encryptBlock($xor);
Strings::increment_str($xor);
$ciphertext .= $block ^ $key;
}
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
if ($start = strlen($plaintext) % $block_size) {
$buffer['ciphertext'] = substr($key,
$start) . $buffer['ciphertext'];
}
}
break;
case self::MODE_CFB:
// cfb loosely routines inspired by openssl's:
// {@link
http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
if ($this->continuousBuffer) {
$iv = &$this->encryptIV;
$pos = &$buffer['pos'];
} else {
$iv = $this->encryptIV;
$pos = 0;
}
$len = strlen($plaintext);
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $block_size - $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
$ciphertext = substr($iv, $orig_pos) ^ $plaintext;
$iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
}
while ($len >= $block_size) {
$iv = $this->encryptBlock($iv) ^ substr($plaintext,
$i, $block_size);
$ciphertext .= $iv;
$len -= $block_size;
$i += $block_size;
}
if ($len) {
$iv = $this->encryptBlock($iv);
$block = $iv ^ substr($plaintext, $i);
$iv = substr_replace($iv, $block, 0, $len);
$ciphertext .= $block;
$pos = $len;
}
break;
case self::MODE_CFB8:
$ciphertext = '';
$len = strlen($plaintext);
$iv = $this->encryptIV;
for ($i = 0; $i < $len; ++$i) {
$ciphertext .= ($c = $plaintext[$i] ^
$this->encryptBlock($iv));
$iv = substr($iv, 1) . $c;
}
if ($this->continuousBuffer) {
if ($len >= $block_size) {
$this->encryptIV = substr($ciphertext,
-$block_size);
} else {
$this->encryptIV = substr($this->encryptIV,
$len - $block_size) . substr($ciphertext, -$len);
}
}
break;
case self::MODE_OFB8:
$ciphertext = '';
$len = strlen($plaintext);
$iv = $this->encryptIV;
for ($i = 0; $i < $len; ++$i) {
$xor = $this->encryptBlock($iv);
$ciphertext .= $plaintext[$i] ^ $xor;
$iv = substr($iv, 1) . $xor[0];
}
if ($this->continuousBuffer) {
$this->encryptIV = $iv;
}
break;
case self::MODE_OFB:
$xor = $this->encryptIV;
if (strlen($buffer['xor'])) {
for ($i = 0; $i < strlen($plaintext); $i +=
$block_size) {
$block = substr($plaintext, $i, $block_size);
if (strlen($block) >
strlen($buffer['xor'])) {
$xor = $this->encryptBlock($xor);
$buffer['xor'] .= $xor;
}
$key = Strings::shift($buffer['xor'],
$block_size);
$ciphertext .= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($plaintext); $i +=
$block_size) {
$xor = $this->encryptBlock($xor);
$ciphertext .= substr($plaintext, $i, $block_size)
^ $xor;
}
$key = $xor;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
if ($start = strlen($plaintext) % $block_size) {
$buffer['xor'] = substr($key, $start) .
$buffer['xor'];
}
}
break;
case self::MODE_STREAM:
$ciphertext = $this->encryptBlock($plaintext);
break;
}
return $ciphertext;
}
/**
* Decrypts a message.
*
* If strlen($ciphertext) is not a multiple of the block size, null
bytes will be added to the end of the string until
* it is.
*
* {@internal Could, but not must, extend by the child Crypt_* class}
*
* @see self::encrypt()
* @param string $ciphertext
* @return string $plaintext
* @throws \LengthException if we're inside a block cipher and the
ciphertext length is not a multiple of the block size
*/
public function decrypt($ciphertext)
{
if ($this->paddable && strlen($ciphertext) %
$this->block_size) {
throw new \LengthException('The ciphertext length ('
. strlen($ciphertext) . ') needs to be a multiple of the block size
(' . $this->block_size . ')');
}
$this->setup();
if ($this->mode == self::MODE_GCM ||
isset($this->poly1305Key)) {
if ($this->oldtag === false) {
throw new InsufficientSetupException('Authentication
Tag has not been set');
}
if (isset($this->poly1305Key)) {
$newtag = $this->poly1305($ciphertext);
} else {
$oldIV = $this->iv;
Strings::increment_str($this->iv);
$cipher = new static('ctr');
$cipher->setKey($this->key);
$cipher->setIV($this->iv);
$plaintext = $cipher->decrypt($ciphertext);
$s = $this->ghash(
self::nullPad128($this->aad) .
self::nullPad128($ciphertext) .
self::len64($this->aad) .
self::len64($ciphertext)
);
$cipher->encryptIV = $this->iv = $this->encryptIV
= $this->decryptIV = $oldIV;
$newtag = $cipher->encrypt($s);
}
if ($this->oldtag != substr($newtag, 0, strlen($newtag))) {
$cipher = clone $this;
unset($cipher->poly1305Key);
$this->usePoly1305 = false;
$plaintext = $cipher->decrypt($ciphertext);
$this->oldtag = false;
throw new BadDecryptionException('Derived
authentication tag and supplied authentication tag do not match');
}
$this->oldtag = false;
return $plaintext;
}
if ($this->engine === self::ENGINE_OPENSSL) {
switch ($this->mode) {
case self::MODE_STREAM:
$plaintext = openssl_decrypt($ciphertext,
$this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA |
OPENSSL_ZERO_PADDING);
break;
case self::MODE_ECB:
$plaintext = openssl_decrypt($ciphertext,
$this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA |
OPENSSL_ZERO_PADDING);
break;
case self::MODE_CBC:
$offset = $this->block_size;
$plaintext = openssl_decrypt($ciphertext,
$this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA |
OPENSSL_ZERO_PADDING, $this->decryptIV);
if ($this->continuousBuffer) {
$this->decryptIV = substr($ciphertext, -$offset,
$this->block_size);
}
break;
case self::MODE_CTR:
$plaintext = $this->openssl_ctr_process($ciphertext,
$this->decryptIV, $this->debuffer);
break;
case self::MODE_CFB:
// cfb loosely routines inspired by openssl's:
// {@link
http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
$plaintext = '';
if ($this->continuousBuffer) {
$iv = &$this->decryptIV;
$pos = &$this->debuffer['pos'];
} else {
$iv = $this->decryptIV;
$pos = 0;
}
$len = strlen($ciphertext);
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $this->block_size - $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%= $this->blocksize
$plaintext = substr($iv, $orig_pos) ^ $ciphertext;
$iv = substr_replace($iv, substr($ciphertext, 0,
$i), $orig_pos, $i);
$ciphertext = substr($ciphertext, $i);
}
$overflow = $len % $this->block_size;
if ($overflow) {
$plaintext .= openssl_decrypt(substr($ciphertext,
0, -$overflow), $this->cipher_name_openssl, $this->key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
if ($len - $overflow) {
$iv = substr($ciphertext, -$overflow -
$this->block_size, -$overflow);
}
$iv = openssl_encrypt(str_repeat("\0",
$this->block_size), $this->cipher_name_openssl, $this->key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
$plaintext .= $iv ^ substr($ciphertext,
-$overflow);
$iv = substr_replace($iv, substr($ciphertext,
-$overflow), 0, $overflow);
$pos = $overflow;
} elseif ($len) {
$plaintext .= openssl_decrypt($ciphertext,
$this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA |
OPENSSL_ZERO_PADDING, $iv);
$iv = substr($ciphertext, -$this->block_size);
}
break;
case self::MODE_CFB8:
$plaintext = openssl_decrypt($ciphertext,
$this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA |
OPENSSL_ZERO_PADDING, $this->decryptIV);
if ($this->continuousBuffer) {
if (($len = strlen($ciphertext)) >=
$this->block_size) {
$this->decryptIV = substr($ciphertext,
-$this->block_size);
} else {
$this->decryptIV =
substr($this->decryptIV, $len - $this->block_size) .
substr($ciphertext, -$len);
}
}
break;
case self::MODE_OFB8:
$plaintext = '';
$len = strlen($ciphertext);
$iv = $this->decryptIV;
for ($i = 0; $i < $len; ++$i) {
$xor = openssl_encrypt($iv,
$this->cipher_name_openssl_ecb, $this->key,
$this->openssl_options, $this->decryptIV);
$plaintext .= $ciphertext[$i] ^ $xor;
$iv = substr($iv, 1) . $xor[0];
}
if ($this->continuousBuffer) {
$this->decryptIV = $iv;
}
break;
case self::MODE_OFB:
$plaintext = $this->openssl_ofb_process($ciphertext,
$this->decryptIV, $this->debuffer);
}
return $this->paddable ? $this->unpad($plaintext) :
$plaintext;
}
if ($this->engine === self::ENGINE_MCRYPT) {
set_error_handler(function () {
});
$block_size = $this->block_size;
if ($this->dechanged) {
mcrypt_generic_init($this->demcrypt, $this->key,
$this->getIV($this->decryptIV));
$this->dechanged = false;
}
if ($this->mode == self::MODE_CFB &&
$this->continuousBuffer) {
$iv = &$this->decryptIV;
$pos = &$this->debuffer['pos'];
$len = strlen($ciphertext);
$plaintext = '';
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $block_size - $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 >= $block_size) {
$cb = substr($ciphertext, $i, $len - $len %
$block_size);
$plaintext .= mcrypt_generic($this->ecb, $iv . $cb)
^ $cb;
$iv = substr($cb, -$block_size);
$len %= $block_size;
}
if ($len) {
$iv = mcrypt_generic($this->ecb, $iv);
$plaintext .= $iv ^ substr($ciphertext, -$len);
$iv = substr_replace($iv, substr($ciphertext, -$len),
0, $len);
$pos = $len;
}
restore_error_handler();
return $plaintext;
}
$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->demcrypt, $this->key,
$this->getIV($this->decryptIV));
}
restore_error_handler();
return $this->paddable ? $this->unpad($plaintext) :
$plaintext;
}
if ($this->engine === self::ENGINE_EVAL) {
$inline = $this->inline_crypt;
return $inline('decrypt', $ciphertext);
}
$block_size = $this->block_size;
$buffer = &$this->debuffer;
$plaintext = '';
switch ($this->mode) {
case self::MODE_ECB:
for ($i = 0; $i < strlen($ciphertext); $i +=
$block_size) {
$plaintext .=
$this->decryptBlock(substr($ciphertext, $i, $block_size));
}
break;
case self::MODE_CBC:
$xor = $this->decryptIV;
for ($i = 0; $i < strlen($ciphertext); $i +=
$block_size) {
$block = substr($ciphertext, $i, $block_size);
$plaintext .= $this->decryptBlock($block) ^ $xor;
$xor = $block;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
}
break;
case self::MODE_CTR:
$xor = $this->decryptIV;
if (strlen($buffer['ciphertext'])) {
for ($i = 0; $i < strlen($ciphertext); $i +=
$block_size) {
$block = substr($ciphertext, $i, $block_size);
if (strlen($block) >
strlen($buffer['ciphertext'])) {
$buffer['ciphertext'] .=
$this->encryptBlock($xor);
Strings::increment_str($xor);
}
$key =
Strings::shift($buffer['ciphertext'], $block_size);
$plaintext .= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($ciphertext); $i +=
$block_size) {
$block = substr($ciphertext, $i, $block_size);
$key = $this->encryptBlock($xor);
Strings::increment_str($xor);
$plaintext .= $block ^ $key;
}
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
if ($start = strlen($ciphertext) % $block_size) {
$buffer['ciphertext'] = substr($key,
$start) . $buffer['ciphertext'];
}
}
break;
case self::MODE_CFB:
if ($this->continuousBuffer) {
$iv = &$this->decryptIV;
$pos = &$buffer['pos'];
} else {
$iv = $this->decryptIV;
$pos = 0;
}
$len = strlen($ciphertext);
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $block_size - $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);
}
while ($len >= $block_size) {
$iv = $this->encryptBlock($iv);
$cb = substr($ciphertext, $i, $block_size);
$plaintext .= $iv ^ $cb;
$iv = $cb;
$len -= $block_size;
$i += $block_size;
}
if ($len) {
$iv = $this->encryptBlock($iv);
$plaintext .= $iv ^ substr($ciphertext, $i);
$iv = substr_replace($iv, substr($ciphertext, $i), 0,
$len);
$pos = $len;
}
break;
case self::MODE_CFB8:
$plaintext = '';
$len = strlen($ciphertext);
$iv = $this->decryptIV;
for ($i = 0; $i < $len; ++$i) {
$plaintext .= $ciphertext[$i] ^
$this->encryptBlock($iv);
$iv = substr($iv, 1) . $ciphertext[$i];
}
if ($this->continuousBuffer) {
if ($len >= $block_size) {
$this->decryptIV = substr($ciphertext,
-$block_size);
} else {
$this->decryptIV = substr($this->decryptIV,
$len - $block_size) . substr($ciphertext, -$len);
}
}
break;
case self::MODE_OFB8:
$plaintext = '';
$len = strlen($ciphertext);
$iv = $this->decryptIV;
for ($i = 0; $i < $len; ++$i) {
$xor = $this->encryptBlock($iv);
$plaintext .= $ciphertext[$i] ^ $xor;
$iv = substr($iv, 1) . $xor[0];
}
if ($this->continuousBuffer) {
$this->decryptIV = $iv;
}
break;
case self::MODE_OFB:
$xor = $this->decryptIV;
if (strlen($buffer['xor'])) {
for ($i = 0; $i < strlen($ciphertext); $i +=
$block_size) {
$block = substr($ciphertext, $i, $block_size);
if (strlen($block) >
strlen($buffer['xor'])) {
$xor = $this->encryptBlock($xor);
$buffer['xor'] .= $xor;
}
$key = Strings::shift($buffer['xor'],
$block_size);
$plaintext .= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($ciphertext); $i +=
$block_size) {
$xor = $this->encryptBlock($xor);
$plaintext .= substr($ciphertext, $i, $block_size)
^ $xor;
}
$key = $xor;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
if ($start = strlen($ciphertext) % $block_size) {
$buffer['xor'] = substr($key, $start) .
$buffer['xor'];
}
}
break;
case self::MODE_STREAM:
$plaintext = $this->decryptBlock($ciphertext);
break;
}
return $this->paddable ? $this->unpad($plaintext) :
$plaintext;
}
/**
* Get the authentication tag
*
* Only used in GCM or Poly1305 mode
*
* @see self::encrypt()
* @param int $length optional
* @return string
* @throws \LengthException if $length isn't of a sufficient
length
* @throws \RuntimeException if GCM mode isn't being used
*/
public function getTag($length = 16)
{
if ($this->mode != self::MODE_GCM &&
!$this->usePoly1305) {
throw new \BadMethodCallException('Authentication tags are
only utilized in GCM mode or with Poly1305');
}
if ($this->newtag === false) {
throw new \BadMethodCallException('A tag can only be
returned after a round of encryption has been performed');
}
// the tag is 128-bits. it can't be greater than 16 bytes
because that's bigger than the tag is. if it
// were 0 you might as well be doing CTR and less than 4 provides
minimal security that could be trivially
// easily brute forced.
// see
https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=36
// for more info
if ($length < 4 || $length > 16) {
throw new \LengthException('The authentication tag must be
between 4 and 16 bytes long');
}
return $length == 16 ?
$this->newtag :
substr($this->newtag, 0, $length);
}
/**
* Sets the authentication tag
*
* Only used in GCM mode
*
* @see self::decrypt()
* @param string $tag
* @throws \LengthException if $length isn't of a sufficient
length
* @throws \RuntimeException if GCM mode isn't being used
*/
public function setTag($tag)
{
if ($this->usePoly1305 && !isset($this->poly1305Key)
&& method_exists($this, 'createPoly1305Key')) {
$this->createPoly1305Key();
}
if ($this->mode != self::MODE_GCM &&
!$this->usePoly1305) {
throw new \BadMethodCallException('Authentication tags are
only utilized in GCM mode or with Poly1305');
}
$length = strlen($tag);
if ($length < 4 || $length > 16) {
throw new \LengthException('The authentication tag must be
between 4 and 16 bytes long');
}
$this->oldtag = $tag;
}
/**
* Get the IV
*
* mcrypt requires an IV even if ECB is used
*
* @see self::encrypt()
* @see self::decrypt()
* @param string $iv
* @return string
*/
protected function getIV($iv)
{
return $this->mode == self::MODE_ECB ?
str_repeat("\0", $this->block_size) : $iv;
}
/**
* OpenSSL CTR Processor
*
* PHP's OpenSSL bindings do not operate in continuous mode so
we'll wrap around it. Since the keystream
* for CTR is the same for both encrypting and decrypting this function
is re-used by both SymmetricKey::encrypt()
* and SymmetricKey::decrypt(). Also, OpenSSL doesn't implement
CTR for all of it's symmetric ciphers so this
* function will emulate CTR with ECB when necessary.
*
* @see self::encrypt()
* @see self::decrypt()
* @param string $plaintext
* @param string $encryptIV
* @param array $buffer
* @return string
*/
private function openssl_ctr_process($plaintext, &$encryptIV,
&$buffer)
{
$ciphertext = '';
$block_size = $this->block_size;
$key = $this->key;
if ($this->openssl_emulate_ctr) {
$xor = $encryptIV;
if (strlen($buffer['ciphertext'])) {
for ($i = 0; $i < strlen($plaintext); $i += $block_size)
{
$block = substr($plaintext, $i, $block_size);
if (strlen($block) >
strlen($buffer['ciphertext'])) {
$buffer['ciphertext'] .=
openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
}
Strings::increment_str($xor);
$otp = Strings::shift($buffer['ciphertext'],
$block_size);
$ciphertext .= $block ^ $otp;
}
} else {
for ($i = 0; $i < strlen($plaintext); $i += $block_size)
{
$block = substr($plaintext, $i, $block_size);
$otp = openssl_encrypt($xor,
$this->cipher_name_openssl_ecb, $key, OPENSSL_RAW_DATA |
OPENSSL_ZERO_PADDING);
Strings::increment_str($xor);
$ciphertext .= $block ^ $otp;
}
}
if ($this->continuousBuffer) {
$encryptIV = $xor;
if ($start = strlen($plaintext) % $block_size) {
$buffer['ciphertext'] = substr($key, $start)
. $buffer['ciphertext'];
}
}
return $ciphertext;
}
if (strlen($buffer['ciphertext'])) {
$ciphertext = $plaintext ^
Strings::shift($buffer['ciphertext'], strlen($plaintext));
$plaintext = substr($plaintext, strlen($ciphertext));
if (!strlen($plaintext)) {
return $ciphertext;
}
}
$overflow = strlen($plaintext) % $block_size;
if ($overflow) {
$plaintext2 = Strings::pop($plaintext, $overflow); // ie. trim
$plaintext to a multiple of $block_size and put rest of $plaintext in
$plaintext2
$encrypted = openssl_encrypt($plaintext .
str_repeat("\0", $block_size), $this->cipher_name_openssl,
$key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $encryptIV);
$temp = Strings::pop($encrypted, $block_size);
$ciphertext .= $encrypted . ($plaintext2 ^ $temp);
if ($this->continuousBuffer) {
$buffer['ciphertext'] = substr($temp, $overflow);
$encryptIV = $temp;
}
} elseif (!strlen($buffer['ciphertext'])) {
$ciphertext .= openssl_encrypt($plaintext .
str_repeat("\0", $block_size), $this->cipher_name_openssl,
$key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $encryptIV);
$temp = Strings::pop($ciphertext, $block_size);
if ($this->continuousBuffer) {
$encryptIV = $temp;
}
}
if ($this->continuousBuffer) {
$encryptIV = openssl_decrypt($encryptIV,
$this->cipher_name_openssl_ecb, $key, OPENSSL_RAW_DATA |
OPENSSL_ZERO_PADDING);
if ($overflow) {
Strings::increment_str($encryptIV);
}
}
return $ciphertext;
}
/**
* OpenSSL OFB Processor
*
* PHP's OpenSSL bindings do not operate in continuous mode so
we'll wrap around it. Since the keystream
* for OFB is the same for both encrypting and decrypting this function
is re-used by both SymmetricKey::encrypt()
* and SymmetricKey::decrypt().
*
* @see self::encrypt()
* @see self::decrypt()
* @param string $plaintext
* @param string $encryptIV
* @param array $buffer
* @return string
*/
private function openssl_ofb_process($plaintext, &$encryptIV,
&$buffer)
{
if (strlen($buffer['xor'])) {
$ciphertext = $plaintext ^ $buffer['xor'];
$buffer['xor'] = substr($buffer['xor'],
strlen($ciphertext));
$plaintext = substr($plaintext, strlen($ciphertext));
} else {
$ciphertext = '';
}
$block_size = $this->block_size;
$len = strlen($plaintext);
$key = $this->key;
$overflow = $len % $block_size;
if (strlen($plaintext)) {
if ($overflow) {
$ciphertext .= openssl_encrypt(substr($plaintext, 0,
-$overflow) . str_repeat("\0", $block_size),
$this->cipher_name_openssl, $key, OPENSSL_RAW_DATA |
OPENSSL_ZERO_PADDING, $encryptIV);
$xor = Strings::pop($ciphertext, $block_size);
if ($this->continuousBuffer) {
$encryptIV = $xor;
}
$ciphertext .= Strings::shift($xor, $overflow) ^
substr($plaintext, -$overflow);
if ($this->continuousBuffer) {
$buffer['xor'] = $xor;
}
} else {
$ciphertext = openssl_encrypt($plaintext,
$this->cipher_name_openssl, $key, OPENSSL_RAW_DATA |
OPENSSL_ZERO_PADDING, $encryptIV);
if ($this->continuousBuffer) {
$encryptIV = substr($ciphertext, -$block_size) ^
substr($plaintext, -$block_size);
}
}
}
return $ciphertext;
}
/**
* phpseclib <-> OpenSSL Mode Mapper
*
* May need to be overwritten by classes extending this one in some
cases
*
* @return string
*/
protected function openssl_translate_mode()
{
switch ($this->mode) {
case self::MODE_ECB:
return 'ecb';
case self::MODE_CBC:
return 'cbc';
case self::MODE_CTR:
case self::MODE_GCM:
return 'ctr';
case self::MODE_CFB:
return 'cfb';
case self::MODE_CFB8:
return 'cfb8';
case self::MODE_OFB:
return 'ofb';
}
}
/**
* Pad "packets".
*
* Block ciphers working by encrypting between their specified
[$this->]block_size at a time
* If you ever need to encrypt or decrypt something that isn't of
the proper length, it becomes necessary to
* pad the input so that it is of the proper length.
*
* Padding is enabled by default. Sometimes, however, it is
undesirable to pad strings. Such is the case in SSH,
* where "packets" are padded with random bytes before being
encrypted. Unpad these packets and you risk stripping
* away characters that shouldn't be stripped away. (SSH knows how
many bytes are added because the length is
* transmitted separately)
*
* @see self::disablePadding()
*/
public function enablePadding()
{
$this->padding = true;
}
/**
* Do not pad packets.
*
* @see self::enablePadding()
*/
public function disablePadding()
{
$this->padding = false;
}
/**
* Treat consecutive "packets" as if they are a continuous
buffer.
*
* Say you have a 32-byte plaintext $plaintext. Using the default
behavior, the two following code snippets
* will yield different outputs:
*
* <code>
* echo $rijndael->encrypt(substr($plaintext, 0, 16));
* echo $rijndael->encrypt(substr($plaintext, 16, 16));
* </code>
* <code>
* echo $rijndael->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>
* $rijndael->encrypt(substr($plaintext, 0, 16));
* echo
$rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16)));
* </code>
* <code>
* echo
$rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16)));
* </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 \phpseclib3\Crypt\*() 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.
*
* {@internal Could, but not must, extend by the child Crypt_* class}
*
* @see self::disableContinuousBuffer()
*/
public function enableContinuousBuffer()
{
if ($this->mode == self::MODE_ECB) {
return;
}
if ($this->mode == self::MODE_GCM) {
throw new \BadMethodCallException('This mode does not run
in continuous mode');
}
$this->continuousBuffer = true;
$this->setEngine();
}
/**
* Treat consecutive packets as if they are a discontinuous buffer.
*
* The default behavior.
*
* {@internal Could, but not must, extend by the child Crypt_* class}
*
* @see self::enableContinuousBuffer()
*/
public function disableContinuousBuffer()
{
if ($this->mode == self::MODE_ECB) {
return;
}
if (!$this->continuousBuffer) {
return;
}
$this->continuousBuffer = false;
$this->setEngine();
}
/**
* Test for engine validity
*
* @see self::__construct()
* @param int $engine
* @return bool
*/
protected function isValidEngineHelper($engine)
{
switch ($engine) {
case self::ENGINE_OPENSSL:
$this->openssl_emulate_ctr = false;
$result = $this->cipher_name_openssl &&
extension_loaded('openssl');
if (!$result) {
return false;
}
$methods = openssl_get_cipher_methods();
if (in_array($this->cipher_name_openssl, $methods)) {
return true;
}
// not all of openssl's symmetric cipher's
support ctr. for those
// that don't we'll emulate it
switch ($this->mode) {
case self::MODE_CTR:
if (in_array($this->cipher_name_openssl_ecb,
$methods)) {
$this->openssl_emulate_ctr = true;
return true;
}
}
return false;
case self::ENGINE_MCRYPT:
set_error_handler(function () {
});
$result = $this->cipher_name_mcrypt &&
extension_loaded('mcrypt') &&
in_array($this->cipher_name_mcrypt,
mcrypt_list_algorithms());
restore_error_handler();
return $result;
case self::ENGINE_EVAL:
return method_exists($this, 'setupInlineCrypt');
case self::ENGINE_INTERNAL:
return true;
}
return false;
}
/**
* Test for engine validity
*
* @see self::__construct()
* @param string $engine
* @return bool
*/
public function isValidEngine($engine)
{
static $reverseMap;
if (!isset($reverseMap)) {
$reverseMap = array_map('strtolower',
self::ENGINE_MAP);
$reverseMap = array_flip($reverseMap);
}
$engine = strtolower($engine);
if (!isset($reverseMap[$engine])) {
return false;
}
return $this->isValidEngineHelper($reverseMap[$engine]);
}
/**
* Sets the preferred crypt engine
*
* Currently, $engine could be:
*
* - libsodium[very fast]
*
* - OpenSSL [very fast]
*
* - mcrypt [fast]
*
* - Eval [slow]
*
* - PHP [slowest]
*
* If the preferred crypt engine is not available the fastest available
one will be used
*
* @see self::__construct()
* @param string $engine
*/
public function setPreferredEngine($engine)
{
static $reverseMap;
if (!isset($reverseMap)) {
$reverseMap = array_map('strtolower',
self::ENGINE_MAP);
$reverseMap = array_flip($reverseMap);
}
$engine = is_string($engine) ? strtolower($engine) : '';
$this->preferredEngine = isset($reverseMap[$engine]) ?
$reverseMap[$engine] : self::ENGINE_LIBSODIUM;
$this->setEngine();
}
/**
* Returns the engine currently being utilized
*
* @see self::setEngine()
*/
public function getEngine()
{
return self::ENGINE_MAP[$this->engine];
}
/**
* Sets the engine as appropriate
*
* @see self::__construct()
*/
protected function setEngine()
{
$this->engine = null;
$candidateEngines = [
self::ENGINE_LIBSODIUM,
self::ENGINE_OPENSSL_GCM,
self::ENGINE_OPENSSL,
self::ENGINE_MCRYPT,
self::ENGINE_EVAL
];
if (isset($this->preferredEngine)) {
$temp = [$this->preferredEngine];
$candidateEngines = array_merge(
$temp,
array_diff($candidateEngines, $temp)
);
}
foreach ($candidateEngines as $engine) {
if ($this->isValidEngineHelper($engine)) {
$this->engine = $engine;
break;
}
}
if (!$this->engine) {
$this->engine = self::ENGINE_INTERNAL;
}
if ($this->engine != self::ENGINE_MCRYPT &&
$this->enmcrypt) {
set_error_handler(function () {
});
// Closing the current mcrypt resource(s). _mcryptSetup() will,
if needed,
// (re)open them with the module named in
$this->cipher_name_mcrypt
mcrypt_module_close($this->enmcrypt);
mcrypt_module_close($this->demcrypt);
$this->enmcrypt = null;
$this->demcrypt = null;
if ($this->ecb) {
mcrypt_module_close($this->ecb);
$this->ecb = null;
}
restore_error_handler();
}
$this->changed = $this->nonIVChanged = true;
}
/**
* Encrypts a block
*
* Note: Must be extended by the child \phpseclib3\Crypt\* class
*
* @param string $in
* @return string
*/
abstract protected function encryptBlock($in);
/**
* Decrypts a block
*
* Note: Must be extended by the child \phpseclib3\Crypt\* class
*
* @param string $in
* @return string
*/
abstract protected function decryptBlock($in);
/**
* Setup the key (expansion)
*
* Only used if $engine == self::ENGINE_INTERNAL
*
* Note: Must extend by the child \phpseclib3\Crypt\* class
*
* @see self::setup()
*/
abstract protected function setupKey();
/**
* Setup the self::ENGINE_INTERNAL $engine
*
* (re)init, if necessary, the internal cipher $engine and flush all
$buffers
* Used (only) if $engine == self::ENGINE_INTERNAL
*
* _setup() will be called each time if $changed === true
* typically this happens when using one or more of following public
methods:
*
* - setKey()
*
* - setIV()
*
* - disableContinuousBuffer()
*
* - First run of encrypt() / decrypt() with no init-settings
*
* {@internal setup() is always called before en/decryption.}
*
* {@internal Could, but not must, extend by the child Crypt_* class}
*
* @see self::setKey()
* @see self::setIV()
* @see self::disableContinuousBuffer()
*/
protected function setup()
{
if (!$this->changed) {
return;
}
$this->changed = false;
if ($this->usePoly1305 && !isset($this->poly1305Key)
&& method_exists($this, 'createPoly1305Key')) {
$this->createPoly1305Key();
}
$this->enbuffer = $this->debuffer = ['ciphertext'
=> '', 'xor' => '', 'pos'
=> 0, 'enmcrypt_init' => true];
//$this->newtag = $this->oldtag = false;
if ($this->usesNonce()) {
if ($this->nonce === false) {
throw new InsufficientSetupException('No nonce has
been defined');
}
if ($this->mode == self::MODE_GCM &&
!in_array($this->engine, [self::ENGINE_LIBSODIUM,
self::ENGINE_OPENSSL_GCM])) {
$this->setupGCM();
}
} else {
$this->iv = $this->origIV;
}
if ($this->iv === false && !in_array($this->mode,
[self::MODE_STREAM, self::MODE_ECB])) {
if ($this->mode != self::MODE_GCM ||
!in_array($this->engine, [self::ENGINE_LIBSODIUM,
self::ENGINE_OPENSSL_GCM])) {
throw new InsufficientSetupException('No IV has been
defined');
}
}
if ($this->key === false) {
throw new InsufficientSetupException('No key has been
defined');
}
$this->encryptIV = $this->decryptIV = $this->iv;
switch ($this->engine) {
case self::ENGINE_MCRYPT:
$this->enchanged = $this->dechanged = true;
set_error_handler(function () {
});
if (!isset($this->enmcrypt)) {
static $mcrypt_modes = [
self::MODE_CTR => 'ctr',
self::MODE_ECB => MCRYPT_MODE_ECB,
self::MODE_CBC => MCRYPT_MODE_CBC,
self::MODE_CFB => 'ncfb',
self::MODE_CFB8 => MCRYPT_MODE_CFB,
self::MODE_OFB => MCRYPT_MODE_NOFB,
self::MODE_OFB8 => MCRYPT_MODE_OFB,
self::MODE_STREAM => MCRYPT_MODE_STREAM,
];
$this->demcrypt =
mcrypt_module_open($this->cipher_name_mcrypt, '',
$mcrypt_modes[$this->mode], '');
$this->enmcrypt =
mcrypt_module_open($this->cipher_name_mcrypt, '',
$mcrypt_modes[$this->mode], '');
// we need the $ecb mcrypt resource (only) in MODE_CFB
with enableContinuousBuffer()
// to workaround mcrypt's broken ncfb
implementation in buffered mode
// see: {@link
http://phpseclib.sourceforge.net/cfb-demo.phps}
if ($this->mode == self::MODE_CFB) {
$this->ecb =
mcrypt_module_open($this->cipher_name_mcrypt, '',
MCRYPT_MODE_ECB, '');
}
} // else should mcrypt_generic_deinit be called?
if ($this->mode == self::MODE_CFB) {
mcrypt_generic_init($this->ecb, $this->key,
str_repeat("\0", $this->block_size));
}
restore_error_handler();
break;
case self::ENGINE_INTERNAL:
$this->setupKey();
break;
case self::ENGINE_EVAL:
if ($this->nonIVChanged) {
$this->setupKey();
$this->setupInlineCrypt();
}
}
$this->nonIVChanged = false;
}
/**
* Pads a string
*
* Pads a string using the RSA PKCS padding standards so that its
length is a multiple of the blocksize.
* $this->block_size - (strlen($text) % $this->block_size) bytes
are added, each of which is equal to
* chr($this->block_size - (strlen($text) % $this->block_size)
*
* If padding is disabled and $text is not a multiple of the blocksize,
the string will be padded regardless
* and padding will, hence forth, be enabled.
*
* @see self::unpad()
* @param string $text
* @throws \LengthException if padding is disabled and the
plaintext's length is not a multiple of the block size
* @return string
*/
protected function pad($text)
{
$length = strlen($text);
if (!$this->padding) {
if ($length % $this->block_size == 0) {
return $text;
} else {
throw new \LengthException("The plaintext's
length ($length) is not a multiple of the block size
({$this->block_size}). Try enabling padding.");
}
}
$pad = $this->block_size - ($length % $this->block_size);
return str_pad($text, $length + $pad, chr($pad));
}
/**
* Unpads a string.
*
* If padding is enabled and the reported padding length is invalid the
encryption key will be assumed to be wrong
* and false will be returned.
*
* @see self::pad()
* @param string $text
* @throws \LengthException if the ciphertext's length is not a
multiple of the block size
* @return string
*/
protected function unpad($text)
{
if (!$this->padding) {
return $text;
}
$length = ord($text[strlen($text) - 1]);
if (!$length || $length > $this->block_size) {
throw new BadDecryptionException("The ciphertext has an
invalid padding length ($length) compared to the block size
({$this->block_size})");
}
return substr($text, 0, -$length);
}
/**
* Setup the performance-optimized function for de/encrypt()
*
* Stores the created (or existing) callback function-name
* in $this->inline_crypt
*
* Internally for phpseclib developers:
*
* _setupInlineCrypt() would be called only if:
*
* - $this->engine === self::ENGINE_EVAL
*
* - each time on _setup(), after(!) _setupKey()
*
*
* This ensures that _setupInlineCrypt() has always a
* full ready2go initializated internal cipher $engine state
* where, for example, the keys already expanded,
* keys/block_size calculated and such.
*
* It is, each time if called, the responsibility of
_setupInlineCrypt():
*
* - to set $this->inline_crypt to a valid and fully working
callback function
* as a (faster) replacement for encrypt() / decrypt()
*
* - NOT to create unlimited callback functions (for memory
reasons!)
* no matter how often _setupInlineCrypt() would be called. At
some
* point of amount they must be generic re-useable.
*
* - the code of _setupInlineCrypt() it self,
* and the generated callback code,
* must be, in following order:
* - 100% safe
* - 100% compatible to encrypt()/decrypt()
* - using only php5+ features/lang-constructs/php-extensions if
* compatibility (down to php4) or fallback is provided
* - readable/maintainable/understandable/commented and...
not-cryptic-styled-code :-)
* - >= 10% faster than encrypt()/decrypt() [which is, by the
way,
* the reason for the existence of _setupInlineCrypt() :-)]
* - memory-nice
* - short (as good as possible)
*
* Note: - _setupInlineCrypt() is using _createInlineCryptFunction() to
create the full callback function code.
* - In case of using inline crypting, _setupInlineCrypt() must
extend by the child \phpseclib3\Crypt\* class.
* - The following variable names are reserved:
* - $_* (all variable names prefixed with an underscore)
* - $self (object reference to it self. Do not use $this, but
$self instead)
* - $in (the content of $in has to en/decrypt by the generated
code)
* - The callback function should not use the 'return'
statement, but en/decrypt'ing the content of $in only
*
* {@internal If a Crypt_* class providing inline crypting it must
extend _setupInlineCrypt()}
*
* @see self::setup()
* @see self::createInlineCryptFunction()
* @see self::encrypt()
* @see self::decrypt()
*/
//protected function setupInlineCrypt();
/**
* Creates the performance-optimized function for en/decrypt()
*
* Internally for phpseclib developers:
*
* _createInlineCryptFunction():
*
* - merge the $cipher_code [setup'ed by _setupInlineCrypt()]
* with the current [$this->]mode of operation code
*
* - create the $inline function, which called by encrypt() /
decrypt()
* as its replacement to speed up the en/decryption operations.
*
* - return the name of the created $inline callback function
*
* - used to speed up en/decryption
*
*
*
* The main reason why can speed up things [up to 50%] this way are:
*
* - using variables more effective then regular.
* (ie no use of expensive arrays but integers $k_0, $k_1 ...
* or even, for example, the pure $key[] values hardcoded)
*
* - avoiding 1000's of function calls of ie _encryptBlock()
* but inlining the crypt operations.
* in the mode of operation for() loop.
*
* - full loop unroll the (sometimes key-dependent) rounds
* avoiding this way ++$i counters and runtime-if's etc...
*
* The basic code architectur of the generated $inline en/decrypt()
* lambda function, in pseudo php, is:
*
* <code>
*
+----------------------------------------------------------------------------------------------+
* | callback $inline = create_function:
|
* | lambda_function_0001_crypt_ECB($action, $text)
|
* | {
|
* | INSERT PHP CODE OF:
|
* | $cipher_code['init_crypt']; //
general init code. |
* | // ie:
$sbox'es declarations used for |
* | // encrypt
and decrypt'ing. |
* |
|
* | switch ($action) {
|
* | case 'encrypt':
|
* | INSERT PHP CODE OF:
|
* | $cipher_code['init_encrypt']; //
encrypt sepcific init code. |
* | ie:
specified $key or $box |
* |
declarations for encrypt'ing. |
* |
|
* | foreach ($ciphertext) {
|
* | $in = $block_size of $ciphertext;
|
* |
|
* | INSERT PHP CODE OF:
|
* | $cipher_code['encrypt_block']; //
encrypt's (string) $in, which is always: |
* | // strlen($in)
== $this->block_size |
* | // here comes
the cipher algorithm in action |
* | // for
encryption. |
* | //
$cipher_code['encrypt_block'] has to |
* | // encrypt the
content of the $in variable |
* |
|
* | $plaintext .= $in;
|
* | }
|
* | return $plaintext;
|
* |
|
* | case 'decrypt':
|
* | INSERT PHP CODE OF:
|
* | $cipher_code['init_decrypt']; //
decrypt sepcific init code |
* | ie:
specified $key or $box |
* |
declarations for decrypt'ing. |
* | foreach ($plaintext) {
|
* | $in = $block_size of $plaintext;
|
* |
|
* | INSERT PHP CODE OF:
|
* | $cipher_code['decrypt_block']; //
decrypt's (string) $in, which is always |
* | // strlen($in)
== $this->block_size |
* | // here comes
the cipher algorithm in action |
* | // for
decryption. |
* | //
$cipher_code['decrypt_block'] has to |
* | // decrypt the
content of the $in variable |
* | $ciphertext .= $in;
|
* | }
|
* | return $ciphertext;
|
* | }
|
* | }
|
*
+----------------------------------------------------------------------------------------------+
* </code>
*
* See also the \phpseclib3\Crypt\*::_setupInlineCrypt()'s for
* productive inline $cipher_code's how they works.
*
* Structure of:
* <code>
* $cipher_code = [
* 'init_crypt' => (string) '', //
optional
* 'init_encrypt' => (string) '', //
optional
* 'init_decrypt' => (string) '', //
optional
* 'encrypt_block' => (string) '', //
required
* 'decrypt_block' => (string) '' //
required
* ];
* </code>
*
* @see self::setupInlineCrypt()
* @see self::encrypt()
* @see self::decrypt()
* @param array $cipher_code
* @return string (the name of the created callback function)
*/
protected function createInlineCryptFunction($cipher_code)
{
$block_size = $this->block_size;
// optional
$init_crypt = isset($cipher_code['init_crypt']) ?
$cipher_code['init_crypt'] : '';
$init_encrypt = isset($cipher_code['init_encrypt']) ?
$cipher_code['init_encrypt'] : '';
$init_decrypt = isset($cipher_code['init_decrypt']) ?
$cipher_code['init_decrypt'] : '';
// required
$encrypt_block = $cipher_code['encrypt_block'];
$decrypt_block = $cipher_code['decrypt_block'];
// Generating mode of operation inline code,
// merged with the $cipher_code algorithm
// for encrypt- and decryption.
switch ($this->mode) {
case self::MODE_ECB:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_plaintext_len = strlen($_text);
for ($_i = 0; $_i < $_plaintext_len; $_i+= ' .
$block_size . ') {
$in = substr($_text, $_i, ' . $block_size .
');
' . $encrypt_block . '
$_ciphertext.= $in;
}
return $_ciphertext;
';
$decrypt = $init_decrypt . '
$_plaintext = "";
$_text = str_pad($_text, strlen($_text) + (' .
$block_size . ' - strlen($_text) % ' . $block_size . ') %
' . $block_size . ', chr(0));
$_ciphertext_len = strlen($_text);
for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' .
$block_size . ') {
$in = substr($_text, $_i, ' . $block_size .
');
' . $decrypt_block . '
$_plaintext.= $in;
}
return $this->unpad($_plaintext);
';
break;
case self::MODE_CTR:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_plaintext_len = strlen($_text);
$_xor = $this->encryptIV;
$_buffer = &$this->enbuffer;
if (strlen($_buffer["ciphertext"])) {
for ($_i = 0; $_i < $_plaintext_len; $_i+=
' . $block_size . ') {
$_block = substr($_text, $_i, ' .
$block_size . ');
if (strlen($_block) >
strlen($_buffer["ciphertext"])) {
$in = $_xor;
' . $encrypt_block . '
\phpseclib3\Common\Functions\Strings::increment_str($_xor);
$_buffer["ciphertext"].= $in;
}
$_key =
\phpseclib3\Common\Functions\Strings::shift($_buffer["ciphertext"],
' . $block_size . ');
$_ciphertext.= $_block ^ $_key;
}
} else {
for ($_i = 0; $_i < $_plaintext_len; $_i+=
' . $block_size . ') {
$_block = substr($_text, $_i, ' .
$block_size . ');
$in = $_xor;
' . $encrypt_block . '
\phpseclib3\Common\Functions\Strings::increment_str($_xor);
$_key = $in;
$_ciphertext.= $_block ^ $_key;
}
}
if ($this->continuousBuffer) {
$this->encryptIV = $_xor;
if ($_start = $_plaintext_len % ' .
$block_size . ') {
$_buffer["ciphertext"] =
substr($_key, $_start) . $_buffer["ciphertext"];
}
}
return $_ciphertext;
';
$decrypt = $init_encrypt . '
$_plaintext = "";
$_ciphertext_len = strlen($_text);
$_xor = $this->decryptIV;
$_buffer = &$this->debuffer;
if (strlen($_buffer["ciphertext"])) {
for ($_i = 0; $_i < $_ciphertext_len; $_i+=
' . $block_size . ') {
$_block = substr($_text, $_i, ' .
$block_size . ');
if (strlen($_block) >
strlen($_buffer["ciphertext"])) {
$in = $_xor;
' . $encrypt_block . '
\phpseclib3\Common\Functions\Strings::increment_str($_xor);
$_buffer["ciphertext"].= $in;
}
$_key =
\phpseclib3\Common\Functions\Strings::shift($_buffer["ciphertext"],
' . $block_size . ');
$_plaintext.= $_block ^ $_key;
}
} else {
for ($_i = 0; $_i < $_ciphertext_len; $_i+=
' . $block_size . ') {
$_block = substr($_text, $_i, ' .
$block_size . ');
$in = $_xor;
' . $encrypt_block . '
\phpseclib3\Common\Functions\Strings::increment_str($_xor);
$_key = $in;
$_plaintext.= $_block ^ $_key;
}
}
if ($this->continuousBuffer) {
$this->decryptIV = $_xor;
if ($_start = $_ciphertext_len % ' .
$block_size . ') {
$_buffer["ciphertext"] =
substr($_key, $_start) . $_buffer["ciphertext"];
}
}
return $_plaintext;
';
break;
case self::MODE_CFB:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_buffer = &$this->enbuffer;
if ($this->continuousBuffer) {
$_iv = &$this->encryptIV;
$_pos = &$_buffer["pos"];
} else {
$_iv = $this->encryptIV;
$_pos = 0;
}
$_len = strlen($_text);
$_i = 0;
if ($_pos) {
$_orig_pos = $_pos;
$_max = ' . $block_size . ' - $_pos;
if ($_len >= $_max) {
$_i = $_max;
$_len-= $_max;
$_pos = 0;
} else {
$_i = $_len;
$_pos+= $_len;
$_len = 0;
}
$_ciphertext = substr($_iv, $_orig_pos) ^ $_text;
$_iv = substr_replace($_iv, $_ciphertext,
$_orig_pos, $_i);
}
while ($_len >= ' . $block_size . ') {
$in = $_iv;
' . $encrypt_block . ';
$_iv = $in ^ substr($_text, $_i, ' .
$block_size . ');
$_ciphertext.= $_iv;
$_len-= ' . $block_size . ';
$_i+= ' . $block_size . ';
}
if ($_len) {
$in = $_iv;
' . $encrypt_block . '
$_iv = $in;
$_block = $_iv ^ substr($_text, $_i);
$_iv = substr_replace($_iv, $_block, 0, $_len);
$_ciphertext.= $_block;
$_pos = $_len;
}
return $_ciphertext;
';
$decrypt = $init_encrypt . '
$_plaintext = "";
$_buffer = &$this->debuffer;
if ($this->continuousBuffer) {
$_iv = &$this->decryptIV;
$_pos = &$_buffer["pos"];
} else {
$_iv = $this->decryptIV;
$_pos = 0;
}
$_len = strlen($_text);
$_i = 0;
if ($_pos) {
$_orig_pos = $_pos;
$_max = ' . $block_size . ' - $_pos;
if ($_len >= $_max) {
$_i = $_max;
$_len-= $_max;
$_pos = 0;
} else {
$_i = $_len;
$_pos+= $_len;
$_len = 0;
}
$_plaintext = substr($_iv, $_orig_pos) ^ $_text;
$_iv = substr_replace($_iv, substr($_text, 0, $_i),
$_orig_pos, $_i);
}
while ($_len >= ' . $block_size . ') {
$in = $_iv;
' . $encrypt_block . '
$_iv = $in;
$cb = substr($_text, $_i, ' . $block_size .
');
$_plaintext.= $_iv ^ $cb;
$_iv = $cb;
$_len-= ' . $block_size . ';
$_i+= ' . $block_size . ';
}
if ($_len) {
$in = $_iv;
' . $encrypt_block . '
$_iv = $in;
$_plaintext.= $_iv ^ substr($_text, $_i);
$_iv = substr_replace($_iv, substr($_text, $_i), 0,
$_len);
$_pos = $_len;
}
return $_plaintext;
';
break;
case self::MODE_CFB8:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_len = strlen($_text);
$_iv = $this->encryptIV;
for ($_i = 0; $_i < $_len; ++$_i) {
$in = $_iv;
' . $encrypt_block . '
$_ciphertext .= ($_c = $_text[$_i] ^ $in);
$_iv = substr($_iv, 1) . $_c;
}
if ($this->continuousBuffer) {
if ($_len >= ' . $block_size . ') {
$this->encryptIV = substr($_ciphertext,
-' . $block_size . ');
} else {
$this->encryptIV =
substr($this->encryptIV, $_len - ' . $block_size . ') .
substr($_ciphertext, -$_len);
}
}
return $_ciphertext;
';
$decrypt = $init_encrypt . '
$_plaintext = "";
$_len = strlen($_text);
$_iv = $this->decryptIV;
for ($_i = 0; $_i < $_len; ++$_i) {
$in = $_iv;
' . $encrypt_block . '
$_plaintext .= $_text[$_i] ^ $in;
$_iv = substr($_iv, 1) . $_text[$_i];
}
if ($this->continuousBuffer) {
if ($_len >= ' . $block_size . ') {
$this->decryptIV = substr($_text, -' .
$block_size . ');
} else {
$this->decryptIV =
substr($this->decryptIV, $_len - ' . $block_size . ') .
substr($_text, -$_len);
}
}
return $_plaintext;
';
break;
case self::MODE_OFB8:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_len = strlen($_text);
$_iv = $this->encryptIV;
for ($_i = 0; $_i < $_len; ++$_i) {
$in = $_iv;
' . $encrypt_block . '
$_ciphertext.= $_text[$_i] ^ $in;
$_iv = substr($_iv, 1) . $in[0];
}
if ($this->continuousBuffer) {
$this->encryptIV = $_iv;
}
return $_ciphertext;
';
$decrypt = $init_encrypt . '
$_plaintext = "";
$_len = strlen($_text);
$_iv = $this->decryptIV;
for ($_i = 0; $_i < $_len; ++$_i) {
$in = $_iv;
' . $encrypt_block . '
$_plaintext.= $_text[$_i] ^ $in;
$_iv = substr($_iv, 1) . $in[0];
}
if ($this->continuousBuffer) {
$this->decryptIV = $_iv;
}
return $_plaintext;
';
break;
case self::MODE_OFB:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_plaintext_len = strlen($_text);
$_xor = $this->encryptIV;
$_buffer = &$this->enbuffer;
if (strlen($_buffer["xor"])) {
for ($_i = 0; $_i < $_plaintext_len; $_i+=
' . $block_size . ') {
$_block = substr($_text, $_i, ' .
$block_size . ');
if (strlen($_block) >
strlen($_buffer["xor"])) {
$in = $_xor;
' . $encrypt_block . '
$_xor = $in;
$_buffer["xor"].= $_xor;
}
$_key =
\phpseclib3\Common\Functions\Strings::shift($_buffer["xor"],
' . $block_size . ');
$_ciphertext.= $_block ^ $_key;
}
} else {
for ($_i = 0; $_i < $_plaintext_len; $_i+=
' . $block_size . ') {
$in = $_xor;
' . $encrypt_block . '
$_xor = $in;
$_ciphertext.= substr($_text, $_i, ' .
$block_size . ') ^ $_xor;
}
$_key = $_xor;
}
if ($this->continuousBuffer) {
$this->encryptIV = $_xor;
if ($_start = $_plaintext_len % ' .
$block_size . ') {
$_buffer["xor"] = substr($_key,
$_start) . $_buffer["xor"];
}
}
return $_ciphertext;
';
$decrypt = $init_encrypt . '
$_plaintext = "";
$_ciphertext_len = strlen($_text);
$_xor = $this->decryptIV;
$_buffer = &$this->debuffer;
if (strlen($_buffer["xor"])) {
for ($_i = 0; $_i < $_ciphertext_len; $_i+=
' . $block_size . ') {
$_block = substr($_text, $_i, ' .
$block_size . ');
if (strlen($_block) >
strlen($_buffer["xor"])) {
$in = $_xor;
' . $encrypt_block . '
$_xor = $in;
$_buffer["xor"].= $_xor;
}
$_key =
\phpseclib3\Common\Functions\Strings::shift($_buffer["xor"],
' . $block_size . ');
$_plaintext.= $_block ^ $_key;
}
} else {
for ($_i = 0; $_i < $_ciphertext_len; $_i+=
' . $block_size . ') {
$in = $_xor;
' . $encrypt_block . '
$_xor = $in;
$_plaintext.= substr($_text, $_i, ' .
$block_size . ') ^ $_xor;
}
$_key = $_xor;
}
if ($this->continuousBuffer) {
$this->decryptIV = $_xor;
if ($_start = $_ciphertext_len % ' .
$block_size . ') {
$_buffer["xor"] = substr($_key,
$_start) . $_buffer["xor"];
}
}
return $_plaintext;
';
break;
case self::MODE_STREAM:
$encrypt = $init_encrypt . '
$_ciphertext = "";
' . $encrypt_block . '
return $_ciphertext;
';
$decrypt = $init_decrypt . '
$_plaintext = "";
' . $decrypt_block . '
return $_plaintext;
';
break;
// case self::MODE_CBC:
default:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_plaintext_len = strlen($_text);
$in = $this->encryptIV;
for ($_i = 0; $_i < $_plaintext_len; $_i+= ' .
$block_size . ') {
$in = substr($_text, $_i, ' . $block_size .
') ^ $in;
' . $encrypt_block . '
$_ciphertext.= $in;
}
if ($this->continuousBuffer) {
$this->encryptIV = $in;
}
return $_ciphertext;
';
$decrypt = $init_decrypt . '
$_plaintext = "";
$_text = str_pad($_text, strlen($_text) + (' .
$block_size . ' - strlen($_text) % ' . $block_size . ') %
' . $block_size . ', chr(0));
$_ciphertext_len = strlen($_text);
$_iv = $this->decryptIV;
for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' .
$block_size . ') {
$in = $_block = substr($_text, $_i, ' .
$block_size . ');
' . $decrypt_block . '
$_plaintext.= $in ^ $_iv;
$_iv = $_block;
}
if ($this->continuousBuffer) {
$this->decryptIV = $_iv;
}
return $this->unpad($_plaintext);
';
break;
}
// Before discrediting this, please read the following:
// @see https://github.com/phpseclib/phpseclib/issues/1293
// @see https://github.com/phpseclib/phpseclib/pull/1143
eval('$func = function ($_action, $_text) { ' .
$init_crypt . 'if ($_action == "encrypt") { ' .
$encrypt . ' } else { ' . $decrypt . ' }};');
return \Closure::bind($func, $this, static::class);
}
/**
* Convert float to int
*
* On ARM CPUs converting floats to ints doesn't always work
*
* @param string $x
* @return int
*/
protected static function safe_intval($x)
{
if (is_int($x)) {
return $x;
}
if (self::$use_reg_intval) {
return PHP_INT_SIZE == 4 && PHP_VERSION_ID >= 80100
? intval($x) : $x;
}
return (fmod($x, 0x80000000) & 0x7FFFFFFF) |
((fmod(floor($x / 0x80000000), 2) & 1) << 31);
}
/**
* eval()'able string for in-line float to int
*
* @return string
*/
protected static function safe_intval_inline()
{
if (self::$use_reg_intval) {
return PHP_INT_SIZE == 4 && PHP_VERSION_ID >= 80100
? 'intval(%s)' : '%s';
}
$safeint = '(is_int($temp = %s) ? $temp : (fmod($temp,
0x80000000) & 0x7FFFFFFF) | ';
return $safeint . '((fmod(floor($temp / 0x80000000), 2) &
1) << 31))';
}
/**
* Sets up GCM parameters
*
* See steps 1-2 of
https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=23
* for more info
*
*/
private function setupGCM()
{
// don't keep on re-calculating $this->h
if (!$this->h || $this->hKey != $this->key) {
$cipher = new static('ecb');
$cipher->setKey($this->key);
$cipher->disablePadding();
$this->h = self::$gcmField->newInteger(
Strings::switchEndianness($cipher->encrypt("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"))
);
$this->hKey = $this->key;
}
if (strlen($this->nonce) == 12) {
$this->iv = $this->nonce . "\0\0\0\1";
} else {
$this->iv = $this->ghash(
self::nullPad128($this->nonce) .
str_repeat("\0", 8) . self::len64($this->nonce)
);
}
}
/**
* Performs GHASH operation
*
* See
https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=20
* for more info
*
* @see self::decrypt()
* @see self::encrypt()
* @param string $x
* @return string
*/
private function ghash($x)
{
$h = $this->h;
$y = ["\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"];
$x = str_split($x, 16);
$n = 0;
// the switchEndianness calls are necessary because the
multiplication algorithm in BinaryField/Integer
// interprets strings as polynomials in big endian order whereas in
GCM they're interpreted in little
// endian order per
https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=19.
// big endian order is what binary field elliptic curves use per
http://www.secg.org/sec1-v2.pdf#page=18.
// we could switchEndianness here instead of in the while loop but
doing so in the while loop seems like it
// might be slightly more performant
//$x = Strings::switchEndianness($x);
foreach ($x as $xn) {
$xn = Strings::switchEndianness($xn);
$t = $y[$n] ^ $xn;
$temp = self::$gcmField->newInteger($t);
$y[++$n] = $temp->multiply($h)->toBytes();
$y[$n] = substr($y[$n], 1);
}
$y[$n] = Strings::switchEndianness($y[$n]);
return $y[$n];
}
/**
* Returns the bit length of a string in a packed format
*
* @see self::decrypt()
* @see self::encrypt()
* @see self::setupGCM()
* @param string $str
* @return string
*/
private static function len64($str)
{
return "\0\0\0\0" . pack('N', 8 *
strlen($str));
}
/**
* NULL pads a string to be a multiple of 128
*
* @see self::decrypt()
* @see self::encrypt()
* @see self::setupGCM()
* @param string $str
* @return string
*/
protected static function nullPad128($str)
{
$len = strlen($str);
return $str . str_repeat("\0", 16 * ceil($len / 16) -
$len);
}
/**
* Calculates Poly1305 MAC
*
* On my system ChaCha20, with libsodium, takes 0.5s. With this custom
Poly1305 implementation
* it takes 1.2s.
*
* @see self::decrypt()
* @see self::encrypt()
* @param string $text
* @return string
*/
protected function poly1305($text)
{
$s = $this->poly1305Key; // strlen($this->poly1305Key) == 32
$r = Strings::shift($s, 16);
$r = strrev($r);
$r &=
"\x0f\xff\xff\xfc\x0f\xff\xff\xfc\x0f\xff\xff\xfc\x0f\xff\xff\xff";
$s = strrev($s);
$r = self::$poly1305Field->newInteger(new BigInteger($r, 256));
$s = self::$poly1305Field->newInteger(new BigInteger($s, 256));
$a = self::$poly1305Field->newInteger(new BigInteger());
$blocks = str_split($text, 16);
foreach ($blocks as $block) {
$n = strrev($block . chr(1));
$n = self::$poly1305Field->newInteger(new BigInteger($n,
256));
$a = $a->add($n);
$a = $a->multiply($r);
}
$r = $a->toBigInteger()->add($s->toBigInteger());
$mask =
"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
return strrev($r->toBytes()) & $mask;
}
/**
* Return the mode
*
* You can do $obj instanceof AES or whatever to get the cipher but you
can't do that to get the mode
*
* @return string
*/
public function getMode()
{
return array_flip(self::MODE_MAP)[$this->mode];
}
/**
* Is the continuous buffer enabled?
*
* @return boolean
*/
public function continuousBufferEnabled()
{
return $this->continuousBuffer;
}
}
phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS1.php000064400000004074151161424250015750
0ustar00<?php
/**
* "PKCS1" Formatted EC Key Handler
*
* PHP version 5
*
* Processes keys with the following headers:
*
* -----BEGIN DH PARAMETERS-----
*
* Technically, PKCS1 is for RSA keys, only, but we're using PKCS1 to
describe
* DSA, whose format isn't really formally described anywhere, so
might as well
* use it to describe this, too.
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\DH\Formats\Keys;
use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
use phpseclib3\File\ASN1;
use phpseclib3\File\ASN1\Maps;
use phpseclib3\Math\BigInteger;
/**
* "PKCS1" Formatted DH Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PKCS1 extends Progenitor
{
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
$key = parent::load($key, $password);
$decoded = ASN1::decodeBER($key);
if (!$decoded) {
throw new \RuntimeException('Unable to decode BER');
}
$components = ASN1::asn1map($decoded[0], Maps\DHParameter::MAP);
if (!is_array($components)) {
throw new \RuntimeException('Unable to perform ASN1
mapping on parameters');
}
return $components;
}
/**
* Convert EC parameters to the appropriate format
*
* @return string
*/
public static function saveParameters(BigInteger $prime, BigInteger
$base, array $options = [])
{
$params = [
'prime' => $prime,
'base' => $base
];
$params = ASN1::encodeDER($params, Maps\DHParameter::MAP);
return "-----BEGIN DH PARAMETERS-----\r\n" .
chunk_split(base64_encode($params), 64) .
"-----END DH PARAMETERS-----\r\n";
}
}
phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS8.php000064400000007552151161424250015763
0ustar00<?php
/**
* PKCS#8 Formatted DH Key Handler
*
* PHP version 5
*
* Processes keys with the following headers:
*
* -----BEGIN ENCRYPTED PRIVATE KEY-----
* -----BEGIN PRIVATE KEY-----
* -----BEGIN PUBLIC KEY-----
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\DH\Formats\Keys;
use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
use phpseclib3\File\ASN1;
use phpseclib3\File\ASN1\Maps;
use phpseclib3\Math\BigInteger;
/**
* PKCS#8 Formatted DH Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PKCS8 extends Progenitor
{
/**
* OID Name
*
* @var string
*/
const OID_NAME = 'dhKeyAgreement';
/**
* OID Value
*
* @var string
*/
const OID_VALUE = '1.2.840.113549.1.3.1';
/**
* Child OIDs loaded
*
* @var bool
*/
protected static $childOIDsLoaded = false;
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
$key = parent::load($key, $password);
$type = isset($key['privateKey']) ?
'privateKey' : 'publicKey';
$decoded = ASN1::decodeBER($key[$type .
'Algorithm']['parameters']->element);
if (empty($decoded)) {
throw new \RuntimeException('Unable to decode BER of
parameters');
}
$components = ASN1::asn1map($decoded[0], Maps\DHParameter::MAP);
if (!is_array($components)) {
throw new \RuntimeException('Unable to perform ASN1
mapping on parameters');
}
$decoded = ASN1::decodeBER($key[$type]);
switch (true) {
case !isset($decoded):
case !isset($decoded[0]['content']):
case !$decoded[0]['content'] instanceof BigInteger:
throw new \RuntimeException('Unable to decode BER of
parameters');
}
$components[$type] = $decoded[0]['content'];
return $components;
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $prime
* @param \phpseclib3\Math\BigInteger $base
* @param \phpseclib3\Math\BigInteger $privateKey
* @param \phpseclib3\Math\BigInteger $publicKey
* @param string $password optional
* @param array $options optional
* @return string
*/
public static function savePrivateKey(BigInteger $prime, BigInteger
$base, BigInteger $privateKey, BigInteger $publicKey, $password =
'', array $options = [])
{
$params = [
'prime' => $prime,
'base' => $base
];
$params = ASN1::encodeDER($params, Maps\DHParameter::MAP);
$params = new ASN1\Element($params);
$key = ASN1::encodeDER($privateKey, ['type' =>
ASN1::TYPE_INTEGER]);
return self::wrapPrivateKey($key, [], $params, $password, null,
'', $options);
}
/**
* Convert a public key to the appropriate format
*
* @param \phpseclib3\Math\BigInteger $prime
* @param \phpseclib3\Math\BigInteger $base
* @param \phpseclib3\Math\BigInteger $publicKey
* @param array $options optional
* @return string
*/
public static function savePublicKey(BigInteger $prime, BigInteger
$base, BigInteger $publicKey, array $options = [])
{
$params = [
'prime' => $prime,
'base' => $base
];
$params = ASN1::encodeDER($params, Maps\DHParameter::MAP);
$params = new ASN1\Element($params);
$key = ASN1::encodeDER($publicKey, ['type' =>
ASN1::TYPE_INTEGER]);
return self::wrapPublicKey($key, $params);
}
}
phpseclib/phpseclib/Crypt/DH/Parameters.php000064400000001417151161424250014702
0ustar00<?php
/**
* DH Parameters
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\DH;
use phpseclib3\Crypt\DH;
/**
* DH Parameters
*
* @author Jim Wigginton <terrafrost@php.net>
*/
final class Parameters extends DH
{
/**
* Returns the parameters
*
* @param string $type
* @param array $options optional
* @return string
*/
public function toString($type = 'PKCS1', array $options =
[])
{
$type = self::validatePlugin('Keys', 'PKCS1',
'saveParameters');
return $type::saveParameters($this->prime, $this->base,
$options);
}
}
phpseclib/phpseclib/Crypt/DH/PrivateKey.php000064400000003270151161424250014661
0ustar00<?php
/**
* DH Private Key
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\DH;
use phpseclib3\Crypt\Common;
use phpseclib3\Crypt\DH;
/**
* DH Private Key
*
* @author Jim Wigginton <terrafrost@php.net>
*/
final class PrivateKey extends DH
{
use Common\Traits\PasswordProtected;
/**
* Private Key
*
* @var \phpseclib3\Math\BigInteger
*/
protected $privateKey;
/**
* Public Key
*
* @var \phpseclib3\Math\BigInteger
*/
protected $publicKey;
/**
* Returns the public key
*
* @return DH\PublicKey
*/
public function getPublicKey()
{
$type = self::validatePlugin('Keys', 'PKCS8',
'savePublicKey');
if (!isset($this->publicKey)) {
$this->publicKey =
$this->base->powMod($this->privateKey, $this->prime);
}
$key = $type::savePublicKey($this->prime, $this->base,
$this->publicKey);
return DH::loadFormat('PKCS8', $key);
}
/**
* Returns the private key
*
* @param string $type
* @param array $options optional
* @return string
*/
public function toString($type, array $options = [])
{
$type = self::validatePlugin('Keys', $type,
'savePrivateKey');
if (!isset($this->publicKey)) {
$this->publicKey =
$this->base->powMod($this->privateKey, $this->prime);
}
return $type::savePrivateKey($this->prime, $this->base,
$this->privateKey, $this->publicKey, $this->password, $options);
}
}
phpseclib/phpseclib/Crypt/DH/PublicKey.php000064400000002024151161424250014461
0ustar00<?php
/**
* DH Public Key
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\DH;
use phpseclib3\Crypt\Common;
use phpseclib3\Crypt\DH;
/**
* DH Public Key
*
* @author Jim Wigginton <terrafrost@php.net>
*/
final class PublicKey extends DH
{
use Common\Traits\Fingerprint;
/**
* Returns the public key
*
* @param string $type
* @param array $options optional
* @return string
*/
public function toString($type, array $options = [])
{
$type = self::validatePlugin('Keys', $type,
'savePublicKey');
return $type::savePublicKey($this->prime, $this->base,
$this->publicKey, $options);
}
/**
* Returns the public key as a BigInteger
*
* @return \phpseclib3\Math\BigInteger
*/
public function toBigInteger()
{
return $this->publicKey;
}
}
phpseclib/phpseclib/Crypt/DSA/Formats/Keys/OpenSSH.php000064400000007232151161424250016521
0ustar00<?php
/**
* OpenSSH Formatted DSA Key Handler
*
* PHP version 5
*
* Place in $HOME/.ssh/authorized_keys
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\DSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\OpenSSH as Progenitor;
use phpseclib3\Math\BigInteger;
/**
* OpenSSH Formatted DSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class OpenSSH extends Progenitor
{
/**
* Supported Key Types
*
* @var array
*/
protected static $types = ['ssh-dss'];
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
$parsed = parent::load($key, $password);
if (isset($parsed['paddedKey'])) {
list($type) = Strings::unpackSSH2('s',
$parsed['paddedKey']);
if ($type != $parsed['type']) {
throw new \RuntimeException("The public and private
keys are not of the same type ($type vs $parsed[type])");
}
list($p, $q, $g, $y, $x, $comment) =
Strings::unpackSSH2('i5s', $parsed['paddedKey']);
return compact('p', 'q', 'g',
'y', 'x', 'comment');
}
list($p, $q, $g, $y) = Strings::unpackSSH2('iiii',
$parsed['publicKey']);
$comment = $parsed['comment'];
return compact('p', 'q', 'g',
'y', 'comment');
}
/**
* Convert a public key to the appropriate format
*
* @param \phpseclib3\Math\BigInteger $p
* @param \phpseclib3\Math\BigInteger $q
* @param \phpseclib3\Math\BigInteger $g
* @param \phpseclib3\Math\BigInteger $y
* @param array $options optional
* @return string
*/
public static function savePublicKey(BigInteger $p, BigInteger $q,
BigInteger $g, BigInteger $y, array $options = [])
{
if ($q->getLength() != 160) {
throw new \InvalidArgumentException('SSH only supports
keys with an N (length of Group Order q) of 160');
}
// from <http://tools.ietf.org/html/rfc4253#page-15>:
// string "ssh-dss"
// mpint p
// mpint q
// mpint g
// mpint y
$DSAPublicKey = Strings::packSSH2('siiii',
'ssh-dss', $p, $q, $g, $y);
if (isset($options['binary']) ?
$options['binary'] : self::$binary) {
return $DSAPublicKey;
}
$comment = isset($options['comment']) ?
$options['comment'] : self::$comment;
$DSAPublicKey = 'ssh-dss ' . base64_encode($DSAPublicKey)
. ' ' . $comment;
return $DSAPublicKey;
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $p
* @param \phpseclib3\Math\BigInteger $q
* @param \phpseclib3\Math\BigInteger $g
* @param \phpseclib3\Math\BigInteger $y
* @param \phpseclib3\Math\BigInteger $x
* @param string $password optional
* @param array $options optional
* @return string
*/
public static function savePrivateKey(BigInteger $p, BigInteger $q,
BigInteger $g, BigInteger $y, BigInteger $x, $password = '',
array $options = [])
{
$publicKey = self::savePublicKey($p, $q, $g, $y,
['binary' => true]);
$privateKey = Strings::packSSH2('si5',
'ssh-dss', $p, $q, $g, $y, $x);
return self::wrapPrivateKey($publicKey, $privateKey, $password,
$options);
}
}
phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS1.php000064400000007767151161424250016100
0ustar00<?php
/**
* PKCS#1 Formatted DSA Key Handler
*
* PHP version 5
*
* Used by File/X509.php
*
* Processes keys with the following headers:
*
* -----BEGIN DSA PRIVATE KEY-----
* -----BEGIN DSA PUBLIC KEY-----
* -----BEGIN DSA PARAMETERS-----
*
* Analogous to ssh-keygen's pem format (as specified by -m)
*
* Also, technically, PKCS1 decribes RSA but I am not aware of a formal
specification for DSA.
* The DSA private key format seems to have been adapted from the RSA
private key format so
* we're just re-using that as the name.
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\DSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
use phpseclib3\File\ASN1;
use phpseclib3\File\ASN1\Maps;
use phpseclib3\Math\BigInteger;
/**
* PKCS#1 Formatted DSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PKCS1 extends Progenitor
{
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
$key = parent::load($key, $password);
$decoded = ASN1::decodeBER($key);
if (!$decoded) {
throw new \RuntimeException('Unable to decode BER');
}
$key = ASN1::asn1map($decoded[0], Maps\DSAParams::MAP);
if (is_array($key)) {
return $key;
}
$key = ASN1::asn1map($decoded[0], Maps\DSAPrivateKey::MAP);
if (is_array($key)) {
return $key;
}
$key = ASN1::asn1map($decoded[0], Maps\DSAPublicKey::MAP);
if (is_array($key)) {
return $key;
}
throw new \RuntimeException('Unable to perform ASN1
mapping');
}
/**
* Convert DSA parameters to the appropriate format
*
* @param \phpseclib3\Math\BigInteger $p
* @param \phpseclib3\Math\BigInteger $q
* @param \phpseclib3\Math\BigInteger $g
* @return string
*/
public static function saveParameters(BigInteger $p, BigInteger $q,
BigInteger $g)
{
$key = [
'p' => $p,
'q' => $q,
'g' => $g
];
$key = ASN1::encodeDER($key, Maps\DSAParams::MAP);
return "-----BEGIN DSA PARAMETERS-----\r\n" .
chunk_split(Strings::base64_encode($key), 64) .
"-----END DSA PARAMETERS-----\r\n";
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $p
* @param \phpseclib3\Math\BigInteger $q
* @param \phpseclib3\Math\BigInteger $g
* @param \phpseclib3\Math\BigInteger $y
* @param \phpseclib3\Math\BigInteger $x
* @param string $password optional
* @param array $options optional
* @return string
*/
public static function savePrivateKey(BigInteger $p, BigInteger $q,
BigInteger $g, BigInteger $y, BigInteger $x, $password = '',
array $options = [])
{
$key = [
'version' => 0,
'p' => $p,
'q' => $q,
'g' => $g,
'y' => $y,
'x' => $x
];
$key = ASN1::encodeDER($key, Maps\DSAPrivateKey::MAP);
return self::wrapPrivateKey($key, 'DSA', $password,
$options);
}
/**
* Convert a public key to the appropriate format
*
* @param \phpseclib3\Math\BigInteger $p
* @param \phpseclib3\Math\BigInteger $q
* @param \phpseclib3\Math\BigInteger $g
* @param \phpseclib3\Math\BigInteger $y
* @return string
*/
public static function savePublicKey(BigInteger $p, BigInteger $q,
BigInteger $g, BigInteger $y)
{
$key = ASN1::encodeDER($y, Maps\DSAPublicKey::MAP);
return self::wrapPublicKey($key, 'DSA');
}
}
phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS8.php000064400000010401151161424250016062
0ustar00<?php
/**
* PKCS#8 Formatted DSA Key Handler
*
* PHP version 5
*
* Processes keys with the following headers:
*
* -----BEGIN ENCRYPTED PRIVATE KEY-----
* -----BEGIN PRIVATE KEY-----
* -----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)
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\DSA\Formats\Keys;
use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
use phpseclib3\File\ASN1;
use phpseclib3\File\ASN1\Maps;
use phpseclib3\Math\BigInteger;
/**
* PKCS#8 Formatted DSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PKCS8 extends Progenitor
{
/**
* OID Name
*
* @var string
*/
const OID_NAME = 'id-dsa';
/**
* OID Value
*
* @var string
*/
const OID_VALUE = '1.2.840.10040.4.1';
/**
* Child OIDs loaded
*
* @var bool
*/
protected static $childOIDsLoaded = false;
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
$key = parent::load($key, $password);
$type = isset($key['privateKey']) ?
'privateKey' : 'publicKey';
$decoded = ASN1::decodeBER($key[$type .
'Algorithm']['parameters']->element);
if (!$decoded) {
throw new \RuntimeException('Unable to decode BER of
parameters');
}
$components = ASN1::asn1map($decoded[0], Maps\DSAParams::MAP);
if (!is_array($components)) {
throw new \RuntimeException('Unable to perform ASN1
mapping on parameters');
}
$decoded = ASN1::decodeBER($key[$type]);
if (empty($decoded)) {
throw new \RuntimeException('Unable to decode BER');
}
$var = $type == 'privateKey' ? 'x' :
'y';
$components[$var] = ASN1::asn1map($decoded[0],
Maps\DSAPublicKey::MAP);
if (!$components[$var] instanceof BigInteger) {
throw new \RuntimeException('Unable to perform ASN1
mapping');
}
if (isset($key['meta'])) {
$components['meta'] = $key['meta'];
}
return $components;
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $p
* @param \phpseclib3\Math\BigInteger $q
* @param \phpseclib3\Math\BigInteger $g
* @param \phpseclib3\Math\BigInteger $y
* @param \phpseclib3\Math\BigInteger $x
* @param string $password optional
* @param array $options optional
* @return string
*/
public static function savePrivateKey(BigInteger $p, BigInteger $q,
BigInteger $g, BigInteger $y, BigInteger $x, $password = '',
array $options = [])
{
$params = [
'p' => $p,
'q' => $q,
'g' => $g
];
$params = ASN1::encodeDER($params, Maps\DSAParams::MAP);
$params = new ASN1\Element($params);
$key = ASN1::encodeDER($x, Maps\DSAPublicKey::MAP);
return self::wrapPrivateKey($key, [], $params, $password, null,
'', $options);
}
/**
* Convert a public key to the appropriate format
*
* @param \phpseclib3\Math\BigInteger $p
* @param \phpseclib3\Math\BigInteger $q
* @param \phpseclib3\Math\BigInteger $g
* @param \phpseclib3\Math\BigInteger $y
* @param array $options optional
* @return string
*/
public static function savePublicKey(BigInteger $p, BigInteger $q,
BigInteger $g, BigInteger $y, array $options = [])
{
$params = [
'p' => $p,
'q' => $q,
'g' => $g
];
$params = ASN1::encodeDER($params, Maps\DSAParams::MAP);
$params = new ASN1\Element($params);
$key = ASN1::encodeDER($y, Maps\DSAPublicKey::MAP);
return self::wrapPublicKey($key, $params);
}
}
phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PuTTY.php000064400000006576151161424250016241
0ustar00<?php
/**
* PuTTY Formatted DSA Key Handler
*
* puttygen does not generate DSA keys with an N of anything other than
160, however,
* it can still load them and convert them. PuTTY will load them, too, but
SSH servers
* won't accept them. Since PuTTY formatted keys are primarily used
with SSH this makes
* keys with N > 160 kinda useless, hence this handlers not supporting
such keys.
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\DSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\PuTTY as Progenitor;
use phpseclib3\Math\BigInteger;
/**
* PuTTY Formatted DSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PuTTY extends Progenitor
{
/**
* Public Handler
*
* @var string
*/
const PUBLIC_HANDLER =
'phpseclib3\Crypt\DSA\Formats\Keys\OpenSSH';
/**
* Algorithm Identifier
*
* @var array
*/
protected static $types = ['ssh-dss'];
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
$components = parent::load($key, $password);
if (!isset($components['private'])) {
return $components;
}
extract($components);
unset($components['public'],
$components['private']);
list($p, $q, $g, $y) = Strings::unpackSSH2('iiii',
$public);
list($x) = Strings::unpackSSH2('i', $private);
return compact('p', 'q', 'g',
'y', 'x', 'comment');
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $p
* @param \phpseclib3\Math\BigInteger $q
* @param \phpseclib3\Math\BigInteger $g
* @param \phpseclib3\Math\BigInteger $y
* @param \phpseclib3\Math\BigInteger $x
* @param string $password optional
* @param array $options optional
* @return string
*/
public static function savePrivateKey(BigInteger $p, BigInteger $q,
BigInteger $g, BigInteger $y, BigInteger $x, $password = false, array
$options = [])
{
if ($q->getLength() != 160) {
throw new \InvalidArgumentException('SSH only supports
keys with an N (length of Group Order q) of 160');
}
$public = Strings::packSSH2('iiii', $p, $q, $g, $y);
$private = Strings::packSSH2('i', $x);
return self::wrapPrivateKey($public, $private, 'ssh-dss',
$password, $options);
}
/**
* Convert a public key to the appropriate format
*
* @param \phpseclib3\Math\BigInteger $p
* @param \phpseclib3\Math\BigInteger $q
* @param \phpseclib3\Math\BigInteger $g
* @param \phpseclib3\Math\BigInteger $y
* @return string
*/
public static function savePublicKey(BigInteger $p, BigInteger $q,
BigInteger $g, BigInteger $y)
{
if ($q->getLength() != 160) {
throw new \InvalidArgumentException('SSH only supports
keys with an N (length of Group Order q) of 160');
}
return self::wrapPublicKey(Strings::packSSH2('iiii', $p,
$q, $g, $y), 'ssh-dss');
}
}
phpseclib/phpseclib/Crypt/DSA/Formats/Keys/Raw.php000064400000004775151161424250016004
0ustar00<?php
/**
* Raw DSA Key Handler
*
* PHP version 5
*
* Reads and creates arrays as DSA keys
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\DSA\Formats\Keys;
use phpseclib3\Math\BigInteger;
/**
* Raw DSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Raw
{
/**
* Break a public or private key down into its constituent components
*
* @param array $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
if (!is_array($key)) {
throw new \UnexpectedValueException('Key should be a array
- not a ' . gettype($key));
}
switch (true) {
case !isset($key['p']) || !isset($key['q'])
|| !isset($key['g']):
case !$key['p'] instanceof BigInteger:
case !$key['q'] instanceof BigInteger:
case !$key['g'] instanceof BigInteger:
case !isset($key['x']) &&
!isset($key['y']):
case isset($key['x']) && !$key['x']
instanceof BigInteger:
case isset($key['y']) && !$key['y']
instanceof BigInteger:
throw new \UnexpectedValueException('Key appears to be
malformed');
}
$options = ['p' => 1, 'q' => 1,
'g' => 1, 'x' => 1, 'y' => 1];
return array_intersect_key($key, $options);
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $p
* @param \phpseclib3\Math\BigInteger $q
* @param \phpseclib3\Math\BigInteger $g
* @param \phpseclib3\Math\BigInteger $y
* @param \phpseclib3\Math\BigInteger $x
* @param string $password optional
* @return string
*/
public static function savePrivateKey(BigInteger $p, BigInteger $q,
BigInteger $g, BigInteger $y, BigInteger $x, $password = '')
{
return compact('p', 'q', 'g',
'y', 'x');
}
/**
* Convert a public key to the appropriate format
*
* @param \phpseclib3\Math\BigInteger $p
* @param \phpseclib3\Math\BigInteger $q
* @param \phpseclib3\Math\BigInteger $g
* @param \phpseclib3\Math\BigInteger $y
* @return string
*/
public static function savePublicKey(BigInteger $p, BigInteger $q,
BigInteger $g, BigInteger $y)
{
return compact('p', 'q', 'g',
'y');
}
}
phpseclib/phpseclib/Crypt/DSA/Formats/Keys/XML.php000064400000012121151161424250015673
0ustar00<?php
/**
* XML Formatted DSA Key Handler
*
* While XKMS defines a private key format for RSA it does not do so for
DSA. Quoting that standard:
*
* "[XKMS] does not specify private key parameters for the DSA
signature algorithm since the algorithm only
* supports signature modes and so the application of server generated
keys and key recovery is of limited
* value"
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\DSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Exception\BadConfigurationException;
use phpseclib3\Math\BigInteger;
/**
* XML Formatted DSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class XML
{
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
if (!Strings::is_stringable($key)) {
throw new \UnexpectedValueException('Key should be a
string - not a ' . gettype($key));
}
if (!class_exists('DOMDocument')) {
throw new BadConfigurationException('The dom extension is
not setup correctly on this system');
}
$use_errors = libxml_use_internal_errors(true);
$dom = new \DOMDocument();
if (substr($key, 0, 5) != '<?xml') {
$key = '<xml>' . $key .
'</xml>';
}
if (!$dom->loadXML($key)) {
libxml_use_internal_errors($use_errors);
throw new \UnexpectedValueException('Key does not appear
to contain XML');
}
$xpath = new \DOMXPath($dom);
$keys = ['p', 'q', 'g',
'y', 'j', 'seed', 'pgencounter'];
foreach ($keys as $key) {
// $dom->getElementsByTagName($key) is case-sensitive
$temp = $xpath->query("//*[translate(local-name(),
'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$key']");
if (!$temp->length) {
continue;
}
$value = new
BigInteger(Strings::base64_decode($temp->item(0)->nodeValue), 256);
switch ($key) {
case 'p': // a prime modulus meeting the [DSS]
requirements
// Parameters P, Q, and G can be public and common to a
group of users. They might be known
// from application context. As such, they are optional
but P and Q must either both appear
// or both be absent
$components['p'] = $value;
break;
case 'q': // an integer in the range 2**159 <
Q < 2**160 which is a prime divisor of P-1
$components['q'] = $value;
break;
case 'g': // an integer with certain properties
with respect to P and Q
$components['g'] = $value;
break;
case 'y': // G**X mod P (where X is part of the
private key and not made public)
$components['y'] = $value;
// the remaining options do not do anything
case 'j': // (P - 1) / Q
// Parameter J is available for inclusion solely for
efficiency as it is calculatable from
// P and Q
case 'seed': // a DSA prime generation seed
// Parameters seed and pgenCounter are used in the DSA
prime number generation algorithm
// specified in [DSS]. As such, they are optional but
must either both be present or both
// be absent
case 'pgencounter': // a DSA prime generation
counter
}
}
libxml_use_internal_errors($use_errors);
if (!isset($components['y'])) {
throw new \UnexpectedValueException('Key is missing y
component');
}
switch (true) {
case !isset($components['p']):
case !isset($components['q']):
case !isset($components['g']):
return ['y' => $components['y']];
}
return $components;
}
/**
* Convert a public key to the appropriate format
*
* See https://www.w3.org/TR/xmldsig-core/#sec-DSAKeyValue
*
* @param \phpseclib3\Math\BigInteger $p
* @param \phpseclib3\Math\BigInteger $q
* @param \phpseclib3\Math\BigInteger $g
* @param \phpseclib3\Math\BigInteger $y
* @return string
*/
public static function savePublicKey(BigInteger $p, BigInteger $q,
BigInteger $g, BigInteger $y)
{
return "<DSAKeyValue>\r\n" .
' <P>' .
Strings::base64_encode($p->toBytes()) . "</P>\r\n" .
' <Q>' .
Strings::base64_encode($q->toBytes()) . "</Q>\r\n" .
' <G>' .
Strings::base64_encode($g->toBytes()) . "</G>\r\n" .
' <Y>' .
Strings::base64_encode($y->toBytes()) . "</Y>\r\n" .
'</DSAKeyValue>';
}
}
phpseclib/phpseclib/Crypt/DSA/Formats/Signature/ASN1.php000064400000002606151161424250016772
0ustar00<?php
/**
* ASN1 Signature Handler
*
* PHP version 5
*
* Handles signatures in the format described in
* https://tools.ietf.org/html/rfc3279#section-2.2.2
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\DSA\Formats\Signature;
use phpseclib3\File\ASN1 as Encoder;
use phpseclib3\File\ASN1\Maps;
use phpseclib3\Math\BigInteger;
/**
* ASN1 Signature Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class ASN1
{
/**
* Loads a signature
*
* @param string $sig
* @return array|bool
*/
public static function load($sig)
{
if (!is_string($sig)) {
return false;
}
$decoded = Encoder::decodeBER($sig);
if (empty($decoded)) {
return false;
}
$components = Encoder::asn1map($decoded[0], Maps\DssSigValue::MAP);
return $components;
}
/**
* Returns a signature in the appropriate format
*
* @param \phpseclib3\Math\BigInteger $r
* @param \phpseclib3\Math\BigInteger $s
* @return string
*/
public static function save(BigInteger $r, BigInteger $s)
{
return Encoder::encodeDER(compact('r', 's'),
Maps\DssSigValue::MAP);
}
}
phpseclib/phpseclib/Crypt/DSA/Formats/Signature/Raw.php000064400000001012151161424250017007
0ustar00<?php
/**
* Raw DSA Signature Handler
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\DSA\Formats\Signature;
use phpseclib3\Crypt\Common\Formats\Signature\Raw as Progenitor;
/**
* Raw DSA Signature Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Raw extends Progenitor
{
}
phpseclib/phpseclib/Crypt/DSA/Formats/Signature/SSH2.php000064400000003313151161424250017003
0ustar00<?php
/**
* SSH2 Signature Handler
*
* PHP version 5
*
* Handles signatures in the format used by SSH2
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\DSA\Formats\Signature;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Math\BigInteger;
/**
* SSH2 Signature Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class SSH2
{
/**
* Loads a signature
*
* @param string $sig
* @return mixed
*/
public static function load($sig)
{
if (!is_string($sig)) {
return false;
}
$result = Strings::unpackSSH2('ss', $sig);
if ($result === false) {
return false;
}
list($type, $blob) = $result;
if ($type != 'ssh-dss' || strlen($blob) != 40) {
return false;
}
return [
'r' => new BigInteger(substr($blob, 0, 20), 256),
's' => new BigInteger(substr($blob, 20), 256)
];
}
/**
* Returns a signature in the appropriate format
*
* @param \phpseclib3\Math\BigInteger $r
* @param \phpseclib3\Math\BigInteger $s
* @return string
*/
public static function save(BigInteger $r, BigInteger $s)
{
if ($r->getLength() > 160 || $s->getLength() > 160) {
return false;
}
return Strings::packSSH2(
'ss',
'ssh-dss',
str_pad($r->toBytes(), 20, "\0", STR_PAD_LEFT) .
str_pad($s->toBytes(), 20, "\0", STR_PAD_LEFT)
);
}
}
phpseclib/phpseclib/Crypt/DSA/Parameters.php000064400000001427151161424250015017
0ustar00<?php
/**
* DSA Parameters
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\DSA;
use phpseclib3\Crypt\DSA;
/**
* DSA Parameters
*
* @author Jim Wigginton <terrafrost@php.net>
*/
final class Parameters extends DSA
{
/**
* Returns the parameters
*
* @param string $type
* @param array $options optional
* @return string
*/
public function toString($type = 'PKCS1', array $options =
[])
{
$type = self::validatePlugin('Keys', 'PKCS1',
'saveParameters');
return $type::saveParameters($this->p, $this->q, $this->g,
$options);
}
}
phpseclib/phpseclib/Crypt/DSA/PrivateKey.php000064400000011361151161424250014775
0ustar00<?php
/**
* DSA Private Key
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\DSA;
use phpseclib3\Crypt\Common;
use phpseclib3\Crypt\DSA;
use phpseclib3\Crypt\DSA\Formats\Signature\ASN1 as ASN1Signature;
use phpseclib3\Math\BigInteger;
/**
* DSA Private Key
*
* @author Jim Wigginton <terrafrost@php.net>
*/
final class PrivateKey extends DSA implements Common\PrivateKey
{
use Common\Traits\PasswordProtected;
/**
* DSA secret exponent x
*
* @var \phpseclib3\Math\BigInteger
*/
protected $x;
/**
* Returns the public key
*
* If you do "openssl rsa -in private.rsa -pubout -outform
PEM" you get a PKCS8 formatted key
* that contains a publicKeyAlgorithm AlgorithmIdentifier and a
publicKey BIT STRING.
* An AlgorithmIdentifier contains an OID and a parameters field. With
RSA public keys this
* parameters field is NULL. With DSA PKCS8 public keys it is not - it
contains the p, q and g
* variables. The publicKey BIT STRING contains, simply, the y
variable. This can be verified
* by getting a DSA PKCS8 public key:
*
* "openssl dsa -in private.dsa -pubout -outform PEM"
*
* ie. just swap out rsa with dsa in the rsa command above.
*
* A PKCS1 public key corresponds to the publicKey portion of the PKCS8
key. In the case of RSA
* the publicKey portion /is/ the key. In the case of DSA it is not.
You cannot verify a signature
* without the parameters and the PKCS1 DSA public key format does not
include the parameters.
*
* @see self::getPrivateKey()
* @return mixed
*/
public function getPublicKey()
{
$type = self::validatePlugin('Keys', 'PKCS8',
'savePublicKey');
if (!isset($this->y)) {
$this->y = $this->g->powMod($this->x, $this->p);
}
$key = $type::savePublicKey($this->p, $this->q, $this->g,
$this->y);
return DSA::loadFormat('PKCS8', $key)
->withHash($this->hash->getHash())
->withSignatureFormat($this->shortFormat);
}
/**
* Create a signature
*
* @see self::verify()
* @param string $message
* @return mixed
*/
public function sign($message)
{
$format = $this->sigFormat;
if (self::$engines['OpenSSL'] &&
in_array($this->hash->getHash(), openssl_get_md_methods())) {
$signature = '';
$result = openssl_sign($message, $signature,
$this->toString('PKCS8'), $this->hash->getHash());
if ($result) {
if ($this->shortFormat == 'ASN1') {
return $signature;
}
extract(ASN1Signature::load($signature));
return $format::save($r, $s);
}
}
$h = $this->hash->hash($message);
$h = $this->bits2int($h);
while (true) {
$k = BigInteger::randomRange(self::$one,
$this->q->subtract(self::$one));
$r = $this->g->powMod($k, $this->p);
list(, $r) = $r->divide($this->q);
if ($r->equals(self::$zero)) {
continue;
}
$kinv = $k->modInverse($this->q);
$temp = $h->add($this->x->multiply($r));
$temp = $kinv->multiply($temp);
list(, $s) = $temp->divide($this->q);
if (!$s->equals(self::$zero)) {
break;
}
}
// the following is an RFC6979 compliant implementation of
deterministic DSA
// it's unused because it's mainly intended for use when
a good CSPRNG isn't
// available. if phpseclib's CSPRNG isn't good then even
key generation is
// suspect
/*
$h1 = $this->hash->hash($message);
$k = $this->computek($h1);
$r = $this->g->powMod($k, $this->p);
list(, $r) = $r->divide($this->q);
$kinv = $k->modInverse($this->q);
$h1 = $this->bits2int($h1);
$temp = $h1->add($this->x->multiply($r));
$temp = $kinv->multiply($temp);
list(, $s) = $temp->divide($this->q);
*/
return $format::save($r, $s);
}
/**
* Returns the private key
*
* @param string $type
* @param array $options optional
* @return string
*/
public function toString($type, array $options = [])
{
$type = self::validatePlugin('Keys', $type,
'savePrivateKey');
if (!isset($this->y)) {
$this->y = $this->g->powMod($this->x, $this->p);
}
return $type::savePrivateKey($this->p, $this->q, $this->g,
$this->y, $this->x, $this->password, $options);
}
}
phpseclib/phpseclib/Crypt/DSA/PublicKey.php000064400000004511151161424250014600
0ustar00<?php
/**
* DSA Public Key
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\DSA;
use phpseclib3\Crypt\Common;
use phpseclib3\Crypt\DSA;
use phpseclib3\Crypt\DSA\Formats\Signature\ASN1 as ASN1Signature;
/**
* DSA Public Key
*
* @author Jim Wigginton <terrafrost@php.net>
*/
final class PublicKey extends DSA implements Common\PublicKey
{
use Common\Traits\Fingerprint;
/**
* Verify a signature
*
* @see self::verify()
* @param string $message
* @param string $signature
* @return mixed
*/
public function verify($message, $signature)
{
$format = $this->sigFormat;
$params = $format::load($signature);
if ($params === false || count($params) != 2) {
return false;
}
extract($params);
if (self::$engines['OpenSSL'] &&
in_array($this->hash->getHash(), openssl_get_md_methods())) {
$sig = $format != 'ASN1' ? ASN1Signature::save($r,
$s) : $signature;
$result = openssl_verify($message, $sig,
$this->toString('PKCS8'), $this->hash->getHash());
if ($result != -1) {
return (bool) $result;
}
}
$q_1 = $this->q->subtract(self::$one);
if (!$r->between(self::$one, $q_1) ||
!$s->between(self::$one, $q_1)) {
return false;
}
$w = $s->modInverse($this->q);
$h = $this->hash->hash($message);
$h = $this->bits2int($h);
list(, $u1) = $h->multiply($w)->divide($this->q);
list(, $u2) = $r->multiply($w)->divide($this->q);
$v1 = $this->g->powMod($u1, $this->p);
$v2 = $this->y->powMod($u2, $this->p);
list(, $v) = $v1->multiply($v2)->divide($this->p);
list(, $v) = $v->divide($this->q);
return $v->equals($r);
}
/**
* Returns the public key
*
* @param string $type
* @param array $options optional
* @return string
*/
public function toString($type, array $options = [])
{
$type = self::validatePlugin('Keys', $type,
'savePublicKey');
return $type::savePublicKey($this->p, $this->q, $this->g,
$this->y, $options);
}
}
phpseclib/phpseclib/Crypt/EC/BaseCurves/Base.php000064400000011214151161424250015503
0ustar00<?php
/**
* Curve methods common to all curves
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\BaseCurves;
use phpseclib3\Math\BigInteger;
/**
* Base
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Base
{
/**
* The Order
*
* @var BigInteger
*/
protected $order;
/**
* Finite Field Integer factory
*
* @var \phpseclib3\Math\FiniteField\Integer
*/
protected $factory;
/**
* Returns a random integer
*
* @return object
*/
public function randomInteger()
{
return $this->factory->randomInteger();
}
/**
* Converts a BigInteger to a \phpseclib3\Math\FiniteField\Integer
integer
*
* @return object
*/
public function convertInteger(BigInteger $x)
{
return $this->factory->newInteger($x);
}
/**
* Returns the length, in bytes, of the modulo
*
* @return integer
*/
public function getLengthInBytes()
{
return $this->factory->getLengthInBytes();
}
/**
* Returns the length, in bits, of the modulo
*
* @return integer
*/
public function getLength()
{
return $this->factory->getLength();
}
/**
* Multiply a point on the curve by a scalar
*
* Uses the montgomery ladder technique as described here:
*
*
https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Montgomery_ladder
* https://github.com/phpecc/phpecc/issues/16#issuecomment-59176772
*
* @return array
*/
public function multiplyPoint(array $p, BigInteger $d)
{
$alreadyInternal = isset($p[2]);
$r = $alreadyInternal ?
[[], $p] :
[[], $this->convertToInternal($p)];
$d = $d->toBits();
for ($i = 0; $i < strlen($d); $i++) {
$d_i = (int) $d[$i];
$r[1 - $d_i] = $this->addPoint($r[0], $r[1]);
$r[$d_i] = $this->doublePoint($r[$d_i]);
}
return $alreadyInternal ? $r[0] : $this->convertToAffine($r[0]);
}
/**
* Creates a random scalar multiplier
*
* @return BigInteger
*/
public function createRandomMultiplier()
{
static $one;
if (!isset($one)) {
$one = new BigInteger(1);
}
return BigInteger::randomRange($one,
$this->order->subtract($one));
}
/**
* Performs range check
*/
public function rangeCheck(BigInteger $x)
{
static $zero;
if (!isset($zero)) {
$zero = new BigInteger();
}
if (!isset($this->order)) {
throw new \RuntimeException('setOrder needs to be called
before this method');
}
if ($x->compare($this->order) > 0 || $x->compare($zero)
<= 0) {
throw new \RangeException('x must be between 1 and the
order of the curve');
}
}
/**
* Sets the Order
*/
public function setOrder(BigInteger $order)
{
$this->order = $order;
}
/**
* Returns the Order
*
* @return \phpseclib3\Math\BigInteger
*/
public function getOrder()
{
return $this->order;
}
/**
* Use a custom defined modular reduction function
*
* @return object
*/
public function setReduction(callable $func)
{
$this->factory->setReduction($func);
}
/**
* Returns the affine point
*
* @return object[]
*/
public function convertToAffine(array $p)
{
return $p;
}
/**
* Converts an affine point to a jacobian coordinate
*
* @return object[]
*/
public function convertToInternal(array $p)
{
return $p;
}
/**
* Negates a point
*
* @return object[]
*/
public function negatePoint(array $p)
{
$temp = [
$p[0],
$p[1]->negate()
];
if (isset($p[2])) {
$temp[] = $p[2];
}
return $temp;
}
/**
* Multiply and Add Points
*
* @return int[]
*/
public function multiplyAddPoints(array $points, array $scalars)
{
$p1 = $this->convertToInternal($points[0]);
$p2 = $this->convertToInternal($points[1]);
$p1 = $this->multiplyPoint($p1, $scalars[0]);
$p2 = $this->multiplyPoint($p2, $scalars[1]);
$r = $this->addPoint($p1, $p2);
return $this->convertToAffine($r);
}
}
phpseclib/phpseclib/Crypt/EC/BaseCurves/Binary.php000064400000023114151161424250016057
0ustar00<?php
/**
* Curves over y^2 + x*y = x^3 + a*x^2 + b
*
* These are curves used in SEC 2 over prime fields:
http://www.secg.org/SEC2-Ver-1.0.pdf
* The curve is a weierstrass curve with a[3] and a[2] set to 0.
*
* Uses Jacobian Coordinates for speed if able:
*
* https://en.wikipedia.org/wiki/Jacobian_curve
*
https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\BaseCurves;
use phpseclib3\Math\BigInteger;
use phpseclib3\Math\BinaryField;
use phpseclib3\Math\BinaryField\Integer as BinaryInteger;
/**
* Curves over y^2 + x*y = x^3 + a*x^2 + b
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class Binary extends Base
{
/**
* Binary Field Integer factory
*
* @var \phpseclib3\Math\BinaryField
*/
protected $factory;
/**
* Cofficient for x^1
*
* @var object
*/
protected $a;
/**
* Cofficient for x^0
*
* @var object
*/
protected $b;
/**
* Base Point
*
* @var object
*/
protected $p;
/**
* The number one over the specified finite field
*
* @var object
*/
protected $one;
/**
* The modulo
*
* @var BigInteger
*/
protected $modulo;
/**
* The Order
*
* @var BigInteger
*/
protected $order;
/**
* Sets the modulo
*/
public function setModulo(...$modulo)
{
$this->modulo = $modulo;
$this->factory = new BinaryField(...$modulo);
$this->one = $this->factory->newInteger("\1");
}
/**
* Set coefficients a and b
*
* @param string $a
* @param string $b
*/
public function setCoefficients($a, $b)
{
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
$this->a = $this->factory->newInteger(pack('H*',
$a));
$this->b = $this->factory->newInteger(pack('H*',
$b));
}
/**
* Set x and y coordinates for the base point
*
* @param string|BinaryInteger $x
* @param string|BinaryInteger $y
*/
public function setBasePoint($x, $y)
{
switch (true) {
case !is_string($x) && !$x instanceof BinaryInteger:
throw new \UnexpectedValueException('Argument 1 passed
to Binary::setBasePoint() must be a string or an instance of
BinaryField\Integer');
case !is_string($y) && !$y instanceof BinaryInteger:
throw new \UnexpectedValueException('Argument 2 passed
to Binary::setBasePoint() must be a string or an instance of
BinaryField\Integer');
}
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
$this->p = [
is_string($x) ?
$this->factory->newInteger(pack('H*', $x)) : $x,
is_string($y) ?
$this->factory->newInteger(pack('H*', $y)) : $y
];
}
/**
* Retrieve the base point as an array
*
* @return array
*/
public function getBasePoint()
{
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
/*
if (!isset($this->p)) {
throw new \RuntimeException('setBasePoint needs to be
called before this method');
}
*/
return $this->p;
}
/**
* Adds two points on the curve
*
* @return FiniteField[]
*/
public function addPoint(array $p, array $q)
{
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
if (!count($p) || !count($q)) {
if (count($q)) {
return $q;
}
if (count($p)) {
return $p;
}
return [];
}
if (!isset($p[2]) || !isset($q[2])) {
throw new \RuntimeException('Affine coordinates need to be
manually converted to "Jacobi" coordinates or vice versa');
}
if ($p[0]->equals($q[0])) {
return !$p[1]->equals($q[1]) ? [] :
$this->doublePoint($p);
}
// formulas from
http://hyperelliptic.org/EFD/g12o/auto-shortw-jacobian.html
list($x1, $y1, $z1) = $p;
list($x2, $y2, $z2) = $q;
$o1 = $z1->multiply($z1);
$b = $x2->multiply($o1);
if ($z2->equals($this->one)) {
$d = $y2->multiply($o1)->multiply($z1);
$e = $x1->add($b);
$f = $y1->add($d);
$z3 = $e->multiply($z1);
$h = $f->multiply($x2)->add($z3->multiply($y2));
$i = $f->add($z3);
$g = $z3->multiply($z3);
$p1 = $this->a->multiply($g);
$p2 = $f->multiply($i);
$p3 = $e->multiply($e)->multiply($e);
$x3 = $p1->add($p2)->add($p3);
$y3 = $i->multiply($x3)->add($g->multiply($h));
return [$x3, $y3, $z3];
}
$o2 = $z2->multiply($z2);
$a = $x1->multiply($o2);
$c = $y1->multiply($o2)->multiply($z2);
$d = $y2->multiply($o1)->multiply($z1);
$e = $a->add($b);
$f = $c->add($d);
$g = $e->multiply($z1);
$h = $f->multiply($x2)->add($g->multiply($y2));
$z3 = $g->multiply($z2);
$i = $f->add($z3);
$p1 = $this->a->multiply($z3->multiply($z3));
$p2 = $f->multiply($i);
$p3 = $e->multiply($e)->multiply($e);
$x3 = $p1->add($p2)->add($p3);
$y3 =
$i->multiply($x3)->add($g->multiply($g)->multiply($h));
return [$x3, $y3, $z3];
}
/**
* Doubles a point on a curve
*
* @return FiniteField[]
*/
public function doublePoint(array $p)
{
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
if (!count($p)) {
return [];
}
if (!isset($p[2])) {
throw new \RuntimeException('Affine coordinates need to be
manually converted to "Jacobi" coordinates or vice versa');
}
// formulas from
http://hyperelliptic.org/EFD/g12o/auto-shortw-jacobian.html
list($x1, $y1, $z1) = $p;
$a = $x1->multiply($x1);
$b = $a->multiply($a);
if ($z1->equals($this->one)) {
$x3 = $b->add($this->b);
$z3 = clone $x1;
$p1 = $a->add($y1)->add($z3)->multiply($this->b);
$p2 = $a->add($y1)->multiply($b);
$y3 = $p1->add($p2);
return [$x3, $y3, $z3];
}
$c = $z1->multiply($z1);
$d = $c->multiply($c);
$x3 = $b->add($this->b->multiply($d->multiply($d)));
$z3 = $x1->multiply($c);
$p1 = $b->multiply($z3);
$p2 =
$a->add($y1->multiply($z1))->add($z3)->multiply($x3);
$y3 = $p1->add($p2);
return [$x3, $y3, $z3];
}
/**
* Returns the X coordinate and the derived Y coordinate
*
* Not supported because it is covered by patents.
* Quoting https://www.openssl.org/docs/man1.1.0/apps/ecparam.html ,
*
* "Due to patent issues the compressed option is disabled by
default for binary curves
* and can be enabled by defining the preprocessor macro
OPENSSL_EC_BIN_PT_COMP at
* compile time."
*
* @return array
*/
public function derivePoint($m)
{
throw new \RuntimeException('Point compression on binary
finite field elliptic curves is not supported');
}
/**
* Tests whether or not the x / y values satisfy the equation
*
* @return boolean
*/
public function verifyPoint(array $p)
{
list($x, $y) = $p;
$lhs = $y->multiply($y);
$lhs = $lhs->add($x->multiply($y));
$x2 = $x->multiply($x);
$x3 = $x2->multiply($x);
$rhs =
$x3->add($this->a->multiply($x2))->add($this->b);
return $lhs->equals($rhs);
}
/**
* Returns the modulo
*
* @return \phpseclib3\Math\BigInteger
*/
public function getModulo()
{
return $this->modulo;
}
/**
* Returns the a coefficient
*
* @return \phpseclib3\Math\PrimeField\Integer
*/
public function getA()
{
return $this->a;
}
/**
* Returns the a coefficient
*
* @return \phpseclib3\Math\PrimeField\Integer
*/
public function getB()
{
return $this->b;
}
/**
* Returns the affine point
*
* A Jacobian Coordinate is of the form (x, y, z).
* To convert a Jacobian Coordinate to an Affine Point
* you do (x / z^2, y / z^3)
*
* @return \phpseclib3\Math\PrimeField\Integer[]
*/
public function convertToAffine(array $p)
{
if (!isset($p[2])) {
return $p;
}
list($x, $y, $z) = $p;
$z = $this->one->divide($z);
$z2 = $z->multiply($z);
return [
$x->multiply($z2),
$y->multiply($z2)->multiply($z)
];
}
/**
* Converts an affine point to a jacobian coordinate
*
* @return \phpseclib3\Math\PrimeField\Integer[]
*/
public function convertToInternal(array $p)
{
if (isset($p[2])) {
return $p;
}
$p[2] = clone $this->one;
$p['fresh'] = true;
return $p;
}
}
phpseclib/phpseclib/Crypt/EC/BaseCurves/KoblitzPrime.php000064400000023675151161424250017262
0ustar00<?php
/**
* Generalized Koblitz Curves over y^2 = x^3 + b.
*
* According to http://www.secg.org/SEC2-Ver-1.0.pdf Koblitz curves are
over the GF(2**m)
* finite field. Both the $a$ and $b$ coefficients are either 0 or 1.
However, SEC2
* generalizes the definition to include curves over GF(P) "which
possess an efficiently
* computable endomorphism".
*
* For these generalized Koblitz curves $b$ doesn't have to be 0 or 1.
Whether or not $a$
* has any restrictions on it is unclear, however, for all the GF(P)
Koblitz curves defined
* in SEC2 v1.0 $a$ is $0$ so all of the methods defined herein will assume
that it is.
*
* I suppose we could rename the $b$ coefficient to $a$, however, the
documentation refers
* to $b$ so we'll just keep it.
*
* If a later version of SEC2 comes out wherein some $a$ values are
non-zero we can create a
* new method for those. eg. KoblitzA1Prime.php or something.
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\BaseCurves;
use phpseclib3\Math\BigInteger;
use phpseclib3\Math\PrimeField;
/**
* Curves over y^2 = x^3 + b
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class KoblitzPrime extends Prime
{
/**
* Basis
*
* @var list<array{a: BigInteger, b: BigInteger}>
*/
protected $basis;
/**
* Beta
*
* @var PrimeField\Integer
*/
protected $beta;
// don't overwrite setCoefficients() with one that only accepts
one parameter so that
// one might be able to switch between KoblitzPrime and Prime more
easily (for benchmarking
// purposes).
/**
* Multiply and Add Points
*
* Uses a efficiently computable endomorphism to achieve a slight
speedup
*
* Adapted from:
*
https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/curve/short.js#L219
*
* @return int[]
*/
public function multiplyAddPoints(array $points, array $scalars)
{
static $zero, $one, $two;
if (!isset($two)) {
$two = new BigInteger(2);
$one = new BigInteger(1);
}
if (!isset($this->beta)) {
// get roots
$inv = $this->one->divide($this->two)->negate();
$s =
$this->three->negate()->squareRoot()->multiply($inv);
$betas = [
$inv->add($s),
$inv->subtract($s)
];
$this->beta = $betas[0]->compare($betas[1]) < 0 ?
$betas[0] : $betas[1];
//echo strtoupper($this->beta->toHex(true)) .
"\n"; exit;
}
if (!isset($this->basis)) {
$factory = new PrimeField($this->order);
$tempOne = $factory->newInteger($one);
$tempTwo = $factory->newInteger($two);
$tempThree = $factory->newInteger(new BigInteger(3));
$inv = $tempOne->divide($tempTwo)->negate();
$s =
$tempThree->negate()->squareRoot()->multiply($inv);
$lambdas = [
$inv->add($s),
$inv->subtract($s)
];
$lhs = $this->multiplyPoint($this->p, $lambdas[0])[0];
$rhs = $this->p[0]->multiply($this->beta);
$lambda = $lhs->equals($rhs) ? $lambdas[0] : $lambdas[1];
$this->basis =
static::extendedGCD($lambda->toBigInteger(), $this->order);
///*
foreach ($this->basis as $basis) {
echo strtoupper($basis['a']->toHex(true)) .
"\n";
echo strtoupper($basis['b']->toHex(true)) .
"\n\n";
}
exit;
//*/
}
$npoints = $nscalars = [];
for ($i = 0; $i < count($points); $i++) {
$p = $points[$i];
$k = $scalars[$i]->toBigInteger();
// begin split
list($v1, $v2) = $this->basis;
$c1 = $v2['b']->multiply($k);
list($c1, $r) = $c1->divide($this->order);
if ($this->order->compare($r->multiply($two)) <= 0)
{
$c1 = $c1->add($one);
}
$c2 = $v1['b']->negate()->multiply($k);
list($c2, $r) = $c2->divide($this->order);
if ($this->order->compare($r->multiply($two)) <= 0)
{
$c2 = $c2->add($one);
}
$p1 = $c1->multiply($v1['a']);
$p2 = $c2->multiply($v2['a']);
$q1 = $c1->multiply($v1['b']);
$q2 = $c2->multiply($v2['b']);
$k1 = $k->subtract($p1)->subtract($p2);
$k2 = $q1->add($q2)->negate();
// end split
$beta = [
$p[0]->multiply($this->beta),
$p[1],
clone $this->one
];
if (isset($p['naf'])) {
$beta['naf'] = array_map(function ($p) {
return [
$p[0]->multiply($this->beta),
$p[1],
clone $this->one
];
}, $p['naf']);
$beta['nafwidth'] = $p['nafwidth'];
}
if ($k1->isNegative()) {
$k1 = $k1->negate();
$p = $this->negatePoint($p);
}
if ($k2->isNegative()) {
$k2 = $k2->negate();
$beta = $this->negatePoint($beta);
}
$pos = 2 * $i;
$npoints[$pos] = $p;
$nscalars[$pos] = $this->factory->newInteger($k1);
$pos++;
$npoints[$pos] = $beta;
$nscalars[$pos] = $this->factory->newInteger($k2);
}
return parent::multiplyAddPoints($npoints, $nscalars);
}
/**
* Returns the numerator and denominator of the slope
*
* @return FiniteField[]
*/
protected function doublePointHelper(array $p)
{
$numerator =
$this->three->multiply($p[0])->multiply($p[0]);
$denominator = $this->two->multiply($p[1]);
return [$numerator, $denominator];
}
/**
* Doubles a jacobian coordinate on the curve
*
* See
http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
*
* @return FiniteField[]
*/
protected function jacobianDoublePoint(array $p)
{
list($x1, $y1, $z1) = $p;
$a = $x1->multiply($x1);
$b = $y1->multiply($y1);
$c = $b->multiply($b);
$d = $x1->add($b);
$d =
$d->multiply($d)->subtract($a)->subtract($c)->multiply($this->two);
$e = $this->three->multiply($a);
$f = $e->multiply($e);
$x3 = $f->subtract($this->two->multiply($d));
$y3 = $e->multiply($d->subtract($x3))->subtract(
$this->eight->multiply($c)
);
$z3 = $this->two->multiply($y1)->multiply($z1);
return [$x3, $y3, $z3];
}
/**
* Doubles a "fresh" jacobian coordinate on the curve
*
* See
http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-mdbl-2007-bl
*
* @return FiniteField[]
*/
protected function jacobianDoublePointMixed(array $p)
{
list($x1, $y1) = $p;
$xx = $x1->multiply($x1);
$yy = $y1->multiply($y1);
$yyyy = $yy->multiply($yy);
$s = $x1->add($yy);
$s =
$s->multiply($s)->subtract($xx)->subtract($yyyy)->multiply($this->two);
$m = $this->three->multiply($xx);
$t =
$m->multiply($m)->subtract($this->two->multiply($s));
$x3 = $t;
$y3 = $s->subtract($t);
$y3 =
$m->multiply($y3)->subtract($this->eight->multiply($yyyy));
$z3 = $this->two->multiply($y1);
return [$x3, $y3, $z3];
}
/**
* Tests whether or not the x / y values satisfy the equation
*
* @return boolean
*/
public function verifyPoint(array $p)
{
list($x, $y) = $p;
$lhs = $y->multiply($y);
$temp = $x->multiply($x)->multiply($x);
$rhs = $temp->add($this->b);
return $lhs->equals($rhs);
}
/**
* Calculates the parameters needed from the Euclidean algorithm as
discussed at
*
http://diamond.boisestate.edu/~liljanab/MATH308/GuideToECC.pdf#page=148
*
* @param BigInteger $u
* @param BigInteger $v
* @return BigInteger[]
*/
protected static function extendedGCD(BigInteger $u, BigInteger $v)
{
$one = new BigInteger(1);
$zero = new BigInteger();
$a = clone $one;
$b = clone $zero;
$c = clone $zero;
$d = clone $one;
$stop = $v->bitwise_rightShift($v->getLength() >> 1);
$a1 = clone $zero;
$b1 = clone $zero;
$a2 = clone $zero;
$b2 = clone $zero;
$postGreatestIndex = 0;
while (!$v->equals($zero)) {
list($q) = $u->divide($v);
$temp = $u;
$u = $v;
$v = $temp->subtract($v->multiply($q));
$temp = $a;
$a = $c;
$c = $temp->subtract($a->multiply($q));
$temp = $b;
$b = $d;
$d = $temp->subtract($b->multiply($q));
if ($v->compare($stop) > 0) {
$a0 = $v;
$b0 = $c;
} else {
$postGreatestIndex++;
}
if ($postGreatestIndex == 1) {
$a1 = $v;
$b1 = $c->negate();
}
if ($postGreatestIndex == 2) {
$rhs =
$a0->multiply($a0)->add($b0->multiply($b0));
$lhs = $v->multiply($v)->add($b->multiply($b));
if ($lhs->compare($rhs) <= 0) {
$a2 = $a0;
$b2 = $b0->negate();
} else {
$a2 = $v;
$b2 = $c->negate();
}
break;
}
}
return [
['a' => $a1, 'b' => $b1],
['a' => $a2, 'b' => $b2]
];
}
}
phpseclib/phpseclib/Crypt/EC/BaseCurves/Montgomery.php000064400000016330151161424250016775
0ustar00<?php
/**
* Curves over y^2 = x^3 + a*x + x
*
* Technically, a Montgomery curve has a coefficient for y^2 but for
Curve25519 and Curve448 that
* coefficient is 1.
*
* Curve25519 and Curve448 do not make use of the y coordinate, which makes
it unsuitable for use
* with ECDSA / EdDSA. A few other differences between Curve25519 and
Ed25519 are discussed at
* https://crypto.stackexchange.com/a/43058/4520
*
* More info:
*
* https://en.wikipedia.org/wiki/Montgomery_curve
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2019 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\BaseCurves;
use phpseclib3\Crypt\EC\Curves\Curve25519;
use phpseclib3\Math\BigInteger;
use phpseclib3\Math\PrimeField;
use phpseclib3\Math\PrimeField\Integer as PrimeInteger;
/**
* Curves over y^2 = x^3 + a*x + x
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class Montgomery extends Base
{
/**
* Prime Field Integer factory
*
* @var \phpseclib3\Math\PrimeField
*/
protected $factory;
/**
* Cofficient for x
*
* @var object
*/
protected $a;
/**
* Constant used for point doubling
*
* @var object
*/
protected $a24;
/**
* The Number Zero
*
* @var object
*/
protected $zero;
/**
* The Number One
*
* @var object
*/
protected $one;
/**
* Base Point
*
* @var object
*/
protected $p;
/**
* The modulo
*
* @var BigInteger
*/
protected $modulo;
/**
* The Order
*
* @var BigInteger
*/
protected $order;
/**
* Sets the modulo
*/
public function setModulo(BigInteger $modulo)
{
$this->modulo = $modulo;
$this->factory = new PrimeField($modulo);
$this->zero = $this->factory->newInteger(new
BigInteger());
$this->one = $this->factory->newInteger(new
BigInteger(1));
}
/**
* Set coefficients a
*/
public function setCoefficients(BigInteger $a)
{
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
$this->a = $this->factory->newInteger($a);
$two = $this->factory->newInteger(new BigInteger(2));
$four = $this->factory->newInteger(new BigInteger(4));
$this->a24 = $this->a->subtract($two)->divide($four);
}
/**
* Set x and y coordinates for the base point
*
* @param BigInteger|PrimeInteger $x
* @param BigInteger|PrimeInteger $y
* @return PrimeInteger[]
*/
public function setBasePoint($x, $y)
{
switch (true) {
case !$x instanceof BigInteger && !$x instanceof
PrimeInteger:
throw new \UnexpectedValueException('Argument 1 passed
to Prime::setBasePoint() must be an instance of either BigInteger or
PrimeField\Integer');
case !$y instanceof BigInteger && !$y instanceof
PrimeInteger:
throw new \UnexpectedValueException('Argument 2 passed
to Prime::setBasePoint() must be an instance of either BigInteger or
PrimeField\Integer');
}
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
$this->p = [
$x instanceof BigInteger ? $this->factory->newInteger($x)
: $x,
$y instanceof BigInteger ? $this->factory->newInteger($y)
: $y
];
}
/**
* Retrieve the base point as an array
*
* @return array
*/
public function getBasePoint()
{
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
/*
if (!isset($this->p)) {
throw new \RuntimeException('setBasePoint needs to be
called before this method');
}
*/
return $this->p;
}
/**
* Doubles and adds a point on a curve
*
* See
https://tools.ietf.org/html/draft-ietf-tls-curve25519-01#appendix-A.1.3
*
* @return FiniteField[][]
*/
private function doubleAndAddPoint(array $p, array $q, PrimeInteger
$x1)
{
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
if (!count($p) || !count($q)) {
return [];
}
if (!isset($p[1])) {
throw new \RuntimeException('Affine coordinates need to be
manually converted to XZ coordinates');
}
list($x2, $z2) = $p;
list($x3, $z3) = $q;
$a = $x2->add($z2);
$aa = $a->multiply($a);
$b = $x2->subtract($z2);
$bb = $b->multiply($b);
$e = $aa->subtract($bb);
$c = $x3->add($z3);
$d = $x3->subtract($z3);
$da = $d->multiply($a);
$cb = $c->multiply($b);
$temp = $da->add($cb);
$x5 = $temp->multiply($temp);
$temp = $da->subtract($cb);
$z5 = $x1->multiply($temp->multiply($temp));
$x4 = $aa->multiply($bb);
$temp = static::class == Curve25519::class ? $bb : $aa;
$z4 =
$e->multiply($temp->add($this->a24->multiply($e)));
return [
[$x4, $z4],
[$x5, $z5]
];
}
/**
* Multiply a point on the curve by a scalar
*
* Uses the montgomery ladder technique as described here:
*
*
https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Montgomery_ladder
* https://github.com/phpecc/phpecc/issues/16#issuecomment-59176772
*
* @return array
*/
public function multiplyPoint(array $p, BigInteger $d)
{
$p1 = [$this->one, $this->zero];
$alreadyInternal = isset($x[1]);
$p2 = $this->convertToInternal($p);
$x = $p[0];
$b = $d->toBits();
$b = str_pad($b, 256, '0', STR_PAD_LEFT);
for ($i = 0; $i < strlen($b); $i++) {
$b_i = (int) $b[$i];
if ($b_i) {
list($p2, $p1) = $this->doubleAndAddPoint($p2, $p1, $x);
} else {
list($p1, $p2) = $this->doubleAndAddPoint($p1, $p2, $x);
}
}
return $alreadyInternal ? $p1 : $this->convertToAffine($p1);
}
/**
* Converts an affine point to an XZ coordinate
*
* From https://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html
*
* XZ coordinates represent x y as X Z satsfying the following
equations:
*
* x=X/Z
*
* @return \phpseclib3\Math\PrimeField\Integer[]
*/
public function convertToInternal(array $p)
{
if (empty($p)) {
return [clone $this->zero, clone $this->one];
}
if (isset($p[1])) {
return $p;
}
$p[1] = clone $this->one;
return $p;
}
/**
* Returns the affine point
*
* @return \phpseclib3\Math\PrimeField\Integer[]
*/
public function convertToAffine(array $p)
{
if (!isset($p[1])) {
return $p;
}
list($x, $z) = $p;
return [$x->divide($z)];
}
}
phpseclib/phpseclib/Crypt/EC/BaseCurves/Prime.php000064400000051701151161424250015712
0ustar00<?php
/**
* Curves over y^2 = x^3 + a*x + b
*
* These are curves used in SEC 2 over prime fields:
http://www.secg.org/SEC2-Ver-1.0.pdf
* The curve is a weierstrass curve with a[1], a[3] and a[2] set to 0.
*
* Uses Jacobian Coordinates for speed if able:
*
* https://en.wikipedia.org/wiki/Jacobian_curve
*
https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\BaseCurves;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Math\BigInteger;
use phpseclib3\Math\Common\FiniteField\Integer;
use phpseclib3\Math\PrimeField;
use phpseclib3\Math\PrimeField\Integer as PrimeInteger;
/**
* Curves over y^2 = x^3 + a*x + b
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class Prime extends Base
{
/**
* Prime Field Integer factory
*
* @var \phpseclib3\Math\PrimeFields
*/
protected $factory;
/**
* Cofficient for x^1
*
* @var object
*/
protected $a;
/**
* Cofficient for x^0
*
* @var object
*/
protected $b;
/**
* Base Point
*
* @var object
*/
protected $p;
/**
* The number one over the specified finite field
*
* @var object
*/
protected $one;
/**
* The number two over the specified finite field
*
* @var object
*/
protected $two;
/**
* The number three over the specified finite field
*
* @var object
*/
protected $three;
/**
* The number four over the specified finite field
*
* @var object
*/
protected $four;
/**
* The number eight over the specified finite field
*
* @var object
*/
protected $eight;
/**
* The modulo
*
* @var BigInteger
*/
protected $modulo;
/**
* The Order
*
* @var BigInteger
*/
protected $order;
/**
* Sets the modulo
*/
public function setModulo(BigInteger $modulo)
{
$this->modulo = $modulo;
$this->factory = new PrimeField($modulo);
$this->two = $this->factory->newInteger(new
BigInteger(2));
$this->three = $this->factory->newInteger(new
BigInteger(3));
// used by jacobian coordinates
$this->one = $this->factory->newInteger(new
BigInteger(1));
$this->four = $this->factory->newInteger(new
BigInteger(4));
$this->eight = $this->factory->newInteger(new
BigInteger(8));
}
/**
* Set coefficients a and b
*/
public function setCoefficients(BigInteger $a, BigInteger $b)
{
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
$this->a = $this->factory->newInteger($a);
$this->b = $this->factory->newInteger($b);
}
/**
* Set x and y coordinates for the base point
*
* @param BigInteger|PrimeInteger $x
* @param BigInteger|PrimeInteger $y
* @return PrimeInteger[]
*/
public function setBasePoint($x, $y)
{
switch (true) {
case !$x instanceof BigInteger && !$x instanceof
PrimeInteger:
throw new \UnexpectedValueException('Argument 1 passed
to Prime::setBasePoint() must be an instance of either BigInteger or
PrimeField\Integer');
case !$y instanceof BigInteger && !$y instanceof
PrimeInteger:
throw new \UnexpectedValueException('Argument 2 passed
to Prime::setBasePoint() must be an instance of either BigInteger or
PrimeField\Integer');
}
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
$this->p = [
$x instanceof BigInteger ? $this->factory->newInteger($x)
: $x,
$y instanceof BigInteger ? $this->factory->newInteger($y)
: $y
];
}
/**
* Retrieve the base point as an array
*
* @return array
*/
public function getBasePoint()
{
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
/*
if (!isset($this->p)) {
throw new \RuntimeException('setBasePoint needs to be
called before this method');
}
*/
return $this->p;
}
/**
* Adds two "fresh" jacobian form on the curve
*
* @return FiniteField[]
*/
protected function jacobianAddPointMixedXY(array $p, array $q)
{
list($u1, $s1) = $p;
list($u2, $s2) = $q;
if ($u1->equals($u2)) {
if (!$s1->equals($s2)) {
return [];
} else {
return $this->doublePoint($p);
}
}
$h = $u2->subtract($u1);
$r = $s2->subtract($s1);
$h2 = $h->multiply($h);
$h3 = $h2->multiply($h);
$v = $u1->multiply($h2);
$x3 =
$r->multiply($r)->subtract($h3)->subtract($v->multiply($this->two));
$y3 = $r->multiply(
$v->subtract($x3)
)->subtract(
$s1->multiply($h3)
);
return [$x3, $y3, $h];
}
/**
* Adds one "fresh" jacobian form on the curve
*
* The second parameter should be the "fresh" one
*
* @return FiniteField[]
*/
protected function jacobianAddPointMixedX(array $p, array $q)
{
list($u1, $s1, $z1) = $p;
list($x2, $y2) = $q;
$z12 = $z1->multiply($z1);
$u2 = $x2->multiply($z12);
$s2 = $y2->multiply($z12->multiply($z1));
if ($u1->equals($u2)) {
if (!$s1->equals($s2)) {
return [];
} else {
return $this->doublePoint($p);
}
}
$h = $u2->subtract($u1);
$r = $s2->subtract($s1);
$h2 = $h->multiply($h);
$h3 = $h2->multiply($h);
$v = $u1->multiply($h2);
$x3 =
$r->multiply($r)->subtract($h3)->subtract($v->multiply($this->two));
$y3 = $r->multiply(
$v->subtract($x3)
)->subtract(
$s1->multiply($h3)
);
$z3 = $h->multiply($z1);
return [$x3, $y3, $z3];
}
/**
* Adds two jacobian coordinates on the curve
*
* @return FiniteField[]
*/
protected function jacobianAddPoint(array $p, array $q)
{
list($x1, $y1, $z1) = $p;
list($x2, $y2, $z2) = $q;
$z12 = $z1->multiply($z1);
$z22 = $z2->multiply($z2);
$u1 = $x1->multiply($z22);
$u2 = $x2->multiply($z12);
$s1 = $y1->multiply($z22->multiply($z2));
$s2 = $y2->multiply($z12->multiply($z1));
if ($u1->equals($u2)) {
if (!$s1->equals($s2)) {
return [];
} else {
return $this->doublePoint($p);
}
}
$h = $u2->subtract($u1);
$r = $s2->subtract($s1);
$h2 = $h->multiply($h);
$h3 = $h2->multiply($h);
$v = $u1->multiply($h2);
$x3 =
$r->multiply($r)->subtract($h3)->subtract($v->multiply($this->two));
$y3 = $r->multiply(
$v->subtract($x3)
)->subtract(
$s1->multiply($h3)
);
$z3 = $h->multiply($z1)->multiply($z2);
return [$x3, $y3, $z3];
}
/**
* Adds two points on the curve
*
* @return FiniteField[]
*/
public function addPoint(array $p, array $q)
{
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
if (!count($p) || !count($q)) {
if (count($q)) {
return $q;
}
if (count($p)) {
return $p;
}
return [];
}
// use jacobian coordinates
if (isset($p[2]) && isset($q[2])) {
if (isset($p['fresh']) &&
isset($q['fresh'])) {
return $this->jacobianAddPointMixedXY($p, $q);
}
if (isset($p['fresh'])) {
return $this->jacobianAddPointMixedX($q, $p);
}
if (isset($q['fresh'])) {
return $this->jacobianAddPointMixedX($p, $q);
}
return $this->jacobianAddPoint($p, $q);
}
if (isset($p[2]) || isset($q[2])) {
throw new \RuntimeException('Affine coordinates need to be
manually converted to Jacobi coordinates or vice versa');
}
if ($p[0]->equals($q[0])) {
if (!$p[1]->equals($q[1])) {
return [];
} else { // eg. doublePoint
list($numerator, $denominator) =
$this->doublePointHelper($p);
}
} else {
$numerator = $q[1]->subtract($p[1]);
$denominator = $q[0]->subtract($p[0]);
}
$slope = $numerator->divide($denominator);
$x =
$slope->multiply($slope)->subtract($p[0])->subtract($q[0]);
$y =
$slope->multiply($p[0]->subtract($x))->subtract($p[1]);
return [$x, $y];
}
/**
* Returns the numerator and denominator of the slope
*
* @return FiniteField[]
*/
protected function doublePointHelper(array $p)
{
$numerator =
$this->three->multiply($p[0])->multiply($p[0])->add($this->a);
$denominator = $this->two->multiply($p[1]);
return [$numerator, $denominator];
}
/**
* Doubles a jacobian coordinate on the curve
*
* @return FiniteField[]
*/
protected function jacobianDoublePoint(array $p)
{
list($x, $y, $z) = $p;
$x2 = $x->multiply($x);
$y2 = $y->multiply($y);
$z2 = $z->multiply($z);
$s = $this->four->multiply($x)->multiply($y2);
$m1 = $this->three->multiply($x2);
$m2 = $this->a->multiply($z2->multiply($z2));
$m = $m1->add($m2);
$x1 =
$m->multiply($m)->subtract($this->two->multiply($s));
$y1 = $m->multiply($s->subtract($x1))->subtract(
$this->eight->multiply($y2->multiply($y2))
);
$z1 = $this->two->multiply($y)->multiply($z);
return [$x1, $y1, $z1];
}
/**
* Doubles a "fresh" jacobian coordinate on the curve
*
* @return FiniteField[]
*/
protected function jacobianDoublePointMixed(array $p)
{
list($x, $y) = $p;
$x2 = $x->multiply($x);
$y2 = $y->multiply($y);
$s = $this->four->multiply($x)->multiply($y2);
$m1 = $this->three->multiply($x2);
$m = $m1->add($this->a);
$x1 =
$m->multiply($m)->subtract($this->two->multiply($s));
$y1 = $m->multiply($s->subtract($x1))->subtract(
$this->eight->multiply($y2->multiply($y2))
);
$z1 = $this->two->multiply($y);
return [$x1, $y1, $z1];
}
/**
* Doubles a point on a curve
*
* @return FiniteField[]
*/
public function doublePoint(array $p)
{
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
if (!count($p)) {
return [];
}
// use jacobian coordinates
if (isset($p[2])) {
if (isset($p['fresh'])) {
return $this->jacobianDoublePointMixed($p);
}
return $this->jacobianDoublePoint($p);
}
list($numerator, $denominator) = $this->doublePointHelper($p);
$slope = $numerator->divide($denominator);
$x =
$slope->multiply($slope)->subtract($p[0])->subtract($p[0]);
$y =
$slope->multiply($p[0]->subtract($x))->subtract($p[1]);
return [$x, $y];
}
/**
* Returns the X coordinate and the derived Y coordinate
*
* @return array
*/
public function derivePoint($m)
{
$y = ord(Strings::shift($m));
$x = new BigInteger($m, 256);
$xp = $this->convertInteger($x);
switch ($y) {
case 2:
$ypn = false;
break;
case 3:
$ypn = true;
break;
default:
throw new \RuntimeException('Coordinate not in
recognized format');
}
$temp = $xp->multiply($this->a);
$temp = $xp->multiply($xp)->multiply($xp)->add($temp);
$temp = $temp->add($this->b);
$b = $temp->squareRoot();
if (!$b) {
throw new \RuntimeException('Unable to derive Y
coordinate');
}
$bn = $b->isOdd();
$yp = $ypn == $bn ? $b : $b->negate();
return [$xp, $yp];
}
/**
* Tests whether or not the x / y values satisfy the equation
*
* @return boolean
*/
public function verifyPoint(array $p)
{
list($x, $y) = $p;
$lhs = $y->multiply($y);
$temp = $x->multiply($this->a);
$temp = $x->multiply($x)->multiply($x)->add($temp);
$rhs = $temp->add($this->b);
return $lhs->equals($rhs);
}
/**
* Returns the modulo
*
* @return \phpseclib3\Math\BigInteger
*/
public function getModulo()
{
return $this->modulo;
}
/**
* Returns the a coefficient
*
* @return \phpseclib3\Math\PrimeField\Integer
*/
public function getA()
{
return $this->a;
}
/**
* Returns the a coefficient
*
* @return \phpseclib3\Math\PrimeField\Integer
*/
public function getB()
{
return $this->b;
}
/**
* Multiply and Add Points
*
* Adapted from:
*
https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/curve/base.js#L125
*
* @return int[]
*/
public function multiplyAddPoints(array $points, array $scalars)
{
$length = count($points);
foreach ($points as &$point) {
$point = $this->convertToInternal($point);
}
$wnd = [$this->getNAFPoints($points[0], 7)];
$wndWidth = [isset($points[0]['nafwidth']) ?
$points[0]['nafwidth'] : 7];
for ($i = 1; $i < $length; $i++) {
$wnd[] = $this->getNAFPoints($points[$i], 1);
$wndWidth[] = isset($points[$i]['nafwidth']) ?
$points[$i]['nafwidth'] : 1;
}
$naf = [];
// comb all window NAFs
$max = 0;
for ($i = $length - 1; $i >= 1; $i -= 2) {
$a = $i - 1;
$b = $i;
if ($wndWidth[$a] != 1 || $wndWidth[$b] != 1) {
$naf[$a] = $scalars[$a]->getNAF($wndWidth[$a]);
$naf[$b] = $scalars[$b]->getNAF($wndWidth[$b]);
$max = max(count($naf[$a]), count($naf[$b]), $max);
continue;
}
$comb = [
$points[$a], // 1
null, // 3
null, // 5
$points[$b] // 7
];
$comb[1] = $this->addPoint($points[$a], $points[$b]);
$comb[2] = $this->addPoint($points[$a],
$this->negatePoint($points[$b]));
$index = [
-3, /* -1 -1 */
-1, /* -1 0 */
-5, /* -1 1 */
-7, /* 0 -1 */
0, /* 0 -1 */
7, /* 0 1 */
5, /* 1 -1 */
1, /* 1 0 */
3 /* 1 1 */
];
$jsf = self::getJSFPoints($scalars[$a], $scalars[$b]);
$max = max(count($jsf[0]), $max);
if ($max > 0) {
$naf[$a] = array_fill(0, $max, 0);
$naf[$b] = array_fill(0, $max, 0);
} else {
$naf[$a] = [];
$naf[$b] = [];
}
for ($j = 0; $j < $max; $j++) {
$ja = isset($jsf[0][$j]) ? $jsf[0][$j] : 0;
$jb = isset($jsf[1][$j]) ? $jsf[1][$j] : 0;
$naf[$a][$j] = $index[3 * ($ja + 1) + $jb + 1];
$naf[$b][$j] = 0;
$wnd[$a] = $comb;
}
}
$acc = [];
$temp = [0, 0, 0, 0];
for ($i = $max; $i >= 0; $i--) {
$k = 0;
while ($i >= 0) {
$zero = true;
for ($j = 0; $j < $length; $j++) {
$temp[$j] = isset($naf[$j][$i]) ? $naf[$j][$i] : 0;
if ($temp[$j] != 0) {
$zero = false;
}
}
if (!$zero) {
break;
}
$k++;
$i--;
}
if ($i >= 0) {
$k++;
}
while ($k--) {
$acc = $this->doublePoint($acc);
}
if ($i < 0) {
break;
}
for ($j = 0; $j < $length; $j++) {
$z = $temp[$j];
$p = null;
if ($z == 0) {
continue;
}
$p = $z > 0 ?
$wnd[$j][($z - 1) >> 1] :
$this->negatePoint($wnd[$j][(-$z - 1) >> 1]);
$acc = $this->addPoint($acc, $p);
}
}
return $this->convertToAffine($acc);
}
/**
* Precomputes NAF points
*
* Adapted from:
*
https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/curve/base.js#L351
*
* @return int[]
*/
private function getNAFPoints(array $point, $wnd)
{
if (isset($point['naf'])) {
return $point['naf'];
}
$res = [$point];
$max = (1 << $wnd) - 1;
$dbl = $max == 1 ? null : $this->doublePoint($point);
for ($i = 1; $i < $max; $i++) {
$res[] = $this->addPoint($res[$i - 1], $dbl);
}
$point['naf'] = $res;
/*
$str = '';
foreach ($res as $re) {
$re[0] = bin2hex($re[0]->toBytes());
$re[1] = bin2hex($re[1]->toBytes());
$str.= " ['$re[0]',
'$re[1]'],\r\n";
}
file_put_contents('temp.txt', $str);
exit;
*/
return $res;
}
/**
* Precomputes points in Joint Sparse Form
*
* Adapted from:
*
https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/utils.js#L96
*
* @return int[]
*/
private static function getJSFPoints(Integer $k1, Integer $k2)
{
static $three;
if (!isset($three)) {
$three = new BigInteger(3);
}
$jsf = [[], []];
$k1 = $k1->toBigInteger();
$k2 = $k2->toBigInteger();
$d1 = 0;
$d2 = 0;
while ($k1->compare(new BigInteger(-$d1)) > 0 ||
$k2->compare(new BigInteger(-$d2)) > 0) {
// first phase
$m14 = $k1->testBit(0) + 2 * $k1->testBit(1);
$m14 += $d1;
$m14 &= 3;
$m24 = $k2->testBit(0) + 2 * $k2->testBit(1);
$m24 += $d2;
$m24 &= 3;
if ($m14 == 3) {
$m14 = -1;
}
if ($m24 == 3) {
$m24 = -1;
}
$u1 = 0;
if ($m14 & 1) { // if $m14 is odd
$m8 = $k1->testBit(0) + 2 * $k1->testBit(1) + 4 *
$k1->testBit(2);
$m8 += $d1;
$m8 &= 7;
$u1 = ($m8 == 3 || $m8 == 5) && $m24 == 2 ? -$m14 :
$m14;
}
$jsf[0][] = $u1;
$u2 = 0;
if ($m24 & 1) { // if $m24 is odd
$m8 = $k2->testBit(0) + 2 * $k2->testBit(1) + 4 *
$k2->testBit(2);
$m8 += $d2;
$m8 &= 7;
$u2 = ($m8 == 3 || $m8 == 5) && $m14 == 2 ? -$m24 :
$m24;
}
$jsf[1][] = $u2;
// second phase
if (2 * $d1 == $u1 + 1) {
$d1 = 1 - $d1;
}
if (2 * $d2 == $u2 + 1) {
$d2 = 1 - $d2;
}
$k1 = $k1->bitwise_rightShift(1);
$k2 = $k2->bitwise_rightShift(1);
}
return $jsf;
}
/**
* Returns the affine point
*
* A Jacobian Coordinate is of the form (x, y, z).
* To convert a Jacobian Coordinate to an Affine Point
* you do (x / z^2, y / z^3)
*
* @return \phpseclib3\Math\PrimeField\Integer[]
*/
public function convertToAffine(array $p)
{
if (!isset($p[2])) {
return $p;
}
list($x, $y, $z) = $p;
$z = $this->one->divide($z);
$z2 = $z->multiply($z);
return [
$x->multiply($z2),
$y->multiply($z2)->multiply($z)
];
}
/**
* Converts an affine point to a jacobian coordinate
*
* @return \phpseclib3\Math\PrimeField\Integer[]
*/
public function convertToInternal(array $p)
{
if (isset($p[2])) {
return $p;
}
$p[2] = clone $this->one;
$p['fresh'] = true;
return $p;
}
}
phpseclib/phpseclib/Crypt/EC/BaseCurves/TwistedEdwards.php000064400000012564151161424250017577
0ustar00<?php
/**
* Curves over a*x^2 + y^2 = 1 + d*x^2*y^2
*
* http://www.secg.org/SEC2-Ver-1.0.pdf provides for curves with custom
parameters.
* ie. the coefficients can be arbitrary set through specially formatted
keys, etc.
* As such, Prime.php is built very generically and it's not able to
take full
* advantage of curves with 0 coefficients to produce simplified point
doubling,
* point addition. Twisted Edwards curves, in contrast, do not have a way,
currently,
* to customize them. As such, we can omit the super generic stuff from
this class
* and let the named curves (Ed25519 and Ed448) define their own custom
tailored
* point addition and point doubling methods.
*
* More info:
*
* https://en.wikipedia.org/wiki/Twisted_Edwards_curve
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\BaseCurves;
use phpseclib3\Math\BigInteger;
use phpseclib3\Math\PrimeField;
use phpseclib3\Math\PrimeField\Integer as PrimeInteger;
/**
* Curves over a*x^2 + y^2 = 1 + d*x^2*y^2
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class TwistedEdwards extends Base
{
/**
* The modulo
*
* @var BigInteger
*/
protected $modulo;
/**
* Cofficient for x^2
*
* @var object
*/
protected $a;
/**
* Cofficient for x^2*y^2
*
* @var object
*/
protected $d;
/**
* Base Point
*
* @var object[]
*/
protected $p;
/**
* The number zero over the specified finite field
*
* @var object
*/
protected $zero;
/**
* The number one over the specified finite field
*
* @var object
*/
protected $one;
/**
* The number two over the specified finite field
*
* @var object
*/
protected $two;
/**
* Sets the modulo
*/
public function setModulo(BigInteger $modulo)
{
$this->modulo = $modulo;
$this->factory = new PrimeField($modulo);
$this->zero = $this->factory->newInteger(new
BigInteger(0));
$this->one = $this->factory->newInteger(new
BigInteger(1));
$this->two = $this->factory->newInteger(new
BigInteger(2));
}
/**
* Set coefficients a and b
*/
public function setCoefficients(BigInteger $a, BigInteger $d)
{
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
$this->a = $this->factory->newInteger($a);
$this->d = $this->factory->newInteger($d);
}
/**
* Set x and y coordinates for the base point
*/
public function setBasePoint($x, $y)
{
switch (true) {
case !$x instanceof BigInteger && !$x instanceof
PrimeInteger:
throw new \UnexpectedValueException('Argument 1 passed
to Prime::setBasePoint() must be an instance of either BigInteger or
PrimeField\Integer');
case !$y instanceof BigInteger && !$y instanceof
PrimeInteger:
throw new \UnexpectedValueException('Argument 2 passed
to Prime::setBasePoint() must be an instance of either BigInteger or
PrimeField\Integer');
}
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
$this->p = [
$x instanceof BigInteger ? $this->factory->newInteger($x)
: $x,
$y instanceof BigInteger ? $this->factory->newInteger($y)
: $y
];
}
/**
* Returns the a coefficient
*
* @return \phpseclib3\Math\PrimeField\Integer
*/
public function getA()
{
return $this->a;
}
/**
* Returns the a coefficient
*
* @return \phpseclib3\Math\PrimeField\Integer
*/
public function getD()
{
return $this->d;
}
/**
* Retrieve the base point as an array
*
* @return array
*/
public function getBasePoint()
{
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
/*
if (!isset($this->p)) {
throw new \RuntimeException('setBasePoint needs to be
called before this method');
}
*/
return $this->p;
}
/**
* Returns the affine point
*
* @return \phpseclib3\Math\PrimeField\Integer[]
*/
public function convertToAffine(array $p)
{
if (!isset($p[2])) {
return $p;
}
list($x, $y, $z) = $p;
$z = $this->one->divide($z);
return [
$x->multiply($z),
$y->multiply($z)
];
}
/**
* Returns the modulo
*
* @return \phpseclib3\Math\BigInteger
*/
public function getModulo()
{
return $this->modulo;
}
/**
* Tests whether or not the x / y values satisfy the equation
*
* @return boolean
*/
public function verifyPoint(array $p)
{
list($x, $y) = $p;
$x2 = $x->multiply($x);
$y2 = $y->multiply($y);
$lhs = $this->a->multiply($x2)->add($y2);
$rhs =
$this->d->multiply($x2)->multiply($y2)->add($this->one);
return $lhs->equals($rhs);
}
}
phpseclib/phpseclib/Crypt/EC/Curves/nistk283.php000064400000000547151161424250015432
0ustar00<?php
/**
* sect283k1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
final class nistk283 extends sect283k1
{
}
phpseclib/phpseclib/Crypt/EC/Curves/nistk409.php000064400000000546151161424250015431
0ustar00<?php
/**
* nistk409
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
final class nistk409 extends sect409k1
{
}
phpseclib/phpseclib/Crypt/EC/Curves/nistp192.php000064400000000546151161424250015435
0ustar00<?php
/**
* nistp192
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
final class nistp192 extends secp192r1
{
}
phpseclib/phpseclib/Crypt/EC/Curves/nistp224.php000064400000000546151161424250015431
0ustar00<?php
/**
* nistp224
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
final class nistp224 extends secp224r1
{
}
phpseclib/phpseclib/Crypt/EC/Curves/nistp256.php000064400000000546151161424250015436
0ustar00<?php
/**
* nistp256
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
final class nistp256 extends secp256r1
{
}
phpseclib/phpseclib/Crypt/EC/Curves/nistp384.php000064400000000546151161424250015440
0ustar00<?php
/**
* nistp384
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
final class nistp384 extends secp384r1
{
}
phpseclib/phpseclib/Crypt/EC/Curves/nistp521.php000064400000000546151161424250015431
0ustar00<?php
/**
* nistp521
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
final class nistp521 extends secp521r1
{
}
phpseclib/phpseclib/Crypt/EC/Curves/nistt571.php000064400000000546151161424250015442
0ustar00<?php
/**
* nistt571
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
final class nistt571 extends sect571k1
{
}
phpseclib/phpseclib/Crypt/EC/Curves/prime192v1.php000064400000000552151161424250015660
0ustar00<?php
/**
* prime192v1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
final class prime192v1 extends secp192r1
{
}
phpseclib/phpseclib/Crypt/EC/Curves/prime192v2.php000064400000002075151161424250015663
0ustar00<?php
/**
* prime192v2
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class prime192v2 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF',
16));
$this->setCoefficients(
new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC',
16),
new
BigInteger('CC22D6DFB95C6B25E49C0D6364A4E5980C393AA21668D953',
16)
);
$this->setBasePoint(
new
BigInteger('EEA2BAE7E1497842F2DE7769CFE9C989C072AD696F48034A',
16),
new
BigInteger('6574D11D69B6EC7A672BB82A083DF2F2B0847DE970B2DE15',
16)
);
$this->setOrder(new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFE5FB1A724DC80418648D8DD31',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/prime192v3.php000064400000002075151161424250015664
0ustar00<?php
/**
* prime192v3
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class prime192v3 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF',
16));
$this->setCoefficients(
new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC',
16),
new
BigInteger('22123DC2395A05CAA7423DAECCC94760A7D462256BD56916',
16)
);
$this->setBasePoint(
new
BigInteger('7D29778100C65A1DA1783716588DCE2B8B4AEE8E228F1896',
16),
new
BigInteger('38A90F22637337334B49DCB66A6DC8F9978ACA7648A943B0',
16)
);
$this->setOrder(new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFF7A62D031C83F4294F640EC13',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/prime239v1.php000064400000002205151161424250015657
0ustar00<?php
/**
* prime239v1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class prime239v1 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF',
16));
$this->setCoefficients(
new
BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC',
16),
new
BigInteger('6B016C3BDCF18941D0D654921475CA71A9DB2FB27D1D37796185C2942C0A',
16)
);
$this->setBasePoint(
new
BigInteger('0FFA963CDCA8816CCC33B8642BEDF905C3D358573D3F27FBBD3B3CB9AAAF',
16),
new
BigInteger('7DEBE8E4E90A5DAE6E4054CA530BA04654B36818CE226B39FCCB7B02F1AE',
16)
);
$this->setOrder(new
BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF9E5E9A9F5D9071FBD1522688909D0B',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/prime239v2.php000064400000002205151161424250015660
0ustar00<?php
/**
* prime239v2
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class prime239v2 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF',
16));
$this->setCoefficients(
new
BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC',
16),
new
BigInteger('617FAB6832576CBBFED50D99F0249C3FEE58B94BA0038C7AE84C8C832F2C',
16)
);
$this->setBasePoint(
new
BigInteger('38AF09D98727705120C921BB5E9E26296A3CDCF2F35757A0EAFD87B830E7',
16),
new
BigInteger('5B0125E4DBEA0EC7206DA0FC01D9B081329FB555DE6EF460237DFF8BE4BA',
16)
);
$this->setOrder(new
BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF800000CFA7E8594377D414C03821BC582063',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/prime239v3.php000064400000002205151161424250015661
0ustar00<?php
/**
* prime239v3
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class prime239v3 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF',
16));
$this->setCoefficients(
new
BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC',
16),
new
BigInteger('255705FA2A306654B1F4CB03D6A750A30C250102D4988717D9BA15AB6D3E',
16)
);
$this->setBasePoint(
new
BigInteger('6768AE8E18BB92CFCF005C949AA2C6D94853D0E660BBF854B1C9505FE95A',
16),
new
BigInteger('1607E6898F390C06BC1D552BAD226F3B6FCFE48B6E818499AF18E3ED6CF3',
16)
);
$this->setOrder(new
BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF975DEB41B3A6057C3C432146526551',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/prime256v1.php000064400000000552151161424250015661
0ustar00<?php
/**
* prime256v1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
final class prime256v1 extends secp256r1
{
}
phpseclib/phpseclib/Crypt/EC/Curves/secp112r1.php000064400000001703151161424250015461
0ustar00<?php
/**
* secp112r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class secp112r1 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('DB7C2ABF62E35E668076BEAD208B', 16));
$this->setCoefficients(
new BigInteger('DB7C2ABF62E35E668076BEAD2088', 16),
new BigInteger('659EF8BA043916EEDE8911702B22', 16)
);
$this->setBasePoint(
new BigInteger('09487239995A5EE76B55F9C2F098', 16),
new BigInteger('A89CE5AF8724C0A23E0E0FF77500', 16)
);
$this->setOrder(new
BigInteger('DB7C2ABF62E35E7628DFAC6561C5', 16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/secp112r2.php000064400000001747151161424250015472
0ustar00<?php
/**
* secp112r2
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class secp112r2 extends Prime
{
public function __construct()
{
// same modulo as secp112r1
$this->setModulo(new
BigInteger('DB7C2ABF62E35E668076BEAD208B', 16));
$this->setCoefficients(
new BigInteger('6127C24C05F38A0AAAF65C0EF02C', 16),
new BigInteger('51DEF1815DB5ED74FCC34C85D709', 16)
);
$this->setBasePoint(
new BigInteger('4BA30AB5E892B4E1649DD0928643', 16),
new BigInteger('ADCD46F5882E3747DEF36E956E97', 16)
);
$this->setOrder(new
BigInteger('36DF0AAFD8B8D7597CA10520D04B', 16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/secp128r1.php000064400000001733151161424250015473
0ustar00<?php
/**
* secp128r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class secp128r1 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF', 16));
$this->setCoefficients(
new BigInteger('FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC',
16),
new BigInteger('E87579C11079F43DD824993C2CEE5ED3',
16)
);
$this->setBasePoint(
new BigInteger('161FF7528B899B2D0C28607CA52C5B86',
16),
new BigInteger('CF5AC8395BAFEB13C02DA292DDED7A83',
16)
);
$this->setOrder(new
BigInteger('FFFFFFFE0000000075A30D1B9038A115', 16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/secp128r2.php000064400000001770151161424250015475
0ustar00<?php
/**
* secp128r2
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class secp128r2 extends Prime
{
public function __construct()
{
// same as secp128r1
$this->setModulo(new
BigInteger('FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF', 16));
$this->setCoefficients(
new BigInteger('D6031998D1B3BBFEBF59CC9BBFF9AEE1',
16),
new BigInteger('5EEEFCA380D02919DC2C6558BB6D8A5D',
16)
);
$this->setBasePoint(
new BigInteger('7B6AA5D85E572983E6FB32A7CDEBC140',
16),
new BigInteger('27B6916A894D3AEE7106FE805FC34B44',
16)
);
$this->setOrder(new
BigInteger('3FFFFFFF7FFFFFFFBE0024720613B5A3', 16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/secp160k1.php000064400000003027151161424250015456
0ustar00<?php
/**
* secp160k1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime;
use phpseclib3\Math\BigInteger;
class secp160k1 extends KoblitzPrime
{
public function __construct()
{
// same as secp160r2
$this->setModulo(new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73', 16));
$this->setCoefficients(
new
BigInteger('0000000000000000000000000000000000000000', 16),
new
BigInteger('0000000000000000000000000000000000000007', 16)
);
$this->setBasePoint(
new
BigInteger('3B4C382CE37AA192A4019E763036F4F5DD4D7EBB', 16),
new
BigInteger('938CF935318FDCED6BC28286531733C3F03C4FEE', 16)
);
$this->setOrder(new
BigInteger('0100000000000000000001B8FA16DFAB9ACA16B6B3', 16));
$this->basis = [];
$this->basis[] = [
'a' => new
BigInteger('0096341F1138933BC2F505', -16),
'b' => new
BigInteger('FF6E9D0418C67BB8D5F562', -16)
];
$this->basis[] = [
'a' => new
BigInteger('01BDCB3A09AAAABEAFF4A8', -16),
'b' => new
BigInteger('04D12329FF0EF498EA67', -16)
];
$this->beta = $this->factory->newInteger(new
BigInteger('645B7345A143464942CC46D7CF4D5D1E1E6CBB68', -16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/secp160r1.php000064400000002015151161424250015461
0ustar00<?php
/**
* secp160r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class secp160r1 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF', 16));
$this->setCoefficients(
new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC', 16),
new
BigInteger('1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45', 16)
);
$this->setBasePoint(
new
BigInteger('4A96B5688EF573284664698968C38BB913CBFC82', 16),
new
BigInteger('23A628553168947D59DCC912042351377AC5FB32', 16)
);
$this->setOrder(new
BigInteger('0100000000000000000001F4C8F927AED3CA752257', 16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/secp160r2.php000064400000002052151161424250015463
0ustar00<?php
/**
* secp160r2
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class secp160r2 extends Prime
{
public function __construct()
{
// same as secp160k1
$this->setModulo(new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73', 16));
$this->setCoefficients(
new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70', 16),
new
BigInteger('B4E134D3FB59EB8BAB57274904664D5AF50388BA', 16)
);
$this->setBasePoint(
new
BigInteger('52DCB034293A117E1F4FF11B30F7199D3144CE6D', 16),
new
BigInteger('FEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E', 16)
);
$this->setOrder(new
BigInteger('0100000000000000000000351EE786A818F3A1A16B', 16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/secp192k1.php000064400000003076151161424250015467
0ustar00<?php
/**
* secp192k1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime;
use phpseclib3\Math\BigInteger;
class secp192k1 extends KoblitzPrime
{
public function __construct()
{
$this->setModulo(new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37',
16));
$this->setCoefficients(
new
BigInteger('000000000000000000000000000000000000000000000000',
16),
new
BigInteger('000000000000000000000000000000000000000000000003',
16)
);
$this->setBasePoint(
new
BigInteger('DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D',
16),
new
BigInteger('9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D',
16)
);
$this->setOrder(new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D',
16));
$this->basis = [];
$this->basis[] = [
'a' => new
BigInteger('00B3FB3400DEC5C4ADCEB8655C', -16),
'b' => new
BigInteger('8EE96418CCF4CFC7124FDA0F', -16)
];
$this->basis[] = [
'a' => new
BigInteger('01D90D03E8F096B9948B20F0A9', -16),
'b' => new
BigInteger('42E49819ABBA9474E1083F6B', -16)
];
$this->beta = $this->factory->newInteger(new
BigInteger('447A96E6C647963E2F7809FEAAB46947F34B0AA3CA0BBA74',
-16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/secp192r1.php000064400000005732151161424250015477
0ustar00<?php
/**
* secp192r1
*
* This is the NIST P-192 curve
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class secp192r1 extends Prime
{
public function __construct()
{
$modulo = new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF',
16);
$this->setModulo($modulo);
// algorithm 2.27 from
http://diamond.boisestate.edu/~liljanab/MATH308/GuideToECC.pdf#page=66
/* in theory this should be faster than regular modular reductions
save for one small issue.
to convert to / from base-2**8 with BCMath you have to call
bcmul() and bcdiv() a lot.
to convert to / from base-2**8 with PHP64 you have to call
base256_rshift() a lot.
in short, converting to / from base-2**8 is pretty expensive and
that expense is
enough to offset whatever else might be gained by a simplified
reduction algorithm.
now, if PHP supported unsigned integers things might be
different. no bit-shifting
would be required for the PHP engine and it'd be a lot
faster. but as is, BigInteger
uses base-2**31 or base-2**26 depending on whether or not the
system is has a 32-bit
or a 64-bit OS.
*/
/*
$m_length = $this->getLengthInBytes();
$this->setReduction(function($c) use ($m_length) {
$cBytes = $c->toBytes();
$className = $this->className;
if (strlen($cBytes) > 2 * $m_length) {
list(, $r) = $c->divide($className::$modulo);
return $r;
}
$c = str_pad($cBytes, 48, "\0", STR_PAD_LEFT);
$c = array_reverse(str_split($c, 8));
$null = "\0\0\0\0\0\0\0\0";
$s1 = new BigInteger($c[2] . $c[1] . $c[0], 256);
$s2 = new BigInteger($null . $c[3] . $c[3], 256);
$s3 = new BigInteger($c[4] . $c[4] . $null, 256);
$s4 = new BigInteger($c[5] . $c[5] . $c[5], 256);
$r = $s1->add($s2)->add($s3)->add($s4);
while ($r->compare($className::$modulo) >= 0) {
$r = $r->subtract($className::$modulo);
}
return $r;
});
*/
$this->setCoefficients(
new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC',
16),
new
BigInteger('64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1',
16)
);
$this->setBasePoint(
new
BigInteger('188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012',
16),
new
BigInteger('07192B95FFC8DA78631011ED6B24CDD573F977A11E794811',
16)
);
$this->setOrder(new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/secp224k1.php000064400000003210151161424250015451
0ustar00<?php
/**
* secp224k1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime;
use phpseclib3\Math\BigInteger;
class secp224k1 extends KoblitzPrime
{
public function __construct()
{
$this->setModulo(new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D',
16));
$this->setCoefficients(
new
BigInteger('00000000000000000000000000000000000000000000000000000000',
16),
new
BigInteger('00000000000000000000000000000000000000000000000000000005',
16)
);
$this->setBasePoint(
new
BigInteger('A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C',
16),
new
BigInteger('7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5',
16)
);
$this->setOrder(new
BigInteger('010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7',
16));
$this->basis = [];
$this->basis[] = [
'a' => new
BigInteger('00B8ADF1378A6EB73409FA6C9C637D', -16),
'b' => new
BigInteger('94730F82B358A3776A826298FA6F', -16)
];
$this->basis[] = [
'a' => new
BigInteger('01DCE8D2EC6184CAF0A972769FCC8B', -16),
'b' => new
BigInteger('4D2100BA3DC75AAB747CCF355DEC', -16)
];
$this->beta = $this->factory->newInteger(new
BigInteger('01F178FFA4B17C89E6F73AECE2AAD57AF4C0A748B63C830947B27E04',
-16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/secp224r1.php000064400000002153151161424250015465
0ustar00<?php
/**
* secp224r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class secp224r1 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001',
16));
$this->setCoefficients(
new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE',
16),
new
BigInteger('B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4',
16)
);
$this->setBasePoint(
new
BigInteger('B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21',
16),
new
BigInteger('BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34',
16)
);
$this->setOrder(new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/secp256k1.php000064400000003501151161424250015461
0ustar00<?php
/**
* secp256k1
*
* This is the curve used in Bitcoin
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
//use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime;
use phpseclib3\Math\BigInteger;
//class secp256k1 extends Prime
class secp256k1 extends KoblitzPrime
{
public function __construct()
{
$this->setModulo(new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F',
16));
$this->setCoefficients(
new
BigInteger('0000000000000000000000000000000000000000000000000000000000000000',
16),
new
BigInteger('0000000000000000000000000000000000000000000000000000000000000007',
16)
);
$this->setOrder(new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141',
16));
$this->setBasePoint(
new
BigInteger('79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798',
16),
new
BigInteger('483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8',
16)
);
$this->basis = [];
$this->basis[] = [
'a' => new
BigInteger('3086D221A7D46BCDE86C90E49284EB15', -16),
'b' => new
BigInteger('FF1BBC8129FEF177D790AB8056F5401B3D', -16)
];
$this->basis[] = [
'a' => new
BigInteger('114CA50F7A8E2F3F657C1108D9D44CFD8', -16),
'b' => new
BigInteger('3086D221A7D46BCDE86C90E49284EB15', -16)
];
$this->beta = $this->factory->newInteger(new
BigInteger('7AE96A2B657C07106E64479EAC3434E99CF0497512F58995C1396C28719501EE',
-16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/secp256r1.php000064400000002233151161424250015471
0ustar00<?php
/**
* secp256r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class secp256r1 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF',
16));
$this->setCoefficients(
new
BigInteger('FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC',
16),
new
BigInteger('5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B',
16)
);
$this->setBasePoint(
new
BigInteger('6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296',
16),
new
BigInteger('4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5',
16)
);
$this->setOrder(new
BigInteger('FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/secp384r1.php000064400000003127151161424250015476
0ustar00<?php
/**
* secp384r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class secp384r1 extends Prime
{
public function __construct()
{
$this->setModulo(new BigInteger(
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF',
16
));
$this->setCoefficients(
new BigInteger(
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC',
16
),
new BigInteger(
'B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF',
16
)
);
$this->setBasePoint(
new BigInteger(
'AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7',
16
),
new BigInteger(
'3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F',
16
)
);
$this->setOrder(new BigInteger(
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973',
16
));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/secp521r1.php000064400000003745151161424250015475
0ustar00<?php
/**
* secp521r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class secp521r1 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'
.
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'
.
'FFFF', 16));
$this->setCoefficients(
new
BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'
.
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'
.
'FFFC', 16),
new
BigInteger('0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF1'
.
'09E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B50'
.
'3F00', 16)
);
$this->setBasePoint(
new
BigInteger('00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D'
.
'3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5'
.
'BD66', 16),
new
BigInteger('011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E'
.
'662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD1'
.
'6650', 16)
);
$this->setOrder(new
BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'
.
'FFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E9138'
.
'6409', 16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/sect113r1.php000064400000001526151161424250015471
0ustar00<?php
/**
* sect113r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Binary;
use phpseclib3\Math\BigInteger;
class sect113r1 extends Binary
{
public function __construct()
{
$this->setModulo(113, 9, 0);
$this->setCoefficients(
'003088250CA6E7C7FE649CE85820F7',
'00E8BEE4D3E2260744188BE0E9C723'
);
$this->setBasePoint(
'009D73616F35F4AB1407D73562C10F',
'00A52830277958EE84D1315ED31886'
);
$this->setOrder(new
BigInteger('0100000000000000D9CCEC8A39E56F', 16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/sect113r2.php000064400000001526151161424250015472
0ustar00<?php
/**
* sect113r2
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Binary;
use phpseclib3\Math\BigInteger;
class sect113r2 extends Binary
{
public function __construct()
{
$this->setModulo(113, 9, 0);
$this->setCoefficients(
'00689918DBEC7E5A0DD6DFC0AA55C7',
'0095E9A9EC9B297BD4BF36E059184F'
);
$this->setBasePoint(
'01A57A6A7B26CA5EF52FCDB8164797',
'00B3ADC94ED1FE674C06E695BABA1D'
);
$this->setOrder(new
BigInteger('010000000000000108789B2496AF93', 16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/sect131r1.php000064400000001560151161424250015467
0ustar00<?php
/**
* sect131r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Binary;
use phpseclib3\Math\BigInteger;
class sect131r1 extends Binary
{
public function __construct()
{
$this->setModulo(131, 8, 3, 2, 0);
$this->setCoefficients(
'07A11B09A76B562144418FF3FF8C2570B8',
'0217C05610884B63B9C6C7291678F9D341'
);
$this->setBasePoint(
'0081BAF91FDF9833C40F9C181343638399',
'078C6E7EA38C001F73C8134B1B4EF9E150'
);
$this->setOrder(new
BigInteger('0400000000000000023123953A9464B54D', 16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/sect131r2.php000064400000001560151161424250015470
0ustar00<?php
/**
* sect131r2
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Binary;
use phpseclib3\Math\BigInteger;
class sect131r2 extends Binary
{
public function __construct()
{
$this->setModulo(131, 8, 3, 2, 0);
$this->setCoefficients(
'03E5A88919D7CAFCBF415F07C2176573B2',
'04B8266A46C55657AC734CE38F018F2192'
);
$this->setBasePoint(
'0356DCD8F2F95031AD652D23951BB366A8',
'0648F06D867940A5366D9E265DE9EB240F'
);
$this->setOrder(new
BigInteger('0400000000000000016954A233049BA98F', 16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/sect163k1.php000064400000001630151161424250015463
0ustar00<?php
/**
* sect163k1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Binary;
use phpseclib3\Math\BigInteger;
class sect163k1 extends Binary
{
public function __construct()
{
$this->setModulo(163, 7, 6, 3, 0);
$this->setCoefficients(
'000000000000000000000000000000000000000001',
'000000000000000000000000000000000000000001'
);
$this->setBasePoint(
'02FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE8',
'0289070FB05D38FF58321F2E800536D538CCDAA3D9'
);
$this->setOrder(new
BigInteger('04000000000000000000020108A2E0CC0D99F8A5EF', 16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/sect163r1.php000064400000001630151161424250015472
0ustar00<?php
/**
* sect163r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Binary;
use phpseclib3\Math\BigInteger;
class sect163r1 extends Binary
{
public function __construct()
{
$this->setModulo(163, 7, 6, 3, 0);
$this->setCoefficients(
'07B6882CAAEFA84F9554FF8428BD88E246D2782AE2',
'0713612DCDDCB40AAB946BDA29CA91F73AF958AFD9'
);
$this->setBasePoint(
'0369979697AB43897789566789567F787A7876A654',
'00435EDB42EFAFB2989D51FEFCE3C80988F41FF883'
);
$this->setOrder(new
BigInteger('03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B', 16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/sect163r2.php000064400000001630151161424250015473
0ustar00<?php
/**
* sect163r2
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Binary;
use phpseclib3\Math\BigInteger;
class sect163r2 extends Binary
{
public function __construct()
{
$this->setModulo(163, 7, 6, 3, 0);
$this->setCoefficients(
'000000000000000000000000000000000000000001',
'020A601907B8C953CA1481EB10512F78744A3205FD'
);
$this->setBasePoint(
'03F0EBA16286A2D57EA0991168D4994637E8343E36',
'00D51FBC6C71A0094FA2CDD545B11C5C0C797324F1'
);
$this->setOrder(new
BigInteger('040000000000000000000292FE77E70C12A4234C33', 16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/sect193r1.php000064400000001673151161424250015504
0ustar00<?php
/**
* sect193r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Binary;
use phpseclib3\Math\BigInteger;
class sect193r1 extends Binary
{
public function __construct()
{
$this->setModulo(193, 15, 0);
$this->setCoefficients(
'0017858FEB7A98975169E171F77B4087DE098AC8A911DF7B01',
'00FDFB49BFE6C3A89FACADAA7A1E5BBC7CC1C2E5D831478814'
);
$this->setBasePoint(
'01F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E1',
'0025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05'
);
$this->setOrder(new
BigInteger('01000000000000000000000000C7F34A778F443ACC920EBA49',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/sect193r2.php000064400000001673151161424250015505
0ustar00<?php
/**
* sect193r2
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Binary;
use phpseclib3\Math\BigInteger;
class sect193r2 extends Binary
{
public function __construct()
{
$this->setModulo(193, 15, 0);
$this->setCoefficients(
'0163F35A5137C2CE3EA6ED8667190B0BC43ECD69977702709B',
'00C9BB9E8927D4D64C377E2AB2856A5B16E3EFB7F61D4316AE'
);
$this->setBasePoint(
'00D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F',
'01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C'
);
$this->setOrder(new
BigInteger('010000000000000000000000015AAB561B005413CCD4EE99D5',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/sect233k1.php000064400000001753151161424250015467
0ustar00<?php
/**
* sect233k1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Binary;
use phpseclib3\Math\BigInteger;
class sect233k1 extends Binary
{
public function __construct()
{
$this->setModulo(233, 74, 0);
$this->setCoefficients(
'000000000000000000000000000000000000000000000000000000000000',
'000000000000000000000000000000000000000000000000000000000001'
);
$this->setBasePoint(
'017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD6126',
'01DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3'
);
$this->setOrder(new
BigInteger('8000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/sect233r1.php000064400000001755151161424250015500
0ustar00<?php
/**
* sect233r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Binary;
use phpseclib3\Math\BigInteger;
class sect233r1 extends Binary
{
public function __construct()
{
$this->setModulo(233, 74, 0);
$this->setCoefficients(
'000000000000000000000000000000000000000000000000000000000001',
'0066647EDE6C332C7F8C0923BB58213B333B20E9CE4281FE115F7D8F90AD'
);
$this->setBasePoint(
'00FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B',
'01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052'
);
$this->setOrder(new
BigInteger('01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/sect239k1.php000064400000001760151161424250015473
0ustar00<?php
/**
* sect239k1
*
* PHP version 5 and 7
*
* @author Jim Wiggint on <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Binary;
use phpseclib3\Math\BigInteger;
class sect239k1 extends Binary
{
public function __construct()
{
$this->setModulo(239, 158, 0);
$this->setCoefficients(
'000000000000000000000000000000000000000000000000000000000000',
'000000000000000000000000000000000000000000000000000000000001'
);
$this->setBasePoint(
'29A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC',
'76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA'
);
$this->setOrder(new
BigInteger('2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/sect283k1.php000064400000002061151161424250015465
0ustar00<?php
/**
* sect283k1
*
* PHP version 5 and 7
*
* @author Jim Wiggint on <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Binary;
use phpseclib3\Math\BigInteger;
class sect283k1 extends Binary
{
public function __construct()
{
$this->setModulo(283, 12, 7, 5, 0);
$this->setCoefficients(
'000000000000000000000000000000000000000000000000000000000000000000000000',
'000000000000000000000000000000000000000000000000000000000000000000000001'
);
$this->setBasePoint(
'0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836',
'01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259'
);
$this->setOrder(new
BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/sect283r1.php000064400000002061151161424250015474
0ustar00<?php
/**
* sect283r1
*
* PHP version 5 and 7
*
* @author Jim Wiggint on <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Binary;
use phpseclib3\Math\BigInteger;
class sect283r1 extends Binary
{
public function __construct()
{
$this->setModulo(283, 12, 7, 5, 0);
$this->setCoefficients(
'000000000000000000000000000000000000000000000000000000000000000000000001',
'027B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5'
);
$this->setBasePoint(
'05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053',
'03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4'
);
$this->setOrder(new
BigInteger('03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/sect409k1.php000064400000002374151161424250015474
0ustar00<?php
/**
* sect409k1
*
* PHP version 5 and 7
*
* @author Jim Wiggint on <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Binary;
use phpseclib3\Math\BigInteger;
class sect409k1 extends Binary
{
public function __construct()
{
$this->setModulo(409, 87, 0);
$this->setCoefficients(
'00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
'00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001'
);
$this->setBasePoint(
'0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746',
'01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B'
);
$this->setOrder(new BigInteger(
'7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F' .
'83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF',
16
));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/sect409r1.php000064400000002376151161424250015505
0ustar00<?php
/**
* sect409r1
*
* PHP version 5 and 7
*
* @author Jim Wiggint on <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Binary;
use phpseclib3\Math\BigInteger;
class sect409r1 extends Binary
{
public function __construct()
{
$this->setModulo(409, 87, 0);
$this->setCoefficients(
'00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001',
'0021A5C2C8EE9FEB5C4B9A753B7B476B7FD6422EF1F3DD674761FA99D6AC27C8A9A197B272822F6CD57A55AA4F50AE317B13545F'
);
$this->setBasePoint(
'015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7',
'0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706'
);
$this->setOrder(new BigInteger(
'010000000000000000000000000000000000000000000000000001E2' .
'AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173',
16
));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/sect571k1.php000064400000003020151161424250015461
0ustar00<?php
/**
* sect571k1
*
* PHP version 5 and 7
*
* @author Jim Wiggint on <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Binary;
use phpseclib3\Math\BigInteger;
class sect571k1 extends Binary
{
public function __construct()
{
$this->setModulo(571, 10, 5, 2, 0);
$this->setCoefficients(
'000000000000000000000000000000000000000000000000000000000000000000000000'
.
'000000000000000000000000000000000000000000000000000000000000000000000000',
'000000000000000000000000000000000000000000000000000000000000000000000000'
.
'000000000000000000000000000000000000000000000000000000000000000000000001'
);
$this->setBasePoint(
'026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA443709584'
.
'93B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972',
'0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0'
.
'AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3'
);
$this->setOrder(new BigInteger(
'020000000000000000000000000000000000000000000000000000000000000000000000'
.
'131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001',
16
));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/sect571r1.php000064400000003020151161424250015470
0ustar00<?php
/**
* sect571r1
*
* PHP version 5 and 7
*
* @author Jim Wiggint on <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Binary;
use phpseclib3\Math\BigInteger;
class sect571r1 extends Binary
{
public function __construct()
{
$this->setModulo(571, 10, 5, 2, 0);
$this->setCoefficients(
'000000000000000000000000000000000000000000000000000000000000000000000000'
.
'000000000000000000000000000000000000000000000000000000000000000000000001',
'02F40E7E2221F295DE297117B7F3D62F5C6A97FFCB8CEFF1CD6BA8CE4A9A18AD84FFABBD'
.
'8EFA59332BE7AD6756A66E294AFD185A78FF12AA520E4DE739BACA0C7FFEFF7F2955727A'
);
$this->setBasePoint(
'0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950'
.
'F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19',
'037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43'
.
'BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B'
);
$this->setOrder(new BigInteger(
'03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'
.
'E661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47',
16
));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160r1.php000064400000002027151161424250016637
0ustar00<?php
/**
* brainpoolP160r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class brainpoolP160r1 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('E95E4A5F737059DC60DFC7AD95B3D8139515620F', 16));
$this->setCoefficients(
new
BigInteger('340E7BE2A280EB74E2BE61BADA745D97E8F7C300', 16),
new
BigInteger('1E589A8595423412134FAA2DBDEC95C8D8675E58', 16)
);
$this->setBasePoint(
new
BigInteger('BED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC3', 16),
new
BigInteger('1667CB477A1A8EC338F94741669C976316DA6321', 16)
);
$this->setOrder(new
BigInteger('E95E4A5F737059DC60DF5991D45029409E60FC09', 16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160t1.php000064400000003353151161424250016644
0ustar00<?php
/**
* brainpoolP160t1
*
* This curve is a twisted version of brainpoolP160r1 with A = -3. With
brainpool,
* the curves ending in r1 are the "regular" curves and the
curves ending in "t1"
* are the twisted version of the r1 curves. Per
https://tools.ietf.org/html/rfc5639#page-7
* you can convert a point on an r1 curve to a point on a t1 curve thusly:
*
* F(x,y) := (x*Z^2, y*Z^3)
*
* The advantage of A = -3 is that some of the point doubling and point
addition can be
* slightly optimized. See
http://hyperelliptic.org/EFD/g1p/auto-shortw-projective-3.html
* vs http://hyperelliptic.org/EFD/g1p/auto-shortw-projective.html for
example.
*
* phpseclib does not currently take advantage of this optimization
opportunity
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class brainpoolP160t1 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('E95E4A5F737059DC60DFC7AD95B3D8139515620F', 16));
$this->setCoefficients(
new
BigInteger('E95E4A5F737059DC60DFC7AD95B3D8139515620C', 16), //
eg. -3
new
BigInteger('7A556B6DAE535B7B51ED2C4D7DAA7A0B5C55F380', 16)
);
$this->setBasePoint(
new
BigInteger('B199B13B9B34EFC1397E64BAEB05ACC265FF2378', 16),
new
BigInteger('ADD6718B7C7C1961F0991B842443772152C9E0AD', 16)
);
$this->setOrder(new
BigInteger('E95E4A5F737059DC60DF5991D45029409E60FC09', 16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192r1.php000064400000002107151161424250016643
0ustar00<?php
/**
* brainpoolP192r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class brainpoolP192r1 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297',
16));
$this->setCoefficients(
new
BigInteger('6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF',
16),
new
BigInteger('469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9',
16)
);
$this->setBasePoint(
new
BigInteger('C0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD6',
16),
new
BigInteger('14B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F',
16)
);
$this->setOrder(new
BigInteger('C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192t1.php000064400000002121151161424250016641
0ustar00<?php
/**
* brainpoolP192t1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class brainpoolP192t1 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297',
16));
$this->setCoefficients(
new
BigInteger('C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86294',
16), // eg. -3
new
BigInteger('13D56FFAEC78681E68F9DEB43B35BEC2FB68542E27897B79',
16)
);
$this->setBasePoint(
new
BigInteger('3AE9E58C82F63C30282E1FE7BBF43FA72C446AF6F4618129',
16),
new
BigInteger('097E2C5667C2223A902AB5CA449D0084B7E5B3DE7CCC01C9',
16)
);
$this->setOrder(new
BigInteger('C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224r1.php000064400000002167151161424250016645
0ustar00<?php
/**
* brainpoolP224r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class brainpoolP224r1 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF',
16));
$this->setCoefficients(
new
BigInteger('68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43',
16),
new
BigInteger('2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B',
16)
);
$this->setBasePoint(
new
BigInteger('0D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D',
16),
new
BigInteger('58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD',
16)
);
$this->setOrder(new
BigInteger('D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224t1.php000064400000002201151161424250016634
0ustar00<?php
/**
* brainpoolP224t1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class brainpoolP224t1 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF',
16));
$this->setCoefficients(
new
BigInteger('D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FC',
16), // eg. -3
new
BigInteger('4B337D934104CD7BEF271BF60CED1ED20DA14C08B3BB64F18A60888D',
16)
);
$this->setBasePoint(
new
BigInteger('6AB1E344CE25FF3896424E7FFE14762ECB49F8928AC0C76029B4D580',
16),
new
BigInteger('0374E9F5143E568CD23F3F4D7C0D4B1E41C8CC0D1C6ABD5F1A46DB4C',
16)
);
$this->setOrder(new
BigInteger('D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256r1.php000064400000002247151161424250016651
0ustar00<?php
/**
* brainpoolP256r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class brainpoolP256r1 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377',
16));
$this->setCoefficients(
new
BigInteger('7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9',
16),
new
BigInteger('26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6',
16)
);
$this->setBasePoint(
new
BigInteger('8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262',
16),
new
BigInteger('547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997',
16)
);
$this->setOrder(new
BigInteger('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256t1.php000064400000002261151161424250016647
0ustar00<?php
/**
* brainpoolP256t1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class brainpoolP256t1 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377',
16));
$this->setCoefficients(
new
BigInteger('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5374',
16), // eg. -3
new
BigInteger('662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04',
16)
);
$this->setBasePoint(
new
BigInteger('A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F4',
16),
new
BigInteger('2D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE',
16)
);
$this->setOrder(new
BigInteger('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7',
16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320r1.php000064400000002740151161424250016637
0ustar00<?php
/**
* brainpoolP320r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class brainpoolP320r1 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F9'
.
'2B9EC7893EC28FCD412B1F1B32E27', 16));
$this->setCoefficients(
new
BigInteger('3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F4'
.
'92F375A97D860EB4', 16),
new
BigInteger('520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD88453981'
.
'6F5EB4AC8FB1F1A6', 16)
);
$this->setBasePoint(
new
BigInteger('43BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C7'
.
'10AF8D0D39E20611', 16),
new
BigInteger('14FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7'
.
'D35245D1692E8EE1', 16)
);
$this->setOrder(new
BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D4'
.
'82EC7EE8658E98691555B44C59311', 16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320t1.php000064400000002752151161424250016644
0ustar00<?php
/**
* brainpoolP320t1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class brainpoolP320t1 extends Prime
{
public function __construct()
{
$this->setModulo(new
BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F9'
.
'2B9EC7893EC28FCD412B1F1B32E27', 16));
$this->setCoefficients(
new
BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28'
.
'FCD412B1F1B32E24', 16), // eg. -3
new
BigInteger('A7F561E038EB1ED560B3D147DB782013064C19F27ED27C6780AAF77FB8A547CE'
.
'B5B4FEF422340353', 16)
);
$this->setBasePoint(
new
BigInteger('925BE9FB01AFC6FB4D3E7D4990010F813408AB106C4F09CB7EE07868CC136FFF'
.
'3357F624A21BED52', 16),
new
BigInteger('63BA3A7A27483EBF6671DBEF7ABB30EBEE084E58A0B077AD42A5A0989D1EE71B'
.
'1B9BC0455FB0D2C3', 16)
);
$this->setOrder(new
BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D4'
.
'82EC7EE8658E98691555B44C59311', 16));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384r1.php000064400000003330151161424250016645
0ustar00<?php
/**
* brainpoolP384r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class brainpoolP384r1 extends Prime
{
public function __construct()
{
$this->setModulo(new BigInteger(
'8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A7'
.
'1874700133107EC53',
16
));
$this->setCoefficients(
new BigInteger(
'7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503'
.
'AD4EB04A8C7DD22CE2826',
16
),
new BigInteger(
'4A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DB'
.
'C9943AB78696FA504C11',
16
)
);
$this->setBasePoint(
new BigInteger(
'1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D'
.
'646AAEF87B2E247D4AF1E',
16
),
new BigInteger(
'8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E464621779'
.
'1811142820341263C5315',
16
)
);
$this->setOrder(new BigInteger(
'8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC31'
.
'03B883202E9046565',
16
));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384t1.php000064400000003343151161424250016653
0ustar00<?php
/**
* brainpoolP384t1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class brainpoolP384t1 extends Prime
{
public function __construct()
{
$this->setModulo(new BigInteger(
'8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A7'
.
'1874700133107EC53',
16
));
$this->setCoefficients(
new BigInteger(
'8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901'
.
'D1A71874700133107EC50',
16
), // eg. -3
new BigInteger(
'7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE1D2074AA263B8'
.
'8805CED70355A33B471EE',
16
)
);
$this->setBasePoint(
new BigInteger(
'18DE98B02DB9A306F2AFCD7235F72A819B80AB12EBD653172476FECD462AABFFC4FF191B946'
.
'A5F54D8D0AA2F418808CC',
16
),
new BigInteger(
'25AB056962D30651A114AFD2755AD336747F93475B7A1FCA3B88F2B6A208CCFE469408584DC'
.
'2B2912675BF5B9E582928',
16
)
);
$this->setOrder(new BigInteger(
'8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC31'
.
'03B883202E9046565',
16
));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512r1.php000064400000003631151161424250016642
0ustar00<?php
/**
* brainpoolP512r1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class brainpoolP512r1 extends Prime
{
public function __construct()
{
$this->setModulo(new BigInteger(
'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC'
.
'66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3',
16
));
$this->setCoefficients(
new BigInteger(
'7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA82'
.
'53AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA',
16
),
new BigInteger(
'3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C'
.
'1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723',
16
)
);
$this->setBasePoint(
new BigInteger(
'81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D'
.
'0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822',
16
),
new BigInteger(
'7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5'
.
'F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892',
16
)
);
$this->setOrder(new BigInteger(
'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA'
.
'92619418661197FAC10471DB1D381085DDADDB58796829CA90069',
16
));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512t1.php000064400000003643151161424250016647
0ustar00<?php
/**
* brainpoolP512t1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Prime;
use phpseclib3\Math\BigInteger;
class brainpoolP512t1 extends Prime
{
public function __construct()
{
$this->setModulo(new BigInteger(
'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC'
.
'66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3',
16
));
$this->setCoefficients(
new BigInteger(
'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC'
.
'66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F0',
16
), // eg. -3
new BigInteger(
'7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36A62BCDFA23049'
.
'76540F6450085F2DAE145C22553B465763689180EA2571867423E',
16
)
);
$this->setBasePoint(
new BigInteger(
'640ECE5C12788717B9C1BA06CBC2A6FEBA85842458C56DDE9DB1758D39C0313D82BA51735CD'
.
'B3EA499AA77A7D6943A64F7A3F25FE26F06B51BAA2696FA9035DA',
16
),
new BigInteger(
'5B534BD595F5AF0FA2C892376C84ACE1BB4E3019B71634C01131159CAE03CEE9D9932184BEE'
.
'F216BD71DF2DADF86A627306ECFF96DBB8BACE198B61E00F8B332',
16
)
);
$this->setOrder(new BigInteger(
'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA'
.
'92619418661197FAC10471DB1D381085DDADDB58796829CA90069',
16
));
}
}
phpseclib/phpseclib/Crypt/EC/Curves/Curve25519.php000064400000004413151161424250015533
0ustar00<?php
/**
* Curve25519
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2019 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Montgomery;
use phpseclib3\Math\BigInteger;
class Curve25519 extends Montgomery
{
public function __construct()
{
// 2^255 - 19
$this->setModulo(new
BigInteger('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED',
16));
$this->a24 = $this->factory->newInteger(new
BigInteger('121666'));
$this->p = [$this->factory->newInteger(new
BigInteger(9))];
// 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed
$this->setOrder(new
BigInteger('1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED',
16));
/*
$this->setCoefficients(
new BigInteger('486662'), // a
);
$this->setBasePoint(
new BigInteger(9),
new
BigInteger('14781619447589544791020593568409986887264606134616475288964881837755586237401')
);
*/
}
/**
* Multiply a point on the curve by a scalar
*
* Modifies the scalar as described at
https://tools.ietf.org/html/rfc7748#page-8
*
* @return array
*/
public function multiplyPoint(array $p, BigInteger $d)
{
//$r = strrev(sodium_crypto_scalarmult($d->toBytes(),
strrev($p[0]->toBytes())));
//return [$this->factory->newInteger(new BigInteger($r,
256))];
$d = $d->toBytes();
$d &= "\xF8" . str_repeat("\xFF", 30) .
"\x7F";
$d = strrev($d);
$d |= "\x40";
$d = new BigInteger($d, -256);
return parent::multiplyPoint($p, $d);
}
/**
* Creates a random scalar multiplier
*
* @return BigInteger
*/
public function createRandomMultiplier()
{
return BigInteger::random(256);
}
/**
* Performs range check
*/
public function rangeCheck(BigInteger $x)
{
if ($x->getLength() > 256 || $x->isNegative()) {
throw new \RangeException('x must be a positive integer
less than 256 bytes in length');
}
}
}
phpseclib/phpseclib/Crypt/EC/Curves/Curve448.php000064400000005107151161424250015366
0ustar00<?php
/**
* Curve448
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2019 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\Montgomery;
use phpseclib3\Math\BigInteger;
class Curve448 extends Montgomery
{
public function __construct()
{
// 2^448 - 2^224 - 1
$this->setModulo(new BigInteger(
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE' .
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',
16
));
$this->a24 = $this->factory->newInteger(new
BigInteger('39081'));
$this->p = [$this->factory->newInteger(new
BigInteger(5))];
// 2^446 -
0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d
$this->setOrder(new BigInteger(
'3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' .
'7CCA23E9C44EDB49AED63690216CC2728DC58F552378C292AB5844F3',
16
));
/*
$this->setCoefficients(
new BigInteger('156326'), // a
);
$this->setBasePoint(
new BigInteger(5),
new BigInteger(
'355293926785568175264127502063783334808976399387714271831880898'
.
'435169088786967410002932673765864550910142774147268105838985595290'
.
'606362')
);
*/
}
/**
* Multiply a point on the curve by a scalar
*
* Modifies the scalar as described at
https://tools.ietf.org/html/rfc7748#page-8
*
* @return array
*/
public function multiplyPoint(array $p, BigInteger $d)
{
//$r = strrev(sodium_crypto_scalarmult($d->toBytes(),
strrev($p[0]->toBytes())));
//return [$this->factory->newInteger(new BigInteger($r,
256))];
$d = $d->toBytes();
$d[0] = $d[0] & "\xFC";
$d = strrev($d);
$d |= "\x80";
$d = new BigInteger($d, 256);
return parent::multiplyPoint($p, $d);
}
/**
* Creates a random scalar multiplier
*
* @return BigInteger
*/
public function createRandomMultiplier()
{
return BigInteger::random(446);
}
/**
* Performs range check
*/
public function rangeCheck(BigInteger $x)
{
if ($x->getLength() > 448 || $x->isNegative()) {
throw new \RangeException('x must be a positive integer
less than 446 bytes in length');
}
}
}
phpseclib/phpseclib/Crypt/EC/Curves/Ed25519.php000064400000023710151161424250015000
0ustar00<?php
/**
* Ed25519
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards;
use phpseclib3\Crypt\Hash;
use phpseclib3\Crypt\Random;
use phpseclib3\Math\BigInteger;
class Ed25519 extends TwistedEdwards
{
const HASH = 'sha512';
/*
Per https://tools.ietf.org/html/rfc8032#page-6 EdDSA has several
parameters, one of which is b:
2. An integer b with 2^(b-1) > p. EdDSA public keys have
exactly b
bits, and EdDSA signatures have exactly 2*b bits. b is
recommended to be a multiple of 8, so public key and signature
lengths are an integral number of octets.
SIZE corresponds to b
*/
const SIZE = 32;
public function __construct()
{
// 2^255 - 19
$this->setModulo(new
BigInteger('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED',
16));
$this->setCoefficients(
// -1
new
BigInteger('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC',
16), // a
// -121665/121666
new
BigInteger('52036CEE2B6FFE738CC740797779E89800700A4D4141D8AB75EB4DCA135978A3',
16) // d
);
$this->setBasePoint(
new
BigInteger('216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A',
16),
new
BigInteger('6666666666666666666666666666666666666666666666666666666666666658',
16)
);
$this->setOrder(new
BigInteger('1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED',
16));
// algorithm 14.47 from
http://cacr.uwaterloo.ca/hac/about/chap14.pdf#page=16
/*
$this->setReduction(function($x) {
$parts = $x->bitwise_split(255);
$className = $this->className;
if (count($parts) > 2) {
list(, $r) = $x->divide($className::$modulo);
return $r;
}
$zero = new BigInteger();
$c = new BigInteger(19);
switch (count($parts)) {
case 2:
list($qi, $ri) = $parts;
break;
case 1:
$qi = $zero;
list($ri) = $parts;
break;
case 0:
return $zero;
}
$r = $ri;
while ($qi->compare($zero) > 0) {
$temp = $qi->multiply($c)->bitwise_split(255);
if (count($temp) == 2) {
list($qi, $ri) = $temp;
} else {
$qi = $zero;
list($ri) = $temp;
}
$r = $r->add($ri);
}
while ($r->compare($className::$modulo) > 0) {
$r = $r->subtract($className::$modulo);
}
return $r;
});
*/
}
/**
* Recover X from Y
*
* Implements steps 2-4 at
https://tools.ietf.org/html/rfc8032#section-5.1.3
*
* Used by EC\Keys\Common.php
*
* @param BigInteger $y
* @param boolean $sign
* @return object[]
*/
public function recoverX(BigInteger $y, $sign)
{
$y = $this->factory->newInteger($y);
$y2 = $y->multiply($y);
$u = $y2->subtract($this->one);
$v = $this->d->multiply($y2)->add($this->one);
$x2 = $u->divide($v);
if ($x2->equals($this->zero)) {
if ($sign) {
throw new \RuntimeException('Unable to recover X
coordinate (x2 = 0)');
}
return clone $this->zero;
}
// find the square root
/* we don't do $x2->squareRoot() because, quoting from
https://tools.ietf.org/html/rfc8032#section-5.1.1:
"For point decoding or "decompression", square
roots modulo p are
needed. They can be computed using the Tonelli-Shanks
algorithm or
the special case for p = 5 (mod 8). To find a square root of
a,
first compute the candidate root x = a^((p+3)/8) (mod p)."
*/
$exp = $this->getModulo()->add(new BigInteger(3));
$exp = $exp->bitwise_rightShift(3);
$x = $x2->pow($exp);
// If v x^2 = -u (mod p), set x <-- x * 2^((p-1)/4), which is a
square root.
if
(!$x->multiply($x)->subtract($x2)->equals($this->zero)) {
$temp = $this->getModulo()->subtract(new BigInteger(1));
$temp = $temp->bitwise_rightShift(2);
$temp = $this->two->pow($temp);
$x = $x->multiply($temp);
if
(!$x->multiply($x)->subtract($x2)->equals($this->zero)) {
throw new \RuntimeException('Unable to recover X
coordinate');
}
}
if ($x->isOdd() != $sign) {
$x = $x->negate();
}
return [$x, $y];
}
/**
* Extract Secret Scalar
*
* Implements steps 1-3 at
https://tools.ietf.org/html/rfc8032#section-5.1.5
*
* Used by the various key handlers
*
* @param string $str
* @return array
*/
public function extractSecret($str)
{
if (strlen($str) != 32) {
throw new \LengthException('Private Key should be 32-bytes
long');
}
// 1. Hash the 32-byte private key using SHA-512, storing the
digest in
// a 64-octet large buffer, denoted h. Only the lower 32 bytes
are
// used for generating the public key.
$hash = new Hash('sha512');
$h = $hash->hash($str);
$h = substr($h, 0, 32);
// 2. Prune the buffer: The lowest three bits of the first octet
are
// cleared, the highest bit of the last octet is cleared, and
the
// second highest bit of the last octet is set.
$h[0] = $h[0] & chr(0xF8);
$h = strrev($h);
$h[0] = ($h[0] & chr(0x3F)) | chr(0x40);
// 3. Interpret the buffer as the little-endian integer, forming a
// secret scalar s.
$dA = new BigInteger($h, 256);
return [
'dA' => $dA,
'secret' => $str
];
}
/**
* Encode a point as a string
*
* @param array $point
* @return string
*/
public function encodePoint($point)
{
list($x, $y) = $point;
$y = $y->toBytes();
$y[0] = $y[0] & chr(0x7F);
if ($x->isOdd()) {
$y[0] = $y[0] | chr(0x80);
}
$y = strrev($y);
return $y;
}
/**
* Creates a random scalar multiplier
*
* @return \phpseclib3\Math\PrimeField\Integer
*/
public function createRandomMultiplier()
{
return $this->extractSecret(Random::string(32))['dA'];
}
/**
* Converts an affine point to an extended homogeneous coordinate
*
* From https://tools.ietf.org/html/rfc8032#section-5.1.4 :
*
* A point (x,y) is represented in extended homogeneous coordinates (X,
Y, Z, T),
* with x = X/Z, y = Y/Z, x * y = T/Z.
*
* @return \phpseclib3\Math\PrimeField\Integer[]
*/
public function convertToInternal(array $p)
{
if (empty($p)) {
return [clone $this->zero, clone $this->one, clone
$this->one, clone $this->zero];
}
if (isset($p[2])) {
return $p;
}
$p[2] = clone $this->one;
$p[3] = $p[0]->multiply($p[1]);
return $p;
}
/**
* Doubles a point on a curve
*
* @return FiniteField[]
*/
public function doublePoint(array $p)
{
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
if (!count($p)) {
return [];
}
if (!isset($p[2])) {
throw new \RuntimeException('Affine coordinates need to be
manually converted to "Jacobi" coordinates or vice versa');
}
// from https://tools.ietf.org/html/rfc8032#page-12
list($x1, $y1, $z1, $t1) = $p;
$a = $x1->multiply($x1);
$b = $y1->multiply($y1);
$c = $this->two->multiply($z1)->multiply($z1);
$h = $a->add($b);
$temp = $x1->add($y1);
$e = $h->subtract($temp->multiply($temp));
$g = $a->subtract($b);
$f = $c->add($g);
$x3 = $e->multiply($f);
$y3 = $g->multiply($h);
$t3 = $e->multiply($h);
$z3 = $f->multiply($g);
return [$x3, $y3, $z3, $t3];
}
/**
* Adds two points on the curve
*
* @return FiniteField[]
*/
public function addPoint(array $p, array $q)
{
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
if (!count($p) || !count($q)) {
if (count($q)) {
return $q;
}
if (count($p)) {
return $p;
}
return [];
}
if (!isset($p[2]) || !isset($q[2])) {
throw new \RuntimeException('Affine coordinates need to be
manually converted to "Jacobi" coordinates or vice versa');
}
if ($p[0]->equals($q[0])) {
return !$p[1]->equals($q[1]) ? [] :
$this->doublePoint($p);
}
// from https://tools.ietf.org/html/rfc8032#page-12
list($x1, $y1, $z1, $t1) = $p;
list($x2, $y2, $z2, $t2) = $q;
$a = $y1->subtract($x1)->multiply($y2->subtract($x2));
$b = $y1->add($x1)->multiply($y2->add($x2));
$c =
$t1->multiply($this->two)->multiply($this->d)->multiply($t2);
$d = $z1->multiply($this->two)->multiply($z2);
$e = $b->subtract($a);
$f = $d->subtract($c);
$g = $d->add($c);
$h = $b->add($a);
$x3 = $e->multiply($f);
$y3 = $g->multiply($h);
$t3 = $e->multiply($h);
$z3 = $f->multiply($g);
return [$x3, $y3, $z3, $t3];
}
}
phpseclib/phpseclib/Crypt/EC/Curves/Ed448.php000064400000017250151161424250014634
0ustar00<?php
/**
* Ed448
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
*/
namespace phpseclib3\Crypt\EC\Curves;
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards;
use phpseclib3\Crypt\Hash;
use phpseclib3\Crypt\Random;
use phpseclib3\Math\BigInteger;
class Ed448 extends TwistedEdwards
{
const HASH = 'shake256-912';
const SIZE = 57;
public function __construct()
{
// 2^448 - 2^224 - 1
$this->setModulo(new BigInteger(
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE' .
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',
16
));
$this->setCoefficients(
new BigInteger(1),
// -39081
new
BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE'
.
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6756', 16)
);
$this->setBasePoint(
new
BigInteger('4F1970C66BED0DED221D15A622BF36DA9E146570470F1767EA6DE324'
.
'A3D3A46412AE1AF72AB66511433B80E18B00938E2626A82BC70CC05E', 16),
new
BigInteger('693F46716EB6BC248876203756C9C7624BEA73736CA3984087789C1E'
.
'05A0C2D73AD3FF1CE67C39C4FDBD132C4ED7C8AD9808795BF230FA14', 16)
);
$this->setOrder(new BigInteger(
'3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' .
'7CCA23E9C44EDB49AED63690216CC2728DC58F552378C292AB5844F3',
16
));
}
/**
* Recover X from Y
*
* Implements steps 2-4 at
https://tools.ietf.org/html/rfc8032#section-5.2.3
*
* Used by EC\Keys\Common.php
*
* @param BigInteger $y
* @param boolean $sign
* @return object[]
*/
public function recoverX(BigInteger $y, $sign)
{
$y = $this->factory->newInteger($y);
$y2 = $y->multiply($y);
$u = $y2->subtract($this->one);
$v = $this->d->multiply($y2)->subtract($this->one);
$x2 = $u->divide($v);
if ($x2->equals($this->zero)) {
if ($sign) {
throw new \RuntimeException('Unable to recover X
coordinate (x2 = 0)');
}
return clone $this->zero;
}
// find the square root
$exp = $this->getModulo()->add(new BigInteger(1));
$exp = $exp->bitwise_rightShift(2);
$x = $x2->pow($exp);
if
(!$x->multiply($x)->subtract($x2)->equals($this->zero)) {
throw new \RuntimeException('Unable to recover X
coordinate');
}
if ($x->isOdd() != $sign) {
$x = $x->negate();
}
return [$x, $y];
}
/**
* Extract Secret Scalar
*
* Implements steps 1-3 at
https://tools.ietf.org/html/rfc8032#section-5.2.5
*
* Used by the various key handlers
*
* @param string $str
* @return array
*/
public function extractSecret($str)
{
if (strlen($str) != 57) {
throw new \LengthException('Private Key should be 57-bytes
long');
}
// 1. Hash the 57-byte private key using SHAKE256(x, 114), storing
the
// digest in a 114-octet large buffer, denoted h. Only the
lower 57
// bytes are used for generating the public key.
$hash = new Hash('shake256-912');
$h = $hash->hash($str);
$h = substr($h, 0, 57);
// 2. Prune the buffer: The two least significant bits of the
first
// octet are cleared, all eight bits the last octet are
cleared, and
// the highest bit of the second to last octet is set.
$h[0] = $h[0] & chr(0xFC);
$h = strrev($h);
$h[0] = "\0";
$h[1] = $h[1] | chr(0x80);
// 3. Interpret the buffer as the little-endian integer, forming a
// secret scalar s.
$dA = new BigInteger($h, 256);
return [
'dA' => $dA,
'secret' => $str
];
$dA->secret = $str;
return $dA;
}
/**
* Encode a point as a string
*
* @param array $point
* @return string
*/
public function encodePoint($point)
{
list($x, $y) = $point;
$y = "\0" . $y->toBytes();
if ($x->isOdd()) {
$y[0] = $y[0] | chr(0x80);
}
$y = strrev($y);
return $y;
}
/**
* Creates a random scalar multiplier
*
* @return \phpseclib3\Math\PrimeField\Integer
*/
public function createRandomMultiplier()
{
return $this->extractSecret(Random::string(57))['dA'];
}
/**
* Converts an affine point to an extended homogeneous coordinate
*
* From https://tools.ietf.org/html/rfc8032#section-5.2.4 :
*
* A point (x,y) is represented in extended homogeneous coordinates (X,
Y, Z, T),
* with x = X/Z, y = Y/Z, x * y = T/Z.
*
* @return \phpseclib3\Math\PrimeField\Integer[]
*/
public function convertToInternal(array $p)
{
if (empty($p)) {
return [clone $this->zero, clone $this->one, clone
$this->one];
}
if (isset($p[2])) {
return $p;
}
$p[2] = clone $this->one;
return $p;
}
/**
* Doubles a point on a curve
*
* @return FiniteField[]
*/
public function doublePoint(array $p)
{
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
if (!count($p)) {
return [];
}
if (!isset($p[2])) {
throw new \RuntimeException('Affine coordinates need to be
manually converted to "Jacobi" coordinates or vice versa');
}
// from https://tools.ietf.org/html/rfc8032#page-18
list($x1, $y1, $z1) = $p;
$b = $x1->add($y1);
$b = $b->multiply($b);
$c = $x1->multiply($x1);
$d = $y1->multiply($y1);
$e = $c->add($d);
$h = $z1->multiply($z1);
$j = $e->subtract($this->two->multiply($h));
$x3 = $b->subtract($e)->multiply($j);
$y3 = $c->subtract($d)->multiply($e);
$z3 = $e->multiply($j);
return [$x3, $y3, $z3];
}
/**
* Adds two points on the curve
*
* @return FiniteField[]
*/
public function addPoint(array $p, array $q)
{
if (!isset($this->factory)) {
throw new \RuntimeException('setModulo needs to be called
before this method');
}
if (!count($p) || !count($q)) {
if (count($q)) {
return $q;
}
if (count($p)) {
return $p;
}
return [];
}
if (!isset($p[2]) || !isset($q[2])) {
throw new \RuntimeException('Affine coordinates need to be
manually converted to "Jacobi" coordinates or vice versa');
}
if ($p[0]->equals($q[0])) {
return !$p[1]->equals($q[1]) ? [] :
$this->doublePoint($p);
}
// from https://tools.ietf.org/html/rfc8032#page-17
list($x1, $y1, $z1) = $p;
list($x2, $y2, $z2) = $q;
$a = $z1->multiply($z2);
$b = $a->multiply($a);
$c = $x1->multiply($x2);
$d = $y1->multiply($y2);
$e = $this->d->multiply($c)->multiply($d);
$f = $b->subtract($e);
$g = $b->add($e);
$h = $x1->add($y1)->multiply($x2->add($y2));
$x3 =
$a->multiply($f)->multiply($h->subtract($c)->subtract($d));
$y3 = $a->multiply($g)->multiply($d->subtract($c));
$z3 = $f->multiply($g);
return [$x3, $y3, $z3];
}
}
phpseclib/phpseclib/Crypt/EC/Curves/nistb233.php000064400000000546151161424250015413
0ustar00<?php
/**
* nistb233
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
final class nistb233 extends sect233r1
{
}
phpseclib/phpseclib/Crypt/EC/Curves/nistb409.php000064400000000546151161424250015420
0ustar00<?php
/**
* nistb409
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
final class nistb409 extends sect409r1
{
}
phpseclib/phpseclib/Crypt/EC/Curves/nistk163.php000064400000000546151161424250015426
0ustar00<?php
/**
* nistk163
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
final class nistk163 extends sect163k1
{
}
phpseclib/phpseclib/Crypt/EC/Curves/nistk233.php000064400000000546151161424250015424
0ustar00<?php
/**
* nistk233
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Crypt\EC\Curves;
final class nistk233 extends sect233k1
{
}
phpseclib/phpseclib/Crypt/EC/Formats/Keys/Common.php000064400000055704151161424250016361
0ustar00<?php
/**
* Generic EC Key Parsing Helper functions
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\EC\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
use phpseclib3\Crypt\EC\BaseCurves\Binary as BinaryCurve;
use phpseclib3\Crypt\EC\BaseCurves\Prime as PrimeCurve;
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
use phpseclib3\Exception\UnsupportedCurveException;
use phpseclib3\File\ASN1;
use phpseclib3\File\ASN1\Maps;
use phpseclib3\Math\BigInteger;
/**
* Generic EC Key Parsing Helper functions
*
* @author Jim Wigginton <terrafrost@php.net>
*/
trait Common
{
/**
* Curve OIDs
*
* @var array
*/
private static $curveOIDs = [];
/**
* Child OIDs loaded
*
* @var bool
*/
protected static $childOIDsLoaded = false;
/**
* Use Named Curves
*
* @var bool
*/
private static $useNamedCurves = true;
/**
* Initialize static variables
*/
private static function initialize_static_variables()
{
if (empty(self::$curveOIDs)) {
// the sec* curves are from the standards for efficient
cryptography group
// sect* curves are curves over binary finite fields
// secp* curves are curves over prime finite fields
// sec*r* curves are regular curves; sec*k* curves are koblitz
curves
// brainpool*r* curves are regular prime finite field curves
// brainpool*t* curves are twisted versions of the brainpool*r*
curves
self::$curveOIDs = [
'prime192v1' =>
'1.2.840.10045.3.1.1', // J.5.1, example 1 (aka secp192r1)
'prime192v2' =>
'1.2.840.10045.3.1.2', // J.5.1, example 2
'prime192v3' =>
'1.2.840.10045.3.1.3', // J.5.1, example 3
'prime239v1' =>
'1.2.840.10045.3.1.4', // J.5.2, example 1
'prime239v2' =>
'1.2.840.10045.3.1.5', // J.5.2, example 2
'prime239v3' =>
'1.2.840.10045.3.1.6', // J.5.2, example 3
'prime256v1' =>
'1.2.840.10045.3.1.7', // J.5.3, example 1 (aka secp256r1)
// https://tools.ietf.org/html/rfc5656#section-10
'nistp256' => '1.2.840.10045.3.1.7',
// aka secp256r1
'nistp384' => '1.3.132.0.34', // aka
secp384r1
'nistp521' => '1.3.132.0.35', // aka
secp521r1
'nistk163' => '1.3.132.0.1', // aka
sect163k1
'nistp192' => '1.2.840.10045.3.1.1',
// aka secp192r1
'nistp224' => '1.3.132.0.33', // aka
secp224r1
'nistk233' => '1.3.132.0.26', // aka
sect233k1
'nistb233' => '1.3.132.0.27', // aka
sect233r1
'nistk283' => '1.3.132.0.16', // aka
sect283k1
'nistk409' => '1.3.132.0.36', // aka
sect409k1
'nistb409' => '1.3.132.0.37', // aka
sect409r1
'nistt571' => '1.3.132.0.38', // aka
sect571k1
// from https://tools.ietf.org/html/rfc5915
'secp192r1' =>
'1.2.840.10045.3.1.1', // aka prime192v1
'sect163k1' => '1.3.132.0.1',
'sect163r2' => '1.3.132.0.15',
'secp224r1' => '1.3.132.0.33',
'sect233k1' => '1.3.132.0.26',
'sect233r1' => '1.3.132.0.27',
'secp256r1' =>
'1.2.840.10045.3.1.7', // aka prime256v1
'sect283k1' => '1.3.132.0.16',
'sect283r1' => '1.3.132.0.17',
'secp384r1' => '1.3.132.0.34',
'sect409k1' => '1.3.132.0.36',
'sect409r1' => '1.3.132.0.37',
'secp521r1' => '1.3.132.0.35',
'sect571k1' => '1.3.132.0.38',
'sect571r1' => '1.3.132.0.39',
// from http://www.secg.org/SEC2-Ver-1.0.pdf
'secp112r1' => '1.3.132.0.6',
'secp112r2' => '1.3.132.0.7',
'secp128r1' => '1.3.132.0.28',
'secp128r2' => '1.3.132.0.29',
'secp160k1' => '1.3.132.0.9',
'secp160r1' => '1.3.132.0.8',
'secp160r2' => '1.3.132.0.30',
'secp192k1' => '1.3.132.0.31',
'secp224k1' => '1.3.132.0.32',
'secp256k1' => '1.3.132.0.10',
'sect113r1' => '1.3.132.0.4',
'sect113r2' => '1.3.132.0.5',
'sect131r1' => '1.3.132.0.22',
'sect131r2' => '1.3.132.0.23',
'sect163r1' => '1.3.132.0.2',
'sect193r1' => '1.3.132.0.24',
'sect193r2' => '1.3.132.0.25',
'sect239k1' => '1.3.132.0.3',
// from
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.202.2977&rep=rep1&type=pdf#page=36
/*
'c2pnb163v1' =>
'1.2.840.10045.3.0.1', // J.4.1, example 1
'c2pnb163v2' =>
'1.2.840.10045.3.0.2', // J.4.1, example 2
'c2pnb163v3' =>
'1.2.840.10045.3.0.3', // J.4.1, example 3
'c2pnb172w1' =>
'1.2.840.10045.3.0.4', // J.4.2, example 1
'c2tnb191v1' =>
'1.2.840.10045.3.0.5', // J.4.3, example 1
'c2tnb191v2' =>
'1.2.840.10045.3.0.6', // J.4.3, example 2
'c2tnb191v3' =>
'1.2.840.10045.3.0.7', // J.4.3, example 3
'c2onb191v4' =>
'1.2.840.10045.3.0.8', // J.4.3, example 4
'c2onb191v5' =>
'1.2.840.10045.3.0.9', // J.4.3, example 5
'c2pnb208w1' =>
'1.2.840.10045.3.0.10', // J.4.4, example 1
'c2tnb239v1' =>
'1.2.840.10045.3.0.11', // J.4.5, example 1
'c2tnb239v2' =>
'1.2.840.10045.3.0.12', // J.4.5, example 2
'c2tnb239v3' =>
'1.2.840.10045.3.0.13', // J.4.5, example 3
'c2onb239v4' =>
'1.2.840.10045.3.0.14', // J.4.5, example 4
'c2onb239v5' =>
'1.2.840.10045.3.0.15', // J.4.5, example 5
'c2pnb272w1' =>
'1.2.840.10045.3.0.16', // J.4.6, example 1
'c2pnb304w1' =>
'1.2.840.10045.3.0.17', // J.4.7, example 1
'c2tnb359v1' =>
'1.2.840.10045.3.0.18', // J.4.8, example 1
'c2pnb368w1' =>
'1.2.840.10045.3.0.19', // J.4.9, example 1
'c2tnb431r1' =>
'1.2.840.10045.3.0.20', // J.4.10, example 1
*/
//
http://www.ecc-brainpool.org/download/Domain-parameters.pdf
// https://tools.ietf.org/html/rfc5639
'brainpoolP160r1' =>
'1.3.36.3.3.2.8.1.1.1',
'brainpoolP160t1' =>
'1.3.36.3.3.2.8.1.1.2',
'brainpoolP192r1' =>
'1.3.36.3.3.2.8.1.1.3',
'brainpoolP192t1' =>
'1.3.36.3.3.2.8.1.1.4',
'brainpoolP224r1' =>
'1.3.36.3.3.2.8.1.1.5',
'brainpoolP224t1' =>
'1.3.36.3.3.2.8.1.1.6',
'brainpoolP256r1' =>
'1.3.36.3.3.2.8.1.1.7',
'brainpoolP256t1' =>
'1.3.36.3.3.2.8.1.1.8',
'brainpoolP320r1' =>
'1.3.36.3.3.2.8.1.1.9',
'brainpoolP320t1' =>
'1.3.36.3.3.2.8.1.1.10',
'brainpoolP384r1' =>
'1.3.36.3.3.2.8.1.1.11',
'brainpoolP384t1' =>
'1.3.36.3.3.2.8.1.1.12',
'brainpoolP512r1' =>
'1.3.36.3.3.2.8.1.1.13',
'brainpoolP512t1' =>
'1.3.36.3.3.2.8.1.1.14'
];
ASN1::loadOIDs([
'prime-field' =>
'1.2.840.10045.1.1',
'characteristic-two-field' =>
'1.2.840.10045.1.2',
'characteristic-two-basis' =>
'1.2.840.10045.1.2.3',
// per http://www.secg.org/SEC1-Ver-1.0.pdf#page=84,
gnBasis "not used here"
'gnBasis' =>
'1.2.840.10045.1.2.3.1', // NULL
'tpBasis' =>
'1.2.840.10045.1.2.3.2', // Trinomial
'ppBasis' => '1.2.840.10045.1.2.3.3'
// Pentanomial
] + self::$curveOIDs);
}
}
/**
* Explicitly set the curve
*
* If the key contains an implicit curve phpseclib needs the curve
* to be explicitly provided
*
* @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
*/
public static function setImplicitCurve(BaseCurve $curve)
{
self::$implicitCurve = $curve;
}
/**
* Returns an instance of \phpseclib3\Crypt\EC\BaseCurves\Base based
* on the curve parameters
*
* @param array $params
* @return \phpseclib3\Crypt\EC\BaseCurves\Base|false
*/
protected static function loadCurveByParam(array $params)
{
if (count($params) > 1) {
throw new \RuntimeException('No parameters are
present');
}
if (isset($params['namedCurve'])) {
$curve = '\phpseclib3\Crypt\EC\Curves\\' .
$params['namedCurve'];
if (!class_exists($curve)) {
throw new UnsupportedCurveException('Named Curve of
' . $params['namedCurve'] . ' is not supported');
}
return new $curve();
}
if (isset($params['implicitCurve'])) {
if (!isset(self::$implicitCurve)) {
throw new \RuntimeException('Implicit curves can be
provided by calling setImplicitCurve');
}
return self::$implicitCurve;
}
if (isset($params['specifiedCurve'])) {
$data = $params['specifiedCurve'];
switch ($data['fieldID']['fieldType']) {
case 'prime-field':
$curve = new PrimeCurve();
$curve->setModulo($data['fieldID']['parameters']);
$curve->setCoefficients(
new
BigInteger($data['curve']['a'], 256),
new
BigInteger($data['curve']['b'], 256)
);
$point = self::extractPoint("\0" .
$data['base'], $curve);
$curve->setBasePoint(...$point);
$curve->setOrder($data['order']);
return $curve;
case 'characteristic-two-field':
$curve = new BinaryCurve();
$params =
ASN1::decodeBER($data['fieldID']['parameters']);
$params = ASN1::asn1map($params[0],
Maps\Characteristic_two::MAP);
$modulo = [(int)
$params['m']->toString()];
switch ($params['basis']) {
case 'tpBasis':
$modulo[] = (int)
$params['parameters']->toString();
break;
case 'ppBasis':
$temp =
ASN1::decodeBER($params['parameters']);
$temp = ASN1::asn1map($temp[0],
Maps\Pentanomial::MAP);
$modulo[] = (int)
$temp['k3']->toString();
$modulo[] = (int)
$temp['k2']->toString();
$modulo[] = (int)
$temp['k1']->toString();
}
$modulo[] = 0;
$curve->setModulo(...$modulo);
$len = ceil($modulo[0] / 8);
$curve->setCoefficients(
Strings::bin2hex($data['curve']['a']),
Strings::bin2hex($data['curve']['b'])
);
$point = self::extractPoint("\0" .
$data['base'], $curve);
$curve->setBasePoint(...$point);
$curve->setOrder($data['order']);
return $curve;
default:
throw new UnsupportedCurveException('Field Type of
' . $data['fieldID']['fieldType'] . ' is not
supported');
}
}
throw new \RuntimeException('No valid parameters are
present');
}
/**
* Extract points from a string
*
* Supports both compressed and uncompressed points
*
* @param string $str
* @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
* @return object[]
*/
public static function extractPoint($str, BaseCurve $curve)
{
if ($curve instanceof TwistedEdwardsCurve) {
// first step of point deciding as discussed at the following
URL's:
// https://tools.ietf.org/html/rfc8032#section-5.1.3
// https://tools.ietf.org/html/rfc8032#section-5.2.3
$y = $str;
$y = strrev($y);
$sign = (bool) (ord($y[0]) & 0x80);
$y[0] = $y[0] & chr(0x7F);
$y = new BigInteger($y, 256);
if ($y->compare($curve->getModulo()) >= 0) {
throw new \RuntimeException('The Y coordinate should
not be >= the modulo');
}
$point = $curve->recoverX($y, $sign);
if (!$curve->verifyPoint($point)) {
throw new \RuntimeException('Unable to verify that
point exists on curve');
}
return $point;
}
// the first byte of a bit string represents the number of bits in
the last byte that are to be ignored but,
// currently, bit strings wanting a non-zero amount of bits trimmed
are not supported
if (($val = Strings::shift($str)) != "\0") {
throw new \UnexpectedValueException('extractPoint expects
the first byte to be null - not ' . Strings::bin2hex($val));
}
if ($str == "\0") {
return [];
}
$keylen = strlen($str);
$order = $curve->getLengthInBytes();
// point compression is being used
if ($keylen == $order + 1) {
return $curve->derivePoint($str);
}
// point compression is not being used
if ($keylen == 2 * $order + 1) {
preg_match("#(.)(.{{$order}})(.{{$order}})#s", $str,
$matches);
list(, $w, $x, $y) = $matches;
if ($w != "\4") {
throw new \UnexpectedValueException('The first byte of
an uncompressed point should be 04 - not ' . Strings::bin2hex($val));
}
$point = [
$curve->convertInteger(new BigInteger($x, 256)),
$curve->convertInteger(new BigInteger($y, 256))
];
if (!$curve->verifyPoint($point)) {
throw new \RuntimeException('Unable to verify that
point exists on curve');
}
return $point;
}
throw new \UnexpectedValueException('The string representation
of the points is not of an appropriate length');
}
/**
* Encode Parameters
*
* @todo Maybe at some point this could be moved to __toString() for
each of the curves?
* @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
* @param bool $returnArray optional
* @param array $options optional
* @return string|false
*/
private static function encodeParameters(BaseCurve $curve, $returnArray
= false, array $options = [])
{
$useNamedCurves = isset($options['namedCurve']) ?
$options['namedCurve'] : self::$useNamedCurves;
$reflect = new \ReflectionClass($curve);
$name = $reflect->getShortName();
if ($useNamedCurves) {
if (isset(self::$curveOIDs[$name])) {
if ($reflect->isFinal()) {
$reflect = $reflect->getParentClass();
$name = $reflect->getShortName();
}
return $returnArray ?
['namedCurve' => $name] :
ASN1::encodeDER(['namedCurve' => $name],
Maps\ECParameters::MAP);
}
foreach (new \DirectoryIterator(__DIR__ .
'/../../Curves/') as $file) {
if ($file->getExtension() != 'php') {
continue;
}
$testName = $file->getBasename('.php');
$class = 'phpseclib3\Crypt\EC\Curves\\' .
$testName;
$reflect = new \ReflectionClass($class);
if ($reflect->isFinal()) {
continue;
}
$candidate = new $class();
switch ($name) {
case 'Prime':
if (!$candidate instanceof PrimeCurve) {
break;
}
if
(!$candidate->getModulo()->equals($curve->getModulo())) {
break;
}
if ($candidate->getA()->toBytes() !=
$curve->getA()->toBytes()) {
break;
}
if ($candidate->getB()->toBytes() !=
$curve->getB()->toBytes()) {
break;
}
list($candidateX, $candidateY) =
$candidate->getBasePoint();
list($curveX, $curveY) = $curve->getBasePoint();
if ($candidateX->toBytes() !=
$curveX->toBytes()) {
break;
}
if ($candidateY->toBytes() !=
$curveY->toBytes()) {
break;
}
return $returnArray ?
['namedCurve' => $testName] :
ASN1::encodeDER(['namedCurve' =>
$testName], Maps\ECParameters::MAP);
case 'Binary':
if (!$candidate instanceof BinaryCurve) {
break;
}
if ($candidate->getModulo() !=
$curve->getModulo()) {
break;
}
if ($candidate->getA()->toBytes() !=
$curve->getA()->toBytes()) {
break;
}
if ($candidate->getB()->toBytes() !=
$curve->getB()->toBytes()) {
break;
}
list($candidateX, $candidateY) =
$candidate->getBasePoint();
list($curveX, $curveY) = $curve->getBasePoint();
if ($candidateX->toBytes() !=
$curveX->toBytes()) {
break;
}
if ($candidateY->toBytes() !=
$curveY->toBytes()) {
break;
}
return $returnArray ?
['namedCurve' => $testName] :
ASN1::encodeDER(['namedCurve' =>
$testName], Maps\ECParameters::MAP);
}
}
}
$order = $curve->getOrder();
// we could try to calculate the order thusly:
// https://crypto.stackexchange.com/a/27914/4520
//
https://en.wikipedia.org/wiki/Schoof%E2%80%93Elkies%E2%80%93Atkin_algorithm
if (!$order) {
throw new \RuntimeException('Specified Curves need the
order to be specified');
}
$point = $curve->getBasePoint();
$x = $point[0]->toBytes();
$y = $point[1]->toBytes();
if ($curve instanceof PrimeCurve) {
/*
* valid versions are:
*
* ecdpVer1:
* - neither the curve or the base point are generated
verifiably randomly.
* ecdpVer2:
* - curve and base point are generated verifiably at random
and curve.seed is present
* ecdpVer3:
* - base point is generated verifiably at random but curve
is not. curve.seed is present
*/
// other (optional) parameters can be calculated using the
methods discused at
// https://crypto.stackexchange.com/q/28947/4520
$data = [
'version' => 'ecdpVer1',
'fieldID' => [
'fieldType' => 'prime-field',
'parameters' => $curve->getModulo()
],
'curve' => [
'a' => $curve->getA()->toBytes(),
'b' => $curve->getB()->toBytes()
],
'base' => "\4" . $x . $y,
'order' => $order
];
return $returnArray ?
['specifiedCurve' => $data] :
ASN1::encodeDER(['specifiedCurve' => $data],
Maps\ECParameters::MAP);
}
if ($curve instanceof BinaryCurve) {
$modulo = $curve->getModulo();
$basis = count($modulo);
$m = array_shift($modulo);
array_pop($modulo); // the last parameter should always be 0
//rsort($modulo);
switch ($basis) {
case 3:
$basis = 'tpBasis';
$modulo = new BigInteger($modulo[0]);
break;
case 5:
$basis = 'ppBasis';
// these should be in strictly ascending order (hence
the commented out rsort above)
$modulo = [
'k1' => new BigInteger($modulo[2]),
'k2' => new BigInteger($modulo[1]),
'k3' => new BigInteger($modulo[0])
];
$modulo = ASN1::encodeDER($modulo,
Maps\Pentanomial::MAP);
$modulo = new ASN1\Element($modulo);
}
$params = ASN1::encodeDER([
'm' => new BigInteger($m),
'basis' => $basis,
'parameters' => $modulo
], Maps\Characteristic_two::MAP);
$params = new ASN1\Element($params);
$a = ltrim($curve->getA()->toBytes(), "\0");
if (!strlen($a)) {
$a = "\0";
}
$b = ltrim($curve->getB()->toBytes(), "\0");
if (!strlen($b)) {
$b = "\0";
}
$data = [
'version' => 'ecdpVer1',
'fieldID' => [
'fieldType' =>
'characteristic-two-field',
'parameters' => $params
],
'curve' => [
'a' => $a,
'b' => $b
],
'base' => "\4" . $x . $y,
'order' => $order
];
return $returnArray ?
['specifiedCurve' => $data] :
ASN1::encodeDER(['specifiedCurve' => $data],
Maps\ECParameters::MAP);
}
throw new UnsupportedCurveException('Curve cannot be
serialized');
}
/**
* Use Specified Curve
*
* A specified curve has all the coefficients, the base points, etc,
explicitely included.
* A specified curve is a more verbose way of representing a curve
*/
public static function useSpecifiedCurve()
{
self::$useNamedCurves = false;
}
/**
* Use Named Curve
*
* A named curve does not include any parameters. It is up to the EC
parameters to
* know what the coefficients, the base points, etc, are from the name
of the curve.
* A named curve is a more concise way of representing a curve
*/
public static function useNamedCurve()
{
self::$useNamedCurves = true;
}
}
phpseclib/phpseclib/Crypt/EC/Formats/Keys/JWK.php000064400000014263151161424250015557
0ustar00<?php
/**
* JSON Web Key (RFC7517 / RFC8037) Formatted EC Handler
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\EC\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\JWK as Progenitor;
use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
use phpseclib3\Crypt\EC\Curves\Ed25519;
use phpseclib3\Crypt\EC\Curves\secp256k1;
use phpseclib3\Crypt\EC\Curves\secp256r1;
use phpseclib3\Crypt\EC\Curves\secp384r1;
use phpseclib3\Crypt\EC\Curves\secp521r1;
use phpseclib3\Exception\UnsupportedCurveException;
use phpseclib3\Math\BigInteger;
/**
* JWK Formatted EC Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class JWK extends Progenitor
{
use Common;
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
$key = parent::load($key, $password);
switch ($key->kty) {
case 'EC':
switch ($key->crv) {
case 'P-256':
case 'P-384':
case 'P-521':
case 'secp256k1':
break;
default:
throw new UnsupportedCurveException('Only
P-256, P-384, P-521 and secp256k1 curves are accepted (' .
$key->crv . ' provided)');
}
break;
case 'OKP':
switch ($key->crv) {
case 'Ed25519':
case 'Ed448':
break;
default:
throw new UnsupportedCurveException('Only
Ed25519 and Ed448 curves are accepted (' . $key->crv . '
provided)');
}
break;
default:
throw new \Exception('Only EC and OKP JWK keys are
supported');
}
$curve = '\phpseclib3\Crypt\EC\Curves\\' .
str_replace('P-', 'nistp', $key->crv);
$curve = new $curve();
if ($curve instanceof TwistedEdwardsCurve) {
$QA = self::extractPoint(Strings::base64url_decode($key->x),
$curve);
if (!isset($key->d)) {
return compact('curve', 'QA');
}
$arr =
$curve->extractSecret(Strings::base64url_decode($key->d));
return compact('curve', 'QA') + $arr;
}
$QA = [
$curve->convertInteger(new
BigInteger(Strings::base64url_decode($key->x), 256)),
$curve->convertInteger(new
BigInteger(Strings::base64url_decode($key->y), 256))
];
if (!$curve->verifyPoint($QA)) {
throw new \RuntimeException('Unable to verify that point
exists on curve');
}
if (!isset($key->d)) {
return compact('curve', 'QA');
}
$dA = new BigInteger(Strings::base64url_decode($key->d), 256);
$curve->rangeCheck($dA);
return compact('curve', 'dA', 'QA');
}
/**
* Returns the alias that corresponds to a curve
*
* @return string
*/
private static function getAlias(BaseCurve $curve)
{
switch (true) {
case $curve instanceof secp256r1:
return 'P-256';
case $curve instanceof secp384r1:
return 'P-384';
case $curve instanceof secp521r1:
return 'P-521';
case $curve instanceof secp256k1:
return 'secp256k1';
}
$reflect = new \ReflectionClass($curve);
$curveName = $reflect->isFinal() ?
$reflect->getParentClass()->getShortName() :
$reflect->getShortName();
throw new UnsupportedCurveException("$curveName is not a
supported curve");
}
/**
* Return the array superstructure for an EC public key
*
* @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
* @return array
*/
private static function savePublicKeyHelper(BaseCurve $curve, array
$publicKey)
{
if ($curve instanceof TwistedEdwardsCurve) {
return [
'kty' => 'OKP',
'crv' => $curve instanceof Ed25519 ?
'Ed25519' : 'Ed448',
'x' =>
Strings::base64url_encode($curve->encodePoint($publicKey))
];
}
return [
'kty' => 'EC',
'crv' => self::getAlias($curve),
'x' =>
Strings::base64url_encode($publicKey[0]->toBytes()),
'y' =>
Strings::base64url_encode($publicKey[1]->toBytes())
];
}
/**
* Convert an EC public key to the appropriate format
*
* @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
* @param array $options optional
* @return string
*/
public static function savePublicKey(BaseCurve $curve, array
$publicKey, array $options = [])
{
$key = self::savePublicKeyHelper($curve, $publicKey);
return self::wrapKey($key, $options);
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $privateKey
* @param \phpseclib3\Crypt\EC\Curves\Ed25519 $curve
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
* @param string $secret optional
* @param string $password optional
* @param array $options optional
* @return string
*/
public static function savePrivateKey(BigInteger $privateKey, BaseCurve
$curve, array $publicKey, $secret = null, $password = '', array
$options = [])
{
$key = self::savePublicKeyHelper($curve, $publicKey);
$key['d'] = $curve instanceof TwistedEdwardsCurve ?
$secret : $privateKey->toBytes();
$key['d'] =
Strings::base64url_encode($key['d']);
return self::wrapKey($key, $options);
}
}
phpseclib/phpseclib/Crypt/EC/Formats/Keys/libsodium.php000064400000007113151161424250017107
0ustar00<?php
/**
* libsodium Key Handler
*
* Different NaCl implementations store the key differently.
* https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/ elaborates.
* libsodium appears to use the same format as SUPERCOP.
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\EC\Formats\Keys;
use phpseclib3\Crypt\EC\Curves\Ed25519;
use phpseclib3\Exception\UnsupportedFormatException;
use phpseclib3\Math\BigInteger;
/**
* libsodium Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class libsodium
{
use Common;
/**
* Is invisible flag
*
*/
const IS_INVISIBLE = true;
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
switch (strlen($key)) {
case 32:
$public = $key;
break;
case 64:
$private = substr($key, 0, 32);
$public = substr($key, -32);
break;
case 96:
$public = substr($key, -32);
if (substr($key, 32, 32) != $public) {
throw new \RuntimeException('Keys with 96 bytes
should have the 2nd and 3rd set of 32 bytes match');
}
$private = substr($key, 0, 32);
break;
default:
throw new \RuntimeException('libsodium keys need to
either be 32 bytes long, 64 bytes long or 96 bytes long');
}
$curve = new Ed25519();
$components = ['curve' => $curve];
if (isset($private)) {
$arr = $curve->extractSecret($private);
$components['dA'] = $arr['dA'];
$components['secret'] = $arr['secret'];
}
$components['QA'] = isset($public) ?
self::extractPoint($public, $curve) :
$curve->multiplyPoint($curve->getBasePoint(),
$components['dA']);
return $components;
}
/**
* Convert an EC public key to the appropriate format
*
* @param \phpseclib3\Crypt\EC\Curves\Ed25519 $curve
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
* @return string
*/
public static function savePublicKey(Ed25519 $curve, array $publicKey)
{
return $curve->encodePoint($publicKey);
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $privateKey
* @param \phpseclib3\Crypt\EC\Curves\Ed25519 $curve
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
* @param string $secret optional
* @param string $password optional
* @return string
*/
public static function savePrivateKey(BigInteger $privateKey, Ed25519
$curve, array $publicKey, $secret = null, $password = '')
{
if (!isset($secret)) {
throw new \RuntimeException('Private Key does not have a
secret set');
}
if (strlen($secret) != 32) {
throw new \RuntimeException('Private Key secret is not of
the correct length');
}
if (!empty($password) && is_string($password)) {
throw new UnsupportedFormatException('libsodium private
keys do not support encryption');
}
return $secret . $curve->encodePoint($publicKey);
}
}
phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php000064400000006404151161424250020615
0ustar00<?php
/**
* Montgomery Private Key Handler
*
* "Naked" Curve25519 private keys can pretty much be any
sequence of random 32x bytes so unless
* we have a "hidden" key handler pretty much every 32 byte
string will be loaded as a curve25519
* private key even if it probably isn't one by PublicKeyLoader.
*
* "Naked" Curve25519 public keys also a string of 32 bytes so
distinguishing between a "naked"
* curve25519 private key and a public key is nigh impossible, hence
separate plugins for each
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\EC\Formats\Keys;
use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
use phpseclib3\Crypt\EC\Curves\Curve25519;
use phpseclib3\Crypt\EC\Curves\Curve448;
use phpseclib3\Exception\UnsupportedFormatException;
use phpseclib3\Math\BigInteger;
/**
* Montgomery Curve Private Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class MontgomeryPrivate
{
/**
* Is invisible flag
*
*/
const IS_INVISIBLE = true;
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
switch (strlen($key)) {
case 32:
$curve = new Curve25519();
break;
case 56:
$curve = new Curve448();
break;
default:
throw new \LengthException('The only supported lengths
are 32 and 56');
}
$components = ['curve' => $curve];
$components['dA'] = new BigInteger($key, 256);
$curve->rangeCheck($components['dA']);
// note that EC::getEncodedCoordinates does some additional
"magic" (it does strrev on the result)
$components['QA'] =
$components['curve']->multiplyPoint($components['curve']->getBasePoint(),
$components['dA']);
return $components;
}
/**
* Convert an EC public key to the appropriate format
*
* @param \phpseclib3\Crypt\EC\BaseCurves\Montgomery $curve
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
* @return string
*/
public static function savePublicKey(MontgomeryCurve $curve, array
$publicKey)
{
return strrev($publicKey[0]->toBytes());
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $privateKey
* @param \phpseclib3\Crypt\EC\BaseCurves\Montgomery $curve
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
* @param string $secret optional
* @param string $password optional
* @return string
*/
public static function savePrivateKey(BigInteger $privateKey,
MontgomeryCurve $curve, array $publicKey, $secret = null, $password =
'')
{
if (!empty($password) && is_string($password)) {
throw new UnsupportedFormatException('MontgomeryPrivate
private keys do not support encryption');
}
return $privateKey->toBytes();
}
}
phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPublic.php000064400000003467151161424250020427
0ustar00<?php
/**
* Montgomery Public Key Handler
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\EC\Formats\Keys;
use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
use phpseclib3\Crypt\EC\Curves\Curve25519;
use phpseclib3\Crypt\EC\Curves\Curve448;
use phpseclib3\Math\BigInteger;
/**
* Montgomery Public Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class MontgomeryPublic
{
/**
* Is invisible flag
*
*/
const IS_INVISIBLE = true;
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
switch (strlen($key)) {
case 32:
$curve = new Curve25519();
break;
case 56:
$curve = new Curve448();
break;
default:
throw new \LengthException('The only supported lengths
are 32 and 56');
}
$components = ['curve' => $curve];
$components['QA'] =
[$components['curve']->convertInteger(new
BigInteger(strrev($key), 256))];
return $components;
}
/**
* Convert an EC public key to the appropriate format
*
* @param \phpseclib3\Crypt\EC\BaseCurves\Montgomery $curve
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
* @return string
*/
public static function savePublicKey(MontgomeryCurve $curve, array
$publicKey)
{
return strrev($publicKey[0]->toBytes());
}
}
phpseclib/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php000064400000015355151161424250016406
0ustar00<?php
/**
* OpenSSH Formatted EC Key Handler
*
* PHP version 5
*
* Place in $HOME/.ssh/authorized_keys
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\EC\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\OpenSSH as Progenitor;
use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
use phpseclib3\Crypt\EC\Curves\Ed25519;
use phpseclib3\Exception\UnsupportedCurveException;
use phpseclib3\Math\BigInteger;
/**
* OpenSSH Formatted EC Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class OpenSSH extends Progenitor
{
use Common;
/**
* Supported Key Types
*
* @var array
*/
protected static $types = [
'ecdsa-sha2-nistp256',
'ecdsa-sha2-nistp384',
'ecdsa-sha2-nistp521',
'ssh-ed25519'
];
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
$parsed = parent::load($key, $password);
if (isset($parsed['paddedKey'])) {
$paddedKey = $parsed['paddedKey'];
list($type) = Strings::unpackSSH2('s', $paddedKey);
if ($type != $parsed['type']) {
throw new \RuntimeException("The public and private
keys are not of the same type ($type vs $parsed[type])");
}
if ($type == 'ssh-ed25519') {
list(, $key, $comment) =
Strings::unpackSSH2('sss', $paddedKey);
$key = libsodium::load($key);
$key['comment'] = $comment;
return $key;
}
list($curveName, $publicKey, $privateKey, $comment) =
Strings::unpackSSH2('ssis', $paddedKey);
$curve = self::loadCurveByParam(['namedCurve' =>
$curveName]);
$curve->rangeCheck($privateKey);
return [
'curve' => $curve,
'dA' => $privateKey,
'QA' =>
self::extractPoint("\0$publicKey", $curve),
'comment' => $comment
];
}
if ($parsed['type'] == 'ssh-ed25519') {
if (Strings::shift($parsed['publicKey'], 4) !=
"\0\0\0\x20") {
throw new \RuntimeException('Length of ssh-ed25519 key
should be 32');
}
$curve = new Ed25519();
$qa = self::extractPoint($parsed['publicKey'],
$curve);
} else {
list($curveName, $publicKey) =
Strings::unpackSSH2('ss', $parsed['publicKey']);
$curveName = '\phpseclib3\Crypt\EC\Curves\\' .
$curveName;
$curve = new $curveName();
$qa = self::extractPoint("\0" . $publicKey, $curve);
}
return [
'curve' => $curve,
'QA' => $qa,
'comment' => $parsed['comment']
];
}
/**
* Returns the alias that corresponds to a curve
*
* @return string
*/
private static function getAlias(BaseCurve $curve)
{
self::initialize_static_variables();
$reflect = new \ReflectionClass($curve);
$name = $reflect->getShortName();
$oid = self::$curveOIDs[$name];
$aliases = array_filter(self::$curveOIDs, function ($v) use ($oid)
{
return $v == $oid;
});
$aliases = array_keys($aliases);
for ($i = 0; $i < count($aliases); $i++) {
if (in_array('ecdsa-sha2-' . $aliases[$i],
self::$types)) {
$alias = $aliases[$i];
break;
}
}
if (!isset($alias)) {
throw new UnsupportedCurveException($name . ' is not a
curve that the OpenSSH plugin supports');
}
return $alias;
}
/**
* Convert an EC public key to the appropriate format
*
* @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
* @param array $options optional
* @return string
*/
public static function savePublicKey(BaseCurve $curve, array
$publicKey, array $options = [])
{
$comment = isset($options['comment']) ?
$options['comment'] : self::$comment;
if ($curve instanceof Ed25519) {
$key = Strings::packSSH2('ss',
'ssh-ed25519', $curve->encodePoint($publicKey));
if (isset($options['binary']) ?
$options['binary'] : self::$binary) {
return $key;
}
$key = 'ssh-ed25519 ' . base64_encode($key) . '
' . $comment;
return $key;
}
$alias = self::getAlias($curve);
$points = "\4" . $publicKey[0]->toBytes() .
$publicKey[1]->toBytes();
$key = Strings::packSSH2('sss', 'ecdsa-sha2-' .
$alias, $alias, $points);
if (isset($options['binary']) ?
$options['binary'] : self::$binary) {
return $key;
}
$key = 'ecdsa-sha2-' . $alias . ' ' .
base64_encode($key) . ' ' . $comment;
return $key;
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $privateKey
* @param \phpseclib3\Crypt\EC\Curves\Ed25519 $curve
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
* @param string $secret optional
* @param string $password optional
* @param array $options optional
* @return string
*/
public static function savePrivateKey(BigInteger $privateKey, BaseCurve
$curve, array $publicKey, $secret = null, $password = '', array
$options = [])
{
if ($curve instanceof Ed25519) {
if (!isset($secret)) {
throw new \RuntimeException('Private Key does not have
a secret set');
}
if (strlen($secret) != 32) {
throw new \RuntimeException('Private Key secret is not
of the correct length');
}
$pubKey = $curve->encodePoint($publicKey);
$publicKey = Strings::packSSH2('ss',
'ssh-ed25519', $pubKey);
$privateKey = Strings::packSSH2('sss',
'ssh-ed25519', $pubKey, $secret . $pubKey);
return self::wrapPrivateKey($publicKey, $privateKey, $password,
$options);
}
$alias = self::getAlias($curve);
$points = "\4" . $publicKey[0]->toBytes() .
$publicKey[1]->toBytes();
$publicKey = self::savePublicKey($curve, $publicKey,
['binary' => true]);
$privateKey = Strings::packSSH2('sssi',
'ecdsa-sha2-' . $alias, $alias, $points, $privateKey);
return self::wrapPrivateKey($publicKey, $privateKey, $password,
$options);
}
}
phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php000064400000016234151161424250015745
0ustar00<?php
/**
* "PKCS1" (RFC5915) Formatted EC Key Handler
*
* PHP version 5
*
* Used by File/X509.php
*
* Processes keys with the following headers:
*
* -----BEGIN EC PRIVATE KEY-----
* -----BEGIN EC PARAMETERS-----
*
* Technically, PKCS1 is for RSA keys, only, but we're using PKCS1 to
describe
* DSA, whose format isn't really formally described anywhere, so
might as well
* use it to describe this, too. PKCS1 is easier to remember than RFC5915,
after
* all. I suppose this could also be named IETF but idk
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\EC\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
use phpseclib3\Exception\UnsupportedCurveException;
use phpseclib3\File\ASN1;
use phpseclib3\File\ASN1\Maps;
use phpseclib3\Math\BigInteger;
/**
* "PKCS1" (RFC5915) Formatted EC Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PKCS1 extends Progenitor
{
use Common;
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
self::initialize_static_variables();
if (!Strings::is_stringable($key)) {
throw new \UnexpectedValueException('Key should be a
string - not a ' . gettype($key));
}
if (strpos($key, 'BEGIN EC PARAMETERS') &&
strpos($key, 'BEGIN EC PRIVATE KEY')) {
$components = [];
preg_match('#-*BEGIN EC PRIVATE KEY-*[^-]*-*END EC PRIVATE
KEY-*#s', $key, $matches);
$decoded = parent::load($matches[0], $password);
$decoded = ASN1::decodeBER($decoded);
if (!$decoded) {
throw new \RuntimeException('Unable to decode
BER');
}
$ecPrivate = ASN1::asn1map($decoded[0],
Maps\ECPrivateKey::MAP);
if (!is_array($ecPrivate)) {
throw new \RuntimeException('Unable to perform ASN1
mapping');
}
if (isset($ecPrivate['parameters'])) {
$components['curve'] =
self::loadCurveByParam($ecPrivate['parameters']);
}
preg_match('#-*BEGIN EC PARAMETERS-*[^-]*-*END EC
PARAMETERS-*#s', $key, $matches);
$decoded = parent::load($matches[0], '');
$decoded = ASN1::decodeBER($decoded);
if (!$decoded) {
throw new \RuntimeException('Unable to decode
BER');
}
$ecParams = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP);
if (!is_array($ecParams)) {
throw new \RuntimeException('Unable to perform ASN1
mapping');
}
$ecParams = self::loadCurveByParam($ecParams);
// comparing $ecParams and $components['curve']
directly won't work because they'll have different
Math\Common\FiniteField classes
// even if the modulo is the same
if (isset($components['curve']) &&
self::encodeParameters($ecParams, false, []) !=
self::encodeParameters($components['curve'], false, [])) {
throw new \RuntimeException('EC PARAMETERS does not
correspond to EC PRIVATE KEY');
}
if (!isset($components['curve'])) {
$components['curve'] = $ecParams;
}
$components['dA'] = new
BigInteger($ecPrivate['privateKey'], 256);
$components['curve']->rangeCheck($components['dA']);
$components['QA'] =
isset($ecPrivate['publicKey']) ?
self::extractPoint($ecPrivate['publicKey'],
$components['curve']) :
$components['curve']->multiplyPoint($components['curve']->getBasePoint(),
$components['dA']);
return $components;
}
$key = parent::load($key, $password);
$decoded = ASN1::decodeBER($key);
if (!$decoded) {
throw new \RuntimeException('Unable to decode BER');
}
$key = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP);
if (is_array($key)) {
return ['curve' => self::loadCurveByParam($key)];
}
$key = ASN1::asn1map($decoded[0], Maps\ECPrivateKey::MAP);
if (!is_array($key)) {
throw new \RuntimeException('Unable to perform ASN1
mapping');
}
if (!isset($key['parameters'])) {
throw new \RuntimeException('Key cannot be loaded without
parameters');
}
$components = [];
$components['curve'] =
self::loadCurveByParam($key['parameters']);
$components['dA'] = new
BigInteger($key['privateKey'], 256);
$components['QA'] =
isset($ecPrivate['publicKey']) ?
self::extractPoint($ecPrivate['publicKey'],
$components['curve']) :
$components['curve']->multiplyPoint($components['curve']->getBasePoint(),
$components['dA']);
return $components;
}
/**
* Convert EC parameters to the appropriate format
*
* @return string
*/
public static function saveParameters(BaseCurve $curve, array $options
= [])
{
self::initialize_static_variables();
if ($curve instanceof TwistedEdwardsCurve || $curve instanceof
MontgomeryCurve) {
throw new UnsupportedCurveException('TwistedEdwards and
Montgomery Curves are not supported');
}
$key = self::encodeParameters($curve, false, $options);
return "-----BEGIN EC PARAMETERS-----\r\n" .
chunk_split(Strings::base64_encode($key), 64) .
"-----END EC PARAMETERS-----\r\n";
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $privateKey
* @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
* @param string $secret optional
* @param string $password optional
* @param array $options optional
* @return string
*/
public static function savePrivateKey(BigInteger $privateKey, BaseCurve
$curve, array $publicKey, $secret = null, $password = '', array
$options = [])
{
self::initialize_static_variables();
if ($curve instanceof TwistedEdwardsCurve || $curve instanceof
MontgomeryCurve) {
throw new UnsupportedCurveException('TwistedEdwards Curves
are not supported');
}
$publicKey = "\4" . $publicKey[0]->toBytes() .
$publicKey[1]->toBytes();
$key = [
'version' => 'ecPrivkeyVer1',
'privateKey' => $privateKey->toBytes(),
'parameters' => new
ASN1\Element(self::encodeParameters($curve)),
'publicKey' => "\0" . $publicKey
];
$key = ASN1::encodeDER($key, Maps\ECPrivateKey::MAP);
return self::wrapPrivateKey($key, 'EC', $password,
$options);
}
}
phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php000064400000017712151161424250015756
0ustar00<?php
/**
* PKCS#8 Formatted EC Key Handler
*
* PHP version 5
*
* Processes keys with the following headers:
*
* -----BEGIN ENCRYPTED PRIVATE KEY-----
* -----BEGIN PRIVATE KEY-----
* -----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)
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\EC\Formats\Keys;
use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
use phpseclib3\Crypt\EC\Curves\Ed25519;
use phpseclib3\Crypt\EC\Curves\Ed448;
use phpseclib3\Exception\UnsupportedCurveException;
use phpseclib3\File\ASN1;
use phpseclib3\File\ASN1\Maps;
use phpseclib3\Math\BigInteger;
/**
* PKCS#8 Formatted EC Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PKCS8 extends Progenitor
{
use Common;
/**
* OID Name
*
* @var array
*/
const OID_NAME = ['id-ecPublicKey', 'id-Ed25519',
'id-Ed448'];
/**
* OID Value
*
* @var string
*/
const OID_VALUE = ['1.2.840.10045.2.1',
'1.3.101.112', '1.3.101.113'];
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
// initialize_static_variables() is defined in both the trait and
the parent class
// when it's defined in two places it's the traits one
that's called
// the parent one is needed, as well, but the parent one is called
by other methods
// in the parent class as needed and in the context of the parent
it's the parent
// one that's called
self::initialize_static_variables();
$key = parent::load($key, $password);
$type = isset($key['privateKey']) ?
'privateKey' : 'publicKey';
switch ($key[$type . 'Algorithm']['algorithm'])
{
case 'id-Ed25519':
case 'id-Ed448':
return self::loadEdDSA($key);
}
$decoded = ASN1::decodeBER($key[$type .
'Algorithm']['parameters']->element);
if (!$decoded) {
throw new \RuntimeException('Unable to decode BER');
}
$params = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP);
if (!$params) {
throw new \RuntimeException('Unable to decode the
parameters using Maps\ECParameters');
}
$components = [];
$components['curve'] = self::loadCurveByParam($params);
if ($type == 'publicKey') {
$components['QA'] = self::extractPoint("\0"
. $key['publicKey'], $components['curve']);
return $components;
}
$decoded = ASN1::decodeBER($key['privateKey']);
if (!$decoded) {
throw new \RuntimeException('Unable to decode BER');
}
$key = ASN1::asn1map($decoded[0], Maps\ECPrivateKey::MAP);
if (isset($key['parameters']) && $params !=
$key['parameters']) {
throw new \RuntimeException('The PKCS8 parameter field
does not match the private key parameter field');
}
$components['dA'] = new
BigInteger($key['privateKey'], 256);
$components['curve']->rangeCheck($components['dA']);
$components['QA'] = isset($key['publicKey']) ?
self::extractPoint($key['publicKey'],
$components['curve']) :
$components['curve']->multiplyPoint($components['curve']->getBasePoint(),
$components['dA']);
return $components;
}
/**
* Break a public or private EdDSA key down into its constituent
components
*
* @return array
*/
private static function loadEdDSA(array $key)
{
$components = [];
if (isset($key['privateKey'])) {
$components['curve'] =
$key['privateKeyAlgorithm']['algorithm'] ==
'id-Ed25519' ? new Ed25519() : new Ed448();
// 0x04 == octet string
// 0x20 == length (32 bytes)
if (substr($key['privateKey'], 0, 2) !=
"\x04\x20") {
throw new \RuntimeException('The first two bytes of
the private key field should be 0x0420');
}
$arr =
$components['curve']->extractSecret(substr($key['privateKey'],
2));
$components['dA'] = $arr['dA'];
$components['secret'] = $arr['secret'];
}
if (isset($key['publicKey'])) {
if (!isset($components['curve'])) {
$components['curve'] =
$key['publicKeyAlgorithm']['algorithm'] ==
'id-Ed25519' ? new Ed25519() : new Ed448();
}
$components['QA'] =
self::extractPoint($key['publicKey'],
$components['curve']);
}
if (isset($key['privateKey']) &&
!isset($components['QA'])) {
$components['QA'] =
$components['curve']->multiplyPoint($components['curve']->getBasePoint(),
$components['dA']);
}
return $components;
}
/**
* Convert an EC public key to the appropriate format
*
* @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
* @param array $options optional
* @return string
*/
public static function savePublicKey(BaseCurve $curve, array
$publicKey, array $options = [])
{
self::initialize_static_variables();
if ($curve instanceof MontgomeryCurve) {
throw new UnsupportedCurveException('Montgomery Curves are
not supported');
}
if ($curve instanceof TwistedEdwardsCurve) {
return self::wrapPublicKey(
$curve->encodePoint($publicKey),
null,
$curve instanceof Ed25519 ? 'id-Ed25519' :
'id-Ed448'
);
}
$params = new ASN1\Element(self::encodeParameters($curve, false,
$options));
$key = "\4" . $publicKey[0]->toBytes() .
$publicKey[1]->toBytes();
return self::wrapPublicKey($key, $params,
'id-ecPublicKey');
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $privateKey
* @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
* @param string $secret optional
* @param string $password optional
* @param array $options optional
* @return string
*/
public static function savePrivateKey(BigInteger $privateKey, BaseCurve
$curve, array $publicKey, $secret = null, $password = '', array
$options = [])
{
self::initialize_static_variables();
if ($curve instanceof MontgomeryCurve) {
throw new UnsupportedCurveException('Montgomery Curves are
not supported');
}
if ($curve instanceof TwistedEdwardsCurve) {
return self::wrapPrivateKey(
"\x04\x20" . $secret,
[],
null,
$password,
$curve instanceof Ed25519 ? 'id-Ed25519' :
'id-Ed448'
);
}
$publicKey = "\4" . $publicKey[0]->toBytes() .
$publicKey[1]->toBytes();
$params = new ASN1\Element(self::encodeParameters($curve, false,
$options));
$key = [
'version' => 'ecPrivkeyVer1',
'privateKey' => $privateKey->toBytes(),
//'parameters' => $params,
'publicKey' => "\0" . $publicKey
];
$key = ASN1::encodeDER($key, Maps\ECPrivateKey::MAP);
return self::wrapPrivateKey($key, [], $params, $password,
'id-ecPublicKey', '', $options);
}
}
phpseclib/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php000064400000010614151161424250016105
0ustar00<?php
/**
* PuTTY Formatted EC Key Handler
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\EC\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\PuTTY as Progenitor;
use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
use phpseclib3\Math\BigInteger;
/**
* PuTTY Formatted EC Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PuTTY extends Progenitor
{
use Common;
/**
* Public Handler
*
* @var string
*/
const PUBLIC_HANDLER =
'phpseclib3\Crypt\EC\Formats\Keys\OpenSSH';
/**
* Supported Key Types
*
* @var array
*/
protected static $types = [
'ecdsa-sha2-nistp256',
'ecdsa-sha2-nistp384',
'ecdsa-sha2-nistp521',
'ssh-ed25519'
];
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
$components = parent::load($key, $password);
if (!isset($components['private'])) {
return $components;
}
$private = $components['private'];
$temp = Strings::base64_encode(Strings::packSSH2('s',
$components['type']) . $components['public']);
$components = OpenSSH::load($components['type'] . '
' . $temp . ' ' . $components['comment']);
if ($components['curve'] instanceof TwistedEdwardsCurve)
{
if (Strings::shift($private, 4) != "\0\0\0\x20") {
throw new \RuntimeException('Length of ssh-ed25519 key
should be 32');
}
$arr =
$components['curve']->extractSecret($private);
$components['dA'] = $arr['dA'];
$components['secret'] = $arr['secret'];
} else {
list($components['dA']) =
Strings::unpackSSH2('i', $private);
$components['curve']->rangeCheck($components['dA']);
}
return $components;
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $privateKey
* @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
* @param string $secret optional
* @param string $password optional
* @param array $options optional
* @return string
*/
public static function savePrivateKey(BigInteger $privateKey, BaseCurve
$curve, array $publicKey, $secret = null, $password = false, array $options
= [])
{
self::initialize_static_variables();
$public = explode(' ', OpenSSH::savePublicKey($curve,
$publicKey));
$name = $public[0];
$public = Strings::base64_decode($public[1]);
list(, $length) = unpack('N', Strings::shift($public,
4));
Strings::shift($public, $length);
// PuTTY pads private keys with a null byte per the following:
//
https://github.com/github/putty/blob/a3d14d77f566a41fc61dfdc5c2e0e384c9e6ae8b/sshecc.c#L1926
if (!$curve instanceof TwistedEdwardsCurve) {
$private = $privateKey->toBytes();
if (!(strlen($privateKey->toBits()) & 7)) {
$private = "\0$private";
}
}
$private = $curve instanceof TwistedEdwardsCurve ?
Strings::packSSH2('s', $secret) :
Strings::packSSH2('s', $private);
return self::wrapPrivateKey($public, $private, $name, $password,
$options);
}
/**
* Convert an EC public key to the appropriate format
*
* @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
* @param \phpseclib3\Math\Common\FiniteField[] $publicKey
* @return string
*/
public static function savePublicKey(BaseCurve $curve, array
$publicKey)
{
$public = explode(' ', OpenSSH::savePublicKey($curve,
$publicKey));
$type = $public[0];
$public = Strings::base64_decode($public[1]);
list(, $length) = unpack('N', Strings::shift($public,
4));
Strings::shift($public, $length);
return self::wrapPublicKey($public, $type);
}
}
phpseclib/phpseclib/Crypt/EC/Formats/Keys/XML.php000064400000042737151161424250015573
0ustar00<?php
/**
* XML Formatted EC Key Handler
*
* More info:
*
* https://www.w3.org/TR/xmldsig-core/#sec-ECKeyValue
* http://en.wikipedia.org/wiki/XML_Signature
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\EC\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
use phpseclib3\Crypt\EC\BaseCurves\Prime as PrimeCurve;
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
use phpseclib3\Exception\BadConfigurationException;
use phpseclib3\Exception\UnsupportedCurveException;
use phpseclib3\Math\BigInteger;
/**
* XML Formatted EC Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class XML
{
use Common;
/**
* Default namespace
*
* @var string
*/
private static $namespace;
/**
* Flag for using RFC4050 syntax
*
* @var bool
*/
private static $rfc4050 = false;
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
self::initialize_static_variables();
if (!Strings::is_stringable($key)) {
throw new \UnexpectedValueException('Key should be a
string - not a ' . gettype($key));
}
if (!class_exists('DOMDocument')) {
throw new BadConfigurationException('The dom extension is
not setup correctly on this system');
}
$use_errors = libxml_use_internal_errors(true);
$temp = self::isolateNamespace($key,
'http://www.w3.org/2009/xmldsig11#');
if ($temp) {
$key = $temp;
}
$temp = self::isolateNamespace($key,
'http://www.w3.org/2001/04/xmldsig-more#');
if ($temp) {
$key = $temp;
}
$dom = new \DOMDocument();
if (substr($key, 0, 5) != '<?xml') {
$key = '<xml>' . $key .
'</xml>';
}
if (!$dom->loadXML($key)) {
libxml_use_internal_errors($use_errors);
throw new \UnexpectedValueException('Key does not appear
to contain XML');
}
$xpath = new \DOMXPath($dom);
libxml_use_internal_errors($use_errors);
$curve = self::loadCurveByParam($xpath);
$pubkey = self::query($xpath, 'publickey', 'Public
Key is not present');
$QA = self::query($xpath, 'ecdsakeyvalue')->length ?
self::extractPointRFC4050($xpath, $curve) :
self::extractPoint("\0" . $pubkey, $curve);
libxml_use_internal_errors($use_errors);
return compact('curve', 'QA');
}
/**
* Case-insensitive xpath query
*
* @param \DOMXPath $xpath
* @param string $name
* @param string $error optional
* @param bool $decode optional
* @return \DOMNodeList
*/
private static function query(\DOMXPath $xpath, $name, $error = null,
$decode = true)
{
$query = '/';
$names = explode('/', $name);
foreach ($names as $name) {
$query .= "/*[translate(local-name(),
'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$name']";
}
$result = $xpath->query($query);
if (!isset($error)) {
return $result;
}
if (!$result->length) {
throw new \RuntimeException($error);
}
return $decode ?
self::decodeValue($result->item(0)->textContent) :
$result->item(0)->textContent;
}
/**
* Finds the first element in the relevant namespace, strips the
namespacing and returns the XML for that element.
*
* @param string $xml
* @param string $ns
*/
private static function isolateNamespace($xml, $ns)
{
$dom = new \DOMDocument();
if (!$dom->loadXML($xml)) {
return false;
}
$xpath = new \DOMXPath($dom);
$nodes = $xpath->query("//*[namespace::*[.='$ns']
and not(../namespace::*[.='$ns'])]");
if (!$nodes->length) {
return false;
}
$node = $nodes->item(0);
$ns_name = $node->lookupPrefix($ns);
if ($ns_name) {
$node->removeAttributeNS($ns, $ns_name);
}
return $dom->saveXML($node);
}
/**
* Decodes the value
*
* @param string $value
*/
private static function decodeValue($value)
{
return Strings::base64_decode(str_replace(["\r",
"\n", ' ', "\t"], '', $value));
}
/**
* Extract points from an XML document
*
* @param \DOMXPath $xpath
* @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
* @return object[]
*/
private static function extractPointRFC4050(\DOMXPath $xpath, BaseCurve
$curve)
{
$x = self::query($xpath, 'publickey/x');
$y = self::query($xpath, 'publickey/y');
if (!$x->length ||
!$x->item(0)->hasAttribute('Value')) {
throw new \RuntimeException('Public Key / X coordinate not
found');
}
if (!$y->length ||
!$y->item(0)->hasAttribute('Value')) {
throw new \RuntimeException('Public Key / Y coordinate not
found');
}
$point = [
$curve->convertInteger(new
BigInteger($x->item(0)->getAttribute('Value'))),
$curve->convertInteger(new
BigInteger($y->item(0)->getAttribute('Value')))
];
if (!$curve->verifyPoint($point)) {
throw new \RuntimeException('Unable to verify that point
exists on curve');
}
return $point;
}
/**
* Returns an instance of \phpseclib3\Crypt\EC\BaseCurves\Base based
* on the curve parameters
*
* @param \DomXPath $xpath
* @return \phpseclib3\Crypt\EC\BaseCurves\Base|false
*/
private static function loadCurveByParam(\DOMXPath $xpath)
{
$namedCurve = self::query($xpath, 'namedcurve');
if ($namedCurve->length == 1) {
$oid =
$namedCurve->item(0)->getAttribute('URN');
$oid = preg_replace('#[^\d.]#', '', $oid);
$name = array_search($oid, self::$curveOIDs);
if ($name === false) {
throw new UnsupportedCurveException('Curve with OID of
' . $oid . ' is not supported');
}
$curve = '\phpseclib3\Crypt\EC\Curves\\' . $name;
if (!class_exists($curve)) {
throw new UnsupportedCurveException('Named Curve of
' . $name . ' is not supported');
}
return new $curve();
}
$params = self::query($xpath, 'explicitparams');
if ($params->length) {
return self::loadCurveByParamRFC4050($xpath);
}
$params = self::query($xpath, 'ecparameters');
if (!$params->length) {
throw new \RuntimeException('No parameters are
present');
}
$fieldTypes = [
'prime-field' => ['fieldid/prime/p'],
'gnb' => ['fieldid/gnb/m'],
'tnb' => ['fieldid/tnb/k'],
'pnb' => ['fieldid/pnb/k1',
'fieldid/pnb/k2', 'fieldid/pnb/k3'],
'unknown' => []
];
foreach ($fieldTypes as $type => $queries) {
foreach ($queries as $query) {
$result = self::query($xpath, $query);
if (!$result->length) {
continue 2;
}
$param = preg_replace('#.*/#', '',
$query);
$$param =
self::decodeValue($result->item(0)->textContent);
}
break;
}
$a = self::query($xpath, 'curve/a', 'A coefficient
is not present');
$b = self::query($xpath, 'curve/b', 'B coefficient
is not present');
$base = self::query($xpath, 'base', 'Base point is
not present');
$order = self::query($xpath, 'order', 'Order is not
present');
switch ($type) {
case 'prime-field':
$curve = new PrimeCurve();
$curve->setModulo(new BigInteger($p, 256));
$curve->setCoefficients(
new BigInteger($a, 256),
new BigInteger($b, 256)
);
$point = self::extractPoint("\0" . $base,
$curve);
$curve->setBasePoint(...$point);
$curve->setOrder(new BigInteger($order, 256));
return $curve;
case 'gnb':
case 'tnb':
case 'pnb':
default:
throw new UnsupportedCurveException('Field Type of
' . $type . ' is not supported');
}
}
/**
* Returns an instance of \phpseclib3\Crypt\EC\BaseCurves\Base based
* on the curve parameters
*
* @param \DomXPath $xpath
* @return \phpseclib3\Crypt\EC\BaseCurves\Base|false
*/
private static function loadCurveByParamRFC4050(\DOMXPath $xpath)
{
$fieldTypes = [
'prime-field' =>
['primefieldparamstype/p'],
'unknown' => []
];
foreach ($fieldTypes as $type => $queries) {
foreach ($queries as $query) {
$result = self::query($xpath, $query);
if (!$result->length) {
continue 2;
}
$param = preg_replace('#.*/#', '',
$query);
$$param = $result->item(0)->textContent;
}
break;
}
$a = self::query($xpath, 'curveparamstype/a', 'A
coefficient is not present', false);
$b = self::query($xpath, 'curveparamstype/b', 'B
coefficient is not present', false);
$x = self::query($xpath,
'basepointparams/basepoint/ecpointtype/x', 'Base Point X is
not present', false);
$y = self::query($xpath,
'basepointparams/basepoint/ecpointtype/y', 'Base Point Y is
not present', false);
$order = self::query($xpath, 'order', 'Order is not
present', false);
switch ($type) {
case 'prime-field':
$curve = new PrimeCurve();
$p = str_replace(["\r", "\n", '
', "\t"], '', $p);
$curve->setModulo(new BigInteger($p));
$a = str_replace(["\r", "\n", '
', "\t"], '', $a);
$b = str_replace(["\r", "\n", '
', "\t"], '', $b);
$curve->setCoefficients(
new BigInteger($a),
new BigInteger($b)
);
$x = str_replace(["\r", "\n", '
', "\t"], '', $x);
$y = str_replace(["\r", "\n", '
', "\t"], '', $y);
$curve->setBasePoint(
new BigInteger($x),
new BigInteger($y)
);
$order = str_replace(["\r", "\n",
' ', "\t"], '', $order);
$curve->setOrder(new BigInteger($order));
return $curve;
default:
throw new UnsupportedCurveException('Field Type of
' . $type . ' is not supported');
}
}
/**
* Sets the namespace. dsig11 is the most common one.
*
* Set to null to unset. Used only for creating public keys.
*
* @param string $namespace
*/
public static function setNamespace($namespace)
{
self::$namespace = $namespace;
}
/**
* Uses the XML syntax specified in https://tools.ietf.org/html/rfc4050
*/
public static function enableRFC4050Syntax()
{
self::$rfc4050 = true;
}
/**
* Uses the XML syntax specified in
https://www.w3.org/TR/xmldsig-core/#sec-ECParameters
*/
public static function disableRFC4050Syntax()
{
self::$rfc4050 = false;
}
/**
* Convert a public key to the appropriate format
*
* @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
* @param array $options optional
* @return string
*/
public static function savePublicKey(BaseCurve $curve, array
$publicKey, array $options = [])
{
self::initialize_static_variables();
if ($curve instanceof TwistedEdwardsCurve || $curve instanceof
MontgomeryCurve) {
throw new UnsupportedCurveException('TwistedEdwards and
Montgomery Curves are not supported');
}
if (empty(static::$namespace)) {
$pre = $post = '';
} else {
$pre = static::$namespace . ':';
$post = ':' . static::$namespace;
}
if (self::$rfc4050) {
return '<' . $pre . 'ECDSAKeyValue
xmlns' . $post .
'="http://www.w3.org/2001/04/xmldsig-more#">' .
"\r\n" .
self::encodeXMLParameters($curve, $pre, $options) .
"\r\n" .
'<' . $pre . 'PublicKey>' .
"\r\n" .
'<' . $pre . 'X Value="' .
$publicKey[0] . '" />' . "\r\n" .
'<' . $pre . 'Y Value="' .
$publicKey[1] . '" />' . "\r\n" .
'</' . $pre . 'PublicKey>' .
"\r\n" .
'</' . $pre .
'ECDSAKeyValue>';
}
$publicKey = "\4" . $publicKey[0]->toBytes() .
$publicKey[1]->toBytes();
return '<' . $pre . 'ECDSAKeyValue xmlns' .
$post . '="http://www.w3.org/2009/xmldsig11#">' .
"\r\n" .
self::encodeXMLParameters($curve, $pre, $options) .
"\r\n" .
'<' . $pre . 'PublicKey>' .
Strings::base64_encode($publicKey) . '</' . $pre .
'PublicKey>' . "\r\n" .
'</' . $pre . 'ECDSAKeyValue>';
}
/**
* Encode Parameters
*
* @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
* @param string $pre
* @param array $options optional
* @return string|false
*/
private static function encodeXMLParameters(BaseCurve $curve, $pre,
array $options = [])
{
$result = self::encodeParameters($curve, true, $options);
if (isset($result['namedCurve'])) {
$namedCurve = '<' . $pre . 'NamedCurve
URI="urn:oid:' .
self::$curveOIDs[$result['namedCurve']] . '"
/>';
return self::$rfc4050 ?
'<DomainParameters>' .
str_replace('URI', 'URN', $namedCurve) .
'</DomainParameters>' :
$namedCurve;
}
if (self::$rfc4050) {
$xml = '<' . $pre . 'ExplicitParams>'
. "\r\n" .
'<' . $pre . 'FieldParams>' .
"\r\n";
$temp = $result['specifiedCurve'];
switch ($temp['fieldID']['fieldType']) {
case 'prime-field':
$xml .= '<' . $pre .
'PrimeFieldParamsType>' . "\r\n" .
'<' . $pre . 'P>' .
$temp['fieldID']['parameters'] . '</' .
$pre . 'P>' . "\r\n" .
'</' . $pre .
'PrimeFieldParamsType>' . "\r\n";
$a = $curve->getA();
$b = $curve->getB();
list($x, $y) = $curve->getBasePoint();
break;
default:
throw new UnsupportedCurveException('Field Type of
' . $temp['fieldID']['fieldType'] . ' is not
supported');
}
$xml .= '</' . $pre . 'FieldParams>'
. "\r\n" .
'<' . $pre .
'CurveParamsType>' . "\r\n" .
'<' . $pre . 'A>' . $a .
'</' . $pre . 'A>' . "\r\n" .
'<' . $pre . 'B>' . $b .
'</' . $pre . 'B>' . "\r\n" .
'</' . $pre .
'CurveParamsType>' . "\r\n" .
'<' . $pre .
'BasePointParams>' . "\r\n" .
'<' . $pre . 'BasePoint>' .
"\r\n" .
'<' . $pre . 'ECPointType>' .
"\r\n" .
'<' . $pre . 'X>' . $x .
'</' . $pre . 'X>' . "\r\n" .
'<' . $pre . 'Y>' . $y .
'</' . $pre . 'Y>' . "\r\n" .
'</' . $pre . 'ECPointType>' .
"\r\n" .
'</' . $pre . 'BasePoint>' .
"\r\n" .
'<' . $pre . 'Order>' .
$curve->getOrder() . '</' . $pre . 'Order>' .
"\r\n" .
'</' . $pre .
'BasePointParams>' . "\r\n" .
'</' . $pre .
'ExplicitParams>' . "\r\n";
return $xml;
}
if (isset($result['specifiedCurve'])) {
$xml = '<' . $pre . 'ECParameters>' .
"\r\n" .
'<' . $pre . 'FieldID>' .
"\r\n";
$temp = $result['specifiedCurve'];
switch ($temp['fieldID']['fieldType']) {
case 'prime-field':
$xml .= '<' . $pre . 'Prime>'
. "\r\n" .
'<' . $pre . 'P>' .
Strings::base64_encode($temp['fieldID']['parameters']->toBytes())
. '</' . $pre . 'P>' . "\r\n" .
'</' . $pre . 'Prime>'
. "\r\n" ;
break;
default:
throw new UnsupportedCurveException('Field Type of
' . $temp['fieldID']['fieldType'] . ' is not
supported');
}
$xml .= '</' . $pre . 'FieldID>' .
"\r\n" .
'<' . $pre . 'Curve>' .
"\r\n" .
'<' . $pre . 'A>' .
Strings::base64_encode($temp['curve']['a']) .
'</' . $pre . 'A>' . "\r\n" .
'<' . $pre . 'B>' .
Strings::base64_encode($temp['curve']['b']) .
'</' . $pre . 'B>' . "\r\n" .
'</' . $pre . 'Curve>' .
"\r\n" .
'<' . $pre . 'Base>' .
Strings::base64_encode($temp['base']) . '</' . $pre
. 'Base>' . "\r\n" .
'<' . $pre . 'Order>' .
Strings::base64_encode($temp['order']) . '</' . $pre
. 'Order>' . "\r\n" .
'</' . $pre . 'ECParameters>';
return $xml;
}
}
}
phpseclib/phpseclib/Crypt/EC/Formats/Signature/ASN1.php000064400000002610151161424250016645
0ustar00<?php
/**
* ASN1 Signature Handler
*
* PHP version 5
*
* Handles signatures in the format described in
* https://tools.ietf.org/html/rfc3279#section-2.2.3
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\EC\Formats\Signature;
use phpseclib3\File\ASN1 as Encoder;
use phpseclib3\File\ASN1\Maps\EcdsaSigValue;
use phpseclib3\Math\BigInteger;
/**
* ASN1 Signature Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class ASN1
{
/**
* Loads a signature
*
* @param string $sig
* @return array
*/
public static function load($sig)
{
if (!is_string($sig)) {
return false;
}
$decoded = Encoder::decodeBER($sig);
if (empty($decoded)) {
return false;
}
$components = Encoder::asn1map($decoded[0], EcdsaSigValue::MAP);
return $components;
}
/**
* Returns a signature in the appropriate format
*
* @param \phpseclib3\Math\BigInteger $r
* @param \phpseclib3\Math\BigInteger $s
* @return string
*/
public static function save(BigInteger $r, BigInteger $s)
{
return Encoder::encodeDER(compact('r', 's'),
EcdsaSigValue::MAP);
}
}
phpseclib/phpseclib/Crypt/EC/Formats/Signature/Raw.php000064400000001010151161424250016665
0ustar00<?php
/**
* Raw EC Signature Handler
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\EC\Formats\Signature;
use phpseclib3\Crypt\Common\Formats\Signature\Raw as Progenitor;
/**
* Raw DSA Signature Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Raw extends Progenitor
{
}
phpseclib/phpseclib/Crypt/EC/Formats/Signature/SSH2.php000064400000004251151161424250016665
0ustar00<?php
/**
* SSH2 Signature Handler
*
* PHP version 5
*
* Handles signatures in the format used by SSH2
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\EC\Formats\Signature;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Math\BigInteger;
/**
* SSH2 Signature Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class SSH2
{
/**
* Loads a signature
*
* @param string $sig
* @return mixed
*/
public static function load($sig)
{
if (!is_string($sig)) {
return false;
}
$result = Strings::unpackSSH2('ss', $sig);
if ($result === false) {
return false;
}
list($type, $blob) = $result;
switch ($type) {
// see https://tools.ietf.org/html/rfc5656#section-3.1.2
case 'ecdsa-sha2-nistp256':
case 'ecdsa-sha2-nistp384':
case 'ecdsa-sha2-nistp521':
break;
default:
return false;
}
$result = Strings::unpackSSH2('ii', $blob);
if ($result === false) {
return false;
}
return [
'r' => $result[0],
's' => $result[1]
];
}
/**
* Returns a signature in the appropriate format
*
* @param \phpseclib3\Math\BigInteger $r
* @param \phpseclib3\Math\BigInteger $s
* @param string $curve
* @return string
*/
public static function save(BigInteger $r, BigInteger $s, $curve)
{
switch ($curve) {
case 'secp256r1':
$curve = 'nistp256';
break;
case 'secp384r1':
$curve = 'nistp384';
break;
case 'secp521r1':
$curve = 'nistp521';
break;
default:
return false;
}
$blob = Strings::packSSH2('ii', $r, $s);
return Strings::packSSH2('ss', 'ecdsa-sha2-' .
$curve, $blob);
}
}
phpseclib/phpseclib/Crypt/EC/Parameters.php000064400000001402151161424250014670
0ustar00<?php
/**
* EC Parameters
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\EC;
use phpseclib3\Crypt\EC;
/**
* EC Parameters
*
* @author Jim Wigginton <terrafrost@php.net>
*/
final class Parameters extends EC
{
/**
* Returns the parameters
*
* @param string $type
* @param array $options optional
* @return string
*/
public function toString($type = 'PKCS1', array $options =
[])
{
$type = self::validatePlugin('Keys', 'PKCS1',
'saveParameters');
return $type::saveParameters($this->curve, $options);
}
}
phpseclib/phpseclib/Crypt/EC/PrivateKey.php000064400000022347151161424250014663
0ustar00<?php
/**
* EC Private Key
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\EC;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common;
use phpseclib3\Crypt\EC;
use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
use phpseclib3\Crypt\EC\Curves\Curve25519;
use phpseclib3\Crypt\EC\Curves\Ed25519;
use phpseclib3\Crypt\EC\Formats\Keys\PKCS1;
use phpseclib3\Crypt\EC\Formats\Signature\ASN1 as ASN1Signature;
use phpseclib3\Crypt\Hash;
use phpseclib3\Exception\UnsupportedOperationException;
use phpseclib3\Math\BigInteger;
/**
* EC Private Key
*
* @author Jim Wigginton <terrafrost@php.net>
*/
final class PrivateKey extends EC implements Common\PrivateKey
{
use Common\Traits\PasswordProtected;
/**
* Private Key dA
*
* sign() converts this to a BigInteger so one might wonder why this is
a FiniteFieldInteger instead of
* a BigInteger. That's because a FiniteFieldInteger, when
converted to a byte string, is null padded by
* a certain amount whereas a BigInteger isn't.
*
* @var object
*/
protected $dA;
/**
* @var string
*/
protected $secret;
/**
* Multiplies an encoded point by the private key
*
* Used by ECDH
*
* @param string $coordinates
* @return string
*/
public function multiply($coordinates)
{
if ($this->curve instanceof MontgomeryCurve) {
if ($this->curve instanceof Curve25519 &&
self::$engines['libsodium']) {
return sodium_crypto_scalarmult($this->dA->toBytes(),
$coordinates);
}
$point = [$this->curve->convertInteger(new
BigInteger(strrev($coordinates), 256))];
$point = $this->curve->multiplyPoint($point,
$this->dA);
return strrev($point[0]->toBytes(true));
}
if (!$this->curve instanceof TwistedEdwardsCurve) {
$coordinates = "\0$coordinates";
}
$point = PKCS1::extractPoint($coordinates, $this->curve);
$point = $this->curve->multiplyPoint($point, $this->dA);
if ($this->curve instanceof TwistedEdwardsCurve) {
return $this->curve->encodePoint($point);
}
if (empty($point)) {
throw new \RuntimeException('The infinity point is
invalid');
}
return "\4" . $point[0]->toBytes(true) .
$point[1]->toBytes(true);
}
/**
* Create a signature
*
* @see self::verify()
* @param string $message
* @return mixed
*/
public function sign($message)
{
if ($this->curve instanceof MontgomeryCurve) {
throw new UnsupportedOperationException('Montgomery Curves
cannot be used to create signatures');
}
$dA = $this->dA;
$order = $this->curve->getOrder();
$shortFormat = $this->shortFormat;
$format = $this->sigFormat;
if ($format === false) {
return false;
}
if ($this->curve instanceof TwistedEdwardsCurve) {
if ($this->curve instanceof Ed25519 &&
self::$engines['libsodium'] && !isset($this->context))
{
$result = sodium_crypto_sign_detached($message,
$this->withPassword()->toString('libsodium'));
return $shortFormat == 'SSH2' ?
Strings::packSSH2('ss', 'ssh-' .
strtolower($this->getCurve()), $result) : $result;
}
// contexts (Ed25519ctx) are supported but prehashing
(Ed25519ph) is not.
// quoting https://tools.ietf.org/html/rfc8032#section-8.5 ,
// "The Ed25519ph and Ed448ph variants ... SHOULD NOT be
used"
$A = $this->curve->encodePoint($this->QA);
$curve = $this->curve;
$hash = new Hash($curve::HASH);
$secret = substr($hash->hash($this->secret),
$curve::SIZE);
if ($curve instanceof Ed25519) {
$dom = !isset($this->context) ? '' :
'SigEd25519 no Ed25519 collisions' .
"\0" . chr(strlen($this->context)) . $this->context;
} else {
$context = isset($this->context) ? $this->context :
'';
$dom = 'SigEd448' . "\0" .
chr(strlen($context)) . $context;
}
// SHA-512(dom2(F, C) || prefix || PH(M))
$r = $hash->hash($dom . $secret . $message);
$r = strrev($r);
$r = new BigInteger($r, 256);
list(, $r) = $r->divide($order);
$R = $curve->multiplyPoint($curve->getBasePoint(), $r);
$R = $curve->encodePoint($R);
$k = $hash->hash($dom . $R . $A . $message);
$k = strrev($k);
$k = new BigInteger($k, 256);
list(, $k) = $k->divide($order);
$S = $k->multiply($dA)->add($r);
list(, $S) = $S->divide($order);
$S = str_pad(strrev($S->toBytes()), $curve::SIZE,
"\0");
return $shortFormat == 'SSH2' ?
Strings::packSSH2('ss', 'ssh-' .
strtolower($this->getCurve()), $R . $S) : $R . $S;
}
if (self::$engines['OpenSSL'] &&
in_array($this->hash->getHash(), openssl_get_md_methods())) {
$signature = '';
// altho PHP's OpenSSL bindings only supported EC key
creation in PHP 7.1 they've long
// supported signing / verification
// we use specified curves to avoid issues with OpenSSL
possibly not supporting a given named curve;
// doing this may mean some curve-specific optimizations
can't be used but idk if OpenSSL even
// has curve-specific optimizations
$result = openssl_sign($message, $signature,
$this->toString('PKCS8', ['namedCurve' =>
false]), $this->hash->getHash());
if ($result) {
if ($shortFormat == 'ASN1') {
return $signature;
}
extract(ASN1Signature::load($signature));
return $shortFormat == 'SSH2' ? $format::save($r,
$s, $this->getCurve()) : $format::save($r, $s);
}
}
$e = $this->hash->hash($message);
$e = new BigInteger($e, 256);
$Ln = $this->hash->getLength() - $order->getLength();
$z = $Ln > 0 ? $e->bitwise_rightShift($Ln) : $e;
while (true) {
$k = BigInteger::randomRange(self::$one,
$order->subtract(self::$one));
list($x, $y) =
$this->curve->multiplyPoint($this->curve->getBasePoint(), $k);
$x = $x->toBigInteger();
list(, $r) = $x->divide($order);
if ($r->equals(self::$zero)) {
continue;
}
$kinv = $k->modInverse($order);
$temp = $z->add($dA->multiply($r));
$temp = $kinv->multiply($temp);
list(, $s) = $temp->divide($order);
if (!$s->equals(self::$zero)) {
break;
}
}
// the following is an RFC6979 compliant implementation of
deterministic ECDSA
// it's unused because it's mainly intended for use when
a good CSPRNG isn't
// available. if phpseclib's CSPRNG isn't good then even
key generation is
// suspect
/*
// if this were actually being used it'd probably be better if
this lived in load() and createKey()
$this->q = $this->curve->getOrder();
$dA = $this->dA->toBigInteger();
$this->x = $dA;
$h1 = $this->hash->hash($message);
$k = $this->computek($h1);
list($x, $y) =
$this->curve->multiplyPoint($this->curve->getBasePoint(), $k);
$x = $x->toBigInteger();
list(, $r) = $x->divide($this->q);
$kinv = $k->modInverse($this->q);
$h1 = $this->bits2int($h1);
$temp = $h1->add($dA->multiply($r));
$temp = $kinv->multiply($temp);
list(, $s) = $temp->divide($this->q);
*/
return $shortFormat == 'SSH2' ? $format::save($r, $s,
$this->getCurve()) : $format::save($r, $s);
}
/**
* Returns the private key
*
* @param string $type
* @param array $options optional
* @return string
*/
public function toString($type, array $options = [])
{
$type = self::validatePlugin('Keys', $type,
'savePrivateKey');
return $type::savePrivateKey($this->dA, $this->curve,
$this->QA, $this->secret, $this->password, $options);
}
/**
* Returns the public key
*
* @see self::getPrivateKey()
* @return mixed
*/
public function getPublicKey()
{
$format = 'PKCS8';
if ($this->curve instanceof MontgomeryCurve) {
$format = 'MontgomeryPublic';
}
$type = self::validatePlugin('Keys', $format,
'savePublicKey');
$key = $type::savePublicKey($this->curve, $this->QA);
$key = EC::loadFormat($format, $key);
if ($this->curve instanceof MontgomeryCurve) {
return $key;
}
$key = $key
->withHash($this->hash->getHash())
->withSignatureFormat($this->shortFormat);
if ($this->curve instanceof TwistedEdwardsCurve) {
$key = $key->withContext($this->context);
}
return $key;
}
}
phpseclib/phpseclib/Crypt/EC/PublicKey.php000064400000012502151161424250014457
0ustar00<?php
/**
* EC Public Key
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\EC;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common;
use phpseclib3\Crypt\EC;
use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
use phpseclib3\Crypt\EC\Curves\Ed25519;
use phpseclib3\Crypt\EC\Formats\Keys\PKCS1;
use phpseclib3\Crypt\EC\Formats\Signature\ASN1 as ASN1Signature;
use phpseclib3\Crypt\Hash;
use phpseclib3\Exception\UnsupportedOperationException;
use phpseclib3\Math\BigInteger;
/**
* EC Public Key
*
* @author Jim Wigginton <terrafrost@php.net>
*/
final class PublicKey extends EC implements Common\PublicKey
{
use Common\Traits\Fingerprint;
/**
* Verify a signature
*
* @see self::verify()
* @param string $message
* @param string $signature
* @return mixed
*/
public function verify($message, $signature)
{
if ($this->curve instanceof MontgomeryCurve) {
throw new UnsupportedOperationException('Montgomery Curves
cannot be used to create signatures');
}
$shortFormat = $this->shortFormat;
$format = $this->sigFormat;
if ($format === false) {
return false;
}
$order = $this->curve->getOrder();
if ($this->curve instanceof TwistedEdwardsCurve) {
if ($shortFormat == 'SSH2') {
list(, $signature) = Strings::unpackSSH2('ss',
$signature);
}
if ($this->curve instanceof Ed25519 &&
self::$engines['libsodium'] && !isset($this->context))
{
return sodium_crypto_sign_verify_detached($signature,
$message, $this->toString('libsodium'));
}
$curve = $this->curve;
if (strlen($signature) != 2 * $curve::SIZE) {
return false;
}
$R = substr($signature, 0, $curve::SIZE);
$S = substr($signature, $curve::SIZE);
try {
$R = PKCS1::extractPoint($R, $curve);
$R = $this->curve->convertToInternal($R);
} catch (\Exception $e) {
return false;
}
$S = strrev($S);
$S = new BigInteger($S, 256);
if ($S->compare($order) >= 0) {
return false;
}
$A = $curve->encodePoint($this->QA);
if ($curve instanceof Ed25519) {
$dom2 = !isset($this->context) ? '' :
'SigEd25519 no Ed25519 collisions' .
"\0" . chr(strlen($this->context)) . $this->context;
} else {
$context = isset($this->context) ? $this->context :
'';
$dom2 = 'SigEd448' . "\0" .
chr(strlen($context)) . $context;
}
$hash = new Hash($curve::HASH);
$k = $hash->hash($dom2 . substr($signature, 0, $curve::SIZE)
. $A . $message);
$k = strrev($k);
$k = new BigInteger($k, 256);
list(, $k) = $k->divide($order);
$qa = $curve->convertToInternal($this->QA);
$lhs = $curve->multiplyPoint($curve->getBasePoint(), $S);
$rhs = $curve->multiplyPoint($qa, $k);
$rhs = $curve->addPoint($rhs, $R);
$rhs = $curve->convertToAffine($rhs);
return $lhs[0]->equals($rhs[0]) &&
$lhs[1]->equals($rhs[1]);
}
$params = $format::load($signature);
if ($params === false || count($params) != 2) {
return false;
}
extract($params);
if (self::$engines['OpenSSL'] &&
in_array($this->hash->getHash(), openssl_get_md_methods())) {
$sig = $format != 'ASN1' ? ASN1Signature::save($r,
$s) : $signature;
$result = openssl_verify($message, $sig,
$this->toString('PKCS8', ['namedCurve' =>
false]), $this->hash->getHash());
if ($result != -1) {
return (bool) $result;
}
}
$n_1 = $order->subtract(self::$one);
if (!$r->between(self::$one, $n_1) ||
!$s->between(self::$one, $n_1)) {
return false;
}
$e = $this->hash->hash($message);
$e = new BigInteger($e, 256);
$Ln = $this->hash->getLength() - $order->getLength();
$z = $Ln > 0 ? $e->bitwise_rightShift($Ln) : $e;
$w = $s->modInverse($order);
list(, $u1) = $z->multiply($w)->divide($order);
list(, $u2) = $r->multiply($w)->divide($order);
$u1 = $this->curve->convertInteger($u1);
$u2 = $this->curve->convertInteger($u2);
list($x1, $y1) = $this->curve->multiplyAddPoints(
[$this->curve->getBasePoint(), $this->QA],
[$u1, $u2]
);
$x1 = $x1->toBigInteger();
list(, $x1) = $x1->divide($order);
return $x1->equals($r);
}
/**
* Returns the public key
*
* @param string $type
* @param array $options optional
* @return string
*/
public function toString($type, array $options = [])
{
$type = self::validatePlugin('Keys', $type,
'savePublicKey');
return $type::savePublicKey($this->curve, $this->QA,
$options);
}
}
phpseclib/phpseclib/Crypt/ChaCha20.php000064400000111001151161424250013544
0ustar00<?php
/**
* Pure-PHP implementation of ChaCha20.
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2019 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt;
use phpseclib3\Exception\BadDecryptionException;
use phpseclib3\Exception\InsufficientSetupException;
/**
* Pure-PHP implementation of ChaCha20.
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class ChaCha20 extends Salsa20
{
/**
* The OpenSSL specific name of the cipher
*
* @var string
*/
protected $cipher_name_openssl = 'chacha20';
/**
* Test for engine validity
*
* This is mainly just a wrapper to set things up for
\phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* @param int $engine
* @return bool
*/
protected function isValidEngineHelper($engine)
{
switch ($engine) {
case self::ENGINE_LIBSODIUM:
// PHP 7.2.0 (30 Nov 2017) added support for libsodium
// we could probably make it so that if $this->counter
== 0 then the first block would be done with either OpenSSL
// or PHP and then subsequent blocks would then be done
with libsodium but idk - it's not a high priority atm
// we could also make it so that if $this->counter == 0
and $this->continuousBuffer then do the first string
// with libsodium and subsequent strings with openssl or
pure-PHP but again not a high priority
return
function_exists('sodium_crypto_aead_chacha20poly1305_ietf_encrypt')
&&
$this->key_length == 32 &&
(($this->usePoly1305 &&
!isset($this->poly1305Key) && $this->counter == 0) ||
$this->counter == 1) &&
!$this->continuousBuffer;
case self::ENGINE_OPENSSL:
// OpenSSL 1.1.0 (released 25 Aug 2016) added support for
chacha20.
// PHP didn't support OpenSSL 1.1.0 until 7.0.19 (11
May 2017)
// if you attempt to provide openssl with a 128 bit key (as
opposed to a 256 bit key) openssl will null
// pad the key to 256 bits and still use the expansion
constant for 256-bit keys. the fact that
// openssl treats the IV as both the counter and nonce,
however, let's us use openssl in continuous mode
// whereas libsodium does not
if ($this->key_length != 32) {
return false;
}
}
return parent::isValidEngineHelper($engine);
}
/**
* Encrypts a message.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
* @see self::crypt()
* @param string $plaintext
* @return string $ciphertext
*/
public function encrypt($plaintext)
{
$this->setup();
if ($this->engine == self::ENGINE_LIBSODIUM) {
return $this->encrypt_with_libsodium($plaintext);
}
return parent::encrypt($plaintext);
}
/**
* Decrypts a message.
*
* $this->decrypt($this->encrypt($plaintext)) ==
$this->encrypt($this->encrypt($plaintext)).
* At least if the continuous buffer is disabled.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see self::crypt()
* @param string $ciphertext
* @return string $plaintext
*/
public function decrypt($ciphertext)
{
$this->setup();
if ($this->engine == self::ENGINE_LIBSODIUM) {
return $this->decrypt_with_libsodium($ciphertext);
}
return parent::decrypt($ciphertext);
}
/**
* Encrypts a message with libsodium
*
* @see self::encrypt()
* @param string $plaintext
* @return string $text
*/
private function encrypt_with_libsodium($plaintext)
{
$params = [$plaintext, $this->aad, $this->nonce,
$this->key];
$ciphertext = strlen($this->nonce) == 8 ?
sodium_crypto_aead_chacha20poly1305_encrypt(...$params) :
sodium_crypto_aead_chacha20poly1305_ietf_encrypt(...$params);
if (!$this->usePoly1305) {
return substr($ciphertext, 0, strlen($plaintext));
}
$newciphertext = substr($ciphertext, 0, strlen($plaintext));
$this->newtag = $this->usingGeneratedPoly1305Key &&
strlen($this->nonce) == 12 ?
substr($ciphertext, strlen($plaintext)) :
$this->poly1305($newciphertext);
return $newciphertext;
}
/**
* Decrypts a message with libsodium
*
* @see self::decrypt()
* @param string $ciphertext
* @return string $text
*/
private function decrypt_with_libsodium($ciphertext)
{
$params = [$ciphertext, $this->aad, $this->nonce,
$this->key];
if (isset($this->poly1305Key)) {
if ($this->oldtag === false) {
throw new InsufficientSetupException('Authentication
Tag has not been set');
}
if ($this->usingGeneratedPoly1305Key &&
strlen($this->nonce) == 12) {
$plaintext =
sodium_crypto_aead_chacha20poly1305_ietf_decrypt(...$params);
$this->oldtag = false;
if ($plaintext === false) {
throw new BadDecryptionException('Derived
authentication tag and supplied authentication tag do not match');
}
return $plaintext;
}
$newtag = $this->poly1305($ciphertext);
if ($this->oldtag != substr($newtag, 0,
strlen($this->oldtag))) {
$this->oldtag = false;
throw new BadDecryptionException('Derived
authentication tag and supplied authentication tag do not match');
}
$this->oldtag = false;
}
$plaintext = strlen($this->nonce) == 8 ?
sodium_crypto_aead_chacha20poly1305_encrypt(...$params) :
sodium_crypto_aead_chacha20poly1305_ietf_encrypt(...$params);
return substr($plaintext, 0, strlen($ciphertext));
}
/**
* Sets the nonce.
*
* @param string $nonce
*/
public function setNonce($nonce)
{
if (!is_string($nonce)) {
throw new \UnexpectedValueException('The nonce should be a
string');
}
/*
from https://tools.ietf.org/html/rfc7539#page-7
"Note also that the original ChaCha had a 64-bit nonce and
64-bit
block count. We have modified this here to be more consistent
with
recommendations in Section 3.2 of [RFC5116]."
*/
switch (strlen($nonce)) {
case 8: // 64 bits
case 12: // 96 bits
break;
default:
throw new \LengthException('Nonce of size ' .
strlen($nonce) . ' not supported by this algorithm. Only 64-bit nonces
or 96-bit nonces are supported');
}
$this->nonce = $nonce;
$this->changed = true;
$this->setEngine();
}
/**
* Setup the self::ENGINE_INTERNAL $engine
*
* (re)init, if necessary, the internal cipher $engine
*
* _setup() will be called each time if $changed === true
* typically this happens when using one or more of following public
methods:
*
* - setKey()
*
* - setNonce()
*
* - First run of encrypt() / decrypt() with no init-settings
*
* @see self::setKey()
* @see self::setNonce()
* @see self::disableContinuousBuffer()
*/
protected function setup()
{
if (!$this->changed) {
return;
}
$this->enbuffer = $this->debuffer = ['ciphertext'
=> '', 'counter' => $this->counter];
$this->changed = $this->nonIVChanged = false;
if ($this->nonce === false) {
throw new InsufficientSetupException('No nonce has been
defined');
}
if ($this->key === false) {
throw new InsufficientSetupException('No key has been
defined');
}
if ($this->usePoly1305 && !isset($this->poly1305Key))
{
$this->usingGeneratedPoly1305Key = true;
if ($this->engine == self::ENGINE_LIBSODIUM) {
return;
}
$this->createPoly1305Key();
}
$key = $this->key;
if (strlen($key) == 16) {
$constant = 'expand 16-byte k';
$key .= $key;
} else {
$constant = 'expand 32-byte k';
}
$this->p1 = $constant . $key;
$this->p2 = $this->nonce;
if (strlen($this->nonce) == 8) {
$this->p2 = "\0\0\0\0" . $this->p2;
}
}
/**
* The quarterround function
*
* @param int $a
* @param int $b
* @param int $c
* @param int $d
*/
protected static function quarterRound(&$a, &$b, &$c,
&$d)
{
// in https://datatracker.ietf.org/doc/html/rfc7539#section-2.1 the
addition,
// xor'ing and rotation are all on the same line so i'm
keeping it on the same
// line here as well
// @codingStandardsIgnoreStart
$a+= $b; $d = self::leftRotate(intval($d) ^ intval($a), 16);
$c+= $d; $b = self::leftRotate(intval($b) ^ intval($c), 12);
$a+= $b; $d = self::leftRotate(intval($d) ^ intval($a), 8);
$c+= $d; $b = self::leftRotate(intval($b) ^ intval($c), 7);
// @codingStandardsIgnoreEnd
}
/**
* The doubleround function
*
* @param int $x0 (by reference)
* @param int $x1 (by reference)
* @param int $x2 (by reference)
* @param int $x3 (by reference)
* @param int $x4 (by reference)
* @param int $x5 (by reference)
* @param int $x6 (by reference)
* @param int $x7 (by reference)
* @param int $x8 (by reference)
* @param int $x9 (by reference)
* @param int $x10 (by reference)
* @param int $x11 (by reference)
* @param int $x12 (by reference)
* @param int $x13 (by reference)
* @param int $x14 (by reference)
* @param int $x15 (by reference)
*/
protected static function doubleRound(&$x0, &$x1, &$x2,
&$x3, &$x4, &$x5, &$x6, &$x7, &$x8, &$x9,
&$x10, &$x11, &$x12, &$x13, &$x14, &$x15)
{
// columnRound
static::quarterRound($x0, $x4, $x8, $x12);
static::quarterRound($x1, $x5, $x9, $x13);
static::quarterRound($x2, $x6, $x10, $x14);
static::quarterRound($x3, $x7, $x11, $x15);
// rowRound
static::quarterRound($x0, $x5, $x10, $x15);
static::quarterRound($x1, $x6, $x11, $x12);
static::quarterRound($x2, $x7, $x8, $x13);
static::quarterRound($x3, $x4, $x9, $x14);
}
/**
* The Salsa20 hash function function
*
* On my laptop this loop unrolled / function dereferenced version of
parent::salsa20 encrypts 1mb of text in
* 0.65s vs the 0.85s that it takes with the parent method.
*
* If we were free to assume that the host OS would always be 64-bits
then the if condition in leftRotate could
* be eliminated and we could knock this done to 0.60s.
*
* For comparison purposes, RC4 takes 0.16s and AES in CTR mode with
the Eval engine takes 0.48s.
* AES in CTR mode with the PHP engine takes 1.19s. Salsa20 / ChaCha20
do not benefit as much from the Eval
* approach due to the fact that there are a lot less variables to
de-reference, fewer loops to unroll, etc
*
* @param string $x
*/
protected static function salsa20($x)
{
list(, $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10,
$x11, $x12, $x13, $x14, $x15) = unpack('V*', $x);
$z0 = $x0;
$z1 = $x1;
$z2 = $x2;
$z3 = $x3;
$z4 = $x4;
$z5 = $x5;
$z6 = $x6;
$z7 = $x7;
$z8 = $x8;
$z9 = $x9;
$z10 = $x10;
$z11 = $x11;
$z12 = $x12;
$z13 = $x13;
$z14 = $x14;
$z15 = $x15;
// @codingStandardsIgnoreStart
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10),
12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11),
12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10),
12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11),
12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10),
12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11),
12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10),
12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11),
12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10),
12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11),
12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10),
12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11),
12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10),
12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11),
12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10),
12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11),
12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10),
12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11),
12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10),
12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11),
12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10),
12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11),
12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10),
12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11),
12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10),
12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11),
12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10),
12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11),
12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10),
12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11),
12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10),
12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11),
12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10),
12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11),
12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10),
12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11),
12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10),
12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11),
12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10),
12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11),
12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// @codingStandardsIgnoreEnd
$x0 += $z0;
$x1 += $z1;
$x2 += $z2;
$x3 += $z3;
$x4 += $z4;
$x5 += $z5;
$x6 += $z6;
$x7 += $z7;
$x8 += $z8;
$x9 += $z9;
$x10 += $z10;
$x11 += $z11;
$x12 += $z12;
$x13 += $z13;
$x14 += $z14;
$x15 += $z15;
return pack('V*', $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7,
$x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15);
}
}
phpseclib/phpseclib/Crypt/DH.php000064400000045341151161424250012603
0ustar00<?php
/**
* Pure-PHP (EC)DH implementation
*
* PHP version 5
*
* Here's an example of how to compute a shared secret with this
library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $ourPrivate = \phpseclib3\Crypt\DH::createKey();
* $secret = DH::computeSecret($ourPrivate, $theirPublic);
*
* ?>
* </code>
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt;
use phpseclib3\Crypt\Common\AsymmetricKey;
use phpseclib3\Crypt\DH\Parameters;
use phpseclib3\Crypt\DH\PrivateKey;
use phpseclib3\Crypt\DH\PublicKey;
use phpseclib3\Exception\NoKeyLoadedException;
use phpseclib3\Exception\UnsupportedOperationException;
use phpseclib3\Math\BigInteger;
/**
* Pure-PHP (EC)DH implementation
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class DH extends AsymmetricKey
{
/**
* Algorithm Name
*
* @var string
*/
const ALGORITHM = 'DH';
/**
* DH prime
*
* @var \phpseclib3\Math\BigInteger
*/
protected $prime;
/**
* DH Base
*
* Prime divisor of p-1
*
* @var \phpseclib3\Math\BigInteger
*/
protected $base;
/**
* Public Key
*
* @var \phpseclib3\Math\BigInteger
*/
protected $publicKey;
/**
* Create DH parameters
*
* This method is a bit polymorphic. It can take any of the following:
* - two BigInteger's (prime and base)
* - an integer representing the size of the prime in bits (the base
is assumed to be 2)
* - a string (eg. diffie-hellman-group14-sha1)
*
* @return Parameters
*/
public static function createParameters(...$args)
{
$class = new \ReflectionClass(static::class);
if ($class->isFinal()) {
throw new \RuntimeException('createParameters() should not
be called from final classes (' . static::class . ')');
}
$params = new Parameters();
if (count($args) == 2 && $args[0] instanceof BigInteger
&& $args[1] instanceof BigInteger) {
//if (!$args[0]->isPrime()) {
// throw new \InvalidArgumentException('The first
parameter should be a prime number');
//}
$params->prime = $args[0];
$params->base = $args[1];
return $params;
} elseif (count($args) == 1 && is_numeric($args[0])) {
$params->prime = BigInteger::randomPrime($args[0]);
$params->base = new BigInteger(2);
return $params;
} elseif (count($args) != 1 || !is_string($args[0])) {
throw new \InvalidArgumentException('Valid parameters are
either: two BigInteger\'s (prime and base), a single integer (the
length of the prime; base is assumed to be 2) or a string');
}
switch ($args[0]) {
// see http://tools.ietf.org/html/rfc2409#section-6.2 and
// http://tools.ietf.org/html/rfc2412, appendex E
case 'diffie-hellman-group1-sha1':
$prime =
'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74'
.
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437'
.
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED'
.
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
break;
// see http://tools.ietf.org/html/rfc3526#section-3
case 'diffie-hellman-group14-sha1': // 2048-bit MODP
Group
case 'diffie-hellman-group14-sha256':
$prime =
'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74'
.
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437'
.
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED'
.
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05'
.
'98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB'
.
'9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B'
.
'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718'
.
'3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF';
break;
// see https://tools.ietf.org/html/rfc3526#section-4
case 'diffie-hellman-group15-sha512': // 3072-bit
MODP Group
$prime =
'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74'
.
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437'
.
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED'
.
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05'
.
'98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB'
.
'9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B'
.
'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718'
.
'3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33'
.
'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7'
.
'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864'
.
'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2'
.
'08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF';
break;
// see https://tools.ietf.org/html/rfc3526#section-5
case 'diffie-hellman-group16-sha512': // 4096-bit
MODP Group
$prime =
'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74'
.
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437'
.
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED'
.
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05'
.
'98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB'
.
'9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B'
.
'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718'
.
'3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33'
.
'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7'
.
'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864'
.
'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2'
.
'08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7'
.
'88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8'
.
'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2'
.
'233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9'
.
'93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF';
break;
// see https://tools.ietf.org/html/rfc3526#section-6
case 'diffie-hellman-group17-sha512': // 6144-bit
MODP Group
$prime =
'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74'
.
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437'
.
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED'
.
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05'
.
'98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB'
.
'9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B'
.
'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718'
.
'3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33'
.
'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7'
.
'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864'
.
'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2'
.
'08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7'
.
'88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8'
.
'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2'
.
'233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9'
.
'93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026'
.
'C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AE'
.
'B06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B'
.
'DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92EC'
.
'F032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E'
.
'59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA'
.
'CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76'
.
'F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468'
.
'043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF';
break;
// see https://tools.ietf.org/html/rfc3526#section-7
case 'diffie-hellman-group18-sha512': // 8192-bit
MODP Group
$prime =
'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74'
.
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437'
.
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED'
.
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05'
.
'98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB'
.
'9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B'
.
'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718'
.
'3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33'
.
'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7'
.
'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864'
.
'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2'
.
'08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7'
.
'88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8'
.
'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2'
.
'233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9'
.
'93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026'
.
'C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AE'
.
'B06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B'
.
'DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92EC'
.
'F032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E'
.
'59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA'
.
'CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76'
.
'F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468'
.
'043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4'
.
'38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED'
.
'2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652D'
.
'E3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B'
.
'4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6'
.
'6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851D'
.
'F9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92'
.
'4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA'
.
'9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF';
break;
default:
throw new \InvalidArgumentException('Invalid named
prime provided');
}
$params->prime = new BigInteger($prime, 16);
$params->base = new BigInteger(2);
return $params;
}
/**
* Create public / private key pair.
*
* The rationale for the second parameter is described in
http://tools.ietf.org/html/rfc4419#section-6.2 :
*
* "To increase the speed of the key exchange, both client and
server may
* reduce the size of their private exponents. It should be at least
* twice as long as the key material that is generated from the shared
* secret. For more details, see the paper by van Oorschot and Wiener
* [VAN-OORSCHOT]."
*
* $length is in bits
*
* @param Parameters $params
* @param int $length optional
* @return DH\PrivateKey
*/
public static function createKey(Parameters $params, $length = 0)
{
$class = new \ReflectionClass(static::class);
if ($class->isFinal()) {
throw new \RuntimeException('createKey() should not be
called from final classes (' . static::class . ')');
}
$one = new BigInteger(1);
if ($length) {
$max = $one->bitwise_leftShift($length);
$max = $max->subtract($one);
} else {
$max = $params->prime->subtract($one);
}
$key = new PrivateKey();
$key->prime = $params->prime;
$key->base = $params->base;
$key->privateKey = BigInteger::randomRange($one, $max);
$key->publicKey = $key->base->powMod($key->privateKey,
$key->prime);
return $key;
}
/**
* Compute Shared Secret
*
* @param PrivateKey|EC $private
* @param PublicKey|BigInteger|string $public
* @return mixed
*/
public static function computeSecret($private, $public)
{
if ($private instanceof PrivateKey) { // DH\PrivateKey
switch (true) {
case $public instanceof PublicKey:
if (!$private->prime->equals($public->prime)
|| !$private->base->equals($public->base)) {
throw new \InvalidArgumentException('The
public and private key do not share the same prime and / or base
numbers');
}
return
$public->publicKey->powMod($private->privateKey,
$private->prime)->toBytes(true);
case is_string($public):
$public = new BigInteger($public, -256);
// fall-through
case $public instanceof BigInteger:
return $public->powMod($private->privateKey,
$private->prime)->toBytes(true);
default:
throw new \InvalidArgumentException('$public needs
to be an instance of DH\PublicKey, a BigInteger or a string');
}
}
if ($private instanceof EC\PrivateKey) {
switch (true) {
case $public instanceof EC\PublicKey:
$public = $public->getEncodedCoordinates();
// fall-through
case is_string($public):
$point = $private->multiply($public);
switch ($private->getCurve()) {
case 'Curve25519':
case 'Curve448':
$secret = $point;
break;
default:
// according to
https://www.secg.org/sec1-v2.pdf#page=33 only X is returned
$secret = substr($point, 1, (strlen($point) -
1) >> 1);
}
/*
if (($secret[0] & "\x80") ===
"\x80") {
$secret = "\0$secret";
}
*/
return $secret;
default:
throw new \InvalidArgumentException('$public needs
to be an instance of EC\PublicKey or a string (an encoded
coordinate)');
}
}
}
/**
* Load the key
*
* @param string $key
* @param string $password optional
* @return AsymmetricKey
*/
public static function load($key, $password = false)
{
try {
return EC::load($key, $password);
} catch (NoKeyLoadedException $e) {
}
return parent::load($key, $password);
}
/**
* OnLoad Handler
*
* @return bool
*/
protected static function onLoad(array $components)
{
if (!isset($components['privateKey']) &&
!isset($components['publicKey'])) {
$new = new Parameters();
} else {
$new = isset($components['privateKey']) ?
new PrivateKey() :
new PublicKey();
}
$new->prime = $components['prime'];
$new->base = $components['base'];
if (isset($components['privateKey'])) {
$new->privateKey = $components['privateKey'];
}
if (isset($components['publicKey'])) {
$new->publicKey = $components['publicKey'];
}
return $new;
}
/**
* Determines which hashing function should be used
*
* @param string $hash
*/
public function withHash($hash)
{
throw new UnsupportedOperationException('DH does not use a
hash algorithm');
}
/**
* Returns the hash algorithm currently being used
*
*/
public function getHash()
{
throw new UnsupportedOperationException('DH does not use a
hash algorithm');
}
/**
* Returns the parameters
*
* A public / private key is only returned if the currently loaded
"key" contains an x or y
* value.
*
* @see self::getPublicKey()
* @return mixed
*/
public function getParameters()
{
$type = DH::validatePlugin('Keys', 'PKCS1',
'saveParameters');
$key = $type::saveParameters($this->prime, $this->base);
return DH::load($key, 'PKCS1');
}
}
phpseclib/phpseclib/Crypt/DSA.php000064400000022205151161424250012711
0ustar00<?php
/**
* Pure-PHP FIPS 186-4 compliant implementation of DSA.
*
* PHP version 5
*
* Here's an example of how to create signatures and verify signatures
with this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $private = \phpseclib3\Crypt\DSA::createKey();
* $public = $private->getPublicKey();
*
* $plaintext = 'terrafrost';
*
* $signature = $private->sign($plaintext);
*
* echo $public->verify($plaintext, $signature) ? 'verified' :
'unverified';
* ?>
* </code>
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt;
use phpseclib3\Crypt\Common\AsymmetricKey;
use phpseclib3\Crypt\DSA\Parameters;
use phpseclib3\Crypt\DSA\PrivateKey;
use phpseclib3\Crypt\DSA\PublicKey;
use phpseclib3\Exception\InsufficientSetupException;
use phpseclib3\Math\BigInteger;
/**
* Pure-PHP FIPS 186-4 compliant implementation of DSA.
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class DSA extends AsymmetricKey
{
/**
* Algorithm Name
*
* @var string
*/
const ALGORITHM = 'DSA';
/**
* DSA Prime P
*
* @var \phpseclib3\Math\BigInteger
*/
protected $p;
/**
* DSA Group Order q
*
* Prime divisor of p-1
*
* @var \phpseclib3\Math\BigInteger
*/
protected $q;
/**
* DSA Group Generator G
*
* @var \phpseclib3\Math\BigInteger
*/
protected $g;
/**
* DSA public key value y
*
* @var \phpseclib3\Math\BigInteger
*/
protected $y;
/**
* Signature Format
*
* @var string
*/
protected $sigFormat;
/**
* Signature Format (Short)
*
* @var string
*/
protected $shortFormat;
/**
* Create DSA parameters
*
* @param int $L
* @param int $N
* @return \phpseclib3\Crypt\DSA|bool
*/
public static function createParameters($L = 2048, $N = 224)
{
self::initialize_static_variables();
$class = new \ReflectionClass(static::class);
if ($class->isFinal()) {
throw new \RuntimeException('createParameters() should not
be called from final classes (' . static::class . ')');
}
if (!isset(self::$engines['PHP'])) {
self::useBestEngine();
}
switch (true) {
case $N == 160:
/*
in FIPS 186-1 and 186-2 N was fixed at 160 whereas K had an
upper bound of 1024.
RFC 4253 (SSH Transport Layer Protocol) references FIPS 186-2
and as such most
SSH DSA implementations only support keys with an N of 160.
puttygen let's you set the size of L (but not the size
of N) and uses 2048 as the
default L value. that's not really compliant with any of
the FIPS standards, however,
for the purposes of maintaining compatibility with puttygen,
we'll support it
*/
//case ($L >= 512 || $L <= 1024) && (($L &
0x3F) == 0) && $N == 160:
// FIPS 186-3 changed this as follows:
//case $L == 1024 && $N == 160:
case $L == 2048 && $N == 224:
case $L == 2048 && $N == 256:
case $L == 3072 && $N == 256:
break;
default:
throw new \InvalidArgumentException('Invalid values
for N and L');
}
$two = new BigInteger(2);
$q = BigInteger::randomPrime($N);
$divisor = $q->multiply($two);
do {
$x = BigInteger::random($L);
list(, $c) = $x->divide($divisor);
$p = $x->subtract($c->subtract(self::$one));
} while ($p->getLength() != $L || !$p->isPrime());
$p_1 = $p->subtract(self::$one);
list($e) = $p_1->divide($q);
// quoting
http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf#page=50 ,
// "h could be obtained from a random number generator or from
a counter that
// changes after each use". PuTTY (sshdssg.c) starts h off at
1 and increments
// it on each loop. wikipedia says "commonly h = 2 is
used" so we'll just do that
$h = clone $two;
while (true) {
$g = $h->powMod($e, $p);
if (!$g->equals(self::$one)) {
break;
}
$h = $h->add(self::$one);
}
$dsa = new Parameters();
$dsa->p = $p;
$dsa->q = $q;
$dsa->g = $g;
return $dsa;
}
/**
* Create public / private key pair.
*
* This method is a bit polymorphic. It can take a DSA/Parameters
object, L / N as two distinct parameters or
* no parameters (at which point L and N will be generated with this
method)
*
* Returns the private key, from which the publickey can be extracted
*
* @param int[] ...$args
* @return DSA\PrivateKey
*/
public static function createKey(...$args)
{
self::initialize_static_variables();
$class = new \ReflectionClass(static::class);
if ($class->isFinal()) {
throw new \RuntimeException('createKey() should not be
called from final classes (' . static::class . ')');
}
if (!isset(self::$engines['PHP'])) {
self::useBestEngine();
}
if (count($args) == 2 && is_int($args[0]) &&
is_int($args[1])) {
$params = self::createParameters($args[0], $args[1]);
} elseif (count($args) == 1 && $args[0] instanceof
Parameters) {
$params = $args[0];
} elseif (!count($args)) {
$params = self::createParameters();
} else {
throw new InsufficientSetupException('Valid parameters are
either two integers (L and N), a single DSA object or no parameters at
all.');
}
$private = new PrivateKey();
$private->p = $params->p;
$private->q = $params->q;
$private->g = $params->g;
$private->x = BigInteger::randomRange(self::$one,
$private->q->subtract(self::$one));
$private->y = $private->g->powMod($private->x,
$private->p);
//$public = clone $private;
//unset($public->x);
return $private
->withHash($params->hash->getHash())
->withSignatureFormat($params->shortFormat);
}
/**
* OnLoad Handler
*
* @return bool
*/
protected static function onLoad(array $components)
{
if (!isset(self::$engines['PHP'])) {
self::useBestEngine();
}
if (!isset($components['x']) &&
!isset($components['y'])) {
$new = new Parameters();
} elseif (isset($components['x'])) {
$new = new PrivateKey();
$new->x = $components['x'];
} else {
$new = new PublicKey();
}
$new->p = $components['p'];
$new->q = $components['q'];
$new->g = $components['g'];
if (isset($components['y'])) {
$new->y = $components['y'];
}
return $new;
}
/**
* Constructor
*
* PublicKey and PrivateKey objects can only be created from abstract
RSA class
*/
protected function __construct()
{
$this->sigFormat = self::validatePlugin('Signature',
'ASN1');
$this->shortFormat = 'ASN1';
parent::__construct();
}
/**
* Returns the key size
*
* More specifically, this L (the length of DSA Prime P) and N (the
length of DSA Group Order q)
*
* @return array
*/
public function getLength()
{
return ['L' => $this->p->getLength(),
'N' => $this->q->getLength()];
}
/**
* Returns the current engine being used
*
* @see self::useInternalEngine()
* @see self::useBestEngine()
* @return string
*/
public function getEngine()
{
if (!isset(self::$engines['PHP'])) {
self::useBestEngine();
}
return self::$engines['OpenSSL'] &&
in_array($this->hash->getHash(), openssl_get_md_methods()) ?
'OpenSSL' : 'PHP';
}
/**
* Returns the parameters
*
* A public / private key is only returned if the currently loaded
"key" contains an x or y
* value.
*
* @see self::getPublicKey()
* @return mixed
*/
public function getParameters()
{
$type = self::validatePlugin('Keys', 'PKCS1',
'saveParameters');
$key = $type::saveParameters($this->p, $this->q,
$this->g);
return DSA::load($key, 'PKCS1')
->withHash($this->hash->getHash())
->withSignatureFormat($this->shortFormat);
}
/**
* Determines the signature padding mode
*
* Valid values are: ASN1, SSH2, Raw
*
* @param string $format
*/
public function withSignatureFormat($format)
{
$new = clone $this;
$new->shortFormat = $format;
$new->sigFormat = self::validatePlugin('Signature',
$format);
return $new;
}
/**
* Returns the signature format currently being used
*
*/
public function getSignatureFormat()
{
return $this->shortFormat;
}
}
phpseclib/phpseclib/Crypt/EC.php000064400000032167151161424250012601
0ustar00<?php
/**
* Pure-PHP implementation of EC.
*
* PHP version 5
*
* Here's an example of how to create signatures and verify signatures
with this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $private = \phpseclib3\Crypt\EC::createKey('secp256k1');
* $public = $private->getPublicKey();
*
* $plaintext = 'terrafrost';
*
* $signature = $private->sign($plaintext);
*
* echo $public->verify($plaintext, $signature) ? 'verified' :
'unverified';
* ?>
* </code>
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt;
use phpseclib3\Crypt\Common\AsymmetricKey;
use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
use phpseclib3\Crypt\EC\Curves\Curve25519;
use phpseclib3\Crypt\EC\Curves\Ed25519;
use phpseclib3\Crypt\EC\Curves\Ed448;
use phpseclib3\Crypt\EC\Formats\Keys\PKCS1;
use phpseclib3\Crypt\EC\Parameters;
use phpseclib3\Crypt\EC\PrivateKey;
use phpseclib3\Crypt\EC\PublicKey;
use phpseclib3\Exception\UnsupportedAlgorithmException;
use phpseclib3\Exception\UnsupportedCurveException;
use phpseclib3\Exception\UnsupportedOperationException;
use phpseclib3\File\ASN1;
use phpseclib3\File\ASN1\Maps\ECParameters;
use phpseclib3\Math\BigInteger;
/**
* Pure-PHP implementation of EC.
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class EC extends AsymmetricKey
{
/**
* Algorithm Name
*
* @var string
*/
const ALGORITHM = 'EC';
/**
* Public Key QA
*
* @var object[]
*/
protected $QA;
/**
* Curve
*
* @var \phpseclib3\Crypt\EC\BaseCurves\Base
*/
protected $curve;
/**
* Signature Format
*
* @var string
*/
protected $format;
/**
* Signature Format (Short)
*
* @var string
*/
protected $shortFormat;
/**
* Curve Name
*
* @var string
*/
private $curveName;
/**
* Curve Order
*
* Used for deterministic ECDSA
*
* @var \phpseclib3\Math\BigInteger
*/
protected $q;
/**
* Alias for the private key
*
* Used for deterministic ECDSA. AsymmetricKey expects $x. I don't
like x because
* with x you have x * the base point yielding an (x, y)-coordinate
that is the
* public key. But the x is different depending on which side of the
equal sign
* you're on. It's less ambiguous if you do dA * base point =
(x, y)-coordinate.
*
* @var \phpseclib3\Math\BigInteger
*/
protected $x;
/**
* Context
*
* @var string
*/
protected $context;
/**
* Signature Format
*
* @var string
*/
protected $sigFormat;
/**
* Create public / private key pair.
*
* @param string $curve
* @return \phpseclib3\Crypt\EC\PrivateKey
*/
public static function createKey($curve)
{
self::initialize_static_variables();
$class = new \ReflectionClass(static::class);
if ($class->isFinal()) {
throw new \RuntimeException('createKey() should not be
called from final classes (' . static::class . ')');
}
if (!isset(self::$engines['PHP'])) {
self::useBestEngine();
}
$curve = strtolower($curve);
if (self::$engines['libsodium'] && $curve ==
'ed25519' &&
function_exists('sodium_crypto_sign_keypair')) {
$kp = sodium_crypto_sign_keypair();
$privatekey = EC::loadFormat('libsodium',
sodium_crypto_sign_secretkey($kp));
//$publickey = EC::loadFormat('libsodium',
sodium_crypto_sign_publickey($kp));
$privatekey->curveName = 'Ed25519';
//$publickey->curveName = $curve;
return $privatekey;
}
$privatekey = new PrivateKey();
$curveName = $curve;
if (preg_match('#(?:^curve|^ed)\d+$#', $curveName)) {
$curveName = ucfirst($curveName);
} elseif (substr($curveName, 0, 10) == 'brainpoolp') {
$curveName = 'brainpoolP' . substr($curveName, 10);
}
$curve = '\phpseclib3\Crypt\EC\Curves\\' . $curveName;
if (!class_exists($curve)) {
throw new UnsupportedCurveException('Named Curve of '
. $curveName . ' is not supported');
}
$reflect = new \ReflectionClass($curve);
$curveName = $reflect->isFinal() ?
$reflect->getParentClass()->getShortName() :
$reflect->getShortName();
$curve = new $curve();
if ($curve instanceof TwistedEdwardsCurve) {
$arr = $curve->extractSecret(Random::string($curve
instanceof Ed448 ? 57 : 32));
$privatekey->dA = $dA = $arr['dA'];
$privatekey->secret = $arr['secret'];
} else {
$privatekey->dA = $dA = $curve->createRandomMultiplier();
}
if ($curve instanceof Curve25519 &&
self::$engines['libsodium']) {
//$r = pack('H*',
'0900000000000000000000000000000000000000000000000000000000000000');
//$QA = sodium_crypto_scalarmult($dA->toBytes(), $r);
$QA =
sodium_crypto_box_publickey_from_secretkey($dA->toBytes());
$privatekey->QA = [$curve->convertInteger(new
BigInteger(strrev($QA), 256))];
} else {
$privatekey->QA =
$curve->multiplyPoint($curve->getBasePoint(), $dA);
}
$privatekey->curve = $curve;
//$publickey = clone $privatekey;
//unset($publickey->dA);
//unset($publickey->x);
$privatekey->curveName = $curveName;
//$publickey->curveName = $curveName;
if ($privatekey->curve instanceof TwistedEdwardsCurve) {
return $privatekey->withHash($curve::HASH);
}
return $privatekey;
}
/**
* OnLoad Handler
*
* @return bool
*/
protected static function onLoad(array $components)
{
if (!isset(self::$engines['PHP'])) {
self::useBestEngine();
}
if (!isset($components['dA']) &&
!isset($components['QA'])) {
$new = new Parameters();
$new->curve = $components['curve'];
return $new;
}
$new = isset($components['dA']) ?
new PrivateKey() :
new PublicKey();
$new->curve = $components['curve'];
$new->QA = $components['QA'];
if (isset($components['dA'])) {
$new->dA = $components['dA'];
$new->secret = $components['secret'];
}
if ($new->curve instanceof TwistedEdwardsCurve) {
return $new->withHash($components['curve']::HASH);
}
return $new;
}
/**
* Constructor
*
* PublicKey and PrivateKey objects can only be created from abstract
RSA class
*/
protected function __construct()
{
$this->sigFormat = self::validatePlugin('Signature',
'ASN1');
$this->shortFormat = 'ASN1';
parent::__construct();
}
/**
* Returns the curve
*
* Returns a string if it's a named curve, an array if not
*
* @return string|array
*/
public function getCurve()
{
if ($this->curveName) {
return $this->curveName;
}
if ($this->curve instanceof MontgomeryCurve) {
$this->curveName = $this->curve instanceof Curve25519 ?
'Curve25519' : 'Curve448';
return $this->curveName;
}
if ($this->curve instanceof TwistedEdwardsCurve) {
$this->curveName = $this->curve instanceof Ed25519 ?
'Ed25519' : 'Ed448';
return $this->curveName;
}
$params = $this->getParameters()->toString('PKCS8',
['namedCurve' => true]);
$decoded = ASN1::extractBER($params);
$decoded = ASN1::decodeBER($decoded);
$decoded = ASN1::asn1map($decoded[0], ECParameters::MAP);
if (isset($decoded['namedCurve'])) {
$this->curveName = $decoded['namedCurve'];
return $decoded['namedCurve'];
}
if (!$namedCurves) {
PKCS1::useSpecifiedCurve();
}
return $decoded;
}
/**
* Returns the key size
*
* Quoting https://tools.ietf.org/html/rfc5656#section-2,
*
* "The size of a set of elliptic curve domain parameters on a
prime
* curve is defined as the number of bits in the binary representation
* of the field order, commonly denoted by p. Size on a
* characteristic-2 curve is defined as the number of bits in the
binary
* representation of the field, commonly denoted by m. A set of
* elliptic curve domain parameters defines a group of order n
generated
* by a base point P"
*
* @return int
*/
public function getLength()
{
return $this->curve->getLength();
}
/**
* Returns the current engine being used
*
* @see self::useInternalEngine()
* @see self::useBestEngine()
* @return string
*/
public function getEngine()
{
if (!isset(self::$engines['PHP'])) {
self::useBestEngine();
}
if ($this->curve instanceof TwistedEdwardsCurve) {
return $this->curve instanceof Ed25519 &&
self::$engines['libsodium'] && !isset($this->context)
?
'libsodium' : 'PHP';
}
return self::$engines['OpenSSL'] &&
in_array($this->hash->getHash(), openssl_get_md_methods()) ?
'OpenSSL' : 'PHP';
}
/**
* Returns the public key coordinates as a string
*
* Used by ECDH
*
* @return string
*/
public function getEncodedCoordinates()
{
if ($this->curve instanceof MontgomeryCurve) {
return strrev($this->QA[0]->toBytes(true));
}
if ($this->curve instanceof TwistedEdwardsCurve) {
return $this->curve->encodePoint($this->QA);
}
return "\4" . $this->QA[0]->toBytes(true) .
$this->QA[1]->toBytes(true);
}
/**
* Returns the parameters
*
* @see self::getPublicKey()
* @param string $type optional
* @return mixed
*/
public function getParameters($type = 'PKCS1')
{
$type = self::validatePlugin('Keys', $type,
'saveParameters');
$key = $type::saveParameters($this->curve);
return EC::load($key, 'PKCS1')
->withHash($this->hash->getHash())
->withSignatureFormat($this->shortFormat);
}
/**
* Determines the signature padding mode
*
* Valid values are: ASN1, SSH2, Raw
*
* @param string $format
*/
public function withSignatureFormat($format)
{
if ($this->curve instanceof MontgomeryCurve) {
throw new UnsupportedOperationException('Montgomery Curves
cannot be used to create signatures');
}
$new = clone $this;
$new->shortFormat = $format;
$new->sigFormat = self::validatePlugin('Signature',
$format);
return $new;
}
/**
* Returns the signature format currently being used
*
*/
public function getSignatureFormat()
{
return $this->shortFormat;
}
/**
* Sets the context
*
* Used by Ed25519 / Ed448.
*
* @see self::sign()
* @see self::verify()
* @param string $context optional
*/
public function withContext($context = null)
{
if (!$this->curve instanceof TwistedEdwardsCurve) {
throw new UnsupportedCurveException('Only Ed25519 and
Ed448 support contexts');
}
$new = clone $this;
if (!isset($context)) {
$new->context = null;
return $new;
}
if (!is_string($context)) {
throw new \InvalidArgumentException('setContext expects a
string');
}
if (strlen($context) > 255) {
throw new \LengthException('The context is supposed to be,
at most, 255 bytes long');
}
$new->context = $context;
return $new;
}
/**
* Returns the signature format currently being used
*
*/
public function getContext()
{
return $this->context;
}
/**
* Determines which hashing function should be used
*
* @param string $hash
*/
public function withHash($hash)
{
if ($this->curve instanceof MontgomeryCurve) {
throw new UnsupportedOperationException('Montgomery Curves
cannot be used to create signatures');
}
if ($this->curve instanceof Ed25519 && $hash !=
'sha512') {
throw new UnsupportedAlgorithmException('Ed25519 only
supports sha512 as a hash');
}
if ($this->curve instanceof Ed448 && $hash !=
'shake256-912') {
throw new UnsupportedAlgorithmException('Ed448 only
supports shake256 with a length of 114 bytes');
}
return parent::withHash($hash);
}
/**
* __toString() magic method
*
* @return string
*/
public function __toString()
{
if ($this->curve instanceof MontgomeryCurve) {
return '';
}
return parent::__toString();
}
}
phpseclib/phpseclib/Crypt/PublicKeyLoader.php000064400000005221151161424250015317
0ustar00<?php
/**
* PublicKeyLoader
*
* Returns a PublicKey or PrivateKey object.
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2009 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt;
use phpseclib3\Crypt\Common\AsymmetricKey;
use phpseclib3\Crypt\Common\PrivateKey;
use phpseclib3\Crypt\Common\PublicKey;
use phpseclib3\Exception\NoKeyLoadedException;
use phpseclib3\File\X509;
/**
* PublicKeyLoader
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PublicKeyLoader
{
/**
* Loads a public or private key
*
* @return AsymmetricKey
* @param string|array $key
* @param string $password optional
*/
public static function load($key, $password = false)
{
try {
return EC::load($key, $password);
} catch (NoKeyLoadedException $e) {
}
try {
return RSA::load($key, $password);
} catch (NoKeyLoadedException $e) {
}
try {
return DSA::load($key, $password);
} catch (NoKeyLoadedException $e) {
}
try {
$x509 = new X509();
$x509->loadX509($key);
$key = $x509->getPublicKey();
if ($key) {
return $key;
}
} catch (\Exception $e) {
}
throw new NoKeyLoadedException('Unable to read key');
}
/**
* Loads a private key
*
* @return PrivateKey
* @param string|array $key
* @param string $password optional
*/
public static function loadPrivateKey($key, $password = false)
{
$key = self::load($key, $password);
if (!$key instanceof PrivateKey) {
throw new NoKeyLoadedException('The key that was loaded
was not a private key');
}
return $key;
}
/**
* Loads a public key
*
* @return PublicKey
* @param string|array $key
*/
public static function loadPublicKey($key)
{
$key = self::load($key);
if (!$key instanceof PublicKey) {
throw new NoKeyLoadedException('The key that was loaded
was not a public key');
}
return $key;
}
/**
* Loads parameters
*
* @return AsymmetricKey
* @param string|array $key
*/
public static function loadParameters($key)
{
$key = self::load($key);
if (!$key instanceof PrivateKey && !$key instanceof
PublicKey) {
throw new NoKeyLoadedException('The key that was loaded
was not a parameter');
}
return $key;
}
}
phpseclib/phpseclib/Crypt/RSA/Formats/Keys/JWK.php000064400000011007151161424250015706
0ustar00<?php
/**
* JSON Web Key (RFC7517) Formatted RSA Handler
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\RSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\JWK as Progenitor;
use phpseclib3\Math\BigInteger;
/**
* JWK Formatted RSA Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class JWK extends Progenitor
{
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
$key = parent::load($key, $password);
if ($key->kty != 'RSA') {
throw new \RuntimeException('Only RSA JWK keys are
supported');
}
$count = $publicCount = 0;
$vars = ['n', 'e', 'd',
'p', 'q', 'dp', 'dq',
'qi'];
foreach ($vars as $var) {
if (!isset($key->$var) || !is_string($key->$var)) {
continue;
}
$count++;
$value = new
BigInteger(Strings::base64url_decode($key->$var), 256);
switch ($var) {
case 'n':
$publicCount++;
$components['modulus'] = $value;
break;
case 'e':
$publicCount++;
$components['publicExponent'] = $value;
break;
case 'd':
$components['privateExponent'] = $value;
break;
case 'p':
$components['primes'][1] = $value;
break;
case 'q':
$components['primes'][2] = $value;
break;
case 'dp':
$components['exponents'][1] = $value;
break;
case 'dq':
$components['exponents'][2] = $value;
break;
case 'qi':
$components['coefficients'][2] = $value;
}
}
if ($count == count($vars)) {
return $components + ['isPublicKey' => false];
}
if ($count == 2 && $publicCount == 2) {
return $components + ['isPublicKey' => true];
}
throw new \UnexpectedValueException('Key does not have an
appropriate number of RSA parameters');
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $n
* @param \phpseclib3\Math\BigInteger $e
* @param \phpseclib3\Math\BigInteger $d
* @param array $primes
* @param array $exponents
* @param array $coefficients
* @param string $password optional
* @param array $options optional
* @return string
*/
public static function savePrivateKey(BigInteger $n, BigInteger $e,
BigInteger $d, array $primes, array $exponents, array $coefficients,
$password = '', array $options = [])
{
if (count($primes) != 2) {
throw new \InvalidArgumentException('JWK does not support
multi-prime RSA keys');
}
$key = [
'kty' => 'RSA',
'n' =>
Strings::base64url_encode($n->toBytes()),
'e' =>
Strings::base64url_encode($e->toBytes()),
'd' =>
Strings::base64url_encode($d->toBytes()),
'p' =>
Strings::base64url_encode($primes[1]->toBytes()),
'q' =>
Strings::base64url_encode($primes[2]->toBytes()),
'dp' =>
Strings::base64url_encode($exponents[1]->toBytes()),
'dq' =>
Strings::base64url_encode($exponents[2]->toBytes()),
'qi' =>
Strings::base64url_encode($coefficients[2]->toBytes())
];
return self::wrapKey($key, $options);
}
/**
* Convert a public key to the appropriate format
*
* @param \phpseclib3\Math\BigInteger $n
* @param \phpseclib3\Math\BigInteger $e
* @param array $options optional
* @return string
*/
public static function savePublicKey(BigInteger $n, BigInteger $e,
array $options = [])
{
$key = [
'kty' => 'RSA',
'n' =>
Strings::base64url_encode($n->toBytes()),
'e' => Strings::base64url_encode($e->toBytes())
];
return self::wrapKey($key, $options);
}
}
phpseclib/phpseclib/Crypt/RSA/Formats/Keys/MSBLOB.php000064400000016553151161424250016244
0ustar00<?php
/**
* Miccrosoft BLOB Formatted RSA Key Handler
*
* More info:
*
*
https://msdn.microsoft.com/en-us/library/windows/desktop/aa375601(v=vs.85).aspx
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\RSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Exception\UnsupportedFormatException;
use phpseclib3\Math\BigInteger;
/**
* Microsoft BLOB Formatted RSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class MSBLOB
{
/**
* Public/Private Key Pair
*
*/
const PRIVATEKEYBLOB = 0x7;
/**
* Public Key
*
*/
const PUBLICKEYBLOB = 0x6;
/**
* Public Key
*
*/
const PUBLICKEYBLOBEX = 0xA;
/**
* RSA public key exchange algorithm
*
*/
const CALG_RSA_KEYX = 0x0000A400;
/**
* RSA public key exchange algorithm
*
*/
const CALG_RSA_SIGN = 0x00002400;
/**
* Public Key
*
*/
const RSA1 = 0x31415352;
/**
* Private Key
*
*/
const RSA2 = 0x32415352;
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
if (!Strings::is_stringable($key)) {
throw new \UnexpectedValueException('Key should be a
string - not a ' . gettype($key));
}
$key = Strings::base64_decode($key);
if (!is_string($key)) {
throw new \UnexpectedValueException('Base64 decoding
produced an error');
}
if (strlen($key) < 20) {
throw new \UnexpectedValueException('Key appears to be
malformed');
}
// PUBLICKEYSTRUC publickeystruc
//
https://msdn.microsoft.com/en-us/library/windows/desktop/aa387453(v=vs.85).aspx
extract(unpack('atype/aversion/vreserved/Valgo',
Strings::shift($key, 8)));
/**
* @var string $type
* @var string $version
* @var integer $reserved
* @var integer $algo
*/
switch (ord($type)) {
case self::PUBLICKEYBLOB:
case self::PUBLICKEYBLOBEX:
$publickey = true;
break;
case self::PRIVATEKEYBLOB:
$publickey = false;
break;
default:
throw new \UnexpectedValueException('Key appears to be
malformed');
}
$components = ['isPublicKey' => $publickey];
//
https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx
switch ($algo) {
case self::CALG_RSA_KEYX:
case self::CALG_RSA_SIGN:
break;
default:
throw new \UnexpectedValueException('Key appears to be
malformed');
}
// RSAPUBKEY rsapubkey
//
https://msdn.microsoft.com/en-us/library/windows/desktop/aa387685(v=vs.85).aspx
// could do V for pubexp but that's unsigned 32-bit whereas
some PHP installs only do signed 32-bit
extract(unpack('Vmagic/Vbitlen/a4pubexp',
Strings::shift($key, 12)));
/**
* @var integer $magic
* @var integer $bitlen
* @var string $pubexp
*/
switch ($magic) {
case self::RSA2:
$components['isPublicKey'] = false;
// fall-through
case self::RSA1:
break;
default:
throw new \UnexpectedValueException('Key appears to be
malformed');
}
$baseLength = $bitlen / 16;
if (strlen($key) != 2 * $baseLength && strlen($key) != 9 *
$baseLength) {
throw new \UnexpectedValueException('Key appears to be
malformed');
}
$components[$components['isPublicKey'] ?
'publicExponent' : 'privateExponent'] = new
BigInteger(strrev($pubexp), 256);
// BYTE modulus[rsapubkey.bitlen/8]
$components['modulus'] = new
BigInteger(strrev(Strings::shift($key, $bitlen / 8)), 256);
if ($publickey) {
return $components;
}
$components['isPublicKey'] = false;
// BYTE prime1[rsapubkey.bitlen/16]
$components['primes'] = [1 => new
BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256)];
// BYTE prime2[rsapubkey.bitlen/16]
$components['primes'][] = new
BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256);
// BYTE exponent1[rsapubkey.bitlen/16]
$components['exponents'] = [1 => new
BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256)];
// BYTE exponent2[rsapubkey.bitlen/16]
$components['exponents'][] = new
BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256);
// BYTE coefficient[rsapubkey.bitlen/16]
$components['coefficients'] = [2 => new
BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256)];
if (isset($components['privateExponent'])) {
$components['publicExponent'] =
$components['privateExponent'];
}
// BYTE privateExponent[rsapubkey.bitlen/8]
$components['privateExponent'] = new
BigInteger(strrev(Strings::shift($key, $bitlen / 8)), 256);
return $components;
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $n
* @param \phpseclib3\Math\BigInteger $e
* @param \phpseclib3\Math\BigInteger $d
* @param array $primes
* @param array $exponents
* @param array $coefficients
* @param string $password optional
* @return string
*/
public static function savePrivateKey(BigInteger $n, BigInteger $e,
BigInteger $d, array $primes, array $exponents, array $coefficients,
$password = '')
{
if (count($primes) != 2) {
throw new \InvalidArgumentException('MSBLOB does not
support multi-prime RSA keys');
}
if (!empty($password) && is_string($password)) {
throw new UnsupportedFormatException('MSBLOB private keys
do not support encryption');
}
$n = strrev($n->toBytes());
$e = str_pad(strrev($e->toBytes()), 4, "\0");
$key = pack('aavV', chr(self::PRIVATEKEYBLOB), chr(2), 0,
self::CALG_RSA_KEYX);
$key .= pack('VVa*', self::RSA2, 8 * strlen($n), $e);
$key .= $n;
$key .= strrev($primes[1]->toBytes());
$key .= strrev($primes[2]->toBytes());
$key .= strrev($exponents[1]->toBytes());
$key .= strrev($exponents[2]->toBytes());
$key .= strrev($coefficients[2]->toBytes());
$key .= strrev($d->toBytes());
return Strings::base64_encode($key);
}
/**
* Convert a public key to the appropriate format
*
* @param \phpseclib3\Math\BigInteger $n
* @param \phpseclib3\Math\BigInteger $e
* @return string
*/
public static function savePublicKey(BigInteger $n, BigInteger $e)
{
$n = strrev($n->toBytes());
$e = str_pad(strrev($e->toBytes()), 4, "\0");
$key = pack('aavV', chr(self::PUBLICKEYBLOB), chr(2), 0,
self::CALG_RSA_KEYX);
$key .= pack('VVa*', self::RSA1, 8 * strlen($n), $e);
$key .= $n;
return Strings::base64_encode($key);
}
}
phpseclib/phpseclib/Crypt/RSA/Formats/Keys/OpenSSH.php000064400000007771151161424250016547
0ustar00<?php
/**
* OpenSSH Formatted RSA Key Handler
*
* PHP version 5
*
* Place in $HOME/.ssh/authorized_keys
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\RSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\OpenSSH as Progenitor;
use phpseclib3\Math\BigInteger;
/**
* OpenSSH Formatted RSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class OpenSSH extends Progenitor
{
/**
* Supported Key Types
*
* @var array
*/
protected static $types = ['ssh-rsa'];
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
static $one;
if (!isset($one)) {
$one = new BigInteger(1);
}
$parsed = parent::load($key, $password);
if (isset($parsed['paddedKey'])) {
list($type) = Strings::unpackSSH2('s',
$parsed['paddedKey']);
if ($type != $parsed['type']) {
throw new \RuntimeException("The public and private
keys are not of the same type ($type vs $parsed[type])");
}
$primes = $coefficients = [];
list(
$modulus,
$publicExponent,
$privateExponent,
$coefficients[2],
$primes[1],
$primes[2],
$comment,
) = Strings::unpackSSH2('i6s',
$parsed['paddedKey']);
$temp = $primes[1]->subtract($one);
$exponents = [1 => $publicExponent->modInverse($temp)];
$temp = $primes[2]->subtract($one);
$exponents[] = $publicExponent->modInverse($temp);
$isPublicKey = false;
return compact('publicExponent', 'modulus',
'privateExponent', 'primes', 'coefficients',
'exponents', 'comment', 'isPublicKey');
}
list($publicExponent, $modulus) =
Strings::unpackSSH2('ii', $parsed['publicKey']);
return [
'isPublicKey' => true,
'modulus' => $modulus,
'publicExponent' => $publicExponent,
'comment' => $parsed['comment']
];
}
/**
* Convert a public key to the appropriate format
*
* @param \phpseclib3\Math\BigInteger $n
* @param \phpseclib3\Math\BigInteger $e
* @param array $options optional
* @return string
*/
public static function savePublicKey(BigInteger $n, BigInteger $e,
array $options = [])
{
$RSAPublicKey = Strings::packSSH2('sii',
'ssh-rsa', $e, $n);
if (isset($options['binary']) ?
$options['binary'] : self::$binary) {
return $RSAPublicKey;
}
$comment = isset($options['comment']) ?
$options['comment'] : self::$comment;
$RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey)
. ' ' . $comment;
return $RSAPublicKey;
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $n
* @param \phpseclib3\Math\BigInteger $e
* @param \phpseclib3\Math\BigInteger $d
* @param array $primes
* @param array $exponents
* @param array $coefficients
* @param string $password optional
* @param array $options optional
* @return string
*/
public static function savePrivateKey(BigInteger $n, BigInteger $e,
BigInteger $d, array $primes, array $exponents, array $coefficients,
$password = '', array $options = [])
{
$publicKey = self::savePublicKey($n, $e, ['binary' =>
true]);
$privateKey = Strings::packSSH2('si6',
'ssh-rsa', $n, $e, $d, $coefficients[2], $primes[1], $primes[2]);
return self::wrapPrivateKey($publicKey, $privateKey, $password,
$options);
}
}
phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS1.php000064400000011612151161424250016076
0ustar00<?php
/**
* PKCS#1 Formatted RSA Key Handler
*
* PHP version 5
*
* Used by File/X509.php
*
* Processes keys with the following headers:
*
* -----BEGIN RSA PRIVATE KEY-----
* -----BEGIN RSA PUBLIC KEY-----
*
* Analogous to ssh-keygen's pem format (as specified by -m)
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\RSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
use phpseclib3\File\ASN1;
use phpseclib3\File\ASN1\Maps;
use phpseclib3\Math\BigInteger;
/**
* PKCS#1 Formatted RSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PKCS1 extends Progenitor
{
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
if (!Strings::is_stringable($key)) {
throw new \UnexpectedValueException('Key should be a
string - not a ' . gettype($key));
}
if (strpos($key, 'PUBLIC') !== false) {
$components = ['isPublicKey' => true];
} elseif (strpos($key, 'PRIVATE') !== false) {
$components = ['isPublicKey' => false];
} else {
$components = [];
}
$key = parent::load($key, $password);
$decoded = ASN1::decodeBER($key);
if (!$decoded) {
throw new \RuntimeException('Unable to decode BER');
}
$key = ASN1::asn1map($decoded[0], Maps\RSAPrivateKey::MAP);
if (is_array($key)) {
$components += [
'modulus' => $key['modulus'],
'publicExponent' =>
$key['publicExponent'],
'privateExponent' =>
$key['privateExponent'],
'primes' => [1 => $key['prime1'],
$key['prime2']],
'exponents' => [1 =>
$key['exponent1'], $key['exponent2']],
'coefficients' => [2 =>
$key['coefficient']]
];
if ($key['version'] == 'multi') {
foreach ($key['otherPrimeInfos'] as $primeInfo) {
$components['primes'][] =
$primeInfo['prime'];
$components['exponents'][] =
$primeInfo['exponent'];
$components['coefficients'][] =
$primeInfo['coefficient'];
}
}
if (!isset($components['isPublicKey'])) {
$components['isPublicKey'] = false;
}
return $components;
}
$key = ASN1::asn1map($decoded[0], Maps\RSAPublicKey::MAP);
if (!is_array($key)) {
throw new \RuntimeException('Unable to perform ASN1
mapping');
}
if (!isset($components['isPublicKey'])) {
$components['isPublicKey'] = true;
}
return $components + $key;
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $n
* @param \phpseclib3\Math\BigInteger $e
* @param \phpseclib3\Math\BigInteger $d
* @param array $primes
* @param array $exponents
* @param array $coefficients
* @param string $password optional
* @param array $options optional
* @return string
*/
public static function savePrivateKey(BigInteger $n, BigInteger $e,
BigInteger $d, array $primes, array $exponents, array $coefficients,
$password = '', array $options = [])
{
$num_primes = count($primes);
$key = [
'version' => $num_primes == 2 ?
'two-prime' : 'multi',
'modulus' => $n,
'publicExponent' => $e,
'privateExponent' => $d,
'prime1' => $primes[1],
'prime2' => $primes[2],
'exponent1' => $exponents[1],
'exponent2' => $exponents[2],
'coefficient' => $coefficients[2]
];
for ($i = 3; $i <= $num_primes; $i++) {
$key['otherPrimeInfos'][] = [
'prime' => $primes[$i],
'exponent' => $exponents[$i],
'coefficient' => $coefficients[$i]
];
}
$key = ASN1::encodeDER($key, Maps\RSAPrivateKey::MAP);
return self::wrapPrivateKey($key, 'RSA', $password,
$options);
}
/**
* Convert a public key to the appropriate format
*
* @param \phpseclib3\Math\BigInteger $n
* @param \phpseclib3\Math\BigInteger $e
* @return string
*/
public static function savePublicKey(BigInteger $n, BigInteger $e)
{
$key = [
'modulus' => $n,
'publicExponent' => $e
];
$key = ASN1::encodeDER($key, Maps\RSAPublicKey::MAP);
return self::wrapPublicKey($key, 'RSA');
}
}
phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS8.php000064400000006445151161424250016115
0ustar00<?php
/**
* PKCS#8 Formatted RSA Key Handler
*
* PHP version 5
*
* Used by PHP's openssl_public_encrypt() and openssl's rsautl
(when -pubin is set)
*
* Processes keys with the following headers:
*
* -----BEGIN ENCRYPTED PRIVATE KEY-----
* -----BEGIN PRIVATE KEY-----
* -----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)
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\RSA\Formats\Keys;
use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
use phpseclib3\File\ASN1;
use phpseclib3\Math\BigInteger;
/**
* PKCS#8 Formatted RSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PKCS8 extends Progenitor
{
/**
* OID Name
*
* @var string
*/
const OID_NAME = 'rsaEncryption';
/**
* OID Value
*
* @var string
*/
const OID_VALUE = '1.2.840.113549.1.1.1';
/**
* Child OIDs loaded
*
* @var bool
*/
protected static $childOIDsLoaded = false;
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
$key = parent::load($key, $password);
if (isset($key['privateKey'])) {
$components['isPublicKey'] = false;
$type = 'private';
} else {
$components['isPublicKey'] = true;
$type = 'public';
}
$result = $components + PKCS1::load($key[$type . 'Key']);
if (isset($key['meta'])) {
$result['meta'] = $key['meta'];
}
return $result;
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $n
* @param \phpseclib3\Math\BigInteger $e
* @param \phpseclib3\Math\BigInteger $d
* @param array $primes
* @param array $exponents
* @param array $coefficients
* @param string $password optional
* @param array $options optional
* @return string
*/
public static function savePrivateKey(BigInteger $n, BigInteger $e,
BigInteger $d, array $primes, array $exponents, array $coefficients,
$password = '', array $options = [])
{
$key = PKCS1::savePrivateKey($n, $e, $d, $primes, $exponents,
$coefficients);
$key = ASN1::extractBER($key);
return self::wrapPrivateKey($key, [], null, $password, null,
'', $options);
}
/**
* Convert a public key to the appropriate format
*
* @param \phpseclib3\Math\BigInteger $n
* @param \phpseclib3\Math\BigInteger $e
* @param array $options optional
* @return string
*/
public static function savePublicKey(BigInteger $n, BigInteger $e,
array $options = [])
{
$key = PKCS1::savePublicKey($n, $e);
$key = ASN1::extractBER($key);
return self::wrapPublicKey($key, null);
}
}
phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PSS.php000064400000017053151161424250015727
0ustar00<?php
/**
* PKCS#8 Formatted RSA-PSS Key Handler
*
* PHP version 5
*
* Used by PHP's openssl_public_encrypt() and openssl's rsautl
(when -pubin is set)
*
* Processes keys with the following headers:
*
* -----BEGIN ENCRYPTED PRIVATE KEY-----
* -----BEGIN PRIVATE KEY-----
* -----BEGIN PUBLIC KEY-----
*
* Analogous to "openssl genpkey -algorithm rsa-pss".
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\RSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
use phpseclib3\File\ASN1;
use phpseclib3\File\ASN1\Maps;
use phpseclib3\Math\BigInteger;
/**
* PKCS#8 Formatted RSA-PSS Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PSS extends Progenitor
{
/**
* OID Name
*
* @var string
*/
const OID_NAME = 'id-RSASSA-PSS';
/**
* OID Value
*
* @var string
*/
const OID_VALUE = '1.2.840.113549.1.1.10';
/**
* OIDs loaded
*
* @var bool
*/
private static $oidsLoaded = false;
/**
* Child OIDs loaded
*
* @var bool
*/
protected static $childOIDsLoaded = false;
/**
* Initialize static variables
*/
private static function initialize_static_variables()
{
if (!self::$oidsLoaded) {
ASN1::loadOIDs([
'md2' => '1.2.840.113549.2.2',
'md4' => '1.2.840.113549.2.4',
'md5' => '1.2.840.113549.2.5',
'id-sha1' => '1.3.14.3.2.26',
'id-sha256' =>
'2.16.840.1.101.3.4.2.1',
'id-sha384' =>
'2.16.840.1.101.3.4.2.2',
'id-sha512' =>
'2.16.840.1.101.3.4.2.3',
'id-sha224' =>
'2.16.840.1.101.3.4.2.4',
'id-sha512/224' =>
'2.16.840.1.101.3.4.2.5',
'id-sha512/256' =>
'2.16.840.1.101.3.4.2.6',
'id-mgf1' => '1.2.840.113549.1.1.8'
]);
self::$oidsLoaded = true;
}
}
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
self::initialize_static_variables();
if (!Strings::is_stringable($key)) {
throw new \UnexpectedValueException('Key should be a
string - not a ' . gettype($key));
}
$components = ['isPublicKey' => strpos($key,
'PUBLIC') !== false];
$key = parent::load($key, $password);
$type = isset($key['privateKey']) ? 'private' :
'public';
$result = $components + PKCS1::load($key[$type . 'Key']);
if (isset($key[$type .
'KeyAlgorithm']['parameters'])) {
$decoded = ASN1::decodeBER($key[$type .
'KeyAlgorithm']['parameters']);
if ($decoded === false) {
throw new \UnexpectedValueException('Unable to decode
parameters');
}
$params = ASN1::asn1map($decoded[0],
Maps\RSASSA_PSS_params::MAP);
} else {
$params = [];
}
if
(isset($params['maskGenAlgorithm']['parameters'])) {
$decoded =
ASN1::decodeBER($params['maskGenAlgorithm']['parameters']);
if ($decoded === false) {
throw new \UnexpectedValueException('Unable to decode
parameters');
}
$params['maskGenAlgorithm']['parameters'] =
ASN1::asn1map($decoded[0], Maps\HashAlgorithm::MAP);
} else {
$params['maskGenAlgorithm'] = [
'algorithm' => 'id-mgf1',
'parameters' => ['algorithm' =>
'id-sha1']
];
}
if
(!isset($params['hashAlgorithm']['algorithm'])) {
$params['hashAlgorithm']['algorithm'] =
'id-sha1';
}
$result['hash'] = str_replace('id-',
'', $params['hashAlgorithm']['algorithm']);
$result['MGFHash'] = str_replace('id-',
'',
$params['maskGenAlgorithm']['parameters']['algorithm']);
if (isset($params['saltLength'])) {
$result['saltLength'] = (int)
$params['saltLength']->toString();
}
if (isset($key['meta'])) {
$result['meta'] = $key['meta'];
}
return $result;
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $n
* @param \phpseclib3\Math\BigInteger $e
* @param \phpseclib3\Math\BigInteger $d
* @param array $primes
* @param array $exponents
* @param array $coefficients
* @param string $password optional
* @param array $options optional
* @return string
*/
public static function savePrivateKey(BigInteger $n, BigInteger $e,
BigInteger $d, array $primes, array $exponents, array $coefficients,
$password = '', array $options = [])
{
self::initialize_static_variables();
$key = PKCS1::savePrivateKey($n, $e, $d, $primes, $exponents,
$coefficients);
$key = ASN1::extractBER($key);
$params = self::savePSSParams($options);
return self::wrapPrivateKey($key, [], $params, $password, null,
'', $options);
}
/**
* Convert a public key to the appropriate format
*
* @param \phpseclib3\Math\BigInteger $n
* @param \phpseclib3\Math\BigInteger $e
* @param array $options optional
* @return string
*/
public static function savePublicKey(BigInteger $n, BigInteger $e,
array $options = [])
{
self::initialize_static_variables();
$key = PKCS1::savePublicKey($n, $e);
$key = ASN1::extractBER($key);
$params = self::savePSSParams($options);
return self::wrapPublicKey($key, $params);
}
/**
* Encodes PSS parameters
*
* @param array $options
* @return string
*/
public static function savePSSParams(array $options)
{
/*
The trailerField field is an integer. It provides
compatibility with IEEE Std 1363a-2004 [P1363A]. The value
MUST be 1, which represents the trailer field with hexadecimal
value 0xBC. Other trailer fields, including the trailer field
composed of HashID concatenated with 0xCC that is specified in
IEEE Std 1363a, are not supported. Implementations that
perform signature generation MUST omit the trailerField field,
indicating that the default trailer field value was used.
Implementations that perform signature validation MUST
recognize both a present trailerField field with value 1 and an
absent trailerField field.
source: https://tools.ietf.org/html/rfc4055#page-9
*/
$params = [
'trailerField' => new BigInteger(1)
];
if (isset($options['hash'])) {
$params['hashAlgorithm']['algorithm'] =
'id-' . $options['hash'];
}
if (isset($options['MGFHash'])) {
$temp = ['algorithm' => 'id-' .
$options['MGFHash']];
$temp = ASN1::encodeDER($temp, Maps\HashAlgorithm::MAP);
$params['maskGenAlgorithm'] = [
'algorithm' => 'id-mgf1',
'parameters' => new ASN1\Element($temp)
];
}
if (isset($options['saltLength'])) {
$params['saltLength'] = new
BigInteger($options['saltLength']);
}
return new ASN1\Element(ASN1::encodeDER($params,
Maps\RSASSA_PSS_params::MAP));
}
}
phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PuTTY.php000064400000007127151161424250016250
0ustar00<?php
/**
* PuTTY Formatted RSA Key Handler
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\RSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\PuTTY as Progenitor;
use phpseclib3\Math\BigInteger;
/**
* PuTTY Formatted RSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PuTTY extends Progenitor
{
/**
* Public Handler
*
* @var string
*/
const PUBLIC_HANDLER =
'phpseclib3\Crypt\RSA\Formats\Keys\OpenSSH';
/**
* Algorithm Identifier
*
* @var array
*/
protected static $types = ['ssh-rsa'];
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
static $one;
if (!isset($one)) {
$one = new BigInteger(1);
}
$components = parent::load($key, $password);
if (!isset($components['private'])) {
return $components;
}
extract($components);
unset($components['public'],
$components['private']);
$isPublicKey = false;
$result = Strings::unpackSSH2('ii', $public);
if ($result === false) {
throw new \UnexpectedValueException('Key appears to be
malformed');
}
list($publicExponent, $modulus) = $result;
$result = Strings::unpackSSH2('iiii', $private);
if ($result === false) {
throw new \UnexpectedValueException('Key appears to be
malformed');
}
$primes = $coefficients = [];
list($privateExponent, $primes[1], $primes[2], $coefficients[2]) =
$result;
$temp = $primes[1]->subtract($one);
$exponents = [1 => $publicExponent->modInverse($temp)];
$temp = $primes[2]->subtract($one);
$exponents[] = $publicExponent->modInverse($temp);
return compact('publicExponent', 'modulus',
'privateExponent', 'primes', 'coefficients',
'exponents', 'comment', 'isPublicKey');
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $n
* @param \phpseclib3\Math\BigInteger $e
* @param \phpseclib3\Math\BigInteger $d
* @param array $primes
* @param array $exponents
* @param array $coefficients
* @param string $password optional
* @param array $options optional
* @return string
*/
public static function savePrivateKey(BigInteger $n, BigInteger $e,
BigInteger $d, array $primes, array $exponents, array $coefficients,
$password = '', array $options = [])
{
if (count($primes) != 2) {
throw new \InvalidArgumentException('PuTTY does not
support multi-prime RSA keys');
}
$public = Strings::packSSH2('ii', $e, $n);
$private = Strings::packSSH2('iiii', $d, $primes[1],
$primes[2], $coefficients[2]);
return self::wrapPrivateKey($public, $private, 'ssh-rsa',
$password, $options);
}
/**
* Convert a public key to the appropriate format
*
* @param \phpseclib3\Math\BigInteger $n
* @param \phpseclib3\Math\BigInteger $e
* @return string
*/
public static function savePublicKey(BigInteger $n, BigInteger $e)
{
return self::wrapPublicKey(Strings::packSSH2('ii', $e,
$n), 'ssh-rsa');
}
}
phpseclib/phpseclib/Crypt/RSA/Formats/Keys/Raw.php000064400000013113151161424250016004
0ustar00<?php
/**
* Raw RSA Key Handler
*
* PHP version 5
*
* An array containing two \phpseclib3\Math\BigInteger objects.
*
* The exponent can be indexed with any of the following:
*
* 0, e, exponent, publicExponent
*
* The modulus can be indexed with any of the following:
*
* 1, n, modulo, modulus
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\RSA\Formats\Keys;
use phpseclib3\Math\BigInteger;
/**
* Raw RSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Raw
{
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
if (!is_array($key)) {
throw new \UnexpectedValueException('Key should be a array
- not a ' . gettype($key));
}
$key = array_change_key_case($key, CASE_LOWER);
$components = ['isPublicKey' => false];
foreach (['e', 'exponent',
'publicexponent', 0, 'privateexponent', 'd']
as $index) {
if (isset($key[$index])) {
$components['publicExponent'] = $key[$index];
break;
}
}
foreach (['n', 'modulo', 'modulus',
1] as $index) {
if (isset($key[$index])) {
$components['modulus'] = $key[$index];
break;
}
}
if (!isset($components['publicExponent']) ||
!isset($components['modulus'])) {
throw new \UnexpectedValueException('Modulus / exponent
not present');
}
if (isset($key['primes'])) {
$components['primes'] = $key['primes'];
} elseif (isset($key['p']) &&
isset($key['q'])) {
$indices = [
['p', 'q'],
['prime1', 'prime2']
];
foreach ($indices as $index) {
list($i0, $i1) = $index;
if (isset($key[$i0]) && isset($key[$i1])) {
$components['primes'] = [1 => $key[$i0],
$key[$i1]];
}
}
}
if (isset($key['exponents'])) {
$components['exponents'] =
$key['exponents'];
} else {
$indices = [
['dp', 'dq'],
['exponent1', 'exponent2']
];
foreach ($indices as $index) {
list($i0, $i1) = $index;
if (isset($key[$i0]) && isset($key[$i1])) {
$components['exponents'] = [1 =>
$key[$i0], $key[$i1]];
}
}
}
if (isset($key['coefficients'])) {
$components['coefficients'] =
$key['coefficients'];
} else {
foreach (['inverseq', 'q\'',
'coefficient'] as $index) {
if (isset($key[$index])) {
$components['coefficients'] = [2 =>
$key[$index]];
}
}
}
if (!isset($components['primes'])) {
$components['isPublicKey'] = true;
return $components;
}
if (!isset($components['exponents'])) {
$one = new BigInteger(1);
$temp = $components['primes'][1]->subtract($one);
$exponents = [1 =>
$components['publicExponent']->modInverse($temp)];
$temp = $components['primes'][2]->subtract($one);
$exponents[] =
$components['publicExponent']->modInverse($temp);
$components['exponents'] = $exponents;
}
if (!isset($components['coefficients'])) {
$components['coefficients'] = [2 =>
$components['primes'][2]->modInverse($components['primes'][1])];
}
foreach (['privateexponent', 'd'] as $index) {
if (isset($key[$index])) {
$components['privateExponent'] = $key[$index];
break;
}
}
return $components;
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $n
* @param \phpseclib3\Math\BigInteger $e
* @param \phpseclib3\Math\BigInteger $d
* @param array $primes
* @param array $exponents
* @param array $coefficients
* @param string $password optional
* @param array $options optional
* @return array
*/
public static function savePrivateKey(BigInteger $n, BigInteger $e,
BigInteger $d, array $primes, array $exponents, array $coefficients,
$password = '', array $options = [])
{
if (!empty($password) && is_string($password)) {
throw new UnsupportedFormatException('Raw private keys do
not support encryption');
}
return [
'e' => clone $e,
'n' => clone $n,
'd' => clone $d,
'primes' => array_map(function ($var) {
return clone $var;
}, $primes),
'exponents' => array_map(function ($var) {
return clone $var;
}, $exponents),
'coefficients' => array_map(function ($var) {
return clone $var;
}, $coefficients)
];
}
/**
* Convert a public key to the appropriate format
*
* @param \phpseclib3\Math\BigInteger $n
* @param \phpseclib3\Math\BigInteger $e
* @return array
*/
public static function savePublicKey(BigInteger $n, BigInteger $e)
{
return ['e' => clone $e, 'n' => clone
$n];
}
}
phpseclib/phpseclib/Crypt/RSA/Formats/Keys/XML.php000064400000013705151161424250015722
0ustar00<?php
/**
* XML Formatted RSA Key Handler
*
* More info:
*
* http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
* http://www.w3.org/TR/xkms2/#XKMS_2_0_Paragraph_269
* http://en.wikipedia.org/wiki/XML_Signature
* http://en.wikipedia.org/wiki/XKMS
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\RSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Exception\BadConfigurationException;
use phpseclib3\Exception\UnsupportedFormatException;
use phpseclib3\Math\BigInteger;
/**
* XML Formatted RSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class XML
{
/**
* Break a public or private key down into its constituent components
*
* @param string $key
* @param string $password optional
* @return array
*/
public static function load($key, $password = '')
{
if (!Strings::is_stringable($key)) {
throw new \UnexpectedValueException('Key should be a
string - not a ' . gettype($key));
}
if (!class_exists('DOMDocument')) {
throw new BadConfigurationException('The dom extension is
not setup correctly on this system');
}
$components = [
'isPublicKey' => false,
'primes' => [],
'exponents' => [],
'coefficients' => []
];
$use_errors = libxml_use_internal_errors(true);
$dom = new \DOMDocument();
if (substr($key, 0, 5) != '<?xml') {
$key = '<xml>' . $key .
'</xml>';
}
if (!$dom->loadXML($key)) {
libxml_use_internal_errors($use_errors);
throw new \UnexpectedValueException('Key does not appear
to contain XML');
}
$xpath = new \DOMXPath($dom);
$keys = ['modulus', 'exponent', 'p',
'q', 'dp', 'dq', 'inverseq',
'd'];
foreach ($keys as $key) {
// $dom->getElementsByTagName($key) is case-sensitive
$temp = $xpath->query("//*[translate(local-name(),
'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$key']");
if (!$temp->length) {
continue;
}
$value = new
BigInteger(Strings::base64_decode($temp->item(0)->nodeValue), 256);
switch ($key) {
case 'modulus':
$components['modulus'] = $value;
break;
case 'exponent':
$components['publicExponent'] = $value;
break;
case 'p':
$components['primes'][1] = $value;
break;
case 'q':
$components['primes'][2] = $value;
break;
case 'dp':
$components['exponents'][1] = $value;
break;
case 'dq':
$components['exponents'][2] = $value;
break;
case 'inverseq':
$components['coefficients'][2] = $value;
break;
case 'd':
$components['privateExponent'] = $value;
}
}
libxml_use_internal_errors($use_errors);
foreach ($components as $key => $value) {
if (is_array($value) && !count($value)) {
unset($components[$key]);
}
}
if (isset($components['modulus']) &&
isset($components['publicExponent'])) {
if (count($components) == 3) {
$components['isPublicKey'] = true;
}
return $components;
}
throw new \UnexpectedValueException('Modulus / exponent not
present');
}
/**
* Convert a private key to the appropriate format.
*
* @param \phpseclib3\Math\BigInteger $n
* @param \phpseclib3\Math\BigInteger $e
* @param \phpseclib3\Math\BigInteger $d
* @param array $primes
* @param array $exponents
* @param array $coefficients
* @param string $password optional
* @return string
*/
public static function savePrivateKey(BigInteger $n, BigInteger $e,
BigInteger $d, array $primes, array $exponents, array $coefficients,
$password = '')
{
if (count($primes) != 2) {
throw new \InvalidArgumentException('XML does not support
multi-prime RSA keys');
}
if (!empty($password) && is_string($password)) {
throw new UnsupportedFormatException('XML private keys do
not support encryption');
}
return "<RSAKeyPair>\r\n" .
' <Modulus>' .
Strings::base64_encode($n->toBytes()) . "</Modulus>\r\n"
.
' <Exponent>' .
Strings::base64_encode($e->toBytes()) .
"</Exponent>\r\n" .
' <P>' .
Strings::base64_encode($primes[1]->toBytes()) .
"</P>\r\n" .
' <Q>' .
Strings::base64_encode($primes[2]->toBytes()) .
"</Q>\r\n" .
' <DP>' .
Strings::base64_encode($exponents[1]->toBytes()) .
"</DP>\r\n" .
' <DQ>' .
Strings::base64_encode($exponents[2]->toBytes()) .
"</DQ>\r\n" .
' <InverseQ>' .
Strings::base64_encode($coefficients[2]->toBytes()) .
"</InverseQ>\r\n" .
' <D>' .
Strings::base64_encode($d->toBytes()) . "</D>\r\n" .
'</RSAKeyPair>';
}
/**
* Convert a public key to the appropriate format
*
* @param \phpseclib3\Math\BigInteger $n
* @param \phpseclib3\Math\BigInteger $e
* @return string
*/
public static function savePublicKey(BigInteger $n, BigInteger $e)
{
return "<RSAKeyValue>\r\n" .
' <Modulus>' .
Strings::base64_encode($n->toBytes()) . "</Modulus>\r\n"
.
' <Exponent>' .
Strings::base64_encode($e->toBytes()) .
"</Exponent>\r\n" .
'</RSAKeyValue>';
}
}
phpseclib/phpseclib/Crypt/RSA/PrivateKey.php000064400000037451151161424250015023
0ustar00<?php
/**
* RSA Private Key
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\RSA;
use phpseclib3\Crypt\Common;
use phpseclib3\Crypt\Random;
use phpseclib3\Crypt\RSA;
use phpseclib3\Crypt\RSA\Formats\Keys\PSS;
use phpseclib3\Exception\UnsupportedFormatException;
use phpseclib3\Math\BigInteger;
/**
* Raw RSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
final class PrivateKey extends RSA implements Common\PrivateKey
{
use Common\Traits\PasswordProtected;
/**
* Primes for Chinese Remainder Theorem (ie. p and q)
*
* @var array
*/
protected $primes;
/**
* Exponents for Chinese Remainder Theorem (ie. dP and dQ)
*
* @var array
*/
protected $exponents;
/**
* Coefficients for Chinese Remainder Theorem (ie. qInv)
*
* @var array
*/
protected $coefficients;
/**
* Private Exponent
*
* @var \phpseclib3\Math\BigInteger
*/
protected $privateExponent;
/**
* RSADP
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2
RFC3447#section-5.1.2}.
*
* @return bool|\phpseclib3\Math\BigInteger
*/
private function rsadp(BigInteger $c)
{
if ($c->compare(self::$zero) < 0 ||
$c->compare($this->modulus) > 0) {
throw new \OutOfRangeException('Ciphertext representative
out of range');
}
return $this->exponentiate($c);
}
/**
* RSASP1
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1
RFC3447#section-5.2.1}.
*
* @return bool|\phpseclib3\Math\BigInteger
*/
private function rsasp1(BigInteger $m)
{
if ($m->compare(self::$zero) < 0 ||
$m->compare($this->modulus) > 0) {
throw new \OutOfRangeException('Signature representative
out of range');
}
return $this->exponentiate($m);
}
/**
* Exponentiate
*
* @param \phpseclib3\Math\BigInteger $x
* @return \phpseclib3\Math\BigInteger
*/
protected function exponentiate(BigInteger $x)
{
switch (true) {
case empty($this->primes):
case $this->primes[1]->equals(self::$zero):
case empty($this->coefficients):
case $this->coefficients[2]->equals(self::$zero):
case empty($this->exponents):
case $this->exponents[1]->equals(self::$zero):
return $x->modPow($this->exponent,
$this->modulus);
}
$num_primes = count($this->primes);
if (!static::$enableBlinding) {
$m_i = [
1 => $x->modPow($this->exponents[1],
$this->primes[1]),
2 => $x->modPow($this->exponents[2],
$this->primes[2])
];
$h = $m_i[1]->subtract($m_i[2]);
$h = $h->multiply($this->coefficients[2]);
list(, $h) = $h->divide($this->primes[1]);
$m = $m_i[2]->add($h->multiply($this->primes[2]));
$r = $this->primes[1];
for ($i = 3; $i <= $num_primes; $i++) {
$m_i = $x->modPow($this->exponents[$i],
$this->primes[$i]);
$r = $r->multiply($this->primes[$i - 1]);
$h = $m_i->subtract($m);
$h = $h->multiply($this->coefficients[$i]);
list(, $h) = $h->divide($this->primes[$i]);
$m = $m->add($r->multiply($h));
}
} else {
$smallest = $this->primes[1];
for ($i = 2; $i <= $num_primes; $i++) {
if ($smallest->compare($this->primes[$i]) > 0) {
$smallest = $this->primes[$i];
}
}
$r = BigInteger::randomRange(self::$one,
$smallest->subtract(self::$one));
$m_i = [
1 => $this->blind($x, $r, 1),
2 => $this->blind($x, $r, 2)
];
$h = $m_i[1]->subtract($m_i[2]);
$h = $h->multiply($this->coefficients[2]);
list(, $h) = $h->divide($this->primes[1]);
$m = $m_i[2]->add($h->multiply($this->primes[2]));
$r = $this->primes[1];
for ($i = 3; $i <= $num_primes; $i++) {
$m_i = $this->blind($x, $r, $i);
$r = $r->multiply($this->primes[$i - 1]);
$h = $m_i->subtract($m);
$h = $h->multiply($this->coefficients[$i]);
list(, $h) = $h->divide($this->primes[$i]);
$m = $m->add($r->multiply($h));
}
}
return $m;
}
/**
* Performs RSA Blinding
*
* Protects against timing attacks by employing RSA Blinding.
* Returns $x->modPow($this->exponents[$i], $this->primes[$i])
*
* @param \phpseclib3\Math\BigInteger $x
* @param \phpseclib3\Math\BigInteger $r
* @param int $i
* @return \phpseclib3\Math\BigInteger
*/
private function blind(BigInteger $x, BigInteger $r, $i)
{
$x = $x->multiply($r->modPow($this->publicExponent,
$this->primes[$i]));
$x = $x->modPow($this->exponents[$i], $this->primes[$i]);
$r = $r->modInverse($this->primes[$i]);
$x = $x->multiply($r);
list(, $x) = $x->divide($this->primes[$i]);
return $x;
}
/**
* EMSA-PSS-ENCODE
*
* See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1
RFC3447#section-9.1.1}.
*
* @return string
* @param string $m
* @throws \RuntimeException on encoding error
* @param int $emBits
*/
private function emsa_pss_encode($m, $emBits)
{
// if $m is larger than two million terrabytes and you're
using sha1, PKCS#1 suggests a "Label too long" error
// be output.
$emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
$sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
$mHash = $this->hash->hash($m);
if ($emLen < $this->hLen + $sLen + 2) {
throw new \LengthException('RSA modulus too short');
}
$salt = Random::string($sLen);
$m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
$h = $this->hash->hash($m2);
$ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
$db = $ps . chr(1) . $salt;
$dbMask = $this->mgf1($h, $emLen - $this->hLen - 1); // ie.
stlren($db)
$maskedDB = $db ^ $dbMask;
$maskedDB[0] = ~chr(0xFF << ($emBits & 7)) &
$maskedDB[0];
$em = $maskedDB . $h . chr(0xBC);
return $em;
}
/**
* RSASSA-PSS-SIGN
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1
RFC3447#section-8.1.1}.
*
* @param string $m
* @return bool|string
*/
private function rsassa_pss_sign($m)
{
// EMSA-PSS encoding
$em = $this->emsa_pss_encode($m, 8 * $this->k - 1);
// RSA signature
$m = $this->os2ip($em);
$s = $this->rsasp1($m);
$s = $this->i2osp($s, $this->k);
// Output the signature S
return $s;
}
/**
* RSASSA-PKCS1-V1_5-SIGN
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1
RFC3447#section-8.2.1}.
*
* @param string $m
* @throws \LengthException if the RSA modulus is too short
* @return bool|string
*/
private function rsassa_pkcs1_v1_5_sign($m)
{
// EMSA-PKCS1-v1_5 encoding
// If the encoding operation outputs "intended encoded message
length too short," output "RSA modulus
// too short" and stop.
try {
$em = $this->emsa_pkcs1_v1_5_encode($m, $this->k);
} catch (\LengthException $e) {
throw new \LengthException('RSA modulus too short');
}
// RSA signature
$m = $this->os2ip($em);
$s = $this->rsasp1($m);
$s = $this->i2osp($s, $this->k);
// Output the signature S
return $s;
}
/**
* Create a signature
*
* @see self::verify()
* @param string $message
* @return string
*/
public function sign($message)
{
switch ($this->signaturePadding) {
case self::SIGNATURE_PKCS1:
case self::SIGNATURE_RELAXED_PKCS1:
return $this->rsassa_pkcs1_v1_5_sign($message);
//case self::SIGNATURE_PSS:
default:
return $this->rsassa_pss_sign($message);
}
}
/**
* RSAES-PKCS1-V1_5-DECRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2
RFC3447#section-7.2.2}.
*
* @param string $c
* @return bool|string
*/
private function rsaes_pkcs1_v1_5_decrypt($c)
{
// Length checking
if (strlen($c) != $this->k) { // or if k < 11
throw new \LengthException('Ciphertext representative too
long');
}
// RSA decryption
$c = $this->os2ip($c);
$m = $this->rsadp($c);
$em = $this->i2osp($m, $this->k);
// EME-PKCS1-v1_5 decoding
if (ord($em[0]) != 0 || ord($em[1]) > 2) {
throw new \RuntimeException('Decryption error');
}
$ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
$m = substr($em, strlen($ps) + 3);
if (strlen($ps) < 8) {
throw new \RuntimeException('Decryption error');
}
// Output M
return $m;
}
/**
* RSAES-OAEP-DECRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2
RFC3447#section-7.1.2}. The fact that the error
* messages aren't distinguishable from one another hinders
debugging, but, to quote from RFC3447#section-7.1.2:
*
* Note. Care must be taken to ensure that an opponent cannot
* distinguish the different error conditions in Step 3.g, whether
by
* error message or timing, or, more generally, learn partial
* information about the encoded message EM. Otherwise an opponent
may
* be able to obtain useful information about the decryption of the
* ciphertext C, leading to a chosen-ciphertext attack such as the
one
* observed by Manger [36].
*
* @param string $c
* @return bool|string
*/
private function rsaes_oaep_decrypt($c)
{
// Length checking
// if $l is larger than two million terrabytes and you're
using sha1, PKCS#1 suggests a "Label too long" error
// be output.
if (strlen($c) != $this->k || $this->k < 2 *
$this->hLen + 2) {
throw new \LengthException('Ciphertext representative too
long');
}
// RSA decryption
$c = $this->os2ip($c);
$m = $this->rsadp($c);
$em = $this->i2osp($m, $this->k);
// EME-OAEP decoding
$lHash = $this->hash->hash($this->label);
$y = ord($em[0]);
$maskedSeed = substr($em, 1, $this->hLen);
$maskedDB = substr($em, $this->hLen + 1);
$seedMask = $this->mgf1($maskedDB, $this->hLen);
$seed = $maskedSeed ^ $seedMask;
$dbMask = $this->mgf1($seed, $this->k - $this->hLen - 1);
$db = $maskedDB ^ $dbMask;
$lHash2 = substr($db, 0, $this->hLen);
$m = substr($db, $this->hLen);
$hashesMatch = hash_equals($lHash, $lHash2);
$leadingZeros = 1;
$patternMatch = 0;
$offset = 0;
for ($i = 0; $i < strlen($m); $i++) {
$patternMatch |= $leadingZeros & ($m[$i] ===
"\1");
$leadingZeros &= $m[$i] === "\0";
$offset += $patternMatch ? 0 : 1;
}
// we do | instead of || to avoid
https://en.wikipedia.org/wiki/Short-circuit_evaluation
// to protect against timing attacks
if (!$hashesMatch | !$patternMatch) {
throw new \RuntimeException('Decryption error');
}
// Output the message M
return substr($m, $offset + 1);
}
/**
* Raw Encryption / Decryption
*
* Doesn't use padding and is not recommended.
*
* @param string $m
* @return bool|string
* @throws \LengthException if strlen($m) > $this->k
*/
private function raw_encrypt($m)
{
if (strlen($m) > $this->k) {
throw new \LengthException('Ciphertext representative too
long');
}
$temp = $this->os2ip($m);
$temp = $this->rsadp($temp);
return $this->i2osp($temp, $this->k);
}
/**
* Decryption
*
* @see self::encrypt()
* @param string $ciphertext
* @return bool|string
*/
public function decrypt($ciphertext)
{
switch ($this->encryptionPadding) {
case self::ENCRYPTION_NONE:
return $this->raw_encrypt($ciphertext);
case self::ENCRYPTION_PKCS1:
return $this->rsaes_pkcs1_v1_5_decrypt($ciphertext);
//case self::ENCRYPTION_OAEP:
default:
return $this->rsaes_oaep_decrypt($ciphertext);
}
}
/**
* Returns the public key
*
* @return mixed
*/
public function getPublicKey()
{
$type = self::validatePlugin('Keys', 'PKCS8',
'savePublicKey');
if (empty($this->modulus) || empty($this->publicExponent)) {
throw new \RuntimeException('Public key components not
found');
}
$key = $type::savePublicKey($this->modulus,
$this->publicExponent);
return RSA::loadFormat('PKCS8', $key)
->withHash($this->hash->getHash())
->withMGFHash($this->mgfHash->getHash())
->withSaltLength($this->sLen)
->withLabel($this->label)
->withPadding($this->signaturePadding |
$this->encryptionPadding);
}
/**
* Returns the private key
*
* @param string $type
* @param array $options optional
* @return string
*/
public function toString($type, array $options = [])
{
$type = self::validatePlugin(
'Keys',
$type,
empty($this->primes) ? 'savePublicKey' :
'savePrivateKey'
);
if ($type == PSS::class) {
if ($this->signaturePadding == self::SIGNATURE_PSS) {
$options += [
'hash' => $this->hash->getHash(),
'MGFHash' =>
$this->mgfHash->getHash(),
'saltLength' => $this->getSaltLength()
];
} else {
throw new UnsupportedFormatException('The PSS format
can only be used when the signature method has been explicitly set to
PSS');
}
}
if (empty($this->primes)) {
return $type::savePublicKey($this->modulus,
$this->exponent, $options);
}
return $type::savePrivateKey($this->modulus,
$this->publicExponent, $this->exponent, $this->primes,
$this->exponents, $this->coefficients, $this->password, $options);
/*
$key = $type::savePrivateKey($this->modulus,
$this->publicExponent, $this->exponent, $this->primes,
$this->exponents, $this->coefficients, $this->password, $options);
if ($key !== false || count($this->primes) == 2) {
return $key;
}
$nSize = $this->getSize() >> 1;
$primes = [1 => clone self::$one, clone self::$one];
$i = 1;
foreach ($this->primes as $prime) {
$primes[$i] = $primes[$i]->multiply($prime);
if ($primes[$i]->getLength() >= $nSize) {
$i++;
}
}
$exponents = [];
$coefficients = [2 => $primes[2]->modInverse($primes[1])];
foreach ($primes as $i => $prime) {
$temp = $prime->subtract(self::$one);
$exponents[$i] = $this->modulus->modInverse($temp);
}
return $type::savePrivateKey($this->modulus,
$this->publicExponent, $this->exponent, $primes, $exponents,
$coefficients, $this->password, $options);
*/
}
}
phpseclib/phpseclib/Crypt/RSA/PublicKey.php000064400000036177151161424250014633
0ustar00<?php
/**
* RSA Public Key
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt\RSA;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common;
use phpseclib3\Crypt\Hash;
use phpseclib3\Crypt\Random;
use phpseclib3\Crypt\RSA;
use phpseclib3\Crypt\RSA\Formats\Keys\PSS;
use phpseclib3\Exception\UnsupportedAlgorithmException;
use phpseclib3\Exception\UnsupportedFormatException;
use phpseclib3\File\ASN1;
use phpseclib3\File\ASN1\Maps\DigestInfo;
use phpseclib3\Math\BigInteger;
/**
* Raw RSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
final class PublicKey extends RSA implements Common\PublicKey
{
use Common\Traits\Fingerprint;
/**
* Exponentiate
*
* @param \phpseclib3\Math\BigInteger $x
* @return \phpseclib3\Math\BigInteger
*/
private function exponentiate(BigInteger $x)
{
return $x->modPow($this->exponent, $this->modulus);
}
/**
* RSAVP1
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2
RFC3447#section-5.2.2}.
*
* @param \phpseclib3\Math\BigInteger $s
* @return bool|\phpseclib3\Math\BigInteger
*/
private function rsavp1($s)
{
if ($s->compare(self::$zero) < 0 ||
$s->compare($this->modulus) > 0) {
return false;
}
return $this->exponentiate($s);
}
/**
* RSASSA-PKCS1-V1_5-VERIFY
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2
RFC3447#section-8.2.2}.
*
* @param string $m
* @param string $s
* @throws \LengthException if the RSA modulus is too short
* @return bool
*/
private function rsassa_pkcs1_v1_5_verify($m, $s)
{
// Length checking
if (strlen($s) != $this->k) {
return false;
}
// RSA verification
$s = $this->os2ip($s);
$m2 = $this->rsavp1($s);
if ($m2 === false) {
return false;
}
$em = $this->i2osp($m2, $this->k);
if ($em === false) {
return false;
}
// EMSA-PKCS1-v1_5 encoding
$exception = false;
// If the encoding operation outputs "intended encoded message
length too short," output "RSA modulus
// too short" and stop.
try {
$em2 = $this->emsa_pkcs1_v1_5_encode($m, $this->k);
$r1 = hash_equals($em, $em2);
} catch (\LengthException $e) {
$exception = true;
}
try {
$em3 = $this->emsa_pkcs1_v1_5_encode_without_null($m,
$this->k);
$r2 = hash_equals($em, $em3);
} catch (\LengthException $e) {
$exception = true;
} catch (UnsupportedAlgorithmException $e) {
$r2 = false;
}
if ($exception) {
throw new \LengthException('RSA modulus too short');
}
// Compare
return $r1 || $r2;
}
/**
* RSASSA-PKCS1-V1_5-VERIFY (relaxed matching)
*
* Per {@link http://tools.ietf.org/html/rfc3447#page-43
RFC3447#page-43} PKCS1 v1.5
* specified the use BER encoding rather than DER encoding that PKCS1
v2.0 specified.
* This means that under rare conditions you can have a perfectly valid
v1.5 signature
* that fails to validate with _rsassa_pkcs1_v1_5_verify(). PKCS1 v2.1
also recommends
* that if you're going to validate these types of signatures you
"should indicate
* whether the underlying BER encoding is a DER encoding and hence
whether the signature
* is valid with respect to the specification given in [PKCS1
v2.0+]". so if you do
* $rsa->getLastPadding() and get RSA::PADDING_RELAXED_PKCS1 back
instead of
* RSA::PADDING_PKCS1... that means BER encoding was used.
*
* @param string $m
* @param string $s
* @return bool
*/
private function rsassa_pkcs1_v1_5_relaxed_verify($m, $s)
{
// Length checking
if (strlen($s) != $this->k) {
return false;
}
// RSA verification
$s = $this->os2ip($s);
$m2 = $this->rsavp1($s);
if ($m2 === false) {
return false;
}
$em = $this->i2osp($m2, $this->k);
if ($em === false) {
return false;
}
if (Strings::shift($em, 2) != "\0\1") {
return false;
}
$em = ltrim($em, "\xFF");
if (Strings::shift($em) != "\0") {
return false;
}
$decoded = ASN1::decodeBER($em);
if (!is_array($decoded) || empty($decoded[0]) || strlen($em) >
$decoded[0]['length']) {
return false;
}
static $oids;
if (!isset($oids)) {
$oids = [
'md2' => '1.2.840.113549.2.2',
'md4' => '1.2.840.113549.2.4', //
from PKCS1 v1.5
'md5' => '1.2.840.113549.2.5',
'id-sha1' => '1.3.14.3.2.26',
'id-sha256' =>
'2.16.840.1.101.3.4.2.1',
'id-sha384' =>
'2.16.840.1.101.3.4.2.2',
'id-sha512' =>
'2.16.840.1.101.3.4.2.3',
// from PKCS1 v2.2
'id-sha224' =>
'2.16.840.1.101.3.4.2.4',
'id-sha512/224' =>
'2.16.840.1.101.3.4.2.5',
'id-sha512/256' =>
'2.16.840.1.101.3.4.2.6',
];
ASN1::loadOIDs($oids);
}
$decoded = ASN1::asn1map($decoded[0], DigestInfo::MAP);
if (!isset($decoded) || $decoded === false) {
return false;
}
if
(!isset($oids[$decoded['digestAlgorithm']['algorithm']]))
{
return false;
}
if
(isset($decoded['digestAlgorithm']['parameters'])
&& $decoded['digestAlgorithm']['parameters']
!== ['null' => '']) {
return false;
}
$hash =
$decoded['digestAlgorithm']['algorithm'];
$hash = substr($hash, 0, 3) == 'id-' ?
substr($hash, 3) :
$hash;
$hash = new Hash($hash);
$em = $hash->hash($m);
$em2 = $decoded['digest'];
return hash_equals($em, $em2);
}
/**
* EMSA-PSS-VERIFY
*
* See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2
RFC3447#section-9.1.2}.
*
* @param string $m
* @param string $em
* @param int $emBits
* @return string
*/
private function emsa_pss_verify($m, $em, $emBits)
{
// if $m is larger than two million terrabytes and you're
using sha1, PKCS#1 suggests a "Label too long" error
// be output.
$emLen = ($emBits + 7) >> 3; // ie. ceil($emBits / 8);
$sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
$mHash = $this->hash->hash($m);
if ($emLen < $this->hLen + $sLen + 2) {
return false;
}
if ($em[strlen($em) - 1] != chr(0xBC)) {
return false;
}
$maskedDB = substr($em, 0, -$this->hLen - 1);
$h = substr($em, -$this->hLen - 1, $this->hLen);
$temp = chr(0xFF << ($emBits & 7));
if ((~$maskedDB[0] & $temp) != $temp) {
return false;
}
$dbMask = $this->mgf1($h, $emLen - $this->hLen - 1);
$db = $maskedDB ^ $dbMask;
$db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
$temp = $emLen - $this->hLen - $sLen - 2;
if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) ||
ord($db[$temp]) != 1) {
return false;
}
$salt = substr($db, $temp + 1); // should be $sLen long
$m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
$h2 = $this->hash->hash($m2);
return hash_equals($h, $h2);
}
/**
* RSASSA-PSS-VERIFY
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2
RFC3447#section-8.1.2}.
*
* @param string $m
* @param string $s
* @return bool|string
*/
private function rsassa_pss_verify($m, $s)
{
// Length checking
if (strlen($s) != $this->k) {
return false;
}
// RSA verification
$modBits = strlen($this->modulus->toBits());
$s2 = $this->os2ip($s);
$m2 = $this->rsavp1($s2);
$em = $this->i2osp($m2, $this->k);
if ($em === false) {
return false;
}
// EMSA-PSS verification
return $this->emsa_pss_verify($m, $em, $modBits - 1);
}
/**
* Verifies a signature
*
* @see self::sign()
* @param string $message
* @param string $signature
* @return bool
*/
public function verify($message, $signature)
{
switch ($this->signaturePadding) {
case self::SIGNATURE_RELAXED_PKCS1:
return $this->rsassa_pkcs1_v1_5_relaxed_verify($message,
$signature);
case self::SIGNATURE_PKCS1:
return $this->rsassa_pkcs1_v1_5_verify($message,
$signature);
//case self::SIGNATURE_PSS:
default:
return $this->rsassa_pss_verify($message, $signature);
}
}
/**
* RSAES-PKCS1-V1_5-ENCRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1
RFC3447#section-7.2.1}.
*
* @param string $m
* @param bool $pkcs15_compat optional
* @throws \LengthException if strlen($m) > $this->k - 11
* @return bool|string
*/
private function rsaes_pkcs1_v1_5_encrypt($m, $pkcs15_compat = false)
{
$mLen = strlen($m);
// Length checking
if ($mLen > $this->k - 11) {
throw new \LengthException('Message too long');
}
// EME-PKCS1-v1_5 encoding
$psLen = $this->k - $mLen - 3;
$ps = '';
while (strlen($ps) != $psLen) {
$temp = Random::string($psLen - strlen($ps));
$temp = str_replace("\x00", '', $temp);
$ps .= $temp;
}
$type = 2;
$em = chr(0) . chr($type) . $ps . chr(0) . $m;
// RSA encryption
$m = $this->os2ip($em);
$c = $this->rsaep($m);
$c = $this->i2osp($c, $this->k);
// Output the ciphertext C
return $c;
}
/**
* RSAES-OAEP-ENCRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1
RFC3447#section-7.1.1} and
* {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding
OAES}.
*
* @param string $m
* @throws \LengthException if strlen($m) > $this->k - 2 *
$this->hLen - 2
* @return string
*/
private function rsaes_oaep_encrypt($m)
{
$mLen = strlen($m);
// Length checking
// if $l is larger than two million terrabytes and you're
using sha1, PKCS#1 suggests a "Label too long" error
// be output.
if ($mLen > $this->k - 2 * $this->hLen - 2) {
throw new \LengthException('Message too long');
}
// EME-OAEP encoding
$lHash = $this->hash->hash($this->label);
$ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen -
2);
$db = $lHash . $ps . chr(1) . $m;
$seed = Random::string($this->hLen);
$dbMask = $this->mgf1($seed, $this->k - $this->hLen - 1);
$maskedDB = $db ^ $dbMask;
$seedMask = $this->mgf1($maskedDB, $this->hLen);
$maskedSeed = $seed ^ $seedMask;
$em = chr(0) . $maskedSeed . $maskedDB;
// RSA encryption
$m = $this->os2ip($em);
$c = $this->rsaep($m);
$c = $this->i2osp($c, $this->k);
// Output the ciphertext C
return $c;
}
/**
* RSAEP
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1
RFC3447#section-5.1.1}.
*
* @param \phpseclib3\Math\BigInteger $m
* @return bool|\phpseclib3\Math\BigInteger
*/
private function rsaep($m)
{
if ($m->compare(self::$zero) < 0 ||
$m->compare($this->modulus) > 0) {
throw new \OutOfRangeException('Message representative out
of range');
}
return $this->exponentiate($m);
}
/**
* Raw Encryption / Decryption
*
* Doesn't use padding and is not recommended.
*
* @param string $m
* @return bool|string
* @throws \LengthException if strlen($m) > $this->k
*/
private function raw_encrypt($m)
{
if (strlen($m) > $this->k) {
throw new \LengthException('Message too long');
}
$temp = $this->os2ip($m);
$temp = $this->rsaep($temp);
return $this->i2osp($temp, $this->k);
}
/**
* Encryption
*
* Both self::PADDING_OAEP and self::PADDING_PKCS1 both place limits on
how long $plaintext can be.
* If $plaintext exceeds those limits it will be broken up so that it
does and the resultant ciphertext's will
* be concatenated together.
*
* @see self::decrypt()
* @param string $plaintext
* @return bool|string
* @throws \LengthException if the RSA modulus is too short
*/
public function encrypt($plaintext)
{
switch ($this->encryptionPadding) {
case self::ENCRYPTION_NONE:
return $this->raw_encrypt($plaintext);
case self::ENCRYPTION_PKCS1:
return $this->rsaes_pkcs1_v1_5_encrypt($plaintext);
//case self::ENCRYPTION_OAEP:
default:
return $this->rsaes_oaep_encrypt($plaintext);
}
}
/**
* Returns the public key
*
* The public key is only returned under two circumstances - if the
private key had the public key embedded within it
* or if the public key was set via setPublicKey(). If the currently
loaded key is supposed to be the public key this
* function won't return it since this library, for the most part,
doesn't distinguish between public and private keys.
*
* @param string $type
* @param array $options optional
* @return mixed
*/
public function toString($type, array $options = [])
{
$type = self::validatePlugin('Keys', $type,
'savePublicKey');
if ($type == PSS::class) {
if ($this->signaturePadding == self::SIGNATURE_PSS) {
$options += [
'hash' => $this->hash->getHash(),
'MGFHash' =>
$this->mgfHash->getHash(),
'saltLength' => $this->getSaltLength()
];
} else {
throw new UnsupportedFormatException('The PSS format
can only be used when the signature method has been explicitly set to
PSS');
}
}
return $type::savePublicKey($this->modulus,
$this->publicExponent, $options);
}
/**
* Converts a public key to a private key
*
* @return RSA
*/
public function asPrivateKey()
{
$new = new PrivateKey();
$new->exponent = $this->exponent;
$new->modulus = $this->modulus;
$new->k = $this->k;
$new->format = $this->format;
return $new
->withHash($this->hash->getHash())
->withMGFHash($this->mgfHash->getHash())
->withSaltLength($this->sLen)
->withLabel($this->label)
->withPadding($this->signaturePadding |
$this->encryptionPadding);
}
}
phpseclib/phpseclib/Crypt/Salsa20.php000064400000034564151161424250013522
0ustar00<?php
/**
* Pure-PHP implementation of Salsa20.
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2019 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\StreamCipher;
use phpseclib3\Exception\BadDecryptionException;
use phpseclib3\Exception\InsufficientSetupException;
/**
* Pure-PHP implementation of Salsa20.
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class Salsa20 extends StreamCipher
{
/**
* Part 1 of the state
*
* @var string|false
*/
protected $p1 = false;
/**
* Part 2 of the state
*
* @var string|false
*/
protected $p2 = false;
/**
* Key Length (in bytes)
*
* @var int
*/
protected $key_length = 32; // = 256 bits
/**
* @see \phpseclib3\Crypt\Salsa20::crypt()
*/
const ENCRYPT = 0;
/**
* @see \phpseclib3\Crypt\Salsa20::crypt()
*/
const DECRYPT = 1;
/**
* Encryption buffer for continuous mode
*
* @var array
*/
protected $enbuffer;
/**
* Decryption buffer for continuous mode
*
* @var array
*/
protected $debuffer;
/**
* Counter
*
* @var int
*/
protected $counter = 0;
/**
* Using Generated Poly1305 Key
*
* @var boolean
*/
protected $usingGeneratedPoly1305Key = false;
/**
* Salsa20 uses a nonce
*
* @return bool
*/
public function usesNonce()
{
return true;
}
/**
* Sets the key.
*
* @param string $key
* @throws \LengthException if the key length isn't supported
*/
public function setKey($key)
{
switch (strlen($key)) {
case 16:
case 32:
break;
default:
throw new \LengthException('Key of size ' .
strlen($key) . ' not supported by this algorithm. Only keys of sizes
16 or 32 are supported');
}
parent::setKey($key);
}
/**
* Sets the nonce.
*
* @param string $nonce
*/
public function setNonce($nonce)
{
if (strlen($nonce) != 8) {
throw new \LengthException('Nonce of size ' .
strlen($key) . ' not supported by this algorithm. Only an 64-bit nonce
is supported');
}
$this->nonce = $nonce;
$this->changed = true;
$this->setEngine();
}
/**
* Sets the counter.
*
* @param int $counter
*/
public function setCounter($counter)
{
$this->counter = $counter;
$this->setEngine();
}
/**
* Creates a Poly1305 key using the method discussed in RFC8439
*
* See https://tools.ietf.org/html/rfc8439#section-2.6.1
*/
protected function createPoly1305Key()
{
if ($this->nonce === false) {
throw new InsufficientSetupException('No nonce has been
defined');
}
if ($this->key === false) {
throw new InsufficientSetupException('No key has been
defined');
}
$c = clone $this;
$c->setCounter(0);
$c->usePoly1305 = false;
$block = $c->encrypt(str_repeat("\0", 256));
$this->setPoly1305Key(substr($block, 0, 32));
if ($this->counter == 0) {
$this->counter++;
}
}
/**
* Setup the self::ENGINE_INTERNAL $engine
*
* (re)init, if necessary, the internal cipher $engine
*
* _setup() will be called each time if $changed === true
* typically this happens when using one or more of following public
methods:
*
* - setKey()
*
* - setNonce()
*
* - First run of encrypt() / decrypt() with no init-settings
*
* @see self::setKey()
* @see self::setNonce()
* @see self::disableContinuousBuffer()
*/
protected function setup()
{
if (!$this->changed) {
return;
}
$this->enbuffer = $this->debuffer = ['ciphertext'
=> '', 'counter' => $this->counter];
$this->changed = $this->nonIVChanged = false;
if ($this->nonce === false) {
throw new InsufficientSetupException('No nonce has been
defined');
}
if ($this->key === false) {
throw new InsufficientSetupException('No key has been
defined');
}
if ($this->usePoly1305 && !isset($this->poly1305Key))
{
$this->usingGeneratedPoly1305Key = true;
$this->createPoly1305Key();
}
$key = $this->key;
if (strlen($key) == 16) {
$constant = 'expand 16-byte k';
$key .= $key;
} else {
$constant = 'expand 32-byte k';
}
$this->p1 = substr($constant, 0, 4) .
substr($key, 0, 16) .
substr($constant, 4, 4) .
$this->nonce .
"\0\0\0\0";
$this->p2 = substr($constant, 8, 4) .
substr($key, 16, 16) .
substr($constant, 12, 4);
}
/**
* Setup the key (expansion)
*/
protected function setupKey()
{
// Salsa20 does not utilize this method
}
/**
* Encrypts a message.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
* @see self::crypt()
* @param string $plaintext
* @return string $ciphertext
*/
public function encrypt($plaintext)
{
$ciphertext = $this->crypt($plaintext, self::ENCRYPT);
if (isset($this->poly1305Key)) {
$this->newtag = $this->poly1305($ciphertext);
}
return $ciphertext;
}
/**
* Decrypts a message.
*
* $this->decrypt($this->encrypt($plaintext)) ==
$this->encrypt($this->encrypt($plaintext)).
* At least if the continuous buffer is disabled.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see self::crypt()
* @param string $ciphertext
* @return string $plaintext
*/
public function decrypt($ciphertext)
{
if (isset($this->poly1305Key)) {
if ($this->oldtag === false) {
throw new InsufficientSetupException('Authentication
Tag has not been set');
}
$newtag = $this->poly1305($ciphertext);
if ($this->oldtag != substr($newtag, 0,
strlen($this->oldtag))) {
$this->oldtag = false;
throw new BadDecryptionException('Derived
authentication tag and supplied authentication tag do not match');
}
$this->oldtag = false;
}
return $this->crypt($ciphertext, self::DECRYPT);
}
/**
* Encrypts a block
*
* @param string $in
*/
protected function encryptBlock($in)
{
// Salsa20 does not utilize this method
}
/**
* Decrypts a block
*
* @param string $in
*/
protected function decryptBlock($in)
{
// Salsa20 does not utilize this method
}
/**
* Encrypts or decrypts a message.
*
* @see self::encrypt()
* @see self::decrypt()
* @param string $text
* @param int $mode
* @return string $text
*/
private function crypt($text, $mode)
{
$this->setup();
if (!$this->continuousBuffer) {
if ($this->engine == self::ENGINE_OPENSSL) {
$iv = pack('V', $this->counter) .
$this->p2;
return openssl_encrypt(
$text,
$this->cipher_name_openssl,
$this->key,
OPENSSL_RAW_DATA,
$iv
);
}
$i = $this->counter;
$blocks = str_split($text, 64);
foreach ($blocks as &$block) {
$block ^= static::salsa20($this->p1 .
pack('V', $i++) . $this->p2);
}
return implode('', $blocks);
}
if ($mode == self::ENCRYPT) {
$buffer = &$this->enbuffer;
} else {
$buffer = &$this->debuffer;
}
if (!strlen($buffer['ciphertext'])) {
$ciphertext = '';
} else {
$ciphertext = $text ^
Strings::shift($buffer['ciphertext'], strlen($text));
$text = substr($text, strlen($ciphertext));
if (!strlen($text)) {
return $ciphertext;
}
}
$overflow = strlen($text) % 64; // & 0x3F
if ($overflow) {
$text2 = Strings::pop($text, $overflow);
if ($this->engine == self::ENGINE_OPENSSL) {
$iv = pack('V', $buffer['counter']) .
$this->p2;
// at this point $text should be a multiple of 64
$buffer['counter'] += (strlen($text) >> 6)
+ 1; // ie. divide by 64
$encrypted = openssl_encrypt(
$text . str_repeat("\0", 64),
$this->cipher_name_openssl,
$this->key,
OPENSSL_RAW_DATA,
$iv
);
$temp = Strings::pop($encrypted, 64);
} else {
$blocks = str_split($text, 64);
if (strlen($text)) {
foreach ($blocks as &$block) {
$block ^= static::salsa20($this->p1 .
pack('V', $buffer['counter']++) . $this->p2);
}
}
$encrypted = implode('', $blocks);
$temp = static::salsa20($this->p1 . pack('V',
$buffer['counter']++) . $this->p2);
}
$ciphertext .= $encrypted . ($text2 ^ $temp);
$buffer['ciphertext'] = substr($temp, $overflow);
} elseif (!strlen($buffer['ciphertext'])) {
if ($this->engine == self::ENGINE_OPENSSL) {
$iv = pack('V', $buffer['counter']) .
$this->p2;
$buffer['counter'] += (strlen($text) >> 6);
$ciphertext .= openssl_encrypt(
$text,
$this->cipher_name_openssl,
$this->key,
OPENSSL_RAW_DATA,
$iv
);
} else {
$blocks = str_split($text, 64);
foreach ($blocks as &$block) {
$block ^= static::salsa20($this->p1 .
pack('V', $buffer['counter']++) . $this->p2);
}
$ciphertext .= implode('', $blocks);
}
}
return $ciphertext;
}
/**
* Left Rotate
*
* @param int $x
* @param int $n
* @return int
*/
protected static function leftRotate($x, $n)
{
if (PHP_INT_SIZE == 8) {
$r1 = $x << $n;
$r1 &= 0xFFFFFFFF;
$r2 = ($x & 0xFFFFFFFF) >> (32 - $n);
} else {
$x = (int) $x;
$r1 = $x << $n;
$r2 = $x >> (32 - $n);
$r2 &= (1 << $n) - 1;
}
return $r1 | $r2;
}
/**
* The quarterround function
*
* @param int $a
* @param int $b
* @param int $c
* @param int $d
*/
protected static function quarterRound(&$a, &$b, &$c,
&$d)
{
$b ^= self::leftRotate($a + $d, 7);
$c ^= self::leftRotate($b + $a, 9);
$d ^= self::leftRotate($c + $b, 13);
$a ^= self::leftRotate($d + $c, 18);
}
/**
* The doubleround function
*
* @param int $x0 (by reference)
* @param int $x1 (by reference)
* @param int $x2 (by reference)
* @param int $x3 (by reference)
* @param int $x4 (by reference)
* @param int $x5 (by reference)
* @param int $x6 (by reference)
* @param int $x7 (by reference)
* @param int $x8 (by reference)
* @param int $x9 (by reference)
* @param int $x10 (by reference)
* @param int $x11 (by reference)
* @param int $x12 (by reference)
* @param int $x13 (by reference)
* @param int $x14 (by reference)
* @param int $x15 (by reference)
*/
protected static function doubleRound(&$x0, &$x1, &$x2,
&$x3, &$x4, &$x5, &$x6, &$x7, &$x8, &$x9,
&$x10, &$x11, &$x12, &$x13, &$x14, &$x15)
{
// columnRound
static::quarterRound($x0, $x4, $x8, $x12);
static::quarterRound($x5, $x9, $x13, $x1);
static::quarterRound($x10, $x14, $x2, $x6);
static::quarterRound($x15, $x3, $x7, $x11);
// rowRound
static::quarterRound($x0, $x1, $x2, $x3);
static::quarterRound($x5, $x6, $x7, $x4);
static::quarterRound($x10, $x11, $x8, $x9);
static::quarterRound($x15, $x12, $x13, $x14);
}
/**
* The Salsa20 hash function function
*
* @param string $x
*/
protected static function salsa20($x)
{
$z = $x = unpack('V*', $x);
for ($i = 0; $i < 10; $i++) {
static::doubleRound($z[1], $z[2], $z[3], $z[4], $z[5], $z[6],
$z[7], $z[8], $z[9], $z[10], $z[11], $z[12], $z[13], $z[14], $z[15],
$z[16]);
}
for ($i = 1; $i <= 16; $i++) {
$x[$i] += $z[$i];
}
return pack('V*', ...$x);
}
/**
* Calculates Poly1305 MAC
*
* @see self::decrypt()
* @see self::encrypt()
* @param string $ciphertext
* @return string
*/
protected function poly1305($ciphertext)
{
if (!$this->usingGeneratedPoly1305Key) {
return parent::poly1305($this->aad . $ciphertext);
} else {
/*
sodium_crypto_aead_chacha20poly1305_encrypt does not calculate
the poly1305 tag
the same way sodium_crypto_aead_chacha20poly1305_ietf_encrypt
does. you can see
how the latter encrypts it in Salsa20::encrypt(). here's
how the former encrypts
it:
$this->newtag = $this->poly1305(
$this->aad .
pack('V', strlen($this->aad)) .
"\0\0\0\0" .
$ciphertext .
pack('V', strlen($ciphertext)) .
"\0\0\0\0"
);
phpseclib opts to use the IETF construction, even when the
nonce is 64-bits
instead of 96-bits
*/
return parent::poly1305(
self::nullPad128($this->aad) .
self::nullPad128($ciphertext) .
pack('V', strlen($this->aad)) .
"\0\0\0\0" .
pack('V', strlen($ciphertext)) .
"\0\0\0\0"
);
}
}
}
phpseclib/phpseclib/Exception/BadConfigurationException.php000064400000000712151161424250020233
0ustar00<?php
/**
* BadConfigurationException
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Exception;
/**
* BadConfigurationException
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class BadConfigurationException extends \RuntimeException
{
}
phpseclib/phpseclib/Exception/BadDecryptionException.php000064400000000701151161424250017542
0ustar00<?php
/**
* BadDecryptionException
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Exception;
/**
* BadDecryptionException
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class BadDecryptionException extends \RuntimeException
{
}
phpseclib/phpseclib/Exception/BadModeException.php000064400000000657151161424250016320
0ustar00<?php
/**
* BadModeException
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Exception;
/**
* BadModeException
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class BadModeException extends \RuntimeException
{
}
phpseclib/phpseclib/Exception/ConnectionClosedException.php000064400000000712151161424250020246
0ustar00<?php
/**
* ConnectionClosedException
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Exception;
/**
* ConnectionClosedException
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class ConnectionClosedException extends \RuntimeException
{
}
phpseclib/phpseclib/Exception/FileNotFoundException.php000064400000000676151161424250017362
0ustar00<?php
/**
* FileNotFoundException
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Exception;
/**
* FileNotFoundException
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class FileNotFoundException extends \RuntimeException
{
}
phpseclib/phpseclib/Exception/InconsistentSetupException.php000064400000000715151161424250020521
0ustar00<?php
/**
* InconsistentSetupException
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Exception;
/**
* InconsistentSetupException
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class InconsistentSetupException extends \RuntimeException
{
}
phpseclib/phpseclib/Exception/InsufficientSetupException.php000064400000000715151161424250020467
0ustar00<?php
/**
* InsufficientSetupException
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Exception;
/**
* InsufficientSetupException
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class InsufficientSetupException extends \RuntimeException
{
}
phpseclib/phpseclib/Exception/NoKeyLoadedException.php000064400000000673151161424250017161
0ustar00<?php
/**
* NoKeyLoadedException
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Exception;
/**
* NoKeyLoadedException
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class NoKeyLoadedException extends \RuntimeException
{
}
phpseclib/phpseclib/Exception/NoSupportedAlgorithmsException.php000064400000000731151161424250021332
0ustar00<?php
/**
* NoSupportedAlgorithmsException
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Exception;
/**
* NoSupportedAlgorithmsException
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class NoSupportedAlgorithmsException extends \RuntimeException
{
}
phpseclib/phpseclib/Exception/UnableToConnectException.php000064400000000707151161424250020044
0ustar00<?php
/**
* UnableToConnectException
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Exception;
/**
* UnableToConnectException
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class UnableToConnectException extends \RuntimeException
{
}
phpseclib/phpseclib/Exception/UnsupportedAlgorithmException.php000064400000000726151161424250021221
0ustar00<?php
/**
* UnsupportedAlgorithmException
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Exception;
/**
* UnsupportedAlgorithmException
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class UnsupportedAlgorithmException extends \RuntimeException
{
}
phpseclib/phpseclib/Exception/UnsupportedCurveException.php000064400000000712151161424250020352
0ustar00<?php
/**
* UnsupportedCurveException
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Exception;
/**
* UnsupportedCurveException
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class UnsupportedCurveException extends \RuntimeException
{
}
phpseclib/phpseclib/Exception/UnsupportedFormatException.php000064400000000715151161424250020521
0ustar00<?php
/**
* UnsupportedFormatException
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Exception;
/**
* UnsupportedFormatException
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class UnsupportedFormatException extends \RuntimeException
{
}
phpseclib/phpseclib/Exception/UnsupportedOperationException.php000064400000000726151161424250021233
0ustar00<?php
/**
* UnsupportedOperationException
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Exception;
/**
* UnsupportedOperationException
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class UnsupportedOperationException extends \RuntimeException
{
}
phpseclib/phpseclib/File/ASN1/Maps/AccessDescription.php000064400000001234151161424250017066
0ustar00<?php
/**
* AccessDescription
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* AccessDescription
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class AccessDescription
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'accessMethod' => ['type' =>
ASN1::TYPE_OBJECT_IDENTIFIER],
'accessLocation' => GeneralName::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/AdministrationDomainName.php000064400000001653151161424250020404
0ustar00<?php
/**
* AdministrationDomainName
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* AdministrationDomainName
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class AdministrationDomainName
{
const MAP = [
'type' => ASN1::TYPE_CHOICE,
// if class isn't present it's assumed to be
\phpseclib3\File\ASN1::CLASS_UNIVERSAL or
// (if constant is present)
\phpseclib3\File\ASN1::CLASS_CONTEXT_SPECIFIC
'class' => ASN1::CLASS_APPLICATION,
'cast' => 2,
'children' => [
'numeric' => ['type' =>
ASN1::TYPE_NUMERIC_STRING],
'printable' => ['type' =>
ASN1::TYPE_PRINTABLE_STRING]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/AlgorithmIdentifier.php000064400000001347151161424250017417
0ustar00<?php
/**
* AlgorithmIdentifier
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* AlgorithmIdentifier
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class AlgorithmIdentifier
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'algorithm' => ['type' =>
ASN1::TYPE_OBJECT_IDENTIFIER],
'parameters' => [
'type' => ASN1::TYPE_ANY,
'optional' => true
]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/AnotherName.php000064400000001415151161424250015663
0ustar00<?php
/**
* AnotherName
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* AnotherName
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class AnotherName
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'type-id' => ['type' =>
ASN1::TYPE_OBJECT_IDENTIFIER],
'value' => [
'type' => ASN1::TYPE_ANY,
'constant' => 0,
'optional' => true,
'explicit' => true
]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/Attribute.php000064400000001361151161424250015425
0ustar00<?php
/**
* Attribute
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* Attribute
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Attribute
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'type' => AttributeType::MAP,
'value' => [
'type' => ASN1::TYPE_SET,
'min' => 1,
'max' => -1,
'children' => AttributeValue::MAP
]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/Attributes.php000064400000001065151161424250015611
0ustar00<?php
/**
* Attributes
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* Attributes
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Attributes
{
const MAP = [
'type' => ASN1::TYPE_SET,
'min' => 1,
'max' => -1,
'children' => Attribute::MAP
];
}
phpseclib/phpseclib/File/ASN1/Maps/AttributeType.php000064400000000757151161424250016277
0ustar00<?php
/**
* AttributeType
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* AttributeType
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class AttributeType
{
const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER];
}
phpseclib/phpseclib/File/ASN1/Maps/AttributeTypeAndValue.php000064400000001204151161424250017703
0ustar00<?php
/**
* AttributeTypeAndValue
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* AttributeTypeAndValue
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class AttributeTypeAndValue
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'type' => AttributeType::MAP,
'value' => AttributeValue::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/AttributeValue.php000064400000000744151161424250016426
0ustar00<?php
/**
* AttributeValue
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* AttributeValue
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class AttributeValue
{
const MAP = ['type' => ASN1::TYPE_ANY];
}
phpseclib/phpseclib/File/ASN1/Maps/AuthorityInfoAccessSyntax.php000064400000001157151161424250020622
0ustar00<?php
/**
* AuthorityInfoAccessSyntax
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* AuthorityInfoAccessSyntax
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class AuthorityInfoAccessSyntax
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => AccessDescription::MAP
];
}
phpseclib/phpseclib/File/ASN1/Maps/AuthorityKeyIdentifier.php000064400000002123151161424250020123
0ustar00<?php
/**
* AuthorityKeyIdentifier
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* AuthorityKeyIdentifier
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class AuthorityKeyIdentifier
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'keyIdentifier' => [
'constant' => 0,
'optional' => true,
'implicit' => true
] + KeyIdentifier::MAP,
'authorityCertIssuer' => [
'constant' => 1,
'optional' => true,
'implicit' => true
] + GeneralNames::MAP,
'authorityCertSerialNumber' => [
'constant' => 2,
'optional' => true,
'implicit' => true
] + CertificateSerialNumber::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/BaseDistance.php000064400000000742151161424250016011
0ustar00<?php
/**
* BaseDistance
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* BaseDistance
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class BaseDistance
{
const MAP = ['type' => ASN1::TYPE_INTEGER];
}
phpseclib/phpseclib/File/ASN1/Maps/BasicConstraints.php000064400000001476151161424250016742
0ustar00<?php
/**
* BasicConstraints
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* BasicConstraints
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class BasicConstraints
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'cA' => [
'type' => ASN1::TYPE_BOOLEAN,
'optional' => true,
'default' => false
],
'pathLenConstraint' => [
'type' => ASN1::TYPE_INTEGER,
'optional' => true
]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttribute.php000064400000001305151161424250021321
0ustar00<?php
/**
* BuiltInDomainDefinedAttribute
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* BuiltInDomainDefinedAttribute
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class BuiltInDomainDefinedAttribute
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'type' => ['type' =>
ASN1::TYPE_PRINTABLE_STRING],
'value' => ['type' =>
ASN1::TYPE_PRINTABLE_STRING]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttributes.php000064400000001251151161424250021504
0ustar00<?php
/**
* BuiltInDomainDefinedAttributes
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* BuiltInDomainDefinedAttributes
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class BuiltInDomainDefinedAttributes
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => 4, // ub-domain-defined-attributes
'children' => BuiltInDomainDefinedAttribute::MAP
];
}
phpseclib/phpseclib/File/ASN1/Maps/BuiltInStandardAttributes.php000064400000003744151161424250020567
0ustar00<?php
/**
* BuiltInStandardAttributes
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* BuiltInStandardAttributes
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class BuiltInStandardAttributes
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'country-name' => ['optional' =>
true] + CountryName::MAP,
'administration-domain-name' =>
['optional' => true] + AdministrationDomainName::MAP,
'network-address' => [
'constant' => 0,
'optional' => true,
'implicit' => true
] + NetworkAddress::MAP,
'terminal-identifier' => [
'constant' => 1,
'optional' => true,
'implicit' => true
] + TerminalIdentifier::MAP,
'private-domain-name' => [
'constant' => 2,
'optional' => true,
'explicit' => true
] + PrivateDomainName::MAP,
'organization-name' => [
'constant' => 3,
'optional' => true,
'implicit' => true
] + OrganizationName::MAP,
'numeric-user-identifier' => [
'constant' => 4,
'optional' => true,
'implicit' => true
] + NumericUserIdentifier::MAP,
'personal-name' => [
'constant' => 5,
'optional' => true,
'implicit' => true
] + PersonalName::MAP,
'organizational-unit-names' => [
'constant' => 6,
'optional' => true,
'implicit' => true
] + OrganizationalUnitNames::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/Certificate.php000064400000001301151161424250015676
0ustar00<?php
/**
* Certificate
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* Certificate
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Certificate
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'tbsCertificate' => TBSCertificate::MAP,
'signatureAlgorithm' => AlgorithmIdentifier::MAP,
'signature' => ['type' =>
ASN1::TYPE_BIT_STRING]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/CertificateIssuer.php000064400000000711151161424250017075
0ustar00<?php
/**
* CertificateIssuer
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
/**
* CertificateIssuer
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class CertificateIssuer
{
const MAP = GeneralNames::MAP;
}
phpseclib/phpseclib/File/ASN1/Maps/CertificateList.php000064400000001307151161424250016540
0ustar00<?php
/**
* CertificateList
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* CertificateList
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class CertificateList
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'tbsCertList' => TBSCertList::MAP,
'signatureAlgorithm' => AlgorithmIdentifier::MAP,
'signature' => ['type' =>
ASN1::TYPE_BIT_STRING]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/CertificatePolicies.php000064400000001135151161424250017373
0ustar00<?php
/**
* CertificatePolicies
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* CertificatePolicies
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class CertificatePolicies
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => PolicyInformation::MAP
];
}
phpseclib/phpseclib/File/ASN1/Maps/CertificateSerialNumber.php000064400000001003151161424250020206
0ustar00<?php
/**
* CertificateSerialNumber
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* CertificateSerialNumber
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class CertificateSerialNumber
{
const MAP = ['type' => ASN1::TYPE_INTEGER];
}
phpseclib/phpseclib/File/ASN1/Maps/CertificationRequest.php000064400000001360151161424250017615
0ustar00<?php
/**
* CertificationRequest
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* CertificationRequest
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class CertificationRequest
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'certificationRequestInfo' =>
CertificationRequestInfo::MAP,
'signatureAlgorithm' => AlgorithmIdentifier::MAP,
'signature' => ['type' =>
ASN1::TYPE_BIT_STRING]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/CertificationRequestInfo.php000064400000001671151161424250020436
0ustar00<?php
/**
* CertificationRequestInfo
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* CertificationRequestInfo
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class CertificationRequestInfo
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'version' => [
'type' => ASN1::TYPE_INTEGER,
'mapping' => ['v1']
],
'subject' => Name::MAP,
'subjectPKInfo' => SubjectPublicKeyInfo::MAP,
'attributes' => [
'constant' => 0,
'optional' => true,
'implicit' => true
] + Attributes::MAP,
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/CertPolicyId.php000064400000000754151161424250016021
0ustar00<?php
/**
* CertPolicyId
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* CertPolicyId
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class CertPolicyId
{
const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER];
}
phpseclib/phpseclib/File/ASN1/Maps/Characteristic_two.php000064400000001446151161424250017307
0ustar00<?php
/**
* Characteristic_two
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* Characteristic_two
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Characteristic_two
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'm' => ['type' =>
ASN1::TYPE_INTEGER], // field size 2**m
'basis' => ['type' =>
ASN1::TYPE_OBJECT_IDENTIFIER],
'parameters' => [
'type' => ASN1::TYPE_ANY,
'optional' => true
]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/CountryName.php000064400000001625151161424250015731
0ustar00<?php
/**
* CountryName
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* CountryName
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class CountryName
{
const MAP = [
'type' => ASN1::TYPE_CHOICE,
// if class isn't present it's assumed to be
\phpseclib3\File\ASN1::CLASS_UNIVERSAL or
// (if constant is present)
\phpseclib3\File\ASN1::CLASS_CONTEXT_SPECIFIC
'class' => ASN1::CLASS_APPLICATION,
'cast' => 1,
'children' => [
'x121-dcc-code' => ['type' =>
ASN1::TYPE_NUMERIC_STRING],
'iso-3166-alpha2-code' => ['type' =>
ASN1::TYPE_PRINTABLE_STRING]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/CPSuri.php000064400000000723151161424250014630
0ustar00<?php
/**
* CPSuri
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* CPSuri
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class CPSuri
{
const MAP = ['type' => ASN1::TYPE_IA5_STRING];
}
phpseclib/phpseclib/File/ASN1/Maps/CRLDistributionPoints.php000064400000001143151161424250017675
0ustar00<?php
/**
* CRLDistributionPoints
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* CRLDistributionPoints
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class CRLDistributionPoints
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => DistributionPoint::MAP
];
}
phpseclib/phpseclib/File/ASN1/Maps/CRLNumber.php000064400000000731151161424250015253
0ustar00<?php
/**
* CRLNumber
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* CRLNumber
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class CRLNumber
{
const MAP = ['type' => ASN1::TYPE_INTEGER];
}
phpseclib/phpseclib/File/ASN1/Maps/CRLReason.php000064400000001542151161424250015253
0ustar00<?php
/**
* CRLReason
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* CRLReason
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class CRLReason
{
const MAP = [
'type' => ASN1::TYPE_ENUMERATED,
'mapping' => [
'unspecified',
'keyCompromise',
'cACompromise',
'affiliationChanged',
'superseded',
'cessationOfOperation',
'certificateHold',
// Value 7 is not used.
8 => 'removeFromCRL',
'privilegeWithdrawn',
'aACompromise'
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/Curve.php000064400000001305151161424250014544
0ustar00<?php
/**
* Curve
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* Curve
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Curve
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'a' => FieldElement::MAP,
'b' => FieldElement::MAP,
'seed' => [
'type' => ASN1::TYPE_BIT_STRING,
'optional' => true
]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/DHParameter.php000064400000001526151161424250015621
0ustar00<?php
/**
* DHParameter
*
* From:
https://www.teletrust.de/fileadmin/files/oid/oid_pkcs-3v1-4.pdf#page=6
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* DHParameter
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class DHParameter
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'prime' => ['type' =>
ASN1::TYPE_INTEGER],
'base' => ['type' =>
ASN1::TYPE_INTEGER],
'privateValueLength' => [
'type' => ASN1::TYPE_INTEGER,
'optional' => true
]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/DigestInfo.php000064400000001301151161424250015507
0ustar00<?php
/**
* DigestInfo
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* DigestInfo
*
* from https://tools.ietf.org/html/rfc2898#appendix-A.3
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class DigestInfo
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'digestAlgorithm' => AlgorithmIdentifier::MAP,
'digest' => ['type' =>
ASN1::TYPE_OCTET_STRING]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/DirectoryString.php000064400000001562151161424250016620
0ustar00<?php
/**
* DirectoryString
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* DirectoryString
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class DirectoryString
{
const MAP = [
'type' => ASN1::TYPE_CHOICE,
'children' => [
'teletexString' => ['type' =>
ASN1::TYPE_TELETEX_STRING],
'printableString' => ['type' =>
ASN1::TYPE_PRINTABLE_STRING],
'universalString' => ['type' =>
ASN1::TYPE_UNIVERSAL_STRING],
'utf8String' => ['type' =>
ASN1::TYPE_UTF8_STRING],
'bmpString' => ['type' =>
ASN1::TYPE_BMP_STRING]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/DisplayText.php000064400000001420151161424250015730
0ustar00<?php
/**
* DisplayText
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* DisplayText
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class DisplayText
{
const MAP = [
'type' => ASN1::TYPE_CHOICE,
'children' => [
'ia5String' => ['type' =>
ASN1::TYPE_IA5_STRING],
'visibleString' => ['type' =>
ASN1::TYPE_VISIBLE_STRING],
'bmpString' => ['type' =>
ASN1::TYPE_BMP_STRING],
'utf8String' => ['type' =>
ASN1::TYPE_UTF8_STRING]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/DistributionPoint.php000064400000002050151161424250017147
0ustar00<?php
/**
* DistributionPoint
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* DistributionPoint
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class DistributionPoint
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'distributionPoint' => [
'constant' => 0,
'optional' => true,
'explicit' => true
] + DistributionPointName::MAP,
'reasons' => [
'constant' => 1,
'optional' => true,
'implicit' => true
] + ReasonFlags::MAP,
'cRLIssuer' => [
'constant' => 2,
'optional' => true,
'implicit' => true
] + GeneralNames::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/DistributionPointName.php000064400000001626151161424250017760
0ustar00<?php
/**
* DistributionPointName
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* DistributionPointName
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class DistributionPointName
{
const MAP = [
'type' => ASN1::TYPE_CHOICE,
'children' => [
'fullName' => [
'constant' => 0,
'optional' => true,
'implicit' => true
] + GeneralNames::MAP,
'nameRelativeToCRLIssuer' => [
'constant' => 1,
'optional' => true,
'implicit' => true
] + RelativeDistinguishedName::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/DSAParams.php000064400000001243151161424250015234
0ustar00<?php
/**
* DSAParams
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* DSAParams
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class DSAParams
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'p' => ['type' =>
ASN1::TYPE_INTEGER],
'q' => ['type' =>
ASN1::TYPE_INTEGER],
'g' => ['type' => ASN1::TYPE_INTEGER]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/DSAPrivateKey.php000064400000001516151161424250016077
0ustar00<?php
/**
* DSAPrivateKey
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* DSAPrivateKey
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class DSAPrivateKey
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'version' => ['type' =>
ASN1::TYPE_INTEGER],
'p' => ['type' =>
ASN1::TYPE_INTEGER],
'q' => ['type' =>
ASN1::TYPE_INTEGER],
'g' => ['type' =>
ASN1::TYPE_INTEGER],
'y' => ['type' =>
ASN1::TYPE_INTEGER],
'x' => ['type' => ASN1::TYPE_INTEGER]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/DSAPublicKey.php000064400000000742151161424250015703
0ustar00<?php
/**
* DSAPublicKey
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* DSAPublicKey
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class DSAPublicKey
{
const MAP = ['type' => ASN1::TYPE_INTEGER];
}
phpseclib/phpseclib/File/ASN1/Maps/DssSigValue.php000064400000001166151161424250015656
0ustar00<?php
/**
* DssSigValue
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* DssSigValue
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class DssSigValue
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'r' => ['type' =>
ASN1::TYPE_INTEGER],
's' => ['type' => ASN1::TYPE_INTEGER]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/EcdsaSigValue.php000064400000001174151161424250016143
0ustar00<?php
/**
* EcdsaSigValue
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* EcdsaSigValue
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class EcdsaSigValue
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'r' => ['type' =>
ASN1::TYPE_INTEGER],
's' => ['type' => ASN1::TYPE_INTEGER]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/ECParameters.php000064400000002164151161424250015777
0ustar00<?php
/**
* ECParameters
*
* From: https://tools.ietf.org/html/rfc5915
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* ECParameters
*
* ECParameters ::= CHOICE {
* namedCurve OBJECT IDENTIFIER
* -- implicitCurve NULL
* -- specifiedCurve SpecifiedECDomain
* }
* -- implicitCurve and specifiedCurve MUST NOT be used in PKIX.
* -- Details for SpecifiedECDomain can be found in [X9.62].
* -- Any future additions to this CHOICE should be coordinated
* -- with ANSI X9.
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class ECParameters
{
const MAP = [
'type' => ASN1::TYPE_CHOICE,
'children' => [
'namedCurve' => ['type' =>
ASN1::TYPE_OBJECT_IDENTIFIER],
'implicitCurve' => ['type' =>
ASN1::TYPE_NULL],
'specifiedCurve' => SpecifiedECDomain::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/ECPoint.php000064400000000730151161424250014762
0ustar00<?php
/**
* ECPoint
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* ECPoint
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class ECPoint
{
const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
}
phpseclib/phpseclib/File/ASN1/Maps/ECPrivateKey.php000064400000002176151161424250015762
0ustar00<?php
/**
* ECPrivateKey
*
* From: https://tools.ietf.org/html/rfc5915
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* ECPrivateKey
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class ECPrivateKey
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'version' => [
'type' => ASN1::TYPE_INTEGER,
'mapping' => [1 =>
'ecPrivkeyVer1']
],
'privateKey' => ['type' =>
ASN1::TYPE_OCTET_STRING],
'parameters' => [
'constant' => 0,
'optional' => true,
'explicit' => true
] + ECParameters::MAP,
'publicKey' => [
'type' => ASN1::TYPE_BIT_STRING,
'constant' => 1,
'optional' => true,
'explicit' => true
]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/EDIPartyName.php000064400000002062151161424250015703
0ustar00<?php
/**
* EDIPartyName
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* EDIPartyName
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class EDIPartyName
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'nameAssigner' => [
'constant' => 0,
'optional' => true,
'implicit' => true
] + DirectoryString::MAP,
// partyName is technically required but \phpseclib3\File\ASN1
doesn't currently support non-optional constants and
// setting it to optional gets the job done in any event.
'partyName' => [
'constant' => 1,
'optional' => true,
'implicit' => true
] + DirectoryString::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/EncryptedData.php000064400000000752151161424250016214
0ustar00<?php
/**
* EncryptedData
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* EncryptedData
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class EncryptedData
{
const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
}
phpseclib/phpseclib/File/ASN1/Maps/EncryptedPrivateKeyInfo.php000064400000001246151161424250020241
0ustar00<?php
/**
* EncryptedPrivateKeyInfo
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* EncryptedPrivateKeyInfo
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class EncryptedPrivateKeyInfo
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'encryptionAlgorithm' => AlgorithmIdentifier::MAP,
'encryptedData' => EncryptedData::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/Extension.php000064400000002053151161424250015435
0ustar00<?php
/**
* Extension
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* Extension
*
* A certificate using system MUST reject the certificate if it encounters
* a critical extension it does not recognize; however, a non-critical
* extension may be ignored if it is not recognized.
*
* http://tools.ietf.org/html/rfc5280#section-4.2
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Extension
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'extnId' => ['type' =>
ASN1::TYPE_OBJECT_IDENTIFIER],
'critical' => [
'type' => ASN1::TYPE_BOOLEAN,
'optional' => true,
'default' => false
],
'extnValue' => ['type' =>
ASN1::TYPE_OCTET_STRING]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttribute.php000064400000001715151161424250017325
0ustar00<?php
/**
* ExtensionAttribute
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* ExtensionAttribute
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class ExtensionAttribute
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'extension-attribute-type' => [
'type' => ASN1::TYPE_PRINTABLE_STRING,
'constant' => 0,
'optional' => true,
'implicit' => true
],
'extension-attribute-value' => [
'type' => ASN1::TYPE_ANY,
'constant' => 1,
'optional' => true,
'explicit' => true
]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttributes.php000064400000001165151161424250017507
0ustar00<?php
/**
* ExtensionAttributes
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* ExtensionAttributes
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class ExtensionAttributes
{
const MAP = [
'type' => ASN1::TYPE_SET,
'min' => 1,
'max' => 256, // ub-extension-attributes
'children' => ExtensionAttribute::MAP
];
}
phpseclib/phpseclib/File/ASN1/Maps/Extensions.php000064400000001316151161424250015621
0ustar00<?php
/**
* Extensions
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* Extensions
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Extensions
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
// technically, it's MAX, but we'll assume anything <
0 is MAX
'max' => -1,
// if 'children' isn't an array then 'min'
and 'max' must be defined
'children' => Extension::MAP
];
}
phpseclib/phpseclib/File/ASN1/Maps/ExtKeyUsageSyntax.php000064400000001122151161424250017062
0ustar00<?php
/**
* ExtKeyUsageSyntax
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* ExtKeyUsageSyntax
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class ExtKeyUsageSyntax
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => KeyPurposeId::MAP
];
}
phpseclib/phpseclib/File/ASN1/Maps/FieldElement.php000064400000000747151161424250016026
0ustar00<?php
/**
* FieldElement
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* FieldElement
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class FieldElement
{
const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
}
phpseclib/phpseclib/File/ASN1/Maps/FieldID.php000064400000001303151161424250014716
0ustar00<?php
/**
* FieldID
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* FieldID
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class FieldID
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'fieldType' => ['type' =>
ASN1::TYPE_OBJECT_IDENTIFIER],
'parameters' => [
'type' => ASN1::TYPE_ANY,
'optional' => true
]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/GeneralName.php000064400000004237151161424250015645
0ustar00<?php
/**
* GeneralName
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* GeneralName
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class GeneralName
{
const MAP = [
'type' => ASN1::TYPE_CHOICE,
'children' => [
'otherName' => [
'constant' => 0,
'optional' => true,
'implicit' => true
] + AnotherName::MAP,
'rfc822Name' => [
'type' => ASN1::TYPE_IA5_STRING,
'constant' => 1,
'optional' => true,
'implicit' => true
],
'dNSName' => [
'type' => ASN1::TYPE_IA5_STRING,
'constant' => 2,
'optional' => true,
'implicit' => true
],
'x400Address' => [
'constant' => 3,
'optional' => true,
'implicit' => true
] + ORAddress::MAP,
'directoryName' => [
'constant' => 4,
'optional' => true,
'explicit' => true
] + Name::MAP,
'ediPartyName' => [
'constant' => 5,
'optional' => true,
'implicit' => true
] + EDIPartyName::MAP,
'uniformResourceIdentifier' => [
'type' => ASN1::TYPE_IA5_STRING,
'constant' => 6,
'optional' => true,
'implicit' => true
],
'iPAddress' => [
'type' => ASN1::TYPE_OCTET_STRING,
'constant' => 7,
'optional' => true,
'implicit' => true
],
'registeredID' => [
'type' => ASN1::TYPE_OBJECT_IDENTIFIER,
'constant' => 8,
'optional' => true,
'implicit' => true
]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/GeneralNames.php000064400000001102151161424250016014
0ustar00<?php
/**
* GeneralNames
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* GeneralNames
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class GeneralNames
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => GeneralName::MAP
];
}
phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtree.php000064400000001660151161424250016373
0ustar00<?php
/**
* GeneralSubtree
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* GeneralSubtree
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class GeneralSubtree
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'base' => GeneralName::MAP,
'minimum' => [
'constant' => 0,
'optional' => true,
'implicit' => true,
'default' => '0'
] + BaseDistance::MAP,
'maximum' => [
'constant' => 1,
'optional' => true,
'implicit' => true,
] + BaseDistance::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtrees.php000064400000001116151161424250016552
0ustar00<?php
/**
* GeneralSubtrees
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* GeneralSubtrees
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class GeneralSubtrees
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => GeneralSubtree::MAP
];
}
phpseclib/phpseclib/File/ASN1/Maps/HashAlgorithm.php000064400000000704151161424250016214
0ustar00<?php
/**
* HashAglorithm
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
/**
* HashAglorithm
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class HashAlgorithm
{
const MAP = AlgorithmIdentifier::MAP;
}
phpseclib/phpseclib/File/ASN1/Maps/HoldInstructionCode.php000064400000001001151161424250017374
0ustar00<?php
/**
* HoldInstructionCode
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* HoldInstructionCode
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class HoldInstructionCode
{
const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER];
}
phpseclib/phpseclib/File/ASN1/Maps/InvalidityDate.php000064400000000761151161424250016377
0ustar00<?php
/**
* InvalidityDate
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* InvalidityDate
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class InvalidityDate
{
const MAP = ['type' => ASN1::TYPE_GENERALIZED_TIME];
}
phpseclib/phpseclib/File/ASN1/Maps/IssuerAltName.php000064400000000675151161424250016205
0ustar00<?php
/**
* IssuerAltName
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
/**
* IssuerAltName
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class IssuerAltName
{
const MAP = GeneralNames::MAP;
}
phpseclib/phpseclib/File/ASN1/Maps/IssuingDistributionPoint.php000064400000003536151161424250020523
0ustar00<?php
/**
* IssuingDistributionPoint
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* IssuingDistributionPoint
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class IssuingDistributionPoint
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'distributionPoint' => [
'constant' => 0,
'optional' => true,
'explicit' => true
] + DistributionPointName::MAP,
'onlyContainsUserCerts' => [
'type' => ASN1::TYPE_BOOLEAN,
'constant' => 1,
'optional' => true,
'default' => false,
'implicit' => true
],
'onlyContainsCACerts' => [
'type' => ASN1::TYPE_BOOLEAN,
'constant' => 2,
'optional' => true,
'default' => false,
'implicit' => true
],
'onlySomeReasons' => [
'constant' => 3,
'optional' => true,
'implicit' => true
] + ReasonFlags::MAP,
'indirectCRL' => [
'type' => ASN1::TYPE_BOOLEAN,
'constant' => 4,
'optional' => true,
'default' => false,
'implicit' => true
],
'onlyContainsAttributeCerts' => [
'type' => ASN1::TYPE_BOOLEAN,
'constant' => 5,
'optional' => true,
'default' => false,
'implicit' => true
]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/KeyIdentifier.php000064400000000752151161424250016220
0ustar00<?php
/**
* KeyIdentifier
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* KeyIdentifier
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class KeyIdentifier
{
const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
}
phpseclib/phpseclib/File/ASN1/Maps/KeyPurposeId.php000064400000000754151161424250016052
0ustar00<?php
/**
* KeyPurposeId
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* KeyPurposeId
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class KeyPurposeId
{
const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER];
}
phpseclib/phpseclib/File/ASN1/Maps/KeyUsage.php000064400000001413151161424250015175
0ustar00<?php
/**
* KeyUsage
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* KeyUsage
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class KeyUsage
{
const MAP = [
'type' => ASN1::TYPE_BIT_STRING,
'mapping' => [
'digitalSignature',
'nonRepudiation',
'keyEncipherment',
'dataEncipherment',
'keyAgreement',
'keyCertSign',
'cRLSign',
'encipherOnly',
'decipherOnly'
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/MaskGenAlgorithm.php000064400000000715151161424250016660
0ustar00<?php
/**
* MaskGenAglorithm
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
/**
* MaskGenAglorithm
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class MaskGenAlgorithm
{
const MAP = AlgorithmIdentifier::MAP;
}
phpseclib/phpseclib/File/ASN1/Maps/Name.php000064400000001050151161424250014335
0ustar00<?php
/**
* Name
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* Name
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Name
{
const MAP = [
'type' => ASN1::TYPE_CHOICE,
'children' => [
'rdnSequence' => RDNSequence::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/NameConstraints.php000064400000001601151161424250016567
0ustar00<?php
/**
* NameConstraints
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* NameConstraints
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class NameConstraints
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'permittedSubtrees' => [
'constant' => 0,
'optional' => true,
'implicit' => true
] + GeneralSubtrees::MAP,
'excludedSubtrees' => [
'constant' => 1,
'optional' => true,
'implicit' => true
] + GeneralSubtrees::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/netscape_ca_policy_url.php000064400000001003151161424250020161
0ustar00<?php
/**
* netscape_ca_policy_url
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* netscape_ca_policy_url
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class netscape_ca_policy_url
{
const MAP = ['type' => ASN1::TYPE_IA5_STRING];
}
phpseclib/phpseclib/File/ASN1/Maps/netscape_cert_type.php000064400000001512151161424250017340
0ustar00<?php
/**
* netscape_cert_type
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* netscape_cert_type
*
* mapping is from
<http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn3.html>
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class netscape_cert_type
{
const MAP = [
'type' => ASN1::TYPE_BIT_STRING,
'mapping' => [
'SSLClient',
'SSLServer',
'Email',
'ObjectSigning',
'Reserved',
'SSLCA',
'EmailCA',
'ObjectSigningCA'
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/netscape_comment.php000064400000000761151161424250017011
0ustar00<?php
/**
* netscape_comment
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* netscape_comment
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class netscape_comment
{
const MAP = ['type' => ASN1::TYPE_IA5_STRING];
}
phpseclib/phpseclib/File/ASN1/Maps/NetworkAddress.php000064400000000757151161424250016431
0ustar00<?php
/**
* NetworkAddress
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* NetworkAddress
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class NetworkAddress
{
const MAP = ['type' => ASN1::TYPE_NUMERIC_STRING];
}
phpseclib/phpseclib/File/ASN1/Maps/NoticeReference.php000064400000001442151161424250016522
0ustar00<?php
/**
* NoticeReference
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* NoticeReference
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class NoticeReference
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'organization' => DisplayText::MAP,
'noticeNumbers' => [
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => 200,
'children' => ['type' =>
ASN1::TYPE_INTEGER]
]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/NumericUserIdentifier.php000064400000001004151161424250017720
0ustar00<?php
/**
* NumericUserIdentifier
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* NumericUserIdentifier
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class NumericUserIdentifier
{
const MAP = ['type' => ASN1::TYPE_NUMERIC_STRING];
}
phpseclib/phpseclib/File/ASN1/Maps/OneAsymmetricKey.php000064400000002207151161424250016712
0ustar00<?php
/**
* OneAsymmetricKey
*
* See https://tools.ietf.org/html/rfc5958
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* OneAsymmetricKey
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class OneAsymmetricKey
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'version' => [
'type' => ASN1::TYPE_INTEGER,
'mapping' => ['v1', 'v2']
],
'privateKeyAlgorithm' => AlgorithmIdentifier::MAP,
'privateKey' => PrivateKey::MAP,
'attributes' => [
'constant' => 0,
'optional' => true,
'implicit' => true
] + Attributes::MAP,
'publicKey' => [
'constant' => 1,
'optional' => true,
'implicit' => true
] + PublicKey::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/ORAddress.php000064400000001437151161424250015314
0ustar00<?php
/**
* ORAddress
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* ORAddress
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class ORAddress
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'built-in-standard-attributes' =>
BuiltInStandardAttributes::MAP,
'built-in-domain-defined-attributes' =>
['optional' => true] + BuiltInDomainDefinedAttributes::MAP,
'extension-attributes' => ['optional'
=> true] + ExtensionAttributes::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/OrganizationalUnitNames.php000064400000001224151161424250020265
0ustar00<?php
/**
* OrganizationalUnitNames
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* OrganizationalUnitNames
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class OrganizationalUnitNames
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => 4, // ub-organizational-units
'children' => ['type' =>
ASN1::TYPE_PRINTABLE_STRING]
];
}
phpseclib/phpseclib/File/ASN1/Maps/OrganizationName.php000064400000000767151161424250016740
0ustar00<?php
/**
* OrganizationName
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* OrganizationName
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class OrganizationName
{
const MAP = ['type' => ASN1::TYPE_PRINTABLE_STRING];
}
phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfo.php000064400000001430151161424250016351
0ustar00<?php
/**
* OtherPrimeInfo
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* OtherPrimeInfo
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class OtherPrimeInfo
{
// version must be multi if otherPrimeInfos present
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'prime' => ['type' =>
ASN1::TYPE_INTEGER], // ri
'exponent' => ['type' =>
ASN1::TYPE_INTEGER], // di
'coefficient' => ['type' =>
ASN1::TYPE_INTEGER] // ti
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfos.php000064400000001206151161424250016535
0ustar00<?php
/**
* OtherPrimeInfos
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* OtherPrimeInfos
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class OtherPrimeInfos
{
// version must be multi if otherPrimeInfos present
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => OtherPrimeInfo::MAP
];
}
phpseclib/phpseclib/File/ASN1/Maps/PBEParameter.php000064400000001312151161424250015725
0ustar00<?php
/**
* PBEParameter
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* PBEParameter
*
* from https://tools.ietf.org/html/rfc2898#appendix-A.3
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PBEParameter
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'salt' => ['type' =>
ASN1::TYPE_OCTET_STRING],
'iterationCount' => ['type' =>
ASN1::TYPE_INTEGER]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/PBES2params.php000064400000001305151161424250015477
0ustar00<?php
/**
* PBES2params
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* PBES2params
*
* from https://tools.ietf.org/html/rfc2898#appendix-A.3
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PBES2params
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'keyDerivationFunc' => AlgorithmIdentifier::MAP,
'encryptionScheme' => AlgorithmIdentifier::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/PBKDF2params.php000064400000002033151161424250015573
0ustar00<?php
/**
* PBKDF2params
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* PBKDF2params
*
* from https://tools.ietf.org/html/rfc2898#appendix-A.3
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PBKDF2params
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
// technically, this is a CHOICE in RFC2898 but the other
"choice" is, currently, more of a placeholder
// in the RFC
'salt' => ['type' =>
ASN1::TYPE_OCTET_STRING],
'iterationCount' => ['type' =>
ASN1::TYPE_INTEGER],
'keyLength' => [
'type' => ASN1::TYPE_INTEGER,
'optional' => true
],
'prf' => AlgorithmIdentifier::MAP +
['optional' => true]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/PBMAC1params.php000064400000001311151161424250015564
0ustar00<?php
/**
* PBMAC1params
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* PBMAC1params
*
* from https://tools.ietf.org/html/rfc2898#appendix-A.3
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PBMAC1params
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'keyDerivationFunc' => AlgorithmIdentifier::MAP,
'messageAuthScheme' => AlgorithmIdentifier::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/Pentanomial.php000064400000001315151161424250015730
0ustar00<?php
/**
* Pentanomial
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* Pentanomial
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Pentanomial
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'k1' => ['type' =>
ASN1::TYPE_INTEGER], // k1 > 0
'k2' => ['type' =>
ASN1::TYPE_INTEGER], // k2 > k1
'k3' => ['type' =>
ASN1::TYPE_INTEGER], // k3 > h2
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/PersonalName.php000064400000002503151161424250016045
0ustar00<?php
/**
* PersonalName
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* PersonalName
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PersonalName
{
const MAP = [
'type' => ASN1::TYPE_SET,
'children' => [
'surname' => [
'type' => ASN1::TYPE_PRINTABLE_STRING,
'constant' => 0,
'optional' => true,
'implicit' => true
],
'given-name' => [
'type' => ASN1::TYPE_PRINTABLE_STRING,
'constant' => 1,
'optional' => true,
'implicit' => true
],
'initials' => [
'type' => ASN1::TYPE_PRINTABLE_STRING,
'constant' => 2,
'optional' => true,
'implicit' => true
],
'generation-qualifier' => [
'type' => ASN1::TYPE_PRINTABLE_STRING,
'constant' => 3,
'optional' => true,
'implicit' => true
]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/PKCS9String.php000064400000001203151161424250015475
0ustar00<?php
/**
* PKCS9String
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* PKCS9String
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PKCS9String
{
const MAP = [
'type' => ASN1::TYPE_CHOICE,
'children' => [
'ia5String' => ['type' =>
ASN1::TYPE_IA5_STRING],
'directoryString' => DirectoryString::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/PolicyInformation.php000064400000001515151161424250017130
0ustar00<?php
/**
* PolicyInformation
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* PolicyInformation
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PolicyInformation
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'policyIdentifier' => CertPolicyId::MAP,
'policyQualifiers' => [
'type' => ASN1::TYPE_SEQUENCE,
'min' => 0,
'max' => -1,
'optional' => true,
'children' => PolicyQualifierInfo::MAP
]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/PolicyMappings.php000064400000001416151161424250016421
0ustar00<?php
/**
* PolicyMappings
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* PolicyMappings
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PolicyMappings
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'issuerDomainPolicy' => CertPolicyId::MAP,
'subjectDomainPolicy' => CertPolicyId::MAP
]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierId.php000064400000000773151161424250017046
0ustar00<?php
/**
* PolicyQualifierId
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* PolicyQualifierId
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PolicyQualifierId
{
const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER];
}
phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierInfo.php000064400000001232151161424250017374
0ustar00<?php
/**
* PolicyQualifierInfo
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* PolicyQualifierInfo
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PolicyQualifierInfo
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'policyQualifierId' => PolicyQualifierId::MAP,
'qualifier' => ['type' =>
ASN1::TYPE_ANY]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/PostalAddress.php000064400000001145151161424250016232
0ustar00<?php
/**
* PostalAddress
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* PostalAddress
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PostalAddress
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'optional' => true,
'min' => 1,
'max' => -1,
'children' => DirectoryString::MAP
];
}
phpseclib/phpseclib/File/ASN1/Maps/Prime_p.php000064400000000723151161424250015056
0ustar00<?php
/**
* Prime_p
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* Prime_p
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Prime_p
{
const MAP = ['type' => ASN1::TYPE_INTEGER];
}
phpseclib/phpseclib/File/ASN1/Maps/PrivateDomainName.php000064400000001244151161424250017025
0ustar00<?php
/**
* PrivateDomainName
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* PrivateDomainName
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PrivateDomainName
{
const MAP = [
'type' => ASN1::TYPE_CHOICE,
'children' => [
'numeric' => ['type' =>
ASN1::TYPE_NUMERIC_STRING],
'printable' => ['type' =>
ASN1::TYPE_PRINTABLE_STRING]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/PrivateKey.php000064400000000741151161424250015546
0ustar00<?php
/**
* PrivateKey
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* PrivateKey
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PrivateKey
{
const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
}
phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyInfo.php000064400000001650151161424250016362
0ustar00<?php
/**
* PrivateKeyInfo
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* PrivateKeyInfo
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PrivateKeyInfo
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'version' => [
'type' => ASN1::TYPE_INTEGER,
'mapping' => ['v1']
],
'privateKeyAlgorithm' => AlgorithmIdentifier::MAP,
'privateKey' => PrivateKey::MAP,
'attributes' => [
'constant' => 0,
'optional' => true,
'implicit' => true
] + Attributes::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyUsagePeriod.php000064400000001651151161424250017677
0ustar00<?php
/**
* PrivateKeyUsagePeriod
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* PrivateKeyUsagePeriod
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PrivateKeyUsagePeriod
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'notBefore' => [
'constant' => 0,
'optional' => true,
'implicit' => true,
'type' => ASN1::TYPE_GENERALIZED_TIME],
'notAfter' => [
'constant' => 1,
'optional' => true,
'implicit' => true,
'type' => ASN1::TYPE_GENERALIZED_TIME]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/PublicKey.php000064400000000734151161424250015354
0ustar00<?php
/**
* PublicKey
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* PublicKey
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PublicKey
{
const MAP = ['type' => ASN1::TYPE_BIT_STRING];
}
phpseclib/phpseclib/File/ASN1/Maps/PublicKeyAndChallenge.php000064400000001235151161424250017577
0ustar00<?php
/**
* PublicKeyAndChallenge
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* PublicKeyAndChallenge
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PublicKeyAndChallenge
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'spki' => SubjectPublicKeyInfo::MAP,
'challenge' => ['type' =>
ASN1::TYPE_IA5_STRING]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/PublicKeyInfo.php000064400000001455151161424250016171
0ustar00<?php
/**
* PublicKeyInfo
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* PublicKeyInfo
*
* this format is not formally defined anywhere but is none-the-less the
form you
* get when you do "openssl rsa -in private.pem -outform PEM
-pubout"
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PublicKeyInfo
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'publicKeyAlgorithm' => AlgorithmIdentifier::MAP,
'publicKey' => ['type' =>
ASN1::TYPE_BIT_STRING]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/RC2CBCParameter.php000064400000001431151161424250016217
0ustar00<?php
/**
* RC2CBCParameter
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* RC2CBCParameter
*
* from https://tools.ietf.org/html/rfc2898#appendix-A.3
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class RC2CBCParameter
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'rc2ParametersVersion' => [
'type' => ASN1::TYPE_INTEGER,
'optional' => true
],
'iv' => ['type' =>
ASN1::TYPE_OCTET_STRING]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/RDNSequence.php000064400000002010151161424250015566
0ustar00<?php
/**
* RDNSequence
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* RDNSequence
*
* In practice, RDNs containing multiple name-value pairs (called
"multivalued RDNs") are rare,
* but they can be useful at times when either there is no unique attribute
in the entry or you
* want to ensure that the entry's DN contains some useful identifying
information.
*
* - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class RDNSequence
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
// RDNSequence does not define a min or a max, which means it
doesn't have one
'min' => 0,
'max' => -1,
'children' => RelativeDistinguishedName::MAP
];
}
phpseclib/phpseclib/File/ASN1/Maps/ReasonFlags.php000064400000001435151161424250015670
0ustar00<?php
/**
* ReasonFlags
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* ReasonFlags
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class ReasonFlags
{
const MAP = [
'type' => ASN1::TYPE_BIT_STRING,
'mapping' => [
'unused',
'keyCompromise',
'cACompromise',
'affiliationChanged',
'superseded',
'cessationOfOperation',
'certificateHold',
'privilegeWithdrawn',
'aACompromise'
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/RelativeDistinguishedName.php000064400000001722151161424250020563
0ustar00<?php
/**
* RelativeDistinguishedName
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* RelativeDistinguishedName
*
* In practice, RDNs containing multiple name-value pairs (called
"multivalued RDNs") are rare,
* but they can be useful at times when either there is no unique attribute
in the entry or you
* want to ensure that the entry's DN contains some useful identifying
information.
*
* - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class RelativeDistinguishedName
{
const MAP = [
'type' => ASN1::TYPE_SET,
'min' => 1,
'max' => -1,
'children' => AttributeTypeAndValue::MAP
];
}
phpseclib/phpseclib/File/ASN1/Maps/RevokedCertificate.php000064400000001371151161424250017225
0ustar00<?php
/**
* RevokedCertificate
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* RevokedCertificate
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class RevokedCertificate
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'userCertificate' => CertificateSerialNumber::MAP,
'revocationDate' => Time::MAP,
'crlEntryExtensions' => [
'optional' => true
] + Extensions::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/RSAPrivateKey.php000064400000002577151161424250016125
0ustar00<?php
/**
* RSAPrivateKey
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* RSAPrivateKey
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class RSAPrivateKey
{
// version must be multi if otherPrimeInfos present
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'version' => [
'type' => ASN1::TYPE_INTEGER,
'mapping' => ['two-prime',
'multi']
],
'modulus' => ['type' =>
ASN1::TYPE_INTEGER], // n
'publicExponent' => ['type' =>
ASN1::TYPE_INTEGER], // e
'privateExponent' => ['type' =>
ASN1::TYPE_INTEGER], // d
'prime1' => ['type' =>
ASN1::TYPE_INTEGER], // p
'prime2' => ['type' =>
ASN1::TYPE_INTEGER], // q
'exponent1' => ['type' =>
ASN1::TYPE_INTEGER], // d mod (p-1)
'exponent2' => ['type' =>
ASN1::TYPE_INTEGER], // d mod (q-1)
'coefficient' => ['type' =>
ASN1::TYPE_INTEGER], // (inverse of q) mod p
'otherPrimeInfos' => OtherPrimeInfos::MAP +
['optional' => true]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/RSAPublicKey.php000064400000001214151161424250015714
0ustar00<?php
/**
* RSAPublicKey
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* RSAPublicKey
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class RSAPublicKey
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'modulus' => ['type' =>
ASN1::TYPE_INTEGER],
'publicExponent' => ['type' =>
ASN1::TYPE_INTEGER]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/RSASSA_PSS_params.php000064400000002767151161424250016561
0ustar00<?php
/**
* RSASSA_PSS_params
*
* As defined in https://tools.ietf.org/html/rfc4055#section-3.1
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* RSASSA_PSS_params
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class RSASSA_PSS_params
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'hashAlgorithm' => [
'constant' => 0,
'optional' => true,
'explicit' => true,
//'default' => 'sha1Identifier'
] + HashAlgorithm::MAP,
'maskGenAlgorithm' => [
'constant' => 1,
'optional' => true,
'explicit' => true,
//'default' => 'mgf1SHA1Identifier'
] + MaskGenAlgorithm::MAP,
'saltLength' => [
'type' => ASN1::TYPE_INTEGER,
'constant' => 2,
'optional' => true,
'explicit' => true,
'default' => 20
],
'trailerField' => [
'type' => ASN1::TYPE_INTEGER,
'constant' => 3,
'optional' => true,
'explicit' => true,
'default' => 1
]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/SignedPublicKeyAndChallenge.php000064400000001377151161424250020740
0ustar00<?php
/**
* SignedPublicKeyAndChallenge
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* SignedPublicKeyAndChallenge
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class SignedPublicKeyAndChallenge
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'publicKeyAndChallenge' =>
PublicKeyAndChallenge::MAP,
'signatureAlgorithm' => AlgorithmIdentifier::MAP,
'signature' => ['type' =>
ASN1::TYPE_BIT_STRING]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/SpecifiedECDomain.php000064400000002130151161424250016710
0ustar00<?php
/**
* SpecifiedECDomain
*
* From: http://www.secg.org/sec1-v2.pdf#page=109
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* SpecifiedECDomain
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class SpecifiedECDomain
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'version' => [
'type' => ASN1::TYPE_INTEGER,
'mapping' => [1 => 'ecdpVer1',
'ecdpVer2', 'ecdpVer3']
],
'fieldID' => FieldID::MAP,
'curve' => Curve::MAP,
'base' => ECPoint::MAP,
'order' => ['type' =>
ASN1::TYPE_INTEGER],
'cofactor' => [
'type' => ASN1::TYPE_INTEGER,
'optional' => true
],
'hash' => ['optional' => true] +
HashAlgorithm::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/SubjectAltName.php000064400000000700151161424250016317
0ustar00<?php
/**
* SubjectAltName
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
/**
* SubjectAltName
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class SubjectAltName
{
const MAP = GeneralNames::MAP;
}
phpseclib/phpseclib/File/ASN1/Maps/SubjectDirectoryAttributes.php000064400000001152151161424250021013
0ustar00<?php
/**
* SubjectDirectoryAttributes
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* SubjectDirectoryAttributes
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class SubjectDirectoryAttributes
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => Attribute::MAP
];
}
phpseclib/phpseclib/File/ASN1/Maps/TBSCertificate.php000064400000004127151161424250016260
0ustar00<?php
/**
* TBSCertificate
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* TBSCertificate
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class TBSCertificate
{
// assert($TBSCertificate['children']['signature']
== $Certificate['children']['signatureAlgorithm'])
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
// technically, default implies optional, but we'll define
it as being optional, none-the-less, just to
// reenforce that fact
'version' => [
'type' => ASN1::TYPE_INTEGER,
'constant' => 0,
'optional' => true,
'explicit' => true,
'mapping' => ['v1', 'v2',
'v3'],
'default' => 'v1'
],
'serialNumber' => CertificateSerialNumber::MAP,
'signature' => AlgorithmIdentifier::MAP,
'issuer' => Name::MAP,
'validity' => Validity::MAP,
'subject' => Name::MAP,
'subjectPublicKeyInfo' =>
SubjectPublicKeyInfo::MAP,
// implicit means that the T in the TLV structure is to be
rewritten, regardless of the type
'issuerUniqueID' => [
'constant' => 1,
'optional' => true,
'implicit' => true
] + UniqueIdentifier::MAP,
'subjectUniqueID' => [
'constant' => 2,
'optional' => true,
'implicit' => true
] + UniqueIdentifier::MAP,
// <http://tools.ietf.org/html/rfc2459#page-74>
doesn't use the EXPLICIT keyword but if
// it's not IMPLICIT, it's EXPLICIT
'extensions' => [
'constant' => 3,
'optional' => true,
'explicit' => true
] + Extensions::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/SubjectInfoAccessSyntax.php000064400000001151151161424250020223
0ustar00<?php
/**
* SubjectInfoAccessSyntax
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* SubjectInfoAccessSyntax
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class SubjectInfoAccessSyntax
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => AccessDescription::MAP
];
}
phpseclib/phpseclib/File/ASN1/Maps/SubjectPublicKeyInfo.php000064400000001245151161424250017506
0ustar00<?php
/**
* SubjectPublicKeyInfo
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* SubjectPublicKeyInfo
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class SubjectPublicKeyInfo
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'algorithm' => AlgorithmIdentifier::MAP,
'subjectPublicKey' => ['type' =>
ASN1::TYPE_BIT_STRING]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/TBSCertList.php000064400000002534151161424250015567
0ustar00<?php
/**
* TBSCertList
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* TBSCertList
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class TBSCertList
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'version' => [
'type' => ASN1::TYPE_INTEGER,
'mapping' => ['v1', 'v2',
'v3'],
'optional' => true,
'default' => 'v2'
],
'signature' => AlgorithmIdentifier::MAP,
'issuer' => Name::MAP,
'thisUpdate' => Time::MAP,
'nextUpdate' => [
'optional' => true
] + Time::MAP,
'revokedCertificates' => [
'type' => ASN1::TYPE_SEQUENCE,
'optional' => true,
'min' => 0,
'max' => -1,
'children' => RevokedCertificate::MAP
],
'crlExtensions' => [
'constant' => 0,
'optional' => true,
'explicit' => true
] + Extensions::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/TerminalIdentifier.php000064400000000775151161424250017250
0ustar00<?php
/**
* TerminalIdentifier
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* TerminalIdentifier
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class TerminalIdentifier
{
const MAP = ['type' => ASN1::TYPE_PRINTABLE_STRING];
}
phpseclib/phpseclib/File/ASN1/Maps/Time.php000064400000001171151161424250014357
0ustar00<?php
/**
* Time
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* Time
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Time
{
const MAP = [
'type' => ASN1::TYPE_CHOICE,
'children' => [
'utcTime' => ['type' =>
ASN1::TYPE_UTC_TIME],
'generalTime' => ['type' =>
ASN1::TYPE_GENERALIZED_TIME]
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/Trinomial.php000064400000000731151161424250015420
0ustar00<?php
/**
* Trinomial
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* Trinomial
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Trinomial
{
const MAP = ['type' => ASN1::TYPE_INTEGER];
}
phpseclib/phpseclib/File/ASN1/Maps/UniqueIdentifier.php000064400000000761151161424250016736
0ustar00<?php
/**
* UniqueIdentifier
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* UniqueIdentifier
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class UniqueIdentifier
{
const MAP = ['type' => ASN1::TYPE_BIT_STRING];
}
phpseclib/phpseclib/File/ASN1/Maps/UserNotice.php000064400000001440151161424250015540
0ustar00<?php
/**
* UserNotice
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* UserNotice
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class UserNotice
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'noticeRef' => [
'optional' => true,
'implicit' => true
] + NoticeReference::MAP,
'explicitText' => [
'optional' => true,
'implicit' => true
] + DisplayText::MAP
]
];
}
phpseclib/phpseclib/File/ASN1/Maps/Validity.php000064400000001122151161424250015242
0ustar00<?php
/**
* Validity
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\File\ASN1\Maps;
use phpseclib3\File\ASN1;
/**
* Validity
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Validity
{
const MAP = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'notBefore' => Time::MAP,
'notAfter' => Time::MAP
]
];
}
phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Base.php000064400000004547151161424270017464
0ustar00<?php
/**
* Modular Exponentiation Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines\BCMath;
use phpseclib3\Math\BigInteger\Engines\BCMath;
/**
* Sliding Window Exponentiation Engine
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Base extends BCMath
{
/**
* Cache constants
*
* $cache[self::VARIABLE] tells us whether or not the cached data is
still valid.
*
*/
const VARIABLE = 0;
/**
* $cache[self::DATA] contains the cached data.
*
*/
const DATA = 1;
/**
* Test for engine validity
*
* @return bool
*/
public static function isValidEngine()
{
return static::class != __CLASS__;
}
/**
* Performs modular exponentiation.
*
* @param BCMath $x
* @param BCMath $e
* @param BCMath $n
* @param string $class
* @return BCMath
*/
protected static function powModHelper(BCMath $x, BCMath $e, BCMath $n,
$class)
{
if (empty($e->value)) {
$temp = new $class();
$temp->value = '1';
return $x->normalize($temp);
}
return $x->normalize(static::slidingWindow($x, $e, $n, $class));
}
/**
* Modular reduction preparation
*
* @param string $x
* @param string $n
* @param string $class
* @see self::slidingWindow()
* @return string
*/
protected static function prepareReduce($x, $n, $class)
{
return static::reduce($x, $n);
}
/**
* Modular multiply
*
* @param string $x
* @param string $y
* @param string $n
* @param string $class
* @see self::slidingWindow()
* @return string
*/
protected static function multiplyReduce($x, $y, $n, $class)
{
return static::reduce(bcmul($x, $y), $n);
}
/**
* Modular square
*
* @param string $x
* @param string $n
* @param string $class
* @see self::slidingWindow()
* @return string
*/
protected static function squareReduce($x, $n, $class)
{
return static::reduce(bcmul($x, $x), $n);
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/BuiltIn.php000064400000001657151161424270020157
0ustar00<?php
/**
* Built-In BCMath Modular Exponentiation Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines\BCMath;
use phpseclib3\Math\BigInteger\Engines\BCMath;
/**
* Built-In BCMath Modular Exponentiation Engine
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class BuiltIn extends BCMath
{
/**
* Performs modular exponentiation.
*
* @param BCMath $x
* @param BCMath $e
* @param BCMath $n
* @return BCMath
*/
protected static function powModHelper(BCMath $x, BCMath $e, BCMath $n)
{
$temp = new BCMath();
$temp->value = bcpowmod($x->value, $e->value,
$n->value);
return $x->normalize($temp);
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/DefaultEngine.php000064400000001111151161424270021304
0ustar00<?php
/**
* BCMath Default Modular Exponentiation Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines\BCMath;
use phpseclib3\Math\BigInteger\Engines\BCMath\Reductions\Barrett;
/**
* PHP Default Modular Exponentiation Engine
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class DefaultEngine extends Barrett
{
}
phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/OpenSSL.php000064400000001067151161424270020067
0ustar00<?php
/**
* OpenSSL Modular Exponentiation Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines\BCMath;
use phpseclib3\Math\BigInteger\Engines\OpenSSL as Progenitor;
/**
* OpenSSL Modular Exponentiation Engine
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class OpenSSL extends Progenitor
{
}
phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/Barrett.php000064400000015021151161424270022321
0ustar00<?php
/**
* BCMath Barrett Modular Exponentiation Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines\BCMath\Reductions;
use phpseclib3\Math\BigInteger\Engines\BCMath\Base;
/**
* PHP Barrett Modular Exponentiation Engine
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Barrett extends Base
{
/**
* Cache constants
*
* $cache[self::VARIABLE] tells us whether or not the cached data is
still valid.
*
*/
const VARIABLE = 0;
/**
* $cache[self::DATA] contains the cached data.
*
*/
const DATA = 1;
/**
* Barrett Modular Reduction
*
* See {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3}
/
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM
6.2.5} for more information. Modified slightly,
* so as not to require negative numbers (initially, this script
didn't support negative numbers).
*
* Employs "folding", as described at
* {@link
http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66
thesis-149.pdf#page=66}. To quote from
* it, "the idea [behind folding] is to find a value x' such
that x (mod m) = x' (mod m), with x' being smaller than x."
*
* Unfortunately, the "Barrett Reduction with Folding"
algorithm described in thesis-149.pdf is not, as written, all that
* usable on account of (1) its not using reasonable radix points as
discussed in
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM
6.2.2} and (2) the fact that, even with reasonable
* radix points, it only works when there are an even number of digits
in the denominator. The reason for (2) is that
* (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even,
they're the same, but if x is odd, they're not. See the in-line
* comments for details.
*
* @param string $n
* @param string $m
* @return string
*/
protected static function reduce($n, $m)
{
static $cache = [
self::VARIABLE => [],
self::DATA => []
];
$m_length = strlen($m);
if (strlen($n) > 2 * $m_length) {
return bcmod($n, $m);
}
// if (m.length >> 1) + 2 <= m.length then m is too small
and n can't be reduced
if ($m_length < 5) {
return self::regularBarrett($n, $m);
}
// n = 2 * m.length
if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]);
$cache[self::VARIABLE][] = $m;
$lhs = '1' . str_repeat('0', $m_length +
($m_length >> 1));
$u = bcdiv($lhs, $m, 0);
$m1 = bcsub($lhs, bcmul($u, $m));
$cache[self::DATA][] = [
'u' => $u, // m.length >> 1 (technically
(m.length >> 1) + 1)
'm1' => $m1 // m.length
];
} else {
extract($cache[self::DATA][$key]);
}
$cutoff = $m_length + ($m_length >> 1);
$lsd = substr($n, -$cutoff);
$msd = substr($n, 0, -$cutoff);
$temp = bcmul($msd, $m1); // m.length + (m.length >> 1)
$n = bcadd($lsd, $temp); // m.length + (m.length >> 1) + 1
(so basically we're adding two same length numbers)
//if ($m_length & 1) {
// return self::regularBarrett($n, $m);
//}
// (m.length + (m.length >> 1) + 1) - (m.length - 1) ==
(m.length >> 1) + 2
$temp = substr($n, 0, -$m_length + 1);
// if even: ((m.length >> 1) + 2) + (m.length >> 1) ==
m.length + 2
// if odd: ((m.length >> 1) + 2) + (m.length >> 1) ==
(m.length - 1) + 2 == m.length + 1
$temp = bcmul($temp, $u);
// if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length
- (m.length >> 1) + 1
// if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length
- (m.length >> 1)
$temp = substr($temp, 0, -($m_length >> 1) - 1);
// if even: (m.length - (m.length >> 1) + 1) + m.length = 2 *
m.length - (m.length >> 1) + 1
// if odd: (m.length - (m.length >> 1)) + m.length = 2 *
m.length - (m.length >> 1)
$temp = bcmul($temp, $m);
// at this point, if m had an odd number of digits, we'd be
subtracting a 2 * m.length - (m.length >> 1) digit
// number from a m.length + (m.length >> 1) + 1 digit number.
ie. there'd be an extra digit and the while loop
// following this comment would loop a lot (hence our calling
_regularBarrett() in that situation).
$result = bcsub($n, $temp);
//if (bccomp($result, '0') < 0) {
if ($result[0] == '-') {
$temp = '1' . str_repeat('0', $m_length +
1);
$result = bcadd($result, $temp);
}
while (bccomp($result, $m) >= 0) {
$result = bcsub($result, $m);
}
return $result;
}
/**
* (Regular) Barrett Modular Reduction
*
* For numbers with more than four digits BigInteger::_barrett() is
faster. The difference between that and this
* is that this function does not fold the denominator into a smaller
form.
*
* @param string $x
* @param string $n
* @return string
*/
private static function regularBarrett($x, $n)
{
static $cache = [
self::VARIABLE => [],
self::DATA => []
];
$n_length = strlen($n);
if (strlen($x) > 2 * $n_length) {
return bcmod($x, $n);
}
if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]);
$cache[self::VARIABLE][] = $n;
$lhs = '1' . str_repeat('0', 2 *
$n_length);
$cache[self::DATA][] = bcdiv($lhs, $n, 0);
}
$temp = substr($x, 0, -$n_length + 1);
$temp = bcmul($temp, $cache[self::DATA][$key]);
$temp = substr($temp, 0, -$n_length - 1);
$r1 = substr($x, -$n_length - 1);
$r2 = substr(bcmul($temp, $n), -$n_length - 1);
$result = bcsub($r1, $r2);
//if (bccomp($result, '0') < 0) {
if ($result[0] == '-') {
$q = '1' . str_repeat('0', $n_length + 1);
$result = bcadd($result, $q);
}
while (bccomp($result, $n) >= 0) {
$result = bcsub($result, $n);
}
return $result;
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php000064400000005502151161424270023134
0ustar00<?php
/**
* BCMath Dynamic Barrett Modular Exponentiation Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines\BCMath\Reductions;
use phpseclib3\Math\BigInteger\Engines\BCMath;
use phpseclib3\Math\BigInteger\Engines\BCMath\Base;
/**
* PHP Barrett Modular Exponentiation Engine
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class EvalBarrett extends Base
{
/**
* Custom Reduction Function
*
* @see self::generateCustomReduction
*/
private static $custom_reduction;
/**
* Barrett Modular Reduction
*
* This calls a dynamically generated loop unrolled function
that's specific to a given modulo.
* Array lookups are avoided as are if statements testing for how many
bits the host OS supports, etc.
*
* @param string $n
* @param string $m
* @return string
*/
protected static function reduce($n, $m)
{
$inline = self::$custom_reduction;
return $inline($n);
}
/**
* Generate Custom Reduction
*
* @param BCMath $m
* @param string $class
* @return callable|void
*/
protected static function generateCustomReduction(BCMath $m, $class)
{
$m_length = strlen($m);
if ($m_length < 5) {
$code = 'return bcmod($x, $n);';
eval('$func = function ($n) { ' . $code .
'};');
self::$custom_reduction = $func;
return;
}
$lhs = '1' . str_repeat('0', $m_length +
($m_length >> 1));
$u = bcdiv($lhs, $m, 0);
$m1 = bcsub($lhs, bcmul($u, $m));
$cutoff = $m_length + ($m_length >> 1);
$m = "'$m'";
$u = "'$u'";
$m1 = "'$m1'";
$code = '
$lsd = substr($n, -' . $cutoff . ');
$msd = substr($n, 0, -' . $cutoff . ');
$temp = bcmul($msd, ' . $m1 . ');
$n = bcadd($lsd, $temp);
$temp = substr($n, 0, ' . (-$m_length + 1) . ');
$temp = bcmul($temp, ' . $u . ');
$temp = substr($temp, 0, ' . (-($m_length >> 1) - 1)
. ');
$temp = bcmul($temp, ' . $m . ');
$result = bcsub($n, $temp);
if ($result[0] == \'-\') {
$temp = \'1' . str_repeat('0',
$m_length + 1) . '\';
$result = bcadd($result, $temp);
}
while (bccomp($result, ' . $m . ') >= 0) {
$result = bcsub($result, ' . $m . ');
}
return $result;';
eval('$func = function ($n) { ' . $code .
'};');
self::$custom_reduction = $func;
return $func;
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/BCMath.php000064400000042454151161424270016611
0ustar00<?php
/**
* BCMath BigInteger Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Exception\BadConfigurationException;
/**
* BCMath Engine.
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class BCMath extends Engine
{
/**
* Can Bitwise operations be done fast?
*
* @see parent::bitwise_leftRotate()
* @see parent::bitwise_rightRotate()
*/
const FAST_BITWISE = false;
/**
* Engine Directory
*
* @see parent::setModExpEngine
*/
const ENGINE_DIR = 'BCMath';
/**
* Test for engine validity
*
* @return bool
* @see parent::__construct()
*/
public static function isValidEngine()
{
return extension_loaded('bcmath');
}
/**
* Default constructor
*
* @param mixed $x integer Base-10 number or base-$base number if $base
set.
* @param int $base
* @see parent::__construct()
*/
public function __construct($x = 0, $base = 10)
{
if (!isset(static::$isValidEngine[static::class])) {
static::$isValidEngine[static::class] = self::isValidEngine();
}
if (!static::$isValidEngine[static::class]) {
throw new BadConfigurationException('BCMath is not setup
correctly on this system');
}
$this->value = '0';
parent::__construct($x, $base);
}
/**
* Initialize a BCMath BigInteger Engine instance
*
* @param int $base
* @see parent::__construct()
*/
protected function initialize($base)
{
switch (abs($base)) {
case 256:
// round $len to the nearest 4
$len = (strlen($this->value) + 3) & ~3;
$x = str_pad($this->value, $len, chr(0), STR_PAD_LEFT);
$this->value = '0';
for ($i = 0; $i < $len; $i += 4) {
$this->value = bcmul($this->value,
'4294967296', 0); // 4294967296 == 2**32
$this->value = bcadd(
$this->value,
0x1000000 * ord($x[$i]) + ((ord($x[$i + 1])
<< 16) | (ord(
$x[$i + 2]
) << 8) | ord($x[$i + 3])),
0
);
}
if ($this->is_negative) {
$this->value = '-' . $this->value;
}
break;
case 16:
$x = (strlen($this->value) & 1) ? '0' .
$this->value : $this->value;
$temp = new self(Strings::hex2bin($x), 256);
$this->value = $this->is_negative ? '-' .
$temp->value : $temp->value;
$this->is_negative = false;
break;
case 10:
// explicitly casting $x to a string is necessary, here,
since doing $x[0] on -1 yields different
// results then doing it on '-1' does (modInverse
does $x[0])
$this->value = $this->value === '-' ?
'0' : (string)$this->value;
}
}
/**
* Converts a BigInteger to a base-10 number.
*
* @return string
*/
public function toString()
{
if ($this->value === '0') {
return '0';
}
return ltrim($this->value, '0');
}
/**
* Converts a BigInteger to a byte string (eg. base-256).
*
* @param bool $twos_compliment
* @return string
*/
public function toBytes($twos_compliment = false)
{
if ($twos_compliment) {
return $this->toBytesHelper();
}
$value = '';
$current = $this->value;
if ($current[0] == '-') {
$current = substr($current, 1);
}
while (bccomp($current, '0', 0) > 0) {
$temp = bcmod($current, '16777216');
$value = chr($temp >> 16) . chr($temp >> 8) .
chr($temp) . $value;
$current = bcdiv($current, '16777216', 0);
}
return $this->precision > 0 ?
substr(str_pad($value, $this->precision >> 3, chr(0),
STR_PAD_LEFT), -($this->precision >> 3)) :
ltrim($value, chr(0));
}
/**
* Adds two BigIntegers.
*
* @param BCMath $y
* @return BCMath
*/
public function add(BCMath $y)
{
$temp = new self();
$temp->value = bcadd($this->value, $y->value);
return $this->normalize($temp);
}
/**
* Subtracts two BigIntegers.
*
* @param BCMath $y
* @return BCMath
*/
public function subtract(BCMath $y)
{
$temp = new self();
$temp->value = bcsub($this->value, $y->value);
return $this->normalize($temp);
}
/**
* Multiplies two BigIntegers.
*
* @param BCMath $x
* @return BCMath
*/
public function multiply(BCMath $x)
{
$temp = new self();
$temp->value = bcmul($this->value, $x->value);
return $this->normalize($temp);
}
/**
* Divides two BigIntegers.
*
* Returns an array whose first element contains the quotient and whose
second element contains the
* "common residue". If the remainder would be positive, the
"common residue" and the remainder are the
* same. If the remainder would be negative, the "common
residue" is equal to the sum of the remainder
* and the divisor (basically, the "common residue" is the
first positive modulo).
*
* @param BCMath $y
* @return array{static, static}
*/
public function divide(BCMath $y)
{
$quotient = new self();
$remainder = new self();
$quotient->value = bcdiv($this->value, $y->value, 0);
$remainder->value = bcmod($this->value, $y->value);
if ($remainder->value[0] == '-') {
$remainder->value = bcadd($remainder->value,
$y->value[0] == '-' ? substr($y->value, 1) : $y->value,
0);
}
return [$this->normalize($quotient),
$this->normalize($remainder)];
}
/**
* Calculates modular inverses.
*
* Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found
using modular inverses.
*
* @param BCMath $n
* @return false|BCMath
*/
public function modInverse(BCMath $n)
{
return $this->modInverseHelper($n);
}
/**
* Calculates the greatest common divisor and Bezout's identity.
*
* Say you have 693 and 609. The GCD is 21. Bezout's identity
states that there exist integers x and y such that
* 693*x + 609*y == 21. In point of fact, there are actually an
infinite number of x and y combinations and which
* combination is returned is dependent upon which mode is in use. See
* {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity
Bezout's identity - Wikipedia} for more information.
*
* @param BCMath $n
* @return array{gcd: static, x: static, y: static}
*/
public function extendedGCD(BCMath $n)
{
// it might be faster to use the binary xGCD algorithim here, as
well, but (1) that algorithim works
// best when the base is a power of 2 and (2) i don't think
it'd make much difference, anyway. as is,
// the basic extended euclidean algorithim is what we're
using.
$u = $this->value;
$v = $n->value;
$a = '1';
$b = '0';
$c = '0';
$d = '1';
while (bccomp($v, '0', 0) != 0) {
$q = bcdiv($u, $v, 0);
$temp = $u;
$u = $v;
$v = bcsub($temp, bcmul($v, $q, 0), 0);
$temp = $a;
$a = $c;
$c = bcsub($temp, bcmul($a, $q, 0), 0);
$temp = $b;
$b = $d;
$d = bcsub($temp, bcmul($b, $q, 0), 0);
}
return [
'gcd' => $this->normalize(new static($u)),
'x' => $this->normalize(new static($a)),
'y' => $this->normalize(new static($b))
];
}
/**
* Calculates the greatest common divisor
*
* Say you have 693 and 609. The GCD is 21.
*
* @param BCMath $n
* @return BCMath
*/
public function gcd(BCMath $n)
{
extract($this->extendedGCD($n));
/** @var BCMath $gcd */
return $gcd;
}
/**
* Absolute value.
*
* @return BCMath
*/
public function abs()
{
$temp = new static();
$temp->value = strlen($this->value) &&
$this->value[0] == '-' ?
substr($this->value, 1) :
$this->value;
return $temp;
}
/**
* Logical And
*
* @param BCMath $x
* @return BCMath
*/
public function bitwise_and(BCMath $x)
{
return $this->bitwiseAndHelper($x);
}
/**
* Logical Or
*
* @param BCMath $x
* @return BCMath
*/
public function bitwise_or(BCMath $x)
{
return $this->bitwiseXorHelper($x);
}
/**
* Logical Exclusive Or
*
* @param BCMath $x
* @return BCMath
*/
public function bitwise_xor(BCMath $x)
{
return $this->bitwiseXorHelper($x);
}
/**
* Logical Right Shift
*
* Shifts BigInteger's by $shift bits, effectively dividing by
2**$shift.
*
* @param int $shift
* @return BCMath
*/
public function bitwise_rightShift($shift)
{
$temp = new static();
$temp->value = bcdiv($this->value, bcpow('2',
$shift, 0), 0);
return $this->normalize($temp);
}
/**
* Logical Left Shift
*
* Shifts BigInteger's by $shift bits, effectively multiplying by
2**$shift.
*
* @param int $shift
* @return BCMath
*/
public function bitwise_leftShift($shift)
{
$temp = new static();
$temp->value = bcmul($this->value, bcpow('2',
$shift, 0), 0);
return $this->normalize($temp);
}
/**
* Compares two numbers.
*
* Although one might think !$x->compare($y) means $x != $y, it, in
fact, means the opposite. The reason for this
* is demonstrated thusly:
*
* $x > $y: $x->compare($y) > 0
* $x < $y: $x->compare($y) < 0
* $x == $y: $x->compare($y) == 0
*
* Note how the same comparison operator is used. If you want to test
for equality, use $x->equals($y).
*
* {@internal Could return $this->subtract($x), but that's not
as fast as what we do do.}
*
* @param BCMath $y
* @return int in case < 0 if $this is less than $y; > 0 if $this
is greater than $y, and 0 if they are equal.
* @see self::equals()
*/
public function compare(BCMath $y)
{
return bccomp($this->value, $y->value, 0);
}
/**
* Tests the equality of two numbers.
*
* If you need to see if one number is greater than or less than
another number, use BigInteger::compare()
*
* @param BCMath $x
* @return bool
*/
public function equals(BCMath $x)
{
return $this->value == $x->value;
}
/**
* Performs modular exponentiation.
*
* @param BCMath $e
* @param BCMath $n
* @return BCMath
*/
public function modPow(BCMath $e, BCMath $n)
{
return $this->powModOuter($e, $n);
}
/**
* Performs modular exponentiation.
*
* Alias for modPow().
*
* @param BCMath $e
* @param BCMath $n
* @return BCMath
*/
public function powMod(BCMath $e, BCMath $n)
{
return $this->powModOuter($e, $n);
}
/**
* Performs modular exponentiation.
*
* @param BCMath $e
* @param BCMath $n
* @return BCMath
*/
protected function powModInner(BCMath $e, BCMath $n)
{
try {
$class = static::$modexpEngine[static::class];
return $class::powModHelper($this, $e, $n, static::class);
} catch (\Exception $err) {
return BCMath\DefaultEngine::powModHelper($this, $e, $n,
static::class);
}
}
/**
* Normalize
*
* Removes leading zeros and truncates (if necessary) to maintain the
appropriate precision
*
* @param BCMath $result
* @return BCMath
*/
protected function normalize(BCMath $result)
{
$result->precision = $this->precision;
$result->bitmask = $this->bitmask;
if ($result->bitmask !== false) {
$result->value = bcmod($result->value,
$result->bitmask->value);
}
return $result;
}
/**
* Generate a random prime number between a range
*
* If there's not a prime within the given range, false will be
returned.
*
* @param BCMath $min
* @param BCMath $max
* @return false|BCMath
*/
public static function randomRangePrime(BCMath $min, BCMath $max)
{
return self::randomRangePrimeOuter($min, $max);
}
/**
* Generate a random number between a range
*
* Returns a random number between $min and $max where $min and $max
* can be defined using one of the two methods:
*
* BigInteger::randomRange($min, $max)
* BigInteger::randomRange($max, $min)
*
* @param BCMath $min
* @param BCMath $max
* @return BCMath
*/
public static function randomRange(BCMath $min, BCMath $max)
{
return self::randomRangeHelper($min, $max);
}
/**
* Make the current number odd
*
* If the current number is odd it'll be unchanged. If it's
even, one will be added to it.
*
* @see self::randomPrime()
*/
protected function make_odd()
{
if (!$this->isOdd()) {
$this->value = bcadd($this->value, '1');
}
}
/**
* Test the number against small primes.
*
* @see self::isPrime()
*/
protected function testSmallPrimes()
{
if ($this->value === '1') {
return false;
}
if ($this->value === '2') {
return true;
}
if ($this->value[strlen($this->value) - 1] % 2 == 0) {
return false;
}
$value = $this->value;
foreach (self::PRIMES as $prime) {
$r = bcmod($this->value, $prime);
if ($r == '0') {
return $this->value == $prime;
}
}
return true;
}
/**
* Scan for 1 and right shift by that amount
*
* ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n,
gmp_pow(gmp_init('2'), $s));
*
* @param BCMath $r
* @return int
* @see self::isPrime()
*/
public static function scan1divide(BCMath $r)
{
$r_value = &$r->value;
$s = 0;
// if $n was 1, $r would be 0 and this would be an infinite loop,
hence our $this->equals(static::$one[static::class]) check earlier
while ($r_value[strlen($r_value) - 1] % 2 == 0) {
$r_value = bcdiv($r_value, '2', 0);
++$s;
}
return $s;
}
/**
* Performs exponentiation.
*
* @param BCMath $n
* @return BCMath
*/
public function pow(BCMath $n)
{
$temp = new self();
$temp->value = bcpow($this->value, $n->value);
return $this->normalize($temp);
}
/**
* Return the minimum BigInteger between an arbitrary number of
BigIntegers.
*
* @param BCMath ...$nums
* @return BCMath
*/
public static function min(BCMath ...$nums)
{
return self::minHelper($nums);
}
/**
* Return the maximum BigInteger between an arbitrary number of
BigIntegers.
*
* @param BCMath ...$nums
* @return BCMath
*/
public static function max(BCMath ...$nums)
{
return self::maxHelper($nums);
}
/**
* Tests BigInteger to see if it is between two integers, inclusive
*
* @param BCMath $min
* @param BCMath $max
* @return bool
*/
public function between(BCMath $min, BCMath $max)
{
return $this->compare($min) >= 0 &&
$this->compare($max) <= 0;
}
/**
* Set Bitmask
*
* @param int $bits
* @return Engine
* @see self::setPrecision()
*/
protected static function setBitmask($bits)
{
$temp = parent::setBitmask($bits);
return $temp->add(static::$one[static::class]);
}
/**
* Is Odd?
*
* @return bool
*/
public function isOdd()
{
return $this->value[strlen($this->value) - 1] % 2 == 1;
}
/**
* Tests if a bit is set
*
* @return bool
*/
public function testBit($x)
{
return bccomp(
bcmod($this->value, bcpow('2', $x + 1, 0)),
bcpow('2', $x, 0),
0
) >= 0;
}
/**
* Is Negative?
*
* @return bool
*/
public function isNegative()
{
return strlen($this->value) && $this->value[0] ==
'-';
}
/**
* Negate
*
* Given $k, returns -$k
*
* @return BCMath
*/
public function negate()
{
$temp = clone $this;
if (!strlen($temp->value)) {
return $temp;
}
$temp->value = $temp->value[0] == '-' ?
substr($this->value, 1) :
'-' . $this->value;
return $temp;
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/Engine.php000064400000113212151161424270016707
0ustar00<?php
/**
* Base BigInteger Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Random;
use phpseclib3\Exception\BadConfigurationException;
use phpseclib3\Math\BigInteger;
/**
* Base Engine.
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Engine implements \JsonSerializable
{
/* final protected */ const PRIMES = [
3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43,
47, 53, 59,
61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
127, 131, 137,
139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199,
211, 223, 227,
229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293,
307, 311, 313,
317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397,
401, 409, 419,
421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491,
499, 503, 509,
521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601,
607, 613, 617,
619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
709, 719, 727,
733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821,
823, 827, 829,
839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929,
937, 941, 947,
953, 967, 971, 977, 983, 991, 997,
];
/**
* BigInteger(0)
*
* @var array<class-string<static>, static>
*/
protected static $zero = [];
/**
* BigInteger(1)
*
* @var array<class-string<static>, static>
*/
protected static $one = [];
/**
* BigInteger(2)
*
* @var array<class-string<static>, static>
*/
protected static $two = [];
/**
* Modular Exponentiation Engine
*
* @var array<class-string<static>,
class-string<static>>
*/
protected static $modexpEngine;
/**
* Engine Validity Flag
*
* @var array<class-string<static>, bool>
*/
protected static $isValidEngine;
/**
* Holds the BigInteger's value
*
* @var \GMP|string|array|int
*/
protected $value;
/**
* Holds the BigInteger's sign
*
* @var bool
*/
protected $is_negative;
/**
* Precision
*
* @see static::setPrecision()
* @var int
*/
protected $precision = -1;
/**
* Precision Bitmask
*
* @see static::setPrecision()
* @var static|false
*/
protected $bitmask = false;
/**
* Recurring Modulo Function
*
* @var callable
*/
protected $reduce;
/**
* Mode independent value used for serialization.
*
* @see self::__sleep()
* @see self::__wakeup()
* @var string
*/
protected $hex;
/**
* Default constructor
*
* @param int|numeric-string $x integer Base-10 number or base-$base
number if $base set.
* @param int $base
*/
public function __construct($x = 0, $base = 10)
{
if (!array_key_exists(static::class, static::$zero)) {
static::$zero[static::class] = null; // Placeholder to prevent
infinite loop.
static::$zero[static::class] = new static(0);
static::$one[static::class] = new static(1);
static::$two[static::class] = new static(2);
}
// '0' counts as empty() but when the base is 256
'0' is equal to ord('0') or 48
// '0' is the only value like this per
http://php.net/empty
if (empty($x) && (abs($base) != 256 || $x !==
'0')) {
return;
}
switch ($base) {
case -256:
case 256:
if ($base == -256 && (ord($x[0]) & 0x80)) {
$this->value = ~$x;
$this->is_negative = true;
} else {
$this->value = $x;
$this->is_negative = false;
}
$this->initialize($base);
if ($this->is_negative) {
$temp = $this->add(new static('-1'));
$this->value = $temp->value;
}
break;
case -16:
case 16:
if ($base > 0 && $x[0] == '-') {
$this->is_negative = true;
$x = substr($x, 1);
}
$x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#s',
'$1', $x);
$is_negative = false;
if ($base < 0 && hexdec($x[0]) >= 8) {
$this->is_negative = $is_negative = true;
$x = Strings::bin2hex(~Strings::hex2bin($x));
}
$this->value = $x;
$this->initialize($base);
if ($is_negative) {
$temp = $this->add(new static('-1'));
$this->value = $temp->value;
}
break;
case -10:
case 10:
// (?<!^)(?:-).*: find any -'s that aren't at
the beginning and then any characters that follow that
// (?<=^|-)0*: find any 0's that are preceded by
the start of the string or by a - (ie. octals)
// [^-0-9].*: find any non-numeric characters and then any
characters that follow that
$this->value =
preg_replace('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#s',
'', $x);
if (!strlen($this->value) || $this->value ==
'-') {
$this->value = '0';
}
$this->initialize($base);
break;
case -2:
case 2:
if ($base > 0 && $x[0] == '-') {
$this->is_negative = true;
$x = substr($x, 1);
}
$x = preg_replace('#^([01]*).*#s',
'$1', $x);
$temp = new static(Strings::bits2bin($x), 128 * $base); //
ie. either -16 or +16
$this->value = $temp->value;
if ($temp->is_negative) {
$this->is_negative = true;
}
break;
default:
// base not supported, so we'll let $this == 0
}
}
/**
* Sets engine type.
*
* Throws an exception if the type is invalid
*
* @param class-string<Engine> $engine
*/
public static function setModExpEngine($engine)
{
$fqengine = '\\phpseclib3\\Math\\BigInteger\\Engines\\' .
static::ENGINE_DIR . '\\' . $engine;
if (!class_exists($fqengine) || !method_exists($fqengine,
'isValidEngine')) {
throw new \InvalidArgumentException("$engine is not a
valid engine");
}
if (!$fqengine::isValidEngine()) {
throw new BadConfigurationException("$engine is not setup
correctly on this system");
}
static::$modexpEngine[static::class] = $fqengine;
}
/**
* Converts a BigInteger to a byte string (eg. base-256).
*
* Negative numbers are saved as positive numbers, unless
$twos_compliment is set to true, at which point, they're
* saved as two's compliment.
* @return string
*/
protected function toBytesHelper()
{
$comparison = $this->compare(new static());
if ($comparison == 0) {
return $this->precision > 0 ? str_repeat(chr(0),
($this->precision + 1) >> 3) : '';
}
$temp = $comparison < 0 ? $this->add(new static(1)) : $this;
$bytes = $temp->toBytes();
if (!strlen($bytes)) { // eg. if the number we're trying to
convert is -1
$bytes = chr(0);
}
if (ord($bytes[0]) & 0x80) {
$bytes = chr(0) . $bytes;
}
return $comparison < 0 ? ~$bytes : $bytes;
}
/**
* Converts a BigInteger to a hex string (eg. base-16).
*
* @param bool $twos_compliment
* @return string
*/
public function toHex($twos_compliment = false)
{
return Strings::bin2hex($this->toBytes($twos_compliment));
}
/**
* Converts a BigInteger to a bit string (eg. base-2).
*
* Negative numbers are saved as positive numbers, unless
$twos_compliment is set to true, at which point, they're
* saved as two's compliment.
*
* @param bool $twos_compliment
* @return string
*/
public function toBits($twos_compliment = false)
{
$hex = $this->toBytes($twos_compliment);
$bits = Strings::bin2bits($hex);
$result = $this->precision > 0 ? substr($bits,
-$this->precision) : ltrim($bits, '0');
if ($twos_compliment && $this->compare(new static())
> 0 && $this->precision <= 0) {
return '0' . $result;
}
return $result;
}
/**
* Calculates modular inverses.
*
* Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found
using modular inverses.
*
* {@internal See {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64}
for more information.}
*
* @param Engine $n
* @return static|false
*/
protected function modInverseHelper(Engine $n)
{
// $x mod -$n == $x mod $n.
$n = $n->abs();
if ($this->compare(static::$zero[static::class]) < 0) {
$temp = $this->abs();
$temp = $temp->modInverse($n);
return $this->normalize($n->subtract($temp));
}
extract($this->extendedGCD($n));
/**
* @var Engine $gcd
* @var Engine $x
*/
if (!$gcd->equals(static::$one[static::class])) {
return false;
}
$x = $x->compare(static::$zero[static::class]) < 0 ?
$x->add($n) : $x;
return $this->compare(static::$zero[static::class]) < 0 ?
$this->normalize($n->subtract($x)) : $this->normalize($x);
}
/**
* Serialize
*
* Will be called, automatically, when serialize() is called on a
BigInteger object.
*
* @return array
*/
public function __sleep()
{
$this->hex = $this->toHex(true);
$vars = ['hex'];
if ($this->precision > 0) {
$vars[] = 'precision';
}
return $vars;
}
/**
* Serialize
*
* Will be called, automatically, when unserialize() is called on a
BigInteger object.
*
* @return void
*/
public function __wakeup()
{
$temp = new static($this->hex, -16);
$this->value = $temp->value;
$this->is_negative = $temp->is_negative;
if ($this->precision > 0) {
// recalculate $this->bitmask
$this->setPrecision($this->precision);
}
}
/**
* JSON Serialize
*
* Will be called, automatically, when json_encode() is called on a
BigInteger object.
*
* @return array{hex: string, precision?: int]
*/
#[\ReturnTypeWillChange]
public function jsonSerialize()
{
$result = ['hex' => $this->toHex(true)];
if ($this->precision > 0) {
$result['precision'] = $this->precision;
}
return $result;
}
/**
* Converts a BigInteger to a base-10 number.
*
* @return string
*/
public function __toString()
{
return $this->toString();
}
/**
* __debugInfo() magic method
*
* Will be called, automatically, when print_r() or var_dump() are
called
*
* @return array
*/
public function __debugInfo()
{
$result = [
'value' => '0x' . $this->toHex(true),
'engine' => basename(static::class)
];
return $this->precision > 0 ? $result +
['precision' => $this->precision] : $result;
}
/**
* Set Precision
*
* Some bitwise operations give different results depending on the
precision being used. Examples include left
* shift, not, and rotates.
*
* @param int $bits
*/
public function setPrecision($bits)
{
if ($bits < 1) {
$this->precision = -1;
$this->bitmask = false;
return;
}
$this->precision = $bits;
$this->bitmask = static::setBitmask($bits);
$temp = $this->normalize($this);
$this->value = $temp->value;
}
/**
* Get Precision
*
* Returns the precision if it exists, -1 if it doesn't
*
* @return int
*/
public function getPrecision()
{
return $this->precision;
}
/**
* Set Bitmask
* @return static
* @param int $bits
* @see self::setPrecision()
*/
protected static function setBitmask($bits)
{
return new static(chr((1 << ($bits & 0x7)) - 1) .
str_repeat(chr(0xFF), $bits >> 3), 256);
}
/**
* Logical Not
*
* @return Engine|string
*/
public function bitwise_not()
{
// calculuate "not" without regard to $this->precision
// (will always result in a smaller number. ie. ~1 isn't 1111
1110 - it's 0)
$temp = $this->toBytes();
if ($temp == '') {
return $this->normalize(static::$zero[static::class]);
}
$pre_msb = decbin(ord($temp[0]));
$temp = ~$temp;
$msb = decbin(ord($temp[0]));
if (strlen($msb) == 8) {
$msb = substr($msb, strpos($msb, '0'));
}
$temp[0] = chr(bindec($msb));
// see if we need to add extra leading 1's
$current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8;
$new_bits = $this->precision - $current_bits;
if ($new_bits <= 0) {
return $this->normalize(new static($temp, 256));
}
// generate as many leading 1's as we need to.
$leading_ones = chr((1 << ($new_bits & 0x7)) - 1) .
str_repeat(chr(0xFF), $new_bits >> 3);
self::base256_lshift($leading_ones, $current_bits);
$temp = str_pad($temp, strlen($leading_ones), chr(0),
STR_PAD_LEFT);
return $this->normalize(new static($leading_ones | $temp, 256));
}
/**
* Logical Left Shift
*
* Shifts binary strings $shift bits, essentially multiplying by
2**$shift.
*
* @param string $x
* @param int $shift
* @return void
*/
protected static function base256_lshift(&$x, $shift)
{
if ($shift == 0) {
return;
}
$num_bytes = $shift >> 3; // eg. floor($shift/8)
$shift &= 7; // eg. $shift % 8
$carry = 0;
for ($i = strlen($x) - 1; $i >= 0; --$i) {
$temp = ord($x[$i]) << $shift | $carry;
$x[$i] = chr($temp);
$carry = $temp >> 8;
}
$carry = ($carry != 0) ? chr($carry) : '';
$x = $carry . $x . str_repeat(chr(0), $num_bytes);
}
/**
* Logical Left Rotate
*
* Instead of the top x bits being dropped they're appended to the
shifted bit string.
*
* @param int $shift
* @return Engine
*/
public function bitwise_leftRotate($shift)
{
$bits = $this->toBytes();
if ($this->precision > 0) {
$precision = $this->precision;
if (static::FAST_BITWISE) {
$mask = $this->bitmask->toBytes();
} else {
$mask = $this->bitmask->subtract(new static(1));
$mask = $mask->toBytes();
}
} else {
$temp = ord($bits[0]);
for ($i = 0; $temp >> $i; ++$i) {
}
$precision = 8 * strlen($bits) - 8 + $i;
$mask = chr((1 << ($precision & 0x7)) - 1) .
str_repeat(chr(0xFF), $precision >> 3);
}
if ($shift < 0) {
$shift += $precision;
}
$shift %= $precision;
if (!$shift) {
return clone $this;
}
$left = $this->bitwise_leftShift($shift);
$left = $left->bitwise_and(new static($mask, 256));
$right = $this->bitwise_rightShift($precision - $shift);
$result = static::FAST_BITWISE ? $left->bitwise_or($right) :
$left->add($right);
return $this->normalize($result);
}
/**
* Logical Right Rotate
*
* Instead of the bottom x bits being dropped they're prepended to
the shifted bit string.
*
* @param int $shift
* @return Engine
*/
public function bitwise_rightRotate($shift)
{
return $this->bitwise_leftRotate(-$shift);
}
/**
* Returns the smallest and largest n-bit number
*
* @param int $bits
* @return array{min: static, max: static}
*/
public static function minMaxBits($bits)
{
$bytes = $bits >> 3;
$min = str_repeat(chr(0), $bytes);
$max = str_repeat(chr(0xFF), $bytes);
$msb = $bits & 7;
if ($msb) {
$min = chr(1 << ($msb - 1)) . $min;
$max = chr((1 << $msb) - 1) . $max;
} else {
$min[0] = chr(0x80);
}
return [
'min' => new static($min, 256),
'max' => new static($max, 256)
];
}
/**
* Return the size of a BigInteger in bits
*
* @return int
*/
public function getLength()
{
return strlen($this->toBits());
}
/**
* Return the size of a BigInteger in bytes
*
* @return int
*/
public function getLengthInBytes()
{
return strlen($this->toBytes());
}
/**
* Performs some pre-processing for powMod
*
* @param Engine $e
* @param Engine $n
* @return static|false
*/
protected function powModOuter(Engine $e, Engine $n)
{
$n = $this->bitmask !== false &&
$this->bitmask->compare($n) < 0 ? $this->bitmask :
$n->abs();
if ($e->compare(new static()) < 0) {
$e = $e->abs();
$temp = $this->modInverse($n);
if ($temp === false) {
return false;
}
return $this->normalize($temp->powModInner($e, $n));
}
if ($this->compare($n) > 0) {
list(, $temp) = $this->divide($n);
return $temp->powModInner($e, $n);
}
return $this->powModInner($e, $n);
}
/**
* Sliding Window k-ary Modular Exponentiation
*
* Based on {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} /
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM
7.7}. In a departure from those algorithims,
* however, this function performs a modular reduction after every
multiplication and squaring operation.
* As such, this function has the same preconditions that the
reductions being used do.
*
* @template T of Engine
* @param Engine $x
* @param Engine $e
* @param Engine $n
* @param class-string<T> $class
* @return T
*/
protected static function slidingWindow(Engine $x, Engine $e, Engine
$n, $class)
{
static $window_ranges = [7, 25, 81, 241, 673, 1793]; // from
BigInteger.java's oddModPow function
//static $window_ranges = [0, 7, 36, 140, 450, 1303, 3529]; // from
MPM 7.3.1
$e_bits = $e->toBits();
$e_length = strlen($e_bits);
// calculate the appropriate window size.
// $window_size == 3 if $window_ranges is between 25 and 81, for
example.
for ($i = 0, $window_size = 1; $i < count($window_ranges)
&& $e_length > $window_ranges[$i]; ++$window_size, ++$i) {
}
$n_value = $n->value;
if (method_exists(static::class,
'generateCustomReduction')) {
static::generateCustomReduction($n, $class);
}
// precompute $this^0 through $this^$window_size
$powers = [];
$powers[1] = static::prepareReduce($x->value, $n_value, $class);
$powers[2] = static::squareReduce($powers[1], $n_value, $class);
// we do every other number since substr($e_bits, $i, $j+1) (see
below) is supposed to end
// in a 1. ie. it's supposed to be odd.
$temp = 1 << ($window_size - 1);
for ($i = 1; $i < $temp; ++$i) {
$i2 = $i << 1;
$powers[$i2 + 1] = static::multiplyReduce($powers[$i2 - 1],
$powers[2], $n_value, $class);
}
$result = new $class(1);
$result = static::prepareReduce($result->value, $n_value,
$class);
for ($i = 0; $i < $e_length;) {
if (!$e_bits[$i]) {
$result = static::squareReduce($result, $n_value, $class);
++$i;
} else {
for ($j = $window_size - 1; $j > 0; --$j) {
if (!empty($e_bits[$i + $j])) {
break;
}
}
// eg. the length of substr($e_bits, $i, $j + 1)
for ($k = 0; $k <= $j; ++$k) {
$result = static::squareReduce($result, $n_value,
$class);
}
$result = static::multiplyReduce($result,
$powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $class);
$i += $j + 1;
}
}
$temp = new $class();
$temp->value = static::reduce($result, $n_value, $class);
return $temp;
}
/**
* Generates a random number of a certain size
*
* Bit length is equal to $size
*
* @param int $size
* @return Engine
*/
public static function random($size)
{
extract(static::minMaxBits($size));
/**
* @var BigInteger $min
* @var BigInteger $max
*/
return static::randomRange($min, $max);
}
/**
* Generates a random prime number of a certain size
*
* Bit length is equal to $size
*
* @param int $size
* @return Engine
*/
public static function randomPrime($size)
{
extract(static::minMaxBits($size));
/**
* @var static $min
* @var static $max
*/
return static::randomRangePrime($min, $max);
}
/**
* Performs some pre-processing for randomRangePrime
*
* @param Engine $min
* @param Engine $max
* @return static|false
*/
protected static function randomRangePrimeOuter(Engine $min, Engine
$max)
{
$compare = $max->compare($min);
if (!$compare) {
return $min->isPrime() ? $min : false;
} elseif ($compare < 0) {
// if $min is bigger then $max, swap $min and $max
$temp = $max;
$max = $min;
$min = $temp;
}
$x = static::randomRange($min, $max);
return static::randomRangePrimeInner($x, $min, $max);
}
/**
* Generate a random number between a range
*
* Returns a random number between $min and $max where $min and $max
* can be defined using one of the two methods:
*
* BigInteger::randomRange($min, $max)
* BigInteger::randomRange($max, $min)
*
* @param Engine $min
* @param Engine $max
* @return Engine
*/
protected static function randomRangeHelper(Engine $min, Engine $max)
{
$compare = $max->compare($min);
if (!$compare) {
return $min;
} elseif ($compare < 0) {
// if $min is bigger then $max, swap $min and $max
$temp = $max;
$max = $min;
$min = $temp;
}
if (!isset(static::$one[static::class])) {
static::$one[static::class] = new static(1);
}
$max =
$max->subtract($min->subtract(static::$one[static::class]));
$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 static(chr(1) . str_repeat("\0",
$size), 256);
$random = new static(Random::string($size), 256);
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(new static(Random::string(1), 256));
$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 $random->add($min);
}
/**
* Performs some post-processing for randomRangePrime
*
* @param Engine $x
* @param Engine $min
* @param Engine $max
* @return static|false
*/
protected static function randomRangePrimeInner(Engine $x, Engine $min,
Engine $max)
{
if (!isset(static::$two[static::class])) {
static::$two[static::class] = new static('2');
}
$x->make_odd();
if ($x->compare($max) > 0) {
// if $x > $max then $max is even and if $min == $max then
no prime number exists between the specified range
if ($min->equals($max)) {
return false;
}
$x = clone $min;
$x->make_odd();
}
$initial_x = clone $x;
while (true) {
if ($x->isPrime()) {
return $x;
}
$x = $x->add(static::$two[static::class]);
if ($x->compare($max) > 0) {
$x = clone $min;
if ($x->equals(static::$two[static::class])) {
return $x;
}
$x->make_odd();
}
if ($x->equals($initial_x)) {
return false;
}
}
}
/**
* Sets the $t parameter for primality testing
*
* @return int
*/
protected function setupIsPrime()
{
$length = $this->getLengthInBytes();
// see HAC 4.49 "Note (controlling the error
probability)"
// @codingStandardsIgnoreStart
if ($length >= 163) { $t = 2; } // floor(1300 / 8)
else if ($length >= 106) { $t = 3; } // floor( 850 / 8)
else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8)
else if ($length >= 68 ) { $t = 5; } // floor( 550 / 8)
else if ($length >= 56 ) { $t = 6; } // floor( 450 / 8)
else if ($length >= 50 ) { $t = 7; } // floor( 400 / 8)
else if ($length >= 43 ) { $t = 8; } // floor( 350 / 8)
else if ($length >= 37 ) { $t = 9; } // floor( 300 / 8)
else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8)
else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8)
else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8)
else { $t = 27; }
// @codingStandardsIgnoreEnd
return $t;
}
/**
* Tests Primality
*
* Uses the {@link
http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test
Miller-Rabin primality test}.
* See {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24} for
more info.
*
* @param int $t
* @return bool
*/
protected function testPrimality($t)
{
if (!$this->testSmallPrimes()) {
return false;
}
$n = clone $this;
$n_1 = $n->subtract(static::$one[static::class]);
$n_2 = $n->subtract(static::$two[static::class]);
$r = clone $n_1;
$s = static::scan1divide($r);
for ($i = 0; $i < $t; ++$i) {
$a = static::randomRange(static::$two[static::class], $n_2);
$y = $a->modPow($r, $n);
if (!$y->equals(static::$one[static::class]) &&
!$y->equals($n_1)) {
for ($j = 1; $j < $s && !$y->equals($n_1);
++$j) {
$y = $y->modPow(static::$two[static::class], $n);
if ($y->equals(static::$one[static::class])) {
return false;
}
}
if (!$y->equals($n_1)) {
return false;
}
}
}
return true;
}
/**
* 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
* $t parameter is distributability. BigInteger::randomPrime() can be
distributed across multiple pageloads
* on a website instead of just one.
*
* @param int|bool $t
* @return bool
*/
public function isPrime($t = false)
{
if (!$t) {
$t = $this->setupIsPrime();
}
return $this->testPrimality($t);
}
/**
* Performs a few preliminary checks on root
*
* @param int $n
* @return Engine
*/
protected function rootHelper($n)
{
if ($n < 1) {
return clone static::$zero[static::class];
} // we want positive exponents
if ($this->compare(static::$one[static::class]) < 0) {
return clone static::$zero[static::class];
} // we want positive numbers
if ($this->compare(static::$two[static::class]) < 0) {
return clone static::$one[static::class];
} // n-th root of 1 or 2 is 1
return $this->rootInner($n);
}
/**
* Calculates the nth root of a biginteger.
*
* Returns the nth root of a positive biginteger, where n defaults to 2
*
* {@internal This function is based off of {@link
http://mathforum.org/library/drmath/view/52605.html this page} and {@link
http://stackoverflow.com/questions/11242920/calculating-nth-root-with-bcmath-in-php
this stackoverflow question}.}
*
* @param int $n
* @return Engine
*/
protected function rootInner($n)
{
$n = new static($n);
// g is our guess number
$g = static::$two[static::class];
// while (g^n < num) g=g*2
while ($g->pow($n)->compare($this) < 0) {
$g = $g->multiply(static::$two[static::class]);
}
// if (g^n==num) num is a power of 2, we're lucky, end of job
// == 0 bccomp(bcpow($g, $n), $n->value)==0
if ($g->pow($n)->equals($this) > 0) {
$root = $g;
return $this->normalize($root);
}
// if we're here num wasn't a power of 2 :(
$og = $g; // og means original guess and here is our upper bound
$g = $g->divide(static::$two[static::class])[0]; // g is set to
be our lower bound
$step =
$og->subtract($g)->divide(static::$two[static::class])[0]; // step is
the half of upper bound - lower bound
$g = $g->add($step); // we start at lower bound + step ,
basically in the middle of our interval
// while step>1
while ($step->compare(static::$one[static::class]) == 1) {
$guess = $g->pow($n);
$step = $step->divide(static::$two[static::class])[0];
$comp = $guess->compare($this); // compare our guess with
real number
switch ($comp) {
case -1: // if guess is lower we add the new step
$g = $g->add($step);
break;
case 1: // if guess is higher we sub the new step
$g = $g->subtract($step);
break;
case 0: // if guess is exactly the num we're done, we
return the value
$root = $g;
break 2;
}
}
if ($comp == 1) {
$g = $g->subtract($step);
}
// whatever happened, g is the closest guess we can make so return
it
$root = $g;
return $this->normalize($root);
}
/**
* Calculates the nth root of a biginteger.
*
* @param int $n
* @return Engine
*/
public function root($n = 2)
{
return $this->rootHelper($n);
}
/**
* Return the minimum BigInteger between an arbitrary number of
BigIntegers.
*
* @param array $nums
* @return Engine
*/
protected static function minHelper(array $nums)
{
if (count($nums) == 1) {
return $nums[0];
}
$min = $nums[0];
for ($i = 1; $i < count($nums); $i++) {
$min = $min->compare($nums[$i]) > 0 ? $nums[$i] : $min;
}
return $min;
}
/**
* Return the minimum BigInteger between an arbitrary number of
BigIntegers.
*
* @param array $nums
* @return Engine
*/
protected static function maxHelper(array $nums)
{
if (count($nums) == 1) {
return $nums[0];
}
$max = $nums[0];
for ($i = 1; $i < count($nums); $i++) {
$max = $max->compare($nums[$i]) < 0 ? $nums[$i] : $max;
}
return $max;
}
/**
* Create Recurring Modulo Function
*
* Sometimes it may be desirable to do repeated modulos with the same
number outside of
* modular exponentiation
*
* @return callable
*/
public function createRecurringModuloFunction()
{
$class = static::class;
$fqengine = !method_exists(static::$modexpEngine[static::class],
'reduce') ?
'\\phpseclib3\\Math\\BigInteger\\Engines\\' .
static::ENGINE_DIR . '\\DefaultEngine' :
static::$modexpEngine[static::class];
if (method_exists($fqengine, 'generateCustomReduction'))
{
$func = $fqengine::generateCustomReduction($this,
static::class);
return eval('return function(' . static::class .
' $x) use ($func, $class) {
$r = new $class();
$r->value = $func($x->value);
return $r;
};');
}
$n = $this->value;
return eval('return function(' . static::class . '
$x) use ($n, $fqengine, $class) {
$r = new $class();
$r->value = $fqengine::reduce($x->value, $n, $class);
return $r;
};');
}
/**
* Calculates the greatest common divisor and Bezout's identity.
*
* @param Engine $n
* @return array{gcd: Engine, x: Engine, y: Engine}
*/
protected function extendedGCDHelper(Engine $n)
{
$u = clone $this;
$v = clone $n;
$one = new static(1);
$zero = new static();
$a = clone $one;
$b = clone $zero;
$c = clone $zero;
$d = clone $one;
while (!$v->equals($zero)) {
list($q) = $u->divide($v);
$temp = $u;
$u = $v;
$v = $temp->subtract($v->multiply($q));
$temp = $a;
$a = $c;
$c = $temp->subtract($a->multiply($q));
$temp = $b;
$b = $d;
$d = $temp->subtract($b->multiply($q));
}
return [
'gcd' => $u,
'x' => $a,
'y' => $b
];
}
/**
* Bitwise Split
*
* Splits BigInteger's into chunks of $split bits
*
* @param int $split
* @return Engine[]
*/
public function bitwise_split($split)
{
if ($split < 1) {
throw new \RuntimeException('Offset must be greater than
1');
}
$mask =
static::$one[static::class]->bitwise_leftShift($split)->subtract(static::$one[static::class]);
$num = clone $this;
$vals = [];
while (!$num->equals(static::$zero[static::class])) {
$vals[] = $num->bitwise_and($mask);
$num = $num->bitwise_rightShift($split);
}
return array_reverse($vals);
}
/**
* Logical And
*
* @param Engine $x
* @return Engine
*/
protected function bitwiseAndHelper(Engine $x)
{
$left = $this->toBytes(true);
$right = $x->toBytes(true);
$length = max(strlen($left), strlen($right));
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
return $this->normalize(new static($left & $right, -256));
}
/**
* Logical Or
*
* @param Engine $x
* @return Engine
*/
protected function bitwiseOrHelper(Engine $x)
{
$left = $this->toBytes(true);
$right = $x->toBytes(true);
$length = max(strlen($left), strlen($right));
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
return $this->normalize(new static($left | $right, -256));
}
/**
* Logical Exclusive Or
*
* @param Engine $x
* @return Engine
*/
protected function bitwiseXorHelper(Engine $x)
{
$left = $this->toBytes(true);
$right = $x->toBytes(true);
$length = max(strlen($left), strlen($right));
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
return $this->normalize(new static($left ^ $right, -256));
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/GMP/DefaultEngine.php000064400000001574151161424270020646
0ustar00<?php
/**
* GMP Modular Exponentiation Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines\GMP;
use phpseclib3\Math\BigInteger\Engines\GMP;
/**
* GMP Modular Exponentiation Engine
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class DefaultEngine extends GMP
{
/**
* Performs modular exponentiation.
*
* @param GMP $x
* @param GMP $e
* @param GMP $n
* @return GMP
*/
protected static function powModHelper(GMP $x, GMP $e, GMP $n)
{
$temp = new GMP();
$temp->value = gmp_powm($x->value, $e->value,
$n->value);
return $x->normalize($temp);
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/GMP.php000064400000040300151161424270016122
0ustar00<?php
/**
* GMP BigInteger Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines;
use phpseclib3\Exception\BadConfigurationException;
/**
* GMP Engine.
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class GMP extends Engine
{
/**
* Can Bitwise operations be done fast?
*
* @see parent::bitwise_leftRotate()
* @see parent::bitwise_rightRotate()
*/
const FAST_BITWISE = true;
/**
* Engine Directory
*
* @see parent::setModExpEngine
*/
const ENGINE_DIR = 'GMP';
/**
* Test for engine validity
*
* @return bool
* @see parent::__construct()
*/
public static function isValidEngine()
{
return extension_loaded('gmp');
}
/**
* Default constructor
*
* @param mixed $x integer Base-10 number or base-$base number if $base
set.
* @param int $base
* @see parent::__construct()
*/
public function __construct($x = 0, $base = 10)
{
if (!isset(static::$isValidEngine[static::class])) {
static::$isValidEngine[static::class] = self::isValidEngine();
}
if (!static::$isValidEngine[static::class]) {
throw new BadConfigurationException('GMP is not setup
correctly on this system');
}
if ($x instanceof \GMP) {
$this->value = $x;
return;
}
$this->value = gmp_init(0);
parent::__construct($x, $base);
}
/**
* Initialize a GMP BigInteger Engine instance
*
* @param int $base
* @see parent::__construct()
*/
protected function initialize($base)
{
switch (abs($base)) {
case 256:
$this->value = gmp_import($this->value);
if ($this->is_negative) {
$this->value = -$this->value;
}
break;
case 16:
$temp = $this->is_negative ? '-0x' .
$this->value : '0x' . $this->value;
$this->value = gmp_init($temp);
break;
case 10:
$this->value = gmp_init(isset($this->value) ?
$this->value : '0');
}
}
/**
* Converts a BigInteger to a base-10 number.
*
* @return string
*/
public function toString()
{
return (string)$this->value;
}
/**
* Converts a BigInteger to a bit string (eg. base-2).
*
* Negative numbers are saved as positive numbers, unless
$twos_compliment is set to true, at which point, they're
* saved as two's compliment.
*
* @param bool $twos_compliment
* @return string
*/
public function toBits($twos_compliment = false)
{
$hex = $this->toHex($twos_compliment);
$bits = gmp_strval(gmp_init($hex, 16), 2);
if ($this->precision > 0) {
$bits = substr($bits, -$this->precision);
}
if ($twos_compliment && $this->compare(new static())
> 0 && $this->precision <= 0) {
return '0' . $bits;
}
return $bits;
}
/**
* Converts a BigInteger to a byte string (eg. base-256).
*
* @param bool $twos_compliment
* @return string
*/
public function toBytes($twos_compliment = false)
{
if ($twos_compliment) {
return $this->toBytesHelper();
}
if (gmp_cmp($this->value, gmp_init(0)) == 0) {
return $this->precision > 0 ? str_repeat(chr(0),
($this->precision + 1) >> 3) : '';
}
$temp = gmp_export($this->value);
return $this->precision > 0 ?
substr(str_pad($temp, $this->precision >> 3, chr(0),
STR_PAD_LEFT), -($this->precision >> 3)) :
ltrim($temp, chr(0));
}
/**
* Adds two BigIntegers.
*
* @param GMP $y
* @return GMP
*/
public function add(GMP $y)
{
$temp = new self();
$temp->value = $this->value + $y->value;
return $this->normalize($temp);
}
/**
* Subtracts two BigIntegers.
*
* @param GMP $y
* @return GMP
*/
public function subtract(GMP $y)
{
$temp = new self();
$temp->value = $this->value - $y->value;
return $this->normalize($temp);
}
/**
* Multiplies two BigIntegers.
*
* @param GMP $x
* @return GMP
*/
public function multiply(GMP $x)
{
$temp = new self();
$temp->value = $this->value * $x->value;
return $this->normalize($temp);
}
/**
* Divides two BigIntegers.
*
* Returns an array whose first element contains the quotient and whose
second element contains the
* "common residue". If the remainder would be positive, the
"common residue" and the remainder are the
* same. If the remainder would be negative, the "common
residue" is equal to the sum of the remainder
* and the divisor (basically, the "common residue" is the
first positive modulo).
*
* @param GMP $y
* @return array{GMP, GMP}
*/
public function divide(GMP $y)
{
$quotient = new self();
$remainder = new self();
list($quotient->value, $remainder->value) =
gmp_div_qr($this->value, $y->value);
if (gmp_sign($remainder->value) < 0) {
$remainder->value = $remainder->value +
gmp_abs($y->value);
}
return [$this->normalize($quotient),
$this->normalize($remainder)];
}
/**
* Compares two numbers.
*
* Although one might think !$x->compare($y) means $x != $y, it, in
fact, means the opposite. The reason for this
* is demonstrated thusly:
*
* $x > $y: $x->compare($y) > 0
* $x < $y: $x->compare($y) < 0
* $x == $y: $x->compare($y) == 0
*
* Note how the same comparison operator is used. If you want to test
for equality, use $x->equals($y).
*
* {@internal Could return $this->subtract($x), but that's not
as fast as what we do do.}
*
* @param GMP $y
* @return int in case < 0 if $this is less than $y; > 0 if $this
is greater than $y, and 0 if they are equal.
* @see self::equals()
*/
public function compare(GMP $y)
{
$r = gmp_cmp($this->value, $y->value);
if ($r < -1) {
$r = -1;
}
if ($r > 1) {
$r = 1;
}
return $r;
}
/**
* Tests the equality of two numbers.
*
* If you need to see if one number is greater than or less than
another number, use BigInteger::compare()
*
* @param GMP $x
* @return bool
*/
public function equals(GMP $x)
{
return $this->value == $x->value;
}
/**
* Calculates modular inverses.
*
* Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found
using modular inverses.
*
* @param GMP $n
* @return false|GMP
*/
public function modInverse(GMP $n)
{
$temp = new self();
$temp->value = gmp_invert($this->value, $n->value);
return $temp->value === false ? false :
$this->normalize($temp);
}
/**
* Calculates the greatest common divisor and Bezout's identity.
*
* Say you have 693 and 609. The GCD is 21. Bezout's identity
states that there exist integers x and y such that
* 693*x + 609*y == 21. In point of fact, there are actually an
infinite number of x and y combinations and which
* combination is returned is dependent upon which mode is in use. See
* {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity
Bezout's identity - Wikipedia} for more information.
*
* @param GMP $n
* @return GMP[]
*/
public function extendedGCD(GMP $n)
{
extract(gmp_gcdext($this->value, $n->value));
return [
'gcd' => $this->normalize(new self($g)),
'x' => $this->normalize(new self($s)),
'y' => $this->normalize(new self($t))
];
}
/**
* Calculates the greatest common divisor
*
* Say you have 693 and 609. The GCD is 21.
*
* @param GMP $n
* @return GMP
*/
public function gcd(GMP $n)
{
$r = gmp_gcd($this->value, $n->value);
return $this->normalize(new self($r));
}
/**
* Absolute value.
*
* @return GMP
*/
public function abs()
{
$temp = new self();
$temp->value = gmp_abs($this->value);
return $temp;
}
/**
* Logical And
*
* @param GMP $x
* @return GMP
*/
public function bitwise_and(GMP $x)
{
$temp = new self();
$temp->value = $this->value & $x->value;
return $this->normalize($temp);
}
/**
* Logical Or
*
* @param GMP $x
* @return GMP
*/
public function bitwise_or(GMP $x)
{
$temp = new self();
$temp->value = $this->value | $x->value;
return $this->normalize($temp);
}
/**
* Logical Exclusive Or
*
* @param GMP $x
* @return GMP
*/
public function bitwise_xor(GMP $x)
{
$temp = new self();
$temp->value = $this->value ^ $x->value;
return $this->normalize($temp);
}
/**
* Logical Right Shift
*
* Shifts BigInteger's by $shift bits, effectively dividing by
2**$shift.
*
* @param int $shift
* @return GMP
*/
public function bitwise_rightShift($shift)
{
// 0xFFFFFFFF >> 2 == -1 (on 32-bit systems)
// gmp_init('0xFFFFFFFF') >> 2 ==
gmp_init('0x3FFFFFFF')
$temp = new self();
$temp->value = $this->value >> $shift;
return $this->normalize($temp);
}
/**
* Logical Left Shift
*
* Shifts BigInteger's by $shift bits, effectively multiplying by
2**$shift.
*
* @param int $shift
* @return GMP
*/
public function bitwise_leftShift($shift)
{
$temp = new self();
$temp->value = $this->value << $shift;
return $this->normalize($temp);
}
/**
* Performs modular exponentiation.
*
* @param GMP $e
* @param GMP $n
* @return GMP
*/
public function modPow(GMP $e, GMP $n)
{
return $this->powModOuter($e, $n);
}
/**
* Performs modular exponentiation.
*
* Alias for modPow().
*
* @param GMP $e
* @param GMP $n
* @return GMP
*/
public function powMod(GMP $e, GMP $n)
{
return $this->powModOuter($e, $n);
}
/**
* Performs modular exponentiation.
*
* @param GMP $e
* @param GMP $n
* @return GMP
*/
protected function powModInner(GMP $e, GMP $n)
{
$class = static::$modexpEngine[static::class];
return $class::powModHelper($this, $e, $n);
}
/**
* Normalize
*
* Removes leading zeros and truncates (if necessary) to maintain the
appropriate precision
*
* @param GMP $result
* @return GMP
*/
protected function normalize(GMP $result)
{
$result->precision = $this->precision;
$result->bitmask = $this->bitmask;
if ($result->bitmask !== false) {
$flip = $result->value < 0;
if ($flip) {
$result->value = -$result->value;
}
$result->value = $result->value &
$result->bitmask->value;
if ($flip) {
$result->value = -$result->value;
}
}
return $result;
}
/**
* Performs some post-processing for randomRangePrime
*
* @param Engine $x
* @param Engine $min
* @param Engine $max
* @return GMP
*/
protected static function randomRangePrimeInner(Engine $x, Engine $min,
Engine $max)
{
$p = gmp_nextprime($x->value);
if ($p <= $max->value) {
return new self($p);
}
if ($min->value != $x->value) {
$x = new self($x->value - 1);
}
return self::randomRangePrime($min, $x);
}
/**
* Generate a random prime number between a range
*
* If there's not a prime within the given range, false will be
returned.
*
* @param GMP $min
* @param GMP $max
* @return false|GMP
*/
public static function randomRangePrime(GMP $min, GMP $max)
{
return self::randomRangePrimeOuter($min, $max);
}
/**
* Generate a random number between a range
*
* Returns a random number between $min and $max where $min and $max
* can be defined using one of the two methods:
*
* BigInteger::randomRange($min, $max)
* BigInteger::randomRange($max, $min)
*
* @param GMP $min
* @param GMP $max
* @return GMP
*/
public static function randomRange(GMP $min, GMP $max)
{
return self::randomRangeHelper($min, $max);
}
/**
* Make the current number odd
*
* If the current number is odd it'll be unchanged. If it's
even, one will be added to it.
*
* @see self::randomPrime()
*/
protected function make_odd()
{
gmp_setbit($this->value, 0);
}
/**
* Tests Primality
*
* @param int $t
* @return bool
*/
protected function testPrimality($t)
{
return gmp_prob_prime($this->value, $t) != 0;
}
/**
* Calculates the nth root of a biginteger.
*
* Returns the nth root of a positive biginteger, where n defaults to 2
*
* @param int $n
* @return GMP
*/
protected function rootInner($n)
{
$root = new self();
$root->value = gmp_root($this->value, $n);
return $this->normalize($root);
}
/**
* Performs exponentiation.
*
* @param GMP $n
* @return GMP
*/
public function pow(GMP $n)
{
$temp = new self();
$temp->value = $this->value ** $n->value;
return $this->normalize($temp);
}
/**
* Return the minimum BigInteger between an arbitrary number of
BigIntegers.
*
* @param GMP ...$nums
* @return GMP
*/
public static function min(GMP ...$nums)
{
return self::minHelper($nums);
}
/**
* Return the maximum BigInteger between an arbitrary number of
BigIntegers.
*
* @param GMP ...$nums
* @return GMP
*/
public static function max(GMP ...$nums)
{
return self::maxHelper($nums);
}
/**
* Tests BigInteger to see if it is between two integers, inclusive
*
* @param GMP $min
* @param GMP $max
* @return bool
*/
public function between(GMP $min, GMP $max)
{
return $this->compare($min) >= 0 &&
$this->compare($max) <= 0;
}
/**
* Create Recurring Modulo Function
*
* Sometimes it may be desirable to do repeated modulos with the same
number outside of
* modular exponentiation
*
* @return callable
*/
public function createRecurringModuloFunction()
{
$temp = $this->value;
return function (GMP $x) use ($temp) {
return new GMP($x->value % $temp);
};
}
/**
* Scan for 1 and right shift by that amount
*
* ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n,
gmp_pow(gmp_init('2'), $s));
*
* @param GMP $r
* @return int
*/
public static function scan1divide(GMP $r)
{
$s = gmp_scan1($r->value, 0);
$r->value >>= $s;
return $s;
}
/**
* Is Odd?
*
* @return bool
*/
public function isOdd()
{
return gmp_testbit($this->value, 0);
}
/**
* Tests if a bit is set
*
* @return bool
*/
public function testBit($x)
{
return gmp_testbit($this->value, $x);
}
/**
* Is Negative?
*
* @return bool
*/
public function isNegative()
{
return gmp_sign($this->value) == -1;
}
/**
* Negate
*
* Given $k, returns -$k
*
* @return GMP
*/
public function negate()
{
$temp = clone $this;
$temp->value = -$this->value;
return $temp;
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/OpenSSL.php000064400000003713151161424270016771
0ustar00<?php
/**
* OpenSSL Modular Exponentiation Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines;
use phpseclib3\Crypt\RSA\Formats\Keys\PKCS8;
use phpseclib3\Math\BigInteger;
/**
* OpenSSL Modular Exponentiation Engine
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class OpenSSL
{
/**
* Test for engine validity
*
* @return bool
*/
public static function isValidEngine()
{
return extension_loaded('openssl') &&
static::class != __CLASS__;
}
/**
* Performs modular exponentiation.
*
* @param Engine $x
* @param Engine $e
* @param Engine $n
* @return Engine
*/
public static function powModHelper(Engine $x, Engine $e, Engine $n)
{
if ($n->getLengthInBytes() < 31 || $n->getLengthInBytes()
> 16384) {
throw new \OutOfRangeException('Only modulo between 31 and
16384 bits are accepted');
}
$key = PKCS8::savePublicKey(
new BigInteger($n),
new BigInteger($e)
);
$plaintext = str_pad($x->toBytes(), $n->getLengthInBytes(),
"\0", STR_PAD_LEFT);
// this is easily prone to failure. if the modulo is a multiple of
2 or 3 or whatever it
// won't work and you'll get a "failure:
error:0906D06C:PEM routines:PEM_read_bio:no start line"
// error. i suppose, for even numbers, we could do what
PHP\Montgomery.php does, but then what
// about odd numbers divisible by 3, by 5, etc?
if (!openssl_public_encrypt($plaintext, $result, $key,
OPENSSL_NO_PADDING)) {
throw new \UnexpectedValueException(openssl_error_string());
}
$class = get_class($x);
return new $class($result, 256);
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Base.php000064400000010762151161424270017011
0ustar00<?php
/**
* PHP Modular Exponentiation Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines\PHP;
use phpseclib3\Math\BigInteger\Engines\PHP;
/**
* PHP Modular Exponentiation Engine
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Base extends PHP
{
/**
* Cache constants
*
* $cache[self::VARIABLE] tells us whether or not the cached data is
still valid.
*
*/
const VARIABLE = 0;
/**
* $cache[self::DATA] contains the cached data.
*
*/
const DATA = 1;
/**
* Test for engine validity
*
* @return bool
*/
public static function isValidEngine()
{
return static::class != __CLASS__;
}
/**
* Performs modular exponentiation.
*
* The most naive approach to modular exponentiation has very
unreasonable requirements, and
* and although the approach involving repeated squaring does vastly
better, it, too, is impractical
* for our purposes. The reason being that division - by far the most
complicated and time-consuming
* of the basic operations (eg. +,-,*,/) - occurs multiple times within
it.
*
* Modular reductions resolve this issue. Although an individual
modular reduction takes more time
* then an individual division, when performed in succession (with the
same modulo), they're a lot faster.
*
* The two most commonly used modular reductions are Barrett and
Montgomery reduction. Montgomery reduction,
* although faster, only works when the gcd of the modulo and of the
base being used is 1. In RSA, when the
* base is a power of two, the modulo - a product of two primes - is
always going to have a gcd of 1 (because
* the product of two odd numbers is odd), but what about when RSA
isn't used?
*
* In contrast, Barrett reduction has no such constraint. As such,
some bigint implementations perform a
* Barrett reduction after every operation in the modpow function.
Others perform Barrett reductions when the
* modulo is even and Montgomery reductions when the modulo is odd.
BigInteger.java's modPow method, however,
* uses a trick involving the Chinese Remainder Theorem to factor the
even modulo into two numbers - one odd and
* the other, a power of two - and recombine them, later. This is the
method that this modPow function uses.
* {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery
Reduction with Even Modulus} elaborates.
*
* @param PHP $x
* @param PHP $e
* @param PHP $n
* @param string $class
* @return PHP
*/
protected static function powModHelper(PHP $x, PHP $e, PHP $n, $class)
{
if (empty($e->value)) {
$temp = new $class();
$temp->value = [1];
return $x->normalize($temp);
}
if ($e->value == [1]) {
list(, $temp) = $x->divide($n);
return $x->normalize($temp);
}
if ($e->value == [2]) {
$temp = new $class();
$temp->value = $class::square($x->value);
list(, $temp) = $temp->divide($n);
return $x->normalize($temp);
}
return $x->normalize(static::slidingWindow($x, $e, $n, $class));
}
/**
* Modular reduction preparation
*
* @param array $x
* @param array $n
* @param string $class
* @see self::slidingWindow()
* @return array
*/
protected static function prepareReduce(array $x, array $n, $class)
{
return static::reduce($x, $n, $class);
}
/**
* Modular multiply
*
* @param array $x
* @param array $y
* @param array $n
* @param string $class
* @see self::slidingWindow()
* @return array
*/
protected static function multiplyReduce(array $x, array $y, array $n,
$class)
{
$temp = $class::multiplyHelper($x, false, $y, false);
return static::reduce($temp[self::VALUE], $n, $class);
}
/**
* Modular square
*
* @param array $x
* @param array $n
* @param string $class
* @see self::slidingWindow()
* @return array
*/
protected static function squareReduce(array $x, array $n, $class)
{
return static::reduce($class::square($x), $n, $class);
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/PHP/DefaultEngine.php000064400000001110151161424270020634
0ustar00<?php
/**
* PHP Default Modular Exponentiation Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines\PHP;
use phpseclib3\Math\BigInteger\Engines\PHP\Reductions\EvalBarrett;
/**
* PHP Default Modular Exponentiation Engine
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class DefaultEngine extends EvalBarrett
{
}
phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Montgomery.php000064400000004537151161424270020302
0ustar00<?php
/**
* PHP Montgomery Modular Exponentiation Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines\PHP;
use phpseclib3\Math\BigInteger\Engines\Engine;
use phpseclib3\Math\BigInteger\Engines\PHP;
use phpseclib3\Math\BigInteger\Engines\PHP\Reductions\PowerOfTwo;
/**
* PHP Montgomery Modular Exponentiation Engine
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Montgomery extends Base
{
/**
* Test for engine validity
*
* @return bool
*/
public static function isValidEngine()
{
return static::class != __CLASS__;
}
/**
* Performs modular exponentiation.
*
* @template T of Engine
* @param Engine $x
* @param Engine $e
* @param Engine $n
* @param class-string<T> $class
* @return T
*/
protected static function slidingWindow(Engine $x, Engine $e, Engine
$n, $class)
{
// is the modulo odd?
if ($n->value[0] & 1) {
return parent::slidingWindow($x, $e, $n, $class);
}
// if it's not, it's even
// find the lowest set bit (eg. the max pow of 2 that divides $n)
for ($i = 0; $i < count($n->value); ++$i) {
if ($n->value[$i]) {
$temp = decbin($n->value[$i]);
$j = strlen($temp) - strrpos($temp, '1') - 1;
$j += $class::BASE * $i;
break;
}
}
// at this point, 2^$j * $n/(2^$j) == $n
$mod1 = clone $n;
$mod1->rshift($j);
$mod2 = new $class();
$mod2->value = [1];
$mod2->lshift($j);
$part1 = $mod1->value != [1] ? parent::slidingWindow($x, $e,
$mod1, $class) : new $class();
$part2 = PowerOfTwo::slidingWindow($x, $e, $mod2, $class);
$y1 = $mod2->modInverse($mod1);
$y2 = $mod1->modInverse($mod2);
$result = $part1->multiply($mod2);
$result = $result->multiply($y1);
$temp = $part2->multiply($mod1);
$temp = $temp->multiply($y2);
$result = $result->add($temp);
list(, $result) = $result->divide($n);
return $result;
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/PHP/OpenSSL.php000064400000001064151161424270017415
0ustar00<?php
/**
* OpenSSL Modular Exponentiation Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines\PHP;
use phpseclib3\Math\BigInteger\Engines\OpenSSL as Progenitor;
/**
* OpenSSL Modular Exponentiation Engine
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class OpenSSL extends Progenitor
{
}
phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Barrett.php000064400000025716151161424270021666
0ustar00<?php
/**
* PHP Barrett Modular Exponentiation Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
use phpseclib3\Math\BigInteger\Engines\PHP;
use phpseclib3\Math\BigInteger\Engines\PHP\Base;
/**
* PHP Barrett Modular Exponentiation Engine
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Barrett extends Base
{
/**
* Barrett Modular Reduction
*
* See {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3}
/
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM
6.2.5} for more information. Modified slightly,
* so as not to require negative numbers (initially, this script
didn't support negative numbers).
*
* Employs "folding", as described at
* {@link
http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66
thesis-149.pdf#page=66}. To quote from
* it, "the idea [behind folding] is to find a value x' such
that x (mod m) = x' (mod m), with x' being smaller than x."
*
* Unfortunately, the "Barrett Reduction with Folding"
algorithm described in thesis-149.pdf is not, as written, all that
* usable on account of (1) its not using reasonable radix points as
discussed in
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM
6.2.2} and (2) the fact that, even with reasonable
* radix points, it only works when there are an even number of digits
in the denominator. The reason for (2) is that
* (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even,
they're the same, but if x is odd, they're not. See the in-line
* comments for details.
*
* @param array $n
* @param array $m
* @param class-string<PHP> $class
* @return array
*/
protected static function reduce(array $n, array $m, $class)
{
static $cache = [
self::VARIABLE => [],
self::DATA => []
];
$m_length = count($m);
// if (self::compareHelper($n, $static::square($m)) >= 0) {
if (count($n) > 2 * $m_length) {
$lhs = new $class();
$rhs = new $class();
$lhs->value = $n;
$rhs->value = $m;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
}
// if (m.length >> 1) + 2 <= m.length then m is too small
and n can't be reduced
if ($m_length < 5) {
return self::regularBarrett($n, $m, $class);
}
// n = 2 * m.length
if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]);
$cache[self::VARIABLE][] = $m;
$lhs = new $class();
$lhs_value = &$lhs->value;
$lhs_value = self::array_repeat(0, $m_length + ($m_length
>> 1));
$lhs_value[] = 1;
$rhs = new $class();
$rhs->value = $m;
list($u, $m1) = $lhs->divide($rhs);
$u = $u->value;
$m1 = $m1->value;
$cache[self::DATA][] = [
'u' => $u, // m.length >> 1 (technically
(m.length >> 1) + 1)
'm1' => $m1 // m.length
];
} else {
extract($cache[self::DATA][$key]);
}
$cutoff = $m_length + ($m_length >> 1);
$lsd = array_slice($n, 0, $cutoff); // m.length + (m.length
>> 1)
$msd = array_slice($n, $cutoff); // m.length >> 1
$lsd = self::trim($lsd);
$temp = $class::multiplyHelper($msd, false, $m1, false); //
m.length + (m.length >> 1)
$n = $class::addHelper($lsd, false, $temp[self::VALUE], false); //
m.length + (m.length >> 1) + 1 (so basically we're adding two
same length numbers)
//if ($m_length & 1) {
// return self::regularBarrett($n[self::VALUE], $m, $class);
//}
// (m.length + (m.length >> 1) + 1) - (m.length - 1) ==
(m.length >> 1) + 2
$temp = array_slice($n[self::VALUE], $m_length - 1);
// if even: ((m.length >> 1) + 2) + (m.length >> 1) ==
m.length + 2
// if odd: ((m.length >> 1) + 2) + (m.length >> 1) ==
(m.length - 1) + 2 == m.length + 1
$temp = $class::multiplyHelper($temp, false, $u, false);
// if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length
- (m.length >> 1) + 1
// if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length
- (m.length >> 1)
$temp = array_slice($temp[self::VALUE], ($m_length >> 1) +
1);
// if even: (m.length - (m.length >> 1) + 1) + m.length = 2 *
m.length - (m.length >> 1) + 1
// if odd: (m.length - (m.length >> 1)) + m.length = 2 *
m.length - (m.length >> 1)
$temp = $class::multiplyHelper($temp, false, $m, false);
// at this point, if m had an odd number of digits, we'd be
subtracting a 2 * m.length - (m.length >> 1) digit
// number from a m.length + (m.length >> 1) + 1 digit number.
ie. there'd be an extra digit and the while loop
// following this comment would loop a lot (hence our calling
_regularBarrett() in that situation).
$result = $class::subtractHelper($n[self::VALUE], false,
$temp[self::VALUE], false);
while (self::compareHelper($result[self::VALUE],
$result[self::SIGN], $m, false) >= 0) {
$result = $class::subtractHelper($result[self::VALUE],
$result[self::SIGN], $m, false);
}
return $result[self::VALUE];
}
/**
* (Regular) Barrett Modular Reduction
*
* For numbers with more than four digits BigInteger::_barrett() is
faster. The difference between that and this
* is that this function does not fold the denominator into a smaller
form.
*
* @param array $x
* @param array $n
* @param string $class
* @return array
*/
private static function regularBarrett(array $x, array $n, $class)
{
static $cache = [
self::VARIABLE => [],
self::DATA => []
];
$n_length = count($n);
if (count($x) > 2 * $n_length) {
$lhs = new $class();
$rhs = new $class();
$lhs->value = $x;
$rhs->value = $n;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
}
if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]);
$cache[self::VARIABLE][] = $n;
$lhs = new $class();
$lhs_value = &$lhs->value;
$lhs_value = self::array_repeat(0, 2 * $n_length);
$lhs_value[] = 1;
$rhs = new $class();
$rhs->value = $n;
list($temp, ) = $lhs->divide($rhs); // m.length
$cache[self::DATA][] = $temp->value;
}
// 2 * m.length - (m.length - 1) = m.length + 1
$temp = array_slice($x, $n_length - 1);
// (m.length + 1) + m.length = 2 * m.length + 1
$temp = $class::multiplyHelper($temp, false,
$cache[self::DATA][$key], false);
// (2 * m.length + 1) - (m.length - 1) = m.length + 2
$temp = array_slice($temp[self::VALUE], $n_length + 1);
// m.length + 1
$result = array_slice($x, 0, $n_length + 1);
// m.length + 1
$temp = self::multiplyLower($temp, false, $n, false, $n_length + 1,
$class);
// $temp == array_slice($class::regularMultiply($temp, false, $n,
false)->value, 0, $n_length + 1)
if (self::compareHelper($result, false, $temp[self::VALUE],
$temp[self::SIGN]) < 0) {
$corrector_value = self::array_repeat(0, $n_length + 1);
$corrector_value[count($corrector_value)] = 1;
$result = $class::addHelper($result, false, $corrector_value,
false);
$result = $result[self::VALUE];
}
// at this point, we're subtracting a number with m.length + 1
digits from another number with m.length + 1 digits
$result = $class::subtractHelper($result, false,
$temp[self::VALUE], $temp[self::SIGN]);
while (self::compareHelper($result[self::VALUE],
$result[self::SIGN], $n, false) > 0) {
$result = $class::subtractHelper($result[self::VALUE],
$result[self::SIGN], $n, false);
}
return $result[self::VALUE];
}
/**
* Performs long multiplication up to $stop digits
*
* If you're going to be doing array_slice($product->value, 0,
$stop), some cycles can be saved.
*
* @see self::regularBarrett()
* @param array $x_value
* @param bool $x_negative
* @param array $y_value
* @param bool $y_negative
* @param int $stop
* @param string $class
* @return array
*/
private static function multiplyLower(array $x_value, $x_negative,
array $y_value, $y_negative, $stop, $class)
{
$x_length = count($x_value);
$y_length = count($y_value);
if (!$x_length || !$y_length) { // a 0 is being multiplied
return [
self::VALUE => [],
self::SIGN => false
];
}
if ($x_length < $y_length) {
$temp = $x_value;
$x_value = $y_value;
$y_value = $temp;
$x_length = count($x_value);
$y_length = count($y_value);
}
$product_value = self::array_repeat(0, $x_length + $y_length);
// the following for loop could be removed if the for loop
following it
// (the one with nested for loops) initially set $i to 0, but
// doing so would also make the result in one set of unnecessary
adds,
// since on the outermost loops first pass, $product->value[$k]
is going
// to always be 0
$carry = 0;
for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i
$temp = $x_value[$j] * $y_value[0] + $carry; //
$product_value[$k] == 0
$carry = $class::BASE === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$product_value[$j] = (int) ($temp - $class::BASE_FULL *
$carry);
}
if ($j < $stop) {
$product_value[$j] = $carry;
}
// the above for loop is what the previous comment was talking
about. the
// following for loop is the "one with nested for loops"
for ($i = 1; $i < $y_length; ++$i) {
$carry = 0;
for ($j = 0, $k = $i; $j < $x_length && $k <
$stop; ++$j, ++$k) {
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] +
$carry;
$carry = $class::BASE === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$product_value[$k] = (int) ($temp - $class::BASE_FULL *
$carry);
}
if ($k < $stop) {
$product_value[$k] = $carry;
}
}
return [
self::VALUE => self::trim($product_value),
self::SIGN => $x_negative != $y_negative
];
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Classic.php000064400000001706151161424270021635
0ustar00<?php
/**
* PHP Classic Modular Exponentiation Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
use phpseclib3\Math\BigInteger\Engines\PHP\Base;
/**
* PHP Classic Modular Exponentiation Engine
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Classic extends Base
{
/**
* Regular Division
*
* @param array $x
* @param array $n
* @param string $class
* @return array
*/
protected static function reduce(array $x, array $n, $class)
{
$lhs = new $class();
$lhs->value = $x;
$rhs = new $class();
$rhs->value = $n;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php000064400000036236151161424270022475
0ustar00<?php
/**
* PHP Dynamic Barrett Modular Exponentiation Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
use phpseclib3\Math\BigInteger\Engines\PHP;
use phpseclib3\Math\BigInteger\Engines\PHP\Base;
/**
* PHP Dynamic Barrett Modular Exponentiation Engine
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class EvalBarrett extends Base
{
/**
* Custom Reduction Function
*
* @see self::generateCustomReduction
*/
private static $custom_reduction;
/**
* Barrett Modular Reduction
*
* This calls a dynamically generated loop unrolled function
that's specific to a given modulo.
* Array lookups are avoided as are if statements testing for how many
bits the host OS supports, etc.
*
* @param array $n
* @param array $m
* @param string $class
* @return array
*/
protected static function reduce(array $n, array $m, $class)
{
$inline = self::$custom_reduction;
return $inline($n);
}
/**
* Generate Custom Reduction
*
* @param PHP $m
* @param string $class
* @return callable
*/
protected static function generateCustomReduction(PHP $m, $class)
{
$m_length = count($m->value);
if ($m_length < 5) {
$code = '
$lhs = new ' . $class . '();
$lhs->value = $x;
$rhs = new ' . $class . '();
$rhs->value = [' .
implode(',', array_map(self::class .
'::float2string', $m->value)) . '];
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
';
eval('$func = function ($x) { ' . $code .
'};');
self::$custom_reduction = $func;
//self::$custom_reduction = \Closure::bind($func, $m, $class);
return $func;
}
$lhs = new $class();
$lhs_value = &$lhs->value;
$lhs_value = self::array_repeat(0, $m_length + ($m_length >>
1));
$lhs_value[] = 1;
$rhs = new $class();
list($u, $m1) = $lhs->divide($m);
if ($class::BASE != 26) {
$u = $u->value;
} else {
$lhs_value = self::array_repeat(0, 2 * $m_length);
$lhs_value[] = 1;
$rhs = new $class();
list($u) = $lhs->divide($m);
$u = $u->value;
}
$m = $m->value;
$m1 = $m1->value;
$cutoff = count($m) + (count($m) >> 1);
$code = '
if (count($n) > ' . (2 * count($m)) . ') {
$lhs = new ' . $class . '();
$rhs = new ' . $class . '();
$lhs->value = $n;
$rhs->value = [' .
implode(',', array_map(self::class .
'::float2string', $m)) . '];
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
}
$lsd = array_slice($n, 0, ' . $cutoff . ');
$msd = array_slice($n, ' . $cutoff . ');';
$code .= self::generateInlineTrim('msd');
$code .= self::generateInlineMultiply('msd', $m1,
'temp', $class);
$code .= self::generateInlineAdd('lsd', 'temp',
'n', $class);
$code .= '$temp = array_slice($n, ' . (count($m) - 1) .
');';
$code .= self::generateInlineMultiply('temp', $u,
'temp2', $class);
$code .= self::generateInlineTrim('temp2');
$code .= $class::BASE == 26 ?
'$temp = array_slice($temp2, ' . (count($m) + 1) .
');' :
'$temp = array_slice($temp2, ' . ((count($m) >>
1) + 1) . ');';
$code .= self::generateInlineMultiply('temp', $m,
'temp2', $class);
$code .= self::generateInlineTrim('temp2');
/*
if ($class::BASE == 26) {
$code.= '$n = array_slice($n, 0, ' . (count($m) + 1)
. ');
$temp2 = array_slice($temp2, 0, ' . (count($m) +
1) . ');';
}
*/
$code .= self::generateInlineSubtract2('n',
'temp2', 'temp', $class);
$subcode = self::generateInlineSubtract1('temp', $m,
'temp2', $class);
$subcode .= '$temp = $temp2;';
$code .= self::generateInlineCompare($m, 'temp',
$subcode);
$code .= 'return $temp;';
eval('$func = function ($n) { ' . $code .
'};');
self::$custom_reduction = $func;
return $func;
//self::$custom_reduction = \Closure::bind($func, $m, $class);
}
/**
* Inline Trim
*
* Removes leading zeros
*
* @param string $name
* @return string
*/
private static function generateInlineTrim($name)
{
return '
for ($i = count($' . $name . ') - 1; $i >= 0;
--$i) {
if ($' . $name . '[$i]) {
break;
}
unset($' . $name . '[$i]);
}';
}
/**
* Inline Multiply (unknown, known)
*
* @param string $input
* @param array $arr
* @param string $output
* @param string $class
* @return string
*/
private static function generateInlineMultiply($input, array $arr,
$output, $class)
{
if (!count($arr)) {
return 'return [];';
}
$regular = '
$length = count($' . $input . ');
if (!$length) {
$' . $output . ' = [];
}else{
$' . $output . ' = array_fill(0, $length + ' .
count($arr) . ', 0);
$carry = 0;';
for ($i = 0; $i < count($arr); $i++) {
$regular .= '
$subtemp = $' . $input . '[0] * ' .
$arr[$i];
$regular .= $i ? ' + $carry;' : ';';
$regular .= '$carry = ';
$regular .= $class::BASE === 26 ?
'intval($subtemp / 0x4000000);' :
'$subtemp >> 31;';
$regular .=
'$' . $output . '[' . $i . '] =
';
if ($class::BASE === 26) {
$regular .= '(int) (';
}
$regular .= '$subtemp - ' . $class::BASE_FULL .
' * $carry';
$regular .= $class::BASE === 26 ? ');' :
';';
}
$regular .= '$' . $output . '[' . count($arr) .
'] = $carry;';
$regular .= '
for ($i = 1; $i < $length; ++$i) {';
for ($j = 0; $j < count($arr); $j++) {
$regular .= $j ? '$k++;' : '$k = $i;';
$regular .= '
$subtemp = $' . $output . '[$k] + $' .
$input . '[$i] * ' . $arr[$j];
$regular .= $j ? ' + $carry;' : ';';
$regular .= '$carry = ';
$regular .= $class::BASE === 26 ?
'intval($subtemp / 0x4000000);' :
'$subtemp >> 31;';
$regular .=
'$' . $output . '[$k] = ';
if ($class::BASE === 26) {
$regular .= '(int) (';
}
$regular .= '$subtemp - ' . $class::BASE_FULL .
' * $carry';
$regular .= $class::BASE === 26 ? ');' :
';';
}
$regular .= '$' . $output . '[++$k] = $carry; $carry
= 0;';
$regular .= '}}';
//if (count($arr) < 2 * self::KARATSUBA_CUTOFF) {
//}
return $regular;
}
/**
* Inline Addition
*
* @param string $x
* @param string $y
* @param string $result
* @param string $class
* @return string
*/
private static function generateInlineAdd($x, $y, $result, $class)
{
$code = '
$length = max(count($' . $x . '), count($' . $y
. '));
$' . $result . ' = array_pad($' . $x . ',
$length + 1, 0);
$_' . $y . ' = array_pad($' . $y . ',
$length, 0);
$carry = 0;
for ($i = 0, $j = 1; $j < $length; $i+=2, $j+=2) {
$sum = ($' . $result . '[$j] + $_' . $y .
'[$j]) * ' . $class::BASE_FULL . '
+ $' . $result . '[$i] + $_' . $y
. '[$i] +
$carry;
$carry = $sum >= ' .
self::float2string($class::MAX_DIGIT2) . ';
$sum = $carry ? $sum - ' .
self::float2string($class::MAX_DIGIT2) . ' : $sum;';
$code .= $class::BASE === 26 ?
'$upper = intval($sum / 0x4000000); $' . $result
. '[$i] = (int) ($sum - ' . $class::BASE_FULL . ' *
$upper);' :
'$upper = $sum >> 31; $' . $result .
'[$i] = $sum - ' . $class::BASE_FULL . ' * $upper;';
$code .= '
$' . $result . '[$j] = $upper;
}
if ($j == $length) {
$sum = $' . $result . '[$i] + $_' . $y .
'[$i] + $carry;
$carry = $sum >= ' .
self::float2string($class::BASE_FULL) . ';
$' . $result . '[$i] = $carry ? $sum - ' .
self::float2string($class::BASE_FULL) . ' : $sum;
++$i;
}
if ($carry) {
for (; $' . $result . '[$i] == ' .
$class::MAX_DIGIT . '; ++$i) {
$' . $result . '[$i] = 0;
}
++$' . $result . '[$i];
}';
$code .= self::generateInlineTrim($result);
return $code;
}
/**
* Inline Subtraction 2
*
* For when $known is more digits than $unknown. This is the harder use
case to optimize for.
*
* @param string $known
* @param string $unknown
* @param string $result
* @param string $class
* @return string
*/
private static function generateInlineSubtract2($known, $unknown,
$result, $class)
{
$code = '
$' . $result . ' = $' . $known . ';
$carry = 0;
$size = count($' . $unknown . ');
for ($i = 0, $j = 1; $j < $size; $i+= 2, $j+= 2) {
$sum = ($' . $known . '[$j] - $' . $unknown
. '[$j]) * ' . $class::BASE_FULL . ' + $' . $known .
'[$i]
- $' . $unknown . '[$i]
- $carry;
$carry = $sum < 0;
if ($carry) {
$sum+= ' . self::float2string($class::MAX_DIGIT2)
. ';
}
$subtemp = ';
$code .= $class::BASE === 26 ?
'intval($sum / 0x4000000);' :
'$sum >> 31;';
$code .= '$' . $result . '[$i] = ';
if ($class::BASE === 26) {
$code .= '(int) (';
}
$code .= '$sum - ' . $class::BASE_FULL . ' *
$subtemp';
if ($class::BASE === 26) {
$code .= ')';
}
$code .= ';
$' . $result . '[$j] = $subtemp;
}
if ($j == $size) {
$sum = $' . $known . '[$i] - $' . $unknown .
'[$i] - $carry;
$carry = $sum < 0;
$' . $result . '[$i] = $carry ? $sum + ' .
$class::BASE_FULL . ' : $sum;
++$i;
}
if ($carry) {
for (; !$' . $result . '[$i]; ++$i) {
$' . $result . '[$i] = ' .
$class::MAX_DIGIT . ';
}
--$' . $result . '[$i];
}';
$code .= self::generateInlineTrim($result);
return $code;
}
/**
* Inline Subtraction 1
*
* For when $unknown is more digits than $known. This is the easier use
case to optimize for.
*
* @param string $unknown
* @param array $known
* @param string $result
* @param string $class
* @return string
*/
private static function generateInlineSubtract1($unknown, array $known,
$result, $class)
{
$code = '$' . $result . ' = $' . $unknown .
';';
for ($i = 0, $j = 1; $j < count($known); $i += 2, $j += 2) {
$code .= '$sum = $' . $unknown . '[' . $j .
'] * ' . $class::BASE_FULL . ' + $' . $unknown .
'[' . $i . '] - ';
$code .= self::float2string($known[$j] * $class::BASE_FULL +
$known[$i]);
if ($i != 0) {
$code .= ' - $carry';
}
$code .= ';
if ($carry = $sum < 0) {
$sum+= ' . self::float2string($class::MAX_DIGIT2)
. ';
}
$subtemp = ';
$code .= $class::BASE === 26 ?
'intval($sum / 0x4000000);' :
'$sum >> 31;';
$code .= '
$' . $result . '[' . $i . '] = ';
if ($class::BASE === 26) {
$code .= ' (int) (';
}
$code .= '$sum - ' . $class::BASE_FULL . ' *
$subtemp';
if ($class::BASE === 26) {
$code .= ')';
}
$code .= ';
$' . $result . '[' . $j . '] =
$subtemp;';
}
$code .= '$i = ' . $i . ';';
if ($j == count($known)) {
$code .= '
$sum = $' . $unknown . '[' . $i . '] -
' . $known[$i] . ' - $carry;
$carry = $sum < 0;
$' . $result . '[' . $i . '] = $carry ?
$sum + ' . $class::BASE_FULL . ' : $sum;
++$i;';
}
$code .= '
if ($carry) {
for (; !$' . $result . '[$i]; ++$i) {
$' . $result . '[$i] = ' .
$class::MAX_DIGIT . ';
}
--$' . $result . '[$i];
}';
$code .= self::generateInlineTrim($result);
return $code;
}
/**
* Inline Comparison
*
* If $unknown >= $known then loop
*
* @param array $known
* @param string $unknown
* @param string $subcode
* @return string
*/
private static function generateInlineCompare(array $known, $unknown,
$subcode)
{
$uniqid = uniqid();
$code = 'loop_' . $uniqid . ':
$clength = count($' . $unknown . ');
switch (true) {
case $clength < ' . count($known) . ':
goto end_' . $uniqid . ';
case $clength > ' . count($known) . ':';
for ($i = count($known) - 1; $i >= 0; $i--) {
$code .= '
case $' . $unknown . '[' . $i . '] >
' . $known[$i] . ':
goto subcode_' . $uniqid . ';
case $' . $unknown . '[' . $i . '] <
' . $known[$i] . ':
goto end_' . $uniqid . ';';
}
$code .= '
default:
// do subcode
}
subcode_' . $uniqid . ':' . $subcode . '
goto loop_' . $uniqid . ';
end_' . $uniqid . ':';
return $code;
}
/**
* Convert a float to a string
*
* If you do echo floatval(pow(2, 52)) you'll get
4.6116860184274E+18. It /can/ be displayed without a loss of
* precision but displayed in this way there will be precision loss,
hence the need for this method.
*
* @param int|float $num
* @return string
*/
private static function float2string($num)
{
if (!is_float($num)) {
return (string) $num;
}
if ($num < 0) {
return '-' . self::float2string(abs($num));
}
$temp = '';
while ($num) {
$temp = fmod($num, 10) . $temp;
$num = floor($num / 10);
}
return $temp;
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Montgomery.php000064400000011024151161424270022406
0ustar00<?php
/**
* PHP Montgomery Modular Exponentiation Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
use phpseclib3\Math\BigInteger\Engines\PHP\Montgomery as Progenitor;
/**
* PHP Montgomery Modular Exponentiation Engine
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Montgomery extends Progenitor
{
/**
* Prepare a number for use in Montgomery Modular Reductions
*
* @param array $x
* @param array $n
* @param string $class
* @return array
*/
protected static function prepareReduce(array $x, array $n, $class)
{
$lhs = new $class();
$lhs->value = array_merge(self::array_repeat(0, count($n)), $x);
$rhs = new $class();
$rhs->value = $n;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
}
/**
* Montgomery Multiply
*
* Interleaves the montgomery reduction and long multiplication
algorithms together as described in
* {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
*
* @param array $x
* @param array $n
* @param string $class
* @return array
*/
protected static function reduce(array $x, array $n, $class)
{
static $cache = [
self::VARIABLE => [],
self::DATA => []
];
if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]);
$cache[self::VARIABLE][] = $x;
$cache[self::DATA][] = self::modInverse67108864($n, $class);
}
$k = count($n);
$result = [self::VALUE => $x];
for ($i = 0; $i < $k; ++$i) {
$temp = $result[self::VALUE][$i] * $cache[self::DATA][$key];
$temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ?
intval($temp / 0x4000000) : ($temp >> 31));
$temp = $class::regularMultiply([$temp], $n);
$temp = array_merge(self::array_repeat(0, $i), $temp);
$result = $class::addHelper($result[self::VALUE], false, $temp,
false);
}
$result[self::VALUE] = array_slice($result[self::VALUE], $k);
if (self::compareHelper($result, false, $n, false) >= 0) {
$result = $class::subtractHelper($result[self::VALUE], false,
$n, false);
}
return $result[self::VALUE];
}
/**
* Modular Inverse of a number mod 2**26 (eg. 67108864)
*
* Based off of the bnpInvDigit function implemented and justified in
the following URL:
*
* {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js}
*
* The following URL provides more info:
*
* {@link
http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85}
*
* As for why we do all the bitmasking... strange things can happen
when converting from floats to ints. For
* instance, on some computers, var_dump((int) -4294967297) yields
int(-1) and on others, it yields
* int(-2147483648). To avoid problems stemming from this, we use
bitmasks to guarantee that ints aren't
* auto-converted to floats. The outermost bitmask is present because
without it, there's no guarantee that
* the "residue" returned would be the so-called "common
residue". We use fmod, in the last step, because the
* maximum possible $x is 26 bits and the maximum $result is 16 bits.
Thus, we have to be able to handle up to
* 40 bits, which only 64-bit floating points will support.
*
* Thanks to Pedro Gimeno Fortea for input!
*
* @param array $x
* @param string $class
* @return int
*/
protected static function modInverse67108864(array $x, $class) // 2**26
== 67,108,864
{
$x = -$x[0];
$result = $x & 0x3; // x**-1 mod 2**2
$result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod
2**4
$result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF;
// x**-1 mod 2**8
$result = ($result * ((2 - ($x & 0xFFFF) * $result) &
0xFFFF)) & 0xFFFF; // x**-1 mod 2**16
$result = $class::BASE == 26 ?
fmod($result * (2 - fmod($x * $result, $class::BASE_FULL)),
$class::BASE_FULL) : // x**-1 mod 2**26
($result * (2 - ($x * $result) % $class::BASE_FULL)) %
$class::BASE_FULL;
return $result & $class::MAX_DIGIT;
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/MontgomeryMult.php000064400000005557151161424270023266
0ustar00<?php
/**
* PHP Montgomery Modular Exponentiation Engine with interleaved
multiplication
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
use phpseclib3\Math\BigInteger\Engines\PHP;
/**
* PHP Montgomery Modular Exponentiation Engine with interleaved
multiplication
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class MontgomeryMult extends Montgomery
{
/**
* Montgomery Multiply
*
* Interleaves the montgomery reduction and long multiplication
algorithms together as described in
* {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
*
* @see self::_prepMontgomery()
* @see self::_montgomery()
* @param array $x
* @param array $y
* @param array $m
* @param class-string<PHP> $class
* @return array
*/
public static function multiplyReduce(array $x, array $y, array $m,
$class)
{
// 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 = [
self::VARIABLE => [],
self::DATA => []
];
if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]);
$cache[self::VARIABLE][] = $m;
$cache[self::DATA][] = self::modInverse67108864($m, $class);
}
$n = max(count($x), count($y), count($m));
$x = array_pad($x, $n, 0);
$y = array_pad($y, $n, 0);
$m = array_pad($m, $n, 0);
$a = [self::VALUE => self::array_repeat(0, $n + 1)];
for ($i = 0; $i < $n; ++$i) {
$temp = $a[self::VALUE][0] + $x[$i] * $y[0];
$temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ?
intval($temp / 0x4000000) : ($temp >> 31));
$temp = $temp * $cache[self::DATA][$key];
$temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ?
intval($temp / 0x4000000) : ($temp >> 31));
$temp = $class::addHelper($class::regularMultiply([$x[$i]],
$y), false, $class::regularMultiply([$temp], $m), false);
$a = $class::addHelper($a[self::VALUE], false,
$temp[self::VALUE], false);
$a[self::VALUE] = array_slice($a[self::VALUE], 1);
}
if (self::compareHelper($a[self::VALUE], false, $m, false) >= 0)
{
$a = $class::subtractHelper($a[self::VALUE], false, $m, false);
}
return $a[self::VALUE];
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/PowerOfTwo.php000064400000002540151161424270022324
0ustar00<?php
/**
* PHP Power of Two Modular Exponentiation Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
use phpseclib3\Math\BigInteger\Engines\PHP\Base;
/**
* PHP Power Of Two Modular Exponentiation Engine
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PowerOfTwo extends Base
{
/**
* Prepare a number for use in Montgomery Modular Reductions
*
* @param array $x
* @param array $n
* @param string $class
* @return array
*/
protected static function prepareReduce(array $x, array $n, $class)
{
return self::reduce($x, $n, $class);
}
/**
* Power Of Two Reduction
*
* @param array $x
* @param array $n
* @param string $class
* @return array
*/
protected static function reduce(array $x, array $n, $class)
{
$lhs = new $class();
$lhs->value = $x;
$rhs = new $class();
$rhs->value = $n;
$temp = new $class();
$temp->value = [1];
$result = $lhs->bitwise_and($rhs->subtract($temp));
return $result->value;
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/PHP.php000064400000113702151161424270016135
0ustar00<?php
/**
* Pure-PHP BigInteger Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Exception\BadConfigurationException;
/**
* Pure-PHP Engine.
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PHP extends Engine
{
/**#@+
* Array constants
*
* Rather than create a thousands and thousands of new BigInteger
objects in repeated function calls to add() and
* multiply() or whatever, we'll just work directly on arrays,
taking them in as parameters and returning them.
*
*/
/**
* $result[self::VALUE] contains the value.
*/
const VALUE = 0;
/**
* $result[self::SIGN] contains the sign.
*/
const SIGN = 1;
/**#@-*/
/**
* Karatsuba Cutoff
*
* At what point do we switch between Karatsuba multiplication and
schoolbook long multiplication?
*
*/
const KARATSUBA_CUTOFF = 25;
/**
* Can Bitwise operations be done fast?
*
* @see parent::bitwise_leftRotate()
* @see parent::bitwise_rightRotate()
*/
const FAST_BITWISE = true;
/**
* Engine Directory
*
* @see parent::setModExpEngine
*/
const ENGINE_DIR = 'PHP';
/**
* Default constructor
*
* @param mixed $x integer Base-10 number or base-$base number if $base
set.
* @param int $base
* @return PHP
* @see parent::__construct()
*/
public function __construct($x = 0, $base = 10)
{
if (!isset(static::$isValidEngine[static::class])) {
static::$isValidEngine[static::class] =
static::isValidEngine();
}
if (!static::$isValidEngine[static::class]) {
throw new BadConfigurationException(static::class . ' is
not setup correctly on this system');
}
$this->value = [];
parent::__construct($x, $base);
}
/**
* Initialize a PHP BigInteger Engine instance
*
* @param int $base
* @see parent::__construct()
*/
protected function initialize($base)
{
switch (abs($base)) {
case 16:
$x = (strlen($this->value) & 1) ? '0' .
$this->value : $this->value;
$temp = new static(Strings::hex2bin($x), 256);
$this->value = $temp->value;
break;
case 10:
$temp = new static();
$multiplier = new static();
$multiplier->value = [static::MAX10];
$x = $this->value;
if ($x[0] == '-') {
$this->is_negative = true;
$x = substr($x, 1);
}
$x = str_pad(
$x,
strlen($x) + ((static::MAX10LEN - 1) * strlen($x)) %
static::MAX10LEN,
0,
STR_PAD_LEFT
);
while (strlen($x)) {
$temp = $temp->multiply($multiplier);
$temp = $temp->add(new
static($this->int2bytes(substr($x, 0, static::MAX10LEN)), 256));
$x = substr($x, static::MAX10LEN);
}
$this->value = $temp->value;
}
}
/**
* Pads strings so that unpack may be used on them
*
* @param string $str
* @return string
*/
protected function pad($str)
{
$length = strlen($str);
$pad = 4 - (strlen($str) % 4);
return str_pad($str, $length + $pad, "\0", STR_PAD_LEFT);
}
/**
* Converts a BigInteger to a base-10 number.
*
* @return string
*/
public function toString()
{
if (!count($this->value)) {
return '0';
}
$temp = clone $this;
$temp->bitmask = false;
$temp->is_negative = false;
$divisor = new static();
$divisor->value = [static::MAX10];
$result = '';
while (count($temp->value)) {
list($temp, $mod) = $temp->divide($divisor);
$result = str_pad(
isset($mod->value[0]) ? $mod->value[0] :
'',
static::MAX10LEN,
'0',
STR_PAD_LEFT
) . $result;
}
$result = ltrim($result, '0');
if (empty($result)) {
$result = '0';
}
if ($this->is_negative) {
$result = '-' . $result;
}
return $result;
}
/**
* Converts a BigInteger to a byte string (eg. base-256).
*
* @param bool $twos_compliment
* @return string
*/
public function toBytes($twos_compliment = false)
{
if ($twos_compliment) {
return $this->toBytesHelper();
}
if (!count($this->value)) {
return $this->precision > 0 ? str_repeat(chr(0),
($this->precision + 1) >> 3) : '';
}
$result = $this->bitwise_small_split(8);
$result = implode('', array_map('chr',
$result));
return $this->precision > 0 ?
str_pad(
substr($result, -(($this->precision + 7) >> 3)),
($this->precision + 7) >> 3,
chr(0),
STR_PAD_LEFT
) :
$result;
}
/**
* Performs addition.
*
* @param array $x_value
* @param bool $x_negative
* @param array $y_value
* @param bool $y_negative
* @return array
*/
protected static function addHelper(array $x_value, $x_negative, array
$y_value, $y_negative)
{
$x_size = count($x_value);
$y_size = count($y_value);
if ($x_size == 0) {
return [
self::VALUE => $y_value,
self::SIGN => $y_negative
];
} elseif ($y_size == 0) {
return [
self::VALUE => $x_value,
self::SIGN => $x_negative
];
}
// subtract, if appropriate
if ($x_negative != $y_negative) {
if ($x_value == $y_value) {
return [
self::VALUE => [],
self::SIGN => false
];
}
$temp = self::subtractHelper($x_value, false, $y_value, false);
$temp[self::SIGN] = self::compareHelper($x_value, false,
$y_value, false) > 0 ?
$x_negative : $y_negative;
return $temp;
}
if ($x_size < $y_size) {
$size = $x_size;
$value = $y_value;
} else {
$size = $y_size;
$value = $x_value;
}
$value[count($value)] = 0; // just in case the carry adds an extra
digit
$carry = 0;
for ($i = 0, $j = 1; $j < $size; $i += 2, $j += 2) {
//$sum = $x_value[$j] * static::BASE_FULL + $x_value[$i] +
$y_value[$j] * static::BASE_FULL + $y_value[$i] + $carry;
$sum = ($x_value[$j] + $y_value[$j]) * static::BASE_FULL +
$x_value[$i] + $y_value[$i] + $carry;
$carry = $sum >= static::MAX_DIGIT2; // eg. floor($sum /
2**52); only possible values (in any base) are 0 and 1
$sum = $carry ? $sum - static::MAX_DIGIT2 : $sum;
$temp = static::BASE === 26 ? intval($sum / 0x4000000) : ($sum
>> 31);
$value[$i] = (int)($sum - static::BASE_FULL * $temp); // eg. a
faster alternative to fmod($sum, 0x4000000)
$value[$j] = $temp;
}
if ($j == $size) { // ie. if $y_size is odd
$sum = $x_value[$i] + $y_value[$i] + $carry;
$carry = $sum >= static::BASE_FULL;
$value[$i] = $carry ? $sum - static::BASE_FULL : $sum;
++$i; // ie. let $i = $j since we've just done $value[$i]
}
if ($carry) {
for (; $value[$i] == static::MAX_DIGIT; ++$i) {
$value[$i] = 0;
}
++$value[$i];
}
return [
self::VALUE => self::trim($value),
self::SIGN => $x_negative
];
}
/**
* Performs subtraction.
*
* @param array $x_value
* @param bool $x_negative
* @param array $y_value
* @param bool $y_negative
* @return array
*/
public static function subtractHelper(array $x_value, $x_negative,
array $y_value, $y_negative)
{
$x_size = count($x_value);
$y_size = count($y_value);
if ($x_size == 0) {
return [
self::VALUE => $y_value,
self::SIGN => !$y_negative
];
} elseif ($y_size == 0) {
return [
self::VALUE => $x_value,
self::SIGN => $x_negative
];
}
// add, if appropriate (ie. -$x - +$y or +$x - -$y)
if ($x_negative != $y_negative) {
$temp = self::addHelper($x_value, false, $y_value, false);
$temp[self::SIGN] = $x_negative;
return $temp;
}
$diff = self::compareHelper($x_value, $x_negative, $y_value,
$y_negative);
if (!$diff) {
return [
self::VALUE => [],
self::SIGN => false
];
}
// switch $x and $y around, if appropriate.
if ((!$x_negative && $diff < 0) || ($x_negative
&& $diff > 0)) {
$temp = $x_value;
$x_value = $y_value;
$y_value = $temp;
$x_negative = !$x_negative;
$x_size = count($x_value);
$y_size = count($y_value);
}
// at this point, $x_value should be at least as big as - if not
bigger than - $y_value
$carry = 0;
for ($i = 0, $j = 1; $j < $y_size; $i += 2, $j += 2) {
$sum = ($x_value[$j] - $y_value[$j]) * static::BASE_FULL +
$x_value[$i] - $y_value[$i] - $carry;
$carry = $sum < 0; // eg. floor($sum / 2**52); only possible
values (in any base) are 0 and 1
$sum = $carry ? $sum + static::MAX_DIGIT2 : $sum;
$temp = static::BASE === 26 ? intval($sum / 0x4000000) : ($sum
>> 31);
$x_value[$i] = (int)($sum - static::BASE_FULL * $temp);
$x_value[$j] = $temp;
}
if ($j == $y_size) { // ie. if $y_size is odd
$sum = $x_value[$i] - $y_value[$i] - $carry;
$carry = $sum < 0;
$x_value[$i] = $carry ? $sum + static::BASE_FULL : $sum;
++$i;
}
if ($carry) {
for (; !$x_value[$i]; ++$i) {
$x_value[$i] = static::MAX_DIGIT;
}
--$x_value[$i];
}
return [
self::VALUE => self::trim($x_value),
self::SIGN => $x_negative
];
}
/**
* Performs multiplication.
*
* @param array $x_value
* @param bool $x_negative
* @param array $y_value
* @param bool $y_negative
* @return array
*/
protected static function multiplyHelper(array $x_value, $x_negative,
array $y_value, $y_negative)
{
//if ( $x_value == $y_value ) {
// return [
// self::VALUE => self::square($x_value),
// self::SIGN => $x_sign != $y_value
// ];
//}
$x_length = count($x_value);
$y_length = count($y_value);
if (!$x_length || !$y_length) { // a 0 is being multiplied
return [
self::VALUE => [],
self::SIGN => false
];
}
return [
self::VALUE => min($x_length, $y_length) < 2 *
self::KARATSUBA_CUTOFF ?
self::trim(self::regularMultiply($x_value, $y_value)) :
self::trim(self::karatsuba($x_value, $y_value)),
self::SIGN => $x_negative != $y_negative
];
}
/**
* Performs Karatsuba multiplication on two BigIntegers
*
* See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm
Karatsuba algorithm} and
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM
5.2.3}.
*
* @param array $x_value
* @param array $y_value
* @return array
*/
private static function karatsuba(array $x_value, array $y_value)
{
$m = min(count($x_value) >> 1, count($y_value) >> 1);
if ($m < self::KARATSUBA_CUTOFF) {
return self::regularMultiply($x_value, $y_value);
}
$x1 = array_slice($x_value, $m);
$x0 = array_slice($x_value, 0, $m);
$y1 = array_slice($y_value, $m);
$y0 = array_slice($y_value, 0, $m);
$z2 = self::karatsuba($x1, $y1);
$z0 = self::karatsuba($x0, $y0);
$z1 = self::addHelper($x1, false, $x0, false);
$temp = self::addHelper($y1, false, $y0, false);
$z1 = self::karatsuba($z1[self::VALUE], $temp[self::VALUE]);
$temp = self::addHelper($z2, false, $z0, false);
$z1 = self::subtractHelper($z1, false, $temp[self::VALUE], false);
$z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
$z1[self::VALUE] = array_merge(array_fill(0, $m, 0),
$z1[self::VALUE]);
$xy = self::addHelper($z2, false, $z1[self::VALUE],
$z1[self::SIGN]);
$xy = self::addHelper($xy[self::VALUE], $xy[self::SIGN], $z0,
false);
return $xy[self::VALUE];
}
/**
* Performs long multiplication on two BigIntegers
*
* Modeled after 'multiply' in MutableBigInteger.java.
*
* @param array $x_value
* @param array $y_value
* @return array
*/
protected static function regularMultiply(array $x_value, array
$y_value)
{
$x_length = count($x_value);
$y_length = count($y_value);
if (!$x_length || !$y_length) { // a 0 is being multiplied
return [];
}
$product_value = self::array_repeat(0, $x_length + $y_length);
// the following for loop could be removed if the for loop
following it
// (the one with nested for loops) initially set $i to 0, but
// doing so would also make the result in one set of unnecessary
adds,
// since on the outermost loops first pass, $product->value[$k]
is going
// to always be 0
$carry = 0;
for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0
$temp = $x_value[$j] * $y_value[0] + $carry; //
$product_value[$k] == 0
$carry = static::BASE === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$product_value[$j] = (int)($temp - static::BASE_FULL * $carry);
}
$product_value[$j] = $carry;
// the above for loop is what the previous comment was talking
about. the
// following for loop is the "one with nested for loops"
for ($i = 1; $i < $y_length; ++$i) {
$carry = 0;
for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) {
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] +
$carry;
$carry = static::BASE === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$product_value[$k] = (int)($temp - static::BASE_FULL *
$carry);
}
$product_value[$k] = $carry;
}
return $product_value;
}
/**
* Divides two BigIntegers.
*
* Returns an array whose first element contains the quotient and whose
second element contains the
* "common residue". If the remainder would be positive, the
"common residue" and the remainder are the
* same. If the remainder would be negative, the "common
residue" is equal to the sum of the remainder
* and the divisor (basically, the "common residue" is the
first positive modulo).
*
* @return array{static, static}
* @internal This function is based off of
* {@link
http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}.
*/
protected function divideHelper(PHP $y)
{
if (count($y->value) == 1) {
list($q, $r) = $this->divide_digit($this->value,
$y->value[0]);
$quotient = new static();
$remainder = new static();
$quotient->value = $q;
$remainder->value = [$r];
$quotient->is_negative = $this->is_negative !=
$y->is_negative;
return [$this->normalize($quotient),
$this->normalize($remainder)];
}
$x = clone $this;
$y = clone $y;
$x_sign = $x->is_negative;
$y_sign = $y->is_negative;
$x->is_negative = $y->is_negative = false;
$diff = $x->compare($y);
if (!$diff) {
$temp = new static();
$temp->value = [1];
$temp->is_negative = $x_sign != $y_sign;
return [$this->normalize($temp),
$this->normalize(static::$zero[static::class])];
}
if ($diff < 0) {
// if $x is negative, "add" $y.
if ($x_sign) {
$x = $y->subtract($x);
}
return [$this->normalize(static::$zero[static::class]),
$this->normalize($x)];
}
// normalize $x and $y as described in HAC 14.23 / 14.24
$msb = $y->value[count($y->value) - 1];
for ($shift = 0; !($msb & static::MSB); ++$shift) {
$msb <<= 1;
}
$x->lshift($shift);
$y->lshift($shift);
$y_value = &$y->value;
$x_max = count($x->value) - 1;
$y_max = count($y->value) - 1;
$quotient = new static();
$quotient_value = &$quotient->value;
$quotient_value = self::array_repeat(0, $x_max - $y_max + 1);
static $temp, $lhs, $rhs;
if (!isset($temp)) {
$temp = new static();
$lhs = new static();
$rhs = new static();
}
if (static::class != get_class($temp)) {
$temp = new static();
$lhs = new static();
$rhs = new static();
}
$temp_value = &$temp->value;
$rhs_value = &$rhs->value;
// $temp = $y << ($x_max - $y_max-1) in base 2**26
$temp_value = array_merge(self::array_repeat(0, $x_max - $y_max),
$y_value);
while ($x->compare($temp) >= 0) {
// calculate the "common residue"
++$quotient_value[$x_max - $y_max];
$x = $x->subtract($temp);
$x_max = count($x->value) - 1;
}
for ($i = $x_max; $i >= $y_max + 1; --$i) {
$x_value = &$x->value;
$x_window = [
isset($x_value[$i]) ? $x_value[$i] : 0,
isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0,
isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0
];
$y_window = [
$y_value[$y_max],
($y_max > 0) ? $y_value[$y_max - 1] : 0
];
$q_index = $i - $y_max - 1;
if ($x_window[0] == $y_window[0]) {
$quotient_value[$q_index] = static::MAX_DIGIT;
} else {
$quotient_value[$q_index] = self::safe_divide(
$x_window[0] * static::BASE_FULL + $x_window[1],
$y_window[0]
);
}
$temp_value = [$y_window[1], $y_window[0]];
$lhs->value = [$quotient_value[$q_index]];
$lhs = $lhs->multiply($temp);
$rhs_value = [$x_window[2], $x_window[1], $x_window[0]];
while ($lhs->compare($rhs) > 0) {
--$quotient_value[$q_index];
$lhs->value = [$quotient_value[$q_index]];
$lhs = $lhs->multiply($temp);
}
$adjust = self::array_repeat(0, $q_index);
$temp_value = [$quotient_value[$q_index]];
$temp = $temp->multiply($y);
$temp_value = &$temp->value;
if (count($temp_value)) {
$temp_value = array_merge($adjust, $temp_value);
}
$x = $x->subtract($temp);
if ($x->compare(static::$zero[static::class]) < 0) {
$temp_value = array_merge($adjust, $y_value);
$x = $x->add($temp);
--$quotient_value[$q_index];
}
$x_max = count($x_value) - 1;
}
// unnormalize the remainder
$x->rshift($shift);
$quotient->is_negative = $x_sign != $y_sign;
// calculate the "common residue", if appropriate
if ($x_sign) {
$y->rshift($shift);
$x = $y->subtract($x);
}
return [$this->normalize($quotient), $this->normalize($x)];
}
/**
* Divides a BigInteger by a regular integer
*
* abc / x = a00 / x + b0 / x + c / x
*
* @param array $dividend
* @param int $divisor
* @return array
*/
private static function divide_digit(array $dividend, $divisor)
{
$carry = 0;
$result = [];
for ($i = count($dividend) - 1; $i >= 0; --$i) {
$temp = static::BASE_FULL * $carry + $dividend[$i];
$result[$i] = self::safe_divide($temp, $divisor);
$carry = (int)($temp - $divisor * $result[$i]);
}
return [$result, $carry];
}
/**
* 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.
*
* @param int $x
* @param int $y
* @return int
*/
private static function safe_divide($x, $y)
{
if (static::BASE === 26) {
return (int)($x / $y);
}
// static::BASE === 31
/** @var int */
return ($x - ($x % $y)) / $y;
}
/**
* Convert an array / boolean to a PHP BigInteger object
*
* @param array $arr
* @return static
*/
protected function convertToObj(array $arr)
{
$result = new static();
$result->value = $arr[self::VALUE];
$result->is_negative = $arr[self::SIGN];
return $this->normalize($result);
}
/**
* Normalize
*
* Removes leading zeros and truncates (if necessary) to maintain the
appropriate precision
*
* @param PHP $result
* @return static
*/
protected function normalize(PHP $result)
{
$result->precision = $this->precision;
$result->bitmask = $this->bitmask;
$value = &$result->value;
if (!count($value)) {
$result->is_negative = false;
return $result;
}
$value = static::trim($value);
if (!empty($result->bitmask->value)) {
$length = min(count($value),
count($result->bitmask->value));
$value = array_slice($value, 0, $length);
for ($i = 0; $i < $length; ++$i) {
$value[$i] = $value[$i] &
$result->bitmask->value[$i];
}
$value = static::trim($value);
}
return $result;
}
/**
* Compares two numbers.
*
* @param array $x_value
* @param bool $x_negative
* @param array $y_value
* @param bool $y_negative
* @return int
* @see static::compare()
*/
protected static function compareHelper(array $x_value, $x_negative,
array $y_value, $y_negative)
{
if ($x_negative != $y_negative) {
return (!$x_negative && $y_negative) ? 1 : -1;
}
$result = $x_negative ? -1 : 1;
if (count($x_value) != count($y_value)) {
return (count($x_value) > count($y_value)) ? $result :
-$result;
}
$size = max(count($x_value), count($y_value));
$x_value = array_pad($x_value, $size, 0);
$y_value = array_pad($y_value, $size, 0);
for ($i = count($x_value) - 1; $i >= 0; --$i) {
if ($x_value[$i] != $y_value[$i]) {
return ($x_value[$i] > $y_value[$i]) ? $result :
-$result;
}
}
return 0;
}
/**
* Absolute value.
*
* @return PHP
*/
public function abs()
{
$temp = new static();
$temp->value = $this->value;
return $temp;
}
/**
* Trim
*
* Removes leading zeros
*
* @param list<static> $value
* @return list<static>
*/
protected static function trim(array $value)
{
for ($i = count($value) - 1; $i >= 0; --$i) {
if ($value[$i]) {
break;
}
unset($value[$i]);
}
return $value;
}
/**
* Logical Right Shift
*
* Shifts BigInteger's by $shift bits, effectively dividing by
2**$shift.
*
* @param int $shift
* @return PHP
*/
public function bitwise_rightShift($shift)
{
$temp = new static();
// could just replace lshift with this, but then all lshift() calls
would need to be rewritten
// and I don't want to do that...
$temp->value = $this->value;
$temp->rshift($shift);
return $this->normalize($temp);
}
/**
* Logical Left Shift
*
* Shifts BigInteger's by $shift bits, effectively multiplying by
2**$shift.
*
* @param int $shift
* @return PHP
*/
public function bitwise_leftShift($shift)
{
$temp = new static();
// could just replace _rshift with this, but then all _lshift()
calls would need to be rewritten
// and I don't want to do that...
$temp->value = $this->value;
$temp->lshift($shift);
return $this->normalize($temp);
}
/**
* Converts 32-bit integers to bytes.
*
* @param int $x
* @return string
*/
private static function int2bytes($x)
{
return ltrim(pack('N', $x), chr(0));
}
/**
* Array Repeat
*
* @param int $input
* @param int $multiplier
* @return array
*/
protected static function array_repeat($input, $multiplier)
{
return $multiplier ? array_fill(0, $multiplier, $input) : [];
}
/**
* Logical Left Shift
*
* Shifts BigInteger's by $shift bits.
*
* @param int $shift
*/
protected function lshift($shift)
{
if ($shift == 0) {
return;
}
$num_digits = (int)($shift / static::BASE);
$shift %= static::BASE;
$shift = 1 << $shift;
$carry = 0;
for ($i = 0; $i < count($this->value); ++$i) {
$temp = $this->value[$i] * $shift + $carry;
$carry = static::BASE === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$this->value[$i] = (int)($temp - $carry *
static::BASE_FULL);
}
if ($carry) {
$this->value[count($this->value)] = $carry;
}
while ($num_digits--) {
array_unshift($this->value, 0);
}
}
/**
* Logical Right Shift
*
* Shifts BigInteger's by $shift bits.
*
* @param int $shift
*/
protected function rshift($shift)
{
if ($shift == 0) {
return;
}
$num_digits = (int)($shift / static::BASE);
$shift %= static::BASE;
$carry_shift = static::BASE - $shift;
$carry_mask = (1 << $shift) - 1;
if ($num_digits) {
$this->value = array_slice($this->value, $num_digits);
}
$carry = 0;
for ($i = count($this->value) - 1; $i >= 0; --$i) {
$temp = $this->value[$i] >> $shift | $carry;
$carry = ($this->value[$i] & $carry_mask) <<
$carry_shift;
$this->value[$i] = $temp;
}
$this->value = static::trim($this->value);
}
/**
* Performs modular exponentiation.
*
* @param PHP $e
* @param PHP $n
* @return PHP
*/
protected function powModInner(PHP $e, PHP $n)
{
try {
$class = static::$modexpEngine[static::class];
return $class::powModHelper($this, $e, $n, static::class);
} catch (\Exception $err) {
return PHP\DefaultEngine::powModHelper($this, $e, $n,
static::class);
}
}
/**
* Performs squaring
*
* @param list<static> $x
* @return list<static>
*/
protected static function square(array $x)
{
return count($x) < 2 * self::KARATSUBA_CUTOFF ?
self::trim(self::baseSquare($x)) :
self::trim(self::karatsubaSquare($x));
}
/**
* Performs traditional squaring on two BigIntegers
*
* Squaring can be done faster than multiplying a number by itself can
be. See
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7
HAC 14.2.4} /
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM
5.3} for more information.
*
* @param array $value
* @return array
*/
protected static function baseSquare(array $value)
{
if (empty($value)) {
return [];
}
$square_value = self::array_repeat(0, 2 * count($value));
for ($i = 0, $max_index = count($value) - 1; $i <= $max_index;
++$i) {
$i2 = $i << 1;
$temp = $square_value[$i2] + $value[$i] * $value[$i];
$carry = static::BASE === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$square_value[$i2] = (int)($temp - static::BASE_FULL * $carry);
// 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) {
$temp = $square_value[$k] + 2 * $value[$j] * $value[$i] +
$carry;
$carry = static::BASE === 26 ? intval($temp / 0x4000000) :
($temp >> 31);
$square_value[$k] = (int)($temp - static::BASE_FULL *
$carry);
}
// the following line can yield values larger 2**15. at this
point, PHP should switch
// over to floats.
$square_value[$i + $max_index + 1] = $carry;
}
return $square_value;
}
/**
* Performs Karatsuba "squaring" on two BigIntegers
*
* See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm
Karatsuba algorithm} and
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM
5.3.4}.
*
* @param array $value
* @return array
*/
protected static function karatsubaSquare(array $value)
{
$m = count($value) >> 1;
if ($m < self::KARATSUBA_CUTOFF) {
return self::baseSquare($value);
}
$x1 = array_slice($value, $m);
$x0 = array_slice($value, 0, $m);
$z2 = self::karatsubaSquare($x1);
$z0 = self::karatsubaSquare($x0);
$z1 = self::addHelper($x1, false, $x0, false);
$z1 = self::karatsubaSquare($z1[self::VALUE]);
$temp = self::addHelper($z2, false, $z0, false);
$z1 = self::subtractHelper($z1, false, $temp[self::VALUE], false);
$z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
$z1[self::VALUE] = array_merge(array_fill(0, $m, 0),
$z1[self::VALUE]);
$xx = self::addHelper($z2, false, $z1[self::VALUE],
$z1[self::SIGN]);
$xx = self::addHelper($xx[self::VALUE], $xx[self::SIGN], $z0,
false);
return $xx[self::VALUE];
}
/**
* Make the current number odd
*
* If the current number is odd it'll be unchanged. If it's
even, one will be added to it.
*
* @see self::randomPrime()
*/
protected function make_odd()
{
$this->value[0] |= 1;
}
/**
* Test the number against small primes.
*
* @see self::isPrime()
*/
protected function testSmallPrimes()
{
if ($this->value == [1]) {
return false;
}
if ($this->value == [2]) {
return true;
}
if (~$this->value[0] & 1) {
return false;
}
$value = $this->value;
foreach (static::PRIMES as $prime) {
list(, $r) = self::divide_digit($value, $prime);
if (!$r) {
return count($value) == 1 && $value[0] == $prime;
}
}
return true;
}
/**
* Scan for 1 and right shift by that amount
*
* ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n,
gmp_pow(gmp_init('2'), $s));
*
* @param PHP $r
* @return int
* @see self::isPrime()
*/
public static function scan1divide(PHP $r)
{
$r_value = &$r->value;
for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i)
{
$temp = ~$r_value[$i] & static::MAX_DIGIT;
for ($j = 1; ($temp >> $j) & 1; ++$j) {
}
if ($j <= static::BASE) {
break;
}
}
$s = static::BASE * $i + $j;
$r->rshift($s);
return $s;
}
/**
* Performs exponentiation.
*
* @param PHP $n
* @return PHP
*/
protected function powHelper(PHP $n)
{
if ($n->compare(static::$zero[static::class]) == 0) {
return new static(1);
} // n^0 = 1
$temp = clone $this;
while (!$n->equals(static::$one[static::class])) {
$temp = $temp->multiply($this);
$n = $n->subtract(static::$one[static::class]);
}
return $temp;
}
/**
* Is Odd?
*
* @return bool
*/
public function isOdd()
{
return (bool)($this->value[0] & 1);
}
/**
* Tests if a bit is set
*
* @return bool
*/
public function testBit($x)
{
$digit = (int) floor($x / static::BASE);
$bit = $x % static::BASE;
if (!isset($this->value[$digit])) {
return false;
}
return (bool)($this->value[$digit] & (1 << $bit));
}
/**
* Is Negative?
*
* @return bool
*/
public function isNegative()
{
return $this->is_negative;
}
/**
* Negate
*
* Given $k, returns -$k
*
* @return static
*/
public function negate()
{
$temp = clone $this;
$temp->is_negative = !$temp->is_negative;
return $temp;
}
/**
* Bitwise Split
*
* Splits BigInteger's into chunks of $split bits
*
* @param int $split
* @return list<static>
*/
public function bitwise_split($split)
{
if ($split < 1) {
throw new \RuntimeException('Offset must be greater than
1');
}
$width = (int)($split / static::BASE);
if (!$width) {
$arr = $this->bitwise_small_split($split);
return array_map(function ($digit) {
$temp = new static();
$temp->value = $digit != 0 ? [$digit] : [];
return $temp;
}, $arr);
}
$vals = [];
$val = $this->value;
$i = $overflow = 0;
$len = count($val);
while ($i < $len) {
$digit = [];
if (!$overflow) {
$digit = array_slice($val, $i, $width);
$i += $width;
$overflow = $split % static::BASE;
if ($overflow) {
$mask = (1 << $overflow) - 1;
$temp = isset($val[$i]) ? $val[$i] : 0;
$digit[] = $temp & $mask;
}
} else {
$remaining = static::BASE - $overflow;
$tempsplit = $split - $remaining;
$tempwidth = (int)($tempsplit / static::BASE + 1);
$digit = array_slice($val, $i, $tempwidth);
$i += $tempwidth;
$tempoverflow = $tempsplit % static::BASE;
if ($tempoverflow) {
$tempmask = (1 << $tempoverflow) - 1;
$temp = isset($val[$i]) ? $val[$i] : 0;
$digit[] = $temp & $tempmask;
}
$newbits = 0;
for ($j = count($digit) - 1; $j >= 0; $j--) {
$temp = $digit[$j] & $mask;
$digit[$j] = ($digit[$j] >> $overflow) |
($newbits << $remaining);
$newbits = $temp;
}
$overflow = $tempoverflow;
$mask = $tempmask;
}
$temp = new static();
$temp->value = static::trim($digit);
$vals[] = $temp;
}
return array_reverse($vals);
}
/**
* Bitwise Split where $split < static::BASE
*
* @param int $split
* @return list<int>
*/
private function bitwise_small_split($split)
{
$vals = [];
$val = $this->value;
$mask = (1 << $split) - 1;
$i = $overflow = 0;
$len = count($val);
$val[] = 0;
$remaining = static::BASE;
while ($i != $len) {
$digit = $val[$i] & $mask;
$val[$i] >>= $split;
if (!$overflow) {
$remaining -= $split;
$overflow = $split <= $remaining ? 0 : $split -
$remaining;
if (!$remaining) {
$i++;
$remaining = static::BASE;
$overflow = 0;
}
} elseif (++$i != $len) {
$tempmask = (1 << $overflow) - 1;
$digit |= ($val[$i] & $tempmask) << $remaining;
$val[$i] >>= $overflow;
$remaining = static::BASE - $overflow;
$overflow = $split <= $remaining ? 0 : $split -
$remaining;
}
$vals[] = $digit;
}
while ($vals[count($vals) - 1] == 0) {
unset($vals[count($vals) - 1]);
}
return array_reverse($vals);
}
/**
* @return bool
*/
protected static function testJITOnWindows()
{
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' &&
function_exists('opcache_get_status') &&
!defined('PHPSECLIB_ALLOW_JIT')) {
$status = opcache_get_status();
if ($status && isset($status['jit'])
&& $status['jit']['enabled'] &&
$status['jit']['on']) {
return true;
}
}
return false;
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/PHP32.php000064400000021571151161424270016304
0ustar00<?php
/**
* Pure-PHP 32-bit BigInteger Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines;
/**
* Pure-PHP 32-bit Engine.
*
* Uses 64-bit floats if int size is 4 bits
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class PHP32 extends PHP
{
// Constants used by PHP.php
const BASE = 26;
const BASE_FULL = 0x4000000;
const MAX_DIGIT = 0x3FFFFFF;
const MSB = 0x2000000;
/**
* MAX10 in greatest MAX10LEN satisfying
* MAX10 = 10**MAX10LEN <= 2**BASE.
*/
const MAX10 = 10000000;
/**
* MAX10LEN in greatest MAX10LEN satisfying
* MAX10 = 10**MAX10LEN <= 2**BASE.
*/
const MAX10LEN = 7;
const MAX_DIGIT2 = 4503599627370496;
/**
* Initialize a PHP32 BigInteger Engine instance
*
* @param int $base
* @see parent::initialize()
*/
protected function initialize($base)
{
if ($base != 256 && $base != -256) {
return parent::initialize($base);
}
$val = $this->value;
$this->value = [];
$vals = &$this->value;
$i = strlen($val);
if (!$i) {
return;
}
while (true) {
$i -= 4;
if ($i < 0) {
if ($i == -4) {
break;
}
$val = substr($val, 0, 4 + $i);
$val = str_pad($val, 4, "\0", STR_PAD_LEFT);
if ($val == "\0\0\0\0") {
break;
}
$i = 0;
}
list(, $digit) = unpack('N', substr($val, $i, 4));
if ($digit < 0) {
$digit += 0xFFFFFFFF + 1;
}
$step = count($vals) & 3;
if ($step) {
$digit = (int) floor($digit / pow(2, 2 * $step));
}
if ($step != 3) {
$digit = (int) fmod($digit, static::BASE_FULL);
$i++;
}
$vals[] = $digit;
}
while (end($vals) === 0) {
array_pop($vals);
}
reset($vals);
}
/**
* Test for engine validity
*
* @see parent::__construct()
* @return bool
*/
public static function isValidEngine()
{
return PHP_INT_SIZE >= 4 && !self::testJITOnWindows();
}
/**
* Adds two BigIntegers.
*
* @param PHP32 $y
* @return PHP32
*/
public function add(PHP32 $y)
{
$temp = self::addHelper($this->value, $this->is_negative,
$y->value, $y->is_negative);
return $this->convertToObj($temp);
}
/**
* Subtracts two BigIntegers.
*
* @param PHP32 $y
* @return PHP32
*/
public function subtract(PHP32 $y)
{
$temp = self::subtractHelper($this->value,
$this->is_negative, $y->value, $y->is_negative);
return $this->convertToObj($temp);
}
/**
* Multiplies two BigIntegers.
*
* @param PHP32 $y
* @return PHP32
*/
public function multiply(PHP32 $y)
{
$temp = self::multiplyHelper($this->value,
$this->is_negative, $y->value, $y->is_negative);
return $this->convertToObj($temp);
}
/**
* Divides two BigIntegers.
*
* Returns an array whose first element contains the quotient and whose
second element contains the
* "common residue". If the remainder would be positive, the
"common residue" and the remainder are the
* same. If the remainder would be negative, the "common
residue" is equal to the sum of the remainder
* and the divisor (basically, the "common residue" is the
first positive modulo).
*
* @param PHP32 $y
* @return array{PHP32, PHP32}
*/
public function divide(PHP32 $y)
{
return $this->divideHelper($y);
}
/**
* Calculates modular inverses.
*
* Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found
using modular inverses.
* @param PHP32 $n
* @return false|PHP32
*/
public function modInverse(PHP32 $n)
{
return $this->modInverseHelper($n);
}
/**
* Calculates modular inverses.
*
* Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found
using modular inverses.
* @param PHP32 $n
* @return PHP32[]
*/
public function extendedGCD(PHP32 $n)
{
return $this->extendedGCDHelper($n);
}
/**
* Calculates the greatest common divisor
*
* Say you have 693 and 609. The GCD is 21.
*
* @param PHP32 $n
* @return PHP32
*/
public function gcd(PHP32 $n)
{
return $this->extendedGCD($n)['gcd'];
}
/**
* Logical And
*
* @param PHP32 $x
* @return PHP32
*/
public function bitwise_and(PHP32 $x)
{
return $this->bitwiseAndHelper($x);
}
/**
* Logical Or
*
* @param PHP32 $x
* @return PHP32
*/
public function bitwise_or(PHP32 $x)
{
return $this->bitwiseOrHelper($x);
}
/**
* Logical Exclusive Or
*
* @param PHP32 $x
* @return PHP32
*/
public function bitwise_xor(PHP32 $x)
{
return $this->bitwiseXorHelper($x);
}
/**
* Compares two numbers.
*
* Although one might think !$x->compare($y) means $x != $y, it, in
fact, means the opposite. The reason for this is
* demonstrated thusly:
*
* $x > $y: $x->compare($y) > 0
* $x < $y: $x->compare($y) < 0
* $x == $y: $x->compare($y) == 0
*
* Note how the same comparison operator is used. If you want to test
for equality, use $x->equals($y).
*
* {@internal Could return $this->subtract($x), but that's not
as fast as what we do do.}
*
* @param PHP32 $y
* @return int in case < 0 if $this is less than $y; > 0 if $this
is greater than $y, and 0 if they are equal.
* @see self::equals()
*/
public function compare(PHP32 $y)
{
return $this->compareHelper($this->value,
$this->is_negative, $y->value, $y->is_negative);
}
/**
* Tests the equality of two numbers.
*
* If you need to see if one number is greater than or less than
another number, use BigInteger::compare()
*
* @param PHP32 $x
* @return bool
*/
public function equals(PHP32 $x)
{
return $this->value === $x->value &&
$this->is_negative == $x->is_negative;
}
/**
* Performs modular exponentiation.
*
* @param PHP32 $e
* @param PHP32 $n
* @return PHP32
*/
public function modPow(PHP32 $e, PHP32 $n)
{
return $this->powModOuter($e, $n);
}
/**
* Performs modular exponentiation.
*
* Alias for modPow().
*
* @param PHP32 $e
* @param PHP32 $n
* @return PHP32
*/
public function powMod(PHP32 $e, PHP32 $n)
{
return $this->powModOuter($e, $n);
}
/**
* Generate a random prime number between a range
*
* If there's not a prime within the given range, false will be
returned.
*
* @param PHP32 $min
* @param PHP32 $max
* @return false|PHP32
*/
public static function randomRangePrime(PHP32 $min, PHP32 $max)
{
return self::randomRangePrimeOuter($min, $max);
}
/**
* Generate a random number between a range
*
* Returns a random number between $min and $max where $min and $max
* can be defined using one of the two methods:
*
* BigInteger::randomRange($min, $max)
* BigInteger::randomRange($max, $min)
*
* @param PHP32 $min
* @param PHP32 $max
* @return PHP32
*/
public static function randomRange(PHP32 $min, PHP32 $max)
{
return self::randomRangeHelper($min, $max);
}
/**
* Performs exponentiation.
*
* @param PHP32 $n
* @return PHP32
*/
public function pow(PHP32 $n)
{
return $this->powHelper($n);
}
/**
* Return the minimum BigInteger between an arbitrary number of
BigIntegers.
*
* @param PHP32 ...$nums
* @return PHP32
*/
public static function min(PHP32 ...$nums)
{
return self::minHelper($nums);
}
/**
* Return the maximum BigInteger between an arbitrary number of
BigIntegers.
*
* @param PHP32 ...$nums
* @return PHP32
*/
public static function max(PHP32 ...$nums)
{
return self::maxHelper($nums);
}
/**
* Tests BigInteger to see if it is between two integers, inclusive
*
* @param PHP32 $min
* @param PHP32 $max
* @return bool
*/
public function between(PHP32 $min, PHP32 $max)
{
return $this->compare($min) >= 0 &&
$this->compare($max) <= 0;
}
}
phpseclib/phpseclib/Math/BigInteger/Engines/PHP64.php000064400000021725151161424270016312
0ustar00<?php
/**
* Pure-PHP 64-bit BigInteger Engine
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math\BigInteger\Engines;
/**
* Pure-PHP 64-bit Engine.
*
* Uses 64-bit integers if int size is 8 bits
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class PHP64 extends PHP
{
// Constants used by PHP.php
const BASE = 31;
const BASE_FULL = 0x80000000;
const MAX_DIGIT = 0x7FFFFFFF;
const MSB = 0x40000000;
/**
* MAX10 in greatest MAX10LEN satisfying
* MAX10 = 10**MAX10LEN <= 2**BASE.
*/
const MAX10 = 1000000000;
/**
* MAX10LEN in greatest MAX10LEN satisfying
* MAX10 = 10**MAX10LEN <= 2**BASE.
*/
const MAX10LEN = 9;
const MAX_DIGIT2 = 4611686018427387904;
/**
* Initialize a PHP64 BigInteger Engine instance
*
* @param int $base
* @see parent::initialize()
*/
protected function initialize($base)
{
if ($base != 256 && $base != -256) {
return parent::initialize($base);
}
$val = $this->value;
$this->value = [];
$vals = &$this->value;
$i = strlen($val);
if (!$i) {
return;
}
while (true) {
$i -= 4;
if ($i < 0) {
if ($i == -4) {
break;
}
$val = substr($val, 0, 4 + $i);
$val = str_pad($val, 4, "\0", STR_PAD_LEFT);
if ($val == "\0\0\0\0") {
break;
}
$i = 0;
}
list(, $digit) = unpack('N', substr($val, $i, 4));
$step = count($vals) & 7;
if (!$step) {
$digit &= static::MAX_DIGIT;
$i++;
} else {
$shift = 8 - $step;
$digit >>= $shift;
$shift = 32 - $shift;
$digit &= (1 << $shift) - 1;
$temp = $i > 0 ? ord($val[$i - 1]) : 0;
$digit |= ($temp << $shift) & 0x7F000000;
}
$vals[] = $digit;
}
while (end($vals) === 0) {
array_pop($vals);
}
reset($vals);
}
/**
* Test for engine validity
*
* @see parent::__construct()
* @return bool
*/
public static function isValidEngine()
{
return PHP_INT_SIZE >= 8 && !self::testJITOnWindows();
}
/**
* Adds two BigIntegers.
*
* @param PHP64 $y
* @return PHP64
*/
public function add(PHP64 $y)
{
$temp = self::addHelper($this->value, $this->is_negative,
$y->value, $y->is_negative);
return $this->convertToObj($temp);
}
/**
* Subtracts two BigIntegers.
*
* @param PHP64 $y
* @return PHP64
*/
public function subtract(PHP64 $y)
{
$temp = self::subtractHelper($this->value,
$this->is_negative, $y->value, $y->is_negative);
return $this->convertToObj($temp);
}
/**
* Multiplies two BigIntegers.
*
* @param PHP64 $y
* @return PHP64
*/
public function multiply(PHP64 $y)
{
$temp = self::multiplyHelper($this->value,
$this->is_negative, $y->value, $y->is_negative);
return $this->convertToObj($temp);
}
/**
* Divides two BigIntegers.
*
* Returns an array whose first element contains the quotient and whose
second element contains the
* "common residue". If the remainder would be positive, the
"common residue" and the remainder are the
* same. If the remainder would be negative, the "common
residue" is equal to the sum of the remainder
* and the divisor (basically, the "common residue" is the
first positive modulo).
*
* @param PHP64 $y
* @return array{PHP64, PHP64}
*/
public function divide(PHP64 $y)
{
return $this->divideHelper($y);
}
/**
* Calculates modular inverses.
*
* Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found
using modular inverses.
* @param PHP64 $n
* @return false|PHP64
*/
public function modInverse(PHP64 $n)
{
return $this->modInverseHelper($n);
}
/**
* Calculates modular inverses.
*
* Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found
using modular inverses.
* @param PHP64 $n
* @return PHP64[]
*/
public function extendedGCD(PHP64 $n)
{
return $this->extendedGCDHelper($n);
}
/**
* Calculates the greatest common divisor
*
* Say you have 693 and 609. The GCD is 21.
*
* @param PHP64 $n
* @return PHP64
*/
public function gcd(PHP64 $n)
{
return $this->extendedGCD($n)['gcd'];
}
/**
* Logical And
*
* @param PHP64 $x
* @return PHP64
*/
public function bitwise_and(PHP64 $x)
{
return $this->bitwiseAndHelper($x);
}
/**
* Logical Or
*
* @param PHP64 $x
* @return PHP64
*/
public function bitwise_or(PHP64 $x)
{
return $this->bitwiseOrHelper($x);
}
/**
* Logical Exclusive Or
*
* @param PHP64 $x
* @return PHP64
*/
public function bitwise_xor(PHP64 $x)
{
return $this->bitwiseXorHelper($x);
}
/**
* Compares two numbers.
*
* Although one might think !$x->compare($y) means $x != $y, it, in
fact, means the opposite. The reason for this is
* demonstrated thusly:
*
* $x > $y: $x->compare($y) > 0
* $x < $y: $x->compare($y) < 0
* $x == $y: $x->compare($y) == 0
*
* Note how the same comparison operator is used. If you want to test
for equality, use $x->equals($y).
*
* {@internal Could return $this->subtract($x), but that's not
as fast as what we do do.}
*
* @param PHP64 $y
* @return int in case < 0 if $this is less than $y; > 0 if $this
is greater than $y, and 0 if they are equal.
* @see self::equals()
*/
public function compare(PHP64 $y)
{
return parent::compareHelper($this->value,
$this->is_negative, $y->value, $y->is_negative);
}
/**
* Tests the equality of two numbers.
*
* If you need to see if one number is greater than or less than
another number, use BigInteger::compare()
*
* @param PHP64 $x
* @return bool
*/
public function equals(PHP64 $x)
{
return $this->value === $x->value &&
$this->is_negative == $x->is_negative;
}
/**
* Performs modular exponentiation.
*
* @param PHP64 $e
* @param PHP64 $n
* @return PHP64
*/
public function modPow(PHP64 $e, PHP64 $n)
{
return $this->powModOuter($e, $n);
}
/**
* Performs modular exponentiation.
*
* Alias for modPow().
*
* @param PHP64 $e
* @param PHP64 $n
* @return PHP64|false
*/
public function powMod(PHP64 $e, PHP64 $n)
{
return $this->powModOuter($e, $n);
}
/**
* Generate a random prime number between a range
*
* If there's not a prime within the given range, false will be
returned.
*
* @param PHP64 $min
* @param PHP64 $max
* @return false|PHP64
*/
public static function randomRangePrime(PHP64 $min, PHP64 $max)
{
return self::randomRangePrimeOuter($min, $max);
}
/**
* Generate a random number between a range
*
* Returns a random number between $min and $max where $min and $max
* can be defined using one of the two methods:
*
* BigInteger::randomRange($min, $max)
* BigInteger::randomRange($max, $min)
*
* @param PHP64 $min
* @param PHP64 $max
* @return PHP64
*/
public static function randomRange(PHP64 $min, PHP64 $max)
{
return self::randomRangeHelper($min, $max);
}
/**
* Performs exponentiation.
*
* @param PHP64 $n
* @return PHP64
*/
public function pow(PHP64 $n)
{
return $this->powHelper($n);
}
/**
* Return the minimum BigInteger between an arbitrary number of
BigIntegers.
*
* @param PHP64 ...$nums
* @return PHP64
*/
public static function min(PHP64 ...$nums)
{
return self::minHelper($nums);
}
/**
* Return the maximum BigInteger between an arbitrary number of
BigIntegers.
*
* @param PHP64 ...$nums
* @return PHP64
*/
public static function max(PHP64 ...$nums)
{
return self::maxHelper($nums);
}
/**
* Tests BigInteger to see if it is between two integers, inclusive
*
* @param PHP64 $min
* @param PHP64 $max
* @return bool
*/
public function between(PHP64 $min, PHP64 $max)
{
return $this->compare($min) >= 0 &&
$this->compare($max) <= 0;
}
}
phpseclib/phpseclib/Math/BinaryField/Integer.php000064400000032716151161424270015671
0ustar00<?php
/**
* Binary Finite Fields
*
* In a binary finite field numbers are actually polynomial equations. If
you
* represent the number as a sequence of bits you get a sequence of
1's or 0's.
* These 1's or 0's represent the coefficients of the x**n, where
n is the
* location of the given bit. When you add numbers over a binary finite
field
* the result should have a coefficient of 1 or 0 as well. Hence addition
* and subtraction become the same operation as XOR.
* eg. 1 + 1 + 1 == 3 % 2 == 1 or 0 - 1 == -1 % 2 == 1
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
*/
namespace phpseclib3\Math\BinaryField;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Math\BigInteger;
use phpseclib3\Math\BinaryField;
use phpseclib3\Math\Common\FiniteField\Integer as Base;
/**
* Binary Finite Fields
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class Integer extends Base
{
/**
* Holds the BinaryField's value
*
* @var string
*/
protected $value;
/**
* Keeps track of current instance
*
* @var int
*/
protected $instanceID;
/**
* Holds the PrimeField's modulo
*
* @var array<int, string>
*/
protected static $modulo;
/**
* Holds a pre-generated function to perform modulo reductions
*
* @var callable[]
*/
protected static $reduce;
/**
* Default constructor
*/
public function __construct($instanceID, $num = '')
{
$this->instanceID = $instanceID;
if (!strlen($num)) {
$this->value = '';
} else {
$reduce = static::$reduce[$instanceID];
$this->value = $reduce($num);
}
}
/**
* Set the modulo for a given instance
* @param int $instanceID
* @param string $modulo
*/
public static function setModulo($instanceID, $modulo)
{
static::$modulo[$instanceID] = $modulo;
}
/**
* Set the modulo for a given instance
*/
public static function setRecurringModuloFunction($instanceID, callable
$function)
{
static::$reduce[$instanceID] = $function;
}
/**
* Tests a parameter to see if it's of the right instance
*
* Throws an exception if the incorrect class is being utilized
*/
private static function checkInstance(self $x, self $y)
{
if ($x->instanceID != $y->instanceID) {
throw new \UnexpectedValueException('The instances of the
two BinaryField\Integer objects do not match');
}
}
/**
* Tests the equality of two numbers.
*
* @return bool
*/
public function equals(self $x)
{
static::checkInstance($this, $x);
return $this->value == $x->value;
}
/**
* Compares two numbers.
*
* @return int
*/
public function compare(self $x)
{
static::checkInstance($this, $x);
$a = $this->value;
$b = $x->value;
$length = max(strlen($a), strlen($b));
$a = str_pad($a, $length, "\0", STR_PAD_LEFT);
$b = str_pad($b, $length, "\0", STR_PAD_LEFT);
return strcmp($a, $b);
}
/**
* Returns the degree of the polynomial
*
* @param string $x
* @return int
*/
private static function deg($x)
{
$x = ltrim($x, "\0");
$xbit = decbin(ord($x[0]));
$xlen = $xbit == '0' ? 0 : strlen($xbit);
$len = strlen($x);
if (!$len) {
return -1;
}
return 8 * strlen($x) - 9 + $xlen;
}
/**
* Perform polynomial division
*
* @return string[]
* @link
https://en.wikipedia.org/wiki/Polynomial_greatest_common_divisor#Euclidean_division
*/
private static function polynomialDivide($x, $y)
{
// in wikipedia's description of the algorithm, lc() is the
leading coefficient. over a binary field that's
// always going to be 1.
$q = chr(0);
$d = static::deg($y);
$r = $x;
while (($degr = static::deg($r)) >= $d) {
$s = '1' . str_repeat('0', $degr - $d);
$s = BinaryField::base2ToBase256($s);
$length = max(strlen($s), strlen($q));
$q = !isset($q) ? $s :
str_pad($q, $length, "\0", STR_PAD_LEFT) ^
str_pad($s, $length, "\0", STR_PAD_LEFT);
$s = static::polynomialMultiply($s, $y);
$length = max(strlen($r), strlen($s));
$r = str_pad($r, $length, "\0", STR_PAD_LEFT) ^
str_pad($s, $length, "\0", STR_PAD_LEFT);
}
return [ltrim($q, "\0"), ltrim($r, "\0")];
}
/**
* Perform polynomial multiplation in the traditional way
*
* @return string
* @link
https://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplication
*/
private static function regularPolynomialMultiply($x, $y)
{
$precomputed = [ltrim($x, "\0")];
$x = strrev(BinaryField::base256ToBase2($x));
$y = strrev(BinaryField::base256ToBase2($y));
if (strlen($x) == strlen($y)) {
$length = strlen($x);
} else {
$length = max(strlen($x), strlen($y));
$x = str_pad($x, $length, '0');
$y = str_pad($y, $length, '0');
}
$result = str_repeat('0', 2 * $length - 1);
$result = BinaryField::base2ToBase256($result);
$size = strlen($result);
$x = strrev($x);
// precompute left shift 1 through 7
for ($i = 1; $i < 8; $i++) {
$precomputed[$i] = BinaryField::base2ToBase256($x .
str_repeat('0', $i));
}
for ($i = 0; $i < strlen($y); $i++) {
if ($y[$i] == '1') {
$temp = $precomputed[$i & 7] .
str_repeat("\0", $i >> 3);
$result ^= str_pad($temp, $size, "\0",
STR_PAD_LEFT);
}
}
return $result;
}
/**
* Perform polynomial multiplation
*
* Uses karatsuba multiplication to reduce x-bit multiplications to a
series of 32-bit multiplications
*
* @return string
* @link https://en.wikipedia.org/wiki/Karatsuba_algorithm
*/
private static function polynomialMultiply($x, $y)
{
if (strlen($x) == strlen($y)) {
$length = strlen($x);
} else {
$length = max(strlen($x), strlen($y));
$x = str_pad($x, $length, "\0", STR_PAD_LEFT);
$y = str_pad($y, $length, "\0", STR_PAD_LEFT);
}
switch (true) {
case PHP_INT_SIZE == 8 && $length <= 4:
return $length != 4 ?
self::subMultiply(str_pad($x, 4, "\0",
STR_PAD_LEFT), str_pad($y, 4, "\0", STR_PAD_LEFT)) :
self::subMultiply($x, $y);
case PHP_INT_SIZE == 4 || $length > 32:
return self::regularPolynomialMultiply($x, $y);
}
$m = $length >> 1;
$x1 = substr($x, 0, -$m);
$x0 = substr($x, -$m);
$y1 = substr($y, 0, -$m);
$y0 = substr($y, -$m);
$z2 = self::polynomialMultiply($x1, $y1);
$z0 = self::polynomialMultiply($x0, $y0);
$z1 = self::polynomialMultiply(
self::subAdd2($x1, $x0),
self::subAdd2($y1, $y0)
);
$z1 = self::subAdd3($z1, $z2, $z0);
$xy = self::subAdd3(
$z2 . str_repeat("\0", 2 * $m),
$z1 . str_repeat("\0", $m),
$z0
);
return ltrim($xy, "\0");
}
/**
* Perform polynomial multiplication on 2x 32-bit numbers, returning
* a 64-bit number
*
* @param string $x
* @param string $y
* @return string
* @link https://www.bearssl.org/constanttime.html#ghash-for-gcm
*/
private static function subMultiply($x, $y)
{
$x = unpack('N', $x)[1];
$y = unpack('N', $y)[1];
$x0 = $x & 0x11111111;
$x1 = $x & 0x22222222;
$x2 = $x & 0x44444444;
$x3 = $x & 0x88888888;
$y0 = $y & 0x11111111;
$y1 = $y & 0x22222222;
$y2 = $y & 0x44444444;
$y3 = $y & 0x88888888;
$z0 = ($x0 * $y0) ^ ($x1 * $y3) ^ ($x2 * $y2) ^ ($x3 * $y1);
$z1 = ($x0 * $y1) ^ ($x1 * $y0) ^ ($x2 * $y3) ^ ($x3 * $y2);
$z2 = ($x0 * $y2) ^ ($x1 * $y1) ^ ($x2 * $y0) ^ ($x3 * $y3);
$z3 = ($x0 * $y3) ^ ($x1 * $y2) ^ ($x2 * $y1) ^ ($x3 * $y0);
$z0 &= 0x1111111111111111;
$z1 &= 0x2222222222222222;
$z2 &= 0x4444444444444444;
$z3 &= -8608480567731124088; // 0x8888888888888888 gets
interpreted as a float
$z = $z0 | $z1 | $z2 | $z3;
return pack('J', $z);
}
/**
* Adds two numbers
*
* @param string $x
* @param string $y
* @return string
*/
private static function subAdd2($x, $y)
{
$length = max(strlen($x), strlen($y));
$x = str_pad($x, $length, "\0", STR_PAD_LEFT);
$y = str_pad($y, $length, "\0", STR_PAD_LEFT);
return $x ^ $y;
}
/**
* Adds three numbers
*
* @param string $x
* @param string $y
* @return string
*/
private static function subAdd3($x, $y, $z)
{
$length = max(strlen($x), strlen($y), strlen($z));
$x = str_pad($x, $length, "\0", STR_PAD_LEFT);
$y = str_pad($y, $length, "\0", STR_PAD_LEFT);
$z = str_pad($z, $length, "\0", STR_PAD_LEFT);
return $x ^ $y ^ $z;
}
/**
* Adds two BinaryFieldIntegers.
*
* @return static
*/
public function add(self $y)
{
static::checkInstance($this, $y);
$length = strlen(static::$modulo[$this->instanceID]);
$x = str_pad($this->value, $length, "\0",
STR_PAD_LEFT);
$y = str_pad($y->value, $length, "\0", STR_PAD_LEFT);
return new static($this->instanceID, $x ^ $y);
}
/**
* Subtracts two BinaryFieldIntegers.
*
* @return static
*/
public function subtract(self $x)
{
return $this->add($x);
}
/**
* Multiplies two BinaryFieldIntegers.
*
* @return static
*/
public function multiply(self $y)
{
static::checkInstance($this, $y);
return new static($this->instanceID,
static::polynomialMultiply($this->value, $y->value));
}
/**
* Returns the modular inverse of a BinaryFieldInteger
*
* @return static
*/
public function modInverse()
{
$remainder0 = static::$modulo[$this->instanceID];
$remainder1 = $this->value;
if ($remainder1 == '') {
return new static($this->instanceID);
}
$aux0 = "\0";
$aux1 = "\1";
while ($remainder1 != "\1") {
list($q, $r) = static::polynomialDivide($remainder0,
$remainder1);
$remainder0 = $remainder1;
$remainder1 = $r;
// the auxiliary in row n is given by the sum of the auxiliary
in
// row n-2 and the product of the quotient and the auxiliary in
row
// n-1
$temp = static::polynomialMultiply($aux1, $q);
$aux = str_pad($aux0, strlen($temp), "\0",
STR_PAD_LEFT) ^
str_pad($temp, strlen($aux0), "\0",
STR_PAD_LEFT);
$aux0 = $aux1;
$aux1 = $aux;
}
$temp = new static($this->instanceID);
$temp->value = ltrim($aux1, "\0");
return $temp;
}
/**
* Divides two PrimeFieldIntegers.
*
* @return static
*/
public function divide(self $x)
{
static::checkInstance($this, $x);
$x = $x->modInverse();
return $this->multiply($x);
}
/**
* Negate
*
* A negative number can be written as 0-12. With modulos, 0 is the
same thing as the modulo
* so 0-12 is the same thing as modulo-12
*
* @return object
*/
public function negate()
{
$x = str_pad($this->value,
strlen(static::$modulo[$this->instanceID]), "\0",
STR_PAD_LEFT);
return new static($this->instanceID, $x ^
static::$modulo[$this->instanceID]);
}
/**
* Returns the modulo
*
* @return string
*/
public static function getModulo($instanceID)
{
return static::$modulo[$instanceID];
}
/**
* Converts an Integer to a byte string (eg. base-256).
*
* @return string
*/
public function toBytes()
{
return str_pad($this->value,
strlen(static::$modulo[$this->instanceID]), "\0",
STR_PAD_LEFT);
}
/**
* Converts an Integer to a hex string (eg. base-16).
*
* @return string
*/
public function toHex()
{
return Strings::bin2hex($this->toBytes());
}
/**
* Converts an Integer to a bit string (eg. base-2).
*
* @return string
*/
public function toBits()
{
//return str_pad(BinaryField::base256ToBase2($this->value),
strlen(static::$modulo[$this->instanceID]), '0',
STR_PAD_LEFT);
return BinaryField::base256ToBase2($this->value);
}
/**
* Converts an Integer to a BigInteger
*
* @return string
*/
public function toBigInteger()
{
return new BigInteger($this->value, 256);
}
/**
* __toString() magic method
*
*/
public function __toString()
{
return (string) $this->toBigInteger();
}
/**
* __debugInfo() magic method
*
*/
public function __debugInfo()
{
return ['value' => $this->toHex()];
}
}
phpseclib/phpseclib/Math/BinaryField.php000064400000011752151161424270014271
0ustar00<?php
/**
* Binary Finite Fields
*
* Utilizes the factory design pattern
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
*/
namespace phpseclib3\Math;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Math\BinaryField\Integer;
use phpseclib3\Math\Common\FiniteField;
/**
* Binary Finite Fields
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class BinaryField extends FiniteField
{
/**
* Instance Counter
*
* @var int
*/
private static $instanceCounter = 0;
/**
* Keeps track of current instance
*
* @var int
*/
protected $instanceID;
/** @var BigInteger */
private $randomMax;
/**
* Default constructor
*/
public function __construct(...$indices)
{
$m = array_shift($indices);
$val = str_repeat('0', $m) . '1';
foreach ($indices as $index) {
$val[$index] = '1';
}
$modulo = static::base2ToBase256(strrev($val));
$mStart = 2 * $m - 2;
$t = ceil($m / 8);
$finalMask = chr((1 << ($m % 8)) - 1);
if ($finalMask == "\0") {
$finalMask = "\xFF";
}
$bitLen = $mStart + 1;
$pad = ceil($bitLen / 8);
$h = $bitLen & 7;
$h = $h ? 8 - $h : 0;
$r = rtrim(substr($val, 0, -1), '0');
$u = [static::base2ToBase256(strrev($r))];
for ($i = 1; $i < 8; $i++) {
$u[] = static::base2ToBase256(strrev(str_repeat('0',
$i) . $r));
}
// implements algorithm 2.40 (in section 2.3.5) in "Guide to
Elliptic Curve Cryptography"
// with W = 8
$reduce = function ($c) use ($u, $mStart, $m, $t, $finalMask, $pad,
$h) {
$c = str_pad($c, $pad, "\0", STR_PAD_LEFT);
for ($i = $mStart; $i >= $m;) {
$g = $h >> 3;
$mask = $h & 7;
$mask = $mask ? 1 << (7 - $mask) : 0x80;
for (; $mask > 0; $mask >>= 1, $i--, $h++) {
if (ord($c[$g]) & $mask) {
$temp = $i - $m;
$j = $temp >> 3;
$k = $temp & 7;
$t1 = $j ? substr($c, 0, -$j) : $c;
$length = strlen($t1);
if ($length) {
$t2 = str_pad($u[$k], $length, "\0",
STR_PAD_LEFT);
$temp = $t1 ^ $t2;
$c = $j ? substr_replace($c, $temp, 0, $length)
: $temp;
}
}
}
}
$c = substr($c, -$t);
if (strlen($c) == $t) {
$c[0] = $c[0] & $finalMask;
}
return ltrim($c, "\0");
};
$this->instanceID = self::$instanceCounter++;
Integer::setModulo($this->instanceID, $modulo);
Integer::setRecurringModuloFunction($this->instanceID, $reduce);
$this->randomMax = new BigInteger($modulo, 2);
}
/**
* Returns an instance of a dynamically generated PrimeFieldInteger
class
*
* @param string $num
* @return Integer
*/
public function newInteger($num)
{
return new Integer($this->instanceID, $num instanceof BigInteger
? $num->toBytes() : $num);
}
/**
* Returns an integer on the finite field between one and the prime
modulo
*
* @return Integer
*/
public function randomInteger()
{
static $one;
if (!isset($one)) {
$one = new BigInteger(1);
}
return new Integer($this->instanceID,
BigInteger::randomRange($one, $this->randomMax)->toBytes());
}
/**
* Returns the length of the modulo in bytes
*
* @return int
*/
public function getLengthInBytes()
{
return strlen(Integer::getModulo($this->instanceID));
}
/**
* Returns the length of the modulo in bits
*
* @return int
*/
public function getLength()
{
return strlen(Integer::getModulo($this->instanceID)) << 3;
}
/**
* Converts a base-2 string to a base-256 string
*
* @param string $x
* @param int|null $size
* @return string
*/
public static function base2ToBase256($x, $size = null)
{
$str = Strings::bits2bin($x);
$pad = strlen($x) >> 3;
if (strlen($x) & 3) {
$pad++;
}
$str = str_pad($str, $pad, "\0", STR_PAD_LEFT);
if (isset($size)) {
$str = str_pad($str, $size, "\0", STR_PAD_LEFT);
}
return $str;
}
/**
* Converts a base-256 string to a base-2 string
*
* @param string $x
* @return string
*/
public static function base256ToBase2($x)
{
if (function_exists('gmp_import')) {
return gmp_strval(gmp_import($x), 2);
}
return Strings::bin2bits($x);
}
}
phpseclib/phpseclib/Math/Common/FiniteField/Integer.php000064400000002000151161424270017072
0ustar00<?php
/**
* Finite Field Integer Base Class
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
*/
namespace phpseclib3\Math\Common\FiniteField;
/**
* Finite Field Integer
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Integer implements \JsonSerializable
{
/**
* JSON Serialize
*
* Will be called, automatically, when json_encode() is called on a
BigInteger object.
*
* PHP Serialize isn't supported because unserializing would
require the factory be
* serialized as well and that just sounds like too much
*
* @return array{hex: string}
*/
#[\ReturnTypeWillChange]
public function jsonSerialize()
{
return ['hex' => $this->toHex(true)];
}
/**
* Converts an Integer to a hex string (eg. base-16).
*
* @return string
*/
abstract public function toHex();
}
phpseclib/phpseclib/Math/Common/FiniteField.php000064400000000567151161424270015515
0ustar00<?php
/**
* Finite Fields Base Class
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
*/
namespace phpseclib3\Math\Common;
/**
* Finite Fields
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class FiniteField
{
}
phpseclib/phpseclib/Math/PrimeField/Integer.php000064400000024226151161424270015516
0ustar00<?php
/**
* Prime Finite Fields
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
*/
namespace phpseclib3\Math\PrimeField;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Math\BigInteger;
use phpseclib3\Math\Common\FiniteField\Integer as Base;
/**
* Prime Finite Fields
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class Integer extends Base
{
/**
* Holds the PrimeField's value
*
* @var BigInteger
*/
protected $value;
/**
* Keeps track of current instance
*
* @var int
*/
protected $instanceID;
/**
* Holds the PrimeField's modulo
*
* @var array<int, BigInteger>
*/
protected static $modulo;
/**
* Holds a pre-generated function to perform modulo reductions
*
* @var array<int, callable(BigInteger):BigInteger>
*/
protected static $reduce;
/**
* Zero
*
* @var BigInteger
*/
protected static $zero;
/**
* Default constructor
*
* @param int $instanceID
*/
public function __construct($instanceID, BigInteger $num = null)
{
$this->instanceID = $instanceID;
if (!isset($num)) {
$this->value = clone static::$zero[static::class];
} else {
$reduce = static::$reduce[$instanceID];
$this->value = $reduce($num);
}
}
/**
* Set the modulo for a given instance
*
* @param int $instanceID
* @return void
*/
public static function setModulo($instanceID, BigInteger $modulo)
{
static::$modulo[$instanceID] = $modulo;
}
/**
* Set the modulo for a given instance
*
* @param int $instanceID
* @return void
*/
public static function setRecurringModuloFunction($instanceID, callable
$function)
{
static::$reduce[$instanceID] = $function;
if (!isset(static::$zero[static::class])) {
static::$zero[static::class] = new BigInteger();
}
}
/**
* Delete the modulo for a given instance
*/
public static function cleanupCache($instanceID)
{
unset(static::$modulo[$instanceID]);
unset(static::$reduce[$instanceID]);
}
/**
* Returns the modulo
*
* @param int $instanceID
* @return BigInteger
*/
public static function getModulo($instanceID)
{
return static::$modulo[$instanceID];
}
/**
* Tests a parameter to see if it's of the right instance
*
* Throws an exception if the incorrect class is being utilized
*
* @return void
*/
public static function checkInstance(self $x, self $y)
{
if ($x->instanceID != $y->instanceID) {
throw new \UnexpectedValueException('The instances of the
two PrimeField\Integer objects do not match');
}
}
/**
* Tests the equality of two numbers.
*
* @return bool
*/
public function equals(self $x)
{
static::checkInstance($this, $x);
return $this->value->equals($x->value);
}
/**
* Compares two numbers.
*
* @return int
*/
public function compare(self $x)
{
static::checkInstance($this, $x);
return $this->value->compare($x->value);
}
/**
* Adds two PrimeFieldIntegers.
*
* @return static
*/
public function add(self $x)
{
static::checkInstance($this, $x);
$temp = new static($this->instanceID);
$temp->value = $this->value->add($x->value);
if
($temp->value->compare(static::$modulo[$this->instanceID]) >=
0) {
$temp->value =
$temp->value->subtract(static::$modulo[$this->instanceID]);
}
return $temp;
}
/**
* Subtracts two PrimeFieldIntegers.
*
* @return static
*/
public function subtract(self $x)
{
static::checkInstance($this, $x);
$temp = new static($this->instanceID);
$temp->value = $this->value->subtract($x->value);
if ($temp->value->isNegative()) {
$temp->value =
$temp->value->add(static::$modulo[$this->instanceID]);
}
return $temp;
}
/**
* Multiplies two PrimeFieldIntegers.
*
* @return static
*/
public function multiply(self $x)
{
static::checkInstance($this, $x);
return new static($this->instanceID,
$this->value->multiply($x->value));
}
/**
* Divides two PrimeFieldIntegers.
*
* @return static
*/
public function divide(self $x)
{
static::checkInstance($this, $x);
$denominator =
$x->value->modInverse(static::$modulo[$this->instanceID]);
return new static($this->instanceID,
$this->value->multiply($denominator));
}
/**
* Performs power operation on a PrimeFieldInteger.
*
* @return static
*/
public function pow(BigInteger $x)
{
$temp = new static($this->instanceID);
$temp->value = $this->value->powMod($x,
static::$modulo[$this->instanceID]);
return $temp;
}
/**
* Calculates the square root
*
* @link https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm
* @return static|false
*/
public function squareRoot()
{
static $one, $two;
if (!isset($one)) {
$one = new BigInteger(1);
$two = new BigInteger(2);
}
$reduce = static::$reduce[$this->instanceID];
$p_1 = static::$modulo[$this->instanceID]->subtract($one);
$q = clone $p_1;
$s = BigInteger::scan1divide($q);
list($pow) = $p_1->divide($two);
for ($z = $one;
!$z->equals(static::$modulo[$this->instanceID]); $z =
$z->add($one)) {
$temp = $z->powMod($pow,
static::$modulo[$this->instanceID]);
if ($temp->equals($p_1)) {
break;
}
}
$m = new BigInteger($s);
$c = $z->powMod($q, static::$modulo[$this->instanceID]);
$t = $this->value->powMod($q,
static::$modulo[$this->instanceID]);
list($temp) = $q->add($one)->divide($two);
$r = $this->value->powMod($temp,
static::$modulo[$this->instanceID]);
while (!$t->equals($one)) {
for ($i == clone $one; $i->compare($m) < 0; $i =
$i->add($one)) {
if ($t->powMod($two->pow($i),
static::$modulo[$this->instanceID])->equals($one)) {
break;
}
}
if ($i->compare($m) == 0) {
return false;
}
$b =
$c->powMod($two->pow($m->subtract($i)->subtract($one)),
static::$modulo[$this->instanceID]);
$m = $i;
$c = $reduce($b->multiply($b));
$t = $reduce($t->multiply($c));
$r = $reduce($r->multiply($b));
}
return new static($this->instanceID, $r);
}
/**
* Is Odd?
*
* @return bool
*/
public function isOdd()
{
return $this->value->isOdd();
}
/**
* Negate
*
* A negative number can be written as 0-12. With modulos, 0 is the
same thing as the modulo
* so 0-12 is the same thing as modulo-12
*
* @return static
*/
public function negate()
{
return new static($this->instanceID,
static::$modulo[$this->instanceID]->subtract($this->value));
}
/**
* Converts an Integer to a byte string (eg. base-256).
*
* @return string
*/
public function toBytes()
{
if (isset(static::$modulo[$this->instanceID])) {
$length =
static::$modulo[$this->instanceID]->getLengthInBytes();
return str_pad($this->value->toBytes(), $length,
"\0", STR_PAD_LEFT);
}
return $this->value->toBytes();
}
/**
* Converts an Integer to a hex string (eg. base-16).
*
* @return string
*/
public function toHex()
{
return Strings::bin2hex($this->toBytes());
}
/**
* Converts an Integer to a bit string (eg. base-2).
*
* @return string
*/
public function toBits()
{
// return $this->value->toBits();
static $length;
if (!isset($length)) {
$length =
static::$modulo[$this->instanceID]->getLength();
}
return str_pad($this->value->toBits(), $length,
'0', STR_PAD_LEFT);
}
/**
* Returns the w-ary non-adjacent form (wNAF)
*
* @param int $w optional
* @return array<int, int>
*/
public function getNAF($w = 1)
{
$w++;
$mask = new BigInteger((1 << $w) - 1);
$sub = new BigInteger(1 << $w);
//$sub = new BigInteger(1 << ($w - 1));
$d = $this->toBigInteger();
$d_i = [];
$i = 0;
while ($d->compare(static::$zero[static::class]) > 0) {
if ($d->isOdd()) {
// start mods
$bigInteger = $d->testBit($w - 1) ?
$d->bitwise_and($mask)->subtract($sub) :
//$sub->subtract($d->bitwise_and($mask)) :
$d->bitwise_and($mask);
// end mods
$d = $d->subtract($bigInteger);
$d_i[$i] = (int) $bigInteger->toString();
} else {
$d_i[$i] = 0;
}
$shift = !$d->equals(static::$zero[static::class])
&&
$d->bitwise_and($mask)->equals(static::$zero[static::class]) ? $w :
1; // $w or $w + 1?
$d = $d->bitwise_rightShift($shift);
while (--$shift > 0) {
$d_i[++$i] = 0;
}
$i++;
}
return $d_i;
}
/**
* Converts an Integer to a BigInteger
*
* @return BigInteger
*/
public function toBigInteger()
{
return clone $this->value;
}
/**
* __toString() magic method
*
* @return string
*/
public function __toString()
{
return (string) $this->value;
}
/**
* __debugInfo() magic method
*
* @return array
*/
public function __debugInfo()
{
return ['value' => $this->toHex()];
}
}
phpseclib/phpseclib/Math/PrimeField.php000064400000005126151161424270014117
0ustar00<?php
/**
* Prime Finite Fields
*
* Utilizes the factory design pattern
*
* PHP version 5 and 7
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib3\Math;
use phpseclib3\Math\Common\FiniteField;
use phpseclib3\Math\PrimeField\Integer;
/**
* Prime Finite Fields
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class PrimeField extends FiniteField
{
/**
* Instance Counter
*
* @var int
*/
private static $instanceCounter = 0;
/**
* Keeps track of current instance
*
* @var int
*/
protected $instanceID;
/**
* Default constructor
*/
public function __construct(BigInteger $modulo)
{
if (!$modulo->isPrime()) {
throw new \UnexpectedValueException('PrimeField requires a
prime number be passed to the constructor');
}
$this->instanceID = self::$instanceCounter++;
Integer::setModulo($this->instanceID, $modulo);
Integer::setRecurringModuloFunction($this->instanceID,
$modulo->createRecurringModuloFunction());
}
/**
* Use a custom defined modular reduction function
*
* @return void
*/
public function setReduction(\Closure $func)
{
$this->reduce = $func->bindTo($this, $this);
}
/**
* Returns an instance of a dynamically generated PrimeFieldInteger
class
*
* @return Integer
*/
public function newInteger(BigInteger $num)
{
return new Integer($this->instanceID, $num);
}
/**
* Returns an integer on the finite field between one and the prime
modulo
*
* @return Integer
*/
public function randomInteger()
{
static $one;
if (!isset($one)) {
$one = new BigInteger(1);
}
return new Integer($this->instanceID,
BigInteger::randomRange($one, Integer::getModulo($this->instanceID)));
}
/**
* Returns the length of the modulo in bytes
*
* @return int
*/
public function getLengthInBytes()
{
return
Integer::getModulo($this->instanceID)->getLengthInBytes();
}
/**
* Returns the length of the modulo in bits
*
* @return int
*/
public function getLength()
{
return Integer::getModulo($this->instanceID)->getLength();
}
/**
* Destructor
*/
public function __destruct()
{
Integer::cleanupCache($this->instanceID);
}
}
phpseclib/phpseclib/System/SSH/Common/Traits/ReadBytes.php000064400000001425151161424270017505
0ustar00<?php
/**
* ReadBytes trait
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT
License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\System\SSH\Common\Traits;
/**
* ReadBytes trait
*
* @author Jim Wigginton <terrafrost@php.net>
*/
trait ReadBytes
{
/**
* Read data
*
* @param int $length
* @throws \RuntimeException on connection errors
*/
public function readBytes($length)
{
$temp = fread($this->fsock, $length);
if (strlen($temp) != $length) {
throw new \RuntimeException("Expected $length bytes; got
" . strlen($temp));
}
return $temp;
}
}