Skip to content

Instantly share code, notes, and snippets.

@michel-zimmer
Last active August 29, 2015 14:06
Show Gist options
  • Save michel-zimmer/470eddea166a3dba8eaf to your computer and use it in GitHub Desktop.
Save michel-zimmer/470eddea166a3dba8eaf to your computer and use it in GitHub Desktop.
ipupdater

This is a dummy program and was developed to find a way to get encrypted communication between a server written in PHP and a client written in Node.js. It is far from being optimal and is more like a prove of concept.

To use it you need to create a openssl (private-public) key-pair.

<?php
/**
* IPU protocol errors
*/
define('IPU_UNKNOWN_ERROR', 0);
define('IPU_TOKEN_MISSING', 1);
define('IPU_MESSAGE_MISSING', 2);
define('IPU_PRIVATE_KEY_UNAVAILABLE', 3);
define('IPU_JSON_ERROR', 4);
define('IPU_UNKNOWN_ACTION', 5);
define('IPU_PHP_EXCEPTION', 6);
/**
* Convenience function for reporting IPU errors
*/
function reportIPUError($code, $payload = NULL) {
$error = new stdClass;;
$error->code = (integer) $code;
if ($payload !== NULL) {
$error->payload = $payload;
}
switch ($error->code) {
case IPU_TOKEN_MISSING:
$error->message = 'Parameter \'token\' is missing';
break;
case IPU_MESSAGE_MISSING:
$error->message = 'Parameter \'message\' is missing';
break;
case IPU_PRIVATE_KEY_UNAVAILABLE:
$error->message = 'Could not load private key';
break;
case IPU_JSON_ERROR;
$error->message = 'JSON error';
$error->json = new stdClass;
$error->json->code = json_last_error();
$error->json->message = json_last_error_msg();
break;
case IPU_UNKNOWN_ACTION;
$error->message = 'Unknown action';
break;
case IPU_PHP_EXCEPTION;
$error->message = 'A PHP exception or error occured';
break;
default:
$error->code = UNKNOWN_ERROR;
$error->message = 'Unknown error';
}
global $response;
$response->errors[] = $error;
}
/**
* Error handler, passes flow over the exception logger with new ErrorException.
*/
function logError($num, $str, $file, $line, $context = null) {
logException(new ErrorException($str, 0, $num, $file, $line));
}
/**
* Uncaught exception handler.
*/
function logException(Exception $e) {
$payload = new stdClass;
$payload->exception->code = $e->getCode();
$payload->exception->message = $e->getMessage();
$payload->exception->file = $e->getFile();
$payload->exception->line = $e->getLine();
reportIPUError(IPU_PHP_EXCEPTION, $payload);
}
/**
* Checks for a fatal error, work around for set_error_handler not working on fatal errors.
*/
function checkForFatal() {
$error = error_get_last();
if ($error['type'] === E_ERROR) {
logError($error['type'], $error['message'], $error['file'], $error['line']);
}
}
// register functions
register_shutdown_function('checkForFatal');
set_error_handler('logError');
set_exception_handler('logException');
<?php
function bitwiseXor($data, $key) {
$data = str_split($data);
$key = str_split($key);
for ($i = 0; $i < count($data); $i++) {
$data[$i] = $data[$i] ^ $key[$i % count($key)];
}
return implode($data);
}
?>
'use strict';
function bitwiseXor(data, key) {
for (var i = data.length - 1; i >= 0; i--) {
data[i] ^= key[i % key.length];
}
return data;
}
require('fs').readFile('id_rsa.pub', 'utf8', function (err, data) {
if (err) {
return console.log(err);
}
var publicKey = require('ursa').createPublicKey(data);
require('crypto').randomBytes(publicKey.getModulus().length - 42, function (ex, buf) {
if (ex) {
throw ex;
}
var postData = require('querystring').stringify({
'token': publicKey.encrypt(buf).toString('base64'),
'message': bitwiseXor(new Buffer('{"actions":[{"name":"getRemoteAddress"},{"name":"echo","parameters":{"echo":"Super secret message"}}]}'), buf).toString('base64')
});
var request = require('http').request({
host: '127.0.0.1',
port: '80',
path: '/ipupdater.php',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': postData.length
}
}, function(res) {
res.setEncoding('utf8');
res.on('data', function (response) {
response = JSON.parse(response);
console.log(JSON.stringify(response, null, 4));
});
});
request.write(postData);
request.end();
});
});
<?php
// data skeleton
$response = new stdClass;
$response->errors = [];
$response->actions = [];
// basic includes
require_once('error.php');
require_once('functions.php');
// handling of IPU protocol
if (!isset($_POST['token'])) {
reportIPUError(IPU_TOKEN_MISSING);
}
if (!isset($_POST['message'])) {
reportIPUError(IPU_MESSAGE_MISSING);
}
if (count($response->errors) === 0) {
openssl_private_decrypt(base64_decode($_POST['token']), $token, file_get_contents('id_rsa'), OPENSSL_PKCS1_OAEP_PADDING);
$message = json_decode(bitwiseXor(base64_decode($_POST['message']), $token));
if (json_last_error() !== JSON_ERROR_NONE) {
reportIPUError(IPU_JSON_ERROR);
}
if (count($response->errors) === 0) {
foreach ($message->actions as $action) {
switch ($action->name) {
case 'getRemoteAddress':
$action->response = new stdClass;
$action->response->remoteAddress = $_SERVER['REMOTE_ADDR'];
$response->actions[] = $action;
break;
case 'echo':
$action->response = new stdClass;
$action->response->echo = $action->parameters->echo;
$response->actions[] = $action;
break;
default:
$payload = new stdClass;
$payload->action = $action;
reportIPUError(IPU_UNKNOWN_ACTION, $payload);
}
}
}
}
echo(json_encode($response));
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment