1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-28 09:42:41 +01:00
phorge-phorge/src/applications/auth/sshkey/PhabricatorAuthSSHPublicKey.php
epriestley 0ce08b4d27 Work around lack of PKCS8 support in OSX ssh-keygen
Summary:
Ref T4209. Ref T6240. Ref T6238. See D10401 for original discussion.

On OSX, `ssh-keygen` doesn't support PKCS8:

  - When we hit an issue with this, raise a more tailored message about it.
  - Allow the user to work around the problem with `auth cache-pkcs8 ...`, providing reasonable guidance / warnings.

In practice, this only really matters very much for one key, which I'm just going to make the services extension cache automatically. So it's sort of moot, but good to have around for weird cases and to make testing easier.

Test Plan: Hit error, cached key, got clean asymmetric auth.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T4209, T6240, T6238

Differential Revision: https://secure.phabricator.com/D11021
2014-12-19 16:36:40 -08:00

155 lines
3.8 KiB
PHP

<?php
/**
* Data structure representing a raw public key.
*/
final class PhabricatorAuthSSHPublicKey extends Phobject {
private $type;
private $body;
private $comment;
private function __construct() {
// <internal>
}
public static function newFromStoredKey(PhabricatorAuthSSHKey $key) {
$public_key = new PhabricatorAuthSSHPublicKey();
$public_key->type = $key->getKeyType();
$public_key->body = $key->getKeyBody();
$public_key->comment = $key->getKeyComment();
return $public_key;
}
public static function newFromRawKey($entire_key) {
$entire_key = trim($entire_key);
if (!strlen($entire_key)) {
throw new Exception(pht('No public key was provided.'));
}
$parts = str_replace("\n", '', $entire_key);
// The third field (the comment) can have spaces in it, so split this
// into a maximum of three parts.
$parts = preg_split('/\s+/', $parts, 3);
if (preg_match('/private\s*key/i', $entire_key)) {
// Try to give the user a better error message if it looks like
// they uploaded a private key.
throw new Exception(pht('Provide a public key, not a private key!'));
}
switch (count($parts)) {
case 1:
throw new Exception(
pht('Provided public key is not properly formatted.'));
case 2:
// Add an empty comment part.
$parts[] = '';
break;
case 3:
// This is the expected case.
break;
}
list($type, $body, $comment) = $parts;
$recognized_keys = array(
'ssh-dsa',
'ssh-dss',
'ssh-rsa',
'ecdsa-sha2-nistp256',
'ecdsa-sha2-nistp384',
'ecdsa-sha2-nistp521',
);
if (!in_array($type, $recognized_keys)) {
$type_list = implode(', ', $recognized_keys);
throw new Exception(
pht(
'Public key type should be one of: %s',
$type_list));
}
$public_key = new PhabricatorAuthSSHPublicKey();
$public_key->type = $type;
$public_key->body = $body;
$public_key->comment = $comment;
return $public_key;
}
public function getType() {
return $this->type;
}
public function getBody() {
return $this->body;
}
public function getComment() {
return $this->comment;
}
public function getHash() {
$body = $this->getBody();
$body = trim($body);
$body = rtrim($body, '=');
return PhabricatorHash::digestForIndex($body);
}
public function getEntireKey() {
$key = $this->type.' '.$this->body;
if (strlen($this->comment)) {
$key = $key.' '.$this->comment;
}
return $key;
}
public function toPKCS8() {
$entire_key = $this->getEntireKey();
$cache_key = $this->getPKCS8CacheKey($entire_key);
$cache = PhabricatorCaches::getImmutableCache();
$pkcs8_key = $cache->getKey($cache_key);
if ($pkcs8_key) {
return $pkcs8_key;
}
$tmp = new TempFile();
Filesystem::writeFile($tmp, $this->getEntireKey());
try {
list($pkcs8_key) = execx(
'ssh-keygen -e -m PKCS8 -f %s',
$tmp);
} catch (CommandException $ex) {
unset($tmp);
throw new PhutilProxyException(
pht(
'Failed to convert public key into PKCS8 format. If you are '.
'developing on OSX, you may be able to use `bin/auth cache-pkcs8` '.
'to work around this issue. %s',
$ex->getMessage()),
$ex);
}
unset($tmp);
$cache->setKey($cache_key, $pkcs8_key);
return $pkcs8_key;
}
public function forcePopulatePKCS8Cache($pkcs8_key) {
$entire_key = $this->getEntireKey();
$cache_key = $this->getPKCS8CacheKey($entire_key);
$cache = PhabricatorCaches::getImmutableCache();
$cache->setKey($cache_key, $pkcs8_key);
}
private function getPKCS8CacheKey($entire_key) {
return 'pkcs8:'.PhabricatorHash::digestForIndex($entire_key);
}
}