pluf2

pluf2 Commit Details


Date:2009-10-05 14:49:45 (15 years 2 months ago)
Author:Loic d'Anterroches
Branch:master
Commit:b14b013875d70cc1235c3ca8b1210a4bcdcd3faf
Parents: 5978e7b0e7c20f1029646640cd8b5d55f28cd99a
Message:Added a set of functions to sign strings and dump/load objects.

Changes:

File differences

src/Pluf/Sign.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
<?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);
}
}
src/Pluf/Tests/Sign/Sign.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
<?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-2007 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 ***** */
class Pluf_Tests_Sign_Sign extends UnitTestCase
{
function __construct()
{
parent::__construct('Test the signing functions.');
}
function setUp()
{
}
function tearDown()
{
}
function testSignUsesCorrectKey()
{
$s = 'This is a string';
$this->assertEqual(Pluf_Sign::sign($s),
$s.'.'.Pluf_Sign::base64_hmac($s,
Pluf::f('secret_key')
)
);
}
function testSignIsReversible()
{
$examples = array(
'q;wjmbk;wkmb',
'3098247529087',
'3098247:529:087:',
'jkw osanteuh ,rcuh nthu aou oauh ,ud du',
);
foreach ($examples as $example) {
$this->assertTrue($example != Pluf_Sign::sign($example));
$this->assertEqual($example, Pluf_Sign::unsign(Pluf_Sign::sign($example)));
}
}
function testUnsignDetectsTampering()
{
$value = 'Another string';
$signed_value = Pluf_Sign::sign($value);
$transforms = array(
strtoupper($signed_value),
$signed_value.'a',
'a'.substr($signed_value, 1),
str_replace('w', '', $signed_value),
);
$this->assertEqual($value, Pluf_Sign::unsign($signed_value));
foreach ($transforms as $t) {
try {
Pluf_Sign::unsign($t);
$this->fail();
} catch (Exception $e) {
$this->pass();
}
}
}
function testEncodeDecode()
{
$objects = array(
array('an', 'array'),
'a string',
(object) array('a' => 'property'),
);
foreach ($objects as $o) {
$this->assertTrue($o != Pluf_Sign::dumps($o));
$this->assertEqual($o, Pluf_Sign::loads(Pluf_Sign::dumps($o)));
}
}
function testDecodeDetectsTampering()
{
$value = array('foo'=> 'bar', 'baz'=> 1);
$encoded = Pluf_Sign::dumps($value);
$transforms = array(
strtoupper($encoded),
$encoded.'a',
'a'.substr($encoded, 1),
str_replace('M', '', $encoded),
);
$this->assertEqual($value, Pluf_Sign::loads($encoded));
foreach ($transforms as $t) {
try {
Pluf_Sign::loads($t);
$this->fail();
} catch (Exception $e) {
$this->pass();
}
}
}
}
src/Pluf/Utils.php
259259
260260
261261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
262292
$code = proc_close($process);
return $output;
}
/**
* URL safe base 64 encoding.
*
* Compatible with python base64's urlsafe methods.
*
* @link http://www.php.net/manual/en/function.base64-encode.php#63543
*/
public static function urlsafe_b64encode($string)
{
return str_replace(array('+','/','='),
array('-','_',''),
base64_encode($string));
}
/**
* URL safe base 64 decoding.
*/
public static function urlsafe_b64decode($string)
{
$data = str_replace(array('-','_'),
array('+','/'),
$string);
$mod4 = strlen($data) % 4;
if ($mod4) {
$data .= substr('====', $mod4);
}
return base64_decode($data);
}
}

Archive Download the corresponding diff file

Branches

Number of commits:
Page rendered in 0.07146s using 13 queries.