summaryrefslogtreecommitdiff
path: root/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php')
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php220
1 files changed, 220 insertions, 0 deletions
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php
new file mode 100644
index 0000000..fe3d85b
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php
@@ -0,0 +1,220 @@
+<?php
+
+/**
+ * OpenSSH Key Handler
+ *
+ * PHP version 5
+ *
+ * Place in $HOME/.ssh/authorized_keys
+ *
+ * @author Jim Wigginton <[email protected]>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\Common\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\AES;
+use phpseclib3\Crypt\Random;
+
+/**
+ * OpenSSH Formatted RSA Key Handler
+ *
+ * @author Jim Wigginton <[email protected]>
+ */
+abstract class OpenSSH
+{
+ /**
+ * Default comment
+ *
+ * @var string
+ */
+ protected static $comment = 'phpseclib-generated-key';
+
+ /**
+ * Binary key flag
+ *
+ * @var bool
+ */
+ protected static $binary = false;
+
+ /**
+ * Sets the default comment
+ *
+ * @param string $comment
+ */
+ public static function setComment($comment)
+ {
+ self::$comment = str_replace(["\r", "\n"], '', $comment);
+ }
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * $type can be either ssh-dss or ssh-rsa
+ *
+ * @param string $key
+ * @param string $password
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ if (!Strings::is_stringable($key)) {
+ throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
+ }
+
+ // key format is described here:
+ // https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?annotate=HEAD
+
+ if (strpos($key, 'BEGIN OPENSSH PRIVATE KEY') !== false) {
+ $key = preg_replace('#(?:^-.*?-[\r\n]*$)|\s#ms', '', $key);
+ $key = Strings::base64_decode($key);
+ $magic = Strings::shift($key, 15);
+ if ($magic != "openssh-key-v1\0") {
+ throw new \RuntimeException('Expected openssh-key-v1');
+ }
+ list($ciphername, $kdfname, $kdfoptions, $numKeys) = Strings::unpackSSH2('sssN', $key);
+ if ($numKeys != 1) {
+ // if we wanted to support multiple keys we could update PublicKeyLoader to preview what the # of keys
+ // would be; it'd then call Common\Keys\OpenSSH.php::load() and get the paddedKey. it'd then pass
+ // that to the appropriate key loading parser $numKey times or something
+ throw new \RuntimeException('Although the OpenSSH private key format supports multiple keys phpseclib does not');
+ }
+ switch ($ciphername) {
+ case 'none':
+ break;
+ case 'aes256-ctr':
+ if ($kdfname != 'bcrypt') {
+ throw new \RuntimeException('Only the bcrypt kdf is supported (' . $kdfname . ' encountered)');
+ }
+ list($salt, $rounds) = Strings::unpackSSH2('sN', $kdfoptions);
+ $crypto = new AES('ctr');
+ //$crypto->setKeyLength(256);
+ //$crypto->disablePadding();
+ $crypto->setPassword($password, 'bcrypt', $salt, $rounds, 32);
+ break;
+ default:
+ throw new \RuntimeException('The only supported cipherse are: none, aes256-ctr (' . $ciphername . ' is being used)');
+ }
+
+ list($publicKey, $paddedKey) = Strings::unpackSSH2('ss', $key);
+ list($type) = Strings::unpackSSH2('s', $publicKey);
+ if (isset($crypto)) {
+ $paddedKey = $crypto->decrypt($paddedKey);
+ }
+ list($checkint1, $checkint2) = Strings::unpackSSH2('NN', $paddedKey);
+ // any leftover bytes in $paddedKey are for padding? but they should be sequential bytes. eg. 1, 2, 3, etc.
+ if ($checkint1 != $checkint2) {
+ throw new \RuntimeException('The two checkints do not match');
+ }
+ self::checkType($type);
+
+ return compact('type', 'publicKey', 'paddedKey');
+ }
+
+ $parts = explode(' ', $key, 3);
+
+ if (!isset($parts[1])) {
+ $key = base64_decode($parts[0]);
+ $comment = false;
+ } else {
+ $asciiType = $parts[0];
+ self::checkType($parts[0]);
+ $key = base64_decode($parts[1]);
+ $comment = isset($parts[2]) ? $parts[2] : false;
+ }
+ if ($key === false) {
+ throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
+ }
+
+ list($type) = Strings::unpackSSH2('s', $key);
+ self::checkType($type);
+ if (isset($asciiType) && $asciiType != $type) {
+ throw new \RuntimeException('Two different types of keys are claimed: ' . $asciiType . ' and ' . $type);
+ }
+ if (strlen($key) <= 4) {
+ throw new \UnexpectedValueException('Key appears to be malformed');
+ }
+
+ $publicKey = $key;
+
+ return compact('type', 'publicKey', 'comment');
+ }
+
+ /**
+ * Toggle between binary and printable keys
+ *
+ * Printable keys are what are generated by default. These are the ones that go in
+ * $HOME/.ssh/authorized_key.
+ *
+ * @param bool $enabled
+ */
+ public static function setBinaryOutput($enabled)
+ {
+ self::$binary = $enabled;
+ }
+
+ /**
+ * Checks to see if the type is valid
+ *
+ * @param string $candidate
+ */
+ private static function checkType($candidate)
+ {
+ if (!in_array($candidate, static::$types)) {
+ throw new \RuntimeException("The key type ($candidate) is not equal to: " . implode(',', static::$types));
+ }
+ }
+
+ /**
+ * Wrap a private key appropriately
+ *
+ * @param string $publicKey
+ * @param string $privateKey
+ * @param string $password
+ * @param array $options
+ * @return string
+ */
+ protected static function wrapPrivateKey($publicKey, $privateKey, $password, $options)
+ {
+ list(, $checkint) = unpack('N', Random::string(4));
+
+ $comment = isset($options['comment']) ? $options['comment'] : self::$comment;
+ $paddedKey = Strings::packSSH2('NN', $checkint, $checkint) .
+ $privateKey .
+ Strings::packSSH2('s', $comment);
+
+ $usesEncryption = !empty($password) && is_string($password);
+
+ /*
+ from http://tools.ietf.org/html/rfc4253#section-6 :
+
+ Note that the length of the concatenation of 'packet_length',
+ 'padding_length', 'payload', and 'random padding' MUST be a multiple
+ of the cipher block size or 8, whichever is larger.
+ */
+ $blockSize = $usesEncryption ? 16 : 8;
+ $paddingLength = (($blockSize - 1) * strlen($paddedKey)) % $blockSize;
+ for ($i = 1; $i <= $paddingLength; $i++) {
+ $paddedKey .= chr($i);
+ }
+ if (!$usesEncryption) {
+ $key = Strings::packSSH2('sssNss', 'none', 'none', '', 1, $publicKey, $paddedKey);
+ } else {
+ $rounds = isset($options['rounds']) ? $options['rounds'] : 16;
+ $salt = Random::string(16);
+ $kdfoptions = Strings::packSSH2('sN', $salt, $rounds);
+ $crypto = new AES('ctr');
+ $crypto->setPassword($password, 'bcrypt', $salt, $rounds, 32);
+ $paddedKey = $crypto->encrypt($paddedKey);
+ $key = Strings::packSSH2('sssNss', 'aes256-ctr', 'bcrypt', $kdfoptions, 1, $publicKey, $paddedKey);
+ }
+ $key = "openssh-key-v1\0$key";
+
+ return "-----BEGIN OPENSSH PRIVATE KEY-----\n" .
+ chunk_split(Strings::base64_encode($key), 70, "\n") .
+ "-----END OPENSSH PRIVATE KEY-----\n";
+ }
+}