PluginProbe ʕ •ᴥ•ʔ
JetBackup – Backup, Restore & Migrate / trunk
JetBackup – Backup, Restore & Migrate vtrunk
3.1.22.4 3.1.22.3 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.8.1 1.4.9 1.5.0 1.5.1 1.5.1.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.10 1.6.11 1.6.12 1.6.13 1.6.15 1.6.5.1 1.6.8.8 1.6.9 1.6.9.1 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7.5 2.0.8.7 2.0.9.11 2.0.9.14 2.0.9.15 2.0.9.6 2.0.9.7 2.0.9.9 3.1.10.7 3.1.11.1 3.1.12.3 3.1.13.4 3.1.14.17 3.1.15.4 3.1.16.1 3.1.17.5 3.1.18.10 3.1.18.8 3.1.18.9 3.1.19.8 3.1.20.3 3.1.21.3 3.1.7.9 3.1.9.2 trunk 1.1.90 1.1.91 1.2.0 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2
backup / src / JetBackup / 3rdparty / phpseclib3 / Crypt / Common / Formats / Keys / OpenSSH.php
backup / src / JetBackup / 3rdparty / phpseclib3 / Crypt / Common / Formats / Keys Last commit date
.htaccess 1 year ago JWK.php 1 year ago OpenSSH.php 1 year ago PKCS.php 1 year ago PKCS1.php 1 year ago PKCS8.php 1 year ago PuTTY.php 1 year ago index.html 1 year ago web.config 1 year ago
OpenSSH.php
214 lines
1 <?php
2
3 /**
4 * OpenSSH 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 declare(strict_types=1);
17
18 namespace phpseclib3\Crypt\Common\Formats\Keys;
19
20 use phpseclib3\Common\Functions\Strings;
21 use phpseclib3\Crypt\AES;
22 use phpseclib3\Crypt\Random;
23 use phpseclib3\Exception\RuntimeException;
24 use phpseclib3\Exception\UnexpectedValueException;
25
26 /**
27 * OpenSSH Formatted RSA Key Handler
28 *
29 * @author Jim Wigginton <terrafrost@php.net>
30 */
31 abstract class OpenSSH
32 {
33 /**
34 * Default comment
35 *
36 * @var string
37 */
38 protected static $comment = 'phpseclib-generated-key';
39
40 /**
41 * Binary key flag
42 *
43 * @var bool
44 */
45 protected static $binary = false;
46
47 /**
48 * Sets the default comment
49 */
50 public static function setComment(string $comment): void
51 {
52 self::$comment = str_replace(["\r", "\n"], '', $comment);
53 }
54
55 /**
56 * Break a public or private key down into its constituent components
57 *
58 * $type can be either ssh-dss or ssh-rsa
59 *
60 * @param string|array $key
61 */
62 public static function load($key, ?string $password = null): array
63 {
64 if (!Strings::is_stringable($key)) {
65 throw new UnexpectedValueException('Key should be a string - not a ' . gettype($key));
66 }
67
68 // key format is described here:
69 // https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?annotate=HEAD
70
71 if (str_contains($key, 'BEGIN OPENSSH PRIVATE KEY')) {
72 $key = preg_replace('#(?:^-.*?-[\r\n]*$)|\s#ms', '', $key);
73 $key = Strings::base64_decode($key);
74 $magic = Strings::shift($key, 15);
75 if ($magic != "openssh-key-v1\0") {
76 throw new RuntimeException('Expected openssh-key-v1');
77 }
78 [$ciphername, $kdfname, $kdfoptions, $numKeys] = Strings::unpackSSH2('sssN', $key);
79 if ($numKeys != 1) {
80 // if we wanted to support multiple keys we could update PublicKeyLoader to preview what the # of keys
81 // would be; it'd then call Common\Keys\OpenSSH.php::load() and get the paddedKey. it'd then pass
82 // that to the appropriate key loading parser $numKey times or something
83 throw new RuntimeException('Although the OpenSSH private key format supports multiple keys phpseclib does not');
84 }
85
86 switch ($ciphername) {
87 case 'none':
88 break;
89 case 'aes256-ctr':
90 if ($kdfname != 'bcrypt') {
91 throw new RuntimeException('Only the bcrypt kdf is supported (' . $kdfname . ' encountered)');
92 }
93 [$salt, $rounds] = Strings::unpackSSH2('sN', $kdfoptions);
94 $crypto = new AES('ctr');
95 //$crypto->setKeyLength(256);
96 //$crypto->disablePadding();
97 $crypto->setPassword($password, 'bcrypt', $salt, $rounds, 32);
98 break;
99 default:
100 throw new RuntimeException('The only supported cipherse are: none, aes256-ctr (' . $ciphername . ' is being used)');
101 }
102
103 [$publicKey, $paddedKey] = Strings::unpackSSH2('ss', $key);
104 [$type] = Strings::unpackSSH2('s', $publicKey);
105 if (isset($crypto)) {
106 $paddedKey = $crypto->decrypt($paddedKey);
107 }
108 [$checkint1, $checkint2] = Strings::unpackSSH2('NN', $paddedKey);
109 // any leftover bytes in $paddedKey are for padding? but they should be sequential bytes. eg. 1, 2, 3, etc.
110 if ($checkint1 != $checkint2) {
111 throw new RuntimeException('The two checkints do not match');
112 }
113 self::checkType($type);
114
115 return compact('type', 'publicKey', 'paddedKey');
116 }
117
118 $parts = explode(' ', $key, 3);
119
120 if (!isset($parts[1])) {
121 $key = base64_decode($parts[0]);
122 $comment = false;
123 } else {
124 $asciiType = $parts[0];
125 self::checkType($parts[0]);
126 $key = base64_decode($parts[1]);
127 $comment = $parts[2] ?? false;
128 }
129 if ($key === false) {
130 throw new UnexpectedValueException('Key should be a string - not a ' . gettype($key));
131 }
132
133 [$type] = Strings::unpackSSH2('s', $key);
134 self::checkType($type);
135 if (isset($asciiType) && $asciiType != $type) {
136 throw new RuntimeException('Two different types of keys are claimed: ' . $asciiType . ' and ' . $type);
137 }
138 if (strlen($key) <= 4) {
139 throw new UnexpectedValueException('Key appears to be malformed');
140 }
141
142 $publicKey = $key;
143
144 return compact('type', 'publicKey', 'comment');
145 }
146
147 /**
148 * Toggle between binary and printable keys
149 *
150 * Printable keys are what are generated by default. These are the ones that go in
151 * $HOME/.ssh/authorized_key.
152 */
153 public static function setBinaryOutput(bool $enabled): void
154 {
155 self::$binary = $enabled;
156 }
157
158 /**
159 * Checks to see if the type is valid
160 */
161 private static function checkType(string $candidate): void
162 {
163 if (!in_array($candidate, static::$types)) {
164 throw new RuntimeException("The key type ($candidate) is not equal to: " . implode(',', static::$types));
165 }
166 }
167
168 /**
169 * Wrap a private key appropriately
170 *
171 * @param string|false $password
172 */
173 protected static function wrapPrivateKey(string $publicKey, string $privateKey, $password, array $options): string
174 {
175 [, $checkint] = unpack('N', Random::string(4));
176
177 $comment = $options['comment'] ?? self::$comment;
178 $paddedKey = Strings::packSSH2('NN', $checkint, $checkint) .
179 $privateKey .
180 Strings::packSSH2('s', $comment);
181
182 $usesEncryption = !empty($password) && is_string($password);
183
184 /*
185 from http://tools.ietf.org/html/rfc4253#section-6 :
186
187 Note that the length of the concatenation of 'packet_length',
188 'padding_length', 'payload', and 'random padding' MUST be a multiple
189 of the cipher block size or 8, whichever is larger.
190 */
191 $blockSize = $usesEncryption ? 16 : 8;
192 $paddingLength = (($blockSize - 1) * strlen($paddedKey)) % $blockSize;
193 for ($i = 1; $i <= $paddingLength; $i++) {
194 $paddedKey .= chr($i);
195 }
196 if (!$usesEncryption) {
197 $key = Strings::packSSH2('sssNss', 'none', 'none', '', 1, $publicKey, $paddedKey);
198 } else {
199 $rounds = $options['rounds'] ?? 16;
200 $salt = Random::string(16);
201 $kdfoptions = Strings::packSSH2('sN', $salt, $rounds);
202 $crypto = new AES('ctr');
203 $crypto->setPassword($password, 'bcrypt', $salt, $rounds, 32);
204 $paddedKey = $crypto->encrypt($paddedKey);
205 $key = Strings::packSSH2('sssNss', 'aes256-ctr', 'bcrypt', $kdfoptions, 1, $publicKey, $paddedKey);
206 }
207 $key = "openssh-key-v1\0$key";
208
209 return "-----BEGIN OPENSSH PRIVATE KEY-----\n" .
210 chunk_split(Strings::base64_encode($key), 70, "\n") .
211 "-----END OPENSSH PRIVATE KEY-----\n";
212 }
213 }
214