srchub-old

srchub-old Mercurial Source Tree


Root/pluf/src/Pluf/Sign.php

<?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');
        }
        $compressed = ($signed_value[0] == '.') ? '.' : '';
        if ($compressed) {
            $signed_value = substr($signed_value, 1);
        }
        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($compressed.$value, $key) == $sig) {
            return $compressed.$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(hash_hmac('sha1', $value, $key, true));
    }
}
Source at commit 55305a934bac created 10 years 4 months ago.
By Nathan Adams, Fixing bug where password would not be hashed in database if user updated password

Archive Download this file

Branches

Tags

Page rendered in 1.05044s using 11 queries.