<?php␊ |
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */␊ |
/*␊ |
# ***** BEGIN LICENSE BLOCK *****␊ |
# This file is part of Plume Framework, a simple PHP Application Framework.␊ |
# Copyright (C) 2001-2009 Loic d'Anterroches and contributors.␊ |
#␊ |
# Plume Framework is free software; you can redistribute it and/or modify␊ |
# it under the terms of the GNU Lesser General Public License as published by␊ |
# the Free Software Foundation; either version 2.1 of the License, or␊ |
# (at your option) any later version.␊ |
#␊ |
# Plume Framework is distributed in the hope that it will be useful,␊ |
# but WITHOUT ANY WARRANTY; without even the implied warranty of␊ |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the␊ |
# GNU Lesser General Public License for more details.␊ |
#␊ |
# You should have received a copy of the GNU Lesser General Public License␊ |
# along with this program; if not, write to the Free Software␊ |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA␊ |
#␊ |
# ***** END LICENSE BLOCK ***** */␊ |
␊ |
␊ |
/**␊ |
* Module to easily and possibly securily sign strings.␊ |
*␊ |
* The goal is to avoid reinventing the wheel each time one needs to␊ |
* sign strings.␊ |
*␊ |
* Usage to sign a string:␊ |
* ␊ |
* <pre>␊ |
* $signed = Pluf_Sign::sign($mystring);␊ |
* // send the string over the wire␊ |
* $mystring = Pluf_Sign::unsign($signed);␊ |
* </pre>␊ |
*␊ |
* Usage to pack and sign an object:␊ |
* <pre>␊ |
* $signed = Pluf_Sign::dumps($myobject);␊ |
* // send the string over the wire␊ |
* $myobject = Pluf_Sign::loads($signed);␊ |
* </pre>␊ |
*␊ |
* Based on the work by Simon Willison:␊ |
* http://github.com/simonw/django-openid/blob/master/django_openid/signed.py␊ |
*/␊ |
class Pluf_Sign␊ |
{␊ |
/**␊ |
* Dump and sign an object.␊ |
*␊ |
* If you want to sign a small string, use directly the␊ |
* sign/unsign function as compression will not help and you will␊ |
* save the overhead of the serialize call.␊ |
*␊ |
* @param mixed Object␊ |
* @param string Key (null)␊ |
* @param bool Compress with gzdeflate (false)␊ |
* @param string Extra key not to use only the secret_key ('')␊ |
* @return string Signed string␊ |
*/␊ |
public static function dumps($obj, $key=null, $compress=false, $extra_key='')␊ |
{␊ |
$serialized = serialize($obj);␊ |
$is_compressed = false; // Flag for if it's been compressed or not␊ |
if ($compress) {␊ |
$compressed = gzdeflate($serialized, 9);␊ |
if (strlen($compressed) < (strlen($serialized) - 1)) {␊ |
$serialized = $compressed;␊ |
$is_compressed = true;␊ |
}␊ |
}␊ |
$base64d = Pluf_Utils::urlsafe_b64encode($serialized);␊ |
if ($is_compressed) {␊ |
$base64d = '.'.$base64d;␊ |
}␊ |
if ($key === null) {␊ |
$key = Pluf::f('secret_key');␊ |
}␊ |
return self::sign($base64d, $key.$extra_key);␊ |
}␊ |
␊ |
/**␊ |
* Reverse of dumps, throw an Exception in case of bad signature.␊ |
*␊ |
* @param string Signed key␊ |
* @param string Key (null)␊ |
* @param string Extra key ('')␊ |
* @return mixed The dumped signed object␊ |
*/␊ |
public static function loads($s, $key=null, $extra_key='')␊ |
{␊ |
if ($key === null) {␊ |
$key = Pluf::f('secret_key');␊ |
}␊ |
$base64d = self::unsign($s, $key.$extra_key);␊ |
$decompress = false;␊ |
if ($base64d[0] == '.') {␊ |
// It's compressed; uncompress it first␊ |
$base64d = substr($base64d, 1);␊ |
$decompress = true;␊ |
}␊ |
$serialized = Pluf_Utils::urlsafe_b64decode($base64d);␊ |
if ($decompress) {␊ |
$serialized = gzinflate($serialized);␊ |
}␊ |
return unserialize($serialized);␊ |
}␊ |
␊ |
/**␊ |
* Sign a string.␊ |
*␊ |
* If the key is not provided, it will use the secret_key␊ |
* available in the configuration file.␊ |
*␊ |
* The signature string is safe to use in URLs. So if the string to␊ |
* sign is too, you can use the signed string in URLs.␊ |
*␊ |
* @param string The string to sign␊ |
* @param string Optional key (null) ␊ |
* @return string Signed string␊ |
*/␊ |
public static function sign($value, $key=null)␊ |
{␊ |
if ($key === null) {␊ |
$key = Pluf::f('secret_key');␊ |
}␊ |
return $value.'.'.self::base64_hmac($value, $key);␊ |
}␊ |
␊ |
␊ |
/**␊ |
* Unsign a value.␊ |
*␊ |
* It will throw an exception in case of error in the process.␊ |
* ␊ |
* @return string Signed string␊ |
* @param string Optional key (null) ␊ |
* @param string The string␊ |
*/␊ |
public static function unsign($signed_value, $key=null)␊ |
{␊ |
if ($key === null) {␊ |
$key = Pluf::f('secret_key');␊ |
}␊ |
if (false === strpos($signed_value, '.')) {␊ |
throw new Exception('Missing signature (no . found in value).');␊ |
}␊ |
list($value, $sig) = explode('.', $signed_value, 2);␊ |
if (self::base64_hmac($value, $key) == $sig) {␊ |
return $value;␊ |
} else {␊ |
throw new Exception(sprintf('Signature failed: "%s".', $sig));␊ |
}␊ |
}␊ |
␊ |
/**␊ |
* Calculate the URL safe base64 encoded SHA1 hmac of a string.␊ |
*␊ |
* @param string The string to sign␊ |
* @param string The key␊ |
* @return string The signature␊ |
*/␊ |
public static function base64_hmac($value, $key)␊ |
{␊ |
return Pluf_Utils::urlsafe_b64encode(self::hmac_sha1($key, $value));␊ |
}␊ |
␊ |
␊ |
␊ |
/**␊ |
* HMAC-SHA1 function.␊ |
*␊ |
* @see http://us.php.net/manual/en/function.sha1.php#39492␊ |
*␊ |
* @param string Key␊ |
* @param string Data␊ |
* @return string Calculated binary HMAC-SHA1␊ |
*/␊ |
public static function hmac_sha1($key, $data) ␊ |
{␊ |
if (strlen($key) > 64) {␊ |
$key = pack('H*', sha1($key));␊ |
}␊ |
$key = str_pad($key, 64, chr(0x00));␊ |
$ipad = str_repeat(chr(0x36), 64);␊ |
$opad = str_repeat(chr(0x5c), 64);␊ |
return pack('H*',sha1(($key^$opad).pack('H*',sha1(($key^$ipad).$data))));␊ |
return bin2hex($hmac);␊ |
}␊ |
} |