/** * Install constructor. * * @param \Twig_Environment $twig * @param array $data */ public function __construct(\Twig_Environment $twig, array $data = []) { if (!Halite::isLibsodiumSetupCorrectly()) { echo \file_get_contents(\dirname(__DIR__) . '/error_pages/old-libsodium.html'); exit(255); } $this->twig = $twig; $this->data = $data; $this->data['airship_version'] = \AIRSHIP_VERSION; $this->csrf = new CSRF(); // We do this to prevent someone from coming along and reading your // half-finished configuration settings (e.g. database passwords): if (empty($this->data['step'])) { $this->data['step'] = 1; } if (empty($this->data['token'])) { $this->data['token'] = Base64::encode(\random_bytes(33)); \setcookie('installer', $this->data['token'], \time() + 8640000, '/'); \Airship\redirect('/'); } elseif (empty($_COOKIE['installer'])) { echo 'No installer authorization token found.', "\n"; exit(255); } elseif (!\hash_equals($this->data['token'], $_COOKIE['installer'])) { // This effectively locks unauthorized users out of the system while installing echo 'Invalid installer authorization token.', "\n"; exit(255); } $dirs = ['comments', 'csp_hash', 'csp_static', 'hash', 'markdown', 'static', 'twig']; foreach ($dirs as $d) { if (!\is_dir(\dirname(__DIR__) . '/tmp/cache/' . $d)) { \mkdir(\dirname(__DIR__) . '/tmp/cache/' . $d, 0775, true); } } }
public function testFailure() { try { KeyPair::generateKeyPair(1024); $this->fail('Accepts too small of a key size!'); return; } catch (\Exception $ex) { $keyPair = KeyPair::generateKeyPair(2048); } $secretKey = $keyPair->getPrivateKey(); $publicKey = $keyPair->getPublicKey(); $plain = 'Short Message'; $encrypt = EasyRSA::encrypt($plain, $publicKey); $dissect = explode('$', $encrypt); // Flip a bit in the key, randomly! $dissect[1] = base64_decode($dissect[1]); $l = mt_rand(0, strlen($dissect[1]) - 1); $dissect[1][$l] = \chr(\ord($dissect[1][$l]) ^ 1 << mt_rand(0, 7)); $dissect[1] = base64_encode($dissect[1]); try { EasyRSA::decrypt(implode('$', $dissect), $secretKey); $this->fail('Checksum collision or logic error.'); return; } catch (\Exception $ex) { $this->assertInstanceOf('\\ParagonIE\\EasyRSA\\Exception\\InvalidChecksumException', $ex); } $dissect[3] = substr(hash('sha256', implode('$', array_slice($dissect, 0, 3))), 0, 16); try { EasyRSA::decrypt(implode('$', $dissect), $secretKey); $this->fail('This should not have passed.'); } catch (\Exception $ex) { $this->assertInstanceOf('\\ParagonIE\\EasyRSA\\Exception\\InvalidCiphertextException', $ex); } /////////////////////////////////////////////////////////////////////// $dissect = explode('$', $encrypt); // Flip a bit in the message, randomly! $dissect[2] = base64_decode($dissect[2]); $l = mt_rand(0, strlen($dissect[2]) - 1); $dissect[2][$l] = \chr(\ord($dissect[2][$l]) ^ 1 << mt_rand(0, 7)); $dissect[2] = Base64::encode($dissect[2]); try { $dummy = EasyRSA::decrypt(implode('$', $dissect), $secretKey); $this->fail('Checksum collision or logic error.'); unset($dummy); return; } catch (\Exception $ex) { $this->assertInstanceOf('\\ParagonIE\\EasyRSA\\Exception\\InvalidChecksumException', $ex); } $dissect[3] = substr(hash('sha256', implode('$', array_slice($dissect, 0, 3))), 0, 16); try { EasyRSA::decrypt(implode('$', $dissect), $secretKey); $this->fail('This should not have passed.'); } catch (\Exception $ex) { $this->assertInstanceOf('\\Defuse\\Crypto\\Exception\\WrongKeyOrModifiedCiphertextException', $ex); } }
public function testVectorBase64() { $this->assertSame(Base64::encode(''), ''); $this->assertSame(Base64::encode('f'), 'Zg=='); $this->assertSame(Base64::encode('fo'), 'Zm8='); $this->assertSame(Base64::encode('foo'), 'Zm9v'); $this->assertSame(Base64::encode('foob'), 'Zm9vYg=='); $this->assertSame(Base64::encode('fooba'), 'Zm9vYmE='); $this->assertSame(Base64::encode('foobar'), 'Zm9vYmFy'); }
/** * 1. VerifyHMAC-then-Decrypt the ciphertext to get the hash * 2. Verify that the password matches the hash * * @param string $password * @param string $ciphertext * @param Key $aesKey * @return bool * @throws \Exception * @throws \InvalidArgumentException */ public static function decryptAndVerify(string $password, string $ciphertext, Key $aesKey) : bool { if (!\is_string($password)) { throw new \InvalidArgumentException('Password must be a string.'); } if (!\is_string($ciphertext)) { throw new \InvalidArgumentException('Ciphertext must be a string.'); } $hash = Crypto::decrypt($ciphertext, $aesKey); return \password_verify(Base64::encode(\hash('sha384', $password, true)), $hash); }
/** * * Encrypt a message with defuse/php-encryption, using an ephemeral key, * then encrypt the key with RSA. * * @param string $ciphertext * @param PrivateKey $rsaPrivateKey * * @return string * @throws InvalidCiphertextException * @throws InvalidChecksumException */ public static function decrypt($ciphertext, PrivateKey $rsaPrivateKey) { $split = explode(self::SEPARATOR, $ciphertext); if (\count($split) !== 4) { throw new InvalidCiphertextException('Invalid ciphertext message'); } if (!\hash_equals($split[0], self::VERSION_TAG)) { throw new InvalidCiphertextException('Invalid version tag'); } $checksum = \substr(\hash('sha256', implode('$', array_slice($split, 0, 3))), 0, 16); if (!\hash_equals($split[3], $checksum)) { throw new InvalidChecksumException('Invalid checksum'); } $key = Key::loadFromAsciiSafeString(self::rsaDecrypt(Base64::decode($split[1]), $rsaPrivateKey)); return Crypto::Decrypt(Base64::decode($split[2]), $key, true); }
/** * @covers Base64::encode() * @covers Base64::decode() */ public function testRandom() { for ($i = 1; $i < 32; ++$i) { for ($j = 0; $j < 50; ++$j) { $random = \random_bytes($i); $enc = Base64::encode($random); $this->assertSame($random, Base64::decode($enc)); $this->assertSame(\base64_encode($random), $enc); $unpadded = \rtrim($enc, '='); $this->assertSame($random, Base64::decode($unpadded)); } } $str = 'MIIFzzCCBLegAwIBAgIDAfdlMA0GCSqGSIb3DQEBBQUAMHMxCzAJBgNVBAYTAlBM' . 'MSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMSQwIgYDVQQ' . 'DDBtDT1BFIFNaQUZJUiAtIEt3YWxpZmlrb3dhbnkxFDASBgNVBAUTC05yIHdwaXN1Oi' . 'A2MB4XDTExMTEwOTA2MDAwMFoXDTEzMTEwOTA2MDAwMFowgdkxCzAJBgNVBAYTAlBMM' . 'RwwGgYDVQQKDBNVcnrEhWQgTWlhc3RhIEdkeW5pMRswGQYDVQQFExJQRVNFTDogNjEw' . 'NjA2MDMxMTgxGTAXBgNVBAMMEEplcnp5IFByemV3b3Jza2kxTzBNBgNVBBAwRgwiQWw' . 'uIE1hcnN6YcWCa2EgUGnFgnN1ZHNraWVnbyA1Mi81NAwNODEtMzgyIEdkeW5pYQwGUG' . '9sc2thDAlwb21vcnNraWUxDjAMBgNVBCoMBUplcnp5MRMwEQYDVQQEDApQcnpld29yc' . '2tpMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCMm5vjGqHPthJCMqKpqssSISRo' . 's0PYDTcEQzyyurfX67EJWKtZj6HNwuDMEGJ02iBNZfjUl7r8dIi28bSKhNlsfycXZKY' . 'RcIjp0+r5RqtR2auo9GQ6veKb61DEAGIqaR+uLLcJVTHCu0w9oXLGbRlGth5eNoj03C' . 'xXVAH2IfhbNwIDAQABo4IChzCCAoMwDAYDVR0TAQH/BAIwADCCAUgGA1UdIAEB/wSCA' . 'TwwggE4MIIBNAYJKoRoAYb3IwEBMIIBJTCB3QYIKwYBBQUHAgIwgdAMgc1EZWtsYXJh' . 'Y2phIHRhIGplc3Qgb8Wbd2lhZGN6ZW5pZW0gd3lkYXdjeSwgxbxlIHRlbiBjZXJ0eWZ' . 'pa2F0IHpvc3RhxYIgd3lkYW55IGpha28gY2VydHlmaWthdCBrd2FsaWZpa293YW55IH' . 'pnb2RuaWUgeiB3eW1hZ2FuaWFtaSB1c3Rhd3kgbyBwb2RwaXNpZSBlbGVrdHJvbmlje' . 'm55bSBvcmF6IHRvd2FyenlzesSFY3ltaSBqZWogcm96cG9yesSFZHplbmlhbWkuMEMG' . 'CCsGAQUFBwIBFjdodHRwOi8vd3d3Lmtpci5jb20ucGwvY2VydHlmaWthY2phX2tsdWN' . '6eS9wb2xpdHlrYS5odG1sMAkGA1UdCQQCMAAwIQYDVR0RBBowGIEWai5wcnpld29yc2' . 'tpQGdkeW5pYS5wbDAOBgNVHQ8BAf8EBAMCBkAwgZ4GA1UdIwSBljCBk4AU3TGldJXip' . 'N4oGS3ZYmnBDMFs8gKhd6R1MHMxCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dh' . 'IEl6YmEgUm96bGljemVuaW93YSBTLkEuMSQwIgYDVQQDDBtDT1BFIFNaQUZJUiAtIEt' . '3YWxpZmlrb3dhbnkxFDASBgNVBAUTC05yIHdwaXN1OiA2ggJb9jBIBgNVHR8EQTA/MD' . '2gO6A5hjdodHRwOi8vd3d3Lmtpci5jb20ucGwvY2VydHlmaWthY2phX2tsdWN6eS9DU' . 'kxfT1pLMzIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQBYPIqnAreyeql7/opJjcar/qWZ' . 'y9ruhB2q0lZFsJOhwgMnbQXzp/4vv93YJqcHGAXdHP6EO8FQX47mjo2ZKQmi+cIHJHL' . 'ONdX/3Im+M17V0iNAh7Z1lOSfTRT+iiwe/F8phcEaD5q2RmvYusR7zXZq/cLL0If0hX' . 'oPZ/EHQxjN8pxzxiUx6bJAgturnIMEfRNesxwghdr1dkUjOhGLf3kHVzgM6j3VAM7oF' . 'mMUb5y5s96Bzl10DodWitjOEH0vvnIcsppSxH1C1dCAi0o9f/1y2XuLNhBNHMAyTqpY' . 'PX8Yvav1c+Z50OMaSXHAnTa20zv8UtiHbaAhwlifCelUMj93S'; try { Base64::decode($str, true); $this->fail('Strict padding not enforced'); } catch (\Exception $ex) { $this->assertSame(Base64::decode($str), \base64_decode($str)); } }
/** * Request Identities * * See "2.5.2 Requesting a list of protocol 2 keys" * Returns an array containing zero or more \phpseclib\System\SSH\Agent\Identity objects * * @return array * @throws \RuntimeException on receipt of unexpected packets * @access public */ function requestIdentities() { if (!$this->fsock) { return array(); } $packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES); if (strlen($packet) != fputs($this->fsock, $packet)) { throw new \RuntimeException('Connection closed while requesting identities'); } $length = current(unpack('N', fread($this->fsock, 4))); $type = ord(fread($this->fsock, 1)); if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) { throw new \RuntimeException('Unable to request identities'); } $identities = array(); $keyCount = current(unpack('N', fread($this->fsock, 4))); for ($i = 0; $i < $keyCount; $i++) { $length = current(unpack('N', fread($this->fsock, 4))); $key_blob = fread($this->fsock, $length); $key_str = 'ssh-rsa ' . Base64::encode($key_blob); $length = current(unpack('N', fread($this->fsock, 4))); if ($length) { $key_str .= ' ' . fread($this->fsock, $length); } $length = current(unpack('N', substr($key_blob, 0, 4))); $key_type = substr($key_blob, 4, $length); switch ($key_type) { case 'ssh-rsa': $key = new RSA(); $key->load($key_str); break; case 'ssh-dss': // not currently supported break; } // resources are passed by reference by default if (isset($key)) { $identity = new Identity($this->fsock); $identity->setPublicKey($key); $identity->setPublicKeyBlob($key_blob); $identities[] = $identity; unset($key); } } return $identities; }
/** * Wrap a public key appropriately * * @access public * @param string $key * @return string */ static function wrapPublicKey($key, $algorithm) { $asn1 = new ASN1(); $key = ['publicKeyAlgorithm' => ['algorithm' => $algorithm, 'parameters' => null], 'publicKey' => Base64::encode("" . $key)]; $key = $asn1->encodeDER($key, PublicKeyInfo); return "-----BEGIN PUBLIC KEY-----\r\n" . chunk_split(Base64::encode($key), 64) . "-----END PUBLIC KEY-----"; }
/** * Convert a public key to the appropriate format * * @access public * @param \phpseclib\Math\BigInteger $n * @param \phpseclib\Math\BigInteger $e * @return string */ static function savePublicKey(BigInteger $n, BigInteger $e) { return "<RSAKeyValue>\r\n" . ' <Modulus>' . Base64::encode($n->toBytes()) . "</Modulus>\r\n" . ' <Exponent>' . Base64::encode($e->toBytes()) . "</Exponent>\r\n" . '</RSAKeyValue>'; }
/** * Add a file or directory to the * * @param string $filename * @param string $dir * @return int */ protected function addautoRun(string $filename, string $dir) : int { if (!empty($dir)) { if ($dir[\strlen($dir) - 1] !== DIRECTORY_SEPARATOR) { $dir .= DIRECTORY_SEPARATOR; } } if (!\file_exists($filename)) { echo $this->c['red'], 'File not found: ', $this->c[''], $filename, "\n"; return 0; } try { $path = $this->getRealPath(\realpath($filename)); } catch (\Error $e) { echo $this->c['red'], $e->getMessage(), $this->c[''], "\n"; return 0; } if (\array_key_exists($path, $this->session['autoRun'])) { echo $this->c['yellow'], 'autoRun script already registered: ', $this->c[''], $path, "\n"; return 0; } // Recursive adding if (\is_dir($path)) { echo $this->c['red'], 'You cannot add a directory to an autoRun script: ', $this->c[''], $path, "\n"; return 0; } $this->session['autoRun'][$path] = ['type' => $this->getType($path), 'data' => Base64::encode(\file_get_contents($path))]; echo $this->c['green'], 'autoRun script registered: ', $this->c[''], $path, "\n"; return 1; }
/** * ASN.1 Encode (Helper function) * * @param string $source * @param string $mapping * @param int $idx * @return string * @throws \RuntimeException if the input has an error in it * @access private */ function _encode_der($source, $mapping, $idx = null, $special = array()) { if ($source instanceof Element) { return $source->element; } // do not encode (implicitly optional) fields with value set to default if (isset($mapping['default']) && $source === $mapping['default']) { return ''; } if (isset($idx)) { if (isset($special[$idx])) { $source = call_user_func($special[$idx], $source); } $this->location[] = $idx; } $tag = $mapping['type']; switch ($tag) { case self::TYPE_SET: // Children order is not important, thus process in sequence. // Children order is not important, thus process in sequence. case self::TYPE_SEQUENCE: $tag |= 0x20; // set the constructed bit // ignore the min and max if (isset($mapping['min']) && isset($mapping['max'])) { $value = array(); $child = $mapping['children']; foreach ($source as $content) { $temp = $this->_encode_der($content, $child, null, $special); if ($temp === false) { return false; } $value[] = $temp; } /* "The encodings of the component values of a set-of value shall appear in ascending order, the encodings being compared as octet strings with the shorter components being padded at their trailing end with 0-octets. NOTE - The padding octets are for comparison purposes only and do not appear in the encodings." -- sec 11.6 of http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf */ if ($mapping['type'] == self::TYPE_SET) { sort($value); } $value = implode($value, ''); break; } $value = ''; foreach ($mapping['children'] as $key => $child) { if (!array_key_exists($key, $source)) { if (!isset($child['optional'])) { return false; } continue; } $temp = $this->_encode_der($source[$key], $child, $key, $special); if ($temp === false) { return false; } // An empty child encoding means it has been optimized out. // Else we should have at least one tag byte. if ($temp === '') { continue; } // if isset($child['constant']) is true then isset($child['optional']) should be true as well if (isset($child['constant'])) { /* From X.680-0207.pdf#page=58 (30.6): "The tagging construction specifies explicit tagging if any of the following holds: ... c) the "Tag Type" alternative is used and the value of "TagDefault" for the module is IMPLICIT TAGS or AUTOMATIC TAGS, but the type defined by "Type" is an untagged choice type, an untagged open type, or an untagged "DummyReference" (see ITU-T Rec. X.683 | ISO/IEC 8824-4, 8.3)." */ if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) { $subtag = chr(self::CLASS_CONTEXT_SPECIFIC << 6 | 0x20 | $child['constant']); $temp = $subtag . Functions::encodeLength(strlen($temp)) . $temp; } else { $subtag = chr(self::CLASS_CONTEXT_SPECIFIC << 6 | ord($temp[0]) & 0x20 | $child['constant']); $temp = $subtag . substr($temp, 1); } } $value .= $temp; } break; case self::TYPE_CHOICE: $temp = false; foreach ($mapping['children'] as $key => $child) { if (!isset($source[$key])) { continue; } $temp = $this->_encode_der($source[$key], $child, $key, $special); if ($temp === false) { return false; } // An empty child encoding means it has been optimized out. // Else we should have at least one tag byte. if ($temp === '') { continue; } $tag = ord($temp[0]); // if isset($child['constant']) is true then isset($child['optional']) should be true as well if (isset($child['constant'])) { if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) { $subtag = chr(self::CLASS_CONTEXT_SPECIFIC << 6 | 0x20 | $child['constant']); $temp = $subtag . Functions::encodeLength(strlen($temp)) . $temp; } else { $subtag = chr(self::CLASS_CONTEXT_SPECIFIC << 6 | ord($temp[0]) & 0x20 | $child['constant']); $temp = $subtag . substr($temp, 1); } } } if (isset($idx)) { array_pop($this->location); } if ($temp && isset($mapping['cast'])) { $temp[0] = chr($mapping['class'] << 6 | $tag & 0x20 | $mapping['cast']); } return $temp; case self::TYPE_INTEGER: case self::TYPE_ENUMERATED: if (!isset($mapping['mapping'])) { if (is_numeric($source)) { $source = new BigInteger($source); } $value = $source->toBytes(true); } else { $value = array_search($source, $mapping['mapping']); if ($value === false) { return false; } $value = new BigInteger($value); $value = $value->toBytes(true); } if (!strlen($value)) { $value = chr(0); } break; case self::TYPE_UTC_TIME: case self::TYPE_GENERALIZED_TIME: $format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y'; $format .= 'mdHis'; $value = @gmdate($format, strtotime($source)) . 'Z'; break; case self::TYPE_BIT_STRING: if (isset($mapping['mapping'])) { $bits = array_fill(0, count($mapping['mapping']), 0); $size = 0; for ($i = 0; $i < count($mapping['mapping']); $i++) { if (in_array($mapping['mapping'][$i], $source)) { $bits[$i] = 1; $size = $i; } } if (isset($mapping['min']) && $mapping['min'] >= 1 && $size < $mapping['min']) { $size = $mapping['min'] - 1; } $offset = 8 - ($size + 1 & 7); $offset = $offset !== 8 ? $offset : 0; $value = chr($offset); for ($i = $size + 1; $i < count($mapping['mapping']); $i++) { unset($bits[$i]); } $bits = implode('', array_pad($bits, $size + $offset + 1, 0)); $bytes = explode(' ', rtrim(chunk_split($bits, 8, ' '))); foreach ($bytes as $byte) { $value .= chr(bindec($byte)); } break; } case self::TYPE_OCTET_STRING: /* The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit, the number of unused bits in the final subsequent octet. The number shall be in the range zero to seven. -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=16 */ $value = Base64::decode($source); break; case self::TYPE_OBJECT_IDENTIFIER: $oid = preg_match('#(?:\\d+\\.)+#', $source) ? $source : array_search($source, $this->oids); if ($oid === false) { throw new \RuntimeException('Invalid OID'); return false; } $value = ''; $parts = explode('.', $oid); $value = chr(40 * $parts[0] + $parts[1]); for ($i = 2; $i < count($parts); $i++) { $temp = ''; if (!$parts[$i]) { $temp = ""; } else { while ($parts[$i]) { $temp = chr(0x80 | $parts[$i] & 0x7f) . $temp; $parts[$i] >>= 7; } $temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7f); } $value .= $temp; } break; case self::TYPE_ANY: $loc = $this->location; if (isset($idx)) { array_pop($this->location); } switch (true) { case !isset($source): return $this->_encode_der(null, array('type' => self::TYPE_NULL) + $mapping, null, $special); case is_int($source): case $source instanceof BigInteger: return $this->_encode_der($source, array('type' => self::TYPE_INTEGER) + $mapping, null, $special); case is_float($source): return $this->_encode_der($source, array('type' => self::TYPE_REAL) + $mapping, null, $special); case is_bool($source): return $this->_encode_der($source, array('type' => self::TYPE_BOOLEAN) + $mapping, null, $special); case is_array($source) && count($source) == 1: $typename = implode('', array_keys($source)); $outtype = array_search($typename, $this->ANYmap, true); if ($outtype !== false) { return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping, null, $special); } } $filters = $this->filters; foreach ($loc as $part) { if (!isset($filters[$part])) { $filters = false; break; } $filters = $filters[$part]; } if ($filters === false) { throw new \RuntimeException('No filters defined for ' . implode('/', $loc)); return false; } return $this->_encode_der($source, $filters + $mapping, null, $special); case self::TYPE_NULL: $value = ''; break; case self::TYPE_NUMERIC_STRING: case self::TYPE_TELETEX_STRING: case self::TYPE_PRINTABLE_STRING: case self::TYPE_UNIVERSAL_STRING: case self::TYPE_UTF8_STRING: case self::TYPE_BMP_STRING: case self::TYPE_IA5_STRING: case self::TYPE_VISIBLE_STRING: case self::TYPE_VIDEOTEX_STRING: case self::TYPE_GRAPHIC_STRING: case self::TYPE_GENERAL_STRING: $value = $source; break; case self::TYPE_BOOLEAN: $value = $source ? "ÿ" : ""; break; default: throw new \RuntimeException('Mapping provides no type definition for ' . implode('/', $this->location)); return false; } if (isset($idx)) { array_pop($this->location); } if (isset($mapping['cast'])) { if (isset($mapping['explicit']) || $mapping['type'] == self::TYPE_CHOICE) { $value = chr($tag) . Functions::encodeLength(strlen($value)) . $value; $tag = $mapping['class'] << 6 | 0x20 | $mapping['cast']; } else { $tag = $mapping['class'] << 6 | ord($temp[0]) & 0x20 | $mapping['cast']; } } return chr($tag) . Functions::encodeLength(strlen($value)) . $value; }
/** * Convert a public key to the appropriate format * * @access public * @param \phpseclib\Math\BigInteger $n * @param \phpseclib\Math\BigInteger $e * @return string */ static function savePublicKey(BigInteger $n, BigInteger $e) { $publicExponent = $e->toBytes(true); $modulus = $n->toBytes(true); // from <http://tools.ietf.org/html/rfc4253#page-15>: // string "ssh-rsa" // mpint e // mpint n $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus); $RSAPublicKey = 'ssh-rsa ' . Base64::encode($RSAPublicKey) . ' ' . self::$comment; return $RSAPublicKey; }
/** * Returns the server public host key. * * Caching this the first time you connect to a server and checking the result on subsequent connections * is recommended. Returns false if the server signature is not signed correctly with the public host key. * * @return mixed * @throws \RuntimeException on badly formatted keys * @throws \phpseclib\Exception\NoSupportedAlgorithmsException when the key isn't in a supported format * @access public */ function getServerPublicHostKey() { if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { if (!$this->_connect()) { return false; } } $signature = $this->signature; $server_public_host_key = $this->server_public_host_key; if (strlen($server_public_host_key) < 4) { return false; } extract(unpack('Nlength', Strings::shift($server_public_host_key, 4))); Strings::shift($server_public_host_key, $length); if ($this->signature_validated) { return $this->bitmap ? $this->signature_format . ' ' . Base64::encode($this->server_public_host_key) : false; } $this->signature_validated = true; switch ($this->signature_format) { case 'ssh-dss': $zero = new BigInteger(); if (strlen($server_public_host_key) < 4) { return false; } $temp = unpack('Nlength', Strings::shift($server_public_host_key, 4)); $p = new BigInteger(Strings::shift($server_public_host_key, $temp['length']), -256); if (strlen($server_public_host_key) < 4) { return false; } $temp = unpack('Nlength', Strings::shift($server_public_host_key, 4)); $q = new BigInteger(Strings::shift($server_public_host_key, $temp['length']), -256); if (strlen($server_public_host_key) < 4) { return false; } $temp = unpack('Nlength', Strings::shift($server_public_host_key, 4)); $g = new BigInteger(Strings::shift($server_public_host_key, $temp['length']), -256); if (strlen($server_public_host_key) < 4) { return false; } $temp = unpack('Nlength', Strings::shift($server_public_host_key, 4)); $y = new BigInteger(Strings::shift($server_public_host_key, $temp['length']), -256); /* The value for 'dss_signature_blob' is encoded as a string containing r, followed by s (which are 160-bit integers, without lengths or padding, unsigned, and in network byte order). */ $temp = unpack('Nlength', Strings::shift($signature, 4)); if ($temp['length'] != 40) { $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new \RuntimeException('Invalid signature'); } $r = new BigInteger(Strings::shift($signature, 20), 256); $s = new BigInteger(Strings::shift($signature, 20), 256); switch (true) { case $r->equals($zero): case $r->compare($q) >= 0: case $s->equals($zero): case $s->compare($q) >= 0: $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new \RuntimeException('Invalid signature'); } $w = $s->modInverse($q); $u1 = $w->multiply(new BigInteger(sha1($this->exchange_hash), 16)); list(, $u1) = $u1->divide($q); $u2 = $w->multiply($r); list(, $u2) = $u2->divide($q); $g = $g->modPow($u1, $p); $y = $y->modPow($u2, $p); $v = $g->multiply($y); list(, $v) = $v->divide($p); list(, $v) = $v->divide($q); if (!$v->equals($r)) { //user_error('Bad server signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); } break; case 'ssh-rsa': if (strlen($server_public_host_key) < 4) { return false; } $temp = unpack('Nlength', Strings::shift($server_public_host_key, 4)); $e = new BigInteger(Strings::shift($server_public_host_key, $temp['length']), -256); if (strlen($server_public_host_key) < 4) { return false; } $temp = unpack('Nlength', Strings::shift($server_public_host_key, 4)); $rawN = Strings::shift($server_public_host_key, $temp['length']); $n = new BigInteger($rawN, -256); $nLength = strlen(ltrim($rawN, "")); /* if (strlen($signature) < 4) { return false; } $temp = unpack('Nlength', Strings::shift($signature, 4)); $signature = Strings::shift($signature, $temp['length']); $rsa = new RSA(); $rsa->load(array('e' => $e, 'n' => $n), 'raw'); $rsa->setHash('sha1'); if (!$rsa->verify($this->exchange_hash, $signature, RSA::PADDING_PKCS1)) { //user_error('Bad server signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); } */ if (strlen($signature) < 4) { return false; } $temp = unpack('Nlength', Strings::shift($signature, 4)); $s = new BigInteger(Strings::shift($signature, $temp['length']), 256); // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the // following URL: // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source. if ($s->compare(new BigInteger()) < 0 || $s->compare($n->subtract(new BigInteger(1))) > 0) { $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new \RuntimeException('Invalid signature'); } $s = $s->modPow($e, $n); $s = $s->toBytes(); $h = pack('N4H*', 0x302130, 0x906052b, 0xe03021a, 0x5000414, sha1($this->exchange_hash)); $h = chr(0x1) . str_repeat(chr(0xff), $nLength - 2 - strlen($h)) . $h; if ($s != $h) { //user_error('Bad server signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); } break; default: $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); throw new NoSupportedAlgorithmsException('Unsupported signature format'); } return $this->signature_format . ' ' . Base64::encode($this->server_public_host_key); }
/** * Add a new nonce to the existing CSP * * @param string $directive * @param string $nonce (if empty, it will be generated) * @return null|string */ public function nonce(string $directive = 'script-src', string $nonce = '') : string { $ruleKeys = \array_keys($this->policies); if (!\in_array($directive, $ruleKeys)) { return ''; } if (empty($nonce)) { $nonce = Base64::encode(\random_bytes(18)); } $this->policies[$directive]['nonces'][] = $nonce; return $nonce; }
/** * Extract raw BER from Base64 encoding * * @access private * @param string $str * @return string */ static function _extractBER($str) { /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them * above and beyond the ceritificate. * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line: * * Bag Attributes * localKeyID: 01 00 00 00 * subject=/O=organization/OU=org unit/CN=common name * issuer=/O=organization/CN=common name */ $temp = preg_replace('#.*?^-+[^-]+-+[\\r\\n ]*$#ms', '', $str, 1); // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff $temp = preg_replace('#-+[^-]+-+#', '', $temp); // remove new lines $temp = str_replace(array("\r", "\n", ' '), '', $temp); $temp = preg_match('#^[a-zA-Z\\d/+]*={0,2}$#', $temp) ? Base64::decode($temp) : false; return $temp != false ? $temp : $str; }
/** * Convert a public key to the appropriate format * * @access public * @param \phpseclib\Math\BigInteger $n * @param \phpseclib\Math\BigInteger $e * @return string */ static function savePublicKey(BigInteger $n, BigInteger $e) { $modulus = $n->toBytes(true); $publicExponent = $e->toBytes(true); // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>: // RSAPublicKey ::= SEQUENCE { // modulus INTEGER, -- n // publicExponent INTEGER -- e // } $components = array('modulus' => pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($modulus)), $modulus), 'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($publicExponent)), $publicExponent)); $RSAPublicKey = pack('Ca*a*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), $components['modulus'], $components['publicExponent']); $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" . chunk_split(Base64::encode($RSAPublicKey), 64) . '-----END RSA PUBLIC KEY-----'; return $RSAPublicKey; }
/** * Coerce a string into base64 format. * * @param string $hash * @param string $algo * @return string * @throws \Exception */ protected function coerceBase64(string $hash, string $algo = 'sha256') : string { switch ($algo) { case 'sha256': $limits = ['raw' => 32, 'hex' => 64, 'pad_min' => 40, 'pad_max' => 44]; break; default: throw new \Exception('Browsers currently only support sha256 public key pins.'); } $len = Binary::safeStrlen($hash); if ($len === $limits['hex']) { $hash = Base64::encode(Hex::decode($hash)); } elseif ($len === $limits['raw']) { $hash = Base64::encode($hash); } elseif ($len > $limits['pad_min'] && $len < $limits['pad_max']) { // Padding was stripped! $hash .= \str_repeat('=', $len % 4); // Base64UrlSsafe encoded. if (\strpos($hash, '_') !== false || \strpos($hash, '-') !== false) { $hash = Base64UrlSafe::decode($hash); } else { $hash = Base64::decode($hash); } $hash = Base64::encode($hash); } return $hash; }
/** * Convert a public key to the appropriate format * * @access public * @param \phpseclib\Math\BigInteger $n * @param \phpseclib\Math\BigInteger $e * @return string */ static function savePublicKey(BigInteger $n, BigInteger $e) { $modulus = $n->toBytes(true); $publicExponent = $e->toBytes(true); // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>: // RSAPublicKey ::= SEQUENCE { // modulus INTEGER, -- n // publicExponent INTEGER -- e // } $components = array('modulus' => pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($modulus)), $modulus), 'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($publicExponent)), $publicExponent)); $RSAPublicKey = pack('Ca*a*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), $components['modulus'], $components['publicExponent']); // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption. $rsaOID = "0\r\t*†H†÷\r"; // hex version of MA0GCSqGSIb3DQEBAQUA $RSAPublicKey = chr(0) . $RSAPublicKey; $RSAPublicKey = chr(3) . ASN1::encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey; $RSAPublicKey = pack('Ca*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey); $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . chunk_split(Base64::encode($RSAPublicKey), 64) . '-----END PUBLIC KEY-----'; return $RSAPublicKey; }
/** * RSASSA-PKCS1-V1_5-VERIFY (relaxed matching) * * Per {@link http://tools.ietf.org/html/rfc3447#page-43 RFC3447#page-43} PKCS1 v1.5 * specified the use BER encoding rather than DER encoding that PKCS1 v2.0 specified. * This means that under rare conditions you can have a perfectly valid v1.5 signature * that fails to validate with _rsassa_pkcs1_v1_5_verify(). PKCS1 v2.1 also recommends * that if you're going to validate these types of signatures you "should indicate * whether the underlying BER encoding is a DER encoding and hence whether the signature * is valid with respect to the specification given in [PKCS1 v2.0+]". so if you do * $rsa->getLastPadding() and get RSA::PADDING_RELAXED_PKCS1 back instead of * RSA::PADDING_PKCS1... that means BER encoding was used. * * @access private * @param string $m * @param string $s * @return bool */ function _rsassa_pkcs1_v1_5_relaxed_verify($m, $s) { // Length checking if (strlen($s) != $this->k) { return false; } // RSA verification $s = $this->_os2ip($s); $m2 = $this->_rsavp1($s); if ($m2 === false) { return false; } $em = $this->_i2osp($m2, $this->k); if ($em === false) { return false; } if ($this->_string_shift($em, 2) != "") { return false; } $em = ltrim($em, "ÿ"); if ($this->_string_shift($em) != "") { return false; } $asn1 = new ASN1(); $decoded = $asn1->decodeBER($em); if (!is_array($decoded) || empty($decoded[0]) || strlen($em) > $decoded[0]['length']) { return false; } $AlgorithmIdentifier = array('type' => ASN1::TYPE_SEQUENCE, 'children' => array('algorithm' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), 'parameters' => array('type' => ASN1::TYPE_ANY, 'optional' => true))); $DigestInfo = array('type' => ASN1::TYPE_SEQUENCE, 'children' => array('digestAlgorithm' => $AlgorithmIdentifier, 'digest' => array('type' => ASN1::TYPE_OCTET_STRING))); $oids = array('1.2.840.113549.2.2' => 'md2', '1.2.840.113549.2.4' => 'md4', '1.2.840.113549.2.5' => 'md5', '1.3.14.3.2.26' => 'sha1', '2.16.840.1.101.3.4.2.1' => 'sha256', '2.16.840.1.101.3.4.2.2' => 'sha384', '2.16.840.1.101.3.4.2.3' => 'sha512'); $asn1->loadOIDs($oids); $decoded = $asn1->asn1map($decoded[0], $DigestInfo); if (!isset($decoded) || $decoded === false) { return false; } if (!in_array($decoded['digestAlgorithm']['algorithm'], $oids)) { return false; } $hash = new Hash($decoded['digestAlgorithm']['algorithm']); $em = $hash->hash($m); $em2 = Base64::decode($decoded['digest']); return $this->_equals($em, $em2); }
/** * Wrap a public key appropriately * * @access public * @param string $key * @param string $type * @return string */ static function wrapPublicKey($key, $type) { return "-----BEGIN {$type} PUBLIC KEY-----\r\n" . chunk_split(Base64::encode($key), 64) . "-----END {$type} PUBLIC KEY-----"; }
/** * Convert a public key to the appropriate format * * @access public * @param \phpseclib\Math\BigInteger $n * @param \phpseclib\Math\BigInteger $e * @return string */ static function savePublicKey(BigInteger $n, BigInteger $e) { $n = $n->toBytes(true); $e = $e->toBytes(true); $key = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($e), $e, strlen($n), $n); $key = "---- BEGIN SSH2 PUBLIC KEY ----\r\n" . 'Comment: "' . str_replace(array('\\', '"'), array('\\\\', '\\"'), self::$comment) . "\"\r\n" . chunk_split(Base64::encode($key), 64) . '---- END SSH2 PUBLIC KEY ----'; return $key; }
/** * Performs modular exponentiation. * * Here's an example: * <code> * <?php * $a = new \phpseclib\Math\BigInteger('10'); * $b = new \phpseclib\Math\BigInteger('20'); * $c = new \phpseclib\Math\BigInteger('30'); * * $c = $a->modPow($b, $c); * * echo $c->toString(); // outputs 10 * ?> * </code> * * @param \phpseclib\Math\BigInteger $e * @param \phpseclib\Math\BigInteger $n * @return \phpseclib\Math\BigInteger * @access public * @internal The most naive approach to modular exponentiation has very unreasonable requirements, and * and although the approach involving repeated squaring does vastly better, it, too, is impractical * for our purposes. The reason being that division - by far the most complicated and time-consuming * of the basic operations (eg. +,-,*,/) - occurs multiple times within it. * * Modular reductions resolve this issue. Although an individual modular reduction takes more time * then an individual division, when performed in succession (with the same modulo), they're a lot faster. * * The two most commonly used modular reductions are Barrett and Montgomery reduction. Montgomery reduction, * although faster, only works when the gcd of the modulo and of the base being used is 1. In RSA, when the * base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because * the product of two odd numbers is odd), but what about when RSA isn't used? * * In contrast, Barrett reduction has no such constraint. As such, some bigint implementations perform a * Barrett reduction after every operation in the modpow function. Others perform Barrett reductions when the * modulo is even and Montgomery reductions when the modulo is odd. BigInteger.java's modPow method, however, * uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and * the other, a power of two - and recombine them, later. This is the method that this modPow function uses. * {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates. */ function modPow(BigInteger $e, BigInteger $n) { $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs(); if ($e->compare(new static()) < 0) { $e = $e->abs(); $temp = $this->modInverse($n); if ($temp === false) { return false; } return $this->_normalize($temp->modPow($e, $n)); } if (MATH_BIGINTEGER_MODE == self::MODE_GMP) { $temp = new static(); $temp->value = gmp_powm($this->value, $e->value, $n->value); return $this->_normalize($temp); } if ($this->compare(new static()) < 0 || $this->compare($n) > 0) { list(, $temp) = $this->divide($n); return $temp->modPow($e, $n); } if (defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { $components = array('modulus' => $n->toBytes(true), 'publicExponent' => $e->toBytes(true)); $components = array('modulus' => pack('Ca*a*', 2, self::_encodeASN1Length(strlen($components['modulus'])), $components['modulus']), 'publicExponent' => pack('Ca*a*', 2, self::_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent'])); $RSAPublicKey = pack('Ca*a*a*', 48, self::_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])), $components['modulus'], $components['publicExponent']); $rsaOID = "0\r\t*†H†÷\r"; // hex version of MA0GCSqGSIb3DQEBAQUA $RSAPublicKey = chr(0) . $RSAPublicKey; $RSAPublicKey = chr(3) . self::_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey; $encapsulated = pack('Ca*a*', 48, self::_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey); $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . chunk_split(Base64::encode($encapsulated)) . '-----END PUBLIC KEY-----'; $plaintext = str_pad($this->toBytes(), strlen($n->toBytes(true)) - 1, "", STR_PAD_LEFT); if (openssl_public_encrypt($plaintext, $result, $RSAPublicKey, OPENSSL_NO_PADDING)) { return new static($result, 256); } } if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { $temp = new static(); $temp->value = bcpowmod($this->value, $e->value, $n->value, 0); return $this->_normalize($temp); } if (empty($e->value)) { $temp = new static(); $temp->value = array(1); return $this->_normalize($temp); } if ($e->value == array(1)) { list(, $temp) = $this->divide($n); return $this->_normalize($temp); } if ($e->value == array(2)) { $temp = new static(); $temp->value = self::_square($this->value); list(, $temp) = $temp->divide($n); return $this->_normalize($temp); } return $this->_normalize($this->_slidingWindow($e, $n, self::BARRETT)); // the following code, although not callable, can be run independently of the above code // although the above code performed better in my benchmarks the following could might // perform better under different circumstances. in lieu of deleting it it's just been // made uncallable // is the modulo odd? if ($n->value[0] & 1) { return $this->_normalize($this->_slidingWindow($e, $n, self::MONTGOMERY)); } // if it's not, it's even // find the lowest set bit (eg. the max pow of 2 that divides $n) for ($i = 0; $i < count($n->value); ++$i) { if ($n->value[$i]) { $temp = decbin($n->value[$i]); $j = strlen($temp) - strrpos($temp, '1') - 1; $j += 26 * $i; break; } } // at this point, 2^$j * $n/(2^$j) == $n $mod1 = clone $n; $mod1->_rshift($j); $mod2 = new static(); $mod2->value = array(1); $mod2->_lshift($j); $part1 = $mod1->value != array(1) ? $this->_slidingWindow($e, $mod1, self::MONTGOMERY) : new static(); $part2 = $this->_slidingWindow($e, $mod2, self::POWEROF2); $y1 = $mod2->modInverse($mod1); $y2 = $mod1->modInverse($mod2); $result = $part1->multiply($mod2); $result = $result->multiply($y1); $temp = $part2->multiply($mod1); $temp = $temp->multiply($y2); $result = $result->add($temp); list(, $result) = $result->divide($n); return $this->_normalize($result); }
/** * Automatic script execution * * @param array $autoRun * @return mixed */ protected function autoRunScript(array $autoRun) { $ret = null; // Get a unique temporary file do { $script = \tempnam(ROOT . DIRECTORY_SEPARATOR . 'tmp', 'update-script-'); } while (\file_exists($script)); // What kind of autoRun script is it? switch ($autoRun['type']) { case 'php': \file_put_contents($script . '.php', Base64::decode($autoRun['data'])); $ret = Sandbox::safeRequire($script . '.php'); \unlink($script . '.php'); break; case 'sh': \file_put_contents($script . '.sh', Base64::decode($autoRun['data'])); $ret = \shell_exec($script . '.sh'); \unlink($script . '.sh'); break; case 'pgsql': case 'mysql': \file_put_contents($script . '.' . $autoRun['type'], Base64::decode($autoRun['data'])); $ret = Sandbox::runSQLFile($script . '.' . $autoRun['type'], $autoRun['type']); \unlink($script . '.' . $autoRun['type']); break; } return $ret; }
/** * Convert a public key to the appropriate format * * @access public * @param \phpseclib\Math\BigInteger $n * @param \phpseclib\Math\BigInteger $e * @return string */ static function savePublicKey(BigInteger $n, BigInteger $e) { $n = strrev($n->toBytes()); $e = str_pad(strrev($e->toBytes()), 4, ""); $key = pack('aavV', chr(self::PUBLICKEYBLOB), chr(2), 0, self::CALG_RSA_KEYX); $key .= pack('VVa*', self::RSA1, 8 * strlen($n), $e); $key .= $n; return Base64::encode($key); }