[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/ -> OpenSSH.php (source)

   1  <?php
   2  
   3  /**
   4   * OpenSSH Formatted EC Key Handler
   5   *
   6   * PHP version 5
   7   *
   8   * Place in $HOME/.ssh/authorized_keys
   9   *
  10   * @author    Jim Wigginton <terrafrost@php.net>
  11   * @copyright 2015 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\EC\Formats\Keys;
  17  
  18  use phpseclib3\Common\Functions\Strings;
  19  use phpseclib3\Crypt\Common\Formats\Keys\OpenSSH as Progenitor;
  20  use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
  21  use phpseclib3\Crypt\EC\Curves\Ed25519;
  22  use phpseclib3\Exception\UnsupportedCurveException;
  23  use phpseclib3\Math\BigInteger;
  24  
  25  /**
  26   * OpenSSH Formatted EC Key Handler
  27   *
  28   * @author  Jim Wigginton <terrafrost@php.net>
  29   */
  30  abstract class OpenSSH extends Progenitor
  31  {
  32      use Common;
  33  
  34      /**
  35       * Supported Key Types
  36       *
  37       * @var array
  38       */
  39      protected static $types = [
  40          'ecdsa-sha2-nistp256',
  41          'ecdsa-sha2-nistp384',
  42          'ecdsa-sha2-nistp521',
  43          'ssh-ed25519'
  44      ];
  45  
  46      /**
  47       * Break a public or private key down into its constituent components
  48       *
  49       * @param string $key
  50       * @param string $password optional
  51       * @return array
  52       */
  53      public static function load($key, $password = '')
  54      {
  55          $parsed = parent::load($key, $password);
  56  
  57          if (isset($parsed['paddedKey'])) {
  58              $paddedKey = $parsed['paddedKey'];
  59              list($type) = Strings::unpackSSH2('s', $paddedKey);
  60              if ($type != $parsed['type']) {
  61                  throw new \RuntimeException("The public and private keys are not of the same type ($type vs $parsed[type])");
  62              }
  63              if ($type == 'ssh-ed25519') {
  64                  list(, $key, $comment) = Strings::unpackSSH2('sss', $paddedKey);
  65                  $key = libsodium::load($key);
  66                  $key['comment'] = $comment;
  67                  return $key;
  68              }
  69              list($curveName, $publicKey, $privateKey, $comment) = Strings::unpackSSH2('ssis', $paddedKey);
  70              $curve = self::loadCurveByParam(['namedCurve' => $curveName]);
  71              $curve->rangeCheck($privateKey);
  72              return [
  73                  'curve' => $curve,
  74                  'dA' => $privateKey,
  75                  'QA' => self::extractPoint("\0$publicKey", $curve),
  76                  'comment' => $comment
  77              ];
  78          }
  79  
  80          if ($parsed['type'] == 'ssh-ed25519') {
  81              if (Strings::shift($parsed['publicKey'], 4) != "\0\0\0\x20") {
  82                  throw new \RuntimeException('Length of ssh-ed25519 key should be 32');
  83              }
  84  
  85              $curve = new Ed25519();
  86              $qa = self::extractPoint($parsed['publicKey'], $curve);
  87          } else {
  88              list($curveName, $publicKey) = Strings::unpackSSH2('ss', $parsed['publicKey']);
  89              $curveName = '\phpseclib3\Crypt\EC\Curves\\' . $curveName;
  90              $curve = new $curveName();
  91  
  92              $qa = self::extractPoint("\0" . $publicKey, $curve);
  93          }
  94  
  95          return [
  96              'curve' => $curve,
  97              'QA' => $qa,
  98              'comment' => $parsed['comment']
  99          ];
 100      }
 101  
 102      /**
 103       * Returns the alias that corresponds to a curve
 104       *
 105       * @return string
 106       */
 107      private static function getAlias(BaseCurve $curve)
 108      {
 109          self::initialize_static_variables();
 110  
 111          $reflect = new \ReflectionClass($curve);
 112          $name = $reflect->getShortName();
 113  
 114          $oid = self::$curveOIDs[$name];
 115          $aliases = array_filter(self::$curveOIDs, function ($v) use ($oid) {
 116              return $v == $oid;
 117          });
 118          $aliases = array_keys($aliases);
 119  
 120          for ($i = 0; $i < count($aliases); $i++) {
 121              if (in_array('ecdsa-sha2-' . $aliases[$i], self::$types)) {
 122                  $alias = $aliases[$i];
 123                  break;
 124              }
 125          }
 126  
 127          if (!isset($alias)) {
 128              throw new UnsupportedCurveException($name . ' is not a curve that the OpenSSH plugin supports');
 129          }
 130  
 131          return $alias;
 132      }
 133  
 134      /**
 135       * Convert an EC public key to the appropriate format
 136       *
 137       * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
 138       * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
 139       * @param array $options optional
 140       * @return string
 141       */
 142      public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = [])
 143      {
 144          $comment = isset($options['comment']) ? $options['comment'] : self::$comment;
 145  
 146          if ($curve instanceof Ed25519) {
 147              $key = Strings::packSSH2('ss', 'ssh-ed25519', $curve->encodePoint($publicKey));
 148  
 149              if (isset($options['binary']) ? $options['binary'] : self::$binary) {
 150                  return $key;
 151              }
 152  
 153              $key = 'ssh-ed25519 ' . base64_encode($key) . ' ' . $comment;
 154              return $key;
 155          }
 156  
 157          $alias = self::getAlias($curve);
 158  
 159          $points = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
 160          $key = Strings::packSSH2('sss', 'ecdsa-sha2-' . $alias, $alias, $points);
 161  
 162          if (isset($options['binary']) ? $options['binary'] : self::$binary) {
 163              return $key;
 164          }
 165  
 166          $key = 'ecdsa-sha2-' . $alias . ' ' . base64_encode($key) . ' ' . $comment;
 167  
 168          return $key;
 169      }
 170  
 171      /**
 172       * Convert a private key to the appropriate format.
 173       *
 174       * @param \phpseclib3\Math\BigInteger $privateKey
 175       * @param \phpseclib3\Crypt\EC\Curves\Ed25519 $curve
 176       * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
 177       * @param string $secret optional
 178       * @param string $password optional
 179       * @param array $options optional
 180       * @return string
 181       */
 182      public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = '', array $options = [])
 183      {
 184          if ($curve instanceof Ed25519) {
 185              if (!isset($secret)) {
 186                  throw new \RuntimeException('Private Key does not have a secret set');
 187              }
 188              if (strlen($secret) != 32) {
 189                  throw new \RuntimeException('Private Key secret is not of the correct length');
 190              }
 191  
 192              $pubKey = $curve->encodePoint($publicKey);
 193  
 194              $publicKey = Strings::packSSH2('ss', 'ssh-ed25519', $pubKey);
 195              $privateKey = Strings::packSSH2('sss', 'ssh-ed25519', $pubKey, $secret . $pubKey);
 196  
 197              return self::wrapPrivateKey($publicKey, $privateKey, $password, $options);
 198          }
 199  
 200          $alias = self::getAlias($curve);
 201  
 202          $points = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
 203          $publicKey = self::savePublicKey($curve, $publicKey, ['binary' => true]);
 204  
 205          $privateKey = Strings::packSSH2('sssi', 'ecdsa-sha2-' . $alias, $alias, $points, $privateKey);
 206  
 207          return self::wrapPrivateKey($publicKey, $privateKey, $password, $options);
 208      }
 209  }