[ Index ] |
PHP Cross Reference of DokuWiki |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Pure-PHP FIPS 186-4 compliant implementation of DSA. 5 * 6 * PHP version 5 7 * 8 * Here's an example of how to create signatures and verify signatures with this library: 9 * <code> 10 * <?php 11 * include 'vendor/autoload.php'; 12 * 13 * $private = \phpseclib3\Crypt\DSA::createKey(); 14 * $public = $private->getPublicKey(); 15 * 16 * $plaintext = 'terrafrost'; 17 * 18 * $signature = $private->sign($plaintext); 19 * 20 * echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified'; 21 * ?> 22 * </code> 23 * 24 * @author Jim Wigginton <terrafrost@php.net> 25 * @copyright 2016 Jim Wigginton 26 * @license http://www.opensource.org/licenses/mit-license.html MIT License 27 * @link http://phpseclib.sourceforge.net 28 */ 29 30 namespace phpseclib3\Crypt; 31 32 use phpseclib3\Crypt\Common\AsymmetricKey; 33 use phpseclib3\Crypt\DSA\Parameters; 34 use phpseclib3\Crypt\DSA\PrivateKey; 35 use phpseclib3\Crypt\DSA\PublicKey; 36 use phpseclib3\Exception\InsufficientSetupException; 37 use phpseclib3\Math\BigInteger; 38 39 /** 40 * Pure-PHP FIPS 186-4 compliant implementation of DSA. 41 * 42 * @author Jim Wigginton <terrafrost@php.net> 43 */ 44 abstract class DSA extends AsymmetricKey 45 { 46 /** 47 * Algorithm Name 48 * 49 * @var string 50 */ 51 const ALGORITHM = 'DSA'; 52 53 /** 54 * DSA Prime P 55 * 56 * @var \phpseclib3\Math\BigInteger 57 */ 58 protected $p; 59 60 /** 61 * DSA Group Order q 62 * 63 * Prime divisor of p-1 64 * 65 * @var \phpseclib3\Math\BigInteger 66 */ 67 protected $q; 68 69 /** 70 * DSA Group Generator G 71 * 72 * @var \phpseclib3\Math\BigInteger 73 */ 74 protected $g; 75 76 /** 77 * DSA public key value y 78 * 79 * @var \phpseclib3\Math\BigInteger 80 */ 81 protected $y; 82 83 /** 84 * Signature Format 85 * 86 * @var string 87 */ 88 protected $sigFormat; 89 90 /** 91 * Signature Format (Short) 92 * 93 * @var string 94 */ 95 protected $shortFormat; 96 97 /** 98 * Create DSA parameters 99 * 100 * @param int $L 101 * @param int $N 102 * @return \phpseclib3\Crypt\DSA|bool 103 */ 104 public static function createParameters($L = 2048, $N = 224) 105 { 106 self::initialize_static_variables(); 107 108 $class = new \ReflectionClass(static::class); 109 if ($class->isFinal()) { 110 throw new \RuntimeException('createParameters() should not be called from final classes (' . static::class . ')'); 111 } 112 113 if (!isset(self::$engines['PHP'])) { 114 self::useBestEngine(); 115 } 116 117 switch (true) { 118 case $N == 160: 119 /* 120 in FIPS 186-1 and 186-2 N was fixed at 160 whereas K had an upper bound of 1024. 121 RFC 4253 (SSH Transport Layer Protocol) references FIPS 186-2 and as such most 122 SSH DSA implementations only support keys with an N of 160. 123 puttygen let's you set the size of L (but not the size of N) and uses 2048 as the 124 default L value. that's not really compliant with any of the FIPS standards, however, 125 for the purposes of maintaining compatibility with puttygen, we'll support it 126 */ 127 //case ($L >= 512 || $L <= 1024) && (($L & 0x3F) == 0) && $N == 160: 128 // FIPS 186-3 changed this as follows: 129 //case $L == 1024 && $N == 160: 130 case $L == 2048 && $N == 224: 131 case $L == 2048 && $N == 256: 132 case $L == 3072 && $N == 256: 133 break; 134 default: 135 throw new \InvalidArgumentException('Invalid values for N and L'); 136 } 137 138 $two = new BigInteger(2); 139 140 $q = BigInteger::randomPrime($N); 141 $divisor = $q->multiply($two); 142 143 do { 144 $x = BigInteger::random($L); 145 list(, $c) = $x->divide($divisor); 146 $p = $x->subtract($c->subtract(self::$one)); 147 } while ($p->getLength() != $L || !$p->isPrime()); 148 149 $p_1 = $p->subtract(self::$one); 150 list($e) = $p_1->divide($q); 151 152 // quoting http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf#page=50 , 153 // "h could be obtained from a random number generator or from a counter that 154 // changes after each use". PuTTY (sshdssg.c) starts h off at 1 and increments 155 // it on each loop. wikipedia says "commonly h = 2 is used" so we'll just do that 156 $h = clone $two; 157 while (true) { 158 $g = $h->powMod($e, $p); 159 if (!$g->equals(self::$one)) { 160 break; 161 } 162 $h = $h->add(self::$one); 163 } 164 165 $dsa = new Parameters(); 166 $dsa->p = $p; 167 $dsa->q = $q; 168 $dsa->g = $g; 169 170 return $dsa; 171 } 172 173 /** 174 * Create public / private key pair. 175 * 176 * This method is a bit polymorphic. It can take a DSA/Parameters object, L / N as two distinct parameters or 177 * no parameters (at which point L and N will be generated with this method) 178 * 179 * Returns the private key, from which the publickey can be extracted 180 * 181 * @param int[] ...$args 182 * @return DSA\PrivateKey 183 */ 184 public static function createKey(...$args) 185 { 186 self::initialize_static_variables(); 187 188 $class = new \ReflectionClass(static::class); 189 if ($class->isFinal()) { 190 throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')'); 191 } 192 193 if (!isset(self::$engines['PHP'])) { 194 self::useBestEngine(); 195 } 196 197 if (count($args) == 2 && is_int($args[0]) && is_int($args[1])) { 198 $params = self::createParameters($args[0], $args[1]); 199 } elseif (count($args) == 1 && $args[0] instanceof Parameters) { 200 $params = $args[0]; 201 } elseif (!count($args)) { 202 $params = self::createParameters(); 203 } else { 204 throw new InsufficientSetupException('Valid parameters are either two integers (L and N), a single DSA object or no parameters at all.'); 205 } 206 207 $private = new PrivateKey(); 208 $private->p = $params->p; 209 $private->q = $params->q; 210 $private->g = $params->g; 211 212 $private->x = BigInteger::randomRange(self::$one, $private->q->subtract(self::$one)); 213 $private->y = $private->g->powMod($private->x, $private->p); 214 215 //$public = clone $private; 216 //unset($public->x); 217 218 return $private 219 ->withHash($params->hash->getHash()) 220 ->withSignatureFormat($params->shortFormat); 221 } 222 223 /** 224 * OnLoad Handler 225 * 226 * @return bool 227 */ 228 protected static function onLoad(array $components) 229 { 230 if (!isset(self::$engines['PHP'])) { 231 self::useBestEngine(); 232 } 233 234 if (!isset($components['x']) && !isset($components['y'])) { 235 $new = new Parameters(); 236 } elseif (isset($components['x'])) { 237 $new = new PrivateKey(); 238 $new->x = $components['x']; 239 } else { 240 $new = new PublicKey(); 241 } 242 243 $new->p = $components['p']; 244 $new->q = $components['q']; 245 $new->g = $components['g']; 246 247 if (isset($components['y'])) { 248 $new->y = $components['y']; 249 } 250 251 return $new; 252 } 253 254 /** 255 * Constructor 256 * 257 * PublicKey and PrivateKey objects can only be created from abstract RSA class 258 */ 259 protected function __construct() 260 { 261 $this->sigFormat = self::validatePlugin('Signature', 'ASN1'); 262 $this->shortFormat = 'ASN1'; 263 264 parent::__construct(); 265 } 266 267 /** 268 * Returns the key size 269 * 270 * More specifically, this L (the length of DSA Prime P) and N (the length of DSA Group Order q) 271 * 272 * @return array 273 */ 274 public function getLength() 275 { 276 return ['L' => $this->p->getLength(), 'N' => $this->q->getLength()]; 277 } 278 279 /** 280 * Returns the current engine being used 281 * 282 * @see self::useInternalEngine() 283 * @see self::useBestEngine() 284 * @return string 285 */ 286 public function getEngine() 287 { 288 if (!isset(self::$engines['PHP'])) { 289 self::useBestEngine(); 290 } 291 return self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods()) ? 292 'OpenSSL' : 'PHP'; 293 } 294 295 /** 296 * Returns the parameters 297 * 298 * A public / private key is only returned if the currently loaded "key" contains an x or y 299 * value. 300 * 301 * @see self::getPublicKey() 302 * @return mixed 303 */ 304 public function getParameters() 305 { 306 $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters'); 307 308 $key = $type::saveParameters($this->p, $this->q, $this->g); 309 return DSA::load($key, 'PKCS1') 310 ->withHash($this->hash->getHash()) 311 ->withSignatureFormat($this->shortFormat); 312 } 313 314 /** 315 * Determines the signature padding mode 316 * 317 * Valid values are: ASN1, SSH2, Raw 318 * 319 * @param string $format 320 */ 321 public function withSignatureFormat($format) 322 { 323 $new = clone $this; 324 $new->shortFormat = $format; 325 $new->sigFormat = self::validatePlugin('Signature', $format); 326 return $new; 327 } 328 329 /** 330 * Returns the signature format currently being used 331 * 332 */ 333 public function getSignatureFormat() 334 { 335 return $this->shortFormat; 336 } 337 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body