Subversion Repositories oidplus

Rev

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