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