[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

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

   1  <?php
   2  
   3  /**
   4   * OpenSSH Key Handler
   5   *
   6   * PHP version 5
   7   *
   8   * Place in $HOME/.ssh/authorized_keys
   9   *
  10   * @category  Crypt
  11   * @package   Common
  12   * @author    Jim Wigginton <[email protected]>
  13   * @copyright 2015 Jim Wigginton
  14   * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  15   * @link      http://phpseclib.sourceforge.net
  16   */
  17  
  18  namespace phpseclib3\Crypt\Common\Formats\Keys;
  19  
  20  use ParagonIE\ConstantTime\Base64;
  21  use phpseclib3\Common\Functions\Strings;
  22  use phpseclib3\Crypt\Random;
  23  use phpseclib3\Exception\UnsupportedFormatException;
  24  
  25  /**
  26   * OpenSSH Formatted RSA Key Handler
  27   *
  28   * @package Common
  29   * @author  Jim Wigginton <[email protected]>
  30   * @access  public
  31   */
  32  abstract class OpenSSH
  33  {
  34      /**
  35       * Default comment
  36       *
  37       * @var string
  38       * @access private
  39       */
  40      protected static $comment = 'phpseclib-generated-key';
  41  
  42      /**
  43       * Binary key flag
  44       *
  45       * @var bool
  46       * @access private
  47       */
  48      protected static $binary = false;
  49  
  50      /**
  51       * Sets the default comment
  52       *
  53       * @access public
  54       * @param string $comment
  55       */
  56      public static function setComment($comment)
  57      {
  58          self::$comment = str_replace(["\r", "\n"], '', $comment);
  59      }
  60  
  61      /**
  62       * Break a public or private key down into its constituent components
  63       *
  64       * $type can be either ssh-dss or ssh-rsa
  65       *
  66       * @access public
  67       * @param string $key
  68       * @param string $password
  69       * @return array
  70       */
  71      public static function load($key, $password = '')
  72      {
  73          if (!Strings::is_stringable($key)) {
  74              throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
  75          }
  76  
  77          // key format is described here:
  78          // https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?annotate=HEAD
  79  
  80          if (strpos($key, 'BEGIN OPENSSH PRIVATE KEY') !== false) {
  81              $key = preg_replace('#(?:^-.*?-[\r\n]*$)|\s#ms', '', $key);
  82              $key = Base64::decode($key);
  83              $magic = Strings::shift($key, 15);
  84              if ($magic != "openssh-key-v1\0") {
  85                  throw new \RuntimeException('Expected openssh-key-v1');
  86              }
  87              list($ciphername, $kdfname, $kdfoptions, $numKeys) = Strings::unpackSSH2('sssN', $key);
  88              if ($numKeys != 1) {
  89                  // if we wanted to support multiple keys we could update PublicKeyLoader to preview what the # of keys
  90                  // would be; it'd then call Common\Keys\OpenSSH.php::load() and get the paddedKey. it'd then pass
  91                  // that to the appropriate key loading parser $numKey times or something
  92                  throw new \RuntimeException('Although the OpenSSH private key format supports multiple keys phpseclib does not');
  93              }
  94              if (strlen($kdfoptions) || $kdfname != 'none' || $ciphername != 'none') {
  95                  /*
  96                    OpenSSH private keys use a customized version of bcrypt. specifically, instead of encrypting
  97                    OrpheanBeholderScryDoubt 64 times OpenSSH's bcrypt variant encrypts
  98                    OxychromaticBlowfishSwatDynamite 64 times. so we can't use crypt().
  99  
 100                    bcrypt is basically Blowfish with an altered key expansion. whereas Blowfish just runs the
 101                    key through the key expansion bcrypt interleaves the key expansion with the salt and
 102                    password. this renders openssl / mcrypt unusuable. this forces us to use a pure-PHP implementation
 103                    of bcrypt. the problem with that is that pure-PHP is too slow to be practically useful.
 104  
 105                    in addition to encrypting a different string 64 times the OpenSSH implementation also performs bcrypt
 106                    from scratch $rounds times. calling crypt() 64x with bcrypt takes 0.7s. PHP is going to be naturally
 107                    slower. pure-PHP is 215x slower than OpenSSL for AES and pure-PHP is 43x slower for bcrypt.
 108                    43 * 0.7 = 30s. no one wants to wait 30s to load a private key.
 109  
 110                    another way to think about this..  according to wikipedia's article on Blowfish,
 111                    "Each new key requires pre-processing equivalent to encrypting about 4 kilobytes of text".
 112                    key expansion is done (9+64*2)*160 times. multiply that by 4 and it turns out that Blowfish,
 113                    OpenSSH style, is the equivalent of encrypting ~80mb of text.
 114  
 115                    more supporting evidence: sodium_compat does not implement Argon2 (another password hashing
 116                    algorithm) because "It's not feasible to polyfill scrypt or Argon2 into PHP and get reasonable
 117                    performance. Users would feel motivated to select parameters that downgrade security to avoid
 118                    denial of service (DoS) attacks. The only winning move is not to play"
 119                      -- https://github.com/paragonie/sodium_compat/blob/master/README.md
 120                  */
 121                  throw new \RuntimeException('Encrypted OpenSSH private keys are not supported');
 122                  //list($salt, $rounds) = Strings::unpackSSH2('sN', $kdfoptions);
 123              }
 124  
 125              list($publicKey, $paddedKey) = Strings::unpackSSH2('ss', $key);
 126              list($type) = Strings::unpackSSH2('s', $publicKey);
 127              list($checkint1, $checkint2) = Strings::unpackSSH2('NN', $paddedKey);
 128              // any leftover bytes in $paddedKey are for padding? but they should be sequential bytes. eg. 1, 2, 3, etc.
 129              if ($checkint1 != $checkint2) {
 130                  throw new \RuntimeException('The two checkints do not match');
 131              }
 132              self::checkType($type);
 133  
 134              return compact('type', 'publicKey', 'paddedKey');
 135          }
 136  
 137          $parts = explode(' ', $key, 3);
 138  
 139          if (!isset($parts[1])) {
 140              $key = base64_decode($parts[0]);
 141              $comment = isset($parts[1]) ? $parts[1] : false;
 142          } else {
 143              $asciiType = $parts[0];
 144              self::checkType($parts[0]);
 145              $key = base64_decode($parts[1]);
 146              $comment = isset($parts[2]) ? $parts[2] : false;
 147          }
 148          if ($key === false) {
 149              throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
 150          }
 151  
 152          list($type) = Strings::unpackSSH2('s', $key);
 153          self::checkType($type);
 154          if (isset($asciiType) && $asciiType != $type) {
 155              throw new \RuntimeException('Two different types of keys are claimed: ' . $asciiType . ' and ' . $type);
 156          }
 157          if (strlen($key) <= 4) {
 158              throw new \UnexpectedValueException('Key appears to be malformed');
 159          }
 160  
 161          $publicKey = $key;
 162  
 163          return compact('type', 'publicKey', 'comment');
 164      }
 165  
 166      /**
 167       * Toggle between binary and printable keys
 168       *
 169       * Printable keys are what are generated by default. These are the ones that go in
 170       * $HOME/.ssh/authorized_key.
 171       *
 172       * @access public
 173       * @param bool $enabled
 174       */
 175      public static function setBinaryOutput($enabled)
 176      {
 177          self::$binary = $enabled;
 178      }
 179  
 180      /**
 181       * Checks to see if the type is valid
 182       *
 183       * @access private
 184       * @param string $candidate
 185       */
 186      private static function checkType($candidate)
 187      {
 188          if (!in_array($candidate, static::$types)) {
 189              throw new \RuntimeException("The key type ($candidate) is not equal to: " . implode(',', static::$types));
 190          }
 191      }
 192  
 193      /**
 194       * Wrap a private key appropriately
 195       *
 196       * @access public
 197       * @param string $publicKey
 198       * @param string $privateKey
 199       * @param string $password
 200       * @param array $options
 201       * @return string
 202       */
 203      protected static function wrapPrivateKey($publicKey, $privateKey, $password, $options)
 204      {
 205          if (!empty($password) && is_string($password)) {
 206              throw new UnsupportedFormatException('Encrypted OpenSSH private keys are not supported');
 207          }
 208  
 209          list(, $checkint) = unpack('N', Random::string(4));
 210  
 211          $comment = isset($options['comment']) ? $options['comment'] : self::$comment;
 212          $paddedKey = Strings::packSSH2('NN', $checkint, $checkint) .
 213                       $privateKey .
 214                       Strings::packSSH2('s', $comment);
 215  
 216          /*
 217             from http://tools.ietf.org/html/rfc4253#section-6 :
 218  
 219             Note that the length of the concatenation of 'packet_length',
 220             'padding_length', 'payload', and 'random padding' MUST be a multiple
 221             of the cipher block size or 8, whichever is larger.
 222           */
 223          $paddingLength = (7 * strlen($paddedKey)) % 8;
 224          for ($i = 1; $i <= $paddingLength; $i++) {
 225              $paddedKey .= chr($i);
 226          }
 227          $key = Strings::packSSH2('sssNss', 'none', 'none', '', 1, $publicKey, $paddedKey);
 228          $key = "openssh-key-v1\0$key";
 229  
 230          return "-----BEGIN OPENSSH PRIVATE KEY-----\n" .
 231                 chunk_split(Base64::encode($key), 70, "\n") .
 232                 "-----END OPENSSH PRIVATE KEY-----\n";
 233      }
 234  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer