diff --git a/phpseclib/Common/Functions/Strings.php b/phpseclib/Common/Functions/Strings.php index 76ff62ff..4edf3b97 100644 --- a/phpseclib/Common/Functions/Strings.php +++ b/phpseclib/Common/Functions/Strings.php @@ -16,6 +16,7 @@ namespace phpseclib\Common\Functions; use phpseclib\Math\BigInteger; +use phpseclib\Math\Common\FiniteField; /** * Common String Functions @@ -106,9 +107,8 @@ abstract class Strings * @param string $format * @param $data * @return mixed - * @access public */ - public static function unpackSSH2($format, $data) + public static function unpackSSH2($format, &$data) { $result = []; for ($i = 0; $i < strlen($format); $i++) { @@ -205,8 +205,8 @@ abstract class Strings $result.= pack('Na*', strlen($element), $element); break; case 'i': - if (!$element instanceof BigInteger) { - throw new \InvalidArgumentException('A phpseclib\Math\BigInteger object was expected.'); + if (!$element instanceof BigInteger && !$element instanceof FiniteField\Integer) { + throw new \InvalidArgumentException('A phpseclib\Math\BigInteger or phpseclib\Math\Common\FiniteField\Integer object was expected.'); } $element = $element->toBytes(true); $result.= pack('Na*', strlen($element), $element); @@ -224,4 +224,95 @@ abstract class Strings } return $result; } + + /** + * 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 + * + * @access public + * @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 + * + * @access public + * @param string $x + * @return string + */ + public static function bin2bits($x) + { + /* + // 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 ltrim($bits, '0'); + } } diff --git a/phpseclib/Crypt/Blowfish.php b/phpseclib/Crypt/Blowfish.php index c3c9cb10..08c942ac 100644 --- a/phpseclib/Crypt/Blowfish.php +++ b/phpseclib/Crypt/Blowfish.php @@ -84,7 +84,7 @@ class Blowfish extends BlockCipher * @access private * @var array */ - private $sbox0 = [ + private static $sbox0 = [ 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, @@ -125,7 +125,7 @@ class Blowfish extends BlockCipher * @access private * @var array */ - private $sbox1 = [ + private static $sbox1 = [ 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, @@ -166,7 +166,7 @@ class Blowfish extends BlockCipher * @access private * @var array */ - private $sbox2 = [ + private static $sbox2 = [ 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, @@ -207,7 +207,7 @@ class Blowfish extends BlockCipher * @access private * @var array */ - private $sbox3 = [ + private static $sbox3 = [ 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, @@ -248,7 +248,7 @@ class Blowfish extends BlockCipher * @var array * @access private */ - private $parray = [ + private static $parray = [ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b @@ -364,10 +364,10 @@ class Blowfish extends BlockCipher $this->bctx = [ 'p' => [], 'sb' => [ - $this->sbox0, - $this->sbox1, - $this->sbox2, - $this->sbox3 + self::$sbox0, + self::$sbox1, + self::$sbox2, + self::$sbox3 ] ]; @@ -382,7 +382,7 @@ class Blowfish extends BlockCipher $j = 0; } } - $this->bctx['p'][] = $this->parray[$i] ^ $data; + $this->bctx['p'][] = self::$parray[$i] ^ $data; } // encrypt the zero-string, replace P1 and P2 with the encrypted data, @@ -411,14 +411,14 @@ class Blowfish extends BlockCipher */ protected 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]; + $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); + $in = unpack('N*', $in); $l = $in[1]; $r = $in[2]; @@ -433,7 +433,7 @@ class Blowfish extends BlockCipher $sb_2[$r >> 8 & 0xff]) + $sb_3[$r & 0xff]); } - return pack("N*", $r ^ $p[17], $l ^ $p[16]); + return pack('N*', $r ^ $p[17], $l ^ $p[16]); } /** @@ -445,13 +445,13 @@ class Blowfish extends BlockCipher */ protected 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]; + $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); + $in = unpack('N*', $in); $l = $in[1]; $r = $in[2]; @@ -466,7 +466,7 @@ class Blowfish extends BlockCipher $sb_2[$r >> 8 & 0xff]) + $sb_3[$r & 0xff]); } - return pack("N*", $r ^ $p[0], $l ^ $p[1]); + return pack('N*', $r ^ $p[0], $l ^ $p[1]); } /** diff --git a/phpseclib/Crypt/Common/AsymmetricKey.php b/phpseclib/Crypt/Common/AsymmetricKey.php index 2fd479d6..a95f7e80 100644 --- a/phpseclib/Crypt/Common/AsymmetricKey.php +++ b/phpseclib/Crypt/Common/AsymmetricKey.php @@ -18,6 +18,8 @@ namespace phpseclib\Crypt\Common; use phpseclib\Math\BigInteger; use phpseclib\Crypt\Hash; use ParagonIE\ConstantTime\Base64; +use phpseclib\Exception\UnsupportedOperationException; +use phpseclib\Exception\FileNotFoundException; /** * Base Class for all stream cipher classes @@ -43,16 +45,6 @@ abstract class AsymmetricKey */ protected static $one; - /** - * Engine - * - * This is only used for key generation. Valid values are RSA::ENGINE_INTERNAL and RSA::ENGINE_OPENSSL - * - * @var int - * @access private - */ - protected static $engine = NULL; - /** * OpenSSL configuration file name. * @@ -132,6 +124,17 @@ abstract class AsymmetricKey */ protected $publicKeyFormat = 'PKCS8'; + /** + * Parameters Format + * + * No setParametersFormat method exists because PKCS1 is the only format that supports + * parameters in both DSA and ECDSA (RSA doesn't have an analog) + * + * @var string + * @access private + */ + protected $parametersFormat = 'PKCS1'; + /** * Hash function * @@ -148,21 +151,21 @@ abstract class AsymmetricKey */ private $hmac; - /**#@+ - * @access private - * @see self::__construct() - */ /** - * To use the pure-PHP implementation - */ - const ENGINE_INTERNAL = 1; - /** - * To use the OpenSSL library + * Hash manually set? * - * (if enabled; otherwise, the internal implementation will be used) + * @var bool + * @access private */ - const ENGINE_OPENSSL = 2; - /**#@-*/ + protected $hashManuallySet = false; + + /** + * Available Engines + * + * @var boolean[] + * @access private + */ + protected static $engines = []; /** * The constructor @@ -180,57 +183,35 @@ abstract class AsymmetricKey /** * Tests engine validity * - * @return boolean * @access public * @param int $val */ - public static function isValidEngine($val) + public static function useBestEngine() { - switch ($val) { - case self::ENGINE_OPENSSL: - return extension_loaded('openssl') && file_exists(self::$configFile); - case self::ENGINE_INTERNAL: - return true; - } - - return false; - } - - /** - * Sets the engine - * - * Only used in RSA::createKey. Valid values are RSA::ENGINE_OPENSSL and RSA::ENGINE_INTERNAL - * - * @access public - * @param int $val - */ - public static function setPreferredEngine($val) - { - static::$engine = null; - $candidateEngines = [ - $val, - self::ENGINE_OPENSSL + static::$engines = [ + 'PHP' => true, + 'OpenSSL' => extension_loaded('openssl') && file_exists(self::$configFile), + // 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') ]; - foreach ($candidateEngines as $engine) { - if (static::isValidEngine($engine)) { - static::$engine = $engine; - break; - } - } - if (!isset(static::$engine)) { - static::$engine = self::ENGINE_INTERNAL; - } + + return static::$engines; } /** - * Returns the engine + * Flag to use internal engine only (useful for unit testing) * * @access public - * @return int */ - public static function getEngine() + public static function useInternalEngine() { - return self::$engine; + static::$engines = [ + 'PHP' => true, + 'OpenSSL' => false, + 'libsodium' => false + ]; } /** @@ -243,7 +224,7 @@ abstract class AsymmetricKey if (!isset(self::$zero)) { self::$zero= new BigInteger(0); self::$one = new BigInteger(1); - self::$configFile = __DIR__ . '/../openssl.cnf'; + self::$configFile = __DIR__ . '/../../openssl.cnf'; } self::loadPlugins('Keys'); @@ -268,6 +249,10 @@ abstract class AsymmetricKey } $name = $file->getBasename('.php'); $type = 'phpseclib\Crypt\\' . static::ALGORITHM . '\\' . $format . '\\' . $name; + $reflect = new \ReflectionClass($type); + if ($reflect->isTrait()) { + continue; + } self::$plugins[static::ALGORITHM][$format][strtolower($name)] = $type; self::$origPlugins[static::ALGORITHM][$format][] = $name; } @@ -305,8 +290,14 @@ abstract class AsymmetricKey * @param string $type * @return array|bool */ - public function load($key, $type) + protected function load($key, $type) { + if ($key instanceof self) { + $this->hmac = $key->hmac; + + return; + } + $components = false; if ($type === false) { foreach (self::$plugins[static::ALGORITHM]['Keys'] as $format) { @@ -343,9 +334,9 @@ abstract class AsymmetricKey * @access private * @param string $key * @param string $type - * @return array|bool + * @return array */ - public function setPublicKey($key, $type) + protected function setPublicKey($key, $type) { $components = false; if ($type === false) { @@ -409,7 +400,7 @@ abstract class AsymmetricKey self::initialize_static_variables(); if (class_exists($fullname)) { - $meta = new \ReflectionClass($path); + $meta = new \ReflectionClass($fullname); $shortname = $meta->getShortName(); self::$plugins[static::ALGORITHM]['Keys'][strtolower($shortname)] = $fullname; self::$origPlugins[static::ALGORITHM]['Keys'][] = $shortname; @@ -471,6 +462,15 @@ abstract class AsymmetricKey return $key; } $key = $this->getPublicKey($this->publicKeyFormat); + if (is_string($key)) { + return $key; + } + + if (!method_exists($this, 'getParameters')) { + return ''; + } + + $key = $this->getParameters($this->parametersFormat); return is_string($key) ? $key : ''; } catch (\Exception $e) { return ''; @@ -499,6 +499,16 @@ abstract class AsymmetricKey */ public function setPrivateKeyFormat($format) { + $type = self::validatePlugin('Keys', $format); + if ($type === false) { + throw new FileNotFoundException('Plugin not found'); + } + + $type = self::validatePlugin('Keys', $format, 'savePrivateKey'); + if ($type === false) { + throw new UnsupportedOperationException('Plugin does not support private keys'); + } + $this->privateKeyFormat = $format; } @@ -511,9 +521,47 @@ abstract class AsymmetricKey */ public function setPublicKeyFormat($format) { + $type = self::validatePlugin('Keys', $format); + if ($type === false) { + throw new FileNotFoundException('Plugin not found'); + } + + $type = self::validatePlugin('Keys', $format, 'savePublicKey'); + if ($type === false) { + throw new UnsupportedOperationException('Plugin does not support public keys'); + } + $this->publicKeyFormat = $format; } + /** + * Determines the key format + * + * Sets both the public key and private key formats to the specified format if those formats support + * the key type + * + * @see self::__toString() + * @access public + * @param string $format + */ + public function setKeyFormat($format) + { + $type = self::validatePlugin('Keys', $format); + if ($type === false) { + throw new FileNotFoundException('Plugin not found'); + } + + try { + $this->setPrivateKeyFormat($format); + } catch (\Exception $e) { + } + + try { + $this->setPublicKeyFormat($format); + } catch (\Exception $e) { + } + } + /** * Returns the format of the loaded key. * @@ -560,6 +608,8 @@ abstract class AsymmetricKey { $this->hash = new Hash($hash); $this->hmac = new Hash($hash); + + $this->hashManuallySet = true; } /** diff --git a/phpseclib/Crypt/Common/Keys/OpenSSH.php b/phpseclib/Crypt/Common/Keys/OpenSSH.php index e0e18679..b66beab8 100644 --- a/phpseclib/Crypt/Common/Keys/OpenSSH.php +++ b/phpseclib/Crypt/Common/Keys/OpenSSH.php @@ -64,12 +64,12 @@ abstract class OpenSSH * @access public * @param string $key * @param string $type - * @return array|bool + * @return array */ public static function load($key, $type) { if (!is_string($key)) { - return false; + throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } $parts = explode(' ', $key, 3); @@ -79,21 +79,20 @@ abstract class OpenSSH $comment = isset($parts[1]) ? $parts[1] : false; } else { if ($parts[0] != $type) { - return false; + throw new \UnexpectedValueException('Expected a ' . $type . ' key - got a ' . $parts[0] . ' key'); } $key = Base64::decode($parts[1]); $comment = isset($parts[2]) ? $parts[2] : false; } if ($key === false) { - return false; + throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } - if (substr($key, 0, 11) != "\0\0\0\7$type") { - return false; + if (Strings::shift($key, strlen($type) + 4) != "\0\0\0" . chr(strlen($type)) . $type) { + throw new \UnexpectedValueException('Key appears to be malformed'); } - Strings::shift($key, 11); if (strlen($key) <= 4) { - return false; + throw new \UnexpectedValueException('Key appears to be malformed'); } return $key; diff --git a/phpseclib/Crypt/Common/Keys/PKCS1.php b/phpseclib/Crypt/Common/Keys/PKCS1.php index 6ae8588d..297f1c85 100644 --- a/phpseclib/Crypt/Common/Keys/PKCS1.php +++ b/phpseclib/Crypt/Common/Keys/PKCS1.php @@ -22,6 +22,7 @@ use phpseclib\Crypt\AES; use phpseclib\Crypt\DES; use phpseclib\Crypt\TripleDES; use phpseclib\File\ASN1; +use phpseclib\Exception\UnsupportedAlgorithmException; /** * PKCS1 Formatted Key Handler @@ -93,7 +94,7 @@ abstract class PKCS1 extends PKCS case preg_match("#^DES-$modes$#", $algo, $matches): return new DES(self::getEncryptionMode($matches[1])); default: - throw new \UnexpectedValueException('Unsupported encryption algorithmn'); + throw new UnsupportedAlgorithmException($algo . ' is not a supported algorithm'); } } @@ -122,12 +123,12 @@ abstract class PKCS1 extends PKCS * @access public * @param string $key * @param string $password optional - * @return array|bool + * @return array */ protected static function load($key, $password) { if (!is_string($key)) { - return false; + 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 @@ -163,7 +164,7 @@ abstract class PKCS1 extends PKCS if ($decoded !== false) { $key = $decoded; } elseif (self::$format == self::MODE_PEM) { - return false; + throw new \UnexpectedValueException('Expected base64-encoded PEM format but was unable to decode base64 text'); } } } diff --git a/phpseclib/Crypt/Common/Keys/PKCS8.php b/phpseclib/Crypt/Common/Keys/PKCS8.php index 579b7633..c6b3eae4 100644 --- a/phpseclib/Crypt/Common/Keys/PKCS8.php +++ b/phpseclib/Crypt/Common/Keys/PKCS8.php @@ -138,6 +138,7 @@ abstract class PKCS8 extends PKCS /** * Returns a SymmetricKey object based on a PBES1 $algo + * * @return \phpseclib\Crypt\Common\SymmetricKey * @access public * @param string $algo @@ -267,51 +268,54 @@ abstract class PKCS8 extends PKCS private static function initialize_static_variables() { if (!static::$childOIDsLoaded) { - ASN1::loadOIDs([static::OID_VALUE => static::OID_NAME]); + 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 - '1.2.840.113549.1.5.1' => 'pbeWithMD2AndDES-CBC', - '1.2.840.113549.1.5.4' => 'pbeWithMD2AndRC2-CBC', - '1.2.840.113549.1.5.3' => 'pbeWithMD5AndDES-CBC', - '1.2.840.113549.1.5.6' => 'pbeWithMD5AndRC2-CBC', - '1.2.840.113549.1.5.10'=> 'pbeWithSHA1AndDES-CBC', - '1.2.840.113549.1.5.11'=> 'pbeWithSHA1AndRC2-CBC', + // 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 - '1.2.840.113549.1.12.1.1' => 'pbeWithSHAAnd128BitRC4', - '1.2.840.113549.1.12.1.2' => 'pbeWithSHAAnd40BitRC4', - '1.2.840.113549.1.12.1.3' => 'pbeWithSHAAnd3-KeyTripleDES-CBC', - '1.2.840.113549.1.12.1.4' => 'pbeWithSHAAnd2-KeyTripleDES-CBC', - '1.2.840.113549.1.12.1.5' => 'pbeWithSHAAnd128BitRC2-CBC', - '1.2.840.113549.1.12.1.6' => 'pbeWithSHAAnd40BitRC2-CBC', + // 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', - '1.2.840.113549.1.5.12' => 'id-PBKDF2', - '1.2.840.113549.1.5.13' => 'id-PBES2', - '1.2.840.113549.1.5.14' => 'id-PBMAC1', + '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 - '1.2.840.113549.2.7' => 'id-hmacWithSHA1', - '1.2.840.113549.2.8' => 'id-hmacWithSHA224', - '1.2.840.113549.2.9' => 'id-hmacWithSHA256', - '1.2.840.113549.2.10'=> 'id-hmacWithSHA384', - '1.2.840.113549.2.11'=> 'id-hmacWithSHA512', - '1.2.840.113549.2.12'=> 'id-hmacWithSHA512-224', - '1.2.840.113549.2.13'=> 'id-hmacWithSHA512-256', + // 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', - '1.3.14.3.2.7' => 'desCBC', - '1.2.840.113549.3.7' => 'des-EDE3-CBC', - '1.2.840.113549.3.2' => 'rc2CBC', - '1.2.840.113549.3.9' => 'rc5-CBC-PAD', + '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', - '2.16.840.1.101.3.4.1.2' => 'aes128-CBC-PAD', - '2.16.840.1.101.3.4.1.22'=> 'aes192-CBC-PAD', - '2.16.840.1.101.3.4.1.42'=> 'aes256-CBC-PAD' + '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; } @@ -323,14 +327,14 @@ abstract class PKCS8 extends PKCS * @access public * @param string $key * @param string $password optional - * @return array|bool + * @return array */ protected static function load($key, $password = '') { self::initialize_static_variables(); if (!is_string($key)) { - return false; + throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } if (self::$format != self::MODE_DER) { @@ -338,13 +342,13 @@ abstract class PKCS8 extends PKCS if ($decoded !== false) { $key = $decoded; } elseif (self::$format == self::MODE_PEM) { - return false; + throw new \UnexpectedValueException('Expected base64-encoded PEM format but was unable to decode base64 text'); } } $decoded = ASN1::decodeBER($key); if (empty($decoded)) { - return false; + throw new \RuntimeException('Unable to decode BER'); } $meta = []; @@ -379,7 +383,7 @@ abstract class PKCS8 extends PKCS $key = $cipher->decrypt($decrypted['encryptedData']); $decoded = ASN1::decodeBER($key); if (empty($decoded)) { - return false; + throw new \RuntimeException('Unable to decode BER 2'); } break; @@ -442,7 +446,7 @@ abstract class PKCS8 extends PKCS $key = $cipher->decrypt($decrypted['encryptedData']); $decoded = ASN1::decodeBER($key); if (empty($decoded)) { - return false; + throw new \RuntimeException('Unable to decode BER 3'); } break; default: @@ -458,11 +462,22 @@ abstract class PKCS8 extends PKCS } } - $private = ASN1::asn1map($decoded[0], Maps\PrivateKeyInfo::MAP); + $private = ASN1::asn1map($decoded[0], Maps\OneAsymmetricKey::MAP); if (is_array($private)) { - return $private['privateKeyAlgorithm']['algorithm'] == static::OID_NAME ? - $private + $meta : - false; + 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'); + } + } + return $private + $meta; } // EncryptedPrivateKeyInfo and PublicKeyInfo have largely identical "signatures". the only difference @@ -470,9 +485,23 @@ abstract class PKCS8 extends PKCS // 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 ($public['publicKey'][0] != "\0" || $public['publicKeyAlgorithm']['algorithm'] != static::OID_NAME) { - return false; + if ($public['publicKey'][0] != "\0") { + throw new \UnexpectedValueException('The first byte of the public key should be null - not ' . Hex::encode($val)); + } + if (is_array(static::OID_NAME)) { + if (!in_array($public['publicKeyAlgorithm']['algorithm'], static::OID_NAME)) { + throw new UnsupportedAlgorithmException($private['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 ' . $private['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; @@ -489,16 +518,18 @@ abstract class PKCS8 extends PKCS * @param string $attr * @param mixed $params * @param string $password + * @param string $oid optional + * @param string $publicKey optional * @return string */ - protected static function wrapPrivateKey($key, $attr, $params, $password) + protected static function wrapPrivateKey($key, $attr, $params, $password, $oid = null, $publicKey = '') { self::initialize_static_variables(); $key = [ 'version' => 'v1', 'privateKeyAlgorithm' => [ - 'algorithm' => static::OID_NAME, + 'algorithm' => is_string(static::OID_NAME) ? static::OID_NAME : $oid, 'parameters' => $params ], 'privateKey' => $key @@ -506,7 +537,11 @@ abstract class PKCS8 extends PKCS if (!empty($attr)) { $key['attributes'] = $attr; } - $key = ASN1::encodeDER($key, Maps\PrivateKeyInfo::MAP); + 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 = self::$defaultIterationCount; @@ -590,13 +625,13 @@ abstract class PKCS8 extends PKCS * @param mixed $params * @return string */ - protected static function wrapPublicKey($key, $params) + protected static function wrapPublicKey($key, $params, $oid = null) { self::initialize_static_variables(); $key = [ 'publicKeyAlgorithm' => [ - 'algorithm' => static::OID_NAME, + 'algorithm' => is_string(static::OID_NAME) ? static::OID_NAME : $oid, 'parameters' => $params ], 'publicKey' => "\0" . $key diff --git a/phpseclib/Crypt/Common/Keys/PuTTY.php b/phpseclib/Crypt/Common/Keys/PuTTY.php index d2357c3b..ba223697 100644 --- a/phpseclib/Crypt/Common/Keys/PuTTY.php +++ b/phpseclib/Crypt/Common/Keys/PuTTY.php @@ -21,6 +21,7 @@ use phpseclib\Crypt\AES; use phpseclib\Crypt\Hash; use phpseclib\Crypt\Random; use phpseclib\Common\Functions\Strings; +use phpseclib\Exception\UnsupportedAlgorithmException; /** * PuTTY Formatted Key Handler @@ -75,26 +76,26 @@ abstract class PuTTY * @access public * @param string $key * @param string $password - * @return array|bool + * @return array */ protected static function load($key, $password) { if (!is_string($key)) { - return false; + throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } - if (strpos($key, 'BEGIN SSH2 PUBLIC KEY')) { + if (strpos($key, 'BEGIN SSH2 PUBLIC KEY') !== false) { $data = preg_split('#[\r\n]+#', $key); $data = array_splice($data, 2, -1); $data = implode('', $data); $components = call_user_func([static::PUBLIC_HANDLER, 'load'], $data); if ($components === false) { - return false; + throw new \UnexpectedValueException('Unable to decode public key'); } if (!preg_match('#Comment: "(.+)"#', $key, $matches)) { - return false; + throw new \UnexpectedValueException('Key is missing a comment'); } $components['comment'] = str_replace(['\\\\', '\"'], ['\\', '"'], $matches[1]); @@ -105,8 +106,12 @@ abstract class PuTTY $key = preg_split('#\r\n|\r|\n#', trim($key)); $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0])); - if ($type != static::TYPE) { - return false; + $components['type'] = $type; + 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])); @@ -114,12 +119,12 @@ abstract class PuTTY $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3])); $public = Base64::decode(implode('', array_map('trim', array_slice($key, 4, $publicLength)))); - $source = Strings::packSSH2('ssss', static::TYPE, $encryption, $components['comment'], $public); + $source = Strings::packSSH2('ssss', $type, $encryption, $components['comment'], $public); extract(unpack('Nlength', Strings::shift($public, 4))); - /** @var integer $length */ - if (Strings::shift($public, $length) != static::TYPE) { - return false; + $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; @@ -165,19 +170,20 @@ abstract class PuTTY * @access private * @param string $public * @param string $private + * @param string $type * @param string $password * @return string */ - protected static function wrapPrivateKey($public, $private, $password) + protected static function wrapPrivateKey($public, $private, $type, $password) { - $key = "PuTTY-User-Key-File-2: " . static::TYPE . "\r\nEncryption: "; + $key = "PuTTY-User-Key-File-2: " . $type . "\r\nEncryption: "; $encryption = (!empty($password) || is_string($password)) ? 'aes256-cbc' : 'none'; $key.= $encryption; $key.= "\r\nComment: " . self::$comment . "\r\n"; - $public = Strings::packSSH2('s', static::TYPE) . $public; + $public = Strings::packSSH2('s', $type) . $public; - $source = Strings::packSSH2('ssss', static::TYPE, $encryption, self::$comment, $public); + $source = Strings::packSSH2('ssss', $type, $encryption, self::$comment, $public); $public = Base64::encode($public); $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n"; @@ -215,11 +221,12 @@ abstract class PuTTY * * @access private * @param string $key + * @param string $type * @return string */ - protected static function wrapPublicKey($key) + protected static function wrapPublicKey($key, $type) { - $key = pack('Na*a*', strlen(static::TYPE), static::TYPE, $key); + $key = pack('Na*a*', strlen($type), $type, $key); $key = "---- BEGIN SSH2 PUBLIC KEY ----\r\n" . 'Comment: "' . str_replace(['\\', '"'], ['\\\\', '\"'], self::$comment) . "\"\r\n" . chunk_split(Base64::encode($key), 64) . diff --git a/phpseclib/Crypt/Common/SymmetricKey.php b/phpseclib/Crypt/Common/SymmetricKey.php index 8f14d06b..6e630de3 100644 --- a/phpseclib/Crypt/Common/SymmetricKey.php +++ b/phpseclib/Crypt/Common/SymmetricKey.php @@ -2133,6 +2133,10 @@ abstract class SymmetricKey throw new \UnexpectedValueException('No IV has been defined'); } + if ($this->key === false) { + throw new \UnexpectedValueException('No key has been defined'); + } + $this->encryptIV = $this->decryptIV = $this->iv; } diff --git a/phpseclib/Crypt/DES.php b/phpseclib/Crypt/DES.php index b7c45ee4..665dc2e0 100644 --- a/phpseclib/Crypt/DES.php +++ b/phpseclib/Crypt/DES.php @@ -161,7 +161,7 @@ class DES extends BlockCipher * @var array * @access private */ - protected $shuffle = [ + protected static $shuffle = [ "\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", @@ -300,7 +300,7 @@ class DES extends BlockCipher * @var array * @access private */ - protected $ipmap = [ + protected static $ipmap = [ 0x00, 0x10, 0x01, 0x11, 0x20, 0x30, 0x21, 0x31, 0x02, 0x12, 0x03, 0x13, 0x22, 0x32, 0x23, 0x33, 0x40, 0x50, 0x41, 0x51, 0x60, 0x70, 0x61, 0x71, @@ -342,7 +342,7 @@ class DES extends BlockCipher * @var array * @access private */ - protected $invipmap = [ + protected static $invipmap = [ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, @@ -386,7 +386,7 @@ class DES extends BlockCipher * @var array * @access private */ - protected $sbox1 = [ + protected static $sbox1 = [ 0x00808200, 0x00000000, 0x00008000, 0x00808202, 0x00808002, 0x00008202, 0x00000002, 0x00008000, 0x00000200, 0x00808200, 0x00808202, 0x00000200, @@ -411,7 +411,7 @@ class DES extends BlockCipher * @var array * @access private */ - protected $sbox2 = [ + protected static $sbox2 = [ 0x40084010, 0x40004000, 0x00004000, 0x00084010, 0x00080000, 0x00000010, 0x40080010, 0x40004010, 0x40000010, 0x40084010, 0x40084000, 0x40000000, @@ -436,7 +436,7 @@ class DES extends BlockCipher * @var array * @access private */ - protected $sbox3 = [ + protected static $sbox3 = [ 0x00000104, 0x04010100, 0x00000000, 0x04010004, 0x04000100, 0x00000000, 0x00010104, 0x04000100, 0x00010004, 0x04000004, 0x04000004, 0x00010000, @@ -461,7 +461,7 @@ class DES extends BlockCipher * @var array * @access private */ - protected $sbox4 = [ + protected static $sbox4 = [ 0x80401000, 0x80001040, 0x80001040, 0x00000040, 0x00401040, 0x80400040, 0x80400000, 0x80001000, 0x00000000, 0x00401000, 0x00401000, 0x80401040, @@ -486,7 +486,7 @@ class DES extends BlockCipher * @var array * @access private */ - protected $sbox5 = [ + protected static $sbox5 = [ 0x00000080, 0x01040080, 0x01040000, 0x21000080, 0x00040000, 0x00000080, 0x20000000, 0x01040000, 0x20040080, 0x00040000, 0x01000080, 0x20040080, @@ -511,7 +511,7 @@ class DES extends BlockCipher * @var array * @access private */ - protected $sbox6 = [ + protected static $sbox6 = [ 0x10000008, 0x10200000, 0x00002000, 0x10202008, 0x10200000, 0x00000008, 0x10202008, 0x00200000, 0x10002000, 0x00202008, 0x00200000, 0x10000008, @@ -536,7 +536,7 @@ class DES extends BlockCipher * @var array * @access private */ - protected $sbox7 = [ + protected static $sbox7 = [ 0x00100000, 0x02100001, 0x02000401, 0x00000000, 0x00000400, 0x02000401, 0x00100401, 0x02100400, 0x02100401, 0x00100000, 0x00000000, 0x02000001, @@ -561,7 +561,7 @@ class DES extends BlockCipher * @var array * @access private */ - protected $sbox8 = [ + protected static $sbox8 = [ 0x08000820, 0x00000800, 0x00020000, 0x08020820, 0x08000000, 0x08000820, 0x00000020, 0x08000000, 0x00020020, 0x08020000, 0x08020820, 0x00020800, @@ -687,18 +687,18 @@ class DES extends BlockCipher { 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); + $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[] = $this->shuffle[$this->ipmap[$i]]; - $shuffleinvip[] = $this->shuffle[$this->invipmap[$i]]; + $shuffleip[] = self::$shuffle[self::$ipmap[$i]]; + $shuffleinvip[] = self::$shuffle[self::$invipmap[$i]]; } } @@ -1229,14 +1229,14 @@ class DES extends BlockCipher // Perform the PC/1 transformation and compute C and D. $t = unpack('Nl/Nr', $key); list($l, $r) = [$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 = (self::$shuffle[$pc1map[ $r & 0xFF]] & "\x80\x80\x80\x80\x80\x80\x80\x00") | + (self::$shuffle[$pc1map[($r >> 8) & 0xFF]] & "\x40\x40\x40\x40\x40\x40\x40\x00") | + (self::$shuffle[$pc1map[($r >> 16) & 0xFF]] & "\x20\x20\x20\x20\x20\x20\x20\x00") | + (self::$shuffle[$pc1map[($r >> 24) & 0xFF]] & "\x10\x10\x10\x10\x10\x10\x10\x00") | + (self::$shuffle[$pc1map[ $l & 0xFF]] & "\x08\x08\x08\x08\x08\x08\x08\x00") | + (self::$shuffle[$pc1map[($l >> 8) & 0xFF]] & "\x04\x04\x04\x04\x04\x04\x04\x00") | + (self::$shuffle[$pc1map[($l >> 16) & 0xFF]] & "\x02\x02\x02\x02\x02\x02\x02\x00") | + (self::$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); @@ -1308,18 +1308,18 @@ class DES extends BlockCipher $init_crypt = '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);' + $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[] = $this->shuffle[$this->ipmap[$i]]; - $shuffleinvip[] = $this->shuffle[$this->invipmap[$i]]; + $shuffleip[] = self::$shuffle[self::$ipmap[$i]]; + $shuffleinvip[] = self::$shuffle[self::$invipmap[$i]]; } } '; diff --git a/phpseclib/Crypt/DSA.php b/phpseclib/Crypt/DSA.php index 7b8c0762..b8a85903 100644 --- a/phpseclib/Crypt/DSA.php +++ b/phpseclib/Crypt/DSA.php @@ -34,7 +34,10 @@ use ParagonIE\ConstantTime\Base64; use phpseclib\File\ASN1; use phpseclib\Math\BigInteger; use phpseclib\Crypt\Common\AsymmetricKey; -use phpseclib\Common\Functions\Strings; +use phpseclib\Math\PrimeField; +use phpseclib\Crypt\ECDSA\Signature\ASN1 as ASN1Signature; +use phpseclib\Exception\UnsupportedOperationException; +use phpseclib\Exception\NoKeyLoadedException; /** * Pure-PHP FIPS 186-4 compliant implementation of DSA. @@ -95,14 +98,6 @@ class DSA extends AsymmetricKey */ private $y; - /** - * Parameters Format - * - * @var string - * @access private - */ - private $parametersFormat = 'PKCS1'; - /** * Create DSA parameters * @@ -111,10 +106,14 @@ class DSA extends AsymmetricKey * @param int $N * @return \phpseclib\Crypt\DSA|bool */ - static function createParameters($L = 2048, $N = 224) + public static function createParameters($L = 2048, $N = 224) { self::initialize_static_variables(); + if (!isset(self::$engines['PHP'])) { + self::useBestEngine(); + } + switch (true) { case $N == 160: /* @@ -186,10 +185,14 @@ class DSA extends AsymmetricKey * @access public * @return array|DSA */ - static function createKey(...$args) + public static function createKey(...$args) { self::initialize_static_variables(); + if (!isset(self::$engines['PHP'])) { + self::useBestEngine(); + } + if (count($args) == 2 && is_int($args[0]) && is_int($args[1])) { $private = self::createParameters($args[0], $args[1]); } else if (count($args) == 1 && $args[0] instanceof DSA) { @@ -220,6 +223,12 @@ class DSA extends AsymmetricKey */ public function load($key, $type = false) { + self::initialize_static_variables(); + + if (!isset(self::$engines['PHP'])) { + self::useBestEngine(); + } + if ($key instanceof DSA) { $this->privateKeyFormat = $key->privateKeyFormat; $this->publicKeyFormat = $key->publicKeyFormat; @@ -236,6 +245,13 @@ class DSA extends AsymmetricKey $components = parent::load($key, $type); if ($components === false) { + $this->format = null; + $this->p = null; + $this->q = null; + $this->g = null; + $this->x = null; + $this->y = null; + return false; } @@ -252,9 +268,7 @@ class DSA extends AsymmetricKey $this->g = $components['g']; } - if (isset($components['x'])) { - $this->x = $components['x']; - } + $this->x = isset($components['x']) ? $components['x'] : null; if (isset($components['y'])) { $this->y = $components['y']; @@ -281,27 +295,6 @@ class DSA extends AsymmetricKey ['L' => 0, 'N' => 0]; } - /** - * __toString() magic method - * - * @access public - * @return string - */ - public function __toString() - { - $key = parent::__toString(); - if (!empty($key)) { - return $key; - } - - try { - $key = $this->getParameters($this->parametersFormat); - return is_string($key) ? $key : ''; - } catch (\Exception $e) { - return ''; - } - } - /** * Returns the private key * @@ -331,6 +324,28 @@ class DSA extends AsymmetricKey return $type::savePrivateKey($this->p, $this->q, $this->g, $this->y, $this->x, $this->password); } + /** + * Is the key a private key? + * + * @access public + * @return bool + */ + public function isPrivateKey() + { + return isset($this->x); + } + + /** + * Is the key a public key? + * + * @access public + * @return bool + */ + public function isPublicKey() + { + return isset($this->p); + } + /** * Returns the public key * @@ -354,8 +369,14 @@ class DSA extends AsymmetricKey * @param string $type optional * @return mixed */ - public function getPublicKey($type = 'PKCS8') + public function getPublicKey($type = null) { + $returnObj = false; + if ($type === null) { + $returnObj = true; + $type = 'PKCS8'; + } + $type = self::validatePlugin('Keys', $type, 'savePublicKey'); if ($type === false) { return false; @@ -368,7 +389,15 @@ class DSA extends AsymmetricKey $this->y = $this->g->powMod($this->x, $this->p); } - return $type::savePublicKey($this->p, $this->q, $this->g, $this->y); + $key = $type::savePublicKey($this->p, $this->q, $this->g, $this->y); + if (!$returnObj) { + return $key; + } + + $public = clone $this; + $public->load($key, 'PKCS8'); + + return $public; } /** @@ -397,6 +426,20 @@ class DSA extends AsymmetricKey return $type::saveParameters($this->p, $this->q, $this->g); } + /** + * Returns the current engine being used + * + * @see self::useInternalEngine() + * @see self::useBestEngine() + * @access public + * @return string + */ + public function getEngine() + { + return self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods()) ? + 'OpenSSL' : 'PHP'; + } + /** * Create a signature * @@ -406,17 +449,43 @@ class DSA extends AsymmetricKey * @param string $format optional * @return mixed */ - function sign($message, $format = 'Raw') + public function sign($message, $format = 'ASN1') { + $shortFormat = $format; $format = self::validatePlugin('Signature', $format); if ($format === false) { return false; } - if (empty($this->x) || empty($this->p)) { - return false; + if (empty($this->x)) { + if (empty($this->y)) { + throw new NoKeyLoadedException('No key has been loaded'); + } + throw new UnsupportedOperationException('A public key cannot be used to sign data'); } + if (empty($this->p)) { + throw new \RuntimeException('DSA Prime P is not set'); + } + + if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) { + $signature = ''; + $result = openssl_sign($message, $signature, $this->getPrivateKey(), $this->hash->getHash()); + + if ($result) { + if ($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); @@ -425,8 +494,6 @@ class DSA extends AsymmetricKey continue; } $kinv = $k->modInverse($this->q); - $h = $this->hash->hash($message); - $h = $this->bits2int($h); $temp = $h->add($this->x->multiply($r)); $temp = $kinv->multiply($temp); list(, $s) = $temp->divide($this->q); @@ -464,7 +531,7 @@ class DSA extends AsymmetricKey * @param string $format optional * @return mixed */ - function verify($message, $signature, $format = 'Raw') + public function verify($message, $signature, $format = 'ASN1') { $format = self::validatePlugin('Signature', $format); if ($format === false) { @@ -477,8 +544,25 @@ class DSA extends AsymmetricKey } extract($params); - if (empty($this->y) || empty($this->p)) { - return false; + if (empty($this->y)) { + if (empty($this->x)) { + throw new NoKeyLoadedException('No key has been loaded'); + } + throw new UnsupportedOperationException('A private key cannot be used to sign data'); + } + + if (empty($this->p)) { + throw new \RuntimeException('DSA Prime P is not set'); + } + + 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->getPublicKey(), $this->hash->getHash()); + + if ($result != -1) { + return (bool) $result; + } } $q_1 = $this->q->subtract(self::$one); @@ -496,6 +580,6 @@ class DSA extends AsymmetricKey list(, $v) = $v1->multiply($v2)->divide($this->p); list(, $v) = $v->divide($this->q); - return Strings::equals($v->toBytes(), $r->toBytes()); + return $v->equals($r); } } \ No newline at end of file diff --git a/phpseclib/Crypt/DSA/Keys/OpenSSH.php b/phpseclib/Crypt/DSA/Keys/OpenSSH.php index 0b5d47b4..4b4163c8 100644 --- a/phpseclib/Crypt/DSA/Keys/OpenSSH.php +++ b/phpseclib/Crypt/DSA/Keys/OpenSSH.php @@ -37,18 +37,15 @@ abstract class OpenSSH extends Progenitor * @access public * @param string $key * @param string $password optional - * @return array|bool + * @return array */ public static function load($key, $password = '') { $key = parent::load($key, 'ssh-dss'); - if ($key === false) { - return false; - } $result = Strings::unpackSSH2('iiii', $key); if ($result === false) { - return false; + throw new \UnexpectedValueException('Key appears to be malformed'); } list($p, $q, $g, $y) = $result; diff --git a/phpseclib/Crypt/DSA/Keys/PKCS1.php b/phpseclib/Crypt/DSA/Keys/PKCS1.php index fefe3117..7d569409 100644 --- a/phpseclib/Crypt/DSA/Keys/PKCS1.php +++ b/phpseclib/Crypt/DSA/Keys/PKCS1.php @@ -15,6 +15,10 @@ * * 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. + * * @category Crypt * @package DSA * @author Jim Wigginton @@ -32,7 +36,7 @@ use phpseclib\File\ASN1\Maps; use ParagonIE\ConstantTime\Base64; /** - * PKCS#1 Formatted RSA Key Handler + * PKCS#1 Formatted DSA Key Handler * * @package RSA * @author Jim Wigginton @@ -46,22 +50,15 @@ abstract class PKCS1 extends Progenitor * @access public * @param string $key * @param string $password optional - * @return array|bool + * @return array */ public static function load($key, $password = '') { - if (!is_string($key)) { - return false; - } - $key = parent::load($key, $password); - if ($key === false) { - return false; - } $decoded = ASN1::decodeBER($key); if (empty($decoded)) { - return false; + throw new \RuntimeException('Unable to decode BER'); } $key = ASN1::asn1map($decoded[0], Maps\DSAParams::MAP); @@ -75,7 +72,11 @@ abstract class PKCS1 extends Progenitor } $key = ASN1::asn1map($decoded[0], Maps\DSAPublicKey::MAP); - return is_array($key) ? $key : false; + if (is_array($key)) { + return $key; + } + + throw new \RuntimeException('Unable to perform ASN1 mapping'); } /** diff --git a/phpseclib/Crypt/DSA/Keys/PKCS8.php b/phpseclib/Crypt/DSA/Keys/PKCS8.php index 4a94a861..f5447b46 100644 --- a/phpseclib/Crypt/DSA/Keys/PKCS8.php +++ b/phpseclib/Crypt/DSA/Keys/PKCS8.php @@ -69,47 +69,45 @@ abstract class PKCS8 extends Progenitor * @access public * @param string $key * @param string $password optional - * @return array|bool + * @return array */ public static function load($key, $password = '') { if (!is_string($key)) { - return false; + throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } $isPublic = strpos($key, 'PUBLIC') !== false; $key = parent::load($key, $password); - if ($key === false) { - return false; - } $type = isset($key['privateKey']) ? 'privateKey' : 'publicKey'; switch (true) { case !$isPublic && $type == 'publicKey': + throw new \UnexpectedValueException('Human readable string claims non-public key but DER encoded string claims public key'); case $isPublic && $type == 'privateKey': - return false; + throw new \UnexpectedValueException('Human readable string claims public key but DER encoded string claims private key'); } $decoded = ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element); if (empty($decoded)) { - return false; + throw new \RuntimeException('Unable to decode BER of parameters'); } $components = ASN1::asn1map($decoded[0], Maps\DSAParams::MAP); if (!is_array($components)) { - return false; + throw new \RuntimeException('Unable to perform ASN1 mapping on parameters'); } $decoded = ASN1::decodeBER($key[$type]); if (empty($decoded)) { - return false; + 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) { - return false; + throw new \RuntimeException('Unable to perform ASN1 mapping'); } if (isset($key['meta'])) { diff --git a/phpseclib/Crypt/DSA/Keys/PuTTY.php b/phpseclib/Crypt/DSA/Keys/PuTTY.php index 3429a54f..2f85fcfe 100644 --- a/phpseclib/Crypt/DSA/Keys/PuTTY.php +++ b/phpseclib/Crypt/DSA/Keys/PuTTY.php @@ -44,10 +44,10 @@ abstract class PuTTY extends Progenitor /** * Algorithm Identifier * - * @var string + * @var array * @access private */ - const TYPE = 'ssh-dss'; + protected static $types = ['ssh-dss']; /** * Break a public or private key down into its constituent components @@ -55,31 +55,29 @@ abstract class PuTTY extends Progenitor * @access public * @param string $key * @param string $password optional - * @return array|bool + * @return array */ public static function load($key, $password = '') { $components = parent::load($key, $password); - if ($components === false || !isset($components['private'])) { + if (!isset($components['private'])) { return $components; } + extract($components); + unset($components['public'], $components['private']); - $result = Strings::unpackSSH2('iiii', $components['public']); + $result = Strings::unpackSSH2('iiii', $public); if ($result === false) { - return false; + throw new \UnexpectedValueException('Key appears to be malformed'); } list($p, $q, $g, $y) = $result; - $result = Strings::unpackSSH2('i', $components['private']); + $result = Strings::unpackSSH2('i', $private); if ($result === false) { - return false; + throw new \UnexpectedValueException('Key appears to be malformed'); } list($x) = $result; - if (isset($components['comment'])) { - $comment = $components['comment']; - } - return compact('p', 'q', 'g', 'y', 'x', 'comment'); } @@ -95,7 +93,7 @@ abstract class PuTTY extends Progenitor * @param string $password optional * @return string */ - public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, $password = '') + public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, $password = false) { if ($q->getLength() != 160) { throw new \InvalidArgumentException('SSH only supports keys with an N (length of Group Order q) of 160'); @@ -104,7 +102,7 @@ abstract class PuTTY extends Progenitor $public = Strings::packSSH2('iiii', $p, $q, $g, $y); $private = Strings::packSSH2('i', $x); - return self::wrapPrivateKey($public, $private, $password); + return self::wrapPrivateKey($public, $private, 'ssh-dsa', $password); } /** @@ -123,6 +121,6 @@ abstract class PuTTY extends Progenitor 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)); + return self::wrapPublicKey(Strings::packSSH2('iiii', $p, $q, $g, $y), 'ssh-dsa'); } } diff --git a/phpseclib/Crypt/DSA/Keys/Raw.php b/phpseclib/Crypt/DSA/Keys/Raw.php index 7adbb363..568c1821 100644 --- a/phpseclib/Crypt/DSA/Keys/Raw.php +++ b/phpseclib/Crypt/DSA/Keys/Raw.php @@ -34,12 +34,12 @@ abstract class Raw * @access public * @param array $key * @param string $password optional - * @return array|bool + * @return array */ public static function load($key, $password = '') { if (!is_array($key)) { - return false; + throw new \UnexpectedValueException('Key should be a array - not a ' . gettype($key)); } switch (true) { @@ -50,7 +50,7 @@ abstract class Raw case !isset($key['x']) && !isset($key['y']): case isset($key['x']) && !$key['x'] instanceof BigInteger: case isset($key['y']) && !$key['y'] instanceof BigInteger: - return false; + throw new \UnexpectedValueException('Key appears to be malformed'); } $options = ['p' => 1, 'q' => 1, 'g' => 1, 'x' => 1, 'y' => 1]; diff --git a/phpseclib/Crypt/DSA/Keys/XML.php b/phpseclib/Crypt/DSA/Keys/XML.php index 0741ae5f..d6a8725b 100644 --- a/phpseclib/Crypt/DSA/Keys/XML.php +++ b/phpseclib/Crypt/DSA/Keys/XML.php @@ -39,12 +39,12 @@ abstract class XML * @access public * @param string $key * @param string $password optional - * @return array|bool + * @return array */ public static function load($key, $password = '') { if (!is_string($key)) { - return false; + throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } $use_errors = libxml_use_internal_errors(true); @@ -54,7 +54,7 @@ abstract class XML $key = '' . $key . ''; } if (!$dom->loadXML($key)) { - return false; + throw new \UnexpectedValueException('Key does not appear to contain XML'); } $xpath = new \DOMXPath($dom); $keys = ['p', 'q', 'g', 'y', 'j', 'seed', 'pgencounter']; @@ -95,7 +95,7 @@ abstract class XML libxml_use_internal_errors($use_errors); if (!isset($components['y'])) { - return false; + throw new \UnexpectedValueException('Key is missing y component'); } switch (true) { diff --git a/phpseclib/Crypt/DSA/Signature/PKCS.php b/phpseclib/Crypt/DSA/Signature/ASN1.php similarity index 84% rename from phpseclib/Crypt/DSA/Signature/PKCS.php rename to phpseclib/Crypt/DSA/Signature/ASN1.php index 6a3f4c31..009e5c18 100644 --- a/phpseclib/Crypt/DSA/Signature/PKCS.php +++ b/phpseclib/Crypt/DSA/Signature/ASN1.php @@ -1,7 +1,7 @@ * @access public */ -abstract class PKCS +abstract class ASN1 { /** * Loads a signature @@ -44,11 +44,11 @@ abstract class PKCS return false; } - $decoded = ASN1::decodeBER($sig); + $decoded = Encoder::decodeBER($sig); if (empty($decoded)) { return false; } - $components = ASN1::asn1map($decoded[0], Maps\DssSigValue::MAP); + $components = Encoder::asn1map($decoded[0], Maps\DssSigValue::MAP); return $components; } diff --git a/phpseclib/Crypt/ECDSA.php b/phpseclib/Crypt/ECDSA.php new file mode 100644 index 00000000..0224cc70 --- /dev/null +++ b/phpseclib/Crypt/ECDSA.php @@ -0,0 +1,756 @@ + + * sign($plaintext, 'ASN1'); + * + * echo $publickey->verify($plaintext, $signature) ? 'verified' : 'unverified'; + * ?> + * + * + * @category Crypt + * @package ECDSA + * @author Jim Wigginton + * @copyright 2016 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; +use phpseclib\Crypt\Common\AsymmetricKey; +use phpseclib\Exception\UnsupportedCurveException; +use phpseclib\Exception\UnsupportedOperationException; +use phpseclib\Exception\NoKeyLoadedException; +use phpseclib\File\ASN1; +use phpseclib\File\ASN1\Maps\ECParameters; +use phpseclib\Crypt\ECDSA\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; +use phpseclib\Crypt\ECDSA\Curves\Ed25519; +use phpseclib\Crypt\ECDSA\Curves\Ed448; +use phpseclib\Crypt\ECDSA\Keys\PKCS1; +use phpseclib\Crypt\ECDSA\Keys\PKCS8; +use phpseclib\Crypt\ECDSA\Signature\ASN1 as ASN1Signature; + +/** + * Pure-PHP implementation of ECDSA. + * + * @package ECDSA + * @author Jim Wigginton + * @access public + */ +class ECDSA extends AsymmetricKey +{ + /** + * Algorithm Name + * + * @var string + * @access private + */ + const ALGORITHM = 'ECDSA'; + + /** + * 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 + */ + private $dA; + + /** + * Public Key QA + * + * @var object[] + */ + private $QA; + + /** + * Curve + * + * @var \phpseclib\Crypt\ECDSA\BaseCurves\Base + */ + private $curve; + + /** + * Curve Name + * + * @var string + */ + private $curveName; + + /** + * Curve Order + * + * Used for deterministic ECDSA + * + * @var \phpseclib\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 \phpseclib\Math\BigInteger + */ + protected $x; + + /** + * 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 \phpseclib\Math\BigInteger + */ + private $context; + + /** + * Create public / private key pair. + * + * @access public + * @param string $curve + * @return \phpseclib\Crypt\ECDSA[] + */ + public static function createKey($curve) + { + self::initialize_static_variables(); + + if (!isset(self::$engines['PHP'])) { + self::useBestEngine(); + } + + if (self::$engines['libsodium'] && $curve == 'Ed25519' && function_exists('sodium_crypto_sign_keypair')) { + $kp = sodium_crypto_sign_keypair(); + + $privatekey = new static(); + $privatekey->load(sodium_crypto_sign_secretkey($kp)); + + $publickey = new static(); + $publickey->load(sodium_crypto_sign_publickey($kp)); + + $publickey->curveName = $privatekey->curveName = $curve; + + return compact('privatekey', 'publickey'); + } + + $privatekey = new static(); + + $curveName = $curve; + $curve = '\phpseclib\Crypt\ECDSA\Curves\\' . $curve; + if (!class_exists($curve)) { + throw new UnsupportedCurveException('Named Curve of ' . $curve . ' is not supported'); + } + $curve = new $curve(); + $privatekey->dA = $dA = $curve->createRandomMultiplier(); + $privatekey->QA = $curve->multiplyPoint($curve->getBasePoint(), $dA); + $privatekey->curve = $curve; + + $publickey = clone $privatekey; + unset($publickey->dA); + unset($publickey->x); + + $publickey->curveName = $privatekey->curveName = $curveName; + + return compact('privatekey', 'publickey'); + } + + /** + * 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 $key + * @param int $type optional + */ + public function load($key, $type = false) + { + self::initialize_static_variables(); + + if (!isset(self::$engines['PHP'])) { + self::useBestEngine(); + } + + if ($key instanceof ECDSA) { + $this->privateKeyFormat = $key->privateKeyFormat; + $this->publicKeyFormat = $key->publicKeyFormat; + $this->format = $key->format; + $this->dA = isset($key->dA) ? $key->dA : null; + $this->QA = $key->QA; + $this->curve = $key->curve; + $this->parametersFormat = $key->parametersFormat; + $this->hash = $key->hash; + + parent::load($key, false); + + return true; + } + + $components = parent::load($key, $type); + if ($components === false) { + $this->clearKey(); + return false; + } + + if ($components['curve'] instanceof Ed25519 && $this->hashManuallySet && $this->hash->getHash() != 'sha512') { + $this->clearKey(); + throw new \RuntimeException('Ed25519 only supports sha512 as a hash'); + } + if ($components['curve'] instanceof Ed448 && $this->hashManuallySet && $this->hash->getHash() != 'shake256-912') { + $this->clearKey(); + throw new \RuntimeException('Ed448 only supports shake256 with a length of 114 bytes'); + } + + $this->curve = $components['curve']; + $this->QA = $components['QA']; + $this->dA = isset($components['dA']) ? $components['dA'] : null; + + return true; + } + + /** + * Removes a key + * + * @access private + */ + private function clearKey() + { + $this->format = null; + $this->dA = null; + $this->QA = null; + $this->curve = null; + } + + /** + * Returns the curve + * + * Returns a string if it's a named curve, an array if not + * + * @access public + * @return string|array + */ + public function getCurve() + { + if ($this->curveName) { + return $this->curveName; + } + + if ($this->curve instanceof TwistedEdwardsCurve) { + $this->curveName = $this->curve instanceof Ed25519 ? 'Ed25519' : 'Ed448'; + return $this->curveName; + } + + $namedCurves = PKCS1::isUsingNamedCurves(); + PKCS1::useNamedCurve(); + + $params = $this->getParameters(); + $decoded = ASN1::extractBER($params); + $decoded = ASN1::decodeBER($decoded); + $decoded = ASN1::asn1map($decoded[0], ECParameters::MAP); + if (isset($decoded['namedCurve'])) { + $this->curveName = $decoded['namedCurve']; + + if (!$namedCurves) { + PKCS1::useSpecifiedCurve(); + } + + 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" + * + * @access public + * @return int + */ + public function getLength() + { + if (!isset($this->QA)) { + return 0; + } + + return $this->curve->getLength(); + } + + /** + * Is the key a private key? + * + * @access public + * @return bool + */ + public function isPrivateKey() + { + return isset($this->dA); + } + + /** + * Is the key a public key? + * + * @access public + * @return bool + */ + public function isPublicKey() + { + return isset($this->QA); + } + + /** + * Returns the private key + * + * @see self::getPublicKey() + * @access public + * @param string $type optional + * @return mixed + */ + public function getPrivateKey($type = 'PKCS8') + { + $type = self::validatePlugin('Keys', $type, 'savePrivateKey'); + if ($type === false) { + return false; + } + + if (!isset($this->dA)) { + return false; + } + + return $type::savePrivateKey($this->dA, $this->curve, $this->QA, $this->password); + } + + /** + * Returns the public key + * + * @see self::getPrivateKey() + * @access public + * @param string $type optional + * @return mixed + */ + public function getPublicKey($type = null) + { + $returnObj = false; + if ($type === null) { + $returnObj = true; + $type = 'PKCS8'; + } + $type = self::validatePlugin('Keys', $type, 'savePublicKey'); + if ($type === false) { + return false; + } + + if (!isset($this->QA)) { + return false; + } + + $key = $type::savePublicKey($this->curve, $this->QA); + if ($returnObj) { + $public = clone $this; + $public->load($key, 'PKCS8'); + + return $public; + } + return $key; + } + + /** + * Returns the parameters + * + * A public / private key is only returned if the currently loaded "key" contains an x or y + * value. + * + * @see self::getPublicKey() + * @see self::getPrivateKey() + * @access public + * @param string $type optional + * @return mixed + */ + public function getParameters($type = 'PKCS1') + { + $type = self::validatePlugin('Keys', $type, 'saveParameters'); + if ($type === false) { + return false; + } + + if (!isset($this->curve) || $this->curve instanceof TwistedEdwardsCurve) { + return false; + } + + return $type::saveParameters($this->curve); + } + + /** + * Returns the current engine being used + * + * @see self::useInternalEngine() + * @see self::useBestEngine() + * @access public + * @return string + */ + public function getEngine() + { + if (!isset($this->curve)) { + throw new \RuntimeException('getEngine should not be called until after a key has been loaded'); + } + + 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'; + } + + /** + * Sets the context + * + * Used by Ed25519 / Ed448. + * + * @see self::sign() + * @see self::verify() + * @access public + * @param string $context optional + */ + public function setContext($context = null) + { + if (!isset($context)) { + $this->context = null; + return; + } + if (!is_string($context)) { + throw new \RuntimeException('setContext expects a string'); + } + if (strlen($context) > 255) { + throw new \RuntimeException('The context is supposed to be, at most, 255 bytes long'); + } + $this->context = $context; + } + + /** + * Determines which hashing function should be used + * + * @access public + * @param string $hash + */ + public function setHash($hash) + { + if ($this->curve instanceof Ed25519 && $this->hash != 'sha512') { + throw new \RuntimeException('Ed25519 only supports sha512 as a hash'); + } + if ($this->curve instanceof Ed448 && $this->hash != 'shake256-912') { + throw new \RuntimeException('Ed448 only supports shake256 with a length of 114 bytes'); + } + + parent::setHash($hash); + } + + /** + * Create a signature + * + * @see self::verify() + * @access public + * @param string $message + * @param string $format optional + * @return mixed + */ + public function sign($message, $format = 'ASN1') + { + if (!isset($this->dA)) { + if (!isset($this->QA)) { + throw new NoKeyLoadedException('No key has been loaded'); + } + throw new UnsupportedOperationException('A public key cannot be used to sign data'); + } + + $dA = $this->dA->toBigInteger(); + + $order = $this->curve->getOrder(); + + if ($this->curve instanceof TwistedEdwardsCurve) { + if ($this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context)) { + return sodium_crypto_sign_detached($message, $this->getPrivateKey('libsodium')); + } + + // 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->dA->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(), $curve->convertInteger($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 $R . $S; + } + + $shortFormat = $format; + $format = self::validatePlugin('Signature', $format); + if ($format === false) { + return false; + } + + if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) { + $namedCurves = PKCS8::isUsingNamedCurves(); + + // 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 + PKCS8::useSpecifiedCurve(); + + $signature = ''; + // altho PHP's OpenSSL bindings only supported ECDSA key creation in PHP 7.1 they've long + // supported signing / verification + $result = openssl_sign($message, $signature, $this->getPrivateKey(), $this->hash->getHash()); + + if ($namedCurves) { + PKCS8::useNamedCurve(); + } + + 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(), $this->curve->convertInteger($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(), $this->curve->convertInteger($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); + } + + /** + * Verify a signature + * + * @see self::verify() + * @access public + * @param string $message + * @param string $format optional + * @return mixed + */ + public function verify($message, $signature, $format = 'ASN1') + { + if (!isset($this->QA)) { + if (!isset($this->dA)) { + throw new NoKeyLoadedException('No key has been loaded'); + } + throw new UnsupportedOperationException('A private key cannot be used to verify data'); + } + + $order = $this->curve->getOrder(); + + if ($this->curve instanceof TwistedEdwardsCurve) { + if ($this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context)) { + return sodium_crypto_sign_verify_detached($signature, $message, $this->getPublicKey('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(), $curve->convertInteger($S)); + $rhs = $curve->multiplyPoint($qa, $curve->convertInteger($k)); + $rhs = $curve->addPoint($rhs, $R); + $rhs = $curve->convertToAffine($rhs); + + return $lhs[0]->equals($rhs[0]) && $lhs[1]->equals($rhs[1]); + } + + $format = self::validatePlugin('Signature', $format); + if ($format === false) { + return false; + } + + $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())) { + $namedCurves = PKCS8::isUsingNamedCurves(); + + PKCS8::useSpecifiedCurve(); + + $sig = $format != 'ASN1' ? ASN1Signature::save($r, $s) : $signature; + + $result = openssl_verify($message, $sig, $this->getPublicKey(), $this->hash->getHash()); + + if ($namedCurves) { + PKCS8::useNamedCurve(); + } + + 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); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/BaseCurves/Base.php b/phpseclib/Crypt/ECDSA/BaseCurves/Base.php new file mode 100644 index 00000000..53cd59c5 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/BaseCurves/Base.php @@ -0,0 +1,220 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\BaseCurves; + +use phpseclib\Math\Common\FiniteField; +use phpseclib\Math\BigInteger; + +/** + * Base + * + * @package Prime + * @author Jim Wigginton + * @access public + */ +abstract class Base +{ + /** + * Doubles + * + * @var object[] + */ + protected $doubles; + + /** + * NAF Points + * + * @var int[] + */ + private $naf; + + /** + * The Order + * + * @var BigInteger + */ + protected $order; + + /** + * Finite Field Integer factory + * + * @var \phpseclib\Math\FiniteField\Integer + */ + protected $factory; + + /** + * Returns a random integer + * + * @return object + */ + public function randomInteger() + { + return $this->factory->randomInteger(); + } + + /** + * Converts a BigInteger to a FiniteField 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, FiniteField\Integer $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 FiniteField + */ + public function createRandomMultiplier() + { + static $one; + if (!isset($one)) { + $one = new BigInteger(1); + } + + $dA = BigInteger::randomRange($one, $this->order->subtract($one)); + return $this->factory->newInteger($dA); + } + + /** + * Sets the Order + */ + public function setOrder(BigInteger $order) + { + $this->order = $order; + } + + /** + * Returns the Order + * + * @return \phpseclib\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); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/BaseCurves/Binary.php b/phpseclib/Crypt/ECDSA/BaseCurves/Binary.php new file mode 100644 index 00000000..bbfeb8c1 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/BaseCurves/Binary.php @@ -0,0 +1,378 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\BaseCurves; + +use phpseclib\Common\Functions\Strings; +use phpseclib\Math\BinaryField; +use phpseclib\Math\BigInteger; +use phpseclib\Math\BinaryField\Integer as BinaryInteger; + +/** + * Curves over y^2 + x*y = x^3 + a*x^2 + b + * + * @package Binary + * @author Jim Wigginton + * @access public + */ +class Binary extends Base +{ + /** + * Binary Field Integer factory + * + * @var \phpseclib\Math\BinaryFields + */ + 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 \phpseclib\Math\BigInteger + */ + public function getModulo() + { + return $this->modulo; + } + + /** + * Returns the a coefficient + * + * @return \phpseclib\Math\PrimeField\Integer + */ + public function getA() + { + return $this->a; + } + + /** + * Returns the a coefficient + * + * @return \phpseclib\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 \phpseclib\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 \phpseclib\Math\PrimeField\Integer[] + */ + public function convertToInternal(array $p) + { + if (isset($p[2])) { + return $p; + } + + $p[2] = clone $this->one; + $p['fresh'] = true; + return $p; + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/BaseCurves/KoblitzPrime.php b/phpseclib/Crypt/ECDSA/BaseCurves/KoblitzPrime.php new file mode 100644 index 00000000..f6a680a0 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/BaseCurves/KoblitzPrime.php @@ -0,0 +1,324 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\BaseCurves; + +use phpseclib\Common\Functions\Strings; +use phpseclib\Math\PrimeField; +use phpseclib\Math\BigInteger; +use phpseclib\Math\PrimeField\Integer as PrimeInteger; + +/** + * Curves over y^2 = x^3 + b + * + * @package KoblitzPrime + * @author Jim Wigginton + * @access public + */ +class KoblitzPrime extends Prime +{ + // 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://git.io/vxbrP + * + * @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 $n + * @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] + ]; + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/BaseCurves/Prime.php b/phpseclib/Crypt/ECDSA/BaseCurves/Prime.php new file mode 100644 index 00000000..dde84ce7 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/BaseCurves/Prime.php @@ -0,0 +1,774 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\BaseCurves; + +use phpseclib\Math\Common\FiniteField\Integer; +use phpseclib\Common\Functions\Strings; +use phpseclib\Math\PrimeField; +use phpseclib\Math\BigInteger; +use phpseclib\Math\PrimeField\Integer as PrimeInteger; + +/** + * Curves over y^2 = x^3 + a*x + b + * + * @package Prime + * @author Jim Wigginton + * @access public + */ +class Prime extends Base +{ + /** + * Prime Field Integer factory + * + * @var \phpseclib\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 \phpseclib\Math\BigInteger + */ + public function getModulo() + { + return $this->modulo; + } + + /** + * Returns the a coefficient + * + * @return \phpseclib\Math\PrimeField\Integer + */ + public function getA() + { + return $this->a; + } + + /** + * Returns the a coefficient + * + * @return \phpseclib\Math\PrimeField\Integer + */ + public function getB() + { + return $this->b; + } + + /** + * Multiply and Add Points + * + * Adapted from https://git.io/vxPUH + * + * @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://git.io/vxY1f + * + * @return int[] + */ + private function getNAFPoints($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://git.io/vxrpD + * + * @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 \phpseclib\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 \phpseclib\Math\PrimeField\Integer[] + */ + public function convertToInternal(array $p) + { + if (isset($p[2])) { + return $p; + } + + $p[2] = clone $this->one; + $p['fresh'] = true; + return $p; + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/BaseCurves/TwistedEdwards.php b/phpseclib/Crypt/ECDSA/BaseCurves/TwistedEdwards.php new file mode 100644 index 00000000..4cd0d246 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/BaseCurves/TwistedEdwards.php @@ -0,0 +1,236 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\BaseCurves; + +use phpseclib\Math\PrimeField; +use phpseclib\Math\BigInteger; +use phpseclib\Math\PrimeField\Integer as PrimeInteger; + +/** + * Curves over a*x^2 + y^2 = 1 + d*x^2*y^2 + * + * @package Prime + * @author Jim Wigginton + * @access public + */ +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 \phpseclib\Math\PrimeField\Integer + */ + public function getA() + { + return $this->a; + } + + /** + * Returns the a coefficient + * + * @return \phpseclib\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 \phpseclib\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 \phpseclib\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); + } + + /** + * Tests whether or not the x / y values satisfy the equation + * + * @return boolean + */ + public function get(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); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/Ed25519.php b/phpseclib/Crypt/ECDSA/Curves/Ed25519.php new file mode 100644 index 00000000..837a0053 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/Ed25519.php @@ -0,0 +1,334 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\TwistedEdwards; +use phpseclib\Math\BigInteger; +use phpseclib\Crypt\Hash; +use phpseclib\Crypt\Random; + +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 ECDSA\Keys\Common.php + * + * @param BigInteger $x + * @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 \phpseclib\Math\PrimeField\Integer + */ + 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); + $dA = $this->factory->newInteger($dA); + + $dA->secret = $str; + return $dA; + } + + /** + * Encode a point as a string + * + * @param string $str + * @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 \phpseclib\Math\PrimeField\Integer + */ + public function createRandomMultiplier() + { + return $this->extractSecret(Random::string(32)); + } + + /** + * 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 \phpseclib\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]; + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/Ed448.php b/phpseclib/Crypt/ECDSA/Curves/Ed448.php new file mode 100644 index 00000000..554c5d03 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/Ed448.php @@ -0,0 +1,267 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\TwistedEdwards; +use phpseclib\Math\BigInteger; +use phpseclib\Crypt\Hash; +use phpseclib\Crypt\Random; + +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 ECDSA\Keys\Common.php + * + * @param BigInteger $x + * @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 \phpseclib\Math\PrimeField\Integer + */ + 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); + $dA = $this->factory->newInteger($dA); + + $dA->secret = $str; + return $dA; + } + + /** + * Encode a point as a string + * + * @param string $str + * @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 \phpseclib\Math\PrimeField\Integer + */ + public function createRandomMultiplier() + { + return $this->extractSecret(Random::string(57)); + } + + /** + * 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 \phpseclib\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]; + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/brainpoolP160r1.php b/phpseclib/Crypt/ECDSA/Curves/brainpoolP160r1.php new file mode 100644 index 00000000..bc07369b --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/brainpoolP160r1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/brainpoolP160t1.php b/phpseclib/Crypt/ECDSA/Curves/brainpoolP160t1.php new file mode 100644 index 00000000..55ac37fa --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/brainpoolP160t1.php @@ -0,0 +1,49 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/brainpoolP192r1.php b/phpseclib/Crypt/ECDSA/Curves/brainpoolP192r1.php new file mode 100644 index 00000000..c6308d4a --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/brainpoolP192r1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/brainpoolP192t1.php b/phpseclib/Crypt/ECDSA/Curves/brainpoolP192t1.php new file mode 100644 index 00000000..89d8f0c2 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/brainpoolP192t1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/brainpoolP224r1.php b/phpseclib/Crypt/ECDSA/Curves/brainpoolP224r1.php new file mode 100644 index 00000000..d74bddcc --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/brainpoolP224r1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/brainpoolP224t1.php b/phpseclib/Crypt/ECDSA/Curves/brainpoolP224t1.php new file mode 100644 index 00000000..7293b693 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/brainpoolP224t1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/brainpoolP256r1.php b/phpseclib/Crypt/ECDSA/Curves/brainpoolP256r1.php new file mode 100644 index 00000000..f9307800 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/brainpoolP256r1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/brainpoolP256t1.php b/phpseclib/Crypt/ECDSA/Curves/brainpoolP256t1.php new file mode 100644 index 00000000..deef2eba --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/brainpoolP256t1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} diff --git a/phpseclib/Crypt/ECDSA/Curves/brainpoolP320r1.php b/phpseclib/Crypt/ECDSA/Curves/brainpoolP320r1.php new file mode 100644 index 00000000..d9469a06 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/brainpoolP320r1.php @@ -0,0 +1,42 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/brainpoolP320t1.php b/phpseclib/Crypt/ECDSA/Curves/brainpoolP320t1.php new file mode 100644 index 00000000..54bb626a --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/brainpoolP320t1.php @@ -0,0 +1,42 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/brainpoolP384r1.php b/phpseclib/Crypt/ECDSA/Curves/brainpoolP384r1.php new file mode 100644 index 00000000..534f1c67 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/brainpoolP384r1.php @@ -0,0 +1,48 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/brainpoolP384t1.php b/phpseclib/Crypt/ECDSA/Curves/brainpoolP384t1.php new file mode 100644 index 00000000..85e1885a --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/brainpoolP384t1.php @@ -0,0 +1,48 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/brainpoolP512r1.php b/phpseclib/Crypt/ECDSA/Curves/brainpoolP512r1.php new file mode 100644 index 00000000..140f6205 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/brainpoolP512r1.php @@ -0,0 +1,48 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/brainpoolP512t1.php b/phpseclib/Crypt/ECDSA/Curves/brainpoolP512t1.php new file mode 100644 index 00000000..50432b07 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/brainpoolP512t1.php @@ -0,0 +1,48 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/nistb233.php b/phpseclib/Crypt/ECDSA/Curves/nistb233.php new file mode 100644 index 00000000..db93276e --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/nistb233.php @@ -0,0 +1,20 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +final class nistb233 extends sect233r1 +{ +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/nistb409.php b/phpseclib/Crypt/ECDSA/Curves/nistb409.php new file mode 100644 index 00000000..79e1f2a9 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/nistb409.php @@ -0,0 +1,20 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +final class nistb409 extends sect409r1 +{ +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/nistk163.php b/phpseclib/Crypt/ECDSA/Curves/nistk163.php new file mode 100644 index 00000000..57bd4950 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/nistk163.php @@ -0,0 +1,20 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +final class nistk163 extends sect163k1 +{ +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/nistk233.php b/phpseclib/Crypt/ECDSA/Curves/nistk233.php new file mode 100644 index 00000000..a6b1ab80 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/nistk233.php @@ -0,0 +1,20 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +final class nistk233 extends sect233k1 +{ +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/nistk283.php b/phpseclib/Crypt/ECDSA/Curves/nistk283.php new file mode 100644 index 00000000..c4ce0f92 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/nistk283.php @@ -0,0 +1,20 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +final class nistk283 extends sect283k1 +{ +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/nistk409.php b/phpseclib/Crypt/ECDSA/Curves/nistk409.php new file mode 100644 index 00000000..03a56cb5 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/nistk409.php @@ -0,0 +1,20 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +final class nistk409 extends sect409k1 +{ +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/nistp192.php b/phpseclib/Crypt/ECDSA/Curves/nistp192.php new file mode 100644 index 00000000..bcb9c7dd --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/nistp192.php @@ -0,0 +1,20 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +final class nistp192 extends secp192r1 +{ +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/nistp224.php b/phpseclib/Crypt/ECDSA/Curves/nistp224.php new file mode 100644 index 00000000..e7598e8a --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/nistp224.php @@ -0,0 +1,20 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +final class nistp224 extends secp224r1 +{ +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/nistp256.php b/phpseclib/Crypt/ECDSA/Curves/nistp256.php new file mode 100644 index 00000000..73613bcb --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/nistp256.php @@ -0,0 +1,20 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +final class nistp256 extends secp256r1 +{ +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/nistp384.php b/phpseclib/Crypt/ECDSA/Curves/nistp384.php new file mode 100644 index 00000000..a30e88ee --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/nistp384.php @@ -0,0 +1,20 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +final class nistp384 extends secp384r1 +{ +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/nistp521.php b/phpseclib/Crypt/ECDSA/Curves/nistp521.php new file mode 100644 index 00000000..22a53758 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/nistp521.php @@ -0,0 +1,20 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +final class nistp521 extends secp521r1 +{ +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/nistt571.php b/phpseclib/Crypt/ECDSA/Curves/nistt571.php new file mode 100644 index 00000000..9f30686b --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/nistt571.php @@ -0,0 +1,20 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +final class nistt571 extends sect571k1 +{ +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/prime192v1.php b/phpseclib/Crypt/ECDSA/Curves/prime192v1.php new file mode 100644 index 00000000..4a5ef8a6 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/prime192v1.php @@ -0,0 +1,20 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +final class prime192v1 extends secp192r1 +{ +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/prime192v2.php b/phpseclib/Crypt/ECDSA/Curves/prime192v2.php new file mode 100644 index 00000000..f3aa260f --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/prime192v2.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/prime192v3.php b/phpseclib/Crypt/ECDSA/Curves/prime192v3.php new file mode 100644 index 00000000..4b0855d4 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/prime192v3.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/prime239v1.php b/phpseclib/Crypt/ECDSA/Curves/prime239v1.php new file mode 100644 index 00000000..2ee69197 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/prime239v1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/prime239v2.php b/phpseclib/Crypt/ECDSA/Curves/prime239v2.php new file mode 100644 index 00000000..4d32a2b6 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/prime239v2.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/prime239v3.php b/phpseclib/Crypt/ECDSA/Curves/prime239v3.php new file mode 100644 index 00000000..0afbfee4 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/prime239v3.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/prime256v1.php b/phpseclib/Crypt/ECDSA/Curves/prime256v1.php new file mode 100644 index 00000000..29fb1ab4 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/prime256v1.php @@ -0,0 +1,20 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +final class prime256v1 extends secp256r1 +{ +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/secp112r1.php b/phpseclib/Crypt/ECDSA/Curves/secp112r1.php new file mode 100644 index 00000000..dfbabe89 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/secp112r1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/secp112r2.php b/phpseclib/Crypt/ECDSA/Curves/secp112r2.php new file mode 100644 index 00000000..f536a02b --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/secp112r2.php @@ -0,0 +1,37 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/secp128r1.php b/phpseclib/Crypt/ECDSA/Curves/secp128r1.php new file mode 100644 index 00000000..b73eec7f --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/secp128r1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/secp128r2.php b/phpseclib/Crypt/ECDSA/Curves/secp128r2.php new file mode 100644 index 00000000..18d92b0d --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/secp128r2.php @@ -0,0 +1,37 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/secp160k1.php b/phpseclib/Crypt/ECDSA/Curves/secp160k1.php new file mode 100644 index 00000000..41acc4e7 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/secp160k1.php @@ -0,0 +1,48 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\KoblitzPrime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/secp160r1.php b/phpseclib/Crypt/ECDSA/Curves/secp160r1.php new file mode 100644 index 00000000..175f5e8b --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/secp160r1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/secp160r2.php b/phpseclib/Crypt/ECDSA/Curves/secp160r2.php new file mode 100644 index 00000000..fcf9591a --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/secp160r2.php @@ -0,0 +1,37 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/secp192k1.php b/phpseclib/Crypt/ECDSA/Curves/secp192k1.php new file mode 100644 index 00000000..acc10f59 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/secp192k1.php @@ -0,0 +1,47 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\KoblitzPrime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/secp192r1.php b/phpseclib/Crypt/ECDSA/Curves/secp192r1.php new file mode 100644 index 00000000..48ad6dcb --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/secp192r1.php @@ -0,0 +1,80 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/secp224k1.php b/phpseclib/Crypt/ECDSA/Curves/secp224k1.php new file mode 100644 index 00000000..e590ea0d --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/secp224k1.php @@ -0,0 +1,47 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\KoblitzPrime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/secp224r1.php b/phpseclib/Crypt/ECDSA/Curves/secp224r1.php new file mode 100644 index 00000000..21125546 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/secp224r1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} diff --git a/phpseclib/Crypt/ECDSA/Curves/secp256k1.php b/phpseclib/Crypt/ECDSA/Curves/secp256k1.php new file mode 100644 index 00000000..3d90d35f --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/secp256k1.php @@ -0,0 +1,51 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +//use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\Crypt\ECDSA\BaseCurves\KoblitzPrime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/secp256r1.php b/phpseclib/Crypt/ECDSA/Curves/secp256r1.php new file mode 100644 index 00000000..67e162d8 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/secp256r1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} diff --git a/phpseclib/Crypt/ECDSA/Curves/secp384r1.php b/phpseclib/Crypt/ECDSA/Curves/secp384r1.php new file mode 100644 index 00000000..f30bf24f --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/secp384r1.php @@ -0,0 +1,54 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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 + )); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/secp521r1.php b/phpseclib/Crypt/ECDSA/Curves/secp521r1.php new file mode 100644 index 00000000..f37d59ef --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/secp521r1.php @@ -0,0 +1,48 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Prime; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/sect113r1.php b/phpseclib/Crypt/ECDSA/Curves/sect113r1.php new file mode 100644 index 00000000..992b5945 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/sect113r1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Binary; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/sect113r2.php b/phpseclib/Crypt/ECDSA/Curves/sect113r2.php new file mode 100644 index 00000000..140b94f3 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/sect113r2.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Binary; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/sect131r1.php b/phpseclib/Crypt/ECDSA/Curves/sect131r1.php new file mode 100644 index 00000000..ba41413a --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/sect131r1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Binary; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/sect131r2.php b/phpseclib/Crypt/ECDSA/Curves/sect131r2.php new file mode 100644 index 00000000..8b1d88bf --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/sect131r2.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Binary; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/sect163k1.php b/phpseclib/Crypt/ECDSA/Curves/sect163k1.php new file mode 100644 index 00000000..562eb144 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/sect163k1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Binary; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/sect163r1.php b/phpseclib/Crypt/ECDSA/Curves/sect163r1.php new file mode 100644 index 00000000..634e300c --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/sect163r1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Binary; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/sect163r2.php b/phpseclib/Crypt/ECDSA/Curves/sect163r2.php new file mode 100644 index 00000000..87ec5afc --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/sect163r2.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Binary; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/sect193r1.php b/phpseclib/Crypt/ECDSA/Curves/sect193r1.php new file mode 100644 index 00000000..e424229c --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/sect193r1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Binary; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/sect193r2.php b/phpseclib/Crypt/ECDSA/Curves/sect193r2.php new file mode 100644 index 00000000..334d86db --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/sect193r2.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Binary; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/sect233k1.php b/phpseclib/Crypt/ECDSA/Curves/sect233k1.php new file mode 100644 index 00000000..1e171f46 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/sect233k1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Binary; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/sect233r1.php b/phpseclib/Crypt/ECDSA/Curves/sect233r1.php new file mode 100644 index 00000000..066e7865 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/sect233r1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Binary; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/sect239k1.php b/phpseclib/Crypt/ECDSA/Curves/sect239k1.php new file mode 100644 index 00000000..92e00f6a --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/sect239k1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Binary; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/sect283k1.php b/phpseclib/Crypt/ECDSA/Curves/sect283k1.php new file mode 100644 index 00000000..b4408009 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/sect283k1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Binary; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/sect283r1.php b/phpseclib/Crypt/ECDSA/Curves/sect283r1.php new file mode 100644 index 00000000..090a6401 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/sect283r1.php @@ -0,0 +1,36 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Binary; +use phpseclib\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)); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/sect409k1.php b/phpseclib/Crypt/ECDSA/Curves/sect409k1.php new file mode 100644 index 00000000..bdc779c9 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/sect409k1.php @@ -0,0 +1,39 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Binary; +use phpseclib\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 + )); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/sect409r1.php b/phpseclib/Crypt/ECDSA/Curves/sect409r1.php new file mode 100644 index 00000000..86f5999d --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/sect409r1.php @@ -0,0 +1,39 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Binary; +use phpseclib\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 + )); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/sect571k1.php b/phpseclib/Crypt/ECDSA/Curves/sect571k1.php new file mode 100644 index 00000000..77a112c0 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/sect571k1.php @@ -0,0 +1,43 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Binary; +use phpseclib\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 + )); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Curves/sect571r1.php b/phpseclib/Crypt/ECDSA/Curves/sect571r1.php new file mode 100644 index 00000000..020d4d77 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Curves/sect571r1.php @@ -0,0 +1,43 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\ECDSA\Curves; + +use phpseclib\Crypt\ECDSA\BaseCurves\Binary; +use phpseclib\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 + )); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Keys/Common.php b/phpseclib/Crypt/ECDSA/Keys/Common.php new file mode 100644 index 00000000..196a3348 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Keys/Common.php @@ -0,0 +1,558 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt\ECDSA\Keys; + +use ParagonIE\ConstantTime\Hex; +use phpseclib\Crypt\ECDSA\BaseCurves\Base as BaseCurve; +use phpseclib\Crypt\ECDSA\BaseCurves\Prime as PrimeCurve; +use phpseclib\Crypt\ECDSA\BaseCurves\Binary as BinaryCurve; +use phpseclib\Crypt\ECDSA\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; +use phpseclib\Common\Functions\Strings; +use phpseclib\Math\BigInteger; +use phpseclib\Math\PrimeField; +use phpseclib\File\ASN1; +use phpseclib\File\ASN1\Maps; +use phpseclib\Exception\UnsupportedCurveException; + +/** + * Generic ECDSA Key Parsing Helper functions + * + * @package ECDSA + * @author Jim Wigginton + * @access public + */ +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 = [ + // 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 + */ + + '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 + + // 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 \phpseclib\Crypt\ECDSA\BaseCurves\Base $curve + */ + public static function setImplicitCurve(BaseCurve $curve) + { + self::$implicitCurve = $curve; + } + + /** + * Returns an instance of \phpseclib\Crypt\ECDSA\BaseCurves\Base based + * on the curve parameters + * + * @param array $params + * @return \phpseclib\Crypt\ECDSA\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 = '\phpseclib\Crypt\ECDSA\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( + Hex::encode($data['curve']['a']), + Hex::encode($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 \phpseclib\Crypt\ECDSA\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 ' . Hex::encode($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 ' . Hex::encode($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 \phpseclib\Crypt\ECDSA\BaseCurves\Base $curve + * @param bool $returnArray + * @return string|false + */ + private static function encodeParameters(BaseCurve $curve, $returnArray = false) + { + $reflect = new \ReflectionClass($curve); + $name = $reflect->getShortName(); + if (isset(self::$curveOIDs[$name]) && self::$useNamedCurves) { + return $returnArray ? + ['namedCurve' => $name] : + ASN1::encodeDER(['namedCurve' => $name], Maps\ECParameters::MAP); + } + if (self::$useNamedCurves) { + foreach (new \DirectoryIterator(__DIR__ . '/../Curves/') as $file) { + if ($file->getExtension() != 'php') { + continue; + } + $testName = $file->getBasename('.php'); + $class = 'phpseclib\Crypt\ECDSA\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 ECDSA 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; + } + + /** + * Returns true if named curves are being used by default + * + * If a named curve is not being used by default than specified curves are being utilized + */ + public static function isUsingNamedCurves() + { + return self::$useNamedCurves; + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/ECDSA/Keys/OpenSSH.php b/phpseclib/Crypt/ECDSA/Keys/OpenSSH.php new file mode 100644 index 00000000..7027acd8 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Keys/OpenSSH.php @@ -0,0 +1,275 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt\ECDSA\Keys; + +use ParagonIE\ConstantTime\Base64; +use phpseclib\Math\BigInteger; +use phpseclib\Common\Functions\Strings; +use phpseclib\Crypt\Common\Keys\OpenSSH as Progenitor; +use phpseclib\Crypt\ECDSA\BaseCurves\Base as BaseCurve; +use phpseclib\Exception\UnsupportedCurveException; +use phpseclib\Crypt\ECDSA\Curves\Ed25519; +use phpseclib\Math\Common\FiniteField\Integer; +use phpseclib\Crypt\Random; + +/** + * OpenSSH Formatted ECDSA Key Handler + * + * @package ECDSA + * @author Jim Wigginton + * @access public + */ +abstract class OpenSSH extends Progenitor +{ + use Common; + + /** + * Supported Key Types + * + * @var array + */ + private static $types = [ + 'ecdsa-sha2-nistp256', + 'ecdsa-sha2-nistp384', + 'ecdsa-sha2-nistp521', + 'ssh-ed25519' + ]; + + /** + * Break a public or private key down into its constituent components + * + * @access public + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + /* + key format is described here: + https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?annotate=HEAD + + this is only supported for ECDSA because of Ed25519. ssh-keygen doesn't generate a + PKCS1/8 formatted private key for Ed25519 - it generates an OpenSSH formatted + private key. probably because, at the time of this writing, there's not an actual + IETF RFC describing an Ed25519 format + */ + if (strpos($key, 'BEGIN OPENSSH PRIVATE KEY') !== false) { + $key = preg_replace('#(?:^-.*?-[\r\n]*$)|\s#ms', '', $key); + $key = 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) { + throw new \RuntimeException('Although the OpenSSH private key format supports multiple keys phpseclib does not'); + } + if (strlen($kdfoptions) || $kdfname != 'none' || $ciphername != 'none') { + /* + OpenSSH private keys use a customized version of bcrypt. specifically, instead of encrypting + OrpheanBeholderScryDoubt 64 times OpenSSH's bcrypt variant encrypts + OxychromaticBlowfishSwatDynamite 64 times. so we can't use crypt(). + + bcrypt is basically Blowfish with an altered key expansion. whereas Blowfish just runs the + key through the key expansion bcrypt interleaves the key expansion with the salt and + password. this renders openssl / mcrypt unusuable. this forces us to use a pure-PHP implementation + of bcrypt. the problem with that is that pure-PHP is too slow to be practically useful. + + in addition to encrypting a different string 64 times the OpenSSH also performs bcrypt from + scratch $rounds times. calling crypt() 64x with bcrypt takes 0.7s. PHP is going to be naturally + slower. pure-PHP is 215x slower than OpenSSL for AES and pure-PHP is 43x slower for bcrypt. + 43 * 0.7 = 30s. no one wants to wait 30s to load a private key. + + another way to think about this.. according to wikipedia's article on Blowfish, + "Each new key requires pre-processing equivalent to encrypting about 4 kilobytes of text". + key expansion is done (9+64*2)*160 times. multiply that by 4 and it turns out that Blowfish, + OpenSSH style, is the equivalent of encrypting ~80mb of text. + + more supporting evidence: sodium_compat does not implement Argon2 (another password hashing + algorithm) because "It's not feasible to polyfill scrypt or Argon2 into PHP and get reasonable + performance. Users would feel motivated to select parameters that downgrade security to avoid + denial of service (DoS) attacks. The only winning move is not to play" + -- https://github.com/paragonie/sodium_compat/blob/master/README.md + */ + throw new \RuntimeException('Encrypted OpenSSH private keys are not supported'); + //list($salt, $rounds) = Strings::unpackSSH2('sN', $kdfoptions); + } + + list($publicKey, $paddedKey) = Strings::unpackSSH2('ss', $key); + list($type, $publicKey) = Strings::unpackSSH2('ss', $publicKey); + if ($type != 'ssh-ed25519') { + throw new UnsupportedCurveException('ssh-ed25519 is the only supported curve for OpenSSH public keys'); + } + list($checkint1, $checkint2, $type, $publicKey2, $privateKey, $comment) = Strings::unpackSSH2('NNssss', $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'); + } + if ($type != 'ssh-ed25519') { + throw new UnsupportedCurveException('ssh-ed25519 is the only supported curve for OpenSSH private keys'); + } + if ($publicKey != $publicKey2 || $publicKey2 != substr($privateKey, 32)) { + throw new \RuntimeException('The public keys do not match up'); + } + $privateKey = substr($privateKey, 0, 32); + $curve = new Ed25519(); + return [ + 'curve' => $curve, + 'dA' => $curve->extractSecret($privateKey), + 'QA' => self::extractPoint($publicKey, $curve), + 'comment' => $comment + ]; + } + + $parts = explode(' ', $key, 3); + + if (!isset($parts[1])) { + $key = Base64::decode($parts[0]); + $comment = isset($parts[1]) ? $parts[1] : false; + } else { + $asciiType = $parts[0]; + if (!in_array($asciiType, self::$types)) { + throw new \RuntimeException('Keys of type ' . $asciiType . ' are not supported'); + } + $key = Base64::decode($parts[1]); + $comment = isset($parts[2]) ? $parts[2] : false; + } + + list($binaryType) = Strings::unpackSSH2('s', $key); + if (isset($asciiType) && $asciiType != $binaryType) { + throw new \RuntimeException('Two different types of keys are claimed: ' . $asciiType . ' and ' . $binaryType); + } elseif (!isset($asciiType) && !in_array($binaryType, self::$types)) { + throw new \RuntimeException('Keys of type ' . $binaryType . ' are not supported'); + } + + if ($binaryType == 'ssh-ed25519') { + if (Strings::shift($key, 4) != "\0\0\0\x20") { + throw new \RuntimeException('Length of ssh-ed25519 key should be 32'); + } + + $curve = new Ed25519(); + $qa = self::extractPoint($key, $curve); + } else { + list($curveName, $publicKey) = Strings::unpackSSH2('ss', $key); + $curveName = '\phpseclib\Crypt\ECDSA\Curves\\' . $curveName; + $curve = new $curveName(); + + $qa = self::extractPoint("\0" . $publicKey, $curve); + } + + return [ + 'curve' => $curve, + 'QA' => $qa, + 'comment' => $comment + ]; + } + + /** + * Convert an ECDSA public key to the appropriate format + * + * @access public + * @param \phpseclib\Crypt\ECDSA\BaseCurves\Base $curve + * @param \phpseclib\Math\Common\FiniteField\Integer[] $publicKey + * @return string + */ + public static function savePublicKey(BaseCurve $curve, array $publicKey) + { + if ($curve instanceof Ed25519) { + $key = Strings::packSSH2('ss', 'ssh-ed25519', $curve->encodePoint($publicKey)); + $key = 'ssh-ed25519 ' . Base64::encode($key) . ' ' . self::$comment; + return $key; + } + + 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'); + } + + $points = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); + $key = Strings::packSSH2('sss', 'ecdsa-sha2-' . $alias, $alias, $points); + + if (self::$binary) { + return $key; + } + + $key = 'ecdsa-sha2-' . $alias . ' ' . Base64::encode($key) . ' ' . self::$comment; + + return $key; + } + + /** + * Convert a private key to the appropriate format. + * + * @access public + * @param \phpseclib\Math\Common\FiniteField\Integer $privateKey + * @param \phpseclib\Crypt\ECDSA\Curves\Ed25519 $curve + * @param \phpseclib\Math\Common\FiniteField\Integer[] $publicKey + * @param string $password optional + * @return string + */ + public static function savePrivateKey(Integer $privateKey, Ed25519 $curve, array $publicKey, $password = '') + { + if (!isset($privateKey->secret)) { + throw new \RuntimeException('Private Key does not have a secret set'); + } + if (strlen($privateKey->secret) != 32) { + throw new \RuntimeException('Private Key secret is not of the correct length'); + } + + list(, $checkint) = unpack('N', Random::string(4)); + $pubKey = $curve->encodePoint($publicKey); + + $publicKey = Strings::packSSH2('ss', 'ssh-ed25519', $pubKey); + $paddedKey = Strings::packSSH2('NNssss', $checkint, $checkint, 'ssh-ed25519', $pubKey, $privateKey->secret . $pubKey, self::$comment); + /* + 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. + */ + $paddingLength = (7 * strlen($paddedKey)) % 8; + for ($i = 1; $i <= $paddingLength; $i++) { + $paddedKey.= chr($i); + } + $key = Strings::packSSH2('sssNss', 'none', 'none', '', 1, $publicKey, $paddedKey); + $key = "openssh-key-v1\0$key"; + + return "-----BEGIN OPENSSH PRIVATE KEY-----\r\n" . + chunk_split(Base64::encode($key), 70) . + "-----END OPENSSH PRIVATE KEY-----"; + } +} diff --git a/phpseclib/Crypt/ECDSA/Keys/PKCS1.php b/phpseclib/Crypt/ECDSA/Keys/PKCS1.php new file mode 100644 index 00000000..aebc84eb --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Keys/PKCS1.php @@ -0,0 +1,141 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt\ECDSA\Keys; + +use phpseclib\Math\Common\FiniteField\Integer; +use phpseclib\Crypt\Common\Keys\PKCS1 as Progenitor; +use phpseclib\File\ASN1; +use phpseclib\File\ASN1\Maps; +use phpseclib\Crypt\ECDSA\BaseCurves\Base as BaseCurve; +use phpseclib\Math\BigInteger; +use ParagonIE\ConstantTime\Base64; +use phpseclib\Crypt\ECDSA\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; +use phpseclib\Exception\UnsupportedCurveException; + +/** + * "PKCS1" (RFC5915) Formatted ECDSA Key Handler + * + * @package ECDSA + * @author Jim Wigginton + * @access public + */ +abstract class PKCS1 extends Progenitor +{ + use Common; + + /** + * Break a public or private key down into its constituent components + * + * @access public + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + self::initialize_static_variables(); + + $key = parent::load($key, $password); + + $decoded = ASN1::decodeBER($key); + if (empty($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'); + } + + $components = []; + $components['curve'] = self::loadCurveByParam($key['parameters']); + $temp = new BigInteger($key['privateKey'], 256); + $components['dA'] = $components['curve']->convertInteger($temp); + $components['QA'] = self::extractPoint($key['publicKey'], $components['curve']); + + return $components; + } + + /** + * Convert ECDSA parameters to the appropriate format + * + * @access public + * @return string + */ + public static function saveParameters(BaseCurve $curve) + { + self::initialize_static_variables(); + + if ($curve instanceof TwistedEdwardsCurve) { + throw new UnsupportedCurveException('TwistedEdwards Curves are not supported'); + } + + $key = self::encodeParameters($curve); + + return "-----BEGIN EC PARAMETERS-----\r\n" . + chunk_split(Base64::encode($key), 64) . + "-----END EC PARAMETERS-----\r\n"; + } + + /** + * Convert a private key to the appropriate format. + * + * @access public + * @param \phpseclib\Math\Common\FiniteField\Integer $privateKey + * @param \phpseclib\Crypt\ECDSA\BaseCurves\Base $curve + * @param \phpseclib\Math\Common\FiniteField\Integer[] $publicKey + * @param string $password optional + * @return string + */ + public static function savePrivateKey(Integer $privateKey, BaseCurve $curve, array $publicKey, $password = '') + { + self::initialize_static_variables(); + + if ($curve instanceof TwistedEdwardsCurve) { + 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); + } +} diff --git a/phpseclib/Crypt/ECDSA/Keys/PKCS8.php b/phpseclib/Crypt/ECDSA/Keys/PKCS8.php new file mode 100644 index 00000000..89f31671 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Keys/PKCS8.php @@ -0,0 +1,233 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt\ECDSA\Keys; + +use phpseclib\Math\BigInteger; +use phpseclib\Crypt\Common\Keys\PKCS8 as Progenitor; +use phpseclib\File\ASN1; +use phpseclib\File\ASN1\Maps; +use phpseclib\Crypt\ECDSA\BaseCurves\Base as BaseCurve; +use phpseclib\Crypt\ECDSA\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; +use phpseclib\Math\Common\FiniteField\Integer; +use phpseclib\Crypt\ECDSA\Curves\Ed25519; +use phpseclib\Crypt\ECDSA\Curves\Ed448; + +/** + * PKCS#8 Formatted ECDSA Key Handler + * + * @package ECDSA + * @author Jim Wigginton + * @access public + */ +abstract class PKCS8 extends Progenitor +{ + use Common; + + /** + * OID Name + * + * @var array + * @access private + */ + const OID_NAME = ['id-ecPublicKey', 'id-Ed25519', 'id-Ed448']; + + /** + * OID Value + * + * @var string + * @access private + */ + 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 + * + * @access public + * @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(); + + if (!is_string($key)) { + throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); + } + + $isPublic = strpos($key, 'PUBLIC') !== false; + + $key = parent::load($key, $password); + + $type = isset($key['privateKey']) ? 'privateKey' : 'publicKey'; + + switch (true) { + case !$isPublic && $type == 'publicKey': + throw new \UnexpectedValueException('Human readable string claims non-public key but DER encoded string claims public key'); + case $isPublic && $type == 'privateKey': + throw new \UnexpectedValueException('Human readable string claims public key but DER encoded string claims private key'); + } + + switch ($key[$type . 'Algorithm']['algorithm']) { + case 'id-Ed25519': + case 'id-Ed448': + return self::loadEdDSA($key); + } + + $decoded = ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element); + $params = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP); + + $components = []; + $components['curve'] = self::loadCurveByParam($params); + + if ($isPublic) { + $components['QA'] = self::extractPoint("\0" . $key['publicKey'], $components['curve']); + + return $components; + } + + $decoded = ASN1::decodeBER($key['privateKey']); + $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'); + } + + $temp = new BigInteger($key['privateKey'], 256); + $components['dA'] = $components['curve']->convertInteger($temp); + + $components['QA'] = self::extractPoint($key['publicKey'], $components['curve']); + + 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'); + } + $components['dA'] = $components['curve']->extractSecret(substr($key['privateKey'], 2)); + } + + if (isset($key['publicKey'])) { + if (!isset($components['curve'])) { + $components['curve'] = $key['publicKeyAlgorithm']['algorithm'] == 'id-Ed25519' ? new Ed25519() : new Ed448(); + } + + $components['QA'] = self::extractPoint(substr($key['publicKey'], 1), $components['curve']); + } + + if (isset($key['privateKey']) && !isset($components['QA'])) { + $components['QA'] = $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']); + } + + return $components; + } + + /** + * Convert an ECDSA public key to the appropriate format + * + * @access public + * @param \phpseclib\Crypt\ECDSA\BaseCurves\Base $curve + * @param \phpseclib\Math\Common\FiniteField\Integer[] $publicKey + * @return string + */ + public static function savePublicKey(BaseCurve $curve, array $publicKey) + { + self::initialize_static_variables(); + + 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)); + + $key = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); + + return self::wrapPublicKey($key, $params, 'id-ecPublicKey'); + } + + /** + * Convert a private key to the appropriate format. + * + * @access public + * @param \phpseclib\Math\Common\FiniteField\Integer $privateKey + * @param \phpseclib\Crypt\ECDSA\BaseCurves\Base $curve + * @param \phpseclib\Math\Common\FiniteField\Integer[] $publicKey + * @param string $password optional + * @return string + */ + public static function savePrivateKey(Integer $privateKey, BaseCurve $curve, array $publicKey, $password = '') + { + self::initialize_static_variables(); + + if ($curve instanceof TwistedEdwardsCurve) { + return self::wrapPrivateKey( + "\x04\x20" . $privateKey->secret, + [], + null, + $password, + $curve instanceof Ed25519 ? 'id-Ed25519' : 'id-Ed448', + $curve->encodePoint($publicKey) + ); + } + + $publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); + + $params = new ASN1\Element(self::encodeParameters($curve)); + + $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'); + } +} diff --git a/phpseclib/Crypt/ECDSA/Keys/PuTTY.php b/phpseclib/Crypt/ECDSA/Keys/PuTTY.php new file mode 100644 index 00000000..ce4eb563 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Keys/PuTTY.php @@ -0,0 +1,145 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt\ECDSA\Keys; + +use ParagonIE\ConstantTime\Base64; +use phpseclib\Math\BigInteger; +use phpseclib\Common\Functions\Strings; +use phpseclib\Crypt\Common\Keys\PuTTY as Progenitor; +use phpseclib\Crypt\ECDSA\BaseCurves\Base as BaseCurve; +use phpseclib\Math\Common\FiniteField\Integer; +use phpseclib\Crypt\ECDSA\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; + +/** + * PuTTY Formatted ECDSA Key Handler + * + * @package ECDSA + * @author Jim Wigginton + * @access public + */ +abstract class PuTTY extends Progenitor +{ + use Common; + + /** + * Public Handler + * + * @var string + * @access private + */ + const PUBLIC_HANDLER = 'phpseclib\Crypt\ECDSA\Keys\OpenSSH'; + + /** + * Supported Key Types + * + * @var array + * @access private + */ + 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 + * + * @access public + * @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 = 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'); + } + $components['dA'] = $components['curve']->extractSecret($private); + } else { + list($temp) = Strings::unpackSSH2('i', $private); + $components['dA'] = $components['curve']->convertInteger($temp); + } + + return $components; + } + + /** + * Convert a private key to the appropriate format. + * + * @access public + * @param \phpseclib\Math\Common\FiniteField\Integer $privateKey + * @param \phpseclib\Crypt\ECDSA\BaseCurves\Base $curve + * @param \phpseclib\Math\Common\FiniteField\Integer[] $publicKey + * @param string $password optional + * @return string + */ + public static function savePrivateKey(Integer $privateKey, BaseCurve $curve, array $publicKey, $password = false) + { + self::initialize_static_variables(); + + $public = explode(' ', OpenSSH::savePublicKey($curve, $publicKey)); + $name = $public[0]; + $public = 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', $privateKey->secret) : + Strings::packSSH2('s', $private); + + return self::wrapPrivateKey($public, $private, $name, $password); + } + + /** + * Convert an ECDSA public key to the appropriate format + * + * @access public + * @param \phpseclib\Crypt\ECDSA\BaseCurves\Base $curve + * @param \phpseclib\Math\Common\FiniteField[] $publicKey + * @return string + */ + public static function savePublicKey(BaseCurve $curve, array $publicKey) + { + $public = explode(' ', OpenSSH::savePublicKey($curve, $publicKey)); + $type = $public[0]; + $public = Base64::decode($public[1]); + list(, $length) = unpack('N', Strings::shift($public, 4)); + Strings::shift($public, $length); + + return self::wrapPublicKey($public, $type); + } +} diff --git a/phpseclib/Crypt/ECDSA/Keys/XML.php b/phpseclib/Crypt/ECDSA/Keys/XML.php new file mode 100644 index 00000000..b846b845 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Keys/XML.php @@ -0,0 +1,480 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt\ECDSA\Keys; + +use ParagonIE\ConstantTime\Base64; +use phpseclib\Math\BigInteger; +use phpseclib\Crypt\ECDSA\BaseCurves\Base as BaseCurve; +use phpseclib\Crypt\ECDSA\BaseCurves\Prime as PrimeCurve; +use phpseclib\Crypt\ECDSA\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; +use phpseclib\Exception\UnsupportedCurveException; + +/** + * XML Formatted ECDSA Key Handler + * + * @package ECDSA + * @author Jim Wigginton + * @access public + */ +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 + * + * @access public + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + self::initialize_static_variables(); + + if (!is_string($key)) { + throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); + } + + $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) != '' . $key . ''; + } + + 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($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); + $node->removeAttributeNS($ns, $ns_name); + return $dom->saveXML($node); + } + + /** + * Decodes the value + * + * @param string $value + */ + private static function decodeValue($value) + { + return Base64::decode(str_replace(["\r", "\n", ' ', "\t"], '', $value)); + } + + /** + * Extract points from an XML document + * + * @param \DOMXPath $xpath + * @param \phpseclib\Crypt\ECDSA\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 \phpseclib\Crypt\ECDSA\BaseCurves\Base based + * on the curve parameters + * + * @param \DomXPath $xpath + * @return \phpseclib\Crypt\ECDSA\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 = '\phpseclib\Crypt\ECDSA\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 \phpseclib\Crypt\ECDSA\BaseCurves\Base based + * on the curve parameters + * + * @param \DomXPath $xpath + * @return \phpseclib\Crypt\ECDSA\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 \phpseclib\Crypt\ECDSA\BaseCurves\Base $curve + * @param \phpseclib\Math\Common\FiniteField\Integer[] $publicKey + * @return string + */ + public static function savePublicKey(BaseCurve $curve, array $publicKey) + { + self::initialize_static_variables(); + + if ($curve instanceof TwistedEdwardsCurve) { + throw new UnsupportedCurveException('TwistedEdwards 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) . "\r\n" . + '<' . $pre . 'PublicKey>' . "\r\n" . + '<' . $pre . 'X Value="' . $publicKey[0] . '" />' . "\r\n" . + '<' . $pre . 'Y Value="' . $publicKey[1] . '" />' . "\r\n" . + '' . "\r\n" . + ''; + } + + $publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); + + return '<' . $pre . 'ECKeyValue xmlns' . $post . '="http://www.w3.org/2009/xmldsig11#">' . "\r\n" . + self::encodeXMLParameters($curve, $pre) . "\r\n" . + '<' . $pre . 'PublicKey>' . Base64::encode($publicKey) . '' . "\r\n" . + ''; + } + + /** + * Encode Parameters + * + * @param \phpseclib\Crypt\ECDSA\BaseCurves\Base $curve + * @param string $pre + * @return string|false + */ + private static function encodeXMLParameters(BaseCurve $curve, $pre) + { + $result = self::encodeParameters($curve, true); + + if (isset($result['namedCurve'])) { + $namedCurve = '<' . $pre . 'NamedCurve URI="urn:oid:' . self::$curveOIDs[$result['namedCurve']] . '" />'; + return self::$rfc4050 ? + '' . str_replace('URI', 'URN', $namedCurve) . '' : + $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'] . '' . "\r\n" . + '' . "\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.= '' . "\r\n" . + '<' . $pre . 'CurveParamsType>' . "\r\n" . + '<' . $pre . 'A>' . $a . '' . "\r\n" . + '<' . $pre . 'B>' . $b . '' . "\r\n" . + '' . "\r\n" . + '<' . $pre . 'BasePointParams>' . "\r\n" . + '<' . $pre . 'BasePoint>' . "\r\n" . + '<' . $pre . 'ECPointType>' . "\r\n" . + '<' . $pre . 'X>' . $x . '' . "\r\n" . + '<' . $pre . 'Y>' . $y . '' . "\r\n" . + '' . "\r\n" . + '' . "\r\n" . + '<' . $pre . 'Order>' . $curve->getOrder() . '' . "\r\n" . + '' . "\r\n" . + '' . "\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>' . Base64::encode($temp['fieldID']['parameters']->toBytes()) . '' . "\r\n" . + '' . "\r\n" ; + break; + default: + throw new UnsupportedCurveException('Field Type of ' . $temp['fieldID']['fieldType'] . ' is not supported'); + } + $xml.= '' . "\r\n" . + '<' . $pre . 'Curve>' . "\r\n" . + '<' . $pre . 'A>' . Base64::encode($temp['curve']['a']) . '' . "\r\n" . + '<' . $pre . 'B>' . Base64::encode($temp['curve']['b']) . '' . "\r\n" . + '' . "\r\n" . + '<' . $pre . 'Base>' . Base64::encode($temp['base']) . '' . "\r\n" . + '<' . $pre . 'Order>' . Base64::encode($temp['order']) . '' . "\r\n" . + ''; + return $xml; + } + } +} diff --git a/phpseclib/Crypt/ECDSA/Keys/libsodium.php b/phpseclib/Crypt/ECDSA/Keys/libsodium.php new file mode 100644 index 00000000..bf091bc9 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Keys/libsodium.php @@ -0,0 +1,111 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt\ECDSA\Keys; + +use phpseclib\Crypt\ECDSA\Curves\Ed25519; +use phpseclib\Math\Common\FiniteField\Integer; + +/** + * libsodium Key Handler + * + * @package ECDSA + * @author Jim Wigginton + * @access public + */ +abstract class libsodium +{ + use Common; + + /** + * Break a public or private key down into its constituent components + * + * @access public + * @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)) { + $components['dA'] = $curve->extractSecret($private); + } + $components['QA'] = isset($public) ? + self::extractPoint($public, $curve) : + $curve->multiplyPoint($curve->getBasePoint(), $components['dA']); + + + return $components; + } + + /** + * Convert an ECDSA public key to the appropriate format + * + * @access public + * @param \phpseclib\Crypt\ECDSA\Curves\Ed25519 $curve + * @param \phpseclib\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. + * + * @access public + * @param \phpseclib\Math\Common\FiniteField\Integer $privateKey + * @param \phpseclib\Crypt\ECDSA\Curves\Ed25519 $curve + * @param \phpseclib\Math\Common\FiniteField\Integer[] $publicKey + * @param string $password optional + * @return string + */ + public static function savePrivateKey(Integer $privateKey, Ed25519 $curve, array $publicKey, $password = '') + { + if (!isset($privateKey->secret)) { + throw new \RuntimeException('Private Key does not have a secret set'); + } + if (strlen($privateKey->secret) != 32) { + throw new \RuntimeException('Private Key secret is not of the correct length'); + } + return $privateKey->secret . $curve->encodePoint($publicKey); + } +} diff --git a/phpseclib/Crypt/ECDSA/Signature/ASN1.php b/phpseclib/Crypt/ECDSA/Signature/ASN1.php new file mode 100644 index 00000000..9baa9fd9 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Signature/ASN1.php @@ -0,0 +1,68 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt\ECDSA\Signature; + +use phpseclib\Math\BigInteger; +use phpseclib\File\ASN1 as Encoder; +use phpseclib\File\ASN1\Maps\EcdsaSigValue; + +/** + * ASN1 Signature Handler + * + * @package Common + * @author Jim Wigginton + * @access public + */ +abstract class ASN1 +{ + /** + * Loads a signature + * + * @access public + * @param array $key + * @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 + * + * @access public + * @param \phpseclib\Math\BigInteger $r + * @param \phpseclib\Math\BigInteger $s + * @return string + */ + public static function save(BigInteger $r, BigInteger $s) + { + return Encoder::encodeDER(compact('r', 's'), EcdsaSigValue::MAP); + } +} diff --git a/phpseclib/Crypt/ECDSA/Signature/Raw.php b/phpseclib/Crypt/ECDSA/Signature/Raw.php new file mode 100644 index 00000000..99e03533 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Signature/Raw.php @@ -0,0 +1,29 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt\ECDSA\Signature; + +use phpseclib\Crypt\Common\Signature\Raw as Progenitor; + +/** + * Raw DSA Signature Handler + * + * @package ECDSA + * @author Jim Wigginton + * @access public + */ +abstract class Raw extends Progenitor +{ +} diff --git a/phpseclib/Crypt/ECDSA/Signature/SSH2.php b/phpseclib/Crypt/ECDSA/Signature/SSH2.php new file mode 100644 index 00000000..65045ac7 --- /dev/null +++ b/phpseclib/Crypt/ECDSA/Signature/SSH2.php @@ -0,0 +1,95 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt\ECDSA\Signature; + +use phpseclib\Math\BigInteger; +use phpseclib\Common\Functions\Strings; + +/** + * SSH2 Signature Handler + * + * @package Common + * @author Jim Wigginton + * @access public + */ +abstract class SSH2 +{ + /** + * Loads a signature + * + * @access public + * @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; + } + + $length = ceil(substr($type, 16) / 8); + + return [ + 'r' => new BigInteger(substr($blob, 0, $length), 256), + 's' => new BigInteger(substr($blob, $length), 256) + ]; + } + + /** + * Returns a signature in the appropriate format + * + * @access public + * @param \phpseclib\Math\BigInteger $r + * @param \phpseclib\Math\BigInteger $s + * @param string $curve + * @return string + */ + public static function save(BigInteger $r, BigInteger $s, $curve) + { + switch ($curve) { + case 'nistp256': + case 'nistp384': + case 'nistp521': + break; + default: + return false; + } + + $length = ceil(substr($curve, 5) / 8); + + return Strings::packSSH2('ss', 'ecdsa-sha2-' . $curve, + str_pad($r->toBytes(), $length, "\0", STR_PAD_LEFT) . + str_pad($s->toBytes(), $length, "\0", STR_PAD_LEFT) + ); + } +} diff --git a/phpseclib/Crypt/RC2.php b/phpseclib/Crypt/RC2.php index d714e58a..cdaa4ec7 100644 --- a/phpseclib/Crypt/RC2.php +++ b/phpseclib/Crypt/RC2.php @@ -152,7 +152,7 @@ class RC2 extends BlockCipher * @var array * @access private */ - private $pitable = [ + private static $pitable = [ 0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED, 0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D, 0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E, @@ -226,7 +226,7 @@ class RC2 extends BlockCipher * @var array * @access private */ - private $invpitable = [ + private static $invpitable = [ 0xD1, 0xDA, 0xB9, 0x6F, 0x9C, 0xC8, 0x78, 0x66, 0x80, 0x2C, 0xF8, 0x37, 0xEA, 0xE0, 0x62, 0xA4, 0xCB, 0x71, 0x50, 0x27, 0x4B, 0x95, 0xD9, 0x20, @@ -378,7 +378,7 @@ class RC2 extends BlockCipher $tm = 0xFF >> (8 * $t8 - $t1); // Expand key. - $pitable = $this->pitable; + $pitable = self::$pitable; for ($i = $t; $i < 128; $i++) { $l[$i] = $pitable[$l[$i - 1] + $l[$i - $t]]; } @@ -389,7 +389,7 @@ class RC2 extends BlockCipher } // Prepare the key for mcrypt. - $l[0] = $this->invpitable[$l[0]]; + $l[0] = self::$invpitable[$l[0]]; array_unshift($l, 'C*'); $this->key = call_user_func_array('pack', $l); @@ -549,7 +549,7 @@ class RC2 extends BlockCipher // 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)); + array_unshift($l, self::$pitable[$l['a']] | ($l['b'] << 8)); unset($l['a']); unset($l['b']); $this->keys = $l; diff --git a/phpseclib/Crypt/RSA.php b/phpseclib/Crypt/RSA.php index 2689b3f3..4df22b1a 100644 --- a/phpseclib/Crypt/RSA.php +++ b/phpseclib/Crypt/RSA.php @@ -45,7 +45,6 @@ namespace phpseclib\Crypt; -use ParagonIE\ConstantTime\Base64; use phpseclib\File\ASN1; use phpseclib\Math\BigInteger; use phpseclib\Common\Functions\Strings; @@ -203,14 +202,6 @@ class RSA extends AsymmetricKey */ private $sLen; - /** - * Comment - * - * @var string - * @access private - */ - private $comment; - /** * Hash function for the Mask Generation Function * @@ -332,40 +323,11 @@ class RSA extends AsymmetricKey * @return array * @access public * @param int $bits - * */ public static function createKey($bits = 2048) { self::initialize_static_variables(); - if (!isset(self::$engine)) { - self::setPreferredEngine(self::ENGINE_OPENSSL); - } - - // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum - - if (self::$engine == self::ENGINE_OPENSSL && $bits >= 384 && self::$defaultExponent == 65537) { - $config = []; - if (isset(self::$configFile)) { - $config['config'] = self::$configFile; - } - $rsa = openssl_pkey_new(['private_key_bits' => $bits] + $config); - openssl_pkey_export($rsa, $privatekeystr, null, $config); - $privatekey = new RSA(); - $privatekey->load($privatekeystr); - - $publickeyarr = openssl_pkey_get_details($rsa); - $publickey = new RSA(); - $publickey->load($publickeyarr['key']); - $publickey->setPublicKey(); - - // clear the buffer of error strings stemming from a minimalistic openssl.cnf - while (openssl_error_string() !== false) { - } - - return compact('privatekey', 'publickey'); - } - static $e; if (!isset($e)) { $e = new BigInteger(self::$defaultExponent); @@ -488,7 +450,8 @@ class RSA extends AsymmetricKey $this->isPublic = $key->isPublic; if (is_object($key->hash)) { - $this->hash = new Hash($key->hash->getHash()); + $this->hashName = $key->hash->getHash(); + $this->hash = new Hash($this->hashName); } if (is_object($key->mgfHash)) { $this->mgfHash = new Hash($key->mgfHash->getHash()); @@ -531,6 +494,7 @@ class RSA extends AsymmetricKey $this->exponents = null; $this->coefficients = null; $this->publicExponent = null; + $this->isPublic = false; return false; } @@ -652,6 +616,19 @@ class RSA extends AsymmetricKey return !isset($this->modulus) ? 0 : $this->modulus->getLength(); } + /** + * Returns the current engine being used + * + * @see self::useInternalEngine() + * @see self::useBestEngine() + * @access public + * @return string + */ + public function getEngine() + { + return 'PHP'; + } + /** * Defines the public key * @@ -704,15 +681,25 @@ class RSA extends AsymmetricKey } /** - * Does the key self-identify as being a public key or not? + * Is the key a public key? * - * @see self::isPublicKey() * @access public * @return bool */ public function isPublicKey() { - return $this->isPublic(); + return $this->isPublic; + } + + /** + * Is the key a private key? + * + * @access public + * @return bool + */ + public function isPrivateKey() + { + return !$this->isPublic && isset($this->modulus); } /** @@ -735,6 +722,7 @@ class RSA extends AsymmetricKey { if ($key === false && !empty($this->publicExponent)) { $this->publicExponent = false; + $this->isPublic = false; return true; } @@ -743,6 +731,7 @@ class RSA extends AsymmetricKey return false; } $rsa->publicExponent = false; + $rsa->isPublic = false; // don't overwrite the old key if the new key is invalid $this->load($rsa); @@ -761,8 +750,14 @@ class RSA extends AsymmetricKey * @param string $type optional * @return mixed */ - public function getPublicKey($type = 'PKCS8') + public function getPublicKey($type = null) { + $returnObj = false; + if ($type === null) { + $returnObj = true; + $type = 'PKCS8'; + } + $type = self::validatePlugin('Keys', $type, 'savePublicKey'); if ($type === false) { return false; @@ -772,7 +767,15 @@ class RSA extends AsymmetricKey return false; } - return $type::savePublicKey($this->modulus, $this->publicExponent); + $key = $type::savePublicKey($this->modulus, $this->publicExponent); + if (!$returnObj) { + return $key; + } + + $public = clone $this; + $public->load($key, 'PKCS8'); + + return $public; } /** @@ -1704,17 +1707,17 @@ class RSA extends AsymmetricKey static $oids; if (!isset($oids)) { $oids = [ - '1.2.840.113549.2.2' => 'md2', - '1.2.840.113549.2.4' => 'md4', // from PKCS1 v1.5 - '1.2.840.113549.2.5' => 'md5', - '1.3.14.3.2.26' => 'id-sha1', - '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', + '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 - '2.16.840.1.101.3.4.2.4' => 'id-sha224', - '2.16.840.1.101.3.4.2.5' => 'id-sha512/224', - '2.16.840.1.101.3.4.2.6' => 'id-sha512/256', + '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); } @@ -1724,7 +1727,7 @@ class RSA extends AsymmetricKey return false; } - if (!in_array($decoded['digestAlgorithm']['algorithm'], $oids)) { + if (!isset($oids[$decoded['digestAlgorithm']['algorithm']])) { return false; } diff --git a/phpseclib/Crypt/RSA/Keys/MSBLOB.php b/phpseclib/Crypt/RSA/Keys/MSBLOB.php index 52223ffd..ef4d7ccc 100644 --- a/phpseclib/Crypt/RSA/Keys/MSBLOB.php +++ b/phpseclib/Crypt/RSA/Keys/MSBLOB.php @@ -71,18 +71,21 @@ abstract class MSBLOB * @access public * @param string $key * @param string $password optional - * @return array|bool + * @return array */ public static function load($key, $password = '') { if (!is_string($key)) { - return false; + throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } $key = Base64::decode($key); - if (!is_string($key) || strlen($key) < 20) { - return false; + 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 @@ -103,7 +106,7 @@ abstract class MSBLOB $publickey = false; break; default: - return false; + throw new \UnexpectedValueException('Key appears to be malformed'); } $components = ['isPublicKey' => $publickey]; @@ -114,7 +117,7 @@ abstract class MSBLOB case self::CALG_RSA_SIGN: break; default: - return false; + throw new \UnexpectedValueException('Key appears to be malformed'); } // RSAPUBKEY rsapubkey @@ -132,12 +135,12 @@ abstract class MSBLOB case self::RSA1: break; default: - return false; + throw new \UnexpectedValueException('Key appears to be malformed'); } $baseLength = $bitlen / 16; if (strlen($key) != 2 * $baseLength && strlen($key) != 9 * $baseLength) { - return false; + throw new \UnexpectedValueException('Key appears to be malformed'); } $components[$components['isPublicKey'] ? 'publicExponent' : 'privateExponent'] = new BigInteger(strrev($pubexp), 256); diff --git a/phpseclib/Crypt/RSA/Keys/OpenSSH.php b/phpseclib/Crypt/RSA/Keys/OpenSSH.php index b8ba1bb5..77545d25 100644 --- a/phpseclib/Crypt/RSA/Keys/OpenSSH.php +++ b/phpseclib/Crypt/RSA/Keys/OpenSSH.php @@ -37,18 +37,15 @@ abstract class OpenSSH extends Progenitor * @access public * @param string $key * @param string $password optional - * @return array|bool + * @return array */ public static function load($key, $password = '') { $key = parent::load($key, 'ssh-rsa'); - if ($key === false) { - return false; - } $result = Strings::unpackSSH2('ii', $key); if ($result === false) { - return false; + throw new \UnexpectedValueException('Key appears to be malformed'); } list($publicExponent, $modulus) = $result; diff --git a/phpseclib/Crypt/RSA/Keys/PKCS1.php b/phpseclib/Crypt/RSA/Keys/PKCS1.php index 6c73d65d..6dbb46f4 100644 --- a/phpseclib/Crypt/RSA/Keys/PKCS1.php +++ b/phpseclib/Crypt/RSA/Keys/PKCS1.php @@ -44,24 +44,21 @@ abstract class PKCS1 extends Progenitor * @access public * @param string $key * @param string $password optional - * @return array|bool + * @return array */ public static function load($key, $password = '') { if (!is_string($key)) { - return false; + throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } $components = ['isPublicKey' => strpos($key, 'PUBLIC') !== false]; $key = parent::load($key, $password); - if ($key === false) { - return false; - } $decoded = ASN1::decodeBER($key); if (empty($decoded)) { - return false; + throw new \RuntimeException('Unable to decode BER'); } $key = ASN1::asn1map($decoded[0], Maps\RSAPrivateKey::MAP); @@ -86,7 +83,11 @@ abstract class PKCS1 extends Progenitor $key = ASN1::asn1map($decoded[0], Maps\RSAPublicKey::MAP); - return is_array($key) ? $components + $key : false; + if (!is_array($key)) { + throw new \RuntimeException('Unable to perform ASN1 mapping'); + } + + return $components + $key; } /** diff --git a/phpseclib/Crypt/RSA/Keys/PKCS8.php b/phpseclib/Crypt/RSA/Keys/PKCS8.php index 40888020..b50c7c0a 100644 --- a/phpseclib/Crypt/RSA/Keys/PKCS8.php +++ b/phpseclib/Crypt/RSA/Keys/PKCS8.php @@ -70,20 +70,17 @@ abstract class PKCS8 extends Progenitor * @access public * @param string $key * @param string $password optional - * @return array|bool + * @return array */ public static function load($key, $password = '') { if (!is_string($key)) { - return false; + throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } $components = ['isPublicKey' => strpos($key, 'PUBLIC') !== false]; $key = parent::load($key, $password); - if ($key === false) { - return false; - } $type = isset($key['privateKey']) ? 'private' : 'public'; diff --git a/phpseclib/Crypt/RSA/Keys/PuTTY.php b/phpseclib/Crypt/RSA/Keys/PuTTY.php index af5653d0..ea8d61b5 100644 --- a/phpseclib/Crypt/RSA/Keys/PuTTY.php +++ b/phpseclib/Crypt/RSA/Keys/PuTTY.php @@ -39,10 +39,10 @@ abstract class PuTTY extends Progenitor /** * Algorithm Identifier * - * @var string + * @var array * @access private */ - const TYPE = 'ssh-rsa'; + protected static $types = ['ssh-rsa']; /** * Break a public or private key down into its constituent components @@ -50,7 +50,7 @@ abstract class PuTTY extends Progenitor * @access public * @param string $key * @param string $password optional - * @return array|bool + * @return array */ public static function load($key, $password = '') { @@ -60,21 +60,23 @@ abstract class PuTTY extends Progenitor } $components = parent::load($key, $password); - if ($components === false || !isset($components['private'])) { + if (!isset($components['private'])) { return $components; } + extract($components); + unset($components['public'], $components['private']); $isPublicKey = false; - $result = Strings::unpackSSH2('ii', $components['public']); + $result = Strings::unpackSSH2('ii', $public); if ($result === false) { - return false; + throw new \UnexpectedValueException('Key appears to be malformed'); } list($publicExponent, $modulus) = $result; - $result = Strings::unpackSSH2('iiii', $components['private']); + $result = Strings::unpackSSH2('iiii', $private); if ($result === false) { - return false; + throw new \UnexpectedValueException('Key appears to be malformed'); } $primes = $coefficients = []; list($privateExponent, $primes[1], $primes[2], $coefficients[2]) = $result; @@ -84,10 +86,6 @@ abstract class PuTTY extends Progenitor $temp = $primes[2]->subtract($one); $exponents[] = $publicExponent->modInverse($temp); - if (isset($components['comment'])) { - $comment = $components['comment']; - } - return compact('publicExponent', 'modulus', 'privateExponent', 'primes', 'coefficients', 'exponents', 'comment', 'isPublicKey'); } @@ -113,7 +111,7 @@ abstract class PuTTY extends Progenitor $public = Strings::packSSH2('ii', $e, $n); $private = Strings::packSSH2('iiii', $d, $primes[1], $primes[2], $coefficients[2]); - return self::wrapPrivateKey($public, $private, $password); + return self::wrapPrivateKey($public, $private, 'ssh-rsa', $password); } /** @@ -126,6 +124,6 @@ abstract class PuTTY extends Progenitor */ public static function savePublicKey(BigInteger $n, BigInteger $e) { - return self::wrapPublicKey(Strings::packSSH2($e, $n)); + return self::wrapPublicKey(Strings::packSSH2($e, $n), 'ssh-rsa'); } } diff --git a/phpseclib/Crypt/RSA/Keys/Raw.php b/phpseclib/Crypt/RSA/Keys/Raw.php index fe3c4c62..28a58cb5 100644 --- a/phpseclib/Crypt/RSA/Keys/Raw.php +++ b/phpseclib/Crypt/RSA/Keys/Raw.php @@ -42,12 +42,12 @@ abstract class Raw * @access public * @param string $key * @param string $password optional - * @return array|bool + * @return array */ public static function load($key, $password = '') { if (!is_array($key)) { - return false; + throw new \UnexpectedValueException('Key should be a array - not a ' . gettype($key)); } if (isset($key['isPublicKey']) && isset($key['modulus'])) { if (isset($key['privateExponent']) || isset($key['publicExponent'])) { @@ -86,7 +86,11 @@ abstract class Raw case isset($key[1]): $components['modulus'] = $key[1]; } - return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false; + if (isset($components['modulus']) && isset($components['publicExponent'])) { + return $components; + } + + throw new \UnexpectedValueException('Modulus / exponent not present'); } /** diff --git a/phpseclib/Crypt/RSA/Keys/XML.php b/phpseclib/Crypt/RSA/Keys/XML.php index 07549026..c8caf4b5 100644 --- a/phpseclib/Crypt/RSA/Keys/XML.php +++ b/phpseclib/Crypt/RSA/Keys/XML.php @@ -40,12 +40,12 @@ abstract class XML * @access public * @param string $key * @param string $password optional - * @return array|bool + * @return array */ public static function load($key, $password = '') { if (!is_string($key)) { - return false; + throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } $components = [ @@ -62,7 +62,7 @@ abstract class XML $key = '' . $key . ''; } if (!$dom->loadXML($key)) { - return false; + throw new \UnexpectedValueException('Key does not appear to contain XML'); } $xpath = new \DOMXPath($dom); $keys = ['modulus', 'exponent', 'p', 'q', 'dp', 'dq', 'inverseq', 'd']; @@ -102,7 +102,11 @@ abstract class XML libxml_use_internal_errors($use_errors); - return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false; + if (isset($components['modulus']) && isset($components['publicExponent'])) { + return $components; + } + + throw new \UnexpectedValueException('Modulus / exponent not present'); } /** diff --git a/phpseclib/Crypt/Twofish.php b/phpseclib/Crypt/Twofish.php index ff77834b..b83818e4 100644 --- a/phpseclib/Crypt/Twofish.php +++ b/phpseclib/Crypt/Twofish.php @@ -73,7 +73,7 @@ class Twofish extends BlockCipher * @var array * @access private */ - private $q0 = [ + private static $q0 = [ 0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76, 0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38, 0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C, @@ -114,7 +114,7 @@ class Twofish extends BlockCipher * @var array * @access private */ - private $q1 = [ + private static $q1 = [ 0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8, 0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B, 0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1, @@ -155,7 +155,7 @@ class Twofish extends BlockCipher * @var array * @access private */ - private $m0 = [ + private static $m0 = [ 0xBCBC3275, 0xECEC21F3, 0x202043C6, 0xB3B3C9F4, 0xDADA03DB, 0x02028B7B, 0xE2E22BFB, 0x9E9EFAC8, 0xC9C9EC4A, 0xD4D409D3, 0x18186BE6, 0x1E1E9F6B, 0x98980E45, 0xB2B2387D, 0xA6A6D2E8, 0x2626B74B, 0x3C3C57D6, 0x93938A32, 0x8282EED8, 0x525298FD, 0x7B7BD437, 0xBBBB3771, 0x5B5B97F1, 0x474783E1, @@ -196,7 +196,7 @@ class Twofish extends BlockCipher * @var array * @access private */ - private $m1 = [ + private static $m1 = [ 0xA9D93939, 0x67901717, 0xB3719C9C, 0xE8D2A6A6, 0x04050707, 0xFD985252, 0xA3658080, 0x76DFE4E4, 0x9A084545, 0x92024B4B, 0x80A0E0E0, 0x78665A5A, 0xE4DDAFAF, 0xDDB06A6A, 0xD1BF6363, 0x38362A2A, 0x0D54E6E6, 0xC6432020, 0x3562CCCC, 0x98BEF2F2, 0x181E1212, 0xF724EBEB, 0xECD7A1A1, 0x6C774141, @@ -237,7 +237,7 @@ class Twofish extends BlockCipher * @var array * @access private */ - private $m2 = [ + private static $m2 = [ 0xBC75BC32, 0xECF3EC21, 0x20C62043, 0xB3F4B3C9, 0xDADBDA03, 0x027B028B, 0xE2FBE22B, 0x9EC89EFA, 0xC94AC9EC, 0xD4D3D409, 0x18E6186B, 0x1E6B1E9F, 0x9845980E, 0xB27DB238, 0xA6E8A6D2, 0x264B26B7, 0x3CD63C57, 0x9332938A, 0x82D882EE, 0x52FD5298, 0x7B377BD4, 0xBB71BB37, 0x5BF15B97, 0x47E14783, @@ -278,7 +278,7 @@ class Twofish extends BlockCipher * @var array * @access private */ - private $m3 = [ + private static $m3 = [ 0xD939A9D9, 0x90176790, 0x719CB371, 0xD2A6E8D2, 0x05070405, 0x9852FD98, 0x6580A365, 0xDFE476DF, 0x08459A08, 0x024B9202, 0xA0E080A0, 0x665A7866, 0xDDAFE4DD, 0xB06ADDB0, 0xBF63D1BF, 0x362A3836, 0x54E60D54, 0x4320C643, 0x62CC3562, 0xBEF298BE, 0x1E12181E, 0x24EBF724, 0xD7A1ECD7, 0x77416C77, @@ -449,12 +449,12 @@ class Twofish extends BlockCipher /* 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; + $m0 = self::$m0; + $m1 = self::$m1; + $m2 = self::$m2; + $m3 = self::$m3; + $q0 = self::$q0; + $q1 = self::$q1; $K = $S0 = $S1 = $S2 = $S3 = []; diff --git a/phpseclib/Exception/UnsupportedCurveException.php b/phpseclib/Exception/UnsupportedCurveException.php new file mode 100644 index 00000000..bd2c81b5 --- /dev/null +++ b/phpseclib/Exception/UnsupportedCurveException.php @@ -0,0 +1,26 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Exception; + +/** + * UnsupportedCurveException + * + * @package UnsupportedCurveException + * @author Jim Wigginton + */ +class UnsupportedCurveException extends \RuntimeException +{ +} diff --git a/phpseclib/File/ASN1.php b/phpseclib/File/ASN1.php index fbc06063..2e3ec8e3 100644 --- a/phpseclib/File/ASN1.php +++ b/phpseclib/File/ASN1.php @@ -818,6 +818,27 @@ abstract class ASN1 } } + /** + * 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 public + * @param string $string + * @return int + */ + public static function decodeLength(&$string) + { + $length = ord(Strings::shift($string)); + if ($length & 0x80) { // definite length, long form + $length&= 0x7F; + $temp = Strings::shift($string, $length); + list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)); + } + return $length; + } + /** * ASN.1 Encode * @@ -1219,8 +1240,8 @@ abstract class ASN1 */ public static function loadOIDs($oids) { - self::$oids+= $oids; - self::$reverseOIDs = array_flip(self::$oids); + self::$reverseOIDs+= $oids; + self::$oids = array_flip(self::$reverseOIDs); } /** diff --git a/phpseclib/File/ASN1/Maps/Characteristic_two.php b/phpseclib/File/ASN1/Maps/Characteristic_two.php new file mode 100644 index 00000000..92817031 --- /dev/null +++ b/phpseclib/File/ASN1/Maps/Characteristic_two.php @@ -0,0 +1,40 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File\ASN1\Maps; + +use phpseclib\File\ASN1; + +/** + * Characteristic_two + * + * @package ASN1 + * @author Jim Wigginton + * @access public + */ +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 + ] + ] + ]; +} \ No newline at end of file diff --git a/phpseclib/File/ASN1/Maps/Curve.php b/phpseclib/File/ASN1/Maps/Curve.php new file mode 100644 index 00000000..1a438028 --- /dev/null +++ b/phpseclib/File/ASN1/Maps/Curve.php @@ -0,0 +1,40 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File\ASN1\Maps; + +use phpseclib\File\ASN1; + +/** + * Curve + * + * @package ASN1 + * @author Jim Wigginton + * @access public + */ +abstract class Curve +{ + const MAP = [ + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => [ + 'a' => FieldElement::MAP, + 'b' => FieldElement::MAP, + 'seed' => [ + 'type' => ASN1::TYPE_BIT_STRING, + 'optional' => true + ] + ] + ]; +} diff --git a/phpseclib/File/ASN1/Maps/ECParameters.php b/phpseclib/File/ASN1/Maps/ECParameters.php new file mode 100644 index 00000000..21e096ea --- /dev/null +++ b/phpseclib/File/ASN1/Maps/ECParameters.php @@ -0,0 +1,49 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File\ASN1\Maps; + +use phpseclib\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. + * + * @package ASN1 + * @author Jim Wigginton + * @access public + */ +abstract class ECParameters +{ + const MAP = [ + 'type' => ASN1::TYPE_CHOICE, + 'children' => [ + 'namedCurve' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER], + 'implicitCurve' => ['type' => ASN1::TYPE_NULL], + 'specifiedCurve' => SpecifiedECDomain::MAP + ] + ]; +} diff --git a/phpseclib/File/ASN1/Maps/ECPoint.php b/phpseclib/File/ASN1/Maps/ECPoint.php new file mode 100644 index 00000000..bd6ea09e --- /dev/null +++ b/phpseclib/File/ASN1/Maps/ECPoint.php @@ -0,0 +1,30 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File\ASN1\Maps; + +use phpseclib\File\ASN1; + +/** + * ECPoint + * + * @package ASN1 + * @author Jim Wigginton + * @access public + */ +abstract class ECPoint +{ + const MAP = ['type' => ASN1::TYPE_OCTET_STRING]; +} diff --git a/phpseclib/File/ASN1/Maps/ECPrivateKey.php b/phpseclib/File/ASN1/Maps/ECPrivateKey.php new file mode 100644 index 00000000..fb17c412 --- /dev/null +++ b/phpseclib/File/ASN1/Maps/ECPrivateKey.php @@ -0,0 +1,52 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File\ASN1\Maps; + +use phpseclib\File\ASN1; + +/** + * ECPrivateKey + * + * @package ASN1 + * @author Jim Wigginton + * @access public + */ +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 + ] + ] + ]; +} diff --git a/phpseclib/File/ASN1/Maps/EcdsaSigValue.php b/phpseclib/File/ASN1/Maps/EcdsaSigValue.php new file mode 100644 index 00000000..69f95bda --- /dev/null +++ b/phpseclib/File/ASN1/Maps/EcdsaSigValue.php @@ -0,0 +1,36 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File\ASN1\Maps; + +use phpseclib\File\ASN1; + +/** + * EcdsaSigValue + * + * @package ASN1 + * @author Jim Wigginton + * @access public + */ +abstract class EcdsaSigValue +{ + const MAP = [ + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => [ + 'r' => ['type' => ASN1::TYPE_INTEGER], + 's' => ['type' => ASN1::TYPE_INTEGER] + ] + ]; +} diff --git a/phpseclib/File/ASN1/Maps/FieldElement.php b/phpseclib/File/ASN1/Maps/FieldElement.php new file mode 100644 index 00000000..5617607f --- /dev/null +++ b/phpseclib/File/ASN1/Maps/FieldElement.php @@ -0,0 +1,30 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File\ASN1\Maps; + +use phpseclib\File\ASN1; + +/** + * FieldElement + * + * @package ASN1 + * @author Jim Wigginton + * @access public + */ +abstract class FieldElement +{ + const MAP = ['type' => ASN1::TYPE_OCTET_STRING]; +} diff --git a/phpseclib/File/ASN1/Maps/FieldID.php b/phpseclib/File/ASN1/Maps/FieldID.php new file mode 100644 index 00000000..b5642030 --- /dev/null +++ b/phpseclib/File/ASN1/Maps/FieldID.php @@ -0,0 +1,39 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File\ASN1\Maps; + +use phpseclib\File\ASN1; + +/** + * FieldID + * + * @package ASN1 + * @author Jim Wigginton + * @access public + */ +abstract class FieldID +{ + const MAP = [ + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => [ + 'fieldType' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER], + 'parameters' => [ + 'type' => ASN1::TYPE_ANY, + 'optional' => true + ] + ] + ]; +} \ No newline at end of file diff --git a/phpseclib/File/ASN1/Maps/HashAlgorithm.php b/phpseclib/File/ASN1/Maps/HashAlgorithm.php new file mode 100644 index 00000000..49e718ec --- /dev/null +++ b/phpseclib/File/ASN1/Maps/HashAlgorithm.php @@ -0,0 +1,30 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File\ASN1\Maps; + +use phpseclib\File\ASN1; + +/** + * HashAglorithm + * + * @package ASN1 + * @author Jim Wigginton + * @access public + */ +abstract class HashAlgorithm +{ + const MAP = AlgorithmIdentifier::MAP; +} \ No newline at end of file diff --git a/phpseclib/File/ASN1/Maps/OneAsymmetricKey.php b/phpseclib/File/ASN1/Maps/OneAsymmetricKey.php new file mode 100644 index 00000000..73af0695 --- /dev/null +++ b/phpseclib/File/ASN1/Maps/OneAsymmetricKey.php @@ -0,0 +1,52 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File\ASN1\Maps; + +use phpseclib\File\ASN1; + +/** + * OneAsymmetricKey + * + * @package ASN1 + * @author Jim Wigginton + * @access public + */ +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 + ] + ]; +} diff --git a/phpseclib/File/ASN1/Maps/Pentanomial.php b/phpseclib/File/ASN1/Maps/Pentanomial.php new file mode 100644 index 00000000..2474680e --- /dev/null +++ b/phpseclib/File/ASN1/Maps/Pentanomial.php @@ -0,0 +1,37 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File\ASN1\Maps; + +use phpseclib\File\ASN1; + +/** + * Pentanomial + * + * @package ASN1 + * @author Jim Wigginton + * @access public + */ +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 + ] + ]; +} \ No newline at end of file diff --git a/phpseclib/File/ASN1/Maps/Prime_p.php b/phpseclib/File/ASN1/Maps/Prime_p.php new file mode 100644 index 00000000..fe107750 --- /dev/null +++ b/phpseclib/File/ASN1/Maps/Prime_p.php @@ -0,0 +1,30 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File\ASN1\Maps; + +use phpseclib\File\ASN1; + +/** + * Prime_p + * + * @package ASN1 + * @author Jim Wigginton + * @access public + */ +abstract class Prime_p +{ + const MAP = ['type' => ASN1::TYPE_INTEGER]; +} \ No newline at end of file diff --git a/phpseclib/File/ASN1/Maps/PublicKey.php b/phpseclib/File/ASN1/Maps/PublicKey.php new file mode 100644 index 00000000..1f269480 --- /dev/null +++ b/phpseclib/File/ASN1/Maps/PublicKey.php @@ -0,0 +1,30 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File\ASN1\Maps; + +use phpseclib\File\ASN1; + +/** + * PublicKey + * + * @package ASN1 + * @author Jim Wigginton + * @access public + */ +abstract class PublicKey +{ + const MAP = ['type' => ASN1::TYPE_BIT_STRING]; +} diff --git a/phpseclib/File/ASN1/Maps/SpecifiedECDomain.php b/phpseclib/File/ASN1/Maps/SpecifiedECDomain.php new file mode 100644 index 00000000..b44541e2 --- /dev/null +++ b/phpseclib/File/ASN1/Maps/SpecifiedECDomain.php @@ -0,0 +1,49 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File\ASN1\Maps; + +use phpseclib\File\ASN1; + +/** + * SpecifiedECDomain + * + * @package ASN1 + * @author Jim Wigginton + * @access public + */ +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 + ] + ]; +} diff --git a/phpseclib/File/ASN1/Maps/Trinomial.php b/phpseclib/File/ASN1/Maps/Trinomial.php new file mode 100644 index 00000000..3e0dac82 --- /dev/null +++ b/phpseclib/File/ASN1/Maps/Trinomial.php @@ -0,0 +1,30 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File\ASN1\Maps; + +use phpseclib\File\ASN1; + +/** + * Trinomial + * + * @package ASN1 + * @author Jim Wigginton + * @access public + */ +abstract class Trinomial +{ + const MAP = ['type' => ASN1::TYPE_INTEGER]; +} \ No newline at end of file diff --git a/phpseclib/File/X509.php b/phpseclib/File/X509.php index 7c3451f1..fe0e32a6 100644 --- a/phpseclib/File/X509.php +++ b/phpseclib/File/X509.php @@ -273,168 +273,168 @@ class X509 if (!self::$oidsLoaded) { // OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2 ASN1::loadOIDs([ - '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', + 'id-pkix' => '1.3.6.1.5.5.7', + 'id-pe' => '1.3.6.1.5.5.7.1', + 'id-qt' => '1.3.6.1.5.5.7.2', + 'id-kp' => '1.3.6.1.5.5.7.3', + 'id-ad' => '1.3.6.1.5.5.7.48', + 'id-qt-cps' => '1.3.6.1.5.5.7.2.1', + 'id-qt-unotice' => '1.3.6.1.5.5.7.2.2', + 'id-ad-ocsp' =>'1.3.6.1.5.5.7.48.1', + 'id-ad-caIssuers' => '1.3.6.1.5.5.7.48.2', + 'id-ad-timeStamping' => '1.3.6.1.5.5.7.48.3', + 'id-ad-caRepository' => '1.3.6.1.5.5.7.48.5', + 'id-at' => '2.5.4', + 'id-at-name' => '2.5.4.41', + 'id-at-surname' => '2.5.4.4', + 'id-at-givenName' => '2.5.4.42', + 'id-at-initials' => '2.5.4.43', + 'id-at-generationQualifier' => '2.5.4.44', + 'id-at-commonName' => '2.5.4.3', + 'id-at-localityName' => '2.5.4.7', + 'id-at-stateOrProvinceName' => '2.5.4.8', + 'id-at-organizationName' => '2.5.4.10', + 'id-at-organizationalUnitName' => '2.5.4.11', + 'id-at-title' => '2.5.4.12', + 'id-at-description' => '2.5.4.13', + 'id-at-dnQualifier' => '2.5.4.46', + 'id-at-countryName' => '2.5.4.6', + 'id-at-serialNumber' => '2.5.4.5', + 'id-at-pseudonym' => '2.5.4.65', + 'id-at-postalCode' => '2.5.4.17', + 'id-at-streetAddress' => '2.5.4.9', + 'id-at-uniqueIdentifier' => '2.5.4.45', + 'id-at-role' => '2.5.4.72', + 'id-at-postalAddress' => '2.5.4.16', - '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', + 'id-domainComponent' => '0.9.2342.19200300.100.1.25', + 'pkcs-9' => '1.2.840.113549.1.9', + 'pkcs-9-at-emailAddress' => '1.2.840.113549.1.9.1', + 'id-ce' => '2.5.29', + 'id-ce-authorityKeyIdentifier' => '2.5.29.35', + 'id-ce-subjectKeyIdentifier' => '2.5.29.14', + 'id-ce-keyUsage' => '2.5.29.15', + 'id-ce-privateKeyUsagePeriod' => '2.5.29.16', + 'id-ce-certificatePolicies' => '2.5.29.32', + 'anyPolicy' => '2.5.29.32.0', - '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', + 'id-ce-policyMappings' => '2.5.29.33', + 'id-ce-subjectAltName' => '2.5.29.17', + 'id-ce-issuerAltName' => '2.5.29.18', + 'id-ce-subjectDirectoryAttributes' => '2.5.29.9', + 'id-ce-basicConstraints' => '2.5.29.19', + 'id-ce-nameConstraints' => '2.5.29.30', + 'id-ce-policyConstraints' => '2.5.29.36', + 'id-ce-cRLDistributionPoints' => '2.5.29.31', + 'id-ce-extKeyUsage' => '2.5.29.37', + 'anyExtendedKeyUsage' => '2.5.29.37.0', + 'id-kp-serverAuth' => '1.3.6.1.5.5.7.3.1', + 'id-kp-clientAuth' => '1.3.6.1.5.5.7.3.2', + 'id-kp-codeSigning' => '1.3.6.1.5.5.7.3.3', + 'id-kp-emailProtection' => '1.3.6.1.5.5.7.3.4', + 'id-kp-timeStamping' => '1.3.6.1.5.5.7.3.8', + 'id-kp-OCSPSigning' => '1.3.6.1.5.5.7.3.9', + 'id-ce-inhibitAnyPolicy' => '2.5.29.54', + 'id-ce-freshestCRL' => '2.5.29.46', + 'id-pe-authorityInfoAccess' => '1.3.6.1.5.5.7.1.1', + 'id-pe-subjectInfoAccess' => '1.3.6.1.5.5.7.1.11', + 'id-ce-cRLNumber' => '2.5.29.20', + 'id-ce-issuingDistributionPoint' => '2.5.29.28', + 'id-ce-deltaCRLIndicator' => '2.5.29.27', + 'id-ce-cRLReasons' => '2.5.29.21', + 'id-ce-certificateIssuer' => '2.5.29.29', + 'id-ce-holdInstructionCode' => '2.5.29.23', + 'holdInstruction' => '1.2.840.10040.2', + 'id-holdinstruction-none' => '1.2.840.10040.2.1', + 'id-holdinstruction-callissuer' => '1.2.840.10040.2.2', + 'id-holdinstruction-reject' => '1.2.840.10040.2.3', + 'id-ce-invalidityDate' => '2.5.29.24', - '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', + 'md2' => '1.2.840.113549.2.2', + 'md5' => '1.2.840.113549.2.5', + 'id-sha1' => '1.3.14.3.2.26', + 'id-dsa' => '1.2.840.10040.4.1', + 'id-dsa-with-sha1' => '1.2.840.10040.4.3', + 'pkcs-1' => '1.2.840.113549.1.1', + 'rsaEncryption' => '1.2.840.113549.1.1.1', + 'md2WithRSAEncryption' => '1.2.840.113549.1.1.2', + 'md5WithRSAEncryption' => '1.2.840.113549.1.1.4', + 'sha1WithRSAEncryption' => '1.2.840.113549.1.1.5', + 'dhpublicnumber' => '1.2.840.10046.2.1', + 'id-keyExchangeAlgorithm' => '2.16.840.1.101.2.1.1.22', + 'ansi-X9-62' => '1.2.840.10045', + 'id-ecSigType' => '1.2.840.10045.4', + 'ecdsa-with-SHA1' => '1.2.840.10045.4.1', + 'id-fieldType' => '1.2.840.10045.1', + 'prime-field' => '1.2.840.10045.1.1', + 'characteristic-two-field' => '1.2.840.10045.1.2', + 'id-characteristic-two-basis' => '1.2.840.10045.1.2.3', + 'gnBasis' => '1.2.840.10045.1.2.3.1', + 'tpBasis' => '1.2.840.10045.1.2.3.2', + 'ppBasis' => '1.2.840.10045.1.2.3.3', + 'id-publicKeyType' => '1.2.840.10045.2', + 'id-ecPublicKey' => '1.2.840.10045.2.1', + 'ellipticCurve' => '1.2.840.10045.3', + 'c-TwoCurve' => '1.2.840.10045.3.0', + 'c2pnb163v1' => '1.2.840.10045.3.0.1', + 'c2pnb163v2' => '1.2.840.10045.3.0.2', + 'c2pnb163v3' => '1.2.840.10045.3.0.3', + 'c2pnb176w1' => '1.2.840.10045.3.0.4', + 'c2pnb191v1' => '1.2.840.10045.3.0.5', + 'c2pnb191v2' => '1.2.840.10045.3.0.6', + 'c2pnb191v3' => '1.2.840.10045.3.0.7', + 'c2pnb191v4' => '1.2.840.10045.3.0.8', + 'c2pnb191v5' => '1.2.840.10045.3.0.9', + 'c2pnb208w1' => '1.2.840.10045.3.0.10', + 'c2pnb239v1' => '1.2.840.10045.3.0.11', + 'c2pnb239v2' => '1.2.840.10045.3.0.12', + 'c2pnb239v3' => '1.2.840.10045.3.0.13', + 'c2pnb239v4' => '1.2.840.10045.3.0.14', + 'c2pnb239v5' => '1.2.840.10045.3.0.15', + 'c2pnb272w1' => '1.2.840.10045.3.0.16', + 'c2pnb304w1' => '1.2.840.10045.3.0.17', + 'c2pnb359v1' => '1.2.840.10045.3.0.18', + 'c2pnb368w1' => '1.2.840.10045.3.0.19', + 'c2pnb431r1' => '1.2.840.10045.3.0.20', + 'primeCurve' => '1.2.840.10045.3.1', + 'prime192v1' => '1.2.840.10045.3.1.1', + 'prime192v2' => '1.2.840.10045.3.1.2', + 'prime192v3' => '1.2.840.10045.3.1.3', + 'prime239v1' => '1.2.840.10045.3.1.4', + 'prime239v2' => '1.2.840.10045.3.1.5', + 'prime239v3' => '1.2.840.10045.3.1.6', + 'prime256v1' => '1.2.840.10045.3.1.7', + 'id-RSAES-OAEP' => '1.2.840.113549.1.1.7', + 'id-pSpecified' => '1.2.840.113549.1.1.9', + 'id-RSASSA-PSS' => '1.2.840.113549.1.1.10', + 'id-mgf1' => '1.2.840.113549.1.1.8', + 'sha224WithRSAEncryption' => '1.2.840.113549.1.1.14', + 'sha256WithRSAEncryption' => '1.2.840.113549.1.1.11', + 'sha384WithRSAEncryption' => '1.2.840.113549.1.1.12', + 'sha512WithRSAEncryption' => '1.2.840.113549.1.1.13', + 'id-sha224' => '2.16.840.1.101.3.4.2.4', + '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-GostR3411-94-with-GostR3410-94' => '1.2.643.2.2.4', + 'id-GostR3411-94-with-GostR3410-2001' => '1.2.643.2.2.3', + 'id-GostR3410-2001' => '1.2.643.2.2.20', + 'id-GostR3410-94' => '1.2.643.2.2.19', // 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', + 'netscape' => '2.16.840.1.113730', + 'netscape-cert-extension' => '2.16.840.1.113730.1', + 'netscape-cert-type' => '2.16.840.1.113730.1.1', + 'netscape-comment' => '2.16.840.1.113730.1.13', + 'netscape-ca-policy-url' => '2.16.840.1.113730.1.8', // 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', + 'id-pe-logotype' => '1.3.6.1.5.5.7.1.12', + 'entrustVersInfo' => '1.2.840.113533.7.65.0', + 'verisignPrivate' => '2.16.840.1.113733.1.6.9', // 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 + 'pkcs-9-at-unstructuredName' => '1.2.840.113549.1.9.2', // PKCS #9 unstructured name + 'pkcs-9-at-challengePassword' => '1.2.840.113549.1.9.7', // Challenge password for certificate revocations + 'pkcs-9-at-extensionRequest' => '1.2.840.113549.1.9.14' // Certificate extension request ]); } } diff --git a/phpseclib/Math/BigInteger.php b/phpseclib/Math/BigInteger.php index 0ba13dad..1ca135aa 100644 --- a/phpseclib/Math/BigInteger.php +++ b/phpseclib/Math/BigInteger.php @@ -114,16 +114,21 @@ class BigInteger implements \Serializable } /** - * Converts base-2, base-10, base-16, and binary strings (base-256) to BigIntegers. + * Returns the engine type * - * 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. - * - * @param $x integer|BigInteger\Engines\Engine Base-10 number or base-$base number if $base set. - * @param int $base - * @return BigInteger + * @return string[] */ - public function __construct($x = 0, $base = 10) + public static function getEngine() + { + self::initialize_static_variables(); + + return self::$engines; + } + + /** + * Initialize static variables + */ + private static function initialize_static_variables() { if (!isset(self::$mainEngine)) { $engines = [ @@ -140,6 +145,21 @@ class BigInteger implements \Serializable } } } + } + + /** + * 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. + * + * @param $x integer|BigInteger\Engines\Engine Base-10 number or base-$base number if $base set. + * @param int $base + * @return BigInteger + */ + public function __construct($x = 0, $base = 10) + { + self::initialize_static_variables(); if ($x instanceof self::$mainEngine) { $this->value = clone $x; @@ -569,6 +589,8 @@ class BigInteger implements \Serializable */ public static function minMaxBits($bits) { + self::initialize_static_variables(); + $class = self::$mainEngine; extract($class::minMaxBits($bits)); /** @var BigInteger $min @@ -610,6 +632,8 @@ class BigInteger implements \Serializable */ public static function random($size) { + self::initialize_static_variables(); + $class = self::$mainEngine; return new static($class::random($size)); } @@ -624,6 +648,8 @@ class BigInteger implements \Serializable */ public static function randomPrime($size) { + self::initialize_static_variables(); + $class = self::$mainEngine; return new static($class::randomPrime($size)); } @@ -746,4 +772,92 @@ class BigInteger implements \Serializable { $this->value = clone $this->value; } + + /** + * Is Odd? + * + * @return boolean + */ + public function isOdd() + { + return $this->value->isOdd(); + } + + /** + * Tests if a bit is set + * + * @param int $x + * @return boolean + */ + public function testBit($x) + { + return $this->value->testBit($x); + } + + /** + * Is Negative? + * + * @return boolean + */ + public function isNegative() + { + return $this->value->isNegative(); + } + + /** + * Negate + * + * Given $k, returns -$k + * + * @return BigInteger + */ + public function negate() + { + return new static($this->value->negate()); + } + + /** + * 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 BigInteger $r + * @return int + */ + public static function scan1divide(BigInteger $r) + { + $class = self::$mainEngine; + return $class::scan1divide($r->value); + } + + /** + * 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() + { + $func = $this->value->createRecurringModuloFunction(); + return function(BigInteger $x) use ($func) { + return new static($func($x->value)); + }; + } + + /** + * Bitwise Split + * + * Splits BigInteger's into chunks of $split bits + * + * @param int $split + * @return \phpseclib\Math\BigInteger[] + */ + public function bitwise_split($split) + { + return array_map(function($val) { + return new static($val); + }, $this->value->bitwise_split($split)); + } } \ No newline at end of file diff --git a/phpseclib/Math/BigInteger/Engines/BCMath.php b/phpseclib/Math/BigInteger/Engines/BCMath.php index d4f2e824..4ed6b05c 100644 --- a/phpseclib/Math/BigInteger/Engines/BCMath.php +++ b/phpseclib/Math/BigInteger/Engines/BCMath.php @@ -353,7 +353,7 @@ class BCMath extends Engine public function abs() { $temp = new static(); - $temp->value = bccomp($this->value, '0', 0) < 0 ? + $temp->value = strlen($this->value) && $this->value[0] == '-' ? substr($this->value, 1) : $this->value; @@ -537,6 +537,8 @@ class BCMath extends Engine */ protected function normalize(BCMath $result) { + unset($result->reduce); + $result->precision = $this->precision; $result->bitmask = $this->bitmask; @@ -588,7 +590,7 @@ class BCMath extends Engine */ protected function make_odd() { - if ($this->value[strlen($this->value) - 1] % 2 == 0) { + if (!$this->isOdd()) { $this->value = bcadd($this->value, '1'); } } @@ -631,7 +633,7 @@ class BCMath extends Engine * @param BCMath $r * @return int */ - protected static function scan1divide(BCMath $r) + public static function scan1divide(BCMath $r) { $r_value = &$r->value; $s = 0; @@ -704,4 +706,60 @@ class BCMath extends Engine $temp = parent::setBitmask($bits); return $temp->add(static::$one); } + + /** + * Is Odd? + * + * @return boolean + */ + public function isOdd() + { + return $this->value[strlen($this->value) - 1] % 2 == 1; + } + + /** + * Tests if a bit is set + * + * @return boolean + */ + public function testBit($x) + { + return bccomp( + bcmod($this->value, bcpow('2', $x + 1, 0), 0), + bcpow('2', $x, 0), + 0 + ) >= 0; + } + + /** + * Is Negative? + * + * @return boolean + */ + 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; + } } \ No newline at end of file diff --git a/phpseclib/Math/BigInteger/Engines/BCMath/Base.php b/phpseclib/Math/BigInteger/Engines/BCMath/Base.php index 09c002f0..d2c1314d 100644 --- a/phpseclib/Math/BigInteger/Engines/BCMath/Base.php +++ b/phpseclib/Math/BigInteger/Engines/BCMath/Base.php @@ -68,11 +68,6 @@ abstract class Base extends BCMath return $x->normalize($temp); } - if ($e->value == '1') { - $temp = bcdiv($x, $n); - return $x->normalize($temp); - } - return $x->normalize(static::slidingWindow($x, $e, $n, $class)); } diff --git a/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php b/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php index ae1e896a..2b5a387f 100644 --- a/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php +++ b/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php @@ -59,6 +59,11 @@ abstract class EvalBarrett extends Base */ protected static function generateCustomReduction(BCMath $m, $class) { + if (isset($n->reduce)) { + self::$custom_reduction = $n->reduce; + return $n->reduce; + } + $m_length = strlen($m); if ($m_length < 5) { @@ -106,5 +111,7 @@ abstract class EvalBarrett extends Base eval('$func = function ($n) { ' . $code . '};'); self::$custom_reduction = $func; + + return $func; } } \ No newline at end of file diff --git a/phpseclib/Math/BigInteger/Engines/Engine.php b/phpseclib/Math/BigInteger/Engines/Engine.php index aa0c793d..f19281d4 100644 --- a/phpseclib/Math/BigInteger/Engines/Engine.php +++ b/phpseclib/Math/BigInteger/Engines/Engine.php @@ -19,6 +19,7 @@ use ParagonIE\ConstantTime\Hex; use phpseclib\Exception\BadConfigurationException; use phpseclib\Crypt\Random; use phpseclib\Math\BigInteger; +use phpseclib\Common\Functions\Strings; /** * Base Engine. @@ -57,6 +58,13 @@ abstract class Engine implements \Serializable */ protected $bitmask = false; + /** + * Recurring Modulo Function + * + * @var callable + */ + protected $reduce; + /** * Default constructor * @@ -137,6 +145,9 @@ abstract class Engine implements \Serializable // (?<=^|-)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('#(?value)) { + $this->value = '0'; + } static::initialize($base); break; case -2: @@ -147,22 +158,12 @@ abstract class Engine implements \Serializable } $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 + $temp = new static(Strings::bits2bin($x), 128 * $base); // ie. either -16 or +16 $this->value = $temp->value; - $this->is_negative = $temp->is_negative; + if ($temp->is_negative) { + $this->is_negative = true; + } break; default: @@ -239,14 +240,9 @@ abstract class Engine implements \Serializable */ public function toBits($twos_compliment = false) { - $hex = $this->toHex($twos_compliment); - $bits = ''; - for ($i = strlen($hex) - 8, $start = strlen($hex) & 7; $i >= $start; $i-=8) { - $bits = str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT) . $bits; - } - if ($start) { // hexdec('') == 0 - $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8, '0', STR_PAD_LEFT) . $bits; - } + $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) { @@ -1058,4 +1054,106 @@ abstract class Engine implements \Serializable } 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, 'reduce') ? + '\\phpseclib\\Math\\BigInteger\\Engines\\' . static::ENGINE_DIR . '\\DefaultEngine' : + static::$modexpEngine; + if (method_exists($fqengine, 'generateCustomReduction')) { + $func = $fqengine::generateCustomReduction($this, static::class); + $this->reduce = eval('return function(' . static::class . ' $x) use ($func, $class) { + $r = new $class(); + $r->value = $func($x->value); + return $r; + };'); + return clone $this->reduce; + } + $n = $this->value; + $this->reduce = eval('return function(' . static::class . ' $x) use ($n, $fqengine, $class) { + $r = new $class(); + $r->value = $fqengine::reduce($x->value, $n, $class); + return $r; + };'); + return clone $this->reduce; + } + + /** + * Calculates the greatest common divisor and Bezout's identity. + * + * @param Engine $n + * @return Engine + */ + protected function extendedGCDHelper(Engine $n, Engine $stop = null) + { + $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 \phpseclib\Math\BigInteger\Engine[] + */ + public function bitwise_split($split) + { + if ($split < 1) { + throw new \RuntimeException('Offset must be greater than 1'); + } + + $mask = static::$one->bitwise_leftShift($split)->subtract(static::$one); + + $num = clone $this; + + $vals = []; + while (!$num->equals(static::$zero)) { + $vals[] = $num->bitwise_and($mask); + $num = $num->bitwise_rightShift($split); + } + + return array_reverse($vals); + } } \ No newline at end of file diff --git a/phpseclib/Math/BigInteger/Engines/GMP.php b/phpseclib/Math/BigInteger/Engines/GMP.php index 17696c17..148492de 100644 --- a/phpseclib/Math/BigInteger/Engines/GMP.php +++ b/phpseclib/Math/BigInteger/Engines/GMP.php @@ -158,6 +158,32 @@ class GMP extends Engine 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). * @@ -476,6 +502,8 @@ class GMP extends Engine */ protected function normalize(GMP $result) { + unset($result->reduce); + $result->precision = $this->precision; $result->bitmask = $this->bitmask; @@ -626,4 +654,81 @@ class GMP extends Engine { 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; + $this->reduce = function(GMP $x) use ($temp) { + return new GMP($x->value % $temp); + }; + return $this->reduce; + } + + /** + * 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 boolean + */ + public function isOdd() + { + return gmp_testbit($this->value, 0); + } + + /** + * Tests if a bit is set + * + * @return boolean + */ + public function testBit($x) + { + return gmp_testbit($this->value, $x); + } + + /** + * Is Negative? + * + * @return boolean + */ + 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; + } } \ No newline at end of file diff --git a/phpseclib/Math/BigInteger/Engines/PHP.php b/phpseclib/Math/BigInteger/Engines/PHP.php index b0df2a36..2b20c906 100644 --- a/phpseclib/Math/BigInteger/Engines/PHP.php +++ b/phpseclib/Math/BigInteger/Engines/PHP.php @@ -19,7 +19,7 @@ use ParagonIE\ConstantTime\Hex; use phpseclib\Exception\BadConfigurationException; /** - * pure-PHP Engine. + * Pure-PHP Engine. * * @package PHP * @author Jim Wigginton @@ -101,18 +101,6 @@ abstract class PHP extends Engine protected function initialize($base) { switch (abs($base)) { - case 256: - $x = $this->value; - $this->value = []; - - while (strlen($x)) { - $this->value[] = self::bytes2int(self::base256_rshift($x, static::BASE)); - } - - if ($this->value == [0]) { - $this->value = []; - } - break; case 16: $x = (strlen($this->value) & 1) ? '0' . $this->value : $this->value; $temp = new static(Hex::decode($x), 256); @@ -205,12 +193,9 @@ abstract class PHP extends Engine if (!count($this->value)) { return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } - $result = self::int2bytes($this->value[count($this->value) - 1]); - for ($i = count($this->value) - 2; $i >= 0; --$i) { - self::base256_lshift($result, static::BASE); - $result = $result | str_pad(self::int2bytes($this->value[$i]), strlen($result), chr(0), STR_PAD_LEFT); - } + $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) : @@ -405,7 +390,7 @@ abstract class PHP extends Engine { //if ( $x_value == $y_value ) { // return [ - // self::VALUE => $this->_square($x_value), + // self::VALUE => self::square($x_value), // self::SIGN => $x_sign != $y_value // ]; //} @@ -743,6 +728,8 @@ abstract class PHP extends Engine */ protected function normalize(PHP $result) { + unset($result->reduce); + $result->precision = $this->precision; $result->bitmask = $this->bitmask; @@ -752,7 +739,7 @@ abstract class PHP extends Engine return $result; } - $value = $this->trim($value); + $value = static::trim($value); if (!empty($result->bitmask->value)) { $length = min(count($value), count($result->bitmask->value)); @@ -801,83 +788,6 @@ abstract class PHP extends Engine return 0; } - /** - * 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 PHP $n - * @return PHP[] - * @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". - */ - protected function extendedGCDHelper(PHP $n) - { - $y = clone $n; - $x = clone $this; - $g = new static(); - $g->value = [1]; - - while (!(($x->value[0] & 1)|| ($y->value[0] & 1))) { - $x->rshift(1); - $y->rshift(1); - $g->lshift(1); - } - - $u = clone $x; - $v = clone $y; - - $a = new static(); - $b = new static(); - $c = new static(); - $d = new static(); - - $a->value = $d->value = $g->value = [1]; - $b->value = $c->value = []; - - 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 [ - 'gcd' => $this->normalize($g->multiply($v)), - 'x' => $this->normalize($c), - 'y' => $this->normalize($d) - ]; - } - /** * Absolute value. * @@ -973,46 +883,6 @@ abstract class PHP extends Engine return $value; } - /** - * Logical Right Shift - * - * Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder. - * - * @param $x String - * @param $shift Integer - * @return string - */ - private static 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)); - } - /** * Logical Right Shift * @@ -1063,18 +933,6 @@ abstract class PHP extends Engine return ltrim(pack('N', $x), chr(0)); } - /** - * Converts bytes to 32-bit integers - * - * @param string $x - * @return int - */ - private static function bytes2int($x) - { - $temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT)); - return $temp['int']; - } - /** * Array Repeat * @@ -1151,7 +1009,7 @@ abstract class PHP extends Engine $this->value[$i] = $temp; } - $this->value = $this->trim($this->value); + $this->value = static::trim($this->value); } /** @@ -1309,7 +1167,7 @@ abstract class PHP extends Engine * @param PHP $r * @return int */ - protected static function scan1divide(PHP $r) + public static function scan1divide(PHP $r) { $r_value = &$r->value; for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) { @@ -1337,7 +1195,6 @@ abstract class PHP extends Engine return new static(1); } // n^0 = 1 - $temp = clone $this; while (!$n->equals(static::$one)) { $temp = $temp->multiply($this); @@ -1346,4 +1203,172 @@ abstract class PHP extends Engine return $temp; } + + /** + * Is Odd? + * + * @return boolean + */ + public function isOdd() + { + return (bool) ($this->value[0] & 1); + } + + /** + * Tests if a bit is set + * + * @return boolean + */ + public function testBit($x) + { + $digit = 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 boolean + */ + public function isNegative() + { + return $this->is_negative; + } + + /** + * Negate + * + * Given $k, returns -$k + * + * @return BigInteger + */ + 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 \phpseclib\Math\BigInteger\Engines\PHP[] + */ + 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 \phpseclib\Math\BigInteger\Engines\PHP[] + */ + 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; + } + } else if (++$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); + } } \ No newline at end of file diff --git a/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php b/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php index ec130b69..479ac385 100644 --- a/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php +++ b/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php @@ -60,6 +60,11 @@ abstract class EvalBarrett extends Base */ protected static function generateCustomReduction(PHP $m, $class) { + if (isset($n->reduce)) { + self::$custom_reduction = $n->reduce; + return $n->reduce; + } + $m_length = count($m->value); if ($m_length < 5) { @@ -75,7 +80,7 @@ abstract class EvalBarrett extends Base eval('$func = function ($x) { ' . $code . '};'); self::$custom_reduction = $func; //self::$custom_reduction = \Closure::bind($func, $m, $class); - return; + return $func; } $lhs = new $class(); @@ -151,6 +156,8 @@ abstract class EvalBarrett extends Base self::$custom_reduction = $func; + return $func; + //self::$custom_reduction = \Closure::bind($func, $m, $class); } diff --git a/phpseclib/Math/BigInteger/Engines/PHP32.php b/phpseclib/Math/BigInteger/Engines/PHP32.php index bf071e7b..6f0a1ec7 100644 --- a/phpseclib/Math/BigInteger/Engines/PHP32.php +++ b/phpseclib/Math/BigInteger/Engines/PHP32.php @@ -92,6 +92,56 @@ class PHP32 extends PHP */ protected static $two; + /** + * 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)); + $step = count($vals) & 3; + if ($step) { + $digit>>= 2 * $step; + } + if ($step != 3) { + $digit&= static::MAX_DIGIT; + $i++; + } + $vals[] = $digit; + } + while (end($vals) === 0) { + array_pop($vals); + } + reset($vals); + } + /** * Test for engine validity * diff --git a/phpseclib/Math/BigInteger/Engines/PHP64.php b/phpseclib/Math/BigInteger/Engines/PHP64.php index 5ebcc599..5966782a 100644 --- a/phpseclib/Math/BigInteger/Engines/PHP64.php +++ b/phpseclib/Math/BigInteger/Engines/PHP64.php @@ -92,6 +92,60 @@ class PHP64 extends PHP */ protected static $two; + /** + * 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 * diff --git a/phpseclib/Math/BinaryField.php b/phpseclib/Math/BinaryField.php new file mode 100644 index 00000000..ac7fa2df --- /dev/null +++ b/phpseclib/Math/BinaryField.php @@ -0,0 +1,193 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ + +namespace phpseclib\Math; + +use ParagonIE\ConstantTime\Hex; +use phpseclib\Math\Common\FiniteField; +use phpseclib\Math\BinaryField\Integer; +use phpseclib\Common\Functions\Strings; + +/** + * Binary Finite Fields + * + * @package Math + * @author Jim Wigginton + * @access public + */ +class BinaryField extends FiniteField +{ + /** + * Instance Counter + * + * @var int + */ + private static $instanceCounter = 0; + + /** + * Keeps track of current instance + * + * @var int + */ + protected $instanceID; + + /** + * 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); + $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 object + */ + 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 object + */ + 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 integer + */ + public function getLengthInBytes() + { + return strlen(Integer::getModulo($this->instanceID)); + } + + /** + * Returns the length of the modulo in bits + * + * @return integer + */ + public function getLength() + { + return strlen(Integer::getModulo($this->instanceID)) << 3; + } + + /** + * Converts a base-2 string to a base-256 string + * + * @param string $x + * @param integer $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); + } +} \ No newline at end of file diff --git a/phpseclib/Math/BinaryField/Integer.php b/phpseclib/Math/BinaryField/Integer.php new file mode 100644 index 00000000..0bde2b07 --- /dev/null +++ b/phpseclib/Math/BinaryField/Integer.php @@ -0,0 +1,400 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ + +namespace phpseclib\Math\BinaryField; + +use phpseclib\Math\Common\FiniteField\Integer as Base; +use phpseclib\Math\BigInteger; +use phpseclib\Math\BinaryField; +use ParagonIE\ConstantTime\Hex; + +/** + * Binary Finite Fields + * + * @package Math + * @author Jim Wigginton + * @access public + */ +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 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 + */ + 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) + { + if (strcmp($x, str_pad($y, strlen($x), "\0", STR_PAD_LEFT)) < 0) { + return ['', ltrim($x, "\0")]; + } + + // 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 + * + * @return string[] + * @link https://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplication + */ + private static function polynomialMultiply($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; + } + + /** + * 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) ^ $temp; + $aux0 = $aux1; + $aux1 = $aux; + } + + $temp = new static($this->instanceID); + $temp->value = $aux1; + 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($x ^ static::$modulo[$this->instanceID]); + } + + /** + * Returns the modulo + * + * @return integer + */ + 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 Hex::encode($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 + * + * @access public + */ + public function __toString() + { + return (string) $this->toBigInteger(); + } + + /** + * __debugInfo() magic method + * + * @access public + */ + public function __debugInfo() + { + return ['value' => $this->toHex()]; + } +} \ No newline at end of file diff --git a/phpseclib/Math/Common/FiniteField.php b/phpseclib/Math/Common/FiniteField.php new file mode 100644 index 00000000..7777d88b --- /dev/null +++ b/phpseclib/Math/Common/FiniteField.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ + +namespace phpseclib\Math\Common; + +/** + * Finite Fields + * + * @package Math + * @author Jim Wigginton + * @access public + */ +abstract class FiniteField +{ +} \ No newline at end of file diff --git a/phpseclib/Math/Common/FiniteField/Integer.php b/phpseclib/Math/Common/FiniteField/Integer.php new file mode 100644 index 00000000..a4caafd8 --- /dev/null +++ b/phpseclib/Math/Common/FiniteField/Integer.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ + +namespace phpseclib\Math\Common\FiniteField; + +/** + * Finite Field Integer + * + * @package Math + * @author Jim Wigginton + * @access public + */ +abstract class Integer +{ +} \ No newline at end of file diff --git a/phpseclib/Math/PrimeField.php b/phpseclib/Math/PrimeField.php new file mode 100644 index 00000000..199c6a35 --- /dev/null +++ b/phpseclib/Math/PrimeField.php @@ -0,0 +1,116 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Math; + +use phpseclib\Math\Common\FiniteField; +use phpseclib\Math\PrimeField\Integer; + +/** + * Prime Finite Fields + * + * @package Math + * @author Jim Wigginton + * @access public + */ +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->modulo = $modulo; + + $this->instanceID = self::$instanceCounter++; + Integer::setModulo($this->instanceID, $modulo); + Integer::setRecurringModuloFunction($this->instanceID, $modulo->createRecurringModuloFunction()); + } + + /** + * Use a custom defined modular reduction function + * + * @return object + */ + public function setReduction(callable $func) + { + $this->reduce = $func->bindTo($this, $this); + } + + /** + * Returns an instance of a dynamically generated PrimeFieldInteger class + * + * @return object + */ + 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 object + */ + 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 integer + */ + public function getLengthInBytes() + { + return Integer::getModulo($this->instanceID)->getLengthInBytes(); + } + + /** + * Returns the length of the modulo in bits + * + * @return integer + */ + public function getLength() + { + return Integer::getModulo($this->instanceID)->getLength(); + } +} \ No newline at end of file diff --git a/phpseclib/Math/PrimeField/Integer.php b/phpseclib/Math/PrimeField/Integer.php new file mode 100644 index 00000000..160ca92b --- /dev/null +++ b/phpseclib/Math/PrimeField/Integer.php @@ -0,0 +1,399 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ + +namespace phpseclib\Math\PrimeField; + +use phpseclib\Math\Common\FiniteField\Integer as Base; +use phpseclib\Math\BigInteger; +use ParagonIE\ConstantTime\Hex; + +/** + * Prime Finite Fields + * + * @package Math + * @author Jim Wigginton + * @access public + */ +class Integer extends Base +{ + /** + * Holds the PrimeField's value + * + * @var \phpseclib\Math\BigInteger + */ + protected $value; + + /** + * Keeps track of current instance + * + * @var int + */ + protected $instanceID; + + /** + * Holds the PrimeField's modulo + * + * @var \phpseclib\Math\BigInteger + */ + protected static $modulo; + + /** + * Holds a pre-generated function to perform modulo reductions + * + * @var Callable + */ + protected static $reduce; + + /** + * Zero + * + * @var \phpseclib\Math\BigInteger + */ + protected static $zero; + + /** + * Default constructor + */ + public function __construct($instanceID, BigInteger $num = null) + { + $this->instanceID = $instanceID; + if (!isset($num)) { + $this->value = clone static::$zero; + } else { + $reduce = static::$reduce[$instanceID]; + $this->value = $reduce($num); + } + } + + /** + * Set the modulo for a given instance + */ + public static function setModulo($instanceID, BigInteger $modulo) + { + static::$modulo[$instanceID] = $modulo; + } + + /** + * Set the modulo for a given instance + */ + public static function setRecurringModuloFunction($instanceID, callable $function) + { + static::$reduce[$instanceID] = $function; + if (!isset(static::$zero)) { + static::$zero = new BigInteger(); + } + } + + /** + * Returns the modulo + * + * @return integer + */ + 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 + */ + 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)) { + $i = clone $one; + + while (!$t->powMod($two->pow($i), static::$modulo[$this->instanceID])->equals($one)) { + $i = $i->add($one); + } + + 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 boolean + */ + 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 object + */ + 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() + { + $length = static::$modulo[$this->instanceID]->getLengthInBytes(); + return str_pad($this->value->toBytes(), $length, "\0", STR_PAD_LEFT); + } + + /** + * Converts an Integer to a hex string (eg. base-16). + * + * @return string + */ + public function toHex() + { + return Hex::encode($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 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) > 0) { + if ($d->isOdd()) { + // start mods + $d_i[$i] = $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($d_i[$i]); + $d_i[$i] = (int) $d_i[$i]->toString(); + } else { + $d_i[$i] = 0; + } + $shift = !$d->equals(static::$zero) && $d->bitwise_and($mask)->equals(static::$zero) ? $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 string + */ + public function toBigInteger() + { + return clone $this->value; + } + + /** + * __toString() magic method + * + * @access public + */ + public function __toString() + { + return (string) $this->value; + } + + /** + * __debugInfo() magic method + * + * @access public + */ + public function __debugInfo() + { + return ['value' => $this->toHex()]; + } +} \ No newline at end of file diff --git a/tests/Unit/Crypt/AES/TestCase.php b/tests/Unit/Crypt/AES/TestCase.php index 643123a6..523a45f3 100644 --- a/tests/Unit/Crypt/AES/TestCase.php +++ b/tests/Unit/Crypt/AES/TestCase.php @@ -407,4 +407,16 @@ abstract class Unit_Crypt_AES_TestCase extends PhpseclibTestCase $this->assertEquals($plaintext, $actual); } + + /** + * @expectedException \UnexpectedValueException + */ + public function testNoKey() + { + $aes = new AES('cbc'); + $aes->setPreferredEngine($this->engine); + $aes->setIV(str_repeat('x', 16)); + + $aes->encrypt(str_repeat('a', 16)); + } } diff --git a/tests/Unit/Crypt/DSA/LoadKeyTest.php b/tests/Unit/Crypt/DSA/LoadKeyTest.php index d7277a9a..996b00f5 100644 --- a/tests/Unit/Crypt/DSA/LoadKeyTest.php +++ b/tests/Unit/Crypt/DSA/LoadKeyTest.php @@ -158,7 +158,7 @@ Syea3pSvWdBpVhWzOX4A7qbxs+bhWAQWAhQiF7sFfCtZ7oOgCb2aJ9ySC9sTug== $this->assertTrue($dsa->load($key)); $this->assertInternalType('string', "$dsa"); $this->assertSame("$dsa", $dsa->getPrivateKey()); - $this->assertInternalType('string', $dsa->getPublicKey()); + $this->assertInstanceOf(DSA::class, $dsa->getPublicKey()); $this->assertInternalType('string', $dsa->getParameters()); } diff --git a/tests/Unit/Crypt/DSA/SignatureTest.php b/tests/Unit/Crypt/DSA/SignatureTest.php index 8ba19de5..8e59a786 100644 --- a/tests/Unit/Crypt/DSA/SignatureTest.php +++ b/tests/Unit/Crypt/DSA/SignatureTest.php @@ -28,7 +28,7 @@ pQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadgzyoFyqkmmUi1kNLyixtRqh+m 2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9MTzUkQjFI9UY7kZeKAhQXiJgI kBniZHdFBAZBTE14YJUBkw== -----END DSA PRIVATE KEY-----'); - $signature = $dsa->sign($message, 'PKCS'); + $signature = $dsa->sign($message, 'ASN1'); $dsa->load('-----BEGIN PUBLIC KEY----- MIIBuDCCASwGByqGSM44BAEwggEfAoGBAOwYAcAzXpuw+XCXuNp5zhAzKdhrRguI @@ -43,8 +43,8 @@ zyoFyqkmmUi1kNLyixtRqh+m2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9M TzUkQjFI9UY7kZeK -----END PUBLIC KEY-----'); - $this->assertTrue($dsa->verify($message, $signature, 'PKCS')); - $this->assertFalse($dsa->verify('foozbar', $signature, 'PKCS')); + $this->assertTrue($dsa->verify($message, $signature, 'ASN1')); + $this->assertFalse($dsa->verify('foozbar', $signature, 'ASN1')); // openssl dgst -dss1 -sign dsa_priv.pem foo.txt > sigfile.bin $signature = '302c021456d7e7da10d1538a6cd45dcb2b0ce15c28bac03402147e973a4de1e92e8a87ed5218c797952a3f854df5'; @@ -52,8 +52,8 @@ TzUkQjFI9UY7kZeK $dsa->setHash('sha1'); - $this->assertTrue($dsa->verify("foobar\n", $signature, 'PKCS')); - $this->assertFalse($dsa->verify('foozbar', $signature, 'PKCS')); + $this->assertTrue($dsa->verify("foobar\n", $signature, 'ASN1')); + $this->assertFalse($dsa->verify('foozbar', $signature, 'ASN1')); // openssl dgst -sha256 -sign dsa_priv.pem foo.txt > sigfile.bin $signature = '302e021500b131ec2682c4c0be13e6558ba3d64929ebc0ac420215009946300a03561cef50c0a51d0cd0a2c835e798fc'; @@ -61,8 +61,8 @@ TzUkQjFI9UY7kZeK $dsa->setHash('sha256'); - $this->assertTrue($dsa->verify('abcdefghijklmnopqrstuvwxyz', $signature, 'PKCS')); - $this->assertFalse($dsa->verify('zzzz', $signature, 'PKCS')); + $this->assertTrue($dsa->verify('abcdefghijklmnopqrstuvwxyz', $signature, 'ASN1')); + $this->assertFalse($dsa->verify('zzzz', $signature, 'ASN1')); } public function testRandomSignature() @@ -83,8 +83,8 @@ pQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadgzyoFyqkmmUi1kNLyixtRqh+m 2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9MTzUkQjFI9UY7kZeKAhQXiJgI kBniZHdFBAZBTE14YJUBkw== -----END DSA PRIVATE KEY-----'); - $signature1 = $dsa->sign($message, 'PKCS'); - $signature2 = $dsa->sign($message, 'PKCS'); + $signature1 = $dsa->sign($message, 'ASN1'); + $signature2 = $dsa->sign($message, 'ASN1'); // phpseclib's DSA implementation uses a CSPRNG to generate the k parameter. // used correctly this should result in different signatures every time. @@ -93,8 +93,8 @@ kBniZHdFBAZBTE14YJUBkw== // unit test would need to be updated $this->assertNotEquals($signature1, $signature2); - $this->assertTrue($dsa->verify($message, $signature1, 'PKCS')); - $this->assertTrue($dsa->verify($message, $signature2, 'PKCS')); + $this->assertTrue($dsa->verify($message, $signature1, 'ASN1')); + $this->assertTrue($dsa->verify($message, $signature2, 'ASN1')); $signature = $dsa->sign($message, 'SSH2'); diff --git a/tests/Unit/Crypt/ECDSA/CurveTest.php b/tests/Unit/Crypt/ECDSA/CurveTest.php new file mode 100644 index 00000000..fb252d72 --- /dev/null +++ b/tests/Unit/Crypt/ECDSA/CurveTest.php @@ -0,0 +1,549 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ + +use phpseclib\Crypt\ECDSA; +use phpseclib\File\ASN1; +use phpseclib\Crypt\ECDSA\Curves\Ed448; +use phpseclib\Math\BigInteger; + +class Ed448PublicKey +{ + use phpseclib\Crypt\ECDSA\Keys\Common; + + public static function load($key, $password = '') + { + $components = ['curve' => new Ed448()]; + $components['QA'] = self::extractPoint($key, $components['curve']); + + return $components; + } +} + +class Ed448PrivateKey +{ + public static function load($key, $password = '') + { + $components = ['curve' => new Ed448()]; + $components['dA'] = $components['curve']->extractSecret($key); + $components['QA'] = $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']); + + return $components; + } +} + +class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase +{ + public function curves() + { + $curves = []; + foreach (new \DirectoryIterator(__DIR__ . '/../../../../phpseclib/Crypt/ECDSA/Curves/') as $file) { + if ($file->getExtension() != 'php') { + continue; + } + $testName = $file->getBasename('.php'); + $class = 'phpseclib\Crypt\ECDSA\Curves\\' . $testName; + $reflect = new \ReflectionClass($class); + if ($reflect->isFinal()) { + continue; + } + $curves[] = [$testName]; + } + + return $curves; + } + + public function allCurves() + { + $curves = []; + foreach (new \DirectoryIterator(__DIR__ . '/../../../../phpseclib/Crypt/ECDSA/Curves/') as $file) { + if ($file->getExtension() != 'php') { + continue; + } + $testName = $file->getBasename('.php'); + $curves[] = [$testName]; + } + + return $curves; + } + + public function curvesWithOIDs() + { + $class = new ReflectionClass('phpseclib\Crypt\ECDSA\Keys\PKCS8'); + + $initialize = $class->getMethod('initialize_static_variables'); + $initialize->setAccessible(true); + $initialize->invoke(null); + + $property = $class->getProperty('curveOIDs'); + $property->setAccessible(true); + + $curves = []; + + foreach ($property->getValue() as $curve => $oid) { + $curves[] = [$curve]; + } + + return $curves; + } + + /** + * Verify that the base points are correct and verify the finite field math + * + * @dataProvider curves + */ + public function testBasePoint($name) + { + $class = 'phpseclib\Crypt\ECDSA\Curves\\' . $name; + $curve = new $class; + $this->assertTrue($curve->verifyPoint($curve->getBasePoint()), "Failed to verify basepoint of curve $name"); + } + + /** + * Verify the correctness of the point addition / doubling / multiplication algorithms + * + * @dataProvider curves + * @requires PHP 7.0 + */ + public function testKeyGeneration($name) + { + $class = 'phpseclib\Crypt\ECDSA\Curves\\' . $name; + $curve = new $class; + $dA = $curve->createRandomMultiplier(); + $QA = $curve->multiplyPoint($curve->getBasePoint(), $dA); + $this->assertTrue($curve->verifyPoint($QA), "Failed to verify point multiplication on curve $name with $dA"); + } + + /** + * Verify that OIDs have corresponding curve class + * + * @dataProvider curvesWithOIDs + */ + public function testCurveExistance($name) + { + $this->assertFileExists(__DIR__ . "/../../../../phpseclib/Crypt/ECDSA/Curves/$name.php"); + } + + /** + * Verify that all named curves have a corresponding OID + * + * @dataProvider allCurves + */ + public function testOIDExistance($name) + { + switch ($name) { + case 'Ed25519': + case 'Ed448': + self::markTestSkipped('Ed25519 / Ed448 OIDs are treated a little differently'); + } + $this->assertNotSame(ASN1::getOID($name), $name); + } + + /** + * Sign with internal engine, verify with best engine + * + * @dataProvider curves + * @requires PHP 7.0 + */ + public function testInternalSign($name) + { + // tests utilizing dataProvider only seem to output when all the dataProvider input + // has been exhausted. ie. when this test has been ran on every curve. on my local + // system it takes about 2.5m for this test to run, with the most time consuming curves + // being sect571r1 and sect571k1, which take about 30s each. on Travis CI, however, + // these curves seem to take about 2m each to run: + // https://travis-ci.org/terrafrost/phpseclib/jobs/450262081 + // because of the 10m timeout that Travis CI has this means that there's a very + // good chance the binary curves (sect*) will timeout before the test is able to + // output anything + if (substr($name, 0, 4) == 'sect') { + self::markTestSkipped('Binary field curves are being skipped'); + } + + $plaintext = 'zzz'; + + ECDSA::useInternalEngine(); + extract(ECDSA::createKey($name)); + $sig = $privatekey->sign($plaintext); + + ECDSA::useBestEngine(); + $this->assertTrue($publickey->verify($plaintext, $sig)); + } + + /** + * Sign with best engine, verify with internal engine + * + * @dataProvider curves + * @requires PHP 7.0 + */ + public function testInternalVerify($name) + { + if (substr($name, 0, 4) == 'sect') { + self::markTestSkipped('Binary field curves are being skipped'); + } + + $plaintext = 'zzz'; + + ECDSA::useBestEngine(); + extract(ECDSA::createKey($name)); + $sig = $privatekey->sign($plaintext); + + ECDSA::useInternalEngine(); + $this->assertTrue($publickey->verify($plaintext, $sig)); + } + + /** + * Ed448 test vectors from https://tools.ietf.org/html/rfc8032#section-7.4 + */ + public function testEd448TestVectors() + { + ECDSA::addFileFormat('Ed448PublicKey'); + ECDSA::addFileFormat('Ed448PrivateKey'); + + $private = pack('H*', '6c82a562cb808d10d632be89c8513ebf6c929f34ddfa8c9f63c9960ef6e348a3528c8a3fcc2f044e39a3fc5b94492f8f032e7549a20098f95b'); + $public = pack('H*', '5fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180'); + + $privateKey = new ECDSA(); + $privateKey->load($private); + + $publicKey = new ECDSA(); + $publicKey->load($public); + + $expected = '533a37f6bbe457251f023c0d88f976ae2dfb504a843e34d2074fd823d41a591f2b233f034f628281f2fd7a22ddd47d7828c59bd0a21bfd3980' . + 'ff0d2028d4b18a9df63e006c5d1c2d345b925d8dc00b4104852db99ac5c7cdda8530a113a0f4dbb61149f05a7363268c71d95808ff2e652600'; + $this->assertSame($expected, bin2hex($sig = $privateKey->sign(''))); + $this->assertTrue($publicKey->verify('', $sig)); + + $private = pack('H*', 'c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463afbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e'); + $public = pack('H*', '43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c0866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480'); + + $privateKey = new ECDSA(); + $privateKey->load($private); + + $publicKey = new ECDSA(); + $publicKey->load($public); + + $expected = '26b8f91727bd62897af15e41eb43c377efb9c610d48f2335cb0bd0087810f4352541b143c4b981b7e18f62de8ccdf633fc1bf037ab7cd77980' . + '5e0dbcc0aae1cbcee1afb2e027df36bc04dcecbf154336c19f0af7e0a6472905e799f1953d2a0ff3348ab21aa4adafd1d234441cf807c03a00'; + $this->assertSame($expected, bin2hex($sig = $privateKey->sign("\x03"))); + $this->assertTrue($publicKey->verify("\x03", $sig)); + + $publicKey->setContext(pack('H*', '666f6f')); + $privateKey->setContext(pack('H*', '666f6f')); + + $expected = 'd4f8f6131770dd46f40867d6fd5d5055de43541f8c5e35abbcd001b32a89f7d2151f7647f11d8ca2ae279fb842d607217fce6e042f6815ea00' . + '0c85741de5c8da1144a6a1aba7f96de42505d7a7298524fda538fccbbb754f578c1cad10d54d0d5428407e85dcbc98a49155c13764e66c3c00'; + $this->assertSame($expected, bin2hex($sig = $privateKey->sign("\x03"))); + $this->assertTrue($publicKey->verify("\x03", $sig)); + + $private = pack('H*', '258cdd4ada32ed9c9ff54e63756ae582fb8fab2ac721f2c8e676a72768513d939f63dddb55609133f29adf86ec9929dccb52c1c5fd2ff7e21b'); + $public = pack('H*', '3ba16da0c6f2cc1f30187740756f5e798d6bc5fc015d7c63cc9510ee3fd44adc24d8e968b6e46e6f94d19b945361726bd75e149ef09817f580'); + + $privateKey = new ECDSA(); + $privateKey->load($private); + + $publicKey = new ECDSA(); + $publicKey->load($public); + + $message = pack('H*', '64a65f3cdedcdd66811e2915'); + + $expected = '7eeeab7c4e50fb799b418ee5e3197ff6bf15d43a14c34389b59dd1a7b1b85b4ae90438aca634bea45e3a2695f1270f07fdcdf7c62b8efeaf00' . + 'b45c2c96ba457eb1a8bf075a3db28e5c24f6b923ed4ad747c3c9e03c7079efb87cb110d3a99861e72003cbae6d6b8b827e4e6c143064ff3c00'; + $this->assertSame($expected, bin2hex($sig = $privateKey->sign($message))); + $this->assertTrue($publicKey->verify($message, $sig)); + + $private = pack('H*', '7ef4e84544236752fbb56b8f31a23a10e42814f5f55ca037cdcc11c64c9a3b2949c1bb60700314611732a6c2fea98eebc0266a11a93970100e'); + $public = pack('H*', 'b3da079b0aa493a5772029f0467baebee5a8112d9d3a22532361da294f7bb3815c5dc59e176b4d9f381ca0938e13c6c07b174be65dfa578e80'); + + $privateKey = new ECDSA(); + $privateKey->load($private); + + $publicKey = new ECDSA(); + $publicKey->load($public); + + $message = pack('H*', '64a65f3cdedcdd66811e2915e7'); + + $expected = '6a12066f55331b6c22acd5d5bfc5d71228fbda80ae8dec26bdd306743c5027cb4890810c162c027468675ecf645a83176c0d7323a2ccde2d80' . + 'efe5a1268e8aca1d6fbc194d3f77c44986eb4ab4177919ad8bec33eb47bbb5fc6e28196fd1caf56b4e7e0ba5519234d047155ac727a1053100'; + $this->assertSame($expected, bin2hex($sig = $privateKey->sign($message))); + $this->assertTrue($publicKey->verify($message, $sig)); + + $private = pack('H*', 'd65df341ad13e008567688baedda8e9dcdc17dc024974ea5b4227b6530e339bff21f99e68ca6968f3cca6dfe0fb9f4fab4fa135d5542ea3f01'); + $public = pack('H*', 'df9705f58edbab802c7f8363cfe5560ab1c6132c20a9f1dd163483a26f8ac53a39d6808bf4a1dfbd261b099bb03b3fb50906cb28bd8a081f00'); + + $privateKey = new ECDSA(); + $privateKey->load($private); + + $publicKey = new ECDSA(); + $publicKey->load($public); + + $message = 'bd0f6a3747cd561bdddf4640a332461a4a30a12a434cd0bf40d766d9c6d458e5512204a30c17d1f50b5079631f64eb3112182da3005835461113718d1a5ef944'; + $message = pack('H*', $message); + + $expected = '554bc2480860b49eab8532d2a533b7d578ef473eeb58c98bb2d0e1ce488a98b18dfde9b9b90775e67f47d4a1c3482058efc9f40d2ca033a080' . + '1b63d45b3b722ef552bad3b4ccb667da350192b61c508cf7b6b5adadc2c8d9a446ef003fb05cba5f30e88e36ec2703b349ca229c2670833900'; + $this->assertSame($expected, bin2hex($sig = $privateKey->sign($message))); + $this->assertTrue($publicKey->verify($message, $sig)); + + $private = pack('H*', '2ec5fe3c17045abdb136a5e6a913e32ab75ae68b53d2fc149b77e504132d37569b7e766ba74a19bd6162343a21c8590aa9cebca9014c636df5'); + $public = pack('H*', '79756f014dcfe2079f5dd9e718be4171e2ef2486a08f25186f6bff43a9936b9bfe12402b08ae65798a3d81e22e9ec80e7690862ef3d4ed3a00'); + + $privateKey = new ECDSA(); + $privateKey->load($private); + + $publicKey = new ECDSA(); + $publicKey->load($public); + + $message = '15777532b0bdd0d1389f636c5f6b9ba734c90af572877e2d272dd078aa1e567cfa80e12928bb542330e8409f3174504107ecd5efac61ae7504dabe2a602ede89e5cca6257a7c77e27a702b3ae39fc769fc54f2395ae6a1178cab4738e543072fc1c177fe71e92e25bf03e4ecb72f47b64d0465aaea4c7fad372536c8ba516a60' . + '39c3c2a39f0e4d832be432dfa9a706a6e5c7e19f397964ca4258002f7c0541b590316dbc5622b6b2a6fe7a4abffd96105eca76ea7b98816af0748c10df048ce012d901015a51f189f3888145c03650aa23ce894c3bd889e030d565071c59f409a9981b51878fd6fc110624dcbcde0bf7a69ccce38fabdf86f3bef6044819de11'; + $message = pack('H*', $message); + + $expected = 'c650ddbb0601c19ca11439e1640dd931f43c518ea5bea70d3dcde5f4191fe53f00cf966546b72bcc7d58be2b9badef28743954e3a44a23f880' . + 'e8d4f1cfce2d7a61452d26da05896f0a50da66a239a8a188b6d825b3305ad77b73fbac0836ecc60987fd08527c1a8e80d5823e65cafe2a3d00'; + $this->assertSame($expected, bin2hex($sig = $privateKey->sign($message))); + $this->assertTrue($publicKey->verify($message, $sig)); + + $private = pack('H*', '872d093780f5d3730df7c212664b37b8a0f24f56810daa8382cd4fa3f77634ec44dc54f1c2ed9bea86fafb7632d8be199ea165f5ad55dd9ce8'); + $public = pack('H*', 'a81b2e8a70a5ac94ffdbcc9badfc3feb0801f258578bb114ad44ece1ec0e799da08effb81c5d685c0c56f64eecaef8cdf11cc38737838cf400'); + + $privateKey = new ECDSA(); + $privateKey->load($private); + + $publicKey = new ECDSA(); + $publicKey->load($public); + + $message = '6ddf802e1aae4986935f7f981ba3f0351d6273c0a0c22c9c0e8339168e675412a3debfaf435ed651558007db4384b650fcc07e3b586a27a4f7a00ac8a6fec2cd86ae4bf1570c41e6a40c931db27b2faa15a8cedd52cff7362c4e6e23daec0fbc3a79b6806e316efcc7b68119bf46bc76a26067a53f296dafdbdc11c77f7777e9' . + '72660cf4b6a9b369a6665f02e0cc9b6edfad136b4fabe723d2813db3136cfde9b6d044322fee2947952e031b73ab5c603349b307bdc27bc6cb8b8bbd7bd323219b8033a581b59eadebb09b3c4f3d2277d4f0343624acc817804728b25ab797172b4c5c21a22f9c7839d64300232eb66e53f31c723fa37fe387c7d3e50bdf9813' . + 'a30e5bb12cf4cd930c40cfb4e1fc622592a49588794494d56d24ea4b40c89fc0596cc9ebb961c8cb10adde976a5d602b1c3f85b9b9a001ed3c6a4d3b1437f52096cd1956d042a597d561a596ecd3d1735a8d570ea0ec27225a2c4aaff26306d1526c1af3ca6d9cf5a2c98f47e1c46db9a33234cfd4d81f2c98538a09ebe76998' . + 'd0d8fd25997c7d255c6d66ece6fa56f11144950f027795e653008f4bd7ca2dee85d8e90f3dc315130ce2a00375a318c7c3d97be2c8ce5b6db41a6254ff264fa6155baee3b0773c0f497c573f19bb4f4240281f0b1f4f7be857a4e59d416c06b4c50fa09e1810ddc6b1467baeac5a3668d11b6ecaa901440016f389f80acc4db9' . + '77025e7f5924388c7e340a732e554440e76570f8dd71b7d640b3450d1fd5f0410a18f9a3494f707c717b79b4bf75c98400b096b21653b5d217cf3565c9597456f70703497a078763829bc01bb1cbc8fa04eadc9a6e3f6699587a9e75c94e5bab0036e0b2e711392cff0047d0d6b05bd2a588bc109718954259f1d86678a579a3' . + '120f19cfb2963f177aeb70f2d4844826262e51b80271272068ef5b3856fa8535aa2a88b2d41f2a0e2fda7624c2850272ac4a2f561f8f2f7a318bfd5caf9696149e4ac824ad3460538fdc25421beec2cc6818162d06bbed0c40a387192349db67a118bada6cd5ab0140ee273204f628aad1c135f770279a651e24d8c14d75a605' . + '9d76b96a6fd857def5e0b354b27ab937a5815d16b5fae407ff18222c6d1ed263be68c95f32d908bd895cd76207ae726487567f9a67dad79abec316f683b17f2d02bf07e0ac8b5bc6162cf94697b3c27cd1fea49b27f23ba2901871962506520c392da8b6ad0d99f7013fbc06c2c17a569500c8a7696481c1cd33e9b14e40b82e' . + '79a5f5db82571ba97bae3ad3e0479515bb0e2b0f3bfcd1fd33034efc6245eddd7ee2086ddae2600d8ca73e214e8c2b0bdb2b047c6a464a562ed77b73d2d841c4b34973551257713b753632efba348169abc90a68f42611a40126d7cb21b58695568186f7e569d2ff0f9e745d0487dd2eb997cafc5abf9dd102e62ff66cba87'; + $message = pack('H*', $message); + + $expected = 'e301345a41a39a4d72fff8df69c98075a0cc082b802fc9b2b6bc503f926b65bddf7f4c8f1cb49f6396afc8a70abe6d8aef0db478d4c6b29700' . + '76c6a0484fe76d76b3a97625d79f1ce240e7c576750d295528286f719b413de9ada3e8eb78ed573603ce30d8bb761785dc30dbc320869e1a00'; + $this->assertSame($expected, bin2hex($sig = $privateKey->sign($message))); + $this->assertTrue($publicKey->verify($message, $sig)); + } + + /** + * Ed25519 test vectors from https://tools.ietf.org/html/rfc8032#section-7.1 (and 7.2) + */ + public function testEd25519TestVectors() + { + ECDSA::useBestEngine(); + + $private = pack('H*', '9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60'); + $public = pack('H*', 'd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a'); + + $privateKey = new ECDSA(); + // libsodium format + $privateKey->load($private . $public); + + $publicKey = new ECDSA(); + $publicKey->load($public); + + $expected = 'e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155' . + '5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b'; + $this->assertSame($expected, bin2hex($sig = $privateKey->sign(''))); + $this->assertTrue($publicKey->verify('', $sig)); + + $private = pack('H*', '4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb'); + $public = pack('H*', '3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c'); + + $privateKey = new ECDSA(); + $privateKey->load($private . $public); + + $publicKey = new ECDSA(); + $publicKey->load($public); + + $expected = '92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da' . + '085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00'; + $this->assertSame($expected, bin2hex($sig = $privateKey->sign("\x72"))); + $this->assertTrue($publicKey->verify("\x72", $sig)); + + $private = pack('H*', 'c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7'); + $public = pack('H*', 'fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025'); + + $privateKey = new ECDSA(); + $privateKey->load($private . $public); + + $publicKey = new ECDSA(); + $publicKey->load($public); + + $expected = '6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac' . + '18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a'; + $this->assertSame($expected, bin2hex($sig = $privateKey->sign("\xaf\x82"))); + $this->assertTrue($publicKey->verify("\xaf\x82", $sig)); + + $private = pack('H*', 'f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5'); + $public = pack('H*', '278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e'); + + $privateKey = new ECDSA(); + $privateKey->load($private . $public); + + $publicKey = new ECDSA(); + $publicKey->load($public); + + $message = '08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98' . + 'fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d8' . + '79de7c0046dc4996d9e773f4bc9efe5738829adb26c81b37c93a1b270b20329d' . + '658675fc6ea534e0810a4432826bf58c941efb65d57a338bbd2e26640f89ffbc' . + '1a858efcb8550ee3a5e1998bd177e93a7363c344fe6b199ee5d02e82d522c4fe' . + 'ba15452f80288a821a579116ec6dad2b3b310da903401aa62100ab5d1a36553e' . + '06203b33890cc9b832f79ef80560ccb9a39ce767967ed628c6ad573cb116dbef' . + 'efd75499da96bd68a8a97b928a8bbc103b6621fcde2beca1231d206be6cd9ec7' . + 'aff6f6c94fcd7204ed3455c68c83f4a41da4af2b74ef5c53f1d8ac70bdcb7ed1' . + '85ce81bd84359d44254d95629e9855a94a7c1958d1f8ada5d0532ed8a5aa3fb2' . + 'd17ba70eb6248e594e1a2297acbbb39d502f1a8c6eb6f1ce22b3de1a1f40cc24' . + '554119a831a9aad6079cad88425de6bde1a9187ebb6092cf67bf2b13fd65f270' . + '88d78b7e883c8759d2c4f5c65adb7553878ad575f9fad878e80a0c9ba63bcbcc' . + '2732e69485bbc9c90bfbd62481d9089beccf80cfe2df16a2cf65bd92dd597b07' . + '07e0917af48bbb75fed413d238f5555a7a569d80c3414a8d0859dc65a46128ba' . + 'b27af87a71314f318c782b23ebfe808b82b0ce26401d2e22f04d83d1255dc51a' . + 'ddd3b75a2b1ae0784504df543af8969be3ea7082ff7fc9888c144da2af58429e' . + 'c96031dbcad3dad9af0dcbaaaf268cb8fcffead94f3c7ca495e056a9b47acdb7' . + '51fb73e666c6c655ade8297297d07ad1ba5e43f1bca32301651339e22904cc8c' . + '42f58c30c04aafdb038dda0847dd988dcda6f3bfd15c4b4c4525004aa06eeff8' . + 'ca61783aacec57fb3d1f92b0fe2fd1a85f6724517b65e614ad6808d6f6ee34df' . + 'f7310fdc82aebfd904b01e1dc54b2927094b2db68d6f903b68401adebf5a7e08' . + 'd78ff4ef5d63653a65040cf9bfd4aca7984a74d37145986780fc0b16ac451649' . + 'de6188a7dbdf191f64b5fc5e2ab47b57f7f7276cd419c17a3ca8e1b939ae49e4' . + '88acba6b965610b5480109c8b17b80e1b7b750dfc7598d5d5011fd2dcc5600a3' . + '2ef5b52a1ecc820e308aa342721aac0943bf6686b64b2579376504ccc493d97e' . + '6aed3fb0f9cd71a43dd497f01f17c0e2cb3797aa2a2f256656168e6c496afc5f' . + 'b93246f6b1116398a346f1a641f3b041e989f7914f90cc2c7fff357876e506b5' . + '0d334ba77c225bc307ba537152f3f1610e4eafe595f6d9d90d11faa933a15ef1' . + '369546868a7f3a45a96768d40fd9d03412c091c6315cf4fde7cb68606937380d' . + 'b2eaaa707b4c4185c32eddcdd306705e4dc1ffc872eeee475a64dfac86aba41c' . + '0618983f8741c5ef68d3a101e8a3b8cac60c905c15fc910840b94c00a0b9d0'; + $message = pack('H*', $message); + + $expected = '0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350' . + 'aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03'; + $this->assertSame($expected, bin2hex($sig = $privateKey->sign($message))); + $this->assertTrue($publicKey->verify($message, $sig)); + + $private = pack('H*', '833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42'); + $public = pack('H*', 'ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf'); + + $privateKey = new ECDSA(); + $privateKey->load($private . $public); + + $publicKey = new ECDSA(); + $publicKey->load($public); + + $message = 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a' . + '2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f'; + $message = pack('H*', $message); // hash('sha512', 'abc') + + $expected = 'dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b589' . + '09351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704'; + $this->assertSame($expected, bin2hex($sig = $privateKey->sign($message))); + $this->assertTrue($publicKey->verify($message, $sig)); + + $private = pack('H*', '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6'); + $public = pack('H*', 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292'); + + $privateKey = new ECDSA(); + $privateKey->load($private . $public); + + $publicKey = new ECDSA(); + $publicKey->load($public); + + $privateKey->setContext("\x62\x61\x72"); + $publicKey->setContext("\x62\x61\x72"); + + $message = 'f726936d19c800494e3fdaff20b276a8'; + $message = pack('H*', $message); + + $expected = 'fc60d5872fc46b3aa69f8b5b4351d5808f92bcc044606db097abab6dbcb1aee3' . + '216c48e8b3b66431b5b186d1d28f8ee15a5ca2df6668346291c2043d4eb3e90d'; + $this->assertSame($expected, bin2hex($sig = $privateKey->sign($message))); + $this->assertTrue($publicKey->verify($message, $sig)); + + $private = pack('H*', '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6'); + $public = pack('H*', 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292'); + + $privateKey = new ECDSA(); + $privateKey->load($private . $public); + + $publicKey = new ECDSA(); + $publicKey->load($public); + + $privateKey->setContext("\x66\x6f\x6f"); + $publicKey->setContext("\x66\x6f\x6f"); + + $message = '508e9e6882b979fea900f62adceaca35'; + $message = pack('H*', $message); + + $expected = '8b70c1cc8310e1de20ac53ce28ae6e7207f33c3295e03bb5c0732a1d20dc6490' . + '8922a8b052cf99b7c4fe107a5abb5b2c4085ae75890d02df26269d8945f84b0b'; + $this->assertSame($expected, bin2hex($sig = $privateKey->sign($message))); + $this->assertTrue($publicKey->verify($message, $sig)); + + $private = pack('H*', 'ab9c2853ce297ddab85c993b3ae14bcad39b2c682beabc27d6d4eb20711d6560'); + $public = pack('H*', '0f1d1274943b91415889152e893d80e93275a1fc0b65fd71b4b0dda10ad7d772'); + + $privateKey = new ECDSA(); + $privateKey->load($private . $public); + + $publicKey = new ECDSA(); + $publicKey->load($public); + + $privateKey->setContext("\x66\x6f\x6f"); + $publicKey->setContext("\x66\x6f\x6f"); + + $message = 'f726936d19c800494e3fdaff20b276a8'; + $message = pack('H*', $message); + + $expected = '21655b5f1aa965996b3f97b3c849eafba922a0a62992f73b3d1b73106a84ad85' . + 'e9b86a7b6005ea868337ff2d20a7f5fbd4cd10b0be49a68da2b2e0dc0ad8960f'; + $this->assertSame($expected, bin2hex($sig = $privateKey->sign($message))); + $this->assertTrue($publicKey->verify($message, $sig)); + } + + public function testRandomSignature() + { + $message = 'hello, world!'; + $private = new ECDSA(); + $private->load('PuTTY-User-Key-File-2: ecdsa-sha2-nistp256 +Encryption: none +Comment: ecdsa-key-20181105 +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJEXCsWA8s18 +m25MJlVE1urbXPYFi4q8oMbb2H0kE2f5WPxizsKXRmb1J68paXQizryL9fC4FTqI +CJ1+UnaPfk0= +Private-Lines: 1 +AAAAIQDwaPlajbXY1SxhuwsUqN1CEZ5g4adsbmJsKm+ZbUVm4g== +Private-MAC: b85ca0eb7c612df5d18af85128821bd53faaa3ef'); + $public = $private->getPublicKey(); + + $signature1 = $private->sign($message, 'ASN1'); + $signature2 = $private->sign($message, 'ASN1'); + // phpseclib's ECDSA implementation uses a CSPRNG to generate the k parameter. + // used correctly this should result in different signatures every time. + // RFC6979 describes a deterministic ECDSA scheme wherein two signatures for the same + // plaintext would yield the same value so if that were to be switched to then this + // unit test would need to be updated + $this->assertNotEquals($signature1, $signature2); + + $this->assertTrue($public->verify($message, $signature1, 'ASN1')); + $this->assertTrue($public->verify($message, $signature2, 'ASN1')); + + $signature1 = $private->sign($message, 'SSH2'); + $signature2 = $private->sign($message, 'SSH2'); + $this->assertNotEquals($signature1, $signature2); + $this->assertTrue($public->verify($message, $signature1, 'SSH2')); + $this->assertTrue($public->verify($message, $signature2, 'SSH2')); + + $signature = $private->sign($message, 'Raw'); + $this->assertTrue($public->verify($message, $signature, 'Raw')); + } +} diff --git a/tests/Unit/Crypt/ECDSA/KeyTest.php b/tests/Unit/Crypt/ECDSA/KeyTest.php new file mode 100644 index 00000000..a47117a4 --- /dev/null +++ b/tests/Unit/Crypt/ECDSA/KeyTest.php @@ -0,0 +1,468 @@ + + * @copyright 2013 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ + +use phpseclib\Crypt\ECDSA; +use phpseclib\Crypt\ECDSA\Keys\PKCS1; +use phpseclib\Crypt\ECDSA\Keys\PKCS8; +use phpseclib\Crypt\ECDSA\Keys\PuTTY; +use phpseclib\Crypt\ECDSA\Keys\OpenSSH; +use phpseclib\Crypt\ECDSA\Keys\XML; + +class Unit_Crypt_ECDSA_LoadKeyTest extends PhpseclibTestCase +{ + // openssl ecparam -name secp256k1 -genkey -noout -out secp256k1.pem + public function testPKCS1PrivateKey() + { + $key = new ECDSA; + $key->load($expected = '-----BEGIN EC PRIVATE KEY----- +MHQCAQEEIEzUawcXqUsQhaEQ51JLeOIY0ddzlO2nNgwDk32ETqwkoAcGBSuBBAAK +oUQDQgAEFuVcVb9iCUhg2cknHPE+BouHGhQ39ORjMaMI3T4RfRxr6dj5HAXdEqVZ +1W94KMe30ndmTndcJ8BPeT1Dd15FdQ== +-----END EC PRIVATE KEY-----'); + $this->assertSame('secp256k1', $key->getCurve()); + //PKCS1::useNamedCurve(); + $this->assertSame($expected, $key->getPrivateKey('PKCS1')); + } + + // openssl ecparam -name secp256k1 -genkey -noout -out secp256k1.pem -param_enc explicit + public function testPKCS1PrivateKeySpecifiedCurve() + { + $key = new ECDSA; + $key->load('-----BEGIN EC PRIVATE KEY----- +MIIBEwIBAQQgFr6TF5meGfgCXDqVxoSEltGI+T94G42PPbA6/ibq+ouggaUwgaIC +AQEwLAYHKoZIzj0BAQIhAP////////////////////////////////////7///wv +MAYEAQAEAQcEQQR5vmZ++dy7rFWgYpXOhwsHApv82y3OKNlZ8oFbFvgXmEg62ncm +o8RlXaT7/A4RCKj9F7RIpoVUGZxH0I/7ENS4AiEA/////////////////////rqu +3OavSKA7v9JejNA2QUECAQGhRANCAASCTRhjXqmdbqphSdxNkfTNAOmDW5cZ5fnZ +ys0Tk4pUv/XdiMZtVCGTNsotGeFbT5X64JkP/BFi3PVqjwy2VhOc +-----END EC PRIVATE KEY-----'); + $this->assertSame('secp256k1', $key->getCurve()); + + // this key and the above key have a few small differences. + // in both keys the coefficient's are 0 and 7. in the above + // key both coefficients are encoded using a single byte. + // in the following key the coefficient's are encoded + // as 32 bytes (ie. the length of the reduction prime). + // eg. one byte null padded to 32 bytes. + // also, in the above key the cofactor (1; optional) is + // included whereas in the following key it is not + $expected = '-----BEGIN EC PRIVATE KEY----- +MIIBTgIBAQQgFr6TF5meGfgCXDqVxoSEltGI+T94G42PPbA6/ibq+ouggeAwgd0C +AQEwLAYHKoZIzj0BAQIhAP////////////////////////////////////7///wv +MEQEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAABwRBBHm+Zn753LusVaBilc6HCwcCm/zbLc4o +2VnygVsW+BeYSDradyajxGVdpPv8DhEIqP0XtEimhVQZnEfQj/sQ1LgCIQD///// +///////////////+uq7c5q9IoDu/0l6M0DZBQaFEA0IABIJNGGNeqZ1uqmFJ3E2R +9M0A6YNblxnl+dnKzROTilS/9d2Ixm1UIZM2yi0Z4VtPlfrgmQ/8EWLc9WqPDLZW +E5w= +-----END EC PRIVATE KEY-----'; + PKCS1::useSpecifiedCurve(); + $this->assertSame($expected, $key->getPrivateKey('PKCS1')); + } + + // openssl ecparam -name secp256k1 -genkey -noout -out secp256k1.pem + // openssl pkcs8 -topk8 -nocrypt -in secp256k1.pem -out secp256k1-2.pem + public function testPKCS8PrivateKey() + { + $key = new ECDSA; + $key->load($expected = '-----BEGIN PRIVATE KEY----- +MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgAYCXwnhqMT6fCIKIkQ0w +cac7QqHrn4TCQMF9a+im74WhRANCAATwCjyGuP8xQbvVjznqazL36oeAnD32I+X2 ++wscW3OmyTDpk41HaWYPh+j+BoufsSkCwf8dBRGEQbCieZbbZogy +-----END PRIVATE KEY-----'); + $this->assertSame('secp256k1', $key->getCurve()); + $this->assertSame($expected, $key->getPrivateKey('PKCS8')); + } + + // openssl ecparam -name secp256k1 -genkey -noout -out secp256k1.pem -param_enc explicit + // openssl pkcs8 -topk8 -nocrypt -in secp256k1.pem -out secp256k1-2.pem + public function testPKCS8PrivateKeySpecifiedCurve() + { + $key = new ECDSA; + $key->load('-----BEGIN PRIVATE KEY----- +MIIBIwIBADCBrgYHKoZIzj0CATCBogIBATAsBgcqhkjOPQEBAiEA//////////// +/////////////////////////v///C8wBgQBAAQBBwRBBHm+Zn753LusVaBilc6H +CwcCm/zbLc4o2VnygVsW+BeYSDradyajxGVdpPv8DhEIqP0XtEimhVQZnEfQj/sQ +1LgCIQD////////////////////+uq7c5q9IoDu/0l6M0DZBQQIBAQRtMGsCAQEE +IKFfw3vfd5pqA5SZOTFtpr7hdJoKP/rmTPMCggkAOA35oUQDQgAEnX66+UCzUW3T +/fkLGIIfZjJm5bIMwAV85LpDom2hI441JRx+/W4WqtGuW+B/LABS6ZHp+qzepThC +HsjS3Q9Pew== +-----END PRIVATE KEY-----'); + $this->assertSame('secp256k1', $key->getCurve()); + + // see testPKCS1PrivateKeySpecifiedCurve for an explanation + // of how this key and the above key differ + $expected = '-----BEGIN PRIVATE KEY----- +MIIBXgIBADCB6QYHKoZIzj0CATCB3QIBATAsBgcqhkjOPQEBAiEA//////////// +/////////////////////////v///C8wRAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBEEE +eb5mfvncu6xVoGKVzocLBwKb/NstzijZWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio +/Re0SKaFVBmcR9CP+xDUuAIhAP////////////////////66rtzmr0igO7/SXozQ +NkFBBG0wawIBAQQgoV/De993mmoDlJk5MW2mvuF0mgo/+uZM8wKCCQA4DfmhRANC +AASdfrr5QLNRbdP9+QsYgh9mMmblsgzABXzkukOibaEjjjUlHH79bhaq0a5b4H8s +AFLpken6rN6lOEIeyNLdD097 +-----END PRIVATE KEY-----'; + PKCS8::useSpecifiedCurve(); + $this->assertSame($expected, $key->getPrivateKey('PKCS8')); + } + + // openssl ecparam -name sect113r1 -genkey -noout -out sect113r1.pem + public function testBinaryPKCS1PrivateKey() + { + $key = new ECDSA; + $key->load($expected = '-----BEGIN EC PRIVATE KEY----- +MEECAQEEDwBZdP4eSzKk/uQa6jdtfKAHBgUrgQQABKEiAyAABAHqCoNb++mK5qvE +c4rCzQEuI19czqvXpEPcAWSXew== +-----END EC PRIVATE KEY-----'); + $this->assertSame('sect113r1', $key->getCurve()); + + PKCS1::useNamedCurve(); + $this->assertSame($expected, $key->getPrivateKey('PKCS1')); + } + + // openssl ecparam -name sect113r1 -genkey -noout -out sect113r1.pem -param_enc explicit + public function testBinaryPKCS1PrivateKeySpecifiedCurve() + { + $key = new ECDSA; + $key->load('-----BEGIN EC PRIVATE KEY----- +MIHNAgEBBA8AuSc4BeeyYTq9rbSDuL2ggZIwgY8CAQEwHAYHKoZIzj0BAjARAgFx +BgkqhkjOPQECAwICAQkwNwQOMIglDKbnx/5knOhYIPcEDui+5NPiJgdEGIvg6ccj +AxUAEOcjqxTWluZ2h1YVF1b+v4/LSakEHwQAnXNhbzX0qxQH1zViwQ8ApSgwJ3lY +7oTRMV7TGIYCDwEAAAAAAAAA2czsijnlbwIBAqEiAyAABAFC7c50y7uw+iuHeMCt +WwCpKNBUcVeiHme609Dv/g== +-----END EC PRIVATE KEY-----'); + $this->assertSame('sect113r1', $key->getCurve()); + + // this key and the above key have a few small differences. + // the above key has the (optional) seed for the verifiably + // random function whereas the following key does not. + // also, in the above key the cofactor (1; optional) is + // included whereas in the following key it is not + $expected = '-----BEGIN EC PRIVATE KEY----- +MIGxAgEBBA8AuSc4BeeyYTq9rbSDuL2gdzB1AgEBMBwGByqGSM49AQIwEQIBcQYJ +KoZIzj0BAgMCAgEJMCAEDjCIJQym58f+ZJzoWCD3BA7ovuTT4iYHRBiL4OnHIwQf +BACdc2FvNfSrFAfXNWLBDwClKDAneVjuhNExXtMYhgIPAQAAAAAAAADZzOyKOeVv +oSIDIAAEAULtznTLu7D6K4d4wK1bAKko0FRxV6IeZ7rT0O/+ +-----END EC PRIVATE KEY-----'; + PKCS1::useSpecifiedCurve(); + $this->assertSame($expected, $key->getPrivateKey('PKCS1')); + } + + // openssl ecparam -name sect113r1 -genkey -noout -out sect113r1.pem + // openssl pkcs8 -topk8 -nocrypt -in sect113r1.pem -out sect113r1-2.pem + // sect113r1's reduction polynomial is a trinomial + public function testBinaryPKCS8PrivateKey() + { + $key = new ECDSA; + $key->load($expected = '-----BEGIN PRIVATE KEY----- +MFECAQAwEAYHKoZIzj0CAQYFK4EEAAQEOjA4AgEBBA8A5OuqAY8HYoFOaz9mE6mh +IgMgAAQASF3rOTPXvH0QdRBvsrMBdLMf27yd8AWABrZTxvI= +-----END PRIVATE KEY-----'); + $this->assertSame('sect113r1', $key->getCurve()); + + PKCS8::useNamedCurve(); + $this->assertSame($expected, $key->getPrivateKey('PKCS8')); + } + + // openssl ecparam -name sect113r1 -genkey -noout -out sect113r1.pem -param_enc explicit + // openssl pkcs8 -topk8 -nocrypt -in sect113r1.pem -out sect113r1-2.pem + public function testBinaryPKCS8PrivateKeySpecifiedCurve() + { + $key = new ECDSA; + $key->load('-----BEGIN PRIVATE KEY----- +MIHdAgEAMIGbBgcqhkjOPQIBMIGPAgEBMBwGByqGSM49AQIwEQIBcQYJKoZIzj0B +AgMCAgEJMDcEDjCIJQym58f+ZJzoWCD3BA7ovuTT4iYHRBiL4OnHIwMVABDnI6sU +1pbmdodWFRdW/r+Py0mpBB8EAJ1zYW819KsUB9c1YsEPAKUoMCd5WO6E0TFe0xiG +Ag8BAAAAAAAAANnM7Io55W8CAQIEOjA4AgEBBA8AXtfDMRsRTx8snPbWHquhIgMg +AAQA9xdWGJ6vV23+vkdq0C8BLJVg5E3amMyf/5keGa4= +-----END PRIVATE KEY-----'); + $this->assertSame('sect113r1', $key->getCurve()); + + // see testBinaryPKCS1PrivateKeySpecifiedCurve() for an + // explanation of the differences between the above key + // and the following key + $expected = '-----BEGIN PRIVATE KEY----- +MIHCAgEAMIGABgcqhkjOPQIBMHUCAQEwHAYHKoZIzj0BAjARAgFxBgkqhkjOPQEC +AwICAQkwIAQOMIglDKbnx/5knOhYIPcEDui+5NPiJgdEGIvg6ccjBB8EAJ1zYW81 +9KsUB9c1YsEPAKUoMCd5WO6E0TFe0xiGAg8BAAAAAAAAANnM7Io55W8EOjA4AgEB +BA8AXtfDMRsRTx8snPbWHquhIgMgAAQA9xdWGJ6vV23+vkdq0C8BLJVg5E3amMyf +/5keGa4= +-----END PRIVATE KEY-----'; + PKCS8::useSpecifiedCurve(); + $this->assertSame($expected, $key->getPrivateKey('PKCS8')); + } + + // openssl ecparam -name sect131r1 -genkey -noout -out sect131r1.pem -param_enc explicit + // sect131r1's reduction polynomial is a pentanomial + public function testBinaryPentanomialPKCS1PrivateKey() + { + $key = new ECDSA; + $key->load('-----BEGIN EC PRIVATE KEY----- +MIHoAgEBBBECPEK9NCISWf2riBsORoTM+6CBpzCBpAIBATAlBgcqhkjOPQECMBoC +AgCDBgkqhkjOPQECAwMwCQIBAgIBAwIBCDA9BBEHoRsJp2tWIURBj/P/jCVwuAQR +AhfAVhCIS2O5xscpFnj500EDFQBNaW5naHVhUXWYW9OtutohtDqX4gQjBACBuvkf +35gzxA+cGBNDY4OZB4xufqOMAB9zyBNLG0754VACEQQAAAAAAAAAAjEjlTqUZLVN +AgECoSYDJAAEBEIolGjo5lnsYqNagqYPOaEGOglkllDO2aWPtB6n+x/WXw== +-----END EC PRIVATE KEY-----'); + $this->assertSame('sect131r1', $key->getCurve()); + + // see testBinaryPKCS1PrivateKeySpecifiedCurve() for an + // explanation of the differences between the above key + // and the following key + $expected = '-----BEGIN EC PRIVATE KEY----- +MIHOAgEBBBECPEK9NCISWf2riBsORoTM+6CBjTCBigIBATAlBgcqhkjOPQECMBoC +AgCDBgkqhkjOPQECAwMwCQIBAgIBAwIBCDAmBBEHoRsJp2tWIURBj/P/jCVwuAQR +AhfAVhCIS2O5xscpFnj500EEIwQAgbr5H9+YM8QPnBgTQ2ODmQeMbn6jjAAfc8gT +SxtO+eFQAhEEAAAAAAAAAAIxI5U6lGS1TaEmAyQABARCKJRo6OZZ7GKjWoKmDzmh +BjoJZJZQztmlj7Qep/sf1l8= +-----END EC PRIVATE KEY-----'; + PKCS1::useSpecifiedCurve(); + $this->assertSame($expected, $key->getPrivateKey('PKCS1')); + } + + // from https://tools.ietf.org/html/draft-ietf-curdle-pkix-07#section-10.1 + public function testEd25519PublicKey() + { + $key = new ECDSA; + $key->load('-----BEGIN PUBLIC KEY----- +MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE= +-----END PUBLIC KEY-----'); + $this->assertSame('Ed25519', $key->getCurve()); + + // in the above key AlgorithmIdentifier has a single "child". in the + // following key it has two. The second one is ("optional") NULL. + // https://security.stackexchange.com/q/110330/15922 elaborates on + // why phpseclib is encoding the NULL as opposed to omitting it. + $expected = '-----BEGIN PUBLIC KEY----- +MCwwBwYDK2VwBQADIQC/RAlphM3+hUG6wWfcO5bIUIaqMLa2ywxcOK1wMWZhgA== +-----END PUBLIC KEY-----'; + $this->assertSame($expected, $key->getPublicKey('PKCS8')); + } + + // from https://tools.ietf.org/html/draft-ietf-curdle-pkix-07#section-10.3 + public function testEd25519PrivateKey() + { + // without public key (public key should be derived) + $key = new ECDSA; + $key->load('-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC +-----END PRIVATE KEY-----'); + $this->assertSame('Ed25519', $key->getCurve()); + $this->assertSame('Ed25519', $key->getPublicKey()->getCurve()); + + // with public key + $key = new ECDSA; + $key->load('-----BEGIN PRIVATE KEY----- +MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC +oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB +Z9w7lshQhqowtrbLDFw4rXAxZuE= +-----END PRIVATE KEY-----'); + $this->assertSame('Ed25519', $key->getCurve()); + $this->assertSame('Ed25519', $key->getPublicKey()->getCurve()); + + // the above key not only omits NULL - it also includes a + // unstructuredName attribute with a value of "Curdle Chairs" + // that the following key does not have + $expected = '-----BEGIN PRIVATE KEY----- +MFICAQEwBwYDK2VwBQAEIgQg1O5y2/kTWErVttjx92n4rTr+fCjL8dT74Jeoj0R1 +WEKBIBm/RAlphM3+hUG6wWfcO5bIUIaqMLa2ywxcOK1wMWbh +-----END PRIVATE KEY-----'; + $this->assertSame($expected, $key->getPrivateKey('PKCS8')); + } + + public function testPuTTYnistp256() + { + $key = new ECDSA; + $key->load($expected = 'PuTTY-User-Key-File-2: ecdsa-sha2-nistp256 +Encryption: none +Comment: ecdsa-key-20181105 +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJEXCsWA8s18 +m25MJlVE1urbXPYFi4q8oMbb2H0kE2f5WPxizsKXRmb1J68paXQizryL9fC4FTqI +CJ1+UnaPfk0= +Private-Lines: 1 +AAAAIQDwaPlajbXY1SxhuwsUqN1CEZ5g4adsbmJsKm+ZbUVm4g== +Private-MAC: b85ca0eb7c612df5d18af85128821bd53faaa3ef +'); + $this->assertSame('nistp256', $key->getCurve()); + + PuTTY::setComment('ecdsa-key-20181105'); + $this->assertSame($expected, $key->getPrivateKey('PuTTY')); + + $key = new ECDSA; + $key->load($expected = 'ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJEXCsWA8s18m25MJlVE1urbXPYFi4q8oMbb2H0kE2f5WPxizsKXRmb1J68paXQizryL9fC4FTqICJ1+UnaPfk0= ecdsa-key-20181105'); + $this->assertSame('nistp256', $key->getCurve()); + + OpenSSH::setComment('ecdsa-key-20181105'); + $this->assertSame($expected, $key->getPublicKey('OpenSSH')); + } + + public function testPuTTYnistp384() + { + $key = new ECDSA; + $key->load($expected = 'PuTTY-User-Key-File-2: ecdsa-sha2-nistp384 +Encryption: none +Comment: ecdsa-key-20181105 +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBOI53wHG3Cdc +AJZq5PXWZAEAxxsNVFQlQgOX9toWEOgqQF5LbK2nWLKRvaHMzocUXaTYZDccSS0A +TZFPT3j1Er1LU9cu4PHpyS07v262jdzkxIvKCPcAeISuV80MC7rHog== +Private-Lines: 2 +AAAAMQCEMkGMDg6N7bUqdvLXe0YmY4qBSi8hmAuMvU38RDoVFVmV+R4RYmMueyrX +be9Oyus= +Private-MAC: 97a990a3d5f6b8f268d4be9c4ab9ebfd8fa79849 +'); + $this->assertSame('nistp384', $key->getCurve()); + + PuTTY::setComment('ecdsa-key-20181105'); + $this->assertSame($expected, $key->getPrivateKey('PuTTY')); + + $key = new ECDSA; + $key->load($expected = 'ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBOI53wHG3CdcAJZq5PXWZAEAxxsNVFQlQgOX9toWEOgqQF5LbK2nWLKRvaHMzocUXaTYZDccSS0ATZFPT3j1Er1LU9cu4PHpyS07v262jdzkxIvKCPcAeISuV80MC7rHog== ecdsa-key-20181105'); + $this->assertSame('nistp384', $key->getCurve()); + + OpenSSH::setComment('ecdsa-key-20181105'); + $this->assertSame($expected, $key->getPublicKey('OpenSSH')); + + } + + public function testPuTTYnistp521() + { + $key = new ECDSA; + $key->load($expected = 'PuTTY-User-Key-File-2: ecdsa-sha2-nistp521 +Encryption: none +Comment: ecdsa-key-20181105 +Public-Lines: 4 +AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAF1Eg0MjaJw +ooFj6HCNh4RWbvmQRY+sdczJyBdT3EaTc/6IUcCfW7w7rAeRp2CDdE9RlAVD8IuL +qW7DJH06Xeov8wBO5G6jUqXu0rlHsOSiC6VcCxBJuWVNB1IorHnS7PX0f6HdLlIE +me73P77drqpn5YY0XLtP6hFrF7H5XfCxpNyaJA== +Private-Lines: 2 +AAAAQgHJl8/dIArolFymdzhagXCfd2l8UF3CQXWGVGDQ0R04nnntlyztYiVdRXXK +r84NnzS7dJcAsR9YaUOZ69NRKNiUAQ== +Private-MAC: 6d49ce289b85549a43d74422dd8bb3ba8798c72c +'); + $this->assertSame('nistp521', $key->getCurve()); + + PuTTY::setComment('ecdsa-key-20181105'); + $this->assertSame($expected, $key->getPrivateKey('PuTTY')); + + $key = new ECDSA; + $key->load($expected = 'ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAF1Eg0MjaJwooFj6HCNh4RWbvmQRY+sdczJyBdT3EaTc/6IUcCfW7w7rAeRp2CDdE9RlAVD8IuLqW7DJH06Xeov8wBO5G6jUqXu0rlHsOSiC6VcCxBJuWVNB1IorHnS7PX0f6HdLlIEme73P77drqpn5YY0XLtP6hFrF7H5XfCxpNyaJA== ecdsa-key-20181105'); + $this->assertSame('nistp521', $key->getCurve()); + + OpenSSH::setComment('ecdsa-key-20181105'); + $this->assertSame($expected, $key->getPublicKey('OpenSSH')); + } + + public function testPuTTYed25519() + { + $key = new ECDSA; + $key->load($expected = 'PuTTY-User-Key-File-2: ssh-ed25519 +Encryption: none +Comment: ed25519-key-20181105 +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAIC6I6RyYAqtBcWXws9EDqGbhFtc5rKG4NMn/G7te +mQtu +Private-Lines: 1 +AAAAIAHu1uI7dxFzo/SleEI2CekXKmgqlXwOgvfaRWxiX4Jd +Private-MAC: 8a06821a1c8b8b40fc40f876e543c4ea3fb81bb9 +'); + $this->assertSame('Ed25519', $key->getCurve()); + + PuTTY::setComment('ed25519-key-20181105'); + $this->assertSame($expected, $key->getPrivateKey('PuTTY')); + + $key = new ECDSA; + $key->load($expected = 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC6I6RyYAqtBcWXws9EDqGbhFtc5rKG4NMn/G7temQtu ed25519-key-20181105'); + $this->assertSame('Ed25519', $key->getCurve()); + + OpenSSH::setComment('ed25519-key-20181105'); + $this->assertSame($expected, $key->getPublicKey('OpenSSH')); + } + + public function testlibsodium() + { + if (!function_exists('sodium_crypto_sign_keypair')) { + self::markTestSkipped('libsodium extension is not available.'); + } + + $kp = sodium_crypto_sign_keypair(); + + $key = new ECDSA; + $key->load($expected = sodium_crypto_sign_secretkey($kp)); + $this->assertSame('Ed25519', $key->getCurve()); + $this->assertSame($expected, $key->getPrivateKey('libsodium')); + + $key = new ECDSA; + $key->load($expected = sodium_crypto_sign_publickey($kp)); + $this->assertSame('Ed25519', $key->getCurve()); + $this->assertSame($expected, $key->getPublicKey('libsodium')); + } + + // ssh-keygen -t ed25519 + public function testOpenSSHPrivateKey() + { + $key = new ECDSA; + $key->load('-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACCpm7dS1/WDTW+uuhp2+aFLPKaJle6+oJqDGLXhlQAX4AAAAJg8TmN5PE5j +eQAAAAtzc2gtZWQyNTUxOQAAACCpm7dS1/WDTW+uuhp2+aFLPKaJle6+oJqDGLXhlQAX4A +AAAEDltCTSbrr42IS4hhkS6ly0W2XItRQwxjLT+03bIyA+V6mbt1LX9YNNb666Gnb5oUs8 +pomV7r6gmoMYteGVABfgAAAAD3ZhZ3JhbnRAdmFncmFudAECAwQFBg== +-----END OPENSSH PRIVATE KEY-----'); + $this->assertSame('Ed25519', $key->getCurve()); + + // testing this key is a little difficult because of this format's + // two back to back checkint fields. both fields correspond to the + // same randomly generated number. ostensibly this let's you verify + // successful decryption of encrypted keys but phpseclib doesn't + // support encrypted keys + // none-the-less, because of the randomized component we can't easily + // see if the key string is equal to another known string + $key2 = new ECDSA; + $key2->load($key->getPrivateKey('OpenSSH')); + $this->assertSame('Ed25519', $key2->getCurve()); + } + + // from https://www.w3.org/TR/xmldsig-core/#sec-RFC4050Compat + public function testXMLKey() + { + $key = new ECDSA; + $key->load($orig = ' + + + + + + + +'); + $this->assertSame('nistp256', $key->getCurve()); + + XML::enableRFC4050Syntax(); + + $dom = new DOMDocument(); + $dom->preserveWhiteSpace = false; + $dom->loadXML($orig); + $expected = $dom->C14N(); + + //$dom = new DOMDocument(); + //$dom->preserveWhiteSpace = false; + $dom->loadXML($key->getPublicKey('XML')); + $actual = $dom->C14N(); + + $this->assertSame($expected, $actual); + } + + public static function assertSame($expected, $actual, $message = '') + { + $expected = str_replace("\r\n", "\n", $expected); + $actual = str_replace("\r\n", "\n", $actual); + return parent::assertSame($expected, $actual, $message); + } +} diff --git a/tests/Unit/Crypt/RSA/CreateKeyTest.php b/tests/Unit/Crypt/RSA/CreateKeyTest.php index 20107991..761ed4e5 100644 --- a/tests/Unit/Crypt/RSA/CreateKeyTest.php +++ b/tests/Unit/Crypt/RSA/CreateKeyTest.php @@ -38,7 +38,7 @@ class Unit_Crypt_RSA_CreateKeyTest extends PhpseclibTestCase public function testMultiPrime() { - RSA::setPreferredEngine(RSA::ENGINE_INTERNAL); + RSA::useInternalEngine(); RSA::setSmallestPrime(256); extract(RSA::createKey(1024)); $this->assertInstanceOf('\phpseclib\Crypt\RSA', $privatekey); @@ -62,6 +62,6 @@ class Unit_Crypt_RSA_CreateKeyTest extends PhpseclibTestCase $rsa->load($rsa->getPublicKey()); $this->assertTrue($rsa->verify('zzz', $signature)); - RSA::setPreferredEngine(RSA::ENGINE_OPENSSL); + RSA::useBestEngine(); } } diff --git a/tests/Unit/Crypt/RSA/LoadKeyTest.php b/tests/Unit/Crypt/RSA/LoadKeyTest.php index c4ea67c4..200145c7 100644 --- a/tests/Unit/Crypt/RSA/LoadKeyTest.php +++ b/tests/Unit/Crypt/RSA/LoadKeyTest.php @@ -9,10 +9,17 @@ use phpseclib\Crypt\RSA; use phpseclib\Crypt\RSA\Keys\PKCS1; use phpseclib\Crypt\RSA\Keys\PKCS8; use phpseclib\Crypt\RSA\Keys\PuTTY; +use phpseclib\Crypt\RSA\Keys\OpenSSH; use phpseclib\Math\BigInteger; class Unit_Crypt_RSA_LoadKeyTest extends PhpseclibTestCase { + public static function setUpBeforeClass() + { + PuTTY::setComment('phpseclib-generated-key'); + OpenSSH::setComment('phpseclib-generated-key'); + } + public function testBadKey() { $rsa = new RSA(); @@ -208,7 +215,7 @@ Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB -----END RSA PUBLIC KEY-----'; $this->assertTrue($rsa->load($key)); - $this->assertInternalType('string', $rsa->getPublicKey()); + $this->assertInstanceOf(RSA::class, $rsa->getPublicKey()); $this->assertFalse($rsa->getPrivateKey()); } @@ -227,7 +234,7 @@ ZQIDAQAB -----END PUBLIC KEY-----'; $this->assertTrue($rsa->load($key)); - $this->assertInternalType('string', $rsa->getPublicKey()); + $this->assertInstanceOf(RSA::class, $rsa->getPublicKey()); $this->assertFalse($rsa->getPrivateKey()); } @@ -241,7 +248,7 @@ ZQIDAQAB 'phpseclib-generated-key'; $this->assertTrue($rsa->load($key)); - $this->assertInternalType('string', $rsa->getPublicKey()); + $this->assertInstanceOf(RSA::class, $rsa->getPublicKey()); $this->assertFalse($rsa->getPrivateKey()); } @@ -393,6 +400,7 @@ Private-MAC: 03e2cb74e1d67652fbad063d2ed0478f31bdf256 $rsa->setPrivateKeyFormat('PuTTY'); $key2 = (string) $rsa; + OpenSSH::setComment('ecdsa-key-20181105'); $this->assertSame($key, $key2); } @@ -936,7 +944,7 @@ Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB -----END RSA PUBLIC KEY-----'; $this->assertTrue($rsa->load($key)); - $this->assertInternalType('string', $rsa->getPublicKey()); + $this->assertInstanceOf(RSA::class, $rsa->getPublicKey()); $this->assertFalse($rsa->load('zzz')); $this->assertFalse($rsa->getPublicKey()); } @@ -958,6 +966,6 @@ Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB '; $this->assertTrue($rsa->load($key)); - $this->assertInternalType('string', $rsa->getPublicKey()); + $this->assertInstanceOf(RSA::class, $rsa->getPublicKey()); } }