/** * Creates new connection to OTServ administration backend. * * <p> * This method automaticly handles RSA and XTEA encryption if such method is required by server including keys negotiations. * </p> * * <p> * After connecting you should check {@link OTS_Admin::requiresLogin() if server requires login}. * </p> * * @version 0.1.2 * @since 0.1.2 * @param string $host Target server. * @param int $port Port (7171 by default). * @throws E_OTS_ErrorCode When receive failed respond or unexpected message. * @throws E_OTS_OutOfBuffer When there is read attemp after end of packet stream. */ public function __construct($host, $port = 7171) { // saves connection sessint $this->host = $host; $this->port = $port; // opens connection $this->socket = fsockopen($this->host, $this->port); // 254 = OTAdmin protocol identifier $message = new OTS_Buffer(chr(254)); // sends initial packet $respond = $this->send($message); $message->reset(); $byte = $respond->getChar(); // checks if it's HELLO packet if ($byte != self::RESPOND_HELLO) { throw new E_OTS_ErrorCode($byte); } // skips respond signature and protocol version $respond->getLong(); $respond->getString(); // saves protocol settings $this->policy = $respond->getShort(); $this->options = $respond->getLong(); // handles encryption initialisation if required if ($this->requiresEncryption() && $this->usesRSA1024XTEA()) { // requests public key $message->putChar(self::REQUEST_KEY_EXCHANGE); $message->putChar(self::ENCRYPTION_RSA1024XTEA); $respond = $this->send($message); $message->reset(); $byte = $respond->getChar(); // checks if it succeded if ($byte != self::RESPOND_KEY_EXCHANGE_OK) { throw new E_OTS_ErrorCode($byte, $respond->getString()); } // we support only RSA 1024bit encryption for XTEA key sending if ($respond->getChar() != self::ENCRYPTION_RSA1024XTEA) { throw new E_OTS_ErrorCode($byte); } // reads binary form of public key (128 bytes) $key = OTS_BinaryTools::bin2Int(strrev($respond->getString(128))); // creates RSA cipher // as we have ready N computer here and we don't compute it by ourselves we can use a little hack, to save N as P * Q we will use P = N and Q = 1 $rsa = new OTS_RSA($key, '1'); $key = ''; // generates random XTEA key for ($i = 0; $i < 4; $i++) { $key .= pack('L', rand(0, 4294967295)); } $xtea = new OTS_XTEA($key); // sends XTEA key $message->putChar(self::REQUEST_ENCRYPTION); $message->putChar(self::ENCRYPTION_RSA1024XTEA); $message->putString($rsa->encrypt(chr(0) . $key), false); // we can't bind cipher yet since only respnd will be XTEA encrypted $respond = new OTS_Buffer($xtea->decrypt($this->send($message)->getBuffer())); $byte = $respond->getChar(); // checks if encryption negotation succeeded if ($byte != self::RESPOND_ENCRYPTION_OK) { throw new E_OTS_ErrorCode($byte, $respond->getString()); } // saves encryption/decryption cipher $this->cipher = $xtea; } }
/** * Ecnrypts message with RSA algorithm. * * @version 0.1.2 * @since 0.1.2 * @param string $message Message to be encrypted. * @return string Encrypted message. */ public function encrypt($message) { // chunk length $length = ceil($this->length / 8); // c = m^e mod n return str_pad(strrev(OTS_BinaryTools::int2Bin(bcpowmod(OTS_BinaryTools::bin2Int(strrev(str_pad($message, $length, chr(0)))), $this->e, $this->n))), $length, chr(0), STR_PAD_LEFT); }
/** * Decrypt XTEA-encrypted string. * * @param string $message Encrypted message. * @return string Decrypted string. */ public function decrypt($message) { // convert data to long $message = array_values(unpack('V*', $message)); $length = count($message); // converts to unsigned integers foreach ($message as $index => $result) { if ($result < 0) { $message[$index] += 0xffffffff + 1; } } // decrypt the long data with the key $result = ''; for ($i = 0; $i < $length; $i++) { $sum = 0xc6ef3720; $delta = 0x61c88647; $y = $message[$i]; $z = $message[++$i]; for ($j = 0; $j < 32; $j++) { $z = OTS_BinaryTools::unsignedAdd($z, -(OTS_BinaryTools::unsignedAdd($y << 4 ^ OTS_BinaryTools::unsignedRightShift($y, 5), $y) ^ OTS_BinaryTools::unsignedAdd($sum, $this->key[OTS_BinaryTools::unsignedRightShift($sum, 11) & 3]))); $sum = OTS_BinaryTools::unsignedAdd($sum, $delta); $y = OTS_BinaryTools::unsignedAdd($y, -(OTS_BinaryTools::unsignedAdd($z << 4 ^ OTS_BinaryTools::unsignedRightShift($z, 5), $z) ^ OTS_BinaryTools::unsignedAdd($sum, $this->key[$sum & 3]))); } // append the deciphered longs to the result data $result .= pack('VV', $y, $z); } // reads message length $offset = unpack('v', $result); return substr($result, 2, $offset[1]); }