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.  * Base Class for all asymmetric key ciphers
  5.  *
  6.  * PHP version 5
  7.  *
  8.  * @category  Crypt
  9.  * @package   AsymmetricKey
  10.  * @author    Jim Wigginton <terrafrost@php.net>
  11.  * @copyright 2016 Jim Wigginton
  12.  * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  13.  * @link      http://phpseclib.sourceforge.net
  14.  */
  15.  
  16. namespace phpseclib3\Crypt\Common;
  17.  
  18. use phpseclib3\Crypt\DSA;
  19. use phpseclib3\Crypt\Hash;
  20. use phpseclib3\Crypt\RSA;
  21. use phpseclib3\Exception\NoKeyLoadedException;
  22. use phpseclib3\Exception\UnsupportedFormatException;
  23. use phpseclib3\Math\BigInteger;
  24.  
  25. /**
  26.  * Base Class for all asymmetric cipher classes
  27.  *
  28.  * @package AsymmetricKey
  29.  * @author  Jim Wigginton <terrafrost@php.net>
  30.  */
  31. abstract class AsymmetricKey
  32. {
  33.     /**
  34.      * Precomputed Zero
  35.      *
  36.      * @var \phpseclib3\Math\BigInteger
  37.      * @access private
  38.      */
  39.     protected static $zero;
  40.  
  41.     /**
  42.      * Precomputed One
  43.      *
  44.      * @var \phpseclib3\Math\BigInteger
  45.      * @access private
  46.      */
  47.     protected static $one;
  48.  
  49.     /**
  50.      * Format of the loaded key
  51.      *
  52.      * @var string
  53.      * @access private
  54.      */
  55.     protected $format;
  56.  
  57.     /**
  58.      * Hash function
  59.      *
  60.      * @var \phpseclib3\Crypt\Hash
  61.      * @access private
  62.      */
  63.     protected $hash;
  64.  
  65.     /**
  66.      * HMAC function
  67.      *
  68.      * @var \phpseclib3\Crypt\Hash
  69.      * @access private
  70.      */
  71.     private $hmac;
  72.  
  73.     /**
  74.      * Supported plugins (lower case)
  75.      *
  76.      * @see self::initialize_static_variables()
  77.      * @var array
  78.      * @access private
  79.      */
  80.     private static $plugins = [];
  81.  
  82.     /**
  83.      * Invisible plugins
  84.      *
  85.      * @see self::initialize_static_variables()
  86.      * @var array
  87.      * @access private
  88.      */
  89.     private static $invisiblePlugins = [];
  90.  
  91.     /**
  92.      * Supported signature formats (lower case)
  93.      *
  94.      * @see self::initialize_static_variables()
  95.      * @var array
  96.      * @access private
  97.      */
  98.     private static $signatureFormats = [];
  99.  
  100.     /**
  101.      * Supported signature formats (original case)
  102.      *
  103.      * @see self::initialize_static_variables()
  104.      * @var array
  105.      * @access private
  106.      */
  107.     private static $signatureFileFormats = [];
  108.  
  109.     /**
  110.      * Available Engines
  111.      *
  112.      * @var boolean[]
  113.      * @access private
  114.      */
  115.     protected static $engines = [];
  116.  
  117.     /**
  118.      * Key Comment
  119.      *
  120.      * @var null|string
  121.      * @access private
  122.      */
  123.     private $comment;
  124.  
  125.     /**
  126.      * @param string $type
  127.      * @return string
  128.      */
  129.     abstract public function toString($type, array $options = []);
  130.  
  131.     /**
  132.      * The constructor
  133.      */
  134.     protected function __construct()
  135.     {
  136.         self::initialize_static_variables();
  137.  
  138.         $this->hash = new Hash('sha256');
  139.         $this->hmac = new Hash('sha256');
  140.     }
  141.  
  142.     /**
  143.      * Initialize static variables
  144.      */
  145.     protected static function initialize_static_variables()
  146.     {
  147.         if (!isset(self::$zero)) {
  148.             self::$zero = new BigInteger(0);
  149.             self::$one = new BigInteger(1);
  150.         }
  151.  
  152.         self::loadPlugins('Keys');
  153.         if (static::ALGORITHM != 'RSA' && static::ALGORITHM != 'DH') {
  154.             self::loadPlugins('Signature');
  155.         }
  156.     }
  157.  
  158.     /**
  159.      * Load the key
  160.      *
  161.      * @param string $key
  162.      * @param string $password optional
  163.      * @return AsymmetricKey
  164.      */
  165.     public static function load($key, $password = false)
  166.     {
  167.         self::initialize_static_variables();
  168.  
  169.         $components = false;
  170.         foreach (self::$plugins[static::ALGORITHM]['Keys'] as $format) {
  171.             if (isset(self::$invisiblePlugins[static::ALGORITHM]) && in_array($format, self::$invisiblePlugins[static::ALGORITHM])) {
  172.                 continue;
  173.             }
  174.             try {
  175.                 $components = $format::load($key, $password);
  176.             } catch (\Exception $e) {
  177.                 $components = false;
  178.             }
  179.             if ($components !== false) {
  180.                 break;
  181.             }
  182.         }
  183.  
  184.         if ($components === false) {
  185.             throw new NoKeyLoadedException('Unable to read key');
  186.         }
  187.  
  188.         $components['format'] = $format;
  189.         $comment = isset($components['comment']) ? $components['comment'] : null;
  190.         $new = static::onLoad($components);
  191.         $new->format = $format;
  192.         $new->comment = $comment;
  193.         return $new instanceof PrivateKey ?
  194.             $new->withPassword($password) :
  195.             $new;
  196.     }
  197.  
  198.     /**
  199.      * Loads a private key
  200.      *
  201.      * @return PrivateKey
  202.      * @access public
  203.      * @param string|array $key
  204.      * @param string $password optional
  205.      */
  206.     public static function loadPrivateKey($key, $password = '')
  207.     {
  208.         $key = self::load($key, $password);
  209.         if (!$key instanceof PrivateKey) {
  210.             throw new NoKeyLoadedException('The key that was loaded was not a private key');
  211.         }
  212.         return $key;
  213.     }
  214.  
  215.     /**
  216.      * Loads a public key
  217.      *
  218.      * @return PublicKey
  219.      * @access public
  220.      * @param string|array $key
  221.      */
  222.     public static function loadPublicKey($key)
  223.     {
  224.         $key = self::load($key);
  225.         if (!$key instanceof PublicKey) {
  226.             throw new NoKeyLoadedException('The key that was loaded was not a public key');
  227.         }
  228.         return $key;
  229.     }
  230.  
  231.     /**
  232.      * Loads parameters
  233.      *
  234.      * @return AsymmetricKey
  235.      * @access public
  236.      * @param string|array $key
  237.      */
  238.     public static function loadParameters($key)
  239.     {
  240.         $key = self::load($key);
  241.         if (!$key instanceof PrivateKey && !$key instanceof PublicKey) {
  242.             throw new NoKeyLoadedException('The key that was loaded was not a parameter');
  243.         }
  244.         return $key;
  245.     }
  246.  
  247.     /**
  248.      * Load the key, assuming a specific format
  249.      *
  250.      * @param string $type
  251.      * @param string $key
  252.      * @param string $password optional
  253.      * @return static
  254.      */
  255.     public static function loadFormat($type, $key, $password = false)
  256.     {
  257.         self::initialize_static_variables();
  258.  
  259.         $components = false;
  260.         $format = strtolower($type);
  261.         if (isset(self::$plugins[static::ALGORITHM]['Keys'][$format])) {
  262.             $format = self::$plugins[static::ALGORITHM]['Keys'][$format];
  263.             $components = $format::load($key, $password);
  264.         }
  265.  
  266.         if ($components === false) {
  267.             throw new NoKeyLoadedException('Unable to read key');
  268.         }
  269.  
  270.         $components['format'] = $format;
  271.  
  272.         $new = static::onLoad($components);
  273.         $new->format = $format;
  274.         return $new instanceof PrivateKey ?
  275.             $new->withPassword($password) :
  276.             $new;
  277.     }
  278.  
  279.     /**
  280.      * Loads a private key
  281.      *
  282.      * @return PrivateKey
  283.      * @access public
  284.      * @param string $type
  285.      * @param string $key
  286.      * @param string $password optional
  287.      */
  288.     public static function loadPrivateKeyFormat($type, $key, $password = false)
  289.     {
  290.         $key = self::loadFormat($type, $key, $password);
  291.         if (!$key instanceof PrivateKey) {
  292.             throw new NoKeyLoadedException('The key that was loaded was not a private key');
  293.         }
  294.         return $key;
  295.     }
  296.  
  297.     /**
  298.      * Loads a public key
  299.      *
  300.      * @return PublicKey
  301.      * @access public
  302.      * @param string $type
  303.      * @param string $key
  304.      */
  305.     public static function loadPublicKeyFormat($type, $key)
  306.     {
  307.         $key = self::loadFormat($type, $key);
  308.         if (!$key instanceof PublicKey) {
  309.             throw new NoKeyLoadedException('The key that was loaded was not a public key');
  310.         }
  311.         return $key;
  312.     }
  313.  
  314.     /**
  315.      * Loads parameters
  316.      *
  317.      * @return AsymmetricKey
  318.      * @access public
  319.      * @param string $type
  320.      * @param string|array $key
  321.      */
  322.     public static function loadParametersFormat($type, $key)
  323.     {
  324.         $key = self::loadFormat($type, $key);
  325.         if (!$key instanceof PrivateKey && !$key instanceof PublicKey) {
  326.             throw new NoKeyLoadedException('The key that was loaded was not a parameter');
  327.         }
  328.         return $key;
  329.     }
  330.  
  331.     /**
  332.      * Validate Plugin
  333.      *
  334.      * @access private
  335.      * @param string $format
  336.      * @param string $type
  337.      * @param string $method optional
  338.      * @return mixed
  339.      */
  340.     protected static function validatePlugin($format, $type, $method = null)
  341.     {
  342.         $type = strtolower($type);
  343.         if (!isset(self::$plugins[static::ALGORITHM][$format][$type])) {
  344.             throw new UnsupportedFormatException("$type is not a supported format");
  345.         }
  346.         $type = self::$plugins[static::ALGORITHM][$format][$type];
  347.         if (isset($method) && !method_exists($type, $method)) {
  348.             throw new UnsupportedFormatException("$type does not implement $method");
  349.         }
  350.  
  351.         return $type;
  352.     }
  353.  
  354.     /**
  355.      * Load Plugins
  356.      *
  357.      * @access private
  358.      * @param string $format
  359.      */
  360.     private static function loadPlugins($format)
  361.     {
  362.         if (!isset(self::$plugins[static::ALGORITHM][$format])) {
  363.             self::$plugins[static::ALGORITHM][$format] = [];
  364.             foreach (new \DirectoryIterator(__DIR__ . '/../' . static::ALGORITHM . '/Formats/' . $format . '/') as $file) {
  365.                 if ($file->getExtension() != 'php') {
  366.                     continue;
  367.                 }
  368.                 $name = $file->getBasename('.php');
  369.                 if ($name[0] == '.') {
  370.                     continue;
  371.                 }
  372.                 $type = 'phpseclib3\Crypt\\' . static::ALGORITHM . '\\Formats\\' . $format . '\\' . $name;
  373.                 $reflect = new \ReflectionClass($type);
  374.                 if ($reflect->isTrait()) {
  375.                     continue;
  376.                 }
  377.                 self::$plugins[static::ALGORITHM][$format][strtolower($name)] = $type;
  378.                 if ($reflect->hasConstant('IS_INVISIBLE')) {
  379.                     self::$invisiblePlugins[static::ALGORITHM][] = $type;
  380.                 }
  381.             }
  382.         }
  383.     }
  384.  
  385.     /**
  386.      * Returns a list of supported formats.
  387.      *
  388.      * @access public
  389.      * @return array
  390.      */
  391.     public static function getSupportedKeyFormats()
  392.     {
  393.         self::initialize_static_variables();
  394.  
  395.         return self::$plugins[static::ALGORITHM]['Keys'];
  396.     }
  397.  
  398.     /**
  399.      * Add a fileformat plugin
  400.      *
  401.      * The plugin needs to either already be loaded or be auto-loadable.
  402.      * Loading a plugin whose shortname overwrite an existing shortname will overwrite the old plugin.
  403.      *
  404.      * @see self::load()
  405.      * @param string $fullname
  406.      * @access public
  407.      * @return bool
  408.      */
  409.     public static function addFileFormat($fullname)
  410.     {
  411.         self::initialize_static_variables();
  412.  
  413.         if (class_exists($fullname)) {
  414.             $meta = new \ReflectionClass($fullname);
  415.             $shortname = $meta->getShortName();
  416.             self::$plugins[static::ALGORITHM]['Keys'][strtolower($shortname)] = $fullname;
  417.             if ($meta->hasConstant('IS_INVISIBLE')) {
  418.                 self::$invisiblePlugins[static::ALGORITHM] = strtolower($name);
  419.             }
  420.         }
  421.     }
  422.  
  423.     /**
  424.      * Returns the format of the loaded key.
  425.      *
  426.      * If the key that was loaded wasn't in a valid or if the key was auto-generated
  427.      * with RSA::createKey() then this will throw an exception.
  428.      *
  429.      * @see self::load()
  430.      * @access public
  431.      * @return mixed
  432.      */
  433.     public function getLoadedFormat()
  434.     {
  435.         if (empty($this->format)) {
  436.             throw new NoKeyLoadedException('This key was created with createKey - it was not loaded with load. Therefore there is no "loaded format"');
  437.         }
  438.  
  439.         $meta = new \ReflectionClass($this->format);
  440.         return $meta->getShortName();
  441.     }
  442.  
  443.     /**
  444.      * Returns the key's comment
  445.      *
  446.      * Not all key formats support comments. If you want to set a comment use toString()
  447.      *
  448.      * @access public
  449.      * @return null|string
  450.      */
  451.     public function getComment()
  452.     {
  453.         return $this->comment;
  454.     }
  455.  
  456.     /**
  457.      * Tests engine validity
  458.      *
  459.      * @access public
  460.      */
  461.     public static function useBestEngine()
  462.     {
  463.         static::$engines = [
  464.             'PHP' => true,
  465.             'OpenSSL' => extension_loaded('openssl'),
  466.             // this test can be satisfied by either of the following:
  467.             // http://php.net/manual/en/book.sodium.php
  468.             // https://github.com/paragonie/sodium_compat
  469.             'libsodium' => function_exists('sodium_crypto_sign_keypair')
  470.         ];
  471.  
  472.         return static::$engines;
  473.     }
  474.  
  475.     /**
  476.      * Flag to use internal engine only (useful for unit testing)
  477.      *
  478.      * @access public
  479.      */
  480.     public static function useInternalEngine()
  481.     {
  482.         static::$engines = [
  483.             'PHP' => true,
  484.             'OpenSSL' => false,
  485.             'libsodium' => false
  486.         ];
  487.     }
  488.  
  489.     /**
  490.      * __toString() magic method
  491.      *
  492.      * @return string
  493.      */
  494.     public function __toString()
  495.     {
  496.         return $this->toString('PKCS8');
  497.     }
  498.  
  499.     /**
  500.      * Determines which hashing function should be used
  501.      *
  502.      * @access public
  503.      * @param string $hash
  504.      */
  505.     public function withHash($hash)
  506.     {
  507.         $new = clone $this;
  508.  
  509.         $new->hash = new Hash($hash);
  510.         $new->hmac = new Hash($hash);
  511.  
  512.         return $new;
  513.     }
  514.  
  515.     /**
  516.      * Returns the hash algorithm currently being used
  517.      *
  518.      * @access public
  519.      */
  520.     public function getHash()
  521.     {
  522.         return clone $this->hash;
  523.     }
  524.  
  525.     /**
  526.      * Compute the pseudorandom k for signature generation,
  527.      * using the process specified for deterministic DSA.
  528.      *
  529.      * @access public
  530.      * @param string $h1
  531.      * @return string
  532.      */
  533.     protected function computek($h1)
  534.     {
  535.         $v = str_repeat("\1", strlen($h1));
  536.  
  537.         $k = str_repeat("\0", strlen($h1));
  538.  
  539.         $x = $this->int2octets($this->x);
  540.         $h1 = $this->bits2octets($h1);
  541.  
  542.         $this->hmac->setKey($k);
  543.         $k = $this->hmac->hash($v . "\0" . $x . $h1);
  544.         $this->hmac->setKey($k);
  545.         $v = $this->hmac->hash($v);
  546.         $k = $this->hmac->hash($v . "\1" . $x . $h1);
  547.         $this->hmac->setKey($k);
  548.         $v = $this->hmac->hash($v);
  549.  
  550.         $qlen = $this->q->getLengthInBytes();
  551.  
  552.         while (true) {
  553.             $t = '';
  554.             while (strlen($t) < $qlen) {
  555.                 $v = $this->hmac->hash($v);
  556.                 $t = $t . $v;
  557.             }
  558.             $k = $this->bits2int($t);
  559.  
  560.             if (!$k->equals(self::$zero) && $k->compare($this->q) < 0) {
  561.                 break;
  562.             }
  563.             $k = $this->hmac->hash($v . "\0");
  564.             $this->hmac->setKey($k);
  565.             $v = $this->hmac->hash($v);
  566.         }
  567.  
  568.         return $k;
  569.     }
  570.  
  571.     /**
  572.      * Integer to Octet String
  573.      *
  574.      * @access private
  575.      * @param \phpseclib3\Math\BigInteger $v
  576.      * @return string
  577.      */
  578.     private function int2octets($v)
  579.     {
  580.         $out = $v->toBytes();
  581.         $rolen = $this->q->getLengthInBytes();
  582.         if (strlen($out) < $rolen) {
  583.             return str_pad($out, $rolen, "\0", STR_PAD_LEFT);
  584.         } elseif (strlen($out) > $rolen) {
  585.             return substr($out, -$rolen);
  586.         } else {
  587.             return $out;
  588.         }
  589.     }
  590.  
  591.     /**
  592.      * Bit String to Integer
  593.      *
  594.      * @access private
  595.      * @param string $in
  596.      * @return \phpseclib3\Math\BigInteger
  597.      */
  598.     protected function bits2int($in)
  599.     {
  600.         $v = new BigInteger($in, 256);
  601.         $vlen = strlen($in) << 3;
  602.         $qlen = $this->q->getLength();
  603.         if ($vlen > $qlen) {
  604.             return $v->bitwise_rightShift($vlen - $qlen);
  605.         }
  606.         return $v;
  607.     }
  608.  
  609.     /**
  610.      * Bit String to Octet String
  611.      *
  612.      * @access private
  613.      * @param string $in
  614.      * @return string
  615.      */
  616.     private function bits2octets($in)
  617.     {
  618.         $z1 = $this->bits2int($in);
  619.         $z2 = $z1->subtract($this->q);
  620.         return $z2->compare(self::$zero) < 0 ?
  621.             $this->int2octets($z1) :
  622.             $this->int2octets($z2);
  623.     }
  624. }
  625.