BaseCurves
9 months ago
Curves
9 months ago
Formats
9 months ago
.htaccess
9 months ago
Parameters.php
9 months ago
PrivateKey.php
9 months ago
PublicKey.php
9 months ago
index.html
9 months ago
web.config
9 months ago
PrivateKey.php
253 lines
| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * EC Private Key |
| 5 | * |
| 6 | * @author Jim Wigginton <terrafrost@php.net> |
| 7 | * @copyright 2015 Jim Wigginton |
| 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License |
| 9 | * @link http://phpseclib.sourceforge.net |
| 10 | */ |
| 11 | |
| 12 | declare(strict_types=1); |
| 13 | |
| 14 | namespace phpseclib3\Crypt\EC; |
| 15 | |
| 16 | use phpseclib3\Common\Functions\Strings; |
| 17 | use phpseclib3\Crypt\Common; |
| 18 | use phpseclib3\Crypt\EC; |
| 19 | use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; |
| 20 | use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; |
| 21 | use phpseclib3\Crypt\EC\Curves\Curve25519; |
| 22 | use phpseclib3\Crypt\EC\Curves\Ed25519; |
| 23 | use phpseclib3\Crypt\EC\Formats\Keys\PKCS1; |
| 24 | use phpseclib3\Crypt\EC\Formats\Signature\ASN1 as ASN1Signature; |
| 25 | use phpseclib3\Crypt\Hash; |
| 26 | use phpseclib3\Exception\RuntimeException; |
| 27 | use phpseclib3\Exception\UnsupportedOperationException; |
| 28 | use phpseclib3\Math\BigInteger; |
| 29 | |
| 30 | /** |
| 31 | * EC Private Key |
| 32 | * |
| 33 | * @author Jim Wigginton <terrafrost@php.net> |
| 34 | */ |
| 35 | final class PrivateKey extends EC implements Common\PrivateKey |
| 36 | { |
| 37 | use Common\Traits\PasswordProtected; |
| 38 | |
| 39 | /** |
| 40 | * Private Key dA |
| 41 | * |
| 42 | * sign() converts this to a BigInteger so one might wonder why this is a FiniteFieldInteger instead of |
| 43 | * a BigInteger. That's because a FiniteFieldInteger, when converted to a byte string, is null padded by |
| 44 | * a certain amount whereas a BigInteger isn't. |
| 45 | * |
| 46 | * @var object |
| 47 | */ |
| 48 | protected $dA; |
| 49 | |
| 50 | /** |
| 51 | * @var string |
| 52 | */ |
| 53 | protected $secret; |
| 54 | |
| 55 | /** |
| 56 | * Multiplies an encoded point by the private key |
| 57 | * |
| 58 | * Used by ECDH |
| 59 | */ |
| 60 | public function multiply(string $coordinates): string |
| 61 | { |
| 62 | if ($this->curve instanceof MontgomeryCurve) { |
| 63 | if ($this->curve instanceof Curve25519 && self::$engines['libsodium']) { |
| 64 | return sodium_crypto_scalarmult($this->dA->toBytes(), $coordinates); |
| 65 | } |
| 66 | |
| 67 | $point = [$this->curve->convertInteger(new BigInteger(strrev($coordinates), 256))]; |
| 68 | $point = $this->curve->multiplyPoint($point, $this->dA); |
| 69 | return strrev($point[0]->toBytes(true)); |
| 70 | } |
| 71 | if (!$this->curve instanceof TwistedEdwardsCurve) { |
| 72 | $coordinates = "\0$coordinates"; |
| 73 | } |
| 74 | $point = PKCS1::extractPoint($coordinates, $this->curve); |
| 75 | $point = $this->curve->multiplyPoint($point, $this->dA); |
| 76 | if ($this->curve instanceof TwistedEdwardsCurve) { |
| 77 | return $this->curve->encodePoint($point); |
| 78 | } |
| 79 | if (empty($point)) { |
| 80 | throw new RuntimeException('The infinity point is invalid'); |
| 81 | } |
| 82 | return "\4" . $point[0]->toBytes(true) . $point[1]->toBytes(true); |
| 83 | } |
| 84 | |
| 85 | /** |
| 86 | * Create a signature |
| 87 | * |
| 88 | * @see self::verify() |
| 89 | * @param string $message |
| 90 | */ |
| 91 | public function sign($message) |
| 92 | { |
| 93 | if ($this->curve instanceof MontgomeryCurve) { |
| 94 | throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures'); |
| 95 | } |
| 96 | |
| 97 | $dA = $this->dA; |
| 98 | $order = $this->curve->getOrder(); |
| 99 | |
| 100 | $shortFormat = $this->shortFormat; |
| 101 | $format = $this->sigFormat; |
| 102 | if ($format === false) { |
| 103 | return false; |
| 104 | } |
| 105 | |
| 106 | if ($this->curve instanceof TwistedEdwardsCurve) { |
| 107 | if ($this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context)) { |
| 108 | $result = sodium_crypto_sign_detached($message, $this->withPassword()->toString('libsodium')); |
| 109 | return $shortFormat == 'SSH2' ? Strings::packSSH2('ss', 'ssh-' . strtolower($this->getCurve()), $result) : $result; |
| 110 | } |
| 111 | |
| 112 | // contexts (Ed25519ctx) are supported but prehashing (Ed25519ph) is not. |
| 113 | // quoting https://tools.ietf.org/html/rfc8032#section-8.5 , |
| 114 | // "The Ed25519ph and Ed448ph variants ... SHOULD NOT be used" |
| 115 | $A = $this->curve->encodePoint($this->QA); |
| 116 | $curve = $this->curve; |
| 117 | $hash = new Hash($curve::HASH); |
| 118 | |
| 119 | $secret = substr($hash->hash($this->secret), $curve::SIZE); |
| 120 | |
| 121 | if ($curve instanceof Ed25519) { |
| 122 | $dom = !isset($this->context) ? '' : |
| 123 | 'SigEd25519 no Ed25519 collisions' . "\0" . chr(strlen($this->context)) . $this->context; |
| 124 | } else { |
| 125 | $context = $this->context ?? ''; |
| 126 | $dom = 'SigEd448' . "\0" . chr(strlen($context)) . $context; |
| 127 | } |
| 128 | // SHA-512(dom2(F, C) || prefix || PH(M)) |
| 129 | $r = $hash->hash($dom . $secret . $message); |
| 130 | $r = strrev($r); |
| 131 | $r = new BigInteger($r, 256); |
| 132 | [, $r] = $r->divide($order); |
| 133 | $R = $curve->multiplyPoint($curve->getBasePoint(), $r); |
| 134 | $R = $curve->encodePoint($R); |
| 135 | $k = $hash->hash($dom . $R . $A . $message); |
| 136 | $k = strrev($k); |
| 137 | $k = new BigInteger($k, 256); |
| 138 | [, $k] = $k->divide($order); |
| 139 | $S = $k->multiply($dA)->add($r); |
| 140 | [, $S] = $S->divide($order); |
| 141 | $S = str_pad(strrev($S->toBytes()), $curve::SIZE, "\0"); |
| 142 | return $shortFormat == 'SSH2' ? Strings::packSSH2('ss', 'ssh-' . strtolower($this->getCurve()), $R . $S) : $R . $S; |
| 143 | } |
| 144 | |
| 145 | if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) { |
| 146 | $signature = ''; |
| 147 | // altho PHP's OpenSSL bindings only supported EC key creation in PHP 7.1 they've long |
| 148 | // supported signing / verification |
| 149 | // we use specified curves to avoid issues with OpenSSL possibly not supporting a given named curve; |
| 150 | // doing this may mean some curve-specific optimizations can't be used but idk if OpenSSL even |
| 151 | // has curve-specific optimizations |
| 152 | $result = openssl_sign($message, $signature, $this->withPassword()->toString('PKCS8', ['namedCurve' => false]), $this->hash->getHash()); |
| 153 | |
| 154 | if ($result) { |
| 155 | if ($shortFormat == 'ASN1') { |
| 156 | return $signature; |
| 157 | } |
| 158 | |
| 159 | extract(ASN1Signature::load($signature)); |
| 160 | |
| 161 | return $shortFormat == 'SSH2' ? $format::save($r, $s, $this->getCurve()) : $format::save($r, $s); |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | $e = $this->hash->hash($message); |
| 166 | $e = new BigInteger($e, 256); |
| 167 | |
| 168 | $Ln = $this->hash->getLength() - $order->getLength(); |
| 169 | $z = $Ln > 0 ? $e->bitwise_rightShift($Ln) : $e; |
| 170 | |
| 171 | while (true) { |
| 172 | $k = BigInteger::randomRange(self::$one, $order->subtract(self::$one)); |
| 173 | [$x, $y] = $this->curve->multiplyPoint($this->curve->getBasePoint(), $k); |
| 174 | $x = $x->toBigInteger(); |
| 175 | [, $r] = $x->divide($order); |
| 176 | if ($r->equals(self::$zero)) { |
| 177 | continue; |
| 178 | } |
| 179 | $kinv = $k->modInverse($order); |
| 180 | $temp = $z->add($dA->multiply($r)); |
| 181 | $temp = $kinv->multiply($temp); |
| 182 | [, $s] = $temp->divide($order); |
| 183 | if (!$s->equals(self::$zero)) { |
| 184 | break; |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | // the following is an RFC6979 compliant implementation of deterministic ECDSA |
| 189 | // it's unused because it's mainly intended for use when a good CSPRNG isn't |
| 190 | // available. if phpseclib's CSPRNG isn't good then even key generation is |
| 191 | // suspect |
| 192 | /* |
| 193 | // if this were actually being used it'd probably be better if this lived in load() and createKey() |
| 194 | $this->q = $this->curve->getOrder(); |
| 195 | $dA = $this->dA->toBigInteger(); |
| 196 | $this->x = $dA; |
| 197 | |
| 198 | $h1 = $this->hash->hash($message); |
| 199 | $k = $this->computek($h1); |
| 200 | list($x, $y) = $this->curve->multiplyPoint($this->curve->getBasePoint(), $k); |
| 201 | $x = $x->toBigInteger(); |
| 202 | list(, $r) = $x->divide($this->q); |
| 203 | $kinv = $k->modInverse($this->q); |
| 204 | $h1 = $this->bits2int($h1); |
| 205 | $temp = $h1->add($dA->multiply($r)); |
| 206 | $temp = $kinv->multiply($temp); |
| 207 | list(, $s) = $temp->divide($this->q); |
| 208 | */ |
| 209 | |
| 210 | return $shortFormat == 'SSH2' ? $format::save($r, $s, $this->getCurve()) : $format::save($r, $s); |
| 211 | } |
| 212 | |
| 213 | /** |
| 214 | * Returns the private key |
| 215 | * |
| 216 | * @param array $options optional |
| 217 | */ |
| 218 | public function toString(string $type, array $options = []): string |
| 219 | { |
| 220 | $type = self::validatePlugin('Keys', $type, 'savePrivateKey'); |
| 221 | |
| 222 | return $type::savePrivateKey($this->dA, $this->curve, $this->QA, $this->secret, $this->password, $options); |
| 223 | } |
| 224 | |
| 225 | /** |
| 226 | * Returns the public key |
| 227 | * |
| 228 | * @see self::getPrivateKey() |
| 229 | */ |
| 230 | public function getPublicKey() |
| 231 | { |
| 232 | $format = 'PKCS8'; |
| 233 | if ($this->curve instanceof MontgomeryCurve) { |
| 234 | $format = 'MontgomeryPublic'; |
| 235 | } |
| 236 | |
| 237 | $type = self::validatePlugin('Keys', $format, 'savePublicKey'); |
| 238 | |
| 239 | $key = $type::savePublicKey($this->curve, $this->QA); |
| 240 | $key = EC::loadFormat($format, $key); |
| 241 | if ($this->curve instanceof MontgomeryCurve) { |
| 242 | return $key; |
| 243 | } |
| 244 | $key = $key |
| 245 | ->withHash($this->hash->getHash()) |
| 246 | ->withSignatureFormat($this->shortFormat); |
| 247 | if ($this->curve instanceof TwistedEdwardsCurve) { |
| 248 | $key = $key->withContext($this->context); |
| 249 | } |
| 250 | return $key; |
| 251 | } |
| 252 | } |
| 253 |