[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/ -> Identity.php (source)

   1  <?php
   2  
   3  /**
   4   * Pure-PHP ssh-agent client.
   5   *
   6   * {@internal See http://api.libssh.org/rfc/PROTOCOL.agent}
   7   *
   8   * PHP version 5
   9   *
  10   * @author    Jim Wigginton <terrafrost@php.net>
  11   * @copyright 2009 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\System\SSH\Agent;
  17  
  18  use phpseclib3\Common\Functions\Strings;
  19  use phpseclib3\Crypt\Common\PrivateKey;
  20  use phpseclib3\Crypt\Common\PublicKey;
  21  use phpseclib3\Crypt\DSA;
  22  use phpseclib3\Crypt\EC;
  23  use phpseclib3\Crypt\RSA;
  24  use phpseclib3\Exception\UnsupportedAlgorithmException;
  25  use phpseclib3\System\SSH\Agent;
  26  use phpseclib3\System\SSH\Common\Traits\ReadBytes;
  27  
  28  /**
  29   * Pure-PHP ssh-agent client identity object
  30   *
  31   * Instantiation should only be performed by \phpseclib3\System\SSH\Agent class.
  32   * This could be thought of as implementing an interface that phpseclib3\Crypt\RSA
  33   * implements. ie. maybe a Net_SSH_Auth_PublicKey interface or something.
  34   * The methods in this interface would be getPublicKey and sign since those are the
  35   * methods phpseclib looks for to perform public key authentication.
  36   *
  37   * @author  Jim Wigginton <terrafrost@php.net>
  38   * @internal
  39   */
  40  class Identity implements PrivateKey
  41  {
  42      use ReadBytes;
  43  
  44      // Signature Flags
  45      // See https://tools.ietf.org/html/draft-miller-ssh-agent-00#section-5.3
  46      const SSH_AGENT_RSA2_256 = 2;
  47      const SSH_AGENT_RSA2_512 = 4;
  48  
  49      /**
  50       * Key Object
  51       *
  52       * @var PublicKey
  53       * @see self::getPublicKey()
  54       */
  55      private $key;
  56  
  57      /**
  58       * Key Blob
  59       *
  60       * @var string
  61       * @see self::sign()
  62       */
  63      private $key_blob;
  64  
  65      /**
  66       * Socket Resource
  67       *
  68       * @var resource
  69       * @see self::sign()
  70       */
  71      private $fsock;
  72  
  73      /**
  74       * Signature flags
  75       *
  76       * @var int
  77       * @see self::sign()
  78       * @see self::setHash()
  79       */
  80      private $flags = 0;
  81  
  82      /**
  83       * Curve Aliases
  84       *
  85       * @var array
  86       */
  87      private static $curveAliases = [
  88          'secp256r1' => 'nistp256',
  89          'secp384r1' => 'nistp384',
  90          'secp521r1' => 'nistp521',
  91          'Ed25519' => 'Ed25519'
  92      ];
  93  
  94      /**
  95       * Default Constructor.
  96       *
  97       * @param resource $fsock
  98       */
  99      public function __construct($fsock)
 100      {
 101          $this->fsock = $fsock;
 102      }
 103  
 104      /**
 105       * Set Public Key
 106       *
 107       * Called by \phpseclib3\System\SSH\Agent::requestIdentities()
 108       *
 109       * @param \phpseclib3\Crypt\Common\PublicKey $key
 110       */
 111      public function withPublicKey(PublicKey $key)
 112      {
 113          if ($key instanceof EC) {
 114              if (is_array($key->getCurve()) || !isset(self::$curveAliases[$key->getCurve()])) {
 115                  throw new UnsupportedAlgorithmException('The only supported curves are nistp256, nistp384, nistp512 and Ed25519');
 116              }
 117          }
 118  
 119          $new = clone $this;
 120          $new->key = $key;
 121          return $new;
 122      }
 123  
 124      /**
 125       * Set Public Key
 126       *
 127       * Called by \phpseclib3\System\SSH\Agent::requestIdentities(). The key blob could be extracted from $this->key
 128       * but this saves a small amount of computation.
 129       *
 130       * @param string $key_blob
 131       */
 132      public function withPublicKeyBlob($key_blob)
 133      {
 134          $new = clone $this;
 135          $new->key_blob = $key_blob;
 136          return $new;
 137      }
 138  
 139      /**
 140       * Get Public Key
 141       *
 142       * Wrapper for $this->key->getPublicKey()
 143       *
 144       * @param string $type optional
 145       * @return mixed
 146       */
 147      public function getPublicKey($type = 'PKCS8')
 148      {
 149          return $this->key;
 150      }
 151  
 152      /**
 153       * Sets the hash
 154       *
 155       * @param string $hash
 156       */
 157      public function withHash($hash)
 158      {
 159          $new = clone $this;
 160  
 161          $hash = strtolower($hash);
 162  
 163          if ($this->key instanceof RSA) {
 164              $new->flags = 0;
 165              switch ($hash) {
 166                  case 'sha1':
 167                      break;
 168                  case 'sha256':
 169                      $new->flags = self::SSH_AGENT_RSA2_256;
 170                      break;
 171                  case 'sha512':
 172                      $new->flags = self::SSH_AGENT_RSA2_512;
 173                      break;
 174                  default:
 175                      throw new UnsupportedAlgorithmException('The only supported hashes for RSA are sha1, sha256 and sha512');
 176              }
 177          }
 178          if ($this->key instanceof EC) {
 179              switch ($this->key->getCurve()) {
 180                  case 'secp256r1':
 181                      $expectedHash = 'sha256';
 182                      break;
 183                  case 'secp384r1':
 184                      $expectedHash = 'sha384';
 185                      break;
 186                  //case 'secp521r1':
 187                  //case 'Ed25519':
 188                  default:
 189                      $expectedHash = 'sha512';
 190              }
 191              if ($hash != $expectedHash) {
 192                  throw new UnsupportedAlgorithmException('The only supported hash for ' . self::$curveAliases[$this->key->getCurve()] . ' is ' . $expectedHash);
 193              }
 194          }
 195          if ($this->key instanceof DSA) {
 196              if ($hash != 'sha1') {
 197                  throw new UnsupportedAlgorithmException('The only supported hash for DSA is sha1');
 198              }
 199          }
 200          return $new;
 201      }
 202  
 203      /**
 204       * Sets the padding
 205       *
 206       * Only PKCS1 padding is supported
 207       *
 208       * @param string $padding
 209       */
 210      public function withPadding($padding)
 211      {
 212          if (!$this->key instanceof RSA) {
 213              throw new UnsupportedAlgorithmException('Only RSA keys support padding');
 214          }
 215          if ($padding != RSA::SIGNATURE_PKCS1 && $padding != RSA::SIGNATURE_RELAXED_PKCS1) {
 216              throw new UnsupportedAlgorithmException('ssh-agent can only create PKCS1 signatures');
 217          }
 218          return $this;
 219      }
 220  
 221      /**
 222       * Determines the signature padding mode
 223       *
 224       * Valid values are: ASN1, SSH2, Raw
 225       *
 226       * @param string $format
 227       */
 228      public function withSignatureFormat($format)
 229      {
 230          if ($this->key instanceof RSA) {
 231              throw new UnsupportedAlgorithmException('Only DSA and EC keys support signature format setting');
 232          }
 233          if ($format != 'SSH2') {
 234              throw new UnsupportedAlgorithmException('Only SSH2-formatted signatures are currently supported');
 235          }
 236  
 237          return $this;
 238      }
 239  
 240      /**
 241       * Returns the curve
 242       *
 243       * Returns a string if it's a named curve, an array if not
 244       *
 245       * @return string|array
 246       */
 247      public function getCurve()
 248      {
 249          if (!$this->key instanceof EC) {
 250              throw new UnsupportedAlgorithmException('Only EC keys have curves');
 251          }
 252  
 253          return $this->key->getCurve();
 254      }
 255  
 256      /**
 257       * Create a signature
 258       *
 259       * See "2.6.2 Protocol 2 private key signature request"
 260       *
 261       * @param string $message
 262       * @return string
 263       * @throws \RuntimeException on connection errors
 264       * @throws \phpseclib3\Exception\UnsupportedAlgorithmException if the algorithm is unsupported
 265       */
 266      public function sign($message)
 267      {
 268          // the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE
 269          $packet = Strings::packSSH2(
 270              'CssN',
 271              Agent::SSH_AGENTC_SIGN_REQUEST,
 272              $this->key_blob,
 273              $message,
 274              $this->flags
 275          );
 276          $packet = Strings::packSSH2('s', $packet);
 277          if (strlen($packet) != fputs($this->fsock, $packet)) {
 278              throw new \RuntimeException('Connection closed during signing');
 279          }
 280  
 281          $length = current(unpack('N', $this->readBytes(4)));
 282          $packet = $this->readBytes($length);
 283  
 284          list($type, $signature_blob) = Strings::unpackSSH2('Cs', $packet);
 285          if ($type != Agent::SSH_AGENT_SIGN_RESPONSE) {
 286              throw new \RuntimeException('Unable to retrieve signature');
 287          }
 288  
 289          if (!$this->key instanceof RSA) {
 290              return $signature_blob;
 291          }
 292  
 293          list($type, $signature_blob) = Strings::unpackSSH2('ss', $signature_blob);
 294  
 295          return $signature_blob;
 296      }
 297  
 298      /**
 299       * Returns the private key
 300       *
 301       * @param string $type
 302       * @param array $options optional
 303       * @return string
 304       */
 305      public function toString($type, array $options = [])
 306      {
 307          throw new \RuntimeException('ssh-agent does not provide a mechanism to get the private key');
 308      }
 309  
 310      /**
 311       * Sets the password
 312       *
 313       * @param string|bool $password
 314       * @return never
 315       */
 316      public function withPassword($password = false)
 317      {
 318          throw new \RuntimeException('ssh-agent does not provide a mechanism to get the private key');
 319      }
 320  }