From b1aef24a866fbb831b0200f85d0e587ac840fd0c Mon Sep 17 00:00:00 2001 From: terrafrost Date: Sun, 21 Aug 2022 19:48:07 -0500 Subject: [PATCH] EC: eliminate dynamic property from Ed25519/448 handling --- phpseclib/Crypt/Common/AsymmetricKey.php | 2 ++ phpseclib/Crypt/Common/Formats/Keys/JWK.php | 2 +- phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php | 1 - phpseclib/Crypt/DSA/Formats/Keys/PKCS1.php | 2 +- phpseclib/Crypt/EC.php | 9 ++++++++- phpseclib/Crypt/EC/Curves/Ed25519.php | 10 ++++++---- phpseclib/Crypt/EC/Curves/Ed448.php | 9 +++++++-- phpseclib/Crypt/EC/Formats/Keys/JWK.php | 17 ++++++++--------- .../Crypt/EC/Formats/Keys/MontgomeryPrivate.php | 3 ++- phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php | 9 +++++---- phpseclib/Crypt/EC/Formats/Keys/PKCS1.php | 3 ++- phpseclib/Crypt/EC/Formats/Keys/PKCS8.php | 9 ++++++--- phpseclib/Crypt/EC/Formats/Keys/PuTTY.php | 9 ++++++--- phpseclib/Crypt/EC/Formats/Keys/libsodium.php | 13 ++++++++----- phpseclib/Crypt/EC/PrivateKey.php | 9 +++++++-- tests/Unit/Crypt/EC/Ed448PrivateKey.php | 4 +++- 16 files changed, 72 insertions(+), 39 deletions(-) diff --git a/phpseclib/Crypt/Common/AsymmetricKey.php b/phpseclib/Crypt/Common/AsymmetricKey.php index 75537cd9..a261dfc4 100644 --- a/phpseclib/Crypt/Common/AsymmetricKey.php +++ b/phpseclib/Crypt/Common/AsymmetricKey.php @@ -156,6 +156,7 @@ abstract class AsymmetricKey } $components['format'] = $format; + $components['secret'] = isset($components['secret']) ? $components['secret'] : ''; $comment = isset($components['comment']) ? $components['comment'] : null; $new = static::onLoad($components); $new->format = $format; @@ -235,6 +236,7 @@ abstract class AsymmetricKey } $components['format'] = $format; + $components['secret'] = isset($components['secret']) ? $components['secret'] : ''; $new = static::onLoad($components); $new->format = $format; diff --git a/phpseclib/Crypt/Common/Formats/Keys/JWK.php b/phpseclib/Crypt/Common/Formats/Keys/JWK.php index 1a2f0ecf..4c761b83 100644 --- a/phpseclib/Crypt/Common/Formats/Keys/JWK.php +++ b/phpseclib/Crypt/Common/Formats/Keys/JWK.php @@ -50,7 +50,7 @@ abstract class JWK return $key; } - if (count($key->keys) != 1) { + if (count($key->keys) != 1) { throw new \RuntimeException('Although the JWK key format supports multiple keys phpseclib does not'); } diff --git a/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php b/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php index 1b79e801..fe3d85bd 100644 --- a/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php +++ b/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php @@ -18,7 +18,6 @@ namespace phpseclib3\Crypt\Common\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\AES; use phpseclib3\Crypt\Random; -use phpseclib3\Exception\UnsupportedFormatException; /** * OpenSSH Formatted RSA Key Handler diff --git a/phpseclib/Crypt/DSA/Formats/Keys/PKCS1.php b/phpseclib/Crypt/DSA/Formats/Keys/PKCS1.php index 3d43ff03..52a04992 100644 --- a/phpseclib/Crypt/DSA/Formats/Keys/PKCS1.php +++ b/phpseclib/Crypt/DSA/Formats/Keys/PKCS1.php @@ -27,11 +27,11 @@ namespace phpseclib3\Crypt\DSA\Formats\Keys; +use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor; use phpseclib3\File\ASN1; use phpseclib3\File\ASN1\Maps; use phpseclib3\Math\BigInteger; -use phpseclib3\Common\Functions\Strings; /** * PKCS#1 Formatted DSA Key Handler diff --git a/phpseclib/Crypt/EC.php b/phpseclib/Crypt/EC.php index a6dc40c7..44d850a2 100644 --- a/phpseclib/Crypt/EC.php +++ b/phpseclib/Crypt/EC.php @@ -170,7 +170,13 @@ abstract class EC extends AsymmetricKey $reflect->getShortName(); $curve = new $curve(); - $privatekey->dA = $dA = $curve->createRandomMultiplier(); + if ($curve instanceof TwistedEdwardsCurve) { + $arr = $curve->extractSecret(Random::string($curve instanceof Ed448 ? 57 : 32)); + $privatekey->dA = $dA = $arr['dA']; + $privatekey->secret = $arr['secret']; + } else { + $privatekey->dA = $dA = $curve->createRandomMultiplier(); + } if ($curve instanceof Curve25519 && self::$engines['libsodium']) { //$r = pack('H*', '0900000000000000000000000000000000000000000000000000000000000000'); //$QA = sodium_crypto_scalarmult($dA->toBytes(), $r); @@ -220,6 +226,7 @@ abstract class EC extends AsymmetricKey if (isset($components['dA'])) { $new->dA = $components['dA']; + $new->secret = $components['secret']; } if ($new->curve instanceof TwistedEdwardsCurve) { diff --git a/phpseclib/Crypt/EC/Curves/Ed25519.php b/phpseclib/Crypt/EC/Curves/Ed25519.php index 0b776a51..9d3de684 100644 --- a/phpseclib/Crypt/EC/Curves/Ed25519.php +++ b/phpseclib/Crypt/EC/Curves/Ed25519.php @@ -156,7 +156,7 @@ class Ed25519 extends TwistedEdwards * Used by the various key handlers * * @param string $str - * @return \phpseclib3\Math\PrimeField\Integer + * @return array */ public function extractSecret($str) { @@ -179,8 +179,10 @@ class Ed25519 extends TwistedEdwards // secret scalar s. $dA = new BigInteger($h, 256); - $dA->secret = $str; - return $dA; + return [ + 'dA' => $dA, + 'secret' => $str + ]; } /** @@ -209,7 +211,7 @@ class Ed25519 extends TwistedEdwards */ public function createRandomMultiplier() { - return $this->extractSecret(Random::string(32)); + return $this->extractSecret(Random::string(32))['dA']; } /** diff --git a/phpseclib/Crypt/EC/Curves/Ed448.php b/phpseclib/Crypt/EC/Curves/Ed448.php index c4b14428..5451f909 100644 --- a/phpseclib/Crypt/EC/Curves/Ed448.php +++ b/phpseclib/Crypt/EC/Curves/Ed448.php @@ -97,7 +97,7 @@ class Ed448 extends TwistedEdwards * Used by the various key handlers * * @param string $str - * @return \phpseclib3\Math\PrimeField\Integer + * @return array */ public function extractSecret($str) { @@ -121,6 +121,11 @@ class Ed448 extends TwistedEdwards // secret scalar s. $dA = new BigInteger($h, 256); + return [ + 'dA' => $dA, + 'secret' => $str + ]; + $dA->secret = $str; return $dA; } @@ -150,7 +155,7 @@ class Ed448 extends TwistedEdwards */ public function createRandomMultiplier() { - return $this->extractSecret(Random::string(57)); + return $this->extractSecret(Random::string(57))['dA']; } /** diff --git a/phpseclib/Crypt/EC/Formats/Keys/JWK.php b/phpseclib/Crypt/EC/Formats/Keys/JWK.php index f79f7861..fd18a981 100644 --- a/phpseclib/Crypt/EC/Formats/Keys/JWK.php +++ b/phpseclib/Crypt/EC/Formats/Keys/JWK.php @@ -13,15 +13,15 @@ namespace phpseclib3\Crypt\EC\Formats\Keys; -use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common\Formats\Keys\JWK as Progenitor; +use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; use phpseclib3\Crypt\EC\Curves\Ed25519; +use phpseclib3\Crypt\EC\Curves\secp256k1; use phpseclib3\Crypt\EC\Curves\secp256r1; use phpseclib3\Crypt\EC\Curves\secp384r1; use phpseclib3\Crypt\EC\Curves\secp521r1; -use phpseclib3\Crypt\EC\Curves\secp256k1; use phpseclib3\Exception\UnsupportedCurveException; use phpseclib3\Math\BigInteger; @@ -71,15 +71,15 @@ abstract class JWK extends Progenitor } $curve = '\phpseclib3\Crypt\EC\Curves\\' . str_replace('P-', 'nistp', $key->crv); - $curve = new $curve; + $curve = new $curve(); if ($curve instanceof TwistedEdwardsCurve) { $QA = self::extractPoint(Strings::base64url_decode($key->x), $curve); if (!isset($key->d)) { return compact('curve', 'QA'); } - $dA = $curve->extractSecret(Strings::base64url_decode($key->d)); - return compact('curve', 'dA', 'QA'); + $arr = $curve->extractSecret(Strings::base64url_decode($key->d)); + return compact('curve', 'QA') + $arr; } $QA = [ @@ -173,16 +173,15 @@ abstract class JWK extends Progenitor * @param \phpseclib3\Math\BigInteger $privateKey * @param \phpseclib3\Crypt\EC\Curves\Ed25519 $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @param string $secret optional * @param string $password optional * @param array $options optional * @return string */ - public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $password = '', array $options = []) + public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = '', array $options = []) { $key = self::savePublicKeyHelper($curve, $publicKey); - $key['d'] = $curve instanceof TwistedEdwardsCurve ? - $privateKey->secret : - $privateKey->toBytes(); + $key['d'] = $curve instanceof TwistedEdwardsCurve ? $secret : $privateKey->toBytes(); $key['d'] = Strings::base64url_encode($key['d']); return self::wrapKey($key, $options); diff --git a/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php b/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php index 3f1d40fc..5741b05a 100644 --- a/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php +++ b/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php @@ -86,10 +86,11 @@ abstract class MontgomeryPrivate * @param \phpseclib3\Math\BigInteger $privateKey * @param \phpseclib3\Crypt\EC\BaseCurves\Montgomery $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @param string $secret optional * @param string $password optional * @return string */ - public static function savePrivateKey(BigInteger $privateKey, MontgomeryCurve $curve, array $publicKey, $password = '') + public static function savePrivateKey(BigInteger $privateKey, MontgomeryCurve $curve, array $publicKey, $secret = null, $password = '') { if (!empty($password) && is_string($password)) { throw new UnsupportedFormatException('MontgomeryPrivate private keys do not support encryption'); diff --git a/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php b/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php index a3aa482f..2cd3e19d 100644 --- a/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php +++ b/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php @@ -174,24 +174,25 @@ abstract class OpenSSH extends Progenitor * @param \phpseclib3\Math\BigInteger $privateKey * @param \phpseclib3\Crypt\EC\Curves\Ed25519 $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @param string $secret optional * @param string $password optional * @param array $options optional * @return string */ - public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $password = '', array $options = []) + public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = '', array $options = []) { if ($curve instanceof Ed25519) { - if (!isset($privateKey->secret)) { + if (!isset($secret)) { throw new \RuntimeException('Private Key does not have a secret set'); } - if (strlen($privateKey->secret) != 32) { + if (strlen($secret) != 32) { throw new \RuntimeException('Private Key secret is not of the correct length'); } $pubKey = $curve->encodePoint($publicKey); $publicKey = Strings::packSSH2('ss', 'ssh-ed25519', $pubKey); - $privateKey = Strings::packSSH2('sss', 'ssh-ed25519', $pubKey, $privateKey->secret . $pubKey); + $privateKey = Strings::packSSH2('sss', 'ssh-ed25519', $pubKey, $secret . $pubKey); return self::wrapPrivateKey($publicKey, $privateKey, $password, $options); } diff --git a/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php b/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php index 983a28c2..9f4b3300 100644 --- a/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php +++ b/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php @@ -165,11 +165,12 @@ abstract class PKCS1 extends Progenitor * @param \phpseclib3\Math\BigInteger $privateKey * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @param string $secret optional * @param string $password optional * @param array $options optional * @return string */ - public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $password = '', array $options = []) + public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = '', array $options = []) { self::initialize_static_variables(); diff --git a/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php b/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php index 5f65421b..a75162ed 100644 --- a/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php +++ b/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php @@ -150,7 +150,9 @@ abstract class PKCS8 extends Progenitor 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)); + $arr = $components['curve']->extractSecret(substr($key['privateKey'], 2)); + $components['dA'] = $arr['dA']; + $components['secret'] = $arr['secret']; } if (isset($key['publicKey'])) { @@ -205,11 +207,12 @@ abstract class PKCS8 extends Progenitor * @param \phpseclib3\Math\BigInteger $privateKey * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @param string $secret optional * @param string $password optional * @param array $options optional * @return string */ - public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $password = '', array $options = []) + public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = '', array $options = []) { self::initialize_static_variables(); @@ -219,7 +222,7 @@ abstract class PKCS8 extends Progenitor if ($curve instanceof TwistedEdwardsCurve) { return self::wrapPrivateKey( - "\x04\x20" . $privateKey->secret, + "\x04\x20" . $secret, [], null, $password, diff --git a/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php b/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php index 985a4182..866c883f 100644 --- a/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php +++ b/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php @@ -70,7 +70,9 @@ abstract class PuTTY extends Progenitor 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); + $arr = $components['curve']->extractSecret($private); + $components['dA'] = $arr['dA']; + $components['secret'] = $arr['secret']; } else { list($components['dA']) = Strings::unpackSSH2('i', $private); $components['curve']->rangeCheck($components['dA']); @@ -85,11 +87,12 @@ abstract class PuTTY extends Progenitor * @param \phpseclib3\Math\BigInteger $privateKey * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @param string $secret optional * @param string $password optional * @param array $options optional * @return string */ - public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $password = false, array $options = []) + public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = false, array $options = []) { self::initialize_static_variables(); @@ -109,7 +112,7 @@ abstract class PuTTY extends Progenitor } $private = $curve instanceof TwistedEdwardsCurve ? - Strings::packSSH2('s', $privateKey->secret) : + Strings::packSSH2('s', $secret) : Strings::packSSH2('s', $private); return self::wrapPrivateKey($public, $private, $name, $password, $options); diff --git a/phpseclib/Crypt/EC/Formats/Keys/libsodium.php b/phpseclib/Crypt/EC/Formats/Keys/libsodium.php index c9259a07..2be6ba59 100644 --- a/phpseclib/Crypt/EC/Formats/Keys/libsodium.php +++ b/phpseclib/Crypt/EC/Formats/Keys/libsodium.php @@ -67,7 +67,9 @@ abstract class libsodium $curve = new Ed25519(); $components = ['curve' => $curve]; if (isset($private)) { - $components['dA'] = $curve->extractSecret($private); + $arr = $curve->extractSecret($private); + $components['dA'] = $arr['dA']; + $components['secret'] = $arr['secret']; } $components['QA'] = isset($public) ? self::extractPoint($public, $curve) : @@ -94,20 +96,21 @@ abstract class libsodium * @param \phpseclib3\Math\BigInteger $privateKey * @param \phpseclib3\Crypt\EC\Curves\Ed25519 $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @param string $secret optional * @param string $password optional * @return string */ - public static function savePrivateKey(BigInteger $privateKey, Ed25519 $curve, array $publicKey, $password = '') + public static function savePrivateKey(BigInteger $privateKey, Ed25519 $curve, array $publicKey, $secret = null, $password = '') { - if (!isset($privateKey->secret)) { + if (!isset($secret)) { throw new \RuntimeException('Private Key does not have a secret set'); } - if (strlen($privateKey->secret) != 32) { + if (strlen($secret) != 32) { throw new \RuntimeException('Private Key secret is not of the correct length'); } if (!empty($password) && is_string($password)) { throw new UnsupportedFormatException('libsodium private keys do not support encryption'); } - return $privateKey->secret . $curve->encodePoint($publicKey); + return $secret . $curve->encodePoint($publicKey); } } diff --git a/phpseclib/Crypt/EC/PrivateKey.php b/phpseclib/Crypt/EC/PrivateKey.php index 3632b778..8bf86862 100644 --- a/phpseclib/Crypt/EC/PrivateKey.php +++ b/phpseclib/Crypt/EC/PrivateKey.php @@ -44,6 +44,11 @@ class PrivateKey extends EC implements Common\PrivateKey */ protected $dA; + /** + * @var string + */ + protected $secret; + /** * Multiplies an encoded point by the private key * @@ -112,7 +117,7 @@ class PrivateKey extends EC implements Common\PrivateKey $curve = $this->curve; $hash = new Hash($curve::HASH); - $secret = substr($hash->hash($this->dA->secret), $curve::SIZE); + $secret = substr($hash->hash($this->secret), $curve::SIZE); if ($curve instanceof Ed25519) { $dom = !isset($this->context) ? '' : @@ -217,7 +222,7 @@ class PrivateKey extends EC implements Common\PrivateKey { $type = self::validatePlugin('Keys', $type, 'savePrivateKey'); - return $type::savePrivateKey($this->dA, $this->curve, $this->QA, $this->password, $options); + return $type::savePrivateKey($this->dA, $this->curve, $this->QA, $this->secret, $this->password, $options); } /** diff --git a/tests/Unit/Crypt/EC/Ed448PrivateKey.php b/tests/Unit/Crypt/EC/Ed448PrivateKey.php index b748be87..9c67e213 100644 --- a/tests/Unit/Crypt/EC/Ed448PrivateKey.php +++ b/tests/Unit/Crypt/EC/Ed448PrivateKey.php @@ -14,7 +14,9 @@ class Ed448PrivateKey } $components = ['curve' => new Ed448()]; - $components['dA'] = $components['curve']->extractSecret($key); + $arr = $components['curve']->extractSecret($key); + $components['dA'] = $arr['dA']; + $components['secret'] = $arr['secret']; $components['QA'] = $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']); return $components;