Subversion Repositories oidplus

Rev

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

  1. <?php
  2.  
  3. /**
  4.  * XML Formatted DSA Key Handler
  5.  *
  6.  * While XKMS defines a private key format for RSA it does not do so for DSA. Quoting that standard:
  7.  *
  8.  * "[XKMS] does not specify private key parameters for the DSA signature algorithm since the algorithm only
  9.  *  supports signature modes and so the application of server generated keys and key recovery is of limited
  10.  *  value"
  11.  *
  12.  * PHP version 5
  13.  *
  14.  * @category  Crypt
  15.  * @package   DSA
  16.  * @author    Jim Wigginton <terrafrost@php.net>
  17.  * @copyright 2015 Jim Wigginton
  18.  * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  19.  * @link      http://phpseclib.sourceforge.net
  20.  */
  21.  
  22. namespace phpseclib3\Crypt\DSA\Formats\Keys;
  23.  
  24. use ParagonIE\ConstantTime\Base64;
  25. use phpseclib3\Common\Functions\Strings;
  26. use phpseclib3\Exception\BadConfigurationException;
  27. use phpseclib3\Math\BigInteger;
  28.  
  29. /**
  30.  * XML Formatted DSA Key Handler
  31.  *
  32.  * @package DSA
  33.  * @author  Jim Wigginton <terrafrost@php.net>
  34.  * @access  public
  35.  */
  36. abstract class XML
  37. {
  38.     /**
  39.      * Break a public or private key down into its constituent components
  40.      *
  41.      * @access public
  42.      * @param string $key
  43.      * @param string $password optional
  44.      * @return array
  45.      */
  46.     public static function load($key, $password = '')
  47.     {
  48.         if (!Strings::is_stringable($key)) {
  49.             throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
  50.         }
  51.  
  52.         if (!class_exists('DOMDocument')) {
  53.             throw new BadConfigurationException('The dom extension is not setup correctly on this system');
  54.         }
  55.  
  56.         $use_errors = libxml_use_internal_errors(true);
  57.  
  58.         $dom = new \DOMDocument();
  59.         if (substr($key, 0, 5) != '<?xml') {
  60.             $key = '<xml>' . $key . '</xml>';
  61.         }
  62.         if (!$dom->loadXML($key)) {
  63.             libxml_use_internal_errors($use_errors);
  64.             throw new \UnexpectedValueException('Key does not appear to contain XML');
  65.         }
  66.         $xpath = new \DOMXPath($dom);
  67.         $keys = ['p', 'q', 'g', 'y', 'j', 'seed', 'pgencounter'];
  68.         foreach ($keys as $key) {
  69.             // $dom->getElementsByTagName($key) is case-sensitive
  70.             $temp = $xpath->query("//*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$key']");
  71.             if (!$temp->length) {
  72.                 continue;
  73.             }
  74.             $value = new BigInteger(Base64::decode($temp->item(0)->nodeValue), 256);
  75.             switch ($key) {
  76.                 case 'p': // a prime modulus meeting the [DSS] requirements
  77.                     // Parameters P, Q, and G can be public and common to a group of users. They might be known
  78.                     // from application context. As such, they are optional but P and Q must either both appear
  79.                     // or both be absent
  80.                     $components['p'] = $value;
  81.                     break;
  82.                 case 'q': // an integer in the range 2**159 < Q < 2**160 which is a prime divisor of P-1
  83.                     $components['q'] = $value;
  84.                     break;
  85.                 case 'g': // an integer with certain properties with respect to P and Q
  86.                     $components['g'] = $value;
  87.                     break;
  88.                 case 'y': // G**X mod P (where X is part of the private key and not made public)
  89.                     $components['y'] = $value;
  90.                     // the remaining options do not do anything
  91.                 case 'j': // (P - 1) / Q
  92.                     // Parameter J is available for inclusion solely for efficiency as it is calculatable from
  93.                     // P and Q
  94.                 case 'seed': // a DSA prime generation seed
  95.                     // Parameters seed and pgenCounter are used in the DSA prime number generation algorithm
  96.                     // specified in [DSS]. As such, they are optional but must either both be present or both
  97.                     // be absent
  98.                 case 'pgencounter': // a DSA prime generation counter
  99.             }
  100.         }
  101.  
  102.         libxml_use_internal_errors($use_errors);
  103.  
  104.         if (!isset($components['y'])) {
  105.             throw new \UnexpectedValueException('Key is missing y component');
  106.         }
  107.  
  108.         switch (true) {
  109.             case !isset($components['p']):
  110.             case !isset($components['q']):
  111.             case !isset($components['g']):
  112.                 return ['y' => $components['y']];
  113.         }
  114.  
  115.         return $components;
  116.     }
  117.  
  118.     /**
  119.      * Convert a public key to the appropriate format
  120.      *
  121.      * See https://www.w3.org/TR/xmldsig-core/#sec-DSAKeyValue
  122.      *
  123.      * @access public
  124.      * @param \phpseclib3\Math\BigInteger $p
  125.      * @param \phpseclib3\Math\BigInteger $q
  126.      * @param \phpseclib3\Math\BigInteger $g
  127.      * @param \phpseclib3\Math\BigInteger $y
  128.      * @return string
  129.      */
  130.     public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y)
  131.     {
  132.         return "<DSAKeyValue>\r\n" .
  133.                '  <P>' . Base64::encode($p->toBytes()) . "</P>\r\n" .
  134.                '  <Q>' . Base64::encode($q->toBytes()) . "</Q>\r\n" .
  135.                '  <G>' . Base64::encode($g->toBytes()) . "</G>\r\n" .
  136.                '  <Y>' . Base64::encode($y->toBytes()) . "</Y>\r\n" .
  137.                '</DSAKeyValue>';
  138.     }
  139. }
  140.