Subversion Repositories oidplus

Rev

Rev 846 | Rev 1042 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

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