mirror of
https://github.com/danog/phpseclib.git
synced 2024-11-30 04:39:21 +01:00
add a new diffie-hellman key exchange class
This commit is contained in:
parent
cb1e6b285c
commit
1e7453b585
@ -81,13 +81,13 @@ abstract class AsymmetricKey
|
||||
private static $plugins = [];
|
||||
|
||||
/**
|
||||
* Supported plugins (original case)
|
||||
* Invisible plugins
|
||||
*
|
||||
* @see self::initialize_static_variables()
|
||||
* @var array
|
||||
* @access private
|
||||
*/
|
||||
private static $origPlugins = [];
|
||||
private static $invisiblePlugins = [];
|
||||
|
||||
/**
|
||||
* Supported signature formats (lower case)
|
||||
@ -137,7 +137,7 @@ abstract class AsymmetricKey
|
||||
}
|
||||
|
||||
self::loadPlugins('Keys');
|
||||
if (static::ALGORITHM != 'RSA') {
|
||||
if (static::ALGORITHM != 'RSA' && static::ALGORITHM != 'DH') {
|
||||
self::loadPlugins('Signature');
|
||||
}
|
||||
}
|
||||
@ -155,6 +155,9 @@ abstract class AsymmetricKey
|
||||
|
||||
$components = false;
|
||||
foreach (self::$plugins[static::ALGORITHM]['Keys'] as $format) {
|
||||
if (isset(self::$invisiblePlugins[static::ALGORITHM]) && in_array($format, self::$invisiblePlugins[static::ALGORITHM])) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$components = $format::load($key, $password);
|
||||
} catch (\Exception $e) {
|
||||
@ -252,7 +255,9 @@ abstract class AsymmetricKey
|
||||
continue;
|
||||
}
|
||||
self::$plugins[static::ALGORITHM][$format][strtolower($name)] = $type;
|
||||
self::$origPlugins[static::ALGORITHM][$format][] = $name;
|
||||
if ($reflect->hasConstant('IS_INVISIBLE')) {
|
||||
self::$invisiblePlugins[static::ALGORITHM][] = $type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -289,7 +294,9 @@ abstract class AsymmetricKey
|
||||
$meta = new \ReflectionClass($fullname);
|
||||
$shortname = $meta->getShortName();
|
||||
self::$plugins[static::ALGORITHM]['Keys'][strtolower($shortname)] = $fullname;
|
||||
self::$origPlugins[static::ALGORITHM]['Keys'][] = $shortname;
|
||||
if ($meta->hasConstant('IS_INVISIBLE')) {
|
||||
self::$invisiblePlugins[static::ALGORITHM] = strtolower($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
401
phpseclib/Crypt/DH.php
Normal file
401
phpseclib/Crypt/DH.php
Normal file
@ -0,0 +1,401 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Pure-PHP (EC)DH implementation
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* Here's an example of how to compute a shared secret with this library:
|
||||
* <code>
|
||||
* <?php
|
||||
* include 'vendor/autoload.php';
|
||||
*
|
||||
* $ourPrivate = \phpseclib\Crypt\DH::createKey();
|
||||
* $secret = DH::computeSecret($ourPrivate, $theirPublic);
|
||||
*
|
||||
* ?>
|
||||
* </code>
|
||||
*
|
||||
* @category Crypt
|
||||
* @package DH
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2016 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
namespace phpseclib\Crypt;
|
||||
|
||||
use phpseclib\Exception\NoKeyLoadedException;
|
||||
use phpseclib\Exception\UnsupportedOperationException;
|
||||
use phpseclib\Crypt\Common\AsymmetricKey;
|
||||
use phpseclib\Crypt\DH\PrivateKey;
|
||||
use phpseclib\Crypt\DH\PublicKey;
|
||||
use phpseclib\Crypt\DH\Parameters;
|
||||
use phpseclib\Math\BigInteger;
|
||||
|
||||
/**
|
||||
* Pure-PHP (EC)DH implementation
|
||||
*
|
||||
* @package DH
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @access public
|
||||
*/
|
||||
abstract class DH extends AsymmetricKey
|
||||
{
|
||||
/**
|
||||
* Algorithm Name
|
||||
*
|
||||
* @var string
|
||||
* @access private
|
||||
*/
|
||||
const ALGORITHM = 'DH';
|
||||
|
||||
/**
|
||||
* DH prime
|
||||
*
|
||||
* @var \phpseclib\Math\BigInteger
|
||||
* @access private
|
||||
*/
|
||||
protected $prime;
|
||||
|
||||
/**
|
||||
* DH Base
|
||||
*
|
||||
* Prime divisor of p-1
|
||||
*
|
||||
* @var \phpseclib\Math\BigInteger
|
||||
* @access private
|
||||
*/
|
||||
protected $base;
|
||||
|
||||
/**
|
||||
* Create DH parameters
|
||||
*
|
||||
* This method is a bit polymorphic. It can take any of the following:
|
||||
* - two BigInteger's (prime and base)
|
||||
* - an integer representing the size of the prime in bits (the base is assumed to be 2)
|
||||
* - a string (eg. diffie-hellman-group14-sha1)
|
||||
*
|
||||
* @access public
|
||||
* @return \phpseclib\Crypt\DH|bool
|
||||
*/
|
||||
public static function createParameters(...$args)
|
||||
{
|
||||
$params = new Parameters;
|
||||
if (count($args) == 2 && $args[0] instanceof BigInteger && $args[1] instanceof BigInteger) {
|
||||
//if (!$args[0]->isPrime()) {
|
||||
// throw new \InvalidArgumentException('The first parameter should be a prime number');
|
||||
//}
|
||||
$params->prime = $args[0];
|
||||
$params->base = $args[1];
|
||||
return $params;
|
||||
} elseif (count($args) == 1 && is_numeric($args[0])) {
|
||||
$params->prime = BigInteger::randomPrime($args[0]);
|
||||
$params->base = new BigInteger(2);
|
||||
return $params;
|
||||
} elseif (count($args) != 1 || !is_string($args[0])) {
|
||||
throw new \InvalidArgumentException('Valid parameters are either: two BigInteger\'s (prime and base), a single integer (the length of the prime; base is assumed to be 2) or a string');
|
||||
}
|
||||
switch ($args[0]) {
|
||||
// see http://tools.ietf.org/html/rfc2409#section-6.2 and
|
||||
// http://tools.ietf.org/html/rfc2412, appendex E
|
||||
case 'diffie-hellman-group1-sha1':
|
||||
$prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
|
||||
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
|
||||
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
|
||||
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
|
||||
break;
|
||||
// see http://tools.ietf.org/html/rfc3526#section-3
|
||||
case 'diffie-hellman-group14-sha1': // 2048-bit MODP Group
|
||||
case 'diffie-hellman-group14-sha256':
|
||||
$prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
|
||||
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
|
||||
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
|
||||
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
|
||||
'98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
|
||||
'9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
|
||||
'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
|
||||
'3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF';
|
||||
break;
|
||||
// see https://tools.ietf.org/html/rfc3526#section-4
|
||||
case 'diffie-hellman-group15-sha512': // 3072-bit MODP Group
|
||||
$prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
|
||||
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
|
||||
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
|
||||
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
|
||||
'98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
|
||||
'9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
|
||||
'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
|
||||
'3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' .
|
||||
'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' .
|
||||
'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' .
|
||||
'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' .
|
||||
'08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF';
|
||||
break;
|
||||
// see https://tools.ietf.org/html/rfc3526#section-5
|
||||
case 'diffie-hellman-group16-sha512': // 4096-bit MODP Group
|
||||
$prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
|
||||
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
|
||||
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
|
||||
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
|
||||
'98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
|
||||
'9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
|
||||
'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
|
||||
'3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' .
|
||||
'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' .
|
||||
'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' .
|
||||
'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' .
|
||||
'08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' .
|
||||
'88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8' .
|
||||
'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' .
|
||||
'233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' .
|
||||
'93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF';
|
||||
break;
|
||||
// see https://tools.ietf.org/html/rfc3526#section-6
|
||||
case 'diffie-hellman-group17-sha512': // 6144-bit MODP Group
|
||||
$prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
|
||||
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
|
||||
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
|
||||
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
|
||||
'98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
|
||||
'9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
|
||||
'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
|
||||
'3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' .
|
||||
'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' .
|
||||
'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' .
|
||||
'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' .
|
||||
'08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' .
|
||||
'88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8' .
|
||||
'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' .
|
||||
'233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' .
|
||||
'93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026' .
|
||||
'C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AE' .
|
||||
'B06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B' .
|
||||
'DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92EC' .
|
||||
'F032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E' .
|
||||
'59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA' .
|
||||
'CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76' .
|
||||
'F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468' .
|
||||
'043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF';
|
||||
break;
|
||||
// see https://tools.ietf.org/html/rfc3526#section-7
|
||||
case 'diffie-hellman-group18-sha512': // 8192-bit MODP Group
|
||||
$prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
|
||||
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
|
||||
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
|
||||
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
|
||||
'98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
|
||||
'9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
|
||||
'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
|
||||
'3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' .
|
||||
'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' .
|
||||
'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' .
|
||||
'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' .
|
||||
'08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' .
|
||||
'88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8' .
|
||||
'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' .
|
||||
'233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' .
|
||||
'93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026' .
|
||||
'C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AE' .
|
||||
'B06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B' .
|
||||
'DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92EC' .
|
||||
'F032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E' .
|
||||
'59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA' .
|
||||
'CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76' .
|
||||
'F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468' .
|
||||
'043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4' .
|
||||
'38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED' .
|
||||
'2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652D' .
|
||||
'E3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B' .
|
||||
'4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6' .
|
||||
'6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851D' .
|
||||
'F9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92' .
|
||||
'4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA' .
|
||||
'9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF';
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException('Invalid named prime provided');
|
||||
}
|
||||
|
||||
$params->prime = new BigInteger($prime, 16);
|
||||
$params->base = new BigInteger(2);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create public / private key pair.
|
||||
*
|
||||
* The rationale for the second parameter is described in http://tools.ietf.org/html/rfc4419#section-6.2 :
|
||||
*
|
||||
* "To increase the speed of the key exchange, both client and server may
|
||||
* reduce the size of their private exponents. It should be at least
|
||||
* twice as long as the key material that is generated from the shared
|
||||
* secret. For more details, see the paper by van Oorschot and Wiener
|
||||
* [VAN-OORSCHOT]."
|
||||
*
|
||||
* $length is in bits
|
||||
*
|
||||
* @param Parameters $params
|
||||
* @param int $length optional
|
||||
* @access public
|
||||
* @return DH\PrivateKey
|
||||
*/
|
||||
public static function createKey(Parameters $params, $length = 0)
|
||||
{
|
||||
$one = new BigInteger(1);
|
||||
if ($length) {
|
||||
$max = $one->bitwise_leftShift($length);
|
||||
$max = $max->subtract($one);
|
||||
} else {
|
||||
$max = $params->prime->subtract($one);
|
||||
}
|
||||
|
||||
$key = new PrivateKey;
|
||||
$key->prime = $params->prime;
|
||||
$key->base = $params->base;
|
||||
$key->privateKey = BigInteger::randomRange($one, $max);
|
||||
$key->publicKey = $key->base->powMod($key->privateKey, $key->prime);
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute Shared Secret
|
||||
*
|
||||
* @param PrivateKey|EC $private
|
||||
* @param PublicKey|BigInteger|string $public
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public static function computeSecret($private, $public)
|
||||
{
|
||||
if ($private instanceof PrivateKey) { // DH\PrivateKey
|
||||
switch (true) {
|
||||
case $public instanceof PublicKey:
|
||||
if (!$private->prime->equals($public->prime) || !$private->base->equals($public->base)) {
|
||||
throw new \InvalidArgumentException('The public and private key do not share the same prime and / or base numbers');
|
||||
}
|
||||
return $public->publicKey->powMod($private->privateKey, $private->prime)->toBytes(true);
|
||||
case is_string($public):
|
||||
$public = new BigInteger($public, -256);
|
||||
case $public instanceof BigInteger:
|
||||
return $public->powMod($private->privateKey, $private->prime)->toBytes(true);
|
||||
default:
|
||||
throw new \InvalidArgumentException('$public needs to be an instance of DH\PublicKey, a BigInteger or a string');
|
||||
}
|
||||
}
|
||||
|
||||
if ($private instanceof EC\PrivateKey) {
|
||||
switch (true) {
|
||||
case $public instanceof EC\PublicKey:
|
||||
$public = $public->getEncodedCoordinates();
|
||||
case is_string($public):
|
||||
$point = $private->multiply($public);
|
||||
switch ($private->getCurve()) {
|
||||
case 'Curve25519':
|
||||
case 'Curve448':
|
||||
$secret = $point;
|
||||
break;
|
||||
default:
|
||||
// according to https://www.secg.org/sec1-v2.pdf#page=33 only X is returned
|
||||
$secret = substr($point, 1, (strlen($point) - 1) >> 1);
|
||||
}
|
||||
/*
|
||||
if (($secret[0] & "\x80") === "\x80") {
|
||||
$secret = "\0$secret";
|
||||
}
|
||||
*/
|
||||
return $secret;
|
||||
default:
|
||||
throw new \InvalidArgumentException('$public needs to be an instance of EC\PublicKey or a string (an encoded coordinate)');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the key
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $password optional
|
||||
* @return AsymmetricKey
|
||||
*/
|
||||
public static function load($key, $password = false)
|
||||
{
|
||||
try {
|
||||
return EC::load($key, $password);
|
||||
} catch (NoKeyLoadedException $e) {}
|
||||
|
||||
return parent::load($key, $password);
|
||||
}
|
||||
|
||||
/**
|
||||
* OnLoad Handler
|
||||
*
|
||||
* @return bool
|
||||
* @access protected
|
||||
* @param array $components
|
||||
*/
|
||||
protected static function onLoad($components)
|
||||
{
|
||||
if (!isset($components['privateKey']) && !isset($components['publicKey'])) {
|
||||
$new = new Parameters;
|
||||
} else {
|
||||
$new = isset($components['privateKey']) ?
|
||||
new PrivateKey :
|
||||
new PublicKey;
|
||||
}
|
||||
|
||||
$new->prime = $components['prime'];
|
||||
$new->base = $components['base'];
|
||||
|
||||
if (isset($components['privateKey'])) {
|
||||
$new->privateKey = $components['privateKey'];
|
||||
}
|
||||
if (isset($components['publicKey'])) {
|
||||
$new->publicKey = $components['publicKey'];
|
||||
}
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines which hashing function should be used
|
||||
*
|
||||
* @access public
|
||||
* @param string $hash
|
||||
*/
|
||||
public function withHash($hash)
|
||||
{
|
||||
throw new UnsupportedOperationException('DH does not use a hash algorithm');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash algorithm currently being used
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function getHash()
|
||||
{
|
||||
throw new UnsupportedOperationException('DH does not use a hash algorithm');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parameters
|
||||
*
|
||||
* A public / private key is only returned if the currently loaded "key" contains an x or y
|
||||
* value.
|
||||
*
|
||||
* @see self::getPublicKey()
|
||||
* @access public
|
||||
* @param string $type optional
|
||||
* @return mixed
|
||||
*/
|
||||
public function getParameters()
|
||||
{
|
||||
$type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters');
|
||||
|
||||
$key = $type::saveParameters($this->prime, $this->base);
|
||||
return self::load($key, 'PKCS1');
|
||||
}
|
||||
}
|
83
phpseclib/Crypt/DH/Formats/Keys/PKCS1.php
Normal file
83
phpseclib/Crypt/DH/Formats/Keys/PKCS1.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* "PKCS1" Formatted EC Key Handler
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* Processes keys with the following headers:
|
||||
*
|
||||
* -----BEGIN DH PARAMETERS-----
|
||||
*
|
||||
* Technically, PKCS1 is for RSA keys, only, but we're using PKCS1 to describe
|
||||
* DSA, whose format isn't really formally described anywhere, so might as well
|
||||
* use it to describe this, too.
|
||||
*
|
||||
* @category Crypt
|
||||
* @package DH
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2015 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
namespace phpseclib\Crypt\DH\Formats\Keys;
|
||||
|
||||
use phpseclib\Math\BigInteger;
|
||||
use phpseclib\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
|
||||
use phpseclib\File\ASN1;
|
||||
use phpseclib\File\ASN1\Maps;
|
||||
|
||||
/**
|
||||
* "PKCS1" Formatted DH Key Handler
|
||||
*
|
||||
* @package DH
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @access public
|
||||
*/
|
||||
abstract class PKCS1 extends Progenitor
|
||||
{
|
||||
/**
|
||||
* 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 = parent::load($key, $password);
|
||||
|
||||
$decoded = ASN1::decodeBER($key);
|
||||
if (empty($decoded)) {
|
||||
throw new \RuntimeException('Unable to decode BER');
|
||||
}
|
||||
|
||||
$components = ASN1::asn1map($decoded[0], Maps\DHParameter::MAP);
|
||||
if (!is_array($components)) {
|
||||
throw new \RuntimeException('Unable to perform ASN1 mapping on parameters');
|
||||
}
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert EC parameters to the appropriate format
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public static function saveParameters(BigInteger $prime, BigInteger $base, array $options = [])
|
||||
{
|
||||
$params = [
|
||||
'prime' => $prime,
|
||||
'base' => $base
|
||||
];
|
||||
$params = ASN1::encodeDER($params, Maps\DHParameter::MAP);
|
||||
|
||||
return "-----BEGIN DH PARAMETERS-----\r\n" .
|
||||
chunk_split(base64_encode($params), 64) .
|
||||
"-----END DH PARAMETERS-----\r\n";
|
||||
}
|
||||
}
|
156
phpseclib/Crypt/DH/Formats/Keys/PKCS8.php
Normal file
156
phpseclib/Crypt/DH/Formats/Keys/PKCS8.php
Normal file
@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PKCS#8 Formatted DH Key Handler
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* Processes keys with the following headers:
|
||||
*
|
||||
* -----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
* -----BEGIN PRIVATE KEY-----
|
||||
* -----BEGIN PUBLIC KEY-----
|
||||
*
|
||||
* @category Crypt
|
||||
* @package DH
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2015 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
namespace phpseclib\Crypt\DH\Formats\Keys;
|
||||
|
||||
use phpseclib\Math\BigInteger;
|
||||
use phpseclib\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
|
||||
use phpseclib\File\ASN1;
|
||||
use phpseclib\File\ASN1\Maps;
|
||||
|
||||
/**
|
||||
* PKCS#8 Formatted DH Key Handler
|
||||
*
|
||||
* @package DH
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @access public
|
||||
*/
|
||||
abstract class PKCS8 extends Progenitor
|
||||
{
|
||||
/**
|
||||
* OID Name
|
||||
*
|
||||
* @var string
|
||||
* @access private
|
||||
*/
|
||||
const OID_NAME = 'dhKeyAgreement';
|
||||
|
||||
/**
|
||||
* OID Value
|
||||
*
|
||||
* @var string
|
||||
* @access private
|
||||
*/
|
||||
const OID_VALUE = '1.2.840.113549.1.3.1';
|
||||
|
||||
/**
|
||||
* Child OIDs loaded
|
||||
*
|
||||
* @var bool
|
||||
* @access private
|
||||
*/
|
||||
protected static $childOIDsLoaded = 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 = '')
|
||||
{
|
||||
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');
|
||||
}
|
||||
|
||||
$decoded = ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element);
|
||||
if (empty($decoded)) {
|
||||
throw new \RuntimeException('Unable to decode BER of parameters');
|
||||
}
|
||||
$components = ASN1::asn1map($decoded[0], Maps\DHParameter::MAP);
|
||||
if (!is_array($components)) {
|
||||
throw new \RuntimeException('Unable to perform ASN1 mapping on parameters');
|
||||
}
|
||||
|
||||
$decoded = ASN1::decodeBER($key[$type]);
|
||||
switch (true) {
|
||||
case empty($decoded):
|
||||
case !is_array($decoded):
|
||||
case !isset($decoded[0]['content']):
|
||||
case !$decoded[0]['content'] instanceof BigInteger:
|
||||
throw new \RuntimeException('Unable to decode BER of parameters');
|
||||
}
|
||||
$components[$type] = $decoded[0]['content'];
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a private key to the appropriate format.
|
||||
*
|
||||
* @access public
|
||||
* @param \phpseclib\Math\BigInteger $prime
|
||||
* @param \phpseclib\Math\BigInteger $base
|
||||
* @param \phpseclib\Math\BigInteger $privateKey
|
||||
* @param \phpseclib\Math\BigInteger $publicKey
|
||||
* @param string $password optional
|
||||
* @param array $options optional
|
||||
* @return string
|
||||
*/
|
||||
public static function savePrivateKey(BigInteger $prime, BigInteger $base, BigInteger $privateKey, BigInteger $publicKey, $password = '', array $options = [])
|
||||
{
|
||||
$params = [
|
||||
'prime' => $prime,
|
||||
'base' => $base
|
||||
];
|
||||
$params = ASN1::encodeDER($params, Maps\DHParameter::MAP);
|
||||
$params = new ASN1\Element($params);
|
||||
$key = ASN1::encodeDER($privateKey, ['type' => ASN1::TYPE_INTEGER]);
|
||||
return self::wrapPrivateKey($key, [], $params, $password, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a public key to the appropriate format
|
||||
*
|
||||
* @access public
|
||||
* @param \phpseclib\Math\BigInteger $prime
|
||||
* @param \phpseclib\Math\BigInteger $base
|
||||
* @param \phpseclib\Math\BigInteger $publicKey
|
||||
* @param array $options optional
|
||||
* @return string
|
||||
*/
|
||||
public static function savePublicKey(BigInteger $prime, BigInteger $base, BigInteger $publicKey, array $options = [])
|
||||
{
|
||||
$params = [
|
||||
'prime' => $prime,
|
||||
'base' => $base
|
||||
];
|
||||
$params = ASN1::encodeDER($params, Maps\DHParameter::MAP);
|
||||
$params = new ASN1\Element($params);
|
||||
$key = ASN1::encodeDER($publicKey, ['type' => ASN1::TYPE_INTEGER]);
|
||||
return self::wrapPublicKey($key, $params);
|
||||
}
|
||||
}
|
40
phpseclib/Crypt/DH/Parameters.php
Normal file
40
phpseclib/Crypt/DH/Parameters.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* DH Parameters
|
||||
*
|
||||
* @category Crypt
|
||||
* @package DH
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2015 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
namespace phpseclib\Crypt\DH;
|
||||
|
||||
use phpseclib\Crypt\DH;
|
||||
|
||||
/**
|
||||
* DH Parameters
|
||||
*
|
||||
* @package DH
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @access public
|
||||
*/
|
||||
class Parameters extends DH
|
||||
{
|
||||
/**
|
||||
* Returns the parameters
|
||||
*
|
||||
* @param string $type
|
||||
* @param array $options optional
|
||||
* @return string
|
||||
*/
|
||||
public function toString($type = 'PKCS1', array $options = [])
|
||||
{
|
||||
$type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters');
|
||||
|
||||
return $type::saveParameters($this->prime, $this->base, $options);
|
||||
}
|
||||
}
|
82
phpseclib/Crypt/DH/PrivateKey.php
Normal file
82
phpseclib/Crypt/DH/PrivateKey.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* DH Private Key
|
||||
*
|
||||
* @category Crypt
|
||||
* @package DH
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2015 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
namespace phpseclib\Crypt\DH;
|
||||
|
||||
use phpseclib\Crypt\DH;
|
||||
use phpseclib\Crypt\Common;
|
||||
|
||||
/**
|
||||
* DH Private Key
|
||||
*
|
||||
* @package DH
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @access public
|
||||
*/
|
||||
class PrivateKey extends DH
|
||||
{
|
||||
use Common\Traits\PasswordProtected;
|
||||
|
||||
/**
|
||||
* Private Key
|
||||
*
|
||||
* @var \phpseclib\Math\BigInteger
|
||||
* @access private
|
||||
*/
|
||||
protected $privateKey;
|
||||
|
||||
/**
|
||||
* Public Key
|
||||
*
|
||||
* @var \phpseclib\Math\BigInteger
|
||||
* @access private
|
||||
*/
|
||||
protected $publicKey;
|
||||
|
||||
/**
|
||||
* Returns the public key
|
||||
*
|
||||
* @access public
|
||||
* @return DH
|
||||
*/
|
||||
public function getPublicKey()
|
||||
{
|
||||
$type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey');
|
||||
|
||||
if (!isset($this->publicKey)) {
|
||||
$this->publicKey = $this->base->powMod($this->privateKey, $this->prime);
|
||||
}
|
||||
|
||||
$key = $type::savePublicKey($this->prime, $this->base, $this->publicKey);
|
||||
|
||||
return DH::loadFormat('PKCS8', $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the private key
|
||||
*
|
||||
* @param string $type
|
||||
* @param array $options optional
|
||||
* @return string
|
||||
*/
|
||||
public function toString($type, array $options = [])
|
||||
{
|
||||
$type = self::validatePlugin('Keys', $type, 'savePrivateKey');
|
||||
|
||||
if (!isset($this->publicKey)) {
|
||||
$this->publicKey = $this->base->powMod($this->privateKey, $this->prime);
|
||||
}
|
||||
|
||||
return $type::savePrivateKey($this->prime, $this->base, $this->privateKey, $this->publicKey, $this->password, $options);
|
||||
}
|
||||
}
|
53
phpseclib/Crypt/DH/PublicKey.php
Normal file
53
phpseclib/Crypt/DH/PublicKey.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* DH Public Key
|
||||
*
|
||||
* @category Crypt
|
||||
* @package DH
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2015 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
namespace phpseclib\Crypt\DH;
|
||||
|
||||
use phpseclib\Crypt\DH;
|
||||
use phpseclib\Crypt\Common;
|
||||
|
||||
/**
|
||||
* DH Public Key
|
||||
*
|
||||
* @package DH
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @access public
|
||||
*/
|
||||
class PublicKey extends DH
|
||||
{
|
||||
use Common\Traits\Fingerprint;
|
||||
|
||||
/**
|
||||
* Returns the public key
|
||||
*
|
||||
* @param string $type
|
||||
* @param array $options optional
|
||||
* @return string
|
||||
*/
|
||||
public function toString($type, array $options = [])
|
||||
{
|
||||
$type = self::validatePlugin('Keys', $type, 'savePublicKey');
|
||||
|
||||
return $type::savePublicKey($this->prime, $this->base, $this->publicKey, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the public key as a BigInteger
|
||||
*
|
||||
* @return \phpseclib\Math\BigInteger
|
||||
*/
|
||||
public function toBigInteger()
|
||||
{
|
||||
return $this->publicKey;
|
||||
}
|
||||
}
|
@ -36,13 +36,17 @@ use phpseclib\Crypt\EC\PrivateKey;
|
||||
use phpseclib\Crypt\EC\PublicKey;
|
||||
use phpseclib\Crypt\EC\Parameters;
|
||||
use phpseclib\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
|
||||
use phpseclib\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
|
||||
use phpseclib\Crypt\EC\Curves\Curve25519;
|
||||
use phpseclib\Crypt\EC\Curves\Ed25519;
|
||||
use phpseclib\Crypt\EC\Curves\Ed448;
|
||||
use phpseclib\Crypt\EC\Formats\Keys\PKCS1;
|
||||
use phpseclib\File\ASN1\Maps\ECParameters;
|
||||
use phpseclib\File\ASN1;
|
||||
use phpseclib\Math\BigInteger;
|
||||
use phpseclib\Exception\UnsupportedCurveException;
|
||||
use phpseclib\Exception\UnsupportedAlgorithmException;
|
||||
use phpseclib\Exception\UnsupportedOperationException;
|
||||
|
||||
/**
|
||||
* Pure-PHP implementation of EC.
|
||||
@ -157,9 +161,13 @@ abstract class EC extends AsymmetricKey
|
||||
$privatekey = new PrivateKey;
|
||||
|
||||
$curveName = $curve;
|
||||
$curve = '\phpseclib\Crypt\EC\Curves\\' . $curve;
|
||||
$curve = '\phpseclib\Crypt\EC\Curves\\' . $curveName;
|
||||
if (!class_exists($curve)) {
|
||||
throw new UnsupportedCurveException('Named Curve of ' . $curveName . ' is not supported');
|
||||
$curveName = ucfirst($curveName);
|
||||
$curve = '\phpseclib\Crypt\EC\Curves\\' . $curveName;
|
||||
if (!class_exists($curve)) {
|
||||
throw new UnsupportedCurveException('Named Curve of ' . $curveName . ' is not supported');
|
||||
}
|
||||
}
|
||||
|
||||
$reflect = new \ReflectionClass($curve);
|
||||
@ -169,7 +177,14 @@ abstract class EC extends AsymmetricKey
|
||||
|
||||
$curve = new $curve();
|
||||
$privatekey->dA = $dA = $curve->createRandomMultiplier();
|
||||
$privatekey->QA = $curve->multiplyPoint($curve->getBasePoint(), $dA);
|
||||
if ($curve instanceof Curve25519 && self::$engines['libsodium']) {
|
||||
//$r = pack('H*', '0900000000000000000000000000000000000000000000000000000000000000');
|
||||
//$QA = sodium_crypto_scalarmult($dA->toBytes(), $r);
|
||||
$QA = sodium_crypto_box_publickey_from_secretkey($dA->toBytes());
|
||||
$privatekey->QA = [$curve->convertInteger(new BigInteger(strrev($QA), 256))];
|
||||
} else {
|
||||
$privatekey->QA = $curve->multiplyPoint($curve->getBasePoint(), $dA);
|
||||
}
|
||||
$privatekey->curve = $curve;
|
||||
|
||||
//$publickey = clone $privatekey;
|
||||
@ -310,6 +325,24 @@ abstract class EC extends AsymmetricKey
|
||||
'OpenSSL' : 'PHP';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the public key coordinates as a string
|
||||
*
|
||||
* Used by ECDH
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEncodedCoordinates()
|
||||
{
|
||||
if ($this->curve instanceof MontgomeryCurve) {
|
||||
return strrev($this->QA[0]->toBytes(true));
|
||||
}
|
||||
if ($this->curve instanceof TwistedEdwardsCurve) {
|
||||
return $this->curve->encodePoint($this->QA);
|
||||
}
|
||||
return "\4" . $this->QA[0]->toBytes(true) . $this->QA[1]->toBytes(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parameters
|
||||
*
|
||||
@ -339,6 +372,10 @@ abstract class EC extends AsymmetricKey
|
||||
*/
|
||||
public function withSignatureFormat($format)
|
||||
{
|
||||
if ($this->curve instanceof MontgomeryCurve) {
|
||||
throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures');
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->shortFormat = $format;
|
||||
$new->format = self::validatePlugin('Signature', $format);
|
||||
@ -404,6 +441,9 @@ abstract class EC extends AsymmetricKey
|
||||
*/
|
||||
public function withHash($hash)
|
||||
{
|
||||
if ($this->curve instanceof MontgomeryCurve) {
|
||||
throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures');
|
||||
}
|
||||
if ($this->curve instanceof Ed25519 && $hash != 'sha512') {
|
||||
throw new UnsupportedAlgorithmException('Ed25519 only supports sha512 as a hash');
|
||||
}
|
||||
@ -413,4 +453,18 @@ abstract class EC extends AsymmetricKey
|
||||
|
||||
return parent::withHash($hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* __toString() magic method
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
if ($this->curve instanceof MontgomeryCurve) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return parent::__toString();
|
||||
}
|
||||
}
|
283
phpseclib/Crypt/EC/BaseCurves/Montgomery.php
Normal file
283
phpseclib/Crypt/EC/BaseCurves/Montgomery.php
Normal file
@ -0,0 +1,283 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Curves over y^2 = x^3 + a*x + x
|
||||
*
|
||||
* Technically, a Montgomery curve has a coefficient for y^2 but for Curve25519 and Curve448 that
|
||||
* coefficient is 1.
|
||||
*
|
||||
* Curve25519 and Curve448 do not make use of the y coordinate, which makes it unsuitable for use
|
||||
* with ECDSA / EdDSA. A few other differences between Curve25519 and Ed25519 are discussed at
|
||||
* https://crypto.stackexchange.com/a/43058/4520
|
||||
*
|
||||
* More info:
|
||||
*
|
||||
* https://en.wikipedia.org/wiki/Montgomery_curve
|
||||
*
|
||||
* PHP version 5 and 7
|
||||
*
|
||||
* @category Crypt
|
||||
* @package EC
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2019 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://pear.php.net/package/Math_BigInteger
|
||||
*/
|
||||
|
||||
namespace phpseclib\Crypt\EC\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 + x
|
||||
*
|
||||
* @package EC
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @access public
|
||||
*/
|
||||
class Montgomery extends Base
|
||||
{
|
||||
/**
|
||||
* Prime Field Integer factory
|
||||
*
|
||||
* @var \phpseclib\Math\PrimeFields
|
||||
*/
|
||||
protected $factory;
|
||||
|
||||
/**
|
||||
* Cofficient for x
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $a;
|
||||
|
||||
/**
|
||||
* Constant used for point doubling
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $a24;
|
||||
|
||||
/**
|
||||
* The Number Zero
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $zero;
|
||||
|
||||
/**
|
||||
* The Number One
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $one;
|
||||
|
||||
/**
|
||||
* Base Point
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $p;
|
||||
|
||||
/**
|
||||
* The modulo
|
||||
*
|
||||
* @var BigInteger
|
||||
*/
|
||||
protected $modulo;
|
||||
|
||||
/**
|
||||
* The Order
|
||||
*
|
||||
* @var BigInteger
|
||||
*/
|
||||
protected $order;
|
||||
|
||||
/**
|
||||
* Sets the modulo
|
||||
*/
|
||||
public function setModulo(BigInteger $modulo)
|
||||
{
|
||||
$this->modulo = $modulo;
|
||||
$this->factory = new PrimeField($modulo);
|
||||
$this->zero = $this->factory->newInteger(new BigInteger());
|
||||
$this->one = $this->factory->newInteger(new BigInteger(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set coefficients a
|
||||
*/
|
||||
public function setCoefficients(BigInteger $a)
|
||||
{
|
||||
if (!isset($this->factory)) {
|
||||
throw new \RuntimeException('setModulo needs to be called before this method');
|
||||
}
|
||||
$this->a = $this->factory->newInteger($a);
|
||||
$two = $this->factory->newInteger(new BigInteger(2));
|
||||
$four = $this->factory->newInteger(new BigInteger(4));
|
||||
$this->a24 = $this->a->subtract($two)->divide($four);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set x and y coordinates for the base point
|
||||
*
|
||||
* @param BigInteger|PrimeInteger $x
|
||||
* @param BigInteger|PrimeInteger $y
|
||||
* @return PrimeInteger[]
|
||||
*/
|
||||
public function setBasePoint($x, $y)
|
||||
{
|
||||
switch (true) {
|
||||
case !$x instanceof BigInteger && !$x instanceof PrimeInteger:
|
||||
throw new \UnexpectedValueException('Argument 1 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\Integer');
|
||||
case !$y instanceof BigInteger && !$y instanceof PrimeInteger:
|
||||
throw new \UnexpectedValueException('Argument 2 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\Integer');
|
||||
}
|
||||
if (!isset($this->factory)) {
|
||||
throw new \RuntimeException('setModulo needs to be called before this method');
|
||||
}
|
||||
$this->p = [
|
||||
$x instanceof BigInteger ? $this->factory->newInteger($x) : $x,
|
||||
$y instanceof BigInteger ? $this->factory->newInteger($y) : $y
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the base point as an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBasePoint()
|
||||
{
|
||||
if (!isset($this->factory)) {
|
||||
throw new \RuntimeException('setModulo needs to be called before this method');
|
||||
}
|
||||
/*
|
||||
if (!isset($this->p)) {
|
||||
throw new \RuntimeException('setBasePoint needs to be called before this method');
|
||||
}
|
||||
*/
|
||||
return $this->p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Doubles and adds a point on a curve
|
||||
*
|
||||
* See https://tools.ietf.org/html/draft-ietf-tls-curve25519-01#appendix-A.1.3
|
||||
*
|
||||
* @return FiniteField[][]
|
||||
*/
|
||||
private function doubleAndAddPoint(array $p, array $q, PrimeInteger $x1)
|
||||
{
|
||||
if (!isset($this->factory)) {
|
||||
throw new \RuntimeException('setModulo needs to be called before this method');
|
||||
}
|
||||
|
||||
if (!count($p) || !count($q)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!isset($p[1])) {
|
||||
throw new \RuntimeException('Affine coordinates need to be manually converted to XZ coordinates');
|
||||
}
|
||||
|
||||
list($x2, $z2) = $p;
|
||||
list($x3, $z3) = $q;
|
||||
|
||||
$a = $x2->add($z2);
|
||||
$aa = $a->multiply($a);
|
||||
$b = $x2->subtract($z2);
|
||||
$bb = $b->multiply($b);
|
||||
$e = $aa->subtract($bb);
|
||||
$c = $x3->add($z3);
|
||||
$d = $x3->subtract($z3);
|
||||
$da = $d->multiply($a);
|
||||
$cb = $c->multiply($b);
|
||||
$temp = $da->add($cb);
|
||||
$x5 = $temp->multiply($temp);
|
||||
$temp = $da->subtract($cb);
|
||||
$z5 = $x1->multiply($temp->multiply($temp));
|
||||
$x4 = $aa->multiply($bb);
|
||||
$z4 = $e->multiply($bb->add($this->a24->multiply($e)));
|
||||
|
||||
return [
|
||||
[$x4, $z4],
|
||||
[$x5, $z5]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply a point on the curve by a scalar
|
||||
*
|
||||
* Uses the montgomery ladder technique as described here:
|
||||
*
|
||||
* https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Montgomery_ladder
|
||||
* https://github.com/phpecc/phpecc/issues/16#issuecomment-59176772
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiplyPoint(array $p, Integer $d)
|
||||
{
|
||||
$p1 = [$this->one, $this->zero];
|
||||
$alreadyInternal = isset($x[1]);
|
||||
$p2 = $this->convertToInternal($p);
|
||||
$x = $p[0];
|
||||
|
||||
$b = $d->toBits();
|
||||
$b = str_pad($b, 256, '0', STR_PAD_LEFT);
|
||||
for ($i = 0; $i < strlen($b); $i++) {
|
||||
$b_i = (int) $b[$i];
|
||||
if ($b_i) {
|
||||
list($p2, $p1) = $this->doubleAndAddPoint($p2, $p1, $x);
|
||||
} else {
|
||||
list($p1, $p2) = $this->doubleAndAddPoint($p1, $p2, $x);
|
||||
}
|
||||
}
|
||||
|
||||
return $alreadyInternal ? $p1 : $this->convertToAffine($p1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an affine point to an XZ coordinate
|
||||
*
|
||||
* From https://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html
|
||||
*
|
||||
* XZ coordinates represent x y as X Z satsfying the following equations:
|
||||
*
|
||||
* x=X/Z
|
||||
*
|
||||
* @return \phpseclib\Math\PrimeField\Integer[]
|
||||
*/
|
||||
public function convertToInternal(array $p)
|
||||
{
|
||||
if (empty($p)) {
|
||||
return [clone $this->zero, clone $this->one];
|
||||
}
|
||||
|
||||
if (isset($p[1])) {
|
||||
return $p;
|
||||
}
|
||||
|
||||
$p[1] = clone $this->one;
|
||||
|
||||
return $p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the affine point
|
||||
*
|
||||
* @return \phpseclib\Math\PrimeField\Integer[]
|
||||
*/
|
||||
public function convertToAffine(array $p)
|
||||
{
|
||||
if (!isset($p[1])) {
|
||||
return $p;
|
||||
}
|
||||
list($x, $z) = $p;
|
||||
return [$x->divide($z)];
|
||||
}
|
||||
}
|
@ -216,21 +216,4 @@ class TwistedEdwards extends Base
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
64
phpseclib/Crypt/EC/Curves/Curve25519.php
Normal file
64
phpseclib/Crypt/EC/Curves/Curve25519.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Curve25519
|
||||
*
|
||||
* PHP version 5 and 7
|
||||
*
|
||||
* @category Crypt
|
||||
* @package EC
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2019 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://pear.php.net/package/Math_BigInteger
|
||||
*/
|
||||
|
||||
namespace phpseclib\Crypt\EC\Curves;
|
||||
|
||||
use phpseclib\Math\Common\FiniteField\Integer;
|
||||
use phpseclib\Crypt\EC\BaseCurves\Montgomery;
|
||||
use phpseclib\Math\BigInteger;
|
||||
|
||||
class Curve25519 extends Montgomery
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// 2^255 - 19
|
||||
$this->setModulo(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED', 16));
|
||||
$this->a24 = $this->factory->newInteger(new BigInteger('121666'));
|
||||
$this->p = [$this->factory->newInteger(new BigInteger(9))];
|
||||
// 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed
|
||||
$this->setOrder(new BigInteger('1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED', 16));
|
||||
|
||||
/*
|
||||
$this->setCoefficients(
|
||||
new BigInteger('486662'), // a
|
||||
);
|
||||
$this->setBasePoint(
|
||||
new BigInteger(9),
|
||||
new BigInteger('14781619447589544791020593568409986887264606134616475288964881837755586237401', 16)
|
||||
);
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply a point on the curve by a scalar
|
||||
*
|
||||
* Modifies the scalar as described at https://tools.ietf.org/html/rfc7748#page-8
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiplyPoint(array $p, Integer $d)
|
||||
{
|
||||
//$r = strrev(sodium_crypto_scalarmult($d->toBytes(), strrev($p[0]->toBytes())));
|
||||
//return [$this->factory->newInteger(new BigInteger($r, 256))];
|
||||
|
||||
$d = $d->toBytes();
|
||||
$d&= "\xF8" . str_repeat("\xFF", 30) . "\x7F";
|
||||
$d = strrev($d);
|
||||
$d|= "\x40";
|
||||
$d = $this->factory->newInteger(new BigInteger($d, -256));
|
||||
|
||||
return parent::multiplyPoint($p, $d);
|
||||
}
|
||||
}
|
92
phpseclib/Crypt/EC/Formats/Keys/Curve25519Private.php
Normal file
92
phpseclib/Crypt/EC/Formats/Keys/Curve25519Private.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Curve25519 Private Key Handler
|
||||
*
|
||||
* "Naked" Curve25519 private keys can pretty much be any sequence of random 32x bytes so unless
|
||||
* we have a "hidden" key handler pretty much every 32 byte string will be loaded as a curve25519
|
||||
* private key even if it probably isn't one by PublicKeyLoader.
|
||||
*
|
||||
* "Naked" Curve25519 public keys also a string of 32 bytes so distinguishing between a "naked"
|
||||
* curve25519 private key and a public key is nigh impossible, hence separate plugins for each
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Crypt
|
||||
* @package EC
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2015 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
namespace phpseclib\Crypt\EC\Formats\Keys;
|
||||
|
||||
use phpseclib\Crypt\EC\Curves\Curve25519;
|
||||
use phpseclib\Math\Common\FiniteField\Integer;
|
||||
use phpseclib\Math\BigInteger;
|
||||
|
||||
/**
|
||||
* Curve25519 Private Key Handler
|
||||
*
|
||||
* @package EC
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @access public
|
||||
*/
|
||||
abstract class Curve25519Private
|
||||
{
|
||||
/**
|
||||
* Is invisible flag
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
const IS_INVISIBLE = true;
|
||||
|
||||
/**
|
||||
* 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 = '')
|
||||
{
|
||||
$curve = new Curve25519();
|
||||
|
||||
$components = ['curve' => $curve];
|
||||
$components['dA'] = $components['curve']->convertInteger(new BigInteger($key, -256));
|
||||
// note that EC::getEncodedCoordinates does some additional "magic" (it does strrev on the result)
|
||||
$components['QA'] = $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an EC public key to the appropriate format
|
||||
*
|
||||
* @access public
|
||||
* @param \phpseclib\Crypt\EC\Curves\Curve25519 $curve
|
||||
* @param \phpseclib\Math\Common\FiniteField\Integer[] $publicKey
|
||||
* @return string
|
||||
*/
|
||||
public static function savePublicKey(Curve25519 $curve, array $publicKey)
|
||||
{
|
||||
return strrev($publicKey[0]->toBytes(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a private key to the appropriate format.
|
||||
*
|
||||
* @access public
|
||||
* @param \phpseclib\Math\Common\FiniteField\Integer $privateKey
|
||||
* @param \phpseclib\Crypt\EC\Curves\Curve25519 $curve
|
||||
* @param \phpseclib\Math\Common\FiniteField\Integer[] $publicKey
|
||||
* @param string $password optional
|
||||
* @return string
|
||||
*/
|
||||
public static function savePrivateKey(Integer $privateKey, Curve25519 $curve, array $publicKey, $password = '')
|
||||
{
|
||||
return $privateKey->toBytes(true);
|
||||
}
|
||||
}
|
68
phpseclib/Crypt/EC/Formats/Keys/Curve25519Public.php
Normal file
68
phpseclib/Crypt/EC/Formats/Keys/Curve25519Public.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Curve25519 Public Key Handler
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Crypt
|
||||
* @package EC
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2015 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
namespace phpseclib\Crypt\EC\Formats\Keys;
|
||||
|
||||
use phpseclib\Crypt\EC\Curves\Curve25519;
|
||||
use phpseclib\Math\Common\FiniteField\Integer;
|
||||
use phpseclib\Math\BigInteger;
|
||||
|
||||
/**
|
||||
* Curve25519 Public Key Handler
|
||||
*
|
||||
* @package EC
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @access public
|
||||
*/
|
||||
abstract class Curve25519Public
|
||||
{
|
||||
/**
|
||||
* Is invisible flag
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
const IS_INVISIBLE = true;
|
||||
|
||||
/**
|
||||
* 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 = '')
|
||||
{
|
||||
$curve = new Curve25519();
|
||||
|
||||
$components = ['curve' => $curve];
|
||||
$components['QA'] = [$components['curve']->convertInteger(new BigInteger(strrev($key), -256))];
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an EC public key to the appropriate format
|
||||
*
|
||||
* @access public
|
||||
* @param \phpseclib\Crypt\EC\Curves\Curve25519 $curve
|
||||
* @param \phpseclib\Math\Common\FiniteField\Integer[] $publicKey
|
||||
* @return string
|
||||
*/
|
||||
public static function savePublicKey(Curve25519 $curve, array $publicKey)
|
||||
{
|
||||
return strrev($publicKey[0]->toBytes(true));
|
||||
}
|
||||
}
|
@ -35,6 +35,7 @@ use phpseclib\Crypt\EC\BaseCurves\Base as BaseCurve;
|
||||
use phpseclib\Math\BigInteger;
|
||||
use ParagonIE\ConstantTime\Base64;
|
||||
use phpseclib\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
|
||||
use phpseclib\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
|
||||
use phpseclib\Exception\UnsupportedCurveException;
|
||||
|
||||
/**
|
||||
@ -96,8 +97,8 @@ abstract class PKCS1 extends Progenitor
|
||||
{
|
||||
self::initialize_static_variables();
|
||||
|
||||
if ($curve instanceof TwistedEdwardsCurve) {
|
||||
throw new UnsupportedCurveException('TwistedEdwards Curves are not supported');
|
||||
if ($curve instanceof TwistedEdwardsCurve || $curve instanceof MontgomeryCurve) {
|
||||
throw new UnsupportedCurveException('TwistedEdwards and Montgomery Curves are not supported');
|
||||
}
|
||||
|
||||
$key = self::encodeParameters($curve, false, $options);
|
||||
@ -122,7 +123,7 @@ abstract class PKCS1 extends Progenitor
|
||||
{
|
||||
self::initialize_static_variables();
|
||||
|
||||
if ($curve instanceof TwistedEdwardsCurve) {
|
||||
if ($curve instanceof TwistedEdwardsCurve || $curve instanceof MontgomeryCurve) {
|
||||
throw new UnsupportedCurveException('TwistedEdwards Curves are not supported');
|
||||
}
|
||||
|
||||
|
@ -31,9 +31,11 @@ use phpseclib\File\ASN1;
|
||||
use phpseclib\File\ASN1\Maps;
|
||||
use phpseclib\Crypt\EC\BaseCurves\Base as BaseCurve;
|
||||
use phpseclib\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
|
||||
use phpseclib\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
|
||||
use phpseclib\Math\Common\FiniteField\Integer;
|
||||
use phpseclib\Crypt\EC\Curves\Ed25519;
|
||||
use phpseclib\Crypt\EC\Curves\Ed448;
|
||||
use phpseclib\Exception\UnsupportedCurveException;
|
||||
|
||||
/**
|
||||
* PKCS#8 Formatted EC Key Handler
|
||||
@ -176,6 +178,10 @@ abstract class PKCS8 extends Progenitor
|
||||
{
|
||||
self::initialize_static_variables();
|
||||
|
||||
if ($curve instanceof MontgomeryCurve) {
|
||||
throw new UnsupportedCurveException('Montgomery Curves are not supported');
|
||||
}
|
||||
|
||||
if ($curve instanceof TwistedEdwardsCurve) {
|
||||
return self::wrapPublicKey(
|
||||
$curve->encodePoint($publicKey),
|
||||
@ -206,6 +212,10 @@ abstract class PKCS8 extends Progenitor
|
||||
{
|
||||
self::initialize_static_variables();
|
||||
|
||||
if ($curve instanceof MontgomeryCurve) {
|
||||
throw new UnsupportedCurveException('Montgomery Curves are not supported');
|
||||
}
|
||||
|
||||
if ($curve instanceof TwistedEdwardsCurve) {
|
||||
return self::wrapPrivateKey(
|
||||
"\x04\x20" . $privateKey->secret,
|
||||
|
@ -25,6 +25,7 @@ use phpseclib\Math\BigInteger;
|
||||
use phpseclib\Crypt\EC\BaseCurves\Base as BaseCurve;
|
||||
use phpseclib\Crypt\EC\BaseCurves\Prime as PrimeCurve;
|
||||
use phpseclib\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
|
||||
use phpseclib\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
|
||||
use phpseclib\Exception\UnsupportedCurveException;
|
||||
|
||||
/**
|
||||
@ -372,8 +373,8 @@ abstract class XML
|
||||
{
|
||||
self::initialize_static_variables();
|
||||
|
||||
if ($curve instanceof TwistedEdwardsCurve) {
|
||||
throw new UnsupportedCurveException('TwistedEdwards Curves are not supported');
|
||||
if ($curve instanceof TwistedEdwardsCurve || $curve instanceof MontgomeryCurve) {
|
||||
throw new UnsupportedCurveException('TwistedEdwards and Montgomery Curves are not supported');
|
||||
}
|
||||
|
||||
if (empty(static::$namespace)) {
|
||||
|
@ -17,10 +17,13 @@ use phpseclib\Crypt\EC;
|
||||
use phpseclib\Crypt\EC\Formats\Signature\ASN1 as ASN1Signature;
|
||||
use phpseclib\Math\BigInteger;
|
||||
use phpseclib\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
|
||||
use phpseclib\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
|
||||
use phpseclib\Crypt\Hash;
|
||||
use phpseclib\Crypt\EC\Curves\Ed25519;
|
||||
use phpseclib\Crypt\EC\Formats\Keys\PKCS8;
|
||||
use phpseclib\Crypt\EC\Curves\Curve25519;
|
||||
use phpseclib\Crypt\EC\Formats\Keys\PKCS1;
|
||||
use phpseclib\Crypt\Common;
|
||||
use phpseclib\Exception\UnsupportedOperationException;
|
||||
|
||||
/**
|
||||
* EC Private Key
|
||||
@ -44,6 +47,39 @@ class PrivateKey extends EC implements Common\PrivateKey
|
||||
*/
|
||||
protected $dA;
|
||||
|
||||
/**
|
||||
* Multiplies an encoded point by the private key
|
||||
*
|
||||
* Used by ECDH
|
||||
*
|
||||
* @param string $coordinates
|
||||
* @return string
|
||||
*/
|
||||
public function multiply($coordinates)
|
||||
{
|
||||
if ($this->curve instanceof MontgomeryCurve) {
|
||||
if ($this->curve instanceof Curve25519 && self::$engines['libsodium']) {
|
||||
return sodium_crypto_scalarmult($this->dA->toBytes(), $coordinates);
|
||||
}
|
||||
|
||||
$point = [$this->curve->convertInteger(new BigInteger(strrev($coordinates), 256))];
|
||||
$point = $this->curve->multiplyPoint($point, $this->dA);
|
||||
return strrev($point[0]->toBytes(true));
|
||||
}
|
||||
if (!$this->curve instanceof TwistedEdwardsCurve) {
|
||||
$coordinates = "\0$coordinates";
|
||||
}
|
||||
$point = PKCS1::extractPoint($coordinates, $this->curve);
|
||||
$point = $this->curve->multiplyPoint($point, $this->dA);
|
||||
if ($this->curve instanceof TwistedEdwardsCurve) {
|
||||
return $this->curve->encodePoint($point);
|
||||
}
|
||||
if (empty($point)) {
|
||||
throw new \RuntimeException('The infinity point is invalid');
|
||||
}
|
||||
return "\4" . $point[0]->toBytes(true) . $point[1]->toBytes(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a signature
|
||||
*
|
||||
@ -54,6 +90,10 @@ class PrivateKey extends EC implements Common\PrivateKey
|
||||
*/
|
||||
public function sign($message)
|
||||
{
|
||||
if ($this->curve instanceof MontgomeryCurve) {
|
||||
throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures');
|
||||
}
|
||||
|
||||
$dA = $this->dA->toBigInteger();
|
||||
|
||||
$order = $this->curve->getOrder();
|
||||
@ -193,10 +233,21 @@ class PrivateKey extends EC implements Common\PrivateKey
|
||||
*/
|
||||
public function getPublicKey()
|
||||
{
|
||||
$type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey');
|
||||
$format = 'PKCS8';
|
||||
if ($this->curve instanceof MontgomeryCurve) {
|
||||
$format = $this->curve instanceof Curve25519 ?
|
||||
'Curve25519Public' :
|
||||
'Curve448Public';
|
||||
}
|
||||
|
||||
$type = self::validatePlugin('Keys', $format, 'savePublicKey');
|
||||
|
||||
$key = $type::savePublicKey($this->curve, $this->QA);
|
||||
$key = EC::loadFormat('PKCS8', $key)
|
||||
$key = EC::loadFormat($format, $key);
|
||||
if ($this->curve instanceof MontgomeryCurve) {
|
||||
return $key;
|
||||
}
|
||||
$key = $key
|
||||
->withHash($this->hash->getHash())
|
||||
->withSignatureFormat($this->shortFormat);
|
||||
if ($this->curve instanceof TwistedEdwardsCurve) {
|
||||
|
@ -18,10 +18,11 @@ use phpseclib\Crypt\Hash;
|
||||
use phpseclib\Math\BigInteger;
|
||||
use phpseclib\Crypt\EC\Formats\Signature\ASN1 as ASN1Signature;
|
||||
use phpseclib\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
|
||||
use phpseclib\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
|
||||
use phpseclib\Crypt\EC\Curves\Ed25519;
|
||||
use phpseclib\Crypt\EC\Formats\Keys\PKCS1;
|
||||
use phpseclib\Crypt\EC\Formats\Keys\PKCS8;
|
||||
use phpseclib\Crypt\Common;
|
||||
use phpseclib\Exception\UnsupportedOperationException;
|
||||
|
||||
/**
|
||||
* EC Public Key
|
||||
@ -45,6 +46,10 @@ class PublicKey extends EC implements Common\PublicKey
|
||||
*/
|
||||
public function verify($message, $signature)
|
||||
{
|
||||
if ($this->curve instanceof MontgomeryCurve) {
|
||||
throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures');
|
||||
}
|
||||
|
||||
$order = $this->curve->getOrder();
|
||||
|
||||
if ($this->curve instanceof TwistedEdwardsCurve) {
|
||||
|
42
phpseclib/File/ASN1/Maps/DHParameter.php
Normal file
42
phpseclib/File/ASN1/Maps/DHParameter.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* DHParameter
|
||||
*
|
||||
* From: https://www.teletrust.de/fileadmin/files/oid/oid_pkcs-3v1-4.pdf#page=6
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category File
|
||||
* @package ASN1
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2016 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
namespace phpseclib\File\ASN1\Maps;
|
||||
|
||||
use phpseclib\File\ASN1;
|
||||
|
||||
/**
|
||||
* DHParameter
|
||||
*
|
||||
* @package ASN1
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @access public
|
||||
*/
|
||||
abstract class DHParameter
|
||||
{
|
||||
const MAP = [
|
||||
'type' => ASN1::TYPE_SEQUENCE,
|
||||
'children' => [
|
||||
'prime' => ['type' => ASN1::TYPE_INTEGER],
|
||||
'base' => ['type' => ASN1::TYPE_INTEGER],
|
||||
'privateValueLength' => [
|
||||
'type' => ASN1::TYPE_INTEGER,
|
||||
'optional' => true
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
@ -58,6 +58,7 @@ use phpseclib\Crypt\Common\PrivateKey;
|
||||
use phpseclib\Crypt\RSA;
|
||||
use phpseclib\Crypt\DSA;
|
||||
use phpseclib\Crypt\EC;
|
||||
use phpseclib\Crypt\DH;
|
||||
use phpseclib\Crypt\TripleDES;
|
||||
use phpseclib\Crypt\Twofish;
|
||||
use phpseclib\Crypt\ChaCha20;
|
||||
@ -1348,6 +1349,7 @@ class SSH2
|
||||
|
||||
/**
|
||||
* Key Exchange
|
||||
*
|
||||
* @return bool
|
||||
* @param string|bool $kexinit_payload_server optional
|
||||
* @throws \UnexpectedValueException on receipt of unexpected packets
|
||||
@ -1485,12 +1487,14 @@ class SSH2
|
||||
// Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty.
|
||||
$exchange_hash_rfc4419 = '';
|
||||
|
||||
if ($this->kex_algorithm === 'curve25519-sha256@libssh.org') {
|
||||
$x = Random::string(32);
|
||||
$eBytes = \Sodium\crypto_box_publickey_from_secretkey($x);
|
||||
if (strpos($this->kex_algorithm, 'curve25519-sha256') === 0 || strpos($this->kex_algorithm, 'ecdh-sha2-nistp') === 0) {
|
||||
$curve = strpos($this->kex_algorithm, 'curve25519-sha256') === 0 ?
|
||||
'Curve25519' :
|
||||
substr($this->kex_algorithm, 10);
|
||||
$ourPrivate = EC::createKey($curve);
|
||||
$ourPublicBytes = $ourPrivate->getPublicKey()->getEncodedCoordinates();
|
||||
$clientKexInitMessage = NET_SSH2_MSG_KEX_ECDH_INIT;
|
||||
$serverKexReplyMessage = NET_SSH2_MSG_KEX_ECDH_REPLY;
|
||||
$kexHash = new Hash('sha256');
|
||||
} else {
|
||||
if (strpos($this->kex_algorithm, 'diffie-hellman-group-exchange') === 0) {
|
||||
$dh_group_sizes_packed = pack(
|
||||
@ -1512,7 +1516,7 @@ class SSH2
|
||||
throw new \RuntimeException('Connection closed by server');
|
||||
}
|
||||
|
||||
list($type, $primeBytes, $gBytes) = unpack('Css', $response);
|
||||
list($type, $primeBytes, $gBytes) = Strings::unpackSSH2('Css', $response);
|
||||
if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) {
|
||||
throw new \UnexpectedValueException('Expected SSH_MSG_KEX_DH_GEX_GROUP');
|
||||
}
|
||||
@ -1525,65 +1529,43 @@ class SSH2
|
||||
$gBytes
|
||||
);
|
||||
|
||||
$params = DH::createParameters($prime, $g);
|
||||
$clientKexInitMessage = NET_SSH2_MSG_KEXDH_GEX_INIT;
|
||||
$serverKexReplyMessage = NET_SSH2_MSG_KEXDH_GEX_REPLY;
|
||||
} else {
|
||||
switch ($this->kex_algorithm) {
|
||||
// see http://tools.ietf.org/html/rfc2409#section-6.2 and
|
||||
// http://tools.ietf.org/html/rfc2412, appendex E
|
||||
case 'diffie-hellman-group1-sha1':
|
||||
$prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
|
||||
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
|
||||
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
|
||||
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
|
||||
break;
|
||||
// see http://tools.ietf.org/html/rfc3526#section-3
|
||||
case 'diffie-hellman-group14-sha1':
|
||||
$prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
|
||||
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
|
||||
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
|
||||
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
|
||||
'98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
|
||||
'9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
|
||||
'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
|
||||
'3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF';
|
||||
break;
|
||||
}
|
||||
// For both diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1
|
||||
// the generator field element is 2 (decimal) and the hash function is sha1.
|
||||
$g = new BigInteger(2);
|
||||
$prime = new BigInteger($prime, 16);
|
||||
$params = DH::createParameters($this->kex_algorithm);
|
||||
$clientKexInitMessage = NET_SSH2_MSG_KEXDH_INIT;
|
||||
$serverKexReplyMessage = NET_SSH2_MSG_KEXDH_REPLY;
|
||||
}
|
||||
|
||||
switch ($this->kex_algorithm) {
|
||||
case 'diffie-hellman-group-exchange-sha256':
|
||||
$kexHash = new Hash('sha256');
|
||||
break;
|
||||
default:
|
||||
$kexHash = new Hash('sha1');
|
||||
}
|
||||
|
||||
/* To increase the speed of the key exchange, both client and server may
|
||||
reduce the size of their private exponents. It should be at least
|
||||
twice as long as the key material that is generated from the shared
|
||||
secret. For more details, see the paper by van Oorschot and Wiener
|
||||
[VAN-OORSCHOT].
|
||||
|
||||
-- http://tools.ietf.org/html/rfc4419#section-6.2 */
|
||||
$one = new BigInteger(1);
|
||||
$keyLength = min($kexHash->getLengthInBytes(), max($encryptKeyLength, $decryptKeyLength));
|
||||
$max = $one->bitwise_leftShift(16 * $keyLength); // 2 * 8 * $keyLength
|
||||
$max = $max->subtract($one);
|
||||
|
||||
$x = BigInteger::randomRange($one, $max);
|
||||
$e = $g->modPow($x, $prime);
|
||||
|
||||
$eBytes = $e->toBytes(true);
|
||||
$ourPrivate = DH::createKey($params, 16 * $keyLength); // 2 * 8 * $keyLength
|
||||
$ourPublic = $ourPrivate->getPublicKey()->toBigInteger();
|
||||
$ourPublicBytes = $ourPublic->toBytes(true);
|
||||
}
|
||||
|
||||
$data = pack('CNa*', $clientKexInitMessage, strlen($eBytes), $eBytes);
|
||||
switch ($this->kex_algorithm) {
|
||||
case 'diffie-hellman-group15-sha512':
|
||||
case 'diffie-hellman-group16-sha512':
|
||||
case 'diffie-hellman-group17-sha512':
|
||||
case 'diffie-hellman-group18-sha512':
|
||||
case 'ecdh-sha2-nistp521':
|
||||
$kexHash = new Hash('sha512');
|
||||
break;
|
||||
case 'ecdh-sha2-nistp384':
|
||||
$kexHash = new Hash('sha384');
|
||||
break;
|
||||
case 'diffie-hellman-group-exchange-sha256':
|
||||
case 'diffie-hellman-group14-sha256':
|
||||
case 'ecdh-sha2-nistp256':
|
||||
case 'curve25519-sha256@libssh.org':
|
||||
case 'curve25519-sha256':
|
||||
$kexHash = new Hash('sha256');
|
||||
break;
|
||||
default:
|
||||
$kexHash = new Hash('sha1');
|
||||
}
|
||||
|
||||
$data = pack('CNa*', $clientKexInitMessage, strlen($ourPublicBytes), $ourPublicBytes);
|
||||
|
||||
$this->send_binary_packet($data);
|
||||
|
||||
@ -1599,7 +1581,7 @@ class SSH2
|
||||
list(
|
||||
$type,
|
||||
$server_public_host_key,
|
||||
$fBytes,
|
||||
$theirPublicBytes,
|
||||
$this->signature
|
||||
) = Strings::unpackSSH2('Csss', $response);
|
||||
|
||||
@ -1615,18 +1597,10 @@ class SSH2
|
||||
}
|
||||
$temp = unpack('Nlength', substr($this->signature, 0, 4));
|
||||
$this->signature_format = substr($this->signature, 4, $temp['length']);
|
||||
if ($this->kex_algorithm === 'curve25519-sha256@libssh.org') {
|
||||
if (strlen($fBytes) !== 32) {
|
||||
throw new \RuntimeException('Received curve25519 public key of invalid length.');
|
||||
return false;
|
||||
}
|
||||
$key = new BigInteger(\Sodium\crypto_scalarmult($x, $fBytes), 256);
|
||||
\Sodium\memzero($x);
|
||||
} else {
|
||||
$f = new BigInteger($fBytes, -256);
|
||||
$key = $f->modPow($x, $prime);
|
||||
$keyBytes = DH::computeSecret($ourPrivate, $theirPublicBytes);
|
||||
if (($keyBytes[0] & "\x80") === "\x80") {
|
||||
$keyBytes = "\0$keyBytes";
|
||||
}
|
||||
$keyBytes = $key->toBytes(true);
|
||||
|
||||
$this->exchange_hash = Strings::packSSH2('s5',
|
||||
$this->identifier,
|
||||
@ -1637,8 +1611,8 @@ class SSH2
|
||||
);
|
||||
$this->exchange_hash.= $exchange_hash_rfc4419;
|
||||
$this->exchange_hash.= Strings::packSSH2('s3',
|
||||
$eBytes,
|
||||
$fBytes,
|
||||
$ourPublicBytes,
|
||||
$theirPublicBytes,
|
||||
$keyBytes
|
||||
);
|
||||
|
||||
@ -3377,7 +3351,7 @@ class SSH2
|
||||
// see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in
|
||||
if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
|
||||
Strings::shift($payload, 1);
|
||||
list($this->banner_message) = Strings::unpackSSH2('s', $response);
|
||||
list($this->banner_message) = Strings::unpackSSH2('s', $payload);
|
||||
$payload = $this->get_binary_packet();
|
||||
}
|
||||
|
||||
@ -4262,22 +4236,27 @@ class SSH2
|
||||
// Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using
|
||||
// Curve25519. See doc/curve25519-sha256@libssh.org.txt in the
|
||||
// libssh repository for more information.
|
||||
'curve25519-sha256',
|
||||
'curve25519-sha256@libssh.org',
|
||||
|
||||
'ecdh-sha2-nistp256', // RFC 5656
|
||||
'ecdh-sha2-nistp384', // RFC 5656
|
||||
'ecdh-sha2-nistp521', // RFC 5656
|
||||
|
||||
'diffie-hellman-group-exchange-sha256',// RFC 4419
|
||||
'diffie-hellman-group-exchange-sha1', // RFC 4419
|
||||
|
||||
// Diffie-Hellman Key Agreement (DH) using integer modulo prime
|
||||
// groups.
|
||||
'diffie-hellman-group1-sha1', // REQUIRED
|
||||
'diffie-hellman-group14-sha256',
|
||||
'diffie-hellman-group14-sha1', // REQUIRED
|
||||
'diffie-hellman-group-exchange-sha1', // RFC 4419
|
||||
'diffie-hellman-group-exchange-sha256', // RFC 4419
|
||||
];
|
||||
'diffie-hellman-group15-sha512',
|
||||
'diffie-hellman-group16-sha512',
|
||||
'diffie-hellman-group17-sha512',
|
||||
'diffie-hellman-group18-sha512',
|
||||
|
||||
if (!function_exists('\\Sodium\\library_version_major')) {
|
||||
$kex_algorithms = array_diff(
|
||||
$kex_algorithms,
|
||||
['curve25519-sha256@libssh.org']
|
||||
);
|
||||
}
|
||||
'diffie-hellman-group1-sha1', // REQUIRED
|
||||
];
|
||||
|
||||
return $kex_algorithms;
|
||||
}
|
||||
|
203
tests/Unit/Crypt/DHTest.php
Normal file
203
tests/Unit/Crypt/DHTest.php
Normal file
@ -0,0 +1,203 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Andreas Fischer <bantu@phpbb.com>
|
||||
* @copyright 2013 Andreas Fischer
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
*/
|
||||
|
||||
use phpseclib\Crypt\DH;
|
||||
use phpseclib\Crypt\DH\PublicKey;
|
||||
use phpseclib\Crypt\DH\PrivateKey;
|
||||
use phpseclib\Crypt\DH\Parameters;
|
||||
use phpseclib\Crypt\EC;
|
||||
use phpseclib\Math\BigInteger;
|
||||
use phpseclib\Crypt\AES;
|
||||
|
||||
class Unit_Crypt_DHTest extends PhpseclibTestCase
|
||||
{
|
||||
public function testParametersWithString()
|
||||
{
|
||||
$a = DH::createParameters('diffie-hellman-group1-sha1');
|
||||
$a = str_replace("\r\n", "\n", trim($a));
|
||||
$b = '-----BEGIN DH PARAMETERS-----
|
||||
MIGHAoGBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiKZ8x0Agu+pjsTmyJR
|
||||
Sgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY37WsL
|
||||
/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7OZTgf//////////AgEC
|
||||
-----END DH PARAMETERS-----';
|
||||
$this->assertSame($b, "$a");
|
||||
}
|
||||
|
||||
public function testParametersWithInteger()
|
||||
{
|
||||
$a = DH::createParameters(512);
|
||||
$this->assertInternalType('string', "$a");
|
||||
}
|
||||
|
||||
public function testParametersWithBigIntegers()
|
||||
{
|
||||
$prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
|
||||
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
|
||||
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
|
||||
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
|
||||
$prime = new BigInteger($prime, 16);
|
||||
$base = new BigInteger(2);
|
||||
$a = DH::createParameters($prime, $base);
|
||||
$a = str_replace("\r\n", "\n", trim($a));
|
||||
$b = '-----BEGIN DH PARAMETERS-----
|
||||
MIGHAoGBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiKZ8x0Agu+pjsTmyJR
|
||||
Sgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY37WsL
|
||||
/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7OZTgf//////////AgEC
|
||||
-----END DH PARAMETERS-----';
|
||||
$this->assertSame($b, "$a");
|
||||
}
|
||||
|
||||
public function testCreateKey()
|
||||
{
|
||||
$param = DH::createParameters('diffie-hellman-group1-sha1');
|
||||
$key = DH::createKey($param);
|
||||
$this->assertInternalType('string', "$key");
|
||||
$this->assertInternalType('string', (string) $key->getPublicKey());
|
||||
}
|
||||
|
||||
public function testLoadPrivate()
|
||||
{
|
||||
$a = DH::load('-----BEGIN PRIVATE KEY-----
|
||||
MIIBIgIBADCBlQYJKoZIhvcNAQMBMIGHAoGBAP//////////yQ/aoiFowjTExmKL
|
||||
gNwc0SkCTgiKZ8x0Agu+pjsTmyJRSgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVt
|
||||
bVHCReSFtXZiXn7G9ExC6aY37WsL/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR
|
||||
7OZTgf//////////AgECBIGEAoGBALJhtp0aNlkpKTcY1qj519XB8CPc7aZii0xV
|
||||
bbb/3R93sweVmk2PlkSqxc2kdcofhL8Ev0DJKxB40Ipdqja71VoBbDZ2nMzS0J6s
|
||||
b6R8Z19Xazc0wq+p/wqalmnuCMBUBuuQ8aNNaW8FGwFwAI3I6CuQSsKObVDJO25m
|
||||
eKDXQq5i
|
||||
-----END PRIVATE KEY-----');
|
||||
$this->assertInstanceOf(PrivateKey::class, $a);
|
||||
$this->assertInstanceOf(PublicKey::class, $a->getPublicKey());
|
||||
$this->assertInstanceOf(Parameters::class, $a->getParameters());
|
||||
}
|
||||
|
||||
public function testLoadPublic()
|
||||
{
|
||||
$a = DH::load('-----BEGIN PUBLIC KEY-----
|
||||
MIIBHzCBlQYJKoZIhvcNAQMBMIGHAoGBAP//////////yQ/aoiFowjTExmKLgNwc
|
||||
0SkCTgiKZ8x0Agu+pjsTmyJRSgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHC
|
||||
ReSFtXZiXn7G9ExC6aY37WsL/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7OZT
|
||||
gf//////////AgECA4GEAAKBgCsa1YmlaQIvbOuIi/6DKr7jsdMcv50u/Opemca5
|
||||
i2REGZNPWmF3SRPrtq/4urrDRU0F2eQks7qnTkrauPK1/UvE1gwbqWrWgBko+6L+
|
||||
Q3ADAIcv9LEmTBnSAOsCs1K9ExAmSv/T2/4+9dW28UYb+p/uV477d1wf+nCWS6VU
|
||||
/gTm
|
||||
-----END PUBLIC KEY-----');
|
||||
$this->assertInstanceOf(PublicKey::class, $a);
|
||||
}
|
||||
|
||||
public function testLoadParameters()
|
||||
{
|
||||
$a = DH::load('-----BEGIN DH PARAMETERS-----
|
||||
MIGHAoGBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiKZ8x0Agu+pjsTmyJR
|
||||
Sgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY37WsL
|
||||
/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7OZTgf//////////AgEC
|
||||
-----END DH PARAMETERS-----');
|
||||
$this->assertInstanceOf(Parameters::class, $a);
|
||||
}
|
||||
|
||||
public function testComputeSecretWithPublicKey()
|
||||
{
|
||||
$ourPriv = DH::load('-----BEGIN PRIVATE KEY-----
|
||||
MIIBIgIBADCBlQYJKoZIhvcNAQMBMIGHAoGBAP//////////yQ/aoiFowjTExmKL
|
||||
gNwc0SkCTgiKZ8x0Agu+pjsTmyJRSgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVt
|
||||
bVHCReSFtXZiXn7G9ExC6aY37WsL/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR
|
||||
7OZTgf//////////AgECBIGEAoGBALJhtp0aNlkpKTcY1qj519XB8CPc7aZii0xV
|
||||
bbb/3R93sweVmk2PlkSqxc2kdcofhL8Ev0DJKxB40Ipdqja71VoBbDZ2nMzS0J6s
|
||||
b6R8Z19Xazc0wq+p/wqalmnuCMBUBuuQ8aNNaW8FGwFwAI3I6CuQSsKObVDJO25m
|
||||
eKDXQq5i
|
||||
-----END PRIVATE KEY-----');
|
||||
$theirPub = DH::load('-----BEGIN PUBLIC KEY-----
|
||||
MIIBHzCBlQYJKoZIhvcNAQMBMIGHAoGBAP//////////yQ/aoiFowjTExmKLgNwc
|
||||
0SkCTgiKZ8x0Agu+pjsTmyJRSgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHC
|
||||
ReSFtXZiXn7G9ExC6aY37WsL/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7OZT
|
||||
gf//////////AgECA4GEAAKBgCsa1YmlaQIvbOuIi/6DKr7jsdMcv50u/Opemca5
|
||||
i2REGZNPWmF3SRPrtq/4urrDRU0F2eQks7qnTkrauPK1/UvE1gwbqWrWgBko+6L+
|
||||
Q3ADAIcv9LEmTBnSAOsCs1K9ExAmSv/T2/4+9dW28UYb+p/uV477d1wf+nCWS6VU
|
||||
/gTm
|
||||
-----END PUBLIC KEY-----');
|
||||
$this->assertInternalType('string', DH::computeSecret($ourPriv, $theirPub));
|
||||
}
|
||||
|
||||
public function testComputeSecret()
|
||||
{
|
||||
// Ed25519 isn't normally used for DH (that honor goes to Curve25519) but that's not to say it can't
|
||||
// be used
|
||||
$curves = ['nistp256', 'curve25519', 'Ed25519'];
|
||||
foreach ($curves as $curve) {
|
||||
$ourPriv = EC::createKey($curve);
|
||||
$theirPub = EC::createKey($curve)->getPublicKey();
|
||||
$this->assertInternalType('string', DH::computeSecret($ourPriv, $theirPub));
|
||||
}
|
||||
}
|
||||
|
||||
public function testEphemeralECDH()
|
||||
{
|
||||
// an RSA like hybrid cryptosystem can be done with ephemeral key ECDH
|
||||
|
||||
$plaintext = 'hello, world!';
|
||||
|
||||
$ourEphemeralPrivate = EC::createKey('Curve25519');
|
||||
$ourEphemeralPublic = $ourEphemeralPrivate->getPublicKey();
|
||||
|
||||
$theirPrivate = EC::createKey('Curve25519');
|
||||
$theirPublic = $theirPrivate->getPublicKey();
|
||||
|
||||
$key = DH::computeSecret($ourEphemeralPrivate, $theirPublic);
|
||||
|
||||
$aes = new AES('ctr');
|
||||
$aes->setKey(substr($key, 0, 16));
|
||||
$aes->setIV(substr($key, 16, 16));
|
||||
|
||||
$encrypted =
|
||||
$ourEphemeralPublic->toString('Curve25519Public') .
|
||||
$aes->encrypt($plaintext);
|
||||
|
||||
$theirPublic = substr($encrypted, 0, 32);
|
||||
$theirPublic = EC::loadFormat('Curve25519Public', $theirPublic);
|
||||
|
||||
$ourPrivate = $theirPrivate;
|
||||
|
||||
$key = DH::computeSecret($ourPrivate, $theirPublic);
|
||||
|
||||
$aes = new AES('ctr');
|
||||
$aes->setKey(substr($key, 0, 16));
|
||||
$aes->setIV(substr($key, 16, 16));
|
||||
|
||||
$this->assertSame($plaintext, $aes->decrypt(substr($encrypted, 32)));
|
||||
}
|
||||
|
||||
public function testMultiPartyDH()
|
||||
{
|
||||
// in multi party (EC)DH everyone, for each public key, everyone (save for the public key owner) "applies"
|
||||
// their private key to it. they do so in series (as opposed to in parallel) and then everyone winds up
|
||||
// with the same shared secret
|
||||
|
||||
$numParties = 4;
|
||||
|
||||
// create private keys
|
||||
$parties = [];
|
||||
for ($i = 0; $i < $numParties; $i++) {
|
||||
$parties[] = EC::createKey('Curve25519');
|
||||
}
|
||||
|
||||
// create shared secrets
|
||||
$secrets = [];
|
||||
for ($i = 0; $i < $numParties; $i++) {
|
||||
$secrets[$i] = $parties[$i]->getPublicKey();
|
||||
for ($j = 0; $j < $numParties; $j++) {
|
||||
if ($i == $j) {
|
||||
continue;
|
||||
}
|
||||
$secrets[$i] = DH::computeSecret($parties[$j], $secrets[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = 1; $i < $numParties; $i++) {
|
||||
$this->assertSame($secrets[0], $secrets[$i]);
|
||||
}
|
||||
}
|
||||
}
|
@ -47,6 +47,9 @@ class Unit_Crypt_EC_CurveTest extends PhpseclibTestCase
|
||||
continue;
|
||||
}
|
||||
$testName = $file->getBasename('.php');
|
||||
if ($testName == 'Curve25519' || $testName == 'Curve448') {
|
||||
continue;
|
||||
}
|
||||
$class = 'phpseclib\Crypt\EC\Curves\\' . $testName;
|
||||
$reflect = new \ReflectionClass($class);
|
||||
if ($reflect->isFinal()) {
|
||||
@ -66,6 +69,9 @@ class Unit_Crypt_EC_CurveTest extends PhpseclibTestCase
|
||||
continue;
|
||||
}
|
||||
$testName = $file->getBasename('.php');
|
||||
if ($testName == 'Curve25519' || $testName == 'Curve448') {
|
||||
continue;
|
||||
}
|
||||
$curves[] = [$testName];
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user