[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/phpseclib/phpseclib/phpseclib/Crypt/ -> DSA.php (source)

   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  }