Skip to content

Instantly share code, notes, and snippets.

@mwgamera
Created September 12, 2012 07:01
Show Gist options
  • Save mwgamera/3704830 to your computer and use it in GitHub Desktop.
Save mwgamera/3704830 to your computer and use it in GitHub Desktop.
<?php
$serverSecret = "The quick fire fox jumps over the lazy server.";
require_once 'lib/Twig/Autoloader.php';
Twig_Autoloader::register();
$twig = new Twig_Environment(new Twig_Loader_Filesystem('./'));
require_once 'TwigObfuscator.php';
$o = new TwigObfuscator($serverSecret);
$twig->addExtension($o);
if ($_SERVER['QUERY_STRING']) {
$o->seed($_SERVER['QUERY_STRING']);
header('Content-type: application/ecmascript; charset=us-ascii');
echo $twig->render('demo_loadkey.js.twig');
}
else {
echo $twig->render('demo_index.html.twig', array(
'people' => array(
array(
'name' => 'user1',
'mail' => '[email protected]',
),
array(
'name' => 'user2',
'mail' => '[email protected]',
),
array(
'name' => 'user3',
'mail' => '[email protected]'
)
),
'message' => 'Zażółć gęślą jaźń!'
));
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Obfuscator demo</title>
<script type="application/ecmascript" src="obfuscator.min.js" defer="defer"></script>
<script type="application/ecmascript" src="?{{ obfuscator.seed }}" defer="defer"></script>
<script type="application/ecmascript">
window.onload = function() {
document.getElementById('message').innerHTML =
klg.obfuscator.decode({{ message|obfuscate|json_encode|raw }});
};
</script>
</head>
<body>
<h1>Obfuscator demo</h1>
<h2>List of e-mails</h2>
<ul>
{% for dweeb in people %}
<li><a href="" data-xhref="{{ ("mailto:" ~ dweeb.mail)|obfuscate }}" onmouseover="klg.obfuscator.href(this)">{{ dweeb.name }}</a></li>
{% endfor %}
</ul>
<div id="message"></div>
</body>
</html>
klg.obfuscator.setKey({{ obfuscator.key|json_encode|raw }})
(function(j){"use strict";function h(a){for(var a=k(a),b=i,c,e=a[0],f,d,g=2654435769*(0|6+52/a.length);0<g;){f=g>>>2;for(d=a.length-1;0<=d;d--)c=a[(a.length+d-1)%a.length],a[d]-=(c>>>5^e<<2)+(e>>>3^c<<4)^(g^e)+(b[(d^f)&3]^c),e=a[d]&=4294967295;g-=2654435769}c="";for(b=1;b<a.length;b++)c+=String.fromCharCode(a[b]>>>24&255,a[b]>>>16&255,a[b]>>>8&255,a[b]>>>0&255);for(b=c.length-1;0<=b&&!c.charCodeAt(b);)--b;return decodeURIComponent(escape(c.substring(0,b+1)))}var i=void 0,k=function(a){return function(b){var c,
e,f,d=[],b=b.replace(/\s+/g,"");for(c=0;c<b.length;c+=5){for(f=0,e=4;0<=e;e--)f*=85,f+=0|a[b.charCodeAt(c+e)];d.push(f)}return d}}(function(){for(var a=[],b=0;85>b;b++)a["0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%()*+,-/:;=?@^_`{|}~".charCodeAt(b)]=b;return a}());return j.obfuscator={setKey:function(a){i=a},decode:h,href:function(a){var b=a.dataset&&a.dataset.a||a.getAttribute("data-xhref")||a.title||a.name;return a.href=h(b)}}})("undefined"!==typeof module&&module.exports||
window.klg||(window.klg={}));
<?php
class ObfuscatorUnconfiguredException extends Exception {}
class TwigObfuscator extends Twig_Extension {
// Tentative name until I get more fluent with
// modern conventions and autoloader features.
public function getName() {
return "obfuscator";
}
public function getGlobals() {
return array(
'obfuscator' => $this
);
}
public function getFilters() {
return array(
'obfuscate' => new Twig_Filter_Method($this, 'obfuscate', array(
'needs_environment' => true,
'is_safe' => array('html')
))
);
}
const SEED_LENGTH = 16;
const KEY_ROUNDS = 20;
const ALPHA85 =
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%()*+,-/:;=?@^_`{|}~';
private $secret;
private $seed, $key;
public function __construct($secret = null, $seed = null) {
$this->secret = (string) $secret;
$this->seed = (string) $seed;
}
/** Set server secret */
public function set_secret($secret) {
$this->secret = $secret;
$this->key = null;
}
/** Get and/or set seed used to generate the key. */
public function seed($seed = null) {
if ($seed) {
$this->seed = (string) $seed;
$this->key = null;
}
if (!$this->seed)
$this->seed = self::make_seed();
return $this->seed;
}
/** Get encryption key. */
public function key() {
if (!$this->key)
$this->key = self::make_key($this->seed());
return $this->key;
}
/** Obfuscate a string by encrypting it. */
public function obfuscate(Twig_Environment $env, $value) {
if (function_exists('mb_convert_encoding'))
$value = mb_convert_encoding($value, 'UTF-8', $env->getCharset());
$k = $this->key();
$d = self::xxtea_enc(self::strpack($value), $k);
return self::ascii85($d);
}
/** Generate a seed. */
private static function make_seed() {
$length = (int) ((self::SEED_LENGTH + 3) / 4);
$s = "";
while ($length--)
$s .= sprintf("%08x", self::sha1rand());
return $s;
}
/** Derive a key from the seed. */
private function make_key($seed) {
if (!$this->secret)
throw new ObfuscatorUnconfiguredException("Server secret not set.");
$s = $this->secret . $seed . $this->secret;
$r = self::KEY_ROUNDS;
while ($r-- > 0)
$s = $this->secret . sha1($s, true) . $seed;
$s = sha1($s);
$a = array();
$a[] = 0xffffffff & hexdec(substr($s, 0, 8));
$a[] = 0xffffffff & hexdec(substr($s, 8, 8));
$a[] = 0xffffffff & hexdec(substr($s,16, 8));
$a[] = 0xffffffff & hexdec(substr($s,24, 8));
return $a;
}
/** Secure-ish PRNG based on the SHA1 primitive. */
private static function sha1rand($more_entropy = false) {
static $pad, $ctr = 0;
// seeding
if (!$ctr) {
$pad = @implode("\x1f", @array_values(@fstat(@fopen(__FILE__, 'r'))));
$pad.= "\x1e". @implode("\x1f", @array_values($_REQUEST));
$pad.= "\x1e". @implode("\x1f", @array_values($_SERVER));
$more_entropy = true;
}
if ($more_entropy) {
$pad .= "\x1e". microtime() . rand() . uniqid(mt_rand(), true);
if ($krng = @fopen('/dev/urandom', 'rb')) {
if (function_exists('stream_set_read_buffer'))
@stream_set_read_buffer($krng, 0);
$pad .= @fread($krng, 20);
@fclose($krng);
}
}
// actual PRNG
$pad = sha1($pad ."\x1f". ++$ctr, true);
return 0xffffffff & hexdec(substr(sha1($pad), 0, 8));
}
/** Convert string to array of words for encryption. */
private static function strpack($str) {
do $str .= "\0\0\0\0";
while (mt_rand(0,1));
$arr = array_values(unpack('N*', $str));
array_unshift($arr, self::sha1rand()); // 32-bit IV
return array_pad($arr, 2, 0);
}
/** Encrypt block of data with XXTEA algorithm. */
private static function xxtea_enc($data, $key) {
$n = count($data);
$z = $data[$n-1];
$q = (int) (6 + 52 / count($data));
$s = 0;
while ($q-- > 0) {
$s = 0xffffffff & ($s + 0x9e3779b9);
$e = $s >> 2;
for ($p = 0; $p < $n; $p++) {
$y = $data[($p+1)%$n];
$a = ($z >> 5 & 0x07ffffff) ^ $y << 2;
$b = ($y >> 3 & 0x1fffffff) ^ $z << 4;
$a = 0xffffffff & ($a + $b);
$b = 0xffffffff & (($s ^ $y) + ($key[($p ^ $e) & 3] ^ $z));
$z = 0xffffffff & ($data[$p] + ($a ^ $b));
$data[$p] = $z;
}
}
return $data;
}
/** Encode 32b words as safe text. */
private static function ascii85($data) {
$str = '';
foreach ($data as $u32) {
for ($i = 0; $i < 5; $i++) {
$str .= substr(self::ALPHA85, $u32 % 85, 1);
$u32 /= 85;
}
}
return $str;
}
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment